diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-01-27 01:05:20 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-01-27 01:05:20 +0000 |
commit | 546db14ee74118296f425f3b91634fb767d67290 (patch) | |
tree | 22b613a3da8d4bf663eec5e155af01b87fdf9094 | |
parent | 1e25e41c4f5474e14452094492dbc169b800e4c8 (diff) |
Merge with Linux 2.3.23. The new bootmem stuff has broken various
platforms. At this time I've only verified that IP22 support compiles
and IP27 actually works.
599 files changed, 41654 insertions, 17064 deletions
@@ -860,7 +860,7 @@ S: Germany N: David Hinds E: dhinds@zen.stanford.edu -W: http://hyper.stanford.edu/~dhinds +W: http://tao.stanford.edu/~dhinds D: PCMCIA and CardBus stuff, PCMCIA-HOWTO, PCMCIA client drivers S: 2019 W. Middlefield Rd #1 S: Mountain View, CA 94043 @@ -907,14 +907,14 @@ S: CV5 8BZ S: United Kingdom N: Ron Holt -E: ron@caldera.com +E: ron@holt.org W: http://www.holt.org/ -P: 1024/1FD44539 DF 4B EB 9F 5B 68 38 9A 40 E3 FB 71 D1 C8 0B 56 +W: http://www.ronholt.com/ D: Kernel development -D: Minor kernel modifications to support Wabi and Wine -S: Caldera, Inc. -S: 240 West Center Street -S: Orem, Utah 84059-1920 +D: Kernel LDT modifications to support Wabi and Wine +S: Holtron Internetics, Inc. +S: 998 East 900 South, Suite 26 +S: Provo, Utah 84606-5607 S: USA N: Rob W. W. Hooft @@ -1329,11 +1329,11 @@ S: 198 00 Praha 9 S: Czech Republic N: Paul Mackerras -E: paulus@cs.anu.edu.au +E: paulus@linuxcare.com D: Linux port for PCI Power Macintosh -S: Dept. of Computer Science -S: Australian National University -S: Canberra ACT 0200 +S: Linuxcare, Inc. +S: 24 Marcus Clarke Street +S: Canberra ACT 2601 S: Australia N: Pat Mackinlay diff --git a/Documentation/Changes b/Documentation/Changes index f910c4c7e..a1c994b2a 100644 --- a/Documentation/Changes +++ b/Documentation/Changes @@ -43,7 +43,7 @@ Current Minimal Requirements encountered a bug! If you're unsure what version you're currently running, the suggested command should tell you. -- Kernel modutils 2.3.5 ; insmod -V +- Kernel modutils 2.3.6 ; insmod -V - Gnu C 2.7.2.3 ; gcc --version - Binutils 2.8.1.0.23 ; ld -v - Linux libc5 C Library 5.4.46 ; ls -l /lib/libc* @@ -60,7 +60,7 @@ running, the suggested command should tell you. - NFS 2.2beta40 ; showmount --version - Bash 1.14.7 ; bash -version - Ncpfs 2.2.0 ; ncpmount -v -- Pcmcia-cs 3.0.7 ; cardmgr -V +- Pcmcia-cs 3.1.2 ; cardmgr -V - PPP 2.3.9 ; pppd --version - Util-linux 2.9i ; chsh -v @@ -567,8 +567,8 @@ ftp://metalab.unc.edu/pub/Linux/GCC/ld.so-1.9.9.tar.gz Modules utilities ================= -The 2.3.5 release: -ftp://ftp.ocs.com.au/pub/modutils/v2.3/modutils-2.3.5.tar.gz +The 2.3.6 release: +ftp://ftp.ocs.com.au/pub/modutils/v2.3/modutils-2.3.6.tar.gz Procps utilities ================ @@ -684,8 +684,8 @@ ftp://ftp.samba.org/pub/samba/samba-2.0.0.tar.gz Pcmcia-cs ========= -The 3.0.7 release: -ftp://hyper.stanford.edu/pub/pcmcia/pcmcia-cs.3.0.7.tar.gz +The 3.1.2 release: +ftp://sourceforge.org/pcmcia/pcmcia-cs-3.1.2.tar.gz Setserial ========= diff --git a/Documentation/Configure.help b/Documentation/Configure.help index 22924c7c8..c4e59215d 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -175,18 +175,25 @@ CONFIG_MATHEMU on the Alpha. The only time you would ever not say Y is to say M in order to debug the code. Say Y unless you know what you are doing. -Big memory support -CONFIG_BIGMEM - Linux can use up to 2 Gigabytes (= 2^31 bytes) of physical memory. - If you are compiling a kernel which will never run on a machine with - more than 1 Gigabyte, answer N here. Otherwise, say Y. - - The actual amount of physical memory should be specified using a - kernel command line option such as "mem=256M". (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. The lilo procedure - is also explained in the SCSI-HOWTO, available from - http://metalab.unc.edu/mdw/linux.html#howto .) +High Memory support +CONFIG_NOHIGHMEM + If you are compiling a kernel which will never run on a machine + with more than 1 Gigabyte total physical RAM, answer "off" + here (default choice). + + Linux can use up to 64 Gigabytes of physical memory on x86 systems. + High memory is all the physical RAM that could not be directly + mapped by the kernel - ie. 3GB if there is 4GB RAM in the system, + 7GB if there is 8GB RAM in the system. + + If 4 Gigabytes physical RAM or less is used then answer "4GB" here. + + If more than 4 Gigabytes is used then answer "64GB" here. This + selection turns Intel PAE (Physical Address Extension) mode on. + PAE implements 3-level paging on IA32 processors. PAE is fully + supported by Linux, PAE mode is implemented on all recent Intel + processors (PPro and better). NOTE: The "64GB" kernel will not + boot CPUs that not support PAE! Normal PC floppy disk support CONFIG_BLK_DEV_FD @@ -578,12 +585,6 @@ IDEDMA_NEW_DRIVE_LISTINGS If in doubt, say N. -Winbond SL82c105 support -CONFIG_BLK_DEV_SL82C105 - If you have a Winbond SL82c105 IDE controller, say Y here to enable - special configuration for this chip. This is common on various CHRP - motherboards, but could be used elsewhere. If in doubt, say Y. - Boot off-board chipsets first support CONFIG_BLK_DEV_OFFBOARD Normally, IDE controllers built into the motherboard (on-board @@ -621,82 +622,32 @@ CONFIG_IDEDMA_PCI_AUTO It is normally safe to answer Y to this question unless your motherboard uses a VIA VP2 chipset, in which case you should say N. -Other IDE chipset support -CONFIG_IDE_CHIPSETS - Say Y here if you want to include enhanced support for various IDE - interface chipsets used on motherboards and add-on cards. You can - then pick your particular IDE chip from among the following options. - This enhanced support may be necessary for Linux to be able to - access the 3rd/4th drives in some systems. It may also enable - setting of higher speed I/O rates to improve system performance with - these chipsets. Most of these also require special kernel boot - parameters to actually turn on the support at runtime; you can find - a list of these in the file Documentation/ide.txt. - - People with SCSI-only systems can say N here. - -Generic 4 drives/port support -CONFIG_BLK_DEV_4DRIVES - Certain older chipsets, including the Tekram 690CD, use a single set - of I/O ports at 0x1f0 to control up to four drives, instead of the - customary two drives per port. Support for this can be enabled at - runtime using the "ide0=four" kernel boot parameter if you say Y - here. - -DTC-2278 support -CONFIG_BLK_DEV_DTC2278 - This driver is enabled at runtime using the "ide0=dtc2278" kernel - boot parameter. It enables support for the secondary IDE interface - of the DTC-2278 card, and permits faster I/O speeds to be set as - well. See the Documentation/ide.txt and drivers/block/dtc2278.c - files for more info. - -Holtek HT6560B support -CONFIG_BLK_DEV_HT6560B - This driver is enabled at runtime using the "ide0=ht6560b" kernel - boot parameter. It enables support for the secondary IDE interface - of the Holtek card, and permits faster I/O speeds to be set as well. - See the Documentation/ide.txt and drivers/block/ht6560b.c files for - more info. +AEC6210 chipset support +CONFIG_BLK_DEV_AEC6210 + This driver adds up to 4 more EIDE devices sharing a single + interrupt. This add-on card is a bootable PCI UDMA controller. In + order to get this card to initialize correctly in some cases, you + should say Y here, and preferably also to "Use DMA by default when + available". -PROMISE DC4030 support (EXPERIMENTAL) -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. CDROM and TAPE devices are not - supported yet. This driver is enabled at runtime using the - "ide0=dc4030" kernel boot parameter. See the Documentation/ide.txt - and drivers/block/pdc4030.c files for more info. + Please read the comments at the top of drivers/block/aec6210.c -PS/2 ESDI hard disk support -CONFIG_BLK_DEV_PS2 - Say Y here if you have a PS/2 machine with a MCA bus and an ESDI - hard disk. - - If you want to compile the driver as a module ( = code which can be - inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called ps2esdi.o. +ALI M15x3 chipset support (EXPERIMENTAL) +CONFIG_BLK_DEV_ALI15X3 + This driver ensures (U)DMA support for ALI 1543 and 1543C, + 1535, 1535D onboard chipsets. It also tests for Simplex mode and + enables normal dual channel support. -Tekram TRM290 chipset support (EXPERIMENTAL) -CONFIG_BLK_DEV_TRM290 - This driver adds support for bus master DMA transfers - using the Tekram TRM290 PCI IDE chip. Volunteers are - needed for further tweaking and development. - Please read the comments at the top of drivers/block/trm290.c. + If you say Y here, you also need to say Y to "Use DMA by default + when available", above. -OPTi 82C621 enhanced support (EXPERIMENTAL) -CONFIG_BLK_DEV_OPTI621 - This is a driver for the OPTi 82C621 EIDE controller. - Please read the comments at the top of drivers/block/opti621.c. + Please read the comments at the top of drivers/block/alim15x3.c -NS87415 support (EXPERIMENTAL) -CONFIG_BLK_DEV_NS87415 - This driver adds detection and support for the NS87415 chip - (used in SPARC64, among others). + If unsure, say N. - Please read the comments at the top of drivers/block/ns87415.c. +CMD646 chipset support (EXPERIMENTAL) +CONFIG_BLK_DEV_CMD646 + Say Y here if you have an IDE controller which uses this chipset. CY82C693 chipset support (EXPERIMENTAL) CONFIG_BLK_DEV_CY82C693 @@ -706,36 +657,80 @@ CONFIG_BLK_DEV_CY82C693 If you say Y here, you need to say Y to "Use DMA by default when available" as well. -VIA82C586 chipset support (EXPERIMENTAL) -CONFIG_BLK_DEV_VIA82C586 - Saying Y here adds initial timing settings for VIA (U)DMA onboard - IDE controllers that are ATA3 compliant. May work with ATA4 systems, - but not tested to date. To use some features of this chipset, you - will have to issue a kernel command line as described in the file - drivers/block/via82c586.c. Furthermore, if you also say Y to "/proc - filesystem support" and set DISPLAY_APOLLO_TIMINGS in via82c586.c, - you will be able to read information about the IDE controller from - the virtual file /proc/ide/via. +HPT34X chipset support +CONFIG_BLK_DEV_HPT34X + This driver adds up to 4 more EIDE devices sharing a single + interrupt. The HPT343 chipset in its current form is a non-bootable + controller; the HPT345/HPT363 chipset is a bootable (needs BIOS FIX) + PCI UDMA controllers. This driver requires dynamic tuning of the + chipset during the ide-probe at boot time. It is reported to support + DVD II drives, by the manufacturer. - If you say Y here, you also need to say Y to "Use DMA by default - when available", above. +HPT34X DMA support (EXPERIMENTAL) +CONFIG_BLK_DEV_HPT34X_DMA + you need to say Y to "Use DMA by default when available" if you say + Y here. + + Please read the comments at the top of drivers/block/hpt34x.c + +HPT366 chipset support +CONFIG_BLK_DEV_HPT366 + This is an Ultra DMA chipset for ATA-66. + + This driver adds up to 4 more EIDE devices sharing a single + interrupt. The HPT366 chipset in its current form is a non-bootable. + This driver requires dynamic tuning of the chipset during the + ide-probe at boot. It is reported to support DVD II drives, by the + manufacturer. + + Please read the comments at the top of drivers/block/hpt366.c + +HPT366 (EXPERIMENTAL) +CONFIG_BLK_DEV_HPT366_SHARED + This requires CONFIG_BLK_DEV_HPT366. + It appears that there are different versions or releases of this hardware + by ABit. Since some cases the second channel of the onboard chipset works + and others fail, it is default disabled. This is required to be set if you + want to attempt the setup of the second channel. + + JUMBO WARNING, do not boot a kernel with this enabled if it is your only + one. You may not be able to get back into your machine without physically + detaching the attached devices. If unsure, say N. -CMD646 chipset support (EXPERIMENTAL) -CONFIG_BLK_DEV_CMD646 - Say Y here if you have an IDE controller which uses this chipset. +NS87415 support (EXPERIMENTAL) +CONFIG_BLK_DEV_NS87415 + This driver adds detection and support for the NS87415 chip + (used in SPARC64, among others). -ALI M15x3 chipset support (EXPERIMENTAL) -CONFIG_BLK_DEV_ALI15X3 - This driver ensures (U)DMA support for ALI 1533, 1543 and 1543C - onboard chipsets. It also tests for Simplex mode and enables - normal dual channel support. + Please read the comments at the top of drivers/block/ns87415.c. - If you say Y here, you also need to say Y to "Use DMA by default - when available", above. +OPTi 82C621 enhanced support (EXPERIMENTAL) +CONFIG_BLK_DEV_OPTI621 + This is a driver for the OPTi 82C621 EIDE controller. + Please read the comments at the top of drivers/block/opti621.c. - Please read the comments at the top of drivers/block/alim15x3.c +Intel PIIXn chipsets support +CONFIG_BLK_DEV_PIIX + This driver adds PIO mode setting and tuning for all PIIX IDE + controllers by Intel. Since the BIOS can sometimes improperly tune + PIO 0-4 mode settings, this allows dynamic tuning of the chipset + via the standard end-user tool 'hdparm'. + + Please read the comments at the top of drivers/block/piix.c + + If unsure, say N. + +PIIXn Tuning support +CONFIG_BLK_DEV_PIIX_TUNING + This driver extension adds DMA mode setting and tuning for all PIIX + IDE controllers by Intel. Since the BIOS can sometimes improperly + set up the device/adapter combination and speed limits, it has + become a necessity to back/forward speed devices as needed. + + Case 430HX/440FX PIIX3 need speed limits to reduce UDMA to DMA mode + 2 if the BIOS can not perform this task at initialization. If unsure, say N. @@ -767,7 +762,7 @@ CONFIG_BLK_DEV_PDC202XX If unsure, say N. -Special UDMA Feature (EXPERIMENTAL) +Special UDMA Feature PDC202XX_FORCE_BURST_BIT For PDC20246 and PDC20262 Ultra DMA chipsets. Designed originally for PDC20246/Ultra33 that has BIOS setup failures when using 3 or @@ -777,71 +772,97 @@ PDC202XX_FORCE_BURST_BIT If unsure, say N. -Special Mode Feature (DANGEROUS) +Special Mode Feature (EXPERIMENTAL) PDC202XX_FORCE_MASTER_MODE For PDC20246 and PDC20262 Ultra DMA chipsets. This is reserved for possible Hardware RAID 0,1 for the FastTrak Series. Say N. -AEC6210 chipset support -CONFIG_BLK_DEV_AEC6210 - This driver adds up to 4 more EIDE devices sharing a single - interrupt. This add-on card is a bootable PCI UDMA controller. In - order to get this card to initialize correctly in some cases, you - should say Y here, and preferably also to "Use DMA by default when - available". +Winbond SL82c105 support +CONFIG_BLK_DEV_SL82C105 + If you have a Winbond SL82c105 IDE controller, say Y here to enable + special configuration for this chip. This is common on various CHRP + motherboards, but could be used elsewhere. If in doubt, say Y. - Please read the comments at the top of drivers/block/aec6210.c +Tekram TRM290 chipset support (EXPERIMENTAL) +CONFIG_BLK_DEV_TRM290 + This driver adds support for bus master DMA transfers + using the Tekram TRM290 PCI IDE chip. Volunteers are + needed for further tweaking and development. + Please read the comments at the top of drivers/block/trm290.c. -HPT366 chipset support -CONFIG_BLK_DEV_HPT366 - This is an Ultra DMA chipset for ATA-66. - - This driver adds up to 4 more EIDE devices sharing a single - interrupt. The HPT366 chipset in its current form is a non-bootable. - This driver requires dynamic tuning of the chipset during the - ide-probe at boot. It is reported to support DVD II drives, by the - manufacturer. - -Intel PIIXn chipsets support -CONFIG_BLK_DEV_PIIX - This driver adds PIO mode setting and tuning for all PIIX IDE - controllers by Intel. Since the BIOS can sometimes improperly tune - PIO 0-4 mode settings, this allows dynamic tuning of the chipset - via the standard end-user tool 'hdparm'. +VIA82CXXX chipset support (EXPERIMENTAL) +CONFIG_BLK_DEV_VIA82CXXX + This allows you to to configure your chipset for a better use while + running (U)DMA: it will allow you to enable efficiently the second + channel dma usage, as it is may not be set by BIOS. It allows you to + run a kernel command line at boot time in order to set fifo config. + If no command line is provided, it will try to set fifo configuration + at its best. It will allow you to get a proc/ide/via display + (while running a "cat") provided you enabled "proc" support. + Please read the comments at the top of drivers/block/via82cxxx.c - Please read the comments at the top of drivers/block/piix.c + If you say Y here, you also need to say Y to "Use DMA by default + when available", above. If unsure, say N. -PIIXn Tuning support (EXPERIMENTAL) -CONFIG_BLK_DEV_PIIX_TUNING - This driver extension adds DMA mode setting and tuning for all PIIX - IDE controllers by Intel. Since the BIOS can sometimes improperly - set up the device/adapter combination and speed limits, it has - become a necessity to back/forward speed devices as needed. +Other IDE chipset support +CONFIG_IDE_CHIPSETS + Say Y here if you want to include enhanced support for various IDE + interface chipsets used on motherboards and add-on cards. You can + then pick your particular IDE chip from among the following options. + This enhanced support may be necessary for Linux to be able to + access the 3rd/4th drives in some systems. It may also enable + setting of higher speed I/O rates to improve system performance with + these chipsets. Most of these also require special kernel boot + parameters to actually turn on the support at runtime; you can find + a list of these in the file Documentation/ide.txt. + + People with SCSI-only systems can say N here. - Case 430HX/440FX PIIX3 need speed limits to reduce UDMA to DMA mode - 2 if the BIOS can not perform this task at initialization. +Generic 4 drives/port support +CONFIG_BLK_DEV_4DRIVES + Certain older chipsets, including the Tekram 690CD, use a single set + of I/O ports at 0x1f0 to control up to four drives, instead of the + customary two drives per port. Support for this can be enabled at + runtime using the "ide0=four" kernel boot parameter if you say Y + here. - If unsure, say N. +ALI M14xx support +CONFIG_BLK_DEV_ALI14XX + This driver is enabled at runtime using the "ide0=ali14xx" kernel + boot parameter. It enables support for the secondary IDE interface + of the ALI M1439/1443/1445/1487/1489 chipsets, and permits faster + I/O speeds to be set as well. See the files Documentation/ide.txt + and drivers/block/ali14xx.c for more info. -HPT34X chipset support -CONFIG_BLK_DEV_HPT34X - This driver adds up to 4 more EIDE devices sharing a single - interrupt. The HPT343 chipset in its current form is a non-bootable - controller; the HPT345/HPT363 chipset is a bootable (needs BIOS FIX) - PCI UDMA controllers. This driver requires dynamic tuning of the - chipset during the ide-probe at boot time. It is reported to support - DVD II drives, by the manufacturer. +DTC-2278 support +CONFIG_BLK_DEV_DTC2278 + This driver is enabled at runtime using the "ide0=dtc2278" kernel + boot parameter. It enables support for the secondary IDE interface + of the DTC-2278 card, and permits faster I/O speeds to be set as + well. See the Documentation/ide.txt and drivers/block/dtc2278.c + files for more info. -HPT34X DMA support (DANGEROUS) -CONFIG_BLK_DEV_HPT34X_DMA - you need to say Y to "Use DMA by default when available" if you say - Y here. +Holtek HT6560B support +CONFIG_BLK_DEV_HT6560B + This driver is enabled at runtime using the "ide0=ht6560b" kernel + boot parameter. It enables support for the secondary IDE interface + of the Holtek card, and permits faster I/O speeds to be set as well. + See the Documentation/ide.txt and drivers/block/ht6560b.c files for + more info. - Please read the comments at the top of drivers/block/hpt343.c +PROMISE DC4030 support (EXPERIMENTAL) +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. CDROM and TAPE devices are not + supported yet. This driver is enabled at runtime using the + "ide0=dc4030" kernel boot parameter. See the Documentation/ide.txt + and drivers/block/pdc4030.c files for more info. QDI QD6580 support CONFIG_BLK_DEV_QD6580 @@ -858,13 +879,15 @@ CONFIG_BLK_DEV_UMC8672 See the files Documentation/ide.txt and drivers/block/umc8672.c for more info. -ALI M14xx support -CONFIG_BLK_DEV_ALI14XX - This driver is enabled at runtime using the "ide0=ali14xx" kernel - boot parameter. It enables support for the secondary IDE interface - of the ALI M1439/1443/1445/1487/1489 chipsets, and permits faster - I/O speeds to be set as well. See the files Documentation/ide.txt - and drivers/block/ali14xx.c for more info. +PS/2 ESDI hard disk support +CONFIG_BLK_DEV_PS2 + Say Y here if you have a PS/2 machine with a MCA bus and an ESDI + hard disk. + + If you want to compile the driver as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read Documentation/modules.txt. The module will be + called ps2esdi.o. Amiga builtin Gayle IDE interface support CONFIG_BLK_DEV_GAYLE @@ -1903,20 +1926,41 @@ CONFIG_ISAPNP If unsure, say Y. -PCMCIA/Cardbus support +PCMCIA/CardBus support CONFIG_PCMCIA - Say Y here if you want to attach PCMCIA's (PC-cards) to your Linux - computer. These are credit-card size devices such as network cards, - modems or hard drives popular with laptops. + Include kernel support for PCMCIA and CardBus devices. Because + PCMCIA support requires additional components that are not part of + the kernel (i.e., the pcmcia-cs package), building PCMCIA into the + kernel is generally not recommended unless you have a specific + need. If unsure, say N. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + When compiled this way, there will be modules called pcmcia_core.o + and ds.o. If you want to compile it as a module, say M here and + read Documentation/modules.txt. You will also need David Hinds' pcmcia-cs package (see the file - Documentation/Changes for location). + Documentation/Changes for location). For more information, see the + PCMCIA-HOWTO. CardBus support CONFIG_CARDBUS - CardBus is a bus mastering architecture for PC-cards (it allows - PC-cards to talk to the rest of the stuff inside your computer). If - unsure, say Y. + There are two types of PCMCIA devices: 16-bit PC Cards, and higher + performance 32-bit CardBus devices. Use this option to include + support for CardBus devices. If unsure, say Y. + +i82365/Yenta compatible bridge support +CONFIG_I82365 + Include support for PCMCIA and CardBus host bridges that are + register compatible with the Intel i82365 and/or the Yenta + specification: this includes virtually all modern PCMCIA bridges. + If unsure, say Y. + +Databook TCIC host bridge support +CONFIG_TCIC + Include support for the Databook TCIC family of PCMCIA host bridges. + These are only found on a handful of old systems. If unsure, say N. System V IPC CONFIG_SYSVIPC @@ -5549,10 +5593,48 @@ CONFIG_X25_ASY say M here and read Documentation/modules.txt. The module will be called x25_asy.o. If unsure, say N. -PCMCIA ethernet cards (NE2000 compatibles: DE-650, ...) +PCMCIA network device support +CONFIG_NET_PCMCIA + Say Y if you would like to include support for any PCMCIA network + adapters. If unsure, say N. + +3Com 3c589 PCMCIA support +CONFIG_PCMCIA_3C589 + Say Y here if you intend to attach a 3Com 3c589 or compatible PCMCIA + (PC-card) Ethernet card to your computer. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called 3c589_cs.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. If unsure, + say N. + +3Com 3c574 PCMCIA support +CONFIG_PCMCIA_3C574 + Say Y here if you intend to attach a 3Com 3c574 or compatible PCMCIA + (PC-card) Fast Ethernet card to your computer. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called 3c574_cs.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. If unsure, + say N. + +Fujitsu FMV-J18x PCMCIA support +CONFIG_PCMCIA_FMVJ18X + Say Y here if you intend to attach a Fujitsu FMV-J18x or compatible + PCMCIA (PC-card) Ethernet card to your computer. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called fmvj18x_cs.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. If unsure, + say N. + +NE2000 compatible PCMCIA support CONFIG_PCMCIA_PCNET Say Y here if you intend to attach an NE2000 compatible PCMCIA - (PC-card) Ethernet networking card to your computer. + (PC-card) Ethernet or Fast Ethernet card to your computer. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -5560,18 +5642,40 @@ CONFIG_PCMCIA_PCNET module, say M here and read Documentation/modules.txt. If unsure, say N. -3Com 3c589 PCMCIA card -CONFIG_PCMCIA_3C589 - Say Y here if you intend to attach a 3Com 3c589 PCMCIA - (PC-card) Ethernet networking card to your computer. +New Media PCMCIA support +CONFIG_PCMCIA_NMCLAN + Say Y here if you intend to attach a New Media Ethernet or LiveWire + PCMCIA (PC-card) Ethernet card to your computer. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). - The module will be called 3c589_cs.o. If you want to compile it as a + The module will be called nmclan_cs.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. If unsure, say N. -Aviator/Raytheon 2.4MHz wireless +SMC 91Cxx PCMCIA support +CONFIG_PCMCIA_SMC91C92 + Say Y here if you intend to attach an SMC 91Cxx compatible PCMCIA + (PC-card) Ethernet or Fast Ethernet card to your computer. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called smc91c92_cs.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. If unsure, + say N. + +Xircom 16-bit PCMCIA support +CONFIG_PCMCIA_XIRC2PS + Say Y here if you intend to attach a Xircom 16-bit PCMCIA + (PC-card) Ethernet or Fast Ethernet card to your computer. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called xirc2ps_cs.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. If unsure, + say N. + +Aviator/Raytheon 2.4MHz wireless support CONFIG_PCMCIA_RAYCS Say Y here if you intend to attach an Aviator/Raytheon PCMCIA (PC-card) wireless Ethernet networking card to your computer. @@ -5582,6 +5686,29 @@ CONFIG_PCMCIA_RAYCS module, say M here and read Documentation/modules.txt. If unsure, say N. +Xircom Netwave AirSurfer wireless support +CONFIG_PCMCIA_NETWAVE + Say Y here if you intend to attach a Xircom Netwave AirSurfer PCMCIA + (PC-card) wireless Ethernet networking card to your computer. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called netwave_cs.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. If unsure, + say N. + +AT&T/Lucent Wavelan wireless support +CONFIG_PCMCIA_WAVELAN + Say Y here if you intend to attach an AT&T/Lucent Wavelan PCMCIA + (PC-card) wireless Ethernet networking card to your computer. This + driver is for the non-IEEE-802.11 Wavelan cards. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called wavelan_cs.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. If unsure, + say N. + PLIP (parallel port) support CONFIG_PLIP PLIP (Parallel Line Internet Protocol) is used to create a @@ -12268,18 +12395,44 @@ Include support for the NetWinder CONFIG_ARCH_NETWINDER Say Y here if you intend to run this kernel on the NetWinder. -Maximum Physical Memory +Virtual/Physical Memory Split CONFIG_1GB - Linux can use up to 2 Gigabytes (= 2^31 bytes) of physical memory. - If you are compiling a kernel which will never run on a machine with - more than 1 Gigabyte, answer "1GB" here. Otherwise, say "2GB". - - The actual amount of physical memory should be specified using a - kernel command line option such as "mem=256M". (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. The lilo procedure - is also explained in the SCSI-HOWTO, available from - http://metalab.unc.edu/mdw/linux.html#howto .) + If you are compiling a kernel which will never run on a machine + with more than 1 Gigabyte total physical RAM, answer "3GB/1GB" + here (default choice). + + On 32-bit x86 systems Linux can use up to 64 Gigabytes of physical + memory. However 32-bit x86 processors have only 4 Gigabytes of + virtual memory space. This option specifies the maximum amount of + virtual memory space one process can potentially use. Certain types + of applications (eg. database servers) perform better if they have + as much virtual memory per process as possible. + + The remaining part of the 4G virtual memory space is used by the + kernel to 'permanently map' as much physical memory as possible. + Certain types of applications perform better if there is more + 'permanently mapped' kernel memory. + + [WARNING! Certain boards do not support PCI DMA to physical addresses + bigger than 2 Gigabytes. Non-DMA-able memory must not be permanently + mapped by the kernel, thus a 1G/3G split will not work on such boxes.] + + As you can see there is no 'perfect split' - the fundamental + problem is that 4G of 32-bit virtual memory space is short. So + you'll have to pick your own choice - depending on the application + load of your box. A 2G/2G split is typically a good choice for a + generic Linux server with lots of RAM. + + Any potentially remaining (not permanently mapped) part of physical + memory is called 'high memory'. How much total high memory the kernel + can handle is influenced by the (next) High Memory configuration option. + + The actual amount of total physical memory will either be + autodetected or can be forced by using a kernel command line option + such as "mem=256M". (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. The lilo procedure is also explained in the + SCSI-HOWTO, available from http://metalab.unc.edu/mdw/linux.html#howto .) Math emulation CONFIG_NWFPE @@ -12890,7 +13043,7 @@ CONFIG_KHTTPD # LocalWords: KERNNAME kname ktype kernelname Kerneltype KERNTYPE Alt RX mdafb # LocalWords: dataless kerneltype SYSNAME Comtrol Rocketport palmtop fbset EGS # LocalWords: nvram SYSRQ SysRq PrintScreen sysrq NVRAMs NvRAM Shortwave RTTY -# LocalWords: Sitor Amtor Pactor GTOR hayes TX TMOUT JFdocs BIGMEM DAC IRQ's +# LocalWords: Sitor Amtor Pactor GTOR hayes TX TMOUT JFdocs HIGHMEM DAC IRQ's # LocalWords: IDEPCI IDEDMA idedma PDC pdc TRM trm raidtools luthien nuclecu # LocalWords: unam mx miguel koobera uic EMUL solaris pp ieee lpsg co DMAs TOS # LocalWords: BLDCONFIG preloading jumperless BOOTINIT modutils multipath GRE diff --git a/Documentation/arm/SA1100/Brutus b/Documentation/arm/SA1100/Brutus new file mode 100644 index 000000000..b01ba4b29 --- /dev/null +++ b/Documentation/arm/SA1100/Brutus @@ -0,0 +1,36 @@ +Brutus is an evaluation platform for the SA1100 manufactured by Intel. +For more details, see: + +http://developer.intel.com/design/strong/applnots/sa1100lx/getstart.htm + +To compile for Brutus, you must issue the following comands: + + make brutus_config + make config + [accept all the defaults] + make dep + make zImage + +The resulting kernel will end up in linux/arch/arm/boot/zImage. This file +must be loaded at 0xc0008000 in Brutus's memory and execution started at +0xc0008000 as well. + +But prior to execute the kernel, a ramdisk image must also be loaded in +memory. Use memory address 0x00800000 for this. + +Currently supported: + - RS232 serial ports + - audio output + - LCD screen + - keyboard (needs to be cleaned up badly... any volunteer?) + +A full PCMCIA support is still missing, although it's possible to hack +some drivers in order to drive already inserted cards at boot time with +little modifications. + +Any contribution is welcome. + +Please send patches to nico@cam.org + +Have Fun ! + diff --git a/Documentation/arm/SA1100/Itsy b/Documentation/arm/SA1100/Itsy new file mode 100644 index 000000000..a2b3ecda3 --- /dev/null +++ b/Documentation/arm/SA1100/Itsy @@ -0,0 +1,12 @@ +Itsy is a research project done by the Western Research Lab, and Systems +Research Center in Palo Alto, CA. The Itsy project is one of several +research projects at Compaq that are related to pocket computing. + +Itsy support has yet to be fully integrated in this kernel. Linux 2.0.x +support is available though. + +For more information, see: + + http://www.research.digital.com/wrl/itsy/index.html + + diff --git a/Documentation/arm/SA1100/LART b/Documentation/arm/SA1100/LART new file mode 100644 index 000000000..5d4084c04 --- /dev/null +++ b/Documentation/arm/SA1100/LART @@ -0,0 +1,17 @@ +Linux Advanced Radio Terminal (LART) +------------------------------------ + +The LART is a small (7.5 x 10cm) SA-1100 board, designed for embedded +applications. It has 32 MB DRAM, 4MB Flash ROM, double RS232 and all +other StrongARM-gadgets. Almost all SA signals are directly accessible +through a number of connectors. The powersupply accepts voltages +between 3.5V and 16V and is overdimensioned to support a range of +daughterboards. A quad Ethernet / IDE / PS2 / sound daughterboard +is under development, with plenty of others in different stages of +planning. + +The designs for this board are to be released under a GPL-like license; +we're working on the details. + +Contact: J.D. Bakker <bakker@thorgal.et.tudelft.nl>; +pictures at http://www-ict.its.tudelft.nl/~erik/open-source/LART/ diff --git a/Documentation/arm/SA1100/PLEB b/Documentation/arm/SA1100/PLEB new file mode 100644 index 000000000..92cae0669 --- /dev/null +++ b/Documentation/arm/SA1100/PLEB @@ -0,0 +1,11 @@ +The PLEB project was started as a student initiative at the School of +Computer Science and Engineering, University of New South Wales to make a +pocket computer capable of running the Linux Kernel. + +PLEB support has yet to be fully integrated. + +For more information, see: + + http://www.cse.unsw.edu.au/~pleb/ + + diff --git a/Documentation/arm/SA1100/Tifon b/Documentation/arm/SA1100/Tifon new file mode 100644 index 000000000..dd1934d9c --- /dev/null +++ b/Documentation/arm/SA1100/Tifon @@ -0,0 +1,7 @@ +Tifon +----- + +More info has to come... + +Contact: Peter Danielsson <peter.danielsson@era-t.ericsson.se> + diff --git a/Documentation/arm/SA1100/Victor b/Documentation/arm/SA1100/Victor new file mode 100644 index 000000000..01e81fc49 --- /dev/null +++ b/Documentation/arm/SA1100/Victor @@ -0,0 +1,16 @@ +Victor is known as a "digital talking book player" manufactured by +VisuAide, Inc. to be used by blind people. + +For more information related to Victor, see: + + http://www.visuaide.com/victor + +Of course Victor is using Linux as its main operating system. +The Victor implementation for Linux is maintained by Nicolas Pitre: + + nico@visuaide.com + nico@cam.org + +For any comments, please feel free to contact me through the above +addresses. + diff --git a/Documentation/arm/SA1100/empeg b/Documentation/arm/SA1100/empeg new file mode 100644 index 000000000..4ece4849a --- /dev/null +++ b/Documentation/arm/SA1100/empeg @@ -0,0 +1,2 @@ +See ../empeg/README + diff --git a/Documentation/arm/Setup b/Documentation/arm/Setup new file mode 100644 index 000000000..44aa9bc1b --- /dev/null +++ b/Documentation/arm/Setup @@ -0,0 +1,121 @@ +Kernel initialisation parameters on ARM Linux +--------------------------------------------- + +The following document describes the kernel initialisation parameter +structure, otherwise known as 'struct param_struct' which is used +for most ARM Linux architectures. + +This structure is used to pass initialisation parameters from the +kernel loader to the Linux kernel proper, and may be short lived +through the kernel initialisation process. As a general rule, it +should not be referenced outside of arch/arm/kernel/setup.c:setup_arch(). + +There are a lot of parameters listed in there, and they are described +below: + + page_size + + This parameter must be set to the page size of the machine, and + will be checked by the kernel. + + nr_pages + + This is the total number of pages of memory in the system. If + the memory is banked, then this should contain the total number + of pages in the system. + + If the system contains separate VRAM, this value should not + include this information. + + ramdisk_size + + This is now obsolete, and should not be used. + + flags + + Various kernel flags, including: + bit 0 - 1 = mount root read only + bit 1 - unused + bit 2 - 0 = load ramdisk + bit 3 - 0 = prompt for ramdisk + + rootdev + + major/minor number pair of device to mount as the root filesystem. + + video_num_cols + video_num_rows + + These two together describe the character size of the dummy console, + or VGA console character size. They should not be used for any other + purpose. + + It's generally a good idea to set these to be either standard VGA, or + the equivalent character size of your fbcon display. This then allows + all the bootup messages to be displayed correctly. + + video_x + video_y + + This describes the character position of cursor on VGA console, and + is otherwise unused. (should not used for other console types, and + should not be used for other purposes). + + memc_control_reg + + MEMC chip control register for Acorn Archimedes and Acorn A5000 + based machines. May be used differently by different architectures. + + sounddefault + + Default sound setting on Acorn machines. May be used differently by + different architectures. + + adfsdrives + + Number of ADFS/MFM disks. May be used differently by different + architectures. + + bytes_per_char_h + bytes_per_char_v + + These are now obsolete, and should not be used. + + pages_in_bank[4] + + Number of pages in each bank of the systems memory (used for RiscPC). + This is intended to be used on systems where the physical memory + is non-contiguous from the processors point of view. + + pages_in_vram + + Number of pages in VRAM (used on Acorn RiscPC). This value may also + be used by loaders if the size of the video RAM can't be obtained + from the hardware. + + initrd_start + initrd_size + + This describes the kernel virtual start address and size of the + inital ramdisk. + + rd_start + + Start address in sectors of the ramdisk image on a floppy disk. + + system_rev + + system revision number. + + system_serial_low + system_serial_high + + system 64-bit serial number + + paths[8][128] + + These are now obsolete, and should not be used. + + commandline + + Kernel command line parameters. Details can be found elsewhere. diff --git a/Documentation/arm/empeg/README b/Documentation/arm/empeg/README new file mode 100644 index 000000000..09cc8d03a --- /dev/null +++ b/Documentation/arm/empeg/README @@ -0,0 +1,13 @@ +Empeg, Ltd's Empeg MP3 Car Audio Player + +The initial design is to go in your car, but you can use it at home, on a +boat... almost anywhere. The principle is to store CD-quality music using +MPEG technology onto a hard disk in the unit, and use the power of the +embedded computer to serve up the music you want. + +For more details, see: + + http://www.empeg.com + + + diff --git a/Documentation/arm/empeg/ir.txt b/Documentation/arm/empeg/ir.txt new file mode 100644 index 000000000..c82e9c513 --- /dev/null +++ b/Documentation/arm/empeg/ir.txt @@ -0,0 +1,49 @@ +Infra-red driver documentation. + +Mike Crowe <mac@empeg.com> +(C) Empeg Ltd 1999 + +Not a lot here yet :-) + +The Kenwood KCA-R6A remote control generates a sequence like the following: + +Go low for approx 16T (Around 9000us) +Go high for approx 8T (Around 4000us) +Go low for less than 2T (Around 750us) + +For each of the 32 bits + Go high for more than 2T (Around 1500us) == 1 + Go high for less than T (Around 400us) == 0 + Go low for less than 2T (Around 750us) + +Rather than repeat a signal when the button is held down certain buttons +generate the following code to indicate repitition. + +Go low for approx 16T +Go high for approx 4T +Go low for less than 2T + +(By removing the <2T from the start of the sequence and placing at the end + it can be considered a stop bit but I found it easier to deal with it at + the start). + +The 32 bits are encoded as XxYy where x and y are the actual data values +while X and Y are the logical inverses of the associated data values. Using +LSB first yields sensible codes for the numbers. + +All codes are of the form b9xx + +The numeric keys generate the code 0x where x is the number pressed. + +Tuner 1c +Tape 1d +CD 1e +CD-MD-CH 1f +Track- 0a +Track+ 0b +Rewind 0c +FF 0d +DNPP 5e +Play/Pause 0e +Vol+ 14 +Vol- 15 diff --git a/Documentation/arm/empeg/mkdevs b/Documentation/arm/empeg/mkdevs new file mode 100644 index 000000000..7a85e28d1 --- /dev/null +++ b/Documentation/arm/empeg/mkdevs @@ -0,0 +1,11 @@ +#!/bin/sh +mknod /dev/display c 244 0 +mknod /dev/ir c 242 0 +mknod /dev/usb0 c 243 0 +mknod /dev/audio c 245 4 +mknod /dev/dsp c 245 3 +mknod /dev/mixer c 245 0 +mknod /dev/empeg_state c 246 0 +mknod /dev/radio0 c 81 64 +ln -sf radio0 radio +ln -sf usb0 usb diff --git a/Documentation/arm/nwfpe/README b/Documentation/arm/nwfpe/README index f05756ef0..884fc5a50 100644 --- a/Documentation/arm/nwfpe/README +++ b/Documentation/arm/nwfpe/README @@ -35,7 +35,7 @@ so far in the emulator. The file TODO contains a information on what remains to be done, and other ideas for the emulator. Bug reports, comments, suggestions should be directed to me at -<scottb@corelcomputer.com>. General reports of "this program doesn't +<scottb@netwinder.com>. General reports of "this program doesn't work correctly when your emulator is installed" are useful for determining that bugs still exist; but are virtually useless when attempting to isolate the problem. Please report them, but don't @@ -46,7 +46,7 @@ problem are a godsend. Legal Notices ------------- -The NetWinder Floating Point Emulator is free software. Everything Corel +The NetWinder Floating Point Emulator is free software. Everything Rebel.com has written is provided under the GNU GPL. See the file COPYING for copying conditions. Excluded from the above is the SoftFloat code. John Hauser's legal notice for SoftFloat is included below. diff --git a/Documentation/arm/nwfpe/README.FPE b/Documentation/arm/nwfpe/README.FPE index c0498f4d1..26f5d7bb9 100644 --- a/Documentation/arm/nwfpe/README.FPE +++ b/Documentation/arm/nwfpe/README.FPE @@ -139,10 +139,10 @@ be implemented in future versions. Signalling: -Signals are implemented. However current ELF kernels produced by Corel -Computer have a bug in them that prevents the module from generating a -SIGFPE. This is caused by a failure to alias fp_current to the kernel -variable current_set[0] correctly. +Signals are implemented. However current ELF kernels produced by Rebel.com +have a bug in them that prevents the module from generating a SIGFPE. This +is caused by a failure to alias fp_current to the kernel variable +current_set[0] correctly. The kernel provided with this distribution (vmlinux-nwfpe-0.93) contains a fix for this problem and also incorporates the current version of the diff --git a/Documentation/pci.txt b/Documentation/pci.txt index 4536c87da..86d0a58c4 100644 --- a/Documentation/pci.txt +++ b/Documentation/pci.txt @@ -4,10 +4,19 @@ "What should you avoid when writing PCI drivers" - by Martin Mares <mj@atrey.karlin.mff.cuni.cz> on 17-Jun-1999 + by Martin Mares <mj@atrey.karlin.mff.cuni.cz> on 09-Oct-1999 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +0. Structure of PCI drivers +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Aa typical PCI device driver needs to perform the following actions: + + 1. Find PCI devices it's able to handle + 2. Enable them + 3. Access their configuration space + 4. Discover addresses and IRQ numbers + 1. How to find PCI devices ~~~~~~~~~~~~~~~~~~~~~~~~~~ In case your driver wants to search for all devices with given vendor/device @@ -19,6 +28,9 @@ ID, it should use: For class-based search, use pci_find_class(CLASS_ID, dev). + If you need to match by subsystem vendor/device ID, use +pci_find_subsys(VENDOR_ID, DEVICE_ID, SUBSYS_VENDOR_ID, SUBSYS_DEVICE_ID, dev). + You can use the constant PCI_ANY_ID as a wildcard replacement for VENDOR_ID or DEVICE_ID. This allows searching for any device from a specific vendor, for example. @@ -26,44 +38,57 @@ specific vendor, for example. In case you want to do some complex matching, look at pci_devices -- it's a linked list of pci_dev structures for all PCI devices in the system. - All these methods return a pointer to a pci_dev structure which is used as a -parameter for many other PCI functions. The rest of them accept bus and -device/function numbers which can be found in pci_dev->bus->number and -pci_dev->devfn. Feel free to use all other fields of the pci_dev structure, but -don't modify them. + The `struct pci_dev *' pointer serves as an identification of a PCI device +and is passed to all other functions operating on PCI devices. + +2. Enabling devices +~~~~~~~~~~~~~~~~~~~ + Before you do anything with the device you've found, you need to enable +it by calling pci_enable_device() which enables I/O and memory regions of +the device, assigns missing resources if needed and wakes up the device +if it was in suspended state. Please note that this function can fail. - The pci_present() function can be used to test presence of PCI in the -machine. + If you want to use the device in bus mastering mode, call pci_set_master() +which enables the bus master bit in PCI_COMMAND register and also fixes +the latency timer value if it's set to something bogus by the BIOS. -2. How to access PCI config space +3. How to access PCI config space ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You can use pci_(read|write)_config_(byte|word|dword) to access the config -space of a device represented by pci_dev. All these functions return 0 when -successful or an error code (PCIBIOS_...) which can be translated to a text +space of a device represented by struct pci_dev *. All these functions return 0 +when successful or an error code (PCIBIOS_...) which can be translated to a text string by pcibios_strerror. Most drivers expect that accesses to valid PCI devices don't fail. - In case you want to address the devices by bus/device/function numbers, -use pcibios_(read_write)_config_(byte|word|dword). - If you access fields in the standard portion of the config header, please use symbolic names of locations and bits declared in <linux/pci.h>. -3. Addresses and interrupts + If you need to access Extended PCI Capability registers, just call +pci_find_capability() for the particular capability and it will find the +corresponding register block for you. + +4. Addresses and interrupts ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Memory and port addresses and interrupt numbers should NOT be read from the config space. You should use the values in the pci_dev structure as they might have been remapped by the kernel. -4. Obsolete functions -~~~~~~~~~~~~~~~~~~~~~ -<linux/bios32.h> is obsolete and should not be included in new code. +5. Other interesting functions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +pci_find_slot() Find pci_dev corresponding to given bus and + slot numbers. +pci_set_power_state() Set PCI Power Management state (0=D0 ... 3=D3) -pcibios_find_(device|class) are also obsolete and should be replaced by -pci_find_(device|class). - -5. Bus mastering -~~~~~~~~~~~~~~~~ - If you need to setup a bus-mastering card, just call pci_set_master(). It -should set PCI_COMMAND_MASTER in the command register and adjust the latency -timer if needed. +6. Obsolete functions +~~~~~~~~~~~~~~~~~~~~~ +There are several functions kept only for compatibility with old drivers +not updated to the new PCI interface. Please don't use them in new code. + +pcibios_present() Since ages, you don't need to test presence + of PCI subsystem when trying to talk with it. + If it's not there, the list of PCI devices + is empty and all functions for searching for + devices just return NULL. +pcibios_(read|write)_* Superseded by their pci_(read|write)_* + counterparts. +pcibios_find_* Superseded by their pci_find_* counterparts. diff --git a/Documentation/vm/locking b/Documentation/vm/locking new file mode 100644 index 000000000..6efdcec3e --- /dev/null +++ b/Documentation/vm/locking @@ -0,0 +1,88 @@ +Started Oct 1999 by Kanoj Sarcar <kanoj@sgi.com> + +The intent of this file is to have an uptodate, running commentary +from different people about how locking and synchronization is done +in the Linux vm code. + +vmlist_access_lock/vmlist_modify_lock +-------------------------------------- + +Page stealers pick processes out of the process pool and scan for +the best process to steal pages from. To guarantee the existance +of the victim mm, a mm_count inc and a mmdrop are done in swap_out(). +Page stealers hold kernel_lock to protect against a bunch of races. +The vma list of the victim mm is also scanned by the stealer, +and the vmlist_lock is used to preserve list sanity against the +process adding/deleting to the list. This also gurantees existance +of the vma. Vma existance gurantee while invoking the driver +swapout() method in try_to_swap_out() also relies on the fact +that do_munmap() temporarily gets lock_kernel before decimating +the vma, thus the swapout() method must snapshot all the vma +fields it needs before going to sleep (which will release the +lock_kernel held by the page stealer). Currently, filemap_swapout +is the only method that depends on this shaky interlocking. + +Any code that modifies the vmlist, or the vm_start/vm_end/ +vm_flags:VM_LOCKED/vm_next of any vma *in the list* must prevent +kswapd from looking at the chain. This does not include driver mmap() +methods, for example, since the vma is still not in the list. + +The rules are: +1. To modify the vmlist (add/delete or change fields in an element), +you must hold mmap_sem to guard against clones doing mmap/munmap/faults, +(ie all vm system calls and faults), and from ptrace, swapin due to +swap deletion etc. +2. To modify the vmlist (add/delete or change fields in an element), +you must also hold vmlist_modify_lock, to guard against page stealers +scanning the list. +3. To scan the vmlist (find_vma()), you must either + a. grab mmap_sem, which should be done by all cases except + page stealer. +or + b. grab vmlist_access_lock, only done by page stealer. +4. While holding the vmlist_modify_lock, you must be able to guarantee +that no code path will lead to page stealing. A better guarantee is +to claim non sleepability, which ensures that you are not sleeping +for a lock, whose holder might in turn be doing page stealing. +5. You must be able to guarantee that while holding vmlist_modify_lock +or vmlist_access_lock of mm A, you will not try to get either lock +for mm B. + +The caveats are: +1. find_vma() makes use of, and updates, the mmap_cache pointer hint. +The update of mmap_cache is racy (page stealer can race with other code +that invokes find_vma with mmap_sem held), but that is okay, since it +is a hint. This can be fixed, if desired, by having find_vma grab the +vmlist lock. + + +Code that add/delete elements from the vmlist chain are +1. callers of insert_vm_struct +2. callers of merge_segments +3. callers of avl_remove + +Code that changes vm_start/vm_end/vm_flags:VM_LOCKED of vma's on +the list: +1. expand_stack +2. mprotect +3. mlock +4. mremap + +It is advisable that changes to vm_start/vm_end be protected, although +in some cases it is not really needed. Eg, vm_start is modified by +expand_stack(), it is hard to come up with a destructive scenario without +having the vmlist protection in this case. + +The vmlist lock nests with the inode i_shared_lock and the kmem cache +c_spinlock spinlocks. This is okay, since code that holds i_shared_lock +never asks for memory, and the kmem code asks for pages after dropping +c_spinlock. + +The vmlist lock can be a sleeping or spin lock. In either case, care +must be taken that it is not held on entry to the driver methods, since +those methods might sleep or ask for memory, causing deadlocks. + +The current implementation of the vmlist lock uses the page_table_lock, +which is also the spinlock that page stealers use to protect changes to +the victim process' ptes. Thus we have a reduction in the total number +of locks. diff --git a/MAINTAINERS b/MAINTAINERS index 67c855554..e92c24e87 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -396,6 +396,12 @@ M: mikulas@artax.karlin.mff.cuni.cz W: http://artax.karlin.mff.cuni.cz/~mikulas/vyplody/hpfs/index-e.cgi S: Maintained +i386 BOOT CODE +P: Riley H. Williams +M: rhw@memalpha.cx +L: Linux-Kernel@vger.rutgers.edu +S: Maintained + IBM MCA SCSI SUBSYSTEM DRIVER P: Michael Lang M: langa2@kph.uni-mainz.de @@ -502,8 +508,9 @@ S: Maintained LINUX FOR POWER MACINTOSH P: Paul Mackerras -M: paulus@cs.anu.edu.au -L: linux-pmac@samba.anu.edu.au +M: paulus@linuxcare.com +W: http://www.linuxppc.org/ +L: linuxppc-dev@lists.linuxppc.org S: Maintained M68K @@ -687,6 +694,7 @@ PCMCIA SUBSYSTEM P: David Hinds M: dhinds@zen.stanford.edu L: linux-kernel@vger.rutgers.edu +W: http://pcmcia.sourceforge.org S: Maintained PCNET32 NETWORK DRIVER @@ -831,6 +839,16 @@ STARMODE RADIO IP (STRIP) PROTOCOL DRIVER W: http://mosquitonet.Stanford.EDU/strip.html S: Unsupported ? +SUPERH +P: Niibe Yutaka +M: gniibe@chroot.org +P: Kazumoto Kojima +M: kkojima@rr.iij4u.or.jp +L: linux-sh@m17n.org +W: http://www.m17n.org/linux-sh/ +W: http://www.rr.iij4u.or.jp/~kkojima/linux-sh4.html +S: Maintained + SVGA HANDLING P: Martin Mares M: mj@atrey.karlin.mff.cuni.cz @@ -1,6 +1,6 @@ VERSION = 2 PATCHLEVEL = 3 -SUBLEVEL = 22 +SUBLEVEL = 23 EXTRAVERSION = ARCH = mips diff --git a/arch/alpha/mm/init.c b/arch/alpha/mm/init.c index 86cf4c925..0ec65b409 100644 --- a/arch/alpha/mm/init.c +++ b/arch/alpha/mm/init.c @@ -166,6 +166,7 @@ show_mem(void) printk("%ld pages shared\n",shared); printk("%ld pages swap cached\n",cached); printk("%ld pages in page table cache\n",pgtable_cache_size); + show_buffers(); #ifdef CONFIG_NET show_net_buffers(); #endif diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 11fefd85b..0895fc0ba 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -12,91 +12,101 @@ # # Copyright (C) 1995-1999 by Russell King -# GCC 2.7 uses different options to later compilers; sort out which we have -CONFIG_GCC_NEW := $(shell if $(CC) --version 2>&1 | grep '^2\.7' > /dev/null; then echo n; else echo y; fi) +LD := $(CROSS_COMPILE)ld +OBJCOPY := $(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -S +CPP := $(CC) -E +PERL := perl +LINKFLAGS := -X -T arch/arm/vmlinux.lds +ARCHCC := $(word 1,$(CC)) + + +CFLAGS_PIPE := -pipe +CFLAGS := $(CFLAGS) $(CFLAGS_PIPE) + +ifdef CONFIG_FRAME_POINTER +CFLAGS := $(CFLAGS:-fomit-frame-pointer=) +endif + +ifdef CONFIG_DEBUG_INFO +CFLAGS += -g +endif -# See if this is ld "2.9.4" or later -NEW_LINKER := $(shell if $(LD) --gc-sections --version >/dev/null 2>&1; then echo y; else echo n; fi) -# CFLAGS_PROC - processor dependent CFLAGS -# PROCESSOR - processor type -# TEXTADDR - Uncompressed kernel link text address -# ZTEXTADDR - Compressed kernel link text address -# ZRELADDR - Compressed kernel relocating address -# (point at which uncompressed kernel is loaded). +# GCC 2.7 uses different options to later compilers; sort out which we have +NEW_GCC := $(shell if $(CC) --version 2>&1 | grep '^2\.7' > /dev/null; then echo n; else echo y; fi) # # select flags depending on the compiler # -ifeq ($(CONFIG_GCC_NEW),y) - CFLAGS_PROC := -mshort-load-bytes -msoft-float - CFLAGS_PROC_CPU_26 := -mcpu=arm3 -Os - CFLAGS_PROC_CPU_32v3 := -march=armv3 - CFLAGS_PROC_CPU_32v4 := -march=armv4 - CFLAGS_ARM6 := -mtune=arm6 - CFLAGS_ARM7 := -mtune=arm7 - CFLAGS_SA110 := -mtune=strongarm110 +ifeq ($(NEW_GCC),y) +CFLAGS += -mshort-load-bytes -msoft-float +CFLAGS_PROC_CPU_26 := -mcpu=arm3 -Os +CFLAGS_PROC_CPU_32v3 := -march=armv3 +CFLAGS_PROC_CPU_32v4 := -march=armv4 +CFLAGS_ARM6 := -mtune=arm6 +CFLAGS_ARM7 := -mtune=arm7 +CFLAGS_SA110 := -mtune=strongarm110 else - CFLAGS_PROC := - CFLAGS_PROC_CPU_26 := -m3 - CFLAGS_PROC_CPU_32v3 := - CFLAGS_PROC_CPU_32v4 := - CFLAGS_ARM6 := -m6 - CFLAGS_ARM7 := -m6 - CFLAGS_SA110 := -m6 +CFLAGS_PROC_CPU_26 := -m3 +CFLAGS_PROC_CPU_32v3 := +CFLAGS_PROC_CPU_32v4 := +CFLAGS_ARM6 := -m6 +CFLAGS_ARM7 := -m6 +CFLAGS_SA110 := -m6 endif +# See if this is ld "2.9.4" or later +NEW_LINKER := $(shell if $(LD) --gc-sections --version >/dev/null 2>&1; then echo y; else echo n; fi) + ifeq ($(NEW_LINKER),y) - ASFLAGS_PROC := -mno-fpu - ASFLAGS_PROC_CPU_26 := -mapcs-26 - ASFLAGS_PROC_CPU_32v3 := -mapcs-32 -marmv3m - ASFLAGS_PROC_CPU_32v4 := -mapcs-32 -marmv4t - LINKFLAGS := -p +AFLAGS += -mno-fpu +AFLAGS_PROC_CPU_26 := -mapcs-26 +AFLAGS_PROC_CPU_32v3 := -mapcs-32 -marmv3m +AFLAGS_PROC_CPU_32v4 := -mapcs-32 -marmv4t +LINKFLAGS := -p $(LINKFLAGS) else - ASFLAGS_PROC := - ASFLAGS_PROC_CPU_26 := -m3 - ASFLAGS_PROC_CPU_32v3 := -m6 - ASFLAGS_PROC_CPU_32v4 := -m6 - LINKFLAGS := +AFLAGS_PROC_CPU_26 := -m3 +AFLAGS_PROC_CPU_32v3 := -m6 +AFLAGS_PROC_CPU_32v4 := -m6 endif +# +# Select CPU dependent flags +# ifeq ($(CONFIG_CPU_26),y) - PROCESSOR = armo - TEXTADDR = 0x02080000 - ZTEXTADDR = 0x01800000 - ZRELADDR = 0x02080000 - CFLAGS_PROC += $(CFLAGS_PROC_CPU_26) - ASFLAGS_PROC += $(ASFLAGS_PROC_CPU_26) + PROCESSOR = armo + TEXTADDR = 0x02080000 + CFLAGS += $(CFLAGS_PROC_CPU_26) + AFLAGS += $(AFLAGS_PROC_CPU_26) endif ifeq ($(CONFIG_CPU_32),y) - PROCESSOR = armv - TEXTADDR = 0xC0008000 - ifeq ($(CONFIG_CPU_32v4),y) - CFLAGS_PROC += $(CFLAGS_PROC_CPU_32v4) - ASFLAGS_PROC += $(ASFLAGS_PROC_CPU_32v4) - else - CFLAGS_PROC += $(CFLAGS_PROC_CPU_32v3) - ASFLAGS_PROC += $(ASFLAGS_PROC_CPU_32v3) - endif - # - # Exactly one of the following must be selected - # - ifeq ($(CONFIG_CPU_ARM6),y) - CFLAGS_PROC += $(CFLAGS_ARM6) - else - ifeq ($(CONFIG_CPU_ARM7),y) - CFLAGS_PROC += $(CFLAGS_ARM7) - else - ifeq ($(CONFIG_CPU_SA110),y) - CFLAGS_PROC += $(CFLAGS_SA110) - endif - endif - endif + PROCESSOR = armv + TEXTADDR = 0xC0008000 + ifeq ($(CONFIG_CPU_32v4),y) + CFLAGS += $(CFLAGS_PROC_CPU_32v4) + AFLAGS += $(AFLAGS_PROC_CPU_32v4) + else + CFLAGS += $(CFLAGS_PROC_CPU_32v3) + AFLAGS += $(AFLAGS_PROC_CPU_32v3) + endif + # + # Exactly one of the following must be selected + # + ifeq ($(CONFIG_CPU_ARM6),y) + CFLAGS += $(CFLAGS_ARM6) + else + ifeq ($(CONFIG_CPU_ARM7),y) + CFLAGS += $(CFLAGS_ARM7) + else + ifeq ($(CONFIG_CPU_SA110),y) + CFLAGS += $(CFLAGS_SA110) + endif + endif + endif endif - -COMPRESSED_HEAD = head.o +GCCLIB := $(shell $(CC) $(CFLAGS) --print-libgcc-file-name) ifeq ($(CONFIG_ARCH_A5K),y) MACHINE = a5k @@ -111,22 +121,16 @@ endif ifeq ($(CONFIG_ARCH_RPC),y) MACHINE = rpc ARCHDIR = rpc -ZTEXTADDR = 0x10008000 -ZRELADDR = 0x10008000 endif ifeq ($(CONFIG_ARCH_EBSA110),y) MACHINE = ebsa110 ARCHDIR = ebsa110 -ZTEXTADDR = 0x00008000 -ZRELADDR = 0x00008000 endif ifeq ($(CONFIG_FOOTBRIDGE),y) MACHINE = footbridge ARCHDIR = ebsa285 -ZTEXTADDR = 0x00008000 -ZRELADDR = 0x00008000 endif ifeq ($(CONFIG_ARCH_CO285),y) @@ -136,56 +140,31 @@ endif ifeq ($(CONFIG_ARCH_NEXUSPCI),y) MACHINE = nexuspci ARCHDIR = nexuspci -ZTEXTADDR = 0x40200000 -ZRELADDR = 0x40008000 -COMPRESSED_EXTRA = $(TOPDIR)/arch/arm/lib/ll_char_wr_scc.o -COMPRESSED_HEAD = head-nexuspci.o endif - - -PERL = perl -LD = $(CROSS_COMPILE)ld -OBJCOPY = $(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -S -OBJDUMP = $(CROSS_COMPILE)objdump -CPP = $(CC) -E -ARCHCC := $(word 1,$(CC)) -GCCLIB := `$(CC) $(CFLAGS_PROC) --print-libgcc-file-name` -HOSTCFLAGS := $(CFLAGS:-fomit-frame-pointer=) -ifeq ($(CONFIG_FRAME_POINTER),y) -CFLAGS := $(CFLAGS:-fomit-frame-pointer=) -endif -CFLAGS := $(CFLAGS_PROC) $(CFLAGS) -pipe -ASFLAGS := $(ASFLAGS_PROC) $(ASFLAGS) -LINKFLAGS += -X -T $(TOPDIR)/arch/arm/vmlinux-$(PROCESSOR).lds -e stext -ZLINKFLAGS = -Ttext $(ZTEXTADDR) - -# If we're intending to debug the kernel, make sure it has line number -# information. This gets stripped out when building (z)Image so it doesn't -# add anything to the footprint of the running kernel. -ifeq ($(CONFIG_DEBUG_INFO),y) -CFLAGS += -g +ifeq ($(CONFIG_ARCH_SA1100),u) +MACHINE = sa1100 +ARCHDIR = sa1100 endif HEAD := arch/arm/kernel/head-$(PROCESSOR).o \ arch/arm/kernel/init_task.o -SUBDIRS := arch/arm/special $(SUBDIRS) arch/arm/lib arch/arm/kernel \ - arch/arm/mm arch/arm/nwfpe +SUBDIRS += arch/arm/kernel arch/arm/mm arch/arm/lib \ + arch/arm/special arch/arm/nwfpe CORE_FILES := arch/arm/kernel/kernel.o arch/arm/mm/mm.o $(CORE_FILES) LIBS := arch/arm/lib/lib.a $(LIBS) $(GCCLIB) DRIVERS += arch/arm/special/special.a -ifeq ($(CONFIG_ARCH_ACORN),y) -SUBDIRS += drivers/acorn/block drivers/acorn/char drivers/acorn/net \ - drivers/acorn/scsi -DRIVERS += drivers/acorn/block/acorn-block.a \ - drivers/acorn/char/acorn-char.a \ - drivers/acorn/net/acorn-net.a \ - drivers/acorn/scsi/acorn-scsi.a +ifeq ($(CONFIG_NWFPE),y) +CORE_FILES += arch/arm/nwfpe/math-emu.o endif -ifeq ($(CONFIG_NWFPE),y) -DRIVERS += arch/arm/nwfpe/math-emu.a +ifeq ($(CONFIG_ARCH_ACORN),y) +SUBDIRS += drivers/acorn +DRIVERS += drivers/acorn/block/acorn-block.a +DRIVERS += drivers/acorn/char/acorn-char.a +DRIVERS += drivers/acorn/net/acorn-net.a +DRIVERS += drivers/acorn/scsi/acorn-scsi.a endif MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot @@ -203,14 +182,9 @@ archsymlinks: $(RM) include/asm-arm/arch include/asm-arm/proc (cd include/asm-arm; ln -sf arch-$(ARCHDIR) arch; ln -sf proc-$(PROCESSOR) proc) -# We need to rebuild the linker script -# each time, in case the architecture has -# changed. -.PHONY: arch/arm/vmlinux-$(PROCESSOR).lds - -vmlinux: arch/arm/vmlinux-$(PROCESSOR).lds +vmlinux: arch/arm/vmlinux.lds -arch/arm/vmlinux-$(PROCESSOR).lds: $(TOPDIR)/arch/arm/vmlinux-$(PROCESSOR).lds.in +arch/arm/vmlinux.lds: arch/arm/vmlinux-$(PROCESSOR).lds.in dummy @sed 's/TEXTADDR/$(TEXTADDR)/' <$< >$@ arch/arm/kernel: dummy @@ -225,15 +199,13 @@ arch/arm/lib: dummy zImage zinstall Image install: vmlinux @$(MAKEBOOT) $@ -# Once we've finished integrating the sources, the @$(MAKE) will disappear archmrproper: - rm -f include/asm-arm/arch include/asm-arm/proc @$(MAKE) -C arch/$(ARCH)/special mrproper - rm -f $(TOPDIR)/arch/arm/vmlinux-*.lds + $(RM) include/asm-arm/arch include/asm-arm/proc archclean: @$(MAKEBOOT) clean - $(RM) arch/arm/lib/constants.h + $(RM) arch/arm/lib/constants.h arch/arm/vmlinux.lds archdep: @$(MAKEBOOT) dep @@ -244,6 +216,10 @@ Img:; @$(MAKEBOOT) Image i:; @$(MAKEBOOT) install zi:; @$(MAKEBOOT) zinstall +# +# Configuration targets. Use these to select a +# configuration for your architecture +# a5k_config: $(RM) arch/arm/defconfig cp arch/arm/def-configs/a5k arch/arm/defconfig @@ -260,3 +236,16 @@ rpc_config: $(RM) arch/arm/defconfig cp arch/arm/def-configs/rpc arch/arm/defconfig +brutus_config: + $(RM) arch/arm/defconfig + cp arch/arm/def-configs/brutus arch/arm/defconfig + +victor_config: + $(RM) arch/arm/defconfig + cp arch/arm/def-configs/victor arch/arm/defconfig + +empeg_config: + $(RM) arch/arm/defconfig + cp arch/arm/def-configs/empeg arch/arm/defconfig + + diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile index e31e8b288..3c0478ab3 100644 --- a/arch/arm/boot/compressed/Makefile +++ b/arch/arm/boot/compressed/Makefile @@ -2,47 +2,97 @@ # linux/arch/arm/boot/compressed/Makefile # # create a compressed vmlinuz image from the original vmlinux -# -# With this config, max compressed image size = 640k -# Uncompressed image size = 1.3M (text+data) -SYSTEM =$(TOPDIR)/vmlinux -HEAD =$(COMPRESSED_HEAD) -OBJS =$(HEAD) misc.o $(COMPRESSED_EXTRA) -CFLAGS =-O2 -DSTDC_HEADERS $(CFLAGS_PROC) -ARFLAGS =rc -FONTC =$(TOPDIR)/drivers/video/font_acorn_8x8.c +HEAD = head.o +OBJS = misc.o +SYSTEM = $(TOPDIR)/vmlinux +CFLAGS = -O2 -DSTDC_HEADERS $(CFLAGS_PROC) +FONTC = $(TOPDIR)/drivers/video/font_acorn_8x8.c +ZLDFLAGS = -X -T vmlinux.lds +# +# Architecture dependencies +# ifeq ($(CONFIG_ARCH_ACORN),y) -OBJS += ll_char_wr.o font.o +OBJS += ll_char_wr.o font.o +endif + +ifeq ($(CONFIG_CPU_26),y) +ZTEXTADDR = 0x02080000 +endif + +ifeq ($(CONFIG_ARCH_RPC),y) +ZTEXTADDR = 0x10008000 +endif + +ifeq ($(CONFIG_ARCH_EBSA110),y) +ZTEXTADDR = 0x00008000 +endif + +ifeq ($(CONFIG_FOOTBRIDGE),y) +ZTEXTADDR = 0x00008000 +endif + +ifeq ($(CONFIG_ARCH_NETWINDER),y) +OBJS += head-netwinder.o endif -ifeq ($(NEW_LINKER),y) -BINFMT := elf32-littlearm +ifeq ($(CONFIG_ARCH_NEXUSPCI),y) +HEAD = head-nexuspci.o +OBJS += $(TOPDIR)/arch/arm/lib/ll_char_wr_scc.o +ZTEXTADDR = 0x40200000 +ZRELADDR = 0x40008000 +endif + +ifeq ($(CONFIG_ARCH_SA110),y) +ifeq ($(CONFIG_SA1100_VICTOR),y) +HEAD = head-victor.o +ZTEXTADDR = 0x00002000 +ZBSSADDR = 0xc0100000 else -BINFMT := elf32-arm +ZTEXTADDR = 0xc0008000 +endif +ZRELADDR = 0xc0008000 +endif + +# +# If you don't define ZRELADDR above, +# then it defaults to ZTEXTADDR +# +ifeq ($(ZRELADDR),) +ZRELADDR = $(ZTEXTADDR) endif +SEDFLAGS = s/TEXT_START/$(ZTEXTADDR)/;s/LOAD_ADDR/$(ZRELADDR)/; + +ifneq ($(ZBSSADDR),) +SEDFLAGS += s/BSS_START/$(ZBSSADDR)/ +else +SEDFLAGS += s/BSS_START/ALIGN(4)/ +endif all: vmlinux -vmlinux: $(OBJS) piggy.o - $(LD) $(ZLINKFLAGS) -o vmlinux $(OBJS) piggy.o +vmlinux: $(HEAD) $(OBJS) piggy.o vmlinux.lds + $(LD) $(ZLDFLAGS) $(HEAD) $(OBJS) piggy.o $(GCCLIB) -o vmlinux $(HEAD): $(HEAD:.o=.S) - $(CC) -traditional -DLOADADDR=$(ZRELADDR) -c $(HEAD:.o=.S) + $(CC) -traditional -c $(HEAD:.o=.S) piggy.o: $(SYSTEM) - tmppiggy=_tmp_$$$$piggy; \ - rm -f $$tmppiggy $$tmppiggy.gz $$tmppiggy.lnk; \ - $(OBJCOPY) $(SYSTEM) $$tmppiggy; \ - gzip -f -9 < $$tmppiggy > $$tmppiggy.gz; \ - echo "SECTIONS { .data : { input_len = .; LONG(input_data_end - input_data) input_data = .; *(.data) input_data_end = .; }}" > $$tmppiggy.lnk; \ - $(LD) -r -o piggy.o -b binary $$tmppiggy.gz -b $(BINFMT) -T $$tmppiggy.lnk; \ - rm -f $$tmppiggy $$tmppiggy.gz $$tmppiggy.lnk; + $(OBJCOPY) $(SYSTEM) piggy + gzip -9 < piggy > piggy.gz + $(LD) -r -o $@ -b binary piggy.gz + rm -f piggy piggy.gz font.o: $(FONTC) $(CC) -Dstatic= -c -o $@ $(FONTC) -clean:; rm -f vmlinux core +vmlinux.lds: vmlinux.lds.in + @sed "$(SEDFLAGS)" < vmlinux.lds.in > $@ + +clean:; rm -f vmlinux core piggy* + +.PHONY: vmlinux.lds clean +misc.o: misc.c $(TOPDIR)/include/asm/arch/uncompress.h $(TOPDIR)/lib/inflate.c diff --git a/arch/arm/boot/compressed/head-netwinder.S b/arch/arm/boot/compressed/head-netwinder.S new file mode 100644 index 000000000..1dcdfcd14 --- /dev/null +++ b/arch/arm/boot/compressed/head-netwinder.S @@ -0,0 +1,30 @@ + .section ".start", #alloc, #execinstr + + adr r2, 1f + ldmdb r2, {r7, r8} + and r3, r2, #0xc000 + teq r3, #0x8000 + beq 2f + bic r3, r2, #0xc000 + orr r3, r3, #0x8000 + mov r0, r3 + mov r4, #64 + sub r5, r8, r7 + b 1f + + .word _start + .word __bss_start + +1: + .rept 4 + ldmia r2!, {r6, r7, r8, r9} + stmia r3!, {r6, r7, r8, r9} + .endr + subs r4, r4, #64 + bcs 1b + movs r4, r5 + mov r5, #0 + movne pc, r0 + + mov r0, #0 +2: diff --git a/arch/arm/boot/compressed/head-victor.S b/arch/arm/boot/compressed/head-victor.S new file mode 100644 index 000000000..e556383ba --- /dev/null +++ b/arch/arm/boot/compressed/head-victor.S @@ -0,0 +1,45 @@ +/* + * linux/arch/arm/boot/compressed/head-victor.S + * + * Copyright (C) 1998 Nicolas Pitre <nico@visuaide.com> + */ + +#include <linux/linkage.h> + + .text + .globl _start +_start: + @ just in case we still use an a.out loader... + nop + nop + nop + nop + nop + nop + nop + nop + + @ load different addresses + adr r2, LC0 + ldmia r2, {r4, r5, r6, sp} + + @ clear BSS + mov r2, #0 +1: str r2, [r5], #4 + cmp r5, r6 + blt 1b + + @ uncompress the kernel + mov r8, r0 @ save cmdline ptr + mov r0, r4 @ where to put uncompressed data + add r1, r6, #31 + bic r1, r1, #31 @ free memory space + add r2, r1, #65536 @ end of free mem space + bl SYMBOL_NAME(decompress_kernel) + mov r0, r8 @ retrieve cmdline ptr + mov pc, r4 @ call via EXEC entry + +LC0: .word _load_addr + .word __bss_start + .word SYMBOL_NAME(_end) + .word SYMBOL_NAME(user_stack)+4096 diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S index ab2541f34..721967e4b 100644 --- a/arch/arm/boot/compressed/head.S +++ b/arch/arm/boot/compressed/head.S @@ -1,129 +1,232 @@ /* * linux/arch/arm/boot/compressed/head.S * - * Copyright (C) 1996,1997,1998 Russell King + * Copyright (C) 1996-1999 Russell King */ #include <linux/linkage.h> - .text + .section ".start", #alloc, #execinstr /* * sort out different calling conventions */ .align - .globl _start -_start: -start: mov r0, r0 - mov r0, r0 - mov r0, r0 - mov r0, r0 - mov r0, r0 - mov r0, r0 - mov r0, r0 +start: + .type start,#function + .rept 8 mov r0, r0 + .endr + b 1f - .word 0x016f2818 @ Magic numbers to help the loader - .word _start + .word 0x016f2818 @ Magic numbers to help the loader + .word start +1: + + /* + * some architecture specific code can + * be inserted by the linker here + */ + + .text 1: teq r0, #0 - beq 2f - mov r4, #0x02000000 - add r4, r4, #0x7C000 - mov r3, #0x4000 - sub r3, r3, #4 -1: ldmia r0!, {r5 - r12} - stmia r4!, {r5 - r12} - subs r3, r3, #32 - bpl 1b -2: adr r2, LC0 - ldmia r2, {r2, r3, r4, r5, r6, sp} - add r2, r2, #3 - add r3, r3, #3 - add sp, sp, #3 - bic r2, r2, #3 - bic r3, r3, #3 - bic sp, sp, #3 - adr r7, start - sub r6, r7, r6 -/* - * Relocate pointers - */ - add r2, r2, r6 - add r3, r3, r6 - add r5, r5, r6 - add sp, sp, r6 -/* - * Clear zero-init - */ - mov r6, #0 -1: str r6, [r2], #4 + bne 1b + mov r7, r1 @ save architecture ID + mrc p15, 0, r6, c0, c0 @ get processor ID + adr r2, LC0 + ldmia r2, {r2, r3, r4, r5, sp} + + mov r0, #0 +1: str r0, [r2], #4 @ clear bss + str r0, [r2], #4 + str r0, [r2], #4 + str r0, [r2], #4 cmp r2, r3 blt 1b - str r1, [r5] @ save architecture -/* - * Uncompress the kernel - */ - mov r1, #0x8000 - add r3, r2, r1, lsl #1 @ Add 64k for malloc - sub r1, r1, #1 - add r3, r3, r1 - bic r5, r3, r1 @ decompress kernel to after end of the compressed + + mov r1, sp @ malloc space above stack + add r2, sp, #0x10000 @ 64k max + + teq r4, r5 @ will we overwrite ourselves? + moveq r5, r2 + movne r5, r4 + mov r0, r5 - mov r1, r2 - mov r2, r0 + mov r3, r7 bl SYMBOL_NAME(decompress_kernel) - add r0, r0, #7 - bic r2, r0, #7 + + teq r4, r5 @ do we need to relocate + beq call_kernel @ the kernel? + + add r0, r0, #127 + bic r0, r0, #127 @ align the kernel length /* - * Now move the kernel to the correct location (r5 -> r4, len r0) + * r0 = decompressed kernel length + * r1-r3 = unused + * r4 = kernel execution address + * r5 = decompressed kernel start + * r6 = processor ID + * r7 = architecture ID + * r8-r14 = unused */ - mov r0, r4 @ r0 = start of real kernel - mov r1, r5 @ r1 = start of kernel image - add r3, r5, r2 @ r3 = end of kernel - adr r4, movecode - adr r5, movecodeend -1: ldmia r4!, {r6 - r12, lr} - stmia r3!, {r6 - r12, lr} - cmp r4, r5 + add r1, r5, r0 @ end of decompressed kernel + adr r2, reloc_start + adr r3, reloc_end +1: ldmia r2!, {r8 - r13} @ copy relocation code + stmia r1!, {r8 - r13} + ldmia r2!, {r8 - r13} + stmia r1!, {r8 - r13} + cmp r2, r3 blt 1b - mrc p15, 0, r5, c0, c0 - eor r5, r5, #0x44 << 24 - eor r5, r5, #0x01 << 16 - eor r5, r5, #0xa1 << 8 - movs r5, r5, lsr #4 - mov r5, #0 - mcreq p15, 0, r5, c7, c5, 0 @ flush I cache - ldr r5, LC0 + 12 @ get architecture - ldr r5, [r5] - add pc, r1, r2 @ Call move code + + eor r1, r6, #0x44 << 24 @ SA-110? + eor r1, r1, #0x01 << 16 + eor r1, r1, #0xa1 << 8 + movs r1, r1, lsr #4 + mcreq p15, 0, r1, c7, c7, 0 @ flush I & D-cache + mcreq p15, 0, r1, c7, c10, 4 @ drain WB + add pc, r5, r0 @ call relocation code /* - * r0 = length, r1 = to, r2 = from + * r0 = decompressed kernel length + * r1-r3 = unused + * r4 = kernel execution address + * r5 = decompressed kernel start + * r6 = processor ID + * r7 = architecture ID + * r8-r14 = unused */ -movecode: add r3, r1, r2 - mov r4, r0 -1: ldmia r1!, {r6 - r12, lr} - stmia r0!, {r6 - r12, lr} - cmp r1, r3 +reloc_start: add r8, r5, r0 +#if 0 + mov r0, r6 + mov r1, #8 + bl phex + mov r0, #':' + bl putc + mov r0, r5 + mov r1, #8 + bl phex + mov r0, #'-' + bl putc + mov r0, r8 + mov r1, #8 + bl phex + mov r0, #'>' + bl putc + mov r0, r4 + mov r1, #8 + bl phex + mov r0, #'\n' + bl putc +#endif + mov r0, r8 + mov r1, r4 +1: + .rept 4 + ldmia r5!, {r2, r3, r8 - r13} @ relocate kernel + stmia r1!, {r2, r3, r8 - r13} + .endr + + cmp r5, r0 blt 1b - mrc p15, 0, r0, c0, c0 - eor r0, r0, #0x44 << 24 +#if 0 + mov r8, r0 + mov r0, r5 + mov r1, #8 + bl phex + mov r0, #'-' + bl putc + mov r0, r8 + mov r1, #8 + bl phex + mov r0, #'\n' + bl putc + mov r0, r4 + bl memdump +#endif + eor r0, r6, #0x44 << 24 @ SA-110? eor r0, r0, #0x01 << 16 eor r0, r0, #0xa1 << 8 movs r0, r0, lsr #4 + mcreq p15, 0, r0, c7, c7, 0 @ flush I cache + mcreq p15, 0, r1, c7, c10, 4 @ drain WB + +call_kernel: mov r0, #0 + mov r1, r7 @ restore architecture number + mov pc, r4 @ call kernel + +phexbuf: .space 12 + +phex: adr r3, phexbuf + mov r2, #0 + strb r2, [r3, r1] +1: subs r1, r1, #1 + movmi r0, r3 + bmi puts + and r2, r0, #15 + mov r0, r0, lsr #4 + cmp r2, #10 + addge r2, r2, #7 + add r2, r2, #'0' + strb r2, [r3, r1] + b 1b + +puts: mov r3, #0x7c000000 +1: ldrb r2, [r0], #1 + teq r2, #0 + moveq pc, lr +2: strb r2, [r3, #0x3f8] + mov r1, #0x00020000 +3: subs r1, r1, #1 + bne 3b + teq r2, #'\n' + moveq r2, #'\r' + beq 2b + teq r0, #0 + bne 1b + mov pc, lr +putc: + mov r2, r0 mov r0, #0 - mcreq p15, 0, r0, c7, c5, 0 @ flush I cache - mov r1, r5 @ call kernel correctly - mov pc, r4 @ call via EXEC entry -movecodeend: - -LC0: .word SYMBOL_NAME(_edata) - .word SYMBOL_NAME(_end) - .word LOADADDR - .word SYMBOL_NAME(architecture) - .word start - .word SYMBOL_NAME(user_stack)+4096 - .align + mov r3, #0x7c000000 + b 2b - .bss -SYMBOL_NAME(architecture): - .space 4 +memdump: mov r12, r0 + mov r10, lr + mov r1, #8 + bl phex + mov r0, #'\n' + bl putc + mov r11, #0 +2: mov r0, r11, lsl #2 + mov r1, #4 + bl phex + mov r0, #':' + bl putc +1: mov r0, #' ' + bl putc + ldr r0, [r12, r11, lsl #2] + mov r1, #8 + bl phex + and r0, r11, #7 + teq r0, #3 + moveq r0, #' ' + bleq putc + and r0, r11, #7 + add r11, r11, #1 + teq r0, #7 + bne 1b + mov r0, #'\n' + bl putc + cmp r11, #64 + blt 2b + mov pc, r10 +reloc_end: + +LC0: .word __bss_start + .word _end + .word _load_addr + .word _start + .word user_stack+4096 .align + + .section ".stack" +user_stack: .space 4096 diff --git a/arch/arm/boot/compressed/misc.c b/arch/arm/boot/compressed/misc.c index 1d8517104..66b4f2d0e 100644 --- a/arch/arm/boot/compressed/misc.c +++ b/arch/arm/boot/compressed/misc.c @@ -7,8 +7,17 @@ * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994 * * Modified for ARM Linux by Russell King + * + * Nicolas Pitre <nico@visuaide.com> 1999/04/14 : + * For this code to run directly from Flash, all constant variables must + * be marked with 'const' and all other variables initialized at run-time + * only. This way all non constant variables will end up in the bss segment, + * which should point to addresses in RAM and cleared to 0 on start. + * This allows for a much quicker boot time. */ +unsigned int __machine_arch_type; + #include <asm/uaccess.h> #include <asm/arch/uncompress.h> #include <asm/proc/uncompress.h> @@ -22,7 +31,7 @@ /* * Optimised C version of memzero for the ARM. */ -extern __inline__ __ptr_t __memzero (__ptr_t s, size_t n) +void __memzero (__ptr_t s, size_t n) { union { void *vp; unsigned long *ulp; unsigned char *ucp; } u; int i; @@ -62,11 +71,8 @@ extern __inline__ __ptr_t __memzero (__ptr_t s, size_t n) if (n & 1) *u.ucp++ = 0; - return s; } -#define memzero(s,n) __memzero(s,n) - extern __inline__ __ptr_t memcpy(__ptr_t __dest, __const __ptr_t __src, size_t __n) { @@ -157,11 +163,11 @@ static void gzip_mark(void **); static void gzip_release(void **); extern char input_data[]; -extern int input_len; +extern char input_data_end[]; static uch *output_data; static ulg output_ptr; -static ulg bytes_out = 0; +static ulg bytes_out; static void *malloc(int size); static void free(void *where); @@ -226,13 +232,14 @@ static void gzip_release(void **ptr) * Fill the input buffer. This is called only when the buffer is empty * and at least one byte is really needed. */ -int fill_inbuf() +int fill_inbuf(void) { if (insize != 0) error("ran out of input data\n"); inbuf = input_data; - insize = input_len; + insize = &input_data_end[0] - &input_data[0]; + inptr = 1; return inbuf[0]; } @@ -241,7 +248,7 @@ int fill_inbuf() * Write the output window window[0..outcnt-1] and update crc and bytes_out. * (Used for the decompressed data only.) */ -void flush_window() +void flush_window(void) { ulg c = crc; unsigned n; @@ -257,6 +264,7 @@ void flush_window() bytes_out += (ulg)outcnt; output_ptr += (ulg)outcnt; outcnt = 0; + puts("."); } static void error(char *x) @@ -270,26 +278,24 @@ static void error(char *x) while(1); /* Halt */ } -#define STACK_SIZE (4096) - -ulg user_stack [STACK_SIZE]; - #ifndef STANDALONE_DEBUG -ulg decompress_kernel(ulg output_start, ulg free_mem_ptr_p, ulg free_mem_ptr_end_p) +ulg +decompress_kernel(ulg output_start, ulg free_mem_ptr_p, ulg free_mem_ptr_end_p, + int arch_id) { - free_mem_ptr = free_mem_ptr_p; - free_mem_ptr_end = free_mem_ptr_end_p; - - proc_decomp_setup (); - arch_decomp_setup (); + output_data = (uch *)output_start; /* Points to kernel start */ + free_mem_ptr = free_mem_ptr_p; + free_mem_ptr_end = free_mem_ptr_end_p; + __machine_arch_type = arch_id; - output_data = (uch *)output_start; /* Points to kernel start */ + proc_decomp_setup(); + arch_decomp_setup(); makecrc(); puts("Uncompressing Linux..."); gunzip(); - puts("done.\nNow booting the kernel\n"); + puts(" done, booting the kernel.\n"); return output_ptr; } #else diff --git a/arch/arm/boot/compressed/vmlinux.lds.in b/arch/arm/boot/compressed/vmlinux.lds.in new file mode 100644 index 000000000..f7469c634 --- /dev/null +++ b/arch/arm/boot/compressed/vmlinux.lds.in @@ -0,0 +1,52 @@ +OUTPUT_ARCH(arm) +ENTRY(_start) +SECTIONS +{ + . = LOAD_ADDR; + _load_addr = .; + + . = TEXT_START; + _text = .; + + .text : { + _start = .; + head.o(.start) + *(.start) + head.o(.text) + *(.text) + *(.fixup) + *(.gnu.warning) + input_data = .; + piggy.o + input_data_end = .; + . = ALIGN(4); + } + + _etext = .; + + .data : { + *(.data) + } + + _edata = .; + + . = BSS_START; + __bss_start = .; + .bss : { + *(.bss) + } + _end = .; + + .stack : { + *(.stack) + } + + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } +} + diff --git a/arch/arm/config.in b/arch/arm/config.in index 52f69bcd8..4271d12a6 100644 --- a/arch/arm/config.in +++ b/arch/arm/config.in @@ -20,7 +20,7 @@ choice 'ARM system type' \ RiscPC CONFIG_ARCH_RPC \ EBSA-110 CONFIG_ARCH_EBSA110 \ FootBridge-based CONFIG_FOOTBRIDGE" RiscPC - +# SA1100-based CONFIG_ARCH_SA1100 if [ "$CONFIG_FOOTBRIDGE" = "y" ]; then bool 'FootBridge in HOST mode' CONFIG_HOST_FOOTBRIDGE if [ "$CONFIG_HOST_FOOTBRIDGE" = "y" ]; then @@ -31,9 +31,9 @@ if [ "$CONFIG_FOOTBRIDGE" = "y" ]; then fi if [ "$CONFIG_HOST_FOOTBRIDGE" = "y" ]; then - bool ' Include support for Intel EBSA285' CONFIG_ARCH_EBSA285 - bool ' Include support for Chalice CATS boards' CONFIG_CATS - bool ' Include support for Corel NetWinder' CONFIG_ARCH_NETWINDER + bool ' Include support for EBSA285' CONFIG_ARCH_EBSA285 + bool ' Include support for CATS' CONFIG_CATS + bool ' Include support for NetWinder' CONFIG_ARCH_NETWINDER fi if [ "$CONFIG_ADDIN_FOOTBRIDGE" = "y" ]; then @@ -42,6 +42,18 @@ if [ "$CONFIG_ADDIN_FOOTBRIDGE" = "y" ]; then define_bool CONFIG_ARCH_CO285 y fi +if [ "$CONFIG_ARCH_SA1100" = "y" ]; then + define_bool CONFIG_CPU_SA1100 y + choice 'SA1100 implementation' \ + "Brutus CONFIG_SA1100_BRUTUS \ + empeg CONFIG_SA1100_EMPEG \ + Itsy CONFIG_SA1100_ITSY \ + LART CONFIG_SA1100_LART \ + PLEB CONFIG_SA1100_PLEB \ + Victor CONFIG_SA1100_VICTOR \ + Tifon CONFIG_SA1100_TIFON" Brutus +fi + # # Select various configuration options depending on the machine type # Easy check for Acorn-style architectures @@ -76,6 +88,7 @@ else # Select CPU and optimisation dependent on architecture # if [ "$CONFIG_ARCH_EBSA110" = "y" -o \ + "$CONFIG_ARCH_SA1100" = "y" -o \ "$CONFIG_FOOTBRIDGE" = "y" -o \ "$CONFIG_ARCH_NEXUSPCI" = "y" ]; then define_bool CONFIG_CPU_32v4 y @@ -139,21 +152,10 @@ if [ "$CONFIG_CPU_32" = "y" ]; then tristate 'RISC OS personality' CONFIG_ARTHUR fi -tristate 'Parallel port support' CONFIG_PARPORT -if [ "$CONFIG_PARPORT" != "n" ]; then - if [ "$CONFIG_ARCH_ARC" = "y" ]; then - dep_tristate ' Archimedes hardware' CONFIG_PARPORT_ARC $CONFIG_PARPORT - fi - dep_tristate ' PC-style hardware' CONFIG_PARPORT_PC $CONFIG_PARPORT - # If exactly one hardware type is selected then parport will optimise away - # support for loading any others. Defeat this if the user is keen. - if [ "$CONFIG_PARPORT_PC" = "n" -o "$CONFIG_PARPORT_ARC" = "n" ]; then - if [ "$CONFIG_PARPORT_PC" != "n" -o "$CONFIG_PARPORT_ARC" != "n" ]; then - bool ' Support foreign hardware' CONFIG_PARPORT_OTHER - fi - fi -fi +source drivers/parport/Config.in + if [ "$CONFIG_ARCH_EBSA110" = "y" -o \ + "$CONFIG_ARCH_SA1100" = "y" -o \ "$CONFIG_ARCH_NETWINDER" = "y" -o \ "$CONFIG_CATS" = "y" ]; then string 'Initial kernel command string' CONFIG_CMDLINE @@ -165,10 +167,10 @@ if [ "$CONFIG_ARCH_NETWINDER" = "y" -o \ bool 'Timer and CPU usage LEDs' CONFIG_LEDS if [ "$CONFIG_LEDS" = "y" ]; then if [ "$CONFIG_ARCH_NETWINDER" = "y" -o \ - "$CONFIG_ARCH_EBSA285" = "y" -o \ + "$CONFIG_ARCH_EBSA285" = "y" -o \ "$CONFIG_ARCH_CO285" = "y" ]; then - bool ' Timer LED' CONFIG_LEDS_TIMER - bool ' CPU usage LED' CONFIG_LEDS_CPU + bool ' Timer LED' CONFIG_LEDS_TIMER + bool ' CPU usage LED' CONFIG_LEDS_CPU fi fi fi @@ -186,11 +188,11 @@ fi source drivers/char/Config.in if [ "$CONFIG_ARCH_ACORN" = "y" ]; then - if [ "$CONFIG_MOUSE" = "y" ]; then + if [ "$CONFIG_BUSMOUSE" = "y" ]; then if [ "$CONFIG_ARCH_RPC" != "y" ]; then - define_bool CONFIG_KBDMOUSE y + define_bool CONFIG_KBDMOUSE y else - define_bool CONFIG_RPCMOUSE y + define_bool CONFIG_RPCMOUSE y fi fi fi @@ -230,7 +232,7 @@ fi # # tristate 'ISDN support' CONFIG_ISDN # if [ "$CONFIG_ISDN" != "n" ]; then -# source drivers/isdn/Config.in +# source drivers/isdn/Config.in # fi # endmenu @@ -267,17 +269,17 @@ bool 'Include debugging information in kernel binary' CONFIG_DEBUG_INFO #bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - if [ "$CONFIG_CPU_26" = "y" ]; then - bool 'Disable pgtable cache (EXPERIMENTAL)' CONFIG_NO_PGT_CACHE - fi - - # These options are only for real kernel hackers - # who want to get their hands dirty. - bool 'Kernel low-level debugging functions' CONFIG_DEBUG_LL - if [ "$CONFIG_DEBUG_LL" = "y" ]; then - if [ "$CONFIG_FOOTBRIDGE" = "y" ]; then - bool 'Kernel low-level debugging messages via DC21285 port' CONFIG_DEBUG_DC21285_PORT - fi - fi + if [ "$CONFIG_CPU_26" = "y" ]; then + bool 'Disable pgtable cache (EXPERIMENTAL)' CONFIG_NO_PGT_CACHE + fi + + # These options are only for real kernel hackers + # who want to get their hands dirty. + bool 'Kernel low-level debugging functions' CONFIG_DEBUG_LL + if [ "$CONFIG_DEBUG_LL" = "y" ]; then + if [ "$CONFIG_FOOTBRIDGE" = "y" ]; then + bool 'Kernel low-level debugging messages via DC21285 port' CONFIG_DEBUG_DC21285_PORT + fi + fi fi endmenu diff --git a/arch/arm/def-configs/brutus b/arch/arm/def-configs/brutus new file mode 100644 index 000000000..a700f7d17 --- /dev/null +++ b/arch/arm/def-configs/brutus @@ -0,0 +1,221 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_ARM=y + +# +# System and processor type +# +# CONFIG_ARCH_ARC is not set +# CONFIG_ARCH_A5K is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_FOOTBRIDGE is not set +CONFIG_ARCH_SA1100=y +CONFIG_CPU_SA1100=y +CONFIG_SA1100_BRUTUS=y +# CONFIG_SA1100_EMPEG is not set +# CONFIG_SA1100_ITSY is not set +# CONFIG_SA1100_LART is not set +# CONFIG_SA1100_PLEB is not set +# CONFIG_SA1100_VICTOR is not set +# CONFIG_SA1100_TIFON is not set +# CONFIG_ARCH_ACORN is not set +CONFIG_CPU_32=y +# CONFIG_CPU_26 is not set +CONFIG_CPU_32v4=y +CONFIG_CPU_SA110=y +# CONFIG_ISA_DMA is not set + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +# CONFIG_ALIGNMENT_TRAP is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +# CONFIG_MODVERSIONS is not set +# CONFIG_KMOD is not set + +# +# General setup +# +# CONFIG_NET is not set +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_SYSCTL is not set +CONFIG_NWFPE=y +CONFIG_BINFMT_AOUT=y +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_ARTHUR is not set +# CONFIG_PARPORT is not set +CONFIG_CMDLINE="" + +# +# I2O device support +# +# CONFIG_I2O is not set +# CONFIG_I2O_PCI is not set +# CONFIG_I2O_BLOCK is not set +# CONFIG_I2O_LAN is not set +# CONFIG_I2O_SCSI is not set +# CONFIG_I2O_PROC is not set + +# +# Plug and Play configuration +# +# CONFIG_PNP is not set +# CONFIG_ISAPNP is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_IDE is not set + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_HD_ONLY is not set +# CONFIG_BLK_CPQ_DA is not set + +# +# Additional Block Devices +# +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_INITRD=y +# CONFIG_BLK_DEV_XD is not set +CONFIG_PARIDE_PARPORT=y +# CONFIG_PARIDE is not set +# CONFIG_BLK_DEV_IDE_MODES is not set +# CONFIG_BLK_DEV_HD is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_SERIAL_SA1100=y +# CONFIG_SERIAL_SA1100_CONSOLE is not set +# CONFIG_SERIAL is not set +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=32 + +# +# Mice +# +# CONFIG_BUSMOUSE is not set +# CONFIG_MOUSE is not set +# CONFIG_QIC02_TAPE is not set +# CONFIG_WATCHDOG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set + +# +# Video For Linux +# +# CONFIG_VIDEO_DEV is not set + +# +# Joystick support +# +# CONFIG_JOYSTICK is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_DRM is not set + +# +# USB drivers - not for the faint of heart +# +# CONFIG_USB is not set + +# +# Console drivers +# +# CONFIG_VGA_CONSOLE is not set +CONFIG_FB=y +CONFIG_DUMMY_CONSOLE=y +CONFIG_FB_SA1100=y +# CONFIG_FB_MATROX is not set +# CONFIG_FB_ATY is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FBCON_ADVANCED is not set +CONFIG_FBCON_CFB2=y +CONFIG_FBCON_CFB4=y +CONFIG_FBCON_CFB8=y +CONFIG_FBCON_CFB16=y +CONFIG_FBCON_FONTWIDTH8_ONLY=y +CONFIG_FBCON_FONTS=y +CONFIG_FONT_8x8=y +# CONFIG_FONT_8x16 is not set +# CONFIG_FONT_SUN8x16 is not set +# CONFIG_FONT_PEARL_8x8 is not set +# CONFIG_FONT_ACORN_8x8 is not set + +# +# SCSI support +# +# CONFIG_SCSI is not set + +# +# Filesystems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_ISO9660_FS is not set +# CONFIG_JOLIET is not set +# CONFIG_UDF_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +CONFIG_DEVPTS_FS=y +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_NLS is not set + +# +# Kernel hacking +# +CONFIG_FRAME_POINTER=y +CONFIG_DEBUG_ERRORS=y +CONFIG_DEBUG_USER=y +CONFIG_DEBUG_INFO=y +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_DEBUG_LL is not set diff --git a/arch/arm/def-configs/empeg b/arch/arm/def-configs/empeg new file mode 100644 index 000000000..cd3792b53 --- /dev/null +++ b/arch/arm/def-configs/empeg @@ -0,0 +1,264 @@ +# +# Example empeg-car kernel configuration file. +# +CONFIG_ARM=y + +# +# System and processor type +# +# CONFIG_ARCH_ARC is not set +# CONFIG_ARCH_A5K is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_FOOTBRIDGE is not set +CONFIG_ARCH_SA1100=y +CONFIG_CPU_SA1100=y +# CONFIG_SA1100_BRUTUS is not set +CONFIG_SA1100_EMPEG=y +# CONFIG_SA1100_ITSY is not set +# CONFIG_SA1100_PLEB is not set +# CONFIG_SA1100_VICTOR is not set +# CONFIG_EMPEG_HENRY is not set +# CONFIG_ARCH_ACORN is not set +# CONFIG_ISA_DMA is not set +CONFIG_CPU_32=y +# CONFIG_CPU_26 is not set +# CONFIG_CPU_ARM2 is not set +# CONFIG_CPU_ARM3 is not set +# CONFIG_CPU_ARM6 is not set +# CONFIG_CPU_ARM7 is not set +CONFIG_CPU_SA110=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +# CONFIG_ALIGNMENT_TRAP is not set +# CONFIG_TEXT_SECTIONS is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODVERSIONS=y +# CONFIG_KMOD is not set + +# +# General setup +# +CONFIG_NET=y +# CONFIG_SYSVIPC is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_SYSCTL is not set +CONFIG_NWFPE=y +CONFIG_BINFMT_AOUT=y +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_ARTHUR is not set +# CONFIG_PARPORT is not set +CONFIG_CMDLINE="" + +# +# Plug and Play support +# +# CONFIG_PNP is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +CONFIG_BLK_DEV_IDE=y +# CONFIG_BLK_DEV_HD_IDE is not set +CONFIG_BLK_DEV_IDEDISK=y +# CONFIG_BLK_DEV_IDECD is not set +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDESCSI is not set +# CONFIG_BLK_DEV_CMD640 is not set +# CONFIG_IDE_CHIPSETS is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_INITRD=y +# CONFIG_BLK_DEV_XD is not set +CONFIG_PARIDE_PARPORT=y +# CONFIG_PARIDE is not set +# CONFIG_BLK_DEV_HD is not set + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_SERIAL_SA1100=y +CONFIG_SERIAL_SA1100_CONSOLE=y +# CONFIG_SERIAL is not set +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_UNIX98_PTYS is not set +# CONFIG_MOUSE is not set +# CONFIG_QIC02_TAPE is not set +# CONFIG_WATCHDOG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +CONFIG_EMPEG_IR=y +CONFIG_EMPEG_USB=y + +# +# Video For Linux +# +CONFIG_VIDEO_DEV=y +# CONFIG_RADIO_RTRACK is not set +# CONFIG_RADIO_RTRACK2 is not set +# CONFIG_RADIO_AZTECH is not set +# CONFIG_RADIO_CADET is not set +# CONFIG_RADIO_MIROPCM20 is not set +# CONFIG_RADIO_GEMTEK is not set +CONFIG_RADIO_EMPEG=y +# CONFIG_VIDEO_BT848 is not set +# CONFIG_VIDEO_PMS is not set +# CONFIG_VIDEO_SAA5249 is not set +# CONFIG_RADIO_SF16FMI is not set +# CONFIG_RADIO_TYPHOON is not set +# CONFIG_RADIO_ZOLTRIX is not set + +# +# Joystick support +# +# CONFIG_JOYSTICK is not set +# CONFIG_DTLK is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set + +# +# Networking options +# +# CONFIG_PACKET is not set +# CONFIG_NETLINK is not set +# CONFIG_FIREWALL is not set +# CONFIG_FILTER is not set +# CONFIG_UNIX is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +# CONFIG_IP_PNP is not set +# CONFIG_IP_ROUTER is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_ALIAS is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_RARP is not set +# CONFIG_SKB_LARGE is not set +# CONFIG_IPV6 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_BRIDGE is not set +# CONFIG_LLC is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set +# CONFIG_CPU_IS_SLOW is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# IrDA subsystem support +# +# CONFIG_IRDA is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +# CONFIG_NET_ETHERNET is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_DLCI is not set +CONFIG_PPP=y +# CONFIG_SLIP is not set +# CONFIG_NET_RADIO is not set +# CONFIG_TR is not set +# CONFIG_SHAPER is not set +# CONFIG_HOSTESS_SV11 is not set +# CONFIG_COSA is not set +# CONFIG_RCPCI is not set + +# +# SCSI support +# +# CONFIG_SCSI is not set + +# +# Filesystems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_ISO9660_FS is not set +# CONFIG_JOLIET is not set +# CONFIG_MINIX_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +# CONFIG_NFS_FS is not set +# CONFIG_NFSD is not set +# CONFIG_SUNRPC is not set +# CONFIG_LOCKD is not set +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set + +# +# Partition Types +# +# CONFIG_OSF_PARTITION is not set +# CONFIG_MAC_PARTITION is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_SGI_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ACORN_PARTITION is not set +# CONFIG_NLS is not set + +# +# Kernel hacking +# +CONFIG_FRAME_POINTER=y +CONFIG_DEBUG_ERRORS=y +CONFIG_DEBUG_USER=y +CONFIG_DEBUG_USER_BACKTRACE=y +# CONFIG_DEBUG_INFO is not set +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_DEBUG_LL is not set diff --git a/arch/arm/def-configs/footbridge b/arch/arm/def-configs/footbridge index ce85d6ffc..a3acd8954 100644 --- a/arch/arm/def-configs/footbridge +++ b/arch/arm/def-configs/footbridge @@ -17,22 +17,18 @@ CONFIG_ARCH_EBSA285=y # CONFIG_CATS is not set CONFIG_ARCH_NETWINDER=y # CONFIG_ARCH_ACORN is not set -CONFIG_PCI=y -CONFIG_ISA_DMA=y CONFIG_CPU_32=y # CONFIG_CPU_26 is not set -# CONFIG_CPU_ARM2 is not set -# CONFIG_CPU_ARM3 is not set -# CONFIG_CPU_ARM6 is not set -# CONFIG_CPU_ARM7 is not set +CONFIG_CPU_32v4=y CONFIG_CPU_SA110=y +CONFIG_PCI=y +CONFIG_ISA_DMA=y # # Code maturity level options # CONFIG_EXPERIMENTAL=y # CONFIG_ALIGNMENT_TRAP is not set -# CONFIG_TEXT_SECTIONS is not set # # Loadable module support @@ -55,15 +51,35 @@ CONFIG_BINFMT_ELF=y # CONFIG_ARTHUR is not set CONFIG_PARPORT=y CONFIG_PARPORT_PC=y +CONFIG_PARPORT_PC_FIFO=y +# CONFIG_PARPORT_PC_PCMCIA is not set +# CONFIG_PARPORT_ARC is not set +# CONFIG_PARPORT_AMIGA is not set +# CONFIG_PARPORT_MFC3 is not set +# CONFIG_PARPORT_ATARI is not set +# CONFIG_PARPORT_SUNBPP is not set +# CONFIG_PARPORT_OTHER is not set +CONFIG_PARPORT_1284=y CONFIG_CMDLINE="root=/dev/hda2 ro mem=32M parport=0x378,7 ide0=autotune" CONFIG_LEDS=y CONFIG_LEDS_TIMER=y # CONFIG_LEDS_CPU is not set # -# Plug and Play support +# I2O device support # -# CONFIG_PNP is not set +# CONFIG_I2O is not set +# CONFIG_I2O_PCI is not set +# CONFIG_I2O_BLOCK is not set +# CONFIG_I2O_LAN is not set +# CONFIG_I2O_SCSI is not set +# CONFIG_I2O_PROC is not set + +# +# Plug and Play configuration +# +CONFIG_PNP=y +CONFIG_ISAPNP=y # # Block devices @@ -76,6 +92,7 @@ CONFIG_BLK_DEV_IDE=y # # CONFIG_BLK_DEV_HD_IDE is not set CONFIG_BLK_DEV_IDEDISK=y +CONFIG_IDEDISK_MULTI_MODE=y # CONFIG_BLK_DEV_IDECD is not set # CONFIG_BLK_DEV_IDETAPE is not set # CONFIG_BLK_DEV_IDEFLOPPY is not set @@ -89,7 +106,7 @@ CONFIG_IDEDMA_AUTO=y # CONFIG_BLK_DEV_OPTI621 is not set # CONFIG_BLK_DEV_TRM290 is not set # CONFIG_BLK_DEV_NS87415 is not set -# CONFIG_BLK_DEV_VIA82C586 is not set +# CONFIG_BLK_DEV_VIA82CXXX is not set # CONFIG_BLK_DEV_CMD646 is not set CONFIG_BLK_DEV_SL82C105=y # CONFIG_IDE_CHIPSETS is not set @@ -136,6 +153,7 @@ CONFIG_PARIDE_KBIC=m CONFIG_PARIDE_KTTI=m CONFIG_PARIDE_ON20=m CONFIG_PARIDE_ON26=m +CONFIG_BLK_DEV_IDE_MODES=y # CONFIG_BLK_DEV_HD is not set # @@ -149,15 +167,14 @@ CONFIG_SERIAL_CONSOLE=y # CONFIG_SERIAL_NONSTANDARD is not set # CONFIG_UNIX98_PTYS is not set CONFIG_PRINTER=m -CONFIG_PRINTER_READBACK=y -CONFIG_MOUSE=y +# CONFIG_LP_CONSOLE is not set +# CONFIG_PPDEV is not set # # Mice # -# CONFIG_ATIXL_BUSMOUSE is not set # CONFIG_BUSMOUSE is not set -# CONFIG_MS_BUSMOUSE is not set +CONFIG_MOUSE=y CONFIG_PSMOUSE=y # CONFIG_82C710_MOUSE is not set # CONFIG_PC110_PAD is not set @@ -172,6 +189,8 @@ CONFIG_WATCHDOG=y CONFIG_SOFT_WATCHDOG=y # CONFIG_PCWATCHDOG is not set # CONFIG_ACQUIRE_WDT is not set +# CONFIG_21285_WATCHDOG is not set +CONFIG_977_WATCHDOG=m CONFIG_DS1620=y CONFIG_NWBUTTON=y CONFIG_NWBUTTON_REBOOT=y @@ -189,6 +208,8 @@ CONFIG_RTC=y # # CONFIG_JOYSTICK is not set # CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set # # Ftape, the floppy tape device driver @@ -196,11 +217,35 @@ CONFIG_RTC=y # CONFIG_FTAPE is not set # +# USB drivers - not for the faint of heart +# +CONFIG_USB=m +# CONFIG_USB_UHCI is not set +CONFIG_USB_OHCI=m +CONFIG_USB_OHCI_DEBUG=y +CONFIG_USB_OHCI_HCD=m +CONFIG_USB_OHCI_VROOTHUB=y +# CONFIG_USB_DEBUG_ISOC is not set +CONFIG_USB_HUB=m +CONFIG_USB_MOUSE=m +CONFIG_USB_KBD=m +CONFIG_USB_AUDIO=m +CONFIG_USB_ACM=m +CONFIG_USB_PRINTER=m +# CONFIG_USB_CPIA is not set +CONFIG_USB_SCSI=m +CONFIG_USB_SCSI_DEBUG=m +# CONFIG_USB_EZUSB is not set +# CONFIG_USB_USS720 is not set +CONFIG_USB_PROC=y + +# # Console drivers # CONFIG_VGA_CONSOLE=y CONFIG_FB=y CONFIG_DUMMY_CONSOLE=y +# CONFIG_FB_CLGEN is not set # CONFIG_FB_PM2 is not set CONFIG_FB_CYBER2000=y # CONFIG_FB_MATROX is not set @@ -220,6 +265,7 @@ CONFIG_FBCON_CFB24=y # CONFIG_FBCON_IPLAN2P4 is not set # CONFIG_FBCON_IPLAN2P8 is not set # CONFIG_FBCON_MAC is not set +# CONFIG_FBCON_VGA_PLANES is not set CONFIG_FBCON_VGA=y # CONFIG_FBCON_FONTWIDTH8_ONLY is not set CONFIG_FBCON_FONTS=y @@ -235,8 +281,9 @@ CONFIG_FONT_ACORN_8x8=y # Networking options # CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set # CONFIG_NETLINK is not set -# CONFIG_FIREWALL is not set +# CONFIG_NETFILTER is not set # CONFIG_FILTER is not set CONFIG_UNIX=y CONFIG_INET=y @@ -254,15 +301,17 @@ CONFIG_IP_ALIAS=y # # (it is safe to leave these untouched) # -# CONFIG_INET_RARP is not set CONFIG_SKB_LARGE=y # CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set +# CONFIG_ATM is not set # # # # CONFIG_IPX is not set # CONFIG_ATALK is not set +# CONFIG_DECNET is not set # CONFIG_X25 is not set # CONFIG_LAPB is not set # CONFIG_BRIDGE is not set @@ -271,7 +320,6 @@ CONFIG_SKB_LARGE=y # CONFIG_WAN_ROUTER is not set # CONFIG_NET_FASTROUTE is not set # CONFIG_NET_HW_FLOWCONTROL is not set -# CONFIG_CPU_IS_SLOW is not set # # QoS and/or fair queueing @@ -284,7 +332,7 @@ CONFIG_SKB_LARGE=y # CONFIG_HAMRADIO is not set # -# IrDA subsystem support +# IrDA (infrared) support # # CONFIG_IRDA is not set @@ -292,9 +340,18 @@ CONFIG_SKB_LARGE=y # Network device support # CONFIG_NETDEVICES=y + +# +# ARCnet devices +# # CONFIG_ARCNET is not set # CONFIG_DUMMY is not set # CONFIG_EQUALIZER is not set +# CONFIG_NET_SB1000 is not set + +# +# Ethernet (10 or 100Mbit) +# CONFIG_NET_ETHERNET=y # CONFIG_ARM_AM79C961A is not set CONFIG_NET_VENDOR_3COM=y @@ -309,11 +366,12 @@ CONFIG_VORTEX=y # CONFIG_NET_VENDOR_SMC is not set # CONFIG_NET_VENDOR_RACAL is not set # CONFIG_RTL8139 is not set +# CONFIG_SIS900 is not set # CONFIG_YELLOWFIN is not set -# CONFIG_ACENIC is not set # CONFIG_NET_ISA is not set CONFIG_NET_EISA=y # CONFIG_PCNET32 is not set +# CONFIG_ACENIC is not set # CONFIG_AC3200 is not set # CONFIG_APRICOT is not set # CONFIG_CS89x0 is not set @@ -332,23 +390,32 @@ CONFIG_NE2K_PCI=y # CONFIG_NET_POCKET is not set # CONFIG_FDDI is not set # CONFIG_HIPPI is not set -# CONFIG_DLCI is not set # CONFIG_PLIP is not set CONFIG_PPP=m - -# -# CCP compressors for PPP are only built as modules. -# +CONFIG_PPP_ASYNC=m +CONFIG_PPP_DEFLATE=m +CONFIG_PPP_BSDCOMP=m CONFIG_SLIP=m CONFIG_SLIP_COMPRESSED=y CONFIG_SLIP_SMART=y CONFIG_SLIP_MODE_SLIP6=y # CONFIG_NET_RADIO is not set + +# +# Token ring devices +# # CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set # CONFIG_SHAPER is not set + +# +# Wan interfaces +# # CONFIG_HOSTESS_SV11 is not set # CONFIG_COSA is not set -# CONFIG_RCPCI is not set +# CONFIG_SEALEVEL_4021 is not set +# CONFIG_DLCI is not set # # SCSI support @@ -359,8 +426,10 @@ CONFIG_SLIP_MODE_SLIP6=y # Sound # CONFIG_SOUND=m +# CONFIG_SOUND_CMPCI is not set # CONFIG_SOUND_ES1370 is not set # CONFIG_SOUND_ES1371 is not set +# CONFIG_SOUND_ESSSOLO1 is not set # CONFIG_SOUND_SONICVIBES is not set # CONFIG_SOUND_MSNDCLAS is not set # CONFIG_SOUND_MSNDPIN is not set @@ -410,6 +479,7 @@ CONFIG_FAT_FS=m CONFIG_MSDOS_FS=m # CONFIG_UMSDOS_FS is not set CONFIG_VFAT_FS=m +# CONFIG_EFS_FS is not set CONFIG_ISO9660_FS=m CONFIG_JOLIET=y # CONFIG_MINIX_FS is not set @@ -438,12 +508,17 @@ CONFIG_LOCKD=y # # Partition Types # +CONFIG_PARTITION_ADVANCED=y # CONFIG_OSF_PARTITION is not set # CONFIG_MAC_PARTITION is not set CONFIG_MSDOS_PARTITION=y +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set # CONFIG_SGI_PARTITION is not set # CONFIG_SUN_PARTITION is not set # CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set CONFIG_ACORN_PARTITION=y CONFIG_ACORN_PARTITION_ADFS=y # CONFIG_ACORN_PARTITION_ICS is not set @@ -479,6 +554,7 @@ CONFIG_NLS_ISO8859_2=m # CONFIG_NLS_ISO8859_7 is not set # CONFIG_NLS_ISO8859_8 is not set # CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_14 is not set CONFIG_NLS_ISO8859_15=m # CONFIG_NLS_KOI8_R is not set @@ -487,7 +563,7 @@ CONFIG_NLS_ISO8859_15=m # CONFIG_FRAME_POINTER=y CONFIG_DEBUG_ERRORS=y -# CONFIG_DEBUG_USER is not set +CONFIG_DEBUG_USER=y # CONFIG_DEBUG_INFO is not set CONFIG_MAGIC_SYSRQ=y # CONFIG_DEBUG_LL is not set diff --git a/arch/arm/def-configs/victor b/arch/arm/def-configs/victor new file mode 100644 index 000000000..569cc458d --- /dev/null +++ b/arch/arm/def-configs/victor @@ -0,0 +1,207 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_ARM=y + +# +# System and processor type +# +# CONFIG_ARCH_ARC is not set +# CONFIG_ARCH_A5K is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_FOOTBRIDGE is not set +CONFIG_ARCH_SA1100=y +CONFIG_CPU_SA1100=y +# CONFIG_SA1100_BRUTUS is not set +# CONFIG_SA1100_EMPEG is not set +# CONFIG_SA1100_ITSY is not set +# CONFIG_SA1100_PLEB is not set +CONFIG_SA1100_VICTOR=y +# CONFIG_VICTOR_BOARD1 is not set +# CONFIG_ARCH_ACORN is not set +# CONFIG_ISA_DMA is not set +CONFIG_CPU_32=y +# CONFIG_CPU_26 is not set +# CONFIG_CPU_ARM2 is not set +# CONFIG_CPU_ARM3 is not set +# CONFIG_CPU_ARM6 is not set +# CONFIG_CPU_ARM7 is not set +CONFIG_CPU_SA110=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +# CONFIG_ALIGNMENT_TRAP is not set +# CONFIG_TEXT_SECTIONS is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +# CONFIG_MODVERSIONS is not set +# CONFIG_KMOD is not set + +# +# General setup +# +# CONFIG_NET is not set +# CONFIG_SYSVIPC is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_SYSCTL is not set +CONFIG_NWFPE=y +# CONFIG_BINFMT_AOUT is not set +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_ARTHUR is not set +# CONFIG_PARPORT is not set +CONFIG_CMDLINE="" + +# +# Plug and Play support +# +# CONFIG_PNP is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +CONFIG_BLK_DEV_IDE=y + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_HD_IDE is not set +# CONFIG_BLK_DEV_IDEDISK is not set +CONFIG_BLK_DEV_IDECD=y +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDESCSI is not set +# CONFIG_BLK_DEV_CMD640 is not set +# CONFIG_IDE_CHIPSETS is not set + +# +# Additional Block Devices +# +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_MD is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_BLK_DEV_XD is not set +CONFIG_PARIDE_PARPORT=y +# CONFIG_PARIDE is not set +# CONFIG_BLK_DEV_HD is not set + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_SERIAL_SA1100=y +CONFIG_SERIAL_SA1100_CONSOLE=y +# CONFIG_SERIAL is not set +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_UNIX98_PTYS is not set +# CONFIG_MOUSE is not set +# CONFIG_QIC02_TAPE is not set +# CONFIG_WATCHDOG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set + +# +# Video For Linux +# +# CONFIG_VIDEO_DEV is not set + +# +# Joystick support +# +# CONFIG_JOYSTICK is not set +# CONFIG_DTLK is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set + +# +# SCSI support +# +# CONFIG_SCSI is not set + +# +# Filesystems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +CONFIG_ISO9660_FS=y +CONFIG_JOLIET=y +# CONFIG_MINIX_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_PROC_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Partition Types +# +# CONFIG_OSF_PARTITION is not set +# CONFIG_MAC_PARTITION is not set +# CONFIG_MSDOS_PARTITION is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ACORN_PARTITION is not set +CONFIG_NLS=y + +# +# Native Language Support +# +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +CONFIG_NLS_CODEPAGE_850=y +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set + +# +# Kernel hacking +# +CONFIG_FRAME_POINTER=y +CONFIG_DEBUG_ERRORS=y +CONFIG_DEBUG_USER=y +# CONFIG_DEBUG_INFO is not set +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_DEBUG_LL is not set diff --git a/arch/arm/defconfig b/arch/arm/defconfig index 4f1ec7e28..d289870ea 100644 --- a/arch/arm/defconfig +++ b/arch/arm/defconfig @@ -55,7 +55,7 @@ CONFIG_BINFMT_ELF=y # CONFIG_ARTHUR is not set CONFIG_PARPORT=y CONFIG_PARPORT_PC=y -CONFIG_CMDLINE="root=/dev/hda2 ro mem=32M parport=0x378,7 ide0=autotune" +CONFIG_CMDLINE="root=/dev/hda1 ro mem=32M parport=0x378,7 ide0=autotune" CONFIG_LEDS=y CONFIG_LEDS_TIMER=y # CONFIG_LEDS_CPU is not set @@ -89,7 +89,7 @@ CONFIG_IDEDMA_PCI_AUTO=y # CONFIG_BLK_DEV_OPTI621 is not set # CONFIG_BLK_DEV_TRM290 is not set # CONFIG_BLK_DEV_NS87415 is not set -# CONFIG_BLK_DEV_VIA82C586 is not set +# CONFIG_BLK_DEV_VIA82CXXX is not set # CONFIG_BLK_DEV_CMD646 is not set CONFIG_BLK_DEV_SL82C105=y # CONFIG_IDE_CHIPSETS is not set diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index 557aa6fc5..31ca81ddc 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -22,6 +22,7 @@ O_OBJS_rpc = dma-rpc.o iic.o fiq.o O_OBJS_ebsa110 = dma-dummy.o O_OBJS_footbridge = dma-footbridge.o $(ISA_DMA_OBJS) isa.o O_OBJS_nexuspci = dma-dummy.o +O_OBJS_sa1100 = dma-dummy.o fiq.o OX_OBJS_arc = dma.o OX_OBJS_a5k = dma.o @@ -29,11 +30,16 @@ OX_OBJS_rpc = dma.o OX_OBJS_ebsa110 = OX_OBJS_footbridge= dma.o hw-footbridge.o OX_OBJS_nexuspci = +OX_OBJS_sa1100 = all: kernel.o $(HEAD_OBJ) init_task.o O_OBJS += $(O_OBJS_$(MACHINE)) +ifeq ($(CONFIG_DEBUG_LL),y) + O_OBJS += debug-$(PROCESSOR).o +endif + ifeq ($(CONFIG_MODULES),y) OX_OBJS = armksyms.o endif @@ -42,17 +48,15 @@ ifeq ($(CONFIG_ARCH_ACORN),y) OX_OBJS += ecard.o endif -ifeq ($(MACHINE),nexuspci) - ifdef CONFIG_PCI +ifeq ($(CONFIG_PCI),y) + ifeq ($(MACHINE),nexuspci) O_OBJS += plx9080.o - endif -else - ifdef CONFIG_PCI + else O_OBJS += bios32.o dec21285.o endif endif -ifdef CONFIG_LEDS +ifeq ($(CONFIG_LEDS),y) OX_OBJS += leds-$(MACHINE).o endif @@ -73,13 +77,11 @@ endif $(HEAD_OBJ): $(HEAD_OBJ:.o=.S) $(CC) -D__ASSEMBLY__ -DTEXTADDR=$(TEXTADDR) -traditional -c $(HEAD_OBJ:.o=.S) -o $@ -$(ENTRY_OBJ): $(ENTRY_OBJ:.o=.S) - $(CC) $(CFLAGS) -D__ASSEMBLY__ -c $(ENTRY_OBJ:.o=.S) -o $@ - include $(TOPDIR)/Rules.make -$(ENTRY_OBJ): ../lib/constants.h +.S.o: + $(CC) -D__ASSEMBLY__ $(AFLAGS) $(AFLAGS_$@) -c -o $*.o $< # Spell out some dependencies that `make dep' doesn't spot -entry-armv.o: calls.S -entry-armo.o: calls.S +entry-armv.o: calls.S ../lib/constants.h +entry-armo.o: calls.S ../lib/constants.h diff --git a/arch/arm/kernel/armksyms.c b/arch/arm/kernel/armksyms.c index 0905d55b5..ebb2f150d 100644 --- a/arch/arm/kernel/armksyms.c +++ b/arch/arm/kernel/armksyms.c @@ -2,12 +2,14 @@ #include <linux/module.h> #include <linux/user.h> #include <linux/string.h> +#include <linux/fs.h> #include <linux/mm.h> #include <linux/mman.h> #include <linux/pci.h> #include <linux/delay.h> #include <linux/in6.h> +#include <asm/byteorder.h> #include <asm/elf.h> #include <asm/io.h> #include <asm/dma.h> @@ -93,6 +95,9 @@ EXPORT_SYMBOL(__ioremap); EXPORT_SYMBOL(__iounmap); #endif EXPORT_SYMBOL(kernel_thread); +EXPORT_SYMBOL(system_rev); +EXPORT_SYMBOL(system_serial_low); +EXPORT_SYMBOL(system_serial_high); EXPORT_SYMBOL(enable_irq); EXPORT_SYMBOL(disable_irq); @@ -108,7 +113,7 @@ EXPORT_SYMBOL(cpu_clean_cache_area); EXPORT_SYMBOL(cpu_flush_ram_page); EXPORT_SYMBOL(cpu_flush_tlb_all); EXPORT_SYMBOL(cpu_flush_tlb_area); -EXPORT_SYMBOL(cpu_switch_mm); +EXPORT_SYMBOL(cpu_set_pgd); EXPORT_SYMBOL(cpu_set_pmd); EXPORT_SYMBOL(cpu_set_pte); EXPORT_SYMBOL(cpu_flush_icache_area); @@ -165,19 +170,20 @@ EXPORT_SYMBOL_NOVERS(strpbrk); EXPORT_SYMBOL_NOVERS(strtok); EXPORT_SYMBOL_NOVERS(strrchr); EXPORT_SYMBOL_NOVERS(strstr); -EXPORT_SYMBOL_NOVERS(memset); +EXPORT_SYMBOL_NOVERS(__memset); +EXPORT_SYMBOL_NOVERS(memset); /* needed for some versions of gcc */ EXPORT_SYMBOL_NOVERS(memcpy); EXPORT_SYMBOL_NOVERS(memmove); EXPORT_SYMBOL_NOVERS(memcmp); EXPORT_SYMBOL_NOVERS(memscan); -EXPORT_SYMBOL_NOVERS(memzero); +EXPORT_SYMBOL_NOVERS(__memzero); /* user mem (segment) */ #if defined(CONFIG_CPU_32) EXPORT_SYMBOL(__arch_copy_from_user); EXPORT_SYMBOL(__arch_copy_to_user); EXPORT_SYMBOL(__arch_clear_user); -EXPORT_SYMBOL(__arch_strlen_user); +EXPORT_SYMBOL(__arch_strnlen_user); #elif defined(CONFIG_CPU_26) EXPORT_SYMBOL(uaccess_kernel); EXPORT_SYMBOL(uaccess_user); diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c index 35bb0a36a..3bd7a7358 100644 --- a/arch/arm/kernel/bios32.c +++ b/arch/arm/kernel/bios32.c @@ -7,6 +7,7 @@ */ #include <linux/kernel.h> #include <linux/pci.h> +#include <linux/errno.h> #include <linux/init.h> #include <asm/irq.h> @@ -73,6 +74,38 @@ struct pci_fixup pcibios_fixups[] = { }; /* + * Assign new address to PCI resource. We hope our resource information + * is complete. On the PC, we don't re-assign resources unless we are + * forced to do so. + * + * Expects start=0, end=size-1, flags=resource type. + */ + +int __init pcibios_assign_resource(struct pci_dev *dev, int i) +{ + struct resource *r = &dev->resource[i]; + struct resource *pr = pci_find_parent_resource(dev, r); + unsigned long size = r->end + 1; + unsigned long flags = 0; + + if (!pr) + return -EINVAL; + if (r->flags & IORESOURCE_IO) { + if (size > 0x100) + return -EFBIG; + if (allocate_resource(pr, r, size, 0x9000, ~0, 1024)) + return -EBUSY; + flags = PCI_BASE_ADDRESS_SPACE_IO; + } else { + if (allocate_resource(pr, r, size, 0x00100000, 0x7fffffff, size)) + return -EBUSY; + } + if (i < 6) + pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + 4*i, r->start | flags); + return 0; +} + +/* * Assign an address to an I/O range. */ static void __init pcibios_fixup_io_addr(struct pci_dev *dev, struct resource *r, int idx) diff --git a/arch/arm/kernel/debug-armo.S b/arch/arm/kernel/debug-armo.S new file mode 100644 index 000000000..1c6aaccc3 --- /dev/null +++ b/arch/arm/kernel/debug-armo.S @@ -0,0 +1,86 @@ +/* + * linux/arch/arm/kernel/debug-armo.S + * + * Copyright (C) 1999 Russell King + * + * 26-bit debugging code + */ +#include <linux/linkage.h> + + .macro addruart,rx + mov \rx, #0x03000000 + orr \rx, \rx, #0x00010000 + orr \rx, \rx, #0x00000fe0 + .endm + + .macro senduart,rd,rx + strb \rd, [\rx] + .endm + + .macro busyuart,rd,rx +1002: ldrb \rd, [\rx, #0x14] + and \rd, \rd, #0x60 + teq \rd, #0x60 + bne 1002b + .endm + + .macro waituart,rd,rx +1001: ldrb \rd, [\rx, #0x18] + tst \rd, #0x10 + beq 1001b + .endm + + .text +/* + * Useful debugging routines + */ +ENTRY(printhex8) + mov r1, #8 + b printhex + +ENTRY(printhex4) + mov r1, #4 + b printhex + +ENTRY(printhex2) + mov r1, #2 +printhex: ldr r2, =hexbuf + add r3, r2, r1 + mov r1, #0 + strb r1, [r3] +1: and r1, r0, #15 + mov r0, r0, lsr #4 + cmp r1, #10 + addlt r1, r1, #'0' + addge r1, r1, #'a' - 10 + strb r1, [r3, #-1]! + teq r3, r2 + bne 1b + mov r0, r2 + b printascii + + .ltorg + +ENTRY(printascii) + addruart r3 + b 2f +1: waituart r2, r3 + senduart r1, r3 + busyuart r2, r3 + teq r1, #'\n' + moveq r1, #'\r' + beq 1b +2: teq r0, #0 + ldrneb r1, [r0], #1 + teqne r1, #0 + bne 1b + mov pc, lr + +ENTRY(printch) + addruart r3 + mov r1, r0 + mov r0, #0 + b 1b + + .bss +hexbuf: .space 16 diff --git a/arch/arm/kernel/debug-armv.S b/arch/arm/kernel/debug-armv.S new file mode 100644 index 000000000..fef1489b7 --- /dev/null +++ b/arch/arm/kernel/debug-armv.S @@ -0,0 +1,206 @@ +/* + * linux/arch/arm/kernel/debug-armv.S + * + * Copyright (C) 1994-1999 Russell King + * + * 32-bit debugging code + */ +#include <linux/config.h> +#include <linux/linkage.h> +#include <asm/hardware.h> +#include <asm/dec21285.h> + + .text + +/* + * Some debugging routines (useful if you've got MM problems and + * printk isn't working). For DEBUGGING ONLY!!! Do not leave + * references to these in a production kernel! + */ +#if defined(CONFIG_ARCH_RPC) + .macro addruart,rx + mov \rx, #0xe0000000 + orr \rx, \rx, #0x00010000 + orr \rx, \rx, #0x00000fe0 + .endm + + .macro senduart,rd,rx + strb \rd, [\rx] + .endm + + .macro busyuart,rd,rx +1002: ldrb \rd, [\rx, #0x14] + and \rd, \rd, #0x60 + teq \rd, #0x60 + bne 1002b + .endm + + .macro waituart,rd,rx +1001: ldrb \rd, [\rx, #0x18] + tst \rd, #0x10 + beq 1001b + .endm + +#elif defined(CONFIG_ARCH_EBSA110) + .macro addruart,rx + mov \rx, #0xf0000000 + orr \rx, \rx, #0x00000be0 + .endm + + .macro senduart,rd,rx + strb \rd, [\rx] + .endm + + .macro busyuart,rd,rx +1002: ldrb \rd, [\rx, #0x14] + and \rd, \rd, #0x60 + teq \rd, #0x60 + bne 1002b + .endm + + .macro waituart,rd,rx +1001: ldrb \rd, [\rx, #0x18] + tst \rd, #0x10 + beq 1001b + .endm + +#elif defined(CONFIG_HOST_FOOTBRIDGE) || defined(CONFIG_ADDIN_FOOTBRIDGE) +#ifndef CONFIG_DEBUG_DC21285_PORT + /* For NetWinder debugging */ + .macro addruart,rx + mov \rx, #0xff000000 + orr \rx, \rx, #0x000003f8 + .endm + + .macro senduart,rd,rx + strb \rd, [\rx] + .endm + + .macro busyuart,rd,rx +1002: ldrb \rd, [\rx, #0x5] + and \rd, \rd, #0x60 + teq \rd, #0x60 + bne 1002b + .endm + + .macro waituart,rd,rx +1001: ldrb \rd, [\rx, #0x6] + tst \rd, #0x10 + beq 1001b + .endm +#else + /* For EBSA285 debugging */ + .equ dc21285_high, ARMCSR_BASE & 0xff000000 + .equ dc21285_low, ARMCSR_BASE & 0x00ffffff + + .macro addruart,rx + mov \rx, #dc21285_high + .if dc21285_low + orr \rx, \rx, #dc21285_low + .endif + .endm + + .macro senduart,rd,rx + str \rd, [\rx, #0x160] @ UARTDR + .endm + + .macro busyuart,rd,rx +1001: ldr \rd, [\rx, #0x178] @ UARTFLG + tst \rd, #1 << 3 + bne 1001b + .endm + + .macro waituart,rd,rx + .endm +#endif +#elif defined(CONFIG_ARCH_NEXUSPCI) + .macro addruart,rx + ldr \rx, =0xfff00000 + .endm + + .macro senduart,rd,rx + str \rd, [\rx, #0xc] + .endm + + .macro busyuart,rd,rx +1001: ldr \rd, [\rx, #0x4] + tst \rd, #1 << 0 + bne 1001b + .endm + + .macro waituart,rd,rx + .endm + +#elif defined(CONFIG_ARCH_SA1100) + .macro addruart,rx + mov \rx, #0xf8000000 + add \rx, \rx, #0x00050000 + .endm + + .macro senduart,rd,rx + str \rd, [\rx, #0x14] @ UARTDR + .endm + + .macro busyuart,rd,rx +1001: ldr \rd, [\rx, #0x20] @ UTSR1 + tst \rd, #1 << 2 + beq 1001b + .endm + +#else +#error Unknown architecture +#endif + +/* + * Useful debugging routines + */ +ENTRY(printhex8) + mov r1, #8 + b printhex + +ENTRY(printhex4) + mov r1, #4 + b printhex + +ENTRY(printhex2) + mov r1, #2 +printhex: ldr r2, =hexbuf + add r3, r2, r1 + mov r1, #0 + strb r1, [r3] +1: and r1, r0, #15 + mov r0, r0, lsr #4 + cmp r1, #10 + addlt r1, r1, #'0' + addge r1, r1, #'a' - 10 + strb r1, [r3, #-1]! + teq r3, r2 + bne 1b + mov r0, r2 + b printascii + + .ltorg + +ENTRY(printascii) + addruart r3 + b 2f +1: waituart r2, r3 + senduart r1, r3 + busyuart r2, r3 + teq r1, #'\n' + moveq r1, #'\r' + beq 1b +2: teq r0, #0 + ldrneb r1, [r0], #1 + teqne r1, #0 + bne 1b + mov pc, lr + +ENTRY(printch) + addruart r3 + mov r1, r0 + mov r0, #0 + b 1b + + .bss +hexbuf: .space 16 diff --git a/arch/arm/kernel/dec21285.c b/arch/arm/kernel/dec21285.c index 6a4988b15..42a9a616f 100644 --- a/arch/arm/kernel/dec21285.c +++ b/arch/arm/kernel/dec21285.c @@ -13,241 +13,318 @@ #include <linux/init.h> #include <linux/ioport.h> +#include <asm/dec21285.h> +#include <asm/io.h> #include <asm/irq.h> #include <asm/system.h> -#include <asm/hardware.h> #define MAX_SLOTS 21 -extern void pcibios_fixup_ebsa285(struct pci_dev *dev); -extern void pcibios_init_ebsa285(void); - -int -pcibios_present(void) -{ - return 1; -} +extern int setup_arm_irq(int, struct irqaction *); +extern void pcibios_report_device_errors(void); +extern int (*pci_irq_fixup)(struct pci_dev *dev); static unsigned long -pcibios_base_address(unsigned char bus, unsigned char dev_fn) +dc21285_base_address(struct pci_dev *dev, int where) { - if (bus == 0) { - int slot = PCI_SLOT(dev_fn); - - if (slot < MAX_SLOTS) - return PCICFG0_BASE + 0xc00000 + - (slot << 11) + (PCI_FUNC(dev_fn) << 8); - else - return 0; - } else - return PCICFG1_BASE | (bus << 16) | (dev_fn << 8); + unsigned long addr = 0; + unsigned int devfn = dev->devfn; + + if (dev->bus->number != 0) + addr = PCICFG1_BASE | (dev->bus->number << 16) | (devfn << 8); + else if (devfn < PCI_DEVFN(MAX_SLOTS, 0)) + addr = PCICFG0_BASE | 0xc00000 | (devfn << 8); + + return addr; } -int -pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, - unsigned char where, unsigned char *val) +static int +dc21285_read_config_byte(struct pci_dev *dev, int where, u8 *value) { - unsigned long addr = pcibios_base_address(bus, dev_fn); - unsigned char v; + unsigned long addr = dc21285_base_address(dev, where); + u8 v; if (addr) - __asm__("ldr%?b %0, [%1, %2]" - : "=r" (v) - : "r" (addr), "r" (where)); + asm("ldr%?b %0, [%1, %2]" + : "=r" (v) : "r" (addr), "r" (where)); else v = 0xff; - *val = v; + + *value = v; + return PCIBIOS_SUCCESSFUL; } -int -pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, - unsigned char where, unsigned short *val) +static int +dc21285_read_config_word(struct pci_dev *dev, int where, u16 *value) { - unsigned long addr = pcibios_base_address(bus, dev_fn); - unsigned short v; + unsigned long addr = dc21285_base_address(dev, where); + u16 v; if (addr) - __asm__("ldr%?h %0, [%1, %2]" - : "=r" (v) - : "r" (addr), "r" (where)); + asm("ldr%?h %0, [%1, %2]" + : "=r" (v) : "r" (addr), "r" (where)); else v = 0xffff; - *val = v; + + *value = v; + return PCIBIOS_SUCCESSFUL; } -int -pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, - unsigned char where, unsigned int *val) +static int +dc21285_read_config_dword(struct pci_dev *dev, int where, u32 *value) { - unsigned long addr = pcibios_base_address(bus, dev_fn); - unsigned int v; + unsigned long addr = dc21285_base_address(dev, where); + u32 v; if (addr) - __asm__("ldr%? %0, [%1, %2]" - : "=r" (v) - : "r" (addr), "r" (where)); + asm("ldr%? %0, [%1, %2]" + : "=r" (v) : "r" (addr), "r" (where)); else v = 0xffffffff; - *val = v; + + *value = v; + return PCIBIOS_SUCCESSFUL; } -int -pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, - unsigned char where, unsigned char val) +static int +dc21285_write_config_byte(struct pci_dev *dev, int where, u8 value) { - unsigned long addr = pcibios_base_address(bus, dev_fn); + unsigned long addr = dc21285_base_address(dev, where); if (addr) - __asm__("str%?b %0, [%1, %2]" - : : "r" (val), "r" (addr), "r" (where)); + asm("str%?b %0, [%1, %2]" + : : "r" (value), "r" (addr), "r" (where)); + return PCIBIOS_SUCCESSFUL; } -int -pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, - unsigned char where, unsigned short val) +static int +dc21285_write_config_word(struct pci_dev *dev, int where, u16 value) { - unsigned long addr = pcibios_base_address(bus, dev_fn); + unsigned long addr = dc21285_base_address(dev, where); if (addr) - __asm__("str%?h %0, [%1, %2]" - : : "r" (val), "r" (addr), "r" (where)); + asm("str%?h %0, [%1, %2]" + : : "r" (value), "r" (addr), "r" (where)); + return PCIBIOS_SUCCESSFUL; } -int -pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, - unsigned char where, unsigned int val) +static int +dc21285_write_config_dword(struct pci_dev *dev, int where, u32 value) { - unsigned long addr = pcibios_base_address(bus, dev_fn); + unsigned long addr = dc21285_base_address(dev, where); if (addr) - __asm__("str%? %0, [%1, %2]" - : : "r" (val), "r" (addr), "r" (where)); + asm("str%? %0, [%1, %2]" + : : "r" (value), "r" (addr), "r" (where)); + return PCIBIOS_SUCCESSFUL; } -void __init pci_set_cmd(struct pci_dev *dev, unsigned short clear, unsigned short set) +static struct pci_ops dc21285_ops = { + dc21285_read_config_byte, + dc21285_read_config_word, + dc21285_read_config_dword, + dc21285_write_config_byte, + dc21285_write_config_word, + dc21285_write_config_dword, +}; + +/* + * Warn on PCI errors. + */ +static void +dc21285_error(int irq, void *dev_id, struct pt_regs *regs) { - unsigned short cmd; + static unsigned long next_warn; + unsigned long cmd = *CSR_PCICMD & 0x0000ffff; + unsigned long ctrl = (*CSR_SA110_CNTL) & 0xffffde07; + unsigned long irqstatus = *CSR_IRQ_RAWSTATUS; + int warn = time_after_eq(jiffies, next_warn); - pci_read_config_word(dev, PCI_COMMAND, &cmd); - cmd = (cmd & ~clear) | set; - pci_write_config_word(dev, PCI_COMMAND, cmd); -} + ctrl |= SA110_CNTL_DISCARDTIMER; -void __init pci_set_base_addr(struct pci_dev *dev, int idx, unsigned int addr) -{ - int reg = PCI_BASE_ADDRESS_0 + (idx << 2); + if (warn) { + next_warn = jiffies + HZ; + printk(KERN_DEBUG "PCI: "); + } - pci_write_config_dword(dev, reg, addr); - pci_read_config_dword(dev, reg, &addr); + if (irqstatus & (1 << 31)) { + if (warn) + printk("parity error "); + cmd |= 1 << 31; + } - dev->base_address[idx] = addr; -} + if (irqstatus & (1 << 30)) { + if (warn) + printk("target abort "); + cmd |= 1 << 28; + } -void __init pcibios_fixup(void) -{ - struct pci_dev *dev; + if (irqstatus & (1 << 29)) { + if (warn) + printk("master abort "); + cmd |= 1 << 29; + } - for (dev = pci_devices; dev; dev = dev->next) { - pcibios_fixup_ebsa285(dev); + if (irqstatus & (1 << 28)) { + if (warn) + printk("data parity error "); + cmd |= 1 << 24; + } - pcibios_write_config_byte(dev->bus->number, dev->devfn, - PCI_INTERRUPT_LINE, dev->irq); + if (irqstatus & (1 << 27)) { + if (warn) + printk("discard timer expired "); + ctrl &= ~SA110_CNTL_DISCARDTIMER; + } - printk(KERN_DEBUG - "PCI: %02x:%02x [%04x/%04x] on irq %d\n", - dev->bus->number, dev->devfn, - dev->vendor, dev->device, dev->irq); + if (irqstatus & (1 << 23)) { + if (warn) + printk("system error "); + ctrl |= SA110_CNTL_RXSERR; } - hw_init(); + if (warn) + printk("pc=[<%08lX>]\n", instruction_pointer(regs)); + + pcibios_report_device_errors(); + + *CSR_PCICMD = cmd; + *CSR_SA110_CNTL = ctrl; } -void __init pcibios_init(void) -{ - unsigned int mem_size = (unsigned int)high_memory - PAGE_OFFSET; - unsigned long cntl; +static struct irqaction dc21285_error_action = { + dc21285_error, SA_INTERRUPT, 0, "PCI error", NULL, NULL +}; - *CSR_SDRAMBASEMASK = (mem_size - 1) & 0x0ffc0000; - *CSR_SDRAMBASEOFFSET = 0; - *CSR_ROMBASEMASK = 0x80000000; - *CSR_CSRBASEMASK = 0; - *CSR_CSRBASEOFFSET = 0; - *CSR_PCIADDR_EXTN = 0; +static int irqmap_ebsa[] __initdata = { IRQ_IN1, IRQ_IN0, IRQ_PCI, IRQ_IN3 }; -#ifdef CONFIG_HOST_FOOTBRIDGE - /* - * Against my better judgement, Philip Blundell still seems - * to be saying that we should initialise the PCI stuff here - * when the PCI_CFN bit is not set, dispite my comment below, - * which he decided to remove. If it is not set, then - * the card is in add-in mode, and we're in a machine where - * the bus is set up by 'others'. - * - * We should therefore not mess about with the mapping in - * anyway, and we should not be using the virt_to_bus functions - * that exist in the HOST architecture mode (since they assume - * a fixed mapping). - * - * Instead, you should be using ADDIN mode, which allows for - * this situation. This does assume that you have correctly - * initialised the PCI bus, which you must have done to get - * your PC booted. - * - * Unfortunately, he seems to be blind to this. I guess he'll - * also remove all this. - * - * And THIS COMMENT STAYS, even if this gets patched, thank - * you. - */ - - /* - * Map our SDRAM at a known address in PCI space, just in case - * the firmware had other ideas. Using a nonzero base is - * necessary, since some VGA cards forcefully use PCI addresses - * in the range 0x000a0000 to 0x000c0000. (eg, S3 cards). - * - * NOTE! If you need to chec the PCI_CFN bit in the SA110 - * control register then you've configured the kernel wrong. - * If you're not using host mode, then DO NOT set - * CONFIG_HOST_FOOTBRIDGE, but use CONFIG_ADDIN_FOOTBRIDGE - * instead. In this case, you MUST supply some firmware - * to allow your PC to boot, plus we should not modify the - * mappings that the PC BIOS has set up for us. - */ - *CSR_PCICACHELINESIZE = 0x00002008; - *CSR_PCICSRBASE = 0; - *CSR_PCICSRIOBASE = 0; - *CSR_PCISDRAMBASE = virt_to_bus((void *)PAGE_OFFSET); - *CSR_PCIROMBASE = 0; - *CSR_PCICMD = PCI_COMMAND_IO | PCI_COMMAND_MEMORY | - PCI_COMMAND_MASTER | PCI_COMMAND_FAST_BACK | - PCI_COMMAND_INVALIDATE | PCI_COMMAND_PARITY | - (1 << 31) | (1 << 29) | (1 << 28) | (1 << 24); -#endif +static int __init ebsa_irqval(struct pci_dev *dev) +{ + u8 pin; + + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); - /* - * Clear any existing errors - we aren't - * interested in historical data... - */ - cntl = *CSR_SA110_CNTL & 0xffffde07; - *CSR_SA110_CNTL = cntl | SA110_CNTL_RXSERR; + return irqmap_ebsa[(PCI_SLOT(dev->devfn) + pin) & 3]; +} - pcibios_init_ebsa285(); +static int irqmap_cats[] __initdata = { IRQ_PCI, IRQ_IN0, IRQ_IN1, IRQ_IN3 }; - printk(KERN_DEBUG"PCI: DEC21285 revision %02lX\n", *CSR_CLASSREV & 0xff); +static int __init cats_irqval(struct pci_dev *dev) +{ + if (dev->irq >= 128) + return 16 + (dev->irq & 0x1f); + + switch (dev->irq) { + case 1 ... 4: + return irqmap_cats[dev->irq - 1]; + + default: + printk("PCI: device %02x:%02x has unknown irq line %x\n", + dev->bus->number, dev->devfn, dev->irq); + case 0: + break; + } + return 0; } -void __init pcibios_fixup_bus(struct pci_bus *bus) +static int __init netwinder_irqval(struct pci_dev *dev) { +#define DEV(v,d) ((v)<<16|(d)) + switch (DEV(dev->vendor, dev->device)) { + case DEV(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21142): + return IRQ_NETWINDER_ETHER100; + + case DEV(PCI_VENDOR_ID_WINBOND2, 0x5a5a): + return IRQ_NETWINDER_ETHER10; + + case DEV(PCI_VENDOR_ID_WINBOND, PCI_DEVICE_ID_WINBOND_83C553): + return 0; + + case DEV(PCI_VENDOR_ID_WINBOND, PCI_DEVICE_ID_WINBOND_82C105): + return IRQ_ISA_HARDDISK1; + + case DEV(PCI_VENDOR_ID_INTERG, PCI_DEVICE_ID_INTERG_2000): + return IRQ_NETWINDER_VGA; + + default: + printk(KERN_ERR "PCI: %02X:%02X [%04X:%04X] unknown device\n", + dev->bus->number, dev->devfn, + dev->vendor, dev->device); + return 0; + } } -char * __init pcibios_setup(char *str) +struct pci_ops * __init dc21285_init(int pass) { - return str; + unsigned int mem_size; + unsigned long cntl; + + if (pass == 0) { + mem_size = (unsigned int)high_memory - PAGE_OFFSET; + *CSR_SDRAMBASEMASK = (mem_size - 1) & 0x0ffc0000; + *CSR_SDRAMBASEOFFSET = 0; + *CSR_ROMBASEMASK = 0x80000000; + *CSR_CSRBASEMASK = 0; + *CSR_CSRBASEOFFSET = 0; + *CSR_PCIADDR_EXTN = 0; + +#ifdef CONFIG_HOST_FOOTBRIDGE + /* + * Map our SDRAM at a known address in PCI space, just in case + * the firmware had other ideas. Using a nonzero base is + * necessary, since some VGA cards forcefully use PCI addresses + * in the range 0x000a0000 to 0x000c0000. (eg, S3 cards). + */ + *CSR_PCICACHELINESIZE = 0x00002008; + *CSR_PCICSRBASE = 0; + *CSR_PCICSRIOBASE = 0; + *CSR_PCISDRAMBASE = virt_to_bus((void *)PAGE_OFFSET); + *CSR_PCIROMBASE = 0; + *CSR_PCICMD = PCI_COMMAND_IO | PCI_COMMAND_MEMORY | + PCI_COMMAND_MASTER | PCI_COMMAND_FAST_BACK | + PCI_COMMAND_INVALIDATE | PCI_COMMAND_PARITY | + (1 << 31) | (1 << 29) | (1 << 28) | (1 << 24); +#endif + + printk(KERN_DEBUG"PCI: DC21285 footbridge, revision %02lX\n", + *CSR_CLASSREV & 0xff); + + switch (machine_arch_type) { + case MACH_TYPE_EBSA285: + pci_irq_fixup = ebsa_irqval; + break; + + case MACH_TYPE_CATS: + pci_irq_fixup = cats_irqval; + break; + + case MACH_TYPE_NETWINDER: + pci_irq_fixup = netwinder_irqval; + break; + } + + return &dc21285_ops; + } else { + /* + * Clear any existing errors - we aren't + * interested in historical data... + */ + cntl = *CSR_SA110_CNTL & 0xffffde07; + *CSR_SA110_CNTL = cntl | SA110_CNTL_RXSERR; + cntl = *CSR_PCICMD & 0x0000ffff; + *CSR_PCICMD = cntl | 1 << 31 | 1 << 29 | 1 << 28 | 1 << 24; + + /* + * Initialise PCI error IRQ after we've finished probing + */ + setup_arm_irq(IRQ_PCI_ERR, &dc21285_error_action); + + return NULL; + } } diff --git a/arch/arm/kernel/dma-dummy.c b/arch/arm/kernel/dma-dummy.c index 7efc85363..ebb9755c1 100644 --- a/arch/arm/kernel/dma-dummy.c +++ b/arch/arm/kernel/dma-dummy.c @@ -17,15 +17,18 @@ int request_dma(int channel, const char *device_id) return -EINVAL; } -void free_dma(int channel) -{ -} - -int get_dma_list(char *buf) +int no_dma(void) { return 0; } -void __init init_dma(void) -{ -} +#define GLOBAL_ALIAS(_a,_b) asm (".set " #_a "," #_b "; .globl " #_a) +GLOBAL_ALIAS(disable_dma, no_dma); +GLOBAL_ALIAS(enable_dma, no_dma); +GLOBAL_ALIAS(free_dma, no_dma); +GLOBAL_ALIAS(get_dma_residue, no_dma); +GLOBAL_ALIAS(get_dma_list, no_dma); +GLOBAL_ALIAS(set_dma_mode, no_dma); +GLOBAL_ALIAS(set_dma_count, no_dma); +GLOBAL_ALIAS(set_dma_addr, no_dma); +GLOBAL_ALIAS(init_dma, no_dma); diff --git a/arch/arm/kernel/dma-footbridge.c b/arch/arm/kernel/dma-footbridge.c index 016b11c15..65875831c 100644 --- a/arch/arm/kernel/dma-footbridge.c +++ b/arch/arm/kernel/dma-footbridge.c @@ -13,14 +13,11 @@ #include <linux/config.h> #include <linux/sched.h> -#include <linux/malloc.h> -#include <linux/mman.h> +#include <linux/errno.h> #include <linux/init.h> -#include <asm/page.h> #include <asm/dma.h> #include <asm/io.h> -#include <asm/hardware.h> #include "dma.h" #include "dma-isa.h" diff --git a/arch/arm/kernel/dma-isa.c b/arch/arm/kernel/dma-isa.c index f5f98bbac..74967a055 100644 --- a/arch/arm/kernel/dma-isa.c +++ b/arch/arm/kernel/dma-isa.c @@ -126,6 +126,13 @@ void isa_disable_dma(int channel, dma_t *dma) outb(channel | 4, isa_dma_port[channel][ISA_DMA_MASK]); } +static struct resource dma_resources[] = { + { "dma1", 0x0000, 0x000f }, + { "dma low page", 0x0080, 0x008f }, + { "dma2", 0x00c0, 0x00df }, + { "dma high page", 0x0480, 0x048f } +}; + int __init isa_init_dma(void) { int dmac_found; diff --git a/arch/arm/kernel/dma-rpc.c b/arch/arm/kernel/dma-rpc.c index e27232255..ad9afac30 100644 --- a/arch/arm/kernel/dma-rpc.c +++ b/arch/arm/kernel/dma-rpc.c @@ -357,6 +357,8 @@ int arch_set_dma_speed(dmach_t channel, dma_t *dma, int cycle) } outb(tcr, IOMD_DMATCR); + + return speed; } void __init arch_dma_init(dma_t *dma) diff --git a/arch/arm/kernel/ecard.c b/arch/arm/kernel/ecard.c index c4b0efef8..99b2b2b1d 100644 --- a/arch/arm/kernel/ecard.c +++ b/arch/arm/kernel/ecard.c @@ -82,7 +82,7 @@ static unsigned int have_expmask; /* List of descriptions of cards which don't have an extended * identification, or chunk directories containing a description. */ -static const struct expcard_blacklist __init blacklist[] = { +static struct expcard_blacklist __initdata blacklist[] = { { MANU_ACORN, PROD_ACORN_ETHER1, "Acorn Ether1" } }; @@ -827,6 +827,8 @@ unsigned int ecard_address(ecard_t *ec, card_type_t type, card_speed_t speed) ectcr |= 1 << slot; break; #endif + default: + break; } #ifdef IOMD_ECTCR diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S index 3e015d866..7e6d4fe51 100644 --- a/arch/arm/kernel/entry-armv.S +++ b/arch/arm/kernel/entry-armv.S @@ -298,6 +298,30 @@ irq_prio_ebsa110: .macro irq_prio_table .endm + +#elif defined(CONFIG_ARCH_SA1100) + + .macro disable_fiq + .endm + + .macro get_irqnr_and_base, irqnr, irqstat, base + mov r4, #0xfa000000 @ ICIP = 0xfa050000 + add r4, r4, #0x00050000 + ldr \irqstat, [r4] @ get irqs + ldr \irqnr, [r4, #4] @ ICMR = 0xfa050004 + ands \irqstat, \irqstat, \irqnr + mov \irqnr, #0 + beq 1002f +1001: tst \irqstat, #1 + addeq \irqnr, \irqnr, #1 + moveq \irqstat, \irqstat, lsr #1 + beq 1001b +1002: + .endm + + .macro irq_prio_table + .endm + #else #error Unknown architecture #endif @@ -449,11 +473,15 @@ __dabt_svc: sub sp, sp, #S_FRAME_SIZE add r5, sp, #S_SP mov r1, lr stmia r5, {r0 - r4} @ save sp_SVC, lr_SVC, pc, cpsr, old_ro + + mrs r9, cpsr @ Enable interrupts if they were tst r3, #I_BIT - mrseq r0, cpsr @ Enable interrupts if they were - biceq r0, r0, #I_BIT @ previously - msreq cpsr, r0 + biceq r9, r9, #I_BIT @ previously + mov r0, r2 +/* + * This routine must not corrupt r9 + */ #ifdef MULTI_CPU ldr r2, .LCprocfns mov lr, pc @@ -461,6 +489,7 @@ __dabt_svc: sub sp, sp, #S_FRAME_SIZE #else bl cpu_data_abort #endif + msr cpsr, r9 mov r3, sp bl SYMBOL_NAME(do_DataAbort) ldr r0, [sp, #S_PSR] @@ -544,9 +573,6 @@ __dabt_usr: sub sp, sp, #S_FRAME_SIZE @ Allocate frame size in one go #endif mov fp, #0 - mrs r2, cpsr @ Enable interrupts if they were - bic r2, r2, #I_BIT @ previously - msr cpsr, r2 #ifdef MULTI_CPU ldr r2, .LCprocfns mov lr, pc @@ -554,6 +580,10 @@ __dabt_usr: sub sp, sp, #S_FRAME_SIZE @ Allocate frame size in one go #else bl cpu_data_abort #endif + mrs r3, cpsr @ Enable interrupts if they were + bic r3, r3, #I_BIT @ previously + msr cpsr, r3 + mov r3, sp adrsvc al, lr, ret_from_sys_call b SYMBOL_NAME(do_DataAbort) diff --git a/arch/arm/kernel/head-armo.S b/arch/arm/kernel/head-armo.S index d6b2b79e6..df5b02ae8 100644 --- a/arch/arm/kernel/head-armo.S +++ b/arch/arm/kernel/head-armo.S @@ -1,5 +1,5 @@ /* - * linux/arch/arm/kernel/head.S + * linux/arch/arm/kernel/head-armo.S * * Copyright (C) 1994, 1995, 1996, 1997 Russell King * @@ -7,11 +7,13 @@ */ #include <linux/linkage.h> - .text - .align + .globl SYMBOL_NAME(swapper_pg_dir) + .equ SYMBOL_NAME(swapper_pg_dir), 0x0207d000 + /* * Entry point. */ + .section ".text.init",#alloc,#execinstr ENTRY(stext) ENTRY(_stext) __entry: cmp pc, #0x02000000 diff --git a/arch/arm/kernel/head-armv.S b/arch/arm/kernel/head-armv.S index e9a05aed6..16436684e 100644 --- a/arch/arm/kernel/head-armv.S +++ b/arch/arm/kernel/head-armv.S @@ -1,54 +1,60 @@ /* - * linux/arch/arm/kernel/head32.S + * linux/arch/arm/kernel/head-armv.S * - * Copyright (C) 1994-1998 Russell King + * Copyright (C) 1994-1999 Russell King * - * Kernel 32 bit startup code for ARM6 / ARM7 / StrongARM + * 32-bit kernel startup code for all architectures */ #include <linux/config.h> #include <linux/linkage.h> + +#include <asm/assembler.h> #include <asm/hardware.h> #include <asm/dec21285.h> +#if (TEXTADDR & 0xffff) != 0x8000 +#error TEXTADDR must start at 0xXXXX8000 +#endif + +#define SWAPPER_PGDIR_OFFSET 0x4000 + .globl SYMBOL_NAME(swapper_pg_dir) - .equ SYMBOL_NAME(swapper_pg_dir), TEXTADDR - 0x4000 + .equ SYMBOL_NAME(swapper_pg_dir), TEXTADDR - 0x8000 + SWAPPER_PGDIR_OFFSET .section ".text.init",#alloc,#execinstr ENTRY(stext) ENTRY(_stext) -#if (TEXTADDR & 0xffff) != 0x8000 -#error TEXTADDR must start at 0xXXXX8000 -#endif - #ifdef CONFIG_ARCH_NETWINDER +/* + * Compatability cruft for old NetWinder NeTTroms. This + * code is currently scheduled for destruction in 2.5.xx + */ + .rept 8 mov r0, r0 - mov r0, r0 - mov r0, r0 - mov r0, r0 - mov r0, r0 - mov r0, r0 - mov r0, r0 - mov r0, r0 + .endr adr r2, 1f ldmdb r2, {r7, r8} - and r3, r2, #0x0000c000 - teq r3, #0x00008000 + and r3, r2, #0xc000 + teq r3, #0x8000 beq __entry bic r3, r2, #0xc000 orr r3, r3, #0x8000 mov r0, r3 - mov r4, #32 + mov r4, #64 sub r5, r8, r7 b 1f .word _stext - .word _end + .word __bss_start -1: ldmia r2!, {r6, r7, r8, r9} +1: + .rept 4 + ldmia r2!, {r6, r7, r8, r9} stmia r3!, {r6, r7, r8, r9} - subs r4, r4, #16 + .endr + subs r4, r4, #64 bcs 1b movs r4, r5 mov r5, #0 @@ -59,78 +65,114 @@ ENTRY(_stext) #endif /* - * Entry point and restart point. Entry *must* be called with r0 == 0, - * MMU off. Note! These should be unique!!! Please read Documentation/ARM-README - * for more information. - * - * r1 = 0 -> DEC EBSA-110 - * r1 = 1 -> Acorn RiscPC - * r1 = 2 -> ebsit - * r1 = 3 -> nexuspci - * r1 = 4 -> DEC EBSA-285 - * r1 = 5 -> Corel Netwinder - * r1 = 6 -> CATS - * r1 = 7 -> tbox - * r1 = 8 -> SA110/21285 as co-processor - * r1 = 9 -> CL-PS7110 system - * r1 = 12 -> SA1100 based system + * Entry point. Entry *must* be called with r0 == 0, with the MMU off. + * r1 contains the unique architecture number. See + * linux/arch/arm/kernel/setup.c machine_desc[] array for the complete + * list. If you require a new number, please follow the instructions + * given in Documentation/ARM-README. */ +__entry: teq r0, #0 + movne r0, #'i' + bne __error + bl __lookup_processor_type + teq r10, #0 @ invalid processor? + moveq r0, #'p' + beq __error + bl __lookup_architecture_type + teq r7, #0 @ invalid architecture? + moveq r0, #'a' + beq __error + bl __create_page_tables + adr lr, __aligned_call + add pc, r10, #12 @ flush caches (returns ctrl reg) + +__switch_data: .long __mmap_switched + .long SYMBOL_NAME(__bss_start) + .long SYMBOL_NAME(_end) + .long SYMBOL_NAME(processor_id) + .long SYMBOL_NAME(__machine_arch_type) + .long SYMBOL_NAME(cr_alignment) + .long SYMBOL_NAME(init_task_union)+8192 + + /* + * This needs to be aligned to a cache line. + */ + .align 5 +__aligned_call: + ldr lr, __switch_data +#ifdef CONFIG_ALIGNMENT_TRAP + orr r0, r0, #2 @ ...........A. +#endif + mcr p15, 0, r0, c1, c0 + mov pc, lr + + /* + * This code follows on after the page + * table switch and jump above. + * + * r0 = processor control register + * r1 = machine ID + * r9 = processor ID + */ + .align 5 +__mmap_switched: + adr r3, __switch_data + 4 + ldmia r3, {r4, r5, r6, r7, r8, sp} @ Setup stack + + mov fp, #0 @ Clear BSS +1: cmp r4, r5 + strcc fp, [r4],#4 + bcc 1b + + str r9, [r6] @ Save processor ID + str r1, [r7] @ Save machine type + bic r2, r0, #2 @ Clear 'A' bit + stmia r8, {r0, r2} @ Save control register values + b SYMBOL_NAME(start_kernel) + -__entry: teq r0, #0 @ check for illegal entry... - bne .Lerror @ loop indefinitely - cmp r1, #9 @ Unknown machine architecture - bge .Lerror -/* First thing to do is to get the page tables set up so that we can call - * the kernel in the correct place. This is relocatable code... - * - Read processor ID register (CP#15, CR0). - */ - mrc p15, 0, r9, c0, c0 @ get Processor ID -/* Values are: - * XX01XXXX = ARMv4 architecture (StrongARM) - * XX00XXXX = ARMv3 architecture - * 4156061X = ARM 610 - */ - adr r10, .LCProcTypes -1: ldmia r10!, {r5, r6, r8} @ Get Set, Mask, MMU Flags - teq r5, #0 @ End of list? - beq .Lerror - eor r5, r5, r9 - tst r5, r6 - addne r10, r10, #8 - bne 1b - adr r4, .LCMachTypes - add r4, r4, r1, lsl #4 - ldmia r4, {r4, r5, r6, r7} /* - * r4 = page dir in physical ram + * Setup the initial page tables. We only setup the barest + * amount which are required to get the kernel running, which + * generally means mapping in the kernel code. + * + * We only map in 2MB of RAM, which should be sufficient in + * all cases. + * + * r4 = physical address of page tables * r5 = physical address of start of RAM - * r6 = I/O address + * r6 = physical IO address + * r7 = byte offset into page tables for IO + * r8 = page table flags */ +__create_page_tables: mov r0, r4 mov r3, #0 - add r2, r0, #0x4000 -1: str r3, [r0], #4 @ Clear page table + add r2, r0, #0x4000 @ Clear page table +1: str r3, [r0], #4 + str r3, [r0], #4 + str r3, [r0], #4 + str r3, [r0], #4 teq r0, r2 bne 1b -/* - * Add enough entries to allow the kernel to be called. - * It will sort out the real mapping in paging_init. - * We map in 2MB of memory into (TEXTADDR-0x8000) + 2MB - */ + /* + * map in two sections (2MB) for kernel. + * these are marked cacheable and bufferable. + */ add r0, r4, #(TEXTADDR - 0x8000) >> 18 - mov r3, #0x0000000c @ SECT_CACHEABLE | SECT_BUFFERABLE + mov r3, #0x0c orr r3, r3, r8 add r3, r3, r5 str r3, [r0], #4 add r3, r3, #1 << 20 str r3, [r0], #4 - add r3, r3, #1 << 20 #ifdef CONFIG_DEBUG_LL -/* Map in IO space - * This allows debug messages to be output via a serial - * before/while paging_init. - */ + /* + * Map in IO space for serial debugging. + * This allows debug messages to be output + * via a serial before paging_init. + */ add r0, r4, r7 orr r3, r6, r8 add r2, r0, #0x0800 @@ -147,45 +189,39 @@ __entry: teq r0, #0 @ check for illegal entry... str r3, [r0], #4 add r3, r3, #1 << 20 str r3, [r0], #4 -1: #endif #endif #ifdef CONFIG_ARCH_RPC -/* Map in screen at 0x02000000 & SCREEN2_BASE - * Similar reasons here - for debug, and when things go - * wrong to a certain extent. This is of limited use to - * non-Acorn RiscPC architectures though. - */ + /* + * Map in screen at 0x02000000 & SCREEN2_BASE + * Similar reasons here - for debug. This is + * only for Acorn RiscPC architectures. + */ teq r5, #0 - addne r0, r4, #0x80 @ 02000000 + addne r0, r4, #0x80 @ 02000000 movne r3, #0x02000000 orrne r3, r3, r8 strne r3, [r0] - addne r0, r4, #0x3600 @ d8000000 + addne r0, r4, #0x3600 @ d8000000 strne r3, [r0] #endif - b aligned_call -/* - * The following should work on both v3 and v4 implementations - * Note: it seems that if the mov pc,lr is on the next cache - * line, it doesn't get fetched before the MMU starts - * translating, which prevents the kernel from booting. - * Ensure that this never happens. - */ - .align 5 -aligned_call: - mov lr, pc - mov pc, r10 @ Call processor flush (returns ctrl reg) - adr r5, __entry - sub r10, r10, r5 @ Make r10 PIC - ldr lr, .Lbranch - mcr p15, 0, r0, c1, c0 @ Enable MMU & caches. In 3 instructions - @ we lose this page! mov pc, lr -.Lerror: + + +/* + * Exception handling. Something went wrong and we can't + * proceed. We ought to tell the user, but since we + * don't have any guarantee that we're even running on + * the right architecture, we do virtually nothing. + * r0 = ascii error character + * + * Generally, only serious errors cause this. + */ +__error: #ifdef CONFIG_ARCH_RPC -/* Turn the screen red on a error - RiscPC only. +/* + * Turn the screen red on a error - RiscPC only. */ mov r0, #0x02000000 mov r3, #0x11 @@ -199,356 +235,171 @@ aligned_call: 1: mov r0, r0 b 1b -.Lbranch: .long .Lalready_done_mmap @ Real address of routine - @ DEC EBSA110 (pg dir phys, phys ram start, phys i/o) -.LCMachTypes: .long SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 @ Address of page tables (physical) - .long 0 @ Address of RAM - .long 0xe0000000 @ I/O address - .long 0x3800 - @ Acorn RiscPC - .long SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 + 0x10000000 - .long 0x10000000 - .long 0x03000000 - .long 0x3800 +/* + * Read processor ID register (CP#15, CR0), and determine + * processor type. + * + * Returns: + * r5, r6, r7 corrupted + * r8 = page table flags + * r9 = processor ID + * r10 = pointer to processor structure + */ +__lookup_processor_type: + adr r5, 2f + ldmia r5, {r7, r9, r10} + sub r5, r5, r9 + add r7, r7, r5 + add r10, r10, r5 + mrc p15, 0, r9, c0, c0 @ get processor id +1: ldmia r10, {r5, r6, r8} @ value, mask, mmuflags + eor r5, r5, r9 + tst r5, r6 + moveq pc, lr + add r10, r10, #36 @ sizeof(proc_info_list) + cmp r10, r7 + blt 1b + mov r10, #0 + mov pc, lr - @ EBSIT ??? - .long SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 - .long 0 - .long 0xe0000000 - .long 0x3800 +2: .long __proc_info_end + .long 2b + .long __proc_info_begin - @ NexusPCI - .long SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 + 0x40000000 - .long 0x40000000 - .long 0x10000000 - .long 0x3800 - - @ DEC EBSA285 - .long SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 @ Address of page tables (physical) - .long 0 @ Address of RAM - .long 0x24000000 @ I/O base address (0x42000000 -> 0xFE000000) - .long 0x3800 - - @ Corel VNC - .long SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 @ Address of page tables (physical) - .long 0 @ Address of RAM - .long 0x24000000 @ I/O base address (0x42000000 -> 0xfe000000) - .long 0x3800 - - @ CATS - .long SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 @ Address of page tables (physical) - .long 0 @ Address of RAM - .long 0x24000000 @ I/O base address (0x42000000 -> 0xfe000000) - .long 0x3800 - - @ tbox - .long SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 + 0x80000000 - .long 0x80000000 @ Address of RAM - .long 0x00400000 @ Uart - .long 0x3800 - - @ DEC EBSA285 as co-processor - .long 0x4000 @ Address of page tables (physical) - .long 0 @ Address of RAM - .long DC21285_ARMCSR_BASE @ Physical I/O base address - .long 0x7cf00000 >> 18 @ Virtual I/O base address - - @ SA1100 - .long SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 + 0xc0000000 - .long 0xc0000000 - .long 0x80000000 @ IO mapping will change when kernel gets on its feet - .long 0x3800 - -.LCProcTypes: @ ARM6 / 610 - .long 0x41560600 - .long 0xffffff00 - .long 0x00000c12 - b .Larmv3_flush_early @ arm v3 flush & ctrl early setup +/* + * Lookup machine architecture + * r1 = machine architecture number + * Returns: + * r4 = physical address of page tables + * r5 = physical start address of RAM + * r6 = physical address of IO + * r7 = byte offset into page tables for IO + */ +__lookup_architecture_type: + cmp r1, #(__arch_types_end - __arch_types_start) / 16 + bge 1f + adr r4, __arch_types_start + add r4, r4, r1, lsl #4 + ldmia r4, {r4, r5, r6, r7} + add r4, r5, #SWAPPER_PGDIR_OFFSET + mov r7, r7, lsr #18 mov pc, lr - - @ ARM7 - .long 0x41007000 - .long 0xfffff000 - .long 0x00000c12 - b .Larmv3_flush_late @ arm v3 flush & ctrl late setup +1: mov r7, #0 mov pc, lr - @ ARM710 - .long 0x41007000 - .long 0xfff8f000 @ arm710 processors are weird - .long 0x00000c12 - b .Larmv3_flush_late @ arm v3 flush & ctrl late setup - mov pc, lr +/* + * Machine parameters. Each machine requires 4 words, which are: + * + * word0: unused + * word1: physical start address of RAM + * word2: physical start address of IO + * word3: virtual start address of IO + * + * The IO mappings entered here are used to set up mappings + * required for debugging information to be shown to the user. + * paging_init() does the real page table initialisation. + */ + @ 0x00 - DEC EBSA110 +__arch_types_start: + .long 0 + .long 0 + .long 0xe0000000 + .long 0xe0000000 - @ StrongARM-110 and StrongARM-1100 - .long 0x4401a100 @ 4401a100 and 4401a110 - .long 0xffffffe0 - .long 0x00000c02 - b .Larmv4_flush_early - b .Lsa_fastclock + @ 0x01 - Acorn RiscPC + .long 0 + .long 0x10000000 + .long 0x03000000 + .long 0xe0000000 + @ 0x02 - Unused + .long 0 .long 0 - .align + .long 0xe0000000 + .long 0xe0000000 -.Larmv3_flush_early: - mov r0, #0 - mcr p15, 0, r0, c7, c0 @ flush caches on v3 - mcr p15, 0, r0, c5, c0 @ flush TLBs on v3 - mcr p15, 0, r4, c2, c0 @ load page table pointer - mov r0, #0x1f @ Domains 0, 1 = client - mcr p15, 0, r0, c3, c0 @ load domain access register -#ifdef CONFIG_ALIGNMENT_TRAP - mov r0, #0x3f @ ....S..DPWCAM -#else - mov r0, #0x3d @ ....S..DPWC.M -#endif - orr r0, r0, #0x100 - mov pc, lr + @ 0x03 - NexusPCI + .long 0 + .long 0x40000000 + .long 0x10000000 + .long 0xe0000000 -.Larmv3_flush_late: - mov r0, #0 - mcr p15, 0, r0, c7, c0 @ flush caches on v3 - mcr p15, 0, r0, c5, c0 @ flush TLBs on v3 - mcr p15, 0, r4, c2, c0 @ load page table pointer - mov r0, #0x1f @ Domains 0, 1 = client - mcr p15, 0, r0, c3, c0 @ load domain access register -#ifdef CONFIG_ALIGNMENT_TRAP - mov r0, #0x7f @ ....S.LDPWCAM -#else - mov r0, #0x7d @ ....S.LDPWC.M -#endif - orr r0, r0, #0x100 - mov pc, lr + @ 0x04 - DEC EBSA285 + .long 0 + .long 0 + .long 0x24000000 @ I/O base address (0x42000000 -> 0xfe000000) + .long 0xe0000000 -.Larmv4_flush_early: - mov r0, #0 - mcr p15, 0, r0, c7, c7 @ flush I,D caches on v4 - mcr p15, 0, r0, c7, c10, 4 @ drain write buffer on v4 - mcr p15, 0, r0, c8, c7 @ flush I,D TLBs on v4 - mcr p15, 0, r4, c2, c0 @ load page table pointer - mov r0, #0x1f @ Domains 0, 1 = client - mcr p15, 0, r0, c3, c0 @ load domain access register - mrc p15, 0, r0, c1, c0 @ get control register v4 - bic r0, r0, #0x0e00 - bic r0, r0, #0x0002 -#ifdef CONFIG_ALIGNMENT_TRAP - orr r0, r0, #0x003f @ I...S..DPWCAM -#else - orr r0, r0, #0x003d @ I...S..DPWC.M -#endif - orr r0, r0, #0x1100 @ v4 supports separate I cache - mov pc, lr + @ 0x05 - Rebel.com NetWinder + .long 0 + .long 0 + .long 0x24000000 @ I/O base address (0x42000000 -> 0xfe000000) + .long 0xe0000000 -.Lsa_fastclock: mcr p15, 0, r4, c15, c1, 2 @ Enable clock switching - mov pc, lr + @ 0x06 - CATS + .long 0 + .long 0 + .long 0x24000000 @ I/O base address (0x42000000 -> 0xfe000000) + .long 0xe0000000 -.LC0: .long SYMBOL_NAME(__entry) - .long SYMBOL_NAME(__machine_arch_type) - .long SYMBOL_NAME(__bss_start) - .long SYMBOL_NAME(processor_id) - .long SYMBOL_NAME(_end) - .long SYMBOL_NAME(cr_alignment) - .long SYMBOL_NAME(init_task_union)+8192 - .align - -.Lalready_done_mmap: - adr r4, .LC0 - ldmia r4, {r3, r4, r5, r6, r7, r8, sp} @ Setup stack - add r10, r10, r3 @ Add base back in - mov fp, #0 -1: cmp r5, r7 @ Clear BSS - strcc fp, [r5],#4 - bcc 1b + @ 0x07 - tbox + .long 0 + .long 0x80000000 + .long 0x00400000 @ Uart + .long 0xe0000000 - bic r2, r0, #2 @ Clear 'A' bit - stmia r8, {r0, r2} @ Save control register values + @ 0x08 - DEC EBSA285 as co-processor + .long 0 + .long 0 + .long DC21285_ARMCSR_BASE @ Physical I/O base address + .long 0x7cf00000 @ Virtual I/O base address - str r1, [r4] @ Save machine type - str r9, [r6] @ Save processor ID - mov lr, pc - add pc, r10, #4 @ Call post-processor init - mov fp, #0 - b SYMBOL_NAME(start_kernel) + @ 0x09 - CL-PS7110 + .long 0 + .long 0 + .long 0 + .long 0 - .text + @ 0x0a - Acorn Archimedes + .long 0 + .long 0 + .long 0 + .long 0 -#ifdef CONFIG_DEBUG_LL -/* - * Some debugging routines (useful if you've got MM problems and - * printk isn't working). For DEBUGGING ONLY!!! Do not leave - * references to these in a production kernel! - */ -#if defined(CONFIG_ARCH_RPC) - .macro addruart,rx - mov \rx, #0xe0000000 - orr \rx, \rx, #0x00010000 - orr \rx, \rx, #0x00000fe0 - .endm - - .macro senduart,rd,rx - strb \rd, [\rx] - .endm - - .macro busyuart,rd,rx -1002: ldrb \rd, [\rx, #0x14] - and \rd, \rd, #0x60 - teq \rd, #0x60 - bne 1002b - .endm - - .macro waituart,rd,rx -1001: ldrb \rd, [\rx, #0x18] - tst \rd, #0x10 - beq 1001b - .endm - -#elif defined(CONFIG_ARCH_EBSA110) - .macro addruart,rx - mov \rx, #0xf0000000 - orr \rx, \rx, #0x00000be0 - .endm - - .macro senduart,rd,rx - strb \rd, [\rx] - .endm - - .macro busyuart,rd,rx -1002: ldrb \rd, [\rx, #0x14] - and \rd, \rd, #0x60 - teq \rd, #0x60 - bne 1002b - .endm - - .macro waituart,rd,rx -1001: ldrb \rd, [\rx, #0x18] - tst \rd, #0x10 - beq 1001b - .endm - -#elif defined(CONFIG_HOST_FOOTBRIDGE) || defined(CONFIG_ADDIN_FOOTBRIDGE) -#ifndef CONFIG_DEBUG_DC21285_PORT - /* For NetWinder debugging */ - .macro addruart,rx - mov \rx, #0xff000000 - orr \rx, \rx, #0x000003f8 - .endm - - .macro senduart,rd,rx - strb \rd, [\rx] - .endm - - .macro busyuart,rd,rx -1002: ldrb \rd, [\rx, #0x5] - and \rd, \rd, #0x60 - teq \rd, #0x60 - bne 1002b - .endm - - .macro waituart,rd,rx -1001: ldrb \rd, [\rx, #0x6] - tst \rd, #0x10 - beq 1001b - .endm -#else - /* For EBSA285 debugging */ - .equ dc21285_high, ARMCSR_BASE & 0xff000000 - .equ dc21285_low, ARMCSR_BASE & 0x00ffffff - - .macro addruart,rx - mov \rx, #dc21285_high - .if dc21285_low - orr \rx, \rx, #dc21285_low - .endif - .endm - - .macro senduart,rd,rx - str \rd, [\rx, #0x160] @ UARTDR - .endm - - .macro busyuart,rd,rx -1001: ldr \rd, [\rx, #0x178] @ UARTFLG - tst \rd, #1 << 3 - bne 1001b - .endm - - .macro waituart,rd,rx - .endm -#endif -#elif defined(CONFIG_ARCH_NEXUSPCI) - .macro addruart,rx - ldr \rx, =0xfff00000 - .endm - - .macro senduart,rd,rx - str \rd, [\rx, #0xc] - .endm - - .macro busyuart,rd,rx -1001: ldr \rd, [\rx, #0x4] - tst \rd, #1 << 0 - bne 1001b - .endm - - .macro waituart,rd,rx - .endm -#else -#error Unknown architecture -#endif + @ 0x0b - Acorn A5000 + .long 0 + .long 0 + .long 0 + .long 0 -/* - * Useful debugging routines - */ -ENTRY(printhex8) - mov r1, #8 - b printhex - -ENTRY(printhex4) - mov r1, #4 - b printhex - -ENTRY(printhex2) - mov r1, #2 -printhex: ldr r2, =hexbuf - add r3, r2, r1 - mov r1, #0 - strb r1, [r3] -1: and r1, r0, #15 - mov r0, r0, lsr #4 - cmp r1, #10 - addlt r1, r1, #'0' - addge r1, r1, #'a' - 10 - strb r1, [r3, #-1]! - teq r3, r2 - bne 1b - mov r0, r2 - b printascii - - .ltorg - -ENTRY(printascii) - addruart r3 - b 2f -1: waituart r2, r3 - senduart r1, r3 - busyuart r2, r3 - teq r1, #'\n' - moveq r1, #'\r' - beq 1b -2: teq r0, #0 - ldrneb r1, [r0], #1 - teqne r1, #0 - bne 1b - mov pc, lr + @ 0x0c - Etoile + .long 0 + .long 0 + .long 0 + .long 0 -ENTRY(printch) - addruart r3 - mov r1, r0 - mov r0, #0 - b 1b + @ 0x0d - LaCie_NAS + .long 0 + .long 0 + .long 0 + .long 0 - .bss -hexbuf: .space 16 + @ 0x0e - CL-PS7500 + .long 0 + .long 0 + .long 0 + .long 0 -#endif + @ 0x0f - Shark + .long 0 + .long 0 + .long 0 + .long 0 +__arch_types_end: + @ unknown - SA1100 + .long 0 + .long 0xc0000000 + .long 0x80000000 + .long 0xe0000000 diff --git a/arch/arm/kernel/hw-footbridge.c b/arch/arm/kernel/hw-footbridge.c index 54a64b811..117011e1d 100644 --- a/arch/arm/kernel/hw-footbridge.c +++ b/arch/arm/kernel/hw-footbridge.c @@ -25,269 +25,8 @@ #include <asm/system.h> #define IRDA_IO_BASE 0x180 -#define ETHER10_IO_BASE 0x301 #define GP1_IO_BASE 0x338 #define GP2_IO_BASE 0x33a -#define DEC21143_IO_BASE 0x401 -#define DEC21143_MEM_BASE 0x00800000 -#define CYBER2000_MEM_BASE 0x01000000 - -int have_isa_bridge; - -extern int setup_arm_irq(int, struct irqaction *); -extern void pci_set_cmd(struct pci_dev *dev, unsigned short clear, unsigned short set); -extern void pci_set_base_addr(struct pci_dev *dev, int idx, unsigned int addr); -extern void pci_set_irq_line(struct pci_dev *dev, unsigned int irq); -extern void (*kd_mksound)(unsigned int hz, unsigned int ticks); - -#ifdef CONFIG_PCI - -static int irqmap_ebsa[] __initdata = { IRQ_IN1, IRQ_IN0, IRQ_PCI, IRQ_IN3 }; - -static int __init ebsa_irqval(struct pci_dev *dev) -{ - unsigned char pin; - - pcibios_read_config_byte(dev->bus->number, - dev->devfn, - PCI_INTERRUPT_PIN, - &pin); - - return irqmap_ebsa[(PCI_SLOT(dev->devfn) + pin) & 3]; -} - -#ifdef CONFIG_CATS -static int irqmap_cats[] __initdata = { IRQ_PCI, IRQ_IN0, IRQ_IN1, IRQ_IN3 }; - -static int __init cats_irqval(struct pci_dev *dev) -{ - if (dev->irq >= 128) - return 16 + (dev->irq & 0x1f); - - switch (dev->irq) { - case 1: - case 2: - case 3: - case 4: - return irqmap_cats[dev->irq - 1]; - case 0: - return 0; - } - - printk("PCI: device %02x:%02x has unknown irq line %x\n", - dev->bus->number, dev->devfn, dev->irq); - return 0; -} -#endif - -void __init pcibios_fixup_ebsa285(struct pci_dev *dev) -{ - /* Latency timer of 32 */ - pci_write_config_byte(dev, PCI_LATENCY_TIMER, 32); - - /* 32-byte cache line size */ - pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 8); - - /* Set SysErr enable, Parity enable */ - pci_set_cmd(dev, 0, PCI_COMMAND_FAST_BACK | PCI_COMMAND_SERR | PCI_COMMAND_PARITY); - - /* If this device is an ISA bridge, set the - * have_isa_bridge flag. We will then go looking - * for things like keyboard, etc - */ - if ((dev->class >> 8) == PCI_CLASS_BRIDGE_ISA || - (dev->class >> 8) == PCI_CLASS_BRIDGE_EISA) - have_isa_bridge = !0; - - /* sort out the irq mapping for this device */ - switch (machine_arch_type) { - case MACH_TYPE_EBSA285: - dev->irq = ebsa_irqval(dev); - /* Turn on bus mastering - boot loader doesn't - * - perhaps it should! - dag - */ - pci_set_cmd(dev, 0, PCI_COMMAND_MASTER); - break; - -#ifdef CONFIG_CATS - case MACH_TYPE_CATS: - dev->irq = cats_irqval(dev); - /* Turn on bus mastering - boot loader doesn't - * - perhaps it should! - dag - */ - pci_set_cmd(dev, 0, PCI_COMMAND_MASTER); - break; -#endif -#ifdef CONFIG_ARCH_NETWINDER - case MACH_TYPE_NETWINDER: - /* disable ROM */ - pci_write_config_dword(dev, PCI_ROM_ADDRESS, 0); - -#define DEV(v,d) ((v)<<16|(d)) - switch (DEV(dev->vendor, dev->device)) { - /* Ether 100 */ - case DEV(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21142): - pci_set_base_addr(dev, 0, DEC21143_IO_BASE); - pci_set_base_addr(dev, 1, DEC21143_MEM_BASE); - pci_set_cmd(dev, 0, PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO); - /* Put the chip to sleep in case the driver isn't loaded */ - pci_write_config_dword(dev, 0x40, 0x80000000); - dev->irq = IRQ_NETWINDER_ETHER100; - break; - - /* Ether 10 */ - case DEV(PCI_VENDOR_ID_WINBOND2,0x5a5a): - pci_set_base_addr(dev, 0, ETHER10_IO_BASE); - pci_set_cmd(dev, PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY, PCI_COMMAND_IO); - dev->irq = IRQ_NETWINDER_ETHER10; - break; - - /* ISA bridge */ - case DEV(PCI_VENDOR_ID_WINBOND,PCI_DEVICE_ID_WINBOND_83C553): - pci_set_base_addr(dev, 0, 0); - pci_set_cmd(dev, PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY, PCI_COMMAND_IO); - /* - * Enable all memory requests from ISA to be channeled to PCI - */ - pci_write_config_byte(dev, 0x48, 255); - /* - * Disable ping-pong (as per errata) - */ - pci_write_config_byte(dev, 0x42, 0); - /* - * Enable PCI packet retry - */ - pci_write_config_byte(dev, 0x40, 0x22); - /* - * Do not use PCI CPU park enable, park on - * last master, disable GAT bit - */ - pci_write_config_byte(dev, 0x83, 0x02); - /* - * Default rotating priorities - */ - pci_write_config_byte(dev, 0x80, 0xe0); - /* - * Rotate bank 4 - */ - pci_write_config_byte(dev, 0x81, 0x01); - break; - - /* IDE */ - case DEV(PCI_VENDOR_ID_WINBOND,PCI_DEVICE_ID_WINBOND_82C105): - pci_set_base_addr(dev, 0, 0x1f1); - pci_set_base_addr(dev, 1, 0x3f5); - pci_set_base_addr(dev, 2, 0x171); - pci_set_base_addr(dev, 3, 0x375); - pci_set_base_addr(dev, 4, 0xe801); - pci_set_cmd(dev, PCI_COMMAND_MEMORY, PCI_COMMAND_MASTER | PCI_COMMAND_IO); - dev->irq = IRQ_ISA_HARDDISK1; - break; - - /* VGA */ - case DEV(PCI_VENDOR_ID_INTERG,0x2000): - pci_set_base_addr(dev, 0, CYBER2000_MEM_BASE); - pci_set_cmd(dev, PCI_COMMAND_MASTER, PCI_COMMAND_IO | PCI_COMMAND_MEMORY); - dev->irq = IRQ_NETWINDER_VGA; - break; - } -#endif - } -} - -static inline void -report_pci_dev_error(void) -{ - struct pci_dev *dev; - - for (dev = pci_devices; dev; dev = dev->next) { - unsigned short status; - - pci_read_config_word(dev, PCI_STATUS, &status); - if (status & 0xf900) { - printk(KERN_DEBUG "PCI: [%04X:%04X] status = %X\n", - dev->vendor, dev->device, status); - - pci_write_config_word(dev, PCI_STATUS, status & 0xf900); - } - } -} -#else -#define report_pci_dev_error() -#endif - -/* - * Warn on PCI errors. Please report any occurances! - */ -static void -irq_pci_err(int irq, void *dev_id, struct pt_regs *regs) -{ - static unsigned long next_warn; - unsigned long cmd = *CSR_PCICMD & 0x0000ffff; - unsigned long ctrl = (*CSR_SA110_CNTL) & 0xffffde07; - unsigned long irqstatus = *CSR_IRQ_RAWSTATUS; - int warn = time_after_eq(jiffies, next_warn); - - ctrl |= SA110_CNTL_DISCARDTIMER; - - if (warn) { - next_warn = jiffies + 3 * HZ / 100; - printk(KERN_DEBUG "PCI: "); - } - - if (irqstatus & (1 << 31)) { - if (warn) - printk("parity error "); - cmd |= 1 << 31; - } - - if (irqstatus & (1 << 30)) { - if (warn) - printk("target abort "); - cmd |= 1 << 28; - } - - if (irqstatus & (1 << 29)) { - if (warn) - printk("master abort "); - cmd |= 1 << 29; - } - - if (irqstatus & (1 << 28)) { - if (warn) - printk("data parity error "); - cmd |= 1 << 24; - } - - if (irqstatus & (1 << 27)) { - if (warn) - printk("discard timer expired "); - ctrl &= ~SA110_CNTL_DISCARDTIMER; - } - - if (irqstatus & (1 << 23)) { - if (warn) - printk("system error "); - ctrl |= SA110_CNTL_RXSERR; - } - - if (warn) - printk("pc=[<%08lX>]\n", instruction_pointer(regs)); - - report_pci_dev_error(); - - *CSR_PCICMD = cmd; - *CSR_SA110_CNTL = ctrl; -} - -static struct irqaction irq_pci_error = { - irq_pci_err, SA_INTERRUPT, 0, "PCI error", NULL, NULL -}; - -void __init pcibios_init_ebsa285(void) -{ - setup_arm_irq(IRQ_PCI_ERR, &irq_pci_error); -} /* * Netwinder stuff @@ -627,7 +366,7 @@ void __netwinder_text cpld_modify(int mask, int set) current_cpld = (current_cpld & ~mask) | set; - gpio_modify_io(GPIO_DATA, 0); + gpio_modify_io(GPIO_DATA | GPIO_IOCLK | GPIO_IOLOAD, 0); gpio_modify_op(GPIO_IOLOAD, 0); for (msk = 8; msk; msk >>= 1) { @@ -647,7 +386,7 @@ static void __init cpld_init(void) unsigned long flags; spin_lock_irqsave(&gpio_lock, flags); - cpld_modify(-1, CPLD_UNMUTE | 4); + cpld_modify(-1, CPLD_UNMUTE | CPLD_7111_DISABLE); spin_unlock_irqrestore(&gpio_lock, flags); } @@ -713,12 +452,15 @@ static inline void rwa010_global_init(void) dprintk("Card no = %d\n", inb(0x203)); + /* disable the modem section of the chip */ WRITE_RWA(7, 3); WRITE_RWA(0x30, 0); + /* disable the cdrom section of the chip */ WRITE_RWA(7, 4); WRITE_RWA(0x30, 0); + /* disable the MPU-401 section of the chip */ WRITE_RWA(7, 2); WRITE_RWA(0x30, 0); } @@ -925,6 +667,7 @@ void __init hw_init(void) extern void register_isa_ports(unsigned int, unsigned int, unsigned int); register_isa_ports(DC21285_PCI_MEM, DC21285_PCI_IO, 0); + #ifdef CONFIG_ARCH_NETWINDER /* * this ought to have a better home... diff --git a/arch/arm/kernel/init_task.c b/arch/arm/kernel/init_task.c index e3853f3d5..1a379ee10 100644 --- a/arch/arm/kernel/init_task.c +++ b/arch/arm/kernel/init_task.c @@ -1,4 +1,5 @@ #include <linux/mm.h> +#include <linux/fs.h> #include <linux/sched.h> #include <asm/uaccess.h> diff --git a/arch/arm/kernel/ioport.c b/arch/arm/kernel/ioport.c index bae897747..d40ea1cf1 100644 --- a/arch/arm/kernel/ioport.c +++ b/arch/arm/kernel/ioport.c @@ -9,10 +9,12 @@ #include <linux/errno.h> #include <linux/types.h> #include <linux/ioport.h> +#include <linux/mm.h> #include <asm/pgtable.h> #include <asm/uaccess.h> +#ifdef CONFIG_CPU_32 asmlinkage int sys_iopl(unsigned long turn_on) { if (turn_on && !capable(CAP_SYS_RAWIO)) @@ -25,3 +27,9 @@ asmlinkage int sys_iopl(unsigned long turn_on) return 0; } +#else +asmlinkage int sys_iopl(unsigned long turn_on) +{ + return -ENOSYS; +} +#endif diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c index 41e2050e6..f6c310905 100644 --- a/arch/arm/kernel/irq.c +++ b/arch/arm/kernel/irq.c @@ -494,8 +494,5 @@ void __init init_IRQ(void) } irq_init_irq(); -#ifdef CONFIG_ARCH_ACORN - init_FIQ(); -#endif init_dma(); } diff --git a/arch/arm/kernel/leds-footbridge.c b/arch/arm/kernel/leds-footbridge.c index 6b4cb001b..90f2fe109 100644 --- a/arch/arm/kernel/leds-footbridge.c +++ b/arch/arm/kernel/leds-footbridge.c @@ -138,7 +138,7 @@ static void __netwinder_text netwinder_leds_event(led_event_t evt) switch (evt) { case led_start: led_state |= LED_STATE_ENABLED; - hw_led_state = 0; + hw_led_state = GPIO_GREEN_LED; break; case led_stop: @@ -223,26 +223,20 @@ static void dummy_leds_event(led_event_t evt) { } -void __init +static void __init init_leds_event(led_event_t evt) { - switch (machine_arch_type) { + leds_event = dummy_leds_event; + #ifdef CONFIG_FOOTBRIDGE - case MACH_TYPE_EBSA285: - case MACH_TYPE_CO285: + if (machine_is_ebsa285() || machine_is_co285()) leds_event = ebsa285_leds_event; - break; #endif #ifdef CONFIG_ARCH_NETWINDER - case MACH_TYPE_NETWINDER: + if (machine_is_netwinder()) leds_event = netwinder_leds_event; - break; #endif - default: - leds_event = dummy_leds_event; - } - leds_event(evt); } diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index 776dc9b88..ab92aae52 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -1,7 +1,7 @@ /* * linux/arch/arm/kernel/process.c * - * Copyright (C) 1996 Russell King - Converted to ARM. + * Copyright (C) 1996-1999 Russell King - Converted to ARM. * Origional Copyright (C) 1995 Linus Torvalds */ @@ -32,6 +32,7 @@ #include <asm/uaccess.h> #include <asm/system.h> +#include <asm/arch/system.h> #include <asm/io.h> extern char *processor_modes[]; @@ -60,26 +61,38 @@ void cpu_idle(void) current->priority = 0; current->counter = -100; while (1) { - if (!current->need_resched && !hlt_counter) - proc_idle(); - schedule(); + if (!hlt_counter) + arch_do_idle(); + if (current->need_resched) { + schedule(); #ifndef CONFIG_NO_PGT_CACHE - check_pgt_cache(); + check_pgt_cache(); #endif + } } } static char reboot_mode = 'h'; -void __init reboot_setup(char *str, int *ints) +int __init reboot_setup(char *str) { reboot_mode = str[0]; + return 0; } +__setup("reboot=", reboot_setup); + void machine_restart(char * __unused) { + /* + * Turn off caches, interrupts, etc + */ + cpu_proc_fin(); + arch_reset(reboot_mode); - panic("Reboot failed\n"); + + printk("Reboot failed -- System halted\n"); + while (1); } @@ -135,6 +148,35 @@ void show_regs(struct pt_regs * regs) #endif } +void show_fpregs(struct user_fp *regs) +{ + int i; + + for (i = 0; i < 8; i++) { + unsigned long *p; + char type; + + p = (unsigned long *)(regs->fpregs + i); + + switch (regs->ftype[i]) { + case 1: type = 'f'; break; + case 2: type = 'd'; break; + case 3: type = 'e'; break; + default: type = '?'; break; + } + if (regs->init_flag) + type = '?'; + + printk(" f%d(%c): %08lx %08lx %08lx%c", + i, type, p[0], p[1], p[2], i & 1 ? '\n' : ' '); + } + + + printk("FPSR: %08lx FPCR: %08lx\n", + (unsigned long)regs->fpsr, + (unsigned long)regs->fpcr); +} + /* * Task structure and kernel stack allocation. * @@ -206,6 +248,7 @@ void exit_thread(void) void flush_thread(void) { memset(¤t->thread.debug, 0, sizeof(current->thread.debug)); + memset(¤t->thread.fpstate, 0, sizeof(current->thread.fpstate)); current->used_math = 0; current->flags &= ~PF_USEDFPU; } @@ -237,12 +280,10 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long esp, */ int dump_fpu (struct pt_regs *regs, struct user_fp *fp) { - int fpvalid = 0; - if (current->used_math) - memcpy (fp, ¤t->thread.fpstate.soft, sizeof (fp)); + memcpy(fp, ¤t->thread.fpstate.soft, sizeof (fp)); - return fpvalid; + return current->used_math; } /* @@ -281,6 +322,7 @@ void dump_thread(struct pt_regs * regs, struct user * dump) */ pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) { + extern long sys_exit(int) __attribute__((noreturn)); pid_t __ret; __asm__ __volatile__( diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c index 6104b4dbe..0692d1a6c 100644 --- a/arch/arm/kernel/ptrace.c +++ b/arch/arm/kernel/ptrace.c @@ -447,8 +447,8 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) unsigned long tmp; ret = read_long(child, addr, &tmp); - if (ret) - put_user(tmp, (unsigned long *) data); + if (!ret) + ret = put_user(tmp, (unsigned long *) data); goto out; } diff --git a/arch/arm/kernel/semaphore.c b/arch/arm/kernel/semaphore.c index 1bb21be3f..71bf85e09 100644 --- a/arch/arm/kernel/semaphore.c +++ b/arch/arm/kernel/semaphore.c @@ -8,6 +8,7 @@ * Modified for ARM by Russell King */ #include <linux/sched.h> +#include <linux/errno.h> #include <asm/semaphore.h> diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index 8258bd767..b09b3798b 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -10,36 +10,27 @@ */ #include <linux/config.h> -#include <linux/errno.h> -#include <linux/sched.h> #include <linux/kernel.h> #include <linux/mm.h> #include <linux/stddef.h> -#include <linux/unistd.h> -#include <linux/ptrace.h> -#include <linux/malloc.h> -#include <linux/user.h> -#include <linux/a.out.h> #include <linux/tty.h> #include <linux/ioport.h> #include <linux/delay.h> -#include <linux/major.h> #include <linux/utsname.h> #include <linux/blk.h> -#include <linux/init.h> #include <linux/console.h> +#include <linux/init.h> #include <asm/elf.h> #include <asm/hardware.h> #include <asm/io.h> -#include <asm/pgtable.h> #include <asm/procinfo.h> -#include <asm/segment.h> #include <asm/setup.h> #include <asm/system.h> +#ifndef MEM_SIZE #define MEM_SIZE (16*1024*1024) -#define COMMAND_LINE_SIZE 256 +#endif #ifndef CONFIG_CMDLINE #define CONFIG_CMDLINE "" @@ -50,7 +41,6 @@ #endif extern void reboot_setup(char *str, int *ints); -extern void fpe_init(void); extern void disable_hlt(void); struct drive_info_struct { char dummy[32]; } drive_info; @@ -64,10 +54,9 @@ struct screen_info screen_info = { }; extern int root_mountflags; -extern int _etext, _edata, _end; +extern int _text, _etext, _edata, _end; unsigned char aux_device_present; - char elf_platform[ELF_PLATFORM_SIZE]; unsigned int elf_hwcap; @@ -76,13 +65,16 @@ unsigned int elf_hwcap; */ unsigned int processor_id; unsigned int __machine_arch_type; +unsigned int vram_size; +unsigned int system_rev; +unsigned int system_serial_low; +unsigned int system_serial_high; #ifdef MULTI_CPU struct processor processor; #endif #ifdef CONFIG_ARCH_ACORN -int memc_ctrl_reg; -int number_mfm_drives; -unsigned int vram_size; +unsigned int memc_ctrl_reg; +unsigned int number_mfm_drives; #endif static struct proc_info_item proc_info; @@ -95,39 +87,41 @@ static union { char c[4]; unsigned long l; } endian_test __initdata = { { 'l', ' * symbol to be empty if not configured. */ -/* - * initial ram disk - */ -#ifdef CONFIG_BLK_DEV_INITRD -static void __init -check_initrd(unsigned long mem_start, unsigned long mem_end) +static void __init setup_processor(void) { - if (initrd_end > mem_end) { - printk ("initrd extends beyond end of memory " - "(0x%08lx > 0x%08lx) - disabling initrd\n", - initrd_end, mem_end); - initrd_start = 0; - } -} + extern struct proc_info_list __proc_info_begin, __proc_info_end; + struct proc_info_list *list; -#else -#define check_initrd(ms,me) -#endif + /* + * locate processor in the list of supported processor + * types. The linker builds this table for us from the + * entries in arch/arm/mm/proc-*.S + */ + for (list = &__proc_info_begin; list < &__proc_info_end ; list++) + if ((processor_id & list->cpu_mask) == list->cpu_val) + break; -void __init -setup_processor(void) -{ - armidindex = 0; + /* + * If processor type is unrecognised, then we + * can do nothing... + */ + if (list >= &__proc_info_end) { + printk("CPU configuration botched (ID %08x), unable " + "to continue.\n", processor_id); + while (1); + } - while ((armidlist[armidindex].id ^ processor_id) & - armidlist[armidindex].mask) - armidindex += 1; + proc_info = *list->info; - if (armidlist[armidindex].id == 0) - while (1); +#ifdef MULTI_CPU + processor = *list->proc; +#endif - processor = *armidlist[armidindex].proc; - processor._proc_init(); + sprintf(system_utsname.machine, "%s%c", list->arch_name, ENDIANNESS); + sprintf(elf_platform, "%s%c", list->elf_name, ENDIANNESS); + elf_hwcap = list->elf_hwcap; + + cpu_proc_init(); } static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE; @@ -135,12 +129,13 @@ static char command_line[COMMAND_LINE_SIZE] = { 0, }; char saved_command_line[COMMAND_LINE_SIZE]; static void __init -setup_mem(char *cmd_line, unsigned long *mem_start, unsigned long *mem_sz) +setup_mem(char *cmd_line, unsigned long *mem_sz) { char c = ' ', *to = command_line; int len = 0; - *mem_start = (unsigned long)&_end; + if (!*mem_sz) + *mem_sz = MEM_SIZE; for (;;) { if (c == ' ') { @@ -181,24 +176,27 @@ setup_mem(char *cmd_line, unsigned long *mem_start, unsigned long *mem_sz) } static void __init -setup_ram(int doload, int prompt, int image_start) +setup_ramdisk(int doload, int prompt, int image_start, unsigned int rd_sz) { #ifdef CONFIG_BLK_DEV_RAM extern int rd_doload; extern int rd_prompt; extern int rd_image_start; + extern int rd_size; rd_image_start = image_start; rd_prompt = prompt; rd_doload = doload; + + if (rd_sz) + rd_size = rd_sz; #endif } /* * initial ram disk */ -static void __init -setup_initrd(unsigned int start, unsigned int size) +static void __init setup_initrd(unsigned int start, unsigned int size) { #ifdef CONFIG_BLK_DEV_INITRD if (start) { @@ -211,30 +209,62 @@ setup_initrd(unsigned int start, unsigned int size) #endif } -#ifdef CONFIG_ARCH_ACORN -int memc_ctrl_reg; -int number_mfm_drives; -unsigned int vram_size; +static void __init check_initrd(unsigned long mem_end) +{ +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_end > mem_end) { + printk ("initrd extends beyond end of memory " + "(0x%08lx > 0x%08lx) - disabling initrd\n", + initrd_end, mem_end); + initrd_start = 0; + } #endif +} -#ifndef PARAMS_BASE -#define PARAMS_BASE NULL -#endif +/* + * Standard memory resources + */ +static struct resource system_ram = { "System RAM", 0, 0, IORESOURCE_MEM | IORESOURCE_BUSY }; +static struct resource video_ram = { "Video RAM", 0, 0, IORESOURCE_MEM }; +static struct resource kernel_code = { "Kernel code", 0, 0, IORESOURCE_MEM }; +static struct resource kernel_data = { "Kernel data", 0, 0, IORESOURCE_MEM }; +static struct resource lpt1 = { "reserved", 0x3bc, 0x3be, IORESOURCE_IO | IORESOURCE_BUSY }; +static struct resource lpt2 = { "reserved", 0x378, 0x37f, IORESOURCE_IO | IORESOURCE_BUSY }; +static struct resource lpt3 = { "reserved", 0x278, 0x27f, IORESOURCE_IO | IORESOURCE_BUSY }; + +static void __init request_standard_resources(unsigned long end) +{ + kernel_code.start = __virt_to_bus((unsigned long) &_text); + kernel_code.end = __virt_to_bus((unsigned long) &_etext - 1); + kernel_data.start = __virt_to_bus((unsigned long) &_etext); + kernel_data.end = __virt_to_bus((unsigned long) &_edata - 1); + system_ram.start = __virt_to_bus(PAGE_OFFSET); + system_ram.end = __virt_to_bus(end - 1); + + request_resource(&iomem_resource, &system_ram); + request_resource(&system_ram, &kernel_code); + request_resource(&system_ram, &kernel_data); + if (video_ram.start != video_ram.end) + request_resource(&iomem_resource, &video_ram); -static union { char c[4]; unsigned long l; } endian_test __initdata = { { 'l', '?', '?', 'b' } }; -#define ENDIANNESS ((char)endian_test.l) + /* + * Some machines don't have the possibility of ever + * possessing LPT1 (lp0) and LPT3 (lp2) + */ + if (machine_is_ebsa110() || machine_is_riscpc() || + machine_is_netwinder()) + request_resource(&ioport_resource, &lpt1); + if (machine_is_riscpc()) + request_resource(&ioport_resource, &lpt2); + if (machine_is_ebsa110() || machine_is_netwinder()) + request_resource(&ioport_resource, &lpt3); +} -void __init -setup_arch(char **cmdline_p, unsigned long * memory_start_p, unsigned long * memory_end_p) +void __init setup_arch(char **cmdline_p, unsigned long * memory_start_p, unsigned long * memory_end_p) { struct param_struct *params = (struct param_struct *)PARAMS_BASE; - static unsigned char smptrap; unsigned long memory_end = 0; - char *from = NULL; - - if (smptrap == 1) - return; - smptrap = 1; + char *from = default_command_line; #if defined(CONFIG_ARCH_ARC) __machine_arch_type = MACH_TYPE_ARCHIMEDES; @@ -244,10 +274,11 @@ setup_arch(char **cmdline_p, unsigned long * memory_start_p, unsigned long * mem setup_processor(); - init_mm.start_code = TASK_SIZE; - init_mm.end_code = TASK_SIZE + (unsigned long) &_etext; - init_mm.end_data = TASK_SIZE + (unsigned long) &_edata; - init_mm.brk = TASK_SIZE + (unsigned long) &_end; + /* + * Defaults + */ + ROOT_DEV = MKDEV(0, 255); + setup_ramdisk(1, 1, 0, 0); /* * Add your machine dependencies here @@ -256,14 +287,35 @@ setup_arch(char **cmdline_p, unsigned long * memory_start_p, unsigned long * mem case MACH_TYPE_EBSA110: /* EBSA110 locks if we execute 'wait for interrupt' */ disable_hlt(); - if (params && params->u1.s.page_size != 4096) + if (params && params->u1.s.page_size != PAGE_SIZE) params = NULL; break; +#ifdef CONFIG_ARCH_ACORN +#ifdef CONFIG_ARCH_RPC case MACH_TYPE_RISCPC: /* RiscPC can't handle half-word loads and stores */ elf_hwcap &= ~HWCAP_HALF; + { + extern void init_dram_banks(struct param_struct *); + init_dram_banks(params); + } + + switch (params->u1.s.pages_in_vram) { + case 512: + vram_size += PAGE_SIZE * 256; + case 256: + vram_size += PAGE_SIZE * 256; + default: + break; + } +#endif + case MACH_TYPE_ARCHIMEDES: + case MACH_TYPE_A5K: + memc_ctrl_reg = params->u1.s.memc_control_reg; + number_mfm_drives = (params->u1.s.adfsdrives >> 3) & 3; break; +#endif case MACH_TYPE_EBSA285: if (params) { @@ -271,6 +323,8 @@ setup_arch(char **cmdline_p, unsigned long * memory_start_p, unsigned long * mem ORIG_Y = params->u1.s.video_y; ORIG_VIDEO_COLS = params->u1.s.video_num_cols; ORIG_VIDEO_LINES = params->u1.s.video_num_rows; + video_ram.start = 0x0a0000; + video_ram.end = 0x0bffff; } break; @@ -288,53 +342,67 @@ setup_arch(char **cmdline_p, unsigned long * memory_start_p, unsigned long * mem break; case MACH_TYPE_CATS: - /* CATS must use soft-reboot */ + /* CATS uses soft-reboot by default, since hard reboots + * fail on early boards. + */ reboot_setup("s", NULL); + params = NULL; + ORIG_VIDEO_LINES = 25; + ORIG_VIDEO_POINTS = 16; + ORIG_Y = 24; + video_ram.start = 0x0a0000; + video_ram.end = 0x0bffff; break; case MACH_TYPE_NETWINDER: /* * to be fixed in a future NeTTrom */ - if (params->u1.s.page_size == 4096) { + if (params->u1.s.page_size == PAGE_SIZE) { if (params->u1.s.nr_pages != 0x2000 && params->u1.s.nr_pages != 0x4000) { printk("Warning: bad NeTTrom parameters detected, using defaults\n"); - /* - * This stuff doesn't appear to be initialised - * properly by NeTTrom 2.0.6 and 2.0.7 - */ + /* + * This stuff doesn't appear to be initialised + * properly by NeTTrom 2.0.6 and 2.0.7 + */ params->u1.s.nr_pages = 0x2000; /* 32MB */ params->u1.s.ramdisk_size = 0; params->u1.s.flags = FLAG_READONLY; params->u1.s.initrd_start = 0; params->u1.s.initrd_size = 0; params->u1.s.rd_start = 0; - params->u1.s.video_x = 0; - params->u1.s.video_y = 0; - params->u1.s.video_num_cols = 80; - params->u1.s.video_num_rows = 30; } } else { printk("Warning: no NeTTrom parameter page detected, using " "compiled-in settings\n"); params = NULL; } + video_ram.start = 0x0a0000; + video_ram.end = 0x0bffff; break; default: break; } - if (params) { - memory_end = params->u1.s.page_size * - params->u1.s.nr_pages; + if (params && params->u1.s.page_size != PAGE_SIZE) { + printk("Warning: wrong page size configuration, " + "trying to continue\n"); + params = NULL; + } - ROOT_DEV = to_kdev_t(params->u1.s.rootdev); + if (params) { + memory_end = PAGE_SIZE * params->u1.s.nr_pages; + ROOT_DEV = to_kdev_t(params->u1.s.rootdev); + system_rev = params->u1.s.system_rev; + system_serial_low = params->u1.s.system_serial_low; + system_serial_high = params->u1.s.system_serial_high; - setup_ram((params->u1.s.flags & FLAG_RDLOAD) == 0, - (params->u1.s.flags & FLAG_RDPROMPT) == 0, - params->u1.s.rd_start); + setup_ramdisk((params->u1.s.flags & FLAG_RDLOAD) == 0, + (params->u1.s.flags & FLAG_RDPROMPT) == 0, + params->u1.s.rd_start, + params->u1.s.ramdisk_size); setup_initrd(params->u1.s.initrd_start, params->u1.s.initrd_size); @@ -342,57 +410,27 @@ setup_arch(char **cmdline_p, unsigned long * memory_start_p, unsigned long * mem if (!(params->u1.s.flags & FLAG_READONLY)) root_mountflags &= ~MS_RDONLY; -#ifdef CONFIG_ARCH_ACORN -#ifdef CONFIG_ARCH_RPC - { - extern void init_dram_banks(struct param_struct *); - init_dram_banks(params); - } -#endif - - memc_ctrl_reg = params->u1.s.memc_control_reg; - number_mfm_drives = (params->u1.s.adfsdrives >> 3) & 3; - vram_size = 0; - - switch (params->u1.s.pages_in_vram) { - case 512: - vram_size += PAGE_SIZE * 256; - case 256: - vram_size += PAGE_SIZE * 256; - default: - break; - } - - memory_end -= vram_size; -#endif - from = params->commandline; - } else { - ROOT_DEV = to_kdev_t(0x00ff); - - setup_ram(1, 1, 0); - setup_initrd(0, 0); } - if (!memory_end) - memory_end = MEM_SIZE; - - if (!from) - from = default_command_line; - -#ifdef CONFIG_NWFPE - fpe_init(); -#endif - /* Save unparsed command line copy for /proc/cmdline */ memcpy(saved_command_line, from, COMMAND_LINE_SIZE); saved_command_line[COMMAND_LINE_SIZE-1] = '\0'; - setup_mem(from, memory_start_p, &memory_end); + setup_mem(from, &memory_end); memory_end += PAGE_OFFSET; - check_initrd(*memory_start_p, memory_end); + *cmdline_p = command_line; + init_mm.start_code = (unsigned long) &_text; + init_mm.end_code = (unsigned long) &_etext; + init_mm.end_data = (unsigned long) &_edata; + init_mm.brk = (unsigned long) &_end; + *memory_start_p = (unsigned long) &_end; + *memory_end_p = memory_end; + + request_standard_resources(memory_end); + check_initrd(memory_end); #ifdef CONFIG_VT #if defined(CONFIG_VGA_CONSOLE) @@ -401,40 +439,49 @@ setup_arch(char **cmdline_p, unsigned long * memory_start_p, unsigned long * mem conswitchp = &dummy_con; #endif #endif - - *cmdline_p = command_line; - *memory_end_p = memory_end; } static const char *machine_desc[] = { - "EBSA110", - "Acorn-RiscPC", + /* Machine name Allocater */ + "EBSA110", /* RMK */ + "Acorn-RiscPC", /* RMK */ "unknown", - "Nexus-FTV/PCI", - "EBSA285", - "Rebel-NetWinder", - "Chalice-CATS", - "unknown-TBOX", - "co-EBSA285", - "CL-PS7110", - "Acorn-Archimedes", - "Acorn-A5000" + "Nexus-FTV/PCI", /* Philip Blundell */ + "EBSA285", /* RMK */ + "Rebel-NetWinder", /* RMK */ + "Chalice-CATS", /* Philip Blundell */ + "unknown-TBOX", /* Philip Blundell */ + "co-EBSA285", /* Mark van Doesburg */ + "CL-PS7110", /* Werner Almesberger */ + "Acorn-Archimedes", /* RMK/DAG */ + "Acorn-A5000", /* RMK/PB */ + "Etoile", /* Alex de Vries */ + "LaCie_NAS", /* Benjamin Herrenschmidt */ + "CL-PS7500", /* Philip Blundell */ + "Shark" /* Alexander Schulz */ }; int get_cpuinfo(char * buffer) { - int len; - - len = sprintf(buffer, - "Processor\t: %s %s rev %d (%s)\n" - "BogoMips\t: %lu.%02lu\n" - "Hardware\t: %s\n", - proc_info.manufacturer, - proc_info.cpu_name, - (int)processor_id & 15, - elf_platform, - (loops_per_sec+2500) / 500000, - ((loops_per_sec+2500) / 5000) % 100, - machine_desc[machine_arch_type]); - return len; + char *p = buffer; + + p += sprintf(p, "Processor\t: %s %s rev %d (%s)\n", + proc_info.manufacturer, proc_info.cpu_name, + (int)processor_id & 15, elf_platform); + + p += sprintf(p, "BogoMIPS\t: %lu.%02lu\n", + (loops_per_sec+2500) / 500000, + ((loops_per_sec+2500) / 5000) % 100); + + p += sprintf(p, "Hardware\t: %s\n", + machine_desc[machine_arch_type]); + + p += sprintf(p, "Revision\t: %04x\n", + system_rev); + + p += sprintf(p, "Serial\t\t: %08x%08x\n", + system_serial_high, + system_serial_low); + + return p - buffer; } diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index 7b2f430c1..3f86f4740 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c @@ -16,6 +16,8 @@ #include <linux/ptrace.h> #include <linux/unistd.h> #include <linux/stddef.h> +#include <linux/binfmts.h> +#include <linux/tty.h> #include <asm/ucontext.h> #include <asm/uaccess.h> @@ -550,6 +552,7 @@ asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall) default: lock_kernel(); sigaddset(¤t->signal, signr); + recalc_sigpending(current); current->flags |= PF_SIGNALED; do_exit(exit_code); /* NOTREACHED */ diff --git a/arch/arm/kernel/sys_arm.c b/arch/arm/kernel/sys_arm.c index 94f158b3e..3d39c8d39 100644 --- a/arch/arm/kernel/sys_arm.c +++ b/arch/arm/kernel/sys_arm.c @@ -19,6 +19,7 @@ #include <linux/shm.h> #include <linux/stat.h> #include <linux/mman.h> +#include <linux/fs.h> #include <linux/file.h> #include <linux/utsname.h> diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index 9f9e6934f..da0d464f6 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c @@ -365,7 +365,6 @@ asmlinkage void baddataabort(int code, unsigned long instr, struct pt_regs *regs { pgd_t *pgd; - printk ("current->thread.memmap = %08lX\n", current->thread.memmap); pgd = pgd_offset(current->mm, addr); printk ("*pgd = %08lx", pgd_val (*pgd)); if (!pgd_none (*pgd)) { diff --git a/arch/arm/lib/Makefile b/arch/arm/lib/Makefile index 7f5ff3012..9a22fe08f 100644 --- a/arch/arm/lib/Makefile +++ b/arch/arm/lib/Makefile @@ -1,11 +1,11 @@ # # linux/arch/arm/lib/Makefile # -# Copyright (C) 1995-1998 Russell King +# Copyright (C) 1995-1999 Russell King # L_TARGET := lib.a -L_OBJS := backtrace.o bitops.o checksum.o delay.o memcpy.o \ +L_OBJS := backtrace.o bitops.o checksum.o delay.o \ string.o system.o uaccess.o ifeq ($(PROCESSOR),armo) @@ -13,7 +13,7 @@ ifeq ($(PROCESSOR),armo) endif ifdef CONFIG_ARCH_ACORN - L_OBJS += loaders.o io-acorn.o + L_OBJS += io-acorn.o ifdef CONFIG_ARCH_A5K L_OBJS += floppydma.o endif @@ -34,13 +34,13 @@ endif include $(TOPDIR)/Rules.make +.S.o: + $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c -o $*.o $< + constants.h: getconsdata.o extractconstants.pl $(PERL) extractconstants.pl $(OBJDUMP) > $@ getconsdata.o: getconsdata.c $(CC) $(CFLAGS) -c getconsdata.c -checksum.o: constants.h - -%.o: %.S - $(CC) $(CFLAGS) -D__ASSEMBLY__ -c -o $@ $< +checksum.o string.o: constants.h diff --git a/arch/arm/lib/backtrace.S b/arch/arm/lib/backtrace.S index 3789d7d4d..fd85107c3 100644 --- a/arch/arm/lib/backtrace.S +++ b/arch/arm/lib/backtrace.S @@ -21,6 +21,11 @@ ENTRY(__backtrace) mov r0, fp ENTRY(c_backtrace) + +#ifndef CONFIG_FRAME_POINTER + mov pc, lr +#else + stmfd sp!, {r4 - r8, lr} @ Save an extra register so we have a location... #ifdef CONFIG_CPU_32 tst r1, #0x10 @ 26 or 32-bit? @@ -103,3 +108,5 @@ ENTRY(c_backtrace) .align .Ldsi: .word 0x00e92dd8 >> 2 .word 0x00e92d00 >> 2 + +#endif diff --git a/arch/arm/lib/checksum.S b/arch/arm/lib/checksum.S index daf49fc94..ae78b657a 100644 --- a/arch/arm/lib/checksum.S +++ b/arch/arm/lib/checksum.S @@ -74,8 +74,9 @@ ENTRY(csum_partial) stmfd sp!, {r1 - r2, r4 - r8, fp, ip, lr, pc} .endm -#define LOAD_REGS(cond) \ - LOADREGS(##cond##ea,fp,{r1 - r2, r4 - r8, fp, sp, pc}) + .macro load_regs,flags + ldm\flags fp, {r1, r2, r4-r8, fp, sp, pc} + .endm .macro load1b, reg1 9999: ldrbt \reg1, [r0], $1 @@ -134,8 +135,9 @@ ENTRY(csum_partial) mov r9, r9, lsr #24 .endm -#define LOAD_REGS(cond) \ - LOADREGS(##cond##ea,fp,{r1 - r2, r4 - r9, fp, sp, pc}) + .macro load_regs,flags + ldm\flags fp, {r1, r2, r4-r9, fp, sp, pc}^ + .endm .macro load1b, reg1 tst r9, #0x01 @@ -245,7 +247,7 @@ ENTRY(csum_partial_copy_from_user) adcs r3, r3, r4 4: ands r2, r2, #3 adceq r0, r3, #0 - LOAD_REGS(eq) + load_regs eqea load1l r4 tst r2, #2 beq .exit @@ -259,11 +261,11 @@ ENTRY(csum_partial_copy_from_user) andne r4, r4, #255 adcnes r3, r3, r4 adcs r0, r3, #0 - LOAD_REGS(al) + load_regs ea .too_small_user: teq r2, #0 - LOAD_REGS(eq) + load_regs eqea cmp r2, #2 blt .too_small_user1 load2b ip, r8 @@ -278,7 +280,7 @@ ENTRY(csum_partial_copy_from_user) strb ip, [r1], #1 adcs r3, r3, ip .csum_exit: adc r0, r3, #0 - LOAD_REGS(al) + load_regs ea .src_not_aligned_user: cmp r2, #4 @@ -331,7 +333,7 @@ ENTRY(csum_partial_copy_from_user) mov r4, r5, lsr #8 4: ands r2, r2, #3 adceq r0, r3, #0 - LOAD_REGS(eq) + load_regs eqea tst r2, #2 beq .exit adcs r3, r3, r4, lsl #16 @@ -384,7 +386,7 @@ ENTRY(csum_partial_copy_from_user) mov r4, r5, lsr #16 4: ands r2, r2, #3 adceq r0, r3, #0 - LOAD_REGS(eq) + load_regs eqea tst r2, #2 beq .exit adcs r3, r3, r4, lsl #16 @@ -437,7 +439,7 @@ ENTRY(csum_partial_copy_from_user) mov r4, r5, lsr #24 4: ands r2, r2, #3 adceq r0, r3, #0 - LOAD_REGS(eq) + load_regs eqea tst r2, #2 beq .exit adcs r3, r3, r4, lsl #16 @@ -461,7 +463,7 @@ ENTRY(csum_partial_copy_from_user) 6002: teq r2, r1 strneb r3, [r1], #1 bne 6002b - LOAD_REGS(al) + load_regs ea #if defined(CONFIG_CPU_32) .previous #endif diff --git a/arch/arm/lib/getconsdata.c b/arch/arm/lib/getconsdata.c index 9c317b639..5b46baad0 100644 --- a/arch/arm/lib/getconsdata.c +++ b/arch/arm/lib/getconsdata.c @@ -6,7 +6,7 @@ #include <linux/config.h> #include <linux/sched.h> #include <linux/mm.h> -#include <linux/unistd.h> + #include <asm/pgtable.h> #include <asm/uaccess.h> @@ -15,13 +15,6 @@ #define OFF_TSK(n) (unsigned long)&(((struct task_struct *)0)->n) #define OFF_MM(n) (unsigned long)&(((struct mm_struct *)0)->n) -#ifdef KERNEL_DOMAIN -unsigned long DOM_KERNELDOMAIN = KERNEL_DOMAIN; -#endif -#ifdef USER_DOMAIN -unsigned long DOM_USERDOMAIN = USER_DOMAIN; -#endif - unsigned long TSK_STATE = OFF_TSK(state); unsigned long TSK_FLAGS = OFF_TSK(flags); unsigned long TSK_NEED_RESCHED = OFF_TSK(need_resched); @@ -34,10 +27,7 @@ unsigned long PGD = OFF_MM(pgd); unsigned long TSS_SAVE = OFF_TSK(thread.save); unsigned long TSS_FPESAVE = OFF_TSK(thread.fpstate.soft.save); -#ifdef CONFIG_CPU_26 -unsigned long TSS_MEMMAP = OFF_TSK(thread.memmap); -unsigned long TSS_MEMCMAP = OFF_TSK(thread.memcmap); -#elif defined(CONFIG_CPU_32) +#ifdef CONFIG_CPU_32 unsigned long TSS_DOMAIN = OFF_TSK(thread.domain); #endif @@ -86,6 +76,8 @@ unsigned long LPTE_EXEC = L_PTE_EXEC; unsigned long LPTE_DIRTY = L_PTE_DIRTY; #endif +unsigned long PAGE_SZ = PAGE_SIZE; + unsigned long KSWI_BASE = 0x900000; unsigned long KSWI_SYS_BASE = 0x9f0000; unsigned long SYS_ERROR0 = 0x9f0000; diff --git a/arch/arm/lib/loaders.S b/arch/arm/lib/loaders.S deleted file mode 100644 index 760e2e311..000000000 --- a/arch/arm/lib/loaders.S +++ /dev/null @@ -1,53 +0,0 @@ -/* - * linux/arch/arm/lib/loaders.S - * - * This file contains the ROM loaders for buggy cards - */ -#include <linux/linkage.h> -#include <asm/assembler.h> - -/* - * Oak SCSI - */ - -ENTRY(oak_scsi_loader) - b Loak_scsi_read - .word 0 -Loak_scsi_reset: bic r10, r11, #0x00ff0000 - ldr r2, [r10] - RETINSTR(mov,pc,lr) - -Loak_scsi_read: mov r2, r1, lsr #3 - and r2, r2, #15 << 9 - bic r10, r11, #0x00ff0000 - ldr r2, [r10, r2] - mov r2, r1, lsl #20 - ldrb r0, [r11, r2, lsr #18] - ldr r2, [r10] - RETINSTR(mov,pc,lr) - -ENTRY(atomwide_serial_loader) - b Latomwide_serial_read - .word 0 -Latomwide_serial_reset: mov r2, #0x3c00 - strb r2, [r11, r2] - RETINSTR(mov,pc,lr) - -Latomwide_serial_read: cmp r1, #0x8000 - RETINSTR(movhi,pc,lr) - add r0, r1, #0x800 - mov r0, r0, lsr #11 - mov r3, #0x3c00 - strb r0, [r11, r3] - mov r2, r1, lsl #21 - ldrb r0, [r11, r2, lsr #19] - strb r2, [r11, r3] - RETINSTR(mov,pc,lr) - -/* - * Cards we don't know about yet - */ -ENTRY(noloader) - mov r0, r0 - mov r0, #0 - RETINSTR(mov,pc,lr) diff --git a/arch/arm/lib/memcpy.S b/arch/arm/lib/memcpy.S deleted file mode 100644 index f26e6cb1c..000000000 --- a/arch/arm/lib/memcpy.S +++ /dev/null @@ -1,319 +0,0 @@ -/* - * linux/arch/arm/lib/segment.S - * - * Copyright (C) 1995, 1996 Russell King - * Except memcpy/memmove routine. - */ - -#include <asm/assembler.h> -#include <linux/linkage.h> - -#ifndef ENTRY -#define ENTRY(x...) \ - .globl _##x; \ -_##x: -#endif - - .text -#define ENTER \ - mov ip,sp ;\ - stmfd sp!,{r4-r9,fp,ip,lr,pc} ;\ - sub fp,ip,#4 - -#define EXIT \ - LOADREGS(ea, fp, {r4 - r9, fp, sp, pc}) - -#define EXITEQ \ - LOADREGS(eqea, fp, {r4 - r9, fp, sp, pc}) - -# Prototype: void memcpy(void *to,const void *from,unsigned long n); -# ARM3: cant use memcopy here!!! - -ENTRY(memcpy) -ENTRY(memmove) - ENTER - cmp r1, r0 - bcc 19f - subs r2, r2, #4 - blt 6f - ands ip, r0, #3 - bne 7f - ands ip, r1, #3 - bne 8f - -1: subs r2, r2, #8 - blt 5f - subs r2, r2, #0x14 - blt 3f -2: ldmia r1!,{r3 - r9, ip} - stmia r0!,{r3 - r9, ip} - subs r2, r2, #32 - bge 2b - cmn r2, #16 - ldmgeia r1!, {r3 - r6} - stmgeia r0!, {r3 - r6} - subge r2, r2, #0x10 -3: adds r2, r2, #0x14 -4: ldmgeia r1!, {r3 - r5} - stmgeia r0!, {r3 - r5} - subges r2, r2, #12 - bge 4b -5: adds r2, r2, #8 - blt 6f - subs r2, r2, #4 - ldrlt r3, [r1], #4 - strlt r3, [r0], #4 - ldmgeia r1!, {r3, r4} - stmgeia r0!, {r3, r4} - subge r2, r2, #4 - -6: adds r2, r2, #4 - EXITEQ - cmp r2, #2 - ldrb r3, [r1], #1 - strb r3, [r0], #1 - ldrgeb r3, [r1], #1 - strgeb r3, [r0], #1 - ldrgtb r3, [r1], #1 - strgtb r3, [r0], #1 - EXIT - -7: rsb ip, ip, #4 - cmp ip, #2 - ldrb r3, [r1], #1 - strb r3, [r0], #1 - ldrgeb r3, [r1], #1 - strgeb r3, [r0], #1 - ldrgtb r3, [r1], #1 - strgtb r3, [r0], #1 - subs r2, r2, ip - blt 6b - ands ip, r1, #3 - beq 1b - -8: bic r1, r1, #3 - ldr r7, [r1], #4 - cmp ip, #2 - bgt 15f - beq 11f - cmp r2, #12 - blt 10f - sub r2, r2, #12 -9: mov r3, r7, lsr #8 - ldmia r1!, {r4 - r7} - orr r3, r3, r4, lsl #24 - mov r4, r4, lsr #8 - orr r4, r4, r5, lsl #24 - mov r5, r5, lsr #8 - orr r5, r5, r6, lsl #24 - mov r6, r6, lsr #8 - orr r6, r6, r7, lsl #24 - stmia r0!, {r3 - r6} - subs r2, r2, #16 - bge 9b - adds r2, r2, #12 - blt 100f -10: mov r3, r7, lsr #8 - ldr r7, [r1], #4 - orr r3, r3, r7, lsl #24 - str r3, [r0], #4 - subs r2, r2, #4 - bge 10b -100: sub r1, r1, #3 - b 6b - -11: cmp r2, #12 - blt 13f /* */ - sub r2, r2, #12 -12: mov r3, r7, lsr #16 - ldmia r1!, {r4 - r7} - orr r3, r3, r4, lsl #16 - mov r4, r4, lsr #16 - orr r4, r4, r5, lsl #16 - mov r5, r5, lsr #16 - orr r5, r5, r6, lsl #16 - mov r6, r6, lsr #16 - orr r6, r6, r7,LSL#16 - stmia r0!, {r3 - r6} - subs r2, r2, #16 - bge 12b - adds r2, r2, #12 - blt 14f -13: mov r3, r7, lsr #16 - ldr r7, [r1], #4 - orr r3, r3, r7, lsl #16 - str r3, [r0], #4 - subs r2, r2, #4 - bge 13b -14: sub r1, r1, #2 - b 6b - -15: cmp r2, #12 - blt 17f - sub r2, r2, #12 -16: mov r3, r7, lsr #24 - ldmia r1!,{r4 - r7} - orr r3, r3, r4, lsl #8 - mov r4, r4, lsr #24 - orr r4, r4, r5, lsl #8 - mov r5, r5, lsr #24 - orr r5, r5, r6, lsl #8 - mov r6, r6, lsr #24 - orr r6, r6, r7, lsl #8 - stmia r0!, {r3 - r6} - subs r2, r2, #16 - bge 16b - adds r2, r2, #12 - blt 18f -17: mov r3, r7, lsr #24 - ldr r7, [r1], #4 - orr r3, r3, r7, lsl#8 - str r3, [r0], #4 - subs r2, r2, #4 - bge 17b -18: sub r1, r1, #1 - b 6b - - -19: add r1, r1, r2 - add r0, r0, r2 - subs r2, r2, #4 - blt 24f - ands ip, r0, #3 - bne 25f - ands ip, r1, #3 - bne 26f - -20: subs r2, r2, #8 - blt 23f - subs r2, r2, #0x14 - blt 22f -21: ldmdb r1!, {r3 - r9, ip} - stmdb r0!, {r3 - r9, ip} - subs r2, r2, #32 - bge 21b -22: cmn r2, #16 - ldmgedb r1!, {r3 - r6} - stmgedb r0!, {r3 - r6} - subge r2, r2, #16 - adds r2, r2, #20 - ldmgedb r1!, {r3 - r5} - stmgedb r0!, {r3 - r5} - subge r2, r2, #12 -23: adds r2, r2, #8 - blt 24f - subs r2, r2, #4 - ldrlt r3, [r1, #-4]! - strlt r3, [r0, #-4]! - ldmgedb r1!, {r3, r4} - stmgedb r0!, {r3, r4} - subge r2, r2, #4 - -24: adds r2, r2, #4 - EXITEQ - cmp r2, #2 - ldrb r3, [r1, #-1]! - strb r3, [r0, #-1]! - ldrgeb r3, [r1, #-1]! - strgeb r3, [r0, #-1]! - ldrgtb r3, [r1, #-1]! - strgtb r3, [r0, #-1]! - EXIT - -25: cmp ip, #2 - ldrb r3, [r1, #-1]! - strb r3, [r0, #-1]! - ldrgeb r3, [r1, #-1]! - strgeb r3, [r0, #-1]! - ldrgtb r3, [r1, #-1]! - strgtb r3, [r0, #-1]! - subs r2, r2, ip - blt 24b - ands ip, r1, #3 - beq 20b - -26: bic r1, r1, #3 - ldr r3, [r1], #0 - cmp ip, #2 - blt 34f - beq 30f - cmp r2, #12 - blt 28f - sub r2, r2, #12 -27: mov r7, r3, lsl #8 - ldmdb r1!, {r3, r4, r5, r6} - orr r7, r7, r6, lsr #24 - mov r6, r6, lsl #8 - orr r6, r6, r5, lsr #24 - mov r5, r5, lsl #8 - orr r5, r5, r4, lsr #24 - mov r4, r4, lsl #8 - orr r4, r4, r3, lsr #24 - stmdb r0!, {r4, r5, r6, r7} - subs r2, r2, #16 - bge 27b - adds r2, r2, #12 - blt 29f -28: mov ip, r3, lsl #8 - ldr r3, [r1, #-4]! - orr ip, ip, r3, lsr #24 - str ip, [r0, #-4]! - subs r2, r2, #4 - bge 28b -29: add r1, r1, #3 - b 24b - -30: cmp r2, #12 - blt 32f - sub r2, r2, #12 -31: mov r7, r3, lsl #16 - ldmdb r1!, {r3, r4, r5, r6} - orr r7, r7, r6, lsr #16 - mov r6, r6, lsl #16 - orr r6, r6, r5, lsr #16 - mov r5, r5, lsl #16 - orr r5, r5, r4, lsr #16 - mov r4, r4, lsl #16 - orr r4, r4, r3, lsr #16 - stmdb r0!, {r4, r5, r6, r7} - subs r2, r2, #16 - bge 31b - adds r2, r2, #12 - blt 33f -32: mov ip, r3, lsl #16 - ldr r3, [r1, #-4]! - orr ip, ip, r3, lsr #16 - str ip, [r0, #-4]! - subs r2, r2, #4 - bge 32b -33: add r1, r1, #2 - b 24b - -34: cmp r2, #12 - blt 36f - sub r2, r2, #12 -35: mov r7, r3, lsl #24 - ldmdb r1!, {r3, r4, r5, r6} - orr r7, r7, r6, lsr #8 - mov r6, r6, lsl #24 - orr r6, r6, r5, lsr #8 - mov r5, r5, lsl #24 - orr r5, r5, r4, lsr #8 - mov r4, r4, lsl #24 - orr r4, r4, r3, lsr #8 - stmdb r0!, {r4, r5, r6, r7} - subs r2, r2, #16 - bge 35b - adds r2, r2, #12 - blt 37f -36: mov ip, r3, lsl #24 - ldr r3, [r1, #-4]! - orr ip, ip, r3, lsr #8 - str ip, [r0, #-4]! - subs r2, r2, #4 - bge 36b -37: add r1, r1, #1 - b 24b - - .align - diff --git a/arch/arm/lib/string.S b/arch/arm/lib/string.S index 17daa9d26..ff809fd51 100644 --- a/arch/arm/lib/string.S +++ b/arch/arm/lib/string.S @@ -1,69 +1,112 @@ /* * linux/arch/arm/lib/string.S * - * Copyright (C) 1995-1998 Russell King + * Copyright (C) 1995-1999 Russell King * - * This is commonly used to clear the frame buffer and the frame - * backing buffer. As such, it will be rarely called with r2 < 32. + * ASM optimised string functions * - * Optimisations by Matthew Wilcox */ #include <linux/linkage.h> #include <asm/assembler.h> +#include "constants.h" + .text -# Prototype: char *strrchr(const char *s,char c); /* * Prototype: void memzero(void *d, size_t n) */ -ENTRY(memzero) - mov r2, r1 - mov r1, #0 -/* - * Prototype: void memsetl(unsigned long *d, unsigned long c, size_t n) - */ -ENTRY(memsetl) - teq r2, #0 - RETINSTR(moveq,pc,lr) - stmfd sp!, {lr} - mov lr, r1 - mov r3, r1 - mov ip, r1 +1: @ 4 <= r1 + cmp ip, #2 @ 1 + strltb r2, [r0], #1 @ 1 + strleb r2, [r0], #1 @ 1 + strb r2, [r0], #1 @ 1 + rsb ip, ip, #4 @ 1 + sub r1, r1, ip @ 1 + cmp r1, #3 @ 1 + bgt 2f @ 1 @ +8 + b 4f @ 1 @ +9 - @ r2 = {32 ... 4} + .align 5 -1: subs r2, r2, #32 - stmgeia r0!, {r1, r3, ip, lr} - stmgeia r0!, {r1, r3, ip, lr} - bgt 1b - LOADREGS(eqfd, sp!, {pc}) +ENTRY(__memzero) + mov r2, #0 @ 1 + cmp r1, #4 @ 1 + blt 4f @ 1 @ = 3 - @ r2 can be {-4 ... -28} + @ r1 >= 4 - cmp r2, #-16 - stmgeia r0!, {r1, r3, ip, lr} - addlts r2, r2, #16 - LOADREGS(eqfd, sp!, {pc}) + ands ip, r0, #3 @ 1 + bne 1b @ 1 @ = 5 - @ r2 can be {-4 ... -12} +2: @ r1 >= 4 && (r0 & 3) = 0 @ = 5 or 11 - cmp r2, #-8 - stmgeia r0!, {r1, r3} - strne r1, [r0] - LOADREGS(fd, sp!, {pc}) + str lr, [sp, #-4]! @ 1 + mov r3, #0 @ 1 + mov ip, #0 @ 1 + mov lr, #0 @ 1 + + @ 4 <= r1 <= 32 @ = 9 or 15 + +3: subs r1, r1, #32 @ 1 + stmgeia r0!, {r2, r3, ip, lr} @ 4 + stmgeia r0!, {r2, r3, ip, lr} @ 4 + bgt 3b @ 1 + LOADREGS(eqfd, sp!, {pc}) @ 1/2 + + @ -28 <= r1 <= -1 + + cmp r1, #-16 @ 1 + stmgeia r0!, {r2, r3, ip, lr} @ 4 + ldr lr, [sp], #4 @ 1 + addlts r1, r1, #16 @ 1 + RETINSTR(moveq,pc,lr) @ 1 - .global __page_memcpy -__page_memcpy: stmfd sp!, {r4, r5, lr} -1: subs r2, r2, #4*8 - ldmgeia r1!, {r3, r4, r5, ip} - stmgeia r0!, {r3, r4, r5, ip} - ldmgeia r1!, {r3, r4, r5, ip} - stmgeia r0!, {r3, r4, r5, ip} - bgt 1b - LOADREGS(fd, sp!, {r4, r5, pc}) - - .global memset -memset: mov r3, r0 + @ -12 <= r1 <= -1 + + cmp r1, #-8 @ 1 + stmgeia r0!, {r2, r3} @ 2 + addlts r1, r1, #8 @ 1 + RETINSTR(moveq,pc,lr) @ 1 + + @ -4 <= r1 <= -1 + + cmp r1, #-4 @ 1 + strge r2, [r0], #4 @ 1 + adds r1, r1, #4 @ 1 + RETINSTR(moveq,pc,lr) @ 1 + +4: @ 1 <= r1 <= 3 + cmp r1, #2 @ 1 + strgtb r2, [r0], #1 @ 1 + strgeb r2, [r0], #1 @ 1 + strb r2, [r0], #1 @ 1 + RETINSTR(mov,pc,lr) @ 1 + +/* + * StrongARM optimised copy_page routine + * now 1.72bytes/cycle, was 1.60 bytes/cycle + * (50MHz bus -> 86MB/s) + */ + +ENTRY(copy_page) + stmfd sp!, {r4, lr} @ 2 + mov r2, #PAGE_SZ/64 @ 1 +1: ldmia r1!, {r3, r4, ip, lr} @ 4 + subs r2, r2, #1 @ 1 + stmia r0!, {r3, r4, ip, lr} @ 4 + ldmia r1!, {r3, r4, ip, lr} @ 4+1 + stmia r0!, {r3, r4, ip, lr} @ 4 + ldmia r1!, {r3, r4, ip, lr} @ 4+1 + stmia r0!, {r3, r4, ip, lr} @ 4 + ldmia r1!, {r3, r4, ip, lr} @ 4+1 + stmia r0!, {r3, r4, ip, lr} @ 4 + bne 1b @ 1 + LOADREGS(fd, sp!, {r4, pc}) @ 3 + + .align 5 +ENTRY(memset) /* needed for some versions of gcc */ +ENTRY(__memset) + mov r3, r0 cmp r2, #16 blt 6f ands ip, r3, #3 @@ -168,3 +211,309 @@ ENTRY(memchr) 2: movne r0, #0 subeq r0, r0, #1 LOADREGS(fd, sp!, {pc}) + + +#define ENTER \ + mov ip,sp ;\ + stmfd sp!,{r4-r9,fp,ip,lr,pc} ;\ + sub fp,ip,#4 + +#define EXIT \ + LOADREGS(ea, fp, {r4 - r9, fp, sp, pc}) + +#define EXITEQ \ + LOADREGS(eqea, fp, {r4 - r9, fp, sp, pc}) + +/* + * Prototype: void memcpy(void *to,const void *from,unsigned long n); + * ARM3: cant use memcopy here!!! + */ +ENTRY(memcpy) +ENTRY(memmove) + ENTER + cmp r1, r0 + bcc 19f + subs r2, r2, #4 + blt 6f + ands ip, r0, #3 + bne 7f + ands ip, r1, #3 + bne 8f + +1: subs r2, r2, #8 + blt 5f + subs r2, r2, #0x14 + blt 3f +2: ldmia r1!,{r3 - r9, ip} + stmia r0!,{r3 - r9, ip} + subs r2, r2, #32 + bge 2b + cmn r2, #16 + ldmgeia r1!, {r3 - r6} + stmgeia r0!, {r3 - r6} + subge r2, r2, #0x10 +3: adds r2, r2, #0x14 +4: ldmgeia r1!, {r3 - r5} + stmgeia r0!, {r3 - r5} + subges r2, r2, #12 + bge 4b +5: adds r2, r2, #8 + blt 6f + subs r2, r2, #4 + ldrlt r3, [r1], #4 + strlt r3, [r0], #4 + ldmgeia r1!, {r3, r4} + stmgeia r0!, {r3, r4} + subge r2, r2, #4 + +6: adds r2, r2, #4 + EXITEQ + cmp r2, #2 + ldrb r3, [r1], #1 + strb r3, [r0], #1 + ldrgeb r3, [r1], #1 + strgeb r3, [r0], #1 + ldrgtb r3, [r1], #1 + strgtb r3, [r0], #1 + EXIT + +7: rsb ip, ip, #4 + cmp ip, #2 + ldrb r3, [r1], #1 + strb r3, [r0], #1 + ldrgeb r3, [r1], #1 + strgeb r3, [r0], #1 + ldrgtb r3, [r1], #1 + strgtb r3, [r0], #1 + subs r2, r2, ip + blt 6b + ands ip, r1, #3 + beq 1b + +8: bic r1, r1, #3 + ldr r7, [r1], #4 + cmp ip, #2 + bgt 15f + beq 11f + cmp r2, #12 + blt 10f + sub r2, r2, #12 +9: mov r3, r7, lsr #8 + ldmia r1!, {r4 - r7} + orr r3, r3, r4, lsl #24 + mov r4, r4, lsr #8 + orr r4, r4, r5, lsl #24 + mov r5, r5, lsr #8 + orr r5, r5, r6, lsl #24 + mov r6, r6, lsr #8 + orr r6, r6, r7, lsl #24 + stmia r0!, {r3 - r6} + subs r2, r2, #16 + bge 9b + adds r2, r2, #12 + blt 100f +10: mov r3, r7, lsr #8 + ldr r7, [r1], #4 + orr r3, r3, r7, lsl #24 + str r3, [r0], #4 + subs r2, r2, #4 + bge 10b +100: sub r1, r1, #3 + b 6b + +11: cmp r2, #12 + blt 13f /* */ + sub r2, r2, #12 +12: mov r3, r7, lsr #16 + ldmia r1!, {r4 - r7} + orr r3, r3, r4, lsl #16 + mov r4, r4, lsr #16 + orr r4, r4, r5, lsl #16 + mov r5, r5, lsr #16 + orr r5, r5, r6, lsl #16 + mov r6, r6, lsr #16 + orr r6, r6, r7,LSL#16 + stmia r0!, {r3 - r6} + subs r2, r2, #16 + bge 12b + adds r2, r2, #12 + blt 14f +13: mov r3, r7, lsr #16 + ldr r7, [r1], #4 + orr r3, r3, r7, lsl #16 + str r3, [r0], #4 + subs r2, r2, #4 + bge 13b +14: sub r1, r1, #2 + b 6b + +15: cmp r2, #12 + blt 17f + sub r2, r2, #12 +16: mov r3, r7, lsr #24 + ldmia r1!,{r4 - r7} + orr r3, r3, r4, lsl #8 + mov r4, r4, lsr #24 + orr r4, r4, r5, lsl #8 + mov r5, r5, lsr #24 + orr r5, r5, r6, lsl #8 + mov r6, r6, lsr #24 + orr r6, r6, r7, lsl #8 + stmia r0!, {r3 - r6} + subs r2, r2, #16 + bge 16b + adds r2, r2, #12 + blt 18f +17: mov r3, r7, lsr #24 + ldr r7, [r1], #4 + orr r3, r3, r7, lsl#8 + str r3, [r0], #4 + subs r2, r2, #4 + bge 17b +18: sub r1, r1, #1 + b 6b + + +19: add r1, r1, r2 + add r0, r0, r2 + subs r2, r2, #4 + blt 24f + ands ip, r0, #3 + bne 25f + ands ip, r1, #3 + bne 26f + +20: subs r2, r2, #8 + blt 23f + subs r2, r2, #0x14 + blt 22f +21: ldmdb r1!, {r3 - r9, ip} + stmdb r0!, {r3 - r9, ip} + subs r2, r2, #32 + bge 21b +22: cmn r2, #16 + ldmgedb r1!, {r3 - r6} + stmgedb r0!, {r3 - r6} + subge r2, r2, #16 + adds r2, r2, #20 + ldmgedb r1!, {r3 - r5} + stmgedb r0!, {r3 - r5} + subge r2, r2, #12 +23: adds r2, r2, #8 + blt 24f + subs r2, r2, #4 + ldrlt r3, [r1, #-4]! + strlt r3, [r0, #-4]! + ldmgedb r1!, {r3, r4} + stmgedb r0!, {r3, r4} + subge r2, r2, #4 + +24: adds r2, r2, #4 + EXITEQ + cmp r2, #2 + ldrb r3, [r1, #-1]! + strb r3, [r0, #-1]! + ldrgeb r3, [r1, #-1]! + strgeb r3, [r0, #-1]! + ldrgtb r3, [r1, #-1]! + strgtb r3, [r0, #-1]! + EXIT + +25: cmp ip, #2 + ldrb r3, [r1, #-1]! + strb r3, [r0, #-1]! + ldrgeb r3, [r1, #-1]! + strgeb r3, [r0, #-1]! + ldrgtb r3, [r1, #-1]! + strgtb r3, [r0, #-1]! + subs r2, r2, ip + blt 24b + ands ip, r1, #3 + beq 20b + +26: bic r1, r1, #3 + ldr r3, [r1], #0 + cmp ip, #2 + blt 34f + beq 30f + cmp r2, #12 + blt 28f + sub r2, r2, #12 +27: mov r7, r3, lsl #8 + ldmdb r1!, {r3, r4, r5, r6} + orr r7, r7, r6, lsr #24 + mov r6, r6, lsl #8 + orr r6, r6, r5, lsr #24 + mov r5, r5, lsl #8 + orr r5, r5, r4, lsr #24 + mov r4, r4, lsl #8 + orr r4, r4, r3, lsr #24 + stmdb r0!, {r4, r5, r6, r7} + subs r2, r2, #16 + bge 27b + adds r2, r2, #12 + blt 29f +28: mov ip, r3, lsl #8 + ldr r3, [r1, #-4]! + orr ip, ip, r3, lsr #24 + str ip, [r0, #-4]! + subs r2, r2, #4 + bge 28b +29: add r1, r1, #3 + b 24b + +30: cmp r2, #12 + blt 32f + sub r2, r2, #12 +31: mov r7, r3, lsl #16 + ldmdb r1!, {r3, r4, r5, r6} + orr r7, r7, r6, lsr #16 + mov r6, r6, lsl #16 + orr r6, r6, r5, lsr #16 + mov r5, r5, lsl #16 + orr r5, r5, r4, lsr #16 + mov r4, r4, lsl #16 + orr r4, r4, r3, lsr #16 + stmdb r0!, {r4, r5, r6, r7} + subs r2, r2, #16 + bge 31b + adds r2, r2, #12 + blt 33f +32: mov ip, r3, lsl #16 + ldr r3, [r1, #-4]! + orr ip, ip, r3, lsr #16 + str ip, [r0, #-4]! + subs r2, r2, #4 + bge 32b +33: add r1, r1, #2 + b 24b + +34: cmp r2, #12 + blt 36f + sub r2, r2, #12 +35: mov r7, r3, lsl #24 + ldmdb r1!, {r3, r4, r5, r6} + orr r7, r7, r6, lsr #8 + mov r6, r6, lsl #24 + orr r6, r6, r5, lsr #8 + mov r5, r5, lsl #24 + orr r5, r5, r4, lsr #8 + mov r4, r4, lsl #24 + orr r4, r4, r3, lsr #8 + stmdb r0!, {r4, r5, r6, r7} + subs r2, r2, #16 + bge 35b + adds r2, r2, #12 + blt 37f +36: mov ip, r3, lsl #24 + ldr r3, [r1, #-4]! + orr ip, ip, r3, lsr #8 + str ip, [r0, #-4]! + subs r2, r2, #4 + bge 36b +37: add r1, r1, #1 + b 24b + + .align + + diff --git a/arch/arm/lib/system.S b/arch/arm/lib/system.S deleted file mode 100644 index f037d56ff..000000000 --- a/arch/arm/lib/system.S +++ /dev/null @@ -1,20 +0,0 @@ -/* - * linux/arch/arm/lib/system.S - * - * Copyright (C) 1995, 1996 Russell King - * - * 07/06/96: Now support tasks running in SVC mode. - */ -#include <linux/linkage.h> -#include <asm/assembler.h> - - .text - -ENTRY(abort) - adr r0, .abort_msg - mov r1, lr - b SYMBOL_NAME(panic) - -.abort_msg: .ascii "Eek! Got to an abort() from %p! " - .ascii "(Please report to rmk@arm.uk.linux.org)\n\0" - .align diff --git a/arch/arm/lib/system.c b/arch/arm/lib/system.c new file mode 100644 index 000000000..f3b32cd82 --- /dev/null +++ b/arch/arm/lib/system.c @@ -0,0 +1,22 @@ +/* + * linux/arch/arm/lib/system.c + * + * Copyright (C) 1999 Russell King + * + * Converted from ASM version 04/09/1999 + */ +#include <linux/kernel.h> + +extern void abort(void) +{ + void *lr = __builtin_return_address(0); + + printk(KERN_CRIT "kernel abort from %p! (Please report to rmk@arm.linux.org.uk)\n", + lr); + + /* force an oops */ + *(int *)0 = 0; + + /* if that doesn't kill us, halt */ + panic("Oops failed to kill thread"); +} diff --git a/arch/arm/lib/uaccess-armo.S b/arch/arm/lib/uaccess-armo.S index 1a740493a..695fdf0c1 100644 --- a/arch/arm/lib/uaccess-armo.S +++ b/arch/arm/lib/uaccess-armo.S @@ -30,7 +30,7 @@ SYMBOL_NAME(uaccess_user): .word __arch_copy_to_user .word __arch_clear_user .word __arch_strncpy_from_user - .word __arch_strlen_user + .word __arch_strnlen_user @ In : r0 = x, r1 = addr, r2 = error diff --git a/arch/arm/lib/uaccess.S b/arch/arm/lib/uaccess.S index 67684cb1a..898d2ef77 100644 --- a/arch/arm/lib/uaccess.S +++ b/arch/arm/lib/uaccess.S @@ -42,11 +42,11 @@ _##x: rsb ip, ip, #4 cmp ip, #2 ldrb r3, [r1], #1 -USER( strbt r3, [r0], #1) // May fault +USER( strbt r3, [r0], #1) @ May fault ldrgeb r3, [r1], #1 -USER( strgebt r3, [r0], #1) // May fault +USER( strgebt r3, [r0], #1) @ May fault ldrgtb r3, [r1], #1 -USER( strgtbt r3, [r0], #1) // May fault +USER( strgtbt r3, [r0], #1) @ May fault sub r2, r2, ip b .c2u_dest_aligned @@ -69,8 +69,8 @@ ENTRY(__arch_copy_to_user) addmi ip, r2, #4 bmi .c2u_0nowords ldr r3, [r1], #4 -USER( strt r3, [r0], #4) // May fault - mov ip, r0, lsl #32 - PAGE_SHIFT // On each page, use a ld/st??t instruction +USER( strt r3, [r0], #4) @ May fault + mov ip, r0, lsl #32 - PAGE_SHIFT @ On each page, use a ld/st??t instruction rsb ip, ip, #0 movs ip, ip, lsr #32 - PAGE_SHIFT beq .c2u_0fupi @@ -84,31 +84,31 @@ USER( strt r3, [r0], #4) // May fault blt .c2u_0rem8lp .c2u_0cpy8lp: ldmia r1!, {r3 - r6} - stmia r0!, {r3 - r6} // Shouldn't fault + stmia r0!, {r3 - r6} @ Shouldn't fault ldmia r1!, {r3 - r6} - stmia r0!, {r3 - r6} // Shouldn't fault + stmia r0!, {r3 - r6} @ Shouldn't fault subs ip, ip, #32 bpl .c2u_0cpy8lp .c2u_0rem8lp: cmn ip, #16 ldmgeia r1!, {r3 - r6} - stmgeia r0!, {r3 - r6} // Shouldn't fault + stmgeia r0!, {r3 - r6} @ Shouldn't fault tst ip, #8 ldmneia r1!, {r3 - r4} - stmneia r0!, {r3 - r4} // Shouldn't fault + stmneia r0!, {r3 - r4} @ Shouldn't fault tst ip, #4 ldrne r3, [r1], #4 - strnet r3, [r0], #4 // Shouldn't fault + strnet r3, [r0], #4 @ Shouldn't fault ands ip, ip, #3 beq .c2u_0fupi .c2u_0nowords: teq ip, #0 beq .c2u_finished .c2u_nowords: cmp ip, #2 ldrb r3, [r1], #1 -USER( strbt r3, [r0], #1) // May fault +USER( strbt r3, [r0], #1) @ May fault ldrgeb r3, [r1], #1 -USER( strgebt r3, [r0], #1) // May fault +USER( strgebt r3, [r0], #1) @ May fault ldrgtb r3, [r1], #1 -USER( strgtbt r3, [r0], #1) // May fault +USER( strgtbt r3, [r0], #1) @ May fault b .c2u_finished .c2u_not_enough: @@ -129,7 +129,7 @@ USER( strgtbt r3, [r0], #1) // May fault mov r3, r7, lsr #8 ldr r7, [r1], #4 orr r3, r3, r7, lsl #24 -USER( strt r3, [r0], #4) // May fault +USER( strt r3, [r0], #4) @ May fault mov ip, r0, lsl #32 - PAGE_SHIFT rsb ip, ip, #0 movs ip, ip, lsr #32 - PAGE_SHIFT @@ -149,7 +149,7 @@ USER( strt r3, [r0], #4) // May fault orr r5, r5, r6, lsl #24 mov r6, r6, lsr #8 orr r6, r6, r7, lsl #24 - stmia r0!, {r3 - r6} // Shouldn't fault + stmia r0!, {r3 - r6} @ Shouldn't fault subs ip, ip, #16 bpl .c2u_1cpy8lp .c2u_1rem8lp: tst ip, #8 @@ -158,23 +158,23 @@ USER( strt r3, [r0], #4) // May fault orrne r3, r3, r4, lsl #24 movne r4, r4, lsr #8 orrne r4, r4, r7, lsl #24 - stmneia r0!, {r3 - r4} // Shouldn't fault + stmneia r0!, {r3 - r4} @ Shouldn't fault tst ip, #4 movne r3, r7, lsr #8 ldrne r7, [r1], #4 orrne r3, r3, r7, lsl #24 - strnet r3, [r0], #4 // Shouldn't fault + strnet r3, [r0], #4 @ Shouldn't fault ands ip, ip, #3 beq .c2u_1fupi .c2u_1nowords: mov r3, r7, lsr #8 teq ip, #0 beq .c2u_finished cmp ip, #2 -USER( strbt r3, [r0], #1) // May fault +USER( strbt r3, [r0], #1) @ May fault movge r3, r3, lsr #8 -USER( strgebt r3, [r0], #1) // May fault +USER( strgebt r3, [r0], #1) @ May fault movgt r3, r3, lsr #8 -USER( strgtbt r3, [r0], #1) // May fault +USER( strgtbt r3, [r0], #1) @ May fault b .c2u_finished .c2u_2fupi: subs r2, r2, #4 @@ -183,7 +183,7 @@ USER( strgtbt r3, [r0], #1) // May fault mov r3, r7, lsr #16 ldr r7, [r1], #4 orr r3, r3, r7, lsl #16 -USER( strt r3, [r0], #4) // May fault +USER( strt r3, [r0], #4) @ May fault mov ip, r0, lsl #32 - PAGE_SHIFT rsb ip, ip, #0 movs ip, ip, lsr #32 - PAGE_SHIFT @@ -203,7 +203,7 @@ USER( strt r3, [r0], #4) // May fault orr r5, r5, r6, lsl #16 mov r6, r6, lsr #16 orr r6, r6, r7, lsl #16 - stmia r0!, {r3 - r6} // Shouldn't fault + stmia r0!, {r3 - r6} @ Shouldn't fault subs ip, ip, #16 bpl .c2u_2cpy8lp .c2u_2rem8lp: tst ip, #8 @@ -212,23 +212,23 @@ USER( strt r3, [r0], #4) // May fault orrne r3, r3, r4, lsl #16 movne r4, r4, lsr #16 orrne r4, r4, r7, lsl #16 - stmneia r0!, {r3 - r4} // Shouldn't fault + stmneia r0!, {r3 - r4} @ Shouldn't fault tst ip, #4 movne r3, r7, lsr #16 ldrne r7, [r1], #4 orrne r3, r3, r7, lsl #16 - strnet r3, [r0], #4 // Shouldn't fault + strnet r3, [r0], #4 @ Shouldn't fault ands ip, ip, #3 beq .c2u_2fupi .c2u_2nowords: mov r3, r7, lsr #16 teq ip, #0 beq .c2u_finished cmp ip, #2 -USER( strbt r3, [r0], #1) // May fault +USER( strbt r3, [r0], #1) @ May fault movge r3, r3, lsr #8 -USER( strgebt r3, [r0], #1) // May fault +USER( strgebt r3, [r0], #1) @ May fault ldrgtb r3, [r1], #0 -USER( strgtbt r3, [r0], #1) // May fault +USER( strgtbt r3, [r0], #1) @ May fault b .c2u_finished .c2u_3fupi: subs r2, r2, #4 @@ -237,7 +237,7 @@ USER( strgtbt r3, [r0], #1) // May fault mov r3, r7, lsr #24 ldr r7, [r1], #4 orr r3, r3, r7, lsl #8 -USER( strt r3, [r0], #4) // May fault +USER( strt r3, [r0], #4) @ May fault mov ip, r0, lsl #32 - PAGE_SHIFT rsb ip, ip, #0 movs ip, ip, lsr #32 - PAGE_SHIFT @@ -257,7 +257,7 @@ USER( strt r3, [r0], #4) // May fault orr r5, r5, r6, lsl #8 mov r6, r6, lsr #24 orr r6, r6, r7, lsl #8 - stmia r0!, {r3 - r6} // Shouldn't fault + stmia r0!, {r3 - r6} @ Shouldn't fault subs ip, ip, #16 bpl .c2u_3cpy8lp .c2u_3rem8lp: tst ip, #8 @@ -266,23 +266,23 @@ USER( strt r3, [r0], #4) // May fault orrne r3, r3, r4, lsl #8 movne r4, r4, lsr #24 orrne r4, r4, r7, lsl #8 - stmneia r0!, {r3 - r4} // Shouldn't fault + stmneia r0!, {r3 - r4} @ Shouldn't fault tst ip, #4 movne r3, r7, lsr #24 ldrne r7, [r1], #4 orrne r3, r3, r7, lsl #8 - strnet r3, [r0], #4 // Shouldn't fault + strnet r3, [r0], #4 @ Shouldn't fault ands ip, ip, #3 beq .c2u_3fupi .c2u_3nowords: mov r3, r7, lsr #24 teq ip, #0 beq .c2u_finished cmp ip, #2 -USER( strbt r3, [r0], #1) // May fault +USER( strbt r3, [r0], #1) @ May fault ldrge r3, [r1], #0 -USER( strgebt r3, [r0], #1) // May fault +USER( strgebt r3, [r0], #1) @ May fault movgt r3, r3, lsr #8 -USER( strgtbt r3, [r0], #1) // May fault +USER( strgtbt r3, [r0], #1) @ May fault b .c2u_finished #ifndef TESTING @@ -302,11 +302,11 @@ USER( strgtbt r3, [r0], #1) // May fault .cfu_dest_not_aligned: rsb ip, ip, #4 cmp ip, #2 -USER( ldrbt r3, [r1], #1) // May fault +USER( ldrbt r3, [r1], #1) @ May fault strb r3, [r0], #1 -USER( ldrgebt r3, [r1], #1) // May fault +USER( ldrgebt r3, [r1], #1) @ May fault strgeb r3, [r0], #1 -USER( ldrgtbt r3, [r1], #1) // May fault +USER( ldrgtbt r3, [r1], #1) @ May fault strgtb r3, [r0], #1 sub r2, r2, ip b .cfu_dest_aligned @@ -330,7 +330,7 @@ ENTRY(__arch_copy_from_user) bmi .cfu_0nowords USER( ldrt r3, [r1], #4) str r3, [r0], #4 - mov ip, r1, lsl #32 - PAGE_SHIFT // On each page, use a ld/st??t instruction + mov ip, r1, lsl #32 - PAGE_SHIFT @ On each page, use a ld/st??t instruction rsb ip, ip, #0 movs ip, ip, lsr #32 - PAGE_SHIFT beq .cfu_0fupi @@ -343,31 +343,31 @@ USER( ldrt r3, [r1], #4) subs ip, ip, #32 blt .cfu_0rem8lp -.cfu_0cpy8lp: ldmia r1!, {r3 - r6} // Shouldn't fault +.cfu_0cpy8lp: ldmia r1!, {r3 - r6} @ Shouldn't fault stmia r0!, {r3 - r6} - ldmia r1!, {r3 - r6} // Shouldn't fault + ldmia r1!, {r3 - r6} @ Shouldn't fault stmia r0!, {r3 - r6} subs ip, ip, #32 bpl .cfu_0cpy8lp .cfu_0rem8lp: cmn ip, #16 - ldmgeia r1!, {r3 - r6} // Shouldn't fault + ldmgeia r1!, {r3 - r6} @ Shouldn't fault stmgeia r0!, {r3 - r6} tst ip, #8 - ldmneia r1!, {r3 - r4} // Shouldn't fault + ldmneia r1!, {r3 - r4} @ Shouldn't fault stmneia r0!, {r3 - r4} tst ip, #4 - ldrnet r3, [r1], #4 // Shouldn't fault + ldrnet r3, [r1], #4 @ Shouldn't fault strne r3, [r0], #4 ands ip, ip, #3 beq .cfu_0fupi .cfu_0nowords: teq ip, #0 beq .cfu_finished .cfu_nowords: cmp ip, #2 -USER( ldrbt r3, [r1], #1) // May fault +USER( ldrbt r3, [r1], #1) @ May fault strb r3, [r0], #1 -USER( ldrgebt r3, [r1], #1) // May fault +USER( ldrgebt r3, [r1], #1) @ May fault strgeb r3, [r0], #1 -USER( ldrgtbt r3, [r1], #1) // May fault +USER( ldrgtbt r3, [r1], #1) @ May fault strgtb r3, [r0], #1 b .cfu_finished @@ -380,7 +380,7 @@ USER( ldrgtbt r3, [r1], #1) // May fault .cfu_src_not_aligned: bic r1, r1, #3 -USER( ldrt r7, [r1], #4) // May fault +USER( ldrt r7, [r1], #4) @ May fault cmp ip, #2 bgt .cfu_3fupi beq .cfu_2fupi @@ -388,7 +388,7 @@ USER( ldrt r7, [r1], #4) // May fault addmi ip, r2, #4 bmi .cfu_1nowords mov r3, r7, lsr #8 -USER( ldrt r7, [r1], #4) // May fault +USER( ldrt r7, [r1], #4) @ May fault orr r3, r3, r7, lsl #24 str r3, [r0], #4 mov ip, r1, lsl #32 - PAGE_SHIFT @@ -402,7 +402,7 @@ USER( ldrt r7, [r1], #4) // May fault blt .cfu_1rem8lp .cfu_1cpy8lp: mov r3, r7, lsr #8 - ldmia r1!, {r4 - r7} // Shouldn't fault + ldmia r1!, {r4 - r7} @ Shouldn't fault orr r3, r3, r4, lsl #24 mov r4, r4, lsr #8 orr r4, r4, r5, lsl #24 @@ -415,14 +415,14 @@ USER( ldrt r7, [r1], #4) // May fault bpl .cfu_1cpy8lp .cfu_1rem8lp: tst ip, #8 movne r3, r7, lsr #8 - ldmneia r1!, {r4, r7} // Shouldn't fault + ldmneia r1!, {r4, r7} @ Shouldn't fault orrne r3, r3, r4, lsl #24 movne r4, r4, lsr #8 orrne r4, r4, r7, lsl #24 stmneia r0!, {r3 - r4} tst ip, #4 movne r3, r7, lsr #8 -USER( ldrnet r7, [r1], #4) // May fault +USER( ldrnet r7, [r1], #4) @ May fault orrne r3, r3, r7, lsl #24 strne r3, [r0], #4 ands ip, ip, #3 @@ -442,7 +442,7 @@ USER( ldrnet r7, [r1], #4) // May fault addmi ip, r2, #4 bmi .cfu_2nowords mov r3, r7, lsr #16 -USER( ldrt r7, [r1], #4) // May fault +USER( ldrt r7, [r1], #4) @ May fault orr r3, r3, r7, lsl #16 str r3, [r0], #4 mov ip, r1, lsl #32 - PAGE_SHIFT @@ -456,7 +456,7 @@ USER( ldrt r7, [r1], #4) // May fault blt .cfu_2rem8lp .cfu_2cpy8lp: mov r3, r7, lsr #16 - ldmia r1!, {r4 - r7} // Shouldn't fault + ldmia r1!, {r4 - r7} @ Shouldn't fault orr r3, r3, r4, lsl #16 mov r4, r4, lsr #16 orr r4, r4, r5, lsl #16 @@ -469,14 +469,14 @@ USER( ldrt r7, [r1], #4) // May fault bpl .cfu_2cpy8lp .cfu_2rem8lp: tst ip, #8 movne r3, r7, lsr #16 - ldmneia r1!, {r4, r7} // Shouldn't fault + ldmneia r1!, {r4, r7} @ Shouldn't fault orrne r3, r3, r4, lsl #16 movne r4, r4, lsr #16 orrne r4, r4, r7, lsl #16 stmneia r0!, {r3 - r4} tst ip, #4 movne r3, r7, lsr #16 -USER( ldrnet r7, [r1], #4) // May fault +USER( ldrnet r7, [r1], #4) @ May fault orrne r3, r3, r7, lsl #16 strne r3, [r0], #4 ands ip, ip, #3 @@ -488,7 +488,7 @@ USER( ldrnet r7, [r1], #4) // May fault strb r3, [r0], #1 movge r3, r3, lsr #8 strgeb r3, [r0], #1 -USER( ldrgtbt r3, [r1], #0) // May fault +USER( ldrgtbt r3, [r1], #0) @ May fault strgtb r3, [r0], #1 b .cfu_finished @@ -496,7 +496,7 @@ USER( ldrgtbt r3, [r1], #0) // May fault addmi ip, r2, #4 bmi .cfu_3nowords mov r3, r7, lsr #24 -USER( ldrt r7, [r1], #4) // May fault +USER( ldrt r7, [r1], #4) @ May fault orr r3, r3, r7, lsl #8 str r3, [r0], #4 mov ip, r1, lsl #32 - PAGE_SHIFT @@ -510,7 +510,7 @@ USER( ldrt r7, [r1], #4) // May fault blt .cfu_3rem8lp .cfu_3cpy8lp: mov r3, r7, lsr #24 - ldmia r1!, {r4 - r7} // Shouldn't fault + ldmia r1!, {r4 - r7} @ Shouldn't fault orr r3, r3, r4, lsl #8 mov r4, r4, lsr #24 orr r4, r4, r5, lsl #8 @@ -523,14 +523,14 @@ USER( ldrt r7, [r1], #4) // May fault bpl .cfu_3cpy8lp .cfu_3rem8lp: tst ip, #8 movne r3, r7, lsr #24 - ldmneia r1!, {r4, r7} // Shouldn't fault + ldmneia r1!, {r4, r7} @ Shouldn't fault orrne r3, r3, r4, lsl #8 movne r4, r4, lsr #24 orrne r4, r4, r7, lsl #8 stmneia r0!, {r3 - r4} tst ip, #4 movne r3, r7, lsr #24 -USER( ldrnet r7, [r1], #4) // May fault +USER( ldrnet r7, [r1], #4) @ May fault orrne r3, r3, r7, lsl #8 strne r3, [r0], #4 ands ip, ip, #3 @@ -540,7 +540,7 @@ USER( ldrnet r7, [r1], #4) // May fault beq .cfu_finished cmp ip, #2 strb r3, [r0], #1 -USER( ldrget r3, [r1], #0) // May fault +USER( ldrget r3, [r1], #0) @ May fault strgeb r3, [r0], #1 movgt r3, r3, lsr #8 strgtb r3, [r0], #1 @@ -553,7 +553,8 @@ USER( ldrget r3, [r1], #0) // May fault data was copied. */ 9001: ldr r0, [sp], #4 ldr r1, [sp] - bl SYMBOL_NAME(memzero) + teq r1, #0 + blne SYMBOL_NAME(__memzero) LOADREGS(fd,sp!, {r0, r4 - r7, pc}) .previous #endif @@ -597,19 +598,23 @@ USER( strnebt r2, [r0], #1) 9001: LOADREGS(fd,sp!, {r0, pc}) .previous -/* Prototype: int __arch_strlen_user(char *str) +/* Prototype: unsigned long __arch_strnlen_user(const char *str, long n) * Purpose : get length of a string in user memory * Params : str - address of string in user memory - * Returns : length of string *including terminator*, or zero on error + * Returns : length of string *including terminator* + * or zero on exception, or n + 1 if too long */ -ENTRY(__arch_strlen_user) +ENTRY(__arch_strnlen_user) stmfd sp!, {lr} mov r2, r0 1: -USER( ldrbt r1, [r0], #1) - teq r1, #0 +USER( ldrbt r3, [r0], #1) + teq r3, #0 + beq 2f + subs r1, r1, #1 bne 1b - sub r0, r0, r2 + add r0, r0, #1 +2: sub r0, r0, r2 LOADREGS(fd,sp!, {pc}) .section .fixup,"ax" diff --git a/arch/arm/mm/Makefile b/arch/arm/mm/Makefile index 42a92201f..b9bffb7c9 100644 --- a/arch/arm/mm/Makefile +++ b/arch/arm/mm/Makefile @@ -8,7 +8,8 @@ # Note 2! The CFLAGS definition is now in the main makefile... O_TARGET := mm.o -O_OBJS := init.o extable.o fault-$(PROCESSOR).o small_page.o +O_OBJS := init.o extable.o fault-$(PROCESSOR).o mm-$(PROCESSOR).o \ + small_page.o ifeq ($(CONFIG_CPU_26),y) O_OBJS += proc-arm2,3.o @@ -24,13 +25,16 @@ ifeq ($(CONFIG_CPU_32),y) ifeq ($(CONFIG_CPU_SA110),y) P_OBJS += proc-sa110.o endif + ifeq ($(CONFIG_CPU_SA1100),y) + P_OBJS += proc-sa110.o + endif O_OBJS += mm-$(MACHINE).o ioremap.o $(sort $(P_OBJS)) endif include $(TOPDIR)/Rules.make -%.o: %.S - $(CC) $(CFLAGS) -D__ASSEMBLY__ -c -o $@ $< +.S.o: + $(CC) -D__ASSEMBLY__ $(AFLAGS) $(AFLAGS_$@) -traditional -c -o $*.o $< # Special dependencies fault-armv.o: fault-common.c diff --git a/arch/arm/mm/fault-armo.c b/arch/arm/mm/fault-armo.c index c51980771..19a1fff7c 100644 --- a/arch/arm/mm/fault-armo.c +++ b/arch/arm/mm/fault-armo.c @@ -23,61 +23,12 @@ #define FAULT_CODE_FORCECOW 0x80 #define FAULT_CODE_PREFETCH 0x04 #define FAULT_CODE_WRITE 0x02 -#define FAULT_CODE_USER 0x01 #define DO_COW(m) ((m) & (FAULT_CODE_WRITE|FAULT_CODE_FORCECOW)) #define READ_FAULT(m) (!((m) & FAULT_CODE_WRITE)) #include "fault-common.c" -static void *alloc_table(int size, int prio) -{ - if (size != 128) - printk("invalid table size\n"); - return (void *)get_page_8k(prio); -} - -void free_table(void *table) -{ - free_page_8k((unsigned long)table); -} - -pgd_t *get_pgd_slow(void) -{ - pgd_t *pgd = (pgd_t *)alloc_table(PTRS_PER_PGD * BYTES_PER_PTR, GFP_KERNEL); - pgd_t *init; - - if (pgd) { - init = pgd_offset(&init_mm, 0); - memzero(pgd, USER_PTRS_PER_PGD * BYTES_PER_PTR); - memcpy(pgd + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD, - (PTRS_PER_PGD - USER_PTRS_PER_PGD) * BYTES_PER_PTR); - } - return pgd; -} - -pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset) -{ - pte_t *pte; - - pte = (pte_t *)alloc_table(PTRS_PER_PTE * BYTES_PER_PTR, GFP_KERNEL); - if (pmd_none(*pmd)) { - if (pte) { - memzero(pte, PTRS_PER_PTE * BYTES_PER_PTR); - set_pmd(pmd, mk_pmd(pte)); - return pte + offset; - } - set_pmd(pmd, mk_pmd(BAD_PAGETABLE)); - return NULL; - } - free_table((void *)pte); - if (pmd_bad(*pmd)) { - __bad_pmd(pmd); - return NULL; - } - return (pte_t *) pmd_page(*pmd) + offset; -} - /* * Handle a data abort. Note that we have to handle a range of addresses * on ARM2/3 for ldm. If both pages are zero-mapped, then we have to force @@ -96,11 +47,11 @@ asmlinkage int do_PrefetchAbort(unsigned long addr, struct pt_regs *regs) { #if 0 - if (the memc mapping for this page exists - can check now...) { + if (the memc mapping for this page exists) { printk ("Page in, but got abort (undefined instruction?)\n"); return 0; } #endif - do_page_fault(addr, FAULT_CODE_USER|FAULT_CODE_PREFETCH, regs); + do_page_fault(addr, FAULT_CODE_PREFETCH, regs); return 1; } diff --git a/arch/arm/mm/fault-armv.c b/arch/arm/mm/fault-armv.c index 0c5582fb7..8c2df7f5e 100644 --- a/arch/arm/mm/fault-armv.c +++ b/arch/arm/mm/fault-armv.c @@ -25,104 +25,12 @@ #include <asm/unaligned.h> #define FAULT_CODE_READ 0x02 -#define FAULT_CODE_USER 0x01 #define DO_COW(m) (!((m) & FAULT_CODE_READ)) #define READ_FAULT(m) ((m) & FAULT_CODE_READ) #include "fault-common.c" -/* - * need to get a 16k page for level 1 - */ -pgd_t *get_pgd_slow(void) -{ - pgd_t *pgd = (pgd_t *)__get_free_pages(GFP_KERNEL,2); - pgd_t *init; - pmd_t *new_pmd; - - if (pgd) { - init = pgd_offset(&init_mm, 0); - memzero(pgd, USER_PTRS_PER_PGD * BYTES_PER_PTR); - memcpy(pgd + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD, - (PTRS_PER_PGD - USER_PTRS_PER_PGD) * BYTES_PER_PTR); - clean_cache_area(pgd, PTRS_PER_PGD * BYTES_PER_PTR); - - /* - * On ARM, first page must always be allocated - */ - if (!pmd_alloc(pgd, 0)) - goto nomem; - else { - pmd_t *old_pmd = pmd_offset(init, 0); - new_pmd = pmd_offset(pgd, 0); - - if (!pte_alloc(new_pmd, 0)) - goto nomem_pmd; - else { - pte_t *new_pte = pte_offset(new_pmd, 0); - pte_t *old_pte = pte_offset(old_pmd, 0); - - set_pte (new_pte, *old_pte); - } - } - } - return pgd; - -nomem_pmd: - pmd_free(new_pmd); -nomem: - return NULL; -} - -pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset) -{ - pte_t *pte; - - pte = (pte_t *)get_page_2k(GFP_KERNEL); - if (pmd_none(*pmd)) { - if (pte) { - memzero(pte, 2 * PTRS_PER_PTE * BYTES_PER_PTR); - clean_cache_area(pte, PTRS_PER_PTE * BYTES_PER_PTR); - pte += PTRS_PER_PTE; - set_pmd(pmd, mk_user_pmd(pte)); - return pte + offset; - } - set_pmd(pmd, mk_user_pmd(BAD_PAGETABLE)); - return NULL; - } - free_page_2k((unsigned long)pte); - if (pmd_bad(*pmd)) { - __bad_pmd(pmd); - return NULL; - } - return (pte_t *) pmd_page(*pmd) + offset; -} - -pte_t *get_pte_kernel_slow(pmd_t *pmd, unsigned long offset) -{ - pte_t *pte; - - pte = (pte_t *)get_page_2k(GFP_KERNEL); - if (pmd_none(*pmd)) { - if (pte) { - memzero(pte, 2 * PTRS_PER_PTE * BYTES_PER_PTR); - clean_cache_area(pte, PTRS_PER_PTE * BYTES_PER_PTR); - pte += PTRS_PER_PTE; - set_pmd(pmd, mk_kernel_pmd(pte)); - return pte + offset; - } - set_pmd(pmd, mk_kernel_pmd(BAD_PAGETABLE)); - return NULL; - } - free_page_2k((unsigned long)pte); - if (pmd_bad(*pmd)) { - __bad_pmd_kernel(pmd); - return NULL; - } - return (pte_t *) pmd_page(*pmd) + offset; -} - #ifdef DEBUG static int sp_valid(unsigned long *sp) { @@ -406,6 +314,10 @@ do_alignment_exception(struct pt_regs *regs) #endif +#define BUG_PROC_MSG \ + "Buggy processor (%08X), trying to continue.\n" \ + "Please read http://www.arm.linux.org.uk/state.html for more information" + asmlinkage void do_DataAbort(unsigned long addr, int fsr, int error_code, struct pt_regs *regs) { @@ -416,17 +328,11 @@ do_DataAbort(unsigned long addr, int fsr, int error_code, struct pt_regs *regs) /* * I want statistical information on this problem! */ - printk(KERN_ERR "Buggy processor (%08X), " - "trying to continue.\n" - "Please send contents of /proc/cpuinfo " - "and this message to linux@arm.linux.org.uk", - fsr); + printk(KERN_ERR BUG_PROC_MSG, fsr); first = 0; } return; } - - error_code |= FAULT_CODE_USER; } #define DIE(signr,nam)\ @@ -522,6 +428,6 @@ do_DataAbort(unsigned long addr, int fsr, int error_code, struct pt_regs *regs) asmlinkage int do_PrefetchAbort(unsigned long addr, struct pt_regs *regs) { - do_page_fault(addr, FAULT_CODE_USER|FAULT_CODE_READ, regs); + do_page_fault(addr, FAULT_CODE_READ, regs); return 1; } diff --git a/arch/arm/mm/fault-common.c b/arch/arm/mm/fault-common.c index a536515af..c87fa760e 100644 --- a/arch/arm/mm/fault-common.c +++ b/arch/arm/mm/fault-common.c @@ -30,7 +30,7 @@ void __bad_pmd_kernel(pmd_t *pmd) * This is useful to dump out the page tables associated with * 'addr' in mm 'mm'. */ -void show_pte(struct mm_struct *mm, unsigned long addr) +static void show_pte(struct mm_struct *mm, unsigned long addr) { pgd_t *pgd; @@ -62,7 +62,9 @@ void show_pte(struct mm_struct *mm, unsigned long addr) pte = pte_offset(pmd, addr); printk(", *pte = %08lx", pte_val(*pte)); +#ifdef CONFIG_CPU_32 printk(", *ppte = %08lx", pte_val(pte[-PTRS_PER_PTE])); +#endif } while(0); printk("\n"); @@ -73,7 +75,7 @@ void show_pte(struct mm_struct *mm, unsigned long addr) * terminate things with extreme prejudice. */ static void -kernel_page_fault(unsigned long addr, int mode, struct pt_regs *regs, +kernel_page_fault(unsigned long addr, int write_access, struct pt_regs *regs, struct task_struct *tsk, struct mm_struct *mm) { char *reason; @@ -90,7 +92,7 @@ kernel_page_fault(unsigned long addr, int mode, struct pt_regs *regs, printk(KERN_ALERT "pgd = %p\n", mm->pgd); show_pte(mm, addr); - die("Oops", regs, mode); + die("Oops", regs, write_access); do_exit(SIGKILL); } @@ -153,7 +155,7 @@ bad_area: up(&mm->mmap_sem); /* User mode accesses just cause a SIGSEGV */ - if (mode & FAULT_CODE_USER) { + if (user_mode(regs)) { tsk->thread.error_code = mode; tsk->thread.trap_no = 14; #ifdef CONFIG_DEBUG_USER @@ -194,7 +196,7 @@ do_sigbus: force_sig(SIGBUS, tsk); /* Kernel mode? Handle exceptions or die */ - if (!(mode & FAULT_CODE_USER)) + if (!user_mode(regs)) goto no_context; } diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index 4fa1ca498..115cec885 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -1,7 +1,7 @@ /* * linux/arch/arm/mm/init.c * - * Copyright (C) 1995, 1996 Russell King + * Copyright (C) 1995-1999 Russell King */ #include <linux/config.h> @@ -15,6 +15,7 @@ #include <linux/mman.h> #include <linux/mm.h> #include <linux/swap.h> +#include <linux/swapctl.h> #include <linux/smp.h> #include <linux/init.h> #ifdef CONFIG_BLK_DEV_INITRD @@ -26,14 +27,18 @@ #include <asm/pgtable.h> #include <asm/dma.h> #include <asm/hardware.h> -#include <asm/proc/mm-init.h> + +#include "map.h" pgd_t swapper_pg_dir[PTRS_PER_PGD]; #ifndef CONFIG_NO_PGT_CACHE struct pgtable_cache_struct quicklists; #endif -extern char _etext, _stext, _edata, __bss_start, _end; +extern unsigned long free_area_init(unsigned long, unsigned long); +extern void show_net_buffers(void); + +extern char _etext, _text, _edata, __bss_start, _end; extern char __init_begin, __init_end; int do_check_pgt_cache(int low, int high) @@ -67,7 +72,6 @@ int do_check_pgt_cache(int low, int high) * ZERO_PAGE is a special page that is used for zero-initialized * data and COW. */ -#if PTRS_PER_PTE != 1 pte_t *empty_bad_page_table; pte_t *__bad_pagetable(void) @@ -81,7 +85,6 @@ pte_t *__bad_pagetable(void) return empty_bad_page_table; } -#endif unsigned long *empty_zero_page; unsigned long *empty_bad_page; @@ -94,27 +97,39 @@ pte_t __bad_page(void) void show_mem(void) { - extern void show_net_buffers(void); - int i,free = 0,total = 0,reserved = 0; - int shared = 0; + int free = 0, total = 0, reserved = 0; + int shared = 0, cached = 0; + struct page *page, *end; printk("Mem-info:\n"); show_free_areas(); printk("Free swap: %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10)); - i = MAP_NR(high_memory); - while (i-- > 0) { + for (page = mem_map, end = mem_map + max_mapnr; + page < end; page++) { + if (PageSkip(page)) { + if (page->next_hash < page) + break; + page = page->next_hash; + } total++; - if (PageReserved(mem_map+i)) + if (PageReserved(page)) reserved++; - else if (!atomic_read(&mem_map[i].count)) + else if (PageSwapCache(page)) + cached++; + else if (!atomic_read(&page->count)) free++; else - shared += atomic_read(&mem_map[i].count) - 1; + shared += atomic_read(&page->count) - 1; } - printk("%d pages of RAM\n",total); - printk("%d free pages\n",free); - printk("%d reserved pages\n",reserved); - printk("%d pages shared\n",shared); + printk("%d pages of RAM\n", total); + printk("%d free pages\n", free); + printk("%d reserved pages\n", reserved); + printk("%d pages shared\n", shared); + printk("%d pages swap cached\n", cached); +#ifndef CONFIG_NO_PGT_CACHE + printk("%ld page tables cached\n", pgtable_cache_size); +#endif + show_buffers(); #ifdef CONFIG_NET show_net_buffers(); #endif @@ -125,25 +140,24 @@ void show_mem(void) */ unsigned long __init paging_init(unsigned long start_mem, unsigned long end_mem) { - extern unsigned long free_area_init(unsigned long, unsigned long); - start_mem = PAGE_ALIGN(start_mem); + empty_zero_page = (unsigned long *)start_mem; + memzero(empty_zero_page, PAGE_SIZE); start_mem += PAGE_SIZE; + empty_bad_page = (unsigned long *)start_mem; start_mem += PAGE_SIZE; -#if PTRS_PER_PTE != 1 + #ifdef CONFIG_CPU_32 start_mem += PTRS_PER_PTE * BYTES_PER_PTR; #endif empty_bad_page_table = (pte_t *)start_mem; start_mem += PTRS_PER_PTE * BYTES_PER_PTR; -#endif - memzero (empty_zero_page, PAGE_SIZE); - start_mem = setup_pagetables (start_mem, end_mem); + + start_mem = setup_page_tables(start_mem, end_mem); flush_tlb_all(); - update_memc_all(); end_mem &= PAGE_MASK; high_memory = (void *)end_mem; @@ -151,6 +165,31 @@ unsigned long __init paging_init(unsigned long start_mem, unsigned long end_mem) return free_area_init(start_mem, end_mem); } +static inline void free_unused_mem_map(void) +{ + struct page *page, *end; + + end = mem_map + max_mapnr; + + for (page = mem_map; page < end; page++) { + unsigned long low, high; + + if (!PageSkip(page)) + continue; + + low = PAGE_ALIGN((unsigned long)(page + 1)); + if (page->next_hash < page) + high = ((unsigned long)end) & PAGE_MASK; + else + high = ((unsigned long)page->next_hash) & PAGE_MASK; + + while (low < high) { + clear_bit(PG_reserved, &mem_map[MAP_NR(low)].flags); + low += PAGE_SIZE; + } + } +} + /* * mem_init() marks the free areas in the mem_map and tells us how much * memory is free. This is done after various parts of the system have @@ -158,28 +197,48 @@ unsigned long __init paging_init(unsigned long start_mem, unsigned long end_mem) */ void __init mem_init(unsigned long start_mem, unsigned long end_mem) { - extern void sound_init(void); int codepages = 0; int reservedpages = 0; int datapages = 0; - int initpages = 0; + int initpages = 0, i, min_nr; unsigned long tmp; - end_mem &= PAGE_MASK; - high_memory = (void *)end_mem; - max_mapnr = num_physpages = MAP_NR(end_mem); + end_mem &= PAGE_MASK; + high_memory = (void *)end_mem; + max_mapnr = MAP_NR(end_mem); + num_physpages = 0; + + /* setup address validity bitmap */ + start_mem = create_mem_holes(start_mem, end_mem); + + start_mem = PAGE_ALIGN(start_mem); /* mark usable pages in the mem_map[] */ - mark_usable_memory_areas(&start_mem, end_mem); + mark_usable_memory_areas(start_mem, end_mem); + + /* free unused mem_map[] entries */ + free_unused_mem_map(); #define BETWEEN(w,min,max) ((w) >= (unsigned long)(min) && \ (w) < (unsigned long)(max)) for (tmp = PAGE_OFFSET; tmp < end_mem ; tmp += PAGE_SIZE) { + if (PageSkip(mem_map+MAP_NR(tmp))) { + unsigned long next; + + next = mem_map[MAP_NR(tmp)].next_hash - mem_map; + + next = (next << PAGE_SHIFT) + PAGE_OFFSET; + + if (next < tmp || next >= end_mem) + break; + tmp = next; + } + num_physpages++; if (PageReserved(mem_map+MAP_NR(tmp))) { if (BETWEEN(tmp, &__init_begin, &__init_end)) initpages++; - else if (BETWEEN(tmp, &_stext, &_etext)) + else if (BETWEEN(tmp, &_text, &_etext)) codepages++; else if (BETWEEN(tmp, &_etext, &_edata)) datapages++; @@ -200,11 +259,24 @@ void __init mem_init(unsigned long start_mem, unsigned long end_mem) printk ("Memory: %luk/%luM available (%dk code, %dk reserved, %dk data, %dk init)\n", (unsigned long) nr_free_pages << (PAGE_SHIFT-10), - max_mapnr >> (20 - PAGE_SHIFT), - codepages << (PAGE_SHIFT-10), + num_physpages >> (20 - PAGE_SHIFT), + codepages << (PAGE_SHIFT-10), reservedpages << (PAGE_SHIFT-10), - datapages << (PAGE_SHIFT-10), - initpages << (PAGE_SHIFT-10)); + datapages << (PAGE_SHIFT-10), + initpages << (PAGE_SHIFT-10)); + + i = nr_free_pages >> 7; + if (PAGE_SIZE < 32768) + min_nr = 10; + else + min_nr = 2; + if (i < min_nr) + i = min_nr; + if (i > 256) + i = 256; + freepages.min = i; + freepages.low = i * 2; + freepages.high = i * 3; #ifdef CONFIG_CPU_26 if (max_mapnr <= 128) { @@ -240,14 +312,16 @@ void free_initmem (void) #ifdef CONFIG_FOOTBRIDGE { - extern int __netwinder_begin, __netwinder_end, __ebsa285_begin, __ebsa285_end; + extern int __netwinder_begin, __netwinder_end, + __ebsa285_begin, __ebsa285_end; if (!machine_is_netwinder()) free_area((unsigned long)(&__netwinder_begin), (unsigned long)(&__netwinder_end), "netwinder"); - if (!machine_is_ebsa285() && !machine_is_cats() && !machine_is_co285()) + if (!machine_is_ebsa285() && !machine_is_cats() && + !machine_is_co285()) free_area((unsigned long)(&__ebsa285_begin), (unsigned long)(&__ebsa285_end), "ebsa285/cats"); @@ -259,22 +333,28 @@ void free_initmem (void) void si_meminfo(struct sysinfo *val) { - int i; + struct page *page, *end; - i = MAP_NR(high_memory); val->totalram = 0; val->sharedram = 0; val->freeram = nr_free_pages << PAGE_SHIFT; val->bufferram = atomic_read(&buffermem); - while (i-- > 0) { - if (PageReserved(mem_map+i)) + for (page = mem_map, end = mem_map + max_mapnr; + page < end; page++) { + if (PageSkip(page)) { + if (page->next_hash < page) + break; + page = page->next_hash; + } + if (PageReserved(page)) continue; val->totalram++; - if (!atomic_read(&mem_map[i].count)) + if (!atomic_read(&page->count)) continue; - val->sharedram += atomic_read(&mem_map[i].count) - 1; + val->sharedram += atomic_read(&page->count) - 1; } val->totalram <<= PAGE_SHIFT; val->sharedram <<= PAGE_SHIFT; + val->totalbig = 0; + val->freebig = 0; } - diff --git a/arch/arm/mm/ioremap.c b/arch/arm/mm/ioremap.c index e4c84fa71..141644ae2 100644 --- a/arch/arm/mm/ioremap.c +++ b/arch/arm/mm/ioremap.c @@ -27,8 +27,12 @@ * FIFO. Unfortunately, it is not possible to tell the DC21285 to * flush this - flushing the area causes the bus to lock. */ - +#include <linux/errno.h> +#include <linux/mm.h> #include <linux/vmalloc.h> + +#include <asm/page.h> +#include <asm/pgtable.h> #include <asm/io.h> static inline void remap_area_pte(pte_t * pte, unsigned long address, unsigned long size, @@ -145,5 +149,5 @@ void * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flag void __iounmap(void *addr) { - return vfree((void *) (PAGE_MASK & (unsigned long) addr)); + vfree((void *) (PAGE_MASK & (unsigned long) addr)); } diff --git a/arch/arm/mm/map.h b/arch/arm/mm/map.h new file mode 100644 index 000000000..a1fc92b2c --- /dev/null +++ b/arch/arm/mm/map.h @@ -0,0 +1,32 @@ +/* + * linux/arch/arm/mm/map.h + * + * Copyright (C) 1999 Russell King + * + * Page table mapping constructs and function prototypes + */ +struct map_desc { + unsigned long virtual; + unsigned long physical; + unsigned long length; + int domain:4, + prot_read:1, + prot_write:1, + cacheable:1, + bufferable:1; +}; + +struct mem_desc { + unsigned long virt_start; + unsigned long virt_end; +}; + +extern struct map_desc io_desc[]; +extern unsigned int io_desc_size; +extern struct mem_desc mem_desc[]; +extern unsigned int mem_desc_size; + +extern void mark_usable_memory_areas(unsigned long start, unsigned long end); +extern unsigned long create_mem_holes(unsigned long start, unsigned long end); +extern unsigned long setup_page_tables(unsigned long start, unsigned long end); + diff --git a/arch/arm/mm/mm-armo.c b/arch/arm/mm/mm-armo.c new file mode 100644 index 000000000..55245f4e8 --- /dev/null +++ b/arch/arm/mm/mm-armo.c @@ -0,0 +1,170 @@ +/* + * arch/arm/mm/mm-armo.c + * + * Page table sludge for older ARM processor architectures. + * + * Copyright (C) 1998-1999 Russell King + */ +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/init.h> + +#include <asm/pgtable.h> +#include <asm/page.h> +#include <asm/arch/memory.h> + +#include "map.h" + +#define MEMC_TABLE_SIZE (256*sizeof(unsigned long)) +#define PGD_TABLE_SIZE (PTRS_PER_PGD * BYTES_PER_PTR) + +/* + * FIXME: the following over-allocates by 6400% + */ +static inline void *alloc_table(int size, int prio) +{ + if (size != 128) + printk("invalid table size\n"); + return (void *)get_page_8k(prio); +} + +/* + * Allocate a page table. Note that we place the MEMC + * table before the page directory. This means we can + * easily get to both tightly-associated data structures + * with a single pointer. This function is slightly + * better - it over-allocates by only 711% + */ +static inline void *alloc_pgd_table(int priority) +{ + unsigned long pg8k; + + pg8k = get_page_8k(priority); + if (pg8k) + pg8k += MEMC_TABLE_SIZE; + + return (void *)pg8k; +} + +void free_table(void *table) +{ + unsigned long tbl = (unsigned long)table; + + tbl &= ~8191; + free_page_8k(tbl); +} + +pgd_t *get_pgd_slow(void) +{ + pgd_t *pgd = (pgd_t *)alloc_pgd_table(GFP_KERNEL); + pmd_t *new_pmd; + + if (pgd) { + pgd_t *init = pgd_offset(&init_mm, 0); + + memzero(pgd, USER_PTRS_PER_PGD * BYTES_PER_PTR); + memcpy(pgd + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD, + (PTRS_PER_PGD - USER_PTRS_PER_PGD) * BYTES_PER_PTR); + + /* + * On ARM, first page must always be allocated + */ + if (!pmd_alloc(pgd, 0)) + goto nomem; + else { + pmd_t *old_pmd = pmd_offset(init, 0); + new_pmd = pmd_offset(pgd, 0); + + if (!pte_alloc(new_pmd, 0)) + goto nomem_pmd; + else { + pte_t *new_pte = pte_offset(new_pmd, 0); + pte_t *old_pte = pte_offset(old_pmd, 0); + + set_pte (new_pte, *old_pte); + } + } + /* update MEMC tables */ + cpu_memc_update_all(pgd); + } + return pgd; + +nomem_pmd: + pmd_free(new_pmd); +nomem: + free_table(pgd); + return NULL; +} + +pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset) +{ + pte_t *pte; + + pte = (pte_t *)alloc_table(PTRS_PER_PTE * BYTES_PER_PTR, GFP_KERNEL); + if (pmd_none(*pmd)) { + if (pte) { + memzero(pte, PTRS_PER_PTE * BYTES_PER_PTR); + set_pmd(pmd, mk_pmd(pte)); + return pte + offset; + } + set_pmd(pmd, mk_pmd(BAD_PAGETABLE)); + return NULL; + } + free_table((void *)pte); + if (pmd_bad(*pmd)) { + __bad_pmd(pmd); + return NULL; + } + return (pte_t *) pmd_page(*pmd) + offset; +} + +/* + * This contains the code to setup the memory map on an ARM2/ARM250/ARM3 + * machine. This is both processor & architecture specific, and requires + * some more work to get it to fit into our separate processor and + * architecture structure. + */ +int page_nr; + +#define PTE_SIZE (PTRS_PER_PTE * BYTES_PER_PTR) + +static inline void setup_swapper_dir (int index, pte_t *ptep) +{ + set_pmd (pmd_offset (swapper_pg_dir + index, 0), mk_pmd (ptep)); +} + +unsigned long __init +setup_page_tables(unsigned long start_mem, unsigned long end_mem) +{ + unsigned int i; + union { unsigned long l; pte_t *pte; } u; + + page_nr = MAP_NR(end_mem); + + /* map in pages for (0x0000 - 0x8000) */ + u.l = ((start_mem + (PTE_SIZE-1)) & ~(PTE_SIZE-1)); + start_mem = u.l + PTE_SIZE; + memzero (u.pte, PTE_SIZE); + u.pte[0] = mk_pte(PAGE_OFFSET + 491520, PAGE_READONLY); + setup_swapper_dir (0, u.pte); + + for (i = 1; i < PTRS_PER_PGD; i++) + pgd_val(swapper_pg_dir[i]) = 0; + + return start_mem; +} + +unsigned long __init +create_mem_holes(unsigned long start, unsigned long end) +{ + return start; +} + +void __init +mark_usable_memory_areas(unsigned long start_mem, unsigned long end_mem) +{ + while (start_mem < end_mem) { + clear_bit(PG_reserved, &mem_map[MAP_NR(start_mem)].flags); + start_mem += PAGE_SIZE; + } +} diff --git a/arch/arm/mm/mm-armv.c b/arch/arm/mm/mm-armv.c index 02890c55b..d52c21cc4 100644 --- a/arch/arm/mm/mm-armv.c +++ b/arch/arm/mm/mm-armv.c @@ -1,72 +1,375 @@ /* * arch/arm/mm/mm-armv.c * - * Common routines for ARM v3 and v4 architectures. + * Page table sludge for ARM v3 and v4 processor architectures. * - * Copyright (C) 1998 Russell King - * - * Do not compile this file directly! + * Copyright (C) 1998-1999 Russell King */ +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/init.h> -#ifndef MAPPING -#error MAPPING not defined - do not compile this file individually -#endif +#include <asm/pgtable.h> +#include <asm/page.h> +#include <asm/io.h> + +#include "map.h" + +unsigned long *valid_addr_bitmap; + +/* + * need to get a 16k page for level 1 + */ +pgd_t *get_pgd_slow(void) +{ + pgd_t *pgd = (pgd_t *)__get_free_pages(GFP_KERNEL,2); + pmd_t *new_pmd; + + if (pgd) { + pgd_t *init = pgd_offset(&init_mm, 0); + + memzero(pgd, USER_PTRS_PER_PGD * BYTES_PER_PTR); + memcpy(pgd + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD, + (PTRS_PER_PGD - USER_PTRS_PER_PGD) * BYTES_PER_PTR); + clean_cache_area(pgd, PTRS_PER_PGD * BYTES_PER_PTR); + + /* + * On ARM, first page must always be allocated + */ + if (!pmd_alloc(pgd, 0)) + goto nomem; + else { + pmd_t *old_pmd = pmd_offset(init, 0); + new_pmd = pmd_offset(pgd, 0); + + if (!pte_alloc(new_pmd, 0)) + goto nomem_pmd; + else { + pte_t *new_pte = pte_offset(new_pmd, 0); + pte_t *old_pte = pte_offset(old_pmd, 0); + + set_pte (new_pte, *old_pte); + } + } + } + return pgd; -static const struct mapping { - unsigned long virtual; - unsigned long physical; - unsigned long length; - int domain:4, - prot_read:1, - prot_write:1; -} mapping[] __initdata = { - MAPPING +nomem_pmd: + pmd_free(new_pmd); +nomem: + free_pages((unsigned long)pgd, 2); + return NULL; +} + +pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset) +{ + pte_t *pte; + + pte = (pte_t *)get_page_2k(GFP_KERNEL); + if (pmd_none(*pmd)) { + if (pte) { + memzero(pte, 2 * PTRS_PER_PTE * BYTES_PER_PTR); + clean_cache_area(pte, PTRS_PER_PTE * BYTES_PER_PTR); + pte += PTRS_PER_PTE; + set_pmd(pmd, mk_user_pmd(pte)); + return pte + offset; + } + set_pmd(pmd, mk_user_pmd(BAD_PAGETABLE)); + return NULL; + } + free_page_2k((unsigned long)pte); + if (pmd_bad(*pmd)) { + __bad_pmd(pmd); + return NULL; + } + return (pte_t *) pmd_page(*pmd) + offset; +} + +pte_t *get_pte_kernel_slow(pmd_t *pmd, unsigned long offset) +{ + pte_t *pte; + + pte = (pte_t *)get_page_2k(GFP_KERNEL); + if (pmd_none(*pmd)) { + if (pte) { + memzero(pte, 2 * PTRS_PER_PTE * BYTES_PER_PTR); + clean_cache_area(pte, PTRS_PER_PTE * BYTES_PER_PTR); + pte += PTRS_PER_PTE; + set_pmd(pmd, mk_kernel_pmd(pte)); + return pte + offset; + } + set_pmd(pmd, mk_kernel_pmd(BAD_PAGETABLE)); + return NULL; + } + free_page_2k((unsigned long)pte); + if (pmd_bad(*pmd)) { + __bad_pmd_kernel(pmd); + return NULL; + } + return (pte_t *) pmd_page(*pmd) + offset; +} + +/* + * Create a SECTION PGD between VIRT and PHYS in domain + * DOMAIN with protection PROT + */ +static inline void +alloc_init_section(unsigned long virt, unsigned long phys, int prot) +{ + pmd_t pmd; + + pmd_val(pmd) = phys | prot; + + set_pmd(pmd_offset(pgd_offset_k(virt), virt), pmd); +} + +/* + * Add a PAGE mapping between VIRT and PHYS in domain + * DOMAIN with protection PROT. Note that due to the + * way we map the PTEs, we must allocate two PTE_SIZE'd + * blocks - one for the Linux pte table, and one for + * the hardware pte table. + */ +static inline void +alloc_init_page(unsigned long *mem, unsigned long virt, unsigned long phys, int domain, int prot) +{ + pmd_t *pmdp; + pte_t *ptep; + + pmdp = pmd_offset(pgd_offset_k(virt), virt); + +#define PTE_SIZE (PTRS_PER_PTE * BYTES_PER_PTR) + + if (pmd_none(*pmdp)) { + unsigned long memory = *mem; + + memory = (memory + PTE_SIZE - 1) & ~(PTE_SIZE - 1); + + ptep = (pte_t *)memory; + memzero(ptep, PTE_SIZE); + memory += PTE_SIZE; + + ptep = (pte_t *)memory; + memzero(ptep, PTE_SIZE); + + set_pmd(pmdp, __mk_pmd(ptep, PMD_TYPE_TABLE | PMD_DOMAIN(domain))); + + *mem = memory + PTE_SIZE; + } + +#undef PTE_SIZE + + ptep = pte_offset(pmdp, virt); + + set_pte(ptep, mk_pte_phys(phys, __pgprot(prot))); +} + +/* + * Clear any PGD mapping. On a two-level page table system, + * the clearance is done by the middle-level functions (pmd) + * rather than the top-level (pgd) functions. + */ +static inline void +free_init_section(unsigned long virt) +{ + pmd_clear(pmd_offset(pgd_offset_k(virt), virt)); +} + +/* + * Create the page directory entries and any necessary + * page tables for the mapping specified by `md'. We + * are able to cope here with varying sizes and address + * offsets, and we take full advantage of sections. + */ +static void __init +create_mapping(unsigned long *mem_ptr, struct map_desc *md) +{ + unsigned long virt, length; + int prot_sect, prot_pte; + long off; + + prot_pte = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY | + (md->prot_read ? L_PTE_USER : 0) | + (md->prot_write ? L_PTE_WRITE : 0) | + (md->cacheable ? L_PTE_CACHEABLE : 0) | + (md->bufferable ? L_PTE_BUFFERABLE : 0); + + prot_sect = PMD_TYPE_SECT | PMD_DOMAIN(md->domain) | + (md->prot_read ? PMD_SECT_AP_READ : 0) | + (md->prot_write ? PMD_SECT_AP_WRITE : 0) | + (md->cacheable ? PMD_SECT_CACHEABLE : 0) | + (md->bufferable ? PMD_SECT_BUFFERABLE : 0); + + virt = md->virtual; + off = md->physical - virt; + length = md->length; + + while ((virt & 1048575 || (virt + off) & 1048575) && length >= PAGE_SIZE) { + alloc_init_page(mem_ptr, virt, virt + off, md->domain, prot_pte); + + virt += PAGE_SIZE; + length -= PAGE_SIZE; + } + + while (length >= PGDIR_SIZE) { + alloc_init_section(virt, virt + off, prot_sect); + + virt += PGDIR_SIZE; + length -= PGDIR_SIZE; + } + + while (length >= PAGE_SIZE) { + alloc_init_page(mem_ptr, virt, virt + off, md->domain, prot_pte); + + virt += PAGE_SIZE; + length -= PAGE_SIZE; + } +} + +/* + * Initial boot-time mapping. This covers just the + * zero page, kernel and the flush area. NB: it + * must be sorted by virtual address, and no + * virtual address overlaps. + * init_map[2..4] are for architectures with small + * amounts of banked memory. + */ +static struct map_desc init_map[] __initdata = { + { 0, 0, PAGE_SIZE, DOMAIN_USER, 0, 0, 1, 0 }, /* zero page */ + { 0, 0, 0, DOMAIN_KERNEL, 0, 1, 1, 1 }, /* kernel memory */ + { 0, 0, 0, DOMAIN_KERNEL, 0, 1, 1, 1 }, + { 0, 0, 0, DOMAIN_KERNEL, 0, 1, 1, 1 }, + { 0, 0, 0, DOMAIN_KERNEL, 0, 1, 1, 1 }, + { 0, 0, PGDIR_SIZE, DOMAIN_KERNEL, 1, 0, 1, 1 }, /* cache flush 1 */ + { 0, 0, 0, DOMAIN_KERNEL, 1, 0, 1, 0 } /* cache flush 2 */ }; -#define SIZEOFMAP (sizeof(mapping) / sizeof(mapping[0])) +#define NR_INIT_MAPS (sizeof(init_map) / sizeof(init_map[0])) -unsigned long __init setup_io_pagetables(unsigned long start_mem) +unsigned long __init +setup_page_tables(unsigned long start_mem, unsigned long end_mem) { - const struct mapping *mp; - int i; - - for (i = 0, mp = mapping; i < SIZEOFMAP; i++, mp++) { - unsigned long virtual, physical, length; - int prot; - - virtual = mp->virtual; - physical = mp->physical; - length = mp->length; - prot = (mp->prot_read ? L_PTE_USER : 0) | (mp->prot_write ? L_PTE_WRITE : 0) - | L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY; - - while ((virtual & 1048575 || physical & 1048575) && length >= PAGE_SIZE) { - alloc_init_page(&start_mem, virtual, physical, mp->domain, prot); - length -= PAGE_SIZE; - virtual += PAGE_SIZE; - physical += PAGE_SIZE; + unsigned long address = 0; + int idx = 0; + + /* + * Correct the above mappings + */ + init_map[0].physical = + init_map[1].physical = __virt_to_phys(PAGE_OFFSET); + init_map[1].virtual = PAGE_OFFSET; + init_map[1].length = end_mem - PAGE_OFFSET; + init_map[5].physical = FLUSH_BASE_PHYS; + init_map[5].virtual = FLUSH_BASE; +#ifdef FLUSH_BASE_MINICACHE + init_map[6].physical = FLUSH_BASE_PHYS + PGDIR_SIZE; + init_map[6].virtual = FLUSH_BASE_MINICACHE; + init_map[6].length = PGDIR_SIZE; +#endif + + /* + * Firstly, go through the initial mappings, + * but clear out any pgdir entries that are + * not in the description. + */ + do { + if (address < init_map[idx].virtual || idx == NR_INIT_MAPS) { + free_init_section(address); + address += PGDIR_SIZE; + } else { + create_mapping(&start_mem, init_map + idx); + + address = init_map[idx].virtual + init_map[idx].length; + address = (address + PGDIR_SIZE - 1) & PGDIR_MASK; + + do { + idx += 1; + } while (init_map[idx].length == 0 && idx < NR_INIT_MAPS); } + } while (address != 0); + + /* + * Now, create the architecture specific mappings + */ + for (idx = 0; idx < io_desc_size; idx++) + create_mapping(&start_mem, io_desc + idx); + + flush_cache_all(); + + return start_mem; +} + +/* + * The mem_map array can get very big. Mark the end of the + * valid mem_map banks with PG_skip, and setup the address + * validity bitmap. + */ +unsigned long __init +create_mem_holes(unsigned long start_mem, unsigned long end_mem) +{ + struct page *pg = NULL; + unsigned int sz, i; - prot = (mp->prot_read ? PMD_SECT_AP_READ : 0) | - (mp->prot_write ? PMD_SECT_AP_WRITE : 0); + if (!machine_is_riscpc()) + return start_mem; - while (length >= 1048576) { - alloc_init_section(&start_mem, virtual, physical, mp->domain, prot); - length -= 1048576; - virtual += 1048576; - physical += 1048576; + sz = (end_mem - PAGE_OFFSET) >> 20; + sz = (sz + 31) >> 3; + + valid_addr_bitmap = (unsigned long *)start_mem; + start_mem += sz; + + memset(valid_addr_bitmap, 0, sz); + + if (start_mem > mem_desc[0].virt_end) + printk(KERN_CRIT "*** Error: RAM bank 0 too small\n"); + + for (i = 0; i < mem_desc_size; i++) { + unsigned int idx, end; + + if (pg) { + pg->next_hash = mem_map + + MAP_NR(mem_desc[i].virt_start); + pg = NULL; } - prot = (mp->prot_read ? L_PTE_USER : 0) | (mp->prot_write ? L_PTE_WRITE : 0) - | L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY; + idx = __kern_valid_idx(mem_desc[i].virt_start); + end = __kern_valid_idx(mem_desc[i].virt_end); + + do + set_bit(idx, valid_addr_bitmap); + while (++idx < end); + + if (mem_desc[i].virt_end < end_mem) { + pg = mem_map + MAP_NR(mem_desc[i].virt_end); - while (length >= PAGE_SIZE) { - alloc_init_page(&start_mem, virtual, physical, mp->domain, prot); - length -= PAGE_SIZE; - virtual += PAGE_SIZE; - physical += PAGE_SIZE; + set_bit(PG_skip, &pg->flags); } } + if (pg) + pg->next_hash = NULL; + return start_mem; } + +void __init +mark_usable_memory_areas(unsigned long start_mem, unsigned long end_mem) +{ + /* + * Mark all of memory from the end of kernel to end of memory + */ + while (start_mem < end_mem) { + clear_bit(PG_reserved, &mem_map[MAP_NR(start_mem)].flags); + start_mem += PAGE_SIZE; + } + + /* + * Mark memory from page 1 to start of the swapper page directory + */ + start_mem = PAGE_OFFSET + PAGE_SIZE; + while (start_mem < (unsigned long)&swapper_pg_dir) { + clear_bit(PG_reserved, &mem_map[MAP_NR(start_mem)].flags); + start_mem += PAGE_SIZE; + } +} diff --git a/arch/arm/mm/mm-ebsa110.c b/arch/arm/mm/mm-ebsa110.c index a85302473..8086bbc08 100644 --- a/arch/arm/mm/mm-ebsa110.c +++ b/arch/arm/mm/mm-ebsa110.c @@ -3,19 +3,27 @@ * * Extra MM routines for the EBSA-110 architecture * - * Copyright (C) 1998 Russell King + * Copyright (C) 1998-1999 Russell King */ -#include <linux/sched.h> #include <linux/mm.h> #include <linux/init.h> #include <asm/pgtable.h> #include <asm/page.h> -#include <asm/io.h> -#include <asm/proc/mm-init.h> + +#include "map.h" -#define MAPPING \ - { IO_BASE - PGDIR_SIZE , 0xc0000000 , PGDIR_SIZE , DOMAIN_IO, 0, 1 }, \ - { IO_BASE , IO_START , IO_SIZE , DOMAIN_IO, 0, 1 } +struct mem_desc mem_desc[] __initdata = { + 0, 0 +}; + +unsigned int __initdata mem_desc_size = 0; + +const struct map_desc io_desc[] __initdata = { + { IO_BASE - PGDIR_SIZE, 0xc0000000, PGDIR_SIZE, DOMAIN_IO, 0, 1, 0, 0 }, + { IO_BASE , IO_START , IO_SIZE , DOMAIN_IO, 0, 1, 0, 0 } +}; + +#define SIZEOFMAP (sizeof(mapping) / sizeof(mapping[0])) -#include "mm-armv.c" +unsigned int __initdata io_desc_size = SIZEOFMAP; diff --git a/arch/arm/mm/mm-footbridge.c b/arch/arm/mm/mm-footbridge.c index ec7e64c90..74bac27ea 100644 --- a/arch/arm/mm/mm-footbridge.c +++ b/arch/arm/mm/mm-footbridge.c @@ -1,9 +1,9 @@ /* - * arch/arm/mm/mm-ebsa285.c + * arch/arm/mm/mm-footbridge.c * * Extra MM routines for the EBSA285 architecture * - * Copyright (C) 1998 Russell King, Dave Gilbert. + * Copyright (C) 1998-1999 Russell King, Dave Gilbert. */ #include <linux/config.h> #include <linux/sched.h> @@ -13,9 +13,10 @@ #include <asm/pgtable.h> #include <asm/page.h> #include <asm/io.h> -#include <asm/proc/mm-init.h> #include <asm/dec21285.h> +#include "map.h" + /* * The first entry allows us to fiddle with the EEPROM from user-space. * This entry will go away in time, once the fmu32 can mmap() the @@ -32,15 +33,15 @@ * The mapping when the footbridge is in host mode. */ #define MAPPING \ - { FLASH_BASE, DC21285_FLASH, FLASH_SIZE, DOMAIN_IO, 0, 1 }, \ - { PCIMEM_BASE, DC21285_PCI_MEM, PCIMEM_SIZE, DOMAIN_IO, 0, 1 }, \ - { PCICFG0_BASE, DC21285_PCI_TYPE_0_CONFIG, PCICFG0_SIZE, DOMAIN_IO, 0, 1 }, \ - { PCICFG1_BASE, DC21285_PCI_TYPE_1_CONFIG, PCICFG1_SIZE, DOMAIN_IO, 0, 1 }, \ - { PCIIACK_BASE, DC21285_PCI_IACK, PCIIACK_SIZE, DOMAIN_IO, 0, 1 }, \ - { WFLUSH_BASE, DC21285_OUTBOUND_WRITE_FLUSH, WFLUSH_SIZE, DOMAIN_IO, 0, 1 }, \ - { ARMCSR_BASE, DC21285_ARMCSR_BASE, ARMCSR_SIZE, DOMAIN_IO, 0, 1 }, \ - { PCIO_BASE, DC21285_PCI_IO, PCIO_SIZE, DOMAIN_IO, 0, 1 }, \ - { XBUS_BASE, 0x40000000, XBUS_SIZE, DOMAIN_IO, 0, 1 } + { FLASH_BASE, DC21285_FLASH, FLASH_SIZE, DOMAIN_IO, 0, 1, 0, 0 }, \ + { PCIMEM_BASE, DC21285_PCI_MEM, PCIMEM_SIZE, DOMAIN_IO, 0, 1, 0, 0 }, \ + { PCICFG0_BASE, DC21285_PCI_TYPE_0_CONFIG, PCICFG0_SIZE, DOMAIN_IO, 0, 1, 0, 0 }, \ + { PCICFG1_BASE, DC21285_PCI_TYPE_1_CONFIG, PCICFG1_SIZE, DOMAIN_IO, 0, 1, 0, 0 }, \ + { PCIIACK_BASE, DC21285_PCI_IACK, PCIIACK_SIZE, DOMAIN_IO, 0, 1, 0, 0 }, \ + { WFLUSH_BASE, DC21285_OUTBOUND_WRITE_FLUSH, WFLUSH_SIZE, DOMAIN_IO, 0, 1, 0, 0 }, \ + { ARMCSR_BASE, DC21285_ARMCSR_BASE, ARMCSR_SIZE, DOMAIN_IO, 0, 1, 0, 0 }, \ + { PCIO_BASE, DC21285_PCI_IO, PCIO_SIZE, DOMAIN_IO, 0, 1, 0, 0 }, \ + { XBUS_BASE, 0x40000000, XBUS_SIZE, DOMAIN_IO, 0, 1, 0, 0 } #else @@ -79,13 +80,26 @@ unsigned long __bus_to_virt(unsigned long res) * The mapping when the footbridge is in add-in mode. */ #define MAPPING \ - { PCIO_BASE, DC21285_PCI_IO, PCIO_SIZE, DOMAIN_IO, 0, 1 }, \ - { XBUS_BASE, 0x40000000, XBUS_SIZE, DOMAIN_IO, 0, 1 }, \ - { ARMCSR_BASE, DC21285_ARMCSR_BASE, ARMCSR_SIZE, DOMAIN_IO, 0, 1 }, \ - { WFLUSH_BASE, DC21285_OUTBOUND_WRITE_FLUSH, WFLUSH_SIZE, DOMAIN_IO, 0, 1 }, \ - { FLASH_BASE, DC21285_FLASH, FLASH_SIZE, DOMAIN_IO, 0, 1 }, \ - { PCIMEM_BASE, DC21285_PCI_MEM, PCIMEM_SIZE, DOMAIN_IO, 0, 1 } + { PCIO_BASE, DC21285_PCI_IO, PCIO_SIZE, DOMAIN_IO, 0, 1, 0, 0 }, \ + { XBUS_BASE, 0x40000000, XBUS_SIZE, DOMAIN_IO, 0, 1, 0, 0 }, \ + { ARMCSR_BASE, DC21285_ARMCSR_BASE, ARMCSR_SIZE, DOMAIN_IO, 0, 1, 0, 0 }, \ + { WFLUSH_BASE, DC21285_OUTBOUND_WRITE_FLUSH, WFLUSH_SIZE, DOMAIN_IO, 0, 1, 0, 0 }, \ + { FLASH_BASE, DC21285_FLASH, FLASH_SIZE, DOMAIN_IO, 0, 1, 0, 0 }, \ + { PCIMEM_BASE, DC21285_PCI_MEM, PCIMEM_SIZE, DOMAIN_IO, 0, 1, 0, 0 } #endif -#include "mm-armv.c" +struct mem_desc mem_desc[] __initdata = { + 0, 0 +}; + +unsigned int __initdata mem_desc_size = 0; + +struct map_desc io_desc[] __initdata = { + MAPPING +}; + +#define SIZE(x) (sizeof(x) / sizeof(x[0])) + +unsigned int __initdata io_desc_size = SIZE(io_desc); + diff --git a/arch/arm/mm/mm-nexuspci.c b/arch/arm/mm/mm-nexuspci.c index a7407c130..a4ee48f8d 100644 --- a/arch/arm/mm/mm-nexuspci.c +++ b/arch/arm/mm/mm-nexuspci.c @@ -5,7 +5,7 @@ * Extra MM routines for the NexusPCI architecture * * Copyright (C) 1998 Phil Blundell - * Copyright (C) 1998 Russell King + * Copyright (C) 1998-1999 Russell King */ #include <linux/sched.h> @@ -15,13 +15,23 @@ #include <asm/pgtable.h> #include <asm/page.h> #include <asm/io.h> -#include <asm/proc/mm-init.h> + +#include "map.h" -#define MAPPING \ - { 0xfff00000, 0x10000000, 0x00001000, DOMAIN_IO, 0, 1 }, \ - { 0xffe00000, 0x20000000, 0x00001000, DOMAIN_IO, 0, 1 }, \ - { 0xffc00000, 0x60000000, 0x00001000, DOMAIN_IO, 0, 1 }, \ - { 0xfe000000, 0x80000000, 0x00100000, DOMAIN_IO, 0, 1 }, \ - { 0xfd000000, 0x88000000, 0x00100000, DOMAIN_IO, 0, 1 } +struct mem_desc mem_desc[] __initdata = { + 0, 0 +}; + +unsigned int __initdata mem_desc_size = 0; + +const struct map_desc io_desc[] __initdata = { + { 0xfff00000, 0x10000000, 0x00001000, DOMAIN_IO, 0, 1, 0, 0 }, + { 0xffe00000, 0x20000000, 0x00001000, DOMAIN_IO, 0, 1, 0, 0 }, + { 0xffc00000, 0x60000000, 0x00001000, DOMAIN_IO, 0, 1, 0, 0 }, + { 0xfe000000, 0x80000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, + { 0xfd000000, 0x88000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 } +}; + +#define SIZEOFMAP (sizeof(mapping) / sizeof(mapping[0])) -#include "mm-armv.c" +unsigned int __initdata io_desc_size = SIZEOFMAP; diff --git a/arch/arm/mm/mm-rpc.c b/arch/arm/mm/mm-rpc.c index 80f821245..634bb3c8f 100644 --- a/arch/arm/mm/mm-rpc.c +++ b/arch/arm/mm/mm-rpc.c @@ -3,95 +3,48 @@ * * Extra MM routines for RiscPC architecture * - * Copyright (C) 1998 Russell King + * Copyright (C) 1998-1999 Russell King */ -#include <linux/sched.h> -#include <linux/slab.h> #include <linux/init.h> -#include <asm/pgtable.h> +#include <asm/hardware.h> +#include <asm/page.h> +#include <asm/proc/domain.h> #include <asm/setup.h> -#include <asm/io.h> -#include <asm/proc/mm-init.h> -#include <asm/arch/mm-init.h> -#define NR_DRAM_BANKS 4 -#define NR_VRAM_BANKS 1 +#include "map.h" -#define NR_BANKS (NR_DRAM_BANKS + NR_VRAM_BANKS) +#define SIZE(x) (sizeof(x) / sizeof(x[0])) -#define FIRST_BANK 0 -#define FIRST_DRAM_BANK 0 -#define FIRST_VRAM_BANK NR_DRAM_BANKS - -#define BANK_SHIFT 26 -#define FIRST_DRAM_ADDR 0x10000000 - -#define PHYS_TO_BANK(x) (((x) >> BANK_SHIFT) & (NR_DRAM_BANKS - 1)) -#define BANK_TO_PHYS(x) ((FIRST_DRAM_ADDR) + \ - (((x) - FIRST_DRAM_BANK) << BANK_SHIFT)) - -struct ram_bank { - unsigned int virt_addr; /* virtual address of the *end* of this bank + 1 */ - signed int phys_offset; /* offset to physical address of this bank */ +struct mem_desc mem_desc[] __initdata = { + { 0xc0000000, 0xc0000000 }, + { 0xc4000000, 0xc4000000 }, + { 0xc8000000, 0xc8000000 }, + { 0xcc000000, 0xcc000000 } }; -static struct ram_bank rambank[NR_BANKS]; - -/* - * Return the physical (0x10000000 -> 0x20000000) address of - * the virtual (0xc0000000 -> 0xd0000000) address - */ -unsigned long __virt_to_phys(unsigned long vpage) -{ - unsigned int bank = FIRST_BANK; - - while (vpage >= rambank[bank].virt_addr && bank < NR_BANKS) - bank ++; - - return vpage - rambank[bank].phys_offset; -} - -/* - * Return the virtual (0xc0000000 -> 0xd0000000) address of - * the physical (0x10000000 -> 0x20000000) address - */ -unsigned long __phys_to_virt(unsigned long phys) -{ - unsigned int bank; - - if (phys > FIRST_DRAM_ADDR) - bank = PHYS_TO_BANK(phys); - else - bank = FIRST_VRAM_BANK; - - return phys + rambank[bank].phys_offset; -} +unsigned int __initdata mem_desc_size = SIZE(mem_desc); void __init init_dram_banks(struct param_struct *params) { unsigned int bank; - unsigned int bytes = 0; - for (bank = FIRST_DRAM_BANK; bank < NR_DRAM_BANKS; bank++) { - rambank[bank].phys_offset = PAGE_OFFSET + bytes - - BANK_TO_PHYS(bank); + for (bank = 0; bank < mem_desc_size; bank++) + mem_desc[bank].virt_end += PAGE_SIZE * + params->u1.s.pages_in_bank[bank]; - bytes += params->u1.s.pages_in_bank[bank - FIRST_DRAM_BANK] * PAGE_SIZE; - - rambank[bank].virt_addr = PAGE_OFFSET + bytes; - } - - rambank[FIRST_VRAM_BANK].phys_offset = 0xd6000000; - rambank[FIRST_VRAM_BANK].virt_addr = 0xd8000000; + params->u1.s.nr_pages = mem_desc[3].virt_end - PAGE_OFFSET; + params->u1.s.nr_pages /= PAGE_SIZE; } -#define MAPPING \ - { SCREEN2_BASE, SCREEN_START, 2*1048576, DOMAIN_IO, 0, 1 }, /* VRAM */ \ - { IO_BASE, IO_START, IO_SIZE , DOMAIN_IO, 0, 1 }, /* IO space */ \ - { EASI_BASE, EASI_START, EASI_SIZE, DOMAIN_IO, 0, 1 } /* EASI space */ -/* - * Include common routine to set up page tables - */ -#include "mm-armv.c" +struct map_desc io_desc[] __initdata = { + /* VRAM */ + { SCREEN2_BASE, SCREEN_START, 2*1048576, DOMAIN_IO, 0, 1, 0, 0 }, + /* IO space */ + { IO_BASE, IO_START, IO_SIZE , DOMAIN_IO, 0, 1, 0, 0 }, + /* EASI space */ + { EASI_BASE, EASI_START, EASI_SIZE, DOMAIN_IO, 0, 1, 0, 0 } +}; + +unsigned int __initdata io_desc_size = SIZE(io_desc); diff --git a/arch/arm/mm/mm-sa1100.c b/arch/arm/mm/mm-sa1100.c new file mode 100644 index 000000000..eba2984b6 --- /dev/null +++ b/arch/arm/mm/mm-sa1100.c @@ -0,0 +1,84 @@ +/* + * arch/arm/mm/mm-sa1100.c + * + * Extra MM routines for the SA1100 architecture + * + * Copyright (C) 1998-1999 Russell King + * Copyright (C) 1999 Hugo Fiennes + * + * 1999/09/12 Nicolas Pitre <nico@visuaide.com> + * Specific RAM implementation details are in + * linux/include/asm/arch-sa1100/memory.h now. + * Allows for better macro optimisations when possible. + */ +#include <linux/config.h> +#include <linux/mm.h> +#include <linux/init.h> + +#include <asm/pgtable.h> +#include <asm/page.h> + +#include "map.h" + +#define SIZE(x) (sizeof(x) / sizeof(x[0])) + +/* + * These are the memory size mappings for the + * SA1100. Note that LART is a special case - + * it doesn't use physical address A23 on the + * DRAM, so we effectively have 4 * 8MB in + * two banks. + */ +struct mem_desc mem_desc[] __initdata = { + /* virt start virt end */ +#if defined(CONFIG_SA1100_BRUTUS) + { 0xc0000000, 0xc0400000 }, /* 4MB */ + { 0xc1000000, 0xc1400000 }, /* 4MB */ + { 0xc2000000, 0xc2400000 }, /* 4MB */ + { 0xc3000000, 0xc3400000 } /* 4MB */ +#elif defined(CONFIG_SA1100_EMPEG) + { 0xc0000000, 0xc0400000 }, /* 4MB */ + { 0xc1000000, 0xc1400000 } /* 4MB */ +#elif defined(CONFIG_SA1100_LART) + { 0xc0000000, 0xc0800000 }, /* 16MB */ + { 0xc1000000, 0xc1800000 }, + { 0xc2000000, 0xc2800000 }, /* 16MB */ + { 0xc3000000, 0xc3800000 } +#elif defined(CONFIG_SA1100_VICTOR) + { 0xc0000000, 0xc0400000 } /* 4MB */ +#elif defined(CONFIG_SA1100_TIFON) + { 0xc0000000, 0xc1000000 }, /* 16MB */ + { 0xc1000000, 0xc2000000 } /* 16MB */ +#else +#error missing memory configuration +#endif +}; + +unsigned int __initdata mem_desc_size = SIZE(mem_desc); + +struct map_desc io_desc[] __initdata = { + /* virtual physical length domain r w c b */ +#if defined(CONFIG_SA1100_VICTOR) + { 0xd0000000, 0x00000000, 0x00200000, DOMAIN_IO, 1, 1, 0, 0 }, /* Flash */ +#elif defined(CONFIG_SA1100_EMPEG) + { EMPEG_FLASHBASE, 0x00000000, 0x00200000, DOMAIN_IO, 1, 1, 0, 0 }, /* Flash */ +#elif defined(CONFIG_SA1100_TIFON) + { 0xd0000000, 0x00000000, 0x00800000, DOMAIN_IO, 1, 1, 0, 0 }, /* Flash bank 1 */ + { 0xd0800000, 0x08000000, 0x00800000, DOMAIN_IO, 1, 1, 0, 0 }, /* Flash bank 2 */ +#endif +#ifdef CONFIG_SA1101 + { 0xdc000000, SA1101_BASE, 0x00400000, DOMAIN_IO, 1, 1, 0, 0 }, /* SA1101 */ +#endif + { 0xe0000000, 0x20000000, 0x04000000, DOMAIN_IO, 0, 1, 0, 0 }, /* PCMCIA0 IO */ + { 0xe4000000, 0x30000000, 0x04000000, DOMAIN_IO, 0, 1, 0, 0 }, /* PCMCIA1 IO */ + { 0xe8000000, 0x28000000, 0x04000000, DOMAIN_IO, 0, 1, 0, 0 }, /* PCMCIA0 attr */ + { 0xec000000, 0x38000000, 0x04000000, DOMAIN_IO, 0, 1, 0, 0 }, /* PCMCIA1 attr */ + { 0xf0000000, 0x2c000000, 0x04000000, DOMAIN_IO, 0, 1, 0, 0 }, /* PCMCIA0 mem */ + { 0xf4000000, 0x3c000000, 0x04000000, DOMAIN_IO, 0, 1, 0, 0 }, /* PCMCIA1 mem */ + { 0xf8000000, 0x80000000, 0x02000000, DOMAIN_IO, 0, 1, 0, 0 }, /* PCM */ + { 0xfa000000, 0x90000000, 0x02000000, DOMAIN_IO, 0, 1, 0, 0 }, /* SCM */ + { 0xfc000000, 0xa0000000, 0x02000000, DOMAIN_IO, 0, 1, 0, 0 }, /* MER */ + { 0xfe000000, 0xb0000000, 0x02000000, DOMAIN_IO, 0, 1, 0, 0 } /* LCD + DMA */ +}; + +unsigned int __initdata io_desc_size = SIZE(io_desc); diff --git a/arch/arm/mm/mm-tbox.c b/arch/arm/mm/mm-tbox.c index 2cc5cc9e1..a6dd2a28f 100644 --- a/arch/arm/mm/mm-tbox.c +++ b/arch/arm/mm/mm-tbox.c @@ -5,7 +5,7 @@ * Extra MM routines for the Tbox architecture * * Copyright (C) 1998 Phil Blundell - * Copyright (C) 1998 Russell King + * Copyright (C) 1998-1999 Russell King */ #include <linux/sched.h> @@ -15,9 +15,15 @@ #include <asm/io.h> #include <asm/pgtable.h> #include <asm/page.h> -#include <asm/proc/mm-init.h> - + +#include "map.h" +struct mem_desc mem_desc[] __initdata = { + 0, 0 +}; + +unsigned int __initdata mem_desc_size = 0; + /* Logical Physical * 0xffff1000 0x00100000 DMA registers * 0xffff2000 0x00200000 MPEG @@ -34,23 +40,26 @@ * 0xffffd000 0x00d00000 Interrupt reset * 0xffffe000 0x00e00000 MPEG DMA throttle */ - -#define MAPPING \ - { 0xffff0000, 0x01000000, 0x00001000, DOMAIN_IO, 0, 1 }, \ - { 0xffff1000, 0x00100000, 0x00001000, DOMAIN_IO, 0, 1 }, \ - { 0xffff2000, 0x00200000, 0x00001000, DOMAIN_IO, 0, 1 }, \ - { 0xffff3000, 0x00300000, 0x00001000, DOMAIN_IO, 0, 1 }, \ - { 0xffff4000, 0x00400000, 0x00001000, DOMAIN_IO, 0, 1 }, \ - { 0xfe000000, 0x00400000, 0x00001000, DOMAIN_IO, 0, 1 }, \ - { 0xffff5000, 0x00500000, 0x00001000, DOMAIN_IO, 0, 1 }, \ - { 0xffff6000, 0x00600000, 0x00001000, DOMAIN_IO, 0, 1 }, \ - { 0xffff7000, 0x00700000, 0x00001000, DOMAIN_IO, 0, 1 }, \ - { 0xffff8000, 0x00800000, 0x00001000, DOMAIN_IO, 0, 1 }, \ - { 0xffff9000, 0x00900000, 0x00001000, DOMAIN_IO, 0, 1 }, \ - { 0xffffa000, 0x00a00000, 0x00001000, DOMAIN_IO, 0, 1 }, \ - { 0xffffb000, 0x00b00000, 0x00001000, DOMAIN_IO, 0, 1 }, \ - { 0xffffc000, 0x00c00000, 0x00001000, DOMAIN_IO, 0, 1 }, \ - { 0xffffd000, 0x00d00000, 0x00001000, DOMAIN_IO, 0, 1 }, \ - { 0xffffe000, 0x00e00000, 0x00001000, DOMAIN_IO, 0, 1 } - -#include "mm-armv.c" + +const struct map_desc io_desc[] __initdata = { + { 0xffff0000, 0x01000000, 0x00001000, DOMAIN_IO, 0, 1, 0, 0 }, + { 0xffff1000, 0x00100000, 0x00001000, DOMAIN_IO, 0, 1, 0, 0 }, + { 0xffff2000, 0x00200000, 0x00001000, DOMAIN_IO, 0, 1, 0, 0 }, + { 0xffff3000, 0x00300000, 0x00001000, DOMAIN_IO, 0, 1, 0, 0 }, + { 0xffff4000, 0x00400000, 0x00001000, DOMAIN_IO, 0, 1, 0, 0 }, + { 0xfe000000, 0x00400000, 0x00001000, DOMAIN_IO, 0, 1, 0, 0 }, + { 0xffff5000, 0x00500000, 0x00001000, DOMAIN_IO, 0, 1, 0, 0 }, + { 0xffff6000, 0x00600000, 0x00001000, DOMAIN_IO, 0, 1, 0, 0 }, + { 0xffff7000, 0x00700000, 0x00001000, DOMAIN_IO, 0, 1, 0, 0 }, + { 0xffff8000, 0x00800000, 0x00001000, DOMAIN_IO, 0, 1, 0, 0 }, + { 0xffff9000, 0x00900000, 0x00001000, DOMAIN_IO, 0, 1, 0, 0 }, + { 0xffffa000, 0x00a00000, 0x00001000, DOMAIN_IO, 0, 1, 0, 0 }, + { 0xffffb000, 0x00b00000, 0x00001000, DOMAIN_IO, 0, 1, 0, 0 }, + { 0xffffc000, 0x00c00000, 0x00001000, DOMAIN_IO, 0, 1, 0, 0 }, + { 0xffffd000, 0x00d00000, 0x00001000, DOMAIN_IO, 0, 1, 0, 0 }, + { 0xffffe000, 0x00e00000, 0x00001000, DOMAIN_IO, 0, 1, 0, 0 } +}; + +#define SIZEOFMAP (sizeof(mapping) / sizeof(mapping[0])) + +unsigned int __initdata io_desc_size = SIZEOFMAP; diff --git a/arch/arm/mm/proc-arm2,3.S b/arch/arm/mm/proc-arm2,3.S index 6ec34b612..df2e13357 100644 --- a/arch/arm/mm/proc-arm2,3.S +++ b/arch/arm/mm/proc-arm2,3.S @@ -12,349 +12,184 @@ #include "../lib/constants.h" /* - * Code common to all processors - MEMC specific not processor - * specific! + * MEMC workhorse code. It's both a horse which things it's a pig. */ - -LC1: .word SYMBOL_NAME(page_nr) -/* - * Function: arm2_3_update_map (struct task_struct *tsk) - * - * Params : tsk Task structure to be updated - * - * Purpose : Re-generate memc maps for task from its pseudo page tables - */ -_arm2_3_update_map: - mov ip, sp - stmfd sp!, {r4 - r6, fp, ip, lr, pc} - sub fp, ip, #4 - add r1, r0, #TSS_MEMCMAP - ldr r2, LC1 - ldr r2, [r2] - mov r3, #0x03f00000 - orr r3, r3, #0x00000f00 - orr r4, r3, #1 - orr r5, r3, #2 - orr r6, r3, #3 -1: stmia r1!, {r3, r4, r5, r6} @ Default mapping (null mapping) - add r3, r3, #4 - add r4, r4, #4 - add r5, r5, #4 - add r6, r6, #4 - stmia r1!, {r3, r4, r5, r6} @ Default mapping (null mapping) - add r3, r3, #4 - add r4, r4, #4 - add r5, r5, #4 - add r6, r6, #4 - subs r2, r2, #8 - bhi 1b - - adr r2, Lphystomemc32 @ r2 = conversion table to logical page number - ldr r4, [r0, #TSS_MEMMAP] @ r4 = active mem map - add r5, r4, #32 << 2 @ r5 = end of active mem map - add r0, r0, #TSS_MEMCMAP @ r0 = memc map - - mov r6, #0 -2: ldmia r4!, {r1, r3} - tst r1, #PAGE_PRESENT - blne update_map_pgd - add r6, r6, #32 << 2 - tst r3, #PAGE_PRESENT - blne update_map_pgd3 - add r6, r6, #32 << 2 - cmp r4, r5 - blt 2b - ldmea fp, {r4 - r6, fp, sp, pc}^ - -@ r0,r2,r3,r4,r5 = preserve -@ r1,ip = available -@ r0 = memc map -@ r1 = pgd entry -@ r2 = conversion table -@ r6 = logical page no << 2 - -update_map_pgd3: - mov r1, r3 -update_map_pgd: stmfd sp!, {r3, r4, r5, lr} - bic r4, r1, #3 @ r4 = page table - sub r5, r6, #1 << 2 - add ip, r4, #32 << 2 @ ip = end of page table - -1: ldr r1, [r4], #4 @ get entry - add r5, r5, #1 << 2 - tst r1, #PAGE_PRESENT @ page present? - blne Lconvertmemc @ yes - ldr r1, [r4], #4 @ get entry - add r5, r5, #1 << 2 - tst r1, #PAGE_PRESENT @ page present? - blne Lconvertmemc @ yes - ldr r1, [r4], #4 @ get entry - add r5, r5, #1 << 2 - tst r1, #PAGE_PRESENT @ page present? - blne Lconvertmemc @ yes - ldr r1, [r4], #4 @ get entry - add r5, r5, #1 << 2 - tst r1, #PAGE_PRESENT @ page present? - blne Lconvertmemc @ yes - cmp r4, ip - blt 1b - ldmfd sp!, {r3, r4, r5, pc}^ - -Lconvertmemc: mov r3, r1, lsr #13 @ - and r3, r3, #0x3fc @ Convert to memc physical page no - ldr r3, [r2, r3] @ - - tst r1, #PAGE_OLD|PAGE_NOT_USER @ check for MEMC read - biceq r3, r3, #0x200 @ - tsteq r1, #PAGE_READONLY|PAGE_CLEAN @ check for MEMC write - biceq r3, r3, #0x300 @ - - orr r3, r3, r5, lsl #13 - and r1, r5, #0x01800000 >> 13 - orr r3, r3, r1 - - and r1, r3, #255 - str r3, [r0, r1, lsl #2] - movs pc, lr - /* - * Function: arm2_3_update_cache (struct task_struct *tsk, unsigned long addr, pte_t pte) - * Params : tsk Task to update - * address Address of fault. - * pte New PTE at address - * Purpose : Update the mapping for this address. - * Notes : does the ARM3 run faster if you do not use the result in the next instruction? + * Function: cpu_memc_update_entry(pgd_t *pgd, unsigned long phys_pte, unsigned long addr) + * Params : pgd Page tables/MEMC mapping + * : phys_pte physical address, or PTE + * : addr virtual address */ -_arm2_3_update_cache: - tst r2, #PAGE_PRESENT - moveqs pc, lr - mov r3, r2, lsr #13 @ Physical page no. - adr ip, Lphystomemc32 @ Convert to logical page number +ENTRY(cpu_memc_update_entry) + tst r1, #PAGE_PRESENT @ is the page present + orreq r1, r1, #PAGE_OLD | PAGE_CLEAN + moveq r2, #0x01f00000 + mov r3, r1, lsr #13 @ convert to physical page nr and r3, r3, #0x3fc - mov r1, r1, lsr #15 - ldr r3, [ip, r3] @ Convert to memc phys page no. - tst r2, #PAGE_OLD|PAGE_NOT_USER + adr ip, memc_phys_table_32 + ldr r3, [ip, r3] + tst r1, #PAGE_OLD | PAGE_NOT_USER biceq r3, r3, #0x200 - tsteq r2, #PAGE_READONLY|PAGE_CLEAN + tsteq r1, #PAGE_READONLY | PAGE_CLEAN biceq r3, r3, #0x300 - mov ip, sp, lsr #13 - orr r3, r3, r1, lsl #15 - mov ip, ip, lsl #13 - and r1, r1, #0x300 - teq ip, r0 - orr r3, r3, r1, lsl #2 - add r0, r0, #TSS_MEMCMAP + mov r2, r2, lsr #15 @ virtual -> nr + orr r3, r3, r2, lsl #15 + and r2, r2, #0x300 + orr r3, r3, r2, lsl #2 and r2, r3, #255 - streqb r3, [r3] + sub r0, r0, #256 * 4 str r3, [r0, r2, lsl #2] - movs pc, lr - -#define PCD(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, aa, ab, ac, ad, ae, af) \ - .long a0| 0x03800300; .long a1| 0x03800300;\ - .long a2| 0x03800300; .long a3| 0x03800300;\ - .long a4| 0x03800300; .long a5| 0x03800300;\ - .long a6| 0x03800300; .long a7| 0x03800300;\ - .long a8| 0x03800300; .long a9| 0x03800300;\ - .long aa| 0x03800300; .long ab| 0x03800300;\ - .long ac| 0x03800300; .long ad| 0x03800300;\ - .long ae| 0x03800300; .long af| 0x03800300 - -@ Table to map from page number to vidc page number -Lphystomemc32: PCD(0x00,0x08,0x10,0x18,0x20,0x28,0x30,0x38,0x40,0x48,0x50,0x58,0x60,0x68,0x70,0x78) - PCD(0x01,0x09,0x11,0x19,0x21,0x29,0x31,0x39,0x41,0x49,0x51,0x59,0x61,0x69,0x71,0x79) - PCD(0x04,0x0C,0x14,0x1C,0x24,0x2C,0x34,0x3C,0x44,0x4C,0x54,0x5C,0x64,0x6C,0x74,0x7C) - PCD(0x05,0x0D,0x15,0x1D,0x25,0x2D,0x35,0x3D,0x45,0x4D,0x55,0x5D,0x65,0x6D,0x75,0x7D) - PCD(0x02,0x0A,0x12,0x1A,0x22,0x2A,0x32,0x3A,0x42,0x4A,0x52,0x5A,0x62,0x6A,0x72,0x7A) - PCD(0x03,0x0B,0x13,0x1B,0x23,0x2B,0x33,0x3B,0x43,0x4B,0x53,0x5B,0x63,0x6B,0x73,0x7B) - PCD(0x06,0x0E,0x16,0x1E,0x26,0x2E,0x36,0x3E,0x46,0x4E,0x56,0x5E,0x66,0x6E,0x76,0x7E) - PCD(0x07,0x0F,0x17,0x1F,0x27,0x2F,0x37,0x3F,0x47,0x4F,0x57,0x5F,0x67,0x6F,0x77,0x7F) - PCD(0x80,0x88,0x90,0x98,0xA0,0xA8,0xB0,0xB8,0xC0,0xC8,0xD0,0xD8,0xE0,0xE8,0xF0,0xF8) - PCD(0x81,0x89,0x91,0x99,0xA1,0xA9,0xB1,0xB9,0xC1,0xC9,0xD1,0xD9,0xE1,0xE9,0xF1,0xF9) - PCD(0x84,0x8C,0x94,0x9C,0xA4,0xAC,0xB4,0xBC,0xC4,0xCC,0xD4,0xDC,0xE4,0xEC,0xF4,0xFC) - PCD(0x85,0x8D,0x95,0x9D,0xA5,0xAD,0xB5,0xBD,0xC5,0xCD,0xD5,0xDD,0xE5,0xED,0xF5,0xFD) - PCD(0x82,0x8A,0x92,0x9A,0xA2,0xAA,0xB2,0xBA,0xC2,0xCA,0xD2,0xDA,0xE2,0xEA,0xF2,0xFA) - PCD(0x83,0x8B,0x93,0x9B,0xA3,0xAB,0xB3,0xBB,0xC3,0xCB,0xD3,0xDB,0xE3,0xEB,0xF3,0xFB) - PCD(0x86,0x8E,0x96,0x9E,0xA6,0xAE,0xB6,0xBE,0xC6,0xCE,0xD6,0xDE,0xE6,0xEE,0xF6,0xFE) - PCD(0x87,0x8F,0x97,0x9F,0xA7,0xAF,0xB7,0xBF,0xC7,0xCF,0xD7,0xDF,0xE7,0xEF,0xF7,0xFF) - + strb r3, [r3] + movs pc, lr /* - * Function: arm2_3_data_abort () - * - * Params : r0 = address of aborted instruction - * - * Purpose : - * - * Returns : r0 = address of abort - * : r1 = FSR - * : r2 != 0 if writing + * Params : r0 = preserved + * : r1 = memc table base (preserved) + * : r2 = page table entry + * : r3 = preserved + * : r4 = unused + * : r5 = memc physical address translation table + * : ip = virtual address (preserved) */ - -_arm2_3_data_abort: +update_pte: + mov r4, r2, lsr #13 + and r4, r4, #0x3fc + ldr r4, [r5, r4] @ covert to MEMC page + + tst r2, #PAGE_OLD | PAGE_NOT_USER @ check for MEMC read + biceq r4, r4, #0x200 + tsteq r2, #PAGE_READONLY | PAGE_CLEAN @ check for MEMC write + biceq r4, r4, #0x300 + + orr r4, r4, ip + and r2, ip, #0x01800000 + orr r4, r4, r2, lsr #13 + + and r2, r4, #255 + str r4, [r1, r2, lsl #2] movs pc, lr -_arm2_3_check_bugs: - bics pc, lr, #0x04000000 @ Clear FIQ disable bit - /* - * Processor specific - ARM2 + * Params : r0 = preserved + * : r1 = memc table base (preserved) + * : r2 = page table base + * : r3 = preserved + * : r4 = unused + * : r5 = memc physical address translation table + * : ip = virtual address (updated) */ +update_pte_table: + stmfd sp!, {r0, lr} + bic r0, r2, #3 +1: ldr r2, [r0], #4 @ get entry + tst r2, #PAGE_PRESENT @ page present + blne update_pte @ process pte + add ip, ip, #32768 @ increment virt addr + ldr r2, [r0], #4 @ get entry + tst r2, #PAGE_PRESENT @ page present + blne update_pte @ process pte + add ip, ip, #32768 @ increment virt addr + ldr r2, [r0], #4 @ get entry + tst r2, #PAGE_PRESENT @ page present + blne update_pte @ process pte + add ip, ip, #32768 @ increment virt addr + ldr r2, [r0], #4 @ get entry + tst r2, #PAGE_PRESENT @ page present + blne update_pte @ process pte + add ip, ip, #32768 @ increment virt addr + tst ip, #32768 * 31 @ finished? + bne 1b + ldmfd sp!, {r0, pc}^ -LC0: .word SYMBOL_NAME(page_nr) /* - * Function: arm2_switch_to (struct task_struct *prev, struct task_struct *next) - * Params : prev Old task structure - * : next New task structure for process to run - * Returns : prev - * Purpose : Perform a task switch, saving the old processes state, and restoring - * the new. - * Notes : We don't fiddle with the FP registers here - we postpone this until - * the new task actually uses FP. This way, we don't swap FP for tasks - * that do not require it. + * Function: cpu_memc_update_all(pgd_t *pgd) + * Params : pgd Page tables/MEMC mapping + * Notes : this is optimised for 32k pages */ -_arm2_switch_to: - stmfd sp!, {r4 - r9, fp, lr} @ Store most regs on stack - str sp, [r0, #TSS_SAVE] @ Save sp_SVC - ldr sp, [r1, #TSS_SAVE] @ Get saved sp_SVC - mov r4, r1 - add r7, r1, #TSS_MEMCMAP @ Remap MEMC - ldr r1, LC0 - ldr r1, [r1] -1: ldmia r7!, {r2, r3, r5, r6} - strb r2, [r2] - strb r3, [r3] - strb r5, [r5] - strb r6, [r6] - ldmia r7!, {r2, r3, r5, r6} - strb r2, [r2] - strb r3, [r3] - strb r5, [r5] - strb r6, [r6] - subs r1, r1, #8 - bhi 1b - ldmfd sp!, {r4 - r9, fp, pc}^ @ Load all regs saved previously +ENTRY(cpu_memc_update_all) + stmfd sp!, {r4, r5, lr} + bl clear_tables + sub r1, r0, #256 * 4 @ start of MEMC tables + adr r5, memc_phys_table_32 @ Convert to logical page number + mov ip, #0 @ virtual address +1: ldmia r0!, {r2, r3} + tst r2, #PAGE_PRESENT + addeq ip, ip, #1048576 + blne update_pte_table + mov r2, r3 + tst r2, #PAGE_PRESENT + addeq ip, ip, #1048576 + blne update_pte_table + teq ip, #32 * 1048576 + bne 1b + ldmfd sp!, {r4, r5, pc}^ /* - * Function: arm2_remap_memc (struct task_struct *tsk) - * - * Params : tsk Task structure specifing the new mapping structure - * - * Purpose : remap MEMC tables + * Build the table to map from physical page number to memc page number */ -_arm2_remap_memc: - stmfd sp!, {lr} - add r0, r0, #TSS_MEMCMAP - ldr r1, LC0 - ldr r1, [r1] -1: ldmia r0!, {r2, r3, ip, lr} - strb r2, [r2] - strb r3, [r3] - strb ip, [ip] - strb lr, [lr] - ldmia r0!, {r2, r3, ip, lr} - strb r2, [r2] - strb r3, [r3] - strb ip, [ip] - strb lr, [lr] - subs r1, r1, #8 - bhi 1b - ldmfd sp!, {pc}^ + .type memc_phys_table_32, #object +memc_phys_table_32: + .irp b7, 0x00, 0x80 + .irp b6, 0x00, 0x02 + .irp b5, 0x00, 0x04 + .irp b4, 0x00, 0x01 + + .irp b3, 0x00, 0x40 + .irp b2, 0x00, 0x20 + .irp b1, 0x00, 0x10 + .irp b0, 0x00, 0x08 + .long 0x03800300 + \b7 + \b6 + \b5 + \b4 + \b3 + \b2 + \b1 + \b0 + .endr + .endr + .endr + .endr + + .endr + .endr + .endr + .endr + .size memc_phys_table_32, . - memc_phys_table_32 /* - * Function: arm2_xchg_1 (int new, volatile void *ptr) - * - * Params : new New value to store at... - * : ptr pointer to byte-wide location - * - * Purpose : Performs an exchange operation - * - * Returns : Original byte data at 'ptr' - * - * Notes : This will have to be changed if we ever use multi-processing using these - * processors, but that is very unlikely... + * helper for cpu_memc_update_all, this clears out all + * mappings, setting them close to the top of memory, + * and inaccessible (0x01f00000). + * Params : r0 = page table pointer */ -_arm2_xchg_1: mov r2, pc - orr r2, r2, #I_BIT - teqp r2, #0 - ldrb r2, [r1] - strb r0, [r1] - mov r0, r2 - movs pc, lr +clear_tables: ldr r1, _arm3_set_pgd - 4 + ldr r2, [r1] + sub r1, r0, #256 * 4 @ start of MEMC tables + add r2, r1, r2, lsl #2 @ end of tables + mov r3, #0x03f00000 @ Default mapping (null mapping) + orr r3, r3, #0x00000f00 + orr r4, r3, #1 + orr r5, r3, #2 + orr ip, r3, #3 +1: stmia r1!, {r3, r4, r5, ip} + add r3, r3, #4 + add r4, r4, #4 + add r5, r5, #4 + add ip, ip, #4 + stmia r1!, {r3, r4, r5, ip} + add r3, r3, #4 + add r4, r4, #4 + add r5, r5, #4 + add ip, ip, #4 + teq r1, r2 + bne 1b + mov pc, lr /* - * Function: arm2_xchg_4 (int new, volatile void *ptr) - * - * Params : new New value to store at... - * : ptr pointer to word-wide location - * - * Purpose : Performs an exchange operation - * - * Returns : Original word data at 'ptr' - * - * Notes : This will have to be changed if we ever use multi-processing using these - * processors, but that is very unlikely... + * Function: *_set_pgd(pgd_t *pgd) + * Params : pgd New page tables/MEMC mapping + * Purpose : update MEMC hardware with new mapping */ -_arm2_xchg_4: mov r2, pc - orr r2, r2, #I_BIT - teqp r2, #0 + .word SYMBOL_NAME(page_nr) +_arm3_set_pgd: mcr p15, 0, r1, c1, c0, 0 @ flush cache +_arm2_set_pgd: stmfd sp!, {lr} + ldr r1, _arm3_set_pgd - 4 ldr r2, [r1] - str r0, [r1] - mov r0, r2 -/* - * fall through - */ -/* - * Function: arm2_proc_init (void) - * : arm2_proc_fin (void) - * - * Purpose : Initialise / finalise processor specifics (none required) - */ -_arm2_proc_init: -_arm2_proc_fin: movs pc, lr -/* - * Function: arm3_switch_to (struct task_struct *prev, struct task_struct *next) - * Params : prev Old task structure - * : next New task structure for process to run - * Returns : prev - * Purpose : Perform a task switch, saving the old processes state, and restoring - * the new. - * Notes : We don't fiddle with the FP registers here - we postpone this until - * the new task actually uses FP. This way, we don't swap FP for tasks - * that do not require it. - */ -_arm3_switch_to: - stmfd sp!, {r4 - r9, fp, lr} @ Store most regs on stack - str sp, [r0, #TSS_SAVE] @ Save sp_SVC - ldr sp, [r1, #TSS_SAVE] @ Get saved sp_SVC - mov r4, r1 - add r7, r1, #TSS_MEMCMAP @ Remap MEMC - ldr r1, LC0 - ldr r1, [r1] -1: ldmia r7!, {r2, r3, r5, r6} - strb r2, [r2] - strb r3, [r3] - strb r5, [r5] - strb r6, [r6] - ldmia r7!, {r2, r3, r5, r6} - strb r2, [r2] - strb r3, [r3] - strb r5, [r5] - strb r6, [r6] - subs r1, r1, #8 - bhi 1b - mcr p15, 0, r7, c1, c0, 0 @ flush cache - ldmfd sp!, {r4 - r9, fp, pc}^ @ Load all regs saved previously -/* - * Function: arm3_remap_memc (struct task_struct *tsk) - * - * Params : tsk Task structure specifing the new mapping structure - * - * Purpose : remap MEMC tables - */ -_arm3_remap_memc: - stmfd sp!, {lr} - add r0, r0, #TSS_MEMCMAP - ldr r1, LC0 - ldr r1, [r1] + sub r0, r0, #256 * 4 @ start of MEMC tables + add r1, r0, r2, lsl #2 @ end of tables 1: ldmia r0!, {r2, r3, ip, lr} strb r2, [r2] strb r3, [r3] @@ -365,14 +200,12 @@ _arm3_remap_memc: strb r3, [r3] strb ip, [ip] strb lr, [lr] - subs r1, r1, #8 - bhi 1b - mcr p15, 0, r0, c1, c0, 0 @ flush cache + teq r0, r1 + bne 1b ldmfd sp!, {pc}^ /* - * Function: arm3_proc_init (void) - * + * Function: *_proc_init (void) * Purpose : Initialise the cache control registers */ _arm3_proc_init: @@ -386,43 +219,55 @@ _arm3_proc_init: mcr p15, 0, r0, c1, c0 @ ARM3 Flush mov r0, #3 mcr p15, 0, r0, c2, c0 @ ARM3 Control +_arm2_proc_init: movs pc, lr /* - * Function: arm3_proc_fin (void) - * + * Function: *_proc_fin (void) * Purpose : Finalise processor (disable caches) */ _arm3_proc_fin: mov r0, #2 mcr p15, 0, r0, c2, c0 - movs pc, lr +_arm2_proc_fin: orrs pc, lr, #I_BIT|F_BIT /* - * Function: arm3_xchg_1 (int new, volatile void *ptr) - * + * Function: *_xchg_1 (int new, volatile void *ptr) * Params : new New value to store at... * : ptr pointer to byte-wide location - * * Purpose : Performs an exchange operation - * * Returns : Original byte data at 'ptr' */ +_arm2_xchg_1: mov r2, pc + orr r2, r2, #I_BIT + teqp r2, #0 + ldrb r2, [r1] + strb r0, [r1] + mov r0, r2 + movs pc, lr + _arm3_xchg_1: swpb r0, r0, [r1] movs pc, lr /* - * Function: arm3_xchg_4 (int new, volatile void *ptr) - * + * Function: *_xchg_4 (int new, volatile void *ptr) * Params : new New value to store at... * : ptr pointer to word-wide location - * * Purpose : Performs an exchange operation - * * Returns : Original word data at 'ptr' */ +_arm2_xchg_4: mov r2, pc + orr r2, r2, #I_BIT + teqp r2, #0 + ldr r2, [r1] + str r0, [r1] + mov r0, r2 + movs pc, lr + _arm3_xchg_4: swp r0, r0, [r1] movs pc, lr +_arm2_3_check_bugs: + bics pc, lr, #0x04000000 @ Clear FIQ disable bit armvlsi_name: .asciz "ARM/VLSI" _arm2_name: .asciz "arm2" @@ -436,57 +281,42 @@ _arm3_name: .asciz "arm3" */ .globl SYMBOL_NAME(arm2_processor_functions) SYMBOL_NAME(arm2_processor_functions): - .word _arm2_switch_to @ 4 - .word _arm2_3_data_abort @ 8 - .word _arm2_3_check_bugs @ 12 - .word _arm2_proc_init @ 16 - .word _arm2_proc_fin @ 20 - - .word _arm2_remap_memc @ 24 - .word _arm2_3_update_map @ 28 - .word _arm2_3_update_cache @ 32 - .word _arm2_xchg_1 @ 36 - .word SYMBOL_NAME(abort) @ 40 - .word _arm2_xchg_4 @ 44 - - .globl SYMBOL_NAME(arm250_processor_functions) -SYMBOL_NAME(arm250_processor_functions): - .word _arm2_switch_to @ 4 - .word _arm2_3_data_abort @ 8 - .word _arm2_3_check_bugs @ 12 - .word _arm2_proc_init @ 16 - .word _arm2_proc_fin @ 20 - - .word _arm2_remap_memc @ 24 - .word _arm2_3_update_map @ 28 - .word _arm2_3_update_cache @ 32 - .word _arm3_xchg_1 @ 36 - .word SYMBOL_NAME(abort) @ 40 - .word _arm3_xchg_4 @ 44 - - .globl SYMBOL_NAME(arm3_processor_functions) -SYMBOL_NAME(arm3_processor_functions): - .word _arm3_switch_to @ 4 - .word _arm2_3_data_abort @ 8 - .word _arm2_3_check_bugs @ 12 - .word _arm3_proc_init @ 16 - .word _arm3_proc_fin @ 20 - - .word _arm3_remap_memc @ 24 - .word _arm2_3_update_map @ 28 - .word _arm2_3_update_cache @ 32 - .word _arm3_xchg_1 @ 36 - .word SYMBOL_NAME(abort) @ 40 - .word _arm3_xchg_4 @ 44 + .word _arm2_3_check_bugs + .word _arm2_proc_init + .word _arm2_proc_fin + .word _arm2_set_pgd + .word _arm2_xchg_1 + .word SYMBOL_NAME(abort) + .word _arm2_xchg_4 cpu_arm2_info: .long armvlsi_name .long _arm2_name + .globl SYMBOL_NAME(arm250_processor_functions) +SYMBOL_NAME(arm250_processor_functions): + .word _arm2_3_check_bugs + .word _arm2_proc_init + .word _arm2_proc_fin + .word _arm2_set_pgd + .word _arm3_xchg_1 + .word SYMBOL_NAME(abort) + .word _arm3_xchg_4 + cpu_arm250_info: .long armvlsi_name .long _arm250_name + .globl SYMBOL_NAME(arm3_processor_functions) +SYMBOL_NAME(arm3_processor_functions): + .word _arm2_3_check_bugs + .word _arm3_proc_init + .word _arm3_proc_fin + .word _arm3_set_pgd + .word _arm3_xchg_1 + .word SYMBOL_NAME(abort) + .word _arm3_xchg_4 + cpu_arm3_info: .long armvlsi_name .long _arm3_name @@ -501,6 +331,8 @@ arm3_elf_name: .asciz "v2" .long 0x41560200 .long 0xfffffff0 + .long 0 + mov pc, lr .long arm2_arch_name .long arm2_elf_name .long 0 @@ -509,6 +341,8 @@ arm3_elf_name: .asciz "v2" .long 0x41560250 .long 0xfffffff0 + .long 0 + mov pc, lr .long arm3_arch_name .long arm3_elf_name .long 0 @@ -517,6 +351,8 @@ arm3_elf_name: .asciz "v2" .long 0x41560300 .long 0xfffffff0 + .long 0 + mov pc, lr .long arm3_arch_name .long arm3_elf_name .long 0 diff --git a/arch/arm/mm/proc-arm6,7.S b/arch/arm/mm/proc-arm6,7.S index 67fb0aa8b..f3819fa01 100644 --- a/arch/arm/mm/proc-arm6,7.S +++ b/arch/arm/mm/proc-arm6,7.S @@ -9,6 +9,7 @@ #include <linux/linkage.h> #include <asm/assembler.h> #include <asm/procinfo.h> +#include <asm/errno.h> #include "../lib/constants.h" /* @@ -70,6 +71,19 @@ ENTRY(cpu_arm7_flush_tlb_area) mov pc, lr /* + * Function: arm6_7_flush_tlb_page (unsigned long address, int flags) + * + * Params : address Address + * : flags b0 = I-TLB as well + * + * Purpose : flush a TLB entry + */ +ENTRY(cpu_arm6_flush_tlb_page) +ENTRY(cpu_arm7_flush_tlb_page) + mcr p15, 0, r0, c6, c0, 0 @ flush TLB + mov pc, lr + +/* * Function: arm6_7_data_abort () * * Params : r0 = address of aborted instruction @@ -89,6 +103,16 @@ msg: .ascii "DA*%p=%p\n\0" .align ENTRY(cpu_arm6_data_abort) +Ldata_simple: + ldr r4, [r0] @ read instruction causing problem + mov r2, r4, lsr #19 @ r2 b1 = L + and r2, r2, #2 @ check read/write bit + mrc p15, 0, r0, c6, c0, 0 @ get FAR + mrc p15, 0, r1, c5, c0, 0 @ get FSR + and r1, r1, #15 + mov pc, lr + +ENTRY(cpu_arm7_data_abort) ldr r4, [r0] @ read instruction causing problem mov r2, r4, lsr #19 @ r2 b1 = L and r1, r4, #15 << 24 @@ -98,10 +122,10 @@ ENTRY(cpu_arm6_data_abort) b Ldata_unknown b Ldata_unknown b Ldata_unknown - b Ldata_earlyldrpost @ ldr rd, [rn], #m - b Ldata_simple @ ldr rd, [rn, #m] @ RegVal - b Ldata_earlyldrpost @ ldr rd, [rn], rm - b Ldata_simple @ ldr rd, [rn, rm] + b Ldata_lateldrpostconst @ ldr rd, [rn], #m + b Ldata_lateldrpreconst @ ldr rd, [rn, #m] @ RegVal + b Ldata_lateldrpostreg @ ldr rd, [rn], rm + b Ldata_lateldrprereg @ ldr rd, [rn, rm] b Ldata_ldmstm @ ldm*a rn, <rlist> b Ldata_ldmstm @ ldm*b rn, <rlist> b Ldata_unknown @@ -119,29 +143,6 @@ Ldata_unknown: @ Part of jumptable bl SYMBOL_NAME(panic) Lstop: b Lstop -ENTRY(cpu_arm7_data_abort) - ldr r4, [r0] @ read instruction causing problem - mov r2, r4, lsr #19 @ r2 b1 = L - and r1, r4, #15 << 24 - add pc, pc, r1, lsr #22 @ Now branch to the relevent processing routine - movs pc, lr - b Ldata_unknown - b Ldata_unknown - b Ldata_unknown - b Ldata_unknown - b Ldata_lateldrpostconst @ ldr rd, [rn], #m - b Ldata_lateldrpreconst @ ldr rd, [rn, #m] @ RegVal - b Ldata_lateldrpostreg @ ldr rd, [rn], rm - b Ldata_lateldrprereg @ ldr rd, [rn, rm] - b Ldata_ldmstm @ ldm*a rn, <rlist> - b Ldata_ldmstm @ ldm*b rn, <rlist> - b Ldata_unknown - b Ldata_unknown - b Ldata_simple @ ldc rd, [rn], #m @ Same as ldr rd, [rn], #m - b Ldata_simple @ ldc rd, [rn, #m] - b Ldata_unknown - b Ldata_unknown - Ldata_ldmstm: tst r4, #1 << 21 @ check writeback bit beq Ldata_simple @@ -165,31 +166,16 @@ Ldata_ldmstm: tst r4, #1 << 21 @ check writeback bit add r7, r0, r7, lsl #2 @ Do correction (signed) str r7, [sp, r5, lsr #14] @ Put register -Ldata_simple: and r2, r2, #2 @ check read/write bit - mrc p15, 0, r0, c6, c0, 0 @ get FAR - mrc p15, 0, r1, c5, c0, 0 @ get FSR - and r1, r1, #15 - mov pc, lr - -Ldata_earlyldrpost: - tst r2, #4 - and r2, r2, #2 @ check read/write bit - orrne r2, r2, #1 @ T bit - mrc p15, 0, r0, c6, c0, 0 @ get FAR - mrc p15, 0, r1, c5, c0, 0 @ get FSR - and r1, r1, #15 - mov pc, lr - Ldata_lateldrpostconst: movs r1, r4, lsl #20 @ Get offset - beq Ldata_earlyldrpost @ if offset is zero, no effect + beq Ldata_simple @ if offset is zero, no effect and r5, r4, #15 << 16 @ Get Rn ldr r0, [sp, r5, lsr #14] tst r4, #1 << 23 @ U bit subne r0, r0, r1, lsr #20 addeq r0, r0, r1, lsr #20 str r0, [sp, r5, lsr #14] @ Put register - b Ldata_earlyldrpost + b Ldata_simple Ldata_lateldrpreconst: tst r4, #1 << 21 @ check writeback bit @@ -252,7 +238,7 @@ Ldata_lateldrpostreg: subne r0, r0, r1 addeq r0, r0, r1 str r0, [sp, r5, lsr #14] @ Put register - b Ldata_earlyldrpost + b Ldata_simple Ldata_lateldrprereg: tst r4, #1 << 21 @ check writeback bit @@ -319,10 +305,24 @@ ENTRY(cpu_arm7_check_bugs) mrs ip, cpsr bic ip, ip, #F_BIT msr cpsr, ip + mov pc, lr + ENTRY(cpu_arm6_proc_init) ENTRY(cpu_arm7_proc_init) + mov pc, lr + ENTRY(cpu_arm6_proc_fin) ENTRY(cpu_arm7_proc_fin) + mrs r0, cpsr + orr r0, r0, #F_BIT | I_BIT + msr cpsr, r0 + mov r0, #0x31 @ ....S..DP...M + mcr p15, 0, r0, c1, c0, 0 @ disable caches + mov pc, lr + +ENTRY(cpu_arm6_do_idle) +ENTRY(cpu_arm7_do_idle) + mov r0, #-EINVAL mov pc, lr /* @@ -381,23 +381,22 @@ ENTRY(cpu_arm6_set_pte) ENTRY(cpu_arm7_set_pte) str r1, [r0], #-1024 @ linux version + eor r1, r1, #LPTE_PRESENT | LPTE_YOUNG | LPTE_WRITE | LPTE_DIRTY + bic r2, r1, #0xff0 bic r2, r2, #3 orr r2, r2, #HPTE_TYPE_SMALL - tst r1, #LPTE_USER | LPTE_EXEC + tst r1, #LPTE_USER | LPTE_EXEC @ User or Exec? orrne r2, r2, #HPTE_AP_READ - tst r1, #LPTE_WRITE - tstne r1, #LPTE_DIRTY - orrne r2, r2, #HPTE_AP_WRITE + tst r1, #LPTE_WRITE | LPTE_DIRTY @ Write and Dirty? + orreq r2, r2, #HPTE_AP_WRITE - tst r1, #LPTE_PRESENT - tstne r1, #LPTE_YOUNG - moveq r2, #0 + tst r1, #LPTE_PRESENT | LPTE_YOUNG @ Present and Young + movne r2, #0 str r2, [r0] @ hardware version - mcr p15, 0, r0, c7, c10, 1 @ clean D entry (drain is done by TLB fns) mov pc, lr /* @@ -407,13 +406,9 @@ ENTRY(cpu_arm7_set_pte) */ ENTRY(cpu_arm6_reset) ENTRY(cpu_arm7_reset) - mrs r1, cpsr - orr r1, r1, #F_BIT|I_BIT - msr cpsr, r1 mov r0, #0 mcr p15, 0, r0, c7, c0, 0 @ flush cache mcr p15, 0, r0, c5, c0, 0 @ flush TLB - mov r1, #F_BIT | I_BIT | 3 mov pc, lr cpu_armvlsi_name: @@ -428,6 +423,26 @@ cpu_arm710_name: .section ".text.init", #alloc, #execinstr +__arm6_setup: mov r0, #0 + mcr p15, 0, r0, c7, c0 @ flush caches on v3 + mcr p15, 0, r0, c5, c0 @ flush TLBs on v3 + mcr p15, 0, r4, c2, c0 @ load page table pointer + mov r0, #0x1f @ Domains 0, 1 = client + mcr p15, 0, r0, c3, c0 @ load domain access register + mov r0, #0x3d @ ....S..DPWC.M + orr r0, r0, #0x100 + mov pc, lr + +__arm7_setup: mov r0, #0 + mcr p15, 0, r0, c7, c0 @ flush caches on v3 + mcr p15, 0, r0, c5, c0 @ flush TLBs on v3 + mcr p15, 0, r4, c2, c0 @ load page table pointer + mov r0, #0x1f @ Domains 0, 1 = client + mcr p15, 0, r0, c3, c0 @ load domain access register + mov r0, #0x7d @ ....S.LDPWC.M + orr r0, r0, #0x100 + mov pc, lr + /* * Purpose : Function pointers used to access above functions - all calls * come through these @@ -452,6 +467,8 @@ ENTRY(arm6_processor_functions) .word cpu_arm6_flush_icache_area .word cpu_arm6_cache_wback_area .word cpu_arm6_cache_purge_area + .word cpu_arm6_flush_tlb_page + .word cpu_arm7_do_idle .size arm6_processor_functions, . - arm6_processor_functions /* @@ -478,6 +495,8 @@ ENTRY(arm7_processor_functions) .word cpu_arm7_flush_icache_area .word cpu_arm7_cache_wback_area .word cpu_arm7_cache_purge_area + .word cpu_arm7_flush_tlb_page + .word cpu_arm7_do_idle .size arm7_processor_functions, . - arm7_processor_functions .type cpu_arm6_info, #object @@ -519,9 +538,11 @@ cpu_elf_name: .asciz "v3" __arm6_proc_info: .long 0x41560600 .long 0xfffffff0 + .long 0x00000c12 + b __arm6_setup .long cpu_arch_name .long cpu_elf_name - .long HWCAP_SWP + .long HWCAP_SWP | HWCAP_26BIT .long cpu_arm6_info .long arm6_processor_functions .size __arm6_proc_info, . - __arm6_proc_info @@ -530,9 +551,11 @@ __arm6_proc_info: __arm610_proc_info: .long 0x41560610 .long 0xfffffff0 + .long 0x00000c12 + b __arm6_setup .long cpu_arch_name .long cpu_elf_name - .long HWCAP_SWP + .long HWCAP_SWP | HWCAP_26BIT .long cpu_arm610_info .long arm6_processor_functions .size __arm610_proc_info, . - __arm610_proc_info @@ -541,9 +564,11 @@ __arm610_proc_info: __arm7_proc_info: .long 0x41007000 .long 0xffffff00 + .long 0x00000c12 + b __arm7_setup .long cpu_arch_name .long cpu_elf_name - .long HWCAP_SWP + .long HWCAP_SWP | HWCAP_26BIT .long cpu_arm7_info .long arm7_processor_functions .size __arm7_proc_info, . - __arm7_proc_info @@ -552,9 +577,11 @@ __arm7_proc_info: __arm710_proc_info: .long 0x41007100 .long 0xfff8ff00 + .long 0x00000c12 + b __arm7_setup .long cpu_arch_name .long cpu_elf_name - .long HWCAP_SWP + .long HWCAP_SWP | HWCAP_26BIT .long cpu_arm710_info .long arm7_processor_functions .size __arm710_proc_info, . - __arm710_proc_info diff --git a/arch/arm/mm/proc-sa110.S b/arch/arm/mm/proc-sa110.S index aecc223af..73c0f83df 100644 --- a/arch/arm/mm/proc-sa110.S +++ b/arch/arm/mm/proc-sa110.S @@ -4,7 +4,7 @@ * (C) 1997-1999 Russell King * * These are the low level assembler for performing cache and TLB - * functions on the sa110. + * functions on the StrongARM-110 and StrongARM-1100 */ #include <linux/linkage.h> #include <asm/assembler.h> @@ -17,6 +17,27 @@ */ #define MAX_AREA_SIZE 32768 + .macro flush_110_dcache rd, ra, re + add \re, \ra, #16384 @ only necessary for 16k +1001: ldr \rd, [\ra], #32 + teq \re, \ra + bne 1001b + .endm + + .macro flush_1100_dcache rd, ra, re + add \re, \ra, #8192 @ only necessary for 8k +1001: ldr \rd, [\ra], #32 + teq \re, \ra + bne 1001b +#ifdef FLUSH_BASE_MINICACHE + add \ra, \ra, #FLUSH_BASE_MINICACHE - FLUSH_BASE + add \re, \ra, #512 @ only 512 bytes +1002: ldr \rd, [\ra], #32 + teq \re, \ra + bne 1002b +#endif + .endm + .data Lclean_switch: .long 0 .text @@ -36,12 +57,27 @@ cpu_sa110_flush_cache_all_r2: eor r1, r1, #1 str r1, [r3] addne ip, ip, #32768 - add r1, ip, #16384 @ only necessary for 16k -1: ldr r3, [ip], #32 - teq r1, ip - bne 1b + flush_110_dcache r3, ip, r1 + mov ip, #0 + teq r2, #0 + mcrne p15, 0, ip, c7, c5, 0 @ flush I cache + mcr p15, 0, ip, c7, c10, 4 @ drain WB + mov pc, lr + + .align 5 +ENTRY(cpu_sa1100_flush_cache_all) @ preserves r0 + mov r2, #1 +cpu_sa1100_flush_cache_all_r2: + ldr r3, =Lclean_switch + ldr ip, =FLUSH_BASE + ldr r1, [r3] + ands r1, r1, #1 + eor r1, r1, #1 + str r1, [r3] + addne ip, ip, #32768 + flush_1100_dcache r3, ip, r1 mov ip, #0 - tst r2, #1 + teq r2, #0 mcrne p15, 0, ip, c7, c5, 0 @ flush I cache mcr p15, 0, ip, c7, c10, 4 @ drain WB mov pc, lr @@ -66,11 +102,17 @@ ENTRY(cpu_sa110_flush_cache_area) add r0, r0, #32 cmp r0, r1 blt 1b - tst r2, #1 + teq r2, #0 movne r0, #0 mcrne p15, 0, r0, c7, c5, 0 @ flush I cache mov pc, lr +ENTRY(cpu_sa1100_flush_cache_area) + sub r3, r1, r0 + cmp r3, #MAX_AREA_SIZE + bgt cpu_sa1100_flush_cache_all_r2 + b 1b + /* * Function: sa110_cache_wback_area(unsigned long address, unsigned long end) * Params : address Area start address @@ -94,6 +136,13 @@ ENTRY(cpu_sa110_cache_wback_area) mcr p15, 0, r2, c7, c10, 4 @ drain WB mov pc, lr +ENTRY(cpu_sa1100_cache_wback_area) + sub r3, r1, r0 + cmp r3, #MAX_AREA_SIZE + mov r2, #0 + bgt cpu_sa1100_flush_cache_all_r2 + bic r0, r0, #31 + b 1b /* * Function: sa110_cache_purge_area(unsigned long address, unsigned long end) * Params : address Area start address @@ -105,6 +154,7 @@ ENTRY(cpu_sa110_cache_wback_area) */ .align 5 ENTRY(cpu_sa110_cache_purge_area) +ENTRY(cpu_sa1100_cache_purge_area) tst r0, #31 bic r0, r0, #31 mcrne p15, 0, r0, c7, c10, 1 @ clean D entry @@ -123,6 +173,7 @@ ENTRY(cpu_sa110_cache_purge_area) */ .align 5 ENTRY(cpu_sa110_flush_cache_entry) +ENTRY(cpu_sa1100_flush_cache_entry) mov r1, #0 mcr p15, 0, r0, c7, c10, 1 @ clean D entry mcr p15, 0, r1, c7, c10, 4 @ drain WB @@ -136,6 +187,7 @@ ENTRY(cpu_sa110_flush_cache_entry) * for page table purposes. */ ENTRY(cpu_sa110_clean_cache_area) +ENTRY(cpu_sa1100_clean_cache_area) 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry (drain is done by TLB fns) add r0, r0, #32 subs r1, r1, #32 @@ -149,6 +201,7 @@ ENTRY(cpu_sa110_clean_cache_area) */ .align 5 ENTRY(cpu_sa110_flush_ram_page) +ENTRY(cpu_sa1100_flush_ram_page) mov r1, #4096 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry add r0, r0, #32 @@ -170,6 +223,7 @@ ENTRY(cpu_sa110_flush_ram_page) */ .align 5 ENTRY(cpu_sa110_flush_tlb_all) +ENTRY(cpu_sa1100_flush_tlb_all) mov r0, #0 mcr p15, 0, r0, c7, c10, 4 @ drain WB mcr p15, 0, r0, c8, c7, 0 @ flush I & D tlbs @@ -184,6 +238,7 @@ ENTRY(cpu_sa110_flush_tlb_all) */ .align 5 ENTRY(cpu_sa110_flush_tlb_area) +ENTRY(cpu_sa1100_flush_tlb_area) mov r3, #0 mcr p15, 0, r3, c7, c10, 4 @ drain WB 1: cmp r0, r1 @@ -193,12 +248,35 @@ ENTRY(cpu_sa110_flush_tlb_area) mcrlt p15, 0, r0, c8, c6, 1 @ flush D TLB entry addlt r0, r0, #4096 blt 1b - tst r2, #1 + teq r2, #0 mcrne p15, 0, r3, c8, c5, 0 @ flush I TLB mov pc, lr +/* + * Function: sa110_flush_tlb_page (unsigned long address, int flags) + * Params : address Address to flush + * : flags b0 = I-TLB as well + * Purpose : flush a TLB entry + */ + .align 5 +ENTRY(cpu_sa110_flush_tlb_page) +ENTRY(cpu_sa1100_flush_tlb_page) + mov r3, #0 + mcr p15, 0, r3, c7, c10, 4 @ drain WB + mcr p15, 0, r0, c8, c6, 1 @ flush D TLB entry + teq r1, #0 + mcrne p15, 0, r3, c8, c5, 0 @ flush I TLB + mov pc, lr + +/* + * Function: sa110_flush_icache_area (unsigned long address, unsigned long size) + * Params : address Address of area to flush + * : size Size of area to flush + * Purpose : flush an area from the Icache + */ .align 5 ENTRY(cpu_sa110_flush_icache_area) +ENTRY(cpu_sa1100_flush_icache_area) 1: mcr p15, 0, r0, c7, c10, 1 @ Clean D entry add r0, r0, #32 subs r1, r1, #32 @@ -218,6 +296,7 @@ ENTRY(cpu_sa110_flush_icache_area) */ .align 5 ENTRY(cpu_sa110_data_abort) +ENTRY(cpu_sa1100_data_abort) ldr r2, [r0] @ read instruction causing problem mrc p15, 0, r0, c6, c0, 0 @ get FAR mov r2, r2, lsr #19 @ b1 = L @@ -237,16 +316,30 @@ ENTRY(cpu_sa110_data_abort) .align 5 ENTRY(cpu_sa110_set_pgd) ldr r3, =Lclean_switch + ldr ip, =FLUSH_BASE ldr r2, [r3] ands r2, r2, #1 eor r2, r2, #1 str r2, [r3] - ldr r2, =FLUSH_BASE - addne r2, r2, #32768 - add r1, r2, #16384 @ only necessary for 16k -1: ldr r3, [r2], #32 - teq r1, r2 - bne 1b + addne ip, ip, #32768 + flush_110_dcache r3, ip, r1 + mov r1, #0 + mcr p15, 0, r1, c7, c5, 0 @ flush I cache + mcr p15, 0, r1, c7, c10, 4 @ drain WB + mcr p15, 0, r0, c2, c0, 0 @ load page table pointer + mcr p15, 0, r1, c8, c7, 0 @ flush TLBs + mov pc, lr + + .align 5 +ENTRY(cpu_sa1100_set_pgd) + ldr r3, =Lclean_switch + ldr ip, =FLUSH_BASE + ldr r2, [r3] + ands r2, r2, #1 + eor r2, r2, #1 + str r2, [r3] + addne ip, ip, #32768 + flush_1100_dcache r3, ip, r1 mov r1, #0 mcr p15, 0, r1, c7, c5, 0 @ flush I cache mcr p15, 0, r1, c7, c10, 4 @ drain WB @@ -262,9 +355,10 @@ ENTRY(cpu_sa110_set_pgd) */ .align 5 ENTRY(cpu_sa110_set_pmd) +ENTRY(cpu_sa1100_set_pmd) str r1, [r0] mcr p15, 0, r0, c7, c10, 1 @ clean D entry - mcr p15, 0, r0, c7, c10, 4 @ drain WB (TLB bypasses WB) + mcr p15, 0, r0, c7, c10, 4 @ drain WB mov pc, lr /* @@ -275,6 +369,7 @@ ENTRY(cpu_sa110_set_pmd) */ .align 5 ENTRY(cpu_sa110_set_pte) +ENTRY(cpu_sa1100_set_pte) str r1, [r0], #-1024 @ linux version eor r1, r1, #LPTE_PRESENT | LPTE_YOUNG | LPTE_WRITE | LPTE_DIRTY @@ -294,8 +389,8 @@ ENTRY(cpu_sa110_set_pte) str r2, [r0] @ hardware version mov r0, r0 - mcr p15, 0, r0, c7, c10, 1 @ clean D entry (drain is done by TLB fns) - mcr p15, 0, r0, c7, c10, 4 @ drain WB (TLB bypasses WB) + mcr p15, 0, r0, c7, c10, 1 @ clean D entry + mcr p15, 0, r0, c7, c10, 4 @ drain WB mov pc, lr /* @@ -305,12 +400,39 @@ ENTRY(cpu_sa110_set_pte) * Notes : This processor does not require these */ ENTRY(cpu_sa110_check_bugs) +ENTRY(cpu_sa1100_check_bugs) mrs ip, cpsr bic ip, ip, #F_BIT msr cpsr, ip + mov pc, lr ENTRY(cpu_sa110_proc_init) +ENTRY(cpu_sa1100_proc_init) + mov r0, #0 + mcr p15, 0, r0, c15, c1, 2 @ Enable clock switching + mov pc, lr + ENTRY(cpu_sa110_proc_fin) +ENTRY(cpu_sa1100_proc_fin) + mrs r0, cpsr + orr r0, r0, #F_BIT | I_BIT + msr cpsr, r0 + mov r0, #0 + mcr p15, 0, r0, c15, c2, 2 @ Disable clock switching + mrc p15, 0, r0, c1, c0, 0 + bic r0, r0, #0x1100 @ ...i...s........ + bic r0, r0, #0x000e @ ............wca. + mcr p15, 0, r0, c1, c0, 0 @ disable caches + mov pc, lr + + .align 5 +ENTRY(cpu_sa110_do_idle) +ENTRY(cpu_sa1100_do_idle) + mov r0, #0 + mcr p15, 0, r0, c15, c2, 2 @ Disable clock switching + @ load from uncacheable loc? + mcr p15, 0, r0, c15, c8, 2 @ Wait for interrupt + mcr p15, 0, r0, c15, c1, 2 @ Enable clock switching mov pc, lr /* @@ -318,17 +440,13 @@ ENTRY(cpu_sa110_proc_fin) * Notes : This sets up everything for a reset */ ENTRY(cpu_sa110_reset) - mrs r1, cpsr - orr r1, r1, #F_BIT | I_BIT - msr cpsr, r1 +ENTRY(cpu_sa1100_reset) stmfd sp!, {r1, lr} - mov r2, #1 bl cpu_sa110_flush_cache_all bl cpu_sa110_flush_tlb_all mcr p15, 0, ip, c7, c7, 0 @ flush I,D caches mrc p15, 0, r0, c1, c0, 0 @ ctrl register - bic r0, r0, #0x1800 - bic r0, r0, #0x000f + bic r0, r0, #1 @ ...............m ldmfd sp!, {r1, pc} /* * Purpose : Function pointers used to access above functions - all calls @@ -338,10 +456,26 @@ ENTRY(cpu_sa110_reset) cpu_manu_name: .asciz "Intel" ENTRY(cpu_sa110_name) .asciz "sa110" +ENTRY(cpu_sa1100_name) + .asciz "sa1100" .align .section ".text.init", #alloc, #execinstr +__sa110_setup: mov r0, #0 + mcr p15, 0, r0, c7, c7 @ flush I,D caches on v4 + mcr p15, 0, r0, c7, c10, 4 @ drain write buffer on v4 + mcr p15, 0, r0, c8, c7 @ flush I,D TLBs on v4 + mcr p15, 0, r4, c2, c0 @ load page table pointer + mov r0, #0x1f @ Domains 0, 1 = client + mcr p15, 0, r0, c3, c0 @ load domain access register + mrc p15, 0, r0, c1, c0 @ get control register v4 + bic r0, r0, #0x0e00 @ ....??r......... + bic r0, r0, #0x0002 @ ..............a. + orr r0, r0, #0x003d @ ..........DPWC.M + orr r0, r0, #0x1100 @ ...I...S........ + mov pc, lr + .type sa110_processor_functions, #object ENTRY(sa110_processor_functions) .word cpu_sa110_data_abort @@ -362,7 +496,8 @@ ENTRY(sa110_processor_functions) .word cpu_sa110_flush_icache_area .word cpu_sa110_cache_wback_area .word cpu_sa110_cache_purge_area - + .word cpu_sa110_flush_tlb_page + .word cpu_sa110_do_idle .size sa110_processor_functions, . - sa110_processor_functions .type cpu_sa110_info, #object @@ -371,6 +506,36 @@ cpu_sa110_info: .long cpu_sa110_name .size cpu_sa110_info, . - cpu_sa110_info + + .type sa1100_processor_functions, #object +ENTRY(sa1100_processor_functions) + .word cpu_sa1100_data_abort + .word cpu_sa1100_check_bugs + .word cpu_sa1100_proc_init + .word cpu_sa1100_proc_fin + .word cpu_sa1100_flush_cache_all + .word cpu_sa1100_flush_cache_area + .word cpu_sa1100_flush_cache_entry + .word cpu_sa1100_clean_cache_area + .word cpu_sa1100_flush_ram_page + .word cpu_sa1100_flush_tlb_all + .word cpu_sa1100_flush_tlb_area + .word cpu_sa1100_set_pgd + .word cpu_sa1100_set_pmd + .word cpu_sa1100_set_pte + .word cpu_sa1100_reset + .word cpu_sa1100_flush_icache_area + .word cpu_sa1100_cache_wback_area + .word cpu_sa1100_cache_purge_area + .word cpu_sa1100_flush_tlb_page + .word cpu_sa1100_do_idle + .size sa1100_processor_functions, . - sa1100_processor_functions + +cpu_sa1100_info: + .long cpu_manu_name + .long cpu_sa1100_name + .size cpu_sa1100_info, . - cpu_sa1100_info + .type cpu_arch_name, #object cpu_arch_name: .asciz "armv4" .size cpu_arch_name, . - cpu_arch_name @@ -385,9 +550,26 @@ cpu_elf_name: .asciz "v4" __sa110_proc_info: .long 0x4401a100 .long 0xfffffff0 + .long 0x00000c02 + b __sa110_setup .long cpu_arch_name .long cpu_elf_name - .long HWCAP_SWP | HWCAP_HALF + .long HWCAP_SWP | HWCAP_HALF | HWCAP_26BIT .long cpu_sa110_info .long sa110_processor_functions .size __sa110_proc_info, . - __sa110_proc_info + + .type __sa1100_proc_info,#object +__sa1100_proc_info: + .long 0x4401a110 + .long 0xfffffff0 + .long 0x00000c02 + b __sa110_setup + .long cpu_arch_name + .long cpu_elf_name + .long HWCAP_SWP | HWCAP_HALF | HWCAP_26BIT + .long cpu_sa1100_info + .long sa1100_processor_functions + .size __sa1100_proc_info, . - __sa1100_proc_info + + diff --git a/arch/arm/nwfpe/ChangeLog b/arch/arm/nwfpe/ChangeLog index e160d36c3..8d76e09ba 100644 --- a/arch/arm/nwfpe/ChangeLog +++ b/arch/arm/nwfpe/ChangeLog @@ -1,10 +1,50 @@ -1998-11-23 Scott Bambrough <scottb@corelcomputer.com> +1999-08-19 Scott Bambrough <scottb@netwinder.org> + + * fpmodule.c - Changed version number to 0.95 + * fpa11.h - modified FPA11, FPREG structures + * fpa11.c - Changes due to FPA11, FPREG structure alterations. + * fpa11_cpdo.c - Changes due to FPA11, FPREG structure alterations. + * fpa11_cpdt.c - Changes due to FPA11, FPREG structure alterations. + * fpa11_cprt.c - Changes due to FPA11, FPREG structure alterations. + * single_cpdo.c - Changes due to FPA11, FPREG structure alterations. + * double_cpdo.c - Changes due to FPA11, FPREG structure alterations. + * extended_cpdo.c - Changes due to FPA11, FPREG structure alterations. + + * I discovered several bugs. First and worst is that the kernel + passes in a pointer to the FPE's state area. This is defined + as a struct user_fp (see user.h). This pointer was cast to a + FPA11*. Unfortunately FPA11 and user_fp are of different sizes; + user_fp is smaller. This meant that the FPE scribbled on things + below its area, which is bad, as the area is in the thread_struct + embedded in the process task structure. Thus we were scribbling + over one of the most important structures in the entire OS. + + * user_fp and FPA11 have now been harmonized. Most of the changes + in the above code were dereferencing problems due to moving the + register type out of FPREG, and getting rid of the union variable + fpvalue. + + * Second I noticed resetFPA11 was not always being called for a + task. This should happen on the first floating point exception + that occurs. It is controlled by init_flag in FPA11. The + comment in the code beside init_flag state the kernel guarantees + this to be zero. Not so. I found that the kernel recycles task + structures, and that recycled ones may not have init_flag zeroed. + I couldn't even find anything that guarantees it is zeroed when + when the task structure is initially allocated. In any case + I now initialize the entire FPE state in the thread structure to + zero when allocated and recycled. See alloc_task_struct() and + flush_thread() in arch/arm/process.c. The change to + alloc_task_struct() may not be necessary, but I left it in for + completeness (better safe than sorry). + +1998-11-23 Scott Bambrough <scottb@netwinder.org> * README.FPE - fix typo in description of lfm/sfm instructions * NOTES - Added file to describe known bugs/problems * fpmodule.c - Changed version number to 0.94 -1998-11-20 Scott Bambrough <scottb@corelcomputer.com> +1998-11-20 Scott Bambrough <scottb@netwinder.org> * README.FPE - fix description of URD, NRM instructions * TODO - remove URD, NRM instructions from TODO list @@ -12,7 +52,7 @@ * double_cpdo.c - implement URD, NRM * extended_cpdo.c - implement URD, NRM -1998-11-19 Scott Bambrough <scottb@corelcomputer.com> +1998-11-19 Scott Bambrough <scottb@netwinder.org> * ChangeLog - Added this file to track changes made. * fpa11.c - added code to initialize register types to typeNone diff --git a/arch/arm/nwfpe/Makefile b/arch/arm/nwfpe/Makefile index 5db79c6d4..a14371e5d 100644 --- a/arch/arm/nwfpe/Makefile +++ b/arch/arm/nwfpe/Makefile @@ -14,16 +14,15 @@ else NWFPE_OBJS += entry.o endif -L_TARGET := math-emu.a - ifeq ($(CONFIG_NWFPE),y) -L_OBJS = $(NWFPE_OBJS) +O_TARGET := math-emu.o +O_OBJS = $(NWFPE_OBJS) else ifeq ($(CONFIG_NWFPE),m) M_OBJS = nwfpe.o MI_OBJS = $(NWFPE_OBJS) endif -endif +endif include $(TOPDIR)/Rules.make diff --git a/arch/arm/nwfpe/double_cpdo.c b/arch/arm/nwfpe/double_cpdo.c index 3db9e7066..0415f31e0 100644 --- a/arch/arm/nwfpe/double_cpdo.c +++ b/arch/arm/nwfpe/double_cpdo.c @@ -19,7 +19,6 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include "config.h" #include "softfloat.h" #include "fpopcode.h" #include "fpa11.h" @@ -54,14 +53,14 @@ unsigned int DoubleCPDO(const unsigned int opcode) } else { - switch (fpa11->fpreg[Fm].fType) + switch (fpa11->fType[Fm]) { case typeSingle: - rFm = float32_to_float64(fpa11->fpreg[Fm].fValue.fSingle); + rFm = float32_to_float64(fpa11->fpreg[Fm].fSingle); break; case typeDouble: - rFm = fpa11->fpreg[Fm].fValue.fDouble; + rFm = fpa11->fpreg[Fm].fDouble; break; case typeExtended: @@ -80,14 +79,14 @@ unsigned int DoubleCPDO(const unsigned int opcode) if (!MONADIC_INSTRUCTION(opcode)) { Fn = getFn(opcode); - switch (fpa11->fpreg[Fn].fType) + switch (fpa11->fType[Fn]) { case typeSingle: - rFn = float32_to_float64(fpa11->fpreg[Fn].fValue.fSingle); + rFn = float32_to_float64(fpa11->fpreg[Fn].fSingle); break; case typeDouble: - rFn = fpa11->fpreg[Fn].fValue.fDouble; + rFn = fpa11->fpreg[Fn].fDouble; break; default: return 0; @@ -100,62 +99,62 @@ unsigned int DoubleCPDO(const unsigned int opcode) { /* dyadic opcodes */ case ADF_CODE: - fpa11->fpreg[Fd].fValue.fDouble = float64_add(rFn,rFm); + fpa11->fpreg[Fd].fDouble = float64_add(rFn,rFm); break; case MUF_CODE: case FML_CODE: - fpa11->fpreg[Fd].fValue.fDouble = float64_mul(rFn,rFm); + fpa11->fpreg[Fd].fDouble = float64_mul(rFn,rFm); break; - case SUF_CODE: - fpa11->fpreg[Fd].fValue.fDouble = float64_sub(rFn,rFm); + case SUF_CODE: + fpa11->fpreg[Fd].fDouble = float64_sub(rFn,rFm); break; case RSF_CODE: - fpa11->fpreg[Fd].fValue.fDouble = float64_sub(rFm,rFn); + fpa11->fpreg[Fd].fDouble = float64_sub(rFm,rFn); break; case DVF_CODE: case FDV_CODE: - fpa11->fpreg[Fd].fValue.fDouble = float64_div(rFn,rFm); + fpa11->fpreg[Fd].fDouble = float64_div(rFn,rFm); break; case RDF_CODE: case FRD_CODE: - fpa11->fpreg[Fd].fValue.fDouble = float64_div(rFm,rFn); + fpa11->fpreg[Fd].fDouble = float64_div(rFm,rFn); break; #if 0 case POW_CODE: - fpa11->fpreg[Fd].fValue.fDouble = float64_pow(rFn,rFm); + fpa11->fpreg[Fd].fDouble = float64_pow(rFn,rFm); break; case RPW_CODE: - fpa11->fpreg[Fd].fValue.fDouble = float64_pow(rFm,rFn); + fpa11->fpreg[Fd].fDouble = float64_pow(rFm,rFn); break; #endif case RMF_CODE: - fpa11->fpreg[Fd].fValue.fDouble = float64_rem(rFn,rFm); + fpa11->fpreg[Fd].fDouble = float64_rem(rFn,rFm); break; #if 0 case POL_CODE: - fpa11->fpreg[Fd].fValue.fDouble = float64_pol(rFn,rFm); + fpa11->fpreg[Fd].fDouble = float64_pol(rFn,rFm); break; #endif /* monadic opcodes */ case MVF_CODE: - fpa11->fpreg[Fd].fValue.fDouble = rFm; + fpa11->fpreg[Fd].fDouble = rFm; break; case MNF_CODE: { unsigned int *p = (unsigned int*)&rFm; p[1] ^= 0x80000000; - fpa11->fpreg[Fd].fValue.fDouble = rFm; + fpa11->fpreg[Fd].fDouble = rFm; } break; @@ -163,55 +162,55 @@ unsigned int DoubleCPDO(const unsigned int opcode) { unsigned int *p = (unsigned int*)&rFm; p[1] &= 0x7fffffff; - fpa11->fpreg[Fd].fValue.fDouble = rFm; + fpa11->fpreg[Fd].fDouble = rFm; } break; case RND_CODE: case URD_CODE: - fpa11->fpreg[Fd].fValue.fDouble = + fpa11->fpreg[Fd].fDouble = int32_to_float64(float64_to_int32(rFm)); break; case SQT_CODE: - fpa11->fpreg[Fd].fValue.fDouble = float64_sqrt(rFm); + fpa11->fpreg[Fd].fDouble = float64_sqrt(rFm); break; #if 0 case LOG_CODE: - fpa11->fpreg[Fd].fValue.fDouble = float64_log(rFm); + fpa11->fpreg[Fd].fDouble = float64_log(rFm); break; case LGN_CODE: - fpa11->fpreg[Fd].fValue.fDouble = float64_ln(rFm); + fpa11->fpreg[Fd].fDouble = float64_ln(rFm); break; case EXP_CODE: - fpa11->fpreg[Fd].fValue.fDouble = float64_exp(rFm); + fpa11->fpreg[Fd].fDouble = float64_exp(rFm); break; case SIN_CODE: - fpa11->fpreg[Fd].fValue.fDouble = float64_sin(rFm); + fpa11->fpreg[Fd].fDouble = float64_sin(rFm); break; case COS_CODE: - fpa11->fpreg[Fd].fValue.fDouble = float64_cos(rFm); + fpa11->fpreg[Fd].fDouble = float64_cos(rFm); break; case TAN_CODE: - fpa11->fpreg[Fd].fValue.fDouble = float64_tan(rFm); + fpa11->fpreg[Fd].fDouble = float64_tan(rFm); break; case ASN_CODE: - fpa11->fpreg[Fd].fValue.fDouble = float64_arcsin(rFm); + fpa11->fpreg[Fd].fDouble = float64_arcsin(rFm); break; case ACS_CODE: - fpa11->fpreg[Fd].fValue.fDouble = float64_arccos(rFm); + fpa11->fpreg[Fd].fDouble = float64_arccos(rFm); break; case ATN_CODE: - fpa11->fpreg[Fd].fValue.fDouble = float64_arctan(rFm); + fpa11->fpreg[Fd].fDouble = float64_arctan(rFm); break; #endif @@ -224,7 +223,7 @@ unsigned int DoubleCPDO(const unsigned int opcode) } } - if (0 != nRc) fpa11->fpreg[Fd].fType = typeDouble; + if (0 != nRc) fpa11->fType[Fd] = typeDouble; return nRc; } diff --git a/arch/arm/nwfpe/entry.S b/arch/arm/nwfpe/entry.S index 6f0077fbe..f6a9a9799 100644 --- a/arch/arm/nwfpe/entry.S +++ b/arch/arm/nwfpe/entry.S @@ -3,7 +3,7 @@ (c) Corel Computer Corporation, 1998 (c) Philip Blundell 1998-1999 - Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com> + Direct questions, comments to Scott Bambrough <scottb@netwinder.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 @@ -85,14 +85,15 @@ nwfpe_enter: mov r10, lr @ save the failure-return addresses ldr r5, [r4, #60] @ get contents of PC; - ldr r0, [r5, #-4] @ get actual instruction into r0 + sub r8, r5, #4 +.Lx2: ldrt r0, [r8], #0 @ get actual instruction into r0 emulate: bl EmulateAll @ emulate the instruction cmp r0, #0 @ was emulation successful moveq pc, r10 @ no, return failure next: -__x1: ldrt r6, [r5], #4 @ get the next instruction and +.Lx1: ldrt r6, [r5], #4 @ get the next instruction and @ increment PC and r2, r6, #0x0F000000 @ test for FP insns @@ -114,13 +115,15 @@ __x1: ldrt r6, [r5], #4 @ get the next instruction and mov r0, r6 @ prepare for EmulateAll() b emulate @ if r0 != 0, goto EmulateAll - @ We need to be prepared for the instruction at __x1 to fault. - @ Emit the appropriate exception gunk to fix things up. + @ We need to be prepared for the instruction at .Lx1 or .Lx2 + @ to fault. .section .fixup,"ax" .align -__f1: mov pc, r9 +.Lfix: mov pc, r9 .previous + .section __ex_table,"a" .align 3 - .long __x1, __f1 + .long .Lx2, .Lfix + .long .Lx1, .Lfix .previous diff --git a/arch/arm/nwfpe/entry26.S b/arch/arm/nwfpe/entry26.S index 6b1ec3354..5108ce63d 100644 --- a/arch/arm/nwfpe/entry26.S +++ b/arch/arm/nwfpe/entry26.S @@ -3,7 +3,7 @@ (c) Corel Computer Corporation, 1998 (c) Philip Blundell 1998-1999 - Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com> + Direct questions, comments to Scott Bambrough <scottb@netwinder.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 diff --git a/arch/arm/nwfpe/extended_cpdo.c b/arch/arm/nwfpe/extended_cpdo.c index 4f55333fc..810f57102 100644 --- a/arch/arm/nwfpe/extended_cpdo.c +++ b/arch/arm/nwfpe/extended_cpdo.c @@ -19,7 +19,6 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include "config.h" #include "softfloat.h" #include "fpopcode.h" #include "fpa11.h" @@ -52,18 +51,18 @@ unsigned int ExtendedCPDO(const unsigned int opcode) } else { - switch (fpa11->fpreg[Fm].fType) + switch (fpa11->fType[Fm]) { case typeSingle: - rFm = float32_to_floatx80(fpa11->fpreg[Fm].fValue.fSingle); + rFm = float32_to_floatx80(fpa11->fpreg[Fm].fSingle); break; case typeDouble: - rFm = float64_to_floatx80(fpa11->fpreg[Fm].fValue.fDouble); + rFm = float64_to_floatx80(fpa11->fpreg[Fm].fDouble); break; case typeExtended: - rFm = fpa11->fpreg[Fm].fValue.fExtended; + rFm = fpa11->fpreg[Fm].fExtended; break; default: return 0; @@ -73,18 +72,18 @@ unsigned int ExtendedCPDO(const unsigned int opcode) if (!MONADIC_INSTRUCTION(opcode)) { Fn = getFn(opcode); - switch (fpa11->fpreg[Fn].fType) + switch (fpa11->fType[Fn]) { case typeSingle: - rFn = float32_to_floatx80(fpa11->fpreg[Fn].fValue.fSingle); + rFn = float32_to_floatx80(fpa11->fpreg[Fn].fSingle); break; case typeDouble: - rFn = float64_to_floatx80(fpa11->fpreg[Fn].fValue.fDouble); + rFn = float64_to_floatx80(fpa11->fpreg[Fn].fDouble); break; case typeExtended: - rFn = fpa11->fpreg[Fn].fValue.fExtended; + rFn = fpa11->fpreg[Fn].fExtended; break; default: return 0; @@ -96,112 +95,112 @@ unsigned int ExtendedCPDO(const unsigned int opcode) { /* dyadic opcodes */ case ADF_CODE: - fpa11->fpreg[Fd].fValue.fExtended = floatx80_add(rFn,rFm); + fpa11->fpreg[Fd].fExtended = floatx80_add(rFn,rFm); break; case MUF_CODE: case FML_CODE: - fpa11->fpreg[Fd].fValue.fExtended = floatx80_mul(rFn,rFm); + fpa11->fpreg[Fd].fExtended = floatx80_mul(rFn,rFm); break; case SUF_CODE: - fpa11->fpreg[Fd].fValue.fExtended = floatx80_sub(rFn,rFm); + fpa11->fpreg[Fd].fExtended = floatx80_sub(rFn,rFm); break; case RSF_CODE: - fpa11->fpreg[Fd].fValue.fExtended = floatx80_sub(rFm,rFn); + fpa11->fpreg[Fd].fExtended = floatx80_sub(rFm,rFn); break; case DVF_CODE: case FDV_CODE: - fpa11->fpreg[Fd].fValue.fExtended = floatx80_div(rFn,rFm); + fpa11->fpreg[Fd].fExtended = floatx80_div(rFn,rFm); break; case RDF_CODE: case FRD_CODE: - fpa11->fpreg[Fd].fValue.fExtended = floatx80_div(rFm,rFn); + fpa11->fpreg[Fd].fExtended = floatx80_div(rFm,rFn); break; #if 0 case POW_CODE: - fpa11->fpreg[Fd].fValue.fExtended = floatx80_pow(rFn,rFm); + fpa11->fpreg[Fd].fExtended = floatx80_pow(rFn,rFm); break; case RPW_CODE: - fpa11->fpreg[Fd].fValue.fExtended = floatx80_pow(rFm,rFn); + fpa11->fpreg[Fd].fExtended = floatx80_pow(rFm,rFn); break; #endif case RMF_CODE: - fpa11->fpreg[Fd].fValue.fExtended = floatx80_rem(rFn,rFm); + fpa11->fpreg[Fd].fExtended = floatx80_rem(rFn,rFm); break; #if 0 case POL_CODE: - fpa11->fpreg[Fd].fValue.fExtended = floatx80_pol(rFn,rFm); + fpa11->fpreg[Fd].fExtended = floatx80_pol(rFn,rFm); break; #endif /* monadic opcodes */ case MVF_CODE: - fpa11->fpreg[Fd].fValue.fExtended = rFm; + fpa11->fpreg[Fd].fExtended = rFm; break; case MNF_CODE: rFm.high ^= 0x8000; - fpa11->fpreg[Fd].fValue.fExtended = rFm; + fpa11->fpreg[Fd].fExtended = rFm; break; case ABS_CODE: rFm.high &= 0x7fff; - fpa11->fpreg[Fd].fValue.fExtended = rFm; + fpa11->fpreg[Fd].fExtended = rFm; break; case RND_CODE: case URD_CODE: - fpa11->fpreg[Fd].fValue.fExtended = + fpa11->fpreg[Fd].fExtended = int32_to_floatx80(floatx80_to_int32(rFm)); break; case SQT_CODE: - fpa11->fpreg[Fd].fValue.fExtended = floatx80_sqrt(rFm); + fpa11->fpreg[Fd].fExtended = floatx80_sqrt(rFm); break; #if 0 case LOG_CODE: - fpa11->fpreg[Fd].fValue.fExtended = floatx80_log(rFm); + fpa11->fpreg[Fd].fExtended = floatx80_log(rFm); break; case LGN_CODE: - fpa11->fpreg[Fd].fValue.fExtended = floatx80_ln(rFm); + fpa11->fpreg[Fd].fExtended = floatx80_ln(rFm); break; case EXP_CODE: - fpa11->fpreg[Fd].fValue.fExtended = floatx80_exp(rFm); + fpa11->fpreg[Fd].fExtended = floatx80_exp(rFm); break; case SIN_CODE: - fpa11->fpreg[Fd].fValue.fExtended = floatx80_sin(rFm); + fpa11->fpreg[Fd].fExtended = floatx80_sin(rFm); break; case COS_CODE: - fpa11->fpreg[Fd].fValue.fExtended = floatx80_cos(rFm); + fpa11->fpreg[Fd].fExtended = floatx80_cos(rFm); break; case TAN_CODE: - fpa11->fpreg[Fd].fValue.fExtended = floatx80_tan(rFm); + fpa11->fpreg[Fd].fExtended = floatx80_tan(rFm); break; case ASN_CODE: - fpa11->fpreg[Fd].fValue.fExtended = floatx80_arcsin(rFm); + fpa11->fpreg[Fd].fExtended = floatx80_arcsin(rFm); break; case ACS_CODE: - fpa11->fpreg[Fd].fValue.fExtended = floatx80_arccos(rFm); + fpa11->fpreg[Fd].fExtended = floatx80_arccos(rFm); break; case ATN_CODE: - fpa11->fpreg[Fd].fValue.fExtended = floatx80_arctan(rFm); + fpa11->fpreg[Fd].fExtended = floatx80_arctan(rFm); break; #endif @@ -214,7 +213,7 @@ unsigned int ExtendedCPDO(const unsigned int opcode) } } - if (0 != nRc) fpa11->fpreg[Fd].fType = typeExtended; + if (0 != nRc) fpa11->fType[Fd] = typeExtended; return nRc; } diff --git a/arch/arm/nwfpe/fpa11.c b/arch/arm/nwfpe/fpa11.c index 3e11d5ddd..dfc4663b7 100644 --- a/arch/arm/nwfpe/fpa11.c +++ b/arch/arm/nwfpe/fpa11.c @@ -19,9 +19,9 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include "config.h" +#include <asm/system.h> + #include "fpa11.h" -#include "milieu.h" #include "fpopcode.h" #include "fpmodule.h" @@ -39,17 +39,18 @@ FPA11 *fpa11; void resetFPA11(void) { int i; - /* initialize the registers */ + + /* initialize the register type array */ for (i=0;i<=7;i++) { - fpa11->fpreg[i].fType = typeNone; + fpa11->fType[i] = typeNone; } /* FPSR: set system id to FP_EMULATOR, clear all other bits */ fpa11->fpsr = FP_EMULATOR; /* FPCR: set SB, AB and DA bits, clear all others */ -#if MAINTAIN_FPCR +#if MAINTAIN_FPCR fpa11->fpcr = MASK_RESET; #endif } @@ -128,6 +129,9 @@ void SetRoundingPrecision(const unsigned int opcode) unsigned int EmulateAll(unsigned int opcode) { unsigned int nRc = 0; + unsigned long flags; + + save_flags(flags); sti(); if (fpa11->initflag == 0) /* good place for __builtin_expect */ { @@ -162,6 +166,8 @@ unsigned int EmulateAll(unsigned int opcode) nRc = 0; } + restore_flags(flags); + return(nRc); } diff --git a/arch/arm/nwfpe/fpa11.h b/arch/arm/nwfpe/fpa11.h index f7040dbb6..c55ac6f9b 100644 --- a/arch/arm/nwfpe/fpa11.h +++ b/arch/arm/nwfpe/fpa11.h @@ -31,25 +31,25 @@ #define typeDouble 0x02 #define typeExtended 0x03 -typedef struct tagFPREG { - unsigned int fType; - union { - float32 fSingle; - float64 fDouble; - floatx80 fExtended; - } fValue; +typedef union tagFPREG { + float32 fSingle; + float64 fDouble; + floatx80 fExtended; } FPREG; /* FPA11 device model */ typedef struct tagFPA11 { + FPREG fpreg[8]; /* 8 floating point registers */ + FPSR fpsr; /* floating point status register */ + FPCR fpcr; /* floating point control register */ + unsigned char fType[8]; /* type of floating point value held in + floating point registers. One of none + single, double or extended. */ int initflag; /* this is special. The kernel guarantees to set it to 0 when a thread is launched, so we can use it to detect whether this instance of the emulator needs to be initialised. */ - FPREG fpreg[8]; /* 8 floating point registers */ - FPSR fpsr; /* floating point status register */ - FPCR fpcr; /* floating point control register */ } FPA11; extern void resetFPA11(void); diff --git a/arch/arm/nwfpe/fpa11.inl b/arch/arm/nwfpe/fpa11.inl index 321ab7c1c..bc86317a2 100644 --- a/arch/arm/nwfpe/fpa11.inl +++ b/arch/arm/nwfpe/fpa11.inl @@ -2,7 +2,7 @@ NetWinder Floating Point Emulator (c) Corel Computer Corporation, 1998 - Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com> + Direct questions, comments to Scott Bambrough <scottb@netwinder.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 diff --git a/arch/arm/nwfpe/fpa11_cpdo.c b/arch/arm/nwfpe/fpa11_cpdo.c index 19dd08802..fd39a59f7 100644 --- a/arch/arm/nwfpe/fpa11_cpdo.c +++ b/arch/arm/nwfpe/fpa11_cpdo.c @@ -19,7 +19,6 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include "config.h" #include "fpa11.h" #include "fpopcode.h" @@ -48,14 +47,14 @@ unsigned int EmulateCPDO(const unsigned int opcode) if (MONADIC_INSTRUCTION(opcode)) nType = nDest; else - nType = fpa11->fpreg[getFn(opcode)].fType; + nType = fpa11->fType[getFn(opcode)]; if (!CONSTANT_FM(opcode)) { register unsigned int Fm = getFm(opcode); - if (nType < fpa11->fpreg[Fm].fType) + if (nType < fpa11->fType[Fm]) { - nType = fpa11->fpreg[Fm].fType; + nType = fpa11->fType[Fm]; } } @@ -71,7 +70,7 @@ unsigned int EmulateCPDO(const unsigned int opcode) destination register is the correct size. If not force it to be. */ Fd = getFd(opcode); - nType = fpa11->fpreg[Fd].fType; + nType = fpa11->fType[Fd]; if ((0 != nRc) && (nDest != nType)) { switch (nDest) @@ -79,38 +78,38 @@ unsigned int EmulateCPDO(const unsigned int opcode) case typeSingle: { if (typeDouble == nType) - fpa11->fpreg[Fd].fValue.fSingle = - float64_to_float32(fpa11->fpreg[Fd].fValue.fDouble); + fpa11->fpreg[Fd].fSingle = + float64_to_float32(fpa11->fpreg[Fd].fDouble); else - fpa11->fpreg[Fd].fValue.fSingle = - floatx80_to_float32(fpa11->fpreg[Fd].fValue.fExtended); + fpa11->fpreg[Fd].fSingle = + floatx80_to_float32(fpa11->fpreg[Fd].fExtended); } break; case typeDouble: { if (typeSingle == nType) - fpa11->fpreg[Fd].fValue.fDouble = - float32_to_float64(fpa11->fpreg[Fd].fValue.fSingle); + fpa11->fpreg[Fd].fDouble = + float32_to_float64(fpa11->fpreg[Fd].fSingle); else - fpa11->fpreg[Fd].fValue.fDouble = - floatx80_to_float64(fpa11->fpreg[Fd].fValue.fExtended); + fpa11->fpreg[Fd].fDouble = + floatx80_to_float64(fpa11->fpreg[Fd].fExtended); } break; case typeExtended: { if (typeSingle == nType) - fpa11->fpreg[Fd].fValue.fExtended = - float32_to_floatx80(fpa11->fpreg[Fd].fValue.fSingle); + fpa11->fpreg[Fd].fExtended = + float32_to_floatx80(fpa11->fpreg[Fd].fSingle); else - fpa11->fpreg[Fd].fValue.fExtended = - float64_to_floatx80(fpa11->fpreg[Fd].fValue.fDouble); + fpa11->fpreg[Fd].fExtended = + float64_to_floatx80(fpa11->fpreg[Fd].fDouble); } break; } - fpa11->fpreg[Fd].fType = nDest; + fpa11->fType[Fd] = nDest; } return nRc; diff --git a/arch/arm/nwfpe/fpa11_cpdt.c b/arch/arm/nwfpe/fpa11_cpdt.c index e32bf3421..0bad18769 100644 --- a/arch/arm/nwfpe/fpa11_cpdt.c +++ b/arch/arm/nwfpe/fpa11_cpdt.c @@ -20,7 +20,6 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include "config.h" #include "softfloat.h" #include "fpopcode.h" #include "fpa11.h" @@ -32,16 +31,16 @@ extern __inline__ void loadSingle(const unsigned int Fn,const unsigned int *pMem) { - fpa11->fpreg[Fn].fType = typeSingle; - get_user(fpa11->fpreg[Fn].fValue.fSingle, pMem); + fpa11->fType[Fn] = typeSingle; + get_user(fpa11->fpreg[Fn].fSingle, pMem); } extern __inline__ void loadDouble(const unsigned int Fn,const unsigned int *pMem) { unsigned int *p; - p = (unsigned int*)&fpa11->fpreg[Fn].fValue.fDouble; - fpa11->fpreg[Fn].fType = typeDouble; + p = (unsigned int*)&fpa11->fpreg[Fn].fDouble; + fpa11->fType[Fn] = typeDouble; get_user(p[0], &pMem[1]); get_user(p[1], &pMem[0]); /* sign & exponent */ } @@ -50,8 +49,8 @@ extern __inline__ void loadExtended(const unsigned int Fn,const unsigned int *pMem) { unsigned int *p; - p = (unsigned int*)&fpa11->fpreg[Fn].fValue.fExtended; - fpa11->fpreg[Fn].fType = typeExtended; + p = (unsigned int*)&fpa11->fpreg[Fn].fExtended; + fpa11->fType[Fn] = typeExtended; get_user(p[0], &pMem[0]); /* sign & exponent */ get_user(p[1], &pMem[2]); /* ls bits */ get_user(p[2], &pMem[1]); /* ms bits */ @@ -63,11 +62,11 @@ void loadMultiple(const unsigned int Fn,const unsigned int *pMem) register unsigned int *p; unsigned long x; - p = (unsigned int*)&(fpa11->fpreg[Fn].fValue); + p = (unsigned int*)&(fpa11->fpreg[Fn]); get_user(x, &pMem[0]); - fpa11->fpreg[Fn].fType = (x >> 14) & 0x00000003; + fpa11->fType[Fn] = (x >> 14) & 0x00000003; - switch (fpa11->fpreg[Fn].fType) + switch (fpa11->fType[Fn]) { case typeSingle: case typeDouble: @@ -94,17 +93,17 @@ void storeSingle(const unsigned int Fn,unsigned int *pMem) float32 val; register unsigned int *p = (unsigned int*)&val; - switch (fpa11->fpreg[Fn].fType) + switch (fpa11->fType[Fn]) { case typeDouble: - val = float64_to_float32(fpa11->fpreg[Fn].fValue.fDouble); + val = float64_to_float32(fpa11->fpreg[Fn].fDouble); break; case typeExtended: - val = floatx80_to_float32(fpa11->fpreg[Fn].fValue.fExtended); + val = floatx80_to_float32(fpa11->fpreg[Fn].fExtended); break; - default: val = fpa11->fpreg[Fn].fValue.fSingle; + default: val = fpa11->fpreg[Fn].fSingle; } put_user(p[0], pMem); @@ -116,17 +115,17 @@ void storeDouble(const unsigned int Fn,unsigned int *pMem) float64 val; register unsigned int *p = (unsigned int*)&val; - switch (fpa11->fpreg[Fn].fType) + switch (fpa11->fType[Fn]) { case typeSingle: - val = float32_to_float64(fpa11->fpreg[Fn].fValue.fSingle); + val = float32_to_float64(fpa11->fpreg[Fn].fSingle); break; case typeExtended: - val = floatx80_to_float64(fpa11->fpreg[Fn].fValue.fExtended); + val = floatx80_to_float64(fpa11->fpreg[Fn].fExtended); break; - default: val = fpa11->fpreg[Fn].fValue.fDouble; + default: val = fpa11->fpreg[Fn].fDouble; } put_user(p[1], &pMem[0]); /* msw */ put_user(p[0], &pMem[1]); /* lsw */ @@ -138,17 +137,17 @@ void storeExtended(const unsigned int Fn,unsigned int *pMem) floatx80 val; register unsigned int *p = (unsigned int*)&val; - switch (fpa11->fpreg[Fn].fType) + switch (fpa11->fType[Fn]) { case typeSingle: - val = float32_to_floatx80(fpa11->fpreg[Fn].fValue.fSingle); + val = float32_to_floatx80(fpa11->fpreg[Fn].fSingle); break; case typeDouble: - val = float64_to_floatx80(fpa11->fpreg[Fn].fValue.fDouble); + val = float64_to_floatx80(fpa11->fpreg[Fn].fDouble); break; - default: val = fpa11->fpreg[Fn].fValue.fExtended; + default: val = fpa11->fpreg[Fn].fExtended; } put_user(p[0], &pMem[0]); /* sign & exp */ @@ -161,8 +160,8 @@ void storeMultiple(const unsigned int Fn,unsigned int *pMem) { register unsigned int nType, *p; - p = (unsigned int*)&(fpa11->fpreg[Fn].fValue); - nType = fpa11->fpreg[Fn].fType; + p = (unsigned int*)&(fpa11->fpreg[Fn]); + nType = fpa11->fType[Fn]; switch (nType) { @@ -187,12 +186,17 @@ void storeMultiple(const unsigned int Fn,unsigned int *pMem) unsigned int PerformLDF(const unsigned int opcode) { - unsigned int *pBase, *pAddress, *pFinal, nRc = 1; - + unsigned int *pBase, *pAddress, *pFinal, nRc = 1, + write_back = WRITE_BACK(opcode); + //fp_printk("PerformLDF(0x%08x), Fd = 0x%08x\n",opcode,getFd(opcode)); pBase = (unsigned int*)readRegister(getRn(opcode)); - if (REG_PC == getRn(opcode)) pBase += 2; + if (REG_PC == getRn(opcode)) + { + pBase += 2; + write_back = 0; + } pFinal = pBase; if (BIT_UP_SET(opcode)) @@ -210,19 +214,24 @@ unsigned int PerformLDF(const unsigned int opcode) default: nRc = 0; } - if (WRITE_BACK(opcode)) writeRegister(getRn(opcode),(unsigned int)pFinal); + if (write_back) writeRegister(getRn(opcode),(unsigned int)pFinal); return nRc; } unsigned int PerformSTF(const unsigned int opcode) { - unsigned int *pBase, *pAddress, *pFinal, nRc = 1; + unsigned int *pBase, *pAddress, *pFinal, nRc = 1, + write_back = WRITE_BACK(opcode); //fp_printk("PerformSTF(0x%08x), Fd = 0x%08x\n",opcode,getFd(opcode)); SetRoundingMode(ROUND_TO_NEAREST); pBase = (unsigned int*)readRegister(getRn(opcode)); - if (REG_PC == getRn(opcode)) pBase += 2; + if (REG_PC == getRn(opcode)) + { + pBase += 2; + write_back = 0; + } pFinal = pBase; if (BIT_UP_SET(opcode)) @@ -240,15 +249,21 @@ unsigned int PerformSTF(const unsigned int opcode) default: nRc = 0; } - if (WRITE_BACK(opcode)) writeRegister(getRn(opcode),(unsigned int)pFinal); + if (write_back) writeRegister(getRn(opcode),(unsigned int)pFinal); return nRc; } unsigned int PerformLFM(const unsigned int opcode) { - unsigned int i, Fd, *pBase, *pAddress, *pFinal; + unsigned int i, Fd, *pBase, *pAddress, *pFinal, + write_back = WRITE_BACK(opcode); + pBase = (unsigned int*)readRegister(getRn(opcode)); - if (REG_PC == getRn(opcode)) pBase += 2; + if (REG_PC == getRn(opcode)) + { + pBase += 2; + write_back = 0; + } pFinal = pBase; if (BIT_UP_SET(opcode)) @@ -266,16 +281,21 @@ unsigned int PerformLFM(const unsigned int opcode) if (Fd == 8) Fd = 0; } - if (WRITE_BACK(opcode)) writeRegister(getRn(opcode),(unsigned int)pFinal); + if (write_back) writeRegister(getRn(opcode),(unsigned int)pFinal); return 1; } unsigned int PerformSFM(const unsigned int opcode) { - unsigned int i, Fd, *pBase, *pAddress, *pFinal; + unsigned int i, Fd, *pBase, *pAddress, *pFinal, + write_back = WRITE_BACK(opcode); pBase = (unsigned int*)readRegister(getRn(opcode)); - if (REG_PC == getRn(opcode)) pBase += 2; + if (REG_PC == getRn(opcode)) + { + pBase += 2; + write_back = 0; + } pFinal = pBase; if (BIT_UP_SET(opcode)) @@ -293,7 +313,7 @@ unsigned int PerformSFM(const unsigned int opcode) if (Fd == 8) Fd = 0; } - if (WRITE_BACK(opcode)) writeRegister(getRn(opcode),(unsigned int)pFinal); + if (write_back) writeRegister(getRn(opcode),(unsigned int)pFinal); return 1; } diff --git a/arch/arm/nwfpe/fpa11_cprt.c b/arch/arm/nwfpe/fpa11_cprt.c index cbfde092e..d479ee932 100644 --- a/arch/arm/nwfpe/fpa11_cprt.c +++ b/arch/arm/nwfpe/fpa11_cprt.c @@ -20,7 +20,6 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include "config.h" #include "milieu.h" #include "softfloat.h" #include "fpopcode.h" @@ -65,37 +64,10 @@ unsigned int EmulateCPRT(const unsigned int opcode) case WFS_CODE >> 20: writeFPSR(readRegister(getRd(opcode))); break; case RFS_CODE >> 20: writeRegister(getRd(opcode),readFPSR()); break; -#if 0 - /* ?? Not at all sure about the mode checks here. Linux never - calls the emulator from a non-USR fault but we always run in SVC - mode. Is there even any point trying to emulate the way FPA11 - behaves in this respect? - - No - and I quote: 'The FPCR may only be present in some - implementations: it is there to control the hardware in an - implementation-specific manner, ... The user mode of the - ARM is not permitted to use this register, and the WFC and - RFC instructions will trap if tried from user mode.' - Therefore, we do not provide the RFC and WFC instructions. - (rmk, 3/05/1999) - */ - case WFC_CODE >> 20: - { - int mode = 0; - __asm__ volatile ("mrs %0, cpsr; and %0, %0, #0x1f;" : : "g" (mode)); - nRc = (0x13 == mode) ? 1 : 0; /* in SVC processor mode? */ - if (nRc) writeFPCR(readRegister(getRd(opcode))); - } - break; - - case RFC_CODE >> 20: - { - int mode = 0; - __asm__ volatile ("mrs %0, cpsr; and %0, %0, #0x1f;" : : "g" (mode)); - nRc = (0x13 == mode) ? 1 : 0; /* in SVC processor mode? */ - if (nRc) writeRegister(getRd(opcode),readFPCR()); break; - } - break; +#if 0 /* We currently have no use for the FPCR, so there's no point + in emulating it. */ + case WFC_CODE >> 20: writeFPCR(readRegister(getRd(opcode))); + case RFC_CODE >> 20: writeRegister(getRd(opcode),readFPCR()); break; #endif default: nRc = 0; @@ -114,24 +86,24 @@ unsigned int PerformFLT(const unsigned int opcode) { case ROUND_SINGLE: { - fpa11->fpreg[getFn(opcode)].fType = typeSingle; - fpa11->fpreg[getFn(opcode)].fValue.fSingle = + fpa11->fType[getFn(opcode)] = typeSingle; + fpa11->fpreg[getFn(opcode)].fSingle = int32_to_float32(readRegister(getRd(opcode))); } break; case ROUND_DOUBLE: { - fpa11->fpreg[getFn(opcode)].fType = typeDouble; - fpa11->fpreg[getFn(opcode)].fValue.fDouble = + fpa11->fType[getFn(opcode)] = typeDouble; + fpa11->fpreg[getFn(opcode)].fDouble = int32_to_float64(readRegister(getRd(opcode))); } break; case ROUND_EXTENDED: { - fpa11->fpreg[getFn(opcode)].fType = typeExtended; - fpa11->fpreg[getFn(opcode)].fValue.fExtended = + fpa11->fType[getFn(opcode)] = typeExtended; + fpa11->fpreg[getFn(opcode)].fExtended = int32_to_floatx80(readRegister(getRd(opcode))); } break; @@ -149,26 +121,26 @@ unsigned int PerformFIX(const unsigned int opcode) SetRoundingMode(opcode); - switch (fpa11->fpreg[Fn].fType) + switch (fpa11->fType[Fn]) { case typeSingle: { writeRegister(getRd(opcode), - float32_to_int32(fpa11->fpreg[Fn].fValue.fSingle)); + float32_to_int32(fpa11->fpreg[Fn].fSingle)); } break; case typeDouble: { writeRegister(getRd(opcode), - float64_to_int32(fpa11->fpreg[Fn].fValue.fDouble)); + float64_to_int32(fpa11->fpreg[Fn].fDouble)); } break; case typeExtended: { writeRegister(getRd(opcode), - floatx80_to_int32(fpa11->fpreg[Fn].fValue.fExtended)); + floatx80_to_int32(fpa11->fpreg[Fn].fExtended)); } break; @@ -226,27 +198,27 @@ static unsigned int PerformComparison(const unsigned int opcode) ?? Might be some mileage in avoiding this conversion if possible. Eg, if both operands are 32-bit, detect this and do a 32-bit comparison (cheaper than an 80-bit one). */ - switch (fpa11->fpreg[Fn].fType) + switch (fpa11->fType[Fn]) { case typeSingle: //fp_printk("single.\n"); - if (float32_is_nan(fpa11->fpreg[Fn].fValue.fSingle)) + if (float32_is_nan(fpa11->fpreg[Fn].fSingle)) goto unordered; - rFn = float32_to_floatx80(fpa11->fpreg[Fn].fValue.fSingle); + rFn = float32_to_floatx80(fpa11->fpreg[Fn].fSingle); break; case typeDouble: //fp_printk("double.\n"); - if (float64_is_nan(fpa11->fpreg[Fn].fValue.fDouble)) + if (float64_is_nan(fpa11->fpreg[Fn].fDouble)) goto unordered; - rFn = float64_to_floatx80(fpa11->fpreg[Fn].fValue.fDouble); + rFn = float64_to_floatx80(fpa11->fpreg[Fn].fDouble); break; case typeExtended: //fp_printk("extended.\n"); - if (floatx80_is_nan(fpa11->fpreg[Fn].fValue.fExtended)) + if (floatx80_is_nan(fpa11->fpreg[Fn].fExtended)) goto unordered; - rFn = fpa11->fpreg[Fn].fValue.fExtended; + rFn = fpa11->fpreg[Fn].fExtended; break; default: return 0; @@ -262,27 +234,27 @@ static unsigned int PerformComparison(const unsigned int opcode) else { //fp_printk("Fm = r%d which contains a ",Fm); - switch (fpa11->fpreg[Fm].fType) + switch (fpa11->fType[Fm]) { case typeSingle: //fp_printk("single.\n"); - if (float32_is_nan(fpa11->fpreg[Fm].fValue.fSingle)) + if (float32_is_nan(fpa11->fpreg[Fm].fSingle)) goto unordered; - rFm = float32_to_floatx80(fpa11->fpreg[Fm].fValue.fSingle); + rFm = float32_to_floatx80(fpa11->fpreg[Fm].fSingle); break; case typeDouble: //fp_printk("double.\n"); - if (float64_is_nan(fpa11->fpreg[Fm].fValue.fDouble)) + if (float64_is_nan(fpa11->fpreg[Fm].fDouble)) goto unordered; - rFm = float64_to_floatx80(fpa11->fpreg[Fm].fValue.fDouble); + rFm = float64_to_floatx80(fpa11->fpreg[Fm].fDouble); break; case typeExtended: //fp_printk("extended.\n"); - if (floatx80_is_nan(fpa11->fpreg[Fm].fValue.fExtended)) + if (floatx80_is_nan(fpa11->fpreg[Fm].fExtended)) goto unordered; - rFm = fpa11->fpreg[Fm].fValue.fExtended; + rFm = fpa11->fpreg[Fm].fExtended; break; default: return 0; @@ -303,6 +275,7 @@ static unsigned int PerformComparison(const unsigned int opcode) the data sheet, observation of how the Acorn emulator actually behaves (and how programs expect it to) and guesswork. */ flags |= CC_OVERFLOW; + flags &= ~(CC_ZERO | CC_NEGATIVE); if (BIT_AC & readFPSR()) flags |= CC_CARRY; diff --git a/arch/arm/nwfpe/fpmodule.c b/arch/arm/nwfpe/fpmodule.c index de28e39f5..b9ca935eb 100644 --- a/arch/arm/nwfpe/fpmodule.c +++ b/arch/arm/nwfpe/fpmodule.c @@ -1,3 +1,4 @@ + /* NetWinder Floating Point Emulator (c) Rebel.com, 1998-1999 @@ -20,30 +21,16 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include "config.h" - -#ifdef MODULE #include <linux/module.h> #include <linux/version.h> -#else -#define MOD_INC_USE_COUNT -#define MOD_DEC_USE_COUNT -#endif +#include <linux/config.h> /* XXX */ #include <linux/types.h> #include <linux/kernel.h> #include <linux/signal.h> #include <linux/sched.h> -#include <linux/mm.h> #include <linux/init.h> -#include <linux/spinlock.h> - -#include <asm/system.h> -#include <asm/uaccess.h> -#include <asm/io.h> -#include <asm/atomic.h> -#include <asm/pgtable.h> /* XXX */ #include "softfloat.h" @@ -62,7 +49,7 @@ typedef struct task_struct* PTASK; int fp_printk(const char *,...); void fp_send_sig(unsigned long sig, PTASK p, int priv); #if LINUX_VERSION_CODE > 0x20115 -MODULE_AUTHOR("Scott Bambrough <scottb@netwinder.org>"); +MODULE_AUTHOR("Scott Bambrough <scottb@rebel.com>"); MODULE_DESCRIPTION("NWFPE floating point emulator"); #endif @@ -73,57 +60,51 @@ MODULE_DESCRIPTION("NWFPE floating point emulator"); #endif /* kernel function prototypes required */ -void C_SYMBOL_NAME(fp_setup)(void); +void fp_setup(void); /* external declarations for saved kernel symbols */ -extern unsigned int C_SYMBOL_NAME(kern_fp_enter); +extern void (*kern_fp_enter)(void); + +/* Original value of fp_enter from kernel before patched by fpe_init. */ +static void (*orig_fp_enter)(void); /* forward declarations */ extern void nwfpe_enter(void); -/* Original value of fp_enter from kernel before patched by fpe_init. */ -static unsigned int orig_fp_enter; - /* Address of user registers on the kernel stack. */ unsigned int *userRegisters; -void __init C_SYMBOL_NAME(fpe_version)(void) +void __init fpe_version(void) { static const char szTitle[] = "<4>NetWinder Floating Point Emulator "; - static const char szVersion[] = "V0.94.1 "; + static const char szVersion[] = "V0.95 "; static const char szCopyright[] = "(c) 1998-1999 Rebel.com\n"; - C_SYMBOL_NAME(fp_printk)(szTitle); - C_SYMBOL_NAME(fp_printk)(szVersion); - C_SYMBOL_NAME(fp_printk)(szCopyright); + fp_printk(szTitle); + fp_printk(szVersion); + fp_printk(szCopyright); } int __init fpe_init(void) { - /* Display title, version and copyright information. */ - C_SYMBOL_NAME(fpe_version)(); - - /* Save pointer to the old FP handler and then patch ourselves in */ - orig_fp_enter = C_SYMBOL_NAME(kern_fp_enter); - C_SYMBOL_NAME(kern_fp_enter) = (unsigned int)C_SYMBOL_NAME(nwfpe_enter); + if (sizeof(FPA11) > sizeof(union fp_state)) + printk(KERN_ERR "nwfpe: bad structure size\n"); + else { + /* Display title, version and copyright information. */ + fpe_version(); + + /* Save pointer to the old FP handler and then patch ourselves in */ + orig_fp_enter = kern_fp_enter; + kern_fp_enter = nwfpe_enter; + } return 0; } -#ifdef MODULE -int init_module(void) -{ - return(fpe_init()); -} - -void cleanup_module(void) +void __exit fpe_exit(void) { /* Restore the values we saved earlier. */ - C_SYMBOL_NAME(kern_fp_enter) = orig_fp_enter; + kern_fp_enter = orig_fp_enter; } -#endif - -#define _ARM_pc 60 -#define _ARM_cpsr 64 /* ScottB: November 4, 1998 @@ -135,7 +116,7 @@ fpmodule.c to integrate with the NetBSD kernel (I hope!). [1/1/99: Not quite true any more unfortunately. There is Linux-specific code to access data in user space in some other source files at the -moment. --philb] +moment (grep for get_user / put_user calls). --philb] float_exception_flags is a global variable in SoftFloat. @@ -147,8 +128,9 @@ cumulative exceptions flag byte are set and we return. void float_raise(signed char flags) { -#if 0 - printk(KERN_DEBUG "NWFPE: exception %08x at %08x from %08x\n", flags, +#ifdef CONFIG_DEBUG_USER + printk(KERN_DEBUG "NWFPE: %s[%d] takes exception %08x at %p from %08x\n", + current->comm, current->pid, flags, __builtin_return_address(0), userRegisters[15]); #endif @@ -156,7 +138,7 @@ void float_raise(signed char flags) if (readFPSR() & (flags << 16)) { /* raise exception */ - C_SYMBOL_NAME(fp_send_sig)(SIGFPE,C_SYMBOL_NAME(current),1); + fp_send_sig(SIGFPE, current, 1); } else { @@ -164,3 +146,6 @@ void float_raise(signed char flags) writeFPSR(flags); } } + +module_init(fpe_init); +module_exit(fpe_exit); diff --git a/arch/arm/nwfpe/fpmodule.inl b/arch/arm/nwfpe/fpmodule.inl index c76b7fd55..127b24218 100644 --- a/arch/arm/nwfpe/fpmodule.inl +++ b/arch/arm/nwfpe/fpmodule.inl @@ -1,23 +1,23 @@ /* NetWinder Floating Point Emulator - (c) Corel Computer Corporation, 1998 + (c) Rebel.com, 1998-1999 - Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com> + Direct questions, comments to Scott Bambrough <scottb@netwinder.org> - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + This program is 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. + 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. - */ + 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. +*/ /* Address of user registers on the kernel stack. */ extern unsigned int *userRegisters; @@ -25,64 +25,60 @@ extern unsigned int *userRegisters; extern __inline__ unsigned int readRegister(const unsigned int nReg) { - /* Note: The CPU thinks it has dealt with the current instruction. As - a result the program counter has been advanced to the next - instruction, and points 4 bytes beyond the actual instruction - that caused the invalid instruction trap to occur. We adjust - for this in this routine. LDF/STF instructions with Rn = PC - depend on the PC being correct, as they use PC+8 in their - address calculations. */ - unsigned int val = userRegisters[nReg]; - - if (REG_PC == nReg) - val -= 4; - - return val; + /* Note: The CPU thinks it has dealt with the current instruction. As + a result the program counter has been advanced to the next + instruction, and points 4 bytes beyond the actual instruction + that caused the invalid instruction trap to occur. We adjust + for this in this routine. LDF/STF instructions with Rn = PC + depend on the PC being correct, as they use PC+8 in their + address calculations. */ + unsigned int val = userRegisters[nReg]; + if (REG_PC == nReg) val -= 4; + return val; } extern __inline__ void writeRegister(const unsigned int nReg, const unsigned int val) { - userRegisters[nReg] = val; + userRegisters[nReg] = val; } extern __inline__ unsigned int readCPSR(void) { - return (readRegister(REG_CPSR)); + return(readRegister(REG_CPSR)); } extern __inline__ void writeCPSR(const unsigned int val) { - writeRegister(REG_CPSR, val); + writeRegister(REG_CPSR,val); } extern __inline__ unsigned int readConditionCodes(void) { #ifdef __FPEM_TEST__ - return (0); + return(0); #else - return (readCPSR() & CC_MASK); + return(readCPSR() & CC_MASK); #endif } extern __inline__ void writeConditionCodes(const unsigned int val) { - unsigned int rval; - - /* - * Operate directly on userRegisters since - * the CPSR may be the PC register itself. - */ - rval = userRegisters[REG_CPSR] & ~CC_MASK; - userRegisters[REG_CPSR] = rval | (val & CC_MASK); + unsigned int rval; + /* + * Operate directly on userRegisters since + * the CPSR may be the PC register itself. + */ + rval = userRegisters[REG_CPSR] & ~CC_MASK; + userRegisters[REG_CPSR] = rval | (val & CC_MASK); } extern __inline__ unsigned int readMemoryInt(unsigned int *pMem) { - return *pMem; + return *pMem; } diff --git a/arch/arm/nwfpe/fpopcode.c b/arch/arm/nwfpe/fpopcode.c index 0d12f7269..4b01b43c6 100644 --- a/arch/arm/nwfpe/fpopcode.c +++ b/arch/arm/nwfpe/fpopcode.c @@ -19,7 +19,6 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include "config.h" #include "softfloat.h" #include "fpopcode.h" #include "fpsr.h" diff --git a/arch/arm/nwfpe/single_cpdo.c b/arch/arm/nwfpe/single_cpdo.c index a2a9f6f81..77bb73515 100644 --- a/arch/arm/nwfpe/single_cpdo.c +++ b/arch/arm/nwfpe/single_cpdo.c @@ -19,8 +19,6 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include "config.h" -#include "milieu.h" #include "softfloat.h" #include "fpopcode.h" #include "fpa11.h" @@ -51,10 +49,10 @@ unsigned int SingleCPDO(const unsigned int opcode) } else { - switch (fpa11->fpreg[Fm].fType) + switch (fpa11->fType[Fm]) { case typeSingle: - rFm = fpa11->fpreg[Fm].fValue.fSingle; + rFm = fpa11->fpreg[Fm].fSingle; break; default: return 0; @@ -64,10 +62,10 @@ unsigned int SingleCPDO(const unsigned int opcode) if (!MONADIC_INSTRUCTION(opcode)) { Fn = getFn(opcode); - switch (fpa11->fpreg[Fn].fType) + switch (fpa11->fType[Fn]) { case typeSingle: - rFn = fpa11->fpreg[Fn].fValue.fSingle; + rFn = fpa11->fpreg[Fn].fSingle; break; default: return 0; @@ -79,112 +77,112 @@ unsigned int SingleCPDO(const unsigned int opcode) { /* dyadic opcodes */ case ADF_CODE: - fpa11->fpreg[Fd].fValue.fSingle = float32_add(rFn,rFm); + fpa11->fpreg[Fd].fSingle = float32_add(rFn,rFm); break; case MUF_CODE: case FML_CODE: - fpa11->fpreg[Fd].fValue.fSingle = float32_mul(rFn,rFm); + fpa11->fpreg[Fd].fSingle = float32_mul(rFn,rFm); break; case SUF_CODE: - fpa11->fpreg[Fd].fValue.fSingle = float32_sub(rFn,rFm); + fpa11->fpreg[Fd].fSingle = float32_sub(rFn,rFm); break; case RSF_CODE: - fpa11->fpreg[Fd].fValue.fSingle = float32_sub(rFm,rFn); + fpa11->fpreg[Fd].fSingle = float32_sub(rFm,rFn); break; case DVF_CODE: case FDV_CODE: - fpa11->fpreg[Fd].fValue.fSingle = float32_div(rFn,rFm); + fpa11->fpreg[Fd].fSingle = float32_div(rFn,rFm); break; case RDF_CODE: case FRD_CODE: - fpa11->fpreg[Fd].fValue.fSingle = float32_div(rFm,rFn); + fpa11->fpreg[Fd].fSingle = float32_div(rFm,rFn); break; #if 0 case POW_CODE: - fpa11->fpreg[Fd].fValue.fSingle = float32_pow(rFn,rFm); + fpa11->fpreg[Fd].fSingle = float32_pow(rFn,rFm); break; case RPW_CODE: - fpa11->fpreg[Fd].fValue.fSingle = float32_pow(rFm,rFn); + fpa11->fpreg[Fd].fSingle = float32_pow(rFm,rFn); break; #endif case RMF_CODE: - fpa11->fpreg[Fd].fValue.fSingle = float32_rem(rFn,rFm); + fpa11->fpreg[Fd].fSingle = float32_rem(rFn,rFm); break; #if 0 case POL_CODE: - fpa11->fpreg[Fd].fValue.fSingle = float32_pol(rFn,rFm); + fpa11->fpreg[Fd].fSingle = float32_pol(rFn,rFm); break; #endif /* monadic opcodes */ case MVF_CODE: - fpa11->fpreg[Fd].fValue.fSingle = rFm; + fpa11->fpreg[Fd].fSingle = rFm; break; case MNF_CODE: rFm ^= 0x80000000; - fpa11->fpreg[Fd].fValue.fSingle = rFm; + fpa11->fpreg[Fd].fSingle = rFm; break; case ABS_CODE: rFm &= 0x7fffffff; - fpa11->fpreg[Fd].fValue.fSingle = rFm; + fpa11->fpreg[Fd].fSingle = rFm; break; case RND_CODE: case URD_CODE: - fpa11->fpreg[Fd].fValue.fSingle = + fpa11->fpreg[Fd].fSingle = int32_to_float32(float32_to_int32(rFm)); break; case SQT_CODE: - fpa11->fpreg[Fd].fValue.fSingle = float32_sqrt(rFm); + fpa11->fpreg[Fd].fSingle = float32_sqrt(rFm); break; #if 0 case LOG_CODE: - fpa11->fpreg[Fd].fValue.fSingle = float32_log(rFm); + fpa11->fpreg[Fd].fSingle = float32_log(rFm); break; case LGN_CODE: - fpa11->fpreg[Fd].fValue.fSingle = float32_ln(rFm); + fpa11->fpreg[Fd].fSingle = float32_ln(rFm); break; case EXP_CODE: - fpa11->fpreg[Fd].fValue.fSingle = float32_exp(rFm); + fpa11->fpreg[Fd].fSingle = float32_exp(rFm); break; case SIN_CODE: - fpa11->fpreg[Fd].fValue.fSingle = float32_sin(rFm); + fpa11->fpreg[Fd].fSingle = float32_sin(rFm); break; case COS_CODE: - fpa11->fpreg[Fd].fValue.fSingle = float32_cos(rFm); + fpa11->fpreg[Fd].fSingle = float32_cos(rFm); break; case TAN_CODE: - fpa11->fpreg[Fd].fValue.fSingle = float32_tan(rFm); + fpa11->fpreg[Fd].fSingle = float32_tan(rFm); break; case ASN_CODE: - fpa11->fpreg[Fd].fValue.fSingle = float32_arcsin(rFm); + fpa11->fpreg[Fd].fSingle = float32_arcsin(rFm); break; case ACS_CODE: - fpa11->fpreg[Fd].fValue.fSingle = float32_arccos(rFm); + fpa11->fpreg[Fd].fSingle = float32_arccos(rFm); break; case ATN_CODE: - fpa11->fpreg[Fd].fValue.fSingle = float32_arctan(rFm); + fpa11->fpreg[Fd].fSingle = float32_arctan(rFm); break; #endif @@ -197,7 +195,7 @@ unsigned int SingleCPDO(const unsigned int opcode) } } - if (0 != nRc) fpa11->fpreg[Fd].fType = typeSingle; + if (0 != nRc) fpa11->fType[Fd] = typeSingle; return nRc; } diff --git a/arch/arm/vmlinux-armo.lds.in b/arch/arm/vmlinux-armo.lds.in index f336370b4..446f49924 100644 --- a/arch/arm/vmlinux-armo.lds.in +++ b/arch/arm/vmlinux-armo.lds.in @@ -3,16 +3,36 @@ * Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz> */ OUTPUT_ARCH(arm) -ENTRY(_start) +ENTRY(stext) SECTIONS { . = TEXTADDR; + __init_begin = .; + .text.init : { *(.text.init) } + __proc_info_begin = .; + .proc.info : { *(.proc.info) } + __proc_info_end = .; + .data.init : { *(.data.init) } + . = ALIGN(16); + __setup_start = .; + .setup.init : { *(.setup.init) } + __setup_end = .; + __initcall_start = .; + .initcall.init : { *(.initcall.init) } + __initcall_end = .; + . = ALIGN(32768); + __init_end = .; + + .init.task : { + *(.init.task) + } + _text = .; /* Text and read-only data */ .text : { *(.text) *(.fixup) *(.gnu.warning) - } = 0x9090 + } .text.lock : { *(.text.lock) } /* out-of-line lock text */ .rodata : { *(.rodata) } .kstrtab : { *(.kstrtab) } @@ -26,27 +46,17 @@ SECTIONS __ksymtab : { *(__ksymtab) } __stop___ksymtab = .; + .got : { *(.got) } /* Global offset table */ + _etext = .; /* End of text section */ - . = ALIGN(8192); .data : { /* Data */ - *(.init.task) *(.data) CONSTRUCTORS } _edata = .; /* End of data section */ - . = ALIGN(32768); /* Init code and data */ - __init_begin = .; - .text.init : { *(.text.init) } - __proc_info_begin = .; - .proc.info : { *(.proc.info) } - __proc_info_end = .; - .data.init : { *(.data.init) } - . = ALIGN(32768); - __init_end = .; - __bss_start = .; /* BSS */ .bss : { *(.bss) diff --git a/arch/arm/vmlinux-armv.lds.in b/arch/arm/vmlinux-armv.lds.in index a1f6d414b..f83109875 100644 --- a/arch/arm/vmlinux-armv.lds.in +++ b/arch/arm/vmlinux-armv.lds.in @@ -3,11 +3,10 @@ * Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz> */ OUTPUT_ARCH(arm) -ENTRY(_start) +ENTRY(stext) SECTIONS { . = TEXTADDR; - _text = .; /* Text and read-only data */ .text : { } /* Set text start address */ __init_begin = .; /* Init code and data */ @@ -38,6 +37,7 @@ SECTIONS . = ALIGN(4096); __netwinder_end = .; + _text = .; /* Text and read-only data */ .text.real : { /* Real text segment */ *(.text) *(.fixup) @@ -57,6 +57,8 @@ SECTIONS __ksymtab : { *(__ksymtab) } __stop___ksymtab = .; + .got : { *(.got) } /* Global offset table */ + _etext = .; /* End of text section */ . = ALIGN(8192); diff --git a/arch/i386/Makefile b/arch/i386/Makefile index 9978cac9d..37b2371a0 100644 --- a/arch/i386/Makefile +++ b/arch/i386/Makefile @@ -84,9 +84,6 @@ MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot vmlinux: arch/i386/vmlinux.lds -arch/i386/vmlinux.lds: arch/i386/vmlinux.lds.S FORCE - $(CPP) -C -P -imacros $(HPATH)/asm-i386/page_offset.h -Ui386 arch/i386/vmlinux.lds.S >arch/i386/vmlinux.lds - FORCE: ; .PHONY: zImage bzImage compressed zlilo bzlilo zdisk bzdisk install \ @@ -119,7 +116,6 @@ archclean: @$(MAKEBOOT) clean archmrproper: - rm -f arch/i386/vmlinux.lds archdep: @$(MAKEBOOT) dep diff --git a/arch/i386/boot/Makefile b/arch/i386/boot/Makefile index 414e51679..de8cdced8 100644 --- a/arch/i386/boot/Makefile +++ b/arch/i386/boot/Makefile @@ -51,11 +51,11 @@ bootsect.o: bootsect.s bootsect.s: bootsect.S Makefile $(BOOT_INCL) $(CPP) -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@ -bbootsect: bbootsect.o bsetup - $(LD) -Ttext 0x0 -s -oformat binary $< -R bsetup.o -o $@ +bbootsect: bbootsect.o + $(LD) -Ttext 0x0 -s -oformat binary $< -o $@ bbootsect.o: bbootsect.s - $(AS) -o $@ $< + $(AS) --defsym bootsect_kludge=0x220 -o $@ $< bbootsect.s: bootsect.S Makefile $(BOOT_INCL) $(CPP) -D__BIG_KERNEL__ -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@ diff --git a/arch/i386/boot/bootsect.S b/arch/i386/boot/bootsect.S index e7327e1e1..58644810e 100644 --- a/arch/i386/boot/bootsect.S +++ b/arch/i386/boot/bootsect.S @@ -64,12 +64,12 @@ _start: movw %ax, %ds movw $INITSEG, %ax movw %ax, %es - movw $128, %cx + movw $256, %cx subw %si, %si subw %di, %di cld rep - movsl + movsw ljmp $INITSEG, $go # bde - changed 0xff00 to 0x4000 to use debugger at 0x6400 up (bde). We @@ -105,11 +105,11 @@ go: movw $0x4000-12, %di # 0x4000 is an arbitrary value >= movw $0x78, %bx # fs:bx is parameter table address pushw %ds ldsw %fs:(%bx), %si # ds:si is source - movb $3, %cl # copy 12 bytes + movb $6, %cl # copy 12 bytes cld pushw %di # di = 0x4000-12. rep - movsl + movsw popw %di popw %ds movb $36, 0x4(%di) # patch sector count @@ -118,7 +118,7 @@ go: movw $0x4000-12, %di # 0x4000 is an arbitrary value >= # Load the setup-sectors directly after the bootblock. # Note that 'es' is already set up. -# Also, cx = 0 from rep movsl above. +# Also, cx = 0 from rep movsw above. load_setup: xorb %ah, %ah # reset FDC @@ -247,7 +247,7 @@ die: jne die # es must be at 64kB boundary xorw %bx, %bx # bx is starting address within segment rp_read: #ifdef __BIG_KERNEL__ - .word 0x1eff, 0x0220 # lcall *bootsect_kludge in setup.S + lcall bootsect_kludge # in setup.S #else movw %es, %ax subw $SYSSEG, %ax diff --git a/arch/i386/boot/setup.S b/arch/i386/boot/setup.S index 859c7248b..9c450bab8 100644 --- a/arch/i386/boot/setup.S +++ b/arch/i386/boot/setup.S @@ -209,14 +209,14 @@ bad_sig: addw $SYSSEG, %bx movw %bx, %cs:start_sys_seg # Move rest of setup code/data to here - movw $2048, %di # four sectors loaded by LILO + movw $4096, %di # four sectors loaded by LILO subw %si, %si movw %cs, %ax # aka SETUPSEG movw %ax, %es movw $SYSSEG, %ax movw %ax, %ds rep - movsl + movsw movw %cs, %ax # aka SETUPSEG movw %ax, %ds cmpw $SIG1, setup_sig1 @@ -532,9 +532,9 @@ do_move: addw $0x100, %bx subw %di, %di subw %si, %si - movw $0x400, %cx + movw $0x800, %cx rep - movsl + movsw cmpw %bp, %bx # assume start_sys_seg > 0x200, # so we will perhaps read one # page more than needed, but diff --git a/arch/i386/boot/video.S b/arch/i386/boot/video.S index 3052b6838..77d22bef1 100644 --- a/arch/i386/boot/video.S +++ b/arch/i386/boot/video.S @@ -1152,7 +1152,7 @@ dosvga: lodsw pushw %es movw $0xc000, %bx movw %bx, %es - call ax # Call test routine + call *%ax # Call test routine popw %es popw %di popw %si @@ -1741,7 +1741,7 @@ even7: movb $0x0c, %al cmpb %bh, %al jne isnot - movb $VIDEO_FIRST_V7>>8, $svga_prefix # Use special mode switching + movb $VIDEO_FIRST_V7>>8, svga_prefix # Use special mode switching ret video7_md: diff --git a/arch/i386/config.in b/arch/i386/config.in index 9d373d1ed..b8f081e65 100644 --- a/arch/i386/config.in +++ b/arch/i386/config.in @@ -42,9 +42,17 @@ if [ "$CONFIG_MK7" = "y" ]; then define_bool CONFIG_X86_USE_3DNOW y fi -choice 'Maximum Physical Memory' \ - "1GB CONFIG_1GB \ - 2GB CONFIG_2GB" 1GB +choice 'High Memory Support' \ + "off CONFIG_NOHIGHMEM \ + 4GB CONFIG_HIGHMEM4G \ + 64GB CONFIG_HIGHMEM64G" off +if [ "$CONFIG_HIGHMEM4G" = "y" ]; then + define_bool CONFIG_HIGHMEM y +fi +if [ "$CONFIG_HIGHMEM64G" = "y" ]; then + define_bool CONFIG_HIGHMEM y + define_bool CONFIG_X86_PAE y +fi bool 'Math emulation' CONFIG_MATH_EMULATION bool 'MTRR (Memory Type Range Register) support' CONFIG_MTRR @@ -63,7 +71,6 @@ endmenu mainmenu_option next_comment comment 'General setup' -bool 'BIGMEM support' CONFIG_BIGMEM bool 'Networking support' CONFIG_NET bool 'SGI Visual Workstation support' CONFIG_VISWS if [ "$CONFIG_VISWS" = "y" ]; then diff --git a/arch/i386/defconfig b/arch/i386/defconfig index 49137cda4..8b6370667 100644 --- a/arch/i386/defconfig +++ b/arch/i386/defconfig @@ -24,8 +24,9 @@ CONFIG_X86_BSWAP=y CONFIG_X86_POPAD_OK=y CONFIG_X86_TSC=y CONFIG_X86_GOOD_APIC=y -CONFIG_1GB=y -# CONFIG_2GB is not set +CONFIG_NOHIGHMEM=y +# CONFIG_HIGHMEM4G is not set +# CONFIG_HIGHMEM64G is not set # CONFIG_MATH_EMULATION is not set # CONFIG_MTRR is not set CONFIG_SMP=y @@ -40,7 +41,6 @@ CONFIG_MODULES=y # # General setup # -# CONFIG_BIGMEM is not set CONFIG_NET=y # CONFIG_VISWS is not set CONFIG_X86_IO_APIC=y @@ -54,10 +54,12 @@ CONFIG_PCI_DIRECT=y # CONFIG_MCA is not set # -# PCMCIA/Cardbus support +# PCMCIA/CardBus support # CONFIG_PCMCIA=y CONFIG_CARDBUS=y +CONFIG_I82365=y +# CONFIG_TCIC is not set CONFIG_SYSVIPC=y # CONFIG_BSD_PROCESS_ACCT is not set CONFIG_SYSCTL=y @@ -111,7 +113,7 @@ CONFIG_BLK_DEV_IDEPCI=y # CONFIG_BLK_DEV_OFFBOARD is not set # CONFIG_BLK_DEV_AEC6210 is not set CONFIG_BLK_DEV_PIIX=y -# CONFIG_BLK_DEV_SIS5513 is not set +# CONFIG_BLK_DEV_PIIX_TUNING is not set # CONFIG_IDE_CHIPSETS is not set # CONFIG_BLK_CPQ_DA is not set @@ -284,11 +286,19 @@ CONFIG_EEXPRESS_PRO100=y # CONFIG_WAN is not set # -# PCMCIA network devices +# PCMCIA network device support # -CONFIG_PCMCIA_PCNET=y +CONFIG_NET_PCMCIA=y # CONFIG_PCMCIA_3C589 is not set +# CONFIG_PCMCIA_3C574 is not set +# CONFIG_PCMCIA_FMVJ18X is not set +CONFIG_PCMCIA_PCNET=y +# CONFIG_PCMCIA_NMCLAN is not set +# CONFIG_PCMCIA_SMC91C92 is not set +# CONFIG_PCMCIA_XIRC2PS is not set CONFIG_PCMCIA_RAYCS=y +# CONFIG_PCMCIA_NETWAVE is not set +# CONFIG_PCMCIA_WAVELAN is not set CONFIG_PCMCIA_NETCARD=y # diff --git a/arch/i386/kernel/head.S b/arch/i386/kernel/head.S index f1aa50586..423308aae 100644 --- a/arch/i386/kernel/head.S +++ b/arch/i386/kernel/head.S @@ -367,11 +367,13 @@ SYMBOL_NAME(gdt): .org 0x1000 ENTRY(swapper_pg_dir) .long 0x00102007 - .fill __USER_PGD_PTRS-1,4,0 - /* default: 767 entries */ + .long 0x00103007 + .fill BOOT_USER_PGD_PTRS-2,4,0 + /* default: 766 entries */ .long 0x00102007 - /* default: 255 entries */ - .fill __KERNEL_PGD_PTRS-1,4,0 + .long 0x00103007 + /* default: 254 entries */ + .fill BOOT_KERNEL_PGD_PTRS-2,4,0 /* * The page tables are initialized to only 4MB here - the final page @@ -509,16 +511,156 @@ ENTRY(pg0) .long 0x3f0007,0x3f1007,0x3f2007,0x3f3007,0x3f4007,0x3f5007,0x3f6007,0x3f7007 .long 0x3f8007,0x3f9007,0x3fa007,0x3fb007,0x3fc007,0x3fd007,0x3fe007,0x3ff007 -.org 0x3000 -ENTRY(empty_bad_page) - +ENTRY(pg1) + .long 0x400007,0x001007,0x002007,0x003007,0x004007,0x005007,0x006007,0x007007 + .long 0x408007,0x009007,0x00a007,0x00b007,0x00c007,0x00d007,0x00e007,0x00f007 + .long 0x410007,0x011007,0x012007,0x013007,0x014007,0x015007,0x016007,0x017007 + .long 0x418007,0x019007,0x01a007,0x01b007,0x01c007,0x01d007,0x01e007,0x01f007 + .long 0x420007,0x021007,0x022007,0x023007,0x024007,0x025007,0x026007,0x027007 + .long 0x428007,0x029007,0x02a007,0x02b007,0x02c007,0x02d007,0x02e007,0x02f007 + .long 0x430007,0x031007,0x032007,0x033007,0x034007,0x035007,0x036007,0x037007 + .long 0x438007,0x039007,0x03a007,0x03b007,0x03c007,0x03d007,0x03e007,0x03f007 + .long 0x440007,0x041007,0x042007,0x043007,0x044007,0x045007,0x046007,0x047007 + .long 0x448007,0x049007,0x04a007,0x04b007,0x04c007,0x04d007,0x04e007,0x04f007 + .long 0x450007,0x051007,0x052007,0x053007,0x054007,0x055007,0x056007,0x057007 + .long 0x458007,0x059007,0x05a007,0x05b007,0x05c007,0x05d007,0x05e007,0x05f007 + .long 0x460007,0x061007,0x062007,0x063007,0x064007,0x065007,0x066007,0x067007 + .long 0x468007,0x069007,0x06a007,0x06b007,0x06c007,0x06d007,0x06e007,0x06f007 + .long 0x470007,0x071007,0x072007,0x073007,0x074007,0x075007,0x076007,0x077007 + .long 0x478007,0x079007,0x07a007,0x07b007,0x07c007,0x07d007,0x07e007,0x07f007 + .long 0x480007,0x081007,0x082007,0x083007,0x084007,0x085007,0x086007,0x087007 + .long 0x488007,0x089007,0x08a007,0x08b007,0x08c007,0x08d007,0x08e007,0x08f007 + .long 0x490007,0x091007,0x092007,0x093007,0x094007,0x095007,0x096007,0x097007 + .long 0x498007,0x099007,0x09a007,0x09b007,0x09c007,0x09d007,0x09e007,0x09f007 + .long 0x4a0007,0x0a1007,0x0a2007,0x0a3007,0x0a4007,0x0a5007,0x0a6007,0x0a7007 + .long 0x4a8007,0x0a9007,0x0aa007,0x0ab007,0x0ac007,0x0ad007,0x0ae007,0x0af007 + .long 0x4b0007,0x0b1007,0x0b2007,0x0b3007,0x0b4007,0x0b5007,0x0b6007,0x0b7007 + .long 0x4b8007,0x0b9007,0x0ba007,0x0bb007,0x0bc007,0x0bd007,0x0be007,0x0bf007 + .long 0x4c0007,0x0c1007,0x0c2007,0x0c3007,0x0c4007,0x0c5007,0x0c6007,0x0c7007 + .long 0x4c8007,0x0c9007,0x0ca007,0x0cb007,0x0cc007,0x0cd007,0x0ce007,0x0cf007 + .long 0x4d0007,0x0d1007,0x0d2007,0x0d3007,0x0d4007,0x0d5007,0x0d6007,0x0d7007 + .long 0x4d8007,0x0d9007,0x0da007,0x0db007,0x0dc007,0x0dd007,0x0de007,0x0df007 + .long 0x4e0007,0x0e1007,0x0e2007,0x0e3007,0x0e4007,0x0e5007,0x0e6007,0x0e7007 + .long 0x4e8007,0x0e9007,0x0ea007,0x0eb007,0x0ec007,0x0ed007,0x0ee007,0x0ef007 + .long 0x4f0007,0x0f1007,0x0f2007,0x0f3007,0x0f4007,0x0f5007,0x0f6007,0x0f7007 + .long 0x4f8007,0x0f9007,0x0fa007,0x0fb007,0x0fc007,0x0fd007,0x0fe007,0x0ff007 + .long 0x500007,0x001007,0x002007,0x003007,0x004007,0x005007,0x006007,0x007007 + .long 0x508007,0x009007,0x00a007,0x00b007,0x00c007,0x00d007,0x00e007,0x00f007 + .long 0x510007,0x011007,0x012007,0x013007,0x014007,0x015007,0x016007,0x017007 + .long 0x518007,0x019007,0x01a007,0x01b007,0x01c007,0x01d007,0x01e007,0x01f007 + .long 0x520007,0x021007,0x022007,0x023007,0x024007,0x025007,0x026007,0x027007 + .long 0x528007,0x029007,0x02a007,0x02b007,0x02c007,0x02d007,0x02e007,0x02f007 + .long 0x530007,0x031007,0x032007,0x033007,0x034007,0x035007,0x036007,0x037007 + .long 0x538007,0x039007,0x03a007,0x03b007,0x03c007,0x03d007,0x03e007,0x03f007 + .long 0x540007,0x041007,0x042007,0x043007,0x044007,0x045007,0x046007,0x047007 + .long 0x548007,0x049007,0x04a007,0x04b007,0x04c007,0x04d007,0x04e007,0x04f007 + .long 0x550007,0x051007,0x052007,0x053007,0x054007,0x055007,0x056007,0x057007 + .long 0x558007,0x059007,0x05a007,0x05b007,0x05c007,0x05d007,0x05e007,0x05f007 + .long 0x560007,0x061007,0x062007,0x063007,0x064007,0x065007,0x066007,0x067007 + .long 0x568007,0x069007,0x06a007,0x06b007,0x06c007,0x06d007,0x06e007,0x06f007 + .long 0x570007,0x071007,0x072007,0x073007,0x074007,0x075007,0x076007,0x077007 + .long 0x578007,0x079007,0x07a007,0x07b007,0x07c007,0x07d007,0x07e007,0x07f007 + .long 0x580007,0x081007,0x082007,0x083007,0x084007,0x085007,0x086007,0x087007 + .long 0x588007,0x089007,0x08a007,0x08b007,0x08c007,0x08d007,0x08e007,0x08f007 + .long 0x590007,0x091007,0x092007,0x093007,0x094007,0x095007,0x096007,0x097007 + .long 0x598007,0x099007,0x09a007,0x09b007,0x09c007,0x09d007,0x09e007,0x09f007 + .long 0x5a0007,0x0a1007,0x0a2007,0x0a3007,0x0a4007,0x0a5007,0x0a6007,0x0a7007 + .long 0x5a8007,0x0a9007,0x0aa007,0x0ab007,0x0ac007,0x0ad007,0x0ae007,0x0af007 + .long 0x5b0007,0x0b1007,0x0b2007,0x0b3007,0x0b4007,0x0b5007,0x0b6007,0x0b7007 + .long 0x5b8007,0x0b9007,0x0ba007,0x0bb007,0x0bc007,0x0bd007,0x0be007,0x0bf007 + .long 0x5c0007,0x0c1007,0x0c2007,0x0c3007,0x0c4007,0x0c5007,0x0c6007,0x0c7007 + .long 0x5c8007,0x0c9007,0x0ca007,0x0cb007,0x0cc007,0x0cd007,0x0ce007,0x0cf007 + .long 0x5d0007,0x0d1007,0x0d2007,0x0d3007,0x0d4007,0x0d5007,0x0d6007,0x0d7007 + .long 0x5d8007,0x0d9007,0x0da007,0x0db007,0x0dc007,0x0dd007,0x0de007,0x0df007 + .long 0x5e0007,0x0e1007,0x0e2007,0x0e3007,0x0e4007,0x0e5007,0x0e6007,0x0e7007 + .long 0x5e8007,0x0e9007,0x0ea007,0x0eb007,0x0ec007,0x0ed007,0x0ee007,0x0ef007 + .long 0x5f0007,0x0f1007,0x0f2007,0x0f3007,0x0f4007,0x0f5007,0x0f6007,0x0f7007 + .long 0x5f8007,0x0f9007,0x0fa007,0x0fb007,0x0fc007,0x0fd007,0x0fe007,0x0ff007 + .long 0x600007,0x001007,0x002007,0x003007,0x004007,0x005007,0x006007,0x007007 + .long 0x608007,0x009007,0x00a007,0x00b007,0x00c007,0x00d007,0x00e007,0x00f007 + .long 0x610007,0x011007,0x012007,0x013007,0x014007,0x015007,0x016007,0x017007 + .long 0x618007,0x019007,0x01a007,0x01b007,0x01c007,0x01d007,0x01e007,0x01f007 + .long 0x620007,0x021007,0x022007,0x023007,0x024007,0x025007,0x026007,0x027007 + .long 0x628007,0x029007,0x02a007,0x02b007,0x02c007,0x02d007,0x02e007,0x02f007 + .long 0x630007,0x031007,0x032007,0x033007,0x034007,0x035007,0x036007,0x037007 + .long 0x638007,0x039007,0x03a007,0x03b007,0x03c007,0x03d007,0x03e007,0x03f007 + .long 0x640007,0x041007,0x042007,0x043007,0x044007,0x045007,0x046007,0x047007 + .long 0x648007,0x049007,0x04a007,0x04b007,0x04c007,0x04d007,0x04e007,0x04f007 + .long 0x650007,0x051007,0x052007,0x053007,0x054007,0x055007,0x056007,0x057007 + .long 0x658007,0x059007,0x05a007,0x05b007,0x05c007,0x05d007,0x05e007,0x05f007 + .long 0x660007,0x061007,0x062007,0x063007,0x064007,0x065007,0x066007,0x067007 + .long 0x668007,0x069007,0x06a007,0x06b007,0x06c007,0x06d007,0x06e007,0x06f007 + .long 0x670007,0x071007,0x072007,0x073007,0x074007,0x075007,0x076007,0x077007 + .long 0x678007,0x079007,0x07a007,0x07b007,0x07c007,0x07d007,0x07e007,0x07f007 + .long 0x680007,0x081007,0x082007,0x083007,0x084007,0x085007,0x086007,0x087007 + .long 0x688007,0x089007,0x08a007,0x08b007,0x08c007,0x08d007,0x08e007,0x08f007 + .long 0x690007,0x091007,0x092007,0x093007,0x094007,0x095007,0x096007,0x097007 + .long 0x698007,0x099007,0x09a007,0x09b007,0x09c007,0x09d007,0x09e007,0x09f007 + .long 0x6a0007,0x0a1007,0x0a2007,0x0a3007,0x0a4007,0x0a5007,0x0a6007,0x0a7007 + .long 0x6a8007,0x0a9007,0x0aa007,0x0ab007,0x0ac007,0x0ad007,0x0ae007,0x0af007 + .long 0x6b0007,0x0b1007,0x0b2007,0x0b3007,0x0b4007,0x0b5007,0x0b6007,0x0b7007 + .long 0x6b8007,0x0b9007,0x0ba007,0x0bb007,0x0bc007,0x0bd007,0x0be007,0x0bf007 + .long 0x6c0007,0x0c1007,0x0c2007,0x0c3007,0x0c4007,0x0c5007,0x0c6007,0x0c7007 + .long 0x6c8007,0x0c9007,0x0ca007,0x0cb007,0x0cc007,0x0cd007,0x0ce007,0x0cf007 + .long 0x6d0007,0x0d1007,0x0d2007,0x0d3007,0x0d4007,0x0d5007,0x0d6007,0x0d7007 + .long 0x6d8007,0x0d9007,0x0da007,0x0db007,0x0dc007,0x0dd007,0x0de007,0x0df007 + .long 0x6e0007,0x0e1007,0x0e2007,0x0e3007,0x0e4007,0x0e5007,0x0e6007,0x0e7007 + .long 0x6e8007,0x0e9007,0x0ea007,0x0eb007,0x0ec007,0x0ed007,0x0ee007,0x0ef007 + .long 0x6f0007,0x0f1007,0x0f2007,0x0f3007,0x0f4007,0x0f5007,0x0f6007,0x0f7007 + .long 0x6f8007,0x0f9007,0x0fa007,0x0fb007,0x0fc007,0x0fd007,0x0fe007,0x0ff007 + .long 0x700007,0x001007,0x002007,0x003007,0x004007,0x005007,0x006007,0x007007 + .long 0x708007,0x009007,0x00a007,0x00b007,0x00c007,0x00d007,0x00e007,0x00f007 + .long 0x710007,0x011007,0x012007,0x013007,0x014007,0x015007,0x016007,0x017007 + .long 0x718007,0x019007,0x01a007,0x01b007,0x01c007,0x01d007,0x01e007,0x01f007 + .long 0x720007,0x021007,0x022007,0x023007,0x024007,0x025007,0x026007,0x027007 + .long 0x728007,0x029007,0x02a007,0x02b007,0x02c007,0x02d007,0x02e007,0x02f007 + .long 0x730007,0x031007,0x032007,0x033007,0x034007,0x035007,0x036007,0x037007 + .long 0x738007,0x039007,0x03a007,0x03b007,0x03c007,0x03d007,0x03e007,0x03f007 + .long 0x740007,0x041007,0x042007,0x043007,0x044007,0x045007,0x046007,0x047007 + .long 0x748007,0x049007,0x04a007,0x04b007,0x04c007,0x04d007,0x04e007,0x04f007 + .long 0x750007,0x051007,0x052007,0x053007,0x054007,0x055007,0x056007,0x057007 + .long 0x758007,0x059007,0x05a007,0x05b007,0x05c007,0x05d007,0x05e007,0x05f007 + .long 0x760007,0x061007,0x062007,0x063007,0x064007,0x065007,0x066007,0x067007 + .long 0x768007,0x069007,0x06a007,0x06b007,0x06c007,0x06d007,0x06e007,0x06f007 + .long 0x770007,0x071007,0x072007,0x073007,0x074007,0x075007,0x076007,0x077007 + .long 0x778007,0x079007,0x07a007,0x07b007,0x07c007,0x07d007,0x07e007,0x07f007 + .long 0x780007,0x081007,0x082007,0x083007,0x084007,0x085007,0x086007,0x087007 + .long 0x788007,0x089007,0x08a007,0x08b007,0x08c007,0x08d007,0x08e007,0x08f007 + .long 0x790007,0x091007,0x092007,0x093007,0x094007,0x095007,0x096007,0x097007 + .long 0x798007,0x099007,0x09a007,0x09b007,0x09c007,0x09d007,0x09e007,0x09f007 + .long 0x7a0007,0x0a1007,0x0a2007,0x0a3007,0x0a4007,0x0a5007,0x0a6007,0x0a7007 + .long 0x7a8007,0x0a9007,0x0aa007,0x0ab007,0x0ac007,0x0ad007,0x0ae007,0x0af007 + .long 0x7b0007,0x0b1007,0x0b2007,0x0b3007,0x0b4007,0x0b5007,0x0b6007,0x0b7007 + .long 0x7b8007,0x0b9007,0x0ba007,0x0bb007,0x0bc007,0x0bd007,0x0be007,0x0bf007 + .long 0x7c0007,0x0c1007,0x0c2007,0x0c3007,0x0c4007,0x0c5007,0x0c6007,0x0c7007 + .long 0x7c8007,0x0c9007,0x0ca007,0x0cb007,0x0cc007,0x0cd007,0x0ce007,0x0cf007 + .long 0x7d0007,0x0d1007,0x0d2007,0x0d3007,0x0d4007,0x0d5007,0x0d6007,0x0d7007 + .long 0x7d8007,0x0d9007,0x0da007,0x0db007,0x0dc007,0x0dd007,0x0de007,0x0df007 + .long 0x7e0007,0x0e1007,0x0e2007,0x0e3007,0x0e4007,0x0e5007,0x0e6007,0x0e7007 + .long 0x7e8007,0x0e9007,0x0ea007,0x0eb007,0x0ec007,0x0ed007,0x0ee007,0x0ef007 + .long 0x7f0007,0x0f1007,0x0f2007,0x0f3007,0x0f4007,0x0f5007,0x0f6007,0x0f7007 + .long 0x7f8007,0x0f9007,0x0fa007,0x0fb007,0x0fc007,0x0fd007,0x0fe007,0x0ff007 .org 0x4000 -ENTRY(empty_bad_page_table) +ENTRY(empty_zero_page) .org 0x5000 -ENTRY(empty_zero_page) +ENTRY(empty_bad_page) .org 0x6000 +ENTRY(empty_bad_pte_table) + +#if CONFIG_X86_PAE + + .org 0x7000 + ENTRY(empty_bad_pmd_table) + + .org 0x8000 + +#else + + .org 0x7000 + +#endif /* * This starts the data section. Note that the above is all diff --git a/arch/i386/kernel/irq.c b/arch/i386/kernel/irq.c index 8ec329287..75659aac4 100644 --- a/arch/i386/kernel/irq.c +++ b/arch/i386/kernel/irq.c @@ -20,6 +20,7 @@ * Naturally it's not a 1:1 relation, but there are similarities. */ +#include <linux/config.h> #include <linux/ptrace.h> #include <linux/errno.h> #include <linux/signal.h> diff --git a/arch/i386/kernel/pci-i386.c b/arch/i386/kernel/pci-i386.c index af362611d..8e609ec36 100644 --- a/arch/i386/kernel/pci-i386.c +++ b/arch/i386/kernel/pci-i386.c @@ -177,7 +177,7 @@ static int __init pcibios_assign_resource(struct pci_dev *dev, int i) * (4) Assign new addresses to resources which were either * not configured at all or misconfigured. If explicitly * requested by the user, configure expansion ROM address - * as well. Finally enable the I/O and Memory bits. + * as well. */ static void __init pcibios_allocate_bus_resources(struct pci_bus *bus) @@ -252,21 +252,18 @@ static void __init pcibios_allocate_resources(int pass) static void __init pcibios_assign_resources(void) { struct pci_dev *dev; - u16 cmd, old_cmd; int idx; - int fault = 0; struct resource *r; for(dev=pci_devices; dev; dev=dev->next) { - pci_read_config_word(dev, PCI_COMMAND, &cmd); - old_cmd = cmd; for(idx=0; idx<6; idx++) { r = &dev->resource[idx]; if (((dev->class >> 8) == PCI_CLASS_STORAGE_IDE && idx < 4) || - ((dev->class >> 8) == PCI_CLASS_DISPLAY_VGA && (r->flags & IORESOURCE_IO))) + ((dev->class >> 8) == PCI_CLASS_DISPLAY_VGA && (r->flags & IORESOURCE_IO)) || + !dev->class || (dev->class >> 8) == PCI_CLASS_BRIDGE_HOST) /* * Don't touch IDE controllers and I/O ports of video cards! - * Neither enable anything in their command registers. + * Also avoid classless devices and host bridges. */ continue; if (!r->start && r->end) { @@ -275,24 +272,9 @@ static void __init pcibios_assign_resources(void) * the BIOS forgot to do so or because we have decided the old * address was unusable for some reason. */ - if (pcibios_assign_resource(dev, idx) < 0) - fault = 1; - } - if (r->flags & IORESOURCE_IO) - cmd |= PCI_COMMAND_IO; - if (r->flags & IORESOURCE_MEM) - cmd |= PCI_COMMAND_MEMORY; - } - - if (cmd != old_cmd) { - if (fault) - printk("PCI: Not enabling device %s because of resource collisions\n", dev->slot_name); - else { - printk("PCI: Enabling device %s (%04x -> %04x)\n", dev->slot_name, old_cmd, cmd); - pci_write_config_word(dev, PCI_COMMAND, cmd); + pcibios_assign_resource(dev, idx); } } - if (pci_probe & PCI_ASSIGN_ROMS) { r = &dev->resource[PCI_ROM_RESOURCE]; r->end -= r->start; @@ -310,3 +292,29 @@ void __init pcibios_resource_survey(void) pcibios_allocate_resources(1); pcibios_assign_resources(); } + +int pcibios_enable_resources(struct pci_dev *dev) +{ + u16 cmd, old_cmd; + int idx; + struct resource *r; + + pci_read_config_word(dev, PCI_COMMAND, &cmd); + old_cmd = cmd; + for(idx=0; idx<6; idx++) { + r = &dev->resource[idx]; + if (!r->start && r->end) { + printk(KERN_ERR "PCI: Device %s not available because of resource collisions\n", dev->slot_name); + return -EINVAL; + } + if (r->flags & IORESOURCE_IO) + cmd |= PCI_COMMAND_IO; + if (r->flags & IORESOURCE_MEM) + cmd |= PCI_COMMAND_MEMORY; + } + if (cmd != old_cmd) { + printk("PCI: Enabling device %s (%04x -> %04x)\n", dev->slot_name, old_cmd, cmd); + pci_write_config_word(dev, PCI_COMMAND, cmd); + } + return 0; +} diff --git a/arch/i386/kernel/pci-i386.h b/arch/i386/kernel/pci-i386.h index 41ac2b856..1be988b6c 100644 --- a/arch/i386/kernel/pci-i386.h +++ b/arch/i386/kernel/pci-i386.h @@ -18,12 +18,13 @@ #define PCI_NO_SORT 0x100 #define PCI_BIOS_SORT 0x200 #define PCI_NO_CHECKS 0x400 -#define PCI_NO_PEER_FIXUP 0x800 +#define PCI_PEER_FIXUP 0x800 #define PCI_ASSIGN_ROMS 0x1000 -#define PCI_NO_IRQ_SCAN 0x2000 +#define PCI_BIOS_IRQ_SCAN 0x2000 extern unsigned int pci_probe; /* pci-i386.c */ void pcibios_resource_survey(void); +int pcibios_enable_resources(struct pci_dev *); diff --git a/arch/i386/kernel/pci-pc.c b/arch/i386/kernel/pci-pc.c index 8ce187d3f..61d13af55 100644 --- a/arch/i386/kernel/pci-pc.c +++ b/arch/i386/kernel/pci-pc.c @@ -688,7 +688,7 @@ static struct irq_routing_table * __init pcibios_get_irq_routing_table(void) struct irq_routing_table *rt; int ret, map; - if (pci_probe & PCI_NO_IRQ_SCAN) + if (!(pci_probe & PCI_BIOS_IRQ_SCAN)) return NULL; pcibios_irq_page = __get_free_page(GFP_KERNEL); if (!pcibios_irq_page) @@ -868,7 +868,30 @@ static void __init pci_fixup_i450nx(struct pci_dev *d) if (suba < subb) pci_scan_bus(suba+1, pci_root->ops, NULL); /* Bus B */ } - pci_probe |= PCI_NO_PEER_FIXUP; +} + +static void __init pci_fixup_rcc(struct pci_dev *d) +{ + /* + * RCC host bridges -- Find and scan all secondary buses. + * Register 0x44 contains first, 0x45 last bus number routed there. + */ + u8 busno; + pci_read_config_byte(d, 0x44, &busno); + printk("PCI: RCC host bridge: secondary bus %02x\n", busno); + pci_scan_bus(busno, pci_root->ops, NULL); +} + +static void __init pci_fixup_compaq(struct pci_dev *d) +{ + /* + * Compaq host bridges -- Find and scan all secondary buses. + * This time registers 0xc8 and 0xc9. + */ + u8 busno; + pci_read_config_byte(d, 0xc8, &busno); + printk("PCI: Compaq host bridge: secondary bus %02x\n", busno); + pci_scan_bus(busno, pci_root->ops, NULL); } static void __init pci_fixup_umc_ide(struct pci_dev *d) @@ -905,6 +928,9 @@ static void __init pci_fixup_ide_bases(struct pci_dev *d) struct pci_fixup pcibios_fixups[] = { { PCI_FIXUP_HEADER, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82451NX, pci_fixup_i450nx }, + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_RCC, PCI_DEVICE_ID_RCC_HE, pci_fixup_rcc }, + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_RCC, PCI_DEVICE_ID_RCC_LE, pci_fixup_rcc }, + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_6010, pci_fixup_compaq }, { PCI_FIXUP_HEADER, PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8886BF, pci_fixup_umc_ide }, { PCI_FIXUP_HEADER, PCI_ANY_ID, PCI_ANY_ID, pci_fixup_ide_bases }, { 0 } @@ -919,6 +945,8 @@ extern int skip_ioapic_setup; #define PIRQ_SIGNATURE (('$' << 0) + ('P' << 8) + ('I' << 16) + ('R' << 24)) #define PIRQ_VERSION 0x0100 +static struct irq_routing_table *pirq_table; + /* * Search 0xf0000 -- 0xfffff for the PCI IRQ Routing Table. */ @@ -974,7 +1002,6 @@ static void __init pcibios_irq_peer_trick(struct irq_routing_table *rt) */ if (busmap[i] && pci_scan_bus(i, pci_root->ops, NULL)) printk("PCI: Discovered primary peer bus %02x [IRQ]\n", i); - pci_probe |= PCI_NO_PEER_FIXUP; } /* @@ -982,7 +1009,7 @@ static void __init pcibios_irq_peer_trick(struct irq_routing_table *rt) * table, but unfortunately we have to know the interrupt router chip. */ -static char * __init pcibios_lookup_irq(struct pci_dev *dev, struct irq_routing_table *rt, int pin) +static char *pcibios_lookup_irq(struct pci_dev *dev, struct irq_routing_table *rt, int pin, int assign) { struct irq_info *q; struct pci_dev *router; @@ -1012,9 +1039,9 @@ static char * __init pcibios_lookup_irq(struct pci_dev *dev, struct irq_routing_ return NULL; } DBG(" -> PIRQ %02x, mask %04x", pirq, mask); - if ((dev->class >> 8) == PCI_CLASS_DISPLAY_VGA) + if (!assign || (dev->class >> 8) == PCI_CLASS_DISPLAY_VGA) newirq = 0; - else for(newirq = 15; newirq && !(mask & (1 << newirq)); newirq--) + else for(newirq = 13; newirq && !(mask & (1 << newirq)); newirq--) ; if (!(router = pci_find_slot(rt->rtr_bus, rt->rtr_devfn))) { DBG(" -> router not found\n"); @@ -1068,7 +1095,7 @@ static void __init pcibios_fixup_irqs(void) struct pci_dev *dev; u8 pin; - rtable = pcibios_find_irq_routing_table(); + rtable = pirq_table = pcibios_find_irq_routing_table(); #ifdef CONFIG_PCI_BIOS if (!rtable && pci_bios_present) rtable = pcibios_get_irq_routing_table(); @@ -1106,7 +1133,7 @@ static void __init pcibios_fixup_irqs(void) dev->irq = irq; } } - rtable = NULL; /* Avoid IRQ assignment below */ + pirq_table = NULL; /* Avoid automatic IRQ assignment */ } #endif /* @@ -1114,10 +1141,10 @@ static void __init pcibios_fixup_irqs(void) */ if (dev->irq >= NR_IRQS) dev->irq = 0; - if (pin && !dev->irq && rtable && rtable->version) { - char *msg = pcibios_lookup_irq(dev, rtable, pin); + if (pin && !dev->irq && pirq_table) { + char *msg = pcibios_lookup_irq(dev, pirq_table, pin, 0); if (msg) - printk("PCI: Assigned IRQ %d to device %s [%s]\n", dev->irq, dev->slot_name, msg); + printk("PCI: Found IRQ %d for device %s [%s]\n", dev->irq, dev->slot_name, msg); } } @@ -1173,7 +1200,7 @@ void __init pcibios_init(void) pci_scan_bus(0, ops, NULL); pcibios_fixup_irqs(); - if (!(pci_probe & PCI_NO_PEER_FIXUP)) + if (pci_probe & PCI_PEER_FIXUP) pcibios_fixup_peer_bridges(); pcibios_resource_survey(); @@ -1199,8 +1226,8 @@ char * __init pcibios_setup(char *str) } else if (!strcmp(str, "nosort")) { pci_probe |= PCI_NO_SORT; return NULL; - } else if (!strcmp(str, "noirq")) { - pci_probe |= PCI_NO_IRQ_SCAN; + } else if (!strcmp(str, "biosirq")) { + pci_probe |= PCI_BIOS_IRQ_SCAN; return NULL; } #endif @@ -1214,8 +1241,8 @@ char * __init pcibios_setup(char *str) return NULL; } #endif - else if (!strcmp(str, "nopeer")) { - pci_probe |= PCI_NO_PEER_FIXUP; + else if (!strcmp(str, "peer")) { + pci_probe |= PCI_PEER_FIXUP; return NULL; } else if (!strcmp(str, "rom")) { pci_probe |= PCI_ASSIGN_ROMS; @@ -1223,3 +1250,21 @@ char * __init pcibios_setup(char *str) } return str; } + +int pcibios_enable_device(struct pci_dev *dev) +{ + int err; + + if ((err = pcibios_enable_resources(dev)) < 0) + return err; + if (!dev->irq && pirq_table) { + u8 pin; + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); + if (pin) { + char *msg = pcibios_lookup_irq(dev, pirq_table, pin, 1); + if (msg) + printk("PCI: Assigned IRQ %d to device %s [%s]\n", dev->irq, dev->slot_name, msg); + } + } + return 0; +} diff --git a/arch/i386/kernel/pci-visws.c b/arch/i386/kernel/pci-visws.c index 31a767a22..8a954ce8b 100644 --- a/arch/i386/kernel/pci-visws.c +++ b/arch/i386/kernel/pci-visws.c @@ -14,6 +14,7 @@ #include <asm/smp.h> #include <asm/lithium.h> +#include <asm/io.h> #include "pci-i386.h" @@ -129,3 +130,8 @@ char * __init pcibios_setup(char *str) { return str; } + +int pcibios_enable_device(struct pci_dev *dev) +{ + return pcibios_enable_resources(dev); +} diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index 734cfca65..31c77bb1d 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -54,7 +54,8 @@ #ifdef CONFIG_BLK_DEV_RAM #include <linux/blk.h> #endif -#include <linux/bigmem.h> +#include <linux/highmem.h> +#include <linux/bootmem.h> #include <asm/processor.h> #include <linux/console.h> #include <asm/uaccess.h> @@ -401,12 +402,15 @@ void __init add_memory_region(unsigned long start, } /* add_memory_region */ -#define LOWMEMSIZE() ((*(unsigned short *)__va(0x413)) * 1024) - +/* + * Do NOT EVER look at the BIOS memory size location. + * It does not work on many machines. + */ +#define LOWMEMSIZE() (0x9f000) void __init setup_memory_region(void) { -#define E820_DEBUG 0 +#define E820_DEBUG 1 #ifdef E820_DEBUG int i; #endif @@ -432,9 +436,8 @@ void __init setup_memory_region(void) memcpy(e820.map, E820_MAP, e820.nr_map * sizeof e820.map[0]); #ifdef E820_DEBUG for (i=0; i < e820.nr_map; i++) { - printk("e820: %ld @ %08lx ", - (unsigned long)(e820.map[i].size), - (unsigned long)(e820.map[i].addr)); + printk("e820: %08x @ %08x ", (int)e820.map[i].size, + (int)e820.map[i].addr); switch (e820.map[i].type) { case E820_RAM: printk("(usable)\n"); break; @@ -464,48 +467,11 @@ void __init setup_memory_region(void) } /* setup_memory_region */ -void __init setup_arch(char **cmdline_p, unsigned long * memory_start_p, unsigned long * memory_end_p) +static inline void parse_mem_cmdline (char ** cmdline_p) { - unsigned long high_pfn, max_pfn; char c = ' ', *to = command_line, *from = COMMAND_LINE; int len = 0; - int i; - int usermem=0; - -#ifdef CONFIG_VISWS - visws_get_board_type_and_rev(); -#endif - - ROOT_DEV = to_kdev_t(ORIG_ROOT_DEV); - drive_info = DRIVE_INFO; - screen_info = SCREEN_INFO; - apm_bios_info = APM_BIOS_INFO; - if( SYS_DESC_TABLE.length != 0 ) { - MCA_bus = SYS_DESC_TABLE.table[3] &0x2; - machine_id = SYS_DESC_TABLE.table[0]; - machine_submodel_id = SYS_DESC_TABLE.table[1]; - BIOS_revision = SYS_DESC_TABLE.table[2]; - } - aux_device_present = AUX_DEVICE_INFO; - -#ifdef CONFIG_BLK_DEV_RAM - rd_image_start = RAMDISK_FLAGS & RAMDISK_IMAGE_START_MASK; - rd_prompt = ((RAMDISK_FLAGS & RAMDISK_PROMPT_FLAG) != 0); - rd_doload = ((RAMDISK_FLAGS & RAMDISK_LOAD_FLAG) != 0); -#endif - setup_memory_region(); - - if (!MOUNT_ROOT_RDONLY) - root_mountflags &= ~MS_RDONLY; - init_mm.start_code = (unsigned long) &_text; - init_mm.end_code = (unsigned long) &_etext; - init_mm.end_data = (unsigned long) &_edata; - init_mm.brk = (unsigned long) &_end; - - code_resource.start = virt_to_bus(&_text); - code_resource.end = virt_to_bus(&_etext)-1; - data_resource.start = virt_to_bus(&_etext); - data_resource.end = virt_to_bus(&_edata)-1; + int usermem = 0; /* Save unparsed command line copy for /proc/cmdline */ memcpy(saved_command_line, COMMAND_LINE, COMMAND_LINE_SIZE); @@ -519,8 +485,9 @@ void __init setup_arch(char **cmdline_p, unsigned long * memory_start_p, unsigne * "mem=XXX[KkmM]@XXX[KkmM]" defines a memory region from * <start> to <start>+<mem>, overriding the bios size. */ - if (c == ' ' && *(const unsigned long *)from == *(const unsigned long *)"mem=") { - if (to != command_line) to--; + if (c == ' ' && !memcmp(from, "mem=", 4)) { + if (to != command_line) + to--; if (!memcmp(from+4, "nopentium", 9)) { from += 9+4; boot_cpu_data.x86_capability &= ~X86_FEATURE_PSE; @@ -542,7 +509,7 @@ void __init setup_arch(char **cmdline_p, unsigned long * memory_start_p, unsigne } mem_size = memparse(from+4, &from); if (*from == '@') - start_at = memparse(from+1,&from); + start_at = memparse(from+1, &from); else { start_at = HIGH_MEMORY; mem_size -= HIGH_MEMORY; @@ -559,54 +526,166 @@ void __init setup_arch(char **cmdline_p, unsigned long * memory_start_p, unsigne } *to = '\0'; *cmdline_p = command_line; +} - /* Find the highest page frame number we have available */ - max_pfn = 0; - for (i=0; i < e820.nr_map; i++) { - /* RAM? */ - if (e820.map[i].type == E820_RAM) { - unsigned long end_pfn = (e820.map[i].addr + e820.map[i].size) >> PAGE_SHIFT; +void __init setup_arch(char **cmdline_p) +{ + unsigned long bootmap_size; + unsigned long start_pfn, max_pfn, max_low_pfn; + int i; - if (end_pfn > max_pfn) - max_pfn = end_pfn; - } +#ifdef CONFIG_VISWS + visws_get_board_type_and_rev(); +#endif + + ROOT_DEV = to_kdev_t(ORIG_ROOT_DEV); + drive_info = DRIVE_INFO; + screen_info = SCREEN_INFO; + apm_bios_info = APM_BIOS_INFO; + if( SYS_DESC_TABLE.length != 0 ) { + MCA_bus = SYS_DESC_TABLE.table[3] &0x2; + machine_id = SYS_DESC_TABLE.table[0]; + machine_submodel_id = SYS_DESC_TABLE.table[1]; + BIOS_revision = SYS_DESC_TABLE.table[2]; } + aux_device_present = AUX_DEVICE_INFO; -/* - * We can only allocate a limited amount of direct-mapped memory - */ -#define VMALLOC_RESERVE (128 << 20) /* 128MB for vmalloc and initrd */ -#define MAXMEM ((unsigned long)(-PAGE_OFFSET-VMALLOC_RESERVE)) -#define MAXMEM_PFN (MAXMEM >> PAGE_SHIFT) +#ifdef CONFIG_BLK_DEV_RAM + rd_image_start = RAMDISK_FLAGS & RAMDISK_IMAGE_START_MASK; + rd_prompt = ((RAMDISK_FLAGS & RAMDISK_PROMPT_FLAG) != 0); + rd_doload = ((RAMDISK_FLAGS & RAMDISK_LOAD_FLAG) != 0); +#endif + setup_memory_region(); + + if (!MOUNT_ROOT_RDONLY) + root_mountflags &= ~MS_RDONLY; + init_mm.start_code = (unsigned long) &_text; + init_mm.end_code = (unsigned long) &_etext; + init_mm.end_data = (unsigned long) &_edata; + init_mm.brk = (unsigned long) &_end; - high_pfn = MAXMEM_PFN; - if (max_pfn < high_pfn) - high_pfn = max_pfn; + code_resource.start = virt_to_bus(&_text); + code_resource.end = virt_to_bus(&_etext)-1; + data_resource.start = virt_to_bus(&_etext); + data_resource.end = virt_to_bus(&_edata)-1; + + parse_mem_cmdline(cmdline_p); + +#define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) +#define PFN_DOWN(x) ((x) >> PAGE_SHIFT) +#define PFN_PHYS(x) ((x) << PAGE_SHIFT) /* - * But the bigmem stuff may be able to use more of it - * (but currently only up to about 4GB) + * 128MB for vmalloc and initrd */ -#ifdef CONFIG_BIGMEM - #define MAXBIGMEM ((unsigned long)(~(VMALLOC_RESERVE-1))) - #define MAXBIGMEM_PFN (MAXBIGMEM >> PAGE_SHIFT) - if (max_pfn > MAX_PFN) - max_pfn = MAX_PFN; - -/* When debugging, make half of "normal" memory be BIGMEM memory instead */ -#ifdef BIGMEM_DEBUG - high_pfn >>= 1; -#endif +#define VMALLOC_RESERVE (unsigned long)(128 << 20) +#define MAXMEM (unsigned long)(-PAGE_OFFSET-VMALLOC_RESERVE) +#define MAXMEM_PFN PFN_DOWN(MAXMEM) + + /* + * partially used pages are not usable - thus + * we are rounding upwards: + */ + start_pfn = PFN_UP(__pa(&_end)); - bigmem_start = high_pfn << PAGE_SHIFT; - bigmem_end = max_pfn << PAGE_SHIFT; - printk(KERN_NOTICE "%ldMB BIGMEM available.\n", (bigmem_end-bigmem_start) >> 20); + /* + * Find the highest page frame number we have available + */ + max_pfn = 0; + for (i = 0; i < e820.nr_map; i++) { + unsigned long curr_pfn; + /* RAM? */ + if (e820.map[i].type != E820_RAM) + continue; + curr_pfn = PFN_DOWN(e820.map[i].addr + e820.map[i].size); + if (curr_pfn > max_pfn) + max_pfn = curr_pfn; + } + + /* + * Determine low and high memory ranges: + */ + max_low_pfn = max_pfn; + if (max_low_pfn > MAXMEM_PFN) + max_low_pfn = MAXMEM_PFN; + +#ifdef CONFIG_HIGHMEM + highstart_pfn = highend_pfn = max_pfn; + if (max_pfn > MAXMEM_PFN) { + highstart_pfn = MAXMEM_PFN; + highend_pfn = max_pfn; + printk(KERN_NOTICE "%ldMB HIGHMEM available.\n", + pages_to_mb(highend_pfn - highstart_pfn)); + } #endif + /* + * Initialize the boot-time allocator (with low memory only): + */ + bootmap_size = init_bootmem(start_pfn, max_low_pfn); + + /* + * FIXME: what about high memory? + */ + ram_resources[1].end = PFN_PHYS(max_low_pfn); + + /* + * Register fully available low RAM pages with the bootmem allocator. + */ + for (i = 0; i < e820.nr_map; i++) { + unsigned long curr_pfn, last_pfn, size; + /* + * Reserve usable low memory + */ + if (e820.map[i].type != E820_RAM) + continue; + /* + * We are rounding up the start address of usable memory: + */ + curr_pfn = PFN_UP(e820.map[i].addr); + if (curr_pfn >= max_low_pfn) + continue; + /* + * ... and at the end of the usable range downwards: + */ + last_pfn = PFN_DOWN(e820.map[i].addr + e820.map[i].size); - ram_resources[1].end = (high_pfn << PAGE_SHIFT)-1; + if (last_pfn > max_low_pfn) + last_pfn = max_low_pfn; - *memory_start_p = (unsigned long) &_end; - *memory_end_p = PAGE_OFFSET + (high_pfn << PAGE_SHIFT); + /* + * .. finally, did all the rounding and playing + * around just make the area go away? + */ + if (last_pfn <= curr_pfn) + continue; + + size = last_pfn - curr_pfn; + free_bootmem(PFN_PHYS(curr_pfn), PFN_PHYS(size)); + } + /* + * Reserve the bootmem bitmap itself as well. We do this in two + * steps (first step was init_bootmem()) because this catches + * the (very unlikely) case of us accidentally initializing the + * bootmem allocator with an invalid RAM area. + */ + reserve_bootmem(HIGH_MEMORY, (PFN_PHYS(start_pfn) + + bootmap_size + PAGE_SIZE-1) - (HIGH_MEMORY)); + + /* + * reserve physical page 0 - it's a special BIOS page on many boxes, + * enabling clean reboots, SMP operation, laptop functions. + */ + reserve_bootmem(0, PAGE_SIZE); + +#ifdef __SMP__ + /* + * But first pinch a few for the stack/trampoline stuff + * FIXME: Don't need the extra page at 4K, but need to fix + * trampoline before removing it. (see the GDT stuff) + */ + reserve_bootmem(PAGE_SIZE, PAGE_SIZE); + smp_alloc_memory(); /* AP processor realmode stacks in low memory*/ +#endif #ifdef __SMP__ /* @@ -616,10 +695,11 @@ void __init setup_arch(char **cmdline_p, unsigned long * memory_start_p, unsigne #endif #ifdef CONFIG_BLK_DEV_INITRD +// FIXME needs to do the new bootmem alloc stuff if (LOADER_TYPE) { initrd_start = INITRD_START ? INITRD_START + PAGE_OFFSET : 0; initrd_end = initrd_start+INITRD_SIZE; - if (initrd_end > memory_end) { + if (initrd_end > (max_low_pfn << PAGE_SHIFT)) { printk("initrd extends beyond end of memory " "(0x%08lx > 0x%08lx)\ndisabling initrd\n", initrd_end,memory_end); diff --git a/arch/i386/kernel/smpboot.c b/arch/i386/kernel/smpboot.c index 46335ee8f..4386e8dd0 100644 --- a/arch/i386/kernel/smpboot.c +++ b/arch/i386/kernel/smpboot.c @@ -39,6 +39,7 @@ #include <linux/kernel_stat.h> #include <linux/smp_lock.h> #include <linux/irq.h> +#include <linux/bootmem.h> #include <linux/delay.h> #include <linux/mc146818rtc.h> @@ -630,12 +631,15 @@ static unsigned long __init setup_trampoline(void) * We are called very early to get the low memory for the * SMP bootup trampoline page. */ -unsigned long __init smp_alloc_memory(unsigned long mem_base) +void __init smp_alloc_memory(void) { - if (virt_to_phys((void *)mem_base) >= 0x9F000) + trampoline_base = (void *) alloc_bootmem_low_pages(PAGE_SIZE); + /* + * Has to be in very low memory so we can execute + * real-mode AP code. + */ + if (__pa(trampoline_base) >= 0x9F000) BUG(); - trampoline_base = (void *)mem_base; - return mem_base + PAGE_SIZE; } /* @@ -804,11 +808,10 @@ void __init setup_local_APIC(void) apic_write(APIC_DFR, value); } -unsigned long __init init_smp_mappings(unsigned long memory_start) +void __init init_smp_mappings(void) { unsigned long apic_phys; - memory_start = PAGE_ALIGN(memory_start); if (smp_found_config) { apic_phys = mp_lapic_addr; } else { @@ -818,11 +821,10 @@ unsigned long __init init_smp_mappings(unsigned long memory_start) * could use the real zero-page, but it's safer * this way if some buggy code writes to this page ... */ - apic_phys = __pa(memory_start); - memset((void *)memory_start, 0, PAGE_SIZE); - memory_start += PAGE_SIZE; + apic_phys = __pa(alloc_bootmem_pages(PAGE_SIZE)); + memset((void *)apic_phys, 0, PAGE_SIZE); } - set_fixmap(FIX_APIC_BASE,apic_phys); + set_fixmap(FIX_APIC_BASE, apic_phys); dprintk("mapped APIC to %08lx (%08lx)\n", APIC_BASE, apic_phys); #ifdef CONFIG_X86_IO_APIC @@ -834,9 +836,8 @@ unsigned long __init init_smp_mappings(unsigned long memory_start) if (smp_found_config) { ioapic_phys = mp_ioapics[i].mpc_apicaddr; } else { - ioapic_phys = __pa(memory_start); - memset((void *)memory_start, 0, PAGE_SIZE); - memory_start += PAGE_SIZE; + ioapic_phys = __pa(alloc_bootmem_pages(PAGE_SIZE)); + memset((void *)ioapic_phys, 0, PAGE_SIZE); } set_fixmap(idx,ioapic_phys); dprintk("mapped IOAPIC to %08lx (%08lx)\n", @@ -845,8 +846,6 @@ unsigned long __init init_smp_mappings(unsigned long memory_start) } } #endif - - return memory_start; } /* @@ -1112,6 +1111,12 @@ int __init start_secondary(void *unused) smp_callin(); while (!atomic_read(&smp_commenced)) /* nothing */ ; + /* + * low-memory mappings have been cleared, flush them from + * the local TLBs too. + */ + local_flush_tlb(); + return cpu_idle(); } @@ -1153,7 +1158,6 @@ static int __init fork_by_hand(void) static void __init do_boot_cpu(int i) { unsigned long cfg; - pgd_t maincfg; struct task_struct *idle; unsigned long send_status, accept_status; int timeout, num_starts, j; @@ -1207,9 +1211,6 @@ static void __init do_boot_cpu(int i) *((volatile unsigned short *) phys_to_virt(0x467)) = start_eip & 0xf; dprintk("3.\n"); - maincfg=swapper_pg_dir[0]; - ((unsigned long *)swapper_pg_dir)[0]=0x102007; - /* * Be paranoid about clearing APIC errors. */ @@ -1367,9 +1368,6 @@ static void __init do_boot_cpu(int i) cpucount--; } - swapper_pg_dir[0]=maincfg; - local_flush_tlb(); - /* mark "stuck" area as not stuck */ *((volatile unsigned long *)phys_to_virt(8192)) = 0; } @@ -1567,14 +1565,9 @@ void __init smp_boot_cpus(void) #ifndef CONFIG_VISWS { - unsigned long cfg; - /* * Install writable page 0 entry to set BIOS data area. */ - cfg = pg0[0]; - /* writeable, present, addr 0 */ - pg0[0] = _PAGE_RW | _PAGE_PRESENT | 0; local_flush_tlb(); /* @@ -1584,12 +1577,6 @@ void __init smp_boot_cpus(void) CMOS_WRITE(0, 0xf); *((volatile long *) phys_to_virt(0x467)) = 0; - - /* - * Restore old page 0 entry. - */ - pg0[0] = cfg; - local_flush_tlb(); } #endif @@ -1646,5 +1633,7 @@ smp_done: */ if (cpu_has_tsc && cpucount) synchronize_tsc_bp(); + + zap_low_mappings(); } diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c index ebd1cd002..f66f2363c 100644 --- a/arch/i386/kernel/traps.c +++ b/arch/i386/kernel/traps.c @@ -581,6 +581,7 @@ asmlinkage void math_emulate(long arg) #endif /* CONFIG_MATH_EMULATION */ +#ifndef CONFIG_M686 void __init trap_init_f00f_bug(void) { unsigned long page; @@ -596,8 +597,8 @@ void __init trap_init_f00f_bug(void) pgd = pgd_offset(&init_mm, page); pmd = pmd_offset(pgd, page); pte = pte_offset(pmd, page); - free_page(pte_page(*pte)); - *pte = mk_pte(&idt_table, PAGE_KERNEL_RO); + __free_page(pte_page(*pte)); + *pte = mk_pte_phys(__pa(&idt_table), PAGE_KERNEL_RO); local_flush_tlb(); /* @@ -608,6 +609,7 @@ void __init trap_init_f00f_bug(void) idt = (struct desc_struct *)page; __asm__ __volatile__("lidt %0": "=m" (idt_descr)); } +#endif #define _set_gate(gate_addr,type,dpl,addr) \ do { \ @@ -772,7 +774,7 @@ cobalt_init(void) #endif void __init trap_init(void) { - if (readl(0x0FFFD9) == 'E' + ('I'<<8) + ('S'<<16) + ('A'<<24)) + if (isa_readl(0x0FFFD9) == 'E'+('I'<<8)+('S'<<16)+('A'<<24)) EISA_bus = 1; set_trap_gate(0,÷_error); diff --git a/arch/i386/kernel/visws_apic.c b/arch/i386/kernel/visws_apic.c index de79fe61e..6c767d0eb 100644 --- a/arch/i386/kernel/visws_apic.c +++ b/arch/i386/kernel/visws_apic.c @@ -37,7 +37,7 @@ #include <asm/cobalt.h> -#include "irq.h" +#include <linux/irq.h> /* * This is the PIIX4-based 8259 that is wired up indirectly to Cobalt diff --git a/arch/i386/kernel/vm86.c b/arch/i386/kernel/vm86.c index 65dd7e9da..3fd5262ac 100644 --- a/arch/i386/kernel/vm86.c +++ b/arch/i386/kernel/vm86.c @@ -102,7 +102,7 @@ static void mark_screen_rdonly(struct task_struct * tsk) if (pgd_none(*pgd)) return; if (pgd_bad(*pgd)) { - printk("vm86: bad pgd entry [%p]:%08lx\n", pgd, pgd_val(*pgd)); + pgd_ERROR(*pgd); pgd_clear(pgd); return; } @@ -110,7 +110,7 @@ static void mark_screen_rdonly(struct task_struct * tsk) if (pmd_none(*pmd)) return; if (pmd_bad(*pmd)) { - printk("vm86: bad pmd entry [%p]:%08lx\n", pmd, pmd_val(*pmd)); + pmd_ERROR(*pmd); pmd_clear(pmd); return; } diff --git a/arch/i386/lib/Makefile b/arch/i386/lib/Makefile index 3f7bef4aa..5c824c08c 100644 --- a/arch/i386/lib/Makefile +++ b/arch/i386/lib/Makefile @@ -7,7 +7,7 @@ L_TARGET = lib.a L_OBJS = checksum.o old-checksum.o delay.o \ - usercopy.o getuser.o putuser.o + usercopy.o getuser.o putuser.o iodebug.o ifdef CONFIG_X86_USE_3DNOW L_OBJS += mmx.o diff --git a/arch/i386/lib/iodebug.c b/arch/i386/lib/iodebug.c new file mode 100644 index 000000000..701a07fe7 --- /dev/null +++ b/arch/i386/lib/iodebug.c @@ -0,0 +1,19 @@ +#include <asm/io.h> + +void * __io_virt_debug(unsigned long x, const char *file, int line) +{ + if (x < PAGE_OFFSET) { + printk("io mapaddr 0x%05lx not valid at %s:%d!\n", x, file, line); + return __va(x); + } + return (void *)x; +} + +unsigned long __io_phys_debug(unsigned long x, const char *file, int line) +{ + if (x < PAGE_OFFSET) { + printk("io mapaddr 0x%05lx not valid at %s:%d!\n", x, file, line); + return x; + } + return __pa(x); +} diff --git a/arch/i386/mm/Makefile b/arch/i386/mm/Makefile index d60bc1969..cee7d4e6d 100644 --- a/arch/i386/mm/Makefile +++ b/arch/i386/mm/Makefile @@ -10,8 +10,4 @@ O_TARGET := mm.o O_OBJS := init.o fault.o ioremap.o extable.o -ifeq ($(CONFIG_BIGMEM),y) -O_OBJS += bigmem.o -endif - include $(TOPDIR)/Rules.make diff --git a/arch/i386/mm/bigmem.c b/arch/i386/mm/bigmem.c deleted file mode 100644 index 8da077927..000000000 --- a/arch/i386/mm/bigmem.c +++ /dev/null @@ -1,33 +0,0 @@ -/* - * BIGMEM IA32 code and variables. - * - * (C) 1999 Andrea Arcangeli, SuSE GmbH, andrea@suse.de - * Gerhard Wichert, Siemens AG, Gerhard.Wichert@pdb.siemens.de - */ - -#include <linux/mm.h> -#include <linux/bigmem.h> - -unsigned long bigmem_start, bigmem_end; - -/* NOTE: fixmap_init alloc all the fixmap pagetables contigous on the - physical space so we can cache the place of the first one and move - around without checking the pgd every time. */ -pte_t *kmap_pte; -pgprot_t kmap_prot; - -#define kmap_get_fixmap_pte(vaddr) \ - pte_offset(pmd_offset(pgd_offset_k(vaddr), (vaddr)), (vaddr)) - -void __init kmap_init(void) -{ - unsigned long kmap_vstart; - - /* cache the first kmap pte */ - kmap_vstart = __fix_to_virt(FIX_KMAP_BEGIN); - kmap_pte = kmap_get_fixmap_pte(kmap_vstart); - - kmap_prot = PAGE_KERNEL; - if (boot_cpu_data.x86_capability & X86_FEATURE_PGE) - pgprot_val(kmap_prot) |= _PAGE_GLOBAL; -} diff --git a/arch/i386/mm/fault.c b/arch/i386/mm/fault.c index 1f7879005..b2a98859b 100644 --- a/arch/i386/mm/fault.c +++ b/arch/i386/mm/fault.c @@ -76,6 +76,31 @@ bad_area: return 0; } +static inline void handle_wp_test (void) +{ + const unsigned long vaddr = PAGE_OFFSET; + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; + + /* + * make it read/writable temporarily, so that the fault + * can be handled. + */ + pgd = swapper_pg_dir + __pgd_offset(vaddr); + pmd = pmd_offset(pgd, vaddr); + pte = pte_offset(pmd, vaddr); + *pte = mk_pte_phys(0, PAGE_KERNEL); + local_flush_tlb(); + + boot_cpu_data.wp_works_ok = 1; + /* + * Beware: Black magic here. The printk is needed here to flush + * CPU state on certain buggy processors. + */ + printk("Ok"); +} + asmlinkage void do_invalid_op(struct pt_regs *, unsigned long); extern unsigned long idt; @@ -226,15 +251,8 @@ no_context: * First we check if it was the bootup rw-test, though.. */ if (boot_cpu_data.wp_works_ok < 0 && - address == PAGE_OFFSET && (error_code & 1)) { - boot_cpu_data.wp_works_ok = 1; - pg0[0] = pte_val(mk_pte(PAGE_OFFSET, PAGE_KERNEL)); - local_flush_tlb(); - /* - * Beware: Black magic here. The printk is needed here to flush - * CPU state on certain buggy processors. - */ - printk("Ok"); + address == PAGE_OFFSET && (error_code & 1)) { + handle_wp_test(); return; } diff --git a/arch/i386/mm/init.c b/arch/i386/mm/init.c index b1140f892..d2089fbd0 100644 --- a/arch/i386/mm/init.c +++ b/arch/i386/mm/init.c @@ -22,7 +22,9 @@ #ifdef CONFIG_BLK_DEV_INITRD #include <linux/blk.h> #endif -#include <linux/bigmem.h> +#include <linux/highmem.h> +#include <linux/pagemap.h> +#include <linux/bootmem.h> #include <asm/processor.h> #include <asm/system.h> @@ -32,22 +34,81 @@ #include <asm/fixmap.h> #include <asm/e820.h> -static unsigned long totalram = 0; -static unsigned long totalbig = 0; +unsigned long highstart_pfn, highend_pfn; +static unsigned long totalram_pages = 0; +static unsigned long totalhigh_pages = 0; extern void show_net_buffers(void); -extern unsigned long init_smp_mappings(unsigned long); -void __bad_pte_kernel(pmd_t *pmd) +/* + * BAD_PAGE is the page that is used for page faults when linux + * is out-of-memory. Older versions of linux just did a + * do_exit(), but using this instead means there is less risk + * for a process dying in kernel mode, possibly leaving an inode + * unused etc.. + * + * BAD_PAGETABLE is the accompanying page-table: it is initialized + * to point to BAD_PAGE entries. + * + * ZERO_PAGE is a special page that is used for zero-initialized + * data and COW. + */ + +/* + * These are allocated in head.S so that we get proper page alignment. + * If you change the size of these then change head.S as well. + */ +extern char empty_bad_page[PAGE_SIZE]; +#if CONFIG_X86_PAE +extern pmd_t empty_bad_pmd_table[PTRS_PER_PMD]; +#endif +extern pte_t empty_bad_pte_table[PTRS_PER_PTE]; + +/* + * We init them before every return and make them writable-shared. + * This guarantees we get out of the kernel in some more or less sane + * way. + */ +#if CONFIG_X86_PAE +static pmd_t * get_bad_pmd_table(void) { - printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd)); - pmd_val(*pmd) = _KERNPG_TABLE + __pa(BAD_PAGETABLE); + pmd_t v; + int i; + + pmd_val(v) = _PAGE_TABLE + __pa(empty_bad_pte_table); + + for (i = 0; i < PAGE_SIZE/sizeof(pmd_t); i++) + empty_bad_pmd_table[i] = v; + + return empty_bad_pmd_table; } +#endif -void __bad_pte(pmd_t *pmd) +static pte_t * get_bad_pte_table(void) { - printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd)); - pmd_val(*pmd) = _PAGE_TABLE + __pa(BAD_PAGETABLE); + pte_t v; + int i; + + v = pte_mkdirty(mk_pte_phys(__pa(empty_bad_page), PAGE_SHARED)); + + for (i = 0; i < PAGE_SIZE/sizeof(pte_t); i++) + empty_bad_pte_table[i] = v; + + return empty_bad_pte_table; +} + + + +void __handle_bad_pmd(pmd_t *pmd) +{ + pmd_ERROR(*pmd); + pmd_val(*pmd) = _PAGE_TABLE + __pa(get_bad_pte_table()); +} + +void __handle_bad_pmd_kernel(pmd_t *pmd) +{ + pmd_ERROR(*pmd); + pmd_val(*pmd) = _KERNPG_TABLE + __pa(get_bad_pte_table()); } pte_t *get_pte_kernel_slow(pmd_t *pmd, unsigned long offset) @@ -57,16 +118,16 @@ pte_t *get_pte_kernel_slow(pmd_t *pmd, unsigned long offset) pte = (pte_t *) __get_free_page(GFP_KERNEL); if (pmd_none(*pmd)) { if (pte) { - clear_page((unsigned long)pte); + clear_page(pte); pmd_val(*pmd) = _KERNPG_TABLE + __pa(pte); return pte + offset; } - pmd_val(*pmd) = _KERNPG_TABLE + __pa(BAD_PAGETABLE); + pmd_val(*pmd) = _KERNPG_TABLE + __pa(get_bad_pte_table()); return NULL; } free_page((unsigned long)pte); if (pmd_bad(*pmd)) { - __bad_pte_kernel(pmd); + __handle_bad_pmd_kernel(pmd); return NULL; } return (pte_t *) pmd_page(*pmd) + offset; @@ -79,19 +140,19 @@ pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset) pte = (unsigned long) __get_free_page(GFP_KERNEL); if (pmd_none(*pmd)) { if (pte) { - clear_page(pte); + clear_page((void *)pte); pmd_val(*pmd) = _PAGE_TABLE + __pa(pte); - return (pte_t *)(pte + offset); + return (pte_t *)pte + offset; } - pmd_val(*pmd) = _PAGE_TABLE + __pa(BAD_PAGETABLE); + pmd_val(*pmd) = _PAGE_TABLE + __pa(get_bad_pte_table()); return NULL; } free_page(pte); if (pmd_bad(*pmd)) { - __bad_pte(pmd); + __handle_bad_pmd(pmd); return NULL; } - return (pte_t *) (pmd_page(*pmd) + offset); + return (pte_t *) pmd_page(*pmd) + offset; } int do_check_pgt_cache(int low, int high) @@ -110,52 +171,36 @@ int do_check_pgt_cache(int low, int high) return freed; } -/* - * BAD_PAGE is the page that is used for page faults when linux - * is out-of-memory. Older versions of linux just did a - * do_exit(), but using this instead means there is less risk - * for a process dying in kernel mode, possibly leaving an inode - * unused etc.. - * - * BAD_PAGETABLE is the accompanying page-table: it is initialized - * to point to BAD_PAGE entries. - * - * ZERO_PAGE is a special page that is used for zero-initialized - * data and COW. - */ -pte_t * __bad_pagetable(void) -{ - extern char empty_bad_page_table[PAGE_SIZE]; - int d0, d1; - - __asm__ __volatile__("cld ; rep ; stosl" - : "=&D" (d0), "=&c" (d1) - : "a" (pte_val(BAD_PAGE)), - "0" ((long) empty_bad_page_table), - "1" (PAGE_SIZE/4) - : "memory"); - return (pte_t *) empty_bad_page_table; -} +/* NOTE: fixmap_init alloc all the fixmap pagetables contigous on the + physical space so we can cache the place of the first one and move + around without checking the pgd every time. */ + +#if CONFIG_HIGHMEM +pte_t *kmap_pte; +pgprot_t kmap_prot; -pte_t __bad_page(void) +#define kmap_get_fixmap_pte(vaddr) \ + pte_offset(pmd_offset(pgd_offset_k(vaddr), (vaddr)), (vaddr)) + +void __init kmap_init(void) { - extern char empty_bad_page[PAGE_SIZE]; - int d0, d1; - - __asm__ __volatile__("cld ; rep ; stosl" - : "=&D" (d0), "=&c" (d1) - : "a" (0), - "0" ((long) empty_bad_page), - "1" (PAGE_SIZE/4) - : "memory"); - return pte_mkdirty(mk_pte((unsigned long) empty_bad_page, PAGE_SHARED)); + unsigned long kmap_vstart; + + /* cache the first kmap pte */ + kmap_vstart = __fix_to_virt(FIX_KMAP_BEGIN); + kmap_pte = kmap_get_fixmap_pte(kmap_vstart); + + kmap_prot = PAGE_KERNEL; + if (boot_cpu_data.x86_capability & X86_FEATURE_PGE) + pgprot_val(kmap_prot) |= _PAGE_GLOBAL; } +#endif void show_mem(void) { - int i,free = 0,total = 0,reserved = 0; + int i,free = 0, total = 0, reserved = 0; int shared = 0, cached = 0; - int bigmem = 0; + int highmem = 0; printk("Mem-info:\n"); show_free_areas(); @@ -163,8 +208,8 @@ void show_mem(void) i = max_mapnr; while (i-- > 0) { total++; - if (PageBIGMEM(mem_map+i)) - bigmem++; + if (PageHighMem(mem_map+i)) + highmem++; if (PageReserved(mem_map+i)) reserved++; else if (PageSwapCache(mem_map+i)) @@ -174,59 +219,42 @@ void show_mem(void) else shared += page_count(mem_map+i) - 1; } - printk("%d pages of RAM\n",total); - printk("%d pages of BIGMEM\n",bigmem); + printk("%d pages of RAM\n", total); + printk("%d pages of HIGHMEM\n",highmem); printk("%d reserved pages\n",reserved); printk("%d pages shared\n",shared); printk("%d pages swap cached\n",cached); printk("%ld pages in page table cache\n",pgtable_cache_size); + show_buffers(); #ifdef CONFIG_NET show_net_buffers(); #endif } -extern unsigned long free_area_init(unsigned long, unsigned long); - /* References to section boundaries */ extern char _text, _etext, _edata, __bss_start, _end; extern char __init_begin, __init_end; -/* - * allocate page table(s) for compile-time fixed mappings - */ -static unsigned long __init fixmap_init(unsigned long start_mem) -{ - pgd_t * pg_dir; - unsigned int idx; - unsigned long address; - - start_mem = PAGE_ALIGN(start_mem); - - for (idx=1; idx <= __end_of_fixed_addresses; idx += PTRS_PER_PTE) - { - address = __fix_to_virt(__end_of_fixed_addresses-idx); - pg_dir = swapper_pg_dir + (address >> PGDIR_SHIFT); - memset((void *)start_mem, 0, PAGE_SIZE); - pgd_val(*pg_dir) = _PAGE_TABLE | __pa(start_mem); - start_mem += PAGE_SIZE; - } - - return start_mem; -} - static void set_pte_phys (unsigned long vaddr, unsigned long phys) { pgprot_t prot; - pte_t * pte; + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; - pte = pte_offset(pmd_offset(pgd_offset_k(vaddr), vaddr), vaddr); + pgd = swapper_pg_dir + __pgd_offset(vaddr); + pmd = pmd_offset(pgd, vaddr); + pte = pte_offset(pmd, vaddr); prot = PAGE_KERNEL; if (boot_cpu_data.x86_capability & X86_FEATURE_PGE) pgprot_val(prot) |= _PAGE_GLOBAL; set_pte(pte, mk_pte_phys(phys, prot)); - local_flush_tlb(); + /* + * It's enough to flush this one mapping. + */ + __flush_tlb_one(vaddr); } void set_fixmap (enum fixed_addresses idx, unsigned long phys) @@ -240,6 +268,123 @@ void set_fixmap (enum fixed_addresses idx, unsigned long phys) set_pte_phys (address,phys); } +static void __init pagetable_init(void) +{ + pgd_t *pgd, *pgd_base; + pmd_t *pmd; + pte_t *pte; + int i, j, k; + unsigned long vaddr; + unsigned long end = (unsigned long)__va(max_low_pfn*PAGE_SIZE); + + pgd_base = swapper_pg_dir; + + vaddr = PAGE_OFFSET; + i = __pgd_offset(vaddr); + pgd = pgd_base + i; + + for (; (i < PTRS_PER_PGD) && (vaddr <= end); pgd++, i++) { + vaddr = i*PGDIR_SIZE; +#if CONFIG_X86_PAE + pmd = (pmd_t *) alloc_bootmem_low_pages(PAGE_SIZE); + memset((void*)pmd, 0, PAGE_SIZE); + pgd_val(*pgd) = __pa(pmd) + 0x1; +#else + pmd = (pmd_t *)pgd; +#endif + if (pmd != pmd_offset(pgd, 0)) + BUG(); + for (j = 0; (j < PTRS_PER_PMD) && (vaddr <= end); pmd++, j++) { + vaddr = i*PGDIR_SIZE + j*PMD_SIZE; + if (cpu_has_pse) { + unsigned long __pe; + + set_in_cr4(X86_CR4_PSE); + boot_cpu_data.wp_works_ok = 1; + __pe = _KERNPG_TABLE + _PAGE_PSE + __pa(vaddr); + /* Make it "global" too if supported */ + if (cpu_has_pge) { + set_in_cr4(X86_CR4_PGE); + __pe += _PAGE_GLOBAL; + } + pmd_val(*pmd) = __pe; + continue; + } + + pte = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE); + memset((void*)pte, 0, PAGE_SIZE); + pmd_val(*pmd) = _KERNPG_TABLE + __pa(pte); + + if (pte != pte_offset(pmd, 0)) + BUG(); + + for (k = 0; + (k < PTRS_PER_PTE) && (vaddr <= end); + pte++, k++) { + vaddr = i*PGDIR_SIZE + j*PMD_SIZE + k*PAGE_SIZE; + *pte = mk_pte_phys(__pa(vaddr), PAGE_KERNEL); + } + } + } + + /* + * Fixed mappings, only the page table structure has to be + * created - mappings will be set by set_fixmap(): + */ + + vaddr = __fix_to_virt(__end_of_fixed_addresses - 1) & PMD_MASK; + i = __pgd_offset(vaddr); + j = __pmd_offset(vaddr); + pgd = pgd_base + i; + + for ( ; (i < PTRS_PER_PGD) && vaddr; pgd++, i++) { +#if CONFIG_X86_PAE + if (pgd_none(*pgd)) { + pmd = (pmd_t *) alloc_bootmem_low_pages(PAGE_SIZE); + memset((void*)pmd, 0, PAGE_SIZE); + pgd_val(*pgd) = __pa(pmd) + 0x1; + if (pmd != pmd_offset(pgd, vaddr)) + BUG(); + } + pmd = pmd_offset(pgd, vaddr); +#else + pmd = (pmd_t *)pgd; +#endif + for (; (j < PTRS_PER_PMD) && vaddr; pmd++, j++) { + if (pmd_none(*pmd)) { + pte = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE); + memset((void*)pte, 0, PAGE_SIZE); + pmd_val(*pmd) = _KERNPG_TABLE + __pa(pte); + if (pte != pte_offset(pmd, 0)) + BUG(); + } + vaddr += PMD_SIZE; + } + j = 0; + } + +#if CONFIG_X86_PAE + /* + * Add low memory identity-mappings - SMP needs it when + * starting up on an AP from real-mode. In the non-PAE + * case we already have these mappings through head.S. + * All user-space mappings are explicitly cleared after + * SMP startup. + */ + pgd_base[0] = pgd_base[USER_PTRS_PER_PGD]; +#endif +} + +void __init zap_low_mappings (void) +{ + int i; + /* + * Zap initial low-memory mappings: + */ + for (i = 0; i < USER_PTRS_PER_PGD; i++) + pgd_clear(swapper_pg_dir + i); +} + /* * paging_init() sets up the page tables - note that the first 4MB are * already mapped by head.S. @@ -247,89 +392,36 @@ void set_fixmap (enum fixed_addresses idx, unsigned long phys) * This routines also unmaps the page at virtual kernel address 0, so * that we can trap those pesky NULL-reference errors in the kernel. */ -unsigned long __init paging_init(unsigned long start_mem, unsigned long end_mem) +void __init paging_init(void) { - pgd_t * pg_dir; - pte_t * pg_table; - unsigned long tmp; - unsigned long address; + pagetable_init(); -/* - * Physical page 0 is special; it's not touched by Linux since BIOS - * and SMM (for laptops with [34]86/SL chips) may need it. It is read - * and write protected to detect null pointer references in the - * kernel. - * It may also hold the MP configuration table when we are booting SMP. - */ - start_mem = PAGE_ALIGN(start_mem); - address = PAGE_OFFSET; - pg_dir = swapper_pg_dir; - /* unmap the original low memory mappings */ - pgd_val(pg_dir[0]) = 0; - - /* Map whole memory from PAGE_OFFSET */ - pg_dir += USER_PGD_PTRS; - while (address < end_mem) { - /* - * If we're running on a Pentium CPU, we can use the 4MB - * page tables. - * - * The page tables we create span up to the next 4MB - * virtual memory boundary, but that's OK as we won't - * use that memory anyway. - */ - if (boot_cpu_data.x86_capability & X86_FEATURE_PSE) { - unsigned long __pe; - - set_in_cr4(X86_CR4_PSE); - boot_cpu_data.wp_works_ok = 1; - __pe = _KERNPG_TABLE + _PAGE_4M + __pa(address); - /* Make it "global" too if supported */ - if (boot_cpu_data.x86_capability & X86_FEATURE_PGE) { - set_in_cr4(X86_CR4_PGE); - __pe += _PAGE_GLOBAL; - } - pgd_val(*pg_dir) = __pe; - pg_dir++; - address += 4*1024*1024; - continue; - } + __asm__( "movl %%ecx,%%cr3\n" ::"c"(__pa(swapper_pg_dir))); - /* - * We're on a [34]86, use normal page tables. - * pg_table is physical at this point - */ - pg_table = (pte_t *) (PAGE_MASK & pgd_val(*pg_dir)); - if (!pg_table) { - pg_table = (pte_t *) __pa(start_mem); - start_mem += PAGE_SIZE; - } +#if CONFIG_X86_PAE + /* + * We will bail out later - printk doesnt work right now so + * the user would just see a hanging kernel. + */ + if (cpu_has_pae) + set_in_cr4(X86_CR4_PAE); +#endif + + __flush_tlb(); - pgd_val(*pg_dir) = _PAGE_TABLE | (unsigned long) pg_table; - pg_dir++; - - /* now change pg_table to kernel virtual addresses */ - pg_table = (pte_t *) __va(pg_table); - for (tmp = 0 ; tmp < PTRS_PER_PTE ; tmp++,pg_table++) { - pte_t pte = mk_pte(address, PAGE_KERNEL); - if (address >= end_mem) - pte_val(pte) = 0; - set_pte(pg_table, pte); - address += PAGE_SIZE; - } - } - start_mem = fixmap_init(start_mem); #ifdef __SMP__ - start_mem = init_smp_mappings(start_mem); + init_smp_mappings(); #endif - local_flush_tlb(); -#ifndef CONFIG_BIGMEM - return free_area_init(start_mem, end_mem); -#else +#ifdef CONFIG_HIGHMEM kmap_init(); /* run after fixmap_init */ - return free_area_init(start_mem, bigmem_end + PAGE_OFFSET); #endif +#ifdef CONFIG_HIGHMEM + free_area_init(highend_pfn); +#else + free_area_init(max_low_pfn); +#endif + return; } /* @@ -340,23 +432,38 @@ unsigned long __init paging_init(unsigned long start_mem, unsigned long end_mem) void __init test_wp_bit(void) { - unsigned char tmp_reg; - unsigned long old = pg0[0]; +/* + * Ok, all PAE-capable CPUs are definitely handling the WP bit right. + */ +//#ifndef CONFIG_X86_PAE + const unsigned long vaddr = PAGE_OFFSET; + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte, old_pte; + char tmp_reg; printk("Checking if this processor honours the WP bit even in supervisor mode... "); - pg0[0] = pte_val(mk_pte(PAGE_OFFSET, PAGE_READONLY)); + + pgd = swapper_pg_dir + __pgd_offset(vaddr); + pmd = pmd_offset(pgd, vaddr); + pte = pte_offset(pmd, vaddr); + old_pte = *pte; + *pte = mk_pte_phys(0, PAGE_READONLY); local_flush_tlb(); + __asm__ __volatile__( "jmp 1f; 1:\n" "movb %0,%1\n" "movb %1,%0\n" "jmp 1f; 1:\n" - :"=m" (*(char *) __va(0)), + :"=m" (*(char *) vaddr), "=q" (tmp_reg) :/* no inputs */ :"memory"); - pg0[0] = old; + + *pte = old_pte; local_flush_tlb(); + if (boot_cpu_data.wp_works_ok < 0) { boot_cpu_data.wp_works_ok = 0; printk("No.\n"); @@ -365,136 +472,95 @@ void __init test_wp_bit(void) #endif } else printk(".\n"); +//#endif } -static void __init mem_init_region(unsigned long pfn, unsigned long count, unsigned long start_mem_pfn) +static inline int page_is_ram (unsigned long pagenr) { - printk("memory region: %luk @ %08lx000\n", count << 2, pfn); + int i; - do { - if (pfn >= max_mapnr) - break; + for (i = 0; i < e820.nr_map; i++) { + unsigned long addr, size; - /* Avoid the kernel mapping between HIGH_MEMORY and "start_mem".. */ - if (pfn < (HIGH_MEMORY >> PAGE_SHIFT) || pfn >= start_mem_pfn) - clear_bit(PG_reserved, &mem_map[pfn].flags); - - pfn++; - } while (--count > 0); + if (e820.map[i].type != E820_RAM) /* not usable memory */ + continue; + addr = (e820.map[i].addr+PAGE_SIZE-1) >> PAGE_SHIFT; + size = e820.map[i].size >> PAGE_SHIFT; + if ((pagenr >= addr) && (pagenr < addr+size)) + return 1; + } + return 0; } -void __init mem_init(unsigned long start_mem, unsigned long end_mem) +void __init mem_init(void) { - unsigned long start_low_mem = PAGE_SIZE; int codepages = 0; int reservedpages = 0; int datapages = 0; int initpages = 0; - unsigned long tmp; - int i, avail; - - end_mem &= PAGE_MASK; -#ifdef CONFIG_BIGMEM - bigmem_start = PAGE_ALIGN(bigmem_start); - bigmem_end &= PAGE_MASK; -#endif - high_memory = (void *) end_mem; -#ifndef CONFIG_BIGMEM - max_mapnr = num_physpages = MAP_NR(end_mem); +#ifdef CONFIG_HIGHMEM + int tmp; + + if (!mem_map) + BUG(); + highmem_start_page = mem_map + highstart_pfn; + /* cache the highmem_mapnr */ + highmem_mapnr = highstart_pfn; + max_mapnr = num_physpages = highend_pfn; #else - max_mapnr = num_physpages = PHYSMAP_NR(bigmem_end); - /* cache the bigmem_mapnr */ - bigmem_mapnr = PHYSMAP_NR(bigmem_start); + max_mapnr = num_physpages = max_low_pfn; #endif + high_memory = (void *) __va(max_low_pfn * PAGE_SIZE); /* clear the zero-page */ memset(empty_zero_page, 0, PAGE_SIZE); - /* mark usable pages in the mem_map[] */ - start_low_mem = PAGE_ALIGN(start_low_mem)+PAGE_OFFSET; + /* this will put all low memory onto the freelists */ + totalram_pages += free_all_bootmem(); -#ifdef __SMP__ - /* - * But first pinch a few for the stack/trampoline stuff - * FIXME: Don't need the extra page at 4K, but need to fix - * trampoline before removing it. (see the GDT stuff) - * - */ - start_low_mem += PAGE_SIZE; /* 32bit startup code */ - start_low_mem = smp_alloc_memory(start_low_mem); /* AP processor stacks */ -#endif - start_mem = PAGE_ALIGN(start_mem); +#ifdef CONFIG_HIGHMEM + for (tmp = highstart_pfn; tmp < highend_pfn; tmp++) { + struct page *page = mem_map + tmp; - /* walk the whitelist, unreserving good memory - */ - for (avail = i = 0; i < e820.nr_map; i++) { - unsigned long start_pfn, end_pfn; - - if (e820.map[i].type != E820_RAM) /* not usable memory */ - continue; - - start_pfn = (e820.map[i].addr + PAGE_SIZE - 1) >> PAGE_SHIFT; - end_pfn = (e820.map[i].addr + e820.map[i].size) >> PAGE_SHIFT; - - /* We have a certain amount of low memory reserved */ - if (start_pfn < MAP_NR(start_low_mem)) - start_pfn = MAP_NR(start_low_mem); - - if (end_pfn <= start_pfn) - continue; - - mem_init_region(start_pfn, end_pfn - start_pfn, MAP_NR(start_mem)); - } - - for (tmp = PAGE_OFFSET ; tmp < end_mem ; tmp += PAGE_SIZE) { - if (tmp >= MAX_DMA_ADDRESS) - clear_bit(PG_DMA, &mem_map[MAP_NR(tmp)].flags); - if (PageReserved(mem_map+MAP_NR(tmp))) { - if (tmp >= (unsigned long) &_text && tmp < (unsigned long) &_edata) { - if (tmp < (unsigned long) &_etext) - codepages++; - else - datapages++; - } else if (tmp >= (unsigned long) &__init_begin - && tmp < (unsigned long) &__init_end) - initpages++; - else if (tmp >= (unsigned long) &__bss_start - && tmp < (unsigned long) start_mem) - datapages++; - else - reservedpages++; + if (!page_is_ram(tmp)) { + SetPageReserved(page); continue; } - set_page_count(mem_map+MAP_NR(tmp), 1); - totalram += PAGE_SIZE; -#ifdef CONFIG_BLK_DEV_INITRD - if (!initrd_start || (tmp < initrd_start || tmp >= initrd_end)) -#endif - free_page(tmp); + ClearPageReserved(page); + set_bit(PG_highmem, &page->flags); + atomic_set(&page->count, 1); + __free_page(page); + totalhigh_pages++; } -#ifdef CONFIG_BIGMEM - for (tmp = bigmem_start; tmp < bigmem_end; tmp += PAGE_SIZE) { - clear_bit(PG_reserved, &mem_map[PHYSMAP_NR(tmp)].flags); - set_bit(PG_BIGMEM, &mem_map[PHYSMAP_NR(tmp)].flags); - atomic_set(&mem_map[PHYSMAP_NR(tmp)].count, 1); - free_page(tmp + PAGE_OFFSET); - totalbig += PAGE_SIZE; - } - totalram += totalbig; + totalram_pages += totalhigh_pages; #endif - printk("Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data, %dk init, %dk bigmem)\n", + printk("Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data, %dk init, %ldk highmem)\n", (unsigned long) nr_free_pages << (PAGE_SHIFT-10), max_mapnr << (PAGE_SHIFT-10), codepages << (PAGE_SHIFT-10), reservedpages << (PAGE_SHIFT-10), datapages << (PAGE_SHIFT-10), initpages << (PAGE_SHIFT-10), - (int) (totalbig >> 10) + (unsigned long) (totalhigh_pages << (PAGE_SHIFT-10)) ); +#if CONFIG_X86_PAE + if (!cpu_has_pae) + panic("cannot execute a PAE-enabled kernel on a PAE-incapable CPU!"); +#endif if (boot_cpu_data.wp_works_ok < 0) test_wp_bit(); + /* + * Subtle. SMP is doing it's boot stuff late (because it has to + * fork idle threads) - but it also needs low mappings for the + * protected-mode entry to work. We zap these entries only after + * the WP-bit has been tested. + */ +#ifndef CONFIG_SMP + zap_low_mappings(); +#endif + } void free_initmem(void) @@ -503,21 +569,22 @@ void free_initmem(void) addr = (unsigned long)(&__init_begin); for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) { - mem_map[MAP_NR(addr)].flags &= ~(1 << PG_reserved); + ClearPageReserved(mem_map + MAP_NR(addr)); set_page_count(mem_map+MAP_NR(addr), 1); free_page(addr); - totalram += PAGE_SIZE; + totalram_pages++; } printk ("Freeing unused kernel memory: %dk freed\n", (&__init_end - &__init_begin) >> 10); } void si_meminfo(struct sysinfo *val) { - val->totalram = totalram; + val->totalram = totalram_pages; val->sharedram = 0; - val->freeram = nr_free_pages << PAGE_SHIFT; - val->bufferram = atomic_read(&buffermem); - val->totalbig = totalbig; - val->freebig = nr_free_bigpages << PAGE_SHIFT; + val->freeram = nr_free_pages; + val->bufferram = atomic_read(&buffermem_pages); + val->totalhigh = totalhigh_pages; + val->freehigh = nr_free_highpages; + val->mem_unit = PAGE_SIZE; return; } diff --git a/arch/i386/mm/ioremap.c b/arch/i386/mm/ioremap.c index 32f3c33fd..d69455310 100644 --- a/arch/i386/mm/ioremap.c +++ b/arch/i386/mm/ioremap.c @@ -20,15 +20,19 @@ static inline void remap_area_pte(pte_t * pte, unsigned long address, unsigned l end = address + size; if (end > PMD_SIZE) end = PMD_SIZE; + if (address >= end) + BUG(); do { - if (!pte_none(*pte)) + if (!pte_none(*pte)) { printk("remap_area_pte: page already exists\n"); + BUG(); + } set_pte(pte, mk_pte_phys(phys_addr, __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | _PAGE_ACCESSED | flags))); address += PAGE_SIZE; phys_addr += PAGE_SIZE; pte++; - } while (address < end); + } while (address && (address < end)); } static inline int remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size, @@ -41,6 +45,8 @@ static inline int remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned lo if (end > PGDIR_SIZE) end = PGDIR_SIZE; phys_addr -= address; + if (address >= end) + BUG(); do { pte_t * pte = pte_alloc_kernel(pmd, address); if (!pte) @@ -48,7 +54,7 @@ static inline int remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned lo remap_area_pte(pte, address, end - address, address + phys_addr, flags); address = (address + PMD_SIZE) & PMD_MASK; pmd++; - } while (address < end); + } while (address && (address < end)); return 0; } @@ -61,8 +67,11 @@ static int remap_area_pages(unsigned long address, unsigned long phys_addr, phys_addr -= address; dir = pgd_offset(&init_mm, address); flush_cache_all(); - while (address < end) { - pmd_t *pmd = pmd_alloc_kernel(dir, address); + if (address >= end) + BUG(); + do { + pmd_t *pmd; + pmd = pmd_alloc_kernel(dir, address); if (!pmd) return -ENOMEM; if (remap_area_pmd(pmd, address, end - address, @@ -71,7 +80,7 @@ static int remap_area_pages(unsigned long address, unsigned long phys_addr, set_pgdir(address, *dir); address = (address + PGDIR_SIZE) & PGDIR_MASK; dir++; - } + } while (address && (address < end)); flush_tlb_all(); return 0; } diff --git a/arch/i386/vmlinux.lds.S b/arch/i386/vmlinux.lds index 9a9ff07e7..9624cae47 100644 --- a/arch/i386/vmlinux.lds.S +++ b/arch/i386/vmlinux.lds @@ -6,7 +6,7 @@ OUTPUT_ARCH(i386) ENTRY(_start) SECTIONS { - . = PAGE_OFFSET_RAW + 0x100000; + . = 0xC0000000 + 0x100000; _text = .; /* Text and read-only data */ .text : { *(.text) diff --git a/arch/m68k/mm/init.c b/arch/m68k/mm/init.c index c22dccfc5..3442c1cfa 100644 --- a/arch/m68k/mm/init.c +++ b/arch/m68k/mm/init.c @@ -112,6 +112,7 @@ void show_mem(void) printk("%d pages shared\n",shared); printk("%d pages swap cached\n",cached); printk("%ld pages in page table cache\n",pgtable_cache_size); + show_buffers(); #ifdef CONFIG_NET show_net_buffers(); #endif diff --git a/arch/mips/arc/memory.c b/arch/mips/arc/memory.c index 851a16e9d..a3b02dc67 100644 --- a/arch/mips/arc/memory.c +++ b/arch/mips/arc/memory.c @@ -4,13 +4,14 @@ * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) * - * $Id: memory.c,v 1.7 1999/12/04 03:58:59 ralf Exp $ + * $Id: memory.c,v 1.8 2000/01/17 23:32:46 ralf Exp $ */ #include <linux/init.h> #include <linux/kernel.h> #include <linux/types.h> #include <linux/sched.h> #include <linux/mm.h> +#include <linux/bootmem.h> #include <linux/swap.h> #include <linux/config.h> @@ -21,6 +22,8 @@ #undef DEBUG +extern char _end; + struct linux_mdesc * __init prom_getmdesc(struct linux_mdesc *curr) { return romvec->get_mdesc(curr); @@ -53,69 +56,87 @@ static char *arc_mtypes[8] = { static struct prom_pmemblock prom_pblocks[PROM_MAX_PMEMBLOCKS]; -struct prom_pmemblock * __init prom_getpblock_array(void) -{ - return &prom_pblocks[0]; -} - #define MEMTYPE_DONTUSE 0 #define MEMTYPE_PROM 1 #define MEMTYPE_FREE 2 -static int __init prom_memtype_classify (union linux_memtypes type) +static inline int memtype_classify_arcs (union linux_memtypes type) { - if (prom_flags & PROM_FLAG_ARCS) { switch (type.arcs) { - case arcs_free: - case arcs_fcontig: - return MEMTYPE_FREE; - case arcs_atmp: - case arcs_aperm: - return MEMTYPE_PROM; - default: - return MEMTYPE_DONTUSE; + case arcs_fcontig: + case arcs_free: + return MEMTYPE_FREE; + case arcs_atmp: + return MEMTYPE_PROM; + case arcs_eblock: + case arcs_rvpage: + case arcs_bmem: + case arcs_prog: + case arcs_aperm: + return MEMTYPE_DONTUSE; + default: + BUG(); } - } else { + while(1); /* Nuke warning. */ +} + +static inline int memtype_classify_arc (union linux_memtypes type) +{ switch (type.arc) { - case arc_free: - case arc_fcontig: - return MEMTYPE_FREE; - case arc_rvpage: - case arc_atmp: - case arc_aperm: - return MEMTYPE_PROM; - default: - return MEMTYPE_DONTUSE; + case arc_free: + case arc_fcontig: + return MEMTYPE_FREE; + case arc_atmp: + return MEMTYPE_PROM; + case arc_eblock: + case arc_rvpage: + case arc_bmem: + case arc_prog: + case arc_aperm: + return MEMTYPE_DONTUSE; + default: + BUG(); } - } + while(1); /* Nuke warning. */ +} + +static int __init prom_memtype_classify (union linux_memtypes type) +{ + if (prom_flags & PROM_FLAG_ARCS) /* SGI is ``different'' ... */ + return memtype_classify_arc(type); + + return memtype_classify_arc(type); } -static void __init prom_setup_memupper(void) +static unsigned long __init find_max_low_pfn(void) { struct prom_pmemblock *p, *highest; - for(p = prom_getpblock_array(), highest = 0; p->size != 0; p++) { - if(p->base == 0xdeadbeef) - prom_printf("WHEEE, bogus pmemblock\n"); - if(!highest || p->base > highest->base) + for (p = prom_pblocks, highest = 0; p->size != 0; p++) { + if (!highest || p->base > highest->base) highest = p; } - mips_memory_upper = highest->base + highest->size; #ifdef DEBUG - prom_printf("prom_setup_memupper: mips_memory_upper = %08lx\n", - mips_memory_upper); + prom_printf("find_max_low_pfn: mips_memory_upper = %08lx\n", highest); #endif + return (highest->base + highest->size) >> PAGE_SHIFT; } +#define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) +#define PFN_DOWN(x) ((x) >> PAGE_SHIFT) +#define PFN_PHYS(x) ((x) << PAGE_SHIFT) +#define PFN_ALIGN(x) (((unsigned long)(x) + (PAGE_SIZE - 1)) & PAGE_MASK) + void __init prom_meminit(void) { + unsigned long start_pfn; struct linux_mdesc *p; int totram; int i = 0; - p = prom_getmdesc(PROM_NULL_MDESC); #ifdef DEBUG prom_printf("ARCS MEMORY DESCRIPTOR dump:\n"); + p = prom_getmdesc(PROM_NULL_MDESC); while(p) { prom_printf("[%d,%p]: base<%08lx> pages<%08lx> type<%s>\n", i, p, p->base, p->pages, mtypes(p->type)); @@ -123,87 +144,72 @@ void __init prom_meminit(void) i++; } #endif - p = prom_getmdesc(PROM_NULL_MDESC); + totram = 0; + p = prom_getmdesc(PROM_NULL_MDESC); i = 0; - while(p) { - prom_pblocks[i].type = prom_memtype_classify (p->type); - prom_pblocks[i].base = ((p->base<<PAGE_SHIFT) + 0x80000000); - prom_pblocks[i].size = p->pages << PAGE_SHIFT; - switch (prom_pblocks[i].type) { - case MEMTYPE_FREE: - totram += prom_pblocks[i].size; + while (p) { + prom_pblocks[i].type = prom_memtype_classify(p->type); + prom_pblocks[i].base = p->base << PAGE_SHIFT; + prom_pblocks[i].size = p->pages << PAGE_SHIFT; + + switch (prom_pblocks[i].type) { + case MEMTYPE_FREE: + totram += prom_pblocks[i].size; #ifdef DEBUG - prom_printf("free_chunk[%d]: base=%08lx size=%d\n", - i, prom_pblocks[i].base, - prom_pblocks[i].size); + prom_printf("free_chunk[%d]: base=%08lx size=%d\n", + i, prom_pblocks[i].base, + prom_pblocks[i].size); #endif - i++; - break; - case MEMTYPE_PROM: + i++; + break; + case MEMTYPE_PROM: #ifdef DEBUG - prom_printf("prom_chunk[%d]: base=%08lx size=%d\n", - i, prom_pblocks[i].base, - prom_pblocks[i].size); + prom_printf("prom_chunk[%d]: base=%08lx size=%d\n", + i, prom_pblocks[i].base, + prom_pblocks[i].size); #endif - i++; - break; - default: - break; - } - p = prom_getmdesc(p); + i++; + break; + default: + break; + } + p = prom_getmdesc(p); } - prom_pblocks[i].base = 0xdeadbeef; - prom_pblocks[i].size = 0; /* indicates last elem. of array */ - printk("PROMLIB: Total free ram %d bytes (%dK,%dMB)\n", - totram, (totram/1024), (totram/1024/1024)); + prom_pblocks[i].size = 0; /* Setup upper physical memory bound. */ - prom_setup_memupper(); -} + max_low_pfn = find_max_low_pfn(); -/* Called from mem_init() to fixup the mem_map page settings. */ -void __init prom_fixup_mem_map(unsigned long start, unsigned long end) -{ - struct prom_pmemblock *p; - int i, nents; - - /* Determine number of pblockarray entries. */ - p = prom_getpblock_array(); - for(i = 0; p[i].size; i++) - ; - nents = i; -restart: - while(start < end) { - for(i = 0; i < nents; i++) { - if((p[i].type == MEMTYPE_FREE) && - (start >= (p[i].base)) && - (start < (p[i].base + p[i].size))) { - start = p[i].base + p[i].size; - start &= PAGE_MASK; - goto restart; - } - } - set_bit(PG_reserved, &mem_map[MAP_NR(start)].flags); - start += PAGE_SIZE; - } + start_pfn = PFN_UP((unsigned long)&_end - PAGE_OFFSET); + init_bootmem(start_pfn, max_low_pfn); + + for (i = 0; prom_pblocks[i].size; i++) + if (prom_pblocks[i].type == MEMTYPE_FREE) + free_bootmem(prom_pblocks[i].base, prom_pblocks[i].size); + + printk("PROMLIB: Total free ram %d bytes (%dK,%dMB)\n", + totram, (totram/1024), (totram/1024/1024)); } void __init prom_free_prom_memory (void) { - struct prom_pmemblock *p; - unsigned long addr; - unsigned long num_pages = 0; - - for(p = prom_getpblock_array(); p->size != 0; p++) { - if (p->type == MEMTYPE_PROM) { - for (addr = p->base; addr < p->base + p->size; addr += PAGE_SIZE) { - mem_map[MAP_NR(addr)].flags &= ~(1 << PG_reserved); - atomic_set(&mem_map[MAP_NR(addr)].count, 1); - free_page(addr); - num_pages++; - } + struct prom_pmemblock *p; + unsigned long freed = 0; + unsigned long addr; + + for (p = prom_pblocks; p->size != 0; p++) { + if (p->type != MEMTYPE_PROM) + continue; + + addr = PAGE_OFFSET + p->base; + while (addr < p->base + p->size) { + ClearPageReserved(mem_map + MAP_NR(addr)); + set_page_count(mem_map + MAP_NR(addr), 1); + free_page(addr); + addr += PAGE_SIZE; + freed += PAGE_SIZE; + } } - } - printk ("Freeing prom memory: %dk freed\n",num_pages << (PAGE_SHIFT - 10)); + printk("Freeing prom memory: %ldk freed\n", freed >> 10); } diff --git a/arch/mips/jazz/jazzdma.c b/arch/mips/jazz/jazzdma.c index bb8007bf1..0990db6a0 100644 --- a/arch/mips/jazz/jazzdma.c +++ b/arch/mips/jazz/jazzdma.c @@ -10,8 +10,10 @@ * and return the more usual NULL pointer as logical address. */ #include <linux/kernel.h> +#include <linux/init.h> #include <linux/errno.h> #include <linux/mm.h> +#include <linux/bootmem.h> #include <asm/mipsregs.h> #include <asm/jazz.h> #include <asm/io.h> @@ -26,7 +28,6 @@ #define CONF_DEBUG_VDMA 0 static unsigned long vdma_pagetable_start = 0; -static unsigned long vdma_pagetable_end = 0; /* * Debug stuff @@ -58,30 +59,31 @@ static inline void vdma_pgtbl_init(void) /* * Initialize the Jazz R4030 dma controller */ -unsigned long vdma_init(unsigned long memory_start, unsigned long memory_end) +void __init vdma_init(void) { - /* - * Allocate 32k of memory for DMA page tables. - * This needs to be page aligned and should be - * uncached to avoid cache flushing after every - * update. - */ - vdma_pagetable_start = KSEG1ADDR((memory_start + 4095) & ~4095); - vdma_pagetable_end = vdma_pagetable_start + VDMA_PGTBL_SIZE; - flush_cache_all(); - - /* - * Clear the R4030 translation table - */ - vdma_pgtbl_init(); - - r4030_write_reg32(JAZZ_R4030_TRSTBL_BASE,PHYSADDR(vdma_pagetable_start)); - r4030_write_reg32(JAZZ_R4030_TRSTBL_LIM,VDMA_PGTBL_SIZE); - r4030_write_reg32(JAZZ_R4030_TRSTBL_INV,0); - - printk("VDMA: R4030 DMA pagetables initialized.\n"); - - return KSEG0ADDR(vdma_pagetable_end); + /* + * Allocate 32k of memory for DMA page tables. This needs to be page + * aligned and should be uncached to avoid cache flushing after every + * update. + */ + vdma_pagetable_start = alloc_bootmem_low_pages(VDMA_PGTBL_SIZE); + if (!vdma_pagetable_start) + BUG(); + dma_cache_wback_inv(vdma_pagetable_start, VDMA_PGTBL_SIZE); + vdma_pagetable_start = KSEG1ADDR(vdma_pagetable_start); + + /* + * Clear the R4030 translation table + */ + vdma_pgtbl_init(); + + r4030_write_reg32(JAZZ_R4030_TRSTBL_BASE,PHYSADDR(vdma_pagetable_start)); + r4030_write_reg32(JAZZ_R4030_TRSTBL_LIM, VDMA_PGTBL_SIZE); + r4030_write_reg32(JAZZ_R4030_TRSTBL_INV, 0); + + printk("VDMA: R4030 DMA pagetables initialized.\n"); + + return KSEG0ADDR(vdma_pagetable_end); } /* diff --git a/arch/mips/jazz/setup.c b/arch/mips/jazz/setup.c index 4b405f405..a16ce7b69 100644 --- a/arch/mips/jazz/setup.c +++ b/arch/mips/jazz/setup.c @@ -1,4 +1,4 @@ -/* $Id: setup.c,v 1.23 1999/10/07 07:31:14 raiko Exp $ +/* $Id: setup.c,v 1.24 1999/10/09 00:00:58 ralf Exp $ * * Setup pointers to hardware-dependent routines. * @@ -23,6 +23,7 @@ #include <asm/keyboard.h> #include <asm/irq.h> #include <asm/jazz.h> +#include <asm/jazzdma.h> #include <asm/ptrace.h> #include <asm/reboot.h> #include <asm/io.h> @@ -79,6 +80,11 @@ static void __init jazz_irq_setup(void) i8259_setup_irq(2, &irq2); } +int __init page_is_ram(unsigned long pagenr) +{ + return 1; +} + void __init jazz_setup(void) { add_wired_entry (0x02000017, 0x03c00017, 0xe0000000, PM_64K); @@ -125,4 +131,6 @@ void __init jazz_setup(void) rtc_ops = &jazz_rtc_ops; kbd_ops = &jazz_kbd_ops; + + vdma_init(); } diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c index c1c556e1c..43cf5ad74 100644 --- a/arch/mips/kernel/setup.c +++ b/arch/mips/kernel/setup.c @@ -1,4 +1,4 @@ -/* $Id: setup.c,v 1.20 1999/10/09 00:00:58 ralf Exp $ +/* $Id: setup.c,v 1.21 2000/01/26 00:07:44 ralf Exp $ * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive @@ -97,13 +97,6 @@ unsigned long mips_machgroup = MACH_GROUP_UNKNOWN; unsigned char aux_device_present; extern int _end; -extern char empty_zero_page[PAGE_SIZE]; - -/* - * This is set up by the setup-routine at boot-time - */ -#define PARAM empty_zero_page - static char command_line[CL_SIZE] = { 0, }; char saved_command_line[CL_SIZE]; extern char arcs_cmdline[CL_SIZE]; @@ -131,10 +124,8 @@ static void __init default_irq_setup(void) panic("Unknown machtype in init_IRQ"); } -void __init setup_arch(char **cmdline_p, - unsigned long * memory_start_p, unsigned long * memory_end_p) +void __init setup_arch(char **cmdline_p) { - unsigned long memory_end; #ifdef CONFIG_BLK_DEV_INITRD unsigned long tmp; unsigned long *initrd_header; @@ -204,25 +195,14 @@ void __init setup_arch(char **cmdline_p, panic("Unsupported architecture"); } - memory_end = mips_memory_upper; - /* - * Due to prefetching and similar mechanism the CPU sometimes - * generates addresses beyond the end of memory. We leave the size - * of one cache line at the end of memory unused to make shure we - * don't catch this type of bus errors. - */ - memory_end -= 128; - memory_end &= PAGE_MASK; - strncpy (command_line, arcs_cmdline, CL_SIZE); memcpy(saved_command_line, command_line, CL_SIZE); saved_command_line[CL_SIZE-1] = '\0'; *cmdline_p = command_line; - *memory_start_p = (unsigned long) &_end; - *memory_end_p = memory_end; #ifdef CONFIG_BLK_DEV_INITRD +#error "Fixme, I'm broken." tmp = (((unsigned long)&_end + PAGE_SIZE-1) & PAGE_MASK) - 8; if (tmp < (unsigned long)&_end) tmp += PAGE_SIZE; diff --git a/arch/mips/ld.script.big b/arch/mips/ld.script.big index 57cc8dba6..68ac48528 100644 --- a/arch/mips/ld.script.big +++ b/arch/mips/ld.script.big @@ -5,26 +5,6 @@ SECTIONS { /* Read-only sections, merged into text segment: */ . = 0x80000000; - .rel.text : { *(.rel.text) } - .rela.text : { *(.rela.text) } - .rel.data : { *(.rel.data) } - .rela.data : { *(.rela.data) } - .rel.rodata : { *(.rel.rodata) } - .rela.rodata : { *(.rela.rodata) } - .rel.got : { *(.rel.got) } - .rela.got : { *(.rela.got) } - .rel.ctors : { *(.rel.ctors) } - .rela.ctors : { *(.rela.ctors) } - .rel.dtors : { *(.rel.dtors) } - .rela.dtors : { *(.rela.dtors) } - .rel.init : { *(.rel.init) } - .rela.init : { *(.rela.init) } - .rel.fini : { *(.rel.fini) } - .rela.fini : { *(.rela.fini) } - .rel.bss : { *(.rel.bss) } - .rela.bss : { *(.rela.bss) } - .rel.plt : { *(.rel.plt) } - .rela.plt : { *(.rela.plt) } .init : { *(.init) } =0 .text : { diff --git a/arch/mips/ld.script.little b/arch/mips/ld.script.little index 1a396ce08..5ee17215d 100644 --- a/arch/mips/ld.script.little +++ b/arch/mips/ld.script.little @@ -5,26 +5,6 @@ SECTIONS { /* Read-only sections, merged into text segment: */ . = 0x80000000; - .rel.text : { *(.rel.text) } - .rela.text : { *(.rela.text) } - .rel.data : { *(.rel.data) } - .rela.data : { *(.rela.data) } - .rel.rodata : { *(.rel.rodata) } - .rela.rodata : { *(.rela.rodata) } - .rel.got : { *(.rel.got) } - .rela.got : { *(.rela.got) } - .rel.ctors : { *(.rel.ctors) } - .rela.ctors : { *(.rela.ctors) } - .rel.dtors : { *(.rel.dtors) } - .rela.dtors : { *(.rela.dtors) } - .rel.init : { *(.rel.init) } - .rela.init : { *(.rela.init) } - .rel.fini : { *(.rel.fini) } - .rela.fini : { *(.rela.fini) } - .rel.bss : { *(.rel.bss) } - .rela.bss : { *(.rela.bss) } - .rel.plt : { *(.rel.plt) } - .rela.plt : { *(.rela.plt) } .init : { *(.init) } =0 .text : { diff --git a/arch/mips/lib/memcpy.S b/arch/mips/lib/memcpy.S index 907a471a6..4850b09ce 100644 --- a/arch/mips/lib/memcpy.S +++ b/arch/mips/lib/memcpy.S @@ -3,7 +3,7 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. * - * $Id: memcpy.S,v 1.2 1998/05/04 09:12:53 ralf Exp $ + * $Id: memcpy.S,v 1.3 1998/07/10 01:14:49 ralf Exp $ * * Unified implementation of memcpy, memmove and the __copy_user backend. * For __rmemcpy and memmove an exception is always a kernel bug, therefore @@ -688,8 +688,8 @@ ru_end_bytes: jr ra move a2, zero - END(__rmemcpy) #endif /* Horror fix */ + END(__rmemcpy) l_fixup: # clear the rest of the buffer lw t0, THREAD_BUADDR($28) diff --git a/arch/mips/mm/andes.c b/arch/mips/mm/andes.c index 05a722cf3..3230106b8 100644 --- a/arch/mips/mm/andes.c +++ b/arch/mips/mm/andes.c @@ -1,4 +1,4 @@ -/* $Id: andes.c,v 1.7 1999/08/09 19:43:16 harald Exp $ +/* $Id: andes.c,v 1.8 1999/10/09 00:00:58 ralf Exp $ * * andes.c: MMU and cache operations for the R10000 (ANDES). * @@ -14,6 +14,83 @@ #include <asm/sgialib.h> #include <asm/mmu_context.h> +/* page functions */ +void andes_clear_page(void * page) +{ + __asm__ __volatile__( + ".set\tnoreorder\n\t" + ".set\tnoat\n\t" + "addiu\t$1,%0,%2\n" + "1:\tsw\t$0,(%0)\n\t" + "sw\t$0,4(%0)\n\t" + "sw\t$0,8(%0)\n\t" + "sw\t$0,12(%0)\n\t" + "addiu\t%0,32\n\t" + "sw\t$0,-16(%0)\n\t" + "sw\t$0,-12(%0)\n\t" + "sw\t$0,-8(%0)\n\t" + "bne\t$1,%0,1b\n\t" + "sw\t$0,-4(%0)\n\t" + ".set\tat\n\t" + ".set\treorder" + :"=r" (page) + :"0" (page), + "I" (PAGE_SIZE) + :"$1","memory"); +} + +static void andes_copy_page(void * to, void * from) +{ + unsigned long dummy1, dummy2; + unsigned long reg1, reg2, reg3, reg4; + + __asm__ __volatile__( + ".set\tnoreorder\n\t" + ".set\tnoat\n\t" + "addiu\t$1,%0,%8\n" + "1:\tlw\t%2,(%1)\n\t" + "lw\t%3,4(%1)\n\t" + "lw\t%4,8(%1)\n\t" + "lw\t%5,12(%1)\n\t" + "sw\t%2,(%0)\n\t" + "sw\t%3,4(%0)\n\t" + "sw\t%4,8(%0)\n\t" + "sw\t%5,12(%0)\n\t" + "lw\t%2,16(%1)\n\t" + "lw\t%3,20(%1)\n\t" + "lw\t%4,24(%1)\n\t" + "lw\t%5,28(%1)\n\t" + "sw\t%2,16(%0)\n\t" + "sw\t%3,20(%0)\n\t" + "sw\t%4,24(%0)\n\t" + "sw\t%5,28(%0)\n\t" + "addiu\t%0,64\n\t" + "addiu\t%1,64\n\t" + "lw\t%2,-32(%1)\n\t" + "lw\t%3,-28(%1)\n\t" + "lw\t%4,-24(%1)\n\t" + "lw\t%5,-20(%1)\n\t" + "sw\t%2,-32(%0)\n\t" + "sw\t%3,-28(%0)\n\t" + "sw\t%4,-24(%0)\n\t" + "sw\t%5,-20(%0)\n\t" + "lw\t%2,-16(%1)\n\t" + "lw\t%3,-12(%1)\n\t" + "lw\t%4,-8(%1)\n\t" + "lw\t%5,-4(%1)\n\t" + "sw\t%2,-16(%0)\n\t" + "sw\t%3,-12(%0)\n\t" + "sw\t%4,-8(%0)\n\t" + "bne\t$1,%0,1b\n\t" + "sw\t%5,-4(%0)\n\t" + ".set\tat\n\t" + ".set\treorder" + :"=r" (dummy1), "=r" (dummy2), + "=&r" (reg1), "=&r" (reg2), "=&r" (reg3), "=&r" (reg4) + :"0" (to), "1" (from), + "I" (PAGE_SIZE)); +} + /* Cache operations. XXX Write these dave... */ static inline void andes_flush_cache_all(void) { @@ -38,7 +115,7 @@ static void andes_flush_cache_page(struct vm_area_struct *vma, /* XXX */ } -static void andes_flush_page_to_ram(unsigned long page) +static void andes_flush_page_to_ram(struct page * page) { /* XXX */ } @@ -86,6 +163,9 @@ void add_wired_entry(unsigned long entrylo0, unsigned long entrylo1, void __init ld_mmu_andes(void) { + clear_page = andes_clear_page; + copy_page = andes_copy_page; + flush_cache_all = andes_flush_cache_all; flush_cache_mm = andes_flush_cache_mm; flush_cache_range = andes_flush_cache_range; diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c index bf77a7998..cf74a6dd5 100644 --- a/arch/mips/mm/init.c +++ b/arch/mips/mm/init.c @@ -1,10 +1,11 @@ -/* $Id: init.c,v 1.19 1999/10/09 00:00:58 ralf Exp $ +/* $Id: init.c,v 1.20 2000/01/26 00:07:44 ralf Exp $ * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 1994 - 1998 by Ralf Baechle + * Copyright (C) 1994 - 2000 by Ralf Baechle + * Copyright (C) 2000 Silicon Graphics, Inc. */ #include <linux/config.h> #include <linux/init.h> @@ -18,6 +19,7 @@ #include <linux/ptrace.h> #include <linux/mman.h> #include <linux/mm.h> +#include <linux/bootmem.h> #include <linux/swap.h> #include <linux/swapctl.h> #ifdef CONFIG_BLK_DEV_INITRD @@ -35,7 +37,7 @@ #endif #include <asm/mmu_context.h> -static unsigned long totalram = 0; +static unsigned long totalram_pages = 0; extern void show_net_buffers(void); extern void prom_fixup_mem_map(unsigned long start, unsigned long end); @@ -45,13 +47,13 @@ extern void prom_free_prom_memory(void); void __bad_pte_kernel(pmd_t *pmd) { printk("Bad pmd in pte_alloc_kernel: %08lx\n", pmd_val(*pmd)); - pmd_val(*pmd) = BAD_PAGETABLE; + pmd_set(pmd, BAD_PAGETABLE); } void __bad_pte(pmd_t *pmd) { printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd)); - pmd_val(*pmd) = BAD_PAGETABLE; + pmd_set(pmd, BAD_PAGETABLE); } pte_t *get_pte_kernel_slow(pmd_t *pmd, unsigned long offset) @@ -61,11 +63,11 @@ pte_t *get_pte_kernel_slow(pmd_t *pmd, unsigned long offset) page = (pte_t *) __get_free_page(GFP_USER); if (pmd_none(*pmd)) { if (page) { - clear_page((unsigned long)page); + clear_page(page); pmd_val(*pmd) = (unsigned long)page; return page + offset; } - pmd_val(*pmd) = BAD_PAGETABLE; + pmd_set(pmd, BAD_PAGETABLE); return NULL; } free_page((unsigned long)page); @@ -83,11 +85,11 @@ pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset) page = (pte_t *) __get_free_page(GFP_KERNEL); if (pmd_none(*pmd)) { if (page) { - clear_page((unsigned long)page); + clear_page(page); pmd_val(*pmd) = (unsigned long)page; return page + offset; } - pmd_val(*pmd) = BAD_PAGETABLE; + pmd_set(pmd, BAD_PAGETABLE); return NULL; } free_page((unsigned long)page); @@ -135,7 +137,7 @@ static inline unsigned long setup_zero_pages(void) panic("Oh boy, that early out of memory?"); pg = MAP_NR(empty_zero_page); - while(pg < MAP_NR(empty_zero_page) + (1 << order)) { + while (pg < MAP_NR(empty_zero_page) + (1 << order)) { set_bit(PG_reserved, &mem_map[pg].flags); set_page_count(mem_map + pg, 0); pg++; @@ -145,7 +147,7 @@ static inline unsigned long setup_zero_pages(void) zero_page_mask = (size - 1) & PAGE_MASK; memset((void *)empty_zero_page, 0, size); - return size; + return 1UL << order; } int do_check_pgt_cache(int low, int high) @@ -201,10 +203,10 @@ pte_t * __bad_pagetable(void) pte_t __bad_page(void) { extern char empty_bad_page[PAGE_SIZE]; - unsigned long page = (unsigned long)empty_bad_page; + unsigned long page = (unsigned long) empty_bad_page; - clear_page(page); - return pte_mkdirty(mk_pte(page, PAGE_SHARED)); + clear_page((void *)page); + return pte_mkdirty(mk_pte_phys(__pa(page), PAGE_SHARED)); } void show_mem(void) @@ -233,85 +235,61 @@ void show_mem(void) printk("%d pages swap cached\n",cached); printk("%ld pages in page table cache\n",pgtable_cache_size); printk("%d free pages\n", free); + show_buffers(); #ifdef CONFIG_NET show_net_buffers(); #endif } -extern unsigned long free_area_init(unsigned long, unsigned long); +/* References to section boundaries */ -unsigned long __init paging_init(unsigned long start_mem, unsigned long end_mem) +extern char _ftext, _etext, _fdata, _edata; +extern char __init_begin, __init_end; + +void __init paging_init(void) { /* Initialize the entire pgd. */ pgd_init((unsigned long)swapper_pg_dir); pgd_init((unsigned long)swapper_pg_dir + PAGE_SIZE / 2); - return free_area_init(start_mem, end_mem); + return free_area_init(max_low_pfn); } -void __init mem_init(unsigned long start_mem, unsigned long end_mem) +extern int page_is_ram(unsigned long pagenr); + +void __init mem_init(void) { - int codepages = 0; - int datapages = 0; + unsigned long codesize, reservedpages, datasize, initsize; unsigned long tmp; - extern int _etext, _ftext; - -#ifdef CONFIG_MIPS_JAZZ - if (mips_machgroup == MACH_GROUP_JAZZ) - start_mem = vdma_init(start_mem, end_mem); -#endif - - end_mem &= PAGE_MASK; - max_mapnr = MAP_NR(end_mem); - high_memory = (void *)end_mem; - num_physpages = 0; - - /* mark usable pages in the mem_map[] */ - start_mem = PAGE_ALIGN(start_mem); - for(tmp = MAP_NR(start_mem);tmp < max_mapnr;tmp++) - clear_bit(PG_reserved, &mem_map[tmp].flags); + max_mapnr = num_physpages = max_low_pfn; + high_memory = (void *) __va(max_mapnr << PAGE_SHIFT); - prom_fixup_mem_map(start_mem, (unsigned long)high_memory); + totalram_pages += free_all_bootmem(); + totalram_pages -= setup_zero_pages(); /* Setup zeroed pages. */ - for (tmp = PAGE_OFFSET; tmp < end_mem; tmp += PAGE_SIZE) { + reservedpages = 0; + for (tmp = 0; tmp < max_low_pfn; tmp++) /* - * This is only for PC-style DMA. The onboard DMA - * of Jazz and Tyne machines is completely different and - * not handled via a flag in mem_map_t. + * Only count resrved RAM pages */ - if (tmp >= MAX_DMA_ADDRESS) - clear_bit(PG_DMA, &mem_map[MAP_NR(tmp)].flags); - if (PageReserved(mem_map+MAP_NR(tmp))) { - if ((tmp < (unsigned long) &_etext) && - (tmp >= (unsigned long) &_ftext)) - codepages++; - else if ((tmp < start_mem) && - (tmp > (unsigned long) &_etext)) - datapages++; - continue; - } - num_physpages++; - set_page_count(mem_map + MAP_NR(tmp), 1); - totalram += PAGE_SIZE; -#ifdef CONFIG_BLK_DEV_INITRD - if (!initrd_start || (tmp < initrd_start || tmp >= - initrd_end)) -#endif - free_page(tmp); - } - tmp = nr_free_pages << PAGE_SHIFT; - - /* Setup zeroed pages. */ - tmp -= setup_zero_pages(); - - printk("Memory: %luk/%luk available (%dk kernel code, %dk data)\n", - tmp >> 10, - max_mapnr << (PAGE_SHIFT-10), - codepages << (PAGE_SHIFT-10), - datapages << (PAGE_SHIFT-10)); + if (page_is_ram(tmp) && PageReserved(mem_map+tmp)) + reservedpages++; + + codesize = (unsigned long) &_etext - (unsigned long) &_ftext; + datasize = (unsigned long) &_edata - (unsigned long) &_fdata; + initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin; + printk("Memory: %luk/%luk available (%ldk kernel code, %ldk reserved, " + "%ldk data, %ldk init)\n", + (unsigned long) nr_free_pages << (PAGE_SHIFT-10), + max_mapnr << (PAGE_SHIFT-10), + codesize >> 10, + reservedpages << (PAGE_SHIFT-10), + datasize >> 10, + initsize >> 10); } extern char __init_begin, __init_end; +extern void prom_free_prom_memory(void); void free_initmem(void) { @@ -319,12 +297,13 @@ void free_initmem(void) prom_free_prom_memory (); - addr = (unsigned long)(&__init_begin); - for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) { - mem_map[MAP_NR(addr)].flags &= ~(1 << PG_reserved); + addr = (unsigned long) &__init_begin; + while (addr < (unsigned long) &__init_end) { + ClearPageReserved(mem_map + MAP_NR(addr)); set_page_count(mem_map + MAP_NR(addr), 1); free_page(addr); - totalram += PAGE_SIZE; + totalram_pages++; + addr += PAGE_SIZE; } printk("Freeing unused kernel memory: %dk freed\n", (&__init_end - &__init_begin) >> 10); @@ -332,10 +311,13 @@ void free_initmem(void) void si_meminfo(struct sysinfo *val) { - val->totalram = totalram; + val->totalram = totalram_pages; val->sharedram = 0; - val->freeram = nr_free_pages << PAGE_SHIFT; - val->bufferram = atomic_read(&buffermem); + val->freeram = nr_free_pages; + val->bufferram = atomic_read(&buffermem_pages); + val->totalhigh = 0; + val->freehigh = 0; + val->mem_unit = PAGE_SIZE; return; } diff --git a/arch/mips/mm/loadmmu.c b/arch/mips/mm/loadmmu.c index ef7720527..cf4816889 100644 --- a/arch/mips/mm/loadmmu.c +++ b/arch/mips/mm/loadmmu.c @@ -3,7 +3,7 @@ * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) * - * $Id: loadmmu.c,v 1.12 1999/09/18 20:48:03 harald Exp $ + * $Id: loadmmu.c,v 1.13 1999/10/09 00:00:58 ralf Exp $ */ #include <linux/init.h> #include <linux/kernel.h> @@ -17,8 +17,8 @@ #include <asm/sgialib.h> /* memory functions */ -void (*clear_page)(unsigned long page); -void (*copy_page)(unsigned long to, unsigned long from); +void (*clear_page)(void * page); +void (*copy_page)(void * to, void * from); /* Cache operations. */ void (*flush_cache_all)(void); @@ -27,7 +27,7 @@ void (*flush_cache_range)(struct mm_struct *mm, unsigned long start, unsigned long end); void (*flush_cache_page)(struct vm_area_struct *vma, unsigned long page); void (*flush_cache_sigtramp)(unsigned long addr); -void (*flush_page_to_ram)(unsigned long page); +void (*flush_page_to_ram)(struct page * page); /* DMA cache operations. */ void (*dma_cache_wback_inv)(unsigned long start, unsigned long size); diff --git a/arch/mips/mm/r2300.c b/arch/mips/mm/r2300.c index 03e1a16d1..b0e72cd79 100644 --- a/arch/mips/mm/r2300.c +++ b/arch/mips/mm/r2300.c @@ -7,7 +7,7 @@ * Copyright (C) 1998 Harald Koerfgen * Copyright (C) 1998 Gleb Raiko & Vladimir Roganov * - * $Id: r2300.c,v 1.11 1999/10/09 00:00:58 ralf Exp $ + * $Id: r2300.c,v 1.12 1999/10/12 17:33:49 harald Exp $ */ #include <linux/init.h> #include <linux/kernel.h> @@ -45,7 +45,7 @@ static struct cache_space { #define NTLB_ENTRIES 64 /* Fixed on all R23000 variants... */ /* page functions */ -void r2300_clear_page(unsigned long page) +void r2300_clear_page(void * page) { __asm__ __volatile__( ".set\tnoreorder\n\t" @@ -69,7 +69,7 @@ void r2300_clear_page(unsigned long page) :"$1","memory"); } -static void r2300_copy_page(unsigned long to, unsigned long from) +static void r2300_copy_page(void * to, void * from) { unsigned long dummy1, dummy2; unsigned long reg1, reg2, reg3, reg4; @@ -200,8 +200,8 @@ static inline unsigned long get_phys_page (unsigned long page, pte = *page_table; if (!pte_present(pte)) return 0; - return pte_page(pte); - } + return pte_val(pte) & PAGE_MASK; + } } } @@ -348,12 +348,12 @@ static void r2300_flush_cache_page(struct vm_area_struct *vma, } } -static void r2300_flush_page_to_ram(unsigned long page) +static void r2300_flush_page_to_ram(struct page * page) { /* * We need to flush both i- & d- caches :-( */ - unsigned long phys_page = get_phys_page(page, NULL); + unsigned long phys_page = get_phys_page(page_address(page), NULL); #ifdef DEBUG_CACHE printk("cram[%08lx]", page); #endif diff --git a/arch/mips/mm/r4xx0.c b/arch/mips/mm/r4xx0.c index 6005bca95..b5c76c801 100644 --- a/arch/mips/mm/r4xx0.c +++ b/arch/mips/mm/r4xx0.c @@ -1,4 +1,4 @@ -/* $Id: r4xx0.c,v 1.25 1999/10/09 00:00:58 ralf Exp $ +/* $Id: r4xx0.c,v 1.26 1999/10/21 00:23:04 ralf Exp $ * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive @@ -78,7 +78,7 @@ struct bcache_ops *bcops = &no_sc_ops; * versions of R4000 and R4400. */ -static void r4k_clear_page_d16(unsigned long page) +static void r4k_clear_page_d16(void * page) { __asm__ __volatile__( ".set\tnoreorder\n\t" @@ -109,7 +109,7 @@ static void r4k_clear_page_d16(unsigned long page) :"$1","memory"); } -static void r4k_clear_page_d32(unsigned long page) +static void r4k_clear_page_d32(void * page) { __asm__ __volatile__( ".set\tnoreorder\n\t" @@ -166,7 +166,7 @@ static void r4k_clear_page_d32(unsigned long page) * nop * cache Hit_Writeback_Invalidate_D */ -static void r4k_clear_page_r4600_v1(unsigned long page) +static void r4k_clear_page_r4600_v1(void * page) { __asm__ __volatile__( ".set\tnoreorder\n\t" @@ -205,7 +205,7 @@ static void r4k_clear_page_r4600_v1(unsigned long page) /* * And this one is for the R4600 V2.0 */ -static void r4k_clear_page_r4600_v2(unsigned long page) +static void r4k_clear_page_r4600_v2(void * page) { unsigned int flags; @@ -248,7 +248,7 @@ static void r4k_clear_page_r4600_v2(unsigned long page) * this the kernel crashed shortly after mounting the root filesystem. CPU * bug? Weirdo cache instruction semantics? */ -static void r4k_clear_page_s16(unsigned long page) +static void r4k_clear_page_s16(void * page) { __asm__ __volatile__( ".set\tnoreorder\n\t" @@ -279,7 +279,7 @@ static void r4k_clear_page_s16(unsigned long page) :"$1","memory"); } -static void r4k_clear_page_s32(unsigned long page) +static void r4k_clear_page_s32(void * page) { __asm__ __volatile__( ".set\tnoreorder\n\t" @@ -308,7 +308,7 @@ static void r4k_clear_page_s32(unsigned long page) :"$1","memory"); } -static void r4k_clear_page_s64(unsigned long page) +static void r4k_clear_page_s64(void * page) { __asm__ __volatile__( ".set\tnoreorder\n\t" @@ -336,7 +336,7 @@ static void r4k_clear_page_s64(unsigned long page) :"$1","memory"); } -static void r4k_clear_page_s128(unsigned long page) +static void r4k_clear_page_s128(void * page) { __asm__ __volatile__( ".set\tnoreorder\n\t" @@ -378,7 +378,7 @@ static void r4k_clear_page_s128(unsigned long page) * virtual address where the copy will be accessed. */ -static void r4k_copy_page_d16(unsigned long to, unsigned long from) +static void r4k_copy_page_d16(void * to, void * from) { unsigned long dummy1, dummy2; unsigned long reg1, reg2, reg3, reg4; @@ -437,7 +437,7 @@ static void r4k_copy_page_d16(unsigned long to, unsigned long from) "i" (Create_Dirty_Excl_D)); } -static void r4k_copy_page_d32(unsigned long to, unsigned long from) +static void r4k_copy_page_d32(void * to, void * from) { unsigned long dummy1, dummy2; unsigned long reg1, reg2, reg3, reg4; @@ -497,7 +497,7 @@ static void r4k_copy_page_d32(unsigned long to, unsigned long from) /* * Again a special version for the R4600 V1.x */ -static void r4k_copy_page_r4600_v1(unsigned long to, unsigned long from) +static void r4k_copy_page_r4600_v1(void * to, void * from) { unsigned long dummy1, dummy2; unsigned long reg1, reg2, reg3, reg4; @@ -562,7 +562,7 @@ static void r4k_copy_page_r4600_v1(unsigned long to, unsigned long from) "i" (Create_Dirty_Excl_D)); } -static void r4k_copy_page_r4600_v2(unsigned long to, unsigned long from) +static void r4k_copy_page_r4600_v2(void * to, void * from) { unsigned long dummy1, dummy2; unsigned long reg1, reg2, reg3, reg4; @@ -633,7 +633,7 @@ static void r4k_copy_page_r4600_v2(unsigned long to, unsigned long from) /* * These are for R4000SC / R4400MC */ -static void r4k_copy_page_s16(unsigned long to, unsigned long from) +static void r4k_copy_page_s16(void * to, void * from) { unsigned long dummy1, dummy2; unsigned long reg1, reg2, reg3, reg4; @@ -692,7 +692,7 @@ static void r4k_copy_page_s16(unsigned long to, unsigned long from) "i" (Create_Dirty_Excl_SD)); } -static void r4k_copy_page_s32(unsigned long to, unsigned long from) +static void r4k_copy_page_s32(void * to, void * from) { unsigned long dummy1, dummy2; unsigned long reg1, reg2, reg3, reg4; @@ -749,7 +749,7 @@ static void r4k_copy_page_s32(unsigned long to, unsigned long from) "i" (Create_Dirty_Excl_SD)); } -static void r4k_copy_page_s64(unsigned long to, unsigned long from) +static void r4k_copy_page_s64(void * to, void * from) { unsigned long dummy1, dummy2; unsigned long reg1, reg2, reg3, reg4; @@ -805,7 +805,7 @@ static void r4k_copy_page_s64(unsigned long to, unsigned long from) "i" (Create_Dirty_Excl_SD)); } -static void r4k_copy_page_s128(unsigned long to, unsigned long from) +static void r4k_copy_page_s128(void * to, void * from) { unsigned long dummy1, dummy2; unsigned long reg1, reg2, reg3, reg4; @@ -1739,7 +1739,7 @@ static void r4k_flush_cache_page_s128d32i32(struct vm_area_struct *vma, * If ownes no valid ASID yet, cannot possibly have gotten * this page into the cache. */ - if(mm->context == 0) + if (mm->context == 0) return; #ifdef DEBUG_CACHE @@ -1962,110 +1962,119 @@ out: * flush. * 3) In KSEG1, no flush necessary. */ -static void r4k_flush_page_to_ram_s16d16i16(unsigned long page) +static void r4k_flush_page_to_ram_s16d16i16(struct page * page) { - page &= PAGE_MASK; - if((page >= KSEG0 && page < KSEG1) || (page >= KSEG2)) { + unsigned long addr = page_address(page) & PAGE_MASK; + + if ((addr >= KSEG0 && addr < KSEG1) || (addr >= KSEG2)) { #ifdef DEBUG_CACHE - printk("cram[%08lx]", page); + printk("cram[%08lx]", addr); #endif - blast_scache16_page(page); + blast_scache16_page(addr); } } -static void r4k_flush_page_to_ram_s32d16i16(unsigned long page) +static void r4k_flush_page_to_ram_s32d16i16(struct page * page) { - page &= PAGE_MASK; - if((page >= KSEG0 && page < KSEG1) || (page >= KSEG2)) { + unsigned long addr = page_address(page) & PAGE_MASK; + + if ((addr >= KSEG0 && addr < KSEG1) || (addr >= KSEG2)) { #ifdef DEBUG_CACHE - printk("cram[%08lx]", page); + printk("cram[%08lx]", addr); #endif - blast_scache32_page(page); + blast_scache32_page(addr); } } -static void r4k_flush_page_to_ram_s64d16i16(unsigned long page) +static void r4k_flush_page_to_ram_s64d16i16(struct page * page) { - page &= PAGE_MASK; - if((page >= KSEG0 && page < KSEG1) || (page >= KSEG2)) { + unsigned long addr = page_address(page) & PAGE_MASK; + + if ((addr >= KSEG0 && addr < KSEG1) || (addr >= KSEG2)) { #ifdef DEBUG_CACHE - printk("cram[%08lx]", page); + printk("cram[%08lx]", addr); #endif - blast_scache64_page(page); + blast_scache64_page(addr); } } -static void r4k_flush_page_to_ram_s128d16i16(unsigned long page) +static void r4k_flush_page_to_ram_s128d16i16(struct page * page) { - page &= PAGE_MASK; - if((page >= KSEG0 && page < KSEG1) || (page >= KSEG2)) { + unsigned long addr = page_address(page) & PAGE_MASK; + + if ((addr >= KSEG0 && addr < KSEG1) || (addr >= KSEG2)) { #ifdef DEBUG_CACHE - printk("cram[%08lx]", page); + printk("cram[%08lx]", addr); #endif - blast_scache128_page(page); + blast_scache128_page(addr); } } -static void r4k_flush_page_to_ram_s32d32i32(unsigned long page) +static void r4k_flush_page_to_ram_s32d32i32(struct page * page) { - page &= PAGE_MASK; - if((page >= KSEG0 && page < KSEG1) || (page >= KSEG2)) { + unsigned long addr = page_address(page) & PAGE_MASK; + + if ((addr >= KSEG0 && addr < KSEG1) || (addr >= KSEG2)) { #ifdef DEBUG_CACHE - printk("cram[%08lx]", page); + printk("cram[%08lx]", addr); #endif - blast_scache32_page(page); + blast_scache32_page(addr); } } -static void r4k_flush_page_to_ram_s64d32i32(unsigned long page) +static void r4k_flush_page_to_ram_s64d32i32(struct page * page) { - page &= PAGE_MASK; - if((page >= KSEG0 && page < KSEG1) || (page >= KSEG2)) { + unsigned long addr = page_address(page) & PAGE_MASK; + + if ((addr >= KSEG0 && addr < KSEG1) || (addr >= KSEG2)) { #ifdef DEBUG_CACHE - printk("cram[%08lx]", page); + printk("cram[%08lx]", addr); #endif - blast_scache64_page(page); + blast_scache64_page(addr); } } -static void r4k_flush_page_to_ram_s128d32i32(unsigned long page) +static void r4k_flush_page_to_ram_s128d32i32(struct page * page) { - page &= PAGE_MASK; - if((page >= KSEG0 && page < KSEG1) || (page >= KSEG2)) { + unsigned long addr = page_address(page) & PAGE_MASK; + + if ((addr >= KSEG0 && addr < KSEG1) || (addr >= KSEG2)) { #ifdef DEBUG_CACHE - printk("cram[%08lx]", page); + printk("cram[%08lx]", addr); #endif - blast_scache128_page(page); + blast_scache128_page(addr); } } -static void r4k_flush_page_to_ram_d16i16(unsigned long page) +static void r4k_flush_page_to_ram_d16i16(struct page * page) { - page &= PAGE_MASK; - if((page >= KSEG0 && page < KSEG1) || (page >= KSEG2)) { + unsigned long addr = page_address(page) & PAGE_MASK; + + if ((addr >= KSEG0 && addr < KSEG1) || (addr >= KSEG2)) { unsigned long flags; #ifdef DEBUG_CACHE - printk("cram[%08lx]", page); + printk("cram[%08lx]", addr); #endif - save_and_cli(flags); - blast_dcache16_page(page); - restore_flags(flags); + __save_and_cli(flags); + blast_dcache16_page(addr); + __restore_flags(flags); } } -static void r4k_flush_page_to_ram_d32i32(unsigned long page) +static void r4k_flush_page_to_ram_d32i32(struct page * page) { - page &= PAGE_MASK; - if((page >= KSEG0 && page < KSEG1) || (page >= KSEG2)) { + unsigned long addr = page_address(page) & PAGE_MASK; + + if ((addr >= KSEG0 && addr < KSEG1) || (addr >= KSEG2)) { unsigned long flags; #ifdef DEBUG_CACHE - printk("cram[%08lx]", page); + printk("cram[%08lx]", addr); #endif - save_and_cli(flags); - blast_dcache32_page(page); - restore_flags(flags); + __save_and_cli(flags); + blast_dcache32_page(addr); + __restore_flags(flags); } } diff --git a/arch/mips/mm/r6000.c b/arch/mips/mm/r6000.c index 9baf83b27..90728f89d 100644 --- a/arch/mips/mm/r6000.c +++ b/arch/mips/mm/r6000.c @@ -1,4 +1,4 @@ -/* $Id: r6000.c,v 1.7 1999/08/09 19:43:16 harald Exp $ +/* $Id: r6000.c,v 1.8 1999/10/09 00:00:58 ralf Exp $ * * r6000.c: MMU and cache routines for the R6000 processors. * @@ -16,7 +16,84 @@ #include <asm/sgialib.h> #include <asm/mmu_context.h> -__asm__(".set mips3"); /* because we know... */ +__asm__(".set mips2"); /* because we know... */ + +/* page functions */ +void r6000_clear_page(void * page) +{ + __asm__ __volatile__( + ".set\tnoreorder\n\t" + ".set\tnoat\n\t" + "addiu\t$1,%0,%2\n" + "1:\tsw\t$0,(%0)\n\t" + "sw\t$0,4(%0)\n\t" + "sw\t$0,8(%0)\n\t" + "sw\t$0,12(%0)\n\t" + "addiu\t%0,32\n\t" + "sw\t$0,-16(%0)\n\t" + "sw\t$0,-12(%0)\n\t" + "sw\t$0,-8(%0)\n\t" + "bne\t$1,%0,1b\n\t" + "sw\t$0,-4(%0)\n\t" + ".set\tat\n\t" + ".set\treorder" + :"=r" (page) + :"0" (page), + "I" (PAGE_SIZE) + :"$1","memory"); +} + +static void r6000_copy_page(void * to, void * from) +{ + unsigned long dummy1, dummy2; + unsigned long reg1, reg2, reg3, reg4; + + __asm__ __volatile__( + ".set\tnoreorder\n\t" + ".set\tnoat\n\t" + "addiu\t$1,%0,%8\n" + "1:\tlw\t%2,(%1)\n\t" + "lw\t%3,4(%1)\n\t" + "lw\t%4,8(%1)\n\t" + "lw\t%5,12(%1)\n\t" + "sw\t%2,(%0)\n\t" + "sw\t%3,4(%0)\n\t" + "sw\t%4,8(%0)\n\t" + "sw\t%5,12(%0)\n\t" + "lw\t%2,16(%1)\n\t" + "lw\t%3,20(%1)\n\t" + "lw\t%4,24(%1)\n\t" + "lw\t%5,28(%1)\n\t" + "sw\t%2,16(%0)\n\t" + "sw\t%3,20(%0)\n\t" + "sw\t%4,24(%0)\n\t" + "sw\t%5,28(%0)\n\t" + "addiu\t%0,64\n\t" + "addiu\t%1,64\n\t" + "lw\t%2,-32(%1)\n\t" + "lw\t%3,-28(%1)\n\t" + "lw\t%4,-24(%1)\n\t" + "lw\t%5,-20(%1)\n\t" + "sw\t%2,-32(%0)\n\t" + "sw\t%3,-28(%0)\n\t" + "sw\t%4,-24(%0)\n\t" + "sw\t%5,-20(%0)\n\t" + "lw\t%2,-16(%1)\n\t" + "lw\t%3,-12(%1)\n\t" + "lw\t%4,-8(%1)\n\t" + "lw\t%5,-4(%1)\n\t" + "sw\t%2,-16(%0)\n\t" + "sw\t%3,-12(%0)\n\t" + "sw\t%4,-8(%0)\n\t" + "bne\t$1,%0,1b\n\t" + "sw\t%5,-4(%0)\n\t" + ".set\tat\n\t" + ".set\treorder" + :"=r" (dummy1), "=r" (dummy2), + "=&r" (reg1), "=&r" (reg2), "=&r" (reg3), "=&r" (reg4) + :"0" (to), "1" (from), + "I" (PAGE_SIZE)); +} /* Cache operations. XXX Write these dave... */ static inline void r6000_flush_cache_all(void) @@ -42,7 +119,7 @@ static void r6000_flush_cache_page(struct vm_area_struct *vma, /* XXX */ } -static void r6000_flush_page_to_ram(unsigned long page) +static void r6000_flush_page_to_ram(struct page * page) { /* XXX */ } @@ -83,22 +160,15 @@ void pgd_init(unsigned long page) unsigned long dummy1, dummy2; /* - * This version is optimized for the R6000. We generate dirty lines - * in the datacache, overwrite these lines with zeros and then flush - * the cache. Sounds horribly complicated but is just a trick to - * avoid unnecessary loads of from memory and uncached stores which - * are very expensive. Not tested yet as the R6000 is a rare CPU only - * available in SGI machines and I don't have one. + * The plain and boring version for the R3000. No cache flushing + * stuff is implemented since the R3000 has physical caches. */ __asm__ __volatile__( ".set\tnoreorder\n" - "1:\t" - "cache\t%5,(%0)\n\t" - "sw\t%2,(%0)\n\t" + "1:\tsw\t%2,(%0)\n\t" "sw\t%2,4(%0)\n\t" "sw\t%2,8(%0)\n\t" "sw\t%2,12(%0)\n\t" - "cache\t%5,16(%0)\n\t" "sw\t%2,16(%0)\n\t" "sw\t%2,20(%0)\n\t" "sw\t%2,24(%0)\n\t" @@ -111,8 +181,7 @@ void pgd_init(unsigned long page) "=r" (dummy2) :"r" ((unsigned long) invalid_pte_table), "0" (page), - "1" (USER_PTRS_PER_PGD/8), - "i" (Create_Dirty_Excl_D)); + "1" (PAGE_SIZE/(sizeof(pmd_t)*8))); } void update_mmu_cache(struct vm_area_struct * vma, @@ -166,6 +235,9 @@ void add_wired_entry(unsigned long entrylo0, unsigned long entrylo1, void __init ld_mmu_r6000(void) { + clear_page = r6000_clear_page; + copy_page = r6000_copy_page; + flush_cache_all = r6000_flush_cache_all; flush_cache_mm = r6000_flush_cache_mm; flush_cache_range = r6000_flush_cache_range; diff --git a/arch/mips/mm/tfp.c b/arch/mips/mm/tfp.c index c0afcd21f..875a93927 100644 --- a/arch/mips/mm/tfp.c +++ b/arch/mips/mm/tfp.c @@ -1,4 +1,4 @@ -/* $Id: tfp.c,v 1.7 1999/08/09 19:43:17 harald Exp $ +/* $Id: tfp.c,v 1.8 1999/10/09 00:00:58 ralf Exp $ * * tfp.c: MMU and cache routines specific to the r8000 (TFP). * @@ -42,7 +42,7 @@ static void tfp_flush_cache_page(struct vm_area_struct *vma, /* XXX */ } -static void tfp_flush_page_to_ram(unsigned long page) +static void tfp_flush_page_to_ram(struct page * page) { /* XXX */ } diff --git a/arch/mips/mm/umap.c b/arch/mips/mm/umap.c index 7ce0d8e5a..73b5d643d 100644 --- a/arch/mips/mm/umap.c +++ b/arch/mips/mm/umap.c @@ -114,16 +114,16 @@ void *vmalloc_uncached (unsigned long size) static inline void free_pte(pte_t page) { if (pte_present(page)) { - unsigned long addr = pte_page(page); - if (MAP_NR(addr) >= max_mapnr || PageReserved(mem_map+MAP_NR(addr))) + unsigned long nr = pte_pagenr(page); + if (nr >= max_mapnr || PageReserved(mem_map+nr)) return; - free_page(addr); + __free_page(pte_page(page)); if (current->mm->rss <= 0) return; current->mm->rss--; return; } - swap_free(pte_val(page)); + swap_free(page); } static inline void forget_pte(pte_t page) @@ -152,15 +152,15 @@ vmap_pte_range (pte_t *pte, unsigned long address, unsigned long size, unsigned end = PMD_SIZE; do { pte_t oldpage = *pte; - unsigned long page; + struct page * page; pte_clear(pte); vdir = pgd_offset_k (vaddr); vpmd = pmd_offset (vdir, vaddr); vpte = pte_offset (vpmd, vaddr); page = pte_page (*vpte); - - set_pte(pte, mk_pte_phys(page, PAGE_USERIO)); + + set_pte(pte, mk_pte(page, PAGE_USERIO)); forget_pte(oldpage); address += PAGE_SIZE; vaddr += PAGE_SIZE; diff --git a/arch/mips/sgi/kernel/setup.c b/arch/mips/sgi/kernel/setup.c index e49ade621..49df754f9 100644 --- a/arch/mips/sgi/kernel/setup.c +++ b/arch/mips/sgi/kernel/setup.c @@ -1,4 +1,4 @@ -/* $Id: setup.c,v 1.27 1999/10/21 00:23:05 ralf Exp $ +/* $Id: setup.c,v 1.28 1999/12/08 11:35:38 ralf Exp $ * * setup.c: SGI specific setup, including init of the feature struct. * @@ -128,6 +128,15 @@ static void __init sgi_irq_setup(void) #endif } +int __init page_is_ram(unsigned long pagenr) +{ + if (pagenr < MAP_NR(PAGE_OFFSET + 0x2000UL)) + return 1; + if (pagenr > MAP_NR(PAGE_OFFSET + 0x08002000)) + return 1; + return 0; +} + void __init sgi_setup(void) { #ifdef CONFIG_SERIAL_CONSOLE diff --git a/arch/mips/sni/setup.c b/arch/mips/sni/setup.c index 5bcd016ee..767e2fc11 100644 --- a/arch/mips/sni/setup.c +++ b/arch/mips/sni/setup.c @@ -1,4 +1,4 @@ -/* $Id: setup.c,v 1.12 1999/10/09 00:00:59 ralf Exp $ +/* $Id: setup.c,v 1.13 1999/12/04 03:59:00 ralf Exp $ * * Setup pointers to hardware-dependent routines. * @@ -103,6 +103,11 @@ static inline void sni_pcimt_detect(void) printk("%s.\n", boardtype); } +int __init page_is_ram(unsigned long pagenr) +{ + return 1; +} + void __init sni_rm200_pci_setup(void) { tag *atag; diff --git a/arch/mips64/defconfig-ip22 b/arch/mips64/defconfig-ip22 index 273653d5d..1a987ef90 100644 --- a/arch/mips64/defconfig-ip22 +++ b/arch/mips64/defconfig-ip22 @@ -142,11 +142,9 @@ CONFIG_NETDEVICES=y # CONFIG_WAN is not set # -# PCMCIA network devices +# PCMCIA network device support # -# CONFIG_PCMCIA_PCNET is not set -# CONFIG_PCMCIA_3C589 is not set -# CONFIG_PCMCIA_RAYCS is not set +# CONFIG_NET_PCMCIA is not set CONFIG_SGISEEQ=y # diff --git a/arch/mips64/kernel/setup.c b/arch/mips64/kernel/setup.c index 2f6c9f001..35e5cb1dd 100644 --- a/arch/mips64/kernel/setup.c +++ b/arch/mips64/kernel/setup.c @@ -142,10 +142,8 @@ static inline void cpu_probe(void) } } -void __init setup_arch(char **cmdline_p, unsigned long * memory_start_p, - unsigned long * memory_end_p) +void __init setup_arch(char **cmdline_p) { - unsigned long memory_end; #ifdef CONFIG_BLK_DEV_INITRD unsigned long tmp; unsigned long *initrd_header; @@ -161,32 +159,14 @@ void __init setup_arch(char **cmdline_p, unsigned long * memory_start_p, ip27_setup(); #endif - memory_end = mips_memory_upper; - - /* - * Due to prefetching and similar mechanism the CPU sometimes - * generates addresses beyond the end of memory. We leave the size - * of one cache line at the end of memory unused to make shure we - * don't catch this type of bus errors. - */ - memory_end -= 128; - memory_end &= PAGE_MASK; - strncpy (command_line, arcs_cmdline, CL_SIZE); memcpy(saved_command_line, command_line, CL_SIZE); saved_command_line[CL_SIZE-1] = '\0'; *cmdline_p = command_line; - *memory_start_p = (unsigned long) &_end; -#ifdef CONFIG_BOOT_ELF64 - /* memory_end is a XKPHYS address but memory_start is in CKSEG. - All memory handling is done using XKPHYS addresses, so convert. */ - *memory_start_p = (*memory_start_p & 0x1ffffffUL) - | 0xa800000000000000UL; -#endif - *memory_end_p = memory_end; #ifdef CONFIG_BLK_DEV_INITRD +#error "Initrd is broken, please fit it." tmp = (((unsigned long)&_end + PAGE_SIZE-1) & PAGE_MASK) - 8; if (tmp < (unsigned long)&_end) tmp += PAGE_SIZE; diff --git a/arch/mips64/ld.script.elf32 b/arch/mips64/ld.script.elf32 index 2ae26c104..a89d049bd 100644 --- a/arch/mips64/ld.script.elf32 +++ b/arch/mips64/ld.script.elf32 @@ -4,26 +4,6 @@ SECTIONS { /* Read-only sections, merged into text segment: */ . = 0x80000000; - .rel.text : { *(.rel.text) } - .rela.text : { *(.rela.text) } - .rel.data : { *(.rel.data) } - .rela.data : { *(.rela.data) } - .rel.rodata : { *(.rel.rodata) } - .rela.rodata : { *(.rela.rodata) } - .rel.got : { *(.rel.got) } - .rela.got : { *(.rela.got) } - .rel.ctors : { *(.rel.ctors) } - .rela.ctors : { *(.rela.ctors) } - .rel.dtors : { *(.rel.dtors) } - .rela.dtors : { *(.rela.dtors) } - .rel.init : { *(.rel.init) } - .rela.init : { *(.rela.init) } - .rel.fini : { *(.rel.fini) } - .rela.fini : { *(.rela.fini) } - .rel.bss : { *(.rel.bss) } - .rela.bss : { *(.rela.bss) } - .rel.plt : { *(.rel.plt) } - .rela.plt : { *(.rela.plt) } .init : { *(.init) } =0 .text : { diff --git a/arch/mips64/lib/memcpy.S b/arch/mips64/lib/memcpy.S index d78327f11..d62c30082 100644 --- a/arch/mips64/lib/memcpy.S +++ b/arch/mips64/lib/memcpy.S @@ -691,8 +691,8 @@ ru_end_bytes: jr ra move a2, zero - END(__rmemcpy) #endif /* Horror fix */ + END(__rmemcpy) l_fixup: # clear the rest of the buffer ld ta0, THREAD_BUADDR($28) diff --git a/arch/mips64/mm/andes.c b/arch/mips64/mm/andes.c index 0c80e0377..895e5db4a 100644 --- a/arch/mips64/mm/andes.c +++ b/arch/mips64/mm/andes.c @@ -1,4 +1,4 @@ -/* $Id: andes.c,v 1.3 1999/12/04 03:59:00 ralf Exp $ +/* $Id: andes.c,v 1.4 2000/01/17 23:32:46 ralf Exp $ * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive @@ -24,7 +24,7 @@ ".set reorder\n\t") /* R10000 has no Create_Dirty type cacheops. */ -static void andes_clear_page(unsigned long page) +static void andes_clear_page(void * page) { __asm__ __volatile__( ".set\tnoreorder\n\t" @@ -47,7 +47,7 @@ static void andes_clear_page(unsigned long page) :"$1", "memory"); } -static void andes_copy_page(unsigned long to, unsigned long from) +static void andes_copy_page(void * to, void * from) { unsigned long dummy1, dummy2, reg1, reg2; @@ -176,16 +176,17 @@ out: /* Hoo hum... will this ever be called for an address that is not in CKSEG0 and not cacheable? */ static void -andes_flush_page_to_ram(unsigned long page) +andes_flush_page_to_ram(struct page * page) { - page &= PAGE_MASK; - if ((page >= K0BASE_NONCOH && page < (0xb0UL << 56)) - || (page >= KSEG0 && page < KSEG1) - || (page >= KSEG2)) { + unsigned long addr = page_address(page) & PAGE_MASK; + + if ((addr >= K0BASE_NONCOH && addr < (0xb0UL << 56)) + || (addr >= KSEG0 && addr < KSEG1) + || (addr >= KSEG2)) { #ifdef DEBUG_CACHE - printk("cram[%08lx]", page); + printk("cram[%08lx]", addr); #endif - blast_dcache32_page(page); + blast_dcache32_page(addr); } } @@ -467,9 +468,8 @@ void __init ld_mmu_andes(void) */ write_32bit_cp0_register(CP0_PAGEMASK, PM_4K); - /* We can't flush the TLB at this time since the IP27 ARC firmware - depends on it. ARC go home. */ - /* flush_tlb_all(); */ + /* From this point on the ARC firmware is dead. */ + flush_tlb_all(); /* Did I tell you that ARC SUCKS? */ } diff --git a/arch/mips64/mm/init.c b/arch/mips64/mm/init.c index 7d1fce965..682db726c 100644 --- a/arch/mips64/mm/init.c +++ b/arch/mips64/mm/init.c @@ -1,11 +1,11 @@ -/* $Id: init.c,v 1.5 1999/12/04 03:59:00 ralf Exp $ +/* $Id: init.c,v 1.6 2000/01/17 03:46:25 ralf Exp $ * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 1994 - 1999 by Ralf Baechle - * Copyright (C) 1999 by Silicon Graphics + * Copyright (C) 1994 - 2000 by Ralf Baechle + * Copyright (C) 1999, 2000 by Silicon Graphics */ #include <linux/config.h> #include <linux/init.h> @@ -19,6 +19,7 @@ #include <linux/ptrace.h> #include <linux/mman.h> #include <linux/mm.h> +#include <linux/bootmem.h> #include <linux/swap.h> #include <linux/swapctl.h> #ifdef CONFIG_BLK_DEV_INITRD @@ -35,6 +36,8 @@ #endif #include <asm/mmu_context.h> +static unsigned long totalram_pages = 0; + extern void show_net_buffers(void); void __bad_pte_kernel(pmd_t *pmd) @@ -137,7 +140,7 @@ pte_t *get_pte_kernel_slow(pmd_t *pmd, unsigned long offset) page = (pte_t *) __get_free_pages(GFP_USER, 1); if (pmd_none(*pmd)) { if (page) { - clear_page((unsigned long)page); + clear_page(page); pmd_set(pmd, page); return page + offset; } @@ -159,7 +162,7 @@ pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset) page = (pte_t *) __get_free_pages(GFP_KERNEL, 1); if (pmd_none(*pmd)) { if (page) { - clear_page((unsigned long)page); + clear_page(page); pmd_val(*pmd) = (unsigned long)page; return page + offset; } @@ -228,7 +231,7 @@ static inline unsigned long setup_zero_pages(void) panic("Oh boy, that early out of memory?"); pg = MAP_NR(empty_zero_page); - while(pg < MAP_NR(empty_zero_page) + (1 << order)) { + while (pg < MAP_NR(empty_zero_page) + (1 << order)) { set_bit(PG_reserved, &mem_map[pg].flags); set_page_count(mem_map + pg, 0); pg++; @@ -238,7 +241,7 @@ static inline unsigned long setup_zero_pages(void) zero_page_mask = (size - 1) & PAGE_MASK; memset((void *)empty_zero_page, 0, size); - return size; + return 1UL << order; } extern inline void pte_init(unsigned long page) @@ -294,10 +297,10 @@ pte_t * __bad_pagetable(void) pte_t __bad_page(void) { extern char empty_bad_page[PAGE_SIZE]; - unsigned long page = (unsigned long)empty_bad_page; + unsigned long page = (unsigned long) empty_bad_page; - clear_page(page); - return pte_mkdirty(mk_pte(page, PAGE_SHARED)); + clear_page((void *)page); + return pte_mkdirty(mk_pte_phys(__pa(page), PAGE_SHARED)); } void show_mem(void) @@ -326,90 +329,59 @@ void show_mem(void) printk("%d pages swap cached\n",cached); printk("%ld pages in page table cache\n", pgtable_cache_size); printk("%d free pages\n", free); + show_buffers(); #ifdef CONFIG_NET show_net_buffers(); #endif } -extern unsigned long free_area_init(unsigned long, unsigned long); +/* References to section boundaries */ + +extern char _ftext, _etext, _fdata, _edata; +extern char __init_begin, __init_end; -unsigned long __init -paging_init(unsigned long start_mem, unsigned long end_mem) +void __init paging_init(void) { /* Initialize the entire pgd. */ pgd_init((unsigned long)swapper_pg_dir); pgd_init((unsigned long)swapper_pg_dir + PAGE_SIZE / 2); pmd_init((unsigned long)invalid_pmd_table); - return free_area_init(start_mem, end_mem); + return free_area_init(max_low_pfn); } -void __init -mem_init(unsigned long start_mem, unsigned long end_mem) -{ - int codepages = 0; - int datapages = 0; - unsigned long tmp, etext, ftext; - extern int _etext, _ftext; - -#ifdef CONFIG_MIPS_JAZZ - if (mips_machgroup == MACH_GROUP_JAZZ) - start_mem = vdma_init(start_mem, end_mem); -#endif +extern int page_is_ram(unsigned long pagenr); - end_mem &= PAGE_MASK; - max_mapnr = MAP_NR(end_mem); - high_memory = (void *)end_mem; - num_physpages = 0; - - etext = (unsigned long) &_etext; - ftext = (unsigned long) &_ftext; -#ifdef CONFIG_BOOT_ELF64 - /* Use etext/ftext value in XPHYS */ - etext = PAGE_OFFSET | CPHYSADDR(etext); - ftext = PAGE_OFFSET | CPHYSADDR(ftext); -#endif - - /* mark usable pages in the mem_map[] */ - start_mem = PAGE_ALIGN(start_mem); +void __init mem_init(void) +{ + unsigned long codesize, reservedpages, datasize, initsize; + unsigned long tmp; - for(tmp = MAP_NR(start_mem);tmp < max_mapnr;tmp++) - clear_bit(PG_reserved, &mem_map[tmp].flags); + max_mapnr = num_physpages = max_low_pfn; + high_memory = (void *) __va(max_mapnr << PAGE_SHIFT); - prom_fixup_mem_map(start_mem, (unsigned long)high_memory); + totalram_pages += free_all_bootmem(); + totalram_pages -= setup_zero_pages(); /* Setup zeroed pages. */ - for (tmp = PAGE_OFFSET; tmp < end_mem; tmp += PAGE_SIZE) { + reservedpages = 0; + for (tmp = 0; tmp < max_low_pfn; tmp++) /* - * This is only for PC-style DMA. The onboard DMA - * of Jazz and Tyne machines is completely different and - * not handled via a flag in mem_map_t. + * Only count reserved RAM pages */ - if (tmp >= MAX_DMA_ADDRESS) - clear_bit(PG_DMA, &mem_map[MAP_NR(tmp)].flags); - if (PageReserved(mem_map+MAP_NR(tmp))) { - if ((tmp < etext) && (tmp >= ftext)) - codepages++; - else if ((tmp < start_mem) && (tmp > etext)) - datapages++; - continue; - } - num_physpages++; - set_page_count(mem_map + MAP_NR(tmp), 1); -#ifdef CONFIG_BLK_DEV_INITRD - if (!initrd_start || (tmp < initrd_start || tmp >= - initrd_end)) -#endif - free_page(tmp); - } - tmp = nr_free_pages << PAGE_SHIFT; - - /* Setup zeroed pages. */ - tmp -= setup_zero_pages(); - - printk("Memory: %luk/%luk available (%dk kernel code, %dk data)\n", - tmp >> 10, - max_mapnr << (PAGE_SHIFT-10), - codepages << (PAGE_SHIFT-10), - datapages << (PAGE_SHIFT-10)); + if (page_is_ram(tmp) && PageReserved(mem_map+tmp)) + reservedpages++; + + codesize = (unsigned long) &_etext - (unsigned long) &_ftext; + datasize = (unsigned long) &_edata - (unsigned long) &_fdata; + initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin; + + printk("Memory: %luk/%luk available (%ldk kernel code, %ldk reserved, " + "%ldk data, %ldk init)\n", + (unsigned long) nr_free_pages << (PAGE_SHIFT-10), + max_mapnr << (PAGE_SHIFT-10), + codesize >> 10, + reservedpages << (PAGE_SHIFT-10), + datasize >> 10, + initsize >> 10); } extern char __init_begin, __init_end; @@ -425,9 +397,10 @@ free_initmem(void) addr = (unsigned long)(&__init_begin); while (addr < (unsigned long)&__init_end) { page = PAGE_OFFSET | CPHYSADDR(addr); - mem_map[MAP_NR(page)].flags &= ~(1 << PG_reserved); + ClearPageReserved(mem_map + MAP_NR(page)); set_page_count(mem_map + MAP_NR(page), 1); free_page(page); + totalram_pages++; addr += PAGE_SIZE; } printk("Freeing unused kernel memory: %ldk freed\n", @@ -437,22 +410,13 @@ free_initmem(void) void si_meminfo(struct sysinfo *val) { - long i; - - i = MAP_NR(high_memory); - val->totalram = 0; + val->totalram = totalram_pages; val->sharedram = 0; - val->freeram = nr_free_pages << PAGE_SHIFT; - val->bufferram = atomic_read(&buffermem); - while (i-- > 0) { - if (PageReserved(mem_map+i)) - continue; - val->totalram++; - if (!page_count(mem_map + i)) - continue; - val->sharedram += page_count(mem_map + i) - 1; - } - val->totalram <<= PAGE_SHIFT; - val->sharedram <<= PAGE_SHIFT; + val->freeram = nr_free_pages; + val->bufferram = atomic_read(&buffermem_pages); + val->totalhigh = 0; + val->freehigh = 0; + val->mem_unit = PAGE_SIZE; + return; } diff --git a/arch/mips64/mm/loadmmu.c b/arch/mips64/mm/loadmmu.c index 0359f92a1..b2dd9e11e 100644 --- a/arch/mips64/mm/loadmmu.c +++ b/arch/mips64/mm/loadmmu.c @@ -1,4 +1,4 @@ -/* $Id: loadmmu.c,v 1.3 1999/12/04 03:59:00 ralf Exp $ +/* $Id: loadmmu.c,v 1.4 2000/01/17 23:32:46 ralf Exp $ * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive @@ -21,8 +21,8 @@ #include <asm/sgialib.h> /* memory functions */ -void (*clear_page)(unsigned long page); -void (*copy_page)(unsigned long to, unsigned long from); +void (*clear_page)(void * page); +void (*copy_page)(void * to, void * from); /* Cache operations. */ void (*flush_cache_all)(void); @@ -31,7 +31,7 @@ void (*flush_cache_range)(struct mm_struct *mm, unsigned long start, unsigned long end); void (*flush_cache_page)(struct vm_area_struct *vma, unsigned long page); void (*flush_cache_sigtramp)(unsigned long addr); -void (*flush_page_to_ram)(unsigned long page); +void (*flush_page_to_ram)(struct page * page); /* DMA cache operations. */ void (*dma_cache_wback_inv)(unsigned long start, unsigned long size); diff --git a/arch/mips64/mm/r4xx0.c b/arch/mips64/mm/r4xx0.c index ff239b38e..4c85d6ba1 100644 --- a/arch/mips64/mm/r4xx0.c +++ b/arch/mips64/mm/r4xx0.c @@ -1,4 +1,4 @@ -/* $Id: r4xx0.c,v 1.5 1999/12/04 03:59:00 ralf Exp $ +/* $Id: r4xx0.c,v 1.6 2000/01/17 23:32:46 ralf Exp $ * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive @@ -74,7 +74,7 @@ struct bcache_ops *bcops = &no_sc_ops; * versions of R4000 and R4400. */ -static void r4k_clear_page_d16(unsigned long page) +static void r4k_clear_page_d16(void * page) { __asm__ __volatile__( ".set\tnoreorder\n\t" @@ -101,7 +101,7 @@ static void r4k_clear_page_d16(unsigned long page) :"$1", "memory"); } -static void r4k_clear_page_d32(unsigned long page) +static void r4k_clear_page_d32(void * page) { __asm__ __volatile__( ".set\tnoreorder\n\t" @@ -154,7 +154,7 @@ static void r4k_clear_page_d32(unsigned long page) * nop * cache Hit_Writeback_Invalidate_D */ -static void r4k_clear_page_r4600_v1(unsigned long page) +static void r4k_clear_page_r4600_v1(void * page) { __asm__ __volatile__( ".set\tnoreorder\n\t" @@ -191,7 +191,7 @@ static void r4k_clear_page_r4600_v1(unsigned long page) /* * And this one is for the R4600 V2.0 */ -static void r4k_clear_page_r4600_v2(unsigned long page) +static void r4k_clear_page_r4600_v2(void * page) { unsigned int flags; @@ -230,7 +230,7 @@ static void r4k_clear_page_r4600_v2(unsigned long page) * this the kernel crashed shortly after mounting the root filesystem. CPU * bug? Weirdo cache instruction semantics? */ -static void r4k_clear_page_s16(unsigned long page) +static void r4k_clear_page_s16(void * page) { __asm__ __volatile__( ".set\tnoreorder\n\t" @@ -257,7 +257,7 @@ static void r4k_clear_page_s16(unsigned long page) :"$1","memory"); } -static void r4k_clear_page_s32(unsigned long page) +static void r4k_clear_page_s32(void * page) { __asm__ __volatile__( ".set\tnoreorder\n\t" @@ -282,7 +282,7 @@ static void r4k_clear_page_s32(unsigned long page) :"$1","memory"); } -static void r4k_clear_page_s64(unsigned long page) +static void r4k_clear_page_s64(void * page) { __asm__ __volatile__( ".set\tnoreorder\n\t" @@ -308,7 +308,7 @@ static void r4k_clear_page_s64(unsigned long page) :"$1","memory"); } -static void r4k_clear_page_s128(unsigned long page) +static void r4k_clear_page_s128(void * page) { __asm__ __volatile__( ".set\tnoreorder\n\t" @@ -348,7 +348,7 @@ static void r4k_clear_page_s128(unsigned long page) * virtual address where the copy will be accessed. */ -static void r4k_copy_page_d16(unsigned long to, unsigned long from) +static void r4k_copy_page_d16(void * to, void * from) { unsigned long dummy1, dummy2, reg1, reg2; @@ -386,7 +386,7 @@ static void r4k_copy_page_d16(unsigned long to, unsigned long from) "i" (Create_Dirty_Excl_D)); } -static void r4k_copy_page_d32(unsigned long to, unsigned long from) +static void r4k_copy_page_d32(void * to, void * from) { unsigned long dummy1, dummy2, reg1, reg2; @@ -425,7 +425,7 @@ static void r4k_copy_page_d32(unsigned long to, unsigned long from) /* * Again a special version for the R4600 V1.x */ -static void r4k_copy_page_r4600_v1(unsigned long to, unsigned long from) +static void r4k_copy_page_r4600_v1(void * to, void * from) { unsigned long dummy1, dummy2, reg1, reg2; @@ -469,7 +469,7 @@ static void r4k_copy_page_r4600_v1(unsigned long to, unsigned long from) "i" (Create_Dirty_Excl_D)); } -static void r4k_copy_page_r4600_v2(unsigned long to, unsigned long from) +static void r4k_copy_page_r4600_v2(void * to, void * from) { unsigned long dummy1, dummy2, reg1, reg2; unsigned int flags; @@ -519,7 +519,7 @@ static void r4k_copy_page_r4600_v2(unsigned long to, unsigned long from) /* * These are for R4000SC / R4400MC */ -static void r4k_copy_page_s16(unsigned long to, unsigned long from) +static void r4k_copy_page_s16(void * to, void * from) { unsigned long dummy1, dummy2, reg1, reg2; @@ -557,7 +557,7 @@ static void r4k_copy_page_s16(unsigned long to, unsigned long from) "i" (Create_Dirty_Excl_SD)); } -static void r4k_copy_page_s32(unsigned long to, unsigned long from) +static void r4k_copy_page_s32(void * to, void * from) { unsigned long dummy1, dummy2, reg1, reg2; @@ -593,7 +593,7 @@ static void r4k_copy_page_s32(unsigned long to, unsigned long from) "i" (Create_Dirty_Excl_SD)); } -static void r4k_copy_page_s64(unsigned long to, unsigned long from) +static void r4k_copy_page_s64(void * to, void * from) { unsigned long dummy1, dummy2, reg1, reg2; @@ -628,7 +628,7 @@ static void r4k_copy_page_s64(unsigned long to, unsigned long from) "i" (Create_Dirty_Excl_SD)); } -static void r4k_copy_page_s128(unsigned long to, unsigned long from) +static void r4k_copy_page_s128(void * to, void * from) { unsigned long dummy1, dummy2; unsigned long reg1, reg2, reg3, reg4; @@ -1727,118 +1727,125 @@ out: restore_flags(flags); } -/* If the addresses passed to these routines are valid, they are - * either: +/* If the addresses passed to these routines are valid, they are either: * * 1) In KSEG0, so we can do a direct flush of the page. - * 2) In KSEG2, and since every process can translate those - * addresses all the time in kernel mode we can do a direct - * flush. + * 2) In KSEG2, and since every process can translate those addresses all + * the time in kernel mode we can do a direct flush. * 3) In KSEG1, no flush necessary. */ -static void r4k_flush_page_to_ram_s16d16i16(unsigned long page) +static void r4k_flush_page_to_ram_s16d16i16(struct page * page) { - page &= PAGE_MASK; - if((page >= KSEG0 && page < KSEG1) || (page >= KSEG2)) { + unsigned long addr = page_address(page) & PAGE_MASK; + + if ((addr >= KSEG0 && addr < KSEG1) || (addr >= KSEG2)) { #ifdef DEBUG_CACHE - printk("cram[%08lx]", page); + printk("cram[%08lx]", addr); #endif - blast_scache16_page(page); + blast_scache16_page(addr); } } -static void r4k_flush_page_to_ram_s32d16i16(unsigned long page) +static void r4k_flush_page_to_ram_s32d16i16(struct page * page) { - page &= PAGE_MASK; - if((page >= KSEG0 && page < KSEG1) || (page >= KSEG2)) { + unsigned long addr = page_address(page) & PAGE_MASK; + + if ((addr >= KSEG0 && addr < KSEG1) || (addr >= KSEG2)) { #ifdef DEBUG_CACHE - printk("cram[%08lx]", page); + printk("cram[%08lx]", addr); #endif - blast_scache32_page(page); + blast_scache32_page(addr); } } -static void r4k_flush_page_to_ram_s64d16i16(unsigned long page) +static void r4k_flush_page_to_ram_s64d16i16(struct page * page) { - page &= PAGE_MASK; - if((page >= KSEG0 && page < KSEG1) || (page >= KSEG2)) { + unsigned long addr = page_address(page) & PAGE_MASK; + + if ((addr >= KSEG0 && addr < KSEG1) || (addr >= KSEG2)) { #ifdef DEBUG_CACHE - printk("cram[%08lx]", page); + printk("cram[%08lx]", addr); #endif - blast_scache64_page(page); + blast_scache64_page(addr); } } -static void r4k_flush_page_to_ram_s128d16i16(unsigned long page) +static void r4k_flush_page_to_ram_s128d16i16(struct page * page) { - page &= PAGE_MASK; - if((page >= KSEG0 && page < KSEG1) || (page >= KSEG2)) { + unsigned long addr = page_address(page) & PAGE_MASK; + + if ((addr >= KSEG0 && addr < KSEG1) || (addr >= KSEG2)) { #ifdef DEBUG_CACHE - printk("cram[%08lx]", page); + printk("cram[%08lx]", addr); #endif - blast_scache128_page(page); + blast_scache128_page(addr); } } -static void r4k_flush_page_to_ram_s32d32i32(unsigned long page) +static void r4k_flush_page_to_ram_s32d32i32(struct page * page) { - page &= PAGE_MASK; - if((page >= KSEG0 && page < KSEG1) || (page >= KSEG2)) { + unsigned long addr = page_address(page) & PAGE_MASK; + + if ((addr >= KSEG0 && addr < KSEG1) || (addr >= KSEG2)) { #ifdef DEBUG_CACHE - printk("cram[%08lx]", page); + printk("cram[%08lx]", addr); #endif - blast_scache32_page(page); + blast_scache32_page(addr); } } -static void r4k_flush_page_to_ram_s64d32i32(unsigned long page) +static void r4k_flush_page_to_ram_s64d32i32(struct page * page) { - page &= PAGE_MASK; - if((page >= KSEG0 && page < KSEG1) || (page >= KSEG2)) { + unsigned long addr = page_address(page) & PAGE_MASK; + + if ((addr >= KSEG0 && addr < KSEG1) || (addr >= KSEG2)) { #ifdef DEBUG_CACHE - printk("cram[%08lx]", page); + printk("cram[%08lx]", addr); #endif - blast_scache64_page(page); + blast_scache64_page(addr); } } -static void r4k_flush_page_to_ram_s128d32i32(unsigned long page) +static void r4k_flush_page_to_ram_s128d32i32(struct page * page) { - page &= PAGE_MASK; - if((page >= KSEG0 && page < KSEG1) || (page >= KSEG2)) { + unsigned long addr = page_address(page) & PAGE_MASK; + + if ((addr >= KSEG0 && addr < KSEG1) || (addr >= KSEG2)) { #ifdef DEBUG_CACHE - printk("cram[%08lx]", page); + printk("cram[%08lx]", addr); #endif - blast_scache128_page(page); + blast_scache128_page(addr); } } -static void r4k_flush_page_to_ram_d16i16(unsigned long page) +static void r4k_flush_page_to_ram_d16i16(struct page * page) { - page &= PAGE_MASK; - if((page >= KSEG0 && page < KSEG1) || (page >= KSEG2)) { + unsigned long addr = page_address(page) & PAGE_MASK; + + if ((addr >= KSEG0 && addr < KSEG1) || (addr >= KSEG2)) { unsigned long flags; #ifdef DEBUG_CACHE - printk("cram[%08lx]", page); + printk("cram[%08lx]", addr); #endif __save_and_cli(flags); - blast_dcache16_page(page); + blast_dcache16_page(addr); __restore_flags(flags); } } -static void r4k_flush_page_to_ram_d32i32(unsigned long page) +static void r4k_flush_page_to_ram_d32i32(struct page * page) { - page &= PAGE_MASK; - if((page >= KSEG0 && page < KSEG1) || (page >= KSEG2)) { + unsigned long addr = page_address(page) & PAGE_MASK; + + if ((addr >= KSEG0 && addr < KSEG1) || (addr >= KSEG2)) { unsigned long flags; #ifdef DEBUG_CACHE - printk("cram[%08lx]", page); + printk("cram[%08lx]", addr); #endif __save_and_cli(flags); - blast_dcache32_page(page); + blast_dcache32_page(addr); __restore_flags(flags); } } diff --git a/arch/mips64/mm/tfp.c b/arch/mips64/mm/tfp.c index aca942a66..ac38d97c8 100644 --- a/arch/mips64/mm/tfp.c +++ b/arch/mips64/mm/tfp.c @@ -1,4 +1,4 @@ -/* $Id: tfp.c,v 1.4 1999/12/04 03:59:01 ralf Exp $ +/* $Id: tfp.c,v 1.5 2000/01/17 23:32:46 ralf Exp $ * * tfp.c: MMU and cache routines specific to the r8000 (TFP). * @@ -16,7 +16,61 @@ #include <asm/sgialib.h> #include <asm/mmu_context.h> -extern unsigned long mips_tlb_entries; +static void tfp_clear_page(void * page) +{ + __asm__ __volatile__( + ".set\tnoreorder\n\t" + ".set\tnoat\n\t" + "daddiu\t$1,%0,%2\n" + "1:\tsd\t$0,(%0)\n\t" + "sd\t$0,8(%0)\n\t" + "sd\t$0,16(%0)\n\t" + "sd\t$0,24(%0)\n\t" + "daddiu\t%0,64\n\t" + "sd\t$0,-32(%0)\n\t" + "sd\t$0,-24(%0)\n\t" + "sd\t$0,-16(%0)\n\t" + "bne\t$1,%0,1b\n\t" + "sd\t$0,-8(%0)\n\t" + ".set\tat\n\t" + ".set\treorder" + :"=r" (page) + :"0" (page), "I" (PAGE_SIZE) + :"$1", "memory"); +} + +static void tfp_copy_page(void * to, void * from) +{ + unsigned long dummy1, dummy2, reg1, reg2; + + __asm__ __volatile__( + ".set\tnoreorder\n\t" + ".set\tnoat\n\t" + "daddiu\t$1,%0,%6\n" + "1:\tld\t%2,(%1)\n\t" + "ld\t%3,8(%1)\n\t" + "sd\t%2,(%0)\n\t" + "sd\t%3,8(%0)\n\t" + "ld\t%2,16(%1)\n\t" + "ld\t%3,24(%1)\n\t" + "sd\t%2,16(%0)\n\t" + "sd\t%3,24(%0)\n\t" + "daddiu\t%0,64\n\t" + "daddiu\t%1,64\n\t" + "ld\t%2,-32(%1)\n\t" + "ld\t%3,-24(%1)\n\t" + "sd\t%2,-32(%0)\n\t" + "sd\t%3,-24(%0)\n\t" + "ld\t%2,-16(%1)\n\t" + "ld\t%3,-8(%1)\n\t" + "sd\t%2,-16(%0)\n\t" + "bne\t$1,%0,1b\n\t" + " sd\t%3,-8(%0)\n\t" + ".set\tat\n\t" + ".set\treorder" + :"=r" (dummy1), "=r" (dummy2), "=&r" (reg1), "=&r" (reg2) + :"0" (to), "1" (from), "I" (PAGE_SIZE)); +} /* Cache operations. XXX Write these dave... */ static inline void tfp_flush_cache_all(void) @@ -42,7 +96,7 @@ static void tfp_flush_cache_page(struct vm_area_struct *vma, /* XXX */ } -static void tfp_flush_page_to_ram(unsigned long page) +static void tfp_flush_page_to_ram(struct page * page) { /* XXX */ } @@ -81,6 +135,9 @@ static int tfp_user_mode(struct pt_regs *regs) void __init ld_mmu_tfp(void) { + clear_page = tfp_clear_page; + copy_page = tfp_copy_page; + flush_cache_all = tfp_flush_cache_all; flush_cache_mm = tfp_flush_cache_mm; flush_cache_range = tfp_flush_cache_range; diff --git a/arch/mips64/mm/umap.c b/arch/mips64/mm/umap.c index 847cc8cb3..dc80a2eff 100644 --- a/arch/mips64/mm/umap.c +++ b/arch/mips64/mm/umap.c @@ -1,4 +1,4 @@ -/* $Id: umap.c,v 1.1 1999/08/18 21:46:52 ralf Exp $ +/* $Id: umap.c,v 1.2 1999/12/04 03:59:01 ralf Exp $ * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive @@ -109,16 +109,16 @@ void *vmalloc_uncached (unsigned long size) static inline void free_pte(pte_t page) { if (pte_present(page)) { - unsigned long addr = pte_page(page); - if (MAP_NR(addr) >= max_mapnr || PageReserved(mem_map+MAP_NR(addr))) + unsigned long nr = pte_pagenr(page); + if (nr >= max_mapnr || PageReserved(mem_map+nr)) return; - free_page(addr); + __free_page(pte_page(page)); if (current->mm->rss <= 0) return; current->mm->rss--; return; } - swap_free(pte_val(page)); + swap_free(page); } static inline void forget_pte(pte_t page) @@ -147,15 +147,15 @@ vmap_pte_range (pte_t *pte, unsigned long address, unsigned long size, unsigned end = PMD_SIZE; do { pte_t oldpage = *pte; - unsigned long page; + struct page * page; pte_clear(pte); vdir = pgd_offset_k (vaddr); vpmd = pmd_offset (vdir, vaddr); vpte = pte_offset (vpmd, vaddr); page = pte_page (*vpte); - - set_pte(pte, mk_pte_phys(page, PAGE_USERIO)); + + set_pte(pte, mk_pte(page, PAGE_USERIO)); forget_pte(oldpage); address += PAGE_SIZE; vaddr += PAGE_SIZE; diff --git a/arch/mips64/sgi-ip22/ip22-setup.c b/arch/mips64/sgi-ip22/ip22-setup.c index a1ae065d8..7505e0c03 100644 --- a/arch/mips64/sgi-ip22/ip22-setup.c +++ b/arch/mips64/sgi-ip22/ip22-setup.c @@ -111,6 +111,15 @@ struct kbd_ops sgi_kbd_ops = { ip22_read_status }; +int __init page_is_ram(unsigned long pagenr) +{ + if (pagenr < MAP_NR(PAGE_OFFSET + 0x2000UL)) + return 1; + if (pagenr > MAP_NR(PAGE_OFFSET + 0x08002000)) + return 1; + return 0; +} + void __init ip22_setup(void) { #ifdef CONFIG_SERIAL_CONSOLE diff --git a/arch/mips64/sgi-ip27/ip27-memory.c b/arch/mips64/sgi-ip27/ip27-memory.c index a3110cc31..ca1b1e6cc 100644 --- a/arch/mips64/sgi-ip27/ip27-memory.c +++ b/arch/mips64/sgi-ip27/ip27-memory.c @@ -13,15 +13,24 @@ #include <linux/init.h> #include <linux/config.h> #include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/bootmem.h> #include <asm/page.h> #include <asm/bootinfo.h> #include <asm/sn/klconfig.h> +extern char _end; + +#define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) +#define PFN_DOWN(x) ((x) >> PAGE_SHIFT) +#define PFN_PHYS(x) ((x) << PAGE_SHIFT) +#define PFN_ALIGN(x) (((unsigned long)(x) + (PAGE_SIZE - 1)) & PAGE_MASK) + void __init prom_meminit(void) { - unsigned long mb; + unsigned long free_start, free_end, start_pfn, mb, bootmap_size; int bank, size; lboard_t *board; klmembnk_t *mem; @@ -43,19 +52,27 @@ prom_meminit(void) } } + free_start = PFN_ALIGN(&_end) - (CKSEG0 - K0BASE); + free_end = K0BASE + (mb << 20); + start_pfn = PFN_UP((unsigned long)&_end - CKSEG0); + if (bank != MD_MEM_BANKS && size != 0) printk("Warning: noncontiguous memory configuration, " "not using entire available memory."); + /* Register all the contiguous memory with the bootmem allocator + and free it. Be careful about the bootmem freemap. */ + bootmap_size = init_bootmem(start_pfn, mb << (20 - PAGE_SHIFT)); + free_bootmem(__pa(free_start), (mb << 20) - __pa(free_start)); + reserve_bootmem(__pa(free_start), bootmap_size); + free_bootmem(0x19000, 0x1c000 - 0x19000); + printk("Found %ldmb of memory.\n", mb); - mips_memory_upper = PAGE_OFFSET + (mb << 20); } -/* Called from mem_init() to fixup the mem_map page settings. */ -void __init -prom_fixup_mem_map(unsigned long start, unsigned long end) +int __init page_is_ram(unsigned long pagenr) { - /* mem_map is already completly setup. */ + return 1; } void __init diff --git a/arch/mips64/sgi-ip27/ip27-pci.c b/arch/mips64/sgi-ip27/ip27-pci.c index 5d1eab729..4413946a7 100644 --- a/arch/mips64/sgi-ip27/ip27-pci.c +++ b/arch/mips64/sgi-ip27/ip27-pci.c @@ -114,8 +114,6 @@ void __init pcibios_init(void) nasid_t nid = get_nasid(); ioport_resource.end = ~0UL; - /* Nothing to do for now. */ - printk("%s called.\n", __FUNCTION__); printk("PCI: Probing PCI hardware on host bus 0, node %d.\n", nid); pci_scan_bus(0, ops, NULL); @@ -185,8 +183,6 @@ pcibios_fixup_bus(struct pci_bus *b) unsigned short command; struct pci_dev *dev; - /* Nothing to do for now. */ - printk("%s called.\n", __FUNCTION__); pci_fixup_irqs(pci_swizzle, pci_map_irq); /* diff --git a/arch/mips64/sgi-ip27/ip27-timer.c b/arch/mips64/sgi-ip27/ip27-timer.c index e5fd6c377..9950eb72d 100644 --- a/arch/mips64/sgi-ip27/ip27-timer.c +++ b/arch/mips64/sgi-ip27/ip27-timer.c @@ -14,6 +14,7 @@ #include <asm/pgtable.h> #include <asm/sgialib.h> +#include <asm/sn/klconfig.h> #include <asm/sn/arch.h> #include <asm/sn/addrs.h> #include <asm/sn/sn0/ip27.h> @@ -88,20 +89,21 @@ extern void ioc3_eth_init(void); void __init time_init(void) { - unsigned int cpufreq; - char *cpufreqstr; - - /* Is this timesource good enough? Ok to assume that all CPUs have - this clockrate? Are they 100% synchronously clocked? */ - cpufreqstr = ArcGetEnvironmentVariable("cpufreq"); - if (cpufreqstr == NULL) - panic("Cannot detect CPU clock rate"); - cpufreq = simple_strtoul(cpufreqstr, NULL, 10); - printk("PROM says CPU clock is %dMHz\n", cpufreq); - - /* We didn't flush the TLB earlier since the ARC firmware depends on - it. So do it now. */ - flush_tlb_all(); + lboard_t *board; + klcpu_t *cpu; + int cpuid; + + /* Don't use ARCS. ARCS is fragile. Klconfig is simple and sane. */ + board = find_lboard(KLTYPE_IP27); + if (!board) + panic("Can't find board info for myself."); + + cpuid = LOCAL_HUB_L(PI_CPU_NUM) ? IP27_CPU0_INDEX : IP27_CPU1_INDEX; + cpu = (klcpu_t *) KLCF_COMP(board, cpuid); + if (!cpu) + panic("No information about myself?"); + + printk("CPU clock is %dMHz.\n", cpu->cpu_speed); /* Don't worry about second CPU, it's disabled. */ LOCAL_HUB_S(PI_RT_EN_A, 1); diff --git a/arch/ppc/kernel/apus_setup.c b/arch/ppc/kernel/apus_setup.c index 80377490a..353482a18 100644 --- a/arch/ppc/kernel/apus_setup.c +++ b/arch/ppc/kernel/apus_setup.c @@ -255,8 +255,7 @@ struct pci_bus * __init pci_scan_peer_bridge(int bus) /*********************************************************** SETUP */ /* From arch/m68k/kernel/setup.c. */ -void __init apus_setup_arch(unsigned long * memory_start_p, - unsigned long * memory_end_p) +void __init apus_setup_arch(void) { #ifdef CONFIG_APUS extern char cmd_line[]; diff --git a/arch/ppc/kernel/chrp_pci.c b/arch/ppc/kernel/chrp_pci.c index da752ccb6..983a9f2eb 100644 --- a/arch/ppc/kernel/chrp_pci.c +++ b/arch/ppc/kernel/chrp_pci.c @@ -16,7 +16,6 @@ #include <asm/hydra.h> #include <asm/prom.h> #include <asm/gg2.h> -#include <asm/ide.h> #include <asm/machdep.h> #include "pci.h" diff --git a/arch/ppc/kernel/chrp_setup.c b/arch/ppc/kernel/chrp_setup.c index 4f71def9a..58d2003c4 100644 --- a/arch/ppc/kernel/chrp_setup.c +++ b/arch/ppc/kernel/chrp_setup.c @@ -28,6 +28,7 @@ #include <linux/reboot.h> #include <linux/init.h> #include <linux/blk.h> +#include <linux/ide.h> #include <linux/ioport.h> #include <linux/console.h> #include <linux/pci.h> @@ -35,13 +36,12 @@ #include <linux/version.h> #include <linux/adb.h> #include <linux/module.h> +#include <linux/delay.h> #include <asm/mmu.h> #include <asm/processor.h> #include <asm/io.h> #include <asm/pgtable.h> -#include <linux/ide.h> -#include <asm/ide.h> #include <asm/prom.h> #include <asm/gg2.h> #include <asm/pci-bridge.h> @@ -227,7 +227,7 @@ static void __init sio_init(void) void __init -chrp_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p) +chrp_setup_arch(void) { extern char cmd_line[]; struct device_node *device; @@ -278,7 +278,7 @@ chrp_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p) #ifdef CONFIG_DUMMY_CONSOLE conswitchp = &dummy_con; #endif - *memory_start_p = pmac_find_bridges(*memory_start_p, *memory_end_p); + pmac_find_bridges(); /* Get the event scan rate for the rtas so we know how * often it expects a heartbeat. -- Cort @@ -363,10 +363,7 @@ int chrp_get_irq( struct pt_regs *regs ) irq = *chrp_int_ack_special; else irq = i8259_irq( smp_processor_id() ); - /* - * Acknowledge as soon as possible to allow i8259 - * interrupt nesting */ - openpic_eoi( smp_processor_id() ); + openpic_eoi( smp_processor_id() ); } if (irq == OPENPIC_VEC_SPURIOUS) /* @@ -374,6 +371,11 @@ int chrp_get_irq( struct pt_regs *regs ) * acknowledged */ irq = -1; + /* + * I would like to openpic_eoi here but there seem to be timing problems + * between the openpic ack and the openpic eoi. + * -- Cort + */ return irq; } @@ -382,89 +384,15 @@ void chrp_post_irq(int irq) /* * If it's an i8259 irq then we've already done the * openpic irq. So we just check to make sure the controller - * is an openpic and if it is then eoi -- Cort + * is an openpic and if it is then eoi + * + * We do it this way since our irq_desc[irq].ctl can change + * with RTL and no longer be open_pic -- Cort */ - if ( irq_desc[irq].ctl == &open_pic ) + if ( irq >= open_pic.irq_offset) openpic_eoi( smp_processor_id() ); } -#if 0 -void -chrp_do_IRQ(struct pt_regs *regs, - int cpu, - int isfake) -{ - int irq; - unsigned long bits = 0; - int openpic_eoi_done = 0; - -#ifdef __SMP__ - { - unsigned int loops = 1000000; - while (test_bit(0, &global_irq_lock)) { - if (smp_processor_id() == global_irq_holder) { - printk("uh oh, interrupt while we hold global irq lock!\n"); -#ifdef CONFIG_XMON - xmon(0); -#endif - break; - } - if (loops-- == 0) { - printk("do_IRQ waiting for irq lock (holder=%d)\n", global_irq_holder); -#ifdef CONFIG_XMON - xmon(0); -#endif - } - } - } -#endif /* __SMP__ */ - - irq = openpic_irq(0); - if (irq == IRQ_8259_CASCADE) - { - /* - * This magic address generates a PCI IACK cycle. - * - * This should go in the above mask/ack code soon. -- Cort - */ - if ( chrp_int_ack_special ) - irq = *chrp_int_ack_special; - else - irq = i8259_irq(0); - /* - * Acknowledge as soon as possible to allow i8259 - * interrupt nesting */ - openpic_eoi(0); - openpic_eoi_done = 1; - } - if (irq == OPENPIC_VEC_SPURIOUS) - { - /* - * Spurious interrupts should never be - * acknowledged - */ - ppc_spurious_interrupts++; - openpic_eoi_done = 1; - goto out; - } - bits = 1UL << irq; - - if (irq < 0) - { - printk(KERN_DEBUG "Bogus interrupt %d from PC = %lx\n", - irq, regs->nip); - ppc_spurious_interrupts++; - } - else - { - ppc_irq_dispatch_handler( regs, irq ); - } -out: - if (!openpic_eoi_done) - openpic_eoi(0); -} -#endif - void __init chrp_init_IRQ(void) { struct device_node *np; diff --git a/arch/ppc/kernel/gemini_setup.c b/arch/ppc/kernel/gemini_setup.c index 6e599cff2..23060ba26 100644 --- a/arch/ppc/kernel/gemini_setup.c +++ b/arch/ppc/kernel/gemini_setup.c @@ -134,7 +134,7 @@ extern int root_mountflags; extern char cmd_line[]; -void __init gemini_setup_arch(unsigned long *memstart, unsigned long *memend) +void __init gemini_setup_arch(void) { unsigned int cpu; extern char cmd_line[]; diff --git a/arch/ppc/kernel/idle.c b/arch/ppc/kernel/idle.c index 09066a848..8f03ac79a 100644 --- a/arch/ppc/kernel/idle.c +++ b/arch/ppc/kernel/idle.c @@ -293,11 +293,8 @@ void power_save(void) /* set the POW bit in the MSR, and enable interrupts * so we wake up sometime! */ + __sti(); /* this keeps rtl from getting confused -- Cort */ _nmask_and_or_msr(0, MSR_POW | MSR_EE); - - /* Disable interrupts again so restore_flags will - * work. */ - _nmask_and_or_msr(MSR_EE, 0); } restore_flags(msr); default: diff --git a/arch/ppc/kernel/irq.c b/arch/ppc/kernel/irq.c index 9c157d41b..9ab3589f6 100644 --- a/arch/ppc/kernel/irq.c +++ b/arch/ppc/kernel/irq.c @@ -296,15 +296,19 @@ asmlinkage void do_IRQ(struct pt_regs *regs, int isfake) irq = ppc_md.get_irq( regs ); if ( irq < 0 ) { - printk(KERN_DEBUG "Bogus interrupt %d from PC = %lx\n", - irq, regs->nip); - ppc_spurious_interrupts++; - return; + /* -2 means ignore, already handled */ + if (irq != -2) { + printk(KERN_DEBUG "Bogus interrupt %d from PC = %lx\n", + irq, regs->nip); + ppc_spurious_interrupts++; + } + goto out; } ppc_irq_dispatch_handler( regs, irq ); if ( ppc_md.post_irq ) ppc_md.post_irq( irq ); - + + out: hardirq_exit( cpu ); } diff --git a/arch/ppc/kernel/m8xx_setup.c b/arch/ppc/kernel/m8xx_setup.c index f99f5b335..4d510612b 100644 --- a/arch/ppc/kernel/m8xx_setup.c +++ b/arch/ppc/kernel/m8xx_setup.c @@ -33,6 +33,7 @@ #include <linux/blk.h> #include <linux/ioport.h> #include <linux/ide.h> +#include <linux/bootmem.h> #include <asm/mmu.h> #include <asm/processor.h> @@ -83,13 +84,12 @@ void __init adbdev_init(void) } void __init -m8xx_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p) +m8xx_setup_arch(void) { int cpm_page; extern char cmd_line[]; - cpm_page = *memory_start_p; - *memory_start_p += PAGE_SIZE; + cpm_page = (int) alloc_bootmem_pages(PAGE_SIZE); printk("Boot arguments: %s\n", cmd_line); @@ -108,6 +108,9 @@ m8xx_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p) rd_doload = 1; rd_image_start = 0; #endif +#if 0 /* XXX this may need to be updated for the new bootmem stuff, + or possibly just deleted (see set_phys_avail() in init.c). + - paulus. */ /* initrd_start and size are setup by boot/head.S and kernel/head.S */ if ( initrd_start ) { @@ -120,6 +123,7 @@ m8xx_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p) } } #endif +#endif } void diff --git a/arch/ppc/kernel/mbx_setup.c b/arch/ppc/kernel/mbx_setup.c index 67cab4503..f6487783b 100644 --- a/arch/ppc/kernel/mbx_setup.c +++ b/arch/ppc/kernel/mbx_setup.c @@ -30,6 +30,7 @@ #include <linux/reboot.h> #include <linux/init.h> #include <linux/blk.h> +#include <linux/ide.h> #include <linux/ioport.h> #include <asm/mmu.h> @@ -37,7 +38,6 @@ #include <asm/residual.h> #include <asm/io.h> #include <asm/pgtable.h> -#include <asm/ide.h> #include <asm/mbx.h> #include <asm/machdep.h> @@ -76,13 +76,12 @@ void __init adbdev_init(void) } void __init -mbx_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p) +mbx_setup_arch(void) { int cpm_page; extern char cmd_line[]; - cpm_page = *memory_start_p; - *memory_start_p += PAGE_SIZE; + cpm_page = (int) alloc_bootmem_pages(PAGE_SIZE); sprintf(cmd_line, "%s root=/dev/nfs nfsroot=/sys/mbxroot", @@ -104,6 +103,9 @@ mbx_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p) rd_doload = 1; rd_image_start = 0; #endif +#if 0 /* XXX this may need to be updated for the new bootmem stuff, + or possibly just deleted (see set_phys_avail() in init.c). + - paulus. */ /* initrd_start and size are setup by boot/head.S and kernel/head.S */ if ( initrd_start ) { @@ -116,6 +118,7 @@ mbx_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p) } } #endif +#endif #ifdef notdef request_region(0x20,0x20,"pic1"); diff --git a/arch/ppc/kernel/misc.S b/arch/ppc/kernel/misc.S index 4d041cc98..89994881b 100644 --- a/arch/ppc/kernel/misc.S +++ b/arch/ppc/kernel/misc.S @@ -235,9 +235,9 @@ _GLOBAL(flush_dcache_range) * snoop from the data cache. * This is a no-op on the 601 which has a unified cache. * - * void flush_page_to_ram(void *page) + * void __flush_page_to_ram(void *page) */ -_GLOBAL(flush_page_to_ram) +_GLOBAL(__flush_page_to_ram) mfspr r5,PVR rlwinm r5,r5,16,16,31 cmpi 0,r5,1 diff --git a/arch/ppc/kernel/pci.c b/arch/ppc/kernel/pci.c index de0a0a36d..0d07c289d 100644 --- a/arch/ppc/kernel/pci.c +++ b/arch/ppc/kernel/pci.c @@ -146,3 +146,25 @@ int pcibios_assign_resource(struct pci_dev *pdev, int resource) { return 0; } + +/* the next two are stolen from the alpha port... */ +void __init +pcibios_update_resource(struct pci_dev *dev, struct resource *root, + struct resource *res, int resource) +{ + unsigned long where, size; + u32 reg; + + where = PCI_BASE_ADDRESS_0 + (resource * 4); + size = res->end - res->start; + pci_read_config_dword(dev, where, ®); + reg = (reg & size) | (((u32)(res->start - root->start)) & ~size); + pci_write_config_dword(dev, where, reg); +} + +void __init +pcibios_update_irq(struct pci_dev *dev, int irq) +{ + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); + /* XXX FIXME - update OF device tree node interrupt property */ +} diff --git a/arch/ppc/kernel/pmac_pci.c b/arch/ppc/kernel/pmac_pci.c index 932e7dbb6..62161f68a 100644 --- a/arch/ppc/kernel/pmac_pci.c +++ b/arch/ppc/kernel/pmac_pci.c @@ -17,6 +17,7 @@ #include <linux/delay.h> #include <linux/string.h> #include <linux/init.h> +#include <linux/bootmem.h> #include <asm/init.h> #include <asm/io.h> @@ -30,7 +31,7 @@ struct bridge_data **bridges, *bridge_list; static int max_bus; -static void add_bridges(struct device_node *dev, unsigned long *mem_ptr); +static void add_bridges(struct device_node *dev); /* * Magic constants for enabling cache coherency in the bandit/PSX bridge. @@ -362,24 +363,22 @@ static void __init init_bandit(struct bridge_data *bp) bp->io_base); } -unsigned long __init pmac_find_bridges(unsigned long mem_start, unsigned long mem_end) +void __init pmac_find_bridges(void) { int bus; struct bridge_data *bridge; bridge_list = 0; max_bus = 0; - add_bridges(find_devices("bandit"), &mem_start); - add_bridges(find_devices("chaos"), &mem_start); - add_bridges(find_devices("pci"), &mem_start); - bridges = (struct bridge_data **) mem_start; - mem_start += (max_bus + 1) * sizeof(struct bridge_data *); + add_bridges(find_devices("bandit")); + add_bridges(find_devices("chaos")); + add_bridges(find_devices("pci")); + bridges = (struct bridge_data **) + alloc_bootmem((max_bus + 1) * sizeof(struct bridge_data *)); memset(bridges, 0, (max_bus + 1) * sizeof(struct bridge_data *)); for (bridge = bridge_list; bridge != NULL; bridge = bridge->next) for (bus = bridge->bus_number; bus <= bridge->max_bus; ++bus) bridges[bus] = bridge; - - return mem_start; } /* @@ -387,7 +386,7 @@ unsigned long __init pmac_find_bridges(unsigned long mem_start, unsigned long me * "pci" (a MPC106) and no bandit or chaos bridges, and contrariwise, * if we have one or more bandit or chaos bridges, we don't have a MPC106. */ -static void __init add_bridges(struct device_node *dev, unsigned long *mem_ptr) +static void __init add_bridges(struct device_node *dev) { int *bus_range; int len; @@ -413,8 +412,7 @@ static void __init add_bridges(struct device_node *dev, unsigned long *mem_ptr) printk(KERN_INFO "PCI buses %d..%d", bus_range[0], bus_range[1]); printk(" controlled by %s at %x\n", dev->name, addr->address); - bp = (struct bridge_data *) *mem_ptr; - *mem_ptr += sizeof(struct bridge_data); + bp = (struct bridge_data *) alloc_bootmem(sizeof(*bp)); if (strcmp(dev->name, "pci") != 0) { bp->cfg_addr = (volatile unsigned int *) ioremap(addr->address + 0x800000, 0x1000); diff --git a/arch/ppc/kernel/pmac_pic.c b/arch/ppc/kernel/pmac_pic.c index 683aac568..f7224f5dd 100644 --- a/arch/ppc/kernel/pmac_pic.c +++ b/arch/ppc/kernel/pmac_pic.c @@ -225,7 +225,7 @@ pmac_get_irq(struct pt_regs *regs) xmon(regs); #endif smp_message_recv(); - return -1; + return -2; /* ignore, already handled */ } { @@ -374,7 +374,7 @@ pmac_pic_init(void) } /* get addresses of second controller */ - irqctrler = (irqctrler->next) ? irqctrler->next : NULL; + irqctrler = irqctrler->next; if (irqctrler && irqctrler->n_addrs > 0) { addr = (unsigned long) ioremap(irqctrler->addrs[0].address, 0x40); @@ -382,6 +382,11 @@ pmac_pic_init(void) pmac_irq_hw[i] = (volatile struct pmac_irq_hw*) (addr + (4 - i) * 0x10); } + } else { + /* older powermacs have a GC (grand central) or ohare at + f3000000, with interrupt control registers at f3000020. */ + addr = (unsigned long) ioremap(0xf3000000, 0x40); + pmac_irq_hw[0] = (volatile struct pmac_irq_hw *) (addr + 0x20); } /* disable all interrupts in all controllers */ diff --git a/arch/ppc/kernel/pmac_setup.c b/arch/ppc/kernel/pmac_setup.c index 3418a7e8b..0a23c4473 100644 --- a/arch/ppc/kernel/pmac_setup.c +++ b/arch/ppc/kernel/pmac_setup.c @@ -39,6 +39,7 @@ #include <linux/ioport.h> #include <linux/major.h> #include <linux/blk.h> +#include <linux/ide.h> #include <linux/vt_kern.h> #include <linux/console.h> #include <linux/ide.h> @@ -57,7 +58,6 @@ #include <asm/ohare.h> #include <asm/mediabay.h> #include <asm/feature.h> -#include <asm/ide.h> #include <asm/machdep.h> #include <asm/keyboard.h> #include <asm/dma.h> @@ -238,7 +238,7 @@ pmac_mksound(unsigned int hz, unsigned int ticks) static volatile u32 *sysctrl_regs; void __init -pmac_setup_arch(unsigned long *memory_start_p, unsigned long *memory_end_p) +pmac_setup_arch(void) { struct device_node *cpu; int *fp; @@ -269,7 +269,7 @@ pmac_setup_arch(unsigned long *memory_start_p, unsigned long *memory_end_p) __ioremap(0xffc00000, 0x400000, pgprot_val(PAGE_READONLY)); ohare_init(); - *memory_start_p = pmac_find_bridges(*memory_start_p, *memory_end_p); + pmac_find_bridges(); init_p2pbridge(); /* Checks "l2cr-value" property in the registry */ @@ -421,7 +421,7 @@ note_scsi_host(struct device_node *node, void *host) #if defined(CONFIG_BLK_DEV_IDE) && defined(CONFIG_BLK_DEV_IDE_PMAC) extern int pmac_ide_count; extern struct device_node *pmac_ide_node[]; -static int ide_majors[] = { 3, 22, 33, 34, 56, 57, 88, 89 }; +static int ide_majors[] = { 3, 22, 33, 34, 56, 57, 88, 89, 90, 91 }; kdev_t __init find_ide_boot(void) { diff --git a/arch/ppc/kernel/ppc_ksyms.c b/arch/ppc/kernel/ppc_ksyms.c index cabce054c..399b99052 100644 --- a/arch/ppc/kernel/ppc_ksyms.c +++ b/arch/ppc/kernel/ppc_ksyms.c @@ -60,7 +60,6 @@ EXPORT_SYMBOL(do_signal); EXPORT_SYMBOL(syscall_trace); EXPORT_SYMBOL(transfer_to_handler); EXPORT_SYMBOL(do_IRQ); -EXPORT_SYMBOL(init_task_union); EXPORT_SYMBOL(MachineCheckException); EXPORT_SYMBOL(AlignmentException); EXPORT_SYMBOL(ProgramCheckException); diff --git a/arch/ppc/kernel/prep_setup.c b/arch/ppc/kernel/prep_setup.c index 53c859a16..fe7043206 100644 --- a/arch/ppc/kernel/prep_setup.c +++ b/arch/ppc/kernel/prep_setup.c @@ -41,7 +41,6 @@ #include <asm/residual.h> #include <asm/io.h> #include <asm/pgtable.h> -#include <asm/ide.h> #include <asm/cache.h> #include <asm/dma.h> #include <asm/machdep.h> @@ -211,7 +210,7 @@ no_l2: } void __init -prep_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p) +prep_setup_arch() { extern char cmd_line[]; unsigned char reg; diff --git a/arch/ppc/kernel/setup.c b/arch/ppc/kernel/setup.c index 57955efca..518446bf9 100644 --- a/arch/ppc/kernel/setup.c +++ b/arch/ppc/kernel/setup.c @@ -11,12 +11,11 @@ #include <linux/reboot.h> #include <linux/delay.h> #include <linux/blk.h> +#include <linux/ide.h> #include <asm/init.h> #include <asm/residual.h> #include <asm/io.h> -#include <linux/ide.h> -#include <asm/ide.h> #include <asm/prom.h> #include <asm/processor.h> #include <asm/pgtable.h> @@ -540,14 +539,12 @@ void __init ppc_init(void) } } -void __init setup_arch(char **cmdline_p, - unsigned long * memory_start_p, unsigned long * memory_end_p) +void __init setup_arch(char **cmdline_p) { extern int panic_timeout; extern char _etext[], _edata[]; extern char *klimit; - extern unsigned long find_available_memory(void); - extern unsigned long *end_of_DRAM; + extern void do_init_bootmem(void); #ifdef CONFIG_XMON extern void xmon_map_scc(void); @@ -556,22 +553,22 @@ void __init setup_arch(char **cmdline_p, xmon(0); #endif /* CONFIG_XMON */ - /* reboot on panic */ + /* reboot on panic */ panic_timeout = 180; init_mm.start_code = PAGE_OFFSET; init_mm.end_code = (unsigned long) _etext; init_mm.end_data = (unsigned long) _edata; - init_mm.brk = (unsigned long) klimit; + init_mm.brk = (unsigned long) klimit; /* Save unparsed command line copy for /proc/cmdline */ strcpy(saved_command_line, cmd_line); *cmdline_p = cmd_line; - *memory_start_p = find_available_memory(); - *memory_end_p = (unsigned long) end_of_DRAM; + /* set up the bootmem stuff with available memory */ + do_init_bootmem(); - ppc_md.setup_arch(memory_start_p, memory_end_p); + ppc_md.setup_arch(); /* clear the progress line */ if ( ppc_md.progress ) ppc_md.progress("arch: exit", 0x3eab); } diff --git a/arch/ppc/mbx_defconfig b/arch/ppc/mbx_defconfig index 494717c54..7080efb3e 100644 --- a/arch/ppc/mbx_defconfig +++ b/arch/ppc/mbx_defconfig @@ -74,7 +74,7 @@ CONFIG_IDEDMA_PCI_AUTO=y # CONFIG_BLK_DEV_OPTI621 is not set # CONFIG_BLK_DEV_TRM290 is not set # CONFIG_BLK_DEV_NS87415 is not set -# CONFIG_BLK_DEV_VIA82C586 is not set +# CONFIG_BLK_DEV_VIA82CXXX is not set # CONFIG_BLK_DEV_CMD646 is not set CONFIG_BLK_DEV_SL82C105=y # CONFIG_IDE_CHIPSETS is not set diff --git a/arch/ppc/mm/init.c b/arch/ppc/mm/init.c index eb158fb1e..240344314 100644 --- a/arch/ppc/mm/init.c +++ b/arch/ppc/mm/init.c @@ -35,6 +35,7 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/openpic.h> +#include <linux/bootmem.h> #ifdef CONFIG_BLK_DEV_INITRD #include <linux/blk.h> /* for initrd_* */ #endif @@ -59,6 +60,9 @@ int prom_trashed; atomic_t next_mmu_context; unsigned long *end_of_DRAM; int mem_init_done; +int init_bootmem_done; +int boot_mapsize; +unsigned long totalram_pages = 0; extern pgd_t swapper_pg_dir[]; extern char _start[], _end[]; extern char etext[], _stext[]; @@ -108,9 +112,9 @@ struct mem_pieces { }; struct mem_pieces phys_mem; struct mem_pieces phys_avail; -struct mem_pieces prom_mem; static void remove_mem_piece(struct mem_pieces *, unsigned, unsigned, int); +static void set_phys_avail(void); void *find_mem_piece(unsigned, unsigned); static void print_mem_pieces(struct mem_pieces *); #if defined(CONFIG_PREP) || defined(CONFIG_APUS) || defined(CONFIG_ALL_PPC) @@ -202,7 +206,7 @@ pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset) pte = (pte_t *) MMU_get_page(); else if ((pte = (pte_t *) get_zero_page_fast()) == NULL) if ((pte = (pte_t *) __get_free_page(GFP_KERNEL))) - clear_page((unsigned long)pte); + clear_page(pte); if (pte) { pmd_val(*pmd) = (unsigned long)pte; return pte + offset; @@ -246,20 +250,20 @@ int do_check_pgt_cache(int low, int high) * ZERO_PAGE is a special page that is used for zero-initialized * data and COW. */ -unsigned long empty_bad_page_table; +pte_t *empty_bad_page_table; pte_t * __bad_pagetable(void) { - __clear_user((void *)empty_bad_page_table, PAGE_SIZE); - return (pte_t *) empty_bad_page_table; + clear_page(empty_bad_page_table); + return empty_bad_page_table; } -unsigned long empty_bad_page; +void *empty_bad_page; pte_t __bad_page(void) { - __clear_user((void *)empty_bad_page, PAGE_SIZE); - return pte_mkdirty(mk_pte(empty_bad_page, PAGE_SHARED)); + clear_page(empty_bad_page); + return pte_mkdirty(mk_pte_phys(__pa(empty_bad_page), PAGE_SHARED)); } void show_mem(void) @@ -289,6 +293,7 @@ void show_mem(void) printk("%d pages shared\n",shared); printk("%d pages swap cached\n",cached); printk("%d pages in page table cache\n",(int)pgtable_cache_size); + show_buffers(); #ifdef CONFIG_NET show_net_buffers(); #endif @@ -345,7 +350,7 @@ void si_meminfo(struct sysinfo *val) val->totalram = 0; val->sharedram = 0; val->freeram = nr_free_pages << PAGE_SHIFT; - val->bufferram = atomic_read(&buffermem); + val->bufferram = atomic_read(&buffermem_pages); while (i-- > 0) { if (PageReserved(mem_map+i)) continue; @@ -590,6 +595,37 @@ mmu_context_overflow(void) #endif /* CONFIG_8xx */ /* + * Set phys_avail to phys_mem less the kernel text/data/bss. + */ +static void __init set_phys_avail(void) +{ + unsigned long kstart, ksize; + + /* we can't call the prom any more at this stage, so + all of memory is available (after klimit) */ + phys_avail = phys_mem; + + /* + * phys_avail records memory we can use. + * Make sure the kernel text/data/bss is not in it. + */ + kstart = __pa(_stext); /* should be 0 */ + ksize = PAGE_ALIGN(klimit - _stext); + remove_mem_piece(&phys_avail, kstart, ksize, 0); + remove_mem_piece(&phys_avail, 0, 0x4000, 0); + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) { + /* + * Remove the initialized ramdisk from the available memory. + */ + remove_mem_piece(&phys_avail, __pa(initrd_start), + initrd_end - initrd_start, 1); + } +#endif /* CONFIG_BLK_DEV_INITRD */ +} + +/* * Scan a region for a piece of a given size with the required alignment. */ void __init *find_mem_piece(unsigned size, unsigned align) @@ -681,7 +717,7 @@ static void __init print_mem_pieces(struct mem_pieces *mp) printk("\n"); } -#if defined(CONFIG_PREP) || defined(CONFIG_APUS) || defined(CONFIG_PPC_ALL) +#if defined(CONFIG_PREP) || defined(CONFIG_APUS) || defined(CONFIG_ALL_PPC) /* * Add some memory to an array of pieces */ @@ -833,8 +869,8 @@ static void __init mapin_ram(void) { int i; unsigned long v, p, s, f; -#ifndef CONFIG_8xx +#ifndef CONFIG_8xx if (!__map_without_bats) { unsigned long tot, mem_base, bl, done; unsigned long max_size = (256<<20); @@ -869,24 +905,7 @@ static void __init mapin_ram(void) RAM_PAGE); } } - v = KERNELBASE; - for (i = 0; i < phys_mem.n_regions; ++i) { - p = phys_mem.regions[i].address; - for (s = 0; s < phys_mem.regions[i].size; s += PAGE_SIZE) { - f = _PAGE_PRESENT | _PAGE_ACCESSED; - if ((char *) v < _stext || (char *) v >= etext) - f |= _PAGE_RW | _PAGE_DIRTY | _PAGE_HWWRITE; - else - /* On the powerpc, no user access - forces R/W kernel access */ - f |= _PAGE_USER; - map_page(v, p, f); - v += PAGE_SIZE; - p += PAGE_SIZE; - } - } - -#else /* CONFIG_8xx */ +#endif /* CONFIG_8xx */ for (i = 0; i < phys_mem.n_regions; ++i) { v = (ulong)__va(phys_mem.regions[i].address); @@ -896,37 +915,35 @@ static void __init mapin_ram(void) * don't get ASID compares on kernel space. */ f = _PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_SHARED; - - /* I don't really need the rest of this code, but - * I grabbed it because I think the line: - * f |= _PAGE_USER - * is incorrect. It needs to be set to bits we - * don't define to cause a kernel read-only. On - * the MPC8xx, the PAGE_DIRTY takes care of that - * for us (along with the RW software state). - */ if ((char *) v < _stext || (char *) v >= etext) f |= _PAGE_RW | _PAGE_DIRTY | _PAGE_HWWRITE; +#ifndef CONFIG_8xx + else + /* On the powerpc (not 8xx), no user access + forces R/W kernel access */ + f |= _PAGE_USER; +#endif /* CONFIG_8xx */ map_page(v, p, f); v += PAGE_SIZE; p += PAGE_SIZE; } - } -#endif /* CONFIG_8xx */ + } } -/* This can get called from ioremap, so don't make it an __init, OK? */ +/* In fact this is only called until mem_init is done. */ static void __init *MMU_get_page(void) { void *p; if (mem_init_done) { p = (void *) __get_free_page(GFP_KERNEL); - if (p == 0) - panic("couldn't get a page in MMU_get_page"); + } else if (init_bootmem_done) { + p = alloc_bootmem_pages(PAGE_SIZE); } else { p = find_mem_piece(PAGE_SIZE, PAGE_SIZE); } + if (p == 0) + panic("couldn't get a page in MMU_get_page"); __clear_user(p, PAGE_SIZE); return p; } @@ -1106,6 +1123,51 @@ void __init MMU_init(void) } /* + * Initialize the bootmem system and give it all the memory we + * have available. + */ +void __init do_init_bootmem(void) +{ + unsigned long start, size; + int i; + + /* + * Find an area to use for the bootmem bitmap. + * We look for the first area which is at least + * 128kB in length (128kB is enough for a bitmap + * for 4GB of memory, using 4kB pages), plus 1 page + * (in case the address isn't page-aligned). + */ + start = 0; + size = 0; + for (i = 0; i < phys_avail.n_regions; ++i) { + unsigned long a = phys_avail.regions[i].address; + unsigned long s = phys_avail.regions[i].size; + if (s <= size) + continue; + start = a; + size = s; + if (s >= 33 * PAGE_SIZE) + break; + } + start = PAGE_ALIGN(start); + + boot_mapsize = init_bootmem(start >> PAGE_SHIFT, + __pa(end_of_DRAM) >> PAGE_SHIFT); + + /* remove the bootmem bitmap from the available memory */ + remove_mem_piece(&phys_avail, start, start + boot_mapsize, 1); + + /* add everything in phys_avail into the bootmem map */ + for (i = 0; i < phys_avail.n_regions; ++i) + free_bootmem(phys_avail.regions[i].address, + phys_avail.regions[i].size); + + init_bootmem_done = 1; +} + +#if 0 +/* * Find some memory for setup_arch to return. * We use the largest chunk of available memory as the area * that setup_arch returns, making sure that there are at @@ -1143,97 +1205,56 @@ unsigned long __init find_available_memory(void) avail_start = (unsigned long) __va(a); return avail_start; } +#endif /* 0 */ /* * paging_init() sets up the page tables - in fact we've already done this. */ -unsigned long __init paging_init(unsigned long start_mem, unsigned long end_mem) +void __init paging_init(void) { - extern unsigned long free_area_init(unsigned long, unsigned long); /* * Grab some memory for bad_page and bad_pagetable to use. */ - empty_bad_page = PAGE_ALIGN(start_mem); - empty_bad_page_table = empty_bad_page + PAGE_SIZE; - start_mem = empty_bad_page + 2 * PAGE_SIZE; - - /* note: free_area_init uses its second argument - to size the mem_map array. */ - start_mem = free_area_init(start_mem, end_mem); - return start_mem; + empty_bad_page = alloc_bootmem_pages(PAGE_SIZE); + empty_bad_page_table = alloc_bootmem_pages(PAGE_SIZE); + + free_area_init(max_low_pfn); } -void __init mem_init(unsigned long start_mem, unsigned long end_mem) +void __init mem_init(void) { unsigned long addr; - int i; - unsigned long a, lim; int codepages = 0; int datapages = 0; int initpages = 0; extern unsigned int rtas_data, rtas_size; - end_mem &= PAGE_MASK; - high_memory = (void *) end_mem; - max_mapnr = MAP_NR(high_memory); - - /* mark usable pages in the mem_map[] */ - start_mem = PAGE_ALIGN(start_mem); - + max_mapnr = max_low_pfn; + high_memory = (void *) __va(max_low_pfn * PAGE_SIZE); num_physpages = max_mapnr; /* RAM is assumed contiguous */ - remove_mem_piece(&phys_avail, __pa(avail_start), - start_mem - avail_start, 1); - for (i = 0; i < phys_avail.n_regions; ++i) { - a = (unsigned long) __va(phys_avail.regions[i].address); - lim = (a + phys_avail.regions[i].size) & PAGE_MASK; - a = PAGE_ALIGN(a); - for (; a < lim; a += PAGE_SIZE) - clear_bit(PG_reserved, &mem_map[MAP_NR(a)].flags); - } + totalram_pages += free_all_bootmem(); #ifdef CONFIG_BLK_DEV_INITRD /* if we are booted from BootX with an initial ramdisk, make sure the ramdisk pages aren't reserved. */ if (initrd_start) { - for (a = initrd_start; a < initrd_end; a += PAGE_SIZE) - clear_bit(PG_reserved, &mem_map[MAP_NR(a)].flags); + for (addr = initrd_start; addr < initrd_end; addr += PAGE_SIZE) + clear_bit(PG_reserved, &mem_map[MAP_NR(addr)].flags); } #endif /* CONFIG_BLK_DEV_INITRD */ - /* free the prom's memory - no-op on prep */ - for (i = 0; i < prom_mem.n_regions; ++i) { - a = (unsigned long) __va(prom_mem.regions[i].address); - lim = (a + prom_mem.regions[i].size) & PAGE_MASK; - a = PAGE_ALIGN(a); - for (; a < lim; a += PAGE_SIZE) - clear_bit(PG_reserved, &mem_map[MAP_NR(a)].flags); - } - - prom_trashed = 1; - - for (addr = PAGE_OFFSET; addr < end_mem; addr += PAGE_SIZE) { - if (PageReserved(mem_map + MAP_NR(addr))) { - if (addr < (ulong) etext) - codepages++; - else if (addr >= (unsigned long)&__init_begin - && addr < (unsigned long)&__init_end) - initpages++; - else if (addr < (ulong) start_mem) - datapages++; + for (addr = PAGE_OFFSET; addr < (unsigned long)end_of_DRAM; + addr += PAGE_SIZE) { + if (!PageReserved(mem_map + MAP_NR(addr))) continue; - } - set_page_count(mem_map + MAP_NR(addr), 1); -#ifdef CONFIG_BLK_DEV_INITRD - if (!initrd_start || - addr < (initrd_start & PAGE_MASK) || addr >= initrd_end) -#endif /* CONFIG_BLK_DEV_INITRD */ -#ifndef CONFIG_8xx - if ( !rtas_data || - addr < (rtas_data & PAGE_MASK) || - addr >= (rtas_data+rtas_size)) -#endif /* CONFIG_8xx */ - free_page(addr); + if (addr < (ulong) etext) + codepages++; + else if (addr >= (unsigned long)&__init_begin + && addr < (unsigned long)&__init_end) + initpages++; + else if (addr < (ulong) klimit) + datapages++; } printk("Memory: %luk available (%dk kernel code, %dk data, %dk init) [%08x,%08lx]\n", @@ -1241,7 +1262,7 @@ void __init mem_init(unsigned long start_mem, unsigned long end_mem) codepages << (PAGE_SHIFT-10), datapages << (PAGE_SHIFT-10), initpages << (PAGE_SHIFT-10), - PAGE_OFFSET, end_mem); + PAGE_OFFSET, (unsigned long) end_of_DRAM); mem_init_done = 1; } @@ -1257,11 +1278,9 @@ void __init mem_init(unsigned long start_mem, unsigned long end_mem) unsigned long __init *pmac_find_end_of_memory(void) { unsigned long a, total; - unsigned long kstart, ksize; - int i; /* max amount of RAM we allow -- Cort */ -#define RAM_LIMIT (256<<20) +#define RAM_LIMIT (768<<20) memory_node = find_devices("memory"); if (memory_node == NULL) { @@ -1309,32 +1328,8 @@ unsigned long __init *pmac_find_end_of_memory(void) phys_mem.n_regions = 1; } - if (boot_infos == 0) { - /* record which bits the prom is using */ - get_mem_prop("available", &phys_avail); - prom_mem = phys_mem; - for (i = 0; i < phys_avail.n_regions; ++i) - remove_mem_piece(&prom_mem, - phys_avail.regions[i].address, - phys_avail.regions[i].size, 0); - } else { - /* booted from BootX - it's all available (after klimit) */ - phys_avail = phys_mem; - prom_mem.n_regions = 0; - } + set_phys_avail(); - /* - * phys_avail records memory we can use now. - * prom_mem records memory allocated by the prom that we - * don't want to use now, but we'll reclaim later. - * Make sure the kernel text/data/bss is in neither. - */ - kstart = __pa(_stext); /* should be 0 */ - ksize = PAGE_ALIGN(klimit - _stext); - remove_mem_piece(&phys_avail, kstart, ksize, 0); - remove_mem_piece(&prom_mem, kstart, ksize, 0); - remove_mem_piece(&phys_avail, 0, 0x4000, 0); - remove_mem_piece(&prom_mem, 0, 0x4000, 0); #undef RAM_LIMIT return __va(total); } @@ -1349,7 +1344,6 @@ unsigned long __init *pmac_find_end_of_memory(void) */ unsigned long __init *prep_find_end_of_memory(void) { - unsigned long kstart, ksize; unsigned long total; total = res->TotalMemory; @@ -1364,11 +1358,7 @@ unsigned long __init *prep_find_end_of_memory(void) printk("Ramsize default to be %ldM\n", total>>20); } append_mem_piece(&phys_mem, 0, total); - phys_avail = phys_mem; - kstart = __pa(_stext); /* should be 0 */ - ksize = PAGE_ALIGN(klimit - _stext); - remove_mem_piece(&phys_avail, kstart, ksize, 0); - remove_mem_piece(&phys_avail, 0, 0x4000, 0); + set_phys_avail(); return (__va(total)); } @@ -1378,7 +1368,7 @@ unsigned long __init *prep_find_end_of_memory(void) #if defined(CONFIG_GEMINI) unsigned long __init *gemini_find_end_of_memory(void) { - unsigned long total, kstart, ksize, *ret; + unsigned long total, *ret; unsigned char reg; reg = readb(GEMINI_MEMCFG); @@ -1390,10 +1380,7 @@ unsigned long __init *gemini_find_end_of_memory(void) phys_mem.n_regions = 1; ret = __va(phys_mem.regions[0].size); - phys_avail = phys_mem; - kstart = __pa(_stext); - ksize = PAGE_ALIGN( _end - _stext ); - remove_mem_piece( &phys_avail, kstart, ksize, 0 ); + set_phys_avail(); return ret; } #endif /* defined(CONFIG_GEMINI) || defined(CONFIG_ALL_PPC) */ @@ -1429,15 +1416,8 @@ unsigned long __init *apus_find_end_of_memory(void) } /* Now register the memory block. */ - { - unsigned long kstart, ksize; - - append_mem_piece(&phys_mem, memory[0].addr, memory[0].size); - phys_avail = phys_mem; - kstart = __pa(_stext); - ksize = PAGE_ALIGN(klimit - _stext); - remove_mem_piece(&phys_avail, kstart, ksize, 0); - } + append_mem_piece(&phys_mem, memory[0].addr, memory[0].size); + set_phys_avail(); /* Remove the memory chunks that are controlled by special Phase5 hardware. */ @@ -1586,7 +1566,6 @@ static void __init hash_init(void) */ unsigned long __init *m8xx_find_end_of_memory(void) { - unsigned long kstart, ksize; bd_t *binfo; unsigned long *ret; extern unsigned char __res[]; @@ -1600,11 +1579,7 @@ unsigned long __init *m8xx_find_end_of_memory(void) ret = __va(phys_mem.regions[0].address+ phys_mem.regions[0].size); - phys_avail = phys_mem; - - kstart = __pa(_stext); /* should be 0 */ - ksize = PAGE_ALIGN(_end - _stext); - remove_mem_piece(&phys_avail, kstart, ksize, 0); + set_phys_avail(); return ret; } #endif /* ndef CONFIG_8xx */ diff --git a/arch/sh/Makefile b/arch/sh/Makefile index c1c6f1e29..ae76ea1eb 100644 --- a/arch/sh/Makefile +++ b/arch/sh/Makefile @@ -4,6 +4,8 @@ # License. See the file "COPYING" in the main directory of this archive # for more details. # +# Copyright (C) 1999 Kaz Kojima +# # This file is included by the global makefile so that you can add your own # architecture-specific flags and dependencies. Remember to do have actions # for "archclean" and "archdep" for cleaning up and making dependencies for @@ -13,25 +15,35 @@ # # Select the object file format to substitute into the linker script. # -tool-prefix = sh-gniibe- -oformat = elf +tool-prefix = sh-elf + +ifdef CONFIG_LITTLE_ENDIAN +CFLAGS += -ml +AFLAGS += -ml +# LINKFLAGS += -EL +LDFLAGS := -EL + +LD =$(CROSS_COMPILE)ld $(LDFLAGS) + +endif ifdef CONFIG_CROSSCOMPILE CROSS_COMPILE = $(tool-prefix) endif -LINKFLAGS = # -EL # -static #-N MODFLAGS += # # -CFLAGS += -m3 # -ml -LINKFLAGS += -LDFLAGS += # -EL -# -# -HOSTCC = cc +ifdef CONFIG_CPU_SH3 +CFLAGS += -m3 +AFLAGS += -m3 +endif +ifdef CONFIG_CPU_SH4 +CFLAGS += -m4 +AFLAGS += -m4 +endif # # Choosing incompatible machines durings configuration will result in @@ -52,14 +64,16 @@ HEAD := arch/sh/kernel/head.o arch/sh/kernel/init_task.o SUBDIRS := $(SUBDIRS) $(addprefix arch/sh/, kernel mm lib) CORE_FILES := arch/sh/kernel/kernel.o arch/sh/mm/mm.o $(CORE_FILES) -LIBS := $(TOPDIR)/arch/sh/lib/lib.a $(LIBS) $(TOPDIR)/arch/sh/lib/lib.a /home/niibe/lib/gcc-lib/sh-gniibe-elf/egcs-2.91.66/libgcc.a +LIBGCC := $(shell $(CC) $(CFLAGS) -print-libgcc-file-name) +LIBS := $(TOPDIR)/arch/sh/lib/lib.a $(LIBS) $(TOPDIR)/arch/sh/lib/lib.a \ + $(LIBGCC) MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot vmlinux: arch/sh/vmlinux.lds arch/sh/vmlinux.lds: arch/sh/vmlinux.lds.S FORCE - gcc -E -C -P -I$(HPATH) -imacros $(HPATH)/linux/config.h -Ush arch/sh/vmlinux.lds.S >arch/sh/vmlinux.lds + gcc -E -C -P -I$(HPATH) -Ush arch/sh/vmlinux.lds.S >arch/sh/vmlinux.lds FORCE: ; @@ -77,6 +91,7 @@ archclean: # $(MAKE) -C arch/$(ARCH)/tools clean archmrproper: + rm -f arch/sh/vmlinux.lds archdep: @$(MAKEBOOT) dep diff --git a/arch/sh/config.in b/arch/sh/config.in index 48874a0f0..516dc3748 100644 --- a/arch/sh/config.in +++ b/arch/sh/config.in @@ -12,28 +12,24 @@ endmenu mainmenu_option next_comment comment 'Processor type and features' choice 'Processor family' \ - "SH3 CONFIG_CPU_SH3 \ - SH4 CONFIG_CPU_SH4" SH3 + "SH-3 CONFIG_CPU_SH3 \ + SH-4 CONFIG_CPU_SH4" SH-3 bool 'Little Endian' CONFIG_LITTLE_ENDIAN -hex 'Physical memory start address' CONFIG_MEMORY_START 0c000000 +hex 'Physical memory start address' CONFIG_MEMORY_START 08000000 +bool 'Use SH CPU internal real time clock' CONFIG_SH_CPU_RTC endmenu mainmenu_option next_comment comment 'Loadable module support' bool 'Enable loadable module support' CONFIG_MODULES if [ "$CONFIG_MODULES" = "y" ]; then - bool ' Set version information on all symbols for modules' CONFIG_MODVERSIONS - bool ' Kernel module loader' CONFIG_KMOD + bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS + bool 'Kernel module loader' CONFIG_KMOD fi endmenu -define_bool CONFIG_SERIAL n -define_bool CONFIG_SH3SCI_SERIAL y -define_bool CONFIG_SERIAL_CONSOLE y - mainmenu_option next_comment -comment 'Floppy, IDE, and other block devices' - +comment 'General setup' bool 'Networking support' CONFIG_NET bool 'System V IPC' CONFIG_SYSVIPC bool 'BSD Process Accounting' CONFIG_BSD_PROCESS_ACCT @@ -41,6 +37,18 @@ bool 'Sysctl support' CONFIG_SYSCTL tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC +endmenu + +mainmenu_option next_comment +comment 'Character devices' +define_bool CONFIG_SERIAL n +define_bool CONFIG_SERIAL_CONSOLE y +bool 'SuperH SCI support' CONFIG_SH_SCI_SERIAL +bool 'SuperH SCIF support' CONFIG_SH_SCIF_SERIAL +endmenu + +mainmenu_option next_comment +comment 'Floppy, IDE, and other block devices' tristate 'RAM disk support' CONFIG_BLK_DEV_RAM if [ "$CONFIG_BLK_DEV_RAM" = "y" ]; then @@ -75,4 +83,5 @@ mainmenu_option next_comment comment 'Kernel hacking' bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ +bool 'GDB Stub kernel debug' CONFIG_DEBUG_KERNEL_WITH_GDB_STUB endmenu diff --git a/arch/sh/defconfig b/arch/sh/defconfig index bd830d4a9..dcf006a15 100644 --- a/arch/sh/defconfig +++ b/arch/sh/defconfig @@ -10,48 +10,41 @@ # # Processor type and features # -CONFIG_CPU_SH3=y -# CONFIG_CPU_SH4 is not set -# CONFIG_LITTLE_ENDIAN is not set -CONFIG_MEMORY_START=0c000000 +# CONFIG_CPU_SH3 is not set +CONFIG_CPU_SH4=y +CONFIG_LITTLE_ENDIAN=y +CONFIG_MEMORY_START=08000000 # # Loadable module support # # CONFIG_MODULES is not set -# CONFIG_SERIAL is not set -CONFIG_SH3SCI_SERIAL=y -CONFIG_SERIAL_CONSOLE=y # -# Floppy, IDE, and other block devices +# General setup # # CONFIG_NET is not set -CONFIG_SYSVIPC=y +# CONFIG_SYSVIPC is not set # CONFIG_BSD_PROCESS_ACCT is not set # CONFIG_SYSCTL is not set CONFIG_BINFMT_ELF=y # CONFIG_BINFMT_MISC is not set -CONFIG_BLK_DEV_RAM=y -CONFIG_BLK_DEV_INITRD=y -# CONFIG_BLK_DEV_LOOP is not set -# CONFIG_BLK_DEV_NBD is not set # -# Networking options +# Character devices # -# CONFIG_PACKET is not set -# CONFIG_NETLINK is not set -# CONFIG_FIREWALL is not set -# CONFIG_FILTER is not set -# CONFIG_UNIX is not set -# CONFIG_INET is not set +# CONFIG_SERIAL is not set +CONFIG_SERIAL_CONSOLE=y +# CONFIG_SH_SCI_SERIAL is not set +CONFIG_SH_SCIF_SERIAL=y # -# +# Floppy, IDE, and other block devices # -# CONFIG_IPX is not set -# CONFIG_ATALK is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_INITRD=y +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set # # Unix 98 PTY support @@ -66,8 +59,12 @@ CONFIG_BLK_DEV_INITRD=y # CONFIG_AFFS_FS is not set # CONFIG_HFS_FS is not set # CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set # CONFIG_ISO9660_FS is not set # CONFIG_JOLIET is not set +# CONFIG_UDF_FS is not set # CONFIG_MINIX_FS is not set # CONFIG_NTFS_FS is not set # CONFIG_HPFS_FS is not set @@ -78,16 +75,15 @@ CONFIG_EXT2_FS=y # CONFIG_UFS_FS is not set # -# Network File Systems -# - -# # Partition Types # # CONFIG_PARTITION_ADVANCED is not set CONFIG_MSDOS_PARTITION=y -# CONFIG_SMD_DISKLABEL is not set -# CONFIG_SGI_DISKLABEL is not set +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_SUN_PARTITION is not set # CONFIG_NLS is not set # @@ -99,3 +95,4 @@ CONFIG_MSDOS_PARTITION=y # Kernel hacking # # CONFIG_MAGIC_SYSRQ is not set +CONFIG_DEBUG_KERNEL_WITH_GDB_STUB=y diff --git a/arch/sh/kernel/Makefile b/arch/sh/kernel/Makefile index 0a2abf858..6cf0b319e 100644 --- a/arch/sh/kernel/Makefile +++ b/arch/sh/kernel/Makefile @@ -11,7 +11,7 @@ O_TARGET := kernel.o O_OBJS := process.o signal.o entry.o traps.o irq.o irq_onchip.o \ - ptrace.o setup.o time.o sys_sh.o test-img.o semaphore.o + ptrace.o setup.o time.o sys_sh.o semaphore.o OX_OBJS := sh_ksyms.o MX_OBJS := diff --git a/arch/sh/kernel/entry.S b/arch/sh/kernel/entry.S index 7fca58b30..bd9c7bb39 100644 --- a/arch/sh/kernel/entry.S +++ b/arch/sh/kernel/entry.S @@ -12,6 +12,7 @@ #include <linux/sys.h> #include <linux/linkage.h> +#include <linux/config.h> ! NOTE: ! GNU as (as of 2.9.1) changes bf/s into bt/s and bra, when the address @@ -28,18 +29,18 @@ * Stack layout in 'ret_from_syscall': * ptrace needs to have all regs on the stack. * if the order here is changed, it needs to be - * updated in process.c:copy_thread, signal.c:do_signal, - * ptrace.c and ptrace.h + * updated in ptrace.c and ptrace.h * - * syscall # + * syscall # + * ssr + * r15 = stack pointer * r0 * ... - * r15 + * r14 * gbr * mach * macl * pr - * ssr * spc * */ @@ -57,14 +58,23 @@ PF_TRACESYS = 0x20 ENOSYS = 38 -TRA = 0xffffffd0 -EXPEVT = 0xffffffd4 -INTEVT = 0xffffffd8 +#if defined(__sh3__) +TRA = 0xffffffd0 +EXPEVT = 0xffffffd4 +INTEVT = 0xffffffd8 +MMU_TEA = 0xfffffffc ! TLB Exception Address Register +#elif defined(__SH4__) +TRA = 0xff000020 +EXPEVT = 0xff000024 +INTEVT = 0xff000028 +MMU_TEA = 0xff00000c ! TLB Exception Address Register +#endif /* Offsets to the stack */ SYSCALL_NR = 0 -R0 = 4 -R15 = 64 +SR = 4 +SP = 8 +R0 = 12 #define k0 r0 #define k1 r1 @@ -99,20 +109,19 @@ R15 = 64 ! Although this could be written in assembly language (and it'd be faster), ! this first version depends *much* on C implementation. ! -MMU_TEA = 0xfffffffc ! TLB Exception Address Register -#define DO_FAULT(write) \ - mov #MMU_TEA,r0; \ - mov.l @r0,r6; \ - /* STI */ \ - mov.l 3f,r1; \ - stc sr,r0; \ - and r1,r0; \ - ldc r0,sr; \ - /* */ \ - mov r15,r4; \ - mov.l 2f,r0; \ - jmp @r0; \ +#define DO_FAULT(write) \ + mov.l 4f,r0; \ + mov.l @r0,r6; \ + /* STI */ \ + mov.l 3f,r1; \ + stc sr,r0; \ + and r1,r0; \ + ldc r0,sr; \ + /* */ \ + mov r15,r4; \ + mov.l 2f,r0; \ + jmp @r0; \ mov #write,r5; .balign 4 @@ -133,22 +142,65 @@ initial_page_write: .balign 4 2: .long SYMBOL_NAME(do_page_fault) 3: .long 0xefffffff ! BL=0 +4: .long MMU_TEA +#ifdef CONFIG_DEBUG_KERNEL_WITH_GDB_STUB + .balign 4 + /* Unwind the stack and jmp to the debug entry */ +debug: + add #4,r15 ! skip syscall number + ldc.l @r15+,ssr + mov.l @r15+,r10 ! original stack + mov.l @r15+,r0 + mov.l @r15+,r1 + mov.l @r15+,r2 + mov.l @r15+,r3 + mov.l @r15+,r4 + mov.l @r15+,r5 + mov.l @r15+,r6 + mov.l @r15+,r7 + stc sr,r14 + mov.l 8f,r9 ! BL =1, RB=1 + or r9,r14 + ldc r14,sr ! here, change the register bank + mov r10,k0 + mov.l @r15+,r8 + mov.l @r15+,r9 + mov.l @r15+,r10 + mov.l @r15+,r11 + mov.l @r15+,r12 + mov.l @r15+,r13 + mov.l @r15+,r14 + ldc.l @r15+,gbr + lds.l @r15+,mach + lds.l @r15+,macl + lds.l @r15+,pr + ldc.l @r15+,spc + mov k0,r15 + ! + mov.l 9f,k0 + jmp @k0 + nop + .balign 4 +8: .long 0x300000f0 +9: .long 0xa0000100 +#endif .balign 4 -error: mov #-1,r0 +error: ! STI mov.l 2f,r1 stc sr,r0 and r1,r0 ldc r0,sr ! - mov.l r0,@r15 ! syscall nr = -1 mov.l 1f,r1 + mov #-1,r0 jmp @r1 - nop + mov.l r0,@r15 ! syscall nr = -1 .balign 4 1: .long SYMBOL_NAME(do_exception_error) +2: .long 0xefffffff ! BL=0 reschedule: mova SYMBOL_NAME(ret_from_syscall),r0 @@ -159,12 +211,13 @@ reschedule: 1: .long SYMBOL_NAME(schedule) badsys: mov #-ENOSYS,r0 - bra SYMBOL_NAME(ret_from_syscall) + rts ! go to ret_from_syscall.. mov.l r0,@(R0,r15) signal_return: ! We can reach here from an interrupt handler, ! so, we need to unblock interrupt. + /* STI */ mov.l 1f,r1 stc sr,r0 and r1,r0 @@ -185,15 +238,25 @@ signal_return: ! ENTRY(ret_from_fork) bra SYMBOL_NAME(ret_from_syscall) - add #4,r15 ! pop down bogus r0 + add #4,r15 ! pop down bogus r0 (see switch_to MACRO) ! ! The immediate value of "trapa" indicates the number of arguments ! placed on the stack. ! +! Note that TRA register contains the value = Imm x 4. +! system_call: - mov #TRA,r2 + mov.l 1f,r2 mov.l @r2,r8 + ! +#ifdef CONFIG_DEBUG_KERNEL_WITH_GDB_STUB + mov #0x20,r1 + extu.b r1,r1 + shll2 r1 + cmp/hs r1,r8 + bt debug +#endif ! STI mov.l 2f,r1 stc sr,r2 @@ -202,14 +265,15 @@ system_call: ! mov.l __n_sys,r1 cmp/ge r1,r0 - bt badsys + bt/s badsys + mov r0,r2 ! stc ksp,r1 ! mov.l __tsk_flags,r0 ! add r0,r1 ! mov.l @r1,r0 ! Is it trace? tst #PF_TRACESYS,r0 - bt 6f + bt 5f ! Trace system call mov #-ENOSYS,r1 mov.l r1,@(R0,r15) @@ -217,32 +281,36 @@ system_call: jsr @r1 nop mova 4f,r0 - bra 7f + bra 6f lds r0,pr ! -6: mova 1f,r0 +5: mova ret,r0 ! normal case lds r0,pr ! Build the stack frame if TRA > 0 -7: cmp/pl r8 + ! +6: mov r2,r3 + mov r8,r2 + cmp/pl r8 bf 9f - shll2 r8 ! x4 - mov #R15,r0 - mov.l @(r0,r15),r0 ! get original stack -8: add #-4,r8 - mov.l @(r0,r8),r1 + mov.l @(SP,r15),r0 ! get original stack +7: add #-4,r8 +8: mov.l @(r0,r8),r1 ! May cause address error exception.. mov.l r1,@-r15 cmp/pl r8 - bt 8b + bt 7b ! -9: mov.l @(SYSCALL_NR,r15),r0 +9: mov r3,r0 shll2 r0 ! x4 mov.l __sct,r1 add r1,r0 mov.l @r0,r1 jmp @r1 - nop + mov r2,r8 + + ! In case of trace .balign 4 -4: mov.l r0,@(R0,r15) ! save the return value +4: add r8,r15 ! pop off the arguments + mov.l r0,@(R0,r15) ! save the return value mov.l 3f,r1 mova SYMBOL_NAME(ret_from_syscall),r0 jmp @r1 @@ -250,11 +318,36 @@ system_call: .balign 4 3: .long SYMBOL_NAME(syscall_trace) 2: .long 0xefffffff ! BL=0 -1: mov.l r0,@(R0,r15) ! save the return value +1: .long TRA + + .section .fixup,"ax" +fixup_syscall_argerr: + rts + mov.l 1f,r0 +1: .long -22 ! -EINVAL +.previous + + .section __ex_table, "a" + .balign 4 + .long 8b,fixup_syscall_argerr +.previous + + +ENTRY(ret_from_irq) + mov.l @(SR,r15),r0 ! get original stack + shll r0 + shll r0 ! kernel space? + bt restore_all ! Yes, it's from kernel, go back soon + ! XXX: Is it better to run through bottom half? + ! In such a case, we should go "ret_from_syscall" instead + bra ret_with_reschedule + nop + +ret: add r8,r15 ! pop off the arguments + mov.l r0,@(R0,r15) ! save the return value /* fall through */ ENTRY(ret_from_syscall) -ENTRY(ret_from_irq) mov.l __bh_mask,r0 mov.l @r0,r1 mov.l __bh_active,r0 @@ -276,9 +369,10 @@ ret_with_reschedule: tst #0xff,r0 bf signal_return ! - .balign 4 restore_all: add #4,r15 ! skip syscall number + ldc.l @r15+,ssr + mov.l @r15+,r10 ! original stack mov.l @r15+,r0 mov.l @r15+,r1 mov.l @r15+,r2 @@ -291,6 +385,7 @@ restore_all: mov.l __blrb_flags,r9 ! BL =1, RB=1 or r9,r14 ldc r14,sr ! here, change the register bank + mov r10,k0 mov.l @r15+,r8 mov.l @r15+,r9 mov.l @r15+,r10 @@ -298,12 +393,10 @@ restore_all: mov.l @r15+,r12 mov.l @r15+,r13 mov.l @r15+,r14 - mov.l @r15+,k0 ldc.l @r15+,gbr lds.l @r15+,mach lds.l @r15+,macl lds.l @r15+,pr - ldc.l @r15+,ssr ldc.l @r15+,spc mov k0,r15 rte @@ -330,29 +423,32 @@ ENTRY(vbr_base) ! .balign 256,0,256 general_exception: - mov #EXPEVT,k2 + mov.l 1f,k2 mov.l 2f,k3 bra handle_exception mov.l @k2,k2 .balign 4 2: .long SYMBOL_NAME(ret_from_syscall) +1: .long EXPEVT ! ! .balign 1024,0,1024 tlb_miss: - mov #EXPEVT,k2 + mov.l 1f,k2 mov.l 3f,k3 bra handle_exception mov.l @k2,k2 ! .balign 512,0,512 interrupt: - mov #INTEVT,k2 + mov.l 2f,k2 mov.l 4f,k3 bra handle_exception mov.l @k2,k2 .balign 4 +1: .long EXPEVT +2: .long INTEVT 3: .long SYMBOL_NAME(ret_from_syscall) 4: .long SYMBOL_NAME(ret_from_irq) @@ -362,15 +458,13 @@ handle_exception: ! Using k0, k1 for scratch registers (r0_bank1, and r1_bank1), ! save all registers onto stack. ! - mov.l 2f,k1 stc ssr,k0 ! from kernel space? shll k0 ! Check MD bit (bit30) shll k0 bt/s 1f ! it's from kernel to kernel transition mov r15,k0 ! save original stack to k0 anyway mov kernel_sp,r15 ! change to kernel stack -1: stc.l spc,@-r15 ! save control registers - stc.l ssr,@-r15 +1: stc.l spc,@-r15 sts.l pr,@-r15 ! lds k3,pr ! Set the return address to pr @@ -378,9 +472,9 @@ handle_exception: sts.l macl,@-r15 sts.l mach,@-r15 stc.l gbr,@-r15 - mov.l k0,@-r15 ! save orignal stack, and general registers mov.l r14,@-r15 ! + mov.l 2f,k1 stc sr,r14 ! back to normal register bank, and and k1,r14 ! .. ldc r14,sr ! ...changed here. @@ -399,6 +493,8 @@ handle_exception: mov.l r2,@-r15 mov.l r1,@-r15 mov.l r0,@-r15 + stc.l r0_bank,@-r15 ! save orignal stack + stc.l ssr,@-r15 mov.l r0,@-r15 ! push r0 again (for syscall number) ! Then, dispatch to the handler, according to the excepiton code. stc k_ex_code,r1 @@ -413,10 +509,14 @@ handle_exception: 1: .long SYMBOL_NAME(exception_handling_table) 2: .long 0xdfffffff ! RB=0, BL=1 +none: + rts + nop + .data ENTRY(exception_handling_table) - .long 0 - .long 0 + .long none /* XXX: Avoid spurious interrupt */ + .long error .long tlb_miss_load .long tlb_miss_store .long initial_page_write @@ -424,13 +524,13 @@ ENTRY(exception_handling_table) .long tlb_protection_violation_store .long error ! address_error_load (filled by trap_init) .long error ! address_error_store (filled by trap_init) - .long 0 - .long 0 + .long error ! fpu_exception + .long error .long system_call ! Unconditional Trap .long error ! reserved_instruction (filled by trap_init) .long error ! illegal_slot_instruction (filled by trap_init) ENTRY(nmi_slot) - .long error ! Not implemented yet + .long none ! Not implemented yet ENTRY(user_break_point_trap) .long error ! Not implemented yet ENTRY(interrupt_table) @@ -450,7 +550,7 @@ ENTRY(interrupt_table) .long SYMBOL_NAME(do_IRQ) ! 1100 .long SYMBOL_NAME(do_IRQ) ! 1101 .long SYMBOL_NAME(do_IRQ) ! 1110 - .long 0 + .long error ! Internal hardware .long SYMBOL_NAME(do_IRQ) ! TMU0 tuni0 .long SYMBOL_NAME(do_IRQ) ! TMU1 tuni1 @@ -468,14 +568,24 @@ ENTRY(interrupt_table) .long SYMBOL_NAME(do_IRQ) ! rovi .long SYMBOL_NAME(do_IRQ) .long SYMBOL_NAME(do_IRQ) + .long SYMBOL_NAME(do_IRQ) ! Hitachi UDI + .long SYMBOL_NAME(do_IRQ) ! GPIO + .long SYMBOL_NAME(do_IRQ) ! DMAC dmte0 + .long SYMBOL_NAME(do_IRQ) ! dmte1 + .long SYMBOL_NAME(do_IRQ) ! dmte2 + .long SYMBOL_NAME(do_IRQ) ! dmte3 + .long SYMBOL_NAME(do_IRQ) ! dmae .long SYMBOL_NAME(do_IRQ) - .long SYMBOL_NAME(do_IRQ) - .long SYMBOL_NAME(do_IRQ) - .long SYMBOL_NAME(do_IRQ) - .long SYMBOL_NAME(do_IRQ) - .long SYMBOL_NAME(do_IRQ) - .long SYMBOL_NAME(do_IRQ) - .long SYMBOL_NAME(do_IRQ) + .long SYMBOL_NAME(do_IRQ) ! SCIF eri + .long SYMBOL_NAME(do_IRQ) ! rxi + .long SYMBOL_NAME(do_IRQ) ! bri + .long SYMBOL_NAME(do_IRQ) ! txi + .long error + .long error + .long error + .long error + .long error ! fpu + .long error ! fpu ENTRY(sys_call_table) .long SYMBOL_NAME(sys_ni_syscall) /* 0 - old "setup()" system call*/ @@ -568,7 +678,7 @@ ENTRY(sys_call_table) .long SYMBOL_NAME(sys_swapon) .long SYMBOL_NAME(sys_reboot) .long SYMBOL_NAME(old_readdir) - .long SYMBOL_NAME(sys_ni_syscall) /* old_mmap */ /* 90 */ + .long SYMBOL_NAME(sys_mmap) /* 90 */ .long SYMBOL_NAME(sys_munmap) .long SYMBOL_NAME(sys_truncate) .long SYMBOL_NAME(sys_ftruncate) diff --git a/arch/sh/kernel/head.S b/arch/sh/kernel/head.S index ed466ba38..17d019707 100644 --- a/arch/sh/kernel/head.S +++ b/arch/sh/kernel/head.S @@ -2,7 +2,7 @@ * * arch/sh/kernel/head.S * - * Copyright (C) 1999 Niibe Yutaka + * Copyright (C) 1999 Niibe Yutaka & Kaz Kojima * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive @@ -10,60 +10,74 @@ * * Head.S contains the SH exception handlers and startup code. */ -#include <linux/config.h> -#include <linux/threads.h> #include <linux/linkage.h> -#include <asm/page.h> -#include <asm/pgtable.h> -#ifdef CONFIG_CPU_SH3 -/* Following values are assumed to be as small as immediate. */ -#define CCR 0xffffffec /* Address of Cache Control Register */ -#define CACHE_INIT 0x00000009 /* 8k-byte cache, flush, enable */ -#elif CONFIG_CPU_SH4 -/* Should fill here. */ -#endif + .section .empty_zero_page, "aw" +ENTRY(empty_zero_page) + .long 1 /* MOUNT_ROOT_RDONLY */ + .long 0 /* RAMDISK_FLAGS */ + .long 0x0200 /* ORIG_ROOT_DEV */ + .long 1 /* LOADER_TYPE */ + .long 0x88400000 /* INITRD_START */ + .long 0x00400000 /* INITRD_SIZE */ + .long 0x89000000 /* MEMORY_END */ + .long 0 + .text + .balign 4096,0,4096 +/* + * Condition at the entry of _stext: + * + * BSC has already been initialized. + * INTC may or may not be initialized. + * VBR may or may not be initialized. + * MMU may or may not be initialized. + * Cache may or may not be initialized. + * Hardware (including on-chip modules) may or may not be initialized. + * + * The register R4&R5 holds the address of the parameter block, which has + * command-line data, etc. + * + */ ENTRY(_stext) - ! Switch to register bank 0 - stc sr,r1 ! - mov.l 1f,r0 ! RB=0, BL=1 - and r1,r0 - ldc r0,sr - ! Enable cache -#ifdef CONFIG_CPU_SH3 - mov #CCR,r1 - mov.l @r1,r0 - cmp/eq #1,r0 ! If it's enabled already, don't flush it - bt/s 8f - mov #CACHE_INIT,r0 - mov.l r0,@r1 -#elif CONFIG_CPU_SH4 - ! Should fill here. +#if defined(__SH4__) + ! Initialize FPSCR + /* GCC (as of 2.95.1) assumes FPU with double precision mode. */ + mov.l 7f,r0 + lds r0,fpscr #endif -8: + ! Initialize Status Register + mov.l 1f,r0 ! MD=1, RB=0, BL=1 + ldc r0,sr ! mov.l 2f,r0 mov r0,r15 ! Set initial r15 (stack pointer) ldc r0,r4_bank ! and stack base + ! + ! Enable cache + mov.l 6f,r0 + jsr @r0 + nop ! Clear BSS area mov.l 3f,r1 + add #4,r1 mov.l 4f,r2 mov #0,r0 -9: mov.l r0,@r1 - cmp/hs r2,r1 - bf/s 9b - add #4,r1 +9: cmp/hs r2,r1 + bf/s 9b ! while (r1 < r2) + mov.l r0,@-r2 ! Start kernel mov.l 5f,r0 jmp @r0 nop .balign 4 -1: .long 0xdfffffff ! RB=0, BL=1 -2: .long SYMBOL_NAME(stack) -3: .long SYMBOL_NAME(__bss_start) -4: .long SYMBOL_NAME(_end) -5: .long SYMBOL_NAME(start_kernel) - -.data +1: .long 0x50000000 ! MD=1, RB=0, BL=1 +2: .long SYMBOL_NAME(stack) +3: .long SYMBOL_NAME(__bss_start) +4: .long SYMBOL_NAME(_end) +5: .long SYMBOL_NAME(start_kernel) +6: .long SYMBOL_NAME(cache_init) +#if defined(__SH4__) +7: .long 0x00080000 +#endif diff --git a/arch/sh/kernel/irq.c b/arch/sh/kernel/irq.c index f75af5003..580f4415c 100644 --- a/arch/sh/kernel/irq.c +++ b/arch/sh/kernel/irq.c @@ -1,10 +1,11 @@ -/* +/* $Id$ + * * linux/arch/sh/kernel/irq.c * * Copyright (C) 1992, 1998 Linus Torvalds, Ingo Molnar * * - * SuperH version: Copyright (C) 1999 Niibe Yutaka + * SuperH version: Copyright (C) 1999 Niibe Yutaka */ /* @@ -48,7 +49,7 @@ spinlock_t irq_controller_lock = SPIN_LOCK_UNLOCKED; /* * Controller mappings for all interrupt sources: */ -irq_desc_t irq_desc[NR_IRQS] = { [0 ... NR_IRQS-1] = { 0, &no_irq_type, }}; +irq_desc_t irq_desc[NR_IRQS] __cacheline_aligned = { [0 ... NR_IRQS-1] = { 0, &no_irq_type, }}; /* * Special irq handlers. @@ -57,6 +58,37 @@ irq_desc_t irq_desc[NR_IRQS] = { [0 ... NR_IRQS-1] = { 0, &no_irq_type, }}; void no_action(int cpl, void *dev_id, struct pt_regs *regs) { } /* + * Generic no controller code + */ + +static void enable_none(unsigned int irq) { } +static unsigned int startup_none(unsigned int irq) { return 0; } +static void disable_none(unsigned int irq) { } +static void ack_none(unsigned int irq) +{ +/* + * 'what should we do if we get a hw irq event on an illegal vector'. + * each architecture has to answer this themselves, it doesnt deserve + * a generic callback i think. + */ + printk("unexpected IRQ trap at vector %02x\n", irq); +} + +/* startup is the same as "enable", shutdown is same as "disable" */ +#define shutdown_none disable_none +#define end_none enable_none + +struct hw_interrupt_type no_irq_type = { + "none", + startup_none, + shutdown_none, + enable_none, + disable_none, + ack_none, + end_none +}; + +/* * Generic, controller-independent functions: */ @@ -203,6 +235,8 @@ asmlinkage int do_IRQ(unsigned long r4, unsigned long r5, struct irqaction * action; unsigned int status; + regs.syscall_nr = -1; /* It's not system call */ + /* Get IRQ number */ asm volatile("stc r2_bank,%0\n\t" "shlr2 %0\n\t" @@ -257,7 +291,7 @@ asmlinkage int do_IRQ(unsigned long r4, unsigned long r5, for (;;) { handle_IRQ_event(irq, ®s, action); spin_lock(&irq_controller_lock); - + if (!(desc->status & IRQ_PENDING)) break; desc->status &= ~IRQ_PENDING; @@ -265,7 +299,7 @@ asmlinkage int do_IRQ(unsigned long r4, unsigned long r5, } desc->status &= ~IRQ_INPROGRESS; if (!(desc->status & IRQ_DISABLED)){ - irq_desc[irq].handler->end(irq); + irq_desc[irq].handler->end(irq); } spin_unlock(&irq_controller_lock); @@ -334,16 +368,22 @@ void free_irq(unsigned int irq, void *dev_id) /* Found it - now remove it from the list of entries */ *pp = action->next; - if (irq_desc[irq].action) - break; - irq_desc[irq].status |= IRQ_DISABLED; - irq_desc[irq].handler->shutdown(irq); - break; + if (!irq_desc[irq].action) { + irq_desc[irq].status |= IRQ_DISABLED; + irq_desc[irq].handler->shutdown(irq); + } + spin_unlock_irqrestore(&irq_controller_lock,flags); + + /* Wait to make sure it's not being used on another CPU */ + while (irq_desc[irq].status & IRQ_INPROGRESS) + barrier(); + kfree(action); + return; } printk("Trying to free free IRQ%d\n",irq); - break; + spin_unlock_irqrestore(&irq_controller_lock,flags); + return; } - spin_unlock_irqrestore(&irq_controller_lock,flags); } /* diff --git a/arch/sh/kernel/irq_onchip.c b/arch/sh/kernel/irq_onchip.c index 2eae049e5..b4ba54f31 100644 --- a/arch/sh/kernel/irq_onchip.c +++ b/arch/sh/kernel/irq_onchip.c @@ -1,4 +1,5 @@ -/* +/* $Id$ + * * linux/arch/sh/kernel/irq_onchip.c * * Copyright (C) 1999 Niibe Yutaka @@ -31,32 +32,6 @@ #include <linux/irq.h> - -/* - * SH (non-)specific no controller code - */ - -static void enable_none(unsigned int irq) { } -static unsigned int startup_none(unsigned int irq) { return 0; } -static void disable_none(unsigned int irq) { } -static void ack_none(unsigned int irq) -{ -} - -/* startup is the same as "enable", shutdown is same as "disable" */ -#define shutdown_none disable_none -#define end_none enable_none - -struct hw_interrupt_type no_irq_type = { - "none", - startup_none, - shutdown_none, - enable_none, - disable_none, - ack_none, - end_none -}; - struct ipr_data { int offset; int priority; @@ -104,22 +79,25 @@ static struct hw_interrupt_type onChip_irq_type = { * IPRC 15-12 11-8 7-4 3-0 * */ +#if defined(__sh3__) #define INTC_IPR 0xfffffee2UL /* Word access */ +#define INTC_SIZE 0x2 +#elif defined(__SH4__) +#define INTC_IPR 0xffd00004UL /* Word access */ +#define INTC_SIZE 0x4 +#endif void disable_onChip_irq(unsigned int irq) { /* Set priority in IPR to 0 */ int offset = ipr_data[irq-TIMER_IRQ].offset; - unsigned long intc_ipr_address = INTC_IPR + offset/16; + unsigned long intc_ipr_address = INTC_IPR + (offset/16*INTC_SIZE); unsigned short mask = 0xffff ^ (0xf << (offset%16)); - unsigned long __dummy; - - asm volatile("mov.w @%1,%0\n\t" - "and %2,%0\n\t" - "mov.w %0,@%1" - : "=&z" (__dummy) - : "r" (intc_ipr_address), "r" (mask) - : "memory" ); + unsigned long val; + + val = ctrl_inw(intc_ipr_address); + val &= mask; + ctrl_outw(val, intc_ipr_address); } static void enable_onChip_irq(unsigned int irq) @@ -127,16 +105,13 @@ static void enable_onChip_irq(unsigned int irq) /* Set priority in IPR back to original value */ int offset = ipr_data[irq-TIMER_IRQ].offset; int priority = ipr_data[irq-TIMER_IRQ].priority; - unsigned long intc_ipr_address = INTC_IPR + offset/16; + unsigned long intc_ipr_address = INTC_IPR + (offset/16*INTC_SIZE); unsigned short value = (priority << (offset%16)); - unsigned long __dummy; - - asm volatile("mov.w @%1,%0\n\t" - "or %2,%0\n\t" - "mov.w %0,@%1" - : "=&z" (__dummy) - : "r" (intc_ipr_address), "r" (value) - : "memory" ); + unsigned long val; + + val = ctrl_inw(intc_ipr_address); + val |= value; + ctrl_outw(val, intc_ipr_address); } void make_onChip_irq(unsigned int irq) @@ -149,13 +124,11 @@ void make_onChip_irq(unsigned int irq) static void mask_and_ack_onChip(unsigned int irq) { disable_onChip_irq(irq); - sti(); } static void end_onChip_irq(unsigned int irq) { enable_onChip_irq(irq); - cli(); } void __init init_IRQ(void) diff --git a/arch/sh/kernel/process.c b/arch/sh/kernel/process.c index 744da694b..2e58ad830 100644 --- a/arch/sh/kernel/process.c +++ b/arch/sh/kernel/process.c @@ -1,9 +1,10 @@ -/* +/* $Id$ + * * linux/arch/sh/kernel/process.c * * Copyright (C) 1995 Linus Torvalds * - * SuperH version: Copyright (C) 1999 Niibe Yutaka + * SuperH version: Copyright (C) 1999 Niibe Yutaka & Kaz Kojima */ /* @@ -41,6 +42,10 @@ #include <linux/irq.h> +#if defined(__SH4__) +struct task_struct *last_task_used_math = NULL; +#endif + static int hlt_counter=0; #define HARD_IDLE_TIMEOUT (HZ / 3) @@ -92,22 +97,21 @@ void machine_power_off(void) void show_regs(struct pt_regs * regs) { printk("\n"); - printk("PC: [<%08lx>]", regs->pc); - printk(" SP: %08lx", regs->u_regs[UREG_SP]); - printk(" SR: %08lx\n", regs->sr); - printk("R0 : %08lx R1 : %08lx R2 : %08lx R3 : %08lx\n", - regs->u_regs[0],regs->u_regs[1], - regs->u_regs[2],regs->u_regs[3]); - printk("R4 : %08lx R5 : %08lx R6 : %08lx R7 : %08lx\n", - regs->u_regs[4],regs->u_regs[5], - regs->u_regs[6],regs->u_regs[7]); - printk("R8 : %08lx R9 : %08lx R10: %08lx R11: %08lx\n", - regs->u_regs[8],regs->u_regs[9], - regs->u_regs[10],regs->u_regs[11]); - printk("R12: %08lx R13: %08lx R14: %08lx\n", - regs->u_regs[12],regs->u_regs[13], - regs->u_regs[14]); - printk("MACH: %08lx MACL: %08lx GBR: %08lx PR: %08lx", + printk("PC : %08lx SP : %08lx SR : %08lx TEA : %08lx\n", + regs->pc, regs->sp, regs->sr, ctrl_inl(MMU_TEA)); + printk("R0 : %08lx R1 : %08lx R2 : %08lx R3 : %08lx\n", + regs->regs[0],regs->regs[1], + regs->regs[2],regs->regs[3]); + printk("R4 : %08lx R5 : %08lx R6 : %08lx R7 : %08lx\n", + regs->regs[4],regs->regs[5], + regs->regs[6],regs->regs[7]); + printk("R8 : %08lx R9 : %08lx R10 : %08lx R11 : %08lx\n", + regs->regs[8],regs->regs[9], + regs->regs[10],regs->regs[11]); + printk("R12 : %08lx R13 : %08lx R14 : %08lx\n", + regs->regs[12],regs->regs[13], + regs->regs[14]); + printk("MACH: %08lx MACL: %08lx GBR : %08lx PR : %08lx\n", regs->mach, regs->macl, regs->gbr, regs->pr); } @@ -163,13 +167,35 @@ int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) */ void exit_thread(void) { +#if defined(__sh3__) /* nothing to do ... */ +#elif defined(__SH4__) +#if 0 /* for the time being... */ + /* Forget lazy fpu state */ + if (last_task_used_math == current) { + set_status_register (SR_FD, 0); + write_system_register (fpscr, FPSCR_PR); + last_task_used_math = NULL; + } +#endif +#endif } void flush_thread(void) { +#if defined(__sh3__) /* do nothing */ /* Possibly, set clear debug registers */ +#elif defined(__SH4__) +#if 0 /* for the time being... */ + /* Forget lazy fpu state */ + if (last_task_used_math == current) { + set_status_register (SR_FD, 0); + write_system_register (fpscr, FPSCR_PR); + last_task_used_math = NULL; + } +#endif +#endif } void release_thread(struct task_struct *dead_task) @@ -180,6 +206,15 @@ void release_thread(struct task_struct *dead_task) /* Fill in the fpu structure for a core dump.. */ int dump_fpu(struct pt_regs *regs, elf_fpregset_t *r) { +#if defined(__SH4__) +#if 0 /* for the time being... */ + /* We store the FPU info in the task->thread area. */ + if (! (regs->sr & SR_FD)) { + memcpy (r, ¤t->thread.fpu, sizeof (*r)); + return 1; + } +#endif +#endif return 0; /* Task didn't use the fpu at all. */ } @@ -191,14 +226,26 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, struct pt_regs *childregs; childregs = ((struct pt_regs *)(THREAD_SIZE + (unsigned long) p)) - 1; - *childregs = *regs; + +#if defined(__SH4__) +#if 0 /* for the time being... */ + if (last_task_used_math == current) { + set_status_register (SR_FD, 0); + sh4_save_fp (p); + } + /* New tasks loose permission to use the fpu. This accelerates context + switching for most programs since they don't use the fpu. */ + p->thread.sr = (read_control_register (sr) &~ SR_MD) | SR_FD; + childregs->sr |= SR_FD; +#endif +#endif if (user_mode(regs)) { - childregs->u_regs[UREG_SP] = usp; + childregs->sp = usp; } else { - childregs->u_regs[UREG_SP] = (unsigned long)p+2*PAGE_SIZE; + childregs->sp = (unsigned long)p+2*PAGE_SIZE; } - childregs->u_regs[0] = 0; /* Set return value for child */ + childregs->regs[0] = 0; /* Set return value for child */ p->thread.sp = (unsigned long) childregs; p->thread.pc = (unsigned long) ret_from_fork; @@ -215,18 +262,22 @@ void dump_thread(struct pt_regs * regs, struct user * dump) { /* changed the size calculations - should hopefully work better. lbt */ dump->magic = CMAGIC; - dump->start_code = 0; - dump->start_stack = regs->u_regs[UREG_SP] & ~(PAGE_SIZE - 1); - dump->u_tsize = ((unsigned long) current->mm->end_code) >> PAGE_SHIFT; - dump->u_dsize = ((unsigned long) (current->mm->brk + (PAGE_SIZE-1))) >> PAGE_SHIFT; - dump->u_dsize -= dump->u_tsize; - dump->u_ssize = 0; + dump->start_code = current->mm->start_code; + dump->start_data = current->mm->start_data; + dump->start_stack = regs->sp & ~(PAGE_SIZE - 1); + dump->u_tsize = (current->mm->end_code - dump->start_code) >> PAGE_SHIFT; + dump->u_dsize = (current->mm->brk + (PAGE_SIZE-1) - dump->start_data) >> PAGE_SHIFT; + dump->u_ssize = (current->mm->start_stack - dump->start_stack + + PAGE_SIZE - 1) >> PAGE_SHIFT; /* Debug registers will come here. */ - if (dump->start_stack < TASK_SIZE) - dump->u_ssize = ((unsigned long) (TASK_SIZE - dump->start_stack)) >> PAGE_SHIFT; - dump->regs = *regs; + +#if 0 /* defined(__SH4__) */ + /* FPU */ + memcpy (&dump->regs[EF_SIZE/4], ¤t->thread.fpu, + sizeof (current->thread.fpu)); +#endif } /* @@ -248,7 +299,7 @@ asmlinkage int sys_fork(unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7, struct pt_regs regs) { - return do_fork(SIGCHLD, regs.u_regs[UREG_SP], ®s); + return do_fork(SIGCHLD, regs.sp, ®s); } asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp, @@ -256,7 +307,7 @@ asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp, struct pt_regs regs) { if (!newsp) - newsp = regs.u_regs[UREG_SP]; + newsp = regs.sp; return do_fork(clone_flags, newsp, ®s); } @@ -274,8 +325,7 @@ asmlinkage int sys_vfork(unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7, struct pt_regs regs) { - return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, - regs.u_regs[UREG_SP], ®s); + return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.sp, ®s); } /* diff --git a/arch/sh/kernel/setup.c b/arch/sh/kernel/setup.c index 6714fd96d..59c97a66b 100644 --- a/arch/sh/kernel/setup.c +++ b/arch/sh/kernel/setup.c @@ -1,4 +1,5 @@ -/* +/* $Id$ + * * linux/arch/sh/kernel/setup.c * * Copyright (C) 1999 Niibe Yutaka @@ -29,6 +30,7 @@ #endif #include <asm/processor.h> #include <linux/console.h> +#include <asm/pgtable.h> #include <asm/uaccess.h> #include <asm/system.h> #include <asm/io.h> @@ -38,8 +40,7 @@ * Machine setup.. */ -struct sh_cpuinfo boot_cpu_data = { 0, 0, 0, 0, }; -extern int _text, _etext, _edata, _end, _stext, __bss_start; +struct sh_cpuinfo boot_cpu_data = { CPU_SH_NONE, 0, 0, 0, }; #ifdef CONFIG_BLK_DEV_RAM extern int rd_doload; /* 1 = load ramdisk, 0 = don't load */ @@ -48,13 +49,31 @@ extern int rd_image_start; /* starting block # of image */ #endif extern int root_mountflags; +extern int _text, _etext, _edata, _end; + +/* + * This is set up by the setup-routine at boot-time + */ +#define PARAM ((unsigned char *)empty_zero_page) + +#define MOUNT_ROOT_RDONLY (*(unsigned long *) (PARAM+0x000)) +#define RAMDISK_FLAGS (*(unsigned long *) (PARAM+0x004)) +#define ORIG_ROOT_DEV (*(unsigned long *) (PARAM+0x008)) +#define LOADER_TYPE (*(unsigned long *) (PARAM+0x00c)) +#define INITRD_START (*(unsigned long *) (PARAM+0x010)) +#define INITRD_SIZE (*(unsigned long *) (PARAM+0x014)) +#define MEMORY_END (*(unsigned long *) (PARAM+0x018)) +/* ... */ +#define COMMAND_LINE ((char *) (PARAM+0x100)) +#define COMMAND_LINE_SIZE 256 + +#define RAMDISK_IMAGE_START_MASK 0x07FF +#define RAMDISK_PROMPT_FLAG 0x8000 +#define RAMDISK_LOAD_FLAG 0x4000 -#define COMMAND_LINE_SIZE 1024 static char command_line[COMMAND_LINE_SIZE] = { 0, }; char saved_command_line[COMMAND_LINE_SIZE]; -extern unsigned char *root_fs_image; - struct resource standard_io_resources[] = { { "dma1", 0x00, 0x1f }, { "pic1", 0x20, 0x3f }, @@ -68,7 +87,6 @@ struct resource standard_io_resources[] = { #define STANDARD_IO_RESOURCES (sizeof(standard_io_resources)/sizeof(struct resource)) - /* System RAM - interrupted by the 640kB-1M hole */ #define code_resource (ram_resources[3]) #define data_resource (ram_resources[4]) @@ -87,17 +105,26 @@ static struct resource rom_resources[MAXROMS] = { { "Video ROM", 0xc0000, 0xc7fff } }; - void __init setup_arch(char **cmdline_p, unsigned long * memory_start_p, unsigned long * memory_end_p) { - *cmdline_p = command_line; - *memory_start_p = (unsigned long) &_end; - *memory_end_p = 0x8c400000; /* For my board. */ - ram_resources[1].end = *memory_end_p-1; + unsigned long memory_start, memory_end; + + ROOT_DEV = to_kdev_t(ORIG_ROOT_DEV); + +#ifdef CONFIG_BLK_DEV_RAM + rd_image_start = RAMDISK_FLAGS & RAMDISK_IMAGE_START_MASK; + rd_prompt = ((RAMDISK_FLAGS & RAMDISK_PROMPT_FLAG) != 0); + rd_doload = ((RAMDISK_FLAGS & RAMDISK_LOAD_FLAG) != 0); +#endif + if (!MOUNT_ROOT_RDONLY) + root_mountflags &= ~MS_RDONLY; + + memory_start = (unsigned long) &_end; + memory_end = MEMORY_END; - init_mm.start_code = (unsigned long)&_stext; + init_mm.start_code = (unsigned long)&_text; init_mm.end_code = (unsigned long) &_etext; init_mm.end_data = (unsigned long) &_edata; init_mm.brk = (unsigned long) &_end; @@ -107,51 +134,25 @@ void __init setup_arch(char **cmdline_p, data_resource.start = virt_to_bus(&_etext); data_resource.end = virt_to_bus(&_edata)-1; - ROOT_DEV = MKDEV(FLOPPY_MAJOR, 0); + /* Save unparsed command line copy for /proc/cmdline */ + memcpy(saved_command_line, COMMAND_LINE, COMMAND_LINE_SIZE); + saved_command_line[COMMAND_LINE_SIZE-1] = '\0'; - initrd_below_start_ok = 1; - initrd_start = (long)&root_fs_image; - initrd_end = (long)&__bss_start; - mount_initrd = 1; + memcpy(command_line, COMMAND_LINE, COMMAND_LINE_SIZE); + command_line[COMMAND_LINE_SIZE-1] = '\0'; + /* Not support "mem=XXX[kKmM]" command line option. */ + *cmdline_p = command_line; -#if 0 - /* Request the standard RAM and ROM resources - they eat up PCI memory space */ - request_resource(&iomem_resource, ram_resources+0); - request_resource(&iomem_resource, ram_resources+1); - request_resource(&iomem_resource, ram_resources+2); - request_resource(ram_resources+1, &code_resource); - request_resource(ram_resources+1, &data_resource); -#endif - -#if 0 - for (i = 0; i < STANDARD_IO_RESOURCES; i++) - request_resource(&ioport_resource, standard_io_resources+i); -#endif - -#if 0 - rd_image_start = (long)root_fs_image; - rd_prompt = 0; - rd_doload = 1; -#endif + memory_end &= PAGE_MASK; + ram_resources[1].end = memory_end-1; -#if 0 - ROOT_DEV = to_kdev_t(ORIG_ROOT_DEV); - -#ifdef CONFIG_BLK_DEV_RAM - rd_image_start = RAMDISK_FLAGS & RAMDISK_IMAGE_START_MASK; - rd_prompt = ((RAMDISK_FLAGS & RAMDISK_PROMPT_FLAG) != 0); - rd_doload = ((RAMDISK_FLAGS & RAMDISK_LOAD_FLAG) != 0); -#endif - - if (!MOUNT_ROOT_RDONLY) - root_mountflags &= ~MS_RDONLY; -#endif + *memory_start_p = memory_start; + *memory_end_p = memory_end; #ifdef CONFIG_BLK_DEV_INITRD -#if 0 if (LOADER_TYPE) { - initrd_start = INITRD_START ? INITRD_START + PAGE_OFFSET : 0; + initrd_start = INITRD_START ? INITRD_START : 0; initrd_end = initrd_start+INITRD_SIZE; if (initrd_end > memory_end) { printk("initrd extends beyond end of memory " @@ -162,6 +163,29 @@ void __init setup_arch(char **cmdline_p, } #endif +#if 0 + /* + * Request the standard RAM and ROM resources - + * they eat up PCI memory space + */ + request_resource(&iomem_resource, ram_resources+0); + request_resource(&iomem_resource, ram_resources+1); + request_resource(&iomem_resource, ram_resources+2); + request_resource(ram_resources+1, &code_resource); + request_resource(ram_resources+1, &data_resource); + probe_roms(); + + /* request I/O space for devices used on all i[345]86 PCs */ + for (i = 0; i < STANDARD_IO_RESOURCES; i++) + request_resource(&ioport_resource, standard_io_resources+i); +#endif + +#ifdef CONFIG_VT +#if defined(CONFIG_VGA_CONSOLE) + conswitchp = &vga_con; +#elif defined(CONFIG_DUMMY_CONSOLE) + conswitchp = &dummy_con; +#endif #endif } @@ -173,12 +197,12 @@ int get_cpuinfo(char *buffer) { char *p = buffer; -#ifdef CONFIG_CPU_SH3 - p += sprintf(p,"cpu family\t: SH3\n" +#if defined(__sh3__) + p += sprintf(p,"cpu family\t: SH-3\n" "cache size\t: 8K-byte\n"); -#elif CONFIG_CPU_SH4 - p += sprintf(p,"cpu family\t: SH4\n" - "cache size\t: ??K-byte\n"); +#elif defined(__SH4__) + p += sprintf(p,"cpu family\t: SH-4\n" + "cache size\t: 8K-byte/16K-byte\n"); #endif p += sprintf(p, "bogomips\t: %lu.%02lu\n\n", (loops_per_sec+2500)/500000, diff --git a/arch/sh/kernel/signal.c b/arch/sh/kernel/signal.c index 66fa36c85..6abc7952e 100644 --- a/arch/sh/kernel/signal.c +++ b/arch/sh/kernel/signal.c @@ -1,4 +1,5 @@ -/* +/* $Id$ + * * linux/arch/sh/kernel/signal.c * * Copyright (C) 1991, 1992 Linus Torvalds @@ -9,8 +10,6 @@ * */ -#include <linux/config.h> - #include <linux/sched.h> #include <linux/mm.h> #include <linux/smp.h> @@ -24,6 +23,7 @@ #include <linux/stddef.h> #include <asm/ucontext.h> #include <asm/uaccess.h> +#include <asm/pgtable.h> #define DEBUG_SIG 0 @@ -50,7 +50,7 @@ sys_sigsuspend(old_sigset_t mask, recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); - regs.u_regs[0] = -EINTR; + regs.regs[0] = -EINTR; while (1) { current->state = TASK_INTERRUPTIBLE; schedule(); @@ -80,7 +80,7 @@ sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize, recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); - regs.u_regs[0] = -EINTR; + regs.regs[0] = -EINTR; while (1) { current->state = TASK_INTERRUPTIBLE; schedule(); @@ -126,7 +126,7 @@ sys_sigaltstack(const stack_t *uss, stack_t *uoss, unsigned long r6, unsigned long r7, struct pt_regs regs) { - return do_sigaltstack(uss, uoss, regs.u_regs[UREG_SP]); + return do_sigaltstack(uss, uoss, regs.sp); } @@ -137,7 +137,7 @@ sys_sigaltstack(const stack_t *uss, stack_t *uoss, struct sigframe { struct sigcontext sc; - /* FPU should come here: SH-3 has no FPU */ + /* FPU data should come here: SH-3 has no FPU */ unsigned long extramask[_NSIG_WORDS-1]; char retcode[4]; }; @@ -158,22 +158,22 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc, int *r0_p) { unsigned int err = 0; -#define COPY(x) err |= __get_user(regs->x, &sc->x) - COPY(u_regs[1]); - COPY(u_regs[2]); COPY(u_regs[3]); - COPY(u_regs[4]); COPY(u_regs[5]); - COPY(u_regs[6]); COPY(u_regs[7]); - COPY(u_regs[8]); COPY(u_regs[9]); - COPY(u_regs[10]); COPY(u_regs[11]); - COPY(u_regs[12]); COPY(u_regs[13]); - COPY(u_regs[14]); COPY(u_regs[15]); - COPY(gbr); COPY(mach); - COPY(macl); COPY(pr); - COPY(sr); COPY(pc); +#define COPY(x) err |= __get_user(regs->x, &sc->sc_##x) + COPY(regs[1]); + COPY(regs[2]); COPY(regs[3]); + COPY(regs[4]); COPY(regs[5]); + COPY(regs[6]); COPY(regs[7]); + COPY(regs[8]); COPY(regs[9]); + COPY(regs[10]); COPY(regs[11]); + COPY(regs[12]); COPY(regs[13]); + COPY(regs[14]); COPY(sp); + COPY(gbr); COPY(mach); + COPY(macl); COPY(pr); + COPY(sr); COPY(pc); #undef COPY regs->syscall_nr = -1; /* disable syscall checks */ - err |= __get_user(*r0_p, &sc->u_regs[0]); + err |= __get_user(*r0_p, &sc->sc_regs[0]); return err; } @@ -182,7 +182,7 @@ asmlinkage int sys_sigreturn(unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7, struct pt_regs regs) { - struct sigframe *frame = (struct sigframe *)regs.u_regs[UREG_SP]; + struct sigframe *frame = (struct sigframe *)regs.sp; sigset_t set; int r0; @@ -213,7 +213,7 @@ asmlinkage int sys_rt_sigreturn(unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7, struct pt_regs regs) { - struct rt_sigframe *frame = (struct rt_sigframe *)regs.u_regs[UREG_SP]; + struct rt_sigframe *frame = (struct rt_sigframe *)regs.sp; sigset_t set; stack_t st; int r0; @@ -236,7 +236,7 @@ asmlinkage int sys_rt_sigreturn(unsigned long r4, unsigned long r5, goto badframe; /* It is more difficult to avoid calling this function than to call it and ignore errors. */ - do_sigaltstack(&st, NULL, regs.u_regs[UREG_SP]); + do_sigaltstack(&st, NULL, regs.sp); return r0; @@ -255,18 +255,18 @@ setup_sigcontext(struct sigcontext *sc, struct pt_regs *regs, { int err = 0; -#define COPY(x) err |= __put_user(regs->x, &sc->x) - COPY(u_regs[0]); COPY(u_regs[1]); - COPY(u_regs[2]); COPY(u_regs[3]); - COPY(u_regs[4]); COPY(u_regs[5]); - COPY(u_regs[6]); COPY(u_regs[7]); - COPY(u_regs[8]); COPY(u_regs[9]); - COPY(u_regs[10]); COPY(u_regs[11]); - COPY(u_regs[12]); COPY(u_regs[13]); - COPY(u_regs[14]); COPY(u_regs[15]); - COPY(gbr); COPY(mach); - COPY(macl); COPY(pr); - COPY(sr); COPY(pc); +#define COPY(x) err |= __put_user(regs->x, &sc->sc_##x) + COPY(regs[0]); COPY(regs[1]); + COPY(regs[2]); COPY(regs[3]); + COPY(regs[4]); COPY(regs[5]); + COPY(regs[6]); COPY(regs[7]); + COPY(regs[8]); COPY(regs[9]); + COPY(regs[10]); COPY(regs[11]); + COPY(regs[12]); COPY(regs[13]); + COPY(regs[14]); COPY(sp); + COPY(gbr); COPY(mach); + COPY(macl); COPY(pr); + COPY(sr); COPY(pc); #undef COPY /* non-iBCS2 extensions.. */ @@ -294,7 +294,7 @@ static void setup_frame(int sig, struct k_sigaction *ka, int err = 0; int signal; - frame = get_sigframe(ka, regs->u_regs[UREG_SP], sizeof(*frame)); + frame = get_sigframe(ka, regs->sp, sizeof(*frame)); if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) goto give_sigsegv; @@ -318,8 +318,8 @@ static void setup_frame(int sig, struct k_sigaction *ka, regs->pr = (unsigned long) ka->sa.sa_restorer; } else { /* This is ; mov #__NR_sigreturn,r0 ; trapa #0 */ -#ifdef CONFIG_LITTLE_ENDIAN - unsigned long code = 0x00c300e0 | (__NR_sigreturn << 8); +#ifdef __LITTLE_ENDIAN__ + unsigned long code = 0xc300e000 | (__NR_sigreturn); #else unsigned long code = 0xe000c300 | (__NR_sigreturn << 16); #endif @@ -332,8 +332,8 @@ static void setup_frame(int sig, struct k_sigaction *ka, goto give_sigsegv; /* Set up registers for signal handler */ - regs->u_regs[UREG_SP] = (unsigned long) frame; - regs->u_regs[4] = signal; /* Arg for signal handler */ + regs->sp = (unsigned long) frame; + regs->regs[4] = signal; /* Arg for signal handler */ regs->pc = (unsigned long) ka->sa.sa_handler; set_fs(USER_DS); @@ -343,6 +343,7 @@ static void setup_frame(int sig, struct k_sigaction *ka, current->comm, current->pid, frame, regs->pc, regs->pr); #endif + flush_icache_range(regs->pr, regs->pr+4); return; give_sigsegv: @@ -358,7 +359,7 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, int err = 0; int signal; - frame = get_sigframe(ka, regs->u_regs[UREG_SP], sizeof(*frame)); + frame = get_sigframe(ka, regs->sp, sizeof(*frame)); if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) goto give_sigsegv; @@ -376,9 +377,9 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, /* Create the ucontext. */ err |= __put_user(0, &frame->uc.uc_flags); err |= __put_user(0, &frame->uc.uc_link); - err |= __put_user(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp); - err |= __put_user(sas_ss_flags(regs->u_regs[UREG_SP]), - &frame->uc.uc_stack.ss_flags); + err |= __put_user((void *)current->sas_ss_sp, + &frame->uc.uc_stack.ss_sp); + err |= __put_user(sas_ss_flags(regs->sp), &frame->uc.uc_stack.ss_flags); err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); err |= setup_sigcontext(&frame->uc.uc_mcontext, regs, set->sig[0]); @@ -390,8 +391,8 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, regs->pr = (unsigned long) ka->sa.sa_restorer; } else { /* This is ; mov #__NR_sigreturn,r0 ; trapa #0 */ -#ifdef CONFIG_LITTLE_ENDIAN - unsigned long code = 0x00c300e0 | (__NR_sigreturn << 8); +#ifdef __LITTLE_ENDIAN__ + unsigned long code = 0xc300e000 | (__NR_sigreturn); #else unsigned long code = 0xe000c300 | (__NR_sigreturn << 16); #endif @@ -404,8 +405,8 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, goto give_sigsegv; /* Set up registers for signal handler */ - regs->u_regs[UREG_SP] = (unsigned long) frame; - regs->u_regs[4] = signal; /* Arg for signal handler */ + regs->sp = (unsigned long) frame; + regs->regs[4] = signal; /* Arg for signal handler */ regs->pc = (unsigned long) ka->sa.sa_handler; set_fs(USER_DS); @@ -415,6 +416,7 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, current->comm, current->pid, frame, regs->pc, regs->pr); #endif + flush_icache_range(regs->pr, regs->pr+4); return; give_sigsegv: @@ -434,19 +436,19 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, /* Are we from a system call? */ if (regs->syscall_nr >= 0) { /* If so, check system call restarting.. */ - switch (regs->u_regs[0]) { + switch (regs->regs[0]) { case -ERESTARTNOHAND: - regs->u_regs[0] = -EINTR; + regs->regs[0] = -EINTR; break; case -ERESTARTSYS: if (!(ka->sa.sa_flags & SA_RESTART)) { - regs->u_regs[0] = -EINTR; + regs->regs[0] = -EINTR; break; } /* fallthrough */ case -ERESTARTNOINTR: - regs->u_regs[0] = regs->syscall_nr; + regs->regs[0] = regs->syscall_nr; regs->pc -= 2; } } @@ -577,7 +579,6 @@ int do_signal(struct pt_regs *regs, sigset_t *oldset) /* NOTREACHED */ } } - /* Whee! Actually deliver the signal. */ handle_signal(signr, ka, &info, oldset, regs); return 1; @@ -586,10 +587,10 @@ int do_signal(struct pt_regs *regs, sigset_t *oldset) /* Did we come from a system call? */ if (regs->syscall_nr >= 0) { /* Restart the system call - no handlers present */ - if (regs->u_regs[0] == -ERESTARTNOHAND || - regs->u_regs[0] == -ERESTARTSYS || - regs->u_regs[0] == -ERESTARTNOINTR) { - regs->u_regs[0] = regs->syscall_nr; + if (regs->regs[0] == -ERESTARTNOHAND || + regs->regs[0] == -ERESTARTSYS || + regs->regs[0] == -ERESTARTNOINTR) { + regs->regs[0] = regs->syscall_nr; regs->pc -= 2; } } diff --git a/arch/sh/kernel/sys_sh.c b/arch/sh/kernel/sys_sh.c index 6999cff81..a485f10a3 100644 --- a/arch/sh/kernel/sys_sh.c +++ b/arch/sh/kernel/sys_sh.c @@ -1,9 +1,11 @@ /* - * linux/arch/i386/kernel/sys_i386.c + * linux/arch/sh/kernel/sys_sh.c * * This file contains various random system calls that - * have a non-standard calling sequence on the Linux/i386 + * have a non-standard calling sequence on the Linux/SuperH * platform. + * + * Taken from i386 version. */ #include <linux/errno.h> @@ -41,66 +43,32 @@ asmlinkage int sys_pipe(unsigned long * fildes) return error; } -/* - * Perform the select(nd, in, out, ex, tv) and mmap() system - * calls. Linux/i386 didn't use to be able to handle more than - * 4 system call parameters, so these system calls used a memory - * block for parameter passing.. - */ - -struct mmap_arg_struct { - unsigned long addr; - unsigned long len; - unsigned long prot; - unsigned long flags; - unsigned long fd; - unsigned long offset; -}; - -asmlinkage int old_mmap(struct mmap_arg_struct *arg) +asmlinkage unsigned long +sys_mmap(int fd, unsigned long addr, + unsigned long len, unsigned long prot, + unsigned long flags, unsigned long off) { int error = -EFAULT; - struct file * file = NULL; - struct mmap_arg_struct a; - - if (copy_from_user(&a, arg, sizeof(a))) - return -EFAULT; + struct file *file = NULL; down(¤t->mm->mmap_sem); lock_kernel(); - if (!(a.flags & MAP_ANONYMOUS)) { + if (!(flags & MAP_ANONYMOUS)) { error = -EBADF; - file = fget(a.fd); + file = fget(fd); if (!file) goto out; } - a.flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); - error = do_mmap(file, a.addr, a.len, a.prot, a.flags, a.offset); + error = do_mmap(file, addr, len, prot, flags, off); if (file) fput(file); out: unlock_kernel(); up(¤t->mm->mmap_sem); - return error; -} - -extern asmlinkage int sys_select(int, fd_set *, fd_set *, fd_set *, struct timeval *); - -struct sel_arg_struct { - unsigned long n; - fd_set *inp, *outp, *exp; - struct timeval *tvp; -}; - -asmlinkage int old_select(struct sel_arg_struct *arg) -{ - struct sel_arg_struct a; - if (copy_from_user(&a, arg, sizeof(a))) - return -EFAULT; - /* sys_select() does the appropriate kernel locking */ - return sys_select(a.n, a.inp, a.outp, a.exp, a.tvp); + return error; } /* @@ -198,9 +166,6 @@ asmlinkage int sys_ipc (uint call, int first, int second, return -EINVAL; } -/* - * Old cruft - */ asmlinkage int sys_uname(struct old_utsname * name) { int err; @@ -212,35 +177,6 @@ asmlinkage int sys_uname(struct old_utsname * name) return err?-EFAULT:0; } -asmlinkage int sys_olduname(struct oldold_utsname * name) -{ - int error; - - if (!name) - return -EFAULT; - if (!access_ok(VERIFY_WRITE,name,sizeof(struct oldold_utsname))) - return -EFAULT; - - down(&uts_sem); - - error = __copy_to_user(&name->sysname,&system_utsname.sysname,__OLD_UTS_LEN); - error |= __put_user(0,name->sysname+__OLD_UTS_LEN); - error |= __copy_to_user(&name->nodename,&system_utsname.nodename,__OLD_UTS_LEN); - error |= __put_user(0,name->nodename+__OLD_UTS_LEN); - error |= __copy_to_user(&name->release,&system_utsname.release,__OLD_UTS_LEN); - error |= __put_user(0,name->release+__OLD_UTS_LEN); - error |= __copy_to_user(&name->version,&system_utsname.version,__OLD_UTS_LEN); - error |= __put_user(0,name->version+__OLD_UTS_LEN); - error |= __copy_to_user(&name->machine,&system_utsname.machine,__OLD_UTS_LEN); - error |= __put_user(0,name->machine+__OLD_UTS_LEN); - - up(&uts_sem); - - error = error ? -EFAULT : 0; - - return error; -} - asmlinkage int sys_pause(void) { current->state = TASK_INTERRUPTIBLE; diff --git a/arch/sh/kernel/test-img.c b/arch/sh/kernel/test-img.c deleted file mode 100644 index daade9f6d..000000000 --- a/arch/sh/kernel/test-img.c +++ /dev/null @@ -1,69 +0,0 @@ -unsigned char root_fs_image[] -__attribute__((__section__(".data.disk_image"))) -= { -0x1f,0x8b,0x08,0x08,0x5d,0xd5,0xc7,0x37,0x00,0x03,0x72,0x2e,0x62,0x69,0x6e,0x00, -0xed,0xdc,0x3f,0x6c,0x1b,0x55,0x1c,0xc0,0xf1,0xdf,0xf9,0xdc,0x04,0x27,0x69,0xb1, -0x93,0x14,0x10,0x48,0x91,0xd3,0x02,0x4d,0x8a,0xb8,0xd4,0x21,0x8a,0x09,0x02,0x02, -0xb5,0x4a,0xab,0x52,0x65,0x69,0x11,0x03,0x42,0xc2,0xb1,0x8f,0xc4,0x92,0xe3,0x03, -0x9f,0x8d,0xca,0x14,0xd8,0x88,0x2a,0xa6,0x0e,0x88,0xa9,0x20,0xb1,0x87,0x8d,0xa5, -0x5b,0x86,0xcc,0x90,0x78,0x77,0xd4,0x60,0x75,0xa9,0x40,0xe2,0xdf,0xd0,0x42,0x78, -0x77,0xef,0x9c,0x38,0x24,0x72,0x49,0x20,0xc9,0x70,0xdf,0x8f,0xf2,0xf3,0xd9,0x77, -0xbf,0xf3,0xbb,0x67,0xbf,0xdf,0xf9,0x4f,0xf4,0x2c,0x02,0x20,0xac,0xe2,0x2a,0x5e, -0x53,0x61,0xaa,0x18,0x0e,0xd6,0x19,0xad,0x09,0x49,0x1d,0x5e,0x5e,0x7d,0x75,0x39, -0xfd,0x6c,0x6d,0x39,0x6d,0x48,0xbf,0x5c,0xfd,0xc9,0xf0,0xf3,0x56,0xd5,0x3a,0x99, -0xba,0xf7,0xd0,0x76,0x8a,0x53,0x5f,0xc4,0xdf,0xcd,0x24,0x56,0x6e,0x9e,0x59,0xb9, -0x30,0x3e,0x73,0x3b,0xf7,0x3f,0x76,0x01,0xc0,0x3e,0x79,0x75,0x1f,0x55,0x71,0x4c, -0x74,0xfd,0x47,0x8f,0xf6,0x70,0x00,0x1c,0xa2,0x8d,0x8d,0x49,0x6f,0xf1,0xc9,0x06, -0x00,0x00,0x08,0x8d,0xe6,0xfb,0x00,0xef,0x73,0x7c,0x33,0x0e,0xf3,0xfd,0xc7,0xbd, -0xd7,0xc5,0xff,0xd0,0x31,0x5a,0x5b,0x4e,0xf7,0x05,0xa1,0xb7,0x1c,0x93,0x48,0x4b, -0x5e,0xe7,0x61,0x1e,0x14,0x80,0x50,0xf0,0xcf,0x3f,0xe7,0x76,0x3b,0xff,0x45,0xe4, -0x89,0x96,0xbc,0x47,0x54,0xc4,0x54,0x74,0xa9,0xe8,0x56,0xd1,0xa3,0xe2,0xb8,0x8a, -0x13,0x2a,0x1e,0x15,0xfd,0xfd,0x68,0x42,0x45,0xaf,0x8a,0xbe,0xbd,0xb6,0xaf,0xce, -0x7f,0x7f,0xaa,0x76,0xef,0x07,0xd1,0x6c,0xbf,0xf5,0xfc,0xd7,0xbf,0xf7,0xae,0x6d, -0x32,0xda,0x6c,0x6b,0xb6,0x7f,0x56,0x9d,0x77,0x4f,0x05,0xb1,0x5b,0xfb,0x27,0x0f, -0xa8,0xfd,0x6f,0x06,0xf5,0xf2,0xfe,0x8e,0xfe,0xff,0x63,0xaf,0xff,0xf0,0xc5,0x54, -0xdb,0xfe,0x7f,0x7a,0xeb,0xf2,0x15,0x53,0xe4,0xe6,0xaa,0x7e,0xed,0x19,0x0b,0xda, -0xbf,0x75,0xd9,0xd8,0xd6,0xff,0xc7,0xf6,0xdf,0x7c,0xdb,0xf6,0x37,0xbe,0xd6,0x63, -0x6a,0xe7,0xe3,0xbf,0x7d,0x2f,0xcb,0x1a,0x29,0x16,0x4a,0xd5,0xeb,0xe5,0x7d,0x7c, -0x73,0xde,0xae,0x7d,0xaf,0x8f,0x3d,0x2a,0xc3,0xda,0xbc,0x1e,0x51,0x6d,0xe9,0x31, -0xde,0xaf,0x8e,0xac,0xe8,0xb8,0x95,0xe7,0xde,0x77,0xaa,0xa5,0xbc,0x1e,0xf3,0x3d, -0x62,0x4a,0xde,0xfe,0xc8,0x1f,0xfb,0x3d,0xea,0x49,0x71,0xa7,0x0b,0x25,0x6f,0xfc, -0xdf,0x36,0x3b,0x65,0xdf,0x07,0x08,0xe0,0x48,0xe8,0xd7,0xb2,0xad,0xfa,0xff,0xd5, -0xd4,0xf5,0x0f,0x20,0x24,0xf8,0xa7,0x1f,0x10,0x5e,0xd4,0x3f,0x10,0x5e,0xd4,0x3f, -0x10,0x5e,0xd4,0x3f,0x10,0x5e,0xd4,0x3f,0x10,0x5e,0xd4,0x3f,0x10,0x5e,0xd4,0x3f, -0x10,0x5e,0xd4,0x3f,0x10,0x5e,0xd4,0x3f,0x10,0x5e,0xd4,0x3f,0x10,0x5e,0xd4,0x3f, -0x10,0x4a,0x7a,0x4e,0xcf,0xce,0xf9,0x3f,0xde,0xbc,0xb6,0xbb,0x66,0xa7,0xe4,0x9c, -0x92,0xeb,0x14,0xed,0xa3,0x3d,0x48,0x00,0x07,0x42,0xcf,0xe3,0xdb,0x59,0xff,0xde, -0x7c,0xd6,0xbb,0x66,0x54,0x0a,0xa5,0x42,0xe5,0x68,0x8f,0x10,0xc0,0x41,0x99,0xbf, -0x70,0xe5,0x0d,0x23,0xd2,0x32,0x43,0x38,0x22,0x67,0xc5,0x9f,0x32,0x1c,0xff,0x4a, -0x2d,0xc7,0xd4,0xd5,0x75,0x7f,0xfd,0x98,0x24,0xd5,0xb6,0x21,0x89,0xf9,0x53,0xe1, -0x83,0x1d,0xe2,0x41,0x18,0xd3,0x3a,0xfc,0x9f,0x11,0x34,0x74,0x78,0xb7,0x07,0x83, -0xd8,0xd4,0xe1,0x27,0x67,0xd6,0x8d,0x46,0x7c,0xa4,0x51,0x8f,0xd6,0x3a,0x4a,0xbf, -0x2c,0xc9,0x7b,0xa7,0x3f,0x33,0x16,0xcc,0x5a,0xb4,0x61,0xd6,0xa3,0x4b,0xe2,0xdc, -0x91,0xee,0xd2,0xef,0x22,0x89,0xa7,0x55,0xbc,0x38,0xd2,0x98,0xff,0xb9,0x1e,0xf1, -0xb2,0xa6,0xcd,0xf3,0x89,0x85,0xce,0x75,0xa3,0xf6,0x78,0xe3,0xa4,0x97,0x27,0xb1, -0xc5,0xbf,0x24,0x76,0x6a,0x68,0xa1,0x7b,0xa5,0x6f,0x4d,0x3e,0x34,0x52,0xe9,0x1b, -0x0f,0xf2,0xa7,0x7f,0x34,0xea,0xcf,0x2c,0xc9,0xe2,0x1f,0x6b,0x6a,0xfb,0xf7,0x27, -0xd6,0x0d,0xab,0xd7,0xbe,0xb3,0x26,0x03,0x89,0x86,0x0c,0xf4,0xd6,0x33,0x03,0x7d, -0x4b,0xf2,0x43,0xd7,0xba,0x21,0xb1,0x5a,0xac,0x71,0xdc,0xbb,0x17,0x2f,0x4f,0xed, -0x7b,0xe6,0xc6,0x83,0xc5,0xdf,0xbc,0xf5,0xaa,0xcd,0x97,0xe5,0x9d,0xcf,0xe7,0x55, -0xbf,0x2a,0xf2,0xdd,0x93,0x1b,0xea,0xf6,0xb5,0x6b,0xb3,0x05,0x37,0xa9,0xfe,0xae, -0x56,0x3f,0xb0,0xcb,0x97,0x06,0xbd,0xe9,0xda,0x32,0x39,0xd9,0x25,0xae,0x33,0x67, -0x57,0x66,0x0b,0xa5,0x99,0x64,0xb5,0x54,0x75,0xab,0xd9,0xa2,0x65,0x59,0xde,0xc6, -0x4b,0x76,0xb1,0xe8,0x24,0xdf,0x76,0xca,0xc5,0xbc,0x97,0x7c,0x31,0x93,0x79,0x29, -0x39,0x74,0x71,0xea,0xad,0xe1,0xa4,0x3d,0x93,0x73,0x9f,0x1f,0xb5,0x26,0x52,0xd6, -0xf8,0x78,0x32,0x35,0x31,0x31,0x71,0xee,0x85,0xd4,0x58,0x72,0xc8,0x5f,0x9d,0xb2, -0x52,0xd6,0x68,0xb2,0x6c,0x17,0xed,0xac,0x6b,0x0f,0x8b,0x58,0xee,0xc7,0x73,0x95, -0xec,0xb4,0x5a,0x56,0xca,0x7a,0x39,0xdb,0xbc,0x56,0xb1,0xaf,0x57,0xc4,0x2a,0x3b, -0xf9,0x6c,0x25,0x2b,0x96,0xbe,0xcc,0x55,0x9c,0xb2,0xab,0x6e,0xe8,0xc5,0xb4,0xab, -0x2e,0x72,0xce,0xdc,0x9c,0x5d,0xda,0xd3,0xe9,0xfb,0xa9,0xe0,0xf9,0xeb,0xf0,0xfb, -0x2f,0xe2,0xc5,0xb7,0x2d,0xdb,0x9b,0x9f,0x14,0x07,0x83,0xbc,0x88,0x7e,0x9e,0x0c, -0x15,0xf2,0xea,0x2e,0x79,0xc3,0x41,0x9e,0xa9,0xc7,0x81,0xd1,0x3a,0x16,0x64,0x6b, -0x1c,0xc9,0xc8,0xd6,0xb8,0x69,0x9b,0x37,0xfe,0x2f,0xf3,0x5e,0x11,0xfd,0x93,0x0d, -0x0f,0x6b,0xf7,0xbc,0x6c,0x9b,0x1e,0xef,0xe7,0xa5,0x77,0xc9,0x4b,0xe8,0xfb,0xda, -0x5c,0xfd,0xa5,0xba,0x78,0x73,0x97,0x3c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x87,0xe8,0x6f,0x20,0x01,0xec,0xc5,0x00,0x00,0x01,0x00, -}; diff --git a/arch/sh/kernel/time.c b/arch/sh/kernel/time.c index 6f4598a7e..efe436336 100644 --- a/arch/sh/kernel/time.c +++ b/arch/sh/kernel/time.c @@ -1,12 +1,15 @@ -/* +/* $Id$ + * * linux/arch/sh/kernel/time.c * - * Copyright (C) 1999 Niibe Yutaka + * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka * * Some code taken from i386 version. * Copyright (C) 1991, 1992, 1995 Linus Torvalds */ +#include <linux/config.h> + #include <linux/errno.h> #include <linux/sched.h> #include <linux/kernel.h> @@ -28,6 +31,11 @@ #include <linux/timex.h> #include <linux/irq.h> +#define TMU_TOCR_INIT 0x00 +#define TMU0_TCR_INIT 0x0020 +#define TMU_TSTR_INIT 1 + +#if defined(__sh3__) #define TMU_TOCR 0xfffffe90 /* Byte access */ #define TMU_TSTR 0xfffffe92 /* Byte access */ @@ -35,13 +43,63 @@ #define TMU0_TCNT 0xfffffe98 /* Long access */ #define TMU0_TCR 0xfffffe9c /* Word access */ -#define TMU_TOCR_INIT 0x00 -#define TMU0_TCR_INIT 0x0020 -#define TMU_TSTR_INIT 1 - -#define CLOCK_MHZ (60/4) #define INTERVAL 37500 /* (1000000*CLOCK_MHZ/HZ/2) ??? */ +/* SH-3 RTC */ +#define R64CNT 0xfffffec0 +#define RSECCNT 0xfffffec2 +#define RMINCNT 0xfffffec4 +#define RHRCNT 0xfffffec6 +#define RWKCNT 0xfffffec8 +#define RDAYCNT 0xfffffeca +#define RMONCNT 0xfffffecc +#define RYRCNT 0xfffffece +#define RSECAR 0xfffffed0 +#define RMINAR 0xfffffed2 +#define RHRAR 0xfffffed4 +#define RWKAR 0xfffffed6 +#define RDAYAR 0xfffffed8 +#define RMONAR 0xfffffeda +#define RCR1 0xfffffedc +#define RCR2 0xfffffede + +#elif defined(__SH4__) +#define TMU_TOCR 0xffd80000 /* Byte access */ +#define TMU_TSTR 0xffd80004 /* Byte access */ + +#define TMU0_TCOR 0xffd80008 /* Long access */ +#define TMU0_TCNT 0xffd8000c /* Long access */ +#define TMU0_TCR 0xffd80010 /* Word access */ + +#define INTERVAL 83333 + +/* SH-4 RTC */ +#define R64CNT 0xffc80000 +#define RSECCNT 0xffc80004 +#define RMINCNT 0xffc80008 +#define RHRCNT 0xffc8000c +#define RWKCNT 0xffc80010 +#define RDAYCNT 0xffc80014 +#define RMONCNT 0xffc80018 +#define RYRCNT 0xffc8001c /* 16bit */ +#define RSECAR 0xffc80020 +#define RMINAR 0xffc80024 +#define RHRAR 0xffc80028 +#define RWKAR 0xffc8002c +#define RDAYAR 0xffc80030 +#define RMONAR 0xffc80034 +#define RCR1 0xffc80038 +#define RCR2 0xffc8003c +#endif + +#ifndef BCD_TO_BIN +#define BCD_TO_BIN(val) ((val)=((val)&15) + ((val)>>4)*10) +#endif + +#ifndef BIN_TO_BCD +#define BIN_TO_BCD(val) ((val)=(((val)/10)<<4) + (val)%10) +#endif + extern rwlock_t xtime_lock; #define TICK_SIZE tick @@ -82,14 +140,48 @@ void do_settimeofday(struct timeval *tv) write_unlock_irq(&xtime_lock); } -/* - */ static int set_rtc_time(unsigned long nowtime) { -/* XXX should be implemented XXXXXXXXXX */ - int retval = -1; +#ifdef CONFIG_SH_CPU_RTC + int retval = 0; + int real_seconds, real_minutes, cmos_minutes; + + ctrl_outb(2, RCR2); /* reset pre-scaler & stop RTC */ + + cmos_minutes = ctrl_inb(RMINCNT); + BCD_TO_BIN(cmos_minutes); + + /* + * since we're only adjusting minutes and seconds, + * don't interfere with hour overflow. This avoids + * messing with unknown time zones but requires your + * RTC not to be off by more than 15 minutes + */ + real_seconds = nowtime % 60; + real_minutes = nowtime / 60; + if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1) + real_minutes += 30; /* correct for half hour time zone */ + real_minutes %= 60; + + if (abs(real_minutes - cmos_minutes) < 30) { + BIN_TO_BCD(real_seconds); + BIN_TO_BCD(real_minutes); + ctrl_outb(real_seconds, RSECCNT); + ctrl_outb(real_minutes, RMINCNT); + } else { + printk(KERN_WARNING + "set_rtc_time: can't update from %d to %d\n", + cmos_minutes, real_minutes); + retval = -1; + } + + ctrl_outb(2, RCR2); /* start RTC */ return retval; +#else + /* XXX should support other clock devices? */ + return -1; +#endif } /* last time the RTC clock got updated */ @@ -131,14 +223,12 @@ static inline void do_timer_interrupt(int irq, void *dev_id, struct pt_regs *reg */ static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - unsigned long __dummy; + unsigned long timer_status; /* Clear UNF bit */ - asm volatile("mov.w %1,%0\n\t" - "and %2,%0\n\t" - "mov.w %0,%1" - : "=&z" (__dummy) - : "m" (__m(TMU0_TCR)), "r" (~0x100)); + timer_status = ctrl_inw(TMU0_TCR); + timer_status &= ~0x100; + ctrl_outw(timer_status, TMU0_TCR); /* * Here we are in the timer irq handler. We just have irqs locally @@ -187,16 +277,67 @@ static inline unsigned long mktime(unsigned int year, unsigned int mon, static unsigned long get_rtc_time(void) { -/* XXX not implemented yet */ +#ifdef CONFIG_SH_CPU_RTC + unsigned int sec, min, hr, wk, day, mon, yr, yr100; + + again: + ctrl_outb(1, RCR1); /* clear CF bit */ + do { + sec = ctrl_inb(RSECCNT); + min = ctrl_inb(RMINCNT); + hr = ctrl_inb(RHRCNT); + wk = ctrl_inb(RWKCNT); + day = ctrl_inb(RDAYCNT); + mon = ctrl_inb(RMONCNT); +#if defined(__SH4__) + yr = ctrl_inw(RYRCNT); + yr100 = (yr >> 8); + yr &= 0xff; +#else + yr = ctrl_inb(RYRCNT); + yr100 = (yr == 0x99) ? 0x19 : 0x20; +#endif + } while ((ctrl_inb(RCR1) & 0x80) != 0); + + BCD_TO_BIN(yr100); + BCD_TO_BIN(yr); + BCD_TO_BIN(mon); + BCD_TO_BIN(day); + BCD_TO_BIN(hr); + BCD_TO_BIN(min); + BCD_TO_BIN(sec); + + if (yr > 99 || mon < 1 || mon > 12 || day > 31 || day < 1 || + hr > 23 || min > 59 || sec > 59) { + printk(KERN_ERR + "SH RTC: invalid value, resetting to 1 Jan 2000\n"); + ctrl_outb(2, RCR2); /* reset, stop */ + ctrl_outb(0, RSECCNT); + ctrl_outb(0, RMINCNT); + ctrl_outb(0, RHRCNT); + ctrl_outb(6, RWKCNT); + ctrl_outb(1, RDAYCNT); + ctrl_outb(1, RMONCNT); +#if defined(__SH4__) + ctrl_outw(0x2000, RYRCNT); +#else + ctrl_outb(0, RYRCNT); +#endif + ctrl_outb(1, RCR2); /* start */ + goto again; + } + + return mktime(yr100 * 100 + yr, mon, day, hr, min, sec); +#else + /* XXX should support other clock devices? */ return 0; +#endif } static struct irqaction irq0 = { timer_interrupt, SA_INTERRUPT, 0, "timer", NULL, NULL}; void __init time_init(void) { - unsigned long __dummy; - xtime.tv_sec = get_rtc_time(); xtime.tv_usec = 0; @@ -204,19 +345,12 @@ void __init time_init(void) setup_irq(TIMER_IRQ, &irq0); /* Start TMU0 */ - asm volatile("mov %1,%0\n\t" - "mov.b %0,%2 ! external clock input\n\t" - "mov %3,%0\n\t" - "mov.w %0,%4 ! enable timer0 interrupt\n\t" - "mov.l %5,%6\n\t" - "mov.l %5,%7\n\t" - "mov %8,%0\n\t" - "mov.b %0,%9" - : "=&z" (__dummy) - : "i" (TMU_TOCR_INIT), "m" (__m(TMU_TOCR)), - "i" (TMU0_TCR_INIT), "m" (__m(TMU0_TCR)), - "r" (INTERVAL), "m" (__m(TMU0_TCOR)), "m" (__m(TMU0_TCNT)), - "i" (TMU_TSTR_INIT), "m" (__m(TMU_TSTR))); + ctrl_outb(TMU_TOCR_INIT,TMU_TOCR); + ctrl_outw(TMU0_TCR_INIT,TMU0_TCR); + ctrl_outl(INTERVAL,TMU0_TCOR); + ctrl_outl(INTERVAL,TMU0_TCNT); + ctrl_outb(TMU_TSTR_INIT,TMU_TSTR); + #if 0 /* Start RTC */ asm volatile(""); diff --git a/arch/sh/kernel/traps.c b/arch/sh/kernel/traps.c index 3d3cba23c..353281518 100644 --- a/arch/sh/kernel/traps.c +++ b/arch/sh/kernel/traps.c @@ -1,4 +1,5 @@ -/* +/* $Id$ + * * linux/arch/sh/traps.c * * SuperH version: Copyright (C) 1999 Niibe Yutaka @@ -56,10 +57,6 @@ asmlinkage void do_##name(unsigned long r4, unsigned long r5, \ #define VMALLOC_OFFSET (8*1024*1024) #define MODULE_RANGE (8*1024*1024) -static void show_registers(struct pt_regs *regs) -{/* Not implemented yet. */ -} - spinlock_t die_lock; void die(const char * str, struct pt_regs * regs, long err) @@ -67,7 +64,7 @@ void die(const char * str, struct pt_regs * regs, long err) console_verbose(); spin_lock_irq(&die_lock); printk("%s: %04lx\n", str, err & 0xffff); - show_registers(regs); + show_regs(regs); spin_unlock_irq(&die_lock); do_exit(SIGSEGV); } diff --git a/arch/sh/lib/Makefile b/arch/sh/lib/Makefile index a9010ddb2..934c84744 100644 --- a/arch/sh/lib/Makefile +++ b/arch/sh/lib/Makefile @@ -6,9 +6,7 @@ $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c $< -o $*.o L_TARGET = lib.a -# L_OBJS = checksum.o old-checksum.o semaphore.o delay.o \ -# usercopy.o getuser.o putuser.o -L_OBJS = delay.o memcpy.o memset.o memmove.o csum_partial_copy.o \ - wordcopy.o checksum.o # usercopy.o getuser.o putuser.o +L_OBJS = delay.o memcpy.o memset.o memmove.o old-checksum.o \ + checksum.o include $(TOPDIR)/Rules.make diff --git a/arch/sh/lib/checksum.S b/arch/sh/lib/checksum.S new file mode 100644 index 000000000..e0727df75 --- /dev/null +++ b/arch/sh/lib/checksum.S @@ -0,0 +1,318 @@ +/* $Id$ + * + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * IP/TCP/UDP checksumming routines + * + * Authors: Jorge Cwik, <jorge@laser.satlink.net> + * Arnt Gulbrandsen, <agulbra@nvg.unit.no> + * Tom May, <ftom@netcom.com> + * Pentium Pro/II routines: + * Alexander Kjeldaas <astor@guardian.no> + * Finn Arne Gangstad <finnag@guardian.no> + * Lots of code moved from tcp.c and ip.c; see those files + * for more names. + * + * Changes: Ingo Molnar, converted csum_partial_copy() to 2.1 exception + * handling. + * Andi Kleen, add zeroing on error + * converted to pure assembler + * + * SuperH version: Copyright (C) 1999 Niibe Yutaka + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <asm/errno.h> +#include <linux/linkage.h> + +/* + * computes a partial checksum, e.g. for TCP/UDP fragments + */ + +/* + * unsigned int csum_partial(const unsigned char *buf, int len, + * unsigned int sum); + */ + +.text +ENTRY(csum_partial) + /* + * Experiments with Ethernet and SLIP connections show that buff + * is aligned on either a 2-byte or 4-byte boundary. We get at + * least a twofold speedup on 486 and Pentium if it is 4-byte aligned. + * Fortunately, it is easy to convert 2-byte alignment to 4-byte + * alignment for the unrolled loop. + */ + mov r5,r1 + mov r4,r0 + tst #2,r0 ! Check alignment. + bt 2f ! Jump if alignment is ok. + ! + add #-2,r5 ! Alignment uses up two bytes. + cmp/pz r5 ! + bt/s 1f ! Jump if we had at least two bytes. + clrt + bra 6f + add #2,r5 ! r5 was < 2. Deal with it. +1: + mov.w @r4+,r0 + extu.w r0,r0 + addc r0,r6 + bf 2f + add #1,r6 +2: + mov #-5,r0 + shld r0,r5 + tst r5,r5 + bt/s 4f ! if it's =0, go to 4f + clrt +3: + mov.l @r4+,r0 + addc r0,r6 + mov.l @r4+,r0 + addc r0,r6 + mov.l @r4+,r0 + addc r0,r6 + mov.l @r4+,r0 + addc r0,r6 + mov.l @r4+,r0 + addc r0,r6 + mov.l @r4+,r0 + addc r0,r6 + mov.l @r4+,r0 + addc r0,r6 + mov.l @r4+,r0 + addc r0,r6 + movt r0 + dt r5 + bf/s 3b + cmp/eq #1,r0 + mov #0,r0 + addc r0,r6 +4: + mov r1,r5 + mov #0x1c,r0 + and r0,r5 + tst r5,r5 + bt/s 6f + clrt + shlr2 r5 +5: + mov.l @r4+,r0 + addc r0,r6 + movt r0 + dt r5 + bf/s 5b + cmp/eq #1,r0 + mov #0,r0 + addc r0,r6 +6: + mov r1,r5 + mov #3,r0 + and r0,r5 + tst r5,r5 + bt 9f ! if it's =0 go to 9f + mov #2,r1 + cmp/hs r1,r5 + bf 7f + mov.w @r4+,r0 + extu.w r0,r0 + cmp/eq r1,r5 + bt/s 8f + clrt + shll16 r0 + addc r0,r6 +7: + mov.b @r4+,r0 + extu.b r0,r0 +8: + addc r0,r6 + mov #0,r0 + addc r0,r6 +9: + rts + mov r6,r0 + +/* +unsigned int csum_partial_copy_generic (const char *src, char *dst, int len, + int sum, int *src_err_ptr, int *dst_err_ptr) + */ + +/* + * Copy from ds while checksumming, otherwise like csum_partial + * + * The macros SRC and DST specify the type of access for the instruction. + * thus we can call a custom exception handler for all access types. + * + * FIXME: could someone double-check whether I haven't mixed up some SRC and + * DST definitions? It's damn hard to trigger all cases. I hope I got + * them all but there's no guarantee. + */ + +#define SRC(y...) \ + 9999: y; \ + .section __ex_table, "a"; \ + .long 9999b, 6001f ; \ + .previous + +#define DST(y...) \ + 9999: y; \ + .section __ex_table, "a"; \ + .long 9999b, 6002f ; \ + .previous + +ENTRY(csum_partial_copy_generic) + mov.l r5,@-r15 + mov.l r6,@-r15 + + mov #2,r0 + tst r0,r5 ! Check alignment. + bt 2f ! Jump if alignment is ok. + add #-2,r6 ! Alignment uses up two bytes. + cmp/pz r6 ! Jump if we had at least two bytes. + bt/s 1f + clrt + bra 4f + add #2,r6 ! ecx was < 2. Deal with it. +SRC(1: mov.w @r4+,r0 ) +DST( mov.w r0,@r5 ) + add #2,r5 + extu.w r0,r0 + addc r0,r7 + mov #0,r0 + addc r0,r7 +2: + mov r6,r2 + mov #-5,r0 + shld r0,r6 + tst r6,r6 + bf/s 2f + clrt +SRC(1: mov.l @r4+,r0 ) +SRC( mov.l @r4+,r1 ) + addc r0,r7 +DST( mov.l r0,@r5 ) + add #4,r5 + addc r1,r7 +DST( mov.l r1,@r5 ) + add #4,r5 + +SRC( mov.l @r4+,r0 ) +SRC( mov.l @r4+,r1 ) + addc r0,r7 +DST( mov.l r0,@r5 ) + add #4,r5 + addc r1,r7 +DST( mov.l r1,@r5 ) + add #4,r5 + +SRC( mov.l @r4+,r0 ) +SRC( mov.l @r4+,r1 ) + addc r0,r7 +DST( mov.l r0,@r5 ) + add #4,r5 + addc r1,r7 +DST( mov.l r1,@r5 ) + add #4,r5 + +SRC( mov.l @r4+,r0 ) +SRC( mov.l @r4+,r1 ) + addc r0,r7 +DST( mov.l r0,@r5 ) + add #4,r5 + addc r1,r7 +DST( mov.l r1,@r5 ) + add #4,r5 + movt r0 + dt r6 + bf/s 1b + cmp/eq #1,r0 + mov #0,r0 + addc r0,r7 + +2: mov r2,r6 + mov #0x1c,r0 + and r0,r6 + cmp/pl r6 + bf/s 4f + clrt + shlr2 r6 +SRC(3: mov.l @r4+,r0 ) + addc r0,r7 +DST( mov.l r0,@r5 ) + add #4,r5 + movt r0 + dt r6 + bf/s 3b + cmp/eq #1,r0 + mov #0,r0 + addc r0,r7 +4: mov r2,r6 + mov #3,r0 + and r0,r6 + cmp/pl r6 + bf 7f + mov #2,r1 + cmp/hs r1,r6 + bf 5f +SRC( mov.w @r4+,r0 ) +DST( mov.w r0,@r5 ) + extu.w r0,r0 + add #2,r5 + cmp/eq r1,r6 + bt/s 6f + clrt + shll16 r0 + addc r0,r7 +SRC(5: mov.b @r4+,r0 ) +DST( mov.b r0,@r5 ) + extu.b r0,r0 +6: addc r0,r7 + mov #0,r0 + addc r0,r7 +7: +5000: + +# Exception handler: +.section .fixup, "ax" + +6001: + mov.l @(8,r15),r0 ! src_err_ptr + mov #-EFAULT,r1 + mov.l r1,@r0 + + ! zero the complete destination - computing the rest + ! is too much work + mov.l @(4,r15),r5 ! dst + mov.l @r15,r6 ! len + mov #0,r7 +1: mov.b r7,@r5 + dt r6 + bf/s 1b + add #1,r5 + mov.l 8000f,r0 + jmp @r0 + nop + .balign 4 +8000: .long 5000b + +6002: + mov.l @(12,r15),r0 ! dst_err_ptr + mov #-EFAULT,r1 + mov.l r1,@r0 + mov.l 8001f,r0 + jmp @r0 + nop + .balign 4 +8001: .long 5000b + +.previous + add #8,r15 + rts + mov r7,r0 diff --git a/arch/sh/lib/checksum.c b/arch/sh/lib/checksum.c deleted file mode 100644 index f076bd3c6..000000000 --- a/arch/sh/lib/checksum.c +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Taken from: - * arch/alpha/lib/checksum.c - * - * This file contains network checksum routines that are better done - * in an architecture-specific manner due to speed.. - */ - -#include <linux/string.h> - -#include <asm/byteorder.h> - -static inline unsigned short from64to16(unsigned long long x) -{ - /* add up 32-bit words for 33 bits */ - x = (x & 0xffffffff) + (x >> 32); - /* add up 16-bit and 17-bit words for 17+c bits */ - x = (x & 0xffff) + (x >> 16); - /* add up 16-bit and 2-bit for 16+c bit */ - x = (x & 0xffff) + (x >> 16); - /* add up carry.. */ - x = (x & 0xffff) + (x >> 16); - return x; -} - -/* - * computes the checksum of the TCP/UDP pseudo-header - * returns a 16-bit checksum, already complemented. - */ -unsigned short int csum_tcpudp_magic(unsigned long saddr, - unsigned long daddr, - unsigned short len, - unsigned short proto, - unsigned int sum) -{ - return ~from64to16(saddr + daddr + sum + - ((unsigned long) ntohs(len) << 16) + - ((unsigned long) proto << 8)); -} - -unsigned int csum_tcpudp_nofold(unsigned long saddr, - unsigned long daddr, - unsigned short len, - unsigned short proto, - unsigned int sum) -{ - unsigned long long result; - - result = (saddr + daddr + sum + - ((unsigned long) ntohs(len) << 16) + - ((unsigned long) proto << 8)); - - /* Fold down to 32-bits so we don't loose in the typedef-less - network stack. */ - /* 64 to 33 */ - result = (result & 0xffffffff) + (result >> 32); - /* 33 to 32 */ - result = (result & 0xffffffff) + (result >> 32); - return result; -} - -/* - * Do a 64-bit checksum on an arbitrary memory area.. - * - * This isn't a great routine, but it's not _horrible_ either. The - * inner loop could be unrolled a bit further, and there are better - * ways to do the carry, but this is reasonable. - */ -static inline unsigned long do_csum(const unsigned char * buff, int len) -{ - int odd, count; - unsigned long long result = 0; - - if (len <= 0) - goto out; - odd = 1 & (unsigned long) buff; - if (odd) { - result = *buff << 8; - len--; - buff++; - } - count = len >> 1; /* nr of 16-bit words.. */ - if (count) { - if (2 & (unsigned long) buff) { - result += *(unsigned short *) buff; - count--; - len -= 2; - buff += 2; - } - count >>= 1; /* nr of 32-bit words.. */ - if (count) { - if (4 & (unsigned long) buff) { - result += *(unsigned int *) buff; - count--; - len -= 4; - buff += 4; - } - count >>= 1; /* nr of 64-bit words.. */ - if (count) { - unsigned long carry = 0; - do { - unsigned long w = *(unsigned long *) buff; - count--; - buff += 8; - result += carry; - result += w; - carry = (w > result); - } while (count); - result += carry; - result = (result & 0xffffffff) + (result >> 32); - } - if (len & 4) { - result += *(unsigned int *) buff; - buff += 4; - } - } - if (len & 2) { - result += *(unsigned short *) buff; - buff += 2; - } - } - if (len & 1) - result += *buff; - result = from64to16(result); - if (odd) - result = ((result >> 8) & 0xff) | ((result & 0xff) << 8); -out: - return result; -} - -/* - * This is a version of ip_compute_csum() optimized for IP headers, - * which always checksum on 4 octet boundaries. - */ -unsigned short ip_fast_csum(unsigned char * iph, unsigned int ihl) -{ - return ~do_csum(iph,ihl*4); -} - -/* - * computes the checksum of a memory block at buff, length len, - * and adds in "sum" (32-bit) - * - * returns a 32-bit number suitable for feeding into itself - * or csum_tcpudp_magic - * - * this function must be called with even lengths, except - * for the last fragment, which may be odd - * - * it's best to have buff aligned on a 32-bit boundary - */ -unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum) -{ - unsigned long long result = do_csum(buff, len); - - /* add in old sum, and carry.. */ - result += sum; - /* 32+c bits -> 32 bits */ - result = (result & 0xffffffff) + (result >> 32); - return result; -} - -/* - * this routine is used for miscellaneous IP-like checksums, mainly - * in icmp.c - */ -unsigned short ip_compute_csum(unsigned char * buff, int len) -{ - return ~from64to16(do_csum(buff,len)); -} diff --git a/arch/sh/lib/csum_partial_copy.c b/arch/sh/lib/csum_partial_copy.c deleted file mode 100644 index 1fb36ab05..000000000 --- a/arch/sh/lib/csum_partial_copy.c +++ /dev/null @@ -1,75 +0,0 @@ -/* - * INET An implementation of the TCP/IP protocol suite for the LINUX - * operating system. INET is implemented using the BSD Socket - * interface as the means of communication with the user level. - * - * MIPS specific IP/TCP/UDP checksumming routines - * - * Authors: Ralf Baechle, <ralf@waldorf-gmbh.de> - * Lots of code moved from tcp.c and ip.c; see those files - * for more names. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * $Id: csum_partial_copy.c,v 1.2 1998/09/16 13:29:32 ralf Exp $ - */ -#include <net/checksum.h> -#include <linux/types.h> -#include <asm/byteorder.h> -#include <asm/string.h> -#include <asm/uaccess.h> - -/* - * copy while checksumming, otherwise like csum_partial - */ -unsigned int csum_partial_copy(const char *src, char *dst, - int len, unsigned int sum) -{ - /* - * It's 2:30 am and I don't feel like doing it real ... - * This is lots slower than the real thing (tm) - */ - sum = csum_partial(src, len, sum); - memcpy(dst, src, len); - - return sum; -} - -/* - * Copy from userspace and compute checksum. If we catch an exception - * then zero the rest of the buffer. - */ -unsigned int csum_partial_copy_from_user (const char *src, char *dst, - int len, unsigned int sum, - int *err_ptr) -{ - int missing; - - missing = copy_from_user(dst, src, len); - if (missing) { - memset(dst + len - missing, 0, missing); - *err_ptr = -EFAULT; - } - - return csum_partial(dst, len, sum); -} - -/* - * Copy to userspace and compute checksum. - */ -unsigned int csum_partial_copy_to_user (const char *src, char *dst, - int len, unsigned int sum, - int *err_ptr) -{ - sum = csum_partial(src, len, sum); - - if (copy_to_user(dst, src, len)) { - *err_ptr = -EFAULT; - return sum; - } - - return sum; -} diff --git a/arch/sh/lib/memcpy.S b/arch/sh/lib/memcpy.S index 3a4aca919..2da59276e 100644 --- a/arch/sh/lib/memcpy.S +++ b/arch/sh/lib/memcpy.S @@ -1,131 +1,227 @@ -! Taken from newlib-1.8.0 +/* $Id$ + * + * "memcpy" implementation of SuperH + * + * Copyright (C) 1999 Niibe Yutaka + * + */ -! -! Fast SH memcpy -! -! by Toshiyasu Morita (tm@netcom.com) -! hacked by J"orn Rernnecke (amylaar@cygnus.co.uk) ("o for o-umlaut) -! -! Entry: r4: destination pointer -! r5: source pointer -! r6: byte count -! -! Exit: r0: destination pointer -! r1-r7: trashed -! -! Notes: Usually one wants to do small reads and write a longword, but -! unfortunately it is difficult in some cases to concatanate bytes -! into a longword on the SH, so this does a longword read and small -! writes. -! -! This implementation makes two assumptions about how it is called: -! -! 1.: If the byte count is nonzero, the address of the last byte to be -! copied is unsigned greater than the address of the first byte to -! be copied. This could be easily swapped for a signed comparison, -! but the algorithm used needs some comparison. -! -! 2.: When there are two or three bytes in the last word of an 11-or-bore -! bytes memory chunk to b copied, the rest of the word can be read -! without size effects. -! This could be easily changed by increasing the minumum size of -! a fast memcpy and the amount subtracted from r7 before L_2l_loop be 2, -! however, this would cost a few extra cyles on average. -! +/* + * void *memcpy(void *dst, const void *src, size_t n); + * No overlap between the memory of DST and of SRC are assumed. + */ #include <linux/linkage.h> ENTRY(memcpy) - ! Big endian version copies with decreasing addresses. - mov r4,r0 - add r6,r0 - sub r4,r5 - mov #11,r1 - cmp/hs r1,r6 - bf/s L_small + tst r6,r6 + bt/s 9f ! if n=0, do nothing + mov r4,r0 + sub r4,r5 ! From here, r5 has the distance to r0 + add r6,r0 ! From here, r0 points the end of copying point + mov #12,r1 + cmp/gt r6,r1 + bt/s 7f ! if it's too small, copy a byte at once add #-1,r5 - mov r5,r3 - add r0,r3 - shlr r3 - bt/s L_even - mov r4,r7 - mov.b @(r0,r5),r2 - add #-1,r3 - mov.b r2,@-r0 -L_even: - tst #1,r0 - add #-1,r5 - bf/s L_odddst - add #8,r7 - tst #2,r0 - bt L_al4dst - add #-1,r3 - mov.w @(r0,r5),r1 - mov.w r1,@-r0 -L_al4dst: - shlr r3 - bt L_al4both - mov.w @(r0,r5),r1 - swap.w r1,r1 - add #4,r7 - add #-4,r5 - .align 2 -L_2l_loop: - mov.l @(r0,r5),r2 - xtrct r2,r1 - mov.l r1,@-r0 - cmp/hs r7,r0 - mov.l @(r0,r5),r1 - xtrct r1,r2 - mov.l r2,@-r0 - bt L_2l_loop - bra L_cleanup - add #5,r5 + add #1,r5 + ! From here, r6 is free + ! + ! r4 --> [ ... ] DST [ ... ] SRC + ! [ ... ] [ ... ] + ! : : + ! r0 --> [ ... ] r0+r5 --> [ ... ] + ! + ! + mov r5,r1 + mov #3,r2 + and r2,r1 + shll2 r1 + mov r0,r3 ! Save the value on R0 to R3 + mova jmptable,r0 + add r1,r0 + mov.l @r0,r1 + jmp @r1 + mov r3,r0 ! and back to R0 + .balign 4 +jmptable: + .long case0 + .long case1 + .long case2 + .long case3 - nop ! avoid nop in executed code. -L_al4both: - add #-2,r5 - .align 2 -L_al4both_loop: - mov.l @(r0,r5),r1 - cmp/hs r7,r0 - bt/s L_al4both_loop + ! copy a byte at once +7: mov r4,r2 + add #1,r2 +8: + cmp/hi r2,r0 + mov.b @(r0,r5),r1 + bt/s 8b ! while (r0>r2) + mov.b r1,@-r0 +9: + rts + nop + +case0: + ! + ! GHIJ KLMN OPQR --> GHIJ KLMN OPQR + ! + ! First, align to long word boundary + mov r0,r3 + and r2,r3 + tst r3,r3 + bt/s 2f + add #-4,r5 + add #3,r5 +1: dt r3 + mov.b @(r0,r5),r1 + bf/s 1b + mov.b r1,@-r0 + ! + add #-3,r5 +2: ! Second, copy a long word at once + mov r4,r2 + add #7,r2 +3: mov.l @(r0,r5),r1 + cmp/hi r2,r0 + bt/s 3b mov.l r1,@-r0 - bra L_cleanup + ! + ! Third, copy a byte at once, if necessary + cmp/eq r4,r0 + bt/s 9b add #3,r5 + bra 8b + add #-6,r2 - nop ! avoid nop in executed code. -L_odddst: - shlr r3 - bt L_al4src - mov.w @(r0,r5),r1 - mov.b r1,@-r0 - shlr8 r1 - mov.b r1,@-r0 -L_al4src: - add #-2,r5 - .align 2 -L_odd_loop: - mov.l @(r0,r5),r2 - cmp/hs r7,r0 - mov.b r2,@-r0 - shlr8 r2 - mov.w r2,@-r0 - shlr16 r2 - mov.b r2,@-r0 - bt L_odd_loop - - add #3,r5 -L_cleanup: -L_small: +case1: + ! + ! GHIJ KLMN OPQR --> ...G HIJK LMNO PQR. + ! + ! First, align to long word boundary + mov r0,r3 + and r2,r3 + tst r3,r3 + bt/s 2f + add #-1,r5 +1: dt r3 + mov.b @(r0,r5),r1 + bf/s 1b + mov.b r1,@-r0 + ! +2: ! Second, read a long word and write a long word at once + mov.l @(r0,r5),r1 + add #-4,r5 + mov r4,r2 + add #7,r2 + ! +#ifdef __LITTLE_ENDIAN__ +3: mov r1,r3 ! RQPO + shll16 r3 + shll8 r3 ! Oxxx + mov.l @(r0,r5),r1 ! NMLK + mov r1,r6 + shlr8 r6 ! xNML + or r6,r3 ! ONML + cmp/hi r2,r0 + bt/s 3b + mov.l r3,@-r0 +#else +3: mov r1,r3 ! OPQR + shlr16 r3 + shlr8 r3 ! xxxO + mov.l @(r0,r5),r1 ! KLMN + mov r1,r6 + shll8 r6 ! LMNx + or r6,r3 ! LMNO + cmp/hi r2,r0 + bt/s 3b + mov.l r3,@-r0 +#endif + ! + ! Third, copy a byte at once, if necessary cmp/eq r4,r0 - bt L_ready - add #1,r4 - .align 2 -L_cleanup_loop: - mov.b @(r0,r5),r2 + bt/s 9b + add #4,r5 + bra 8b + add #-6,r2 + +case2: + ! + ! GHIJ KLMN OPQR --> ..GH IJKL MNOP QR.. + ! + ! First, align to word boundary + tst #1,r0 + bt/s 2f + add #-1,r5 + mov.b @(r0,r5),r1 + mov.b r1,@-r0 + ! +2: ! Second, read a word and write a word at once + add #-1,r5 + mov r4,r2 + add #3,r2 + ! +3: mov.w @(r0,r5),r1 + cmp/hi r2,r0 + bt/s 3b + mov.w r1,@-r0 + ! + ! Third, copy a byte at once, if necessary cmp/eq r4,r0 - mov.b r2,@-r0 - bf L_cleanup_loop -L_ready: + bt/s 9b + add #1,r5 + mov.b @(r0,r5),r1 rts - nop + mov.b r1,@-r0 + +case3: + ! + ! GHIJ KLMN OPQR --> .GHI JKLM NOPQ R... + ! + ! First, align to long word boundary + mov r0,r3 + and r2,r3 + tst r3,r3 + bt/s 2f + add #-1,r5 +1: dt r3 + mov.b @(r0,r5),r1 + bf/s 1b + mov.b r1,@-r0 + ! +2: ! Second, read a long word and write a long word at once + add #-2,r5 + mov.l @(r0,r5),r1 + add #-4,r5 + mov r4,r2 + add #7,r2 + ! +#ifdef __LITTLE_ENDIAN__ +3: mov r1,r3 ! RQPO + shll8 r3 ! QPOx + mov.l @(r0,r5),r1 ! NMLK + mov r1,r6 + shlr16 r6 + shlr8 r6 ! xxxN + or r6,r3 ! QPON + cmp/hi r2,r0 + bt/s 3b + mov.l r3,@-r0 +#else +3: mov r1,r3 ! OPQR + shlr8 r3 ! xOPQ + mov.l @(r0,r5),r1 ! KLMN + mov r1,r6 + shll16 r6 + shll8 r6 ! Nxxx + or r6,r3 ! NOPQ + cmp/hi r2,r0 + bt/s 3b + mov.l r3,@-r0 +#endif + ! + ! Third, copy a byte at once, if necessary + cmp/eq r4,r0 + bt/s 9b + add #6,r5 + bra 8b + add #-6,r2 diff --git a/arch/sh/lib/memmove.S b/arch/sh/lib/memmove.S index e95dc3709..9e5c2fad3 100644 --- a/arch/sh/lib/memmove.S +++ b/arch/sh/lib/memmove.S @@ -1,422 +1,254 @@ +/* $Id$ + * + * "memmove" implementation of SuperH + * + * Copyright (C) 1999 Niibe Yutaka + * + */ + +/* + * void *memmove(void *dst, const void *src, size_t n); + * The memory areas may overlap. + */ + #include <linux/linkage.h> ENTRY(memmove) - mov.l r8,@-r15 - mov.l r9,@-r15 - mov.l r14,@-r15 - sts.l pr,@-r15 - add #-28,r15 - mov r15,r14 - mov.l r4,@r14 - mov.l r5,@(4,r14) - mov.l r6,@(8,r14) - mov.l @r14,r1 - mov.l r1,@(12,r14) - mov.l @(4,r14),r1 - mov.l r1,@(16,r14) - mov.l @(12,r14),r1 - mov.l @(16,r14),r2 - sub r2,r1 - mov.l @(8,r14),r2 - cmp/hs r2,r1 - bt .L54 - bra .L2 - nop -.L54: - mov.l @(8,r14),r1 - mov #15,r2 - cmp/gt r2,r1 - bt .LF100 - bra .L52 - nop -.LF100: - mov.l @(12,r14),r2 - neg r2,r1 - mov #3,r2 - and r1,r2 - mov.l @(8,r14),r1 - mov r1,r9 - sub r2,r9 - mov r9,r2 - mov.l r2,@(8,r14) -.L4: - mov.l @(12,r14),r2 - neg r2,r1 - mov #3,r2 - and r1,r2 - mov.l r2,@(20,r14) -.L7: - mov.l @(20,r14),r1 - cmp/pl r1 - bt .L9 - bra .L6 - nop - .align 2 -.L9: - mov r14,r2 - mov r14,r1 - add #24,r1 - mov.l @(16,r14),r2 - mov.b @r2,r3 - mov.b r3,@r1 - mov.l @(16,r14),r1 - mov r1,r2 - add #1,r2 - mov.l r2,@(16,r14) - mov.l @(20,r14),r1 - mov r1,r2 - add #-1,r2 - mov.l r2,@(20,r14) - mov.l @(12,r14),r1 - mov r14,r2 - mov r14,r3 - add #24,r3 - mov.b @r3,r2 - mov.b r2,@r1 - mov.l @(12,r14),r1 - mov r1,r2 - add #1,r2 - mov.l r2,@(12,r14) - bra .L7 - nop - .align 2 -.L8: -.L6: - bra .L5 - nop - .align 2 -.L10: - bra .L4 - nop - .align 2 -.L5: - nop -.L11: - mov.l @(16,r14),r1 + ! if dest > src, call memcpy (it copies in decreasing order) + cmp/hi r5,r4 + bf 1f + mov.l 2f,r0 + jmp @r0 + nop + .balign 4 +2: .long SYMBOL_NAME(memcpy) +1: + sub r5,r4 ! From here, r4 has the distance to r0 + tst r6,r6 + bt/s 9f ! if n=0, do nothing + mov r5,r0 + add r6,r5 + mov #12,r1 + cmp/gt r6,r1 + bt/s 8f ! if it's too small, copy a byte at once + add #-1,r4 + add #1,r4 + ! + ! [ ... ] DST [ ... ] SRC + ! [ ... ] [ ... ] + ! : : + ! r0+r4--> [ ... ] r0 --> [ ... ] + ! : : + ! [ ... ] [ ... ] + ! r5 --> + ! + mov r4,r1 mov #3,r2 - and r1,r2 - tst r2,r2 - bf .L14 - mov r15,r2 - mov.l @(12,r14),r1 - mov.l @(16,r14),r2 - mov.l @(8,r14),r7 - mov r7,r3 - shlr2 r3 - mov r1,r4 - mov r2,r5 - mov r3,r6 - mov.l .L46,r8 - jsr @r8 - nop - bra .L15 - nop - .align 2 -.L14: - mov r15,r2 - mov.l @(12,r14),r1 - mov.l @(16,r14),r2 - mov.l @(8,r14),r7 - mov r7,r3 - shlr2 r3 - mov r1,r4 - mov r2,r5 - mov r3,r6 - mov.l .L47,r8 - jsr @r8 - nop -.L15: - mov.l @(8,r14),r1 - mov #-4,r2 - and r2,r1 - mov.l @(16,r14),r2 - add r2,r1 - mov.l r1,@(16,r14) - mov.l @(8,r14),r1 - mov #-4,r2 and r2,r1 - mov.l @(12,r14),r2 - add r2,r1 - mov.l r1,@(12,r14) - mov.l @(8,r14),r1 - mov #3,r2 - and r1,r2 - mov.l r2,@(8,r14) -.L13: -.L52: - bra .L3 - nop - .align 2 -.L16: - bra .L11 - nop - .align 2 -.L12: -.L3: - nop -.L17: - mov.l @(8,r14),r1 - mov.l r1,@(20,r14) -.L20: - mov.l @(20,r14),r1 - cmp/pl r1 - bt .L22 - bra .L19 - nop - .align 2 -.L22: - mov r14,r2 - mov r14,r1 - add #24,r1 - mov.l @(16,r14),r2 - mov.b @r2,r3 - mov.b r3,@r1 - mov.l @(16,r14),r1 - mov r1,r2 - add #1,r2 - mov.l r2,@(16,r14) - mov.l @(20,r14),r1 - mov r1,r2 - add #-1,r2 - mov.l r2,@(20,r14) - mov.l @(12,r14),r1 - mov r14,r2 - mov r14,r3 - add #24,r3 - mov.b @r3,r2 - mov.b r2,@r1 - mov.l @(12,r14),r1 + shll2 r1 + mov r0,r3 ! Save the value on R0 to R3 + mova jmptable,r0 + add r1,r0 + mov.l @r0,r1 + jmp @r1 + mov r3,r0 ! and back to R0 + .balign 4 +jmptable: + .long case0 + .long case1 + .long case2 + .long case3 + + ! copy a byte at once +8: mov.b @r0+,r1 + cmp/hs r5,r0 + bf/s 8b ! while (r0<r5) + mov.b r1,@(r0,r4) + add #1,r4 +9: + add r4,r0 + rts + sub r6,r0 + +case_none: + bra 8b + add #-1,r4 + +case0: + ! + ! GHIJ KLMN OPQR --> GHIJ KLMN OPQR + ! + ! First, align to long word boundary + mov r0,r3 + and r2,r3 + tst r3,r3 + bt/s 2f + add #-1,r4 + mov #4,r2 + sub r3,r2 +1: dt r2 + mov.b @r0+,r1 + bf/s 1b + mov.b r1,@(r0,r4) + ! +2: ! Second, copy a long word at once + add #-3,r4 + add #-3,r5 +3: mov.l @r0+,r1 + cmp/hs r5,r0 + bf/s 3b + mov.l r1,@(r0,r4) + add #3,r5 + ! + ! Third, copy a byte at once, if necessary + cmp/eq r5,r0 + bt/s 9b + add #4,r4 + bra 8b + add #-1,r4 + +case3: + ! + ! GHIJ KLMN OPQR --> ...G HIJK LMNO PQR. + ! + ! First, align to long word boundary + mov r0,r3 + and r2,r3 + tst r3,r3 + bt/s 2f + add #-1,r4 + mov #4,r2 + sub r3,r2 +1: dt r2 + mov.b @r0+,r1 + bf/s 1b + mov.b r1,@(r0,r4) + ! +2: ! Second, read a long word and write a long word at once + add #-2,r4 + mov.l @(r0,r4),r1 + add #-7,r5 + add #-4,r4 + ! +#ifdef __LITTLE_ENDIAN__ + shll8 r1 +3: mov r1,r3 ! JIHG + shlr8 r3 ! xJIH + mov.l @r0+,r1 ! NMLK mov r1,r2 - add #1,r2 - mov.l r2,@(12,r14) - bra .L20 - nop - .align 2 -.L21: -.L19: - bra .L18 - nop - .align 2 -.L23: - bra .L17 - nop - .align 2 -.L18: - bra .L24 - nop - .align 2 -.L2: - mov.l @(16,r14),r1 - mov.l @(8,r14),r2 - add r2,r1 - mov.l r1,@(16,r14) - mov.l @(12,r14),r1 - mov.l @(8,r14),r2 - add r2,r1 - mov.l r1,@(12,r14) - mov.l @(8,r14),r1 - mov #15,r2 - cmp/gt r2,r1 - bt .LF101 - bra .L53 - nop -.LF101: - mov.l @(12,r14),r1 - mov #3,r2 - and r1,r2 - mov.l @(8,r14),r1 - mov r1,r9 - sub r2,r9 - mov r9,r2 - mov.l r2,@(8,r14) -.L26: - mov.l @(12,r14),r1 - mov #3,r2 - and r1,r2 - mov.l r2,@(20,r14) -.L29: - mov.l @(20,r14),r1 - cmp/pl r1 - bt .L31 - bra .L28 - nop - .align 2 -.L31: - mov.l @(16,r14),r1 - mov r1,r2 - add #-1,r2 - mov.l r2,@(16,r14) - mov r14,r2 - mov r14,r1 - add #24,r1 - mov.l @(16,r14),r2 - mov.b @r2,r3 - mov.b r3,@r1 - mov.l @(12,r14),r1 - mov r1,r2 - add #-1,r2 - mov.l r2,@(12,r14) - mov.l @(20,r14),r1 - mov r1,r2 - add #-1,r2 - mov.l r2,@(20,r14) - mov.l @(12,r14),r1 - mov r14,r2 - mov r14,r3 - add #24,r3 - mov.b @r3,r2 - mov.b r2,@r1 - bra .L29 - nop - .align 2 -.L30: -.L28: - bra .L27 - nop - .align 2 -.L32: - bra .L26 - nop - .align 2 -.L27: - nop -.L33: - mov.l @(16,r14),r1 - mov #3,r2 - and r1,r2 - tst r2,r2 - bf .L36 - mov r15,r2 - mov.l @(12,r14),r1 - mov.l @(16,r14),r2 - mov.l @(8,r14),r7 - mov r7,r3 - shlr2 r3 - mov r1,r4 - mov r2,r5 - mov r3,r6 - mov.l .L48,r8 - jsr @r8 - nop - bra .L37 - nop - .align 2 -.L36: - mov r15,r2 - mov.l @(12,r14),r1 - mov.l @(16,r14),r2 - mov.l @(8,r14),r7 - mov r7,r3 - shlr2 r3 - mov r1,r4 - mov r2,r5 - mov r3,r6 - mov.l .L49,r8 - jsr @r8 - nop -.L37: - mov.l @(8,r14),r1 - mov #-4,r2 - and r2,r1 - mov.l @(16,r14),r2 - mov r2,r9 - sub r1,r9 - mov r9,r1 - mov.l r1,@(16,r14) - mov.l @(8,r14),r1 - mov #-4,r2 - and r2,r1 - mov.l @(12,r14),r2 - mov r2,r9 - sub r1,r9 - mov r9,r1 - mov.l r1,@(12,r14) - mov.l @(8,r14),r1 - mov #3,r2 - and r1,r2 - mov.l r2,@(8,r14) -.L35: -.L53: - bra .L25 - nop - .align 2 -.L38: - bra .L33 - nop - .align 2 -.L34: -.L25: - nop -.L39: - mov.l @(8,r14),r1 - mov.l r1,@(20,r14) -.L42: - mov.l @(20,r14),r1 - cmp/pl r1 - bt .L44 - bra .L41 - nop - .align 2 -.L44: - mov.l @(16,r14),r1 + shll16 r2 + shll8 r2 ! Kxxx + or r2,r3 ! KJIH + cmp/hs r5,r0 + bf/s 3b + mov.l r3,@(r0,r4) +#else + shlr8 r1 +3: mov r1,r3 ! GHIJ + shll8 r3 ! HIJx + mov.l @r0+,r1 ! KLMN mov r1,r2 - add #-1,r2 - mov.l r2,@(16,r14) - mov r14,r2 - mov r14,r1 - add #24,r1 - mov.l @(16,r14),r2 - mov.b @r2,r3 - mov.b r3,@r1 - mov.l @(12,r14),r1 + shlr16 r2 + shlr8 r2 ! xxxK + or r2,r3 ! HIJK + cmp/hs r5,r0 + bf/s 3b + mov.l r3,@(r0,r4) +#endif + add #7,r5 + ! + ! Third, copy a byte at once, if necessary + cmp/eq r5,r0 + bt/s 9b + add #7,r4 + add #-3,r0 + bra 8b + add #-1,r4 + +case2: + ! + ! GHIJ KLMN OPQR --> ..GH IJKL MNOP QR.. + ! + ! First, align to word boundary + tst #1,r0 + bt/s 2f + add #-1,r4 + mov.b @r0+,r1 + mov.b r1,@(r0,r4) + ! +2: ! Second, read a word and write a word at once + add #-1,r4 + add #-1,r5 + ! +3: mov.w @r0+,r1 + cmp/hs r5,r0 + bf/s 3b + mov.w r1,@(r0,r4) + add #1,r5 + ! + ! Third, copy a byte at once, if necessary + cmp/eq r5,r0 + bt/s 9b + add #2,r4 + mov.b @r0,r1 + mov.b r1,@(r0,r4) + bra 9b + add #1,r0 + +case1: + ! + ! GHIJ KLMN OPQR --> .GHI JKLM NOPQ R... + ! + ! First, align to long word boundary + mov r0,r3 + and r2,r3 + tst r3,r3 + bt/s 2f + add #-1,r4 + mov #4,r2 + sub r3,r2 +1: dt r2 + mov.b @r0+,r1 + bf/s 1b + mov.b r1,@(r0,r4) + ! +2: ! Second, read a long word and write a long word at once + mov.l @(r0,r4),r1 + add #-7,r5 + add #-4,r4 + ! +#ifdef __LITTLE_ENDIAN__ + shll16 r1 + shll8 r1 +3: mov r1,r3 ! JIHG + shlr16 r3 + shlr8 r3 ! xxxJ + mov.l @r0+,r1 ! NMLK mov r1,r2 - add #-1,r2 - mov.l r2,@(12,r14) - mov.l @(20,r14),r1 + shll8 r2 ! MLKx + or r2,r3 ! MLKJ + cmp/hs r5,r0 + bf/s 3b + mov.l r3,@(r0,r4) +#else + shlr16 r1 + shlr8 r1 +3: mov r1,r3 ! GHIJ + shll16 r3 + shll8 r3 ! Jxxx + mov.l @r0+,r1 ! KLMN mov r1,r2 - add #-1,r2 - mov.l r2,@(20,r14) - mov.l @(12,r14),r1 - mov r14,r2 - mov r14,r3 - add #24,r3 - mov.b @r3,r2 - mov.b r2,@r1 - bra .L42 - nop - .align 2 -.L43: -.L41: - bra .L24 - nop - .align 2 -.L45: - bra .L39 - nop - .align 2 -.L40: -.L24: - mov.l @r14,r1 - mov r1,r0 - bra .L1 - nop - .align 2 -.L1: - add #28,r14 - mov r14,r15 - lds.l @r15+,pr - mov.l @r15+,r14 - mov.l @r15+,r9 - mov.l @r15+,r8 - rts - nop -.L50: - .align 2 -.L46: - .long __wordcopy_fwd_aligned -.L47: - .long __wordcopy_fwd_dest_aligned -.L48: - .long __wordcopy_bwd_aligned -.L49: - .long __wordcopy_bwd_dest_aligned -.Lfe1: + shlr8 r2 ! xKLM + or r2,r3 ! JKLM + cmp/hs r5,r0 + bf/s 3b ! while(r0<r5) + mov.l r3,@(r0,r4) +#endif + add #7,r5 + ! + ! Third, copy a byte at once, if necessary + cmp/eq r5,r0 + bt/s 9b + add #5,r4 + add #-3,r0 + bra 8b + add #-1,r4 diff --git a/arch/sh/lib/memset.S b/arch/sh/lib/memset.S index c97648a4a..40505631d 100644 --- a/arch/sh/lib/memset.S +++ b/arch/sh/lib/memset.S @@ -1,72 +1,57 @@ -! Taken from newlib-1.8.0 +/* $Id$ + * + * "memset" implementation of SuperH + * + * Copyright (C) 1999 Niibe Yutaka + * + */ + +/* + * void *memset(void *s, int c, size_t n); + */ -! -! Fast SH memset -! -! by Toshiyasu Morita (tm@netcom.com) -! -! Entry: r4: destination pointer -! r5: fill value -! r6: byte count -! -! Exit: r0-r3: trashed -! #include <linux/linkage.h> ENTRY(memset) - mov r4,r3 ! Save return value - - mov r6,r0 ! Check explicitly for zero - cmp/eq #0,r0 - bt L_exit - - mov #12,r0 ! Check for small number of bytes + tst r6,r6 + bt/s 5f ! if n=0, do nothing + add r6,r4 + mov #12,r0 cmp/gt r6,r0 - bt L_store_byte_loop - - neg r4,r0 ! Align destination - add #4,r0 + bt/s 4f ! if it's too small, set a byte at once + mov r4,r0 and #3,r0 - tst r0,r0 - bt L_dup_bytes - .balignw 4,0x0009 -L_align_loop: - mov.b r5,@r4 - add #-1,r6 - add #1,r4 + cmp/eq #0,r0 + bt/s 2f ! It's aligned + sub r0,r6 +1: dt r0 - bf L_align_loop - -L_dup_bytes: - extu.b r5,r5 ! Duplicate bytes across longword - swap.b r5,r0 - or r0,r5 - swap.w r5,r0 - or r0,r5 - - mov r6,r2 ! Calculate number of double longwords - shlr2 r2 - shlr r2 - - .balignw 4,0x0009 -L_store_long_loop: - mov.l r5,@r4 ! Store double longs to memory - dt r2 - mov.l r5,@(4,r4) - add #8,r4 - bf L_store_long_loop - + bf/s 1b + mov.b r5,@-r4 +2: ! make VVVV + swap.b r5,r0 ! V0 + or r0,r5 ! VV + swap.w r5,r0 ! VV00 + or r0,r5 ! VVVV + ! + mov r6,r0 + shlr2 r0 + shlr r0 ! r0 = r6 >> 3 +3: + dt r0 + mov.l r5,@-r4 ! set 8-byte at once + bf/s 3b + mov.l r5,@-r4 + ! mov #7,r0 and r0,r6 tst r6,r6 - bt L_exit - .balignw 4,0x0009 -L_store_byte_loop: - mov.b r5,@r4 ! Store bytes to memory - add #1,r4 + bt 5f + ! fill bytes +4: dt r6 - bf L_store_byte_loop - -L_exit: + bf/s 4b + mov.b r5,@-r4 +5: rts - mov r3,r0 + mov r4,r0 diff --git a/arch/sh/lib/old-checksum.c b/arch/sh/lib/old-checksum.c index ae3a38043..df741335d 100644 --- a/arch/sh/lib/old-checksum.c +++ b/arch/sh/lib/old-checksum.c @@ -15,5 +15,3 @@ unsigned int csum_partial_copy( const char *src, char *dst, int len, int sum) return sum; } - - diff --git a/arch/sh/lib/wordcopy.S b/arch/sh/lib/wordcopy.S deleted file mode 100644 index c116623d0..000000000 --- a/arch/sh/lib/wordcopy.S +++ /dev/null @@ -1,1289 +0,0 @@ -#include <linux/linkage.h> -ENTRY(_wordcopy_fwd_aligned) - mov.l r14,@-r15 - add #-20,r15 - mov r15,r14 - mov.l r4,@r14 - mov.l r5,@(4,r14) - mov.l r6,@(8,r14) - mov.l @(8,r14),r2 - mov #7,r1 - and r2,r1 - mov #0,r2 - mov #7,r3 - sub r2,r1 - cmp/hi r3,r1 - bf .L29 - bra .L2 - nop -.L29: - mova .L22,r0 - add r1,r1 - mov.w @(r0,r1),r1 - add r0,r1 - jmp @r1 - nop - .align 2 - .align 2 -.L22: - .word .L15-.L22 - .word .L18-.L22 - .word .L3-.L22 - .word .L5-.L22 - .word .L7-.L22 - .word .L9-.L22 - .word .L11-.L22 - .word .L13-.L22 - .align 2 -.L3: - mov.l @(4,r14),r1 - mov.l @r1,r2 - mov.l r2,@(12,r14) - mov.l @(4,r14),r1 - mov r1,r2 - add #-24,r2 - mov.l r2,@(4,r14) - mov.l @r14,r1 - mov r1,r2 - add #-28,r2 - mov.l r2,@r14 - mov.l @(8,r14),r1 - mov r1,r2 - add #6,r2 - mov.l r2,@(8,r14) - bra .L4 - nop - .align 2 -.L5: - mov.l @(4,r14),r1 - mov.l @r1,r2 - mov.l r2,@(16,r14) - mov.l @(4,r14),r1 - mov r1,r2 - add #-20,r2 - mov.l r2,@(4,r14) - mov.l @r14,r1 - mov r1,r2 - add #-24,r2 - mov.l r2,@r14 - mov.l @(8,r14),r1 - mov r1,r2 - add #5,r2 - mov.l r2,@(8,r14) - bra .L6 - nop - .align 2 -.L7: - mov.l @(4,r14),r1 - mov.l @r1,r2 - mov.l r2,@(12,r14) - mov.l @(4,r14),r1 - mov r1,r2 - add #-16,r2 - mov.l r2,@(4,r14) - mov.l @r14,r1 - mov r1,r2 - add #-20,r2 - mov.l r2,@r14 - mov.l @(8,r14),r1 - mov r1,r2 - add #4,r2 - mov.l r2,@(8,r14) - bra .L8 - nop - .align 2 -.L9: - mov.l @(4,r14),r1 - mov.l @r1,r2 - mov.l r2,@(16,r14) - mov.l @(4,r14),r1 - mov r1,r2 - add #-12,r2 - mov.l r2,@(4,r14) - mov.l @r14,r1 - mov r1,r2 - add #-16,r2 - mov.l r2,@r14 - mov.l @(8,r14),r1 - mov r1,r2 - add #3,r2 - mov.l r2,@(8,r14) - bra .L10 - nop - .align 2 -.L11: - mov.l @(4,r14),r1 - mov.l @r1,r2 - mov.l r2,@(12,r14) - mov.l @(4,r14),r1 - mov r1,r2 - add #-8,r2 - mov.l r2,@(4,r14) - mov.l @r14,r1 - mov r1,r2 - add #-12,r2 - mov.l r2,@r14 - mov.l @(8,r14),r1 - mov r1,r2 - add #2,r2 - mov.l r2,@(8,r14) - bra .L12 - nop - .align 2 -.L13: - mov.l @(4,r14),r1 - mov.l @r1,r2 - mov.l r2,@(16,r14) - mov.l @(4,r14),r1 - mov r1,r2 - add #-4,r2 - mov.l r2,@(4,r14) - mov.l @r14,r1 - mov r1,r2 - add #-8,r2 - mov.l r2,@r14 - mov.l @(8,r14),r1 - mov r1,r2 - add #1,r2 - mov.l r2,@(8,r14) - bra .L14 - nop - .align 2 -.L15: - bra .L16 - nop - bra .L1 - nop - .align 2 -.L16: - mov.l @(4,r14),r1 - mov.l @r1,r2 - mov.l r2,@(12,r14) - mov.l @r14,r1 - mov r1,r2 - add #-4,r2 - mov.l r2,@r14 - bra .L17 - nop - .align 2 -.L18: - mov.l @(4,r14),r1 - mov.l @r1,r2 - mov.l r2,@(16,r14) - mov.l @(4,r14),r1 - mov r1,r2 - add #4,r2 - mov.l r2,@(4,r14) - mov.l @(8,r14),r1 - mov r1,r2 - add #-1,r2 - mov.l r2,@(8,r14) - bra .L19 - nop - bra .L20 - nop - .align 2 -.L19: - bra .L21 - nop - .align 2 -.L23: -.L2: - nop -.L24: -.L21: - mov.l @(4,r14),r1 - mov.l @r1,r2 - mov.l r2,@(12,r14) - mov.l @r14,r1 - mov.l @(16,r14),r2 - mov.l r2,@r1 -.L17: - mov.l @(4,r14),r2 - mov r2,r1 - add #4,r1 - mov.l @r1,r2 - mov.l r2,@(16,r14) - mov.l @r14,r2 - mov r2,r1 - add #4,r1 - mov.l @(12,r14),r2 - mov.l r2,@r1 -.L14: - mov.l @(4,r14),r2 - mov r2,r1 - add #8,r1 - mov.l @r1,r2 - mov.l r2,@(12,r14) - mov.l @r14,r2 - mov r2,r1 - add #8,r1 - mov.l @(16,r14),r2 - mov.l r2,@r1 -.L12: - mov.l @(4,r14),r2 - mov r2,r1 - add #12,r1 - mov.l @r1,r2 - mov.l r2,@(16,r14) - mov.l @r14,r2 - mov r2,r1 - add #12,r1 - mov.l @(12,r14),r2 - mov.l r2,@r1 -.L10: - mov.l @(4,r14),r2 - mov r2,r1 - add #16,r1 - mov.l @r1,r2 - mov.l r2,@(12,r14) - mov.l @r14,r2 - mov r2,r1 - add #16,r1 - mov.l @(16,r14),r2 - mov.l r2,@r1 -.L8: - mov.l @(4,r14),r2 - mov r2,r1 - add #20,r1 - mov.l @r1,r2 - mov.l r2,@(16,r14) - mov.l @r14,r2 - mov r2,r1 - add #20,r1 - mov.l @(12,r14),r2 - mov.l r2,@r1 -.L6: - mov.l @(4,r14),r2 - mov r2,r1 - add #24,r1 - mov.l @r1,r2 - mov.l r2,@(12,r14) - mov.l @r14,r2 - mov r2,r1 - add #24,r1 - mov.l @(16,r14),r2 - mov.l r2,@r1 -.L4: - mov.l @(4,r14),r2 - mov r2,r1 - add #28,r1 - mov.l @r1,r2 - mov.l r2,@(16,r14) - mov.l @r14,r2 - mov r2,r1 - add #28,r1 - mov.l @(12,r14),r2 - mov.l r2,@r1 - mov.l @(4,r14),r1 - mov r1,r2 - add #32,r2 - mov.l r2,@(4,r14) - mov.l @r14,r1 - mov r1,r2 - add #32,r2 - mov.l r2,@r14 - mov.l @(8,r14),r1 - mov r1,r2 - add #-8,r2 - mov.l r2,@(8,r14) -.L26: - mov.l @(8,r14),r1 - tst r1,r1 - bf .L27 - bra .L25 - nop - .align 2 -.L27: - bra .L21 - nop - .align 2 -.L25: - nop -.L20: - mov.l @r14,r1 - mov.l @(16,r14),r2 - mov.l r2,@r1 -.L1: - add #20,r14 - mov r14,r15 - mov.l @r15+,r14 - rts - nop -.Lfe1: - .size __wordcopy_fwd_aligned,.Lfe1-__wordcopy_fwd_aligned - .global ___lshrsi3 - .global ___ashlsi3 - .align 2 - .global __wordcopy_fwd_dest_aligned - .type __wordcopy_fwd_dest_aligned,@function -__wordcopy_fwd_dest_aligned: - mov.l r8,@-r15 - mov.l r9,@-r15 - mov.l r14,@-r15 - sts.l pr,@-r15 - add #-40,r15 - mov r15,r14 - mov.l r4,@r14 - mov.l r5,@(4,r14) - mov.l r6,@(8,r14) - mov.l @(4,r14),r1 - mov #3,r2 - and r1,r2 - mov r2,r1 - mov r1,r2 - shll2 r2 - add r2,r2 - mov.l r2,@(28,r14) - mov.l @(28,r14),r2 - neg r2,r1 - add #32,r1 - mov.l r1,@(32,r14) - mov.l @(4,r14),r1 - mov #-4,r2 - and r2,r1 - mov.l r1,@(4,r14) - mov.l @(8,r14),r2 - mov #3,r1 - and r2,r1 - mov #0,r2 - mov #3,r3 - sub r2,r1 - cmp/hi r3,r1 - bf .L53 - bra .L31 - nop -.L53: - mova .L43,r0 - add r1,r1 - mov.w @(r0,r1),r1 - add r0,r1 - jmp @r1 - nop - .align 2 - .align 2 -.L43: - .word .L36-.L43 - .word .L39-.L43 - .word .L32-.L43 - .word .L34-.L43 - .align 2 -.L32: - mov.l @(4,r14),r1 - mov.l @r1,r2 - mov.l r2,@(16,r14) - mov.l @(4,r14),r2 - mov r2,r1 - add #4,r1 - mov.l @r1,r2 - mov.l r2,@(20,r14) - mov.l @(4,r14),r1 - mov r1,r2 - add #-4,r2 - mov.l r2,@(4,r14) - mov.l @r14,r1 - mov r1,r2 - add #-12,r2 - mov.l r2,@r14 - mov.l @(8,r14),r1 - mov r1,r2 - add #2,r2 - mov.l r2,@(8,r14) - bra .L33 - nop - .align 2 -.L34: - mov.l @(4,r14),r1 - mov.l @r1,r2 - mov.l r2,@(12,r14) - mov.l @(4,r14),r2 - mov r2,r1 - add #4,r1 - mov.l @r1,r2 - mov.l r2,@(16,r14) - mov.l @r14,r1 - mov r1,r2 - add #-8,r2 - mov.l r2,@r14 - mov.l @(8,r14),r1 - mov r1,r2 - add #1,r2 - mov.l r2,@(8,r14) - bra .L35 - nop - .align 2 -.L36: - bra .L37 - nop - bra .L30 - nop - .align 2 -.L37: - mov.l @(4,r14),r1 - mov.l @r1,r2 - mov.l r2,@(24,r14) - mov.l @(4,r14),r2 - mov r2,r1 - add #4,r1 - mov.l @r1,r2 - mov.l r2,@(12,r14) - mov.l @(4,r14),r1 - mov r1,r2 - add #4,r2 - mov.l r2,@(4,r14) - mov.l @r14,r1 - mov r1,r2 - add #-4,r2 - mov.l r2,@r14 - bra .L38 - nop - .align 2 -.L39: - mov.l @(4,r14),r1 - mov.l @r1,r2 - mov.l r2,@(20,r14) - mov.l @(4,r14),r2 - mov r2,r1 - add #4,r1 - mov.l @r1,r2 - mov.l r2,@(24,r14) - mov.l @(4,r14),r1 - mov r1,r2 - add #8,r2 - mov.l r2,@(4,r14) - mov.l @(8,r14),r1 - mov r1,r2 - add #-1,r2 - mov.l r2,@(8,r14) - bra .L40 - nop - bra .L41 - nop - .align 2 -.L40: - bra .L42 - nop - .align 2 -.L44: -.L31: - nop -.L45: -.L42: - mov.l @(4,r14),r1 - mov.l @r1,r2 - mov.l r2,@(12,r14) - mov.l @r14,r8 - mov.l .L49,r1 - mov.l @(20,r14),r4 - mov.l @(28,r14),r5 - jsr @r1 - nop - mov r0,r9 - mov.l .L50,r1 - mov.l @(24,r14),r4 - mov.l @(32,r14),r5 - jsr @r1 - nop - mov.l r0,@(36,r14) - mov.l @(36,r14),r1 - or r9,r1 - mov.l r1,@r8 -.L38: - mov.l @(4,r14),r2 - mov r2,r1 - add #4,r1 - mov.l @r1,r2 - mov.l r2,@(16,r14) - mov.l @r14,r1 - mov r1,r8 - add #4,r8 - mov.l .L49,r1 - mov.l @(24,r14),r4 - mov.l @(28,r14),r5 - jsr @r1 - nop - mov r0,r9 - mov.l .L50,r1 - mov.l @(12,r14),r4 - mov.l @(32,r14),r5 - jsr @r1 - nop - mov.l r0,@(36,r14) - mov.l @(36,r14),r1 - or r9,r1 - mov.l r1,@r8 -.L35: - mov.l @(4,r14),r2 - mov r2,r1 - add #8,r1 - mov.l @r1,r2 - mov.l r2,@(20,r14) - mov.l @r14,r1 - mov r1,r8 - add #8,r8 - mov.l .L49,r1 - mov.l @(12,r14),r4 - mov.l @(28,r14),r5 - jsr @r1 - nop - mov r0,r9 - mov.l .L50,r1 - mov.l @(16,r14),r4 - mov.l @(32,r14),r5 - jsr @r1 - nop - mov.l r0,@(36,r14) - mov.l @(36,r14),r1 - or r9,r1 - mov.l r1,@r8 -.L33: - mov.l @(4,r14),r2 - mov r2,r1 - add #12,r1 - mov.l @r1,r2 - mov.l r2,@(24,r14) - mov.l @r14,r1 - mov r1,r8 - add #12,r8 - mov.l .L49,r1 - mov.l @(16,r14),r4 - mov.l @(28,r14),r5 - jsr @r1 - nop - mov r0,r9 - mov.l .L50,r1 - mov.l @(20,r14),r4 - mov.l @(32,r14),r5 - jsr @r1 - nop - mov.l r0,@(36,r14) - mov.l @(36,r14),r1 - or r9,r1 - mov.l r1,@r8 - mov.l @(4,r14),r1 - mov r1,r2 - add #16,r2 - mov.l r2,@(4,r14) - mov.l @r14,r1 - mov r1,r2 - add #16,r2 - mov.l r2,@r14 - mov.l @(8,r14),r1 - mov r1,r2 - add #-4,r2 - mov.l r2,@(8,r14) -.L47: - mov.l @(8,r14),r1 - tst r1,r1 - bf .L48 - bra .L46 - nop - .align 2 -.L48: - bra .L42 - nop - .align 2 -.L46: - nop -.L41: - mov.l @r14,r8 - mov.l .L49,r1 - mov.l @(20,r14),r4 - mov.l @(28,r14),r5 - jsr @r1 - nop - mov r0,r9 - mov.l .L50,r1 - mov.l @(24,r14),r4 - mov.l @(32,r14),r5 - jsr @r1 - nop - mov.l r0,@(36,r14) - mov.l @(36,r14),r1 - or r9,r1 - mov.l r1,@r8 -.L30: - add #40,r14 - mov r14,r15 - lds.l @r15+,pr - mov.l @r15+,r14 - mov.l @r15+,r9 - mov.l @r15+,r8 - rts - nop -.L51: - .align 2 -.L49: - .long ___lshrsi3 -.L50: - .long ___ashlsi3 -.Lfe2: - .size __wordcopy_fwd_dest_aligned,.Lfe2-__wordcopy_fwd_dest_aligned - .align 2 - .global __wordcopy_bwd_aligned - .type __wordcopy_bwd_aligned,@function -__wordcopy_bwd_aligned: - mov.l r14,@-r15 - add #-20,r15 - mov r15,r14 - mov.l r4,@r14 - mov.l r5,@(4,r14) - mov.l r6,@(8,r14) - mov.l @(8,r14),r2 - mov #7,r1 - and r2,r1 - mov #0,r2 - mov #7,r3 - sub r2,r1 - cmp/hi r3,r1 - bf .L82 - bra .L55 - nop -.L82: - mova .L75,r0 - add r1,r1 - mov.w @(r0,r1),r1 - add r0,r1 - jmp @r1 - nop - .align 2 - .align 2 -.L75: - .word .L68-.L75 - .word .L71-.L75 - .word .L56-.L75 - .word .L58-.L75 - .word .L60-.L75 - .word .L62-.L75 - .word .L64-.L75 - .word .L66-.L75 - .align 2 -.L56: - mov.l @(4,r14),r1 - mov r1,r2 - add #-8,r2 - mov.l r2,@(4,r14) - mov.l @r14,r1 - mov r1,r2 - add #-4,r2 - mov.l r2,@r14 - mov.l @(4,r14),r2 - mov r2,r1 - add #4,r1 - mov.l @r1,r2 - mov.l r2,@(12,r14) - mov.l @(8,r14),r1 - mov r1,r2 - add #6,r2 - mov.l r2,@(8,r14) - bra .L57 - nop - .align 2 -.L58: - mov.l @(4,r14),r1 - mov r1,r2 - add #-12,r2 - mov.l r2,@(4,r14) - mov.l @r14,r1 - mov r1,r2 - add #-8,r2 - mov.l r2,@r14 - mov.l @(4,r14),r2 - mov r2,r1 - add #8,r1 - mov.l @r1,r2 - mov.l r2,@(16,r14) - mov.l @(8,r14),r1 - mov r1,r2 - add #5,r2 - mov.l r2,@(8,r14) - bra .L59 - nop - .align 2 -.L60: - mov.l @(4,r14),r1 - mov r1,r2 - add #-16,r2 - mov.l r2,@(4,r14) - mov.l @r14,r1 - mov r1,r2 - add #-12,r2 - mov.l r2,@r14 - mov.l @(4,r14),r2 - mov r2,r1 - add #12,r1 - mov.l @r1,r2 - mov.l r2,@(12,r14) - mov.l @(8,r14),r1 - mov r1,r2 - add #4,r2 - mov.l r2,@(8,r14) - bra .L61 - nop - .align 2 -.L62: - mov.l @(4,r14),r1 - mov r1,r2 - add #-20,r2 - mov.l r2,@(4,r14) - mov.l @r14,r1 - mov r1,r2 - add #-16,r2 - mov.l r2,@r14 - mov.l @(4,r14),r2 - mov r2,r1 - add #16,r1 - mov.l @r1,r2 - mov.l r2,@(16,r14) - mov.l @(8,r14),r1 - mov r1,r2 - add #3,r2 - mov.l r2,@(8,r14) - bra .L63 - nop - .align 2 -.L64: - mov.l @(4,r14),r1 - mov r1,r2 - add #-24,r2 - mov.l r2,@(4,r14) - mov.l @r14,r1 - mov r1,r2 - add #-20,r2 - mov.l r2,@r14 - mov.l @(4,r14),r2 - mov r2,r1 - add #20,r1 - mov.l @r1,r2 - mov.l r2,@(12,r14) - mov.l @(8,r14),r1 - mov r1,r2 - add #2,r2 - mov.l r2,@(8,r14) - bra .L65 - nop - .align 2 -.L66: - mov.l @(4,r14),r1 - mov r1,r2 - add #-28,r2 - mov.l r2,@(4,r14) - mov.l @r14,r1 - mov r1,r2 - add #-24,r2 - mov.l r2,@r14 - mov.l @(4,r14),r2 - mov r2,r1 - add #24,r1 - mov.l @r1,r2 - mov.l r2,@(16,r14) - mov.l @(8,r14),r1 - mov r1,r2 - add #1,r2 - mov.l r2,@(8,r14) - bra .L67 - nop - .align 2 -.L68: - bra .L69 - nop - bra .L54 - nop - .align 2 -.L69: - mov.l @(4,r14),r1 - mov r1,r2 - add #-32,r2 - mov.l r2,@(4,r14) - mov.l @r14,r1 - mov r1,r2 - add #-28,r2 - mov.l r2,@r14 - mov.l @(4,r14),r2 - mov r2,r1 - add #28,r1 - mov.l @r1,r2 - mov.l r2,@(12,r14) - bra .L70 - nop - .align 2 -.L71: - mov.l @(4,r14),r1 - mov r1,r2 - add #-36,r2 - mov.l r2,@(4,r14) - mov.l @r14,r1 - mov r1,r2 - add #-32,r2 - mov.l r2,@r14 - mov.l @(4,r14),r2 - mov r2,r1 - add #32,r1 - mov.l @r1,r2 - mov.l r2,@(16,r14) - mov.l @(8,r14),r1 - mov r1,r2 - add #-1,r2 - mov.l r2,@(8,r14) - bra .L72 - nop - bra .L73 - nop - .align 2 -.L72: - bra .L74 - nop - .align 2 -.L76: -.L55: - nop -.L77: -.L74: - mov.l @(4,r14),r2 - mov r2,r1 - add #28,r1 - mov.l @r1,r2 - mov.l r2,@(12,r14) - mov.l @r14,r2 - mov r2,r1 - add #28,r1 - mov.l @(16,r14),r2 - mov.l r2,@r1 -.L70: - mov.l @(4,r14),r2 - mov r2,r1 - add #24,r1 - mov.l @r1,r2 - mov.l r2,@(16,r14) - mov.l @r14,r2 - mov r2,r1 - add #24,r1 - mov.l @(12,r14),r2 - mov.l r2,@r1 -.L67: - mov.l @(4,r14),r2 - mov r2,r1 - add #20,r1 - mov.l @r1,r2 - mov.l r2,@(12,r14) - mov.l @r14,r2 - mov r2,r1 - add #20,r1 - mov.l @(16,r14),r2 - mov.l r2,@r1 -.L65: - mov.l @(4,r14),r2 - mov r2,r1 - add #16,r1 - mov.l @r1,r2 - mov.l r2,@(16,r14) - mov.l @r14,r2 - mov r2,r1 - add #16,r1 - mov.l @(12,r14),r2 - mov.l r2,@r1 -.L63: - mov.l @(4,r14),r2 - mov r2,r1 - add #12,r1 - mov.l @r1,r2 - mov.l r2,@(12,r14) - mov.l @r14,r2 - mov r2,r1 - add #12,r1 - mov.l @(16,r14),r2 - mov.l r2,@r1 -.L61: - mov.l @(4,r14),r2 - mov r2,r1 - add #8,r1 - mov.l @r1,r2 - mov.l r2,@(16,r14) - mov.l @r14,r2 - mov r2,r1 - add #8,r1 - mov.l @(12,r14),r2 - mov.l r2,@r1 -.L59: - mov.l @(4,r14),r2 - mov r2,r1 - add #4,r1 - mov.l @r1,r2 - mov.l r2,@(12,r14) - mov.l @r14,r2 - mov r2,r1 - add #4,r1 - mov.l @(16,r14),r2 - mov.l r2,@r1 -.L57: - mov.l @(4,r14),r1 - mov.l @r1,r2 - mov.l r2,@(16,r14) - mov.l @r14,r1 - mov.l @(12,r14),r2 - mov.l r2,@r1 - mov.l @(4,r14),r1 - mov r1,r2 - add #-32,r2 - mov.l r2,@(4,r14) - mov.l @r14,r1 - mov r1,r2 - add #-32,r2 - mov.l r2,@r14 - mov.l @(8,r14),r1 - mov r1,r2 - add #-8,r2 - mov.l r2,@(8,r14) -.L79: - mov.l @(8,r14),r1 - tst r1,r1 - bf .L80 - bra .L78 - nop - .align 2 -.L80: - bra .L74 - nop - .align 2 -.L78: - nop -.L73: - mov.l @r14,r2 - mov r2,r1 - add #28,r1 - mov.l @(16,r14),r2 - mov.l r2,@r1 -.L54: - add #20,r14 - mov r14,r15 - mov.l @r15+,r14 - rts - nop -.Lfe3: - .size __wordcopy_bwd_aligned,.Lfe3-__wordcopy_bwd_aligned - .align 2 - .global __wordcopy_bwd_dest_aligned - .type __wordcopy_bwd_dest_aligned,@function -__wordcopy_bwd_dest_aligned: - mov.l r8,@-r15 - mov.l r9,@-r15 - mov.l r14,@-r15 - sts.l pr,@-r15 - add #-40,r15 - mov r15,r14 - mov.l r4,@r14 - mov.l r5,@(4,r14) - mov.l r6,@(8,r14) - mov.l @(4,r14),r1 - mov #3,r2 - and r1,r2 - mov r2,r1 - mov r1,r2 - shll2 r2 - add r2,r2 - mov.l r2,@(28,r14) - mov.l @(28,r14),r2 - neg r2,r1 - add #32,r1 - mov.l r1,@(32,r14) - mov.l @(4,r14),r1 - mov #-4,r2 - and r2,r1 - mov.l r1,@(4,r14) - mov.l @(4,r14),r1 - mov r1,r2 - add #4,r2 - mov.l r2,@(4,r14) - mov.l @(8,r14),r2 - mov #3,r1 - and r2,r1 - mov #0,r2 - mov #3,r3 - sub r2,r1 - cmp/hi r3,r1 - bf .L106 - bra .L84 - nop -.L106: - mova .L96,r0 - add r1,r1 - mov.w @(r0,r1),r1 - add r0,r1 - jmp @r1 - nop - .align 2 - .align 2 -.L96: - .word .L89-.L96 - .word .L92-.L96 - .word .L85-.L96 - .word .L87-.L96 - .align 2 -.L85: - mov.l @(4,r14),r1 - mov r1,r2 - add #-12,r2 - mov.l r2,@(4,r14) - mov.l @r14,r1 - mov r1,r2 - add #-4,r2 - mov.l r2,@r14 - mov.l @(4,r14),r2 - mov r2,r1 - add #8,r1 - mov.l @r1,r2 - mov.l r2,@(20,r14) - mov.l @(4,r14),r2 - mov r2,r1 - add #4,r1 - mov.l @r1,r2 - mov.l r2,@(16,r14) - mov.l @(8,r14),r1 - mov r1,r2 - add #2,r2 - mov.l r2,@(8,r14) - bra .L86 - nop - .align 2 -.L87: - mov.l @(4,r14),r1 - mov r1,r2 - add #-16,r2 - mov.l r2,@(4,r14) - mov.l @r14,r1 - mov r1,r2 - add #-8,r2 - mov.l r2,@r14 - mov.l @(4,r14),r2 - mov r2,r1 - add #12,r1 - mov.l @r1,r2 - mov.l r2,@(24,r14) - mov.l @(4,r14),r2 - mov r2,r1 - add #8,r1 - mov.l @r1,r2 - mov.l r2,@(20,r14) - mov.l @(8,r14),r1 - mov r1,r2 - add #1,r2 - mov.l r2,@(8,r14) - bra .L88 - nop - .align 2 -.L89: - bra .L90 - nop - bra .L83 - nop - .align 2 -.L90: - mov.l @(4,r14),r1 - mov r1,r2 - add #-20,r2 - mov.l r2,@(4,r14) - mov.l @r14,r1 - mov r1,r2 - add #-12,r2 - mov.l r2,@r14 - mov.l @(4,r14),r2 - mov r2,r1 - add #16,r1 - mov.l @r1,r2 - mov.l r2,@(12,r14) - mov.l @(4,r14),r2 - mov r2,r1 - add #12,r1 - mov.l @r1,r2 - mov.l r2,@(24,r14) - bra .L91 - nop - .align 2 -.L92: - mov.l @(4,r14),r1 - mov r1,r2 - add #-24,r2 - mov.l r2,@(4,r14) - mov.l @r14,r1 - mov r1,r2 - add #-16,r2 - mov.l r2,@r14 - mov.l @(4,r14),r2 - mov r2,r1 - add #20,r1 - mov.l @r1,r2 - mov.l r2,@(16,r14) - mov.l @(4,r14),r2 - mov r2,r1 - add #16,r1 - mov.l @r1,r2 - mov.l r2,@(12,r14) - mov.l @(8,r14),r1 - mov r1,r2 - add #-1,r2 - mov.l r2,@(8,r14) - bra .L93 - nop - bra .L94 - nop - .align 2 -.L93: - bra .L95 - nop - .align 2 -.L97: -.L84: - nop -.L98: -.L95: - mov.l @(4,r14),r2 - mov r2,r1 - add #12,r1 - mov.l @r1,r2 - mov.l r2,@(24,r14) - mov.l @r14,r1 - mov r1,r8 - add #12,r8 - mov.l .L102,r1 - mov.l @(12,r14),r4 - mov.l @(28,r14),r5 - jsr @r1 - nop - mov r0,r9 - mov.l .L103,r1 - mov.l @(16,r14),r4 - mov.l @(32,r14),r5 - jsr @r1 - nop - mov.l r0,@(36,r14) - mov.l @(36,r14),r1 - or r9,r1 - mov.l r1,@r8 -.L91: - mov.l @(4,r14),r2 - mov r2,r1 - add #8,r1 - mov.l @r1,r2 - mov.l r2,@(20,r14) - mov.l @r14,r1 - mov r1,r8 - add #8,r8 - mov.l .L102,r1 - mov.l @(24,r14),r4 - mov.l @(28,r14),r5 - jsr @r1 - nop - mov r0,r9 - mov.l .L103,r1 - mov.l @(12,r14),r4 - mov.l @(32,r14),r5 - jsr @r1 - nop - mov.l r0,@(36,r14) - mov.l @(36,r14),r1 - or r9,r1 - mov.l r1,@r8 -.L88: - mov.l @(4,r14),r2 - mov r2,r1 - add #4,r1 - mov.l @r1,r2 - mov.l r2,@(16,r14) - mov.l @r14,r1 - mov r1,r8 - add #4,r8 - mov.l .L102,r1 - mov.l @(20,r14),r4 - mov.l @(28,r14),r5 - jsr @r1 - nop - mov r0,r9 - mov.l .L103,r1 - mov.l @(24,r14),r4 - mov.l @(32,r14),r5 - jsr @r1 - nop - mov.l r0,@(36,r14) - mov.l @(36,r14),r1 - or r9,r1 - mov.l r1,@r8 -.L86: - mov.l @(4,r14),r1 - mov.l @r1,r2 - mov.l r2,@(12,r14) - mov.l @r14,r8 - mov.l .L102,r1 - mov.l @(16,r14),r4 - mov.l @(28,r14),r5 - jsr @r1 - nop - mov r0,r9 - mov.l .L103,r1 - mov.l @(20,r14),r4 - mov.l @(32,r14),r5 - jsr @r1 - nop - mov.l r0,@(36,r14) - mov.l @(36,r14),r1 - or r9,r1 - mov.l r1,@r8 - mov.l @(4,r14),r1 - mov r1,r2 - add #-16,r2 - mov.l r2,@(4,r14) - mov.l @r14,r1 - mov r1,r2 - add #-16,r2 - mov.l r2,@r14 - mov.l @(8,r14),r1 - mov r1,r2 - add #-4,r2 - mov.l r2,@(8,r14) -.L100: - mov.l @(8,r14),r1 - tst r1,r1 - bf .L101 - bra .L99 - nop - .align 2 -.L101: - bra .L95 - nop - .align 2 -.L99: - nop -.L94: - mov.l @r14,r1 - mov r1,r8 - add #12,r8 - mov.l .L102,r1 - mov.l @(12,r14),r4 - mov.l @(28,r14),r5 - jsr @r1 - nop - mov r0,r9 - mov.l .L103,r1 - mov.l @(16,r14),r4 - mov.l @(32,r14),r5 - jsr @r1 - nop - mov.l r0,@(36,r14) - mov.l @(36,r14),r1 - or r9,r1 - mov.l r1,@r8 -.L83: - add #40,r14 - mov r14,r15 - lds.l @r15+,pr - mov.l @r15+,r14 - mov.l @r15+,r9 - mov.l @r15+,r8 - rts - nop -.L104: - .align 2 -.L102: - .long ___lshrsi3 -.L103: - .long ___ashlsi3 -.Lfe4: diff --git a/arch/sh/mm/Makefile b/arch/sh/mm/Makefile index c89c2b9e3..981c25380 100644 --- a/arch/sh/mm/Makefile +++ b/arch/sh/mm/Makefile @@ -8,6 +8,6 @@ # Note 2! The CFLAGS definition is now in the main makefile... O_TARGET := mm.o -O_OBJS := init.o fault.o ioremap.o extable.o +O_OBJS := init.o fault.o ioremap.o extable.o cache.o include $(TOPDIR)/Rules.make diff --git a/arch/sh/mm/cache.c b/arch/sh/mm/cache.c new file mode 100644 index 000000000..129aba7cc --- /dev/null +++ b/arch/sh/mm/cache.c @@ -0,0 +1,366 @@ +/* $Id$ + * + * linux/arch/sh/mm/cache.c + * + * Copyright (C) 1999 Niibe Yutaka + * + */ + +#include <linux/init.h> +#include <linux/mman.h> +#include <linux/mm.h> +#include <linux/threads.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/processor.h> +#include <asm/cache.h> +#include <asm/io.h> +#include <asm/uaccess.h> + +#if defined(__sh3__) +#define CCR 0xffffffec /* Address of Cache Control Register */ +#define CCR_CACHE_VAL 0x00000005 /* 8k-byte cache, P1-wb, enable */ +#define CCR_CACHE_INIT 0x0000000d /* 8k-byte cache, CF, P1-wb, enable */ +#define CCR_CACHE_ENABLE 1 + +#define CACHE_IC_ADDRESS_ARRAY 0xf0000000 /* SH-3 has unified cache system */ +#define CACHE_OC_ADDRESS_ARRAY 0xf0000000 +#define CACHE_VALID 1 +#define CACHE_UPDATED 2 + +/* 7709A/7729 has 16K cache (256-entry), while 7702 has only 2K(direct) + 7702 is not supported (yet) */ +struct _cache_system_info { + int way_shift; + int entry_mask; + int num_entries; +}; + +static struct _cache_system_info cache_system_info; + +#define CACHE_OC_WAY_SHIFT (cache_system_info.way_shift) +#define CACHE_IC_WAY_SHIFT (cache_system_info.way_shift) +#define CACHE_OC_ENTRY_SHIFT 4 +#define CACHE_OC_ENTRY_MASK (cache_system_info.entry_mask) +#define CACHE_IC_ENTRY_MASK (cache_system_info.entry_mask) +#define CACHE_OC_NUM_ENTRIES (cache_system_info.num_entries) +#define CACHE_OC_NUM_WAYS 4 +#define CACHE_IC_NUM_WAYS 4 +#elif defined(__SH4__) +#define CCR 0xff00001c /* Address of Cache Control Register */ +#define CCR_CACHE_VAL 0x00000105 /* 8k+16k-byte cache,P1-wb,enable */ +#define CCR_CACHE_INIT 0x0000090d /* 8k+16k-byte cache,CF,P1-wb,enable */ +#define CCR_CACHE_ENABLE 0x00000101 + +#define CACHE_IC_ADDRESS_ARRAY 0xf0000000 +#define CACHE_OC_ADDRESS_ARRAY 0xf4000000 +#define CACHE_VALID 1 +#define CACHE_UPDATED 2 + +#define CACHE_OC_WAY_SHIFT 13 +#define CACHE_IC_WAY_SHIFT 13 +#define CACHE_OC_ENTRY_SHIFT 5 +#define CACHE_OC_ENTRY_MASK 0x3fe0 +#define CACHE_IC_ENTRY_MASK 0x1fe0 +#define CACHE_OC_NUM_ENTRIES 512 +#define CACHE_OC_NUM_WAYS 1 +#define CACHE_IC_NUM_WAYS 1 +#endif + +#define jump_to_p2(__dummy) \ + asm volatile("mova 1f,%0\n\t" \ + "add %1,%0\n\t" \ + "jmp @r0 ! Jump to P2 area\n\t" \ + " nop\n\t" \ + ".balign 4\n" \ + "1:" \ + : "=&z" (__dummy) \ + : "r" (0x20000000)) + +#define back_to_p1(__dummy) \ + asm volatile("nop;nop;nop;nop;nop;nop\n\t" \ + "mova 9f,%0\n\t" \ + "sub %1,%0\n\t" \ + "jmp @r0 ! Back to P1 area\n\t" \ + " nop\n\t" \ + ".balign 4\n" \ + "9:" \ + : "=&z" (__dummy) \ + : "r" (0x20000000), "0" (__dummy)) + +/* Write back caches to memory (if needed) and invalidates the caches */ +void cache_flush_area(unsigned long start, unsigned long end) +{ + unsigned long flags, __dummy; + unsigned long addr, data, v, p; + + start &= ~(L1_CACHE_BYTES-1); + save_and_cli(flags); + jump_to_p2(__dummy); + + for (v = start; v < end; v+=L1_CACHE_BYTES) { + p = __pa(v); + addr = CACHE_IC_ADDRESS_ARRAY | + (v&CACHE_IC_ENTRY_MASK) | 0x8 /* A-bit */; + data = (v&0xfffffc00); /* U=0, V=0 */ + ctrl_outl(data,addr); +#if CACHE_IC_ADDRESS_ARRAY != CACHE_OC_ADDRESS_ARRAY + asm volatile("ocbp %0" + : /* no output */ + : "m" (__m(v))); +#endif + } + back_to_p1(__dummy); + restore_flags(flags); +} + +/* Purge (just invalidate, no write back) the caches */ +/* This is expected to work well.. but.. + + On SH7708S, the write-back cache is written back on "purge". + (it's not expected, though). + + It seems that we have no way to just purge (with no write back action) + the cache line. */ +void cache_purge_area(unsigned long start, unsigned long end) +{ + unsigned long flags, __dummy; + unsigned long addr, data, v, p, j; + + start &= ~(L1_CACHE_BYTES-1); + save_and_cli(flags); + jump_to_p2(__dummy); + + for (v = start; v < end; v+=L1_CACHE_BYTES) { + p = __pa(v); + for (j=0; j<CACHE_IC_NUM_WAYS; j++) { + addr = CACHE_IC_ADDRESS_ARRAY|(j<<CACHE_IC_WAY_SHIFT)| + (v&CACHE_IC_ENTRY_MASK); + data = ctrl_inl(addr); + if ((data & 0xfffffc00) == (p&0xfffffc00) + && (data & CACHE_VALID)) { + data &= ~CACHE_VALID; + ctrl_outl(data,addr); + break; + } + } +#if CACHE_IC_ADDRESS_ARRAY != CACHE_OC_ADDRESS_ARRAY + asm volatile("ocbi %0" + : /* no output */ + : "m" (__m(v))); +#endif + } + back_to_p1(__dummy); + restore_flags(flags); +} + +/* write back the dirty cache, but not invalidate the cache */ +void cache_wback_area(unsigned long start, unsigned long end) +{ + unsigned long flags, __dummy; + unsigned long v; + + start &= ~(L1_CACHE_BYTES-1); + save_and_cli(flags); + jump_to_p2(__dummy); + + for (v = start; v < end; v+=L1_CACHE_BYTES) { +#if CACHE_IC_ADDRESS_ARRAY == CACHE_OC_ADDRESS_ARRAY + unsigned long addr, data, j; + unsigned long p = __pa(v); + + for (j=0; j<CACHE_OC_NUM_WAYS; j++) { + addr = CACHE_OC_ADDRESS_ARRAY|(j<<CACHE_OC_WAY_SHIFT)| + (v&CACHE_OC_ENTRY_MASK); + data = ctrl_inl(addr); + if ((data & 0xfffffc00) == (p&0xfffffc00) + && (data & CACHE_VALID) + && (data & CACHE_UPDATED)) { + data &= ~CACHE_UPDATED; + ctrl_outl(data,addr); + break; + } + } +#else + asm volatile("ocbwb %0" + : /* no output */ + : "m" (__m(v))); +#endif + } + back_to_p1(__dummy); + restore_flags(flags); +} + +/* + * Write back the cache. + * + * For SH-4, flush (write back) Operand Cache, as Instruction Cache + * doesn't have "updated" data. + */ +static void cache_wback_all(void) +{ + unsigned long flags, __dummy; + unsigned long addr, data, i, j; + + save_and_cli(flags); + jump_to_p2(__dummy); + + for (i=0; i<CACHE_OC_NUM_ENTRIES; i++) { + for (j=0; j<CACHE_OC_NUM_WAYS; j++) { + addr = CACHE_OC_ADDRESS_ARRAY|(j<<CACHE_OC_WAY_SHIFT)| + (i<<CACHE_OC_ENTRY_SHIFT); + data = ctrl_inl(addr); + if (data & CACHE_VALID) { + data &= ~(CACHE_VALID|CACHE_UPDATED); + ctrl_outl(data,addr); + } + } + } + + back_to_p1(__dummy); + restore_flags(flags); +} + +static void +detect_cpu_and_cache_system(void) +{ +#if defined(__sh3__) + unsigned long __dummy, addr0, addr1, data0, data1, data2, data3; + + jump_to_p2(__dummy); + /* Check if the entry shadows or not. + * When shadowed, it's 128-entry system. + * Otherwise, it's 256-entry system. + */ + addr0 = CACHE_OC_ADDRESS_ARRAY + (3 << 12); + addr1 = CACHE_OC_ADDRESS_ARRAY + (1 << 12); + data0 = ctrl_inl(addr0); + data0 ^= 0x00000001; + ctrl_outl(data0,addr0); + data1 = ctrl_inl(addr1); + data2 = data1 ^ 0x00000001; + ctrl_outl(data2,addr1); + data3 = ctrl_inl(addr0); + + /* Invaliate them, in case the cache has been enabled already. */ + ctrl_outl(data0&~0x00000001,addr0); + ctrl_outl(data2&~0x00000001,addr1); + back_to_p1(__dummy); + + if (data0 == data1 && data2 == data3) { /* Shadow */ + cache_system_info.way_shift = 11; + cache_system_info.entry_mask = 0x7f0; + cache_system_info.num_entries = 128; + cpu_data->type = CPU_SH7708; + } else { /* 7709A or 7729 */ + cache_system_info.way_shift = 12; + cache_system_info.entry_mask = 0xff0; + cache_system_info.num_entries = 256; + cpu_data->type = CPU_SH7729; + } +#elif defined(__SH4__) + cpu_data->type = CPU_SH7750; +#endif +} + +void __init cache_init(void) +{ + unsigned long __dummy, ccr; + + detect_cpu_and_cache_system(); + + ccr = ctrl_inl(CCR); + if (ccr == CCR_CACHE_VAL) + return; + if (ccr & CCR_CACHE_ENABLE) + /* Should check RA here. If RA was 1, + we only need to flush the half of the caches. */ + cache_wback_all(); + + jump_to_p2(__dummy); + ctrl_outl(CCR_CACHE_INIT, CCR); + back_to_p1(__dummy); +} + +#if defined(__SH4__) +/* Write back data caches, and invalidates instructiin caches */ +void flush_icache_range(unsigned long start, unsigned long end) +{ + unsigned long flags, __dummy; + unsigned long addr, data, v; + + start &= ~(L1_CACHE_BYTES-1); + save_and_cli(flags); + jump_to_p2(__dummy); + + for (v = start; v < end; v+=L1_CACHE_BYTES) { + /* Write back O Cache */ + asm volatile("ocbwb %0" + : /* no output */ + : "m" (__m(v))); + /* Invalidate I Cache */ + addr = CACHE_IC_ADDRESS_ARRAY | + (v&CACHE_IC_ENTRY_MASK) | 0x8 /* A-bit */; + data = (v&0xfffffc00); /* Valid=0 */ + ctrl_outl(data,addr); + } + back_to_p1(__dummy); + restore_flags(flags); +} + +void flush_cache_all(void) +{ + unsigned long flags,__dummy; + + /* Write back Operand Cache */ + cache_wback_all (); + + /* Then, invalidate Instruction Cache and Operand Cache */ + save_and_cli(flags); + jump_to_p2(__dummy); + ctrl_outl(CCR_CACHE_INIT, CCR); + back_to_p1(__dummy); + restore_flags(flags); +} + +void flush_cache_mm(struct mm_struct *mm) +{ + /* Is there any good way? */ + /* XXX: possibly call flush_cache_range for each vm area */ + flush_cache_all(); +} + +void flush_cache_range(struct mm_struct *mm, unsigned long start, + unsigned long end) +{ + unsigned long flags, __dummy; + unsigned long addr, data, v; + + start &= ~(L1_CACHE_BYTES-1); + save_and_cli(flags); + jump_to_p2(__dummy); + + for (v = start; v < end; v+=L1_CACHE_BYTES) { + addr = CACHE_IC_ADDRESS_ARRAY | + (v&CACHE_IC_ENTRY_MASK) | 0x8 /* A-bit */; + data = (v&0xfffffc00); /* Update=0, Valid=0 */ + ctrl_outl(data,addr); + addr = CACHE_OC_ADDRESS_ARRAY | + (v&CACHE_OC_ENTRY_MASK) | 0x8 /* A-bit */; + ctrl_outl(data,addr); + } + back_to_p1(__dummy); + restore_flags(flags); +} + +void flush_cache_page(struct vm_area_struct *vma, unsigned long addr) +{ + flush_cache_range(vma->vm_mm, addr, addr+PAGE_SIZE); +} + +void flush_page_to_ram(unsigned long page) +{ /* Page is in physical address */ + /* XXX: for the time being... */ + flush_cache_all(); +} +#endif diff --git a/arch/sh/mm/extable.c b/arch/sh/mm/extable.c index 35c4451eb..72fcfee4a 100644 --- a/arch/sh/mm/extable.c +++ b/arch/sh/mm/extable.c @@ -1,4 +1,5 @@ -/* +/* $Id$ + * * linux/arch/sh/mm/extable.c * Taken from: * linux/arch/i386/mm/extable.c diff --git a/arch/sh/mm/fault.c b/arch/sh/mm/fault.c index c1348d5b4..93c352679 100644 --- a/arch/sh/mm/fault.c +++ b/arch/sh/mm/fault.c @@ -1,4 +1,5 @@ -/* +/* $Id$ + * * linux/arch/sh/mm/fault.c * Copyright (C) 1999 Niibe Yutaka * @@ -20,6 +21,7 @@ #include <linux/interrupt.h> #include <asm/system.h> +#include <asm/io.h> #include <asm/uaccess.h> #include <asm/pgtable.h> #include <asm/hardirq.h> @@ -225,20 +227,23 @@ void update_mmu_cache(struct vm_area_struct * vma, asid = get_asid(); - save_and_cli(flags); address &= PAGE_MASK; +#if 0/*defined(__SH4__)*//* SH-4 has separate I/D caches: XXX really needed? */ + if ((vma->vm_flags & VM_EXEC) != 0) +/* && + ((pte_val(pte) & (_PAGE_PRESENT | _PAGE_DIRTY)) == + (_PAGE_PRESENT | _PAGE_DIRTY))) */ + flush_icache_range(address,address+PAGE_SIZE); +#endif + save_and_cli(flags); /* Set PTEH register */ - asm volatile ("mov.l %0,%1" - : /* no output */ - : "r" (address | asid), "m" (__m(MMU_PTEH))); + ctrl_outl((address|asid), MMU_PTEH); pteval = pte_val(pte); pteval &= _PAGE_FLAGS_HARDWARE_MASK; /* drop software flags */ pteval |= _PAGE_FLAGS_HARDWARE_DEFAULT; /* add default flags */ /* Set PTEL register */ - asm volatile ("mov.l %0,%1" - : /* no output */ - : "r" (pteval), "m" (__m(MMU_PTEL))); + ctrl_outl(pteval, MMU_PTEL); /* Load the TLB */ asm volatile ("ldtlb" : /* no output */ : /* no input */ @@ -250,11 +255,27 @@ static __inline__ void __flush_tlb_page(unsigned long asid, unsigned long page) { unsigned long addr, data; +#if defined(__sh3__) addr = MMU_TLB_ADDRESS_ARRAY | (page & 0x1F000) | MMU_PAGE_ASSOC_BIT; data = page | asid; /* VALID bit is off */ - __asm__ __volatile__ ("mov.l %0,%1" - : /* no output */ - : "r" (data), "m" (__m(addr))); + ctrl_outl(data, addr); +#elif defined(__SH4__) + int i; + + addr = MMU_UTLB_ADDRESS_ARRAY | MMU_PAGE_ASSOC_BIT; + data = page | asid; /* VALID bit is off */ + ctrl_outl(data, addr); + + for (i=0; i<4; i++) { + addr = MMU_ITLB_ADDRESS_ARRAY | (i<<8); + data = ctrl_inl(addr); + data &= ~0x30; + if (data == (page | asid)) { + ctrl_outl(data, addr); + break; + } + } +#endif } void flush_tlb_page(struct vm_area_struct *vma, unsigned long page) @@ -262,9 +283,13 @@ void flush_tlb_page(struct vm_area_struct *vma, unsigned long page) unsigned long asid; if (vma->vm_mm->context != NO_CONTEXT) { + unsigned long flags; + page &= PAGE_MASK; asid = vma->vm_mm->context & MMU_CONTEXT_ASID_MASK; + save_and_cli(flags); __flush_tlb_page (asid, page); + restore_flags(flags); } } @@ -277,7 +302,7 @@ void flush_tlb_range(struct mm_struct *mm, unsigned long start, save_and_cli(flags); size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; - if (size > (MMU_NTLB_ENTRIES/4)) { /* So many TLB to flush */ + if (size > (MMU_NTLB_ENTRIES/4)) { /* Too many TLB to flush */ get_new_mmu_context(mm); if (mm == current->mm) set_asid(mm->context & MMU_CONTEXT_ASID_MASK); @@ -314,13 +339,11 @@ void flush_tlb_mm(struct mm_struct *mm) void flush_tlb_all(void) { - unsigned long flags, __dummy; + unsigned long flags, status; save_and_cli(flags); - asm volatile("mov.l %1,%0\n\t" - "or #4,%0\n\t" /* Set TF-bit to flush */ - "mov.l %0,%1" - : "=&z" (__dummy) - : "m" (__m(MMUCR))); + status = ctrl_inl(MMUCR); + status |= 0x04; /* Set TF-bit to flush */ + ctrl_outl(status,MMUCR); restore_flags(flags); } diff --git a/arch/sh/mm/init.c b/arch/sh/mm/init.c index 3a6bfc1a2..4c602a385 100644 --- a/arch/sh/mm/init.c +++ b/arch/sh/mm/init.c @@ -1,4 +1,5 @@ -/* +/* $Id$ + * * linux/arch/sh/mm/init.c * * Copyright (C) 1999 Niibe Yutaka @@ -29,6 +30,7 @@ #include <asm/uaccess.h> #include <asm/pgtable.h> #include <asm/mmu_context.h> +#include <asm/io.h> /* * Cache of MMU context last used. @@ -127,7 +129,7 @@ int do_check_pgt_cache(int low, int high) */ pte_t * __bad_pagetable(void) { - extern char empty_bad_page_table[PAGE_SIZE]; + extern unsigned long empty_bad_page_table[PAGE_SIZE]; unsigned long page = (unsigned long)empty_bad_page_table; clear_page(page); @@ -202,16 +204,17 @@ paging_init(unsigned long start_mem, unsigned long end_mem) pgd_val(pg_dir[0]) = 0; /* Enable MMU */ - __asm__ __volatile__ ("mov.l %0,%1" - : /* no output */ - : "r" (MMU_CONTROL_INIT), "m" (__m(MMUCR))); + ctrl_outl(MMU_CONTROL_INIT, MMUCR); + + mmu_context_cache = MMU_CONTEXT_FIRST_VERSION; + set_asid(mmu_context_cache & MMU_CONTEXT_ASID_MASK); return free_area_init(start_mem, end_mem); } unsigned long empty_bad_page[1024]; unsigned long empty_bad_page_table[1024]; -unsigned long empty_zero_page[1024]; +extern unsigned long empty_zero_page[1024]; void __init mem_init(unsigned long start_mem, unsigned long end_mem) { diff --git a/arch/sh/mm/ioremap.c b/arch/sh/mm/ioremap.c index f786379cf..d86d29279 100644 --- a/arch/sh/mm/ioremap.c +++ b/arch/sh/mm/ioremap.c @@ -1,4 +1,5 @@ -/* +/* $Id$ + * * arch/sh/mm/ioremap.c * * Re-map IO memory to kernel address space so that we can access it. diff --git a/arch/sh/vmlinux.lds.S b/arch/sh/vmlinux.lds.S index a812c91f8..374cb4ad7 100644 --- a/arch/sh/vmlinux.lds.S +++ b/arch/sh/vmlinux.lds.S @@ -1,4 +1,5 @@ -/* ld script to make SuperH Linux kernel +/* $Id$ + * ld script to make SuperH Linux kernel * Written by Niibe Yutaka */ #include <linux/config.h> @@ -15,6 +16,7 @@ SECTIONS __text = .; /* Text and read-only data */ _text = .; /* Text and read-only data */ .text : { + *(.empty_zero_page) *(.text) *(.fixup) *(.gnu.warning) @@ -66,9 +68,6 @@ SECTIONS . = ALIGN(32); .data.cacheline_aligned : { *(.data.cacheline_aligned) } - . = ALIGN(4096); - .data.disk_image : { *(.data.disk_image) } - . = ALIGN(4); ___bss_start = .; /* BSS */ .bss : { diff --git a/arch/sparc/mm/init.c b/arch/sparc/mm/init.c index 69c69d212..40aab1d66 100644 --- a/arch/sparc/mm/init.c +++ b/arch/sparc/mm/init.c @@ -106,6 +106,7 @@ void show_mem(void) printk("%ld page tables cached\n",pgtable_cache_size); if (sparc_cpu_model == sun4m || sparc_cpu_model == sun4d) printk("%ld page dirs cached\n", pgd_cache_size); + show_buffers(); #ifdef CONFIG_NET show_net_buffers(); #endif diff --git a/arch/sparc64/mm/init.c b/arch/sparc64/mm/init.c index c1d8d24ae..6df374b4e 100644 --- a/arch/sparc64/mm/init.c +++ b/arch/sparc64/mm/init.c @@ -150,6 +150,7 @@ void show_mem(void) #ifndef __SMP__ printk("%d entries in page dir cache\n",pgd_cache_size); #endif + show_buffers(); #ifdef CONFIG_NET show_net_buffers(); #endif diff --git a/drivers/acorn/Makefile b/drivers/acorn/Makefile new file mode 100644 index 000000000..53b3b975a --- /dev/null +++ b/drivers/acorn/Makefile @@ -0,0 +1,12 @@ +# +# Makefile for the Acorn-specific Linux kernel 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 (not a .c file). + +SUB_DIRS := block char net scsi +MOD_SUB_DIRS := $(SUB_DIRS) +ALL_SUB_DIRS := $(SUB_DIRS) + +include $(TOPDIR)/Rules.make diff --git a/drivers/acorn/README b/drivers/acorn/README index 283bfa5be..d399c09ca 100644 --- a/drivers/acorn/README +++ b/drivers/acorn/README @@ -1,3 +1 @@ Drivers for the ACORN "podule" ARM specific bus. - - diff --git a/drivers/acorn/block/Makefile b/drivers/acorn/block/Makefile index 4db3192b6..9f5953b39 100644 --- a/drivers/acorn/block/Makefile +++ b/drivers/acorn/block/Makefile @@ -32,17 +32,11 @@ endif include $(TOPDIR)/Rules.make +.S.o: + $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c -o $*.o $< + fd1772_mod.o: $(FLOPPY) $(LD) -r -o $@ $(FLOPPY) mfmhd_mod.o: mfmhd.o mfm.o $(LD) -r -o $@ mfmhd.o mfm.o - -%.o: %.S -ifndef $(CONFIG_BINUTILS_NEW) - $(CC) $(CFLAGS) -D__ASSEMBLY__ -E $< | tr ';$$' '\n#' > ..tmp.s - $(CC) $(CFLAGS) -c -o $@ ..tmp.s - $(RM) ..tmp.s -else - $(CC) $(CFLAGS) -D__ASSEMBLY__ -c -o $@ $< -endif diff --git a/drivers/acorn/char/keyb_arc.c b/drivers/acorn/char/keyb_arc.c index 282f151f4..44395fd97 100644 --- a/drivers/acorn/char/keyb_arc.c +++ b/drivers/acorn/char/keyb_arc.c @@ -31,7 +31,7 @@ #include <asm/ioc.h> #include <asm/hardware.h> -#include "../../char/mouse.h" +#include "../../char/busmouse.h" extern void kbd_reset_kdown(void); @@ -47,7 +47,7 @@ static char kbd_txval[4]; static unsigned char kbd_txhead, kbd_txtail; #define KBD_INCTXPTR(ptr) ((ptr) = ((ptr) + 1) & 3) static int kbd_id = -1; -static struct wait_queue *kbd_waitq; +static DECLARE_WAIT_QUEUE_HEAD(kbd_waitq); #ifdef CONFIG_KBDMOUSE static int mousedev; #endif diff --git a/drivers/acorn/net/ether1.c b/drivers/acorn/net/ether1.c index a719b4563..5c8b0e26d 100644 --- a/drivers/acorn/net/ether1.c +++ b/drivers/acorn/net/ether1.c @@ -255,7 +255,7 @@ ether1_readbuffer (struct net_device *dev, void *data, unsigned int start, unsig } static int __init -ether1_ramtest (struct net_device *dev, unsigned char byte) +ether1_ramtest(struct net_device *dev, unsigned char byte) { unsigned char *buffer = kmalloc (BUFFER_SIZE, GFP_KERNEL); int i, ret = BUFFER_SIZE; @@ -309,7 +309,7 @@ ether1_reset (struct net_device *dev) } static int __init -ether1_init_2 (struct net_device *dev) +ether1_init_2(struct net_device *dev) { int i; dev->mem_start = 0; @@ -613,7 +613,7 @@ ether1_init_for_open (struct net_device *dev) } static int __init -ether1_probe1 (struct net_device *dev) +ether1_probe1(struct net_device *dev) { static unsigned int version_printed = 0; struct ether1_priv *priv; @@ -665,7 +665,7 @@ ether1_probe1 (struct net_device *dev) /* ------------------------------------------------------------------------- */ static void __init -ether1_addr (struct net_device *dev) +ether1_addr(struct net_device *dev) { int i; @@ -674,7 +674,7 @@ ether1_addr (struct net_device *dev) } int __init -ether1_probe (struct net_device *dev) +ether1_probe(struct net_device *dev) { #ifndef MODULE struct expansion_card *ec; diff --git a/drivers/acorn/net/net-probe.c b/drivers/acorn/net/net-probe.c deleted file mode 100644 index e4629dfd2..000000000 --- a/drivers/acorn/net/net-probe.c +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Acorn specific net device driver probe routine - * - * Copyright (C) 1998 Russell King - */ -#include <linux/config.h> -#include <linux/netdevice.h> -#include <linux/errno.h> -#include <linux/init.h> - -extern int ether1_probe (struct net_device *dev); -extern int ether3_probe (struct net_device *dev); -extern int etherh_probe (struct net_device *dev); - -int __init acorn_ethif_probe(struct net_device *dev) -{ - if (1 -#ifdef CONFIG_ARM_ETHERH - && etherh_probe (dev) -#endif -#ifdef CONFIG_ARM_ETHER3 - && ether3_probe (dev) -#endif -#ifdef CONFIG_ARM_ETHER1 - && ether1_probe (dev) -#endif - && 1) { - return 1; - } - return 0; -} diff --git a/drivers/acorn/scsi/Makefile b/drivers/acorn/scsi/Makefile index 6efd22681..ee7152a44 100644 --- a/drivers/acorn/scsi/Makefile +++ b/drivers/acorn/scsi/Makefile @@ -108,14 +108,8 @@ endif include $(TOPDIR)/Rules.make +.S.o: + $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c -o $*.o $< + acornscsi_mod.o: acornscsi.o acornscsi-io.o $(LD) $(LD_RFLAG) -r -o $@ acornscsi.o acornscsi-io.o - -%.o: %.S -ifndef $(CONFIG_BINUTILS_NEW) - $(CC) $(CFLAGS) -D__ASSEMBLY__ -E $< | tr ';$$' '\n#' > ..tmp.$<.s - $(CC) $(CFLAGS:-pipe=) -c -o $@ ..tmp.$<.s - $(RM) ..tmp.$<.s -else - $(CC) $(CFLAGS) -D__ASSEMBLY__ -c -o $@ $< -endif diff --git a/drivers/block/Config.in b/drivers/block/Config.in index c23b8d094..7f70e32ee 100644 --- a/drivers/block/Config.in +++ b/drivers/block/Config.in @@ -62,17 +62,15 @@ else fi if [ "$CONFIG_BLK_DEV_IDEDMA_PCI" = "y" ]; then bool ' HPT34X chipset support' CONFIG_BLK_DEV_HPT34X - if [ "$CONFIG_IDEDMA_PCI_EXPERIMENTAL" = "y" -a \ - "$CONFIG_BLK_DEV_HPT34X" = "y" ]; then - bool ' HPT34X DMA support (DANGEROUS)' CONFIG_BLK_DEV_HPT34X_DMA + if [ "$CONFIG_IDEDMA_PCI_EXPERIMENTAL" = "y" -a "$CONFIG_BLK_DEV_HPT34X" = "y" ]; then + bool ' HPT34X DMA support (EXPERIMENTAL)' CONFIG_BLK_DEV_HPT34X_DMA fi bool ' HPT366 chipset support' CONFIG_BLK_DEV_HPT366 fi if [ "$CONFIG_X86" = "y" ]; then bool ' Intel PIIXn chipsets support' CONFIG_BLK_DEV_PIIX - if [ "$CONFIG_IDEDMA_PCI_EXPERIMENTAL" = "y" -a \ - "$CONFIG_BLK_DEV_PIIX" = "y" ]; then - bool ' PIIXn Tuning support (EXPERIMENTAL)' CONFIG_BLK_DEV_PIIX_TUNING + if [ "$CONFIG_BLK_DEV_PIIX" = "y" ]; then + bool ' PIIXn Tuning support' CONFIG_BLK_DEV_PIIX_TUNING fi fi if [ "$CONFIG_IDEDMA_PCI_EXPERIMENTAL" = "y" ]; then @@ -83,19 +81,20 @@ else fi if [ "$CONFIG_BLK_DEV_IDEDMA_PCI" = "y" ]; then bool ' PROMISE PDC20246/PDC20262 support' CONFIG_BLK_DEV_PDC202XX - if [ "$CONFIG_EXPERIMENTAL" = "y" -a \ - "$CONFIG_BLK_DEV_PDC202XX" = "y" ]; then - bool ' Special UDMA Feature (EXPERIMENTAL)' CONFIG_PDC202XX_FORCE_BURST_BIT - bool ' Special Mode Feature (DANGEROUS)' CONFIG_PDC202XX_FORCE_MASTER_MODE + if [ "$CONFIG_BLK_DEV_PDC202XX" = "y" ]; then + bool ' Special UDMA Feature' CONFIG_PDC202XX_FORCE_BURST_BIT + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + bool ' Special Mode Feature (EXPERIMENTAL)' CONFIG_PDC202XX_FORCE_MASTER_MODE + fi + fi + if [ "$CONFIG_X86" = "y" ]; then + bool ' SiS5513 chipset support' CONFIG_BLK_DEV_SIS5513 fi - fi - if [ "$CONFIG_X86" = "y" ]; then - bool ' SiS5513 chipset support' CONFIG_BLK_DEV_SIS5513 fi if [ "$CONFIG_IDEDMA_PCI_EXPERIMENTAL" = "y" ]; then bool ' Tekram TRM290 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_TRM290 if [ "$CONFIG_X86" = "y" ]; then - bool ' VIA82C586 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_VIA82C586 + bool ' VIA82CXXX chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_VIA82CXXX fi fi fi @@ -139,8 +138,7 @@ else bool ' ALI M14xx support' CONFIG_BLK_DEV_ALI14XX bool ' DTC-2278 support' CONFIG_BLK_DEV_DTC2278 bool ' Holtek HT6560B support' CONFIG_BLK_DEV_HT6560B - if [ "$CONFIG_BLK_DEV_IDEDISK" = "y" -a \ - "$CONFIG_EXPERIMENTAL" = "y" ]; then + if [ "$CONFIG_BLK_DEV_IDEDISK" = "y" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then bool ' PROMISE DC4030 support (EXPERIMENTAL)' CONFIG_BLK_DEV_PDC4030 fi bool ' QDI QD6580 support' CONFIG_BLK_DEV_QD6580 @@ -225,6 +223,7 @@ if [ "$CONFIG_IDE_CHIPSETS" = "y" -o \ "$CONFIG_BLK_DEV_HPT366" = "y" -o \ "$CONFIG_BLK_DEV_IDE_PMAC" = "y" -o \ "$CONFIG_BLK_DEV_OPTI621" = "y" -o \ + "$CONFIG_BLK_DEV_PDC202XX" = "y" -o \ "$CONFIG_BLK_DEV_PIIX" = "y" -o \ "$CONFIG_BLK_DEV_SIS5513" = "y" -o \ "$CONFIG_BLK_DEV_SL82C105" = "y" ]; then diff --git a/drivers/block/Makefile b/drivers/block/Makefile index 8519318d9..1de4fa581 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -230,8 +230,8 @@ ifeq ($(CONFIG_BLK_DEV_UMC8672),y) IDE_OBJS += umc8672.o endif -ifeq ($(CONFIG_BLK_DEV_VIA82C586),y) -IDE_OBJS += via82c586.o +ifeq ($(CONFIG_BLK_DEV_VIA82CXXX),y) +IDE_OBJS += via82cxxx.o endif ### if CONFIG_BLK_DEV_IDE is n, IDE_OBJS will be ignored @@ -379,4 +379,3 @@ ide-mod.o: ide.o $(IDE_OBJS) ide-probe-mod.o: ide-probe.o ide-geometry.o $(LD) $(LD_RFLAG) -r -o $@ ide-probe.o ide-geometry.o - diff --git a/drivers/block/aec6210.c b/drivers/block/aec6210.c index c52d8450d..75bb8657f 100644 --- a/drivers/block/aec6210.c +++ b/drivers/block/aec6210.c @@ -1,7 +1,8 @@ /* - * linux/drivers/block/aec6210.c Version 0.01 Nov 17, 1998 + * linux/drivers/block/aec6210.c Version 0.02 Sept. 3, 1999 * - * Copyright (C) 1998-99 Andre Hedrick + * Copyright (C) 1998-99 Andre Hedrick (andre@suse.com) + * May be copied or modified under the terms of the GNU General Public License * * pio 0 :: 40: 00 07 00 00 00 00 00 00 02 07 a6 04 00 02 00 02 * pio 1 :: 40: 0a 07 00 00 00 00 00 00 02 07 a6 05 00 02 00 02 @@ -50,6 +51,10 @@ #include <asm/io.h> #include <asm/irq.h> +/* + * TO DO: active tuning and correction of cards without a bios. + */ + unsigned int __init pci_init_aec6210 (struct pci_dev *dev, const char *name) { if (dev->resource[PCI_ROM_RESOURCE].start) { diff --git a/drivers/block/alim15x3.c b/drivers/block/alim15x3.c index b7a0a5c4f..545361052 100644 --- a/drivers/block/alim15x3.c +++ b/drivers/block/alim15x3.c @@ -1,21 +1,28 @@ /* - * linux/drivers/block/alim15x3.c Version 0.05 Jun. 29, 1999 + * linux/drivers/block/alim15x3.c Version 0.06 Sept. 3, 1999 * * Copyright (C) 1998-99 Michel Aubry, Maintainer * Copyright (C) 1998-99 Andrzej Krzysztofowicz, Maintainer - * Copyright (C) 1998-99 Andre Hedrick, Integrater and Maintainer * - * (U)DMA capable version of ali 1533/1543(C) + * Copyright (C) 1998-99 Andre Hedrick (andre@suse.com) + * May be copied or modified under the terms of the GNU General Public License * - * Default disable (U)DMA on all devices execpt hard disks. - * This measure of overkill is needed to stablize the chipset code. + * (U)DMA capable version of ali 1533/1543(C), 1535(D) * + * version: 1.0 beta2 (Sep. 2, 1999) + * e-mail your problems to cjtsai@ali.com.tw + * + ********************************************************************** + * 9/7/99 --Parts from the above author are included and need to be + * converted into standard interface, once I finish the thought. */ #include <linux/config.h> #include <linux/types.h> #include <linux/kernel.h> #include <linux/pci.h> +#include <linux/delay.h> +#include <linux/hdreg.h> #include <linux/ide.h> #include <asm/io.h> @@ -59,188 +66,7 @@ char *channel_status[8] = { "error DRQ ", "error DRQ busy" }; -#endif /* defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS) */ - -static void ali15x3_tune_drive (ide_drive_t *drive, byte pio) -{ - ide_pio_data_t d; - ide_hwif_t *hwif = HWIF(drive); - struct pci_dev *dev = hwif->pci_dev; - int s_time, a_time, c_time; - byte s_clc, a_clc, r_clc; - unsigned long flags; - int bus_speed = ide_system_bus_speed(); - int port = hwif->index ? 0x5c : 0x58; - - pio = ide_get_best_pio_mode(drive, pio, 5, &d); - s_time = ide_pio_timings[pio].setup_time; - a_time = ide_pio_timings[pio].active_time; - if ((s_clc = (s_time * bus_speed + 999) / 1000) >= 8) - s_clc = 0; - if ((a_clc = (a_time * bus_speed + 999) / 1000) >= 8) - a_clc = 0; - c_time = ide_pio_timings[pio].cycle_time; - -#if 0 - if ((r_clc = ((c_time - s_time - a_time) * bus_speed + 999) / 1000) >= 16) - r_clc = 0; -#endif - if (!(r_clc = (c_time * bus_speed + 999) / 1000 - a_clc - s_clc)) { - r_clc = 1; - } else { - if (r_clc >= 16) - r_clc = 0; - } - save_flags(flags); - cli(); - pci_write_config_byte(dev, port, s_clc); - pci_write_config_byte(dev, port+drive->select.b.unit+2, (a_clc << 4) | r_clc); - restore_flags(flags); - - /* - * setup active rec - * { 70, 165, 365 }, PIO Mode 0 - * { 50, 125, 208 }, PIO Mode 1 - * { 30, 100, 110 }, PIO Mode 2 - * { 30, 80, 70 }, PIO Mode 3 with IORDY - * { 25, 70, 25 }, PIO Mode 4 with IORDY ns - * { 20, 50, 30 } PIO Mode 5 with IORDY (nonstandard) - */ - -} - -unsigned int __init pci_init_ali15x3 (struct pci_dev *dev, const char *name) -{ - byte confreg0 = 0, confreg1 =0, progif = 0; - int errors = 0; - - if (pci_read_config_byte(dev, 0x50, &confreg1)) - goto veryspecialsettingserror; - if (!(confreg1 & 0x02)) - if (pci_write_config_byte(dev, 0x50, confreg1 | 0x02)) - goto veryspecialsettingserror; - - if (pci_read_config_byte(dev, PCI_CLASS_PROG, &progif)) - goto veryspecialsettingserror; - if (!(progif & 0x40)) { - /* - * The way to enable them is to set progif - * writable at 0x4Dh register, and set bit 6 - * of progif to 1: - */ - if (pci_read_config_byte(dev, 0x4d, &confreg0)) - goto veryspecialsettingserror; - if (confreg0 & 0x80) - if (pci_write_config_byte(dev, 0x4d, confreg0 & ~0x80)) - goto veryspecialsettingserror; - if (pci_write_config_byte(dev, PCI_CLASS_PROG, progif | 0x40)) - goto veryspecialsettingserror; - if (confreg0 & 0x80) - if (pci_write_config_byte(dev, 0x4d, confreg0)) - errors++; - } - - if ((pci_read_config_byte(dev, PCI_CLASS_PROG, &progif)) || (!(progif & 0x40))) - goto veryspecialsettingserror; - - printk("%s: enabled read of IDE channels state (en/dis-abled) %s.\n", - name, errors ? "with Error(s)" : "Succeeded" ); - return 0; - -veryspecialsettingserror: - printk("%s: impossible to enable read of IDE channels state (en/dis-abled)!\n", name); - return 0; -} - -int ali15x3_dmaproc (ide_dma_action_t func, ide_drive_t *drive) -{ - switch (func) { - case ide_dma_check: - if (drive->media == ide_cdrom) { - ide_hwif_t *hwif = HWIF(drive); - struct pci_dev *dev = hwif->pci_dev; - struct hd_driveid *id = drive->id; - byte cd_dma_fifo = 0; - - pci_read_config_byte(dev, 0x53, &cd_dma_fifo); - - if (((id->field_valid & 4) || (id->field_valid & 2)) && - (id->capability & 1) && hwif->autodma) { - unsigned long dma_set_bit = hwif->dma_base + 2; -#if 0 - if (cd_dma_fifo & 0x02) - pci_write_config_byte(dev, 0x53, cd_dma_fifo & ~0x02); - pci_write_config_byte(dev, 0x53, cd_dma_fifo|0x01); -#else - pci_write_config_byte(dev, 0x53, cd_dma_fifo|0x01|0x02); -#endif - if (drive->select.b.unit & 0x01) { - outb(inb(dma_set_bit)|0x40, dma_set_bit); - } else { - outb(inb(dma_set_bit)|0x20, dma_set_bit); - } - } else { - if (cd_dma_fifo & 0x01) - pci_write_config_byte(dev, 0x53, cd_dma_fifo & ~0x01); - pci_write_config_byte(dev, 0x53, cd_dma_fifo|0x02); - } - } else if (drive->media != ide_disk) { - return ide_dmaproc(ide_dma_off_quietly, drive); - } - default: - break; - } - return ide_dmaproc(func, drive); /* use standard DMA stuff */ -} - -void __init ide_init_ali15x3 (ide_hwif_t *hwif) -{ - struct pci_dev *dev; - byte ideic, inmir, iderev; - byte irq_routing_table[] = { -1, 9, 3, 10, 4, 5, 7, 6, - 1, 11, 0, 12, 0, 14, 0, 15 }; - - pci_read_config_byte(hwif->pci_dev, PCI_REVISION_ID, &iderev); - - hwif->irq = hwif->channel ? 15 : 14; - for (dev = pci_devices; dev; dev=dev->next) /* look for ISA bridge */ - if (dev->vendor==PCI_VENDOR_ID_AL && - dev->device==PCI_DEVICE_ID_AL_M1533) - break; - if (dev) { - pci_read_config_byte(dev, 0x58, &ideic); - ideic = ideic & 0x03; - if ((hwif->channel && ideic == 0x03) || - (!hwif->channel && !ideic)) { - pci_read_config_byte(dev, 0x44, &inmir); - inmir = inmir & 0x0f; - hwif->irq = irq_routing_table[inmir]; - } else - if (hwif->channel && !(ideic & 0x01)) { - pci_read_config_byte(dev, 0x75, &inmir); - inmir = inmir & 0x0f; - hwif->irq = irq_routing_table[inmir]; - } - } -#if defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS) - bmide_dev = hwif->pci_dev; - ali_display_info = &ali_get_info; -#endif /* defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS) */ - - hwif->tuneproc = &ali15x3_tune_drive; - if ((hwif->dma_base) && (iderev >= 0xC1)) { - /* M1543C or newer for DMAing */ - hwif->dmaproc = &ali15x3_dmaproc; - } else { - hwif->autodma = 0; - hwif->drives[0].autotune = 1; - hwif->drives[1].autotune = 1; - } - return; -} - -#if defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS) static int ali_get_info(char *buffer, char **addr, off_t offset, int count, int dummy) { byte reg53h, reg5xh, reg5yh, reg5xh1, reg5yh1; @@ -407,3 +233,540 @@ static int ali_get_info(char *buffer, char **addr, off_t offset, int count, int return p-buffer; /* => must be less than 4k! */ } #endif /* defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS) */ + +static byte m5229_revision = 0; +static byte chip_is_1543c_e = 0; +static byte cable_80_pin[2] = { 0, 0 }; + +byte ali_proc = 0; +static struct pci_dev *isa_dev; + +static void ali15x3_tune_drive (ide_drive_t *drive, byte pio) +{ + ide_pio_data_t d; + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + int s_time, a_time, c_time; + byte s_clc, a_clc, r_clc; + unsigned long flags; + int bus_speed = ide_system_bus_speed(); + int port = hwif->index ? 0x5c : 0x58; + + pio = ide_get_best_pio_mode(drive, pio, 5, &d); + s_time = ide_pio_timings[pio].setup_time; + a_time = ide_pio_timings[pio].active_time; + if ((s_clc = (s_time * bus_speed + 999) / 1000) >= 8) + s_clc = 0; + if ((a_clc = (a_time * bus_speed + 999) / 1000) >= 8) + a_clc = 0; + c_time = ide_pio_timings[pio].cycle_time; + +#if 0 + if ((r_clc = ((c_time - s_time - a_time) * bus_speed + 999) / 1000) >= 16) + r_clc = 0; +#endif + + if (!(r_clc = (c_time * bus_speed + 999) / 1000 - a_clc - s_clc)) { + r_clc = 1; + } else { + if (r_clc >= 16) + r_clc = 0; + } + save_flags(flags); + cli(); + pci_write_config_byte(dev, port, s_clc); + pci_write_config_byte(dev, port+drive->select.b.unit+2, (a_clc << 4) | r_clc); + restore_flags(flags); + + /* + * setup active rec + * { 70, 165, 365 }, PIO Mode 0 + * { 50, 125, 208 }, PIO Mode 1 + * { 30, 100, 110 }, PIO Mode 2 + * { 30, 80, 70 }, PIO Mode 3 with IORDY + * { 25, 70, 25 }, PIO Mode 4 with IORDY ns + * { 20, 50, 30 } PIO Mode 5 with IORDY (nonstandard) + */ + +} + +static __inline__ unsigned char dma2_bits_to_command(unsigned char bits) +{ + if (bits & 0x04) + return XFER_MW_DMA_2; + if (bits & 0x02) + return XFER_MW_DMA_1; + return XFER_MW_DMA_0; +} + +static __inline__ unsigned char udma2_bits_to_command(unsigned char bits) +{ + if (bits & 0x10) + return XFER_UDMA_4; + if (bits & 0x08) + return XFER_UDMA_3; + if (bits & 0x04) + return XFER_UDMA_2; + if (bits & 0x02) + return XFER_UDMA_1; + return XFER_UDMA_0; +} + +static __inline__ int wait_for_ready(ide_drive_t *drive) +{ + int timeout = 20000; /* (old value: 100) */ + byte stat; + + while (--timeout) { + stat = GET_STAT(); + /* + * printk("STAT(%2x) ", stat); + */ + if (!(stat & BUSY_STAT)) { + if ((stat & READY_STAT) || (stat & ERR_STAT)) { + break; + } + } + /* + * (old value: 100) + */ + udelay(150); + } + if ((stat & ERR_STAT) || timeout <= 0) + return 1; + return 0; +} + +static void ali15x3_do_setfeature(ide_drive_t *drive, byte command) +{ + unsigned long flags; + byte old_select; + + save_flags(flags); + cli(); + + /* save old selected device */ + old_select = IN_BYTE(IDE_SELECT_REG); + /* "SELECT " */ + OUT_BYTE(drive->select.all, IDE_SELECT_REG); + /* "SETXFER " */ + OUT_BYTE(SETFEATURES_XFER, IDE_FEATURE_REG); + /* "CMND " */ + OUT_BYTE(command, IDE_NSECTOR_REG); + + if(wait_for_ready(drive)) /* "wait " */ + goto out; + + /* "SETFEATURE " */ + OUT_BYTE(WIN_SETFEATURES, IDE_COMMAND_REG); + /* "wait " */ + (void) wait_for_ready(drive); + +out: + /* + * restore to old "selected device" + */ + OUT_BYTE(old_select, IDE_SELECT_REG); + restore_flags(flags); +} + +static void ali15x3_dma2_enable(ide_drive_t *drive, unsigned long dma_base) +{ + byte unit = (drive->select.b.unit & 0x01); + byte bits = (drive->id->dma_mword | drive->id->dma_1word) & 0x07; + byte tmpbyte; + ide_hwif_t *hwif = HWIF(drive); + unsigned long flags; + int m5229_udma_setting_index = hwif->channel? 0x57 : 0x56; + + ali15x3_do_setfeature(drive, dma2_bits_to_command(bits)); + + /* + * clear "ultra enable" bit + */ + pci_read_config_byte(hwif->pci_dev, m5229_udma_setting_index, &tmpbyte); + if (unit) { + tmpbyte &= 0x7f; + } else { + tmpbyte &= 0xf7; + } + save_flags(flags); + cli(); + pci_write_config_byte(hwif->pci_dev, m5229_udma_setting_index, tmpbyte); + restore_flags(flags); + drive->id->dma_ultra = 0x00; + + /* + * Enable DMA + */ + outb(inb(dma_base+2)|(1<<(5+unit)), dma_base+2); + printk("ALI15X3: MultiWord DMA enabled\n"); +} + +static void ali15x3_udma_enable(ide_drive_t *drive, unsigned long dma_base) +{ + byte unit = (drive->select.b.unit & 0x01); + byte bits = drive->id->dma_ultra & 0x1f; + byte tmpbyte; + ide_hwif_t *hwif = HWIF(drive); + unsigned long flags; + unsigned char udma_mode = 0; + int m5229_udma_setting_index = hwif->channel? 0x57 : 0x56; + + if (bits & 0x18) { + /* + * 00011000, disk: ultra66 + */ + if (m5229_revision < 0xc2) { + /* + * controller: ultra33 + */ + bits = 0x04; + /* + * 00000100, use ultra33, mode 2 + */ + drive->id->dma_ultra &= ~0xFF00; + drive->id->dma_ultra |= 0x0004; + } else { + /* + * controller: ultra66 + * + * Try to detect word93 bit13 and + * 80-pin cable (from host view) + */ + if (!((drive->id->word93 & 0x2000) && + cable_80_pin[hwif->channel])) { + bits = 0x04; + /* + * 00000100, use ultra33, mode 2 + */ + drive->id->dma_ultra &= ~0xFF00; + drive->id->dma_ultra |= 0x0004; + } + } + } + + /* + * set feature regardless + */ + ali15x3_do_setfeature(drive, udma_mode = udma2_bits_to_command(bits)); + udma_mode &= 0x0f; /* get UDMA mode */ + + /* + * Enable DMA and UltraDMA + */ + outb(inb(dma_base+2)|(1<<(5+unit)), dma_base+2); + /* + * m5229 ultra + */ + pci_read_config_byte(hwif->pci_dev, m5229_udma_setting_index, &tmpbyte); + /* + * clear bit0~3 or bit 4~7 + */ + tmpbyte &= (0x0f << ((1-unit) << 2)); + /* + * enable ultra dma and set timing + */ + tmpbyte |= ((0x08 | (4-udma_mode)) << (unit << 2)); + /* + * set to m5229 + */ + save_flags(flags); + cli(); + pci_write_config_byte(hwif->pci_dev, m5229_udma_setting_index, tmpbyte); + restore_flags(flags); + + if (udma_mode >= 3) { + /* + * ultra 66 + */ + pci_read_config_byte(hwif->pci_dev, 0x4b, &tmpbyte); + tmpbyte |= 1; + save_flags(flags); + cli(); + pci_write_config_byte(hwif->pci_dev, 0x4b, tmpbyte); + restore_flags(flags); + } + + printk("ALI15X3: Ultra DMA enabled\n"); +} + +static int ali15x3_dma_onoff(ide_drive_t *drive, int enable) +{ + if (enable) { + ide_hwif_t *hwif = HWIF(drive); + unsigned long dma_base = hwif->dma_base; + struct hd_driveid *id = drive->id; + + if ((id->field_valid & 0x0004) && + (id->dma_ultra & 0x001f)) { + /* + * 1543C_E, in ultra mode, WDC "harddisk" + * will cause "CRC" errors (even if no CRC problem), + * so we try to use "DMA" here + */ + if (m5229_revision <= 0x20) { + /* + * Normal MultiWord DMA modes. + */ + ali15x3_dma2_enable(drive, dma_base); + } else if ((m5229_revision < 0xC2) && + ((drive->media!=ide_disk) || + (chip_is_1543c_e && + strstr(id->model, "WDC ")))) { + /* + * Normal MultiWord DMA modes. + */ + ali15x3_dma2_enable(drive, dma_base); + } else { + /* + * m5229_revision >= 0xC2 for UltraDMA modes. + */ + ali15x3_udma_enable(drive, dma_base); + } + } else { + /* + * Normal MultiWord DMA modes. + */ + ali15x3_dma2_enable(drive, dma_base); + } + } + + drive->using_dma = enable; /* on, off */ + return 0; +} + +static int ali15x3_config_drive_for_dma(ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + ide_hwif_t *hwif = HWIF(drive); + + if ((m5229_revision<=0x20) && (drive->media!=ide_disk)) + return hwif->dmaproc(ide_dma_off_quietly, drive); + /* + * Even if the drive is not _currently_ in a DMA + * mode, we succeed, and we'll enable it manually + * below in alim15x3_dma_onoff + */ + if ((id != NULL) && (id->capability & 1) && hwif->autodma) { + if (id->field_valid & 0x0004) { + if (id->dma_ultra & 0x001F) + return hwif->dmaproc(ide_dma_on, drive); + } + if (id->field_valid & 0x0002) { + if ((id->dma_mword & 0x0007) || (id->dma_1word & 0x0007)) + return hwif->dmaproc(ide_dma_on, drive); + } + } + return hwif->dmaproc(ide_dma_off_quietly, drive); +} + +static int ali15x3_dmaproc (ide_dma_action_t func, ide_drive_t *drive) +{ + switch(func) { + case ide_dma_check: + return ali15x3_config_drive_for_dma(drive); + case ide_dma_on: + case ide_dma_off: + case ide_dma_off_quietly: + return ali15x3_dma_onoff(drive, (func == ide_dma_on)); + case ide_dma_write: + if ((m5229_revision < 0xC2) && (drive->media != ide_disk)) + return 1; /* try PIO instead of DMA */ + break; + default: + break; + } + + return ide_dmaproc(func, drive); /* use standard DMA stuff */ +} + +unsigned int __init pci_init_ali15x3 (struct pci_dev *dev, const char *name) +{ + struct pci_dev *isa; + unsigned long fixdma_base = dev->resource[4].start; + byte tmpbyte; + + pci_read_config_byte(dev, PCI_REVISION_ID, &m5229_revision); + + for (isa = pci_devices; isa; isa=isa->next) { + /* + * look for ISA bridge + */ + if (isa->vendor == PCI_VENDOR_ID_AL && + isa->device == PCI_DEVICE_ID_AL_M1533) { + isa_dev = isa; + break; + } + } + + if (!fixdma_base || fixdma_base == PCI_BASE_ADDRESS_IO_MASK) { + /* + * + */ + } else { + /* + * enable DMA capable bit, and "not" simplex only + */ + outb(inb(fixdma_base+2) & 0x60, fixdma_base+2); + + if (inb(fixdma_base+2) & 0x80) + printk("%s: simplex device: DMA will fail!!\n", name); + } + + /* + * FIXME !!! This detection needs to be in "ata66_ali15x3()" + * below as a standard detection return. + */ + + if (m5229_revision >= 0xC2) { + unsigned long flags; + /* + * 1543C-B?, 1535, 1535D, 1553 + * Note 1: not all "motherboard" support this detection + * Note 2: if no udma 66 device, the detection may "error". + * but in this case, we will not set the device to + * ultra 66, the detection result is not important + */ + save_flags(flags); + cli(); + + /* + * enable "Cable Detection", m5229, 0x4b, bit3 + */ + pci_read_config_byte(dev, 0x4b, &tmpbyte); + pci_write_config_byte(dev, 0x4b, tmpbyte | 0x08); + + /* + * set south-bridge's enable bit, m1533, 0x79 + */ + pci_read_config_byte(isa_dev, 0x79, &tmpbyte); + if (m5229_revision == 0xC2) { + /* + * 1543C-B0 (m1533, 0x79, bit 2) + */ + pci_write_config_byte(isa_dev, 0x79, tmpbyte | 0x04); + } else if (m5229_revision == 0xC3) { + /* + * 1553/1535 (m1533, 0x79, bit 1) + */ + pci_write_config_byte(isa_dev, 0x79, tmpbyte | 0x02); + } + restore_flags(flags); + /* + * Ultra66 cable detection (from Host View) + * m5229, 0x4a, bit0: primary, bit1: secondary 80 pin + */ + pci_read_config_byte(dev, 0x4a, &tmpbyte); + /* + * 0x4a, bit0 is 0 => primary channel + * has 80-pin (from host view) + */ + if (!(tmpbyte & 0x01)) + cable_80_pin[0] = 1; + /* + * 0x4a, bit1 is 0 => secondary channel + * has 80-pin (from host view) + */ + if (!(tmpbyte & 0x02)) + cable_80_pin[1] = 1; + } else { + unsigned long flags; + /* + * revision 0x20 (1543-E, 1543-F) + * revision 0xC0, 0xC1 (1543C-C, 1543C-D, 1543C-E) + * clear CD-ROM DMA write bit, m5229, 0x4b, bit 7 + */ + pci_read_config_byte(dev, 0x4b, &tmpbyte); + save_flags(flags); + cli(); + /* + * clear bit 7 + */ + pci_write_config_byte(dev, 0x4b, tmpbyte & 0x7F); + restore_flags(flags); + + /* + * check m1533, 0x5e, bit 1~4 == 1001 => & 00011110 = 00010010 + */ + pci_read_config_byte(isa_dev, 0x5e, &tmpbyte); + chip_is_1543c_e = ((tmpbyte & 0x1e) == 0x12) ? 1: 0; + } + + return 0; +} + +unsigned int __init ata66_ali15x3 (ide_hwif_t *hwif) +{ + /* + * FIXME !!!! + * {0x4a,0x01,0x01}, {0x4a,0x02,0x02} + */ + return 0; +} + +void __init ide_init_ali15x3 (ide_hwif_t *hwif) +{ + byte ideic, inmir; + byte irq_routing_table[] = { -1, 9, 3, 10, 4, 5, 7, 6, + 1, 11, 0, 12, 0, 14, 0, 15 }; + + hwif->irq = hwif->channel ? 15 : 14; + + if (isa_dev) { + /* + * read IDE interface control + */ + pci_read_config_byte(isa_dev, 0x58, &ideic); + + /* bit0, bit1 */ + ideic = ideic & 0x03; + + /* get IRQ for IDE Controller */ + if ((hwif->channel && ideic == 0x03) || + (!hwif->channel && !ideic)) { + /* + * get SIRQ1 routing table + */ + pci_read_config_byte(isa_dev, 0x44, &inmir); + inmir = inmir & 0x0f; + hwif->irq = irq_routing_table[inmir]; + } else if (hwif->channel && !(ideic & 0x01)) { + /* + * get SIRQ2 routing table + */ + pci_read_config_byte(isa_dev, 0x75, &inmir); + inmir = inmir & 0x0f; + hwif->irq = irq_routing_table[inmir]; + } + } + + hwif->tuneproc = &ali15x3_tune_drive; + if ((hwif->dma_base) && (m5229_revision >= 0xC1)) { + /* + * M1543C or newer for DMAing + */ + hwif->dmaproc = &ali15x3_dmaproc; + hwif->autodma = 1; + } else { + hwif->autodma = 0; + hwif->drives[0].autotune = 1; + hwif->drives[1].autotune = 1; + } + +#if defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS) + ali_proc = 1; + bmide_dev = hwif->pci_dev; + ali_display_info = &ali_get_info; +#endif /* defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS) */ + + return; +} + +void ide_dmacapable_ali15x3 (ide_hwif_t *hwif, unsigned long dmabase) +{ + if ((dmabase) && (m5229_revision < 0x20)) { + return; + } + ide_setup_dma(hwif, dmabase, 8); +} diff --git a/drivers/block/cy82c693.c b/drivers/block/cy82c693.c index e204bc0e6..77fdf0821 100644 --- a/drivers/block/cy82c693.c +++ b/drivers/block/cy82c693.c @@ -1,8 +1,8 @@ /* - * linux/drivers/block/cy82c693.c Version 0.33 Jan. 23, 1999 + * linux/drivers/block/cy82c693.c Version 0.34 Sept 3, 1999 * - * Copyright (C) 1998, 1999 Andreas S. Krebs (akrebs@altavista.net), Maintainer - * Copyright (C) 1998 Andre Hedrick, Integrater + * Copyright (C) 1998-99 Andreas S. Krebs (akrebs@altavista.net), Maintainer + * Copyright (C) 1998-99 Andre Hedrick, Integrater * * CYPRESS CY82C693 chipset IDE controller * @@ -31,6 +31,7 @@ * * * History: + * AMH@1999-08-24: v0.34 init_cy82c693_chip moved to pci_init_cy82c693 * ASK@1999-01-23: v0.33 made a few minor code clean ups * removed DMA clock speed setting by default * added boot message @@ -53,7 +54,7 @@ #include "ide_modes.h" /* the current version */ -#define CY82_VERSION "CY82C693U driver v0.33 99-01-23 Andreas S. Krebs (akrebs@altavista.net)" +#define CY82_VERSION "CY82C693U driver v0.34 99-09-03 Andreas S. Krebs (akrebs@altavista.net)" /* * The following are used to debug the driver. @@ -377,20 +378,10 @@ static void cy82c693_tune_drive (ide_drive_t *drive, byte pio) unsigned int __init pci_init_cy82c693(struct pci_dev *dev, const char *name) { - return 0; -} - -static void init_cy82c693_chip (struct pci_dev *dev) -{ - static int initDone = 0; #ifdef CY82C693_SETDMA_CLOCK byte data; #endif /* CY82C693_SETDMA_CLOCK */ - if (initDone != 0) /* only perform setup once */ - return; - initDone = 1; - /* write info about this verion of the driver */ printk (KERN_INFO CY82_VERSION "\n"); @@ -401,7 +392,7 @@ static void init_cy82c693_chip (struct pci_dev *dev) data = IN_BYTE(CY82_DATA_PORT); #if CY82C693_DEBUG_INFO - printk (KERN_INFO "CY82U693: Peripheral Configuration Register: 0x%X\n", data); + printk (KERN_INFO "%s: Peripheral Configuration Register: 0x%X\n", name, data); #endif /* CY82C693_DEBUG_INFO */ /* @@ -422,10 +413,11 @@ static void init_cy82c693_chip (struct pci_dev *dev) OUT_BYTE(data, CY82_DATA_PORT); #if CY82C693_DEBUG_INFO - printk (KERN_INFO "CY82U693: New Peripheral Configuration Register: 0x%X\n", data); + printk (KERN_INFO "%s: New Peripheral Configuration Register: 0x%X\n", name, data); #endif /* CY82C693_DEBUG_INFO */ -#endif /* CY82C693_SETDMA_CLOCK */ +#endif /* CY82C693_SETDMA_CLOCK */ + return 0; } /* @@ -437,7 +429,5 @@ void __init ide_init_cy82c693(ide_hwif_t *hwif) if (hwif->dma_base) hwif->dmaproc = &cy82c693_dmaproc; hwif->tuneproc = &cy82c693_tune_drive; - - init_cy82c693_chip(hwif->pci_dev); } diff --git a/drivers/block/genhd.c b/drivers/block/genhd.c index aece0447d..3c9a35b97 100644 --- a/drivers/block/genhd.c +++ b/drivers/block/genhd.c @@ -26,6 +26,8 @@ extern int net_dev_init(void); extern void console_map_init(void); extern int soc_probe(void); extern int atmdev_init(void); +extern int i2o_init(void); +extern int cpqarray_init(void); void __init device_init(void) { diff --git a/drivers/block/hpt34x.c b/drivers/block/hpt34x.c index 284bf40bc..816b4b805 100644 --- a/drivers/block/hpt34x.c +++ b/drivers/block/hpt34x.c @@ -1,7 +1,9 @@ /* - * linux/drivers/block/hpt34x.c Version 0.25 July 11, 1999 + * linux/drivers/block/hpt34x.c Version 0.27 Sept 03, 1999 + * + * Copyright (C) 1998-99 Andre Hedrick (andre@suse.com) + * May be copied or modified under the terms of the GNU General Public License * - * Copyright (C) 1998-99 Andre Hedrick * * 00:12.0 Unknown mass storage controller: * Triones Technologies, Inc. @@ -14,9 +16,6 @@ * hdg: DMA 1 (0x0012 0x0052) (0x0030 0x0070) * hdh: DMA 1 (0x0052 0x0252) (0x0070 0x00f0) * - * drive_number - * = ((HWIF(drive)->channel ? 2 : 0) + (drive->select.b.unit & 0x01)); - * = ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01)); */ #include <linux/config.h> @@ -58,7 +57,7 @@ static void hpt34x_clear_chipset (ide_drive_t *drive) pci_read_config_dword(HWIF(drive)->pci_dev, 0x44, ®1); pci_read_config_dword(HWIF(drive)->pci_dev, 0x48, ®2); tmp1 = ((0x00 << (3*drive_number)) | (reg1 & ~(7 << (3*drive_number)))); - tmp2 = ((0x00 << drive_number) | reg2); + tmp2 = (reg2 & ~(0x11 << drive_number)); pci_write_config_dword(HWIF(drive)->pci_dev, 0x44, tmp1); pci_write_config_dword(HWIF(drive)->pci_dev, 0x48, tmp2); } @@ -105,7 +104,7 @@ static int hpt34x_tune_chipset (ide_drive_t *drive, byte speed) * after the drive is reported by the OS. Initally for designed for * HPT343 UDMA chipset by HighPoint|Triones Technologies, Inc. */ -static int config_chipset_for_dma (ide_drive_t *drive) +static int config_chipset_for_dma (ide_drive_t *drive, byte ultra) { struct hd_driveid *id = drive->id; byte speed = 0x00; @@ -117,76 +116,29 @@ static int config_chipset_for_dma (ide_drive_t *drive) return ((int) ide_dma_off_quietly); #endif /* HPT343_DISABLE_ALL_DMAING */ - if (id->dma_ultra & 0x0010) { - goto backspeed; - } else if (id->dma_ultra & 0x0008) { - goto backspeed; - } else if (id->dma_ultra & 0x0004) { -backspeed: - if (!((id->dma_ultra >> 8) & 4)) { - drive->id->dma_ultra &= ~0xFF00; - drive->id->dma_ultra |= 0x0404; - drive->id->dma_mword &= ~0x0F00; - drive->id->dma_1word &= ~0x0F00; - } + hpt34x_clear_chipset(drive); + + if ((id->dma_ultra & 0x0010) && ultra) { speed = XFER_UDMA_2; - } else if (id->dma_ultra & 0x0002) { - if (!((id->dma_ultra >> 8) & 2)) { - drive->id->dma_ultra &= ~0xFF00; - drive->id->dma_ultra |= 0x0202; - drive->id->dma_mword &= ~0x0F00; - drive->id->dma_1word &= ~0x0F00; - } + } else if ((id->dma_ultra & 0x0008) && ultra) { + speed = XFER_UDMA_2; + } else if ((id->dma_ultra & 0x0004) && ultra) { + speed = XFER_UDMA_2; + } else if ((id->dma_ultra & 0x0002) && ultra) { speed = XFER_UDMA_1; - } else if (id->dma_ultra & 0x0001) { - if (!((id->dma_ultra >> 8) & 1)) { - drive->id->dma_ultra &= ~0xFF00; - drive->id->dma_ultra |= 0x0101; - drive->id->dma_mword &= ~0x0F00; - drive->id->dma_1word &= ~0x0F00; - } + } else if ((id->dma_ultra & 0x0001) && ultra) { speed = XFER_UDMA_0; } else if (id->dma_mword & 0x0004) { - if (!((id->dma_mword >> 8) & 4)) { - drive->id->dma_mword &= ~0x0F00; - drive->id->dma_mword |= 0x0404; - drive->id->dma_1word &= ~0x0F00; - } speed = XFER_MW_DMA_2; } else if (id->dma_mword & 0x0002) { - if (!((id->dma_mword >> 8) & 2)) { - drive->id->dma_mword &= ~0x0F00; - drive->id->dma_mword |= 0x0202; - drive->id->dma_1word &= ~0x0F00; - } speed = XFER_MW_DMA_1; } else if (id->dma_mword & 0x0001) { - if (!((id->dma_mword >> 8) & 1)) { - drive->id->dma_mword &= ~0x0F00; - drive->id->dma_mword |= 0x0101; - drive->id->dma_1word &= ~0x0F00; - } speed = XFER_MW_DMA_0; } else if (id->dma_1word & 0x0004) { - if (!((id->dma_1word >> 8) & 4)) { - drive->id->dma_1word &= ~0x0F00; - drive->id->dma_1word |= 0x0404; - drive->id->dma_mword &= ~0x0F00; - } speed = XFER_SW_DMA_2; } else if (id->dma_1word & 0x0002) { - if (!((id->dma_1word >> 8) & 2)) { - drive->id->dma_1word &= ~0x0F00; - drive->id->dma_1word |= 0x0202; - drive->id->dma_mword &= ~0x0F00; - } speed = XFER_SW_DMA_1; } else if (id->dma_1word & 0x0001) { - if (!((id->dma_1word >> 8) & 1)) { - drive->id->dma_1word &= ~0x0F00; - drive->id->dma_1word |= 0x0101; - drive->id->dma_mword &= ~0x0F00; - } speed = XFER_SW_DMA_0; } else { return ((int) ide_dma_off_quietly); @@ -242,7 +194,6 @@ static void hpt34x_tune_drive (ide_drive_t *drive, byte pio) { byte speed; - hpt34x_clear_chipset(drive); switch(pio) { case 4: speed = XFER_PIO_4;break; case 3: speed = XFER_PIO_3;break; @@ -250,6 +201,7 @@ static void hpt34x_tune_drive (ide_drive_t *drive, byte pio) case 1: speed = XFER_PIO_1;break; default: speed = XFER_PIO_0;break; } + hpt34x_clear_chipset(drive); (void) hpt34x_tune_chipset(drive, speed); } @@ -268,7 +220,7 @@ static int config_drive_xfer_rate (ide_drive_t *drive) if (id->field_valid & 4) { if (id->dma_ultra & 0x0007) { /* Force if Capable UltraDMA */ - dma_func = config_chipset_for_dma(drive); + dma_func = config_chipset_for_dma(drive, 1); if ((id->field_valid & 2) && (dma_func != ide_dma_on)) goto try_dma_modes; @@ -278,7 +230,7 @@ try_dma_modes: if ((id->dma_mword & 0x0007) || (id->dma_1word & 0x0007)) { /* Force if Capable regular DMA modes */ - dma_func = config_chipset_for_dma(drive); + dma_func = config_chipset_for_dma(drive, 0); if (dma_func != ide_dma_on) goto no_dma_set; } @@ -287,7 +239,7 @@ try_dma_modes: goto no_dma_set; } /* Consult the list of known "good" drives */ - dma_func = config_chipset_for_dma(drive); + dma_func = config_chipset_for_dma(drive, 0); if (dma_func != ide_dma_on) goto no_dma_set; } else { @@ -300,6 +252,12 @@ no_dma_set: config_chipset_for_pio(drive); } + +#if 0 + if (dma_func == ide_dma_on) + dma_func = ide_dma_off; +#endif + return HWIF(drive)->dmaproc(dma_func, drive); } @@ -313,22 +271,44 @@ no_dma_set: int hpt34x_dmaproc (ide_dma_action_t func, ide_drive_t *drive) { + ide_hwif_t *hwif = HWIF(drive); + unsigned long dma_base = hwif->dma_base; + byte unit = (drive->select.b.unit & 0x01); + unsigned int count, reading = 0; + byte dma_stat; + switch (func) { - case ide_dma_check: - hpt34x_clear_chipset(drive); - return config_drive_xfer_rate(drive); -#if 0 case ide_dma_off: case ide_dma_off_quietly: + outb(inb(dma_base+2) & ~(1<<(5+unit)), dma_base+2); + break; case ide_dma_on: + outb(inb(dma_base+2)|(1<<(5+unit)), dma_base+2); + break; case ide_dma_check: return config_drive_xfer_rate(drive); case ide_dma_read: + reading = 1 << 3; case ide_dma_write: - case ide_dma_begin: - case ide_dma_end: - case ide_dma_test_irq: -#endif + if (!(count = ide_build_dmatable(drive, func))) + return 1; /* try PIO instead of DMA */ + outl(virt_to_bus(hwif->dmatable), dma_base + 4); /* PRD table */ + reading |= 0x01; + outb(reading, dma_base); /* specify r/w */ + outb(inb(dma_base+2)|6, dma_base+2); /* clear INTR & ERROR flags */ + drive->waiting_for_dma = 1; + if (drive->media != ide_disk) + return 0; + drive->timeout = WAIT_CMD; + ide_set_handler(drive, &ide_dma_intr); /* issue cmd to drive */ + OUT_BYTE((reading == 9) ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG); + return 0; + case ide_dma_end: /* returns 1 on error, 0 otherwise */ + drive->waiting_for_dma = 0; + outb(inb(dma_base)&~1, dma_base); /* stop DMA */ + dma_stat = inb(dma_base+2); /* get DMA status */ + outb(dma_stat|6, dma_base+2); /* clear the INTR & ERROR bits */ + return (dma_stat & 7) != 4; /* verify good DMA status */ default: break; } @@ -342,10 +322,17 @@ int hpt34x_dmaproc (ide_dma_action_t func, ide_drive_t *drive) unsigned int __init pci_init_hpt34x (struct pci_dev *dev, const char *name) { + int i = 0; + unsigned long hpt34xIoBase = dev->resource[4].start; unsigned short cmd; + unsigned long flags; + + __save_flags(flags); /* local CPU only */ + __cli(); /* local CPU only */ pci_write_config_byte(dev, HPT34X_PCI_INIT_REG, 0x00); pci_read_config_word(dev, PCI_COMMAND, &cmd); + if (cmd & PCI_COMMAND_MEMORY) { if (dev->resource[PCI_ROM_RESOURCE].start) { pci_write_config_byte(dev, PCI_ROM_ADDRESS, dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE); @@ -353,27 +340,28 @@ unsigned int __init pci_init_hpt34x (struct pci_dev *dev, const char *name) } pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0xF0); } else { - int i = 0; - unsigned long hpt34xIoBase = dev->resource[4].start; - - pci_write_config_word(dev, PCI_COMMAND, cmd & ~PCI_COMMAND_IO); - dev->resource[0].start = (hpt34xIoBase + 0x20); - dev->resource[1].start = (hpt34xIoBase + 0x34); - dev->resource[2].start = (hpt34xIoBase + 0x28); - dev->resource[3].start = (hpt34xIoBase + 0x3c); - for(i=0; i<4; i++) - dev->resource[i].flags |= PCI_BASE_ADDRESS_SPACE_IO; - /* - * Since 20-23 can be assigned and are R/W, we correct them. - */ - pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, dev->resource[0].start); - pci_write_config_dword(dev, PCI_BASE_ADDRESS_1, dev->resource[1].start); - pci_write_config_dword(dev, PCI_BASE_ADDRESS_2, dev->resource[2].start); - pci_write_config_dword(dev, PCI_BASE_ADDRESS_3, dev->resource[3].start); - - pci_write_config_word(dev, PCI_COMMAND, cmd); pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x20); } + + pci_write_config_word(dev, PCI_COMMAND, cmd & ~PCI_COMMAND_IO); + dev->resource[0].start = (hpt34xIoBase + 0x20); + dev->resource[1].start = (hpt34xIoBase + 0x34); + dev->resource[2].start = (hpt34xIoBase + 0x28); + dev->resource[3].start = (hpt34xIoBase + 0x3c); + for(i=0; i<4; i++) + dev->resource[i].flags |= PCI_BASE_ADDRESS_SPACE_IO; + /* + * Since 20-23 can be assigned and are R/W, we correct them. + */ + pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, dev->resource[0].start); + pci_write_config_dword(dev, PCI_BASE_ADDRESS_1, dev->resource[1].start); + pci_write_config_dword(dev, PCI_BASE_ADDRESS_2, dev->resource[2].start); + pci_write_config_dword(dev, PCI_BASE_ADDRESS_3, dev->resource[3].start); + + pci_write_config_word(dev, PCI_COMMAND, cmd); + + __restore_flags(flags); /* local CPU only */ + return dev->irq; } diff --git a/drivers/block/hpt366.c b/drivers/block/hpt366.c index b61f71687..d45b5f719 100644 --- a/drivers/block/hpt366.c +++ b/drivers/block/hpt366.c @@ -1,11 +1,13 @@ /* - * linux/drivers/block/hpt366.c Version 0.12 August 16, 1999 + * linux/drivers/block/hpt366.c Version 0.13 Sept. 3, 1999 * * Copyright (C) 1999 Andre Hedrick <andre@suse.com> + * May be copied or modified under the terms of the GNU General Public License * - * drive_number - * = ((HWIF(drive)->channel ? 2 : 0) + (drive->select.b.unit & 0x01)); - * = ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01)); + * Thanks to HighPoint Technologies for their assistance, and hardware. + * Special Thanks to Jon Burchmore in SanDiego for the deep pockets, his + * donation of an ABit BP6 mainboard, processor, and memory acellerated + * development and support. */ #include <linux/types.h> @@ -114,17 +116,18 @@ struct chipset_bus_clock_list_entry twenty_five_base [] = { }; #define HPT366_DEBUG_DRIVE_INFO 0 -#define HPT366_ALLOW_ATA66_4 0 +#define HPT366_ALLOW_ATA66_4 1 #define HPT366_ALLOW_ATA66_3 1 -#define HPT366_ALLOW_ATA33_2 1 -#define HPT366_ALLOW_ATA33_1 1 -#define HPT366_ALLOW_ATA33_0 1 extern char *ide_xfer_verbose (byte xfer_rate); +byte hpt363_shared_irq = 0; static int check_in_drive_lists (ide_drive_t *drive, const char **list) { struct hd_driveid *id = drive->id; +#if HPT366_DEBUG_DRIVE_INFO + printk("check_in_drive_lists(%s, %p)\n", drive->name, list); +#endif /* HPT366_DEBUG_DRIVE_INFO */ while (*list) { if (!strcmp(*list++,id->model)) { @@ -139,50 +142,62 @@ static int check_in_drive_lists (ide_drive_t *drive, const char **list) static unsigned int pci_bus_clock_list (byte speed, struct chipset_bus_clock_list_entry * chipset_table) { +#if HPT366_DEBUG_DRIVE_INFO + printk("pci_bus_clock_list(speed=0x%02x, table=%p)\n", speed, chipset_table); +#endif /* HPT366_DEBUG_DRIVE_INFO */ for ( ; chipset_table->xfer_speed ; chipset_table++) - if (chipset_table->xfer_speed == speed) + if (chipset_table->xfer_speed == speed) { +#if HPT366_DEBUG_DRIVE_INFO + printk("pci_bus_clock_list: found match: 0x%08x\n", chipset_table->chipset_settings); +#endif /* HPT366_DEBUG_DRIVE_INFO */ return chipset_table->chipset_settings; + } +#if HPT366_DEBUG_DRIVE_INFO + printk("pci_bus_clock_list: using default: 0x%08x\n", 0x01208585); +#endif /* HPT366_DEBUG_DRIVE_INFO */ return 0x01208585; } static int hpt366_tune_chipset (ide_drive_t *drive, byte speed) { int err; - byte busclock; - #if HPT366_DEBUG_DRIVE_INFO int drive_number = ((HWIF(drive)->channel ? 2 : 0) + (drive->select.b.unit & 0x01)); #endif /* HPT366_DEBUG_DRIVE_INFO */ - byte regtime = (drive->select.b.unit & 0x01) ? 0x43 : 0x40; + byte regtime = (drive->select.b.unit & 0x01) ? 0x44 : 0x40; unsigned int reg1 = 0; unsigned int reg2 = 0; +#if HPT366_DEBUG_DRIVE_INFO + printk("hpt366_tune_chipset(%s, speed=0x%02x)\n", drive->name, speed); +#endif /* HPT366_DEBUG_DRIVE_INFO */ + pci_read_config_dword(HWIF(drive)->pci_dev, regtime, ®1); - pci_read_config_byte(HWIF(drive)->pci_dev, regtime|0x01, &busclock); - switch(busclock) { - case 0xd9: + /* detect bus speed by looking at control reg timing: */ + switch((reg1 >> 8) & 7) { + case 5: reg2 = pci_bus_clock_list(speed, forty_base); break; - case 0x85: + case 9: reg2 = pci_bus_clock_list(speed, twenty_five_base); break; - case 0xa7: default: + printk("hpt366: assuming 33Mhz PCI bus\n"); + case 7: reg2 = pci_bus_clock_list(speed, thirty_three_base); break; } - - if (drive->id->dword_io & 1) - reg2 |= 0x80000000; - else - reg2 &= ~0x80000000; + /* + * Disable on-chip PIO FIFO/buffer (to avoid problems handling I/O errors later) + */ + reg2 &= ~0x80000000; pci_write_config_dword(HWIF(drive)->pci_dev, regtime, reg2); err = ide_config_drive_speed(drive, speed); #if HPT366_DEBUG_DRIVE_INFO - printk("%s: %s drive%d (0x%08x 0x%08x) 0x%04x\n", - drive->name, ide_xfer_verbose(speed), + printk("%s: speed=0x%02x(%s), drive%d, old=0x%08x, new=0x%08x, err=0x%04x\n", + drive->name, speed, ide_xfer_verbose(speed), drive_number, reg1, reg2, err); #endif /* HPT366_DEBUG_DRIVE_INFO */ return(err); @@ -203,127 +218,89 @@ static int config_chipset_for_dma (ide_drive_t *drive) { struct hd_driveid *id = drive->id; byte speed = 0x00; + unsigned int reg40 = 0; + int rval; if ((id->dma_ultra & 0x0010) && (!check_in_drive_lists(drive, bad_ata66_4)) && (HPT366_ALLOW_ATA66_4) && (HWIF(drive)->udma_four)) { - if (!((id->dma_ultra >> 8) & 16)) { - drive->id->dma_ultra &= ~0xFF00; - drive->id->dma_ultra |= 0x1010; - drive->id->dma_mword &= ~0x0F00; - drive->id->dma_1word &= ~0x0F00; - } speed = XFER_UDMA_4; } else if ((id->dma_ultra & 0x0008) && (!check_in_drive_lists(drive, bad_ata66_3)) && (HPT366_ALLOW_ATA66_3) && (HWIF(drive)->udma_four)) { - if (!((id->dma_ultra >> 8) & 8)) { - drive->id->dma_ultra &= ~0xFF00; - drive->id->dma_ultra |= 0x0808; - drive->id->dma_mword &= ~0x0F00; - drive->id->dma_1word &= ~0x0F00; - } speed = XFER_UDMA_3; - } else if ((id->dma_ultra & 0x0004) && - (HPT366_ALLOW_ATA33_2) && - (!check_in_drive_lists(drive, bad_ata33))) { - if (!((id->dma_ultra >> 8) & 4)) { - drive->id->dma_ultra &= ~0xFF00; - drive->id->dma_ultra |= 0x0404; - drive->id->dma_mword &= ~0x0F00; - drive->id->dma_1word &= ~0x0F00; - } - speed = XFER_UDMA_2; - } else if ((id->dma_ultra & 0x0002) && - (HPT366_ALLOW_ATA33_1) && - (!check_in_drive_lists(drive, bad_ata33))) { - if (!((id->dma_ultra >> 8) & 2)) { - drive->id->dma_ultra &= ~0xFF00; - drive->id->dma_ultra |= 0x0202; - drive->id->dma_mword &= ~0x0F00; - drive->id->dma_1word &= ~0x0F00; + } else if (id->dma_ultra && (!check_in_drive_lists(drive, bad_ata33))) { + if (id->dma_ultra & 0x0004) { + speed = XFER_UDMA_2; + } else if (id->dma_ultra & 0x0002) { + speed = XFER_UDMA_1; + } else if (id->dma_ultra & 0x0001) { + speed = XFER_UDMA_0; } - speed = XFER_UDMA_1; - } else if ((id->dma_ultra & 0x0001) && - (HPT366_ALLOW_ATA33_0) && - (!check_in_drive_lists(drive, bad_ata33))) { - if (!((id->dma_ultra >> 8) & 1)) { - drive->id->dma_ultra &= ~0xFF00; - drive->id->dma_ultra |= 0x0101; - drive->id->dma_mword &= ~0x0F00; - drive->id->dma_1word &= ~0x0F00; - } - speed = XFER_UDMA_0; } else if (id->dma_mword & 0x0004) { - drive->id->dma_ultra &= ~0xFF00; - if (!((id->dma_mword >> 8) & 4)) { - drive->id->dma_mword &= ~0x0F00; - drive->id->dma_mword |= 0x0404; - drive->id->dma_1word &= ~0x0F00; - } speed = XFER_MW_DMA_2; } else if (id->dma_mword & 0x0002) { - drive->id->dma_ultra &= ~0xFF00; - if (!((id->dma_mword >> 8) & 2)) { - drive->id->dma_mword &= ~0x0F00; - drive->id->dma_mword |= 0x0202; - drive->id->dma_1word &= ~0x0F00; - } speed = XFER_MW_DMA_1; } else if (id->dma_mword & 0x0001) { - drive->id->dma_ultra &= ~0xFF00; - if (!((id->dma_mword >> 8) & 1)) { - drive->id->dma_mword &= ~0x0F00; - drive->id->dma_mword |= 0x0101; - drive->id->dma_1word &= ~0x0F00; - } speed = XFER_MW_DMA_0; } else if (id->dma_1word & 0x0004) { - drive->id->dma_ultra &= ~0xFF00; - if (!((id->dma_1word >> 8) & 4)) { - drive->id->dma_1word &= ~0x0F00; - drive->id->dma_1word |= 0x0404; - drive->id->dma_mword &= ~0x0F00; - } speed = XFER_SW_DMA_2; } else if (id->dma_1word & 0x0002) { - drive->id->dma_ultra &= ~0xFF00; - if (!((id->dma_1word >> 8) & 2)) { - drive->id->dma_1word &= ~0x0F00; - drive->id->dma_1word |= 0x0202; - drive->id->dma_mword &= ~0x0F00; - } speed = XFER_SW_DMA_1; } else if (id->dma_1word & 0x0001) { - drive->id->dma_ultra &= ~0xFF00; - if (!((id->dma_1word >> 8) & 1)) { - drive->id->dma_1word &= ~0x0F00; - drive->id->dma_1word |= 0x0101; - drive->id->dma_mword &= ~0x0F00; - } speed = XFER_SW_DMA_0; } else { +#if HPT366_DEBUG_DRIVE_INFO + printk("%s: config_chipset_for_dma: returning 'ide_dma_off_quietly'\n", drive->name); +#endif /* HPT366_DEBUG_DRIVE_INFO */ return ((int) ide_dma_off_quietly); } + /* Disable the "fast interrupt" prediction. + * Instead, always wait for the real interrupt from the drive! + */ + { + byte reg51h = 0; + pci_read_config_byte(HWIF(drive)->pci_dev, 0x51, ®51h); + if (reg51h & 0x80) + pci_write_config_byte(HWIF(drive)->pci_dev, 0x51, reg51h & ~0x80); + } + + /* + * Preserve existing PIO settings: + */ + pci_read_config_dword(HWIF(drive)->pci_dev, 0x40, ®40); + speed = (speed & ~0xc0000000) | (reg40 & 0xc0000000); + +#if HPT366_DEBUG_DRIVE_INFO + printk("%s: config_chipset_for_dma: speed=0x%04x\n", drive->name, speed); +#endif /* HPT366_DEBUG_DRIVE_INFO */ (void) hpt366_tune_chipset(drive, speed); - return ((int) ((id->dma_ultra >> 11) & 3) ? ide_dma_on : + rval = (int)( ((id->dma_ultra >> 11) & 3) ? ide_dma_on : ((id->dma_ultra >> 8) & 7) ? ide_dma_on : ((id->dma_mword >> 8) & 7) ? ide_dma_on : ((id->dma_1word >> 8) & 7) ? ide_dma_on : ide_dma_off_quietly); + +#if HPT366_DEBUG_DRIVE_INFO + printk("%s: config_chipset_for_dma: returning %d (%s)\n", drive->name, rval, rval == ide_dma_on ? "dma_on" : "dma_off"); +#endif /* HPT366_DEBUG_DRIVE_INFO */ + return rval; } static void config_chipset_for_pio (ide_drive_t *drive) { unsigned short eide_pio_timing[6] = {960, 480, 240, 180, 120, 90}; unsigned short xfer_pio = drive->id->eide_pio_modes; - byte timing, speed, pio; + unsigned int reg40 = 0; +#if HPT366_DEBUG_DRIVE_INFO + printk("%s: config_chipset_for_pio\n", drive->name); +#endif /* HPT366_DEBUG_DRIVE_INFO */ pio = ide_get_best_pio_mode(drive, 255, 5, NULL); if (xfer_pio> 4) @@ -353,6 +330,14 @@ static void config_chipset_for_pio (ide_drive_t *drive) speed = (!drive->id->tPIO) ? XFER_PIO_0 : XFER_PIO_SLOW; break; } + /* + * Preserve existing DMA settings: + */ + pci_read_config_dword(HWIF(drive)->pci_dev, 0x40, ®40); + speed = (speed & ~0x30070000) | (reg40 & 0x30070000); +#if HPT366_DEBUG_DRIVE_INFO + printk("%s: config_chipset_for_pio: speed=0x%04x\n", drive->name, speed); +#endif /* HPT366_DEBUG_DRIVE_INFO */ (void) hpt366_tune_chipset(drive, speed); } @@ -428,8 +413,9 @@ no_dma_set: int hpt366_dmaproc (ide_dma_action_t func, ide_drive_t *drive) { +#if 0 byte reg50h = 0, reg52h = 0; - +#endif switch (func) { case ide_dma_check: return config_drive_xfer_rate(drive); @@ -463,28 +449,81 @@ int hpt366_dmaproc (ide_dma_action_t func, ide_drive_t *drive) unsigned int __init pci_init_hpt366 (struct pci_dev *dev, const char *name) { - byte ata66 = 0; + byte test = 0; - pci_read_config_byte(dev, 0x5a, &ata66); if (dev->resource[PCI_ROM_RESOURCE].start) pci_write_config_byte(dev, PCI_ROM_ADDRESS, dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE); - printk("%s: reg5ah=0x%02x ATA-%s Cable Port%d\n", name, ata66, (ata66 & 0x02) ? "33" : "66", PCI_FUNC(dev->devfn)); + + pci_read_config_byte(dev, PCI_CACHE_LINE_SIZE, &test); + if (test != 0x08) + pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 0x08); + + pci_read_config_byte(dev, PCI_LATENCY_TIMER, &test); + if (test != 0x78) + pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x78); + + pci_read_config_byte(dev, PCI_MIN_GNT, &test); + if (test != 0x08) + pci_write_config_byte(dev, PCI_MIN_GNT, 0x08); + + pci_read_config_byte(dev, PCI_MAX_LAT, &test); + if (test != 0x08) + pci_write_config_byte(dev, PCI_MAX_LAT, 0x08); + return dev->irq; } +unsigned int __init ata66_hpt366 (ide_hwif_t *hwif) +{ + byte ata66 = 0; + + pci_read_config_byte(hwif->pci_dev, 0x5a, &ata66); +#ifdef DEBUG + printk("HPT366: reg5ah=0x%02x ATA-%s Cable Port%d\n", + ata66, (ata66 & 0x02) ? "33" : "66", + PCI_FUNC(hwif->pci_dev->devfn)); +#endif /* DEBUG */ + return ((ata66 & 0x02) ? 0 : 1); +} + void __init ide_init_hpt366 (ide_hwif_t *hwif) { +#if 0 + if ((PCI_FUNC(hwif->pci_dev->devfn) & 1) && (hpt363_shared_irq)) { + hwif->mate = &ide_hwifs[hwif->index-1]; + hwif->mate->mate = hwif; + hwif->serialized = hwif->mate->serialized = 1; + } +#endif + hwif->tuneproc = &hpt366_tune_drive; if (hwif->dma_base) { - byte ata66 = 0; - hwif->dmaproc = &hpt366_dmaproc; - pci_read_config_byte(hwif->pci_dev, 0x5a, &ata66); - hwif->udma_four = (ata66 & 0x02) ? 0 : 1; } else { - hwif->udma_four = 0; hwif->autodma = 0; hwif->drives[0].autotune = 1; hwif->drives[1].autotune = 1; } } + +void ide_dmacapable_hpt366 (ide_hwif_t *hwif, unsigned long dmabase) +{ + byte masterdma = 0, slavedma = 0; + byte dma_new = 0, dma_old = inb(dmabase+2); + unsigned long flags; + + __save_flags(flags); /* local CPU only */ + __cli(); /* local CPU only */ + + dma_new = dma_old; + pci_read_config_byte(hwif->pci_dev, 0x43, &masterdma); + pci_read_config_byte(hwif->pci_dev, 0x47, &slavedma); + + if (masterdma & 0x30) dma_new |= 0x20; + if (slavedma & 0x30) dma_new |= 0x40; + if (dma_new != dma_old) outb(dma_new, dmabase+2); + + __restore_flags(flags); /* local CPU only */ + + ide_setup_dma(hwif, dmabase, 8); +} diff --git a/drivers/block/icside.c b/drivers/block/icside.c index 629ad927d..5d007fce3 100644 --- a/drivers/block/icside.c +++ b/drivers/block/icside.c @@ -288,7 +288,6 @@ icside_build_dmatable(ide_drive_t *drive, int reading) static int icside_config_drive(ide_drive_t *drive, int mode) { - ide_hwif_t *hwif = HWIF(drive); int speed, err; if (mode == 2) { @@ -382,7 +381,8 @@ icside_dmaproc(ide_dma_action_t func, ide_drive_t *drive) if (drive->media != ide_disk) return 0; - ide_set_handler(drive, &ide_dma_intr, WAIT_CMD); + drive->timeout = WAIT_CMD; + ide_set_handler(drive, &ide_dma_intr); OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG); diff --git a/drivers/block/ide-cd.c b/drivers/block/ide-cd.c index b3ba03562..19fe18fe0 100644 --- a/drivers/block/ide-cd.c +++ b/drivers/block/ide-cd.c @@ -339,14 +339,15 @@ void cdrom_analyze_sense_data (ide_drive_t *drive, failed_command->c[0] == GPCMD_READ_SUBCHANNEL) return; } + if (reqbuf->error_code == 0x70 && reqbuf->sense_key == 0x02 && ((reqbuf->asc == 0x3a && reqbuf->ascq == 0x00) || (reqbuf->asc == 0x04 && reqbuf->ascq == 0x01))) { /* * Suppress the following errors: - * "Medium not present", and "in progress of becoming ready", - * to keep the noise level down to a dull roar. + * "Medium not present", "in progress of becoming ready", + * and "writing" to keep the noise level down to a dull roar. */ return; } @@ -431,6 +432,19 @@ void cdrom_analyze_sense_data (ide_drive_t *drive, printk ("\"\n"); } + /* The SKSV bit specifies validity of the sense_key_specific + * in the next two commands. It is bit 7 of the first byte. + * In the case of NOT_READY, if SKSV is set the drive can + * give us nice ETA readings. + */ + if (reqbuf->sense_key == NOT_READY && + (reqbuf->sense_key_specific[0] & 0x80)) { + int progress = (reqbuf->sense_key_specific[1] << 8 | + reqbuf->sense_key_specific[2]) * 100; + printk(" Command is %02d%% complete\n", progress / 0xffff); + + } + if (reqbuf->sense_key == ILLEGAL_REQUEST && (reqbuf->sense_key_specific[0] & 0x80) != 0) { printk (" Error in %s byte %d", @@ -466,21 +480,6 @@ void cdrom_analyze_sense_data (ide_drive_t *drive, #endif /* not VERBOSE_IDE_CD_ERRORS */ } - -/* Fix up a possibly partially-processed request so that we can - start it over entirely, or even put it back on the request queue. */ -static void restore_request (struct request *rq) -{ - if (rq->buffer != rq->bh->b_data) { - int n = (rq->buffer - rq->bh->b_data) / SECTOR_SIZE; - rq->buffer = rq->bh->b_data; - rq->nr_sectors += n; - rq->sector -= n; - } - rq->current_nr_sectors = rq->bh->b_size >> SECTOR_BITS; -} - - static void cdrom_queue_request_sense (ide_drive_t *drive, struct semaphore *sem, struct atapi_request_sense *reqbuf, @@ -533,10 +532,8 @@ static void cdrom_end_request (int uptodate, ide_drive_t *drive) struct packet_command *pc = (struct packet_command *) rq->buffer; cdrom_analyze_sense_data (drive, - (struct atapi_request_sense *) - (pc->buffer - pc->c[4]), - (struct packet_command *) - pc->sense_data); + (struct atapi_request_sense *) (pc->buffer - pc->c[4]), + (struct packet_command *) pc->sense_data); } if (rq->cmd == READ && !rq->current_nr_sectors) uptodate = 1; @@ -680,7 +677,7 @@ static int cdrom_start_packet_command (ide_drive_t *drive, int xferlen, struct cdrom_info *info = drive->driver_data; /* Wait for the controller to be idle. */ - if (ide_wait_stat (drive, 0, BUSY_STAT, WAIT_READY)) return 1; + if (ide_wait_stat(drive, 0, BUSY_STAT, WAIT_READY)) return 1; if (info->dma) info->dma = !HWIF(drive)->dmaproc(ide_dma_read, drive); @@ -699,7 +696,7 @@ static int cdrom_start_packet_command (ide_drive_t *drive, int xferlen, (void) (HWIF(drive)->dmaproc(ide_dma_begin, drive)); if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) { - ide_set_handler (drive, handler, WAIT_CMD); + ide_set_handler (drive, handler); OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */ } else { OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */ @@ -716,9 +713,14 @@ static int cdrom_start_packet_command (ide_drive_t *drive, int xferlen, HANDLER is the interrupt handler to call when the command completes or there's data ready. */ static int cdrom_transfer_packet_command (ide_drive_t *drive, - char *cmd_buf, int cmd_len, + unsigned char *cmd_buf, int cmd_len, ide_handler_t *handler) { + /* don't timeout for blank and format commands. they may take + * a _very_ long time. */ + if (cmd_buf[0] == GPCMD_BLANK || cmd_buf[0] == GPCMD_FORMAT_UNIT) + drive->timeout = 0; + if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) { /* Here we should have been called after receiving an interrupt from the device. DRQ should how be set. */ @@ -734,7 +736,7 @@ static int cdrom_transfer_packet_command (ide_drive_t *drive, } /* Arm the interrupt handler. */ - ide_set_handler (drive, handler, WAIT_CMD); + ide_set_handler (drive, handler); /* Send the command to the device. */ atapi_output_bytes (drive, cmd_buf, cmd_len); @@ -770,13 +772,12 @@ static void cdrom_buffer_sectors (ide_drive_t *drive, unsigned long sector, /* If we don't yet have a sector buffer, try to allocate one. If we can't get one atomically, it's not fatal -- we'll just throw the data away rather than caching it. */ - if (info->sector_buffer == NULL) { - info->sector_buffer = (char *) kmalloc (SECTOR_BUFFER_SIZE, - GFP_ATOMIC); + if (info->buffer == NULL) { + info->buffer = (char *) kmalloc(SECTOR_BUFFER_SIZE, GFP_ATOMIC); /* If we couldn't get a buffer, don't try to buffer anything... */ - if (info->sector_buffer == NULL) + if (info->buffer == NULL) sectors_to_buffer = 0; } @@ -785,7 +786,7 @@ static void cdrom_buffer_sectors (ide_drive_t *drive, unsigned long sector, info->sector_buffered = sector; /* Read the data into the buffer. */ - dest = info->sector_buffer + info->nsectors_buffered * SECTOR_SIZE; + dest = info->buffer + info->nsectors_buffered * SECTOR_SIZE; while (sectors_to_buffer > 0) { atapi_input_bytes (drive, dest, SECTOR_SIZE); --sectors_to_buffer; @@ -802,7 +803,6 @@ static void cdrom_buffer_sectors (ide_drive_t *drive, unsigned long sector, } } - /* * Check the contents of the interrupt reason register from the cdrom * and attempt to recover if there are problems. Returns 0 if everything's @@ -827,6 +827,12 @@ int cdrom_read_check_ireason (ide_drive_t *drive, int len, int ireason) atapi_output_bytes (drive, &dum, sizeof (dum)); len -= sizeof (dum); } + } else if (ireason == 1) { + /* Some drives (ASUS) seem to tell us that status + * info is available. just get it and ignore. + */ + GET_STAT(); + return 0; } else { /* Drive wants a command packet, or invalid ireason... */ printk ("%s: cdrom_read_intr: bad interrupt reason %d\n", @@ -837,7 +843,6 @@ int cdrom_read_check_ireason (ide_drive_t *drive, int len, int ireason) return -1; } - /* * Interrupt routine. Called when a read request has completed. */ @@ -912,8 +917,7 @@ static void cdrom_read_intr (ide_drive_t *drive) /* First, figure out if we need to bit-bucket any of the leading sectors. */ - nskip = MIN ((int)(rq->current_nr_sectors - - (rq->bh->b_size >> SECTOR_BITS)), + nskip = MIN ((int)(rq->current_nr_sectors - (rq->bh->b_size >> SECTOR_BITS)), sectors_to_transfer); while (nskip > 0) { @@ -939,8 +943,7 @@ static void cdrom_read_intr (ide_drive_t *drive) /* If the buffers are full, cache the rest of the data in our internal buffer. */ if (rq->current_nr_sectors == 0) { - cdrom_buffer_sectors (drive, - rq->sector, sectors_to_transfer); + cdrom_buffer_sectors(drive, rq->sector, sectors_to_transfer); sectors_to_transfer = 0; } else { /* Transfer data to the buffers. @@ -952,8 +955,7 @@ static void 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_input_bytes(drive, rq->buffer, SECTOR_SIZE); rq->buffer += SECTOR_SIZE; --rq->nr_sectors; --rq->current_nr_sectors; @@ -966,10 +968,9 @@ static void cdrom_read_intr (ide_drive_t *drive) /* Done moving data! Wait for another interrupt. */ - ide_set_handler (drive, &cdrom_read_intr, WAIT_CMD); + ide_set_handler(drive, &cdrom_read_intr); } - /* * Try to satisfy some of the current read request from our cached data. * Returns nonzero if the request has been completed, zero otherwise. @@ -980,7 +981,7 @@ static int cdrom_read_from_buffer (ide_drive_t *drive) struct request *rq = HWGROUP(drive)->rq; /* Can't do anything if there's no buffer. */ - if (info->sector_buffer == NULL) return 0; + if (info->buffer == NULL) return 0; /* Loop while this request needs data and the next block is present in our cache. */ @@ -991,7 +992,7 @@ static int cdrom_read_from_buffer (ide_drive_t *drive) cdrom_end_request (1, drive); memcpy (rq->buffer, - info->sector_buffer + + info->buffer + (rq->sector - info->sector_buffered) * SECTOR_SIZE, SECTOR_SIZE); rq->buffer += SECTOR_SIZE; @@ -1026,8 +1027,6 @@ static int cdrom_read_from_buffer (ide_drive_t *drive) return 0; } - - /* * Routine to send a read packet command to the drive. * This is usually called directly from cdrom_start_read. @@ -1056,14 +1055,13 @@ static void cdrom_start_read_continuation (ide_drive_t *drive) nskip = (sector % SECTORS_PER_FRAME); if (nskip > 0) { /* Sanity check... */ - if (rq->current_nr_sectors != - (rq->bh->b_size >> SECTOR_BITS)) { - printk ("%s: cdrom_start_read_continuation: buffer botch (%ld)\n", + if (rq->current_nr_sectors != (rq->bh->b_size >> SECTOR_BITS) && + (rq->sector % CD_FRAMESIZE != 0)) { + printk ("%s: cdrom_start_read_continuation: buffer botch (%lu)\n", drive->name, rq->current_nr_sectors); cdrom_end_request (0, drive); return; } - sector -= nskip; nsect += nskip; rq->current_nr_sectors += nskip; @@ -1091,9 +1089,10 @@ static void cdrom_start_read_continuation (ide_drive_t *drive) &cdrom_read_intr); } + #define IDECD_SEEK_THRESHOLD (1000) /* 1000 blocks */ -#define IDECD_SEEK_TIMER (2 * WAIT_MIN_SLEEP) /* 40 ms */ -#define IDECD_SEEK_TIMEOUT WAIT_CMD /* 10 sec */ +#define IDECD_SEEK_TIMER (5 * WAIT_MIN_SLEEP) /* 100 ms */ +#define IDECD_SEEK_TIMEOUT WAIT_CMD /* 10 sec */ static void cdrom_seek_intr (ide_drive_t *drive) { @@ -1107,7 +1106,7 @@ static void cdrom_seek_intr (ide_drive_t *drive) if (retry && jiffies - info->start_seek > IDECD_SEEK_TIMER) { if (--retry == 0) { - printk ("%s: disabled DSC seek overlap\n", drive->name); + printk("%s: disabled DSC seek overlap\n", drive->name); drive->dsc_overlap = 0; } } @@ -1127,7 +1126,7 @@ static void cdrom_start_seek_continuation (ide_drive_t *drive) memset (&pc.c, 0, sizeof (pc.c)); pc.c[0] = GPCMD_SEEK; - put_unaligned(htonl (frame), (unsigned int *) &pc.c[2]); + put_unaligned(cpu_to_be32(frame), (unsigned int *) &pc.c[2]); (void) cdrom_transfer_packet_command (drive, pc.c, sizeof (pc.c), &cdrom_seek_intr); } @@ -1140,6 +1139,19 @@ static void cdrom_start_seek (ide_drive_t *drive, unsigned int block) cdrom_start_packet_command (drive, 0, cdrom_start_seek_continuation); } +/* Fix up a possibly partially-processed request so that we can + start it over entirely, or even put it back on the request queue. */ +static void restore_request (struct request *rq) +{ + if (rq->buffer != rq->bh->b_data) { + int n = (rq->buffer - rq->bh->b_data) / SECTOR_SIZE; + rq->buffer = rq->bh->b_data; + rq->nr_sectors += n; + rq->sector -= n; + } + rq->current_nr_sectors = rq->bh->b_size >> SECTOR_BITS; +} + /* * Start a read request from the CD-ROM. */ @@ -1162,25 +1174,22 @@ static void cdrom_start_read (ide_drive_t *drive, unsigned int block) restore_request (rq); /* Satisfy whatever we can of this request from our cached sector. */ - if (cdrom_read_from_buffer (drive)) + if (cdrom_read_from_buffer(drive)) return; - /* Clear the local sector buffer. */ info->nsectors_buffered = 0; - if (drive->using_dma && (rq->sector % SECTORS_PER_FRAME == 0) && (rq->nr_sectors % SECTORS_PER_FRAME == 0)) + /* use dma, if possible. */ + if (drive->using_dma && (rq->sector % SECTORS_PER_FRAME == 0) && + (rq->nr_sectors % SECTORS_PER_FRAME == 0)) info->dma = 1; else info->dma = 0; /* Start sending the read request to the drive. */ - cdrom_start_packet_command (drive, 32768, - cdrom_start_read_continuation); + cdrom_start_packet_command(drive, 32768, cdrom_start_read_continuation); } - - - /**************************************************************************** * Execute all other packet commands. */ @@ -1197,6 +1206,9 @@ static void cdrom_pc_intr (ide_drive_t *drive) struct request *rq = HWGROUP(drive)->rq; struct packet_command *pc = (struct packet_command *)rq->buffer; + /* restore timeout after blank or format command */ + drive->timeout = WAIT_CMD; + /* Check for errors. */ if (cdrom_decode_status (drive, 0, &stat)) return; @@ -1282,7 +1294,7 @@ static void cdrom_pc_intr (ide_drive_t *drive) } /* Now we wait for another interrupt. */ - ide_set_handler (drive, &cdrom_pc_intr, WAIT_CMD); + ide_set_handler (drive, &cdrom_pc_intr); } @@ -1334,6 +1346,7 @@ int cdrom_queue_packet_command (ide_drive_t *drive, struct packet_command *pc) if (pc->sense_data == NULL) pc->sense_data = &my_reqbuf; pc->sense_data->sense_key = 0; + /* Start of retry loop. */ do { ide_init_drive_cmd (&req); @@ -1353,7 +1366,7 @@ int cdrom_queue_packet_command (ide_drive_t *drive, struct packet_command *pc) if (reqbuf->sense_key == UNIT_ATTENTION) cdrom_saw_media_change (drive); else if (reqbuf->sense_key == NOT_READY && - reqbuf->asc == 4) { + reqbuf->asc == 4 && reqbuf->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. */ @@ -1368,7 +1381,6 @@ int cdrom_queue_packet_command (ide_drive_t *drive, struct packet_command *pc) /* End of retry loop. */ } while (pc->stat != 0 && retries >= 0); - /* Return an error if the command failed. */ if (pc->stat != 0) return -EIO; @@ -1397,25 +1409,17 @@ int cdrom_queue_packet_command (ide_drive_t *drive, struct packet_command *pc) static void ide_do_rw_cdrom (ide_drive_t *drive, struct request *rq, unsigned long block) { - if (rq -> cmd == PACKET_COMMAND || rq -> cmd == REQUEST_SENSE_COMMAND) - cdrom_do_packet_command (drive); - else if (rq -> cmd == RESET_DRIVE_COMMAND) { - cdrom_end_request (1, drive); - ide_do_reset (drive); - return; - } else if (rq -> cmd != READ) { - printk ("ide-cd: bad cmd %d\n", rq -> cmd); - cdrom_end_request (0, drive); - } else { - struct cdrom_info *info = drive->driver_data; + struct cdrom_info *info = drive->driver_data; + switch (rq->cmd) { + case READ: { if (CDROM_CONFIG_FLAGS(drive)->seeking) { unsigned long elpased = jiffies - info->start_seek; int stat = GET_STAT(); if ((stat & SEEK_STAT) != SEEK_STAT) { if (elpased < IDECD_SEEK_TIMEOUT) { - ide_stall_queue (drive, IDECD_SEEK_TIMER); + ide_stall_queue(drive, IDECD_SEEK_TIMER); return; } printk ("%s: DSC timeout\n", drive->name); @@ -1427,6 +1431,26 @@ void ide_do_rw_cdrom (ide_drive_t *drive, struct request *rq, unsigned long bloc else cdrom_start_read (drive, block); info->last_block = block; + break; + } + + case PACKET_COMMAND: + case REQUEST_SENSE_COMMAND: { + cdrom_do_packet_command(drive); + break; + } + + case RESET_DRIVE_COMMAND: { + cdrom_end_request(1, drive); + ide_do_reset(drive); + break; + } + + default: { + printk("ide-cd: bad cmd %d\n", rq -> cmd); + cdrom_end_request(0, drive); + break; + } } } @@ -1600,7 +1624,7 @@ cdrom_read_capacity (ide_drive_t *drive, unsigned *capacity, stat = cdrom_queue_packet_command (drive, &pc); if (stat == 0) - *capacity = ntohl (capbuf.lba); + *capacity = be32_to_cpu(capbuf.lba); return stat; } @@ -1879,64 +1903,7 @@ int cdrom_get_toc_entry (ide_drive_t *drive, int track, -/* This gets the mechanism status per ATAPI draft spec 2.6 */ -static int -cdrom_read_mech_status (ide_drive_t *drive, char *buf, int buflen, - struct atapi_request_sense *reqbuf) -{ - struct packet_command pc; - - memset (&pc, 0, sizeof (pc)); - pc.sense_data = reqbuf; - - pc.buffer = buf; - pc.buflen = buflen; - pc.c[0] = GPCMD_MECHANISM_STATUS; - pc.c[8] = (buflen >> 8); - pc.c[9] = (buflen & 0xff); - return cdrom_queue_packet_command (drive, &pc); -} - -/* Read the drive mechanism status and slot table into our internal buffer. - If the buffer does not yet exist, allocate it. */ -static int -cdrom_read_changer_info (ide_drive_t *drive) -{ - int nslots; - struct cdrom_info *info = drive->driver_data; - - if (info->changer_info) - nslots = info->changer_info->hdr.nslots; - - else { - struct atapi_mechstat_header mechbuf; - int stat; - - stat = cdrom_read_mech_status (drive, - (char *)&mechbuf, - sizeof (mechbuf), - NULL); - if (stat) - return stat; - - nslots = mechbuf.nslots; - info->changer_info = - (struct atapi_changer_info *) - kmalloc (sizeof (struct atapi_changer_info) + - nslots * sizeof (struct atapi_slot), - GFP_KERNEL); - - if (info->changer_info == NULL) - return -ENOMEM; - } - return cdrom_read_mech_status - (drive, - (char *)&info->changer_info->hdr, - sizeof (struct atapi_mechstat_header) + - nslots * sizeof (struct atapi_slot), - NULL); -} /* the generic packet interface to cdrom.c */ static int ide_cdrom_packet(struct cdrom_device_info *cdi, @@ -1952,8 +1919,7 @@ static int ide_cdrom_packet(struct cdrom_device_info *cdi, memcpy(pc.c, cgc->cmd, CDROM_PACKET_SIZE); pc.buffer = cgc->buffer; pc.buflen = cgc->buflen; - cgc->stat = cdrom_queue_packet_command(drive, &pc); - return cgc->stat; + return cgc->stat = cdrom_queue_packet_command(drive, &pc); } static @@ -2130,16 +2096,19 @@ static int ide_cdrom_drive_status (struct cdrom_device_info *cdi, int slot_nr) { ide_drive_t *drive = (ide_drive_t*) cdi->handle; - struct cdrom_info *info = drive->driver_data; if (slot_nr == CDSL_CURRENT) { - struct atapi_request_sense my_reqbuf; - int stat = cdrom_check_status (drive, &my_reqbuf); - if (stat == 0 || my_reqbuf.sense_key == UNIT_ATTENTION) + struct atapi_request_sense sense; + int stat = cdrom_check_status (drive, &sense); + if (stat == 0 || sense.sense_key == UNIT_ATTENTION) + return CDS_DISC_OK; + + if (sense.sense_key == NOT_READY && sense.asc == 0x04 && + sense.ascq == 0x04) return CDS_DISC_OK; - if (my_reqbuf.sense_key == NOT_READY) { + if (sense.sense_key == NOT_READY) { /* ATAPI doesn't have anything that can help us decide whether the drive is really emtpy or the tray is just open. irk. */ @@ -2147,24 +2116,8 @@ int ide_cdrom_drive_status (struct cdrom_device_info *cdi, int slot_nr) } return CDS_DRIVE_NOT_READY; - } - -#if ! STANDARD_ATAPI - else if (cdi->sanyo_slot > 0) - return CDS_NO_INFO; -#endif /* not STANDARD_ATAPI */ - - else { - struct atapi_changer_info *ci; - int stat = cdrom_read_changer_info (drive); - if (stat < 0) - return stat; - ci = info->changer_info; - - if (ci->slots[slot_nr].disc_present) - return CDS_DISC_OK; - else - return CDS_NO_DISC; + } else { + return -EINVAL; } } @@ -2215,47 +2168,14 @@ int ide_cdrom_check_media_change_real (struct cdrom_device_info *cdi, int slot_nr) { ide_drive_t *drive = (ide_drive_t*) cdi->handle; - struct cdrom_info *info = drive->driver_data; - struct atapi_request_sense reqbuf; - int retval; if (slot_nr == CDSL_CURRENT) { (void) cdrom_check_status (drive, NULL); - retval = CDROM_STATE_FLAGS (drive)->media_changed; CDROM_STATE_FLAGS (drive)->media_changed = 0; + return CDROM_STATE_FLAGS (drive)->media_changed; + } else { + return -EINVAL; } - -#if ! STANDARD_ATAPI - else if (cdi->sanyo_slot > 0) { - retval = 0; - } -#endif /* not STANDARD_ATAPI */ - - else { - struct atapi_changer_info *ci; - int stat = cdrom_read_changer_info (drive); - if (stat < 0) - return stat; - ci = info->changer_info; - - /* This test may be redundant with cdrom.c. */ - if (slot_nr < 0 || slot_nr >= ci->hdr.nslots) - return -EINVAL; - - retval = ci->slots[slot_nr].change; - } - - /* if the media has changed, check if a disc is in the drive - and read the toc info. */ - if (retval || !CDROM_STATE_FLAGS (drive)->toc_valid) { - /* if cdrom_read_toc fails, return 1 to indicate - that a disc change has occured. there might not - be a disc in the drive. */ - if ((retval = cdrom_read_toc (drive, &reqbuf))) - return 1; - } - - return retval; } @@ -2361,11 +2281,12 @@ int ide_cdrom_probe_capabilities (ide_drive_t *drive) * be queued with ide_cdrom_packet(), which extracts the * drive from cdi->handle. Since this device hasn't been * registered with the Uniform layer yet, it can't do this. - * Same goes cdi->ops. + * Same goes for cdi->ops. */ cdi->handle = (ide_drive_t *) drive; cdi->ops = &ide_cdrom_dops; - do { /* we seem to get stat=0x01,err=0x00 the first time (??) */ + /* we seem to get stat=0x01,err=0x00 the first time (??) */ + do { if (attempts-- <= 0) return 0; stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CAPABILITIES_PAGE, 0); @@ -2402,14 +2323,9 @@ int ide_cdrom_probe_capabilities (ide_drive_t *drive) #endif /* not STANDARD_ATAPI */ if (buf.cap.mechtype == mechtype_individual_changer || buf.cap.mechtype == mechtype_cartridge_changer) { - struct atapi_mechstat_header mechbuf; - - stat = cdrom_read_mech_status (drive, (char*)&mechbuf, - sizeof (mechbuf), NULL); - if (!stat) { + if ((nslots = cdrom_number_of_slots(cdi)) > 1) { CDROM_CONFIG_FLAGS (drive)->is_changer = 1; CDROM_CONFIG_FLAGS (drive)->supp_disc_present = 1; - nslots = mechbuf.nslots; } } @@ -2445,7 +2361,7 @@ int ide_cdrom_probe_capabilities (ide_drive_t *drive) else printk (" drive"); - printk (", %dkB Cache", ntohs(buf.cap.buffer_size)); + printk (", %dkB Cache", be16_to_cpu(buf.cap.buffer_size)); if (drive->using_dma) { if ((drive->id->field_valid & 4) && @@ -2473,7 +2389,6 @@ static void ide_cdrom_add_settings(ide_drive_t *drive) ide_add_setting(drive, "dsc_overlap", SETTING_RW, -1, -1, TYPE_BYTE, 0, 1, 1, 1, &drive->dsc_overlap, NULL); } - static int ide_cdrom_setup (ide_drive_t *drive) { @@ -2482,13 +2397,12 @@ int ide_cdrom_setup (ide_drive_t *drive) int minor = drive->select.b.unit << PARTN_BITS; int nslots; - kdev_t dev = MKDEV(HWIF(drive)->major, minor); - - set_device_ro (dev, 1); + set_device_ro(MKDEV(HWIF(drive)->major, minor), 1); blksize_size[HWIF(drive)->major][minor] = CD_FRAMESIZE; - drive->special.all = 0; - drive->ready_stat = 0; + drive->special.all = 0; + drive->ready_stat = 0; + drive->timeout = WAIT_CMD; CDROM_STATE_FLAGS (drive)->media_changed = 1; CDROM_STATE_FLAGS (drive)->toc_valid = 0; @@ -2589,11 +2503,13 @@ int ide_cdrom_setup (ide_drive_t *drive) } #endif /* not STANDARD_ATAPI */ - info->toc = NULL; - info->sector_buffer = NULL; - info->sector_buffered = 0; - info->nsectors_buffered = 0; + info->toc = NULL; + info->buffer = NULL; + info->sector_buffered = 0; + info->nsectors_buffered = 0; info->changer_info = NULL; + info->last_block = 0; + info->start_seek = 0; nslots = ide_cdrom_probe_capabilities (drive); @@ -2653,15 +2569,15 @@ int ide_cdrom_cleanup(ide_drive_t *drive) if (ide_unregister_subdriver (drive)) return 1; - if (info->sector_buffer != NULL) - kfree (info->sector_buffer); + if (info->buffer != NULL) + kfree(info->buffer); if (info->toc != NULL) - kfree (info->toc); + kfree(info->toc); if (info->changer_info != NULL) - kfree (info->changer_info); + kfree(info->changer_info); if (devinfo->handle == drive && unregister_cdrom (devinfo)) printk ("%s: ide_cdrom_cleanup failed to unregister device from the cdrom driver.\n", drive->name); - kfree (info); + kfree(info); drive->driver_data = NULL; return 0; } diff --git a/drivers/block/ide-cd.h b/drivers/block/ide-cd.h index ae1a252ed..b65baa5fe 100644 --- a/drivers/block/ide-cd.h +++ b/drivers/block/ide-cd.h @@ -34,19 +34,13 @@ #define NO_DOOR_LOCKING 0 #endif - -/* Size of buffer to allocate, in blocks, for audio reads. */ - -#ifndef CDROM_NBLOCKS_BUFFER -#define CDROM_NBLOCKS_BUFFER 8 -#endif - - /************************************************************************/ -#define SECTOR_SIZE 512 -#define SECTOR_BITS 9 -#define SECTORS_PER_FRAME (CD_FRAMESIZE / SECTOR_SIZE) +#define SECTOR_SIZE 512 +#define SECTOR_BITS 9 +#define SECTORS_PER_FRAME (CD_FRAMESIZE / SECTOR_SIZE) +#define SECTOR_BUFFER_SIZE (CD_FRAMESIZE * 32) +#define SECTORS_BUFFER (SECTOR_BUFFER_SIZE / SECTOR_SIZE) #define MIN(a,b) ((a) < (b) ? (a) : (b)) @@ -84,7 +78,8 @@ struct ide_cd_config_flags { __u8 seeking : 1; /* Seeking in progress */ __u8 audio_play : 1; /* can do audio related commands */ __u8 close_tray : 1; /* can close the tray */ - __u8 reserved : 4; + __u8 writing : 1; /* pseudo write in progress */ + __u8 reserved : 3; byte max_speed; /* Max speed of the drive */ }; #define CDROM_CONFIG_FLAGS(drive) (&(((struct cdrom_info *)(drive->driver_data))->config_flags)) @@ -96,7 +91,8 @@ struct ide_cd_state_flags { __u8 media_changed : 1; /* Driver has noticed a media change. */ __u8 toc_valid : 1; /* Saved TOC information is current. */ __u8 door_locked : 1; /* We think that the drive door is locked. */ - __u8 reserved : 5; + __u8 writing : 1; /* the drive is currently writing */ + __u8 reserved : 4; byte current_speed; /* Current speed of the drive */ }; #define CDROM_STATE_FLAGS(drive) (&(((struct cdrom_info *)(drive->driver_data))->state_flags)) @@ -487,13 +483,11 @@ struct atapi_slot { byte reserved2[3]; }; - struct atapi_changer_info { struct atapi_mechstat_header hdr; struct atapi_slot slots[0]; }; - /* Extra per-device info for cdrom drives. */ struct cdrom_info { @@ -502,17 +496,9 @@ struct cdrom_info { struct atapi_toc *toc; - /* Sector buffer. If a read request wants only the first part - of a cdrom block, we cache the rest of the block here, - in the expectation that the data is going to be wanted soon. - SECTOR_BUFFERED is the number of the first buffered sector, - and NSECTORS_BUFFERED is the number of sectors in the buffer. - Before the buffer is allocated, we should have - SECTOR_BUFFER == NULL and NSECTORS_BUFFERED == 0. */ - - unsigned long sector_buffered; - unsigned long nsectors_buffered; - char *sector_buffer; + unsigned long sector_buffered; + unsigned long nsectors_buffered; + unsigned char *buffer; /* The result of the last successful request sense command on this device. */ @@ -533,10 +519,6 @@ struct cdrom_info { struct cdrom_device_info devinfo; }; - -#define SECTOR_BUFFER_SIZE CD_FRAMESIZE - - /**************************************************************************** * Descriptions of ATAPI error codes. */ @@ -652,6 +634,7 @@ const struct { { 0x000013, "Play operation successfully completed" }, { 0x000014, "Play operation stopped due to error" }, { 0x000015, "No current audio status to return" }, + { 0x010c0a, "Write error - padding blocks added" }, { 0x011700, "Recovered data with no error correction applied" }, { 0x011701, "Recovered data with retries" }, { 0x011702, "Recovered data with positive head offset" }, @@ -668,6 +651,7 @@ const struct { { 0x015d01, "Failure prediction threshold exceeded - Predicted media failure" }, { 0x015dff, "Failure prediction threshold exceeded - False" }, + { 0x017301, "Power calibration area almost full" }, { 0x020400, "Logical unit not ready - cause not reportable" }, /* Following is misspelled in ATAPI 2.6, _and_ in Mt. Fuji */ { 0x020401, @@ -680,9 +664,35 @@ const struct { { 0x023a00, "Medium not present" }, { 0x025300, "Media load or eject failed" }, { 0x025700, "Unable to recover table of contents" }, + { 0x030300, "Peripheral device write fault" }, + { 0x030301, "No write current" }, + { 0x030302, "Excessive write errors" }, + { 0x030c00, "Write error" }, + { 0x030c01, "Write error - Recovered with auto reallocation" }, + { 0x030c02, "Write error - auto reallocation failed" }, + { 0x030c03, "Write error - recommend reassignment" }, + { 0x030c04, "Compression check miscompare error" }, + { 0x030c05, "Data expansion occurred during compress" }, + { 0x030c06, "Block not compressible" }, + { 0x030c07, "Write error - recovery needed" }, + { 0x030c08, "Write error - recovery failed" }, + { 0x030c09, "Write error - loss of streaming" }, { 0x031100, "Unrecovered read error" }, { 0x031106, "CIRC unrecovered error" }, { 0x033101, "Format command failed" }, + { 0x033200, "No defect spare location available" }, + { 0x033201, "Defect list update failure" }, + { 0x035100, "Erase failure" }, + { 0x037200, "Session fixation error" }, + { 0x037201, "Session fixation error writin lead-in" }, + { 0x037202, "Session fixation error writin lead-out" }, + { 0x037300, "CD control error" }, + { 0x037302, "Power calibration area is full" }, + { 0x037303, "Power calibration area error" }, + { 0x037304, "Program memory area / RMA update failure" }, + { 0x037305, "Program memory area / RMA is full" }, + { 0x037306, "Program memory area / RMA is (almost) full" }, + { 0x040200, "No seek complete" }, { 0x040300, "Write fault" }, { 0x040900, "Track following error" }, @@ -700,11 +710,15 @@ const struct { { 0x052000, "Invalid command operation code" }, { 0x052c00, "Command sequence error" }, { 0x052100, "Logical block address out of range" }, + { 0x052102, "Invalid address for write" }, { 0x052400, "Invalid field in command packet" }, { 0x052600, "Invalid field in parameter list" }, { 0x052601, "Parameter not supported" }, { 0x052602, "Parameter value invalid" }, { 0x052700, "Write protected media" }, + { 0x052c00, "Command sequence error" }, + { 0x052c03, "Current program area is not empty" }, + { 0x052c04, "Current program area is empty" }, { 0x053001, "Cannot read medium - unknown format" }, { 0x053002, "Cannot read medium - incompatible format" }, { 0x053900, "Saving parameters not supported" }, @@ -713,11 +727,15 @@ const struct { { 0x055500, "System resource failure" }, { 0x056300, "End of user area encountered on this track" }, { 0x056400, "Illegal mode for this track or incompatible medium" }, - { 0x056f00, - "Copy protection key exchange failure - Authentication failure" }, + { 0x056f00, "Copy protection key exchange failure - Authentication failure" }, { 0x056f01, "Copy protection key exchange failure - Key not present" }, - { 0x056f02, - "Copy protection key exchange failure - Key not established" }, + { 0x056f02, "Copy protection key exchange failure - Key not established" }, + { 0x056f03, "Read of scrambled sector without authentication" }, + { 0x056f04, "Media region code is mismatched to logical unit" }, + { 0x056f05, "Drive region must be permanent / region reset count error" }, + { 0x057203, "Session fixation error - incomplete track in session" }, + { 0x057204, "Empty or partially written reserved track" }, + { 0x057205, "No more RZONE reservations are allowed" }, { 0x05bf00, "Loss of streaming" }, { 0x062800, "Not ready to ready transition, medium may have changed" }, { 0x062900, "Power on, reset or hardware reset occurred" }, diff --git a/drivers/block/ide-disk.c b/drivers/block/ide-disk.c index d2941a1ab..8fa7745b4 100644 --- a/drivers/block/ide-disk.c +++ b/drivers/block/ide-disk.c @@ -175,7 +175,7 @@ read_next: if (i > 0) { if (msect) goto read_next; - ide_set_handler (drive, &read_intr, WAIT_CMD); + ide_set_handler (drive, &read_intr); } } @@ -206,7 +206,7 @@ static void write_intr (ide_drive_t *drive) ide_end_request(1, hwgroup); if (i > 0) { idedisk_output_data (drive, rq->buffer, SECTOR_WORDS); - ide_set_handler (drive, &write_intr, WAIT_CMD); + ide_set_handler (drive, &write_intr); } goto out; } @@ -271,7 +271,7 @@ static void multwrite_intr (ide_drive_t *drive) if (stat & DRQ_STAT) { if (rq->nr_sectors) { ide_multwrite(drive, drive->mult_count); - ide_set_handler (drive, &multwrite_intr, WAIT_CMD); + ide_set_handler (drive, &multwrite_intr); goto out; } } else { @@ -385,7 +385,7 @@ static void do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long bl if (drive->using_dma && !(HWIF(drive)->dmaproc(ide_dma_read, drive))) return; #endif /* CONFIG_BLK_DEV_IDEDMA */ - ide_set_handler(drive, &read_intr, WAIT_CMD); + ide_set_handler(drive, &read_intr); OUT_BYTE(drive->mult_count ? WIN_MULTREAD : WIN_READ, IDE_COMMAND_REG); return; } @@ -404,10 +404,10 @@ static void do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long bl __cli(); /* local CPU only */ if (drive->mult_count) { HWGROUP(drive)->wrq = *rq; /* scratchpad */ - ide_set_handler (drive, &multwrite_intr, WAIT_CMD); + ide_set_handler (drive, &multwrite_intr); ide_multwrite(drive, drive->mult_count); } else { - ide_set_handler (drive, &write_intr, WAIT_CMD); + ide_set_handler (drive, &write_intr); idedisk_output_data(drive, rq->buffer, SECTOR_WORDS); } return; @@ -506,6 +506,7 @@ static void idedisk_pre_reset (ide_drive_t *drive) drive->special.all = 0; drive->special.b.set_geometry = 1; drive->special.b.recalibrate = 1; + drive->timeout = WAIT_CMD; if (OK_TO_RESET_CONTROLLER) drive->mult_count = 0; if (!drive->keep_settings) diff --git a/drivers/block/ide-dma.c b/drivers/block/ide-dma.c index d3c657516..338d51c9a 100644 --- a/drivers/block/ide-dma.c +++ b/drivers/block/ide-dma.c @@ -420,7 +420,8 @@ int ide_dmaproc (ide_dma_action_t func, ide_drive_t *drive) drive->waiting_for_dma = 1; if (drive->media != ide_disk) return 0; - ide_set_handler(drive, &ide_dma_intr, WAIT_CMD);/* issue cmd to drive */ + drive->timeout = WAIT_CMD; + ide_set_handler(drive, &ide_dma_intr);/* issue cmd to drive */ OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG); case ide_dma_begin: /* Note that this is done *after* the cmd has @@ -460,7 +461,7 @@ int ide_dmaproc (ide_dma_action_t func, ide_drive_t *drive) int ide_release_dma (ide_hwif_t *hwif) { if (hwif->dmatable) { - clear_page((unsigned long)hwif->dmatable); /* clear PRD 1st */ + clear_page((void *)hwif->dmatable); /* clear PRD 1st */ free_page((unsigned long)hwif->dmatable); /* free PRD 2nd */ } if ((hwif->dma_extra) && (hwif->channel == 0)) @@ -537,14 +538,7 @@ unsigned long __init ide_get_or_set_dma_base (ide_hwif_t *hwif, int extra, const switch(dev->device) { case PCI_DEVICE_ID_CMD_643: -#ifdef CONFIG_BLK_DEV_ALI15X3 case PCI_DEVICE_ID_AL_M5219: - case PCI_DEVICE_ID_AL_M5229: - /* - * Ali 15x3 chipsets know as ALI IV and V report - * this as simplex, skip this test for them. - */ -#endif /* CONFIG_BLK_DEV_ALI15X3 */ outb(inb(dma_base+2) & 0x60, dma_base+2); if (inb(dma_base+2) & 0x80) { printk("%s: simplex device: DMA forced\n", name); diff --git a/drivers/block/ide-floppy.c b/drivers/block/ide-floppy.c index f1c8baef8..3db1c1515 100644 --- a/drivers/block/ide-floppy.c +++ b/drivers/block/ide-floppy.c @@ -916,7 +916,7 @@ static void idefloppy_pc_intr (ide_drive_t *drive) if (temp > pc->buffer_size) { printk (KERN_ERR "ide-floppy: The floppy wants to send us more data than expected - discarding data\n"); idefloppy_discard_data (drive,bcount.all); - ide_set_handler (drive,&idefloppy_pc_intr,IDEFLOPPY_WAIT_CMD); + ide_set_handler (drive,&idefloppy_pc_intr); return; } #if IDEFLOPPY_DEBUG_LOG @@ -938,7 +938,7 @@ static void idefloppy_pc_intr (ide_drive_t *drive) pc->actually_transferred+=bcount.all; /* Update the current position */ pc->current_position+=bcount.all; - ide_set_handler (drive,&idefloppy_pc_intr,IDEFLOPPY_WAIT_CMD); /* And set the interrupt handler again */ + ide_set_handler (drive,&idefloppy_pc_intr); /* And set the interrupt handler again */ } static void idefloppy_transfer_pc (ide_drive_t *drive) @@ -956,7 +956,7 @@ static void idefloppy_transfer_pc (ide_drive_t *drive) ide_do_reset (drive); return; } - ide_set_handler (drive, &idefloppy_pc_intr, IDEFLOPPY_WAIT_CMD); /* Set the interrupt routine */ + ide_set_handler (drive, &idefloppy_pc_intr); /* Set the interrupt routine */ atapi_output_bytes (drive, floppy->pc->c, 12); /* Send the actual packet */ } @@ -1025,7 +1025,7 @@ static void idefloppy_issue_pc (ide_drive_t *drive, idefloppy_pc_t *pc) #endif /* CONFIG_BLK_DEV_IDEDMA */ if (test_bit (IDEFLOPPY_DRQ_INTERRUPT, &floppy->flags)) { - ide_set_handler (drive, &idefloppy_transfer_pc, IDEFLOPPY_WAIT_CMD); + ide_set_handler (drive, &idefloppy_transfer_pc); OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* Issue the packet command */ } else { OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); @@ -1519,6 +1519,7 @@ static void idefloppy_setup (ide_drive_t *drive, idefloppy_floppy_t *floppy) *((unsigned short *) &gcw) = drive->id->config; drive->driver_data = floppy; drive->ready_stat = 0; + drive->timeout = IDEFLOPPY_WAIT_CMD; memset (floppy, 0, sizeof (idefloppy_floppy_t)); floppy->drive = drive; floppy->pc = floppy->pc_stack; diff --git a/drivers/block/ide-geometry.c b/drivers/block/ide-geometry.c index 974fb24f1..44aa1844d 100644 --- a/drivers/block/ide-geometry.c +++ b/drivers/block/ide-geometry.c @@ -5,6 +5,9 @@ #include <asm/io.h> +extern ide_drive_t * get_info_ptr(kdev_t); +extern unsigned long current_capacity (ide_drive_t *); + /* * We query CMOS about hard disks : it could be that we have a SCSI/ESDI/etc * controller that is BIOS compatible with ST-506, and thus showing up in our diff --git a/drivers/block/ide-pci.c b/drivers/block/ide-pci.c index 311bd470b..73ffa67ea 100644 --- a/drivers/block/ide-pci.c +++ b/drivers/block/ide-pci.c @@ -57,8 +57,9 @@ #define DEVID_CY82C693 ((ide_pci_devid_t){PCI_VENDOR_ID_CONTAQ, PCI_DEVICE_ID_CONTAQ_82C693}) #define DEVID_HINT ((ide_pci_devid_t){0x3388, 0x8013}) #define DEVID_CX5530 ((ide_pci_devid_t){PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_IDE}) +#define DEVID_AMD7409 ((ide_pci_devid_t){PCI_VENDOR_ID_AMD, 0x7409}) -#define IDE_IGNORE ((void *)-1) +#define IDE_IGNORE ((void *)-1) #ifdef CONFIG_BLK_DEV_TRM290 extern void ide_init_trm290(ide_hwif_t *); @@ -106,54 +107,66 @@ extern void ide_init_rz1000(ide_hwif_t *); #define INIT_RZ1000 IDE_IGNORE #endif -#ifdef CONFIG_BLK_DEV_VIA82C586 -extern unsigned int pci_init_via82c568(struct pci_dev *, const char *); -extern void ide_init_via82c586(ide_hwif_t *); -extern void ide_dmacapable_via82c586(ide_hwif_t *, unsigned long dmabase); -#define PCI_VIA82C586 &pci_init_via82c568 -#define INIT_VIA82C586 &ide_init_via82c586 -#define DMA_VIA82C586 &ide_dmacapable_via82c586 +#ifdef CONFIG_BLK_DEV_VIA82CXXX +extern unsigned int pci_init_via82cxxx(struct pci_dev *, const char *); +extern unsigned int ata66_via82cxxx(ide_hwif_t *); +extern void ide_init_via82cxxx(ide_hwif_t *); +extern void ide_dmacapable_via82cxxx(ide_hwif_t *, unsigned long); +#define PCI_VIA82CXXX &pci_init_via82cxxx +#define ATA66_VIA82CXXX &ata66_via82cxxx +#define INIT_VIA82CXXX &ide_init_via82cxxx +#define DMA_VIA82CXXX &ide_dmacapable_via82cxxx #else -#define PCI_VIA82C586 NULL -#define INIT_VIA82C586 NULL -#define DMA_VIA82C586 NULL +#define PCI_VIA82CXXX NULL +#define ATA66_VIA82CXXX NULL +#define INIT_VIA82CXXX NULL +#define DMA_VIA82CXXX NULL #endif #ifdef CONFIG_BLK_DEV_ALI15X3 extern unsigned int pci_init_ali15x3(struct pci_dev *, const char *); +extern unsigned int ata66_ali15x3(ide_hwif_t *); extern void ide_init_ali15x3(ide_hwif_t *); +extern void ide_dmacapable_ali15x3(ide_hwif_t *, unsigned long); #define PCI_ALI15X3 &pci_init_ali15x3 -#define INIT_ALI15X3 &ide_init_ali15x3 +#define ATA66_ALI15X3 &ata66_ali15x3 +#define INIT_ALI15X3 &ide_init_ali15x3 +#define DMA_ALI15X3 &ide_dmacapable_ali15x3 #else #define PCI_ALI15X3 NULL -#define INIT_ALI15X3 NULL +#define ATA66_ALI15X3 NULL +#define INIT_ALI15X3 NULL +#define DMA_ALI15X3 NULL #endif #ifdef CONFIG_BLK_DEV_CY82C693 extern unsigned int pci_init_cy82c693(struct pci_dev *, const char *); extern void ide_init_cy82c693(ide_hwif_t *); #define PCI_CY82C693 &pci_init_cy82c693 -#define INIT_CY82C693 &ide_init_cy82c693 +#define INIT_CY82C693 &ide_init_cy82c693 #else #define PCI_CY82C693 NULL -#define INIT_CY82C693 NULL +#define INIT_CY82C693 NULL #endif #ifdef CONFIG_BLK_DEV_PDC202XX extern unsigned int pci_init_pdc202xx(struct pci_dev *, const char *); +extern unsigned int ata66_pdc202xx(ide_hwif_t *); extern void ide_init_pdc202xx(ide_hwif_t *); #define PCI_PDC202XX &pci_init_pdc202xx -#define INIT_PDC202XX &ide_init_pdc202xx +#define ATA66_PDC202XX &ata66_pdc202xx +#define INIT_PDC202XX &ide_init_pdc202xx #else #define PCI_PDC202XX NULL -#define INIT_PDC202XX NULL +#define ATA66_PDC202XX NULL +#define INIT_PDC202XX NULL #endif #ifdef CONFIG_BLK_DEV_PIIX extern void ide_init_piix(ide_hwif_t *); -#define INIT_PIIX &ide_init_piix +#define INIT_PIIX &ide_init_piix #else -#define INIT_PIIX NULL +#define INIT_PIIX NULL #endif #ifdef CONFIG_BLK_DEV_AEC6210 @@ -167,29 +180,40 @@ extern unsigned int pci_init_aec6210(struct pci_dev *, const char *); extern unsigned int pci_init_hpt34x(struct pci_dev *, const char *); extern void ide_init_hpt34x(ide_hwif_t *); #define PCI_HPT34X &pci_init_hpt34x -#define INIT_HPT34X &ide_init_hpt34x +#define INIT_HPT34X &ide_init_hpt34x #else #define PCI_HPT34X NULL -#define INIT_HPT34X NULL +#define INIT_HPT34X NULL #endif #ifdef CONFIG_BLK_DEV_HPT366 +extern byte hpt363_shared_irq; extern unsigned int pci_init_hpt366(struct pci_dev *, const char *); +extern unsigned int ata66_hpt366(ide_hwif_t *); extern void ide_init_hpt366(ide_hwif_t *); +extern void ide_dmacapable_hpt366(ide_hwif_t *, unsigned long); #define PCI_HPT366 &pci_init_hpt366 +#define ATA66_HPT366 &ata66_hpt366 #define INIT_HPT366 &ide_init_hpt366 +#define DMA_HPT366 &ide_dmacapable_hpt366 #else +static byte hpt363_shared_irq = 0; #define PCI_HPT366 NULL -#define INIT_HPT366 IDE_IGNORE +#define ATA66_HPT366 NULL +#define INIT_HPT366 NULL +#define DMA_HPT366 NULL #endif #ifdef CONFIG_BLK_DEV_SIS5513 extern unsigned int pci_init_sis5513(struct pci_dev *, const char *); +extern unsigned int ata66_sis5513(ide_hwif_t *); extern void ide_init_sis5513(ide_hwif_t *); #define PCI_SIS5513 &pci_init_sis5513 +#define ATA66_SIS5513 &ata66_sis5513 #define INIT_SIS5513 &ide_init_sis5513 #else #define PCI_SIS5513 NULL +#define ATA66_SIS5513 NULL #define INIT_SIS5513 NULL #endif @@ -206,49 +230,48 @@ typedef struct ide_pci_device_s { ide_pci_devid_t devid; const char *name; unsigned int (*init_chipset)(struct pci_dev *dev, const char *name); + unsigned int (*ata66_check)(ide_hwif_t *hwif); void (*init_hwif)(ide_hwif_t *hwif); void (*dma_init)(ide_hwif_t *hwif, unsigned long dmabase); ide_pci_enablebit_t enablebits[2]; byte bootable; - byte sixtysix; unsigned int extra; } ide_pci_device_t; static ide_pci_device_t ide_pci_chipsets[] __initdata = { - {DEVID_PIIXa, "PIIX", NULL, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0, 0 }, - {DEVID_PIIXb, "PIIX", NULL, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0, 0 }, - {DEVID_PIIX3, "PIIX3", NULL, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0, 0 }, - {DEVID_PIIX4, "PIIX4", NULL, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0, 0 }, - {DEVID_VIA_IDE, "VIA_IDE", NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0, 0 }, - {DEVID_VP_IDE, "VP_IDE", PCI_VIA82C586, INIT_VIA82C586, DMA_VIA82C586, {{0x40,0x02,0x02}, {0x40,0x01,0x01}}, ON_BOARD, 0, 0 }, - {DEVID_PDC20246,"PDC20246", PCI_PDC202XX, INIT_PDC202XX, NULL, {{0x50,0x02,0x02}, {0x50,0x04,0x04}}, OFF_BOARD, 0, 16 }, - {DEVID_PDC20262,"PDC20262", PCI_PDC202XX, INIT_PDC202XX, NULL, {{0x50,0x02,0x02}, {0x50,0x04,0x04}}, OFF_BOARD, 1, 48 }, - {DEVID_RZ1000, "RZ1000", NULL, INIT_RZ1000, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0, 0 }, - {DEVID_RZ1001, "RZ1001", NULL, INIT_RZ1000, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0, 0 }, - {DEVID_SAMURAI, "SAMURAI", NULL, INIT_SAMURAI, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0, 0 }, - {DEVID_CMD640, "CMD640", NULL, IDE_IGNORE, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0, 0 }, - {DEVID_NS87410, "NS87410", NULL, NULL, NULL, {{0x43,0x08,0x08}, {0x47,0x08,0x08}}, ON_BOARD, 0, 0 }, - {DEVID_SIS5513, "SIS5513", PCI_SIS5513, INIT_SIS5513, NULL, {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}}, ON_BOARD, 1, 0 }, - {DEVID_CMD643, "CMD643", NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0, 0 }, - {DEVID_CMD646, "CMD646", NULL, INIT_CMD646, NULL, {{0x00,0x00,0x00}, {0x51,0x80,0x80}}, ON_BOARD, 0, 0 }, - {DEVID_HT6565, "HT6565", NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0, 0 }, - {DEVID_OPTI621, "OPTI621", NULL, INIT_OPTI621, NULL, {{0x45,0x80,0x00}, {0x40,0x08,0x00}}, ON_BOARD, 0, 0 }, - {DEVID_OPTI621X,"OPTI621X", NULL, INIT_OPTI621, NULL, {{0x45,0x80,0x00}, {0x40,0x08,0x00}}, ON_BOARD, 0, 0 }, - {DEVID_TRM290, "TRM290", NULL, INIT_TRM290, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0, 0 }, - {DEVID_NS87415, "NS87415", NULL, INIT_NS87415, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0, 0 }, - {DEVID_AEC6210, "AEC6210", PCI_AEC6210, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 0, 0 }, - {DEVID_W82C105, "W82C105", NULL, INIT_W82C105, NULL, {{0x40,0x01,0x01}, {0x40,0x10,0x10}}, ON_BOARD, 0, 0 }, - {DEVID_UM8886A, "UM8886A", NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0, 0 }, - {DEVID_UM8886BF,"UM8886BF", NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0, 0 }, - {DEVID_HPT34X, "HPT34X", PCI_HPT34X, INIT_HPT34X, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, NEVER_BOARD, 0, 16 }, - {DEVID_HPT366, "HPT366", PCI_HPT366, INIT_HPT366, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 1, 256 }, - {DEVID_ALI15X3, "ALI15X3", PCI_ALI15X3, INIT_ALI15X3, NULL, {{0x09,0x20,0x20}, {0x09,0x10,0x10}}, ON_BOARD, 0, 0 }, - {DEVID_CY82C693,"CY82C693", PCI_CY82C693, INIT_CY82C693, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0, 0 }, - {DEVID_HINT, "HINT_IDE", NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0, 0 }, - {DEVID_CX5530, "CX5530", NULL, INIT_CX5530, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0, 0 }, - {IDE_PCI_DEVID_NULL, "PCI_IDE", NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0, 0 }}; - -static byte hpt363_shared_irq = 0; + {DEVID_PIIXa, "PIIX", NULL, NULL, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, + {DEVID_PIIXb, "PIIX", NULL, NULL, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, + {DEVID_PIIX3, "PIIX3", NULL, NULL, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, + {DEVID_PIIX4, "PIIX4", NULL, NULL, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, + {DEVID_VIA_IDE, "VIA_IDE", NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, + {DEVID_VP_IDE, "VP_IDE", PCI_VIA82CXXX, ATA66_VIA82CXXX,INIT_VIA82CXXX, DMA_VIA82CXXX, {{0x40,0x02,0x02}, {0x40,0x01,0x01}}, ON_BOARD, 0 }, + {DEVID_PDC20246,"PDC20246", PCI_PDC202XX, NULL, INIT_PDC202XX, NULL, {{0x50,0x02,0x02}, {0x50,0x04,0x04}}, OFF_BOARD, 16 }, + {DEVID_PDC20262,"PDC20262", PCI_PDC202XX, ATA66_PDC202XX, INIT_PDC202XX, NULL, {{0x50,0x02,0x02}, {0x50,0x04,0x04}}, OFF_BOARD, 48 }, + {DEVID_RZ1000, "RZ1000", NULL, NULL, INIT_RZ1000, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, + {DEVID_RZ1001, "RZ1001", NULL, NULL, INIT_RZ1000, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, + {DEVID_SAMURAI, "SAMURAI", NULL, NULL, INIT_SAMURAI, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, + {DEVID_CMD640, "CMD640", NULL, NULL, IDE_IGNORE, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, + {DEVID_NS87410, "NS87410", NULL, NULL, NULL, NULL, {{0x43,0x08,0x08}, {0x47,0x08,0x08}}, ON_BOARD, 0 }, + {DEVID_SIS5513, "SIS5513", PCI_SIS5513, ATA66_SIS5513, INIT_SIS5513, NULL, {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}}, ON_BOARD, 0 }, + {DEVID_CMD643, "CMD643", NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, + {DEVID_CMD646, "CMD646", NULL, NULL, INIT_CMD646, NULL, {{0x00,0x00,0x00}, {0x51,0x80,0x80}}, ON_BOARD, 0 }, + {DEVID_HT6565, "HT6565", NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, + {DEVID_OPTI621, "OPTI621", NULL, NULL, INIT_OPTI621, NULL, {{0x45,0x80,0x00}, {0x40,0x08,0x00}}, ON_BOARD, 0 }, + {DEVID_OPTI621X,"OPTI621X", NULL, NULL, INIT_OPTI621, NULL, {{0x45,0x80,0x00}, {0x40,0x08,0x00}}, ON_BOARD, 0 }, + {DEVID_TRM290, "TRM290", NULL, NULL, INIT_TRM290, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, + {DEVID_NS87415, "NS87415", NULL, NULL, INIT_NS87415, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, + {DEVID_AEC6210, "AEC6210", PCI_AEC6210, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 0 }, + {DEVID_W82C105, "W82C105", NULL, NULL, INIT_W82C105, NULL, {{0x40,0x01,0x01}, {0x40,0x10,0x10}}, ON_BOARD, 0 }, + {DEVID_UM8886A, "UM8886A", NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, + {DEVID_UM8886BF,"UM8886BF", NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, + {DEVID_HPT34X, "HPT34X", PCI_HPT34X, NULL, INIT_HPT34X, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, NEVER_BOARD, 16 }, + {DEVID_HPT366, "HPT366", PCI_HPT366, ATA66_HPT366, INIT_HPT366, DMA_HPT366, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 256 }, + {DEVID_ALI15X3, "ALI15X3", PCI_ALI15X3, ATA66_ALI15X3, INIT_ALI15X3, DMA_ALI15X3, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, + {DEVID_CY82C693,"CY82C693", PCI_CY82C693, NULL, INIT_CY82C693, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, + {DEVID_HINT, "HINT_IDE", NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, + {DEVID_CX5530, "CX5530", NULL, NULL, INIT_CX5530, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, + {DEVID_AMD7409, "AMD7409", NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, + {IDE_PCI_DEVID_NULL, "PCI_IDE", NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }}; /* * This allows offboard ide-pci cards the enable a BIOS, verify interrupt @@ -260,24 +283,24 @@ static unsigned int __init ide_special_settings (struct pci_dev *dev, const char switch(dev->device) { case PCI_DEVICE_ID_TTI_HPT343: { + int i; + unsigned long hpt34xIoBase = dev->resource[4].start; unsigned short pcicmd = 0; pci_write_config_byte(dev, 0x80, 0x00); pci_read_config_word(dev, PCI_COMMAND, &pcicmd); if (!(pcicmd & PCI_COMMAND_MEMORY)) { - int i; - unsigned long hpt34xIoBase = dev->resource[4].start; - - dev->resource[0].start = (hpt34xIoBase + 0x20); - dev->resource[1].start = (hpt34xIoBase + 0x34); - dev->resource[2].start = (hpt34xIoBase + 0x28); - dev->resource[3].start = (hpt34xIoBase + 0x3c); - for(i=0; i<4; i++) - dev->resource[i].flags |= PCI_BASE_ADDRESS_SPACE_IO; pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x20); } else { pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0xF0); } + + dev->resource[0].start = (hpt34xIoBase + 0x20); + dev->resource[1].start = (hpt34xIoBase + 0x34); + dev->resource[2].start = (hpt34xIoBase + 0x28); + dev->resource[3].start = (hpt34xIoBase + 0x3c); + for(i=0; i<4; i++) + dev->resource[i].flags |= PCI_BASE_ADDRESS_SPACE_IO; } case PCI_DEVICE_ID_TTI_HPT366: case PCI_DEVICE_ID_PROMISE_20246: @@ -530,8 +553,7 @@ check_if_enabled: hwif->irq = hwif->channel ? 15 : 14; goto bypass_umc_dma; } - if ((!d->sixtysix) && (hwif->udma_four)) - hwif->udma_four = 0; + hwif->udma_four = (d->ata66_check) ? d->ata66_check(hwif) : 0; #ifdef CONFIG_BLK_DEV_IDEDMA if (IDE_PCI_DEVID_EQ(d->devid, DEVID_SIS5513) || IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT34X)) @@ -582,40 +604,35 @@ bypass_umc_dma: static void __init hpt366_device_order_fixup (struct pci_dev *dev, ide_pci_device_t *d) { - struct pci_dev *dev2; + struct pci_dev *dev2 = NULL, *findev; ide_pci_device_t *d2; unsigned char pin1 = 0, pin2 = 0; - d2 = d; if (PCI_FUNC(dev->devfn) & 1) return; - - for (dev2=pci_devices; dev2; dev2=dev2->next) { - if ((dev2->vendor == dev->vendor) && - (dev2->device == dev->device) && - (PCI_FUNC(dev2->devfn) & 1)) - break; - } pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin1); - if (dev2) { - pci_read_config_byte(dev2, PCI_INTERRUPT_PIN, &pin2); - hpt363_shared_irq = (pin1 != pin2) ? 1 : 0; - } - - if (hpt363_shared_irq) { - printk("%s: onboard version of chipset, pin1=%d pin2=%d\n", - d->name, pin1, pin2); + for (findev=pci_devices; findev; findev=findev->next) { + if ((findev->vendor == dev->vendor) && + (findev->device == dev->device) && + ((findev->devfn - dev->devfn) == 1) && + (PCI_FUNC(findev->devfn) & 1)) { + dev2 = findev; + pci_read_config_byte(dev2, PCI_INTERRUPT_PIN, &pin2); + hpt363_shared_irq = (pin1 != pin2) ? 1 : 0; + if (hpt363_shared_irq) { + d->bootable = ON_BOARD; + printk("%s: onboard version of chipset, pin1=%d pin2=%d\n", d->name, pin1, pin2); + } + break; + } } - - printk("%s: IDE controller on PCI bus %02x dev %02x\n", - d->name, dev->bus->number, dev->devfn); + printk("%s: IDE controller on PCI bus %02x dev %02x\n", d->name, dev->bus->number, dev->devfn); ide_setup_pci_device(dev, d); - - if (dev2 && !hpt363_shared_irq) { - printk("%s: IDE controller on PCI bus %02x dev %02x\n", - d2->name, dev2->bus->number, dev2->devfn); - ide_setup_pci_device(dev2, d2); - } + if (!dev2) + return; + d2 = d; + printk("%s: IDE controller on PCI bus %02x dev %02x\n", d2->name, dev2->bus->number, dev2->devfn); + ide_setup_pci_device(dev2, d2); } /* diff --git a/drivers/block/ide-pmac.c b/drivers/block/ide-pmac.c index 1ad5b3b96..c3e800249 100644 --- a/drivers/block/ide-pmac.c +++ b/drivers/block/ide-pmac.c @@ -363,7 +363,8 @@ int pmac_ide_dmaproc(ide_dma_action_t func, ide_drive_t *drive) drive->waiting_for_dma = 1; if (drive->media != ide_disk) return 0; - ide_set_handler(drive, &ide_dma_intr, WAIT_CMD); + drive->timeout = WAIT_CMD; + ide_set_handler(drive, &ide_dma_intr); OUT_BYTE(func==ide_dma_write? WIN_WRITEDMA: WIN_READDMA, IDE_COMMAND_REG); case ide_dma_begin: diff --git a/drivers/block/ide-probe.c b/drivers/block/ide-probe.c index 62008341e..d430c5b1d 100644 --- a/drivers/block/ide-probe.c +++ b/drivers/block/ide-probe.c @@ -42,7 +42,6 @@ #include <linux/delay.h> #include <linux/ide.h> - #include <asm/byteorder.h> #include <asm/irq.h> #include <asm/uaccess.h> @@ -85,6 +84,9 @@ static inline void do_identify (ide_drive_t *drive, byte cmd) ide_fixstring (id->fw_rev, sizeof(id->fw_rev), bswap); ide_fixstring (id->serial_no, sizeof(id->serial_no), bswap); + if (strstr(id->model, "E X A B Y T E N E S T")) + return; + id->model[sizeof(id->model)-1] = '\0'; /* we depend on this a lot! */ printk("%s: %s, ", drive->name, id->model); drive->present = 1; @@ -324,6 +326,35 @@ static int do_probe (ide_drive_t *drive, byte cmd) } /* + * + */ +static void enable_nest (ide_drive_t *drive) +{ + unsigned long timeout; + + printk("%s: enabling %s -- ", HWIF(drive)->name, drive->id->model); + SELECT_DRIVE(HWIF(drive), drive); + ide_delay_50ms(); + OUT_BYTE(EXABYTE_ENABLE_NEST, IDE_COMMAND_REG); + timeout = jiffies + WAIT_WORSTCASE; + do { + if (jiffies > timeout) { + printk("failed (timeout)\n"); + return; + } + ide_delay_50ms(); + } while (GET_STAT() & BUSY_STAT); + ide_delay_50ms(); + if (!OK_STAT(GET_STAT(), 0, BAD_STAT)) + printk("failed (status = 0x%02x)\n", GET_STAT()); + else + printk("success\n"); + if (do_probe(drive, WIN_IDENTIFY) >= 2) { /* if !(success||timed-out) */ + (void) do_probe(drive, WIN_PIDENTIFY); /* look for ATAPI device */ + } +} + +/* * probe_for_drive() tests for existence of a given drive using do_probe(). * * Returns: 0 no device was found @@ -336,6 +367,8 @@ static inline byte probe_for_drive (ide_drive_t *drive) if (do_probe(drive, WIN_IDENTIFY) >= 2) { /* if !(success||timed-out) */ (void) do_probe(drive, WIN_PIDENTIFY); /* look for ATAPI device */ } + if (drive->id && strstr(drive->id->model, "E X A B Y T E N E S T")) + enable_nest(drive); if (!drive->present) return 0; /* drive not found */ if (drive->id == NULL) { /* identification failed? */ diff --git a/drivers/block/ide-proc.c b/drivers/block/ide-proc.c index 8afe1568f..48d77c160 100644 --- a/drivers/block/ide-proc.c +++ b/drivers/block/ide-proc.c @@ -73,14 +73,21 @@ #define MIN(a,b) (((a) < (b)) ? (a) : (b)) #endif -#ifdef CONFIG_BLK_DEV_VIA82C586 -int (*via_display_info)(char *, char **, off_t, int, int) = NULL; -#endif /* CONFIG_BLK_DEV_VIA82C586 */ - #ifdef CONFIG_BLK_DEV_ALI15X3 +extern byte ali_proc; int (*ali_display_info)(char *, char **, off_t, int, int) = NULL; #endif /* CONFIG_BLK_DEV_ALI15X3 */ +#ifdef CONFIG_BLK_DEV_SIS5513 +extern byte sis_proc; +int (*sis_display_info)(char *, char **, off_t, int, int) = NULL; +#endif /* CONFIG_BLK_DEV_SIS5513 */ + +#ifdef CONFIG_BLK_DEV_VIA82CXXX +extern byte via_proc; +int (*via_display_info)(char *, char **, off_t, int, int) = NULL; +#endif /* CONFIG_BLK_DEV_VIA82CXXX */ + static int ide_getxdigit(char c) { int digit; @@ -243,7 +250,27 @@ static int proc_ide_write_config } #endif /* CONFIG_BLK_DEV_IDEPCI */ } else { /* not pci */ -#ifndef CONFIG_Q40 +#if !defined(__mc68000__) && !defined(CONFIG_APUS) + +/* + * Geert Uytterhoeven + * + * unless you can explain me what it really does. + * On m68k, we don't have outw() and outl() yet, + * and I need a good reason to implement it. + * + * BTW, IMHO the main remaining portability problem with the IDE driver + * is that it mixes IO (ioport) and MMIO (iomem) access on different platforms. + * + * I think all accesses should be done using + * + * ide_in[bwl](ide_device_instance, offset) + * ide_out[bwl](ide_device_instance, value, offset) + * + * so the architecture specific code can #define ide_{in,out}[bwl] to the + * appropriate function. + * + */ switch (digits) { case 2: outb(val, reg); break; @@ -252,7 +279,7 @@ static int proc_ide_write_config case 8: outl(val, reg); break; } -#endif /* CONFIG_Q40 */ +#endif /* !__mc68000__ && !CONFIG_APUS */ } } } @@ -750,18 +777,24 @@ void proc_ide_create(void) ent = create_proc_entry("drivers", 0, proc_ide_root); if (!ent) return; ent->read_proc = proc_ide_read_drivers; -#ifdef CONFIG_BLK_DEV_VIA82C586 - if (via_display_info) { - ent = create_proc_entry("via", 0, proc_ide_root); - ent->get_info = via_display_info; - } -#endif /* CONFIG_BLK_DEV_VIA82C586 */ #ifdef CONFIG_BLK_DEV_ALI15X3 - if (ali_display_info) { + if ((ali_display_info) && (ali_proc)) { ent = create_proc_entry("ali", 0, proc_ide_root); ent->get_info = ali_display_info; } #endif /* CONFIG_BLK_DEV_ALI15X3 */ +#ifdef CONFIG_BLK_DEV_SIS5513 + if ((sis_display_info) && (sis_proc)) { + ent = create_proc_entry("sis", 0, proc_ide_root); + ent->get_info = sis_display_info; + } +#endif /* CONFIG_BLK_DEV_SIS5513 */ +#ifdef CONFIG_BLK_DEV_VIA82CXXX + if ((via_display_info) && (via_proc)) { + ent = create_proc_entry("via", 0, proc_ide_root); + ent->get_info = via_display_info; + } +#endif /* CONFIG_BLK_DEV_VIA82CXXX */ } void proc_ide_destroy(void) @@ -770,14 +803,18 @@ void proc_ide_destroy(void) * Mmmm.. does this free up all resources, * or do we need to do a more proper cleanup here ?? */ -#ifdef CONFIG_BLK_DEV_VIA82C586 - if (via_display_info) - remove_proc_entry("ide/via",0); -#endif /* CONFIG_BLK_DEV_VIA82C586 */ #ifdef CONFIG_BLK_DEV_ALI15X3 - if (ali_display_info) + if ((ali_display_info) && (ali_proc)) remove_proc_entry("ide/ali",0); #endif /* CONFIG_BLK_DEV_ALI15X3 */ +#ifdef CONFIG_BLK_DEV_SIS5513 + if ((sis_display_info) && (sis_proc)) + remove_proc_entry("ide/sis", 0); +#endif /* CONFIG_BLK_DEV_SIS5513 */ +#ifdef CONFIG_BLK_DEV_VIA82CXXX + if ((via_display_info) && (via_proc)) + remove_proc_entry("ide/via",0); +#endif /* CONFIG_BLK_DEV_VIA82CXXX */ remove_proc_entry("ide/drivers", 0); destroy_proc_ide_interfaces(); remove_proc_entry("ide", 0); diff --git a/drivers/block/ide-tape.c b/drivers/block/ide-tape.c index 6e8313733..bbf222338 100644 --- a/drivers/block/ide-tape.c +++ b/drivers/block/ide-tape.c @@ -1833,7 +1833,7 @@ static void idetape_pc_intr (ide_drive_t *drive) if (temp > pc->buffer_size) { printk (KERN_ERR "ide-tape: The tape wants to send us more data than expected - discarding data\n"); idetape_discard_data (drive,bcount.all); - ide_set_handler (drive,&idetape_pc_intr,IDETAPE_WAIT_CMD); + ide_set_handler (drive,&idetape_pc_intr); return; } #if IDETAPE_DEBUG_LOG @@ -1855,7 +1855,7 @@ static void idetape_pc_intr (ide_drive_t *drive) pc->actually_transferred+=bcount.all; /* Update the current position */ pc->current_position+=bcount.all; - ide_set_handler (drive,&idetape_pc_intr,IDETAPE_WAIT_CMD); /* And set the interrupt handler again */ + ide_set_handler (drive,&idetape_pc_intr); /* And set the interrupt handler again */ } /* @@ -1928,7 +1928,7 @@ static void idetape_transfer_pc(ide_drive_t *drive) ide_do_reset (drive); return; } - ide_set_handler(drive, &idetape_pc_intr, IDETAPE_WAIT_CMD); /* Set the interrupt routine */ + ide_set_handler(drive, &idetape_pc_intr); /* Set the interrupt routine */ atapi_output_bytes (drive,pc->c,12); /* Send the actual packet */ } @@ -1995,7 +1995,7 @@ static void idetape_issue_packet_command (ide_drive_t *drive, idetape_pc_t *pc) } #endif /* CONFIG_BLK_DEV_IDEDMA */ if (test_bit(IDETAPE_DRQ_INTERRUPT, &tape->flags)) { - ide_set_handler(drive, &idetape_transfer_pc, IDETAPE_WAIT_CMD); + ide_set_handler(drive, &idetape_transfer_pc); OUT_BYTE(WIN_PACKETCMD, IDE_COMMAND_REG); } else { OUT_BYTE(WIN_PACKETCMD, IDE_COMMAND_REG); @@ -3579,6 +3579,7 @@ static void idetape_setup (ide_drive_t *drive, idetape_tape_t *tape, int minor) spin_lock_init(&tape->spinlock); drive->driver_data = tape; drive->ready_stat = 0; /* An ATAPI device ignores DRDY */ + drive->timeout = IDETAPE_WAIT_CMD; #ifdef CONFIG_BLK_DEV_IDEPCI /* * These two ide-pci host adapters appear to need this disabled. diff --git a/drivers/block/ide.c b/drivers/block/ide.c index d2cb4b34b..f0c9d58da 100644 --- a/drivers/block/ide.c +++ b/drivers/block/ide.c @@ -149,9 +149,9 @@ #include <linux/kmod.h> #endif /* CONFIG_KMOD */ -#ifdef CONFIG_BLK_DEV_VIA82C586 -extern byte fifoconfig; /* defined in via82c586.c used by ide_setup()*/ -#endif +#ifdef CONFIG_BLK_DEV_VIA82CXXX +extern byte fifoconfig; /* defined in via82cxxx.c used by ide_setup() */ +#endif /* CONFIG_BLK_DEV_VIA82CXXX */ static const byte ide_hwif_to_major[] = { IDE0_MAJOR, IDE1_MAJOR, IDE2_MAJOR, IDE3_MAJOR, @@ -498,13 +498,25 @@ void ide_end_request(byte uptodate, ide_hwgroup_t *hwgroup) } /* + * The below two are helpers used when modifying the drive timeout. + */ +static inline unsigned long set_timeout(ide_drive_t *drive, unsigned long timeout) +{ + unsigned long foo = drive->timeout; + drive->timeout = timeout; + return foo; +} + +#define restore_timeout(drive, old) (drive->timeout = old) + +/* * This should get invoked any time we exit the driver to * wait for an interrupt response from a drive. handler() points * at the appropriate code to handle the next interrupt, and a * timer is started to prevent us from waiting forever in case * something goes wrong (see the ide_timer_expiry() handler later on). */ -void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler, unsigned int timeout) +void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler) { unsigned long flags; ide_hwgroup_t *hwgroup = HWGROUP(drive); @@ -517,8 +529,11 @@ void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler, unsigned int t } #endif hwgroup->handler = handler; - hwgroup->timer.expires = jiffies + timeout; - add_timer(&(hwgroup->timer)); + /* 0 means don't timeout */ + if (drive->timeout && !timer_pending(&hwgroup->timer)) { + hwgroup->timer.expires = jiffies + drive->timeout; + add_timer(&(hwgroup->timer)); + } spin_unlock_irqrestore(&hwgroup->spinlock, flags); } @@ -565,6 +580,7 @@ static void do_reset1 (ide_drive_t *, int); /* needed below */ static void atapi_reset_pollfunc (ide_drive_t *drive) { ide_hwgroup_t *hwgroup = HWGROUP(drive); + unsigned long old_timeout; byte stat; SELECT_DRIVE(HWIF(drive),drive); @@ -574,7 +590,9 @@ static void atapi_reset_pollfunc (ide_drive_t *drive) printk("%s: ATAPI reset complete\n", drive->name); } else { if (0 < (signed long)(hwgroup->poll_timeout - jiffies)) { - ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20); + old_timeout = set_timeout(drive, HZ / 20); + ide_set_handler (drive, &atapi_reset_pollfunc); + restore_timeout(drive, old_timeout); return; /* continue polling */ } hwgroup->poll_timeout = 0; /* end of polling */ @@ -595,11 +613,14 @@ static void reset_pollfunc (ide_drive_t *drive) { ide_hwgroup_t *hwgroup = HWGROUP(drive); ide_hwif_t *hwif = HWIF(drive); + unsigned long old_timeout; byte tmp; if (!OK_STAT(tmp=GET_STAT(), 0, BUSY_STAT)) { if (0 < (signed long)(hwgroup->poll_timeout - jiffies)) { - ide_set_handler (drive, &reset_pollfunc, HZ/20); + old_timeout = set_timeout(drive, HZ / 20); + ide_set_handler (drive, &reset_pollfunc); + restore_timeout(drive, old_timeout); return; /* continue polling */ } printk("%s: reset timed-out, status=0x%02x\n", hwif->name, tmp); @@ -667,6 +688,7 @@ static void do_reset1 (ide_drive_t *drive, int do_not_try_atapi) unsigned long flags; ide_hwif_t *hwif = HWIF(drive); ide_hwgroup_t *hwgroup = HWGROUP(drive); + unsigned long old_timeout; __save_flags(flags); /* local CPU only */ __cli(); /* local CPU only */ @@ -678,7 +700,9 @@ static void do_reset1 (ide_drive_t *drive, int do_not_try_atapi) udelay (20); OUT_BYTE (WIN_SRST, IDE_COMMAND_REG); hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE; - ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20); + old_timeout = set_timeout(drive, HZ / 20); + ide_set_handler (drive, &atapi_reset_pollfunc); + restore_timeout(drive, old_timeout); __restore_flags (flags); /* local CPU only */ return; } @@ -708,7 +732,18 @@ static void do_reset1 (ide_drive_t *drive, int do_not_try_atapi) OUT_BYTE(drive->ctl|2,IDE_CONTROL_REG); /* clear SRST, leave nIEN */ udelay(10); /* more than enough time */ hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE; - ide_set_handler (drive, &reset_pollfunc, HZ/20); + old_timeout = set_timeout(drive, HZ / 20); + ide_set_handler (drive, &reset_pollfunc); + restore_timeout(drive, old_timeout); + + /* + * Some weird controller like resetting themselves to a strange + * state when the disks are reset this way. At least, the Winbond + * 553 documentation says that + */ + if (hwif->resetproc != NULL) + hwif->resetproc(drive); + #endif /* OK_TO_RESET_CONTROLLER */ __restore_flags (flags); /* local CPU only */ @@ -899,7 +934,7 @@ void ide_error (ide_drive_t *drive, const char *msg, byte stat) */ void ide_cmd(ide_drive_t *drive, byte cmd, byte nsect, ide_handler_t *handler) { - ide_set_handler (drive, handler, WAIT_CMD); + ide_set_handler (drive, handler); if (IDE_CONTROL_REG) OUT_BYTE(drive->ctl,IDE_CONTROL_REG); /* clear nIEN */ OUT_BYTE(nsect,IDE_NSECTOR_REG); @@ -1018,6 +1053,10 @@ static void execute_drive_cmd (ide_drive_t *drive, struct request *rq) if (args[0] == WIN_SMART) { OUT_BYTE(0x4f, IDE_LCYL_REG); OUT_BYTE(0xc2, IDE_HCYL_REG); + OUT_BYTE(args[2],IDE_FEATURE_REG); + OUT_BYTE(args[1],IDE_SECTOR_REG); + ide_cmd(drive, args[0], args[3], &drive_cmd_intr); + return; } OUT_BYTE(args[2],IDE_FEATURE_REG); ide_cmd(drive, args[0], args[1], &drive_cmd_intr); @@ -1218,13 +1257,37 @@ static void ide_do_request (ide_hwgroup_t *hwgroup, unsigned long *hwgroup_flags bdev->current_request = hwgroup->rq = drive->queue; spin_unlock_irqrestore(&io_request_lock, io_flags); +#if 0 if (hwif->irq != masked_irq) - disable_irq(hwif->irq); + disable_irq_nosync(hwif->irq); spin_unlock_irqrestore(&hwgroup->spinlock, *hwgroup_flags); start_request(drive); spin_lock_irqsave(&hwgroup->spinlock, *hwgroup_flags); if (hwif->irq != masked_irq) enable_irq(hwif->irq); +#else + + if (masked_irq && hwif->irq != masked_irq) { + printk("%s: (disable_irq) %smasked_irq %d\n", + drive->name, + masked_irq ? "" : "un_", hwif->irq); + +#if 0 + disable_irq(hwif->irq); +#else + disable_irq_nosync(hwif->irq); +#endif + } + spin_unlock_irqrestore(&hwgroup->spinlock, *hwgroup_flags); + start_request(drive); + spin_lock_irqsave(&hwgroup->spinlock, *hwgroup_flags); + if (masked_irq && hwif->irq != masked_irq) { + printk("%s: (enable_irq) %smasked_irq %d\n", + drive->name, + masked_irq ? "" : "un_", hwif->irq); + enable_irq(hwif->irq); + } +#endif } } @@ -1373,8 +1436,8 @@ void ide_timer_expiry (unsigned long data) } hwgroup->busy = 1; /* should already be "1" */ hwgroup->handler = NULL; - del_timer(&hwgroup->timer); /* Is this needed?? */ - if (hwgroup->poll_timeout != 0) { /* polling in progress? */ + /* polling in progress or just don't timeout */ + if (hwgroup->poll_timeout != 0) { spin_unlock_irqrestore(&hwgroup->spinlock, flags); handler(drive); } else if (drive_is_ready(drive)) { @@ -1612,8 +1675,10 @@ int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t actio } spin_unlock_irqrestore(&io_request_lock, flags); do_hwgroup_request(hwgroup); - if (action == ide_wait && rq->rq_status != RQ_INACTIVE) + if (action == ide_wait) { down(&sem); /* wait for it to be serviced */ + rq->sem = NULL; + } return rq->errors ? -EIO : 0; /* return -EIO if errors */ } @@ -2270,8 +2335,13 @@ void ide_delay_50ms (void) int ide_config_drive_speed (ide_drive_t *drive, byte speed) { + struct hd_driveid *id = drive->id; + unsigned long flags; int err; + __save_flags(flags); /* local CPU only */ + __cli(); /* local CPU only */ + /* * Don't use ide_wait_cmd here - it will * attempt to set_geometry and recalibrate, @@ -2287,6 +2357,109 @@ int ide_config_drive_speed (ide_drive_t *drive, byte speed) err = ide_wait_stat(drive, DRIVE_READY, BUSY_STAT|DRQ_STAT|ERR_STAT, WAIT_CMD); +#if 0 + if (IDE_CONTROL_REG) + OUT_BYTE(drive->ctl, IDE_CONTROL_REG); +#endif + + __restore_flags(flags); /* local CPU only */ + + switch(speed) { + case XFER_UDMA_4: + if (!((id->dma_ultra >> 8) & 16)) { + drive->id->dma_ultra &= ~0xFF00; + drive->id->dma_ultra |= 0x1010; + } + drive->id->dma_mword &= ~0x0F00; + drive->id->dma_1word &= ~0x0F00; + break; + case XFER_UDMA_3: + if (!((id->dma_ultra >> 8) & 8)) { + drive->id->dma_ultra &= ~0xFF00; + drive->id->dma_ultra |= 0x0808; + } + drive->id->dma_mword &= ~0x0F00; + drive->id->dma_1word &= ~0x0F00; + break; + case XFER_UDMA_2: + if (!((id->dma_ultra >> 8) & 4)) { + drive->id->dma_ultra &= ~0xFF00; + drive->id->dma_ultra |= 0x0404; + } + drive->id->dma_mword &= ~0x0F00; + drive->id->dma_1word &= ~0x0F00; + break; + case XFER_UDMA_1: + if (!((id->dma_ultra >> 8) & 2)) { + drive->id->dma_ultra &= ~0xFF00; + drive->id->dma_ultra |= 0x0202; + } + drive->id->dma_mword &= ~0x0F00; + drive->id->dma_1word &= ~0x0F00; + break; + case XFER_UDMA_0: + if (!((id->dma_ultra >> 8) & 1)) { + drive->id->dma_ultra &= ~0xFF00; + drive->id->dma_ultra |= 0x0101; + } + drive->id->dma_mword &= ~0x0F00; + drive->id->dma_1word &= ~0x0F00; + break; + case XFER_MW_DMA_2: + drive->id->dma_ultra &= ~0xFF00; + if (!((id->dma_mword >> 8) & 4)) { + drive->id->dma_mword &= ~0x0F00; + drive->id->dma_mword |= 0x0404; + } + drive->id->dma_1word &= ~0x0F00; + break; + case XFER_MW_DMA_1: + drive->id->dma_ultra &= ~0xFF00; + if (!((id->dma_mword >> 8) & 2)) { + drive->id->dma_mword &= ~0x0F00; + drive->id->dma_mword |= 0x0202; + } + drive->id->dma_1word &= ~0x0F00; + break; + case XFER_MW_DMA_0: + drive->id->dma_ultra &= ~0xFF00; + if (!((id->dma_mword >> 8) & 1)) { + drive->id->dma_mword &= ~0x0F00; + drive->id->dma_mword |= 0x0101; + } + drive->id->dma_1word &= ~0x0F00; + break; + case XFER_SW_DMA_2: + drive->id->dma_ultra &= ~0xFF00; + drive->id->dma_mword &= ~0x0F00; + if (!((id->dma_1word >> 8) & 4)) { + drive->id->dma_1word &= ~0x0F00; + drive->id->dma_1word |= 0x0404; + } + break; + case XFER_SW_DMA_1: + drive->id->dma_ultra &= ~0xFF00; + drive->id->dma_mword &= ~0x0F00; + if (!((id->dma_1word >> 8) & 2)) { + drive->id->dma_1word &= ~0x0F00; + drive->id->dma_1word |= 0x0202; + } + break; + case XFER_SW_DMA_0: + drive->id->dma_ultra &= ~0xFF00; + drive->id->dma_mword &= ~0x0F00; + if (!((id->dma_1word >> 8) & 1)) { + drive->id->dma_1word &= ~0x0F00; + drive->id->dma_1word |= 0x0101; + } + break; + default: + drive->id->dma_ultra &= ~0xFF00; + drive->id->dma_mword &= ~0x0F00; + drive->id->dma_1word &= ~0x0F00; + break; + } + return(err); } @@ -2803,7 +2976,7 @@ void __init ide_setup (char *s) } } -#if defined(CONFIG_BLK_DEV_VIA82C586) +#if defined(CONFIG_BLK_DEV_VIA82CXXX) /* * Look for drive option "splitfifo=..." */ @@ -2853,7 +3026,7 @@ void __init ide_setup (char *s) fifoconfig = tmp; goto done; } -#endif /* defined(CONFIG_BLK_DEV_VIA82C586) */ +#endif /* defined(CONFIG_BLK_DEV_VIA82CXXX) */ if (s[0] != 'i' || s[1] != 'd' || s[2] != 'e') goto bad_option; @@ -3393,6 +3566,7 @@ EXPORT_SYMBOL(do_ide9_request); EXPORT_SYMBOL(ide_scan_devices); EXPORT_SYMBOL(ide_register_subdriver); EXPORT_SYMBOL(ide_unregister_subdriver); +EXPORT_SYMBOL(ide_replace_subdriver); EXPORT_SYMBOL(ide_input_data); EXPORT_SYMBOL(ide_output_data); EXPORT_SYMBOL(atapi_input_bytes); @@ -3464,7 +3638,11 @@ static void __init parse_options (char *line) while ((line = next) != NULL) { if ((next = strchr(line,' ')) != NULL) *next++ = 0; - if (!strncmp(line,"ide",3) || (!strncmp(line,"hd",2) && line[2] != '=')) + if (!strncmp(line,"ide",3) || +#ifdef CONFIG_BLK_DEV_VIA82CXXX + !strncmp(line,"splitfifo",9) || +#endif /* CONFIG_BLK_DEV_VIA82CXXX */ + (!strncmp(line,"hd",2) && line[2] != '=')) ide_setup(line); } } diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c index 0114dd8ec..9366a8856 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c @@ -416,10 +416,6 @@ void make_request(int major,int rw, struct buffer_head * bh) count = bh->b_size >> 9; sector = bh->b_rsector; - /* We'd better have a real physical mapping! */ - if (!buffer_mapped(bh)) - BUG(); - /* It had better not be a new buffer by the time we see it */ if (buffer_new(bh)) BUG(); @@ -480,6 +476,13 @@ void make_request(int major,int rw, struct buffer_head * bh) goto end_io; } + /* We'd better have a real physical mapping! + Check this bit only if the buffer was dirty and just locked + down by us so at this point flushpage will block and + won't clear the mapped bit under us. */ + if (!buffer_mapped(bh)) + BUG(); + /* look for a free request. */ /* Loop uses two requests, 1 for loop and 1 for the real device. * Cut max_req in half to avoid running out and deadlocking. */ @@ -694,7 +697,7 @@ void ll_rw_block(int rw, int nr, struct buffer_head * bh[]) sorry: for (i = 0; i < nr; i++) { - clear_bit(BH_Dirty, &bh[i]->b_state); + mark_buffer_clean(bh[i]); /* remeber to refile it */ clear_bit(BH_Uptodate, &bh[i]->b_state); bh[i]->b_end_io(bh[i], 0); } diff --git a/drivers/block/paride/pcd.c b/drivers/block/paride/pcd.c index 027517320..96e0e421e 100644 --- a/drivers/block/paride/pcd.c +++ b/drivers/block/paride/pcd.c @@ -214,8 +214,11 @@ static int pcd_drive_reset(struct cdrom_device_info *cdi); static int pcd_get_mcn (struct cdrom_device_info *cdi, struct cdrom_mcn *mcn); static int pcd_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void *arg); +static int pcd_packet(struct cdrom_device_info *cdi, + struct cdrom_generic_command *cgc); static int pcd_detect(void); +static void pcd_probe_capabilities(void); static void do_pcd_read_drq(void); static void do_pcd_request(void); static void do_pcd_read(void); @@ -276,14 +279,18 @@ static struct cdrom_device_ops pcd_dops = { pcd_drive_reset, pcd_audio_ioctl, 0, /* dev_ioctl */ - CDC_CLOSE_TRAY | - CDC_OPEN_TRAY | - CDC_LOCK | - CDC_MCN | - CDC_MEDIA_CHANGED | - CDC_RESET | - CDC_PLAY_AUDIO, - 0 + CDC_CLOSE_TRAY | + CDC_OPEN_TRAY | + CDC_LOCK | + CDC_MCN | + CDC_MEDIA_CHANGED | + CDC_RESET | + CDC_PLAY_AUDIO | + CDC_GENERIC_PACKET | + CDC_CD_R | + CDC_CD_RW, + 0, + pcd_packet, }; static void pcd_init_units( void ) @@ -325,6 +332,9 @@ int pcd_init (void) /* preliminary initialisation */ if (pcd_detect()) return -1; + /* get the atapi capabilities page */ + pcd_probe_capabilities(); + if (register_blkdev(MAJOR_NR,name,&cdrom_fops)) { printk("pcd: unable to get major number %d\n",MAJOR_NR); return -1; @@ -525,6 +535,16 @@ static int pcd_atapi( int unit, char * cmd, int dlen, char * buf, char * fun ) return r; } +static int pcd_packet(struct cdrom_device_info *cdi, + struct cdrom_generic_command *cgc) +{ + char *un_cmd; + int unit = DEVICE_NR(cdi->dev); + + un_cmd = cgc->cmd; + return pcd_atapi(unit,un_cmd,cgc->buflen,cgc->buffer, "generic packet"); +} + #define DBMSG(msg) ((verbose>1)?(msg):NULL) static int pcd_media_changed(struct cdrom_device_info *cdi, int slot_nr) @@ -667,6 +687,32 @@ static int pcd_probe( int unit, int ms, char * id ) return -1; } +static void pcd_probe_capabilities( void ) + +{ int unit, r; + char buffer[32]; + char cmd[12]={0x5a,1<<3,0x2a,0,0,0,0,18,0,0,0,0}; + + for (unit=0;unit<PCD_UNITS;unit++) { + if (!PCD.present) continue; + r = pcd_atapi(unit,cmd,18, buffer,"mode sense capabilities"); + if (r) continue; + /* we should now have the cap page */ + if ((buffer[11] & 1) == 0) + PCD.info.mask |= CDC_CD_R; + if ((buffer[11] & 2) == 0) + PCD.info.mask |= CDC_CD_RW; + if ((buffer[12] & 1) == 0) + PCD.info.mask |= CDC_PLAY_AUDIO; + if ((buffer[14] & 1) == 0) + PCD.info.mask |= CDC_LOCK; + if ((buffer[14] & 8) == 0) + PCD.info.mask |= CDC_OPEN_TRAY; + if ((buffer[14] >> 6) == 0) + PCD.info.mask |= CDC_CLOSE_TRAY; + } +} + static int pcd_detect( void ) { char id[18]; @@ -836,63 +882,6 @@ static int pcd_audio_ioctl(struct cdrom_device_info *cdi, switch (cmd) { - case CDROMPAUSE: - - { char cmd[12]={GPCMD_PAUSE_RESUME,0,0,0,0,0,0,0,0,0,0,0}; - - return (pcd_atapi(unit,cmd,0,NULL,"pause")) * EIO; - } - - case CDROMRESUME: - - { char cmd[12]={GPCMD_PAUSE_RESUME,0,0,0,0,0,0,0,1,0,0,0}; - - return (pcd_atapi(unit,cmd,0,NULL,"resume")) * EIO; - } - - case CDROMPLAYMSF: - - { char cmd[12]={GPCMD_PLAY_AUDIO_MSF,0,0,0,0,0,0,0,0,0,0,0}; - struct cdrom_msf* msf = (struct cdrom_msf*)arg; - - cmd[3] = msf->cdmsf_min0; - cmd[4] = msf->cdmsf_sec0; - cmd[5] = msf->cdmsf_frame0; - cmd[6] = msf->cdmsf_min1; - cmd[7] = msf->cdmsf_sec1; - cmd[8] = msf->cdmsf_frame1; - - return (pcd_atapi(unit,cmd,0,NULL,"play msf")) * EIO; - } - - case CDROMPLAYBLK: - - { char cmd[12]={GPCMD_PLAY_AUDIO_10,0,0,0,0,0,0,0,0,0,0,0}; - struct cdrom_blk* blk = (struct cdrom_blk*)arg; - - cmd[2] = blk->from >> 24; - cmd[3] = blk->from >> 16; - cmd[4] = blk->from >> 8; - cmd[5] = blk->from; - cmd[7] = blk->len >> 8; - cmd[8] = blk->len; - - return (pcd_atapi(unit,cmd,0,NULL,"play block")) * EIO; - } - - case CDROMPLAYTRKIND: - - { char cmd[12]={GPCMD_PLAYAUDIO_TI,0,0,0,0,0,0,0,0,0,0,0}; - struct cdrom_ti* ti = (struct cdrom_ti*)arg; - - cmd[4] = ti->cdti_trk0; - cmd[5] = ti->cdti_ind0; - cmd[7] = ti->cdti_trk1; - cmd[8] = ti->cdti_ind1; - - return (pcd_atapi(unit,cmd,0,NULL,"play track")) * EIO; - } - case CDROMREADTOCHDR: { char cmd[12]={GPCMD_READ_TOC_PMA_ATIP,0,0,0,0,0,0,0,12,0,0,0}; @@ -936,97 +925,6 @@ static int pcd_audio_ioctl(struct cdrom_device_info *cdi, return r * EIO; } - case CDROMSTOP: - - { char cmd[12]={GPCMD_START_STOP_UNIT,1,0,0,0,0,0,0,0,0,0,0}; - - return (pcd_atapi(unit,cmd,0,NULL,"stop")) * EIO; - } - - case CDROMSTART: - - { char cmd[12]={GPCMD_START_STOP_UNIT,1,0,0,1,0,0,0,0,0,0,0}; - - return (pcd_atapi(unit,cmd,0,NULL,"start")) * EIO; - } - - case CDROMVOLCTRL: - - { char cmd[12]={GPCMD_MODE_SENSE_10,0,0,0,0,0,0,0,0,0,0,0}; - char buffer[32]; - char mask[32]; - struct cdrom_volctrl* volctrl = (struct cdrom_volctrl*)arg; - - cmd[2] = 0xe; - cmd[4] = 28; - - if (pcd_atapi(unit,cmd,28,buffer,"mode sense vol")) - return -EIO; - - cmd[2] = 0x4e; - - if (pcd_atapi(unit,cmd,28,buffer,"mode sense vol mask")) - return -EIO; - - buffer[0] = 0; - - buffer[21] = volctrl->channel0 & mask[21]; - buffer[23] = volctrl->channel1 & mask[23]; - buffer[25] = volctrl->channel2 & mask[25]; - buffer[27] = volctrl->channel3 & mask[27]; - - cmd[0] = 0x55; - cmd[1] = 0x10; - - return pcd_atapi(unit,cmd,28,buffer,"mode select vol") * EIO; - } - - case CDROMVOLREAD: - - { char cmd[12]={GPCMD_MODE_SENSE_10,0,0,0,0,0,0,0,0,0,0,0}; - char buffer[32]; - struct cdrom_volctrl* volctrl = (struct cdrom_volctrl*)arg; - int r; - - cmd[2] = 0xe; - cmd[4] = 28; - - r = pcd_atapi(unit,cmd,28,buffer,"mode sense vol read"); - - volctrl->channel0 = buffer[21]; - volctrl->channel1 = buffer[23]; - volctrl->channel2 = buffer[25]; - volctrl->channel3 = buffer[27]; - - return r * EIO; - } - - - case CDROMSUBCHNL: - - { char cmd[12]={GPCMD_READ_SUBCHANNEL,2,0x40,1,0,0,0,0,16,0,0,0}; - struct cdrom_subchnl* subchnl = (struct cdrom_subchnl*)arg; - char buffer[32]; - - if (pcd_atapi(unit,cmd,16,buffer,"read subchannel")) - return -EIO; - - subchnl->cdsc_audiostatus = buffer[1]; - subchnl->cdsc_format = CDROM_MSF; - subchnl->cdsc_ctrl = buffer[5] & 0xf; - subchnl->cdsc_trk = buffer[6]; - subchnl->cdsc_ind = buffer[7]; - - subchnl->cdsc_reladdr.msf.minute = buffer[13]; - subchnl->cdsc_reladdr.msf.second = buffer[14]; - subchnl->cdsc_reladdr.msf.frame = buffer[15]; - subchnl->cdsc_absaddr.msf.minute = buffer[9]; - subchnl->cdsc_absaddr.msf.second = buffer[10]; - subchnl->cdsc_absaddr.msf.frame = buffer[11]; - - return 0; - } - default: return -ENOSYS; diff --git a/drivers/block/pdc202xx.c b/drivers/block/pdc202xx.c index aeffcc9ba..2155aa8af 100644 --- a/drivers/block/pdc202xx.c +++ b/drivers/block/pdc202xx.c @@ -1,8 +1,8 @@ /* - * linux/drivers/block/pdc202xx.c Version 0.26 May 12, 1999 + * linux/drivers/block/pdc202xx.c Version 0.27 Sept. 3, 1999 * - * Copyright (C) 1998-99 Andre Hedrick - * (hedrick@astro.dyer.vanderbilt.edu) + * Copyright (C) 1998-99 Andre Hedrick (andre@suse.com) + * May be copied or modified under the terms of the GNU General Public License * * Promise Ultra33 cards with BIOS v1.20 through 1.28 will need this * compiled into the kernel if you have more than one card installed. @@ -72,6 +72,12 @@ * = ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01)); */ +/* + * Portions Copyright (C) 1999 Promise Technology, Inc. + * Author: Frank Tiernan (frankt@promise.com) + * Released under terms of General Public License + */ + #include <linux/types.h> #include <linux/kernel.h> #include <linux/delay.h> @@ -88,6 +94,8 @@ #include <asm/io.h> #include <asm/irq.h> +#include "ide_modes.h" + #define PDC202XX_DEBUG_DRIVE_INFO 0 #define PDC202XX_DECODE_REGISTER_INFO 0 @@ -208,27 +216,78 @@ static int config_chipset_for_dma (ide_drive_t *drive, byte ultra) struct hd_driveid *id = drive->id; ide_hwif_t *hwif = HWIF(drive); struct pci_dev *dev = hwif->pci_dev; + unsigned long high_16 = dev->resource[4].start & PCI_BASE_ADDRESS_IO_MASK; int err; unsigned int drive_conf; byte drive_pci; - byte test1, test2, speed; - byte AP, BP, CP, DP, EP; + byte test1, test2, speed = -1; + byte AP, BP, CP, DP, TB, TC; + unsigned short EP; + byte CLKSPD = IN_BYTE(high_16 + 0x11); int drive_number = ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01)); byte udma_66 = ((id->word93 & 0x2000) && (hwif->udma_four)) ? 1 : 0; - byte udma_33 = ultra ? (inb((dev->resource[4].start & PCI_BASE_ADDRESS_IO_MASK) + 0x001f) & 1) : 0; - - pci_read_config_byte(dev, 0x50, &EP); + byte udma_33 = ultra ? (inb(high_16 + 0x001f) & 1) : 0; + + /* + * Set the control register to use the 66Mhz system + * clock for UDMA 3/4 mode operation. If one drive on + * a channel is U66 capable but the other isn't we + * fall back to U33 mode. The BIOS INT 13 hooks turn + * the clock on then off for each read/write issued. I don't + * do that here because it would require modifying the + * kernel, seperating the fop routines from the kernel or + * somehow hooking the fops calls. It may also be possible to + * leave the 66Mhz clock on and readjust the timing + * parameters. + */ + + byte mask = hwif->channel ? 0x08 : 0x02; + unsigned short c_mask = hwif->channel ? (1<<11) : (1<<10); + byte ultra_66 = ((id->dma_ultra & 0x0010) || (id->dma_ultra & 0x0008)) ? 1 : 0; + + pci_read_config_word(dev, 0x50, &EP); + + if ((ultra_66) && (EP & c_mask)) { +#ifdef DEBUG + printk("ULTRA66: %s channel of Ultra 66 requires an 80-pin cable for Ultra66 operation.\n", hwif->channel ? "Secondary", "Primary"); + printk(" Switching to Ultra33 mode.\n"); +#endif /* DEBUG */ + /* Primary : zero out second bit */ + /* Secondary : zero out fourth bit */ + OUT_BYTE(CLKSPD & ~mask, (high_16 + 0x11)); + } else { + if (ultra_66) { + /* + * check to make sure drive on same channel + * is u66 capable + */ + if (hwif->drives[!(drive_number%2)].id) { + if ((hwif->drives[!(drive_number%2)].id->dma_ultra & 0x0010) || + (hwif->drives[!(drive_number%2)].id->dma_ultra & 0x0008)) { + OUT_BYTE(CLKSPD | mask, (high_16 + 0x11)); + } else { + OUT_BYTE(CLKSPD & ~mask, (high_16 + 0x11)); + } + } else { /* udma4 drive by itself */ + OUT_BYTE(CLKSPD | mask, (high_16 + 0x11)); + } + } + } switch(drive_number) { case 0: drive_pci = 0x60; pci_read_config_dword(dev, drive_pci, &drive_conf); + if ((drive_conf != 0x004ff304) && (drive_conf != 0x004ff3c4)) + goto chipset_is_set; pci_read_config_byte(dev, (drive_pci), &test1); if (!(test1 & SYNC_ERRDY_EN)) pci_write_config_byte(dev, (drive_pci), test1|SYNC_ERRDY_EN); break; case 1: drive_pci = 0x64; pci_read_config_dword(dev, drive_pci, &drive_conf); + if ((drive_conf != 0x004ff304) && (drive_conf != 0x004ff3c4)) + goto chipset_is_set; pci_read_config_byte(dev, 0x60, &test1); pci_read_config_byte(dev, (drive_pci), &test2); if ((test1 & SYNC_ERRDY_EN) && !(test2 & SYNC_ERRDY_EN)) @@ -236,12 +295,16 @@ static int config_chipset_for_dma (ide_drive_t *drive, byte ultra) break; case 2: drive_pci = 0x68; pci_read_config_dword(dev, drive_pci, &drive_conf); + if ((drive_conf != 0x004ff304) && (drive_conf != 0x004ff3c4)) + goto chipset_is_set; pci_read_config_byte(dev, (drive_pci), &test1); if (!(test1 & SYNC_ERRDY_EN)) pci_write_config_byte(dev, (drive_pci), test1|SYNC_ERRDY_EN); break; case 3: drive_pci = 0x6c; pci_read_config_dword(dev, drive_pci, &drive_conf); + if ((drive_conf != 0x004ff304) && (drive_conf != 0x004ff3c4)) + goto chipset_is_set; pci_read_config_byte(dev, 0x68, &test1); pci_read_config_byte(dev, (drive_pci), &test2); if ((test1 & SYNC_ERRDY_EN) && !(test2 & SYNC_ERRDY_EN)) @@ -251,6 +314,8 @@ static int config_chipset_for_dma (ide_drive_t *drive, byte ultra) return ide_dma_off; } +chipset_is_set: + if (drive->media != ide_disk) return ide_dma_off_quietly; @@ -285,127 +350,46 @@ static int config_chipset_for_dma (ide_drive_t *drive, byte ultra) pci_read_config_byte(dev, (drive_pci)|0x02, &CP); if ((id->dma_ultra & 0x0010) && (udma_66) && (udma_33)) { - if (!((id->dma_ultra >> 8) & 16)) { - drive->id->dma_ultra &= ~0xFF00; - drive->id->dma_ultra |= 0x1010; - drive->id->dma_mword &= ~0x0F00; - drive->id->dma_1word &= ~0x0F00; - } /* speed 8 == UDMA mode 4 == speed 6 plus cable */ - pci_write_config_byte(dev, (drive_pci)|0x01, BP|0x20); - pci_write_config_byte(dev, (drive_pci)|0x02, CP|0x01); - speed = XFER_UDMA_4; + speed = XFER_UDMA_4; TB = 0x20; TC = 0x01; } else if ((id->dma_ultra & 0x0008) && (udma_66) && (udma_33)) { - if (!((id->dma_ultra >> 8) & 8)) { - drive->id->dma_ultra &= ~0xFF00; - drive->id->dma_ultra |= 0x0808; - drive->id->dma_mword &= ~0x0F00; - drive->id->dma_1word &= ~0x0F00; - } /* speed 7 == UDMA mode 3 == speed 5 plus cable */ - pci_write_config_byte(dev, (drive_pci)|0x01, BP|0x40); - pci_write_config_byte(dev, (drive_pci)|0x02, CP|0x02); - speed = XFER_UDMA_3; + speed = XFER_UDMA_3; TB = 0x40; TC = 0x02; } else if ((id->dma_ultra & 0x0004) && (udma_33)) { - if (!((id->dma_ultra >> 8) & 4)) { - drive->id->dma_ultra &= ~0xFF00; - drive->id->dma_ultra |= 0x0404; - drive->id->dma_mword &= ~0x0F00; - drive->id->dma_1word &= ~0x0F00; - } /* speed 6 == UDMA mode 2 */ - pci_write_config_byte(dev, (drive_pci)|0x01, BP|0x20); - pci_write_config_byte(dev, (drive_pci)|0x02, CP|0x01); - speed = XFER_UDMA_2; + speed = XFER_UDMA_2; TB = 0x20; TC = 0x01; } else if ((id->dma_ultra & 0x0002) && (udma_33)) { - if (!((id->dma_ultra >> 8) & 2)) { - drive->id->dma_ultra &= ~0xFF00; - drive->id->dma_ultra |= 0x0202; - drive->id->dma_mword &= ~0x0F00; - drive->id->dma_1word &= ~0x0F00; - } /* speed 5 == UDMA mode 1 */ - pci_write_config_byte(dev, (drive_pci)|0x01, BP|0x40); - pci_write_config_byte(dev, (drive_pci)|0x02, CP|0x02); - speed = XFER_UDMA_1; + speed = XFER_UDMA_1; TB = 0x40; TC = 0x02; } else if ((id->dma_ultra & 0x0001) && (udma_33)) { - if (!((id->dma_ultra >> 8) & 1)) { - drive->id->dma_ultra &= ~0xFF00; - drive->id->dma_ultra |= 0x0101; - drive->id->dma_mword &= ~0x0F00; - drive->id->dma_1word &= ~0x0F00; - } /* speed 4 == UDMA mode 0 */ - pci_write_config_byte(dev, (drive_pci)|0x01, BP|0x60); - pci_write_config_byte(dev, (drive_pci)|0x02, CP|0x03); - speed = XFER_UDMA_0; + speed = XFER_UDMA_0; TB = 0x60; TC = 0x03; } else if (id->dma_mword & 0x0004) { - if (!((id->dma_mword >> 8) & 4)) { - drive->id->dma_mword &= ~0x0F00; - drive->id->dma_mword |= 0x0404; - drive->id->dma_1word &= ~0x0F00; - } /* speed 4 == DMA mode 2 multi-word */ - pci_write_config_byte(dev, (drive_pci)|0x01, BP|0x60); - pci_write_config_byte(dev, (drive_pci)|0x02, CP|0x03); - speed = XFER_MW_DMA_2; + speed = XFER_MW_DMA_2; TB = 0x60; TC = 0x03; } else if (id->dma_mword & 0x0002) { - if (!((id->dma_mword >> 8) & 2)) { - drive->id->dma_mword &= ~0x0F00; - drive->id->dma_mword |= 0x0202; - drive->id->dma_1word &= ~0x0F00; - } /* speed 3 == DMA mode 1 multi-word */ - pci_write_config_byte(dev, (drive_pci)|0x01, BP|0x60); - pci_write_config_byte(dev, (drive_pci)|0x02, CP|0x04); - speed = XFER_MW_DMA_1; + speed = XFER_MW_DMA_1; TB = 0x60; TC = 0x04; } else if (id->dma_mword & 0x0001) { - if (!((id->dma_mword >> 8) & 1)) { - drive->id->dma_mword &= ~0x0F00; - drive->id->dma_mword |= 0x0101; - drive->id->dma_1word &= ~0x0F00; - } /* speed 2 == DMA mode 0 multi-word */ - pci_write_config_byte(dev, (drive_pci)|0x01, BP|0x60); - pci_write_config_byte(dev, (drive_pci)|0x02, CP|0x05); - speed = XFER_MW_DMA_0; + speed = XFER_MW_DMA_0; TB = 0x60; TC = 0x05; } else if (id->dma_1word & 0x0004) { - if (!((id->dma_1word >> 8) & 4)) { - drive->id->dma_mword &= ~0x0F00; - drive->id->dma_1word &= ~0x0F00; - drive->id->dma_1word |= 0x0404; - } /* speed 2 == DMA mode 2 single-word */ - pci_write_config_byte(dev, (drive_pci)|0x01, BP|0x60); - pci_write_config_byte(dev, (drive_pci)|0x02, CP|0x05); - speed = XFER_SW_DMA_2; + speed = XFER_SW_DMA_2; TB = 0x60; TC = 0x05; } else if (id->dma_1word & 0x0002) { - if (!((id->dma_1word >> 8) & 2)) { - drive->id->dma_mword &= ~0x0F00; - drive->id->dma_1word &= ~0x0F00; - drive->id->dma_1word |= 0x0202; - } /* speed 1 == DMA mode 1 single-word */ - pci_write_config_byte(dev, (drive_pci)|0x01, BP|0x80); - pci_write_config_byte(dev, (drive_pci)|0x02, CP|0x06); - speed = XFER_SW_DMA_1; + speed = XFER_SW_DMA_1; TB = 0x80; TC = 0x06; } else if (id->dma_1word & 0x0001) { - if (!((id->dma_1word >> 8) & 1)) { - drive->id->dma_mword &= ~0x0F00; - drive->id->dma_1word &= ~0x0F00; - drive->id->dma_1word |= 0x0101; - } /* speed 0 == DMA mode 0 single-word */ - pci_write_config_byte(dev, (drive_pci)|0x01, BP|0xC0); - pci_write_config_byte(dev, (drive_pci)|0x02, CP|0x0B); - speed = XFER_SW_DMA_0; + speed = XFER_SW_DMA_0; TB = 0xC0; TC = 0x0B; } else { /* restore original pci-config space */ pci_write_config_dword(dev, drive_pci, drive_conf); return ide_dma_off_quietly; } - err = ide_config_drive_speed(drive, speed); + pci_write_config_byte(dev, (drive_pci)|0x01, BP|TB); + pci_write_config_byte(dev, (drive_pci)|0x02, CP|TC); #if PDC202XX_DECODE_REGISTER_INFO pci_read_config_byte(dev, (drive_pci), &AP); @@ -418,6 +402,8 @@ static int config_chipset_for_dma (ide_drive_t *drive, byte ultra) decode_registers(REG_D, DP); #endif /* PDC202XX_DECODE_REGISTER_INFO */ + err = ide_config_drive_speed(drive, speed); + #if PDC202XX_DEBUG_DRIVE_INFO printk("%s: %s drive%d 0x%08x ", drive->name, ide_xfer_verbose(speed), @@ -441,6 +427,83 @@ static int config_chipset_for_dma (ide_drive_t *drive, byte ultra) * 11, 5, 4, 3, 2, 1, 0 */ +static int config_chipset_for_pio (ide_drive_t *drive, byte pio) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + byte drive_pci, speed; + byte AP, BP, TA, TB; + + int drive_number = ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01)); + int err; + + switch (drive_number) { + case 0: drive_pci = 0x60; break; + case 1: drive_pci = 0x64; break; + case 2: drive_pci = 0x68; break; + case 3: drive_pci = 0x6c; break; + default: return 1; + } + + pci_read_config_byte(dev, (drive_pci), &AP); + pci_read_config_byte(dev, (drive_pci)|0x01, &BP); + + + if ((AP & 0x0F) || (BP & 0x07)) { + /* clear PIO modes of lower 8421 bits of A Register */ + pci_write_config_byte(dev, (drive_pci), AP & ~0x0F); + pci_read_config_byte(dev, (drive_pci), &AP); + + /* clear PIO modes of lower 421 bits of B Register */ + pci_write_config_byte(dev, (drive_pci)|0x01, BP & ~0x07); + pci_read_config_byte(dev, (drive_pci)|0x01, &BP); + + pci_read_config_byte(dev, (drive_pci), &AP); + pci_read_config_byte(dev, (drive_pci)|0x01, &BP); + } + + pio = (pio == 5) ? 4 : pio; + switch (ide_get_best_pio_mode(drive, 255, pio, NULL)) { + case 4: speed = XFER_PIO_4; TA=0x01; TB=0x04; break; + case 3: speed = XFER_PIO_3; TA=0x02; TB=0x06; break; + case 2: speed = XFER_PIO_2; TA=0x03; TB=0x08; break; + case 1: speed = XFER_PIO_1; TA=0x05; TB=0x0C; break; + case 0: + default: speed = XFER_PIO_0; TA=0x09; TB=0x13; break; + } + pci_write_config_byte(dev, (drive_pci), AP|TA); + pci_write_config_byte(dev, (drive_pci)|0x01, BP|TB); + +#if PDC202XX_DECODE_REGISTER_INFO + pci_read_config_byte(dev, (drive_pci), &AP); + pci_read_config_byte(dev, (drive_pci)|0x01, &BP); + pci_read_config_byte(dev, (drive_pci)|0x02, &CP); + pci_read_config_byte(dev, (drive_pci)|0x03, &DP); + + decode_registers(REG_A, AP); + decode_registers(REG_B, BP); + decode_registers(REG_C, CP); + decode_registers(REG_D, DP); +#endif /* PDC202XX_DECODE_REGISTER_INFO */ + + err = ide_config_drive_speed(drive, speed); + +#if PDC202XX_DEBUG_DRIVE_INFO + printk("%s: %s drive%d 0x%08x ", + drive->name, ide_xfer_verbose(speed), + drive_number, drive_conf); + pci_read_config_dword(dev, drive_pci, &drive_conf); + printk("0x%08x\n", drive_conf); +#endif /* PDC202XX_DEBUG_DRIVE_INFO */ + + return err; +} + +static void pdc202xx_tune_drive (ide_drive_t *drive, byte pio) +{ + (void) config_chipset_for_pio(drive, pio); +} + static int config_drive_xfer_rate (ide_drive_t *drive) { struct hd_driveid *id = drive->id; @@ -450,9 +513,10 @@ static int config_drive_xfer_rate (ide_drive_t *drive) if (id && (id->capability & 1) && hwif->autodma) { /* Consult the list of known "bad" drives */ if (ide_dmaproc(ide_dma_bad_drive, drive)) { - return HWIF(drive)->dmaproc(ide_dma_off, drive); + dma_func = ide_dma_off; + goto fast_ata_pio; } - + dma_func = ide_dma_off_quietly; if (id->field_valid & 4) { if (id->dma_ultra & 0x001F) { /* Force if Capable UltraDMA */ @@ -463,17 +527,31 @@ static int config_drive_xfer_rate (ide_drive_t *drive) } } else if (id->field_valid & 2) { try_dma_modes: - if ((id->dma_mword & 0x0004) || - (id->dma_1word & 0x0004)) { + if ((id->dma_mword & 0x0007) || + (id->dma_1word & 0x0007)) { /* Force if Capable regular DMA modes */ dma_func = config_chipset_for_dma(drive, 0); + if (dma_func != ide_dma_on) + goto no_dma_set; + } + } else if (ide_dmaproc(ide_dma_good_drive, drive)) { + if (id->eide_dma_time > 150) { + goto no_dma_set; } - } else if ((ide_dmaproc(ide_dma_good_drive, drive)) && - (id->eide_dma_time > 150)) { /* Consult the list of known "good" drives */ dma_func = config_chipset_for_dma(drive, 0); + if (dma_func != ide_dma_on) + goto no_dma_set; + } else { + goto fast_ata_pio; } + } else if ((id->capability & 8) || (id->field_valid & 2)) { +fast_ata_pio: + dma_func = ide_dma_off_quietly; +no_dma_set: + (void) config_chipset_for_pio(drive, 5); } + return HWIF(drive)->dmaproc(dma_func, drive); } @@ -498,6 +576,26 @@ unsigned int __init pci_init_pdc202xx (struct pci_dev *dev, const char *name) byte primary_mode = inb(high_16 + 0x001a); byte secondary_mode = inb(high_16 + 0x001b); + if (dev->device == PCI_DEVICE_ID_PROMISE_20262) { + int i = 0; + /* + * software reset - this is required because the bios + * will set UDMA timing on if the hdd supports it. The + * user may want to turn udma off. A bug in the pdc20262 + * is that it cannot handle a downgrade in timing from UDMA + * to DMA. Disk accesses after issuing a set feature command + * will result in errors. A software reset leaves the timing + * registers intact, but resets the drives. + */ + + OUT_BYTE(udma_speed_flag | 0x10, high_16 + 0x001f); + ide_delay_50ms(); + ide_delay_50ms(); + OUT_BYTE(udma_speed_flag & ~0x10, high_16 + 0x001f); + for (i=0; i<40; i++) + ide_delay_50ms(); + } + if (dev->resource[PCI_ROM_RESOURCE].start) { pci_write_config_dword(dev, PCI_ROM_ADDRESS, dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE); printk("%s: ROM enabled at 0x%08lx\n", name, dev->resource[PCI_ROM_RESOURCE].start); @@ -547,24 +645,23 @@ unsigned int __init pci_init_pdc202xx (struct pci_dev *dev, const char *name) return dev->irq; } +unsigned int __init ata66_pdc202xx (ide_hwif_t *hwif) +{ + unsigned short mask = (hwif->channel) ? (1<<11) : (1<<10); + unsigned short CIS; + + pci_read_config_word(hwif->pci_dev, 0x50, &CIS); + return ((CIS & mask) ? 0 : 1); +} + void __init ide_init_pdc202xx (ide_hwif_t *hwif) { + hwif->tuneproc = &pdc202xx_tune_drive; + if (hwif->dma_base) { hwif->dmaproc = &pdc202xx_dmaproc; - - switch(hwif->pci_dev->device) { - case PCI_DEVICE_ID_PROMISE_20262: -#if 0 - { - unsigned long high_16 = hwif->pci_dev->base_address[4] & PCI_BASE_ADDRESS_IO_MASK; - hwif->udma_four = 1; - } -#endif - break; - case PCI_DEVICE_ID_PROMISE_20246: - default: - hwif->udma_four = 0; - break; - } + } else { + hwif->drives[0].autotune = 1; + hwif->drives[1].autotune = 1; } } diff --git a/drivers/block/pdc4030.c b/drivers/block/pdc4030.c index 84fbb4856..080d353a5 100644 --- a/drivers/block/pdc4030.c +++ b/drivers/block/pdc4030.c @@ -162,6 +162,7 @@ int __init setup_pdc4030 (ide_hwif_t *hwif) if (!hwif) return 0; drive = &hwif->drives[0]; + drive->timeout = HZ/100; hwif2 = &ide_hwifs[hwif->index+1]; if (hwif->chipset == ide_pdc4030) /* we've already been found ! */ return 1; @@ -169,7 +170,8 @@ int __init setup_pdc4030 (ide_hwif_t *hwif) if (IN_BYTE(IDE_NSECTOR_REG) == 0xFF || IN_BYTE(IDE_SECTOR_REG) == 0xFF) { return 0; } - OUT_BYTE(0x08,IDE_CONTROL_REG); + if (IDE_CONTROL_REG) + OUT_BYTE(0x08,IDE_CONTROL_REG); if (pdc4030_cmd(drive,PROMISE_GET_CONFIG)) { return 0; } @@ -307,6 +309,9 @@ static void promise_read_intr (ide_drive_t *drive) unsigned int sectors_left, sectors_avail, nsect; struct request *rq; + /* reset timeout */ + drive->timeout = HZ/100; + if (!OK_STAT(stat=GET_STAT(),DATA_READY,BAD_R_STAT)) { ide_error(drive, "promise_read_intr", stat); return; @@ -361,7 +366,8 @@ read_next: if (stat & DRQ_STAT) goto read_again; if (stat & BUSY_STAT) { - ide_set_handler (drive, &promise_read_intr, WAIT_CMD); + drive->timeout = WAIT_CMD; + ide_set_handler (drive, &promise_read_intr); #ifdef DEBUG_READ printk(KERN_DEBUG "%s: promise_read: waiting for" "interrupt\n", drive->name); @@ -390,7 +396,7 @@ static void promise_complete_pollfunc(ide_drive_t *drive) if (GET_STAT() & BUSY_STAT) { if (time_before(jiffies, hwgroup->poll_timeout)) { - ide_set_handler(drive, &promise_complete_pollfunc, 1); + ide_set_handler(drive, &promise_complete_pollfunc); return; /* continue polling... */ } hwgroup->poll_timeout = 0; @@ -419,7 +425,7 @@ static void promise_write_pollfunc (ide_drive_t *drive) if (IN_BYTE(IDE_NSECTOR_REG) != 0) { if (time_before(jiffies, hwgroup->poll_timeout)) { - ide_set_handler (drive, &promise_write_pollfunc, 1); + ide_set_handler (drive, &promise_write_pollfunc); return; /* continue polling... */ } hwgroup->poll_timeout = 0; @@ -433,7 +439,7 @@ static void promise_write_pollfunc (ide_drive_t *drive) */ ide_multwrite(drive, 4); hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE; - ide_set_handler(drive, &promise_complete_pollfunc, 1); + ide_set_handler(drive, &promise_complete_pollfunc); #ifdef DEBUG_WRITE printk(KERN_DEBUG "%s: Done last 4 sectors - status = %02x\n", drive->name, GET_STAT()); @@ -466,7 +472,7 @@ static void promise_write (ide_drive_t *drive) if (rq->nr_sectors > 4) { ide_multwrite(drive, rq->nr_sectors - 4); hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE; - ide_set_handler (drive, &promise_write_pollfunc, 1); + ide_set_handler (drive, &promise_write_pollfunc); } else { /* * There are 4 or fewer sectors to transfer, do them all in one go @@ -474,7 +480,7 @@ static void promise_write (ide_drive_t *drive) */ ide_multwrite(drive, rq->nr_sectors); hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE; - ide_set_handler(drive, &promise_complete_pollfunc, 1); + ide_set_handler(drive, &promise_complete_pollfunc); #ifdef DEBUG_WRITE printk(KERN_DEBUG "%s: promise_write: <= 4 sectors, " "status = %02x\n", drive->name, GET_STAT()); @@ -517,7 +523,8 @@ void do_pdc4030_io (ide_drive_t *drive, struct request *rq) printk(KERN_DEBUG "%s: read: waiting for " "interrupt\n", drive->name); #endif - ide_set_handler(drive, &promise_read_intr, WAIT_CMD); + drive->timeout = WAIT_CMD; + ide_set_handler(drive, &promise_read_intr); return; } udelay(1); diff --git a/drivers/block/piix.c b/drivers/block/piix.c index b1fd76014..a9bcb347c 100644 --- a/drivers/block/piix.c +++ b/drivers/block/piix.c @@ -1,8 +1,9 @@ /* - * linux/drivers/block/piix.c Version 0.25 July 11, 1999 + * linux/drivers/block/piix.c Version 0.27 Sept. 3, 1999 * * Copyright (C) 1998-1999 Andrzej Krzysztofowicz, Author and Maintainer - * Copyright (C) 1998-1999 Andre Hedrick, Author and Maintainer + * Copyright (C) 1998-1999 Andre Hedrick (andre@suse.com) + * May be copied or modified under the terms of the GNU General Public License * * PIO mode setting function for Intel chipsets. * For use instead of BIOS settings. @@ -182,30 +183,13 @@ static int piix_config_drive_for_dma(ide_drive_t *drive) } } - if ((id->dma_ultra & 0x0010) && (ultra)) { - goto backspeed; - } else if ((id->dma_ultra & 0x0008) && (ultra)) { - goto backspeed; - } else if ((id->dma_ultra & 0x0004) && (ultra)) { -backspeed: - drive->id->dma_mword &= ~0x0F00; - drive->id->dma_1word &= ~0x0F00; - if (!((id->dma_ultra >> 8) & 4)) { - drive->id->dma_ultra &= ~0x0F00; - drive->id->dma_ultra |= 0x0404; - } + if (((id->dma_ultra & 0x0010) || (id->dma_ultra & 0x0008) || (id->dma_ultra & 0x0004)) && (ultra)) { u_speed = 2 << (drive_number * 4); if (!(reg4a & u_speed)) { pci_write_config_word(dev, 0x4a, reg4a|u_speed); } speed = XFER_UDMA_2; } else if ((id->dma_ultra & 0x0002) && (ultra)) { - drive->id->dma_mword &= ~0x0F00; - drive->id->dma_1word &= ~0x0F00; - if (!((id->dma_ultra >> 8) & 2)) { - drive->id->dma_ultra &= ~0x0F00; - drive->id->dma_ultra |= 0x0202; - } u_speed = 1 << (drive_number * 4); if (!(reg4a & u_speed)) { pci_write_config_word(dev, 0x4a, reg4a & ~a_speed); @@ -213,12 +197,6 @@ backspeed: } speed = XFER_UDMA_1; } else if ((id->dma_ultra & 0x0001) && (ultra)) { - drive->id->dma_mword &= ~0x0F00; - drive->id->dma_1word &= ~0x0F00; - if (!((id->dma_ultra >> 8) & 1)) { - drive->id->dma_ultra &= ~0x0F00; - drive->id->dma_ultra |= 0x0101; - } u_speed = 0 << (drive_number * 4); if (!(reg4a & u_speed)) { pci_write_config_word(dev, 0x4a, reg4a & ~a_speed); @@ -228,32 +206,14 @@ backspeed: } else if (id->dma_mword & 0x0004) { if (reg4a & a_speed) pci_write_config_word(dev, 0x4a, reg4a & ~a_speed); - drive->id->dma_ultra &= ~0x0F00; - drive->id->dma_1word &= ~0x0F00; - if (!((id->dma_mword >> 8) & 4)) { - drive->id->dma_mword &= ~0x0F00; - drive->id->dma_mword |= 0x0404; - } speed = XFER_MW_DMA_2; } else if (id->dma_mword & 0x0002) { if (reg4a & a_speed) pci_write_config_word(dev, 0x4a, reg4a & ~a_speed); - drive->id->dma_ultra &= ~0x0F00; - drive->id->dma_1word &= ~0x0F00; - if (!((id->dma_mword >> 8) & 2)) { - drive->id->dma_mword &= ~0x0F00; - drive->id->dma_mword |= 0x0202; - } speed = XFER_MW_DMA_1; } else if (id->dma_1word & 0x0004) { if (reg4a & a_speed) pci_write_config_word(dev, 0x4a, reg4a & ~a_speed); - drive->id->dma_ultra &= ~0x0F00; - drive->id->dma_mword &= ~0x0F00; - if (!((id->dma_1word >> 8) & 4)) { - drive->id->dma_1word &= ~0x0F00; - drive->id->dma_1word |= 0x0404; - } speed = XFER_SW_DMA_2; } else { speed = XFER_PIO_0 + ide_get_best_pio_mode(drive, 255, 5, NULL); @@ -264,10 +224,7 @@ backspeed: (void) ide_config_drive_speed(drive, speed); #if PIIX_DEBUG_DRIVE_INFO - printk("%s: %s drive%d ", - drive->name, - ide_xfer_verbose(speed), - drive_number); + printk("%s: %s drive%d ", drive->name, ide_xfer_verbose(speed), drive_number); printk("\n"); #endif /* PIIX_DEBUG_DRIVE_INFO */ @@ -291,7 +248,7 @@ static int piix_dmaproc(ide_dma_action_t func, ide_drive_t *drive) } #endif /* CONFIG_BLK_DEV_PIIX_TUNING */ -void ide_init_piix (ide_hwif_t *hwif) +void __init ide_init_piix (ide_hwif_t *hwif) { hwif->tuneproc = &piix_tune_drive; diff --git a/drivers/block/rd.c b/drivers/block/rd.c index 7aeb3a30f..7c2b23dab 100644 --- a/drivers/block/rd.c +++ b/drivers/block/rd.c @@ -704,8 +704,9 @@ void __init initrd_load(void) #define OF(args) args +#ifndef memzero #define memzero(s, n) memset ((s), 0, (n)) - +#endif typedef unsigned char uch; typedef unsigned short ush; diff --git a/drivers/block/sis5513.c b/drivers/block/sis5513.c index 08bbb1c91..8a3abd025 100644 --- a/drivers/block/sis5513.c +++ b/drivers/block/sis5513.c @@ -1,11 +1,11 @@ /* - * linux/drivers/block/sis5513.c Version 0.06 July 11, 1999 + * linux/drivers/block/sis5513.c Version 0.07 Sept. 3, 1999 * - * Copyright (C) 1999 Andre Hedrick + * Copyright (C) 1999 Andre Hedrick (andre@suse.com) + * May be copied or modified under the terms of the GNU General Public License * - * drive_number - * = ((HWIF(drive)->channel ? 2 : 0) + (drive->select.b.unit & 0x01)); - * = ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01)); + * Thanks to SIS Taiwan for direct support and hardware. + * Tested and designed on the SiS620/5513 chipset. */ #include <linux/types.h> @@ -27,10 +27,178 @@ #include "ide_modes.h" +#define SIS5513_DEBUG_DRIVE_INFO 0 + +#define DISPLAY_SIS_TIMINGS + static struct pci_dev *host_dev; -#define SIS5513_DEBUG_DRIVE_INFO 0 +#if 0 +static struct _pio_mode_mapping { + byte data_active; + byte recovery; + byte pio_mode; +} pio_mode_mapping[] = { + { 8, 12, 0 }, + { 6, 7, 1 }, + { 4, 4, 2 }, + { 3, 3, 3 }, + { 3, 1, 4 } +}; + +static struct _dma_mode_mapping { + byte data_active; + byte recovery; + byte dma_mode; +} dma_mode_mapping[] = { + { 8, 8, 0 }, + { 3, 2, 1 }, + { 3, 1, 2 } +}; + +static struct _udma_mode_mapping { + byte cycle_time; + char * udma_mode; +} udma_mode_mapping[] = { + { 8, "Mode 0" }, + { 6, "Mode 1" }, + { 4, "Mode 2" }, + { 3, "Mode 3" }, + { 2, "Mode 4" }, + { 0, "Undefined" } +}; + +static __inline__ char * find_udma_mode (byte cycle_time) +{ + int n; + + for (n = 0; n <= 4; n++) + if (udma_mode_mapping[n].cycle_time <= cycle_time) + return udma_mode_mapping[n].udma_mode; + return udma_mode_mapping[4].udma_mode; +} +#endif + +#if defined(DISPLAY_SIS_TIMINGS) && defined(CONFIG_PROC_FS) +#include <linux/stat.h> +#include <linux/proc_fs.h> + +static int sis_get_info(char *, char **, off_t, int, int); +extern int (*sis_display_info)(char *, char **, off_t, int, int); /* ide-proc.c */ +struct pci_dev *bmide_dev; + +static char *cable_type[] = { + "80 pins", + "40 pins" +}; + +static char *recovery_time [] ={ + "12 PCICLK", "1 PCICLK", + "2 PCICLK", "3 PCICLK", + "4 PCICLK", "5 PCICLCK", + "6 PCICLK", "7 PCICLCK", + "8 PCICLK", "9 PCICLCK", + "10 PCICLK", "11 PCICLK", + "13 PCICLK", "14 PCICLK", + "15 PCICLK", "15 PCICLK" +}; + +static char *cycle_time [] = { + "Undefined", "2 CLCK", + "3 CLK", "4 CLK", + "5 CLK", "6 CLK", + "7 CLK", "8 CLK" +}; + +static char *active_time [] = { + "8 PCICLK", "1 PCICLCK", + "2 PCICLK", "2 PCICLK", + "4 PCICLK", "5 PCICLK", + "6 PCICLK", "12 PCICLK" +}; + +static int sis_get_info (char *buffer, char **addr, off_t offset, int count, int dummy) +{ + int rc; + char *p = buffer; + byte reg,reg1; +#if 0 + byte cyc, rec, act; +#endif + u16 reg2, reg3; + + p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n"); + rc = pci_read_config_byte(bmide_dev, 0x4a, ®); + p += sprintf(p, "Channel Status: %s \t \t \t \t %s \n", + (reg & 0x02) ? "On" : "Off", + (reg & 0x04) ? "On" : "Off"); + + rc = pci_read_config_byte(bmide_dev, 0x09, ®); + p += sprintf(p, "Operation Mode: %s \t \t \t %s \n", + (reg & 0x01) ? "Native" : "Compatible", + (reg & 0x04) ? "Native" : "Compatible"); + + rc = pci_read_config_byte(bmide_dev, 0x48, ®); + p += sprintf(p, "Cable Type: %s \t \t \t %s\n", + (reg & 0x10) ? cable_type[1] : cable_type[0], + (reg & 0x20) ? cable_type[1] : cable_type[0]); + + rc = pci_read_config_word(bmide_dev, 0x4c, ®2); + rc = pci_read_config_word(bmide_dev, 0x4e, ®3); + p += sprintf(p, "Prefetch Count: %d \t \t \t \t %d\n", + reg2, reg3); + + rc = pci_read_config_byte(bmide_dev, 0x4b, ®); + p += sprintf(p, "Drvie 0: Postwrite %s \t \t Postwrite %s\n", + (reg & 0x10) ? "Enabled" : "Disabled", + (reg & 0x40) ? "Enabled" : "Disabled"); + p += sprintf(p, " Prefetch %s \t \t Prefetch %s\n", + (reg & 0x01) ? "Enabled" : "Disabled", + (reg & 0x04) ? "Enabled" : "Disabled"); + + rc = pci_read_config_byte(bmide_dev, 0x41, ®); + rc = pci_read_config_byte(bmide_dev, 0x45, ®1); + p += sprintf(p, " UDMA %s \t \t \t UDMA %s\n", + (reg & 0x80) ? "Enabled" : "Disabled", + (reg1 & 0x80) ? "Enabled" : "Disabled"); + p += sprintf(p, " UDMA Cycle Time %s \t UDMA Cycle Time %s\n", + cycle_time[(reg & 0x70) >> 4], cycle_time[(reg1 & 0x70) >> 4]); + p += sprintf(p, " Data Active Time %s \t Data Active Time %s\n", + active_time[(reg & 0x07)], active_time[(reg &0x07)] ); + + rc = pci_read_config_byte(bmide_dev, 0x40, ®); + rc = pci_read_config_byte(bmide_dev, 0x44, ®1); + p += sprintf(p, " Data Recovery Time %s \t Data Recovery Time %s\n", + recovery_time[(reg & 0x0f)], recovery_time[(reg1 & 0x0f)]); + + + rc = pci_read_config_byte(bmide_dev, 0x4b, ®); + p += sprintf(p, "Drvie 1: Postwrite %s \t \t Postwrite %s\n", + (reg & 0x20) ? "Enabled" : "Disabled", + (reg & 0x80) ? "Enabled" : "Disabled"); + p += sprintf(p, " Prefetch %s \t \t Prefetch %s\n", + (reg & 0x02) ? "Enabled" : "Disabled", + (reg & 0x08) ? "Enabled" : "Disabled"); + + rc = pci_read_config_byte(bmide_dev, 0x43, ®); + rc = pci_read_config_byte(bmide_dev, 0x47, ®1); + p += sprintf(p, " UDMA %s \t \t \t UDMA %s\n", + (reg & 0x80) ? "Enabled" : "Disabled", + (reg1 & 0x80) ? "Enabled" : "Disabled"); + p += sprintf(p, " UDMA Cycle Time %s \t UDMA Cycle Time %s\n", + cycle_time[(reg & 0x70) >> 4], cycle_time[(reg1 & 0x70) >> 4]); + p += sprintf(p, " Data Active Time %s \t Data Active Time %s\n", + active_time[(reg & 0x07)], active_time[(reg &0x07)] ); + + rc = pci_read_config_byte(bmide_dev, 0x42, ®); + rc = pci_read_config_byte(bmide_dev, 0x46, ®1); + p += sprintf(p, " Data Recovery Time %s \t Data Recovery Time %s\n", + recovery_time[(reg & 0x0f)], recovery_time[(reg1 & 0x0f)]); + return p-buffer; +} +#endif /* defined(DISPLAY_SIS_TIMINGS) && defined(CONFIG_PROC_FS) */ +byte sis_proc = 0; extern char *ide_xfer_verbose (byte xfer_rate); /* @@ -79,36 +247,18 @@ static int config_chipset_for_dma (ide_drive_t *drive, byte ultra) } if ((id->dma_ultra & 0x0010) && (ultra) && (udma_66) && (four_two)) { - if (!((id->dma_ultra >> 8) & 16)) { - drive->id->dma_ultra &= ~0xFF00; - drive->id->dma_ultra |= 0x1010; - drive->id->dma_mword &= ~0x0F00; - drive->id->dma_1word &= ~0x0F00; - } if (!(test2 & 0x90)) { pci_write_config_byte(dev, drive_pci|0x01, test2 & ~unmask); pci_write_config_byte(dev, drive_pci|0x01, test2|0x90); } speed = XFER_UDMA_4; } else if ((id->dma_ultra & 0x0008) && (ultra) && (udma_66) && (four_two)) { - if (!((id->dma_ultra >> 8) & 8)) { - drive->id->dma_ultra &= ~0xFF00; - drive->id->dma_ultra |= 0x0808; - drive->id->dma_mword &= ~0x0F00; - drive->id->dma_1word &= ~0x0F00; - } if (!(test2 & 0xA0)) { pci_write_config_byte(dev, drive_pci|0x01, test2 & ~unmask); pci_write_config_byte(dev, drive_pci|0x01, test2|0xA0); } speed = XFER_UDMA_3; } else if ((id->dma_ultra & 0x0004) && (ultra)) { - if (!((id->dma_ultra >> 8) & 4)) { - drive->id->dma_ultra &= ~0xFF00; - drive->id->dma_ultra |= 0x0404; - drive->id->dma_mword &= ~0x0F00; - drive->id->dma_1word &= ~0x0F00; - } mask = (four_two) ? 0xB0 : 0xA0; if (!(test2 & mask)) { pci_write_config_byte(dev, drive_pci|0x01, test2 & ~unmask); @@ -116,12 +266,6 @@ static int config_chipset_for_dma (ide_drive_t *drive, byte ultra) } speed = XFER_UDMA_2; } else if ((id->dma_ultra & 0x0002) && (ultra)) { - if (!((id->dma_ultra >> 8) & 2)) { - drive->id->dma_ultra &= ~0xFF00; - drive->id->dma_ultra |= 0x0202; - drive->id->dma_mword &= ~0x0F00; - drive->id->dma_1word &= ~0x0F00; - } mask = (four_two) ? 0xD0 : 0xC0; if (!(test2 & mask)) { pci_write_config_byte(dev, drive_pci|0x01, test2 & ~unmask); @@ -129,58 +273,22 @@ static int config_chipset_for_dma (ide_drive_t *drive, byte ultra) } speed = XFER_UDMA_1; } else if ((id->dma_ultra & 0x0001) && (ultra)) { - if (!((id->dma_ultra >> 8) & 1)) { - drive->id->dma_ultra &= ~0xFF00; - drive->id->dma_ultra |= 0x0101; - drive->id->dma_mword &= ~0x0F00; - drive->id->dma_1word &= ~0x0F00; - } if (!(test2 & unmask)) { pci_write_config_byte(dev, drive_pci|0x01, test2 & ~unmask); pci_write_config_byte(dev, drive_pci|0x01, test2|unmask); } speed = XFER_UDMA_0; } else if (id->dma_mword & 0x0004) { - if (!((id->dma_mword >> 8) & 4)) { - drive->id->dma_mword &= ~0x0F00; - drive->id->dma_mword |= 0x0404; - drive->id->dma_1word &= ~0x0F00; - } speed = XFER_MW_DMA_2; } else if (id->dma_mword & 0x0002) { - if (!((id->dma_mword >> 8) & 2)) { - drive->id->dma_mword &= ~0x0F00; - drive->id->dma_mword |= 0x0202; - drive->id->dma_1word &= ~0x0F00; - } speed = XFER_MW_DMA_1; } else if (id->dma_mword & 0x0001) { - if (!((id->dma_mword >> 8) & 1)) { - drive->id->dma_mword &= ~0x0F00; - drive->id->dma_mword |= 0x0101; - drive->id->dma_1word &= ~0x0F00; - } speed = XFER_MW_DMA_0; } else if (id->dma_1word & 0x0004) { - if (!((id->dma_1word >> 8) & 4)) { - drive->id->dma_1word &= ~0x0F00; - drive->id->dma_1word |= 0x0404; - drive->id->dma_mword &= ~0x0F00; - } speed = XFER_SW_DMA_2; } else if (id->dma_1word & 0x0002) { - if (!((id->dma_1word >> 8) & 2)) { - drive->id->dma_1word &= ~0x0F00; - drive->id->dma_1word |= 0x0202; - drive->id->dma_mword &= ~0x0F00; - } speed = XFER_SW_DMA_1; } else if (id->dma_1word & 0x0001) { - if (!((id->dma_1word >> 8) & 1)) { - drive->id->dma_1word &= ~0x0F00; - drive->id->dma_1word |= 0x0101; - drive->id->dma_mword &= ~0x0F00; - } speed = XFER_SW_DMA_0; } else { return ((int) ide_dma_off_quietly); @@ -189,10 +297,7 @@ static int config_chipset_for_dma (ide_drive_t *drive, byte ultra) err = ide_config_drive_speed(drive, speed); #if SIS5513_DEBUG_DRIVE_INFO - printk("%s: %s drive%d\n", - drive->name, - ide_xfer_verbose(speed), - drive_number); + printk("%s: %s drive%d\n", drive->name, ide_xfer_verbose(speed), drive_number); #endif /* SIS5513_DEBUG_DRIVE_INFO */ return ((int) ((id->dma_ultra >> 11) & 3) ? ide_dma_on : @@ -333,10 +438,9 @@ int sis5513_dmaproc (ide_dma_action_t func, ide_drive_t *drive) unsigned int __init pci_init_sis5513 (struct pci_dev *dev, const char *name) { struct pci_dev *host; - byte latency = 0, reg48h = 0; + byte latency = 0; pci_read_config_byte(dev, PCI_LATENCY_TIMER, &latency); - pci_read_config_byte(dev, 0x48, ®48h); for (host = pci_devices; host; host=host->next) { if (host->vendor == PCI_VENDOR_ID_SI && @@ -344,30 +448,18 @@ unsigned int __init pci_init_sis5513 (struct pci_dev *dev, const char *name) if (latency != 0x10) pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x10); host_dev = host; - printk("%s: Chipset Core ATA-66, SiS620\n", name); - printk("%s: Primary ATA-%s, Secondary ATA-%s Cable Detect\n", - name, - (reg48h & 0x10) ? "33" : "66", - (reg48h & 0x20) ? "33" : "66"); break; } else if (host->vendor == PCI_VENDOR_ID_SI && host->device == PCI_DEVICE_ID_SI_530) { host_dev = host; - printk("%s: Chipset Core ATA-66, SiS530\n", name); - printk("%s: Primary ATA-%s, Secondary ATA-%s Cable Detect\n", - name, - (reg48h & 0x10) ? "33" : "66", - (reg48h & 0x20) ? "33" : "66"); break; } else if (host->vendor == PCI_VENDOR_ID_SI && host->device == PCI_DEVICE_ID_SI_5600) { host_dev = host; - printk("SIS5600:%s Chipset Core ATA-33\n", name); break; } else if (host->vendor == PCI_VENDOR_ID_SI && host->device == PCI_DEVICE_ID_SI_5597) { host_dev = host; - printk("SIS5597:%s Chipset Core ATA-33\n", name); break; } } @@ -377,18 +469,38 @@ unsigned int __init pci_init_sis5513 (struct pci_dev *dev, const char *name) pci_read_config_byte(dev, 0x52, ®52h); if (!(reg52h & 0x04)) + /* set IDE controller to operate in Compabitility mode obly */ pci_write_config_byte(dev, 0x52, reg52h|0x04); +#if defined(DISPLAY_SIS_TIMINGS) && defined(CONFIG_PROC_FS) + sis_proc = 1; + bmide_dev = dev; + sis_display_info = &sis_get_info; +#endif /* defined(DISPLAY_SIS_TIMINGS) && defined(CONFIG_PROC_FS) */ } - return 0; } -void __init ide_init_sis5513 (ide_hwif_t *hwif) +unsigned int __init ata66_sis5513 (ide_hwif_t *hwif) { - byte reg48h = 0; + byte reg48h = 0, ata66 = 0; byte mask = hwif->channel ? 0x20 : 0x10; - pci_read_config_byte(hwif->pci_dev, 0x48, ®48h); + + if (host_dev) { + switch(host_dev->device) { + case PCI_DEVICE_ID_SI_530: + case PCI_DEVICE_ID_SI_620: + ata66 = (reg48h & mask) ? 0 : 1; + default: + break; + } + } + return (ata66); +} + +void __init ide_init_sis5513 (ide_hwif_t *hwif) +{ + hwif->irq = hwif->channel ? 15 : 14; if (!(hwif->dma_base)) @@ -398,20 +510,15 @@ void __init ide_init_sis5513 (ide_hwif_t *hwif) switch(host_dev->device) { case PCI_DEVICE_ID_SI_530: case PCI_DEVICE_ID_SI_620: - hwif->autodma = 1; - hwif->udma_four = (reg48h & mask) ? 0 : 1; - hwif->dmaproc = &sis5513_dmaproc; - return; case PCI_DEVICE_ID_SI_5600: case PCI_DEVICE_ID_SI_5597: hwif->autodma = 1; - hwif->udma_four = 0; hwif->dmaproc = &sis5513_dmaproc; - return; + break; default: hwif->autodma = 0; - hwif->udma_four = 0; - return; + break; } } + return; } diff --git a/drivers/block/sl82c105.c b/drivers/block/sl82c105.c index d4e22d33b..fa47adbb7 100644 --- a/drivers/block/sl82c105.c +++ b/drivers/block/sl82c105.c @@ -5,7 +5,7 @@ * * Maintainer unknown. * - * Drive tuning added from Corel Computer's kernel sources + * Drive tuning added from Rebel.com's kernel sources * -- Russell King (15/11/98) linux@arm.linux.org.uk */ diff --git a/drivers/block/trm290.c b/drivers/block/trm290.c index ad6a75d0f..5c7c8fd3c 100644 --- a/drivers/block/trm290.c +++ b/drivers/block/trm290.c @@ -192,7 +192,8 @@ static int trm290_dmaproc (ide_dma_action_t func, ide_drive_t *drive) outw((count * 2) - 1, hwif->dma_base+2); /* start DMA */ if (drive->media != ide_disk) return 0; - ide_set_handler(drive, &ide_dma_intr, WAIT_CMD); + drive->timeout = WAIT_CMD; + ide_set_handler(drive, &ide_dma_intr); OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG); return 0; case ide_dma_begin: diff --git a/drivers/block/via82c586.c b/drivers/block/via82cxxx.c index e5697014e..e6f61c03f 100644 --- a/drivers/block/via82c586.c +++ b/drivers/block/via82cxxx.c @@ -1,9 +1,12 @@ /* - * linux/drivers/block/via82c586.c Version 0.04 July 11, 1999 + * linux/drivers/block/via82cxxx.c Version 0.05 Sept. 03, 1999 * - * Copyright (C) 1998 Michel Aubry, Maintainer - * Copyright (C) 1998 Andre Hedrick, Maintainer + * Copyright (C) 1998-99 Michel Aubry, Maintainer + * Copyright (C) 1999 Jeff Garzik, MVP4 Support (jgarzik@pobox.com) + * Copyright (C) 1998-99 Andre Hedrick (andre@suse.com) + * May be copied or modified under the terms of the GNU General Public License * + * The VIA MVP-4 is reported OK with UDMA. * The VIA MVP-3 is reported OK with UDMA. * The TX Pro III is also reported OK with UDMA. * @@ -57,8 +60,44 @@ #include <asm/io.h> -static struct pci_dev *host_dev; -static struct pci_dev *isa_dev; +static struct pci_dev *host_dev = NULL; +static struct pci_dev *isa_dev = NULL; + +static const struct { + const char *name; + unsigned short host_id; +} ApolloHostChipInfo[] = { + { "VT 82C585 Apollo VP1/VPX", PCI_DEVICE_ID_VIA_82C585, }, + { "VT 82C595 Apollo VP2", PCI_DEVICE_ID_VIA_82C595, }, + { "VT 82C597 Apollo VP3", PCI_DEVICE_ID_VIA_82C597_0, }, + { "VT 82C598 Apollo MVP3", PCI_DEVICE_ID_VIA_82C598_0, }, + { "VT 82C680 Apollo P6", PCI_DEVICE_ID_VIA_82C680, }, + { "VT 82C691 Apollo Pro", PCI_DEVICE_ID_VIA_82C691, }, + { "VT 82C693 Apollo Pro Plus", PCI_DEVICE_ID_VIA_82C693, }, + { "Apollo MVP4", PCI_DEVICE_ID_VIA_8501_0, }, +}; + +#define NUM_APOLLO_ISA_CHIP_DEVICES 2 +#define VIA_FLAG_CHECK_REV 0x00000001 +#define VIA_FLAG_ATA_66 0x00000002 + +static const struct { + unsigned short host_id; + unsigned short isa_id; + unsigned int flags; +} ApolloISAChipInfo[] = { + { PCI_DEVICE_ID_VIA_82C585, PCI_DEVICE_ID_VIA_82C586_1, VIA_FLAG_CHECK_REV }, + { PCI_DEVICE_ID_VIA_82C595, PCI_DEVICE_ID_VIA_82C586_1, VIA_FLAG_CHECK_REV }, + { PCI_DEVICE_ID_VIA_82C597_0, PCI_DEVICE_ID_VIA_82C586_1, VIA_FLAG_CHECK_REV }, + { PCI_DEVICE_ID_VIA_82C598_0, PCI_DEVICE_ID_VIA_82C586_1, VIA_FLAG_CHECK_REV }, + { PCI_DEVICE_ID_VIA_82C598_0, PCI_DEVICE_ID_VIA_82C596, 0 }, + { PCI_DEVICE_ID_VIA_82C680, PCI_DEVICE_ID_VIA_82C586_1, VIA_FLAG_CHECK_REV }, + { PCI_DEVICE_ID_VIA_82C691, PCI_DEVICE_ID_VIA_82C596, 0 }, + { PCI_DEVICE_ID_VIA_82C693, PCI_DEVICE_ID_VIA_82C596, 0 }, + { PCI_DEVICE_ID_VIA_8501_0, PCI_DEVICE_ID_VIA_82C686, VIA_FLAG_ATA_66 }, +}; + +#define arraysize(x) (sizeof(x)/sizeof(*(x))) #define DISPLAY_VIA_TIMINGS @@ -298,6 +337,7 @@ static int via_get_info (char *buffer, char **addr, off_t offset, int count, int * Used to set Fifo configuration via kernel command line: */ +byte via_proc = 0; byte fifoconfig = 0; static byte newfifo = 0; @@ -340,8 +380,8 @@ static void set_via_timings (ide_hwif_t *hwif) /* * setting Channel read and End-of-sector FIFO flush. * (This feature ensures that FIFO flush is enabled: - * - for read DMA when interrupt asserts the given channel. - * - at the end of each sector for the given channel.) + * - for read DMA when interrupt asserts the given channel. + * - at the end of each sector for the given channel.) */ if ((rc = pci_read_config_byte(dev, 0x46, &via_config))) errors++; @@ -363,7 +403,7 @@ static void set_via_timings (ide_hwif_t *hwif) } /* - * Sets VIA 82c586 FIFO configuration: + * Sets VIA 82cxxx FIFO configuration: * This chipsets gets a splitable fifo. This can be driven either by command * line option (eg "splitfifo=2,2,3" which asks this driver to switch all the * 16 fifo levels to the second drive, and give it a threshold of 3 for (u)dma @@ -435,139 +475,71 @@ static int via_set_fifoconfig(ide_hwif_t *hwif) (newfifo & 0x01) ? "1/4" : "1/2")); #if defined(DISPLAY_VIA_TIMINGS) && defined(CONFIG_PROC_FS) + via_proc = 1; bmide_dev = hwif->pci_dev; via_display_info = &via_get_info; #endif /* DISPLAY_VIA_TIMINGS && CONFIG_PROC_FS*/ return 0; } -unsigned int __init pci_init_via82c568 (struct pci_dev *dev, const char *name) +unsigned int __init pci_init_via82cxxx (struct pci_dev *dev, const char *name) { struct pci_dev *host; struct pci_dev *isa; + int i, j, ata33, ata66; byte revision = 0; - for (host = pci_devices; host; host=host->next) { - if (host->vendor == PCI_VENDOR_ID_VIA && - host->device == PCI_DEVICE_ID_VIA_82C585) { - host_dev = host; - printk("VT 82C585 Apollo VP1/VPX"); - for (isa = pci_devices; isa; isa=isa->next) { - if (isa->vendor == PCI_VENDOR_ID_VIA && - isa->device == PCI_DEVICE_ID_VIA_82C586_1) { - isa_dev = isa; - pci_read_config_byte(isa_dev, 0x0d, &revision); - if (revision >= 0x20) - printk(" Chipset Core ATA-33"); - break; - } - } - printk("\n"); - break; - } else if (host->vendor == PCI_VENDOR_ID_VIA && - host->device == PCI_DEVICE_ID_VIA_82C595) { - host_dev = host; - printk("VT 82C595 Apollo VP2"); - for (isa = pci_devices; isa; isa=isa->next) { - if (isa->vendor == PCI_VENDOR_ID_VIA && - isa->device == PCI_DEVICE_ID_VIA_82C586_1) { - isa_dev = isa; - pci_read_config_byte(isa_dev, 0x0d, &revision); - if (revision >= 0x20) - printk(" Chipset Core ATA-33"); - break; - } - } - printk("\n"); - break; - } else if (host->vendor == PCI_VENDOR_ID_VIA && - host->device == PCI_DEVICE_ID_VIA_82C597_0) { - host_dev = host; - printk("VT 82C597 Apollo VP3"); - for (isa = pci_devices; isa; isa=isa->next) { - if (isa->vendor == PCI_VENDOR_ID_VIA && - isa->device == PCI_DEVICE_ID_VIA_82C586_1) { - isa_dev = isa; - pci_read_config_byte(isa_dev, 0x0d, &revision); - if (revision >= 0x20) - printk(" Chipset Core ATA-33"); - break; - } - } - printk("\n"); - break; - } else if (host->vendor == PCI_VENDOR_ID_VIA && - host->device == PCI_DEVICE_ID_VIA_82C598_0) { - host_dev = host; - printk("VT 82C598 Apollo MVP3"); - for (isa = pci_devices; isa; isa=isa->next) { - if (isa->vendor == PCI_VENDOR_ID_VIA && - isa->device == PCI_DEVICE_ID_VIA_82C586_1) { - isa_dev = isa; - pci_read_config_byte(isa_dev, 0x0d, &revision); - if (revision >= 0x20) - printk(" Chipset Core ATA-33"); - break; - } else if (isa->vendor == PCI_VENDOR_ID_VIA && - isa->device == PCI_DEVICE_ID_VIA_82C596) { - isa_dev = isa; - printk(" Chipset Core ATA-33"); - break; - } - } - printk("\n"); - break; - } else if (host->vendor == PCI_VENDOR_ID_VIA && - host->device == PCI_DEVICE_ID_VIA_82C680) { - host_dev = host; - printk("VT 82C680 Apollo P6"); - for (isa = pci_devices; isa; isa=isa->next) { - if (isa->vendor == PCI_VENDOR_ID_VIA && - isa->device == PCI_DEVICE_ID_VIA_82C586_1) { - isa_dev = isa; - pci_read_config_byte(isa_dev, 0x0d, &revision); - if (revision >= 0x20) - printk(" Chipset Core ATA-33"); - break; - } + for (i = 0; i < arraysize (ApolloHostChipInfo) && !host_dev; i++) { + host = pci_find_device (PCI_VENDOR_ID_VIA, + ApolloHostChipInfo[i].host_id, + NULL); + if (!host) + continue; + + host_dev = host; + printk(ApolloHostChipInfo[i].name); + + for (j = 0; j < arraysize (ApolloISAChipInfo) && !isa_dev; j++) { + if (ApolloISAChipInfo[j].host_id != + ApolloHostChipInfo[i].host_id) + continue; + + isa = pci_find_device (PCI_VENDOR_ID_VIA, + ApolloISAChipInfo[i].isa_id, + NULL); + if (!isa) + continue; + + isa_dev = isa; + + ata33 = 1; + ata66 = 0; + + if (ApolloISAChipInfo[i].flags & VIA_FLAG_CHECK_REV) { + pci_read_config_byte(isa_dev, 0x0d, &revision); + ata33 = (revision >= 0x20) ? 1 : 0; + } else if (ApolloISAChipInfo[i].flags & VIA_FLAG_ATA_66) { + ata33 = 0; + ata66 = 1; } - printk("\n"); - break; - } else if (host->vendor == PCI_VENDOR_ID_VIA && - host->device == PCI_DEVICE_ID_VIA_82C691) { - host_dev = host; - printk("VT 82C691 Apollo Pro"); - for (isa = pci_devices; isa; isa=isa->next) { - if (isa->vendor == PCI_VENDOR_ID_VIA && - isa->device == PCI_DEVICE_ID_VIA_82C596) { - isa_dev = isa; - printk(" Chipset Core ATA-33"); - break; - } - } - printk("\n"); - break; - } else if (host->vendor == PCI_VENDOR_ID_VIA && - host->device == PCI_DEVICE_ID_VIA_82C693) { - host_dev = host; - printk("VT 82C693 Apollo Pro Plus"); - for (isa = pci_devices; isa; isa=isa->next) { - if (isa->vendor == PCI_VENDOR_ID_VIA && - isa->device == PCI_DEVICE_ID_VIA_82C596) { - isa_dev = isa; - printk(" Chipset Core ATA-33"); - break; - } - } - printk("\n"); - break; + + if (ata33 | ata66) + printk(" Chipset Core ATA-%s", ata66 ? "66" : "33"); } + printk("\n"); } + + return 0; +} + +unsigned int __init ata66_via82cxxx (ide_hwif_t *hwif) +{ + /* (Jeff Garzik) FIXME!!! for MVP4 */ return 0; } -void __init ide_init_via82c586 (ide_hwif_t *hwif) +void __init ide_init_via82cxxx (ide_hwif_t *hwif) { set_via_timings(hwif); } @@ -581,7 +553,7 @@ void __init ide_init_via82c586 (ide_hwif_t *hwif) * bypasses the setup if not capable. */ -void ide_dmacapable_via82c586 (ide_hwif_t *hwif, unsigned long dmabase) +void ide_dmacapable_via82cxxx (ide_hwif_t *hwif, unsigned long dmabase) { if (!done) { via_set_fifoconfig(hwif); diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c index 7a46ed183..e57de2523 100644 --- a/drivers/cdrom/cdrom.c +++ b/drivers/cdrom/cdrom.c @@ -177,10 +177,15 @@ for ide-cd to handle multisession discs. -- Export cdrom_mode_sense and cdrom_mode_select. -- init_cdrom_command() for setting up a cgc command. + + 3.05 Sep 23, 1999 - Jens Axboe <axboe@image.dk> + -- Changed the interface for CDROM_SEND_PACKET. Before it was virtually + impossible to send the drive data in a sensible way. + -------------------------------------------------------------------------*/ -#define REVISION "Revision: 3.04" -#define VERSION "Id: cdrom.c 3.04 1999/09/14" +#define REVISION "Revision: 3.05" +#define VERSION "Id: cdrom.c 3.05 1999/09/23" /* I use an error-log mask to give fine grain control over the type of messages dumped to the system logs. The available masks include: */ @@ -213,6 +218,7 @@ #include <linux/cdrom.h> #include <linux/sysctl.h> #include <linux/proc_fs.h> +#include <linux/init.h> #include <asm/fcntl.h> #include <asm/segment.h> #include <asm/uaccess.h> @@ -316,7 +322,7 @@ int register_cdrom(struct cdrom_device_info *cdi) if (cdo->open == NULL || cdo->release == NULL) return -2; if ( !banner_printed ) { - printk(KERN_INFO "Uniform CDROM driver " REVISION "\n"); + printk(KERN_INFO "Uniform CD-ROM driver " REVISION "\n"); banner_printed = 1; #ifdef CONFIG_SYSCTL cdrom_sysctl_register(); @@ -404,6 +410,8 @@ struct cdrom_device_info *cdrom_find_device (kdev_t dev) return cdi; } +static int cdrom_setup_writemode(struct cdrom_device_info *cdi); + /* We use the open-option O_NONBLOCK to indicate that the * purpose of opening is only for subsequent ioctl() calls; no device * integrity checks are performed. @@ -415,22 +423,31 @@ struct cdrom_device_info *cdrom_find_device (kdev_t dev) static int cdrom_open(struct inode *ip, struct file *fp) { + struct cdrom_device_info *cdi; kdev_t dev = ip->i_rdev; - struct cdrom_device_info *cdi = cdrom_find_device(dev); - int purpose = !!(fp->f_flags & O_NONBLOCK); - int ret=0; + int ret; cdinfo(CD_OPEN, "entering cdrom_open\n"); - if (cdi == NULL) + if ((cdi = cdrom_find_device(dev)) == NULL) return -ENODEV; - if (fp->f_mode & FMODE_WRITE) - return -EROFS; - purpose = purpose || !(cdi->options & CDO_USE_FFLAGS); - if (purpose) - ret = cdi->ops->open(cdi, purpose); + + /* just CD-RW for now. DVD-RW will come soon, CD-R and DVD-R + * need to be handled differently. */ + if ((fp->f_mode & FMODE_WRITE) && !CDROM_CAN(CDC_CD_RW)) + return -EROFS; + + /* if this was a O_NONBLOCK open and we should honor the flags, + * do a quick open without drive/disc integrity checks. */ + if ((fp->f_flags & O_NONBLOCK) && (cdi->options & CDO_USE_FFLAGS)) + ret = cdi->ops->open(cdi, 1); else ret = open_for_data(cdi); + if (!ret) cdi->use_count++; + + if (fp->f_mode & FMODE_WRITE && !cdi->write.writeable) + cdi->write.writeable = !cdrom_setup_writemode(cdi); + cdinfo(CD_OPEN, "Use count for \"/dev/%s\" now %d\n", cdi->name, cdi->use_count); /* Do this on open. Don't wait for mount, because they might not be mounting, but opening with O_NONBLOCK */ @@ -525,7 +542,7 @@ int open_for_data(struct cdrom_device_info * cdi) if (CDROM_CAN(CDC_LOCK) && cdi->options & CDO_LOCK) { cdo->lock_door(cdi, 1); cdinfo(CD_OPEN, "door locked.\n"); - } + } cdinfo(CD_OPEN, "device opened successfully.\n"); return ret; @@ -616,7 +633,7 @@ int cdrom_release(struct inode *ip, struct file *fp) if (cdi->use_count > 0) cdi->use_count--; if (cdi->use_count == 0) cdinfo(CD_CLOSE, "Use count for \"/dev/%s\" now zero\n", cdi->name); - if (cdi->use_count == 0 && /* last process that closes dev*/ + if (cdi->use_count == 0 && cdo->capability & CDC_LOCK && !keeplocked) { cdinfo(CD_CLOSE, "Unlocking door!\n"); cdo->lock_door(cdi, 0); @@ -1701,8 +1718,8 @@ static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, unsigned long arg) { struct cdrom_device_ops *cdo = cdi->ops; - kdev_t dev = cdi->dev; struct cdrom_generic_command cgc; + kdev_t dev = cdi->dev; char buffer[32]; int ret = 0; @@ -1919,7 +1936,7 @@ static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, case CDROMSTART: case CDROMSTOP: { - cdinfo(CD_DO_IOCTL, "entering audio ioctl (start/stop)\n"); + cdinfo(CD_DO_IOCTL, "entering CDROMSTART/CDROMSTOP\n"); cgc.cmd[0] = GPCMD_START_STOP_UNIT; cgc.cmd[1] = 1; cgc.cmd[4] = (cmd == CDROMSTART) ? 1 : 0; @@ -1928,7 +1945,7 @@ static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, case CDROMPAUSE: case CDROMRESUME: { - cdinfo(CD_DO_IOCTL, "entering audio ioctl (pause/resume)\n"); + cdinfo(CD_DO_IOCTL, "entering CDROMPAUSE/CDROMRESUME\n"); cgc.cmd[0] = GPCMD_PAUSE_RESUME; cgc.cmd[8] = (cmd == CDROMRESUME) ? 1 : 0; return cdo->generic_packet(cdi, &cgc); @@ -1938,7 +1955,7 @@ static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, dvd_struct s; if (!CDROM_CAN(CDC_DVD)) return -ENOSYS; - cdinfo(CD_DO_IOCTL, "entering dvd_read_struct\n"); + cdinfo(CD_DO_IOCTL, "entering DVD_READ_STRUCT\n"); IOCTL_IN(arg, dvd_struct, s); if ((ret = dvd_read_struct(cdi, &s))) return ret; @@ -1950,7 +1967,7 @@ static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, dvd_authinfo ai; if (!CDROM_CAN(CDC_DVD)) return -ENOSYS; - cdinfo(CD_DO_IOCTL, "entering dvd_auth\n"); + cdinfo(CD_DO_IOCTL, "entering DVD_AUTH\n"); IOCTL_IN(arg, dvd_authinfo, ai); if ((ret = dvd_do_auth (cdi, &ai))) return ret; @@ -1959,26 +1976,58 @@ static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, } case CDROM_SEND_PACKET: { + __u8 *userbuf, copy = 0; if (!CDROM_CAN(CDC_GENERIC_PACKET)) return -ENOSYS; - cdinfo(CD_DO_IOCTL, "entering send_packet\n"); + cdinfo(CD_DO_IOCTL, "entering CDROM_SEND_PACKET\n"); IOCTL_IN(arg, struct cdrom_generic_command, cgc); - cgc.buffer = kmalloc(cgc.buflen, GFP_KERNEL); + copy = !!cgc.buflen; + userbuf = cgc.buffer; + cgc.buffer = NULL; + if (userbuf != NULL && copy) { + /* usually commands just copy data one way, i.e. + * we send a buffer to the drive and the command + * specifies whether the drive will read or + * write to that buffer. usually the buffers + * are very small, so we don't loose that much + * by doing a redundant copy each time. */ + if (!access_ok(VERIFY_WRITE, userbuf, cgc.buflen)) { + printk("can't get write perms\n"); + return -EFAULT; + } + if (!access_ok(VERIFY_READ, userbuf, cgc.buflen)) { + printk("can't get read perms\n"); + return -EFAULT; + } + } + /* reasonable limits */ + if (cgc.buflen < 0 || cgc.buflen > 131072) { + printk("invalid size given\n"); + return -EINVAL; + } + if (copy) { + cgc.buffer = kmalloc(cgc.buflen, GFP_KERNEL); + if (cgc.buffer == NULL) + return -ENOMEM; + __copy_from_user(cgc.buffer, userbuf, cgc.buflen); + } ret = cdo->generic_packet(cdi, &cgc); - if (copy_to_user((void*)arg, cgc.buffer, cgc.buflen)) - ret = -EFAULT; + if (copy && !ret) + __copy_to_user(userbuf, cgc.buffer, cgc.buflen); kfree(cgc.buffer); return ret; } case CDROM_NEXT_WRITABLE: { - long next; + long next = 0; + cdinfo(CD_DO_IOCTL, "entering CDROM_NEXT_WRITABLE\n"); if ((ret = cdrom_get_next_writable(dev, &next))) return ret; IOCTL_OUT(arg, long, next); return 0; } case CDROM_LAST_WRITTEN: { - long last; + long last = 0; + cdinfo(CD_DO_IOCTL, "entering CDROM_LAST_WRITTEN\n"); if ((ret = cdrom_get_last_written(dev, &last))) return ret; IOCTL_OUT(arg, long, last); @@ -2038,10 +2087,10 @@ int cdrom_get_last_written(kdev_t dev, long *last_written) track_information ti; __u32 last_track; int ret = -1; - + if (!CDROM_CAN(CDC_GENERIC_PACKET)) goto use_toc; - + if ((ret = cdrom_get_disc_info(dev, &di))) goto use_toc; @@ -2089,7 +2138,7 @@ int cdrom_get_next_writable(kdev_t dev, long *next_writable) track_information ti; __u16 last_track; int ret = -1; - + if (!CDROM_CAN(CDC_GENERIC_PACKET)) goto use_last_written; @@ -2125,6 +2174,99 @@ use_last_written: } } +/* return the buffer size of writeable drives */ +static int cdrom_read_buffer_capacity(struct cdrom_device_info *cdi) +{ + struct cdrom_device_ops *cdo = cdi->ops; + struct cdrom_generic_command cgc; + struct { + unsigned int pad; + unsigned int buffer_size; + unsigned int buffer_free; + } buf; + int ret; + + init_cdrom_command(&cgc, &buf, 12); + cgc.cmd[0] = 0x5c; + cgc.cmd[8] = 12; + + if ((ret = cdo->generic_packet(cdi, &cgc))) + return ret; + + return be32_to_cpu(buf.buffer_size); +} + +/* return 0 if succesful and the disc can be considered writeable. */ +static int cdrom_setup_writemode(struct cdrom_device_info *cdi) +{ + struct cdrom_generic_command cgc; + write_param_page wp; + disc_information di; + track_information ti; + int ret, last_track; + + memset(&di, 0, sizeof(disc_information)); + memset(&ti, 0, sizeof(track_information)); + memset(&wp, 0, sizeof(write_param_page)); + memset(&cdi->write, 0, sizeof(struct cdrom_write_settings)); + + if ((ret = cdrom_get_disc_info(cdi->dev, &di))) + return ret; + + last_track = (di.last_track_msb << 8) | di.last_track_lsb; + if ((ret = cdrom_get_track_info(cdi->dev, last_track, 1, &ti))) + return ret; + + /* if the media is erasable, then it is either CD-RW or + * DVD-RW - use fixed packets for those. non-erasable media + * indicated CD-R or DVD-R media, use varible sized packets for + * those (where the packet size is a bit less than the buffer + * capacity of the drive. */ + if (di.erasable) { + cdi->write.fpacket = 1; + /* FIXME: DVD-RW is 16, should get the packet size instead */ + cdi->write.packet_size = 32; + } else { + int buf_size; + cdi->write.fpacket = 0; + buf_size = cdrom_read_buffer_capacity(cdi); + buf_size -= 100*1024; + cdi->write.packet_size = buf_size / CD_FRAMESIZE; + } + + init_cdrom_command(&cgc, &wp, 0x3c); + if ((ret = cdrom_mode_sense(cdi, &cgc, GPMODE_WRITE_PARMS_PAGE, 0))) + return ret; + + /* sanity checks */ + if ((ti.damage && !ti.nwa_v) || ti.blank) { + cdinfo(CD_WARNING, "can't write to this disc\n"); + return 1; + } + + /* NWA is only for CD-R and DVD-R. -RW media is randomly + * writeable once it has been formatted. */ + cdi->write.nwa = ti.nwa_v ? be32_to_cpu(ti.next_writable) : 0; + + wp.fp = cdi->write.fpacket ? 1 : 0; + wp.track_mode = 0; + wp.write_type = 0; + wp.data_block_type = 8; + wp.session_format = 0; + wp.multi_session = 3; + wp.audio_pause = cpu_to_be16(0x96); + wp.packet_size = cdi->write.fpacket ? cpu_to_be32(cdi->write.packet_size) : 0; + wp.track_mode = 5; /* should be ok with both CD and DVD */ + + if ((ret = cdrom_mode_select(cdi, &cgc))) + return ret; + + cdinfo(CD_WARNING, "%s: writeable with %lu block %s packets\n", + cdi->name, cdi->write.packet_size, + cdi->write.fpacket ? "fixed" : "variable"); + return 0; +} + EXPORT_SYMBOL(cdrom_get_next_writable); EXPORT_SYMBOL(cdrom_get_last_written); EXPORT_SYMBOL(cdrom_count_tracks); @@ -2384,22 +2526,22 @@ static void cdrom_sysctl_register(void) initialized = 1; } +#endif /* endif CONFIG_SYSCTL */ + #ifdef MODULE static void cdrom_sysctl_unregister(void) { +#ifdef CONFIG_SYSCTL unregister_sysctl_table(cdrom_sysctl_header); +#endif } -#endif /* endif MODULE */ -#endif /* endif CONFIG_SYSCTL */ - -#ifdef MODULE int init_module(void) { #ifdef CONFIG_SYSCTL cdrom_sysctl_register(); -#endif /* CONFIG_SYSCTL */ +#endif return 0; } @@ -2410,5 +2552,5 @@ void cleanup_module(void) cdrom_sysctl_unregister(); #endif /* CONFIG_SYSCTL */ } - #endif /* endif MODULE */ + diff --git a/drivers/char/Config.in b/drivers/char/Config.in index 3000b6a73..979f18ae0 100644 --- a/drivers/char/Config.in +++ b/drivers/char/Config.in @@ -10,15 +10,15 @@ if [ "$CONFIG_VT" = "y" ]; then fi tristate 'Standard/generic (dumb) serial support' CONFIG_SERIAL if [ "$CONFIG_SERIAL" = "y" ]; then - bool ' Support for console on serial port' CONFIG_SERIAL_CONSOLE + bool ' Support for console on serial port' CONFIG_SERIAL_CONSOLE fi bool 'Extended dumb serial driver options' CONFIG_SERIAL_EXTENDED if [ "$CONFIG_SERIAL_EXTENDED" = "y" ]; then - bool ' Support more than 4 serial ports' CONFIG_SERIAL_MANY_PORTS - bool ' Support for sharing serial interrupts' CONFIG_SERIAL_SHARE_IRQ - bool ' Autodetect IRQ on standard ports (unsafe)' CONFIG_SERIAL_DETECT_IRQ - bool ' Support special multiport boards' CONFIG_SERIAL_MULTIPORT - bool ' Support the Bell Technologies HUB6 card' CONFIG_HUB6 + bool ' Support more than 4 serial ports' CONFIG_SERIAL_MANY_PORTS + bool ' Support for sharing serial interrupts' CONFIG_SERIAL_SHARE_IRQ + bool ' Autodetect IRQ on standard ports (unsafe)' CONFIG_SERIAL_DETECT_IRQ + bool ' Support special multiport boards' CONFIG_SERIAL_MULTIPORT + bool ' Support the Bell Technologies HUB6 card' CONFIG_HUB6 fi bool 'Non-standard serial port support' CONFIG_SERIAL_NONSTANDARD if [ "$CONFIG_SERIAL_NONSTANDARD" = "y" ]; then @@ -86,31 +86,31 @@ tristate 'QIC-02 tape support' CONFIG_QIC02_TAPE if [ "$CONFIG_QIC02_TAPE" != "n" ]; then bool 'Do you want runtime configuration for QIC-02' CONFIG_QIC02_DYNCONF if [ "$CONFIG_QIC02_DYNCONF" != "y" ]; then - comment ' Edit configuration parameters in ./include/linux/tpqic02.h!' + comment ' Edit configuration parameters in ./include/linux/tpqic02.h!' else - comment ' Setting runtime QIC-02 configuration is done with qic02conf' - comment ' from the tpqic02-support package. It is available at' - comment ' metalab.unc.edu or ftp://titus.cfw.com/pub/Linux/util/' + comment ' Setting runtime QIC-02 configuration is done with qic02conf' + comment ' from the tpqic02-support package. It is available at' + comment ' metalab.unc.edu or ftp://titus.cfw.com/pub/Linux/util/' fi dep_tristate 'Zoran ZR36057/36060 support' CONFIG_VIDEO_ZORAN $CONFIG_VIDEO_DEV - dep_tristate ' Include support for Iomega Buz' CONFIG_VIDEO_BUZ $CONFIG_VIDEO_ZORAN + dep_tristate ' Include support for Iomega Buz' CONFIG_VIDEO_BUZ $CONFIG_VIDEO_ZORAN fi bool 'Watchdog Timer Support' CONFIG_WATCHDOG if [ "$CONFIG_WATCHDOG" != "n" ]; then mainmenu_option next_comment comment 'Watchdog Cards' - bool ' Disable watchdog shutdown on close' CONFIG_WATCHDOG_NOWAYOUT - tristate ' WDT Watchdog timer' CONFIG_WDT + bool ' Disable watchdog shutdown on close' CONFIG_WATCHDOG_NOWAYOUT + tristate ' WDT Watchdog timer' CONFIG_WDT if [ "$CONFIG_WDT" != "n" ]; then - bool ' WDT501 features' CONFIG_WDT_501 + bool ' WDT501 features' CONFIG_WDT_501 if [ "$CONFIG_WDT_501" = "y" ]; then - bool ' Fan Tachometer' CONFIG_WDT_501_FAN + bool ' Fan Tachometer' CONFIG_WDT_501_FAN fi fi - tristate ' Software Watchdog' CONFIG_SOFT_WATCHDOG - tristate ' Berkshire Products PC Watchdog' CONFIG_PCWATCHDOG - tristate ' Acquire SBC Watchdog Timer' CONFIG_ACQUIRE_WDT + tristate ' Software Watchdog' CONFIG_SOFT_WATCHDOG + tristate ' Berkshire Products PC Watchdog' CONFIG_PCWATCHDOG + tristate ' Acquire SBC Watchdog Timer' CONFIG_ACQUIRE_WDT endmenu fi diff --git a/drivers/char/Makefile b/drivers/char/Makefile index b9d58b64c..490ad02e3 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -133,7 +133,7 @@ else endif ifeq ($(CONFIG_COMPUTONE),y) -L_OBJS += ip2.o ip2main.o +O_OBJS += ip2.o ip2main.o else ifeq ($(CONFIG_COMPUTONE),m) M_OBJS += ip2.o ip2main.o @@ -181,7 +181,7 @@ else endif ifeq ($(CONFIG_SX),y) -L_OBJS += sx.o generic_serial.o +O_OBJS += sx.o generic_serial.o else ifeq ($(CONFIG_SX),m) M_OBJS += sx.o diff --git a/drivers/char/console.c b/drivers/char/console.c index f452237ab..49512fe42 100644 --- a/drivers/char/console.c +++ b/drivers/char/console.c @@ -94,6 +94,7 @@ #ifdef CONFIG_APM #include <linux/apm_bios.h> #endif +#include <linux/bootmem.h> #include <asm/io.h> #include <asm/system.h> @@ -2286,7 +2287,7 @@ static void vc_init(unsigned int currcons, unsigned int rows, unsigned int cols, struct tty_driver console_driver; static int console_refcount; -unsigned long __init con_init(unsigned long kmem_start) +void __init con_init(void) { const char *display_desc = NULL; unsigned int currcons = 0; @@ -2295,7 +2296,7 @@ unsigned long __init con_init(unsigned long kmem_start) display_desc = conswitchp->con_startup(); if (!display_desc) { fg_console = 0; - return kmem_start; + return; } memset(&console_driver, 0, sizeof(struct tty_driver)); @@ -2336,19 +2337,18 @@ unsigned long __init con_init(unsigned long kmem_start) timer_active |= 1<<BLANK_TIMER; } - /* Unfortunately, kmalloc is not running yet */ - /* Due to kmalloc roundup allocating statically is more efficient - - so provide MIN_NR_CONSOLES for people with very little memory */ + /* + * kmalloc is not running yet - we use the bootmem allocator. + */ for (currcons = 0; currcons < MIN_NR_CONSOLES; currcons++) { int j, k ; - vc_cons[currcons].d = (struct vc_data *) kmem_start; - kmem_start += sizeof(struct vc_data); - vt_cons[currcons] = (struct vt_struct *) kmem_start; - kmem_start += sizeof(struct vt_struct); + vc_cons[currcons].d = (struct vc_data *) + alloc_bootmem(sizeof(struct vc_data)); + vt_cons[currcons] = (struct vt_struct *) + alloc_bootmem(sizeof(struct vt_struct)); visual_init(currcons, 1); - screenbuf = (unsigned short *) kmem_start; - kmem_start += screenbuf_size; + screenbuf = (unsigned short *) alloc_bootmem(screenbuf_size); kmalloced = 0; vc_init(currcons, video_num_lines, video_num_columns, currcons || !sw->con_save_screen); @@ -2376,8 +2376,6 @@ unsigned long __init con_init(unsigned long kmem_start) #endif init_bh(CONSOLE_BH, console_bh); - - return kmem_start; } #ifndef VT_SINGLE_DRIVER diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c index 5176f28f4..57f3d7199 100644 --- a/drivers/char/istallion.c +++ b/drivers/char/istallion.c @@ -34,6 +34,7 @@ #include <linux/serial.h> #include <linux/cdk.h> #include <linux/comstats.h> +#include <linux/version.h> #include <linux/istallion.h> #include <linux/ioport.h> #include <linux/delay.h> diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 9be0a51f5..a334205cb 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -158,6 +158,9 @@ static inline unsigned long pgprot_noncached(unsigned long prot) prot = (prot & _CACHEMASK040) | _PAGE_NOCACHE_S; #elif defined(__mips__) prot = (prot & ~_CACHE_MASK) | _CACHE_UNCACHED; +#elif defined(__arm__) && defined(CONFIG_CPU_32) + /* Turn off caching for all I/O areas */ + prot &= ~(L_PTE_CACHEABLE | L_PTE_BUFFERABLE); #endif return prot; diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c index 9027aa67e..0da69c55c 100644 --- a/drivers/char/n_tty.c +++ b/drivers/char/n_tty.c @@ -811,7 +811,7 @@ static int n_tty_open(struct tty_struct *tty) if (!tty->read_buf) { tty->read_buf = (unsigned char *) - get_free_page(in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); + get_zeroed_page(in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); if (!tty->read_buf) return -ENOMEM; } diff --git a/drivers/char/pcxx.c b/drivers/char/pcxx.c index 36db3299d..75f27e830 100644 --- a/drivers/char/pcxx.c +++ b/drivers/char/pcxx.c @@ -1589,8 +1589,8 @@ load_fep: ch->blocked_open = 0; ch->callout_termios = pcxe_callout.init_termios; ch->normal_termios = pcxe_driver.init_termios; - init_waitqueue_head(ch->open_wait); - init_waitqueue_head(ch->close_wait); + init_waitqueue_head(&ch->open_wait); + init_waitqueue_head(&ch->close_wait); ch->asyncflags = 0; } diff --git a/drivers/char/radio-cadet.c b/drivers/char/radio-cadet.c index 239a611b2..98b66f0c2 100644 --- a/drivers/char/radio-cadet.c +++ b/drivers/char/radio-cadet.c @@ -39,6 +39,7 @@ struct timer_list tunertimer,rdstimer,readtimer; static __u8 rdsin=0,rdsout=0,rdsstat=0; static unsigned char rdsbuf[RDS_BUFFER]; static int cadet_lock=0; +static int cadet_probe(void); /* * Signal Strength Threshold Values diff --git a/drivers/char/serial.c b/drivers/char/serial.c index b4bb6865f..38bbf438b 100644 --- a/drivers/char/serial.c +++ b/drivers/char/serial.c @@ -1173,7 +1173,7 @@ static int startup(struct async_struct * info) unsigned short ICP; #endif - page = get_free_page(GFP_KERNEL); + page = get_zeroed_page(GFP_KERNEL); if (!page) return -ENOMEM; @@ -3020,7 +3020,7 @@ static int rs_open(struct tty_struct *tty, struct file * filp) #endif if (!tmp_buf) { - page = get_free_page(GFP_KERNEL); + page = get_zeroed_page(GFP_KERNEL); if (!page) { return -ENOMEM; } @@ -4443,10 +4443,9 @@ static struct console sercons = { /* * Register console. */ -long __init serial_console_init(long kmem_start, long kmem_end) +void __init serial_console_init(void) { register_console(&sercons); - return kmem_start; } #endif diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c index b25be2cd0..aac42d44b 100644 --- a/drivers/char/stallion.c +++ b/drivers/char/stallion.c @@ -28,6 +28,7 @@ #include <linux/config.h> #include <linux/module.h> +#include <linux/version.h> /* for linux/stallion.h */ #include <linux/malloc.h> #include <linux/interrupt.h> #include <linux/tty_flip.h> diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 17711734f..f78171281 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -129,7 +129,7 @@ static int tty_fasync(int fd, struct file * filp, int on); extern int sx_init (void); #endif #ifdef CONFIG_8xx -extern long console_8xx_init(long, long); +extern console_8xx_init(void); extern int rs_8xx_init(void); #endif /* CONFIG_8xx */ @@ -798,7 +798,7 @@ static int init_dev(kdev_t device, struct tty_struct **ret_tty) tp = o_tp = NULL; ltp = o_ltp = NULL; - tty = (struct tty_struct*) get_free_page(GFP_KERNEL); + tty = (struct tty_struct*) get_zeroed_page(GFP_KERNEL); if(!tty) goto fail_no_mem; initialize_tty_struct(tty); @@ -824,7 +824,7 @@ static int init_dev(kdev_t device, struct tty_struct **ret_tty) } if (driver->type == TTY_DRIVER_TYPE_PTY) { - o_tty = (struct tty_struct *) get_free_page(GFP_KERNEL); + o_tty = (struct tty_struct *) get_zeroed_page(GFP_KERNEL); if (!o_tty) goto free_mem_out; initialize_tty_struct(o_tty); @@ -2062,7 +2062,7 @@ int tty_unregister_driver(struct tty_driver *driver) * Just do some early initializations, and do the complex setup * later. */ -long __init console_init(long kmem_start, long kmem_end) +void __init console_init(void) { /* Setup the default TTY line discipline. */ memset(ldiscs, 0, sizeof(ldiscs)); @@ -2085,16 +2085,15 @@ long __init console_init(long kmem_start, long kmem_end) * inform about problems etc.. */ #ifdef CONFIG_VT - kmem_start = con_init(kmem_start); + con_init(); #endif #ifdef CONFIG_SERIAL_CONSOLE #ifdef CONFIG_8xx - kmem_start = console_8xx_init(kmem_start, kmem_end); + console_8xx_init(); #else - kmem_start = serial_console_init(kmem_start, kmem_end); + serial_console_init(); #endif /* CONFIG_8xx */ #endif - return kmem_start; } static struct tty_driver dev_tty_driver, dev_syscons_driver; @@ -2109,7 +2108,7 @@ static struct tty_driver dev_console_driver; * Ok, now we can initialize the rest of the tty devices and can count * on memory allocations, interrupts etc.. */ -int __init tty_init(void) +void __init tty_init(void) { if (sizeof(struct tty_struct) > PAGE_SIZE) panic("size of tty structure > PAGE_SIZE!"); @@ -2220,5 +2219,4 @@ int __init tty_init(void) #ifdef CONFIG_VT vcs_init(); #endif - return 0; } diff --git a/drivers/char/vt.c b/drivers/char/vt.c index 020120d4d..3de97ce21 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -61,7 +61,7 @@ struct vt_struct *vt_cons[MAX_NR_CONSOLES]; */ unsigned char keyboard_type = KB_101; -#if !defined(__alpha__) && !defined(__mips__) +#if !defined(__alpha__) && !defined(__mips__) && !defined(__arm__) asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int on); #endif @@ -89,7 +89,8 @@ unsigned int video_scan_lines; */ #if defined(__i386__) || defined(__alpha__) || defined(__powerpc__) \ - || (defined(__mips__) && !defined(CONFIG_SGI_IP22)) + || (defined(__mips__) && !defined(CONFIG_SGI_IP22)) \ + || (defined(__arm__) && defined(CONFIG_HOST_FOOTBRIDGE)) static void kd_nosound(unsigned long ignored) @@ -470,7 +471,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, ucval = keyboard_type; goto setchar; -#if !defined(__alpha__) && !defined(__mips__) +#if !defined(__alpha__) && !defined(__mips__) && !defined(__arm__) /* * These cannot be implemented on any machine that implements * ioperm() in user level (such as Alpha PCs). diff --git a/drivers/i2o/Config.in b/drivers/i2o/Config.in index 3a0735c25..4cc286578 100644 --- a/drivers/i2o/Config.in +++ b/drivers/i2o/Config.in @@ -5,7 +5,9 @@ tristate 'I2O support' CONFIG_I2O dep_tristate ' I2O PCI support' CONFIG_I2O_PCI $CONFIG_I2O dep_tristate ' I2O Block OSM' CONFIG_I2O_BLOCK $CONFIG_I2O -dep_tristate ' I2O LAN OSM' CONFIG_I2O_LAN $CONFIG_I2O +if [ "$CONFIG_NET" = "y" ]; then + dep_tristate ' I2O LAN OSM' CONFIG_I2O_LAN $CONFIG_I2O +fi dep_tristate ' I2O SCSI OSM' CONFIG_I2O_SCSI $CONFIG_I2O $CONFIG_SCSI dep_tristate ' I2O /proc support' CONFIG_I2O_PROC $CONFIG_I2O diff --git a/drivers/macintosh/macserial.c b/drivers/macintosh/macserial.c index 53590a42a..720932cc9 100644 --- a/drivers/macintosh/macserial.c +++ b/drivers/macintosh/macserial.c @@ -5,6 +5,10 @@ * * Copyright (C) 1996 Paul Mackerras (Paul.Mackerras@cs.anu.edu.au) * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + * + * Receive DMA code by Takashi Oe <toe@unlserve.unl.edu>. + * + * $Id$ */ #include <linux/config.h> @@ -26,6 +30,7 @@ #ifdef CONFIG_SERIAL_CONSOLE #include <linux/console.h> #endif +#include <linux/slab.h> #include <asm/init.h> #include <asm/io.h> @@ -41,6 +46,7 @@ #ifdef CONFIG_KGDB #include <asm/kgdb.h> #endif +#include <asm/dbdma.h> #include "macserial.h" @@ -52,6 +58,8 @@ static struct pmu_sleep_notifier serial_sleep_notifier = { }; #endif +#define SUPPORT_SERIAL_DMA + /* * It would be nice to dynamically allocate everything that * depends on NUM_SERIAL, so we could support any number of @@ -127,6 +135,13 @@ static void change_speed(struct mac_serial *info, struct termios *old); static void rs_wait_until_sent(struct tty_struct *tty, int timeout); static int set_scc_power(struct mac_serial * info, int state); static int setup_scc(struct mac_serial * info); +static void dbdma_reset(volatile struct dbdma_regs *dma); +static void dbdma_flush(volatile struct dbdma_regs *dma); +static void rs_txdma_irq(int irq, void *dev_id, struct pt_regs *regs); +static void rs_rxdma_irq(int irq, void *dev_id, struct pt_regs *regs); +static void dma_init(struct mac_serial * info); +static void rxdma_start(struct mac_serial * info, int current); +static void rxdma_to_tty(struct mac_serial * info); static struct tty_struct *serial_table[NUM_CHANNELS]; static struct termios *serial_termios[NUM_CHANNELS]; @@ -288,6 +303,39 @@ static inline void rs_recv_clear(struct mac_zschannel *zsc) } /* + * Reset a Descriptor-Based DMA channel. + */ +static void dbdma_reset(volatile struct dbdma_regs *dma) +{ + int i; + + out_le32(&dma->control, (WAKE|FLUSH|PAUSE|RUN) << 16); + + /* + * Yes this looks peculiar, but apparently it needs to be this + * way on some machines. (We need to make sure the DBDMA + * engine has actually got the write above and responded + * to it. - paulus) + */ + for (i = 200; i > 0; --i) + if (ld_le32(&dma->control) & RUN) + udelay(1); +} + +/* + * Tells a DBDMA channel to stop and write any buffered data + * it might have to memory. + */ +static _INLINE_ void dbdma_flush(volatile struct dbdma_regs *dma) +{ + int i = 0; + + out_le32(&dma->control, (FLUSH << 16) | FLUSH); + while (((in_le32(&dma->status) & FLUSH) != 0) && (i++ < 100)) + udelay(1); +} + +/* * ---------------------------------------------------------------------- * * Here starts the interrupt handling routines. All of the following @@ -310,6 +358,22 @@ static _INLINE_ void rs_sched_event(struct mac_serial *info, mark_bh(MACSERIAL_BH); } +/* Work out the flag value for a z8530 status value. */ +static _INLINE_ int stat_to_flag(int stat) +{ + int flag; + + if (stat & Rx_OVR) { + flag = TTY_OVERRUN; + } else if (stat & FRM_ERR) { + flag = TTY_FRAME; + } else if (stat & PAR_ERR) { + flag = TTY_PARITY; + } else + flag = 0; + return flag; +} + static _INLINE_ void receive_chars(struct mac_serial *info, struct pt_regs *regs) { @@ -347,14 +411,7 @@ static _INLINE_ void receive_chars(struct mac_serial *info, if (flip_max_cnt < tty->flip.count) flip_max_cnt = tty->flip.count; } - if (stat & Rx_OVR) { - flag = TTY_OVERRUN; - } else if (stat & FRM_ERR) { - flag = TTY_FRAME; - } else if (stat & PAR_ERR) { - flag = TTY_PARITY; - } else - flag = 0; + flag = stat_to_flag(stat); if (flag) /* reset the error indication */ write_zsreg(info->zs_channel, 0, ERR_RES); @@ -450,6 +507,32 @@ static _INLINE_ void status_handle(struct mac_serial *info) info->read_reg_zero = status; } +static _INLINE_ void receive_special_dma(struct mac_serial *info) +{ + unsigned char stat, flag; + volatile struct dbdma_regs *rd = &info->rx->dma; + int where = RX_BUF_SIZE; + + spin_lock(&info->rx_dma_lock); + if ((ld_le32(&rd->status) & ACTIVE) != 0) + dbdma_flush(rd); + if (in_le32(&rd->cmdptr) + == virt_to_bus(info->rx_cmds[info->rx_cbuf] + 1)) + where -= in_le16(&info->rx->res_count); + where--; + + stat = read_zsreg(info->zs_channel, R1); + + flag = stat_to_flag(stat); + if (flag) { + info->rx_flag_buf[info->rx_cbuf][where] = flag; + /* reset the error indication */ + write_zsreg(info->zs_channel, 0, ERR_RES); + } + + spin_unlock(&info->rx_dma_lock); +} + /* * This is the serial driver's generic interrupt routine */ @@ -459,6 +542,12 @@ static void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs) unsigned char zs_intreg; int shift; + if (!(info->flags & ZILOG_INITIALIZED)) { + printk("rs_interrupt: irq %d, port not initialized\n", irq); + disable_irq(irq); + return; + } + /* NOTE: The read register 3, which holds the irq status, * does so for both channels on each chip. Although * the status value itself must be read from the A @@ -475,19 +564,21 @@ static void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs) for (;;) { zs_intreg = read_zsreg(info->zs_chan_a, 3) >> shift; #ifdef SERIAL_DEBUG_INTR - printk("rs_interrupt: irq %d, zs_intreg 0x%x\n", irq, (int)zs_intreg); + printk("rs_interrupt: irq %d, zs_intreg 0x%x\n", + irq, (int)zs_intreg); #endif if ((zs_intreg & CHAN_IRQMASK) == 0) break; - if (!(info->flags & ZILOG_INITIALIZED)) { - printk("rs_interrupt: irq %d, port not initialized\n", irq); - break; + if (zs_intreg & CHBRxIP) { + /* If we are doing DMA, we only ask for interrupts + on characters with errors or special conditions. */ + if (info->dma_initted) + receive_special_dma(info); + else + receive_chars(info, regs); } - - if (zs_intreg & CHBRxIP) - receive_chars(info, regs); if (zs_intreg & CHBTxIP) transmit_chars(info); if (zs_intreg & CHBEXT) @@ -495,6 +586,39 @@ static void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs) } } +/* Transmit DMA interrupt - not used at present */ +static void rs_txdma_irq(int irq, void *dev_id, struct pt_regs *regs) +{ +} + +/* + * Receive DMA interrupt. + */ +static void rs_rxdma_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + struct mac_serial *info = (struct mac_serial *) dev_id; + volatile struct dbdma_cmd *cd; + + if (!info->dma_initted) + return; + spin_lock(&info->rx_dma_lock); + /* First, confirm that this interrupt is, indeed, coming */ + /* from Rx DMA */ + cd = info->rx_cmds[info->rx_cbuf] + 2; + if ((in_le16(&cd->xfer_status) & (RUN | ACTIVE)) != (RUN | ACTIVE)) { + spin_unlock(&info->rx_dma_lock); + return; + } + if (info->rx_fbuf != RX_NO_FBUF) { + info->rx_cbuf = info->rx_fbuf; + if (++info->rx_fbuf == info->rx_nbuf) + info->rx_fbuf = 0; + if (info->rx_fbuf == info->rx_ubuf) + info->rx_fbuf = RX_NO_FBUF; + } + spin_unlock(&info->rx_dma_lock); +} + /* * ------------------------------------------------------------------- * Here ends the serial interrupt routines. @@ -590,10 +714,6 @@ static void do_softint(void *private_) } } -static void rs_timer(void) -{ -} - static int startup(struct mac_serial * info, int can_sleep) { int delay; @@ -629,6 +749,10 @@ static int startup(struct mac_serial * info, int can_sleep) info->flags |= ZILOG_INITIALIZED; enable_irq(info->irq); + if (info->dma_initted) { +// enable_irq(info->tx_dma_irq); + enable_irq(info->rx_dma_irq); + } if (delay) { if (can_sleep) { @@ -642,6 +766,187 @@ static int startup(struct mac_serial * info, int can_sleep) return 0; } +static _INLINE_ void rxdma_start(struct mac_serial * info, int current) +{ + volatile struct dbdma_regs *rd = &info->rx->dma; + volatile struct dbdma_cmd *cd = info->rx_cmds[current]; + +//printk(KERN_DEBUG "SCC: rxdma_start\n"); + + st_le32(&rd->cmdptr, virt_to_bus(cd)); + out_le32(&rd->control, (RUN << 16) | RUN); +} + +static void rxdma_to_tty(struct mac_serial *info) +{ + struct tty_struct *tty = info->tty; + volatile struct dbdma_regs *rd = &info->rx->dma; + unsigned long flags; + int residue, available, space, do_queue; + + if (!tty) + return; + + do_queue = 0; + spin_lock_irqsave(&info->rx_dma_lock, flags); +more: + space = TTY_FLIPBUF_SIZE - tty->flip.count; + if (!space) { + do_queue++; + goto out; + } + residue = 0; + if (info->rx_ubuf == info->rx_cbuf) { + if ((ld_le32(&rd->status) & ACTIVE) != 0) { + dbdma_flush(rd); + if (in_le32(&rd->cmdptr) + == virt_to_bus(info->rx_cmds[info->rx_cbuf]+1)) + residue = in_le16(&info->rx->res_count); + } + } + available = RX_BUF_SIZE - residue - info->rx_done_bytes; + if (available > space) + available = space; + if (available) { + memcpy(tty->flip.char_buf_ptr, + info->rx_char_buf[info->rx_ubuf] + info->rx_done_bytes, + available); + memcpy(tty->flip.flag_buf_ptr, + info->rx_flag_buf[info->rx_ubuf] + info->rx_done_bytes, + available); + tty->flip.char_buf_ptr += available; + tty->flip.count += available; + tty->flip.flag_buf_ptr += available; + memset(info->rx_flag_buf[info->rx_ubuf] + info->rx_done_bytes, + 0, available); + info->rx_done_bytes += available; + do_queue++; + } + if (info->rx_done_bytes == RX_BUF_SIZE) { + volatile struct dbdma_cmd *cd = info->rx_cmds[info->rx_ubuf]; + + if (info->rx_ubuf == info->rx_cbuf) + goto out; + /* mark rx_char_buf[rx_ubuf] free */ + st_le16(&cd->command, DBDMA_NOP); + cd++; + st_le32(&cd->cmd_dep, 0); + st_le32((unsigned int *)&cd->res_count, 0); + cd++; + st_le16(&cd->xfer_status, 0); + + if (info->rx_fbuf == RX_NO_FBUF) { + info->rx_fbuf = info->rx_ubuf; + if (!(ld_le32(&rd->status) & ACTIVE)) { + dbdma_reset(&info->rx->dma); + rxdma_start(info, info->rx_ubuf); + info->rx_cbuf = info->rx_ubuf; + } + } + info->rx_done_bytes = 0; + if (++info->rx_ubuf == info->rx_nbuf) + info->rx_ubuf = 0; + if (info->rx_fbuf == info->rx_ubuf) + info->rx_fbuf = RX_NO_FBUF; + goto more; + } +out: + spin_unlock_irqrestore(&info->rx_dma_lock, flags); + if (do_queue) + queue_task(&tty->flip.tqueue, &tq_timer); +} + +static void poll_rxdma(void *private_) +{ + struct mac_serial *info = (struct mac_serial *) private_; + unsigned long flags; + + rxdma_to_tty(info); + spin_lock_irqsave(&info->rx_dma_lock, flags); + mod_timer(&info->poll_dma_timer, RX_DMA_TIMER); + spin_unlock_irqrestore(&info->rx_dma_lock, flags); +} + +static void dma_init(struct mac_serial * info) +{ + int i, size; + volatile struct dbdma_cmd *cd; + unsigned char *p; + +//printk(KERN_DEBUG "SCC: dma_init\n"); + + info->rx_nbuf = 8; + + /* various mem set up */ + size = sizeof(struct dbdma_cmd) * (3 * info->rx_nbuf + 2) + + (RX_BUF_SIZE * 2 + sizeof(*info->rx_cmds) + + sizeof(*info->rx_char_buf) + sizeof(*info->rx_flag_buf)) + * info->rx_nbuf; + info->dma_priv = kmalloc(size, GFP_KERNEL | GFP_DMA); + if (info->dma_priv == NULL) + return; + memset(info->dma_priv, 0, size); + + info->rx_cmds = (volatile struct dbdma_cmd **)info->dma_priv; + info->rx_char_buf = (unsigned char **) (info->rx_cmds + info->rx_nbuf); + info->rx_flag_buf = info->rx_char_buf + info->rx_nbuf; + p = (unsigned char *) (info->rx_flag_buf + info->rx_nbuf); + for (i = 0; i < info->rx_nbuf; i++, p += RX_BUF_SIZE) + info->rx_char_buf[i] = p; + for (i = 0; i < info->rx_nbuf; i++, p += RX_BUF_SIZE) + info->rx_flag_buf[i] = p; + + /* a bit of DMA programming */ + cd = info->rx_cmds[0] = (volatile struct dbdma_cmd *) DBDMA_ALIGN(p); + st_le16(&cd->command, DBDMA_NOP); + cd++; + st_le16(&cd->req_count, RX_BUF_SIZE); + st_le16(&cd->command, INPUT_MORE); + st_le32(&cd->phy_addr, virt_to_bus(info->rx_char_buf[0])); + cd++; + st_le16(&cd->req_count, 4); + st_le16(&cd->command, STORE_WORD | INTR_ALWAYS); + st_le32(&cd->phy_addr, virt_to_bus(cd-2)); + st_le32(&cd->cmd_dep, DBDMA_STOP); + for (i = 1; i < info->rx_nbuf; i++) { + info->rx_cmds[i] = ++cd; + st_le16(&cd->command, DBDMA_NOP); + cd++; + st_le16(&cd->req_count, RX_BUF_SIZE); + st_le16(&cd->command, INPUT_MORE); + st_le32(&cd->phy_addr, virt_to_bus(info->rx_char_buf[i])); + cd++; + st_le16(&cd->req_count, 4); + st_le16(&cd->command, STORE_WORD | INTR_ALWAYS); + st_le32(&cd->phy_addr, virt_to_bus(cd-2)); + st_le32(&cd->cmd_dep, DBDMA_STOP); + } + cd++; + st_le16(&cd->command, DBDMA_NOP | BR_ALWAYS); + st_le32(&cd->cmd_dep, virt_to_bus(info->rx_cmds[0])); + + /* setup DMA to our liking */ + dbdma_reset(&info->rx->dma); + st_le32(&info->rx->dma.intr_sel, 0x10001); + st_le32(&info->rx->dma.br_sel, 0x10001); + out_le32(&info->rx->dma.wait_sel, 0x10001); + + /* set various flags */ + info->rx_ubuf = 0; + info->rx_cbuf = 0; + info->rx_fbuf = info->rx_ubuf + 1; + if (info->rx_fbuf == info->rx_nbuf) + info->rx_fbuf = RX_NO_FBUF; + info->rx_done_bytes = 0; + + /* setup polling */ + init_timer(&info->poll_dma_timer); + info->poll_dma_timer.function = (void *)&poll_rxdma; + info->poll_dma_timer.data = (unsigned long)info; + + info->dma_initted = 1; +} + static int setup_scc(struct mac_serial * info) { unsigned long flags; @@ -667,6 +972,12 @@ static int setup_scc(struct mac_serial * info) info->xmit_fifo_size = 1; /* + * Reset DMAs + */ + if (info->has_dma) + dma_init(info); + + /* * Clear the interrupt registers. */ write_zsreg(info->zs_channel, 0, ERR_RES); @@ -680,7 +991,23 @@ static int setup_scc(struct mac_serial * info) /* * Finally, enable sequencing and interrupts */ - info->curregs[1] = (info->curregs[1] & ~0x18) | (EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB); + if (!info->dma_initted) { + /* interrupt on ext/status changes, all received chars, + transmit ready */ + info->curregs[1] = (info->curregs[1] & ~0x18) + | (EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB); + } else { + /* interrupt on ext/status changes, W/Req pin is + receive DMA request */ + info->curregs[1] = (info->curregs[1] & ~(0x18 | TxINT_ENAB)) + | (EXT_INT_ENAB | WT_RDY_RT | WT_FN_RDYFN); + write_zsreg(info->zs_channel, 1, info->curregs[1]); + /* enable W/Req pin */ + info->curregs[1] |= WT_RDY_ENAB; + write_zsreg(info->zs_channel, 1, info->curregs[1]); + /* enable interrupts on transmit ready and receive errors */ + info->curregs[1] |= INT_ERR_Rx | TxINT_ENAB; + } info->pendregs[1] = info->curregs[1]; info->curregs[3] |= (RxENABLE | Rx8); info->pendregs[3] = info->curregs[3]; @@ -706,6 +1033,14 @@ static int setup_scc(struct mac_serial * info) restore_flags(flags); + if (info->dma_initted) { + spin_lock_irqsave(&info->rx_dma_lock, flags); + rxdma_start(info, 0); + info->poll_dma_timer.expires = RX_DMA_TIMER; + add_timer(&info->poll_dma_timer); + spin_unlock_irqrestore(&info->rx_dma_lock, flags); + } + return 0; } @@ -727,7 +1062,14 @@ static void shutdown(struct mac_serial * info) return; } - + + if (info->has_dma) { + del_timer(&info->poll_dma_timer); + dbdma_reset(info->tx_dma); + dbdma_reset(&info->rx->dma); + disable_irq(info->tx_dma_irq); + disable_irq(info->rx_dma_irq); + } disable_irq(info->irq); info->pendregs[1] = info->curregs[1] = 0; @@ -753,6 +1095,12 @@ static void shutdown(struct mac_serial * info) info->xmit_buf = 0; } + if (info->has_dma && info->dma_priv) { + kfree(info->dma_priv); + info->dma_priv = NULL; + info->dma_initted = 0; + } + memset(info->curregs, 0, sizeof(info->curregs)); memset(info->curregs, 0, sizeof(info->pendregs)); @@ -795,7 +1143,7 @@ static int set_scc_power(struct mac_serial * info, int state) feature_set(info->dev_node, FEATURE_Modem_Reset); mdelay(5); feature_clear(info->dev_node, FEATURE_Modem_Reset); - delay = 1000; /* wait for 1s before using */ + delay = 2500; /* wait for 2.5s before using */ } #ifdef CONFIG_PMAC_PBOOK if (info->is_pwbk_ir) @@ -1050,7 +1398,6 @@ static int rs_write(struct tty_struct * tty, int from_user, if (!tty || !info->xmit_buf || !tmp_buf) return 0; - save_flags(flags); if (from_user) { down(&tmp_buf_sem); while (1) { @@ -1066,6 +1413,7 @@ static int rs_write(struct tty_struct * tty, int from_user, ret = -EFAULT; break; } + save_flags(flags); cli(); c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, SERIAL_XMIT_SIZE - info->xmit_head)); @@ -1081,6 +1429,7 @@ static int rs_write(struct tty_struct * tty, int from_user, up(&tmp_buf_sem); } else { while (1) { + save_flags(flags); cli(); c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, @@ -1102,7 +1451,6 @@ static int rs_write(struct tty_struct * tty, int from_user, if (info->xmit_cnt && !tty->stopped && !info->tx_stopped && !info->tx_active) transmit_chars(info); - restore_flags(flags); return ret; } @@ -1131,12 +1479,13 @@ static int rs_chars_in_buffer(struct tty_struct *tty) static void rs_flush_buffer(struct tty_struct *tty) { struct mac_serial *info = (struct mac_serial *)tty->driver_data; + unsigned long flags; if (serial_paranoia_check(info, tty->device, "rs_flush_buffer")) return; - cli(); + save_flags(flags); cli(); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - sti(); + restore_flags(flags); wake_up_interruptible(&tty->write_wait); if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) @@ -1156,7 +1505,6 @@ static void rs_throttle(struct tty_struct * tty) struct mac_serial *info = (struct mac_serial *)tty->driver_data; unsigned long flags; #ifdef SERIAL_DEBUG_THROTTLE - char buf[64]; printk("throttle %ld....\n",tty->ldisc.chars_in_buffer(tty)); #endif @@ -1193,7 +1541,6 @@ static void rs_unthrottle(struct tty_struct * tty) struct mac_serial *info = (struct mac_serial *)tty->driver_data; unsigned long flags; #ifdef SERIAL_DEBUG_THROTTLE - char buf[64]; printk("unthrottle %s: %d....\n",tty->ldisc.chars_in_buffer(tty)); #endif @@ -1471,6 +1818,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp) save_flags(flags); cli(); if (tty_hung_up_p(filp)) { + MOD_DEC_USE_COUNT; restore_flags(flags); return; } @@ -1496,6 +1844,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp) info->count = 0; } if (info->count) { + MOD_DEC_USE_COUNT; restore_flags(flags); return; } @@ -1516,8 +1865,12 @@ static void rs_close(struct tty_struct *tty, struct file * filp) printk("waiting end of Tx... (timeout:%d)\n", info->closing_wait); #endif tty->closing = 1; - if (info->closing_wait != ZILOG_CLOSING_WAIT_NONE) + if (info->closing_wait != ZILOG_CLOSING_WAIT_NONE) { + restore_flags(flags); tty_wait_until_sent(tty, info->closing_wait); + save_flags(flags); cli(); + } + /* * At this point we stop accepting input. To do this, we * disable the receiver and receive interrupts. @@ -1537,7 +1890,9 @@ static void rs_close(struct tty_struct *tty, struct file * filp) #ifdef SERIAL_DEBUG_OPEN printk("waiting end of Rx...\n"); #endif + restore_flags(flags); rs_wait_until_sent(tty, info->timeout); + save_flags(flags); cli(); } shutdown(info); @@ -1563,6 +1918,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp) info->flags &= ~(ZILOG_NORMAL_ACTIVE|ZILOG_CALLOUT_ACTIVE| ZILOG_CLOSING); wake_up_interruptible(&info->close_wait); + MOD_DEC_USE_COUNT; } /* @@ -1772,14 +2128,19 @@ static int rs_open(struct tty_struct *tty, struct file * filp) int retval, line; unsigned long page; + MOD_INC_USE_COUNT; line = MINOR(tty->device) - tty->driver.minor_start; - if ((line < 0) || (line >= zs_channels_found)) + if ((line < 0) || (line >= zs_channels_found)) { + MOD_DEC_USE_COUNT; return -ENODEV; + } info = zs_soft + line; #ifdef CONFIG_KGDB - if (info->kgdb_channel) + if (info->kgdb_channel) { + MOD_DEC_USE_COUNT; return -ENODEV; + } #endif if (serial_paranoia_check(info, tty->device, "rs_open")) return -ENODEV; @@ -1862,7 +2223,58 @@ static int rs_open(struct tty_struct *tty, struct file * filp) static void show_serial_version(void) { - printk("PowerMac Z8530 serial driver version 1.01\n"); + printk("PowerMac Z8530 serial driver version 2.0\n"); +} + +/* + * Initialize one channel, both the mac_serial and mac_zschannel + * structs. We use the dev_node field of the mac_serial struct. + */ +static void +chan_init(struct mac_serial *zss, struct mac_zschannel *zs_chan, + struct mac_zschannel *zs_chan_a) +{ + struct device_node *ch = zss->dev_node; + char *conn; + int len; + + zss->irq = ch->intrs[0].line; + zss->has_dma = 0; +#if !defined(CONFIG_KGDB) && defined(SUPPORT_SERIAL_DMA) + if (ch->n_addrs == 3 && ch->n_intrs == 3) + zss->has_dma = 1; +#endif + zss->dma_initted = 0; + + zs_chan->control = (volatile unsigned char *) + ioremap(ch->addrs[0].address, 0x1000); + zs_chan->data = zs_chan->control + 0x10; + spin_lock_init(&zs_chan->lock); + zs_chan->parent = zss; + zss->zs_channel = zs_chan; + zss->zs_chan_a = zs_chan_a; + + /* setup misc varariables */ + zss->kgdb_channel = 0; + zss->is_cobalt_modem = device_is_compatible(ch, "cobalt"); + + /* XXX tested only with wallstreet PowerBook, + should do no harm anyway */ + conn = get_property(ch, "AAPL,connector", &len); + zss->is_pwbk_ir = conn && (strcmp(conn, "infrared") == 0); + + if (zss->has_dma) { + zss->dma_priv = NULL; + /* it seems that the last two addresses are the + DMA controllers */ + zss->tx_dma = (volatile struct dbdma_regs *) + ioremap(ch->addrs[ch->n_addrs - 2].address, 0x100); + zss->rx = (volatile struct mac_dma *) + ioremap(ch->addrs[ch->n_addrs - 1].address, 0x100); + zss->tx_dma_irq = ch->intrs[1].line; + zss->rx_dma_irq = ch->intrs[2].line; + spin_lock_init(&zss->rx_dma_lock); + } } /* Ask the PROM how many Z8530s we have and initialize their zs_channels */ @@ -1871,51 +2283,63 @@ probe_sccs() { struct device_node *dev, *ch; struct mac_serial **pp; - int n, lenp; - char *conn; + int n, chip, nchan; + struct mac_zschannel *zs_chan; + int chan_a_index; n = 0; pp = &zs_chain; + zs_chan = zs_channels; for (dev = find_devices("escc"); dev != 0; dev = dev->next) { + nchan = 0; + chip = n; if (n >= NUM_CHANNELS) { printk("Sorry, can't use %s: no more channels\n", dev->full_name); continue; } + chan_a_index = 0; for (ch = dev->child; ch != 0; ch = ch->sibling) { + if (nchan >= 2) { + printk(KERN_WARNING "SCC: Only 2 channels per " + "chip are supported\n"); + break; + } if (ch->n_addrs < 1 || (ch ->n_intrs < 1)) { printk("Can't use %s: %d addrs %d intrs\n", ch->full_name, ch->n_addrs, ch->n_intrs); continue; } - zs_channels[n].control = (volatile unsigned char *) - ioremap(ch->addrs[0].address, 0x1000); - zs_channels[n].data = zs_channels[n].control + 0x10; - spin_lock_init(&zs_channels[n].lock); - zs_soft[n].zs_channel = &zs_channels[n]; - zs_soft[n].dev_node = ch; - zs_soft[n].irq = ch->intrs[0].line; - zs_soft[n].zs_channel->parent = &zs_soft[n]; - zs_soft[n].is_cobalt_modem = device_is_compatible(ch, "cobalt"); - - /* XXX tested only with wallstreet PowerBook, - should do no harm anyway */ - conn = get_property(ch, "AAPL,connector", &lenp); - zs_soft[n].is_pwbk_ir = - conn && (strcmp(conn, "infrared") == 0); - - /* XXX this assumes the prom puts chan A before B */ - if (n & 1) - zs_soft[n].zs_chan_a = &zs_channels[n-1]; - else - zs_soft[n].zs_chan_a = &zs_channels[n]; + /* The channel with the higher address + will be the A side. */ + if (nchan > 0 && + ch->addrs[0].address + > zs_soft[n-1].dev_node->addrs[0].address) + chan_a_index = 1; + + /* minimal initialization for now */ + zs_soft[n].dev_node = ch; *pp = &zs_soft[n]; pp = &zs_soft[n].zs_next; + ++nchan; ++n; } + if (nchan == 0) + continue; + + /* set up A side */ + chan_init(&zs_soft[chip + chan_a_index], zs_chan, zs_chan); + ++zs_chan; + + /* set up B side, if it exists */ + if (nchan > 1) + chan_init(&zs_soft[chip + 1 - chan_a_index], + zs_chan, zs_chan - 1); + ++zs_chan; } *pp = 0; + zs_channels_found = n; #ifdef CONFIG_PMAC_PBOOK if (n) @@ -1932,8 +2356,6 @@ int macserial_init(void) /* Setup base handler, and timer table. */ init_bh(MACSERIAL_BH, do_serial_bh); - timer_table[RS_TIMER].fn = rs_timer; - timer_table[RS_TIMER].expires = 0; /* Find out how many Z8530 SCCs we have */ if (zs_chain == 0) @@ -1945,6 +2367,18 @@ int macserial_init(void) /* Register the interrupt handler for each one */ save_flags(flags); cli(); for (i = 0; i < zs_channels_found; ++i) { + if (zs_soft[i].has_dma) { + if (request_irq(zs_soft[i].tx_dma_irq, rs_txdma_irq, 0, + "SCC-txdma", &zs_soft[i])) + printk(KERN_ERR "macserial: can't get irq %d\n", + zs_soft[i].tx_dma_irq); + disable_irq(zs_soft[i].tx_dma_irq); + if (request_irq(zs_soft[i].rx_dma_irq, rs_rxdma_irq, 0, + "SCC-rxdma", &zs_soft[i])) + printk(KERN_ERR "macserial: can't get irq %d\n", + zs_soft[i].rx_dma_irq); + disable_irq(zs_soft[i].rx_dma_irq); + } if (request_irq(zs_soft[i].irq, rs_interrupt, 0, "SCC", &zs_soft[i])) printk(KERN_ERR "macserial: can't get irq %d\n", @@ -2083,6 +2517,7 @@ int macserial_init(void) /* By default, disable the port */ set_scc_power(info, 0); } + tmp_buf = 0; return 0; } @@ -2103,11 +2538,26 @@ void cleanup_module(void) for (info = zs_chain, i = 0; info; info = info->zs_next, i++) set_scc_power(info, 0); save_flags(flags); cli(); - for (i = 0; i < zs_channels_found; ++i) + for (i = 0; i < zs_channels_found; ++i) { free_irq(zs_soft[i].irq, &zs_soft[i]); + if (zs_soft[i].has_dma) { + free_irq(zs_soft[i].tx_dma_irq, &zs_soft[i]); + free_irq(zs_soft[i].rx_dma_irq, &zs_soft[i]); + } + } restore_flags(flags); tty_unregister_driver(&callout_driver); tty_unregister_driver(&serial_driver); + + if (tmp_buf) { + free_page((unsigned long) tmp_buf); + tmp_buf = 0; + } + +#ifdef CONFIG_PMAC_PBOOK + if (zs_channels_found) + pmu_unregister_sleep_notifier(&serial_sleep_notifier); +#endif /* CONFIG_PMAC_PBOOK */ } #endif /* MODULE */ @@ -2224,6 +2674,8 @@ static int __init serial_console_setup(struct console *co, char *options) if (zs_chain == 0) return -1; + set_scc_power(info, 1); + /* Reset the channel */ write_zsreg(info->zs_channel, R9, CHRA); @@ -2467,14 +2919,13 @@ void __init zs_kgdb_hook(int tty_num) if (zs_chain == 0) probe_sccs(); - set_scc_power(&zs_soft[n], 1); + set_scc_power(&zs_soft[tty_num], 1); zs_kgdbchan = zs_soft[tty_num].zs_channel; zs_soft[tty_num].change_needed = 0; zs_soft[tty_num].clk_divisor = 16; zs_soft[tty_num].zs_baud = 38400; zs_soft[tty_num].kgdb_channel = 1; /* This runs kgdb */ - zs_soft[tty_num ^ 1].kgdb_channel = 0; /* This does not */ /* Turn on transmitter/receiver at 8-bits/char */ kgdb_chaninit(zs_soft[tty_num].zs_channel, 1, 38400); diff --git a/drivers/macintosh/macserial.h b/drivers/macintosh/macserial.h index e2f137f73..3ba2e6062 100644 --- a/drivers/macintosh/macserial.h +++ b/drivers/macintosh/macserial.h @@ -92,6 +92,13 @@ struct mac_zschannel { struct mac_serial* parent; }; +struct mac_dma { + volatile struct dbdma_regs dma; + volatile unsigned short res_count; + volatile unsigned short command; + volatile unsigned int buf_addr; +}; + struct mac_serial { struct mac_serial *zs_next; /* For IRQ servicing chain */ struct mac_zschannel *zs_channel; /* Channel registers */ @@ -156,6 +163,28 @@ struct mac_serial { struct termios callout_termios; wait_queue_head_t open_wait; wait_queue_head_t close_wait; + + volatile struct dbdma_regs *tx_dma; + int tx_dma_irq; + volatile struct dbdma_cmd *tx_cmds; + volatile struct mac_dma *rx; + int rx_dma_irq; + volatile struct dbdma_cmd **rx_cmds; + unsigned char **rx_char_buf; + unsigned char **rx_flag_buf; +#define RX_BUF_SIZE 256 + int rx_nbuf; + int rx_done_bytes; + int rx_ubuf; + int rx_fbuf; +#define RX_NO_FBUF (-1) + int rx_cbuf; + spinlock_t rx_dma_lock; + int has_dma; + int dma_initted; + void *dma_priv; + struct timer_list poll_dma_timer; +#define RX_DMA_TIMER (jiffies + 10*HZ/1000) }; @@ -226,9 +255,9 @@ struct mac_serial { #define INT_ALL_Rx 0x10 /* Int on all Rx Characters or error */ #define INT_ERR_Rx 0x18 /* Int on error only */ -#define WT_RDY_RT 0x20 /* Wait/Ready on R/T */ -#define WT_FN_RDYFN 0x40 /* Wait/FN/Ready FN */ -#define WT_RDY_ENAB 0x80 /* Wait/Ready Enable */ +#define WT_RDY_RT 0x20 /* W/Req reflects recv if 1, xmit if 0 */ +#define WT_FN_RDYFN 0x40 /* W/Req pin is DMA request if 1, wait if 0 */ +#define WT_RDY_ENAB 0x80 /* Enable W/Req pin */ /* Write Register #2 (Interrupt Vector) */ @@ -286,6 +315,9 @@ struct mac_serial { /* Write Register 7 (Sync bits 8-15/SDLC 01111110) */ +/* Write Register 7' (Some enhanced feature control) */ +#define ENEXREAD 0x40 /* Enable read of some write registers */ + /* Write Register 8 (transmit buffer) */ /* Write Register 9 (Master interrupt control) */ @@ -346,7 +378,9 @@ struct mac_serial { #define SNRZI 0xe0 /* Set NRZI mode */ /* Write Register 15 (external/status interrupt control) */ +#define EN85C30 1 /* Enable some 85c30-enhanced registers */ #define ZCIE 2 /* Zero count IE */ +#define ENSTFIFO 4 /* Enable status FIFO (SDLC) */ #define DCDIE 8 /* DCD IE */ #define SYNCIE 0x10 /* Sync/hunt IE */ #define CTSIE 0x20 /* CTS IE */ @@ -382,6 +416,15 @@ struct mac_serial { #define END_FR 0x80 /* End of Frame (SDLC) */ /* Read Register 2 (channel b only) - Interrupt vector */ +#define CHB_Tx_EMPTY 0x00 +#define CHB_EXT_STAT 0x02 +#define CHB_Rx_AVAIL 0x04 +#define CHB_SPECIAL 0x06 +#define CHA_Tx_EMPTY 0x08 +#define CHA_EXT_STAT 0x0a +#define CHA_Rx_AVAIL 0x0c +#define CHA_SPECIAL 0x0e +#define STATUS_MASK 0x06 /* Read Register 3 (interrupt pending register) ch a only */ #define CHBEXT 0x1 /* Channel B Ext/Stat IP */ diff --git a/drivers/misc/acpi.c b/drivers/misc/acpi.c index e6878dde6..03c39fb61 100644 --- a/drivers/misc/acpi.c +++ b/drivers/misc/acpi.c @@ -246,18 +246,18 @@ static int __init acpi_map_tables(void) struct acpi_table *rsdt; u32 *rsdt_entry; int rsdt_entry_count; - u8 *i; + unsigned long i; // search BIOS memory for RSDP for (i = ACPI_BIOS_ROM_BASE; i < ACPI_BIOS_ROM_END; i += 16) { - rsdp = (struct acpi_rsdp *) i; - if (readl(rsdp->signature) == ACPI_RSDP1_SIG - && readl(rsdp->signature + 1) == ACPI_RSDP2_SIG) { + rsdp = (struct acpi_rsdp *) phys_to_virt(i); + if (rsdp->signature[0] == ACPI_RSDP1_SIG && + rsdp->signature[1] == ACPI_RSDP2_SIG) { char oem[7]; int j; // strip trailing space and print OEM identifier - memcpy_fromio(oem, rsdp->oem, 6); + memcpy(oem, rsdp->oem, 6); oem[6] = '\0'; for (j = 5; j > 0 && (oem[j] == '\0' || oem[j] == ' '); @@ -275,7 +275,7 @@ static int __init acpi_map_tables(void) return -ENODEV; } // fetch RSDT from RSDP - rsdt = acpi_map_table(readl(&rsdp->rsdt)); + rsdt = acpi_map_table(rsdp->rsdt); if (!rsdt || rsdt->signature != ACPI_RSDT_SIG) { printk(KERN_ERR "ACPI: no RSDT found\n"); acpi_unmap_table(rsdt); diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 336d081b3..fe9208d4d 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -27,10 +27,10 @@ CONFIG_82596_MODULE := ifeq ($(CONFIG_PCMCIA),y) SUB_DIRS += pcmcia - MOD_SUB_DIRS += pcmcia + MOD_IN_SUB_DIRS += pcmcia else ifeq ($(CONFIG_PCMCIA),m) - MOD_SUB_DIRS += pcmcia + MOD_IN_SUB_DIRS += pcmcia endif endif @@ -829,14 +829,6 @@ else endif endif -ifeq ($(CONFIG_PCMCIA_PCNET),y) -CONFIG_8390_BUILTIN = y -else - ifeq ($(CONFIG_PCMCIA_PCNET),m) - CONFIG_8390_MODULE = y - endif -endif - # If anything built-in uses the 8390, then build it into the kernel also. # If not, but a module uses it, build as a module. ifdef CONFIG_8390_BUILTIN diff --git a/drivers/net/eepro100.c b/drivers/net/eepro100.c index e1a4a3c91..aac698b0d 100644 --- a/drivers/net/eepro100.c +++ b/drivers/net/eepro100.c @@ -1495,7 +1495,7 @@ speedo_rx(struct net_device *dev) rxf = sp->rx_ringp[entry] = (struct RxFD *)skb->tail; skb->dev = dev; skb_reserve(skb, sizeof(struct RxFD)); - rxf->rx_buf_addr = virt_to_le32bus(skb->tail); + rxf->rx_buf_addr = virt_to_bus(skb->tail); } else { rxf = sp->rx_ringp[entry]; } diff --git a/drivers/net/hamradio/baycom_par.c b/drivers/net/hamradio/baycom_par.c index 9ba70b255..0bd2a1725 100644 --- a/drivers/net/hamradio/baycom_par.c +++ b/drivers/net/hamradio/baycom_par.c @@ -572,7 +572,7 @@ module_exit(cleanup_baycompar); static int __init baycom_par_setup(char *str) { - static unsigned __initdata nr_dev = 0; + static unsigned nr_dev = 0; int ints[2]; if (nr_dev >= NR_PORTS) diff --git a/drivers/net/hamradio/soundmodem/sm.c b/drivers/net/hamradio/soundmodem/sm.c index 99d9061f9..6fa4aca6b 100644 --- a/drivers/net/hamradio/soundmodem/sm.c +++ b/drivers/net/hamradio/soundmodem/sm.c @@ -726,7 +726,7 @@ module_exit(cleanup_soundmodem); static int __init sm_setup(char *str) { - static unsigned __initdata nr_dev = 0; + static unsigned nr_dev = 0; int ints[8]; if (nr_dev >= NR_PORTS) diff --git a/drivers/net/irda/w83977af_ir.c b/drivers/net/irda/w83977af_ir.c index 0885e74a3..32e965275 100644 --- a/drivers/net/irda/w83977af_ir.c +++ b/drivers/net/irda/w83977af_ir.c @@ -10,16 +10,16 @@ * Modified by: Dag Brattli <dagb@cs.uit.no> * * Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no> - * Copyright (c) 1998 Corel Computer Corp. + * Copyright (c) 1998-1999 Rebel.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. * - * Neither Paul VanderSpek nor Corel Computer Corp. admit liability - * nor provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. + * Neither Paul VanderSpek nor Rebel.com admit liability nor provide + * warranty for any of this software. This material is provided "AS-IS" + * and at no charge. * * If you find bugs in this file, its very likely that the same bug * will also be in pc87108.c since the implementations is quite diff --git a/drivers/net/pcmcia/3c574_cs.c b/drivers/net/pcmcia/3c574_cs.c new file mode 100644 index 000000000..40c53a2cf --- /dev/null +++ b/drivers/net/pcmcia/3c574_cs.c @@ -0,0 +1,1494 @@ +/* 3c574.c: A PCMCIA ethernet driver for the 3com 3c574 "RoadRunner". + + Written 1993-1998 by + Donald Becker, becker@cesdis.gsfc.nasa.gov, (driver core) and + David Hinds, dhinds@allegro.stanford.edu (derived from his PC card code). + + This software may be used and distributed according to the terms of + the GNU Public License, incorporated herein by reference. + + This driver derives from Donald Becker's 3c509 core, which has the + following copyright: + Copyright 1993 United States Government as represented by the + Director, National Security Agency. + +*/ + +/* Driver author info must always be in the binary. Version too.. */ +static const char *tc574_version = +"3c574_cs.c v1.08 9/24/98 Donald Becker/David Hinds, becker@cesdis.gsfc.nasa.gov.\n"; + +/* + Theory of Operation + +I. Board Compatibility + +This device driver is designed for the 3Com 3c574 PC card Fast Ethernet +Adapter. + +II. Board-specific settings + +None -- PC cards are autoconfigured. + +III. Driver operation + +The 3c574 uses a Boomerang-style interface, without the bus-master capability. +See the Boomerang driver and documentation for most details. + +IV. Notes and chip documentation. + +Two added registers are used to enhance PIO performance, RunnerRdCtrl and +RunnerWrCtrl. These are 11 bit down-counters that are preloaded with the +count of word (16 bits) reads or writes the driver is about to do to the Rx +or Tx FIFO. The chip is then able to hide the internal-PCI-bus to PC-card +translation latency by buffering the I/O operations with an 8 word FIFO. +Note: No other chip accesses are permitted when this buffer is used. + +A second enhancement is that both attribute and common memory space +0x0800-0x0fff can translated to the PIO FIFO. Thus memory operations (faster +with *some* PCcard bridges) may be used instead of I/O operations. +This is enabled by setting the 0x10 bit in the PCMCIA LAN COR. + +Some slow PC card bridges work better if they never see a WAIT signal. +This is configured by setting the 0x20 bit in the PCMCIA LAN COR. +Only do this after testing that it is reliable and improves performance. + +The upper five bits of RunnerRdCtrl are used to window into PCcard +configuration space registers. Window 0 is the regular Boomerang/Odie +register set, 1-5 are various PC card control registers, and 16-31 are +the (reversed!) CIS table. + +A final note: writing the InternalConfig register in window 3 with an +invalid ramWidth is Very Bad. + +V. References + +http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html +http://www.national.com/pf/DP/DP83840.html + +Thanks to Terry Murphy of 3Com for providing development information for +earlier 3Com products. + +*/ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/malloc.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/interrupt.h> +#include <linux/in.h> +#include <linux/delay.h> +#include <asm/io.h> +#include <asm/system.h> +#include <asm/bitops.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/if_arp.h> +#include <linux/ioport.h> + +#include <pcmcia/version.h> +#include <pcmcia/cs_types.h> +#include <pcmcia/cs.h> +#include <pcmcia/cistpl.h> +#include <pcmcia/cisreg.h> +#include <pcmcia/ciscode.h> +#include <pcmcia/ds.h> +#include <pcmcia/mem_op.h> + +/* A few values that may be tweaked. */ +MODULE_PARM(irq_mask, "i"); +MODULE_PARM(irq_list, "1-4i"); +MODULE_PARM(max_interrupt_work, "i"); +MODULE_PARM(full_duplex, "i"); +#ifdef BROKEN_FEATURES +MODULE_PARM(use_fifo_buffer, "i"); +MODULE_PARM(use_memory_ops, "i"); +MODULE_PARM(no_wait, "i"); +#endif + +/* Now-standard PC card module parameters. */ +static u_int irq_mask = 0xdeb8; /* IRQ3,4,5,7,9,10,11,12,14,15 */ +static int irq_list[4] = { -1 }; + +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT ((800*HZ)/1000) + +/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ +static int max_interrupt_work = 64; + +/* Force full duplex modes? */ +static int full_duplex = 0; + +#ifdef BROKEN_FEATURES +/* Performance features: best left disabled. */ +/* Set to buffer all Tx/RxFIFO accesses. */ +static int use_fifo_buffer = 0; +/* Set iff memory ops are faster than I/O ops. */ +static int use_memory_ops = 0; +/* Set iff disabling the WAIT signal is reliable and faster. */ +static int no_wait = 0; +#endif + +/* To minimize the size of the driver source and make the driver more + readable not all constants are symbolically defined. + You'll need the manual if you want to understand driver details anyway. */ +/* Offsets from base I/O address. */ +#define EL3_DATA 0x00 +#define EL3_CMD 0x0e +#define EL3_STATUS 0x0e + +#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD) + +/* The top five bits written to EL3_CMD are a command, the lower + 11 bits are the parameter, if applicable. */ +enum el3_cmds { + TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11, + RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, RxDiscard = 8<<11, + TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11, + FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrEnb = 14<<11, + SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11, + SetTxThreshold = 18<<11, SetTxStart = 19<<11, StatsEnable = 21<<11, + StatsDisable = 22<<11, StopCoax = 23<<11, +}; + +enum elxl_status { + IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004, + TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020, + IntReq = 0x0040, StatsFull = 0x0080, CmdBusy = 0x1000 }; + +/* The SetRxFilter command accepts the following classes: */ +enum RxFilter { + RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8 +}; + +enum Window0 { + Wn0EepromCmd = 10, Wn0EepromData = 12, /* EEPROM command/address, data. */ + IntrStatus=0x0E, /* Valid in all windows. */ +}; +/* These assumes the larger EEPROM. */ +enum Win0_EEPROM_cmds { + EEPROM_Read = 0x200, EEPROM_WRITE = 0x100, EEPROM_ERASE = 0x300, + EEPROM_EWENB = 0x30, /* Enable erasing/writing for 10 msec. */ + EEPROM_EWDIS = 0x00, /* Disable EWENB before 10 msec timeout. */ +}; + +/* Register window 1 offsets, the window used in normal operation. + On the "Odie" this window is always mapped at offsets 0x10-0x1f. + Except for TxFree, which is overlapped by RunnerWrCtrl. */ +enum Window1 { + TX_FIFO = 0x10, RX_FIFO = 0x10, RxErrors = 0x14, + RxStatus = 0x18, Timer=0x1A, TxStatus = 0x1B, + TxFree = 0x0C, /* Remaining free bytes in Tx buffer. */ + RunnerRdCtrl = 0x16, RunnerWrCtrl = 0x1c, +}; + +enum Window3 { /* Window 3: MAC/config bits. */ + Wn3_Config=0, Wn3_MAC_Ctrl=6, Wn3_Options=8, +}; +union wn3_config { + int i; + struct w3_config_fields { + unsigned int ram_size:3, ram_width:1, ram_speed:2, rom_size:2; + int pad8:8; + unsigned int ram_split:2, pad18:2, xcvr:3, pad21:1, autoselect:1; + int pad24:7; + } u; +}; + +enum Window4 { /* Window 4: Xcvr/media bits. */ + Wn4_FIFODiag = 4, Wn4_NetDiag = 6, Wn4_PhysicalMgmt=8, Wn4_Media = 10, +}; + +#define MEDIA_TP 0x00C0 /* Enable link beat and jabber for 10baseT. */ + +struct el3_private { + dev_node_t node; + struct net_device_stats stats; + u16 advertising, partner; /* NWay media advertisement */ + unsigned char phys[2]; /* MII device addresses. */ + unsigned int + autoselect:1, default_media:3; /* Read from the EEPROM/Wn3_Config. */ + /* for transceiver monitoring */ + struct timer_list media; + u_short media_status; + u_short fast_poll; + u_long last_irq; +}; + +/* Set iff a MII transceiver on any interface requires mdio preamble. + This only set with the original DP83840 on older 3c905 boards, so the extra + code size of a per-interface flag is not worthwhile. */ +static char mii_preamble_required = 0; + +#ifdef PCMCIA_DEBUG +static int pc_debug = PCMCIA_DEBUG; +MODULE_PARM(pc_debug, "i"); +#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) +static char *version = +"3c574_cs.c 1.000 1998/1/8 Donald Becker, becker@cesdis.gsfc.nasa.gov.\n"; +#else +#define DEBUG(n, args...) +#endif + +/* Index of functions. */ + +static void tc574_config(dev_link_t *link); +static void tc574_release(u_long arg); +static int tc574_event(event_t event, int priority, + event_callback_args_t *args); + +static void mdio_sync(ioaddr_t ioaddr, int bits); +static int mdio_read(ioaddr_t ioaddr, int phy_id, int location); +static void mdio_write(ioaddr_t ioaddr, int phy_id, int location, int value); +static u_short read_eeprom(ioaddr_t ioaddr, int index); +static void wait_for_completion(struct net_device *dev, int cmd); + +static void tc574_reset(struct net_device *dev); +static void media_check(u_long arg); +static int el3_open(struct net_device *dev); +static int el3_start_xmit(struct sk_buff *skb, struct net_device *dev); +static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static void update_stats(ioaddr_t addr, struct net_device *dev); +static struct net_device_stats *el3_get_stats(struct net_device *dev); +static int el3_rx(struct net_device *dev, int worklimit); +static int el3_close(struct net_device *dev); +#ifdef HAVE_PRIVATE_IOCTL +static int private_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +#endif +static void set_rx_mode(struct net_device *dev); + +static dev_info_t dev_info = "3c574_cs"; + +static dev_link_t *tc574_attach(void); +static void tc574_detach(dev_link_t *); + +static dev_link_t *dev_list = NULL; + +static void flush_stale_links(void) +{ + dev_link_t *link, *next; + for (link = dev_list; link; link = next) { + next = link->next; + if (link->state & DEV_STALE_LINK) + tc574_detach(link); + } +} + +static void cs_error(client_handle_t handle, int func, int ret) +{ +#if CS_RELEASE_CODE < 0x2911 + CardServices(ReportError, dev_info, (void *)func, (void *)ret); +#else + error_info_t err = { func, ret }; + CardServices(ReportError, handle, &err); +#endif +} + +/* + We never need to do anything when a tc574 device is "initialized" + by the net software, because we only register already-found cards. +*/ + +static int tc574_init(struct net_device *dev) +{ + return 0; +} + +/* + tc574_attach() creates an "instance" of the driver, allocating + local data structures for one device. The device is registered + with Card Services. +*/ + +static dev_link_t *tc574_attach(void) +{ + client_reg_t client_reg; + dev_link_t *link; + struct net_device *dev; + int i, ret; + + DEBUG(0, "3c574_attach()\n"); + flush_stale_links(); + + /* Create the PC card device object. */ + link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL); + memset(link, 0, sizeof(struct dev_link_t)); + link->release.function = &tc574_release; + link->release.data = (u_long)link; + link->io.NumPorts1 = 32; + link->io.Attributes1 = IO_DATA_PATH_WIDTH_16; + link->io.IOAddrLines = 5; + link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; + link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID; + if (irq_list[0] == -1) + link->irq.IRQInfo2 = irq_mask; + else + for (i = 0; i < 4; i++) + link->irq.IRQInfo2 |= 1 << irq_list[i]; + link->irq.Handler = &el3_interrupt; + link->conf.Attributes = CONF_ENABLE_IRQ; + link->conf.Vcc = 50; + link->conf.IntType = INT_MEMORY_AND_IO; + link->conf.ConfigIndex = 1; + link->conf.Present = PRESENT_OPTION; + + /* Create the network device object. */ + dev = kmalloc(sizeof(struct net_device), GFP_KERNEL); + memset(dev, 0, sizeof(struct net_device)); + + /* Make up a Odie-specific data structure. */ + dev->priv = kmalloc(sizeof(struct el3_private), GFP_KERNEL); + memset(dev->priv, 0, sizeof(struct el3_private)); + + /* The EL3-specific entries in the device structure. */ + dev->hard_start_xmit = &el3_start_xmit; + dev->get_stats = &el3_get_stats; +#ifdef HAVE_PRIVATE_IOCTL + dev->do_ioctl = &private_ioctl; +#endif + dev->set_multicast_list = &set_rx_mode; + ether_setup(dev); + dev->name = ((struct el3_private *)dev->priv)->node.dev_name; + dev->init = &tc574_init; + dev->open = &el3_open; + dev->stop = &el3_close; + dev->tbusy = 1; + + link->priv = dev; +#if CS_RELEASE_CODE > 0x2911 + link->irq.Instance = dev; +#endif + + /* Register with Card Services */ + link->next = dev_list; + dev_list = link; + client_reg.dev_info = &dev_info; + client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; + client_reg.EventMask = + CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | + CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | + CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; + client_reg.event_handler = &tc574_event; + client_reg.Version = 0x0210; + client_reg.event_callback_args.client_data = link; + ret = CardServices(RegisterClient, &link->handle, &client_reg); + if (ret != 0) { + cs_error(link->handle, RegisterClient, ret); + tc574_detach(link); + return NULL; + } + + return link; +} /* tc574_attach */ + +/* + + This deletes a driver "instance". The device is de-registered + with Card Services. If it has been released, all local data + structures are freed. Otherwise, the structures will be freed + when the device is released. + +*/ + +static void tc574_detach(dev_link_t *link) +{ + dev_link_t **linkp; + long flags; + + DEBUG(0, "3c574_detach(0x%p)\n", link); + + /* Locate device structure */ + for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) + if (*linkp == link) break; + if (*linkp == NULL) + return; + + save_flags(flags); + cli(); + if (link->state & DEV_RELEASE_PENDING) { + del_timer(&link->release); + link->state &= ~DEV_RELEASE_PENDING; + } + restore_flags(flags); + + if (link->state & DEV_CONFIG) { + tc574_release((u_long)link); + if (link->state & DEV_STALE_CONFIG) { + link->state |= DEV_STALE_LINK; + return; + } + } + + if (link->handle) + CardServices(DeregisterClient, link->handle); + + /* Unlink device structure, free bits */ + *linkp = link->next; + if (link->priv) { + struct net_device *dev = link->priv; + if (link->dev != NULL) + unregister_netdev(dev); + if (dev->priv) + kfree(dev->priv); + kfree(link->priv); + } + kfree(link); + +} /* tc574_detach */ + +/* + tc574_config() is scheduled to run after a CARD_INSERTION event + is received, to configure the PCMCIA socket, and to make the + ethernet device available to the system. +*/ + +#define CS_CHECK(fn, args...) \ +while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed + +static void tc574_config(dev_link_t *link) +{ + client_handle_t handle; + struct net_device *dev; + struct el3_private *lp; + tuple_t tuple; + cisparse_t parse; + u_short buf[32]; + int last_fn, last_ret, i, j; + ioaddr_t ioaddr; + u16 *phys_addr; + char *cardname; + + handle = link->handle; + dev = link->priv; + phys_addr = (u16 *)dev->dev_addr; + + DEBUG(0, "3c574_config(0x%p)\n", link); + + tuple.Attributes = 0; + tuple.DesiredTuple = CISTPL_CONFIG; + CS_CHECK(GetFirstTuple, handle, &tuple); + tuple.TupleData = (cisdata_t *)buf; + tuple.TupleDataMax = 64; + tuple.TupleOffset = 0; + CS_CHECK(GetTupleData, handle, &tuple); + CS_CHECK(ParseTuple, handle, &tuple, &parse); + link->conf.ConfigBase = parse.config.base; + link->conf.Present = parse.config.rmask[0]; + + /* Configure card */ + link->state |= DEV_CONFIG; + + for (i = j = 0; j < 0x400; j += 0x20) { + link->io.BasePort1 = j ^ 0x300; + i = CardServices(RequestIO, link->handle, &link->io); + if (i == CS_SUCCESS) break; + } + if (i != CS_SUCCESS) { + cs_error(link->handle, RequestIO, i); + goto failed; + } + CS_CHECK(RequestIRQ, link->handle, &link->irq); + CS_CHECK(RequestConfiguration, link->handle, &link->conf); + + dev->mem_start = 0; +#ifdef BROKEN_FEATURES + if (use_memory_ops) { + win_req_t req; + memreq_t mem; + req.Attributes = WIN_DATA_WIDTH_16 | WIN_MEMORY_TYPE_CM | + WIN_ENABLE | WIN_USE_WAIT; + req.Base = 0; + req.Size = 0x1000; + req.AccessSpeed = 0; + link->win = (window_handle_t)link->handle; + i = CardServices(RequestWindow, &link->win, &req); + if (i == CS_SUCCESS) { + mem.Page = mem.CardOffset = 0; + CardServices(MapMemPage, link->win, &mem); + dev->mem_start = (long)(ioremap(req.Base, 0x1000)) + 0x800; + } else + cs_error(link->handle, RequestWindow, i); + } +#endif + + dev->irq = link->irq.AssignedIRQ; + dev->base_addr = link->io.BasePort1; + + dev->tbusy = 0; + if (register_netdev(dev) != 0) { + printk(KERN_NOTICE "3c574_cs: register_netdev() failed\n"); + goto failed; + } + + link->state &= ~DEV_CONFIG_PENDING; + + ioaddr = dev->base_addr; + lp = (struct el3_private *)dev->priv; + link->dev = &lp->node; + + /* The 3c574 normally uses an EEPROM for configuration info, including + the hardware address. The future products may include a modem chip + and put the address in the CIS. */ + tuple.DesiredTuple = 0x88; + if (CardServices(GetFirstTuple, handle, &tuple) == CS_SUCCESS) { + CardServices(GetTupleData, handle, &tuple); + for (i = 0; i < 3; i++) + phys_addr[i] = htons(buf[i]); + } else { + EL3WINDOW(0); + for (i = 0; i < 3; i++) + phys_addr[i] = htons(read_eeprom(ioaddr, i + 10)); + if (phys_addr[0] == 0x6060) { + printk(KERN_NOTICE "3c574_cs: IO port conflict at 0x%03lx" + "-0x%03lx\n", dev->base_addr, dev->base_addr+15); + goto failed; + } + } + tuple.DesiredTuple = CISTPL_VERS_1; + if (CardServices(GetFirstTuple, handle, &tuple) == CS_SUCCESS && + CardServices(GetTupleData, handle, &tuple) == CS_SUCCESS && + CardServices(ParseTuple, handle, &tuple, &parse) == CS_SUCCESS) { + cardname = parse.version_1.str + parse.version_1.ofs[1]; + } else + cardname = "3Com 3c574"; + + printk(KERN_INFO "%s: %s at io %#3lx, irq %d, hw_addr ", + dev->name, cardname, dev->base_addr, dev->irq); + + for (i = 0; i < 6; i++) + printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : ".\n")); + + if (dev->mem_start) + printk(KERN_INFO" Acceleration window at memory base %#lx.\n", + dev->mem_start); + + { + u_char mcr, *ram_split[] = {"5:3", "3:1", "1:1", "3:5"}; + union wn3_config config; + outw(2<<11, ioaddr + RunnerRdCtrl); + mcr = inb(ioaddr + 2); + outw(0<<11, ioaddr + RunnerRdCtrl); + printk(KERN_INFO " ASIC rev %d,", mcr>>3); + EL3WINDOW(3); + config.i = inl(ioaddr + Wn3_Config); + printk(" %dK FIFO split %s Rx:Tx, %sMII interface.\n", + 8 << config.u.ram_size, ram_split[config.u.ram_split], + config.u.autoselect ? "autoselect " : ""); + lp->default_media = config.u.xcvr; + lp->autoselect = config.u.autoselect; + } + + { + int phy, phy_idx = 0; + + /* Roadrunner only: Turn on the MII transceiver */ + outw(0x8040, ioaddr + Wn3_Options); + udelay(1000); + outw(0xc040, ioaddr + Wn3_Options); + wait_for_completion(dev, TxReset); + wait_for_completion(dev, RxReset); + udelay(1000); + outw(0x8040, ioaddr + Wn3_Options); + + EL3WINDOW(4); + for (phy = 1; phy <= 32 && phy_idx < sizeof(lp->phys); phy++) { + int mii_status; + mdio_sync(ioaddr, 32); + mii_status = mdio_read(ioaddr, phy & 0x1f, 1); + if (mii_status != 0xffff) { + lp->phys[phy_idx++] = phy & 0x1f; + DEBUG(0, " MII transceiver at index %d, status %x.\n", + phy, mii_status); + if ((mii_status & 0x0040) == 0) + mii_preamble_required = 1; + } + } + if (phy_idx == 0) { + printk(KERN_NOTICE " No MII transceivers found!\n"); + goto failed; + } + i = mdio_read(ioaddr, lp->phys[0], 16) | 0x40; + mdio_write(ioaddr, lp->phys[0], 16, i); + lp->advertising = mdio_read(ioaddr, lp->phys[0], 4); + if (full_duplex) { + /* Only advertise the FD media types. */ + lp->advertising &= ~0x02a0; + mdio_write(ioaddr, lp->phys[0], 4, lp->advertising); + } + } + + return; + +cs_failed: + cs_error(link->handle, last_fn, last_ret); +failed: + tc574_release((u_long)link); + return; + +} /* tc574_config */ + +/* + After a card is removed, tc574_release() will unregister the net + device, and release the PCMCIA configuration. If the device is + still open, this will be postponed until it is closed. +*/ + +static void tc574_release(u_long arg) +{ + dev_link_t *link = (dev_link_t *)arg; + struct net_device *dev = link->priv; + + DEBUG(0, "3c574_release(0x%p)\n", link); + + if (link->open) { + DEBUG(1, "3c574_cs: release postponed, '%s' still open\n", + link->dev->dev_name); + link->state |= DEV_STALE_CONFIG; + return; + } + + CardServices(ReleaseConfiguration, link->handle); + CardServices(ReleaseIO, link->handle, &link->io); + CardServices(ReleaseIRQ, link->handle, &link->irq); + if (link->win) { + iounmap((void *)(dev->mem_start - 0x800)); + CardServices(ReleaseWindow, link->win); + } + + link->state &= ~(DEV_CONFIG | DEV_RELEASE_PENDING); + +} /* tc574_release */ + +/* + The card status event handler. Mostly, this schedules other + stuff to run after an event is received. A CARD_REMOVAL event + also sets some flags to discourage the net drivers from trying + to talk to the card any more. +*/ + +static int tc574_event(event_t event, int priority, + event_callback_args_t *args) +{ + dev_link_t *link = args->client_data; + struct net_device *dev = link->priv; + + DEBUG(1, "3c574_event(0x%06x)\n", event); + + switch (event) { + case CS_EVENT_CARD_REMOVAL: + link->state &= ~DEV_PRESENT; + if (link->state & DEV_CONFIG) { + dev->tbusy = 1; dev->start = 0; + link->release.expires = jiffies + HZ/20; + add_timer(&link->release); + } + break; + case CS_EVENT_CARD_INSERTION: + link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; + tc574_config(link); + break; + case CS_EVENT_PM_SUSPEND: + link->state |= DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_RESET_PHYSICAL: + if (link->state & DEV_CONFIG) { + if (link->open) { + dev->tbusy = 1; dev->start = 0; + } + CardServices(ReleaseConfiguration, link->handle); + } + break; + case CS_EVENT_PM_RESUME: + link->state &= ~DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_CARD_RESET: + if (link->state & DEV_CONFIG) { + CardServices(RequestConfiguration, link->handle, &link->conf); + if (link->open) { + tc574_reset(dev); + dev->tbusy = 0; dev->start = 1; + } + } + break; + } + return 0; +} /* tc574_event */ + +static void dump_status(struct net_device *dev) +{ + ioaddr_t ioaddr = dev->base_addr; + EL3WINDOW(1); + printk(KERN_INFO " irq status %04x, rx status %04x, tx status " + "%02x, tx free %04x\n", inw(ioaddr+EL3_STATUS), + inw(ioaddr+RxStatus), inb(ioaddr+TxStatus), + inw(ioaddr+TxFree)); + EL3WINDOW(4); + printk(KERN_INFO " diagnostics: fifo %04x net %04x ethernet %04x" + " media %04x\n", inw(ioaddr+0x04), inw(ioaddr+0x06), + inw(ioaddr+0x08), inw(ioaddr+0x0a)); + EL3WINDOW(1); +} + +/* + Use this for commands that may take time to finish +*/ +static void wait_for_completion(struct net_device *dev, int cmd) +{ + int i = 1500; + outw(cmd, dev->base_addr + EL3_CMD); + while (--i > 0) + if (!(inw(dev->base_addr + EL3_STATUS) & 0x1000)) break; + if (i == 0) + printk(KERN_NOTICE "%s: command 0x%04x did not complete!\n", + dev->name, cmd); +} + +/* Read a word from the EEPROM using the regular EEPROM access register. + Assume that we are in register window zero. + */ +static u_short read_eeprom(ioaddr_t ioaddr, int index) +{ + int timer; + outw(EEPROM_Read + index, ioaddr + Wn0EepromCmd); + /* Pause for at least 162 usec for the read to take place. */ + for (timer = 1620; timer >= 0; timer--) { + if ((inw(ioaddr + Wn0EepromCmd) & 0x8000) == 0) + break; + } + return inw(ioaddr + Wn0EepromData); +} + +/* MII transceiver control section. + Read and write the MII registers using software-generated serial + MDIO protocol. See the MII specifications or DP83840A data sheet + for details. + The maxium data clock rate is 2.5 Mhz. The timing is easily met by the + slow PC card interface. */ + +#define MDIO_SHIFT_CLK 0x01 +#define MDIO_DIR_WRITE 0x04 +#define MDIO_DATA_WRITE0 (0x00 | MDIO_DIR_WRITE) +#define MDIO_DATA_WRITE1 (0x02 | MDIO_DIR_WRITE) +#define MDIO_DATA_READ 0x02 +#define MDIO_ENB_IN 0x00 + +/* Generate the preamble required for initial synchronization and + a few older transceivers. */ +static void mdio_sync(ioaddr_t ioaddr, int bits) +{ + int mdio_addr = ioaddr + Wn4_PhysicalMgmt; + + /* Establish sync by sending at least 32 logic ones. */ + while (-- bits >= 0) { + outw(MDIO_DATA_WRITE1, mdio_addr); + outw(MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr); + } +} + +static int mdio_read(ioaddr_t ioaddr, int phy_id, int location) +{ + int i; + int read_cmd = (0xf6 << 10) | (phy_id << 5) | location; + unsigned int retval = 0; + int mdio_addr = ioaddr + Wn4_PhysicalMgmt; + + if (mii_preamble_required) + mdio_sync(ioaddr, 32); + + /* Shift the read command bits out. */ + for (i = 14; i >= 0; i--) { + int dataval = (read_cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0; + outw(dataval, mdio_addr); + outw(dataval | MDIO_SHIFT_CLK, mdio_addr); + } + /* Read the two transition, 16 data, and wire-idle bits. */ + for (i = 19; i > 0; i--) { + outw(MDIO_ENB_IN, mdio_addr); + retval = (retval << 1) | ((inw(mdio_addr) & MDIO_DATA_READ) ? 1 : 0); + outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); + } + return (retval>>1) & 0xffff; +} + +static void mdio_write(ioaddr_t ioaddr, int phy_id, int location, int value) +{ + int write_cmd = 0x50020000 | (phy_id << 23) | (location << 18) | value; + int mdio_addr = ioaddr + Wn4_PhysicalMgmt; + int i; + + if (mii_preamble_required) + mdio_sync(ioaddr, 32); + + /* Shift the command bits out. */ + for (i = 31; i >= 0; i--) { + int dataval = (write_cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0; + outw(dataval, mdio_addr); + outw(dataval | MDIO_SHIFT_CLK, mdio_addr); + } + /* Leave the interface idle. */ + for (i = 1; i >= 0; i--) { + outw(MDIO_ENB_IN, mdio_addr); + outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); + } + + return; +} + +/* Reset and restore all of the 3c574 registers. */ +static void tc574_reset(struct net_device *dev) +{ + struct el3_private *lp = (struct el3_private *)dev->priv; + int i, ioaddr = dev->base_addr; + + wait_for_completion(dev, TotalReset|0x10); + +#ifdef BROKEN_FEATURES + /* Set the PIO ctrl bits in the PC card LAN COR using Runner window 1. */ + if (dev->mem_start || no_wait) { + u8 lan_cor; + outw(1<<11, ioaddr + RunnerRdCtrl); + lan_cor = inw(ioaddr) & ~0x30; + if (dev->mem_start) /* Iff use_memory_ops worked! */ + lan_cor |= 0x10; + if (no_wait) + lan_cor |= 0x20; + outw(lan_cor, ioaddr); + } +#endif + /* Clear any transactions in progress. */ + outw(0, ioaddr + RunnerWrCtrl); + outw(0, ioaddr + RunnerRdCtrl); + + /* Set the station address and mask. */ + EL3WINDOW(2); + for (i = 0; i < 6; i++) + outb(dev->dev_addr[i], ioaddr + i); + for (; i < 12; i+=2) + outw(0, ioaddr + i); + + /* Reset config options */ + EL3WINDOW(3); + outb((dev->mtu > 1500 ? 0x40 : 0), ioaddr + Wn3_MAC_Ctrl); + outl((lp->autoselect ? 0x01000000 : 0) | 0x0062001b, + ioaddr + Wn3_Config); + + /* Roadrunner only: Turn on the MII transceiver. */ + outw(0x8040, ioaddr + Wn3_Options); + udelay(1000); + outw(0xc040, ioaddr + Wn3_Options); + wait_for_completion(dev, TxReset); + wait_for_completion(dev, RxReset); + udelay(1000); + outw(0x8040, ioaddr + Wn3_Options); + + /* Switch to the stats window, and clear all stats by reading. */ + outw(StatsDisable, ioaddr + EL3_CMD); + EL3WINDOW(6); + for (i = 0; i < 10; i++) + inb(ioaddr + i); + inw(ioaddr + 10); + inw(ioaddr + 12); + + EL3WINDOW(4); + /* New: On the Vortex/Odie we must also clear the BadSSD counter.. */ + inb(ioaddr + 12); + /* .. enable any extra statistics bits.. */ + outw(0x0040, ioaddr + Wn4_NetDiag); + /* .. re-sync MII and re-fill what NWay is advertising. */ + mdio_sync(ioaddr, 32); + mdio_write(ioaddr, lp->phys[0], 4, lp->advertising); + + /* Switch to register set 1 for normal use, just for TxFree. */ + EL3WINDOW(1); + + set_rx_mode(dev); + outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */ + outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */ + outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */ + /* Allow status bits to be seen. */ + outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD); + /* Ack all pending events, and set active indicator mask. */ + outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq, + ioaddr + EL3_CMD); + outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull + | AdapterFailure | RxEarly, ioaddr + EL3_CMD); +} + +static int el3_open(struct net_device *dev) +{ + struct el3_private *lp = (struct el3_private *)dev->priv; + dev_link_t *link; + + for (link = dev_list; link; link = link->next) + if (link->priv == dev) break; + if (!DEV_OK(link)) + return -ENODEV; + + link->open++; + MOD_INC_USE_COUNT; + dev->interrupt = 0; dev->tbusy = 0; dev->start = 1; + + tc574_reset(dev); + lp->media.function = &media_check; + lp->media.data = (u_long)dev; + lp->media.expires = jiffies + HZ; + add_timer(&lp->media); + + DEBUG(2, "%s: opened, status %4.4x.\n", + dev->name, inw(dev->base_addr + EL3_STATUS)); + + return 0; /* Always succeed */ +} + +static void el3_tx_timeout(struct net_device *dev) +{ + struct el3_private *lp = (struct el3_private *)dev->priv; + ioaddr_t ioaddr = dev->base_addr; + + printk(KERN_NOTICE "%s: Transmit timed out!\n", dev->name); + dump_status(dev); + lp->stats.tx_errors++; + dev->trans_start = jiffies; + /* Issue TX_RESET and TX_START commands. */ + wait_for_completion(dev, TxReset); + outw(TxEnable, ioaddr + EL3_CMD); + dev->tbusy = 0; +} + +static void pop_tx_status(struct net_device *dev) +{ + struct el3_private *lp = (struct el3_private *)dev->priv; + ioaddr_t ioaddr = dev->base_addr; + int i; + + /* Clear the Tx status stack. */ + for (i = 32; i > 0; i--) { + u_char tx_status = inb(ioaddr + TxStatus); + if (!(tx_status & 0x84)) break; + /* reset transmitter on jabber error or underrun */ + if (tx_status & 0x30) + wait_for_completion(dev, TxReset); + if (tx_status & 0x38) { + DEBUG(1, "%s: transmit error: status 0x%02x\n", + dev->name, tx_status); + outw(TxEnable, ioaddr + EL3_CMD); + lp->stats.tx_aborted_errors++; + } + outb(0x00, ioaddr + TxStatus); /* Pop the status stack. */ + } +} + +static int el3_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + ioaddr_t ioaddr = dev->base_addr; +#ifdef BROKEN_FEATURES + long flags = 0; +#endif + + /* Transmitter timeout, serious problems. */ + if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { + if (jiffies - dev->trans_start < TX_TIMEOUT) + return 1; + el3_tx_timeout(dev); + } + + DEBUG(3, "%s: el3_start_xmit(length = %ld) called, " + "status %4.4x.\n", dev->name, (long)skb->len, + inw(ioaddr + EL3_STATUS)); + +#ifdef BROKEN_FEATURES + if (use_fifo_buffer) { + /* Avoid other accesses to the chip while RunnerWrCtrl is non-zero. */ + save_flags(flags); + cli(); + outw((((skb->len + 7)>>2)<<1), ioaddr + RunnerWrCtrl); + DEBUG(0, "TxFree %x, tx length %x, RunnerWrCtrl is %4.4x.\n", + inw(ioaddr+TxFree), skb->len, inw(ioaddr+RunnerWrCtrl)); + } + + /* Put out the doubleword header... */ + /* ... and the packet rounded to a doubleword. */ + if (dev->mem_start) { + writew(skb->len, (void *)dev->mem_start); + writew(0, (void *)dev->mem_start); + copy_to_pc((void*)dev->mem_start, skb->data, (skb->len+3)&~3); + } else { + outw(skb->len, ioaddr + TX_FIFO); + outw(0, ioaddr + TX_FIFO); + outsl(ioaddr + TX_FIFO, skb->data, (skb->len+3)>>2); + } + + if (use_fifo_buffer) { + DEBUG(0, " RunnerWr/RdCtrl is %4.4x/%4.4x, TxFree %x.\n", + inw(ioaddr + RunnerWrCtrl), inw(ioaddr + RunnerRdCtrl), + inw(ioaddr + TxFree)); + restore_flags(flags); + } +#else + outw(skb->len, ioaddr + TX_FIFO); + outw(0, ioaddr + TX_FIFO); + outsl(ioaddr + TX_FIFO, skb->data, (skb->len+3)>>2); +#endif + + dev->trans_start = jiffies; + + /* TxFree appears only in Window 1, not offset 0x1c. */ + if (inw(ioaddr + TxFree) > 1536) { + dev->tbusy = 0; + } else + /* Interrupt us when the FIFO has room for max-sized packet. + The threshold is in units of dwords. */ + outw(SetTxThreshold + (1536>>2), ioaddr + EL3_CMD); + + dev_kfree_skb (skb); + pop_tx_status(dev); + + return 0; +} + +/* The EL3 interrupt handler. */ +static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct el3_private *lp; + ioaddr_t ioaddr, status; + int work_budget = max_interrupt_work; + + if ((dev == NULL) || !dev->start) + return; + lp = (struct el3_private *)dev->priv; + ioaddr = dev->base_addr; + +#ifdef PCMCIA_DEBUG + if (test_and_set_bit(0, (void*)&dev->interrupt)) { + printk(KERN_NOTICE "%s: re-entering the interrupt handler.\n", + dev->name); + return; + } + DEBUG(3, "%s: interrupt, status %4.4x.\n", + dev->name, inw(ioaddr + EL3_STATUS)); +#endif + + while ((status = inw(ioaddr + EL3_STATUS)) & + (IntLatch | RxComplete | RxEarly | StatsFull)) { + if ((dev->start == 0) || ((status & 0xe000) != 0x2000)) { + DEBUG(1, "%s: Interrupt from dead card\n", dev->name); + break; + } + + if (status & RxComplete) + work_budget = el3_rx(dev, work_budget); + + if (status & TxAvailable) { + DEBUG(3, " TX room bit was handled.\n"); + /* There's room in the FIFO for a full-sized packet. */ + outw(AckIntr | TxAvailable, ioaddr + EL3_CMD); + dev->tbusy = 0; + mark_bh(NET_BH); + } + + if (status & TxComplete) + pop_tx_status(dev); + + if (status & (AdapterFailure | RxEarly | StatsFull)) { + /* Handle all uncommon interrupts. */ + if (status & StatsFull) + update_stats(ioaddr, dev); + if (status & RxEarly) { + work_budget = el3_rx(dev, work_budget); + outw(AckIntr | RxEarly, ioaddr + EL3_CMD); + } + if (status & AdapterFailure) { + u16 fifo_diag; + EL3WINDOW(4); + fifo_diag = inw(ioaddr + Wn4_FIFODiag); + EL3WINDOW(1); + printk(KERN_NOTICE "%s: adapter failure, FIFO diagnostic" + " register %04x.\n", dev->name, fifo_diag); + if (fifo_diag & 0x0400) { + /* Tx overrun */ + wait_for_completion(dev, TxReset); + outw(TxEnable, ioaddr + EL3_CMD); + } + if (fifo_diag & 0x2000) { + /* Rx underrun */ + wait_for_completion(dev, RxReset); + set_rx_mode(dev); + outw(RxEnable, ioaddr + EL3_CMD); + } + outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD); + } + } + + if (--work_budget < 0) { + DEBUG(0, "%s: Too much work in interrupt, " + "status %4.4x.\n", dev->name, status); + /* Clear all interrupts */ + outw(AckIntr | 0xFF, ioaddr + EL3_CMD); + break; + } + /* Acknowledge the IRQ. */ + outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD); + } + +#ifdef PCMCIA_DEBUG + DEBUG(3, "%s: exiting interrupt, status %4.4x.\n", + dev->name, inw(ioaddr + EL3_STATUS)); + dev->interrupt = 0; +#endif + return; +} + +/* + This timer serves two purposes: to check for missed interrupts + (and as a last resort, poll the NIC for events), and to monitor + the MII, reporting changes in cable status. +*/ +static void media_check(u_long arg) +{ + struct net_device *dev = (struct net_device *)(arg); + struct el3_private *lp = (struct el3_private *)dev->priv; + ioaddr_t ioaddr = dev->base_addr; + u_long flags; + u_short /* cable, */ media, partner; + + if (dev->start == 0) goto reschedule; + + /* Check for pending interrupt with expired latency timer: with + this, we can limp along even if the interrupt is blocked */ + if ((inw(ioaddr + EL3_STATUS) & IntLatch) && + (inb(ioaddr + Timer) == 0xff)) { + if (!lp->fast_poll) + printk(KERN_INFO "%s: interrupt(s) dropped!\n", dev->name); + el3_interrupt(dev->irq, dev, NULL); + lp->fast_poll = HZ; + } + if (lp->fast_poll) { + lp->fast_poll--; + lp->media.expires = jiffies + 2; + add_timer(&lp->media); + return; + } + + save_flags(flags); + cli(); + EL3WINDOW(4); + media = mdio_read(ioaddr, lp->phys[0], 1); + partner = mdio_read(ioaddr, lp->phys[0], 5); + EL3WINDOW(1); + restore_flags(flags); + + if (media != lp->media_status) { + if ((media ^ lp->media_status) & 0x0004) + printk(KERN_INFO "%s: %s link beat\n", dev->name, + (lp->media_status & 0x0004) ? "lost" : "found"); + if ((media ^ lp->media_status) & 0x0020) { + lp->partner = 0; + if (lp->media_status & 0x0020) { + printk(KERN_INFO "%s: autonegotiation restarted\n", + dev->name); + } else if (partner) { + partner &= lp->advertising; + lp->partner = partner; + printk(KERN_INFO "%s: autonegotiation complete: " + "%sbaseT-%cD selected\n", dev->name, + ((partner & 0x0180) ? "100" : "10"), + ((partner & 0x0140) ? 'F' : 'H')); + } else { + printk(KERN_INFO "%s: link partner did not autonegotiate\n", + dev->name); + } + + EL3WINDOW(3); + outb((partner & 0x0140 ? 0x20 : 0) | + (dev->mtu > 1500 ? 0x40 : 0), ioaddr + Wn3_MAC_Ctrl); + EL3WINDOW(1); + + } + if (media & 0x0010) + printk(KERN_INFO "%s: remote fault detected\n", + dev->name); + if (media & 0x0002) + printk(KERN_INFO "%s: jabber detected\n", dev->name); + lp->media_status = media; + } + +reschedule: + lp->media.expires = jiffies + HZ; + add_timer(&lp->media); +} + +static struct net_device_stats *el3_get_stats(struct net_device *dev) +{ + struct el3_private *lp = (struct el3_private *)dev->priv; + + if (dev->start) + update_stats(dev->base_addr, dev); + return &lp->stats; +} + +/* Update statistics. + Suprisingly this need not be run single-threaded, but it effectively is. + The counters clear when read, so the adds must merely be atomic. + */ +static void update_stats(ioaddr_t ioaddr, struct net_device *dev) +{ + struct el3_private *lp = (struct el3_private *)dev->priv; + u8 upper_cnt; + + DEBUG(2, "%s: updating the statistics.\n", dev->name); + + if (inw(ioaddr+EL3_STATUS) == 0xffff) /* No card. */ + return; + + /* Unlike the 3c509 we need not turn off stats updates while reading. */ + /* Switch to the stats window, and read everything. */ + EL3WINDOW(6); + lp->stats.tx_carrier_errors += inb(ioaddr + 0); + lp->stats.tx_heartbeat_errors += inb(ioaddr + 1); + /* Multiple collisions. */ inb(ioaddr + 2); + lp->stats.collisions += inb(ioaddr + 3); + lp->stats.tx_window_errors += inb(ioaddr + 4); + lp->stats.rx_fifo_errors += inb(ioaddr + 5); + lp->stats.tx_packets += inb(ioaddr + 6); + upper_cnt = inb(ioaddr + 9); + lp->stats.tx_packets += (upper_cnt&0x30) << 4; + /* Rx packets */ inb(ioaddr + 7); + /* Tx deferrals */ inb(ioaddr + 8); + lp->stats.rx_bytes += inw(ioaddr + 10); + lp->stats.tx_bytes += inw(ioaddr + 12); + + /* With Vortex and later we must also clear the BadSSD counter. */ + EL3WINDOW(4); + inb(ioaddr + 12); + + EL3WINDOW(1); +} + +static int el3_rx(struct net_device *dev, int worklimit) +{ + struct el3_private *lp = (struct el3_private *)dev->priv; + ioaddr_t ioaddr = dev->base_addr; + short rx_status; + + DEBUG(3, "%s: in rx_packet(), status %4.4x, rx_status %4.4x.\n", + dev->name, inw(ioaddr+EL3_STATUS), inw(ioaddr+RxStatus)); + while (!((rx_status = inw(ioaddr + RxStatus)) & 0x8000) && + (--worklimit >= 0)) { + if (rx_status & 0x4000) { /* Error, update stats. */ + short error = rx_status & 0x3800; + lp->stats.rx_errors++; + switch (error) { + case 0x0000: lp->stats.rx_over_errors++; break; + case 0x0800: lp->stats.rx_length_errors++; break; + case 0x1000: lp->stats.rx_frame_errors++; break; + case 0x1800: lp->stats.rx_length_errors++; break; + case 0x2000: lp->stats.rx_frame_errors++; break; + case 0x2800: lp->stats.rx_crc_errors++; break; + } + } else { + short pkt_len = rx_status & 0x7ff; + struct sk_buff *skb; + + skb = dev_alloc_skb(pkt_len+5); + + DEBUG(3, " Receiving packet size %d status %4.4x.\n", + pkt_len, rx_status); + if (skb != NULL) { + skb->dev = dev; + skb_reserve(skb, 2); + +#ifdef BROKEN_FEATURES + if (use_fifo_buffer) { + outw(((pkt_len+3)>>2)<<1, ioaddr + RunnerRdCtrl); + DEBUG(0,"Start Rx %x -- RunnerRdCtrl is %4.4x.\n", + pkt_len, inw(ioaddr + RunnerRdCtrl)); + } + if (dev->mem_start) { + copy_from_pc(skb_put(skb, pkt_len), + (void*)dev->mem_start, (pkt_len+3)&~3); + } else { + insl(ioaddr+RX_FIFO, skb_put(skb, pkt_len), + ((pkt_len+3)>>2)); + } + if (use_fifo_buffer) { + DEBUG(0," RunnerRdCtrl is now %4.4x.\n", + inw(ioaddr + RunnerRdCtrl)); + outw(0, ioaddr + RunnerRdCtrl); + DEBUG(0, " Rx packet with data %2.2x:%2.2x:%2.2x\n", + skb->head[0], skb->head[1], skb->head[2]); + } +#else + insl(ioaddr+RX_FIFO, skb_put(skb, pkt_len), + ((pkt_len+3)>>2)); +#endif + + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + lp->stats.rx_packets++; + } else { + DEBUG(1, "%s: couldn't allocate a sk_buff of" + " size %d.\n", dev->name, pkt_len); + lp->stats.rx_dropped++; + } + } + wait_for_completion(dev, RxDiscard); + } + + return worklimit; +} + +#ifdef HAVE_PRIVATE_IOCTL +/* Provide ioctl() calls to examine the MII xcvr state. */ +static int private_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct el3_private *vp = (struct el3_private *)dev->priv; + ioaddr_t ioaddr = dev->base_addr; + u16 *data = (u16 *)&rq->ifr_data; + int phy = vp->phys[0] & 0x1f; + + DEBUG(2, "%s: In ioct(%-.6s, %#4.4x) %4.4x %4.4x %4.4x %4.4x.\n", + dev->name, rq->ifr_ifrn.ifrn_name, cmd, + data[0], data[1], data[2], data[3]); + + switch(cmd) { + case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */ + data[0] = phy; + case SIOCDEVPRIVATE+1: /* Read the specified MII register. */ + { + int saved_window; + long flags; + + save_flags(flags); + cli(); + saved_window = inw(ioaddr + EL3_CMD) >> 13; + EL3WINDOW(4); + data[3] = mdio_read(ioaddr, data[0] & 0x1f, data[1] & 0x1f); + EL3WINDOW(saved_window); + restore_flags(flags); + return 0; + } + case SIOCDEVPRIVATE+2: /* Write the specified MII register */ + { + int saved_window; + long flags; + + if (!suser()) + return -EPERM; + save_flags(flags); + cli(); + saved_window = inw(ioaddr + EL3_CMD) >> 13; + EL3WINDOW(4); + mdio_write(ioaddr, data[0] & 0x1f, data[1] & 0x1f, data[2]); + EL3WINDOW(saved_window); + restore_flags(flags); + return 0; + } + default: + return -EOPNOTSUPP; + } +} +#endif /* HAVE_PRIVATE_IOCTL */ + +/* The Odie chip has a 64 bin multicast filter, but the bit layout is not + documented. Until it is we revert to receiving all multicast frames when + any multicast reception is desired. + Note: My other drivers emit a log message whenever promiscuous mode is + entered to help detect password sniffers. This is less desirable on + typical PC card machines, so we omit the message. + */ + +static void set_rx_mode(struct net_device *dev) +{ + ioaddr_t ioaddr = dev->base_addr; + + if (dev->flags & IFF_PROMISC) + outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm, + ioaddr + EL3_CMD); + else if (dev->mc_count || (dev->flags & IFF_ALLMULTI)) + outw(SetRxFilter|RxStation|RxMulticast|RxBroadcast, ioaddr + EL3_CMD); + else + outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD); +} + +static int el3_close(struct net_device *dev) +{ + ioaddr_t ioaddr = dev->base_addr; + dev_link_t *link; + + for (link = dev_list; link; link = link->next) + if (link->priv == dev) break; + if (link == NULL) + return -ENODEV; + + DEBUG(2, "%s: shutting down ethercard.\n", dev->name); + + if (DEV_OK(link)) { + /* Turn off statistics ASAP. We update lp->stats below. */ + outw(StatsDisable, ioaddr + EL3_CMD); + + /* Disable the receiver and transmitter. */ + outw(RxDisable, ioaddr + EL3_CMD); + outw(TxDisable, ioaddr + EL3_CMD); + + /* Note: Switching to window 0 may disable the IRQ. */ + EL3WINDOW(0); + + update_stats(ioaddr, dev); + } + + link->open--; + dev->start = 0; + del_timer(&((struct el3_private *)dev->priv)->media); + if (link->state & DEV_STALE_CONFIG) { + link->release.expires = jiffies + HZ/20; + link->state |= DEV_RELEASE_PENDING; + add_timer(&link->release); + } + + MOD_DEC_USE_COUNT; + + return 0; +} + +static int __init init_3c574_cs(void) +{ + servinfo_t serv; + + /* Always emit the version, before any failure. */ + printk(KERN_INFO"%s", tc574_version); + DEBUG(0, "%s\n", version); + CardServices(GetCardServicesInfo, &serv); + if (serv.Revision != CS_RELEASE_CODE) { + printk(KERN_NOTICE "3c574_cs: Card Services release " + "does not match!\n"); + return -1; + } + register_pccard_driver(&dev_info, &tc574_attach, &tc574_detach); + return 0; +} + +static void __exit exit_3c574_cs(void) +{ + DEBUG(0, "3c574_cs: unloading\n"); + unregister_pccard_driver(&dev_info); + while (dev_list != NULL) + tc574_detach(dev_list); +} + +module_init(init_3c574_cs); +module_exit(exit_3c574_cs); + +/* + * Local variables: + * compile-command: "make 3c574_cs.o" + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff --git a/drivers/net/pcmcia/3c589_cs.c b/drivers/net/pcmcia/3c589_cs.c index 6e511f4b7..dea14d53a 100644 --- a/drivers/net/pcmcia/3c589_cs.c +++ b/drivers/net/pcmcia/3c589_cs.c @@ -4,7 +4,7 @@ Copyright (C) 1999 David A. Hinds -- dhinds@hyper.stanford.edu - 3c589_cs.c 1.134 1999/09/15 15:33:09 + 3c589_cs.c 1.135 1999/10/07 20:14:54 The network driver code is based on Donald Becker's 3c589 code: @@ -115,7 +115,7 @@ static int pc_debug = PCMCIA_DEBUG; MODULE_PARM(pc_debug, "i"); #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) static char *version = -"3c589_cs.c 1.134 1999/09/15 15:33:09 (David Hinds)"; +"3c589_cs.c 1.135 1999/10/07 20:14:54 (David Hinds)"; #else #define DEBUG(n, args...) #endif @@ -142,14 +142,14 @@ static void tc589_release(u_long arg); static int tc589_event(event_t event, int priority, event_callback_args_t *args); -static ushort read_eeprom(short ioaddr, int index); +static u_short read_eeprom(ioaddr_t ioaddr, int index); static void tc589_reset(struct net_device *dev); static void media_check(u_long arg); static int el3_config(struct net_device *dev, struct ifmap *map); static int el3_open(struct net_device *dev); static int el3_start_xmit(struct sk_buff *skb, struct net_device *dev); static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs); -static void update_stats(int addr, struct net_device *dev); +static void update_stats(ioaddr_t addr, struct net_device *dev); static struct net_device_stats *el3_get_stats(struct net_device *dev); static int el3_rx(struct net_device *dev); static int el3_close(struct net_device *dev); @@ -355,14 +355,14 @@ static void tc589_config(dev_link_t *link) struct net_device *dev; tuple_t tuple; cisparse_t parse; - u_short buf[32]; + u_short buf[32], *phys_addr; int last_fn, last_ret, i, j, multi = 0; - short ioaddr, *phys_addr; + ioaddr_t ioaddr; char *ram_split[] = {"5:3", "3:1", "1:1", "3:5"}; handle = link->handle; dev = link->priv; - phys_addr = (short *)dev->dev_addr; + phys_addr = (u_short *)dev->dev_addr; DEBUG(0, "3c589_config(0x%p)\n", link); @@ -571,7 +571,7 @@ static void wait_for_completion(struct net_device *dev, int cmd) Read a word from the EEPROM using the regular EEPROM access register. Assume that we are in register window zero. */ -static ushort read_eeprom(short ioaddr, int index) +static u_short read_eeprom(ioaddr_t ioaddr, int index) { int i; outw(EEPROM_READ + index, ioaddr + 10); @@ -589,7 +589,7 @@ static ushort read_eeprom(short ioaddr, int index) static void tc589_set_xcvr(struct net_device *dev, int if_port) { struct el3_private *lp = (struct el3_private *)dev->priv; - ushort ioaddr = dev->base_addr; + ioaddr_t ioaddr = dev->base_addr; EL3WINDOW(0); switch (if_port) { @@ -611,7 +611,7 @@ static void tc589_set_xcvr(struct net_device *dev, int if_port) static void dump_status(struct net_device *dev) { - int ioaddr = dev->base_addr; + ioaddr_t ioaddr = dev->base_addr; EL3WINDOW(1); printk(KERN_INFO " irq status %04x, rx status %04x, tx status " "%02x tx free %04x\n", inw(ioaddr+EL3_STATUS), @@ -627,7 +627,7 @@ static void dump_status(struct net_device *dev) /* Reset and restore all of the 3c589 registers. */ static void tc589_reset(struct net_device *dev) { - ushort ioaddr = dev->base_addr; + ioaddr_t ioaddr = dev->base_addr; int i; EL3WINDOW(0); @@ -709,7 +709,7 @@ static int el3_open(struct net_device *dev) static void el3_tx_timeout(struct net_device *dev) { struct el3_private *lp = (struct el3_private *)dev->priv; - int ioaddr = dev->base_addr; + ioaddr_t ioaddr = dev->base_addr; printk(KERN_NOTICE "%s: Transmit timed out!\n", dev->name); dump_status(dev); @@ -724,7 +724,7 @@ static void el3_tx_timeout(struct net_device *dev) static void pop_tx_status(struct net_device *dev) { struct el3_private *lp = (struct el3_private *)dev->priv; - int ioaddr = dev->base_addr; + ioaddr_t ioaddr = dev->base_addr; int i; /* Clear the Tx status stack. */ @@ -747,7 +747,7 @@ static void pop_tx_status(struct net_device *dev) static int el3_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct el3_private *lp = (struct el3_private *)dev->priv; - int ioaddr = dev->base_addr; + ioaddr_t ioaddr = dev->base_addr; /* Transmitter timeout, serious problems. */ if (dev->tbusy) { @@ -791,7 +791,7 @@ static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct net_device *dev = (struct net_device *)dev_id; struct el3_private *lp; - int ioaddr, status; + ioaddr_t ioaddr, status; int i = 0; if ((dev == NULL) || !dev->start) @@ -885,7 +885,7 @@ static void media_check(u_long arg) { struct net_device *dev = (struct net_device *)(arg); struct el3_private *lp = (struct el3_private *)dev->priv; - int ioaddr = dev->base_addr; + ioaddr_t ioaddr = dev->base_addr; u_short media, errs; u_long flags; @@ -984,7 +984,7 @@ static struct net_device_stats *el3_get_stats(struct net_device *dev) operation, and it's simpler for the rest of the driver to assume that window 1 is always valid rather than use a special window-state variable. */ -static void update_stats(int ioaddr, struct net_device *dev) +static void update_stats(ioaddr_t ioaddr, struct net_device *dev) { struct el3_private *lp = (struct el3_private *)dev->priv; @@ -1013,7 +1013,7 @@ static void update_stats(int ioaddr, struct net_device *dev) static int el3_rx(struct net_device *dev) { struct el3_private *lp = (struct el3_private *)dev->priv; - int ioaddr = dev->base_addr; + ioaddr_t ioaddr = dev->base_addr; int worklimit = 32; short rx_status; @@ -1073,7 +1073,7 @@ static int el3_rx(struct net_device *dev) */ static void set_multicast_list(struct net_device *dev) { - short ioaddr = dev->base_addr; + ioaddr_t ioaddr = dev->base_addr; dev_link_t *link; for (link = dev_list; link; link = link->next) if (link->priv == dev) break; @@ -1099,7 +1099,7 @@ static void set_multicast_list(struct net_device *dev) static int el3_close(struct net_device *dev) { - int ioaddr = dev->base_addr; + ioaddr_t ioaddr = dev->base_addr; dev_link_t *link; for (link = dev_list; link; link = link->next) diff --git a/drivers/net/pcmcia/Config.in b/drivers/net/pcmcia/Config.in index cc4efbf88..cca2ff57a 100644 --- a/drivers/net/pcmcia/Config.in +++ b/drivers/net/pcmcia/Config.in @@ -3,13 +3,34 @@ # mainmenu_option next_comment -comment 'PCMCIA network devices' +comment 'PCMCIA network device support' -dep_tristate 'PCMCIA ethernet cards (NE2000 compatibles: DE-650, ...)' CONFIG_PCMCIA_PCNET $CONFIG_PCMCIA -dep_tristate '3Com 3c589 PCMCIA card' CONFIG_PCMCIA_3C589 $CONFIG_PCMCIA -dep_tristate 'Aviator/Raytheon 2.4MHz wireless' CONFIG_PCMCIA_RAYCS $CONFIG_PCMCIA +bool 'PCMCIA network device support' CONFIG_NET_PCMCIA +if [ "$CONFIG_NET_PCMCIA" = "y" ]; then + dep_tristate ' 3Com 3c589 PCMCIA support' CONFIG_PCMCIA_3C589 $CONFIG_PCMCIA + dep_tristate ' 3Com 3c574 PCMCIA support' CONFIG_PCMCIA_3C574 $CONFIG_PCMCIA + dep_tristate ' Fujitsu FMV-J18x PCMCIA support' CONFIG_PCMCIA_FMVJ18X $CONFIG_PCMCIA + dep_tristate ' NE2000 compatible PCMCIA support' CONFIG_PCMCIA_PCNET $CONFIG_PCMCIA + dep_tristate ' New Media PCMCIA support' CONFIG_PCMCIA_NMCLAN $CONFIG_PCMCIA + dep_tristate ' SMC 91Cxx PCMCIA support' CONFIG_PCMCIA_SMC91C92 $CONFIG_PCMCIA + dep_tristate ' Xircom 16-bit PCMCIA support' CONFIG_PCMCIA_XIRC2PS $CONFIG_PCMCIA -if [ "$CONFIG_PCMCIA_3C589" = "y" -o "$CONFIG_PCMCIA_RAYCS" = "y" -o "$CONFIG_PCMCIA_PCNET" = "y" ]; then + # if [ "$CONFIG_CARDBUS" = "y" ]; then + # dep_tristate ' 3Com 3c575 CardBus support' CONFIG_PCMCIA_3C575 $CONFIG_PCMCIA + # dep_tristate ' DEC Tulip CardBus support' CONFIG_PCMCIA_TULIP $CONFIG_PCMCIA + # dep_tristate ' SMC EPIC CardBus support' CONFIG_PCMCIA_EPIC100 $CONFIG_PCMCIA + # fi + + dep_tristate ' Aviator/Raytheon 2.4MHz wireless support' CONFIG_PCMCIA_RAYCS $CONFIG_PCMCIA + dep_tristate ' Xircom Netwave AirSurfer wireless support' CONFIG_PCMCIA_NETWAVE $CONFIG_PCMCIA + dep_tristate ' AT&T/Lucent Wavelan wireless support' CONFIG_PCMCIA_WAVELAN $CONFIG_PCMCIA +fi + +if [ "$CONFIG_PCMCIA_3C589" = "y" -o "$CONFIG_PCMCIA_3C574" = "y" -o \ + "$CONFIG_PCMCIA_FMVJ18X" = "y" -o "$CONFIG_PCMCIA_PCNET" = "y" -o \ + "$CONFIG_PCMCIA_NMCLAN" = "y" -o "$CONFIG_PCMCIA_SMC91C92" = "y" -o \ + "$CONFIG_PCMCIA_XIRC2PS" = "y" -o "$CONFIG_PCMCIA_RAYCS" = "y" -o \ + "$CONFIG_PCMCIA_NETWAVE" = "y" -o "$CONFIG_PCMCIA_WAVELAN" = "y" ]; then define_bool CONFIG_PCMCIA_NETCARD y fi diff --git a/drivers/net/pcmcia/Makefile b/drivers/net/pcmcia/Makefile index 3b59b1b93..779abb20c 100644 --- a/drivers/net/pcmcia/Makefile +++ b/drivers/net/pcmcia/Makefile @@ -13,22 +13,81 @@ O_OBJS := M_OBJS := MOD_LIST_NAME := PCMCIA_MODULES +#CFLAGS_3c575_cb.o = -DCARDBUS +#CFLAGS_tulip_cb.o = -DCARDBUS + +ifeq ($(CONFIG_PCMCIA_3C589),y) + O_OBJS += 3c589_cs.o +else + ifeq ($(CONFIG_PCMCIA_3C589),m) + M_OBJS += 3c589_cs.o + endif +endif + +ifeq ($(CONFIG_PCMCIA_3C574),y) + O_OBJS += 3c574_cs.o +else + ifeq ($(CONFIG_PCMCIA_3C574),m) + M_OBJS += 3c574_cs.o + endif +endif + +ifeq ($(CONFIG_PCMCIA_FMVJ18X),y) + O_OBJS += fmvj18x_cs.o +else + ifeq ($(CONFIG_PCMCIA_FMVJ18X),m) + M_OBJS += fmvj18x_cs.o + endif +endif + +ifeq ($(CONFIG_PCMCIA_NMCLAN),y) + O_OBJS += nmclan_cs.o +else + ifeq ($(CONFIG_PCMCIA_NMCLAN),m) + M_OBJS += nmclan_cs.o + endif +endif + ifeq ($(CONFIG_PCMCIA_PCNET),y) O_OBJS += pcnet_cs.o else ifeq ($(CONFIG_PCMCIA_PCNET),m) - MX_OBJS += pcnet_cs.o + M_OBJS += pcnet_cs.o endif endif -ifeq ($(CONFIG_PCMCIA_3C589),y) - O_OBJS += 3c589_cs.o +ifeq ($(CONFIG_PCMCIA_SMC91C92),y) + O_OBJS += smc91c92_cs.o else - ifeq ($(CONFIG_PCMCIA_3C589),m) - MX_OBJS += 3c589_cs.o + ifeq ($(CONFIG_PCMCIA_SMC91C92),m) + M_OBJS += smc91c92_cs.o endif endif +#ifeq ($(CONFIG_PCMCIA_3C575),y) +# O_OBJS += 3c575_cb.o +#else +# ifeq ($(CONFIG_PCMCIA_3C575),m) +# M_OBJS += 3c575_cb.o +# endif +#endif + +#ifeq ($(CONFIG_PCMCIA_TULIP),y) +# O_OBJS += tulip_cb.o +#else +# ifeq ($(CONFIG_PCMCIA_TULIP),m) +# M_OBJS += tulip_cb.o +# endif +#endif + +#ifeq ($(CONFIG_PCMCIA_EPIC100),y) +# O_OBJS += epic100_cb.o +#else +# ifeq ($(CONFIG_PCMCIA_EPIC100),m) +# M_OBJS += epic100_cb.o +# endif +#endif + ifeq ($(CONFIG_PCMCIA_RAYCS),y) OX_OBJS += ray_cs.o else @@ -37,4 +96,23 @@ else endif endif +ifeq ($(CONFIG_PCMCIA_NETWAVE),y) + OX_OBJS += netwave_cs.o +else + ifeq ($(CONFIG_PCMCIA_NETWAVE),m) + M_OBJS += netwave_cs.o + endif +endif + +ifeq ($(CONFIG_PCMCIA_WAVELAN),y) + OX_OBJS += wavelan_cs.o +else + ifeq ($(CONFIG_PCMCIA_WAVELAN),m) + M_OBJS += wavelan_cs.o + endif +endif + include $(TOPDIR)/Rules.make + +#epic100_cb.o: ../epic100.c +# $(CC) $(CFLAGS) -DCARDBUS -c -o $@ ../epic100.c diff --git a/drivers/net/pcmcia/fmvj18x_cs.c b/drivers/net/pcmcia/fmvj18x_cs.c new file mode 100644 index 000000000..82e3ce57a --- /dev/null +++ b/drivers/net/pcmcia/fmvj18x_cs.c @@ -0,0 +1,1205 @@ +/*====================================================================== + fmvj18x_cs.c,v 1.9 1996/08/06 03:13:53 root Exp + + A fmvj18x (and its compatibles) PCMCIA client driver + + Contributed by Shingo Fujimoto, shingo@flab.fujitsu.co.jp + + TDK LAK-CD021 and CONTEC C-NET(PC)C support added by + Nobuhiro Katayama, kata-n@po.iijnet.or.jp + + The PCMCIA client code is based on code written by David Hinds. + Network code is based on the "FMV-18x driver" by Yutaka TAMIYA + but is actually largely Donald Becker's AT1700 driver, which + carries the following attribution: + + Written 1993-94 by Donald Becker. + + Copyright 1993 United States Government as represented by the + Director, National Security Agency. + + This software may be used and distributed according to the terms + of the GNU Public License, incorporated herein by reference. + + The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O + Center of Excellence in Space Data and Information Sciences + Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 + +======================================================================*/ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/ptrace.h> +#include <linux/malloc.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/interrupt.h> +#include <linux/in.h> +#include <linux/delay.h> +#include <asm/io.h> +#include <asm/system.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/if_arp.h> +#include <linux/ioport.h> + +#include <pcmcia/version.h> +#include <pcmcia/cs_types.h> +#include <pcmcia/cs.h> +#include <pcmcia/cistpl.h> +#include <pcmcia/ciscode.h> +#include <pcmcia/ds.h> + +/* + All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If + you do not define PCMCIA_DEBUG at all, all the debug code will be + left out. If you compile with PCMCIA_DEBUG=0, the debug code will + be present but disabled -- but it can then be enabled for specific + modules at load time with a 'pc_debug=#' option to insmod. +*/ +#ifdef PCMCIA_DEBUG +static int pc_debug = PCMCIA_DEBUG; +MODULE_PARM(pc_debug, "i"); +#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) +#else +#define DEBUG(n, args...) +#endif + +/* + For debugging this driver you may need more information. + To enable printing registers or status, set 'fmvj18x_debug=#' option . + */ +#ifdef FMVJ18X_DEBUG +static int fmvj18x_debug = FMVJ18X_DEBUG; +#else +static int fmvj18x_debug = 2; +#endif /* FMVJ18X_DEBUG */ + +/* Bit map of interrupts to choose from */ +/* This means pick from 15, 14, 12, 11, 10, 9, 7, 5, 4, and 3 */ +static u_int irq_mask = 0xdeb8; +static int irq_list[4] = { -1 }; + +/* SRAM configuration */ +/* 0:4KB*2 TX buffer else:8KB*2 TX buffer */ +static int sram_config = 0; + +MODULE_PARM(irq_mask, "i"); +MODULE_PARM(irq_list, "1-4i"); +MODULE_PARM(sram_config, "i"); + +/*====================================================================*/ +/* + driver version infomation + */ +#ifdef PCMCIA_DEBUG +static char *version = + "fmvj18x_cs.c,v 1.9 1996/08/06 03:13:53 root Exp"; +#endif + +/*====================================================================*/ +/* + PCMCIA event handlers + */ +static void fmvj18x_config(dev_link_t *link); +static void fmvj18x_release(u_long arg); +static int fmvj18x_event(event_t event, int priority, + event_callback_args_t *args); +static int fmvj18x_init(struct net_device *dev); +static dev_link_t *fmvj18x_attach(void); +static void fmvj18x_detach(dev_link_t *); + +/* + LAN controler(MBH86960A) specific routines + */ +static int fjn_config(struct net_device *dev, struct ifmap *map); +static int fjn_open(struct net_device *dev); +static int fjn_close(struct net_device *dev); +static int fjn_start_xmit(struct sk_buff *skb, struct net_device *dev); +static void fjn_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static void fjn_rx(struct net_device *dev); +static void fjn_reset(struct net_device *dev); +static struct net_device_stats *fjn_get_stats(struct net_device *dev); +static void set_rx_mode(struct net_device *dev); + +static dev_info_t dev_info = "fmvj18x_cs"; +static dev_link_t *dev_list = NULL; + +/* + card type + */ +typedef enum { MBH10302, MBH10304, TDK, CONTEC, LA501 } cardtype_t; + +/* + driver specific data structure +*/ +typedef struct local_info_t { + dev_node_t node; + struct net_device_stats stats; + long open_time; + uint tx_started:1; + uint tx_queue; + u_short tx_queue_len; + cardtype_t cardtype; + u_short sent; + u_char mc_filter[8]; +} local_info_t; + +#define MC_FILTERBREAK 64 + +/*====================================================================*/ +/* + ioport offset from the base address + */ +#define TX_STATUS 0 /* transmit status register */ +#define RX_STATUS 1 /* receive status register */ +#define TX_INTR 2 /* transmit interrupt mask register */ +#define RX_INTR 3 /* receive interrupt mask register */ +#define TX_MODE 4 /* transmit mode register */ +#define RX_MODE 5 /* receive mode register */ +#define CONFIG_0 6 /* configuration register 0 */ +#define CONFIG_1 7 /* configuration register 1 */ + +#define NODE_ID 8 /* node ID register (bank 0) */ +#define MAR_ADR 8 /* multicast address registers (bank 1) */ + +#define DATAPORT 8 /* buffer mem port registers (bank 2) */ +#define TX_START 10 /* transmit start register */ +#define COL_CTRL 11 /* 16 collision control register */ +#define BMPR12 12 /* reserved */ +#define BMPR13 13 /* reserved */ +#define RX_SKIP 14 /* skip received packet register */ + +#define LAN_CTRL 16 /* LAN card control register */ + +#define MAC_ID 0x1a /* hardware address */ + +/* + control bits + */ +#define ENA_TMT_OK 0x80 +#define ENA_TMT_REC 0x20 +#define ENA_COL 0x04 +#define ENA_16_COL 0x02 +#define ENA_TBUS_ERR 0x01 + +#define ENA_PKT_RDY 0x80 +#define ENA_BUS_ERR 0x40 +#define ENA_LEN_ERR 0x08 +#define ENA_ALG_ERR 0x04 +#define ENA_CRC_ERR 0x02 +#define ENA_OVR_FLO 0x01 + +/* flags */ +#define F_TMT_RDY 0x80 /* can accept new packet */ +#define F_NET_BSY 0x40 /* carrier is detected */ +#define F_TMT_OK 0x20 /* send packet successfully */ +#define F_SRT_PKT 0x10 /* short packet error */ +#define F_COL_ERR 0x04 /* collision error */ +#define F_16_COL 0x02 /* 16 collision error */ +#define F_TBUS_ERR 0x01 /* bus read error */ + +#define F_PKT_RDY 0x80 /* packet(s) in buffer */ +#define F_BUS_ERR 0x40 /* bus read error */ +#define F_LEN_ERR 0x08 /* short packet */ +#define F_ALG_ERR 0x04 /* frame error */ +#define F_CRC_ERR 0x02 /* CRC error */ +#define F_OVR_FLO 0x01 /* overflow error */ + +#define F_BUF_EMP 0x40 /* receive buffer is empty */ + +#define F_SKP_PKT 0x05 /* drop packet in buffer */ + +/* default bitmaps */ +#define D_TX_INTR ( ENA_TMT_OK ) +#define D_RX_INTR ( ENA_PKT_RDY | ENA_LEN_ERR \ + | ENA_ALG_ERR | ENA_CRC_ERR | ENA_OVR_FLO ) +#define TX_STAT_M ( F_TMT_RDY ) +#define RX_STAT_M ( F_PKT_RDY | F_LEN_ERR \ + | F_ALG_ERR | F_CRC_ERR | F_OVR_FLO ) + +/* commands */ +#define D_TX_MODE 0x06 /* no tests, detect carrier */ +#define ID_MATCHED 0x02 /* (RX_MODE) */ +#define RECV_ALL 0x03 /* (RX_MODE) */ +#define CONFIG0_DFL 0x5a /* 16bit bus, 4K x 2 Tx queues */ +#define CONFIG0_DFL_1 0x5e /* 16bit bus, 8K x 2 Tx queues */ +#define CONFIG0_RST 0xda /* Data Link Controler off (CONFIG_0) */ +#define CONFIG0_RST_1 0xde /* Data Link Controler off (CONFIG_0) */ +#define BANK_0 0xa0 /* bank 0 (CONFIG_1) */ +#define BANK_1 0xa4 /* bank 1 (CONFIG_1) */ +#define BANK_2 0xa8 /* bank 2 (CONFIG_1) */ +#define CHIP_OFF 0x80 /* contrl chip power off (CONFIG_1) */ +#define DO_TX 0x80 /* do transmit packet */ +#define SEND_PKT 0x81 /* send a packet */ +#define AUTO_MODE 0x07 /* Auto skip packet on 16 col detected */ +#define MANU_MODE 0x03 /* Stop and skip packet on 16 col */ +#define TDK_AUTO_MODE 0x47 /* Auto skip packet on 16 col detected */ +#define TDK_MANU_MODE 0x43 /* Stop and skip packet on 16 col */ +#define INTR_OFF 0x0d /* LAN controler ignores interrupts */ +#define INTR_ON 0x1d /* LAN controler will catch interrupts */ + +/*====================================================================== + + This bit of code is used to avoid unregistering network devices + at inappropriate times. 2.2 and later kernels are fairly picky + about when this can happen. + +======================================================================*/ + +static void flush_stale_links(void) +{ + dev_link_t *link, *next; + for (link = dev_list; link; link = next) { + next = link->next; + if (link->state & DEV_STALE_LINK) + fmvj18x_detach(link); + } +} + +/*====================================================================*/ + +static void cs_error(client_handle_t handle, int func, int ret) +{ + error_info_t err = { func, ret }; + CardServices(ReportError, handle, &err); +} + +/*====================================================================*/ + +static dev_link_t *fmvj18x_attach(void) +{ + client_reg_t client_reg; + dev_link_t *link; + struct net_device *dev; + int i, ret; + + DEBUG(0, "fmvj18x_attach()\n"); + flush_stale_links(); + + /* Initialize the dev_link_t structure */ + link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL); + memset(link, 0, sizeof(struct dev_link_t)); + + link->release.function = &fmvj18x_release; + link->release.data = (u_long)link; + + /* The io structure describes IO port mapping */ + link->io.NumPorts1 = 32; + link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; + link->io.IOAddrLines = 5; + + /* Interrupt setup */ + link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; + link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID; + if (irq_list[0] == -1) + link->irq.IRQInfo2 = irq_mask; + else + for (i = 0; i < 4; i++) + link->irq.IRQInfo2 |= 1 << irq_list[i]; + link->irq.Handler = &fjn_interrupt; + + /* General socket configuration */ + link->conf.Attributes = CONF_ENABLE_IRQ; + link->conf.Vcc = 50; + link->conf.IntType = INT_MEMORY_AND_IO; + + /* Make up a FMVJ18x specific data structure */ + dev = kmalloc(sizeof(struct net_device), GFP_KERNEL); + memset(dev, 0, sizeof(struct net_device)); + dev->priv = kmalloc(sizeof(local_info_t), GFP_KERNEL); + memset(dev->priv, 0, sizeof(local_info_t)); + + /* The FMVJ18x specific entries in the device structure. */ + dev->hard_start_xmit = &fjn_start_xmit; + dev->set_config = &fjn_config; + dev->get_stats = &fjn_get_stats; + dev->set_multicast_list = &set_rx_mode; + ether_setup(dev); + dev->name = ((local_info_t *)dev->priv)->node.dev_name; + dev->init = &fmvj18x_init; + dev->open = &fjn_open; + dev->stop = &fjn_close; + dev->tbusy = 0xFF; + link->priv = link->irq.Instance = dev; + + /* Register with Card Services */ + link->next = dev_list; + dev_list = link; + client_reg.dev_info = &dev_info; + client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; + client_reg.EventMask = + CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | + CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | + CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; + client_reg.event_handler = &fmvj18x_event; + client_reg.Version = 0x0210; + client_reg.event_callback_args.client_data = link; + ret = CardServices(RegisterClient, &link->handle, &client_reg); + if (ret != 0) { + cs_error(link->handle, RegisterClient, ret); + fmvj18x_detach(link); + return NULL; + } + + return link; +} /* fmvj18x_attach */ + +/*====================================================================*/ + +static void fmvj18x_detach(dev_link_t *link) +{ + dev_link_t **linkp; + long flags; + + DEBUG(0, "fmvj18x_detach(0x%p)\n", link); + + /* Locate device structure */ + for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) + if (*linkp == link) break; + if (*linkp == NULL) + return; + + save_flags(flags); + cli(); + if (link->state & DEV_RELEASE_PENDING) { + del_timer(&link->release); + link->state &= ~DEV_RELEASE_PENDING; + } + restore_flags(flags); + + if (link->state & DEV_CONFIG) { + fmvj18x_release((u_long)link); + if (link->state & DEV_STALE_CONFIG) { + link->state |= DEV_STALE_LINK; + return; + } + } + + /* Break the link with Card Services */ + if (link->handle) + CardServices(DeregisterClient, link->handle); + + /* Unlink device structure, free pieces */ + *linkp = link->next; + if (link->priv) { + struct net_device *dev = link->priv; + if (link->dev != NULL) + unregister_netdev(dev); + if (dev->priv) + kfree(dev->priv); + kfree(dev); + } + kfree(link); + +} /* fmvj18x_detach */ + +/*====================================================================*/ + +#define CS_CHECK(fn, args...) \ +while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed + +static void fmvj18x_config(dev_link_t *link) +{ + client_handle_t handle; + tuple_t tuple; + cisparse_t parse; + struct net_device *dev; + u_short buf[32]; + int i, last_fn, last_ret; + ioaddr_t ioaddr; + cardtype_t cardtype; + char *card_name = "unknown"; + u_char *node_id; + + handle = link->handle; + dev =link->priv; + + DEBUG(0, "fmvj18x_config(0x%p)\n", link); + + /* + This reads the card's CONFIG tuple to find its configuration + registers. + */ + tuple.DesiredTuple = CISTPL_CONFIG; + CS_CHECK(GetFirstTuple, handle, &tuple); + tuple.TupleData = (u_char *)buf; + tuple.TupleDataMax = 64; + tuple.TupleOffset = 0; + CS_CHECK(GetTupleData, handle, &tuple); + CS_CHECK(ParseTuple, handle, &tuple, &parse); + + /* Configure card */ + link->state |= DEV_CONFIG; + + link->conf.ConfigBase = parse.config.base; + link->conf.Present = parse.config.rmask[0]; + + tuple.DesiredTuple = CISTPL_FUNCE; + tuple.TupleOffset = 0; + if (CardServices(GetFirstTuple, handle, &tuple) == CS_SUCCESS) { + /* Yes, I have CISTPL_FUNCE. Let's check CISTPL_MANFID */ + tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; + CS_CHECK(GetFirstTuple, handle, &tuple); + CS_CHECK(GetTupleData, handle, &tuple); + CS_CHECK(ParseTuple, handle, &tuple, &parse); + link->conf.ConfigIndex = parse.cftable_entry.index; + tuple.DesiredTuple = CISTPL_MANFID; + CS_CHECK(GetFirstTuple, handle, &tuple); + CS_CHECK(GetTupleData, handle, &tuple); + + switch (le16_to_cpu(buf[0])) { + case MANFID_TDK: + cardtype = TDK; + break; + case MANFID_CONTEC: + cardtype = CONTEC; + break; + case MANFID_FUJITSU: + if (le16_to_cpu(buf[1]) == PRODID_FUJITSU_MBH10302) + cardtype = MBH10302; + else if (le16_to_cpu(buf[1]) == PRODID_FUJITSU_MBH10304) + cardtype = MBH10304; + else + cardtype = LA501; + break; + default: + cardtype = MBH10304; + } + } else { + /* old type card */ + cardtype = MBH10302; + link->conf.ConfigIndex = 1; + } + CS_CHECK(RequestIO, link->handle, &link->io); + CS_CHECK(RequestIRQ, link->handle, &link->irq); + CS_CHECK(RequestConfiguration, link->handle, &link->conf); + dev->irq = link->irq.AssignedIRQ; + dev->base_addr = link->io.BasePort1; + dev->tbusy = 0; + if (register_netdev(dev) != 0) { + printk(KERN_NOTICE "fmvj18x_cs: register_netdev() failed\n"); + goto failed; + } + + ioaddr = dev->base_addr; + + /* Power On chip and select bank 0 */ + outb(BANK_0, ioaddr + CONFIG_1); + /* Reset controler */ + if( sram_config == 0 ) + outb(CONFIG0_RST, ioaddr + CONFIG_0); + else + outb(CONFIG0_RST_1, ioaddr + CONFIG_0); + + /* Set hardware address */ + switch (cardtype) { + case MBH10304: + case TDK: + case LA501: + case CONTEC: + tuple.DesiredTuple = CISTPL_FUNCE; + tuple.TupleOffset = 0; + CS_CHECK(GetFirstTuple, handle, &tuple); + tuple.TupleOffset = 0; + CS_CHECK(GetTupleData, handle, &tuple); + if (cardtype == MBH10304) { + /* MBH10304's CIS_FUNCE is corrupted */ + node_id = &(tuple.TupleData[5]); + card_name = "FMV-J182"; + } else { + while (tuple.TupleData[0] != CISTPL_FUNCE_LAN_NODE_ID ) { + CS_CHECK(GetNextTuple, handle, &tuple) ; + CS_CHECK(GetTupleData, handle, &tuple) ; + } + node_id = &(tuple.TupleData[2]); + if( cardtype == TDK ) { + card_name = "TDK LAK-CD021"; + } else if( cardtype == LA501 ) { + card_name = "LA501"; + } else { + card_name = "C-NET(PC)C"; + } + } + /* Read MACID from CIS */ + for (i = 0; i < 6; i++) + dev->dev_addr[i] = node_id[i]; + break; + case MBH10302: + default: + /* Read MACID from register */ + for (i = 0; i < 6; i++) + dev->dev_addr[i] = inb(ioaddr + MAC_ID + i); + card_name = "FMV-J181"; + break; + } + + link->dev = &((local_info_t *)dev->priv)->node; + link->state &= ~DEV_CONFIG_PENDING; + + ((struct local_info_t *)dev->priv)->cardtype = cardtype ; + /* print current configuration */ + printk(KERN_INFO "%s: %s, sram %s, port %#3lx, irq %d, hw_addr ", + dev->name, card_name, sram_config == 0 ? "4K TX*2" : "8K TX*2", + dev->base_addr, dev->irq); + for (i = 0; i < 6; i++) + printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n")); + + return; + +cs_failed: + /* All Card Services errors end up here */ + cs_error(link->handle, last_fn, last_ret); +failed: + fmvj18x_release((u_long)link); + +} /* fmvj18x_config */ + +/*====================================================================*/ + +static void fmvj18x_release(u_long arg) +{ + dev_link_t *link = (dev_link_t *)arg; + + DEBUG(0, "fmvj18x_release(0x%p)\n", link); + + /* + If the device is currently in use, we won't release until it + is actually closed. + */ + if (link->open) { + DEBUG(1, "fmvj18x_cs: release postponed, '%s' " + "still open\n", link->dev->dev_name); + link->state |= DEV_STALE_CONFIG; + return; + } + + /* Don't bother checking to see if these succeed or not */ + CardServices(ReleaseWindow, link->win); + CardServices(ReleaseConfiguration, link->handle); + CardServices(ReleaseIO, link->handle, &link->io); + CardServices(ReleaseIRQ, link->handle, &link->irq); + + link->state &= ~(DEV_CONFIG | DEV_RELEASE_PENDING); + +} /* fmvj18x_release */ + +/*====================================================================*/ + +static int fmvj18x_event(event_t event, int priority, + event_callback_args_t *args) +{ + dev_link_t *link = args->client_data; + struct net_device *dev = link->priv; + + DEBUG(1, "fmvj18x_event(0x%06x)\n", event); + + switch (event) { + case CS_EVENT_CARD_REMOVAL: + link->state &= ~DEV_PRESENT; + if (link->state & DEV_CONFIG) { + dev->tbusy = 0xFF; + dev->start = 0; + link->release.expires = jiffies + HZ/20; + add_timer(&link->release); + } + break; + case CS_EVENT_CARD_INSERTION: + link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; + fmvj18x_config(link); + break; + case CS_EVENT_PM_SUSPEND: + link->state |= DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_RESET_PHYSICAL: + if (link->state & DEV_CONFIG) { + if (link->open) { + dev->tbusy = 0xFF; + dev->start = 0; + } + CardServices(ReleaseConfiguration, link->handle); + } + break; + case CS_EVENT_PM_RESUME: + link->state &= ~DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_CARD_RESET: + if (link->state & DEV_CONFIG) { + CardServices(RequestConfiguration, link->handle, &link->conf); + if (link->open) { + dev->tbusy = 0; + dev->start = 1; + fjn_reset(dev); + } + } + break; + } + return 0; +} /* fmvj18x_event */ + +static int fmvj18x_init(struct net_device *dev) +{ + return 0; +} /* fmvj18x_init */ + +/*====================================================================*/ + +static int __init init_fmvj18x_cs(void) +{ + servinfo_t serv; + DEBUG(0, "%s\n", version); + CardServices(GetCardServicesInfo, &serv); + if (serv.Revision != CS_RELEASE_CODE) { + printk(KERN_NOTICE "fmvj18x: Card Services release " + "does not match!\n"); + return -1; + } + register_pccard_driver(&dev_info, &fmvj18x_attach, &fmvj18x_detach); + return 0; +} + +static void __exit exit_fmvj18x_cs(void) +{ + DEBUG(0, "fmvj18x_cs: unloading\n"); + unregister_pccard_driver(&dev_info); + while (dev_list != NULL) + fmvj18x_detach(dev_list); +} + +module_init(init_fmvj18x_cs); +module_exit(exit_fmvj18x_cs); + +/*====================================================================*/ + +static void fjn_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *)dev_id; + ioaddr_t ioaddr; + local_info_t *lp; + unsigned short tx_stat, rx_stat; + + if (dev == NULL) { + printk(KERN_NOTICE "fjn_interrupt(): irq %d for " + "unknown device.\n", irq); + return; + } + if (dev->interrupt) { + printk(KERN_NOTICE "%s: re-entering the interrupt handler.\n", + dev->name); + return; + } + dev->interrupt = 1; + lp = (struct local_info_t *)dev->priv; + ioaddr = dev->base_addr; + + /* avoid multiple interrupts */ + outw(0x0000, ioaddr + TX_INTR); + + /* wait for a while */ + udelay(1); + + /* get status */ + tx_stat = inb(ioaddr + TX_STATUS); + rx_stat = inb(ioaddr + RX_STATUS); + + /* clear status */ + outb(tx_stat, ioaddr + TX_STATUS); + outb(rx_stat, ioaddr + RX_STATUS); + + if (fmvj18x_debug > 4) { + printk(KERN_DEBUG "%s: interrupt, rx_status %02x.\n", + dev->name, rx_stat); + printk(KERN_DEBUG " tx_status %02x.\n", + tx_stat); + } + + if (rx_stat || (inb(ioaddr + RX_MODE) & F_BUF_EMP) == 0) { + /* there is packet(s) in rx buffer */ + fjn_rx(dev); + } + if (tx_stat & F_TMT_RDY) { + lp->stats.tx_packets += lp->sent ; + lp->sent = 0 ; + if (lp->tx_queue) { + outb(DO_TX | lp->tx_queue, ioaddr + TX_START); + lp->sent = lp->tx_queue ; + lp->tx_queue = 0; + lp->tx_queue_len = 0; + dev->trans_start = jiffies; + dev->tbusy = 0; + mark_bh(NET_BH); /* Inform upper layers. */ + } else { + lp->tx_started = 0; + dev->tbusy = 0; + mark_bh(NET_BH); /* Inform upper layers. */ + } + } + if (fmvj18x_debug > 4) { + printk(KERN_DEBUG "%s: exiting interrupt,\n", dev->name); + printk(KERN_DEBUG " tx_status %02x, rx_status %02x.\n", + tx_stat, rx_stat); + } + + dev->interrupt = 0; + outb(D_TX_INTR, ioaddr + TX_INTR); + outb(D_RX_INTR, ioaddr + RX_INTR); + + return; +} /* fjn_interrupt */ + +/*====================================================================*/ + +static int fjn_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct local_info_t *lp = (struct local_info_t *)dev->priv; + ioaddr_t ioaddr = dev->base_addr; + + if (dev->tbusy) { + /* If we get here, some higher level has decided we are broken. + There should really be a "kick me" function call instead. */ + int tickssofar = jiffies - dev->trans_start; + if (tickssofar < 10) + return 1; + printk(KERN_NOTICE "%s: transmit timed out with status %04x, %s?\n", + dev->name, htons(inw(ioaddr + TX_STATUS)), + inb(ioaddr + TX_STATUS) & F_TMT_RDY + ? "IRQ conflict" : "network cable problem"); + printk(KERN_NOTICE "%s: timeout registers: %04x %04x %04x " + "%04x %04x %04x %04x %04x.\n", + dev->name, htons(inw(ioaddr + 0)), + htons(inw(ioaddr + 2)), htons(inw(ioaddr + 4)), + htons(inw(ioaddr + 6)), htons(inw(ioaddr + 8)), + htons(inw(ioaddr +10)), htons(inw(ioaddr +12)), + htons(inw(ioaddr +14))); + lp->stats.tx_errors++; + /* ToDo: We should try to restart the adaptor... */ + cli(); + + fjn_reset(dev); + + lp->tx_started = 0; + lp->tx_queue = 0; + lp->tx_queue_len = 0; + lp->sent = 0; + lp->open_time = jiffies; + dev->interrupt = 0; + dev->tbusy = 0; + dev->start = 1; + + sti(); + } + + /* Block a timer-based transmit from overlapping. This could better be + done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ + if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) + printk(KERN_NOTICE "%s: Transmitter access conflict.\n", dev->name); + else { + short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; + unsigned char *buf = skb->data; + + if (length > ETH_FRAME_LEN) { + if (fmvj18x_debug) + printk(KERN_NOTICE "%s: Attempting to send a large packet" + " (%d bytes).\n", dev->name, length); + return 1; + } + + if (fmvj18x_debug > 4) + printk(KERN_DEBUG "%s: Transmitting a packet of length %lu.\n", + dev->name, (unsigned long)skb->len); + lp->stats.tx_bytes += skb->len; + + /* Disable both interrupts. */ + outw(0x0000, ioaddr + TX_INTR); + + /* wait for a while */ + udelay(1); + + outw(length, ioaddr + DATAPORT); + outsw(ioaddr + DATAPORT, buf, (length + 1) >> 1); + + lp->tx_queue++; + lp->tx_queue_len += ((length+3) & ~1); + + if (lp->tx_started == 0) { + /* If the Tx is idle, always trigger a transmit. */ + outb(DO_TX | lp->tx_queue, ioaddr + TX_START); + lp->sent = lp->tx_queue ; + lp->tx_queue = 0; + lp->tx_queue_len = 0; + dev->trans_start = jiffies; + lp->tx_started = 1; + dev->tbusy = 0; + } else { + if( sram_config == 0 ) { + if (lp->tx_queue_len < (4096 - (ETH_FRAME_LEN +2)) ) + /* Yes, there is room for one more packet. */ + dev->tbusy = 0; + } else { + if (lp->tx_queue_len < (8192 - (ETH_FRAME_LEN +2)) && + lp->tx_queue < 127 ) + /* Yes, there is room for one more packet. */ + dev->tbusy = 0; + } + } + + /* Re-enable interrupts */ + outb(D_TX_INTR, ioaddr + TX_INTR); + outb(D_RX_INTR, ioaddr + RX_INTR); + } + dev_kfree_skb (skb); + + return 0; +} /* fjn_start_xmit */ + +/*====================================================================*/ + +static void fjn_reset(struct net_device *dev) +{ + struct local_info_t *lp = (struct local_info_t *)dev->priv; + ioaddr_t ioaddr = dev->base_addr; + int i; + + if (fmvj18x_debug > 4) { + printk(KERN_DEBUG "fjn_reset(%s) called.\n",dev->name); + } + + /* Power On chip and select bank 0 */ + outb(BANK_0, ioaddr + CONFIG_1); + /* Reset buffers */ + if( sram_config == 0 ) + outb(CONFIG0_RST, ioaddr + CONFIG_0); + else + outb(CONFIG0_RST_1, ioaddr + CONFIG_0); + + /* Set Tx modes */ + outb(D_TX_MODE, ioaddr + TX_MODE); + /* set Rx modes */ + outb(ID_MATCHED, ioaddr + RX_MODE); + + /* Set hardware address */ + for (i = 0; i < 6; i++) + outb(dev->dev_addr[i], ioaddr + NODE_ID + i); + + if (fmvj18x_debug > 4) { + printk(KERN_DEBUG "node id: "); + for (i = 0; i < 6; i++) + printk("%02X ",inb(ioaddr + NODE_ID + i)); + printk("\n"); + } + /* Switch to bank 1 */ + outb(BANK_1, ioaddr + CONFIG_1); + + /* set the multicast table to accept none. */ + for (i = 0; i < 6; i++) + outb(0x00, ioaddr + MAR_ADR + i); + + /* Switch to bank 2 (runtime mode) */ + outb(BANK_2, ioaddr + CONFIG_1); + + /* set 16col ctrl bits */ + if( lp->cardtype == TDK ) + outb(TDK_AUTO_MODE, ioaddr + COL_CTRL); + else + outb(AUTO_MODE, ioaddr + COL_CTRL); + + /* clear Reserved Regs */ + outb(0x00, ioaddr + BMPR12); + outb(0x00, ioaddr + BMPR13); + + /* reset Skip packet reg. */ + outb(0x01, ioaddr + RX_SKIP); + + /* Enable Tx and Rx */ + if( sram_config == 0 ) + outb(CONFIG0_DFL, ioaddr + CONFIG_0); + else + outb(CONFIG0_DFL_1, ioaddr + CONFIG_0); + + /* Init receive pointer ? */ + inw(ioaddr + DATAPORT); + inw(ioaddr + DATAPORT); + + /* Clear all status */ + outb(0xff, ioaddr + TX_STATUS); + outb(0xff, ioaddr + RX_STATUS); + + if( lp->cardtype != TDK ) + outb(INTR_OFF, ioaddr + LAN_CTRL); + + /* Turn on Rx interrupts */ + outb(D_TX_INTR, ioaddr + TX_INTR); + outb(D_RX_INTR, ioaddr + RX_INTR); + + /* Turn on interrupts from LAN card controler */ + if( lp->cardtype != TDK ) + outb(INTR_ON, ioaddr + LAN_CTRL); +} /* fjn_reset */ + +/*====================================================================*/ + +static void fjn_rx(struct net_device *dev) +{ + struct local_info_t *lp = (struct local_info_t *)dev->priv; + ioaddr_t ioaddr = dev->base_addr; + int boguscount = 10; /* 5 -> 10: by agy 19940922 */ + + if (fmvj18x_debug > 4) + printk(KERN_DEBUG "%s: in rx_packet(), rx_status %02x.\n", + dev->name, inb(ioaddr + RX_STATUS)); + + while ((inb(ioaddr + RX_MODE) & F_BUF_EMP) == 0) { + u_short status = inw(ioaddr + DATAPORT); + + if (fmvj18x_debug > 4) + printk(KERN_DEBUG "%s: Rxing packet mode %02x status %04x.\n", + dev->name, inb(ioaddr + RX_MODE), status); +#ifndef final_version + if (status == 0) { + outb(F_SKP_PKT, ioaddr + RX_SKIP); + break; + } +#endif + if ((status & 0xF0) != 0x20) { /* There was an error. */ + lp->stats.rx_errors++; + if (status & F_LEN_ERR) lp->stats.rx_length_errors++; + if (status & F_ALG_ERR) lp->stats.rx_frame_errors++; + if (status & F_CRC_ERR) lp->stats.rx_crc_errors++; + if (status & F_OVR_FLO) lp->stats.rx_over_errors++; + } else { + u_short pkt_len = inw(ioaddr + DATAPORT); + /* Malloc up new buffer. */ + struct sk_buff *skb; + + if (pkt_len > 1550) { + printk(KERN_NOTICE "%s: The FMV-18x claimed a very " + "large packet, size %d.\n", dev->name, pkt_len); + outb(F_SKP_PKT, ioaddr + RX_SKIP); + lp->stats.rx_errors++; + break; + } + skb = dev_alloc_skb(pkt_len+2); + if (skb == NULL) { + printk(KERN_NOTICE "%s: Memory squeeze, dropping " + "packet (len %d).\n", dev->name, pkt_len); + outb(F_SKP_PKT, ioaddr + RX_SKIP); + lp->stats.rx_dropped++; + break; + } + skb->dev = dev; + + skb_reserve(skb, 2); + insw(ioaddr + DATAPORT, skb_put(skb, pkt_len), + (pkt_len + 1) >> 1); + skb->protocol = eth_type_trans(skb, dev); + + if (fmvj18x_debug > 5) { + int i; + printk(KERN_DEBUG "%s: Rxed packet of length %d: ", + dev->name, pkt_len); + for (i = 0; i < 14; i++) + printk(" %02x", skb->data[i]); + printk(".\n"); + } + + netif_rx(skb); + lp->stats.rx_packets++; + lp->stats.rx_bytes += skb->len; + } + if (--boguscount <= 0) + break; + } + + /* If any worth-while packets have been received, dev_rint() + has done a mark_bh(NET_BH) for us and will work on them + when we get to the bottom-half routine. */ +/* + if( lp->cardtype != TDK ) { + int i; + for (i = 0; i < 20; i++) { + if ((inb(ioaddr + RX_MODE) & F_BUF_EMP) == F_BUF_EMP) + break; + (void)inw(ioaddr + DATAPORT); /+ dummy status read +/ + outb(F_SKP_PKT, ioaddr + RX_SKIP); + } + + if (fmvj18x_debug > 5 && i > 0) + printk(KERN_DEBUG "%s: Exint Rx packet with mode %02x after" + " %d ticks.\n", dev->name, inb(ioaddr + RX_MODE), i); + } +*/ + + return; +} /* fjn_rx */ + +/*====================================================================*/ + +static int fjn_config(struct net_device *dev, struct ifmap *map){ + return 0; +} /* fjn_config */ + +static int fjn_open(struct net_device *dev) +{ + struct local_info_t *lp = (struct local_info_t *)dev->priv; + dev_link_t *link; + + if (fmvj18x_debug > 4) + printk(KERN_DEBUG "fjn_open('%s').\n", dev->name); + + for (link = dev_list; link; link = link->next) + if (link->priv == dev) break; + if (!DEV_OK(link)) + return -ENODEV; + + link->open++; + + fjn_reset(dev); + + lp->tx_started = 0; + lp->tx_queue = 0; + lp->tx_queue_len = 0; + lp->open_time = jiffies; + dev->interrupt = 0; + dev->tbusy = 0; + dev->start = 1; + + MOD_INC_USE_COUNT; + + return 0; +} /* fjn_open */ + +/*====================================================================*/ + +static int fjn_close(struct net_device *dev) +{ + ioaddr_t ioaddr = dev->base_addr; + struct local_info_t *lp = (struct local_info_t *)dev->priv; + dev_link_t *link; + + if (fmvj18x_debug > 4) + printk(KERN_DEBUG "fjn_open('%s').\n", dev->name); + + for (link = dev_list; link; link = link->next) + if (link->priv == dev) break; + if (link == NULL) + return -ENODEV; + + if (fmvj18x_debug > 2) + printk(KERN_DEBUG "%s: shutting down ethercard.\n", dev->name); + + ((struct local_info_t *)dev->priv)->open_time = 0; + + dev->tbusy = 1; + dev->start = 0; + + /* Set configuration register 0 to disable Tx and Rx. */ + if( sram_config == 0 ) + outb(CONFIG0_RST ,ioaddr + CONFIG_0); + else + outb(CONFIG0_RST_1 ,ioaddr + CONFIG_0); + + /* Update the statistics -- ToDo. */ + + /* Power-down the chip. Green, green, green! */ + outb(CHIP_OFF ,ioaddr + CONFIG_1); + + /* Set the ethernet adaptor disable IRQ */ + if( lp->cardtype != TDK ) + outb(INTR_OFF, ioaddr + LAN_CTRL); + + link->open--; + dev->start = 0; + if (link->state & DEV_STALE_CONFIG) { + link->release.expires = jiffies + HZ/20; + link->state |= DEV_RELEASE_PENDING; + add_timer(&link->release); + } + MOD_DEC_USE_COUNT; + + return 0; +} /* fjn_close */ + +/*====================================================================*/ + +static struct net_device_stats *fjn_get_stats(struct net_device *dev) +{ + local_info_t *lp = (local_info_t *)dev->priv; + return &lp->stats; +} /* fjn_get_stats */ + +/*====================================================================*/ + +/* + Set the multicast/promiscuous mode for this adaptor. +*/ + +/* The little-endian AUTODIN II ethernet CRC calculation. + N.B. Do not use for bulk data, use a table-based routine instead. + This is common code and should be moved to net/core/crc.c */ +static unsigned const ethernet_polynomial_le = 0xedb88320U; +static inline unsigned ether_crc_le(int length, unsigned char *data) +{ + unsigned int crc = 0xffffffff; /* Initial value. */ + while(--length >= 0) { + unsigned char current_octet = *data++; + int bit; + for (bit = 8; --bit >= 0; current_octet >>= 1) { + if ((crc ^ current_octet) & 1) { + crc >>= 1; + crc ^= ethernet_polynomial_le; + } else + crc >>= 1; + } + } + return crc; +} + +static void set_rx_mode(struct net_device *dev) +{ + ioaddr_t ioaddr = dev->base_addr; + struct local_info_t *lp = (struct local_info_t *)dev->priv; + unsigned char mc_filter[8]; /* Multicast hash filter */ + long flags; + int i; + + if (dev->flags & IFF_PROMISC) { + /* Unconditionally log net taps. */ + printk("%s: Promiscuous mode enabled.\n", dev->name); + memset(mc_filter, 0xff, sizeof(mc_filter)); + outb(3, ioaddr + RX_MODE); /* Enable promiscuous mode */ + } else if (dev->mc_count > MC_FILTERBREAK + || (dev->flags & IFF_ALLMULTI)) { + /* Too many to filter perfectly -- accept all multicasts. */ + memset(mc_filter, 0xff, sizeof(mc_filter)); + outb(2, ioaddr + RX_MODE); /* Use normal mode. */ + } else if (dev->mc_count == 0) { + memset(mc_filter, 0x00, sizeof(mc_filter)); + outb(1, ioaddr + RX_MODE); /* Ignore almost all multicasts. */ + } else { + struct dev_mc_list *mclist; + int i; + + memset(mc_filter, 0, sizeof(mc_filter)); + for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; + i++, mclist = mclist->next) + set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x3f, + mc_filter); + } + + save_flags(flags); + cli(); + if (memcmp(mc_filter, lp->mc_filter, sizeof(mc_filter))) { + int saved_bank = inb(ioaddr + CONFIG_1); + /* Switch to bank 1 and set the multicast table. */ + outb(0xe4, ioaddr + CONFIG_1); + for (i = 0; i < 8; i++) + outb(mc_filter[i], ioaddr + 8 + i); + memcpy(lp->mc_filter, mc_filter, sizeof(mc_filter)); + outb(saved_bank, ioaddr + CONFIG_1); + } + restore_flags(flags); +} diff --git a/drivers/net/pcmcia/i82593.h b/drivers/net/pcmcia/i82593.h new file mode 100644 index 000000000..6d66baf6d --- /dev/null +++ b/drivers/net/pcmcia/i82593.h @@ -0,0 +1,224 @@ +/* + * Definitions for Intel 82593 CSMA/CD Core LAN Controller + * The definitions are taken from the 1992 users manual with Intel + * order number 297125-001. + * + * /usr/src/pc/RCS/i82593.h,v 1.1 1996/07/17 15:23:12 root Exp + * + * Copyright 1994, Anders Klemets <klemets@it.kth.se> + * + * This software may be freely distributed for noncommercial purposes + * as long as this notice is retained. + * + * HISTORY + * i82593.h,v + * Revision 1.1 1996/07/17 15:23:12 root + * Initial revision + * + * Revision 1.3 1995/04/05 15:13:58 adj + * Initial alpha release + * + * Revision 1.2 1994/06/16 23:57:31 klemets + * Mirrored all the fields in the configuration block. + * + * Revision 1.1 1994/06/02 20:25:34 klemets + * Initial revision + * + * + */ +#ifndef _I82593_H +#define _I82593_H + +/* Intel 82593 CSMA/CD Core LAN Controller */ + +/* Port 0 Command Register definitions */ + +/* Execution operations */ +#define OP0_NOP 0 /* CHNL = 0 */ +#define OP0_SWIT_TO_PORT_1 0 /* CHNL = 1 */ +#define OP0_IA_SETUP 1 +#define OP0_CONFIGURE 2 +#define OP0_MC_SETUP 3 +#define OP0_TRANSMIT 4 +#define OP0_TDR 5 +#define OP0_DUMP 6 +#define OP0_DIAGNOSE 7 +#define OP0_TRANSMIT_NO_CRC 9 +#define OP0_RETRANSMIT 12 +#define OP0_ABORT 13 +/* Reception operations */ +#define OP0_RCV_ENABLE 8 +#define OP0_RCV_DISABLE 10 +#define OP0_STOP_RCV 11 +/* Status pointer control operations */ +#define OP0_FIX_PTR 15 /* CHNL = 1 */ +#define OP0_RLS_PTR 15 /* CHNL = 0 */ +#define OP0_RESET 14 + +#define CR0_CHNL (1 << 4) /* 0=Channel 0, 1=Channel 1 */ +#define CR0_STATUS_0 0x00 +#define CR0_STATUS_1 0x20 +#define CR0_STATUS_2 0x40 +#define CR0_STATUS_3 0x60 +#define CR0_INT_ACK (1 << 7) /* 0=No ack, 1=acknowledge */ + +/* Port 0 Status Register definitions */ + +#define SR0_NO_RESULT 0 /* dummy */ +#define SR0_EVENT_MASK 0x0f +#define SR0_IA_SETUP_DONE 1 +#define SR0_CONFIGURE_DONE 2 +#define SR0_MC_SETUP_DONE 3 +#define SR0_TRANSMIT_DONE 4 +#define SR0_TDR_DONE 5 +#define SR0_DUMP_DONE 6 +#define SR0_DIAGNOSE_PASSED 7 +#define SR0_TRANSMIT_NO_CRC_DONE 9 +#define SR0_RETRANSMIT_DONE 12 +#define SR0_EXECUTION_ABORTED 13 +#define SR0_END_OF_FRAME 8 +#define SR0_RECEPTION_ABORTED 10 +#define SR0_DIAGNOSE_FAILED 15 +#define SR0_STOP_REG_HIT 11 + +#define SR0_CHNL (1 << 4) +#define SR0_EXECUTION (1 << 5) +#define SR0_RECEPTION (1 << 6) +#define SR0_INTERRUPT (1 << 7) +#define SR0_BOTH_RX_TX (SR0_EXECUTION | SR0_RECEPTION) + +#define SR3_EXEC_STATE_MASK 0x03 +#define SR3_EXEC_IDLE 0 +#define SR3_TX_ABORT_IN_PROGRESS 1 +#define SR3_EXEC_ACTIVE 2 +#define SR3_ABORT_IN_PROGRESS 3 +#define SR3_EXEC_CHNL (1 << 2) +#define SR3_STP_ON_NO_RSRC (1 << 3) +#define SR3_RCVING_NO_RSRC (1 << 4) +#define SR3_RCV_STATE_MASK 0x60 +#define SR3_RCV_IDLE 0x00 +#define SR3_RCV_READY 0x20 +#define SR3_RCV_ACTIVE 0x40 +#define SR3_RCV_STOP_IN_PROG 0x60 +#define SR3_RCV_CHNL (1 << 7) + +/* Port 1 Command Register definitions */ + +#define OP1_NOP 0 +#define OP1_SWIT_TO_PORT_0 1 +#define OP1_INT_DISABLE 2 +#define OP1_INT_ENABLE 3 +#define OP1_SET_TS 5 +#define OP1_RST_TS 7 +#define OP1_POWER_DOWN 8 +#define OP1_RESET_RING_MNGMT 11 +#define OP1_RESET 14 +#define OP1_SEL_RST 15 + +#define CR1_STATUS_4 0x00 +#define CR1_STATUS_5 0x20 +#define CR1_STATUS_6 0x40 +#define CR1_STOP_REG_UPDATE (1 << 7) + +/* Receive frame status bits */ + +#define RX_RCLD (1 << 0) +#define RX_IA_MATCH (1 << 1) +#define RX_NO_AD_MATCH (1 << 2) +#define RX_NO_SFD (1 << 3) +#define RX_SRT_FRM (1 << 7) +#define RX_OVRRUN (1 << 8) +#define RX_ALG_ERR (1 << 10) +#define RX_CRC_ERR (1 << 11) +#define RX_LEN_ERR (1 << 12) +#define RX_RCV_OK (1 << 13) +#define RX_TYP_LEN (1 << 15) + +/* Transmit status bits */ + +#define TX_NCOL_MASK 0x0f +#define TX_FRTL (1 << 4) +#define TX_MAX_COL (1 << 5) +#define TX_HRT_BEAT (1 << 6) +#define TX_DEFER (1 << 7) +#define TX_UND_RUN (1 << 8) +#define TX_LOST_CTS (1 << 9) +#define TX_LOST_CRS (1 << 10) +#define TX_LTCOL (1 << 11) +#define TX_OK (1 << 13) +#define TX_COLL (1 << 15) + +struct i82593_conf_block { + u_char fifo_limit : 4, + forgnesi : 1, + fifo_32 : 1, + d6mod : 1, + throttle_enb : 1; + u_char throttle : 6, + cntrxint : 1, + contin : 1; + u_char addr_len : 3, + acloc : 1, + preamb_len : 2, + loopback : 2; + u_char lin_prio : 3, + tbofstop : 1, + exp_prio : 3, + bof_met : 1; + u_char : 4, + ifrm_spc : 4; + u_char : 5, + slottim_low : 3; + u_char slottim_hi : 3, + : 1, + max_retr : 4; + u_char prmisc : 1, + bc_dis : 1, + : 1, + crs_1 : 1, + nocrc_ins : 1, + crc_1632 : 1, + : 1, + crs_cdt : 1; + u_char cs_filter : 3, + crs_src : 1, + cd_filter : 3, + : 1; + u_char : 2, + min_fr_len : 6; + u_char lng_typ : 1, + lng_fld : 1, + rxcrc_xf : 1, + artx : 1, + sarec : 1, + tx_jabber : 1, /* why is this called max_len in the manual? */ + hash_1 : 1, + lbpkpol : 1; + u_char : 6, + fdx : 1, + : 1; + u_char dummy_6 : 6, /* supposed to be ones */ + mult_ia : 1, + dis_bof : 1; + u_char dummy_1 : 1, /* supposed to be one */ + tx_ifs_retrig : 2, + mc_all : 1, + rcv_mon : 2, + frag_acpt : 1, + tstrttrs : 1; + u_char fretx : 1, + runt_eop : 1, + hw_sw_pin : 1, + big_endn : 1, + syncrqs : 1, + sttlen : 1, + tx_eop : 1, + rx_eop : 1; + u_char rbuf_size : 5, + rcvstop : 1, + : 2; +}; + +#define I82593_MAX_MULTICAST_ADDRESSES 128 /* Hardware hashed filter */ + +#endif _I82593_H diff --git a/drivers/net/pcmcia/netwave_cs.c b/drivers/net/pcmcia/netwave_cs.c new file mode 100644 index 000000000..0e9a1045f --- /dev/null +++ b/drivers/net/pcmcia/netwave_cs.c @@ -0,0 +1,1709 @@ +/********************************************************************* + * + * Filename: netwave_cs.c + * Version: 0.4.1 + * Description: Netwave AirSurfer Wireless LAN PC Card driver + * Status: Experimental. + * Authors: John Markus Bjørndalen <johnm@cs.uit.no> + * Dag Brattli <dagb@cs.uit.no> + * David Hinds <dhinds@hyper.stanford.edu> + * Created at: A long time ago! + * Modified at: Mon Nov 10 11:54:37 1997 + * Modified by: Dag Brattli <dagb@cs.uit.no> + * + * Copyright (c) 1997 University of Tromsø, Norway + * + * Revision History: + * + * 08-Nov-97 15:14:47 John Markus Bjørndalen <johnm@cs.uit.no> + * - Fixed some bugs in netwave_rx and cleaned it up a bit. + * (One of the bugs would have destroyed packets when receiving + * multiple packets per interrupt). + * - Cleaned up parts of newave_hw_xmit. + * - A few general cleanups. + * 24-Oct-97 13:17:36 Dag Brattli <dagb@cs.uit.no> + * - Fixed netwave_rx receive function (got updated docs) + * Others: + * - Changed name from xircnw to netwave, take a look at + * http://www.netwave-wireless.com + * - Some reorganizing of the code + * - Removed possible race condition between interrupt handler and transmit + * function + * - Started to add wireless extensions, but still needs some coding + * - Added watchdog for better handling of transmission timeouts + * (hopefully this works better) + ********************************************************************/ + +/* To have statistics (just packets sent) define this */ +#undef NETWAVE_STATS + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/types.h> +#include <linux/fcntl.h> +#include <linux/interrupt.h> +#include <linux/ptrace.h> +#include <linux/ioport.h> +#include <linux/in.h> +#include <linux/malloc.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <asm/system.h> +#include <asm/bitops.h> +#include <asm/io.h> +#include <asm/dma.h> +#include <linux/errno.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> + +#ifdef CONFIG_NET_RADIO +#include <linux/wireless.h> +#endif + +#include <pcmcia/version.h> +#include <pcmcia/cs_types.h> +#include <pcmcia/cs.h> +#include <pcmcia/cistpl.h> +#include <pcmcia/cisreg.h> +#include <pcmcia/ds.h> +#include <pcmcia/mem_op.h> + +#define NETWAVE_REGOFF 0x8000 +/* The Netwave IO registers, offsets to iobase */ +#define NETWAVE_REG_COR 0x0 +#define NETWAVE_REG_CCSR 0x2 +#define NETWAVE_REG_ASR 0x4 +#define NETWAVE_REG_IMR 0xa +#define NETWAVE_REG_PMR 0xc +#define NETWAVE_REG_IOLOW 0x6 +#define NETWAVE_REG_IOHI 0x7 +#define NETWAVE_REG_IOCONTROL 0x8 +#define NETWAVE_REG_DATA 0xf +/* The Netwave Extended IO registers, offsets to RamBase */ +#define NETWAVE_EREG_ASCC 0x114 +#define NETWAVE_EREG_RSER 0x120 +#define NETWAVE_EREG_RSERW 0x124 +#define NETWAVE_EREG_TSER 0x130 +#define NETWAVE_EREG_TSERW 0x134 +#define NETWAVE_EREG_CB 0x100 +#define NETWAVE_EREG_SPCQ 0x154 +#define NETWAVE_EREG_SPU 0x155 +#define NETWAVE_EREG_LIF 0x14e +#define NETWAVE_EREG_ISPLQ 0x156 +#define NETWAVE_EREG_HHC 0x158 +#define NETWAVE_EREG_NI 0x16e +#define NETWAVE_EREG_MHS 0x16b +#define NETWAVE_EREG_TDP 0x140 +#define NETWAVE_EREG_RDP 0x150 +#define NETWAVE_EREG_PA 0x160 +#define NETWAVE_EREG_EC 0x180 +#define NETWAVE_EREG_CRBP 0x17a +#define NETWAVE_EREG_ARW 0x166 + +/* + * Commands used in the extended command buffer + * NETWAVE_EREG_CB (0x100-0x10F) + */ +#define NETWAVE_CMD_NOP 0x00 +#define NETWAVE_CMD_SRC 0x01 +#define NETWAVE_CMD_STC 0x02 +#define NETWAVE_CMD_AMA 0x03 +#define NETWAVE_CMD_DMA 0x04 +#define NETWAVE_CMD_SAMA 0x05 +#define NETWAVE_CMD_ER 0x06 +#define NETWAVE_CMD_DR 0x07 +#define NETWAVE_CMD_TL 0x08 +#define NETWAVE_CMD_SRP 0x09 +#define NETWAVE_CMD_SSK 0x0a +#define NETWAVE_CMD_SMD 0x0b +#define NETWAVE_CMD_SAPD 0x0c +#define NETWAVE_CMD_SSS 0x11 +/* End of Command marker */ +#define NETWAVE_CMD_EOC 0x00 + +/* ASR register bits */ +#define NETWAVE_ASR_RXRDY 0x80 +#define NETWAVE_ASR_TXBA 0x01 + +#define TX_TIMEOUT 20 +#define WATCHDOG_JIFFIES 32 + +static const unsigned int imrConfRFU1 = 0x10; /* RFU interrupt mask, keep high */ +static const unsigned int imrConfIENA = 0x02; /* Interrupt enable */ + +static const unsigned int corConfIENA = 0x01; /* Interrupt enable */ +static const unsigned int corConfLVLREQ = 0x40; /* Keep high */ + +static const unsigned int rxConfRxEna = 0x80; /* Receive Enable */ +static const unsigned int rxConfMAC = 0x20; /* MAC host receive mode*/ +static const unsigned int rxConfPro = 0x10; /* Promiscuous */ +static const unsigned int rxConfAMP = 0x08; /* Accept Multicast Packets */ +static const unsigned int rxConfBcast = 0x04; /* Accept Broadcast Packets */ + +static const unsigned int txConfTxEna = 0x80; /* Transmit Enable */ +static const unsigned int txConfMAC = 0x20; /* Host sends MAC mode */ +static const unsigned int txConfEUD = 0x10; /* Enable Uni-Data packets */ +static const unsigned int txConfKey = 0x02; /* Scramble data packets */ +static const unsigned int txConfLoop = 0x01; /* Loopback mode */ + +/*static int netwave_debug = 0;*/ + +/* + All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If + you do not define PCMCIA_DEBUG at all, all the debug code will be + left out. If you compile with PCMCIA_DEBUG=0, the debug code will + be present but disabled -- but it can then be enabled for specific + modules at load time with a 'pc_debug=#' option to insmod. +*/ + +#ifdef PCMCIA_DEBUG +static int pc_debug = PCMCIA_DEBUG; +MODULE_PARM(pc_debug, "i"); +#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) +static char *version = +"netwave_cs.c 0.3.0 Thu Jul 17 14:36:02 1997 (John Markus Bjørndalen)\n"; +#else +#define DEBUG(n, args...) +#endif + +static dev_info_t dev_info = "netwave_cs"; + +/*====================================================================*/ + +/* Parameters that can be set with 'insmod' */ + +/* Choose the domain, default is 0x100 */ +static u_int domain = 0x100; + +/* Scramble key, range from 0x0 to 0xffff. + * 0x0 is no scrambling. + */ +static u_int scramble_key = 0x0; + +/* Shared memory speed, in ns. The documentation states that + * the card should not be read faster than every 400ns. + * This timing should be provided by the HBA. If it becomes a + * problem, try setting mem_speed to 400. + */ +static int mem_speed = 0; + +/* Bit map of interrupts to choose from */ +/* This means pick from 15, 14, 12, 11, 10, 9, 7, 5, 4, and 3 */ +static u_int irq_mask = 0xdeb8; +static int irq_list[4] = { -1 }; + +MODULE_PARM(domain, "i"); +MODULE_PARM(scramble_key, "i"); +MODULE_PARM(mem_speed, "i"); +MODULE_PARM(irq_mask, "i"); +MODULE_PARM(irq_list, "1-4i"); + +/*====================================================================*/ + +/* PCMCIA (Card Services) related functions */ +static void netwave_release(u_long arg); /* Card removal */ +static int netwave_event(event_t event, int priority, + event_callback_args_t *args); +static void netwave_pcmcia_config(dev_link_t *arg); /* Runs after card + insertion */ +static dev_link_t *netwave_attach(void); /* Create instance */ +static void netwave_detach(dev_link_t *); /* Destroy instance */ +static void netwave_flush_stale_links(void); /* Destroy all staled instances */ + +/* Hardware configuration */ +static void netwave_doreset(ioaddr_t iobase, u_char* ramBase); +static void netwave_reset(struct net_device *dev); + +/* Misc device stuff */ +static int netwave_open(struct net_device *dev); /* Open the device */ +static int netwave_close(struct net_device *dev); /* Close the device */ +static int netwave_config(struct net_device *dev, struct ifmap *map); + +/* Packet transmission and Packet reception */ +static int netwave_start_xmit( struct sk_buff *skb, struct net_device *dev); +static int netwave_rx( struct net_device *dev); + +/* Interrupt routines */ +static void netwave_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static void netwave_watchdog(u_long); /* Transmission watchdog */ + +/* Statistics */ +static void update_stats(struct net_device *dev); +static struct enet_statistics *netwave_get_stats(struct net_device *dev); + +/* Wireless extensions */ +#ifdef WIRELESS_EXT +static struct iw_statistics* netwave_get_wireless_stats(struct net_device *dev); +#endif +static int netwave_ioctl(struct net_device *, struct ifreq *, int); + +static void set_multicast_list(struct net_device *dev); + +/* + A linked list of "instances" of the skeleton device. Each actual + PCMCIA card corresponds to one device instance, and is described + by one dev_link_t structure (defined in ds.h). + + You may not want to use a linked list for this -- for example, the + memory card driver uses an array of dev_link_t pointers, where minor + device numbers are used to derive the corresponding array index. +*/ +static dev_link_t *dev_list = NULL; + +/* + A dev_link_t structure has fields for most things that are needed + to keep track of a socket, but there will usually be some device + specific information that also needs to be kept track of. The + 'priv' pointer in a dev_link_t structure can be used to point to + a device-specific private data structure, like this. + + A driver needs to provide a dev_node_t structure for each device + on a card. In some cases, there is only one device per card (for + example, ethernet cards, modems). In other cases, there may be + many actual or logical devices (SCSI adapters, memory cards with + multiple partitions). The dev_node_t structures need to be kept + in a linked list starting at the 'dev' field of a dev_link_t + structure. We allocate them in the card's private data structure, + because they generally can't be allocated dynamically. +*/ + +#define SIOCGIPSNAP SIOCDEVPRIVATE /* Site Survey Snapshot */ +/*#define SIOCGIPQTHR SIOCDEVPRIVATE + 1*/ + +#define MAX_ESA 10 + +typedef struct net_addr { + u_char addr48[6]; +} net_addr; + +struct site_survey { + u_short length; + u_char struct_revision; + u_char roaming_state; + + u_char sp_existsFlag; + u_char sp_link_quality; + u_char sp_max_link_quality; + u_char linkQualityGoodFairBoundary; + u_char linkQualityFairPoorBoundary; + u_char sp_utilization; + u_char sp_goodness; + u_char sp_hotheadcount; + u_char roaming_condition; + + net_addr sp; + u_char numAPs; + net_addr nearByAccessPoints[MAX_ESA]; +}; + +typedef struct netwave_private { + dev_node_t node; + u_char *ramBase; + int timeoutCounter; + int lastExec; + struct timer_list watchdog; /* To avoid blocking state */ + struct site_survey nss; + struct enet_statistics stats; +#ifdef WIRELESS_EXT + struct iw_statistics iw_stats; /* Wireless stats */ +#endif +} netwave_private; + +#ifdef NETWAVE_STATS +static struct enet_statistics *netwave_get_stats(struct net_device *dev); +#endif + +/* + * The Netwave card is little-endian, so won't work for big endian + * systems. + */ +static inline unsigned short get_uint16(u_char* staddr) +{ + return readw(staddr); /* Return only 16 bits */ +} + +static inline short get_int16(u_char* staddr) +{ + return readw(staddr); +} + +/**************************************************************************/ + +static void cs_error(client_handle_t handle, int func, int ret) +{ + error_info_t err = { func, ret }; + CardServices(ReportError, handle, &err); +} + +/* + * Wait until the WOC (Write Operation Complete) bit in the + * ASR (Adapter Status Register) is asserted. + * This should have aborted if it takes too long time. + */ +static inline void wait_WOC(unsigned int iobase) +{ + /* Spin lock */ + while ((inb(iobase + NETWAVE_REG_ASR) & 0x8) != 0x8) ; +} + +#ifdef WIRELESS_EXT +static void netwave_snapshot(netwave_private *priv, u_char *ramBase, + ioaddr_t iobase) { + u_short resultBuffer; + + /* if time since last snapshot is > 1 sec. (100 jiffies?) then take + * new snapshot, else return cached data. This is the recommended rate. + */ + if ( jiffies - priv->lastExec > 100) { + /* Take site survey snapshot */ + /*printk( KERN_DEBUG "Taking new snapshot. %ld\n", jiffies - + priv->lastExec); */ + wait_WOC(iobase); + writeb(NETWAVE_CMD_SSS, ramBase + NETWAVE_EREG_CB + 0); + writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 1); + wait_WOC(iobase); + + /* Get result and copy to cach */ + resultBuffer = readw(ramBase + NETWAVE_EREG_CRBP); + copy_from_pc( &priv->nss, ramBase+resultBuffer, + sizeof(struct site_survey)); + } +} +#endif + +#ifdef WIRELESS_EXT +/* + * Function netwave_get_wireless_stats (dev) + * + * Wireless extensions statistics + * + */ +static struct iw_statistics *netwave_get_wireless_stats(struct net_device *dev) +{ + unsigned long flags; + ioaddr_t iobase = dev->base_addr; + netwave_private *priv = (netwave_private *) dev->priv; + u_char *ramBase = priv->ramBase; + struct iw_statistics* wstats; + + wstats = &priv->iw_stats; + + save_flags(flags); + cli(); + + netwave_snapshot( priv, ramBase, iobase); + + wstats->status = priv->nss.roaming_state; + wstats->qual.qual = readb( ramBase + NETWAVE_EREG_SPCQ); + wstats->qual.level = readb( ramBase + NETWAVE_EREG_ISPLQ); + wstats->qual.noise = readb( ramBase + NETWAVE_EREG_SPU) & 0x3f; + wstats->discard.nwid = 0L; + wstats->discard.code = 0L; + wstats->discard.misc = 0L; + + restore_flags(flags); + + return &priv->iw_stats; +} +#endif + +/* + * Function netwave_init (dev) + * + * We never need to do anything when a device is "initialized" + * by the net software, because we only register already-found cards. + */ +int netwave_init(struct net_device *dev) +{ + /* We do all the initialization of this in netwave_attach instead */ + return 0; +} + +/* + * Function netwave_attach (void) + * + * Creates an "instance" of the driver, allocating local data + * structures for one device. The device is registered with Card + * Services. + * + * The dev_link structure is initialized, but we don't actually + * configure the card at this point -- we wait until we receive a + * card insertion event. + */ +static dev_link_t *netwave_attach(void) +{ + client_reg_t client_reg; + dev_link_t *link; + struct net_device *dev; + netwave_private *priv; + int i, ret; + + DEBUG(0, "netwave_attach()\n"); + + /* Perform some cleanup */ + netwave_flush_stale_links(); + + /* Initialize the dev_link_t structure */ + link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL); + memset(link, 0, sizeof(struct dev_link_t)); + link->release.function = &netwave_release; + link->release.data = (u_long)link; + + /* The io structure describes IO port mapping */ + link->io.NumPorts1 = 16; + link->io.Attributes1 = IO_DATA_PATH_WIDTH_16; + /* link->io.NumPorts2 = 16; + link->io.Attributes2 = IO_DATA_PATH_WIDTH_16; */ + link->io.IOAddrLines = 5; + + /* Interrupt setup */ + link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; + link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID; + if (irq_list[0] == -1) + link->irq.IRQInfo2 = irq_mask; + else + for (i = 0; i < 4; i++) + link->irq.IRQInfo2 |= 1 << irq_list[i]; + link->irq.Handler = &netwave_interrupt; + + /* General socket configuration */ + link->conf.Attributes = CONF_ENABLE_IRQ; + link->conf.Vcc = 50; + link->conf.IntType = INT_MEMORY_AND_IO; + link->conf.ConfigIndex = 1; + link->conf.Present = PRESENT_OPTION; + + /* Allocate space for private device-specific data */ + dev = kmalloc(sizeof(struct net_device), GFP_KERNEL); + memset(dev, 0, sizeof(struct net_device)); + + dev->priv = kmalloc(sizeof(netwave_private), GFP_KERNEL); + memset(dev->priv, 0, sizeof(netwave_private)); + + /* Set the watchdog timer */ + priv = (netwave_private *) dev->priv; + priv->watchdog.function = &netwave_watchdog; + priv->watchdog.data = (unsigned long) dev; + + /* Netwave specific entries in the device structure */ + dev->hard_start_xmit = &netwave_start_xmit; + dev->set_config = &netwave_config; + dev->get_stats = &netwave_get_stats; + dev->set_multicast_list = &set_multicast_list; + /* wireless extensions */ +#ifdef WIRELESS_EXT + dev->get_wireless_stats = &netwave_get_wireless_stats; +#endif + dev->do_ioctl = &netwave_ioctl; + + ether_setup(dev); + dev->name = ((struct netwave_private *)dev->priv)->node.dev_name; + dev->init = &netwave_init; + dev->open = &netwave_open; + dev->stop = &netwave_close; + dev->tbusy = 1; + link->priv = link->irq.Instance = dev; + + /* Register with Card Services */ + link->next = dev_list; + dev_list = link; + client_reg.dev_info = &dev_info; + client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; + client_reg.EventMask = + CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | + CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | + CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; + client_reg.event_handler = &netwave_event; + client_reg.Version = 0x0210; + client_reg.event_callback_args.client_data = link; + ret = CardServices(RegisterClient, &link->handle, &client_reg); + if (ret != 0) { + cs_error(link->handle, RegisterClient, ret); + netwave_detach(link); + return NULL; + } + + return link; +} /* netwave_attach */ + +/* + * Function netwave_detach (link) + * + * This deletes a driver "instance". The device is de-registered + * with Card Services. If it has been released, all local data + * structures are freed. Otherwise, the structures will be freed + * when the device is released. + */ +static void netwave_detach(dev_link_t *link) +{ + dev_link_t **linkp; + long flags; + + DEBUG(0, "netwave_detach(0x%p)\n", link); + + save_flags(flags); + if (link->state & DEV_RELEASE_PENDING) { + del_timer(&link->release); + link->state &= ~DEV_RELEASE_PENDING; + } + cli(); + restore_flags(flags); + + /* + If the device is currently configured and active, we won't + actually delete it yet. Instead, it is marked so that when + the release() function is called, that will trigger a proper + detach(). + */ + if (link->state & DEV_CONFIG) { + netwave_release((u_long) link); + if (link->state & DEV_STALE_CONFIG) { + DEBUG(1, "netwave_cs: detach postponed, '%s' still " + "locked\n", link->dev->dev_name); + + link->state |= DEV_STALE_LINK; + return; + } + } + + /* Break the link with Card Services */ + if (link->handle) + CardServices(DeregisterClient, link->handle); + + /* Locate device structure */ + for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) + if (*linkp == link) break; + if (*linkp == NULL) + { + DEBUG(1, "netwave_cs: detach fail, '%s' not in list\n", + link->dev->dev_name); + return; + } + + /* Unlink device structure, free pieces */ + *linkp = link->next; + if (link->priv) { + struct net_device *dev = link->priv; + if (link->dev != NULL) + unregister_netdev(dev); + link->dev = NULL; + if (dev->priv) + kfree(dev->priv); + kfree(link->priv); + } + kfree(link); + +} /* netwave_detach */ + +/* + * Function netwave_flush_stale_links (void) + * + * This deletes all driver "instances" that need to be deleted. + * Sometimes, netwave_detach can't be performed following a call from + * cardmgr (device still open) and the device is put in a STALE_LINK + * state. + * This function is in charge of making the cleanup... + */ +static void netwave_flush_stale_links(void) +{ + dev_link_t * link; /* Current node in linked list */ + dev_link_t * next; /* Next node in linked list */ + + DEBUG(1, "netwave_flush_stale_links(0x%p)\n", dev_list); + + /* Go through the list */ + for (link = dev_list; link; link = next) { + next = link->next; + /* Check if in need of being removed */ + if(link->state & DEV_STALE_LINK) + netwave_detach(link); + } +} /* netwave_flush_stale_links */ + +/* + * Function netwave_ioctl (dev, rq, cmd) + * + * Perform ioctl : config & info stuff + * This is the stuff that are treated the wireless extensions (iwconfig) + * + */ +static int netwave_ioctl(struct net_device *dev, /* ioctl device */ + struct ifreq *rq, /* Data passed */ + int cmd) /* Ioctl number */ +{ + unsigned long flags; + int ret = 0; +#ifdef WIRELESS_EXT + ioaddr_t iobase = dev->base_addr; + netwave_private *priv = (netwave_private *) dev->priv; + u_char *ramBase = priv->ramBase; + struct iwreq *wrq = (struct iwreq *) rq; +#endif + + DEBUG( 0, "%s: ->netwave_ioctl(cmd=0x%X)\n", dev->name, cmd); + + /* Disable interrupts & save flags */ + save_flags(flags); + cli(); + + /* Look what is the request */ + switch(cmd) { + /* --------------- WIRELESS EXTENSIONS --------------- */ +#ifdef WIRELESS_EXT + case SIOCGIWNAME: + /* Get name */ + strcpy(wrq->u.name, "Netwave"); + break; + case SIOCSIWNWID: + /* Set domain */ +#if WIRELESS_EXT > 8 + if(!wrq->u.nwid.disabled) { + domain = wrq->u.nwid.value; +#else /* WIRELESS_EXT > 8 */ + if(wrq->u.nwid.on) { + domain = wrq->u.nwid.nwid; +#endif /* WIRELESS_EXT > 8 */ + printk( KERN_DEBUG "Setting domain to 0x%x%02x\n", + (domain >> 8) & 0x01, domain & 0xff); + wait_WOC(iobase); + writeb(NETWAVE_CMD_SMD, ramBase + NETWAVE_EREG_CB + 0); + writeb( domain & 0xff, ramBase + NETWAVE_EREG_CB + 1); + writeb((domain >>8 ) & 0x01,ramBase + NETWAVE_EREG_CB+2); + writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3); + } break; + case SIOCGIWNWID: + /* Read domain*/ +#if WIRELESS_EXT > 8 + wrq->u.nwid.value = domain; + wrq->u.nwid.disabled = 0; + wrq->u.nwid.fixed = 1; +#else /* WIRELESS_EXT > 8 */ + wrq->u.nwid.nwid = domain; + wrq->u.nwid.on = 1; +#endif /* WIRELESS_EXT > 8 */ + break; +#if WIRELESS_EXT > 8 /* Note : The API did change... */ + case SIOCGIWENCODE: + /* Get scramble key */ + if(wrq->u.encoding.pointer != (caddr_t) 0) + { + char key[2]; + key[1] = scramble_key & 0xff; + key[0] = (scramble_key>>8) & 0xff; + wrq->u.encoding.flags = IW_ENCODE_ENABLED; + wrq->u.encoding.length = 2; + if(copy_to_user(wrq->u.encoding.pointer, key, 2)) + ret = -EFAULT; + } + break; + case SIOCSIWENCODE: + /* Set scramble key */ + if(wrq->u.encoding.pointer != (caddr_t) 0) + { + char key[2]; + if(copy_from_user(key, wrq->u.encoding.pointer, 2)) + { + ret = -EFAULT; + break; + } + scramble_key = (key[0] << 8) | key[1]; + wait_WOC(iobase); + writeb(NETWAVE_CMD_SSK, ramBase + NETWAVE_EREG_CB + 0); + writeb(scramble_key & 0xff, ramBase + NETWAVE_EREG_CB + 1); + writeb((scramble_key>>8) & 0xff, ramBase + NETWAVE_EREG_CB + 2); + writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3); + } + break; + case SIOCGIWMODE: + /* Mode of operation */ + if(domain & 0x100) + wrq->u.mode = IW_MODE_INFRA; + else + wrq->u.mode = IW_MODE_ADHOC; + break; +#else /* WIRELESS_EXT > 8 */ + case SIOCGIWENCODE: + /* Get scramble key */ + wrq->u.encoding.code = scramble_key; + wrq->u.encoding.method = 1; + break; + case SIOCSIWENCODE: + /* Set scramble key */ + scramble_key = wrq->u.encoding.code; + wait_WOC(iobase); + writeb(NETWAVE_CMD_SSK, ramBase + NETWAVE_EREG_CB + 0); + writeb(scramble_key & 0xff, ramBase + NETWAVE_EREG_CB + 1); + writeb((scramble_key>>8) & 0xff, ramBase + NETWAVE_EREG_CB + 2); + writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3); + break; +#endif /* WIRELESS_EXT > 8 */ + case SIOCGIWRANGE: + /* Basic checking... */ + if(wrq->u.data.pointer != (caddr_t) 0) { + struct iw_range range; + + /* Set the length (useless : its constant...) */ + wrq->u.data.length = sizeof(struct iw_range); + + /* Set information in the range struct */ + range.throughput = 450 * 1000; /* don't argue on this ! */ + range.min_nwid = 0x0000; + range.max_nwid = 0x01FF; + + range.num_channels = range.num_frequency = 0; + + range.sensitivity = 0x3F; + range.max_qual.qual = 255; + range.max_qual.level = 255; + range.max_qual.noise = 0; + +#if WIRELESS_EXT > 7 + range.num_bitrates = 1; + range.bitrate[0] = 1000000; /* 1 Mb/s */ +#endif /* WIRELESS_EXT > 7 */ + +#if WIRELESS_EXT > 8 + range.encoding_size[0] = 2; /* 16 bits scrambling */ + range.num_encoding_sizes = 1; + range.max_encoding_tokens = 1; /* Only one key possible */ +#endif /* WIRELESS_EXT > 8 */ + + /* Copy structure to the user buffer */ + if(copy_to_user(wrq->u.data.pointer, &range, + sizeof(struct iw_range))) + ret = -EFAULT; + } + break; + case SIOCGIWPRIV: + /* Basic checking... */ + if(wrq->u.data.pointer != (caddr_t) 0) { + struct iw_priv_args priv[] = + { /* cmd, set_args, get_args, name */ + { SIOCGIPSNAP, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 0, + sizeof(struct site_survey), + "getsitesurvey" }, + }; + + /* Set the number of ioctl available */ + wrq->u.data.length = 1; + + /* Copy structure to the user buffer */ + if(copy_to_user(wrq->u.data.pointer, (u_char *) priv, + sizeof(priv))) + ret = -EFAULT; + } + break; + case SIOCGIPSNAP: + if(wrq->u.data.pointer != (caddr_t) 0) { + /* Take snapshot of environment */ + netwave_snapshot( priv, ramBase, iobase); + wrq->u.data.length = priv->nss.length; + /* Copy structure to the user buffer */ + if(copy_to_user(wrq->u.data.pointer, + (u_char *) &priv->nss, + sizeof( struct site_survey))) + { + printk(KERN_DEBUG "Bad buffer!\n"); + break; + } + + priv->lastExec = jiffies; + } + break; +#endif + default: + ret = -EOPNOTSUPP; + } + + /* ReEnable interrupts & restore flags */ + restore_flags(flags); + + return ret; +} + +/* + * Function netwave_pcmcia_config (link) + * + * netwave_pcmcia_config() is scheduled to run after a CARD_INSERTION + * event is received, to configure the PCMCIA socket, and to make the + * device available to the system. + * + */ + +#define CS_CHECK(fn, args...) \ +while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed + +static void netwave_pcmcia_config(dev_link_t *link) { + client_handle_t handle; + tuple_t tuple; + cisparse_t parse; + struct net_device *dev; + int i, j, last_ret, last_fn; + u_char buf[64]; + win_req_t req; + memreq_t mem; + u_char *ramBase = NULL; + /* modwin_t mod; + short iobase, *phys_addr; + */ + handle = link->handle; + dev = link->priv; + + DEBUG(0, "netwave_pcmcia_config(0x%p)\n", link); + + /* + This reads the card's CONFIG tuple to find its configuration + registers. + */ + tuple.Attributes = 0; + tuple.TupleData = (cisdata_t *) buf; + tuple.TupleDataMax = 64; + tuple.TupleOffset = 0; + tuple.DesiredTuple = CISTPL_CONFIG; + CS_CHECK(GetFirstTuple, handle, &tuple); + CS_CHECK(GetTupleData, handle, &tuple); + CS_CHECK(ParseTuple, handle, &tuple, &parse); + link->conf.ConfigBase = parse.config.base; + link->conf.Present = parse.config.rmask[0]; + + /* Configure card */ + link->state |= DEV_CONFIG; + + /* + * Try allocating IO ports. This tries a few fixed addresses. + * If you want, you can also read the card's config table to + * pick addresses -- see the serial driver for an example. + */ + for (j = 0x0; j < 0x400; j += 0x20) { + link->io.BasePort1 = j ^ 0x300; + i = CardServices(RequestIO, link->handle, &link->io); + if (i == CS_SUCCESS) break; + } + if (i != CS_SUCCESS) { + cs_error(link->handle, RequestIO, i); + goto failed; + } + + /* + * Now allocate an interrupt line. Note that this does not + * actually assign a handler to the interrupt. + */ + CS_CHECK(RequestIRQ, handle, &link->irq); + + /* + * This actually configures the PCMCIA socket -- setting up + * the I/O windows and the interrupt mapping. + */ + CS_CHECK(RequestConfiguration, handle, &link->conf); + + /* + * Allocate a 32K memory window. Note that the dev_link_t + * structure provides space for one window handle -- if your + * device needs several windows, you'll need to keep track of + * the handles in your private data structure, link->priv. + */ + DEBUG(1, "Setting mem speed of %d\n", mem_speed); + + req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_CM|WIN_ENABLE; + req.Base = 0; req.Size = 0x8000; + req.AccessSpeed = mem_speed; + link->win = (window_handle_t)link->handle; + CS_CHECK(RequestWindow, &link->win, &req); + mem.CardOffset = 0x20000; mem.Page = 0; + CS_CHECK(MapMemPage, link->win, &mem); + + /* Store base address of the common window frame */ + ramBase = ioremap(req.Base, 0x8000); + ((netwave_private*)dev->priv)->ramBase = ramBase; + + dev->irq = link->irq.AssignedIRQ; + dev->base_addr = link->io.BasePort1; + dev->tbusy = 0; + if (register_netdev(dev) != 0) { + printk(KERN_DEBUG "netwave_cs: register_netdev() failed\n"); + goto failed; + } + + link->state &= ~DEV_CONFIG_PENDING; + + link->dev = &((netwave_private *)dev->priv)->node; + + /* Reset card before reading physical address */ + netwave_doreset(dev->base_addr, ramBase); + + /* Read the ethernet address and fill in the Netwave registers. */ + for (i = 0; i < 6; i++) + dev->dev_addr[i] = readb(ramBase + NETWAVE_EREG_PA + i); + + printk(KERN_INFO "%s: Netwave: port %#3lx, irq %d, mem %lx id " + "%c%c, hw_addr ", dev->name, dev->base_addr, dev->irq, + (u_long) ramBase, (int) readb(ramBase+NETWAVE_EREG_NI), + (int) readb(ramBase+NETWAVE_EREG_NI+1)); + for (i = 0; i < 6; i++) + printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n")); + + /* get revision words */ + printk(KERN_DEBUG "Netwave_reset: revision %04x %04x\n", + get_uint16(ramBase + NETWAVE_EREG_ARW), + get_uint16(ramBase + NETWAVE_EREG_ARW+2)); + return; + +cs_failed: + cs_error(link->handle, last_fn, last_ret); +failed: + netwave_release((u_long)link); + return; +} /* netwave_pcmcia_config */ + +/* + * Function netwave_release (arg) + * + * After a card is removed, netwave_release() will unregister the net + * device, and release the PCMCIA configuration. If the device is + * still open, this will be postponed until it is closed. + */ +static void netwave_release(u_long arg) { + dev_link_t *link = (dev_link_t *)arg; + struct net_device *dev = link->priv; + + DEBUG(0, "netwave_release(0x%p)\n", link); + + /* + If the device is currently in use, we won't release until it + is actually closed. + */ + if (link->open) { + printk(KERN_DEBUG "netwave_cs: release postponed, '%s' still open\n", + link->dev->dev_name); + link->state |= DEV_STALE_CONFIG; + return; + } + + /* Don't bother checking to see if these succeed or not */ + if (link->win) { + iounmap(((netwave_private *)dev->priv)->ramBase); + CardServices(ReleaseWindow, link->win); + } + CardServices(ReleaseConfiguration, link->handle); + CardServices(ReleaseIO, link->handle, &link->io); + CardServices(ReleaseIRQ, link->handle, &link->irq); + + link->state &= ~(DEV_CONFIG | DEV_RELEASE_PENDING | DEV_STALE_CONFIG); + +} /* netwave_release */ + +/* + * Function netwave_event (event, priority, args) + * + * The card status event handler. Mostly, this schedules other + * stuff to run after an event is received. A CARD_REMOVAL event + * also sets some flags to discourage the net drivers from trying + * to talk to the card any more. + * + * When a CARD_REMOVAL event is received, we immediately set a flag + * to block future accesses to this device. All the functions that + * actually access the device should check this flag to make sure + * the card is still present. + * + */ +static int netwave_event(event_t event, int priority, + event_callback_args_t *args) { + dev_link_t *link = args->client_data; + struct net_device *dev = link->priv; + + DEBUG(1, "netwave_event(0x%06x)\n", event); + + switch (event) { + case CS_EVENT_REGISTRATION_COMPLETE: + DEBUG(0, "netwave_cs: registration complete\n"); + break; + + case CS_EVENT_CARD_REMOVAL: + link->state &= ~DEV_PRESENT; + if (link->state & DEV_CONFIG) { + dev->tbusy = 1; dev->start = 0; + /* ((netwave_private *)link->priv)->block = 1; */ + link->release.expires = jiffies + 5; + add_timer(&link->release); + } + break; + case CS_EVENT_CARD_INSERTION: + link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; + netwave_pcmcia_config( link); + break; + case CS_EVENT_PM_SUSPEND: + link->state |= DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_RESET_PHYSICAL: + if (link->state & DEV_CONFIG) { + if (link->open) { + dev->tbusy = 1; dev->start = 0; + } + CardServices(ReleaseConfiguration, link->handle); + } + break; + case CS_EVENT_PM_RESUME: + link->state &= ~DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_CARD_RESET: + if (link->state & DEV_CONFIG) { + CardServices(RequestConfiguration, link->handle, &link->conf); + if (link->open) { + netwave_reset(dev); + dev->tbusy = 0; dev->start = 1; + } + } + break; + } + return 0; +} /* netwave_event */ + +/* + * Function netwave_doreset (ioBase, ramBase) + * + * Proper hardware reset of the card. + */ +static void netwave_doreset(ioaddr_t ioBase, u_char* ramBase) { + /* Reset card */ + wait_WOC(ioBase); + outb(0x80, ioBase + NETWAVE_REG_PMR); + writeb(0x08, ramBase + NETWAVE_EREG_ASCC); /* Bit 3 is WOC */ + outb(0x0, ioBase + NETWAVE_REG_PMR); /* release reset */ +} + +/* + * Function netwave_reset (dev) + * + * Reset and restore all of the netwave registers + */ +static void netwave_reset(struct net_device *dev) { + /* u_char state; */ + netwave_private *priv = (netwave_private*) dev->priv; + u_char *ramBase = priv->ramBase; + ioaddr_t iobase = dev->base_addr; + + DEBUG(0, "netwave_reset: Done with hardware reset\n"); + + priv->timeoutCounter = 0; + + /* If watchdog was activated, kill it ! */ + del_timer(&priv->watchdog); + + /* Reset card */ + netwave_doreset(iobase, ramBase); + printk(KERN_DEBUG "netwave_reset: Done with hardware reset\n"); + + /* Write a NOP to check the card */ + wait_WOC(iobase); + writeb(NETWAVE_CMD_NOP, ramBase + NETWAVE_EREG_CB + 0); + writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 1); + + /* Set receive conf */ + wait_WOC(iobase); + writeb(NETWAVE_CMD_SRC, ramBase + NETWAVE_EREG_CB + 0); + writeb(rxConfRxEna + rxConfBcast, ramBase + NETWAVE_EREG_CB + 1); + writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 2); + + /* Set transmit conf */ + wait_WOC(iobase); + writeb(NETWAVE_CMD_STC, ramBase + NETWAVE_EREG_CB + 0); + writeb(txConfTxEna, ramBase + NETWAVE_EREG_CB + 1); + writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 2); + + /* Now set the MU Domain */ + printk(KERN_DEBUG "Setting domain to 0x%x%02x\n", (domain >> 8) & 0x01, domain & 0xff); + wait_WOC(iobase); + writeb(NETWAVE_CMD_SMD, ramBase + NETWAVE_EREG_CB + 0); + writeb(domain & 0xff, ramBase + NETWAVE_EREG_CB + 1); + writeb((domain>>8) & 0x01, ramBase + NETWAVE_EREG_CB + 2); + writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3); + + /* Set scramble key */ + printk(KERN_DEBUG "Setting scramble key to 0x%x\n", scramble_key); + wait_WOC(iobase); + writeb(NETWAVE_CMD_SSK, ramBase + NETWAVE_EREG_CB + 0); + writeb(scramble_key & 0xff, ramBase + NETWAVE_EREG_CB + 1); + writeb((scramble_key>>8) & 0xff, ramBase + NETWAVE_EREG_CB + 2); + writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3); + + /* Enable interrupts, bit 4 high to keep unused + * source from interrupting us, bit 2 high to + * set interrupt enable, 567 to enable TxDN, + * RxErr and RxRdy + */ + wait_WOC(iobase); + outb(imrConfIENA+imrConfRFU1, iobase + NETWAVE_REG_IMR); + + /* Hent 4 bytes fra 0x170. Skal vaere 0a,29,88,36 + * waitWOC + * skriv 80 til d000:3688 + * sjekk om det ble 80 + */ + + /* Enable Receiver */ + wait_WOC(iobase); + writeb(NETWAVE_CMD_ER, ramBase + NETWAVE_EREG_CB + 0); + writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 1); + + /* Set the IENA bit in COR */ + wait_WOC(iobase); + outb(corConfIENA + corConfLVLREQ, iobase + NETWAVE_REG_COR); +} + +/* + * Function netwave_config (dev, map) + * + * Configure device, this work is done by netwave_pcmcia_config when a + * card is inserted + */ +static int netwave_config(struct net_device *dev, struct ifmap *map) { + return 0; +} + +/* + * Function netwave_hw_xmit (data, len, dev) + */ +static int netwave_hw_xmit(unsigned char* data, int len, + struct net_device* dev) { + unsigned long flags; + unsigned int TxFreeList, + curBuff, + MaxData, + DataOffset; + int tmpcount; + + netwave_private *priv = (netwave_private *) dev->priv; + u_char* ramBase = priv->ramBase; + ioaddr_t iobase = dev->base_addr; + + /* Disable interrupts & save flags */ + save_flags(flags); + cli(); + + /* Check if there are transmit buffers available */ + wait_WOC(iobase); + if ((inb(iobase+NETWAVE_REG_ASR) & NETWAVE_ASR_TXBA) == 0) { + /* No buffers available */ + printk(KERN_DEBUG "netwave_hw_xmit: %s - no xmit buffers available.\n", + dev->name); + return 1; + } + + priv->stats.tx_bytes += len; + + DEBUG(3, "Transmitting with SPCQ %x SPU %x LIF %x ISPLQ %x\n", + readb(ramBase + NETWAVE_EREG_SPCQ), + readb(ramBase + NETWAVE_EREG_SPU), + readb(ramBase + NETWAVE_EREG_LIF), + readb(ramBase + NETWAVE_EREG_ISPLQ)); + + /* Now try to insert it into the adapters free memory */ + wait_WOC(iobase); + TxFreeList = get_uint16(ramBase + NETWAVE_EREG_TDP); + MaxData = get_uint16(ramBase + NETWAVE_EREG_TDP+2); + DataOffset = get_uint16(ramBase + NETWAVE_EREG_TDP+4); + + DEBUG(3, "TxFreeList %x, MaxData %x, DataOffset %x\n", + TxFreeList, MaxData, DataOffset); + + /* Copy packet to the adapter fragment buffers */ + curBuff = TxFreeList; + tmpcount = 0; + while (tmpcount < len) { + int tmplen = len - tmpcount; + copy_to_pc(ramBase + curBuff + DataOffset, data + tmpcount, + (tmplen < MaxData) ? tmplen : MaxData); + tmpcount += MaxData; + + /* Advance to next buffer */ + curBuff = get_uint16(ramBase + curBuff); + } + + /* Now issue transmit list */ + wait_WOC(iobase); + writeb(NETWAVE_CMD_TL, ramBase + NETWAVE_EREG_CB + 0); + writeb(len & 0xff, ramBase + NETWAVE_EREG_CB + 1); + writeb((len>>8) & 0xff, ramBase + NETWAVE_EREG_CB + 2); + writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 3); + + /* If watchdog not already active, activate it... */ + if(priv->watchdog.prev == (struct timer_list *) NULL) { + + /* set timer to expire in WATCHDOG_JIFFIES */ + priv->watchdog.expires = jiffies + WATCHDOG_JIFFIES; + add_timer(&priv->watchdog); + } + restore_flags( flags); + return 0; +} + +static int netwave_start_xmit(struct sk_buff *skb, struct net_device *dev) { + /* This flag indicate that the hardware can't perform a transmission. + * Theoritically, NET3 check it before sending a packet to the driver, + * but in fact it never do that and pool continuously. + * As the watchdog will abort too long transmissions, we are quite safe... + */ + + if (dev->tbusy) { + /* Handled by watchdog */ + return 1; + + /* If we get here, some higher level has decided we are broken. + There should really be a 'kick me' function call instead. + */ + /*int tickssofar = jiffies - dev->trans_start;*/ + /* printk("xmit called with busy. tickssofar %d\n", tickssofar); */ + /*if (tickssofar < TX_TIMEOUT) + return 1; + */ + /* Should also detect if the kernel tries to xmit + * on a stopped card. + */ + + /*if (netwave_debug > 0) + printk(KERN_DEBUG "%s timed out.\n", dev->name); + netwave_reset(dev); + dev->trans_start = jiffies; + dev->tbusy = 0;*/ + } + + /* Sending a NULL skb means some higher layer thinks we've missed an + * tx-done interrupt. Caution: dev_tint() handles the cli()/sti() + * itself. + */ + + /* Block a timer-based transmit from overlapping. This could + * better be done with atomic_swap(1, dev->tbusy, but set_bit() + * works as well + */ + if ( test_and_set_bit(0, (void*)&dev->tbusy) != 0) + printk("%s: Transmitter access conflict.\n", dev->name); + else { + short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; + unsigned char* buf = skb->data; + + if (netwave_hw_xmit( buf, length, dev) == 1) { + /* Some error, let's make them call us another time? */ + dev->tbusy = 0; + } + dev->trans_start = jiffies; + } + dev_kfree_skb(skb); + + return 0; +} /* netwave_start_xmit */ + +/* + * Function netwave_interrupt (irq, dev_id, regs) + * + * This function is the interrupt handler for the Netwave card. This + * routine will be called whenever: + * 1. A packet is received. + * 2. A packet has successfully been transfered and the unit is + * ready to transmit another packet. + * 3. A command has completed execution. + */ +static void netwave_interrupt(int irq, void* dev_id, struct pt_regs *regs) { + ioaddr_t iobase; + u_char *ramBase; + struct net_device *dev = (struct net_device *)dev_id; + struct netwave_private *priv; + int i; + dev_link_t *link; + + if ((dev == NULL) | (!dev->start)) + return; + + priv = (netwave_private *)dev->priv; + + if (dev->interrupt) { + printk("%s: re-entering the interrupt handler.\n", dev->name); + return; + } + dev->interrupt = 1; + + /* Find the correct dev_link_t */ + for (link = dev_list; NULL != link; link = link->next) + if (dev == link->priv) break; + + iobase = dev->base_addr; + ramBase = priv->ramBase; + + /* Now find what caused the interrupt, check while interrupts ready */ + for (i = 0; i < 10; i++) { + u_char status; + + wait_WOC(iobase); + if (!(inb(iobase+NETWAVE_REG_CCSR) & 0x02)) + break; /* None of the interrupt sources asserted */ + + status = inb(iobase + NETWAVE_REG_ASR); + + if ( ! (link->state & DEV_PRESENT) || link->state & DEV_SUSPEND ) { + DEBUG( 1, "netwave_interupt: Interrupt with status 0x%x " + "from removed or suspended card!\n", status); + break; + } + + /* RxRdy */ + if (status & 0x80) { + netwave_rx(dev); + /* wait_WOC(iobase); */ + /* RxRdy cannot be reset directly by the host */ + } + /* RxErr */ + if (status & 0x40) { + u_char rser; + + rser = readb(ramBase + NETWAVE_EREG_RSER); + + if (rser & 0x04) { + ++priv->stats.rx_dropped; + ++priv->stats.rx_crc_errors; + } + if (rser & 0x02) + ++priv->stats.rx_frame_errors; + + /* Clear the RxErr bit in RSER. RSER+4 is the + * write part. Also clear the RxCRC (0x04) and + * RxBig (0x02) bits if present */ + wait_WOC(iobase); + writeb(0x40 | (rser & 0x06), ramBase + NETWAVE_EREG_RSER + 4); + + /* Write bit 6 high to ASCC to clear RxErr in ASR, + * WOC must be set first! + */ + wait_WOC(iobase); + writeb(0x40, ramBase + NETWAVE_EREG_ASCC); + + /* Remember to count up priv->stats on error packets */ + ++priv->stats.rx_errors; + } + /* TxDN */ + if (status & 0x20) { + int txStatus; + + txStatus = readb(ramBase + NETWAVE_EREG_TSER); + DEBUG(3, "Transmit done. TSER = %x id %x\n", + txStatus, readb(ramBase + NETWAVE_EREG_TSER + 1)); + + if (txStatus & 0x20) { + /* Transmitting was okay, clear bits */ + wait_WOC(iobase); + writeb(0x2f, ramBase + NETWAVE_EREG_TSER + 4); + ++priv->stats.tx_packets; + } + + if (txStatus & 0xd0) { + if (txStatus & 0x80) { + ++priv->stats.collisions; /* Because of /proc/net/dev*/ + /* ++priv->stats.tx_aborted_errors; */ + /* printk("Collision. %ld\n", jiffies - dev->trans_start); */ + } + if (txStatus & 0x40) + ++priv->stats.tx_carrier_errors; + /* 0x80 TxGU Transmit giveup - nine times and no luck + * 0x40 TxNOAP No access point. Discarded packet. + * 0x10 TxErr Transmit error. Always set when + * TxGU and TxNOAP is set. (Those are the only ones + * to set TxErr). + */ + DEBUG(3, "netwave_interrupt: TxDN with error status %x\n", + txStatus); + + /* Clear out TxGU, TxNOAP, TxErr and TxTrys */ + wait_WOC(iobase); + writeb(0xdf & txStatus, ramBase+NETWAVE_EREG_TSER+4); + ++priv->stats.tx_errors; + } + DEBUG(3, "New status is TSER %x ASR %x\n", + readb(ramBase + NETWAVE_EREG_TSER), + inb(iobase + NETWAVE_REG_ASR)); + + /* If watchdog was activated, kill it ! */ + del_timer(&priv->watchdog); + + dev->tbusy = 0; + mark_bh(NET_BH); + } + /* TxBA, this would trigger on all error packets received */ + /* if (status & 0x01) { + if (netwave_debug > 3) + printk(KERN_DEBUG "Transmit buffers available, %x\n", status); + } + */ + } + /* done.. */ + dev->interrupt = 0; + return; +} /* netwave_interrupt */ + +/* + * Function netwave_watchdog (a) + * + * Watchdog : when we start a transmission, we set a timer in the + * kernel. If the transmission complete, this timer is disabled. If + * it expire, we reset the card. + * + */ +static void netwave_watchdog(u_long a) { + struct net_device *dev; + ioaddr_t iobase; + + dev = (struct net_device *) a; + iobase = dev->base_addr; + + DEBUG( 1, "%s: netwave_watchdog: watchdog timer expired\n", dev->name); + + netwave_reset(dev); + + /* We are not waiting anymore... */ + dev->tbusy = 0; + +} /* netwave_watchdog */ + +static struct enet_statistics *netwave_get_stats(struct net_device *dev) { + netwave_private *priv = (netwave_private*)dev->priv; + + update_stats(dev); + + DEBUG(2, "netwave: SPCQ %x SPU %x LIF %x ISPLQ %x MHS %x rxtx %x" + " %x tx %x %x %x %x\n", + readb(priv->ramBase + NETWAVE_EREG_SPCQ), + readb(priv->ramBase + NETWAVE_EREG_SPU), + readb(priv->ramBase + NETWAVE_EREG_LIF), + readb(priv->ramBase + NETWAVE_EREG_ISPLQ), + readb(priv->ramBase + NETWAVE_EREG_MHS), + readb(priv->ramBase + NETWAVE_EREG_EC + 0xe), + readb(priv->ramBase + NETWAVE_EREG_EC + 0xf), + readb(priv->ramBase + NETWAVE_EREG_EC + 0x18), + readb(priv->ramBase + NETWAVE_EREG_EC + 0x19), + readb(priv->ramBase + NETWAVE_EREG_EC + 0x1a), + readb(priv->ramBase + NETWAVE_EREG_EC + 0x1b)); + + return &priv->stats; +} + +static void update_stats(struct net_device *dev) { + unsigned long flags; + + save_flags(flags); + cli(); + +/* netwave_private *priv = (netwave_private*) dev->priv; + priv->stats.rx_packets = readb(priv->ramBase + 0x18e); + priv->stats.tx_packets = readb(priv->ramBase + 0x18f); */ + + restore_flags(flags); +} + +static int netwave_rx(struct net_device *dev) { + netwave_private *priv = (netwave_private*)(dev->priv); + u_char *ramBase = priv->ramBase; + ioaddr_t iobase = dev->base_addr; + u_char rxStatus; + struct sk_buff *skb = NULL; + unsigned int curBuffer, + rcvList; + int rcvLen; + int tmpcount = 0; + int dataCount, dataOffset; + int i; + u_char *ptr; + + DEBUG(3, "xinw_rx: Receiving ... \n"); + + /* Receive max 10 packets for now. */ + for (i = 0; i < 10; i++) { + /* Any packets? */ + wait_WOC(iobase); + rxStatus = readb(ramBase + NETWAVE_EREG_RSER); + if ( !( rxStatus & 0x80)) /* No more packets */ + break; + + /* Check if multicast/broadcast or other */ + /* multicast = (rxStatus & 0x20); */ + + /* The receive list pointer and length of the packet */ + wait_WOC(iobase); + rcvLen = get_int16( ramBase + NETWAVE_EREG_RDP); + rcvList = get_uint16( ramBase + NETWAVE_EREG_RDP + 2); + + if (rcvLen < 0) { + printk(KERN_DEBUG "netwave_rx: Receive packet with len %d\n", + rcvLen); + return 0; + } + + skb = dev_alloc_skb(rcvLen+5); + if (skb == NULL) { + DEBUG(1, "netwave_rx: Could not allocate an sk_buff of " + "length %d\n", rcvLen); + ++priv->stats.rx_dropped; + /* Tell the adapter to skip the packet */ + wait_WOC(iobase); + writeb(NETWAVE_CMD_SRP, ramBase + NETWAVE_EREG_CB + 0); + writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 1); + return 0; + } + + skb_reserve( skb, 2); /* Align IP on 16 byte */ + skb_put( skb, rcvLen); + skb->dev = dev; + + /* Copy packet fragments to the skb data area */ + ptr = (u_char*) skb->data; + curBuffer = rcvList; + tmpcount = 0; + while ( tmpcount < rcvLen) { + /* Get length and offset of current buffer */ + dataCount = get_uint16( ramBase+curBuffer+2); + dataOffset = get_uint16( ramBase+curBuffer+4); + + copy_from_pc( ptr + tmpcount, + ramBase+curBuffer+dataOffset, dataCount); + + tmpcount += dataCount; + + /* Point to next buffer */ + curBuffer = get_uint16(ramBase + curBuffer); + } + + skb->protocol = eth_type_trans(skb,dev); + /* Queue packet for network layer */ + netif_rx(skb); + + /* Got the packet, tell the adapter to skip it */ + wait_WOC(iobase); + writeb(NETWAVE_CMD_SRP, ramBase + NETWAVE_EREG_CB + 0); + writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 1); + DEBUG(3, "Packet reception ok\n"); + + priv->stats.rx_packets++; + + priv->stats.rx_bytes += skb->len; + } + return 0; +} + +static int netwave_open(struct net_device *dev) { + dev_link_t *link; + + DEBUG(1, "netwave_open: starting.\n"); + + for (link = dev_list; link; link = link->next) + if (link->priv == dev) break; + + if (!DEV_OK(link)) + return -ENODEV; + + link->open++; + MOD_INC_USE_COUNT; + + dev->interrupt = 0; dev->tbusy = 0; dev->start = 1; + netwave_reset(dev); + + return 0; +} + +static int netwave_close(struct net_device *dev) { + dev_link_t *link; + netwave_private *priv = (netwave_private *) dev->priv; + + DEBUG(1, "netwave_close: finishing.\n"); + + for (link = dev_list; link; link = link->next) + if (link->priv == dev) break; + if (link == NULL) + return -ENODEV; + + /* If watchdog was activated, kill it ! */ + del_timer(&priv->watchdog); + + link->open--; + dev->start = 0; + if (link->state & DEV_STALE_CONFIG) { + link->release.expires = jiffies + 5; + link->state |= DEV_RELEASE_PENDING; + add_timer(&link->release); + } + + MOD_DEC_USE_COUNT; + return 0; +} + +static int __init init_netwave_cs(void) { + servinfo_t serv; + + DEBUG(0, "%s\n", version); + + CardServices(GetCardServicesInfo, &serv); + if (serv.Revision != CS_RELEASE_CODE) { + printk("netwave_cs: Card Services release does not match!\n"); + return -1; + } + + register_pccard_driver(&dev_info, &netwave_attach, &netwave_detach); + + return 0; +} + +static void __exit exit_netwave_cs(void) { + DEBUG(1, "netwave_cs: unloading\n"); + + unregister_pccard_driver(&dev_info); + + /* Do some cleanup of the device list */ + netwave_flush_stale_links(); + if(dev_list != NULL) /* Critical situation */ + printk("netwave_cs: devices remaining when removing module\n"); +} + +module_init(init_netwave_cs); +module_exit(exit_netwave_cs); + +/* Set or clear the multicast filter for this adaptor. + num_addrs == -1 Promiscuous mode, receive all packets + num_addrs == 0 Normal mode, clear multicast list + num_addrs > 0 Multicast mode, receive normal and MC packets, and do + best-effort filtering. + */ +static void set_multicast_list(struct net_device *dev) +{ + ioaddr_t iobase = dev->base_addr; + u_char* ramBase = ((netwave_private*) dev->priv)->ramBase; + u_char rcvMode = 0; + +#ifdef PCMCIA_DEBUG + if (pc_debug > 2) { + static int old = 0; + if (old != dev->mc_count) { + old = dev->mc_count; + DEBUG(0, "%s: setting Rx mode to %d addresses.\n", + dev->name, dev->mc_count); + } + } +#endif + + if (dev->mc_count || (dev->flags & IFF_ALLMULTI)) { + /* Multicast Mode */ + rcvMode = rxConfRxEna + rxConfAMP + rxConfBcast; + } else if (dev->flags & IFF_PROMISC) { + /* Promiscous mode */ + rcvMode = rxConfRxEna + rxConfPro + rxConfAMP + rxConfBcast; + } else { + /* Normal mode */ + rcvMode = rxConfRxEna + rxConfBcast; + } + + /* printk("netwave set_multicast_list: rcvMode to %x\n", rcvMode);*/ + /* Now set receive mode */ + wait_WOC(iobase); + writeb(NETWAVE_CMD_SRC, ramBase + NETWAVE_EREG_CB + 0); + writeb(rcvMode, ramBase + NETWAVE_EREG_CB + 1); + writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 2); +} diff --git a/drivers/net/pcmcia/nmclan_cs.c b/drivers/net/pcmcia/nmclan_cs.c new file mode 100644 index 000000000..45aab6d62 --- /dev/null +++ b/drivers/net/pcmcia/nmclan_cs.c @@ -0,0 +1,1778 @@ +/* ---------------------------------------------------------------------------- +Linux PCMCIA ethernet adapter driver for the New Media Ethernet LAN. + nmclan_cs.c,v 0.16 1995/07/01 06:42:17 rpao Exp rpao + + The Ethernet LAN uses the Advanced Micro Devices (AMD) Am79C940 Media + Access Controller for Ethernet (MACE). It is essentially the Am2150 + PCMCIA Ethernet card contained in the the Am2150 Demo Kit. + +Written by Roger C. Pao <rpao@paonet.org> + Copyright 1995 Roger C. Pao + + This software may be used and distributed according to the terms of + the GNU Public License. + +Ported to Linux 1.3.* network driver environment by + Matti Aarnio <mea@utu.fi> + +References + + Am2150 Technical Reference Manual, Revision 1.0, August 17, 1993 + Am79C940 (MACE) Data Sheet, 1994 + Am79C90 (C-LANCE) Data Sheet, 1994 + Linux PCMCIA Programmer's Guide v1.17 + /usr/src/linux/net/inet/dev.c, Linux kernel 1.2.8 + + Eric Mears, New Media Corporation + Tom Pollard, New Media Corporation + Dean Siasoyco, New Media Corporation + Ken Lesniak, Silicon Graphics, Inc. <lesniak@boston.sgi.com> + Donald Becker <becker@cesdis1.gsfc.nasa.gov> + David Hinds <dhinds@hyper.stanford.edu> + + The Linux client driver is based on the 3c589_cs.c client driver by + David Hinds. + + The Linux network driver outline is based on the 3c589_cs.c driver, + the 8390.c driver, and the example skeleton.c kernel code, which are + by Donald Becker. + + The Am2150 network driver hardware interface code is based on the + OS/9000 driver for the New Media Ethernet LAN by Eric Mears. + + Special thanks for testing and help in debugging this driver goes + to Ken Lesniak. + +------------------------------------------------------------------------------- +Driver Notes and Issues +------------------------------------------------------------------------------- + +1. Developed on a Dell 320SLi + PCMCIA Card Services 2.6.2 + Linux dell 1.2.10 #1 Thu Jun 29 20:23:41 PDT 1995 i386 + +2. rc.pcmcia may require loading pcmcia_core with io_speed=300: + 'insmod pcmcia_core.o io_speed=300'. + This will avoid problems with fast systems which causes rx_framecnt + to return random values. + +3. If hot extraction does not work for you, use 'ifconfig eth0 down' + before extraction. + +4. There is a bad slow-down problem in this driver. + +5. Future: Multicast processing. In the meantime, do _not_ compile your + kernel with multicast ip enabled. + +------------------------------------------------------------------------------- +History +------------------------------------------------------------------------------- +Log: nmclan_cs.c,v + * Revision 0.16 1995/07/01 06:42:17 rpao + * Bug fix: nmclan_reset() called CardServices incorrectly. + * + * Revision 0.15 1995/05/24 08:09:47 rpao + * Re-implement MULTI_TX dev->tbusy handling. + * + * Revision 0.14 1995/05/23 03:19:30 rpao + * Added, in nmclan_config(), "tuple.Attributes = 0;". + * Modified MACE ID check to ignore chip revision level. + * Avoid tx_free_frames race condition between _start_xmit and _interrupt. + * + * Revision 0.13 1995/05/18 05:56:34 rpao + * Statistics changes. + * Bug fix: nmclan_reset did not enable TX and RX: call restore_multicast_list. + * Bug fix: mace_interrupt checks ~MACE_IMR_DEFAULT. Fixes driver lockup. + * + * Revision 0.12 1995/05/14 00:12:23 rpao + * Statistics overhaul. + * + +95/05/13 rpao V0.10a + Bug fix: MACE statistics counters used wrong I/O ports. + Bug fix: mace_interrupt() needed to allow statistics to be + processed without RX or TX interrupts pending. +95/05/11 rpao V0.10 + Multiple transmit request processing. + Modified statistics to use MACE counters where possible. +95/05/10 rpao V0.09 Bug fix: Must use IO_DATA_PATH_WIDTH_AUTO. + *Released +95/05/10 rpao V0.08 + Bug fix: Make all non-exported functions private by using + static keyword. + Bug fix: Test IntrCnt _before_ reading MACE_IR. +95/05/10 rpao V0.07 Statistics. +95/05/09 rpao V0.06 Fix rx_framecnt problem by addition of PCIC wait states. + +---------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------------- +Conditional Compilation Options +---------------------------------------------------------------------------- */ + +#define MULTI_TX 0 +#define TIMEOUT_TX 1 +#define RESET_ON_TIMEOUT 1 +#define TX_INTERRUPTABLE 1 +#define RESET_XILINX 0 + +/* ---------------------------------------------------------------------------- +Include Files +---------------------------------------------------------------------------- */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/ptrace.h> +#include <linux/malloc.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/interrupt.h> +#include <linux/in.h> +#include <linux/delay.h> +#include <asm/io.h> +#include <asm/system.h> +#include <asm/bitops.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/if_arp.h> +#include <linux/ioport.h> + +#include <pcmcia/version.h> +#include <pcmcia/cs_types.h> +#include <pcmcia/cs.h> +#include <pcmcia/cisreg.h> +#include <pcmcia/cistpl.h> +#include <pcmcia/ds.h> + +/* ---------------------------------------------------------------------------- +Defines +---------------------------------------------------------------------------- */ + +#define ETHER_ADDR_LEN ETH_ALEN + /* 6 bytes in an Ethernet Address */ +#define MACE_LADRF_LEN 8 + /* 8 bytes in Logical Address Filter */ + +/* Transmitter Busy Bit Index Defines */ +#define TBUSY_UNSPECIFIED 0 +#define TBUSY_PARTIAL_TX_FRAME 0 +#define TBUSY_NO_FREE_TX_FRAMES 1 + +/* Loop Control Defines */ +#define MACE_MAX_IR_ITERATIONS 10 +#define MACE_MAX_RX_ITERATIONS 12 + /* + TBD: Dean brought this up, and I assumed the hardware would + handle it: + + If MACE_MAX_RX_ITERATIONS is > 1, rx_framecnt may still be + non-zero when the isr exits. We may not get another interrupt + to process the remaining packets for some time. + */ + +/* +The Am2150 has a Xilinx XC3042 field programmable gate array (FPGA) +which manages the interface between the MACE and the PCMCIA bus. It +also includes buffer management for the 32K x 8 SRAM to control up to +four transmit and 12 receive frames at a time. +*/ +#define AM2150_MAX_TX_FRAMES 4 +#define AM2150_MAX_RX_FRAMES 12 + +/* Am2150 Ethernet Card I/O Mapping */ +#define AM2150_RCV 0x00 +#define AM2150_XMT 0x04 +#define AM2150_XMT_SKIP 0x09 +#define AM2150_RCV_NEXT 0x0A +#define AM2150_RCV_FRAME_COUNT 0x0B +#define AM2150_MACE_BANK 0x0C +#define AM2150_MACE_BASE 0x10 + +/* MACE Registers */ +#define MACE_RCVFIFO 0 +#define MACE_XMTFIFO 1 +#define MACE_XMTFC 2 +#define MACE_XMTFS 3 +#define MACE_XMTRC 4 +#define MACE_RCVFC 5 +#define MACE_RCVFS 6 +#define MACE_FIFOFC 7 +#define MACE_IR 8 +#define MACE_IMR 9 +#define MACE_PR 10 +#define MACE_BIUCC 11 +#define MACE_FIFOCC 12 +#define MACE_MACCC 13 +#define MACE_PLSCC 14 +#define MACE_PHYCC 15 +#define MACE_CHIPIDL 16 +#define MACE_CHIPIDH 17 +#define MACE_IAC 18 +/* Reserved */ +#define MACE_LADRF 20 +#define MACE_PADR 21 +/* Reserved */ +/* Reserved */ +#define MACE_MPC 24 +/* Reserved */ +#define MACE_RNTPC 26 +#define MACE_RCVCC 27 +/* Reserved */ +#define MACE_UTR 29 +#define MACE_RTR1 30 +#define MACE_RTR2 31 + +/* MACE Bit Masks */ +#define MACE_XMTRC_EXDEF 0x80 +#define MACE_XMTRC_XMTRC 0x0F + +#define MACE_XMTFS_XMTSV 0x80 +#define MACE_XMTFS_UFLO 0x40 +#define MACE_XMTFS_LCOL 0x20 +#define MACE_XMTFS_MORE 0x10 +#define MACE_XMTFS_ONE 0x08 +#define MACE_XMTFS_DEFER 0x04 +#define MACE_XMTFS_LCAR 0x02 +#define MACE_XMTFS_RTRY 0x01 + +#define MACE_RCVFS_RCVSTS 0xF000 +#define MACE_RCVFS_OFLO 0x8000 +#define MACE_RCVFS_CLSN 0x4000 +#define MACE_RCVFS_FRAM 0x2000 +#define MACE_RCVFS_FCS 0x1000 + +#define MACE_FIFOFC_RCVFC 0xF0 +#define MACE_FIFOFC_XMTFC 0x0F + +#define MACE_IR_JAB 0x80 +#define MACE_IR_BABL 0x40 +#define MACE_IR_CERR 0x20 +#define MACE_IR_RCVCCO 0x10 +#define MACE_IR_RNTPCO 0x08 +#define MACE_IR_MPCO 0x04 +#define MACE_IR_RCVINT 0x02 +#define MACE_IR_XMTINT 0x01 + +#define MACE_MACCC_PROM 0x80 +#define MACE_MACCC_DXMT2PD 0x40 +#define MACE_MACCC_EMBA 0x20 +#define MACE_MACCC_RESERVED 0x10 +#define MACE_MACCC_DRCVPA 0x08 +#define MACE_MACCC_DRCVBC 0x04 +#define MACE_MACCC_ENXMT 0x02 +#define MACE_MACCC_ENRCV 0x01 + +#define MACE_PHYCC_LNKFL 0x80 +#define MACE_PHYCC_DLNKTST 0x40 +#define MACE_PHYCC_REVPOL 0x20 +#define MACE_PHYCC_DAPC 0x10 +#define MACE_PHYCC_LRT 0x08 +#define MACE_PHYCC_ASEL 0x04 +#define MACE_PHYCC_RWAKE 0x02 +#define MACE_PHYCC_AWAKE 0x01 + +#define MACE_IAC_ADDRCHG 0x80 +#define MACE_IAC_PHYADDR 0x04 +#define MACE_IAC_LOGADDR 0x02 + +#define MACE_UTR_RTRE 0x80 +#define MACE_UTR_RTRD 0x40 +#define MACE_UTR_RPA 0x20 +#define MACE_UTR_FCOLL 0x10 +#define MACE_UTR_RCVFCSE 0x08 +#define MACE_UTR_LOOP_INCL_MENDEC 0x06 +#define MACE_UTR_LOOP_NO_MENDEC 0x04 +#define MACE_UTR_LOOP_EXTERNAL 0x02 +#define MACE_UTR_LOOP_NONE 0x00 +#define MACE_UTR_RESERVED 0x01 + +/* Switch MACE register bank (only 0 and 1 are valid) */ +#define MACEBANK(win_num) outb((win_num), ioaddr + AM2150_MACE_BANK) + +#define MACE_IMR_DEFAULT \ + (0xFF - \ + ( \ + MACE_IR_CERR | \ + MACE_IR_RCVCCO | \ + MACE_IR_RNTPCO | \ + MACE_IR_MPCO | \ + MACE_IR_RCVINT | \ + MACE_IR_XMTINT \ + ) \ + ) +#undef MACE_IMR_DEFAULT +#define MACE_IMR_DEFAULT 0x00 /* New statistics handling: grab everything */ + +/* ---------------------------------------------------------------------------- +Type Definitions +---------------------------------------------------------------------------- */ + +typedef struct _mace_statistics { + /* MACE_XMTFS */ + int xmtsv; + int uflo; + int lcol; + int more; + int one; + int defer; + int lcar; + int rtry; + + /* MACE_XMTRC */ + int exdef; + int xmtrc; + + /* RFS1--Receive Status (RCVSTS) */ + int oflo; + int clsn; + int fram; + int fcs; + + /* RFS2--Runt Packet Count (RNTPC) */ + int rfs_rntpc; + + /* RFS3--Receive Collision Count (RCVCC) */ + int rfs_rcvcc; + + /* MACE_IR */ + int jab; + int babl; + int cerr; + int rcvcco; + int rntpco; + int mpco; + + /* MACE_MPC */ + int mpc; + + /* MACE_RNTPC */ + int rntpc; + + /* MACE_RCVCC */ + int rcvcc; +} mace_statistics; + +typedef struct _mace_private { + dev_node_t node; + struct net_device_stats linux_stats; /* Linux statistics counters */ + mace_statistics mace_stats; /* MACE chip statistics counters */ + + /* restore_multicast_list() state variables */ + int multicast_ladrf[MACE_LADRF_LEN]; /* Logical address filter */ + int multicast_num_addrs; + + char tx_free_frames; /* Number of free transmit frame buffers */ + char tx_irq_disabled; /* MACE TX interrupt disabled */ +} mace_private; + +/* ---------------------------------------------------------------------------- +Private Global Variables +---------------------------------------------------------------------------- */ + +#ifdef PCMCIA_DEBUG +static char rcsid[] = +"nmclan_cs.c,v 0.16 1995/07/01 06:42:17 rpao Exp rpao"; +static char *version = +"nmclan_cs 0.16 (Roger C. Pao)"; +#endif + +static dev_info_t dev_info="nmclan_cs"; +static dev_link_t *dev_list=NULL; + +static char *if_names[]={ + "Auto", + "10baseT", + "BNC", +}; + +#ifdef PCMCIA_DEBUG +static int pc_debug = PCMCIA_DEBUG; +MODULE_PARM(pc_debug, "i"); +#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) +#else +#define DEBUG(n, args...) +#endif + +/* ---------------------------------------------------------------------------- +Parameters + These are the parameters that can be set during loading with + 'insmod'. +---------------------------------------------------------------------------- */ + +static int if_port=0; /* default=auto */ + /* + * 0=auto + * 1=10base-T (twisted pair) + * 2=10base-2 (BNC) + */ + +/* Bit map of interrupts to choose from */ +static u_int irq_mask = 0xdeb8; +static int irq_list[4] = { -1 }; + +MODULE_PARM(if_port, "i"); +MODULE_PARM(irq_mask, "i"); +MODULE_PARM(irq_list, "1-4i"); + +/* ---------------------------------------------------------------------------- +Function Prototypes +---------------------------------------------------------------------------- */ + +static void nmclan_config(dev_link_t *link); +static void nmclan_release(u_long arg); +static int nmclan_event(event_t event, int priority, + event_callback_args_t *args); + +static void nmclan_reset(struct net_device *dev); +static int mace_config(struct net_device *dev, struct ifmap *map); +static int mace_open(struct net_device *dev); +static int mace_close(struct net_device *dev); +static int mace_start_xmit(struct sk_buff *skb, struct net_device *dev); +static void mace_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static struct net_device_stats *mace_get_stats(struct net_device *dev); +static int mace_rx(struct net_device *dev, unsigned char RxCnt); +static void restore_multicast_list(struct net_device *dev); + +static void set_multicast_list(struct net_device *dev); + +static dev_link_t *nmclan_attach(void); +static void nmclan_detach(dev_link_t *); + +/* ---------------------------------------------------------------------------- +flush_stale_links + Clean up stale device structures +---------------------------------------------------------------------------- */ + +static void flush_stale_links(void) +{ + dev_link_t *link, *next; + for (link = dev_list; link; link = next) { + next = link->next; + if (link->state & DEV_STALE_LINK) + nmclan_detach(link); + } +} + +/* ---------------------------------------------------------------------------- +cs_error + Report a Card Services related error. +---------------------------------------------------------------------------- */ + +static void cs_error(client_handle_t handle, int func, int ret) +{ + error_info_t err = { func, ret }; + CardServices(ReportError, handle, &err); +} + +/* ---------------------------------------------------------------------------- +nmclan_init + We never need to do anything when a nmclan device is "initialized" + by the net software, because we only register already-found cards. +---------------------------------------------------------------------------- */ +static int nmclan_init(struct net_device *dev) +{ + return 0; +} /* nmclan_init */ + +/* ---------------------------------------------------------------------------- +nmclan_attach + Creates an "instance" of the driver, allocating local data + structures for one device. The device is registered with Card + Services. +---------------------------------------------------------------------------- */ +static dev_link_t *nmclan_attach(void) +{ + client_reg_t client_reg; + dev_link_t *link; + struct net_device *dev; + int i, ret; + + DEBUG(0, "nmclan_attach()\n"); + DEBUG(1, "%s\n", rcsid); + flush_stale_links(); + + /* Create new ethernet device */ + link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL); + memset(link, 0, sizeof(struct dev_link_t)); + link->release.function = &nmclan_release; + link->release.data = (u_long)link; + link->io.NumPorts1 = 32; + link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; + link->io.IOAddrLines = 5; + link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; + link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID; + if (irq_list[0] == -1) + link->irq.IRQInfo2 = irq_mask; + else + for (i = 0; i < 4; i++) + link->irq.IRQInfo2 |= 1 << irq_list[i]; + link->irq.Handler = &mace_interrupt; + link->conf.Attributes = CONF_ENABLE_IRQ; + link->conf.Vcc = 50; + link->conf.IntType = INT_MEMORY_AND_IO; + link->conf.ConfigIndex = 1; + link->conf.Present = PRESENT_OPTION; + + dev = kmalloc(sizeof(struct net_device), GFP_KERNEL); + memset(dev, 0, sizeof(struct net_device)); + + /* Allocate private data area for this device. */ + dev->priv = kmalloc(sizeof(mace_private), GFP_KERNEL); + memset(dev->priv, 0, sizeof(mace_private)); + ((mace_private *)dev->priv)->tx_free_frames=AM2150_MAX_TX_FRAMES; + + dev->hard_start_xmit = &mace_start_xmit; + dev->set_config = &mace_config; + dev->get_stats = &mace_get_stats; + dev->set_multicast_list = &set_multicast_list; + ether_setup(dev); + dev->name = ((mace_private *)dev->priv)->node.dev_name; + dev->init = &nmclan_init; + dev->open = &mace_open; + dev->stop = &mace_close; + dev->tbusy = 0xFF; + link->priv = link->irq.Instance = dev; + + /* Register with Card Services */ + link->next = dev_list; + dev_list = link; + client_reg.dev_info = &dev_info; + client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; + client_reg.EventMask = + CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | + CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | + CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; + client_reg.event_handler = &nmclan_event; + client_reg.Version = 0x0210; + client_reg.event_callback_args.client_data = link; + ret = CardServices(RegisterClient, &link->handle, &client_reg); + if (ret != 0) { + cs_error(link->handle, RegisterClient, ret); + nmclan_detach(link); + return NULL; + } + + return link; +} /* nmclan_attach */ + +/* ---------------------------------------------------------------------------- +nmclan_detach + This deletes a driver "instance". The device is de-registered + with Card Services. If it has been released, all local data + structures are freed. Otherwise, the structures will be freed + when the device is released. +---------------------------------------------------------------------------- */ +static void nmclan_detach(dev_link_t *link) +{ + dev_link_t **linkp; + + DEBUG(0, "nmclan_detach(0x%p)\n", link); + + /* Locate device structure */ + for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) + if (*linkp == link) break; + if (*linkp == NULL) + return; + + if (link->state & DEV_CONFIG) { + nmclan_release((u_long)link); + if (link->state & DEV_STALE_CONFIG) { + link->state |= DEV_STALE_LINK; + return; + } + } + + if (link->handle) + CardServices(DeregisterClient, link->handle); + + /* Unlink device structure, free bits */ + *linkp = link->next; + if (link->priv) { + struct net_device *dev = link->priv; + if (link->dev != NULL) + unregister_netdev(dev); + if (dev->priv) + kfree(dev->priv); + kfree(link->priv); + } + kfree(link); + +} /* nmclan_detach */ + +/* ---------------------------------------------------------------------------- +mace_read + Reads a MACE register. This is bank independent; however, the + caller must ensure that this call is not interruptable. We are + assuming that during normal operation, the MACE is always in + bank 0. +---------------------------------------------------------------------------- */ +static int mace_read(ioaddr_t ioaddr, int reg) +{ + int data = 0xFF; + unsigned long flags; + + switch (reg >> 4) { + case 0: /* register 0-15 */ + data = inb(ioaddr + AM2150_MACE_BASE + reg); + break; + case 1: /* register 16-31 */ + save_flags(flags); + cli(); + MACEBANK(1); + data = inb(ioaddr + AM2150_MACE_BASE + (reg & 0x0F)); + MACEBANK(0); + restore_flags(flags); + break; + } + return (data & 0xFF); +} /* mace_read */ + +/* ---------------------------------------------------------------------------- +mace_write + Writes to a MACE register. This is bank independent; however, + the caller must ensure that this call is not interruptable. We + are assuming that during normal operation, the MACE is always in + bank 0. +---------------------------------------------------------------------------- */ +static void mace_write(ioaddr_t ioaddr, int reg, int data) +{ + unsigned long flags; + + switch (reg >> 4) { + case 0: /* register 0-15 */ + outb(data & 0xFF, ioaddr + AM2150_MACE_BASE + reg); + break; + case 1: /* register 16-31 */ + save_flags(flags); + cli(); + MACEBANK(1); + outb(data & 0xFF, ioaddr + AM2150_MACE_BASE + (reg & 0x0F)); + MACEBANK(0); + restore_flags(flags); + break; + } +} /* mace_write */ + +/* ---------------------------------------------------------------------------- +mace_init + Resets the MACE chip. +---------------------------------------------------------------------------- */ +static void mace_init(ioaddr_t ioaddr, char *enet_addr) +{ + int i; + + /* MACE Software reset */ + mace_write(ioaddr, MACE_BIUCC, 1); + while (mace_read(ioaddr, MACE_BIUCC) & 0x01) { + /* Wait for reset bit to be cleared automatically after <= 200ns */; + } + mace_write(ioaddr, MACE_BIUCC, 0); + + /* The Am2150 requires that the MACE FIFOs operate in burst mode. */ + mace_write(ioaddr, MACE_FIFOCC, 0x0F); + + mace_write(ioaddr, MACE_RCVFC, 0); /* Disable Auto Strip Receive */ + mace_write(ioaddr, MACE_IMR, 0xFF); /* Disable all interrupts until _open */ + + /* + * Bit 2-1 PORTSEL[1-0] Port Select. + * 00 AUI/10Base-2 + * 01 10Base-T + * 10 DAI Port (reserved in Am2150) + * 11 GPSI + * For this card, only the first two are valid. + * So, PLSCC should be set to + * 0x00 for 10Base-2 + * 0x02 for 10Base-T + * Or just set ASEL in PHYCC below! + */ + switch (if_port) { + case 1: + mace_write(ioaddr, MACE_PLSCC, 0x02); + break; + case 2: + mace_write(ioaddr, MACE_PLSCC, 0x00); + break; + default: + mace_write(ioaddr, MACE_PHYCC, /* ASEL */ 4); + /* ASEL Auto Select. When set, the PORTSEL[1-0] bits are overridden, + and the MACE device will automatically select the operating media + interface port. */ + break; + } + + mace_write(ioaddr, MACE_IAC, MACE_IAC_ADDRCHG | MACE_IAC_PHYADDR); + /* Poll ADDRCHG bit */ + while (mace_read(ioaddr, MACE_IAC) & MACE_IAC_ADDRCHG) + ; + /* Set PADR register */ + for (i = 0; i < ETHER_ADDR_LEN; i++) + mace_write(ioaddr, MACE_PADR, enet_addr[i]); + + /* MAC Configuration Control Register should be written last */ + /* Let set_multicast_list set this. */ + /* mace_write(ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); */ + mace_write(ioaddr, MACE_MACCC, 0x00); +} /* mace_init */ + +/* ---------------------------------------------------------------------------- +nmclan_config + This routine is scheduled to run after a CARD_INSERTION event + is received, to configure the PCMCIA socket, and to make the + ethernet device available to the system. +---------------------------------------------------------------------------- */ + +#define CS_CHECK(fn, args...) \ +while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed + +static void nmclan_config(dev_link_t *link) +{ + client_handle_t handle; + struct net_device *dev; + tuple_t tuple; + cisparse_t parse; + u_char buf[64]; + int i, last_ret, last_fn; + ioaddr_t ioaddr; + u_short *phys_addr; + + handle = link->handle; + dev = link->priv; + phys_addr = (u_short *)dev->dev_addr; + + DEBUG(0, "nmclan_config(0x%p)\n", link); + + tuple.Attributes = 0; + tuple.TupleData = buf; + tuple.TupleDataMax = 64; + tuple.TupleOffset = 0; + tuple.DesiredTuple = CISTPL_CONFIG; + CS_CHECK(GetFirstTuple, handle, &tuple); + CS_CHECK(GetTupleData, handle, &tuple); + CS_CHECK(ParseTuple, handle, &tuple, &parse); + link->conf.ConfigBase = parse.config.base; + + /* Configure card */ + link->state |= DEV_CONFIG; + + CS_CHECK(RequestIO, handle, &link->io); + CS_CHECK(RequestIRQ, handle, &link->irq); + CS_CHECK(RequestConfiguration, handle, &link->conf); + dev->irq = link->irq.AssignedIRQ; + dev->base_addr = link->io.BasePort1; + dev->tbusy = 0; + i = register_netdev(dev); + if (i != 0) { + printk(KERN_NOTICE "nmclan_cs: register_netdev() failed\n"); + goto failed; + } + + ioaddr = dev->base_addr; + + /* Read the ethernet address from the CIS. */ + tuple.DesiredTuple = 0x80 /* CISTPL_CFTABLE_ENTRY_MISC */; + tuple.TupleData = buf; + tuple.TupleDataMax = 64; + tuple.TupleOffset = 0; + CS_CHECK(GetFirstTuple, handle, &tuple); + CS_CHECK(GetTupleData, handle, &tuple); + memcpy(dev->dev_addr, tuple.TupleData, ETHER_ADDR_LEN); + + /* Verify configuration by reading the MACE ID. */ + { + char sig[2]; + + sig[0] = mace_read(ioaddr, MACE_CHIPIDL); + sig[1] = mace_read(ioaddr, MACE_CHIPIDH); + if ((sig[0] == 0x40) && ((sig[1] & 0x0F) == 0x09)) { + DEBUG(0, "nmclan_cs configured: mace id=%x %x\n", + sig[0], sig[1]); + } else { + printk(KERN_NOTICE "nmclan_cs: mace id not found: %x %x should" + " be 0x40 0x?9\n", sig[0], sig[1]); + link->state &= ~DEV_CONFIG_PENDING; + return; + } + } + + mace_init(ioaddr, dev->dev_addr); + + /* The if_port symbol can be set when the module is loaded */ + if (if_port <= 2) + dev->if_port = if_port; + else + printk(KERN_NOTICE "nmclan_cs: invalid if_port requested\n"); + + link->dev = &((mace_private *)dev->priv)->node; + link->state &= ~DEV_CONFIG_PENDING; + + printk(KERN_INFO "%s: nmclan: port %#3lx, irq %d, %s port, hw_addr ", + dev->name, dev->base_addr, dev->irq, if_names[dev->if_port]); + for (i = 0; i < 6; i++) + printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n")); + return; + +cs_failed: + cs_error(link->handle, last_fn, last_ret); +failed: + nmclan_release((u_long)link); + return; + +} /* nmclan_config */ + +/* ---------------------------------------------------------------------------- +nmclan_release + After a card is removed, nmclan_release() will unregister the + net device, and release the PCMCIA configuration. If the device + is still open, this will be postponed until it is closed. +---------------------------------------------------------------------------- */ +static void nmclan_release(u_long arg) +{ + dev_link_t *link = (dev_link_t *)arg; + + DEBUG(0, "nmclan_release(0x%p)\n", link); + + if (link->open) { + DEBUG(1, "nmclan_cs: release postponed, '%s' " + "still open\n", link->dev->dev_name); + link->state |= DEV_STALE_CONFIG; + return; + } + + CardServices(ReleaseConfiguration, link->handle); + CardServices(ReleaseIO, link->handle, &link->io); + CardServices(ReleaseIRQ, link->handle, &link->irq); + + link->state &= ~(DEV_CONFIG | DEV_RELEASE_PENDING); + +} /* nmclan_release */ + +/* ---------------------------------------------------------------------------- +nmclan_event + The card status event handler. Mostly, this schedules other + stuff to run after an event is received. A CARD_REMOVAL event + also sets some flags to discourage the net drivers from trying + to talk to the card any more. +---------------------------------------------------------------------------- */ +static int nmclan_event(event_t event, int priority, + event_callback_args_t *args) +{ + dev_link_t *link = args->client_data; + struct net_device *dev = link->priv; + + DEBUG(1, "nmclan_event(0x%06x)\n", event); + + switch (event) { + case CS_EVENT_CARD_REMOVAL: + link->state &= ~DEV_PRESENT; + if (link->state & DEV_CONFIG) { + dev->tbusy = 0xFF; dev->start = 0; + link->release.expires = jiffies + HZ/20; + add_timer(&link->release); + } + break; + case CS_EVENT_CARD_INSERTION: + link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; + nmclan_config(link); + break; + case CS_EVENT_PM_SUSPEND: + link->state |= DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_RESET_PHYSICAL: + if (link->state & DEV_CONFIG) { + if (link->open) { + dev->tbusy = 0xFF; dev->start = 0; + } + CardServices(ReleaseConfiguration, link->handle); + } + break; + case CS_EVENT_PM_RESUME: + link->state &= ~DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_CARD_RESET: + if (link->state & DEV_CONFIG) { + CardServices(RequestConfiguration, link->handle, &link->conf); + if (link->open) { + dev->tbusy = 0; + dev->start = 1; + nmclan_reset(dev); + } + } + break; + case CS_EVENT_RESET_REQUEST: + return 1; + break; + } + return 0; +} /* nmclan_event */ + +/* ------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------------- +nmclan_reset + Reset and restore all of the Xilinx and MACE registers. +---------------------------------------------------------------------------- */ +static void nmclan_reset(struct net_device *dev) +{ + +#if RESET_XILINX + dev_link_t *link; + conf_reg_t reg; + u_long OrigCorValue; + + /* Find our client handle. */ + for (link = dev_list; link; link = link->next) + if (link->priv == dev) break; + if (link == NULL) { + printk(KERN_NOTICE "nmclan_cs: bad device pointer!\n"); + return; + } + + /* Save original COR value */ + reg.Function = 0; + reg.Action = CS_READ; + reg.Offset = CISREG_COR; + reg.Value = 0; + CardServices(AccessConfigurationRegister, link->handle, ®); + OrigCorValue = reg.Value; + + /* Reset Xilinx */ + reg.Action = CS_WRITE; + reg.Offset = CISREG_COR; + DEBUG(1, "nmclan_reset: OrigCorValue=0x%lX, resetting...\n", + OrigCorValue); + reg.Value = COR_SOFT_RESET; + CardServices(AccessConfigurationRegister, link->handle, ®); + /* Need to wait for 20 ms for PCMCIA to finish reset. */ + + /* Restore original COR configuration index */ + reg.Value = COR_LEVEL_REQ | (OrigCorValue & COR_CONFIG_MASK); + CardServices(AccessConfigurationRegister, link->handle, ®); + /* Xilinx is now completely reset along with the MACE chip. */ + ((mace_private *)dev->priv)->tx_free_frames=AM2150_MAX_TX_FRAMES; + +#endif /* #if RESET_XILINX */ + + /* Xilinx is now completely reset along with the MACE chip. */ + ((mace_private *)dev->priv)->tx_free_frames=AM2150_MAX_TX_FRAMES; + + /* Reinitialize the MACE chip for operation. */ + mace_init(dev->base_addr, dev->dev_addr); + mace_write(dev->base_addr, MACE_IMR, MACE_IMR_DEFAULT); + + /* Restore the multicast list and enable TX and RX. */ + restore_multicast_list(dev); +} /* nmclan_reset */ + +/* ---------------------------------------------------------------------------- +mace_config + [Someone tell me what this is supposed to do? Is if_port a defined + standard? If so, there should be defines to indicate 1=10Base-T, + 2=10Base-2, etc. including limited automatic detection.] +---------------------------------------------------------------------------- */ +static int mace_config(struct net_device *dev, struct ifmap *map) +{ + if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) { + if (map->port <= 2) { + dev->if_port = map->port; + printk(KERN_INFO "%s: switched to %s port\n", dev->name, + if_names[dev->if_port]); + } + else + return -EINVAL; + } + return 0; +} /* mace_config */ + +/* ---------------------------------------------------------------------------- +mace_open + Open device driver. +---------------------------------------------------------------------------- */ +static int mace_open(struct net_device *dev) +{ + ioaddr_t ioaddr = dev->base_addr; + dev_link_t *link; + + for (link = dev_list; link; link = link->next) + if (link->priv == dev) break; + if (!DEV_OK(link)) + return -ENODEV; + + link->open++; + MOD_INC_USE_COUNT; + + MACEBANK(0); + + dev->interrupt = 0; + dev->tbusy = 0; + dev->start = 1; + + nmclan_reset(dev); + + return 0; /* Always succeed */ +} /* mace_open */ + +/* ---------------------------------------------------------------------------- +mace_close + Closes device driver. +---------------------------------------------------------------------------- */ +static int mace_close(struct net_device *dev) +{ + ioaddr_t ioaddr = dev->base_addr; + dev_link_t *link; + + for (link = dev_list; link; link = link->next) + if (link->priv == dev) break; + if (link == NULL) + return -ENODEV; + + DEBUG(2, "%s: shutting down ethercard.\n", dev->name); + + /* Mask off all interrupts from the MACE chip. */ + outb(0xFF, ioaddr + AM2150_MACE_BASE + MACE_IMR); + + link->open--; + dev->start = 0; + if (link->state & DEV_STALE_CONFIG) { + link->release.expires = jiffies + HZ/20; + link->state |= DEV_RELEASE_PENDING; + add_timer(&link->release); + } + + MOD_DEC_USE_COUNT; + + return 0; +} /* mace_close */ + +/* ---------------------------------------------------------------------------- +mace_start_xmit + This routine begins the packet transmit function. When completed, + it will generate a transmit interrupt. + + According to /usr/src/linux/net/inet/dev.c, if _start_xmit + returns 0, the "packet is now solely the responsibility of the + driver." If _start_xmit returns non-zero, the "transmission + failed, put skb back into a list." +---------------------------------------------------------------------------- */ +static int mace_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + mace_private *lp = (mace_private *)dev->priv; + ioaddr_t ioaddr = dev->base_addr; + dev_link_t *link; + +#if TIMEOUT_TX + /* Transmitter timeout. */ + if (dev->tbusy) { + int tickssofar = jiffies - dev->trans_start; + + if (tickssofar < (HZ/5)) + return 1; + printk(KERN_NOTICE "%s: transmit timed out -- ", dev->name); +#if RESET_ON_TIMEOUT + printk("resetting card\n"); + for (link = dev_list; link; link = link->next) + if (link->priv == dev) break; + if (link) + CardServices(ResetCard, link->handle); +#else /* #if RESET_ON_TIMEOUT */ + printk("NOT resetting card\n"); +#endif /* #if RESET_ON_TIMEOUT */ + dev->trans_start = jiffies; + return 1; + } +#else /* #if TIMEOUT_TX */ + if (dev->tbusy) + return 1; +#endif /* #if TIMEOUT_TX */ + + DEBUG(3, "%s: mace_start_xmit(length = %ld) called.\n", + dev->name, (long)skb->len); + + /* Avoid timer-based retransmission conflicts. */ + if (test_and_set_bit(TBUSY_UNSPECIFIED, (void*)&dev->tbusy) != 0) { + printk(KERN_NOTICE "%s: transmitter access conflict.\n", + dev->name); + return 1; + } + +#if (!TX_INTERRUPTABLE) + /* Disable MACE TX interrupts. */ + outb(MACE_IMR_DEFAULT | MACE_IR_XMTINT, + ioaddr + AM2150_MACE_BASE + MACE_IMR); + lp->tx_irq_disabled=1; +#endif /* #if (!TX_INTERRUPTABLE) */ + + { + /* This block must not be interrupted by another transmit request! + dev->tbusy will take care of timer-based retransmissions from + the upper layers. The interrupt handler is guaranteed never to + service a transmit interrupt while we are in here. + */ + set_bit(TBUSY_PARTIAL_TX_FRAME, (void*)&dev->tbusy); + + lp->linux_stats.tx_bytes += skb->len; + lp->tx_free_frames--; + set_bit(TBUSY_NO_FREE_TX_FRAMES, (void*)&dev->tbusy); + + /* WARNING: Write the _exact_ number of bytes written in the header! */ + /* Put out the word header [must be an outw()] . . . */ + outw(skb->len, ioaddr + AM2150_XMT); + /* . . . and the packet [may be any combination of outw() and outb()] */ + outsw(ioaddr + AM2150_XMT, skb->data, skb->len >> 1); + if (skb->len & 1) { + /* Odd byte transfer */ + outb(skb->data[skb->len-1], ioaddr + AM2150_XMT); + } + + dev->trans_start = jiffies; + + if (lp->tx_free_frames > 0) { +#if MULTI_TX + clear_bit(TBUSY_NO_FREE_TX_FRAMES, (void*)&dev->tbusy); +#endif /* #if MULTI_TX */ + } + + clear_bit(TBUSY_PARTIAL_TX_FRAME, (void*)&dev->tbusy); + } + +#if (!TX_INTERRUPTABLE) + /* Re-enable MACE TX interrupts. */ + lp->tx_irq_disabled=0; + outb(MACE_IMR_DEFAULT, ioaddr + AM2150_MACE_BASE + MACE_IMR); +#endif /* #if (!TX_INTERRUPTABLE) */ + + dev_kfree_skb(skb); + + return 0; +} /* mace_start_xmit */ + +/* ---------------------------------------------------------------------------- +mace_interrupt + The interrupt handler. +---------------------------------------------------------------------------- */ +static void mace_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *)dev_id; + mace_private *lp = (mace_private *)dev->priv; + ioaddr_t ioaddr = dev->base_addr; + int status; + int IntrCnt = MACE_MAX_IR_ITERATIONS; + + if (dev == NULL) { + DEBUG(2, "mace_interrupt(): irq 0x%X for unknown device.\n", + irq); + return; + } + + if (dev->interrupt || lp->tx_irq_disabled) { + sti(); + printk( + (lp->tx_irq_disabled? + KERN_NOTICE "%s: Interrupt with tx_irq_disabled " + "[isr=%02X, imr=%02X]\n": + KERN_NOTICE "%s: Re-entering the interrupt handler " + "[isr=%02X, imr=%02X]\n"), + dev->name, + inb(ioaddr + AM2150_MACE_BASE + MACE_IR), + inb(ioaddr + AM2150_MACE_BASE + MACE_IMR) + ); + /* WARNING: MACE_IR has been read! */ + return; + } + dev->interrupt = 1; + sti(); + + if (dev->start == 0) { + DEBUG(2, "%s: interrupt from dead card\n", dev->name); + goto exception; + } + + do { + /* WARNING: MACE_IR is a READ/CLEAR port! */ + status = inb(ioaddr + AM2150_MACE_BASE + MACE_IR); + + DEBUG(3, "mace_interrupt: irq 0x%X status 0x%X.\n", irq, status); + + if (status & MACE_IR_RCVINT) { + mace_rx(dev, MACE_MAX_RX_ITERATIONS); + } + + if (status & MACE_IR_XMTINT) { + unsigned char fifofc; + unsigned char xmtrc; + unsigned char xmtfs; + + fifofc = inb(ioaddr + AM2150_MACE_BASE + MACE_FIFOFC); + if ((fifofc & MACE_FIFOFC_XMTFC)==0) { + lp->linux_stats.tx_errors++; + outb(0xFF, ioaddr + AM2150_XMT_SKIP); + } + + /* Transmit Retry Count (XMTRC, reg 4) */ + xmtrc = inb(ioaddr + AM2150_MACE_BASE + MACE_XMTRC); + if (xmtrc & MACE_XMTRC_EXDEF) lp->mace_stats.exdef++; + lp->mace_stats.xmtrc += (xmtrc & MACE_XMTRC_XMTRC); + + if ( + (xmtfs = inb(ioaddr + AM2150_MACE_BASE + MACE_XMTFS)) & + MACE_XMTFS_XMTSV /* Transmit Status Valid */ + ) { + lp->mace_stats.xmtsv++; + + if (xmtfs & ~MACE_XMTFS_XMTSV) { + if (xmtfs & MACE_XMTFS_UFLO) { + /* Underflow. Indicates that the Transmit FIFO emptied before + the end of frame was reached. */ + lp->mace_stats.uflo++; + } + if (xmtfs & MACE_XMTFS_LCOL) { + /* Late Collision */ + lp->mace_stats.lcol++; + } + if (xmtfs & MACE_XMTFS_MORE) { + /* MORE than one retry was needed */ + lp->mace_stats.more++; + } + if (xmtfs & MACE_XMTFS_ONE) { + /* Exactly ONE retry occurred */ + lp->mace_stats.one++; + } + if (xmtfs & MACE_XMTFS_DEFER) { + /* Transmission was defered */ + lp->mace_stats.defer++; + } + if (xmtfs & MACE_XMTFS_LCAR) { + /* Loss of carrier */ + lp->mace_stats.lcar++; + } + if (xmtfs & MACE_XMTFS_RTRY) { + /* Retry error: transmit aborted after 16 attempts */ + lp->mace_stats.rtry++; + } + } /* if (xmtfs & ~MACE_XMTFS_XMTSV) */ + + } /* if (xmtfs & MACE_XMTFS_XMTSV) */ + + lp->linux_stats.tx_packets++; + lp->tx_free_frames++; + clear_bit(TBUSY_NO_FREE_TX_FRAMES, (void*)&dev->tbusy); + mark_bh(NET_BH); + } /* if (status & MACE_IR_XMTINT) */ + + if (status & ~MACE_IMR_DEFAULT & ~MACE_IR_RCVINT & ~MACE_IR_XMTINT) { + if (status & MACE_IR_JAB) { + /* Jabber Error. Excessive transmit duration (20-150ms). */ + lp->mace_stats.jab++; + } + if (status & MACE_IR_BABL) { + /* Babble Error. >1518 bytes transmitted. */ + lp->mace_stats.babl++; + } + if (status & MACE_IR_CERR) { + /* Collision Error. CERR indicates the absence of the + Signal Quality Error Test message after a packet + transmission. */ + lp->mace_stats.cerr++; + } + if (status & MACE_IR_RCVCCO) { + /* Receive Collision Count Overflow; */ + lp->mace_stats.rcvcco++; + } + if (status & MACE_IR_RNTPCO) { + /* Runt Packet Count Overflow */ + lp->mace_stats.rntpco++; + } + if (status & MACE_IR_MPCO) { + /* Missed Packet Count Overflow */ + lp->mace_stats.mpco++; + } + } /* if (status & ~MACE_IMR_DEFAULT & ~MACE_IR_RCVINT & ~MACE_IR_XMTINT) */ + + } while ((status & ~MACE_IMR_DEFAULT) && (--IntrCnt)); + +exception: + dev->interrupt = 0; + return; +} /* mace_interrupt */ + +/* ---------------------------------------------------------------------------- +mace_rx + Receives packets. +---------------------------------------------------------------------------- */ +static int mace_rx(struct net_device *dev, unsigned char RxCnt) +{ + mace_private *lp = (mace_private *)dev->priv; + ioaddr_t ioaddr = dev->base_addr; + unsigned char rx_framecnt; + unsigned short rx_status; + + while ( + ((rx_framecnt = inb(ioaddr + AM2150_RCV_FRAME_COUNT)) > 0) && + (rx_framecnt <= 12) && /* rx_framecnt==0xFF if card is extracted. */ + (RxCnt--) + ) { + rx_status = inw(ioaddr + AM2150_RCV); + + DEBUG(3, "%s: in mace_rx(), framecnt 0x%X, rx_status" + " 0x%X.\n", dev->name, rx_framecnt, rx_status); + + if (rx_status & MACE_RCVFS_RCVSTS) { /* Error, update stats. */ + lp->linux_stats.rx_errors++; + if (rx_status & MACE_RCVFS_OFLO) { + lp->mace_stats.oflo++; + } + if (rx_status & MACE_RCVFS_CLSN) { + lp->mace_stats.clsn++; + } + if (rx_status & MACE_RCVFS_FRAM) { + lp->mace_stats.fram++; + } + if (rx_status & MACE_RCVFS_FCS) { + lp->mace_stats.fcs++; + } + } else { + short pkt_len = (rx_status & ~MACE_RCVFS_RCVSTS) - 4; + /* Auto Strip is off, always subtract 4 */ + struct sk_buff *skb; + + lp->mace_stats.rfs_rntpc += inb(ioaddr + AM2150_RCV); + /* runt packet count */ + lp->mace_stats.rfs_rcvcc += inb(ioaddr + AM2150_RCV); + /* rcv collision count */ + + DEBUG(3, " receiving packet size 0x%X rx_status" + " 0x%X.\n", pkt_len, rx_status); + + skb = dev_alloc_skb(pkt_len+2); + + if (skb != NULL) { + skb->dev = dev; + + skb_reserve(skb, 2); + insw(ioaddr + AM2150_RCV, skb_put(skb, pkt_len), pkt_len>>1); + if (pkt_len & 1) + *(skb->tail-1) = inb(ioaddr + AM2150_RCV); + skb->protocol = eth_type_trans(skb, dev); + + netif_rx(skb); /* Send the packet to the upper (protocol) layers. */ + + lp->linux_stats.rx_packets++; + lp->linux_stats.rx_bytes += skb->len; + outb(0xFF, ioaddr + AM2150_RCV_NEXT); /* skip to next frame */ + continue; + } else { + DEBUG(1, "%s: couldn't allocate a sk_buff of size" + " %d.\n", dev->name, pkt_len); + lp->linux_stats.rx_dropped++; + } + } + outb(0xFF, ioaddr + AM2150_RCV_NEXT); /* skip to next frame */ + } /* while */ + + return 0; +} /* mace_rx */ + +/* ---------------------------------------------------------------------------- +pr_linux_stats +---------------------------------------------------------------------------- */ +static void pr_linux_stats(struct net_device_stats *pstats) +{ + DEBUG(2, "pr_linux_stats\n"); + DEBUG(2, " rx_packets=%-7ld tx_packets=%ld\n", + (long)pstats->rx_packets, (long)pstats->tx_packets); + DEBUG(2, " rx_errors=%-7ld tx_errors=%ld\n", + (long)pstats->rx_errors, (long)pstats->tx_errors); + DEBUG(2, " rx_dropped=%-7ld tx_dropped=%ld\n", + (long)pstats->rx_dropped, (long)pstats->tx_dropped); + DEBUG(2, " multicast=%-7ld collisions=%ld\n", + (long)pstats->multicast, (long)pstats->collisions); + + DEBUG(2, " rx_length_errors=%-7ld rx_over_errors=%ld\n", + (long)pstats->rx_length_errors, (long)pstats->rx_over_errors); + DEBUG(2, " rx_crc_errors=%-7ld rx_frame_errors=%ld\n", + (long)pstats->rx_crc_errors, (long)pstats->rx_frame_errors); + DEBUG(2, " rx_fifo_errors=%-7ld rx_missed_errors=%ld\n", + (long)pstats->rx_fifo_errors, (long)pstats->rx_missed_errors); + + DEBUG(2, " tx_aborted_errors=%-7ld tx_carrier_errors=%ld\n", + (long)pstats->tx_aborted_errors, (long)pstats->tx_carrier_errors); + DEBUG(2, " tx_fifo_errors=%-7ld tx_heartbeat_errors=%ld\n", + (long)pstats->tx_fifo_errors, (long)pstats->tx_heartbeat_errors); + DEBUG(2, " tx_window_errors=%ld\n", + (long)pstats->tx_window_errors); +} /* pr_linux_stats */ + +/* ---------------------------------------------------------------------------- +pr_mace_stats +---------------------------------------------------------------------------- */ +static void pr_mace_stats(mace_statistics *pstats) +{ + DEBUG(2, "pr_mace_stats\n"); + + DEBUG(2, " xmtsv=%-7d uflo=%d\n", + pstats->xmtsv, pstats->uflo); + DEBUG(2, " lcol=%-7d more=%d\n", + pstats->lcol, pstats->more); + DEBUG(2, " one=%-7d defer=%d\n", + pstats->one, pstats->defer); + DEBUG(2, " lcar=%-7d rtry=%d\n", + pstats->lcar, pstats->rtry); + + /* MACE_XMTRC */ + DEBUG(2, " exdef=%-7d xmtrc=%d\n", + pstats->exdef, pstats->xmtrc); + + /* RFS1--Receive Status (RCVSTS) */ + DEBUG(2, " oflo=%-7d clsn=%d\n", + pstats->oflo, pstats->clsn); + DEBUG(2, " fram=%-7d fcs=%d\n", + pstats->fram, pstats->fcs); + + /* RFS2--Runt Packet Count (RNTPC) */ + /* RFS3--Receive Collision Count (RCVCC) */ + DEBUG(2, " rfs_rntpc=%-7d rfs_rcvcc=%d\n", + pstats->rfs_rntpc, pstats->rfs_rcvcc); + + /* MACE_IR */ + DEBUG(2, " jab=%-7d babl=%d\n", + pstats->jab, pstats->babl); + DEBUG(2, " cerr=%-7d rcvcco=%d\n", + pstats->cerr, pstats->rcvcco); + DEBUG(2, " rntpco=%-7d mpco=%d\n", + pstats->rntpco, pstats->mpco); + + /* MACE_MPC */ + DEBUG(2, " mpc=%d\n", pstats->mpc); + + /* MACE_RNTPC */ + DEBUG(2, " rntpc=%d\n", pstats->rntpc); + + /* MACE_RCVCC */ + DEBUG(2, " rcvcc=%d\n", pstats->rcvcc); + +} /* pr_mace_stats */ + +/* ---------------------------------------------------------------------------- +update_stats + Update statistics. We change to register window 1, so this + should be run single-threaded if the device is active. This is + expected to be a rare operation, and it's simpler for the rest + of the driver to assume that window 0 is always valid rather + than use a special window-state variable. + + oflo & uflo should _never_ occur since it would mean the Xilinx + was not able to transfer data between the MACE FIFO and the + card's SRAM fast enough. If this happens, something is + seriously wrong with the hardware. +---------------------------------------------------------------------------- */ +static void update_stats(ioaddr_t ioaddr, struct net_device *dev) +{ + mace_private *lp = (mace_private *)dev->priv; + + lp->mace_stats.rcvcc += mace_read(ioaddr, MACE_RCVCC); + lp->mace_stats.rntpc += mace_read(ioaddr, MACE_RNTPC); + lp->mace_stats.mpc += mace_read(ioaddr, MACE_MPC); + /* At this point, mace_stats is fully updated for this call. + We may now update the linux_stats. */ + + /* The MACE has no equivalent for linux_stats field which are commented + out. */ + + /* lp->linux_stats.multicast; */ + lp->linux_stats.collisions = + lp->mace_stats.rcvcco * 256 + lp->mace_stats.rcvcc; + /* Collision: The MACE may retry sending a packet 15 times + before giving up. The retry count is in XMTRC. + Does each retry constitute a collision? + If so, why doesn't the RCVCC record these collisions? */ + + /* detailed rx_errors: */ + lp->linux_stats.rx_length_errors = + lp->mace_stats.rntpco * 256 + lp->mace_stats.rntpc; + /* lp->linux_stats.rx_over_errors */ + lp->linux_stats.rx_crc_errors = lp->mace_stats.fcs; + lp->linux_stats.rx_frame_errors = lp->mace_stats.fram; + lp->linux_stats.rx_fifo_errors = lp->mace_stats.oflo; + lp->linux_stats.rx_missed_errors = + lp->mace_stats.mpco * 256 + lp->mace_stats.mpc; + + /* detailed tx_errors */ + lp->linux_stats.tx_aborted_errors = lp->mace_stats.rtry; + lp->linux_stats.tx_carrier_errors = lp->mace_stats.lcar; + /* LCAR usually results from bad cabling. */ + lp->linux_stats.tx_fifo_errors = lp->mace_stats.uflo; + lp->linux_stats.tx_heartbeat_errors = lp->mace_stats.cerr; + /* lp->linux_stats.tx_window_errors; */ + + return; +} /* update_stats */ + +/* ---------------------------------------------------------------------------- +mace_get_stats + Gathers ethernet statistics from the MACE chip. +---------------------------------------------------------------------------- */ +static struct net_device_stats *mace_get_stats(struct net_device *dev) +{ + mace_private *lp = (mace_private *)dev->priv; + + update_stats(dev->base_addr, dev); + + DEBUG(1, "%s: updating the statistics.\n", dev->name); + pr_linux_stats(&lp->linux_stats); + pr_mace_stats(&lp->mace_stats); + + return &lp->linux_stats; +} /* net_device_stats */ + +/* ---------------------------------------------------------------------------- +updateCRC + Modified from Am79C90 data sheet. +---------------------------------------------------------------------------- */ + +#if BROKEN_MULTICAST + +static void updateCRC(int *CRC, int bit) +{ + int poly[]={ + 1,1,1,0, 1,1,0,1, + 1,0,1,1, 1,0,0,0, + 1,0,0,0, 0,0,1,1, + 0,0,1,0, 0,0,0,0 + }; /* CRC polynomial. poly[n] = coefficient of the x**n term of the + CRC generator polynomial. */ + + int j; + + /* shift CRC and control bit (CRC[32]) */ + for (j = 32; j > 0; j--) + CRC[j] = CRC[j-1]; + CRC[0] = 0; + + /* If bit XOR(control bit) = 1, set CRC = CRC XOR polynomial. */ + if (bit ^ CRC[32]) + for (j = 0; j < 32; j++) + CRC[j] ^= poly[j]; +} /* updateCRC */ + +/* ---------------------------------------------------------------------------- +BuildLAF + Build logical address filter. + Modified from Am79C90 data sheet. + +Input + ladrf: logical address filter (contents initialized to 0) + adr: ethernet address +---------------------------------------------------------------------------- */ +static void BuildLAF(int *ladrf, int *adr) +{ + int CRC[33]={1}; /* CRC register, 1 word/bit + extra control bit */ + + int i, byte; /* temporary array indices */ + int hashcode; /* the output object */ + + CRC[32]=0; + + for (byte = 0; byte < 6; byte++) + for (i = 0; i < 8; i++) + updateCRC(CRC, (adr[byte] >> i) & 1); + + hashcode = 0; + for (i = 0; i < 6; i++) + hashcode = (hashcode << 1) + CRC[i]; + + byte = hashcode >> 3; + ladrf[byte] |= (1 << (hashcode & 7)); + +#ifdef PCMCIA_DEBUG + if (pc_debug > 2) { + printk(KERN_DEBUG " adr ="); + for (i = 0; i < 6; i++) + printk(" %02X", adr[i]); + printk("\n" KERN_DEBUG " hashcode = %d(decimal), ladrf[0:63]" + " =", hashcode); + for (i = 0; i < 8; i++) + printk(" %02X", ladrf[i]); + printk("\n"); + } +#endif +} /* BuildLAF */ + +/* ---------------------------------------------------------------------------- +restore_multicast_list + Restores the multicast filter for MACE chip to the last + set_multicast_list() call. + +Input + multicast_num_addrs + multicast_ladrf[] +---------------------------------------------------------------------------- */ +static void restore_multicast_list(struct net_device *dev) +{ + mace_private *lp = (mace_private *)dev->priv; + int num_addrs = lp->multicast_num_addrs; + int *ladrf = lp->multicast_ladrf; + ioaddr_t ioaddr = dev->base_addr; + int i; + + DEBUG(2, "%s: restoring Rx mode to %d addresses.\n", + dev->name, num_addrs); + + if (num_addrs > 0) { + + DEBUG(1, "Attempt to restore multicast list detected.\n"); + + mace_write(ioaddr, MACE_IAC, MACE_IAC_ADDRCHG | MACE_IAC_LOGADDR); + /* Poll ADDRCHG bit */ + while (mace_read(ioaddr, MACE_IAC) & MACE_IAC_ADDRCHG) + ; + /* Set LADRF register */ + for (i = 0; i < MACE_LADRF_LEN; i++) + mace_write(ioaddr, MACE_LADRF, ladrf[i]); + + mace_write(ioaddr, MACE_UTR, MACE_UTR_RCVFCSE | MACE_UTR_LOOP_EXTERNAL); + mace_write(ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); + + } else if (num_addrs < 0) { + + /* Promiscuous mode: receive all packets */ + mace_write(ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL); + mace_write(ioaddr, MACE_MACCC, + MACE_MACCC_PROM | MACE_MACCC_ENXMT | MACE_MACCC_ENRCV + ); + + } else { + + /* Normal mode */ + mace_write(ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL); + mace_write(ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); + + } +} /* restore_multicast_list */ + +/* ---------------------------------------------------------------------------- +set_multicast_list + Set or clear the multicast filter for this adaptor. + +Input + num_addrs == -1 Promiscuous mode, receive all packets + num_addrs == 0 Normal mode, clear multicast list + num_addrs > 0 Multicast mode, receive normal and MC packets, and do + best-effort filtering. +Output + multicast_num_addrs + multicast_ladrf[] +---------------------------------------------------------------------------- */ + +static void set_multicast_list(struct net_device *dev) +{ + mace_private *lp = (mace_private *)dev->priv; + int adr[ETHER_ADDR_LEN] = {0}; /* Ethernet address */ + int i; + struct dev_mc_list *dmi = dev->mc_list; + +#ifdef PCMCIA_DEBUG + if (pc_debug > 1) { + static int old = 0; + if (dev->mc_count != old) { + old = dev->mc_count; + DEBUG(0, "%s: setting Rx mode to %d addresses.\n", + dev->name, old); + } + } +#endif + + /* Set multicast_num_addrs. */ + lp->multicast_num_addrs = dev->mc_count; + + /* Set multicast_ladrf. */ + if (num_addrs > 0) { + /* Calculate multicast logical address filter */ + memset(lp->multicast_ladrf, 0, MACE_LADRF_LEN); + for (i = 0; i < dev->mc_count; i++) { + memcpy(adr, dmi->dmi_addr, ETHER_ADDR_LEN); + dmi = dmi->next; + BuildLAF(lp->multicast_ladrf, adr); + } + } + + restore_multicast_list(dev); + +} /* set_multicast_list */ + +#endif /* BROKEN_MULTICAST */ + +static void restore_multicast_list(struct net_device *dev) +{ + ioaddr_t ioaddr = dev->base_addr; + + DEBUG(2, "%s: restoring Rx mode to %d addresses.\n", dev->name, + ((mace_private *)(dev->priv))->multicast_num_addrs); + + if (dev->flags & IFF_PROMISC) { + /* Promiscuous mode: receive all packets */ + mace_write(ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL); + mace_write(ioaddr, MACE_MACCC, + MACE_MACCC_PROM | MACE_MACCC_ENXMT | MACE_MACCC_ENRCV + ); + } else { + /* Normal mode */ + mace_write(ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL); + mace_write(ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); + } +} /* restore_multicast_list */ + +static void set_multicast_list(struct net_device *dev) +{ + mace_private *lp = (mace_private *)dev->priv; + +#ifdef PCMCIA_DEBUG + if (pc_debug > 1) { + static int old = 0; + if (dev->mc_count != old) { + old = dev->mc_count; + DEBUG(0, "%s: setting Rx mode to %d addresses.\n", + dev->name, old); + } + } +#endif + + lp->multicast_num_addrs = dev->mc_count; + restore_multicast_list(dev); + +} /* set_multicast_list */ + +/* ---------------------------------------------------------------------------- +init_nmclan_cs +---------------------------------------------------------------------------- */ + +static int __init init_nmclan_cs(void) +{ + servinfo_t serv; + DEBUG(0, "%s\n", version); + CardServices(GetCardServicesInfo, &serv); + if (serv.Revision != CS_RELEASE_CODE) { + printk(KERN_NOTICE "nmclan_cs: Card Services release does not match!\n"); + return -1; + } + register_pccard_driver(&dev_info, &nmclan_attach, &nmclan_detach); + return 0; +} + +/* ---------------------------------------------------------------------------- +exit_nmclan_cs +---------------------------------------------------------------------------- */ + +static void __exit exit_nmclan_cs(void) +{ + DEBUG(0, "nmclan_cs: unloading\n"); + unregister_pccard_driver(&dev_info); + while (dev_list != NULL) + nmclan_detach(dev_list); +} + +module_init(init_nmclan_cs); +module_exit(exit_nmclan_cs); diff --git a/drivers/net/pcmcia/ositech.h b/drivers/net/pcmcia/ositech.h new file mode 100644 index 000000000..749bbaf21 --- /dev/null +++ b/drivers/net/pcmcia/ositech.h @@ -0,0 +1,358 @@ +/* + This file contains the firmware of Seven of Diamonds from OSITECH. + (Special thanks to Kevin MacPherson of OSITECH) + + This software may be used and distributed according to the terms of + the GNU Public License, incorporated herein by reference. +*/ + + static const u_char __Xilinx7OD[] = { + 0xFF, 0x04, 0xA0, 0x36, 0xF3, 0xEC, 0xFF, 0xFF, 0xFF, 0xDF, 0xFB, 0xFF, + 0xF3, 0xFF, 0xFF, 0xFF, + 0xEF, 0x3F, 0xFF, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0x7F, 0xFE, 0xFF, + 0xCE, 0xFE, 0xFE, 0xFE, + 0xFE, 0xDE, 0xBD, 0xDD, 0xFD, 0xFF, 0xFD, 0xCF, 0xF7, 0xBF, 0x7F, 0xFF, + 0x7F, 0x3F, 0xFE, 0xBF, + 0xFF, 0xFF, 0xFF, 0xBC, 0xFF, 0xFF, 0xBD, 0xB5, 0x7F, 0x7F, 0xBF, 0xBF, + 0x7F, 0xFF, 0xEF, 0xFF, + 0xFF, 0xFF, 0xFB, 0xFF, 0xF7, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xDE, + 0xFE, 0xFE, 0xFA, 0xDE, + 0xBD, 0xFD, 0xED, 0xFD, 0xFD, 0xCF, 0xEF, 0xEF, 0xEF, 0xEF, 0xC7, 0xDF, + 0xDF, 0xDF, 0xDF, 0xDF, + 0xFF, 0x7E, 0xFE, 0xFD, 0x7D, 0x6D, 0xEE, 0xFE, 0x7C, 0xFB, 0xF4, 0xFB, + 0xCF, 0xDB, 0xDF, 0xFF, + 0xFF, 0xBB, 0x7F, 0xFF, 0x7F, 0xFF, 0xF7, 0xFF, 0x9E, 0xBF, 0x3B, 0xBF, + 0xBF, 0x7F, 0x7F, 0x7F, + 0x7E, 0x6F, 0xDF, 0xEF, 0xF5, 0xF6, 0xFD, 0xF6, 0xF5, 0xED, 0xEB, 0xFF, + 0xEF, 0xEF, 0xEF, 0x7E, + 0x7F, 0x7F, 0x6F, 0x7F, 0xFF, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xEF, 0xBF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0x1F, 0x1F, 0xEE, 0xFF, 0xBC, + 0xB7, 0xFF, 0xDF, 0xFF, + 0xDF, 0xEF, 0x3B, 0xE3, 0xD3, 0xFF, 0xFB, 0xFF, 0xFF, 0xDF, 0xFF, 0xFF, + 0xFF, 0xBA, 0xBF, 0x2D, + 0xDB, 0xBD, 0xFD, 0xDB, 0xDF, 0xFA, 0xFB, 0xFF, 0xEF, 0xFB, 0xDB, 0xF3, + 0xFF, 0xDF, 0xFD, 0x7F, + 0xEF, 0xFB, 0xFF, 0xFF, 0xBE, 0xBF, 0x27, 0xBA, 0xFE, 0xFB, 0xDF, 0xFF, + 0xF6, 0xFF, 0xFF, 0xEF, + 0xFB, 0xDB, 0xF3, 0xD9, 0x9A, 0x3F, 0xFF, 0xAF, 0xBF, 0xFF, 0xFF, 0xBE, + 0x3F, 0x37, 0xBD, 0x96, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xAE, 0xFB, 0xF3, 0xF3, 0xEB, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xF7, 0xFA, 0xBC, 0xAE, 0xFE, 0xBE, 0xFE, 0xBB, 0x7F, 0xFD, 0xFF, + 0x7F, 0xEF, 0xF7, 0xFB, + 0xBB, 0xD7, 0xF7, 0x7F, 0xFF, 0xF7, 0xFF, 0xFF, 0xF7, 0xBC, 0xED, 0xFD, + 0xBD, 0x9D, 0x7D, 0x7B, + 0xFB, 0x7B, 0x7B, 0xFB, 0xAF, 0xFF, 0xFE, 0xFD, 0xFD, 0xFE, 0xFE, 0xFF, + 0xFF, 0xFF, 0xFF, 0xF7, + 0xAA, 0xB9, 0xBF, 0x8F, 0xBF, 0xDF, 0xFF, 0x7F, 0xFF, 0xFF, 0x7F, 0xCF, + 0xFB, 0xEB, 0xCB, 0xEB, + 0xEE, 0xFF, 0xFF, 0xD7, 0xFF, 0xFF, 0xFF, 0x3E, 0x33, 0x3F, 0x1C, 0x7C, + 0xFC, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xCF, 0xD3, 0xF3, 0xE3, 0xF3, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xEB, 0xFE, 0x35, + 0x3F, 0x3D, 0xFD, 0xFD, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF, 0xEF, 0x6F, 0xE3, + 0xE3, 0xE3, 0xEF, 0xFF, + 0xFF, 0xDF, 0xFF, 0xFF, 0xF7, 0xFE, 0x3E, 0x5E, 0xFE, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFD, 0xFF, 0xFF, + 0xAF, 0xCF, 0xF2, 0xCB, 0xCF, 0x8E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, + 0xFC, 0x3E, 0x1F, 0x9E, + 0xAD, 0xFD, 0xFF, 0xFF, 0xBF, 0xFF, 0xFF, 0xEF, 0xFF, 0xB3, 0xF7, 0xE7, + 0xF7, 0xFA, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xEE, 0xEB, 0xAB, 0xAF, 0x9F, 0xE3, 0x7F, 0xFF, 0xDE, + 0xFF, 0x7F, 0xEE, 0xFF, + 0xFF, 0xFB, 0x3A, 0xFA, 0xFF, 0xF2, 0x77, 0xFF, 0xFF, 0xF7, 0xFE, 0xFF, + 0xFE, 0xBD, 0xAE, 0xDE, + 0x7D, 0x7D, 0xFD, 0xFF, 0xBF, 0xEE, 0xFF, 0xFD, 0xFF, 0xDB, 0xFB, 0xFF, + 0xF7, 0xEF, 0xFB, 0xFF, + 0xFF, 0xFE, 0xFF, 0x2D, 0xAF, 0xB9, 0xFD, 0x79, 0xFB, 0xFA, 0xFF, 0xBF, + 0xEF, 0xFF, 0xFF, 0x91, + 0xFA, 0xFB, 0xDF, 0xF7, 0xF7, 0xFF, 0xFF, 0xFF, 0xFC, 0xCF, 0x37, 0xBF, + 0xBF, 0xFF, 0x7F, 0x7F, + 0xFF, 0xFF, 0xFF, 0xAF, 0xFF, 0xFF, 0xF3, 0xFB, 0xFB, 0xFF, 0xF5, 0xEF, + 0xFF, 0xFF, 0xF7, 0xFA, + 0xFF, 0xFF, 0xEE, 0xFA, 0xFE, 0xFB, 0x55, 0xDD, 0xFF, 0x7F, 0xAF, 0xFE, + 0xFF, 0xFB, 0xFB, 0xF5, + 0xFF, 0xF7, 0xEF, 0xFF, 0xFF, 0xFF, 0xBE, 0xBD, 0xBD, 0xBD, 0xBD, 0x7D, + 0x7B, 0x7B, 0x7B, 0x7B, + 0xFB, 0xAE, 0xFF, 0xFD, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xF7, 0xDA, 0xB7, 0x61, + 0xFF, 0xB9, 0x59, 0xF3, 0x73, 0xF3, 0xDF, 0x7F, 0x6F, 0xDF, 0xEF, 0xF7, + 0xEB, 0xEB, 0xD7, 0xFF, + 0xD7, 0xFF, 0xFF, 0xF7, 0xFE, 0x7F, 0xFB, 0x3E, 0x38, 0x73, 0xF6, 0x7F, + 0xFC, 0xFF, 0xFF, 0xCF, + 0xFF, 0xB7, 0xFB, 0xB3, 0xB3, 0x67, 0xFF, 0xE7, 0xFD, 0xFF, 0xEF, 0xF6, + 0x7F, 0xB7, 0xBC, 0xF5, + 0x7B, 0xF6, 0xF7, 0xF5, 0xFF, 0xFF, 0xEF, 0xFF, 0xF7, 0xFF, 0xF7, 0xCE, + 0xE7, 0xFF, 0x9F, 0xFF, + 0xFF, 0xF5, 0xFE, 0x7D, 0xFF, 0x5F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xEF, 0xFF, 0xF6, + 0xCB, 0xDB, 0xEE, 0xFE, 0xFF, 0xDF, 0xFF, 0xFF, 0xFF, 0xFE, 0x7F, 0xBE, + 0x1E, 0x3E, 0xFE, 0xFF, + 0x7D, 0xFE, 0xFF, 0xFF, 0xEF, 0xBF, 0xE7, 0xFF, 0xE3, 0xE3, 0xFF, 0xDF, + 0xE7, 0xFF, 0xFF, 0xFF, + 0xB8, 0xEF, 0xB7, 0x2F, 0xEE, 0xFF, 0xDF, 0xFF, 0xBF, 0xFF, 0x7F, 0xEF, + 0xEB, 0xBF, 0xA3, 0xD3, + 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xBE, 0xFD, 0x3F, 0xCF, 0xFD, + 0xFB, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xAF, 0xFB, 0xBF, 0xBB, 0xBF, 0xDB, 0xFD, 0xFB, 0xFF, 0xFF, + 0xFF, 0xFF, 0x3E, 0xFE, + 0x3F, 0xBA, 0xBA, 0xFE, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xEF, 0xC3, 0x7F, + 0xB2, 0x9B, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0x3C, 0xFF, 0x3F, 0x3C, 0xFF, 0xFE, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xAF, 0xF3, 0xFE, 0xF3, 0xE3, 0xEB, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0xF7, + 0x9A, 0xFE, 0xAF, 0x9E, + 0xBE, 0xFE, 0xFF, 0xDF, 0xFF, 0xFF, 0x7B, 0xEF, 0xF7, 0xBF, 0xFB, 0xFB, + 0xFB, 0xFF, 0xFF, 0x7F, + 0xFF, 0xFF, 0xFF, 0xBC, 0xBD, 0xFD, 0xBD, 0xDD, 0x7D, 0x7B, 0x7B, 0x7B, + 0x7B, 0xFB, 0xAE, 0xFF, + 0xFF, 0xFF, 0xFE, 0xFE, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xF7, 0x9A, 0xFF, + 0x9F, 0xFF, 0xAF, 0xEF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xCF, 0xF3, 0xFF, 0xEB, 0xFF, 0xEB, 0xFF, + 0xFF, 0xBF, 0xFF, 0xFF, + 0xEF, 0xFE, 0xFF, 0x37, 0xFC, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xCF, 0xEF, 0xFD, 0xF3, + 0xFF, 0xEE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x6E, 0xFD, 0x2F, 0xFD, + 0xFF, 0xFD, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xEF, 0xCF, 0xFF, 0xF3, 0xBF, 0x69, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFE, + 0xFB, 0x9F, 0xFF, 0xBF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0x87, + 0xFE, 0xDA, 0xEF, 0xCF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xEF, 0xBF, 0xEF, 0xEF, 0xFD, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xEF, 0xFD, 0xFF, 0x7B, 0xFF, 0xEB, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, + 0xEB, 0xF8, 0xFF, 0xEF, + 0xAF, 0xFF, 0xFF, 0xBD, 0xFF, 0xFF, 0xFF, 0x7F, 0xEE, 0x7F, 0xEF, 0xFF, + 0xBB, 0xFF, 0xBF, 0xFB, + 0xFF, 0xFF, 0xFF, 0xF7, 0xF6, 0xFB, 0xBD, 0xFD, 0xDD, 0xF5, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xAF, + 0xFF, 0x5F, 0xF5, 0xDF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF6, + 0xF3, 0xFF, 0xDE, 0xFE, + 0xEF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xDE, 0xDF, 0x5F, 0xDF, + 0xFD, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFE, 0xFE, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, + 0xFF, 0xAF, 0xFF, 0xFF, + 0xEF, 0xED, 0xFF, 0xDF, 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, 0xDA, 0xBD, 0xBE, + 0xAE, 0xFE, 0x7F, 0xFD, + 0xDF, 0xFF, 0xFF, 0x7F, 0xEF, 0xFF, 0xFB, 0xFB, 0xFB, 0x7F, 0xF7, 0xFF, + 0xFF, 0xFF, 0xFF, 0xF7, + 0xBC, 0xFD, 0xBD, 0xBD, 0xBD, 0xFD, 0x7B, 0x7B, 0x7B, 0x7B, 0xFB, 0xAE, + 0xFF, 0xFF, 0xFD, 0xFF, + 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFA, 0x9F, 0xBF, 0xBF, 0xCF, + 0x7F, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xAF, 0xFF, 0xEB, 0xEB, 0xEB, 0xFF, 0xD7, 0xFE, 0xFF, 0xFF, + 0xBF, 0xE7, 0xFE, 0xBF, + 0x7F, 0xFC, 0xFF, 0xFF, 0xED, 0xFF, 0xFF, 0xFF, 0xFF, 0x4F, 0xFF, 0xFB, + 0xFB, 0xFF, 0xFF, 0xDD, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBD, 0xDF, 0x9D, 0xFD, 0xDF, 0xB9, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xEF, 0xFF, 0xFB, 0xEF, 0xEB, 0xFF, 0xDE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xF6, 0x9F, 0xFF, 0xFC, + 0xFE, 0xFB, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xDF, 0xFA, 0xCD, 0xCF, + 0xBF, 0x9F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xF7, 0xFE, 0xBF, 0xFF, 0xDF, 0xEF, 0x5F, 0xFF, 0xFF, 0xFF, + 0xFF, 0x7F, 0x6F, 0xFF, + 0xBB, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0xFF, + 0x5F, 0xFF, 0xBF, 0xBF, + 0xF9, 0xFF, 0xFF, 0xFF, 0x7F, 0x6E, 0x7B, 0xFF, 0xEF, 0xFD, 0xEB, 0xDF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xF7, 0xB6, 0x3E, 0xFC, 0xFD, 0xBF, 0x7E, 0xFB, 0xFF, 0xFF, 0xFF, 0xF7, + 0xEF, 0xF7, 0xF3, 0xF7, + 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x6E, 0x35, 0x79, 0xFF, + 0xBF, 0xFC, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xEF, 0xFB, 0x53, 0xDF, 0xFF, 0xEB, 0xBF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xBC, + 0xFF, 0xFF, 0xFF, 0xBF, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xAF, 0xF5, + 0xFF, 0xF7, 0xFF, 0xFB, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBA, 0xAA, 0xEE, 0xFE, 0x3F, 0x7D, + 0xFD, 0xFF, 0xFF, 0xFF, + 0x7F, 0xAF, 0x77, 0xFB, 0xFB, 0xFF, 0xFB, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, + 0xF7, 0xBE, 0xBD, 0xBD, + 0xBD, 0xBD, 0xFD, 0x7B, 0x7B, 0x7B, 0x7B, 0xFB, 0xAE, 0xFF, 0xEF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFC, + 0xFF, 0xFF, 0xFF, 0xFF, 0x9A, 0xD9, 0xB8, 0xFF, 0xFF, 0x79, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xCF, + 0xFB, 0xFF, 0xEB, 0xFF, 0xEB, 0xD7, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xDE, + 0xF8, 0xFB, 0xFE, 0x3F, + 0xFB, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xCF, 0xAD, 0xBF, 0xFA, 0xFF, 0x73, + 0xDF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x3A, 0xF5, 0xB7, 0xFC, 0x3F, 0xF9, 0xFD, 0xFF, 0xFF, 0xFF, + 0x7F, 0xEF, 0xF3, 0xFF, + 0xBF, 0xFE, 0xF3, 0x9F, 0xFE, 0xFF, 0xFF, 0xFF, 0xF7, 0x3E, 0xFF, 0xFF, + 0xFF, 0xBF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xAF, 0xD3, 0xFE, 0xDB, 0xFF, 0xDB, 0xDF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0x3E, 0xFF, 0xBF, 0xFF, 0x7F, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0x8F, + 0xF3, 0xFF, 0xED, 0xFF, + 0xF7, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xF6, 0x3C, 0xFE, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x9F, 0xEF, 0xEF, 0xD1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x7E, 0xBF, + 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBB, 0xEF, 0xDF, 0xF1, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEE, 0x3E, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xBF, + 0xEF, 0xFD, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF, + 0xFC, 0x3E, 0xFE, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2E, 0xEF, 0xF3, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xF7, 0xBA, 0xBE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x7F, 0xAF, 0xFB, + 0xFB, 0xFD, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xF2, 0xD6, 0xED, + 0xBD, 0xBD, 0xBD, 0x7D, + 0x7B, 0x7B, 0x7B, 0x7B, 0xFB, 0xAF, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x92, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, + 0xAF, 0xEB, 0xEB, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xFE, 0x2E, 0xFE, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x4F, 0xEF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFE, + 0x3C, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xCE, + 0xC3, 0xFD, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x5D, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xEF, 0xCF, 0xEB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xF7, 0xEE, 0x3E, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xEF, 0xDF, 0xE2, 0xFF, + 0xFF, 0xFF, 0xFB, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xF6, 0xBE, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x7F, 0xEE, + 0x5F, 0xE6, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3E, + 0x7D, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xF3, 0xFB, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xBF, 0xF7, 0x36, 0xBE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xEF, 0xD3, 0xF6, + 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x7F, 0xEE, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xAF, 0xEF, 0xEB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xBA, 0xBE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEE, + 0xFB, 0xFA, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xD6, 0xFD, 0xBD, 0xBD, 0xBD, + 0x7D, 0x7B, 0x7B, 0x7B, + 0x7B, 0xFB, 0xAE, 0xFF, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xF7, 0xBA, 0xBF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xEF, 0xEB, 0x6B, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xFE, 0xBE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0x4F, 0xEF, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, + 0x3E, 0x6E, 0xFC, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xC3, 0xC9, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x3E, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xEF, 0xFB, + 0xD5, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFE, + 0xFE, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x6F, 0xEF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFB, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xF6, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFE, + 0xEF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xFF, 0xFE, 0xFF, 0xF7, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x7F, 0xFA, 0xEF, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xE7, 0xFF, 0xFE, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFE, 0xEF, 0xBF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xA7, 0xFF, 0xFC, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x7F, + 0xFE, 0xAE, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, + 0xF7, 0xFA, 0xFF, 0xFD, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xAF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xF7, 0xBE, 0xBD, 0xBD, 0xBD, 0xBD, 0x7D, 0x7B, 0x7B, + 0x7B, 0x7B, 0xFB, 0xAF, + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCA, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x6F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xE7, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xCF, 0xFE, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xDF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xEF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, + 0xFF, 0xE7, 0xF2, 0xFC, + 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xAE, 0xEF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0x7E, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xEF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF, + 0xFE, 0xFE, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xDF, 0xEF, 0xDD, 0xFE, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xAF, 0xEF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBA, 0xFE, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFA, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xF6, 0x9C, 0xBD, 0xBD, 0xBD, 0xBD, 0x7D, 0x7B, 0x7B, 0x7B, 0x7B, 0xFB, + 0xAE, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0x7A, 0xFF, 0xFF, 0xFF, + 0xFF, 0xDF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x6F, 0xEF, 0xF7, 0xFF, 0xFF, 0xFF, 0xDF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xF7, 0xFE, + 0xFE, 0xFF, 0xFF, 0xFF, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCF, 0xEB, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0x9E, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xEF, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFE, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xEF, 0xCB, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFD, + 0xFF, 0xFF, 0xFF, 0xFF, 0xBE, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xEF, + 0xEF, 0xFF, 0xFF, 0xFF, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFB, 0xAF, 0x7F, 0xFF, + 0xFF, 0xFF, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xEF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xBF, 0xFF, + 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xAE, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xFA, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x7F, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xF7, 0xBC, 0xBD, + 0xBD, 0xBD, 0xBD, 0x7D, 0x7B, 0x7B, 0x7B, 0x7B, 0xFB, 0xAF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xFA, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x7F, + 0xAF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, + 0xFE, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, + 0xFF, 0xFF, 0xEF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xBF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xEF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFE, 0xFF, 0x9F, 0x9F, + 0x9F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0xFF, 0xEF, 0xDF, 0xDF, 0xDF, 0xDF, 0xCF, 0xB7, 0xBF, 0xBF, + 0xBF, 0xBF, 0xFF, 0xBC, + 0xB9, 0x9D, 0xBD, 0xBD, 0x7D, 0x7B, 0x7B, 0x7B, 0x7B, 0xFB, 0xEF, 0xD7, + 0xF5, 0xF3, 0xF1, 0xD1, + 0x65, 0xE3, 0xE3, 0xE3, 0xA3, 0xFF, 0xFE, 0x7F, 0xFE, 0xDE, 0xDE, 0xFF, + 0xBD, 0xBD, 0xBD, 0xBD, + 0xDF, 0xEF, 0xFB, 0xF7, 0xF3, 0xF3, 0xF3, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, + 0xFB, 0xFE, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + + }; diff --git a/drivers/net/pcmcia/pcnet_cs.c b/drivers/net/pcmcia/pcnet_cs.c index f5d4b3c49..26fa47ee9 100644 --- a/drivers/net/pcmcia/pcnet_cs.c +++ b/drivers/net/pcmcia/pcnet_cs.c @@ -11,7 +11,7 @@ Copyright (C) 1999 David A. Hinds -- dhinds@hyper.stanford.edu - pcnet_cs.c 1.99 1999/09/15 15:33:09 + pcnet_cs.c 1.101 1999/10/21 00:56:19 The network driver code is based on Donald Becker's NE2000 code: @@ -72,7 +72,7 @@ static int pc_debug = PCMCIA_DEBUG; MODULE_PARM(pc_debug, "i"); #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) static char *version = -"pcnet_cs.c 1.99 1999/09/15 15:33:09 (David Hinds)"; +"pcnet_cs.c 1.101 1999/10/21 00:56:19 (David Hinds)"; #else #define DEBUG(n, args...) #endif @@ -477,7 +477,8 @@ static hw_info_t *get_prom(dev_link_t *link) { struct net_device *dev = link->priv; unsigned char prom[32]; - int i, j, ioaddr; + ioaddr_t ioaddr; + int i, j; /* This is lifted straight from drivers/net/ne.c */ struct { @@ -851,7 +852,7 @@ static int pcnet_event(event_t event, int priority, static void set_misc_reg(struct net_device *dev) { - int nic_base = dev->base_addr; + ioaddr_t nic_base = dev->base_addr; pcnet_dev_t *info = (pcnet_dev_t *)dev; u_char tmp; @@ -889,7 +890,7 @@ static int pcnet_open(struct net_device *dev) int i; for (i = 0; i < 20; i++) { if ((inb(dev->base_addr+0x1c) & 0x01) == 0) break; - current->state = TASK_INTERRUPTIBLE; + __set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(HZ/10); } } @@ -941,7 +942,7 @@ static int pcnet_close(struct net_device *dev) static void pcnet_reset_8390(struct net_device *dev) { - int nic_base = dev->base_addr; + ioaddr_t nic_base = dev->base_addr; int i; ei_status.txing = ei_status.dmaing = 0; @@ -996,7 +997,7 @@ static void ei_watchdog(u_long arg) { pcnet_dev_t *info = (pcnet_dev_t *)(arg); struct net_device *dev = &info->dev; - int nic_base = dev->base_addr; + ioaddr_t nic_base = dev->base_addr; if (dev->start == 0) goto reschedule; @@ -1027,7 +1028,7 @@ static void dma_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page) { - int nic_base = dev->base_addr; + ioaddr_t nic_base = dev->base_addr; if (ei_status.dmaing) { printk(KERN_NOTICE "%s: DMAing conflict in dma_block_input." @@ -1059,7 +1060,7 @@ static void dma_get_8390_hdr(struct net_device *dev, static void dma_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset) { - int nic_base = dev->base_addr; + ioaddr_t nic_base = dev->base_addr; int xfer_count = count; char *buf = skb->data; @@ -1116,7 +1117,7 @@ static void dma_block_output(struct net_device *dev, int count, const unsigned char *buf, const int start_page) { - int nic_base = dev->base_addr; + ioaddr_t nic_base = dev->base_addr; pcnet_dev_t *info = (pcnet_dev_t *)dev; #ifdef PCMCIA_DEBUG int retries = 0; diff --git a/drivers/net/pcmcia/smc91c92_cs.c b/drivers/net/pcmcia/smc91c92_cs.c new file mode 100644 index 000000000..3abdf7d9d --- /dev/null +++ b/drivers/net/pcmcia/smc91c92_cs.c @@ -0,0 +1,2013 @@ +/*====================================================================== + + A PCMCIA ethernet driver for SMC91c92-based cards. + + This driver supports Megahertz PCMCIA ethernet cards; and + Megahertz, Motorola, Ositech, and Psion Dacom ethernet/modem + multifunction cards. + + Copyright (C) 1999 David A. Hinds -- dhinds@hyper.stanford.edu + + smc91c92_cs.c 1.79 1999/10/19 00:38:29 + + This driver contains code written by Donald Becker + (becker@cesdis.gsfc.nasa.gov), Rowan Hughes (x-csrdh@jcu.edu.au), + David Hinds (dhinds@hyper.stanford.edu), and Erik Stahlman + (erik@vt.edu). Donald wrote the SMC 91c92 code using parts of + Erik's SMC 91c94 driver. Rowan wrote a similar driver, and I've + incorporated some parts of his driver here. I (Dave) wrote most + of the PCMCIA glue code, and the Ositech support code. Kelly + Stephens (kstephen@holli.com) added support for the Motorola + Mariner, with help from Allen Brost. + + This software may be used and distributed according to the terms of + the GNU Public License, incorporated herein by reference. + +======================================================================*/ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/malloc.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <asm/io.h> +#include <asm/system.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/if_arp.h> +#include <linux/ioport.h> + +#include <pcmcia/version.h> +#include <pcmcia/cs_types.h> +#include <pcmcia/cs.h> +#include <pcmcia/cistpl.h> +#include <pcmcia/cisreg.h> +#include <pcmcia/ciscode.h> +#include <pcmcia/ds.h> + +/* Ositech Seven of Diamonds firmware */ +#include "ositech.h" + +/*====================================================================*/ + +#ifdef PCMCIA_DEBUG +static int pc_debug = PCMCIA_DEBUG; +MODULE_PARM(pc_debug, "i"); +static const char *version = +"smc91c92_cs.c 0.09 1996/8/4 Donald Becker, becker@cesdis.gsfc.nasa.gov.\n"; +#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) +#else +#define DEBUG(n, args...) +#endif + +static char *if_names[] = { "auto", "10baseT", "10base2"}; + +/* Parameters that can be set with 'insmod' */ + +/* + Transceiver/media type. + 0 = auto + 1 = 10baseT (and autoselect if #define AUTOSELECT), + 2 = AUI/10base2, +*/ +static int if_port = 0; + +/* Bit map of interrupts to choose from. */ +static u_int irq_mask = 0xdeb8; +static int irq_list[4] = { -1 }; + +MODULE_PARM(if_port, "i"); +MODULE_PARM(irq_mask, "i"); +MODULE_PARM(irq_list, "1-4i"); + +/* Operational parameter that usually are not changed. */ + +/* Do you want to use 32 bit xfers? This should work on all chips, + but could cause trouble with some PCMCIA controllers... */ +#define USE_32_BIT 1 + +/* Time in jiffies before concluding Tx hung */ +#define TX_TIMEOUT ((400*HZ)/1000) + +/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ +#define INTR_WORK 4 + +/* Times to check the check the chip before concluding that it doesn't + currently have room for another Tx packet. */ +#define MEMORY_WAIT_TIME 8 + +/* Values that should be specific lengths */ +typedef unsigned short uint16; + +static dev_info_t dev_info = "smc91c92_cs"; + +static dev_link_t *dev_list = NULL; + +struct smc_private { + u_short manfid; + u_short cardid; + struct net_device_stats stats; + dev_node_t node; + struct sk_buff *saved_skb; + int packets_waiting; + caddr_t base; + u_short cfg; + struct timer_list media; + int watchdog, tx_err; + u_short media_status; + u_short fast_poll; + u_long last_rx; +}; + +/* Special definitions for Megahertz multifunction cards */ +#define MEGAHERTZ_ISR 0x0380 + +/* Special function registers for Motorola Mariner */ +#define MOT_LAN 0x0000 +#define MOT_UART 0x0020 +#define MOT_EEPROM 0x20 + +#define MOT_NORMAL \ +(COR_LEVEL_REQ | COR_FUNC_ENA | COR_ADDR_DECODE | COR_IREQ_ENA) + +/* Special function registers for Ositech cards */ +#define OSITECH_AUI_CTL 0x0c +#define OSITECH_PWRDOWN 0x0d +#define OSITECH_RESET 0x0e +#define OSITECH_ISR 0x0f +#define OSITECH_AUI_PWR 0x0c +#define OSITECH_RESET_ISR 0x0e + +#define OSI_AUI_PWR 0x40 +#define OSI_LAN_PWRDOWN 0x02 +#define OSI_MODEM_PWRDOWN 0x01 +#define OSI_LAN_RESET 0x02 +#define OSI_MODEM_RESET 0x01 + +/* Symbolic constants for the SMC91c9* series chips, from Erik Stahlman. */ +#define BANK_SELECT 14 /* Window select register. */ +#define SMC_SELECT_BANK(x) { outw( x, ioaddr + BANK_SELECT); } + +/* Bank 0 registers. */ +#define TCR 0 /* transmit control register */ +#define TCR_CLEAR 0 /* do NOTHING */ +#define TCR_ENABLE 0x0001 /* if this is 1, we can transmit */ +#define TCR_PAD_EN 0x0080 /* pads short packets to 64 bytes */ +#define TCR_MONCSN 0x0400 /* Monitor Carrier. */ +#define TCR_FDUPLX 0x0800 /* Full duplex mode. */ +#define TCR_NORMAL TCR_ENABLE | TCR_PAD_EN + +#define EPH 2 /* Ethernet Protocol Handler report. */ +#define EPH_TX_SUC 0x0001 +#define EPH_SNGLCOL 0x0002 +#define EPH_MULCOL 0x0004 +#define EPH_LTX_MULT 0x0008 +#define EPH_16COL 0x0010 +#define EPH_SQET 0x0020 +#define EPH_LTX_BRD 0x0040 +#define EPH_TX_DEFR 0x0080 +#define EPH_LAT_COL 0x0200 +#define EPH_LOST_CAR 0x0400 +#define EPH_EXC_DEF 0x0800 +#define EPH_CTR_ROL 0x1000 +#define EPH_RX_OVRN 0x2000 +#define EPH_LINK_OK 0x4000 +#define EPH_TX_UNRN 0x8000 +#define MEMINFO 8 /* Memory Information Register */ +#define MEMCFG 10 /* Memory Configuration Register */ + +/* Bank 1 registers. */ +#define CONFIG 0 +#define CFG_MII_SELECT 0x8000 /* 91C100 only */ +#define CFG_NO_WAIT 0x1000 +#define CFG_FULL_STEP 0x0400 +#define CFG_SET_SQLCH 0x0200 +#define CFG_AUI_SELECT 0x0100 +#define CFG_16BIT 0x0080 +#define CFG_DIS_LINK 0x0040 +#define CFG_STATIC 0x0030 +#define CFG_IRQ_SEL_1 0x0004 +#define CFG_IRQ_SEL_0 0x0002 +#define BASE_ADDR 2 +#define ADDR0 4 +#define GENERAL 10 +#define CONTROL 12 +#define CTL_STORE 0x0001 +#define CTL_RELOAD 0x0002 +#define CTL_EE_SELECT 0x0004 +#define CTL_TE_ENABLE 0x0020 +#define CTL_CR_ENABLE 0x0040 +#define CTL_LE_ENABLE 0x0080 +#define CTL_AUTO_RELEASE 0x0800 +#define CTL_POWERDOWN 0x2000 + +/* Bank 2 registers. */ +#define MMU_CMD 0 +#define MC_ALLOC 0x20 /* or with number of 256 byte packets */ +#define MC_RESET 0x40 +#define MC_RELEASE 0x80 /* remove and release the current rx packet */ +#define MC_FREEPKT 0xA0 /* Release packet in PNR register */ +#define MC_ENQUEUE 0xC0 /* Enqueue the packet for transmit */ +#define PNR_ARR 2 +#define FIFO_PORTS 4 +#define FP_RXEMPTY 0x8000 +#define POINTER 6 +#define PTR_AUTO_INC 0x0040 +#define PTR_READ 0x2000 +#define PTR_AUTOINC 0x4000 +#define PTR_RCV 0x8000 +#define DATA_1 8 +#define INTERRUPT 12 +#define IM_RCV_INT 0x1 +#define IM_TX_INT 0x2 +#define IM_TX_EMPTY_INT 0x4 +#define IM_ALLOC_INT 0x8 +#define IM_RX_OVRN_INT 0x10 +#define IM_EPH_INT 0x20 + +#define RCR 4 +enum RxCfg { RxAllMulti = 0x0004, RxPromisc = 0x0002, + RxEnable = 0x0100, RxStripCRC = 0x0200}; +#define RCR_SOFTRESET 0x8000 /* resets the chip */ +#define RCR_STRIP_CRC 0x200 /* strips CRC */ +#define RCR_ENABLE 0x100 /* IFF this is set, we can recieve packets */ +#define RCR_ALMUL 0x4 /* receive all multicast packets */ +#define RCR_PROMISC 0x2 /* enable promiscuous mode */ + +/* the normal settings for the RCR register : */ +#define RCR_NORMAL (RCR_STRIP_CRC | RCR_ENABLE) +#define RCR_CLEAR 0x0 /* set it to a base state */ +#define COUNTER 6 + +/* BANK 3 -- not the same values as in smc9194! */ +#define MULTICAST0 0 +#define MULTICAST2 2 +#define MULTICAST4 4 +#define MULTICAST6 6 +#define REVISION 0x0a + +/* Transmit status bits. */ +#define TS_SUCCESS 0x0001 +#define TS_16COL 0x0010 +#define TS_LATCOL 0x0200 +#define TS_LOSTCAR 0x0400 + +/* Receive status bits. */ +#define RS_ALGNERR 0x8000 +#define RS_BADCRC 0x2000 +#define RS_ODDFRAME 0x1000 +#define RS_TOOLONG 0x0800 +#define RS_TOOSHORT 0x0400 +#define RS_MULTICAST 0x0001 +#define RS_ERRORS (RS_ALGNERR | RS_BADCRC | RS_TOOLONG | RS_TOOSHORT) + +#define set_bits(v, p) outw(inw(p)|(v), (p)) +#define mask_bits(v, p) outw(inw(p)&(v), (p)) + +/*====================================================================*/ + +static dev_link_t *smc91c92_attach(void); +static void smc91c92_detach(dev_link_t *); +static void smc91c92_config(dev_link_t *link); +static void smc91c92_release(u_long arg); +static int smc91c92_event(event_t event, int priority, + event_callback_args_t *args); + +static int smc91c92_open(struct net_device *dev); +static int smc91c92_close(struct net_device *dev); +static int smc_start_xmit(struct sk_buff *skb, struct net_device *dev); +static void smc_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static void smc_rx(struct net_device *dev); +static struct net_device_stats *smc91c92_get_stats(struct net_device *dev); +static void set_rx_mode(struct net_device *dev); +static int s9k_config(struct net_device *dev, struct ifmap *map); +static void smc_set_xcvr(struct net_device *dev, int if_port); +static void smc_reset(struct net_device *dev); +static void media_check(u_long arg); + +/*====================================================================== + + This bit of code is used to avoid unregistering network devices + at inappropriate times. 2.2 and later kernels are fairly picky + about when this can happen. + +======================================================================*/ + +static void flush_stale_links(void) +{ + dev_link_t *link, *next; + for (link = dev_list; link; link = next) { + next = link->next; + if (link->state & DEV_STALE_LINK) + smc91c92_detach(link); + } +} + +/*====================================================================*/ + +static void cs_error(client_handle_t handle, int func, int ret) +{ + error_info_t err = { func, ret }; + CardServices(ReportError, handle, &err); +} + +/*====================================================================*/ + +static int smc91c92_init(struct net_device *dev) +{ + DEBUG(0, "%s: smc91c92_init called!\n", dev->name); + return 0; +} + +/*====================================================================== + + smc91c92_attach() creates an "instance" of the driver, allocating + local data structures for one device. The device is registered + with Card Services. + +======================================================================*/ + +static dev_link_t *smc91c92_attach(void) +{ + client_reg_t client_reg; + dev_link_t *link; + struct net_device *dev; + int i, ret; + + DEBUG(0, "smc91c92_attach()\n"); + flush_stale_links(); + + /* Create new ethernet device */ + link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL); + memset(link, 0, sizeof(struct dev_link_t)); + link->release.function = &smc91c92_release; + link->release.data = (u_long)link; + link->io.NumPorts1 = 16; + link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; + link->io.IOAddrLines = 4; + link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; + link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID; + if (irq_list[0] == -1) + link->irq.IRQInfo2 = irq_mask; + else + for (i = 0; i < 4; i++) + link->irq.IRQInfo2 |= 1 << irq_list[i]; + link->irq.Handler = &smc_interrupt; + link->conf.Attributes = CONF_ENABLE_IRQ; + link->conf.Vcc = 50; + link->conf.IntType = INT_MEMORY_AND_IO; + + dev = kmalloc(sizeof(struct net_device), GFP_KERNEL); + memset(dev, 0, sizeof(struct net_device)); + + /* Make up a SMC91-specific-data structure. */ + dev->priv = kmalloc(sizeof(struct smc_private), GFP_KERNEL); + memset(dev->priv, 0, sizeof(struct smc_private)); + + /* The SMC91c92-specific entries in the device structure. */ + dev->hard_start_xmit = &smc_start_xmit; + dev->get_stats = &smc91c92_get_stats; + dev->set_config = &s9k_config; + dev->set_multicast_list = &set_rx_mode; + ether_setup(dev); + dev->name = ((struct smc_private *)dev->priv)->node.dev_name; + dev->init = &smc91c92_init; + dev->open = &smc91c92_open; + dev->stop = &smc91c92_close; + dev->tbusy = 1; + link->priv = link->irq.Instance = dev; + + /* Register with Card Services */ + link->next = dev_list; + dev_list = link; + client_reg.dev_info = &dev_info; + client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; + client_reg.EventMask = CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | + CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | + CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; + client_reg.event_handler = &smc91c92_event; + client_reg.Version = 0x0210; + client_reg.event_callback_args.client_data = link; + ret = CardServices(RegisterClient, &link->handle, &client_reg); + if (ret != 0) { + cs_error(link->handle, RegisterClient, ret); + smc91c92_detach(link); + return NULL; + } + + return link; +} /* smc91c92_attach */ + +/*====================================================================== + + This deletes a driver "instance". The device is de-registered + with Card Services. If it has been released, all local data + structures are freed. Otherwise, the structures will be freed + when the device is released. + +======================================================================*/ + +static void smc91c92_detach(dev_link_t *link) +{ + dev_link_t **linkp; + long flags; + + DEBUG(0, "smc91c92_detach(0x%p)\n", link); + + /* Locate device structure */ + for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) + if (*linkp == link) break; + if (*linkp == NULL) + return; + + save_flags(flags); + cli(); + if (link->state & DEV_RELEASE_PENDING) { + del_timer(&link->release); + link->state &= ~DEV_RELEASE_PENDING; + } + restore_flags(flags); + + if (link->state & DEV_CONFIG) { + smc91c92_release((u_long)link); + if (link->state & DEV_STALE_CONFIG) { + link->state |= DEV_STALE_LINK; + return; + } + } + + if (link->handle) + CardServices(DeregisterClient, link->handle); + + /* Unlink device structure, free bits */ + *linkp = link->next; + if (link->priv) { + struct net_device *dev = link->priv; + if (link->dev != NULL) + unregister_netdev(dev); + if (dev->priv) + kfree(dev->priv); + kfree(link->priv); + } + kfree(link); + +} /* smc91c92_detach */ + +/*====================================================================*/ + +static int cvt_ascii_address(struct net_device *dev, char *s) +{ + int i, j, da, c; + + if (strlen(s) != 12) + return -1; + for (i = 0; i < 6; i++) { + da = 0; + for (j = 0; j < 2; j++) { + c = *s++; + da <<= 4; + da += ((c >= '0') && (c <= '9')) ? + (c - '0') : ((c & 0x0f) + 9); + } + dev->dev_addr[i] = da; + } + return 0; +} + +/*====================================================================*/ + +static int get_tuple(int fn, client_handle_t handle, tuple_t *tuple, + cisparse_t *parse) +{ + int i; + i = CardServices(fn, handle, tuple); + if (i != CS_SUCCESS) return i; + i = CardServices(GetTupleData, handle, tuple); + if (i != CS_SUCCESS) return i; + return CardServices(ParseTuple, handle, tuple, parse); +} + +#define first_tuple(a, b, c) get_tuple(GetFirstTuple, a, b, c) +#define next_tuple(a, b, c) get_tuple(GetNextTuple, a, b, c) + +/*====================================================================== + + Configuration stuff for Megahertz cards + + mhz_3288_power() is used to power up a 3288's ethernet chip. + mhz_mfc_config() handles socket setup for multifunction (1144 + and 3288) cards. mhz_setup() gets a card's hardware ethernet + address. + +======================================================================*/ + +static int mhz_3288_power(dev_link_t *link) +{ + struct net_device *dev = link->priv; + struct smc_private *lp = dev->priv; + u_char tmp; + + /* Read the ISR twice... */ + readb(lp->base+MEGAHERTZ_ISR); + udelay(5); + readb(lp->base+MEGAHERTZ_ISR); + + /* Pause 200ms... */ + mdelay(200); + + /* Now read and write the COR... */ + tmp = readb(lp->base + link->conf.ConfigBase + CISREG_COR); + udelay(5); + writeb(tmp, lp->base + link->conf.ConfigBase + CISREG_COR); + + return 0; +} + +static int mhz_mfc_config(dev_link_t *link) +{ + struct net_device *dev = link->priv; + struct smc_private *lp = dev->priv; + tuple_t tuple; + cisparse_t parse; + u_char buf[255]; + cistpl_cftable_entry_t *cf = &parse.cftable_entry; + win_req_t req; + memreq_t mem; + int i, k; + + link->conf.Attributes |= CONF_ENABLE_SPKR; + link->conf.Status = CCSR_AUDIO_ENA; + link->irq.Attributes = + IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED|IRQ_HANDLE_PRESENT; + link->io.Attributes2 = IO_DATA_PATH_WIDTH_8; + link->io.NumPorts2 = 8; + + tuple.Attributes = tuple.TupleOffset = 0; + tuple.TupleData = (cisdata_t *)buf; + tuple.TupleDataMax = sizeof(buf); + tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; + + i = first_tuple(link->handle, &tuple, &parse); + /* The Megahertz combo cards have modem-like CIS entries, so + we have to explicitly try a bunch of port combinations. */ + while (i == CS_SUCCESS) { + link->conf.ConfigIndex = cf->index; + link->io.BasePort2 = cf->io.win[0].base; + for (k = 0; k < 0x400; k += 0x10) { + if (k & 0x80) continue; + link->io.BasePort1 = k ^ 0x300; + i = CardServices(RequestIO, link->handle, &link->io); + if (i == CS_SUCCESS) break; + } + if (i == CS_SUCCESS) break; + i = next_tuple(link->handle, &tuple, &parse); + } + if (i != CS_SUCCESS) + return i; + dev->base_addr = link->io.BasePort1; + + /* Allocate a memory window, for accessing the ISR */ + req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE; + req.Base = 0; + req.Size = 0x1000; + req.AccessSpeed = 0; + link->win = (window_handle_t)link->handle; + i = CardServices(RequestWindow, &link->win, &req); + if (i != CS_SUCCESS) + return i; + lp->base = ioremap(req.Base, 0x1000); + mem.CardOffset = mem.Page = 0; + if (lp->manfid == MANFID_MOTOROLA) + mem.CardOffset = link->conf.ConfigBase; + i = CardServices(MapMemPage, link->win, &mem); + + if ((i == CS_SUCCESS) + && (lp->manfid == MANFID_MEGAHERTZ) + && (lp->cardid == PRODID_MEGAHERTZ_EM3288)) + mhz_3288_power(link); + + return i; +} + +static int mhz_setup(dev_link_t *link) +{ + client_handle_t handle = link->handle; + struct net_device *dev = link->priv; + tuple_t tuple; + cisparse_t parse; + u_char buf[255], *station_addr; + + tuple.Attributes = tuple.TupleOffset = 0; + tuple.TupleData = buf; + tuple.TupleDataMax = sizeof(buf); + + /* Read the station address from the CIS. It is stored as the last + (fourth) string in the Version 1 Version/ID tuple. */ + tuple.DesiredTuple = CISTPL_VERS_1; + if (first_tuple(handle, &tuple, &parse) != CS_SUCCESS) + return -1; + /* Ugh -- the EM1144 card has two VERS_1 tuples!?! */ + if (next_tuple(handle, &tuple, &parse) != CS_SUCCESS) + first_tuple(handle, &tuple, &parse); + if (parse.version_1.ns > 3) { + station_addr = parse.version_1.str + parse.version_1.ofs[3]; + if (cvt_ascii_address(dev, station_addr) == 0) + return 0; + } + + /* Another possibility: for the EM3288, in a special tuple */ + tuple.DesiredTuple = 0x81; + if (CardServices(GetFirstTuple, handle, &tuple) != CS_SUCCESS) + return -1; + if (CardServices(GetTupleData, handle, &tuple) != CS_SUCCESS) + return -1; + buf[12] = '\0'; + if (cvt_ascii_address(dev, buf) == 0) + return 0; + + return -1; +} + +/*====================================================================== + + Configuration stuff for the Motorola Mariner + + mot_config() writes directly to the Mariner configuration + registers because the CIS is just bogus. + +======================================================================*/ + +static void mot_config(dev_link_t *link) +{ + struct net_device *dev = link->priv; + struct smc_private *lp = dev->priv; + ioaddr_t ioaddr = dev->base_addr; + ioaddr_t iouart = link->io.BasePort2; + + /* Set UART base address and force map with COR bit 1 */ + writeb(iouart & 0xff, lp->base + MOT_UART + CISREG_IOBASE_0); + writeb((iouart >> 8) & 0xff, lp->base + MOT_UART + CISREG_IOBASE_1); + writeb(MOT_NORMAL, lp->base + MOT_UART + CISREG_COR); + + /* Set SMC base address and force map with COR bit 1 */ + writeb(ioaddr & 0xff, lp->base + MOT_LAN + CISREG_IOBASE_0); + writeb((ioaddr >> 8) & 0xff, lp->base + MOT_LAN + CISREG_IOBASE_1); + writeb(MOT_NORMAL, lp->base + MOT_LAN + CISREG_COR); + + /* Wait for things to settle down */ + mdelay(100); +} + +static int mot_setup(dev_link_t *link) { + struct net_device *dev = link->priv; + ioaddr_t ioaddr = dev->base_addr; + int i, wait, loop; + unsigned int addr; + + /* Read Ethernet address from Serial EEPROM */ + + for (i = 0; i < 3; i++) { + SMC_SELECT_BANK( 2 ); + outw(MOT_EEPROM + i, ioaddr + POINTER); + SMC_SELECT_BANK( 1 ); + outw((CTL_RELOAD | CTL_EE_SELECT), ioaddr + CONTROL); + + for (loop = 0; loop < 200; loop++) { + udelay(10); + wait = ((CTL_RELOAD | CTL_STORE) & inw(ioaddr + CONTROL)); + if (wait == 0) break; + } + + if (wait) + return -1; + + addr = inw(ioaddr + GENERAL); + dev->dev_addr[2*i] = addr & 0xff; + dev->dev_addr[2*i+1] = (addr >> 8) & 0xff; + } + + return 0; +} + +/*====================================================================*/ + +static int smc_config(dev_link_t *link) +{ + struct net_device *dev = link->priv; + tuple_t tuple; + cisparse_t parse; + u_char buf[255]; + cistpl_cftable_entry_t *cf = &parse.cftable_entry; + int i; + + tuple.Attributes = tuple.TupleOffset = 0; + tuple.TupleData = (cisdata_t *)buf; + tuple.TupleDataMax = sizeof(buf); + tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; + + link->io.NumPorts1 = 16; + i = first_tuple(link->handle, &tuple, &parse); + while (i != CS_NO_MORE_ITEMS) { + if (i == CS_SUCCESS) { + link->conf.ConfigIndex = cf->index; + link->io.BasePort1 = cf->io.win[0].base; + i = CardServices(RequestIO, link->handle, &link->io); + if (i == CS_SUCCESS) break; + } + i = next_tuple(link->handle, &tuple, &parse); + } + if (i == CS_SUCCESS) + dev->base_addr = link->io.BasePort1; + return i; +} + +static int smc_setup(dev_link_t *link) +{ + client_handle_t handle = link->handle; + struct net_device *dev = link->priv; + tuple_t tuple; + cisparse_t parse; + cistpl_lan_node_id_t *node_id; + u_char buf[255], *station_addr; + int i; + + tuple.Attributes = tuple.TupleOffset = 0; + tuple.TupleData = buf; + tuple.TupleDataMax = sizeof(buf); + + /* Check for a LAN function extension tuple */ + tuple.DesiredTuple = CISTPL_FUNCE; + i = first_tuple(handle, &tuple, &parse); + while (i == CS_SUCCESS) { + if (parse.funce.type == CISTPL_FUNCE_LAN_NODE_ID) + break; + i = next_tuple(handle, &tuple, &parse); + } + if (i == CS_SUCCESS) { + node_id = (cistpl_lan_node_id_t *)parse.funce.data; + if (node_id->nb == 6) { + for (i = 0; i < 6; i++) + dev->dev_addr[i] = node_id->id[i]; + return 0; + } + } + + /* Try the third string in the Version 1 Version/ID tuple. */ + tuple.DesiredTuple = CISTPL_VERS_1; + if (first_tuple(handle, &tuple, &parse) != CS_SUCCESS) + return -1; + station_addr = parse.version_1.str + parse.version_1.ofs[2]; + if (cvt_ascii_address(dev, station_addr) == 0) + return 0; + + return -1; +} + +/*====================================================================*/ + +static int osi_config(dev_link_t *link) +{ + struct net_device *dev = link->priv; + static ioaddr_t com[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 }; + int i, j; + + link->conf.Attributes |= CONF_ENABLE_SPKR; + link->conf.Status = CCSR_AUDIO_ENA; + link->irq.Attributes = + IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED|IRQ_HANDLE_PRESENT; + link->io.NumPorts1 = 64; + link->io.Attributes2 = IO_DATA_PATH_WIDTH_8; + link->io.NumPorts2 = 8; + + /* Enable Hard Decode, LAN, Modem */ + link->conf.ConfigIndex = 0x23; + + for (j = 0; j < 4; j++) { + link->io.BasePort2 = com[j]; + i = CardServices(RequestIO, link->handle, &link->io); + if (i == CS_SUCCESS) break; + } + if (i != CS_SUCCESS) { + /* Fallback: turn off hard decode */ + link->conf.ConfigIndex = 0x03; + link->io.NumPorts2 = 0; + i = CardServices(RequestIO, link->handle, &link->io); + } + dev->base_addr = link->io.BasePort1 + 0x10; + return i; +} + +static int osi_setup(dev_link_t *link, u_short manfid, u_short cardid) +{ + client_handle_t handle = link->handle; + struct net_device *dev = link->priv; + tuple_t tuple; + u_char buf[255]; + int i; + + tuple.Attributes = TUPLE_RETURN_COMMON; + tuple.TupleData = buf; + tuple.TupleDataMax = sizeof(buf); + tuple.TupleOffset = 0; + + /* Read the station address from tuple 0x90, subtuple 0x04 */ + tuple.DesiredTuple = 0x90; + i = CardServices(GetFirstTuple, handle, &tuple); + while (i == CS_SUCCESS) { + i = CardServices(GetTupleData, handle, &tuple); + if ((i != CS_SUCCESS) || (buf[0] == 0x04)) + break; + i = CardServices(GetNextTuple, handle, &tuple); + } + if (i != CS_SUCCESS) + return -1; + for (i = 0; i < 6; i++) + dev->dev_addr[i] = buf[i+2]; + + if (manfid != MANFID_OSITECH) return 0; + + if (cardid == PRODID_OSITECH_SEVEN) { + /* Download the Seven of Diamonds firmware */ + for (i = 0; i < sizeof(__Xilinx7OD); i++) { + outb(__Xilinx7OD[i], link->io.BasePort1+2); + udelay(50); + } + } else { + /* Make sure both functions are powered up */ + set_bits(0x300, link->io.BasePort1 + OSITECH_AUI_PWR); + /* Now, turn on the interrupt for both card functions */ + set_bits(0x300, link->io.BasePort1 + OSITECH_RESET_ISR); + DEBUG(2, "AUI/PWR: %4.4x RESET/ISR: %4.4x\n", + inw(link->io.BasePort1 + OSITECH_AUI_PWR), + inw(link->io.BasePort1 + OSITECH_RESET_ISR)); + } + + return 0; +} + +/*====================================================================== + + This verifies that the chip is some SMC91cXX variant, and returns + the revision code if successful. Otherwise, it returns -ENODEV. + +======================================================================*/ + +static int check_sig(dev_link_t *link) +{ + struct net_device *dev = link->priv; + ioaddr_t ioaddr = dev->base_addr; + int width; + u_short s; + + SMC_SELECT_BANK(1); + if (inw(ioaddr + BANK_SELECT) >> 8 != 0x33) { + /* Try powering up the chip */ + outw(0, ioaddr + CONTROL); + mdelay(55); + } + + /* Try setting bus width */ + width = (link->io.Attributes1 == IO_DATA_PATH_WIDTH_AUTO); + s = inb(ioaddr + CONFIG); + if (width) + s |= CFG_16BIT; + else + s &= ~CFG_16BIT; + outb(s, ioaddr + CONFIG); + + /* Check Base Address Register to make sure bus width is OK */ + s = inw(ioaddr + BASE_ADDR); + if ((inw(ioaddr + BANK_SELECT) >> 8 == 0x33) && + ((s >> 8) != (s & 0xff))) { + SMC_SELECT_BANK(3); + s = inw(ioaddr + REVISION); + return (s & 0xff); + } + + if (width) { + event_callback_args_t args; + printk(KERN_INFO "smc91c92_cs: using 8-bit IO window.\n"); + args.client_data = link; + smc91c92_event(CS_EVENT_RESET_PHYSICAL, 0, &args); + CardServices(ReleaseIO, link->handle, &link->io); + link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; + CardServices(RequestIO, link->handle, &link->io); + smc91c92_event(CS_EVENT_CARD_RESET, 0, &args); + return check_sig(link); + } + return -ENODEV; +} + +/*====================================================================== + + smc91c92_config() is scheduled to run after a CARD_INSERTION event + is received, to configure the PCMCIA socket, and to make the + ethernet device available to the system. + +======================================================================*/ + +#define CS_EXIT_TEST(ret, svc, label) \ +if (ret != CS_SUCCESS) { cs_error(link->handle, svc, ret); goto label; } + +static void smc91c92_config(dev_link_t *link) +{ + client_handle_t handle = link->handle; + struct net_device *dev = link->priv; + struct smc_private *lp = dev->priv; + tuple_t tuple; + cisparse_t parse; + u_short buf[32]; + char *name; + int i, rev; + + DEBUG(0, "smc91c92_config(0x%p)\n", link); + + tuple.Attributes = tuple.TupleOffset = 0; + tuple.TupleData = (cisdata_t *)buf; + tuple.TupleDataMax = sizeof(buf); + + tuple.DesiredTuple = CISTPL_CONFIG; + i = first_tuple(handle, &tuple, &parse); + CS_EXIT_TEST(i, ParseTuple, config_failed); + link->conf.ConfigBase = parse.config.base; + link->conf.Present = parse.config.rmask[0]; + + tuple.DesiredTuple = CISTPL_MANFID; + tuple.Attributes = TUPLE_RETURN_COMMON; + if (first_tuple(handle, &tuple, &parse) == CS_SUCCESS) { + lp->manfid = parse.manfid.manf; + lp->cardid = parse.manfid.card; + } + + /* Configure card */ + link->state |= DEV_CONFIG; + + if (lp->manfid == MANFID_OSITECH) { + i = osi_config(link); + } else if (lp->manfid == MANFID_MOTOROLA + || ((lp->manfid == MANFID_MEGAHERTZ) + && ((lp->cardid == PRODID_MEGAHERTZ_VARIOUS) + || (lp->cardid == PRODID_MEGAHERTZ_EM3288)))) { + i = mhz_mfc_config(link); + } else { + i = smc_config(link); + } + CS_EXIT_TEST(i, RequestIO, config_failed); + + i = CardServices(RequestIRQ, link->handle, &link->irq); + CS_EXIT_TEST(i, RequestIRQ, config_failed); + i = CardServices(RequestConfiguration, link->handle, &link->conf); + CS_EXIT_TEST(i, RequestConfiguration, config_failed); + + if (lp->manfid == MANFID_MOTOROLA) + mot_config(link); + + dev->irq = link->irq.AssignedIRQ; + + if ((if_port >= 0) && (if_port <= 2)) + dev->if_port = if_port; + else + printk(KERN_NOTICE "smc91c92_cs: invalid if_port requested\n"); + dev->tbusy = 0; + + if (register_netdev(dev) != 0) { + printk(KERN_ERR "smc91c92_cs: register_netdev() failed\n"); + goto config_undo; + } + + switch (lp->manfid) { + case MANFID_OSITECH: + case MANFID_PSION: + i = osi_setup(link, lp->manfid, lp->cardid); break; + case MANFID_SMC: + case MANFID_NEW_MEDIA: + i = smc_setup(link); break; + case 0x128: /* For broken Megahertz cards */ + case MANFID_MEGAHERTZ: + i = mhz_setup(link); break; + case MANFID_MOTOROLA: + default: /* get the hw address from EEPROM */ + i = mot_setup(link); break; + } + + if (i != 0) { + printk(KERN_NOTICE "smc91c92_cs: Unable to find hardware address.\n"); + link->state &= ~DEV_CONFIG_PENDING; + goto config_undo; + } + + link->dev = &lp->node; + link->state &= ~DEV_CONFIG_PENDING; + + rev = check_sig(link); + name = "???"; + if (rev > 0) + switch (rev >> 4) { + case 3: name = "92"; break; + case 4: name = ((rev & 15) >= 6) ? "96" : "94"; break; + case 5: name = "95"; break; + case 7: name = "100"; break; + case 8: name = "100-FD"; break; + } + printk(KERN_INFO "%s: smc91c%s rev %d: io %#3lx, irq %d, " + "hw_addr ", dev->name, name, (rev & 0x0f), dev->base_addr, + dev->irq); + for (i = 0; i < 6; i++) + printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n")); + if (rev > 0) { + u_long mir, mcr, mii; + ioaddr_t ioaddr = dev->base_addr; + SMC_SELECT_BANK(0); + mir = inw(ioaddr + MEMINFO) & 0xff; + if (mir == 0xff) mir++; + /* Get scale factor for memory size */ + mcr = ((rev >> 4) > 3) ? inw(ioaddr + MEMCFG) : 0x0200; + mir *= 128 * (1<<((mcr >> 9) & 7)); + if (mir & 0x3ff) + printk(KERN_INFO " %lu byte", mir); + else + printk(KERN_INFO " %lu kb", mir>>10); + SMC_SELECT_BANK(1); + mii = inw(ioaddr + CONFIG) & CFG_MII_SELECT; + printk(" buffer, %s xcvr\n", mii ? "MII" : if_names[dev->if_port]); + } + + return; + +config_undo: + unregister_netdev(dev); +config_failed: /* CS_EXIT_TEST() calls jump to here... */ + smc91c92_release((u_long)link); + +} /* smc91c92_config */ + +/*====================================================================== + + After a card is removed, smc91c92_release() will unregister the net + device, and release the PCMCIA configuration. If the device is + still open, this will be postponed until it is closed. + +======================================================================*/ + +static void smc91c92_release(u_long arg) +{ + dev_link_t *link = (dev_link_t *)arg; + struct net_device *dev = link->priv; + + DEBUG(0, "smc91c92_release(0x%p)\n", link); + + if (link->open) { + DEBUG(1, "smc91c92_cs: release postponed, '%s' still open\n", + dev->name); + link->state |= DEV_STALE_CONFIG; + return; + } + + CardServices(ReleaseConfiguration, link->handle); + CardServices(ReleaseIO, link->handle, &link->io); + CardServices(ReleaseIRQ, link->handle, &link->irq); + if (link->win) { + struct smc_private *lp = dev->priv; + iounmap(lp->base); + CardServices(ReleaseWindow, link->win); + } + + link->state &= ~(DEV_CONFIG | DEV_RELEASE_PENDING); + +} /* smc91c92_release */ + +/*====================================================================== + + The card status event handler. Mostly, this schedules other + stuff to run after an event is received. A CARD_REMOVAL event + also sets some flags to discourage the net drivers from trying + to talk to the card any more. + +======================================================================*/ + +static int smc91c92_event(event_t event, int priority, + event_callback_args_t *args) +{ + dev_link_t *link = args->client_data; + struct net_device *dev = link->priv; + + DEBUG(1, "smc91c92_event(0x%06x)\n", event); + + switch (event) { + case CS_EVENT_CARD_REMOVAL: + link->state &= ~DEV_PRESENT; + if (link->state & DEV_CONFIG) { + dev->tbusy = 1; + dev->start = 0; + link->release.expires = jiffies + HZ/20; + link->state |= DEV_RELEASE_PENDING; + add_timer(&link->release); + } + break; + case CS_EVENT_CARD_INSERTION: + link->state |= DEV_PRESENT; + smc91c92_config(link); + break; + case CS_EVENT_PM_SUSPEND: + link->state |= DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_RESET_PHYSICAL: + if (link->state & DEV_CONFIG) { + if (link->open) { + dev->tbusy = 1; dev->start = 0; + } + CardServices(ReleaseConfiguration, link->handle); + } + break; + case CS_EVENT_PM_RESUME: + link->state &= ~DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_CARD_RESET: + if (link->state & DEV_CONFIG) { + struct smc_private *lp = dev->priv; + if ((lp->manfid == MANFID_MEGAHERTZ) && + (lp->cardid == PRODID_MEGAHERTZ_EM3288)) + mhz_3288_power(link); + CardServices(RequestConfiguration, link->handle, &link->conf); + if (lp->manfid == MANFID_MOTOROLA) + mot_config(link); + if ((lp->manfid == MANFID_OSITECH) && + (lp->cardid != PRODID_OSITECH_SEVEN)) { + /* Power up the card and enable interrupts */ + set_bits(0x0300, dev->base_addr-0x10+OSITECH_AUI_PWR); + set_bits(0x0300, dev->base_addr-0x10+OSITECH_RESET_ISR); + } + if (link->open) { + smc_reset(dev); + dev->tbusy = 0; dev->start = 1; + } + } + break; + } + return 0; +} /* smc91c92_event */ + +/*====================================================================== + + The driver core code, most of which should be common with a + non-PCMCIA implementation. + +======================================================================*/ + +#ifdef PCMCIA_DEBUG +static void smc_dump(struct net_device *dev) +{ + ioaddr_t ioaddr = dev->base_addr; + u_short i, w, save; + save = inw(ioaddr + BANK_SELECT); + for (w = 0; w < 4; w++) { + SMC_SELECT_BANK(w); + printk(KERN_DEBUG "bank %d: ", w); + for (i = 0; i < 14; i += 2) + printk(" %04x", inw(ioaddr + i)); + printk("\n"); + } + outw(save, ioaddr + BANK_SELECT); +} +#endif + +static int smc91c92_open(struct net_device *dev) +{ + struct smc_private *lp = (struct smc_private *)dev->priv; + dev_link_t *link; + +#ifdef PCMCIA_DEBUG + DEBUG(0, "%s: smc91c92_open(%p), ID/Window %4.4x.\n", + dev->name, dev, inw(dev->base_addr + BANK_SELECT)); + if (pc_debug > 1) smc_dump(dev); +#endif + + /* Check that the PCMCIA card is still here. */ + for (link = dev_list; link; link = link->next) + if (link->priv == dev) break; + /* Physical device present signature. */ + if (!DEV_OK(link)) + return -ENODEV; + if (check_sig(link) < 0) { + printk("smc91c92_cs: Yikes! Bad chip signature!\n"); + return -ENODEV; + } + link->open++; + MOD_INC_USE_COUNT; + + dev->interrupt = 0; dev->tbusy = 0; dev->start = 1; + lp->saved_skb = 0; + lp->packets_waiting = 0; + + smc_reset(dev); + lp->media.function = &media_check; + lp->media.data = (u_long)dev; + lp->media.expires = jiffies + HZ; + add_timer(&lp->media); + + return 0; +} /* smc91c92_open */ + +/*====================================================================*/ + +static int smc91c92_close(struct net_device *dev) +{ + ioaddr_t ioaddr = dev->base_addr; + dev_link_t *link; + + DEBUG(0, "%s: smc91c92_close(), status %4.4x.\n", + dev->name, inw(ioaddr + BANK_SELECT)); + + dev->tbusy = 1; + dev->start = 0; + + /* Shut off all interrupts, and turn off the Tx and Rx sections. + Don't bother to check for chip present. */ + SMC_SELECT_BANK( 2 ); /* Nominally paranoia, but do no assume... */ + outw(0, ioaddr + INTERRUPT); + SMC_SELECT_BANK( 0 ); + mask_bits(0xff00, ioaddr + RCR); + mask_bits(0xff00, ioaddr + TCR); + + /* Put the chip into power-down mode. */ + SMC_SELECT_BANK( 1 ); + outw(CTL_POWERDOWN, ioaddr + CONTROL ); + + for (link = dev_list; link; link = link->next) + if (link->priv == dev) break; + if (link == NULL) + return -ENODEV; + + link->open--; dev->start = 0; + del_timer(&((struct smc_private *)dev->priv)->media); + if (link->state & DEV_STALE_CONFIG) { + link->release.expires = jiffies + HZ/20; + link->state |= DEV_RELEASE_PENDING; + add_timer(&link->release); + } + + MOD_DEC_USE_COUNT; + + return 0; +} /* smc91c92_close */ + +/*====================================================================== + + Transfer a packet to the hardware and trigger the packet send. + This may be called at either from either the Tx queue code + or the interrupt handler. + +======================================================================*/ + +static void smc_hardware_send_packet( struct net_device * dev ) +{ + struct smc_private *lp = (struct smc_private *)dev->priv; + struct sk_buff *skb = lp->saved_skb; + ioaddr_t ioaddr = dev->base_addr; + unsigned char packet_no; + + if ( !skb ) { + printk(KERN_ERR "%s: In XMIT with no packet to send.\n", dev->name); + return; + } + + /* There should be a packet slot waiting. */ + packet_no = inw(ioaddr + PNR_ARR) >> 8; + if ( packet_no & 0x80 ) { + /* If not, there is a hardware problem! Likely an ejected card. */ + printk(KERN_WARNING "%s: 91c92 hardware Tx buffer allocation" + " failed, status %#2.2x.\n", dev->name, packet_no); + dev_kfree_skb (skb); + lp->saved_skb = NULL; + dev->tbusy = 0; + return; + } + + lp->stats.tx_bytes += skb->len; + /* The card should use the just-allocated buffer. */ + outw( packet_no, ioaddr + PNR_ARR ); + /* point to the beginning of the packet */ + outw( PTR_AUTOINC , ioaddr + POINTER ); + + /* Send the packet length ( +6 for status, length and ctl byte ) + and the status word ( set to zeros ). */ + { + unsigned char *buf = skb->data; + int length = skb->len; /* The chip will pad to ethernet min length. */ + + DEBUG(2, "%s: Trying to xmit packet of length %d.\n", + dev->name, length); + +#ifdef USE_32_BIT + outl((length+6) << 16, ioaddr + DATA_1); + if (length & 0x2) { + outsl(ioaddr + DATA_1, buf, length >> 2 ); + outw( *((uint16 *)(buf + (length & 0xFFFFFFFC))),ioaddr +DATA_1); + } else + outsl(ioaddr + DATA_1, buf, length >> 2 ); +#else + /* send the packet length: +6 for status words, length, and ctl */ + outw( 0, ioaddr + DATA_1 ); + outw(length + 6, ioaddr + DATA_1 ); + outsw(ioaddr + DATA_1 , buf, length >> 1); +#endif + + /* The odd last byte, if there is one, goes in the control word. */ + outw((length & 1) ? 0x2000 | buf[length-1] : 0, ioaddr + DATA_1 ); + } + + /* Enable the Tx interrupts, both Tx (TxErr) and TxEmpty. */ + outw(((IM_TX_INT|IM_TX_EMPTY_INT)<<8) | + (inw(ioaddr + INTERRUPT) & 0xff00), + ioaddr + INTERRUPT); + + /* The chip does the rest of the work. */ + outw( MC_ENQUEUE , ioaddr + MMU_CMD ); + + lp->saved_skb = NULL; + dev_kfree_skb (skb); + dev->trans_start = jiffies; + dev->tbusy = 0; + return; +} + +/*====================================================================*/ + +static int smc_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct smc_private *lp = (struct smc_private *)dev->priv; + ioaddr_t ioaddr = dev->base_addr; + unsigned short num_pages; + short time_out, ir; + + /* Transmitter timeout, serious problems. */ + if (dev->tbusy) { + int tickssofar = jiffies - dev->trans_start; + if (tickssofar < TX_TIMEOUT) + return 1; + printk(KERN_NOTICE "%s: SMC91c92 transmit timed out, " + "Tx_status %2.2x status %4.4x.\n", + dev->name, inw(ioaddr)&0xff, inw(ioaddr + 2)); + lp->stats.tx_errors++; + smc_reset(dev); + dev->trans_start = jiffies; + dev->tbusy = 0; + lp->saved_skb = NULL; + } + + DEBUG(2, "%s: smc91c92_start_xmit(length = %d) called," + " status %4.4x.\n", dev->name, skb->len, inw(ioaddr + 2)); + + /* Avoid timer-based retransmission conflicts. */ + if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { + printk(KERN_ERR "%s: transmitter access conflict.\n", dev->name); + return 1; + } + + if ( lp->saved_skb) { + /* THIS SHOULD NEVER HAPPEN. */ + lp->stats.tx_aborted_errors++; + printk(KERN_DEBUG "%s: Internal error -- sent packet while busy.\n", + dev->name); + return 1; + } + lp->saved_skb = skb; + + num_pages = skb->len >> 8; + + if (num_pages > 7) { + printk(KERN_ERR "%s: Far too big packet error.\n", dev->name); + dev_kfree_skb (skb); + lp->saved_skb = NULL; + lp->stats.tx_dropped++; + return 0; /* Do not re-queue this packet. */ + } + /* A packet is now waiting. */ + lp->packets_waiting++; + + SMC_SELECT_BANK( 2 ); /* Paranoia, we should always be in window 2 */ + + /* Allocate the memory; send the packet now if we win. */ + outw( MC_ALLOC | num_pages, ioaddr + MMU_CMD ); + for (time_out = MEMORY_WAIT_TIME; time_out >= 0; time_out--) { + ir = inw(ioaddr+INTERRUPT); + if (ir & IM_ALLOC_INT) { + /* Acknowledge the interrupt, send the packet. */ + outw((ir&0xff00) | IM_ALLOC_INT, ioaddr + INTERRUPT); + smc_hardware_send_packet(dev); /* Send the packet now.. */ + return 0; + } + } + + /* Otherwise defer until the Tx-space-allocated interrupt. */ + DEBUG(2, "%s: memory allocation deferred.\n", dev->name); + outw((IM_ALLOC_INT << 8) | (ir & 0xff00), ioaddr + INTERRUPT); + + return 0; +} + +/*====================================================================== + + Handle a Tx anomolous event. Entered while in Window 2. + +======================================================================*/ + +static void smc_tx_err( struct net_device * dev ) +{ + struct smc_private *lp = (struct smc_private *)dev->priv; + ioaddr_t ioaddr = dev->base_addr; + int saved_packet = inw(ioaddr + PNR_ARR) & 0xff; + int packet_no = inw(ioaddr + FIFO_PORTS) & 0x7f; + int tx_status; + + /* select this as the packet to read from */ + outw( packet_no, ioaddr + PNR_ARR ); + + /* read the first word from this packet */ + outw( PTR_AUTOINC | PTR_READ | 0, ioaddr + POINTER ); + + tx_status = inw(ioaddr + DATA_1); + + lp->stats.tx_errors++; + if (tx_status & TS_LOSTCAR) lp->stats.tx_carrier_errors++; + if (tx_status & TS_LATCOL) lp->stats.tx_window_errors++; + if (tx_status & TS_16COL) { + lp->stats.tx_aborted_errors++; + lp->tx_err++; + } + + if ( tx_status & TS_SUCCESS ) { + printk(KERN_NOTICE "%s: Successful packet caused error " + "interrupt?\n", dev->name); + } + /* re-enable transmit */ + SMC_SELECT_BANK( 0 ); + outw( inw(ioaddr + TCR) | TCR_ENABLE, ioaddr + TCR); + SMC_SELECT_BANK( 2 ); + + outw( MC_FREEPKT, ioaddr + MMU_CMD ); /* Free the packet memory. */ + + /* one less packet waiting for me */ + lp->packets_waiting--; + + outw( saved_packet, ioaddr + PNR_ARR ); + return; +} + +/*====================================================================*/ + +static void smc_eph_irq(struct net_device *dev) +{ + struct smc_private *lp = dev->priv; + ioaddr_t ioaddr = dev->base_addr; + unsigned short card_stats, ephs; + + SMC_SELECT_BANK(0); + ephs = inw(ioaddr + EPH); + DEBUG(2, "%s: Ethernet protocol handler interrupt, status" + " %4.4x.\n", dev->name, ephs); + /* Could be a counter roll-over warning: update stats. */ + card_stats = inw( ioaddr + COUNTER ); + /* single collisions */ + lp->stats.collisions += card_stats & 0xF; + card_stats >>= 4; + /* multiple collisions */ + lp->stats.collisions += card_stats & 0xF; +#if 0 /* These are for when linux supports these statistics */ + card_stats >>= 4; /* deferred */ + card_stats >>= 4; /* excess deferred */ +#endif + /* If we had a transmit error we must re-enable the transmitter. */ + outw( inw(ioaddr + TCR) | TCR_ENABLE, ioaddr + TCR); + + /* Clear a link error interrupt. */ + SMC_SELECT_BANK(1); + outw(CTL_AUTO_RELEASE | 0x0000, ioaddr + CONTROL); + outw(CTL_AUTO_RELEASE | CTL_TE_ENABLE | CTL_CR_ENABLE, + ioaddr + CONTROL); + SMC_SELECT_BANK(2); +} + +/*====================================================================*/ + +static void smc_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = dev_id; + struct smc_private *lp; + ioaddr_t ioaddr; + u_short saved_bank, saved_pointer, mask, status; + char bogus_cnt = INTR_WORK; /* Work we are willing to do. */ + + if ((dev == NULL) || !dev->start) + return; + ioaddr = dev->base_addr; + +#ifdef PCMCIA_DEBUG + if (dev->interrupt) { + printk(KERN_ERR "%s: re-entering the interrupt handler.\n", + dev->name); + return; + } + dev->interrupt = 1; + + DEBUG(3, "%s: SMC91c92 interrupt %d at %#x.\n", dev->name, + irq, ioaddr); +#endif + + lp = (struct smc_private *)dev->priv; + lp->watchdog = 0; + saved_bank = inw(ioaddr + BANK_SELECT); + if ((saved_bank & 0xff00) != 0x3300) { + /* The device does not exist -- the card could be off-line, or + maybe it has been ejected. */ +#ifdef PCMCIA_DEBUG + if (dev->start) + DEBUG(1, "%s: SMC91c92 interrupt %d for non-existent" + "/ejected device.\n", dev->name, irq); + dev->interrupt = 0; +#endif + goto irq_done; + } + + SMC_SELECT_BANK(2); + saved_pointer = inw(ioaddr + POINTER); + mask = inw(ioaddr + INTERRUPT) >> 8; + /* clear all interrupts */ + outw( 0, ioaddr + INTERRUPT ); + + do { /* read the status flag, and mask it */ + status = inw(ioaddr + INTERRUPT) & 0xff; + DEBUG(3, "%s: Status is %#2.2x (mask %#2.2x).\n", dev->name, + status, mask); + if ((status & mask) == 0) + break; + + if (status & IM_RCV_INT) { + /* Got a packet(s). */ + smc_rx(dev); + lp->last_rx = jiffies; + } + if (status & IM_TX_INT) { + smc_tx_err(dev); + outw(IM_TX_INT, ioaddr + INTERRUPT); + } + status &= mask; + if (status & IM_TX_EMPTY_INT) { + outw(IM_TX_EMPTY_INT, ioaddr + INTERRUPT); + mask &= ~IM_TX_EMPTY_INT; + lp->stats.tx_packets += lp->packets_waiting; + lp->packets_waiting = 0; + } + if (status & IM_ALLOC_INT) { + /* Clear this interrupt so it doesn't happen again */ + mask &= ~IM_ALLOC_INT; + + smc_hardware_send_packet(dev); + + /* enable xmit interrupts based on this */ + mask |= ( IM_TX_EMPTY_INT | IM_TX_INT ); + + /* and let the card send more packets to me */ + mark_bh( NET_BH ); + } + if (status & IM_RX_OVRN_INT) { + lp->stats.rx_errors++; + lp->stats.rx_fifo_errors++; + outw(IM_RX_OVRN_INT, ioaddr + INTERRUPT); + } + if (status & IM_EPH_INT) + smc_eph_irq(dev); + } while ( --bogus_cnt); + + DEBUG(3, " Restoring saved registers mask %2.2x bank %4.4x" + " pointer %4.4x.\n", mask, saved_bank, saved_pointer); + + /* restore state register */ + outw((mask<<8), ioaddr + INTERRUPT); + outw(saved_pointer, ioaddr + POINTER); + SMC_SELECT_BANK( saved_bank ); + +#ifdef PCMCIA_DEBUG + dev->interrupt = 0; + DEBUG(3, "%s: Exiting interrupt IRQ%d.\n", dev->name, irq); +#endif + +irq_done: + + if ((lp->manfid == MANFID_OSITECH) && + (lp->cardid != PRODID_OSITECH_SEVEN)) { + /* Retrigger interrupt if needed */ + mask_bits(0x00ff, ioaddr-0x10+OSITECH_RESET_ISR); + set_bits(0x0300, ioaddr-0x10+OSITECH_RESET_ISR); + } + if (lp->manfid == MANFID_MOTOROLA) { + u_char cor; + cor = readb(lp->base + MOT_UART + CISREG_COR); + writeb(cor & ~COR_IREQ_ENA, lp->base + MOT_UART + CISREG_COR); + writeb(cor, lp->base + MOT_UART + CISREG_COR); + cor = readb(lp->base + MOT_LAN + CISREG_COR); + writeb(cor & ~COR_IREQ_ENA, lp->base + MOT_LAN + CISREG_COR); + writeb(cor, lp->base + MOT_LAN + CISREG_COR); + } +#ifdef DOES_NOT_WORK + if (lp->base != NULL) { /* Megahertz MFC's */ + readb(lp->base+MEGAHERTZ_ISR); + readb(lp->base+MEGAHERTZ_ISR); + } +#endif +} + +/*====================================================================*/ + +static void smc_rx(struct net_device *dev) +{ + struct smc_private *lp = (struct smc_private *)dev->priv; + ioaddr_t ioaddr = dev->base_addr; + int rx_status; + int packet_length; /* Caution: not frame length, rather words + to transfer from the chip. */ + + /* Assertion: we are in Window 2. */ + + if (inw(ioaddr + FIFO_PORTS) & FP_RXEMPTY) { + printk(KERN_ERR "%s: smc_rx() with nothing on Rx FIFO.\n", + dev->name); + return; + } + + /* Reset the read pointer, and read the status and packet length. */ + outw( PTR_READ | PTR_RCV | PTR_AUTOINC, ioaddr + POINTER ); + rx_status = inw(ioaddr + DATA_1); + packet_length = inw(ioaddr + DATA_1) & 0x07ff; + + DEBUG(2, "%s: Receive status %4.4x length %d.\n", + dev->name, rx_status, packet_length); + + if ( !(rx_status & RS_ERRORS )) { + /* do stuff to make a new packet */ + struct sk_buff *skb; + + /* Note: packet_length adds 5 or 6 extra bytes here! */ + skb = dev_alloc_skb(packet_length+2); + + if ( skb == NULL ) { + DEBUG(1, "%s: Low memory, packet dropped.\n", dev->name); + lp->stats.rx_dropped++; + outw( MC_RELEASE, ioaddr + MMU_CMD ); + return; + } + + packet_length -= (rx_status & RS_ODDFRAME ? 5 : 6); + skb_reserve(skb, 2); + insw(ioaddr+DATA_1, skb_put(skb, packet_length), + (packet_length+1)>>1); + skb->protocol = eth_type_trans(skb, dev); + + skb->dev = dev; + netif_rx(skb); + lp->stats.rx_packets++; + lp->stats.rx_bytes += skb->len; + if (rx_status & RS_MULTICAST) + lp->stats.multicast++; + } else { + /* error ... */ + lp->stats.rx_errors++; + + if (rx_status & RS_ALGNERR) lp->stats.rx_frame_errors++; + if (rx_status & (RS_TOOSHORT | RS_TOOLONG)) + lp->stats.rx_length_errors++; + if (rx_status & RS_BADCRC) lp->stats.rx_crc_errors++; + } + /* Let the MMU free the memory of this packet. */ + outw(MC_RELEASE, ioaddr + MMU_CMD); + + return; +} + +/*====================================================================*/ + +static struct net_device_stats *smc91c92_get_stats(struct net_device *dev) +{ + struct smc_private *lp = (struct smc_private *)dev->priv; + /* Nothing to update - the 91c92 is a pretty primative chip. */ + return &lp->stats; +} + +/*====================================================================== + + Compute the AUTODIN polynomial "CRC32" for ethernet packets. + +======================================================================*/ + +static unsigned const ethernet_polynomial = 0x04c11db7U; + +static unsigned ether_crc(int length, unsigned char *data) +{ + int crc = 0xffffffff; /* Initial value. */ + + while (--length >= 0) { + unsigned char current_octet = *data++; + int bit; + for (bit = 0; bit < 8; bit++, current_octet >>= 1) { + crc = (crc << 1) ^ + ((crc < 0) ^ (current_octet & 1) ? ethernet_polynomial : 0); + } + } + /* The hash index is the either the upper or lower bits of the CRC, so + * we return the entire CRC. + */ + return crc; +} + +/*====================================================================== + + Calculate values for the hardware multicast filter hash table. + +======================================================================*/ + +static void fill_multicast_tbl(int count, struct dev_mc_list *addrs, + unsigned char *multicast_table) +{ + struct dev_mc_list *mc_addr; + + for (mc_addr = addrs; mc_addr && --count > 0; mc_addr = mc_addr->next) { + unsigned int position = ether_crc(6, mc_addr->dmi_addr); +#ifndef final_version /* Verify multicast address. */ + if ( (mc_addr->dmi_addr[0] & 1) == 0) + continue; +#endif + multicast_table[position >> 29] |= 1 << ((position >> 26) & 7); + } +} + +/*====================================================================== + + Set the receive mode. + + This routine is used by both the protocol level to notify us of + promiscuous/multicast mode changes, and by the open/reset code to + initialize the Rx registers. We always set the multicast list and + leave the receiver running. + +======================================================================*/ + +static void set_rx_mode(struct net_device *dev) +{ + ioaddr_t ioaddr = dev->base_addr; + unsigned int multicast_table[ 2 ] = { 0, }; + long flags; + uint16 rx_cfg_setting; + + if (dev->flags & IFF_PROMISC) { + printk(KERN_NOTICE "%s: setting Rx mode to promiscuous.\n", dev->name); + rx_cfg_setting = RxStripCRC | RxEnable | RxPromisc | RxAllMulti; + } else if (dev->flags & IFF_ALLMULTI) + rx_cfg_setting = RxStripCRC | RxEnable | RxAllMulti; + else { + if (dev->mc_count) { + fill_multicast_tbl(dev->mc_count, dev->mc_list, + (unsigned char *)multicast_table); + } + rx_cfg_setting = RxStripCRC | RxEnable; + } + + /* Load MC table and Rx setting into the chip without interrupts. */ + save_flags(flags); + cli(); + SMC_SELECT_BANK( 3 ); + outl(multicast_table[0], ioaddr + MULTICAST0); + outl(multicast_table[1], ioaddr + MULTICAST4); + SMC_SELECT_BANK(0); + outw(rx_cfg_setting, ioaddr + RCR); + SMC_SELECT_BANK(2); + restore_flags(flags); + + return; +} + +/*====================================================================== + + Senses when a card's config changes. Here, it's coax or TP. + +======================================================================*/ + +static int s9k_config(struct net_device *dev, struct ifmap *map) +{ + if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) { + if (map->port <= 2) { + dev->if_port = map->port; + printk(KERN_INFO "%s: switched to %s port\n", + dev->name, if_names[dev->if_port]); + } else + return -EINVAL; + } + smc_reset(dev); + return 0; +} + +/*====================================================================== + + Reset the chip, reloading every register that might be corrupted. + +======================================================================*/ + +/* + Set transceiver type, perhaps to something other than what the user + specified in dev->if_port. +*/ +static void smc_set_xcvr(struct net_device *dev, int if_port) +{ + struct smc_private *lp = (struct smc_private *)dev->priv; + ioaddr_t ioaddr = dev->base_addr; + u_short saved_bank; + + saved_bank = inw(ioaddr + BANK_SELECT); + SMC_SELECT_BANK(1); + if (if_port == 2) { + outw(lp->cfg | CFG_AUI_SELECT, ioaddr + CONFIG); + if ((lp->manfid == MANFID_OSITECH) && + (lp->cardid != PRODID_OSITECH_SEVEN)) + set_bits(OSI_AUI_PWR, ioaddr - 0x10 + OSITECH_AUI_PWR); + lp->media_status = ((dev->if_port == 0) ? 0x0001 : 0x0002); + } else { + outw(lp->cfg, ioaddr + CONFIG); + if ((lp->manfid == MANFID_OSITECH) && + (lp->cardid != PRODID_OSITECH_SEVEN)) + mask_bits(~OSI_AUI_PWR, ioaddr - 0x10 + OSITECH_AUI_PWR); + lp->media_status = ((dev->if_port == 0) ? 0x0012 : 0x4001); + } + SMC_SELECT_BANK(saved_bank); +} + +static void smc_reset(struct net_device *dev) +{ + ioaddr_t ioaddr = dev->base_addr; + struct smc_private *lp = dev->priv; + int i; + + DEBUG(0, "%s: smc91c92 reset called.\n", dev->name); + + /* The first interaction must be a write to bring the chip out + of sleep mode. */ + SMC_SELECT_BANK( 0 ); + /* Reset the chip. */ + outw( RCR_SOFTRESET, ioaddr + RCR ); + udelay(10); + + /* Clear the transmit and receive configuration registers. */ + outw( RCR_CLEAR, ioaddr + RCR ); + outw( TCR_CLEAR, ioaddr + TCR ); + + /* Set the Window 1 control, configuration and station addr registers. + No point in writing the I/O base register ;-> */ + SMC_SELECT_BANK(1); + /* Automatically release succesfully transmitted packets, + Accept link errors, counter and Tx error interrupts. */ + outw(CTL_AUTO_RELEASE | CTL_TE_ENABLE | CTL_CR_ENABLE, + ioaddr + CONTROL); + lp->cfg = inw(ioaddr + CONFIG) & ~CFG_AUI_SELECT; + lp->cfg |= CFG_NO_WAIT | CFG_16BIT | CFG_STATIC | + (lp->manfid == MANFID_OSITECH ? (CFG_IRQ_SEL_1 | CFG_IRQ_SEL_0) : 0); + smc_set_xcvr(dev, dev->if_port); + if ((lp->manfid == MANFID_OSITECH) && + (lp->cardid != PRODID_OSITECH_SEVEN)) + outw((dev->if_port == 2 ? OSI_AUI_PWR : 0) | + (inw(ioaddr-0x10+OSITECH_AUI_PWR) & 0xff00), + ioaddr - 0x10 + OSITECH_AUI_PWR); + + /* Fill in the physical address. The databook is wrong about the order! */ + for (i = 0; i < 6; i += 2) + outw((dev->dev_addr[i+1]<<8)+dev->dev_addr[i], + ioaddr + ADDR0 + i); + + /* Reset the MMU */ + SMC_SELECT_BANK(2); + outw(MC_RESET, ioaddr + MMU_CMD ); + outw(0, ioaddr + INTERRUPT ); + + /* Re-enable the chip. */ + SMC_SELECT_BANK(0); + outw(((lp->cfg & CFG_MII_SELECT) ? 0 : TCR_MONCSN) | + TCR_ENABLE | TCR_PAD_EN, ioaddr + TCR); + set_rx_mode(dev); + + /* Enable interrupts. */ + SMC_SELECT_BANK( 2 ); + outw( (IM_EPH_INT | IM_RX_OVRN_INT | IM_RCV_INT) << 8, + ioaddr + INTERRUPT ); +} + +/*====================================================================== + + Media selection timer routine + +======================================================================*/ + +static void media_check(u_long arg) +{ + struct net_device *dev = (struct net_device *)(arg); + struct smc_private *lp = (struct smc_private *)dev->priv; + ioaddr_t ioaddr = dev->base_addr; + u_short i, media, saved_bank; + + if (dev->start == 0) goto reschedule; + + lp = (struct smc_private *)dev->priv; + saved_bank = inw(ioaddr + BANK_SELECT); + SMC_SELECT_BANK(2); + i = inw(ioaddr + INTERRUPT); + SMC_SELECT_BANK(0); + media = inw(ioaddr + EPH) & EPH_LINK_OK; + SMC_SELECT_BANK(1); + media |= (inw(ioaddr + CONFIG) & CFG_AUI_SELECT) ? 2 : 1; + SMC_SELECT_BANK(saved_bank); + + /* Check for pending interrupt with watchdog flag set: with + this, we can limp along even if the interrupt is blocked */ + if (lp->watchdog++ && ((i>>8) & i)) { + if (!lp->fast_poll) + printk(KERN_INFO "%s: interrupt(s) dropped!\n", dev->name); + smc_interrupt(dev->irq, dev, NULL); + lp->fast_poll = HZ; + } + if (lp->fast_poll) { + lp->fast_poll--; + lp->media.expires = jiffies + 1; + add_timer(&lp->media); + return; + } + + if (lp->cfg & CFG_MII_SELECT) + goto reschedule; + + /* Ignore collisions unless we've had no rx's recently */ + if (jiffies - lp->last_rx > HZ) { + if (lp->tx_err || (lp->media_status & EPH_16COL)) + media |= EPH_16COL; + } + lp->tx_err = 0; + + if (media != lp->media_status) { + if ((media & lp->media_status & 1) && + ((lp->media_status ^ media) & EPH_LINK_OK)) + printk(KERN_INFO "%s: %s link beat\n", dev->name, + (lp->media_status & EPH_LINK_OK ? "lost" : "found")); + else if ((media & lp->media_status & 2) && + ((lp->media_status ^ media) & EPH_16COL)) + printk(KERN_INFO "%s: coax cable %s\n", dev->name, + (media & EPH_16COL ? "problem" : "ok")); + if (dev->if_port == 0) { + if (media & 1) { + if (media & EPH_LINK_OK) + printk(KERN_INFO "%s: flipped to 10baseT\n", + dev->name); + else + smc_set_xcvr(dev, 2); + } else { + if (media & EPH_16COL) + smc_set_xcvr(dev, 1); + else + printk(KERN_INFO "%s: flipped to 10base2\n", + dev->name); + } + } + lp->media_status = media; + } + +reschedule: + lp->media.expires = jiffies + HZ; + add_timer(&lp->media); +} + +/*====================================================================*/ + +static int __init init_smc91c92_cs(void) +{ + servinfo_t serv; + DEBUG(0, "%s\n", version); + CardServices(GetCardServicesInfo, &serv); + if (serv.Revision != CS_RELEASE_CODE) { + printk(KERN_ERR + "smc91c92_cs: Card Services release does not match!\n"); + return -1; + } + register_pccard_driver(&dev_info, &smc91c92_attach, &smc91c92_detach); + return 0; +} + +static void __exit exit_smc91c92_cs(void) +{ + DEBUG(0, "smc91c92_cs: unloading\n"); + unregister_pccard_driver(&dev_info); + while (dev_list != NULL) + smc91c92_detach(dev_list); +} + +module_init(init_smc91c92_cs); +module_exit(exit_smc91c92_cs); diff --git a/drivers/net/pcmcia/wavelan.h b/drivers/net/pcmcia/wavelan.h new file mode 100644 index 000000000..0e1ee227d --- /dev/null +++ b/drivers/net/pcmcia/wavelan.h @@ -0,0 +1,383 @@ +/* + * Wavelan Pcmcia driver + * + * Jean II - HPLB '96 + * + * Reorganization and extension of the driver. + * Original copyright follow. See wavelan_cs.h for details. + * + * This file contain the declarations of the Wavelan hardware. Note that + * the Pcmcia Wavelan include a i82593 controler (see definitions in + * file i82593.h). + * + * The main difference between the pcmcia hardware and the ISA one is + * the Ethernet Controler (i82593 instead of i82586). The i82593 allow + * only one send buffer. The PSA (Parameter Storage Area : EEprom for + * permanent storage of various info) is memory mapped, but not the + * MMI (Modem Management Interface). + */ + +/* + * Definitions for the AT&T GIS (formerly NCR) WaveLAN PCMCIA card: + * An Ethernet-like radio transceiver controlled by an Intel 82593 + * coprocessor. + * + * + **************************************************************************** + * Copyright 1995 + * Anthony D. Joseph + * Massachusetts Institute of Technology + * + * Permission to use, copy, modify, and distribute this program + * for any purpose and without fee is hereby granted, provided + * that this copyright and permission notice appear on all copies + * and supporting documentation, the name of M.I.T. not be used + * in advertising or publicity pertaining to distribution of the + * program without specific prior permission, and notice be given + * in supporting documentation that copying and distribution is + * by permission of M.I.T. M.I.T. makes no representations about + * the suitability of this software for any purpose. It is pro- + * vided "as is" without express or implied warranty. + **************************************************************************** + * + * + * Credits: + * Special thanks to Jan Hoogendoorn of AT&T GIS Utrecht for + * providing extremely useful information about WaveLAN PCMCIA hardware + * + * This driver is based upon several other drivers, in particular: + * David Hinds' Linux driver for the PCMCIA 3c589 ethernet adapter + * Bruce Janson's Linux driver for the AT-bus WaveLAN adapter + * Anders Klemets' PCMCIA WaveLAN adapter driver + * Robert Morris' BSDI driver for the PCMCIA WaveLAN adapter + */ + +#ifndef _WAVELAN_H +#define _WAVELAN_H + +/************************** MAGIC NUMBERS ***************************/ + +/* The detection of the wavelan card is made by reading the MAC address + * from the card and checking it. If you have a non AT&T product (OEM, + * like DEC RoamAbout, or Digital Ocean, Epson, ...), you must modify this + * part to accomodate your hardware... + */ +const unsigned char MAC_ADDRESSES[][3] = +{ + { 0x08, 0x00, 0x0E }, /* AT&T Wavelan (standard) & DEC RoamAbout */ + { 0x08, 0x00, 0x6A }, /* AT&T Wavelan (alternate) */ + { 0x00, 0x00, 0xE1 }, /* Hitachi Wavelan */ + { 0x00, 0x60, 0x1D } /* Lucent Wavelan (another one) */ + /* Add your card here and send me the patch ! */ +}; + +/* + * Constants used to convert channels to frequencies + */ + +/* Frequency available in the 2.0 modem, in units of 250 kHz + * (as read in the offset register of the dac area). + * Used to map channel numbers used by `wfreqsel' to frequencies + */ +const short channel_bands[] = { 0x30, 0x58, 0x64, 0x7A, 0x80, 0xA8, + 0xD0, 0xF0, 0xF8, 0x150 }; + +/* Frequencies of the 1.0 modem (fixed frequencies). + * Use to map the PSA `subband' to a frequency + * Note : all frequencies apart from the first one need to be multiplied by 10 + */ +const int fixed_bands[] = { 915e6, 2.425e8, 2.46e8, 2.484e8, 2.4305e8 }; + +/*************************** PC INTERFACE ****************************/ + +/* WaveLAN host interface definitions */ + +#define LCCR(base) (base) /* LAN Controller Command Register */ +#define LCSR(base) (base) /* LAN Controller Status Register */ +#define HACR(base) (base+0x1) /* Host Adapter Command Register */ +#define HASR(base) (base+0x1) /* Host Adapter Status Register */ +#define PIORL(base) (base+0x2) /* Program I/O Register Low */ +#define RPLL(base) (base+0x2) /* Receive Pointer Latched Low */ +#define PIORH(base) (base+0x3) /* Program I/O Register High */ +#define RPLH(base) (base+0x3) /* Receive Pointer Latched High */ +#define PIOP(base) (base+0x4) /* Program I/O Port */ +#define MMR(base) (base+0x6) /* MMI Address Register */ +#define MMD(base) (base+0x7) /* MMI Data Register */ + +/* Host Adaptor Command Register bit definitions */ + +#define HACR_LOF (1 << 3) /* Lock Out Flag, toggle every 250ms */ +#define HACR_PWR_STAT (1 << 4) /* Power State, 1=active, 0=sleep */ +#define HACR_TX_DMA_RESET (1 << 5) /* Reset transmit DMA ptr on high */ +#define HACR_RX_DMA_RESET (1 << 6) /* Reset receive DMA ptr on high */ +#define HACR_ROM_WEN (1 << 7) /* EEPROM write enabled when true */ + +#define HACR_RESET (HACR_TX_DMA_RESET | HACR_RX_DMA_RESET) +#define HACR_DEFAULT (HACR_PWR_STAT) + +/* Host Adapter Status Register bit definitions */ + +#define HASR_MMI_BUSY (1 << 2) /* MMI is busy when true */ +#define HASR_LOF (1 << 3) /* Lock out flag status */ +#define HASR_NO_CLK (1 << 4) /* active when modem not connected */ + +/* Miscellaneous bit definitions */ + +#define PIORH_SEL_TX (1 << 5) /* PIOR points to 0=rx/1=tx buffer */ +#define MMR_MMI_WR (1 << 0) /* Next MMI cycle is 0=read, 1=write */ +#define PIORH_MASK 0x1f /* only low 5 bits are significant */ +#define RPLH_MASK 0x1f /* only low 5 bits are significant */ +#define MMI_ADDR_MASK 0x7e /* Bits 1-6 of MMR are significant */ + +/* Attribute Memory map */ + +#define CIS_ADDR 0x0000 /* Card Information Status Register */ +#define PSA_ADDR 0x0e00 /* Parameter Storage Area address */ +#define EEPROM_ADDR 0x1000 /* EEPROM address (unused ?) */ +#define COR_ADDR 0x4000 /* Configuration Option Register */ + +/* Configuration Option Register bit definitions */ + +#define COR_CONFIG (1 << 0) /* Config Index, 0 when unconfigured */ +#define COR_SW_RESET (1 << 7) /* Software Reset on true */ +#define COR_LEVEL_IRQ (1 << 6) /* Level IRQ */ + +/* Local Memory map */ + +#define RX_BASE 0x0000 /* Receive memory, 8 kB */ +#define TX_BASE 0x2000 /* Transmit memory, 2 kB */ +#define UNUSED_BASE 0x2800 /* Unused, 22 kB */ +#define RX_SIZE (TX_BASE-RX_BASE) /* Size of receive area */ +#define RX_SIZE_SHIFT 6 /* Bits to shift in stop register */ + +#define TRUE 1 +#define FALSE 0 + +#define MOD_ENAL 1 +#define MOD_PROM 2 + +/* Size of a MAC address */ +#define WAVELAN_ADDR_SIZE 6 + +/* Maximum size of Wavelan packet */ +#define WAVELAN_MTU 1500 + +#define MAXDATAZ (6 + 6 + 2 + WAVELAN_MTU) + +/********************** PARAMETER STORAGE AREA **********************/ + +/* + * Parameter Storage Area (PSA). + */ +typedef struct psa_t psa_t; +struct psa_t +{ + /* For the PCMCIA Adapter, locations 0x00-0x0F are unused and fixed at 00 */ + unsigned char psa_io_base_addr_1; /* [0x00] Base address 1 ??? */ + unsigned char psa_io_base_addr_2; /* [0x01] Base address 2 */ + unsigned char psa_io_base_addr_3; /* [0x02] Base address 3 */ + unsigned char psa_io_base_addr_4; /* [0x03] Base address 4 */ + unsigned char psa_rem_boot_addr_1; /* [0x04] Remote Boot Address 1 */ + unsigned char psa_rem_boot_addr_2; /* [0x05] Remote Boot Address 2 */ + unsigned char psa_rem_boot_addr_3; /* [0x06] Remote Boot Address 3 */ + unsigned char psa_holi_params; /* [0x07] HOst Lan Interface (HOLI) Parameters */ + unsigned char psa_int_req_no; /* [0x08] Interrupt Request Line */ + unsigned char psa_unused0[7]; /* [0x09-0x0F] unused */ + + unsigned char psa_univ_mac_addr[WAVELAN_ADDR_SIZE]; /* [0x10-0x15] Universal (factory) MAC Address */ + unsigned char psa_local_mac_addr[WAVELAN_ADDR_SIZE]; /* [0x16-1B] Local MAC Address */ + unsigned char psa_univ_local_sel; /* [0x1C] Universal Local Selection */ +#define PSA_UNIVERSAL 0 /* Universal (factory) */ +#define PSA_LOCAL 1 /* Local */ + unsigned char psa_comp_number; /* [0x1D] Compatability Number: */ +#define PSA_COMP_PC_AT_915 0 /* PC-AT 915 MHz */ +#define PSA_COMP_PC_MC_915 1 /* PC-MC 915 MHz */ +#define PSA_COMP_PC_AT_2400 2 /* PC-AT 2.4 GHz */ +#define PSA_COMP_PC_MC_2400 3 /* PC-MC 2.4 GHz */ +#define PSA_COMP_PCMCIA_915 4 /* PCMCIA 915 MHz or 2.0 */ + unsigned char psa_thr_pre_set; /* [0x1E] Modem Threshold Preset */ + unsigned char psa_feature_select; /* [0x1F] Call code required (1=on) */ +#define PSA_FEATURE_CALL_CODE 0x01 /* Call code required (Japan) */ + unsigned char psa_subband; /* [0x20] Subband */ +#define PSA_SUBBAND_915 0 /* 915 MHz or 2.0 */ +#define PSA_SUBBAND_2425 1 /* 2425 MHz */ +#define PSA_SUBBAND_2460 2 /* 2460 MHz */ +#define PSA_SUBBAND_2484 3 /* 2484 MHz */ +#define PSA_SUBBAND_2430_5 4 /* 2430.5 MHz */ + unsigned char psa_quality_thr; /* [0x21] Modem Quality Threshold */ + unsigned char psa_mod_delay; /* [0x22] Modem Delay ??? (reserved) */ + unsigned char psa_nwid[2]; /* [0x23-0x24] Network ID */ + unsigned char psa_nwid_select; /* [0x25] Network ID Select On Off */ + unsigned char psa_encryption_select; /* [0x26] Encryption On Off */ + unsigned char psa_encryption_key[8]; /* [0x27-0x2E] Encryption Key */ + unsigned char psa_databus_width; /* [0x2F] AT bus width select 8/16 */ + unsigned char psa_call_code[8]; /* [0x30-0x37] (Japan) Call Code */ + unsigned char psa_nwid_prefix[2]; /* [0x38-0x39] Roaming domain */ + unsigned char psa_reserved[2]; /* [0x3A-0x3B] Reserved - fixed 00 */ + unsigned char psa_conf_status; /* [0x3C] Conf Status, bit 0=1:config*/ + unsigned char psa_crc[2]; /* [0x3D] CRC-16 over PSA */ + unsigned char psa_crc_status; /* [0x3F] CRC Valid Flag */ +}; + +/* Size for structure checking (if padding is correct) */ +#define PSA_SIZE 64 + +/* Calculate offset of a field in the above structure + * Warning : only even addresses are used */ +#define psaoff(p,f) ((unsigned short) ((void *)(&((psa_t *) ((void *) NULL + (p)))->f) - (void *) NULL)) + +/******************** MODEM MANAGEMENT INTERFACE ********************/ + +/* + * Modem Management Controller (MMC) write structure. + */ +typedef struct mmw_t mmw_t; +struct mmw_t +{ + unsigned char mmw_encr_key[8]; /* encryption key */ + unsigned char mmw_encr_enable; /* enable/disable encryption */ +#define MMW_ENCR_ENABLE_MODE 0x02 /* Mode of security option */ +#define MMW_ENCR_ENABLE_EN 0x01 /* Enable security option */ + unsigned char mmw_unused0[1]; /* unused */ + unsigned char mmw_des_io_invert; /* Encryption option */ +#define MMW_DES_IO_INVERT_RES 0x0F /* Reserved */ +#define MMW_DES_IO_INVERT_CTRL 0xF0 /* Control ??? (set to 0) */ + unsigned char mmw_unused1[5]; /* unused */ + unsigned char mmw_loopt_sel; /* looptest selection */ +#define MMW_LOOPT_SEL_DIS_NWID 0x40 /* disable NWID filtering */ +#define MMW_LOOPT_SEL_INT 0x20 /* activate Attention Request */ +#define MMW_LOOPT_SEL_LS 0x10 /* looptest w/o collision avoidance */ +#define MMW_LOOPT_SEL_LT3A 0x08 /* looptest 3a */ +#define MMW_LOOPT_SEL_LT3B 0x04 /* looptest 3b */ +#define MMW_LOOPT_SEL_LT3C 0x02 /* looptest 3c */ +#define MMW_LOOPT_SEL_LT3D 0x01 /* looptest 3d */ + unsigned char mmw_jabber_enable; /* jabber timer enable */ + /* Abort transmissions > 200 ms */ + unsigned char mmw_freeze; /* freeze / unfreeeze signal level */ + /* 0 : signal level & qual updated for every new message, 1 : frozen */ + unsigned char mmw_anten_sel; /* antenna selection */ +#define MMW_ANTEN_SEL_SEL 0x01 /* direct antenna selection */ +#define MMW_ANTEN_SEL_ALG_EN 0x02 /* antenna selection algo. enable */ + unsigned char mmw_ifs; /* inter frame spacing */ + /* min time between transmission in bit periods (.5 us) - bit 0 ignored */ + unsigned char mmw_mod_delay; /* modem delay (synchro) */ + unsigned char mmw_jam_time; /* jamming time (after collision) */ + unsigned char mmw_unused2[1]; /* unused */ + unsigned char mmw_thr_pre_set; /* level threshold preset */ + /* Discard all packet with signal < this value (4) */ + unsigned char mmw_decay_prm; /* decay parameters */ + unsigned char mmw_decay_updat_prm; /* decay update parameterz */ + unsigned char mmw_quality_thr; /* quality (z-quotient) threshold */ + /* Discard all packet with quality < this value (3) */ + unsigned char mmw_netw_id_l; /* NWID low order byte */ + unsigned char mmw_netw_id_h; /* NWID high order byte */ + /* Network ID or Domain : create virtual net on the air */ + + /* 2.0 Hardware extension - frequency selection support */ + unsigned char mmw_mode_select; /* for analog tests (set to 0) */ + unsigned char mmw_unused3[1]; /* unused */ + unsigned char mmw_fee_ctrl; /* frequency eeprom control */ +#define MMW_FEE_CTRL_PRE 0x10 /* Enable protected instructions */ +#define MMW_FEE_CTRL_DWLD 0x08 /* Download eeprom to mmc */ +#define MMW_FEE_CTRL_CMD 0x07 /* EEprom commands : */ +#define MMW_FEE_CTRL_READ 0x06 /* Read */ +#define MMW_FEE_CTRL_WREN 0x04 /* Write enable */ +#define MMW_FEE_CTRL_WRITE 0x05 /* Write data to address */ +#define MMW_FEE_CTRL_WRALL 0x04 /* Write data to all addresses */ +#define MMW_FEE_CTRL_WDS 0x04 /* Write disable */ +#define MMW_FEE_CTRL_PRREAD 0x16 /* Read addr from protect register */ +#define MMW_FEE_CTRL_PREN 0x14 /* Protect register enable */ +#define MMW_FEE_CTRL_PRCLEAR 0x17 /* Unprotect all registers */ +#define MMW_FEE_CTRL_PRWRITE 0x15 /* Write addr in protect register */ +#define MMW_FEE_CTRL_PRDS 0x14 /* Protect register disable */ + /* Never issue this command (PRDS) : it's irreversible !!! */ + + unsigned char mmw_fee_addr; /* EEprom address */ +#define MMW_FEE_ADDR_CHANNEL 0xF0 /* Select the channel */ +#define MMW_FEE_ADDR_OFFSET 0x0F /* Offset in channel data */ +#define MMW_FEE_ADDR_EN 0xC0 /* FEE_CTRL enable operations */ +#define MMW_FEE_ADDR_DS 0x00 /* FEE_CTRL disable operations */ +#define MMW_FEE_ADDR_ALL 0x40 /* FEE_CTRL all operations */ +#define MMW_FEE_ADDR_CLEAR 0xFF /* FEE_CTRL clear operations */ + + unsigned char mmw_fee_data_l; /* Write data to EEprom */ + unsigned char mmw_fee_data_h; /* high octet */ + unsigned char mmw_ext_ant; /* Setting for external antenna */ +#define MMW_EXT_ANT_EXTANT 0x01 /* Select external antenna */ +#define MMW_EXT_ANT_POL 0x02 /* Polarity of the antenna */ +#define MMW_EXT_ANT_INTERNAL 0x00 /* Internal antenna */ +#define MMW_EXT_ANT_EXTERNAL 0x03 /* External antenna */ +#define MMW_EXT_ANT_IQ_TEST 0x1C /* IQ test pattern (set to 0) */ +}; + +/* Size for structure checking (if padding is correct) */ +#define MMW_SIZE 37 + +/* Calculate offset of a field in the above structure */ +#define mmwoff(p,f) (unsigned short)((void *)(&((mmw_t *)((void *)0 + (p)))->f) - (void *)0) + +/* + * Modem Management Controller (MMC) read structure. + */ +typedef struct mmr_t mmr_t; +struct mmr_t +{ + unsigned char mmr_unused0[8]; /* unused */ + unsigned char mmr_des_status; /* encryption status */ + unsigned char mmr_des_avail; /* encryption available (0x55 read) */ +#define MMR_DES_AVAIL_DES 0x55 /* DES available */ +#define MMR_DES_AVAIL_AES 0x33 /* AES (AT&T) available */ + unsigned char mmr_des_io_invert; /* des I/O invert register */ + unsigned char mmr_unused1[5]; /* unused */ + unsigned char mmr_dce_status; /* DCE status */ +#define MMR_DCE_STATUS_RX_BUSY 0x01 /* receiver busy */ +#define MMR_DCE_STATUS_LOOPT_IND 0x02 /* loop test indicated */ +#define MMR_DCE_STATUS_TX_BUSY 0x04 /* transmitter on */ +#define MMR_DCE_STATUS_JBR_EXPIRED 0x08 /* jabber timer expired */ +#define MMR_DCE_STATUS 0x0F /* mask to get the bits */ + unsigned char mmr_dsp_id; /* DSP id (AA = Daedalus rev A) */ + unsigned char mmr_unused2[2]; /* unused */ + unsigned char mmr_correct_nwid_l; /* # of correct NWID's rxd (low) */ + unsigned char mmr_correct_nwid_h; /* # of correct NWID's rxd (high) */ + /* Warning : Read high order octet first !!! */ + unsigned char mmr_wrong_nwid_l; /* # of wrong NWID's rxd (low) */ + unsigned char mmr_wrong_nwid_h; /* # of wrong NWID's rxd (high) */ + unsigned char mmr_thr_pre_set; /* level threshold preset */ +#define MMR_THR_PRE_SET 0x3F /* level threshold preset */ +#define MMR_THR_PRE_SET_CUR 0x80 /* Current signal above it */ + unsigned char mmr_signal_lvl; /* signal level */ +#define MMR_SIGNAL_LVL 0x3F /* signal level */ +#define MMR_SIGNAL_LVL_VALID 0x80 /* Updated since last read */ + unsigned char mmr_silence_lvl; /* silence level (noise) */ +#define MMR_SILENCE_LVL 0x3F /* silence level */ +#define MMR_SILENCE_LVL_VALID 0x80 /* Updated since last read */ + unsigned char mmr_sgnl_qual; /* signal quality */ +#define MMR_SGNL_QUAL 0x0F /* signal quality */ +#define MMR_SGNL_QUAL_ANT 0x80 /* current antenna used */ + unsigned char mmr_netw_id_l; /* NWID low order byte ??? */ + unsigned char mmr_unused3[3]; /* unused */ + + /* 2.0 Hardware extension - frequency selection support */ + unsigned char mmr_fee_status; /* Status of frequency eeprom */ +#define MMR_FEE_STATUS_ID 0xF0 /* Modem revision id */ +#define MMR_FEE_STATUS_DWLD 0x08 /* Download in progress */ +#define MMR_FEE_STATUS_BUSY 0x04 /* EEprom busy */ + unsigned char mmr_unused4[1]; /* unused */ + unsigned char mmr_fee_data_l; /* Read data from eeprom (low) */ + unsigned char mmr_fee_data_h; /* Read data from eeprom (high) */ +}; + +/* Size for structure checking (if padding is correct) */ +#define MMR_SIZE 36 + +/* Calculate offset of a field in the above structure */ +#define mmroff(p,f) (unsigned short)((void *)(&((mmr_t *)((void *)0 + (p)))->f) - (void *)0) + +/* Make the two above structures one */ +typedef union mm_t +{ + struct mmw_t w; /* Write to the mmc */ + struct mmr_t r; /* Read from the mmc */ +} mm_t; + +#endif /* _WAVELAN_H */ diff --git a/drivers/net/pcmcia/wavelan_cs.c b/drivers/net/pcmcia/wavelan_cs.c new file mode 100644 index 000000000..5e9c9e6e2 --- /dev/null +++ b/drivers/net/pcmcia/wavelan_cs.c @@ -0,0 +1,4856 @@ +/* + * Wavelan Pcmcia driver + * + * Jean II - HPLB '96 + * + * Reorganisation and extension of the driver. + * Original copyright follow. See wavelan_cs.h for details. + * + * This code is derived from Anthony D. Joseph's code and all the changes here + * are also under the original copyright below. + * + * This code supports version 2.00 of WaveLAN/PCMCIA cards (2.4GHz), and + * can work on Linux 2.0.36 with support of David Hinds' PCMCIA Card Services + * + * Joe Finney (joe@comp.lancs.ac.uk) at Lancaster University in UK added + * critical code in the routine to initialize the Modem Management Controller. + * + * Thanks to Alan Cox and Bruce Janson for their advice. + * + * -- Yunzhou Li (scip4166@nus.sg) + * +#ifdef WAVELAN_ROAMING + * Roaming support added 07/22/98 by Justin Seger (jseger@media.mit.edu) + * based on patch by Joe Finney from Lancaster University. +#endif :-) + * + * Lucent (formerly AT&T GIS, formerly NCR) WaveLAN PCMCIA card: An + * Ethernet-like radio transceiver controlled by an Intel 82593 coprocessor. + * + * A non-shared memory PCMCIA ethernet driver for linux + * + * ISA version modified to support PCMCIA by Anthony Joseph (adj@lcs.mit.edu) + * + * + * Joseph O'Sullivan & John Langford (josullvn@cs.cmu.edu & jcl@cs.cmu.edu) + * + * Apr 2 '98 made changes to bring the i82593 control/int handling in line + * with offical specs... + * + **************************************************************************** + * Copyright 1995 + * Anthony D. Joseph + * Massachusetts Institute of Technology + * + * Permission to use, copy, modify, and distribute this program + * for any purpose and without fee is hereby granted, provided + * that this copyright and permission notice appear on all copies + * and supporting documentation, the name of M.I.T. not be used + * in advertising or publicity pertaining to distribution of the + * program without specific prior permission, and notice be given + * in supporting documentation that copying and distribution is + * by permission of M.I.T. M.I.T. makes no representations about + * the suitability of this software for any purpose. It is pro- + * vided "as is" without express or implied warranty. + **************************************************************************** + * + */ + +#include "wavelan_cs.h" /* Private header */ + +/************************* MISC SUBROUTINES **************************/ +/* + * Subroutines which won't fit in one of the following category + * (wavelan modem or i82593) + */ + +/*------------------------------------------------------------------*/ +/* + * Wrapper for disabling interrupts. + */ +static inline unsigned long +wv_splhi(void) +{ + unsigned long flags; + + save_flags(flags); + cli(); + + return(flags); +} + +/*------------------------------------------------------------------*/ +/* + * Wrapper for re-enabling interrupts. + */ +static inline void +wv_splx(unsigned long flags) +{ + restore_flags(flags); +} + +/*------------------------------------------------------------------*/ +/* + * Wrapper for reporting error to cardservices + */ +static void cs_error(client_handle_t handle, int func, int ret) +{ + error_info_t err = { func, ret }; + CardServices(ReportError, handle, &err); +} + +#ifdef STRUCT_CHECK +/*------------------------------------------------------------------*/ +/* + * Sanity routine to verify the sizes of the various WaveLAN interface + * structures. + */ +static char * +wv_structuct_check(void) +{ +#define SC(t,s,n) if (sizeof(t) != s) return(n); + + SC(psa_t, PSA_SIZE, "psa_t"); + SC(mmw_t, MMW_SIZE, "mmw_t"); + SC(mmr_t, MMR_SIZE, "mmr_t"); + +#undef SC + + return((char *) NULL); +} /* wv_structuct_check */ +#endif /* STRUCT_CHECK */ + +/******************* MODEM MANAGEMENT SUBROUTINES *******************/ +/* + * Usefull subroutines to manage the modem of the wavelan + */ + +/*------------------------------------------------------------------*/ +/* + * Read from card's Host Adaptor Status Register. + */ +static inline u_char +hasr_read(u_long base) +{ + return(inb(HASR(base))); +} /* hasr_read */ + +/*------------------------------------------------------------------*/ +/* + * Write to card's Host Adapter Command Register. + */ +static inline void +hacr_write(u_long base, + u_char hacr) +{ + outb(hacr, HACR(base)); +} /* hacr_write */ + +/*------------------------------------------------------------------*/ +/* + * Write to card's Host Adapter Command Register. Include a delay for + * those times when it is needed. + */ +static inline void +hacr_write_slow(u_long base, + u_char hacr) +{ + hacr_write(base, hacr); + /* delay might only be needed sometimes */ + udelay(1000L); +} /* hacr_write_slow */ + +/*------------------------------------------------------------------*/ +/* + * Read the Parameter Storage Area from the WaveLAN card's memory + */ +static void +psa_read(device * dev, + int o, /* offset in PSA */ + u_char * b, /* buffer to fill */ + int n) /* size to read */ +{ + u_char * ptr = ((u_char *)dev->mem_start) + PSA_ADDR + (o << 1); + + while(n-- > 0) + { + *b++ = readb(ptr); + /* Due to a lack of address decode pins, the WaveLAN PCMCIA card + * only supports reading even memory addresses. That means the + * increment here MUST be two. + * Because of that, we can't use memcpy_fromio()... + */ + ptr += 2; + } +} /* psa_read */ + +/*------------------------------------------------------------------*/ +/* + * Write the Paramter Storage Area to the WaveLAN card's memory + */ +static void +psa_write(device * dev, + int o, /* Offset in psa */ + u_char * b, /* Buffer in memory */ + int n) /* Length of buffer */ +{ + u_char * ptr = ((u_char *) dev->mem_start) + PSA_ADDR + (o << 1); + int count = 0; + ioaddr_t base = dev->base_addr; + /* As there seem to have no flag PSA_BUSY as in the ISA model, we are + * oblige to verify this address to know when the PSA is ready... */ + volatile u_char * verify = ((u_char *) dev->mem_start) + PSA_ADDR + + (psaoff(0, psa_comp_number) << 1); + + /* Authorize writting to PSA */ + hacr_write(base, HACR_PWR_STAT | HACR_ROM_WEN); + + while(n-- > 0) + { + /* write to PSA */ + writeb(*b++, ptr); + ptr += 2; + + /* I don't have the spec, so I don't know what the correct + * sequence to write is. This hack seem to work for me... */ + count = 0; + while((readb(verify) != PSA_COMP_PCMCIA_915) && (count++ < 100)) + udelay(1000); + } + + /* Put the host interface back in standard state */ + hacr_write(base, HACR_DEFAULT); +} /* psa_write */ + +#ifdef SET_PSA_CRC +/*------------------------------------------------------------------*/ +/* + * Calculate the PSA CRC + * Thanks to Valster, Nico <NVALSTER@wcnd.nl.lucent.com> for the code + * NOTE: By specifying a length including the CRC position the + * returned value should be zero. (i.e. a correct checksum in the PSA) + * + * The Windows drivers don't use the CRC, but the AP and the PtP tool + * depend on it. + */ +static u_short +psa_crc(unsigned char * psa, /* The PSA */ + int size) /* Number of short for CRC */ +{ + int byte_cnt; /* Loop on the PSA */ + u_short crc_bytes = 0; /* Data in the PSA */ + int bit_cnt; /* Loop on the bits of the short */ + + for(byte_cnt = 0; byte_cnt < size; byte_cnt++ ) + { + crc_bytes ^= psa[byte_cnt]; /* Its an xor */ + + for(bit_cnt = 1; bit_cnt < 9; bit_cnt++ ) + { + if(crc_bytes & 0x0001) + crc_bytes = (crc_bytes >> 1) ^ 0xA001; + else + crc_bytes >>= 1 ; + } + } + + return crc_bytes; +} /* psa_crc */ +#endif /* SET_PSA_CRC */ + +/*------------------------------------------------------------------*/ +/* + * update the checksum field in the Wavelan's PSA + */ +static void +update_psa_checksum(device * dev) +{ +#ifdef SET_PSA_CRC + psa_t psa; + u_short crc; + + /* read the parameter storage area */ + psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa)); + + /* update the checksum */ + crc = psa_crc((unsigned char *) &psa, + sizeof(psa) - sizeof(psa.psa_crc[0]) - sizeof(psa.psa_crc[1]) + - sizeof(psa.psa_crc_status)); + + psa.psa_crc[0] = crc & 0xFF; + psa.psa_crc[1] = (crc & 0xFF00) >> 8; + + /* Write it ! */ + psa_write(dev, (char *)&psa.psa_crc - (char *)&psa, + (unsigned char *)&psa.psa_crc, 2); + +#ifdef DEBUG_IOCTL_INFO + printk (KERN_DEBUG "%s: update_psa_checksum(): crc = 0x%02x%02x\n", + dev->name, psa.psa_crc[0], psa.psa_crc[1]); + + /* Check again (luxury !) */ + crc = psa_crc((unsigned char *) &psa, + sizeof(psa) - sizeof(psa.psa_crc_status)); + + if(crc != 0) + printk(KERN_WARNING "%s: update_psa_checksum(): CRC does not agree with PSA data (even after recalculating)\n", dev->name); +#endif /* DEBUG_IOCTL_INFO */ +#endif /* SET_PSA_CRC */ +} /* update_psa_checksum */ + +/*------------------------------------------------------------------*/ +/* + * Write 1 byte to the MMC. + */ +static inline void +mmc_out(u_long base, + u_short o, + u_char d) +{ + /* Wait for MMC to go idle */ + while(inb(HASR(base)) & HASR_MMI_BUSY) + ; + + outb((u_char)((o << 1) | MMR_MMI_WR), MMR(base)); + outb(d, MMD(base)); +} + +/*------------------------------------------------------------------*/ +/* + * Routine to write bytes to the Modem Management Controller. + * We start by the end because it is the way it should be ! + */ +static inline void +mmc_write(u_long base, + u_char o, + u_char * b, + int n) +{ + o += n; + b += n; + + while(n-- > 0 ) + mmc_out(base, --o, *(--b)); +} /* mmc_write */ + +/*------------------------------------------------------------------*/ +/* + * Read 1 byte from the MMC. + * Optimised version for 1 byte, avoid using memory... + */ +static inline u_char +mmc_in(u_long base, + u_short o) +{ + while(inb(HASR(base)) & HASR_MMI_BUSY) + ; + outb(o << 1, MMR(base)); /* Set the read address */ + + outb(0, MMD(base)); /* Required dummy write */ + + while(inb(HASR(base)) & HASR_MMI_BUSY) + ; + return (u_char) (inb(MMD(base))); /* Now do the actual read */ +} + +/*------------------------------------------------------------------*/ +/* + * Routine to read bytes from the Modem Management Controller. + * The implementation is complicated by a lack of address lines, + * which prevents decoding of the low-order bit. + * (code has just been moved in the above function) + * We start by the end because it is the way it should be ! + */ +static inline void +mmc_read(u_long base, + u_char o, + u_char * b, + int n) +{ + o += n; + b += n; + + while(n-- > 0) + *(--b) = mmc_in(base, --o); +} /* mmc_read */ + +/*------------------------------------------------------------------*/ +/* + * Get the type of encryption available... + */ +static inline int +mmc_encr(u_long base) /* i/o port of the card */ +{ + int temp; + + temp = mmc_in(base, mmroff(0, mmr_des_avail)); + if((temp != MMR_DES_AVAIL_DES) && (temp != MMR_DES_AVAIL_AES)) + return 0; + else + return temp; +} + +/*------------------------------------------------------------------*/ +/* + * Wait for the frequency EEprom to complete a command... + * I hope this one will be optimally inlined... + */ +static inline void +fee_wait(u_long base, /* i/o port of the card */ + int delay, /* Base delay to wait for */ + int number) /* Number of time to wait */ +{ + int count = 0; /* Wait only a limited time */ + + while((count++ < number) && + (mmc_in(base, mmroff(0, mmr_fee_status)) & MMR_FEE_STATUS_BUSY)) + udelay(delay); +} + +/*------------------------------------------------------------------*/ +/* + * Read bytes from the Frequency EEprom (frequency select cards). + */ +static void +fee_read(u_long base, /* i/o port of the card */ + u_short o, /* destination offset */ + u_short * b, /* data buffer */ + int n) /* number of registers */ +{ + b += n; /* Position at the end of the area */ + + /* Write the address */ + mmc_out(base, mmwoff(0, mmw_fee_addr), o + n - 1); + + /* Loop on all buffer */ + while(n-- > 0) + { + /* Write the read command */ + mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_READ); + + /* Wait until EEprom is ready (should be quick !) */ + fee_wait(base, 10, 100); + + /* Read the value */ + *--b = ((mmc_in(base, mmroff(0, mmr_fee_data_h)) << 8) | + mmc_in(base, mmroff(0, mmr_fee_data_l))); + } +} + +#ifdef WIRELESS_EXT /* If wireless extension exist in the kernel */ + +/*------------------------------------------------------------------*/ +/* + * Write bytes from the Frequency EEprom (frequency select cards). + * This is a bit complicated, because the frequency eeprom has to + * be unprotected and the write enabled. + * Jean II + */ +static void +fee_write(u_long base, /* i/o port of the card */ + u_short o, /* destination offset */ + u_short * b, /* data buffer */ + int n) /* number of registers */ +{ + b += n; /* Position at the end of the area */ + +#ifdef EEPROM_IS_PROTECTED /* disabled */ +#ifdef DOESNT_SEEM_TO_WORK /* disabled */ + /* Ask to read the protected register */ + mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRREAD); + + fee_wait(base, 10, 100); + + /* Read the protected register */ + printk("Protected 2 : %02X-%02X\n", + mmc_in(base, mmroff(0, mmr_fee_data_h)), + mmc_in(base, mmroff(0, mmr_fee_data_l))); +#endif /* DOESNT_SEEM_TO_WORK */ + + /* Enable protected register */ + mmc_out(base, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN); + mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PREN); + + fee_wait(base, 10, 100); + + /* Unprotect area */ + mmc_out(base, mmwoff(0, mmw_fee_addr), o + n); + mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE); +#ifdef DOESNT_SEEM_TO_WORK /* disabled */ + /* Or use : */ + mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRCLEAR); +#endif /* DOESNT_SEEM_TO_WORK */ + + fee_wait(base, 10, 100); +#endif /* EEPROM_IS_PROTECTED */ + + /* Write enable */ + mmc_out(base, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN); + mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WREN); + + fee_wait(base, 10, 100); + + /* Write the EEprom address */ + mmc_out(base, mmwoff(0, mmw_fee_addr), o + n - 1); + + /* Loop on all buffer */ + while(n-- > 0) + { + /* Write the value */ + mmc_out(base, mmwoff(0, mmw_fee_data_h), (*--b) >> 8); + mmc_out(base, mmwoff(0, mmw_fee_data_l), *b & 0xFF); + + /* Write the write command */ + mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WRITE); + + /* Wavelan doc says : wait at least 10 ms for EEBUSY = 0 */ + udelay(10000); + fee_wait(base, 10, 100); + } + + /* Write disable */ + mmc_out(base, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_DS); + mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WDS); + + fee_wait(base, 10, 100); + +#ifdef EEPROM_IS_PROTECTED /* disabled */ + /* Reprotect EEprom */ + mmc_out(base, mmwoff(0, mmw_fee_addr), 0x00); + mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE); + + fee_wait(base, 10, 100); +#endif /* EEPROM_IS_PROTECTED */ +} +#endif /* WIRELESS_EXT */ + +/******************* WaveLAN Roaming routines... ********************/ + +#ifdef WAVELAN_ROAMING /* Conditional compile, see wavelan_cs.h */ + +unsigned char WAVELAN_BEACON_ADDRESS[]= {0x09,0x00,0x0e,0x20,0x03,0x00}; + +void wv_roam_init(struct net_device *dev) +{ + net_local *lp= (net_local *)dev->priv; + + /* Do not remove this unless you have a good reason */ + printk(KERN_NOTICE "%s: Warning, you have enabled roaming on" + " device %s !\n", dev->name, dev->name); + printk(KERN_NOTICE "Roaming is currently an experimental unsuported feature" + " of the Wavelan driver.\n"); + printk(KERN_NOTICE "It may work, but may also make the driver behave in" + " erratic ways or crash.\n"); + + lp->wavepoint_table.head=NULL; /* Initialise WavePoint table */ + lp->wavepoint_table.num_wavepoints=0; + lp->wavepoint_table.locked=0; + lp->curr_point=NULL; /* No default WavePoint */ + lp->cell_search=0; + + lp->cell_timer.data=(int)lp; /* Start cell expiry timer */ + lp->cell_timer.function=wl_cell_expiry; + lp->cell_timer.expires=jiffies+CELL_TIMEOUT; + add_timer(&lp->cell_timer); + + wv_nwid_filter(NWID_PROMISC,lp) ; /* Enter NWID promiscuous mode */ + /* to build up a good WavePoint */ + /* table... */ + printk(KERN_DEBUG "WaveLAN: Roaming enabled on device %s\n",dev->name); +} + +void wv_roam_cleanup(struct net_device *dev) +{ + wavepoint_history *ptr,*old_ptr; + net_local *lp= (net_local *)dev->priv; + + printk(KERN_DEBUG "WaveLAN: Roaming Disabled on device %s\n",dev->name); + + /* Fixme : maybe we should check that the timer exist before deleting it */ + del_timer(&lp->cell_timer); /* Remove cell expiry timer */ + ptr=lp->wavepoint_table.head; /* Clear device's WavePoint table */ + while(ptr!=NULL) + { + old_ptr=ptr; + ptr=ptr->next; + wl_del_wavepoint(old_ptr,lp); + } +} + +/* Enable/Disable NWID promiscuous mode on a given device */ +void wv_nwid_filter(unsigned char mode, net_local *lp) +{ + mm_t m; + unsigned long x; + +#ifdef WAVELAN_ROAMING_DEBUG + printk(KERN_DEBUG "WaveLAN: NWID promisc %s, device %s\n",(mode==NWID_PROMISC) ? "on" : "off", lp->dev->name); +#endif + + /* Disable interrupts & save flags */ + x = wv_splhi(); + + m.w.mmw_loopt_sel = (mode==NWID_PROMISC) ? MMW_LOOPT_SEL_DIS_NWID : 0x00; + mmc_write(lp->dev->base_addr, (char *)&m.w.mmw_loopt_sel - (char *)&m, (unsigned char *)&m.w.mmw_loopt_sel, 1); + + /* ReEnable interrupts & restore flags */ + wv_splx(x); + + if(mode==NWID_PROMISC) + lp->cell_search=1; + else + lp->cell_search=0; +} + +/* Find a record in the WavePoint table matching a given NWID */ +wavepoint_history *wl_roam_check(unsigned short nwid, net_local *lp) +{ + wavepoint_history *ptr=lp->wavepoint_table.head; + + while(ptr!=NULL){ + if(ptr->nwid==nwid) + return ptr; + ptr=ptr->next; + } + return NULL; +} + +/* Create a new wavepoint table entry */ +wavepoint_history *wl_new_wavepoint(unsigned short nwid, unsigned char seq, net_local* lp) +{ + wavepoint_history *new_wavepoint; + +#ifdef WAVELAN_ROAMING_DEBUG + printk(KERN_DEBUG "WaveLAN: New Wavepoint, NWID:%.4X\n",nwid); +#endif + + if(lp->wavepoint_table.num_wavepoints==MAX_WAVEPOINTS) + return NULL; + + new_wavepoint=(wavepoint_history *) kmalloc(sizeof(wavepoint_history),GFP_ATOMIC); + if(new_wavepoint==NULL) + return NULL; + + new_wavepoint->nwid=nwid; /* New WavePoints NWID */ + new_wavepoint->average_fast=0; /* Running Averages..*/ + new_wavepoint->average_slow=0; + new_wavepoint->qualptr=0; /* Start of ringbuffer */ + new_wavepoint->last_seq=seq-1; /* Last sequence no.seen */ + memset(new_wavepoint->sigqual,0,WAVEPOINT_HISTORY);/* Empty ringbuffer */ + + new_wavepoint->next=lp->wavepoint_table.head;/* Add to wavepoint table */ + new_wavepoint->prev=NULL; + + if(lp->wavepoint_table.head!=NULL) + lp->wavepoint_table.head->prev=new_wavepoint; + + lp->wavepoint_table.head=new_wavepoint; + + lp->wavepoint_table.num_wavepoints++; /* no. of visible wavepoints */ + + return new_wavepoint; +} + +/* Remove a wavepoint entry from WavePoint table */ +void wl_del_wavepoint(wavepoint_history *wavepoint, struct net_local *lp) +{ + if(wavepoint==NULL) + return; + + if(lp->curr_point==wavepoint) + lp->curr_point=NULL; + + if(wavepoint->prev!=NULL) + wavepoint->prev->next=wavepoint->next; + + if(wavepoint->next!=NULL) + wavepoint->next->prev=wavepoint->prev; + + if(lp->wavepoint_table.head==wavepoint) + lp->wavepoint_table.head=wavepoint->next; + + lp->wavepoint_table.num_wavepoints--; + kfree(wavepoint); +} + +/* Timer callback function - checks WavePoint table for stale entries */ +void wl_cell_expiry(unsigned long data) +{ + net_local *lp=(net_local *)data; + wavepoint_history *wavepoint=lp->wavepoint_table.head,*old_point; + +#if WAVELAN_ROAMING_DEBUG > 1 + printk(KERN_DEBUG "WaveLAN: Wavepoint timeout, dev %s\n",lp->dev->name); +#endif + + if(lp->wavepoint_table.locked) + { +#if WAVELAN_ROAMING_DEBUG > 1 + printk(KERN_DEBUG "WaveLAN: Wavepoint table locked...\n"); +#endif + + lp->cell_timer.expires=jiffies+1; /* If table in use, come back later */ + add_timer(&lp->cell_timer); + return; + } + + while(wavepoint!=NULL) + { + if(wavepoint->last_seen < jiffies-CELL_TIMEOUT) + { +#ifdef WAVELAN_ROAMING_DEBUG + printk(KERN_DEBUG "WaveLAN: Bye bye %.4X\n",wavepoint->nwid); +#endif + + old_point=wavepoint; + wavepoint=wavepoint->next; + wl_del_wavepoint(old_point,lp); + } + else + wavepoint=wavepoint->next; + } + lp->cell_timer.expires=jiffies+CELL_TIMEOUT; + add_timer(&lp->cell_timer); +} + +/* Update SNR history of a wavepoint */ +void wl_update_history(wavepoint_history *wavepoint, unsigned char sigqual, unsigned char seq) +{ + int i=0,num_missed=0,ptr=0; + int average_fast=0,average_slow=0; + + num_missed=(seq-wavepoint->last_seq)%WAVEPOINT_HISTORY;/* Have we missed + any beacons? */ + if(num_missed) + for(i=0;i<num_missed;i++) + { + wavepoint->sigqual[wavepoint->qualptr++]=0; /* If so, enter them as 0's */ + wavepoint->qualptr %=WAVEPOINT_HISTORY; /* in the ringbuffer. */ + } + wavepoint->last_seen=jiffies; /* Add beacon to history */ + wavepoint->last_seq=seq; + wavepoint->sigqual[wavepoint->qualptr++]=sigqual; + wavepoint->qualptr %=WAVEPOINT_HISTORY; + ptr=(wavepoint->qualptr-WAVEPOINT_FAST_HISTORY+WAVEPOINT_HISTORY)%WAVEPOINT_HISTORY; + + for(i=0;i<WAVEPOINT_FAST_HISTORY;i++) /* Update running averages */ + { + average_fast+=wavepoint->sigqual[ptr++]; + ptr %=WAVEPOINT_HISTORY; + } + + average_slow=average_fast; + for(i=WAVEPOINT_FAST_HISTORY;i<WAVEPOINT_HISTORY;i++) + { + average_slow+=wavepoint->sigqual[ptr++]; + ptr %=WAVEPOINT_HISTORY; + } + + wavepoint->average_fast=average_fast/WAVEPOINT_FAST_HISTORY; + wavepoint->average_slow=average_slow/WAVEPOINT_HISTORY; +} + +/* Perform a handover to a new WavePoint */ +void wv_roam_handover(wavepoint_history *wavepoint, net_local *lp) +{ + ioaddr_t base = lp->dev->base_addr; + mm_t m; + unsigned long x; + + if(wavepoint==lp->curr_point) /* Sanity check... */ + { + wv_nwid_filter(!NWID_PROMISC,lp); + return; + } + +#ifdef WAVELAN_ROAMING_DEBUG + printk(KERN_DEBUG "WaveLAN: Doing handover to %.4X, dev %s\n",wavepoint->nwid,lp->dev->name); +#endif + + /* Disable interrupts & save flags */ + x = wv_splhi(); + + m.w.mmw_netw_id_l = wavepoint->nwid & 0xFF; + m.w.mmw_netw_id_h = (wavepoint->nwid & 0xFF00) >> 8; + + mmc_write(base, (char *)&m.w.mmw_netw_id_l - (char *)&m, (unsigned char *)&m.w.mmw_netw_id_l, 2); + + /* ReEnable interrupts & restore flags */ + wv_splx(x); + + wv_nwid_filter(!NWID_PROMISC,lp); + lp->curr_point=wavepoint; +} + +/* Called when a WavePoint beacon is received */ +static inline void wl_roam_gather(device * dev, + u_char * hdr, /* Beacon header */ + u_char * stats) /* SNR, Signal quality + of packet */ +{ + wavepoint_beacon *beacon= (wavepoint_beacon *)hdr; /* Rcvd. Beacon */ + unsigned short nwid=ntohs(beacon->nwid); + unsigned short sigqual=stats[2] & MMR_SGNL_QUAL; /* SNR of beacon */ + wavepoint_history *wavepoint=NULL; /* WavePoint table entry */ + net_local *lp=(net_local *)dev->priv; /* Device info */ + +#if WAVELAN_ROAMING_DEBUG > 1 + printk(KERN_DEBUG "WaveLAN: beacon, dev %s:\n",dev->name); + printk(KERN_DEBUG "Domain: %.4X NWID: %.4X SigQual=%d\n",ntohs(beacon->domain_id),nwid,sigqual); +#endif + + lp->wavepoint_table.locked=1; /* <Mutex> */ + + wavepoint=wl_roam_check(nwid,lp); /* Find WavePoint table entry */ + if(wavepoint==NULL) /* If no entry, Create a new one... */ + { + wavepoint=wl_new_wavepoint(nwid,beacon->seq,lp); + if(wavepoint==NULL) + goto out; + } + if(lp->curr_point==NULL) /* If this is the only WavePoint, */ + wv_roam_handover(wavepoint, lp); /* Jump on it! */ + + wl_update_history(wavepoint, sigqual, beacon->seq); /* Update SNR history + stats. */ + + if(lp->curr_point->average_slow < SEARCH_THRESH_LOW) /* If our current */ + if(!lp->cell_search) /* WavePoint is getting faint, */ + wv_nwid_filter(NWID_PROMISC,lp); /* start looking for a new one */ + + if(wavepoint->average_slow > + lp->curr_point->average_slow + WAVELAN_ROAMING_DELTA) + wv_roam_handover(wavepoint, lp); /* Handover to a better WavePoint */ + + if(lp->curr_point->average_slow > SEARCH_THRESH_HIGH) /* If our SNR is */ + if(lp->cell_search) /* getting better, drop out of cell search mode */ + wv_nwid_filter(!NWID_PROMISC,lp); + +out: + lp->wavepoint_table.locked=0; /* </MUTEX> :-) */ +} + +/* Test this MAC frame a WavePoint beacon */ +static inline int WAVELAN_BEACON(unsigned char *data) +{ + wavepoint_beacon *beacon= (wavepoint_beacon *)data; + static wavepoint_beacon beacon_template={0xaa,0xaa,0x03,0x08,0x00,0x0e,0x20,0x03,0x00}; + + if(memcmp(beacon,&beacon_template,9)==0) + return 1; + else + return 0; +} +#endif /* WAVELAN_ROAMING */ + +/************************ I82593 SUBROUTINES *************************/ +/* + * Usefull subroutines to manage the Ethernet controler + */ + +/*------------------------------------------------------------------*/ +/* + * Routine to synchronously send a command to the i82593 chip. + * Should be called with interrupts enabled. + */ +static int +wv_82593_cmd(device * dev, + char * str, + int cmd, + int result) +{ + ioaddr_t base = dev->base_addr; + net_local * lp = (net_local *)dev->priv; + int status; + long spin; + u_long opri; + + /* Spin until the chip finishes executing its current command (if any) */ + do + { + opri = wv_splhi(); + outb(OP0_NOP | CR0_STATUS_3, LCCR(base)); + status = inb(LCSR(base)); + wv_splx(opri); + } + while((status & SR3_EXEC_STATE_MASK) != SR3_EXEC_IDLE); + + /* We are waiting for command completion */ + wv_wait_completed = TRUE; + + /* Issue the command to the controler */ + outb(cmd, LCCR(base)); + + /* If we don't have to check the result of the command */ + if(result == SR0_NO_RESULT) + { + wv_wait_completed = FALSE; + return(TRUE); + } + + /* Busy wait while the LAN controller executes the command. + * Note : wv_wait_completed should be volatile */ + spin = 0; + while(wv_wait_completed && (spin++ < 1000)) + udelay(10); + + /* If the interrupt handler hasn't be called */ + if(wv_wait_completed) + { + outb(OP0_NOP, LCCR(base)); + status = inb(LCSR(base)); + if(status & SR0_INTERRUPT) + { + /* There was an interrupt : call the interrupt handler */ +#ifdef DEBUG_INTERRUPT_ERROR + printk(KERN_WARNING "wv_82593_cmd: interrupt handler not installed or interrupt disabled\n"); +#endif + + wavelan_interrupt(dev->irq, (void *) dev, + (struct pt_regs *) NULL); + } + else + { + wv_wait_completed = 0; /* XXX */ +#ifdef DEBUG_INTERRUPT_ERROR + printk(KERN_INFO "wv_82593_cmd: %s timeout, status0 0x%02x\n", + str, status); +#endif + /* We probably should reset the controller here */ + return(FALSE); + } + } + + /* Check the return code provided by the interrupt handler against + * the expected return code provided by the caller */ + if((lp->status & SR0_EVENT_MASK) != result) + { +#ifdef DEBUG_INTERRUPT_ERROR + printk(KERN_INFO "wv_82593_cmd: %s failed, status0 = 0x%x\n", + str, lp->status); +#endif + return(FALSE); + } + + return(TRUE); +} /* wv_82593_cmd */ + +/*------------------------------------------------------------------*/ +/* + * This routine does a 593 op-code number 7, and obtains the diagnose + * status for the WaveLAN. + */ +static inline int +wv_diag(device * dev) +{ + if(wv_82593_cmd(dev, "wv_diag(): diagnose", + OP0_DIAGNOSE, SR0_DIAGNOSE_PASSED)) + return(TRUE); + +#ifdef DEBUG_CONFIG_ERROR + printk(KERN_INFO "wavelan_cs: i82593 Self Test failed!\n"); +#endif + return(FALSE); +} /* wv_diag */ + +/*------------------------------------------------------------------*/ +/* + * Routine to read len bytes from the i82593's ring buffer, starting at + * chip address addr. The results read from the chip are stored in buf. + * The return value is the address to use for next the call. + */ +static int +read_ringbuf(device * dev, + int addr, + char * buf, + int len) +{ + ioaddr_t base = dev->base_addr; + int ring_ptr = addr; + int chunk_len; + char * buf_ptr = buf; + +#ifdef OLDIES + /* After having check skb_put (net/core/skbuff.c) in the kernel, it seem + * quite safe to remove this... */ + + /* If buf is NULL, just increment the ring buffer pointer */ + if(buf == NULL) + return((ring_ptr - RX_BASE + len) % RX_SIZE + RX_BASE); +#endif + + /* Get all the buffer */ + while(len > 0) + { + /* Position the Program I/O Register at the ring buffer pointer */ + outb(ring_ptr & 0xff, PIORL(base)); + outb(((ring_ptr >> 8) & PIORH_MASK), PIORH(base)); + + /* First, determine how much we can read without wrapping around the + ring buffer */ + if((addr + len) < (RX_BASE + RX_SIZE)) + chunk_len = len; + else + chunk_len = RX_BASE + RX_SIZE - addr; + insb(PIOP(base), buf_ptr, chunk_len); + buf_ptr += chunk_len; + len -= chunk_len; + ring_ptr = (ring_ptr - RX_BASE + chunk_len) % RX_SIZE + RX_BASE; + } + return(ring_ptr); +} /* read_ringbuf */ + +/*------------------------------------------------------------------*/ +/* + * Reconfigure the i82593, or at least ask for it... + * Because wv_82593_config use the transmission buffer, we must do it + * when we are sure that there is no transmission, so we do it now + * or in wavelan_packet_xmit() (I can't find any better place, + * wavelan_interrupt is not an option...), so you may experience + * some delay sometime... + */ +static inline void +wv_82593_reconfig(device * dev) +{ + net_local * lp = (net_local *)dev->priv; + dev_link_t * link = ((net_local *) dev->priv)->link; + + /* Check if we can do it now ! */ + if(!(link->open) || (test_and_set_bit(0, (void *)&dev->tbusy) != 0)) + { + lp->reconfig_82593 = TRUE; +#ifdef DEBUG_IOCTL_INFO + printk(KERN_DEBUG "%s: wv_82593_reconfig(): delayed (busy = %ld, link = %d)\n", + dev->name, dev->tbusy, link->open); +#endif + } + else + { + lp->reconfig_82593 = FALSE; + wv_82593_config(dev); + dev->tbusy = 0; + } +} + +#ifdef OLDIES +/*------------------------------------------------------------------*/ +/* + * Dumps the current i82593 receive buffer to the console. + */ +static void wavelan_dump(device *dev) +{ + ioaddr_t base = dev->base_addr; + int i, c; + + /* disable receiver so we can use channel 1 */ + outb(OP0_RCV_DISABLE, LCCR(base)); + + /* reset receive DMA pointer */ + hacr_write_slow(base, HACR_PWR_STAT | HACR_RX_DMA_RESET); + hacr_write(base, HACR_DEFAULT); + + /* dump into receive buffer */ + wv_82593_cmd(dev, "wavelan_dump(): dump", CR0_CHNL|OP0_DUMP, SR0_DUMP_DONE); + + /* set read pointer to start of receive buffer */ + outb(0, PIORL(base)); + outb(0, PIORH(base)); + + printk(KERN_DEBUG "wavelan_cs: dump:\n"); + printk(KERN_DEBUG " 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F"); + for(i = 0; i < 73; i++){ + if((i % 16) == 0) { + printk("\n0x%02x:", i); + if (!i) { + printk(" "); + continue; + } + } + c = inb(PIOP(base)); + printk("%02x ", c); + } + printk("\n"); + + /* enable the receiver again */ + wv_ru_start(dev); +} +#endif + +/********************* DEBUG & INFO SUBROUTINES *********************/ +/* + * This routines are used in the code to show debug informations. + * Most of the time, it dump the content of hardware structures... + */ + +#ifdef DEBUG_PSA_SHOW +/*------------------------------------------------------------------*/ +/* + * Print the formatted contents of the Parameter Storage Area. + */ +static void +wv_psa_show(psa_t * p) +{ + printk(KERN_DEBUG "##### wavelan psa contents: #####\n"); + printk(KERN_DEBUG "psa_io_base_addr_1: 0x%02X %02X %02X %02X\n", + p->psa_io_base_addr_1, + p->psa_io_base_addr_2, + p->psa_io_base_addr_3, + p->psa_io_base_addr_4); + printk(KERN_DEBUG "psa_rem_boot_addr_1: 0x%02X %02X %02X\n", + p->psa_rem_boot_addr_1, + p->psa_rem_boot_addr_2, + p->psa_rem_boot_addr_3); + printk(KERN_DEBUG "psa_holi_params: 0x%02x, ", p->psa_holi_params); + printk("psa_int_req_no: %d\n", p->psa_int_req_no); +#ifdef DEBUG_SHOW_UNUSED + printk(KERN_DEBUG "psa_unused0[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X\n", + p->psa_unused0[0], + p->psa_unused0[1], + p->psa_unused0[2], + p->psa_unused0[3], + p->psa_unused0[4], + p->psa_unused0[5], + p->psa_unused0[6]); +#endif /* DEBUG_SHOW_UNUSED */ + printk(KERN_DEBUG "psa_univ_mac_addr[]: %02x:%02x:%02x:%02x:%02x:%02x\n", + p->psa_univ_mac_addr[0], + p->psa_univ_mac_addr[1], + p->psa_univ_mac_addr[2], + p->psa_univ_mac_addr[3], + p->psa_univ_mac_addr[4], + p->psa_univ_mac_addr[5]); + printk(KERN_DEBUG "psa_local_mac_addr[]: %02x:%02x:%02x:%02x:%02x:%02x\n", + p->psa_local_mac_addr[0], + p->psa_local_mac_addr[1], + p->psa_local_mac_addr[2], + p->psa_local_mac_addr[3], + p->psa_local_mac_addr[4], + p->psa_local_mac_addr[5]); + printk(KERN_DEBUG "psa_univ_local_sel: %d, ", p->psa_univ_local_sel); + printk("psa_comp_number: %d, ", p->psa_comp_number); + printk("psa_thr_pre_set: 0x%02x\n", p->psa_thr_pre_set); + printk(KERN_DEBUG "psa_feature_select/decay_prm: 0x%02x, ", + p->psa_feature_select); + printk("psa_subband/decay_update_prm: %d\n", p->psa_subband); + printk(KERN_DEBUG "psa_quality_thr: 0x%02x, ", p->psa_quality_thr); + printk("psa_mod_delay: 0x%02x\n", p->psa_mod_delay); + printk(KERN_DEBUG "psa_nwid: 0x%02x%02x, ", p->psa_nwid[0], p->psa_nwid[1]); + printk("psa_nwid_select: %d\n", p->psa_nwid_select); + printk(KERN_DEBUG "psa_encryption_select: %d, ", p->psa_encryption_select); + printk("psa_encryption_key[]: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", + p->psa_encryption_key[0], + p->psa_encryption_key[1], + p->psa_encryption_key[2], + p->psa_encryption_key[3], + p->psa_encryption_key[4], + p->psa_encryption_key[5], + p->psa_encryption_key[6], + p->psa_encryption_key[7]); + printk(KERN_DEBUG "psa_databus_width: %d\n", p->psa_databus_width); + printk(KERN_DEBUG "psa_call_code/auto_squelch: 0x%02x, ", + p->psa_call_code[0]); + printk("psa_call_code[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", + p->psa_call_code[0], + p->psa_call_code[1], + p->psa_call_code[2], + p->psa_call_code[3], + p->psa_call_code[4], + p->psa_call_code[5], + p->psa_call_code[6], + p->psa_call_code[7]); +#ifdef DEBUG_SHOW_UNUSED + printk(KERN_DEBUG "psa_reserved[]: %02X:%02X:%02X:%02X\n", + p->psa_reserved[0], + p->psa_reserved[1], + p->psa_reserved[2], + p->psa_reserved[3]); +#endif /* DEBUG_SHOW_UNUSED */ + printk(KERN_DEBUG "psa_conf_status: %d, ", p->psa_conf_status); + printk("psa_crc: 0x%02x%02x, ", p->psa_crc[0], p->psa_crc[1]); + printk("psa_crc_status: 0x%02x\n", p->psa_crc_status); +} /* wv_psa_show */ +#endif /* DEBUG_PSA_SHOW */ + +#ifdef DEBUG_MMC_SHOW +/*------------------------------------------------------------------*/ +/* + * Print the formatted status of the Modem Management Controller. + * This function need to be completed... + */ +static void +wv_mmc_show(device * dev) +{ + ioaddr_t base = dev->base_addr; + net_local * lp = (net_local *)dev->priv; + mmr_t m; + + /* Basic check */ + if(hasr_read(base) & HASR_NO_CLK) + { + printk(KERN_WARNING "%s: wv_mmc_show: modem not connected\n", + dev->name); + return; + } + + /* Read the mmc */ + mmc_out(base, mmwoff(0, mmw_freeze), 1); + mmc_read(base, 0, (u_char *)&m, sizeof(m)); + mmc_out(base, mmwoff(0, mmw_freeze), 0); + +#ifdef WIRELESS_EXT /* If wireless extension exist in the kernel */ + /* Don't forget to update statistics */ + lp->wstats.discard.nwid += (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l; +#endif /* WIRELESS_EXT */ + + printk(KERN_DEBUG "##### wavelan modem status registers: #####\n"); +#ifdef DEBUG_SHOW_UNUSED + printk(KERN_DEBUG "mmc_unused0[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", + m.mmr_unused0[0], + m.mmr_unused0[1], + m.mmr_unused0[2], + m.mmr_unused0[3], + m.mmr_unused0[4], + m.mmr_unused0[5], + m.mmr_unused0[6], + m.mmr_unused0[7]); +#endif /* DEBUG_SHOW_UNUSED */ + printk(KERN_DEBUG "Encryption algorythm: %02X - Status: %02X\n", + m.mmr_des_avail, m.mmr_des_status); +#ifdef DEBUG_SHOW_UNUSED + printk(KERN_DEBUG "mmc_unused1[]: %02X:%02X:%02X:%02X:%02X\n", + m.mmr_unused1[0], + m.mmr_unused1[1], + m.mmr_unused1[2], + m.mmr_unused1[3], + m.mmr_unused1[4]); +#endif /* DEBUG_SHOW_UNUSED */ + printk(KERN_DEBUG "dce_status: 0x%x [%s%s%s%s]\n", + m.mmr_dce_status, + (m.mmr_dce_status & MMR_DCE_STATUS_RX_BUSY) ? "energy detected,":"", + (m.mmr_dce_status & MMR_DCE_STATUS_LOOPT_IND) ? + "loop test indicated," : "", + (m.mmr_dce_status & MMR_DCE_STATUS_TX_BUSY) ? "transmitter on," : "", + (m.mmr_dce_status & MMR_DCE_STATUS_JBR_EXPIRED) ? + "jabber timer expired," : ""); + printk(KERN_DEBUG "Dsp ID: %02X\n", + m.mmr_dsp_id); +#ifdef DEBUG_SHOW_UNUSED + printk(KERN_DEBUG "mmc_unused2[]: %02X:%02X\n", + m.mmr_unused2[0], + m.mmr_unused2[1]); +#endif /* DEBUG_SHOW_UNUSED */ + printk(KERN_DEBUG "# correct_nwid: %d, # wrong_nwid: %d\n", + (m.mmr_correct_nwid_h << 8) | m.mmr_correct_nwid_l, + (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l); + printk(KERN_DEBUG "thr_pre_set: 0x%x [current signal %s]\n", + m.mmr_thr_pre_set & MMR_THR_PRE_SET, + (m.mmr_thr_pre_set & MMR_THR_PRE_SET_CUR) ? "above" : "below"); + printk(KERN_DEBUG "signal_lvl: %d [%s], ", + m.mmr_signal_lvl & MMR_SIGNAL_LVL, + (m.mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) ? "new msg" : "no new msg"); + printk("silence_lvl: %d [%s], ", m.mmr_silence_lvl & MMR_SILENCE_LVL, + (m.mmr_silence_lvl & MMR_SILENCE_LVL_VALID) ? "update done" : "no new update"); + printk("sgnl_qual: 0x%x [%s]\n", m.mmr_sgnl_qual & MMR_SGNL_QUAL, + (m.mmr_sgnl_qual & MMR_SGNL_QUAL_ANT) ? "Antenna 1" : "Antenna 0"); +#ifdef DEBUG_SHOW_UNUSED + printk(KERN_DEBUG "netw_id_l: %x\n", m.mmr_netw_id_l); +#endif /* DEBUG_SHOW_UNUSED */ +} /* wv_mmc_show */ +#endif /* DEBUG_MMC_SHOW */ + +#ifdef DEBUG_I82593_SHOW +/*------------------------------------------------------------------*/ +/* + * Print the formatted status of the i82593's receive unit. + */ +static void +wv_ru_show(device * dev) +{ + net_local *lp = (net_local *) dev->priv; + + printk(KERN_DEBUG "##### wavelan i82593 receiver status: #####\n"); + printk(KERN_DEBUG "ru: rfp %d stop %d", lp->rfp, lp->stop); + /* + * Not implemented yet... + */ + printk("\n"); +} /* wv_ru_show */ +#endif /* DEBUG_I82593_SHOW */ + +#ifdef DEBUG_DEVICE_SHOW +/*------------------------------------------------------------------*/ +/* + * Print the formatted status of the WaveLAN PCMCIA device driver. + */ +static void +wv_dev_show(device * dev) +{ + printk(KERN_DEBUG "dev:"); + printk(" start=%d,", dev->start); + printk(" tbusy=%ld,", dev->tbusy); + printk(" interrupt=%d,", dev->interrupt); + printk(" trans_start=%ld,", dev->trans_start); + printk(" flags=0x%x,", dev->flags); + printk("\n"); +} /* wv_dev_show */ + +/*------------------------------------------------------------------*/ +/* + * Print the formatted status of the WaveLAN PCMCIA device driver's + * private information. + */ +static void +wv_local_show(device * dev) +{ + net_local *lp; + + lp = (net_local *)dev->priv; + + printk(KERN_DEBUG "local:"); + /* + * Not implemented yet... + */ + printk("\n"); +} /* wv_local_show */ +#endif /* DEBUG_DEVICE_SHOW */ + +#if defined(DEBUG_RX_INFO) || defined(DEBUG_TX_INFO) +/*------------------------------------------------------------------*/ +/* + * Dump packet header (and content if necessary) on the screen + */ +static inline void +wv_packet_info(u_char * p, /* Packet to dump */ + int length, /* Length of the packet */ + char * msg1, /* Name of the device */ + char * msg2) /* Name of the function */ +{ + int i; + int maxi; + + printk(KERN_DEBUG "%s: %s(): dest %02X:%02X:%02X:%02X:%02X:%02X, length %d\n", + msg1, msg2, p[0], p[1], p[2], p[3], p[4], p[5], length); + printk(KERN_DEBUG "%s: %s(): src %02X:%02X:%02X:%02X:%02X:%02X, type 0x%02X%02X\n", + msg1, msg2, p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13]); + +#ifdef DEBUG_PACKET_DUMP + + printk(KERN_DEBUG "data=\""); + + if((maxi = length) > DEBUG_PACKET_DUMP) + maxi = DEBUG_PACKET_DUMP; + for(i = 14; i < maxi; i++) + if(p[i] >= ' ' && p[i] <= '~') + printk(" %c", p[i]); + else + printk("%02X", p[i]); + if(maxi < length) + printk(".."); + printk("\"\n"); + printk(KERN_DEBUG "\n"); +#endif /* DEBUG_PACKET_DUMP */ +} +#endif /* defined(DEBUG_RX_INFO) || defined(DEBUG_TX_INFO) */ + +/*------------------------------------------------------------------*/ +/* + * This is the information which is displayed by the driver at startup + * There is a lot of flag to configure it at your will... + */ +static inline void +wv_init_info(device * dev) +{ + ioaddr_t base = dev->base_addr; + psa_t psa; + int i; + + /* Read the parameter storage area */ + psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa)); + +#ifdef DEBUG_PSA_SHOW + wv_psa_show(&psa); +#endif +#ifdef DEBUG_MMC_SHOW + wv_mmc_show(dev); +#endif +#ifdef DEBUG_I82593_SHOW + wv_ru_show(dev); +#endif + +#ifdef DEBUG_BASIC_SHOW + /* Now, let's go for the basic stuff */ + printk(KERN_NOTICE "%s: WaveLAN: port %#x, irq %d, hw_addr", + dev->name, base, dev->irq); + for(i = 0; i < WAVELAN_ADDR_SIZE; i++) + printk("%s%02X", (i == 0) ? " " : ":", dev->dev_addr[i]); + + /* Print current network id */ + if(psa.psa_nwid_select) + printk(", nwid 0x%02X-%02X", psa.psa_nwid[0], psa.psa_nwid[1]); + else + printk(", nwid off"); + + /* If 2.00 card */ + if(!(mmc_in(base, mmroff(0, mmr_fee_status)) & + (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) + { + unsigned short freq; + + /* Ask the EEprom to read the frequency from the first area */ + fee_read(base, 0x00 /* 1st area - frequency... */, + &freq, 1); + + /* Print frequency */ + printk(", 2.00, %ld", (freq >> 6) + 2400L); + + /* Hack !!! */ + if(freq & 0x20) + printk(".5"); + } + else + { + printk(", PCMCIA, "); + switch (psa.psa_subband) + { + case PSA_SUBBAND_915: + printk("915"); + break; + case PSA_SUBBAND_2425: + printk("2425"); + break; + case PSA_SUBBAND_2460: + printk("2460"); + break; + case PSA_SUBBAND_2484: + printk("2484"); + break; + case PSA_SUBBAND_2430_5: + printk("2430.5"); + break; + default: + printk("???"); + } + } + + printk(" MHz\n"); +#endif /* DEBUG_BASIC_SHOW */ + +#ifdef DEBUG_VERSION_SHOW + /* Print version information */ + printk(KERN_NOTICE "%s", version); +#endif +} /* wv_init_info */ + +/********************* IOCTL, STATS & RECONFIG *********************/ +/* + * We found here routines that are called by Linux on differents + * occasions after the configuration and not for transmitting data + * These may be called when the user use ifconfig, /proc/net/dev + * or wireless extensions + */ + +/*------------------------------------------------------------------*/ +/* + * Get the current ethernet statistics. This may be called with the + * card open or closed. + * Used when the user read /proc/net/dev + */ +static en_stats * +wavelan_get_stats(device * dev) +{ +#ifdef DEBUG_IOCTL_TRACE + printk(KERN_DEBUG "%s: <>wavelan_get_stats()\n", dev->name); +#endif + + return(&((net_local *) dev->priv)->stats); +} + +/*------------------------------------------------------------------*/ +/* + * Set or clear the multicast filter for this adaptor. + * num_addrs == -1 Promiscuous mode, receive all packets + * num_addrs == 0 Normal mode, clear multicast list + * num_addrs > 0 Multicast mode, receive normal and MC packets, + * and do best-effort filtering. + */ + +static void +wavelan_set_multicast_list(device * dev) +{ + net_local * lp = (net_local *) dev->priv; + +#ifdef DEBUG_IOCTL_TRACE + printk(KERN_DEBUG "%s: ->wavelan_set_multicast_list()\n", dev->name); +#endif + +#ifdef DEBUG_IOCTL_INFO + printk(KERN_DEBUG "%s: wavelan_set_multicast_list(): setting Rx mode %02X to %d addresses.\n", + dev->name, dev->flags, dev->mc_count); +#endif + + if(dev->flags & IFF_PROMISC) + { + /* + * Enable promiscuous mode: receive all packets. + */ + if(!lp->promiscuous) + { + lp->promiscuous = 1; + lp->allmulticast = 0; + lp->mc_count = 0; + + wv_82593_reconfig(dev); + + /* Tell the kernel that we are doing a really bad job... */ + dev->flags |= IFF_PROMISC; + } + } + else + /* If all multicast addresses + * or too much multicast addresses for the hardware filter */ + if((dev->flags & IFF_ALLMULTI) || + (dev->mc_count > I82593_MAX_MULTICAST_ADDRESSES)) + { + /* + * Disable promiscuous mode, but active the all multicast mode + */ + if(!lp->allmulticast) + { + lp->promiscuous = 0; + lp->allmulticast = 1; + lp->mc_count = 0; + + wv_82593_reconfig(dev); + + /* Tell the kernel that we are doing a really bad job... */ + dev->flags |= IFF_ALLMULTI; + } + } + else + /* If there is some multicast addresses to send */ + if(dev->mc_list != (struct dev_mc_list *) NULL) + { + /* + * Disable promiscuous mode, but receive all packets + * in multicast list + */ +#ifdef MULTICAST_AVOID + if(lp->promiscuous || lp->allmulticast || + (dev->mc_count != lp->mc_count)) +#endif + { + lp->promiscuous = 0; + lp->allmulticast = 0; + lp->mc_count = dev->mc_count; + + wv_82593_reconfig(dev); + } + } + else + { + /* + * Switch to normal mode: disable promiscuous mode and + * clear the multicast list. + */ + if(lp->promiscuous || lp->mc_count == 0) + { + lp->promiscuous = 0; + lp->allmulticast = 0; + lp->mc_count = 0; + + wv_82593_reconfig(dev); + } + } +#ifdef DEBUG_IOCTL_TRACE + printk(KERN_DEBUG "%s: <-wavelan_set_multicast_list()\n", dev->name); +#endif +} + +/*------------------------------------------------------------------*/ +/* + * This function doesn't exist... + * (Note : it was a nice way to test the reconfigure stuff...) + */ +#ifdef SET_MAC_ADDRESS +static int +wavelan_set_mac_address(device * dev, + void * addr) +{ + struct sockaddr * mac = addr; + + /* Copy the address */ + memcpy(dev->dev_addr, mac->sa_data, WAVELAN_ADDR_SIZE); + + /* Reconfig the beast */ + wv_82593_reconfig(dev); + + return 0; +} +#endif /* SET_MAC_ADDRESS */ + +#ifdef WIRELESS_EXT /* If wireless extension exist in the kernel */ + +/*------------------------------------------------------------------*/ +/* + * Frequency setting (for hardware able of it) + * It's a bit complicated and you don't really want to look into it... + * (called in wavelan_ioctl) + */ +static inline int +wv_set_frequency(u_long base, /* i/o port of the card */ + iw_freq * frequency) +{ + const int BAND_NUM = 10; /* Number of bands */ + long freq = 0L; /* offset to 2.4 GHz in .5 MHz */ +#ifdef DEBUG_IOCTL_INFO + int i; +#endif + + /* Setting by frequency */ + /* Theoritically, you may set any frequency between + * the two limits with a 0.5 MHz precision. In practice, + * I don't want you to have trouble with local + * regulations... */ + if((frequency->e == 1) && + (frequency->m >= (int) 2.412e8) && (frequency->m <= (int) 2.487e8)) + { + freq = ((frequency->m / 10000) - 24000L) / 5; + } + + /* Setting by channel (same as wfreqsel) */ + /* Warning : each channel is 22MHz wide, so some of the channels + * will interfere... */ + if((frequency->e == 0) && + (frequency->m >= 0) && (frequency->m < BAND_NUM)) + { + /* Get frequency offset. */ + freq = channel_bands[frequency->m] >> 1; + } + + /* Verify if the frequency is allowed */ + if(freq != 0L) + { + u_short table[10]; /* Authorized frequency table */ + + /* Read the frequency table */ + fee_read(base, 0x71 /* frequency table */, + table, 10); + +#ifdef DEBUG_IOCTL_INFO + printk(KERN_DEBUG "Frequency table :"); + for(i = 0; i < 10; i++) + { + printk(" %04X", + table[i]); + } + printk("\n"); +#endif + + /* Look in the table if the frequency is allowed */ + if(!(table[9 - ((freq - 24) / 16)] & + (1 << ((freq - 24) % 16)))) + return -EINVAL; /* not allowed */ + } + else + return -EINVAL; + + /* If we get a usable frequency */ + if(freq != 0L) + { + unsigned short area[16]; + unsigned short dac[2]; + unsigned short area_verify[16]; + unsigned short dac_verify[2]; + /* Corresponding gain (in the power adjust value table) + * see AT&T Wavelan Data Manual, REF 407-024689/E, page 3-8 + * & WCIN062D.DOC, page 6.2.9 */ + unsigned short power_limit[] = { 40, 80, 120, 160, 0 }; + int power_band = 0; /* Selected band */ + unsigned short power_adjust; /* Correct value */ + + /* Search for the gain */ + power_band = 0; + while((freq > power_limit[power_band]) && + (power_limit[++power_band] != 0)) + ; + + /* Read the first area */ + fee_read(base, 0x00, + area, 16); + + /* Read the DAC */ + fee_read(base, 0x60, + dac, 2); + + /* Read the new power adjust value */ + fee_read(base, 0x6B - (power_band >> 1), + &power_adjust, 1); + if(power_band & 0x1) + power_adjust >>= 8; + else + power_adjust &= 0xFF; + +#ifdef DEBUG_IOCTL_INFO + printk(KERN_DEBUG "Wavelan EEprom Area 1 :"); + for(i = 0; i < 16; i++) + { + printk(" %04X", + area[i]); + } + printk("\n"); + + printk(KERN_DEBUG "Wavelan EEprom DAC : %04X %04X\n", + dac[0], dac[1]); +#endif + + /* Frequency offset (for info only...) */ + area[0] = ((freq << 5) & 0xFFE0) | (area[0] & 0x1F); + + /* Receiver Principle main divider coefficient */ + area[3] = (freq >> 1) + 2400L - 352L; + area[2] = ((freq & 0x1) << 4) | (area[2] & 0xFFEF); + + /* Transmitter Main divider coefficient */ + area[13] = (freq >> 1) + 2400L; + area[12] = ((freq & 0x1) << 4) | (area[2] & 0xFFEF); + + /* Others part of the area are flags, bit streams or unused... */ + + /* Set the value in the DAC */ + dac[1] = ((power_adjust >> 1) & 0x7F) | (dac[1] & 0xFF80); + dac[0] = ((power_adjust & 0x1) << 4) | (dac[0] & 0xFFEF); + + /* Write the first area */ + fee_write(base, 0x00, + area, 16); + + /* Write the DAC */ + fee_write(base, 0x60, + dac, 2); + + /* We now should verify here that the EEprom writting was ok */ + + /* ReRead the first area */ + fee_read(base, 0x00, + area_verify, 16); + + /* ReRead the DAC */ + fee_read(base, 0x60, + dac_verify, 2); + + /* Compare */ + if(memcmp(area, area_verify, 16 * 2) || + memcmp(dac, dac_verify, 2 * 2)) + { +#ifdef DEBUG_IOCTL_ERROR + printk(KERN_INFO "Wavelan: wv_set_frequency : unable to write new frequency to EEprom (??)\n"); +#endif + return -EOPNOTSUPP; + } + + /* We must download the frequency parameters to the + * synthetisers (from the EEprom - area 1) + * Note : as the EEprom is auto decremented, we set the end + * if the area... */ + mmc_out(base, mmwoff(0, mmw_fee_addr), 0x0F); + mmc_out(base, mmwoff(0, mmw_fee_ctrl), + MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD); + + /* Wait until the download is finished */ + fee_wait(base, 100, 100); + + /* We must now download the power adjust value (gain) to + * the synthetisers (from the EEprom - area 7 - DAC) */ + mmc_out(base, mmwoff(0, mmw_fee_addr), 0x61); + mmc_out(base, mmwoff(0, mmw_fee_ctrl), + MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD); + + /* Wait until the download is finished */ + fee_wait(base, 100, 100); + +#ifdef DEBUG_IOCTL_INFO + /* Verification of what we have done... */ + + printk(KERN_DEBUG "Wavelan EEprom Area 1 :"); + for(i = 0; i < 16; i++) + { + printk(" %04X", + area_verify[i]); + } + printk("\n"); + + printk(KERN_DEBUG "Wavelan EEprom DAC : %04X %04X\n", + dac_verify[0], dac_verify[1]); +#endif + + return 0; + } + else + return -EINVAL; /* Bah, never get there... */ +} + +/*------------------------------------------------------------------*/ +/* + * Give the list of available frequencies + */ +static inline int +wv_frequency_list(u_long base, /* i/o port of the card */ + iw_freq * list, /* List of frequency to fill */ + int max) /* Maximum number of frequencies */ +{ + u_short table[10]; /* Authorized frequency table */ + long freq = 0L; /* offset to 2.4 GHz in .5 MHz + 12 MHz */ + int i; /* index in the table */ +#if WIRELESS_EXT > 7 + const int BAND_NUM = 10; /* Number of bands */ + int c = 0; /* Channel number */ +#endif WIRELESS_EXT + + /* Read the frequency table */ + fee_read(base, 0x71 /* frequency table */, + table, 10); + + /* Look all frequencies */ + i = 0; + for(freq = 0; freq < 150; freq++) + /* Look in the table if the frequency is allowed */ + if(table[9 - (freq / 16)] & (1 << (freq % 16))) + { +#if WIRELESS_EXT > 7 + /* Compute approximate channel number */ + while((((channel_bands[c] >> 1) - 24) < freq) && + (c < BAND_NUM)) + c++; + list[i].i = c; /* Set the list index */ +#endif WIRELESS_EXT + + /* put in the list */ + list[i].m = (((freq + 24) * 5) + 24000L) * 10000; + list[i++].e = 1; + + /* Check number */ + if(i >= max) + return(i); + } + + return(i); +} + +#ifdef WIRELESS_SPY +/*------------------------------------------------------------------*/ +/* + * Gather wireless spy statistics : for each packet, compare the source + * address with out list, and if match, get the stats... + * Sorry, but this function really need wireless extensions... + */ +static inline void +wl_spy_gather(device * dev, + u_char * mac, /* MAC address */ + u_char * stats) /* Statistics to gather */ +{ + net_local * lp = (net_local *) dev->priv; + int i; + + /* Look all addresses */ + for(i = 0; i < lp->spy_number; i++) + /* If match */ + if(!memcmp(mac, lp->spy_address[i], WAVELAN_ADDR_SIZE)) + { + /* Update statistics */ + lp->spy_stat[i].qual = stats[2] & MMR_SGNL_QUAL; + lp->spy_stat[i].level = stats[0] & MMR_SIGNAL_LVL; + lp->spy_stat[i].noise = stats[1] & MMR_SILENCE_LVL; + lp->spy_stat[i].updated = 0x7; + } +} +#endif /* WIRELESS_SPY */ + +#ifdef HISTOGRAM +/*------------------------------------------------------------------*/ +/* + * This function calculate an histogram on the signal level. + * As the noise is quite constant, it's like doing it on the SNR. + * We have defined a set of interval (lp->his_range), and each time + * the level goes in that interval, we increment the count (lp->his_sum). + * With this histogram you may detect if one wavelan is really weak, + * or you may also calculate the mean and standard deviation of the level... + */ +static inline void +wl_his_gather(device * dev, + u_char * stats) /* Statistics to gather */ +{ + net_local * lp = (net_local *) dev->priv; + u_char level = stats[0] & MMR_SIGNAL_LVL; + int i; + + /* Find the correct interval */ + i = 0; + while((i < (lp->his_number - 1)) && (level >= lp->his_range[i++])) + ; + + /* Increment interval counter */ + (lp->his_sum[i])++; +} +#endif /* HISTOGRAM */ + +/*------------------------------------------------------------------*/ +/* + * Perform ioctl : config & info stuff + * This is here that are treated the wireless extensions (iwconfig) + */ +static int +wavelan_ioctl(struct net_device * dev, /* Device on wich the ioctl apply */ + struct ifreq * rq, /* Data passed */ + int cmd) /* Ioctl number */ +{ + ioaddr_t base = dev->base_addr; + net_local * lp = (net_local *)dev->priv; /* lp is not unused */ + struct iwreq * wrq = (struct iwreq *) rq; + psa_t psa; + mm_t m; + unsigned long x; + int ret = 0; + +#ifdef DEBUG_IOCTL_TRACE + printk(KERN_DEBUG "%s: ->wavelan_ioctl(cmd=0x%X)\n", dev->name, cmd); +#endif + + /* Disable interrupts & save flags */ + x = wv_splhi(); + + /* Look what is the request */ + switch(cmd) + { + /* --------------- WIRELESS EXTENSIONS --------------- */ + + case SIOCGIWNAME: + strcpy(wrq->u.name, "Wavelan"); + break; + + case SIOCSIWNWID: + /* Set NWID in wavelan */ +#if WIRELESS_EXT > 8 + if(!wrq->u.nwid.disabled) + { + /* Set NWID in psa */ + psa.psa_nwid[0] = (wrq->u.nwid.value & 0xFF00) >> 8; + psa.psa_nwid[1] = wrq->u.nwid.value & 0xFF; +#else /* WIRELESS_EXT > 8 */ + if(wrq->u.nwid.on) + { + /* Set NWID in psa */ + psa.psa_nwid[0] = (wrq->u.nwid.nwid & 0xFF00) >> 8; + psa.psa_nwid[1] = wrq->u.nwid.nwid & 0xFF; +#endif /* WIRELESS_EXT > 8 */ + psa.psa_nwid_select = 0x01; + psa_write(dev, (char *)psa.psa_nwid - (char *)&psa, + (unsigned char *)psa.psa_nwid, 3); + + /* Set NWID in mmc */ + m.w.mmw_netw_id_l = psa.psa_nwid[1]; + m.w.mmw_netw_id_h = psa.psa_nwid[0]; + mmc_write(base, (char *)&m.w.mmw_netw_id_l - (char *)&m, + (unsigned char *)&m.w.mmw_netw_id_l, 2); + mmc_out(base, mmwoff(0, mmw_loopt_sel), 0x00); + } + else + { + /* Disable nwid in the psa */ + psa.psa_nwid_select = 0x00; + psa_write(dev, (char *)&psa.psa_nwid_select - (char *)&psa, + (unsigned char *)&psa.psa_nwid_select, 1); + + /* Disable nwid in the mmc (no filtering) */ + mmc_out(base, mmwoff(0, mmw_loopt_sel), MMW_LOOPT_SEL_DIS_NWID); + } + /* update the Wavelan checksum */ + update_psa_checksum(dev); + break; + + case SIOCGIWNWID: + /* Read the NWID */ + psa_read(dev, (char *)psa.psa_nwid - (char *)&psa, + (unsigned char *)psa.psa_nwid, 3); +#if WIRELESS_EXT > 8 + wrq->u.nwid.value = (psa.psa_nwid[0] << 8) + psa.psa_nwid[1]; + wrq->u.nwid.disabled = !(psa.psa_nwid_select); + wrq->u.nwid.fixed = 1; /* Superfluous */ +#else /* WIRELESS_EXT > 8 */ + wrq->u.nwid.nwid = (psa.psa_nwid[0] << 8) + psa.psa_nwid[1]; + wrq->u.nwid.on = psa.psa_nwid_select; +#endif /* WIRELESS_EXT > 8 */ + break; + + case SIOCSIWFREQ: + /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable) */ + if(!(mmc_in(base, mmroff(0, mmr_fee_status)) & + (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) + ret = wv_set_frequency(base, &(wrq->u.freq)); + else + ret = -EOPNOTSUPP; + break; + + case SIOCGIWFREQ: + /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable) + * (does it work for everybody ??? - especially old cards...) */ + if(!(mmc_in(base, mmroff(0, mmr_fee_status)) & + (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) + { + unsigned short freq; + + /* Ask the EEprom to read the frequency from the first area */ + fee_read(base, 0x00 /* 1st area - frequency... */, + &freq, 1); + wrq->u.freq.m = ((freq >> 5) * 5 + 24000L) * 10000; + wrq->u.freq.e = 1; + } + else + { + psa_read(dev, (char *)&psa.psa_subband - (char *)&psa, + (unsigned char *)&psa.psa_subband, 1); + + if(psa.psa_subband <= 4) + { + wrq->u.freq.m = fixed_bands[psa.psa_subband]; + wrq->u.freq.e = (psa.psa_subband != 0); + } + else + ret = -EOPNOTSUPP; + } + break; + + case SIOCSIWSENS: + /* Set the level threshold */ +#if WIRELESS_EXT > 7 + /* We should complain loudly if wrq->u.sens.fixed = 0, because we + * can't set auto mode... */ + psa.psa_thr_pre_set = wrq->u.sens.value & 0x3F; +#else /* WIRELESS_EXT > 7 */ + psa.psa_thr_pre_set = wrq->u.sensitivity & 0x3F; +#endif /* WIRELESS_EXT > 7 */ + psa_write(dev, (char *)&psa.psa_thr_pre_set - (char *)&psa, + (unsigned char *)&psa.psa_thr_pre_set, 1); + /* update the Wavelan checksum */ + update_psa_checksum(dev); + mmc_out(base, mmwoff(0, mmw_thr_pre_set), psa.psa_thr_pre_set); + break; + + case SIOCGIWSENS: + /* Read the level threshold */ + psa_read(dev, (char *)&psa.psa_thr_pre_set - (char *)&psa, + (unsigned char *)&psa.psa_thr_pre_set, 1); +#if WIRELESS_EXT > 7 + wrq->u.sens.value = psa.psa_thr_pre_set & 0x3F; + wrq->u.sens.fixed = 1; +#else /* WIRELESS_EXT > 7 */ + wrq->u.sensitivity = psa.psa_thr_pre_set & 0x3F; +#endif /* WIRELESS_EXT > 7 */ + break; + +#if WIRELESS_EXT > 8 + case SIOCSIWENCODE: + /* Set encryption key */ + if(!mmc_encr(base)) + { + ret = -EOPNOTSUPP; + break; + } + + /* Basic checking... */ + if(wrq->u.encoding.pointer != (caddr_t) 0) + { + /* Check the size of the key */ + if(wrq->u.encoding.length != 8) + { + ret = -EINVAL; + break; + } + + /* Copy the key in the driver */ + if(copy_from_user(psa.psa_encryption_key, wrq->u.encoding.pointer, + wrq->u.encoding.length)) + { + ret = -EFAULT; + break; + } + + psa.psa_encryption_select = 1; + psa_write(dev, (char *) &psa.psa_encryption_select - (char *) &psa, + (unsigned char *) &psa.psa_encryption_select, 8+1); + + mmc_out(base, mmwoff(0, mmw_encr_enable), + MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE); + mmc_write(base, mmwoff(0, mmw_encr_key), + (unsigned char *) &psa.psa_encryption_key, 8); + } + + if(wrq->u.encoding.flags & IW_ENCODE_DISABLED) + { /* disable encryption */ + psa.psa_encryption_select = 0; + psa_write(dev, (char *) &psa.psa_encryption_select - (char *) &psa, + (unsigned char *) &psa.psa_encryption_select, 1); + + mmc_out(base, mmwoff(0, mmw_encr_enable), 0); + } + /* update the Wavelan checksum */ + update_psa_checksum(dev); + break; + + case SIOCGIWENCODE: + /* Read the encryption key */ + if(!mmc_encr(base)) + { + ret = -EOPNOTSUPP; + break; + } + + /* only super-user can see encryption key */ + if(!suser()) + { + ret = -EPERM; + break; + } + + /* Basic checking... */ + if(wrq->u.encoding.pointer != (caddr_t) 0) + { + psa_read(dev, (char *) &psa.psa_encryption_select - (char *) &psa, + (unsigned char *) &psa.psa_encryption_select, 1+8); + + /* encryption is enabled ? */ + if(psa.psa_encryption_select) + wrq->u.encoding.flags = IW_ENCODE_ENABLED; + else + wrq->u.encoding.flags = IW_ENCODE_DISABLED; + wrq->u.encoding.flags |= mmc_encr(base); + + /* Copy the key to the user buffer */ + wrq->u.encoding.length = 8; + if(copy_to_user(wrq->u.encoding.pointer, psa.psa_encryption_key, 8)) + ret = -EFAULT; + } + break; +#endif /* WIRELESS_EXT > 8 */ + +#ifdef WAVELAN_ROAMING_EXT +#if WIRELESS_EXT > 5 + case SIOCSIWESSID: + /* Check if disable */ + if(wrq->u.data.flags == 0) + lp->filter_domains = 0; + else + /* Basic checking... */ + if(wrq->u.data.pointer != (caddr_t) 0) + { + char essid[IW_ESSID_MAX_SIZE + 1]; + char * endp; + + /* Check the size of the string */ + if(wrq->u.data.length > IW_ESSID_MAX_SIZE + 1) + { + ret = -E2BIG; + break; + } + + /* Copy the string in the driver */ + if(copy_from_user(essid, wrq->u.data.pointer, wrq->u.data.length)) + { + ret = -EFAULT; + break; + } + essid[IW_ESSID_MAX_SIZE] = '\0'; + +#ifdef DEBUG_IOCTL_INFO + printk(KERN_DEBUG "SetEssid : ``%s''\n", essid); +#endif /* DEBUG_IOCTL_INFO */ + + /* Convert to a number (note : Wavelan specific) */ + lp->domain_id = simple_strtoul(essid, &endp, 16); + /* Has it worked ? */ + if(endp > essid) + lp->filter_domains = 1; + else + { + lp->filter_domains = 0; + ret = -EINVAL; + } + } + break; + + case SIOCGIWESSID: + /* Basic checking... */ + if(wrq->u.data.pointer != (caddr_t) 0) + { + char essid[IW_ESSID_MAX_SIZE + 1]; + + /* Is the domain ID active ? */ + wrq->u.data.flags = lp->filter_domains; + + /* Copy Domain ID into a string (Wavelan specific) */ + /* Sound crazy, be we can't have a snprintf in the kernel !!! */ + sprintf(essid, "%lX", lp->domain_id); + essid[IW_ESSID_MAX_SIZE] = '\0'; + + /* Set the length */ + wrq->u.data.length = strlen(essid) + 1; + + /* Copy structure to the user buffer */ + if(copy_to_user(wrq->u.data.pointer, essid, wrq->u.data.length)) + ret = -EFAULT; + } + break; + + case SIOCSIWAP: +#ifdef DEBUG_IOCTL_INFO + printk(KERN_DEBUG "Set AP to : %02X:%02X:%02X:%02X:%02X:%02X\n", + wrq->u.ap_addr.sa_data[0], + wrq->u.ap_addr.sa_data[1], + wrq->u.ap_addr.sa_data[2], + wrq->u.ap_addr.sa_data[3], + wrq->u.ap_addr.sa_data[4], + wrq->u.ap_addr.sa_data[5]); +#endif /* DEBUG_IOCTL_INFO */ + + ret = -EOPNOTSUPP; /* Not supported yet */ + break; + + case SIOCGIWAP: + /* Should get the real McCoy instead of own Ethernet address */ + memcpy(wrq->u.ap_addr.sa_data, dev->dev_addr, WAVELAN_ADDR_SIZE); + wrq->u.ap_addr.sa_family = ARPHRD_ETHER; + + ret = -EOPNOTSUPP; /* Not supported yet */ + break; +#endif /* WIRELESS_EXT > 5 */ +#endif /* WAVELAN_ROAMING_EXT */ + +#if WIRELESS_EXT > 8 +#ifdef WAVELAN_ROAMING + case SIOCSIWMODE: + switch(wrq->u.mode) + { + case IW_MODE_ADHOC: + if(do_roaming) + { + wv_roam_cleanup(dev); + do_roaming = 0; + } + break; + case IW_MODE_INFRA: + if(!do_roaming) + { + wv_roam_init(dev); + do_roaming = 1; + } + break; + default: + ret = -EINVAL; + } + break; + + case SIOCGIWMODE: + if(do_roaming) + wrq->u.mode = IW_MODE_INFRA; + else + wrq->u.mode = IW_MODE_ADHOC; + break; +#endif /* WAVELAN_ROAMING */ +#endif /* WIRELESS_EXT > 8 */ + + case SIOCGIWRANGE: + /* Basic checking... */ + if(wrq->u.data.pointer != (caddr_t) 0) + { + struct iw_range range; + + /* Set the length (useless : its constant...) */ + wrq->u.data.length = sizeof(struct iw_range); + + /* Set information in the range struct */ + range.throughput = 1.4 * 1000 * 1000; /* don't argue on this ! */ + range.min_nwid = 0x0000; + range.max_nwid = 0xFFFF; + + /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable) */ + if(!(mmc_in(base, mmroff(0, mmr_fee_status)) & + (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) + { + range.num_channels = 10; + range.num_frequency = wv_frequency_list(base, range.freq, + IW_MAX_FREQUENCIES); + } + else + range.num_channels = range.num_frequency = 0; + + range.sensitivity = 0x3F; + range.max_qual.qual = MMR_SGNL_QUAL; + range.max_qual.level = MMR_SIGNAL_LVL; + range.max_qual.noise = MMR_SILENCE_LVL; + +#if WIRELESS_EXT > 7 + range.num_bitrates = 1; + range.bitrate[0] = 2000000; /* 2 Mb/s */ +#endif /* WIRELESS_EXT > 7 */ + +#if WIRELESS_EXT > 8 + /* Encryption supported ? */ + if(mmc_encr(base)) + { + range.encoding_size[0] = 8; /* DES = 64 bits key */ + range.num_encoding_sizes = 1; + range.max_encoding_tokens = 1; /* Only one key possible */ + } + else + { + range.num_encoding_sizes = 0; + range.max_encoding_tokens = 0; + } +#endif /* WIRELESS_EXT > 8 */ + + /* Copy structure to the user buffer */ + if(copy_to_user(wrq->u.data.pointer, &range, + sizeof(struct iw_range))) + ret = -EFAULT; + } + break; + + case SIOCGIWPRIV: + /* Basic checking... */ + if(wrq->u.data.pointer != (caddr_t) 0) + { + struct iw_priv_args priv[] = + { /* cmd, set_args, get_args, name */ + { SIOCSIPQTHR, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, 0, "setqualthr" }, + { SIOCGIPQTHR, 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, "getqualthr" }, + { SIOCSIPHISTO, IW_PRIV_TYPE_BYTE | 16, 0, "sethisto" }, + { SIOCGIPHISTO, 0, IW_PRIV_TYPE_INT | 16, "gethisto" }, + { SIOCSIPROAM, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1 , 0, "setroam" }, + { SIOCGIPROAM, 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, "getroam" }, + }; + + /* Set the number of ioctl available */ + wrq->u.data.length = 6; + + /* Copy structure to the user buffer */ + if(copy_to_user(wrq->u.data.pointer, (u_char *) priv, + sizeof(priv))) + ret = -EFAULT; + } + break; + +#ifdef WIRELESS_SPY + case SIOCSIWSPY: + /* Set the spy list */ + + /* Check the number of addresses */ + if(wrq->u.data.length > IW_MAX_SPY) + { + ret = -E2BIG; + break; + } + lp->spy_number = wrq->u.data.length; + + /* If there is some addresses to copy */ + if(lp->spy_number > 0) + { + struct sockaddr address[IW_MAX_SPY]; + int i; + + /* Copy addresses to the driver */ + if(copy_from_user(address, wrq->u.data.pointer, + sizeof(struct sockaddr) * lp->spy_number)) + { + ret = -EFAULT; + break; + } + + /* Copy addresses to the lp structure */ + for(i = 0; i < lp->spy_number; i++) + { + memcpy(lp->spy_address[i], address[i].sa_data, + WAVELAN_ADDR_SIZE); + } + + /* Reset structure... */ + memset(lp->spy_stat, 0x00, sizeof(iw_qual) * IW_MAX_SPY); + +#ifdef DEBUG_IOCTL_INFO + printk(KERN_DEBUG "SetSpy - Set of new addresses is :\n"); + for(i = 0; i < wrq->u.data.length; i++) + printk(KERN_DEBUG "%02X:%02X:%02X:%02X:%02X:%02X\n", + lp->spy_address[i][0], + lp->spy_address[i][1], + lp->spy_address[i][2], + lp->spy_address[i][3], + lp->spy_address[i][4], + lp->spy_address[i][5]); +#endif /* DEBUG_IOCTL_INFO */ + } + + break; + + case SIOCGIWSPY: + /* Get the spy list and spy stats */ + + /* Set the number of addresses */ + wrq->u.data.length = lp->spy_number; + + /* If the user want to have the addresses back... */ + if((lp->spy_number > 0) && (wrq->u.data.pointer != (caddr_t) 0)) + { + struct sockaddr address[IW_MAX_SPY]; + int i; + + /* Copy addresses from the lp structure */ + for(i = 0; i < lp->spy_number; i++) + { + memcpy(address[i].sa_data, lp->spy_address[i], + WAVELAN_ADDR_SIZE); + address[i].sa_family = ARPHRD_ETHER; + } + + /* Copy addresses to the user buffer */ + if(copy_to_user(wrq->u.data.pointer, address, + sizeof(struct sockaddr) * lp->spy_number)) + { + ret = -EFAULT; + break; + } + + /* Copy stats to the user buffer (just after) */ + if(copy_to_user(wrq->u.data.pointer + + (sizeof(struct sockaddr) * lp->spy_number), + lp->spy_stat, sizeof(iw_qual) * lp->spy_number)) + { + ret = -EFAULT; + break; + } + + /* Reset updated flags */ + for(i = 0; i < lp->spy_number; i++) + lp->spy_stat[i].updated = 0x0; + } /* if(pointer != NULL) */ + + break; +#endif /* WIRELESS_SPY */ + + /* ------------------ PRIVATE IOCTL ------------------ */ + + case SIOCSIPQTHR: + if(!suser()) + { + ret = -EPERM; + break; + } + psa.psa_quality_thr = *(wrq->u.name) & 0x0F; + psa_write(dev, (char *)&psa.psa_quality_thr - (char *)&psa, + (unsigned char *)&psa.psa_quality_thr, 1); + /* update the Wavelan checksum */ + update_psa_checksum(dev); + mmc_out(base, mmwoff(0, mmw_quality_thr), psa.psa_quality_thr); + break; + + case SIOCGIPQTHR: + psa_read(dev, (char *)&psa.psa_quality_thr - (char *)&psa, + (unsigned char *)&psa.psa_quality_thr, 1); + *(wrq->u.name) = psa.psa_quality_thr & 0x0F; + break; + +#ifdef WAVELAN_ROAMING + case SIOCSIPROAM: + /* Note : should check if user == root */ + if(do_roaming && (*wrq->u.name)==0) + wv_roam_cleanup(dev); + else if(do_roaming==0 && (*wrq->u.name)!=0) + wv_roam_init(dev); + + do_roaming = (*wrq->u.name); + + break; + + case SIOCGIPROAM: + *(wrq->u.name) = do_roaming; + break; +#endif /* WAVELAN_ROAMING */ + +#ifdef HISTOGRAM + case SIOCSIPHISTO: + /* Verif if the user is root */ + if(!suser()) + { + ret = -EPERM; + } + + /* Check the number of intervals */ + if(wrq->u.data.length > 16) + { + ret = -E2BIG; + break; + } + lp->his_number = wrq->u.data.length; + + /* If there is some addresses to copy */ + if(lp->his_number > 0) + { + /* Copy interval ranges to the driver */ + if(copy_from_user(lp->his_range, wrq->u.data.pointer, + sizeof(char) * lp->his_number)) + { + ret = -EFAULT; + break; + } + + /* Reset structure... */ + memset(lp->his_sum, 0x00, sizeof(long) * 16); + } + break; + + case SIOCGIPHISTO: + /* Set the number of intervals */ + wrq->u.data.length = lp->his_number; + + /* Give back the distribution statistics */ + if((lp->his_number > 0) && (wrq->u.data.pointer != (caddr_t) 0)) + { + /* Copy data to the user buffer */ + if(copy_to_user(wrq->u.data.pointer, lp->his_sum, + sizeof(long) * lp->his_number)) + ret = -EFAULT; + + } /* if(pointer != NULL) */ + break; +#endif /* HISTOGRAM */ + + /* ------------------- OTHER IOCTL ------------------- */ + + default: + ret = -EOPNOTSUPP; + } + + /* ReEnable interrupts & restore flags */ + wv_splx(x); + +#ifdef DEBUG_IOCTL_TRACE + printk(KERN_DEBUG "%s: <-wavelan_ioctl()\n", dev->name); +#endif + return ret; +} + +/*------------------------------------------------------------------*/ +/* + * Get wireless statistics + * Called by /proc/net/wireless... + */ +static iw_stats * +wavelan_get_wireless_stats(device * dev) +{ + ioaddr_t base = dev->base_addr; + net_local * lp = (net_local *) dev->priv; + mmr_t m; + iw_stats * wstats; + unsigned long x; + +#ifdef DEBUG_IOCTL_TRACE + printk(KERN_DEBUG "%s: ->wavelan_get_wireless_stats()\n", dev->name); +#endif + + /* Disable interrupts & save flags */ + x = wv_splhi(); + + if(lp == (net_local *) NULL) + return (iw_stats *) NULL; + wstats = &lp->wstats; + + /* Get data from the mmc */ + mmc_out(base, mmwoff(0, mmw_freeze), 1); + + mmc_read(base, mmroff(0, mmr_dce_status), &m.mmr_dce_status, 1); + mmc_read(base, mmroff(0, mmr_wrong_nwid_l), &m.mmr_wrong_nwid_l, 2); + mmc_read(base, mmroff(0, mmr_thr_pre_set), &m.mmr_thr_pre_set, 4); + + mmc_out(base, mmwoff(0, mmw_freeze), 0); + + /* Copy data to wireless stuff */ + wstats->status = m.mmr_dce_status & MMR_DCE_STATUS; + wstats->qual.qual = m.mmr_sgnl_qual & MMR_SGNL_QUAL; + wstats->qual.level = m.mmr_signal_lvl & MMR_SIGNAL_LVL; + wstats->qual.noise = m.mmr_silence_lvl & MMR_SILENCE_LVL; + wstats->qual.updated = (((m.mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) >> 7) | + ((m.mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) >> 6) | + ((m.mmr_silence_lvl & MMR_SILENCE_LVL_VALID) >> 5)); + wstats->discard.nwid += (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l; + wstats->discard.code = 0L; + wstats->discard.misc = 0L; + + /* ReEnable interrupts & restore flags */ + wv_splx(x); + +#ifdef DEBUG_IOCTL_TRACE + printk(KERN_DEBUG "%s: <-wavelan_get_wireless_stats()\n", dev->name); +#endif + return &lp->wstats; +} +#endif /* WIRELESS_EXT */ + +/************************* PACKET RECEPTION *************************/ +/* + * This part deal with receiving the packets. + * The interrupt handler get an interrupt when a packet has been + * successfully received and called this part... + */ + +/*------------------------------------------------------------------*/ +/* + * Calculate the starting address of the frame pointed to by the receive + * frame pointer and verify that the frame seem correct + * (called by wv_packet_rcv()) + */ +static inline int +wv_start_of_frame(device * dev, + int rfp, /* end of frame */ + int wrap) /* start of buffer */ +{ + ioaddr_t base = dev->base_addr; + int rp; + int len; + + rp = (rfp - 5 + RX_SIZE) % RX_SIZE; + outb(rp & 0xff, PIORL(base)); + outb(((rp >> 8) & PIORH_MASK), PIORH(base)); + len = inb(PIOP(base)); + len |= inb(PIOP(base)) << 8; + + /* Sanity checks on size */ + /* Frame too big */ + if(len > MAXDATAZ + 100) + { +#ifdef DEBUG_RX_ERROR + printk(KERN_INFO "%s: wv_start_of_frame: Received frame too large, rfp %d len 0x%x\n", + dev->name, rfp, len); +#endif + return(-1); + } + + /* Frame too short */ + if(len < 7) + { +#ifdef DEBUG_RX_ERROR + printk(KERN_INFO "%s: wv_start_of_frame: Received null frame, rfp %d len 0x%x\n", + dev->name, rfp, len); +#endif + return(-1); + } + + /* Wrap around buffer */ + if(len > ((wrap - (rfp - len) + RX_SIZE) % RX_SIZE)) /* magic formula ! */ + { +#ifdef DEBUG_RX_ERROR + printk(KERN_INFO "%s: wv_start_of_frame: wrap around buffer, wrap %d rfp %d len 0x%x\n", + dev->name, wrap, rfp, len); +#endif + return(-1); + } + + return((rp - len + RX_SIZE) % RX_SIZE); +} /* wv_start_of_frame */ + +/*------------------------------------------------------------------*/ +/* + * This routine does the actual copy of data (including the ethernet + * header structure) from the WaveLAN card to an sk_buff chain that + * will be passed up to the network interface layer. NOTE: We + * currently don't handle trailer protocols (neither does the rest of + * the network interface), so if that is needed, it will (at least in + * part) be added here. The contents of the receive ring buffer are + * copied to a message chain that is then passed to the kernel. + * + * Note: if any errors occur, the packet is "dropped on the floor" + * (called by wv_packet_rcv()) + */ +static inline void +wv_packet_read(device * dev, + int fd_p, + int sksize) +{ + net_local * lp = (net_local *) dev->priv; + struct sk_buff * skb; + +#ifdef DEBUG_RX_TRACE + printk(KERN_DEBUG "%s: ->wv_packet_read(0x%X, %d)\n", + dev->name, fd_p, sksize); +#endif + + /* Allocate some buffer for the new packet */ + if((skb = dev_alloc_skb(sksize+2)) == (struct sk_buff *) NULL) + { +#ifdef DEBUG_RX_ERROR + printk(KERN_INFO "%s: wv_packet_read(): could not alloc_skb(%d, GFP_ATOMIC)\n", + dev->name, sksize); +#endif + lp->stats.rx_dropped++; + /* + * Not only do we want to return here, but we also need to drop the + * packet on the floor to clear the interrupt. + */ + return; + } + + skb->dev = dev; + + skb_reserve(skb, 2); + fd_p = read_ringbuf(dev, fd_p, (char *) skb_put(skb, sksize), sksize); + skb->protocol = eth_type_trans(skb, dev); + +#ifdef DEBUG_RX_INFO + /* Another glitch : Due to the way the GET_PACKET macro is written, + * we are not sure to have the same thing in skb->data. On the other + * hand, skb->mac.raw is not defined everywhere... + * For versions between 1.2.13 and those where skb->mac.raw appear, + * I don't have a clue... + */ + wv_packet_info(skb->mac.raw, sksize, dev->name, "wv_packet_read"); +#endif /* DEBUG_RX_INFO */ + + /* Statistics gathering & stuff associated. + * It seem a bit messy with all the define, but it's really simple... */ + if( +#ifdef WIRELESS_SPY + (lp->spy_number > 0) || +#endif /* WIRELESS_SPY */ +#ifdef HISTOGRAM + (lp->his_number > 0) || +#endif /* HISTOGRAM */ +#ifdef WAVELAN_ROAMING + (do_roaming) || +#endif /* WAVELAN_ROAMING */ + 0) + { + u_char stats[3]; /* Signal level, Noise level, Signal quality */ + + /* read signal level, silence level and signal quality bytes */ + fd_p = read_ringbuf(dev, (fd_p + 4) % RX_SIZE + RX_BASE, + stats, 3); +#ifdef DEBUG_RX_INFO + printk(KERN_DEBUG "%s: wv_packet_read(): Signal level %d/63, Silence level %d/63, signal quality %d/16\n", + dev->name, stats[0] & 0x3F, stats[1] & 0x3F, stats[2] & 0x0F); +#endif + +#ifdef WAVELAN_ROAMING + if(do_roaming) + if(WAVELAN_BEACON(skb->data)) + wl_roam_gather(dev, skb->data, stats); +#endif /* WAVELAN_ROAMING */ + + /* Spying stuff */ +#ifdef WIRELESS_SPY + /* Same as above */ + wl_spy_gather(dev, skb->mac.raw + WAVELAN_ADDR_SIZE, stats); +#endif /* WIRELESS_SPY */ +#ifdef HISTOGRAM + wl_his_gather(dev, stats); +#endif /* HISTOGRAM */ + } + + /* + * Hand the packet to the Network Module + */ + netif_rx(skb); + + /* Keep stats up to date */ + lp->stats.rx_packets++; + lp->stats.rx_bytes += skb->len; + +#ifdef DEBUG_RX_TRACE + printk(KERN_DEBUG "%s: <-wv_packet_read()\n", dev->name); +#endif + return; +} + +/*------------------------------------------------------------------*/ +/* + * This routine is called by the interrupt handler to initiate a + * packet transfer from the card to the network interface layer above + * this driver. This routine checks if a buffer has been successfully + * received by the WaveLAN card. If so, the routine wv_packet_read is + * called to do the actual transfer of the card's data including the + * ethernet header into a packet consisting of an sk_buff chain. + * (called by wavelan_interrupt()) + */ +static inline void +wv_packet_rcv(device * dev) +{ + ioaddr_t base = dev->base_addr; + net_local * lp = (net_local *) dev->priv; + int newrfp; + int rp; + int len; + int f_start; + int status; + int i593_rfp; + int stat_ptr; + u_char c[4]; + +#ifdef DEBUG_RX_TRACE + printk(KERN_DEBUG "%s: ->wv_packet_rcv()\n", dev->name); +#endif + + /* Get the new receive frame pointer from the i82593 chip */ + outb(CR0_STATUS_2 | OP0_NOP, LCCR(base)); + i593_rfp = inb(LCSR(base)); + i593_rfp |= inb(LCSR(base)) << 8; + i593_rfp %= RX_SIZE; + + /* Get the new receive frame pointer from the WaveLAN card. + * It is 3 bytes more than the increment of the i82593 receive + * frame pointer, for each packet. This is because it includes the + * 3 roaming bytes added by the mmc. + */ + newrfp = inb(RPLL(base)); + newrfp |= inb(RPLH(base)) << 8; + newrfp %= RX_SIZE; + +#ifdef DEBUG_RX_INFO + printk(KERN_DEBUG "%s: wv_packet_rcv(): i593_rfp %d stop %d newrfp %d lp->rfp %d\n", + dev->name, i593_rfp, lp->stop, newrfp, lp->rfp); +#endif + +#ifdef DEBUG_RX_ERROR + /* If no new frame pointer... */ + if(lp->overrunning || newrfp == lp->rfp) + printk(KERN_INFO "%s: wv_packet_rcv(): no new frame: i593_rfp %d stop %d newrfp %d lp->rfp %d\n", + dev->name, i593_rfp, lp->stop, newrfp, lp->rfp); +#endif + + /* Read all frames (packets) received */ + while(newrfp != lp->rfp) + { + /* A frame is composed of the packet, followed by a status word, + * the length of the frame (word) and the mmc info (SNR & qual). + * It's because the length is at the end that we can only scan + * frames backward. */ + + /* Find the first frame by skipping backwards over the frames */ + rp = newrfp; /* End of last frame */ + while(((f_start = wv_start_of_frame(dev, rp, newrfp)) != lp->rfp) && + (f_start != -1)) + rp = f_start; + + /* If we had a problem */ + if(f_start == -1) + { +#ifdef DEBUG_RX_ERROR + printk(KERN_INFO "wavelan_cs: cannot find start of frame "); + printk(" i593_rfp %d stop %d newrfp %d lp->rfp %d\n", + i593_rfp, lp->stop, newrfp, lp->rfp); +#endif + lp->rfp = rp; /* Get to the last usable frame */ + continue; + } + + /* f_start point to the beggining of the first frame received + * and rp to the beggining of the next one */ + + /* Read status & length of the frame */ + stat_ptr = (rp - 7 + RX_SIZE) % RX_SIZE; + stat_ptr = read_ringbuf(dev, stat_ptr, c, 4); + status = c[0] | (c[1] << 8); + len = c[2] | (c[3] << 8); + + /* Check status */ + if((status & RX_RCV_OK) != RX_RCV_OK) + { + lp->stats.rx_errors++; + if(status & RX_NO_SFD) + lp->stats.rx_frame_errors++; + if(status & RX_CRC_ERR) + lp->stats.rx_crc_errors++; + if(status & RX_OVRRUN) + lp->stats.rx_over_errors++; + +#ifdef DEBUG_RX_FAIL + printk(KERN_DEBUG "%s: wv_packet_rcv(): packet not received ok, status = 0x%x\n", + dev->name, status); +#endif + } + else + /* Read the packet and transmit to Linux */ + wv_packet_read(dev, f_start, len - 2); + + /* One frame has been processed, skip it */ + lp->rfp = rp; + } + + /* + * Update the frame stop register, but set it to less than + * the full 8K to allow space for 3 bytes of signal strength + * per packet. + */ + lp->stop = (i593_rfp + RX_SIZE - ((RX_SIZE / 64) * 3)) % RX_SIZE; + outb(OP0_SWIT_TO_PORT_1 | CR0_CHNL, LCCR(base)); + outb(CR1_STOP_REG_UPDATE | (lp->stop >> RX_SIZE_SHIFT), LCCR(base)); + outb(OP1_SWIT_TO_PORT_0, LCCR(base)); + +#ifdef DEBUG_RX_TRACE + printk(KERN_DEBUG "%s: <-wv_packet_rcv()\n", dev->name); +#endif +} + +/*********************** PACKET TRANSMISSION ***********************/ +/* + * This part deal with sending packet through the wavelan + * We copy the packet to the send buffer and then issue the send + * command to the i82593. The result of this operation will be + * checked in wavelan_interrupt() + */ + +/*------------------------------------------------------------------*/ +/* + * This routine fills in the appropriate registers and memory + * locations on the WaveLAN card and starts the card off on + * the transmit. + * (called in wavelan_packet_xmit()) + */ +static inline void +wv_packet_write(device * dev, + void * buf, + short length) +{ + net_local * lp = (net_local *) dev->priv; + ioaddr_t base = dev->base_addr; + unsigned long x; + int clen = length; + register u_short xmtdata_base = TX_BASE; + +#ifdef DEBUG_TX_TRACE + printk(KERN_DEBUG "%s: ->wv_packet_write(%d)\n", dev->name, length); +#endif + + x = wv_splhi(); + + /* Check if we need some padding */ + if(clen < ETH_ZLEN) + clen = ETH_ZLEN; + + /* Write the length of data buffer followed by the buffer */ + outb(xmtdata_base & 0xff, PIORL(base)); + outb(((xmtdata_base >> 8) & PIORH_MASK) | PIORH_SEL_TX, PIORH(base)); + outb(clen & 0xff, PIOP(base)); /* lsb */ + outb(clen >> 8, PIOP(base)); /* msb */ + + /* Send the data */ + outsb(PIOP(base), buf, clen); + + /* Indicate end of transmit chain */ + outb(OP0_NOP, PIOP(base)); + /* josullvn@cs.cmu.edu: need to send a second NOP for alignment... */ + outb(OP0_NOP, PIOP(base)); + + /* Reset the transmit DMA pointer */ + hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET); + hacr_write(base, HACR_DEFAULT); + /* Send the transmit command */ + wv_82593_cmd(dev, "wv_packet_write(): transmit", + OP0_TRANSMIT, SR0_NO_RESULT); + + /* Keep stats up to date */ + lp->stats.tx_bytes += length; + + /* If watchdog not already active, activate it... */ + if(lp->watchdog.prev == (timer_list *) NULL) + { + /* set timer to expire in WATCHDOG_JIFFIES */ + lp->watchdog.expires = jiffies + WATCHDOG_JIFFIES; + add_timer(&lp->watchdog); + } + + wv_splx(x); + +#ifdef DEBUG_TX_INFO + wv_packet_info((u_char *) buf, length, dev->name, "wv_packet_write"); +#endif /* DEBUG_TX_INFO */ + +#ifdef DEBUG_TX_TRACE + printk(KERN_DEBUG "%s: <-wv_packet_write()\n", dev->name); +#endif +} + +/*------------------------------------------------------------------*/ +/* + * This routine is called when we want to send a packet (NET3 callback) + * In this routine, we check if the the harware is ready to accept + * the packet. We also prevent reentrance. Then, we call the function + * to send the packet... + */ +static int +wavelan_packet_xmit(struct sk_buff * skb, + device * dev) +{ + net_local * lp = (net_local *)dev->priv; + +#ifdef DEBUG_TX_TRACE + printk(KERN_DEBUG "%s: ->wavelan_packet_xmit(0x%X)\n", dev->name, + (unsigned) skb); +#endif + + /* This flag indicate that the hardware can't perform a transmission. + * Theoritically, NET3 check it before sending a packet to the driver, + * but in fact it never do that and pool continuously. + * As the watchdog will abort too long transmissions, we are quite safe... + */ + if(dev->tbusy) + return(1); + + /* + * For ethernet, fill in the header. + */ + + /* + * Block a timer-based transmit from overlapping a previous transmit. + * In other words, prevent reentering this routine. + */ + if(test_and_set_bit(0, (void *)&dev->tbusy) != 0) +#ifdef DEBUG_TX_ERROR + printk(KERN_INFO "%s: Transmitter access conflict.\n", dev->name); +#endif + else + { + /* If somebody has asked to reconfigure the controler, we can do it now */ + if(lp->reconfig_82593) + { + lp->reconfig_82593 = FALSE; + wv_82593_config(dev); + } + +#ifdef DEBUG_TX_ERROR + if(skb->next) + printk(KERN_INFO "skb has next\n"); +#endif + + wv_packet_write(dev, skb->data, skb->len); + } + + dev_kfree_skb(skb); + +#ifdef DEBUG_TX_TRACE + printk(KERN_DEBUG "%s: <-wavelan_packet_xmit()\n", dev->name); +#endif + return(0); +} + +/********************** HARDWARE CONFIGURATION **********************/ +/* + * This part do the real job of starting and configuring the hardware. + */ + +/*------------------------------------------------------------------*/ +/* + * Routine to initialize the Modem Management Controller. + * (called by wv_hw_config()) + */ +static inline int +wv_mmc_init(device * dev) +{ + ioaddr_t base = dev->base_addr; + psa_t psa; + mmw_t m; + int configured; + int i; /* Loop counter */ + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: ->wv_mmc_init()\n", dev->name); +#endif + + /* Read the parameter storage area */ + psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa)); + + /* + * Check the first three octets of the MAC addr for the manufacturer's code. + * Note: If you get the error message below, you've got a + * non-NCR/AT&T/Lucent PCMCIA cards, see wavelan_cs.h for detail on + * how to configure your card... + */ + for(i = 0; i < (sizeof(MAC_ADDRESSES) / sizeof(char) / 3); i++) + if((psa.psa_univ_mac_addr[0] == MAC_ADDRESSES[i][0]) && + (psa.psa_univ_mac_addr[1] == MAC_ADDRESSES[i][1]) && + (psa.psa_univ_mac_addr[2] == MAC_ADDRESSES[i][2])) + break; + + /* If we have not found it... */ + if(i == (sizeof(MAC_ADDRESSES) / sizeof(char) / 3)) + { +#ifdef DEBUG_CONFIG_ERRORS + printk(KERN_WARNING "%s: wv_mmc_init(): Invalid MAC address: %02X:%02X:%02X:...\n", + dev->name, psa.psa_univ_mac_addr[0], + psa.psa_univ_mac_addr[1], psa.psa_univ_mac_addr[2]); +#endif + return FALSE; + } + + /* Get the MAC address */ + memcpy(&dev->dev_addr[0], &psa.psa_univ_mac_addr[0], WAVELAN_ADDR_SIZE); + +#ifdef USE_PSA_CONFIG + configured = psa.psa_conf_status & 1; +#else + configured = 0; +#endif + + /* Is the PSA is not configured */ + if(!configured) + { + /* User will be able to configure NWID after (with iwconfig) */ + psa.psa_nwid[0] = 0; + psa.psa_nwid[1] = 0; + + /* As NWID is not set : no NWID checking */ + psa.psa_nwid_select = 0; + + /* Disable encryption */ + psa.psa_encryption_select = 0; + + /* Set to standard values + * 0x04 for AT, + * 0x01 for MCA, + * 0x04 for PCMCIA and 2.00 card (AT&T 407-024689/E document) + */ + if (psa.psa_comp_number & 1) + psa.psa_thr_pre_set = 0x01; + else + psa.psa_thr_pre_set = 0x04; + psa.psa_quality_thr = 0x03; + + /* It is configured */ + psa.psa_conf_status |= 1; + +#ifdef USE_PSA_CONFIG + /* Write the psa */ + psa_write(dev, (char *)psa.psa_nwid - (char *)&psa, + (unsigned char *)psa.psa_nwid, 4); + psa_write(dev, (char *)&psa.psa_thr_pre_set - (char *)&psa, + (unsigned char *)&psa.psa_thr_pre_set, 1); + psa_write(dev, (char *)&psa.psa_quality_thr - (char *)&psa, + (unsigned char *)&psa.psa_quality_thr, 1); + psa_write(dev, (char *)&psa.psa_conf_status - (char *)&psa, + (unsigned char *)&psa.psa_conf_status, 1); + /* update the Wavelan checksum */ + update_psa_checksum(dev); +#endif /* USE_PSA_CONFIG */ + } + + /* Zero the mmc structure */ + memset(&m, 0x00, sizeof(m)); + + /* Copy PSA info to the mmc */ + m.mmw_netw_id_l = psa.psa_nwid[1]; + m.mmw_netw_id_h = psa.psa_nwid[0]; + + if(psa.psa_nwid_select & 1) + m.mmw_loopt_sel = 0x00; + else + m.mmw_loopt_sel = MMW_LOOPT_SEL_DIS_NWID; + + memcpy(&m.mmw_encr_key, &psa.psa_encryption_key, + sizeof(m.mmw_encr_key)); + + if(psa.psa_encryption_select) + m.mmw_encr_enable = MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE; + else + m.mmw_encr_enable = 0; + + m.mmw_thr_pre_set = psa.psa_thr_pre_set & 0x3F; + m.mmw_quality_thr = psa.psa_quality_thr & 0x0F; + + /* + * Set default modem control parameters. + * See NCR document 407-0024326 Rev. A. + */ + m.mmw_jabber_enable = 0x01; + m.mmw_anten_sel = MMW_ANTEN_SEL_ALG_EN; + m.mmw_ifs = 0x20; + m.mmw_mod_delay = 0x04; + m.mmw_jam_time = 0x38; + + m.mmw_des_io_invert = 0; + m.mmw_freeze = 0; + m.mmw_decay_prm = 0; + m.mmw_decay_updat_prm = 0; + + /* Write all info to mmc */ + mmc_write(base, 0, (u_char *)&m, sizeof(m)); + + /* The following code start the modem of the 2.00 frequency + * selectable cards at power on. It's not strictly needed for the + * following boots... + * The original patch was by Joe Finney for the PCMCIA driver, but + * I've cleaned it a bit and add documentation. + * Thanks to Loeke Brederveld from Lucent for the info. + */ + + /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable) + * (does it work for everybody ??? - especially old cards...) */ + /* Note : WFREQSEL verify that it is able to read from EEprom + * a sensible frequency (address 0x00) + that MMR_FEE_STATUS_ID + * is 0xA (Xilinx version) or 0xB (Ariadne version). + * My test is more crude but do work... */ + if(!(mmc_in(base, mmroff(0, mmr_fee_status)) & + (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) + { + /* We must download the frequency parameters to the + * synthetisers (from the EEprom - area 1) + * Note : as the EEprom is auto decremented, we set the end + * if the area... */ + m.mmw_fee_addr = 0x0F; + m.mmw_fee_ctrl = MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD; + mmc_write(base, (char *)&m.mmw_fee_ctrl - (char *)&m, + (unsigned char *)&m.mmw_fee_ctrl, 2); + + /* Wait until the download is finished */ + fee_wait(base, 100, 100); + +#ifdef DEBUG_CONFIG_INFO + /* The frequency was in the last word downloaded... */ + mmc_read(base, (char *)&m.mmw_fee_data_l - (char *)&m, + (unsigned char *)&m.mmw_fee_data_l, 2); + + /* Print some info for the user */ + printk(KERN_DEBUG "%s: Wavelan 2.00 recognised (frequency select) : Current frequency = %ld\n", + dev->name, + ((m.mmw_fee_data_h << 4) | + (m.mmw_fee_data_l >> 4)) * 5 / 2 + 24000L); +#endif + + /* We must now download the power adjust value (gain) to + * the synthetisers (from the EEprom - area 7 - DAC) */ + m.mmw_fee_addr = 0x61; + m.mmw_fee_ctrl = MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD; + mmc_write(base, (char *)&m.mmw_fee_ctrl - (char *)&m, + (unsigned char *)&m.mmw_fee_ctrl, 2); + + /* Wait until the download is finished */ + } /* if 2.00 card */ + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: <-wv_mmc_init()\n", dev->name); +#endif + return TRUE; +} + +/*------------------------------------------------------------------*/ +/* + * Routine to gracefully turn off reception, and wait for any commands + * to complete. + * (called in wv_ru_start() and wavelan_close() and wavelan_event()) + */ +static int +wv_ru_stop(device * dev) +{ + ioaddr_t base = dev->base_addr; + unsigned long opri; + int status; + int spin; + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: ->wv_ru_stop()\n", dev->name); +#endif + + /* First, send the LAN controller a stop receive command */ + wv_82593_cmd(dev, "wv_graceful_shutdown(): stop-rcv", + OP0_STOP_RCV, SR0_NO_RESULT); + + /* Then, spin until the receive unit goes idle */ + spin = 0; + do + { + udelay(10); + opri = wv_splhi(); + outb(OP0_NOP | CR0_STATUS_3, LCCR(base)); + status = inb(LCSR(base)); + wv_splx(opri); + } + while(((status & SR3_RCV_STATE_MASK) != SR3_RCV_IDLE) && (spin++ < 300)); + + /* Now, spin until the chip finishes executing its current command */ + do + { + udelay(10); + opri = wv_splhi(); + outb(OP0_NOP | CR0_STATUS_3, LCCR(base)); + status = inb(LCSR(base)); + wv_splx(opri); + } + while(((status & SR3_EXEC_STATE_MASK) != SR3_EXEC_IDLE) && (spin++ < 300)); + + /* If there was a problem */ + if(spin > 300) + { +#ifdef DEBUG_CONFIG_ERROR + printk(KERN_INFO "%s: wv_ru_stop(): The chip doesn't want to stop...\n", + dev->name); +#endif + return FALSE; + } + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: <-wv_ru_stop()\n", dev->name); +#endif + return TRUE; +} /* wv_ru_stop */ + +/*------------------------------------------------------------------*/ +/* + * This routine starts the receive unit running. First, it checks if + * the card is actually ready. Then the card is instructed to receive + * packets again. + * (called in wv_hw_reset() & wavelan_open()) + */ +static int +wv_ru_start(device * dev) +{ + ioaddr_t base = dev->base_addr; + net_local * lp = (net_local *) dev->priv; + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: ->wv_ru_start()\n", dev->name); +#endif + + /* + * We need to start from a quiescent state. To do so, we could check + * if the card is already running, but instead we just try to shut + * it down. First, we disable reception (in case it was already enabled). + */ + if(!wv_ru_stop(dev)) + return FALSE; + + /* Now we know that no command is being executed. */ + + /* Set the receive frame pointer and stop pointer */ + lp->rfp = 0; + outb(OP0_SWIT_TO_PORT_1 | CR0_CHNL, LCCR(base)); + + /* Reset ring management. This sets the receive frame pointer to 1 */ + outb(OP1_RESET_RING_MNGMT, LCCR(base)); + + /* but I set it to 3 bytes per packet less than 8K */ + lp->stop = (0 + RX_SIZE - ((RX_SIZE / 64) * 3)) % RX_SIZE; + outb(CR1_STOP_REG_UPDATE | (lp->stop >> RX_SIZE_SHIFT), LCCR(base)); + outb(OP1_INT_ENABLE, LCCR(base)); + outb(OP1_SWIT_TO_PORT_0, LCCR(base)); + + /* Reset receive DMA pointer */ + hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET); + hacr_write_slow(base, HACR_DEFAULT); + + /* Receive DMA on channel 1 */ + wv_82593_cmd(dev, "wv_ru_start(): rcv-enable", + CR0_CHNL | OP0_RCV_ENABLE, SR0_NO_RESULT); + +#ifdef DEBUG_I82593_SHOW + { + int status; + int opri; + int i = 0; + + /* spin until the chip starts receiving */ + do + { + opri = wv_splhi(); + outb(OP0_NOP | CR0_STATUS_3, LCCR(base)); + status = inb(LCSR(base)); + wv_splx(opri); + if(i++ > 10000) + break; + } + while(((status & SR3_RCV_STATE_MASK) != SR3_RCV_ACTIVE) && + ((status & SR3_RCV_STATE_MASK) != SR3_RCV_READY)); + printk(KERN_DEBUG "rcv status is 0x%x [i:%d]\n", + (status & SR3_RCV_STATE_MASK), i); + } +#endif +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: <-wv_ru_start()\n", dev->name); +#endif + return TRUE; +} + +/*------------------------------------------------------------------*/ +/* + * This routine does a standard config of the WaveLAN controler (i82593). + * In the ISA driver, this is integrated in wavelan_hardware_reset() + * (called by wv_hw_config(), wv_82593_reconfig() & wavelan_packet_xmit()) + */ +static int +wv_82593_config(device * dev) +{ + ioaddr_t base = dev->base_addr; + net_local * lp = (net_local *) dev->priv; + struct i82593_conf_block cfblk; + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: ->wv_82593_config()\n", dev->name); +#endif + + /* Create & fill i82593 config block + * + * Now conform to Wavelan document WCIN085B + */ + memset(&cfblk, 0x00, sizeof(struct i82593_conf_block)); + cfblk.d6mod = FALSE; /* Run in i82593 advanced mode */ + cfblk.fifo_limit = 5; /* = 56 B rx and 40 B tx fifo thresholds */ + cfblk.forgnesi = FALSE; /* 0=82C501, 1=AMD7992B compatibility */ + cfblk.fifo_32 = 1; + cfblk.throttle_enb = FALSE; + cfblk.contin = TRUE; /* enable continuous mode */ + cfblk.cntrxint = FALSE; /* enable continuous mode receive interrupts */ + cfblk.addr_len = WAVELAN_ADDR_SIZE; + cfblk.acloc = TRUE; /* Disable source addr insertion by i82593 */ + cfblk.preamb_len = 0; /* 2 bytes preamble (SFD) */ + cfblk.loopback = FALSE; + cfblk.lin_prio = 0; /* conform to 802.3 backoff algoritm */ + cfblk.exp_prio = 5; /* conform to 802.3 backoff algoritm */ + cfblk.bof_met = 1; /* conform to 802.3 backoff algoritm */ + cfblk.ifrm_spc = 0x20; /* 32 bit times interframe spacing */ + cfblk.slottim_low = 0x20; /* 32 bit times slot time */ + cfblk.slottim_hi = 0x0; + cfblk.max_retr = 15; + cfblk.prmisc = ((lp->promiscuous) ? TRUE: FALSE); /* Promiscuous mode */ + cfblk.bc_dis = FALSE; /* Enable broadcast reception */ + cfblk.crs_1 = TRUE; /* Transmit without carrier sense */ + cfblk.nocrc_ins = FALSE; /* i82593 generates CRC */ + cfblk.crc_1632 = FALSE; /* 32-bit Autodin-II CRC */ + cfblk.crs_cdt = FALSE; /* CD not to be interpreted as CS */ + cfblk.cs_filter = 0; /* CS is recognized immediately */ + cfblk.crs_src = FALSE; /* External carrier sense */ + cfblk.cd_filter = 0; /* CD is recognized immediately */ + cfblk.min_fr_len = ETH_ZLEN >> 2; /* Minimum frame length 64 bytes */ + cfblk.lng_typ = FALSE; /* Length field > 1500 = type field */ + cfblk.lng_fld = TRUE; /* Disable 802.3 length field check */ + cfblk.rxcrc_xf = TRUE; /* Don't transfer CRC to memory */ + cfblk.artx = TRUE; /* Disable automatic retransmission */ + cfblk.sarec = TRUE; /* Disable source addr trig of CD */ + cfblk.tx_jabber = TRUE; /* Disable jabber jam sequence */ + cfblk.hash_1 = FALSE; /* Use bits 0-5 in mc address hash */ + cfblk.lbpkpol = TRUE; /* Loopback pin active high */ + cfblk.fdx = FALSE; /* Disable full duplex operation */ + cfblk.dummy_6 = 0x3f; /* all ones */ + cfblk.mult_ia = FALSE; /* No multiple individual addresses */ + cfblk.dis_bof = FALSE; /* Disable the backoff algorithm ?! */ + cfblk.dummy_1 = TRUE; /* set to 1 */ + cfblk.tx_ifs_retrig = 3; /* Hmm... Disabled */ +#ifdef MULTICAST_ALL + cfblk.mc_all = (lp->allmulticast ? TRUE: FALSE); /* Allow all multicasts */ +#else + cfblk.mc_all = FALSE; /* No multicast all mode */ +#endif + cfblk.rcv_mon = 0; /* Monitor mode disabled */ + cfblk.frag_acpt = TRUE; /* Do not accept fragments */ + cfblk.tstrttrs = FALSE; /* No start transmission threshold */ + cfblk.fretx = TRUE; /* FIFO automatic retransmission */ + cfblk.syncrqs = FALSE; /* Synchronous DRQ deassertion... */ + cfblk.sttlen = TRUE; /* 6 byte status registers */ + cfblk.rx_eop = TRUE; /* Signal EOP on packet reception */ + cfblk.tx_eop = TRUE; /* Signal EOP on packet transmission */ + cfblk.rbuf_size = RX_SIZE>>11; /* Set receive buffer size */ + cfblk.rcvstop = TRUE; /* Enable Receive Stop Register */ + +#ifdef DEBUG_I82593_SHOW + { + u_char *c = (u_char *) &cfblk; + int i; + printk(KERN_DEBUG "wavelan_cs: config block:"); + for(i = 0; i < sizeof(struct i82593_conf_block); i++,c++) + { + if((i % 16) == 0) printk("\n" KERN_DEBUG); + printk("%02x ", *c); + } + printk("\n"); + } +#endif + + /* Copy the config block to the i82593 */ + outb(TX_BASE & 0xff, PIORL(base)); + outb(((TX_BASE >> 8) & PIORH_MASK) | PIORH_SEL_TX, PIORH(base)); + outb(sizeof(struct i82593_conf_block) & 0xff, PIOP(base)); /* lsb */ + outb(sizeof(struct i82593_conf_block) >> 8, PIOP(base)); /* msb */ + outsb(PIOP(base), (char *) &cfblk, sizeof(struct i82593_conf_block)); + + /* reset transmit DMA pointer */ + hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET); + hacr_write(base, HACR_DEFAULT); + if(!wv_82593_cmd(dev, "wv_82593_config(): configure", + OP0_CONFIGURE, SR0_CONFIGURE_DONE)) + return(FALSE); + + /* Initialize adapter's ethernet MAC address */ + outb(TX_BASE & 0xff, PIORL(base)); + outb(((TX_BASE >> 8) & PIORH_MASK) | PIORH_SEL_TX, PIORH(base)); + outb(WAVELAN_ADDR_SIZE, PIOP(base)); /* byte count lsb */ + outb(0, PIOP(base)); /* byte count msb */ + outsb(PIOP(base), &dev->dev_addr[0], WAVELAN_ADDR_SIZE); + + /* reset transmit DMA pointer */ + hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET); + hacr_write(base, HACR_DEFAULT); + if(!wv_82593_cmd(dev, "wv_82593_config(): ia-setup", + OP0_IA_SETUP, SR0_IA_SETUP_DONE)) + return(FALSE); + +#ifdef WAVELAN_ROAMING + /* If roaming is enabled, join the "Beacon Request" multicast group... */ + /* But only if it's not in there already! */ + if(do_roaming) + dev_mc_add(dev,WAVELAN_BEACON_ADDRESS, WAVELAN_ADDR_SIZE, 1); +#endif /* WAVELAN_ROAMING */ + + /* If any multicast address to set */ + if(lp->mc_count) + { + struct dev_mc_list * dmi; + int addrs_len = WAVELAN_ADDR_SIZE * lp->mc_count; + +#ifdef DEBUG_CONFIG_INFO + printk(KERN_DEBUG "%s: wv_hw_config(): set %d multicast addresses:\n", + dev->name, lp->mc_count); + for(dmi=dev->mc_list; dmi; dmi=dmi->next) + printk(KERN_DEBUG " %02x:%02x:%02x:%02x:%02x:%02x\n", + dmi->dmi_addr[0], dmi->dmi_addr[1], dmi->dmi_addr[2], + dmi->dmi_addr[3], dmi->dmi_addr[4], dmi->dmi_addr[5] ); +#endif + + /* Initialize adapter's ethernet multicast addresses */ + outb(TX_BASE & 0xff, PIORL(base)); + outb(((TX_BASE >> 8) & PIORH_MASK) | PIORH_SEL_TX, PIORH(base)); + outb(addrs_len & 0xff, PIOP(base)); /* byte count lsb */ + outb((addrs_len >> 8), PIOP(base)); /* byte count msb */ + for(dmi=dev->mc_list; dmi; dmi=dmi->next) + outsb(PIOP(base), dmi->dmi_addr, dmi->dmi_addrlen); + + /* reset transmit DMA pointer */ + hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET); + hacr_write(base, HACR_DEFAULT); + if(!wv_82593_cmd(dev, "wv_82593_config(): mc-setup", + OP0_MC_SETUP, SR0_MC_SETUP_DONE)) + return(FALSE); + lp->mc_count = dev->mc_count; /* remember to avoid repeated reset */ + } + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: <-wv_82593_config()\n", dev->name); +#endif + return(TRUE); +} + +/*------------------------------------------------------------------*/ +/* + * Read the Access Configuration Register, perform a software reset, + * and then re-enable the card's software. + * + * If I understand correctly : reset the pcmcia interface of the + * wavelan. + * (called by wv_config()) + */ +static inline int +wv_pcmcia_reset(device * dev) +{ + int i; + conf_reg_t reg = { 0, CS_READ, CISREG_COR, 0 }; + dev_link_t * link = ((net_local *) dev->priv)->link; + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: ->wv_pcmcia_reset()\n", dev->name); +#endif + + i = CardServices(AccessConfigurationRegister, link->handle, ®); + if(i != CS_SUCCESS) + { + cs_error(link->handle, AccessConfigurationRegister, i); + return FALSE; + } + +#ifdef DEBUG_CONFIG_INFO + printk(KERN_DEBUG "%s: wavelan_pcmcia_reset(): Config reg is 0x%x\n", + dev->name, (u_int) reg.Value); +#endif + + reg.Action = CS_WRITE; + reg.Value = reg.Value | COR_SW_RESET; + i = CardServices(AccessConfigurationRegister, link->handle, ®); + if(i != CS_SUCCESS) + { + cs_error(link->handle, AccessConfigurationRegister, i); + return FALSE; + } + + reg.Action = CS_WRITE; + reg.Value = COR_LEVEL_IRQ | COR_CONFIG; + i = CardServices(AccessConfigurationRegister, link->handle, ®); + if(i != CS_SUCCESS) + { + cs_error(link->handle, AccessConfigurationRegister, i); + return FALSE; + } + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: <-wv_pcmcia_reset()\n", dev->name); +#endif + return TRUE; +} + +/*------------------------------------------------------------------*/ +/* + * wavelan_hw_config() is called after a CARD_INSERTION event is + * received, to configure the wavelan hardware. + * Note that the reception will be enabled in wavelan->open(), so the + * device is configured but idle... + * Performs the following actions: + * 1. A pcmcia software reset (using wv_pcmcia_reset()) + * 2. A power reset (reset DMA) + * 3. Reset the LAN controller + * 4. Initialize the radio modem (using wv_mmc_init) + * 5. Configure LAN controller (using wv_82593_config) + * 6. Perform a diagnostic on the LAN controller + * (called by wavelan_event() & wv_hw_reset()) + */ +static int +wv_hw_config(device * dev) +{ + net_local * lp = (net_local *) dev->priv; + ioaddr_t base = dev->base_addr; + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: ->wv_hw_config()\n", dev->name); +#endif + +#ifdef STRUCT_CHECK + if(wv_structuct_check() != (char *) NULL) + { + printk(KERN_WARNING "%s: wv_hw_config: structure/compiler botch: \"%s\"\n", + dev->name, wv_structuct_check()); + return FALSE; + } +#endif /* STRUCT_CHECK == 1 */ + + /* Reset the pcmcia interface */ + if(wv_pcmcia_reset(dev) == FALSE) + return FALSE; + + /* Power UP the module + reset the modem + reset host adapter + * (in fact, reset DMA channels) */ + hacr_write_slow(base, HACR_RESET); + hacr_write(base, HACR_DEFAULT); + + /* Check if the the module has been powered up... */ + if(hasr_read(base) & HASR_NO_CLK) + { +#ifdef DEBUG_CONFIG_ERRORS + printk(KERN_WARNING "%s: wv_hw_config(): modem not connected or not a wavelan card\n", + dev->name); +#endif + return FALSE; + } + + /* initialize the modem */ + if(wv_mmc_init(dev) == FALSE) + return FALSE; + + /* reset the LAN controller (i82593) */ + outb(OP0_RESET, LCCR(base)); + udelay(1000L); /* A bit crude ! */ + + /* Initialize the LAN controler */ + if((wv_82593_config(dev) == FALSE) || + (wv_diag(dev) == FALSE)) + { +#ifdef DEBUG_CONFIG_ERRORS + printk(KERN_INFO "%s: wv_hw_config(): i82593 init failed\n", dev->name); +#endif + return FALSE; + } + + /* + * insert code for loopback test here + */ + + /* The device is now configured */ + lp->configured = 1; + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: <-wv_hw_config()\n", dev->name); +#endif + return TRUE; +} + +/*------------------------------------------------------------------*/ +/* + * Totally reset the wavelan and restart it. + * Performs the following actions: + * 1. Call wv_hw_config() + * 2. Start the LAN controller's receive unit + * (called by wavelan_event(), wavelan_watchdog() and wavelan_open()) + */ +static inline void +wv_hw_reset(device * dev) +{ + net_local * lp = (net_local *) dev->priv; + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: ->wv_hw_reset()\n", dev->name); +#endif + + /* If watchdog was activated, kill it ! */ + if(lp->watchdog.prev != (timer_list *) NULL) + del_timer(&lp->watchdog); + + lp->nresets++; + lp->configured = 0; + + /* Call wv_hw_config() for most of the reset & init stuff */ + if(wv_hw_config(dev) == FALSE) + return; + + /* start receive unit */ + wv_ru_start(dev); + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: <-wv_hw_reset()\n", dev->name); +#endif +} + +/*------------------------------------------------------------------*/ +/* + * wv_pcmcia_config() is called after a CARD_INSERTION event is + * received, to configure the PCMCIA socket, and to make the ethernet + * device available to the system. + * (called by wavelan_event()) + */ +static inline int +wv_pcmcia_config(dev_link_t * link) +{ + client_handle_t handle; + tuple_t tuple; + cisparse_t parse; + struct net_device * dev; + int i; + u_char buf[64]; + win_req_t req; + memreq_t mem; + + handle = link->handle; + dev = (device *) link->priv; + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "->wv_pcmcia_config(0x%p)\n", link); +#endif + + /* + * This reads the card's CONFIG tuple to find its configuration + * registers. + */ + do + { + tuple.Attributes = 0; + tuple.DesiredTuple = CISTPL_CONFIG; + i = CardServices(GetFirstTuple, handle, &tuple); + if(i != CS_SUCCESS) + break; + tuple.TupleData = (cisdata_t *)buf; + tuple.TupleDataMax = 64; + tuple.TupleOffset = 0; + i = CardServices(GetTupleData, handle, &tuple); + if(i != CS_SUCCESS) + break; + i = CardServices(ParseTuple, handle, &tuple, &parse); + if(i != CS_SUCCESS) + break; + link->conf.ConfigBase = parse.config.base; + link->conf.Present = parse.config.rmask[0]; + } + while(0); + if(i != CS_SUCCESS) + { + cs_error(link->handle, ParseTuple, i); + link->state &= ~DEV_CONFIG_PENDING; + return FALSE; + } + + /* Configure card */ + link->state |= DEV_CONFIG; + do + { + i = CardServices(RequestIO, link->handle, &link->io); + if(i != CS_SUCCESS) + { + cs_error(link->handle, RequestIO, i); + break; + } + + /* + * Now allocate an interrupt line. Note that this does not + * actually assign a handler to the interrupt. + */ + i = CardServices(RequestIRQ, link->handle, &link->irq); + if(i != CS_SUCCESS) + { + cs_error(link->handle, RequestIRQ, i); + break; + } + + /* + * This actually configures the PCMCIA socket -- setting up + * the I/O windows and the interrupt mapping. + */ + link->conf.ConfigIndex = 1; + i = CardServices(RequestConfiguration, link->handle, &link->conf); + if(i != CS_SUCCESS) + { + cs_error(link->handle, RequestConfiguration, i); + break; + } + + /* + * Allocate a 4K memory window. Note that the dev_link_t + * structure provides space for one window handle -- if your + * device needs several windows, you'll need to keep track of + * the handles in your private data structure, link->priv. + */ + req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE; + req.Base = 0; req.Size = 0x1000; + req.AccessSpeed = mem_speed; + link->win = (window_handle_t)link->handle; + i = CardServices(RequestWindow, &link->win, &req); + if(i != CS_SUCCESS) + { + cs_error(link->handle, RequestWindow, i); + break; + } + + dev->rmem_start = dev->mem_start = + (u_long)ioremap(req.Base, 0x1000); + dev->rmem_end = dev->mem_end = dev->mem_start + req.Size; + + mem.CardOffset = 0; mem.Page = 0; + i = CardServices(MapMemPage, link->win, &mem); + if(i != CS_SUCCESS) + { + cs_error(link->handle, MapMemPage, i); + break; + } + + /* Feed device with this info... */ + dev->irq = link->irq.AssignedIRQ; + dev->base_addr = link->io.BasePort1; + dev->tbusy = 0; + +#ifdef DEBUG_CONFIG_INFO + printk(KERN_DEBUG "wv_pcmcia_config: MEMSTART 0x%x IRQ %d IOPORT 0x%x\n", + (u_int) dev->mem_start, dev->irq, (u_int) dev->base_addr); +#endif + + i = register_netdev(dev); + if(i != 0) + { +#ifdef DEBUG_CONFIG_ERRORS + printk(KERN_INFO "wv_pcmcia_config(): register_netdev() failed\n"); +#endif + break; + } + } + while(0); /* Humm... Disguised goto !!! */ + + link->state &= ~DEV_CONFIG_PENDING; + /* If any step failed, release any partially configured state */ + if(i != 0) + { + wv_pcmcia_release((u_long) link); + return FALSE; + } + + /* ???? Could you explain me this, Dave ? */ + link->dev = &((net_local *) dev->priv)->node; + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "<-wv_pcmcia_config()\n"); +#endif + return TRUE; +} + +/*------------------------------------------------------------------*/ +/* + * After a card is removed, wv_pcmcia_release() will unregister the net + * device, and release the PCMCIA configuration. If the device is + * still open, this will be postponed until it is closed. + */ +static void +wv_pcmcia_release(u_long arg) /* Address of the interface struct */ +{ + dev_link_t * link = (dev_link_t *) arg; + device * dev = (device *) link->priv; + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: -> wv_pcmcia_release(0x%p)\n", dev->name, link); +#endif + + /* If the device is currently in use, we won't release until it is + * actually closed. */ + if(link->open) + { +#ifdef DEBUG_CONFIG_INFO + printk(KERN_DEBUG "%s: wv_pcmcia_release: release postponed, device still open\n", + dev->name); +#endif + link->state |= DEV_STALE_CONFIG; + return; + } + + /* Don't bother checking to see if these succeed or not */ + iounmap((u_char *)dev->mem_start); + CardServices(ReleaseWindow, link->win); + CardServices(ReleaseConfiguration, link->handle); + CardServices(ReleaseIO, link->handle, &link->io); + CardServices(ReleaseIRQ, link->handle, &link->irq); + + link->state &= ~(DEV_CONFIG | DEV_RELEASE_PENDING | DEV_STALE_CONFIG); + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "%s: <- wv_pcmcia_release()\n", dev->name); +#endif +} /* wv_pcmcia_release */ + +/*------------------------------------------------------------------*/ +/* + * Sometimes, netwave_detach can't be performed following a call from + * cardmgr (device still open, pcmcia_release not done) and the device + * is put in a STALE_LINK state and remains in memory. + * + * This function run through our current list of device and attempt + * another time to remove them. We hope that since last time the + * device has properly been closed. + * + * (called by wavelan_attach() & cleanup_module()) + */ +static void +wv_flush_stale_links(void) +{ + dev_link_t * link; /* Current node in linked list */ + dev_link_t * next; /* Next node in linked list */ + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "-> wv_flush_stale_links(0x%p)\n", dev_list); +#endif + + /* Go through the list */ + for (link = dev_list; link; link = next) + { + next = link->next; + + /* Check if in need of being removed */ + if((link->state & DEV_STALE_LINK) || + (! (link->state & DEV_PRESENT))) + wavelan_detach(link); + + } + +#ifdef DEBUG_CONFIG_TRACE + printk(KERN_DEBUG "<- wv_flush_stale_links()\n"); +#endif +} + +/************************ INTERRUPT HANDLING ************************/ + +/* + * This function is the interrupt handler for the WaveLAN card. This + * routine will be called whenever: + * 1. A packet is received. + * 2. A packet has successfully been transfered and the unit is + * ready to transmit another packet. + * 3. A command has completed execution. + */ +static void +wavelan_interrupt(int irq, + void * dev_id, + struct pt_regs * regs) +{ + device * dev; + net_local * lp; + ioaddr_t base; + int status0; + u_int tx_status; + + if((dev = (device *)dev_id) == (device *) NULL) + { +#ifdef DEBUG_INTERRUPT_ERROR + printk(KERN_WARNING "wavelan_interrupt(): irq %d for unknown device.\n", + irq); +#endif + return; + } + +#ifdef DEBUG_INTERRUPT_TRACE + printk(KERN_DEBUG "%s: ->wavelan_interrupt()\n", dev->name); +#endif + + lp = (net_local *) dev->priv; + base = dev->base_addr; + + /* Prevent reentrance. What should we do here ? */ +#ifdef DEBUG_INTERRUPT_ERROR + if(dev->interrupt) + printk(KERN_INFO "%s: wavelan_interrupt(): Re-entering the interrupt handler.\n", + dev->name); +#endif + dev->interrupt = 1; + + /* Treat all pending interrupts */ + while(1) + { + /* ---------------- INTERRUPT CHECKING ---------------- */ + /* + * Look for the interrupt and verify the validity + */ + outb(CR0_STATUS_0 | OP0_NOP, LCCR(base)); + status0 = inb(LCSR(base)); + +#ifdef DEBUG_INTERRUPT_INFO + printk(KERN_DEBUG "status0 0x%x [%s => 0x%x]", status0, + (status0&SR0_INTERRUPT)?"int":"no int",status0&~SR0_INTERRUPT); + if(status0&SR0_INTERRUPT) + { + printk(" [%s => %d]\n", (status0 & SR0_CHNL) ? "chnl" : + ((status0 & SR0_EXECUTION) ? "cmd" : + ((status0 & SR0_RECEPTION) ? "recv" : "unknown")), + (status0 & SR0_EVENT_MASK)); + } + else + printk("\n"); +#endif + + /* Return if no actual interrupt from i82593 (normal exit) */ + if(!(status0 & SR0_INTERRUPT)) + break; + + /* If interrupt is both Rx and Tx or none... + * This code in fact is there to catch the spurious interrupt + * when you remove the wavelan pcmcia card from the socket */ + if(((status0 & SR0_BOTH_RX_TX) == SR0_BOTH_RX_TX) || + ((status0 & SR0_BOTH_RX_TX) == 0x0)) + { +#ifdef DEBUG_INTERRUPT_INFO + printk(KERN_INFO "%s: wv_interrupt(): bogus interrupt (or from dead card) : %X\n", + dev->name, status0); +#endif + /* Acknowledge the interrupt */ + outb(CR0_INT_ACK | OP0_NOP, LCCR(base)); + break; + } + + lp->status = status0; /* Save current status (for commands) */ + + /* ----------------- RECEIVING PACKET ----------------- */ + /* + * When the wavelan signal the reception of a new packet, + * we call wv_packet_rcv() to copy if from the buffer and + * send it to NET3 + */ + if(status0 & SR0_RECEPTION) + { +#ifdef DEBUG_INTERRUPT_INFO + printk(KERN_DEBUG "%s: wv_interrupt(): receive\n", dev->name); +#endif + + if((status0 & SR0_EVENT_MASK) == SR0_STOP_REG_HIT) + { +#ifdef DEBUG_INTERRUPT_ERROR + printk(KERN_INFO "%s: wv_interrupt(): receive buffer overflow\n", + dev->name); +#endif + lp->stats.rx_over_errors++; + lp->overrunning = 1; + } + + /* Get the packet */ + wv_packet_rcv(dev); + lp->overrunning = 0; + + /* Acknowledge the interrupt */ + outb(CR0_INT_ACK | OP0_NOP, LCCR(base)); + continue; + } + + /* ---------------- COMMAND COMPLETION ---------------- */ + /* + * Interrupts issued when the i82593 has completed a command. + * Most likely : transmission done + */ + + /* If we are already waiting elsewhere for the command to complete */ + if(wv_wait_completed) + { +#ifdef DEBUG_INTERRUPT_INFO + printk(KERN_DEBUG "%s: wv_interrupt(): command completed\n", + dev->name); +#endif + + /* Signal command completion */ + wv_wait_completed = 0; + + /* Acknowledge the interrupt */ + outb(CR0_INT_ACK | OP0_NOP, LCCR(base)); + continue; + } + + /* If a transmission has been done */ + if((status0 & SR0_EVENT_MASK) == SR0_TRANSMIT_DONE || + (status0 & SR0_EVENT_MASK) == SR0_RETRANSMIT_DONE || + (status0 & SR0_EVENT_MASK) == SR0_TRANSMIT_NO_CRC_DONE) + { +#ifdef DEBUG_TX_ERROR + if((status0 & SR0_EVENT_MASK) == SR0_TRANSMIT_NO_CRC_DONE) + printk(KERN_INFO "%s: wv_interrupt(): packet transmitted without CRC.\n", + dev->name); +#endif + + /* If watchdog was activated, kill it ! */ + if(lp->watchdog.prev != (timer_list *) NULL) + del_timer(&lp->watchdog); + + /* Get transmission status */ + tx_status = inb(LCSR(base)); + tx_status |= (inb(LCSR(base)) << 8); +#ifdef DEBUG_INTERRUPT_INFO + printk(KERN_DEBUG "%s: wv_interrupt(): transmission done\n", + dev->name); + { + u_int rcv_bytes; + u_char status3; + rcv_bytes = inb(LCSR(base)); + rcv_bytes |= (inb(LCSR(base)) << 8); + status3 = inb(LCSR(base)); + printk(KERN_DEBUG "tx_status 0x%02x rcv_bytes 0x%02x status3 0x%x\n", + tx_status, rcv_bytes, (u_int) status3); + } +#endif + /* Check for possible errors */ + if((tx_status & TX_OK) != TX_OK) + { + lp->stats.tx_errors++; + + if(tx_status & TX_FRTL) + { +#ifdef DEBUG_TX_ERROR + printk(KERN_INFO "%s: wv_interrupt(): frame too long\n", + dev->name); +#endif + } + if(tx_status & TX_UND_RUN) + { +#ifdef DEBUG_TX_FAIL + printk(KERN_DEBUG "%s: wv_interrupt(): DMA underrun\n", + dev->name); +#endif + lp->stats.tx_aborted_errors++; + } + if(tx_status & TX_LOST_CTS) + { +#ifdef DEBUG_TX_FAIL + printk(KERN_DEBUG "%s: wv_interrupt(): no CTS\n", dev->name); +#endif + lp->stats.tx_carrier_errors++; + } + if(tx_status & TX_LOST_CRS) + { +#ifdef DEBUG_TX_FAIL + printk(KERN_DEBUG "%s: wv_interrupt(): no carrier\n", + dev->name); +#endif + lp->stats.tx_carrier_errors++; + } + if(tx_status & TX_HRT_BEAT) + { +#ifdef DEBUG_TX_FAIL + printk(KERN_DEBUG "%s: wv_interrupt(): heart beat\n", dev->name); +#endif + lp->stats.tx_heartbeat_errors++; + } + if(tx_status & TX_DEFER) + { +#ifdef DEBUG_TX_FAIL + printk(KERN_DEBUG "%s: wv_interrupt(): channel jammed\n", + dev->name); +#endif + } + /* Ignore late collisions since they're more likely to happen + * here (the WaveLAN design prevents the LAN controller from + * receiving while it is transmitting). We take action only when + * the maximum retransmit attempts is exceeded. + */ + if(tx_status & TX_COLL) + { + if(tx_status & TX_MAX_COL) + { +#ifdef DEBUG_TX_FAIL + printk(KERN_DEBUG "%s: wv_interrupt(): channel congestion\n", + dev->name); +#endif + if(!(tx_status & TX_NCOL_MASK)) + { + lp->stats.collisions += 0x10; + } + } + } + } /* if(!(tx_status & TX_OK)) */ + + lp->stats.collisions += (tx_status & TX_NCOL_MASK); + lp->stats.tx_packets++; + + dev->tbusy = FALSE; + mark_bh(NET_BH); + outb(CR0_INT_ACK | OP0_NOP, LCCR(base)); /* Acknowledge the interrupt */ + } + else /* if interrupt = transmit done or retransmit done */ + { +#ifdef DEBUG_INTERRUPT_ERROR + printk(KERN_INFO "wavelan_cs: unknown interrupt, status0 = %02x\n", + status0); +#endif + outb(CR0_INT_ACK | OP0_NOP, LCCR(base)); /* Acknowledge the interrupt */ + } + } + dev->interrupt = FALSE; + +#ifdef DEBUG_INTERRUPT_TRACE + printk(KERN_DEBUG "%s: <-wavelan_interrupt()\n", dev->name); +#endif +} /* wv_interrupt */ + +/*------------------------------------------------------------------*/ +/* + * Watchdog : when we start a transmission, we set a timer in the + * kernel. If the transmission complete, this timer is disabled. If + * it expire, it try to unlock the hardware. + * + * Note : this watchdog doesn't work on the same principle as the + * watchdog in the ISA driver. I make it this way because the overhead + * of add_timer() and del_timer() is nothing and that it avoid calling + * the watchdog, saving some CPU... If you want to apply the same + * watchdog to the ISA driver, you should be a bit carefull, because + * of the many transmit buffers... + * This watchdog is also move clever, it try to abort the current + * command before reseting everything... + */ +static void +wavelan_watchdog(u_long a) +{ + device * dev; + net_local * lp; + ioaddr_t base; + int spin; + + dev = (device *) a; + base = dev->base_addr; + lp = (net_local *) dev->priv; + +#ifdef DEBUG_INTERRUPT_TRACE + printk(KERN_DEBUG "%s: ->wavelan_watchdog()\n", dev->name); +#endif + +#ifdef DEBUG_INTERRUPT_ERROR + printk(KERN_INFO "%s: wavelan_watchdog: watchdog timer expired\n", + dev->name); +#endif + + /* We are waiting for command completion */ + wv_wait_completed = TRUE; + + /* Ask to abort the current command */ + outb(OP0_ABORT, LCCR(base)); + + /* Busy wait while the LAN controller executes the command. + * Note : wv_wait_completed should be volatile */ + spin = 0; + while(wv_wait_completed && (spin++ < 250)) + udelay(10); + + /* If the interrupt handler hasn't be called or invalid status */ + if((wv_wait_completed) || + ((lp->status & SR0_EVENT_MASK) != SR0_EXECUTION_ABORTED)) + { + /* It seem that it wasn't enough */ +#ifdef DEBUG_INTERRUPT_ERROR + printk(KERN_INFO "%s: wavelan_watchdog: abort failed, trying reset\n", + dev->name); +#endif + wv_hw_reset(dev); + } + +#ifdef DEBUG_PSA_SHOW + { + psa_t psa; + psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa)); + wv_psa_show(&psa); + } +#endif +#ifdef DEBUG_MMC_SHOW + wv_mmc_show(dev); +#endif +#ifdef DEBUG_I82593_SHOW + wv_ru_show(dev); +#endif + + /* We are no more waiting for something... */ + dev->tbusy = 0; + +#ifdef DEBUG_INTERRUPT_TRACE + printk(KERN_DEBUG "%s: <-wavelan_watchdog()\n", dev->name); +#endif +} + +/********************* CONFIGURATION CALLBACKS *********************/ +/* + * Here are the functions called by the pcmcia package (cardmgr) and + * linux networking (NET3) for initialization, configuration and + * deinstallations of the Wavelan Pcmcia Hardware. + */ + +/*------------------------------------------------------------------*/ +/* + * Configure and start up the WaveLAN PCMCIA adaptor. + * Called by NET3 when it "open" the device. + */ +static int +wavelan_open(device * dev) +{ + dev_link_t * link = ((net_local *) dev->priv)->link; + net_local * lp = (net_local *)dev->priv; + ioaddr_t base = dev->base_addr; + +#ifdef DEBUG_CALLBACK_TRACE + printk(KERN_DEBUG "%s: ->wavelan_open(dev=0x%x)\n", dev->name, + (unsigned int) dev); +#endif + + /* Check if the modem is powered up (wavelan_close() power it down */ + if(hasr_read(base) & HASR_NO_CLK) + { + /* Power up (power up time is 250us) */ + hacr_write(base, HACR_DEFAULT); + + /* Check if the the module has been powered up... */ + if(hasr_read(base) & HASR_NO_CLK) + { +#ifdef DEBUG_CONFIG_ERRORS + printk(KERN_WARNING "%s: wavelan_open(): modem not connected\n", + dev->name); +#endif + return FALSE; + } + } + + /* Start reception and declare the driver ready */ + if(!lp->configured) + return FALSE; + if(!wv_ru_start(dev)) + wv_hw_reset(dev); /* If problem : reset */ + dev->interrupt = 0; + dev->start = 1; + dev->tbusy = 0; + + /* Mark the device as used */ + link->open++; + MOD_INC_USE_COUNT; + +#ifdef WAVELAN_ROAMING + if(do_roaming) + wv_roam_init(dev); +#endif /* WAVELAN_ROAMING */ + +#ifdef DEBUG_CALLBACK_TRACE + printk(KERN_DEBUG "%s: <-wavelan_open()\n", dev->name); +#endif + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Shutdown the WaveLAN PCMCIA adaptor. + * Called by NET3 when it "close" the device. + */ +static int +wavelan_close(device * dev) +{ + dev_link_t * link = ((net_local *) dev->priv)->link; + net_local * lp = (net_local *)dev->priv; + ioaddr_t base = dev->base_addr; + +#ifdef DEBUG_CALLBACK_TRACE + printk(KERN_DEBUG "%s: ->wavelan_close(dev=0x%x)\n", dev->name, + (unsigned int) dev); +#endif + + /* If the device isn't open, then nothing to do */ + if(!link->open) + { +#ifdef DEBUG_CONFIG_INFO + printk(KERN_DEBUG "%s: wavelan_close(): device not open\n", dev->name); +#endif + return 0; + } + +#ifdef WAVELAN_ROAMING + /* Cleanup of roaming stuff... */ + if(do_roaming) + wv_roam_cleanup(dev); +#endif /* WAVELAN_ROAMING */ + + /* If watchdog was activated, kill it ! */ + if(lp->watchdog.prev != (timer_list *) NULL) + del_timer(&lp->watchdog); + + link->open--; + MOD_DEC_USE_COUNT; + + /* If the card is still present */ + if(dev->start) + { + dev->tbusy = 1; + dev->start = 0; + + /* Stop receiving new messages and wait end of transmission */ + wv_ru_stop(dev); + + /* Power down the module */ + hacr_write(base, HACR_DEFAULT & (~HACR_PWR_STAT)); + } + else + /* The card is no more there (flag is activated in wv_pcmcia_release) */ + if(link->state & DEV_STALE_CONFIG) + wv_pcmcia_release((u_long)link); + +#ifdef DEBUG_CALLBACK_TRACE + printk(KERN_DEBUG "%s: <-wavelan_close()\n", dev->name); +#endif + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * We never need to do anything when a wavelan device is "initialized" + * by the net software, because we only register already-found cards. + */ +static int +wavelan_init(device * dev) +{ +#ifdef DEBUG_CALLBACK_TRACE + printk(KERN_DEBUG "<>wavelan_init()\n"); +#endif + + return(0); +} + +/*------------------------------------------------------------------*/ +/* + * wavelan_attach() creates an "instance" of the driver, allocating + * local data structures for one device (one interface). The device + * is registered with Card Services. + * + * The dev_link structure is initialized, but we don't actually + * configure the card at this point -- we wait until we receive a + * card insertion event. + */ +static dev_link_t * +wavelan_attach(void) +{ + client_reg_t client_reg; /* Register with cardmgr */ + dev_link_t * link; /* Info for cardmgr */ + device * dev; /* Interface generic data */ + net_local * lp; /* Interface specific data */ + int i, ret; + +#ifdef DEBUG_CALLBACK_TRACE + printk(KERN_DEBUG "-> wavelan_attach()\n"); +#endif + + /* Perform some cleanup */ + wv_flush_stale_links(); + + /* Initialize the dev_link_t structure */ + link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL); + memset(link, 0, sizeof(struct dev_link_t)); + + /* Unused for the Wavelan */ + link->release.function = &wv_pcmcia_release; + link->release.data = (u_long) link; + + /* The io structure describes IO port mapping */ + link->io.NumPorts1 = 8; + link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; + link->io.IOAddrLines = 3; + + /* Interrupt setup */ + link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; + link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID; + if (irq_list[0] == -1) + link->irq.IRQInfo2 = irq_mask; + else + for (i = 0; i < 4; i++) + link->irq.IRQInfo2 |= 1 << irq_list[i]; + link->irq.Handler = wavelan_interrupt; + + /* General socket configuration */ + link->conf.Attributes = CONF_ENABLE_IRQ; + link->conf.Vcc = 50; + link->conf.IntType = INT_MEMORY_AND_IO; + + /* Chain drivers */ + link->next = dev_list; + dev_list = link; + + /* Allocate the generic data structure */ + dev = kmalloc(sizeof(struct net_device), GFP_KERNEL); + memset(dev, 0x00, sizeof(struct net_device)); + link->priv = link->irq.Instance = dev; + + /* Allocate the wavelan-specific data structure. */ + dev->priv = lp = (net_local *) kmalloc(sizeof(net_local), GFP_KERNEL); + memset(lp, 0x00, sizeof(net_local)); + + /* Init specific data */ + wv_wait_completed = 0; + lp->status = FALSE; + lp->configured = 0; + lp->reconfig_82593 = FALSE; + lp->nresets = 0; + + /* Set the watchdog timer */ + lp->watchdog.function = wavelan_watchdog; + lp->watchdog.data = (unsigned long) dev; + + /* back links */ + lp->link = link; + lp->dev = dev; + + /* Standard setup for generic data */ + ether_setup(dev); + + /* wavelan NET3 callbacks */ + dev->init = &wavelan_init; + dev->open = &wavelan_open; + dev->stop = &wavelan_close; + dev->hard_start_xmit = &wavelan_packet_xmit; + dev->get_stats = &wavelan_get_stats; + dev->set_multicast_list = &wavelan_set_multicast_list; +#ifdef SET_MAC_ADDRESS + dev->set_mac_address = &wavelan_set_mac_address; +#endif /* SET_MAC_ADDRESS */ + +#ifdef WIRELESS_EXT /* If wireless extension exist in the kernel */ + dev->do_ioctl = wavelan_ioctl; /* wireless extensions */ + dev->get_wireless_stats = wavelan_get_wireless_stats; +#endif + + /* Other specific data */ + /* Provide storage area for device name */ + dev->name = ((net_local *)dev->priv)->node.dev_name; + dev->tbusy = 1; + dev->mtu = WAVELAN_MTU; + + /* Register with Card Services */ + client_reg.dev_info = &dev_info; + client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; + client_reg.EventMask = + CS_EVENT_REGISTRATION_COMPLETE | + CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | + CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | + CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; + client_reg.event_handler = &wavelan_event; + client_reg.Version = 0x0210; + client_reg.event_callback_args.client_data = link; + +#ifdef DEBUG_CONFIG_INFO + printk(KERN_DEBUG "wavelan_attach(): almost done, calling CardServices\n"); +#endif + + ret = CardServices(RegisterClient, &link->handle, &client_reg); + if(ret != 0) + { + cs_error(link->handle, RegisterClient, ret); + wavelan_detach(link); + return NULL; + } + +#ifdef DEBUG_CALLBACK_TRACE + printk(KERN_DEBUG "<- wavelan_attach()\n"); +#endif + + return link; +} + +/*------------------------------------------------------------------*/ +/* + * This deletes a driver "instance". The device is de-registered with + * Card Services. If it has been released, all local data structures + * are freed. Otherwise, the structures will be freed when the device + * is released. + */ +static void +wavelan_detach(dev_link_t * link) +{ +#ifdef DEBUG_CALLBACK_TRACE + printk(KERN_DEBUG "-> wavelan_detach(0x%p)\n", link); +#endif + + /* + * If the device is currently configured and active, we won't + * actually delete it yet. Instead, it is marked so that when the + * release() function is called, that will trigger a proper + * detach(). + */ + if(link->state & DEV_CONFIG) + { + /* Some others haven't done their job : give them another chance */ + wv_pcmcia_release((u_long) link); + if(link->state & DEV_STALE_CONFIG) + { +#ifdef DEBUG_CONFIG_INFO + printk(KERN_DEBUG "wavelan_detach: detach postponed," + " '%s' still locked\n", link->dev->dev_name); +#endif + link->state |= DEV_STALE_LINK; + return; + } + } + + /* Break the link with Card Services */ + if(link->handle) + CardServices(DeregisterClient, link->handle); + + /* Remove the interface data from the linked list */ + if(dev_list == link) + dev_list = link->next; + else + { + dev_link_t * prev = dev_list; + + while((prev != (dev_link_t *) NULL) && (prev->next != link)) + prev = prev->next; + + if(prev == (dev_link_t *) NULL) + { +#ifdef DEBUG_CONFIG_ERRORS + printk(KERN_WARNING "wavelan_detach : Attempting to remove a nonexistent device.\n"); +#endif + return; + } + + prev->next = link->next; + } + + /* Free pieces */ + if(link->priv) + { + device * dev = (device *) link->priv; + + /* Remove ourselves from the kernel list of ethernet devices */ + /* Warning : can't be called from interrupt, timer or wavelan_close() */ + if(link->dev != NULL) + unregister_netdev(dev); + link->dev = NULL; + + if(dev->priv) + { + /* Sound strange, but safe... */ + ((net_local *) dev->priv)->link = (dev_link_t *) NULL; + ((net_local *) dev->priv)->dev = (device *) NULL; + kfree(dev->priv); + } + kfree(link->priv); + } + kfree(link); + +#ifdef DEBUG_CALLBACK_TRACE + printk(KERN_DEBUG "<- wavelan_detach()\n"); +#endif +} + +/*------------------------------------------------------------------*/ +/* + * The card status event handler. Mostly, this schedules other stuff + * to run after an event is received. A CARD_REMOVAL event also sets + * some flags to discourage the net drivers from trying to talk to the + * card any more. + */ +static int +wavelan_event(event_t event, /* The event received */ + int priority, + event_callback_args_t * args) +{ + dev_link_t * link = (dev_link_t *) args->client_data; + device * dev = (device *) link->priv; + +#ifdef DEBUG_CALLBACK_TRACE + printk(KERN_DEBUG "->wavelan_event(): %s\n", + ((event == CS_EVENT_REGISTRATION_COMPLETE)?"registration complete" : + ((event == CS_EVENT_CARD_REMOVAL) ? "card removal" : + ((event == CS_EVENT_CARD_INSERTION) ? "card insertion" : + ((event == CS_EVENT_PM_SUSPEND) ? "pm suspend" : + ((event == CS_EVENT_RESET_PHYSICAL) ? "physical reset" : + ((event == CS_EVENT_PM_RESUME) ? "pm resume" : + ((event == CS_EVENT_CARD_RESET) ? "card reset" : + "unknown")))))))); +#endif + + switch(event) + { + case CS_EVENT_REGISTRATION_COMPLETE: +#ifdef DEBUG_CONFIG_INFO + printk(KERN_DEBUG "wavelan_cs: registration complete\n"); +#endif + break; + + case CS_EVENT_CARD_REMOVAL: + /* Oups ! The card is no more there */ + link->state &= ~DEV_PRESENT; + if(link->state & DEV_CONFIG) + { + /* Accept no more transmissions */ + dev->tbusy = 1; dev->start = 0; + + /* Release the card */ + wv_pcmcia_release((u_long) link); + } + break; + + case CS_EVENT_CARD_INSERTION: + /* Reset and configure the card */ + link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; + if(wv_pcmcia_config(link) && + wv_hw_config(dev)) + wv_init_info(dev); + else + dev->irq = 0; + break; + + case CS_EVENT_PM_SUSPEND: + /* NB: wavelan_close will be called, but too late, so we are + * obliged to close nicely the wavelan here. David, could you + * close the device before suspending them ? And, by the way, + * could you, on resume, add a "route add -net ..." after the + * ifconfig up ??? Thanks... */ + + /* Stop receiving new messages and wait end of transmission */ + wv_ru_stop(dev); + + /* Power down the module */ + hacr_write(dev->base_addr, HACR_DEFAULT & (~HACR_PWR_STAT)); + + /* The card is now suspended */ + link->state |= DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_RESET_PHYSICAL: + if(link->state & DEV_CONFIG) + { + if(link->open) + { + dev->tbusy = 1; + dev->start = 0; + } + CardServices(ReleaseConfiguration, link->handle); + } + break; + + case CS_EVENT_PM_RESUME: + link->state &= ~DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_CARD_RESET: + if(link->state & DEV_CONFIG) + { + CardServices(RequestConfiguration, link->handle, &link->conf); + if(link->open) /* If RESET -> True, If RESUME -> False ??? */ + { + wv_hw_reset(dev); + dev->tbusy = 0; + dev->start = 1; + } + } + break; + } + +#ifdef DEBUG_CALLBACK_TRACE + printk(KERN_DEBUG "<-wavelan_event()\n"); +#endif + return 0; +} + +/****************************** MODULE ******************************/ +/* + * Module entry points : insertion & removal + */ + +/*------------------------------------------------------------------*/ +/* + * Module insertion : initialisation of the module. + * Register the card with cardmgr... + */ +static int __init +init_wavelan_cs(void) +{ + servinfo_t serv; + +#ifdef DEBUG_MODULE_TRACE + printk(KERN_DEBUG "-> init_wavelan_cs()\n"); +#ifdef DEBUG_VERSION_SHOW + printk(KERN_DEBUG "%s", version); +#endif +#endif + + CardServices(GetCardServicesInfo, &serv); + if(serv.Revision != CS_RELEASE_CODE) + { +#ifdef DEBUG_CONFIG_ERRORS + printk(KERN_WARNING "init_wavelan_cs: Card Services release does not match!\n"); +#endif + return -1; + } + + register_pccard_driver(&dev_info, &wavelan_attach, &wavelan_detach); + +#ifdef DEBUG_MODULE_TRACE + printk(KERN_DEBUG "<- init_wavelan_cs()\n"); +#endif + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Module removal + */ +static void __exit +exit_wavelan_cs(void) +{ +#ifdef DEBUG_MODULE_TRACE + printk(KERN_DEBUG "-> cleanup_module()\n"); +#endif +#ifdef DEBUG_BASIC_SHOW + printk(KERN_NOTICE "wavelan_cs: unloading\n"); +#endif + + /* Do some cleanup of the device list */ + wv_flush_stale_links(); + + /* If there remain some devices... */ +#ifdef DEBUG_CONFIG_ERRORS + if(dev_list != NULL) + { + /* Honestly, if this happen we are in a deep s**t */ + printk(KERN_INFO "wavelan_cs: devices remaining when removing module\n"); + printk(KERN_INFO "Please flush your disks and reboot NOW !\n"); + } +#endif + + unregister_pccard_driver(&dev_info); + +#ifdef DEBUG_MODULE_TRACE + printk(KERN_DEBUG "<- cleanup_module()\n"); +#endif +} + +module_init(init_wavelan_cs); +module_exit(exit_wavelan_cs); diff --git a/drivers/net/pcmcia/wavelan_cs.h b/drivers/net/pcmcia/wavelan_cs.h new file mode 100644 index 000000000..84dc2114d --- /dev/null +++ b/drivers/net/pcmcia/wavelan_cs.h @@ -0,0 +1,773 @@ +/* + * Wavelan Pcmcia driver + * + * Jean II - HPLB '96 + * + * Reorganisation and extension of the driver. + * + * This file contain all definition and declarations necessary for the + * wavelan pcmcia driver. This file is a private header, so it should + * be included only on wavelan_cs.c !!! + */ + +#ifndef WAVELAN_CS_H +#define WAVELAN_CS_H + +/************************** DOCUMENTATION **************************/ +/* + * This driver provide a Linux interface to the Wavelan Pcmcia hardware + * The Wavelan is a product of Lucent (http://www.wavelan.com/). + * This division was formerly part of NCR and then AT&T. + * Wavelan are also distributed by DEC (RoamAbout DS)... + * + * To know how to use this driver, read the PCMCIA HOWTO. + * If you want to exploit the many other fonctionalities, look comments + * in the code... + * + * This driver is the result of the effort of many peoples (see below). + */ + +/* ------------------------ SPECIFIC NOTES ------------------------ */ +/* + * Web page + * -------- + * I try to maintain a web page with the Wireless LAN Howto at : + * http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Wavelan.html + * + * Debugging and options + * --------------------- + * You will find below a set of '#define" allowing a very fine control + * on the driver behaviour and the debug messages printed. + * The main options are : + * o WAVELAN_ROAMING, for the experimental roaming support. + * o SET_PSA_CRC, to have your card correctly recognised by + * an access point and the Point-to-Point diagnostic tool. + * o USE_PSA_CONFIG, to read configuration from the PSA (EEprom) + * (otherwise we always start afresh with some defaults) + * + * wavelan_cs.o is darn too big + * ------------------------- + * That's true ! There is a very simple way to reduce the driver + * object by 33% (yes !). Comment out the following line : + * #include <linux/wireless.h> + * Other compile options can also reduce the size of it... + * + * MAC address and hardware detection : + * ---------------------------------- + * The detection code of the wavelan chech that the first 3 + * octets of the MAC address fit the company code. This type of + * detection work well for AT&T cards (because the AT&T code is + * hardcoded in wavelan.h), but of course will fail for other + * manufacturer. + * + * If you are sure that your card is derived from the wavelan, + * here is the way to configure it : + * 1) Get your MAC address + * a) With your card utilities (wfreqsel, instconf, ...) + * b) With the driver : + * o compile the kernel with DEBUG_CONFIG_INFO enabled + * o Boot and look the card messages + * 2) Set your MAC code (3 octets) in MAC_ADDRESSES[][3] (wavelan.h) + * 3) Compile & verify + * 4) Send me the MAC code - I will include it in the next version... + * + */ + +/* --------------------- WIRELESS EXTENSIONS --------------------- */ +/* + * This driver is the first one to support "wireless extensions". + * This set of extensions provide you some way to control the wireless + * caracteristics of the hardware in a standard way and support for + * applications for taking advantage of it (like Mobile IP). + * + * You will need to enable the CONFIG_NET_RADIO define in the kernel + * configuration to enable the wireless extensions (this is the one + * giving access to the radio network device choice). + * + * It might also be a good idea as well to fetch the wireless tools to + * configure the device and play a bit. + */ + +/* ---------------------------- FILES ---------------------------- */ +/* + * wavelan_cs.c : The actual code for the driver - C functions + * + * wavelan_cs.h : Private header : local types / vars for the driver + * + * wavelan.h : Description of the hardware interface & structs + * + * i82593.h : Description if the Ethernet controler + */ + +/* --------------------------- HISTORY --------------------------- */ +/* + * The history of the Wavelan drivers is as complicated as history of + * the Wavelan itself (NCR -> AT&T -> Lucent). + * + * All started with Anders Klemets <klemets@paul.rutgers.edu>, + * writting a Wavelan ISA driver for the MACH microkernel. Girish + * Welling <welling@paul.rutgers.edu> had also worked on it. + * Keith Moore modify this for the Pcmcia hardware. + * + * Robert Morris <rtm@das.harvard.edu> port these two drivers to BSDI + * and add specific Pcmcia support (there is currently no equivalent + * of the PCMCIA package under BSD...). + * + * Jim Binkley <jrb@cs.pdx.edu> port both BSDI drivers to FreeBSD. + * + * Bruce Janson <bruce@cs.usyd.edu.au> port the BSDI ISA driver to Linux. + * + * Anthony D. Joseph <adj@lcs.mit.edu> started modify Bruce driver + * (with help of the BSDI PCMCIA driver) for PCMCIA. + * Yunzhou Li <yunzhou@strat.iol.unh.edu> finished is work. + * Joe Finney <joe@comp.lancs.ac.uk> patched the driver to start + * correctly 2.00 cards (2.4 GHz with frequency selection). + * David Hinds <dhinds@hyper.stanford.edu> integrated the whole in his + * Pcmcia package (+ bug corrections). + * + * I (Jean Tourrilhes - jt@hplb.hpl.hp.com) then started to make some + * patchs to the Pcmcia driver. After, I added code in the ISA driver + * for Wireless Extensions and full support of frequency selection + * cards. Now, I'm doing the same to the Pcmcia driver + some + * reorganisation. + * Loeke Brederveld <lbrederv@wavelan.com> from Lucent has given me + * much needed informations on the Wavelan hardware. + */ + +/* By the way : for the copyright & legal stuff : + * Almost everybody wrote code under GNU or BSD license (or alike), + * and want that their original copyright remain somewhere in the + * code (for myself, I go with the GPL). + * Nobody want to take responsibility for anything, except the fame... + */ + +/* --------------------------- CREDITS --------------------------- */ +/* + * Credits: + * Special thanks to Jan Hoogendoorn of AT&T GIS Utrecht and + * Loeke Brederveld of Lucent for providing extremely useful + * information about WaveLAN PCMCIA hardware + * + * This driver is based upon several other drivers, in particular: + * David Hinds' Linux driver for the PCMCIA 3c589 ethernet adapter + * Bruce Janson's Linux driver for the AT-bus WaveLAN adapter + * Anders Klemets' PCMCIA WaveLAN adapter driver + * Robert Morris' BSDI driver for the PCMCIA WaveLAN adapter + * + * Additional Credits: + * + * This software was originally developed under Linux 1.2.3 + * (Slackware 2.0 distribution). + * And then under Linux 2.0.x (Debian 1.1 - pcmcia 2.8.18-23) with + * HP OmniBook 4000 & 5500. + * + * It is based on other device drivers and information either written + * or supplied by: + * James Ashton (jaa101@syseng.anu.edu.au), + * Ajay Bakre (bakre@paul.rutgers.edu), + * Donald Becker (becker@super.org), + * Jim Binkley <jrb@cs.pdx.edu>, + * Loeke Brederveld <lbrederv@wavelan.com>, + * Allan Creighton (allanc@cs.su.oz.au), + * Brent Elphick <belphick@uwaterloo.ca>, + * Joe Finney <joe@comp.lancs.ac.uk>, + * Matthew Geier (matthew@cs.su.oz.au), + * Remo di Giovanni (remo@cs.su.oz.au), + * Mark Hagan (mhagan@wtcpost.daytonoh.NCR.COM), + * David Hinds <dhinds@hyper.stanford.edu>, + * Jan Hoogendoorn (c/o marteijn@lucent.com), + * Bruce Janson <bruce@cs.usyd.edu.au>, + * Anthony D. Joseph <adj@lcs.mit.edu>, + * Anders Klemets (klemets@paul.rutgers.edu), + * Yunzhou Li <yunzhou@strat.iol.unh.edu>, + * Marc Meertens (mmeertens@lucent.com), + * Keith Moore, + * Robert Morris (rtm@das.harvard.edu), + * Ian Parkin (ian@cs.su.oz.au), + * John Rosenberg (johnr@cs.su.oz.au), + * George Rossi (george@phm.gov.au), + * Arthur Scott (arthur@cs.su.oz.au), + * Stanislav Sinyagin <stas@isf.ru> + * Peter Storey, + * Jean Tourrilhes <jt@hpl.hp.com>, + * Girish Welling (welling@paul.rutgers.edu) + * Clark Woodworth <clark@hiway1.exit109.com> + * Yongguang Zhang <ygz@isl.hrl.hac.com>... + */ + +/* ------------------------- IMPROVEMENTS ------------------------- */ +/* + * I proudly present : + * + * Changes made in 2.8.22 : + * ---------------------- + * - improved wv_set_multicast_list + * - catch spurious interrupt + * - correct release of the device + * + * Changes mades in release : + * ------------------------ + * - Reorganisation of the code, function name change + * - Creation of private header (wavelan_cs.h) + * - Reorganised debug messages + * - More comments, history, ... + * - Configure earlier (in "insert" instead of "open") + * and do things only once + * - mmc_init : configure the PSA if not done + * - mmc_init : 2.00 detection better code for 2.00 init + * - better info at startup + * - Correct a HUGE bug (volatile & uncalibrated busy loop) + * in wv_82593_cmd => config speedup + * - Stop receiving & power down on close (and power up on open) + * use "ifconfig down" & "ifconfig up ; route add -net ..." + * - Send packets : add watchdog instead of pooling + * - Receive : check frame wrap around & try to recover some frames + * - wavelan_set_multicast_list : avoid reset + * - add wireless extensions (ioctl & get_wireless_stats) + * get/set nwid/frequency on fly, info for /proc/net/wireless + * - Supress useless stuff from lp (net_local), but add link + * - More inlines + * - Lot of others minor details & cleanups + * + * Changes made in second release : + * ------------------------------ + * - Optimise wv_85893_reconfig stuff, fix potential problems + * - Change error values for ioctl + * - Non blocking wv_ru_stop() + call wv_reset() in case of problems + * - Remove development printk from wavelan_watchdog() + * - Remove of the watchdog to wavelan_close instead of wavelan_release + * fix potential problems... + * - Start debugging suspend stuff (but it's still a bit weird) + * - Debug & optimize dump header/packet in Rx & Tx (debug) + * - Use "readb" and "writeb" to be kernel 2.1 compliant + * - Better handling of bogus interrupts + * - Wireless extension : SETSPY and GETSPY + * - Remove old stuff (stats - for those needing it, just ask me...) + * - Make wireless extensions optional + * + * Changes made in third release : + * ----------------------------- + * - cleanups & typos + * - modif wireless ext (spy -> only one pointer) + * - new private ioctl to set/get quality & level threshold + * - Init : correct default value of level threshold for pcmcia + * - kill watchdog in hw_reset + * - more 2.1 support (copy_to/from_user instead of memcpy_to/fromfs) + * - Add message level (debug stuff in /var/adm/debug & errors not + * displayed at console and still in /var/adm/messages) + * + * Changes made in fourth release : + * ------------------------------ + * - multicast support (yes !) thanks to Yongguang Zhang. + * + * Changes made in fifth release (2.9.0) : + * ------------------------------------- + * - Revisited multicast code (it was mostly wrong). + * - protect code in wv_82593_reconfig with dev->tbusy (oups !) + * + * Changes made in sixth release (2.9.1a) : + * -------------------------------------- + * - Change the detection code for multi manufacturer code support + * - Correct bug (hang kernel) in init when we were "rejecting" a card + * + * Changes made in seventh release (2.9.1b) : + * ---------------------------------------- + * - Update to wireless extensions changes + * - Silly bug in card initial configuration (psa_conf_status) + * + * Changes made in eigth release : + * ----------------------------- + * - Small bug in debug code (probably not the last one...) + * - 1.2.13 support (thanks to Clark Woodworth) + * + * Changes made for release in 2.9.2b : + * ---------------------------------- + * - Level threshold is now a standard wireless extension (version 4 !) + * - modules parameters types for kernel > 2.1.17 + * - updated man page + * - Others cleanup from David Hinds + * + * Changes made for release in 2.9.5 : + * --------------------------------- + * - byte count stats (courtesy of David Hinds) + * - Remove dev_tint stuff (courtesy of David Hinds) + * - Others cleanup from David Hinds + * - Encryption setting from Brent Elphick (thanks a lot !) + * - 'base' to 'u_long' for the Alpha (thanks to Stanislav Sinyagin) + * + * Changes made for release in 2.9.6 : + * --------------------------------- + * - fix bug : no longuer disable watchdog in case of bogus interrupt + * - increase timeout in config code for picky hardware + * - mask unused bits in status (Wireless Extensions) + * + * Changes integrated by Justin Seger <jseger@MIT.EDU> & David Hinds : + * ----------------------------------------------------------------- + * - Roaming "hack" from Joe Finney <joe@comp.lancs.ac.uk> + * - PSA CRC code from Bob Gray <rgray@bald.cs.dartmouth.edu> + * - Better initialisation of the i82593 controller + * from Joseph K. O'Sullivan <josullvn+@cs.cmu.edu> + * + * Changes made for release in 3.0.10 : + * ---------------------------------- + * - Fix eject "hang" of the driver under 2.2.X : + * o create wv_flush_stale_links() + * o Rename wavelan_release to wv_pcmcia_release & move up + * o move unregister_netdev to wavelan_detach() + * o wavelan_release() no longer call wavelan_detach() + * o Supress "release" timer + * o Other cleanups & fixes + * - New MAC address in the probe + * - Reorg PSA_CRC code (endian neutral & cleaner) + * - Correct initialisation of the i82593 from Lucent manual + * - Put back the watchdog, with larger timeout + * - TRANSMIT_NO_CRC is a "normal" error, so recover from it + * from Derrick J Brashear <shadow@dementia.org> + * - Better handling of TX and RX normal failure conditions + * - #ifdef out all the roaming code + * - Add ESSID & "AP current address" ioctl stubs + * - General cleanup of the code + * + * Changes made for release in 3.0.13 : + * ---------------------------------- + * - Re-enable compilation of roaming code by default, but with + * do_roaming = 0 + * - Nuke `nwid=nwid^ntohs(beacon->domain_id)' in wl_roam_gather + * at the demand of John Carol Langford <jcl@gs176.sp.cs.cmu.edu> + * - Introduced WAVELAN_ROAMING_EXT for incomplete ESSID stuff. + * + * Changes made for release in 3.0.15 : + * ---------------------------------- + * - Change e-mail and web page addresses + * - Watchdog timer is now correctly expressed in HZ, not in jiffies + * - Add channel number to the list of frequencies in range + * - Add the (short) list of bit-rates in range + * - Developp a new sensitivity... (sens.value & sens.fixed) + * + * Changes made for release in 3.1.2 : + * --------------------------------- + * - Fix check for root permission (break instead of exit) + * - New nwid & encoding setting (Wireless Extension 9) + * + * Wishes & dreams: + * ---------------- + * - Cleanup and integrate the roaming code + * (std debug, set DomainID, decay avg and co...) + */ + +/***************************** INCLUDES *****************************/ + +/* Linux headers that we need */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/ptrace.h> +#include <linux/malloc.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/interrupt.h> +#include <linux/in.h> +#include <linux/delay.h> +#include <asm/uaccess.h> +#include <asm/io.h> +#include <asm/system.h> +#include <asm/bitops.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/if_arp.h> +#include <linux/ioport.h> +#include <linux/fcntl.h> + +#ifdef CONFIG_NET_RADIO +#include <linux/wireless.h> /* Wireless extensions */ +#endif + +/* Pcmcia headers that we need */ +#include <pcmcia/cs_types.h> +#include <pcmcia/cs.h> +#include <pcmcia/cistpl.h> +#include <pcmcia/cisreg.h> +#include <pcmcia/ds.h> +#include <pcmcia/version.h> + +/* Wavelan declarations */ +#include "i82593.h" /* Definitions for the Intel chip */ + +#include "wavelan.h" /* Others bits of the hardware */ + +/************************** DRIVER OPTIONS **************************/ +/* + * `#define' or `#undef' the following constant to change the behaviour + * of the driver... + */ +#define WAVELAN_ROAMING /* Include experimental roaming code */ +#undef WAVELAN_ROAMING_EXT /* Enable roaming wireless extensions */ +#undef SET_PSA_CRC /* Set the CRC in PSA (slower) */ +#define USE_PSA_CONFIG /* Use info from the PSA */ +#undef STRUCT_CHECK /* Verify padding of structures */ +#undef EEPROM_IS_PROTECTED /* Doesn't seem to be necessary */ +#define MULTICAST_AVOID /* Avoid extra multicast (I'm sceptical) */ +#undef SET_MAC_ADDRESS /* Experimental */ + +#ifdef WIRELESS_EXT /* If wireless extension exist in the kernel */ +/* Warning : these stuff will slow down the driver... */ +#define WIRELESS_SPY /* Enable spying addresses */ +#undef HISTOGRAM /* Enable histogram of sig level... */ +#endif + +/****************************** DEBUG ******************************/ + +#undef DEBUG_MODULE_TRACE /* Module insertion/removal */ +#undef DEBUG_CALLBACK_TRACE /* Calls made by Linux */ +#undef DEBUG_INTERRUPT_TRACE /* Calls to handler */ +#undef DEBUG_INTERRUPT_INFO /* type of interrupt & so on */ +#define DEBUG_INTERRUPT_ERROR /* problems */ +#undef DEBUG_CONFIG_TRACE /* Trace the config functions */ +#undef DEBUG_CONFIG_INFO /* What's going on... */ +#define DEBUG_CONFIG_ERRORS /* Errors on configuration */ +#undef DEBUG_TX_TRACE /* Transmission calls */ +#undef DEBUG_TX_INFO /* Header of the transmited packet */ +#undef DEBUG_TX_FAIL /* Normal failure conditions */ +#define DEBUG_TX_ERROR /* Unexpected conditions */ +#undef DEBUG_RX_TRACE /* Transmission calls */ +#undef DEBUG_RX_INFO /* Header of the transmited packet */ +#undef DEBUG_RX_FAIL /* Normal failure conditions */ +#define DEBUG_RX_ERROR /* Unexpected conditions */ +#undef DEBUG_PACKET_DUMP 32 /* Dump packet on the screen */ +#undef DEBUG_IOCTL_TRACE /* Misc call by Linux */ +#undef DEBUG_IOCTL_INFO /* Various debug info */ +#define DEBUG_IOCTL_ERROR /* What's going wrong */ +#define DEBUG_BASIC_SHOW /* Show basic startup info */ +#undef DEBUG_VERSION_SHOW /* Print version info */ +#undef DEBUG_PSA_SHOW /* Dump psa to screen */ +#undef DEBUG_MMC_SHOW /* Dump mmc to screen */ +#undef DEBUG_SHOW_UNUSED /* Show also unused fields */ +#undef DEBUG_I82593_SHOW /* Show i82593 status */ +#undef DEBUG_DEVICE_SHOW /* Show device parameters */ + +/************************ CONSTANTS & MACROS ************************/ + +#ifdef DEBUG_VERSION_SHOW +static const char *version = "wavelan_cs.c : v21 (wireless extensions) 18/10/99\n"; +#endif + +/* Watchdog temporisation */ +#define WATCHDOG_JIFFIES (256*HZ/100) + +/* Fix a bug in some old wireless extension definitions */ +#ifndef IW_ESSID_MAX_SIZE +#define IW_ESSID_MAX_SIZE 32 +#endif + +/* ------------------------ PRIVATE IOCTL ------------------------ */ + +#define SIOCSIPQTHR SIOCDEVPRIVATE /* Set quality threshold */ +#define SIOCGIPQTHR SIOCDEVPRIVATE + 1 /* Get quality threshold */ +#define SIOCSIPROAM SIOCDEVPRIVATE + 2 /* Set roaming state */ +#define SIOCGIPROAM SIOCDEVPRIVATE + 3 /* Get roaming state */ + +#define SIOCSIPHISTO SIOCDEVPRIVATE + 6 /* Set histogram ranges */ +#define SIOCGIPHISTO SIOCDEVPRIVATE + 7 /* Get histogram values */ + +/*************************** WaveLAN Roaming **************************/ +#ifdef WAVELAN_ROAMING /* Conditional compile, see above in options */ + +#define WAVELAN_ROAMING_DEBUG 0 /* 1 = Trace of handover decisions */ + /* 2 = Info on each beacon rcvd... */ +#define MAX_WAVEPOINTS 7 /* Max visible at one time */ +#define WAVEPOINT_HISTORY 5 /* SNR sample history slow search */ +#define WAVEPOINT_FAST_HISTORY 2 /* SNR sample history fast search */ +#define SEARCH_THRESH_LOW 10 /* SNR to enter cell search */ +#define SEARCH_THRESH_HIGH 13 /* SNR to leave cell search */ +#define WAVELAN_ROAMING_DELTA 1 /* Hysteresis value (+/- SNR) */ +#define CELL_TIMEOUT 2*HZ /* in jiffies */ + +#define FAST_CELL_SEARCH 1 /* Boolean values... */ +#define NWID_PROMISC 1 /* for code clarity. */ + +typedef struct wavepoint_beacon +{ + unsigned char dsap, /* Unused */ + ssap, /* Unused */ + ctrl, /* Unused */ + O,U,I, /* Unused */ + spec_id1, /* Unused */ + spec_id2, /* Unused */ + pdu_type, /* Unused */ + seq; /* WavePoint beacon sequence number */ + unsigned short domain_id, /* WavePoint Domain ID */ + nwid; /* WavePoint NWID */ +} wavepoint_beacon; + +typedef struct wavepoint_history +{ + unsigned short nwid; /* WavePoint's NWID */ + int average_slow; /* SNR running average */ + int average_fast; /* SNR running average */ + unsigned char sigqual[WAVEPOINT_HISTORY]; /* Ringbuffer of recent SNR's */ + unsigned char qualptr; /* Index into ringbuffer */ + unsigned char last_seq; /* Last seq. no seen for WavePoint */ + struct wavepoint_history *next; /* Next WavePoint in table */ + struct wavepoint_history *prev; /* Previous WavePoint in table */ + unsigned long last_seen; /* Time of last beacon recvd, jiffies */ +} wavepoint_history; + +struct wavepoint_table +{ + wavepoint_history *head; /* Start of ringbuffer */ + int num_wavepoints; /* No. of WavePoints visible */ + unsigned char locked; /* Table lock */ +}; + +#endif /* WAVELAN_ROAMING */ + +/****************************** TYPES ******************************/ + +/* Shortcuts */ +typedef struct net_device device; +typedef struct net_device_stats en_stats; +typedef struct iw_statistics iw_stats; +typedef struct iw_quality iw_qual; +typedef struct iw_freq iw_freq; +typedef struct net_local net_local; +typedef struct timer_list timer_list; + +/* Basic types */ +typedef u_char mac_addr[WAVELAN_ADDR_SIZE]; /* Hardware address */ + +/* + * Static specific data for the interface. + * + * For each network interface, Linux keep data in two structure. "device" + * keep the generic data (same format for everybody) and "net_local" keep + * the additional specific data. + * Note that some of this specific data is in fact generic (en_stats, for + * example). + */ +struct net_local +{ + dev_node_t node; /* ???? What is this stuff ???? */ + device * dev; /* Reverse link... */ + dev_link_t * link; /* pcmcia structure */ + en_stats stats; /* Ethernet interface statistics */ + int nresets; /* Number of hw resets */ + u_char configured; /* If it is configured */ + u_char reconfig_82593; /* Need to reconfigure the controler */ + u_char promiscuous; /* Promiscuous mode */ + u_char allmulticast; /* All Multicast mode */ + int mc_count; /* Number of multicast addresses */ + timer_list watchdog; /* To avoid blocking state */ + + u_char status; /* Current i82593 status */ + int stop; /* Current i82593 Stop Hit Register */ + int rfp; /* Last DMA machine receive pointer */ + int overrunning; /* Receiver overrun flag */ + +#ifdef WIRELESS_EXT + iw_stats wstats; /* Wireless specific stats */ +#endif + +#ifdef WIRELESS_SPY + int spy_number; /* Number of addresses to spy */ + mac_addr spy_address[IW_MAX_SPY]; /* The addresses to spy */ + iw_qual spy_stat[IW_MAX_SPY]; /* Statistics gathered */ +#endif /* WIRELESS_SPY */ +#ifdef HISTOGRAM + int his_number; /* Number of intervals */ + u_char his_range[16]; /* Boundaries of interval ]n-1; n] */ + u_long his_sum[16]; /* Sum in interval */ +#endif /* HISTOGRAM */ +#ifdef WAVELAN_ROAMING + u_long domain_id; /* Domain ID we lock on for roaming */ + int filter_domains; /* Check Domain ID of beacon found */ + struct wavepoint_table wavepoint_table; /* Table of visible WavePoints*/ + wavepoint_history * curr_point; /* Current wavepoint */ + int cell_search; /* Searching for new cell? */ + struct timer_list cell_timer; /* Garbage collection */ +#endif /* WAVELAN_ROAMING */ +}; + +/**************************** PROTOTYPES ****************************/ + +#ifdef WAVELAN_ROAMING +/* ---------------------- ROAMING SUBROUTINES -----------------------*/ + +wavepoint_history *wl_roam_check(unsigned short nwid, net_local *lp); +wavepoint_history *wl_new_wavepoint(unsigned short nwid, unsigned char seq, net_local *lp); +void wl_del_wavepoint(wavepoint_history *wavepoint, net_local *lp); +void wl_cell_expiry(unsigned long data); +wavepoint_history *wl_best_sigqual(int fast_search, net_local *lp); +void wl_update_history(wavepoint_history *wavepoint, unsigned char sigqual, unsigned char seq); +void wv_roam_handover(wavepoint_history *wavepoint, net_local *lp); +void wv_nwid_filter(unsigned char mode, net_local *lp); +void wv_roam_init(struct net_device *dev); +void wv_roam_cleanup(struct net_device *dev); +#endif /* WAVELAN_ROAMING */ + +/* ----------------------- MISC SUBROUTINES ------------------------ */ +static inline unsigned long /* flags */ + wv_splhi(void); /* Disable interrupts */ +static inline void + wv_splx(unsigned long); /* ReEnable interrupts : flags */ +static void + cs_error(client_handle_t, /* Report error to cardmgr */ + int, + int); +/* ----------------- MODEM MANAGEMENT SUBROUTINES ----------------- */ +static inline u_char /* data */ + hasr_read(u_long); /* Read the host interface : base address */ +static inline void + hacr_write(u_long, /* Write to host interface : base address */ + u_char), /* data */ + hacr_write_slow(u_long, + u_char); +static void + psa_read(device *, /* Read the Parameter Storage Area */ + int, /* offset in PSA */ + u_char *, /* buffer to fill */ + int), /* size to read */ + psa_write(device *, /* Write to the PSA */ + int, /* Offset in psa */ + u_char *, /* Buffer in memory */ + int); /* Length of buffer */ +static inline void + mmc_out(u_long, /* Write 1 byte to the Modem Manag Control */ + u_short, + u_char), + mmc_write(u_long, /* Write n bytes to the MMC */ + u_char, + u_char *, + int); +static inline u_char /* Read 1 byte from the MMC */ + mmc_in(u_long, + u_short); +static inline void + mmc_read(u_long, /* Read n bytes from the MMC */ + u_char, + u_char *, + int), + fee_wait(u_long, /* Wait for frequency EEprom : base address */ + int, /* Base delay to wait for */ + int); /* Number of time to wait */ +static void + fee_read(u_long, /* Read the frequency EEprom : base address */ + u_short, /* destination offset */ + u_short *, /* data buffer */ + int); /* number of registers */ +/* ---------------------- I82593 SUBROUTINES ----------------------- */ +static int + wv_82593_cmd(device *, /* synchronously send a command to i82593 */ + char *, + int, + int); +static inline int + wv_diag(device *); /* Diagnostique the i82593 */ +static int + read_ringbuf(device *, /* Read a receive buffer */ + int, + char *, + int); +static inline void + wv_82593_reconfig(device *); /* Reconfigure the controler */ +/* ------------------- DEBUG & INFO SUBROUTINES ------------------- */ +static inline void + wv_init_info(device *); /* display startup info */ +/* ------------------- IOCTL, STATS & RECONFIG ------------------- */ +static en_stats * + wavelan_get_stats(device *); /* Give stats /proc/net/dev */ +/* ----------------------- PACKET RECEPTION ----------------------- */ +static inline int + wv_start_of_frame(device *, /* Seek beggining of current frame */ + int, /* end of frame */ + int); /* start of buffer */ +static inline void + wv_packet_read(device *, /* Read a packet from a frame */ + int, + int), + wv_packet_rcv(device *); /* Read all packets waiting */ +/* --------------------- PACKET TRANSMISSION --------------------- */ +static inline void + wv_packet_write(device *, /* Write a packet to the Tx buffer */ + void *, + short); +static int + wavelan_packet_xmit(struct sk_buff *, /* Send a packet */ + device *); +/* -------------------- HARDWARE CONFIGURATION -------------------- */ +static inline int + wv_mmc_init(device *); /* Initialize the modem */ +static int + wv_ru_stop(device *), /* Stop the i82593 receiver unit */ + wv_ru_start(device *); /* Start the i82593 receiver unit */ +static int + wv_82593_config(device *); /* Configure the i82593 */ +static inline int + wv_pcmcia_reset(device *); /* Reset the pcmcia interface */ +static int + wv_hw_config(device *); /* Reset & configure the whole hardware */ +static inline void + wv_hw_reset(device *); /* Same, + start receiver unit */ +static inline int + wv_pcmcia_config(dev_link_t *); /* Configure the pcmcia interface */ +static void + wv_pcmcia_release(u_long), /* Remove a device */ + wv_flush_stale_links(void); /* "detach" all possible devices */ +/* ---------------------- INTERRUPT HANDLING ---------------------- */ +static void +wavelan_interrupt(int, /* Interrupt handler */ + void *, + struct pt_regs *); +static void + wavelan_watchdog(u_long); /* Transmission watchdog */ +/* ------------------- CONFIGURATION CALLBACKS ------------------- */ +static int + wavelan_open(device *), /* Open the device */ + wavelan_close(device *), /* Close the device */ + wavelan_init(device *); /* Do nothing */ +static dev_link_t * + wavelan_attach(void); /* Create a new device */ +static void + wavelan_detach(dev_link_t *); /* Destroy a removed device */ +static int + wavelan_event(event_t, /* Manage pcmcia events */ + int, + event_callback_args_t *); + +/**************************** VARIABLES ****************************/ + +static dev_info_t dev_info = "wavelan_cs"; +static dev_link_t *dev_list = NULL; /* Linked list of devices */ + +/* WARNING : the following variable MUST be volatile + * It is used by wv_82593_cmd to syncronise with wavelan_interrupt */ +static volatile int wv_wait_completed = 0; + +/* + * Parameters that can be set with 'insmod' + * The exact syntax is 'insmod wavelan_cs.o <var>=<value>' + */ + +/* Bit map of interrupts to choose from */ +/* This means pick from 15, 14, 12, 11, 10, 9, 7, 5, 4 and 3 */ +static int irq_mask = 0xdeb8; +static int irq_list[4] = { -1 }; + +/* Shared memory speed, in ns */ +static int mem_speed = 0; + +/* New module interface */ +MODULE_PARM(irq_mask, "i"); +MODULE_PARM(irq_list, "1-4i"); +MODULE_PARM(mem_speed, "i"); + +#ifdef WAVELAN_ROAMING /* Conditional compile, see above in options */ +/* Enable roaming mode ? No ! Please keep this to 0 */ +static int do_roaming = 0; +MODULE_PARM(do_roaming, "i"); +#endif /* WAVELAN_ROAMING */ + +#endif /* WAVELAN_CS_H */ + diff --git a/drivers/net/pcmcia/xirc2ps_cs.c b/drivers/net/pcmcia/xirc2ps_cs.c new file mode 100644 index 000000000..d09230655 --- /dev/null +++ b/drivers/net/pcmcia/xirc2ps_cs.c @@ -0,0 +1,2364 @@ +/* [xirc2ps_cs.c wk 14.04.97] (1.31 1998/12/09 19:32:55) + * Xircom Creditcard Ethernet Adapter IIps driver + * + * This driver works for the CE2, CEM28, CEM33, CE3 and CEM56 cards. + * The CEM56 has some problems, but it works. + * The CEII card with the 14k4 modem and other old cards do not work. + * + * Written by Werner Koch (werner.koch@guug.de), + * based on David Hinds skeleton driver. + * + * You can get the latest driver revision from + * "http://www.d.shuttle.de/isil/xircom/xirc2ps.html" + * + * Please report bugs to: "xircom-bugs@isil.d.shuttle.de" + * + * A bug fix for the CEM56 to use modem and ethernet simultaneously + * was provided by Koen Van Herck (Koen.Van.Herck@xircom.com). + * + * If your card locks up you should use the option "lockup_hack=1"; + * this may solve the problem but violates a kernel timing convention + * (Thanks to David Luyer). + * + * Thanks to David Hinds for the PCMCIA package, Donald Becker for some + * advice, Xircom for providing specs and help, 4PC GmbH Duesseldorf for + * providing some hardware and last not least to all folks who helped to + * develop this driver. + * + * For those, who are willing to do alpha testing of drivers, I have setup + * the mailing list "xircom-devel@isil.d.shuttle.de" (To subscribe send a + * message containing the word "subscribe" in the subject or somewhere at + * the beginning of a line to "xircom-devel-request@isil.d.shuttle.de"). + * + ************************************************************************ + * Copyright (c) 1997,1998 Werner Koch (dd9jn) + * + * This driver 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. + * + * It 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 + * + * + * ALTERNATIVELY, this driver may be distributed under the terms of + * the following license, in which case the provisions of this license + * are required INSTEAD OF the GNU General Public License. (This clause + * is necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* Enable the bug fix for CEM56 to use modem and ethernet simultaneously */ +#define CEM56_FIX + +#if !defined(PCMCIA_DEBUG) && 0 + #define PCMCIA_DEBUG 4 +#endif + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/ptrace.h> +#include <linux/malloc.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/interrupt.h> +#include <linux/in.h> +#include <linux/delay.h> +#include <asm/io.h> +#include <asm/system.h> +#include <asm/bitops.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/if_arp.h> +#include <linux/ioport.h> + +#include <pcmcia/version.h> +#include <pcmcia/cs_types.h> +#include <pcmcia/cs.h> +#include <pcmcia/cistpl.h> +#include <pcmcia/cisreg.h> +#include <pcmcia/ciscode.h> + +#ifndef MANFID_XIRCOM + #define MANFID_XIRCOM 0x0105 +#endif +#ifndef MANFID_COMPAQ + #define MANFID_COMPAQ 0x0138 + #define MANFID_COMPAQ2 0x0183 /* is this correct? */ +#endif +#ifndef MANFID_INTEL + #define MANFID_INTEL 0x0089 +#endif + +#include <pcmcia/ds.h> + +/* Time in jiffies before concluding Tx hung */ +#define TX_TIMEOUT ((400*HZ)/1000) + +/**************** + * Some constants used to access the hardware + */ + +/* Register offsets and value constans */ +#define XIRCREG_CR 0 /* Command register (wr) */ +enum xirc_cr { + TransmitPacket = 0x01, + SoftReset = 0x02, + EnableIntr = 0x04, + ForceIntr = 0x08, + ClearTxFIFO = 0x10, + ClearRxOvrun = 0x20, + RestartTx = 0x40 +}; +#define XIRCREG_ESR 0 /* Ethernet status register (rd) */ +enum xirc_esr { + FullPktRcvd = 0x01, /* full packet in receive buffer */ + PktRejected = 0x04, /* a packet has been rejected */ + TxPktPend = 0x08, /* TX Packet Pending */ + IncorPolarity = 0x10, + MediaSelect = 0x20 /* set if TP, clear if AUI */ +}; +#define XIRCREG_PR 1 /* Page Register select */ +#define XIRCREG_EDP 4 /* Ethernet Data Port Register */ +#define XIRCREG_ISR 6 /* Ethernet Interrupt Status Register */ +enum xirc_isr { + TxBufOvr = 0x01, /* TX Buffer Overflow */ + PktTxed = 0x02, /* Packet Transmitted */ + MACIntr = 0x04, /* MAC Interrupt occured */ + TxResGrant = 0x08, /* Tx Reservation Granted */ + RxFullPkt = 0x20, /* Rx Full Packet */ + RxPktRej = 0x40, /* Rx Packet Rejected */ + ForcedIntr= 0x80 /* Forced Interrupt */ +}; +#define XIRCREG1_IMR0 12 /* Ethernet Interrupt Mask Register (on page 1)*/ +#define XIRCREG1_IMR1 13 +#define XIRCREG0_TSO 8 /* Transmit Space Open Register (on page 0)*/ +#define XIRCREG0_TRS 10 /* Transmit reservation Size Register (page 0)*/ +#define XIRCREG0_DO 12 /* Data Offset Register (page 0) (wr) */ +#define XIRCREG0_RSR 12 /* Receive Status Register (page 0) (rd) */ +enum xirc_rsr { + PhyPkt = 0x01, /* set:physical packet, clear: multicast packet */ + BrdcstPkt = 0x02, /* set if it is a broadcast packet */ + PktTooLong = 0x04, /* set if packet length > 1518 */ + AlignErr = 0x10, /* incorrect CRC and last octet not complete */ + CRCErr = 0x20, /* incorrect CRC and last octet is complete */ + PktRxOk = 0x80 /* received ok */ +}; +#define XIRCREG0_PTR 13 /* packets transmitted register (rd) */ +#define XIRCREG0_RBC 14 /* receive byte count regsister (rd) */ +#define XIRCREG1_ECR 14 /* ethernet configurationn register */ +enum xirc_ecr { + FullDuplex = 0x04, /* enable full duplex mode */ + LongTPMode = 0x08, /* adjust for longer lengths of TP cable */ + DisablePolCor = 0x10,/* disable auto polarity correction */ + DisableLinkPulse = 0x20, /* disable link pulse generation */ + DisableAutoTx = 0x40, /* disable auto-transmit */ +}; +#define XIRCREG2_RBS 8 /* receive buffer start register */ +#define XIRCREG2_LED 10 /* LED Configuration register */ +/* values for the leds: Bits 2-0 for led 1 + * 0 disabled Bits 5-3 for led 2 + * 1 collision + * 2 noncollision + * 3 link_detected + * 4 incor_polarity + * 5 jabber + * 6 auto_assertion + * 7 rx_tx_activity + */ +#define XIRCREG2_MSR 12 /* Mohawk specific register */ + +#define XIRCREG4_GPR0 8 /* General Purpose Register 0 */ +#define XIRCREG4_GPR1 9 /* General Purpose Register 1 */ +#define XIRCREG2_GPR2 13 /* General Purpose Register 2 (page2!)*/ +#define XIRCREG4_BOV 10 /* Bonding Version Register */ +#define XIRCREG4_LMA 12 /* Local Memory Address Register */ +#define XIRCREG4_LMD 14 /* Local Memory Data Port */ +/* MAC register can only by accessed with 8 bit operations */ +#define XIRCREG40_CMD0 8 /* Command Register (wr) */ +enum xirc_cmd { /* Commands */ + Transmit = 0x01, + EnableRecv = 0x04, + DisableRecv = 0x08, + Abort = 0x10, + Online = 0x20, + IntrAck = 0x40, + Offline = 0x80 +}; +#define XIRCREG5_RHSA0 10 /* Rx Host Start Address */ +#define XIRCREG40_RXST0 9 /* Receive Status Register */ +#define XIRCREG40_TXST0 11 /* Transmit Status Register 0 */ +#define XIRCREG40_TXST1 12 /* Transmit Status Register 10 */ +#define XIRCREG40_RMASK0 13 /* Receive Mask Register */ +#define XIRCREG40_TMASK0 14 /* Transmit Mask Register 0 */ +#define XIRCREG40_TMASK1 15 /* Transmit Mask Register 0 */ +#define XIRCREG42_SWC0 8 /* Software Configuration 0 */ +#define XIRCREG42_SWC1 9 /* Software Configuration 1 */ +#define XIRCREG42_BOC 10 /* Back-Off Configuration */ +#define XIRCREG44_TDR0 8 /* Time Domain Reflectometry 0 */ +#define XIRCREG44_TDR1 9 /* Time Domain Reflectometry 1 */ +#define XIRCREG44_RXBC_LO 10 /* Rx Byte Count 0 (rd) */ +#define XIRCREG44_RXBC_HI 11 /* Rx Byte Count 1 (rd) */ +#define XIRCREG45_REV 15 /* Revision Register (rd) */ +#define XIRCREG50_IA 8 /* Individual Address (8-13) */ + +static char *if_names[] = { "Auto", "10BaseT", "10Base2", "AUI", "100BaseT" }; + +/**************** + * All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If + * you do not define PCMCIA_DEBUG at all, all the debug code will be + * left out. If you compile with PCMCIA_DEBUG=0, the debug code will + * be present but disabled -- but it can then be enabled for specific + * modules at load time with a 'pc_debug=#' option to insmod. + */ +#ifdef PCMCIA_DEBUG +static int pc_debug = PCMCIA_DEBUG; +MODULE_PARM(pc_debug, "i"); +#endif +static char *version = +"xirc2ps_cs.c 1.31 1998/12/09 19:32:55 (dd9jn+kvh)"; + /* !--- CVS revision */ +#define KDBG_XIRC KERN_DEBUG "xirc2ps_cs: " +#define KERR_XIRC KERN_ERR "xirc2ps_cs: " +#define KWRN_XIRC KERN_WARNING "xirc2ps_cs: " +#define KNOT_XIRC KERN_NOTICE "xirc2ps_cs: " +#define KINF_XIRC KERN_INFO "xirc2ps_cs: " + +/* card types */ +#define XIR_UNKNOWN 0 /* unknown: not supported */ +#define XIR_CE 1 /* (prodid 1) different hardware: not supported */ +#define XIR_CE2 2 /* (prodid 2) */ +#define XIR_CE3 3 /* (prodid 3) */ +#define XIR_CEM 4 /* (prodid 1) different hardware: not supported */ +#define XIR_CEM2 5 /* (prodid 2) */ +#define XIR_CEM3 6 /* (prodid 3) */ +#define XIR_CEM33 7 /* (prodid 4) */ +#define XIR_CEM56M 8 /* (prodid 5) */ +#define XIR_CEM56 9 /* (prodid 6) */ +#define XIR_CM28 10 /* (prodid 3) modem only: not supported here */ +#define XIR_CM33 11 /* (prodid 4) modem only: not supported here */ +#define XIR_CM56 12 /* (prodid 5) modem only: not supported here */ +#define XIR_CG 13 /* (prodid 1) GSM modem only: not supported */ +#define XIR_CBE 14 /* (prodid 1) cardbus ethernet: not supported */ +/*====================================================================*/ + +/* Parameters that can be set with 'insmod' */ +static int if_port = 0; +MODULE_PARM(if_port, "i"); + +/* Bit map of interrupts to choose from */ +/* This means pick from 15, 14, 12, 11, 10, 9, 7, 5, 4, and 3 */ +static u_long irq_mask = 0xdeb8; +MODULE_PARM(irq_mask, "i"); + +static int irq_list[4] = { -1 }; +MODULE_PARM(irq_list, "1-4i"); + +static int do_sound = 1; +MODULE_PARM(do_sound, "i"); + +static int card_type = 0; +MODULE_PARM(card_type, "i"); /* dummy, not used anymore */ + +static int lockup_hack = 0; +MODULE_PARM(lockup_hack, "i"); /* anti lockup hack */ + +/*====================================================================*/ + +/* We do not process more than these number of bytes during one + * interrupt. (Of course we receive complete packets, so this is not + * an exact value). + * Something between 2000..22000; first value gives best interrupt latency, + * the second enables the usage of the complete on-chip buffer. We use the + * high value as the initial value. + */ +static unsigned maxrx_bytes = 22000; + +/* MII management prototypes */ +static void mii_idle(ioaddr_t ioaddr); +static void mii_putbit(ioaddr_t ioaddr, unsigned data); +static int mii_getbit( ioaddr_t ioaddr ); +static void mii_wbits(ioaddr_t ioaddr, unsigned data, int len); +static unsigned mii_rd(ioaddr_t ioaddr, u_char phyaddr, u_char phyreg); +static void mii_wr(ioaddr_t ioaddr, u_char phyaddr, u_char phyreg, + unsigned data, int len); + +/* + * The event() function is this driver's Card Services event handler. + * It will be called by Card Services when an appropriate card status + * event is received. The config() and release() entry points are + * used to configure or release a socket, in response to card insertion + * and ejection events. They are invoked from the event handler. + */ + +static int has_ce2_string(dev_link_t * link); +static void xirc2ps_config(dev_link_t * link); +static void xirc2ps_release(u_long arg); +static int xirc2ps_event(event_t event, int priority, + event_callback_args_t * args); + +/**************** + * The attach() and detach() entry points are used to create and destroy + * "instances" of the driver, where each instance represents everything + * needed to manage one actual PCMCIA card. + */ + +static dev_link_t *xirc2ps_attach(void); +static void xirc2ps_detach(dev_link_t *); + +/**************** + * You'll also need to prototype all the functions that will actually + * be used to talk to your device. See 'pcmem_cs' for a good example + * of a fully self-sufficient driver; the other drivers rely more or + * less on other parts of the kernel. + */ + +static void xirc2ps_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +/* + * The dev_info variable is the "key" that is used to match up this + * device driver with appropriate cards, through the card configuration + * database. + */ + +static dev_info_t dev_info = "xirc2ps_cs"; + +/**************** + * A linked list of "instances" of the device. Each actual + * PCMCIA card corresponds to one device instance, and is described + * by one dev_link_t structure (defined in ds.h). + * + * You may not want to use a linked list for this -- for example, the + * memory card driver uses an array of dev_link_t pointers, where minor + * device numbers are used to derive the corresponding array index. + */ + +static dev_link_t *dev_list = NULL; + +/**************** + * A dev_link_t structure has fields for most things that are needed + * to keep track of a socket, but there will usually be some device + * specific information that also needs to be kept track of. The + * 'priv' pointer in a dev_link_t structure can be used to point to + * a device-specific private data structure, like this. + * + * A driver needs to provide a dev_node_t structure for each device + * on a card. In some cases, there is only one device per card (for + * example, ethernet cards, modems). In other cases, there may be + * many actual or logical devices (SCSI adapters, memory cards with + * multiple partitions). The dev_node_t structures need to be kept + * in a linked list starting at the 'dev' field of a dev_link_t + * structure. We allocate them in the card's private data structure, + * because they generally can't be allocated dynamically. + */ + +typedef struct local_info_t { + dev_node_t node; + struct enet_statistics stats; + int card_type; + int probe_port; + int silicon; /* silicon revision. 0=old CE2, 1=Scipper, 4=Mohawk */ + int mohawk; /* a CE3 type card */ + int dingo; /* a CEM56 type card */ + int modem; /* is a multi function card (i.e with a modem) */ + caddr_t dingo_ccr; /* only used for CEM56 cards */ + int suspended; + unsigned last_ptr_value; /* last packets transmitted value */ + const char *manf_str; +} local_info_t; + +/**************** + * Some more prototypes + */ +static int do_start_xmit(struct sk_buff *skb, struct net_device *dev); +static struct enet_statistics *do_get_stats(struct net_device *dev); +static void set_addresses(struct net_device *dev); +static void set_multicast_list(struct net_device *dev); +static int do_init(struct net_device *dev); +static int set_card_type( dev_link_t *link, const void *s ); +static int do_config(struct net_device *dev, struct ifmap *map); +static int do_open(struct net_device *dev); +static int do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static void hardreset(struct net_device *dev); +static void do_reset(struct net_device *dev, int full); +static int init_mii(struct net_device *dev); +static void do_powerdown(struct net_device *dev); +static int do_stop(struct net_device *dev); + +/*=============== Helper functions =========================*/ +static void +flush_stale_links(void) +{ + dev_link_t *link, *next; + for (link = dev_list; link; link = next) { + next = link->next; + if (link->state & DEV_STALE_LINK) + xirc2ps_detach(link); + } +} + +static void +cs_error(client_handle_t handle, int func, int ret) +{ + error_info_t err = { func, ret }; + CardServices(ReportError, handle, &err); +} + +static int +get_tuple_data(int fn, client_handle_t handle, tuple_t *tuple ) +{ + int err; + + if( (err=CardServices(fn, handle, tuple)) ) + return err; + return CardServices(GetTupleData, handle, tuple); +} + +static int +get_tuple(int fn, client_handle_t handle, tuple_t *tuple, cisparse_t *parse) +{ + int err; + + if( (err=get_tuple_data(fn, handle, tuple)) ) + return err; + return CardServices(ParseTuple, handle, tuple, parse); +} + +#define first_tuple(a, b, c) get_tuple(GetFirstTuple, a, b, c) +#define next_tuple(a, b, c) get_tuple(GetNextTuple, a, b, c) + +#define SelectPage(pgnr) outb((pgnr), ioaddr + XIRCREG_PR) +#define GetByte(reg) ((unsigned)inb(ioaddr + (reg))) +#define GetWord(reg) ((unsigned)inw(ioaddr + (reg))) +#define PutByte(reg,value) outb((value), ioaddr+(reg)) +#define PutWord(reg,value) outw((value), ioaddr+(reg)) + +static void +busy_loop(u_long len) +{ + u_long timeout = jiffies + len; + u_long flags; + + save_flags(flags); + sti(); + while(timeout >= jiffies) + ; + restore_flags(flags); +} + +/*====== Functions used for debugging =================================*/ +#if defined(PCMCIA_DEBUG) && 0 /* reading regs may change system status */ +static void +PrintRegisters(struct net_device *dev) +{ + ioaddr_t ioaddr = dev->base_addr; + + if(pc_debug > 1) { + int i, page; + + printk(KDBG_XIRC "Register common: "); + for(i = 0; i < 8; i++ ) + printk(" %2.2x", GetByte(i) ); + printk("\n"); + for(page = 0; page <= 8; page++) { + printk(KDBG_XIRC "Register page %2x: ", page); + SelectPage(page); + for(i = 8; i < 16; i++) + printk(" %2.2x", GetByte(i)); + printk("\n"); + } + for(page=0x40 ; page <= 0x5f; page++) { + if( page == 0x43 || (page >= 0x46 && page <= 0x4f) + || (page >= 0x51 && page <=0x5e) ) + continue; + printk(KDBG_XIRC "Register page %2x: ", page); + SelectPage(page); + for(i = 8; i < 16; i++) + printk(" %2.2x", GetByte(i)); + printk("\n"); + } + } +} +#endif /* PCMCIA_DEBUG */ + +/*============== MII Management functions ===============*/ + +/**************** + * Turn around for read + */ +static void +mii_idle(ioaddr_t ioaddr) +{ + PutByte(XIRCREG2_GPR2, 0x04|0 ); /* drive MDCK low */ + udelay(1); + PutByte(XIRCREG2_GPR2, 0x04|1 ); /* and drive MDCK high */ + udelay(1); +} + +/**************** + * Write a bit to MDI/O + */ +static void +mii_putbit(ioaddr_t ioaddr, unsigned data) +{ + #if 1 + if( data ) { + PutByte(XIRCREG2_GPR2, 0x0c|2|0 ); /* set MDIO */ + udelay(1); + PutByte(XIRCREG2_GPR2, 0x0c|2|1 ); /* and drive MDCK high */ + udelay(1); + } + else { + PutByte(XIRCREG2_GPR2, 0x0c|0|0 ); /* clear MDIO */ + udelay(1); + PutByte(XIRCREG2_GPR2, 0x0c|0|1 ); /* and drive MDCK high */ + udelay(1); + } + #else + if( data ) { + PutWord(XIRCREG2_GPR2-1, 0x0e0e ); + udelay(1); + PutWord(XIRCREG2_GPR2-1, 0x0f0f ); + udelay(1); + } + else { + PutWord(XIRCREG2_GPR2-1, 0x0c0c ); + udelay(1); + PutWord(XIRCREG2_GPR2-1, 0x0d0d ); + udelay(1); + } + #endif +} + +/**************** + * Get a bit from MDI/O + */ +static int +mii_getbit( ioaddr_t ioaddr ) +{ + unsigned d; + + PutByte(XIRCREG2_GPR2, 4|0 ); /* drive MDCK low */ + udelay(1); + d = GetByte(XIRCREG2_GPR2); /* read MDIO */ + PutByte(XIRCREG2_GPR2, 4|1 ); /* drive MDCK high again */ + udelay(1); + return d & 0x20; /* read MDIO */ +} + +static void +mii_wbits(ioaddr_t ioaddr, unsigned data, int len) +{ + unsigned m = 1 << (len-1); + for( ; m; m >>= 1) + mii_putbit( ioaddr, data & m ); +} + +static unsigned +mii_rd(ioaddr_t ioaddr, u_char phyaddr, u_char phyreg) +{ + int i; + unsigned data=0, m; + + SelectPage(2); + for( i=0; i < 32; i++ ) /* 32 bit preamble */ + mii_putbit(ioaddr, 1); + mii_wbits(ioaddr, 0x06, 4); /* Start and opcode for read */ + mii_wbits(ioaddr, phyaddr, 5); /* PHY address to be accessed */ + mii_wbits(ioaddr, phyreg, 5); /* PHY register to read */ + mii_idle(ioaddr); /* turn around */ + mii_getbit( ioaddr); + + for( m = 1<<15; m; m >>= 1 ) + if( mii_getbit( ioaddr ) ) + data |= m; + mii_idle(ioaddr); + return data; +} + +static void +mii_wr(ioaddr_t ioaddr, u_char phyaddr, u_char phyreg, unsigned data, int len) +{ + int i; + + SelectPage(2); + for( i=0; i < 32; i++ ) /* 32 bit preamble */ + mii_putbit(ioaddr, 1); + mii_wbits(ioaddr, 0x05, 4); /* Start and opcode for write */ + mii_wbits(ioaddr, phyaddr, 5); /* PHY address to be accessed */ + mii_wbits(ioaddr, phyreg, 5); /* PHY Register to write */ + mii_putbit(ioaddr, 1); /* turn around */ + mii_putbit(ioaddr, 0); + mii_wbits(ioaddr, data, len); /* And write the data */ + mii_idle(ioaddr); +} + +#ifdef PCMCIA_DEBUG +static void +mii_dump(struct net_device *dev) +{ + ioaddr_t ioaddr = dev->base_addr; + int i; + + /* Note that registers 14, 1d,1e and 1f are reserved and should + * not be read according to the DP83840A specs. + */ + printk(KERN_DEBUG "%s: MII register dump:\n", dev->name); + for(i=0; i < 32; i++ ) { + if( !(i % 8) ) { + if( i ) + printk("\n"); + printk(KERN_DEBUG "%s:", dev->name ); + } + printk(" %04x", mii_rd(ioaddr, 0, i) ); + } + printk("\n"); +} +#endif + +/*============= Main bulk of functions =========================*/ + +/**************** + * xirc2ps_attach() creates an "instance" of the driver, allocating + * local data structures for one device. The device is registered + * with Card Services. + * + * The dev_link structure is initialized, but we don't actually + * configure the card at this point -- we wait until we receive a + * card insertion event. + */ + +static dev_link_t * +xirc2ps_attach(void) +{ + client_reg_t client_reg; + dev_link_t *link; + struct net_device *dev; + local_info_t *local; + int err; + + #ifdef PCMCIA_DEBUG + if(pc_debug) + printk(KDBG_XIRC "attach()\n"); + #endif + flush_stale_links(); + + /* Initialize the dev_link_t structure */ + link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL); + memset(link, 0, sizeof(struct dev_link_t)); + link->release.function = &xirc2ps_release; + link->release.data = (u_long) link; + + /* General socket configuration */ + link->conf.Attributes = CONF_ENABLE_IRQ; + link->conf.Vcc = 50; + link->conf.IntType = INT_MEMORY_AND_IO; + link->conf.ConfigIndex = 1; + link->conf.Present = PRESENT_OPTION; + + /* Allocate space for a device structure */ + dev = kmalloc(sizeof(struct net_device), GFP_KERNEL); + memset(dev, 0, sizeof(struct net_device)); + local = kmalloc(sizeof(local_info_t), GFP_KERNEL); + memset(local, 0, sizeof(local_info_t)); + dev->priv = local; + + /* Fill in card specific entries */ + dev->hard_start_xmit = &do_start_xmit; + dev->set_config = &do_config; + dev->get_stats = &do_get_stats; + dev->do_ioctl = &do_ioctl; + dev->set_multicast_list = &set_multicast_list; + ether_setup(dev); + dev->name = local->node.dev_name; + dev->init = &do_init; + dev->open = &do_open; + dev->stop = &do_stop; + dev->tbusy = 1; + link->priv = dev; + + /* Register with Card Services */ + link->next = dev_list; + dev_list = link; + client_reg.dev_info = &dev_info; + client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; + client_reg.EventMask = + CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | + CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | + CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; + client_reg.event_handler = &xirc2ps_event; + client_reg.Version = 0x0210; + client_reg.event_callback_args.client_data = link; + if( (err = CardServices(RegisterClient, &link->handle, &client_reg)) ) { + cs_error(link->handle, RegisterClient, err); + xirc2ps_detach(link); + return NULL; + } + + return link; +} /* xirc2ps_attach */ + +/**************** + * This deletes a driver "instance". The device is de-registered + * with Card Services. If it has been released, all local data + * structures are freed. Otherwise, the structures will be freed + * when the device is released. + */ + +static void +xirc2ps_detach(dev_link_t * link) +{ + dev_link_t **linkp; + long flags; + + #ifdef PCMCIA_DEBUG + if(pc_debug) + printk(KDBG_XIRC "detach(0x%p)\n", link); + #endif + + /* Locate device structure */ + for( linkp = &dev_list; *linkp; linkp = &(*linkp)->next ) + if( *linkp == link) + break; + if( !*linkp ) { + #ifdef PCMCIA_DEBUG + printk(KDBG_XIRC "detach(0x%p): dev_link lost\n", link ); + #endif + return; + } + + save_flags(flags); + cli(); + if( link->state & DEV_RELEASE_PENDING ) { + del_timer(&link->release); + link->state &= ~DEV_RELEASE_PENDING; + } + restore_flags(flags); + + /* + * If the device is currently configured and active, we won't + * actually delete it yet. Instead, it is marked so that when + * the release() function is called, that will trigger a proper + * detach(). + */ + if(link->state & DEV_CONFIG) { + #ifdef PCMCIA_DEBUG + printk(KDBG_XIRC "detach postponed, '%s' " + "still locked\n", link->dev->dev_name); + #endif + link->state |= DEV_STALE_LINK; + return; + } + + /* Break the link with Card Services */ + if(link->handle) + CardServices(DeregisterClient, link->handle); + + /* Unlink device structure, free pieces */ + *linkp = link->next; + if(link->priv) { + struct net_device *dev = link->priv; + if (link->dev != NULL) + unregister_netdev(dev); + if( dev->priv ) + kfree(dev->priv); + kfree(link->priv); + } + kfree(link); + +} /* xirc2ps_detach */ + +/**************** + * Detect the type of the card. s is the buffer with the data of tuple 0x20 + * Returns: 0 := not supported + * mediaid=11 and prodid=47 + * Media-Id bits: + * Ethernet 0x01 + * Tokenring 0x02 + * Arcnet 0x04 + * Wireless 0x08 + * Modem 0x10 + * GSM only 0x20 + * Prod-Id bits: + * Pocket 0x10 + * External 0x20 + * Creditcard 0x40 + * Cardbus 0x80 + * + */ +static int +set_card_type( dev_link_t *link, const void *s ) +{ + struct net_device *dev = link->priv; + local_info_t *local = dev->priv; + #ifdef PCMCIA_DEBUG + unsigned cisrev = ((const unsigned char *)s)[2]; + #endif + unsigned mediaid= ((const unsigned char *)s)[3]; + unsigned prodid = ((const unsigned char *)s)[4]; + + #ifdef PCMCIA_DEBUG + if(pc_debug) + printk(KDBG_XIRC "cisrev=%02x mediaid=%02x prodid=%02x\n", + cisrev, mediaid, prodid ); + #endif + + local->mohawk = 0; + local->dingo = 0; + local->modem = 0; + local->card_type = XIR_UNKNOWN; + if( !(prodid & 0x40) ) { + printk(KNOT_XIRC "Ooops: Not a creditcard\n"); + return 0; + } + if( !(mediaid & 0x01) ) { + printk(KNOT_XIRC "Not an Ethernet card\n"); + return 0; + } + if( mediaid & 0x10 ) { + local->modem = 1; + switch( prodid & 15 ) { + case 1: local->card_type = XIR_CEM ; break; + case 2: local->card_type = XIR_CEM2 ; break; + case 3: local->card_type = XIR_CEM3 ; break; + case 4: local->card_type = XIR_CEM33 ; break; + case 5: local->card_type = XIR_CEM56M; + local->mohawk = 1; + break; + case 6: + case 7: /* 7 is the RealPort 10/56 */ + local->card_type = XIR_CEM56 ; + local->mohawk = 1; + local->dingo = 1; + break; + } + } + else { + switch( prodid & 15 ) { + case 1: local->card_type = has_ce2_string(link)? XIR_CE2 : XIR_CE ; + break; + case 2: local->card_type = XIR_CE2; break; + case 3: local->card_type = XIR_CE3; + local->mohawk = 1; + break; + } + } + if( local->card_type == XIR_CE || local->card_type == XIR_CEM ) { + printk(KNOT_XIRC "Sorry, this is an old CE card\n"); + return 0; + } + if( local->card_type == XIR_UNKNOWN ) + printk(KNOT_XIRC "Warning: Unknown card (mediaid=%02x prodid=%02x)\n", + mediaid, prodid ); + + return 1; +} + +/**************** + * There are some CE2 cards out which claim to be a CE card. + * This function looks for a "CE2" in the 3rd version field. + * Returns: true if this is a CE2 + */ +static int +has_ce2_string(dev_link_t * link) +{ + client_handle_t handle = link->handle; + tuple_t tuple; + cisparse_t parse; + u_char buf[256]; + + tuple.Attributes = 0; + tuple.TupleData = buf; + tuple.TupleDataMax = 254; + tuple.TupleOffset = 0; + tuple.DesiredTuple = CISTPL_VERS_1; + if( !first_tuple(handle, &tuple, &parse) && parse.version_1.ns > 2 ) { + if( strstr(parse.version_1.str + parse.version_1.ofs[2], "CE2") ) + return 1; + } + return 0; +} + +/**************** + * xirc2ps_config() is scheduled to run after a CARD_INSERTION event + * is received, to configure the PCMCIA socket, and to make the + * ethernet device available to the system. + */ +static void +xirc2ps_config(dev_link_t * link) +{ + client_handle_t handle; + tuple_t tuple; + cisparse_t parse; + struct net_device *dev; + local_info_t *local; + ioaddr_t ioaddr; + int err, i; + u_char buf[64]; + cistpl_lan_node_id_t *node_id = (cistpl_lan_node_id_t*)parse.funce.data; + cistpl_cftable_entry_t *cf = &parse.cftable_entry; + + handle = link->handle; + dev = link->priv; + local = dev->priv; + local->dingo_ccr = 0; + + #ifdef PCMCIA_DEBUG + if(pc_debug) + printk(KDBG_XIRC "config(0x%p)\n", link); + #endif + + /* + * This reads the card's CONFIG tuple to find its configuration + * registers. + */ + tuple.Attributes = 0; + tuple.TupleData = buf; + tuple.TupleDataMax = 64; + tuple.TupleOffset = 0; + + /* Is this a valid card */ + tuple.DesiredTuple = CISTPL_MANFID; + if( (err=first_tuple(handle, &tuple, &parse))) { + printk(KNOT_XIRC "manfid not found in CIS\n"); + goto failure; + } + + switch( parse.manfid.manf ) { + case MANFID_XIRCOM: + local->manf_str = "Xircom"; + #ifdef PCMCIA_DEBUG + if(pc_debug) + printk(KDBG_XIRC "found xircom card\n"); + #endif + break; + case MANFID_ACCTON: + local->manf_str = "Accton"; + #ifdef PCMCIA_DEBUG + if(pc_debug) + printk(KDBG_XIRC "found Accton card\n"); + #endif + break; + case MANFID_COMPAQ: + case MANFID_COMPAQ2: + local->manf_str = "Compaq"; + #ifdef PCMCIA_DEBUG + if(pc_debug) + printk(KDBG_XIRC "found Compaq card\n"); + #endif + break; + case MANFID_INTEL: + local->manf_str = "Intel"; + #ifdef PCMCIA_DEBUG + if(pc_debug) + printk(KDBG_XIRC "found Intel card\n"); + #endif + break; + default: + printk(KNOT_XIRC "Unknown Card Manufacturer ID: 0x%04x\n", + (unsigned)parse.manfid.manf); + goto failure; + } + + if( !set_card_type(link, buf ) ) { + printk(KNOT_XIRC "this card is not supported\n"); + goto failure; + } + + /* get configuration stuff */ + tuple.DesiredTuple = CISTPL_CONFIG; + if( (err=first_tuple(handle, &tuple, &parse))) + goto cis_error; + link->conf.ConfigBase = parse.config.base; + link->conf.Present = parse.config.rmask[0]; + + /* get the ethernet address from the CIS */ + tuple.DesiredTuple = CISTPL_FUNCE; + for( err = first_tuple(handle, &tuple, &parse); !err; + err = next_tuple(handle, &tuple, &parse) ) { + /* Once I saw two CISTPL_FUNCE_LAN_NODE_ID entries: + * the first one with a length of zero the second correct - + * so I skip all entries with length 0 */ + if( parse.funce.type == CISTPL_FUNCE_LAN_NODE_ID + && ((cistpl_lan_node_id_t *)parse.funce.data)->nb ) + break; + } + if( err ) { /* not found: try to get the node-id from tuple 0x89 */ + tuple.DesiredTuple = 0x89; /* data layout looks like tuple 0x22 */ + if( !(err = get_tuple_data(GetFirstTuple, handle, &tuple )) ) { + if( tuple.TupleDataLen == 8 && *buf == CISTPL_FUNCE_LAN_NODE_ID ) + memcpy( &parse, buf, 8 ); + else + err = -1; + } + } + if( err ) { /* another try (James Lehmer's CE2 version 4.1)*/ + tuple.DesiredTuple = CISTPL_FUNCE; + for( err = first_tuple(handle, &tuple, &parse); !err; + err = next_tuple(handle, &tuple, &parse) ) { + if( parse.funce.type == 0x02 && parse.funce.data[0] == 1 + && parse.funce.data[1] == 6 && tuple.TupleDataLen == 13 ) { + buf[1] = 4; + memcpy( &parse, buf+1, 8 ); + break; + } + } + } + if( err ) { + printk(KNOT_XIRC "node-id not found in CIS\n"); + goto failure; + } + node_id = (cistpl_lan_node_id_t *)parse.funce.data; + if( node_id->nb != 6 ) { + printk(KNOT_XIRC "malformed node-id in CIS\n"); + goto failure; + } + for( i=0; i < 6; i++ ) + dev->dev_addr[i] = node_id->id[i]; + + /* Configure card */ + link->state |= DEV_CONFIG; + + link->io.IOAddrLines =10; + link->io.Attributes1 = IO_DATA_PATH_WIDTH_16; + link->irq.Attributes = IRQ_HANDLE_PRESENT; + link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID; + if( irq_list[0] == -1 ) + link->irq.IRQInfo2 = irq_mask; + else { + for( i = 0; i < 4; i++) + link->irq.IRQInfo2 |= 1 << irq_list[i]; + } + link->irq.Handler = xirc2ps_interrupt; + link->irq.Instance = dev; + if( local->modem ) { + int pass; + + if( do_sound ) { + link->conf.Attributes |= CONF_ENABLE_SPKR; + link->conf.Status |= CCSR_AUDIO_ENA; + } + link->irq.Attributes |= IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED ; + link->io.NumPorts2 = 8; + link->io.Attributes2 = IO_DATA_PATH_WIDTH_8; + if( local->dingo ) { + /* Take the Modem IO port from the CIS and scan for a free + * Ethernet port */ + link->io.NumPorts1 = 16; /* no Mako stuff anymore */ + tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; + for( err = first_tuple(handle, &tuple, &parse); !err; + err = next_tuple(handle, &tuple, &parse) ) { + if( cf->io.nwin > 0 && (cf->io.win[0].base & 0xf) == 8 ) { + for(ioaddr = 0x300; ioaddr < 0x400; ioaddr += 0x10) { + link->conf.ConfigIndex = cf->index ; + link->io.BasePort2 = cf->io.win[0].base; + link->io.BasePort1 = ioaddr; + if( !(err=CardServices(RequestIO, link->handle, + &link->io)) ) + goto port_found; + } + } + } + } + else { + link->io.NumPorts1 = 18; + /* We do 2 passes here: The first one uses the regular mapping and + * the second tries again, thereby considering that the 32 ports are + * mirrored every 32 bytes. Actually we use a mirrored port for + * the Mako if (on the first pass) the COR bit 5 is set. + */ + for( pass=0; pass < 2; pass++ ) { + tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; + for( err = first_tuple(handle, &tuple, &parse); !err; + err = next_tuple(handle, &tuple, &parse) ){ + if( cf->io.nwin > 0 && (cf->io.win[0].base & 0xf) == 8 ){ + link->conf.ConfigIndex = cf->index ; + link->io.BasePort2 = cf->io.win[0].base; + link->io.BasePort1 = link->io.BasePort2 + + (pass ? ( cf->index & 0x20 ? -24:8 ) + : ( cf->index & 0x20 ? 8:-24) ); + if( !(err=CardServices(RequestIO, link->handle, + &link->io))) + goto port_found; + } + } + } + /* if special option: + * try to configure as Ethernet only. + * .... */ + } + printk(KNOT_XIRC "no ports available\n"); + } + else { + link->irq.Attributes |= IRQ_TYPE_EXCLUSIVE; + link->io.NumPorts1 = 16; + for(ioaddr = 0x300; ioaddr < 0x400; ioaddr += 0x10) { + link->io.BasePort1 = ioaddr; + if( !(err=CardServices(RequestIO, link->handle, &link->io)) ) + goto port_found; + } + link->io.BasePort1 = 0; /* let CS decide */ + if( (err=CardServices(RequestIO, link->handle, &link->io)) ) { + cs_error(link->handle, RequestIO, err); + goto config_error; + } + } + port_found: + if( err ) + goto config_error; + + /**************** + * Now allocate an interrupt line. Note that this does not + * actually assign a handler to the interrupt. + */ + if( (err=CardServices(RequestIRQ, link->handle, &link->irq))) { + cs_error(link->handle, RequestIRQ, err); + goto config_error; + } + + /**************** + * This actually configures the PCMCIA socket -- setting up + * the I/O windows and the interrupt mapping. + */ + if( (err=CardServices(RequestConfiguration, + link->handle, &link->conf)) ) { + cs_error(link->handle, RequestConfiguration, err); + goto config_error; + } + + if( local->dingo ) { + #ifdef CEM56_FIX + conf_reg_t reg; + #endif + win_req_t req; + memreq_t mem; + + #ifdef CEM56_FIX + /* Reset the modem's BAR to the correct value + * This is necessary because in the RequestConfiguration call, + * the base address of the ethernet port (BasePort1) is written + * to the BAR registers of the modem. + */ + reg.Action = CS_WRITE; + reg.Offset = CISREG_IOBASE_0; + reg.Value = link->io.BasePort2 & 0xff; + if( (err = CardServices(AccessConfigurationRegister, link->handle, + ® )) ) { + cs_error(link->handle, AccessConfigurationRegister, err); + goto config_error; + } + reg.Action = CS_WRITE; + reg.Offset = CISREG_IOBASE_1; + reg.Value = (link->io.BasePort2 >> 8) & 0xff; + if( (err = CardServices(AccessConfigurationRegister, link->handle, + ® )) ) { + cs_error(link->handle, AccessConfigurationRegister, err); + goto config_error; + } + #endif + + /* There is no config entry for the Ethernet part which + * is at 0x0800. So we allocate a window into the attribute + * memory and write direct to the CIS registers + */ + req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE; + req.Base = 0; + req.Size = 0x1000; /* 4k window */ + req.AccessSpeed = 0; + link->win = (window_handle_t)link->handle; + if( (err = CardServices(RequestWindow, &link->win, &req )) ) { + cs_error(link->handle, RequestWindow, err); + goto config_error; + } + local->dingo_ccr = ioremap(req.Base,0x1000) + 0x0800; + mem.CardOffset = 0x0; + mem.Page = 0; + if( (err = CardServices(MapMemPage, link->win, &mem) ) ) { + cs_error(link->handle, MapMemPage, err); + goto config_error; + } + + /* Setup the CCRs; there are no infos in the CIS about the Ethernet + * part. + */ + writeb(0x47, local->dingo_ccr + CISREG_COR ); + ioaddr = link->io.BasePort1; + writeb( ioaddr & 0xff , local->dingo_ccr + CISREG_IOBASE_0 ); + writeb((ioaddr >> 8)&0xff , local->dingo_ccr + CISREG_IOBASE_1 ); + + #if 0 + { + u_char tmp; + printk(KERN_INFO "ECOR:" ); + for(i=0; i < 7; i++ ) { + tmp = readb(local->dingo_ccr + i*2 ); + printk(" %02x", tmp ); + } + printk("\n" ); + printk(KERN_INFO "DCOR:" ); + for(i=0; i < 4; i++ ) { + tmp = readb(local->dingo_ccr + 0x20 + i*2 ); + printk(" %02x", tmp ); + } + printk("\n" ); + printk(KERN_INFO "SCOR:" ); + for(i=0; i < 10; i++ ) { + tmp = readb(local->dingo_ccr + 0x40 + i*2 ); + printk(" %02x", tmp ); + } + printk("\n" ); + } + #endif + + writeb( 0x01 , local->dingo_ccr + 0x20 ); + writeb( 0x0c , local->dingo_ccr + 0x22 ); + writeb( 0x00 , local->dingo_ccr + 0x24 ); + writeb( 0x00 , local->dingo_ccr + 0x26 ); + writeb( 0x00 , local->dingo_ccr + 0x28 ); + } + + /* The if_port symbol can be set when the module is loaded */ + local->probe_port=0; + if( !if_port ) { + local->probe_port=1; + dev->if_port = 1; + } + else if( (if_port >= 1 && if_port <= 2) || (local->mohawk && if_port==4) ) + dev->if_port = if_port; + else + printk(KERN_NOTICE "xirc2ps_cs: invalid if_port requested\n"); + + /* we can now register the device with the net subsystem */ + dev->irq = link->irq.AssignedIRQ; + dev->base_addr = link->io.BasePort1; + dev->tbusy = 0; + if( (err=register_netdev(dev)) ) { + printk(KERN_NOTICE "xirc2ps_cs: register_netdev() failed\n"); + goto config_error; + } + + link->state &= ~DEV_CONFIG_PENDING; + link->dev = &local->node; + + if( local->dingo ) + do_reset(dev, 1); /* a kludge to make the cem56 work */ + + /* give some infos about the hardware */ + printk(KERN_INFO "%s: %s: port %#3lx, irq %d, hwaddr", + dev->name, local->manf_str,(u_long)dev->base_addr, (int)dev->irq ); + for(i = 0; i < 6; i++) + printk("%c%02X", i?':':' ', dev->dev_addr[i]); + printk("\n"); + + return; + + config_error: + link->state &= ~DEV_CONFIG_PENDING; + xirc2ps_release((u_long)link); + return; + + cis_error: + printk(KERN_NOTICE "xirc2ps_cs: unable to parse CIS\n"); + failure: + link->state &= ~DEV_CONFIG_PENDING; +} /* xirc2ps_config */ + +/**************** + * After a card is removed, xirc2ps_release() will unregister the net + * device, and release the PCMCIA configuration. If the device is + * still open, this will be postponed until it is closed. + */ +static void +xirc2ps_release( u_long arg) +{ + dev_link_t *link = (dev_link_t *) arg; + struct net_device *dev = link->priv; + + #ifdef PCMCIA_DEBUG + if(pc_debug) + printk(KDBG_XIRC "release(0x%p)\n", link); + #endif + + /* + * If the device is currently in use, we won't release until it + * is actually closed. + */ + if(link->open) { + #ifdef PCMCIA_DEBUG + if(pc_debug) + printk(KDBG_XIRC "release postponed, '%s' " + "still open\n", link->dev->dev_name); + #endif + link->state |= DEV_STALE_CONFIG; + return; + } + + if( link->win ) { + local_info_t *local = dev->priv; + if( local->dingo ) + iounmap( local->dingo_ccr - 0x0800 ); + CardServices(ReleaseWindow, link->win ); + } + CardServices(ReleaseConfiguration, link->handle); + CardServices(ReleaseIO, link->handle, &link->io); + CardServices(ReleaseIRQ, link->handle, &link->irq); + link->state &= ~(DEV_CONFIG | DEV_RELEASE_PENDING); + +} /* xirc2ps_release */ + +/*====================================================================*/ + +/**************** + * The card status event handler. Mostly, this schedules other + * stuff to run after an event is received. A CARD_REMOVAL event + * also sets some flags to discourage the net drivers from trying + * to talk to the card any more. + * + * When a CARD_REMOVAL event is received, we immediately set a flag + * to block future accesses to this device. All the functions that + * actually access the device should check this flag to make sure + * the card is still present. + */ + +static int +xirc2ps_event(event_t event, int priority, + event_callback_args_t * args) +{ + dev_link_t *link = args->client_data; + struct net_device *dev = link->priv; + local_info_t *lp = dev? dev->priv : NULL; + + #ifdef PCMCIA_DEBUG + if(pc_debug) + printk(KDBG_XIRC "event(%d)\n", (int)event ); + #endif + + switch (event) { + case CS_EVENT_REGISTRATION_COMPLETE: + #ifdef PCMCIA_DEBUG + if(pc_debug) + printk(KDBG_XIRC "registration complete\n"); + #endif + break; + case CS_EVENT_CARD_REMOVAL: + link->state &= ~DEV_PRESENT; + if(link->state & DEV_CONFIG) { + dev->tbusy = 1; dev->start = 0; + link->release.expires = jiffies + HZ / 20; + add_timer(&link->release); + } + break; + case CS_EVENT_CARD_INSERTION: + link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; + xirc2ps_config(link); + break; + case CS_EVENT_PM_SUSPEND: + link->state |= DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_RESET_PHYSICAL: + if(link->state & DEV_CONFIG) { + if(link->open) { + dev->tbusy = 1; dev->start = 0; + lp->suspended=1; + do_powerdown(dev); + } + CardServices(ReleaseConfiguration, link->handle); + } + break; + case CS_EVENT_PM_RESUME: + link->state &= ~DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_CARD_RESET: + if(link->state & DEV_CONFIG) { + CardServices(RequestConfiguration, link->handle, &link->conf); + if( link->open) { + do_reset(dev,1); + lp->suspended=0; + dev->tbusy = 0; dev->start = 1; + } + } + break; + } + return 0; +} /* xirc2ps_event */ + +/*====================================================================*/ + +/**************** + * This is the Interrupt service route. + */ +static void +xirc2ps_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *)dev_id; + local_info_t *lp; + ioaddr_t ioaddr; + u_char saved_page; + unsigned bytes_rcvd; + unsigned int_status, eth_status, rx_status, tx_status; + unsigned rsr, pktlen; + ulong start_ticks = jiffies; /* fixme: jiffies rollover every 497 days + * is this something to worry about? + * -- on a laptop? + */ + + if( !dev->start ) + return; + + if( dev->interrupt ) { + printk(KERR_XIRC "re-entering isr on irq %d (dev=%p)\n", irq, dev); + return; + } + dev->interrupt = 1; + lp = dev->priv; + ioaddr = dev->base_addr; + if( lp->mohawk ) { /* must disable the interrupt */ + PutByte(XIRCREG_CR, 0 ); + } + + #ifdef PCMCIA_DEBUG + if(pc_debug > 6 ) + printk(KERN_DEBUG "%s: interrupt %d at %#x.\n", dev->name, irq, ioaddr); + #endif + + saved_page = GetByte(XIRCREG_PR); + /* Read the ISR to see whats the cause for the interrupt. + * This also clears the interrupt flags on CE2 cards + */ + int_status = GetByte(XIRCREG_ISR); + bytes_rcvd = 0; + loop_entry: + if( int_status == 0xff ) { /* card may be ejected */ + #ifdef PCMCIA_DEBUG + if( pc_debug > 3 ) + printk(KERN_DEBUG "%s: interrupt %d for dead card\n", dev->name, irq ); + #endif + goto leave; + } + eth_status = GetByte(XIRCREG_ESR); + + SelectPage(0x40); + rx_status = GetByte(XIRCREG40_RXST0); + PutByte(XIRCREG40_RXST0, (~rx_status & 0xff) ); + tx_status = GetByte(XIRCREG40_TXST0); + tx_status |= GetByte(XIRCREG40_TXST1) << 8; + PutByte(XIRCREG40_TXST0, 0 ); + PutByte(XIRCREG40_TXST1, 0 ); + + #ifdef PCMCIA_DEBUG + if( pc_debug > 3 ) + printk(KERN_DEBUG "%s: ISR=%#2.2x ESR=%#2.2x RSR=%#2.2x TSR=%#4.4x\n", + dev->name, int_status, eth_status, rx_status, tx_status ); + #endif + + /***** receive section ******/ + SelectPage(0); + while( eth_status & FullPktRcvd ) { + rsr = GetByte(XIRCREG0_RSR); + if( bytes_rcvd > maxrx_bytes && (rsr & PktRxOk) ) { + /* too many bytes received during this int, drop the rest of the + * packets */ + lp->stats.rx_dropped++; + #ifdef PCMCIA_DEBUG + if( pc_debug > 3 ) + printk(KNOT_XIRC "%s: RX drop, too much done\n", dev->name); + #endif + PutWord(XIRCREG0_DO, 0x8000 ); /* issue cmd: skip_rx_packet */ + } + else if( rsr & PktRxOk ) { + struct sk_buff *skb; + + pktlen = GetWord(XIRCREG0_RBC); + bytes_rcvd += pktlen; + + #ifdef PCMCIA_DEBUG + if( pc_debug > 5 ) + printk(KDBG_XIRC "rsr=%#02x packet_length=%u\n", rsr, pktlen ); + #endif + + skb = dev_alloc_skb(pktlen+3); /* 1 extra so we can use insw */ + if( !skb ) { + #ifdef PCMCIA_DEBUG + if( pc_debug ) + printk(KNOT_XIRC "low memory, packet dropped (size=%u)\n", + pktlen ); + #endif + lp->stats.rx_dropped++; + } + else { /* okay get the packet */ + skb_reserve(skb, 2); + if( lp->silicon == 0 ) { /* work around a hardware bug */ + unsigned rhsa; /* receive start address */ + + SelectPage(5); + rhsa = GetWord(XIRCREG5_RHSA0); + SelectPage(0); + rhsa += 3; /* skip control infos */ + if( rhsa >= 0x8000 ) + rhsa = 0; + if( rhsa + pktlen > 0x8000 ) { + unsigned i; + u_char *buf = skb_put(skb, pktlen); + for(i=0; i < pktlen ; i++, rhsa++ ) { + buf[i] = GetByte(XIRCREG_EDP); + if( rhsa == 0x8000 ) { + rhsa = 0; + i--; + } + } + } else { + insw(ioaddr+XIRCREG_EDP, + skb_put(skb, pktlen), (pktlen+1)>>1 ); + } + } + #if 0 + else if( lp->mohawk ) { + /* To use this 32 bit access we should use + * a manual optimized loop + * Also the words are swapped, we can get more + * performance by using 32 bit access and swapping + * the words in a register. Will need this for cardbus + * + * Note: don't forget to change the ALLOC_SKB to .. +3 + */ + unsigned i; + u_long *p = skb_put(skb, pktlen); + register u_long a; + ioaddr_t edpreg = ioaddr+XIRCREG_EDP-2; + for(i=0; i < len ; i += 4, p++ ) { + a = inl(edpreg); + __asm__("rorl $16,%0\n\t" + :"=q" (a) + : "0" (a)); + *p = a; + } + } + #endif + else { + insw(ioaddr+XIRCREG_EDP, skb_put(skb, pktlen), + (pktlen+1)>>1 ); + } + skb->protocol = eth_type_trans(skb, dev); + skb->dev = dev; + netif_rx(skb); + lp->stats.rx_packets++; + lp->stats.rx_bytes += pktlen; + if( !(rsr & PhyPkt) ) + lp->stats.multicast++; + } + PutWord(XIRCREG0_DO, 0x8000 ); /* issue cmd: skip_rx_packet */ + } + else { + #ifdef PCMCIA_DEBUG + if( pc_debug > 5 ) + printk("rsr=%#02x\n", rsr ); + #endif + } + if( rsr & PktTooLong ) { + lp->stats.rx_frame_errors++; + #ifdef PCMCIA_DEBUG + if( pc_debug > 3 ) + printk(KNOT_XIRC "%s: Packet too long\n", dev->name); + #endif + } + if( rsr & CRCErr ) { + lp->stats.rx_crc_errors++; + #ifdef PCMCIA_DEBUG + if( pc_debug > 3 ) + printk(KNOT_XIRC "%s: CRC error\n", dev->name); + #endif + } + if( rsr & AlignErr ) { + lp->stats.rx_fifo_errors++; /* okay ? */ + #ifdef PCMCIA_DEBUG + if( pc_debug > 3 ) + printk(KNOT_XIRC "%s: Alignment error\n", dev->name); + #endif + } + + /* get the new ethernet status */ + eth_status = GetByte(XIRCREG_ESR); + } + if( rx_status & 0x10 ) { /* Receive overrun */ + lp->stats.rx_over_errors++; + PutByte(XIRCREG_CR, ClearRxOvrun); + #ifdef PCMCIA_DEBUG + if( pc_debug > 3 ) + printk(KDBG_XIRC "receive overrun cleared\n" ); + #endif + } + + /***** transmit section ******/ + if( int_status & PktTxed ) { + unsigned n, nn; + + n = lp->last_ptr_value; + nn = GetByte(XIRCREG0_PTR); + lp->last_ptr_value = nn; + if( nn < n ) /* rollover */ + lp->stats.tx_packets += 256 - n; + else if( n == nn ) { /* happens sometimes - don't know why */ + #ifdef PCMCIA_DEBUG + if( pc_debug ) + printk(KDBG_XIRC "PTR not changed?\n" ); + #endif + } + else + lp->stats.tx_packets += lp->last_ptr_value - n; + dev->tbusy = 0; + mark_bh(NET_BH); /* Inform upper layers. */ + } + if( tx_status & 0x0002 ) { /* Execessive collissions */ + #ifdef PCMCIA_DEBUG + if( pc_debug ) + printk(KDBG_XIRC "tx restarted due to execssive collissions\n" ); + #endif + PutByte(XIRCREG_CR, RestartTx ); /* restart transmitter process */ + } + if( tx_status & 0x0040 ) + lp->stats.tx_aborted_errors++; + + /* recalculate our work chunk so that we limit the duration of this + * ISR to about 1/10 of a second. + * Calculate only if we received a reasonable amount of bytes. + */ + if( bytes_rcvd > 1000 ) { + u_long duration = jiffies - start_ticks; + + if( duration >= HZ/10 ) { /* if more than about 1/10 second */ + maxrx_bytes = (bytes_rcvd * (HZ/10)) / duration; + if( maxrx_bytes < 2000 ) + maxrx_bytes = 2000; + else if( maxrx_bytes > 22000 ) + maxrx_bytes = 22000; + #ifdef PCMCIA_DEBUG + if( pc_debug > 1) + printk(KDBG_XIRC "set maxrx=%u (rcvd=%u ticks=%lu)\n", + maxrx_bytes, bytes_rcvd, duration ); + #endif + } + else if( !duration && maxrx_bytes < 22000 ) { /* now much faster*/ + maxrx_bytes += 2000; + if( maxrx_bytes > 22000 ) + maxrx_bytes = 22000; + #ifdef PCMCIA_DEBUG + if( pc_debug > 1 ) + printk(KDBG_XIRC "set maxrx=%u\n", maxrx_bytes ); + #endif + } + } + + leave: + if( lockup_hack ) { + if( int_status != 0xff && (int_status = GetByte(XIRCREG_ISR)) != 0 ) + goto loop_entry; + } + SelectPage(saved_page); + dev->interrupt = 0; + PutByte(XIRCREG_CR, EnableIntr ); /* re-enable interrupts */ + /* Instead of dropping packets during a receive, we could + * force an interrupt with this command: + * PutByte(XIRCREG_CR, EnableIntr|ForceIntr ); + */ +} /* xirc2ps_interrupt */ + +/*====================================================================*/ + +static int +do_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + local_info_t *lp = dev->priv; + ioaddr_t ioaddr = dev->base_addr; + int okay; + unsigned freespace; + unsigned pktlen = skb? skb->len : 0; + + #ifdef PCMCIA_DEBUG + if(pc_debug>1 ) + printk(KDBG_XIRC "do_start_xmit(skb=%p, dev=%p) len=%u\n", + skb, dev, pktlen ); + #endif + + /* Transmitter timeout, serious problems */ + if( dev->tbusy ) { + int tickssofar = jiffies - dev->trans_start; + + if( lp->suspended ) { + dev_kfree_skb (skb); + dev->trans_start = jiffies; + lp->stats.tx_dropped++; + return 0; + } + if( tickssofar < TX_TIMEOUT ) + return 1; + + printk(KERN_NOTICE "%s: transmit timed out\n", dev->name ); + lp->stats.tx_errors++; + /* reset the card */ + do_reset(dev,1); + dev->trans_start = jiffies; + dev->tbusy = 0; + } + + if( test_and_set_bit(0, (void*)&dev->tbusy ) ) { + printk(KWRN_XIRC "transmitter access conflict\n"); + dev_kfree_skb (skb); + return 0; + } + + /* adjust the packet length to min. required + * and hope that the buffer is large enough + * to provide some random data. + * fixme: For Mohawk we can change this by sending + * a larger packetlen than we actually have; the chip will + * pad this in his buffer with random bytes + */ + if( pktlen < ETH_ZLEN ) + pktlen = ETH_ZLEN; + + SelectPage(0); + PutWord(XIRCREG0_TRS, (u_short)pktlen+2 ); + freespace = GetWord(XIRCREG0_TSO); + okay = freespace & 0x8000; + freespace &= 0x7fff; + /* TRS doesn't work - (indeed it is eliminated with sil-rev 1) */ + okay = pktlen +2 < freespace; + #ifdef PCMCIA_DEBUG + if(pc_debug > 2 + ( okay ? 2 : 0) ) + printk(KERN_DEBUG "%s: avail. tx space=%u%s\n", dev->name, freespace, + okay? " (okay)":" (not enough)" ); + #endif + if( !okay ) { /* not enough space */ + dev->tbusy = 1; + return 1; /* upper layer may decide to requeue this packet */ + } + /* send the packet */ + PutWord(XIRCREG_EDP, (u_short)pktlen ); + outsw(ioaddr+XIRCREG_EDP, skb->data, pktlen>>1 ); + if( pktlen & 1 ) + PutByte(XIRCREG_EDP, skb->data[pktlen-1] ); + + if( lp->mohawk ) + PutByte(XIRCREG_CR, TransmitPacket|EnableIntr ); + + dev_kfree_skb (skb); + dev->trans_start = jiffies; + dev->tbusy = 0; + lp->stats.tx_bytes += pktlen; + return 0; +} + +static struct enet_statistics * +do_get_stats(struct net_device *dev) +{ + local_info_t *lp = dev->priv; + + /* lp->stats.rx_missed_errors = GetByte(?) */ + return &lp->stats; +} + +/**************** + * Set all addresses: This first one is the individual address, + * the next 9 addresses are taken from the multicast list and + * the rest is filled with the individual address. + */ +static void +set_addresses(struct net_device *dev) +{ + ioaddr_t ioaddr = dev->base_addr; + local_info_t *lp = dev->priv; + struct dev_mc_list *dmi = dev->mc_list; + char *addr; + int i,j,k,n; + + SelectPage(k=0x50); + for(i=0,j=8,n=0; ; i++, j++) { + if( i > 5 ) { + if( ++n > 9 ) + break; + i = 0; + } + if( j > 15 ) { + j = 8; + k++; + SelectPage(k); + } + + if( n && n <= dev->mc_count && dmi ) { + addr = dmi->dmi_addr; + dmi = dmi->next; + } + else + addr = dev->dev_addr; + + if( lp->mohawk ) + PutByte( j, addr[5-i] ); + else + PutByte( j, addr[i] ); + } + SelectPage(0); +} + +/**************** + * Set or clear the multicast filter for this adaptor. + * We can filter up to 9 addresses, if more are requested we set + * multicast promiscuous mode. + */ + +static void +set_multicast_list(struct net_device *dev) +{ + ioaddr_t ioaddr = dev->base_addr; + + SelectPage(0x42); + if( dev->flags & IFF_PROMISC ) { /* snoop */ + PutByte(XIRCREG42_SWC1, 0x06); /* set MPE and PME */ + } + else if( dev->mc_count > 9 || (dev->flags & IFF_ALLMULTI) ) { + PutByte(XIRCREG42_SWC1, 0x06); /* set MPE */ + } + else if( dev->mc_count ) { /* the chip can filter 9 addresses perfectly */ + PutByte(XIRCREG42_SWC1, 0x00); + SelectPage(0x40); + PutByte(XIRCREG40_CMD0, Offline ); + set_addresses(dev); + SelectPage(0x40); + PutByte(XIRCREG40_CMD0, EnableRecv | Online ); + } + else { /* standard usage */ + PutByte(XIRCREG42_SWC1, 0x00); + } + SelectPage(0); +} + +/**************** + * We never need to do anything when a IIps device is "initialized" + * by the net software, because we only register already-found cards. + */ +static int +do_init(struct net_device *dev) +{ + #ifdef PCMCIA_DEBUG + if(pc_debug) + printk(KDBG_XIRC "do_init(%p)\n", dev ); + #endif + return 0; +} + +static int +do_config(struct net_device *dev, struct ifmap *map) +{ + local_info_t *local = dev->priv; + + #ifdef PCMCIA_DEBUG + if(pc_debug) + printk(KDBG_XIRC "do_config(%p)\n", dev ); + #endif + + if( map->port != 255 && map->port != dev->if_port ) { + if( map->port <= 4 ) { + if( !map->port ) { + local->probe_port = 1; + dev->if_port = 1; + } + else { + local->probe_port = 0; + dev->if_port = map->port; + } + printk(KERN_INFO "%s: switching to %s port\n", + dev->name, if_names[dev->if_port]); + do_reset(dev,1); /* not the fine way :-) */ + } + else + return -EINVAL; + } + #ifdef PCMCIA_DEBUG + else if( map->port == dev->if_port && local->mohawk ) { + /* kludge to print the mii regsiters */ + mii_dump(dev); + } + #endif + return 0; +} + +/**************** + * Open the driver + */ +static int +do_open(struct net_device *dev) +{ + local_info_t *lp = dev->priv; + dev_link_t *link; + + #ifdef PCMCIA_DEBUG + if(pc_debug) + printk(KDBG_XIRC "do_open(%p)\n", dev ); + #endif + + /* Check that the PCMCIA card is still here. */ + for( link = dev_list; link; link = link->next ) + if( link->priv == dev ) + break; + /* Physical device present signature. */ + if( !DEV_OK(link) ) + return -ENODEV; + + /* okay */ + link->open++; + MOD_INC_USE_COUNT; + + dev->interrupt = 0; dev->tbusy = 0; dev->start = 1; + lp->suspended = 0; + do_reset(dev,1); + + return 0; +} + +static int +do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + local_info_t *local = dev->priv; + ioaddr_t ioaddr = dev->base_addr; + u16 *data = (u16 *)&rq->ifr_data; + + #ifdef PCMCIA_DEBUG + if(pc_debug > 1) + printk(KERN_DEBUG "%s: ioctl(%-.6s, %#04x) %04x %04x %04x %04x\n", + dev->name, rq->ifr_ifrn.ifrn_name, cmd, + data[0], data[1], data[2], data[3] ); + #endif + + if( !local->mohawk ) + return -EOPNOTSUPP; + + switch( cmd ) { + case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */ + data[0] = 0; /* we have only this address */ + /* fall trough */ + case SIOCDEVPRIVATE+1: /* Read the specified MII register. */ + data[3] = mii_rd( ioaddr, data[0] & 0x1f, data[1] & 0x1f); + break; + case SIOCDEVPRIVATE+2: /* Write the specified MII register */ + if( !suser() ) + return -EPERM; + mii_wr(ioaddr, data[0] & 0x1f, data[1] & 0x1f, data[2], 16); + break; + default: + return -EOPNOTSUPP; + } + return 0; +} + +static void +hardreset(struct net_device *dev) +{ + local_info_t *local = dev->priv; + ioaddr_t ioaddr = dev->base_addr; + + SelectPage(4); + udelay(1); + PutByte(XIRCREG4_GPR1, 0); /* clear bit 0: power down */ + busy_loop(HZ/25); /* wait 40 msec */ + if( local->mohawk ) + PutByte(XIRCREG4_GPR1, 1); /* set bit 0: power up */ + else + PutByte(XIRCREG4_GPR1, 1 | 4); /* set bit 0: power up, bit 2: AIC */ + busy_loop(HZ/50); /* wait 20 msec */ +} + +static void +do_reset(struct net_device *dev, int full) +{ + local_info_t *local = dev->priv; + ioaddr_t ioaddr = dev->base_addr; + unsigned value; + + #ifdef PCMCIA_DEBUG + if(pc_debug) + printk(KERN_DEBUG "%s: do_reset(%p,%d)\n", + dev? dev->name:"eth?", dev, full ); + #endif + + hardreset(dev); + PutByte(XIRCREG_CR, SoftReset ); /* set */ + busy_loop(HZ/50); /* wait 20 msec */ + PutByte(XIRCREG_CR, 0 ); /* clear */ + busy_loop(HZ/25); /* wait 40 msec */ + if( local->mohawk ) { + SelectPage(4); + /* set pin GP1 and GP2 to output (0x0c) + * set GP1 to low to power up the ML6692 (0x00) + * set GP2 to high to power up the 10Mhz chip (0x02) + */ + PutByte(XIRCREG4_GPR0, 0x0e); + } + + /* give the circuits some time to power up */ + busy_loop(HZ/2); /* about 500ms */ + + local->last_ptr_value = 0; + local->silicon = local->mohawk ? (GetByte(XIRCREG4_BOV) & 0x70) >> 4 + : (GetByte(XIRCREG4_BOV) & 0x30) >> 4; + + if( local->probe_port ) { + if( !local->mohawk ) { + SelectPage(4); + PutByte(XIRCREG4_GPR0, 4); + local->probe_port = 0; + } + } + else if( dev->if_port == 2 ) { /* enable 10Base2 */ + SelectPage(0x42); + PutByte(XIRCREG42_SWC1, 0xC0); + } + else { /* enable 10BaseT */ + SelectPage(0x42); + PutByte(XIRCREG42_SWC1, 0x80); + } + busy_loop(HZ/25); /* wait 40 msec to let it complete */ + + #ifdef PCMCIA_DEBUG + if(pc_debug) { + SelectPage(0); + value = GetByte(XIRCREG_ESR); /* read the ESR */ + printk(KERN_DEBUG "%s: ESR is: %#02x\n", dev->name, value); + } + #endif + + /* setup the ECR */ + SelectPage(1); + PutByte(XIRCREG1_IMR0, 0xff ); /* allow all ints */ + PutByte(XIRCREG1_IMR1, 1 ); /* and Set TxUnderrunDetect */ + value = GetByte(XIRCREG1_ECR); + #if 0 + if( local->mohawk ) + value |= DisableLinkPulse; + PutByte(XIRCREG1_ECR, value); + #endif + #ifdef PCMCIA_DEBUG + if(pc_debug) + printk(KERN_DEBUG "%s: ECR is: %#02x\n", dev->name, value); + #endif + + SelectPage(0x42); + PutByte(XIRCREG42_SWC0, 0x20); /* disable source insertion */ + + if( local->silicon != 1 ) { + /* set the local memory dividing line. + * The comments in the sample code say that this is only + * settable with the scipper version 2 which is revision 0. + * Always for CE3 cards + */ + SelectPage(2); + PutWord(XIRCREG2_RBS, 0x2000 ); + } + + if( full ) + set_addresses(dev); + + /* Hardware workaround: + * The receive byte pointer after reset is off by 1 so we need + * to move the offset pointer back to 0. + */ + SelectPage(0); + PutWord(XIRCREG0_DO, 0x2000 ); /* change offset command, off=0 */ + + /* setup MAC IMRs and clear status registers */ + SelectPage(0x40); /* Bit 7 ... bit 0 */ + PutByte(XIRCREG40_RMASK0, 0xff); /* ROK, RAB, rsv, RO, CRC, AE, PTL, MP */ + PutByte(XIRCREG40_TMASK0, 0xff); /* TOK, TAB, SQE, LL, TU, JAB, EXC, CRS */ + PutByte(XIRCREG40_TMASK1, 0xb0); /* rsv, rsv, PTD, EXT, rsv,rsv,rsv, rsv*/ + PutByte(XIRCREG40_RXST0, 0x00); /* ROK, RAB, REN, RO, CRC, AE, PTL, MP */ + PutByte(XIRCREG40_TXST0, 0x00); /* TOK, TAB, SQE, LL, TU, JAB, EXC, CRS */ + PutByte(XIRCREG40_TXST1, 0x00); /* TEN, rsv, PTD, EXT, retry_counter:4 */ + + if( full && local->mohawk && init_mii(dev) ) { + if( dev->if_port == 4 || local->dingo ) { /* and use it */ + SelectPage(2); + value = GetByte(XIRCREG2_MSR); + value |= 0x08; /* Select MII */ + PutByte(XIRCREG2_MSR, value); + busy_loop(HZ/50); /* wait 20 msec */ + #ifdef PCMCIA_DEBUG + if(pc_debug) + printk(KERN_DEBUG "%s: MII selected\n", dev->name); + #endif + } + else { + SelectPage(2); + PutByte(XIRCREG2_MSR, GetByte(XIRCREG2_MSR) | 0x08); + busy_loop(HZ/50); + #ifdef PCMCIA_DEBUG + if(pc_debug) + printk(KERN_DEBUG "%s: MII detected; using 10mbs\n", + dev->name); + #endif + SelectPage(0x42); + if( dev->if_port == 2 ) /* enable 10Base2 */ + PutByte(XIRCREG42_SWC1, 0xC0); + else /* enable 10BaseT */ + PutByte(XIRCREG42_SWC1, 0x80); + busy_loop(HZ/25); /* wait 40 msec to let it complete */ + } + } + else { /* No MII */ + SelectPage(0); + value = GetByte(XIRCREG_ESR); /* read the ESR */ + dev->if_port = (value & MediaSelect) ? 1 : 2; + } + + /* configure the LEDs */ + SelectPage(2); + if( dev->if_port == 1 || dev->if_port == 4 ) /* TP: Link and Activity */ + PutByte(XIRCREG2_LED, 0x3b ); + else /* Coax: Not-Collision and Activity */ + PutByte(XIRCREG2_LED, 0x3a ); + + if (local->dingo) + PutByte( 0x0b, 0x04 ); /* 100 Mbit LED */ + + /* enable receiver and put the mac online */ + if( full ) { + SelectPage(0x40); + PutByte(XIRCREG40_CMD0, EnableRecv | Online ); + } + + /* setup Ethernet IMR and enable interrupts */ + SelectPage(1); + PutByte(XIRCREG1_IMR0, 0xff ); + udelay(1); + SelectPage(0); + PutByte(XIRCREG_CR, EnableIntr ); + if( local->modem && !local->dingo ) { /* do some magic */ + if( !(GetByte( 0x10 ) & 0x01 ) ) + PutByte( 0x10, 0x11 ); /* unmask master-int bit */ + } + + if( full ) + printk(KERN_INFO "%s: media %s, silicon revision %d\n", dev->name, + if_names[dev->if_port], local->silicon); + /* We should switch back to page 0 to avoid a bug in revision 0 + * where regs with offset below 8 can't be read after an access + * to the MAC registers */ + SelectPage(0); +} + +/**************** + * Initialize the Media-Independent-Interface + * Returns: True if we have a good MII + */ +static int +init_mii(struct net_device *dev) +{ + local_info_t *local = dev->priv; + ioaddr_t ioaddr = dev->base_addr; + unsigned control, status, linkpartner; + int i; + + #ifdef PCMCIA_DEBUG + if(pc_debug>1) { + mii_dump(dev); + } + #endif + + status = mii_rd(ioaddr, 0, 1 ); + if( (status & 0xff00) != 0x7800 ) + return 0; /* No MII */ + + if( local->probe_port ) + control = 0x1000; /* auto neg */ + else if( dev->if_port == 4 ) + control = 0x2000; /* no auto neg, 100mbs mode */ + else + control = 0x0000; /* no auto neg, 10mbs mode */ + mii_wr(ioaddr, 0, 0, control, 16 ); + udelay(100); + control = mii_rd(ioaddr, 0, 0 ); + + if( control & 0x0400 ) { + printk(KERN_NOTICE "%s can't take PHY out of isolation mode\n", + dev->name); + local->probe_port = 0; + return 0; + } + + if( local->probe_port ) { + /* according to the DP83840A specs the auto negotation process + * may take up to 3.5 sec, so we use this also for our ML6692 + * Fixme: Better to use a timer here! + */ + for(i=0; i < 35; i++ ) { + busy_loop(HZ/10); /* wait 100 msec */ + status = mii_rd(ioaddr, 0, 1 ); + if( (status & 0x0020) && (status & 0x0004) ) + break; + } + + if( !(status & 0x0020) ) { + printk(KERN_NOTICE "%s: auto negotation failed;" + " using 10mbs\n", dev->name ); + control = 0x0000; + mii_wr(ioaddr, 0, 0, control, 16 ); + udelay(100); + SelectPage(0); + dev->if_port = (GetByte(XIRCREG_ESR) & MediaSelect) ? 1 : 2; + } + else { + linkpartner = mii_rd(ioaddr, 0, 5 ); + printk(KERN_INFO "%s: MII link partner: %04x\n", dev->name, + linkpartner ); + if( linkpartner & 0x0080 ) { /* 100BaseTx capability */ + dev->if_port = 4; + } + else + dev->if_port = 1; + } + local->probe_port = 0; + } + + #ifdef PCMCIA_DEBUG + if( pc_debug ) + mii_dump(dev); + #endif + + return 1; +} + +static void +do_powerdown(struct net_device *dev) +{ + + ioaddr_t ioaddr = dev->base_addr; + + #ifdef PCMCIA_DEBUG + if(pc_debug) + printk(KDBG_XIRC "do_powerdown(%p)\n", dev ); + #endif + + SelectPage(4); + PutByte(XIRCREG4_GPR1, 0); /* clear bit 0: power down */ + SelectPage(0); +} + +static int +do_stop( struct net_device *dev) +{ + ioaddr_t ioaddr = dev->base_addr; + dev_link_t *link; + + #ifdef PCMCIA_DEBUG + if(pc_debug) + printk(KDBG_XIRC "do_stop(%p)\n", dev ); + #endif + + for(link = dev_list; link; link = link->next) + if(link->priv == dev) + break; + if( !link ) + return -ENODEV; + + #ifdef PCMCIA_DEBUG + if(pc_debug) + printk(KDBG_XIRC "shutting down\n"); + #endif + dev->tbusy = 1; + dev->start = 0; + + SelectPage(0); + PutByte(XIRCREG_CR, 0 ); /* disable interrupts */ + SelectPage(0x01); + PutByte(XIRCREG1_IMR0, 0x00 ); /* forbid all ints */ + SelectPage(4); + PutByte(XIRCREG4_GPR1, 0); /* clear bit 0: power down */ + SelectPage(0); + + link->open--; dev->start = 0; + if (link->state & DEV_STALE_CONFIG) { + link->release.expires = jiffies + HZ/20; + link->state |= DEV_RELEASE_PENDING; + add_timer(&link->release); + } + + MOD_DEC_USE_COUNT; + + return 0; +} + +static int __init +init_xirc2ps_cs(void) +{ + servinfo_t serv; + + printk(KERN_INFO "%s\n", version); + if( card_type ) + printk(KINF_XIRC "option card_type is obsolete\n"); + if( lockup_hack ) + printk(KINF_XIRC "lockup hack is enabled\n"); + CardServices(GetCardServicesInfo, &serv); + if( serv.Revision != CS_RELEASE_CODE ) { + printk(KNOT_XIRC "Card Services release does not match!\n"); + return -1; + } + #ifdef PCMCIA_DEBUG + if( pc_debug ) + printk(KDBG_XIRC "pc_debug=%d\n", pc_debug); + #endif + register_pccard_driver(&dev_info, &xirc2ps_attach, &xirc2ps_detach); + return 0; +} + +static void __exit +exit_xirc2ps_cs(void) +{ + #ifdef PCMCIA_DEBUG + if(pc_debug) + printk(KDBG_XIRC "unloading\n"); + #endif + unregister_pccard_driver(&dev_info); + while( dev_list ) { + if( dev_list->state & DEV_CONFIG ) + xirc2ps_release( (u_long)dev_list ); + if( dev_list ) /* xirc2ps_release() might already have detached... */ + xirc2ps_detach( dev_list ); + } +} + +module_init(init_xirc2ps_cs); +module_exit(exit_xirc2ps_cs); + diff --git a/drivers/net/slhc.c b/drivers/net/slhc.c index 7dc3fbf39..87e498884 100644 --- a/drivers/net/slhc.c +++ b/drivers/net/slhc.c @@ -758,12 +758,6 @@ void __init slhc_install(void) #endif /* MODULE */ #else /* CONFIG_INET */ -EXPORT_SYMBOL(slhc_init); -EXPORT_SYMBOL(slhc_free); -EXPORT_SYMBOL(slhc_remember); -EXPORT_SYMBOL(slhc_compress); -EXPORT_SYMBOL(slhc_uncompress); -EXPORT_SYMBOL(slhc_toss); int slhc_toss(struct slcompress *comp) @@ -804,5 +798,11 @@ slhc_init(int rslots, int tslots) printk(KERN_DEBUG "Called IP function on non IP-system: slhc_init"); return NULL; } +EXPORT_SYMBOL(slhc_init); +EXPORT_SYMBOL(slhc_free); +EXPORT_SYMBOL(slhc_remember); +EXPORT_SYMBOL(slhc_compress); +EXPORT_SYMBOL(slhc_uncompress); +EXPORT_SYMBOL(slhc_toss); #endif /* CONFIG_INET */ diff --git a/drivers/net/starfire.c b/drivers/net/starfire.c index 09891202c..b036deee4 100644 --- a/drivers/net/starfire.c +++ b/drivers/net/starfire.c @@ -81,6 +81,7 @@ static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; #endif #include <linux/kernel.h> +#include <linux/version.h> #include <linux/sched.h> #include <linux/string.h> #include <linux/timer.h> diff --git a/drivers/net/via-rhine.c b/drivers/net/via-rhine.c index 21275376c..a05f54bb3 100644 --- a/drivers/net/via-rhine.c +++ b/drivers/net/via-rhine.c @@ -111,11 +111,6 @@ static const int multicast_filter_limit = 32; #ifdef MODULE char kernel_version[] = UTS_RELEASE; -#else -#ifndef __alpha__ -#define ioremap vremap -#define iounmap vfree -#endif #endif #if defined(MODULE) && LINUX_VERSION_CODE > 0x20115 MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>"); diff --git a/drivers/pci/helper.c b/drivers/pci/helper.c index 928cec4b5..883bfb89d 100644 --- a/drivers/pci/helper.c +++ b/drivers/pci/helper.c @@ -57,7 +57,7 @@ int pci_simple_probe (struct pci_simple_probe_entry *list, size_t match_limit, if (match_limit && match_limit == matches) return matches; - ent++; + break; /* stop list search on first match */ } dev = pci_find_device (PCI_ANY_ID, PCI_ANY_ID, dev); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 5cf991521..7d55ba550 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -140,6 +140,64 @@ pci_find_parent_resource(struct pci_dev *dev, struct resource *res) return best; } +/* + * Set power management state of a device. For transitions from state D3 + * it isn't as straightforward as one could assume since many devices forget + * their configuration space during wakeup. Returns old power state. + */ +int +pci_set_power_state(struct pci_dev *dev, int new_state) +{ + u32 base[5], romaddr; + u16 pci_command, pwr_command; + u8 pci_latency, pci_cacheline; + int i, old_state; + int pm = pci_find_capability(dev, PCI_CAP_ID_PM); + + if (!pm) + return 0; + pci_read_config_word(dev, pm + PCI_PM_CTRL, &pwr_command); + old_state = pwr_command & PCI_PM_CTRL_STATE_MASK; + if (old_state == new_state) + return old_state; + DBG("PCI: %s goes from D%d to D%d\n", dev->slot_name, old_state, new_state); + if (old_state == 3) { + pci_read_config_word(dev, PCI_COMMAND, &pci_command); + pci_write_config_word(dev, PCI_COMMAND, pci_command & ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY)); + for (i = 0; i < 5; i++) + pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + i*4, &base[i]); + pci_read_config_dword(dev, PCI_ROM_ADDRESS, &romaddr); + pci_read_config_byte(dev, PCI_LATENCY_TIMER, &pci_latency); + pci_read_config_byte(dev, PCI_CACHE_LINE_SIZE, &pci_cacheline); + pci_write_config_word(dev, pm + PCI_PM_CTRL, new_state); + for (i = 0; i < 5; i++) + pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + i*4, base[i]); + pci_write_config_dword(dev, PCI_ROM_ADDRESS, romaddr); + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); + pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, pci_cacheline); + pci_write_config_byte(dev, PCI_LATENCY_TIMER, pci_latency); + pci_write_config_word(dev, PCI_COMMAND, pci_command); + } else + pci_write_config_word(dev, pm + PCI_PM_CTRL, (pwr_command & ~PCI_PM_CTRL_STATE_MASK) | new_state); + return old_state; +} + +/* + * Initialize device before it's used by a driver. Ask low-level code + * to enable I/O and memory. Wake up the device if it was suspended. + * Beware, this function can fail. + */ +int +pci_enable_device(struct pci_dev *dev) +{ + int err; + + if ((err = pcibios_enable_device(dev)) < 0) + return err; + pci_set_power_state(dev, 0); + return 0; +} + /* * This interrupt-safe spinlock protects all accesses to PCI diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 3a2ae4599..7c2ff12d5 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -128,6 +128,7 @@ static struct pci_fixup pci_fixups[] __initdata = { */ { PCI_FIXUP_FINAL, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_0, quirk_isa_dma_hangs }, { PCI_FIXUP_FINAL, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C596, quirk_isa_dma_hangs }, + { PCI_FIXUP_FINAL, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_0, quirk_isa_dma_hangs }, { PCI_FIXUP_HEADER, PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_868, quirk_s3_64M }, { PCI_FIXUP_HEADER, PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_968, quirk_s3_64M }, { PCI_FIXUP_FINAL, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437, quirk_triton }, diff --git a/drivers/pci/setup.c b/drivers/pci/setup.c index 9c752d0de..930484b16 100644 --- a/drivers/pci/setup.c +++ b/drivers/pci/setup.c @@ -323,3 +323,9 @@ pci_fixup_irqs(u8 (*swizzle)(struct pci_dev *, u8 *), for (dev = pci_devices; dev; dev = dev->next) pdev_fixup_irq(dev, swizzle, map_irq); } + +int +pcibios_enable_device(struct pci_dev *dev) +{ + return 0; +} diff --git a/drivers/pcmcia/Config.in b/drivers/pcmcia/Config.in index 2883046c1..aaedbf74a 100644 --- a/drivers/pcmcia/Config.in +++ b/drivers/pcmcia/Config.in @@ -2,13 +2,15 @@ # PCMCIA bus subsystem configuration # mainmenu_option next_comment -comment 'PCMCIA/Cardbus support' +comment 'PCMCIA/CardBus support' -tristate 'PCMCIA/Cardbus support' CONFIG_PCMCIA +tristate 'PCMCIA/CardBus support' CONFIG_PCMCIA if [ "$CONFIG_PCMCIA" != "n" ]; then if [ "$CONFIG_PCI" != "n" ]; then bool ' CardBus support' CONFIG_CARDBUS fi + bool ' i82365/Yenta compatible bridge support' CONFIG_I82365 + bool ' Databook TCIC host bridge support' CONFIG_TCIC fi endmenu diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile index 71750544d..703a7c6c8 100644 --- a/drivers/pcmcia/Makefile +++ b/drivers/pcmcia/Makefile @@ -15,20 +15,34 @@ ALL_SUB_DIRS := $(SUB_DIRS) MOD_LIST_NAME := PCMCIA_MODULES ifeq ($(CONFIG_PCMCIA),y) - O_OBJS := i82365.o tcic.o cistpl.o rsrc_mgr.o bulkmem.o + O_OBJS := cistpl.o rsrc_mgr.o bulkmem.o OX_OBJS := ds.o cs.o O_TARGET := pcmcia.o + ifeq ($(CONFIG_I82365),y) + O_OBJS += i82365.o + endif + ifeq ($(CONFIG_TCIC),y) + O_OBJS += tcic.o + endif ifeq ($(CONFIG_CARDBUS),y) O_OBJS += cardbus.o + OX_OBJS += cb_enabler.o endif else ifeq ($(CONFIG_PCMCIA),m) - M_OBJS := i82365.o tcic.o pcmcia_core.o + M_OBJS := pcmcia_core.o MX_OBJS := ds.o MIX_OBJS := cs.o CORE_OBJS := cistpl.o rsrc_mgr.o bulkmem.o cs.o + ifeq ($(CONFIG_I82365),y) + M_OBJS += i82365.o + endif + ifeq ($(CONFIG_TCIC),y) + M_OBJS += tcic.o + endif ifeq ($(CONFIG_CARDBUS),y) CORE_OBJS += cardbus.o + MX_OBJS += cb_enabler.o endif endif endif diff --git a/drivers/pcmcia/cardbus.c b/drivers/pcmcia/cardbus.c index de8b76da3..490182e00 100644 --- a/drivers/pcmcia/cardbus.c +++ b/drivers/pcmcia/cardbus.c @@ -2,7 +2,7 @@ Cardbus device configuration - cardbus.c 1.59 1999/09/15 15:32:19 + cardbus.c 1.61 1999/10/20 22:36:57 The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file @@ -325,9 +325,9 @@ int cb_alloc(socket_info_t *s) pci_readl(bus, i, PCI_CLASS_REVISION, &c[i].dev.class); c[i].dev.class >>= 8; c[i].dev.hdr_type = hdr; -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_PROC_FS pci_proc_attach_device(&c[i].dev); -#endif +#endif } return CS_SUCCESS; @@ -344,9 +344,9 @@ void cb_free(socket_info_t *s) if (*p == &c[0].dev) break; for (q = *p; q; q = q->next) { if (q->bus != (*p)->bus) break; -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_PROC_FS pci_proc_detach_device(q); -#endif +#endif } if (*p) *p = q; s->cap.cb_bus->devices = NULL; @@ -496,7 +496,8 @@ int cb_config(socket_info_t *s) s->irq.AssignedIRQ = irq; } } - c[0].dev.irq = irq; + for (i = 0; i < fn; i++) + c[i].dev.irq = irq; return CS_SUCCESS; diff --git a/drivers/pcmcia/cb_enabler.c b/drivers/pcmcia/cb_enabler.c index b0e75ceec..00cf824b3 100644 --- a/drivers/pcmcia/cb_enabler.c +++ b/drivers/pcmcia/cb_enabler.c @@ -2,7 +2,7 @@ Cardbus device enabler - cb_enabler.c 1.23 1999/09/15 15:32:19 + cb_enabler.c 1.24 1999/10/20 00:19:09 The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file @@ -58,7 +58,7 @@ static int pc_debug = PCMCIA_DEBUG; MODULE_PARM(pc_debug, "i"); #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) static char *version = -"cb_enabler.c 1.23 1999/09/15 15:32:19 (David Hinds)"; +"cb_enabler.c 1.24 1999/10/20 00:19:09 (David Hinds)"; #else #define DEBUG(n, args...) do { } while (0) #endif @@ -372,6 +372,9 @@ void unregister_driver(struct driver_operations *ops) /*====================================================================*/ +EXPORT_SYMBOL(register_driver); +EXPORT_SYMBOL(unregister_driver); + static int __init init_cb_enabler(void) { servinfo_t serv; diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index 470514c55..f7fda7d8d 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -2,7 +2,7 @@ PCMCIA Card Services -- core services - cs.c 1.228 1999/09/15 15:32:19 + cs.c 1.232 1999/10/20 22:17:24 The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file @@ -70,7 +70,7 @@ static int handle_apm_event(apm_event_t event); int pc_debug = PCMCIA_DEBUG; MODULE_PARM(pc_debug, "i"); static const char *version = -"cs.c 1.228 1999/09/15 15:32:19 (David Hinds)"; +"cs.c 1.232 1999/10/20 22:17:24 (David Hinds)"; #endif static const char *release = "Linux PCMCIA Card Services " CS_RELEASE; @@ -353,41 +353,40 @@ void unregister_ss_entry(ss_entry_t ss_entry) socket_info_t *s = NULL; client_t *client; +#ifdef CONFIG_PROC_FS + for (i = 0; i < sockets; i++) { + s = socket_table[i]; + if (s->ss_entry != ss_entry) continue; + if (proc_pccard) { + char name[3]; + sprintf(name, "%02d", i); +#ifdef PCMCIA_DEBUG + remove_proc_entry("clients", s->proc); +#endif + } + } +#endif + for (;;) { for (i = 0; i < sockets; i++) { s = socket_table[i]; if (s->ss_entry == ss_entry) break; } - if (i == sockets) { + if (i == sockets) break; - } else { -#ifdef CONFIG_PROC_FS - if (proc_pccard) { - char name[3]; - sprintf(name, "%02d", i); -#ifdef PCMCIA_DEBUG - remove_proc_entry("clients", s->proc); -#endif - remove_proc_entry(name, proc_pccard); - } -#endif - while (s->clients) { - client = s->clients; - s->clients = s->clients->next; - kfree(client); - } - init_socket(s); - release_cis_mem(s); -#ifdef CONFIG_CARDBUS - cb_release_cis_mem(s); -#endif - s->ss_entry = NULL; - kfree(s); - socket_table[i] = NULL; - for (j = i; j < sockets-1; j++) - socket_table[j] = socket_table[j+1]; - sockets--; + shutdown_socket(i); + release_cis_mem(s); + while (s->clients) { + client = s->clients; + s->clients = s->clients->next; + kfree(client); } + s->ss_entry = NULL; + kfree(s); + socket_table[i] = NULL; + for (j = i; j < sockets-1; j++) + socket_table[j] = socket_table[j+1]; + sockets--; } } /* unregister_ss_entry */ @@ -1808,7 +1807,7 @@ static int request_window(client_handle_t *handle, win_req_t *req) { socket_info_t *s; window_t *win; - int w; + int w, align; if (CHECK_HANDLE(*handle)) return CS_BAD_HANDLE; @@ -1835,9 +1834,10 @@ static int request_window(client_handle_t *handle, win_req_t *req) win->sock = s; win->base = req->Base; win->size = req->Size; + align = ((s->cap.features & SS_CAP_MEM_ALIGN) || + (req->Attributes & WIN_STRICT_ALIGN)); if (find_mem_region(&win->base, win->size, (*handle)->dev_info, - ((s->cap.features & SS_CAP_MEM_ALIGN) ? - req->Size : s->cap.map_size), + (align ? req->Size : s->cap.map_size), (req->Attributes & WIN_MAP_BELOW_1MB) || !(s->cap.features & SS_CAP_PAGE_REGS))) return CS_IN_USE; diff --git a/drivers/pcmcia/i82365.c b/drivers/pcmcia/i82365.c index 9e80943e4..ed7bd3c30 100644 --- a/drivers/pcmcia/i82365.c +++ b/drivers/pcmcia/i82365.c @@ -3,7 +3,7 @@ Device driver for Intel 82365 and compatible PC Card controllers, and Yenta-compatible PCI-to-CardBus controllers. - i82365.c 1.254 1999/09/15 15:32:19 + i82365.c 1.260 1999/10/21 00:56:07 The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file @@ -76,7 +76,7 @@ static int pc_debug = PCMCIA_DEBUG; MODULE_PARM(pc_debug, "i"); #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) static const char *version = -"i82365.c 1.254 1999/09/15 15:32:19 (David Hinds)"; +"i82365.c 1.260 1999/10/21 00:56:07 (David Hinds)"; #else #define DEBUG(n, args...) do { } while (0) #endif @@ -189,7 +189,7 @@ MODULE_PARM(cb_write_post, "i"); #ifdef CONFIG_ISA #ifdef CONFIG_PCI /* PCI card status change interrupts? */ -static int pci_csc = 0; +static int pci_csc = 1; /* PCI IO card functional interrupts? */ static int pci_int = 0; MODULE_PARM(pci_csc, "i"); @@ -240,7 +240,7 @@ typedef struct topic_state_t { typedef struct socket_info_t { u_short type, flags; socket_cap_t cap; - u_short ioaddr; + ioaddr_t ioaddr; u_short psock; u_char cs_irq, intr; void (*handler)(void *info, u_int events); @@ -278,18 +278,18 @@ static socket_info_t socket[8] = { /* Default ISA interrupt mask */ #define I365_MASK 0xdeb8 /* irq 15,14,12,11,10,9,7,5,4,3 */ -static void pcic_interrupt_wrapper(u_long); -static void pcic_interrupt(int irq, void *dev, - struct pt_regs *regs); -static int pcic_service(u_int sock, u_int cmd, void *arg); -#ifdef CONFIG_PROC_FS -static void pcic_proc_remove(u_short sock); -#endif - #ifdef CONFIG_ISA static int grab_irq; static spinlock_t isa_lock = SPIN_LOCK_UNLOCKED; +#define ISA_LOCK(n, f) \ + if (!(socket[n].flags & IS_CARDBUS)) spin_lock_irqsave(&isa_lock, f) +#define ISA_UNLOCK(n, f) \ + if (!(socket[n].flags & IS_CARDBUS)) spin_unlock_irqrestore(&isa_lock, f) +#else +#define ISA_LOCK(n, f) do { } while (0) +#define ISA_UNLOCK(n, f) do { } while (0) #endif + static struct timer_list poll_timer; /*====================================================================*/ @@ -322,7 +322,7 @@ typedef enum pcic_id { #ifdef CONFIG_PCI IS_PD6729, IS_PD6730, IS_OZ6729, IS_OZ6730, IS_I82092AA, IS_OM82C092G, - IS_PD6832, IS_OZ6832, IS_OZ6836, + IS_PD6832, IS_OZ6832, IS_OZ6836, IS_OZ6812, IS_RL5C465, IS_RL5C466, IS_RL5C475, IS_RL5C476, IS_RL5C478, IS_SMC34C90, IS_TI1130, IS_TI1131, IS_TI1250A, IS_TI1220, IS_TI1221, IS_TI1210, @@ -388,6 +388,8 @@ static pcic_t pcic[] = { PCI_VENDOR_ID_O2, PCI_DEVICE_ID_O2_6832 }, { "O2Micro OZ6836/OZ6860", IS_O2MICRO|IS_CARDBUS|IS_VG_PWR, PCI_VENDOR_ID_O2, PCI_DEVICE_ID_O2_6836 }, + { "O2Micro OZ6812", IS_O2MICRO|IS_CARDBUS|IS_VG_PWR, + PCI_VENDOR_ID_O2, PCI_DEVICE_ID_O2_6812 }, { "Ricoh RL5C465", IS_RICOH|IS_CARDBUS|IS_DF_PWR, PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_RL5C465 }, { "Ricoh RL5C466", IS_RICOH|IS_CARDBUS|IS_DF_PWR, @@ -441,8 +443,6 @@ static pcic_t pcic[] = { /* Some PCI shortcuts */ -#ifdef CONFIG_PCI - #define pci_readb pcibios_read_config_byte #define pci_writeb pcibios_write_config_byte #define pci_readw pcibios_read_config_word @@ -457,7 +457,6 @@ static pcic_t pcic[] = { static void cb_get_power(u_short sock, socket_state_t *state); static void cb_set_power(u_short sock, socket_state_t *state); -#endif /*====================================================================*/ @@ -469,7 +468,7 @@ static u_char i365_get(u_short sock, u_short reg) else #endif { - u_short port = socket[sock].ioaddr; + ioaddr_t port = socket[sock].ioaddr; u_char val; reg = I365_REG(socket[sock].psock, reg); outb(reg, port); val = inb(port+1); @@ -485,7 +484,7 @@ static void i365_set(u_short sock, u_short reg, u_char data) else #endif { - u_short port = socket[sock].ioaddr; + ioaddr_t port = socket[sock].ioaddr; u_char val = I365_REG(socket[sock].psock, reg); outb(val, port); outb(data, port+1); } @@ -954,6 +953,8 @@ static u_int __init o2micro_set_opts(u_short s, char *buf) p->mode_e &= ~O2_MODE_E_MHPG_DMA; p->mhpg |= O2_MHPG_CINT_ENA | O2_MHPG_CSC_ENA; p->mhpg &= ~O2_MHPG_CHANNEL; + if (t->revision == 0x34) + p->mode_c = 0x20; } else { if (p->mode_b & O2_MODE_B_IRQ15_RI) mask &= ~0x8000; } @@ -1125,7 +1126,7 @@ static void __init cb_set_opts(u_short s, char *buf) ======================================================================*/ -static void get_host_state(u_short s) +static void get_bridge_state(u_short s) { socket_info_t *t = &socket[s]; if (t->flags & IS_CIRRUS) @@ -1148,7 +1149,7 @@ static void get_host_state(u_short s) #endif } -static void set_host_state(u_short s) +static void set_bridge_state(u_short s) { socket_info_t *t = &socket[s]; #ifdef CONFIG_PCI @@ -1178,7 +1179,7 @@ static void set_host_state(u_short s) #endif } -static u_int __init set_host_opts(u_short s, u_short ns) +static u_int __init set_bridge_opts(u_short s, u_short ns) { u_short i; u_int m = 0xffff; @@ -1190,7 +1191,7 @@ static u_int __init set_host_opts(u_short s, u_short ns) continue; } buf[0] = '\0'; - get_host_state(i); + get_bridge_state(i); if (socket[i].flags & IS_CIRRUS) m = cirrus_set_opts(i, buf); #ifdef CONFIG_ISA @@ -1209,7 +1210,7 @@ static u_int __init set_host_opts(u_short s, u_short ns) if (socket[i].flags & IS_CARDBUS) cb_set_opts(i, buf+strlen(buf)); #endif - set_host_state(i); + set_bridge_state(i); printk(KERN_INFO " host opts [%d]:%s\n", i, (*buf) ? buf : " none"); } @@ -1255,7 +1256,7 @@ static u_int __init test_irq(u_short sock, int irq, int pci) if (request_irq(irq, irq_count, (pci?SA_SHIRQ:0), "scan", NULL) != 0) return 1; irq_hits = 0; irq_sock = sock; - current->state = TASK_INTERRUPTIBLE; + __set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(HZ/100); if (irq_hits) { free_irq(irq, NULL); @@ -1310,7 +1311,7 @@ static u_int __init isa_scan(u_short sock, u_int mask0) (cb_set_irq_mode(sock, 0, 0) == 0)) #endif if (do_scan) { - set_host_state(sock); + set_bridge_state(sock); i365_set(sock, I365_CSCINT, 0); for (i = 0; i < 16; i++) if ((mask0 & (1 << i)) && (test_irq(sock, i, 0) == 0)) @@ -1351,7 +1352,7 @@ static void __init pci_scan(u_short sock) u_int i; cb_set_irq_mode(sock, 1, 0); - set_host_state(sock); + set_bridge_state(sock); i365_set(sock, I365_CSCINT, 0); /* Only probe irq's 9..11, to be conservative */ for (i = 9; i < 12; i++) { @@ -1371,7 +1372,7 @@ static void __init pci_scan(u_short sock) static int to_cycles(int ns) { return ns/cycle_time; -} /* speed_convert */ +} static int to_ns(int cycles) { @@ -1517,7 +1518,7 @@ static void __init add_pcic(int ns, int type) for (i = mask = 0; i < 16; i++) mask |= (1<<irq_list[i]); #endif - mask &= I365_MASK & set_host_opts(base, ns); + mask &= I365_MASK & set_bridge_opts(base, ns); #ifdef CONFIG_ISA /* Scan for ISA interrupts */ mask = isa_scan(base, mask); @@ -1672,8 +1673,7 @@ static void __init add_cb_bridge(int type, u_char bus, u_char devfn, s->cb_virt = ioremap(s->cb_phys, 0x1000); pci_writel(bus, devfn, PCI_BASE_ADDRESS_0, s->cb_phys); /* Simple sanity checks */ - if (((readb(s->cb_virt+0x800+I365_IDENT) & 0xf0) - == 0x80) && + if (!(readb(s->cb_virt+0x800+I365_IDENT) & 0x70) && !(readb(s->cb_virt+0x800+I365_CSC) && readb(s->cb_virt+0x800+I365_CSC) && readb(s->cb_virt+0x800+I365_CSC))) @@ -1712,7 +1712,7 @@ static void __init add_cb_bridge(int type, u_char bus, u_char devfn, /* Re-do card type & voltage detection */ cb_writel(sockets-ns, CB_SOCKET_FORCE, CB_SF_CVSTEST); - current->state = TASK_INTERRUPTIBLE; + __set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(HZ/5); /* Set up PCI bus bridge structures if needed */ @@ -1762,9 +1762,8 @@ static void __init pci_probe(u_int class, void (add_fn) static void __init isa_probe(void) { - int i, j, sock, k; - int ns, id; - u_short port; + int i, j, sock, k, ns, id; + ioaddr_t port; if (check_region(i365_base, 2) != 0) { if (sockets == 0) @@ -1812,115 +1811,6 @@ static void __init isa_probe(void) /*====================================================================*/ -static int __init init_i82365(void) -{ - servinfo_t serv; - CardServices(GetCardServicesInfo, &serv); - if (serv.Revision != CS_RELEASE_CODE) { - printk(KERN_NOTICE "i82365: Card Services release " - "does not match!\n"); - return -1; - } - DEBUG(0, "%s\n", version); - printk(KERN_INFO "Intel PCIC probe: "); - sockets = 0; - -#ifdef CONFIG_PCI - if (do_pci_probe && pcibios_present()) { - pci_probe(PCI_CLASS_BRIDGE_CARDBUS, add_cb_bridge); - pci_probe(PCI_CLASS_BRIDGE_PCMCIA, add_pci_bridge); - } -#endif - -#ifdef CONFIG_ISA - isa_probe(); -#endif - - if (sockets == 0) { - printk("not found.\n"); - return -ENODEV; - } - - /* Set up interrupt handler(s) */ -#ifdef CONFIG_ISA - if (grab_irq != 0) - request_irq(cs_irq, pcic_interrupt, 0, "i82365", NULL); -#endif -#ifdef CONFIG_PCI - if (pci_csc) { - u_int i, irq, mask = 0; - for (i = 0; i < sockets; i++) { - irq = socket[i].cap.pci_irq; - if (irq && !(mask & (1<<irq))) - request_irq(irq, pcic_interrupt, SA_SHIRQ, "i82365", NULL); - mask |= (1<<irq); - } - } -#endif - - if (register_ss_entry(sockets, &pcic_service) != 0) - printk(KERN_NOTICE "i82365: register_ss_entry() failed\n"); - - /* Finally, schedule a polling interrupt */ - if (poll_interval != 0) { - poll_timer.function = pcic_interrupt_wrapper; - poll_timer.data = 0; - poll_timer.prev = poll_timer.next = NULL; - poll_timer.expires = jiffies + poll_interval; - add_timer(&poll_timer); - } - - return 0; - -} /* init_i82365 */ - -/*====================================================================*/ - -static void __exit exit_i82365(void) -{ - int i; -#ifdef CONFIG_PROC_FS - for (i = 0; i < sockets; i++) pcic_proc_remove(i); -#endif - unregister_ss_entry(&pcic_service); - if (poll_interval != 0) - del_timer(&poll_timer); -#ifdef CONFIG_ISA - if (grab_irq != 0) - free_irq(cs_irq, NULL); -#endif -#ifdef CONFIG_PCI - if (pci_csc) { - u_int irq, mask = 0; - for (i = 0; i < sockets; i++) { - irq = socket[i].cap.pci_irq; - if (irq && !(mask & (1<<irq))) - free_irq(irq, NULL); - mask |= (1<<irq); - } - } -#endif - for (i = 0; i < sockets; i++) { - i365_set(i, I365_CSCINT, 0); -#ifdef CONFIG_PCI - if (socket[i].cb_virt) { - iounmap(socket[i].cb_virt); - release_mem_region(socket[i].cb_phys, 0x1000); - } else -#endif - release_region(socket[i].ioaddr, 2); - } -} /* exit_i82365 */ - -/*====================================================================*/ - -static void pcic_interrupt_wrapper(u_long data) -{ - pcic_interrupt(0, NULL, NULL); - poll_timer.expires = jiffies + poll_interval; - add_timer(&poll_timer); -} - static void pcic_interrupt(int irq, void *dev, struct pt_regs *regs) { @@ -1938,17 +1828,18 @@ static void pcic_interrupt(int irq, void *dev, if ((socket[i].cs_irq != irq) && (socket[i].cap.pci_irq != irq)) continue; -#ifdef CONFIG_ISA - if (!(socket[i].flags & IS_CARDBUS)) - spin_lock_irqsave(&isa_lock, flags); -#endif + ISA_LOCK(i, flags); csc = i365_get(i, I365_CSC); +#ifdef CONFIG_PCI + if ((socket[i].flags & IS_CARDBUS) && + (cb_readl(i,CB_SOCKET_EVENT) & (CB_SE_CCD1|CB_SE_CCD2))) { + cb_writel(i, CB_SOCKET_EVENT, CB_SE_CCD1|CB_SE_CCD2); + csc |= I365_CSC_DETECT; + } +#endif if ((csc == 0) || (!socket[i].handler) || (i365_get(i, I365_IDENT) & 0x70)) { -#ifdef CONFIG_ISA - if (!(socket[i].flags & IS_CARDBUS)) - spin_unlock_irqrestore(&isa_lock, flags); -#endif + ISA_UNLOCK(i, flags); continue; } events = (csc & I365_CSC_DETECT) ? SS_DETECT : 0; @@ -1959,10 +1850,7 @@ static void pcic_interrupt(int irq, void *dev, events |= (csc & I365_CSC_BVD2) ? SS_BATWARN : 0; events |= (csc & I365_CSC_READY) ? SS_READY : 0; } -#ifdef CONFIG_ISA - if (!(socket[i].flags & IS_CARDBUS)) - spin_unlock_irqrestore(&isa_lock, flags); -#endif + ISA_UNLOCK(i, flags); DEBUG(2, "i82365: socket %d event 0x%02x\n", i, events); if (events) socket[i].handler(socket[i].info, events); @@ -1976,6 +1864,13 @@ static void pcic_interrupt(int irq, void *dev, DEBUG(4, "i82365: interrupt done\n"); } /* pcic_interrupt */ +static void pcic_interrupt_wrapper(u_long data) +{ + pcic_interrupt(0, NULL, NULL); + poll_timer.expires = jiffies + poll_interval; + add_timer(&poll_timer); +} + /*====================================================================*/ static int pcic_register_callback(u_short sock, ss_callback_t *call) @@ -2149,7 +2044,7 @@ static int i365_set_socket(u_short sock, socket_state_t *state) (t->cap.pci_irq == state->io_irq)); t->bcr &= ~CB_BCR_CB_RESET; #endif - set_host_state(sock); + set_bridge_state(sock); /* IO card, RESET flag, IO interrupt */ reg = t->intr; @@ -2250,6 +2145,13 @@ static int i365_set_socket(u_short sock, socket_state_t *state) } i365_set(sock, I365_CSCINT, reg); i365_get(sock, I365_CSC); +#ifdef CONFIG_PCI + if (t->flags & IS_CARDBUS) { + if (t->cs_irq || (pci_csc && t->cap.pci_irq)) + cb_writel(sock, CB_SOCKET_MASK, CB_SM_CCD); + cb_writel(sock, CB_SOCKET_EVENT, -1); + } +#endif return 0; } /* i365_set_socket */ @@ -2428,7 +2330,7 @@ static void cb_get_power(u_short sock, socket_state_t *state) case CB_SC_VCC_3V: state->Vcc = 33; break; case CB_SC_VCC_5V: state->Vcc = 50; break; } - switch (reg & CB_SC_VCC_MASK) { + switch (reg & CB_SC_VPP_MASK) { case CB_SC_VPP_3V: state->Vpp = 33; break; case CB_SC_VPP_5V: state->Vpp = 50; break; case CB_SC_VPP_12V: state->Vpp = 120; break; @@ -2509,12 +2411,12 @@ static int cb_set_socket(u_short sock, socket_state_t *state) (s->cap.pci_irq == state->io_irq)); s->bcr &= ~CB_BCR_CB_RESET; s->bcr |= (state->flags & SS_RESET) ? CB_BCR_CB_RESET : 0; - set_host_state(sock); + set_bridge_state(sock); cb_set_power(sock, state); /* Handle IO interrupt using ISA routing */ - reg = i365_get(sock, I365_INTCTL) & ~I365_IRQ_MASK; + reg = s->intr; if (state->io_irq != s->cap.pci_irq) reg |= state->io_irq; i365_set(sock, I365_INTCTL, reg); @@ -2523,6 +2425,9 @@ static int cb_set_socket(u_short sock, socket_state_t *state) if (state->csc_mask & SS_DETECT) reg |= I365_CSC_DETECT; i365_set(sock, I365_CSCINT, reg); i365_get(sock, I365_CSC); + if (s->cs_irq || (pci_csc && s->cap.pci_irq)) + cb_writel(sock, CB_SOCKET_MASK, CB_SM_CCD); + cb_writel(sock, CB_SOCKET_EVENT, -1); return 0; } /* cb_set_socket */ @@ -2623,9 +2528,8 @@ static int proc_read_exca(char *buf, char **start, off_t pos, #ifdef CONFIG_ISA u_long flags = 0; - if (!(socket[sock].flags & IS_CARDBUS)) - spin_lock_irqsave(&isa_lock, flags); #endif + ISA_LOCK(sock, flags); top = 0x40; if (socket[sock].flags & IS_CARDBUS) top = (socket[sock].flags & IS_CIRRUS) ? 0x140 : 0x50; @@ -2639,10 +2543,7 @@ static int proc_read_exca(char *buf, char **start, off_t pos, i365_get(sock,i+2), i365_get(sock,i+3), ((i % 16) == 12) ? "\n" : " "); } -#ifdef CONFIG_ISA - if (!(socket[sock].flags & IS_CARDBUS)) - spin_unlock_irqrestore(&isa_lock, flags); -#endif + ISA_UNLOCK(sock, flags); return (p - buf); } @@ -2672,13 +2573,15 @@ static int proc_read_cardbus(char *buf, char **start, off_t pos, int count, int *eof, void *data) { u_short sock = (socket_info_t *)data - socket; - int len; - - len = sprintf(buf, "%08x %08x %08x %08x %08x %08x\n", - cb_readl(sock,0), cb_readl(sock,4), - cb_readl(sock,8), cb_readl(sock,12), - cb_readl(sock,16), cb_readl(sock,32)); - return len; + char *p = buf; + int i, top; + + top = (socket[sock].flags & IS_O2MICRO) ? 0x30 : 0x20; + for (i = 0; i < top; i += 0x10) + p += sprintf(p, "%08x %08x %08x %08x\n", + cb_readl(sock,i+0x00), cb_readl(sock,i+0x04), + cb_readl(sock,i+0x08), cb_readl(sock,i+0x0c)); + return (p - buf); } #endif @@ -2757,7 +2660,11 @@ static subfn_t pcic_service_table[] = { static int pcic_service(u_int sock, u_int cmd, void *arg) { subfn_t fn; - + int ret; +#ifdef CONFIG_ISA + u_long flags = 0; +#endif + DEBUG(2, "pcic_ioctl(%d, %d, 0x%p)\n", sock, cmd, arg); if (cmd >= NFUNC) @@ -2782,20 +2689,114 @@ static int pcic_service(u_int sock, u_int cmd, void *arg) } #endif + ISA_LOCK(sock, flags); + ret = (fn == NULL) ? -EINVAL : fn(sock, arg); + ISA_UNLOCK(sock, flags); + return ret; +} /* pcic_service */ + +/*====================================================================*/ + +static int __init init_i82365(void) +{ + servinfo_t serv; + CardServices(GetCardServicesInfo, &serv); + if (serv.Revision != CS_RELEASE_CODE) { + printk(KERN_NOTICE "i82365: Card Services release " + "does not match!\n"); + return -1; + } + DEBUG(0, "%s\n", version); + printk(KERN_INFO "Intel PCIC probe: "); + sockets = 0; + +#ifdef CONFIG_PCI + if (do_pci_probe && pcibios_present()) { + pci_probe(PCI_CLASS_BRIDGE_CARDBUS, add_cb_bridge); + pci_probe(PCI_CLASS_BRIDGE_PCMCIA, add_pci_bridge); + } +#endif + #ifdef CONFIG_ISA - if (!(socket[sock].flags & IS_CARDBUS)) { - int ret; - u_long flags; - spin_lock_irqsave(&isa_lock, flags); - ret = (fn == NULL) ? -EINVAL : fn(sock, arg); - spin_unlock_irqrestore(&isa_lock, flags); - return ret; + isa_probe(); +#endif + + if (sockets == 0) { + printk("not found.\n"); + return -ENODEV; } + + /* Set up interrupt handler(s) */ +#ifdef CONFIG_ISA + if (grab_irq != 0) + request_irq(cs_irq, pcic_interrupt, 0, "i82365", NULL); #endif - return (fn == NULL) ? -EINVAL : fn(sock, arg); -} /* pcic_service */ +#ifdef CONFIG_PCI + if (pci_csc) { + u_int i, irq, mask = 0; + for (i = 0; i < sockets; i++) { + irq = socket[i].cap.pci_irq; + if (irq && !(mask & (1<<irq))) + request_irq(irq, pcic_interrupt, SA_SHIRQ, "i82365", NULL); + mask |= (1<<irq); + } + } +#endif + + if (register_ss_entry(sockets, &pcic_service) != 0) + printk(KERN_NOTICE "i82365: register_ss_entry() failed\n"); -/*====================================================================*/ + /* Finally, schedule a polling interrupt */ + if (poll_interval != 0) { + poll_timer.function = pcic_interrupt_wrapper; + poll_timer.data = 0; + poll_timer.prev = poll_timer.next = NULL; + poll_timer.expires = jiffies + poll_interval; + add_timer(&poll_timer); + } + + return 0; + +} /* init_i82365 */ + +static void __exit exit_i82365(void) +{ + int i; +#ifdef CONFIG_PROC_FS + for (i = 0; i < sockets; i++) pcic_proc_remove(i); +#endif + unregister_ss_entry(&pcic_service); + if (poll_interval != 0) + del_timer(&poll_timer); +#ifdef CONFIG_ISA + if (grab_irq != 0) + free_irq(cs_irq, NULL); +#endif +#ifdef CONFIG_PCI + if (pci_csc) { + u_int irq, mask = 0; + for (i = 0; i < sockets; i++) { + irq = socket[i].cap.pci_irq; + if (irq && !(mask & (1<<irq))) + free_irq(irq, NULL); + mask |= (1<<irq); + } + } +#endif + for (i = 0; i < sockets; i++) { + /* Turn off all interrupt sources! */ + i365_set(i, I365_CSCINT, 0); +#ifdef CONFIG_PCI + if (socket[i].flags & IS_CARDBUS) + cb_writel(i, CB_SOCKET_MASK, 0); + if (socket[i].cb_virt) { + iounmap(socket[i].cb_virt); + release_mem_region(socket[i].cb_phys, 0x1000); + } else +#endif + release_region(socket[i].ioaddr, 2); + } +} /* exit_i82365 */ module_init(init_i82365); module_exit(exit_i82365); diff --git a/drivers/pcmcia/o2micro.h b/drivers/pcmcia/o2micro.h index 89cb6cf01..a3f922239 100644 --- a/drivers/pcmcia/o2micro.h +++ b/drivers/pcmcia/o2micro.h @@ -1,5 +1,5 @@ /* - * o2micro.h 1.10 1999/09/03 16:43:35 + * o2micro.h 1.12 1999/10/16 01:43:24 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in @@ -45,17 +45,38 @@ #ifndef PCI_DEVICE_ID_O2_6836 #define PCI_DEVICE_ID_O2_6836 0x6836 #endif +#ifndef PCI_DEVICE_ID_O2_6812 +#define PCI_DEVICE_ID_O2_6812 0x6872 +#endif + +/* Additional PCI configuration registers */ + +#define O2_MUX_CONTROL 0x90 /* 32 bit */ +#define O2_MUX_RING_OUT 0x0000000f +#define O2_MUX_SKTB_ACTV 0x000000f0 +#define O2_MUX_SCTA_ACTV_ENA 0x00000100 +#define O2_MUX_SCTB_ACTV_ENA 0x00000200 +#define O2_MUX_SER_IRQ_ROUTE 0x0000e000 +#define O2_MUX_SER_PCI 0x00010000 + +#define O2_MUX_SKTA_TURBO 0x000c0000 /* for 6833, 6860 */ +#define O2_MUX_SKTB_TURBO 0x00300000 +#define O2_MUX_AUX_VCC_3V 0x00400000 +#define O2_MUX_PCI_VCC_5V 0x00800000 +#define O2_MUX_PME_MUX 0x0f000000 + +/* Additional ExCA registers */ #define O2_MODE_A 0x38 -#define O2_MODE_A_2 0x26 /* For 6833B, 6860C */ +#define O2_MODE_A_2 0x26 /* for 6833B, 6860C */ #define O2_MODE_A_CD_PULSE 0x04 #define O2_MODE_A_SUSP_EDGE 0x08 #define O2_MODE_A_HOST_SUSP 0x10 -#define O2_MODE_A_PWRCHIP 0x60 +#define O2_MODE_A_PWR_MASK 0x60 #define O2_MODE_A_QUIET 0x80 #define O2_MODE_B 0x39 -#define O2_MODE_B_2 0x2e /* For 6833B, 6860C */ +#define O2_MODE_B_2 0x2e /* for 6833B, 6860C */ #define O2_MODE_B_IDENT 0x03 #define O2_MODE_B_ID_BSTEP 0x00 #define O2_MODE_B_ID_CSTEP 0x01 @@ -70,12 +91,16 @@ #define O2_MODE_C_DREQ_WP 0x02 #define O2_MODE_C_DREQ_BVD2 0x03 #define O2_MODE_C_ZVIDEO 0x08 +#define O2_MODE_C_IREQ_SEL 0x30 +#define O2_MODE_C_MGMT_SEL 0xc0 #define O2_MODE_D 0x3b #define O2_MODE_D_IRQ_MODE 0x03 +#define O2_MODE_D_PCI_CLKRUN 0x04 +#define O2_MODE_D_CB_CLKRUN 0x08 #define O2_MODE_D_SKT_ACTV 0x20 #define O2_MODE_D_PCI_FIFO 0x40 /* for OZ6729, OZ6730 */ -#define O2_MODE_D_W97_IRQ 0x40 /* for OZ6832 */ +#define O2_MODE_D_W97_IRQ 0x40 #define O2_MODE_D_ISA_IRQ 0x80 #define O2_MHPG_DMA 0x3c diff --git a/drivers/pcmcia/rsrc_mgr.c b/drivers/pcmcia/rsrc_mgr.c index 52813cb5a..fa6eba768 100644 --- a/drivers/pcmcia/rsrc_mgr.c +++ b/drivers/pcmcia/rsrc_mgr.c @@ -2,7 +2,7 @@ Resource management routines - rsrc_mgr.c 1.71 1999/09/15 15:32:19 + rsrc_mgr.c 1.73 1999/10/19 00:54:04 The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file @@ -98,124 +98,15 @@ static irq_info_t irq_table[NR_IRQS] = { { 0, 0, 0 }, /* etc */ }; #endif -static spinlock_t rsrc_lock = SPIN_LOCK_UNLOCKED; - /*====================================================================== Linux resource management extensions ======================================================================*/ -typedef struct resource_entry_t { - u_long base, num; - char *name; - struct resource_entry_t *next; -} resource_entry_t; - -/* Ordered linked lists of allocated IO and memory blocks */ -static resource_entry_t io_list = { 0, 0, NULL, NULL }; - -static resource_entry_t *find_gap(resource_entry_t *root, - resource_entry_t *entry) -{ - resource_entry_t *p; - - if (entry->base > entry->base+entry->num-1) - return NULL; - for (p = root; ; p = p->next) { - if ((p != root) && (p->base+p->num-1 >= entry->base)) { - p = NULL; - break; - } - if ((p->next == NULL) || - (p->next->base > entry->base+entry->num-1)) - break; - } - return p; -} - -static int register_my_resource(resource_entry_t *list, - u_long base, u_long num, char *name) -{ - u_long flags; - resource_entry_t *p, *entry; - - entry = kmalloc(sizeof(resource_entry_t), GFP_ATOMIC); - entry->base = base; - entry->num = num; - entry->name = name; - - spin_lock_irqsave(&rsrc_lock, flags); - p = find_gap(list, entry); - if (p == NULL) { - spin_unlock_irqrestore(&rsrc_lock, flags); - kfree(entry); - return -EBUSY; - } - entry->next = p->next; - p->next = entry; - spin_unlock_irqrestore(&rsrc_lock, flags); - return 0; -} - -static void release_my_resource(resource_entry_t *list, - u_long base, u_long num) -{ - u_long flags; - resource_entry_t *p, *q; - - spin_lock_irqsave(&rsrc_lock, flags); - for (p = list; ; p = q) { - q = p->next; - if (q == NULL) break; - if ((q->base == base) && (q->num == num)) { - p->next = q->next; - kfree(q); - spin_unlock_irqrestore(&rsrc_lock, flags); - return; - } - } - spin_unlock_irqrestore(&rsrc_lock, flags); - return; -} - -static int check_my_resource(resource_entry_t *list, - u_long base, u_long num) -{ - if (register_my_resource(list, base, num, NULL) != 0) - return -EBUSY; - release_my_resource(list, base, num); - return 0; -} +static spinlock_t rsrc_lock = SPIN_LOCK_UNLOCKED; -int check_io_region(u_long base, u_long num) -{ - return check_my_resource(&io_list, base, num); -} -void request_io_region(u_long base, u_long num, char *name) -{ - register_my_resource(&io_list, base, num, name); -} -void release_io_region(u_long base, u_long num) -{ - release_my_resource(&io_list, base, num); -} -#ifdef CONFIG_PROC_FS -int proc_read_io(char *buf, char **start, off_t pos, - int count, int *eof, void *data) -{ - resource_entry_t *r; - u_long flags; - char *p = buf; - - spin_lock_irqsave(&rsrc_lock, flags); - for (r = io_list.next; r; r = r->next) - p += sprintf(p, "%04lx-%04lx : %s\n", r->base, - r->base+r->num-1, r->name); - spin_unlock_irqrestore(&rsrc_lock, flags); - return (p - buf); -} -#endif +#define check_io_region(b,n) (0) /*====================================================================== @@ -773,7 +664,6 @@ int adjust_resource_info(client_handle_t handle, adjust_t *adj) void release_resource_db(void) { resource_map_t *p, *q; - resource_entry_t *u, *v; for (p = mem_db.next; p != &mem_db; p = q) { q = p->next; @@ -783,8 +673,4 @@ void release_resource_db(void) q = p->next; kfree(p); } - for (u = io_list.next; u; u = v) { - v = u->next; - kfree(u); - } } diff --git a/drivers/pcmcia/rsrc_mgr.h b/drivers/pcmcia/rsrc_mgr.h index 3b7f12e09..37faa3b26 100644 --- a/drivers/pcmcia/rsrc_mgr.h +++ b/drivers/pcmcia/rsrc_mgr.h @@ -30,10 +30,4 @@ #ifndef _RSRC_MGR_H #define _RSRC_MGR_H -#ifdef __BEOS__ -int check_resource(int type, u_long base, u_long num); -int register_resource(int type, u_long base, u_long num); -int release_resource(int type, u_long base, u_long num); -#endif - #endif /* _RSRC_MGR_H */ diff --git a/drivers/scsi/atp870u.c b/drivers/scsi/atp870u.c index 83215d115..be2db8091 100644 --- a/drivers/scsi/atp870u.c +++ b/drivers/scsi/atp870u.c @@ -1,7 +1,7 @@ /* $Id: atp870u.c,v 1.0 1997/05/07 15:22:00 root Exp root $ * linux/kernel/atp870u.c * - * Copyright (C) 1997 Wu Ching Chen + * Copyright (C) 1997 Wu Ching Chen * 2.1.x update (C) 1998 Krzysztof G. Baranowski * * Marcelo Tosatti <marcelo@conectiva.com.br> : SMP fixes @@ -30,745 +30,620 @@ #include<linux/stat.h> -struct proc_dir_entry proc_scsi_atp870u = { - PROC_SCSI_ATP870U, 7, "atp870u", - S_IFDIR | S_IRUGO | S_IXUGO, 2 +struct proc_dir_entry proc_scsi_atp870u = +{ + PROC_SCSI_ATP870U, 7, "atp870u", + S_IFDIR | S_IRUGO | S_IXUGO, 2 }; void mydlyu(unsigned int); + /* -static const char RCSid[] = "$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/atp870u.c,v 1.0 1997/05/07 15:22:00 root Exp root $"; -*/ - -static unsigned char admaxu=1,host_idu[2],chip_veru[2],scam_on[2],global_map[2]; -static unsigned short int active_idu[2],wide_idu[2],sync_idu,ultra_map[2]; -static int workingu[2]={0,0}; -static Scsi_Cmnd *querequ[2][qcnt],*curr_req[2][16]; -static unsigned char devspu[2][16] = {{0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20}, - {0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20}}; -static unsigned char dirctu[2][16],last_cmd[2],in_snd[2],in_int[2]; + * static const char RCSid[] = "$Header$"; + */ + +static unsigned char admaxu = 1, host_idu[2], chip_veru[2], scam_on[2], global_map[2]; +static unsigned short int active_idu[2], wide_idu[2], sync_idu, ultra_map[2]; +static int workingu[2] = {0, 0}; + +static Scsi_Cmnd *querequ[2][qcnt], *curr_req[2][16]; + +static unsigned char devspu[2][16] = { + {0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20}, + {0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20} +}; + +static unsigned char dirctu[2][16], last_cmd[2], in_snd[2], in_int[2]; static unsigned char ata_cdbu[2][16]; -static unsigned int ioportu[2]={0,0}; -static unsigned int irqnumu[2]={0,0}; +static unsigned int ioportu[2] = {0, 0}; +static unsigned int irqnumu[2] = {0, 0}; static unsigned short int pciportu[2]; -static unsigned long prdaddru[2][16],tran_lenu[2][16],last_lenu[2][16]; +static unsigned long prdaddru[2][16], tran_lenu[2][16], last_lenu[2][16]; static unsigned char prd_tableu[2][16][1024]; static unsigned char *prd_posu[2][16]; -static unsigned char quhdu[2],quendu[2]; -static unsigned char devtypeu[2][16] = {{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, - { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}; -static struct Scsi_Host * atp_host[2]={NULL,NULL}; +static unsigned char quhdu[2], quendu[2]; + +static unsigned char devtypeu[2][16] = +{ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} +}; + +static struct Scsi_Host *atp_host[2] = {NULL, NULL}; static void atp870u_intr_handle(int irq, void *dev_id, struct pt_regs *regs) { - unsigned long flags; - unsigned short int tmpcip,id; - unsigned char i,j,h,tarid,lun; - unsigned char *prd; - Scsi_Cmnd *workrequ; - unsigned int workportu,tmport; - unsigned long adrcntu,k; - int errstus; - - for ( h=0; h < 2; h++ ) - { - if ( ( irq & 0x0f ) == irqnumu[h] ) - { - goto irq_numok; + unsigned long flags; + unsigned short int tmpcip, id; + unsigned char i, j, h, tarid, lun; + unsigned char *prd; + Scsi_Cmnd *workrequ; + unsigned int workportu, tmport; + unsigned long adrcntu, k; + int errstus; + + for (h = 0; h < 2; h++) { + if (irq == irqnumu[h]) { + goto irq_numok; + } } - } - return; + return; irq_numok: - in_int[h]=1; - workportu=ioportu[h]; - tmport=workportu; - - if ( workingu[h] != 0 ) - { - tmport += 0x1f; - j=inb(tmport); - tmpcip=pciportu[h]; - if ((inb(tmpcip) & 0x08) != 0) - { - tmpcip += 0x2; - while((inb(tmpcip) & 0x08) != 0); - } - tmpcip=pciportu[h]; - outb(0x00,tmpcip); - tmport -=0x08; - i=inb(tmport); - if ((j & 0x40) == 0) - { - if ((last_cmd[h] & 0x40) == 0) - { - last_cmd[h]=0xff; - } - } - else - { - last_cmd[h] |= 0x40; - } - tmport -= 0x02; - tarid=inb(tmport); - tmport += 0x02; - if ((tarid & 0x40) != 0) - { - tarid=(tarid & 0x07) | 0x08; - } - else - { - tarid &= 0x07; - } - if ( i == 0x85 ) - { - if (wide_idu[h] != 0) - { - tmport=workportu+0x1b; - j=inb(tmport) & 0x0e; - j |= 0x01; - outb(j,tmport); - } - if (((quhdu[h] != quendu[h]) || (last_cmd[h] != 0xff)) && - (in_snd[h] == 0)) - { - send_s870(h); - } - in_int[h]=0; - return; - } - if ( i == 0x21 ) - { - tmport -= 0x05; - adrcntu=0; - ((unsigned char *)&adrcntu)[2]=inb(tmport++); - ((unsigned char *)&adrcntu)[1]=inb(tmport++); - ((unsigned char *)&adrcntu)[0]=inb(tmport); - k=last_lenu[h][tarid]; - k -= adrcntu; - tran_lenu[h][tarid]= k; - last_lenu[h][tarid]=adrcntu; - tmport -= 0x04; - outb(0x41,tmport); - tmport += 0x08; - outb(0x08,tmport); - in_int[h]=0; - return ; - } - - if ((i == 0x80) || (i == 0x8f)) - { - lun=0; - tmport -= 0x07; - j=inb(tmport); - if ( j == 0x44 ) - { - tmport += 0x0d; - lun=inb(tmport) & 0x07; - } - else - { - if ( j == 0x41 ) - { + in_int[h] = 1; + workportu = ioportu[h]; + tmport = workportu; + + if (workingu[h] != 0) + { + tmport += 0x1f; + j = inb(tmport); + + tmpcip = pciportu[h]; + if ((inb(tmpcip) & 0x08) != 0) + { + tmpcip += 0x2; + while ((inb(tmpcip) & 0x08) != 0); + } + tmpcip = pciportu[h]; + outb(0x00, tmpcip); + tmport -= 0x08; + + i = inb(tmport); + if ((j & 0x40) == 0) + { + if ((last_cmd[h] & 0x40) == 0) + { + last_cmd[h] = 0xff; + } + } + else last_cmd[h] |= 0x40; + + tmport -= 0x02; + tarid = inb(tmport); tmport += 0x02; - adrcntu=0; - ((unsigned char *)&adrcntu)[2]=inb(tmport++); - ((unsigned char *)&adrcntu)[1]=inb(tmport++); - ((unsigned char *)&adrcntu)[0]=inb(tmport); - k=last_lenu[h][tarid]; - k -= adrcntu; - tran_lenu[h][tarid]= k; - last_lenu[h][tarid]=adrcntu; - tmport += 0x04; - outb(0x08,tmport); - in_int[h]=0; - return ; - } - else - { - outb(0x46,tmport); - dirctu[h][tarid]=0x00; + + if ((tarid & 0x40) != 0) { + tarid = (tarid & 0x07) | 0x08; + } else { + tarid &= 0x07; + } + if (i == 0x85) + { + if (wide_idu[h] != 0) + { + tmport = workportu + 0x1b; + j = inb(tmport) & 0x0e; + j |= 0x01; + outb(j, tmport); + } + if (((quhdu[h] != quendu[h]) || (last_cmd[h] != 0xff)) && + (in_snd[h] == 0)) + { + send_s870(h); + } + in_int[h] = 0; + return; + } + if (i == 0x21) + { + tmport -= 0x05; + adrcntu = 0; + ((unsigned char *) &adrcntu)[2] = inb(tmport++); + ((unsigned char *) &adrcntu)[1] = inb(tmport++); + ((unsigned char *) &adrcntu)[0] = inb(tmport); + k = last_lenu[h][tarid]; + k -= adrcntu; + tran_lenu[h][tarid] = k; + last_lenu[h][tarid] = adrcntu; + tmport -= 0x04; + outb(0x41, tmport); + tmport += 0x08; + outb(0x08, tmport); + in_int[h] = 0; + return; + } + if ((i == 0x80) || (i == 0x8f)) + { + lun = 0; + tmport -= 0x07; + j = inb(tmport); + if (j == 0x44) { + tmport += 0x0d; + lun = inb(tmport) & 0x07; + } else { + if (j == 0x41) + { + tmport += 0x02; + adrcntu = 0; + ((unsigned char *) &adrcntu)[2] = inb(tmport++); + ((unsigned char *) &adrcntu)[1] = inb(tmport++); + ((unsigned char *) &adrcntu)[0] = inb(tmport); + k = last_lenu[h][tarid]; + k -= adrcntu; + tran_lenu[h][tarid] = k; + last_lenu[h][tarid] = adrcntu; + tmport += 0x04; + outb(0x08, tmport); + in_int[h] = 0; + return; + } + else + { + outb(0x46, tmport); + dirctu[h][tarid] = 0x00; + tmport += 0x02; + outb(0x00, tmport++); + outb(0x00, tmport++); + outb(0x00, tmport++); + tmport += 0x03; + outb(0x08, tmport); + in_int[h] = 0; + return; + } + } + tmport = workportu + 0x10; + outb(0x45, tmport); + tmport += 0x06; + tarid = inb(tmport); + if ((tarid & 0x10) != 0) + { + tarid = (tarid & 0x07) | 0x08; + } else { + tarid &= 0x07; + } + workrequ = curr_req[h][tarid]; + tmport = workportu + 0x0f; + outb(lun, tmport); + tmport += 0x02; + outb(devspu[h][tarid], tmport++); + adrcntu = tran_lenu[h][tarid]; + k = last_lenu[h][tarid]; + outb(((unsigned char *) &k)[2], tmport++); + outb(((unsigned char *) &k)[1], tmport++); + outb(((unsigned char *) &k)[0], tmport++); + j = tarid; + if (tarid > 7) { + j = (j & 0x07) | 0x40; + } + j |= dirctu[h][tarid]; + outb(j, tmport++); + outb(0x80, tmport); + tmport = workportu + 0x1b; + j = inb(tmport) & 0x0e; + id = 1; + id = id << tarid; + if ((id & wide_idu[h]) != 0) { + j |= 0x01; + } + outb(j, tmport); + if (last_lenu[h][tarid] == 0) { + tmport = workportu + 0x18; + outb(0x08, tmport); + in_int[h] = 0; + return; + } + prd = prd_posu[h][tarid]; + while (adrcntu != 0) + { + id = ((unsigned short int *) (prd))[2]; + if (id == 0) { + k = 0x10000; + } else { + k = id; + } + if (k > adrcntu) { + ((unsigned short int *) (prd))[2] = (unsigned short int) + (k - adrcntu); + ((unsigned long *) (prd))[0] += adrcntu; + adrcntu = 0; + prd_posu[h][tarid] = prd; + } else { + adrcntu -= k; + prdaddru[h][tarid] += 0x08; + prd += 0x08; + if (adrcntu == 0) { + prd_posu[h][tarid] = prd; + } + } + } + tmpcip = pciportu[h] + 0x04; + outl(prdaddru[h][tarid], tmpcip); + tmpcip -= 0x02; + outb(0x06, tmpcip); + outb(0x00, tmpcip); + tmpcip -= 0x02; + tmport = workportu + 0x18; + if (dirctu[h][tarid] != 0) { + outb(0x08, tmport); + outb(0x01, tmpcip); + in_int[h] = 0; + return; + } + outb(0x08, tmport); + outb(0x09, tmpcip); + in_int[h] = 0; + return; + } + workrequ = curr_req[h][tarid]; + if (i == 0x42) { + errstus = 0x02; + workrequ->result = errstus; + goto go_42; + } + if (i == 0x16) + { + errstus = 0; + tmport -= 0x08; + errstus = inb(tmport); + workrequ->result = errstus; +go_42: + spin_lock_irqsave(&io_request_lock, flags); + (*workrequ->scsi_done) (workrequ); + spin_unlock_irqrestore(&io_request_lock, flags); + + curr_req[h][tarid] = 0; + workingu[h]--; + if (wide_idu[h] != 0) { + tmport = workportu + 0x1b; + j = inb(tmport) & 0x0e; + j |= 0x01; + outb(j, tmport); + } + if (((last_cmd[h] != 0xff) || (quhdu[h] != quendu[h])) && + (in_snd[h] == 0)) + { + send_s870(h); + } + in_int[h] = 0; + return; + } + if (i == 0x4f) { + i = 0x89; + } + i &= 0x0f; + if (i == 0x09) { + tmpcip = tmpcip + 4; + outl(prdaddru[h][tarid], tmpcip); + tmpcip = tmpcip - 2; + outb(0x06, tmpcip); + outb(0x00, tmpcip); + tmpcip = tmpcip - 2; + tmport = workportu + 0x10; + outb(0x41, tmport); + dirctu[h][tarid] = 0x00; + tmport += 0x08; + outb(0x08, tmport); + outb(0x09, tmpcip); + in_int[h] = 0; + return; + } + if (i == 0x08) { + tmpcip = tmpcip + 4; + outl(prdaddru[h][tarid], tmpcip); + tmpcip = tmpcip - 2; + outb(0x06, tmpcip); + outb(0x00, tmpcip); + tmpcip = tmpcip - 2; + tmport = workportu + 0x10; + outb(0x41, tmport); + tmport += 0x05; + outb((unsigned char) (inb(tmport) | 0x20), tmport); + dirctu[h][tarid] = 0x20; + tmport += 0x03; + outb(0x08, tmport); + outb(0x01, tmpcip); + in_int[h] = 0; + return; + } + tmport -= 0x07; + if (i == 0x0a) { + outb(0x30, tmport); + } else { + outb(0x46, tmport); + } + dirctu[h][tarid] = 0x00; tmport += 0x02; - outb(0x00,tmport++); - outb(0x00,tmport++); - outb(0x00,tmport++); - tmport+=0x03; - outb(0x08,tmport); - in_int[h]=0; + outb(0x00, tmport++); + outb(0x00, tmport++); + outb(0x00, tmport++); + tmport += 0x03; + outb(0x08, tmport); + in_int[h] = 0; return; - } - } - tmport=workportu + 0x10; - outb(0x45,tmport); - tmport += 0x06; - tarid=inb(tmport); - if ((tarid & 0x10) != 0) - { - tarid=(tarid & 0x07) | 0x08; - } - else - { - tarid &= 0x07; - } - workrequ=curr_req[h][tarid]; - tmport=workportu + 0x0f; - outb(lun,tmport); - tmport += 0x02; - outb(devspu[h][tarid],tmport++); - adrcntu=tran_lenu[h][tarid]; - k=last_lenu[h][tarid]; - outb(((unsigned char *)&k)[2],tmport++); - outb(((unsigned char *)&k)[1],tmport++); - outb(((unsigned char *)&k)[0],tmport++); - j=tarid; - if ( tarid > 7 ) - { - j = (j & 0x07) | 0x40; - } - j |= dirctu[h][tarid]; - outb(j,tmport++); - outb(0x80,tmport); - tmport=workportu + 0x1b; - j=inb(tmport) & 0x0e; - id=1; - id=id << tarid; - if ((id & wide_idu[h]) != 0) - { - j |= 0x01; - } - outb(j,tmport); - if ( last_lenu[h][tarid] == 0 ) - { - tmport=workportu + 0x18; - outb(0x08,tmport); - in_int[h]=0; - return ; - } - prd=prd_posu[h][tarid]; - while ( adrcntu != 0 ) - { - id=((unsigned short int *)(prd))[2]; - if ( id == 0 ) - { - k=0x10000; - } - else - { - k=id; - } - if ( k > adrcntu ) - { - ((unsigned short int *)(prd))[2] =(unsigned short int) - (k - adrcntu); - ((unsigned long *)(prd))[0] += adrcntu; - adrcntu=0; - prd_posu[h][tarid]=prd; - } - else - { - adrcntu -= k; - prdaddru[h][tarid] += 0x08; - prd += 0x08; - if ( adrcntu == 0 ) - { - prd_posu[h][tarid]=prd; - } - } - } - tmpcip=pciportu[h] + 0x04; - outl(prdaddru[h][tarid],tmpcip); - tmpcip -= 0x02; - outb(0x06,tmpcip); - outb(0x00,tmpcip); - tmpcip -= 0x02; - tmport=workportu + 0x18; - if ( dirctu[h][tarid] != 0 ) - { - outb(0x08,tmport); - outb(0x01,tmpcip); - in_int[h]=0; - return; - } - outb(0x08,tmport); - outb(0x09,tmpcip); - in_int[h]=0; - return; - } - - workrequ=curr_req[h][tarid]; - if ( i == 0x42 ) - { - errstus=0x02; - workrequ->result=errstus; - goto go_42; - } - if ( i == 0x16 ) - { - errstus=0; - tmport -= 0x08; - errstus=inb(tmport); - workrequ->result=errstus; -/* if ( errstus == 0x02 ) - { - tmport +=0x10; - if ((inb(tmport) & 0x80) != 0) - { - printk(" autosense "); - } - tmport -=0x09; - outb(0,tmport); - tmport=workportu+0x3a; - outb((unsigned char)(inb(tmport) | 0x10),tmport); - tmport -= 0x39; - - outb(0x08,tmport++); - outb(0x7f,tmport++); - outb(0x03,tmport++); - outb(0x00,tmport++); - outb(0x00,tmport++); - outb(0x00,tmport++); - outb(0x0e,tmport++); - outb(0x00,tmport); - tmport+=0x07; - outb(0x00,tmport++); - tmport++; - outb(devspu[h][workrequ->target],tmport++); - outb(0x00,tmport++); - outb(0x00,tmport++); - outb(0x0e,tmport++); - tmport+=0x03; - outb(0x09,tmport); - tmport+=0x07; - i=0; - adrcntu=(unsigned long)(&workrequ->sense_buffer[0]); -get_sens: - j=inb(tmport); - if ((j & 0x01) != 0) - { - tmport-=0x06; - (unsigned char)(((caddr_t) adrcntu)[i++])=inb(tmport); - tmport+=0x06; - goto get_sens; - } - if ((j & 0x80) == 0) - { - goto get_sens; - } - if ((j & 0x40) == 0) - { - tmport-=0x08; - i=inb(tmport); - } - tmport=workportu+0x3a; - outb((unsigned char)(inb(tmport) & 0xef),tmport); - tmport=workportu+0x01; - outb(0x2c,tmport); - tmport += 0x15; - outb(0x80,tmport); - } */ -go_42: - spin_lock_irqsave(&io_request_lock, flags); - (*workrequ->scsi_done)(workrequ); - spin_unlock_irqrestore(&io_request_lock, flags); - - curr_req[h][tarid]=0; - workingu[h]--; - if (wide_idu[h] != 0) - { - tmport=workportu+0x1b; - j=inb(tmport) & 0x0e; - j |= 0x01; - outb(j,tmport); - } - if (((last_cmd[h] != 0xff) || (quhdu[h] != quendu[h])) && - (in_snd[h] == 0)) - { - send_s870(h); - } - in_int[h]=0; - return; - } - if ( i == 0x4f ) - { - i=0x89; - } - i &= 0x0f; - if ( i == 0x09 ) - { - tmpcip=tmpcip+4; - outl(prdaddru[h][tarid],tmpcip); - tmpcip=tmpcip-2; - outb(0x06,tmpcip); - outb(0x00,tmpcip); - tmpcip=tmpcip-2; - tmport=workportu+0x10; - outb(0x41,tmport); - dirctu[h][tarid]=0x00; - tmport += 0x08; - outb(0x08,tmport); - outb(0x09,tmpcip); - in_int[h]=0; - return; - } - if ( i == 0x08 ) - { - tmpcip=tmpcip+4; - outl(prdaddru[h][tarid],tmpcip); - tmpcip=tmpcip-2; - outb(0x06,tmpcip); - outb(0x00,tmpcip); - tmpcip=tmpcip-2; - tmport=workportu+0x10; - outb(0x41,tmport); - tmport += 0x05; - outb((unsigned char)(inb(tmport) | 0x20),tmport); - dirctu[h][tarid]=0x20; - tmport += 0x03; - outb(0x08,tmport); - outb(0x01,tmpcip); - in_int[h]=0; - return; - } - tmport -= 0x07; - if ( i == 0x0a ) - { - outb(0x30,tmport); - } - else - { - outb(0x46,tmport); - } - dirctu[h][tarid]=0x00; - tmport += 0x02; - outb(0x00,tmport++); - outb(0x00,tmport++); - outb(0x00,tmport++); - tmport+=0x03; - outb(0x08,tmport); - in_int[h]=0; - return; - } - else - { - tmport=workportu+0x17; - inb(tmport); - workingu[h]=0; - in_int[h]=0; - return; - } + } else { + tmport = workportu + 0x17; + inb(tmport); + workingu[h] = 0; + in_int[h] = 0; + return; + } } -int atp870u_queuecommand(Scsi_Cmnd * req_p, void (*done)(Scsi_Cmnd *)) +int atp870u_queuecommand(Scsi_Cmnd * req_p, void (*done) (Scsi_Cmnd *)) { - unsigned char i,h; - unsigned long flags; - unsigned short int m; - unsigned int tmport; - - for( h=0; h <= admaxu; h++ ) - { - if ( req_p->host == atp_host[h] ) - { - goto host_ok; - } - } - return 0; + unsigned char i, h; + unsigned long flags; + unsigned short int m; + unsigned int tmport; + + for (h = 0; h <= admaxu; h++) { + if (req_p->host == atp_host[h]) { + goto host_ok; + } + } + return 0; host_ok: - if ( req_p->channel != 0 ) - { - req_p->result = 0x00040000; - done(req_p); - return 0; - } - m=1; - m= m << req_p->target; - if ( ( m & active_idu[h] ) == 0 ) - { - req_p->result = 0x00040000; - done(req_p); - return 0; - } - if (done) - { - req_p->scsi_done = done; - } - else - { - printk("atp870u_queuecommand: done can't be NULL\n"); - req_p->result = 0; - done(req_p); - return 0; - } - quendu[h]++; - if ( quendu[h] >= qcnt ) - { - quendu[h]=0; - } - wait_que_empty: - if ( quhdu[h] == quendu[h] ) - { - goto wait_que_empty; - } - save_flags(flags); - cli(); - querequ[h][quendu[h]]=req_p; - if ( quendu[h] == 0 ) - { - i=qcnt-1; - } - else - { - i=quendu[h]-1; - } - tmport = ioportu[h]+0x1c; - restore_flags(flags); - if ((inb(tmport) == 0) && (in_int[h] == 0) && (in_snd[h] == 0)) - { - send_s870(h); - } - return 0; + if (req_p->channel != 0) { + req_p->result = 0x00040000; + done(req_p); + return 0; + } + m = 1; + m = m << req_p->target; + if ((m & active_idu[h]) == 0) { + req_p->result = 0x00040000; + done(req_p); + return 0; + } + if (done) { + req_p->scsi_done = done; + } else { + printk("atp870u_queuecommand: done can't be NULL\n"); + req_p->result = 0; + done(req_p); + return 0; + } + quendu[h]++; + if (quendu[h] >= qcnt) { + quendu[h] = 0; + } +wait_que_empty: + if (quhdu[h] == quendu[h]) { + goto wait_que_empty; + } + save_flags(flags); + cli(); + querequ[h][quendu[h]] = req_p; + if (quendu[h] == 0) { + i = qcnt - 1; + } else { + i = quendu[h] - 1; + } + tmport = ioportu[h] + 0x1c; + restore_flags(flags); + if ((inb(tmport) == 0) && (in_int[h] == 0) && (in_snd[h] == 0)) { + send_s870(h); + } + return 0; } -void mydlyu(unsigned int dlycnt ) +void mydlyu(unsigned int dlycnt) { - unsigned int i ; - for ( i = 0 ; i < dlycnt ; i++ ) - { - inb(0x80); - } + unsigned int i; + for (i = 0; i < dlycnt; i++) { + inb(0x80); + } } void send_s870(unsigned char h) { - unsigned int tmport; - Scsi_Cmnd *workrequ; - unsigned long flags; - unsigned int i; - unsigned char j,tarid; - unsigned char *prd; - unsigned short int tmpcip,w; - unsigned long l,bttl; - unsigned int workportu; - struct scatterlist * sgpnt; + unsigned int tmport; + Scsi_Cmnd *workrequ; + unsigned long flags; + unsigned int i; + unsigned char j, tarid; + unsigned char *prd; + unsigned short int tmpcip, w; + unsigned long l, bttl; + unsigned int workportu; + struct scatterlist *sgpnt; save_flags(flags); cli(); - if ( in_snd[h] != 0 ) - { - restore_flags(flags); - return; + if (in_snd[h] != 0) { + restore_flags(flags); + return; } - in_snd[h]=1; - if ((last_cmd[h] != 0xff) && ((last_cmd[h] & 0x40) != 0)) - { - last_cmd[h] &= 0x0f; - workrequ=curr_req[h][last_cmd[h]]; - goto cmd_subp; + in_snd[h] = 1; + if ((last_cmd[h] != 0xff) && ((last_cmd[h] & 0x40) != 0)) { + last_cmd[h] &= 0x0f; + workrequ = curr_req[h][last_cmd[h]]; + goto cmd_subp; } workingu[h]++; - j=quhdu[h]; + j = quhdu[h]; quhdu[h]++; - if ( quhdu[h] >= qcnt ) - { - quhdu[h]=0; + if (quhdu[h] >= qcnt) { + quhdu[h] = 0; } - workrequ=querequ[h][quhdu[h]]; - if ( curr_req[h][workrequ->target] == 0 ) - { - curr_req[h][workrequ->target]=workrequ; - last_cmd[h]=workrequ->target; - goto cmd_subp; + workrequ = querequ[h][quhdu[h]]; + if (curr_req[h][workrequ->target] == 0) { + curr_req[h][workrequ->target] = workrequ; + last_cmd[h] = workrequ->target; + goto cmd_subp; } - quhdu[h]=j; + quhdu[h] = j; workingu[h]--; - in_snd[h]=0; + in_snd[h] = 0; restore_flags(flags); - return ; + return; cmd_subp: - workportu=ioportu[h]; - tmport=workportu+0x1f; - if ((inb(tmport) & 0xb0) != 0) - { - goto abortsnd; - } - tmport=workportu+0x1c; - if ( inb(tmport) == 0 ) - { - goto oktosend; - } + workportu = ioportu[h]; + tmport = workportu + 0x1f; + if ((inb(tmport) & 0xb0) != 0) { + goto abortsnd; + } + tmport = workportu + 0x1c; + if (inb(tmport) == 0) { + goto oktosend; + } abortsnd: - last_cmd[h] |= 0x40; - in_snd[h]=0; - restore_flags(flags); - return; -oktosend: - memcpy(&ata_cdbu[h][0], &workrequ->cmnd[0], workrequ->cmd_len); - if ( ata_cdbu[h][0] == 0x25 ) - { - if ( workrequ->request_bufflen > 8 ) - { - workrequ->request_bufflen=0x08; - } - } - if ( ata_cdbu[h][0] == 0x12 ) - { - if ( workrequ->request_bufflen > 0x24 ) - { - workrequ->request_bufflen = 0x24; - ata_cdbu[h][4]=0x24; - } - } - - tmport=workportu+0x1b; - j=inb(tmport) & 0x0e; - tarid=workrequ->target; - w=1; - w = w << tarid; - if ((w & wide_idu[h]) != 0) - { - j |= 0x01; - } - outb(j,tmport); - tmport=workportu; - outb(workrequ->cmd_len,tmport++); - outb(0x2c,tmport++); - outb(0xcf,tmport++); - for ( i=0 ; i < workrequ->cmd_len ; i++ ) - { - outb(ata_cdbu[h][i],tmport++); - } - tmport=workportu+0x0f; - outb(0x00,tmport); - tmport+=0x02; - outb(devspu[h][tarid],tmport++); - if (workrequ->use_sg) - { - - l=0; - sgpnt = (struct scatterlist *) workrequ->request_buffer; - for(i=0; i<workrequ->use_sg; i++) - { - if(sgpnt[i].length == 0 || workrequ->use_sg > ATP870U_SCATTER) - { - panic("Foooooooood fight!"); - } - l += sgpnt[i].length; - } - } - else - { - l=workrequ->request_bufflen; - } - outb((unsigned char)(((unsigned char *)(&l))[2]),tmport++); - outb((unsigned char)(((unsigned char *)(&l))[1]),tmport++); - outb((unsigned char)(((unsigned char *)(&l))[0]),tmport++); - j=tarid; - last_lenu[h][j]=l; - tran_lenu[h][j]=0; - if ((j & 0x08) != 0) - { - j=(j & 0x07) | 0x40; - } - if ((ata_cdbu[h][0] == 0x0a) || (ata_cdbu[h][0] == 0x2a) || - (ata_cdbu[h][0] == 0xaa) || (ata_cdbu[h][0] == 0x15)) - { - outb((unsigned char)(j | 0x20),tmport++); - } - else - { - outb(j,tmport++); - } - outb(0x80,tmport); - tmport=workportu + 0x1c; - dirctu[h][tarid]=0; - if ( l == 0 ) - { - if ( inb(tmport) == 0 ) - { - tmport=workportu+0x18; - outb(0x08,tmport); - } - else - { last_cmd[h] |= 0x40; - } - in_snd[h]=0; - restore_flags(flags); - return; - } - tmpcip=pciportu[h]; - prd=&prd_tableu[h][tarid][0]; - prd_posu[h][tarid]=prd; - if (workrequ->use_sg) - { - sgpnt = (struct scatterlist *) workrequ->request_buffer; - i=0; - for(j=0; j<workrequ->use_sg; j++) - { - (unsigned long)(((unsigned long *)(prd))[i >> 1])=(unsigned long)sgpnt[j].address; - (unsigned short int)(((unsigned short int *)(prd))[i+2])=sgpnt[j].length; - (unsigned short int)(((unsigned short int *)(prd))[i+3])=0; - i +=0x04; - } - (unsigned short int)(((unsigned short int *)(prd))[i-1])=0x8000; - } - else - { - bttl=(unsigned long)workrequ->request_buffer; - l=workrequ->request_bufflen; - i=0; - while ( l > 0x10000 ) - { - (unsigned short int)(((unsigned short int *)(prd))[i+3])=0x0000; - (unsigned short int)(((unsigned short int *)(prd))[i+2])=0x0000; - (unsigned long)(((unsigned long *)(prd))[i >> 1])=bttl; - l -= 0x10000; - bttl += 0x10000; - i += 0x04; - } - (unsigned short int)(((unsigned short int *)(prd))[i+3])=0x8000; - (unsigned short int)(((unsigned short int *)(prd))[i+2])=l; - (unsigned long)(((unsigned long *)(prd))[i >> 1])=bttl; - } - tmpcip=tmpcip+4; - prdaddru[h][tarid]=(unsigned long)&prd_tableu[h][tarid][0]; - outl(prdaddru[h][tarid],tmpcip); - tmpcip=tmpcip-2; - outb(0x06,tmpcip); - outb(0x00,tmpcip); - tmpcip=tmpcip-2; - if ((ata_cdbu[h][0] == 0x0a) || (ata_cdbu[h][0] == 0x2a) || - (ata_cdbu[h][0] == 0xaa) || (ata_cdbu[h][0] == 0x15)) - { - dirctu[h][tarid]=0x20; - if ( inb(tmport) == 0 ) - { - tmport=workportu+0x18; - outb(0x08,tmport); - outb(0x01,tmpcip); - } - else - { - last_cmd[h] |= 0x40; - } - in_snd[h]=0; - restore_flags(flags); - return; - } - if ( inb(tmport) == 0 ) - { - tmport=workportu+0x18; - outb(0x08,tmport); - outb(0x09,tmpcip); - } - else - { - last_cmd[h] |= 0x40; - } - in_snd[h]=0; - restore_flags(flags); - return; + in_snd[h] = 0; + restore_flags(flags); + return; +oktosend: + memcpy(&ata_cdbu[h][0], &workrequ->cmnd[0], workrequ->cmd_len); + if (ata_cdbu[h][0] == 0x25) { + if (workrequ->request_bufflen > 8) { + workrequ->request_bufflen = 0x08; + } + } + if (ata_cdbu[h][0] == 0x12) { + if (workrequ->request_bufflen > 0x24) { + workrequ->request_bufflen = 0x24; + ata_cdbu[h][4] = 0x24; + } + } + tmport = workportu + 0x1b; + j = inb(tmport) & 0x0e; + tarid = workrequ->target; + w = 1; + w = w << tarid; + if ((w & wide_idu[h]) != 0) { + j |= 0x01; + } + outb(j, tmport); + tmport = workportu; + outb(workrequ->cmd_len, tmport++); + outb(0x2c, tmport++); + outb(0xcf, tmport++); + for (i = 0; i < workrequ->cmd_len; i++) { + outb(ata_cdbu[h][i], tmport++); + } + tmport = workportu + 0x0f; + outb(0x00, tmport); + tmport += 0x02; + outb(devspu[h][tarid], tmport++); + if (workrequ->use_sg) + { + l = 0; + sgpnt = (struct scatterlist *) workrequ->request_buffer; + for (i = 0; i < workrequ->use_sg; i++) + { + if (sgpnt[i].length == 0 || workrequ->use_sg > ATP870U_SCATTER) + { + panic("Foooooooood fight!"); + } + l += sgpnt[i].length; + } + } else { + l = workrequ->request_bufflen; + } + outb((unsigned char) (((unsigned char *) (&l))[2]), tmport++); + outb((unsigned char) (((unsigned char *) (&l))[1]), tmport++); + outb((unsigned char) (((unsigned char *) (&l))[0]), tmport++); + j = tarid; + last_lenu[h][j] = l; + tran_lenu[h][j] = 0; + if ((j & 0x08) != 0) { + j = (j & 0x07) | 0x40; + } + if ((ata_cdbu[h][0] == 0x0a) || (ata_cdbu[h][0] == 0x2a) || + (ata_cdbu[h][0] == 0xaa) || (ata_cdbu[h][0] == 0x15)) { + outb((unsigned char) (j | 0x20), tmport++); + } else { + outb(j, tmport++); + } + outb(0x80, tmport); + tmport = workportu + 0x1c; + dirctu[h][tarid] = 0; + if (l == 0) { + if (inb(tmport) == 0) { + tmport = workportu + 0x18; + outb(0x08, tmport); + } else { + last_cmd[h] |= 0x40; + } + in_snd[h] = 0; + restore_flags(flags); + return; + } + tmpcip = pciportu[h]; + prd = &prd_tableu[h][tarid][0]; + prd_posu[h][tarid] = prd; + if (workrequ->use_sg) + { + sgpnt = (struct scatterlist *) workrequ->request_buffer; + i = 0; + for (j = 0; j < workrequ->use_sg; j++) { + (unsigned long) (((unsigned long *) (prd))[i >> 1]) = virt_to_bus(sgpnt[j].address); + (unsigned short int) (((unsigned short int *) (prd))[i + 2]) = sgpnt[j].length; + (unsigned short int) (((unsigned short int *) (prd))[i + 3]) = 0; + i += 0x04; + } + (unsigned short int) (((unsigned short int *) (prd))[i - 1]) = 0x8000; + } else { + bttl = virt_to_bus(workrequ->request_buffer); + l = workrequ->request_bufflen; + i = 0; + while (l > 0x10000) { + (unsigned short int) (((unsigned short int *) (prd))[i + 3]) = 0x0000; + (unsigned short int) (((unsigned short int *) (prd))[i + 2]) = 0x0000; + (unsigned long) (((unsigned long *) (prd))[i >> 1]) = bttl; + l -= 0x10000; + bttl += 0x10000; + i += 0x04; + } + (unsigned short int) (((unsigned short int *) (prd))[i + 3]) = 0x8000; + (unsigned short int) (((unsigned short int *) (prd))[i + 2]) = l; + (unsigned long) (((unsigned long *) (prd))[i >> 1]) = bttl; + } + tmpcip = tmpcip + 4; + prdaddru[h][tarid] = virt_to_bus(&prd_tableu[h][tarid][0]); + outl(prdaddru[h][tarid], tmpcip); + tmpcip = tmpcip - 2; + outb(0x06, tmpcip); + outb(0x00, tmpcip); + tmpcip = tmpcip - 2; + if ((ata_cdbu[h][0] == 0x0a) || (ata_cdbu[h][0] == 0x2a) || + (ata_cdbu[h][0] == 0xaa) || (ata_cdbu[h][0] == 0x15)) + { + dirctu[h][tarid] = 0x20; + if (inb(tmport) == 0) { + tmport = workportu + 0x18; + outb(0x08, tmport); + outb(0x01, tmpcip); + } else { + last_cmd[h] |= 0x40; + } + in_snd[h] = 0; + restore_flags(flags); + return; + } + if (inb(tmport) == 0) + { + tmport = workportu + 0x18; + outb(0x08, tmport); + outb(0x09, tmpcip); + } else { + last_cmd[h] |= 0x40; + } + in_snd[h] = 0; + restore_flags(flags); + return; } @@ -780,1101 +655,964 @@ static void internal_done(Scsi_Cmnd * SCpnt) int atp870u_command(Scsi_Cmnd * SCpnt) { - atp870u_queuecommand(SCpnt, internal_done); + atp870u_queuecommand(SCpnt, internal_done); - SCpnt->SCp.Status = 0; - while (!SCpnt->SCp.Status) - barrier(); - return SCpnt->result; + SCpnt->SCp.Status = 0; + while (!SCpnt->SCp.Status) + barrier(); + return SCpnt->result; } -unsigned char fun_scam ( unsigned char host,unsigned short int * val ) +unsigned char fun_scam(unsigned char host, unsigned short int *val) { - unsigned int tmport ; - unsigned short int i,k; - unsigned char j; + unsigned int tmport; + unsigned short int i, k; + unsigned char j; - tmport = ioportu[host]+0x1c; - outw(*val,tmport); + tmport = ioportu[host] + 0x1c; + outw(*val, tmport); FUN_D7: - for ( i=0; i < 10; i++ ) /* stable >= bus settle delay(400 ns) */ - { - k=inw(tmport); - j= (unsigned char)(k >> 8); - if ((k & 0x8000) != 0) /* DB7 all release? */ - { - goto FUN_D7; + for (i = 0; i < 10; i++) { /* stable >= bus settle delay(400 ns) */ + k = inw(tmport); + j = (unsigned char) (k >> 8); + if ((k & 0x8000) != 0) { /* DB7 all release? */ + goto FUN_D7; + } } - } - *val |= 0x4000; /* assert DB6 */ - outw(*val,tmport); - *val &= 0xdfff; /* assert DB5 */ - outw(*val,tmport); + *val |= 0x4000; /* assert DB6 */ + outw(*val, tmport); + *val &= 0xdfff; /* assert DB5 */ + outw(*val, tmport); FUN_D5: - for ( i=0; i < 10; i++ ) /* stable >= bus settle delay(400 ns) */ - { - if ((inw(tmport) & 0x2000) != 0) /* DB5 all release? */ - { - goto FUN_D5; - } - } - *val |= 0x8000; /* no DB4-0, assert DB7 */ - *val &= 0xe0ff; - outw(*val,tmport); - *val &= 0xbfff; /* release DB6 */ - outw(*val,tmport); -FUN_D6: - for ( i=0; i < 10; i++ ) /* stable >= bus settle delay(400 ns) */ - { - if ((inw(tmport) & 0x4000) != 0) /* DB6 all release? */ - { - goto FUN_D6; - } - } - - return j; + for (i = 0; i < 10; i++) { /* stable >= bus settle delay(400 ns) */ + if ((inw(tmport) & 0x2000) != 0) { /* DB5 all release? */ + goto FUN_D5; + } + } + *val |= 0x8000; /* no DB4-0, assert DB7 */ + *val &= 0xe0ff; + outw(*val, tmport); + *val &= 0xbfff; /* release DB6 */ + outw(*val, tmport); + FUN_D6: + for (i = 0; i < 10; i++) { /* stable >= bus settle delay(400 ns) */ + if ((inw(tmport) & 0x4000) != 0) { /* DB6 all release? */ + goto FUN_D6; + } + } + + return j; } -void tscam( unsigned char host ) +void tscam(unsigned char host) { - unsigned int tmport ; - unsigned char i,j,k; - unsigned long n; - unsigned short int m,assignid_map,val; - unsigned char mbuf[33],quintet[2]; - static unsigned char g2q_tab[8]={ 0x38,0x31,0x32,0x2b,0x34,0x2d,0x2e,0x27 }; + unsigned int tmport; + unsigned char i, j, k; + unsigned long n; + unsigned short int m, assignid_map, val; + unsigned char mbuf[33], quintet[2]; + static unsigned char g2q_tab[8] = + {0x38, 0x31, 0x32, 0x2b, 0x34, 0x2d, 0x2e, 0x27}; - for ( i=0; i < 0x10; i++ ) - { - mydlyu(0xffff); - } - - tmport = ioportu[host]+1; - outb(0x08,tmport++); - outb(0x7f,tmport); - tmport = ioportu[host]+0x11; - outb(0x20,tmport); - - if ((scam_on[host] & 0x40) == 0) - { - return; - } - - m=1; - m <<= host_idu[host]; - j=16; - if ( chip_veru[host] < 4 ) - { - m |= 0xff00; - j=8; - } - assignid_map=m; - tmport = ioportu[host]+0x02; - outb(0x02,tmport++); /* 2*2=4ms,3EH 2/32*3E=3.9ms */ - outb(0,tmport++); - outb(0,tmport++); - outb(0,tmport++); - outb(0,tmport++); - outb(0,tmport++); - outb(0,tmport++); - - for ( i = 0 ; i < j ; i ++ ) - { - m=1; - m=m<<i; - if ( ( m & assignid_map ) != 0 ) - { - continue; - } - tmport = ioportu[host]+0x0f; - outb(0,tmport++); - tmport += 0x02; - outb(0,tmport++); - outb(0,tmport++); - outb(0,tmport++); - if ( i > 7 ) - { - k=(i & 0x07) | 0x40; - } - else - { - k=i; - } - outb(k,tmport++); - tmport = ioportu[host]+0x1b; - if ( chip_veru[host] == 4 ) - { - outb((unsigned char)((inb(tmport) & 0x0e) | 0x01),tmport); - } - else - { - outb((unsigned char)(inb(tmport) & 0x0e),tmport); - } + for (i = 0; i < 0x10; i++) { + mydlyu(0xffff); + } + + tmport = ioportu[host] + 1; + outb(0x08, tmport++); + outb(0x7f, tmport); + tmport = ioportu[host] + 0x11; + outb(0x20, tmport); + + if ((scam_on[host] & 0x40) == 0) { + return; + } + m = 1; + m <<= host_idu[host]; + j = 16; + if (chip_veru[host] < 4) { + m |= 0xff00; + j = 8; + } + assignid_map = m; + tmport = ioportu[host] + 0x02; + outb(0x02, tmport++); /* 2*2=4ms,3EH 2/32*3E=3.9ms */ + outb(0, tmport++); + outb(0, tmport++); + outb(0, tmport++); + outb(0, tmport++); + outb(0, tmport++); + outb(0, tmport++); + + for (i = 0; i < j; i++) { + m = 1; + m = m << i; + if ((m & assignid_map) != 0) { + continue; + } + tmport = ioportu[host] + 0x0f; + outb(0, tmport++); + tmport += 0x02; + outb(0, tmport++); + outb(0, tmport++); + outb(0, tmport++); + if (i > 7) { + k = (i & 0x07) | 0x40; + } else { + k = i; + } + outb(k, tmport++); + tmport = ioportu[host] + 0x1b; + if (chip_veru[host] == 4) { + outb((unsigned char) ((inb(tmport) & 0x0e) | 0x01), tmport); + } else { + outb((unsigned char) (inb(tmport) & 0x0e), tmport); + } wait_rdyok: - tmport = ioportu[host]+0x18; - outb(0x09,tmport); - tmport += 0x07; - - while ((inb(tmport) & 0x80) == 0x00); - tmport -= 0x08; - k=inb(tmport); - if ( k != 0x16 ) - { - if ((k == 0x85) || (k == 0x42)) - { - continue; - } - tmport = ioportu[host]+0x10; - outb(0x41,tmport); - goto wait_rdyok; - } - assignid_map |= m; - - } - tmport = ioportu[host]+0x02; - outb(0x7f,tmport); - tmport = ioportu[host]+0x1b; - outb(0x02,tmport); - - outb(0,0x80); - - val=0x0080; /* bsy */ - tmport = ioportu[host]+0x1c; - outw(val,tmport); - val |=0x0040; /* sel */ - outw(val,tmport); - val |=0x0004; /* msg */ - outw(val,tmport); - inb(0x80); /* 2 deskew delay(45ns*2=90ns) */ - val &=0x007f; /* no bsy */ - outw(val,tmport); - mydlyu(0xffff); /* recommanded SCAM selection response time */ - mydlyu(0xffff); - val &=0x00fb; /* after 1ms no msg */ - outw(val,tmport); + tmport = ioportu[host] + 0x18; + outb(0x09, tmport); + tmport += 0x07; + + while ((inb(tmport) & 0x80) == 0x00); + tmport -= 0x08; + k = inb(tmport); + if (k != 0x16) { + if ((k == 0x85) || (k == 0x42)) { + continue; + } + tmport = ioportu[host] + 0x10; + outb(0x41, tmport); + goto wait_rdyok; + } + assignid_map |= m; + + } + tmport = ioportu[host] + 0x02; + outb(0x7f, tmport); + tmport = ioportu[host] + 0x1b; + outb(0x02, tmport); + + outb(0, 0x80); + + val = 0x0080; /* bsy */ + tmport = ioportu[host] + 0x1c; + outw(val, tmport); + val |= 0x0040; /* sel */ + outw(val, tmport); + val |= 0x0004; /* msg */ + outw(val, tmport); + inb(0x80); /* 2 deskew delay(45ns*2=90ns) */ + val &= 0x007f; /* no bsy */ + outw(val, tmport); + mydlyu(0xffff); /* recommanded SCAM selection response time */ + mydlyu(0xffff); + val &= 0x00fb; /* after 1ms no msg */ + outw(val, tmport); wait_nomsg: - if ((inb(tmport) & 0x04) != 0) - { - goto wait_nomsg; - } - outb(1,0x80); - mydlyu(100); - for ( n=0; n < 0x30000; n++ ) - { - if ((inb(tmport) & 0x80) != 0) /* bsy ? */ - { - goto wait_io; + if ((inb(tmport) & 0x04) != 0) { + goto wait_nomsg; + } + outb(1, 0x80); + mydlyu(100); + for (n = 0; n < 0x30000; n++) { + if ((inb(tmport) & 0x80) != 0) { /* bsy ? */ + goto wait_io; + } } - } - goto TCM_SYNC; + goto TCM_SYNC; wait_io: - for ( n=0; n < 0x30000; n++ ) - { - if ((inb(tmport) & 0x81) == 0x0081) - { - goto wait_io1; + for (n = 0; n < 0x30000; n++) { + if ((inb(tmport) & 0x81) == 0x0081) { + goto wait_io1; + } } - } - goto TCM_SYNC; + goto TCM_SYNC; wait_io1: - inb(0x80); - val |=0x8003; /* io,cd,db7 */ - outw(val,tmport); - inb(0x80); - val &=0x00bf; /* no sel */ - outw(val,tmport); - outb(2,0x80); + inb(0x80); + val |= 0x8003; /* io,cd,db7 */ + outw(val, tmport); + inb(0x80); + val &= 0x00bf; /* no sel */ + outw(val, tmport); + outb(2, 0x80); TCM_SYNC: - mydlyu(0x800); - if ((inb(tmport) & 0x80) == 0x00) /* bsy ? */ - { - outw(0,tmport--); - outb(0,tmport); - tmport=ioportu[host] + 0x15; - outb(0,tmport); - tmport += 0x03; - outb(0x09,tmport); - tmport += 0x07; - while ((inb(tmport) & 0x80) == 0); - tmport -= 0x08; - inb(tmport); - return; - } - - val &= 0x00ff; /* synchronization */ - val |= 0x3f00; - fun_scam(host,&val); - outb(3,0x80); - val &= 0x00ff; /* isolation */ - val |= 0x2000; - fun_scam(host,&val); - outb(4,0x80); - i=8; - j=0; + mydlyu(0x800); + if ((inb(tmport) & 0x80) == 0x00) { /* bsy ? */ + outw(0, tmport--); + outb(0, tmport); + tmport = ioportu[host] + 0x15; + outb(0, tmport); + tmport += 0x03; + outb(0x09, tmport); + tmport += 0x07; + while ((inb(tmport) & 0x80) == 0); + tmport -= 0x08; + inb(tmport); + return; + } + val &= 0x00ff; /* synchronization */ + val |= 0x3f00; + fun_scam(host, &val); + outb(3, 0x80); + val &= 0x00ff; /* isolation */ + val |= 0x2000; + fun_scam(host, &val); + outb(4, 0x80); + i = 8; + j = 0; TCM_ID: - if ((inw(tmport) & 0x2000) == 0) - { - goto TCM_ID; - } - outb(5,0x80); - val &= 0x00ff; /* get ID_STRING */ - val |= 0x2000; - k=fun_scam(host,&val); - if ((k & 0x03) == 0) - { - goto TCM_5; - } - mbuf[j] <<= 0x01; - mbuf[j] &= 0xfe; - if ((k & 0x02) != 0) - { - mbuf[j] |= 0x01; - } - i--; - if ( i > 0 ) - { - goto TCM_ID; - } - j++; - i=8; - goto TCM_ID; - -TCM_5: /* isolation complete.. */ + if ((inw(tmport) & 0x2000) == 0) { + goto TCM_ID; + } + outb(5, 0x80); + val &= 0x00ff; /* get ID_STRING */ + val |= 0x2000; + k = fun_scam(host, &val); + if ((k & 0x03) == 0) { + goto TCM_5; + } + mbuf[j] <<= 0x01; + mbuf[j] &= 0xfe; + if ((k & 0x02) != 0) { + mbuf[j] |= 0x01; + } + i--; + if (i > 0) { + goto TCM_ID; + } + j++; + i = 8; + goto TCM_ID; + +TCM_5: /* isolation complete.. */ /* mbuf[32]=0; - printk(" \n%x %x %x %s\n ",assignid_map,mbuf[0],mbuf[1],&mbuf[2]); */ - i=15; - j=mbuf[0]; - if ((j & 0x20) != 0) /* bit5=1:ID upto 7 */ - { - i=7; - } - if ((j & 0x06) == 0) /* IDvalid? */ - { - goto G2Q5; - } - k=mbuf[1]; + printk(" \n%x %x %x %s\n ",assignid_map,mbuf[0],mbuf[1],&mbuf[2]); */ + i = 15; + j = mbuf[0]; + if ((j & 0x20) != 0) { /* bit5=1:ID upto 7 */ + i = 7; + } + if ((j & 0x06) == 0) { /* IDvalid? */ + goto G2Q5; + } + k = mbuf[1]; small_id: - m=1; - m <<= k; - if ((m & assignid_map) == 0) - { - goto G2Q_QUIN; - } - if ( k > 0 ) - { - k--; - goto small_id; - } -G2Q5: /* srch from max acceptable ID# */ - k=i; /* max acceptable ID# */ + m = 1; + m <<= k; + if ((m & assignid_map) == 0) { + goto G2Q_QUIN; + } + if (k > 0) { + k--; + goto small_id; + } +G2Q5: /* srch from max acceptable ID# */ + k = i; /* max acceptable ID# */ G2Q_LP: - m=1; - m <<= k; - if ((m & assignid_map) == 0) - { - goto G2Q_QUIN; - } - if ( k > 0 ) - { - k--; - goto G2Q_LP; - } -G2Q_QUIN: /* k=binID#, */ - assignid_map |= m; - if ( k < 8 ) - { - quintet[0]=0x38; /* 1st dft ID<8 */ - } - else - { - quintet[0]=0x31; /* 1st ID>=8 */ - } - k &= 0x07; - quintet[1]=g2q_tab[k]; - - val &= 0x00ff; /* AssignID 1stQuintet,AH=001xxxxx */ - m=quintet[0] << 8; - val |= m; - fun_scam(host,&val); - val &= 0x00ff; /* AssignID 2ndQuintet,AH=001xxxxx */ - m=quintet[1] << 8; - val |= m; - fun_scam(host,&val); - - goto TCM_SYNC; + m = 1; + m <<= k; + if ((m & assignid_map) == 0) { + goto G2Q_QUIN; + } + if (k > 0) { + k--; + goto G2Q_LP; + } +G2Q_QUIN: /* k=binID#, */ + assignid_map |= m; + if (k < 8) { + quintet[0] = 0x38; /* 1st dft ID<8 */ + } else { + quintet[0] = 0x31; /* 1st ID>=8 */ + } + k &= 0x07; + quintet[1] = g2q_tab[k]; + + val &= 0x00ff; /* AssignID 1stQuintet,AH=001xxxxx */ + m = quintet[0] << 8; + val |= m; + fun_scam(host, &val); + val &= 0x00ff; /* AssignID 2ndQuintet,AH=001xxxxx */ + m = quintet[1] << 8; + val |= m; + fun_scam(host, &val); + + goto TCM_SYNC; } -void is870(unsigned long host,unsigned int wkport ) +void is870(unsigned long host, unsigned int wkport) { - unsigned int tmport ; - unsigned char i,j,k,rmb; - unsigned short int m; - static unsigned char mbuf[512]; - static unsigned char satn[9] = { 0,0,0,0,0,0,0,6,6 }; - static unsigned char inqd[9] = { 0x12,0,0,0,0x24,0,0,0x24,6 }; - static unsigned char synn[6] = { 0x80,1,3,1,0x19,0x0e }; - static unsigned char synu[6] = { 0x80,1,3,1,0x0c,0x0e }; - static unsigned char synw[6] = { 0x80,1,3,1,0x0c,0x07 }; - static unsigned char wide[6] = { 0x80,1,2,3,1,0 }; - - sync_idu=0; - tmport=wkport+0x3a; - outb((unsigned char)(inb(tmport) | 0x10),tmport); - - for ( i = 0 ; i < 16 ; i ++ ) - { - if ((chip_veru[host] != 4) && (i > 7)) - { - break; - } - m=1; - m=m<<i; - if ( ( m & active_idu[host] ) != 0 ) - { - continue; - } - if ( i == host_idu[host] ) - { - printk(" ID: %2d Host Adapter\n",host_idu[host]); - continue; - } - if ( chip_veru[host] == 4 ) - { - tmport=wkport+0x1b; - j=(inb(tmport) & 0x0e) | 0x01; - outb(j,tmport); - } - tmport=wkport+1; - outb(0x08,tmport++); - outb(0x7f,tmport++); - outb(satn[0],tmport++); - outb(satn[1],tmport++); - outb(satn[2],tmport++); - outb(satn[3],tmport++); - outb(satn[4],tmport++); - outb(satn[5],tmport++); - tmport+=0x06; - outb(0,tmport); - tmport+=0x02; - outb(devspu[host][i],tmport++); - outb(0,tmport++); - outb(satn[6],tmport++); - outb(satn[7],tmport++); - j=i; - if ((j & 0x08) != 0) - { - j=(j & 0x07) | 0x40; - } - outb(j,tmport); - tmport+=0x03; - outb(satn[8],tmport); - tmport+=0x07; - - while ((inb(tmport) & 0x80) == 0x00); - tmport-=0x08; - if ((inb(tmport) != 0x11) && (inb(tmport) != 0x8e)) - { - continue; - } - while ( inb(tmport) != 0x8e ); - active_idu[host] |= m; - - tmport=wkport+0x10; - outb(0x30,tmport); - tmport=wkport+0x04; - outb(0x00,tmport); + unsigned int tmport; + unsigned char i, j, k, rmb; + unsigned short int m; + static unsigned char mbuf[512]; + static unsigned char satn[9] = {0, 0, 0, 0, 0, 0, 0, 6, 6}; + static unsigned char inqd[9] = {0x12, 0, 0, 0, 0x24, 0, 0, 0x24, 6}; + static unsigned char synn[6] = {0x80, 1, 3, 1, 0x19, 0x0e}; + static unsigned char synu[6] = {0x80, 1, 3, 1, 0x0c, 0x0e}; + static unsigned char synw[6] = {0x80, 1, 3, 1, 0x0c, 0x07}; + static unsigned char wide[6] = {0x80, 1, 2, 3, 1, 0}; + + sync_idu = 0; + tmport = wkport + 0x3a; + outb((unsigned char) (inb(tmport) | 0x10), tmport); + + for (i = 0; i < 16; i++) { + if ((chip_veru[host] != 4) && (i > 7)) { + break; + } + m = 1; + m = m << i; + if ((m & active_idu[host]) != 0) { + continue; + } + if (i == host_idu[host]) { + printk(" ID: %2d Host Adapter\n", host_idu[host]); + continue; + } + if (chip_veru[host] == 4) { + tmport = wkport + 0x1b; + j = (inb(tmport) & 0x0e) | 0x01; + outb(j, tmport); + } + tmport = wkport + 1; + outb(0x08, tmport++); + outb(0x7f, tmport++); + outb(satn[0], tmport++); + outb(satn[1], tmport++); + outb(satn[2], tmport++); + outb(satn[3], tmport++); + outb(satn[4], tmport++); + outb(satn[5], tmport++); + tmport += 0x06; + outb(0, tmport); + tmport += 0x02; + outb(devspu[host][i], tmport++); + outb(0, tmport++); + outb(satn[6], tmport++); + outb(satn[7], tmport++); + j = i; + if ((j & 0x08) != 0) { + j = (j & 0x07) | 0x40; + } + outb(j, tmport); + tmport += 0x03; + outb(satn[8], tmport); + tmport += 0x07; + + while ((inb(tmport) & 0x80) == 0x00); + tmport -= 0x08; + if ((inb(tmport) != 0x11) && (inb(tmport) != 0x8e)) { + continue; + } + while (inb(tmport) != 0x8e); + active_idu[host] |= m; + + tmport = wkport + 0x10; + outb(0x30, tmport); + tmport = wkport + 0x04; + outb(0x00, tmport); phase_cmd: - tmport=wkport+0x18; - outb(0x08,tmport); - tmport+=0x07; - while ((inb(tmport) & 0x80) == 0x00); - tmport-=0x08; - j=inb(tmport); - if ( j != 0x16 ) - { - tmport=wkport+0x10; - outb(0x41,tmport); - goto phase_cmd; - } + tmport = wkport + 0x18; + outb(0x08, tmport); + tmport += 0x07; + while ((inb(tmport) & 0x80) == 0x00); + tmport -= 0x08; + j = inb(tmport); + if (j != 0x16) { + tmport = wkport + 0x10; + outb(0x41, tmport); + goto phase_cmd; + } sel_ok: - tmport=wkport+3; - outb(inqd[0],tmport++); - outb(inqd[1],tmport++); - outb(inqd[2],tmport++); - outb(inqd[3],tmport++); - outb(inqd[4],tmport++); - outb(inqd[5],tmport); - tmport+=0x07; - outb(0,tmport); - tmport+=0x02; - outb(devspu[host][i],tmport++); - outb(0,tmport++); - outb(inqd[6],tmport++); - outb(inqd[7],tmport++); - tmport+=0x03; - outb(inqd[8],tmport); - tmport+=0x07; - while ((inb(tmport) & 0x80) == 0x00); - tmport-=0x08; - if ((inb(tmport) != 0x11) && (inb(tmport) != 0x8e)) - { - continue; - } - while ( inb(tmport) != 0x8e ); - if ( chip_veru[host] == 4 ) - { - tmport=wkport+0x1b; - j=inb(tmport) & 0x0e; - outb(j,tmport); - } - tmport=wkport+0x18; - outb(0x08,tmport); - tmport += 0x07; - j=0; + tmport = wkport + 3; + outb(inqd[0], tmport++); + outb(inqd[1], tmport++); + outb(inqd[2], tmport++); + outb(inqd[3], tmport++); + outb(inqd[4], tmport++); + outb(inqd[5], tmport); + tmport += 0x07; + outb(0, tmport); + tmport += 0x02; + outb(devspu[host][i], tmport++); + outb(0, tmport++); + outb(inqd[6], tmport++); + outb(inqd[7], tmport++); + tmport += 0x03; + outb(inqd[8], tmport); + tmport += 0x07; + while ((inb(tmport) & 0x80) == 0x00); + tmport -= 0x08; + if ((inb(tmport) != 0x11) && (inb(tmport) != 0x8e)) { + continue; + } + while (inb(tmport) != 0x8e); + if (chip_veru[host] == 4) { + tmport = wkport + 0x1b; + j = inb(tmport) & 0x0e; + outb(j, tmport); + } + tmport = wkport + 0x18; + outb(0x08, tmport); + tmport += 0x07; + j = 0; rd_inq_data: - k=inb(tmport); - if ((k & 0x01) != 0 ) - { - tmport-=0x06; - mbuf[j++]=inb(tmport); - tmport+=0x06; - goto rd_inq_data; - } - if ((k & 0x80) == 0 ) - { - goto rd_inq_data; - } - tmport-=0x08; - j=inb(tmport); - if ( j == 0x16 ) - { - goto inq_ok; - } - tmport=wkport+0x10; - outb(0x46,tmport); - tmport+=0x02; - outb(0,tmport++); - outb(0,tmport++); - outb(0,tmport++); - tmport+=0x03; - outb(0x08,tmport); - tmport+=0x07; - while ((inb(tmport) & 0x80) == 0x00); - tmport-=0x08; - if (inb(tmport) != 0x16) - { - goto sel_ok; - } + k = inb(tmport); + if ((k & 0x01) != 0) { + tmport -= 0x06; + mbuf[j++] = inb(tmport); + tmport += 0x06; + goto rd_inq_data; + } + if ((k & 0x80) == 0) { + goto rd_inq_data; + } + tmport -= 0x08; + j = inb(tmport); + if (j == 0x16) { + goto inq_ok; + } + tmport = wkport + 0x10; + outb(0x46, tmport); + tmport += 0x02; + outb(0, tmport++); + outb(0, tmport++); + outb(0, tmport++); + tmport += 0x03; + outb(0x08, tmport); + tmport += 0x07; + while ((inb(tmport) & 0x80) == 0x00); + tmport -= 0x08; + if (inb(tmport) != 0x16) { + goto sel_ok; + } inq_ok: - mbuf[36]=0; - printk(" ID: %2d %s\n",i,&mbuf[8]); - devtypeu[host][i]=mbuf[0]; - rmb=mbuf[1]; - if ( chip_veru[host] != 4 ) - { - goto not_wide; - } - if ((mbuf[7] & 0x60) == 0) - { - goto not_wide; - } - if ((global_map[host] & 0x20) == 0) - { - goto not_wide; - } - tmport=wkport+0x1b; - j=(inb(tmport) & 0x0e) | 0x01; - outb(j,tmport); - tmport=wkport+3; - outb(satn[0],tmport++); - outb(satn[1],tmport++); - outb(satn[2],tmport++); - outb(satn[3],tmport++); - outb(satn[4],tmport++); - outb(satn[5],tmport++); - tmport+=0x06; - outb(0,tmport); - tmport+=0x02; - outb(devspu[host][i],tmport++); - outb(0,tmport++); - outb(satn[6],tmport++); - outb(satn[7],tmport++); - tmport+=0x03; - outb(satn[8],tmport); - tmport+=0x07; - - while ((inb(tmport) & 0x80) == 0x00); - tmport-=0x08; - if ((inb(tmport) != 0x11) && (inb(tmport) != 0x8e)) - { - continue; - } - while ( inb(tmport) != 0x8e ); + mbuf[36] = 0; + printk(" ID: %2d %s\n", i, &mbuf[8]); + devtypeu[host][i] = mbuf[0]; + rmb = mbuf[1]; + if (chip_veru[host] != 4) { + goto not_wide; + } + if ((mbuf[7] & 0x60) == 0) { + goto not_wide; + } + if ((global_map[host] & 0x20) == 0) { + goto not_wide; + } + tmport = wkport + 0x1b; + j = (inb(tmport) & 0x0e) | 0x01; + outb(j, tmport); + tmport = wkport + 3; + outb(satn[0], tmport++); + outb(satn[1], tmport++); + outb(satn[2], tmport++); + outb(satn[3], tmport++); + outb(satn[4], tmport++); + outb(satn[5], tmport++); + tmport += 0x06; + outb(0, tmport); + tmport += 0x02; + outb(devspu[host][i], tmport++); + outb(0, tmport++); + outb(satn[6], tmport++); + outb(satn[7], tmport++); + tmport += 0x03; + outb(satn[8], tmport); + tmport += 0x07; + + while ((inb(tmport) & 0x80) == 0x00); + tmport -= 0x08; + if ((inb(tmport) != 0x11) && (inb(tmport) != 0x8e)) { + continue; + } + while (inb(tmport) != 0x8e); try_wide: - j=0; - tmport=wkport+0x14; - outb(0x05,tmport); - tmport += 0x04; - outb(0x20,tmport); - tmport+=0x07; - - while ((inb(tmport) & 0x80) == 0 ) - { - if ((inb(tmport) & 0x01) != 0 ) - { - tmport-=0x06; - outb(wide[j++],tmport); - tmport+=0x06; - } - } - tmport-=0x08; - while ((inb(tmport) & 0x80) == 0x00); - j=inb(tmport) & 0x0f; - if ( j == 0x0f ) - { - goto widep_in; - } - if ( j == 0x0a ) - { - goto widep_cmd; - } - if ( j == 0x0e ) - { - goto try_wide; - } - continue; + j = 0; + tmport = wkport + 0x14; + outb(0x05, tmport); + tmport += 0x04; + outb(0x20, tmport); + tmport += 0x07; + + while ((inb(tmport) & 0x80) == 0) { + if ((inb(tmport) & 0x01) != 0) { + tmport -= 0x06; + outb(wide[j++], tmport); + tmport += 0x06; + } + } + tmport -= 0x08; + while ((inb(tmport) & 0x80) == 0x00); + j = inb(tmport) & 0x0f; + if (j == 0x0f) { + goto widep_in; + } + if (j == 0x0a) { + goto widep_cmd; + } + if (j == 0x0e) { + goto try_wide; + } + continue; widep_out: - tmport=wkport+0x18; - outb(0x20,tmport); - tmport+=0x07; - while ((inb(tmport) & 0x80) == 0 ) - { - if ((inb(tmport) & 0x01) != 0 ) - { - tmport-=0x06; - outb(0,tmport); - tmport+=0x06; - } - } - tmport-=0x08; - j=inb(tmport) & 0x0f; - if ( j == 0x0f ) - { - goto widep_in; - } - if ( j == 0x0a ) - { - goto widep_cmd; - } - if ( j == 0x0e ) - { - goto widep_out; - } - continue; + tmport = wkport + 0x18; + outb(0x20, tmport); + tmport += 0x07; + while ((inb(tmport) & 0x80) == 0) { + if ((inb(tmport) & 0x01) != 0) { + tmport -= 0x06; + outb(0, tmport); + tmport += 0x06; + } + } + tmport -= 0x08; + j = inb(tmport) & 0x0f; + if (j == 0x0f) { + goto widep_in; + } + if (j == 0x0a) { + goto widep_cmd; + } + if (j == 0x0e) { + goto widep_out; + } + continue; widep_in: - tmport=wkport+0x14; - outb(0xff,tmport); - tmport += 0x04; - outb(0x20,tmport); - tmport+=0x07; - k=0; + tmport = wkport + 0x14; + outb(0xff, tmport); + tmport += 0x04; + outb(0x20, tmport); + tmport += 0x07; + k = 0; widep_in1: - j=inb(tmport); - if ((j & 0x01) != 0) - { - tmport-=0x06; - mbuf[k++]=inb(tmport); - tmport+=0x06; - goto widep_in1; - } - if ((j & 0x80) == 0x00) - { - goto widep_in1; - } - tmport-=0x08; - j=inb(tmport) & 0x0f; - if ( j == 0x0f ) - { - goto widep_in; - } - if ( j == 0x0a ) - { - goto widep_cmd; - } - if ( j == 0x0e ) - { - goto widep_out; - } - continue; + j = inb(tmport); + if ((j & 0x01) != 0) { + tmport -= 0x06; + mbuf[k++] = inb(tmport); + tmport += 0x06; + goto widep_in1; + } + if ((j & 0x80) == 0x00) { + goto widep_in1; + } + tmport -= 0x08; + j = inb(tmport) & 0x0f; + if (j == 0x0f) { + goto widep_in; + } + if (j == 0x0a) { + goto widep_cmd; + } + if (j == 0x0e) { + goto widep_out; + } + continue; widep_cmd: - tmport=wkport+0x10; - outb(0x30,tmport); - tmport=wkport+0x14; - outb(0x00,tmport); - tmport+=0x04; - outb(0x08,tmport); - tmport+=0x07; - while ((inb(tmport) & 0x80) == 0x00); - tmport-=0x08; - j=inb(tmport); - if ( j != 0x16 ) - { - if ( j == 0x4e ) - { - goto widep_out; - } - continue; - } - if ( mbuf[0] != 0x01 ) - { - goto not_wide; - } - if ( mbuf[1] != 0x02 ) - { - goto not_wide; - } - if ( mbuf[2] != 0x03 ) - { - goto not_wide; - } - if ( mbuf[3] != 0x01 ) - { - goto not_wide; - } - m=1; - m = m << i; - wide_idu[host] |= m; + tmport = wkport + 0x10; + outb(0x30, tmport); + tmport = wkport + 0x14; + outb(0x00, tmport); + tmport += 0x04; + outb(0x08, tmport); + tmport += 0x07; + while ((inb(tmport) & 0x80) == 0x00); + tmport -= 0x08; + j = inb(tmport); + if (j != 0x16) { + if (j == 0x4e) { + goto widep_out; + } + continue; + } + if (mbuf[0] != 0x01) { + goto not_wide; + } + if (mbuf[1] != 0x02) { + goto not_wide; + } + if (mbuf[2] != 0x03) { + goto not_wide; + } + if (mbuf[3] != 0x01) { + goto not_wide; + } + m = 1; + m = m << i; + wide_idu[host] |= m; not_wide: - if ((devtypeu[host][i] == 0x00) || (devtypeu[host][i] == 0x07)) - { - goto set_sync; - } - continue; + if ((devtypeu[host][i] == 0x00) || (devtypeu[host][i] == 0x07)) { + goto set_sync; + } + continue; set_sync: - tmport=wkport+0x1b; - j=inb(tmport) & 0x0e; - if ((m & wide_idu[host]) != 0 ) - { - j |= 0x01; - } - outb(j,tmport); - tmport=wkport+3; - outb(satn[0],tmport++); - outb(satn[1],tmport++); - outb(satn[2],tmport++); - outb(satn[3],tmport++); - outb(satn[4],tmport++); - outb(satn[5],tmport++); - tmport+=0x06; - outb(0,tmport); - tmport+=0x02; - outb(devspu[host][i],tmport++); - outb(0,tmport++); - outb(satn[6],tmport++); - outb(satn[7],tmport++); - tmport+=0x03; - outb(satn[8],tmport); - tmport+=0x07; - - while ((inb(tmport) & 0x80) == 0x00); - tmport-=0x08; - if ((inb(tmport) != 0x11) && (inb(tmport) != 0x8e)) - { - continue; - } - while ( inb(tmport) != 0x8e); + tmport = wkport + 0x1b; + j = inb(tmport) & 0x0e; + if ((m & wide_idu[host]) != 0) { + j |= 0x01; + } + outb(j, tmport); + tmport = wkport + 3; + outb(satn[0], tmport++); + outb(satn[1], tmport++); + outb(satn[2], tmport++); + outb(satn[3], tmport++); + outb(satn[4], tmport++); + outb(satn[5], tmport++); + tmport += 0x06; + outb(0, tmport); + tmport += 0x02; + outb(devspu[host][i], tmport++); + outb(0, tmport++); + outb(satn[6], tmport++); + outb(satn[7], tmport++); + tmport += 0x03; + outb(satn[8], tmport); + tmport += 0x07; + + while ((inb(tmport) & 0x80) == 0x00); + tmport -= 0x08; + if ((inb(tmport) != 0x11) && (inb(tmport) != 0x8e)) { + continue; + } + while (inb(tmport) != 0x8e); try_sync: - j=0; - tmport=wkport+0x14; - outb(0x06,tmport); - tmport += 0x04; - outb(0x20,tmport); - tmport+=0x07; - - while ((inb(tmport) & 0x80) == 0 ) - { - if ((inb(tmport) & 0x01) != 0 ) - { - tmport-=0x06; - if ( rmb != 0 ) - { - outb(synn[j++],tmport); - } - else - { - if ((m & wide_idu[host]) != 0) - { - outb(synw[j++],tmport); - } - else - { - if ((m & ultra_map[host]) != 0) - { - outb(synu[j++],tmport); + j = 0; + tmport = wkport + 0x14; + outb(0x06, tmport); + tmport += 0x04; + outb(0x20, tmport); + tmport += 0x07; + + while ((inb(tmport) & 0x80) == 0) { + if ((inb(tmport) & 0x01) != 0) { + tmport -= 0x06; + if (rmb != 0) { + outb(synn[j++], tmport); + } else { + if ((m & wide_idu[host]) != 0) { + outb(synw[j++], tmport); + } else { + if ((m & ultra_map[host]) != 0) { + outb(synu[j++], tmport); + } else { + outb(synn[j++], tmport); + } + } + } + tmport += 0x06; + } } - else - { - outb(synn[j++],tmport); - } - } - } - tmport+=0x06; - } - } - tmport-=0x08; - while ((inb(tmport) & 0x80) == 0x00); - j=inb(tmport) & 0x0f; - if ( j == 0x0f ) - { - goto phase_ins; - } - if ( j == 0x0a ) - { - goto phase_cmds; - } - if ( j == 0x0e ) - { - goto try_sync; - } - continue; + tmport -= 0x08; + while ((inb(tmport) & 0x80) == 0x00); + j = inb(tmport) & 0x0f; + if (j == 0x0f) { + goto phase_ins; + } + if (j == 0x0a) { + goto phase_cmds; + } + if (j == 0x0e) { + goto try_sync; + } + continue; phase_outs: - tmport=wkport+0x18; - outb(0x20,tmport); - tmport+=0x07; - while ((inb(tmport) & 0x80) == 0x00) - { - if ((inb(tmport) & 0x01) != 0x00) - { - tmport-=0x06; - outb(0x00,tmport); - tmport+=0x06; - } - } - tmport-=0x08; - j=inb(tmport); - if ( j == 0x85 ) - { - goto tar_dcons; - } - j &= 0x0f; - if ( j == 0x0f ) - { - goto phase_ins; - } - if ( j == 0x0a ) - { - goto phase_cmds; - } - if ( j == 0x0e ) - { - goto phase_outs; - } - continue; + tmport = wkport + 0x18; + outb(0x20, tmport); + tmport += 0x07; + while ((inb(tmport) & 0x80) == 0x00) { + if ((inb(tmport) & 0x01) != 0x00) { + tmport -= 0x06; + outb(0x00, tmport); + tmport += 0x06; + } + } + tmport -= 0x08; + j = inb(tmport); + if (j == 0x85) { + goto tar_dcons; + } + j &= 0x0f; + if (j == 0x0f) { + goto phase_ins; + } + if (j == 0x0a) { + goto phase_cmds; + } + if (j == 0x0e) { + goto phase_outs; + } + continue; phase_ins: - tmport=wkport+0x14; - outb(0xff,tmport); - tmport += 0x04; - outb(0x20,tmport); - tmport+=0x07; - k=0; + tmport = wkport + 0x14; + outb(0xff, tmport); + tmport += 0x04; + outb(0x20, tmport); + tmport += 0x07; + k = 0; phase_ins1: - j=inb(tmport); - if ((j & 0x01) != 0x00) - { - tmport-=0x06; - mbuf[k++]=inb(tmport); - tmport+=0x06; - goto phase_ins1; - } - if ((j & 0x80) == 0x00) - { - goto phase_ins1; - } - tmport-=0x08; - while ((inb(tmport) & 0x80) == 0x00); - j=inb(tmport); - if ( j == 0x85 ) - { - goto tar_dcons; - } - j &= 0x0f; - if ( j == 0x0f ) - { - goto phase_ins; - } - if ( j == 0x0a ) - { - goto phase_cmds; - } - if ( j == 0x0e ) - { - goto phase_outs; - } - continue; + j = inb(tmport); + if ((j & 0x01) != 0x00) { + tmport -= 0x06; + mbuf[k++] = inb(tmport); + tmport += 0x06; + goto phase_ins1; + } + if ((j & 0x80) == 0x00) { + goto phase_ins1; + } + tmport -= 0x08; + while ((inb(tmport) & 0x80) == 0x00); + j = inb(tmport); + if (j == 0x85) { + goto tar_dcons; + } + j &= 0x0f; + if (j == 0x0f) { + goto phase_ins; + } + if (j == 0x0a) { + goto phase_cmds; + } + if (j == 0x0e) { + goto phase_outs; + } + continue; phase_cmds: - tmport=wkport+0x10; - outb(0x30,tmport); + tmport = wkport + 0x10; + outb(0x30, tmport); tar_dcons: - tmport=wkport+0x14; - outb(0x00,tmport); - tmport+=0x04; - outb(0x08,tmport); - tmport+=0x07; - while ((inb(tmport) & 0x80) == 0x00); - tmport-=0x08; - j=inb(tmport); - if ( j != 0x16 ) - { - continue; - } - if ( mbuf[0] != 0x01 ) - { - continue; - } - if ( mbuf[1] != 0x03 ) - { - continue; - } - if ( mbuf[4] == 0x00 ) - { - continue; - } - if ( mbuf[3] > 0x64 ) - { - continue; - } - if ( mbuf[4] > 0x0c ) - { - mbuf[4]=0x0c; - } - devspu[host][i] = mbuf[4]; - if ((mbuf[3] < 0x0d) && (rmb == 0)) - { - j=0xa0; - goto set_syn_ok; - } - if ( mbuf[3] < 0x1a ) - { - j=0x20; - goto set_syn_ok; - } - if ( mbuf[3] < 0x33 ) - { - j=0x40; - goto set_syn_ok; - } - if ( mbuf[3] < 0x4c ) - { - j=0x50; - goto set_syn_ok; - } - j=0x60; -set_syn_ok: - devspu[host][i] = (devspu[host][i] & 0x0f) | j; - } - tmport=wkport+0x3a; - outb((unsigned char)(inb(tmport) & 0xef),tmport); + tmport = wkport + 0x14; + outb(0x00, tmport); + tmport += 0x04; + outb(0x08, tmport); + tmport += 0x07; + while ((inb(tmport) & 0x80) == 0x00); + tmport -= 0x08; + j = inb(tmport); + if (j != 0x16) { + continue; + } + if (mbuf[0] != 0x01) { + continue; + } + if (mbuf[1] != 0x03) { + continue; + } + if (mbuf[4] == 0x00) { + continue; + } + if (mbuf[3] > 0x64) { + continue; + } + if (mbuf[4] > 0x0c) { + mbuf[4] = 0x0c; + } + devspu[host][i] = mbuf[4]; + if ((mbuf[3] < 0x0d) && (rmb == 0)) { + j = 0xa0; + goto set_syn_ok; + } + if (mbuf[3] < 0x1a) { + j = 0x20; + goto set_syn_ok; + } + if (mbuf[3] < 0x33) { + j = 0x40; + goto set_syn_ok; + } + if (mbuf[3] < 0x4c) { + j = 0x50; + goto set_syn_ok; + } + j = 0x60; + set_syn_ok: + devspu[host][i] = (devspu[host][i] & 0x0f) | j; + } + tmport = wkport + 0x3a; + outb((unsigned char) (inb(tmport) & 0xef), tmport); } /* return non-zero on detection */ int atp870u_detect(Scsi_Host_Template * tpnt) { - unsigned char irq,h,k; - unsigned long flags; - unsigned int base_io,error,tmport; - unsigned short index = 0; - unsigned char pci_bus[3], pci_device_fn[3], chip_ver[3],host_id; - struct Scsi_Host * shpnt = NULL; - int count = 0; - static unsigned short devid[7]={0x8002,0x8010,0x8020,0x8030,0x8040,0x8050,0}; - static struct pci_dev *pdev = NULL, *acard_pdev[3]; - - printk("aec671x_detect: \n"); - if (!pci_present()) - { - printk(" NO BIOS32 SUPPORT.\n"); - return count; - } - - tpnt->proc_dir = &proc_scsi_atp870u; - - for ( h = 0 ; h < 2 ; h++ ) - { - active_idu[h]=0; - wide_idu[h]=0; - host_idu[h]=0x07; - quhdu[h]=0; - quendu[h]=0; - pci_bus[h]=0; - pci_device_fn[h]=0xff; - chip_ver[h]=0; - last_cmd[h]=0xff; - in_snd[h]=0; - in_int[h]=0; - for ( k = 0 ; k < qcnt ; k++ ) - { - querequ[h][k]=0; - } - for ( k = 0 ; k < 16 ; k++ ) - { - curr_req[h][k]=0; - } - } - h=0; - while ( devid[h] != 0 ) - { - pdev = pci_find_device(0x1191,devid[h],pdev); - if (pdev == NULL) { - h++; - index=0; - continue; - } - chip_ver[2]=0; - - /* To avoid messing with the things below... */ - acard_pdev[2] = pdev; - pci_device_fn[2] = pdev->devfn; - pci_bus[2] = pdev->bus->number; - - if ( devid[h] == 0x8002 ) - { - error = pci_read_config_byte(pdev,0x08,&chip_ver[2]); - if ( chip_ver[2] < 2 ) - { - goto nxt_devfn; - } - } - if ( devid[h] == 0x8010 ) - { - chip_ver[2]=0x04; - } - if ( pci_device_fn[2] < pci_device_fn[0] ) - { - acard_pdev[1]=acard_pdev[0]; - pci_bus[1]=pci_bus[0]; - pci_device_fn[1]=pci_device_fn[0]; - chip_ver[1]=chip_ver[0]; - acard_pdev[0]=acard_pdev[2]; - pci_bus[0]=pci_bus[2]; - pci_device_fn[0]=pci_device_fn[2]; - chip_ver[0]=chip_ver[2]; - } - else if ( pci_device_fn[2] < pci_device_fn[1] ) - { - acard_pdev[1]=acard_pdev[2]; - pci_bus[1]=pci_bus[2]; - pci_device_fn[1]=pci_device_fn[2]; - chip_ver[1]=chip_ver[2]; - } -nxt_devfn: - index++; - if ( index > 3 ) - { - index=0; - h++; - } - } - for ( h=0; h < 2; h++ ) - { - if ( pci_device_fn[h] == 0xff ) - { - return count; - } - - pdev = acard_pdev[h]; - pdev->devfn = pci_device_fn[h]; - pdev->bus->number = pci_bus[h]; - - /* Found an atp870u/w. */ - error = pci_read_config_dword(pdev,0x10,&base_io); - error += pci_read_config_byte(pdev,0x3c,&irq); - error += pci_read_config_byte(pdev,0x49,&host_id); - - base_io &= 0xfffffff8; - printk(" ACARD AEC-671X PCI Ultra/W SCSI-3 Host Adapter: %d IO:%x, IRQ:%d.\n" - ,h,base_io,irq); - ioportu[h]=base_io; - pciportu[h]=base_io + 0x20; - irqnumu[h]=irq; - host_id &= 0x07; - host_idu[h]=host_id; - chip_veru[h]=chip_ver[h]; - - tmport=base_io+0x22; - scam_on[h]=inb(tmport); - tmport += 0x0b; - global_map[h]=inb(tmport++); - ultra_map[h]=inw(tmport); - if ( ultra_map[h] == 0 ) - { - scam_on[h]=0x00; - global_map[h]=0x20; - ultra_map[h]=0xffff; - } - - shpnt = scsi_register(tpnt,4); - - save_flags(flags); - cli(); - if (request_irq(irq,atp870u_intr_handle, 0, "atp870u", NULL)) - { - printk("Unable to allocate IRQ for Acard controller.\n"); - goto unregister; - } - - tmport=base_io+0x3a; - k=(inb(tmport) & 0xf3) | 0x10; - outb(k,tmport); - outb((k & 0xdf),tmport); - mydlyu(0x8000); - outb(k,tmport); - mydlyu(0x8000); - tmport=base_io; - outb((host_id | 0x08),tmport); - tmport += 0x18; - outb(0,tmport); - tmport += 0x07; - while ((inb(tmport) & 0x80) == 0); - tmport -= 0x08; - inb(tmport); - tmport = base_io +1; - outb(8,tmport++); - outb(0x7f,tmport); - tmport = base_io + 0x11; - outb(0x20,tmport); - - tscam(h); - is870(h,base_io); - tmport=base_io+0x3a; - outb((inb(tmport) & 0xef),tmport); - - atp_host[h] = shpnt; - if ( chip_ver[h] == 4 ) - { - shpnt->max_id = 16; - } - shpnt->this_id = host_id; - shpnt->unique_id = base_io; - shpnt->io_port = base_io; - shpnt->n_io_port = 0x40; /* Number of bytes of I/O space used */ - shpnt->irq = irq; - restore_flags(flags); - request_region(base_io, 0x40,"atp870u"); /* Register the IO ports that we use */ - count++; - index++; - continue; + unsigned char irq, h, k; + unsigned long flags; + unsigned int base_io, error, tmport; + unsigned short index = 0; + unsigned char pci_bus[3], pci_device_fn[3], chip_ver[3], host_id; + struct Scsi_Host *shpnt = NULL; + int count = 0; + static unsigned short devid[7] = + {0x8002, 0x8010, 0x8020, 0x8030, 0x8040, 0x8050, 0}; + static struct pci_dev *pdev = NULL, *acard_pdev[3]; + + printk("aec671x_detect: \n"); + if (!pci_present()) { + printk(" NO BIOS32 SUPPORT.\n"); + return count; + } + tpnt->proc_dir = &proc_scsi_atp870u; + + for (h = 0; h < 2; h++) { + active_idu[h] = 0; + wide_idu[h] = 0; + host_idu[h] = 0x07; + quhdu[h] = 0; + quendu[h] = 0; + pci_bus[h] = 0; + pci_device_fn[h] = 0xff; + chip_ver[h] = 0; + last_cmd[h] = 0xff; + in_snd[h] = 0; + in_int[h] = 0; + for (k = 0; k < qcnt; k++) { + querequ[h][k] = 0; + } + for (k = 0; k < 16; k++) { + curr_req[h][k] = 0; + } + } + h = 0; + while (devid[h] != 0) { + pdev = pci_find_device(0x1191, devid[h], pdev); + if (pdev == NULL) { + h++; + index = 0; + continue; + } + chip_ver[2] = 0; + + /* To avoid messing with the things below... */ + acard_pdev[2] = pdev; + pci_device_fn[2] = pdev->devfn; + pci_bus[2] = pdev->bus->number; + + if (devid[h] == 0x8002) { + error = pci_read_config_byte(pdev, 0x08, &chip_ver[2]); + if (chip_ver[2] < 2) { + goto nxt_devfn; + } + } + if (devid[h] == 0x8010) { + chip_ver[2] = 0x04; + } + if (pci_device_fn[2] < pci_device_fn[0]) { + acard_pdev[1] = acard_pdev[0]; + pci_bus[1] = pci_bus[0]; + pci_device_fn[1] = pci_device_fn[0]; + chip_ver[1] = chip_ver[0]; + acard_pdev[0] = acard_pdev[2]; + pci_bus[0] = pci_bus[2]; + pci_device_fn[0] = pci_device_fn[2]; + chip_ver[0] = chip_ver[2]; + } else if (pci_device_fn[2] < pci_device_fn[1]) { + acard_pdev[1] = acard_pdev[2]; + pci_bus[1] = pci_bus[2]; + pci_device_fn[1] = pci_device_fn[2]; + chip_ver[1] = chip_ver[2]; + } + nxt_devfn: + index++; + if (index > 3) { + index = 0; + h++; + } + } + for (h = 0; h < 2; h++) { + if (pci_device_fn[h] == 0xff) { + return count; + } + pdev = acard_pdev[h]; + pdev->devfn = pci_device_fn[h]; + pdev->bus->number = pci_bus[h]; + + /* Found an atp870u/w. */ + error = pci_read_config_dword(pdev, 0x10, &base_io); + error += pci_read_config_byte(pdev, 0x3c, &irq); + error += pci_read_config_byte(pdev, 0x49, &host_id); + + base_io &= 0xfffffff8; + printk(" ACARD AEC-671X PCI Ultra/W SCSI-3 Host Adapter: %d IO:%x, IRQ:%d.\n" + ,h, base_io, irq); + ioportu[h] = base_io; + pciportu[h] = base_io + 0x20; + irqnumu[h] = irq; + host_id &= 0x07; + host_idu[h] = host_id; + chip_veru[h] = chip_ver[h]; + + tmport = base_io + 0x22; + scam_on[h] = inb(tmport); + tmport += 0x0b; + global_map[h] = inb(tmport++); + ultra_map[h] = inw(tmport); + if (ultra_map[h] == 0) { + scam_on[h] = 0x00; + global_map[h] = 0x20; + ultra_map[h] = 0xffff; + } + shpnt = scsi_register(tpnt, 4); + + save_flags(flags); + cli(); + if (request_irq(irq, atp870u_intr_handle, 0, "atp870u", NULL)) { + printk("Unable to allocate IRQ for Acard controller.\n"); + goto unregister; + } + tmport = base_io + 0x3a; + k = (inb(tmport) & 0xf3) | 0x10; + outb(k, tmport); + outb((k & 0xdf), tmport); + mydlyu(0x8000); + outb(k, tmport); + mydlyu(0x8000); + tmport = base_io; + outb((host_id | 0x08), tmport); + tmport += 0x18; + outb(0, tmport); + tmport += 0x07; + while ((inb(tmport) & 0x80) == 0); + tmport -= 0x08; + inb(tmport); + tmport = base_io + 1; + outb(8, tmport++); + outb(0x7f, tmport); + tmport = base_io + 0x11; + outb(0x20, tmport); + + tscam(h); + is870(h, base_io); + tmport = base_io + 0x3a; + outb((inb(tmport) & 0xef), tmport); + + atp_host[h] = shpnt; + if (chip_ver[h] == 4) { + shpnt->max_id = 16; + } + shpnt->this_id = host_id; + shpnt->unique_id = base_io; + shpnt->io_port = base_io; + shpnt->n_io_port = 0x40; /* Number of bytes of I/O space used */ + shpnt->irq = irq; + restore_flags(flags); + request_region(base_io, 0x40, "atp870u"); /* Register the IO ports that we use */ + count++; + index++; + continue; unregister: - scsi_unregister(shpnt); - restore_flags(flags); - index++; - continue; - } + scsi_unregister(shpnt); + restore_flags(flags); + index++; + continue; + } - return count; + return count; } /* The abort command does not leave the device in a clean state where @@ -1883,162 +1621,147 @@ unregister: int atp870u_abort(Scsi_Cmnd * SCpnt) { - unsigned char h,j; - unsigned int tmport; + unsigned char h, j; + unsigned int tmport; /* printk(" atp870u_abort: \n"); */ - for ( h=0; h <= admaxu; h++ ) - { - if ( SCpnt->host == atp_host[h] ) - { - goto find_adp; + for (h = 0; h <= admaxu; h++) { + if (SCpnt->host == atp_host[h]) { + goto find_adp; + } } - } - panic("Abort host not found !"); + panic("Abort host not found !"); find_adp: - printk(" workingu=%x last_cmd=%x ",workingu[h],last_cmd[h]); - printk(" quhdu=%x quendu=%x ",quhdu[h],quendu[h]); - tmport=ioportu[h]; - for ( j=0; j < 0x17; j++) - { - printk(" r%2x=%2x",j,inb(tmport++)); - } - tmport += 0x05; - printk(" r1c=%2x",inb(tmport)); - tmport += 0x03; - printk(" r1f=%2x in_snd=%2x ",inb(tmport),in_snd[h]); - tmport++; - printk(" r20=%2x",inb(tmport)); - tmport += 0x02; - printk(" r22=%2x \n",inb(tmport)); - return (SCSI_ABORT_SNOOZE); + printk(" workingu=%x last_cmd=%x ", workingu[h], last_cmd[h]); + printk(" quhdu=%x quendu=%x ", quhdu[h], quendu[h]); + tmport = ioportu[h]; + for (j = 0; j < 0x17; j++) { + printk(" r%2x=%2x", j, inb(tmport++)); + } + tmport += 0x05; + printk(" r1c=%2x", inb(tmport)); + tmport += 0x03; + printk(" r1f=%2x in_snd=%2x ", inb(tmport), in_snd[h]); + tmport++; + printk(" r20=%2x", inb(tmport)); + tmport += 0x02; + printk(" r22=%2x \n", inb(tmport)); + return (SCSI_ABORT_SNOOZE); } int atp870u_reset(Scsi_Cmnd * SCpnt, unsigned int reset_flags) { - unsigned char h; - /* - * See if a bus reset was suggested. - */ -/* printk("atp870u_reset: \n"); */ - for( h=0; h <= admaxu; h++ ) - { - if ( SCpnt->host == atp_host[h] ) - { - goto find_host; - } - } - panic("Reset bus host not found !"); + unsigned char h; + /* + * See if a bus reset was suggested. + */ +/* printk("atp870u_reset: \n"); */ + for (h = 0; h <= admaxu; h++) { + if (SCpnt->host == atp_host[h]) { + goto find_host; + } + } + panic("Reset bus host not found !"); find_host: -/* SCpnt->result = 0x00080000; - SCpnt->scsi_done(SCpnt); - workingu[h]=0; - quhdu[h]=0; - quendu[h]=0; - return (SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET); */ - return (SCSI_RESET_SNOOZE); +/* SCpnt->result = 0x00080000; + SCpnt->scsi_done(SCpnt); + workingu[h]=0; + quhdu[h]=0; + quendu[h]=0; + return (SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET); */ + return (SCSI_RESET_SNOOZE); } -const char * -atp870u_info(struct Scsi_Host *notused) +const char *atp870u_info(struct Scsi_Host *notused) { - static char buffer[128]; + static char buffer[128]; - strcpy(buffer, "ACARD AEC-6710/6712 PCI Ultra/W SCSI-3 Adapter Driver V1.0 "); + strcpy(buffer, "ACARD AEC-6710/6712 PCI Ultra/W SCSI-3 Adapter Driver V1.0 "); - return buffer; + return buffer; } -int -atp870u_set_info(char *buffer, int length, struct Scsi_Host *HBAptr) +int atp870u_set_info(char *buffer, int length, struct Scsi_Host *HBAptr) { - return (-ENOSYS); /* Currently this is a no-op */ + return (-ENOSYS); /* Currently this is a no-op */ } #define BLS buffer + len + size -int -atp870u_proc_info(char *buffer, char **start, off_t offset, int length, - int hostno, int inout) +int atp870u_proc_info(char *buffer, char **start, off_t offset, int length, + int hostno, int inout) { - struct Scsi_Host *HBAptr; - static u8 buff[512]; - int i; - int size = 0; - int len = 0; - off_t begin = 0; - off_t pos = 0; - - HBAptr = NULL; - for (i = 0; i < 2; i++) - { - if ((HBAptr = atp_host[i]) != NULL) - { - if (HBAptr->host_no == hostno) - { - break; - } - HBAptr = NULL; - } - } - - if (HBAptr == NULL) - { - size += sprintf(BLS, "Can't find adapter for host number %d\n", hostno); - len += size; pos = begin + len; size = 0; - goto stop_output; - } - - if (inout == TRUE) /* Has data been written to the file? */ - { - return (atp870u_set_info(buffer, length, HBAptr)); - } - - if (offset == 0) - { - memset(buff, 0, sizeof(buff)); - } - - size += sprintf(BLS, "ACARD AEC-671X Driver Version: 1.0\n"); - len += size; pos = begin + len; size = 0; - - size += sprintf(BLS, "\n"); - size += sprintf(BLS, "Adapter Configuration:\n"); - size += sprintf(BLS, " Base IO: %#.4lx\n", HBAptr->io_port); - size += sprintf(BLS, " IRQ: %d\n", HBAptr->irq); - len += size; pos = begin + len; size = 0; - -stop_output: - *start = buffer + (offset - begin); /* Start of wanted data */ - len -= (offset - begin); /* Start slop */ - if (len > length) - { - len = length; /* Ending slop */ - } - - return (len); + struct Scsi_Host *HBAptr; + static u8 buff[512]; + int i; + int size = 0; + int len = 0; + off_t begin = 0; + off_t pos = 0; + + HBAptr = NULL; + for (i = 0; i < 2; i++) { + if ((HBAptr = atp_host[i]) != NULL) { + if (HBAptr->host_no == hostno) { + break; + } + HBAptr = NULL; + } + } + + if (HBAptr == NULL) { + size += sprintf(BLS, "Can't find adapter for host number %d\n", hostno); + len += size; + pos = begin + len; + size = 0; + goto stop_output; + } + if (inout == TRUE) { /* Has data been written to the file? */ + return (atp870u_set_info(buffer, length, HBAptr)); + } + if (offset == 0) { + memset(buff, 0, sizeof(buff)); + } + size += sprintf(BLS, "ACARD AEC-671X Driver Version: 1.0\n"); + len += size; + pos = begin + len; + size = 0; + + size += sprintf(BLS, "\n"); + size += sprintf(BLS, "Adapter Configuration:\n"); + size += sprintf(BLS, " Base IO: %#.4lx\n", HBAptr->io_port); + size += sprintf(BLS, " IRQ: %d\n", HBAptr->irq); + len += size; + pos = begin + len; + size = 0; + + stop_output: + *start = buffer + (offset - begin); /* Start of wanted data */ + len -= (offset - begin); /* Start slop */ + if (len > length) { + len = length; /* Ending slop */ + } + return (len); } #include "sd.h" -int atp870u_biosparam(Scsi_Disk * disk, kdev_t dev, int * ip) +int atp870u_biosparam(Scsi_Disk * disk, kdev_t dev, int *ip) { - int heads, sectors, cylinders; + int heads, sectors, cylinders; - heads = 64; - sectors = 32; - cylinders = disk->capacity / (heads * sectors); + heads = 64; + sectors = 32; + cylinders = disk->capacity / (heads * sectors); - if ( cylinders > 1024 ) - { - heads = 255; - sectors = 63; - cylinders = disk->capacity / (heads * sectors); - } - - ip[0] = heads; - ip[1] = sectors; - ip[2] = cylinders; + if (cylinders > 1024) { + heads = 255; + sectors = 63; + cylinders = disk->capacity / (heads * sectors); + } + ip[0] = heads; + ip[1] = sectors; + ip[2] = cylinders; - return 0; + return 0; } #ifdef MODULE @@ -2046,4 +1769,3 @@ Scsi_Host_Template driver_template = ATP870U; #include "scsi_module.c" #endif - diff --git a/drivers/scsi/atp870u.h b/drivers/scsi/atp870u.h index 64cb56b9e..d5a2c5faf 100644 --- a/drivers/scsi/atp870u.h +++ b/drivers/scsi/atp870u.h @@ -1,10 +1,13 @@ #ifndef _ATP870U_H -/* $Id: atp870u.h,v 1.0 1997/05/07 15:09:00 root Exp root $ +/* $Id$ * * Header file for the ACARD 870U/W driver for Linux * - * $Log: atp870u.h,v $ + * $Log$ + * Revision 1.2 2000/01/26 20:23:40 ralf + * Merge with Linux 2.3.23, part 1. + * * Revision 1.0 1997/05/07 15:09:00 root * Initial revision * @@ -20,10 +23,10 @@ int atp870u_detect(Scsi_Host_Template *); int atp870u_command(Scsi_Cmnd *); -int atp870u_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); +int atp870u_queuecommand(Scsi_Cmnd *, void (*done) (Scsi_Cmnd *)); int atp870u_abort(Scsi_Cmnd *); int atp870u_reset(Scsi_Cmnd *, unsigned int); -int atp870u_biosparam(Disk *, kdev_t, int*); +int atp870u_biosparam(Disk *, kdev_t, int *); void send_s870(unsigned char); #define qcnt 32 @@ -31,7 +34,7 @@ void send_s870(unsigned char); #define ATP870U_CMDLUN 1 #ifndef NULL - #define NULL 0 +#define NULL 0 #endif extern struct proc_dir_entry proc_scsi_atp870u; diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index b89f39c8f..78328d5ee 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -299,11 +299,6 @@ static void idescsi_end_request (byte uptodate, ide_hwgroup_t *hwgroup) scsi->pc = NULL; } -static inline unsigned long get_timeout(idescsi_pc_t *pc) -{ - return IDE_MAX(WAIT_CMD, pc->timeout - jiffies); -} - /* * Our interrupt handler. */ @@ -364,7 +359,8 @@ static void idescsi_pc_intr (ide_drive_t *drive) pc->actually_transferred += temp; pc->current_position += temp; idescsi_discard_data (drive,bcount - temp); - ide_set_handler(drive, &idescsi_pc_intr, get_timeout(pc)); + drive->timeout = IDE_MAX(WAIT_CMD, pc->timeout - jiffies); + ide_set_handler(drive, &idescsi_pc_intr); return; } #if IDESCSI_DEBUG_LOG @@ -388,7 +384,8 @@ static void idescsi_pc_intr (ide_drive_t *drive) pc->actually_transferred+=bcount; /* Update the current position */ pc->current_position+=bcount; - ide_set_handler(drive, &idescsi_pc_intr, get_timeout(pc)); /* And set the interrupt handler again */ + drive->timeout = IDE_MAX(WAIT_CMD, pc->timeout - jiffies); + ide_set_handler(drive, &idescsi_pc_intr); /* And set the interrupt handler again */ } static void idescsi_transfer_pc (ide_drive_t *drive) @@ -407,7 +404,8 @@ static void idescsi_transfer_pc (ide_drive_t *drive) ide_do_reset (drive); return; } - ide_set_handler(drive, &idescsi_pc_intr, get_timeout(pc)); /* Set the interrupt routine */ + drive->timeout = IDE_MAX(WAIT_CMD, pc->timeout - jiffies); + ide_set_handler(drive, &idescsi_pc_intr); /* Set the interrupt routine */ atapi_output_bytes (drive, scsi->pc->c, 12); /* Send the actual packet */ } @@ -441,7 +439,8 @@ static void idescsi_issue_pc (ide_drive_t *drive, idescsi_pc_t *pc) (void) (HWIF(drive)->dmaproc(ide_dma_begin, drive)); } if (test_bit (IDESCSI_DRQ_INTERRUPT, &scsi->flags)) { - ide_set_handler (drive, &idescsi_transfer_pc, get_timeout(pc)); + drive->timeout = IDE_MAX(WAIT_CMD, pc->timeout - jiffies); + ide_set_handler (drive, &idescsi_transfer_pc); OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* Issue the packet command */ } else { OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); @@ -488,7 +487,7 @@ static void idescsi_add_settings(ide_drive_t *drive) /* * drive setting name read/write ioctl ioctl data type min max mul_factor div_factor data pointer set function */ - ide_add_setting(drive, "bios_cyl", SETTING_RW, -1, -1, TYPE_SHORT, 0, 1023, 1, 1, &drive->bios_cyl, NULL); + ide_add_setting(drive, "bios_cyl", SETTING_RW, -1, -1, TYPE_INT, 0, 1023, 1, 1, &drive->bios_cyl, NULL); 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, "transform", SETTING_RW, -1, -1, TYPE_INT, 0, 3, 1, 1, &scsi->transform, NULL); diff --git a/drivers/scsi/inia100.h b/drivers/scsi/inia100.h index 51d63a589..3486897ee 100644 --- a/drivers/scsi/inia100.h +++ b/drivers/scsi/inia100.h @@ -349,8 +349,8 @@ typedef struct orc_scb { /* Scsi_Ctrl_Blk */ #define ORC_BUSDEVRST 0x01 /* SCSI Bus Device Reset */ /* Status of ORCSCB_Status */ -#define SCB_COMPLETE 0x00 /* SCB request completed */ -#define SCB_POST 0x01 /* SCB is posted by the HOST */ +#define ORCSCB_COMPLETE 0x00 /* SCB request completed */ +#define ORCSCB_POST 0x01 /* SCB is posted by the HOST */ /* Bit Definition for ORCSCB_Flags */ #define SCF_DISINT 0x01 /* Disable HOST interrupt */ diff --git a/drivers/scsi/ips.c b/drivers/scsi/ips.c index 450fc370e..3a422e6bc 100644 --- a/drivers/scsi/ips.c +++ b/drivers/scsi/ips.c @@ -1840,10 +1840,10 @@ ips_next(ips_ha_t *ha) { } if ((scb->scsi_cmd->request.cmd == READ) && (SC->request_bufflen)) - scb->dcdb.cmd_attribute |= DATA_IN; + scb->dcdb.cmd_attribute |= IPS_DATA_IN; if ((scb->scsi_cmd->request.cmd == WRITE) && (SC->request_bufflen)) - scb->dcdb.cmd_attribute |= DATA_OUT; + scb->dcdb.cmd_attribute |= IPS_DATA_OUT; if (scb->data_len >= IPS_MAX_XFER) { scb->dcdb.cmd_attribute |= TRANSFER_64K; @@ -2274,10 +2274,10 @@ ips_done(ips_ha_t *ha, ips_scb_t *scb) { } if ((scb->scsi_cmd->request.cmd == READ) && (scb->data_len)) - scb->dcdb.cmd_attribute |= DATA_IN; + scb->dcdb.cmd_attribute |= IPS_DATA_IN; if ((scb->scsi_cmd->request.cmd == WRITE) && (scb->data_len)) - scb->dcdb.cmd_attribute |= DATA_OUT; + scb->dcdb.cmd_attribute |= IPS_DATA_OUT; if (scb->data_len >= IPS_MAX_XFER) { scb->dcdb.cmd_attribute |= TRANSFER_64K; diff --git a/drivers/scsi/ips.h b/drivers/scsi/ips.h index a4244234a..791b144d7 100644 --- a/drivers/scsi/ips.h +++ b/drivers/scsi/ips.h @@ -237,8 +237,8 @@ #define NO_DISCONNECT 0x00 #define DISCONNECT_ALLOWED 0x80 #define NO_AUTO_REQUEST_SENSE 0x40 - #define DATA_IN 0x01 - #define DATA_OUT 0x02 + #define IPS_DATA_IN 0x01 + #define IPS_DATA_OUT 0x02 #define TRANSFER_64K 0x08 #define NOTIMEOUT 0x00 #define TIMEOUT10 0x10 diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index 29042cab9..057ed9e03 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -11,7 +11,7 @@ Copyright 1992 - 1999 Kai Makisara email Kai.Makisara@metla.fi - Last modified: Sat Aug 7 13:54:31 1999 by makisara@kai.makisara.local + Last modified: Tue Oct 19 21:39:15 1999 by makisara@kai.makisara.local Some small formal changes - aeb, 950809 */ @@ -36,10 +36,17 @@ is defined and non-zero. */ #define DEBUG 0 +#if DEBUG /* The message level for the debug messages is currently set to KERN_NOTICE so that people can easily see the messages. Later when the debugging messages in the drivers are more widely classified, this may be changed to KERN_DEBUG. */ #define ST_DEB_MSG KERN_NOTICE +#define DEB(a) a +#define DEBC(a) if (debugging) { a ; } +#else +#define DEB(a) +#define DEBC(a) +#endif #define MAJOR_NR SCSI_TAPE_MAJOR #include <linux/blk.h> @@ -100,9 +107,7 @@ static struct st_dev_parm { #error "Buffer size should not exceed (2 << 24 - 1) bytes!" #endif -#if DEBUG -static int debugging = 1; -#endif +DEB( static int debugging = DEBUG; ) #define MAX_RETRIES 0 #define MAX_WRITE_RETRIES 0 @@ -164,9 +169,7 @@ static int st_chk_result(Scsi_Cmnd * SCpnt) int dev = TAPE_NR(SCpnt->request.rq_dev); int result = SCpnt->result; unsigned char *sense = SCpnt->sense_buffer, scode; -#if DEBUG - const char *stp; -#endif + DEB(const char *stp;) if (!result /* && SCpnt->sense_buffer[0] == 0 */ ) return 0; @@ -178,35 +181,35 @@ static int st_chk_result(Scsi_Cmnd * SCpnt) scode = 0; } -#if DEBUG - if (debugging) { - printk(ST_DEB_MSG "st%d: Error: %x, cmd: %x %x %x %x %x %x Len: %d\n", + DEB( + if (debugging) { + printk(ST_DEB_MSG "st%d: Error: %x, cmd: %x %x %x %x %x %x Len: %d\n", dev, result, SCpnt->data_cmnd[0], SCpnt->data_cmnd[1], SCpnt->data_cmnd[2], SCpnt->data_cmnd[3], SCpnt->data_cmnd[4], SCpnt->data_cmnd[5], SCpnt->request_bufflen); if (driver_byte(result) & DRIVER_SENSE) print_sense("st", SCpnt); - } else -#endif + } else ) /* end DEB */ if (!(driver_byte(result) & DRIVER_SENSE) || ((sense[0] & 0x70) == 0x70 && scode != NO_SENSE && scode != RECOVERED_ERROR && -/* scode != UNIT_ATTENTION && */ + /* scode != UNIT_ATTENTION && */ scode != BLANK_CHECK && scode != VOLUME_OVERFLOW && SCpnt->data_cmnd[0] != MODE_SENSE && - SCpnt->data_cmnd[0] != TEST_UNIT_READY)) { /* Abnormal conditions for tape */ + SCpnt->data_cmnd[0] != TEST_UNIT_READY)) { /* Abnormal conditions for tape */ if (driver_byte(result) & DRIVER_SENSE) { printk(KERN_WARNING "st%d: Error with sense data: ", dev); print_sense("st", SCpnt); } else printk(KERN_WARNING "st%d: Error %x (sugg. bt 0x%x, driver bt 0x%x, host bt 0x%x).\n", - dev, result, suggestion(result), driver_byte(result) & DRIVER_MASK, - host_byte(result)); + dev, result, suggestion(result), + driver_byte(result) & DRIVER_MASK, host_byte(result)); } + if ((sense[0] & 0x70) == 0x70 && scode == RECOVERED_ERROR #if ST_RECOVERED_WRITE_FATAL @@ -216,7 +219,8 @@ static int st_chk_result(Scsi_Cmnd * SCpnt) ) { scsi_tapes[dev].recover_count++; scsi_tapes[dev].mt_status->mt_erreg += (1 << MT_ST_SOFTERR_SHIFT); -#if DEBUG + + DEB( if (debugging) { if (SCpnt->data_cmnd[0] == READ_6) stp = "read"; @@ -226,8 +230,8 @@ static int st_chk_result(Scsi_Cmnd * SCpnt) stp = "ioctl"; printk(ST_DEB_MSG "st%d: Recovered %s error (%d).\n", dev, stp, scsi_tapes[dev].recover_count); - } -#endif + } ) /* end DEB */ + if ((sense[2] & 0xe0) == 0) return 0; } @@ -250,8 +254,9 @@ static void st_sleep_done(Scsi_Cmnd * SCpnt) /* EOM at write-behind, has all been written? */ if ((SCpnt->sense_buffer[0] & 0x80) != 0) remainder = (SCpnt->sense_buffer[3] << 24) | - (SCpnt->sense_buffer[4] << 16) | - (SCpnt->sense_buffer[5] << 8) | SCpnt->sense_buffer[6]; + (SCpnt->sense_buffer[4] << 16) | + (SCpnt->sense_buffer[5] << 8) | + SCpnt->sense_buffer[6]; else remainder = 0; if ((SCpnt->sense_buffer[2] & 0x0f) == VOLUME_OVERFLOW || @@ -263,16 +268,14 @@ static void st_sleep_done(Scsi_Cmnd * SCpnt) (STp->buffer)->last_result = SCpnt->result; SCpnt->request.rq_status = RQ_SCSI_DONE; (STp->buffer)->last_SCpnt = SCpnt; + DEB( STp->write_pending = 0; ) -#if DEBUG - STp->write_pending = 0; -#endif up(SCpnt->request.sem); } -#if DEBUG + DEB( else if (debugging) printk(KERN_ERR "st?: Illegal interrupt device %x\n", st_nbr); -#endif + ) /* end DEB */ } @@ -289,10 +292,12 @@ static Scsi_Cmnd * spin_lock_irqsave(&io_request_lock, flags); if (SCpnt == NULL) if ((SCpnt = scsi_allocate_device(NULL, STp->device, 1)) == NULL) { - printk(KERN_ERR "st%d: Can't get SCSI request.\n", TAPE_NR(STp->devt)); + printk(KERN_ERR "st%d: Can't get SCSI request.\n", + TAPE_NR(STp->devt)); spin_unlock_irqrestore(&io_request_lock, flags); return NULL; } + cmd[1] |= (SCpnt->lun << 5) & 0xe0; init_MUTEX_LOCKED(&STp->sem); SCpnt->use_sg = (bytes > (STp->buffer)->sg[0].length) ? @@ -314,7 +319,6 @@ static Scsi_Cmnd * if (do_wait) { down(SCpnt->request.sem); - (STp->buffer)->last_result_fatal = st_chk_result(SCpnt); } return SCpnt; @@ -329,12 +333,12 @@ static void write_behind_check(Scsi_Tape * STp) STbuffer = STp->buffer; -#if DEBUG + DEB( if (STp->write_pending) STp->nbr_waits++; else STp->nbr_finished++; -#endif + ) /* end DEB */ down(&(STp->sem)); @@ -347,7 +351,8 @@ static void write_behind_check(Scsi_Tape * STp) STbuffer->b_data + STbuffer->writing, STbuffer->buffer_bytes - STbuffer->writing); #else - printk(KERN_WARNING "st: write_behind_check: something left in buffer!\n"); + printk(KERN_WARNING + "st: write_behind_check: something left in buffer!\n"); #endif STbuffer->buffer_bytes -= STbuffer->writing; STps = &(STp->ps[STp->partition]); @@ -378,11 +383,9 @@ static int cross_eof(Scsi_Tape * STp, int forward) } else cmd[2] = cmd[3] = cmd[4] = 0xff; /* -1 filemarks */ cmd[5] = 0; -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: Stepping over filemark %s.\n", - TAPE_NR(STp->devt), forward ? "forward" : "backward"); -#endif + + DEBC(printk(ST_DEB_MSG "st%d: Stepping over filemark %s.\n", + TAPE_NR(STp->devt), forward ? "forward" : "backward")); SCpnt = st_do_scsi(NULL, STp, cmd, 0, STp->timeout, MAX_RETRIES, TRUE); if (!SCpnt) @@ -411,11 +414,9 @@ static int flush_write_buffer(Scsi_Tape * STp) if ((STp->buffer)->writing) { write_behind_check(STp); if ((STp->buffer)->last_result_fatal) { -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: Async write error (flush) %x.\n", - TAPE_NR(STp->devt), (STp->buffer)->last_result); -#endif + DEBC(printk(ST_DEB_MSG + "st%d: Async write error (flush) %x.\n", + TAPE_NR(STp->devt), (STp->buffer)->last_result)) if ((STp->buffer)->last_result == INT_MAX) return (-ENOSPC); return (-EIO); @@ -430,10 +431,9 @@ static int flush_write_buffer(Scsi_Tape * STp) offset = (STp->buffer)->buffer_bytes; transfer = ((offset + STp->block_size - 1) / STp->block_size) * STp->block_size; -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: Flushing %d bytes.\n", TAPE_NR(STp->devt), transfer); -#endif + DEBC(printk(ST_DEB_MSG "st%d: Flushing %d bytes.\n", + TAPE_NR(STp->devt), transfer)); + memset((STp->buffer)->b_data + offset, 0, transfer - offset); memset(cmd, 0, 10); @@ -444,8 +444,8 @@ static int flush_write_buffer(Scsi_Tape * STp) cmd[3] = blks >> 8; cmd[4] = blks; - SCpnt = st_do_scsi(NULL, STp, cmd, transfer, STp->timeout, MAX_WRITE_RETRIES, - TRUE); + SCpnt = st_do_scsi(NULL, STp, cmd, transfer, STp->timeout, + MAX_WRITE_RETRIES, TRUE); if (!SCpnt) return (-EBUSY); @@ -458,7 +458,8 @@ static int flush_write_buffer(Scsi_Tape * STp) (STp->buffer)->buffer_bytes = 0; result = (-ENOSPC); } else { - printk(KERN_ERR "st%d: Error on flush.\n", TAPE_NR(STp->devt)); + printk(KERN_ERR "st%d: Error on flush.\n", + TAPE_NR(STp->devt)); result = (-EIO); } STps->drv_block = (-1); @@ -514,7 +515,7 @@ static int flush_buffer(struct inode *inode, struct file *filp, int seek_next) result = 0; if (!seek_next) { if (STps->eof == ST_FM_HIT) { - result = cross_eof(STp, FALSE); /* Back over the EOF hit */ + result = cross_eof(STp, FALSE); /* Back over the EOF hit */ if (!result) STps->eof = ST_NOEOF; else { @@ -590,19 +591,14 @@ static int scsi_tape_open(struct inode *inode, struct file *filp) } STp = &(scsi_tapes[dev]); if (STp->in_use) { -#if DEBUG - printk(ST_DEB_MSG "st%d: Device already in use.\n", dev); -#endif + DEB( printk(ST_DEB_MSG "st%d: Device already in use.\n", dev); ) return (-EBUSY); } STp->rew_at_close = (MINOR(inode->i_rdev) & 0x80) == 0; if (mode != STp->current_mode) { -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: Mode change from %d to %d.\n", - dev, STp->current_mode, mode); -#endif + DEBC(printk(ST_DEB_MSG "st%d: Mode change from %d to %d.\n", + dev, STp->current_mode, mode)); new_session = TRUE; STp->current_mode = mode; } @@ -646,9 +642,7 @@ static int scsi_tape_open(struct inode *inode, struct file *filp) } STp->ready = ST_READY; STp->recover_count = 0; -#if DEBUG - STp->nbr_waits = STp->nbr_finished = 0; -#endif + DEB( STp->nbr_waits = STp->nbr_finished = 0; ) if (scsi_tapes[dev].device->host->hostt->module) __MOD_INC_USE_COUNT(scsi_tapes[dev].device->host->hostt->module); @@ -667,18 +661,26 @@ static int scsi_tape_open(struct inode *inode, struct file *filp) __MOD_DEC_USE_COUNT(st_template.module); return (-EBUSY); } + if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 && (SCpnt->sense_buffer[2] & 0x0f) == UNIT_ATTENTION) { /* New media? */ - memset((void *) &cmd[0], 0, 10); - cmd[0] = TEST_UNIT_READY; - SCpnt = st_do_scsi(SCpnt, STp, cmd, 0, STp->long_timeout, MAX_READY_RETRIES, - TRUE); + /* Flush the queued UNIT ATTENTION sense data */ + for (i=0; i < 10; i++) { + memset((void *) &cmd[0], 0, 10); + cmd[0] = TEST_UNIT_READY; + SCpnt = st_do_scsi(SCpnt, STp, cmd, 0, STp->long_timeout, + MAX_READY_RETRIES, TRUE); + if ((SCpnt->sense_buffer[0] & 0x70) != 0x70 || + (SCpnt->sense_buffer[2] & 0x0f) != UNIT_ATTENTION) + break; + } (STp->device)->was_reset = 0; STp->partition = STp->new_partition = 0; if (STp->can_partitions) - STp->nbr_partitions = 1; /* This guess will be updated later if necessary */ + STp->nbr_partitions = 1; /* This guess will be updated later + if necessary */ for (i = 0; i < ST_NBR_PARTITIONS; i++) { STps = &(STp->ps[i]); STps->rw = ST_IDLE; @@ -690,6 +692,7 @@ static int scsi_tape_open(struct inode *inode, struct file *filp) } new_session = TRUE; } + if ((STp->buffer)->last_result_fatal != 0) { if ((STp->device)->scsi_level >= SCSI_2 && (SCpnt->sense_buffer[0] & 0x70) == 0x70 && @@ -709,30 +712,29 @@ static int scsi_tape_open(struct inode *inode, struct file *filp) STp->in_use = 1; return 0; } + if (STp->omit_blklims) STp->min_block = STp->max_block = (-1); else { memset((void *) &cmd[0], 0, 10); cmd[0] = READ_BLOCK_LIMITS; - SCpnt = st_do_scsi(SCpnt, STp, cmd, 6, STp->timeout, MAX_READY_RETRIES, TRUE); + SCpnt = st_do_scsi(SCpnt, STp, cmd, 6, STp->timeout, MAX_READY_RETRIES, + TRUE); if (!SCpnt->result && !SCpnt->sense_buffer[0]) { STp->max_block = ((STp->buffer)->b_data[1] << 16) | ((STp->buffer)->b_data[2] << 8) | (STp->buffer)->b_data[3]; STp->min_block = ((STp->buffer)->b_data[4] << 8) | (STp->buffer)->b_data[5]; -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: Block limits %d - %d bytes.\n", dev, STp->min_block, - STp->max_block); -#endif + if ( DEB( debugging || ) !STp->inited) + printk(KERN_WARNING + "st%d: Block limits %d - %d bytes.\n", dev, + STp->min_block, STp->max_block); } else { STp->min_block = STp->max_block = (-1); -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: Can't read block limits.\n", dev); -#endif + DEBC(printk(ST_DEB_MSG "st%d: Can't read block limits.\n", + dev)); } } @@ -743,40 +745,33 @@ static int scsi_tape_open(struct inode *inode, struct file *filp) SCpnt = st_do_scsi(SCpnt, STp, cmd, 12, STp->timeout, MAX_READY_RETRIES, TRUE); if ((STp->buffer)->last_result_fatal != 0) { -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: No Mode Sense.\n", dev); -#endif + DEBC(printk(ST_DEB_MSG "st%d: No Mode Sense.\n", dev)); STp->block_size = ST_DEFAULT_BLOCK; /* Educated guess (?) */ (STp->buffer)->last_result_fatal = 0; /* Prevent error propagation */ STp->drv_write_prot = 0; } else { - -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: Mode sense. Length %d, medium %x, WBS %x, BLL %d\n", - dev, - (STp->buffer)->b_data[0], (STp->buffer)->b_data[1], - (STp->buffer)->b_data[2], (STp->buffer)->b_data[3]); -#endif + DEBC(printk(ST_DEB_MSG + "st%d: Mode sense. Length %d, medium %x, WBS %x, BLL %d\n", + dev, + (STp->buffer)->b_data[0], (STp->buffer)->b_data[1], + (STp->buffer)->b_data[2], (STp->buffer)->b_data[3])); if ((STp->buffer)->b_data[3] >= 8) { STp->drv_buffer = ((STp->buffer)->b_data[2] >> 4) & 7; STp->density = (STp->buffer)->b_data[4]; STp->block_size = (STp->buffer)->b_data[9] * 65536 + (STp->buffer)->b_data[10] * 256 + (STp->buffer)->b_data[11]; -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: Density %x, tape length: %x, drv buffer: %d\n", - dev, STp->density, (STp->buffer)->b_data[5] * 65536 + - (STp->buffer)->b_data[6] * 256 + (STp->buffer)->b_data[7], - STp->drv_buffer); -#endif + DEBC(printk(ST_DEB_MSG + "st%d: Density %x, tape length: %x, drv buffer: %d\n", + dev, STp->density, (STp->buffer)->b_data[5] * 65536 + + (STp->buffer)->b_data[6] * 256 + (STp->buffer)->b_data[7], + STp->drv_buffer)); } + if (STp->block_size > (STp->buffer)->buffer_size && !enlarge_buffer(STp->buffer, STp->block_size, STp->restr_dma)) { - printk(KERN_NOTICE "st%d: Blocksize %d too large for buffer.\n", dev, - STp->block_size); + printk(KERN_NOTICE "st%d: Blocksize %d too large for buffer.\n", + dev, STp->block_size); scsi_release_command(SCpnt); (STp->buffer)->in_use = 0; STp->buffer = NULL; @@ -790,26 +785,25 @@ static int scsi_tape_open(struct inode *inode, struct file *filp) } scsi_release_command(SCpnt); SCpnt = NULL; + STp->inited = TRUE; if (STp->block_size > 0) - (STp->buffer)->buffer_blocks = (STp->buffer)->buffer_size / STp->block_size; + (STp->buffer)->buffer_blocks = + (STp->buffer)->buffer_size / STp->block_size; else (STp->buffer)->buffer_blocks = 1; (STp->buffer)->buffer_bytes = (STp->buffer)->read_pointer = 0; -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: Block size: %d, buffer size: %d (%d blocks).\n", dev, + DEBC(printk(ST_DEB_MSG + "st%d: Block size: %d, buffer size: %d (%d blocks).\n", dev, STp->block_size, (STp->buffer)->buffer_size, - (STp->buffer)->buffer_blocks); -#endif + (STp->buffer)->buffer_blocks)); if (STp->drv_write_prot) { STp->write_prot = 1; -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: Write protected\n", dev); -#endif + + DEBC(printk(ST_DEB_MSG "st%d: Write protected\n", dev)); + if ((flags & O_ACCMODE) == O_WRONLY || (flags & O_ACCMODE) == O_RDWR) { (STp->buffer)->in_use = 0; STp->buffer = NULL; @@ -820,14 +814,13 @@ static int scsi_tape_open(struct inode *inode, struct file *filp) return (-EROFS); } } + if (STp->can_partitions && STp->nbr_partitions < 1) { /* This code is reached when the device is opened for the first time after the driver has been initialized with tape in the drive and the partition support has been enabled. */ -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: Updating partition number in status.\n", dev); -#endif + DEBC(printk(ST_DEB_MSG + "st%d: Updating partition number in status.\n", dev)); if ((STp->partition = find_partition(inode)) < 0) { (STp->buffer)->in_use = 0; STp->buffer = NULL; @@ -838,8 +831,9 @@ static int scsi_tape_open(struct inode *inode, struct file *filp) return STp->partition; } STp->new_partition = STp->partition; - STp->nbr_partitions = 1; /* This guess will be updated when necessary */ + STp->nbr_partitions = 1; /* This guess will be updated when necessary */ } + if (new_session) { /* Change the drive parameters for the new mode */ STp->density_changed = STp->blksize_changed = FALSE; STp->compression_changed = FALSE; @@ -853,9 +847,11 @@ static int scsi_tape_open(struct inode *inode, struct file *filp) __MOD_DEC_USE_COUNT(st_template.module); return i; } + if (STp->default_drvbuffer != 0xff) { if (st_int_ioctl(inode, MTSETDRVBUFFER, STp->default_drvbuffer)) - printk(KERN_WARNING "st%d: Can't set default drive buffering to %d.\n", + printk(KERN_WARNING + "st%d: Can't set default drive buffering to %d.\n", dev, STp->default_drvbuffer); } } @@ -889,24 +885,20 @@ static int scsi_tape_flush(struct file *filp) if (STp->can_partitions && (result = update_partition(inode)) < 0) { -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: update_partition at close failed.\n", dev); -#endif + DEBC(printk(ST_DEB_MSG + "st%d: update_partition at close failed.\n", dev)); goto out; } + if (STps->rw == ST_WRITING && !(STp->device)->was_reset) { result = flush_write_buffer(STp); -#if DEBUG - if (debugging) { - printk(ST_DEB_MSG "st%d: File length %ld bytes.\n", - dev, (long) (filp->f_pos)); - printk(ST_DEB_MSG "st%d: Async write waits %d, finished %d.\n", - dev, STp->nbr_waits, STp->nbr_finished); - } -#endif + DEBC(printk(ST_DEB_MSG "st%d: File length %ld bytes.\n", + dev, (long) (filp->f_pos)); + printk(ST_DEB_MSG "st%d: Async write waits %d, finished %d.\n", + dev, STp->nbr_waits, STp->nbr_finished); + ) if (result == 0 || result == (-ENOSPC)) { @@ -914,8 +906,8 @@ static int scsi_tape_flush(struct file *filp) cmd[0] = WRITE_FILEMARKS; cmd[4] = 1 + STp->two_fm; - SCpnt = st_do_scsi(NULL, STp, cmd, 0, STp->timeout, MAX_WRITE_RETRIES, - TRUE); + SCpnt = st_do_scsi(NULL, STp, cmd, 0, STp->timeout, + MAX_WRITE_RETRIES, TRUE); if (!SCpnt) goto out; @@ -943,11 +935,9 @@ static int scsi_tape_flush(struct file *filp) STps->eof = ST_FM; } } -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: Buffer flushed, %d EOF(s) written\n", - dev, cmd[4]); -#endif + + DEBC(printk(ST_DEB_MSG "st%d: Buffer flushed, %d EOF(s) written\n", + dev, cmd[4])); } else if (!STp->rew_at_close) { STps = &(STp->ps[STp->partition]); if (!STm->sysv || STps->rw != ST_READING) { @@ -972,6 +962,7 @@ static int scsi_tape_flush(struct file *filp) STps->eof = ST_FM; } } + out: if (STp->rew_at_close) { result2 = st_int_ioctl(inode, MTREW, 1); @@ -1001,6 +992,7 @@ static int scsi_tape_close(struct inode *inode, struct file *filp) normalize_buffer(STp->buffer); (STp->buffer)->in_use = 0; } + STp->in_use = 0; if (scsi_tapes[dev].device->host->hostt->module) __MOD_DEC_USE_COUNT(scsi_tapes[dev].device->host->hostt->module); @@ -1039,16 +1031,19 @@ static ssize_t if (!scsi_block_when_processing_errors(STp->device)) { return -ENXIO; } + if (ppos != &filp->f_pos) { /* "A request was outside the capabilities of the device." */ return -ENXIO; } + if (STp->ready != ST_READY) { if (STp->ready == ST_NO_TAPE) return (-ENOMEDIUM); else return (-EIO); } + STm = &(STp->modes[STp->current_mode]); if (!STm->defined) return (-ENXIO); @@ -1062,12 +1057,11 @@ static ssize_t if (STp->device->was_reset) return (-EIO); -#if DEBUG + DEB( if (!STp->in_use) { printk(ST_DEB_MSG "st%d: Incorrect device.\n", dev); return (-EIO); - } -#endif + } ) /* end DEB */ /* Write must be integral number of blocks */ if (STp->block_size != 0 && (count % STp->block_size) != 0) { @@ -1075,6 +1069,7 @@ static ssize_t dev); return (-EIO); } + if (STp->can_partitions && (retval = update_partition(inode)) < 0) return retval; @@ -1111,20 +1106,19 @@ static ssize_t } } } + if ((STp->buffer)->writing) { write_behind_check(STp); if ((STp->buffer)->last_result_fatal) { -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: Async write error (write) %x.\n", dev, - (STp->buffer)->last_result); -#endif + DEBC(printk(ST_DEB_MSG "st%d: Async write error (write) %x.\n", + dev, (STp->buffer)->last_result)); if ((STp->buffer)->last_result == INT_MAX) STps->eof = ST_EOM_OK; else STps->eof = ST_EOM_ERROR; } } + if (STps->eof == ST_EOM_OK) return (-ENOSPC); else if (STps->eof == ST_EOM_ERROR) @@ -1178,6 +1172,7 @@ static ssize_t } return i; } + if (STp->block_size == 0) blks = transfer = do_count; else { @@ -1195,18 +1190,18 @@ static ssize_t return (-EBUSY); if ((STp->buffer)->last_result_fatal != 0) { -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: Error on write:\n", dev); -#endif + DEBC(printk(ST_DEB_MSG "st%d: Error on write:\n", dev)); if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 && (SCpnt->sense_buffer[2] & 0x40)) { - if (STp->block_size != 0 && (SCpnt->sense_buffer[0] & 0x80) != 0) + if (STp->block_size != 0 && + (SCpnt->sense_buffer[0] & 0x80) != 0) transfer = (SCpnt->sense_buffer[3] << 24) | (SCpnt->sense_buffer[4] << 16) | - (SCpnt->sense_buffer[5] << 8) | SCpnt->sense_buffer[6]; + (SCpnt->sense_buffer[5] << 8) | + SCpnt->sense_buffer[6]; else if (STp->block_size == 0 && - (SCpnt->sense_buffer[2] & 0x0f) == VOLUME_OVERFLOW) + (SCpnt->sense_buffer[2] & 0x0f) == + VOLUME_OVERFLOW) transfer = do_count; else transfer = 0; @@ -1216,26 +1211,26 @@ static ssize_t filp->f_pos += do_count - transfer; count -= do_count - transfer; if (STps->drv_block >= 0) { - if (STp->block_size == 0 && transfer < do_count) + if (STp->block_size == 0 && + transfer < do_count) STps->drv_block++; else if (STp->block_size != 0) - STps->drv_block += (do_count - transfer) / STp->block_size; + STps->drv_block += + (do_count - transfer) / + STp->block_size; } STps->eof = ST_EOM_OK; - retval = (-ENOSPC); /* EOM within current request */ -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: EOM with %d bytes unwritten.\n", - dev, transfer); -#endif + retval = (-ENOSPC); /* EOM within current request */ + DEBC(printk(ST_DEB_MSG + "st%d: EOM with %d bytes unwritten.\n", + dev, transfer)); } else { STps->eof = ST_EOM_ERROR; - STps->drv_block = (-1); /* Too cautious? */ + STps->drv_block = (-1); /* Too cautious? */ retval = (-EIO); /* EOM for old data */ -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: EOM with lost data.\n", dev); -#endif + DEBC(printk(ST_DEB_MSG + "st%d: EOM with lost data.\n", + dev)); } } else { STps->drv_block = (-1); /* Too cautious? */ @@ -1276,11 +1271,13 @@ static ssize_t filp->f_pos += count; count = 0; } + if (doing_write && (STp->buffer)->last_result_fatal != 0) { scsi_release_command(SCpnt); SCpnt = NULL; return (STp->buffer)->last_result_fatal; } + if (STm->do_async_writes && (((STp->buffer)->buffer_bytes >= STp->write_threshold && (STp->buffer)->buffer_bytes >= STp->block_size) || @@ -1301,9 +1298,7 @@ static ssize_t cmd[2] = blks >> 16; cmd[3] = blks >> 8; cmd[4] = blks; -#if DEBUG - STp->write_pending = 1; -#endif + DEB( STp->write_pending = 1; ) SCpnt = st_do_scsi(SCpnt, STp, cmd, (STp->buffer)->writing, STp->timeout, MAX_WRITE_RETRIES, FALSE); @@ -1376,21 +1371,18 @@ static long read_tape(struct inode *inode, long count, Scsi_Cmnd ** aSCpnt) /* Something to check */ if ((STp->buffer)->last_result_fatal) { retval = 1; -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: Sense: %2x %2x %2x %2x %2x %2x %2x %2x\n", - dev, - SCpnt->sense_buffer[0], SCpnt->sense_buffer[1], - SCpnt->sense_buffer[2], SCpnt->sense_buffer[3], - SCpnt->sense_buffer[4], SCpnt->sense_buffer[5], - SCpnt->sense_buffer[6], SCpnt->sense_buffer[7]); -#endif + DEBC(printk(ST_DEB_MSG "st%d: Sense: %2x %2x %2x %2x %2x %2x %2x %2x\n", + dev, + SCpnt->sense_buffer[0], SCpnt->sense_buffer[1], + SCpnt->sense_buffer[2], SCpnt->sense_buffer[3], + SCpnt->sense_buffer[4], SCpnt->sense_buffer[5], + SCpnt->sense_buffer[6], SCpnt->sense_buffer[7])); if ((SCpnt->sense_buffer[0] & 0x70) == 0x70) { /* extended sense */ if ((SCpnt->sense_buffer[2] & 0x0f) == BLANK_CHECK) - SCpnt->sense_buffer[2] &= 0xcf; /* No need for EOM in this case */ + SCpnt->sense_buffer[2] &= 0xcf; /* No need for EOM in this case */ - if ((SCpnt->sense_buffer[2] & 0xe0) != 0) { /* EOF, EOM, or ILI */ + if ((SCpnt->sense_buffer[2] & 0xe0) != 0) { /* EOF, EOM, or ILI */ /* Compute the residual count */ if ((SCpnt->sense_buffer[0] & 0x80) != 0) transfer = (SCpnt->sense_buffer[3] << 24) | @@ -1410,7 +1402,7 @@ static long read_tape(struct inode *inode, long count, Scsi_Cmnd ** aSCpnt) } else { scsi_release_command(SCpnt); SCpnt = *aSCpnt = NULL; - if (transfer == blks) { /* We did not get anything, error */ + if (transfer == blks) { /* We did not get anything, error */ printk(KERN_NOTICE "st%d: Incorrect block size.\n", dev); if (STps->drv_block >= 0) STps->drv_block += blks - transfer + 1; @@ -1420,12 +1412,9 @@ static long read_tape(struct inode *inode, long count, Scsi_Cmnd ** aSCpnt) /* We have some data, deliver it */ (STp->buffer)->buffer_bytes = (blks - transfer) * STp->block_size; -#if DEBUG - if (debugging) - printk(ST_DEB_MSG - "st%d: ILI but enough data received %ld %d.\n", - dev, count, (STp->buffer)->buffer_bytes); -#endif + DEBC(printk(ST_DEB_MSG + "st%d: ILI but enough data received %ld %d.\n", + dev, count, (STp->buffer)->buffer_bytes)); if (STps->drv_block >= 0) STps->drv_block += 1; if (st_int_ioctl(inode, MTBSR, 1)) @@ -1441,12 +1430,9 @@ static long read_tape(struct inode *inode, long count, Scsi_Cmnd ** aSCpnt) else (STp->buffer)->buffer_bytes = bytes - transfer * STp->block_size; -#if DEBUG - if (debugging) - printk(ST_DEB_MSG - "st%d: EOF detected (%d bytes read).\n", - dev, (STp->buffer)->buffer_bytes); -#endif + DEBC(printk(ST_DEB_MSG + "st%d: EOF detected (%d bytes read).\n", + dev, (STp->buffer)->buffer_bytes)); } else if (SCpnt->sense_buffer[2] & 0x40) { if (STps->eof == ST_FM) STps->eof = ST_EOD_1; @@ -1457,28 +1443,21 @@ static long read_tape(struct inode *inode, long count, Scsi_Cmnd ** aSCpnt) else (STp->buffer)->buffer_bytes = bytes - transfer * STp->block_size; -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: EOM detected (%d bytes read).\n", - dev, (STp->buffer)->buffer_bytes); -#endif + + DEBC(printk(ST_DEB_MSG "st%d: EOM detected (%d bytes read).\n", + dev, (STp->buffer)->buffer_bytes)); } } /* end of EOF, EOM, ILI test */ else { /* nonzero sense key */ -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: Tape error while reading.\n", dev); -#endif + DEBC(printk(ST_DEB_MSG + "st%d: Tape error while reading.\n", dev)); STps->drv_block = (-1); if (STps->eof == ST_FM && (SCpnt->sense_buffer[2] & 0x0f) == BLANK_CHECK) { -#if DEBUG - if (debugging) - printk(ST_DEB_MSG - "st%d: Zero returned for first BLANK CHECK after EOF.\n", - dev); -#endif + DEBC(printk(ST_DEB_MSG + "st%d: Zero returned for first BLANK CHECK after EOF.\n", + dev)); STps->eof = ST_EOD_2; /* First BLANK_CHECK after FM */ } else /* Some other extended sense code */ retval = (-EIO); @@ -1529,10 +1508,12 @@ static ssize_t if (!scsi_block_when_processing_errors(STp->device)) { return -ENXIO; } + if (ppos != &filp->f_pos) { /* "A request was outside the capabilities of the device." */ return -ENXIO; } + if (STp->ready != ST_READY) { if (STp->ready == ST_NO_TAPE) return (-ENOMEDIUM); @@ -1542,12 +1523,11 @@ static ssize_t STm = &(STp->modes[STp->current_mode]); if (!STm->defined) return (-ENXIO); -#if DEBUG + DEB( if (!STp->in_use) { printk(ST_DEB_MSG "st%d: Incorrect device.\n", dev); return (-EIO); - } -#endif + } ) /* end DEB */ if (STp->can_partitions && (total = update_partition(inode)) < 0) @@ -1573,11 +1553,12 @@ static ssize_t return transfer; STps->rw = ST_READING; } -#if DEBUG + DEB( if (debugging && STps->eof != ST_NOEOF) printk(ST_DEB_MSG "st%d: EOF/EOM flag up (%d). Bytes %d\n", dev, STps->eof, (STp->buffer)->buffer_bytes); -#endif + ) /* end DEB */ + if ((STp->buffer)->buffer_bytes == 0 && STps->eof >= ST_EOD_1) { if (STps->eof < ST_EOD) { @@ -1586,6 +1567,7 @@ static ssize_t } return (-EIO); /* EOM or Blank Check */ } + /* Check the buffer writability before any tape movement. Don't alter buffer data. */ if (copy_from_user(&i, buf, 1) != 0 || @@ -1610,13 +1592,16 @@ static ssize_t return special; } } + /* Move the data from driver buffer to user buffer */ if ((STp->buffer)->buffer_bytes > 0) { -#if DEBUG + DEB( if (debugging && STps->eof != ST_NOEOF) - printk(ST_DEB_MSG "st%d: EOF up (%d). Left %d, needed %d.\n", dev, - STps->eof, (STp->buffer)->buffer_bytes, count - total); -#endif + printk(ST_DEB_MSG + "st%d: EOF up (%d). Left %d, needed %d.\n", dev, + STps->eof, (STp->buffer)->buffer_bytes, + count - total); + ) /* end DEB */ transfer = (STp->buffer)->buffer_bytes < count - total ? (STp->buffer)->buffer_bytes : count - total; i = from_buffer(STp->buffer, buf, transfer); @@ -1631,15 +1616,18 @@ static ssize_t buf += transfer; total += transfer; } + if (STp->block_size == 0) break; /* Read only one variable length block */ - } /* for (total = 0, special = 0; total < count && !special; ) */ + } /* for (total = 0, special = 0; + total < count && !special; ) */ if (SCpnt != NULL) { scsi_release_command(SCpnt); SCpnt = NULL; } + /* Change the eof state if no data from tape or buffer */ if (total == 0) { if (STps->eof == ST_FM_HIT) { @@ -1678,11 +1666,9 @@ static void st_log_options(Scsi_Tape * STp, ST_mode * STm, int dev) STp->scsi2_logical); printk(KERN_INFO "st%d: sysv: %d\n", dev, STm->sysv); -#if DEBUG - printk(KERN_INFO - "st%d: debugging: %d\n", - dev, debugging); -#endif + DEB(printk(KERN_INFO + "st%d: debugging: %d\n", + dev, debugging);) } @@ -1699,12 +1685,11 @@ static int st_set_options(struct inode *inode, long options) if (!STm->defined) { memcpy(STm, &(STp->modes[0]), sizeof(ST_mode)); modes_defined = TRUE; -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: Initialized mode %d definition from mode 0\n", - dev, STp->current_mode); -#endif + DEBC(printk(ST_DEB_MSG + "st%d: Initialized mode %d definition from mode 0\n", + dev, STp->current_mode)); } + code = options & MT_ST_OPTIONS; if (code == MT_ST_BOOLEANS) { STm->do_buffer_writes = (options & MT_ST_BUFFER_WRITES) != 0; @@ -1720,9 +1705,7 @@ static int st_set_options(struct inode *inode, long options) STp->can_partitions = (options & MT_ST_CAN_PARTITIONS) != 0; STp->scsi2_logical = (options & MT_ST_SCSI2LOGICAL) != 0; STm->sysv = (options & MT_ST_SYSV) != 0; -#if DEBUG - debugging = (options & MT_ST_DEBUGGING) != 0; -#endif + DEB( debugging = (options & MT_ST_DEBUGGING) != 0; ) st_log_options(STp, STm, dev); } else if (code == MT_ST_SETBOOLEANS || code == MT_ST_CLEARBOOLEANS) { value = (code == MT_ST_SETBOOLEANS); @@ -1751,15 +1734,15 @@ static int st_set_options(struct inode *inode, long options) STp->scsi2_logical = value; if ((options & MT_ST_SYSV) != 0) STm->sysv = value; -#if DEBUG + DEB( if ((options & MT_ST_DEBUGGING) != 0) - debugging = value; -#endif + debugging = value; ) st_log_options(STp, STm, dev); } else if (code == MT_ST_WRITE_THRESHOLD) { value = (options & ~MT_ST_OPTIONS) * ST_KILOBYTE; if (value < 1 || value > st_buffer_size) { - printk(KERN_WARNING "st%d: Write threshold %d too small or too large.\n", + printk(KERN_WARNING + "st%d: Write threshold %d too small or too large.\n", dev, value); return (-EIO); } @@ -1784,8 +1767,8 @@ static int st_set_options(struct inode *inode, long options) (value & ~MT_ST_SET_LONG_TIMEOUT)); } else { STp->timeout = value * HZ; - printk(KERN_INFO "st%d: Normal timeout set to %d seconds.\n", dev, - value); + printk(KERN_INFO "st%d: Normal timeout set to %d seconds.\n", + dev, value); } } else if (code == MT_ST_DEF_OPTIONS) { code = (options & ~MT_ST_CLEAR_DEFAULT); @@ -1793,7 +1776,8 @@ static int st_set_options(struct inode *inode, long options) if (code == MT_ST_DEF_DENSITY) { if (value == MT_ST_CLEAR_DEFAULT) { STm->default_density = (-1); - printk(KERN_INFO "st%d: Density default disabled.\n", dev); + printk(KERN_INFO "st%d: Density default disabled.\n", + dev); } else { STm->default_density = value & 0xff; printk(KERN_INFO "st%d: Density default set to %x\n", @@ -1802,16 +1786,19 @@ static int st_set_options(struct inode *inode, long options) } else if (code == MT_ST_DEF_DRVBUFFER) { if (value == MT_ST_CLEAR_DEFAULT) { STp->default_drvbuffer = 0xff; - printk(KERN_INFO "st%d: Drive buffer default disabled.\n", dev); + printk(KERN_INFO + "st%d: Drive buffer default disabled.\n", dev); } else { STp->default_drvbuffer = value & 7; - printk(KERN_INFO "st%d: Drive buffer default set to %x\n", + printk(KERN_INFO + "st%d: Drive buffer default set to %x\n", dev, STp->default_drvbuffer); } } else if (code == MT_ST_DEF_COMPRESSION) { if (value == MT_ST_CLEAR_DEFAULT) { STm->default_compression = ST_DONT_TOUCH; - printk(KERN_INFO "st%d: Compression default disabled.\n", dev); + printk(KERN_INFO + "st%d: Compression default disabled.\n", dev); } else { STm->default_compression = (value & 1 ? ST_YES : ST_NO); printk(KERN_INFO "st%d: Compression default set to %x\n", @@ -1858,30 +1845,23 @@ static int st_compression(Scsi_Tape * STp, int state) dev = TAPE_NR(SCpnt->request.rq_dev); if ((STp->buffer)->last_result_fatal != 0) { -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: Compression mode page not supported.\n", dev); -#endif + DEBC(printk(ST_DEB_MSG "st%d: Compression mode page not supported.\n", + dev)); scsi_release_command(SCpnt); SCpnt = NULL; return (-EIO); } -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: Compression state is %d.\n", dev, - ((STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] & DCE_MASK ? 1 : 0)); -#endif + DEBC(printk(ST_DEB_MSG "st%d: Compression state is %d.\n", dev, + ((STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] & DCE_MASK ? 1 : 0))); /* Check if compression can be changed */ if (((STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] & DCC_MASK) == 0) { -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: Compression not supported.\n", dev); -#endif + DEBC(printk(ST_DEB_MSG "st%d: Compression not supported.\n", dev)); scsi_release_command(SCpnt); SCpnt = NULL; return (-EIO); } + /* Do the change */ if (state) (STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] |= DCE_MASK; @@ -1899,19 +1879,13 @@ static int st_compression(Scsi_Tape * STp, int state) SCpnt = st_do_scsi(SCpnt, STp, cmd, cmd[4], STp->timeout, 0, TRUE); if ((STp->buffer)->last_result_fatal != 0) { -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: Compression change failed.\n", dev); -#endif + DEBC(printk(ST_DEB_MSG "st%d: Compression change failed.\n", dev)); scsi_release_command(SCpnt); SCpnt = NULL; return (-EIO); } -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: Compression state changed to %d.\n", - dev, state); -#endif + DEBC(printk(ST_DEB_MSG "st%d: Compression state changed to %d.\n", + dev, state)); scsi_release_command(SCpnt); SCpnt = NULL; @@ -1959,11 +1933,8 @@ static int st_int_ioctl(struct inode *inode, cmd[2] = (arg >> 16); cmd[3] = (arg >> 8); cmd[4] = arg; -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: Spacing tape forward over %d filemarks.\n", - dev, cmd[2] * 65536 + cmd[3] * 256 + cmd[4]); -#endif + DEBC(printk(ST_DEB_MSG "st%d: Spacing tape forward over %d filemarks.\n", + dev, cmd[2] * 65536 + cmd[3] * 256 + cmd[4])); if (fileno >= 0) fileno += arg; blkno = 0; @@ -1978,15 +1949,14 @@ static int st_int_ioctl(struct inode *inode, cmd[2] = (ltmp >> 16); cmd[3] = (ltmp >> 8); cmd[4] = ltmp; -#if DEBUG - if (debugging) { - if (cmd[2] & 0x80) - ltmp = 0xff000000; - ltmp = ltmp | (cmd[2] << 16) | (cmd[3] << 8) | cmd[4]; - printk(ST_DEB_MSG "st%d: Spacing tape backward over %ld filemarks.\n", - dev, (-ltmp)); - } -#endif + DEBC( + if (cmd[2] & 0x80) + ltmp = 0xff000000; + ltmp = ltmp | (cmd[2] << 16) | (cmd[3] << 8) | cmd[4]; + printk(ST_DEB_MSG + "st%d: Spacing tape backward over %ld filemarks.\n", + dev, (-ltmp)); + ) if (fileno >= 0) fileno -= arg; blkno = (-1); /* We can't know the block number */ @@ -1998,11 +1968,8 @@ static int st_int_ioctl(struct inode *inode, cmd[2] = (arg >> 16); cmd[3] = (arg >> 8); cmd[4] = arg; -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: Spacing tape forward %d blocks.\n", dev, - cmd[2] * 65536 + cmd[3] * 256 + cmd[4]); -#endif + DEBC(printk(ST_DEB_MSG "st%d: Spacing tape forward %d blocks.\n", dev, + cmd[2] * 65536 + cmd[3] * 256 + cmd[4])); if (blkno >= 0) blkno += arg; at_sm &= (arg == 0); @@ -2014,14 +1981,13 @@ static int st_int_ioctl(struct inode *inode, cmd[2] = (ltmp >> 16); cmd[3] = (ltmp >> 8); cmd[4] = ltmp; -#if DEBUG - if (debugging) { - if (cmd[2] & 0x80) - ltmp = 0xff000000; - ltmp = ltmp | (cmd[2] << 16) | (cmd[3] << 8) | cmd[4]; - printk(ST_DEB_MSG "st%d: Spacing tape backward %ld blocks.\n", dev, (-ltmp)); - } -#endif + DEBC( + if (cmd[2] & 0x80) + ltmp = 0xff000000; + ltmp = ltmp | (cmd[2] << 16) | (cmd[3] << 8) | cmd[4]; + printk(ST_DEB_MSG + "st%d: Spacing tape backward %ld blocks.\n", dev, (-ltmp)); + ) if (blkno >= 0) blkno -= arg; at_sm &= (arg == 0); @@ -2032,11 +1998,8 @@ static int st_int_ioctl(struct inode *inode, cmd[2] = (arg >> 16); cmd[3] = (arg >> 8); cmd[4] = arg; -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: Spacing tape forward %d setmarks.\n", dev, - cmd[2] * 65536 + cmd[3] * 256 + cmd[4]); -#endif + DEBC(printk(ST_DEB_MSG "st%d: Spacing tape forward %d setmarks.\n", dev, + cmd[2] * 65536 + cmd[3] * 256 + cmd[4])); if (arg != 0) { blkno = fileno = (-1); at_sm = 1; @@ -2049,15 +2012,13 @@ static int st_int_ioctl(struct inode *inode, cmd[2] = (ltmp >> 16); cmd[3] = (ltmp >> 8); cmd[4] = ltmp; -#if DEBUG - if (debugging) { - if (cmd[2] & 0x80) + DEBC( + if (cmd[2] & 0x80) ltmp = 0xff000000; - ltmp = ltmp | (cmd[2] << 16) | (cmd[3] << 8) | cmd[4]; - printk(ST_DEB_MSG "st%d: Spacing tape backward %ld setmarks.\n", - dev, (-ltmp)); - } -#endif + ltmp = ltmp | (cmd[2] << 16) | (cmd[3] << 8) | cmd[4]; + printk(ST_DEB_MSG "st%d: Spacing tape backward %ld setmarks.\n", + dev, (-ltmp)); + ) if (arg != 0) { blkno = fileno = (-1); at_sm = 1; @@ -2074,16 +2035,14 @@ static int st_int_ioctl(struct inode *inode, cmd[3] = (arg >> 8); cmd[4] = arg; timeout = STp->timeout; -#if DEBUG - if (debugging) { - if (cmd_in == MTWEOF) - printk(ST_DEB_MSG "st%d: Writing %d filemarks.\n", dev, + DEBC( + if (cmd_in == MTWEOF) + printk(ST_DEB_MSG "st%d: Writing %d filemarks.\n", dev, cmd[2] * 65536 + cmd[3] * 256 + cmd[4]); - else + else printk(ST_DEB_MSG "st%d: Writing %d setmarks.\n", dev, cmd[2] * 65536 + cmd[3] * 256 + cmd[4]); - } -#endif + ) if (fileno >= 0) fileno += arg; blkno = 0; @@ -2095,10 +2054,7 @@ static int st_int_ioctl(struct inode *inode, cmd[1] = 1; /* Don't wait for completion */ timeout = STp->timeout; #endif -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: Rewinding tape.\n", dev); -#endif + DEBC(printk(ST_DEB_MSG "st%d: Rewinding tape.\n", dev)); fileno = blkno = at_sm = 0; break; case MTOFFL: @@ -2113,14 +2069,10 @@ static int st_int_ioctl(struct inode *inode, if (cmd_in != MTOFFL && arg >= 1 + MT_ST_HPLOADER_OFFSET && arg <= 6 + MT_ST_HPLOADER_OFFSET) { -#if DEBUG - if (debugging) { - printk(ST_DEB_MSG "st%d: Enhanced %sload slot %2ld.\n", - dev, (cmd[4]) ? "" : "un", - arg - MT_ST_HPLOADER_OFFSET); - } -#endif - cmd[3] = arg - MT_ST_HPLOADER_OFFSET; /* MediaID field of C1553A */ + DEBC(printk(ST_DEB_MSG "st%d: Enhanced %sload slot %2ld.\n", + dev, (cmd[4]) ? "" : "un", + arg - MT_ST_HPLOADER_OFFSET)); + cmd[3] = arg - MT_ST_HPLOADER_OFFSET; /* MediaID field of C1553A */ } #if ST_NOWAIT cmd[1] = 1; /* Don't wait for completion */ @@ -2128,21 +2080,16 @@ static int st_int_ioctl(struct inode *inode, #else timeout = STp->long_timeout; #endif -#if DEBUG - if (debugging) { + DEBC( if (cmd_in != MTLOAD) printk(ST_DEB_MSG "st%d: Unloading tape.\n", dev); else printk(ST_DEB_MSG "st%d: Loading tape.\n", dev); - } -#endif + ) fileno = blkno = at_sm = 0; break; case MTNOP: -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: No op on tape.\n", dev); -#endif + DEBC(printk(ST_DEB_MSG "st%d: No op on tape.\n", dev)); return 0; /* Should do something ? */ break; case MTRETEN: @@ -2152,10 +2099,7 @@ static int st_int_ioctl(struct inode *inode, timeout = STp->timeout; #endif cmd[4] = 3; -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: Retensioning tape.\n", dev); -#endif + DEBC(printk(ST_DEB_MSG "st%d: Retensioning tape.\n", dev)); fileno = blkno = at_sm = 0; break; case MTEOM: @@ -2173,10 +2117,8 @@ static int st_int_ioctl(struct inode *inode, fileno = (-1); cmd[0] = SPACE; cmd[1] = 3; -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: Spacing to end of recorded medium.\n", dev); -#endif + DEBC(printk(ST_DEB_MSG "st%d: Spacing to end of recorded medium.\n", + dev)); blkno = 0; at_sm = 0; break; @@ -2191,29 +2133,20 @@ static int st_int_ioctl(struct inode *inode, #else timeout = STp->long_timeout * 8; #endif -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: Erasing tape.\n", dev); -#endif + DEBC(printk(ST_DEB_MSG "st%d: Erasing tape.\n", dev)); fileno = blkno = at_sm = 0; break; case MTLOCK: chg_eof = FALSE; cmd[0] = ALLOW_MEDIUM_REMOVAL; cmd[4] = SCSI_REMOVAL_PREVENT; -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: Locking drive door.\n", dev); -#endif /* ; */ + DEBC(printk(ST_DEB_MSG "st%d: Locking drive door.\n", dev)); break; case MTUNLOCK: chg_eof = FALSE; cmd[0] = ALLOW_MEDIUM_REMOVAL; cmd[4] = SCSI_REMOVAL_ALLOW; -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: Unlocking drive door.\n", dev); -#endif /* ; */ + DEBC(printk(ST_DEB_MSG "st%d: Unlocking drive door.\n", dev)); break; case MTSETBLK: /* Set block length */ case MTSETDENSITY: /* Set tape density */ @@ -2250,28 +2183,29 @@ static int st_int_ioctl(struct inode *inode, if (cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK) { ltmp = arg & MT_ST_BLKSIZE_MASK; if (cmd_in == MTSETBLK) - STp->blksize_changed = TRUE; /* At least we tried ;-) */ + STp->blksize_changed = TRUE; /* At least we tried ;-) */ } else ltmp = STp->block_size; (STp->buffer)->b_data[9] = (ltmp >> 16); (STp->buffer)->b_data[10] = (ltmp >> 8); (STp->buffer)->b_data[11] = ltmp; timeout = STp->timeout; -#if DEBUG - if (debugging) { + DEBC( if (cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK) - printk(ST_DEB_MSG "st%d: Setting block size to %d bytes.\n", dev, + printk(ST_DEB_MSG + "st%d: Setting block size to %d bytes.\n", dev, (STp->buffer)->b_data[9] * 65536 + (STp->buffer)->b_data[10] * 256 + (STp->buffer)->b_data[11]); if (cmd_in == MTSETDENSITY || cmd_in == SET_DENS_AND_BLK) - printk(ST_DEB_MSG "st%d: Setting density code to %x.\n", dev, + printk(ST_DEB_MSG + "st%d: Setting density code to %x.\n", dev, (STp->buffer)->b_data[4]); if (cmd_in == MTSETDRVBUFFER) - printk(ST_DEB_MSG "st%d: Setting drive buffer code to %d.\n", dev, + printk(ST_DEB_MSG + "st%d: Setting drive buffer code to %d.\n", dev, ((STp->buffer)->b_data[2] >> 4) & 7); - } -#endif + ) break; default: return (-ENOSYS); @@ -2331,8 +2265,8 @@ static int st_int_ioctl(struct inode *inode, } STp->partition = 0; } - } else { /* SCSI command was not completely successful. Don't return - from this block without releasing the SCSI command block! */ + } else { /* SCSI command was not completely successful. Don't return + from this block without releasing the SCSI command block! */ if (SCpnt->sense_buffer[2] & 0x40) { if (cmd_in != MTBSF && cmd_in != MTBSFM && @@ -2340,6 +2274,7 @@ static int st_int_ioctl(struct inode *inode, STps->eof = ST_EOM_OK; STps->drv_block = 0; } + undone = ( (SCpnt->sense_buffer[3] << 24) + (SCpnt->sense_buffer[4] << 16) + @@ -2446,10 +2381,7 @@ static int get_location(struct inode *inode, unsigned int *block, int *partition (STp->device->scsi_level >= SCSI_2 && ((STp->buffer)->b_data[0] & 4) != 0)) { *block = *partition = 0; -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: Can't read tape position.\n", dev); -#endif + DEBC(printk(ST_DEB_MSG "st%d: Can't read tape position.\n", dev)); result = (-EIO); } else { result = 0; @@ -2468,12 +2400,8 @@ static int get_location(struct inode *inode, unsigned int *block, int *partition (STp->buffer)->b_data[1] == 0) /* BOP of partition 0 */ STp->ps[0].drv_block = STp->ps[0].drv_file = 0; } -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: Got tape pos. blk %d part %d.\n", dev, - *block, *partition); -#endif - + DEBC(printk(ST_DEB_MSG "st%d: Got tape pos. blk %d part %d.\n", dev, + *block, *partition)); } scsi_release_command(SCpnt); SCpnt = NULL; @@ -2502,13 +2430,10 @@ static int set_location(struct inode *inode, unsigned int block, int partition, timeout = STp->long_timeout; STps = &(STp->ps[STp->partition]); -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: Setting block to %d and partition to %d.\n", - dev, block, partition); - if (partition < 0) - return (-EIO); -#endif + DEBC(printk(ST_DEB_MSG "st%d: Setting block to %d and partition to %d.\n", + dev, block, partition)); + DEB(if (partition < 0) + return (-EIO); ) /* Update the location at the partition we are leaving */ if ((!STp->can_partitions && partition != 0) || @@ -2520,13 +2445,12 @@ static int set_location(struct inode *inode, unsigned int block, int partition, else { STps->last_block_valid = TRUE; STps->last_block_visited = blk; -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: Visited block %d for partition %d saved.\n", - dev, blk, STp->partition); -#endif + DEBC(printk(ST_DEB_MSG + "st%d: Visited block %d for partition %d saved.\n", + dev, blk, STp->partition)); } } + memset(scmd, 0, 10); if ((STp->device)->scsi_level < SCSI_2) { scmd[0] = QFA_SEEK_BLOCK; @@ -2545,11 +2469,9 @@ static int set_location(struct inode *inode, unsigned int block, int partition, if (STp->partition != partition) { scmd[1] |= 2; scmd[8] = partition; -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: Trying to change partition from %d to %d\n", - dev, STp->partition, partition); -#endif + DEBC(printk(ST_DEB_MSG + "st%d: Trying to change partition from %d to %d\n", + dev, STp->partition, partition)); } } #if ST_NOWAIT @@ -2655,17 +2577,12 @@ static int nbr_partitions(struct inode *inode) SCpnt = NULL; if ((STp->buffer)->last_result_fatal != 0) { -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: Can't read medium partition page.\n", dev); -#endif + DEBC(printk(ST_DEB_MSG "st%d: Can't read medium partition page.\n", + dev)); result = (-EIO); } else { result = (STp->buffer)->b_data[MODE_HEADER_LENGTH + 3] + 1; -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: Number of partitions %d.\n", dev, result); -#endif + DEBC(printk(ST_DEB_MSG "st%d: Number of partitions %d.\n", dev, result)); } return result; @@ -2691,20 +2608,16 @@ static int partition_tape(struct inode *inode, int size) if (size <= 0) { length = 8; bp[MODE_HEADER_LENGTH + 3] = 0; -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: Formatting tape with one partition.\n", dev); -#endif + DEBC(printk(ST_DEB_MSG "st%d: Formatting tape with one partition.\n", + dev)); } else { length = 10; bp[MODE_HEADER_LENGTH + 3] = 1; bp[MODE_HEADER_LENGTH + 8] = (size >> 8) & 0xff; bp[MODE_HEADER_LENGTH + 9] = size & 0xff; -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: Formatting tape with two partition (1 = %d MB).\n", - dev, size); -#endif + DEBC(printk(ST_DEB_MSG + "st%d: Formatting tape with two partition (1 = %d MB).\n", + dev, size)); } bp[MODE_HEADER_LENGTH + 6] = 0; bp[MODE_HEADER_LENGTH + 7] = 0; @@ -2752,12 +2665,12 @@ static int st_ioctl(struct inode *inode, struct file *file, int dev = TAPE_NR(inode->i_rdev); STp = &(scsi_tapes[dev]); -#if DEBUG + DEB( if (debugging && !STp->in_use) { printk(ST_DEB_MSG "st%d: Incorrect device.\n", dev); return (-EIO); - } -#endif + } ) /* end DEB */ + STm = &(STp->modes[STp->current_mode]); STps = &(STp->ps[STp->partition]); @@ -2782,7 +2695,8 @@ static int st_ioctl(struct inode *inode, struct file *file, return (-EFAULT); if (mtc.mt_op == MTSETDRVBUFFER && !capable(CAP_SYS_ADMIN)) { - printk(KERN_WARNING "st%d: MTSETDRVBUFFER only allowed for root.\n", dev); + printk(KERN_WARNING + "st%d: MTSETDRVBUFFER only allowed for root.\n", dev); return (-EPERM); } if (!STm->defined && @@ -2792,7 +2706,8 @@ static int st_ioctl(struct inode *inode, struct file *file, if (!(STp->device)->was_reset) { if (STps->eof == ST_FM_HIT) { - if (mtc.mt_op == MTFSF || mtc.mt_op == MTFSFM || mtc.mt_op == MTEOM) { + if (mtc.mt_op == MTFSF || mtc.mt_op == MTFSFM || + mtc.mt_op == MTEOM) { mtc.mt_count -= 1; if (STps->drv_file >= 0) STps->drv_file += 1; @@ -2802,8 +2717,10 @@ static int st_ioctl(struct inode *inode, struct file *file, STps->drv_file += 1; } } + if (mtc.mt_op == MTSEEK) { - /* Old position must be restored if partition will be changed */ + /* Old position must be restored if partition will be + changed */ i = !STp->can_partitions || (STp->new_partition != STp->partition); } else { @@ -2832,7 +2749,8 @@ static int st_ioctl(struct inode *inode, struct file *file, if (STp->door_locked != ST_UNLOCKED && STp->door_locked != ST_LOCK_FAILS) { if (st_int_ioctl(inode, MTLOCK, 0)) { - printk(KERN_NOTICE "st%d: Could not relock door after bus reset.\n", + printk(KERN_NOTICE + "st%d: Could not relock door after bus reset.\n", dev); STp->door_locked = ST_UNLOCKED; } @@ -2850,6 +2768,7 @@ static int st_ioctl(struct inode *inode, struct file *file, if (mtc.mt_op == MTSETDRVBUFFER && (mtc.mt_count & MT_ST_OPTIONS) != 0) return st_set_options(inode, mtc.mt_count); + if (mtc.mt_op == MTSETPART) { if (!STp->can_partitions || mtc.mt_count < 0 || mtc.mt_count >= ST_NBR_PARTITIONS) @@ -2862,6 +2781,7 @@ static int st_ioctl(struct inode *inode, struct file *file, STp->new_partition = mtc.mt_count; return 0; } + if (mtc.mt_op == MTMKPART) { if (!STp->can_partitions) return (-EINVAL); @@ -2878,15 +2798,18 @@ static int st_ioctl(struct inode *inode, struct file *file, STps->drv_block = STps->drv_file = 0; return 0; } + if (mtc.mt_op == MTSEEK) { i = set_location(inode, mtc.mt_count, STp->new_partition, 0); if (!STp->can_partitions) STp->ps[0].rw = ST_IDLE; return i; } + if (STp->can_partitions && STp->ready == ST_READY && (i = update_partition(inode)) < 0) return i; + if (mtc.mt_op == MTCOMPRESSION) return st_compression(STp, (mtc.mt_count & 1)); else @@ -2916,9 +2839,11 @@ static int st_ioctl(struct inode *inode, struct file *file, (STp->mt_status)->mt_blkno += (STp->buffer)->buffer_bytes / STp->block_size; else if (STps->rw == ST_READING) - (STp->mt_status)->mt_blkno -= ((STp->buffer)->buffer_bytes + - STp->block_size - 1) / STp->block_size; + (STp->mt_status)->mt_blkno -= + ((STp->buffer)->buffer_bytes + + STp->block_size - 1) / STp->block_size; } + (STp->mt_status)->mt_gstat = 0; if (STp->drv_write_prot) (STp->mt_status)->mt_gstat |= GMT_WR_PROT(0xffffffff); @@ -2945,7 +2870,8 @@ static int st_ioctl(struct inode *inode, struct file *file, (STp->mt_status)->mt_gstat |= GMT_DR_OPEN(0xffffffff); if (STps->at_sm) (STp->mt_status)->mt_gstat |= GMT_SM(0xffffffff); - if (STm->do_async_writes || (STm->do_buffer_writes && STp->block_size != 0) || + if (STm->do_async_writes || + (STm->do_buffer_writes && STp->block_size != 0) || STp->drv_buffer != 0) (STp->mt_status)->mt_gstat |= GMT_IM_REP_EN(0xffffffff); @@ -3014,7 +2940,8 @@ static ST_buffer * } else { /* Got something, continue */ for (b_size = PAGE_SIZE; - st_buffer_size > tb->sg[0].length + (ST_FIRST_SG - 1) * b_size;) + st_buffer_size > + tb->sg[0].length + (ST_FIRST_SG - 1) * b_size;) b_size *= 2; for (segs = 1, got = tb->sg[0].length; @@ -3024,11 +2951,13 @@ static ST_buffer * if (tb->sg[segs].address == NULL) { if (st_buffer_size - got <= (ST_FIRST_SG - segs) * b_size / 2) { - b_size /= 2; /* Large enough for the rest of the buffers */ + b_size /= 2; /* Large enough for the + rest of the buffers */ continue; } for (i = 0; i < segs - 1; i++) - scsi_init_free(tb->sg[i].address, tb->sg[i].length); + scsi_init_free(tb->sg[i].address, + tb->sg[i].length); scsi_init_free((char *) tb, tb->this_size); tb = NULL; break; @@ -3040,6 +2969,7 @@ static ST_buffer * } } } + if (!tb) { printk(KERN_NOTICE "st: Can't allocate new tape buffer (nbr %d).\n", st_nbr_buffers); @@ -3048,16 +2978,13 @@ static ST_buffer * tb->sg_segs = tb->orig_sg_segs = segs; tb->b_data = tb->sg[0].address; -#if DEBUG - if (debugging) { - printk(ST_DEB_MSG - "st: Allocated tape buffer %d (%d bytes, %d segments, dma: %d, a: %p).\n", - st_nbr_buffers, got, tb->sg_segs, need_dma, tb->b_data); - printk(ST_DEB_MSG - "st: segment sizes: first %d, last %d bytes.\n", - tb->sg[0].length, tb->sg[segs - 1].length); - } -#endif + DEBC(printk(ST_DEB_MSG + "st: Allocated tape buffer %d (%d bytes, %d segments, dma: %d, a: %p).\n", + st_nbr_buffers, got, tb->sg_segs, need_dma, tb->b_data); + printk(ST_DEB_MSG + "st: segment sizes: first %d, last %d bytes.\n", + tb->sg[0].length, tb->sg[segs - 1].length); + ) tb->in_use = 0; tb->dma = need_dma; tb->buffer_size = got; @@ -3094,7 +3021,7 @@ static int enlarge_buffer(ST_buffer * STbuffer, int new_size, int need_dma) (unsigned char *) scsi_init_malloc(b_size, priority); if (STbuffer->sg[segs].address == NULL) { if (new_size - got <= (max_segs - segs) * b_size / 2) { - b_size /= 2; /* Large enough for the rest of the buffers */ + b_size /= 2; /* Large enough for the rest of the buffers */ continue; } printk(KERN_NOTICE "st: failed to enlarge buffer to %d bytes.\n", @@ -3109,12 +3036,9 @@ static int enlarge_buffer(ST_buffer * STbuffer, int new_size, int need_dma) STbuffer->buffer_size = got; segs++; } -#if DEBUG - if (debugging) - printk(ST_DEB_MSG - "st: Succeeded to enlarge buffer to %d bytes (segs %d->%d, %d).\n", - got, STbuffer->orig_sg_segs, STbuffer->sg_segs, b_size); -#endif + DEBC(printk(ST_DEB_MSG + "st: Succeeded to enlarge buffer to %d bytes (segs %d->%d, %d).\n", + got, STbuffer->orig_sg_segs, STbuffer->sg_segs, b_size)); return TRUE; } @@ -3129,11 +3053,11 @@ static void normalize_buffer(ST_buffer * STbuffer) scsi_init_free(STbuffer->sg[i].address, STbuffer->sg[i].length); STbuffer->buffer_size -= STbuffer->sg[i].length; } -#if DEBUG + DEB( if (debugging && STbuffer->orig_sg_segs < STbuffer->sg_segs) printk(ST_DEB_MSG "st: Buffer at %p normalized to %d bytes (segs %d).\n", STbuffer->b_data, STbuffer->buffer_size, STbuffer->sg_segs); -#endif + ) /* end DEB */ STbuffer->sg_segs = STbuffer->orig_sg_segs; } @@ -3244,7 +3168,8 @@ static int __init st_setup(char *str) len = strlen(parms[i].name); if (!strncmp(stp, parms[i].name, len) && (*(stp + len) == ':' || *(stp + len) == '=')) { - *parms[i].val = simple_strtoul(stp + len + 1, NULL, 0); + *parms[i].val = + simple_strtoul(stp + len + 1, NULL, 0); break; } } @@ -3296,6 +3221,7 @@ static int st_attach(Scsi_Device * SDp) SDp->attached--; return 1; } + for (tpnt = scsi_tapes, i = 0; i < st_template.dev_max; i++, tpnt++) if (!tpnt->device) break; @@ -3309,6 +3235,7 @@ static int st_attach(Scsi_Device * SDp) else scsi_tapes[i].mt_status->mt_type = MT_ISSCSI2; + tpnt->inited = 0; tpnt->devt = MKDEV(SCSI_TAPE_MAJOR, i); tpnt->dirty = 0; tpnt->in_use = 0; @@ -3392,7 +3319,8 @@ static int st_init() if (!st_registered) { if (register_chrdev(SCSI_TAPE_MAJOR, "st", &st_fops)) { - printk(KERN_ERR "Unable to get major %d for SCSI tapes\n", MAJOR_NR); + printk(KERN_ERR "Unable to get major %d for SCSI tapes\n", + MAJOR_NR); return 1; } st_registered++; @@ -3412,10 +3340,9 @@ static int st_init() unregister_chrdev(SCSI_TAPE_MAJOR, "st"); return 1; } -#if DEBUG - printk(ST_DEB_MSG "st: Buffer size %d bytes, write threshold %d bytes.\n", - st_buffer_size, st_write_threshold); -#endif + + DEB(printk(ST_DEB_MSG "st: Buffer size %d bytes, write threshold %d bytes.\n", + st_buffer_size, st_write_threshold)); memset(scsi_tapes, 0, st_template.dev_max * sizeof(Scsi_Tape)); for (i = 0; i < st_template.dev_max; ++i) { @@ -3447,7 +3374,8 @@ static int st_init() for (i = st_nbr_buffers = 0; i < target_nbr; i++) { if (!new_tape_buffer(TRUE, TRUE)) { if (i == 0) { - printk(KERN_INFO "No tape buffers allocated at initialization.\n"); + printk(KERN_INFO + "No tape buffers allocated at initialization.\n"); break; } printk(KERN_INFO "Number of tape buffers adjusted.\n"); @@ -3509,10 +3437,12 @@ void cleanup_module(void) for (j = 0; j < st_buffers[i]->sg_segs; j++) scsi_init_free((char *) st_buffers[i]->sg[j].address, st_buffers[i]->sg[j].length); - scsi_init_free((char *) st_buffers[i], st_buffers[i]->this_size); + scsi_init_free((char *) st_buffers[i], + st_buffers[i]->this_size); } } - scsi_init_free((char *) st_buffers, st_template.dev_max * sizeof(ST_buffer *)); + scsi_init_free((char *) st_buffers, + st_template.dev_max * sizeof(ST_buffer *)); } } st_template.dev_max = 0; diff --git a/drivers/scsi/st.h b/drivers/scsi/st.h index 9d386767f..9d5428e3f 100644 --- a/drivers/scsi/st.h +++ b/drivers/scsi/st.h @@ -104,6 +104,7 @@ typedef struct { unsigned char density; unsigned char door_locked; unsigned char rew_at_close; + unsigned char inited; int block_size; int min_block; int max_block; diff --git a/drivers/scsi/u14-34f.h b/drivers/scsi/u14-34f.h index 381e718d8..ff49b56ea 100644 --- a/drivers/scsi/u14-34f.h +++ b/drivers/scsi/u14-34f.h @@ -18,7 +18,9 @@ int u14_34f_biosparam(Disk *, kdev_t, int *); #define U14_34F_VERSION "5.11.00" +#ifndef LinuxVersionCode #define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s)) +#endif #define ULTRASTOR_14_34F { \ name: "UltraStor 14F/34F rev. " U14_34F_VERSION " ", \ diff --git a/drivers/sgi/char/sgiserial.c b/drivers/sgi/char/sgiserial.c index ed9928cdd..7ec370b22 100644 --- a/drivers/sgi/char/sgiserial.c +++ b/drivers/sgi/char/sgiserial.c @@ -419,6 +419,7 @@ static _INLINE_ void receive_chars(struct sgi_serial *info, struct pt_regs *regs show_state(); return; } else if (ch == 2) { + show_buffers(); return; } /* It is a 'keyboard interrupt' ;-) */ @@ -2246,10 +2247,7 @@ static struct console sgi_console_driver = { /* * Register console. */ -long __init serial_console_init(long kmem_start, long kmem_end) +void __init serial_console_init(void) { register_console(&sgi_console_driver); - return kmem_start; } - - diff --git a/drivers/sound/dev_table.h b/drivers/sound/dev_table.h index 0ce2c49b0..14c8cd2ac 100644 --- a/drivers/sound/dev_table.h +++ b/drivers/sound/dev_table.h @@ -33,7 +33,7 @@ #define SNDCARD_WAVEFRONT 41 #define SNDCARD_OPL3SA2 42 #define SNDCARD_OPL3SA2_MPU 43 -#define SNDCARD_WAVEARTIST 44 /* Rebel Waveartist */ +#define SNDCARD_WAVEARTIST 44 /* Waveartist */ #define SNDCARD_OPL3SA2_MSS 45 /* Originally missed */ #define SNDCARD_AD1816 88 diff --git a/drivers/sound/maestro.c b/drivers/sound/maestro.c index f64554dee..07c1dc7b3 100644 --- a/drivers/sound/maestro.c +++ b/drivers/sound/maestro.c @@ -847,7 +847,7 @@ static void maestro_write(struct ess_state *ess, u16 reg, u16 data) __maestro_write(ess->card,reg,data); - spin_unlock_irqrestore(&card->lock,flags); + spin_unlock_irqrestore(&ess->card->lock,flags); } static u16 __maestro_read(struct ess_card *card, u16 reg) diff --git a/drivers/sound/sound_core.c b/drivers/sound/sound_core.c index 8e31dfe12..ede422a46 100644 --- a/drivers/sound/sound_core.c +++ b/drivers/sound/sound_core.c @@ -58,6 +58,9 @@ extern int msnd_classic_init(void); #ifdef CONFIG_SOUND_MSNDPIN extern int msnd_pinnacle_init(void); #endif +#ifdef CONFIG_SOUND_CMPCI +extern init_cmpci(void); +#endif /* * Low level list operator. Scan the ordered list, find a hole and diff --git a/drivers/sound/waveartist.c b/drivers/sound/waveartist.c index 85a9efaec..0d2145347 100644 --- a/drivers/sound/waveartist.c +++ b/drivers/sound/waveartist.c @@ -1,14 +1,15 @@ /* - * drivers/sound/waveartist.c + * linux/drivers/sound/waveartist.c * * The low level driver for the RWA010 Rockwell Wave Artist - * codec chip used in the Corel Computer NetWinder. + * codec chip used in the Rebel.com NetWinder. * * Cleaned up and integrated into 2.1 by Russell King (rmk@arm.linux.org.uk) + * and Pat Beirne (patb@corel.ca) */ /* - * Copyright (C) by Corel Computer 1998 + * Copyright (C) by Rebel.com 1998-1999 * * RWA010 specs received under NDA from Rockwell * @@ -29,19 +30,17 @@ #define debug_flg (0) -#define DEB(x) -#define DDB(x) -#define DEB1(x) - #include <linux/module.h> #include <linux/config.h> #include <linux/sched.h> #include <linux/interrupt.h> #include <linux/delay.h> #include <linux/smp.h> +#include <linux/spinlock.h> #include <asm/dec21285.h> #include <asm/hardware.h> +#include <asm/system.h> #include "soundmodule.h" #include "sound_config.h" @@ -54,45 +53,24 @@ #define _ISA_IRQ(x) (x) #endif -#define VNC_TIMER_PERIOD (HZ/4) //check slider 4 times/sec - -#define MIXER_PRIVATE3_RESET 0x53570000 -#define MIXER_PRIVATE3_READ 0x53570001 -#define MIXER_PRIVATE3_WRITE 0x53570002 +#define POSSIBLE_RECORDING_DEVICES (SOUND_MASK_LINE |\ + SOUND_MASK_MIC |\ + SOUND_MASK_LINE1) -#define VNC_INTERNAL_SPKR 0x01 //the sw mute on/off control bit -#define VNC_INTERNAL_MIC 0x10 //the hw internal/handset mic bit - -/* Use RECSRC = speaker to mark the internal microphone - * - * Some cheating involved here: there is no way to relay - * to the system, which microphone in in use - * (left = handset, or right = internal) - * - * So while I do not flag SPEAKER in the Recording Devices - * Mask, when on internal - * - * mike - I set the speaker bit hi. Some mixers can be - * confused a bit... - */ - -#define POSSIBLE_RECORDING_DEVICES (SOUND_MASK_LINE |\ - SOUND_MASK_MIC |\ - SOUND_MASK_LINE1) //Line1 = analog phone - -#define SUPPORTED_MIXER_DEVICES (SOUND_MASK_SYNTH |\ - SOUND_MASK_PCM |\ - SOUND_MASK_LINE |\ - SOUND_MASK_MIC | \ - SOUND_MASK_LINE1 |\ - SOUND_MASK_RECLEV |\ - SOUND_MASK_VOLUME) +#define SUPPORTED_MIXER_DEVICES (SOUND_MASK_SYNTH |\ + SOUND_MASK_PCM |\ + SOUND_MASK_LINE |\ + SOUND_MASK_MIC |\ + SOUND_MASK_LINE1 |\ + SOUND_MASK_RECLEV |\ + SOUND_MASK_VOLUME |\ + SOUND_MASK_IMIX) static unsigned short levels[SOUND_MIXER_NRDEVICES] = { 0x5555, /* Master Volume */ 0x0000, /* Bass */ 0x0000, /* Treble */ - 0x5555, /* Synth (FM) */ + 0x2323, /* Synth (FM) */ 0x4b4b, /* PCM */ 0x0000, /* PC Speaker */ 0x0000, /* Ext Line */ @@ -129,15 +107,20 @@ typedef struct { int dev_no; /* Mixer parameters */ - unsigned short *levels; - int handset_state; - signed int slider_vol; /* hardware slider volume */ + unsigned short *levels; /* cache of volume settings */ int recmask; /* currently enabled recording device! */ - int supported_devices; /* SUPPORTED_MIXER_DEVICES */ + int supported_devices; /* SUPPORTED_MIXER_DEVICES */ int rec_devices; /* POSSIBLE_RECORDING_DEVICES */ - int handset_mute_sw :1;/* 1 - handset controlled in sw */ - int use_slider :1;/* use slider setting for o/p vol */ - int mute_state :1; + +#ifdef CONFIG_ARCH_NETWINDER + signed int slider_vol; /* hardware slider volume */ + unsigned int handset_detect :1; + unsigned int telephone_detect:1; + unsigned int no_autoselect :1;/* handset/telephone autoselects a path */ + unsigned int spkr_mute_state :1;/* set by ioctl or autoselect */ + unsigned int line_mute_state :1;/* set by ioctl or autoselect */ + unsigned int use_slider :1;/* use slider setting for o/p vol */ +#endif } wavnc_info; typedef struct wavnc_port_info { @@ -147,271 +130,18 @@ typedef struct wavnc_port_info { int audio_format; } wavnc_port_info; -static int nr_waveartist_devs; -static wavnc_info adev_info[MAX_AUDIO_DEV]; - -static int waveartist_mixer_set(wavnc_info *devc, int whichDev, unsigned int level); - -/* - * Corel Netwinder specifics... - */ -static struct timer_list vnc_timer; -extern spinlock_t gpio_lock; - -static void -vnc_mute(wavnc_info *devc, int mute) -{ - unsigned long flags; - - spin_lock_irqsave(&gpio_lock, flags); - cpld_modify(CPLD_UNMUTE, mute ? 0 : CPLD_UNMUTE); - spin_unlock_irqrestore(&gpio_lock, flags); - - devc->mute_state = mute; -} - -static int -vnc_volume_slider(wavnc_info *devc) -{ - static signed int old_slider_volume; - unsigned long flags; - signed int volume = 255; - - *CSR_TIMER1_LOAD = 0x00ffffff; - - save_flags(flags); - cli(); - - outb(0xFF, 0x201); - *CSR_TIMER1_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_DIV1; +static int nr_waveartist_devs; +static wavnc_info adev_info[MAX_AUDIO_DEV]; +static spinlock_t waveartist_lock = SPIN_LOCK_UNLOCKED; - while (volume && (inb(0x201) & 0x01)) - volume--; - - *CSR_TIMER1_CNTL = 0; - - restore_flags(flags); - - volume = 0x00ffffff - *CSR_TIMER1_VALUE; - - -#ifndef REVERSE - volume = 150 - (volume >> 5); -#else - volume = (volume >> 6) - 25; +#ifndef machine_is_netwinder +#define machine_is_netwinder() 0 #endif - if (volume < 0) - volume = 0; - - if (volume > 100) - volume = 100; - - /* - * slider quite often reads +-8, so debounce this random noise - */ - if ((volume - old_slider_volume) > 7 || - (old_slider_volume - volume) > 7) { - old_slider_volume = volume; - - DEB(printk("Slider volume: %d.\n", old_slider_volume)); - } - - return old_slider_volume; -} - -static int -vnc_slider(wavnc_info *devc) -{ - signed int slider_volume; - unsigned int temp; - - /* - * read the "buttons" state. - * Bit 4 = handset present, - * Bit 5 = offhook - */ - // the state should be "querable" via a private IOCTL call - temp = inb(0x201) & 0x30; - - if (!devc->handset_mute_sw && - (temp ^ devc->handset_state) & VNC_INTERNAL_MIC) { - devc->handset_state = temp; - devc->handset_mute_sw = 0; - - vnc_mute(devc, (temp & VNC_INTERNAL_MIC) ? 1 : 0); - } - - slider_volume = vnc_volume_slider(devc); - - /* - * If we're using software controlled volume, and - * the slider moves by more than 20%, then we - * switch back to slider controlled volume. - */ - if (devc->slider_vol > slider_volume) { - if (devc->slider_vol - slider_volume > 20) - devc->use_slider = 1; - } else { - if (slider_volume - devc->slider_vol > 20) - devc->use_slider = 1; - } - - /* - * use only left channel - */ - temp = levels[SOUND_MIXER_VOLUME] & 0xFF; - - if (slider_volume != temp && devc->use_slider) { - devc->slider_vol = slider_volume; - - waveartist_mixer_set(devc, SOUND_MIXER_VOLUME, - slider_volume | slider_volume << 8); - - return 1; - } - - return 0; -} - -static void -vnc_slider_tick(unsigned long data) -{ - int next_timeout; - - if (vnc_slider(adev_info + data)) - next_timeout = 5; // mixer reported change - else - next_timeout = VNC_TIMER_PERIOD; - - mod_timer(&vnc_timer, jiffies + next_timeout); -} - -static int -vnc_private_ioctl(int dev, unsigned int cmd, caddr_t arg) -{ - wavnc_info *devc = (wavnc_info *)audio_devs[dev]->devc; - int val, temp; - - if (cmd == SOUND_MIXER_PRIVATE1) { - /* - * Use this call to override the automatic handset - * behaviour - ignore handset. - * bit 7 = total control over handset - do not - * to plug/unplug - * bit 4 = internal mic - * bit 0 = mute internal speaker - */ - if (get_user(val, (int *)arg)) - return -EFAULT; - - devc->handset_mute_sw = val; - - temp = val & VNC_INTERNAL_SPKR; - if (devc->mute_state != temp) - vnc_mute(devc, temp); - - devc->handset_state = val & VNC_INTERNAL_MIC; - waveartist_mixer_set(devc, SOUND_MIXER_RECSRC, SOUND_MASK_MIC); - return 0; - } -#if 0 - if (cmd == SOUND_MIXER_PRIVATE2) { -#define VNC_SOUND_PAUSE 0x53 //to pause the DSP -#define VNC_SOUND_RESUME 0x57 //to unpause the DSP - int val; - - val = *(int *) arg; - - printk("MIXER_PRIVATE2: passed parameter = 0x%X.\n",val); - - if (val == VNC_SOUND_PAUSE) { - wa_sendcmd(0x16); //PAUSE the ADC - } else if (val == VNC_SOUND_RESUME) { - wa_sendcmd(0x18); //RESUME the ADC - } else { - return -EINVAL; //invalid parameters... - } - return 0; - } - - if (cmd == SOUND_MIXER_PRIVATE3) { - long unsigned flags; - int mixer_reg[15]; //reg 14 is actually a command: read,write,reset - - int val; - int i; - - val = *(int *) arg; - - if (verify_area(VERIFY_READ, (void *) val, sizeof(mixer_reg) == -EFAULT)) - return (-EFAULT); - - memcpy_fromfs(&mixer_reg, (void *) val, sizeof(mixer_reg)); - - if (mixer_reg[0x0E] == MIXER_PRIVATE3_RESET) { //reset command?? - wavnc_mixer_reset(devc); - return (0); - } else if (mixer_reg[0x0E] == MIXER_PRIVATE3_WRITE) { //write command?? -// printk("WaveArtist Mixer: Private write command.\n"); - - wa_sendcmd(0x32); //Pair1 - word 1 and 5 - wa_sendcmd(mixer_reg[0]); - wa_sendcmd(mixer_reg[4]); - - wa_sendcmd(0x32); //Pair2 - word 2 and 6 - wa_sendcmd(mixer_reg[1]); - wa_sendcmd(mixer_reg[5]); - - wa_sendcmd(0x32); //Pair3 - word 3 and 7 - wa_sendcmd(mixer_reg[2]); - wa_sendcmd(mixer_reg[6]); - - wa_sendcmd(0x32); //Pair4 - word 4 and 8 - wa_sendcmd(mixer_reg[3]); - wa_sendcmd(mixer_reg[7]); - - wa_sendcmd(0x32); //Pair5 - word 9 and 10 - wa_sendcmd(mixer_reg[8]); - wa_sendcmd(mixer_reg[9]); - - wa_sendcmd(0x0031); //set left and right PCM - wa_sendcmd(mixer_reg[0x0A]); - wa_sendcmd(mixer_reg[0x0B]); - - wa_sendcmd(0x0131); //set left and right FM - wa_sendcmd(mixer_reg[0x0C]); - wa_sendcmd(mixer_reg[0x0D]); - - return 0; - } else if (mixer_reg[0x0E] == MIXER_PRIVATE3_READ) { //read command? -// printk("WaveArtist Mixer: Private read command.\n"); - - //first read all current values... - save_flags(flags); - cli(); - - for (i = 0; i < 14; i++) { - wa_sendcmd((i << 8) + 0x30); // get ready for command nn30H - - while (!(inb(STATR) & CMD_RF)) { - }; //wait for response ready... - - mixer_reg[i] = inw(CMDR); - } - restore_flags(flags); - - if (verify_area(VERIFY_WRITE, (void *) val, sizeof(mixer_reg) == -EFAULT)) - return (-EFAULT); - - memcpy_tofs((void *) val, &mixer_reg, sizeof(mixer_reg)); - return 0; - } else - return -EINVAL; - } -#endif - return -EINVAL; -} +static struct timer_list vnc_timer; +static void vnc_configure_mixer(wavnc_info *devc); +static int vnc_private_ioctl(int dev, unsigned int cmd, caddr_t arg); +static void vnc_slider_tick(unsigned long data); static inline void waveartist_set_ctlr(struct address_info *hw, unsigned char clear, unsigned char set) @@ -472,7 +202,7 @@ waveartist_reset(wavnc_info *devc) } while (timeout--); if (timeout == 0) { - printk("WaveArtist: reset timeout "); + printk(KERN_WARNING "WaveArtist: reset timeout "); if (res != (unsigned int)-1) printk("(res=%04X)", res); printk("\n"); @@ -481,6 +211,10 @@ waveartist_reset(wavnc_info *devc) return 0; } +/* Helper function to send and receive words + * from WaveArtist. It handles all the handshaking + * and can send or receive multiple words. + */ static int waveartist_cmd(wavnc_info *devc, int nr_cmd, unsigned int *cmd, @@ -554,6 +288,32 @@ waveartist_cmd(wavnc_info *devc, return timed_out ? 1 : 0; } +/* + * Send one command word + */ +static inline int +waveartist_cmd1(wavnc_info *devc, unsigned int cmd) +{ + return waveartist_cmd(devc, 1, &cmd, 0, NULL); +} + +/* + * Send one command, receive one word + */ +static inline unsigned int +waveartist_cmd1_r(wavnc_info *devc, unsigned int cmd) +{ + unsigned int ret; + + waveartist_cmd(devc, 1, &cmd, 1, &ret); + + return ret; +} + +/* + * Send a double command, receive one + * word (and throw it away) + */ static inline int waveartist_cmd2(wavnc_info *devc, unsigned int cmd, unsigned int arg) { @@ -562,11 +322,12 @@ waveartist_cmd2(wavnc_info *devc, unsigned int cmd, unsigned int arg) vals[0] = cmd; vals[1] = arg; - waveartist_cmd(devc, 2, vals, 1, vals); - - return 0; + return waveartist_cmd(devc, 2, vals, 1, vals); } +/* + * Send a triple command + */ static inline int waveartist_cmd3(wavnc_info *devc, unsigned int cmd, unsigned int arg1, unsigned int arg2) @@ -581,72 +342,18 @@ waveartist_cmd3(wavnc_info *devc, unsigned int cmd, } static int -waveartist_sendcmd(struct address_info *hw, unsigned int cmd) +waveartist_getrev(wavnc_info *devc, char *rev) { - int count; + unsigned int temp[2]; + unsigned int cmd = WACMD_GETREV; - if (debug_flg & DEBUG_CMD) - printk("waveartist_sendcmd: cmd=0x%04X...", cmd); + waveartist_cmd(devc, 1, &cmd, 2, temp); - udelay(10); - - if (inb(hw->io_base + STATR) & CMD_RF) { - /* - * flush the port - */ - count = inw(hw->io_base + CMDR); - - udelay(10); - - if (debug_flg & DEBUG_CMD) - printk(" flushed %04X...", count); - } - - /* - * preset timeout at 5000 loops - */ - count = 5000; - - while (count --) - if (inb(hw->io_base + STATR) & CMD_WE) { - /* wait till CMD_WE is high - * then output the command - */ - outw(cmd, hw->io_base + CMDR); - break; - } - - /* ready BEFORE timeout? - */ - if (debug_flg & DEBUG_CMD) - printk(" %s\n", count ? "Done OK." : "Error!"); - - udelay(10); - - return count ? 0 : 1; -} - -static int -waveartist_getrev(struct address_info *hw, char *rev) -{ - int temp; - - waveartist_sendcmd(hw, 0); - udelay(20); - temp = inw(hw->io_base + CMDR); - udelay(20); - inw(hw->io_base + CMDR); // discard second word == 0 - - rev[0] = temp >> 8; - rev[1] = temp & 255; + rev[0] = temp[0] >> 8; + rev[1] = temp[0] & 255; rev[2] = '\0'; - return temp; -} - -inline void -waveartist_mute(wavnc_info *devc, int mute) -{ + return temp[0]; } static void waveartist_halt_output(int dev); @@ -667,10 +374,9 @@ waveartist_open(int dev, int mode) devc = (wavnc_info *) audio_devs[dev]->devc; portc = (wavnc_port_info *) audio_devs[dev]->portc; - save_flags(flags); - cli(); + spin_lock_irqsave(&waveartist_lock, flags); if (portc->open_mode || (devc->open_mode & mode)) { - restore_flags(flags); + spin_unlock_irqrestore(&waveartist_lock, flags); return -EBUSY; } @@ -683,13 +389,7 @@ waveartist_open(int dev, int mode) devc->record_dev = dev; if (mode & OPEN_WRITE) devc->playback_dev = dev; - restore_flags(flags); - - /* - * Mute output until the playback really starts. This - * decreases clicking (hope so). - */ - waveartist_mute(devc, 1); + spin_unlock_irqrestore(&waveartist_lock, flags); return 0; } @@ -701,8 +401,7 @@ waveartist_close(int dev) wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; unsigned long flags; - save_flags(flags); - cli(); + spin_lock_irqsave(&waveartist_lock, flags); waveartist_halt(dev); @@ -710,9 +409,7 @@ waveartist_close(int dev) devc->open_mode &= ~portc->open_mode; portc->open_mode = 0; - waveartist_mute(devc, 1); - - restore_flags(flags); + spin_unlock_irqrestore(&waveartist_lock, flags); } static void @@ -747,18 +444,17 @@ waveartist_output_block(int dev, unsigned long buf, int __count, int intrflag) */ } - save_flags(flags); - cli(); + spin_lock_irqsave(&waveartist_lock, flags); /* * set sample count */ - waveartist_cmd2(devc, 0x0024, count); + waveartist_cmd2(devc, WACMD_OUTPUTSIZE, count); devc->xfer_count = count; devc->audio_mode |= PCM_ENABLE_OUTPUT; - restore_flags(flags); + spin_unlock_irqrestore(&waveartist_lock, flags); } static void @@ -791,19 +487,17 @@ waveartist_start_input(int dev, unsigned long buf, int __count, int intrflag) */ } - save_flags(flags); - cli(); + spin_lock_irqsave(&waveartist_lock, flags); /* * set sample count */ - waveartist_cmd2(devc, 0x0014, count); - waveartist_mute(devc, 0); + waveartist_cmd2(devc, WACMD_INPUTSIZE, count); devc->xfer_count = count; devc->audio_mode |= PCM_ENABLE_INPUT; - restore_flags(flags); + spin_unlock_irqrestore(&waveartist_lock, flags); } static int @@ -869,40 +563,42 @@ waveartist_prepare_for_input(int dev, int bsize, int bcount) speed = waveartist_get_speed(portc); bits = waveartist_get_bits(portc); - save_flags(flags); - cli(); + spin_lock_irqsave(&waveartist_lock, flags); if (waveartist_cmd2(devc, WACMD_INPUTFORMAT, bits)) - printk("waveartist: error setting the record format to %d\n", - portc->audio_format); + printk(KERN_WARNING "waveartist: error setting the " + "record format to %d\n", portc->audio_format); if (waveartist_cmd2(devc, WACMD_INPUTCHANNELS, portc->channels)) - printk("waveartist: error setting record to %d channels\n", - portc->channels); + printk(KERN_WARNING "waveartist: error setting record " + "to %d channels\n", portc->channels); /* * write cmd SetSampleSpeedTimeConstant */ if (waveartist_cmd2(devc, WACMD_INPUTSPEED, speed)) - printk("waveartist: error setting the record speed " - "to %dHz.\n", portc->speed); + printk(KERN_WARNING "waveartist: error setting the record " + "speed to %dHz.\n", portc->speed); if (waveartist_cmd2(devc, WACMD_INPUTDMA, 1)) - printk("waveartist: error setting the record data path " - "to 0x%X\n", 1); + printk(KERN_WARNING "waveartist: error setting the record " + "data path to 0x%X\n", 1); if (waveartist_cmd2(devc, WACMD_INPUTFORMAT, bits)) - printk("waveartist: error setting the record format to %d\n", - portc->audio_format); + printk(KERN_WARNING "waveartist: error setting the record " + "format to %d\n", portc->audio_format); devc->xfer_count = 0; - restore_flags(flags); + spin_unlock_irqrestore(&waveartist_lock, flags); waveartist_halt_input(dev); if (debug_flg & DEBUG_INTR) { - printk("WA CTLR reg: 0x%02X.\n",inb(devc->hw.io_base + CTLR)); - printk("WA STAT reg: 0x%02X.\n",inb(devc->hw.io_base + STATR)); - printk("WA IRQS reg: 0x%02X.\n",inb(devc->hw.io_base + IRQSTAT)); + printk("WA CTLR reg: 0x%02X.\n", + inb(devc->hw.io_base + CTLR)); + printk("WA STAT reg: 0x%02X.\n", + inb(devc->hw.io_base + STATR)); + printk("WA IRQS reg: 0x%02X.\n", + inb(devc->hw.io_base + IRQSTAT)); } return 0; @@ -922,28 +618,27 @@ waveartist_prepare_for_output(int dev, int bsize, int bcount) speed = waveartist_get_speed(portc); bits = waveartist_get_bits(portc); - save_flags(flags); - cli(); + spin_lock_irqsave(&waveartist_lock, flags); if (waveartist_cmd2(devc, WACMD_OUTPUTSPEED, speed) && waveartist_cmd2(devc, WACMD_OUTPUTSPEED, speed)) - printk("waveartist: error setting the playback speed " - "to %dHz.\n", portc->speed); + printk(KERN_WARNING "waveartist: error setting the playback " + "speed to %dHz.\n", portc->speed); if (waveartist_cmd2(devc, WACMD_OUTPUTCHANNELS, portc->channels)) - printk("waveartist: error setting the playback to" - " %d channels\n", portc->channels); + printk(KERN_WARNING "waveartist: error setting the playback " + "to %d channels\n", portc->channels); if (waveartist_cmd2(devc, WACMD_OUTPUTDMA, 0)) - printk("waveartist: error setting the playback data path " - "to 0x%X\n", 0); + printk(KERN_WARNING "waveartist: error setting the playback " + "data path to 0x%X\n", 0); if (waveartist_cmd2(devc, WACMD_OUTPUTFORMAT, bits)) - printk("waveartist: error setting the playback format to %d\n", - portc->audio_format); + printk(KERN_WARNING "waveartist: error setting the playback " + "format to %d\n", portc->audio_format); devc->xfer_count = 0; - restore_flags(flags); + spin_unlock_irqrestore(&waveartist_lock, flags); waveartist_halt_output(dev); if (debug_flg & DEBUG_INTR) { @@ -961,7 +656,6 @@ waveartist_halt(int dev) wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; wavnc_info *devc; - if (portc->open_mode & OPEN_WRITE) waveartist_halt_output(dev); @@ -978,19 +672,13 @@ waveartist_halt_input(int dev) wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc; unsigned long flags; - save_flags(flags); - cli(); - - waveartist_mute(devc, 1); - -//RMK disable_dma(audio_devs[dev]->dmap_in->dma); + spin_lock_irqsave(&waveartist_lock, flags); /* * Stop capture */ - waveartist_sendcmd(&devc->hw, 0x17); + waveartist_cmd1(devc, WACMD_INPUTSTOP); -//RMK enable_dma(audio_devs[dev]->dmap_in->dma); devc->audio_mode &= ~PCM_ENABLE_INPUT; /* @@ -1002,7 +690,7 @@ waveartist_halt_input(int dev) // devc->audio_mode &= ~PCM_ENABLE_INPUT; - restore_flags(flags); + spin_unlock_irqrestore(&waveartist_lock, flags); } static void @@ -1011,16 +699,9 @@ waveartist_halt_output(int dev) wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc; unsigned long flags; - save_flags(flags); - cli(); - - waveartist_mute(devc, 1); - -//RMK disable_dma(audio_devs[dev]->dmap_out->dma); - - waveartist_sendcmd(&devc->hw, 0x27); + spin_lock_irqsave(&waveartist_lock, flags); -//RMK enable_dma(audio_devs[dev]->dmap_out->dma); + waveartist_cmd1(devc, WACMD_OUTPUTSTOP); devc->audio_mode &= ~PCM_ENABLE_OUTPUT; @@ -1033,7 +714,7 @@ waveartist_halt_output(int dev) // devc->audio_mode &= ~PCM_ENABLE_OUTPUT; - restore_flags(flags); + spin_unlock_irqrestore(&waveartist_lock, flags); } static void @@ -1052,8 +733,7 @@ waveartist_trigger(int dev, int state) printk("\n"); } - save_flags(flags); - cli(); + spin_lock_irqsave(&waveartist_lock, flags); state &= devc->audio_mode; @@ -1062,18 +742,16 @@ waveartist_trigger(int dev, int state) /* * enable ADC Data Transfer to PC */ - waveartist_sendcmd(&devc->hw, 0x15); + waveartist_cmd1(devc, WACMD_INPUTSTART); if (portc->open_mode & OPEN_WRITE && state & PCM_ENABLE_OUTPUT) /* * enable DAC data transfer from PC */ - waveartist_sendcmd(&devc->hw, 0x25); - - waveartist_mute(devc, 0); + waveartist_cmd1(devc, WACMD_OUTPUTSTART); - restore_flags(flags); + spin_unlock_irqrestore(&waveartist_lock, flags); } static int @@ -1158,7 +836,7 @@ waveartist_intr(int irq, void *dev_id, struct pt_regs *regs) if (status & IRQ_REQ) /* Clear interrupt */ waveartist_iack(devc); else - printk("waveartist: unexpected interrupt\n"); + printk(KERN_WARNING "waveartist: unexpected interrupt\n"); #ifdef CONFIG_AUDIO if (irqstatus & 0x01) { @@ -1175,12 +853,12 @@ waveartist_intr(int irq, void *dev_id, struct pt_regs *regs) temp = 0; } if (temp) //default: - printk("WaveArtist: Unknown interrupt\n"); + printk(KERN_WARNING "waveartist: Unknown interrupt\n"); } #endif if (irqstatus & 0x2) // We do not use SB mode natively... - printk("WaveArtist: Unexpected SB interrupt...\n"); + printk(KERN_WARNING "waveartist: Unexpected SB interrupt...\n"); } /* ------------------------------------------------------------------------- @@ -1198,6 +876,9 @@ waveartist_mixer_update(wavnc_info *devc, int whichDev) #define SCALE(lev,max) ((lev) * (max) / 100) + if (machine_is_netwinder() && whichDev == SOUND_MIXER_PHONEOUT) + whichDev = SOUND_MIXER_VOLUME; + switch(whichDev) { case SOUND_MIXER_VOLUME: mask = 0x000e; @@ -1208,6 +889,8 @@ waveartist_mixer_update(wavnc_info *devc, int whichDev) break; case SOUND_MIXER_LINE: + if ((devc->recmask & SOUND_MASK_LINE) == 0) + return; mask = 0x07c0; reg_l = 0x000; reg_r = 0x400; @@ -1216,6 +899,8 @@ waveartist_mixer_update(wavnc_info *devc, int whichDev) break; case SOUND_MIXER_MIC: + if ((devc->recmask & SOUND_MASK_MIC) == 0) + return; mask = 0x0030; reg_l = 0x200; reg_r = 0x600; @@ -1232,6 +917,8 @@ waveartist_mixer_update(wavnc_info *devc, int whichDev) break; case SOUND_MIXER_LINE1: + if ((devc->recmask & SOUND_MASK_LINE1) == 0) + return; mask = 0x003e; reg_l = 0x000; reg_r = 0x400; @@ -1240,12 +927,14 @@ waveartist_mixer_update(wavnc_info *devc, int whichDev) break; case SOUND_MIXER_PCM: - waveartist_cmd3(devc, 0x0031, SCALE(lev_left, 32767), + waveartist_cmd3(devc, WACMD_SET_LEVEL, + SCALE(lev_left, 32767), SCALE(lev_right, 32767)); return; case SOUND_MIXER_SYNTH: - waveartist_cmd3(devc, 0x0131, SCALE(lev_left, 32767), + waveartist_cmd3(devc, 0x0100 | WACMD_SET_LEVEL, + SCALE(lev_left, 32767), SCALE(lev_right, 32767)); return; @@ -1254,7 +943,7 @@ waveartist_mixer_update(wavnc_info *devc, int whichDev) } /* read left setting */ - vals[0] = reg_l + 0x30; + vals[0] = reg_l + WACMD_GET_LEVEL; waveartist_cmd(devc, 1, vals, 1, vals + 1); /* read right setting */ @@ -1265,7 +954,7 @@ waveartist_mixer_update(wavnc_info *devc, int whichDev) vals[2] = (vals[2] & ~mask) | (lev_right & mask); /* write left,right back */ - vals[0] = 0x32; + vals[0] = WACMD_SET_MIXER; waveartist_cmd(devc, 3, vals, 0, NULL); } @@ -1273,20 +962,6 @@ static void waveartist_select_input(wavnc_info *devc, unsigned int input) { unsigned int vals[3]; -#if 1 - /* New mixer programming - switch recording source - * using R/L_ADC_Mux_Select. We are playing with - * left/right mux bit fields in reg 9. - * - * We can not switch Mux_Select while recording, so - * for microphones, enable both left and right and - * play with levels only! - * - * Unfortunately, we need to select the src of mono - * recording (left or right) before starting the - * recording - so can not dynamically switch between - * handset amd internal microphones... - */ /* * Get reg 9 @@ -1304,48 +979,14 @@ waveartist_select_input(wavnc_info *devc, unsigned int input) printk("RECSRC: old left: 0x%04X, old right: 0x%04X.\n", vals[1] & 0x07, (vals[1] >> 3) & 0x07); - vals[1] &= ~0x03F; //kill current left/right mux input select + /* + * kill current left/right mux input select + */ + vals[1] &= ~0x03F; switch (input) { - /* - * Handset or internal MIC - */ case SOUND_MASK_MIC: /* - * handset not plugged in? - */ - if (devc->handset_state & VNC_INTERNAL_MIC) { - /* - * set mono recording from right mic - */ - waveartist_sendcmd(&devc->hw, 0x0134); -#if 0 - /* - * right=mic, left=none - */ - vals[1] |= 0x0028; - /* - * pretend int mic - */ - devc->rec_devices |= SOUND_MASK_SPEAKER; -#endif - } else { - /* - * set mono rec from left mic - */ - waveartist_sendcmd(&devc->hw, 0x0034); -#if 0 - /* - * right=none, left=mic - */ - vals[1] |= 0x0005; - /* - * show no int mic - */ - devc->rec_devices &= ~SOUND_MASK_SPEAKER; -#endif - } - /* * right=mic, left=mic */ vals[1] |= 0x002D; @@ -1353,10 +994,6 @@ waveartist_select_input(wavnc_info *devc, unsigned int input) case SOUND_MASK_LINE1: /* - * set mono rec from left aux1 - */ - waveartist_sendcmd(&devc->hw, 0x0034); - /* * right=none, left=Aux1; */ vals[1] |= 0x0004; @@ -1364,10 +1001,6 @@ waveartist_select_input(wavnc_info *devc, unsigned int input) case SOUND_MASK_LINE: /* - * set mono rec from left (default) - */ - waveartist_sendcmd(&devc->hw, 0x0034); - /* * right=Line, left=Line; */ vals[1] |= 0x0012; @@ -1378,101 +1011,10 @@ waveartist_select_input(wavnc_info *devc, unsigned int input) printk("RECSRC %d: left=0x%04X, right=0x%04X.\n", input, vals[1] & 0x07, (vals[1] >> 3) & 0x07); -#else - /* This part is good, if input connected to - * a mixer, so can be used for record-only modes... - */ - - /* - * get reg 4 - */ - vals[0] = 0x0330; - waveartist_cmd(devc, 1, vals, 1, vals + 1); - - /* - * get reg 8 - */ - vals[0] = 0x0730; - waveartist_cmd(devc, 1, vals, 1, vals + 2); - - if (debug_flg & DEBUG_MIXER) - printk("RECSRC: old left: 0x%04X, old right: 0x%04X.\n", - vals[1], vals[2]); - - /* - * kill current left/right mux input select - */ - vals[1] &= ~0x07F8; - vals[2] &= ~0x07F8; - - switch (input) { - /* - * handset or internal mic - */ - case SOUND_MASK_MIC: - /* - * handset not plugged in? - */ - if (devc->handset_state & VNC_INTERNAL_MIC) { - /* - * set mono recording from right mic - */ - waveartist_sendcmd(&devc->hw, 0x0134); - /* - * left = none, right = mic, RX filter gain - */ - vals[1] |= 0x0C00; - vals[2] |= 0x0C88; - /* - * pretend int mic - */ - devc->rec_devices |= SOUND_MASK_SPEAKER; - } else { - /* - * set mono rec from left mic - */ - waveartist_sendcmd(&devc->hw, 0x0034); - /* - * left = mic, RX filter gain, right = none; - */ - vals[1] |= 0x0C88; - vals[2] |= 0x0C00; - /* - * show no int mic - */ - devc->rec_devices &= ~SOUND_MASK_SPEAKER; - } - break; - - case SOUND_MASK_LINE1: - /* - * set mono rec from left aux1 - */ - waveartist_sendcmd(&devc->hw, 0x0034); - /* - * left = Aux1, right = none - */ - vals[1] |= 0x0C40; - vals[2] |= 0x0C00; - break; - - case SOUND_MASK_LINE: - /* - * left = Line, right = Line - */ - vals[1] |= 0x0C10; - vals[2] |= 0x0C10; - break; - } - - if (debug_flg & DEBUG_MIXER) - printk("RECSRC %d: left(4) 0x%04X, right(8) 0x%04X.\n", - level, vals[1], vals[2]); -#endif /* * and finally - write the reg pair back.... */ - vals[0] = 0x32; + vals[0] = WACMD_SET_MIXER; waveartist_cmd(devc, 3, vals, 0, NULL); } @@ -1482,8 +1024,7 @@ waveartist_mixer_set(wavnc_info *devc, int whichDev, unsigned int level) { unsigned int lev_left = level & 0x007f; unsigned int lev_right = (level & 0x7f00) >> 8; - - int left, right, devmask, changed, i; + int left, right, devmask; left = level & 0x7f; right = (level & 0x7f00) >> 8; @@ -1493,85 +1034,38 @@ waveartist_mixer_set(wavnc_info *devc, int whichDev, unsigned int level) whichDev, level); switch (whichDev) { - /* Master volume (0-7) - * We have 3 bits on the Left/Right Mixer Gain, - * bits 3,2,1 on 3 and 7 - */ - case SOUND_MIXER_VOLUME: - devc->levels[whichDev] = lev_left | lev_right << 8; - waveartist_mixer_update(devc, whichDev); - break; - - - /* External line (0-31) - * use LOUT/ROUT bits 10...6, reg 1 and 5 - */ - case SOUND_MIXER_LINE: - devc->levels[whichDev] = lev_left | lev_right << 8; - waveartist_mixer_update(devc, whichDev); - break; - - /* Mono microphone (0-3) mute, - * 0db,10db,20db - */ - case SOUND_MIXER_MIC: -#if 1 + case SOUND_MIXER_VOLUME: /* master volume (0-7) */ + case SOUND_MIXER_LINE: /* external line (0-31) */ + case SOUND_MIXER_MIC: /* mono mic (0-3) */ + case SOUND_MIXER_RECLEV: /* recording level (0-7) */ + case SOUND_MIXER_LINE1: /* mono external aux1 (0-31) */ + case SOUND_MIXER_PCM: /* Waveartist PCM (0-32767) */ + case SOUND_MIXER_SYNTH: /* internal synth (0-31) */ + case SOUND_MIXER_IMIX: /* recording feedback */ devc->levels[whichDev] = lev_left | lev_right << 8; -#else - /* we do not need to mute volume of - * an unused mic - it is simply unused... - */ - if (devc->handset_state & VNC_INTERNAL_MIC) - devc->levels[whichDev] = lev_right << 8; - else - levels[whichDev] = lev_left; -#endif waveartist_mixer_update(devc, whichDev); break; - /* Recording level (0-7) - */ - case SOUND_MIXER_RECLEV: - devc->levels[whichDev] = lev_left | lev_right << 8; - waveartist_mixer_update(devc, whichDev); - break; - - /* Mono External Aux1 (0-31) - * use LINE1 bits 5...1, reg 1 and 5 - */ - case SOUND_MIXER_LINE1: - devc->levels[whichDev] = lev_left | lev_right << 8; - waveartist_mixer_update(devc, whichDev); - break; - - /* WaveArtist PCM (0-32767) - */ - case SOUND_MIXER_PCM: - devc->levels[whichDev] = lev_left | lev_right << 8; - waveartist_mixer_update(devc, whichDev); - break; - - /* Internal synthesizer (0-31) - */ - case SOUND_MIXER_SYNTH: - devc->levels[whichDev] = lev_left | lev_right << 8; - waveartist_mixer_update(devc, whichDev); - break; - - /* Select recording input source */ case SOUND_MIXER_RECSRC: - devmask = level & POSSIBLE_RECORDING_DEVICES; + devmask = level & devc->rec_devices; - changed = devmask ^ devc->recmask; - devc->recmask = devmask; +#ifdef CONFIG_ARCH_NETWINDER + if (machine_is_netwinder()) + vnc_configure_mixer(devc); + else +#endif + { + waveartist_select_input(devc, level); - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) - if (changed & (1 << i)) - waveartist_mixer_update(devc, i); + /* + * if record monitoring is on, make sure the bit is set + */ + if (devc->levels[SOUND_MIXER_IMIX]) + waveartist_mixer_update(devc, SOUND_MIXER_IMIX); + } - waveartist_select_input(devc, level); /* * do not save in "levels", return current setting */ @@ -1595,53 +1089,54 @@ waveartist_mixer_reset(wavnc_info *devc) /* * reset mixer cmd */ - waveartist_sendcmd(&devc->hw, 0x33); + waveartist_cmd1(devc, WACMD_RST_MIXER); /* - * set input for ADC to come from - * a mux (left and right) == reg 9, - * initially none + * set input for ADC to come from 'quiet' + * turn on default modes */ - waveartist_cmd3(devc, 0x0032, 0x9800, 0xa836); + waveartist_cmd3(devc, WACMD_SET_MIXER, 0x9800, 0xa836); /* * set mixer input select to none, RX filter gains 0 db */ - waveartist_cmd3(devc, 0x0032, 0x4c00, 0x8c00); + waveartist_cmd3(devc, WACMD_SET_MIXER, 0x4c00, 0x8c00); /* * set bit 0 reg 2 to 1 - unmute MonoOut */ - waveartist_cmd3(devc, 0x0032, 0x2801, 0x6800); - - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) - waveartist_mixer_update(devc, i); + waveartist_cmd3(devc, WACMD_SET_MIXER, 0x2801, 0x6800); /* set default input device = internal mic * current recording device = none - * no handset */ devc->recmask = 0; - devc->handset_state = VNC_INTERNAL_MIC; -// waveartist_mixer_set(devc, SOUND_MIXER_RECSRC, SOUND_MASK_MIC); - /* - * start from enabling the hw setting - */ - devc->handset_mute_sw = 0; + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) + waveartist_mixer_update(devc, i); + devc->supported_devices = SUPPORTED_MIXER_DEVICES; devc->rec_devices = POSSIBLE_RECORDING_DEVICES; + + if (machine_is_netwinder()) { + devc->supported_devices |= SOUND_MASK_PHONEIN | SOUND_MASK_PHONEOUT; + devc->rec_devices |= SOUND_MASK_PHONEIN; + } } static int waveartist_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) { wavnc_info *devc = (wavnc_info *)audio_devs[dev]->devc; + int ret; - if (cmd == SOUND_MIXER_PRIVATE1 || - cmd == SOUND_MIXER_PRIVATE2 || - cmd == SOUND_MIXER_PRIVATE3) - return vnc_private_ioctl(dev, cmd, arg); +#ifdef CONFIG_ARCH_NETWINDER + if (machine_is_netwinder()) { + ret = vnc_private_ioctl(dev, cmd, arg); + if (ret != -ENOIOCTLCMD) + return ret; + } +#endif if (((cmd >> 8) & 0xff) == 'M') { if (_SIOC_DIR(cmd) & _SIOC_WRITE) { @@ -1650,30 +1145,14 @@ waveartist_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) if (get_user(val, (int *)arg)) return -EFAULT; - /* - * special case for master volume: if we - * received this call - switch from hw - * volume control to a software volume - * control, till the hw volume is modified - * to signal that user wants to be back in - * hardware... - */ - if ((cmd & 0xff) == SOUND_MIXER_VOLUME) - devc->use_slider = 0; - return waveartist_mixer_set(devc, cmd & 0xff, val); } else { - int ret; - /* * Return parameters */ switch (cmd & 0xff) { case SOUND_MIXER_RECSRC: ret = devc->recmask; - - if (devc->handset_state & VNC_INTERNAL_MIC) - ret |= SOUND_MASK_SPEAKER; break; case SOUND_MIXER_DEVMASK: @@ -1700,7 +1179,7 @@ waveartist_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) return -EINVAL; } - return put_user(ret, (int *)arg) ? -EINVAL : 0; + return put_user(ret, (int *)arg) ? -EFAULT : 0; } } @@ -1725,7 +1204,7 @@ waveartist_init(wavnc_info *devc) sprintf(dev_name, "%s (%s", devc->hw.name, devc->chip_name); - if (waveartist_getrev(&devc->hw, rev)) { + if (waveartist_getrev(devc, rev)) { strcat(dev_name, " rev. "); strcat(dev_name, rev); } @@ -1758,20 +1237,20 @@ waveartist_init(wavnc_info *devc) waveartist_iack(devc); if (request_irq(devc->hw.irq, waveartist_intr, 0, devc->hw.name, devc) < 0) { - printk("%s: IRQ %d in use\n", + printk(KERN_ERR "%s: IRQ %d in use\n", devc->hw.name, devc->hw.irq); goto uninstall; } if (sound_alloc_dma(devc->hw.dma, devc->hw.name)) { - printk("%s: Can't allocate DMA%d\n", + printk(KERN_ERR "%s: Can't allocate DMA%d\n", devc->hw.name, devc->hw.dma); goto uninstall_irq; } if (devc->hw.dma != devc->hw.dma2 && devc->hw.dma2 != NO_DMA) if (sound_alloc_dma(devc->hw.dma2, devc->hw.name)) { - printk("%s: can't allocate DMA%d\n", + printk(KERN_ERR "%s: can't allocate DMA%d\n", devc->hw.name, devc->hw.dma2); goto uninstall_dma; } @@ -1809,22 +1288,24 @@ probe_waveartist(struct address_info *hw_config) wavnc_info *devc = &adev_info[nr_waveartist_devs]; if (nr_waveartist_devs >= MAX_AUDIO_DEV) { - printk("waveartist: too many audio devices\n"); + printk(KERN_WARNING "waveartist: too many audio devices\n"); return 0; } if (check_region(hw_config->io_base, 15)) { - printk("WaveArtist: I/O port conflict\n"); + printk(KERN_WARNING "WaveArtist: I/O port conflict\n"); return 0; } if (hw_config->irq > _ISA_IRQ(15) || hw_config->irq < _ISA_IRQ(0)) { - printk("WaveArtist: Bad IRQ %d\n", hw_config->irq); + printk(KERN_WARNING "WaveArtist: Bad IRQ %d\n", + hw_config->irq); return 0; } if (hw_config->dma != _ISA_DMA(3)) { - printk("WaveArtist: Bad DMA %d\n", hw_config->dma); + printk(KERN_WARNING "WaveArtist: Bad DMA %d\n", + hw_config->dma); return 0; } @@ -1864,15 +1345,18 @@ attach_waveartist(struct address_info *hw) if (devc->dev_no < 0) release_region(hw->io_base, 15); else { - init_timer(&vnc_timer); - vnc_timer.function = vnc_slider_tick; - vnc_timer.expires = jiffies; - vnc_timer.data = nr_waveartist_devs; - add_timer(&vnc_timer); - +#ifdef CONFIG_ARCH_NETWINDER + if (machine_is_netwinder()) { + init_timer(&vnc_timer); + vnc_timer.function = vnc_slider_tick; + vnc_timer.expires = jiffies; + vnc_timer.data = nr_waveartist_devs; + add_timer(&vnc_timer); + + vnc_configure_mixer(devc); + } +#endif nr_waveartist_devs += 1; - - vnc_mute(devc, 0); } } @@ -1891,6 +1375,11 @@ unload_waveartist(struct address_info *hw) if (devc != NULL) { int mixer; +#ifdef CONFIG_ARCH_NETWINDER + if (machine_is_netwinder()) + del_timer(&vnc_timer); +#endif + release_region(devc->hw.io_base, 15); waveartist_set_ctlr(&devc->hw, DMA1_IE|DMA0_IE, 0); @@ -1904,8 +1393,6 @@ unload_waveartist(struct address_info *hw) devc->hw.dma2 != NO_DMA) sound_free_dma(devc->hw.dma2); - del_timer(&vnc_timer); - mixer = audio_devs[devc->dev_no]->mixer_dev; if (mixer >= 0) @@ -1919,7 +1406,356 @@ unload_waveartist(struct address_info *hw) for (; i < nr_waveartist_devs; i++) adev_info[i] = adev_info[i + 1]; } else - printk("waveartist: can't find device to unload\n"); + printk(KERN_WARNING "waveartist: can't find device " + "to unload\n"); +} + +/* + * Rebel.com Netwinder specifics... + */ + +#define VNC_TIMER_PERIOD (HZ/4) //check slider 4 times/sec + +#define MIXER_PRIVATE3_RESET 0x53570000 +#define MIXER_PRIVATE3_READ 0x53570001 +#define MIXER_PRIVATE3_WRITE 0x53570002 + +#define VNC_MUTE_INTERNAL_SPKR 0x01 //the sw mute on/off control bit +#define VNC_MUTE_LINE_OUT 0x10 +#define VNC_PHONE_DETECT 0x20 +#define VNC_HANDSET_DETECT 0x40 +#define VNC_DISABLE_AUTOSWITCH 0x80 + +extern spinlock_t gpio_lock; + +static inline void +vnc_update_spkr_mute(wavnc_info *devc) +{ + unsigned long flags; + + spin_lock_irqsave(&gpio_lock, flags); + cpld_modify(CPLD_UNMUTE, devc->spkr_mute_state ? 0 : CPLD_UNMUTE); + spin_unlock_irqrestore(&gpio_lock, flags); +} + +static void +vnc_mute_lout(wavnc_info *devc, int mute) +{ +} + +static int +vnc_volume_slider(wavnc_info *devc) +{ + static signed int old_slider_volume; + unsigned long flags; + signed int volume = 255; + + *CSR_TIMER1_LOAD = 0x00ffffff; + + save_flags(flags); + cli(); + + outb(0xFF, 0x201); + *CSR_TIMER1_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_DIV1; + + while (volume && (inb(0x201) & 0x01)) + volume--; + + *CSR_TIMER1_CNTL = 0; + + restore_flags(flags); + + volume = 0x00ffffff - *CSR_TIMER1_VALUE; + + +#ifndef REVERSE + volume = 150 - (volume >> 5); +#else + volume = (volume >> 6) - 25; +#endif + + if (volume < 0) + volume = 0; + + if (volume > 100) + volume = 100; + + /* + * slider quite often reads +-8, so debounce this random noise + */ + if (abs(volume - old_slider_volume) > 7) { + old_slider_volume = volume; + + if (debug_flg & DEBUG_MIXER) + printk(KERN_DEBUG "Slider volume: %d.\n", volume); + } + + return old_slider_volume; +} + +static void +vnc_configure_mixer(wavnc_info *devc) +{ + u_int vals[3]; + + if (!devc->no_autoselect) { + if (devc->handset_detect) { + devc->recmask = SOUND_MASK_LINE1; + devc->spkr_mute_state = devc->line_mute_state = 1; + } else if (devc->telephone_detect) { + devc->recmask = SOUND_MASK_PHONEIN; + devc->spkr_mute_state = devc->line_mute_state = 1; + } else { + /* unless someone has asked for LINE-IN, + * we default to MIC + */ + if ((devc->recmask & SOUND_MASK_LINE) == 0) + devc->recmask = SOUND_MASK_MIC; + devc->spkr_mute_state = devc->line_mute_state = 0; + } + vnc_update_spkr_mute(devc); + vnc_mute_lout(devc, devc->spkr_mute_state); + } + + /* Ok. At this point, we have done the autoswitch logic, or we + * have had a command from an ioctl. We have a valid devc->recmask. + * Now we have to connect up the hardware to reflect the recmask. + */ + vals[1] = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | 0x800); + vals[2] = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | 0x900); + + vals[1] &= ~0x3f; + + switch(devc->recmask) { + case SOUND_MASK_MIC: /* builtin mic */ + waveartist_cmd1(devc, WACMD_SET_MONO | 0x100); /* right */ + vals[1] |= 0x28; + break; + + case SOUND_MASK_LINE1: /* out handset */ + waveartist_cmd1(devc, WACMD_SET_MONO); /* left */ + vals[1] |= 0x05; + break; + + case SOUND_MASK_PHONEIN: /* our telephone mic */ + waveartist_cmd1(devc, WACMD_SET_MONO); /* left */ + vals[1] |= 0x04; + break; + + case SOUND_MASK_LINE: /* stereo line in */ + vals[1] |= 12; + break; + + default: + return; + } + + vals[0] = WACMD_SET_MIXER; + waveartist_cmd(devc, 3, vals, 0, NULL); + + waveartist_mixer_update(devc, SOUND_MIXER_IMIX); +} + +static int +vnc_slider(wavnc_info *devc) +{ + signed int slider_volume; + unsigned int temp, old_hs, old_td; + + /* + * read the "buttons" state. + * Bit 4 = handset present, + * Bit 5 = offhook + */ + temp = inb(0x201) & 0x30; + + old_hs = devc->handset_detect; + old_td = devc->telephone_detect; + + devc->handset_detect = !(temp & 0x10); + devc->telephone_detect = !!(temp & 0x20); + + if (!devc->no_autoselect && + (old_hs != devc->handset_detect || + old_td != devc->telephone_detect)) + vnc_configure_mixer(devc); + + slider_volume = vnc_volume_slider(devc); + + /* + * If we're using software controlled volume, and + * the slider moves by more than 20%, then we + * switch back to slider controlled volume. + */ + if (abs(devc->slider_vol - slider_volume) > 20) + devc->use_slider = 1; + + /* + * use only left channel + */ + temp = levels[SOUND_MIXER_VOLUME] & 0xFF; + + if (slider_volume != temp && devc->use_slider) { + devc->slider_vol = slider_volume; + + waveartist_mixer_set(devc, SOUND_MIXER_VOLUME, + slider_volume | slider_volume << 8); + + return 1; + } + + return 0; +} + +static void +vnc_slider_tick(unsigned long data) +{ + int next_timeout; + + if (vnc_slider(adev_info + data)) + next_timeout = 5; // mixer reported change + else + next_timeout = VNC_TIMER_PERIOD; + + mod_timer(&vnc_timer, jiffies + next_timeout); +} + +static int +vnc_private_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + wavnc_info *devc = (wavnc_info *)audio_devs[dev]->devc; + int val; + + switch (cmd) { + case SOUND_MIXER_PRIVATE1: + { + u_int prev_spkr_mute, prev_line_mute, prev_auto_state; + int val; + + get_user_ret(val, (int *)arg, -EFAULT); + + /* check if parameter is logical */ + if (val & ~(VNC_MUTE_INTERNAL_SPKR | + VNC_MUTE_LINE_OUT | + VNC_DISABLE_AUTOSWITCH)) + return -EINVAL; + + prev_auto_state = devc->no_autoselect; + prev_spkr_mute = devc->spkr_mute_state; + prev_line_mute = devc->line_mute_state; + + devc->no_autoselect = (val & VNC_DISABLE_AUTOSWITCH) ? 1 : 0; + devc->spkr_mute_state = (val & VNC_MUTE_INTERNAL_SPKR) ? 1 : 0; + devc->line_mute_state = (val & VNC_MUTE_LINE_OUT) ? 1 : 0; + + if (prev_spkr_mute != devc->spkr_mute_state) + vnc_update_spkr_mute(devc); + + if (prev_line_mute != devc->line_mute_state) + vnc_mute_lout(devc, devc->line_mute_state); + + if (prev_auto_state != devc->no_autoselect) + vnc_configure_mixer(devc); + waveartist_mixer_set(devc, SOUND_MIXER_RECSRC, SOUND_MASK_MIC); + return 0; + } + + case SOUND_MIXER_PRIVATE2: + get_user_ret(val, (int *)arg, -EFAULT); + + switch (val) { +#define VNC_SOUND_PAUSE 0x53 //to pause the DSP +#define VNC_SOUND_RESUME 0x57 //to unpause the DSP + case VNC_SOUND_PAUSE: + waveartist_cmd1(devc, 0x16); + break; + + case VNC_SOUND_RESUME: + waveartist_cmd1(devc, 0x18); + break; + + default: + return -EINVAL; + } + return 0; + + /* private ioctl to allow bulk access to waveartist */ + case SOUND_MIXER_PRIVATE3: + { + unsigned long flags; + int mixer_reg[15], i, val; + + get_user_ret(val, (int *)arg, -EFAULT); + copy_from_user_ret(mixer_reg, (void *)val, sizeof(mixer_reg), -EFAULT); + + switch (mixer_reg[14]) { + case MIXER_PRIVATE3_RESET: + waveartist_mixer_reset(devc); + break; + + case MIXER_PRIVATE3_WRITE: + waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[0], mixer_reg[4]); + waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[1], mixer_reg[5]); + waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[2], mixer_reg[6]); + waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[3], mixer_reg[7]); + waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[8], mixer_reg[9]); + + waveartist_cmd3(devc, WACMD_SET_LEVEL, mixer_reg[10], mixer_reg[11]); + waveartist_cmd3(devc, WACMD_SET_LEVEL, mixer_reg[12], mixer_reg[13]); + break; + + case MIXER_PRIVATE3_READ: + spin_lock_irqsave(&waveartist_lock, flags); + + for (i = 0x30; i < 14 << 8; i += 1 << 8) + waveartist_cmd(devc, 1, &i, 1, mixer_reg + (i >> 8)); + + spin_unlock_irqrestore(&waveartist_lock, flags); + + copy_to_user_ret((void *)val, mixer_reg, sizeof(mixer_reg), -EFAULT); + break; + + default: + return -EINVAL; + } + return 0; + } + + /* read back the state from PRIVATE1 */ + case SOUND_MIXER_PRIVATE4: + val = (devc->spkr_mute_state ? VNC_MUTE_INTERNAL_SPKR : 0) | + (devc->line_mute_state ? VNC_MUTE_LINE_OUT : 0) | + (devc->handset_detect ? VNC_HANDSET_DETECT : 0) | + (devc->telephone_detect ? VNC_PHONE_DETECT : 0) | + (devc->no_autoselect ? VNC_DISABLE_AUTOSWITCH : 0); + + return put_user(val, (int *)arg) ? -EFAULT : 0; + } + + if (((cmd >> 8) & 0xff) == 'M') { + if (_SIOC_DIR(cmd) & _SIOC_WRITE) { + /* + * special case for master volume: if we + * received this call - switch from hw + * volume control to a software volume + * control, till the hw volume is modified + * to signal that user wants to be back in + * hardware... + */ + if ((cmd & 0xff) == SOUND_MIXER_VOLUME) + devc->use_slider = 0; + } else if ((cmd & 0xff) == SOUND_MIXER_STEREODEVS) { + val = devc->supported_devices & + ~(SOUND_MASK_IMIX | + SOUND_MASK_MIC | + SOUND_MASK_LINE1 | + SOUND_MASK_PHONEIN | + SOUND_MASK_PHONEOUT); + return put_user(val, (int *)arg) ? -EFAULT : 0; + } + } + + return -ENOIOCTLCMD; } #ifdef MODULE diff --git a/drivers/sound/waveartist.h b/drivers/sound/waveartist.h index 45b62d1b5..2d2163fa8 100644 --- a/drivers/sound/waveartist.h +++ b/drivers/sound/waveartist.h @@ -1,5 +1,8 @@ - -// def file for Rockwell RWA010 chip set, as installed in Corel NetWinder +/* + * linux/drivers/sound/waveartist.h + * + * def file for Rockwell RWA010 chip set, as installed in Rebel.com NetWinder + */ //registers #define CMDR 0 @@ -33,7 +36,8 @@ //commands -#define WACMD_SYSTEMID 0 +#define WACMD_SYSTEMID 0x00 +#define WACMD_GETREV 0x00 #define WACMD_INPUTFORMAT 0x10 //0-8S, 1-16S, 2-8U #define WACMD_INPUTCHANNELS 0x11 //1-Mono, 2-Stereo #define WACMD_INPUTSPEED 0x12 //sampling rate @@ -56,8 +60,11 @@ #define WACMD_OUTPUTRESUME 0x28 //resume ADC #define WACMD_OUTPUTPIO 0x29 //PIO ADC +#define WACMD_GET_LEVEL 0x30 +#define WACMD_SET_LEVEL 0x31 +#define WACMD_SET_MIXER 0x32 +#define WACMD_RST_MIXER 0x33 +#define WACMD_SET_MONO 0x34 - - -int wa_sendcmd(unsigned int cmd); -int wa_writecmd(unsigned int cmd, unsigned int arg); +int wa_sendcmd(unsigned int cmd); +int wa_writecmd(unsigned int cmd, unsigned int arg); diff --git a/drivers/usb/cpia.c b/drivers/usb/cpia.c index 02df61455..976ab02da 100644 --- a/drivers/usb/cpia.c +++ b/drivers/usb/cpia.c @@ -356,7 +356,7 @@ goto error; int i; int y, u, y1, v, r, g, b; - /* We want atleast 2 bytes for the length */ + /* We want at least 2 bytes for the length */ if (scratch_left(data) < 2) goto out; @@ -536,8 +536,9 @@ static int cpia_compress_isochronous(struct usb_cpia *cpia, struct usb_isoc_desc return totlen; } -static int cpia_isoc_irq(int status, void *__buffer, int len, void *dev_id) +static int cpia_isoc_irq(int status, void *__buffer, int len, void *isocdesc) { + void *dev_id = ((struct usb_isoc_desc *)isocdesc)->context; struct usb_cpia *cpia = (struct usb_cpia *)dev_id; struct cpia_sbuf *sbuf; int i; @@ -584,6 +585,12 @@ static int cpia_init_isoc(struct usb_cpia *cpia) cpia->cursbuf = 0; cpia->scratchlen = 0; + /* Alternate interface 3 is is the biggest frame size */ + if (usb_set_interface(cpia->dev, 1, 3) < 0) { + printk("usb_set_interface error\n"); + return -EBUSY; + } + /* We double buffer the Iso lists */ err = usb_init_isoc(dev, usb_rcvisocpipe(dev, 1), FRAMES_PER_DESC, cpia, &cpia->sbuf[0].isodesc); @@ -618,7 +625,7 @@ static int cpia_init_isoc(struct usb_cpia *cpia) for (fx = 0; fx < FRAMES_PER_DESC; fx++) id->frames[fx].frame_length = FRAME_SIZE_PER_DESC; - /* and the desc. [1] */ + /* and for desc. [1] */ id = cpia->sbuf[1].isodesc; id->start_type = 0; /* will follow the first desc. */ id->callback_frames = 10; /* on every 10th frame */ @@ -628,17 +635,16 @@ static int cpia_init_isoc(struct usb_cpia *cpia) for (fx = 0; fx < FRAMES_PER_DESC; fx++) id->frames[fx].frame_length = FRAME_SIZE_PER_DESC; - usb_run_isoc(cpia->sbuf[0].isodesc, NULL); - usb_run_isoc(cpia->sbuf[1].isodesc, cpia->sbuf[0].isodesc); + err = usb_run_isoc(cpia->sbuf[0].isodesc, NULL); + if (err) + printk(KERN_ERR "CPiA USB driver error (%d) on usb_run_isoc\n", err); + err = usb_run_isoc(cpia->sbuf[1].isodesc, cpia->sbuf[0].isodesc); + if (err) + printk(KERN_ERR "CPiA USB driver error (%d) on usb_run_isoc\n", err); #ifdef CPIA_DEBUG printk("done scheduling\n"); #endif - /* Alternate interface 3 is is the biggest frame size */ - if (usb_set_interface(cpia->dev, 1, 3) < 0) { - printk("usb_set_interface error\n"); - return -EBUSY; - } #if 0 if (usb_cpia_grab_frame(dev, 120) < 0) { @@ -915,11 +921,8 @@ static int cpia_ioctl(struct video_device *dev, unsigned int cmd, void *arg) p.brightness = 180 << 8; /* XXX */ p.contrast = 192 << 8; /* XXX */ p.whiteness = 105 << 8; /* XXX */ -#if 0 p.depth = 24; -#endif - p.depth = 16; - p.palette = VIDEO_PALETTE_YUYV; + p.palette = VIDEO_PALETTE_RGB24; if (copy_to_user(arg, &p, sizeof(p))) return -EFAULT; @@ -940,6 +943,8 @@ static int cpia_ioctl(struct video_device *dev, unsigned int cmd, void *arg) #ifdef CPIA_DEBUG printk("Attempting to set palette %d, depth %d\n", p.palette, p.depth); + printk("SPICT: brightness=%d, hue=%d, colour=%d, contrast=%d, whiteness=%d\n", + p.brightness, p.hue, p.colour, p.contrast, p.whiteness); #endif return 0; @@ -1009,7 +1014,6 @@ static int cpia_ioctl(struct video_device *dev, unsigned int cmd, void *arg) { struct video_mmap vm; - if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm))) return -EFAULT; @@ -1090,11 +1094,26 @@ printk("back from sleeping\n"); return cpia_new_frame(cpia, -1); } + case VIDIOCGFBUF: + { + struct video_buffer vb; + +#ifdef CPIA_DEBUG + printk("GFBUF\n"); +#endif + + memset(&vb, 0, sizeof(vb)); + vb.base = NULL; /* frame buffer not supported, not used */ + + if (copy_to_user((void *)arg, (void *)&vb, sizeof(vb))) + return -EFAULT; + + return 0; + } case VIDIOCKEY: return 0; case VIDIOCCAPTURE: return -EINVAL; - case VIDIOCGFBUF: case VIDIOCSFBUF: return -EINVAL; case VIDIOCGTUNER: @@ -1333,4 +1352,3 @@ void cleanup_module(void) usb_cpia_cleanup(); } #endif - diff --git a/drivers/usb/hp_scanner.c b/drivers/usb/hp_scanner.c index e69d8f63e..1da9f605a 100644 --- a/drivers/usb/hp_scanner.c +++ b/drivers/usb/hp_scanner.c @@ -192,7 +192,7 @@ read_scanner(struct file * file, char * buffer, if (partial) { count = this_read = partial; - } else if (result == USB_ST_TIMEOUT || result == 15) { + } else if (result == USB_ST_TIMEOUT || result == 15) { /* FIXME: 15 ??? */ if(!maxretry--) { printk(KERN_DEBUG "read_scanner: maxretry timeout\n"); return -ETIME; diff --git a/drivers/usb/inits.h b/drivers/usb/inits.h index 1d9acb1a4..b3b45f59a 100644 --- a/drivers/usb/inits.h +++ b/drivers/usb/inits.h @@ -1,13 +1,14 @@ -int usb_kbd_init(void); +int usb_acm_init(void); int usb_audio_init(void); int usb_hub_init(void); -int usb_acm_init(void); -int usb_printer_init(void); void usb_hub_cleanup(void); +int usb_kbd_init(void); +void usb_major_init(void); void usb_mouse_cleanup(void); -int usb_scsi_init(void); int usb_hp_scanner_init(void); void usb_hp_scanner_cleanup(void); +int usb_printer_init(void); int proc_usb_init (void); void proc_usb_cleanup (void); +int usb_scsi_init(void); int usb_serial_init (void); diff --git a/drivers/usb/printer.c b/drivers/usb/printer.c index dffd76559..8bc2cfc1c 100644 --- a/drivers/usb/printer.c +++ b/drivers/usb/printer.c @@ -25,37 +25,47 @@ #define NAK_TIMEOUT (HZ) /* stall wait for printer */ #define MAX_RETRY_COUNT ((60*60*HZ)/NAK_TIMEOUT) /* should not take 1 minute a page! */ +#define BIG_BUF_SIZE 8192 + +/* + * USB Printer Requests + */ +#define USB_PRINTER_REQ_GET_DEVICE_ID 0 +#define USB_PRINTER_REQ_GET_PORT_STATUS 1 +#define USB_PRINTER_REQ_SOFT_RESET 2 + #define MAX_PRINTERS 8 struct pp_usb_data { struct usb_device *pusb_dev; - __u8 isopen; /* nz if open */ - __u8 noinput; /* nz if no input stream */ + __u8 isopen; /* True if open */ + __u8 noinput; /* True if no input stream */ __u8 minor; /* minor number of device */ __u8 status; /* last status from device */ int maxin, maxout; /* max transfer size in and out */ char *obuf; /* transfer buffer (out only) */ wait_queue_head_t wait_q; /* for timeouts */ unsigned int last_error; /* save for checking */ + int bulk_in_ep; /* Bulk IN endpoint */ + int bulk_out_ep; /* Bulk OUT endpoint */ + int bulk_in_index; /* endpoint[bulk_in_index] */ + int bulk_out_index; /* endpoint[bulk_out_index] */ }; static struct pp_usb_data *minor_data[MAX_PRINTERS]; #define PPDATA(x) ((struct pp_usb_data *)(x)) -unsigned char printer_read_status(struct pp_usb_data *p) +static unsigned char printer_read_status(struct pp_usb_data *p) { __u8 status; - devrequest dr; struct usb_device *dev = p->pusb_dev; - dr.requesttype = USB_TYPE_CLASS | USB_RT_INTERFACE | 0x80; - dr.request = 1; - dr.value = 0; - dr.index = 0; - dr.length = 1; - if (dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, &status, 1, HZ)) { - return 0; + if (usb_control_msg(dev, usb_rcvctrlpipe(dev,0), + USB_PRINTER_REQ_GET_PORT_STATUS, + USB_TYPE_CLASS | USB_RT_INTERFACE | USB_DIR_IN, + 0, 0, &status, 1, HZ)) { + return 0; } return status; } @@ -90,24 +100,21 @@ static int printer_check_status(struct pp_usb_data *p) return status; } -void printer_reset(struct pp_usb_data *p) +static void printer_reset(struct pp_usb_data *p) { - devrequest dr; struct usb_device *dev = p->pusb_dev; - dr.requesttype = USB_TYPE_CLASS | USB_RECIP_OTHER; - dr.request = 2; - dr.value = 0; - dr.index = 0; - dr.length = 0; - dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0, HZ); + usb_control_msg(dev, usb_sndctrlpipe(dev,0), + USB_PRINTER_REQ_SOFT_RESET, + USB_TYPE_CLASS | USB_RECIP_OTHER, + 0, 0, NULL, 0, HZ); } static int open_printer(struct inode * inode, struct file * file) { struct pp_usb_data *p; - if(MINOR(inode->i_rdev) >= MAX_PRINTERS || + if (MINOR(inode->i_rdev) >= MAX_PRINTERS || !minor_data[MINOR(inode->i_rdev)]) { return -ENODEV; } @@ -141,7 +148,7 @@ static int close_printer(struct inode * inode, struct file * file) p->isopen = 0; file->private_data = NULL; /* free the resources if the printer is no longer around */ - if(!p->pusb_dev) { + if (!p->pusb_dev) { minor_data[p->minor] = NULL; kfree(p); } @@ -158,12 +165,7 @@ static ssize_t write_printer(struct file * file, unsigned long partial; int result = USB_ST_NOERROR; int maxretry; - int endpoint_num; - struct usb_interface_descriptor *interface; - interface = p->pusb_dev->config->interface->altsetting; - endpoint_num = (interface->endpoint[1].bEndpointAddress & 0x0f); - do { char *obuf = p->obuf; unsigned long thistime; @@ -179,7 +181,7 @@ static ssize_t write_printer(struct file * file, return bytes_written ? bytes_written : -EINTR; } result = p->pusb_dev->bus->op->bulk_msg(p->pusb_dev, - usb_sndbulkpipe(p->pusb_dev, endpoint_num), + usb_sndbulkpipe(p->pusb_dev, p->bulk_out_ep), obuf, thistime, &partial, HZ*20); if (partial) { obuf += partial; @@ -187,7 +189,7 @@ static ssize_t write_printer(struct file * file, maxretry = MAX_RETRY_COUNT; } if (result == USB_ST_TIMEOUT) { /* NAK - so hold for a while */ - if(!maxretry--) + if (!maxretry--) return -ETIME; interruptible_sleep_on_timeout(&p->wait_q, NAK_TIMEOUT); continue; @@ -214,21 +216,15 @@ static ssize_t read_printer(struct file * file, char * buffer, size_t count, loff_t *ppos) { struct pp_usb_data *p = file->private_data; - int read_count; + int read_count = 0; int this_read; char buf[64]; unsigned long partial; int result; - int endpoint_num; - struct usb_interface_descriptor *interface; - interface = p->pusb_dev->config->interface->altsetting; - endpoint_num = (interface->endpoint[0].bEndpointAddress & 0x0f); - if (p->noinput) return -EINVAL; - read_count = 0; while (count) { if (signal_pending(current)) { return read_count ? read_count : -EINTR; @@ -238,7 +234,7 @@ static ssize_t read_printer(struct file * file, this_read = (count > sizeof(buf)) ? sizeof(buf) : count; result = p->pusb_dev->bus->op->bulk_msg(p->pusb_dev, - usb_rcvbulkpipe(p->pusb_dev, endpoint_num), + usb_rcvbulkpipe(p->pusb_dev, p->bulk_in_ep), buf, this_read, &partial, HZ*20); /* unlike writes, we don't retry a NAK, just stop now */ @@ -266,8 +262,8 @@ static int printer_probe(struct usb_device *dev) /* * FIXME - this will not cope with combined printer/scanners */ - if ((dev->descriptor.bDeviceClass != 7 && - dev->descriptor.bDeviceClass != 0) || + if ((dev->descriptor.bDeviceClass != USB_CLASS_PRINTER && + dev->descriptor.bDeviceClass != 0) || dev->descriptor.bNumConfigurations != 1 || dev->config[0].bNumInterfaces != 1) { return -1; @@ -275,34 +271,50 @@ static int printer_probe(struct usb_device *dev) interface = &dev->config[0].interface[0].altsetting[0]; - /* Lets be paranoid (for the moment)*/ - if (interface->bInterfaceClass != 7 || + /* Let's be paranoid (for the moment). */ + if (interface->bInterfaceClass != USB_CLASS_PRINTER || interface->bInterfaceSubClass != 1 || - (interface->bInterfaceProtocol != 2 && interface->bInterfaceProtocol != 1)|| + (interface->bInterfaceProtocol != 2 && interface->bInterfaceProtocol != 1) || interface->bNumEndpoints > 2) { return -1; } - if ((interface->endpoint[0].bEndpointAddress & 0xf0) != 0x00 || - interface->endpoint[0].bmAttributes != 0x02 || - (interface->bNumEndpoints > 1 && ( - (interface->endpoint[1].bEndpointAddress & 0xf0) != 0x80 || - interface->endpoint[1].bmAttributes != 0x02))) { + /* Does this (these) interface(s) support bulk transfers? */ + if ((interface->endpoint[0].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + != USB_ENDPOINT_XFER_BULK) { return -1; } + if ((interface->bNumEndpoints > 1) && + ((interface->endpoint[1].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + != USB_ENDPOINT_XFER_BULK)) { + return -1; + } + + /* + * Does this interface have at least one OUT endpoint + * that we can write to: endpoint index 0 or 1? + */ + if ((interface->endpoint[0].bEndpointAddress & USB_ENDPOINT_DIR_MASK) + != USB_DIR_OUT && + (interface->bNumEndpoints > 1 && + (interface->endpoint[1].bEndpointAddress & USB_ENDPOINT_DIR_MASK) + != USB_DIR_OUT)) { + return -1; + } for (i=0; i<MAX_PRINTERS; i++) { if (!minor_data[i]) break; } if (i >= MAX_PRINTERS) { + printk("No minor table space available for USB Printer\n"); return -1; } printk(KERN_INFO "USB Printer found at address %d\n", dev->devnum); if (!(dev->private = kmalloc(sizeof(struct pp_usb_data), GFP_KERNEL))) { - printk( KERN_DEBUG "usb_printer: no memory!\n"); + printk(KERN_DEBUG "usb_printer: no memory!\n"); return -1; } @@ -310,48 +322,63 @@ static int printer_probe(struct usb_device *dev) minor_data[i] = PPDATA(dev->private); minor_data[i]->minor = i; minor_data[i]->pusb_dev = dev; - /* The max packet size can't be more than 64 (& will be 64 for - * any decent bulk device); this calculation was silly. -greg - * minor_data[i]->maxout = interface->endpoint[0].wMaxPacketSize * 16; - */ - minor_data[i]->maxout = 8192; - if (minor_data[i]->maxout > PAGE_SIZE) { - minor_data[i]->maxout = PAGE_SIZE; - } - if (interface->bInterfaceProtocol != 2) + minor_data[i]->maxout = (BIG_BUF_SIZE > PAGE_SIZE) ? PAGE_SIZE : BIG_BUF_SIZE; + if (interface->bInterfaceProtocol != 2) /* if not bidirectional */ minor_data[i]->noinput = 1; - else { - minor_data[i]->maxin = interface->endpoint[1].wMaxPacketSize; + + minor_data[i]->bulk_out_index = + ((interface->endpoint[0].bEndpointAddress & USB_ENDPOINT_DIR_MASK) + == USB_DIR_OUT) ? 0 : 1; + minor_data[i]->bulk_in_index = minor_data[i]->noinput ? -1 : + (minor_data[i]->bulk_out_index == 0) ? 1 : 0; + minor_data[i]->bulk_in_ep = minor_data[i]->noinput ? -1 : + interface->endpoint[minor_data[i]->bulk_in_index].bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK; + minor_data[i]->bulk_out_ep = + interface->endpoint[minor_data[i]->bulk_out_index].bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK; + if (interface->bInterfaceProtocol == 2) { /* if bidirectional */ + minor_data[i]->maxin = + interface->endpoint[minor_data[i]->bulk_in_index].wMaxPacketSize; } if (usb_set_configuration(dev, dev->config[0].bConfigurationValue)) { printk(KERN_INFO " Failed usb_set_configuration: printer\n"); return -1; } + + printk(KERN_INFO "USB Printer Summary:\n"); + printk(KERN_INFO "index=%d, maxout=%d, noinput=%d\n", + i, minor_data[i]->maxout, minor_data[i]->noinput); + printk(KERN_INFO "bulk_in_ix=%d, bulk_in_ep=%d, bulk_out_ix=%d, bulk_out_ep=%d\n", + minor_data[i]->bulk_in_index, + minor_data[i]->bulk_in_ep, + minor_data[i]->bulk_out_index, + minor_data[i]->bulk_out_ep); + #if 0 { __u8 status; __u8 ieee_id[64]; - devrequest dr; - - /* Lets get the device id if possible */ - dr.requesttype = USB_TYPE_CLASS | USB_RT_INTERFACE | 0x80; - dr.request = 0; - dr.value = 0; - dr.index = 0; - dr.length = sizeof(ieee_id) - 1; - if (dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, ieee_id, sizeof(ieee_id)-1, HZ) == 0) { + + /* Let's get the device id if possible. */ + if (usb_control_msg(dev, usb_rcvctrlpipe(dev,0), + USB_PRINTER_REQ_GET_DEVICE_ID, + USB_TYPE_CLASS | USB_RT_INTERFACE | USB_DIR_IN, + 0, 0, ieee_id, + sizeof(ieee_id)-1, HZ) == 0) { if (ieee_id[1] < sizeof(ieee_id) - 1) ieee_id[ieee_id[1]+2] = '\0'; else ieee_id[sizeof(ieee_id)-1] = '\0'; - printk(KERN_INFO " Printer ID is %s\n", &ieee_id[2]); + printk(KERN_INFO " USB Printer ID is %s\n", + &ieee_id[2]); } status = printer_read_status(PPDATA(dev->private)); printk(KERN_INFO " Status is %s,%s,%s\n", - (status & 0x10) ? "Selected" : "Not Selected", - (status & 0x20) ? "No Paper" : "Paper", - (status & 0x08) ? "No Error" : "Error"); + (status & LP_PSELECD) ? "Selected" : "Not Selected", + (status & LP_POUTPA) ? "No Paper" : "Paper", + (status & LP_PERRORP) ? "No Error" : "Error"); } #endif return 0; @@ -397,7 +424,13 @@ static struct usb_driver printer_driver = { int usb_printer_init(void) { - usb_register(&printer_driver); + if (usb_register(&printer_driver)) { + printk(KERN_ERR "USB Printer driver cannot register: " + "minor number %d already in use\n", + printer_driver.minor); + return 1; + } + printk(KERN_INFO "USB Printer support registered.\n"); return 0; } diff --git a/drivers/usb/uhci.c b/drivers/usb/uhci.c index ae2b4ec73..20be3faab 100644 --- a/drivers/usb/uhci.c +++ b/drivers/usb/uhci.c @@ -91,7 +91,7 @@ static int uhci_kill_isoc (struct usb_isoc_desc *isocdesc); /* * Map status to standard result codes * - * <status> is (td->status & 0xFE0000) [a.k.a. uhci_status_bits(td->status) + * <status> is (td->status & 0xFE0000) [a.k.a. uhci_status_bits(td->status)] * <dir_out> is True for output TDs and False for input TDs. */ static int uhci_map_status(int status, int dir_out) @@ -127,7 +127,7 @@ static int uhci_td_result(struct uhci_device *dev, struct uhci_td *td, unsigned { unsigned int status; struct uhci_td *tmp; - int count = 1000, bytesreceived = 0; + int count = 1000, actlength, explength; if (rval) *rval = 0; @@ -147,29 +147,26 @@ static int uhci_td_result(struct uhci_device *dev, struct uhci_td *td, unsigned if (status) break; -#if 0 - if (debug) { - /* Must reset the toggle on first error */ - if (uhci_debug) { - printk(KERN_DEBUG "Set toggle from %x rval %ld\n", - (unsigned int)tmp, rval ? *rval : 0); - } - usb_settoggle(dev->usb, uhci_endpoint(tmp->info), - uhci_packetout(tmp->info) ^ 1, - uhci_toggle(tmp->info)); - break; - } - } else { - if (rval && ((tmp->info & 0xFF) == USB_PID_IN)) - *rval += uhci_actual_length(tmp->status); - } -#endif /* The length field is only valid if the TD was completed */ if (!(tmp->status & TD_CTRL_ACTIVE) && uhci_packetin(tmp->info)) { - bytesreceived += uhci_actual_length(tmp->status); + explength = uhci_expected_length(tmp->info); + actlength = uhci_actual_length(tmp->status); if (rval) - *rval += uhci_actual_length(tmp->status); + *rval += actlength; + if (explength != actlength) { + /* Reset the data toggle on error. */ + if (debug || uhci_debug) + printk(KERN_DEBUG "Set toggle from %p rval %ld%c for status=%x to %d, exp=%d, act=%d\n", + tmp, rval ? *rval : 0, + rval ? '*' : '/', tmp->status, + uhci_toggle(tmp->info) ^ 1, + explength, actlength); + usb_settoggle(dev->usb, uhci_endpoint(tmp->info), + uhci_packetout(tmp->info), + uhci_toggle(tmp->info) ^ 1); + break; // Short packet + } } if ((tmp->link & UHCI_PTR_TERM) || @@ -190,26 +187,24 @@ static int uhci_td_result(struct uhci_device *dev, struct uhci_td *td, unsigned if (!status) return USB_ST_NOERROR; - /* XXX FIXME APC BackUPS Pro kludge */ + /* APC BackUPS Pro kludge */ /* It tries to send all of the descriptor instead of */ /* the amount we requested */ if (tmp->status & TD_CTRL_IOC && tmp->status & TD_CTRL_ACTIVE && - tmp->status & TD_CTRL_NAK /* && its a control TD */) + tmp->status & TD_CTRL_NAK && + tmp->pipetype == PIPE_CONTROL) return USB_ST_NOERROR; -#if 0 /* We got to an error, but the controller hasn't finished */ - /* with it, yet */ + /* with it yet. */ if (tmp->status & TD_CTRL_ACTIVE) return USB_ST_NOCHANGE; -#endif /* If this wasn't the last TD and SPD is set, ACTIVE */ /* is not and NAK isn't then we received a short */ /* packet */ - if (tmp->status & TD_CTRL_SPD && - !(tmp->status & TD_CTRL_NAK)) + if (tmp->status & TD_CTRL_SPD && !(tmp->status & TD_CTRL_NAK)) return USB_ST_NOERROR; } @@ -516,7 +511,7 @@ static void uhci_remove_irq_list(struct uhci_td *td) * bit set. Maybe it could be extended to handle the QH's also, * but it doesn't seem necessary right now. * The layout looks as follows: - * frame list pointer -> iso td's (if any) -> + * frame list pointer -> iso td's (if any) -> * periodic interrupt td (if framelist 0) -> irq qh -> control qh -> bulk qh */ @@ -532,7 +527,7 @@ static void uhci_add_frame_list(struct uhci *uhci, struct uhci_td *td, unsigned td->backptr = &uhci->fl->frame[framenum]; td->link = uhci->fl->frame[framenum]; if (!(td->link & (UHCI_PTR_TERM | UHCI_PTR_QH))) { - nexttd = (struct uhci_td *)bus_to_virt(td->link & ~15); + nexttd = (struct uhci_td *)uhci_ptr_to_virt(td->link); nexttd->backptr = &td->link; } wmb(); @@ -550,7 +545,7 @@ static void uhci_remove_frame_list(struct uhci *uhci, struct uhci_td *td) spin_lock_irqsave(&framelist_lock, flags); *(td->backptr) = td->link; if (!(td->link & (UHCI_PTR_TERM | UHCI_PTR_QH))) { - nexttd = (struct uhci_td *)bus_to_virt(td->link & ~15); + nexttd = (struct uhci_td *)uhci_ptr_to_virt(td->link); nexttd->backptr = td->backptr; } spin_unlock_irqrestore(&framelist_lock, flags); @@ -568,7 +563,7 @@ static void uhci_remove_frame_list(struct uhci *uhci, struct uhci_td *td) unsigned frn = inw(uhci->io_addr + USBFRNUM); __u32 link = uhci->fl->frame[frn % UHCI_NUMFRAMES]; if (!(link & (UHCI_PTR_TERM | UHCI_PTR_QH))) { - struct uhci_td *tdl = (struct uhci_td *)bus_to_virt(link & ~15); + struct uhci_td *tdl = (struct uhci_td *)uhci_ptr_to_virt(link); for (;;) { if (tdl == td) { printk(KERN_WARNING "uhci_remove_frame_list: td possibly still in use!!\n"); @@ -576,9 +571,9 @@ static void uhci_remove_frame_list(struct uhci *uhci, struct uhci_td *td) } if (tdl->link & (UHCI_PTR_TERM | UHCI_PTR_QH)) break; - tdl = (struct uhci_td *)bus_to_virt(tdl->link & ~15); + tdl = (struct uhci_td *)uhci_ptr_to_virt(tdl->link); } - } + } } } @@ -603,6 +598,7 @@ static void uhci_remove_transfer(struct uhci_td *td, char removeirq) /* Remove it from the skeleton */ uhci_remove_qh(td->qh->skel, td->qh); uhci_qh_free(td->qh); + do { nextlink = curtd->link; @@ -612,10 +608,11 @@ static void uhci_remove_transfer(struct uhci_td *td, char removeirq) uhci_remove_td(curtd); uhci_td_free(curtd); + if (nextlink & UHCI_PTR_TERM) /* Tail? */ break; - curtd = bus_to_virt(nextlink & ~UHCI_PTR_BITS); + curtd = (struct uhci_td *)uhci_ptr_to_virt(nextlink); if (!--maxcount) { printk(KERN_ERR "runaway td's!?\n"); break; @@ -624,7 +621,7 @@ static void uhci_remove_transfer(struct uhci_td *td, char removeirq) } /* - * Request a interrupt handler.. + * Request an interrupt handler.. * * Returns 0 (success) or negative (failure). * Also returns/sets a "handle pointer" that release_irq can use to stop this @@ -665,9 +662,8 @@ static int uhci_request_irq(struct usb_device *usb_dev, unsigned int pipe, td->bandwidth_alloc = bustime; /* if period 0, set _REMOVE flag */ - if (period == 0) { + if (!period) td->flags |= UHCI_TD_REMOVE; - } qh->skel = &dev->uhci->skelqh[__interval_to_skel(period)]; @@ -690,7 +686,7 @@ static int uhci_request_irq(struct usb_device *usb_dev, unsigned int pipe, * * This function can NOT be called from an interrupt. */ -int uhci_release_irq(struct usb_device *usb, void *handle) +static int uhci_release_irq(struct usb_device *usb, void *handle) { struct uhci_td *td; struct uhci_qh *qh; @@ -699,12 +695,13 @@ int uhci_release_irq(struct usb_device *usb, void *handle) if (!td) return USB_ST_INTERNALERROR; + qh = td->qh; + /* Remove it from the internal irq_list */ uhci_remove_irq_list(td); /* Remove the interrupt TD and QH */ uhci_remove_td(td); - qh = td->qh; uhci_remove_qh(qh->skel, qh); if (td->completed != NULL) @@ -744,7 +741,7 @@ static int uhci_get_current_frame_number(struct usb_device *usb_dev) */ static int uhci_init_isoc (struct usb_device *usb_dev, unsigned int pipe, - int frame_count, /* bandwidth % = 100 * this / 1000 */ + int frame_count, /* bandwidth % = 100 * this / 1024 */ void *context, struct usb_isoc_desc **isocdesc) { @@ -896,7 +893,7 @@ static int uhci_run_isoc (struct usb_isoc_desc *isocdesc, } } /* end START_RELATIVE */ else - if (isocdesc->start_type == START_ABSOLUTE) { /* within the scope of cur_frame */ + if (isocdesc->start_type == START_ABSOLUTE) { /* within the scope of cur_frame */ ix = USB_WRAP_FRAMENR(isocdesc->start_frame - cur_frame); if (ix < START_FRAME_FUDGE || /* too small */ ix > CAN_SCHEDULE_FRAMES) { /* too large */ @@ -999,6 +996,7 @@ static int uhci_run_isoc (struct usb_isoc_desc *isocdesc, td->completed = isocdesc->callback_fn; uhci_add_irq_list(dev->uhci, td, isocdesc->callback_fn, isocdesc); /* TBD: D.K. ??? */ } + return 0; } /* end uhci_run_isoc */ @@ -1028,6 +1026,7 @@ static int uhci_kill_isoc (struct usb_isoc_desc *isocdesc) } for (ix = 0, td = isocdesc->td; ix < isocdesc->frame_count; ix++, td++) { + /* Deactivate and unlink */ uhci_remove_frame_list(uhci, td); td->status &= ~(TD_CTRL_ACTIVE | TD_CTRL_IOC); } /* end for ix */ @@ -1044,9 +1043,9 @@ static void uhci_free_isoc (struct usb_isoc_desc *isocdesc) if (isocdesc->start_frame >= 0) uhci_kill_isoc(isocdesc); - /* Remove all td's from the IRQ list. */ - for(i = 0; i < isocdesc->frame_count; i++) - uhci_remove_irq_list(((struct uhci_td *)(isocdesc->td))+i); + /* Remove all TD's from the IRQ list. */ + for (i = 0; i < isocdesc->frame_count; i++) + uhci_remove_irq_list(((struct uhci_td *)isocdesc->td) + i); /* Free the associate memory. */ if (isocdesc->td) @@ -1137,7 +1136,8 @@ static int uhci_run_control(struct uhci_device *dev, struct uhci_td *first, stru * there is no restriction on length of transfers * anymore */ -static int uhci_control_msg(struct usb_device *usb_dev, unsigned int pipe, devrequest *cmd, void *data, int len, int timeout) +static int uhci_control_msg(struct usb_device *usb_dev, unsigned int pipe, devrequest *cmd, + void *data, int len, int timeout) { struct uhci_device *dev = usb_to_uhci(usb_dev); struct uhci_td *first, *td, *prevtd; @@ -1147,6 +1147,9 @@ static int uhci_control_msg(struct usb_device *usb_dev, unsigned int pipe, devre __u32 nextlink; unsigned long bytesrequested = len; unsigned long bytesread = 0; +#ifdef DUMP_RAW + unsigned char *orig_data = (unsigned char *) data; +#endif first = td = uhci_td_alloc(dev); if (!td) @@ -1176,8 +1179,10 @@ static int uhci_control_msg(struct usb_device *usb_dev, unsigned int pipe, devre prevtd = td; td = uhci_td_alloc(dev); - if (!td) - return -ENOMEM; + if (!td) { + uhci_td_free(prevtd); + return -ENOMEM; + } prevtd->link = virt_to_bus(td) | UHCI_PTR_DEPTH; @@ -1261,9 +1266,29 @@ static int uhci_control_msg(struct usb_device *usb_dev, unsigned int pipe, devre if (uhci_debug && ret) { __u8 *p = (__u8 *)cmd; + printk("dev %d, pipe %X requested %ld bytes, got %ld, status=%d:\n", + usb_dev->devnum, pipe, bytesrequested, bytesread, ret); printk(KERN_DEBUG "Failed cmd - %02X %02X %02X %02X %02X %02X %02X %02X\n", p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); } + +#ifdef DUMP_RAW + if (!ret && usb_pipein(pipe)) { /* good Input control msg */ + int i; + + printk (KERN_CRIT "ctrl msg [%02x %02x %04x %04x %04x] on pipe %x returned:\n", + cmd->requesttype, cmd->request, + cmd->value, cmd->index, cmd->length, pipe); + for (i = 0; i < bytesrequested; ) { + printk(" %02x", orig_data[i]); + if (++i % 16 == 0) + printk("\n"); + } + if (i % 16 != 0) + printk("\n"); + } +#endif + return ret; } @@ -1344,7 +1369,7 @@ static int uhci_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *da if (!td) return -ENOMEM; - prevtd = first; //This is fake, but at least it's not NULL + prevtd = first; // This is fake, but at least it's not NULL while (len > 0) { /* Build the TD for control status */ int pktsze = len; @@ -1376,7 +1401,7 @@ static int uhci_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *da usb_dotoggle(usb_dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)); } - td->link = 1; /* Terminate */ + td->link = UHCI_PTR_TERM; /* Terminate */ td->status |= TD_CTRL_IOC; /* CHANGE DIRECTION HERE! SAVE IT SOMEWHERE IN THE ENDPOINT!!! */ @@ -1619,7 +1644,6 @@ static void uhci_check_configuration(struct uhci *uhci) } while (nr < maxchild); } -#if 0 static int fixup_isoc_desc (struct uhci_td *td) { struct usb_isoc_desc *isocdesc = td->dev_id; @@ -1647,7 +1671,7 @@ static int fixup_isoc_desc (struct uhci_td *td) if ((frm->frame_status = uhci_map_status (uhci_status_bits (prtd->status), uhci_packetout (prtd->info)))) isocdesc->error_count++; - + prtd++; frm++; if (++fx >= isocdesc->frame_count) { /* wrap fx, prtd, and frm */ @@ -1671,8 +1695,106 @@ static int fixup_isoc_desc (struct uhci_td *td) return 0; } -#endif /* 0 */ +int uhci_isoc_callback(struct uhci *uhci, struct uhci_td *td, int status, unsigned long rval) +{ + struct usb_isoc_desc *isocdesc = td->dev_id; + int ret; + + /* + * Fixup the isocdesc for the driver: total_completed_frames, + * error_count, total_length, frames array. + * + * ret = callback_fn (int error_count, void *buffer, + * int len, void *isocdesc); + */ + + fixup_isoc_desc (td); + + ret = td->completed (isocdesc->error_count, bus_to_virt (td->buffer), + isocdesc->total_length, isocdesc); + + /* + * Isoc. handling of return value from td->completed (callback function) + */ + + switch (ret) { + case CB_CONTINUE: /* similar to the REMOVE condition below */ + /* TBD */ + uhci_td_free (td); + break; + + case CB_REUSE: /* similar to the re-add condition below, + * but Not ACTIVE */ + /* TBD */ + /* usb_dev = td->dev->usb; */ + + list_add(&td->irq_list, &uhci->interrupt_list); + + td->status = (td->status & (TD_CTRL_SPD | TD_CTRL_C_ERR_MASK | + TD_CTRL_LS | TD_CTRL_IOS | TD_CTRL_IOC)) | + TD_CTRL_IOC; + + /* The HC removes it, so re-add it */ + /* Insert into a QH? */ + uhci_insert_td_in_qh(td->qh, td); + break; + + case CB_RESTART: /* similar to re-add, but mark ACTIVE */ + /* TBD */ + /* usb_dev = td->dev->usb; */ + + list_add(&td->irq_list, &uhci->interrupt_list); + + td->status = (td->status & (TD_CTRL_SPD | TD_CTRL_C_ERR_MASK | + TD_CTRL_LS | TD_CTRL_IOS | TD_CTRL_IOC)) | + TD_CTRL_ACTIVE | TD_CTRL_IOC; + + /* The HC removes it, so re-add it */ + uhci_insert_td_in_qh(td->qh, td); + break; + + case CB_ABORT: /* kill/abort */ + /* TBD */ + uhci_kill_isoc (isocdesc); + break; + } /* end isoc. TD switch */ + + return 0; +} + +int uhci_callback(struct uhci *uhci, struct uhci_td *td, int status, unsigned long rval) +{ + if (td->completed(status, bus_to_virt(td->buffer), rval, td->dev_id)) { + struct usb_device *usb_dev = td->dev->usb; + + list_add(&td->irq_list, &uhci->interrupt_list); + + usb_dotoggle(usb_dev, uhci_endpoint(td->info), uhci_packetout(td->info)); + td->info &= ~(1 << TD_TOKEN_TOGGLE); /* clear data toggle */ + td->info |= usb_gettoggle(usb_dev, uhci_endpoint(td->info), + uhci_packetout(td->info)) << TD_TOKEN_TOGGLE; /* toggle between data0 and data1 */ + td->status = (td->status & 0x2F000000) | TD_CTRL_ACTIVE | TD_CTRL_IOC; + /* The HC only removes it when it completed */ + /* successfully, so force remove and re-add it. */ + uhci_remove_td(td); + uhci_insert_td_in_qh(td->qh, td); + } else if (td->flags & UHCI_TD_REMOVE) { + struct usb_device *usb_dev = td->dev->usb; + + /* marked for removal */ + td->flags &= ~UHCI_TD_REMOVE; + usb_dotoggle(usb_dev, uhci_endpoint(td->info), uhci_packetout(td->info)); + uhci_remove_qh(td->qh->skel, td->qh); + uhci_qh_free(td->qh); + if (td->pipetype == PIPE_INTERRUPT) + usb_release_bandwidth(usb_dev, td->bandwidth_alloc); + uhci_td_free(td); + } + + return 0; +} + static void uhci_interrupt_notify(struct uhci *uhci) { struct list_head *tmp, *head = &uhci->interrupt_list; @@ -1690,39 +1812,18 @@ static void uhci_interrupt_notify(struct uhci *uhci) /* TD's completed successfully */ status = uhci_td_result(td->dev, td, &rval, 0); - if ((status == USB_ST_NOERROR) && (td->status & TD_CTRL_ACTIVE)) + if (status == USB_ST_NOCHANGE) continue; /* remove from IRQ list */ list_del(&td->irq_list); INIT_LIST_HEAD(&td->irq_list); - if (td->completed(status, bus_to_virt(td->buffer), rval, - td->dev_id)) { - struct usb_device *usb_dev = td->dev->usb; - - list_add(&td->irq_list, &uhci->interrupt_list); - - usb_dotoggle(usb_dev, usb_pipeendpoint(td->info), usb_pipeout(td->info) ^ 1); - td->info &= ~(1 << 19); /* clear data toggle */ - td->info |= usb_gettoggle(usb_dev, usb_pipeendpoint(td->info), - uhci_packetout(td->info)) << 19; /* toggle between data0 and data1 */ - td->status = (td->status & 0x2F000000) | TD_CTRL_ACTIVE | TD_CTRL_IOC; - /* The HC only removes it when it completed */ - /* successfully, so force remove and re-add it */ - uhci_remove_td(td); - uhci_insert_td_in_qh(td->qh, td); - } else if (td->flags & UHCI_TD_REMOVE) { - struct usb_device *usb_dev = td->dev->usb; - - /* marked for removal */ - td->flags &= ~UHCI_TD_REMOVE; - usb_dotoggle(usb_dev, usb_pipeendpoint(td->info), uhci_packetout(td->info)); - uhci_remove_qh(td->qh->skel, td->qh); - uhci_qh_free(td->qh); - uhci_td_free(td); - if (td->pipetype == PIPE_INTERRUPT) - usb_release_bandwidth(usb_dev, td->bandwidth_alloc); + if (td->pipetype == PIPE_ISOCHRONOUS) { + uhci_isoc_callback(uhci, td, status, rval); + } + else { + uhci_callback(uhci, td, status, rval); } /* If completed does not wants to reactivate, then */ @@ -1800,7 +1901,8 @@ static void uhci_init_ticktd(struct uhci *uhci) td->link = uhci->fl->frame[0]; td->backptr = &uhci->fl->frame[0]; td->status = TD_CTRL_IOC; - td->info = (15 << 21) | (0x7f << 8) | USB_PID_IN; /* (ignored) input packet, 16 bytes, device 127 */ + td->info = (15 << 21) | (0x7f << 8) | USB_PID_IN; + /* (ignored) input packet, 16 bytes, device 127 */ td->buffer = 0; td->qh = NULL; td->pipetype = -1; @@ -1843,7 +1945,8 @@ static void start_hc(struct uhci *uhci) } /* Turn on all interrupts */ - outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP, io_addr + USBINTR); + outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP, + io_addr + USBINTR); /* Start at frame 0 */ outw(0, io_addr + USBFRNUM); @@ -2169,6 +2272,8 @@ static int start_uhci(struct pci_dev *dev) /* disable legacy emulation */ pci_write_config_word(dev, USBLEGSUP, USBLEGSUP_DEFAULT); + pci_enable_device(dev); + return found_uhci(dev->irq, io_addr, io_size); } return -1; diff --git a/drivers/usb/uhci.h b/drivers/usb/uhci.h index 7d3ead8f6..e460fb858 100644 --- a/drivers/usb/uhci.h +++ b/drivers/usb/uhci.h @@ -125,6 +125,7 @@ struct uhci_framelist { #define TD_TOKEN_TOGGLE 19 #define uhci_maxlen(token) ((token) >> 21) +#define uhci_expected_length(info) (((info >> 21) + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */ #define uhci_toggle(token) (((token) >> TD_TOKEN_TOGGLE) & 1) #define uhci_endpoint(token) (((token) >> 15) & 0xf) #define uhci_devaddr(token) (((token) >> 8) & 0x7f) @@ -181,12 +182,6 @@ struct uhci_td { */ struct uhci; -#if 0 -#define UHCI_MAXTD 64 - -#define UHCI_MAXQH 16 -#endif - /* The usb device part must be first! Not anymore -jerdfelt */ struct uhci_device { struct usb_device *usb; @@ -194,10 +189,6 @@ struct uhci_device { atomic_t refcnt; struct uhci *uhci; -#if 0 - struct uhci_qh qh[UHCI_MAXQH]; /* These are the "common" qh's for each device */ - struct uhci_td td[UHCI_MAXTD]; -#endif unsigned long data[16]; }; @@ -278,7 +269,7 @@ struct uhci_device { * and we should meet that frequency when requested to do so. * This will require some change(s) to the UHCI skeleton. */ -static inline int __interval_to_skel(interval) +static inline int __interval_to_skel(int interval) { if (interval < 16) { if (interval < 4) { diff --git a/drivers/usb/usb.c b/drivers/usb/usb.c index 88e0e9662..add3bb463 100644 --- a/drivers/usb/usb.c +++ b/drivers/usb/usb.c @@ -789,10 +789,7 @@ int usb_set_protocol(struct usb_device *dev, int protocol) dr.index = 1; dr.length = 0; - if (dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), &dr, NULL, 0, HZ)) - return -1; - - return 0; + return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), &dr, NULL, 0, HZ); } /* keyboards want a nonzero duration according to HID spec, but @@ -807,10 +804,7 @@ int usb_set_idle(struct usb_device *dev, int duration, int report_id) dr.index = 1; dr.length = 0; - if (dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), &dr, NULL, 0, HZ)) - return -1; - - return 0; + return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), &dr, NULL, 0, HZ); } static void usb_set_maxpacket(struct usb_device *dev) @@ -873,8 +867,8 @@ int usb_clear_halt(struct usb_device *dev, int endp) if (result) return result; - if (status & 1) - return 1; /* still halted */ + if (status & 1) /* endpoint status is Halted */ + return USB_ST_STALL; /* still halted */ #endif usb_endpoint_running(dev, endp & 0x0f, usb_endpoint_out(endp)); @@ -888,6 +882,7 @@ int usb_clear_halt(struct usb_device *dev, int endp) int usb_set_interface(struct usb_device *dev, int interface, int alternate) { devrequest dr; + int err; dr.requesttype = 1; dr.request = USB_REQ_SET_INTERFACE; @@ -895,8 +890,9 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) dr.index = interface; dr.length = 0; - if (dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), &dr, NULL, 0, HZ)) - return -1; + err = dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), &dr, NULL, 0, HZ); + if (err) + return err; dev->ifnum = interface; dev->actconfig->interface[interface].act_altsetting = alternate; @@ -946,10 +942,7 @@ int usb_get_report(struct usb_device *dev, unsigned char type, unsigned char id, dr.index = index; dr.length = size; - if (dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev, 0), &dr, buf, size, HZ)) - return -1; - - return 0; + return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev, 0), &dr, buf, size, HZ); } int usb_get_configuration(struct usb_device *dev) @@ -1056,10 +1049,13 @@ char *usb_string(struct usb_device *dev, int index) * 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 * get the ball rolling.. + * + * Returns 0 for success, != 0 for error. */ int usb_new_device(struct usb_device *dev) { int addr; + int err; printk(KERN_INFO "USB new device connect, assigned device number %d\n", dev->devnum); @@ -1073,8 +1069,10 @@ int usb_new_device(struct usb_device *dev) dev->devnum = 0; /* Slow devices */ - if (usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, 8)) { - printk(KERN_ERR "usbcore: USB device not responding, giving up\n"); + err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, 8); + if (err) { + printk(KERN_ERR "usbcore: USB device not responding, giving up (error=%d)\n", + err); dev->devnum = -1; return 1; } @@ -1090,16 +1088,20 @@ int usb_new_device(struct usb_device *dev) dev->devnum = addr; - if (usb_set_address(dev)) { - printk(KERN_ERR "usbcore: USB device not accepting new address\n"); + err = usb_set_address(dev); + if (err) { + printk(KERN_ERR "usbcore: USB device not accepting new address (error=%d)\n", + err); dev->devnum = -1; return 1; } wait_ms(10); /* Let the SET_ADDRESS settle */ - if (usb_get_device_descriptor(dev)) { - printk(KERN_ERR "usbcore: unable to get device descriptor\n"); + err = usb_get_device_descriptor(dev); + if (err) { + printk(KERN_ERR "usbcore: unable to get device descriptor (error=%d)\n", + err); dev->devnum = -1; return 1; } @@ -1324,7 +1326,6 @@ void usb_major_init(void) if (register_chrdev(180,"usb",&usb_fops)) { printk("unable to get major %d for usb devices\n", MISC_MAJOR); - return -EIO; } } diff --git a/drivers/usb/usb.h b/drivers/usb/usb.h index 71510623d..f31c5b60b 100644 --- a/drivers/usb/usb.h +++ b/drivers/usb/usb.h @@ -199,31 +199,32 @@ typedef struct { } devrequest __attribute__ ((packed)); /* - * Status codes (these follow OHCI controllers condition codes) + * Status codes (these [used to] follow OHCI controllers condition codes) */ -#define USB_ST_NOERROR 0x0 -#define USB_ST_CRC 0x1 -#define USB_ST_BITSTUFF 0x2 -#define USB_ST_DTMISMATCH 0x3 /* data toggle mismatch */ -#define USB_ST_STALL 0x4 -#define USB_ST_NORESPONSE 0x5 /* device not responding/handshaking */ -#define USB_ST_PIDCHECK 0x6 /* Check bits on PID failed */ -#define USB_ST_PIDUNDEF 0x7 /* PID unexpected/undefined */ -#define USB_ST_DATAOVERRUN 0x8 -#define USB_ST_DATAUNDERRUN 0x9 -#define USB_ST_RESERVED1 0xA -#define USB_ST_RESERVED2 0xB -#define USB_ST_BUFFEROVERRUN 0xC -#define USB_ST_BUFFERUNDERRUN 0xD -#define USB_ST_RESERVED3 0xE -#define USB_ST_RESERVED4 0xF +#define USB_ST_NOERROR 0 +#define USB_ST_CRC -1 +#define USB_ST_BITSTUFF -2 +#define USB_ST_DTMISMATCH -3 /* data toggle mismatch */ +#define USB_ST_STALL -4 +#define USB_ST_NORESPONSE -5 /* device not responding/handshaking */ +#define USB_ST_PIDCHECK -6 /* Check bits on PID failed */ +#define USB_ST_PIDUNDEF -7 /* PID unexpected/undefined */ +#define USB_ST_DATAOVERRUN -8 +#define USB_ST_DATAUNDERRUN -9 +#define USB_ST_RESERVED1 -10 +#define USB_ST_RESERVED2 -11 +#define USB_ST_BUFFEROVERRUN -12 +#define USB_ST_BUFFERUNDERRUN -13 +#define USB_ST_RESERVED3 -14 +#define USB_ST_RESERVED4 -15 /* internal errors */ -#define USB_ST_REMOVED 0x100 -#define USB_ST_TIMEOUT 0x110 -#define USB_ST_INTERNALERROR -1 -#define USB_ST_NOTSUPPORTED -2 -#define USB_ST_BANDWIDTH_ERROR -3 +#define USB_ST_REMOVED -100 +#define USB_ST_TIMEOUT -101 +#define USB_ST_INTERNALERROR -200 +#define USB_ST_NOTSUPPORTED -201 +#define USB_ST_BANDWIDTH_ERROR -202 +#define USB_ST_NOCHANGE -203 /* @@ -684,7 +685,6 @@ int usb_get_protocol(struct usb_device *dev); int usb_set_protocol(struct usb_device *dev, int protocol); int usb_set_interface(struct usb_device *dev, int interface, int alternate); int usb_set_idle(struct usb_device *dev, int duration, int report_id); -int usb_set_interface(struct usb_device *dev, int interface, int alternate); int usb_set_configuration(struct usb_device *dev, int configuration); int usb_get_report(struct usb_device *dev, unsigned char type, unsigned char id, unsigned char index, void *buf, int size); char *usb_string(struct usb_device *dev, int index); @@ -722,7 +722,7 @@ void usb_show_device(struct usb_device *); void usb_show_string(struct usb_device *dev, char *id, int index); #ifdef USB_DEBUG -#define PRINTD(format, args...) printk("usb: " format "\n" , ## args); +#define PRINTD(format, args...) printk(KERN_DEBUG "usb: " format "\n" , ## args); #else /* NOT DEBUGGING */ #define PRINTD(fmt, arg...) do {} while (0) #endif /* USB_DEBUG */ diff --git a/drivers/video/Config.in b/drivers/video/Config.in index a8e223f6b..77d72f3f3 100644 --- a/drivers/video/Config.in +++ b/drivers/video/Config.in @@ -96,6 +96,7 @@ if [ "$CONFIG_FB" = "y" ]; then bool ' Multihead support' CONFIG_FB_MATROX_MULTIHEAD fi tristate ' ATI Mach64 display support (EXPERIMENTAL)' CONFIG_FB_ATY + bool ' ATI Rage 128 display support (EXPERIMENTAL)' CONFIG_FB_ATY128 bool ' 3Dfx Banshee/Voodoo3 display support (EXPERIMENTAL)' CONFIG_FB_3DFX fi fi diff --git a/drivers/video/Makefile b/drivers/video/Makefile index e4466cfcb..5c85927cb 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -113,6 +113,10 @@ else endif endif +ifeq ($(CONFIG_FB_ATY128),y) +L_OBJS += aty128fb.o +endif + ifeq ($(CONFIG_FB_IGA),y) L_OBJS += igafb.o endif diff --git a/drivers/video/aty128.h b/drivers/video/aty128.h new file mode 100644 index 000000000..cdc15e04f --- /dev/null +++ b/drivers/video/aty128.h @@ -0,0 +1,594 @@ +/* $Id$ + * linux/drivers/video/aty128.h + * Register definitions for ATI Rage128 boards + * + * Anthony Tong <atong@uiuc.edu>, 1999 + */ + +#ifndef REG_RAGE128_H +#define REG_RAGE128_H + +#define MM_INDEX 0x0000 +#define MM_DATA 0x0004 +#define CLOCK_CNTL_INDEX 0x0008 +#define CLOCK_CNTL_DATA 0x000c +#define BIOS_0_SCRATCH 0x0010 +#define BIOS_1_SCRATCH 0x0014 +#define BIOS_2_SCRATCH 0x0018 +#define BIOS_3_SCRATCH 0x001c +#define BUS_CNTL 0x0030 +#define BUS_CNTL1 0x0034 +#define MEM_VGA_WP_SEL 0x0038 +#define MEM_VGA_RP_SEL 0x003c +#define GEN_INT_CNTL 0x0040 +#define GEN_INT_STATUS 0x0044 +#define CRTC_GEN_CNTL 0x0050 +#define CRTC_EXT_CNTL 0x0054 +#define DAC_CNTL 0x0058 +#define CRTC_STATUS 0x005c +#define GPIO_MONID 0x0068 +#define I2C_CNTL_1 0x0094 +#define PALETTE_INDEX 0x00b0 +#define PALETTE_DATA 0x00b4 +#define CONFIG_CNTL 0x00e0 +#define CONFIG_XSTRAP 0x00e4 +#define CONFIG_BONDS 0x00e8 +#define GEN_RESET_CNTL 0x00f0 +#define GEN_STATUS 0x00f4 +#define CONFIG_MEMSIZE 0x00f8 +#define CONFIG_APER_0_BASE 0x0100 +#define CONFIG_APER_1_BASE 0x0104 +#define CONFIG_APER_SIZE 0x0108 +#define CONFIG_REG_1_BASE 0x010c +#define CONFIG_REG_APER_SIZE 0x0110 +#define CONFIG_MEMSIZE_EMBEDDED 0x0114 +#define TEST_DEBUG_CNTL 0x0120 +#define TEST_DEBUG_MUX 0x0124 +#define HW_DEBUG 0x0128 +#define TEST_DEBUG_OUT 0x012c +#define HOST_PATH_CNTL 0x0130 +#define SW_SEMAPHORE 0x013c +#define MEM_CNTL 0x0140 +#define EXT_MEM_CNTL 0x0144 +#define MEM_ADDR_CONFIG 0x0148 +#define MEM_INTF_CNTL 0x014c +#define MEM_STR_CNTL 0x0150 +#define MEM_INIT_LAT_TIMER 0x0154 +#define MEM_SDRAM_MODE_REG 0x0158 +#define AGP_BASE 0x0170 +#define AGP_CNTL 0x0174 +#define AGP_APER_OFFSET 0x0178 +#define PCI_GART_PAGE 0x017c +#define PC_NGUI_MODE 0x0180 +#define PC_NGUI_CTLSTAT 0x0184 +#define VIDEOMUX_CNTL 0x0190 +#define MPP_TB_CONFIG 0x01C0 +#define MPP_GP_CONFIG 0x01C8 +#define VIPH_CONTROL 0x01D0 +#define CRTC_H_TOTAL_DISP 0x0200 +#define CRTC_H_SYNC_STRT_WID 0x0204 +#define CRTC_V_TOTAL_DISP 0x0208 +#define CRTC_V_SYNC_STRT_WID 0x020c +#define CRTC_VLINE_CRNT_VLINE 0x0210 +#define CRTC_CRNT_FRAME 0x0214 +#define CRTC_GUI_TRIG_VLINE 0x0218 +#define CRTC_DEBUG 0x021c +#define CRTC_OFFSET 0x0224 +#define CRTC_OFFSET_CNTL 0x0228 +#define CRTC_PITCH 0x022c +#define OVR_CLR 0x0230 +#define OVR_WID_LEFT_RIGHT 0x0234 +#define OVR_WID_TOP_BOTTOM 0x0238 +#define SNAPSHOT_VH_COUNTS 0x0240 +#define SNAPSHOT_F_COUNT 0x0244 +#define N_VIF_COUNT 0x0248 +#define SNAPSHOT_VIF_COUNT 0x024c +#define CUR_OFFSET 0x0260 +#define CUR_HORZ_VERT_POSN 0x0264 +#define CUR_HORZ_VERT_OFF 0x0268 +#define CUR_CLR0 0x026c +#define CUR_CLR1 0x0270 +#define DAC_CRC_SIG 0x02cc +#define DDA_CONFIG 0x02e0 +#define DDA_ON_OFF 0x02e4 +#define VGA_DDA_CONFIG 0x02e8 +#define VGA_DDA_ON_OFF 0x02ec +#define OV0_Y_X_START 0x0400 +#define OV0_Y_X_END 0x0404 +#define OV0_EXCLUSIVE_HORZ 0x0408 +#define OV0_EXCLUSIVE_VERT 0x040c +#define OV0_REG_LOAD_CNTL 0x0410 +#define OV0_SCALE_CNTL 0x0420 +#define OV0_V_INC 0x0424 +#define OV0_P1_V_ACCUM_INIT 0x0428 +#define OV0_P23_V_ACCUM_INIT 0x042c +#define OV0_P1_BLANK_LINES_AT_TOP 0x0430 +#define OV0_P23_BLANK_LINES_AT_TOP 0x0434 +#define OV0_VID_BUF0_BASE_ADRS 0x0440 +#define OV0_VID_BUF1_BASE_ADRS 0x0444 +#define OV0_VID_BUF2_BASE_ADRS 0x0448 +#define OV0_VID_BUF3_BASE_ADRS 0x044c +#define OV0_VID_BUF4_BASE_ADRS 0x0450 +#define OV0_VID_BUF5_BASE_ADRS 0x0454 +#define OV0_VID_BUF_PITCH0_VALUE 0x0460 +#define OV0_VID_BUF_PITCH1_VALUE 0x0464 +#define OV0_OCTWORDS_PER_LINE_M1 0x046c +#define OV0_AUTO_FLIP_CNTRL 0x0470 +#define OV0_DEINTERLACE_PATTERN 0x0474 +#define OV0_H_INC 0x0480 +#define OV0_STEP_BY 0x0484 +#define OV0_P1_H_ACCUM_INIT 0x0488 +#define OV0_P23_H_ACCUM_INIT 0x048c +#define OV0_P1_X_START_END 0x0494 +#define OV0_P2_X_START_END 0x0498 +#define OV0_P3_X_START_END 0x049c +#define OV0_FILTER_CNTL 0x04a0 +#define OV0_FOUR_TAP_COEF_0 0x04b0 +#define OV0_FOUR_TAP_COEF_1 0x04b4 +#define OV0_FOUR_TAP_COEF_2 0x04b8 +#define OV0_FOUR_TAP_COEF_3 0x04bc +#define OV0_FOUR_TAP_COEF_4 0x04c0 +#define OV0_COLOR_CNTL 0x04e0 +#define OV0_VIDEO_KEY_CLR 0x04e4 +#define OV0_VIDEO_KEY_MASK 0x04e8 +#define OV0_GRAPHICS_KEY_CLR 0x04ec +#define OV0_GRAPHICS_KEY_MASK 0x04f0 +#define OV0_KEY_CNTL 0x04f4 +#define OV0_TEST 0x04f8 +#define SUBPIC_CNTL 0x0540 +#define PM4_BUFFER_OFFSET 0x0700 +#define PM4_BUFFER_CNTL 0x0704 +#define PM4_BUFFER_WM_CNTL 0x0708 +#define PM4_BUFFER_DL_RPTR_ADDR 0x070c +#define PM4_BUFFER_DL_RPTR 0x0710 +#define PM4_BUFFER_DL_WPTR 0x0714 +#define PM4_VC_FPU_SETUP 0x071c +#define PM4_FPU_CNTL 0x0720 +#define PM4_VC_FORMAT 0x0724 +#define PM4_VC_CNTL 0x0728 +#define PM4_VC_I01 0x072c +#define PM4_VC_VLOFF 0x0730 +#define PM4_VC_VLSIZE 0x0734 +#define PM4_IW_INDOFF 0x0738 +#define PM4_IW_INDSIZE 0x073c +#define PM4_FPU_FPX0 0x0740 +#define CRC_CMDFIFO_ADDR 0x0740 +#define PM4_FPU_FPY0 0x0744 +#define CRC_CMDFIFO_DOUT 0x0744 +#define PM4_FPU_FPX1 0x0748 +#define PM4_FPU_FPY1 0x074c +#define PM4_FPU_FPX2 0x0750 +#define PM4_FPU_FPY2 0x0754 +#define PM4_FPU_FPY3 0x0758 +#define PM4_FPU_FPY4 0x075c +#define PM4_FPU_FPY5 0x0760 +#define PM4_FPU_FPY6 0x0764 +#define PM4_FPU_FPR 0x0768 +#define PM4_FPU_FPG 0x076c +#define PM4_FPU_FPB 0x0770 +#define PM4_FPU_FPA 0x0774 +#define PM4_FPU_INTXY0 0x0780 +#define PM4_FPU_INTXY1 0x0784 +#define PM4_FPU_INTXY2 0x0788 +#define PM4_FPU_INTARGB 0x078c +#define PM4_FPU_FPTWICEAREA 0x0790 +#define PM4_FPU_DMAJOR01 0x0794 +#define PM4_FPU_DMAJOR12 0x0798 +#define PM4_FPU_DMAJOR02 0x079c +#define PM4_FPU_STAT 0x07a0 +#define PM4_STAT 0x07b8 +#define PM4_TEST_CNTL 0x07d0 +#define PM4_MICROCODE_ADDR 0x07d4 +#define PM4_MICROCODE_RADDR 0x07d8 +#define PM4_MICROCODE_DATAH 0x07dc +#define PM4_MICROCODE_DATAL 0x07e0 +#define PM4_CMDFIFO_ADDR 0x07e4 +#define PM4_CMDFIFO_DATAH 0x07e8 +#define PM4_CMDFIFO_DATAL 0x07ec +#define PM4_BUFFER_ADDR 0x07f0 +#define PM4_BUFFER_DATAH 0x07f4 +#define PM4_BUFFER_DATAL 0x07f8 +#define PM4_MICRO_CNTL 0x07fc +#define VID_BUFFER_CONTROL 0x0900 +#define CAP_INT_CNTL 0x0908 +#define CAP_INT_STATUS 0x090c +#define CAP0_BUF0_OFFSET 0x0920 +#define CAP0_BUF1_OFFSET 0x0924 +#define CAP0_BUF0_EVEN_OFFSET 0x0928 +#define CAP0_BUF1_EVEN_OFFSET 0x092c +#define CAP0_BUF_PITCH 0x0930 +#define CAP0_V_WINDOW 0x0934 +#define CAP0_H_WINDOW 0x0938 +#define CAP0_VBI_ODD_OFFSET 0x093c +#define CAP0_VBI_EVEN_OFFSET 0x0940 +#define CAP0_VBI_V_WINDOW 0x0944 +#define CAP0_VBI_H_WINDOW 0x0948 +#define CAP0_PORT_MODE_CNTL 0x094c +#define CAP0_TRIG_CNTL 0x0950 +#define CAP0_DEBUG 0x0954 +#define CAP0_CONFIG 0x0958 +#define CAP0_ANC_ODD_OFFSET 0x095c +#define CAP0_ANC_EVEN_OFFSET 0x0960 +#define CAP0_ANC_H_WINDOW 0x0964 +#define CAP0_VIDEO_SYNC_TEST 0x0968 +#define CAP0_ONESHOT_BUF_OFFSET 0x096c +#define CAP0_BUF_STATUS 0x0970 +#define CAP0_DWNSC_XRATIO 0x0978 +#define CAP0_XSHARPNESS 0x097c +#define CAP1_BUF0_OFFSET 0x0990 +#define CAP1_BUF1_OFFSET 0x0994 +#define CAP1_BUF0_EVEN_OFFSET 0x0998 +#define CAP1_BUF1_EVEN_OFFSET 0x099c +#define CAP1_BUF_PITCH 0x09a0 +#define CAP1_V_WINDOW 0x09a4 +#define CAP1_H_WINDOW 0x09a8 +#define CAP1_VBI_ODD_OFFSET 0x09ac +#define CAP1_VBI_EVEN_OFFSET 0x09b0 +#define CAP1_VBI_V_WINDOW 0x09b4 +#define CAP1_VBI_H_WINDOW 0x09b8 +#define CAP1_PORT_MODE_CNTL 0x09bc +#define CAP1_TRIG_CNTL 0x09c0 +#define CAP1_DEBUG 0x09c4 +#define CAP1_CONFIG 0x09c8 +#define CAP1_ANC_ODD_OFFSET 0x09cc +#define CAP1_ANC_EVEN_OFFSET 0x09d0 +#define CAP1_ANC_H_WINDOW 0x09d4 +#define CAP1_VIDEO_SYNC_TEST 0x09d8 +#define CAP1_ONESHOT_BUF_OFFSET 0x09dc +#define CAP1_BUF_STATUS 0x09e0 +#define CAP1_DWNSC_XRATIO 0x09e8 +#define CAP1_XSHARPNESS 0x09ec +#define BM_FRAME_BUF_OFFSET 0x0a00 +#define BM_SYSTEM_MEM_ADDR 0x0a04 +#define BM_COMMAND 0x0a08 +#define BM_STATUS 0x0a0c +#define BM_QUEUE_STATUS 0x0a10 +#define BM_QUEUE_FREE_STATUS 0x0A14 +#define BM_CHUNK_0_VAL 0x0a18 +#define BM_CHUNK_1_VAL 0x0a1C +#define BM_VIP0_BUF 0x0A20 +#define BM_VIP0_ACTIVE 0x0A24 +#define BM_VIP1_BUF 0x0A30 +#define BM_VIP1_ACTIVE 0x0A34 +#define BM_VIP2_BUF 0x0A40 +#define BM_VIP2_ACTIVE 0x0A44 +#define BM_VIP3_BUF 0x0A50 +#define BM_VIP3_ACTIVE 0x0A54 +#define BM_VIDCAP_BUF0 0x0a60 +#define BM_VIDCAP_BUF1 0x0a64 +#define BM_VIDCAP_BUF2 0x0a68 +#define BM_VIDCAP_ACTIVE 0x0a6c +#define BM_GUI 0x0a80 +#define SURFACE_DELAY 0x0b00 + +/****************************************************************************** + * GUI Block Memory Mapped Registers * + * These registers are FIFOed. * + *****************************************************************************/ +#define PM4_FIFO_DATA_EVEN 0x1000 +#define PM4_FIFO_DATA_ODD 0x1004 + +#define DST_OFFSET 0x1404 +#define DST_PITCH 0x1408 +#define DST_WIDTH 0x140c +#define DST_HEIGHT 0x1410 +#define SRC_X 0x1414 +#define SRC_Y 0x1418 +#define DST_X 0x141c +#define DST_Y 0x1420 +#define SRC_PITCH_OFFSET 0x1428 +#define DST_PITCH_OFFSET 0x142c +#define SRC_Y_X 0x1434 +#define DST_Y_X 0x1438 +#define DST_HEIGHT_WIDTH 0x143c +#define DP_GUI_MASTER_CNTL 0x146c +#define BRUSH_SCALE 0x1470 +#define BRUSH_Y_X 0x1474 +#define DP_BRUSH_BKGD_CLR 0x1478 +#define DP_BRUSH_FRGD_CLR 0x147c +#define BRUSH_DATA0 0x1480 +#define BRUSH_DATA1 0x1484 +#define BRUSH_DATA2 0x1488 +#define BRUSH_DATA3 0x148c +#define BRUSH_DATA4 0x1490 +#define BRUSH_DATA5 0x1494 +#define BRUSH_DATA6 0x1498 +#define BRUSH_DATA7 0x149c +#define BRUSH_DATA8 0x14a0 +#define BRUSH_DATA9 0x14a4 +#define BRUSH_DATA10 0x14a8 +#define BRUSH_DATA11 0x14ac +#define BRUSH_DATA12 0x14b0 +#define BRUSH_DATA13 0x14b4 +#define BRUSH_DATA14 0x14b8 +#define BRUSH_DATA15 0x14bc +#define BRUSH_DATA16 0x14c0 +#define BRUSH_DATA17 0x14c4 +#define BRUSH_DATA18 0x14c8 +#define BRUSH_DATA19 0x14cc +#define BRUSH_DATA20 0x14d0 +#define BRUSH_DATA21 0x14d4 +#define BRUSH_DATA22 0x14d8 +#define BRUSH_DATA23 0x14dc +#define BRUSH_DATA24 0x14e0 +#define BRUSH_DATA25 0x14e4 +#define BRUSH_DATA26 0x14e8 +#define BRUSH_DATA27 0x14ec +#define BRUSH_DATA28 0x14f0 +#define BRUSH_DATA29 0x14f4 +#define BRUSH_DATA30 0x14f8 +#define BRUSH_DATA31 0x14fc +#define BRUSH_DATA32 0x1500 +#define BRUSH_DATA33 0x1504 +#define BRUSH_DATA34 0x1508 +#define BRUSH_DATA35 0x150c +#define BRUSH_DATA36 0x1510 +#define BRUSH_DATA37 0x1514 +#define BRUSH_DATA38 0x1518 +#define BRUSH_DATA39 0x151c +#define BRUSH_DATA40 0x1520 +#define BRUSH_DATA41 0x1524 +#define BRUSH_DATA42 0x1528 +#define BRUSH_DATA43 0x152c +#define BRUSH_DATA44 0x1530 +#define BRUSH_DATA45 0x1534 +#define BRUSH_DATA46 0x1538 +#define BRUSH_DATA47 0x153c +#define BRUSH_DATA48 0x1540 +#define BRUSH_DATA49 0x1544 +#define BRUSH_DATA50 0x1548 +#define BRUSH_DATA51 0x154c +#define BRUSH_DATA52 0x1550 +#define BRUSH_DATA53 0x1554 +#define BRUSH_DATA54 0x1558 +#define BRUSH_DATA55 0x155c +#define BRUSH_DATA56 0x1560 +#define BRUSH_DATA57 0x1564 +#define BRUSH_DATA58 0x1568 +#define BRUSH_DATA59 0x156c +#define BRUSH_DATA60 0x1570 +#define BRUSH_DATA61 0x1574 +#define BRUSH_DATA62 0x1578 +#define BRUSH_DATA63 0x157c +#define DST_WIDTH_X 0x1588 +#define DST_HEIGHT_WIDTH_8 0x158c +#define SRC_X_Y 0x1590 +#define DST_X_Y 0x1594 +#define DST_WIDTH_HEIGHT 0x1598 +#define DST_WIDTH_X_INCY 0x159c +#define DST_HEIGHT_Y 0x15a0 +#define DST_X_SUB 0x15a4 +#define DST_Y_SUB 0x15a8 +#define SRC_OFFSET 0x15ac +#define SRC_PITCH 0x15b0 +#define DST_HEIGHT_WIDTH_BW 0x15b4 +#define CLR_CMP_CNTL 0x15c0 +#define CLR_CMP_CLR_SRC 0x15c4 +#define CLR_CMP_CLR_DST 0x15c8 +#define CLR_CMP_MASK 0x15cc +#define DP_SRC_FRGD_CLR 0x15d8 +#define DP_SRC_BKGD_CLR 0x15dc +#define GUI_SCRATCH_REG0 0x15e0 +#define GUI_SCRATCH_REG1 0x15e4 +#define GUI_SCRATCH_REG2 0x15e8 +#define GUI_SCRATCH_REG3 0x15ec +#define GUI_SCRATCH_REG4 0x15f0 +#define GUI_SCRATCH_REG5 0x15f4 +#define LEAD_BRES_ERR 0x1600 +#define LEAD_BRES_INC 0x1604 +#define LEAD_BRES_DEC 0x1608 +#define TRAIL_BRES_ERR 0x160c +#define TRAIL_BRES_INC 0x1610 +#define TRAIL_BRES_DEC 0x1614 +#define TRAIL_X 0x1618 +#define LEAD_BRES_LNTH 0x161c +#define TRAIL_X_SUB 0x1620 +#define LEAD_BRES_LNTH_SUB 0x1624 +#define DST_BRES_ERR 0x1628 +#define DST_BRES_INC 0x162c +#define DST_BRES_DEC 0x1630 +#define DST_BRES_LNTH 0x1634 +#define DST_BRES_LNTH_SUB 0x1638 +#define SC_LEFT 0x1640 +#define SC_RIGHT 0x1644 +#define SC_TOP 0x1648 +#define SC_BOTTOM 0x164c +#define SRC_SC_RIGHT 0x1654 +#define SRC_SC_BOTTOM 0x165c +#define AUX_SC_CNTL 0x1660 +#define AUX1_SC_LEFT 0x1664 +#define AUX1_SC_RIGHT 0x1668 +#define AUX1_SC_TOP 0x166c +#define AUX1_SC_BOTTOM 0x1670 +#define AUX2_SC_LEFT 0x1674 +#define AUX2_SC_RIGHT 0x1678 +#define AUX2_SC_TOP 0x167c +#define AUX2_SC_BOTTOM 0x1680 +#define AUX3_SC_LEFT 0x1684 +#define AUX3_SC_RIGHT 0x1688 +#define AUX3_SC_TOP 0x168c +#define AUX3_SC_BOTTOM 0x1690 +#define GUI_DEBUG0 0x16a0 +#define GUI_DEBUG1 0x16a4 +#define GUI_TIMEOUT 0x16b0 +#define GUI_TIMEOUT0 0x16b4 +#define GUI_TIMEOUT1 0x16b8 +#define GUI_PROBE 0x16bc +#define DP_CNTL 0x16c0 +#define DP_DATATYPE 0x16c4 +#define DP_MIX 0x16c8 +#define DP_WRITE_MASK 0x16cc +#define DP_CNTL_XDIR_YDIR_YMAJOR 0x16d0 +#define DEFAULT_OFFSET 0x16e0 +#define DEFAULT_PITCH 0x16e4 +#define DEFAULT_SC_BOTTOM_RIGHT 0x16e8 +#define SC_TOP_LEFT 0x16ec +#define SC_BOTTOM_RIGHT 0x16f0 +#define SRC_SC_BOTTOM_RIGHT 0x16f4 +#define WAIT_UNTIL 0x1720 +#define CACHE_CNTL 0x1724 +#define GUI_STAT 0x1740 +#define PC_GUI_MODE 0x1744 +#define PC_GUI_CTLSTAT 0x1748 +#define PC_DEBUG_MODE 0x1760 +#define BRES_DST_ERR_DEC 0x1780 +#define TRAIL_BRES_T12_ERR_DEC 0x1784 +#define TRAIL_BRES_T12_INC 0x1788 +#define DP_T12_CNTL 0x178c +#define DST_BRES_T1_LNTH 0x1790 +#define DST_BRES_T2_LNTH 0x1794 +#define HOST_DATA0 0x17c0 +#define HOST_DATA1 0x17c4 +#define HOST_DATA2 0x17c8 +#define HOST_DATA3 0x17cc +#define HOST_DATA4 0x17d0 +#define HOST_DATA5 0x17d4 +#define HOST_DATA6 0x17d8 +#define HOST_DATA7 0x17dc +#define HOST_DATA_LAST 0x17e0 +#define SECONDARY_SCALE_PITCH 0x1980 +#define SECONDARY_SCALE_X_INC 0x1984 +#define SECONDARY_SCALE_Y_INC 0x1988 +#define SECONDARY_SCALE_HACC 0x198c +#define SECONDARY_SCALE_VACC 0x1990 +#define SCALE_SRC_HEIGHT_WIDTH 0x1994 +#define SCALE_OFFSET_0 0x1998 +#define SCALE_PITCH 0x199c +#define SCALE_X_INC 0x19a0 +#define SCALE_Y_INC 0x19a4 +#define SCALE_HACC 0x19a8 +#define SCALE_VACC 0x19ac +#define SCALE_DST_X_Y 0x19b0 +#define SCALE_DST_HEIGHT_WIDTH 0x19b4 +#define SCALE_3D_CNTL 0x1a00 +#define SCALE_3D_DATATYPE 0x1a20 +#define SETUP_CNTL 0x1bc4 +#define SOLID_COLOR 0x1bc8 +#define WINDOW_XY_OFFSET 0x1bcc +#define DRAW_LINE_POINT 0x1bd0 +#define SETUP_CNTL_PM4 0x1bd4 +#define DST_PITCH_OFFSET_C 0x1c80 +#define DP_GUI_MASTER_CNTL_C 0x1c84 +#define SC_TOP_LEFT_C 0x1c88 +#define SC_BOTTOM_RIGHT_C 0x1c8c + +#define CLR_CMP_MASK_3D 0x1A28 +#define MISC_3D_STATE_CNTL_REG 0x1CA0 +#define MC_SRC1_CNTL 0x19D8 +#define TEX_CNTL 0x1800 + +/* CONSTANTS */ +#define GUI_ACTIVE 0x80000000 +#define ENGINE_IDLE 0x0 + +#define PLL_WR_EN 0x00000080 + +#define CLK_PIN_CNTL 0x0001 +#define PPLL_CNTL 0x0002 +#define PPLL_REF_DIV 0x0003 +#define PPLL_DIV_0 0x0004 +#define PPLL_DIV_1 0x0005 +#define PPLL_DIV_2 0x0006 +#define PPLL_DIV_3 0x0007 +#define VCLK_ECP_CNTL 0x0008 +#define HTOTAL_CNTL 0x0009 +#define X_MPLL_REF_FB_DIV 0x000a +#define XPLL_CNTL 0x000b +#define XDLL_CNTL 0x000c +#define XCLK_CNTL 0x000d +#define MPLL_CNTL 0x000e +#define MCLK_CNTL 0x000f +#define AGP_PLL_CNTL 0x0010 +#define FCP_CNTL 0x0012 +#define PLL_TEST_CNTL 0x0013 + +#define PPLL_RESET 0x01 +#define PPLL_ATOMIC_UPDATE_EN 0x10000 +#define PPLL_VGA_ATOMIC_UPDATE_EN 0x20000 +#define PPLL_REF_DIV_MASK 0x3FF +#define PPLL_FB3_DIV_MASK 0x7FF +#define PPLL_POST3_DIV_MASK 0x70000 +#define PPLL_ATOMIC_UPDATE_R 0x8000 +#define PPLL_ATOMIC_UPDATE_W 0x8000 +#define MEM_CFG_TYPE_MASK 0x3 +#define XCLK_SRC_SEL_MASK 0x7 +#define XPLL_FB_DIV_MASK 0xFF00 +#define X_MPLL_REF_DIV_MASK 0xFF + +/* CRTC control values (CRTC_GEN_CNTL) */ +#define CRTC_CSYNC_EN 0x00000010 + +#define CRTC_PIX_WIDTH_MASK 0x00000700 +#define CRTC_PIX_WIDTH_4BPP 0x00000100 +#define CRTC_PIX_WIDTH_8BPP 0x00000200 +#define CRTC_PIX_WIDTH_15BPP 0x00000300 +#define CRTC_PIX_WIDTH_16BPP 0x00000400 +#define CRTC_PIX_WIDTH_24BPP 0x00000500 +#define CRTC_PIX_WIDTH_32BPP 0x00000600 + +/* DAC_CNTL bit constants */ +#define DAC_8BIT_EN 0x00000100 + +/* GEN_RESET_CNTL bit constants */ +#define SOFT_RESET_GUI 0x00000001 +#define SOFT_RESET_VCLK 0x00000100 +#define SOFT_RESET_PCLK 0x00000200 +#define SOFT_RESET_ECP 0x00000400 +#define SOFT_RESET_DISPENG_XCLK 0x00000800 + +/* PC_GUI_CTLSTAT bit constants */ +#define PC_BUSY_INIT 0x10000000 +#define PC_BUSY_GUI 0x20000000 +#define PC_BUSY_NGUI 0x40000000 +#define PC_BUSY 0x80000000 + +#define BUS_MASTER_DIS 0x00000040 +#define PM4_BUFFER_CNTL_NONPM4 0x00000000 + +/* DP_GUI_MASTER_CNTL bit constants */ +#define GMC_SRC_PITCH_OFFSET_DEFAULT 0x00000000 +#define GMC_DST_PITCH_OFFSET_DEFAULT 0x00000000 +#define GMC_SRC_CLIP_DEFAULT 0x00000000 +#define GMC_DST_CLIP_DEFAULT 0x00000000 +#define GMC_BRUSH_SOLIDCOLOR 0x000000d0 +#define GMC_SRC_DSTCOLOR 0x00003000 +#define GMC_BYTE_ORDER_MSB_TO_LSB 0x00000000 +#define GMC_DP_SRC_RECT 0x02000000 +#define GMC_3D_FCN_EN_CLR 0x00000000 +#define GMC_AUX_CLIP_CLEAR 0x20000000 +#define GMC_DST_CLR_CMP_FCN_CLEAR 0x10000000 +#define GMC_WRITE_MASK_SET 0x40000000 +#define GMC_DP_CONVERSION_TEMP_6500 0x00000000 + +/* DP_GUI_MASTER_CNTL ROP3 named constants */ +#define ROP3_PATCOPY 0x00f00000 +#define ROP3_SRCCOPY 0x00cc0000 // S + +#define SRC_DSTCOLOR 0x00030000 + +/* DP_CNTL bit constants */ +#define DST_X_RIGHT_TO_LEFT 0x00000000 +#define DST_X_LEFT_TO_RIGHT 0x00000001 +#define DST_Y_BOTTOM_TO_TOP 0x00000000 +#define DST_Y_TOP_TO_BOTTOM 0x00000002 +#define DST_X_MAJOR 0x00000000 +#define DST_Y_MAJOR 0x00000004 +#define DST_X_TILE 0x00000008 +#define DST_Y_TILE 0x00000010 +#define DST_LAST_PEL 0x00000020 +#define DST_TRAIL_X_RIGHT_TO_LEFT 0x00000000 +#define DST_TRAIL_X_LEFT_TO_RIGHT 0x00000040 +#define DST_TRAP_FILL_RIGHT_TO_LEFT 0x00000000 +#define DST_TRAP_FILL_LEFT_TO_RIGHT 0x00000080 +#define DST_BRES_SIGN 0x00000100 +#define DST_HOST_BIG_ENDIAN_EN 0x00000200 +#define DST_POLYLINE_NONLAST 0x00008000 +#define DST_RASTER_STALL 0x00010000 +#define DST_POLY_EDGE 0x00040000 + +/* DP_MIX bit constants */ +#define DP_SRC_RECT 0x00000200 +#define DP_SRC_HOST 0x00000300 +#define DP_SRC_HOST_BYTEALIGN 0x00000400 + +#endif /* REG_RAGE128_H */ diff --git a/drivers/video/aty128fb.c b/drivers/video/aty128fb.c new file mode 100644 index 000000000..09ad29c30 --- /dev/null +++ b/drivers/video/aty128fb.c @@ -0,0 +1,2151 @@ +/* $Id$ + * linux/drivers/video/aty128fb.c -- Frame buffer device for ATI Rage128 + * + * Copyright (C) Summer 1999, Anthony Tong <atong@uiuc.edu> + * + * Brad Douglas <brad@neruo.com> + * - x86 support + * - MTRR + * - Probe ROM for PLL + * + * Based off of Geert's atyfb.c and vfb.c. + * + * TODO: + * - panning + * - fix 15/16 bpp on big endian arch's + * - monitor sensing (DDC) + * - other platform support (only ppc/x86 supported) + * - PPLL_REF_DIV & XTALIN calculation + * - determine MCLK from previous hardware setting + */ + +/* + * A special note of gratitude to ATI's devrel for providing documentation, + * example code and hardware. Thanks Nitya. -atong + */ + + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/tty.h> +#include <linux/malloc.h> +#include <linux/vmalloc.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <asm/uaccess.h> +#include <linux/fb.h> +#include <linux/init.h> +#include <linux/selection.h> +#include <linux/pci.h> +#include <asm/io.h> + +#if defined(CONFIG_PPC) +#include <asm/prom.h> +#include <asm/pci-bridge.h> +#include <linux/nvram.h> +#include <video/macmodes.h> +#endif + +#include <video/fbcon.h> +#include <video/fbcon-cfb8.h> +#include <video/fbcon-cfb16.h> +#include <video/fbcon-cfb24.h> +#include <video/fbcon-cfb32.h> + +#ifdef CONFIG_MTRR +#include <asm/mtrr.h> +#endif + +#include "aty128.h" + +#undef DEBUG +#undef CONFIG_MTRR /* not ready? */ + +#ifdef DEBUG +#define DBG(x) printk(KERN_INFO "aty128fb: %s\n",(x)); +#else +#define DBG(x) +#endif + +static char *aty128fb_name = "ATY Rage128"; + +static struct fb_var_screeninfo default_var = { + /* 640x480, 60 Hz, Non-Interlaced (25.175 MHz dotclock) */ + 640, 480, 640, 480, 0, 0, 8, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, 0, -1, -1, 0, 39722, 48, 16, 33, 10, 96, 2, + 0, FB_VMODE_NONINTERLACED +}; + +#pragma pack(1) +typedef struct { + u8 clock_chip_type; + u8 struct_size; + u8 accelerator_entry; + u8 VGA_entry; + u16 VGA_table_offset; + u16 POST_table_offset; + u16 XCLK; + u16 MCLK; + u8 num_PLL_blocks; + u8 size_PLL_blocks; + u16 PCLK_ref_freq; + u16 PCLK_ref_divider; + u32 PCLK_min_freq; + u32 PCLK_max_freq; + u16 MCLK_ref_freq; + u16 MCLK_ref_divider; + u32 MCLK_min_freq; + u32 MCLK_max_freq; + u16 XCLK_ref_freq; + u16 XCLK_ref_divider; + u32 XCLK_min_freq; + u32 XCLK_max_freq; +} PLL_BLOCK; +#pragma pack() + +struct aty128_meminfo { + u8 ML; + u8 MB; + u8 Trcd; + u8 Trp; + u8 Twr; + u8 CL; + u8 Tr2w; + u8 LoopLatency; + u8 DspOn; + u8 Rloop; +}; + +const struct aty128_meminfo sdr_128 = { 4, 4, 3, 3, 1, 3, 1, 16, 30, 16 }; +const struct aty128_meminfo sdr_64 = { 4, 8, 3, 3, 1, 3, 1, 17, 46, 17 }; +const struct aty128_meminfo sdr_sgram = { 4, 4, 1, 2, 1, 2, 1, 16, 24, 16 }; +const struct aty128_meminfo ddr_sgram = { 4, 4, 3, 3, 2, 3, 1, 16, 31, 16 }; + +static int currcon = 0; +static char fontname[40] __initdata = { 0 }; + +#if defined(CONFIG_PPC) +static int default_vmode __initdata = VMODE_NVRAM; +static int default_cmode __initdata = CMODE_NVRAM; +#endif + +#if defined(CONFIG_MTRR) +static int mtrr = 1; +#endif + +struct aty128_constants { + u32 dotclock; + u32 ppll_min; + u32 ppll_max; + u32 ref_divider; + u32 xclk; + u32 fifo_width; + u32 fifo_depth; +}; + +struct aty128_crtc { + u32 gen_cntl; + u32 ext_cntl; + u32 h_total, h_sync_strt_wid; + u32 v_total, v_sync_strt_wid; + u32 pitch; + u32 offset, offset_cntl; + u32 vxres, vyres; + u32 bpp; +}; + +struct aty128_pll { + u32 post_divider; + u32 feedback_divider; + u32 vclk; +}; + +struct aty128_ddafifo { + u32 dda_config; + u32 dda_on_off; +}; + +/* register values for a specific mode */ +struct aty128fb_par { + struct aty128_crtc crtc; + struct aty128_pll pll; + struct aty128_ddafifo fifo_reg; + u32 accel_flags; +}; + +struct fb_info_aty128 { + struct fb_info fb_info; + struct aty128_constants constants; + unsigned long regbase_phys, regbase; + unsigned long frame_buffer_phys, frame_buffer; + const struct aty128_meminfo *mem; + u32 vram_size; + u32 BIOS_SEG; +#ifdef CONFIG_MTRR + struct { int vram; int vram_valid; } mtrr; +#endif + struct aty128fb_par default_par, current_par; + struct display disp; + struct { u8 red, green, blue, pad; } palette[256]; + union { +#ifdef FBCON_HAS_CFB16 + u16 cfb16[16]; +#endif +#ifdef FBCON_HAS_CFB24 + u32 cfb24[16]; +#endif +#ifdef FBCON_HAS_CFB32 + u32 cfb32[16]; +#endif + } fbcon_cmap; + int blitter_may_be_busy; +}; + +#define round_div(n, d) ((n+(d/2))/d) + + /* + * Interface used by the world + */ + +int aty128fb_setup(char *options); + +static int aty128fb_open(struct fb_info *info, int user); +static int aty128fb_release(struct fb_info *info, int user); +static int aty128fb_get_fix(struct fb_fix_screeninfo *fix, int con, + struct fb_info *info); +static int aty128fb_get_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +static int aty128fb_set_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +static int aty128fb_get_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info); +static int aty128fb_set_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info); +static int aty128fb_pan_display(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +static int aty128fb_ioctl(struct inode *inode, struct file *file, u_int cmd, + u_long arg, int con, struct fb_info *info); + + + /* + * Interface to the low level console driver + */ + +void aty128fb_init(void); +#ifdef CONFIG_FB_OF +void aty128fb_of_init(struct device_node *dp); +#endif +static int aty128fbcon_switch(int con, struct fb_info *info); +static void aty128fbcon_blank(int blank, struct fb_info *info); + + + /* + * Internal routines + */ + +static void aty128_encode_fix(struct fb_fix_screeninfo *fix, + struct aty128fb_par *par, + const struct fb_info_aty128 *info); +static void aty128_set_disp(struct display *disp, + struct fb_info_aty128 *info, int bpp, int accel); +static int aty128_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue, + u_int *transp, struct fb_info *info); +static int aty128_setcolreg(u_int regno, u_int red, u_int green, u_int blue, + u_int transp, struct fb_info *info); +static void do_install_cmap(int con, struct fb_info *info); +static void aty128pci_probe(void); +static int aty128find_ROM(struct fb_info_aty128 *info); +static void aty128_timings(struct fb_info_aty128 *info); +static void aty128_get_pllinfo(struct fb_info_aty128 *info); +static void aty128_reset_engine(const struct fb_info_aty128 *info); +static void aty128_flush_pixel_cache(const struct fb_info_aty128 *info); +static void wait_for_fifo(u16 entries, const struct fb_info_aty128 *info); +static void wait_for_idle(const struct fb_info_aty128 *info); +static u32 bpp_to_depth(u32 bpp); + +#ifdef FBCON_HAS_CFB8 +static struct display_switch fbcon_aty128_8; +#endif + + +static struct fb_ops aty128fb_ops = { + aty128fb_open, aty128fb_release, aty128fb_get_fix, + aty128fb_get_var, aty128fb_set_var, aty128fb_get_cmap, + aty128fb_set_cmap, aty128fb_pan_display, aty128fb_ioctl +}; + + + /* + * Functions to read from/write to the mmio registers + * - endian conversions may possibly be avoided by flipping CONFIG_CNTL + * or using the other register aperture? TODO. + */ +static inline u32 +_aty_ld_le32(volatile unsigned int regindex, + const struct fb_info_aty128 *info) +{ + unsigned long temp; + u32 val; + +#if defined(__powerpc__) + eieio(); + temp = info->regbase; + asm("lwbrx %0,%1,%2" : "=b"(val) : "b" (regindex), "b" (temp)); +#elif defined(__sparc_v9__) + temp = info->regbase + regindex; + asm("lduwa [%1] %2, %0" : "=r" (val) : "r" (temp), "i" (ASI_PL)); +#else + temp = info->regbase+regindex; + val = le32_to_cpu(*((volatile u32 *)(temp))); +#endif + return val; +} + +static inline void +_aty_st_le32(volatile unsigned int regindex, u32 val, + const struct fb_info_aty128 *info) +{ + unsigned long temp; + +#if defined(__powerpc__) + eieio(); + temp = info->regbase; + asm("stwbrx %0,%1,%2" : : "b" (val), "b" (regindex), "b" (temp) : + "memory"); +#elif defined(__sparc_v9__) + temp = info->regbase + regindex; + asm("stwa %0, [%1] %2" : : "r" (val), "r" (temp), "i" (ASI_PL) : "memory"); +#else + temp = info->regbase+regindex; + *((volatile u32 *)(temp)) = cpu_to_le32(val); +#endif +} + +static inline u8 +_aty_ld_8(volatile unsigned int regindex, + const struct fb_info_aty128 *info) +{ +#if defined(__powerpc__) + eieio(); +#endif + return *(volatile u8 *)(info->regbase+regindex); +} + +static inline void +_aty_st_8(volatile unsigned int regindex, u8 val, + const struct fb_info_aty128 *info) +{ +#if defined(__powerpc__) + eieio(); +#endif + *(volatile u8 *)(info->regbase+regindex) = val; +} + +#define aty_ld_le32(regindex) _aty_ld_le32(regindex, info) +#define aty_st_le32(regindex, val) _aty_st_le32(regindex, val, info) +#define aty_ld_8(regindex) _aty_ld_8(regindex, info) +#define aty_st_8(regindex, val) _aty_st_8(regindex, val, info) + + /* + * Functions to read from/write to the pll registers + */ + +#define aty_ld_pll(pll_index) _aty_ld_pll(pll_index, info) +#define aty_st_pll(pll_index, val) _aty_st_pll(pll_index, val, info) + +static u32 +_aty_ld_pll(unsigned int pll_index, + const struct fb_info_aty128 *info) +{ + aty_st_8(CLOCK_CNTL_INDEX, pll_index & 0x1F); + return aty_ld_le32(CLOCK_CNTL_DATA); +} + +static void +_aty_st_pll(unsigned int pll_index, u32 val, + const struct fb_info_aty128 *info) +{ + aty_st_8(CLOCK_CNTL_INDEX, (pll_index & 0x1F) | PLL_WR_EN); + aty_st_le32(CLOCK_CNTL_DATA, val); +} + +/* return true when the PLL has completed an atomic update */ +static int +aty_pll_readupdate(const struct fb_info_aty128 *info) +{ + return !(aty_ld_pll(PPLL_REF_DIV) & PPLL_ATOMIC_UPDATE_R); +} + +static void +aty_pll_wait_readupdate(const struct fb_info_aty128 *info) +{ + unsigned long timeout = jiffies + HZ/100; // should be more than enough + int reset = 1; + + while (time_before(jiffies, timeout)) + if (aty_pll_readupdate(info)) { + reset = 0; + break; + } + +#ifdef DEBUG + if (reset) /* reset engine?? */ + printk(KERN_ERR "aty128fb: PLL write timeout!"); +#endif +} + +/* tell PLL to update */ +static void +aty_pll_writeupdate(const struct fb_info_aty128 *info) +{ + aty_pll_wait_readupdate(info); + + aty_st_pll(PPLL_REF_DIV, + aty_ld_pll(PPLL_REF_DIV) | PPLL_ATOMIC_UPDATE_W); +} + + +/* write to the scratch register to test r/w functionality */ +static u32 +register_test(const struct fb_info_aty128 *info) +{ + u32 val, flag = 0; + + val = aty_ld_le32(BIOS_0_SCRATCH); + + aty_st_le32(BIOS_0_SCRATCH, 0x55555555); + if (aty_ld_le32(BIOS_0_SCRATCH) == 0x55555555) { + aty_st_le32(BIOS_0_SCRATCH, 0xAAAAAAAA); + + if (aty_ld_le32(BIOS_0_SCRATCH) == 0xAAAAAAAA) + flag = 1; + } + + aty_st_le32(BIOS_0_SCRATCH, val); // restore value + return flag; +} + + + /* + * Accelerator functions + */ +static void +wait_for_idle(const struct fb_info_aty128 *info) +{ + unsigned long timeout = jiffies + HZ/20; + int reset = 1; + + wait_for_fifo(64, info); + + while (time_before(jiffies, timeout)) + if ((aty_ld_le32(GUI_STAT) & GUI_ACTIVE) != ENGINE_IDLE) { + reset = 0; + break; + } + + if (reset) + aty128_reset_engine(info); +} + + +static void +wait_for_fifo(u16 entries, const struct fb_info_aty128 *info) +{ + unsigned long timeout = jiffies + HZ/20; + int reset = 1; + + while (time_before(jiffies, timeout)) + if ((aty_ld_le32(GUI_STAT) & 0x00000FFF) < entries) { + reset = 0; + break; + } + + if (reset) + aty128_reset_engine(info); +} + + +static void +aty128_flush_pixel_cache(const struct fb_info_aty128 *info) +{ + int i = 16384; + + aty_st_le32(PC_NGUI_CTLSTAT, aty_ld_le32(PC_NGUI_CTLSTAT) | 0x000000ff); + + while (i && (aty_ld_le32(PC_NGUI_CTLSTAT) & PC_BUSY)) + i--; +} + + +static void +aty128_reset_engine(const struct fb_info_aty128 *info) +{ + u32 gen_reset_cntl, clock_cntl_index, mclk_cntl; + + aty128_flush_pixel_cache(info); + + clock_cntl_index = aty_ld_le32(CLOCK_CNTL_INDEX); + mclk_cntl = aty_ld_pll(MCLK_CNTL); + + aty_st_pll(MCLK_CNTL, mclk_cntl | 0x00030000); + + gen_reset_cntl = aty_ld_le32(GEN_RESET_CNTL); + aty_st_le32(GEN_RESET_CNTL, gen_reset_cntl | SOFT_RESET_GUI); + aty_ld_le32(GEN_RESET_CNTL); + aty_st_le32(GEN_RESET_CNTL, gen_reset_cntl & ~(SOFT_RESET_GUI)); + aty_ld_le32(GEN_RESET_CNTL); + + aty_st_pll(MCLK_CNTL, mclk_cntl); + aty_st_le32(CLOCK_CNTL_INDEX, clock_cntl_index); + aty_st_le32(GEN_RESET_CNTL, gen_reset_cntl); + + /* use old pio mode */ + aty_st_le32(PM4_BUFFER_CNTL, PM4_BUFFER_CNTL_NONPM4); + +#ifdef DEBUG + printk("aty128fb: engine reset\n"); +#endif +} + + +static void +aty128_init_engine(const struct aty128fb_par *par, + const struct fb_info_aty128 *info) +{ + u32 temp; + aty_st_le32(SCALE_3D_CNTL, 0x00000000); + + aty128_reset_engine(info); + + temp = par->crtc.pitch; /* fix this up */ + if (par->crtc.bpp == 24) { + temp = temp * 3; + } + + /* setup engine offset registers */ + wait_for_fifo(4, info); + aty_st_le32(DEFAULT_OFFSET, 0x00000000); + + /* setup engine pitch registers */ + aty_st_le32(DEFAULT_PITCH, temp); + + /* set the default scissor register to max dimensions */ + wait_for_fifo(1, info); + aty_st_le32(DEFAULT_SC_BOTTOM_RIGHT, (0x1FFF << 16) | 0x1FFF); + + /* set the drawing controls registers */ + wait_for_fifo(1, info); + aty_st_le32(DP_GUI_MASTER_CNTL, + GMC_SRC_PITCH_OFFSET_DEFAULT | + GMC_DST_PITCH_OFFSET_DEFAULT | + GMC_SRC_CLIP_DEFAULT | + GMC_DST_CLIP_DEFAULT | + GMC_BRUSH_SOLIDCOLOR | + (bpp_to_depth(par->crtc.bpp) << 8) | + GMC_SRC_DSTCOLOR | + GMC_BYTE_ORDER_MSB_TO_LSB | + GMC_DP_CONVERSION_TEMP_6500 | + ROP3_PATCOPY | + GMC_DP_SRC_RECT | + GMC_3D_FCN_EN_CLR | + GMC_DST_CLR_CMP_FCN_CLEAR | + GMC_AUX_CLIP_CLEAR | + GMC_WRITE_MASK_SET); + wait_for_fifo(8, info); + + /* clear the line drawing registers */ + aty_st_le32(DST_BRES_ERR, 0); + aty_st_le32(DST_BRES_INC, 0); + aty_st_le32(DST_BRES_DEC, 0); + + /* set brush color registers */ + aty_st_le32(DP_BRUSH_FRGD_CLR, 0xFFFFFFFF); + aty_st_le32(DP_BRUSH_BKGD_CLR, 0x00000000); + + /* set source color registers */ + aty_st_le32(DP_SRC_FRGD_CLR, 0xFFFFFFFF); + aty_st_le32(DP_SRC_BKGD_CLR, 0x00000000); + + /* default write mask */ + aty_st_le32(DP_WRITE_MASK, 0xFFFFFFFF); + + /* Wait for all the writes to be completed before returning */ + wait_for_idle(info); +} + + + /* + * CRTC programming + */ + +/* convert bpp values to their register representation */ +static u32 +bpp_to_depth(u32 bpp) +{ + if (bpp <= 8) + return 2; + else if (bpp <= 15) + return 3; + else if (bpp <= 16) +#if 0 /* force 15bpp */ + return 4; +#else + return 3; +#endif + else if (bpp <= 24) + return 5; + else if (bpp <= 32) + return 6; + + return -EINVAL; +} + + +static void +aty128_set_crtc(const struct aty128_crtc *crtc, + const struct fb_info_aty128 *info) +{ + aty_st_le32(CRTC_GEN_CNTL, crtc->gen_cntl); + // aty_st_le32(CRTC_EXT_CNTL, crtc->ext_cntl); + aty_st_le32(CRTC_H_TOTAL_DISP, crtc->h_total); + aty_st_le32(CRTC_H_SYNC_STRT_WID, crtc->h_sync_strt_wid); + aty_st_le32(CRTC_V_TOTAL_DISP, crtc->v_total); + aty_st_le32(CRTC_V_SYNC_STRT_WID, crtc->v_sync_strt_wid); + aty_st_le32(CRTC_PITCH, crtc->pitch); + aty_st_le32(CRTC_OFFSET, crtc->offset); + aty_st_le32(CRTC_OFFSET_CNTL, crtc->offset_cntl); +} + + +static int +aty128_var_to_crtc(const struct fb_var_screeninfo *var, + struct aty128_crtc *crtc, + const struct fb_info_aty128 *info) +{ + u32 xres, yres, vxres, vyres, xoffset, yoffset, bpp; + u32 left, right, upper, lower, hslen, vslen, sync, vmode; + u32 h_total, h_disp, h_sync_strt, h_sync_wid, h_sync_pol; + u32 v_total, v_disp, v_sync_strt, v_sync_wid, v_sync_pol, c_sync; + u32 depth; + u8 hsync_strt_pix[5] = { 0, 0x12, 9, 6, 5 }; + + /* input */ + xres = var->xres; + yres = var->yres; + vxres = var->xres_virtual; + vyres = var->yres_virtual; + xoffset = var->xoffset; + yoffset = var->yoffset; + bpp = var->bits_per_pixel; + left = var->left_margin; + right = var->right_margin; + upper = var->upper_margin; + lower = var->lower_margin; + hslen = var->hsync_len; + vslen = var->vsync_len; + sync = var->sync; + vmode = var->vmode; + + /* check for mode eligibility */ + + /* accept only non interlaced modes */ + if ((vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED) + return -EINVAL; + + /* convert (and round up) and validate */ + xres = (xres + 7) & ~7; + xoffset = (xoffset + 7) & ~7; + + if (vxres < xres + xoffset) + vxres = xres + xoffset; + + if (vyres < yres + yoffset) + vyres = yres + yoffset; + + if (bpp <= 8) + bpp = 8; + else if (bpp <= 16) + bpp = 16; + else if (bpp <= 32) + bpp = 32; + + if (vxres * vyres * (bpp/8) > info->vram_size) + return -EINVAL; + + h_disp = xres / 8 - 1; + h_total = (xres + right + hslen + left) / 8 - 1; + + v_disp = yres - 1; + v_total = yres + upper + vslen + lower - 1; + + h_sync_wid = hslen / 8; + if (h_sync_wid == 0) + h_sync_wid = 1; + else if (h_sync_wid > 0x3f) + h_sync_wid = 0x3f; + + h_sync_strt = (xres + right - 8) + hsync_strt_pix[bpp/8]; + + v_disp = yres - 1; + v_sync_wid = vslen; + if (v_sync_wid == 0) + v_sync_wid = 1; + else if (v_sync_wid > 0x1f) + v_sync_wid = 0x1f; + + v_sync_strt = yres + lower - 1; + + h_sync_pol = sync & FB_SYNC_HOR_HIGH_ACT ? 0 : (1 << 23); + v_sync_pol = sync & FB_SYNC_VERT_HIGH_ACT ? 0 : (1 << 23); + + depth = bpp_to_depth(bpp); + c_sync = sync & FB_SYNC_COMP_HIGH_ACT ? (1 << 4) : 0; + + crtc->gen_cntl = 0x03000000 | c_sync | depth << 8; + + crtc->h_total = (h_disp << 16) | (h_total & 0x0000FFFF); + crtc->v_total = (v_disp << 16) | (v_total & 0x0000FFFF); + + crtc->h_sync_strt_wid = (h_sync_wid << 16) | (h_sync_strt) | h_sync_pol; + crtc->v_sync_strt_wid = (v_sync_wid << 16) | (v_sync_strt) | v_sync_pol; + + crtc->pitch = xres / 8; + + crtc->offset = 0; + crtc->offset_cntl = 0; + + crtc->vxres = vxres; + crtc->vyres = vyres; + crtc->bpp = bpp; + + return 0; +} + + +static int +aty128_crtc_to_var(const struct aty128_crtc *crtc, + struct fb_var_screeninfo *var) +{ +#ifdef notyet /* xoffset and yoffset are not correctly calculated */ + u32 xres, yres, bpp, left, right, upper, lower, hslen, vslen, sync; + u32 h_total, h_disp, h_sync_strt, h_sync_dly, h_sync_wid, h_sync_pol; + u32 v_total, v_disp, v_sync_strt, v_sync_wid, v_sync_pol, c_sync; + u32 pix_width; + + h_total = crtc->h_total & 0x1ff; + h_disp = (crtc->h_total>>16) & 0xff; + h_sync_strt = (crtc->h_sync_strt_wid & 0xff) | + ((crtc->h_sync_strt_wid>>4) & 0x100); + h_sync_dly = (crtc->h_sync_strt_wid>>8) & 0x7; + h_sync_wid = (crtc->h_sync_strt_wid>>16) & 0x1f; + h_sync_pol = (crtc->h_sync_strt_wid>>21) & 0x1; + v_total = crtc->v_total & 0x7ff; + v_disp = (crtc->v_total>>16) & 0x7ff; + v_sync_strt = crtc->v_sync_strt_wid & 0x7ff; + v_sync_wid = (crtc->v_sync_strt_wid>>16) & 0x1f; + v_sync_pol = (crtc->v_sync_strt_wid>>21) & 0x1; + c_sync = crtc->gen_cntl & CRTC_CSYNC_EN ? 1 : 0; + pix_width = crtc->gen_cntl & CRTC_PIX_WIDTH_MASK; + + xres = (h_disp+1)*8; + yres = v_disp+1; + left = (h_total-h_sync_strt-h_sync_wid)*8-h_sync_dly; + right = (h_sync_strt-h_disp)*8+h_sync_dly; + hslen = h_sync_wid*8; + upper = v_total-v_sync_strt-v_sync_wid; + lower = v_sync_strt-v_disp; + vslen = v_sync_wid; + sync = (h_sync_pol ? 0 : FB_SYNC_HOR_HIGH_ACT) | + (v_sync_pol ? 0 : FB_SYNC_VERT_HIGH_ACT) | + (c_sync ? FB_SYNC_COMP_HIGH_ACT : 0); + + switch (pix_width) { +#if 0 + case CRTC_PIX_WIDTH_4BPP: + bpp = 4; + var->red.offset = 0; + var->red.length = 8; + var->green.offset = 0; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + var->transp.offset = 0; + var->transp.length = 0; + break; +#endif + case CRTC_PIX_WIDTH_8BPP: + bpp = 8; + var->red.offset = 0; + var->red.length = 8; + var->green.offset = 0; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + var->transp.offset = 0; + var->transp.length = 0; + break; + case CRTC_PIX_WIDTH_15BPP: + bpp = 16; + var->red.offset = 10; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 5; + var->blue.offset = 0; + var->blue.length = 5; + var->transp.offset = 0; + var->transp.length = 0; + break; + case CRTC_PIX_WIDTH_16BPP: + bpp = 16; + var->red.offset = 11; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 6; + var->blue.offset = 0; + var->blue.length = 5; + var->transp.offset = 0; + var->transp.length = 0; + break; + case CRTC_PIX_WIDTH_24BPP: + bpp = 24; + var->red.offset = 16; + var->red.length = 8; + var->green.offset = 8; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + var->transp.offset = 0; + var->transp.length = 0; + break; + case CRTC_PIX_WIDTH_32BPP: + bpp = 32; + var->red.offset = 16; + var->red.length = 8; + var->green.offset = 8; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + var->transp.offset = 24; + var->transp.length = 8; + break; + default: + printk(KERN_ERR "Invalid pixel width\n"); + } + +//Godda do math for xoffset and yoffset: does not exist in crtc + var->xres = xres; + var->yres = yres; + var->xres_virtual = crtc->vxres; + var->yres_virtual = crtc->vyres; + var->bits_per_pixel = bpp; + var->xoffset = crtc->xoffset; + var->yoffset = crtc->yoffset; + var->left_margin = left; + var->right_margin = right; + var->upper_margin = upper; + var->lower_margin = lower; + var->hsync_len = hslen; + var->vsync_len = vslen; + var->sync = sync; + var->vmode = FB_VMODE_NONINTERLACED; + +#endif /* notyet */ + return 0; +} + +static int +aty128_bpp_to_var(int bpp, struct fb_var_screeninfo *var) +{ + /* fill in pixel info */ + switch (bpp) { + case 8: + var->red.offset = 0; + var->red.length = 8; + var->green.offset = 0; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + var->transp.offset = 0; + var->transp.length = 0; + break; + case 15: + var->bits_per_pixel = 16; + var->red.offset = 10; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 5; + var->blue.offset = 0; + var->blue.length = 5; + var->transp.offset = 15; + var->transp.length = 1; + break; + case 16: + var->bits_per_pixel = 16; + var->red.offset = 11; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 6; + var->blue.offset = 0; + var->blue.length = 5; + var->transp.offset = 0; + var->transp.length = 0; + break; + case 32: + var->red.offset = 16; + var->red.length = 8; + var->green.offset = 8; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + var->transp.offset = 24; + var->transp.length = 8; + break; + } + + return 0; +} + + +static void +aty128_set_pll(struct aty128_pll *pll, const struct fb_info_aty128 *info) +{ + int div3; + unsigned char post_conv[] = /* register values for post dividers */ + { 2, 0, 1, 4, 2, 2, 6, 2, 3, 2, 2, 2, 7 }; + + /* select PPLL_DIV_3 */ + aty_st_le32(CLOCK_CNTL_INDEX, aty_ld_le32(CLOCK_CNTL_INDEX) | (3 << 8)); + + /* reset ppll */ + aty_st_pll(PPLL_CNTL, + aty_ld_pll(PPLL_CNTL) | PPLL_RESET | PPLL_ATOMIC_UPDATE_EN); + + div3 = aty_ld_pll(PPLL_DIV_3); + + div3 &= ~PPLL_FB3_DIV_MASK; + div3 |= pll->feedback_divider; + + div3 &= ~PPLL_POST3_DIV_MASK; + div3 |= post_conv[pll->post_divider] << 16; + + /* write feedback and post dividers */ + aty_st_pll(PPLL_DIV_3, div3); + aty_pll_writeupdate(info); + aty_pll_wait_readupdate(info); + + aty_st_pll(HTOTAL_CNTL, 0); /* no horiz crtc adjustment */ + + aty_pll_writeupdate(info); + + /* clear the reset, just in case */ + aty_st_pll(PPLL_CNTL, aty_ld_pll(PPLL_CNTL) & ~PPLL_RESET); +} + + +static int +aty128_var_to_pll(u32 vclk_per, struct aty128_pll *pll, + const struct fb_info_aty128 *info) +{ + const struct aty128_constants c = info->constants; + unsigned char post_dividers [] = {1,2,4,8,3,6,12}; + u32 output_freq, vclk; + int i; + u32 n, d; + + vclk = 100000000 / vclk_per; /* convert units to 10 kHz */ + + /* adjust pixel clock if necessary */ + if (vclk > c.ppll_max) + vclk = c.ppll_max; + if (vclk * 12 < c.ppll_min) + vclk = c.ppll_min; + + /* now, find an acceptable divider */ + for (i = 0; i < sizeof(post_dividers); i++) { + output_freq = post_dividers[i] * vclk; + if (output_freq >= c.ppll_min && output_freq <= c.ppll_max) + break; + } + pll->post_divider = post_dividers[i]; + + /* calculate feedback divider */ + n = c.ref_divider * output_freq; + d = c.dotclock; + pll->feedback_divider = round_div(n, d); + + pll->vclk = vclk; +#ifdef DEBUG + printk("post %x feedback %x vlck %x output %x\n", + pll->post_divider, pll->feedback_divider, vclk, output_freq); +#endif + + return 0; +} + + +static int +aty128_pll_to_var(const struct aty128_pll *pll, struct fb_var_screeninfo *var) +{ + /* TODO */ + return 0; +} + + +static void +aty128_set_fifo(const struct aty128_ddafifo *dsp, + const struct fb_info_aty128 *info) +{ + aty_st_le32(DDA_CONFIG, dsp->dda_config); + aty_st_le32(DDA_ON_OFF, dsp->dda_on_off); +} + + +static int +aty128_ddafifo(struct aty128_ddafifo *dsp, + const struct aty128_pll *pll, + u32 bpp, + const struct fb_info_aty128 *info) +{ + const struct aty128_meminfo *m = info->mem; + u32 xclk = info->constants.xclk; + u32 fifo_width = info->constants.fifo_width; + u32 fifo_depth = info->constants.fifo_depth; + s32 x, b, p, ron, roff; + u32 n, d; + + if (bpp == 15) + bpp = 16; + + n = xclk * fifo_width; + d = pll->vclk*bpp; + x = round_div(n, d); + + ron = 4 * m->MB + + 3 * ((m->Trcd - 2 > 0) ? m->Trcd - 2 : 0) + + 2 * m->Trp + + m->Twr + + m->CL + + m->Tr2w + + x; + +#ifdef DEBUG + printk("x %x\n", x); +#endif + b = 0; + while (x) { + x >>= 1; + b++; + } + p = b + 1; + + ron <<= (11 - p); + + n <<= (11 - p); + x = round_div(n, d); + roff = x * (fifo_depth - 4); + if ((ron + m->Rloop) >= roff) { + printk("Mode out of range\n"); + return -EINVAL; + } + +#ifdef DEBUG + printk("p: %x rloop: %x x: %x ron: %x roff: %x\n", p, m->Rloop, x, + ron, roff); +#endif + dsp->dda_config = p << 16 | m->Rloop << 20 | x; + dsp->dda_on_off = ron << 16 | roff; + + return 0; +} + + +/* + * This actually sets the video mode. + */ +static void +aty128_set_par(struct aty128fb_par *par, + struct fb_info_aty128 *info) +{ + u32 config; + + info->current_par = *par; + + /* clear all registers that may interfere with mode setting */ + aty_st_le32(OVR_CLR, 0); + aty_st_le32(OVR_WID_LEFT_RIGHT, 0); + aty_st_le32(OVR_WID_TOP_BOTTOM, 0); + aty_st_le32(OV0_SCALE_CNTL, 0); + aty_st_le32(MPP_TB_CONFIG, 0); + aty_st_le32(MPP_GP_CONFIG, 0); + aty_st_le32(SUBPIC_CNTL, 0); + aty_st_le32(VIPH_CONTROL, 0); + aty_st_le32(I2C_CNTL_1, 0); + aty_st_le32(GEN_INT_CNTL, 0); /* turn off interrupts */ + aty_st_le32(CAP0_TRIG_CNTL, 0); + aty_st_le32(CAP1_TRIG_CNTL, 0); + + aty_st_8(CRTC_EXT_CNTL + 1, 4); /* turn video off */ + + aty128_set_crtc(&par->crtc, info); + aty128_set_pll(&par->pll, info); + aty128_set_fifo(&par->fifo_reg, info); + + config = aty_ld_le32(CONFIG_CNTL) & ~3; + +#if defined(__BIG_ENDIAN) + if (par->crtc.bpp >= 24) + config |= 2; /* make aperture do 32 byte swapping */ + else if (par->crtc.bpp > 8) + config |= 1; /* make aperture do 16 byte swapping */ +#endif + + aty_st_le32(CONFIG_CNTL, config); + + aty_st_8(CRTC_EXT_CNTL + 1, 0); /* turn the video back on */ +} + + + /* + * Open/Release the frame buffer device + */ + +static int aty128fb_open(struct fb_info *info, int user) +{ + MOD_INC_USE_COUNT; + return(0); +} + + +static int aty128fb_release(struct fb_info *info, int user) +{ + MOD_DEC_USE_COUNT; + return(0); +} + + +static int +aty128_decode_var(struct fb_var_screeninfo *var, struct aty128fb_par *par, + const struct fb_info_aty128 *info) +{ + int err; + + if ((err = aty128_var_to_crtc(var, &(par->crtc), info))) + return err; + + if ((err = aty128_var_to_pll(var->pixclock, &(par->pll), info))) + return err; + + if ((err = aty128_ddafifo(&par->fifo_reg, &par->pll, par->crtc.bpp, info))) + return err; + + if (var->accel_flags & FB_ACCELF_TEXT) + par->accel_flags = FB_ACCELF_TEXT; + else + par->accel_flags = 0; + + return 0; +} + + +static int +aty128_encode_var(struct fb_var_screeninfo *var, + const struct aty128fb_par *par, + const struct fb_info_aty128 *info) +{ + int err; + + //memset(var, 0, sizeof(struct fb_var_screeninfo)); + + /* XXX aty128_*_to_var() aren't fully implemented! */ + if ((err = aty128_crtc_to_var(&par->crtc, var))) + return err; + + if ((err = aty128_pll_to_var(&par->pll, var))) + return err; + + if ((err = aty128_bpp_to_var(var->bits_per_pixel, var))) + return err; + + var->height = -1; + var->width = -1; + var->accel_flags = par->accel_flags; + + return 0; +} + + + /* + * Get the User Defined Part of the Display + */ + +static int +aty128fb_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *fb) +{ + const struct fb_info_aty128 *info = (struct fb_info_aty128 *)fb; + + if (con == -1) + aty128_encode_var(var, &info->default_par, info); + else + *var = fb_display[con].var; + return 0; +} + + + /* + * Set the User Defined Part of the Display + */ + +static int +aty128fb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *fb) +{ + struct fb_info_aty128 *info = (struct fb_info_aty128 *)fb; + struct aty128fb_par par; + struct display *display; + int oldxres, oldyres, oldvxres, oldvyres, oldbpp, oldaccel; + int accel, err; + + display = (con >= 0) ? &fb_display[con] : fb->disp; + + if ((err = aty128_decode_var(var, &par, info))) + return err; + + aty128_encode_var(var, &par, info); + + if ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW) + return 0; + + oldxres = display->var.xres; + oldyres = display->var.yres; + oldvxres = display->var.xres_virtual; + oldvyres = display->var.yres_virtual; + oldbpp = display->var.bits_per_pixel; + oldaccel = display->var.accel_flags; + display->var = *var; + if (oldxres != var->xres || oldyres != var->yres || + oldvxres != var->xres_virtual || oldvyres != var->yres_virtual || + oldbpp != var->bits_per_pixel || oldaccel != var->accel_flags) { + + struct fb_fix_screeninfo fix; + aty128_encode_fix(&fix, &par, info); + display->screen_base = (char *) info->frame_buffer; + display->visual = fix.visual; + display->type = fix.type; + display->type_aux = fix.type_aux; + display->ypanstep = fix.ypanstep; + display->ywrapstep = fix.ywrapstep; + display->line_length = fix.line_length; + display->can_soft_blank = 1; + display->inverse = 0; + + accel = var->accel_flags & FB_ACCELF_TEXT; + aty128_set_disp(display, info, var->bits_per_pixel, accel); + +#if 0 /* acceleration is not ready */ + if (accel) + display->scrollmode = 0; + else +#endif + display->scrollmode = SCROLL_YREDRAW; + + if (info->fb_info.changevar) + (*info->fb_info.changevar)(con); + } + + if (!info->fb_info.display_fg || info->fb_info.display_fg->vc_num == con) + aty128_set_par(&par, info); + + if (oldbpp != var->bits_per_pixel) { + if ((err = fb_alloc_cmap(&display->cmap, 0, 0))) + return err; + do_install_cmap(con, &info->fb_info); + } + + return 0; +} + + +static void +aty128_set_disp(struct display *disp, + struct fb_info_aty128 *info, int bpp, int accel) +{ + switch (bpp) { +#ifdef FBCON_HAS_CFB8 + case 8: + disp->dispsw = accel ? &fbcon_aty128_8 : &fbcon_cfb8; + break; +#endif +#ifdef FBCON_HAS_CFB16 + case 16: + disp->dispsw = &fbcon_cfb16; + disp->dispsw_data = info->fbcon_cmap.cfb16; + break; +#endif +#ifdef FBCON_HAS_CFB24 + case 24: + disp->dispsw = &fbcon_cfb24; + disp->dispsw_data = info->fbcon_cmap.cfb24; + break; +#endif +#ifdef FBCON_HAS_CFB32 + case 32: + disp->dispsw = &fbcon_cfb32; + disp->dispsw_data = info->fbcon_cmap.cfb32; + break; +#endif + default: + disp->dispsw = &fbcon_dummy; + } +} + + +static void +aty128_encode_fix(struct fb_fix_screeninfo *fix, + struct aty128fb_par *par, + const struct fb_info_aty128 *info) +{ + memset(fix, 0, sizeof(struct fb_fix_screeninfo)); + + strcpy(fix->id, aty128fb_name); + fix->smem_start = (long) info->frame_buffer_phys; + fix->smem_len = info->vram_size; + + fix->mmio_start = (long) info->regbase_phys; + fix->mmio_len = 0x1fff; + + fix->type = FB_TYPE_PACKED_PIXELS; + fix->line_length = par->crtc.vxres*par->crtc.bpp/8; + fix->visual = par->crtc.bpp <= 8 ? FB_VISUAL_PSEUDOCOLOR + : FB_VISUAL_DIRECTCOLOR; + + fix->xpanstep = 8; + fix->ypanstep = 1; + + fix->accel = FB_ACCEL_ATI_RAGE128; + return; +} + + + /* + * Get the Fixed Part of the Display + */ +static int +aty128fb_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *fb) +{ + const struct fb_info_aty128 *info = (struct fb_info_aty128 *)fb; + struct aty128fb_par par; + + if (con == -1) + par = info->default_par; + else + aty128_decode_var(&fb_display[con].var, &par, info); + + aty128_encode_fix(fix, &par, info); + return 0; +} + + + /* + * Pan or Wrap the Display + * + * Not supported (yet!) + */ +static int +aty128fb_pan_display(struct fb_var_screeninfo *var, int con, + struct fb_info *info) +{ + if (var->xoffset != 0 || var->yoffset != 0) + return -EINVAL; + + return 0; +} + + + /* + * Get the Colormap + */ + +static int +aty128fb_get_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info) +{ + if (!info->display_fg || + con == info->display_fg->vc_num) /* current console ? */ + return fb_get_cmap(cmap, kspc, aty128_getcolreg, info); + else if (fb_display[con].cmap.len) /* non default colormap? */ + fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2); + else { + int size = (fb_display[con].var.bits_per_pixel <= 8) ? 256 : 32; + fb_copy_cmap(fb_default_cmap(size), cmap, kspc ? 0 : 2); + } + return 0; +} + + /* + * Set the Colormap + */ + +static int +aty128fb_set_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info) +{ + int err; + struct display *disp; + + if (con >= 0) + disp = &fb_display[con]; + else + disp = info->disp; + if (!disp->cmap.len) { /* no colormap allocated? */ + int size = (disp->var.bits_per_pixel <= 16) ? 256 : 32; + if ((err = fb_alloc_cmap(&disp->cmap, size, 0))) + return err; + } + if (!info->display_fg || con == info->display_fg->vc_num) +/* current console? */ + return fb_set_cmap(cmap, kspc, aty128_setcolreg, info); + else + fb_copy_cmap(cmap, &disp->cmap, kspc ? 0 : 1); + return 0; +} + + + /* + * Virtual Frame Buffer Specific ioctls + */ + +static int +aty128fb_ioctl(struct inode *inode, struct file *file, u_int cmd, + u_long arg, int con, struct fb_info *info) +{ + return -EINVAL; +} + + +int __init +aty128fb_setup(char *options) +{ + char *this_opt; + + if (!options || !*options) + return 0; + + for (this_opt = strtok(options, ","); this_opt; + this_opt = strtok(NULL, ",")) { + if (!strncmp(this_opt, "font:", 5)) { + char *p; + int i; + + p = this_opt +5; + for (i = 0; i < sizeof(fontname) - 1; i++) + if (!*p || *p == ' ' || *p == ',') + break; + memcpy(fontname, this_opt + 5, i); + fontname[i] = 0; + } +#if defined(CONFIG_PPC) + if (!strncmp(this_opt, "vmode:", 6)) { + unsigned int vmode = simple_strtoul(this_opt+6, NULL, 0); + if (vmode > 0 && vmode <= VMODE_MAX) + default_vmode = vmode; + } else if (!strncmp(this_opt, "cmode:", 6)) { + unsigned int cmode = simple_strtoul(this_opt+6, NULL, 0); + switch (cmode) { + case 0: + case 8: + default_cmode = CMODE_8; + break; + case 15: + case 16: + default_cmode = CMODE_16; + break; + case 24: + case 32: + default_cmode = CMODE_32; + break; + } + } +#endif +#ifdef CONFIG_MTRR + if(mtrr) { + ACCESS_FBINFO(mtrr.vram) = + mtrr_add(video_base_phys, ACCESS_FBINFO(video.len), + MTRR_TYPE_WRCOMB, 1); + ACCESS_FBINFO(mtrr.valid_vram) = 1; + printk(KERN_INFO "aty128fb: MTRR set to ON\n"); + } +#endif + } + return 0; +} + + + /* + * Initialisation + */ + +static int +aty128_init(struct fb_info_aty128 *info, const char *name) +{ + struct fb_var_screeninfo var; + u32 dac; + int j, k; + u8 chip_rev; + + if (!register_test(info)) { + printk("Can't write to video registers\n"); + return 0; + } + + if (!info->vram_size) /* may have already been probed */ + info->vram_size = aty_ld_le32(CONFIG_MEMSIZE) & 0x03FFFFFF; + + chip_rev = (aty_ld_le32(CONFIG_CNTL) >> 16) & 0x1F; + + /* TODO be more verbose */ + printk("aty128fb: Rage128 [rev 0x%x] ", chip_rev); + + if (info->vram_size % (1024 * 1024) == 0) + printk("%dM ", info->vram_size / (1024*1024)); + else + printk("%dk ", info->vram_size / 1024); + + var = default_var; + +#ifdef CONFIG_PMAC + + if (default_vmode == VMODE_NVRAM) { +#ifdef CONFIG_NVRAM + default_vmode = nvram_read_byte(NV_VMODE); + if (default_vmode <= 0 || default_vmode > VMODE_MAX) +#endif /* CONFIG_NVRAM */ + default_vmode = VMODE_CHOOSE; + } + + if (default_cmode == CMODE_NVRAM) { +#ifdef CONFIG_NVRAM + default_cmode = nvram_read_byte(NV_CMODE); + if (default_cmode < CMODE_8 || default_cmode > CMODE_32) +#endif /* CONFIG_NVRAM */ + default_vmode = VMODE_CHOOSE; + } + + if (default_vmode != VMODE_CHOOSE && + mac_vmode_to_var(default_vmode, default_cmode, &var)) + var = default_var; + +#endif /* CONFIG_PMAC */ + + if (aty128_decode_var(&var, &info->default_par, info)) { + printk("Cannot set default mode.\n"); + return 0; + } + + /* fill in info */ + strcpy(info->fb_info.modename, aty128fb_name); + info->fb_info.node = -1; + info->fb_info.fbops = &aty128fb_ops; + info->fb_info.disp = &info->disp; + strcpy(info->fb_info.fontname, fontname); + info->fb_info.changevar = NULL; + info->fb_info.switch_con = &aty128fbcon_switch; + info->fb_info.blank = &aty128fbcon_blank; + info->fb_info.flags = FBINFO_FLAG_DEFAULT; + + for (j = 0; j < 16; j++) { + k = color_table[j]; + info->palette[j].red = default_red[k]; + info->palette[j].green = default_grn[k]; + info->palette[j].blue = default_blu[k]; + } + + dac = aty_ld_le32(DAC_CNTL) & 15; /* preserve lower three bits */ + dac |= DAC_8BIT_EN; /* set 8 bit dac */ + dac |= (0xFF << 24); /* set DAC mask */ + aty_st_le32(DAC_CNTL, dac); + + /* turn off bus mastering, just in case */ + aty_st_le32(BUS_CNTL, aty_ld_le32(BUS_CNTL) | BUS_MASTER_DIS); + + aty128fb_set_var(&var, -1, &info->fb_info); + aty128_init_engine(&info->default_par, info); + + printk("\n"); + if (register_framebuffer(&info->fb_info) < 0) + return 0; + + printk("fb%d: %s frame buffer device on %s\n", + GET_FB_IDX(info->fb_info.node), aty128fb_name, name); + + return 1; /* success! */ +} + + +void __init +aty128fb_init(void) +{ +#if defined(CONFIG_FB_OF) +/* let offb handle init */ +#elif defined (CONFIG_PCI) + aty128pci_probe(); +#endif +} + + +void +aty128pci_probe(void) +{ + struct pci_dev *pdev; + struct fb_info_aty128 *info; + unsigned long fb_addr, reg_addr; + u16 tmp; + + for (pdev = pci_devices; pdev; pdev = pdev->next) { + if (((pdev->class >> 16) == PCI_BASE_CLASS_DISPLAY) && + (pdev->vendor == PCI_VENDOR_ID_ATI)) { + struct resource *rp; + + /* FIXME add other known R128 device ID's */ + switch (pdev->device) { + case 0x5245: + case 0x5246: + case 0x524B: + case 0x524C: + break; + default: + continue; + } + + rp = &pdev->resource[0]; + fb_addr = rp->start; + if (!fb_addr) + continue; + fb_addr &= PCI_BASE_ADDRESS_MEM_MASK; + + rp = &pdev->resource[2]; + reg_addr = rp->start; + if (!reg_addr) + continue; + reg_addr &= PCI_BASE_ADDRESS_MEM_MASK; + + info = kmalloc(sizeof(struct fb_info_aty128), GFP_ATOMIC); + if (!info) { + printk("aty128fb: can't alloc fb_info_aty128\n"); + return; + } + memset(info, 0, sizeof(struct fb_info_aty128)); + + info->regbase_phys = reg_addr; + info->regbase = (unsigned long) ioremap(reg_addr, 0x1FFF); + + if (!info->regbase) { + kfree(info); + return; + } + + info->vram_size = aty_ld_le32(CONFIG_MEMSIZE) & 0x03FFFFFF; + + info->frame_buffer = fb_addr; + info->frame_buffer = (unsigned long) + ioremap(fb_addr, info->vram_size); + + if (!info->frame_buffer) { + kfree(info); + return; + } + + pci_read_config_word(pdev, PCI_COMMAND, &tmp); + if (!(tmp & PCI_COMMAND_MEMORY)) { + tmp |= PCI_COMMAND_MEMORY; + pci_write_config_word(pdev, PCI_COMMAND, tmp); + } + +#if defined(CONFIG_PPC) + aty128_timings(info); +#else + if (!aty128find_ROM(info)) { + printk("Rage128 BIOS not located. Guessing...\n"); + aty128_timings(info); + } + else + aty128_get_pllinfo(info); +#endif + + if (!aty128_init(info, "PCI")) { + kfree(info); + return; + } + } + } +} + + +static int +aty128find_ROM(struct fb_info_aty128 *info) +{ + u32 segstart; + char *rom_base; + char *rom_base1; + char *rom; + int stage; + int i; + char aty_rom_sig[] = "761295520"; + char R128_sig[] = "R128"; + int flag = 0; +DBG("E aty128find_ROM"); + + for (segstart = 0x000c0000; segstart < 0x000f0000; segstart += 0x00001000) { + stage = 1; + + rom_base = (char *) ioremap(segstart, 0x1000); + rom_base1 = (char *) (rom_base+1); + + if ((*rom_base == 0x55) && (((*rom_base1) & 0xff) == 0xaa)) { + stage = 2; + } + + if (stage != 2) { + iounmap(rom_base); + continue; + } + rom = rom_base; + + for (i = 0; (i < 128 - strlen(aty_rom_sig)) && (stage != 3); i++) { + if (aty_rom_sig[0] == *rom) { + if (strncmp(aty_rom_sig, rom, strlen(aty_rom_sig)) == 0) { + stage = 3; + } + } + rom++; + } + if (stage != 3) { + iounmap(rom_base); + continue; + } + rom = rom_base; + + for (i = 0; (i < 512) && (stage != 4); i++) { + if (R128_sig[0] == *rom) { + if (strncmp(R128_sig, rom, strlen(R128_sig)) == 0) { + stage = 4; + } + } + rom++; + } + if (stage != 4) { + iounmap(rom_base); + continue; + } + + printk("Rage128 BIOS located at segment %4.4X\n", (u32)rom_base); + info->BIOS_SEG = (u32)rom_base; + flag = 1; + + break; + } +DBG("L aty128find_ROM"); + return (flag); +} + + +static void +aty128_get_pllinfo(struct fb_info_aty128 *info) +{ + u32 bios_header; + u32 *header_ptr; + u16 bios_header_offset, pll_info_offset; + PLL_BLOCK pll; +DBG("E aty128_get_pllinfo"); + + bios_header = info->BIOS_SEG + 0x48L; + header_ptr = (u32 *)bios_header; + + bios_header_offset = (u16)*header_ptr; + bios_header = info->BIOS_SEG + (u32)bios_header_offset; + bios_header += 0x30; + + header_ptr = (u32 *)bios_header; + pll_info_offset = (u16)*header_ptr; + header_ptr = (u32 *)(info->BIOS_SEG + (u32)pll_info_offset); + + memcpy(&pll, header_ptr, 50); + + info->constants.ppll_max = pll.PCLK_max_freq; + info->constants.ppll_min = pll.PCLK_min_freq; + info->constants.xclk = (u32)pll.XCLK; + info->constants.ref_divider = (u32)pll.PCLK_ref_divider; + info->constants.dotclock = (u32)pll.PCLK_ref_freq; + + info->constants.fifo_width = 128; + info->constants.fifo_depth = 32; + + switch(aty_ld_le32(MEM_CNTL) & 0x03) { + case 0: + info->mem = &sdr_128; + break; + case 1: + info->mem = &sdr_sgram; + break; + case 2: + info->mem = &ddr_sgram; + break; + default: + info->mem = &sdr_sgram; + } + +DBG("L aty128get_pllinfo"); + return; +} + + +#ifdef CONFIG_FB_OF +void +aty128fb_of_init(struct device_node *dp) +{ + unsigned long addr, reg_addr, fb_addr; + struct fb_info_aty128 *info; + u8 bus, devfn; + u16 cmd; + + switch (dp->n_addrs) { + case 3: + fb_addr = dp->addrs[0].address; + reg_addr = dp->addrs[2].address; + break; + default: + printk("aty128fb: TODO unexpected addresses\n"); + return; + } + + addr = (unsigned long) ioremap(reg_addr, 0x1FFF); + if (!addr) { + printk("aty128fb: can't map memory registers\n"); + return; + } + + info = kmalloc(sizeof(struct fb_info_aty128), GFP_ATOMIC); + if (!info) { + printk("aty128fb: can't alloc fb_info_aty128\n"); + return; + } + memset(info, 0, sizeof(struct fb_info_aty128)); + + info->regbase_phys = reg_addr; + info->regbase = addr; + + /* enabled memory-space accesses using config-space command register */ + if (pci_device_loc(dp, &bus, &devfn) == 0) { + pcibios_read_config_word(bus, devfn, PCI_COMMAND, &cmd); + if (!(cmd & PCI_COMMAND_MEMORY)) { + cmd |= PCI_COMMAND_MEMORY; + pcibios_write_config_word(bus, devfn, PCI_COMMAND, cmd); + } + } + + info->vram_size = aty_ld_le32(CONFIG_MEMSIZE) & 0x03FFFFFF; + info->frame_buffer_phys = fb_addr; + info->frame_buffer = (unsigned long) ioremap(fb_addr, info->vram_size); + + /* + * TODO find OF values/hints. + * + * If we are booted from BootX, the MacOS ATI driver will likely have + * left useful tidbits in the DeviceRegistry. + */ + + if (!info->frame_buffer) { + printk("aty128fb: can't map frame buffer\n"); + return; + } + + aty128_timings(info); + + if (!aty128_init(info, dp->full_name)) { + kfree(info); + return; + } +} +#endif + + +/* fill in known card constants if pll_block is not available */ +static void +aty128_timings(struct fb_info_aty128 *info) +{ + /* TODO make an attempt at probing */ + + info->constants.dotclock = 2950; + + /* from documentation */ + info->constants.ppll_min = 12500; + info->constants.ppll_max = 25000; /* 23000 on some cards? */ + +#if 1 + /* XXX TODO. Calculate properly. Fix OF's pll ideas. */ + info->constants.ref_divider = 0x3b; + aty_st_pll(PPLL_REF_DIV, info->constants.ref_divider); + aty_pll_writeupdate(info); + + aty_st_pll(X_MPLL_REF_FB_DIV, 0x004c4c1e); + aty_pll_writeupdate(info); +#else + info->constants.ref_divider = aty_ld_pll(PPLL_REF_DIV) & PPLL_REF_DIV_MASK; +#endif + + /* TODO. Calculate */ + info->constants.xclk = 0x1d4d; /* same as mclk */ + + info->constants.fifo_width = 128; + info->constants.fifo_depth = 32; + + switch (aty_ld_le32(MEM_CNTL) & 0x3) { + case 0: + info->mem = &sdr_128; + break; + case 1: + info->mem = &sdr_sgram; + break; + case 2: + info->mem = &ddr_sgram; + break; + default: + info->mem = &sdr_sgram; + } +} + + +static int +aty128fbcon_switch(int con, struct fb_info *fb) +{ + currcon = con; + + /* Do we have to save the colormap? */ + if (fb_display[currcon].cmap.len) + fb_get_cmap(&fb_display[currcon].cmap, 1, aty128_getcolreg, fb); + +#if 1 + aty128fb_set_var(&fb_display[con].var, con, fb); +#else +{ + struct fb_info_aty128 *info = (struct fb_info_aty128 *) fb; + struct aty128fb_par par; + + aty128_decode_var(&fb_display[con].var, &par, info); + aty128_set_par(&par, info); + aty128_set_disp(&fb_display[con], info, + fb_display[con].var.bits_per_pixel); + + do_install_cmap(con, fb); +} +#endif + + return 1; +} + + + /* + * Blank the display. + */ + +static void +aty128fbcon_blank(int blank, struct fb_info *fb) +{ + struct fb_info_aty128 *info = (struct fb_info_aty128 *)fb; + u8 state = 0; + + if (blank & VESA_VSYNC_SUSPEND) + state |= 2; + if (blank & VESA_HSYNC_SUSPEND) + state |= 1; + if (blank & VESA_POWERDOWN) + state |= 4; + + aty_st_8(CRTC_EXT_CNTL+1, state); +} + + + /* + * Read a single color register and split it into + * colors/transparent. Return != 0 for invalid regno. + */ +static int +aty128_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue, + u_int *transp, struct fb_info *fb) +{ + struct fb_info_aty128 *info = (struct fb_info_aty128 *) fb; + + if (regno > 255) + return 1; + + *red = (info->palette[regno].red<<8) | info->palette[regno].red; + *green = (info->palette[regno].green<<8) | info->palette[regno].green; + *blue = (info->palette[regno].blue<<8) | info->palette[regno].blue; + *transp = 0; + return 0; +} + + + /* + * Set a single color register. The values supplied are already + * rounded down to the hardware's capabilities (according to the + * entries in the var structure). Return != 0 for invalid regno. + */ +static int +aty128_setcolreg(u_int regno, u_int red, u_int green, u_int blue, + u_int transp, struct fb_info *fb) +{ + struct fb_info_aty128 *info = (struct fb_info_aty128 *) fb; + u32 col; + + if (regno > 255) + return 1; + + red >>= 8; + green >>= 8; + blue >>= 8; + info->palette[regno].red = red; + info->palette[regno].green = green; + info->palette[regno].blue = blue; + + aty_st_8(PALETTE_INDEX, regno); + col = red << 16 | green << 8 | blue; + aty_st_le32(PALETTE_DATA, col); + + if (regno < 16) + switch (info->current_par.crtc.bpp) { +#ifdef FBCON_HAS_CFB16 + case 16: + info->fbcon_cmap.cfb16[regno] = (regno << 10) | (regno << 5) | + regno; + break; +#endif +#ifdef FBCON_HAS_CFB24 + case 24: + info->fbcon_cmap.cfb24[regno] = (regno << 16) | (regno << 8) | + regno; + break; +#endif +#ifdef FBCON_HAS_CFB32 + case 32: + { + u32 i; + i = (regno << 8) | regno; + info->fbcon_cmap.cfb32[regno] = (i << 16) | i; + } + break; +#endif + } + return 0; +} + + +static void +do_install_cmap(int con, struct fb_info *info) +{ + if (con != currcon) + return; + if (fb_display[con].cmap.len) + fb_set_cmap(&fb_display[con].cmap, 1, aty128_setcolreg, info); + else { + int size = (fb_display[con].var.bits_per_pixel <= 8) ? 256 : 16; + fb_set_cmap(fb_default_cmap(size), 1, aty128_setcolreg, info); + } +} + + + /* + * Accelerated functions + */ + +static void +aty128_rectdraw(s16 x, s16 y, u16 width, u16 height, + struct fb_info_aty128 *info) +{ + /* perform rectangle fill */ + wait_for_fifo(2, info); + aty_st_le32(DST_Y_X, (y << 16) | x); + aty_st_le32(DST_HEIGHT_WIDTH, (height << 16) | width); +} + + +static void +aty128_rectcopy(int srcx, int srcy, int dstx, int dsty, + u_int width, u_int height, + struct fb_info_aty128 *info) +{ + u32 direction = DST_LAST_PEL; + u32 pitch_value; + + if (!width || !height) + return; + + pitch_value = info->current_par.crtc.vxres; + if (info->current_par.crtc.bpp == 24) { + /* In 24 bpp, the engine is in 8 bpp - this requires that all */ + /* horizontal coordinates and widths must be adjusted */ + pitch_value *= 3; + srcx *= 3; + dstx *= 3; + width *= 3; + } + + if (srcy < dsty) { + dsty += height - 1; + srcy += height - 1; + } else + direction |= DST_Y_TOP_TO_BOTTOM; + + if (srcx < dstx) { + dstx += width - 1; + srcx += width - 1; + } else + direction |= DST_X_LEFT_TO_RIGHT; + + wait_for_fifo(4, info); + aty_st_le32(SRC_Y_X, (srcy << 16) | srcx); + aty_st_le32(DP_MIX, ROP3_SRCCOPY | DP_SRC_RECT); + aty_st_le32(DP_CNTL, direction); + aty_st_le32(DP_DATATYPE, aty_ld_le32(DP_DATATYPE) | SRC_DSTCOLOR); + aty128_rectdraw(dstx, dsty, width, height, info); +} + +static void +fbcon_aty128_bmove(struct display *p, int sy, int sx, int dy, int dx, + int height, int width) +{ + sx *= fontwidth(p); + sy *= fontheight(p); + dx *= fontwidth(p); + dy *= fontheight(p); + width *= fontwidth(p); + height *= fontheight(p); + + aty128_rectcopy(sx, sy, dx, dy, width, height, + (struct fb_info_aty128 *)p->fb_info); +} + +#ifdef FBCON_HAS_CFB8 +static struct display_switch fbcon_aty128_8 = { + fbcon_cfb8_setup, fbcon_aty128_bmove, fbcon_cfb8_clear, fbcon_cfb8_putc, + fbcon_cfb8_putcs, fbcon_cfb8_revc, NULL, NULL, fbcon_cfb8_clear_margins, + FONTWIDTH(4)|FONTWIDTH(8)|FONTWIDTH(12)|FONTWIDTH(16) +}; +#endif + +#if defined(MODULE) && defined(DEBUG) +int +init_module(void) +{ + aty128pci_probe(); + return 0; +} + +void +cleanup_module(void) +{ +/* XXX unregister! */ +} +#endif /* MODULE */ diff --git a/drivers/video/atyfb.c b/drivers/video/atyfb.c index edba704a3..db21bfc6b 100644 --- a/drivers/video/atyfb.c +++ b/drivers/video/atyfb.c @@ -2192,7 +2192,7 @@ static int aty_dsp_gt(const struct fb_info_aty *info, u8 bpp, } dsp_precision -= 5; /* fifo_off<<6 */ - fifo_off = ((xclks_per_row*(fifo_size-1))>>5)+(1<<6); + fifo_off = ((xclks_per_row*(fifo_size-1))>>5)+(3<<6); if (info->total_vram > 1*1024*1024) { if (info->ram_type >= SDRAM) { @@ -2219,7 +2219,7 @@ static int aty_dsp_gt(const struct fb_info_aty *info, u8 bpp, if (xclks_per_row >= (page_size<<11)) fifo_on = ((2*page_size+1)<<6)+(xclks_per_row>>5); else - fifo_on = (3*page_size)<<6; + fifo_on = (3*page_size+2)<<6; dsp_xclks_per_row = xclks_per_row>>dsp_precision; dsp_on = fifo_on>>dsp_precision; diff --git a/drivers/video/chipsfb.c b/drivers/video/chipsfb.c index dd20b767e..8078a6676 100644 --- a/drivers/video/chipsfb.c +++ b/drivers/video/chipsfb.c @@ -282,6 +282,14 @@ static void chipsfb_blank(int blank, struct fb_info *info) // useful at blank = 1 too (saves battery, extends backlight life) if (blank) { pmu_enable_backlight(0); + /* get the palette from the chip */ + for (i = 0; i < 256; ++i) { + out_8(p->io_base + 0x3c7, i); + udelay(1); + p->palette[i].red = in_8(p->io_base + 0x3c9); + p->palette[i].green = in_8(p->io_base + 0x3c9); + p->palette[i].blue = in_8(p->io_base + 0x3c9); + } for (i = 0; i < 256; ++i) { out_8(p->io_base + 0x3c8, i); udelay(1); @@ -291,7 +299,13 @@ static void chipsfb_blank(int blank, struct fb_info *info) } } else { pmu_enable_backlight(1); - do_install_cmap(currcon, info); + for (i = 0; i < 256; ++i) { + out_8(p->io_base + 0x3c8, i); + udelay(1); + out_8(p->io_base + 0x3c9, p->palette[i].red); + out_8(p->io_base + 0x3c9, p->palette[i].green); + out_8(p->io_base + 0x3c9, p->palette[i].blue); + } } } @@ -711,20 +725,10 @@ chips_sleep_notify(struct pmu_sleep_notifier *self, int when) for (p = all_chips; p != NULL; p = p->next) { int nb = p->var.yres * p->fix.line_length; - int i; switch (when) { case PBOOK_SLEEP_NOW: chipsfb_blank(1, (struct fb_info *)p); - /* get the palette from the chip, Xpmac seems - to set it directly in the chip */ - for (i = 0; i < 256; ++i) { - out_8(p->io_base + 0x3c8, i); - udelay(1); - p->palette[i].red = in_8(p->io_base + 0x3c9); - p->palette[i].green = in_8(p->io_base + 0x3c9); - p->palette[i].blue = in_8(p->io_base + 0x3c9); - } p->save_framebuffer = vmalloc(nb); if (p->save_framebuffer) memcpy(p->save_framebuffer, diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index 24504b17a..9bcb6920e 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -68,6 +68,8 @@ extern int offb_init(void); extern int offb_setup(char*); extern int atyfb_init(void); extern int atyfb_setup(char*); +extern int aty128fb_init(void); +extern int aty128fb_setup(char*); extern int igafb_init(void); extern int igafb_setup(char*); extern int imsttfb_init(void); @@ -150,6 +152,9 @@ static struct { #ifdef CONFIG_FB_ATY { "atyfb", atyfb_init, atyfb_setup }, #endif +#ifdef CONFIG_FB_ATY128 + { "aty128fb", aty128fb_init, aty128fb_setup }, +#endif #ifdef CONFIG_FB_IGA { "igafb", igafb_init, igafb_setup }, #endif diff --git a/drivers/video/offb.c b/drivers/video/offb.c index 068282244..d5e0c2b04 100644 --- a/drivers/video/offb.c +++ b/drivers/video/offb.c @@ -293,6 +293,9 @@ static int offb_ioctl(struct inode *inode, struct file *file, u_int cmd, #ifdef CONFIG_FB_ATY extern void atyfb_of_init(struct device_node *dp); #endif /* CONFIG_FB_ATY */ +#ifdef CONFIG_FB_ATY128 +extern void aty128fb_of_init(struct device_node *dp); +#endif /* CONFIG_FB_ATY */ #ifdef CONFIG_FB_S3TRIO extern void s3triofb_init_of(struct device_node *dp); #endif /* CONFIG_FB_S3TRIO */ @@ -349,6 +352,21 @@ int __init offb_init(void) dp->addrs[0].size = 0x01000000; } } + + /* + * The LTPro on the Lombard powerbook has no addresses + * on the display nodes, they are on their parent. + */ + if (dp->n_addrs == 0 && device_is_compatible(dp, "ATY,264LTPro")) { + int na; + unsigned int *ap = (unsigned int *) + get_property(dp, "AAPL,address", &na); + if (ap != 0) + for (na /= sizeof(unsigned int); na > 0; --na, ++ap) + if (*ap <= addr && addr < *ap + 0x1000000) + goto foundit; + } + /* * See if the display address is in one of the address * ranges for this display. @@ -359,6 +377,7 @@ int __init offb_init(void) break; } if (i < dp->n_addrs) { + foundit: printk(KERN_INFO "MacOS display is %s\n", dp->full_name); macos_display = dp; break; @@ -397,6 +416,12 @@ int __init offb_init(void) static int __init offb_init_driver(struct device_node *dp) { +#ifdef CONFIG_FB_ATY128 + if (!strncmp(dp->name, "ATY,Rage128", 11)) { + aty128fb_of_init(dp); + return 1; + } +#endif #ifdef CONFIG_FB_ATY if (!strncmp(dp->name, "ATY", 3)) { atyfb_of_init(dp); diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c index cc72f4e18..ca5d8e8cb 100644 --- a/fs/binfmt_aout.c +++ b/fs/binfmt_aout.c @@ -270,7 +270,6 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs unsigned long fd_offset; unsigned long rlim; int retval; - static unsigned long error_time=0; ex = *((struct exec *) bprm->buf); /* exec-header */ if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != OMAGIC && @@ -282,29 +281,6 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs fd_offset = N_TXTOFF(ex); -#ifdef __i386__ - if (N_MAGIC(ex) == ZMAGIC && fd_offset != BLOCK_SIZE) { - if((jiffies-error_time) >5) - { - printk(KERN_NOTICE "N_TXTOFF != BLOCK_SIZE. See a.out.h.\n"); - error_time=jiffies; - } - return -ENOEXEC; - } - - if (N_MAGIC(ex) == ZMAGIC && ex.a_text && - bprm->dentry->d_inode->i_op && - bprm->dentry->d_inode->i_op->get_block && - (fd_offset < bprm->dentry->d_inode->i_sb->s_blocksize)) { - if((jiffies-error_time) >5) - { - printk(KERN_NOTICE "N_TXTOFF < BLOCK_SIZE. Please convert binary.\n"); - error_time=jiffies; - } - return -ENOEXEC; - } -#endif - /* Check initial limits. This avoids letting people circumvent * size limits imposed on them by creating programs with large * arrays in the data or bss. @@ -364,26 +340,32 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs flush_icache_range((unsigned long) 0, (unsigned long) ex.a_text+ex.a_data); } else { + static unsigned long error_time, error_time2; if ((ex.a_text & 0xfff || ex.a_data & 0xfff) && - (N_MAGIC(ex) != NMAGIC)) + (N_MAGIC(ex) != NMAGIC) && (jiffies-error_time2) > 5*HZ) + { printk(KERN_NOTICE "executable not page aligned\n"); + error_time2 = jiffies; + } fd = open_dentry(bprm->dentry, O_RDONLY); if (fd < 0) return fd; file = fget(fd); - if ((fd_offset & ~PAGE_MASK) != 0) { + if ((fd_offset & ~PAGE_MASK) != 0 && + (jiffies-error_time) > 5*HZ) + { printk(KERN_WARNING "fd_offset is not page aligned. Please convert program: %s\n", - file->f_dentry->d_name.name - ); + file->f_dentry->d_name.name); + error_time = jiffies; } if (!file->f_op || !file->f_op->mmap || ((fd_offset & ~PAGE_MASK) != 0)) { fput(file); sys_close(fd); - do_brk(0, ex.a_text+ex.a_data); + do_brk(N_TXTADDR(ex), ex.a_text+ex.a_data); read_exec(bprm->dentry, fd_offset, (char *) N_TXTADDR(ex), ex.a_text+ex.a_data, 0); flush_icache_range((unsigned long) N_TXTADDR(ex), @@ -493,12 +475,6 @@ do_load_aout_library(int fd) goto out_putf; } - if (N_MAGIC(ex) == ZMAGIC && N_TXTOFF(ex) && - (N_TXTOFF(ex) < inode->i_sb->s_blocksize)) { - printk("N_TXTOFF < BLOCK_SIZE. Please convert library\n"); - goto out_putf; - } - if (N_FLAGS(ex)) goto out_putf; @@ -508,14 +484,17 @@ do_load_aout_library(int fd) start_addr = ex.a_entry & 0xfffff000; if ((N_TXTOFF(ex) & ~PAGE_MASK) != 0) { - printk(KERN_WARNING - "N_TXTOFF is not page aligned. Please convert library: %s\n", - file->f_dentry->d_name.name - ); - - do_mmap(NULL, start_addr & PAGE_MASK, ex.a_text + ex.a_data + ex.a_bss, - PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_FIXED| MAP_PRIVATE, 0); + static unsigned long error_time; + + if ((jiffies-error_time) > 5*HZ) + { + printk(KERN_WARNING + "N_TXTOFF is not page aligned. Please convert library: %s\n", + file->f_dentry->d_name.name); + error_time = jiffies; + } + + do_brk(start_addr, ex.a_text + ex.a_data + ex.a_bss); read_exec(file->f_dentry, N_TXTOFF(ex), (char *)start_addr, ex.a_text + ex.a_data, 0); diff --git a/fs/buffer.c b/fs/buffer.c index c43c54a36..39dd880f8 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -26,6 +26,8 @@ /* Thread it... -DaveM */ +/* async buffer flushing, 1999 Andrea Arcangeli <andrea@suse.de> */ + #include <linux/sched.h> #include <linux/fs.h> #include <linux/malloc.h> @@ -76,6 +78,7 @@ static rwlock_t hash_table_lock = RW_LOCK_UNLOCKED; static struct buffer_head *lru_list[NR_LIST]; static spinlock_t lru_list_lock = SPIN_LOCK_UNLOCKED; static int nr_buffers_type[NR_LIST] = {0,}; +static unsigned long size_buffers_type[NR_LIST] = {0,}; static struct buffer_head * unused_list = NULL; static int nr_unused_buffer_heads = 0; @@ -93,7 +96,7 @@ static kmem_cache_t *bh_cachep; static int grow_buffers(int size); /* This is used by some architectures to estimate available memory. */ -atomic_t buffermem = ATOMIC_INIT(0); +atomic_t buffermem_pages = ATOMIC_INIT(0); /* Here is the parameter block for the bdflush process. If you add or * remove any of the parameters, make sure to update kernel/sysctl.c. @@ -114,18 +117,18 @@ union bdflush_param { each time we call refill */ int nref_dirt; /* Dirty buffer threshold for activating bdflush when trying to refill buffers. */ - int dummy1; /* unused */ + int interval; /* jiffies delay between kupdate flushes */ int age_buffer; /* Time for normal buffer to age before we flush it */ int age_super; /* Time for superblock to age before we flush it */ int dummy2; /* unused */ int dummy3; /* unused */ } b_un; unsigned int data[N_PARAM]; -} bdf_prm = {{40, 500, 64, 256, 15, 30*HZ, 5*HZ, 1884, 2}}; +} bdf_prm = {{40, 500, 64, 256, 5*HZ, 30*HZ, 5*HZ, 1884, 2}}; /* These are the min and max parameter values that we will allow to be assigned */ int bdflush_min[N_PARAM] = { 0, 10, 5, 25, 0, 1*HZ, 1*HZ, 1, 1}; -int bdflush_max[N_PARAM] = {100,50000, 20000, 20000,1000, 6000*HZ, 6000*HZ, 2047, 5}; +int bdflush_max[N_PARAM] = {100,50000, 20000, 20000,600*HZ, 6000*HZ, 6000*HZ, 2047, 5}; void wakeup_bdflush(int); @@ -482,6 +485,7 @@ static void __insert_into_lru_list(struct buffer_head * bh, int blist) (*bhp)->b_prev_free->b_next_free = bh; (*bhp)->b_prev_free = bh; nr_buffers_type[blist]++; + size_buffers_type[blist] += bh->b_size; } static void __remove_from_lru_list(struct buffer_head * bh, int blist) @@ -495,6 +499,7 @@ static void __remove_from_lru_list(struct buffer_head * bh, int blist) lru_list[blist] = NULL; bh->b_next_free = bh->b_prev_free = NULL; nr_buffers_type[blist]--; + size_buffers_type[blist] -= bh->b_size; } } @@ -813,6 +818,27 @@ out: return bh; } +/* -1 -> no need to flush + 0 -> async flush + 1 -> sync flush (wait for I/O completation) */ +static int balance_dirty_state(kdev_t dev) +{ + unsigned long dirty, tot, hard_dirty_limit, soft_dirty_limit; + + dirty = size_buffers_type[BUF_DIRTY] >> PAGE_SHIFT; + tot = nr_lru_pages + nr_free_pages + nr_free_highpages; + hard_dirty_limit = tot * bdf_prm.b_un.nfract / 100; + soft_dirty_limit = hard_dirty_limit >> 1; + + if (dirty > soft_dirty_limit) + { + if (dirty > hard_dirty_limit) + return 1; + return 0; + } + return -1; +} + /* * if a new dirty buffer is created we need to balance bdflush. * @@ -820,23 +846,13 @@ out: * pressures on different devices - thus the (currently unused) * 'dev' parameter. */ -static int too_many_dirty_buffers; - void balance_dirty(kdev_t dev) { - int dirty = nr_buffers_type[BUF_DIRTY]; - int ndirty = bdf_prm.b_un.ndirty; - - if (dirty > ndirty) { - if (dirty > 2*ndirty) { - too_many_dirty_buffers = 1; - wakeup_bdflush(1); - return; - } - wakeup_bdflush(0); - } - too_many_dirty_buffers = 0; - return; + int state = balance_dirty_state(dev); + + if (state < 0) + return; + wakeup_bdflush(state); } static inline void __mark_dirty(struct buffer_head *bh, int flag) @@ -1250,7 +1266,7 @@ int block_flushpage(struct inode *inode, struct page *page, unsigned long offset */ if (!offset) { if (!try_to_free_buffers(page)) { - atomic_add(PAGE_CACHE_SIZE, &buffermem); + atomic_inc(&buffermem_pages); return 0; } } @@ -1364,6 +1380,7 @@ int block_write_partial_page(struct file *file, struct page *page, unsigned long unsigned long bbits, blocks, i, len; struct buffer_head *bh, *head; char * target_buf; + int need_balance_dirty; target_buf = (char *)page_address(page) + offset; @@ -1403,6 +1420,7 @@ int block_write_partial_page(struct file *file, struct page *page, unsigned long i = 0; bh = head; partial = 0; + need_balance_dirty = 0; do { if (!bh) BUG(); @@ -1473,8 +1491,7 @@ int block_write_partial_page(struct file *file, struct page *page, unsigned long set_bit(BH_Uptodate, &bh->b_state); if (!test_and_set_bit(BH_Dirty, &bh->b_state)) { __mark_dirty(bh, 0); - if (too_many_dirty_buffers) - balance_dirty(bh->b_dev); + need_balance_dirty = 1; } if (err) { @@ -1488,6 +1505,9 @@ skip: bh = bh->b_this_page; } while (bh != head); + if (need_balance_dirty) + balance_dirty(bh->b_dev); + /* * is this a partial write that happened to make all buffers * uptodate then we can optimize away a bogus readpage() for @@ -1519,6 +1539,7 @@ int block_write_cont_page(struct file *file, struct page *page, unsigned long of struct buffer_head *bh, *head; char * target_buf, *target_data; unsigned long data_offset = offset; + int need_balance_dirty; offset = inode->i_size - page->offset; if (page->offset>inode->i_size) @@ -1566,6 +1587,7 @@ int block_write_cont_page(struct file *file, struct page *page, unsigned long of i = 0; bh = head; partial = 0; + need_balance_dirty = 0; do { if (!bh) BUG(); @@ -1644,8 +1666,7 @@ int block_write_cont_page(struct file *file, struct page *page, unsigned long of set_bit(BH_Uptodate, &bh->b_state); if (!test_and_set_bit(BH_Dirty, &bh->b_state)) { __mark_dirty(bh, 0); - if (too_many_dirty_buffers) - balance_dirty(bh->b_dev); + need_balance_dirty = 1; } if (err) { @@ -1659,6 +1680,9 @@ skip: bh = bh->b_this_page; } while (bh != head); + if (need_balance_dirty) + balance_dirty(bh->b_dev); + /* * is this a partial write that happened to make all buffers * uptodate then we can optimize away a bogus readpage() for @@ -1809,12 +1833,12 @@ int brw_kiovec(int rw, int nr, struct kiobuf *iovec[], dprintk ("iobuf %d %d %d\n", offset, length, size); for (pageind = 0; pageind < iobuf->nr_pages; pageind++) { - page = iobuf->pagelist[pageind]; map = iobuf->maplist[pageind]; - if (map && PageBIGMEM(map)) { + if (map && PageHighMem(map)) { err = -EIO; goto error; } + page = page_address(map); while (length > 0) { blocknr = b[bufind++]; @@ -2090,7 +2114,7 @@ static int grow_buffers(int size) page_map = mem_map + MAP_NR(page); page_map->buffers = bh; lru_cache_add(page_map); - atomic_add(PAGE_SIZE, &buffermem); + atomic_inc(&buffermem_pages); return 1; no_buffer_head: @@ -2168,12 +2192,53 @@ out: busy_buffer_page: /* Uhhuh, start writeback so that we don't end up with all dirty pages */ - too_many_dirty_buffers = 1; wakeup_bdflush(0); ret = 0; goto out; } +/* ================== Debugging =================== */ + +void show_buffers(void) +{ + struct buffer_head * bh; + int found = 0, locked = 0, dirty = 0, used = 0, lastused = 0; + int protected = 0; + int nlist; + static char *buf_types[NR_LIST] = { "CLEAN", "LOCKED", "DIRTY" }; + + printk("Buffer memory: %6dkB\n", + atomic_read(&buffermem_pages) << (PAGE_SHIFT-10)); + +#ifdef __SMP__ /* trylock does nothing on UP and so we could deadlock */ + if (!spin_trylock(&lru_list_lock)) + return; + for(nlist = 0; nlist < NR_LIST; nlist++) { + found = locked = dirty = used = lastused = protected = 0; + bh = lru_list[nlist]; + if(!bh) continue; + + do { + found++; + if (buffer_locked(bh)) + locked++; + if (buffer_protected(bh)) + protected++; + if (buffer_dirty(bh)) + dirty++; + if (atomic_read(&bh->b_count)) + used++, lastused = found; + bh = bh->b_next_free; + } while (bh != lru_list[nlist]); + printk("%8s: %d buffers, %d used (last=%d), " + "%d locked, %d protected, %d dirty\n", + buf_types[nlist], found, used, lastused, + locked, protected, dirty); + } + spin_unlock(&lru_list_lock); +#endif +} + /* ===================== Init ======================= */ /* @@ -2181,7 +2246,7 @@ busy_buffer_page: * Use gfp() for the hash table to decrease TLB misses, use * SLAB cache for buffer heads. */ -void __init buffer_init(unsigned long memory_size) +void __init buffer_init(unsigned long mempages) { int order, i; unsigned int nr_hash; @@ -2189,9 +2254,11 @@ void __init buffer_init(unsigned long memory_size) /* The buffer cache hash table is less important these days, * trim it a bit. */ - memory_size >>= 14; - memory_size *= sizeof(struct buffer_head *); - for (order = 0; (PAGE_SIZE << order) < memory_size; order++) + mempages >>= 14; + + mempages *= sizeof(struct buffer_head *); + + for (order = 0; (1 << order) < mempages; order++) ; /* try to allocate something until we get it or we're asking @@ -2246,21 +2313,92 @@ void __init buffer_init(unsigned long memory_size) * response to dirty buffers. Once this process is activated, we write back * a limited number of buffers to the disks and then go back to sleep again. */ -static DECLARE_WAIT_QUEUE_HEAD(bdflush_wait); static DECLARE_WAIT_QUEUE_HEAD(bdflush_done); struct task_struct *bdflush_tsk = 0; -void wakeup_bdflush(int wait) +void wakeup_bdflush(int block) { + DECLARE_WAITQUEUE(wait, current); + if (current == bdflush_tsk) return; - if (wait) - run_task_queue(&tq_disk); - wake_up(&bdflush_wait); - if (wait) - sleep_on(&bdflush_done); + + if (!block) + { + wake_up_process(bdflush_tsk); + return; + } + + /* kflushd can wakeup us before we have a chance to + go to sleep so we must be smart in handling + this wakeup event from kflushd to avoid deadlocking in SMP + (we are not holding any lock anymore in these two paths). */ + __set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&bdflush_done, &wait); + + wake_up_process(bdflush_tsk); + schedule(); + + remove_wait_queue(&bdflush_done, &wait); + __set_current_state(TASK_RUNNING); } +/* This is the _only_ function that deals with flushing async writes + to disk. + NOTENOTENOTENOTE: we _only_ need to browse the DIRTY lru list + as all dirty buffers lives _only_ in the DIRTY lru list. + As we never browse the LOCKED and CLEAN lru lists they are infact + completly useless. */ +static void flush_dirty_buffers(int check_flushtime) +{ + struct buffer_head * bh, *next; + int flushed = 0, i; + + restart: + spin_lock(&lru_list_lock); + bh = lru_list[BUF_DIRTY]; + if (!bh) + goto out_unlock; + for (i = nr_buffers_type[BUF_DIRTY]; i-- > 0; bh = next) + { + next = bh->b_next_free; + + if (!buffer_dirty(bh)) + { + __refile_buffer(bh); + continue; + } + if (buffer_locked(bh)) + continue; + + if (check_flushtime) + { + /* The dirty lru list is chronogical ordered so + if the current bh is not yet timed out, + then also all the following bhs + will be too young. */ + if (time_before(jiffies, bh->b_flushtime)) + goto out_unlock; + } + else + { + if (++flushed > bdf_prm.b_un.ndirty) + goto out_unlock; + } + + /* OK, now we are committed to write it out. */ + atomic_inc(&bh->b_count); + spin_unlock(&lru_list_lock); + ll_rw_block(WRITE, 1, &bh); + atomic_dec(&bh->b_count); + + if (current->need_resched) + schedule(); + goto restart; + } + out_unlock: + spin_unlock(&lru_list_lock); +} /* * Here we attempt to write back old buffers. We also try to flush inodes @@ -2272,47 +2410,13 @@ void wakeup_bdflush(int wait) static int sync_old_buffers(void) { - int nlist; - lock_kernel(); sync_supers(0); sync_inodes(0); unlock_kernel(); - for(nlist = BUF_LOCKED; nlist <= BUF_DIRTY; nlist++) { - struct buffer_head *bh; - repeat: - spin_lock(&lru_list_lock); - bh = lru_list[nlist]; - if(bh) { - struct buffer_head *next; - int i; - for (i = nr_buffers_type[nlist]; i-- > 0; bh = next) { - next = bh->b_next_free; - - /* If the buffer is not on the proper list, - * then refile it. - */ - if ((nlist == BUF_DIRTY && - (!buffer_dirty(bh) && !buffer_locked(bh))) || - (nlist == BUF_LOCKED && !buffer_locked(bh))) { - __refile_buffer(bh); - continue; - } - - if (buffer_locked(bh) || !buffer_dirty(bh)) - continue; - - /* OK, now we are committed to write it out. */ - atomic_inc(&bh->b_count); - spin_unlock(&lru_list_lock); - ll_rw_block(WRITE, 1, &bh); - atomic_dec(&bh->b_count); - goto repeat; - } - } - spin_unlock(&lru_list_lock); - } + flush_dirty_buffers(1); + /* must really sync all the active I/O request to disk here */ run_task_queue(&tq_disk); return 0; } @@ -2328,6 +2432,10 @@ asmlinkage long sys_bdflush(int func, long data) return -EPERM; if (func == 1) { + /* do_exit directly and let kupdate to do its work alone. */ + do_exit(0); +#if 0 /* left here as it's the only example of lazy-mm-stuff used from + a syscall that doesn't care about the current mm context. */ int error; struct mm_struct *user_mm; @@ -2341,6 +2449,7 @@ asmlinkage long sys_bdflush(int func, long data) error = sync_old_buffers(); end_lazy_tlb(user_mm); return error; +#endif } /* Basically func 1 means read param 1, 2 means write param 1, etc */ @@ -2383,85 +2492,103 @@ int bdflush(void * unused) sprintf(current->comm, "kflushd"); bdflush_tsk = current; - for (;;) { - int nlist; + /* avoid getting signals */ + spin_lock_irq(¤t->sigmask_lock); + flush_signals(current); + sigfillset(¤t->blocked); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + for (;;) { CHECK_EMERGENCY_SYNC - for(nlist = BUF_LOCKED; nlist <= BUF_DIRTY; nlist++) { - int nr, major, written = 0; - struct buffer_head *next; - - repeat: - spin_lock(&lru_list_lock); - next = lru_list[nlist]; - nr = nr_buffers_type[nlist]; - while (nr-- > 0) { - struct buffer_head *bh = next; - - next = next->b_next_free; - - /* If the buffer is not on the correct list, - * then refile it. - */ - if ((nlist == BUF_DIRTY && - (!buffer_dirty(bh) && !buffer_locked(bh))) || - (nlist == BUF_LOCKED && !buffer_locked(bh))) { - __refile_buffer(bh); - continue; - } - - /* If we aren't in panic mode, don't write out too much - * at a time. Also, don't write out buffers we don't - * really have to write out yet.. - */ - if (!too_many_dirty_buffers) { - if (written > bdf_prm.b_un.ndirty) - break; - if (time_before(jiffies, bh->b_flushtime)) - continue; - } - - if (buffer_locked(bh) || !buffer_dirty(bh)) - continue; - - major = MAJOR(bh->b_dev); - written++; - - /* - * For the loop major we can try to do asynchronous writes, - * but we have to guarantee that we're making some progress.. - */ - atomic_inc(&bh->b_count); - spin_unlock(&lru_list_lock); - ll_rw_block(WRITE, 1, &bh); - atomic_dec(&bh->b_count); - goto repeat; - } - spin_unlock(&lru_list_lock); - } - run_task_queue(&tq_disk); + flush_dirty_buffers(0); + + /* If wakeup_bdflush will wakeup us + after our bdflush_done wakeup, then + we must make sure to not sleep + in schedule_timeout otherwise + wakeup_bdflush may wait for our + bdflush_done wakeup that would never arrive + (as we would be sleeping) and so it would + deadlock in SMP. */ + __set_current_state(TASK_INTERRUPTIBLE); wake_up(&bdflush_done); - /* * If there are still a lot of dirty buffers around, * skip the sleep and flush some more. Otherwise, we - * sleep for a while and mark us as not being in panic - * mode.. + * sleep for a while. */ - if (!too_many_dirty_buffers || nr_buffers_type[BUF_DIRTY] < bdf_prm.b_un.ndirty) { - too_many_dirty_buffers = 0; - spin_lock_irq(¤t->sigmask_lock); - flush_signals(current); - spin_unlock_irq(¤t->sigmask_lock); - interruptible_sleep_on_timeout(&bdflush_wait, 5*HZ); + if (balance_dirty_state(NODEV) < 0) + schedule_timeout(5*HZ); + /* Remember to mark us as running otherwise + the next schedule will block. */ + __set_current_state(TASK_RUNNING); + } +} + +/* + * This is the kernel update daemon. It was used to live in userspace + * but since it's need to run safely we want it unkillable by mistake. + * You don't need to change your userspace configuration since + * the userspace `update` will do_exit(0) at the first sys_bdflush(). + */ +int kupdate(void * unused) +{ + struct task_struct * tsk = current; + int interval; + + tsk->session = 1; + tsk->pgrp = 1; + strcpy(tsk->comm, "kupdate"); + + /* sigstop and sigcont will stop and wakeup kupdate */ + spin_lock_irq(&tsk->sigmask_lock); + sigfillset(&tsk->blocked); + siginitsetinv(¤t->blocked, sigmask(SIGCONT) | sigmask(SIGSTOP)); + recalc_sigpending(tsk); + spin_unlock_irq(&tsk->sigmask_lock); + + for (;;) { + /* update interval */ + interval = bdf_prm.b_un.interval; + if (interval) + { + tsk->state = TASK_INTERRUPTIBLE; + schedule_timeout(interval); + } + else + { + stop_kupdate: + tsk->state = TASK_STOPPED; + schedule(); /* wait for SIGCONT */ } + /* check for sigstop */ + if (signal_pending(tsk)) + { + int stopped = 0; + spin_lock_irq(&tsk->sigmask_lock); + if (sigismember(&tsk->signal, SIGSTOP)) + { + sigdelset(&tsk->signal, SIGSTOP); + stopped = 1; + } + recalc_sigpending(tsk); + spin_unlock_irq(&tsk->sigmask_lock); + if (stopped) + goto stop_kupdate; + } +#ifdef DEBUG + printk("kupdate() activated...\n"); +#endif + sync_old_buffers(); } } static int __init bdflush_init(void) { kernel_thread(bdflush, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); + kernel_thread(kupdate, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); return 0; } diff --git a/fs/dcache.c b/fs/dcache.c index ef45eba7d..b6f7a7203 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -169,6 +169,11 @@ out: int d_invalidate(struct dentry * dentry) { /* + * If it's already been dropped, return OK. + */ + if (list_empty(&dentry->d_hash)) + return 0; + /* * Check whether to do a partial shrink_dcache * to get rid of unused child entries. */ @@ -415,7 +420,7 @@ int shrink_dcache_memory(int priority, unsigned int gfp_mask) unlock_kernel(); /* FIXME: kmem_cache_shrink here should tell us the number of pages freed, and it should - work in a __GFP_DMA/__GFP_BIGMEM behaviour + work in a __GFP_DMA/__GFP_HIGHMEM behaviour to free only the interesting pages in function of the needs of the current allocation. */ kmem_cache_shrink(dentry_cache); @@ -31,6 +31,8 @@ #include <linux/fcntl.h> #include <linux/smp_lock.h> #include <linux/init.h> +#include <linux/pagemap.h> +#include <linux/highmem.h> #include <asm/uaccess.h> #include <asm/pgtable.h> @@ -212,20 +214,42 @@ int copy_strings(int argc,char ** argv, struct linux_binprm *bprm) /* XXX: add architecture specific overflow check here. */ pos = bprm->p; - while (len>0) { - char *pag; + while (len > 0) { + char *kaddr; + int i, new, err; + struct page *page; int offset, bytes_to_copy; offset = pos % PAGE_SIZE; - if (!(pag = (char *) bprm->page[pos/PAGE_SIZE]) && - !(pag = (char *) bprm->page[pos/PAGE_SIZE] = - (unsigned long *) get_free_page(GFP_USER))) - return -ENOMEM; + i = pos/PAGE_SIZE; + page = bprm->page[i]; + new = 0; + if (!page) { + /* + * Cannot yet use highmem page because + * we cannot sleep with a kmap held. + */ + page = __get_pages(GFP_USER, 0); + bprm->page[i] = page; + if (!page) + return -ENOMEM; + new = 1; + } + kaddr = (char *)kmap(page, KM_WRITE); + if (new && offset) + memset(kaddr, 0, offset); bytes_to_copy = PAGE_SIZE - offset; - if (bytes_to_copy > len) + if (bytes_to_copy > len) { bytes_to_copy = len; - if (copy_from_user(pag + offset, str, bytes_to_copy)) + if (new) + memset(kaddr+offset+len, 0, PAGE_SIZE-offset-len); + } + err = copy_from_user(kaddr + offset, str, bytes_to_copy); + flush_page_to_ram(page); + kunmap((unsigned long)kaddr, KM_WRITE); + + if (err) return -EFAULT; pos += bytes_to_copy; @@ -276,7 +300,9 @@ int setup_arg_pages(struct linux_binprm *bprm) mpnt->vm_offset = 0; mpnt->vm_file = NULL; mpnt->vm_private_data = (void *) 0; + vmlist_modify_lock(current->mm); insert_vm_struct(current->mm, mpnt); + vmlist_modify_unlock(current->mm); current->mm->total_vm = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT; } @@ -467,6 +493,11 @@ int flush_old_exec(struct linux_binprm * bprm) permission(bprm->dentry->d_inode,MAY_READ)) current->dumpable = 0; + /* An exec changes our domain. We are no longer part of the thread + group */ + + current->self_exec_id++; + flush_signal_handlers(current); flush_old_files(current->files); @@ -640,14 +671,22 @@ void remove_arg_zero(struct linux_binprm *bprm) { if (bprm->argc) { unsigned long offset; - char * page; + char * kaddr; + struct page *page; + offset = bprm->p % PAGE_SIZE; - page = (char*)bprm->page[bprm->p/PAGE_SIZE]; - while(bprm->p++,*(page+offset++)) - if(offset==PAGE_SIZE){ - offset=0; - page = (char*)bprm->page[bprm->p/PAGE_SIZE]; - } + goto inside; + + while (bprm->p++, *(kaddr+offset++)) { + if (offset != PAGE_SIZE) + continue; + offset = 0; + kunmap((unsigned long)kaddr, KM_WRITE); +inside: + page = bprm->page[bprm->p/PAGE_SIZE]; + kaddr = (char *)kmap(page, KM_WRITE); + } + kunmap((unsigned long)kaddr, KM_WRITE); bprm->argc--; } } @@ -676,8 +715,8 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs) bprm->dentry = NULL; bprm_loader.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *); - for (i=0 ; i<MAX_ARG_PAGES ; i++) /* clear page-table */ - bprm_loader.page[i] = 0; + for (i = 0 ; i < MAX_ARG_PAGES ; i++) /* clear page-table */ + bprm_loader.page[i] = NULL; dentry = open_namei(dynloader[0], 0, 0); retval = PTR_ERR(dentry); @@ -793,8 +832,9 @@ out: /* Assumes that free_page() can take a NULL argument. */ /* I hope this is ok for all architectures */ - for (i=0 ; i<MAX_ARG_PAGES ; i++) - free_page(bprm.page[i]); + for (i = 0 ; i < MAX_ARG_PAGES ; i++) + if (bprm.page[i]) + __free_page(bprm.page[i]); return retval; } @@ -16,7 +16,7 @@ /* - * Allocate an fd array, using get_free_page() if possible. + * Allocate an fd array, using __get_free_page() if possible. * Note: the array isn't cleared at allocation time. */ struct file ** alloc_fd_array(int num) @@ -129,7 +129,7 @@ out: } /* - * Allocate an fdset array, using get_free_page() if possible. + * Allocate an fdset array, using __get_free_page() if possible. * Note: the array isn't cleared at allocation time. */ fd_set * alloc_fdset(int num) diff --git a/fs/hpfs/anode.c b/fs/hpfs/anode.c index 62410ca26..6fb9c1633 100644 --- a/fs/hpfs/anode.c +++ b/fs/hpfs/anode.c @@ -293,10 +293,10 @@ void hpfs_remove_btree(struct super_block *s, struct bplus_header *btree) if (!level) return; if (s->s_hpfs_chk) if (hpfs_stop_cycles(s, ano, &c1, &c2, "hpfs_remove_btree #2")) return; + brelse(bh); hpfs_free_sectors(s, ano, 1); oano = ano; ano = anode->up; - brelse(bh); if (--level) { anode = hpfs_map_anode(s, ano, &bh); btree1 = &anode->btree; diff --git a/fs/hpfs/dir.c b/fs/hpfs/dir.c index 8af35847d..36e665c32 100644 --- a/fs/hpfs/dir.c +++ b/fs/hpfs/dir.c @@ -20,7 +20,34 @@ int hpfs_dir_release(struct inode *inode, struct file *filp) return 0; } -int hpfs_readdir(struct file *filp, void * dirent, filldir_t filldir) +/* This is slow, but it's not used often */ + +loff_t hpfs_dir_lseek(struct file *filp, loff_t off, int whence) +{ + loff_t new_off = off + (whence == 1 ? filp->f_pos : 0); + loff_t pos; + struct quad_buffer_head qbh; + struct inode *i = filp->f_dentry->d_inode; + struct super_block *s = filp->f_dentry->d_sb; + /*printk("dir lseek\n");*/ + if (new_off == 0 || new_off == 1 || new_off == 11 || new_off == 12 || new_off == 13) goto ok; + hpfs_lock_inode(i); + pos = ((loff_t) hpfs_de_as_down_as_possible(s, i->i_hpfs_dno) << 4) + 1; + while (pos != new_off) { + if (map_pos_dirent(i, &pos, &qbh)) hpfs_brelse4(&qbh); + else goto fail; + if (pos == 12) goto fail; + } + hpfs_unlock_inode(i); + ok: + return filp->f_pos = new_off; + fail: + hpfs_unlock_inode(i); + /*printk("illegal lseek: %016llx\n", new_off);*/ + return -ESPIPE; +} + +int hpfs_readdir(struct file *filp, void *dirent, filldir_t filldir) { struct inode *inode = filp->f_dentry->d_inode; struct quad_buffer_head qbh; @@ -54,11 +81,11 @@ int hpfs_readdir(struct file *filp, void * dirent, filldir_t filldir) if (e) return -EFSERROR; } lc = inode->i_sb->s_hpfs_lowercase; - if (filp->f_pos == -2) { /* diff -r requires this (note, that diff -r */ - filp->f_pos = -3; /* also fails on msdos filesystem in 2.0) */ + if (filp->f_pos == 12) { /* diff -r requires this (note, that diff -r */ + filp->f_pos = 13; /* also fails on msdos filesystem in 2.0) */ return 0; } - if (filp->f_pos == -3) return -ENOENT; + if (filp->f_pos == 13) return -ENOENT; hpfs_lock_inode(inode); @@ -72,7 +99,7 @@ int hpfs_readdir(struct file *filp, void * dirent, filldir_t filldir) hpfs_unlock_inode(inode); return -EFSERROR; } - if (filp->f_pos == -2) { + if (filp->f_pos == 12) { hpfs_unlock_inode(inode); return 0; } @@ -86,9 +113,9 @@ int hpfs_readdir(struct file *filp, void * dirent, filldir_t filldir) hpfs_unlock_inode(inode); return 0; } - filp->f_pos = -1; + filp->f_pos = 11; } - if (filp->f_pos == -1) { + if (filp->f_pos == 11) { if (filldir(dirent, "..", 2, filp->f_pos, inode->i_hpfs_parent_dir) < 0) { hpfs_unlock_inode(inode); return 0; diff --git a/fs/hpfs/dnode.c b/fs/hpfs/dnode.c index e4b4bbc91..d1ca8e3e6 100644 --- a/fs/hpfs/dnode.c +++ b/fs/hpfs/dnode.c @@ -539,7 +539,7 @@ static void delete_empty_dnode(struct inode *i, dnode_secno dno) brelse(bh); } i->i_hpfs_dno = down; - for_all_poss(i, hpfs_pos_subst, ((loff_t)dno << 4) | 1, (loff_t) -2); + for_all_poss(i, hpfs_pos_subst, ((loff_t)dno << 4) | 1, (loff_t) 12); return; } if (!(dnode = hpfs_map_dnode(i->i_sb, up, &qbh))) return; @@ -876,7 +876,7 @@ struct hpfs_dirent *map_pos_dirent(struct inode *inode, loff_t *posp, hpfs_brelse4(&qbh0); bail: - *posp = -2; + *posp = 12; return de; } diff --git a/fs/hpfs/file.c b/fs/hpfs/file.c index fbb1f2f6c..066ce5c28 100644 --- a/fs/hpfs/file.c +++ b/fs/hpfs/file.c @@ -58,91 +58,96 @@ void hpfs_truncate(struct inode *i) hpfs_write_inode(i); } -int hpfs_getblk_block(struct inode *inode, long block, int create, int *err, int *created) +int hpfs_get_block(struct inode *inode, long iblock, struct buffer_head *bh_result, int create) { - int add; - int sec = 0; - down(&inode->i_sem); - if (err) *err = 0; - if (created) *created = 0; - if (!inode->i_blocks) { - hpfs_error(inode->i_sb, "hpfs_get_block: inode %08x has no blocks", inode->i_ino); - if (err) *err = -EFSERROR; - up(&inode->i_sem); + secno s; + if (iblock < inode->i_blocks - 1) { + s = hpfs_bmap(inode, iblock); + bh_result->b_dev = inode->i_dev; + bh_result->b_blocknr = s; + bh_result->b_state |= (1UL << BH_Mapped); return 0; } - if (block < ((add = inode->i_blocks - 1))) { - int bm; - if (!(bm = hpfs_bmap(inode, block))) { - hpfs_error(inode->i_sb, "hpfs_get_block: cound not bmap block %08x, inode %08x, size %08x", (int)block, inode->i_ino, (int)inode->i_size); - *err = -EFSERROR; - } - up(&inode->i_sem); - return bm; - } - if (!create) { - if (err) *err = -EFBIG; - up(&inode->i_sem); - return 0; + if (!create) return 0; + if (iblock > inode->i_blocks - 1) { + //hpfs_error(inode->i_sb, "hpfs_get_block beyond file end (requested %08x, inode size %08x", (int)iblock, (int)inode->i_blocks - 1); + printk("HPFS: could not write beyond file end. This is known bug.\n"); + return -EFSERROR; } - if (created) *created = 1; - while (add <= block) { - if ((sec = hpfs_add_sector_to_btree(inode->i_sb, inode->i_ino, 1, add)) == -1) { - if (err) *err = -ENOSPC; - hpfs_truncate_btree(inode->i_sb, inode->i_ino, 1, inode->i_blocks - 1); - return 0; - } /* FIXME: clear block */ - add++; + if ((s = hpfs_add_sector_to_btree(inode->i_sb, inode->i_ino, 1, inode->i_blocks - 1)) == -1) { + hpfs_truncate_btree(inode->i_sb, inode->i_ino, 1, inode->i_blocks - 1); + return -ENOSPC; } - inode->i_blocks = add + 1; - up(&inode->i_sem); - return sec; + inode->i_blocks++; + bh_result->b_dev = inode->i_dev; + bh_result->b_blocknr = s; + bh_result->b_state |= (1UL << BH_Mapped) | (1UL << BH_New); + return 0; } -/* copied from ext2fs */ -static int hpfs_get_block(struct inode *inode, unsigned long block, struct buffer_head *bh, int update) +static int hpfs_write_partial_page(struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf) { - if (!bh->b_blocknr) { - int error, created; - unsigned long blocknr; - - blocknr = hpfs_getblk_block(inode, block, 1, &error, &created); - if (!blocknr) { - if (!error) - error = -ENOSPC; - return error; + struct dentry *dentry = file->f_dentry; + struct inode *inode = dentry->d_inode; + struct page *new_page, **hash; + unsigned long pgpos; + unsigned long page_cache = 0; + long status; + + printk("- off: %08x\n", (int)page->offset); + pgpos = (inode->i_blocks - 1) * 512 & PAGE_CACHE_MASK; + while (pgpos < page->offset) { +long pgp = pgpos; + printk("pgpos: %08x, bl: %d\n", (int)pgpos, (int)inode->i_blocks); + hash = page_hash(inode, pgpos); +repeat_find: new_page = __find_lock_page(inode, pgpos, hash); + if (!new_page) { + if (!page_cache) { + page_cache = page_cache_alloc(); + if (page_cache) + goto repeat_find; + status = -ENOMEM; + goto out; + } + new_page = page_cache_entry(page_cache); + if (add_to_page_cache_unique(new_page,inode,pgpos,hash)) + goto repeat_find; + page_cache = 0; } - - bh->b_dev = inode->i_dev; - bh->b_blocknr = blocknr; - - if (!update) - return 0; - - if (created) { - memset(bh->b_data, 0, bh->b_size); - set_bit(BH_Uptodate, &bh->b_state); - return 0; + printk("A\n"); + status = block_write_cont_page(file, new_page, PAGE_SIZE, 0, NULL); + printk("B\n"); + UnlockPage(new_page); + page_cache_release(new_page); + if (status < 0) + goto out; + pgpos = (inode->i_blocks - 1) * 512 & PAGE_CACHE_MASK; + printk("pgpos2: %08x, bl: %d\n", (int)pgpos, (int)inode->i_blocks); + if (pgpos == pgp) { + status = -1; + printk("ERROR\n"); + goto out; } } - - if (!update) - return 0; - - lock_kernel(); - ll_rw_block(READ, 1, &bh); - wait_on_buffer(bh); - unlock_kernel(); - - return buffer_uptodate(bh) ? 0 : -EIO; + //if ((status = block_write_cont_page(file, page, PAGE_SIZE, 0, NULL)) < 0) goto out; + printk("C\n"); + status = block_write_cont_page(file, page, offset, bytes, buf); + printk("D\n"); +out: + printk("O\n"); + if (page_cache) + page_cache_free(page_cache); + printk("E\n"); + return status; } + ssize_t hpfs_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) { ssize_t retval; retval = generic_file_write(file, buf, count, - ppos, block_write_partial_page); + ppos, /*hpfs_write_partial_page*/block_write_partial_page); if (retval > 0) { struct inode *inode = file->f_dentry->d_inode; inode->i_mtime = CURRENT_TIME; diff --git a/fs/hpfs/hpfs_fn.h b/fs/hpfs/hpfs_fn.h index 050b63597..9ae4a67da 100644 --- a/fs/hpfs/hpfs_fn.h +++ b/fs/hpfs/hpfs_fn.h @@ -226,6 +226,7 @@ void hpfs_set_dentry_operations(struct dentry *); int hpfs_dir_read(struct file *, char *, size_t, loff_t *); int hpfs_dir_release(struct inode *, struct file *); +loff_t hpfs_dir_lseek(struct file *, loff_t, int); int hpfs_readdir(struct file *, void *, filldir_t); struct dentry *hpfs_lookup(struct inode *, struct dentry *); @@ -258,9 +259,8 @@ int hpfs_open(struct inode *, struct file *); int hpfs_file_fsync(struct file *, struct dentry *); secno hpfs_bmap(struct inode *, unsigned); void hpfs_truncate(struct inode *); -ssize_t hpfs_file_read(struct file *, char *, size_t, loff_t *); -ssize_t hpfs_file_write(struct file *, const char *, size_t, loff_t *); -int hpfs_writepage (struct file *, struct page *); +int hpfs_get_block(struct inode *inode, long iblock, struct buffer_head *bh_result, int create); +ssize_t hpfs_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos); /* inode.c */ diff --git a/fs/hpfs/inode.c b/fs/hpfs/inode.c index efc776218..d79e55814 100644 --- a/fs/hpfs/inode.c +++ b/fs/hpfs/inode.c @@ -41,11 +41,9 @@ static const struct inode_operations hpfs_file_iops = NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - (int (*)(struct inode *, int)) -#warning Someone needs to code up hpfs_get_block properly... -DaveM - &hpfs_bmap, /* get_block */ + &hpfs_get_block, /* get_block */ block_read_full_page, /* readpage */ - hpfs_writepage, /* writepage */ + block_write_full_page, /* writepage */ block_flushpage, /* flushpage */ hpfs_truncate, /* truncate */ NULL, /* permission */ @@ -55,7 +53,7 @@ static const struct inode_operations hpfs_file_iops = static const struct file_operations hpfs_dir_ops = { - NULL, /* lseek - default */ + hpfs_dir_lseek, /* lseek */ hpfs_dir_read, /* read */ NULL, /* write - bad */ hpfs_readdir, /* readdir */ diff --git a/fs/inode.c b/fs/inode.c index 55eddfde8..f03295d5c 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -89,6 +89,7 @@ static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) memset(inode, 0, sizeof(*inode)); init_waitqueue_head(&inode->i_wait); INIT_LIST_HEAD(&inode->i_hash); + INIT_LIST_HEAD(&inode->i_pages); INIT_LIST_HEAD(&inode->i_dentry); sema_init(&inode->i_sem, 1); spin_lock_init(&inode->i_shared_lock); @@ -401,7 +402,7 @@ int shrink_icache_memory(int priority, int gfp_mask) prune_icache(count); /* FIXME: kmem_cache_shrink here should tell us the number of pages freed, and it should - work in a __GFP_DMA/__GFP_BIGMEM behaviour + work in a __GFP_DMA/__GFP_HIGHMEM behaviour to free only the interesting pages in function of the needs of the current allocation. */ kmem_cache_shrink(inode_cachep); @@ -429,7 +430,7 @@ static inline void __iget(struct inode * inode) * by hand after calling find_inode now! This simplify iunique and won't * add any additional branch in the common code. */ -static struct inode * find_inode(struct super_block * sb, unsigned long ino, struct list_head *head) +static struct inode * find_inode(struct super_block * sb, unsigned long ino, struct list_head *head, find_inode_t find_actor, void *opaque) { struct list_head *tmp; struct inode * inode; @@ -445,6 +446,8 @@ static struct inode * find_inode(struct super_block * sb, unsigned long ino, str continue; if (inode->i_ino != ino) continue; + if (find_actor && !find_actor(inode, ino, opaque)) + continue; break; } return inode; @@ -504,7 +507,7 @@ struct inode * get_empty_inode(void) * We no longer cache the sb_flags in i_flags - see fs.h * -- rmk@arm.uk.linux.org */ -static struct inode * get_new_inode(struct super_block *sb, unsigned long ino, struct list_head *head) +static struct inode * get_new_inode(struct super_block *sb, unsigned long ino, struct list_head *head, find_inode_t find_actor, void *opaque) { struct inode * inode; @@ -514,7 +517,7 @@ static struct inode * get_new_inode(struct super_block *sb, unsigned long ino, s spin_lock(&inode_lock); /* We released the lock, so.. */ - old = find_inode(sb, ino, head); + old = find_inode(sb, ino, head, find_actor, opaque); if (!old) { list_add(&inode->i_list, &inode_in_use); @@ -570,7 +573,7 @@ ino_t iunique(struct super_block *sb, ino_t max_reserved) retry: if (counter > max_reserved) { head = inode_hashtable + hash(sb,counter); - inode = find_inode(sb, res = counter++, head); + inode = find_inode(sb, res = counter++, head, NULL, NULL); if (!inode) { spin_unlock(&inode_lock); return res; @@ -595,13 +598,13 @@ struct inode *igrab(struct inode *inode) return inode; } -struct inode *iget(struct super_block *sb, unsigned long ino) +struct inode *iget4(struct super_block *sb, unsigned long ino, find_inode_t find_actor, void *opaque) { struct list_head * head = inode_hashtable + hash(sb,ino); struct inode * inode; spin_lock(&inode_lock); - inode = find_inode(sb, ino, head); + inode = find_inode(sb, ino, head, find_actor, opaque); if (inode) { __iget(inode); spin_unlock(&inode_lock); @@ -614,7 +617,7 @@ struct inode *iget(struct super_block *sb, unsigned long ino) * get_new_inode() will do the right thing, re-trying the search * in case it had to block at any point. */ - return get_new_inode(sb, ino, head); + return get_new_inode(sb, ino, head, find_actor, opaque); } void insert_inode_hash(struct inode *inode) diff --git a/fs/iobuf.c b/fs/iobuf.c index b46a13bfd..eaabf2f7c 100644 --- a/fs/iobuf.c +++ b/fs/iobuf.c @@ -50,7 +50,6 @@ int alloc_kiovec(int nr, struct kiobuf **bufp) init_waitqueue_head(&iobuf->wait_queue); iobuf->end_io = simple_wakeup_kiobuf; iobuf->array_len = KIO_STATIC_PAGES; - iobuf->pagelist = iobuf->page_array; iobuf->maplist = iobuf->map_array; *bufp++ = iobuf; } @@ -65,50 +64,35 @@ void free_kiovec(int nr, struct kiobuf **bufp) for (i = 0; i < nr; i++) { iobuf = bufp[i]; - if (iobuf->array_len > KIO_STATIC_PAGES) { - kfree (iobuf->pagelist); + if (iobuf->array_len > KIO_STATIC_PAGES) kfree (iobuf->maplist); - } kmem_cache_free(kiobuf_cachep, bufp[i]); } } int expand_kiobuf(struct kiobuf *iobuf, int wanted) { - unsigned long * pagelist; struct page ** maplist; if (iobuf->array_len >= wanted) return 0; - pagelist = (unsigned long *) - kmalloc(wanted * sizeof(unsigned long), GFP_KERNEL); - if (!pagelist) - return -ENOMEM; - maplist = (struct page **) kmalloc(wanted * sizeof(struct page **), GFP_KERNEL); - if (!maplist) { - kfree(pagelist); + if (!maplist) return -ENOMEM; - } /* Did it grow while we waited? */ if (iobuf->array_len >= wanted) { - kfree(pagelist); kfree(maplist); return 0; } - memcpy (pagelist, iobuf->pagelist, wanted * sizeof(unsigned long)); memcpy (maplist, iobuf->maplist, wanted * sizeof(struct page **)); - if (iobuf->array_len > KIO_STATIC_PAGES) { - kfree (iobuf->pagelist); + if (iobuf->array_len > KIO_STATIC_PAGES) kfree (iobuf->maplist); - } - iobuf->pagelist = pagelist; iobuf->maplist = maplist; iobuf->array_len = wanted; return 0; diff --git a/fs/minix/truncate.c b/fs/minix/truncate.c index f26aa086c..70b01dc20 100644 --- a/fs/minix/truncate.c +++ b/fs/minix/truncate.c @@ -33,7 +33,7 @@ */ #define DATA_BUFFER_USED(bh) \ - (atomic_read(&bh->b_count) || buffer_locked(bh)) + (atomic_read(&bh->b_count) > 1 || buffer_locked(bh)) /* * The functions for minix V1 fs truncation. diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 6b52b2d54..b7ec225ac 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -308,8 +308,7 @@ static struct page *try_to_get_dirent_page(struct file *file, __u32 cookie, int struct nfs_readdirres rd_res; struct dentry *dentry = file->f_dentry; struct inode *inode = dentry->d_inode; - struct page *page, **hash; - unsigned long page_cache; + struct page *page, **hash, *page_cache; long offset; __u32 *cookiep; @@ -341,14 +340,14 @@ repeat: goto unlock_out; } - page = page_cache_entry(page_cache); + page = page_cache; if (add_to_page_cache_unique(page, inode, offset, hash)) { page_cache_release(page); goto repeat; } rd_args.fh = NFS_FH(dentry); - rd_res.buffer = (char *)page_cache; + rd_res.buffer = (char *)page_address(page_cache); rd_res.bufsiz = PAGE_CACHE_SIZE; rd_res.cookie = *cookiep; do { @@ -533,13 +532,15 @@ static inline int nfs_dentry_force_reval(struct dentry *dentry, int flags) * If mtime is close to present time, we revalidate * more often. */ +#define NFS_REVALIDATE_NEGATIVE (1 * HZ) static inline int nfs_neg_need_reval(struct dentry *dentry) { - unsigned long timeout = 30 * HZ; - long diff = CURRENT_TIME - dentry->d_parent->d_inode->i_mtime; + struct inode *dir = dentry->d_parent->d_inode; + unsigned long timeout = NFS_ATTRTIMEO(dir); + long diff = CURRENT_TIME - dir->i_mtime; - if (diff < 5*60) - timeout = 1 * HZ; + if (diff < 5*60 && timeout > NFS_REVALIDATE_NEGATIVE) + timeout = NFS_REVALIDATE_NEGATIVE; return time_after(jiffies, dentry->d_time + timeout); } @@ -581,12 +582,14 @@ static int nfs_lookup_revalidate(struct dentry * dentry, int flags) goto out_bad; } - if (IS_ROOT(dentry)) - goto out_valid; - if (!nfs_dentry_force_reval(dentry, flags)) goto out_valid; + if (IS_ROOT(dentry)) { + __nfs_revalidate_inode(NFS_DSERVER(dentry), dentry); + goto out_valid_renew; + } + /* * Do a new lookup and check the dentry attributes. */ @@ -596,32 +599,29 @@ static int nfs_lookup_revalidate(struct dentry * dentry, int flags) goto out_bad; /* Inode number matches? */ - if (fattr.fileid != inode->i_ino) + if (NFS_FSID(inode) != fattr.fsid || + NFS_FILEID(inode) != fattr.fileid) goto out_bad; /* Filehandle matches? */ - if (memcmp(dentry->d_fsdata, &fhandle, sizeof(struct nfs_fh))) { - if (!list_empty(&dentry->d_subdirs)) - shrink_dcache_parent(dentry); - if (dentry->d_count < 2) - goto out_bad; - } + if (memcmp(dentry->d_fsdata, &fhandle, sizeof(struct nfs_fh))) + goto out_bad; /* Ok, remeber that we successfully checked it.. */ - nfs_renew_times(dentry); nfs_refresh_inode(inode, &fattr); + out_valid_renew: + nfs_renew_times(dentry); out_valid: return 1; out_bad: + d_drop(dentry); + if (!list_empty(&dentry->d_subdirs)) + shrink_dcache_parent(dentry); /* Purge readdir caches. */ if (dentry->d_parent->d_inode) { - invalidate_inode_pages(dentry->d_parent->d_inode); - nfs_flush_dircache(dentry->d_parent->d_inode); - } - if (inode && S_ISDIR(inode->i_mode)) { - invalidate_inode_pages(inode); - nfs_flush_dircache(inode); + nfs_zap_caches(dentry->d_parent->d_inode); + NFS_CACHEINV(dentry->d_parent->d_inode); } return 0; } @@ -649,21 +649,6 @@ static void nfs_dentry_delete(struct dentry *dentry) dentry->d_name.name, error); } -#ifdef NFS_PARANOIA - /* - * Sanity check: if the dentry has been unhashed and the - * inode still has users, we could have problems ... - */ - if (list_empty(&dentry->d_hash) && dentry->d_inode) { - struct inode *inode = dentry->d_inode; - int max_count = (S_ISDIR(inode->i_mode) ? 1 : inode->i_nlink); - if (inode->i_count > max_count) { -printk("nfs_dentry_delete: %s/%s: ino=%ld, count=%d, nlink=%d\n", -dentry->d_parent->d_name.name, dentry->d_name.name, -inode->i_ino, inode->i_count, inode->i_nlink); - } - } -#endif } static kmem_cache_t *nfs_fh_cachep; @@ -750,14 +735,6 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry) error = -EACCES; inode = nfs_fhget(dentry, &fhandle, &fattr); if (inode) { -#ifdef NFS_PARANOIA -if (inode->i_count > (S_ISDIR(inode->i_mode) ? 1 : inode->i_nlink)) { -printk("nfs_lookup: %s/%s ino=%ld in use, count=%d, nlink=%d\n", -dentry->d_parent->d_name.name, dentry->d_name.name, -inode->i_ino, inode->i_count, inode->i_nlink); -show_dentry(&inode->i_dentry); -} -#endif no_entry: d_add(dentry, inode); nfs_renew_times(dentry); @@ -779,14 +756,6 @@ static int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle, inode = nfs_fhget(dentry, fhandle, fattr); if (inode) { -#ifdef NFS_PARANOIA -if (inode->i_count > (S_ISDIR(inode->i_mode) ? 1 : inode->i_nlink)) { -printk("nfs_instantiate: %s/%s ino=%ld in use, count=%d, nlink=%d\n", -dentry->d_parent->d_name.name, dentry->d_name.name, -inode->i_ino, inode->i_count, inode->i_nlink); -show_dentry(&inode->i_dentry); -} -#endif d_instantiate(dentry, inode); nfs_renew_times(dentry); error = 0; @@ -803,16 +772,15 @@ show_dentry(&inode->i_dentry); static int nfs_create(struct inode *dir, struct dentry *dentry, int mode) { int error; - struct nfs_sattr sattr; + struct iattr attr; struct nfs_fattr fattr; struct nfs_fh fhandle; dfprintk(VFS, "NFS: create(%x/%ld, %s\n", dir->i_dev, dir->i_ino, dentry->d_name.name); - sattr.mode = mode; - sattr.uid = sattr.gid = sattr.size = (unsigned) -1; - sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1; + attr.ia_mode = mode; + attr.ia_valid = ATTR_MODE; /* * Invalidate the dir cache before the operation to avoid a race. @@ -820,7 +788,7 @@ static int nfs_create(struct inode *dir, struct dentry *dentry, int mode) invalidate_inode_pages(dir); nfs_flush_dircache(dir); error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dentry->d_parent), - dentry->d_name.name, &sattr, &fhandle, &fattr); + dentry->d_name.name, &attr, &fhandle, &fattr); if (!error) error = nfs_instantiate(dentry, &fhandle, &fattr); if (error) @@ -834,23 +802,25 @@ static int nfs_create(struct inode *dir, struct dentry *dentry, int mode) static int nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev) { int error; - struct nfs_sattr sattr; + struct iattr attr; struct nfs_fattr fattr; struct nfs_fh fhandle; dfprintk(VFS, "NFS: mknod(%x/%ld, %s\n", dir->i_dev, dir->i_ino, dentry->d_name.name); - sattr.mode = mode; - sattr.uid = sattr.gid = sattr.size = (unsigned) -1; - if (S_ISCHR(mode) || S_ISBLK(mode)) - sattr.size = rdev; /* get out your barf bag */ - sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1; + attr.ia_mode = mode; + attr.ia_valid = ATTR_MODE; + /* FIXME: move this to a special nfs_proc_mknod() */ + if (S_ISCHR(mode) || S_ISBLK(mode)) { + attr.ia_size = rdev; /* get out your barf bag */ + attr.ia_valid |= ATTR_SIZE; + } invalidate_inode_pages(dir); nfs_flush_dircache(dir); error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dentry->d_parent), - dentry->d_name.name, &sattr, &fhandle, &fattr); + dentry->d_name.name, &attr, &fhandle, &fattr); if (!error) error = nfs_instantiate(dentry, &fhandle, &fattr); if (error) @@ -864,16 +834,15 @@ static int nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rde static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) { int error; - struct nfs_sattr sattr; + struct iattr attr; struct nfs_fattr fattr; struct nfs_fh fhandle; dfprintk(VFS, "NFS: mkdir(%x/%ld, %s\n", dir->i_dev, dir->i_ino, dentry->d_name.name); - sattr.mode = mode | S_IFDIR; - sattr.uid = sattr.gid = sattr.size = (unsigned) -1; - sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1; + attr.ia_valid = ATTR_MODE; + attr.ia_mode = mode | S_IFDIR; /* * Always drop the dentry, we can't always depend on @@ -885,7 +854,7 @@ static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) invalidate_inode_pages(dir); nfs_flush_dircache(dir); error = nfs_proc_mkdir(NFS_DSERVER(dentry), NFS_FH(dentry->d_parent), - dentry->d_name.name, &sattr, &fhandle, &fattr); + dentry->d_name.name, &attr, &fhandle, &fattr); if (!error) dir->i_nlink++; return error; @@ -898,13 +867,6 @@ static int nfs_rmdir(struct inode *dir, struct dentry *dentry) dfprintk(VFS, "NFS: rmdir(%x/%ld, %s\n", dir->i_dev, dir->i_ino, dentry->d_name.name); -#ifdef NFS_PARANOIA -if (dentry->d_inode->i_count > 1) -printk("nfs_rmdir: %s/%s inode busy?? i_count=%d, i_nlink=%d\n", -dentry->d_parent->d_name.name, dentry->d_name.name, -dentry->d_inode->i_count, dentry->d_inode->i_nlink); -#endif - invalidate_inode_pages(dir); nfs_flush_dircache(dir); error = nfs_proc_rmdir(NFS_SERVER(dir), NFS_FH(dentry->d_parent), @@ -1082,12 +1044,6 @@ dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count); #endif goto out; } -#ifdef NFS_PARANOIA -if (inode && inode->i_count > inode->i_nlink) -printk("nfs_safe_remove: %s/%s inode busy?? i_count=%d, i_nlink=%d\n", -dentry->d_parent->d_name.name, dentry->d_name.name, -inode->i_count, inode->i_nlink); -#endif /* * Unhash the dentry while we remove the file ... */ @@ -1141,7 +1097,7 @@ static int nfs_unlink(struct inode *dir, struct dentry *dentry) static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { - struct nfs_sattr sattr; + struct iattr attr; int error; dfprintk(VFS, "NFS: symlink(%x/%ld, %s, %s)\n", @@ -1160,9 +1116,8 @@ dentry->d_parent->d_name.name, dentry->d_name.name); * Fill in the sattr for the call. * Note: SunOS 4.1.2 crashes if the mode isn't initialized! */ - sattr.mode = S_IFLNK | S_IRWXUGO; - sattr.uid = sattr.gid = sattr.size = (unsigned) -1; - sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1; + attr.ia_valid = ATTR_MODE; + attr.ia_mode = S_IFLNK | S_IRWXUGO; /* * Drop the dentry in advance to force a new lookup. @@ -1173,7 +1128,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name); invalidate_inode_pages(dir); nfs_flush_dircache(dir); error = nfs_proc_symlink(NFS_SERVER(dir), NFS_FH(dentry->d_parent), - dentry->d_name.name, symname, &sattr); + dentry->d_name.name, symname, &attr); if (!error) { nfs_renew_times(dentry->d_parent); } else if (error == -EEXIST) { @@ -1332,13 +1287,6 @@ do_rename: * To prevent any new references to the target during the rename, * we unhash the dentry and free the inode in advance. */ -#ifdef NFS_PARANOIA -if (new_inode && - new_inode->i_count > (S_ISDIR(new_inode->i_mode) ? 1 : new_inode->i_nlink)) -printk("nfs_rename: %s/%s inode busy?? i_count=%d, i_nlink=%d\n", -new_dentry->d_parent->d_name.name, new_dentry->d_name.name, -new_inode->i_count, new_inode->i_nlink); -#endif if (!list_empty(&new_dentry->d_hash)) { d_drop(new_dentry); rehash = update; diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 5421cebf9..ab1e51485 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -37,7 +37,7 @@ #define NFS_PARANOIA 1 static struct inode * __nfs_fhget(struct super_block *, struct nfs_fattr *); -static void nfs_zap_caches(struct inode *); +void nfs_zap_caches(struct inode *); static void nfs_invalidate_inode(struct inode *); static void nfs_read_inode(struct inode *); @@ -78,6 +78,8 @@ nfs_read_inode(struct inode * inode) inode->i_mode = 0; inode->i_rdev = 0; inode->i_op = NULL; + NFS_FILEID(inode) = 0; + NFS_FSID(inode) = 0; NFS_CACHEINV(inode); NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode); } @@ -415,13 +417,15 @@ restart: dprintk("nfs_free_dentries: found %s/%s, d_count=%d, hashed=%d\n", dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count, !list_empty(&dentry->d_hash)); + if (!list_empty(&dentry->d_subdirs)) + shrink_dcache_parent(dentry); if (!dentry->d_count) { dget(dentry); d_drop(dentry); dput(dentry); goto restart; } - if (!list_empty(&dentry->d_hash)) + if (list_empty(&dentry->d_hash)) unhashed++; } return unhashed; @@ -430,7 +434,7 @@ restart: /* * Invalidate the local caches */ -static void +void nfs_zap_caches(struct inode *inode) { NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode); @@ -466,6 +470,8 @@ nfs_fill_inode(struct inode *inode, struct nfs_fattr *fattr) * do this once. (We don't allow inodes to change types.) */ if (inode->i_mode == 0) { + NFS_FILEID(inode) = fattr->fileid; + NFS_FSID(inode) = fattr->fsid; inode->i_mode = fattr->mode; if (S_ISREG(inode->i_mode)) inode->i_op = &nfs_file_inode_operations; @@ -487,6 +493,54 @@ nfs_fill_inode(struct inode *inode, struct nfs_fattr *fattr) } /* + * In NFSv3 we can have 64bit inode numbers. In order to support + * this, and re-exported directories (also seen in NFSv2) + * we are forced to allow 2 different inodes to have the same + * i_ino. + */ +static int +nfs_find_actor(struct inode *inode, unsigned long ino, void *opaque) +{ + struct nfs_fattr *fattr = (struct nfs_fattr *)opaque; + if (NFS_FSID(inode) != fattr->fsid) + return 0; + if (NFS_FILEID(inode) != fattr->fileid) + return 0; + return 1; +} + +static int +nfs_inode_is_stale(struct inode *inode, struct nfs_fattr *fattr) +{ + int unhashed; + int is_stale = 0; + + if (inode->i_mode && + (fattr->mode & S_IFMT) != (inode->i_mode & S_IFMT)) + is_stale = 1; + + if (is_bad_inode(inode)) + is_stale = 1; + + /* + * If the inode seems stale, free up cached dentries. + */ + unhashed = nfs_free_dentries(inode); + + /* Assume we're holding an i_count + * + * NB: sockets sometimes have volatile file handles + * don't invalidate their inodes even if all dentries are + * unhashed. + */ + if (unhashed && inode->i_count == unhashed + 1 + && !S_ISSOCK(inode->i_mode) && !S_ISFIFO(inode->i_mode)) + is_stale = 1; + + return is_stale; +} + +/* * This is our own version of iget that looks up inodes by file handle * instead of inode number. We use this technique instead of using * the vfs read_inode function because there is no way to pass the @@ -545,54 +599,40 @@ nfs_fhget(struct dentry *dentry, struct nfs_fh *fhandle, static struct inode * __nfs_fhget(struct super_block *sb, struct nfs_fattr *fattr) { - struct inode *inode; - int max_count, stale_inode, unhashed = 0; + struct inode *inode = NULL; + unsigned long ino; -retry: - inode = iget(sb, fattr->fileid); - if (!inode) + if (!fattr->nlink) { + printk("NFS: Buggy server - nlink == 0!\n"); goto out_no_inode; - /* N.B. This should be impossible ... */ - if (inode->i_ino != fattr->fileid) - goto out_bad_id; + } - /* - * Check for busy inodes, and attempt to get rid of any - * unused local references. If successful, we release the - * inode and try again. - * - * Note that the busy test uses the values in the fattr, - * as the inode may have become a different object. - * (We can probably handle modes changes here, too.) - */ - stale_inode = inode->i_mode && - ((fattr->mode ^ inode->i_mode) & S_IFMT); - stale_inode |= inode->i_count && inode->i_count == unhashed; - max_count = S_ISDIR(fattr->mode) ? 1 : fattr->nlink; - if (stale_inode || inode->i_count > max_count + unhashed) { - dprintk("__nfs_fhget: inode %ld busy, i_count=%d, i_nlink=%d\n", - inode->i_ino, inode->i_count, inode->i_nlink); - unhashed = nfs_free_dentries(inode); - if (stale_inode || inode->i_count > max_count + unhashed) { - printk("__nfs_fhget: inode %ld still busy, i_count=%d\n", - inode->i_ino, inode->i_count); - if (!list_empty(&inode->i_dentry)) { - struct dentry *dentry; - dentry = list_entry(inode->i_dentry.next, - struct dentry, d_alias); - printk("__nfs_fhget: killing %s/%s filehandle\n", - dentry->d_parent->d_name.name, - dentry->d_name.name); - memset(dentry->d_fsdata, 0, - sizeof(struct nfs_fh)); - } - remove_inode_hash(inode); - nfs_invalidate_inode(inode); - unhashed = 0; - } + ino = fattr->fileid; + + while((inode = iget4(sb, ino, nfs_find_actor, fattr)) != NULL) { + + /* + * Check for busy inodes, and attempt to get rid of any + * unused local references. If successful, we release the + * inode and try again. + * + * Note that the busy test uses the values in the fattr, + * as the inode may have become a different object. + * (We can probably handle modes changes here, too.) + */ + if (!nfs_inode_is_stale(inode,fattr)) + break; + + dprintk("__nfs_fhget: inode %ld still busy, i_count=%d\n", + inode->i_ino, inode->i_count); + nfs_zap_caches(inode); + remove_inode_hash(inode); iput(inode); - goto retry; } + + if (!inode) + goto out_no_inode; + nfs_fill_inode(inode, fattr); dprintk("NFS: __nfs_fhget(%x/%ld ct=%d)\n", inode->i_dev, inode->i_ino, inode->i_count); @@ -603,18 +643,14 @@ out: out_no_inode: printk("__nfs_fhget: iget failed\n"); goto out; -out_bad_id: - printk("__nfs_fhget: unexpected inode from iget\n"); - goto out; } int nfs_notify_change(struct dentry *dentry, struct iattr *attr) { struct inode *inode = dentry->d_inode; - int error; - struct nfs_sattr sattr; struct nfs_fattr fattr; + int error; /* * Make sure the inode is up-to-date. @@ -627,54 +663,29 @@ printk("nfs_notify_change: revalidate failed, error=%d\n", error); goto out; } - sattr.mode = (u32) -1; - if (attr->ia_valid & ATTR_MODE) - sattr.mode = attr->ia_mode; - - sattr.uid = (u32) -1; - if (attr->ia_valid & ATTR_UID) - sattr.uid = attr->ia_uid; - - sattr.gid = (u32) -1; - if (attr->ia_valid & ATTR_GID) - sattr.gid = attr->ia_gid; - - sattr.size = (u32) -1; - if ((attr->ia_valid & ATTR_SIZE) && S_ISREG(inode->i_mode)) - sattr.size = attr->ia_size; - - sattr.mtime.seconds = sattr.mtime.useconds = (u32) -1; - if (attr->ia_valid & ATTR_MTIME) { - sattr.mtime.seconds = attr->ia_mtime; - sattr.mtime.useconds = 0; - } - - sattr.atime.seconds = sattr.atime.useconds = (u32) -1; - if (attr->ia_valid & ATTR_ATIME) { - sattr.atime.seconds = attr->ia_atime; - sattr.atime.useconds = 0; - } + if (!S_ISREG(inode->i_mode)) + attr->ia_valid &= ~ATTR_SIZE; error = nfs_wb_all(inode); if (error) goto out; error = nfs_proc_setattr(NFS_DSERVER(dentry), NFS_FH(dentry), - &sattr, &fattr); + &fattr, attr); if (error) goto out; /* * If we changed the size or mtime, update the inode * now to avoid invalidating the page cache. */ - if (sattr.size != (u32) -1) { - if (sattr.size != fattr.size) - printk("nfs_notify_change: sattr=%d, fattr=%d??\n", - sattr.size, fattr.size); - inode->i_size = sattr.size; + if (attr->ia_valid & ATTR_SIZE) { + if (attr->ia_size != fattr.size) + printk("nfs_notify_change: attr=%ld, fattr=%d??\n", + attr->ia_size, fattr.size); + inode->i_size = attr->ia_size; inode->i_mtime = fattr.mtime.seconds; } - if (sattr.mtime.seconds != (u32) -1) + if (attr->ia_valid & ATTR_MTIME) inode->i_mtime = fattr.mtime.seconds; error = nfs_refresh_inode(inode, &fattr); out: @@ -682,6 +693,34 @@ out: } /* + * Wait for the inode to get unlocked. + * (Used for NFS_INO_LOCKED and NFS_INO_REVALIDATING). + */ +int +nfs_wait_on_inode(struct inode *inode, int flag) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + int intr, error = 0; + + intr = NFS_SERVER(inode)->flags & NFS_MOUNT_INTR; + add_wait_queue(&inode->i_wait, &wait); + for (;;) { + set_task_state(tsk, (intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE)); + error = 0; + if (!(NFS_FLAGS(inode) & flag)) + break; + error = -ERESTARTSYS; + if (intr && signalled()) + break; + schedule(); + } + set_task_state(tsk, TASK_RUNNING); + remove_wait_queue(&inode->i_wait, &wait); + return error; +} + +/* * Externally visible revalidation function */ int @@ -711,7 +750,7 @@ int nfs_release(struct inode *inode, struct file *filp) * the cached attributes have to be refreshed. */ int -_nfs_revalidate_inode(struct nfs_server *server, struct dentry *dentry) +__nfs_revalidate_inode(struct nfs_server *server, struct dentry *dentry) { struct inode *inode = dentry->d_inode; int status = 0; @@ -720,6 +759,19 @@ _nfs_revalidate_inode(struct nfs_server *server, struct dentry *dentry) dfprintk(PAGECACHE, "NFS: revalidating %s/%s, ino=%ld\n", dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_ino); + + if (!inode || is_bad_inode(inode)) + return -ESTALE; + + while (NFS_REVALIDATING(inode)) { + status = nfs_wait_on_inode(inode, NFS_INO_REVALIDATING); + if (status < 0) + return status; + if (time_before(jiffies,NFS_READTIME(inode)+NFS_ATTRTIMEO(inode))) + return 0; + } + NFS_FLAGS(inode) |= NFS_INO_REVALIDATING; + status = nfs_proc_getattr(server, NFS_FH(dentry), &fattr); if (status) { int error; @@ -759,6 +811,8 @@ _nfs_revalidate_inode(struct nfs_server *server, struct dentry *dentry) dfprintk(PAGECACHE, "NFS: %s/%s revalidation complete\n", dentry->d_parent->d_name.name, dentry->d_name.name); out: + NFS_FLAGS(inode) &= ~NFS_INO_REVALIDATING; + wake_up(&inode->i_wait); return status; } diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c index 1bc7d3d37..a7e53e6db 100644 --- a/fs/nfs/nfs2xdr.c +++ b/fs/nfs/nfs2xdr.c @@ -118,19 +118,35 @@ xdr_decode_fattr(u32 *p, struct nfs_fattr *fattr) return p; } + +#define SATTR(p, attr, flag, field) \ + *p++ = (attr->ia_valid & flag) ? htonl(attr->field) : ~(u32) 0 static inline u32 * -xdr_encode_sattr(u32 *p, struct nfs_sattr *sattr) +xdr_encode_sattr(u32 *p, struct iattr *attr) { - *p++ = htonl(sattr->mode); - *p++ = htonl(sattr->uid); - *p++ = htonl(sattr->gid); - *p++ = htonl(sattr->size); - *p++ = htonl(sattr->atime.seconds); - *p++ = htonl(sattr->atime.useconds); - *p++ = htonl(sattr->mtime.seconds); - *p++ = htonl(sattr->mtime.useconds); - return p; + SATTR(p, attr, ATTR_MODE, ia_mode); + SATTR(p, attr, ATTR_UID, ia_uid); + SATTR(p, attr, ATTR_GID, ia_gid); + SATTR(p, attr, ATTR_SIZE, ia_size); + + if (attr->ia_valid & (ATTR_ATIME|ATTR_ATIME_SET)) { + *p++ = htonl(attr->ia_atime); + *p++ = 0; + } else { + *p++ = ~(u32) 0; + *p++ = ~(u32) 0; + } + + if (attr->ia_valid & (ATTR_MTIME|ATTR_MTIME_SET)) { + *p++ = htonl(attr->ia_mtime); + *p++ = 0; + } else { + *p++ = ~(u32) 0; + *p++ = ~(u32) 0; + } + return p; } +#undef SATTR /* * NFS encode functions diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 3b48b326a..bb55ce6d6 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -65,7 +65,7 @@ nfs_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, int nfs_proc_setattr(struct nfs_server *server, struct nfs_fh *fhandle, - struct nfs_sattr *sattr, struct nfs_fattr *fattr) + struct nfs_fattr *fattr, struct iattr *sattr) { struct nfs_sattrargs arg = { fhandle, sattr }; int status; @@ -123,7 +123,7 @@ nfs_proc_write(struct nfs_server *server, struct nfs_fh *fhandle, int swap, int nfs_proc_create(struct nfs_server *server, struct nfs_fh *dir, - const char *name, struct nfs_sattr *sattr, + const char *name, struct iattr *sattr, struct nfs_fh *fhandle, struct nfs_fattr *fattr) { struct nfs_createargs arg = { dir, name, sattr }; @@ -178,7 +178,7 @@ nfs_proc_link(struct nfs_server *server, struct nfs_fh *fhandle, int nfs_proc_symlink(struct nfs_server *server, struct nfs_fh *dir, const char *name, const char *path, - struct nfs_sattr *sattr) + struct iattr *sattr) { struct nfs_symlinkargs arg = { dir, name, path, sattr }; int status; @@ -191,7 +191,7 @@ nfs_proc_symlink(struct nfs_server *server, struct nfs_fh *dir, int nfs_proc_mkdir(struct nfs_server *server, struct nfs_fh *dir, - const char *name, struct nfs_sattr *sattr, + const char *name, struct iattr *sattr, struct nfs_fh *fhandle, struct nfs_fattr *fattr) { struct nfs_createargs arg = { dir, name, sattr }; diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c index 6cd892740..6b0d0f05b 100644 --- a/fs/nfs/symlink.c +++ b/fs/nfs/symlink.c @@ -59,8 +59,7 @@ struct inode_operations nfs_symlink_inode_operations = { static struct page *try_to_get_symlink_page(struct dentry *dentry, struct inode *inode) { struct nfs_readlinkargs rl_args; - struct page *page, **hash; - unsigned long page_cache; + struct page *page, **hash, *page_cache; page = NULL; page_cache = page_cache_alloc(); @@ -75,7 +74,7 @@ repeat: goto unlock_out; } - page = page_cache_entry(page_cache); + page = page_cache; if (add_to_page_cache_unique(page, inode, 0, hash)) { page_cache_release(page); goto repeat; @@ -86,7 +85,7 @@ repeat: * XDR response verification will NULL terminate it. */ rl_args.fh = NFS_FH(dentry); - rl_args.buffer = (const void *)page_cache; + rl_args.buffer = (const void *)page_address(page_cache); if (rpc_call(NFS_CLIENT(inode), NFSPROC_READLINK, &rl_args, NULL, 0) < 0) goto error; diff --git a/fs/proc/array.c b/fs/proc/array.c index d7f8ad9dd..249abd8cd 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -38,6 +38,7 @@ * * aeb@cwi.nl : /proc/partitions * + * * Alan Cox : security fixes. * <Alan.Cox@linux.org> * @@ -45,11 +46,6 @@ * * Gerhard Wichert : added BIGMEM support * Siemens AG <Gerhard.Wichert@pdb.siemens.de> - * - * Chuck Lever : safe handling of task_struct - * <cel@monkey.org> - * - * Andrea Arcangeli : SMP race/security fixes. */ #include <linux/types.h> @@ -71,7 +67,6 @@ #include <linux/slab.h> #include <linux/smp.h> #include <linux/signal.h> -#include <linux/smp_lock.h> #include <asm/uaccess.h> #include <asm/pgtable.h> @@ -365,16 +360,24 @@ static int get_meminfo(char * buffer) struct sysinfo i; int len; +/* + * display in kilobytes. + */ +#define K(x) ((x) << (PAGE_SHIFT - 10)) + si_meminfo(&i); si_swapinfo(&i); len = sprintf(buffer, " total: used: free: shared: buffers: cached:\n" - "Mem: %8lu %8lu %8lu %8lu %8lu %8lu\n" + "Mem: %8lu %8lu %8lu %8lu %8lu %8u\n" "Swap: %8lu %8lu %8lu\n", - i.totalram, i.totalram-i.freeram, i.freeram, i.sharedram, i.bufferram, (unsigned long) atomic_read(&page_cache_size)*PAGE_SIZE, - i.totalswap, i.totalswap-i.freeswap, i.freeswap); + K(i.totalram), K(i.totalram-i.freeram), K(i.freeram), + K(i.sharedram), K(i.bufferram), + K(atomic_read(&page_cache_size)), K(i.totalswap), + K(i.totalswap-i.freeswap), K(i.freeswap)); /* - * Tagged format, for easy grepping and expansion. The above will go away - * eventually, once the tools have been updated. + * Tagged format, for easy grepping and expansion. + * The above will go away eventually, once the tools + * have been updated. */ return len + sprintf(buffer+len, "MemTotal: %8lu kB\n" @@ -382,19 +385,20 @@ static int get_meminfo(char * buffer) "MemShared: %8lu kB\n" "Buffers: %8lu kB\n" "Cached: %8u kB\n" - "BigTotal: %8lu kB\n" - "BigFree: %8lu kB\n" + "HighTotal: %8lu kB\n" + "HighFree: %8lu kB\n" "SwapTotal: %8lu kB\n" "SwapFree: %8lu kB\n", - i.totalram >> 10, - i.freeram >> 10, - i.sharedram >> 10, - i.bufferram >> 10, - atomic_read(&page_cache_size) << (PAGE_SHIFT - 10), - i.totalbig >> 10, - i.freebig >> 10, - i.totalswap >> 10, - i.freeswap >> 10); + K(i.totalram), + K(i.freeram), + K(i.sharedram), + K(i.bufferram), + K(atomic_read(&page_cache_size)), + K(i.totalhigh), + K(i.freehigh), + K(i.totalswap), + K(i.freeswap)); +#undef K } static int get_version(char * buffer) @@ -412,69 +416,68 @@ static int get_cmdline(char * buffer) return sprintf(buffer, "%s\n", saved_command_line); } -static unsigned long get_phys_addr(struct mm_struct * mm, unsigned long ptr) +static struct page * get_phys_addr(struct mm_struct * mm, unsigned long ptr) { - pgd_t *page_dir; - pmd_t *page_middle; + pgd_t *pgd; + pmd_t *pmd; pte_t pte; if (ptr >= TASK_SIZE) return 0; - page_dir = pgd_offset(mm,ptr); - if (pgd_none(*page_dir)) + pgd = pgd_offset(mm,ptr); + if (pgd_none(*pgd)) return 0; - if (pgd_bad(*page_dir)) { - printk("bad page directory entry %08lx\n", pgd_val(*page_dir)); - pgd_clear(page_dir); + if (pgd_bad(*pgd)) { + pgd_ERROR(*pgd); + pgd_clear(pgd); return 0; } - page_middle = pmd_offset(page_dir,ptr); - if (pmd_none(*page_middle)) + pmd = pmd_offset(pgd,ptr); + if (pmd_none(*pmd)) return 0; - if (pmd_bad(*page_middle)) { - printk("bad page middle entry %08lx\n", pmd_val(*page_middle)); - pmd_clear(page_middle); + if (pmd_bad(*pmd)) { + pmd_ERROR(*pmd); + pmd_clear(pmd); return 0; } - pte = *pte_offset(page_middle,ptr); + pte = *pte_offset(pmd,ptr); if (!pte_present(pte)) return 0; - return pte_page(pte) + (ptr & ~PAGE_MASK); + return pte_page(pte); } -#include <linux/bigmem.h> - static int get_array(struct mm_struct *mm, unsigned long start, unsigned long end, char * buffer) { - unsigned long addr; + struct page *page; + unsigned long kaddr; int size = 0, result = 0; char c; if (start >= end) return result; for (;;) { - addr = get_phys_addr(mm, start); - if (!addr) + page = get_phys_addr(mm, start); + if (!page) return result; - addr = kmap(addr, KM_READ); + kaddr = kmap(page, KM_READ) + (start & ~PAGE_MASK); do { - c = *(char *) addr; + c = *(char *) kaddr; if (!c) result = size; if (size < PAGE_SIZE) buffer[size++] = c; else { - kunmap(addr, KM_READ); + kunmap(kaddr, KM_READ); return result; } - addr++; + kaddr++; start++; if (!c && start >= end) { - kunmap(addr, KM_READ); + kunmap(kaddr, KM_READ); return result; } - } while (addr & ~PAGE_MASK); - kunmap(addr-1, KM_READ); + } while (kaddr & ~PAGE_MASK); + kunmap(kaddr, KM_READ); } return result; } @@ -483,9 +486,7 @@ static struct mm_struct *get_mm(int pid) { struct task_struct *p; struct mm_struct *mm = NULL; - - /* need kernel lock to avoid the tsk->mm to go away under us */ - lock_kernel(); + read_lock(&tasklist_lock); p = find_task_by_pid(pid); if (p) @@ -493,10 +494,10 @@ static struct mm_struct *get_mm(int pid) if (mm) atomic_inc(&mm->mm_users); read_unlock(&tasklist_lock); - unlock_kernel(); return mm; } + static int get_env(int pid, char * buffer) { struct mm_struct *mm = get_mm(pid); @@ -859,9 +860,6 @@ static inline char * task_mem(struct mm_struct *mm, char *buffer) return buffer; } -/* - * These next two assume that the task's sigmask_lock is held by the caller. - */ static void collect_sigign_sigcatch(struct task_struct *p, sigset_t *ign, sigset_t *catch) { @@ -914,115 +912,77 @@ extern inline char *task_cap(struct task_struct *p, char *buffer) cap_t(p->cap_effective)); } -/* - * This is somewhat safer than it was before. However... - * - * Embedded pointers in the task structure may reference data that - * can be changed or that is no longer valid after the tasklist - * lock is released, or that isn't even protected by the tasklist - * lock. Eg. tsk->tty, tsk->sig, and tsk->p_pptr can change after - * we make our own copy of the task structure. This doesn't matter - * unless we are trying to use the pointed-to data as an address. - * So there are still a few safety issues to be addressed here. - */ + static int get_status(int pid, char * buffer) { char * orig = buffer; struct task_struct *tsk; struct mm_struct *mm = NULL; - /* - * We lock the whole kernel here because p->files and p->mm are still - * protected by the global kernel lock. - */ - lock_kernel(); - read_lock(&tasklist_lock); tsk = find_task_by_pid(pid); - if (tsk) { + if (tsk) mm = tsk->mm; - if (mm) - atomic_inc(&mm->mm_users); - - buffer = task_name(tsk, buffer); - buffer = task_state(tsk, buffer); - - spin_lock_irq(&tsk->sigmask_lock); - buffer = task_sig(tsk, buffer); - spin_unlock_irq(&tsk->sigmask_lock); - - buffer = task_cap(tsk, buffer); - } - read_unlock(&tasklist_lock); - - unlock_kernel(); - - /* - * We can't hold the tasklist_lock and jiggle the mmap_sem -- - * that can result in a deadlock. - */ - if (mm) { + if (mm) + atomic_inc(&mm->mm_users); + read_unlock(&tasklist_lock); /* FIXME!! This should be done after the last use */ + if (!tsk) + return 0; + buffer = task_name(tsk, buffer); + buffer = task_state(tsk, buffer); + if (mm) buffer = task_mem(mm, buffer); + buffer = task_sig(tsk, buffer); + buffer = task_cap(tsk, buffer); + if (mm) mmput(mm); - } - - /* - * (buffer - orig) will be zero on an error exit. - */ return buffer - orig; } static int get_stat(int pid, char * buffer) { struct task_struct *tsk; - struct mm_struct *mm; + struct mm_struct *mm = NULL; unsigned long vsize, eip, esp, wchan; long priority, nice; - pid_t ppid = 0; + int tty_pgrp; sigset_t sigign, sigcatch; char state; - int res = 0; - unsigned int tty_device; - int tty_pgrp; + int res; read_lock(&tasklist_lock); tsk = find_task_by_pid(pid); - if (!tsk) - goto out_unlock; - /* avoid the task list to go away under us (security) */ - get_page(MAP_NR(tsk) + mem_map); - ppid = tsk->p_pptr->pid; - read_unlock(&tasklist_lock); - - /* we need the big kernel lock to avoid tsk->mm and tsk->tty - to change under us */ - lock_kernel(); - mm = tsk->mm; + if (tsk) + mm = tsk->mm; if (mm) atomic_inc(&mm->mm_users); - tty_device = tsk->tty ? kdev_t_to_nr(tsk->tty->device) : 0; - tty_pgrp = tsk->tty ? tsk->tty->pgrp : -1; - unlock_kernel(); - - spin_lock_irq(&tsk->sigmask_lock); - collect_sigign_sigcatch(tsk, &sigign, &sigcatch); - spin_unlock_irq(&tsk->sigmask_lock); - - eip = KSTK_EIP(tsk); - esp = KSTK_ESP(tsk); - wchan = get_wchan(tsk); - + read_unlock(&tasklist_lock); /* FIXME!! This should be done after the last use */ + if (!tsk) + return 0; state = *get_task_state(tsk); vsize = eip = esp = 0; - if (mm) - { + if (mm) { struct vm_area_struct *vma; down(&mm->mmap_sem); - for (vma = mm->mmap; vma; vma = vma->vm_next) + vma = mm->mmap; + while (vma) { vsize += vma->vm_end - vma->vm_start; + vma = vma->vm_next; + } + eip = KSTK_EIP(tsk); + esp = KSTK_ESP(tsk); up(&mm->mmap_sem); } + wchan = get_wchan(tsk); + + collect_sigign_sigcatch(tsk, &sigign, &sigcatch); + + if (tsk->tty) + tty_pgrp = tsk->tty->pgrp; + else + tty_pgrp = -1; + /* scale priority and nice values from timeslices to -20..20 */ /* to make it look like a "normal" Unix priority/nice value */ priority = tsk->counter; @@ -1036,10 +996,10 @@ static int get_stat(int pid, char * buffer) pid, tsk->comm, state, - ppid, + tsk->p_pptr->pid, tsk->pgrp, tsk->session, - tty_device, + tsk->tty ? kdev_t_to_nr(tsk->tty->device) : 0, tty_pgrp, tsk->flags, tsk->min_flt, @@ -1076,16 +1036,9 @@ static int get_stat(int pid, char * buffer) tsk->cnswap, tsk->exit_signal, tsk->processor); - if (mm) mmput(mm); - free_task_struct(tsk); return res; - -out_unlock: - read_unlock(&tasklist_lock); - unlock_kernel(); - return 0; } static inline void statm_pte_range(pmd_t * pmd, unsigned long address, unsigned long size, @@ -1097,7 +1050,7 @@ static inline void statm_pte_range(pmd_t * pmd, unsigned long address, unsigned if (pmd_none(*pmd)) return; if (pmd_bad(*pmd)) { - printk("statm_pte_range: bad pmd (%08lx)\n", pmd_val(*pmd)); + pmd_ERROR(*pmd); pmd_clear(pmd); return; } @@ -1135,7 +1088,7 @@ static inline void statm_pmd_range(pgd_t * pgd, unsigned long address, unsigned if (pgd_none(*pgd)) return; if (pgd_bad(*pgd)) { - printk("statm_pmd_range: bad pgd (%08lx)\n", pgd_val(*pgd)); + pgd_ERROR(*pgd); pgd_clear(pgd); return; } @@ -1233,11 +1186,11 @@ static ssize_t read_maps (int pid, struct file * file, char * buf, size_t count, loff_t *ppos) { struct task_struct *p; - struct mm_struct *mm = NULL; struct vm_area_struct * map, * next; char * destptr = buf, * buffer; loff_t lineno; ssize_t column, i; + int volatile_task; long retval; /* @@ -1249,30 +1202,24 @@ static ssize_t read_maps (int pid, struct file * file, char * buf, goto out; retval = -EINVAL; - lock_kernel(); read_lock(&tasklist_lock); p = find_task_by_pid(pid); - if (p) { - mm = p->mm; - if (mm) - atomic_inc(&mm->mm_users); - } - read_unlock(&tasklist_lock); - unlock_kernel(); + read_unlock(&tasklist_lock); /* FIXME!! This should be done after the last use */ if (!p) goto freepage_out; - /* nothing to map */ - if (!mm || count == 0) + if (!p->mm || count == 0) goto getlen_out; + /* Check whether the mmaps could change if we sleep */ + volatile_task = (p != current || atomic_read(&p->mm->mm_users) > 1); + /* decode f_pos */ lineno = *ppos >> MAPS_LINE_SHIFT; column = *ppos & (MAPS_LINE_LENGTH-1); - down(&mm->mmap_sem); - /* quickly go to line "lineno" */ - for (map = mm->mmap, i = 0; map && (i < lineno); map = map->vm_next, i++) + /* quickly go to line lineno */ + for (map = p->mm->mmap, i = 0; map && (i < lineno); map = map->vm_next, i++) continue; for ( ; map ; map = next ) { @@ -1343,13 +1290,17 @@ static ssize_t read_maps (int pid, struct file * file, char * buf, /* done? */ if (count == 0) break; + + /* By writing to user space, we might have slept. + * Stop the loop, to avoid a race condition. + */ + if (volatile_task) + break; } - up(&mm->mmap_sem); /* encode f_pos */ *ppos = (lineno << MAPS_LINE_SHIFT) + column; - mmput(mm); getlen_out: retval = destptr - buf; @@ -1362,31 +1313,28 @@ out: #ifdef __SMP__ static int get_pidcpu(int pid, char * buffer) { - struct task_struct * tsk; + struct task_struct * tsk = current ; int i, len = 0; - /* - * Hold the tasklist_lock to guarantee that the task_struct - * address will remain valid while we examine its contents. - */ read_lock(&tasklist_lock); - tsk = find_task_by_pid(pid); - if (tsk) - get_page(MAP_NR(tsk) + mem_map); - read_unlock(&tasklist_lock); - if (tsk) { - len = sprintf(buffer, - "cpu %lu %lu\n", - HZ_TO_STD(tsk->times.tms_utime), - HZ_TO_STD(tsk->times.tms_stime)); - - for (i = 0 ; i < smp_num_cpus; i++) - len += sprintf(buffer + len, "cpu%d %lu %lu\n", - i, - HZ_TO_STD(tsk->per_cpu_utime[cpu_logical_map(i)]), - HZ_TO_STD(tsk->per_cpu_stime[cpu_logical_map(i)])); - free_task_struct(tsk); - } + if (pid != tsk->pid) + tsk = find_task_by_pid(pid); + read_unlock(&tasklist_lock); /* FIXME!! This should be done after the last use */ + + if (tsk == NULL) + return 0; + + len = sprintf(buffer, + "cpu %lu %lu\n", + HZ_TO_STD(tsk->times.tms_utime), + HZ_TO_STD(tsk->times.tms_stime)); + + for (i = 0 ; i < smp_num_cpus; i++) + len += sprintf(buffer + len, "cpu%d %lu %lu\n", + i, + HZ_TO_STD(tsk->per_cpu_utime[cpu_logical_map(i)]), + HZ_TO_STD(tsk->per_cpu_stime[cpu_logical_map(i)])); + return len; } #endif @@ -1519,6 +1467,12 @@ static int process_unauthorized(int type, int pid) int ok = 0; read_lock(&tasklist_lock); + + /* + * Grab the lock, find the task, save the uid and + * check it has an mm still (ie its not dead) + */ + p = find_task_by_pid(pid); if (p) { euid=p->euid; @@ -1526,7 +1480,9 @@ static int process_unauthorized(int type, int pid) if(!cap_issubset(p->cap_permitted, current->cap_permitted)) ok=0; } + read_unlock(&tasklist_lock); + if (!p) return 1; diff --git a/fs/proc/mem.c b/fs/proc/mem.c index f9fcb0970..90cd79722 100644 --- a/fs/proc/mem.c +++ b/fs/proc/mem.c @@ -10,7 +10,7 @@ #include <linux/kernel.h> #include <linux/mm.h> #include <linux/proc_fs.h> -#include <linux/bigmem.h> +#include <linux/highmem.h> #include <asm/page.h> #include <asm/uaccess.h> @@ -79,9 +79,10 @@ static ssize_t mem_read(struct file * file, char * buf, pgd_t *page_dir; pmd_t *page_middle; pte_t pte; - char * page; + struct page * page; struct task_struct * tsk; unsigned long addr; + unsigned long maddr; /* temporary mapped address */ char *tmp; ssize_t scount, i; @@ -102,7 +103,7 @@ static ssize_t mem_read(struct file * file, char * buf, if (pgd_none(*page_dir)) break; if (pgd_bad(*page_dir)) { - printk("Bad page dir entry %08lx\n", pgd_val(*page_dir)); + pgd_ERROR(*page_dir); pgd_clear(page_dir); break; } @@ -110,20 +111,20 @@ static ssize_t mem_read(struct file * file, char * buf, if (pmd_none(*page_middle)) break; if (pmd_bad(*page_middle)) { - printk("Bad page middle entry %08lx\n", pmd_val(*page_middle)); + pmd_ERROR(*page_middle); pmd_clear(page_middle); break; } pte = *pte_offset(page_middle,addr); if (!pte_present(pte)) break; - page = (char *) pte_page(pte) + (addr & ~PAGE_MASK); + page = pte_page(pte); i = PAGE_SIZE-(addr & ~PAGE_MASK); if (i > scount) i = scount; - page = (char *) kmap((unsigned long) page, KM_READ); - copy_to_user(tmp, page, i); - kunmap((unsigned long) page, KM_READ); + maddr = kmap(page, KM_READ); + copy_to_user(tmp, (char *)maddr + (addr & ~PAGE_MASK), i); + kunmap(maddr, KM_READ); addr += i; tmp += i; scount -= i; @@ -141,9 +142,10 @@ static ssize_t mem_write(struct file * file, char * buf, pgd_t *page_dir; pmd_t *page_middle; pte_t pte; - char * page; + struct page * page; struct task_struct * tsk; unsigned long addr; + unsigned long maddr; /* temporary mapped address */ char *tmp; long i; @@ -159,7 +161,7 @@ static ssize_t mem_write(struct file * file, char * buf, if (pgd_none(*page_dir)) break; if (pgd_bad(*page_dir)) { - printk("Bad page dir entry %08lx\n", pgd_val(*page_dir)); + pgd_ERROR(*page_dir); pgd_clear(page_dir); break; } @@ -167,7 +169,7 @@ static ssize_t mem_write(struct file * file, char * buf, if (pmd_none(*page_middle)) break; if (pmd_bad(*page_middle)) { - printk("Bad page middle entry %08lx\n", pmd_val(*page_middle)); + pmd_ERROR(*page_middle); pmd_clear(page_middle); break; } @@ -176,13 +178,13 @@ static ssize_t mem_write(struct file * file, char * buf, break; if (!pte_write(pte)) break; - page = (char *) pte_page(pte) + (addr & ~PAGE_MASK); + page = pte_page(pte); i = PAGE_SIZE-(addr & ~PAGE_MASK); if (i > count) i = count; - page = (unsigned long) kmap((unsigned long) page, KM_WRITE); - copy_from_user(page, tmp, i); - kunmap((unsigned long) page, KM_WRITE); + maddr = kmap(page, KM_WRITE); + copy_from_user((char *)maddr + (addr & ~PAGE_MASK), tmp, i); + kunmap(maddr, KM_WRITE); addr += i; tmp += i; count -= i; @@ -248,14 +250,14 @@ int mem_mmap(struct file * file, struct vm_area_struct * vma) if (pgd_none(*src_dir)) return -EINVAL; if (pgd_bad(*src_dir)) { - printk("Bad source page dir entry %08lx\n", pgd_val(*src_dir)); + pgd_ERROR(*src_dir); return -EINVAL; } src_middle = pmd_offset(src_dir, stmp); if (pmd_none(*src_middle)) return -EINVAL; if (pmd_bad(*src_middle)) { - printk("Bad source page middle entry %08lx\n", pmd_val(*src_middle)); + pmd_ERROR(*src_middle); return -EINVAL; } src_table = pte_offset(src_middle, stmp); @@ -301,9 +303,9 @@ int mem_mmap(struct file * file, struct vm_area_struct * vma) set_pte(src_table, pte_mkdirty(*src_table)); set_pte(dest_table, *src_table); - mapnr = MAP_NR(pte_page(*src_table)); + mapnr = pte_pagenr(*src_table); if (mapnr < max_mapnr) - get_page(mem_map + MAP_NR(pte_page(*src_table))); + get_page(mem_map + pte_pagenr(*src_table)); stmp += PAGE_SIZE; dtmp += PAGE_SIZE; diff --git a/fs/super.c b/fs/super.c index 693017eee..3b58d13cc 100644 --- a/fs/super.c +++ b/fs/super.c @@ -135,7 +135,7 @@ out: return lptr; } -static void remove_vfsmnt(kdev_t dev) +void remove_vfsmnt(kdev_t dev) { struct vfsmount *lptr, *tofree; @@ -508,7 +508,7 @@ out: /* * Find a super_block with no device assigned. */ -static struct super_block *get_empty_super(void) +struct super_block *get_empty_super(void) { struct super_block *s; diff --git a/include/asm-arm/a.out.h b/include/asm-arm/a.out.h index ae967b0a6..f78a7b295 100644 --- a/include/asm-arm/a.out.h +++ b/include/asm-arm/a.out.h @@ -27,7 +27,8 @@ struct exec #define M_ARM 103 #ifdef __KERNEL__ -#include <asm/arch/a.out.h> +#define STACK_TOP ((current->personality == PER_LINUX_32BIT) ? \ + TASK_SIZE : TASK_SIZE_26) #endif #ifndef LIBRARY_START_TEXT diff --git a/include/asm-arm/arcaudio.h b/include/asm-arm/arcaudio.h deleted file mode 100644 index 43a31bc79..000000000 --- a/include/asm-arm/arcaudio.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * arcaudio.h - * - */ - -#ifndef _LINUX_ARCAUDIO_H -#define _LINUX_ARCAUDIO_H - -#define ARCAUDIO_MAXCHANNELS 8 - -enum ch_type -{ - ARCAUDIO_NONE, /* No sound (muted) */ - ARCAUDIO_8BITSIGNED, /* signed 8 bits per samples */ - ARCAUDIO_8BITUNSIGNED, /* unsigned 8 bits per samples */ - ARCAUDIO_16BITSIGNED, /* signed 16 bits per samples (little endian) */ - ARCAUDIO_16BITUNSIGNED, /* unsigned 16 bits per samples (little endian) */ - ARCAUDIO_LOG /* Vidc Log */ -}; - -/* - * Global information - */ -struct arcaudio -{ - int sample_rate; /* sample rate (Hz) */ - int num_channels; /* number of channels */ - int volume; /* overall system volume */ -}; - -/* - * Per channel information - */ -struct arcaudio_channel -{ - int stereo_position; /* Channel position */ - int channel_volume; /* Channel volume */ - enum ch_type channel_type; /* Type of channel */ - int buffer_size; /* Size of channel buffer */ -}; - -/* IOCTLS */ -#define ARCAUDIO_GETINFO 0x6101 -#define ARCAUDIO_SETINFO 0x6102 -#define ARCAUDIO_GETCHANNELINFO 0x6111 -#define ARCAUDIO_SETCHANNELINFO 0x6112 -#define ARCAUDIO_GETOPTS 0x61f0 -#define ARCAUDIO_SETOPTS 0x61f1 -#define ARCAUDIO_OPTSPKR 1<<0 - -#endif diff --git a/include/asm-arm/arch-arc/a.out.h b/include/asm-arm/arch-arc/a.out.h deleted file mode 100644 index 0c13102d0..000000000 --- a/include/asm-arm/arch-arc/a.out.h +++ /dev/null @@ -1,14 +0,0 @@ -/* - * linux/include/asm-arm/arch-arc/a.out.h - * - * Copyright (C) 1996-1999 Russell King - */ -#ifndef __ASM_ARCH_A_OUT_H -#define __ASM_ARCH_A_OUT_H - -#include <asm/arch/memory.h> - -#define STACK_TOP TASK_SIZE - -#endif - diff --git a/include/asm-arm/arch-arc/hardware.h b/include/asm-arm/arch-arc/hardware.h index 33b4659f8..5a47717ab 100644 --- a/include/asm-arm/arch-arc/hardware.h +++ b/include/asm-arm/arch-arc/hardware.h @@ -49,7 +49,7 @@ #define SCREEN1_BASE 0x01f88000 -#ifndef __ASSEMBLER__ +#ifndef __ASSEMBLY__ /* * for use with inb/outb @@ -101,5 +101,17 @@ #define PCIO_BASE 0x03010000 #endif + +#ifdef HAS_EXPMASK +#ifndef __ASSEMBLY__ +#define __EXPMASK(offset) (((volatile unsigned char *)EXPMASK_BASE)[offset]) +#else +#define __EXPMASK(offset) offset +#endif + +#define EXPMASK_STATUS __EXPMASK(0x00) +#define EXPMASK_ENABLE __EXPMASK(0x04) + #endif +#endif diff --git a/include/asm-arm/arch-arc/ide.h b/include/asm-arm/arch-arc/ide.h index 35c28427f..bccbe3f50 100644 --- a/include/asm-arm/arch-arc/ide.h +++ b/include/asm-arm/arch-arc/ide.h @@ -42,7 +42,7 @@ static __inline__ void ide_init_default_hwifs(void) #ifdef CONFIG_ARCH_A5K hw_regs_t hw; - memset(hw, 0, sizeof(*hw)); + memset(&hw, 0, sizeof(hw)); ide_init_hwif_ports(&hw, 0x1f0, 0x3f6, IRQ_HARDDISK); ide_register_hw(&hw, NULL); diff --git a/include/asm-arm/arch-arc/irq.h b/include/asm-arm/arch-arc/irq.h index 97d3722bd..97b3c499e 100644 --- a/include/asm-arm/arch-arc/irq.h +++ b/include/asm-arm/arch-arc/irq.h @@ -152,4 +152,6 @@ static __inline__ void irq_init_irq(void) break; } } + + init_FIQ(); } diff --git a/include/asm-arm/arch-arc/memory.h b/include/asm-arm/arch-arc/memory.h index 8741f6222..4cc800cea 100644 --- a/include/asm-arm/arch-arc/memory.h +++ b/include/asm-arm/arch-arc/memory.h @@ -15,6 +15,7 @@ * User space: 26MB */ #define TASK_SIZE (0x01a00000UL) +#define TASK_SIZE_26 (0x01a00000UL) /* * Page offset: 32MB diff --git a/include/asm-arm/arch-arc/param.h b/include/asm-arm/arch-arc/param.h new file mode 100644 index 000000000..16b3fc0e4 --- /dev/null +++ b/include/asm-arm/arch-arc/param.h @@ -0,0 +1,3 @@ +/* + * linux/include/asm-arm/arch-arc/param.h + */ diff --git a/include/asm-arm/arch-arc/processor.h b/include/asm-arm/arch-arc/processor.h index acc587961..564565c46 100644 --- a/include/asm-arm/arch-arc/processor.h +++ b/include/asm-arm/arch-arc/processor.h @@ -11,8 +11,6 @@ #ifndef __ASM_ARCH_PROCESSOR_H #define __ASM_ARCH_PROCESSOR_H -#include <asm/arch/memory.h> - /* * Bus types */ diff --git a/include/asm-arm/arch-arc/shmparam.h b/include/asm-arm/arch-arc/shmparam.h deleted file mode 100644 index e4e47e254..000000000 --- a/include/asm-arm/arch-arc/shmparam.h +++ /dev/null @@ -1,5 +0,0 @@ -/* - * linux/include/asm-arm/arch-arc/shmparam.h - * - * Copyright (c) 1996 Russell King. - */ diff --git a/include/asm-arm/arch-arc/system.h b/include/asm-arm/arch-arc/system.h index 068c968cc..e88114616 100644 --- a/include/asm-arm/arch-arc/system.h +++ b/include/asm-arm/arch-arc/system.h @@ -1,11 +1,8 @@ /* * linux/include/asm-arm/arch-arc/system.h * - * Copyright (c) 1996 Russell King and Dave Gilbert + * Copyright (c) 1996-1999 Russell King and Dave Gilbert */ -#ifndef __ASM_ARCH_SYSTEM_H -#define __ASM_ARCH_SYSTEM_H - #include <linux/config.h> #ifdef CONFIG_ARCH_ARC @@ -23,16 +20,13 @@ #endif +#define arch_do_idle() do { } while (0) + extern __inline__ void arch_reset(char mode) { extern void ecard_reset(int card); /* - * Do any cleanups that the processor may require - */ - cpu_proc_fin(); - - /* * Reset all expansion cards. */ ecard_reset(-1); @@ -43,5 +37,3 @@ extern __inline__ void arch_reset(char mode) *(unsigned long *)0 = *(unsigned long *)0x03800000; ((void(*)(void))0)(); } - -#endif diff --git a/include/asm-arm/arch-ebsa110/a.out.h b/include/asm-arm/arch-ebsa110/a.out.h deleted file mode 100644 index 71fcffe8e..000000000 --- a/include/asm-arm/arch-ebsa110/a.out.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * linux/include/asm-arm/arch-ebsa110/a.out.h - * - * Copyright (C) 1996-1999 Russell King - */ -#ifndef __ASM_ARCH_A_OUT_H -#define __ASM_ARCH_A_OUT_H - -#include <asm/arch/memory.h> - -#define STACK_TOP \ - ((current->personality == PER_LINUX_32BIT) ? \ - TASK_SIZE : 0x04000000) - -#endif - diff --git a/include/asm-arm/arch-ebsa110/hardware.h b/include/asm-arm/arch-ebsa110/hardware.h index afa7275b0..0de487421 100644 --- a/include/asm-arm/arch-ebsa110/hardware.h +++ b/include/asm-arm/arch-ebsa110/hardware.h @@ -8,7 +8,7 @@ #ifndef __ASM_ARCH_HARDWARE_H #define __ASM_ARCH_HARDWARE_H -#ifndef __ASSEMBLER__ +#ifndef __ASSEMBLY__ /* * IO definitions @@ -28,11 +28,11 @@ */ #define FLUSH_BASE_PHYS 0x40000000 -#else +#else /* __ASSEMBLY__ */ #define IO_BASE 0 -#endif +#endif /* __ASSEMBLY__ */ #define IO_SIZE 0x20000000 #define IO_START 0xe0000000 diff --git a/include/asm-arm/arch-ebsa110/memory.h b/include/asm-arm/arch-ebsa110/memory.h index 67fa3a917..81bbb720f 100644 --- a/include/asm-arm/arch-ebsa110/memory.h +++ b/include/asm-arm/arch-ebsa110/memory.h @@ -16,6 +16,7 @@ * Task size: 3GB */ #define TASK_SIZE (0xc0000000UL) +#define TASK_SIZE_26 (0x04000000UL) /* * Page offset: 3GB diff --git a/include/asm-arm/arch-ebsa110/param.h b/include/asm-arm/arch-ebsa110/param.h index 46cff02d8..1dcb668bf 100644 --- a/include/asm-arm/arch-ebsa110/param.h +++ b/include/asm-arm/arch-ebsa110/param.h @@ -1,9 +1,4 @@ /* * linux/include/asm-arm/arch-ebsa110/param.h - * - * Copyright (C) 1996 Russell King - * Copyright (C) 1998 Philip Blundell */ - -#define HZ 100 #define HZ_TO_STD(a) (a) diff --git a/include/asm-arm/arch-ebsa110/processor.h b/include/asm-arm/arch-ebsa110/processor.h index bd99869af..d94e9b808 100644 --- a/include/asm-arm/arch-ebsa110/processor.h +++ b/include/asm-arm/arch-ebsa110/processor.h @@ -10,8 +10,6 @@ #ifndef __ASM_ARCH_PROCESSOR_H #define __ASM_ARCH_PROCESSOR_H -#include <asm/arch/memory.h> - /* * Bus types */ diff --git a/include/asm-arm/arch-ebsa110/shmparam.h b/include/asm-arm/arch-ebsa110/shmparam.h deleted file mode 100644 index 9c36489cb..000000000 --- a/include/asm-arm/arch-ebsa110/shmparam.h +++ /dev/null @@ -1,5 +0,0 @@ -/* - * linux/include/asm-arm/arch-ebsa110/shmparam.h - * - * Copyright (c) 1996 Russell King. - */ diff --git a/include/asm-arm/arch-ebsa110/system.h b/include/asm-arm/arch-ebsa110/system.h index ba0c99258..064227627 100644 --- a/include/asm-arm/arch-ebsa110/system.h +++ b/include/asm-arm/arch-ebsa110/system.h @@ -1,17 +1,12 @@ /* * linux/include/asm-arm/arch-ebsa110/system.h * - * Copyright (c) 1996-1998 Russell King. + * Copyright (c) 1996-1999 Russell King. */ #ifndef __ASM_ARCH_SYSTEM_H #define __ASM_ARCH_SYSTEM_H -extern __inline__ void arch_reset(char mode) -{ - /* - * loop endlessly - */ - cli(); -} +#define arch_do_idle() do { } while (0) +#define arch_reset(mode) do { } while (0) #endif diff --git a/include/asm-arm/arch-ebsa110/time.h b/include/asm-arm/arch-ebsa110/time.h index 21728e469..dfb62984c 100644 --- a/include/asm-arm/arch-ebsa110/time.h +++ b/include/asm-arm/arch-ebsa110/time.h @@ -23,13 +23,13 @@ #define PIT1_COUNT 0xecbe #elif defined(MCLK_47_8) /* - * This should be 0x10AE1, but that doesn't exactly fit. + * This should be 0x10B43, but that doesn't exactly fit. * We run the timer interrupt at 5ms, and then divide it by * two in software... This is so that the user processes * see exactly the same model whichever ARM processor they're * running on. */ -#define PIT1_COUNT 0x8570 +#define PIT1_COUNT 0x85A1 #define DIVISOR 2 #endif diff --git a/include/asm-arm/arch-ebsa285/a.out.h b/include/asm-arm/arch-ebsa285/a.out.h deleted file mode 100644 index 71fcffe8e..000000000 --- a/include/asm-arm/arch-ebsa285/a.out.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * linux/include/asm-arm/arch-ebsa110/a.out.h - * - * Copyright (C) 1996-1999 Russell King - */ -#ifndef __ASM_ARCH_A_OUT_H -#define __ASM_ARCH_A_OUT_H - -#include <asm/arch/memory.h> - -#define STACK_TOP \ - ((current->personality == PER_LINUX_32BIT) ? \ - TASK_SIZE : 0x04000000) - -#endif - diff --git a/include/asm-arm/arch-ebsa285/hardware.h b/include/asm-arm/arch-ebsa285/hardware.h index c989e0f09..ebc42770c 100644 --- a/include/asm-arm/arch-ebsa285/hardware.h +++ b/include/asm-arm/arch-ebsa285/hardware.h @@ -122,8 +122,10 @@ #define GPIO_RESET 0x001 /* CPLD pins */ -#define CPLD_DSRESET 8 +#define CPLD_DS_ENABLE 8 +#define CPLD_7111_DISABLE 4 #define CPLD_UNMUTE 2 +#define CPLD_FLASH_WR_ENABLE 1 #ifndef __ASSEMBLY__ extern void gpio_modify_op(int mask, int set); diff --git a/include/asm-arm/arch-ebsa285/io.h b/include/asm-arm/arch-ebsa285/io.h index 462d6ba72..a9cc3afe0 100644 --- a/include/asm-arm/arch-ebsa285/io.h +++ b/include/asm-arm/arch-ebsa285/io.h @@ -74,7 +74,9 @@ extern __inline__ void __outw(unsigned int value, unsigned int port) void *_ret = NULL; \ if (valid_ioaddr(_addr, _size)) { \ _addr = io_to_phys(_addr); \ - _ret = __ioremap(_addr, _size, 0) - IO_FUDGE_FACTOR; \ + _ret = __ioremap(_addr, _size, 0); \ + if (_ret) \ + _ret = (void *)((int) _ret - IO_FUDGE_FACTOR); \ } \ _ret; }) diff --git a/include/asm-arm/arch-ebsa285/irq.h b/include/asm-arm/arch-ebsa285/irq.h index 1c4bc42be..d97490079 100644 --- a/include/asm-arm/arch-ebsa285/irq.h +++ b/include/asm-arm/arch-ebsa285/irq.h @@ -10,7 +10,7 @@ * 26-Jan-1999 PJB Don't use IACK on CATS * 16-Mar-1999 RMK Added autodetect of ISA PICs */ -#include <linux/config.h> +/* no need for config.h - arch/arm/kernel/irq.c does this for us */ #include <asm/hardware.h> #include <asm/dec21285.h> #include <asm/irq.h> @@ -135,28 +135,21 @@ static __inline__ void irq_init_irq(void) * Determine the ISA settings for * the machine we're running on. */ - switch (machine_arch_type) { - default: - isa_irq = -1; - break; + isa_irq = -1; - case MACH_TYPE_EBSA285: + if (machine_is_ebsa285()) /* The following is dependent on which slot * you plug the Southbridge card into. We * currently assume that you plug it into * the right-hand most slot. */ isa_irq = IRQ_PCI; - break; - case MACH_TYPE_CATS: + if (machine_is_cats()) isa_irq = IRQ_IN2; - break; - case MACH_TYPE_NETWINDER: + if (machine_is_netwinder()) isa_irq = IRQ_IN3; - break; - } if (isa_irq != -1) { /* @@ -176,8 +169,6 @@ static __inline__ void irq_init_irq(void) outb(0x01, PIC_MASK_HI); /* x86 */ outb(0xfa, PIC_MASK_HI); /* pattern: 11111010 */ -// outb(0x68, PIC_LO); /* enable special mode */ -// outb(0x68, PIC_HI); /* enable special mode */ outb(0x0b, PIC_LO); outb(0x0b, PIC_HI); diff --git a/include/asm-arm/arch-ebsa285/keyboard.h b/include/asm-arm/arch-ebsa285/keyboard.h index dcc7b49f9..691514e6b 100644 --- a/include/asm-arm/arch-ebsa285/keyboard.h +++ b/include/asm-arm/arch-ebsa285/keyboard.h @@ -69,3 +69,23 @@ extern unsigned char pckbd_sysrq_xlate[128]; #define kbd_enable_irq() #define SYSRQ_KEY 0x54 + +/* resource allocation */ +#define kbd_request_region() +#define kbd_request_irq(handler) request_irq(KEYBOARD_IRQ, handler, 0, \ + "keyboard", NULL) + +/* How to access the keyboard macros on this platform. */ +#define kbd_read_input() inb(KBD_DATA_REG) +#define kbd_read_status() inb(KBD_STATUS_REG) +#define kbd_write_output(val) outb(val, KBD_DATA_REG) +#define kbd_write_command(val) outb(val, KBD_CNTL_REG) + +/* Some stoneage hardware needs delays after some operations. */ +#define kbd_pause() do { } while(0) + +#define aux_request_irq(hand, dev_id) \ + request_irq(AUX_IRQ, hand, SA_SHIRQ, "PS/2 Mouse", dev_id) + +#define aux_free_irq(dev_id) free_irq(AUX_IRQ, dev_id) + diff --git a/include/asm-arm/arch-ebsa285/memory.h b/include/asm-arm/arch-ebsa285/memory.h index a03cea639..25e720489 100644 --- a/include/asm-arm/arch-ebsa285/memory.h +++ b/include/asm-arm/arch-ebsa285/memory.h @@ -23,6 +23,7 @@ * Task size: 3GB */ #define TASK_SIZE (0xc0000000UL) +#define TASK_SIZE_26 (0x04000000UL) /* * Page offset: 3GB @@ -42,6 +43,7 @@ * Task size: 1.5GB */ #define TASK_SIZE (0x60000000UL) +#define TASK_SIZE_26 (0x04000000UL) /* * Page offset: 1.5GB diff --git a/include/asm-arm/arch-ebsa285/param.h b/include/asm-arm/arch-ebsa285/param.h index 1efdf79a2..29c931cc6 100644 --- a/include/asm-arm/arch-ebsa285/param.h +++ b/include/asm-arm/arch-ebsa285/param.h @@ -1,9 +1,4 @@ /* * linux/include/asm-arm/arch-ebsa285/param.h - * - * Copyright (C) 1996 Russell King - * Copyright (C) 1998 Philip Blundell */ - -#define HZ 100 #define HZ_TO_STD(a) (a) diff --git a/include/asm-arm/arch-ebsa285/processor.h b/include/asm-arm/arch-ebsa285/processor.h index bd99869af..d94e9b808 100644 --- a/include/asm-arm/arch-ebsa285/processor.h +++ b/include/asm-arm/arch-ebsa285/processor.h @@ -10,8 +10,6 @@ #ifndef __ASM_ARCH_PROCESSOR_H #define __ASM_ARCH_PROCESSOR_H -#include <asm/arch/memory.h> - /* * Bus types */ diff --git a/include/asm-arm/arch-ebsa285/shmparam.h b/include/asm-arm/arch-ebsa285/shmparam.h deleted file mode 100644 index 9c36489cb..000000000 --- a/include/asm-arm/arch-ebsa285/shmparam.h +++ /dev/null @@ -1,5 +0,0 @@ -/* - * linux/include/asm-arm/arch-ebsa110/shmparam.h - * - * Copyright (c) 1996 Russell King. - */ diff --git a/include/asm-arm/arch-ebsa285/system.h b/include/asm-arm/arch-ebsa285/system.h index 4f3850b44..24a22d31c 100644 --- a/include/asm-arm/arch-ebsa285/system.h +++ b/include/asm-arm/arch-ebsa285/system.h @@ -1,24 +1,22 @@ /* * linux/include/asm-arm/arch-ebsa285/system.h * - * Copyright (c) 1996,1997,1998 Russell King. + * Copyright (c) 1996-1999 Russell King. */ #include <asm/dec21285.h> #include <asm/io.h> #include <asm/hardware.h> #include <asm/leds.h> +#define arch_do_idle() cpu_do_idle() + extern __inline__ void arch_reset(char mode) { - cli(); - if (mode == 's') { __asm__ volatile ( "mov lr, #0x41000000 @ prepare to jump to ROM - mov r0, #0x130 - mcr p15, 0, r0, c1, c0 @ MMU off - mcr p15, 0, ip, c7, c7 @ flush caches - mov pc, lr" : : : "cc"); + mcr p15, 0, %0, c1, c0, 0 @ MMU off + mov pc, lr" : : "r" (cpu_reset()) : "cc"); } else { if (machine_is_netwinder()) { /* open up the SuperIO chip @@ -51,6 +49,3 @@ extern __inline__ void arch_reset(char mode) } } } - -#define arch_start_idle() leds_event(led_idle_start) -#define arch_end_idle() leds_event(led_idle_end) diff --git a/include/asm-arm/arch-ebsa285/time.h b/include/asm-arm/arch-ebsa285/time.h index 91e3ae284..3c5bcca3b 100644 --- a/include/asm-arm/arch-ebsa285/time.h +++ b/include/asm-arm/arch-ebsa285/time.h @@ -282,7 +282,7 @@ set_dummy_time(unsigned long secs) */ extern __inline__ void setup_timer(void) { - if (machine_arch_type == MACH_TYPE_CO285) + if (machine_is_co285()) /* * Add-in 21285s shouldn't access the RTC */ diff --git a/include/asm-arm/arch-ebsa285/uncompress.h b/include/asm-arm/arch-ebsa285/uncompress.h index e72e4f362..ab103585c 100644 --- a/include/asm-arm/arch-ebsa285/uncompress.h +++ b/include/asm-arm/arch-ebsa285/uncompress.h @@ -1,18 +1,24 @@ /* * linux/include/asm-arm/arch-ebsa285/uncompress.h * - * Copyright (C) 1996,1997,1998 Russell King + * Copyright (C) 1996-1999 Russell King */ /* * Note! This could cause problems on the NetWinder */ -#define BASE 0x42000160 +#define DC21285_BASE ((volatile unsigned int *)0x42000160) +#define SER0_BASE ((volatile unsigned char *)0x7c0003f8) static __inline__ void putc(char c) { - while (*((volatile unsigned int *)(BASE + 0x18)) & 8); - *((volatile unsigned int *)(BASE)) = c; + if (machine_is_netwinder()) { + while ((SER0_BASE[5] & 0x60) != 0x60); + SER0_BASE[0] = c; + } else { + while (DC21285_BASE[6] & 8); + DC21285_BASE[0] = c; + } } /* diff --git a/include/asm-arm/arch-nexuspci/a.out.h b/include/asm-arm/arch-nexuspci/a.out.h deleted file mode 100644 index 1cc4d571d..000000000 --- a/include/asm-arm/arch-nexuspci/a.out.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * linux/include/asm-arm/arch-nexuspci/a.out.h - * - * Copyright (C) 1996-1999 Russell King - */ -#ifndef __ASM_ARCH_A_OUT_H -#define __ASM_ARCH_A_OUT_H - -#include <asm/arch/memory.h> - -#define STACK_TOP \ - ((current->personality == PER_LINUX_32BIT) ? \ - TASK_SIZE : 0x04000000) - -#endif - diff --git a/include/asm-arm/arch-nexuspci/memory.h b/include/asm-arm/arch-nexuspci/memory.h index e721c040d..7b89119aa 100644 --- a/include/asm-arm/arch-nexuspci/memory.h +++ b/include/asm-arm/arch-nexuspci/memory.h @@ -12,6 +12,7 @@ * Task size: 3GB */ #define TASK_SIZE (0xc0000000UL) +#define TASK_SIZE_26 (0x04000000UL) /* * Page offset: 3GB diff --git a/include/asm-arm/arch-nexuspci/param.h b/include/asm-arm/arch-nexuspci/param.h index cb3e53ea1..fb1354008 100644 --- a/include/asm-arm/arch-nexuspci/param.h +++ b/include/asm-arm/arch-nexuspci/param.h @@ -1,9 +1,4 @@ /* * linux/include/asm-arm/arch-nexuspci/param.h - * - * Copyright (C) 1996 Russell King - * Copyright (C) 1998 Philip Blundell */ - -#define HZ 100 #define HZ_TO_STD(a) (a) diff --git a/include/asm-arm/arch-nexuspci/processor.h b/include/asm-arm/arch-nexuspci/processor.h index 8349d6d46..542dd9106 100644 --- a/include/asm-arm/arch-nexuspci/processor.h +++ b/include/asm-arm/arch-nexuspci/processor.h @@ -11,8 +11,6 @@ #ifndef __ASM_ARCH_PROCESSOR_H #define __ASM_ARCH_PROCESSOR_H -#include <asm/arch/memory.h> - /* * Bus types */ diff --git a/include/asm-arm/arch-nexuspci/shmparam.h b/include/asm-arm/arch-nexuspci/shmparam.h deleted file mode 100644 index 191bd514d..000000000 --- a/include/asm-arm/arch-nexuspci/shmparam.h +++ /dev/null @@ -1,5 +0,0 @@ -/* - * linux/include/asm-arm/arch-nexuspci/shmparam.h - * - * Copyright (c) 1996 Russell King. - */ diff --git a/include/asm-arm/arch-nexuspci/system.h b/include/asm-arm/arch-nexuspci/system.h index b5a2464b2..c1605d3fa 100644 --- a/include/asm-arm/arch-nexuspci/system.h +++ b/include/asm-arm/arch-nexuspci/system.h @@ -1,17 +1,12 @@ /* * linux/include/asm-arm/arch-nexuspci/system.h * - * Copyright (c) 1996,1997,1998 Russell King. + * Copyright (c) 1996-1999 Russell King. */ #ifndef __ASM_ARCH_SYSTEM_H #define __ASM_ARCH_SYSTEM_H -extern __inline__ void arch_reset(char mode) -{ - /* - * loop endlessly - the watchdog will reset us if it's enabled. - */ - cli(); -} +#define arch_do_idle() do { } while (0) +#define arch_reset(mode) do { } while (0) #endif diff --git a/include/asm-arm/arch-rpc/a.out.h b/include/asm-arm/arch-rpc/a.out.h deleted file mode 100644 index 56b2f4f7a..000000000 --- a/include/asm-arm/arch-rpc/a.out.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * linux/include/asm-arm/arch-rpc/a.out.h - * - * Copyright (C) 1996-1999 Russell King - */ -#ifndef __ASM_ARCH_A_OUT_H -#define __ASM_ARCH_A_OUT_H - -#include <asm/arch/memory.h> - -#define STACK_TOP \ - ((current->personality == PER_LINUX_32BIT) ? \ - TASK_SIZE : 0x04000000) - -#endif - diff --git a/include/asm-arm/arch-rpc/acornfb.h b/include/asm-arm/arch-rpc/acornfb.h new file mode 100644 index 000000000..531481215 --- /dev/null +++ b/include/asm-arm/arch-rpc/acornfb.h @@ -0,0 +1,99 @@ +/* + * linux/include/asm-arm/arch-rpc/acornfb.h + * + * (C) 1999 Russell King + * + * AcornFB architecture specific code + */ + +#define acornfb_valid_pixrate(rate) (1) + +/* + * Try to find the best PLL parameters for the pixel clock. + * This algorithm seems to give best predictable results, + * and produces the same values as detailed in the VIDC20 + * data sheet. + */ +static inline u_int +acornfb_vidc20_find_pll(u_int pixclk) +{ + u_int r, best_r = 2, best_v = 2; + int best_d = 0x7fffffff; + + for (r = 2; r <= 32; r++) { + u_int rr, v, p; + int d; + + rr = 41667 * r; + + v = (rr + pixclk / 2) / pixclk; + + if (v > 32 || v < 2) + continue; + + p = (rr + v / 2) / v; + + d = pixclk - p; + + if (d < 0) + d = -d; + + if (d < best_d) { + best_d = d; + best_v = v - 1; + best_r = r - 1; + } + + if (d == 0) + break; + } + + return best_v << 8 | best_r; +} + +static inline void +acornfb_vidc20_find_rates(struct vidc_timing *vidc, + struct fb_var_screeninfo *var) +{ + u_int div, bandwidth; + + /* Select pixel-clock divisor to keep PLL in range */ + div = var->pixclock / 9090; /*9921*/ + + /* Limit divisor */ + if (div == 0) + div = 1; + if (div > 8) + div = 8; + + /* Encode divisor to VIDC20 setting */ + switch (div) { + case 1: vidc->control |= VIDC20_CTRL_PIX_CK; break; + case 2: vidc->control |= VIDC20_CTRL_PIX_CK2; break; + case 3: vidc->control |= VIDC20_CTRL_PIX_CK3; break; + case 4: vidc->control |= VIDC20_CTRL_PIX_CK4; break; + case 5: vidc->control |= VIDC20_CTRL_PIX_CK5; break; + case 6: vidc->control |= VIDC20_CTRL_PIX_CK6; break; + case 7: vidc->control |= VIDC20_CTRL_PIX_CK7; break; + case 8: vidc->control |= VIDC20_CTRL_PIX_CK8; break; + } + + /* Calculate bandwidth */ + bandwidth = var->pixclock * 8 / var->bits_per_pixel; + + /* Encode bandwidth as VIDC20 setting */ + if (bandwidth > 33334) + vidc->control |= VIDC20_CTRL_FIFO_16; /* < 30.0MB/s */ + else if (bandwidth > 26666) + vidc->control |= VIDC20_CTRL_FIFO_20; /* < 37.5MB/s */ + else if (bandwidth > 22222) + vidc->control |= VIDC20_CTRL_FIFO_24; /* < 45.0MB/s */ + else + vidc->control |= VIDC20_CTRL_FIFO_28; /* > 45.0MB/s */ + + /* Find the PLL values */ + vidc->pll_ctl = acornfb_vidc20_find_pll(var->pixclock / div); +} + +#define acornfb_default_control() (VIDC20_CTRL_PIX_VCLK) +#define acornfb_default_econtrol() (VIDC20_ECTL_DAC | VIDC20_ECTL_REG(3)) diff --git a/include/asm-arm/arch-rpc/hardware.h b/include/asm-arm/arch-rpc/hardware.h index 0e3002f85..0ae320b86 100644 --- a/include/asm-arm/arch-rpc/hardware.h +++ b/include/asm-arm/arch-rpc/hardware.h @@ -41,7 +41,7 @@ #define FLUSH_BASE 0xdf000000 -#ifndef __ASSEMBLER__ +#ifndef __ASSEMBLY__ /* * for use with inb/outb @@ -93,5 +93,17 @@ #define PCIO_BASE 0xe0010000 #endif + +#ifdef HAS_EXPMASK +#ifndef __ASSEMBLY__ +#define __EXPMASK(offset) (((volatile unsigned char *)EXPMASK_BASE)[offset]) +#else +#define __EXPMASK(offset) offset +#endif + +#define EXPMASK_STATUS __EXPMASK(0x00) +#define EXPMASK_ENABLE __EXPMASK(0x04) + #endif +#endif diff --git a/include/asm-arm/arch-rpc/irq.h b/include/asm-arm/arch-rpc/irq.h index ea284b9a5..c7d98b0c0 100644 --- a/include/asm-arm/arch-rpc/irq.h +++ b/include/asm-arm/arch-rpc/irq.h @@ -174,4 +174,6 @@ static __inline__ void irq_init_irq(void) break; } } + + init_FIQ(); } diff --git a/include/asm-arm/arch-rpc/memory.h b/include/asm-arm/arch-rpc/memory.h index 6922cd0f3..d5a6a4f9f 100644 --- a/include/asm-arm/arch-rpc/memory.h +++ b/include/asm-arm/arch-rpc/memory.h @@ -18,16 +18,17 @@ * Task size: 3GB */ #define TASK_SIZE (0xc0000000UL) +#define TASK_SIZE_26 (0x04000000UL) /* * Page offset: 3GB */ #define PAGE_OFFSET (0xc0000000UL) -#ifndef __ASSEMBLY__ -extern unsigned long __virt_to_phys(unsigned long vpage); -extern unsigned long __phys_to_virt(unsigned long ppage); -#endif +#define __virt_to_phys__is_a_macro +#define __virt_to_phys(vpage) ((vpage) - PAGE_OFFSET + 0x10000000) +#define __phys_to_virt__is_a_macro +#define __phys_to_virt(ppage) ((ppage) + PAGE_OFFSET - 0x10000000) /* * These are exactly the same on the RiscPC as the diff --git a/include/asm-arm/arch-rpc/mm-init.h b/include/asm-arm/arch-rpc/mm-init.h deleted file mode 100644 index 81de91a01..000000000 --- a/include/asm-arm/arch-rpc/mm-init.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * linux/include/asm-arm/arch-rpc/mmap.h - * - * Copyright (C) 1996 Russell King - */ - -#define HAVE_MAP_VID_MEM - -unsigned long map_screen_mem(unsigned long log_start, unsigned long kmem, int update) -{ - static int updated = 0; - unsigned long address; - pgd_t *pgd; - - if (updated) - return 0; - updated = update; - - address = SCREEN_START | PMD_TYPE_SECT | PMD_DOMAIN(DOMAIN_KERNEL) | PMD_SECT_AP_WRITE; - pgd = swapper_pg_dir + (SCREEN2_BASE >> PGDIR_SHIFT); - pgd_val(pgd[0]) = address; - pgd_val(pgd[1]) = address + (1 << PGDIR_SHIFT); - - if (update) { - unsigned long pgtable = PAGE_ALIGN(kmem), *p; - int i; - - memzero ((void *)pgtable, 4096); - - pgd_val(pgd[-2]) = __virt_to_phys(pgtable) | PMD_TYPE_TABLE | PMD_DOMAIN(DOMAIN_KERNEL); - pgd_val(pgd[-1]) = __virt_to_phys(pgtable + PTRS_PER_PTE*4) | PMD_TYPE_TABLE | PMD_DOMAIN(DOMAIN_KERNEL); - p = (unsigned long *)pgtable; - - i = PTRS_PER_PTE * 2 - ((SCREEN1_END - log_start) >> PAGE_SHIFT); - address = SCREEN_START | PTE_TYPE_SMALL | PTE_AP_WRITE; - - while (i < PTRS_PER_PTE * 2) { - p[i++] = address; - address += PAGE_SIZE; - } - - flush_page_to_ram(pgtable); - - kmem = pgtable + PAGE_SIZE; - } - return kmem; -} diff --git a/include/asm-arm/arch-rpc/param.h b/include/asm-arm/arch-rpc/param.h index c71625c93..4ac90b86b 100644 --- a/include/asm-arm/arch-rpc/param.h +++ b/include/asm-arm/arch-rpc/param.h @@ -1,9 +1,4 @@ /* * linux/include/asm-arm/arch-rpc/param.h - * - * Copyright (C) 1996 Russell King - * Copyright (C) 1998 Philip Blundell */ - -#define HZ 100 #define HZ_TO_STD(a) (a) diff --git a/include/asm-arm/arch-rpc/processor.h b/include/asm-arm/arch-rpc/processor.h index b2b6aec12..fd9aa088d 100644 --- a/include/asm-arm/arch-rpc/processor.h +++ b/include/asm-arm/arch-rpc/processor.h @@ -11,8 +11,6 @@ #ifndef __ASM_ARCH_PROCESSOR_H #define __ASM_ARCH_PROCESSOR_H -#include <asm/arch/memory.h> - /* * Bus types */ diff --git a/include/asm-arm/arch-rpc/shmparam.h b/include/asm-arm/arch-rpc/shmparam.h deleted file mode 100644 index 2f7dec9c3..000000000 --- a/include/asm-arm/arch-rpc/shmparam.h +++ /dev/null @@ -1,5 +0,0 @@ -/* - * linux/include/asm-arm/arch-rpc/shmparam.h - * - * Copyright (c) 1996 Russell King. - */ diff --git a/include/asm-arm/arch-rpc/system.h b/include/asm-arm/arch-rpc/system.h index a4b92939e..af4ebc348 100644 --- a/include/asm-arm/arch-rpc/system.h +++ b/include/asm-arm/arch-rpc/system.h @@ -1,23 +1,24 @@ /* * linux/include/asm-arm/arch-rpc/system.h * - * Copyright (c) 1996 Russell King + * Copyright (c) 1996-1999 Russell King. */ -#ifndef __ASM_ARCH_SYSTEM_H -#define __ASM_ARCH_SYSTEM_H - +#include <asm/arch/hardware.h> #include <asm/iomd.h> +#include <asm/io.h> + +#define arch_do_idle() cpu_do_idle() + +extern __inline__ void arch_reset(char mode) +{ + extern void ecard_reset(int card); + + ecard_reset(-1); -#define arch_reset(mode) { \ - extern void ecard_reset (int card); \ - outb (0, IOMD_ROMCR0); \ - ecard_reset(-1); \ - cli(); \ - __asm__ __volatile__("msr spsr, r1;" \ - "mcr p15, 0, %0, c1, c0, 0;" \ - "movs pc, #0" \ - : \ - : "r" (cpu_reset())); \ - } + outb(0, IOMD_ROMCR0); -#endif + __asm__ __volatile__( + "mcr p15, 0, %0, c1, c0, 0\n\t" + "movs pc, #0" + : : "r" (cpu_reset())); +} diff --git a/include/asm-arm/arch-sa1100/dma.h b/include/asm-arm/arch-sa1100/dma.h new file mode 100644 index 000000000..4aabcaf32 --- /dev/null +++ b/include/asm-arm/arch-sa1100/dma.h @@ -0,0 +1,9 @@ +#ifndef __ASM_ARCH_DMA_H +#define __ASM_ARCH_DMA_H + +#define MAX_DMA_ADDRESS 0xffffffff + +/* No DMA (strictly, a lie :-) ) */ +#define MAX_DMA_CHANNELS 0 + +#endif /* _ASM_ARCH_DMA_H */ diff --git a/include/asm-arm/arch-sa1100/hardware.h b/include/asm-arm/arch-sa1100/hardware.h new file mode 100644 index 000000000..407641aa9 --- /dev/null +++ b/include/asm-arm/arch-sa1100/hardware.h @@ -0,0 +1,25 @@ +/* + * linux/include/asm-arm/arch-brutus/hardware.h + * + * Copyright (C) 1998 Nicolas Pitre <nico@visuaide.com> + * + * This file contains the hardware definitions for SA1100 architecture + */ + +#ifndef __ASM_ARCH_HARDWARE_H +#define __ASM_ARCH_HARDWARE_H + +/* Flushing areas */ +#define FLUSH_BASE_PHYS 0xe0000000 /* SA1100 zero bank */ +#define FLUSH_BASE 0xdf000000 +#define FLUSH_BASE_MINICACHE 0xde000000 + +/* + * PCMCIA IO is mapped to 0xe0000000. We are likely to use in*()/out*() + * IO macros for what might appear there... + * The SA1100 PCMCIA interface can be seen like a PC ISA bus for IO. + */ +#define PCIO_BASE 0xe0000000 /* PCMCIA0 IO space */ + +#endif + diff --git a/include/asm-arm/arch-sa1100/io.h b/include/asm-arm/arch-sa1100/io.h new file mode 100644 index 000000000..6c8d3711a --- /dev/null +++ b/include/asm-arm/arch-sa1100/io.h @@ -0,0 +1,46 @@ +/* + * linux/include/asm-arm/arch-ebsa285/io.h + * + * Copyright (C) 1997-1999 Russell King + * + * Modifications: + * 06-12-1997 RMK Created. + * 07-04-1999 RMK Major cleanup + */ +#ifndef __ASM_ARM_ARCH_IO_H +#define __ASM_ARM_ARCH_IO_H + +/* + * This architecture does not require any delayed IO + */ +#undef ARCH_IO_DELAY + +#define __pci_io_addr(x) (PCIO_BASE + (unsigned int)(x)) + +#define __inb(p) (*(volatile unsigned char *)__pci_io_addr(p)) +#define __inl(p) (*(volatile unsigned long *)__pci_io_addr(p)) + +extern __inline__ unsigned int __inw(unsigned int port) +{ + unsigned int value; + __asm__ __volatile__( + "ldr%?h %0, [%1, %2] @ inw" + : "=&r" (value) + : "r" (PCIO_BASE), "r" (port)); + return value; +} + + +#define __outb(v,p) (*(volatile unsigned char *)__pci_io_addr(p) = (v)) +#define __outl(v,p) (*(volatile unsigned long *)__pci_io_addr(p) = (v)) + +extern __inline__ void __outw(unsigned int value, unsigned int port) +{ + __asm__ __volatile__( + "str%?h %0, [%1, %2] @ outw" + : : "r" (value), "r" (PCIO_BASE), "r" (port)); +} + +#define __ioaddr(p) __pci_io_addr(p) + +#endif diff --git a/include/asm-arm/arch-sa1100/irq.h b/include/asm-arm/arch-sa1100/irq.h new file mode 100644 index 000000000..e81a99910 --- /dev/null +++ b/include/asm-arm/arch-sa1100/irq.h @@ -0,0 +1,183 @@ +/* + * linux/include/asm-arm/arch-sa1100/irq.h + * + * Copyright (C) 1996-1999 Russell king + * Copyright (C) 1999 Hugo Fiennes + * + * Changelog: + * 22-08-1998 RMK Restructured IRQ routines + * 06-01-1999 HBF SA1100 twiddles + * 12-02-1999 NP added ICCR + * 17-02-1999 NP empeg henry ugly hacks now in a separate file ;) + * 11-08-1999 PD SA1101 support added + * 25-09-1999 RMK Merged into main ARM tree, cleaned up + */ +static inline unsigned int fixup_irq(unsigned int irq) +{ +#ifdef CONFIG_SA1101 + if (irq == SA1101_IRQ) { + unsigned long mask; + mask = INTSTATCLR0 & INTENABLE0; + irq = 32; + if (!mask) { + mask = IRQSTATCLR1 & INTENABLE1; + irq = 64; + } + if (mask) + while ((mask & 1) == 0) { + mask >>= 1; + irq += 1; + } + } +#endif + return irq; +} + +/* We don't need to ACK IRQs on the SA1100 unless they're <= 10, + * ie, an edge-detect. + */ +static void sa1100_mask_and_ack_irq(unsigned int irq) +{ + ICMR &= ~(1 << irq); + if (irq <= 10) + GEDR = 1 << irq; +} + +static void sa1100_mask_irq(unsigned int irq) +{ + ICMR &= ~(1 << irq); +} + +static void sa1100_unmask_irq(unsigned int irq) +{ + ICMR |= 1 << irq; +} + +#ifdef CONFIG_SA1101 + +static void sa1101_mask_and_ack_lowirq(unsigned int irq) +{ + unsigned int mask = 1 << (irq & 31); + + INTENABLE0 &= ~mask; + GEDR = 1 << SA1101_IRQ; + INTSTATCLR0 = mask; +} + +static void sa1101_mask_and_ack_highirq(unsigned int irq) +{ + unsigned int mask = 1 << (irq & 31); + + INTENABLE1 &= ~mask; + GEDR = 1 << SA1101_IRQ; + INTSTATCLR1 = mask; +} + +static void sa1101_mask_lowirq(unsigned int irq) +{ + unsigned int mask = 1 << (irq & 31); + + INTENABLE0 &= ~mask; +} + +static void sa1101_mask_highirq(unsigned int irq) +{ + unsigned int mask = 1 << (irq & 31); + + INTENABLE1 &= ~mask; +} + +/* + * unmasking an IRq with the wrong polarity can be + * fatal, but there is no need to check this in the + * interrupt code - it will be spotted anyway ;-) + */ +static void sa1101_unmask_lowirq(unsigned int irq) +{ + unsigned int mask = 1 << (irq & 31); + + INTENABLE0 |= mask; + ICMR |= 1 << SA1101_IRQ; +} + +static void sa1101_unmask_highirq(unsigned int irq) +{ + unsigned int mask = 1 << (irq & 31); + + INTENABLE1 |= mask; + ICMR |= 1 << SA1101_IRQ; +} +#endif + +static __inline__ void irq_init_irq(void) +{ + int irq; + + /* disable all IRQs */ + ICMR = 0; + + /* all IRQs are IRQ, not FIQ */ + ICLR = 0; + + /* clear all GPIO edge detects */ + GEDR = -1; + +#ifdef CONFIG_SA1101 + /* turn on interrupt controller */ + SKPCR |= 4; + + /* disable all IRQs */ + INTENABLE0 = 0; + INTENABLE1 = 0; + + /* detect on rising edge */ + INTPOL0 = 0; + INTPOL1 = 0; + + /* clear all IRQs */ + INTSTATCLR0 = -1; + INTSTATCLR1 = -1; + + /* SA1101 generates a rising edge */ + GRER |= 1 << SA1101_IRQ; + GPER &= ~(1 << SA1101_IRQ); +#endif + + /* + * Whatever the doc says, this has to be set for the wait-on-irq + * instruction to work... on a SA1100 rev 9 at least. + */ + ICCR = 1; + +#ifndef CONFIG_SA1101 + for (irq = 0; irq < NR_IRQS; irq++) { + irq_desc[irq].valid = 1; + irq_desc[irq].probe_ok = 1; + irq_desc[irq].mask_ack = sa1100_mask_and_ack_irq; + irq_desc[irq].mask = sa1100_mask_irq; + irq_desc[irq].unmask = sa1100_unmask_irq; + } +#else + for (irq = 0; irq < 31; irq++) { + irq_desc[irq].valid = 1; + irq_desc[irq].probe_ok = 1; + irq_desc[irq].mask_ack = sa1100_mask_and_ack_irq; + irq_desc[irq].mask = sa1100_mask_irq; + irq_desc[irq].unmask = sa1100_unmask_irq; + } + for (irq = 32; irq < 63; irq++) { + irq_desc[irq].valid = 1; + irq_desc[irq].probe_ok = 1; + irq_desc[irq].mask_ack = sa1101_mask_and_ack_lowirq; + irq_desc[irq].mask = sa1101_mask_lowirq; + irq_desc[irq].unmask = sa1101_unmask_lowirq; + } + for (irq = 64; irq < NR_IRQS; irq++) { + irq_desc[irq].valid = 1; + irq_desc[irq].probe_ok = 1; + irq_desc[irq].mask_ack = sa1101_mask_and_ack_highirq; + irq_desc[irq].mask = sa1101_mask_highirq; + irq_desc[irq].unmask = sa1101_unmask_highirq; + } +#endif +} diff --git a/include/asm-arm/arch-sa1100/irqs.h b/include/asm-arm/arch-sa1100/irqs.h new file mode 100644 index 000000000..f09145c77 --- /dev/null +++ b/include/asm-arm/arch-sa1100/irqs.h @@ -0,0 +1,111 @@ +/* + * linux/include/asm-arm/arch-sa1100/irqs.h + * + * Copyright (C) 1996 Russell King + * Copyright (C) 1998 Deborah Wallach (updates for SA1100/Brutus). + */ + +#ifdef CONFIG_SA1101 +#define NR_IRQS 95 + +#define GPAIN0 32 +#define GPAIN1 33 +#define GPAIN2 34 +#define GPAIN3 35 +#define GPAIN4 36 +#define GPAIN5 37 +#define GPAIN6 38 +#define GPAIN7 39 +#define GPBIN0 40 +#define GPBIN1 41 +#define GPBIN2 42 +#define GPBIN3 43 +#define GPBIN4 44 +#define GPBIN5 45 +#define GPBIN6 46 +#define RESERVED 47 +#define KPXIN0 48 +#define KPXIN1 49 +#define KPXIN2 50 +#define KPXIN3 51 +#define KPXIN4 52 +#define KPXIN5 53 +#define KPXIN6 54 +#define KPXIN7 55 +#define KPYIN0 56 +#define KPYIN1 57 +#define KPYIN2 58 +#define KPYIN3 59 +#define KPYIN4 60 +#define KPYIN5 61 +#define KPYIN6 62 +#define KPYIN7 63 +#define KPYIN8 64 +#define KPYIN9 65 +#define KPYIN10 66 +#define KPYIN11 67 +#define KPYIN12 68 +#define KPYIN13 69 +#define KPYIN14 70 +#define KPYIN15 71 +#define MSTXINT 72 +#define MSRXINT 73 +#define TPTXINT 74 +#define TPRXINT 75 +#define INTREQTRC 76 +#define INTREQTIM 77 +#define INTREQRAV 78 +#define INTREQINT 79 +#define INTREQEMP 80 +#define INTREQDAT 81 +#define VIDEOINT 82 +#define FIFOINT 83 +#define NIRQHCIM 84 +#define IRQHCIBUFFACC 85 +#define IRQHCIRMTWKP 86 +#define NHCIMFCLIR 87 +#define USBERROR 88 +#define S0_READY_NIREQ 89 +#define S1_READY_NIREQ 90 +#define S0_CDVALID 91 +#define S1_CDVALID 92 +#define S0_BVD1_STSCHG 93 +#define S1_BVD1_STSCHG 94 +#define USB_PORT_RESUME 95 + +#else +#define NR_IRQS 32 +#endif + +#define IRQ_GPIO0 0 +#define IRQ_GPIO1 1 +#define IRQ_GPIO2 2 +#define IRQ_GPIO3 3 +#define IRQ_GPIO4 4 +#define IRQ_GPIO5 5 +#define IRQ_GPIO6 6 +#define IRQ_GPIO7 7 +#define IRQ_GPIO8 8 +#define IRQ_GPIO9 9 +#define IRQ_GPIO10 10 +#define IRQ_GPIO11_27 11 +#define IRQ_LCD 12 /* LCD controller */ +#define IRQ_Ser0UDC 13 /* Ser. port 0 UDC */ +#define IRQ_Ser1SDLC 14 /* Ser. port 1 SDLC */ +#define IRQ_Ser1UART 15 /* Ser. port 1 UART */ +#define IRQ_Ser2ICP 16 /* Ser. port 2 ICP */ +#define IRQ_Ser3UART 17 /* Ser. port 3 UART */ +#define IRQ_Ser4MCP 18 /* Ser. port 4 MCP */ +#define IRQ_Ser4SSP 19 /* Ser. port 4 SSP */ +#define IRQ_DMA0 20 /* DMA controller channel 0 */ +#define IRQ_DMA1 21 /* DMA controller channel 1 */ +#define IRQ_DMA2 22 /* DMA controller channel 2 */ +#define IRQ_DMA3 23 /* DMA controller channel 3 */ +#define IRQ_DMA4 24 /* DMA controller channel 4 */ +#define IRQ_DMA5 25 /* DMA controller channel 5 */ +#define IRQ_OST0 26 /* OS Timer match 0 */ +#define IRQ_OST1 27 /* OS Timer match 1 */ +#define IRQ_OST2 28 /* OS Timer match 2 */ +#define IRQ_OST3 29 /* OS Timer match 3 */ +#define IRQ_RTC1Hz 30 /* RTC 1 Hz clock */ +#define IRQ_RTCAlrm 31 /* RTC Alarm */ diff --git a/include/asm-arm/arch-sa1100/keyboard.h b/include/asm-arm/arch-sa1100/keyboard.h new file mode 100644 index 000000000..c03526bf5 --- /dev/null +++ b/include/asm-arm/arch-sa1100/keyboard.h @@ -0,0 +1,27 @@ +/* + * linux/include/asm-arm/arch-sa1100/keyboard.h + * + * Keyboard driver definitions for SA1100 architecture + * + * This really has to be cleaned up somehow... + * + */ + +#define KEYBOARD_IRQ + +#define NR_SCANCODES 128 + +#define kbd_setkeycode(sc,kc) (-EINVAL) +#define kbd_getkeycode(sc) (-EINVAL) +#define kbd_pretranslate(sc,kc) 1 +#define kbd_translate(sc, kcp, raw) kbd_drv_translate(sc, kcp, raw) +#define kbd_init_hw() kbd_drv_init() +#define kbd_unexpected_up + +#define kbd_leds(leds) + +#define kbd_sysrq_xlate +#define kbd_disable_irq() +#define kbd_enable_irq() + +#define SYSRQ_KEY 0x54 diff --git a/include/asm-arm/arch-sa1100/memory.h b/include/asm-arm/arch-sa1100/memory.h new file mode 100644 index 000000000..a49764467 --- /dev/null +++ b/include/asm-arm/arch-sa1100/memory.h @@ -0,0 +1,46 @@ +/* + * linux/include/asm-arm/arch-sa1100/memory.h + * + * Copyright (c) 1999 Nicolas Pitre <nico@visuaide.com> + */ + +#ifndef __ASM_ARCH_MEMORY_H +#define __ASM_ARCH_MEMORY_H + + +/* + * Task size: 3GB + */ +#define TASK_SIZE (0xc0000000UL) + +/* + * Page offset: 3GB + */ +#define PAGE_OFFSET (0xc0000000UL) + +#define __virt_to_phys__is_a_macro +#define __phys_to_virt__is_a_macro + +/* + * The following gives a maximum memory size of 128MB (32MB in each bank). + * + * Does this still need to be optimised for one bank machines? + */ +#define __virt_to_phys(x) (((x) & 0xe0ffffff) | ((x) & 0x06000000) << 2) +#define __phys_to_virt(x) (((x) & 0xe7ffffff) | ((x) & 0x30000000) >> 2) + +/* + * Virtual view <-> DMA view memory address translations + * virt_to_bus: Used to translate the virtual address to an + * address suitable to be passed to set_dma_addr + * bus_to_virt: Used to convert an address for DMA operations + * to an address that the kernel can use. + * + * On the SA1100, bus addresses are equivalent to physical addresses. + */ +#define __virt_to_bus__is_a_macro +#define __bus_to_virt__is_a_macro +#define __virt_to_bus(x) __virt_to_phys(x) +#define __bus_to_virt(x) __phys_to_virt(x) + +#endif diff --git a/include/asm-arm/arch-sa1100/param.h b/include/asm-arm/arch-sa1100/param.h new file mode 100644 index 000000000..3444dc74c --- /dev/null +++ b/include/asm-arm/arch-sa1100/param.h @@ -0,0 +1,3 @@ +/* + * linux/include/asm-arm/arch-ebsa110/param.h + */ diff --git a/include/asm-arm/arch-sa1100/processor.h b/include/asm-arm/arch-sa1100/processor.h new file mode 100644 index 000000000..1d0c5a6c3 --- /dev/null +++ b/include/asm-arm/arch-sa1100/processor.h @@ -0,0 +1,28 @@ +/* + * linux/include/asm-arm/arch-sa1100/processor.h + * + * Copyright (c) 1996 Russell King. + * + * Changelog: + * 10-09-1996 RMK Created + * 05-01-1999 HBF Mods for SA1100 + * 21-09-1999 NP SWAPPER_PG_DIR readjusted for SA1100 + */ + +#ifndef __ASM_ARCH_PROCESSOR_H +#define __ASM_ARCH_PROCESSOR_H + +/* + * Bus types + */ +#define EISA_bus 0 +#define EISA_bus__is_a_macro /* for versions in ksyms.c */ +#define MCA_bus 0 +#define MCA_bus__is_a_macro /* for versions in ksyms.c */ + +/* This decides where the kernel will search for a free chunk of vm + * space during mmap's. + */ +#define TASK_UNMAPPED_BASE (TASK_SIZE / 3) + +#endif diff --git a/include/asm-arm/arch-sa1100/serial.h b/include/asm-arm/arch-sa1100/serial.h new file mode 100644 index 000000000..ad7766ace --- /dev/null +++ b/include/asm-arm/arch-sa1100/serial.h @@ -0,0 +1,30 @@ +/* + * include/asm-arm/arch-sa1100/serial.h + * (C) 1999 Nicolas Pitre <nico@visuaide.com> + * + * All this is intended to be used with a 16550-like UART on the SA1100's + * PCMCIA bus. It has nothing to do with the SA1100's internal serial ports. + * This is included by serial.c -- serial_sa1100.c makes no use of it. + */ + + +/* + * This assumes you have a 1.8432 MHz clock for your UART. + * + * It'd be nice if someone built a serial card with a 24.576 MHz + * clock, since the 16550A is capable of handling a top speed of 1.5 + * megabits/second; but this requires the faster clock. + */ +#define BASE_BAUD ( 1843200 / 16 ) + +/* Standard COM flags */ +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST) + +#define STD_SERIAL_PORT_DEFNS \ + /* UART CLK PORT IRQ FLAGS */ \ + { 0, BASE_BAUD, 0x3F8, IRQ_GPIO3, STD_COM_FLAGS }, /* ttyS0 */ \ + { 0, BASE_BAUD, 0x2F8, IRQ_GPIO3, STD_COM_FLAGS }, /* ttyS1 */ \ + { 0, BASE_BAUD, 0x3E8, IRQ_GPIO3, STD_COM_FLAGS }, /* ttyS2 */ \ + { 0, BASE_BAUD, 0x2E8, IRQ_GPIO3, STD_COM4_FLAGS }, /* ttyS3 */ + + diff --git a/include/asm-arm/arch-sa1100/serial_reg.h b/include/asm-arm/arch-sa1100/serial_reg.h new file mode 100644 index 000000000..de675b3cd --- /dev/null +++ b/include/asm-arm/arch-sa1100/serial_reg.h @@ -0,0 +1,79 @@ +/* + * include/asm/arch/serial_reg.h + * + * Redistribution of this file is permitted under the terms of the GNU + * Public License (GPL) + * + * These are the SA1100 UART port assignments, expressed as long index + * of the base address. + */ + +#ifndef ASM_ARCH_SERIAL_REG_H +#define ASM_ARCH_SERIAL_REG_H + + +/* + * Register index. + */ +#define UTCR0 0 /* 0x00 UART_LCR Line control register */ +#define UTCR1 1 /* 0x04 UART_DLLSB */ +#define UTCR2 2 /* 0x08 UART_DLMSB */ +#define UTCR3 3 /* 0x0c UART_IER */ +#define UTDR 5 /* 0x14 UART_RX, UART_TX */ +#define UTSR0 7 /* 0x1c */ +#define UTSR1 8 /* 0x20 UART_LSR Line Status register */ + +#define UART_RX UTDR /* Receive port, read only */ +#define UART_TX UTDR /* transmit port, write only */ + +#if 0 +/* + * Line control register flags + */ +#define UTCR0_PE 1 /* Parity enable */ +#define UTCR0_EP 2 /* Even parity */ +#define UTCR0_SB 4 /* Stop bit */ +#define UTCR0_DB 8 /* Data bits in transmission (0 = 7, 1 = 8) */ + +#define UTCR0_OES UTCR0_EP +#define UTCR0_SBS UTCR0_SB +#define UTCR0_DSS UTCR0_DB +#define UTCR0_SCE 16 /* Sample clock enable */ +#define UTCR0_RCE 32 /* Receive clock edge select */ +#define UTCR0_TCE 64 /* Transmit clock edge select */ + + +/* + * Line status bits. + */ +#define UTSR1_TBY 1 /* transmitter busy flag */ +#define UTSR1_RNE 2 /* receiver not empty (LSR_DR) */ +#define UTSR1_TNF 4 /* transmit fifo non full */ +#define UTSR1_PRE 8 /* parity read error (LSR_PE) */ +#define UTSR1_FRE 16 /* framing error (LSR_FE) */ +#define UTSR1_ROR 32 /* receive fifo overrun (LSR_OE) */ + +#define UTSR1_ERROR (UTSR1_PRE | UTSR1_FRE | UTSR1_ROR) /* LSR_ERROR */ + + +#define UTSR0_TFS 1 /* transmit fifo service request */ +#define UTSR0_RFS 2 /* receive fifo service request */ +#define UTSR0_RID 4 /* receiver idle */ +#define UTSR0_RBB 8 /* receiver begin of break */ +#define UTSR0_REB 16 /* receiver end of break */ +#define UTSR0_EIF 32 /* error in fifo */ + + +/* + * Interrupt enable register (IER) + */ +#define UTCR3_RXE 1 /* Receiver enable */ +#define UTCR3_TXE 2 /* Transmit enable */ +#define UTCR3_BRK 4 /* Break */ +#define UTCR3_RIM 8 /* Receive FIFO interrupt mask (IER_RDA) */ +#define UTCR3_TIM 16 /* Transmit FIFO interrupt mask (IER_THRE) */ +#define UTCR3_LBM 32 /* Loop Back Mode */ + +#endif + +#endif /* ASM_ARCH_SERIAL_REG_H */ diff --git a/include/asm-arm/arch-sa1100/system.h b/include/asm-arm/arch-sa1100/system.h new file mode 100644 index 000000000..90dbe88f9 --- /dev/null +++ b/include/asm-arm/arch-sa1100/system.h @@ -0,0 +1,41 @@ +/* + * linux/include/asm-arm/arch-sa1100/system.h + * + * Copyright (c) 1999 Nicolas Pitre <nico@visuaide.com> + */ +#ifdef CONFIG_SA1100_VICTOR + +#define arch_reset( x ) { \ + /* switch off power supply */ \ + mdelay(2000); \ + GPCR = GPIO_GPIO23; \ + while(1); \ + } + +#else + +#define arch_reset( x ) { \ + __asm__ volatile ( \ +" mcr p15, 0, %0, c1, c0 @ MMU off\n" \ +" mov pc, #0\n" : : "r" (cpu_reset())); \ + } + +#endif + + +/* Enter SA1100 idle mode (see data sheet sec 9.5). + * It seems that the wait-on-interrupt just hang the CPU forever if it's + * on the end of a cache line. Workaround: we force an explicit alignment + * before it. + */ +#define arch_do_idle() \ + do { \ + __asm__ __volatile__( \ +" mcr p15, 0, %0, c15, c2, 2 @ Disable clock switching \n" \ +" ldr %0, [%0] @ Must perform a non-cached access \n" \ +" b 1f @ Seems we must align the next \n" \ +" .align 5 @ instruction on a cache line \n" \ +"1: mcr p15, 0, %0, c15, c8, 2 @ Wait for interrupts \n" \ +" mcr p15, 0, %0, c15, c1, 2 @ Reenable clock switching \n" \ + : : "r" (&ICIP) : "cc" ); \ + } while (0) diff --git a/include/asm-arm/arch-sa1100/timex.h b/include/asm-arm/arch-sa1100/timex.h new file mode 100644 index 000000000..82aa1a3c1 --- /dev/null +++ b/include/asm-arm/arch-sa1100/timex.h @@ -0,0 +1,13 @@ +/* + * linux/include/asm-arm/arch-sa1100/timex.h + * + * SA1100 architecture timex specifications + * + * Copyright (C) 1998 + */ + +/* + * SA1100 timer + */ +#define CLOCK_TICK_RATE 3686400 +#define CLOCK_TICK_FACTOR 80 diff --git a/include/asm-arm/assembler.h b/include/asm-arm/assembler.h index e2dae1533..9d122b4de 100644 --- a/include/asm-arm/assembler.h +++ b/include/asm-arm/assembler.h @@ -7,28 +7,5 @@ * Do not include any C declarations in this file - it is included by * assembler source. */ - -/* - * LOADREGS: multiple register load (ldm) with pc in register list - * (takes account of ARM6 not using ^) - * - * RETINSTR: return instruction: adds the 's' in at the end of the - * instruction if this is not an ARM6 - * - * SAVEIRQS: save IRQ state (not required on ARM2/ARM3 - done - * implicitly - * - * RESTOREIRQS: restore IRQ state (not required on ARM2/ARM3 - done - * implicitly with ldm ... ^ or movs. - * - * These next two need thinking about - can't easily use stack... (see system.S) - * DISABLEIRQS: disable IRQS in SVC mode - * - * ENABLEIRQS: enable IRQS in SVC mode - * - * USERMODE: switch to USER mode - * - * SVCMODE: switch to SVC mode - */ - +#include <asm/proc/ptrace.h> #include <asm/proc/assembler.h> diff --git a/include/asm-arm/atomic.h b/include/asm-arm/atomic.h index 92bcf6f8e..e19eea07c 100644 --- a/include/asm-arm/atomic.h +++ b/include/asm-arm/atomic.h @@ -27,7 +27,7 @@ typedef struct { int counter; } atomic_t; #define ATOMIC_INIT(i) { (i) } #ifdef __KERNEL__ -#include <asm/system.h> +#include <asm/proc/system.h> #define atomic_read(v) ((v)->counter) #define atomic_set(v,i) (((v)->counter) = (i)) diff --git a/include/asm-arm/cache.h b/include/asm-arm/cache.h index d8f057be1..d5bb17841 100644 --- a/include/asm-arm/cache.h +++ b/include/asm-arm/cache.h @@ -1,5 +1,5 @@ /* - * include/asm-i386/cache.h + * linux/include/asm-arm/cache.h */ #ifndef __ASMARM_CACHE_H #define __ASMARM_CACHE_H diff --git a/include/asm-arm/checksum.h b/include/asm-arm/checksum.h index 2323bb5d9..b827c490c 100644 --- a/include/asm-arm/checksum.h +++ b/include/asm-arm/checksum.h @@ -9,10 +9,6 @@ #ifndef __ASM_ARM_CHECKSUM_H #define __ASM_ARM_CHECKSUM_H -#ifndef __ASM_ARM_SEGMENT_H -#include <asm/segment.h> -#endif - /* * computes the checksum of a memory block at buff, length len, * and adds in "sum" (32-bit) @@ -64,17 +60,14 @@ csum_partial_copy(const char *src, char *dst, int len, int sum); * which always checksum on 4 octet boundaries. * * Converted and optimised for ARM by R. M. King. - * - * Note: the order that the LDM registers are loaded with respect to - * the adc's doesn't matter. */ static inline unsigned short ip_fast_csum(unsigned char * iph, unsigned int ihl) { unsigned int sum, tmp1; - __asm__ __volatile__(" - sub %2, %2, #5 + __asm__ __volatile__( + "sub %2, %2, #5 @ ip_fast_csum ldr %0, [%1], #4 ldr %3, [%1], #4 adds %0, %0, %3 @@ -94,7 +87,8 @@ ip_fast_csum(unsigned char * iph, unsigned int ihl) mov %0, %0, lsr #16 " : "=&r" (sum), "=&r" (iph), "=&r" (ihl), "=&r" (tmp1) - : "1" (iph), "2" (ihl)); + : "1" (iph), "2" (ihl) + : "cc"); return(sum); } @@ -104,25 +98,28 @@ ip_fast_csum(unsigned char * iph, unsigned int ihl) static inline unsigned int csum_fold(unsigned int sum) { - __asm__(" - adds %0, %0, %0, lsl #16 + __asm__( + "adds %0, %1, %1, lsl #16 @ csum_fold addcs %0, %0, #0x10000" : "=r" (sum) - : "0" (sum)); + : "r" (sum) + : "cc"); return (~sum) >> 16; } -static inline unsigned long +static inline unsigned int csum_tcpudp_nofold(unsigned long saddr, unsigned long daddr, unsigned short len, - unsigned short proto, unsigned int sum) + unsigned int proto, unsigned int sum) { - __asm__(" - adds %0, %0, %1 - adcs %0, %0, %2 + __asm__( + "adds %0, %1, %2 @ csum_tcpudp_nofold adcs %0, %0, %3 + adcs %0, %0, %4 + adcs %0, %0, %5 adc %0, %0, #0" : "=&r"(sum) - : "r" (daddr), "r" (saddr), "r" ((ntohs(len)<<16)+proto*256), "0" (sum)); + : "r" (sum), "r" (daddr), "r" (saddr), "r" (ntohs(len) << 16), "Ir" (proto << 8) + : "cc"); return sum; } /* @@ -131,7 +128,7 @@ csum_tcpudp_nofold(unsigned long saddr, unsigned long daddr, unsigned short len, */ static inline unsigned short int csum_tcpudp_magic(unsigned long saddr, unsigned long daddr, unsigned short len, - unsigned short proto, unsigned int sum) + unsigned int proto, unsigned int sum) { return csum_fold(csum_tcpudp_nofold(saddr, daddr, len, proto, sum)); } diff --git a/include/asm-arm/cpu-multi26.h b/include/asm-arm/cpu-multi26.h index 5f02bfc9c..c09edf8b1 100644 --- a/include/asm-arm/cpu-multi26.h +++ b/include/asm-arm/cpu-multi26.h @@ -10,40 +10,15 @@ struct task_struct; * relies on it. */ extern struct processor { - /* MISC - * get data abort address/flags - */ - void (*_data_abort)(unsigned long pc); - /* - * check for any bugs - */ + /* check for any bugs */ void (*_check_bugs)(void); - /* - * Set up any processor specifics - */ + /* Set up any processor specifics */ void (*_proc_init)(void); - /* - * Disable any processor specifics - */ + /* Disable any processor specifics */ void (*_proc_fin)(void); - /* - * Processor architecture specific - */ - /* MEMC - * - * remap memc tables - */ - void (*_remap_memc)(void *tsk); - /* - * update task's idea of mmap - */ - void (*_update_map)(void *tsk); - /* - * update task's idea after abort - */ - void (*_update_mmu_cache)(void *vma, unsigned long addr, pte_t pte); - /* XCHG - */ + /* set the MEMC hardware mappings */ + void (*_set_pgd)(pgd_t *pgd); + /* XCHG */ unsigned long (*_xchg_1)(unsigned long x, volatile void *ptr); unsigned long (*_xchg_2)(unsigned long x, volatile void *ptr); unsigned long (*_xchg_4)(unsigned long x, volatile void *ptr); @@ -53,16 +28,16 @@ extern const struct processor arm2_processor_functions; extern const struct processor arm250_processor_functions; extern const struct processor arm3_processor_functions; -#define cpu_data_abort(pc) processor._data_abort(pc) #define cpu_check_bugs() processor._check_bugs() #define cpu_proc_init() processor._proc_init() #define cpu_proc_fin() processor._proc_fin() - -#define cpu_remap_memc(tsk) processor._remap_memc(tsk) -#define cpu_update_map(tsk) processor._update_map(tsk) -#define cpu_update_mmu_cache(vma,addr,pte) processor._update_mmu_cache(vma,addr,pte) +#define cpu_do_idle() do { } while (0) +#define cpu_switch_mm(pgd,tsk) processor._set_pgd(pgd) #define cpu_xchg_1(x,ptr) processor._xchg_1(x,ptr) #define cpu_xchg_2(x,ptr) processor._xchg_2(x,ptr) #define cpu_xchg_4(x,ptr) processor._xchg_4(x,ptr) +extern void cpu_memc_update_all(pgd_t *pgd); +extern void cpu_memc_update_entry(pgd_t *pgd, unsigned long phys_pte, unsigned long log_addr); + #endif diff --git a/include/asm-arm/cpu-multi32.h b/include/asm-arm/cpu-multi32.h index ed80e894e..5b5b07921 100644 --- a/include/asm-arm/cpu-multi32.h +++ b/include/asm-arm/cpu-multi32.h @@ -88,6 +88,14 @@ extern struct processor { * purge cached data without (necessarily) writing it back */ void (*_cache_purge_area)(unsigned long start, unsigned long end); + /* + * flush a specific TLB + */ + void (*_flush_tlb_page)(unsigned long address, int flags); + /* + * Idle the processor + */ + int (*_do_idle)(void); } processor; extern const struct processor arm6_processor_functions; @@ -98,6 +106,7 @@ extern const struct processor sa110_processor_functions; #define cpu_check_bugs() processor._check_bugs() #define cpu_proc_init() processor._proc_init() #define cpu_proc_fin() processor._proc_fin() +#define cpu_do_idle() processor._do_idle() #define cpu_flush_cache_all() processor._flush_cache_all() #define cpu_flush_cache_area(start,end,flags) processor._flush_cache_area(start,end,flags) @@ -106,7 +115,8 @@ extern const struct processor sa110_processor_functions; #define cpu_flush_ram_page(page) processor._flush_ram_page(page) #define cpu_flush_tlb_all() processor._flush_tlb_all() #define cpu_flush_tlb_area(start,end,flags) processor._flush_tlb_area(start,end,flags) -#define cpu_switch_mm(pgd,tsk) processor._set_pgd(pgd) +#define cpu_flush_tlb_page(addr,flags) processor._flush_tlb_page(addr,flags) +#define cpu_set_pgd(pgd) processor._set_pgd(pgd) #define cpu_set_pmd(pmdp, pmd) processor._set_pmd(pmdp, pmd) #define cpu_set_pte(ptep, pte) processor._set_pte(ptep, pte) #define cpu_reset() processor.reset() @@ -114,4 +124,6 @@ extern const struct processor sa110_processor_functions; #define cpu_cache_wback_area(start,end) processor._cache_wback_area(start,end) #define cpu_cache_purge_area(start,end) processor._cache_purge_area(start,end) +#define cpu_switch_mm(pgd,tsk) cpu_set_pgd(__virt_to_phys((unsigned long)(pgd))) + #endif diff --git a/include/asm-arm/cpu-single.h b/include/asm-arm/cpu-single.h index 009dffb15..fcfaf7745 100644 --- a/include/asm-arm/cpu-single.h +++ b/include/asm-arm/cpu-single.h @@ -1,7 +1,11 @@ /* * Single CPU */ +#ifdef __STDC__ #define __cpu_fn(name,x) cpu_##name##x +#else +#define __cpu_fn(name,x) cpu_/**/name/**/x +#endif #define cpu_fn(name,x) __cpu_fn(name,x) /* @@ -14,6 +18,7 @@ #define cpu_check_bugs cpu_fn(CPU_NAME,_check_bugs) #define cpu_proc_init cpu_fn(CPU_NAME,_proc_init) #define cpu_proc_fin cpu_fn(CPU_NAME,_proc_fin) +#define cpu_do_idle cpu_fn(CPU_NAME,_do_idle) #define cpu_flush_cache_all cpu_fn(CPU_NAME,_flush_cache_all) #define cpu_flush_cache_area cpu_fn(CPU_NAME,_flush_cache_area) @@ -22,10 +27,11 @@ #define cpu_flush_ram_page cpu_fn(CPU_NAME,_flush_ram_page) #define cpu_flush_tlb_all cpu_fn(CPU_NAME,_flush_tlb_all) #define cpu_flush_tlb_area cpu_fn(CPU_NAME,_flush_tlb_area) -#define cpu_switch_mm cpu_fn(CPU_NAME,_set_pgd) +#define cpu_flush_tlb_page cpu_fn(CPU_NAME,_flush_tlb_page) +#define cpu_set_pgd cpu_fn(CPU_NAME,_set_pgd) #define cpu_set_pmd cpu_fn(CPU_NAME,_set_pmd) #define cpu_set_pte cpu_fn(CPU_NAME,_set_pte) -#define cpu_reset cpu_fn(CPU_NAME,reset) +#define cpu_reset cpu_fn(CPU_NAME,_reset) #define cpu_flush_icache_area cpu_fn(CPU_NAME,_flush_icache_area) #define cpu_cache_wback_area cpu_fn(CPU_NAME,_cache_wback_area) #define cpu_cache_purge_area cpu_fn(CPU_NAME,_cache_purge_area) @@ -42,6 +48,7 @@ extern void cpu_data_abort(unsigned long pc); extern void cpu_check_bugs(void); extern void cpu_proc_init(void); extern void cpu_proc_fin(void); +extern int cpu_do_idle(void); extern void cpu_flush_cache_all(void); extern void cpu_flush_cache_area(unsigned long address, unsigned long end, int flags); @@ -50,7 +57,8 @@ extern void cpu_clean_cache_area(unsigned long start, unsigned long size); extern void cpu_flush_ram_page(unsigned long page); extern void cpu_flush_tlb_all(void); extern void cpu_flush_tlb_area(unsigned long address, unsigned long end, int flags); -extern void cpu_switch_mm(unsigned long pgd_phys, struct task_struct *tsk); +extern void cpu_flush_tlb_page(unsigned long address, int flags); +extern void cpu_set_pgd(unsigned long pgd_phys); extern void cpu_set_pmd(pmd_t *pmdp, pmd_t pmd); extern void cpu_set_pte(pte_t *ptep, pte_t pte); extern unsigned long cpu_reset(void); @@ -58,4 +66,6 @@ extern void cpu_flush_icache_area(unsigned long start, unsigned long size); extern void cpu_cache_wback_area(unsigned long start, unsigned long end); extern void cpu_cache_purge_area(unsigned long start, unsigned long end); +#define cpu_switch_mm(pgd,tsk) cpu_set_pgd(__virt_to_phys((unsigned long)(pgd))) + #endif diff --git a/include/asm-arm/current.h b/include/asm-arm/current.h index 01e20e285..b2b83ebb7 100644 --- a/include/asm-arm/current.h +++ b/include/asm-arm/current.h @@ -8,12 +8,19 @@ static inline unsigned long get_sp(void) return sp; } -//static inline struct task_struct *get_current(void) __attribute__ (( __const__ )); +/* Old compilers seem to generate bad code if we allow `current' to be + non volatile. */ +#if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ > 90) +static inline struct task_struct *get_current(void) __attribute__ (( __const__ )); +#define __VOLATILE_CURRENT +#else +#define __VOLATILE_CURRENT volatile +#endif static inline struct task_struct *get_current(void) { struct task_struct *ts; - __asm__ __volatile__ ( + __asm__ __VOLATILE_CURRENT ( "bic %0, sp, #0x1f00 @ get_current bic %0, %0, #0x00ff" : "=r" (ts)); diff --git a/include/asm-arm/elf.h b/include/asm-arm/elf.h index c25a02479..a182127fd 100644 --- a/include/asm-arm/elf.h +++ b/include/asm-arm/elf.h @@ -10,8 +10,10 @@ #include <asm/procinfo.h> typedef unsigned long elf_greg_t; +typedef unsigned long elf_freg_t[3]; #define EM_ARM 40 +#define EF_ARM_APCS26 0x08 #define ELF_NGREG (sizeof (struct pt_regs) / sizeof(elf_greg_t)) typedef elf_greg_t elf_gregset_t[ELF_NGREG]; @@ -27,7 +29,11 @@ typedef struct { void *null; } elf_fpregset_t; * These are used to set parameters in the core dumps. */ #define ELF_CLASS ELFCLASS32 +#ifdef __ARMEB__ #define ELF_DATA ELFDATA2LSB; +#else +#define ELF_DATA ELFDATA2LSB; +#endif #define ELF_ARCH EM_ARM #define USE_ELF_CORE_DUMP diff --git a/include/asm-arm/hardware.h b/include/asm-arm/hardware.h index bc7854221..697976d89 100644 --- a/include/asm-arm/hardware.h +++ b/include/asm-arm/hardware.h @@ -11,16 +11,4 @@ #include <asm/arch/hardware.h> -#ifdef HAS_EXPMASK -#ifndef __ASSEMBLER__ -#define __EXPMASK(offset) (((volatile unsigned char *)EXPMASK_BASE)[offset]) -#else -#define __EXPMASK(offset) offset -#endif - -#define EXPMASK_STATUS __EXPMASK(0x00) -#define EXPMASK_ENABLE __EXPMASK(0x04) - -#endif - #endif diff --git a/include/asm-arm/init.h b/include/asm-arm/init.h deleted file mode 100644 index e364b5173..000000000 --- a/include/asm-arm/init.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef _ASMARM_INIT_H -#define _ASMARM_INIT_H - -#include <linux/config.h> - -/* C routines */ - -#ifdef CONFIG_TEXT_SECTIONS - -#define __init __attribute__ ((__section__ (".text.init"))) - -#else - -#define __init - -#endif - -#define __initdata __attribute__ ((__section__ (".data.init"))) - -/* Assembly routines */ -#define __INIT .section ".text.init",@alloc,@execinstr -#define __INITDATA .section ".data.init",@alloc,@write -#define __FINIT .previous - -#define __cacheline_aligned __attribute__ \ - ((__aligned__ (L1_CACHE_BYTES))) - -#endif diff --git a/include/asm-arm/io.h b/include/asm-arm/io.h index 5ee6dc4df..5f72c4ecd 100644 --- a/include/asm-arm/io.h +++ b/include/asm-arm/io.h @@ -25,7 +25,7 @@ extern void __iounmap(void *addr); #endif -#include <asm/hardware.h> +#include <asm/arch/hardware.h> #include <asm/arch/memory.h> #include <asm/arch/io.h> #include <asm/proc/io.h> diff --git a/include/asm-arm/ioc.h b/include/asm-arm/ioc.h index 950046048..68286872b 100644 --- a/include/asm-arm/ioc.h +++ b/include/asm-arm/ioc.h @@ -5,7 +5,7 @@ #ifndef IOC_CONTROL -#ifndef __ASSEMBLER__ +#ifndef __ASSEMBLY__ #define __IOC(offset) (IOC_BASE + (offset >> 2)) #else #define __IOC(offset) offset diff --git a/include/asm-arm/iomd.h b/include/asm-arm/iomd.h index 87299be50..299b4c5a0 100644 --- a/include/asm-arm/iomd.h +++ b/include/asm-arm/iomd.h @@ -1,6 +1,6 @@ #include <linux/config.h> -#ifndef __ASSEMBLER__ +#ifndef __ASSEMBLY__ #define __IOMD(offset) (IO_IOMD_BASE + (offset >> 2)) #else #define __IOMD(offset) offset @@ -215,7 +215,7 @@ #define VDMA_START IOMD_VIDSTART #define VDMA_END IOMD_VIDEND -#ifndef __ASSEMBLER__ +#ifndef __ASSEMBLY__ extern unsigned int vram_half_sam; #define video_set_dma(start,end,offset) \ do { \ diff --git a/include/asm-arm/memc.h b/include/asm-arm/memc.h index 8eea08635..66873dc9e 100644 --- a/include/asm-arm/memc.h +++ b/include/asm-arm/memc.h @@ -4,7 +4,7 @@ #define VDMA_START 1 #define VDMA_END 2 -#ifndef __ASSEMBLER__ +#ifndef __ASSEMBLY__ extern void memc_write(unsigned int reg, unsigned long val); #define video_set_dma(start,end,offset) \ diff --git a/include/asm-arm/mmu_context.h b/include/asm-arm/mmu_context.h index 460d7b966..4b8125cba 100644 --- a/include/asm-arm/mmu_context.h +++ b/include/asm-arm/mmu_context.h @@ -26,7 +26,7 @@ switch_mm(struct mm_struct *prev, struct mm_struct *next, struct task_struct *tsk, unsigned int cpu) { if (prev != next) { - cpu_switch_mm(__virt_to_phys((unsigned long)next->pgd), tsk); + cpu_switch_mm(next->pgd, tsk); clear_bit(cpu, &prev->cpu_vm_mask); } set_bit(cpu, &next->cpu_vm_mask); diff --git a/include/asm-arm/namei.h b/include/asm-arm/namei.h index 858f400b6..082f2c910 100644 --- a/include/asm-arm/namei.h +++ b/include/asm-arm/namei.h @@ -1,18 +1,62 @@ /* * linux/include/asm-arm/namei.h * + * Routines to handle famous /usr/gnemul + * Derived from the Sparc version of this file + * * Included from linux/fs/namei.c */ #ifndef __ASMARM_NAMEI_H #define __ASMARM_NAMEI_H -/* This dummy routine maybe changed to something useful - * for /usr/gnemul/ emulation stuff. - * Look at asm-sparc/namei.h for details. - */ +#define ARM_BSD_EMUL "usr/gnemul/bsd/" + +static inline struct dentry * +__arm_lookup_dentry(const char *name, int lookup_flags) +{ + struct dentry *base; + char *emul; + + switch (current->personality) { + case PER_BSD: + emul = ARM_BSD_EMUL; break; + default: + return NULL; + } + + base = lookup_dentry (emul, dget (current->fs->root), + (LOOKUP_FOLLOW | LOOKUP_DIRECTORY | LOOKUP_SLASHOK)); + + if (IS_ERR (base)) return NULL; + + base = lookup_dentry (name, base, lookup_flags); + + if (IS_ERR (base)) return NULL; + + if (!base->d_inode) { + struct dentry *fromroot; + + fromroot = lookup_dentry (name, dget (current->fs->root), + lookup_flags); + + if (IS_ERR (fromroot)) return base; + + if (fromroot->d_inode) { + dput(base); + return fromroot; + } + + dput(fromroot); + } + + return base; +} -#define __prefix_lookup_dentry(name, lookup_flags) \ - do {} while (0) +#define __prefix_lookup_dentry(name, lookup_flags) \ + if (current->personality) { \ + dentry = __arm_lookup_dentry (name, lookup_flags); \ + if (dentry) return dentry; \ + } #endif /* __ASMARM_NAMEI_H */ diff --git a/include/asm-arm/nwflash.h b/include/asm-arm/nwflash.h new file mode 100644 index 000000000..04e5a557a --- /dev/null +++ b/include/asm-arm/nwflash.h @@ -0,0 +1,9 @@ +#ifndef _FLASH_H +#define _FLASH_H + +#define FLASH_MINOR 160 /* MAJOR is 10 - miscdevice */ +#define CMD_WRITE_DISABLE 0 +#define CMD_WRITE_ENABLE 0x28 +#define CMD_WRITE_BASE64K_ENABLE 0x47 + +#endif /* _FLASH_H */ diff --git a/include/asm-arm/page.h b/include/asm-arm/page.h index b047806fa..977b2f619 100644 --- a/include/asm-arm/page.h +++ b/include/asm-arm/page.h @@ -1,10 +1,62 @@ #ifndef _ASMARM_PAGE_H #define _ASMARM_PAGE_H -#include <asm/arch/memory.h> #include <asm/proc/page.h> +#define PAGE_SIZE (1UL << PAGE_SHIFT) +#define PAGE_MASK (~(PAGE_SIZE-1)) + #ifdef __KERNEL__ +#ifndef __ASSEMBLY__ + +#define STRICT_MM_TYPECHECKS + +#define clear_page(page) memzero((void *)(page), PAGE_SIZE) +extern void copy_page(unsigned long to, unsigned long from); + +#ifdef STRICT_MM_TYPECHECKS +/* + * These are used to make use of C type-checking.. + */ +typedef struct { unsigned long pte; } pte_t; +typedef struct { unsigned long pmd; } pmd_t; +typedef struct { unsigned long pgd; } pgd_t; +typedef struct { unsigned long pgprot; } pgprot_t; + +#define pte_val(x) ((x).pte) +#define pmd_val(x) ((x).pmd) +#define pgd_val(x) ((x).pgd) +#define pgprot_val(x) ((x).pgprot) + +#define __pte(x) ((pte_t) { (x) } ) +#define __pmd(x) ((pmd_t) { (x) } ) +#define __pgd(x) ((pgd_t) { (x) } ) +#define __pgprot(x) ((pgprot_t) { (x) } ) + +#else +/* + * .. while these make it easier on the compiler + */ +typedef unsigned long pte_t; +typedef unsigned long pmd_t; +typedef unsigned long pgd_t; +typedef unsigned long pgprot_t; + +#define pte_val(x) (x) +#define pmd_val(x) (x) +#define pgd_val(x) (x) +#define pgprot_val(x) (x) + +#define __pte(x) (x) +#define __pmd(x) (x) +#define __pgd(x) (x) +#define __pgprot(x) (x) + +#endif +#endif /* !__ASSEMBLY__ */ + +/* to align the pointer to the (next) page boundary */ +#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) #ifndef __ASSEMBLY__ @@ -17,19 +69,14 @@ BUG(); \ } while (0) -#endif /* __ASSEMBLY__ */ - -#define get_user_page(vaddr) __get_free_page(GFP_KERNEL) -#define free_user_page(page, addr) free_page(addr) -#define clear_page(page) memzero((void *)(page), PAGE_SIZE) -#define copy_page(to,from) memcpy((void *)(to), (void *)(from), PAGE_SIZE) +#endif /* !__ASSEMBLY__ */ -#endif +#include <asm/arch/memory.h> -/* unsigned long __pa(void *x) */ #define __pa(x) __virt_to_phys((unsigned long)(x)) - -/* void *__va(unsigned long x) */ #define __va(x) ((void *)(__phys_to_virt((unsigned long)(x)))) +#define MAP_NR(addr) (((unsigned long)(addr) - PAGE_OFFSET) >> PAGE_SHIFT) + +#endif #endif diff --git a/include/asm-arm/param.h b/include/asm-arm/param.h index 0d7de8684..b0bf058ec 100644 --- a/include/asm-arm/param.h +++ b/include/asm-arm/param.h @@ -1 +1,28 @@ -#include <asm/proc/param.h> +/* + * linux/include/asm-arm/param.h + * + * Copyright (C) 1995-1999 Russell King + */ +#ifndef __ASM_PARAM_H +#define __ASM_PARAM_H + +#include <asm/arch/param.h> /* for HZ */ +#include <asm/proc/page.h> /* for EXEC_PAGE_SIZE */ + +#ifndef HZ +#define HZ 100 +#endif + +#ifndef NGROUPS +#define NGROUPS 32 +#endif + +#ifndef NOGROUP +#define NOGROUP (-1) +#endif + +/* max length of hostname */ +#define MAXHOSTNAMELEN 64 + +#endif + diff --git a/include/asm-arm/parport.h b/include/asm-arm/parport.h index c08ee4686..bcb533af4 100644 --- a/include/asm-arm/parport.h +++ b/include/asm-arm/parport.h @@ -1,13 +1,13 @@ /* - * parport.h: ia32-specific parport initialisation + * parport.h: ARM-specific parport initialisation * * Copyright (C) 1999 Tim Waugh <tim@cyberelk.demon.co.uk> * * This file should only be included by drivers/parport/parport_pc.c. */ -#ifndef _ASM_I386_PARPORT_H -#define _ASM_I386_PARPORT_H 1 +#ifndef __ASM_ARM_PARPORT_H +#define __ASM_ARM_PARPORT_H #include <linux/config.h> diff --git a/include/asm-arm/pci.h b/include/asm-arm/pci.h new file mode 100644 index 000000000..5505909d4 --- /dev/null +++ b/include/asm-arm/pci.h @@ -0,0 +1,7 @@ +#ifndef ASMARM_PCI_H +#define ASMARM_PCI_H + +#define pcibios_assign_all_busses() 0 + +#endif + diff --git a/include/asm-arm/pgtable.h b/include/asm-arm/pgtable.h index 993598dcb..1f9d7b175 100644 --- a/include/asm-arm/pgtable.h +++ b/include/asm-arm/pgtable.h @@ -1,18 +1,193 @@ #ifndef _ASMARM_PGTABLE_H #define _ASMARM_PGTABLE_H +#include <linux/config.h> + +#include <asm/arch/memory.h> /* For TASK_SIZE */ #include <asm/proc-fns.h> +#include <asm/system.h> +#include <asm/proc/cache.h> + +#define LIBRARY_TEXT_START 0x0c000000 + +#undef TEST_VERIFY_AREA + +/* + * BAD_PAGETABLE is used when we need a bogus page-table, while + * BAD_PAGE is used for a bogus page. + * + * ZERO_PAGE is a global shared page that is always zero: used + * for zero-mapped memory areas etc.. + */ +extern pte_t __bad_page(void); +extern pte_t * __bad_pagetable(void); +extern unsigned long *empty_zero_page; + +#define BAD_PAGETABLE __bad_pagetable() +#define BAD_PAGE __bad_page() +#define ZERO_PAGE(vaddr) ((unsigned long) empty_zero_page) + +/* number of bits that fit into a memory pointer */ +#define BYTES_PER_PTR (sizeof(unsigned long)) +#define BITS_PER_PTR (8*BYTES_PER_PTR) + +/* to align the pointer to a pointer address */ +#define PTR_MASK (~(sizeof(void*)-1)) + +/* sizeof(void*)==1<<SIZEOF_PTR_LOG2 */ +#define SIZEOF_PTR_LOG2 2 + +/* to find an entry in a page-table */ +#define PAGE_PTR(address) \ + ((unsigned long)(address)>>(PAGE_SHIFT-SIZEOF_PTR_LOG2)&PTR_MASK&~PAGE_MASK) + +extern void __bad_pmd(pmd_t *pmd); +extern void __bad_pmd_kernel(pmd_t *pmd); + +/* + * Page table cache stuff + */ +#ifndef CONFIG_NO_PGT_CACHE + +#ifndef __SMP__ +extern struct pgtable_cache_struct { + unsigned long *pgd_cache; + unsigned long *pte_cache; + unsigned long pgtable_cache_sz; +} quicklists; + +#define pgd_quicklist (quicklists.pgd_cache) +#define pmd_quicklist ((unsigned long *)0) +#define pte_quicklist (quicklists.pte_cache) +#define pgtable_cache_size (quicklists.pgtable_cache_sz) + +#else /* __SMP__ */ +#error Pgtable caches have to be per-CPU, so that no locking is needed. +#endif /* __SMP__ */ + +/* used for quicklists */ +#define __pgd_next(pgd) (((unsigned long *)pgd)[1]) +#define __pte_next(pte) (((unsigned long *)pte)[0]) + +extern __inline__ pgd_t *get_pgd_fast(void) +{ + unsigned long *ret; + + if((ret = pgd_quicklist) != NULL) { + pgd_quicklist = (unsigned long *)__pgd_next(ret); + ret[1] = ret[2]; + clean_cache_area(ret + 1, 4); + pgtable_cache_size--; + } + return (pgd_t *)ret; +} + +/* We don't use pmd cache, so this is a dummy routine */ +extern __inline__ pmd_t *get_pmd_fast(void) +{ + return (pmd_t *)0; +} + +extern __inline__ pte_t *get_pte_fast(void) +{ + unsigned long *ret; + + if((ret = pte_quicklist) != NULL) { + pte_quicklist = (unsigned long *)__pte_next(ret); + ret[0] = ret[1]; + clean_cache_area(ret, 4); + pgtable_cache_size--; + } + return (pte_t *)ret; +} + +extern __inline__ void free_pgd_fast(pgd_t *pgd) +{ + __pgd_next(pgd) = (unsigned long) pgd_quicklist; + pgd_quicklist = (unsigned long *) pgd; + pgtable_cache_size++; +} + +extern __inline__ void free_pmd_fast(pmd_t *pmd) +{ +} + +extern __inline__ void free_pte_fast(pte_t *pte) +{ + __pte_next(pte) = (unsigned long) pte_quicklist; + pte_quicklist = (unsigned long *) pte; + pgtable_cache_size++; +} + +#else /* CONFIG_NO_PGT_CACHE */ + +#define get_pgd_fast() (NULL) +#define get_pmd_fast() (NULL) +#define get_pte_fast() (NULL) + +#define free_pgd_fast(pgd) free_pgd_slow(pgd) +#define free_pmd_fast(pmd) free_pmd_slow(pmd) +#define free_pte_fast(pte) free_pte_slow(pte) + +#endif /* CONFIG_NO_PGT_CACHE */ + #include <asm/proc/pgtable.h> -#define module_map vmalloc -#define module_unmap vfree +extern __inline__ void set_pgdir(unsigned long address, pgd_t entry) +{ + struct task_struct * p; + + read_lock(&tasklist_lock); + for_each_task(p) { + if (!p->mm) + continue; + *pgd_offset(p->mm,address) = entry; + } + read_unlock(&tasklist_lock); + +#ifndef CONFIG_NO_PGT_CACHE + { + pgd_t *pgd; + for (pgd = (pgd_t *)pgd_quicklist; pgd; + pgd = (pgd_t *)__pgd_next(pgd)) + pgd[address >> PGDIR_SHIFT] = entry; + } +#endif +} + +extern pgd_t swapper_pg_dir[PTRS_PER_PGD]; + +#define update_mmu_cache(vma,address,pte) + +#define SWP_TYPE(entry) (((entry) >> 2) & 0x7f) +#define SWP_OFFSET(entry) ((entry) >> 9) +#define SWP_ENTRY(type,offset) (((type) << 2) | ((offset) << 9)) + +#define module_map vmalloc +#define module_unmap vfree extern int do_check_pgt_cache(int, int); +/* + * We rely on GCC optimising this code away for + * architectures which it doesn't apply to. Note + * that `addr' is checked against PAGE_OFFSET and + * end_mem by the calling code. + */ +#define __kern_valid_idx(a) (((a) - PAGE_OFFSET) >> 20) + +extern __inline__ int __kern_valid_addr(unsigned long addr) +{ + extern unsigned long *valid_addr_bitmap; + unsigned int idx = __kern_valid_idx(addr); + + return test_bit(idx, valid_addr_bitmap); +} + /* Needs to be defined here and not in linux/mm.h, as it is arch dependent */ -#define PageSkip(page) (0) -#define kern_addr_valid(addr) (1) +#define PageSkip(page) (machine_is_riscpc() && test_bit(PG_skip, &(page)->flags)) +#define kern_addr_valid(addr) (!machine_is_riscpc() || __kern_valid_addr(addr)) -#define io_remap_page_range remap_page_range +#define io_remap_page_range remap_page_range #endif /* _ASMARM_PGTABLE_H */ diff --git a/include/asm-arm/proc-armo/assembler.h b/include/asm-arm/proc-armo/assembler.h index 5b9e7c058..d611ea4dd 100644 --- a/include/asm-arm/proc-armo/assembler.h +++ b/include/asm-arm/proc-armo/assembler.h @@ -6,54 +6,30 @@ * This file contains arm architecture specific defines * for the different processors */ +#ifndef __ASSEMBLY__ +#error "Only include this from assembly code" +#endif -/* - * LOADREGS: multiple register load (ldm) with pc in register list - * (takes account of ARM6 not using ^) - * - * RETINSTR: return instruction: adds the 's' in at the end of the - * instruction if this is not an ARM6 - * - * SAVEIRQS: save IRQ state (not required on ARM2/ARM3 - done - * implicitly - * - * RESTOREIRQS: restore IRQ state (not required on ARM2/ARM3 - done - * implicitly with ldm ... ^ or movs. - * - * These next two need thinking about - can't easily use stack... (see system.S) - * DISABLEIRQS: disable IRQS in SVC mode - * - * ENABLEIRQS: enable IRQS in SVC mode - * - * USERMODE: switch to USER mode - * - * SVCMODE: switch to SVC mode - */ - -#define N_BIT (1 << 31) -#define Z_BIT (1 << 30) -#define C_BIT (1 << 29) -#define V_BIT (1 << 28) - -#define PCMASK 0xfc000003 - -#ifdef __ASSEMBLER__ - -#define I_BIT (1 << 27) -#define F_BIT (1 << 26) - -#define MODE_USR 0 -#define MODE_FIQ 1 -#define MODE_IRQ 2 -#define MODE_SVC 3 +#define MODE_USR USR26_MODE +#define MODE_FIQ FIQ26_MODE +#define MODE_IRQ IRQ26_MODE +#define MODE_SVC SVC26_MODE #define DEFAULT_FIQ MODE_FIQ +#ifdef __STDC__ #define LOADREGS(cond, base, reglist...)\ ldm##cond base,reglist^ #define RETINSTR(instr, regs...)\ instr##s regs +#else +#define LOADREGS(cond, base, reglist...)\ + ldm/**/cond base,reglist^ + +#define RETINSTR(instr, regs...)\ + instr/**/s regs +#endif #define MODENOP\ mov r0, r0 @@ -84,5 +60,3 @@ #define SVCMODE(tmpreg)\ teqp pc, $0x00000003;\ mov r0, r0 - -#endif diff --git a/include/asm-arm/proc-armo/cache.h b/include/asm-arm/proc-armo/cache.h new file mode 100644 index 000000000..d39b7b79f --- /dev/null +++ b/include/asm-arm/proc-armo/cache.h @@ -0,0 +1,67 @@ +/* + * Cache flushing... + */ +#define flush_cache_all() do { } while (0) +#define flush_cache_mm(mm) do { } while (0) +#define flush_cache_range(mm,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_range(start,end) do { } while (0) + +/* + * TLB flushing: + * + * - flush_tlb_all() flushes all processes TLBs + * - flush_tlb_mm(mm) flushes the specified mm context TLB's + * - flush_tlb_page(vma, vmaddr) flushes one page + * - flush_tlb_range(mm, start, end) flushes a range of pages + */ +#define flush_tlb_all() memc_update_all() +#define flush_tlb_mm(mm) do { } while (0) +#define flush_tlb_range(mm, start, end) do { (void)(start); (void)(end); } while (0) +#define flush_tlb_page(vma, vmaddr) do { } while (0) + +/* + * The following handle the weird MEMC chip + */ +extern __inline__ void memc_update_all(void) +{ + struct task_struct *p; + + cpu_memc_update_all(init_mm.pgd); + for_each_task(p) { + if (!p->mm) + continue; + cpu_memc_update_all(p->mm->pgd); + } + processor._set_pgd(current->active_mm->pgd); +} + +extern __inline__ void memc_update_mm(struct mm_struct *mm) +{ + cpu_memc_update_all(mm->pgd); + + if (mm == current->active_mm) + processor._set_pgd(mm->pgd); +} + +extern __inline__ void +memc_update_addr(struct mm_struct *mm, pte_t pte, unsigned long addr) +{ + cpu_memc_update_entry(mm->pgd, pte_val(pte), addr); + + if (mm == current->active_mm) + processor._set_pgd(mm->pgd); +} + +extern __inline__ void +memc_clear(struct mm_struct *mm, unsigned long phys_addr) +{ + cpu_memc_update_entry(mm->pgd, phys_addr, 0); + + if (mm == current->active_mm) + processor._set_pgd(mm->pgd); +} + +#define __flush_entry_to_ram(entry) + diff --git a/include/asm-arm/proc-armo/elf.h b/include/asm-arm/proc-armo/elf.h index 535deef31..1fc5e957e 100644 --- a/include/asm-arm/proc-armo/elf.h +++ b/include/asm-arm/proc-armo/elf.h @@ -4,14 +4,11 @@ #define ELF_EXEC_PAGESIZE 32768 -#if 0 /* not yet */ +#ifdef __KERNEL__ + +/* We can only execute 26-bit code. */ #define ELF_PROC_OK(x) \ ((x)->e_flags & EF_ARM_APCS26) -#else -#define ELF_PROC_OK(x) (1) -#endif - -#ifdef __KERNEL__ #define SET_PERSONALITY(ex,ibcs2) \ current->personality = PER_LINUX diff --git a/include/asm-arm/proc-armo/mm-init.h b/include/asm-arm/proc-armo/mm-init.h deleted file mode 100644 index ce0fd84e6..000000000 --- a/include/asm-arm/proc-armo/mm-init.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * linux/include/asm-arm/proc-armo/mm-init.h - * - * Copyright (C) 1996 Russell King - * - * This contains the code to setup the memory map on an ARM2/ARM250/ARM3 - * machine. This is both processor & architecture specific, and requires - * some more work to get it to fit into our separate processor and - * architecture structure. - */ -#include <asm/arch/memory.h> - -int page_nr; - -#define setup_processor_functions() -#define PTE_SIZE (PTRS_PER_PTE * BYTES_PER_PTR) - -static inline void setup_swapper_dir (int index, pte_t *ptep) -{ - set_pmd (pmd_offset (swapper_pg_dir + index, 0), mk_pmd (ptep)); -} - -static inline unsigned long -setup_pagetables(unsigned long start_mem, unsigned long end_mem) -{ - unsigned int i; - union { unsigned long l; pte_t *pte; } u; - - page_nr = MAP_NR(end_mem); - - /* map in pages for (0x0000 - 0x8000) */ - u.l = ((start_mem + (PTE_SIZE-1)) & ~(PTE_SIZE-1)); - start_mem = u.l + PTE_SIZE; - memzero (u.pte, PTE_SIZE); - u.pte[0] = mk_pte(PAGE_OFFSET + 491520, PAGE_READONLY); - setup_swapper_dir (0, u.pte); - - for (i = 1; i < PTRS_PER_PGD; i++) - pgd_val(swapper_pg_dir[i]) = 0; - - return start_mem; -} - -static inline void -mark_usable_memory_areas(unsigned long *start_mem, unsigned long end_mem) -{ - unsigned long smem; - - *start_mem = smem = PAGE_ALIGN(*start_mem); - - while (smem < end_mem) { - clear_bit(PG_reserved, &mem_map[MAP_NR(smem)].flags); - smem += PAGE_SIZE; - } -} diff --git a/include/asm-arm/proc-armo/page.h b/include/asm-arm/proc-armo/page.h index 3768284a2..dd3331994 100644 --- a/include/asm-arm/proc-armo/page.h +++ b/include/asm-arm/proc-armo/page.h @@ -3,74 +3,18 @@ * * Copyright (C) 1995, 1996 Russell King */ - #ifndef __ASM_PROC_PAGE_H #define __ASM_PROC_PAGE_H #include <linux/config.h> /* PAGE_SHIFT determines the page size. This is configurable. */ -#if defined(CONFIG_PAGESIZE_8) -#define PAGE_SHIFT 13 /* 8K */ -#elif defined(CONFIG_PAGESIZE_16) +#if defined(CONFIG_PAGESIZE_16) #define PAGE_SHIFT 14 /* 16K */ #else /* default */ #define PAGE_SHIFT 15 /* 32K */ #endif -#define PAGE_SIZE (1UL << PAGE_SHIFT) -#define PAGE_MASK (~(PAGE_SIZE-1)) - -#ifdef __KERNEL__ - -#define STRICT_MM_TYPECHECKS - -#ifdef STRICT_MM_TYPECHECKS -/* - * These are used to make use of C type-checking.. - */ -typedef struct { unsigned long pte; } pte_t; -typedef struct { unsigned long pmd; } pmd_t; -typedef struct { unsigned long pgd; } pgd_t; -typedef struct { unsigned long pgprot; } pgprot_t; - -#define pte_val(x) ((x).pte) -#define pmd_val(x) ((x).pmd) -#define pgd_val(x) ((x).pgd) -#define pgprot_val(x) ((x).pgprot) - -#define __pte(x) ((pte_t) { (x) } ) -#define __pmd(x) ((pmd_t) { (x) } ) -#define __pgd(x) ((pgd_t) { (x) } ) -#define __pgprot(x) ((pgprot_t) { (x) } ) - -#else -/* - * .. while these make it easier on the compiler - */ -typedef unsigned long pte_t; -typedef unsigned long pmd_t; -typedef unsigned long pgd_t; -typedef unsigned long pgprot_t; - -#define pte_val(x) (x) -#define pmd_val(x) (x) -#define pgd_val(x) (x) -#define pgprot_val(x) (x) - -#define __pte(x) (x) -#define __pmd(x) (x) -#define __pgd(x) (x) -#define __pgprot(x) (x) - -#endif - -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) -/* This handles the memory map.. */ -#define MAP_NR(addr) (((unsigned long)(addr) - PAGE_OFFSET) >> PAGE_SHIFT) - -#endif /* __KERNEL__ */ +#define EXEC_PAGESIZE 32768 #endif /* __ASM_PROC_PAGE_H */ - diff --git a/include/asm-arm/proc-armo/param.h b/include/asm-arm/proc-armo/param.h deleted file mode 100644 index 9ffb72f18..000000000 --- a/include/asm-arm/proc-armo/param.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * linux/include/asm-arm/proc-armo/param.h - * - * Copyright (C) 1995, 1996 Russell King - */ - -#ifndef __ASM_PROC_PARAM_H -#define __ASM_PROC_PARAM_H - -#ifndef HZ -#define HZ 100 -#define HZ_TO_STD(a) (a) -#endif - -#define EXEC_PAGESIZE 32768 - -#ifndef NGROUPS -#define NGROUPS 32 -#endif - -#ifndef NOGROUP -#define NOGROUP (-1) -#endif - -#define MAXHOSTNAMELEN 64 /* max length of hostname */ - -#endif - diff --git a/include/asm-arm/proc-armo/pgtable.h b/include/asm-arm/proc-armo/pgtable.h index 4a7b8c6f0..5a0b4e833 100644 --- a/include/asm-arm/proc-armo/pgtable.h +++ b/include/asm-arm/proc-armo/pgtable.h @@ -1,105 +1,25 @@ /* * linux/include/asm-arm/proc-armo/pgtable.h * - * Copyright (C) 1995, 1996 Russell King - * Modified 18/19-Oct-1997 for two-level page table + * Copyright (C) 1995-1999 Russell King + * + * 18-Oct-1997 RMK Now two-level (32x32) */ #ifndef __ASM_PROC_PGTABLE_H #define __ASM_PROC_PGTABLE_H -#include <linux/config.h> -#include <linux/slab.h> -#include <asm/arch/memory.h> /* For TASK_SIZE */ - -#define LIBRARY_TEXT_START 0x0c000000 - -/* - * Cache flushing... - */ -#define flush_cache_all() do { } while (0) -#define flush_cache_mm(mm) do { } while (0) -#define flush_cache_range(mm,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_range(start,end) do { } while (0) - -/* - * TLB flushing: - * - * - flush_tlb() flushes the current mm struct TLBs - * - flush_tlb_all() flushes all processes TLBs - * - flush_tlb_mm(mm) flushes the specified mm context TLB's - * - flush_tlb_page(vma, vmaddr) flushes one page - * - flush_tlb_range(mm, start, end) flushes a range of pages - */ -#define flush_tlb() do { } while (0) -#define flush_tlb_all() do { } while (0) -#define flush_tlb_mm(mm) do { } while (0) -#define flush_tlb_range(mm, start, end) do { } while (0) -#define flush_tlb_page(vma, vmaddr) do { } while (0) +#include <asm/proc/domain.h> /* - * We have a mem map cache... + * PMD_SHIFT determines the size of the area a second-level page table can map */ -extern __inline__ void update_memc_all(void) -{ - struct task_struct *p; - - p = &init_task; - do { - processor.u.armv2._update_map(p); - p = p->next_task; - } while (p != &init_task); - - processor.u.armv2._remap_memc (current); -} - -extern __inline__ void update_memc_task(struct task_struct *tsk) -{ - processor.u.armv2._update_map(tsk); - - if (tsk == current) - processor.u.armv2._remap_memc (tsk); -} - -extern __inline__ void update_memc_mm(struct mm_struct *mm) -{ - struct task_struct *p; - - p = &init_task; - do { - if (p->mm == mm) - processor.u.armv2._update_map(p); - p = p->next_task; - } while (p != &init_task); - - if (current->mm == mm) - processor.u.armv2._remap_memc (current); -} - -extern __inline__ void update_memc_addr(struct mm_struct *mm, unsigned long addr, pte_t pte) -{ - struct task_struct *p; - - p = &init_task; - do { - if (p->mm == mm) - processor.u.armv2._update_mmu_cache(p, addr, pte); - p = p->next_task; - } while (p != &init_task); - - if (current->mm == mm) - processor.u.armv2._remap_memc (current); -} - -#define __flush_entry_to_ram(entry) - -/* PMD_SHIFT determines the size of the area a second-level page table can map */ #define PMD_SHIFT 20 #define PMD_SIZE (1UL << PMD_SHIFT) #define PMD_MASK (~(PMD_SIZE-1)) -/* PGDIR_SHIFT determines what a third-level page table entry can map */ +/* + * PGDIR_SHIFT determines what a third-level page table entry can map + */ #define PGDIR_SHIFT 20 #define PGDIR_SIZE (1UL << PGDIR_SHIFT) #define PGDIR_MASK (~(PGDIR_SIZE-1)) @@ -107,8 +27,6 @@ extern __inline__ void update_memc_addr(struct mm_struct *mm, unsigned long addr /* * entries per page directory level: the arm3 is one-level, so * we don't really have any PMD or PTE directory physically. - * - * 18-Oct-1997 RMK Now two-level (32x32) */ #define PTRS_PER_PTE 32 #define PTRS_PER_PMD 1 @@ -119,11 +37,11 @@ extern __inline__ void update_memc_addr(struct mm_struct *mm, unsigned long addr #define VMALLOC_VMADDR(x) ((unsigned long)(x)) #define VMALLOC_END 0x01c00000 -#define _PAGE_PRESENT 0x01 -#define _PAGE_READONLY 0x02 -#define _PAGE_NOT_USER 0x04 -#define _PAGE_OLD 0x08 -#define _PAGE_CLEAN 0x10 +#define _PAGE_PRESENT 0x01 +#define _PAGE_READONLY 0x02 +#define _PAGE_NOT_USER 0x04 +#define _PAGE_OLD 0x08 +#define _PAGE_CLEAN 0x10 #define _PAGE_TABLE (_PAGE_PRESENT) #define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_OLD | _PAGE_CLEAN) @@ -157,47 +75,6 @@ extern __inline__ void update_memc_addr(struct mm_struct *mm, unsigned long addr #define __S110 PAGE_SHARED #define __S111 PAGE_SHARED -#undef TEST_VERIFY_AREA - -extern unsigned long *empty_zero_page; - -/* - * BAD_PAGETABLE is used when we need a bogus page-table, while - * BAD_PAGE is used for a bogus page. - * - * ZERO_PAGE is a global shared page that is always zero: used - * for zero-mapped memory areas etc.. - */ -extern pte_t __bad_page(void); -extern pte_t *__bad_pagetable(void); - -#define BAD_PAGETABLE __bad_pagetable() -#define BAD_PAGE __bad_page() -#define ZERO_PAGE(vaddr) ((unsigned long) empty_zero_page) - -/* number of bits that fit into a memory pointer */ -#define BYTES_PER_PTR (sizeof(unsigned long)) -#define BITS_PER_PTR (8*BYTES_PER_PTR) - -/* to align the pointer to a pointer address */ -#define PTR_MASK (~(sizeof(void*)-1)) - -/* sizeof(void*)==1<<SIZEOF_PTR_LOG2 */ -#define SIZEOF_PTR_LOG2 2 - -/* to find an entry in a page-table */ -#define PAGE_PTR(address) \ -((unsigned long)(address)>>(PAGE_SHIFT-SIZEOF_PTR_LOG2)&PTR_MASK&~PAGE_MASK) - -/* to set the page-dir */ -#define SET_PAGE_DIR(tsk,pgdir) \ -do { \ - tsk->tss.memmap = (unsigned long)pgdir; \ - processor.u.armv2._update_map(tsk); \ - if ((tsk) == current) \ - processor.u.armv2._remap_memc (current); \ -} while (0) - extern unsigned long physical_start; extern unsigned long physical_end; @@ -229,7 +106,6 @@ extern inline int pte_write(pte_t pte) { return !(pte_val(pte) & _PAGE_ extern inline int pte_exec(pte_t pte) { return !(pte_val(pte) & _PAGE_NOT_USER); } extern inline int pte_dirty(pte_t pte) { return !(pte_val(pte) & _PAGE_CLEAN); } extern inline int pte_young(pte_t pte) { return !(pte_val(pte) & _PAGE_OLD); } -#define pte_cacheable(pte) 1 extern inline pte_t pte_nocache(pte_t pte) { return pte; } extern inline pte_t pte_wrprotect(pte_t pte) { pte_val(pte) |= _PAGE_READONLY; return pte; } @@ -322,128 +198,45 @@ extern __inline__ pte_t * pte_offset(pmd_t *dir, unsigned long address) * if any. */ -#ifndef __SMP__ -#ifndef CONFIG_NO_PGT_CACHE -extern struct pgtable_cache_struct { - unsigned long *pgd_cache; - unsigned long *pte_cache; - unsigned long pgtable_cache_sz; -} quicklists; - -#define pmd_quicklist ((unsigned long *)0) -#define pte_quicklist (quicklists.pte_cache) -#define pgd_quicklist (quicklists.pgd_cache) -#define pgtable_cache_size (quicklists.pgtable_cache_sz) -#endif - -#else -#error Pgtable caches have to be per-CPU, so that no locking is needed. -#endif - -extern pgd_t *get_pgd_slow(void); extern void free_table(void *table); -#ifndef CONFIG_NO_PGT_CACHE -extern __inline__ pgd_t *get_pgd_fast(void) -{ - unsigned long *ret; - - if((ret = pgd_quicklist) != NULL) { - pgd_quicklist = (unsigned long *)(*ret); - ret[0] = ret[1]; - pgtable_cache_size--; - } else - ret = (unsigned long *)get_pgd_slow(); - return (pgd_t *)ret; -} - -extern __inline__ void free_pgd_fast(pgd_t *pgd) -{ - *(unsigned long *)pgd = (unsigned long) pgd_quicklist; - pgd_quicklist = (unsigned long *) pgd; - pgtable_cache_size++; -} -#endif - /* keep this as an inline so we get type checking */ extern __inline__ void free_pgd_slow(pgd_t *pgd) { - free_table((void *)pgd); -} - -extern pte_t *get_pte_slow(pmd_t *pmd, unsigned long address_preadjusted); - -#ifndef CONFIG_NO_PGT_CACHE -extern __inline__ pte_t *get_pte_fast(void) -{ - unsigned long *ret; - - if((ret = (unsigned long *)pte_quicklist) != NULL) { - pte_quicklist = (unsigned long *)(*ret); - ret[0] = ret[1]; - pgtable_cache_size--; - } - return (pte_t *)ret; + free_table(pgd); } -extern __inline__ void free_pte_fast(pte_t *pte) -{ - *(unsigned long *)pte = (unsigned long) pte_quicklist; - pte_quicklist = (unsigned long *) pte; - pgtable_cache_size++; -} -#endif - /* keep this as an inline so we get type checking */ extern __inline__ void free_pte_slow(pte_t *pte) { - free_table((void *)pte); -} - -/* We don't use pmd cache, so this is a dummy routine */ -extern __inline__ pmd_t *get_pmd_fast(void) -{ - return (pmd_t *)0; -} - -extern __inline__ void free_pmd_fast(pmd_t *pmd) -{ + free_table(pte); } extern __inline__ void free_pmd_slow(pmd_t *pmd) { } -extern void __bad_pmd(pmd_t *pmd); -extern void __bad_pmd_kernel(pmd_t *pmd); - -#ifdef CONFIG_NO_PGT_CACHE -#define pte_free_kernel(pte) free_pte_slow(pte) -#define pte_free(pte) free_pte_slow(pte) -#define pgd_free(pgd) free_pgd_slow(pgd) -#define pgd_alloc() get_pgd_slow() +#define pgd_free(pgd) free_pgd_fast(pgd) -extern __inline__ pte_t *pte_alloc(pmd_t * pmd, unsigned long address) +extern __inline__ pgd_t *pgd_alloc(void) { - address = (address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1); + extern pgd_t *get_pgd_slow(void); + pgd_t *pgd; - if (pmd_none (*pmd)) { - return get_pte_slow(pmd, address); - } - if (pmd_bad (*pmd)) { - __bad_pmd(pmd); - return NULL; - } - return (pte_t *) pmd_page(*pmd) + address; + pgd = get_pgd_fast(); + if (!pgd) + pgd = get_pgd_slow(); + + return pgd; } -#else + #define pte_free_kernel(pte) free_pte_fast(pte) #define pte_free(pte) free_pte_fast(pte) -#define pgd_free(pgd) free_pgd_fast(pgd) -#define pgd_alloc() get_pgd_fast() extern __inline__ pte_t *pte_alloc(pmd_t * pmd, unsigned long address) { + extern pte_t *get_pte_slow(pmd_t *pmd, unsigned long address_preadjusted); + address = (address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1); if (pmd_none (*pmd)) { @@ -460,7 +253,6 @@ extern __inline__ pte_t *pte_alloc(pmd_t * pmd, unsigned long address) } return (pte_t *) pmd_page(*pmd) + address; } -#endif /* * allocating and freeing a pmd is trivial: the 1-entry pmd is @@ -479,34 +271,4 @@ extern __inline__ pmd_t *pmd_alloc(pgd_t *pgd, unsigned long address) #define pmd_alloc_kernel pmd_alloc #define pte_alloc_kernel pte_alloc -extern __inline__ void set_pgdir(unsigned long address, pgd_t entry) -{ - struct task_struct * p; - - read_lock(&tasklist_lock); - for_each_task(p) { - if (!p->mm) - continue; - *pgd_offset(p->mm,address) = entry; - } - read_unlock(&tasklist_lock); -#ifndef CONFIG_NO_PGT_CACHE - { - pgd_t *pgd; - for (pgd = (pgd_t *)pgd_quicklist; pgd; - pgd = (pgd_t *)*(unsigned long *)pgd) - pgd[address >> PGDIR_SHIFT] = entry; - } -#endif -} - -extern pgd_t swapper_pg_dir[PTRS_PER_PGD]; - -#define update_mmu_cache(vma,address,pte) - -#define SWP_TYPE(entry) (((entry) >> 1) & 0x7f) -#define SWP_OFFSET(entry) ((entry) >> 8) -#define SWP_ENTRY(type,offset) (((type) << 1) | ((offset) << 8)) - -#endif /* __ASM_PROC_PAGE_H */ - +#endif /* __ASM_PROC_PGTABLE_H */ diff --git a/include/asm-arm/proc-armo/processor.h b/include/asm-arm/proc-armo/processor.h index 087dcbcd0..049e2c054 100644 --- a/include/asm-arm/proc-armo/processor.h +++ b/include/asm-arm/proc-armo/processor.h @@ -15,7 +15,6 @@ #ifndef __ASM_PROC_PROCESSOR_H #define __ASM_PROC_PROCESSOR_H -#include <asm/assembler.h> #include <linux/string.h> #define KERNEL_STACK_SIZE 4096 @@ -45,20 +44,16 @@ typedef struct { unsigned long (*copy_to_user)(void *to, const void *from, unsigned long sz); unsigned long (*clear_user)(void *addr, unsigned long sz); unsigned long (*strncpy_from_user)(char *to, const char *from, unsigned long sz); - unsigned long (*strlen_user)(const char *s); + unsigned long (*strnlen_user)(const char *s, long n); } uaccess_t; extern uaccess_t uaccess_user, uaccess_kernel; #define EXTRA_THREAD_STRUCT \ - uaccess_t *uaccess; /* User access functions*/ \ - unsigned long memcmap[256]; + uaccess_t *uaccess; /* User access functions*/ #define EXTRA_THREAD_STRUCT_INIT \ - ,&uaccess_kernel, \ - { 0, } - -#define SWAPPER_PG_DIR ((unsigned long)swapper_pg_dir) + ,&uaccess_kernel #define start_thread(regs,pc,sp) \ ({ \ diff --git a/include/asm-arm/proc-armo/ptrace.h b/include/asm-arm/proc-armo/ptrace.h index 513501b7d..9bf506501 100644 --- a/include/asm-arm/proc-armo/ptrace.h +++ b/include/asm-arm/proc-armo/ptrace.h @@ -1,12 +1,26 @@ /* * linux/include/asm-arm/proc-armo/ptrace.h * - * Copyright (C) 1996 Russell King + * Copyright (C) 1996-1999 Russell King */ - #ifndef __ASM_PROC_PTRACE_H #define __ASM_PROC_PTRACE_H +#define USR26_MODE 0x00 +#define FIQ26_MODE 0x01 +#define IRQ26_MODE 0x02 +#define SVC26_MODE 0x03 +#define MODE_MASK 0x03 +#define F_BIT (1 << 26) +#define I_BIT (1 << 27) +#define CC_V_BIT (1 << 28) +#define CC_C_BIT (1 << 29) +#define CC_Z_BIT (1 << 30) +#define CC_N_BIT (1 << 31) +#define PCMASK 0xfc000003 + +#ifndef __ASSEMBLY__ + /* this struct defines the way the registers are stored on the stack during a system call. */ @@ -30,19 +44,7 @@ struct pt_regs { #define ARM_r2 uregs[2] #define ARM_r1 uregs[1] #define ARM_r0 uregs[0] -#define ARM_ORIG_r0 uregs[16] /* -1 */ - -#define USR26_MODE 0x00 -#define FIQ26_MODE 0x01 -#define IRQ26_MODE 0x02 -#define SVC26_MODE 0x03 -#define MODE_MASK 0x03 -#define F_BIT (1 << 26) -#define I_BIT (1 << 27) -#define CC_V_BIT (1 << 28) -#define CC_C_BIT (1 << 29) -#define CC_Z_BIT (1 << 30) -#define CC_N_BIT (1 << 31) +#define ARM_ORIG_r0 uregs[16] #ifdef __KERNEL__ @@ -61,12 +63,6 @@ struct pt_regs { #define condition_codes(regs) \ ((regs)->ARM_pc & (CC_V_BIT|CC_C_BIT|CC_Z_BIT|CC_N_BIT)) -#define pc_pointer(v) \ - ((v) & 0x03fffffc) - -#define instruction_pointer(regs) \ - (pc_pointer((regs)->ARM_pc)) - /* Are the current registers suitable for user mode? * (used to maintain security in signal handlers) */ @@ -86,5 +82,7 @@ static inline int valid_user_regs(struct pt_regs *regs) #endif /* __KERNEL__ */ +#endif /* __ASSEMBLY__ */ + #endif diff --git a/include/asm-arm/proc-armo/system.h b/include/asm-arm/proc-armo/system.h index 3d6ba7554..1cc3fc606 100644 --- a/include/asm-arm/proc-armo/system.h +++ b/include/asm-arm/proc-armo/system.h @@ -13,6 +13,8 @@ extern const char xchg_str[]; extern __inline__ unsigned long __xchg(unsigned long x, volatile void *ptr, int size) { + extern void arm_invalidptr(const char *, int); + switch (size) { case 1: return cpu_xchg_1(x, ptr); case 2: return cpu_xchg_2(x, ptr); @@ -29,11 +31,6 @@ extern __inline__ unsigned long __xchg(unsigned long x, volatile void *ptr, int #define proc_hard_reset() cpu_proc_fin() /* - * This processor does not idle - */ -#define proc_idle() - -/* * A couple of speedups for the ARM */ diff --git a/include/asm-arm/proc-armo/uaccess.h b/include/asm-arm/proc-armo/uaccess.h index 1a6b950ed..7e1a62c5e 100644 --- a/include/asm-arm/proc-armo/uaccess.h +++ b/include/asm-arm/proc-armo/uaccess.h @@ -19,16 +19,12 @@ #define KERNEL_DS 0x03000000 #define USER_DS 0x02000000 -#define get_ds() (KERNEL_DS) -#define get_fs() (current->addr_limit) -#define segment_eq(a,b) ((a) == (b)) - extern uaccess_t uaccess_user, uaccess_kernel; extern __inline__ void set_fs (mm_segment_t fs) { current->addr_limit = fs; - current->tss.uaccess = fs == USER_DS ? &uaccess_user : &uaccess_kernel; + current->thread.uaccess = fs == USER_DS ? &uaccess_user : &uaccess_kernel; } #define __range_ok(addr,size) ({ \ @@ -47,8 +43,6 @@ extern __inline__ void set_fs (mm_segment_t fs) : "cc"); \ (flag == 0); }) -#define access_ok(type,addr,size) (__range_ok(addr,size) == 0) - #define __put_user_asm_byte(x,addr,err) \ __asm__ __volatile__( \ " mov r0, %1\n" \ @@ -58,7 +52,7 @@ extern __inline__ void set_fs (mm_segment_t fs) " mov pc, %3\n" \ " mov %0, r2\n" \ : "=r" (err) \ - : "r" (x), "r" (addr), "r" (current->tss.uaccess->put_byte), \ + : "r" (x), "r" (addr), "r" (current->thread.uaccess->put_byte), \ "0" (err) \ : "r0", "r1", "r2", "lr") @@ -71,7 +65,7 @@ extern __inline__ void set_fs (mm_segment_t fs) " mov pc, %3\n" \ " mov %0, r2\n" \ : "=r" (err) \ - : "r" (x), "r" (addr), "r" (current->tss.uaccess->put_half), \ + : "r" (x), "r" (addr), "r" (current->thread.uaccess->put_half), \ "0" (err) \ : "r0", "r1", "r2", "lr") @@ -84,7 +78,7 @@ extern __inline__ void set_fs (mm_segment_t fs) " mov pc, %3\n" \ " mov %0, r2\n" \ : "=r" (err) \ - : "r" (x), "r" (addr), "r" (current->tss.uaccess->put_word), \ + : "r" (x), "r" (addr), "r" (current->thread.uaccess->put_word), \ "0" (err) \ : "r0", "r1", "r2", "lr") @@ -97,7 +91,7 @@ extern __inline__ void set_fs (mm_segment_t fs) " mov %0, r1\n" \ " mov %1, r0\n" \ : "=r" (err), "=r" (x) \ - : "r" (addr), "r" (current->tss.uaccess->get_byte), "0" (err) \ + : "r" (addr), "r" (current->thread.uaccess->get_byte), "0" (err) \ : "r0", "r1", "r2", "lr") #define __get_user_asm_half(x,addr,err) \ @@ -109,7 +103,7 @@ extern __inline__ void set_fs (mm_segment_t fs) " mov %0, r1\n" \ " mov %1, r0\n" \ : "=r" (err), "=r" (x) \ - : "r" (addr), "r" (current->tss.uaccess->get_half), "0" (err) \ + : "r" (addr), "r" (current->thread.uaccess->get_half), "0" (err) \ : "r0", "r1", "r2", "lr") #define __get_user_asm_word(x,addr,err) \ @@ -121,20 +115,20 @@ extern __inline__ void set_fs (mm_segment_t fs) " mov %0, r1\n" \ " mov %1, r0\n" \ : "=r" (err), "=r" (x) \ - : "r" (addr), "r" (current->tss.uaccess->get_word), "0" (err) \ + : "r" (addr), "r" (current->thread.uaccess->get_word), "0" (err) \ : "r0", "r1", "r2", "lr") #define __do_copy_from_user(to,from,n) \ - (n) = current->tss.uaccess->copy_from_user((to),(from),(n)) + (n) = current->thread.uaccess->copy_from_user((to),(from),(n)) #define __do_copy_to_user(to,from,n) \ - (n) = current->tss.uaccess->copy_to_user((to),(from),(n)) + (n) = current->thread.uaccess->copy_to_user((to),(from),(n)) #define __do_clear_user(addr,sz) \ - (sz) = current->tss.uaccess->clear_user((addr),(sz)) + (sz) = current->thread.uaccess->clear_user((addr),(sz)) #define __do_strncpy_from_user(dst,src,count,res) \ - (res) = current->tss.uaccess->strncpy_from_user(dst,src,count) + (res) = current->thread.uaccess->strncpy_from_user(dst,src,count) -#define __do_strlen_user(s,res) \ - (res) = current->tss.uaccess->strlen_user(s) +#define __do_strnlen_user(s,n,res) \ + (res) = current->thread.uaccess->strnlen_user(s,n) diff --git a/include/asm-arm/proc-armv/assembler.h b/include/asm-arm/proc-armv/assembler.h index 2294981c3..128dd1982 100644 --- a/include/asm-arm/proc-armv/assembler.h +++ b/include/asm-arm/proc-armv/assembler.h @@ -6,79 +6,81 @@ * This file contains arm architecture specific defines * for the different processors */ +#ifndef __ASSEMBLY__ +#error "Only include this from assembly code" +#endif -/* - * LOADREGS: multiple register load (ldm) with pc in register list - * (takes account of ARM6 not using ^) - * - * RETINSTR: return instruction: adds the 's' in at the end of the - * instruction if this is not an ARM6 - * - * SAVEIRQS: save IRQ state (not required on ARM2/ARM3 - done - * implicitly - * - * RESTOREIRQS: restore IRQ state (not required on ARM2/ARM3 - done - * implicitly with ldm ... ^ or movs. - * - * These next two need thinking about - can't easily use stack... (see system.S) - * DISABLEIRQS: disable IRQS in SVC mode - * - * ENABLEIRQS: enable IRQS in SVC mode - * - * USERMODE: switch to USER mode - * - * SVCMODE: switch to SVC mode - */ - -#define N_BIT (1 << 31) -#define Z_BIT (1 << 30) -#define C_BIT (1 << 29) -#define V_BIT (1 << 28) - -#define PCMASK 0 - -#ifdef __ASSEMBLER__ - -#define I_BIT (1 << 7) -#define F_BIT (1 << 6) - -#define MODE_FIQ26 0x01 -#define MODE_FIQ32 0x11 +#define MODE_USR USR_MODE +#define MODE_FIQ FIQ_MODE +#define MODE_IRQ IRQ_MODE +#define MODE_SVC SVC_MODE -#define DEFAULT_FIQ MODE_FIQ32 +#define DEFAULT_FIQ MODE_FIQ +/* + * LOADREGS - ldm with PC in register list (eg, ldmfd sp!, {pc}) + * RETINSTR - return instruction (eg, mov pc, lr) + */ +#ifdef __STDC__ #define LOADREGS(cond, base, reglist...)\ ldm##cond base,reglist #define RETINSTR(instr, regs...)\ instr regs +#else +#define LOADREGS(cond, base, reglist...)\ + ldm/**/cond base,reglist +#define RETINSTR(instr, regs...)\ + instr regs +#endif + +/* + * No nop required after mode change + */ #define MODENOP +/* + * Change to `mode' + */ #define MODE(savereg,tmpreg,mode) \ mrs savereg, cpsr; \ bic tmpreg, savereg, $0x1f; \ orr tmpreg, tmpreg, $mode; \ msr cpsr, tmpreg +/* + * Restore mode + */ #define RESTOREMODE(savereg) \ msr cpsr, savereg - + +/* + * save interrupt state (uses stack) + */ #define SAVEIRQS(tmpreg)\ mrs tmpreg, cpsr; \ str tmpreg, [sp, $-4]! +/* + * restore interrupt state (uses stack) + */ #define RESTOREIRQS(tmpreg)\ ldr tmpreg, [sp], $4; \ msr cpsr, tmpreg +/* + * disable IRQs + */ #define DISABLEIRQS(tmpreg)\ mrs tmpreg , cpsr; \ orr tmpreg , tmpreg , $I_BIT; \ msr cpsr, tmpreg +/* + * enable IRQs + */ #define ENABLEIRQS(tmpreg)\ mrs tmpreg , cpsr; \ bic tmpreg , tmpreg , $I_BIT; \ msr cpsr, tmpreg -#endif diff --git a/include/asm-arm/proc-armv/cache.h b/include/asm-arm/proc-armv/cache.h new file mode 100644 index 000000000..9b79c7747 --- /dev/null +++ b/include/asm-arm/proc-armv/cache.h @@ -0,0 +1,93 @@ +/* + * Cache flushing... + */ +#define flush_cache_all() \ + cpu_flush_cache_all() + +#define flush_cache_mm(_mm) \ + do { \ + if ((_mm) == current->mm) \ + cpu_flush_cache_all(); \ + } while (0) + +#define flush_cache_range(_mm,_start,_end) \ + do { \ + if ((_mm) == current->mm) \ + cpu_flush_cache_area((_start), (_end), 1); \ + } while (0) + +#define flush_cache_page(_vma,_vmaddr) \ + do { \ + if ((_vma)->vm_mm == current->mm) \ + cpu_flush_cache_area((_vmaddr), \ + (_vmaddr) + PAGE_SIZE, \ + ((_vma)->vm_flags & VM_EXEC)); \ + } while (0) + +#define clean_cache_range(_start,_end) \ + do { \ + unsigned long _s, _sz; \ + _s = (unsigned long)_start; \ + _sz = (unsigned long)_end - _s; \ + cpu_clean_cache_area(_s, _sz); \ + } while (0) + +#define clean_cache_area(_start,_size) \ + do { \ + unsigned long _s; \ + _s = (unsigned long)_start; \ + cpu_clean_cache_area(_s, _size); \ + } while (0) + +#define flush_icache_range(_start,_end) \ + cpu_flush_icache_area((_start), (_end) - (_start)) + +/* + * We don't have a MEMC chip... + */ +#define memc_update_all() do { } while (0) +#define memc_update_mm(mm) do { } while (0) +#define memc_update_addr(mm,pte,log) do { } while (0) +#define memc_clear(mm,physaddr) do { } while (0) + +/* + * This flushes back any buffered write data. We have to clean the entries + * in the cache for this page. This does not invalidate either I or D caches. + */ +#define flush_page_to_ram(_page) \ + cpu_flush_ram_page((_page) & PAGE_MASK); + +/* + * TLB flushing: + * + * - flush_tlb_all() flushes all processes TLBs + * - flush_tlb_mm(mm) flushes the specified mm context TLB's + * - flush_tlb_page(vma, vmaddr) flushes one page + * - flush_tlb_range(mm, start, end) flushes a range of pages + * + * We drain the write buffer in here to ensure that the page tables in ram + * are really up to date. It is more efficient to do this here... + */ +#define flush_tlb_all() \ + cpu_flush_tlb_all() + +#define flush_tlb_mm(_mm) \ + do { \ + if ((_mm) == current->mm) \ + cpu_flush_tlb_all(); \ + } while (0) + +#define flush_tlb_range(_mm,_start,_end) \ + do { \ + if ((_mm) == current->mm) \ + cpu_flush_tlb_area((_start), (_end), 1); \ + } while (0) + +#define flush_tlb_page(_vma,_vmaddr) \ + do { \ + if ((_vma)->vm_mm == current->mm) \ + cpu_flush_tlb_page((_vmaddr), \ + ((_vma)->vm_flags & VM_EXEC)); \ + } while (0) + + diff --git a/include/asm-arm/proc-armv/elf.h b/include/asm-arm/proc-armv/elf.h index f82a786db..a865dce68 100644 --- a/include/asm-arm/proc-armv/elf.h +++ b/include/asm-arm/proc-armv/elf.h @@ -4,18 +4,20 @@ #define ELF_EXEC_PAGESIZE 4096 -/* We can execute both 32-bit and 26-bit code. */ -#define ELF_PROC_OK(x) (1) - #ifdef __KERNEL__ -#if 0 /* not yet */ +/* 32-bit code is always OK. Some cpus can do 26-bit, some can't. */ +#define ELF_PROC_OK(x) \ + ((! ((x)->e_flags & EF_ARM_APCS26)) \ + || (elf_hwcap & HWCAP_26BIT)) + +/* Old NetWinder binaries were compiled in such a way that the iBCS + heuristic always trips on them. Until these binaries become uncommon + enough not to care, don't trust the `ibcs' flag here. In any case + there is no other ELF system currently supported by iBCS. + @@ Could print a warning message to encourage users to upgrade. */ #define SET_PERSONALITY(ex,ibcs2) \ - current_personality = (ex->e_flags & EF_ARM_APCS26) ? \ + current->personality = (ex.e_flags & EF_ARM_APCS26) ? \ PER_LINUX : PER_LINUX_32BIT -#else -#define SET_PERSONALITY(ex,ibcs2) \ - current->personality = PER_LINUX_32BIT -#endif #endif diff --git a/include/asm-arm/proc-armv/mm-init.h b/include/asm-arm/proc-armv/mm-init.h deleted file mode 100644 index a07e9a50e..000000000 --- a/include/asm-arm/proc-armv/mm-init.h +++ /dev/null @@ -1,177 +0,0 @@ -/* - * linux/include/asm-arm/proc-armv/mm-init.h - * - * Copyright (C) 1996 Russell King - * - * This contains the code to setup the memory map on an ARM v3 or v4 machine. - * This is both processor & architecture specific, and requires some - * more work to get it to fit into our separate processor and architecture - * structure. - */ - -/* - * On ebsa, we want the memory map set up so: - * - * PHYS VIRT - * 00000000 00000000 Zero page - * 000003ff 000003ff Zero page end - * 00000000 c0000000 Kernel and all physical memory - * 01ffffff c1ffffff End of physical (32MB) - * e0000000 e0000000 IO start - * ffffffff ffffffff IO end - * - * On rpc, we want: - * - * PHYS VIRT - * 10000000 00000000 Zero page - * 100003ff 000003ff Zero page end - * 10000000 c0000000 Kernel and all physical memory - * 1fffffff cfffffff End of physical (32MB) - * 02000000 d?000000 Screen memory (first image) - * 02000000 d8000000 Screen memory (second image) - * 00000000 df000000 StrongARM cache invalidation area - * 03000000 e0000000 IO start - * 03ffffff e0ffffff IO end - * - * We set it up using the section page table entries. - */ -#include <asm/pgtable.h> - -#define PTE_SIZE (PTRS_PER_PTE * BYTES_PER_PTR) - -extern unsigned long setup_io_pagetables(unsigned long start_mem); - -/* - * Add a SECTION mapping between VIRT and PHYS in domain DOMAIN with protection PROT - */ -static inline void -alloc_init_section(unsigned long *mem, unsigned long virt, unsigned long phys, int domain, int prot) -{ - pgd_t *pgdp; - pmd_t *pmdp, pmd; - - pgdp = pgd_offset_k(virt); - pmdp = pmd_offset(pgdp, virt); - - pmd_val(pmd) = phys | PMD_TYPE_SECT | PMD_DOMAIN(domain) | prot; - set_pmd(pmdp, pmd); -} - -/* - * Clear any mapping - */ -static inline void -free_init_section(unsigned long virt) -{ - pgd_t *pgdp; - pmd_t *pmdp; - - pgdp = pgd_offset_k(virt); - pmdp = pmd_offset(pgdp, virt); - - pmd_clear(pmdp); -} - -/* - * Add a PAGE mapping between VIRT and PHYS in domain DOMAIN with protection PROT - */ -static inline void -alloc_init_page(unsigned long *mem, unsigned long virt, unsigned long phys, int domain, int prot) -{ - pgd_t *pgdp; - pmd_t *pmdp; - pte_t *ptep; - - pgdp = pgd_offset_k(virt); - pmdp = pmd_offset(pgdp, virt); - - if (pmd_none(*pmdp)) { - unsigned long memory = *mem; - - memory = (memory + PTE_SIZE - 1) & ~(PTE_SIZE - 1); - - ptep = (pte_t *)memory; - memzero(ptep, PTE_SIZE); - memory += PTE_SIZE; - - ptep = (pte_t *)memory; - memzero(ptep, PTE_SIZE); - - set_pmd(pmdp, __mk_pmd(ptep, PMD_TYPE_TABLE | PMD_DOMAIN(domain))); - - *mem = memory + PTE_SIZE; - } - - ptep = pte_offset(pmdp, virt); - - set_pte(ptep, mk_pte_phys(phys, __pgprot(prot))); -} - -static inline unsigned long -setup_pagetables(unsigned long start_mem, unsigned long end_mem) -{ - unsigned long address = 0; - - do { - if (address >= PAGE_OFFSET && address < end_mem) - /* - * map in physical ram & kernel - */ - alloc_init_section(&start_mem, address, __virt_to_phys(address), DOMAIN_KERNEL, - PMD_SECT_CACHEABLE | PMD_SECT_BUFFERABLE | PMD_SECT_AP_WRITE); - else - /* - * unmap everything else - */ - free_init_section(address); - - address += PGDIR_SIZE; - } while (address != 0); - - /* - * An area to invalidate the cache - */ - alloc_init_section(&start_mem, FLUSH_BASE, FLUSH_BASE_PHYS, DOMAIN_KERNEL, - PMD_SECT_CACHEABLE | PMD_SECT_AP_READ); - - /* - * Now set up our IO mappings - */ - start_mem = setup_io_pagetables(start_mem); - - /* - * map in zero page - */ - alloc_init_page(&start_mem, 0, __virt_to_phys(PAGE_OFFSET), - DOMAIN_USER, L_PTE_CACHEABLE | L_PTE_YOUNG | L_PTE_PRESENT); - - flush_cache_all(); - - return start_mem; -} - -static inline -void mark_usable_memory_areas(unsigned long *start_mem, unsigned long end_mem) -{ - unsigned long smem; - - *start_mem = smem = PAGE_ALIGN(*start_mem); - - /* - * Mark all of memory from the end of kernel to end of memory - */ - while (smem < end_mem) { - clear_bit(PG_reserved, &mem_map[MAP_NR(smem)].flags); - smem += PAGE_SIZE; - } - - /* - * Mark memory from page 1 to start of the swapper page directory - */ - smem = PAGE_OFFSET + PAGE_SIZE; - while (smem < (unsigned long)&swapper_pg_dir) { - clear_bit(PG_reserved, &mem_map[MAP_NR(smem)].flags); - smem += PAGE_SIZE; - } -} - diff --git a/include/asm-arm/proc-armv/page.h b/include/asm-arm/proc-armv/page.h index 8acec4c75..cd80dec3a 100644 --- a/include/asm-arm/proc-armv/page.h +++ b/include/asm-arm/proc-armv/page.h @@ -3,71 +3,12 @@ * * Copyright (C) 1995, 1996 Russell King */ - #ifndef __ASM_PROC_PAGE_H #define __ASM_PROC_PAGE_H -#include <asm/hardware.h> - /* PAGE_SHIFT determines the page size */ #define PAGE_SHIFT 12 -#define PAGE_SIZE (1UL << PAGE_SHIFT) -#define PAGE_MASK (~(PAGE_SIZE-1)) - -#ifdef __KERNEL__ - -#define STRICT_MM_TYPECHECKS - -#ifdef STRICT_MM_TYPECHECKS -/* - * These are used to make use of C type-checking.. - */ -typedef struct { unsigned long pte; } pte_t; -typedef struct { unsigned long pmd; } pmd_t; -typedef struct { unsigned long pgd; } pgd_t; -typedef struct { unsigned long pgprot; } pgprot_t; - -#define pte_val(x) ((x).pte) -#define pmd_val(x) ((x).pmd) -#define pgd_val(x) ((x).pgd) -#define pgprot_val(x) ((x).pgprot) - -#define __pte(x) ((pte_t) { (x) } ) -#define __pmd(x) ((pmd_t) { (x) } ) -#define __pgd(x) ((pgd_t) { (x) } ) -#define __pgprot(x) ((pgprot_t) { (x) } ) - -#else -/* - * .. while these make it easier on the compiler - */ -typedef unsigned long pte_t; -typedef unsigned long pmd_t; -typedef unsigned long pgd_t; -typedef unsigned long pgprot_t; - -#define pte_val(x) (x) -#define pmd_val(x) (x) -#define pgd_val(x) (x) -#define pgprot_val(x) (x) - -#define __pte(x) (x) -#define __pmd(x) (x) -#define __pgd(x) (x) -#define __pgprot(x) (x) - -#endif - -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) - -/* This handles the memory map.. */ -#ifndef PAGE_OFFSET -#define PAGE_OFFSET 0xc0000000 -#endif - -#define MAP_NR(addr) (((unsigned long)(addr) - PAGE_OFFSET) >> PAGE_SHIFT) -#endif /* __KERNEL__ */ +#define EXEC_PAGESIZE 4096 #endif /* __ASM_PROC_PAGE_H */ diff --git a/include/asm-arm/proc-armv/param.h b/include/asm-arm/proc-armv/param.h deleted file mode 100644 index 45bb5662d..000000000 --- a/include/asm-arm/proc-armv/param.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * linux/include/asm-arm/proc-armv/param.h - * - * Copyright (C) 1996 Russell King - */ - -#ifndef __ASM_PROC_PARAM_H -#define __ASM_PROC_PARAM_H - -#include <asm/arch/param.h> /* for HZ */ - -#define EXEC_PAGESIZE 4096 - -#ifndef NGROUPS -#define NGROUPS 32 -#endif - -#ifndef NOGROUP -#define NOGROUP (-1) -#endif - -#define MAXHOSTNAMELEN 64 /* max length of hostname */ - -#endif - diff --git a/include/asm-arm/proc-armv/pgtable.h b/include/asm-arm/proc-armv/pgtable.h index f6bf55889..6ea25c125 100644 --- a/include/asm-arm/proc-armv/pgtable.h +++ b/include/asm-arm/proc-armv/pgtable.h @@ -1,7 +1,7 @@ /* * linux/include/asm-arm/proc-armv/pgtable.h * - * Copyright (C) 1995, 1996, 1997 Russell King + * Copyright (C) 1995-1999 Russell King * * 12-Jan-1997 RMK Altered flushing routines to use function pointers * now possible to combine ARM6, ARM7 and StrongARM versions. @@ -11,106 +11,7 @@ #ifndef __ASM_PROC_PGTABLE_H #define __ASM_PROC_PGTABLE_H -#include <asm/arch/memory.h> /* For TASK_SIZE */ - -#define LIBRARY_TEXT_START 0x0c000000 - -/* - * Cache flushing... - */ -#define flush_cache_all() \ - cpu_flush_cache_all() - -#define flush_cache_mm(_mm) \ - do { \ - if ((_mm) == current->mm) \ - cpu_flush_cache_all(); \ - } while (0) - -#define flush_cache_range(_mm,_start,_end) \ - do { \ - if ((_mm) == current->mm) \ - cpu_flush_cache_area((_start), (_end), 1); \ - } while (0) - -#define flush_cache_page(_vma,_vmaddr) \ - do { \ - if ((_vma)->vm_mm == current->mm) \ - cpu_flush_cache_area((_vmaddr), \ - (_vmaddr) + PAGE_SIZE, \ - ((_vma)->vm_flags & VM_EXEC) ? 1 : 0); \ - } while (0) - -#define clean_cache_range(_start,_end) \ - do { \ - unsigned long _s, _sz; \ - _s = (unsigned long)_start; \ - _sz = (unsigned long)_end - _s; \ - cpu_clean_cache_area(_s, _sz); \ - } while (0) - -#define clean_cache_area(_start,_size) \ - do { \ - unsigned long _s; \ - _s = (unsigned long)_start; \ - cpu_clean_cache_area(_s, _size); \ - } while (0) - -#define flush_icache_range(_start,_end) \ - cpu_flush_icache_area((_start), (_end) - (_start)) - -/* - * We don't have a MEMC chip... - */ -#define update_memc_all() do { } while (0) -#define update_memc_task(tsk) do { } while (0) -#define update_memc_mm(mm) do { } while (0) -#define update_memc_addr(mm,addr,pte) do { } while (0) - -/* - * This flushes back any buffered write data. We have to clean and flush the entries - * in the cache for this page. Is it necessary to invalidate the I-cache? - */ -#define flush_page_to_ram(_page) \ - cpu_flush_ram_page((_page) & PAGE_MASK); - -/* - * TLB flushing: - * - * - flush_tlb() flushes the current mm struct TLBs - * - flush_tlb_all() flushes all processes TLBs - * - flush_tlb_mm(mm) flushes the specified mm context TLB's - * - flush_tlb_page(vma, vmaddr) flushes one page - * - flush_tlb_range(mm, start, end) flushes a range of pages - * - * GCC uses conditional instructions, and expects the assembler code to do so as well. - * - * We drain the write buffer in here to ensure that the page tables in ram - * are really up to date. It is more efficient to do this here... - */ -#define flush_tlb() flush_tlb_all() - -#define flush_tlb_all() \ - cpu_flush_tlb_all() - -#define flush_tlb_mm(_mm) \ - do { \ - if ((_mm) == current->mm) \ - cpu_flush_tlb_all(); \ - } while (0) - -#define flush_tlb_range(_mm,_start,_end) \ - do { \ - if ((_mm) == current->mm) \ - cpu_flush_tlb_area((_start), (_end), 1); \ - } while (0) - -#define flush_tlb_page(_vma,_vmaddr) \ - do { \ - if ((_vma)->vm_mm == current->mm) \ - cpu_flush_tlb_area((_vmaddr), (_vmaddr) + PAGE_SIZE, \ - ((_vma)->vm_flags & VM_EXEC) ? 1 : 0); \ - } while (0) +#include <asm/proc/domain.h> /* * PMD_SHIFT determines the size of the area a second-level page table can map @@ -135,8 +36,8 @@ #define PTRS_PER_PGD 4096 #define USER_PTRS_PER_PGD (TASK_SIZE/PGDIR_SIZE) - -/* Just any arbitrary offset to the start of the vmalloc VM area: the +/* + * Just any arbitrary offset to the start of the vmalloc VM area: the * current 8MB value just means that there will be a 8MB "hole" after the * physical memory until the kernel virtual memory starts. That means that * any out-of-bounds memory accesses will hopefully be caught. @@ -226,21 +127,6 @@ extern void free_page_2k(unsigned long page); * if any. */ -#ifndef __SMP__ -extern struct pgtable_cache_struct { - unsigned long *pgd_cache; - unsigned long *pte_cache; - unsigned long pgtable_cache_sz; -} quicklists; - -#define pgd_quicklist (quicklists.pgd_cache) -#define pmd_quicklist ((unsigned long *)0) -#define pte_quicklist (quicklists.pte_cache) -#define pgtable_cache_size (quicklists.pgtable_cache_sz) -#else -#error Pgtable caches have to be per-CPU, so that no locking is needed. -#endif - /**************** * PMD functions * ****************/ @@ -275,23 +161,10 @@ extern __inline__ int pmd_present(pmd_t pmd) return ((pmd_val(pmd) + 1) & 2); } -/* We don't use pmd cache, so this is a dummy routine */ -extern __inline__ pmd_t *get_pmd_fast(void) -{ - return (pmd_t *)0; -} - -extern __inline__ void free_pmd_fast(pmd_t *pmd) -{ -} - extern __inline__ void free_pmd_slow(pmd_t *pmd) { } -extern void __bad_pmd(pmd_t *pmd); -extern void __bad_pmd_kernel(pmd_t *pmd); - /* * allocating and freeing a pmd is trivial: the 1-entry pmd is * inside the pgd, so has no extra memory associated with it. @@ -378,29 +251,8 @@ extern __inline__ unsigned long pte_page(pte_t pte) return __phys_to_virt(pte_val(pte) & PAGE_MASK); } -extern pte_t *get_pte_slow(pmd_t *pmd, unsigned long address_preadjusted); extern pte_t *get_pte_kernel_slow(pmd_t *pmd, unsigned long address_preadjusted); -extern __inline__ pte_t *get_pte_fast(void) -{ - unsigned long *ret; - - if((ret = (unsigned long *)pte_quicklist) != NULL) { - pte_quicklist = (unsigned long *)(*ret); - ret[0] = ret[1]; - clean_cache_area(ret, 4); - pgtable_cache_size--; - } - return (pte_t *)ret; -} - -extern __inline__ void free_pte_fast(pte_t *pte) -{ - *(unsigned long *)pte = (unsigned long) pte_quicklist; - pte_quicklist = (unsigned long *) pte; - pgtable_cache_size++; -} - extern __inline__ void free_pte_slow(pte_t *pte) { free_page_2k((unsigned long)(pte - PTRS_PER_PTE)); @@ -539,6 +391,8 @@ extern __inline__ pte_t * pte_alloc_kernel(pmd_t *pmd, unsigned long address) extern __inline__ pte_t * pte_alloc(pmd_t * pmd, unsigned long address) { + extern pte_t *get_pte_slow(pmd_t *pmd, unsigned long address_preadjusted); + address = (address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1); if (pmd_none(*pmd)) { @@ -569,38 +423,12 @@ extern __inline__ pte_t * pte_alloc(pmd_t * pmd, unsigned long address) /* to find an entry in a kernel page-table-directory */ #define pgd_offset_k(address) pgd_offset(&init_mm, address) -/* used for quicklists */ -#define __pgd_next(pgd) (((unsigned long *)pgd)[1]) - /* to find an entry in a page-table-directory */ extern __inline__ pgd_t * pgd_offset(struct mm_struct * mm, unsigned long address) { return mm->pgd + (address >> PGDIR_SHIFT); } -extern pgd_t *get_pgd_slow(void); - -extern __inline__ pgd_t *get_pgd_fast(void) -{ - unsigned long *ret; - - if((ret = pgd_quicklist) != NULL) { - pgd_quicklist = (unsigned long *)__pgd_next(ret); - ret[1] = ret[2]; - clean_cache_area(ret + 1, 4); - pgtable_cache_size--; - } else - ret = (unsigned long *)get_pgd_slow(); - return (pgd_t *)ret; -} - -extern __inline__ void free_pgd_fast(pgd_t *pgd) -{ - __pgd_next(pgd) = (unsigned long) pgd_quicklist; - pgd_quicklist = (unsigned long *) pgd; - pgtable_cache_size++; -} - extern __inline__ void free_pgd_slow(pgd_t *pgd) { do { @@ -630,28 +458,17 @@ extern __inline__ void free_pgd_slow(pgd_t *pgd) } #define pgd_free(pgd) free_pgd_fast(pgd) -#define pgd_alloc() get_pgd_fast() -extern __inline__ void set_pgdir(unsigned long address, pgd_t entry) +extern __inline__ pgd_t *pgd_alloc(void) { - struct task_struct * p; + extern pgd_t *get_pgd_slow(void); pgd_t *pgd; - read_lock(&tasklist_lock); - for_each_task(p) { - if (!p->mm) - continue; - *pgd_offset(p->mm,address) = entry; - } - read_unlock(&tasklist_lock); - for (pgd = (pgd_t *)pgd_quicklist; pgd; pgd = (pgd_t *)__pgd_next(pgd)) - pgd[address >> PGDIR_SHIFT] = entry; -} + pgd = get_pgd_fast(); + if (!pgd) + pgd = get_pgd_slow(); -extern pgd_t swapper_pg_dir[PTRS_PER_PGD]; - -#define SWP_TYPE(entry) (((entry) >> 2) & 0x7f) -#define SWP_OFFSET(entry) ((entry) >> 9) -#define SWP_ENTRY(type,offset) (((type) << 2) | ((offset) << 9)) + return pgd; +} -#endif /* __ASM_PROC_PAGE_H */ +#endif /* __ASM_PROC_PGTABLE_H */ diff --git a/include/asm-arm/proc-armv/processor.h b/include/asm-arm/proc-armv/processor.h index 45af61ea7..48b57243a 100644 --- a/include/asm-arm/proc-armv/processor.h +++ b/include/asm-arm/proc-armv/processor.h @@ -41,8 +41,6 @@ struct context_save_struct { domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \ domain_val(DOMAIN_IO, DOMAIN_CLIENT) -#define SWAPPER_PG_DIR (((unsigned long)swapper_pg_dir) - PAGE_OFFSET) - #define start_thread(regs,pc,sp) \ ({ \ unsigned long *stack = (unsigned long *)sp; \ diff --git a/include/asm-arm/proc-armv/ptrace.h b/include/asm-arm/proc-armv/ptrace.h index 01f0a8ac8..362eac178 100644 --- a/include/asm-arm/proc-armv/ptrace.h +++ b/include/asm-arm/proc-armv/ptrace.h @@ -1,12 +1,33 @@ /* * linux/include/asm-arm/proc-armv/ptrace.h * - * Copyright (C) 1996 Russell King + * Copyright (C) 1996-1999 Russell King */ - #ifndef __ASM_PROC_PTRACE_H #define __ASM_PROC_PTRACE_H +#define USR26_MODE 0x00 +#define FIQ26_MODE 0x01 +#define IRQ26_MODE 0x02 +#define SVC26_MODE 0x03 +#define USR_MODE 0x10 +#define FIQ_MODE 0x11 +#define IRQ_MODE 0x12 +#define SVC_MODE 0x13 +#define ABT_MODE 0x17 +#define UND_MODE 0x1b +#define SYSTEM_MODE 0x1f +#define MODE_MASK 0x1f +#define F_BIT 0x40 +#define I_BIT 0x80 +#define CC_V_BIT (1 << 28) +#define CC_C_BIT (1 << 29) +#define CC_Z_BIT (1 << 30) +#define CC_N_BIT (1 << 31) +#define PCMASK 0 + +#ifndef __ASSEMBLY__ + /* this struct defines the way the registers are stored on the stack during a system call. */ @@ -31,37 +52,12 @@ struct pt_regs { #define ARM_r2 uregs[2] #define ARM_r1 uregs[1] #define ARM_r0 uregs[0] -#define ARM_ORIG_r0 uregs[17] /* -1 */ - -#define USR26_MODE 0x00 -#define FIQ26_MODE 0x01 -#define IRQ26_MODE 0x02 -#define SVC26_MODE 0x03 -#define USR_MODE 0x10 -#define FIQ_MODE 0x11 -#define IRQ_MODE 0x12 -#define SVC_MODE 0x13 -#define ABT_MODE 0x17 -#define UND_MODE 0x1b -#define SYSTEM_MODE 0x1f -#define MODE_MASK 0x1f -#define F_BIT 0x40 -#define I_BIT 0x80 -#define CC_V_BIT (1 << 28) -#define CC_C_BIT (1 << 29) -#define CC_Z_BIT (1 << 30) -#define CC_N_BIT (1 << 31) +#define ARM_ORIG_r0 uregs[17] #ifdef __KERNEL__ -#if 0 /* GCC/egcs should be able to optimise this, IMHO */ -#define user_mode(regs) \ - ((((regs)->ARM_cpsr & MODE_MASK) == USR_MODE) || \ - (((regs)->ARM_cpsr & MODE_MASK) == USR26_MODE)) -#else #define user_mode(regs) \ (((regs)->ARM_cpsr & 0xf) == 0) -#endif #define processor_mode(regs) \ ((regs)->ARM_cpsr & MODE_MASK) @@ -75,9 +71,6 @@ struct pt_regs { #define condition_codes(regs) \ ((regs)->ARM_cpsr & (CC_V_BIT|CC_C_BIT|CC_Z_BIT|CC_N_BIT)) -#define instruction_pointer(regs) ((regs)->ARM_pc) -#define pc_pointer(v) (v) - /* Are the current registers suitable for user mode? * (used to maintain security in signal handlers) */ @@ -97,5 +90,7 @@ static inline int valid_user_regs(struct pt_regs *regs) #endif /* __KERNEL__ */ +#endif /* __ASSEMBLY__ */ + #endif diff --git a/include/asm-arm/proc-armv/system.h b/include/asm-arm/proc-armv/system.h index 20250f659..a7551ec93 100644 --- a/include/asm-arm/proc-armv/system.h +++ b/include/asm-arm/proc-armv/system.h @@ -11,6 +11,8 @@ extern const char xchg_str[]; extern __inline__ unsigned long __xchg(unsigned long x, volatile void *ptr, int size) { + extern void arm_invalidptr(const char *, int); + switch (size) { case 1: __asm__ __volatile__ ("swpb %0, %1, [%2]" : "=r" (x) : "r" (x), "r" (ptr) : "memory"); break; @@ -33,16 +35,6 @@ extern unsigned long cr_no_alignment; /* defined in entry-armv.S */ extern unsigned long cr_alignment; /* defined in entry-armv.S */ /* - * We can wait for an interrupt... - */ -#define proc_idle() \ - do { \ - __asm__ __volatile__( \ -" mcr p15, 0, %0, c15, c8, 2 @ proc_idle" \ - : : "r" (0)); \ - } while (0) - -/* * A couple of speedups for the ARM */ diff --git a/include/asm-arm/proc-armv/uaccess.h b/include/asm-arm/proc-armv/uaccess.h index 7bef26d38..67afdd2ae 100644 --- a/include/asm-arm/proc-armv/uaccess.h +++ b/include/asm-arm/proc-armv/uaccess.h @@ -2,7 +2,7 @@ * linux/include/asm-arm/proc-armv/uaccess.h */ -#include <asm/hardware.h> +#include <asm/arch/memory.h> #include <asm/proc/domain.h> /* @@ -11,11 +11,6 @@ #define KERNEL_DS 0x00000000 #define USER_DS PAGE_OFFSET -#define get_ds() (KERNEL_DS) -#define get_fs() (current->addr_limit) - -#define segment_eq(a,b) ((a) == (b)) - extern __inline__ void set_fs (mm_segment_t fs) { current->addr_limit = fs; @@ -40,8 +35,6 @@ extern __inline__ void set_fs (mm_segment_t fs) : "cc"); \ (flag == 0); }) -#define access_ok(type,addr,size) (__range_ok(addr,size) == 0) - #define __put_user_asm_byte(x,addr,err) \ __asm__ __volatile__( \ "1: strbt %1,[%2],#0\n" \ @@ -172,6 +165,6 @@ extern unsigned long __arch_strncpy_from_user(char *to, const char *from, unsign #define __do_strncpy_from_user(dst,src,count,res) \ (res) = __arch_strncpy_from_user(dst,src,count) -extern unsigned long __arch_strlen_user(const char *s); -#define __do_strlen_user(s,res) \ - (res) = __arch_strlen_user(s) +extern unsigned long __arch_strnlen_user(const char *s, long n); +#define __do_strnlen_user(s,n,res) \ + (res) = __arch_strnlen_user(s,n) diff --git a/include/asm-arm/proc-fns.h b/include/asm-arm/proc-fns.h index 8970cf137..6bbe53e7c 100644 --- a/include/asm-arm/proc-fns.h +++ b/include/asm-arm/proc-fns.h @@ -58,4 +58,14 @@ #endif /* __KERNEL__ */ +#if 0 + * The following is to fool mkdep into generating the correct + * dependencies. Without this, it cant figure out that this + * file does indeed depend on the cpu-*.h files. +#include <asm/cpu-single.h> +#include <asm/cpu-multi26.h> +#include <asm/cpu-multi32.h> + * +#endif + #endif /* __ASM_PROCFNS_H */ diff --git a/include/asm-arm/processor.h b/include/asm-arm/processor.h index c0903f5e8..33b98efa1 100644 --- a/include/asm-arm/processor.h +++ b/include/asm-arm/processor.h @@ -32,11 +32,10 @@ typedef unsigned long mm_segment_t; /* domain register */ #ifdef __KERNEL__ -#include <asm/assembler.h> - #define NR_DEBUGS 5 -#include <asm/proc/ptrace.h> +#include <asm/ptrace.h> +#include <asm/arch/memory.h> #include <asm/arch/processor.h> #include <asm/proc/processor.h> @@ -80,7 +79,7 @@ struct thread_struct { */ extern __inline__ unsigned long thread_saved_pc(struct thread_struct *t) { - return t->save ? t->save->pc & ~PCMASK : 0; + return t->save ? pc_pointer(t->save->pc) : 0; } extern __inline__ unsigned long get_css_fp(struct thread_struct *t) diff --git a/include/asm-arm/procinfo.h b/include/asm-arm/procinfo.h index 8c11dfd80..511a78005 100644 --- a/include/asm-arm/procinfo.h +++ b/include/asm-arm/procinfo.h @@ -6,7 +6,7 @@ #ifndef __ASM_PROCINFO_H #define __ASM_PROCINFO_H -#ifndef __ASSEMBLER__ +#ifndef __ASSEMBLY__ #include <asm/proc-fns.h> @@ -18,11 +18,15 @@ struct proc_info_item { /* * Note! struct processor is always defined if we're * using MULTI_CPU, otherwise this entry is unused, - * but still exists. + * but still exists. NOTE! This structure is used + * by assembler code! Check: + * arch/arm/mm/proc-*.S and arch/arm/kernel/head-armv.S */ struct proc_info_list { unsigned int cpu_val; unsigned int cpu_mask; + unsigned long __cpu_mmu_flags; /* used by head-armv.S */ + unsigned long __cpu_flush; /* used by head-armv.S */ const char *arch_name; const char *elf_name; unsigned int elf_hwcap; @@ -34,7 +38,7 @@ struct proc_info_list { #endif }; -#endif +#endif /* __ASSEMBLY__ */ #define HWCAP_SWP 1 #define HWCAP_HALF 2 diff --git a/include/asm-arm/ptrace.h b/include/asm-arm/ptrace.h index 5cd91dc31..0b4714211 100644 --- a/include/asm-arm/ptrace.h +++ b/include/asm-arm/ptrace.h @@ -3,9 +3,18 @@ #include <asm/proc/ptrace.h> +#ifndef __ASSEMBLY__ +#define pc_pointer(v) \ + ((v) & ~PCMASK) + +#define instruction_pointer(regs) \ + (pc_pointer((regs)->ARM_pc)) + #ifdef __KERNEL__ extern void show_regs(struct pt_regs *); #endif +#endif /* __ASSEMBLY__ */ + #endif diff --git a/include/asm-arm/semaphore.h b/include/asm-arm/semaphore.h index 71509e9e8..5ba171be8 100644 --- a/include/asm-arm/semaphore.h +++ b/include/asm-arm/semaphore.h @@ -5,10 +5,11 @@ #define __ASM_ARM_SEMAPHORE_H #include <linux/linkage.h> -#include <asm/atomic.h> #include <linux/spinlock.h> #include <linux/wait.h> +#include <asm/atomic.h> + struct semaphore { atomic_t count; int sleepers; diff --git a/include/asm-arm/setup.h b/include/asm-arm/setup.h index 07aefda05..de737ee40 100644 --- a/include/asm-arm/setup.h +++ b/include/asm-arm/setup.h @@ -1,13 +1,24 @@ /* * include/asm/setup.h * - * Structure passed to kernel to tell it about the hardware it's running on + * Structure passed to kernel to tell it about the + * hardware it's running on. See linux/Documentation/arm/Setup + * for more info. * - * Copyright (C) 1997,1998 Russell King + * Copyright (C) 1997-1999 Russell King */ #ifndef __ASMARM_SETUP_H #define __ASMARM_SETUP_H +/* + * Usage: + * - do not go blindly adding fields, add them at the end + * - when adding fields, don't rely on the address until + * a patch from me has been released + * - unused fields should be zero (for future expansion) + */ +#define COMMAND_LINE_SIZE 1024 + struct param_struct { union { struct { @@ -33,6 +44,9 @@ struct param_struct { unsigned long initrd_start; /* 64 */ unsigned long initrd_size; /* 68 */ unsigned long rd_start; /* 72 */ + unsigned long system_rev; /* 76 */ + unsigned long system_serial_low; /* 80 */ + unsigned long system_serial_high; /* 84 */ } s; char unused[256]; } u1; @@ -43,7 +57,7 @@ struct param_struct { char n[1024 - sizeof(unsigned long)]; } s; } u2; - char commandline[256]; + char commandline[COMMAND_LINE_SIZE]; }; #endif diff --git a/include/asm-arm/shmparam.h b/include/asm-arm/shmparam.h index 74d2a6122..02c9b195f 100644 --- a/include/asm-arm/shmparam.h +++ b/include/asm-arm/shmparam.h @@ -1,12 +1,6 @@ #ifndef _ASMARM_SHMPARAM_H #define _ASMARM_SHMPARAM_H -/* - * Include the machine specific shm parameters before the processor - * dependent parameters so that the machine parameters can override - * the processor parameters - */ -#include <asm/arch/shmparam.h> #include <asm/proc/shmparam.h> /* diff --git a/include/asm-arm/string.h b/include/asm-arm/string.h index 9c72b0173..5d631341e 100644 --- a/include/asm-arm/string.h +++ b/include/asm-arm/string.h @@ -2,7 +2,8 @@ #define __ASM_ARM_STRING_H /* - * inline versions, hmm... + * We don't do inline string functions, since the + * optimised inline asm versions are not small. */ #define __HAVE_ARCH_STRRCHR @@ -13,13 +14,25 @@ extern char * strchr(const char * s, int c); #define __HAVE_ARCH_MEMCPY #define __HAVE_ARCH_MEMMOVE -#define __HAVE_ARCH_MEMSET #define __HAVE_ARCH_MEMCHR - #define __HAVE_ARCH_MEMZERO -extern void memzero(void *ptr, int n); +#define __HAVE_ARCH_MEMSET + +extern void __memzero(void *ptr, __kernel_size_t n); +extern void __memset(void *ptr, int v, __kernel_size_t n); + +#define memset(p,v,n) \ + ({ \ + if ((n) != 0) { \ + if (__builtin_constant_p((v)) && (v) == 0) \ + __memzero((p),(n)); \ + else \ + __memset((p),(v),(n)); \ + } \ + (p); \ + }) -extern void memsetl (unsigned long *, unsigned long, int n); +#define memzero(p,n) ({ if ((n) != 0) __memzero((p),(n)); (p); }) #endif diff --git a/include/asm-arm/system.h b/include/asm-arm/system.h index b4f4d4136..8725ad806 100644 --- a/include/asm-arm/system.h +++ b/include/asm-arm/system.h @@ -18,9 +18,15 @@ #define __netwinder_text #endif +/* information about the system we're running on */ +extern unsigned int system_rev; +extern unsigned int system_serial_low; +extern unsigned int system_serial_high; + /* The type of machine we're running on */ extern unsigned int __machine_arch_type; +/* see arch/arm/kernel/setup.c for a description of these */ #define MACH_TYPE_EBSA110 0 #define MACH_TYPE_RISCPC 1 #define MACH_TYPE_NEXUSPCI 3 @@ -32,7 +38,10 @@ extern unsigned int __machine_arch_type; #define MACH_TYPE_CLPS7110 9 #define MACH_TYPE_ARCHIMEDES 10 #define MACH_TYPE_A5K 11 -#define MACH_TYPE_SA1100 12 /* not allocated!!! */ +#define MACH_TYPE_ETOILE 12 +#define MACH_TYPE_LACIE_NAS 13 +#define MACH_TYPE_CLPS7500 14 +#define MACH_TYPE_SHARK 15 /* * Sort out a definition for machine_arch_type @@ -127,14 +136,12 @@ extern unsigned int __machine_arch_type; #define tas(ptr) (xchg((ptr),1)) extern void arm_malalignedptr(const char *, void *, volatile void *); -extern void arm_invalidptr(const char *, int); extern asmlinkage void __backtrace(void); /* * Include processor dependent parts */ #include <asm/proc/system.h> -#include <asm/arch/system.h> #define mb() __asm__ __volatile__ ("" : : : "memory") #define rmb() mb() diff --git a/include/asm-arm/therm.h b/include/asm-arm/therm.h new file mode 100644 index 000000000..e51c923ec --- /dev/null +++ b/include/asm-arm/therm.h @@ -0,0 +1,28 @@ +/* + * linux/include/asm-arm/therm.h: Definitions for Dallas Semiconductor + * DS1620 thermometer driver (as used in the Rebel.com NetWinder) + */ +#ifndef __ASM_THERM_H +#define __ASM_THERM_H + +/* ioctl numbers for /dev/therm */ +#define CMD_SET_THERMOSTATE 0x53 +#define CMD_GET_THERMOSTATE 0x54 +#define CMD_GET_STATUS 0x56 +#define CMD_GET_TEMPERATURE 0x57 +#define CMD_SET_THERMOSTATE2 0x58 +#define CMD_GET_THERMOSTATE2 0x59 +#define CMD_GET_TEMPERATURE2 0x5a +#define CMD_GET_FAN 0x5b +#define CMD_SET_FAN 0x5c + +#define FAN_OFF 0 +#define FAN_ON 1 +#define FAN_ALWAYS_ON 2 + +struct therm { + int hi; + int lo; +}; + +#endif diff --git a/include/asm-arm/uaccess.h b/include/asm-arm/uaccess.h index 5e4c118a9..8bcfa7078 100644 --- a/include/asm-arm/uaccess.h +++ b/include/asm-arm/uaccess.h @@ -5,6 +5,7 @@ * User space memory access functions */ #include <linux/sched.h> +#include <asm/errno.h> #define VERIFY_READ 0 #define VERIFY_WRITE 1 @@ -30,11 +31,17 @@ struct exception_table_entry /* Returns 0 if exception not found and fixup otherwise. */ extern unsigned long search_exception_table(unsigned long); +#define get_ds() (KERNEL_DS) +#define get_fs() (current->addr_limit) +#define segment_eq(a,b) ((a) == (b)) + #include <asm/proc/uaccess.h> -extern inline int verify_area(int type, const void * addr, unsigned long size) +#define access_ok(type,addr,size) (__range_ok(addr,size) == 0) + +extern __inline__ int verify_area(int type, const void * addr, unsigned long size) { - return access_ok(type,addr,size) ? 0 : -EFAULT; + return access_ok(type, addr, size) ? 0 : -EFAULT; } /* @@ -121,12 +128,14 @@ static __inline__ long __strncpy_from_user (char *dst, const char *src, long cou return res; } -extern __inline__ long strlen_user (const char *s) +#define strlen_user(s) strnlen_user(s, ~0UL >> 1) + +extern __inline__ long strnlen_user(const char *s, long n) { unsigned long res = 0; if (__addr_ok(s)) - __do_strlen_user (s, res); + __do_strnlen_user(s, n, res); return res; } diff --git a/include/asm-arm/unaligned.h b/include/asm-arm/unaligned.h index 9b86bd415..277d778a1 100644 --- a/include/asm-arm/unaligned.h +++ b/include/asm-arm/unaligned.h @@ -1,59 +1,46 @@ -#ifndef __ARM_UNALIGNED_H -#define __ARM_UNALIGNED_H +#ifndef __ASM_ARM_UNALIGNED_H +#define __ASM_ARM_UNALIGNED_H #define get_unaligned(ptr) \ - ((__typeof__(*(ptr)))__get_unaligned((ptr), sizeof(*(ptr)))) + ((__typeof__(*(ptr)))__get_unaligned_size((ptr), sizeof(*(ptr)))) #define put_unaligned(val, ptr) \ - __put_unaligned((unsigned long)(val), (ptr), sizeof(*(ptr))) - -extern void bad_unaligned_access_length (void); - -extern inline unsigned long __get_unaligned(const void *ptr, size_t size) + __put_unaligned_size((unsigned long)(val), (ptr), sizeof(*(ptr))) + +/* + * We use a similar method to the uaccess.h badness detection. + * + * These are actually never defined anywhere, and therefore + * catch errors at compile/link time. Don't be tempted to + * provide a declaration for them; doing so will mask the + * errors. + */ +extern unsigned long __get_unaligned_bad(void); +extern void __put_unaligned_bad(void); + +extern __inline__ unsigned long __get_unaligned_size(const void *ptr, size_t size) { - unsigned long val; - switch (size) { - case 1: - val = *(const unsigned char *)ptr; - break; - - case 2: - val = ((const unsigned char *)ptr)[0] | (((const unsigned char *)ptr)[1] << 8); - break; - - case 4: - val = ((const unsigned char *)ptr)[0] | (((const unsigned char *)ptr)[1] << 8) | - (((const unsigned char *)ptr)[2]) << 16 | (((const unsigned char *)ptr)[3] << 24); - break; - - default: - bad_unaligned_access_length (); - } - return val; + const unsigned char *p = (const unsigned char *)ptr; + unsigned long val = 0; + + switch (size) { + case 4: val = p[2] << 16 | p[3] << 24; + case 2: val |= p[1] << 8; + case 1: val |= p[0]; break; + default: val = __get_unaligned_bad(); break; + } + return val; } -extern inline void __put_unaligned(unsigned long val, void *ptr, size_t size) +extern __inline__ void __put_unaligned_size(unsigned long val, void *ptr, size_t size) { - switch (size) { - case 1: - *(unsigned char *)ptr = val; - break; - - case 2: - ((unsigned char *)ptr)[0] = val; - ((unsigned char *)ptr)[1] = val >> 8; - break; - - case 4: - ((unsigned char *)ptr)[0] = val; - ((unsigned char *)ptr)[1] = val >> 8; - ((unsigned char *)ptr)[2] = val >> 16; - ((unsigned char *)ptr)[3] = val >> 24; - break; - - default: - bad_unaligned_access_length (); - } + switch (size) { + case 4: ((unsigned char *)ptr)[3] = val >> 24; + ((unsigned char *)ptr)[2] = val >> 16; + case 2: ((unsigned char *)ptr)[1] = val >> 8; + case 1: ((unsigned char *)ptr)[0] = val; break; + default: __put_unaligned_bad(); break; + } } #endif diff --git a/include/asm-arm/unistd.h b/include/asm-arm/unistd.h index 9a436259d..397d80ed5 100644 --- a/include/asm-arm/unistd.h +++ b/include/asm-arm/unistd.h @@ -362,6 +362,7 @@ static inline long open(const char *file, int flag, int mode) static inline long close(int fd) { + extern long sys_close(unsigned int); return sys_close(fd); } @@ -397,6 +398,3 @@ static inline _syscall3(int,execve,const char *,file,char **,argv,char **,envp); #endif #endif /* __ASM_ARM_UNISTD_H */ - - - diff --git a/include/asm-arm/user.h b/include/asm-arm/user.h index 7afe1a2a9..e49d3db8e 100644 --- a/include/asm-arm/user.h +++ b/include/asm-arm/user.h @@ -42,6 +42,8 @@ struct user_fp { } fpregs[8]; unsigned int fpsr:32; unsigned int fpcr:32; + unsigned char ftype[8]; + unsigned int init_flag; }; /* When the kernel dumps core, it starts by dumping the user struct - diff --git a/include/asm-i386/bigmem.h b/include/asm-i386/bigmem.h deleted file mode 100644 index 1c5c4cf4b..000000000 --- a/include/asm-i386/bigmem.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * bigmem.h: virtual kernel memory mappings for big memory - * - * Used in CONFIG_BIGMEM systems for memory pages which are not - * addressable by direct kernel virtual adresses. - * - * Copyright (C) 1999 Gerhard Wichert, Siemens AG - * Gerhard.Wichert@pdb.siemens.de - */ - -#ifndef _ASM_BIGMEM_H -#define _ASM_BIGMEM_H - -#include <linux/init.h> - -#define BIGMEM_DEBUG /* undef for production */ - -/* declarations for bigmem.c */ -extern unsigned long bigmem_start, bigmem_end; -extern int nr_free_bigpages; - -extern pte_t *kmap_pte; -extern pgprot_t kmap_prot; - -extern void kmap_init(void) __init; - -/* kmap helper functions necessary to access the bigmem pages in kernel */ -#include <asm/pgtable.h> -#include <asm/kmap_types.h> - -extern inline unsigned long kmap(unsigned long kaddr, enum km_type type) -{ - if (__pa(kaddr) < bigmem_start) - return kaddr; - { - enum fixed_addresses idx = type+KM_TYPE_NR*smp_processor_id(); - unsigned long vaddr = __fix_to_virt(FIX_KMAP_BEGIN+idx); - -#ifdef BIGMEM_DEBUG - if (!pte_none(*(kmap_pte-idx))) - { - __label__ here; - here: - printk(KERN_ERR "not null pte on CPU %d from %p\n", - smp_processor_id(), &&here); - } -#endif - set_pte(kmap_pte-idx, mk_pte(kaddr & PAGE_MASK, kmap_prot)); - __flush_tlb_one(vaddr); - - return vaddr | (kaddr & ~PAGE_MASK); - } -} - -extern inline void kunmap(unsigned long vaddr, enum km_type type) -{ -#ifdef BIGMEM_DEBUG - enum fixed_addresses idx = type+KM_TYPE_NR*smp_processor_id(); - if ((vaddr & PAGE_MASK) == __fix_to_virt(FIX_KMAP_BEGIN+idx)) - { - /* force other mappings to Oops if they'll try to access - this pte without first remap it */ - pte_clear(kmap_pte-idx); - __flush_tlb_one(vaddr); - } -#endif -} - -#endif /* _ASM_BIGMEM_H */ diff --git a/include/asm-i386/bugs.h b/include/asm-i386/bugs.h index 1914385eb..4ae25be50 100644 --- a/include/asm-i386/bugs.h +++ b/include/asm-i386/bugs.h @@ -236,6 +236,7 @@ static void __init check_amd_k6(void) * have the F0 0F bug, which lets nonpriviledged users lock up the system: */ +#ifndef CONFIG_M686 extern void trap_init_f00f_bug(void); static void __init check_pentium_f00f(void) @@ -250,6 +251,7 @@ static void __init check_pentium_f00f(void) trap_init_f00f_bug(); } } +#endif /* * Perform the Cyrix 5/2 test. A Cyrix won't change @@ -424,7 +426,9 @@ static void __init check_bugs(void) check_hlt(); check_popad(); check_amd_k6(); +#ifndef CONFIG_M686 check_pentium_f00f(); +#endif check_cyrix_coma(); system_utsname.machine[1] = '0' + boot_cpu_data.x86; } diff --git a/include/asm-i386/fixmap.h b/include/asm-i386/fixmap.h index 34c82dbe0..01f6a1871 100644 --- a/include/asm-i386/fixmap.h +++ b/include/asm-i386/fixmap.h @@ -17,7 +17,7 @@ #include <linux/kernel.h> #include <asm/apic.h> #include <asm/page.h> -#ifdef CONFIG_BIGMEM +#ifdef CONFIG_HIGHMEM #include <linux/threads.h> #include <asm/kmap_types.h> #endif @@ -34,7 +34,7 @@ * * these 'compile-time allocated' memory buffers are * fixed-size 4k pages. (or larger if used with an increment - * bigger than 1) use fixmap_set(idx,phys) to associate + * highger than 1) use fixmap_set(idx,phys) to associate * physical memory with fixmap indices. * * TLB entries of such buffers will not be flushed across @@ -61,7 +61,7 @@ enum fixed_addresses { FIX_LI_PCIA, /* Lithium PCI Bridge A */ FIX_LI_PCIB, /* Lithium PCI Bridge B */ #endif -#ifdef CONFIG_BIGMEM +#ifdef CONFIG_HIGHMEM FIX_KMAP_BEGIN, /* reserved pte's for temporary kernel mappings */ FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1, #endif diff --git a/include/asm-i386/highmem.h b/include/asm-i386/highmem.h new file mode 100644 index 000000000..bd5564aea --- /dev/null +++ b/include/asm-i386/highmem.h @@ -0,0 +1,85 @@ +/* + * highmem.h: virtual kernel memory mappings for high memory + * + * Used in CONFIG_HIGHMEM systems for memory pages which + * are not addressable by direct kernel virtual adresses. + * + * Copyright (C) 1999 Gerhard Wichert, Siemens AG + * Gerhard.Wichert@pdb.siemens.de + * + * + * Redesigned the x86 32-bit VM architecture to deal with + * up to 16 Terrabyte physical memory. With current x86 CPUs + * we now support up to 64 Gigabytes physical RAM. + * + * Copyright (C) 1999 Ingo Molnar <mingo@redhat.com> + */ + +#ifndef _ASM_HIGHMEM_H +#define _ASM_HIGHMEM_H + +#include <linux/init.h> + +/* undef for production */ +#define HIGHMEM_DEBUG 1 + +/* declarations for highmem.c */ +extern unsigned long highstart_pfn, highend_pfn; + +extern pte_t *kmap_pte; +extern pgprot_t kmap_prot; + +extern void kmap_init(void) __init; + +/* kmap helper functions necessary to access the highmem pages in kernel */ +#include <asm/pgtable.h> +#include <asm/kmap_types.h> + +extern inline unsigned long kmap(struct page *page, enum km_type type) +{ + if (page < highmem_start_page) + return page_address(page); + { + enum fixed_addresses idx = type+KM_TYPE_NR*smp_processor_id(); + unsigned long vaddr = __fix_to_virt(FIX_KMAP_BEGIN+idx); + +#if HIGHMEM_DEBUG + if (!pte_none(*(kmap_pte-idx))) + { + __label__ here; + here: + printk(KERN_ERR "not null pte on CPU %d from %p\n", + smp_processor_id(), &&here); + } +#endif + set_pte(kmap_pte-idx, mk_pte(page, kmap_prot)); + __flush_tlb_one(vaddr); + + return vaddr; + } +} + +extern inline void kunmap(unsigned long vaddr, enum km_type type) +{ +#if HIGHMEM_DEBUG + enum fixed_addresses idx = type+KM_TYPE_NR*smp_processor_id(); + if ((vaddr & PAGE_MASK) == __fix_to_virt(FIX_KMAP_BEGIN+idx)) + { + /* force other mappings to Oops if they'll try to access + this pte without first remap it */ + pte_clear(kmap_pte-idx); + __flush_tlb_one(vaddr); + } +#endif +} + +extern inline void kmap_check(void) +{ +#if HIGHMEM_DEBUG + int idx_base = KM_TYPE_NR*smp_processor_id(), i; + for (i = idx_base; i < idx_base+KM_TYPE_NR; i++) + if (!pte_none(*(kmap_pte-i))) + BUG(); +#endif +} +#endif /* _ASM_HIGHMEM_H */ diff --git a/include/asm-i386/io.h b/include/asm-i386/io.h index 906fca475..f451a608a 100644 --- a/include/asm-i386/io.h +++ b/include/asm-i386/io.h @@ -103,28 +103,32 @@ __OUTS(l) #include <linux/vmalloc.h> #include <asm/page.h> -#define __io_virt(x) ((void *)(PAGE_OFFSET | (unsigned long)(x))) -#define __io_phys(x) ((unsigned long)(x) & ~PAGE_OFFSET) +/* + * Temporary debugging check to catch old code using + * unmapped ISA addresses. Will be removed in 2.4. + */ +#if 1 + extern void *__io_virt_debug(unsigned long x, const char *file, int line); + extern unsigned long __io_phys_debug(unsigned long x, const char *file, int line); + #define __io_virt(x) __io_virt_debug((unsigned long)(x), __FILE__, __LINE__) +//#define __io_phys(x) __io_phys_debug((unsigned long)(x), __FILE__, __LINE__) +#else + #define __io_virt(x) ((void *)(x)) +//#define __io_phys(x) __pa(x) +#endif + /* * Change virtual addresses to physical addresses and vv. * These are pretty trivial */ extern inline unsigned long virt_to_phys(volatile void * address) { -#ifdef CONFIG_BIGMEM return __pa(address); -#else - return __io_phys(address); -#endif } extern inline void * phys_to_virt(unsigned long address) { -#ifdef CONFIG_BIGMEM return __va(address); -#else - return __io_virt(address); -#endif } extern void * __ioremap(unsigned long offset, unsigned long size, unsigned long flags); @@ -178,6 +182,23 @@ extern void iounmap(void *addr); #define memcpy_toio(a,b,c) memcpy(__io_virt(a),(b),(c)) /* + * ISA space is 'always mapped' on a typical x86 system, no need to + * explicitly ioremap() it. The fact that the ISA IO space is mapped + * to PAGE_OFFSET is pure coincidence - it does not mean ISA values + * are physical addresses. The following constant pointer can be + * used as the IO-area pointer (it can be iounmapped as well, so the + * analogy with PCI is quite large): + */ +#define __ISA_IO_base ((char *)(PAGE_OFFSET)) + +#define isa_readb(a) readb(__ISA_IO_base + (a)) +#define isa_readw(a) readb(__ISA_IO_base + (a)) +#define isa_readl(a) readb(__ISA_IO_base + (a)) +#define isa_writeb(b,a) writeb(b,__ISA_IO_base + (a)) +#define isa_writew(w,a) writeb(w,__ISA_IO_base + (a)) +#define isa_writel(l,a) writeb(l,__ISA_IO_base + (a)) + +/* * Again, i386 does not require mem IO specific function. */ diff --git a/include/asm-i386/page.h b/include/asm-i386/page.h index c3719d5d9..11577168a 100644 --- a/include/asm-i386/page.h +++ b/include/asm-i386/page.h @@ -9,8 +9,6 @@ #ifdef __KERNEL__ #ifndef __ASSEMBLY__ -#define STRICT_MM_TYPECHECKS - #include <linux/config.h> #ifdef CONFIG_X86_USE_3DNOW @@ -32,13 +30,19 @@ #endif -#ifdef STRICT_MM_TYPECHECKS /* * These are used to make use of C type-checking.. */ +#if CONFIG_X86_PAE +typedef struct { unsigned long long pte; } pte_t; +typedef struct { unsigned long long pmd; } pmd_t; +typedef struct { unsigned long long pgd; } pgd_t; +#else typedef struct { unsigned long pte; } pte_t; typedef struct { unsigned long pmd; } pmd_t; typedef struct { unsigned long pgd; } pgd_t; +#endif + typedef struct { unsigned long pgprot; } pgprot_t; #define pte_val(x) ((x).pte) @@ -51,26 +55,6 @@ typedef struct { unsigned long pgprot; } pgprot_t; #define __pgd(x) ((pgd_t) { (x) } ) #define __pgprot(x) ((pgprot_t) { (x) } ) -#else -/* - * .. while these make it easier on the compiler - */ -typedef unsigned long pte_t; -typedef unsigned long pmd_t; -typedef unsigned long pgd_t; -typedef unsigned long pgprot_t; - -#define pte_val(x) (x) -#define pmd_val(x) (x) -#define pgd_val(x) (x) -#define pgprot_val(x) (x) - -#define __pte(x) (x) -#define __pmd(x) (x) -#define __pgd(x) (x) -#define __pgprot(x) (x) - -#endif #endif /* !__ASSEMBLY__ */ /* to align the pointer to the (next) page boundary */ @@ -83,29 +67,26 @@ typedef unsigned long pgprot_t; * * A __PAGE_OFFSET of 0xC0000000 means that the kernel has * a virtual address space of one gigabyte, which limits the - * amount of physical memory you can use to about 950MB. If - * you want to use more physical memory, change this define. - * - * For example, if you have 2GB worth of physical memory, you - * could change this define to 0x80000000, which gives the - * kernel 2GB of virtual memory (enough to most of your physical memory - * as the kernel needs a bit extra for various io-memory mappings) - * - * IF YOU CHANGE THIS, PLEASE ALSO CHANGE - * - * arch/i386/vmlinux.lds + * amount of physical memory you can use to about 950MB. * - * which has the same constant encoded.. + * If you want more physical memory than this then see the CONFIG_BIGMEM + * option in the kernel configuration. */ -#include <asm/page_offset.h> - -#define __PAGE_OFFSET (PAGE_OFFSET_RAW) +#define __PAGE_OFFSET (0xC0000000) #ifndef __ASSEMBLY__ +extern int console_loglevel; + +/* + * Tell the user there is some problem. Beep too, so we can + * see^H^H^Hhear bugs in early bootup as well! + */ #define BUG() do { \ + __asm__ __volatile__ ("movb $0x3,%al; outb %al,$0x61"); \ printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); \ + console_loglevel = 0; \ __asm__ __volatile__(".byte 0x0f,0x0b"); \ } while (0) diff --git a/include/asm-i386/page_offset.h b/include/asm-i386/page_offset.h deleted file mode 100644 index bb3a64dc1..000000000 --- a/include/asm-i386/page_offset.h +++ /dev/null @@ -1,8 +0,0 @@ -#include <linux/config.h> -#ifdef CONFIG_1GB -#define PAGE_OFFSET_RAW 0xC0000000 -#elif defined(CONFIG_2GB) -#define PAGE_OFFSET_RAW 0x80000000 -#elif defined(CONFIG_3GB) -#define PAGE_OFFSET_RAW 0x40000000 -#endif diff --git a/include/asm-i386/pgtable-2level.h b/include/asm-i386/pgtable-2level.h new file mode 100644 index 000000000..a8a05239c --- /dev/null +++ b/include/asm-i386/pgtable-2level.h @@ -0,0 +1,62 @@ +#ifndef _I386_PGTABLE_2LEVEL_H +#define _I386_PGTABLE_2LEVEL_H + +/* + * traditional i386 two-level paging structure: + */ + +#define PGDIR_SHIFT 22 +#define PTRS_PER_PGD 1024 + +/* + * the i386 is two-level, so we don't really have any + * PMD directory physically. + */ +#define PMD_SHIFT 22 +#define PTRS_PER_PMD 1 + +#define PTRS_PER_PTE 1024 + +#define pte_ERROR(e) \ + printk("%s:%d: bad pte %08lx.\n", __FILE__, __LINE__, pte_val(e)) +#define pmd_ERROR(e) \ + printk("%s:%d: bad pmd %08lx.\n", __FILE__, __LINE__, pmd_val(e)) +#define pgd_ERROR(e) \ + printk("%s:%d: bad pgd %08lx.\n", __FILE__, __LINE__, pgd_val(e)) + +/* + * The "pgd_xxx()" functions here are trivial for a folded two-level + * setup: the pgd is never bad, and a pmd always exists (as it's folded + * into the pgd entry) + */ +extern inline int pgd_none(pgd_t pgd) { return 0; } +extern inline int pgd_bad(pgd_t pgd) { return 0; } +extern inline int pgd_present(pgd_t pgd) { return 1; } +#define pgd_clear(xp) do { pgd_val(*(xp)) = 0; } while (0) + +#define pgd_page(pgd) \ +((unsigned long) __va(pgd_val(pgd) & PAGE_MASK)) + +extern inline pmd_t * pmd_offset(pgd_t * dir, unsigned long address) +{ + return (pmd_t *) dir; +} + +extern __inline__ pmd_t *get_pmd_fast(void) +{ + return (pmd_t *)0; +} + +extern __inline__ void free_pmd_fast(pmd_t *pmd) { } +extern __inline__ void free_pmd_slow(pmd_t *pmd) { } + +extern inline pmd_t * pmd_alloc(pgd_t *pgd, unsigned long address) +{ + if (!pgd) + BUG(); + return (pmd_t *) pgd; +} + +#define SWP_ENTRY(type,offset) __pte((((type) << 1) | ((offset) << 8))) + +#endif /* _I386_PGTABLE_2LEVEL_H */ diff --git a/include/asm-i386/pgtable-3level.h b/include/asm-i386/pgtable-3level.h new file mode 100644 index 000000000..99d718115 --- /dev/null +++ b/include/asm-i386/pgtable-3level.h @@ -0,0 +1,131 @@ +#ifndef _I386_PGTABLE_3LEVEL_H +#define _I386_PGTABLE_3LEVEL_H + +/* + * Intel Physical Address Extension (PAE) Mode - three-level page + * tables on PPro+ CPUs. + * + * Copyright (C) 1999 Ingo Molnar <mingo@redhat.com> + */ + +/* + * PGDIR_SHIFT determines what a top-level page table entry can map + */ +#define PGDIR_SHIFT 30 +#define PTRS_PER_PGD 4 + +/* + * PMD_SHIFT determines the size of the area a middle-level + * page table can map + */ +#define PMD_SHIFT 21 +#define PTRS_PER_PMD 512 + +/* + * entries per page directory level + */ +#define PTRS_PER_PTE 512 + +#define pte_ERROR(e) \ + printk("%s:%d: bad pte %016Lx.\n", __FILE__, __LINE__, pte_val(e)) +#define pmd_ERROR(e) \ + printk("%s:%d: bad pmd %016Lx.\n", __FILE__, __LINE__, pmd_val(e)) +#define pgd_ERROR(e) \ + printk("%s:%d: bad pgd %016Lx.\n", __FILE__, __LINE__, pgd_val(e)) + +/* + * Subtle, in PAE mode we cannot have zeroes in the top level + * page directory, the CPU enforces this. + */ +#define pgd_none(x) (pgd_val(x) == 1ULL) +extern inline int pgd_bad(pgd_t pgd) { return 0; } +extern inline int pgd_present(pgd_t pgd) { return !pgd_none(pgd); } +/* + * Pentium-II errata A13: in PAE mode we explicitly have to flush + * the TLB via cr3 if the top-level pgd is changed... This was one tough + * thing to find out - guess i should first read all the documentation + * next time around ;) + */ +extern inline void __pgd_clear (pgd_t * pgd) +{ + pgd_val(*pgd) = 1; // no zero allowed! +} + +extern inline void pgd_clear (pgd_t * pgd) +{ + __pgd_clear(pgd); + __flush_tlb(); +} + +#define pgd_page(pgd) \ +((unsigned long) __va(pgd_val(pgd) & PAGE_MASK)) + +/* Find an entry in the second-level page table.. */ +#define pmd_offset(dir, address) ((pmd_t *) pgd_page(*(dir)) + \ + __pmd_offset(address)) + +extern __inline__ pmd_t *get_pmd_slow(void) +{ + pmd_t *ret = (pmd_t *)__get_free_page(GFP_KERNEL); + + if (ret) + memset(ret, 0, PAGE_SIZE); + return ret; +} + +extern __inline__ pmd_t *get_pmd_fast(void) +{ + unsigned long *ret; + + if ((ret = pmd_quicklist) != NULL) { + pmd_quicklist = (unsigned long *)(*ret); + ret[0] = 0; + pgtable_cache_size--; + } else + ret = (unsigned long *)get_pmd_slow(); + return (pmd_t *)ret; +} + +extern __inline__ void free_pmd_fast(pmd_t *pmd) +{ + *(unsigned long *)pmd = (unsigned long) pmd_quicklist; + pmd_quicklist = (unsigned long *) pmd; + pgtable_cache_size++; +} + +extern __inline__ void free_pmd_slow(pmd_t *pmd) +{ + free_page((unsigned long)pmd); +} + +extern inline pmd_t * pmd_alloc(pgd_t *pgd, unsigned long address) +{ + if (!pgd) + BUG(); + address = (address >> PMD_SHIFT) & (PTRS_PER_PMD - 1); + if (pgd_none(*pgd)) { + pmd_t *page = get_pmd_fast(); + + if (!page) + page = get_pmd_slow(); + if (page) { + if (pgd_none(*pgd)) { + pgd_val(*pgd) = 1 + __pa(page); + __flush_tlb(); + return page + address; + } else + free_pmd_fast(page); + } else + return NULL; + } + return (pmd_t *)pgd_page(*pgd) + address; +} + +/* + * Subtle. offset can overflow 32 bits and that's a feature - we can do + * up to 16 TB swap on PAE. (Not that anyone should need that much + * swapspace, but who knows?) + */ +#define SWP_ENTRY(type,offset) __pte((((type) << 1) | ((offset) << 8ULL))) + +#endif /* _I386_PGTABLE_3LEVEL_H */ diff --git a/include/asm-i386/pgtable.h b/include/asm-i386/pgtable.h index 36303437b..abd454b40 100644 --- a/include/asm-i386/pgtable.h +++ b/include/asm-i386/pgtable.h @@ -44,7 +44,7 @@ extern pgd_t swapper_pg_dir[1024]; do { unsigned long tmpreg; __asm__ __volatile__("movl %%cr3,%0\n\tmovl %0,%%cr3":"=r" (tmpreg) : :"memory"); } while (0) #ifndef CONFIG_X86_INVLPG -#define __flush_tlb_one(addr) flush_tlb() +#define __flush_tlb_one(addr) __flush_tlb() #else #define __flush_tlb_one(addr) \ __asm__ __volatile__("invlpg %0": :"m" (*(char *) addr)) @@ -100,44 +100,50 @@ static inline void flush_tlb_range(struct mm_struct * mm, unsigned long start, u flush_tlb_mm(mm); } - #endif #endif /* !__ASSEMBLY__ */ +#define pgd_quicklist (current_cpu_data.pgd_quick) +#define pmd_quicklist (current_cpu_data.pmd_quick) +#define pte_quicklist (current_cpu_data.pte_quick) +#define pgtable_cache_size (current_cpu_data.pgtable_cache_sz) + +/* + * The Linux x86 paging architecture is 'compile-time dual-mode', it + * implements both the traditional 2-level x86 page tables and the + * newer 3-level PAE-mode page tables. + */ +#ifndef __ASSEMBLY__ +#if CONFIG_X86_PAE +# include <asm/pgtable-3level.h> +#else +# include <asm/pgtable-2level.h> +#endif +#endif -/* Certain architectures need to do special things when PTEs +/* + * Certain architectures need to do special things when PTEs * within a page table are directly modified. Thus, the following * hook is made available. */ #define set_pte(pteptr, pteval) ((*(pteptr)) = (pteval)) -/* PMD_SHIFT determines the size of the area a second-level page table can map */ -#define PMD_SHIFT 22 +#define __beep() asm("movb $0x3,%al; outb %al,$0x61") + #define PMD_SIZE (1UL << PMD_SHIFT) #define PMD_MASK (~(PMD_SIZE-1)) - -/* PGDIR_SHIFT determines what a third-level page table entry can map */ -#define PGDIR_SHIFT 22 #define PGDIR_SIZE (1UL << PGDIR_SHIFT) #define PGDIR_MASK (~(PGDIR_SIZE-1)) -/* - * entries per page directory level: the i386 is two-level, so - * we don't really have any PMD directory physically. - */ -#define PTRS_PER_PTE 1024 -#define PTRS_PER_PMD 1 -#define PTRS_PER_PGD 1024 #define USER_PTRS_PER_PGD (TASK_SIZE/PGDIR_SIZE) -/* - * pgd entries used up by user/kernel: - */ - #define USER_PGD_PTRS (PAGE_OFFSET >> PGDIR_SHIFT) #define KERNEL_PGD_PTRS (PTRS_PER_PGD-USER_PGD_PTRS) -#define __USER_PGD_PTRS ((__PAGE_OFFSET >> PGDIR_SHIFT) & 0x3ff) -#define __KERNEL_PGD_PTRS (PTRS_PER_PGD-__USER_PGD_PTRS) + +#define TWOLEVEL_PGDIR_SHIFT 22 +#define BOOT_USER_PGD_PTRS (__PAGE_OFFSET >> TWOLEVEL_PGDIR_SHIFT) +#define BOOT_KERNEL_PGD_PTRS (1024-BOOT_USER_PGD_PTRS) + #ifndef __ASSEMBLY__ /* Just any arbitrary offset to the start of the vmalloc VM area: the @@ -166,7 +172,7 @@ static inline void flush_tlb_range(struct mm_struct * mm, unsigned long start, u #define _PAGE_PCD 0x010 #define _PAGE_ACCESSED 0x020 #define _PAGE_DIRTY 0x040 -#define _PAGE_4M 0x080 /* 4 MB page, Pentium+, if present.. */ +#define _PAGE_PSE 0x080 /* 4 MB (or 2MB) page, Pentium+, if present.. */ #define _PAGE_GLOBAL 0x100 /* Global TLB entry PPro+ */ #define _PAGE_PROTNONE 0x080 /* If not present */ @@ -213,40 +219,24 @@ static inline void flush_tlb_range(struct mm_struct * mm, unsigned long start, u /* page table for 0-4MB for everybody */ extern unsigned long pg0[1024]; -/* zero page used for uninitialized stuff */ -extern unsigned long empty_zero_page[1024]; /* - * BAD_PAGETABLE is used when we need a bogus page-table, while - * BAD_PAGE is used for a bogus page. - * * ZERO_PAGE is a global shared page that is always zero: used * for zero-mapped memory areas etc.. */ -extern pte_t __bad_page(void); -extern pte_t * __bad_pagetable(void); - -#define BAD_PAGETABLE __bad_pagetable() -#define BAD_PAGE __bad_page() +extern unsigned long empty_zero_page[1024]; #define ZERO_PAGE(vaddr) ((unsigned long) empty_zero_page) -/* number of bits that fit into a memory pointer */ -#define BITS_PER_PTR (8*sizeof(unsigned long)) - -/* to align the pointer to a pointer address */ -#define PTR_MASK (~(sizeof(void*)-1)) - -/* sizeof(void*)==1<<SIZEOF_PTR_LOG2 */ -/* 64-bit machines, beware! SRB. */ -#define SIZEOF_PTR_LOG2 2 - -/* to find an entry in a page-table */ -#define PAGE_PTR(address) \ -((unsigned long)(address)>>(PAGE_SHIFT-SIZEOF_PTR_LOG2)&PTR_MASK&~PAGE_MASK) +/* + * Handling allocation failures during page table setup. + */ +extern void __handle_bad_pmd (pmd_t * pmd); +extern void __handle_bad_pmd_kernel(pmd_t * pmd); #define pte_none(x) (!pte_val(x)) #define pte_present(x) (pte_val(x) & (_PAGE_PRESENT | _PAGE_PROTNONE)) #define pte_clear(xp) do { pte_val(*(xp)) = 0; } while (0) +#define pte_pagenr(x) ((unsigned long)((pte_val(x) >> PAGE_SHIFT))) #define pmd_none(x) (!pmd_val(x)) #define pmd_bad(x) ((pmd_val(x) & (~PAGE_MASK & ~_PAGE_USER)) != _KERNPG_TABLE) @@ -254,14 +244,12 @@ extern pte_t * __bad_pagetable(void); #define pmd_clear(xp) do { pmd_val(*(xp)) = 0; } while (0) /* - * The "pgd_xxx()" functions here are trivial for a folded two-level - * setup: the pgd is never bad, and a pmd always exists (as it's folded - * into the pgd entry) + * Permanent address of a page. Obviously must never be + * called on a highmem page. */ -extern inline int pgd_none(pgd_t pgd) { return 0; } -extern inline int pgd_bad(pgd_t pgd) { return 0; } -extern inline int pgd_present(pgd_t pgd) { return 1; } -extern inline void pgd_clear(pgd_t * pgdp) { } +#define page_address(page) ({ if (PageHighMem(page)) BUG(); PAGE_OFFSET + (((page) - mem_map) << PAGE_SHIFT); }) +#define pages_to_mb(x) ((x) >> (20-PAGE_SHIFT)) +#define pte_page(x) (mem_map+pte_pagenr(x)) /* * The following only work if pte_present() is true. @@ -288,8 +276,15 @@ extern inline pte_t pte_mkwrite(pte_t pte) { pte_val(pte) |= _PAGE_RW; return pt * Conversion functions: convert a page and protection to a page entry, * and a page entry and page directory to the page they refer to. */ -#define mk_pte(page, pgprot) \ -({ pte_t __pte; pte_val(__pte) = __pa(page) + pgprot_val(pgprot); __pte; }) + +extern inline pte_t mk_pte(struct page *page, pgprot_t pgprot) +{ + pte_t __pte; + + pte_val(__pte) = (page-mem_map)*(unsigned long long)PAGE_SIZE + + pgprot_val(pgprot); + return __pte; +} /* This takes a physical page address that is used by the remapping functions */ #define mk_pte_phys(physpage, pgprot) \ @@ -298,28 +293,29 @@ extern inline pte_t pte_mkwrite(pte_t pte) { pte_val(pte) |= _PAGE_RW; return pt extern inline pte_t pte_modify(pte_t pte, pgprot_t newprot) { pte_val(pte) = (pte_val(pte) & _PAGE_CHG_MASK) | pgprot_val(newprot); return pte; } -#define pte_page(pte) \ -((unsigned long) __va(pte_val(pte) & PAGE_MASK)) +#define page_pte_prot(page,prot) mk_pte(page, prot) +#define page_pte(page) page_pte_prot(page, __pgprot(0)) #define pmd_page(pmd) \ ((unsigned long) __va(pmd_val(pmd) & PAGE_MASK)) -/* to find an entry in a page-table-directory */ -#define pgd_offset(mm, address) \ -((mm)->pgd + ((address) >> PGDIR_SHIFT)) +/* to find an entry in a page-table-directory. */ +#define __pgd_offset(address) \ + ((address >> PGDIR_SHIFT) & (PTRS_PER_PGD-1)) + +#define pgd_offset(mm, address) ((mm)->pgd+__pgd_offset(address)) /* to find an entry in a kernel page-table-directory */ #define pgd_offset_k(address) pgd_offset(&init_mm, address) -/* Find an entry in the second-level page table.. */ -extern inline pmd_t * pmd_offset(pgd_t * dir, unsigned long address) -{ - return (pmd_t *) dir; -} +#define __pmd_offset(address) \ + (((address) >> PMD_SHIFT) & (PTRS_PER_PMD-1)) -/* Find an entry in the third-level page table.. */ -#define pte_offset(pmd, address) \ -((pte_t *) (pmd_page(*pmd) + ((address>>10) & ((PTRS_PER_PTE-1)<<2)))) +/* Find an entry in the third-level page table.. */ +#define __pte_offset(address) \ + ((address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1)) +#define pte_offset(dir, address) ((pte_t *) pmd_page(*(dir)) + \ + __pte_offset(address)) /* * Allocate and free page tables. The xxx_kernel() versions are @@ -327,17 +323,25 @@ extern inline pmd_t * pmd_offset(pgd_t * dir, unsigned long address) * if any. */ -#define pgd_quicklist (current_cpu_data.pgd_quick) -#define pmd_quicklist ((unsigned long *)0) -#define pte_quicklist (current_cpu_data.pte_quick) -#define pgtable_cache_size (current_cpu_data.pgtable_cache_sz) - extern __inline__ pgd_t *get_pgd_slow(void) { pgd_t *ret = (pgd_t *)__get_free_page(GFP_KERNEL); if (ret) { +#if 0 + /* + * On PAE allocating a whole page is overkill - we will + * either embedd this in mm_struct, or do a SLAB cache. + */ + memcpy(ret, swapper_pg_dir, PTRS_PER_PGD * sizeof(pgd_t)); +#endif +#if CONFIG_X86_PAE + int i; + for (i = 0; i < USER_PTRS_PER_PGD; i++) + __pgd_clear(ret + i); +#else memset(ret, 0, USER_PTRS_PER_PGD * sizeof(pgd_t)); +#endif memcpy(ret + USER_PTRS_PER_PGD, swapper_pg_dir + USER_PTRS_PER_PGD, (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t)); } return ret; @@ -395,30 +399,15 @@ extern __inline__ void free_pte_slow(pte_t *pte) free_page((unsigned long)pte); } -/* We don't use pmd cache, so these are dummy routines */ -extern __inline__ pmd_t *get_pmd_fast(void) -{ - return (pmd_t *)0; -} - -extern __inline__ void free_pmd_fast(pmd_t *pmd) -{ -} - -extern __inline__ void free_pmd_slow(pmd_t *pmd) -{ -} - -extern void __bad_pte(pmd_t *pmd); -extern void __bad_pte_kernel(pmd_t *pmd); - #define pte_free_kernel(pte) free_pte_slow(pte) -#define pte_free(pte) free_pte_slow(pte) -#define pgd_free(pgd) free_pgd_slow(pgd) -#define pgd_alloc() get_pgd_fast() +#define pte_free(pte) free_pte_slow(pte) +#define pgd_free(pgd) free_pgd_slow(pgd) +#define pgd_alloc() get_pgd_fast() extern inline pte_t * pte_alloc_kernel(pmd_t * pmd, unsigned long address) { + if (!pmd) + BUG(); address = (address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1); if (pmd_none(*pmd)) { pte_t * page = (pte_t *) get_pte_fast(); @@ -429,7 +418,7 @@ extern inline pte_t * pte_alloc_kernel(pmd_t * pmd, unsigned long address) return page + address; } if (pmd_bad(*pmd)) { - __bad_pte_kernel(pmd); + __handle_bad_pmd_kernel(pmd); return NULL; } return (pte_t *) pmd_page(*pmd) + address; @@ -437,13 +426,13 @@ extern inline pte_t * pte_alloc_kernel(pmd_t * pmd, unsigned long address) extern inline pte_t * pte_alloc(pmd_t * pmd, unsigned long address) { - address = (address >> (PAGE_SHIFT-2)) & 4*(PTRS_PER_PTE - 1); + address = (address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1); if (pmd_none(*pmd)) goto getnew; if (pmd_bad(*pmd)) goto fix; - return (pte_t *) (pmd_page(*pmd) + address); + return (pte_t *)pmd_page(*pmd) + address; getnew: { unsigned long page = (unsigned long) get_pte_fast(); @@ -451,25 +440,19 @@ getnew: if (!page) return get_pte_slow(pmd, address); pmd_val(*pmd) = _PAGE_TABLE + __pa(page); - return (pte_t *) (page + address); + return (pte_t *)page + address; } fix: - __bad_pte(pmd); + __handle_bad_pmd(pmd); return NULL; } /* * allocating and freeing a pmd is trivial: the 1-entry pmd is * inside the pgd, so has no extra memory associated with it. + * (In the PAE case we free the page.) */ -extern inline void pmd_free(pmd_t * pmd) -{ -} - -extern inline pmd_t * pmd_alloc(pgd_t * pgd, unsigned long address) -{ - return (pmd_t *) pgd; -} +#define pmd_free(pmd) free_pmd_slow(pmd) #define pmd_free_kernel pmd_free #define pmd_alloc_kernel pmd_alloc @@ -483,7 +466,7 @@ extern inline void set_pgdir(unsigned long address, pgd_t entry) #ifdef __SMP__ int i; #endif - + read_lock(&tasklist_lock); for_each_task(p) { if (!p->mm) @@ -512,9 +495,8 @@ extern inline void update_mmu_cache(struct vm_area_struct * vma, { } -#define SWP_TYPE(entry) (((entry) >> 1) & 0x3f) -#define SWP_OFFSET(entry) ((entry) >> 8) -#define SWP_ENTRY(type,offset) (((type) << 1) | ((offset) << 8)) +#define SWP_TYPE(entry) (((pte_val(entry)) >> 1) & 0x3f) +#define SWP_OFFSET(entry) ((pte_val(entry)) >> 8) #define module_map vmalloc #define module_unmap vfree @@ -527,4 +509,4 @@ extern inline void update_mmu_cache(struct vm_area_struct * vma, #define io_remap_page_range remap_page_range -#endif /* _I386_PAGE_H */ +#endif /* _I386_PGTABLE_H */ diff --git a/include/asm-i386/processor.h b/include/asm-i386/processor.h index 939ca0b31..88f066864 100644 --- a/include/asm-i386/processor.h +++ b/include/asm-i386/processor.h @@ -46,6 +46,7 @@ struct cpuinfo_x86 { int coma_bug; unsigned long loops_per_sec; unsigned long *pgd_quick; + unsigned long *pmd_quick; unsigned long *pte_quick; unsigned long pgtable_cache_sz; }; @@ -106,6 +107,12 @@ extern struct cpuinfo_x86 cpu_data[]; #define current_cpu_data boot_cpu_data #endif +#define cpu_has_pge \ + (boot_cpu_data.x86_capability & X86_FEATURE_PGE) +#define cpu_has_pse \ + (boot_cpu_data.x86_capability & X86_FEATURE_PSE) +#define cpu_has_pae \ + (boot_cpu_data.x86_capability & X86_FEATURE_PAE) #define cpu_has_tsc \ (cpu_data[smp_processor_id()].x86_capability & X86_FEATURE_TSC) diff --git a/include/asm-i386/smp.h b/include/asm-i386/smp.h index 52c27bead..2aa6aec4e 100644 --- a/include/asm-i386/smp.h +++ b/include/asm-i386/smp.h @@ -166,7 +166,8 @@ struct mpc_config_lintsrc extern int smp_found_config; extern void init_smp_config(void); -extern unsigned long smp_alloc_memory(unsigned long mem_base); +extern void init_smp_mappings(void); +extern void smp_alloc_memory(void); extern unsigned long cpu_present_map; extern unsigned long cpu_online_map; extern volatile unsigned long smp_invalidate_needed; @@ -179,6 +180,7 @@ extern void smp_invalidate_rcv(void); /* Process an NMI */ extern void smp_local_timer_interrupt(struct pt_regs * regs); extern void (*mtrr_hook) (void); extern void setup_APIC_clocks(void); +extern void zap_low_mappings (void); extern volatile int cpu_number_map[NR_CPUS]; extern volatile int __cpu_logical_map[NR_CPUS]; extern inline int cpu_logical_map(int cpu) diff --git a/include/asm-mips/highmem.h b/include/asm-mips/highmem.h new file mode 100644 index 000000000..fe6b44516 --- /dev/null +++ b/include/asm-mips/highmem.h @@ -0,0 +1,27 @@ +/* $Id$ + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2000 by Ralf Baechle + * Copyright (C) 2000 Silicon Graphics, Inc. + */ +#ifndef _ASM_HIGHMEM_H +#define _ASM_HIGHMEM_H + +#include <linux/init.h> +#include <asm/pgtable.h> + +/* declarations for highmem.c */ +extern unsigned long highstart_pfn, highend_pfn; + +extern pte_t *kmap_pte; +extern pgprot_t kmap_prot; + +#define kmap_init() do { } while(0) +#define kmap(page, type) page_address(page) +#define kunmap(vaddr, type) do { } while(0) +#define kmap_check() do { } while(0) + +#endif /* _ASM_HIGHMEM_H */ diff --git a/include/asm-mips/io.h b/include/asm-mips/io.h index 20bdbeb34..d4c77dd78 100644 --- a/include/asm-mips/io.h +++ b/include/asm-mips/io.h @@ -1,4 +1,4 @@ -/* $Id$ +/* $Id: io.h,v 1.7 1999/10/09 00:01:43 ralf Exp $ * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive @@ -174,6 +174,23 @@ extern inline void iounmap(void *addr) /* END SNI HACKS ... */ /* + * ISA space is 'always mapped' on currently supported MIPS systems, no need + * to explicitly ioremap() it. The fact that the ISA IO space is mapped + * to PAGE_OFFSET is pure coincidence - it does not mean ISA values + * are physical addresses. The following constant pointer can be + * used as the IO-area pointer (it can be iounmapped as well, so the + * analogy with PCI is quite large): + */ +#define __ISA_IO_base ((char *)(PAGE_OFFSET)) + +#define isa_readb(a) readb(a) +#define isa_readw(a) readb(a) +#define isa_readl(a) readb(a) +#define isa_writeb(b,a) writeb(b,a) +#define isa_writew(w,a) writeb(w,a) +#define isa_writel(l,a) writeb(l,a) + +/* * We don't have csum_partial_copy_fromio() yet, so we cheat here and * just copy it. The net code will then do the checksum later. */ diff --git a/include/asm-mips/offset.h b/include/asm-mips/offset.h index 69a32238a..ba0b87698 100644 --- a/include/asm-mips/offset.h +++ b/include/asm-mips/offset.h @@ -52,7 +52,7 @@ #define TASK_COUNTER 24 #define TASK_PRIORITY 28 #define TASK_MM 792 -#define TASK_STRUCT_SIZE 856 +#define TASK_STRUCT_SIZE 864 /* MIPS specific thread_struct offsets. */ #define THREAD_REG16 568 diff --git a/include/asm-mips/page.h b/include/asm-mips/page.h index c24112ea1..0fb32cb97 100644 --- a/include/asm-mips/page.h +++ b/include/asm-mips/page.h @@ -1,4 +1,4 @@ -/* $Id: page.h,v 1.6 1999/01/04 16:09:24 ralf Exp $ +/* $Id: page.h,v 1.7 1999/06/22 23:07:47 ralf Exp $ * * Definitions for page handling * @@ -8,8 +8,8 @@ * * Copyright (C) 1994 - 1999 by Ralf Baechle */ -#ifndef __ASM_MIPS_PAGE_H -#define __ASM_MIPS_PAGE_H +#ifndef __ASM_PAGE_H +#define __ASM_PAGE_H /* PAGE_SHIFT determines the page size */ #define PAGE_SHIFT 12 @@ -18,17 +18,14 @@ #ifdef __KERNEL__ -#define STRICT_MM_TYPECHECKS - #ifndef _LANGUAGE_ASSEMBLY #define BUG() do { printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); *(int *)0=0; } while (0) #define PAGE_BUG(page) do { BUG(); } while (0) -extern void (*clear_page)(unsigned long page); -extern void (*copy_page)(unsigned long to, unsigned long from); +extern void (*clear_page)(void * page); +extern void (*copy_page)(void * to, void * from); -#ifdef STRICT_MM_TYPECHECKS /* * These are used to make use of C type-checking.. */ @@ -47,27 +44,6 @@ typedef struct { unsigned long pgprot; } pgprot_t; #define __pgd(x) ((pgd_t) { (x) } ) #define __pgprot(x) ((pgprot_t) { (x) } ) -#else /* !defined (STRICT_MM_TYPECHECKS) */ -/* - * .. while these make it easier on the compiler - */ -typedef unsigned long pte_t; -typedef unsigned long pmd_t; -typedef unsigned long pgd_t; -typedef unsigned long pgprot_t; - -#define pte_val(x) (x) -#define pmd_val(x) (x) -#define pgd_val(x) (x) -#define pgprot_val(x) (x) - -#define __pte(x) (x) -#define __pmd(x) (x) -#define __pgd(x) (x) -#define __pgprot(x) (x) - -#endif /* !defined (STRICT_MM_TYPECHECKS) */ - #endif /* _LANGUAGE_ASSEMBLY */ /* to align the pointer to the (next) page boundary */ @@ -84,4 +60,4 @@ typedef unsigned long pgprot_t; #endif /* defined (__KERNEL__) */ -#endif /* __ASM_MIPS_PAGE_H */ +#endif /* __ASM_PAGE_H */ diff --git a/include/asm-mips/pgtable.h b/include/asm-mips/pgtable.h index 1dc438914..66ffa79f2 100644 --- a/include/asm-mips/pgtable.h +++ b/include/asm-mips/pgtable.h @@ -1,4 +1,4 @@ -/* $Id: pgtable.h,v 1.24 1999/08/18 23:37:49 ralf Exp $ +/* $Id: pgtable.h,v 1.25 1999/10/09 00:01:43 ralf Exp $ * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive @@ -32,7 +32,7 @@ extern void (*flush_cache_range)(struct mm_struct *mm, unsigned long start, unsigned long end); extern void (*flush_cache_page)(struct vm_area_struct *vma, unsigned long page); extern void (*flush_cache_sigtramp)(unsigned long addr); -extern void (*flush_page_to_ram)(unsigned long page); +extern void (*flush_page_to_ram)(struct page * page); #define flush_icache_range(start, end) flush_cache_all() /* TLB flushing: @@ -187,6 +187,13 @@ extern void add_wired_entry(unsigned long entrylo0, unsigned long entrylo1, #if !defined (_LANGUAGE_ASSEMBLY) +#define pte_ERROR(e) \ + printk("%s:%d: bad pte %016lx.\n", __FILE__, __LINE__, pte_val(e)) +#define pmd_ERROR(e) \ + printk("%s:%d: bad pmd %016lx.\n", __FILE__, __LINE__, pmd_val(e)) +#define pgd_ERROR(e) \ + printk("%s:%d: bad pgd %016lx.\n", __FILE__, __LINE__, pgd_val(e)) + /* * BAD_PAGETABLE is used when we need a bogus page-table, while * BAD_PAGE is used for a bogus page. @@ -202,8 +209,8 @@ extern unsigned long zero_page_mask; #define BAD_PAGETABLE __bad_pagetable() #define BAD_PAGE __bad_page() -#define ZERO_PAGE(__vaddr) \ - (empty_zero_page + (((unsigned long)(__vaddr)) & zero_page_mask)) +#define ZERO_PAGE(vaddr) \ + (mem_map + MAP_NR(empty_zero_page + (((unsigned long)(vaddr)) & zero_page_mask))) /* number of bits that fit into a memory pointer */ #define BITS_PER_PTR (8*sizeof(unsigned long)) @@ -260,6 +267,8 @@ extern inline void pte_clear(pte_t *ptep) set_pte(ptep, __pte(0)); } +#define pte_pagenr(x) ((unsigned long)((pte_val(x) >> PAGE_SHIFT))) + /* * Empty pgd/pmd entries point to the invalid_pte_table. */ @@ -295,6 +304,16 @@ extern inline int pgd_present(pgd_t pgd) { return 1; } extern inline void pgd_clear(pgd_t *pgdp) { } /* + * Permanent address of a page. On MIPS64 we never have highmem, so this + * is simple. + * called on a highmem page. + */ +#define page_address(page) \ + (PAGE_OFFSET + (((page) - mem_map) << PAGE_SHIFT)) +#define pages_to_mb(x) ((x) >> (20-PAGE_SHIFT)) /* Unused? */ +#define pte_page(x) (mem_map+pte_pagenr(x)) + +/* * The following only work if pte_present() is true. * Undefined behaviour if not.. */ @@ -363,9 +382,14 @@ extern inline pte_t pte_mkyoung(pte_t pte) * Conversion functions: convert a page and protection to a page entry, * and a page entry and page directory to the page they refer to. */ -extern inline pte_t mk_pte(unsigned long page, pgprot_t pgprot) +extern inline pte_t mk_pte(struct page * page, pgprot_t pgprot) { - return __pte(((page & PAGE_MASK) - PAGE_OFFSET) | pgprot_val(pgprot)); + pte_t __pte; + + pte_val(__pte) = ((unsigned long)(page - mem_map) << PAGE_SHIFT) | + pgprot_val(pgprot); + + return __pte; } extern inline pte_t mk_pte_phys(unsigned long physpage, pgprot_t pgprot) @@ -378,6 +402,9 @@ extern inline pte_t pte_modify(pte_t pte, pgprot_t newprot) return __pte((pte_val(pte) & _PAGE_CHG_MASK) | pgprot_val(newprot)); } +#define page_pte_prot(page,prot) mk_pte(page, prot) +#define page_pte(page) page_pte_prot(page, __pgprot(0)) + /* to find an entry in a kernel page-table-directory */ #define pgd_offset_k(address) pgd_offset(&init_mm, address) @@ -592,11 +619,21 @@ extern void update_mmu_cache(struct vm_area_struct *vma, unsigned long address, pte_t pte); /* + * Non-present pages: high 24 bits are offset, next 8 bits type, + * low 32 bits zero. + */ +extern inline pte_t mk_swap_pte(unsigned long type, unsigned long offset) +{ + pte_t pte; pte_val(pte) = (type << 1) | (offset << 8); + return pte; +} + +/* * Kernel with 32 bit address space */ -#define SWP_TYPE(entry) (((entry) >> 8) & 0x7f) -#define SWP_OFFSET(entry) ((entry) >> 15) -#define SWP_ENTRY(type,offset) (((type) << 8) | ((offset) << 15)) +#define SWP_TYPE(entry) ((pte_val(entry) >> 1) & 0x3f) +#define SWP_OFFSET(entry) (pte_val(entry) >> 8) +#define SWP_ENTRY(type,offset) mk_swap_pte((type),(offset)) #define module_map vmalloc #define module_unmap vfree diff --git a/include/asm-mips64/highmem.h b/include/asm-mips64/highmem.h new file mode 100644 index 000000000..fe6b44516 --- /dev/null +++ b/include/asm-mips64/highmem.h @@ -0,0 +1,27 @@ +/* $Id$ + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2000 by Ralf Baechle + * Copyright (C) 2000 Silicon Graphics, Inc. + */ +#ifndef _ASM_HIGHMEM_H +#define _ASM_HIGHMEM_H + +#include <linux/init.h> +#include <asm/pgtable.h> + +/* declarations for highmem.c */ +extern unsigned long highstart_pfn, highend_pfn; + +extern pte_t *kmap_pte; +extern pgprot_t kmap_prot; + +#define kmap_init() do { } while(0) +#define kmap(page, type) page_address(page) +#define kunmap(vaddr, type) do { } while(0) +#define kmap_check() do { } while(0) + +#endif /* _ASM_HIGHMEM_H */ diff --git a/include/asm-mips64/io.h b/include/asm-mips64/io.h index 3114b1070..09976d4b0 100644 --- a/include/asm-mips64/io.h +++ b/include/asm-mips64/io.h @@ -1,4 +1,4 @@ -/* $Id: io.h,v 1.3 2000/01/17 23:32:47 ralf Exp $ +/* $Id: io.h,v 1.4 2000/01/25 21:58:42 kanoj Exp $ * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive @@ -157,6 +157,23 @@ extern inline void iounmap(void *addr) /* END SNI HACKS ... */ /* + * ISA space is 'always mapped' on currently supported MIPS systems, no need + * to explicitly ioremap() it. The fact that the ISA IO space is mapped + * to PAGE_OFFSET is pure coincidence - it does not mean ISA values + * are physical addresses. The following constant pointer can be + * used as the IO-area pointer (it can be iounmapped as well, so the + * analogy with PCI is quite large): + */ +#define __ISA_IO_base ((char *)(PAGE_OFFSET)) + +#define isa_readb(a) readb(a) +#define isa_readw(a) readb(a) +#define isa_readl(a) readb(a) +#define isa_writeb(b,a) writeb(b,a) +#define isa_writew(w,a) writeb(w,a) +#define isa_writel(l,a) writeb(l,a) + +/* * We don't have csum_partial_copy_fromio() yet, so we cheat here and * just copy it. The net code will then do the checksum later. */ diff --git a/include/asm-mips64/offset.h b/include/asm-mips64/offset.h index f2451394c..8fc30f570 100644 --- a/include/asm-mips64/offset.h +++ b/include/asm-mips64/offset.h @@ -52,7 +52,7 @@ #define TASK_COUNTER 48 #define TASK_PRIORITY 56 #define TASK_MM 1304 -#define TASK_STRUCT_SIZE 1392 +#define TASK_STRUCT_SIZE 1400 /* MIPS specific thread_struct offsets. */ #define THREAD_REG16 864 diff --git a/include/asm-mips64/page.h b/include/asm-mips64/page.h index 607da8bb4..71373c9b6 100644 --- a/include/asm-mips64/page.h +++ b/include/asm-mips64/page.h @@ -1,4 +1,4 @@ -/* $Id: page.h,v 1.2 1999/12/04 03:59:12 ralf Exp $ +/* $Id: page.h,v 1.3 2000/01/17 23:32:47 ralf Exp $ * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive @@ -19,17 +19,14 @@ #ifdef __KERNEL__ -#define STRICT_MM_TYPECHECKS - #ifndef _LANGUAGE_ASSEMBLY #define BUG() do { printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); *(int *)0=0; } while (0) #define PAGE_BUG(page) do { BUG(); } while (0) -extern void (*clear_page)(unsigned long page); -extern void (*copy_page)(unsigned long to, unsigned long from); +extern void (*clear_page)(void * page); +extern void (*copy_page)(void * to, void * from); -#ifdef STRICT_MM_TYPECHECKS /* * These are used to make use of C type-checking.. */ @@ -48,27 +45,6 @@ typedef struct { unsigned long pgprot; } pgprot_t; #define __pgd(x) ((pgd_t) { (x) } ) #define __pgprot(x) ((pgprot_t) { (x) } ) -#else /* !defined (STRICT_MM_TYPECHECKS) */ -/* - * .. while these make it easier on the compiler - */ -typedef unsigned long pte_t; -typedef unsigned long pmd_t; -typedef unsigned long pgd_t; -typedef unsigned long pgprot_t; - -#define pte_val(x) (x) -#define pmd_val(x) (x) -#define pgd_val(x) (x) -#define pgprot_val(x) (x) - -#define __pte(x) (x) -#define __pmd(x) (x) -#define __pgd(x) (x) -#define __pgprot(x) (x) - -#endif /* !defined (STRICT_MM_TYPECHECKS) */ - #endif /* _LANGUAGE_ASSEMBLY */ /* to align the pointer to the (next) page boundary */ diff --git a/include/asm-mips64/pgtable.h b/include/asm-mips64/pgtable.h index 0f1fe41b3..76ebeb925 100644 --- a/include/asm-mips64/pgtable.h +++ b/include/asm-mips64/pgtable.h @@ -31,7 +31,7 @@ extern void (*flush_cache_range)(struct mm_struct *mm, unsigned long start, unsigned long end); extern void (*flush_cache_page)(struct vm_area_struct *vma, unsigned long page); extern void (*flush_cache_sigtramp)(unsigned long addr); -extern void (*flush_page_to_ram)(unsigned long page); +extern void (*flush_page_to_ram)(struct page * page); #define flush_icache_range(start, end) flush_cache_all() /* TLB flushing: @@ -163,6 +163,13 @@ extern void (*flush_tlb_page)(struct vm_area_struct *vma, unsigned long page); #if !defined (_LANGUAGE_ASSEMBLY) +#define pte_ERROR(e) \ + printk("%s:%d: bad pte %016lx.\n", __FILE__, __LINE__, pte_val(e)) +#define pmd_ERROR(e) \ + printk("%s:%d: bad pmd %016lx.\n", __FILE__, __LINE__, pmd_val(e)) +#define pgd_ERROR(e) \ + printk("%s:%d: bad pgd %016lx.\n", __FILE__, __LINE__, pgd_val(e)) + /* * BAD_PAGETABLE is used when we need a bogus page-table, while * BAD_PAGE is used for a bogus page. @@ -180,8 +187,8 @@ extern unsigned long zero_page_mask; #define BAD_PAGETABLE __bad_pagetable() #define BAD_PMDTABLE __bad_pmd_table() #define BAD_PAGE __bad_page() -#define ZERO_PAGE(__vaddr) \ - (empty_zero_page + (((unsigned long)(__vaddr)) & zero_page_mask)) +#define ZERO_PAGE(vaddr) \ + (mem_map + MAP_NR(empty_zero_page + (((unsigned long)(vaddr)) & zero_page_mask))) /* number of bits that fit into a memory pointer */ #define BITS_PER_PTR (8*sizeof(unsigned long)) @@ -254,6 +261,8 @@ extern inline void pte_clear(pte_t *ptep) set_pte(ptep, __pte(0)); } +#define pte_pagenr(x) ((unsigned long)((pte_val(x) >> PAGE_SHIFT))) + /* * Empty pmd entries point to the invalid_pte_table. */ @@ -303,6 +312,16 @@ extern inline void pgd_clear(pgd_t *pgdp) } /* + * Permanent address of a page. On MIPS64 we never have highmem, so this + * is simple. + * called on a highmem page. + */ +#define page_address(page) \ + (PAGE_OFFSET + (((page) - mem_map) << PAGE_SHIFT)) +#define pages_to_mb(x) ((x) >> (20-PAGE_SHIFT)) /* Unused? */ +#define pte_page(x) (mem_map+pte_pagenr(x)) + +/* * The following only work if pte_present() is true. * Undefined behaviour if not.. */ @@ -386,9 +405,14 @@ extern inline pte_t pte_mkyoung(pte_t pte) * Conversion functions: convert a page and protection to a page entry, * and a page entry and page directory to the page they refer to. */ -extern inline pte_t mk_pte(unsigned long page, pgprot_t pgprot) +extern inline pte_t mk_pte(struct page * page, pgprot_t pgprot) { - return __pte(((page & PAGE_MASK) - PAGE_OFFSET) | pgprot_val(pgprot)); + pte_t __pte; + + pte_val(__pte) = ((unsigned long)(page - mem_map) << PAGE_SHIFT) | + pgprot_val(pgprot); + + return __pte; } extern inline pte_t mk_pte_phys(unsigned long physpage, pgprot_t pgprot) @@ -401,6 +425,9 @@ extern inline pte_t pte_modify(pte_t pte, pgprot_t newprot) return __pte((pte_val(pte) & _PAGE_CHG_MASK) | pgprot_val(newprot)); } +#define page_pte_prot(page,prot) mk_pte(page, prot) +#define page_pte(page) page_pte_prot(page, __pgprot(0)) + /* to find an entry in a kernel page-table-directory */ #define pgd_offset_k(address) pgd_offset(&init_mm, address) @@ -640,9 +667,9 @@ extern inline pte_t mk_swap_pte(unsigned long type, unsigned long offset) return pte; } -#define SWP_TYPE(entry) (((entry) >> 32) & 0xff) -#define SWP_OFFSET(entry) ((entry) >> 40) -#define SWP_ENTRY(type,offset) pte_val(mk_swap_pte((type),(offset))) +#define SWP_TYPE(entry) ((pte_val(entry) >> 32) & 0xff) +#define SWP_OFFSET(entry) (pte_val(entry) >> 40) +#define SWP_ENTRY(type,offset) mk_swap_pte((type),(offset)) #define module_map vmalloc #define module_unmap vfree diff --git a/include/asm-mips64/siginfo.h b/include/asm-mips64/siginfo.h index dfb385636..cdd8e5ff4 100644 --- a/include/asm-mips64/siginfo.h +++ b/include/asm-mips64/siginfo.h @@ -1,4 +1,4 @@ -/* $Id$ +/* $Id: siginfo.h,v 1.1 1999/08/18 23:37:52 ralf Exp $ * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive @@ -196,7 +196,7 @@ typedef struct siginfo { #define SIGEV_THREAD 131 /* deliver via thread creation */ #define SIGEV_MAX_SIZE 64 -#define SIGEV_PAD_SIZE ((SIGEV_MAX_SIZE/sizeof(int)) - 4) +#define SIGEV_PAD_SIZE ((SIGEV_MAX_SIZE/sizeof(int)) - 6) /* XXX This one isn't yet IRIX / ABI compatible. */ typedef struct sigevent { diff --git a/include/asm-ppc/machdep.h b/include/asm-ppc/machdep.h index 6f85554ca..40e5ea046 100644 --- a/include/asm-ppc/machdep.h +++ b/include/asm-ppc/machdep.h @@ -12,8 +12,7 @@ struct pt_regs; struct pci_bus; struct machdep_calls { - void (*setup_arch)(unsigned long * memory_start_p, - unsigned long * memory_end_p); + void (*setup_arch)(void); /* Optional, may be NULL. */ int (*setup_residual)(char *buffer); /* Optional, may be NULL. */ diff --git a/include/asm-ppc/page.h b/include/asm-ppc/page.h index b67ca5cc0..55168a8b2 100644 --- a/include/asm-ppc/page.h +++ b/include/asm-ppc/page.h @@ -76,7 +76,7 @@ typedef unsigned long pgprot_t; /* to align the pointer to the (next) page boundary */ #define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) -extern void clear_page(unsigned long page); +extern void clear_page(void *page); #define copy_page(to,from) memcpy((void *)(to), (void *)(from), PAGE_SIZE) /* map phys->virtual and virtual->phys for RAM pages */ diff --git a/include/asm-ppc/pci-bridge.h b/include/asm-ppc/pci-bridge.h index e3f3c1590..47a2cc87d 100644 --- a/include/asm-ppc/pci-bridge.h +++ b/include/asm-ppc/pci-bridge.h @@ -1,7 +1,7 @@ #ifndef _ASM_PCI_BRIDGE_H #define _ASM_PCI_BRIDGE_H -unsigned long pmac_find_bridges(unsigned long, unsigned long); +void pmac_find_bridges(void); /* * pci_io_base returns the memory address at which you can access diff --git a/include/asm-ppc/pgtable.h b/include/asm-ppc/pgtable.h index 3a822590b..ee42b4547 100644 --- a/include/asm-ppc/pgtable.h +++ b/include/asm-ppc/pgtable.h @@ -5,6 +5,7 @@ #ifndef __ASSEMBLY__ #include <linux/mm.h> +#include <linux/threads.h> #include <asm/processor.h> /* For TASK_SIZE */ #include <asm/mmu.h> #include <asm/page.h> @@ -50,7 +51,8 @@ extern inline void flush_hash_page(unsigned context, unsigned long va) #define flush_cache_page(vma, p) do { } while (0) extern void flush_icache_range(unsigned long, unsigned long); -extern void flush_page_to_ram(unsigned long); +extern void __flush_page_to_ram(unsigned long page_va); +#define flush_page_to_ram(page) __flush_page_to_ram(page_address(page)) extern unsigned long va_to_phys(unsigned long address); extern pte_t *va_to_pte(struct task_struct *tsk, unsigned long address); @@ -109,6 +111,16 @@ extern unsigned long ioremap_bot, ioremap_base; #define PTRS_PER_PGD 1024 #define USER_PTRS_PER_PGD (TASK_SIZE / PGDIR_SIZE) +#define USER_PGD_PTRS (PAGE_OFFSET >> PGDIR_SHIFT) +#define KERNEL_PGD_PTRS (PTRS_PER_PGD-USER_PGD_PTRS) + +#define pte_ERROR(e) \ + printk("%s:%d: bad pte %08lx.\n", __FILE__, __LINE__, pte_val(e)) +#define pmd_ERROR(e) \ + printk("%s:%d: bad pmd %08lx.\n", __FILE__, __LINE__, pmd_val(e)) +#define pgd_ERROR(e) \ + printk("%s:%d: bad pgd %08lx.\n", __FILE__, __LINE__, pgd_val(e)) + /* * Just any arbitrary offset to the start of the vmalloc VM area: the * current 64MB value just means that there will be a 64MB "hole" after the @@ -126,7 +138,7 @@ extern unsigned long ioremap_bot, ioremap_base; * system. This really does become a problem for machines with good amounts * of RAM. -- Cort */ -#define VMALLOC_OFFSET (0x4000000) /* 64M */ +#define VMALLOC_OFFSET (0x1000000) /* 16M */ #define VMALLOC_START ((((long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1))) #define VMALLOC_VMADDR(x) ((unsigned long)(x)) #define VMALLOC_END ioremap_bot @@ -216,6 +228,14 @@ extern unsigned long ioremap_bot, ioremap_base; #define __S110 PAGE_SHARED #define __S111 PAGE_SHARED +#ifndef __ASSEMBLY__ +/* + * ZERO_PAGE is a global shared page that is always zero: used + * for zero-mapped memory areas etc.. + */ +extern unsigned long empty_zero_page[1024]; +#define ZERO_PAGE(vaddr) (mem_map + MAP_NR(empty_zero_page)) + /* * BAD_PAGETABLE is used when we need a bogus page-table, while * BAD_PAGE is used for a bogus page. @@ -227,11 +247,8 @@ extern unsigned long ioremap_bot, ioremap_base; extern pte_t __bad_page(void); extern pte_t * __bad_pagetable(void); -extern unsigned long empty_zero_page[1024]; -#endif __ASSEMBLY__ #define BAD_PAGETABLE __bad_pagetable() #define BAD_PAGE __bad_page() -#define ZERO_PAGE(vaddr) ((unsigned long) empty_zero_page) /* number of bits that fit into a memory pointer */ #define BITS_PER_PTR (8*sizeof(unsigned long)) @@ -243,17 +260,24 @@ extern unsigned long empty_zero_page[1024]; /* 64-bit machines, beware! SRB. */ #define SIZEOF_PTR_LOG2 2 -#ifndef __ASSEMBLY__ -extern inline int pte_none(pte_t pte) { return !pte_val(pte); } -extern inline int pte_present(pte_t pte) { return pte_val(pte) & _PAGE_PRESENT; } -extern inline void pte_clear(pte_t *ptep) { pte_val(*ptep) = 0; } +#define pte_none(pte) (!pte_val(pte)) +#define pte_present(pte) (pte_val(pte) & _PAGE_PRESENT) +#define pte_clear(ptep) do { pte_val(*(ptep)) = 0; } while (0) +#define pte_pagenr(x) ((unsigned long)((pte_val(x) >> PAGE_SHIFT))) -extern inline int pmd_none(pmd_t pmd) { return !pmd_val(pmd); } -extern inline int pmd_bad(pmd_t pmd) { return (pmd_val(pmd) & ~PAGE_MASK) != 0; } -extern inline int pmd_present(pmd_t pmd) { return (pmd_val(pmd) & PAGE_MASK) != 0; } -extern inline void pmd_clear(pmd_t * pmdp) { pmd_val(*pmdp) = 0; } +#define pmd_none(pmd) (!pmd_val(pmd)) +#define pmd_bad(pmd) ((pmd_val(pmd) & ~PAGE_MASK) != 0) +#define pmd_present(pmd) ((pmd_val(pmd) & PAGE_MASK) != 0) +#define pmd_clear(pmdp) do { pmd_val(*(pmdp)) = 0; } while (0) +/* + * Permanent address of a page. + */ +#define page_address(page) (PAGE_OFFSET + (((page) - mem_map) << PAGE_SHIFT)) +#define pages_to_mb(x) ((x) >> (20-PAGE_SHIFT)) +#define pte_page(x) (mem_map+pte_pagenr(x)) +#ifndef __ASSEMBLY__ /* * The "pgd_xxx()" functions here are trivial for a folded two-level * setup: the pgd is never bad, and a pmd always exists (as it's folded @@ -262,7 +286,10 @@ extern inline void pmd_clear(pmd_t * pmdp) { pmd_val(*pmdp) = 0; } extern inline int pgd_none(pgd_t pgd) { return 0; } extern inline int pgd_bad(pgd_t pgd) { return 0; } extern inline int pgd_present(pgd_t pgd) { return 1; } -extern inline void pgd_clear(pgd_t * pgdp) { } +#define pgd_clear(xp) do { pgd_val(*(xp)) = 0; } while (0) + +#define pgd_page(pgd) \ + ((unsigned long) __va(pgd_val(pgd) & PAGE_MASK)) /* * The following only work if pte_present() is true. @@ -313,43 +340,37 @@ extern inline pte_t pte_mkyoung(pte_t pte) { * within a page table are directly modified. Thus, the following * hook is made available. */ -#if 1 #define set_pte(pteptr, pteval) ((*(pteptr)) = (pteval)) -#else -extern inline void set_pte(pte_t *pteptr, pte_t pteval) -{ - unsigned long val = pte_val(pteval); - extern void xmon(void *); - - if ((val & _PAGE_PRESENT) && ((val < 0x111000 || (val & 0x800) - || ((val & _PAGE_HWWRITE) && (~val & (_PAGE_RW|_PAGE_DIRTY)))) { - printk("bad pte val %lx ptr=%p\n", val, pteptr); - xmon(0); - } - *pteptr = pteval; -} -#endif /* * Conversion functions: convert a page and protection to a page entry, * and a page entry and page directory to the page they refer to. */ -static inline pte_t mk_pte_phys(unsigned long page, pgprot_t pgprot) -{ pte_t pte; pte_val(pte) = (page) | pgprot_val(pgprot); return pte; } +extern inline pte_t mk_pte_phys(unsigned long physpage, pgprot_t pgprot) +{ + pte_t pte; + pte_val(pte) = physpage | pgprot_val(pgprot); + return pte; +} -extern inline pte_t mk_pte(unsigned long page, pgprot_t pgprot) -{ pte_t pte; pte_val(pte) = __pa(page) | pgprot_val(pgprot); return pte; } +extern inline pte_t mk_pte(struct page *page, pgprot_t pgprot) +{ + pte_t pte; + pte_val(pte) = ((page - mem_map) << PAGE_SHIFT) | pgprot_val(pgprot); + return pte; +} extern inline pte_t pte_modify(pte_t pte, pgprot_t newprot) -{ pte_val(pte) = (pte_val(pte) & _PAGE_CHG_MASK) | pgprot_val(newprot); return pte; } - -extern inline unsigned long pte_page(pte_t pte) -{ return (unsigned long) __va(pte_val(pte) & PAGE_MASK); } +{ + pte_val(pte) = (pte_val(pte) & _PAGE_CHG_MASK) | pgprot_val(newprot); + return pte; +} -extern inline unsigned long pmd_page(pmd_t pmd) -{ return pmd_val(pmd); } +#define page_pte_prot(page,prot) mk_pte(page, prot) +#define page_pte(page) page_pte_prot(page, __pgprot(0)) +#define pmd_page(pmd) (pmd_val(pmd)) /* to find an entry in a kernel page-table-directory */ #define pgd_offset_k(address) pgd_offset(&init_mm, address) @@ -418,7 +439,7 @@ extern unsigned long get_zero_page_fast(void); extern __inline__ pgd_t *get_pgd_slow(void) { - pgd_t *ret/* = (pgd_t *)__get_free_page(GFP_KERNEL)*/, *init; + pgd_t *ret, *init; if ( (ret = (pgd_t *)get_zero_page_fast()) == NULL ) { @@ -427,7 +448,6 @@ extern __inline__ pgd_t *get_pgd_slow(void) } if (ret) { init = pgd_offset(&init_mm, 0); - /*memset (ret, 0, USER_PTRS_PER_PGD * sizeof(pgd_t));*/ memcpy (ret + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD, (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t)); } @@ -612,9 +632,9 @@ extern void flush_hash_segments(unsigned low_vsid, unsigned high_vsid); extern void flush_hash_page(unsigned context, unsigned long va); -#define SWP_TYPE(entry) (((entry) >> 1) & 0x7f) -#define SWP_OFFSET(entry) ((entry) >> 8) -#define SWP_ENTRY(type,offset) (((type) << 1) | ((offset) << 8)) +#define SWP_TYPE(entry) (((pte_val(entry)) >> 1) & 0x7f) +#define SWP_OFFSET(entry) ((pte_val(entry)) >> 8) +#define SWP_ENTRY(type,offset) __pte(((type) << 1) | ((offset) << 8)) #define module_map vmalloc #define module_unmap vfree diff --git a/include/asm-sh/addrspace.h b/include/asm-sh/addrspace.h index 152708837..561b3d60c 100644 --- a/include/asm-sh/addrspace.h +++ b/include/asm-sh/addrspace.h @@ -3,39 +3,48 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 1996 by Ralf Baechle - * Copyright (C) 1999 by Niibe Yutaka + * Copyright (C) 1999 by Kaz Kojima * * Defitions for the address spaces of the SH CPUs. */ #ifndef __ASM_SH_ADDRSPACE_H #define __ASM_SH_ADDRSPACE_H -/* - * Memory segments (32bit kernel mode addresses) - */ -#define KUSEG 0x00000000 -#define KSEG0 0x80000000 -#define KSEG1 0xa0000000 -#define KSEG2 0xc0000000 -#define KSEG3 0xe0000000 +/* Memory segments (32bit Priviledged mode addresses) */ +#define P0SEG 0x00000000 +#define P1SEG 0x80000000 +#define P2SEG 0xa0000000 +#define P3SEG 0xc0000000 +#define P4SEG 0xe0000000 -/* - * Returns the kernel segment base of a given address - */ -#define KSEGX(a) (((unsigned long)(a)) & 0xe0000000) +#if defined(__sh3__) +/* Should fill here */ +#elif defined(__SH4__) +/* Detailed P4SEG */ +#define P4SEG_STORE_QUE (P4SEG) +#define P4SEG_IC_ADDR 0xf0000000 +#define P4SEG_IC_DATA 0xf1000000 +#define P4SEG_ITLB_ADDR 0xf2000000 +#define P4SEG_ITLB_DATA 0xf3000000 +#define P4SEG_OC_ADDR 0xf4000000 +#define P4SEG_OC_DATA 0xf5000000 +#define P4SEG_TLB_ADDR 0xf6000000 +#define P4SEG_TLB_DATA 0xf7000000 +#define P4SEG_REG_BASE 0xff000000 +#endif -/* - * Returns the physical address of a KSEG0/KSEG1 address - */ -#define PHYSADDR(a) (((unsigned long)(a)) & 0x1fffffff) +/* Returns the privileged segment base of a given address */ +#define PXSEG(a) (((unsigned long)(a)) & 0xe0000000) + +/* Returns the physical address of a PnSEG (n=1,2) address */ +#define PHYSADDR(a) (((unsigned long)(a)) & 0x1fffffff) /* - * Map an address to a certain kernel segment + * Map an address to a certain privileged segment */ -#define KSEG0ADDR(a) ((__typeof__(a))(((unsigned long)(a) & 0x1fffffff) | KSEG0)) -#define KSEG1ADDR(a) ((__typeof__(a))(((unsigned long)(a) & 0x1fffffff) | KSEG1)) -#define KSEG2ADDR(a) ((__typeof__(a))(((unsigned long)(a) & 0x1fffffff) | KSEG2)) -#define KSEG3ADDR(a) ((__typeof__(a))(((unsigned long)(a) & 0x1fffffff) | KSEG3)) +#define P1SEGADDR(a) ((__typeof__(a))(((unsigned long)(a) & 0x1fffffff) | P1SEG)) +#define P2SEGADDR(a) ((__typeof__(a))(((unsigned long)(a) & 0x1fffffff) | P2SEG)) +#define P3SEGADDR(a) ((__typeof__(a))(((unsigned long)(a) & 0x1fffffff) | P3SEG)) +#define P4SEGADDR(a) ((__typeof__(a))(((unsigned long)(a) & 0x1fffffff) | P4SEG)) #endif /* __ASM_SH_ADDRSPACE_H */ diff --git a/include/asm-sh/atomic.h b/include/asm-sh/atomic.h index d2af3d769..7f580a9aa 100644 --- a/include/asm-sh/atomic.h +++ b/include/asm-sh/atomic.h @@ -25,7 +25,7 @@ typedef struct { int counter; } atomic_t; * on us. We need to use _exactly_ the address the user gave us, * not some alias that contains the same information. */ -#define __atomic_fool_gcc(x) (*(struct { int a[100]; } *)x) +#define __atomic_fool_gcc(x) (*(volatile struct { int a[100]; } *)x) /* * To get proper branch prediction for the main line, we must branch @@ -37,8 +37,7 @@ extern __inline__ void atomic_add(int i, atomic_t * v) { unsigned long flags; - save_flags(flags); - cli(); + save_and_cli(flags); *(long *)v += i; restore_flags(flags); } @@ -47,8 +46,7 @@ extern __inline__ void atomic_sub(int i, atomic_t *v) { unsigned long flags; - save_flags(flags); - cli(); + save_and_cli(flags); *(long *)v -= i; restore_flags(flags); } @@ -57,8 +55,7 @@ extern __inline__ int atomic_add_return(int i, atomic_t * v) { unsigned long temp, flags; - save_flags(flags); - cli(); + save_and_cli(flags); temp = *(long *)v; temp += i; *(long *)v = temp; @@ -71,8 +68,7 @@ extern __inline__ int atomic_sub_return(int i, atomic_t * v) { unsigned long temp, flags; - save_flags(flags); - cli(); + save_and_cli(flags); temp = *(long *)v; temp -= i; *(long *)v = temp; @@ -90,22 +86,20 @@ extern __inline__ int atomic_sub_return(int i, atomic_t * v) #define atomic_inc(v) atomic_add(1,(v)) #define atomic_dec(v) atomic_sub(1,(v)) -extern __inline__ void atomic_clear_mask(int mask, atomic_t *v) +extern __inline__ void atomic_clear_mask(unsigned int mask, atomic_t *v) { unsigned long flags; - save_flags(flags); - cli(); + save_and_cli(flags); *(long *)v &= ~mask; restore_flags(flags); } -extern __inline__ void atomic_set_mask(int mask, atomic_t *v) +extern __inline__ void atomic_set_mask(unsigned int mask, atomic_t *v) { unsigned long flags; - save_flags(flags); - cli(); + save_and_cli(flags); *(long *)v |= mask; restore_flags(flags); } diff --git a/include/asm-sh/bitops.h b/include/asm-sh/bitops.h index 9808789c7..de026bcd2 100644 --- a/include/asm-sh/bitops.h +++ b/include/asm-sh/bitops.h @@ -14,8 +14,7 @@ extern __inline__ void set_bit(int nr, void * addr) a += nr >> 5; mask = 1 << (nr & 0x1f); - save_flags(flags); - cli(); + save_and_cli(flags); *a |= mask; restore_flags(flags); } @@ -28,8 +27,7 @@ extern __inline__ void clear_bit(int nr, void * addr) a += nr >> 5; mask = 1 << (nr & 0x1f); - save_flags(flags); - cli(); + save_and_cli(flags); *a &= ~mask; restore_flags(flags); } @@ -42,8 +40,7 @@ extern __inline__ void change_bit(int nr, void * addr) a += nr >> 5; mask = 1 << (nr & 0x1f); - save_flags(flags); - cli(); + save_and_cli(flags); *a ^= mask; restore_flags(flags); } @@ -56,8 +53,7 @@ extern __inline__ int test_and_set_bit(int nr, void * addr) a += nr >> 5; mask = 1 << (nr & 0x1f); - save_flags(flags); - cli(); + save_and_cli(flags); retval = (mask & *a) != 0; *a |= mask; restore_flags(flags); @@ -73,8 +69,7 @@ extern __inline__ int test_and_clear_bit(int nr, void * addr) a += nr >> 5; mask = 1 << (nr & 0x1f); - save_flags(flags); - cli(); + save_and_cli(flags); retval = (mask & *a) != 0; *a &= ~mask; restore_flags(flags); @@ -90,8 +85,7 @@ extern __inline__ int test_and_change_bit(int nr, void * addr) a += nr >> 5; mask = 1 << (nr & 0x1f); - save_flags(flags); - cli(); + save_and_cli(flags); retval = (mask & *a) != 0; *a ^= mask; restore_flags(flags); @@ -112,7 +106,7 @@ extern __inline__ unsigned long ffz(unsigned long word) __asm__("1:\n" "shlr %1\n\t" "bt/s 1b\n\t" - "add #1, %0" + " add #1, %0" : "=r" (result) : "r" (word), "0" (~0L)); return result; @@ -165,7 +159,7 @@ extern __inline__ int ext2_set_bit(int nr,void * addr) ADDR += nr >> 3; mask = 1 << (nr & 0x07); - save_flags(flags); cli(); + save_and_cli(flags); retval = (mask & *ADDR) != 0; *ADDR |= mask; restore_flags(flags); @@ -180,7 +174,7 @@ extern __inline__ int ext2_clear_bit(int nr, void * addr) ADDR += nr >> 3; mask = 1 << (nr & 0x07); - save_flags(flags); cli(); + save_and_cli(flags); retval = (mask & *ADDR) != 0; *ADDR &= ~mask; restore_flags(flags); diff --git a/include/asm-sh/bugs.h b/include/asm-sh/bugs.h index 4c20b768d..99728234a 100644 --- a/include/asm-sh/bugs.h +++ b/include/asm-sh/bugs.h @@ -16,5 +16,23 @@ static void __init check_bugs(void) { + extern unsigned long loops_per_sec; + + cpu_data->loops_per_sec = loops_per_sec; + + switch (cpu_data->type) { + case CPU_SH7708: + printk("CPU: SH7708/SH7709\n"); + break; + case CPU_SH7729: + printk("CPU: SH7709A/SH7729\n"); + break; + case CPU_SH7750: + printk("CPU: SH7750\n"); + break; + default: + printk("CPU: ??????\n"); + break; + } } #endif /* __ASM_SH_BUGS_H */ diff --git a/include/asm-sh/byteorder.h b/include/asm-sh/byteorder.h index 5a6863485..dcc9f8241 100644 --- a/include/asm-sh/byteorder.h +++ b/include/asm-sh/byteorder.h @@ -5,7 +5,6 @@ * Copyright (C) 1999 Niibe Yutaka */ -#include <linux/config.h> #include <asm/types.h> static __inline__ __const__ __u32 ___arch__swab32(__u32 x) @@ -34,7 +33,7 @@ static __inline__ __const__ __u16 ___arch__swab16(__u16 x) # define __SWAB_64_THRU_32__ #endif -#ifdef CONFIG_LITTLE_ENDIAN +#ifdef __LITTLE_ENDIAN__ #include <linux/byteorder/little_endian.h> #else #include <linux/byteorder/big_endian.h> diff --git a/include/asm-sh/cache.h b/include/asm-sh/cache.h index d22ab4e24..f9113e77b 100644 --- a/include/asm-sh/cache.h +++ b/include/asm-sh/cache.h @@ -6,7 +6,11 @@ #define __ASM_SH_CACHE_H /* bytes per L1 cache line */ +#if defined(__sh3__) #define L1_CACHE_BYTES 16 +#elif defined(__SH4__) +#define L1_CACHE_BYTES 32 +#endif #define L1_CACHE_ALIGN(x) (((x)+(L1_CACHE_BYTES-1))&~(L1_CACHE_BYTES-1)) @@ -20,4 +24,8 @@ __section__(".data.cacheline_aligned"))) #endif +extern void cache_flush_area(unsigned long start, unsigned long end); +extern void cache_purge_area(unsigned long start, unsigned long end); +extern void cache_wback_area(unsigned long start, unsigned long end); + #endif /* __ASM_SH_CACHE_H */ diff --git a/include/asm-sh/checksum.h b/include/asm-sh/checksum.h index b2bbd8afb..6d5f89c04 100644 --- a/include/asm-sh/checksum.h +++ b/include/asm-sh/checksum.h @@ -1,22 +1,13 @@ #ifndef __ASM_SH_CHECKSUM_H #define __ASM_SH_CHECKSUM_H - -/* - * This is a version of ip_compute_csum() optimized for IP headers, - * which always checksum on 4 octet boundaries. - */ -extern unsigned short ip_fast_csum(unsigned char * iph, unsigned int ihl); - /* - * computes the checksum of the TCP/UDP pseudo-header - * returns a 16-bit checksum, already complemented + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1999 by Kaz Kojima & Niibe Yutaka */ -extern unsigned short int csum_tcpudp_magic(unsigned long saddr, - unsigned long daddr, - unsigned short len, - unsigned short proto, - unsigned int sum); /* * computes the checksum of a memory block at buff, length len, @@ -30,55 +21,220 @@ extern unsigned short int csum_tcpudp_magic(unsigned long saddr, * * it's best to have buff aligned on a 32-bit boundary */ -extern unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum); +asmlinkage unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum); /* * the same as csum_partial, but copies from src while it - * checksums + * checksums, and handles user-space pointer exceptions correctly, when needed. * * here even more important to align src and dst on a 32-bit (or even * better 64-bit) boundary */ -unsigned int csum_partial_copy(const char *src, char *dst, int len, unsigned int sum); + +asmlinkage unsigned int csum_partial_copy_generic( const char *src, char *dst, int len, int sum, + int *src_err_ptr, int *dst_err_ptr); + +/* + * Note: when you get a NULL pointer exception here this means someone + * passed in an incorrect kernel address to one of these functions. + * + * If you use these functions directly please don't forget the + * verify_area(). + */ +extern __inline__ +unsigned int csum_partial_copy_nocheck ( const char *src, char *dst, + int len, int sum) +{ + return csum_partial_copy_generic ( src, dst, len, sum, NULL, NULL); +} + +extern __inline__ +unsigned int csum_partial_copy_from_user ( const char *src, char *dst, + int len, int sum, int *err_ptr) +{ + return csum_partial_copy_generic ( src, dst, len, sum, err_ptr, NULL); +} + +#if 0 + +/* Not used at the moment. It is difficult to imagine for what purpose + it can be used :-) Please, do not forget to verify_area before it --ANK + */ + +/* + * This combination is currently not used, but possible: + */ + +extern __inline__ +unsigned int csum_partial_copy_to_user ( const char *src, char *dst, + int len, int sum, int *err_ptr) +{ + return csum_partial_copy_generic ( src, dst, len, sum, NULL, err_ptr); +} +#endif /* - * the same as csum_partial, but copies from user space (but on the alpha - * we have just one address space, so this is identical to the above) + * These are the old (and unsafe) way of doing checksums, a warning message will be + * printed if they are used and an exeption occurs. * - * this is obsolete and will go away. + * these functions should go away after some time. */ + #define csum_partial_copy_fromuser csum_partial_copy +unsigned int csum_partial_copy( const char *src, char *dst, int len, int sum); /* - * this is a new version of the above that records errors it finds in *errp, - * but continues and zeros the rest of the buffer. + * Fold a partial checksum */ -unsigned int csum_partial_copy_from_user(const char *src, char *dst, int len, unsigned int sum, int *errp); + +static __inline__ unsigned int csum_fold(unsigned int sum) +{ + unsigned int __dummy; + __asm__("clrt\n\t" + "mov %0,%1\n\t" + "shll16 %0\n\t" + "addc %0,%1\n\t" + "movt %0\n\t" + "shlr16 %1\n\t" + "add %1,%0" + : "=r" (sum), "=&r" (__dummy) + : "0" (sum)); + return ~sum; +} /* - * this routine is used for miscellaneous IP-like checksums, mainly - * in icmp.c + * This is a version of ip_compute_csum() optimized for IP headers, + * which always checksum on 4 octet boundaries. + * + * i386 version by Jorge Cwik <jorge@laser.satlink.net>, adapted + * for linux by * Arnt Gulbrandsen. */ +static __inline__ unsigned short ip_fast_csum(unsigned char * iph, unsigned int ihl) +{ + unsigned int sum, __dummy; + + __asm__ __volatile__( + "mov.l @%1+,%0\n\t" + "add #-4,%2\n\t" + "clrt\n\t" + "mov.l @%1+,%3\n\t" + "addc %3,%0\n\t" + "mov.l @%1+,%3\n\t" + "addc %3,%0\n\t" + "mov.l @%1+,%3\n\t" + "addc %3,%0\n" + "1:\t" + "mov.l @%1+,%3\n\t" + "addc %3,%0\n\t" + "movt %3\n\t" + "dt %2\n\t" + "bf/s 1b\n\t" + " cmp/eq #1,%3\n\t" + "mov #0,%3\n\t" + "addc %3,%0\n\t" + /* Since the input registers which are loaded with iph and ihl + are modified, we must also specify them as outputs, or gcc + will assume they contain their original values. */ + : "=r" (sum), "=r" (iph), "=r" (ihl), "=&z" (__dummy) + : "1" (iph), "2" (ihl)); + + return csum_fold(sum); +} -extern unsigned short ip_compute_csum(unsigned char * buff, int len); +static __inline__ unsigned long csum_tcpudp_nofold(unsigned long saddr, + unsigned long daddr, + unsigned short len, + unsigned short proto, + unsigned int sum) +{ +#ifdef __LITTLE_ENDIAN__ + unsigned long len_proto = (ntohs(len)<<16)+proto*256; +#else + unsigned long len_proto = (proto<<16)+len; +#endif + __asm__("clrt\n\t" + "addc %0,%1\n\t" + "addc %2,%1\n\t" + "addc %3,%1\n\t" + "movt %0\n\t" + "add %1,%0" + : "=r" (sum), "=r" (len_proto) + : "r" (daddr), "r" (saddr), "1" (len_proto), "0" (sum)); + return sum; +} /* - * Fold a partial checksum without adding pseudo headers + * computes the checksum of the TCP/UDP pseudo-header + * returns a 16-bit checksum, already complemented */ +static __inline__ unsigned short int csum_tcpudp_magic(unsigned long saddr, + unsigned long daddr, + unsigned short len, + unsigned short proto, + unsigned int sum) +{ + return csum_fold(csum_tcpudp_nofold(saddr,daddr,len,proto,sum)); +} -static inline unsigned short csum_fold(unsigned int sum) +/* + * this routine is used for miscellaneous IP-like checksums, mainly + * in icmp.c + */ + +static __inline__ unsigned short ip_compute_csum(unsigned char * buff, int len) { - sum = (sum & 0xffff) + (sum >> 16); - sum = (sum & 0xffff) + (sum >> 16); - return ~sum; + return csum_fold (csum_partial(buff, len, 0)); } #define _HAVE_ARCH_IPV6_CSUM -/* -extern unsigned short int csum_ipv6_magic(struct in6_addr *saddr, - struct in6_addr *daddr, - __u16 len, - unsigned short proto, - unsigned int sum); -*/ +static __inline__ unsigned short int csum_ipv6_magic(struct in6_addr *saddr, + struct in6_addr *daddr, + __u16 len, + unsigned short proto, + unsigned int sum) +{ + unsigned int __dummy; + __asm__("clrt\n\t" + "mov.l @(0,%2),%1\n\t" + "addc %1,%0\n\t" + "mov.l @(4,%2),%1\n\t" + "addc %1,%0\n\t" + "mov.l @(8,%2),%1\n\t" + "addc %1,%0\n\t" + "mov.l @(12,%2),%1\n\t" + "addc %1,%0\n\t" + "mov.l @(0,%3),%1\n\t" + "addc %1,%0\n\t" + "mov.l @(4,%3),%1\n\t" + "addc %1,%0\n\t" + "mov.l @(8,%3),%1\n\t" + "addc %1,%0\n\t" + "mov.l @(12,%3),%1\n\t" + "addc %1,%0\n\t" + "addc %4,%0\n\t" + "addc %5,%0\n\t" + "movt %1\n\t" + "add %1,%0\n" + : "=r" (sum), "=&r" (__dummy) + : "r" (saddr), "r" (daddr), + "r" (htonl((__u32) (len))), "r" (htonl(proto)), "0" (sum)); + + return csum_fold(sum); +} + +/* + * Copy and checksum to user + */ +#define HAVE_CSUM_COPY_USER +static __inline__ unsigned int csum_and_copy_to_user (const char *src, char *dst, + int len, int sum, int *err_ptr) +{ + if (access_ok(VERIFY_WRITE, dst, len)) + return csum_partial_copy_generic(src, dst, len, sum, NULL, err_ptr); + + if (len) + *err_ptr = -EFAULT; + + return -1; /* invalid checksum */ +} #endif /* __ASM_SH_CHECKSUM_H */ diff --git a/include/asm-sh/delay.h b/include/asm-sh/delay.h index 9eb03f65d..222561a3e 100644 --- a/include/asm-sh/delay.h +++ b/include/asm-sh/delay.h @@ -2,26 +2,26 @@ #define __ASM_SH_DELAY_H /* - * Copyright (C) 1999 Niibe Yutaka + * Copyright (C) 1999 Kaz Kojima */ extern __inline__ void __delay(unsigned long loops) { - unsigned long __dummy; __asm__ __volatile__( - "1:\t" - "dt %0\n\t" - "bf 1b" - :"=r" (__dummy) - :"0" (loops)); + "tst %0,%0\n\t" + "1:\t" + "bf/s 1b\n\t" + " dt %0" + : "=r" (loops) + : "0" (loops)); } extern __inline__ void __udelay(unsigned long usecs, unsigned long lps) { usecs *= 0x000010c6; /* 2**32 / 1000000 */ - __asm__("mul.l %0,%2\n\t" - "sts macl,%0" - : "=&r" (usecs) + __asm__("dmulu.l %0,%2\n\t" + "sts mach,%0" + : "=r" (usecs) : "0" (usecs), "r" (lps) : "macl", "mach"); __delay(usecs); @@ -36,9 +36,4 @@ extern __inline__ void __udelay(unsigned long usecs, unsigned long lps) #define udelay(usecs) __udelay((usecs),__udelay_val) -extern __inline__ unsigned long muldiv(unsigned long a, unsigned long b, unsigned long c) -{ - return (a*b)/c; -} - #endif /* __ASM_SH_DELAY_H */ diff --git a/include/asm-sh/elf.h b/include/asm-sh/elf.h index de5512ce3..a0e98de5f 100644 --- a/include/asm-sh/elf.h +++ b/include/asm-sh/elf.h @@ -5,7 +5,6 @@ * ELF register definitions.. */ -#include <linux/config.h> #include <asm/ptrace.h> #include <asm/user.h> #include <asm/byteorder.h> @@ -15,12 +14,10 @@ typedef unsigned long elf_greg_t; #define ELF_NGREG (sizeof (struct pt_regs) / sizeof(elf_greg_t)) typedef elf_greg_t elf_gregset_t[ELF_NGREG]; -#ifdef CONFIG_CPU_SH4 +/* Though SH-3 has no floating point regs.. */ +#define ELF_NFPREG 34 typedef double elf_fpreg_t; typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG]; -#else /* SH 3 has no floating point regs */ -typedef struct { void *null; } elf_fpregset_t; -#endif /* * This is used to ensure we don't load something for the wrong architecture. @@ -31,7 +28,7 @@ typedef struct { void *null; } elf_fpregset_t; * These are used to set parameters in the core dumps. */ #define ELF_CLASS ELFCLASS32 -#ifdef __LITTLE_ENDIAN +#ifdef __LITTLE_ENDIAN__ #define ELF_DATA ELFDATA2LSB #else #define ELF_DATA ELFDATA2MSB @@ -68,6 +65,12 @@ typedef struct { void *null; } elf_fpregset_t; #define ELF_PLATFORM (NULL) +#define ELF_PLAT_INIT(_r) \ + do { _r->regs[0]=0; _r->regs[1]=0; _r->regs[2]=0; _r->regs[3]=0; \ + _r->regs[4]=0; _r->regs[5]=0; _r->regs[6]=0; _r->regs[7]=0; \ + _r->regs[8]=0; _r->regs[9]=0; _r->regs[10]=0; _r->regs[11]=0; \ + _r->regs[12]=0; _r->regs[13]=0; _r->regs[14]=0; } while (0) + #ifdef __KERNEL__ #define SET_PERSONALITY(ex, ibcs2) \ current->personality = PER_LINUX_32BIT diff --git a/include/asm-sh/hardirq.h b/include/asm-sh/hardirq.h index 1957bb7b0..bc5866f7e 100644 --- a/include/asm-sh/hardirq.h +++ b/include/asm-sh/hardirq.h @@ -14,7 +14,7 @@ extern unsigned int local_irq_count[NR_CPUS]; #define hardirq_enter(cpu) (local_irq_count[cpu]++) #define hardirq_exit(cpu) (local_irq_count[cpu]--) -#define synchronize_irq() do { } while (0) +#define synchronize_irq() barrier() #else diff --git a/include/asm-sh/hdreg.h b/include/asm-sh/hdreg.h new file mode 100644 index 000000000..4a2272c8a --- /dev/null +++ b/include/asm-sh/hdreg.h @@ -0,0 +1,12 @@ +/* + * linux/include/asm-sh/hdreg.h + * + * Copyright (C) 1994-1996 Linus Torvalds & authors + */ + +#ifndef __ASM_SH_HDREG_H +#define __ASM_SH_HDREG_H + +typedef unsigned short ide_ioreg_t; + +#endif /* __ASM_SH_HDREG_H */ diff --git a/include/asm-sh/ide.h b/include/asm-sh/ide.h new file mode 100644 index 000000000..b9b3efcb4 --- /dev/null +++ b/include/asm-sh/ide.h @@ -0,0 +1,112 @@ +/* + * linux/include/asm-sh/ide.h + * + * Copyright (C) 1994-1996 Linus Torvalds & authors + */ + +/* + * This file contains the i386 architecture specific IDE code. + * In future, SuperH code. + */ + +#ifndef __ASM_SH_IDE_H +#define __ASM_SH_IDE_H + +#ifdef __KERNEL__ + +#include <linux/config.h> + +#ifndef MAX_HWIFS +#define MAX_HWIFS 10 +#endif + +#define ide__sti() __sti() + +static __inline__ int ide_default_irq(ide_ioreg_t base) +{ + switch (base) { + case 0x1f0: return 14; + case 0x170: return 15; + case 0x1e8: return 11; + case 0x168: return 10; + case 0x1e0: return 8; + case 0x160: return 12; + default: + return 0; + } +} + +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; + } +} + +static __inline__ void ide_init_hwif_ports(hw_regs_t *hw, ide_ioreg_t data_port, ide_ioreg_t ctrl_port, int *irq) +{ + ide_ioreg_t reg = data_port; + int i; + + for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) { + hw->io_ports[i] = reg; + reg += 1; + } + if (ctrl_port) { + hw->io_ports[IDE_CONTROL_OFFSET] = ctrl_port; + } else { + hw->io_ports[IDE_CONTROL_OFFSET] = hw->io_ports[IDE_DATA_OFFSET] + 0x206; + } + if (irq != NULL) + *irq = 0; +} + +static __inline__ void ide_init_default_hwifs(void) +{ +#ifndef CONFIG_BLK_DEV_IDEPCI + hw_regs_t hw; + int index; + + for(index = 0; index < MAX_HWIFS; index++) { + ide_init_hwif_ports(&hw, ide_default_io_base(index), 0, NULL); + hw.irq = ide_default_irq(ide_default_io_base(index)); + ide_register_hw(&hw, NULL); + } +#endif /* CONFIG_BLK_DEV_IDEPCI */ +} + +typedef union { + unsigned all : 8; /* all of the bits together */ + struct { + unsigned head : 4; /* always zeros here */ + unsigned unit : 1; /* drive select number, 0 or 1 */ + unsigned bit5 : 1; /* always 1 */ + unsigned lba : 1; /* using LBA instead of CHS */ + unsigned bit7 : 1; /* always 1 */ + } b; + } select_t; + +#define ide_request_irq(irq,hand,flg,dev,id) request_irq((irq),(hand),(flg),(dev),(id)) +#define ide_free_irq(irq,dev_id) free_irq((irq), (dev_id)) +#define ide_check_region(from,extent) check_region((from), (extent)) +#define ide_request_region(from,extent,name) request_region((from), (extent), (name)) +#define ide_release_region(from,extent) release_region((from), (extent)) + +/* + * The following are not needed for the non-m68k ports + */ +#define ide_ack_intr(hwif) (1) +#define ide_fix_driveid(id) do {} while (0) +#define ide_release_lock(lock) do {} while (0) +#define ide_get_lock(lock, hdlr, data) do {} while (0) + +#endif /* __KERNEL__ */ + +#endif /* __ASM_SH_IDE_H */ diff --git a/include/asm-sh/io.h b/include/asm-sh/io.h index e13c80dbe..57f0367b0 100644 --- a/include/asm-sh/io.h +++ b/include/asm-sh/io.h @@ -1,6 +1,18 @@ #ifndef __ASM_SH_IO_H #define __ASM_SH_IO_H -/* XXXXXXXXXXXXXXXXX */ + +/* + * Convention: + * read{b,w,l}/write{b,w,l} are for PCI, + * while in{b,w,l}/out{b,w,l} are for ISA + * These may (will) be platform specific function. + * + * In addition, we have + * ctrl_in{b,w,l}/ctrl_out{b,w,l} for SuperH specific I/O. + * which are processor specific. + */ + +#include <asm/cache.h> #define virt_to_bus virt_to_phys #define bus_to_virt phys_to_virt @@ -20,7 +32,7 @@ extern __inline__ unsigned long readl(unsigned long addr) return *(volatile unsigned long*)addr; } -extern __inline__ void writeb(unsigned short b, unsigned long addr) +extern __inline__ void writeb(unsigned char b, unsigned long addr) { *(volatile unsigned char*)addr = b; } @@ -75,6 +87,36 @@ extern __inline__ void outl(unsigned int b, unsigned long addr) return writel(b,addr); } +extern __inline__ unsigned long ctrl_inb(unsigned long addr) +{ + return *(volatile unsigned char*)addr; +} + +extern __inline__ unsigned long ctrl_inw(unsigned long addr) +{ + return *(volatile unsigned short*)addr; +} + +extern __inline__ unsigned long ctrl_inl(unsigned long addr) +{ + return *(volatile unsigned long*)addr; +} + +extern __inline__ void ctrl_outb(unsigned char b, unsigned long addr) +{ + *(volatile unsigned char*)addr = b; +} + +extern __inline__ void ctrl_outw(unsigned short b, unsigned long addr) +{ + *(volatile unsigned short*)addr = b; +} + +extern __inline__ void ctrl_outl(unsigned int b, unsigned long addr) +{ + *(volatile unsigned long*)addr = b; +} + #define inb_p inb #define outb_p outb @@ -93,7 +135,7 @@ extern __inline__ unsigned long virt_to_phys(volatile void * address) extern __inline__ void * phys_to_virt(unsigned long address) { - return (void *)KSEG0ADDR(address); + return (void *)P1SEGADDR(address); } extern void * ioremap(unsigned long phys_addr, unsigned long size); @@ -115,7 +157,7 @@ extern void iounmap(void *addr); */ extern __inline__ void * ioremap(unsigned long offset, unsigned long size) { - return (void *) KSEG1ADDR(offset); + return (void *) P2SEGADDR(offset); } /* @@ -125,7 +167,7 @@ extern __inline__ void * ioremap(unsigned long offset, unsigned long size) */ extern __inline__ void * ioremap_nocache (unsigned long offset, unsigned long size) { - return (void *) KSEG1ADDR(offset); + return (void *) P2SEGADDR(offset); } extern __inline__ void iounmap(void *addr) @@ -148,11 +190,30 @@ out: return retval; } -/* Nothing to do */ +/* + * The caches on some architectures aren't dma-coherent and have need to + * handle this in software. There are three types of operations that + * can be applied to dma buffers. + * + * - dma_cache_wback_inv(start, size) makes caches and RAM coherent by + * writing the content of the caches back to memory, if necessary. + * The function also invalidates the affected part of the caches as + * necessary before DMA transfers from outside to memory. + * - dma_cache_inv(start, size) invalidates the affected parts of the + * caches. Dirty lines of the caches may be written back or simply + * be discarded. This operation is necessary before dma operations + * to the memory. + * - dma_cache_wback(start, size) writes back any dirty lines but does + * not invalidate the cache. This can be used before DMA reads from + * memory, + */ -#define dma_cache_inv(_start,_size) do { } while (0) -#define dma_cache_wback(_start,_size) do { } while (0) -#define dma_cache_wback_inv(_start,_size) do { } while (0) +#define dma_cache_wback_inv(_start,_size) \ + cache_flush_area((unsigned long)(_start),((unsigned long)(_start)+(_size))) +#define dma_cache_inv(_start,_size) \ + cache_purge_area((unsigned long)(_start),((unsigned long)(_start)+(_size))) +#define dma_cache_wback(_start,_size) \ + cache_wback_area((unsigned long)(_start),((unsigned long)(_start)+(_size))) #endif /* __KERNEL__ */ #endif /* __ASM_SH_IO_H */ diff --git a/include/asm-sh/ioctls.h b/include/asm-sh/ioctls.h index 56b5bff70..3905e572c 100644 --- a/include/asm-sh/ioctls.h +++ b/include/asm-sh/ioctls.h @@ -39,9 +39,9 @@ #define TIOCSTI _IOW('T', 18, char) /* 0x5412 */ #define TIOCMGET _IOR('T', 21, unsigned int) /* 0x5415 */ -#define TIOCMBIS _IOW('T', 22, unsigne int) /* 0x5416 */ -#define TIOCMBIC _IOW('T', 23, unsigne int) /* 0x5417 */ -#define TIOCMSET _IOW('T', 24, unsigne int) /* 0x5418 */ +#define TIOCMBIS _IOW('T', 22, unsigned int) /* 0x5416 */ +#define TIOCMBIC _IOW('T', 23, unsigned int) /* 0x5417 */ +#define TIOCMSET _IOW('T', 24, unsigned int) /* 0x5418 */ # define TIOCM_LE 0x001 # define TIOCM_DTR 0x002 # define TIOCM_RTS 0x004 diff --git a/include/asm-sh/irq.h b/include/asm-sh/irq.h index 1a5cf7727..a1c06075b 100644 --- a/include/asm-sh/irq.h +++ b/include/asm-sh/irq.h @@ -14,13 +14,13 @@ #define TIMER_PRIORITY 1 /* - * 40 = 24+16 + * 48 = 32+16 * - * 24 for on chip support modules. + * 32 for on chip support modules. * 16 for external interrupts. * */ -#define NR_IRQS 40 +#define NR_IRQS 48 extern void disable_irq(unsigned int); extern void disable_irq_nosync(unsigned int); diff --git a/include/asm-sh/mmu_context.h b/include/asm-sh/mmu_context.h index 7bdb2bd5f..d08cf7863 100644 --- a/include/asm-sh/mmu_context.h +++ b/include/asm-sh/mmu_context.h @@ -24,6 +24,8 @@ extern unsigned long mmu_context_cache; extern __inline__ void get_new_mmu_context(struct mm_struct *mm) { + extern void flush_tlb_all(void); + unsigned long mc = ++mmu_context_cache; if (!(mc & MMU_CONTEXT_ASID_MASK)) { @@ -38,7 +40,7 @@ get_new_mmu_context(struct mm_struct *mm) mm->context = mc; } -/*P +/* * Get MMU context if needed. */ extern __inline__ void @@ -53,7 +55,7 @@ get_mmu_context(struct mm_struct *mm) } } -/*P +/* * Initialize the context related info for a new mm_struct * instance. */ @@ -63,7 +65,7 @@ extern __inline__ void init_new_context(struct task_struct *tsk, mm->context = NO_CONTEXT; } -/*P +/* * Destroy context related info for an mm_struct that is about * to be put to rest. */ @@ -74,18 +76,35 @@ extern __inline__ void destroy_context(struct mm_struct *mm) /* Other MMU related constants. */ +#if defined(__sh3__) #define MMU_PTEH 0xFFFFFFF0 /* Page table entry register HIGH */ #define MMU_PTEL 0xFFFFFFF4 /* Page table entry register LOW */ +#define MMU_TTB 0xFFFFFFF8 /* Translation table base register */ +#define MMU_TEA 0xFFFFFFFC /* TLB Exception Address */ + #define MMUCR 0xFFFFFFE0 /* MMU Control Register */ #define MMU_TLB_ADDRESS_ARRAY 0xF2000000 #define MMU_PAGE_ASSOC_BIT 0x80 #define MMU_NTLB_ENTRIES 128 /* for 7708 */ - #define MMU_CONTROL_INIT 0x007 /* SV=0, TF=1, IX=1, AT=1 */ -#include <asm/uaccess.h> /* to get the definition of __m */ +#elif defined(__SH4__) +#define MMU_PTEH 0xFF000000 /* Page table entry register HIGH */ +#define MMU_PTEL 0xFF000004 /* Page table entry register LOW */ +#define MMU_TTB 0xFF000008 /* Translation table base register */ +#define MMU_TEA 0xFF00000C /* TLB Exception Address */ + +#define MMUCR 0xFF000010 /* MMU Control Register */ + +#define MMU_ITLB_ADDRESS_ARRAY 0xF2000000 +#define MMU_UTLB_ADDRESS_ARRAY 0xF6000000 +#define MMU_PAGE_ASSOC_BIT 0x80 + +#define MMU_NTLB_ENTRIES 64 /* for 7750 */ +#define MMU_CONTROL_INIT 0x205 /* SQMD=1, SV=0, TI=1, AT=1 */ +#endif extern __inline__ void set_asid (unsigned long asid) { @@ -105,7 +124,7 @@ extern __inline__ unsigned long get_asid (void) return asid; } -/*P +/* * After we have set current->mm to a new value, this activates * the context for the new mm so we see the new mappings. */ @@ -117,7 +136,6 @@ extern __inline__ void activate_context(struct mm_struct *mm) /* MMU_TTB can be used for optimizing the fault handling. (Currently not used) */ -#define MMU_TTB 0xFFFFFFF8 /* Translation table base register */ extern __inline__ void switch_mm(struct mm_struct *prev, struct mm_struct *next, struct task_struct *tsk, unsigned int cpu) diff --git a/include/asm-sh/page.h b/include/asm-sh/page.h index ce6a737f7..9c8b732b2 100644 --- a/include/asm-sh/page.h +++ b/include/asm-sh/page.h @@ -5,12 +5,12 @@ * Copyright (C) 1999 Niibe Yutaka */ -/* XXX - [ P0 (virtual) ] 0x00000000 <------ User space - [ P1 (fixed) write-through] 0x80000000 <------ Kernel space - [ P2 (fixed) non-cachable] 0xA0000000 <------ Physical access - [ P3 (virtual) write-back] 0xC0000000 <------ not used - [ P4 control ] 0xE0000000 +/* + [ P0/U0 (virtual) ] 0x00000000 <------ User space + [ P1 (fixed) cached ] 0x80000000 <------ Kernel space + [ P2 (fixed) non-cachable] 0xA0000000 <------ Physical access + [ P3 (virtual) cached] 0xC0000000 <------ not used + [ P4 control ] 0xE0000000 */ #include <linux/config.h> diff --git a/include/asm-sh/pgtable.h b/include/asm-sh/pgtable.h index 1520ea690..cf5eab380 100644 --- a/include/asm-sh/pgtable.h +++ b/include/asm-sh/pgtable.h @@ -3,19 +3,18 @@ /* Copyright (C) 1999 Niibe Yutaka */ -#include <linux/config.h> - /* * This file contains the functions and defines necessary to modify and use * the SuperH page table tree. */ #ifndef __ASSEMBLY__ #include <asm/processor.h> +#include <asm/addrspace.h> #include <linux/threads.h> extern pgd_t swapper_pg_dir[1024]; -#ifdef CONFIG_CPU_SH3 +#if defined(__sh3__) /* Cache flushing: * * - flush_cache_all() flushes entire cache @@ -33,17 +32,17 @@ extern pgd_t swapper_pg_dir[1024]; #define flush_cache_page(vma, vmaddr) do { } while (0) #define flush_page_to_ram(page) do { } while (0) #define flush_icache_range(start, end) do { } while (0) -#elif CONFIG_CPU_SH4 +#elif defined(__SH4__) /* * Caches are broken on SH-4, so we need them. - * You do bad job! */ -flush_cache_all() -flush_cache_mm(mm) -flush_cache_range(mm, start, end) -flush_cache_page(vma, vmaddr) -flush_page_to_ram(page) -flush_icache_range(start, end) +extern void flush_cache_all(void); +extern void flush_cache_mm(struct mm_struct *mm); +extern void flush_cache_range(struct mm_struct *mm, unsigned long start, + unsigned long end); +extern void flush_cache_page(struct vm_area_struct *vma, unsigned long addr); +extern void flush_page_to_ram(unsigned long page); +extern void flush_icache_range(unsigned long start, unsigned long end); #endif /* TLB flushing: @@ -86,9 +85,9 @@ extern void flush_tlb_page(struct vm_area_struct *vma, unsigned long page); #define USER_PTRS_PER_PGD (TASK_SIZE/PGDIR_SIZE) #ifndef __ASSEMBLY__ -#define VMALLOC_START 0xc0000000 +#define VMALLOC_START P3SEG #define VMALLOC_VMADDR(x) ((unsigned long)(x)) -#define VMALLOC_END 0xe0000000 +#define VMALLOC_END P4SEG #define _PAGE_READ 0x001 /* software: read access alowed */ #define _PAGE_ACCESSED 0x002 /* software: page referenced */ @@ -100,11 +99,17 @@ extern void flush_tlb_page(struct vm_area_struct *vma, unsigned long page); /* 0x080 */ #define _PAGE_PRESENT 0x100 /* V-bit : page is valid */ +#if defined(__sh3__) /* Mask which drop software flags */ -#define _PAGE_FLAGS_HARDWARE_MASK 0xfffff164 +#define _PAGE_FLAGS_HARDWARE_MASK 0x1ffff164 /* Flags defalult: SZ=1 (4k-byte), C=1 (cachable), SH=0 (not shared) */ #define _PAGE_FLAGS_HARDWARE_DEFAULT 0x00000018 - +#elif defined(__SH4__) +/* Mask which drops software flags */ +#define _PAGE_FLAGS_HARDWARE_MASK 0x1ffff164 +/* Flags defalult: SZ=01 (4k-byte), C=1 (cachable), SH=0 (not shared), WT=0 */ +#define _PAGE_FLAGS_HARDWARE_DEFAULT 0x00000018 +#endif #define _PAGE_TABLE (_PAGE_PRESENT | _PAGE_RW | _PAGE_USER | _PAGE_ACCESSED | _PAGE_DIRTY) #define _KERNPG_TABLE (_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY) diff --git a/include/asm-sh/processor.h b/include/asm-sh/processor.h index 3087af5d0..4efcf3fe2 100644 --- a/include/asm-sh/processor.h +++ b/include/asm-sh/processor.h @@ -20,9 +20,20 @@ /* * CPU type and hardware bug flags. Kept separately for each CPU. */ +enum cpu_type { + CPU_SH7708, /* Represents 7708, 7708S, 7708R, 7709 */ + CPU_SH7729, /* Represents 7709A, 7729 */ + CPU_SH7750, + CPU_SH_NONE +}; struct sh_cpuinfo { + enum cpu_type type; unsigned long loops_per_sec; + + char hard_math; + + /* Not yet used */ unsigned long *pgd_quick; unsigned long *pte_quick; unsigned long pgtable_cache_sz; @@ -30,7 +41,7 @@ struct sh_cpuinfo { extern struct sh_cpuinfo boot_cpu_data; -#define cpu_data &boot_cpu_data +#define cpu_data (&boot_cpu_data) #define current_cpu_data boot_cpu_data /* @@ -43,6 +54,33 @@ extern struct sh_cpuinfo boot_cpu_data; */ #define TASK_UNMAPPED_BASE (TASK_SIZE / 3) +#define NUM_FPU_REGS 16 + +struct sh_fpu_hard_struct { + unsigned long fp_regs[NUM_FPU_REGS]; + unsigned long xf_regs[NUM_FPU_REGS]; + unsigned long fpscr; + unsigned long fpul; + + long status; /* software status information */ +}; + +/* Dummy fpu emulator */ +struct sh_fpu_soft_struct { + unsigned long fp_regs[NUM_FPU_REGS]; + unsigned long xf_regs[NUM_FPU_REGS]; + unsigned long fpscr; + unsigned long fpul; + + unsigned char lookahead; + unsigned long entry_pc; +}; + +union sh_fpu_union { + struct sh_fpu_hard_struct hard; + struct sh_fpu_soft_struct soft; +}; + struct thread_struct { unsigned long sp; unsigned long pc; @@ -50,6 +88,9 @@ struct thread_struct { unsigned long trap_no, error_code; unsigned long address; /* Hardware debugging registers may come here */ + + /* floating point info */ + union sh_fpu_union fpu; }; #define INIT_MMAP \ @@ -59,6 +100,8 @@ struct thread_struct { sizeof(init_stack) + (long) &init_stack, /* sp */ \ 0, /* pc */ \ 0, 0, \ + 0, \ + {{{0,}},} \ } /* @@ -69,7 +112,7 @@ struct thread_struct { regs->pr = 0; \ regs->sr = 0; /* User mode. */ \ regs->pc = new_pc; \ - regs->u_regs[UREG_SP] = new_sp + regs->sp = new_sp /* Forward declaration, a strange C thing */ struct task_struct; @@ -97,6 +140,49 @@ extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags); #define forget_segments() do { } while (0) /* + * FPU lazy state save handling.. + */ +#define SR_FD 0x00008000 + +extern __inline__ void release_fpu(void) +{ + unsigned long __dummy; + + /* Set FD flag in SR */ + __asm__ __volatile__ ("stc sr,%0\n\t" + "or %1,%0\n\t" + "ldc %0,sr" + : "=&r" (__dummy) + : "r" (SR_FD)); +} + +extern __inline__ void grab_fpu(void) +{ + unsigned long __dummy; + + /* Clear out FD flag in SR */ + __asm__ __volatile__ ("stc sr,%0\n\t" + "and %1,%0\n\t" + "ldc %0,sr" + : "=&r" (__dummy) + : "r" (~SR_FD)); +} + +extern void save_fpu(struct task_struct *__tsk); + +#define unlazy_fpu(tsk) do { \ + if (tsk->flags & PF_USEDFPU) \ + save_fpu(tsk); \ +} while (0) + +#define clear_fpu(tsk) do { \ + if (tsk->flags & PF_USEDFPU) { \ + tsk->flags &= ~PF_USEDFPU; \ + release_fpu(); \ + } \ +} while (0) + +/* * Return saved PC of a blocked thread. */ extern __inline__ unsigned long thread_saved_pc(struct thread_struct *t) diff --git a/include/asm-sh/ptrace.h b/include/asm-sh/ptrace.h index 3f2da6d2d..ad3ab8905 100644 --- a/include/asm-sh/ptrace.h +++ b/include/asm-sh/ptrace.h @@ -2,7 +2,7 @@ #define __ASM_SH_PTRACE_H /* - * Copyright (C) 1999 Niibe Yutaka + * Copyright (C) 1999 Niibe Yutaka * */ @@ -12,12 +12,13 @@ */ struct pt_regs { long syscall_nr; - unsigned long u_regs[16]; + unsigned long sr; + unsigned long sp; + unsigned long regs[15]; unsigned long gbr; unsigned long mach; unsigned long macl; unsigned long pr; - unsigned long sr; unsigned long pc; }; @@ -27,5 +28,4 @@ struct pt_regs { extern void show_regs(struct pt_regs *); #endif -#define UREG_SP 15 #endif /* __ASM_SH_PTRACE_H */ diff --git a/include/asm-sh/sigcontext.h b/include/asm-sh/sigcontext.h index de712bcca..c1a77873b 100644 --- a/include/asm-sh/sigcontext.h +++ b/include/asm-sh/sigcontext.h @@ -5,13 +5,14 @@ struct sigcontext { unsigned long oldmask; /* CPU registers */ - unsigned long u_regs[16]; - unsigned long gbr; - unsigned long mach; - unsigned long macl; - unsigned long pr; - unsigned long sr; - unsigned long pc; + unsigned long sc_regs[15]; + unsigned long sc_gbr; + unsigned long sc_mach; + unsigned long sc_macl; + unsigned long sc_pr; + unsigned long sc_sp; + unsigned long sc_sr; + unsigned long sc_pc; }; #endif /* __ASM_SH_SIGCONTEXT_H */ diff --git a/include/asm-sh/signal.h b/include/asm-sh/signal.h index d76c0daf2..ab4663210 100644 --- a/include/asm-sh/signal.h +++ b/include/asm-sh/signal.h @@ -6,10 +6,6 @@ /* Avoid too many header ordering problems. */ struct siginfo; -#ifdef __KERNEL__ -/* Most things should be clean enough to redefine this at will, if care - is taken to make libc match. */ - #define _NSIG 64 #define _NSIG_BPW 32 #define _NSIG_WORDS (_NSIG / _NSIG_BPW) @@ -20,14 +16,6 @@ typedef struct { unsigned long sig[_NSIG_WORDS]; } sigset_t; -#else -/* Here we must cater to libcs that poke about in kernel headers. */ - -#define NSIG 32 -typedef unsigned long sigset_t; - -#endif /* __KERNEL__ */ - #define SIGHUP 1 #define SIGINT 2 #define SIGQUIT 3 diff --git a/include/asm-sh/smp_lock.h b/include/asm-sh/smp_lock.h deleted file mode 100644 index dfe8722a3..000000000 --- a/include/asm-sh/smp_lock.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef __ASM_SH_SMPLOCK_H -#define __ASM_SH_SMPLOCK_H - -#ifndef __SMP__ - -#define lock_kernel() do { } while(0) -#define unlock_kernel() do { } while(0) -#define release_kernel_lock(task, cpu, depth) ((depth) = 1) -#define reacquire_kernel_lock(task, cpu, depth) do { } while(0) - -#else - -#error "We do not support SMP on SH yet" - -#endif /* __SMP__ */ - -#endif /* __ASM_SH_SMPLOCK_H */ diff --git a/include/asm-sh/smplock.h b/include/asm-sh/smplock.h new file mode 100644 index 000000000..29495778e --- /dev/null +++ b/include/asm-sh/smplock.h @@ -0,0 +1,70 @@ +#ifndef __ASM_SH_SMPLOCK_H +#define __ASM_SH_SMPLOCK_H + +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#ifndef __SMP__ + +#define lock_kernel() do { } while(0) +#define unlock_kernel() do { } while(0) +#define release_kernel_lock(task, cpu, depth) ((depth) = 1) +#define reacquire_kernel_lock(task, cpu, depth) do { } while(0) + +#else + +#error "We do not support SMP on SH yet" +/* + * Default SMP lock implementation + */ + +#include <linux/interrupt.h> +#include <asm/spinlock.h> + +extern spinlock_t kernel_flag; + +/* + * Getting the big kernel lock. + * + * This cannot happen asynchronously, + * so we only need to worry about other + * CPU's. + */ +extern __inline__ void lock_kernel(void) +{ + if (!++current->lock_depth) + spin_lock(&kernel_flag); +} + +extern __inline__ void unlock_kernel(void) +{ + if (--current->lock_depth < 0) + spin_unlock(&kernel_flag); +} + +/* + * Release global kernel lock and global interrupt lock + */ +#define release_kernel_lock(task, cpu) \ +do { \ + if (task->lock_depth >= 0) \ + spin_unlock(&kernel_flag); \ + release_irqlock(cpu); \ + __sti(); \ +} while (0) + +/* + * Re-acquire the kernel lock + */ +#define reacquire_kernel_lock(task) \ +do { \ + if (task->lock_depth >= 0) \ + spin_lock(&kernel_flag); \ +} while (0) + +#endif /* __SMP__ */ + +#endif /* __ASM_SH_SMPLOCK_H */ diff --git a/include/asm-sh/socket.h b/include/asm-sh/socket.h index c6d2ae97d..67f9b89e5 100644 --- a/include/asm-sh/socket.h +++ b/include/asm-sh/socket.h @@ -33,19 +33,10 @@ #define SO_SECURITY_ENCRYPTION_TRANSPORT 23 #define SO_SECURITY_ENCRYPTION_NETWORK 24 -/* Nasty libc5 fixup - bletch */ -#if defined(__KERNEL__) -/* Socket types. */ -#define SOCK_STREAM 1 /* stream (connection) socket */ -#define SOCK_DGRAM 2 /* datagram (conn.less) socket */ -#define SOCK_RAW 3 /* raw socket */ -#define SOCK_RDM 4 /* reliably-delivered message */ -#define SOCK_SEQPACKET 5 /* sequential packet socket */ -#define SOCK_PACKET 10 /* linux specific way of */ - /* getting packets at the dev */ - /* level. For writing rarp and */ - /* other similar things on the */ - /* user level. */ -#endif +#define SO_BINDTODEVICE 25 + +/* Socket filtering */ +#define SO_ATTACH_FILTER 26 +#define SO_DETACH_FILTER 27 #endif /* __ASM_SH_SOCKET_H */ diff --git a/include/asm-sh/softirq.h b/include/asm-sh/softirq.h index 66b0963a8..a1ff593fd 100644 --- a/include/asm-sh/softirq.h +++ b/include/asm-sh/softirq.h @@ -13,8 +13,7 @@ extern unsigned int local_bh_count[NR_CPUS]; #define local_bh_enable() cpu_bh_enable(smp_processor_id()) #define get_active_bhs() (bh_mask & bh_active) -/* XXX */ -#define clear_active_bhs(x) atomic_clear_mask((x),&bh_active) +#define clear_active_bhs(x) atomic_clear_mask((x),(atomic_t *)&bh_active) extern inline void init_bh(int nr, void (*routine)(void)) { diff --git a/include/asm-sh/system.h b/include/asm-sh/system.h index f2e452ca2..40c54212f 100644 --- a/include/asm-sh/system.h +++ b/include/asm-sh/system.h @@ -26,6 +26,7 @@ typedef struct { register unsigned long *__ts6 __asm__ ("r6") = &next->thread.sp; \ register unsigned long __ts7 __asm__ ("r7") = next->thread.pc; \ __asm__ __volatile__ (".balign 4\n\t" \ + "stc.l gbr,@-r15\n\t" \ "sts.l pr,@-r15\n\t" \ "mov.l r8,@-r15\n\t" \ "mov.l r9,@-r15\n\t" \ @@ -41,11 +42,11 @@ typedef struct { "mov.l %0,@r2 ! save PC\n\t" \ "mov.l 2f,%0\n\t" \ "jmp @%0 ! call __switch_to\n\t" \ - " lds r7,pr ! with return to new PC\n" \ - "2:\n" \ - ".long " "_" "__switch_to\n\t" \ + " lds r7,pr ! with return to new PC\n\t" \ ".balign 4\n" \ - "1:\n" \ + "2:\n\t" \ + ".long " "_" "__switch_to\n" \ + "1:\n\t" \ "mov.l @r15+,%0 ! pop R0 from new stack\n\t" \ "mov.l @r15+,r14\n\t" \ "mov.l @r15+,r13\n\t" \ @@ -55,6 +56,7 @@ typedef struct { "mov.l @r15+,r9\n\t" \ "mov.l @r15+,r8\n\t" \ "lds.l @r15+,pr\n\t" \ + "ldc.l @r15+,gbr\n\t" \ :"=&z" (__last) \ :"0" (prev), \ "r" (__ts1), "r" (__ts2), \ @@ -84,6 +86,9 @@ extern void __xchg_called_with_bad_pointer(void); #define mb() __asm__ __volatile__ ("": : :"memory") #define rmb() mb() #define wmb() __asm__ __volatile__ ("": : :"memory") +#define set_rmb(var, value) do { xchg(&var, value); } while (0) +#define set_mb(var, value) set_rmb(var, value) +#define set_wmb(var, value) do { var = value; wmb(); } while (0) /* Interrupt Control */ extern __inline__ void __sti(void) @@ -164,7 +169,7 @@ extern __inline__ unsigned long xchg_u32(volatile int * m, unsigned long val) return retval; } -static __inline__ unsigned long __xchg(unsigned long x, void * ptr, int size) +static __inline__ unsigned long __xchg(unsigned long x, volatile void * ptr, int size) { switch (size) { case 4: diff --git a/include/asm-sh/termbits.h b/include/asm-sh/termbits.h index da16fd45c..20e70f139 100644 --- a/include/asm-sh/termbits.h +++ b/include/asm-sh/termbits.h @@ -117,10 +117,21 @@ struct termios { #define HUPCL 0002000 #define CLOCAL 0004000 #define CBAUDEX 0010000 -#define B57600 0010001 -#define B115200 0010002 -#define B230400 0010003 -#define B460800 0010004 +#define B57600 0010001 +#define B115200 0010002 +#define B230400 0010003 +#define B460800 0010004 +#define B500000 0010005 +#define B576000 0010006 +#define B921600 0010007 +#define B1000000 0010010 +#define B1152000 0010011 +#define B1500000 0010012 +#define B2000000 0010013 +#define B2500000 0010014 +#define B3000000 0010015 +#define B3500000 0010016 +#define B4000000 0010017 #define CIBAUD 002003600000 /* input baud rate (not used) */ #define CMSPAR 010000000000 /* mark or space (stick) parity */ #define CRTSCTS 020000000000 /* flow control */ diff --git a/include/asm-sh/termios.h b/include/asm-sh/termios.h index e3fada644..f579c57a6 100644 --- a/include/asm-sh/termios.h +++ b/include/asm-sh/termios.h @@ -47,7 +47,12 @@ struct termio { #define N_AX25 5 #define N_X25 6 /* X.25 async */ #define N_6PACK 7 -#define N_MASC 8 /* Reserved fo Mobitex module <kaz@cafe.net> */ +#define N_MASC 8 /* Reserved for Mobitex module <kaz@cafe.net> */ +#define N_R3964 9 /* Reserved for Simatic R3964 module */ +#define N_PROFIBUS_FDL 10 /* Reserved for Profibus <Dave@mvhi.com> */ +#define N_IRDA 11 /* Linux IR - http://www.cs.uit.no/~dagb/irda/irda.html */ +#define N_SMSBLOCK 12 /* SMS block mode - for talking to GSM data cards about SMS messages */ +#define N_HDLC 13 /* synchronous HDLC */ #ifdef __KERNEL__ diff --git a/include/asm-sh/types.h b/include/asm-sh/types.h index d23eeb74f..2782ac295 100644 --- a/include/asm-sh/types.h +++ b/include/asm-sh/types.h @@ -27,16 +27,16 @@ typedef unsigned long long __u64; */ #ifdef __KERNEL__ -typedef signed char s8; +typedef __signed__ char s8; typedef unsigned char u8; -typedef signed short s16; +typedef __signed__ short s16; typedef unsigned short u16; -typedef signed int s32; +typedef __signed__ int s32; typedef unsigned int u32; -typedef signed long long s64; +typedef __signed__ long long s64; typedef unsigned long long u64; #define BITS_PER_LONG 32 diff --git a/include/asm-sh/uaccess.h b/include/asm-sh/uaccess.h index 37da01331..81a426d8d 100644 --- a/include/asm-sh/uaccess.h +++ b/include/asm-sh/uaccess.h @@ -1,7 +1,8 @@ -/* +/* $Id$ + * * User space memory access functions * - * Copyright (C) 1999 Niibe Yutaka + * Copyright (C) 1999 Niibe Yutaka * * Based on: * MIPS implementation version 1.15 by @@ -48,7 +49,7 @@ unsigned long flag,sum; \ __asm__("clrt; addc %3,%1; movt %0; cmp/hi %4,%1; rotcl %0" \ :"=&r" (flag), "=r" (sum) \ - :"1" (addr), "r" (size), "r" (current->addr_limit.seg)); \ + :"1" (addr), "r" ((int)(size)), "r" (current->addr_limit.seg)); \ flag; }) #define access_ok(type,addr,size) (__range_ok(addr,size) == 0) @@ -229,11 +230,11 @@ __copy_user(void *__to, const void *__from, __kernel_size_t __n) "mov.l 5f,%1\n\t" "jmp @%1\n\t" " mov %7,%0\n\t" - ".align 4\n" + ".balign 4\n" "5: .long 2b\n" ".previous\n" ".section __ex_table,\"a\"\n" - " .align 4\n" + " .balign 4\n" " .long 9b,3b\n" " .long 1b,2b\n" ".previous" @@ -313,11 +314,11 @@ __clear_user(void *addr, __kernel_size_t size) "mov.l 4f,%0\n\t" "jmp @%0\n\t" " mov %7,%0\n" - ".align 4\n" + ".balign 4\n" "4: .long 2b\n" ".previous\n" ".section __ex_table,\"a\"\n" - " .align 4\n" + " .balign 4\n" " .long 1b,3b\n" ".previous" : "=&r" (res), "=&r" (__a), "=&r" (__s) @@ -356,11 +357,11 @@ __strncpy_from_user(unsigned long __dest, unsigned long __src, int __count) "mov.l 4f,%1\n\t" "jmp @%1\n\t" " mov %8,%0\n\t" - ".align 4\n" + ".balign 4\n" "4: .long 2b\n" ".previous\n" ".section __ex_table,\"a\"\n" - " .align 4\n" + " .balign 4\n" " .long 9b,3b\n" " .long 1b,2b\n" ".previous" @@ -380,46 +381,51 @@ if(__access_ok(__sfu_src, __sfu_count)) { \ __sfu_res = __strncpy_from_user((unsigned long) (dest), __sfu_src, __sfu_count); \ } __sfu_res; }) +#define strlen_user(str) strnlen_user(str, ~0UL >> 1) + /* * Return the size of a string (including the ending 0!) */ -extern __inline__ long __strlen_user(const char *__s) +extern __inline__ long __strnlen_user(const char *__s, long __n) { unsigned long res; unsigned long __dummy; __asm__ __volatile__( "mov #-1,%1\n" - "9:\n" + "9:\n\t" + "cmp/eq %4,%0\n\t" + "bt 5f\n\t" "cmp/eq #0,%1\n\t" "bf/s 9b\n\t" "1:\t" " mov.b @%0+,%1\n\t" + "5:\t" "sub %3,%0\n" "2:\n" ".section .fixup,\"ax\"\n" "3:\n\t" "mov.l 4f,%1\n\t" "jmp @%1\n\t" - " mov %4,%0\n" - ".align 4\n" + " mov %5,%0\n" + ".balign 4\n" "4: .long 2b\n" ".previous\n" ".section __ex_table,\"a\"\n" - " .align 4\n" + " .balign 4\n" " .long 1b,3b\n" ".previous" : "=&r" (res), "=&z" (__dummy) - : "0" (__s), "r" (__s), "i" (-EFAULT)); + : "0" (__s), "r" (__s), "r" (__s+__n), "i" (-EFAULT)); return res; } -extern __inline__ long strlen_user(const char *s) +extern __inline__ long strnlen_user(const char *s, long n) { - if(!access_ok(VERIFY_READ, s, 0)) + if(!access_ok(VERIFY_READ, s, n)) return 0; else - return __strlen_user(s); + return __strnlen_user(s, n); } struct exception_table_entry diff --git a/include/asm-sh/unistd.h b/include/asm-sh/unistd.h index aeb6d8715..c416fda37 100644 --- a/include/asm-sh/unistd.h +++ b/include/asm-sh/unistd.h @@ -222,7 +222,8 @@ type name(void) \ register long __sc0 __asm__ ("r0") = __NR_##name; \ __asm__ __volatile__ ("trapa #0" \ : "=z" (__sc0) \ - : "0" (__sc0)); \ + : "0" (__sc0) \ + : "memory" ); \ __syscall_return(type,__sc0); \ } @@ -233,7 +234,8 @@ register long __sc0 __asm__ ("r0") = __NR_##name; \ register long __sc4 __asm__ ("r4") = (long) arg1; \ __asm__ __volatile__ ("trapa #0" \ : "=z" (__sc0) \ - : "0" (__sc0), "r" (__sc4)); \ + : "0" (__sc0), "r" (__sc4) \ + : "memory"); \ __syscall_return(type,__sc0); \ } @@ -245,7 +247,8 @@ register long __sc4 __asm__ ("r4") = (long) arg1; \ register long __sc5 __asm__ ("r5") = (long) arg2; \ __asm__ __volatile__ ("trapa #0" \ : "=z" (__sc0) \ - : "0" (__sc0), "r" (__sc4), "r" (__sc5)); \ + : "0" (__sc0), "r" (__sc4), "r" (__sc5) \ + : "memory"); \ __syscall_return(type,__sc0); \ } @@ -258,7 +261,8 @@ register long __sc5 __asm__ ("r5") = (long) arg2; \ register long __sc6 __asm__ ("r6") = (long) arg3; \ __asm__ __volatile__ ("trapa #0" \ : "=z" (__sc0) \ - : "0" (__sc0), "r" (__sc4), "r" (__sc5), "r" (__sc6)); \ + : "0" (__sc0), "r" (__sc4), "r" (__sc5), "r" (__sc6) \ + : "memory"); \ __syscall_return(type,__sc0); \ } @@ -273,7 +277,8 @@ register long __sc6 __asm__ ("r7") = (long) arg4; \ __asm__ __volatile__ ("trapa #0" \ : "=z" (__sc0) \ : "0" (__sc0), "r" (__sc4), "r" (__sc5), "r" (__sc6), \ - "r" (__sc7)); \ + "r" (__sc7) \ + : "memory" ); \ __syscall_return(type,__sc0); \ } diff --git a/include/linux/acpi.h b/include/linux/acpi.h index d6521ab67..1f5ec5f7a 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -28,8 +28,8 @@ #define ACPI_MINOR_DEV 167 /* RSDP location */ -#define ACPI_BIOS_ROM_BASE ((__u8*) 0xe0000) -#define ACPI_BIOS_ROM_END ((__u8*) 0x100000) +#define ACPI_BIOS_ROM_BASE (0x0e0000) +#define ACPI_BIOS_ROM_END (0x100000) /* Table signatures */ #define ACPI_RSDP1_SIG 0x20445352 /* 'RSD ' */ diff --git a/include/linux/bigmem.h b/include/linux/bigmem.h deleted file mode 100644 index 289183bfe..000000000 --- a/include/linux/bigmem.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef _LINUX_BIGMEM_H -#define _LINUX_BIGMEM_H - -#include <linux/config.h> - -#ifdef CONFIG_BIGMEM - -#include <asm/bigmem.h> - -/* declarations for linux/mm/bigmem.c */ -extern unsigned long bigmem_mapnr; -extern int nr_free_bigpages; - -extern struct page * prepare_bigmem_swapout(struct page *); -extern struct page * replace_with_bigmem(struct page *); - -#else /* CONFIG_BIGMEM */ - -#define prepare_bigmem_swapout(page) page -#define replace_with_bigmem(page) page -#define kmap(kaddr, type) kaddr -#define kunmap(vaddr, type) do { } while (0) -#define nr_free_bigpages 0 - -#endif /* CONFIG_BIGMEM */ - -/* when CONFIG_BIGMEM is not set these will be plain clear/copy_page */ -extern inline void clear_bigpage(unsigned long kaddr) -{ - unsigned long vaddr; - - vaddr = kmap(kaddr, KM_WRITE); - clear_page(vaddr); - kunmap(vaddr, KM_WRITE); -} - -extern inline void copy_bigpage(unsigned long to, unsigned long from) -{ - unsigned long vfrom, vto; - - vfrom = kmap(from, KM_READ); - vto = kmap(to, KM_WRITE); - copy_page(vto, vfrom); - kunmap(vfrom, KM_READ); - kunmap(vto, KM_WRITE); -} - -#endif /* _LINUX_BIGMEM_H */ diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h index 4f7fe13f7..31721c101 100644 --- a/include/linux/binfmts.h +++ b/include/linux/binfmts.h @@ -18,7 +18,7 @@ */ struct linux_binprm{ char buf[128]; - unsigned long page[MAX_ARG_PAGES]; + struct page *page[MAX_ARG_PAGES]; unsigned long p; /* current top of mem */ int sh_bang; struct dentry * dentry; diff --git a/include/linux/bootmem.h b/include/linux/bootmem.h new file mode 100644 index 000000000..a0727169e --- /dev/null +++ b/include/linux/bootmem.h @@ -0,0 +1,29 @@ +#ifndef _LINUX_BOOTMEM_H +#define _LINUX_BOOTMEM_H + +#include <linux/config.h> +#include <asm/pgtable.h> +#include <asm/dma.h> + +/* + * simple boot-time physical memory area allocator. + */ + +extern unsigned long max_low_pfn; + +extern unsigned long __init init_bootmem (unsigned long addr, unsigned long memend); +extern void __init reserve_bootmem (unsigned long addr, unsigned long size); +extern void __init free_bootmem (unsigned long addr, unsigned long size); +extern void * __init __alloc_bootmem (unsigned long size, unsigned long align, unsigned long goal); +#define alloc_bootmem(x) \ + __alloc_bootmem((x), SMP_CACHE_BYTES, __pa(MAX_DMA_ADDRESS)) +#define alloc_bootmem_pages(x) \ + __alloc_bootmem((x), PAGE_SIZE, __pa(MAX_DMA_ADDRESS)) +#define alloc_bootmem_low_pages(x) \ + __alloc_bootmem((x), PAGE_SIZE, 0) +extern unsigned long __init free_all_bootmem (void); + +#endif /* _LINUX_BOOTMEM_H */ + + + diff --git a/include/linux/cdrom.h b/include/linux/cdrom.h index bf3796692..bfcce8e71 100644 --- a/include/linux/cdrom.h +++ b/include/linux/cdrom.h @@ -273,6 +273,7 @@ struct cdrom_generic_command unsigned char *buffer; unsigned int buflen; int stat; + void *reserved[4]; }; @@ -655,6 +656,14 @@ typedef union { } dvd_authinfo; #ifdef __KERNEL__ + +struct cdrom_write_settings { + unsigned char fpacket; /* fixed/variable packets */ + unsigned long packet_size; /* write out this number of packets */ + unsigned long nwa; /* next writeable address */ + unsigned char writeable; /* cdrom is writeable */ +}; + /* Uniform cdrom data structures for cdrom.c */ struct cdrom_device_info { struct cdrom_device_ops *ops; /* link to device_ops */ @@ -673,6 +682,7 @@ struct cdrom_device_info { /* per-device flags */ __u8 sanyo_slot : 2; /* Sanyo 3 CD changer support */ __u8 reserved : 6; /* not used yet */ + struct cdrom_write_settings write; }; struct cdrom_device_ops { @@ -760,8 +770,6 @@ typedef struct { __u8 uru : 1; __u8 dbc_v : 1; __u8 did_v : 1; -#else -#error "Please fix <asm/byteorder.h>" #endif __u8 disc_type; __u8 n_sessions_msb; @@ -806,8 +814,6 @@ typedef struct { __u8 nwa_v : 1; __u8 lra_v : 1; __u8 reserved3 : 6; -#else -#error "Please fix <asm/byteorder.h>" #endif __u32 track_start; __u32 next_writable; @@ -835,15 +841,12 @@ struct cdrom_mechstat_header { __u8 reserved1 : 4; __u8 door_open : 1; __u8 mech_state : 3; -#else -#error "Please fix <asm/byteorder.h>" #endif __u8 curlba[3]; __u8 nslots; __u8 short slot_tablelen; }; - struct cdrom_slot { #if defined(__BIG_ENDIAN_BITFIELD) __u8 disc_present : 1; @@ -853,8 +856,6 @@ struct cdrom_slot { __u8 change : 1; __u8 reserved1 : 6; __u8 disc_present : 1; -#else -#error "Please fix <asm/byteorder.h>" #endif __u8 reserved2[3]; }; @@ -872,6 +873,71 @@ typedef enum { mechtype_cartridge_changer = 5 } mechtype_t; +struct mode_page_header { + __u16 mode_data_length; + __u8 medium_type; + __u8 reserved1; + __u8 reserved2; + __u8 reserved3; + __u16 desc_length; +}; + +typedef struct { + struct mode_page_header header; +#if defined(__BIG_ENDIAN_BITFIELD) + __u8 ps : 1; + __u8 reserved1 : 1; + __u8 page_code : 6; + __u8 page_length; + __u8 reserved2 : 1; + __u8 bufe : 1; + __u8 ls_v : 1; + __u8 test_write : 1; + __u8 write_type : 4; + __u8 multi_session : 2; /* or border, DVD */ + __u8 fp : 1; + __u8 copy : 1; + __u8 track_mode : 4; + __u8 reserved3 : 4; + __u8 data_block_type : 4; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + __u8 page_code : 6; + __u8 reserved1 : 1; + __u8 ps : 1; + __u8 page_length; + __u8 write_type : 4; + __u8 test_write : 1; + __u8 ls_v : 1; + __u8 bufe : 1; + __u8 reserved2 : 1; + __u8 track_mode : 4; + __u8 copy : 1; + __u8 fp : 1; + __u8 multi_session : 2; /* or border, DVD */ + __u8 data_block_type : 4; + __u8 reserved3 : 4; +#endif + __u8 link_size; + __u8 reserved4; +#if defined(__BIG_ENDIAN_BITFIELD) + __u8 reserved5 : 2; + __u8 app_code : 6; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + __u8 app_code : 6; + __u8 reserved5 : 2; +#endif + __u8 session_format; + __u8 reserved6; + __u32 packet_size; + __u16 audio_pause; + __u8 mcn[16]; + __u8 isrc[16]; + __u8 subhdr0; + __u8 subhdr1; + __u8 subhdr2; + __u8 subhdr3; +} write_param_page __attribute__((packed)); + #endif /* End of kernel only stuff */ #endif /* _LINUX_CDROM_H */ diff --git a/include/linux/fs.h b/include/linux/fs.h index 6d88414ea..c6c7d76d2 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -323,6 +323,11 @@ struct iattr { #include <linux/quota.h> #include <linux/mount.h> +/* + * oh the beauties of C type declarations. + */ +struct page; + struct inode { struct list_head i_hash; struct list_head i_list; @@ -350,7 +355,7 @@ struct inode { wait_queue_head_t i_wait; struct file_lock *i_flock; struct vm_area_struct *i_mmap; - struct page *i_pages; + struct list_head i_pages; spinlock_t i_shared_lock; struct dquot *i_dquot[MAXQUOTAS]; struct pipe_inode_info *i_pipe; @@ -769,8 +774,6 @@ extern int fs_may_mount(kdev_t); extern int try_to_free_buffers(struct page *); extern void refile_buffer(struct buffer_head * buf); -extern atomic_t buffermem; - #define BUF_CLEAN 0 #define BUF_LOCKED 1 /* Buffers scheduled for write */ #define BUF_DIRTY 2 /* Dirty buffers, not yet scheduled for write */ @@ -874,7 +877,7 @@ typedef struct { int error; } read_descriptor_t; -typedef int (*read_actor_t)(read_descriptor_t *, const char *, unsigned long); +typedef int (*read_actor_t)(read_descriptor_t *, struct page *, unsigned long, unsigned long); extern struct dentry * lookup_dentry(const char *, struct dentry *, unsigned int); @@ -886,7 +889,14 @@ extern struct dentry * __namei(const char *, unsigned int); extern void iput(struct inode *); extern struct inode * igrab(struct inode *); extern ino_t iunique(struct super_block *, ino_t); -extern struct inode * iget(struct super_block *, unsigned long); + +typedef int (*find_inode_t)(struct inode *, unsigned long, void *); +extern struct inode * iget4(struct super_block *, unsigned long, find_inode_t, void *); +static inline struct inode *iget(struct super_block *sb, unsigned long ino) +{ + return iget4(sb, ino, NULL, NULL); +} + extern void clear_inode(struct inode *); extern struct inode * get_empty_inode(void); @@ -934,11 +944,14 @@ extern void do_generic_file_read(struct file * filp, loff_t *ppos, read_descript extern struct super_block *get_super(kdev_t); +struct super_block *get_empty_super(void); +void remove_vfsmnt(kdev_t dev); extern void put_super(kdev_t); unsigned long generate_cluster(kdev_t, int b[], int); unsigned long generate_cluster_swab32(kdev_t, int b[], int); extern kdev_t ROOT_DEV; +extern void show_buffers(void); extern void mount_root(void); #ifdef CONFIG_BLK_DEV_INITRD diff --git a/include/linux/hdreg.h b/include/linux/hdreg.h index 2fee75fec..df52d3ca9 100644 --- a/include/linux/hdreg.h +++ b/include/linux/hdreg.h @@ -81,6 +81,8 @@ #define WIN_SRST 0x08 /* ATAPI soft reset command */ #define WIN_PACKETCMD 0xa0 /* Send a packet command. */ +#define EXABYTE_ENABLE_NEST 0xf0 + /* WIN_SMART sub-commands */ #define SMART_READ_VALUES 0xd0 diff --git a/include/linux/highmem.h b/include/linux/highmem.h new file mode 100644 index 000000000..3879d1e61 --- /dev/null +++ b/include/linux/highmem.h @@ -0,0 +1,77 @@ +#ifndef _LINUX_HIGHMEM_H +#define _LINUX_HIGHMEM_H + +#include <linux/config.h> +#include <asm/pgtable.h> + +#ifdef CONFIG_HIGHMEM + +extern struct page *highmem_start_page; + +#include <asm/highmem.h> + +/* declarations for linux/mm/highmem.c */ +extern unsigned long highmem_mapnr; +extern unsigned long nr_free_highpages; + +extern struct page * prepare_highmem_swapout(struct page *); +extern struct page * replace_with_highmem(struct page *); + +#else /* CONFIG_HIGHMEM */ + +#define prepare_highmem_swapout(page) page +#define replace_with_highmem(page) page +#define kmap(page, type) page_address(page) +#define kunmap(vaddr, type) do { } while (0) +#define nr_free_highpages 0UL + +#endif /* CONFIG_HIGHMEM */ + +/* when CONFIG_HIGHMEM is not set these will be plain clear/copy_page */ +extern inline void clear_highpage(struct page *page) +{ + unsigned long kaddr; + + kaddr = kmap(page, KM_WRITE); + clear_page((void *)kaddr); + kunmap(kaddr, KM_WRITE); +} + +extern inline void memclear_highpage(struct page *page, unsigned int offset, unsigned int size) +{ + unsigned long kaddr; + + if (offset + size > PAGE_SIZE) + BUG(); + kaddr = kmap(page, KM_WRITE); + memset((void *)(kaddr + offset), 0, size); + kunmap(kaddr, KM_WRITE); +} + +/* + * Same but also flushes aliased cache contents to RAM. + */ +extern inline void memclear_highpage_flush(struct page *page, unsigned int offset, unsigned int size) +{ + unsigned long kaddr; + + if (offset + size > PAGE_SIZE) + BUG(); + kaddr = kmap(page, KM_WRITE); + memset((void *)(kaddr + offset), 0, size); + flush_page_to_ram(page); + kunmap(kaddr, KM_WRITE); +} + +extern inline void copy_highpage(struct page *to, struct page *from) +{ + unsigned long vfrom, vto; + + vfrom = kmap(from, KM_READ); + vto = kmap(to, KM_WRITE); + copy_page((void *)vto, (void *)vfrom); + kunmap(vfrom, KM_READ); + kunmap(vto, KM_WRITE); +} + +#endif /* _LINUX_HIGHMEM_H */ diff --git a/include/linux/ide.h b/include/linux/ide.h index 3ebe6c211..4af0cd253 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -236,6 +236,7 @@ typedef struct ide_drive_s { unsigned long sleep; /* sleep until this time */ unsigned long service_start; /* time we started last request */ unsigned long service_time; /* service time of last request */ + unsigned long timeout; /* max time to wait for irq */ special_t special; /* special action flags */ byte keep_settings; /* restore settings after drive reset */ byte using_dma; /* disk is using dma for read/write */ @@ -338,6 +339,7 @@ typedef void (ide_tuneproc_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 *); /* * hwif_chipset_t is used to keep track of the specific hardware @@ -367,6 +369,7 @@ typedef struct hwif_s { struct gendisk *gd; /* gendisk structure */ ide_tuneproc_t *tuneproc; /* routine to tune PIO mode for drives */ ide_selectproc_t *selectproc; /* tweaks hardware to select drive */ + ide_resetproc_t *resetproc; /* routine to reset controller after a disk reset */ ide_dmaproc_t *dmaproc; /* dma read/write/abort routine */ unsigned long *dmatable; /* dma physical region descriptor table */ struct hwif_s *mate; /* other hwif from same PCI chip */ @@ -581,7 +584,7 @@ void atapi_output_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecoun * This is used on exit from the driver, to designate the next irq handler * and also to start the safety timer. */ -void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler, unsigned int timeout); +void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler); /* * Error reporting, in human readable form (luxurious, but a memory hog). diff --git a/include/linux/iobuf.h b/include/linux/iobuf.h index 9418888f2..420285faf 100644 --- a/include/linux/iobuf.h +++ b/include/linux/iobuf.h @@ -41,7 +41,6 @@ struct kiobuf * region, there won't necessarily be page structs defined for * every address. */ - unsigned long * pagelist; struct page ** maplist; unsigned int locked : 1; /* If set, pages has been locked */ diff --git a/include/linux/joystick.h b/include/linux/joystick.h index 996babd51..ce6923de5 100644 --- a/include/linux/joystick.h +++ b/include/linux/joystick.h @@ -173,26 +173,16 @@ typedef struct { int something; } spinlock_t; * Parport stuff */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) #define USE_PARPORT -#endif -#ifdef USE_PARPORT #include <linux/parport.h> +#include <linux/parport_pc.h> #define JS_PAR_DATA_IN(y) parport_read_data(y->port) #define JS_PAR_DATA_OUT(x,y) parport_write_data(y->port, x) #define JS_PAR_STATUS(y) parport_read_status(y->port) #define JS_PAR_CTRL_IN(y) parport_read_control(y->port) #define JS_PAR_CTRL_OUT(x,y) parport_write_control(y->port, x) -#define JS_PAR_ECTRL_OUT(x,y) parport_write_econtrol(y->port, x) -#else -#define JS_PAR_DATA_IN(y) inb(y) -#define JS_PAR_DATA_OUT(x,y) outb(x,y) -#define JS_PAR_STATUS(y) inb(y+1) -#define JS_PAR_CTRL_IN(y) inb(y+2) -#define JS_PAR_CTRL_OUT(x,y) outb(x,y+2) -#define JS_PAR_ECTRL_OUT(x,y) outb(x,y+0x402) -#endif +#define JS_PAR_ECTRL_OUT(x,y) outb(x, ECONTROL(y->port)) #define JS_PAR_STATUS_INVERT (0x80) #define JS_PAR_CTRL_INVERT (0x04) diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 89bea4477..d5b204c2c 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -94,9 +94,10 @@ struct sysinfo { unsigned long totalswap; /* Total swap space size */ unsigned long freeswap; /* swap space still available */ unsigned short procs; /* Number of current processes */ - unsigned long totalbig; /* Total big memory size */ - unsigned long freebig; /* Available big memory size */ - char _f[20-2*sizeof(long)]; /* Padding: libc5 uses this.. */ + unsigned long totalhigh; /* Total high memory size */ + unsigned long freehigh; /* Available high memory size */ + unsigned int mem_unit; /* Memory unit size in bytes */ + char _f[20-2*sizeof(long)-sizeof(int)]; /* Padding: libc5 uses this.. */ }; #endif diff --git a/include/linux/mm.h b/include/linux/mm.h index 281f6838e..778511f6f 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -8,6 +8,7 @@ #include <linux/config.h> #include <linux/string.h> +#include <linux/list.h> extern unsigned long max_mapnr; extern unsigned long num_physpages; @@ -103,9 +104,8 @@ struct vm_operations_struct { void (*protect)(struct vm_area_struct *area, unsigned long, size_t, unsigned int newprot); int (*sync)(struct vm_area_struct *area, unsigned long, size_t, unsigned int flags); void (*advise)(struct vm_area_struct *area, unsigned long, size_t, unsigned int advise); - unsigned long (*nopage)(struct vm_area_struct * area, unsigned long address, int write_access); - unsigned long (*wppage)(struct vm_area_struct * area, unsigned long address, - unsigned long page); + struct page * (*nopage)(struct vm_area_struct * area, unsigned long address, int write_access); + struct page * (*wppage)(struct vm_area_struct * area, unsigned long address, struct page * page); int (*swapout)(struct vm_area_struct *, struct page *); }; @@ -119,8 +119,7 @@ struct vm_operations_struct { */ typedef struct page { /* these must be first (free area handling) */ - struct page *next; - struct page *prev; + struct list_head list; struct inode *inode; unsigned long offset; struct page *next_hash; @@ -149,11 +148,11 @@ typedef struct page { #define PG_uptodate 3 #define PG_decr_after 5 #define PG_DMA 7 -#define PG_Slab 8 +#define PG_slab 8 #define PG_swap_cache 9 #define PG_skip 10 #define PG_swap_entry 11 -#define PG_BIGMEM 12 +#define PG_highmem 12 /* bits 21-30 unused */ #define PG_reserved 31 @@ -183,27 +182,32 @@ if (!test_and_clear_bit(PG_locked, &(page)->flags)) { \ #define PageReferenced(page) (test_bit(PG_referenced, &(page)->flags)) #define PageDecrAfter(page) (test_bit(PG_decr_after, &(page)->flags)) #define PageDMA(page) (test_bit(PG_DMA, &(page)->flags)) -#define PageSlab(page) (test_bit(PG_Slab, &(page)->flags)) +#define PageSlab(page) (test_bit(PG_slab, &(page)->flags)) #define PageSwapCache(page) (test_bit(PG_swap_cache, &(page)->flags)) #define PageReserved(page) (test_bit(PG_reserved, &(page)->flags)) -#define PageSetSlab(page) (set_bit(PG_Slab, &(page)->flags)) +#define PageSetSlab(page) (set_bit(PG_slab, &(page)->flags)) #define PageSetSwapCache(page) (set_bit(PG_swap_cache, &(page)->flags)) #define PageTestandSetSwapCache(page) \ (test_and_set_bit(PG_swap_cache, &(page)->flags)) -#define PageClearSlab(page) (clear_bit(PG_Slab, &(page)->flags)) +#define PageClearSlab(page) (clear_bit(PG_slab, &(page)->flags)) #define PageClearSwapCache(page)(clear_bit(PG_swap_cache, &(page)->flags)) #define PageTestandClearSwapCache(page) \ (test_and_clear_bit(PG_swap_cache, &(page)->flags)) -#ifdef CONFIG_BIGMEM -#define PageBIGMEM(page) (test_bit(PG_BIGMEM, &(page)->flags)) +#ifdef CONFIG_HIGHMEM +#define PageHighMem(page) (test_bit(PG_highmem, &(page)->flags)) #else -#define PageBIGMEM(page) 0 /* needed to optimize away at compile time */ +#define PageHighMem(page) 0 /* needed to optimize away at compile time */ #endif +#define SetPageReserved(page) do { set_bit(PG_reserved, &(page)->flags); \ + } while (0) +#define ClearPageReserved(page) do { test_and_clear_bit(PG_reserved, &(page)->flags); } while (0) + + /* * Various page->flags bits: * @@ -224,7 +228,7 @@ if (!test_and_clear_bit(PG_locked, &(page)->flags)) { \ * (e.g. a private data page of one process). * * A page may be used for kmalloc() or anyone else who does a - * get_free_page(). In this case the page->count is at least 1, and + * __get_free_page(). In this case the page->count is at least 1, and * all other fields are unused but should be 0 or NULL. The * management of this page is the responsibility of the one who uses * it. @@ -281,20 +285,27 @@ extern mem_map_t * mem_map; * goes to clearing the page. If you want a page without the clearing * overhead, just use __get_free_page() directly.. */ +extern struct page * __get_pages(int gfp_mask, unsigned long order); #define __get_free_page(gfp_mask) __get_free_pages((gfp_mask),0) #define __get_dma_pages(gfp_mask, order) __get_free_pages((gfp_mask) | GFP_DMA,(order)) extern unsigned long FASTCALL(__get_free_pages(int gfp_mask, unsigned long gfp_order)); +extern struct page * get_free_highpage(int gfp_mask); -extern inline unsigned long get_free_page(int gfp_mask) +extern inline unsigned long get_zeroed_page(int gfp_mask) { unsigned long page; page = __get_free_page(gfp_mask); if (page) - clear_page(page); + clear_page((void *)page); return page; } +/* + * The old interface name will be removed in 2.5: + */ +#define get_free_page get_zeroed_page + /* memory.c & swap.c*/ #define free_page(addr) free_pages((addr),0) @@ -302,7 +313,7 @@ extern int FASTCALL(free_pages(unsigned long addr, unsigned long order)); extern int FASTCALL(__free_page(struct page *)); extern void show_free_areas(void); -extern unsigned long put_dirty_page(struct task_struct * tsk,unsigned long page, +extern struct page * put_dirty_page(struct task_struct * tsk, struct page *page, unsigned long address); extern void clear_page_tables(struct mm_struct *, unsigned long, int); @@ -322,12 +333,13 @@ extern int ptrace_writedata(struct task_struct *tsk, char * src, unsigned long d extern int pgt_cache_water[2]; extern int check_pgt_cache(void); -extern unsigned long paging_init(unsigned long start_mem, unsigned long end_mem); -extern void mem_init(unsigned long start_mem, unsigned long end_mem); +extern void paging_init(void); +extern void free_area_init(unsigned long); +extern void mem_init(void); extern void show_mem(void); extern void oom(struct task_struct * tsk); extern void si_meminfo(struct sysinfo * val); -extern void swapin_readahead(unsigned long); +extern void swapin_readahead(pte_t); /* mmap.c */ extern void vma_init(void); @@ -359,19 +371,19 @@ extern void put_cached_page(unsigned long); #define __GFP_HIGH 0x08 #define __GFP_IO 0x10 #define __GFP_SWAP 0x20 -#ifdef CONFIG_BIGMEM -#define __GFP_BIGMEM 0x40 +#ifdef CONFIG_HIGHMEM +#define __GFP_HIGHMEM 0x40 #else -#define __GFP_BIGMEM 0x0 /* noop */ +#define __GFP_HIGHMEM 0x0 /* noop */ #endif -#define __GFP_UNCACHED 0x40 #define __GFP_DMA 0x80 +#define __GFP_UNCACHED 0x100 #define GFP_BUFFER (__GFP_LOW | __GFP_WAIT) #define GFP_ATOMIC (__GFP_HIGH) -#define GFP_BIGUSER (__GFP_LOW | __GFP_WAIT | __GFP_IO | __GFP_BIGMEM) #define GFP_USER (__GFP_LOW | __GFP_WAIT | __GFP_IO) +#define GFP_HIGHUSER (GFP_USER | __GFP_HIGHMEM) #define GFP_KERNEL (__GFP_MED | __GFP_WAIT | __GFP_IO) #define GFP_NFS (__GFP_HIGH | __GFP_WAIT | __GFP_IO) #define GFP_KSWAPD (__GFP_IO | __GFP_SWAP) @@ -389,10 +401,10 @@ extern void put_cached_page(unsigned long); #define GFP_DMA __GFP_DMA -/* Flag - indicates that the buffer can be taken from big memory which is not +/* Flag - indicates that the buffer can be taken from high memory which is not directly addressable by the kernel */ -#define GFP_BIGMEM __GFP_BIGMEM +#define GFP_HIGHMEM __GFP_HIGHMEM /* vma is the first one with address < vma->vm_end, * and even address < vma->vm_start. Have to extend vma. */ @@ -431,11 +443,17 @@ static inline struct vm_area_struct * find_vma_intersection(struct mm_struct * m extern struct vm_area_struct *find_extend_vma(struct task_struct *tsk, unsigned long addr); -#define buffer_under_min() ((atomic_read(&buffermem) >> PAGE_SHIFT) * 100 < \ +#define buffer_under_min() (atomic_read(&buffermem_pages) * 100 < \ buffer_mem.min_percent * num_physpages) #define pgcache_under_min() (atomic_read(&page_cache_size) * 100 < \ page_cache.min_percent * num_physpages) +#define vmlist_access_lock(mm) spin_lock(&mm->page_table_lock) +#define vmlist_access_unlock(mm) spin_unlock(&mm->page_table_lock) +#define vmlist_modify_lock(mm) vmlist_access_lock(mm) +#define vmlist_modify_unlock(mm) vmlist_access_unlock(mm) + + #endif /* __KERNEL__ */ #endif diff --git a/include/linux/nfs.h b/include/linux/nfs.h index 4f7d3230e..c188fc808 100644 --- a/include/linux/nfs.h +++ b/include/linux/nfs.h @@ -122,15 +122,6 @@ struct nfs_fattr { struct nfs_time ctime; }; -struct nfs_sattr { - __u32 mode; - __u32 uid; - __u32 gid; - __u32 size; - struct nfs_time atime; - struct nfs_time mtime; -}; - struct nfs_fsinfo { __u32 tsize; __u32 bsize; @@ -150,7 +141,7 @@ struct nfs_writeargs { struct nfs_sattrargs { struct nfs_fh * fh; - struct nfs_sattr * sattr; + struct iattr * sattr; }; struct nfs_diropargs { @@ -173,7 +164,7 @@ struct nfs_readargs { struct nfs_createargs { struct nfs_fh * fh; const char * name; - struct nfs_sattr * sattr; + struct iattr * sattr; }; struct nfs_renameargs { @@ -193,7 +184,7 @@ struct nfs_symlinkargs { struct nfs_fh * fromfh; const char * fromname; const char * topath; - struct nfs_sattr * sattr; + struct iattr * sattr; }; struct nfs_readdirargs { diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index d91a0b641..df6b7c8df 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -77,11 +77,14 @@ do { \ : NFS_SERVER(inode)->acregmax) #define NFS_FLAGS(inode) ((inode)->u.nfs_i.flags) -#define NFS_REVALIDATING(inode) (NFS_FLAGS(inode) & NFS_INO_REVALIDATE) +#define NFS_REVALIDATING(inode) (NFS_FLAGS(inode) & NFS_INO_REVALIDATING) #define NFS_WRITEBACK(inode) ((inode)->u.nfs_i.writeback) #define NFS_COOKIES(inode) ((inode)->u.nfs_i.cookies) #define NFS_DIREOF(inode) ((inode)->u.nfs_i.direof) +#define NFS_FILEID(inode) ((inode)->u.nfs_i.fileid) +#define NFS_FSID(inode) ((inode)->u.nfs_i.fsid) + /* * These are the default flags for swap requests */ @@ -137,7 +140,7 @@ struct nfs_wreq { extern int nfs_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr); extern int nfs_proc_setattr(struct nfs_server *server, struct nfs_fh *fhandle, - struct nfs_sattr *sattr, struct nfs_fattr *fattr); + struct nfs_fattr *fattr, struct iattr *sattr); extern int nfs_proc_lookup(struct nfs_server *server, struct nfs_fh *dir, const char *name, struct nfs_fh *fhandle, struct nfs_fattr *fattr); @@ -148,7 +151,7 @@ extern int nfs_proc_write(struct nfs_server *server, struct nfs_fh *fhandle, int swap, unsigned long offset, unsigned int count, const void *buffer, struct nfs_fattr *fattr); extern int nfs_proc_create(struct nfs_server *server, struct nfs_fh *dir, - const char *name, struct nfs_sattr *sattr, + const char *name, struct iattr *sattr, struct nfs_fh *fhandle, struct nfs_fattr *fattr); extern int nfs_proc_remove(struct nfs_server *server, struct nfs_fh *dir, const char *name); @@ -159,9 +162,9 @@ extern int nfs_proc_link(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fh *dir, const char *name); extern int nfs_proc_symlink(struct nfs_server *server, struct nfs_fh *dir, const char *name, const char *path, - struct nfs_sattr *sattr); + struct iattr *sattr); extern int nfs_proc_mkdir(struct nfs_server *server, struct nfs_fh *dir, - const char *name, struct nfs_sattr *sattr, + const char *name, struct iattr *sattr, struct nfs_fh *fhandle, struct nfs_fattr *fattr); extern int nfs_proc_rmdir(struct nfs_server *server, struct nfs_fh *dir, const char *name); @@ -174,13 +177,14 @@ extern int nfs_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle, */ extern struct super_block *nfs_read_super(struct super_block *, void *, int); extern int init_nfs_fs(void); +extern void nfs_zap_caches(struct inode *); extern struct inode *nfs_fhget(struct dentry *, struct nfs_fh *, struct nfs_fattr *); extern int nfs_refresh_inode(struct inode *, struct nfs_fattr *); extern int nfs_revalidate(struct dentry *); extern int nfs_open(struct inode *, struct file *); extern int nfs_release(struct inode *, struct file *); -extern int _nfs_revalidate_inode(struct nfs_server *, struct dentry *); +extern int __nfs_revalidate_inode(struct nfs_server *, struct dentry *); /* * linux/fs/nfs/file.c @@ -244,9 +248,9 @@ static inline int nfs_revalidate_inode(struct nfs_server *server, struct dentry *dentry) { struct inode *inode = dentry->d_inode; - if (jiffies - NFS_READTIME(inode) < NFS_ATTRTIMEO(inode)) + if (time_before(jiffies, NFS_READTIME(inode)+NFS_ATTRTIMEO(inode))) return 0; - return _nfs_revalidate_inode(server, dentry); + return __nfs_revalidate_inode(server, dentry); } /* NFS root */ diff --git a/include/linux/nfs_fs_i.h b/include/linux/nfs_fs_i.h index 13bf610b6..22681be9d 100644 --- a/include/linux/nfs_fs_i.h +++ b/include/linux/nfs_fs_i.h @@ -9,6 +9,12 @@ */ struct nfs_inode_info { /* + * The 64bit 'inode number' + */ + __u32 fsid; + __u32 fileid; + + /* * Various flags */ unsigned short flags; @@ -49,7 +55,7 @@ struct nfs_inode_info { /* * Legal inode flag values */ -#define NFS_INO_REVALIDATE 0x0001 /* revalidating attrs */ +#define NFS_INO_REVALIDATING 0x0001 /* revalidating attrs */ #define NFS_IS_SNAPSHOT 0x0010 /* a snapshot file */ /* diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h index 0aff25c29..6410d3d1e 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h @@ -11,10 +11,16 @@ #include <linux/mm.h> #include <linux/fs.h> +#include <linux/highmem.h> +#include <linux/list.h> -static inline unsigned long page_address(struct page * page) +extern inline pte_t get_pagecache_pte(struct page *page) { - return PAGE_OFFSET + ((page - mem_map) << PAGE_SHIFT); + /* + * the pagecache is still machineword sized. The rest of the VM + * can deal with arbitrary sized ptes. + */ + return __pte(page->offset); } /* @@ -30,8 +36,8 @@ static inline unsigned long page_address(struct page * page) #define PAGE_CACHE_MASK PAGE_MASK #define PAGE_CACHE_ALIGN(addr) (((addr)+PAGE_CACHE_SIZE-1)&PAGE_CACHE_MASK) -#define page_cache_alloc() __get_free_page(GFP_USER) -#define page_cache_free(x) free_page(x) +#define page_cache_alloc() __get_pages(GFP_USER, 0) +#define page_cache_free(x) __free_page(x) #define page_cache_release(x) __free_page(x) /* @@ -54,7 +60,7 @@ extern void page_cache_init(unsigned long); * inode pointer and offsets are distributed (ie, we * roughly know which bits are "significant") */ -static inline unsigned long _page_hashfn(struct inode * inode, unsigned long offset) +extern inline unsigned long _page_hashfn(struct inode * inode, unsigned long offset) { #define i (((unsigned long) inode)/(sizeof(struct inode) & ~ (sizeof(struct inode) - 1))) #define o (offset >> PAGE_SHIFT) @@ -82,26 +88,37 @@ extern void __add_page_to_hash_queue(struct page * page, struct page **p); extern void add_to_page_cache(struct page * page, struct inode * inode, unsigned long offset); extern int add_to_page_cache_unique(struct page * page, struct inode * inode, unsigned long offset, struct page **hash); -static inline void add_page_to_hash_queue(struct page * page, struct inode * inode, unsigned long offset) +extern inline void add_page_to_hash_queue(struct page * page, struct inode * inode, unsigned long offset) { __add_page_to_hash_queue(page, page_hash(inode,offset)); } -static inline void add_page_to_inode_queue(struct inode * inode, struct page * page) +extern inline void add_page_to_inode_queue(struct inode * inode, struct page * page) { - struct page **p = &inode->i_pages; - - inode->i_nrpages++; + struct list_head *head = &inode->i_pages; + + if (!inode->i_nrpages++) { + if (!list_empty(head)) + BUG(); + } else { + if (list_empty(head)) + BUG(); + } + list_add(&page->list, head); page->inode = inode; - page->prev = NULL; - if ((page->next = *p) != NULL) - page->next->prev = page; - *p = page; +} + +extern inline void remove_page_from_inode_queue(struct page * page) +{ + struct inode * inode = page->inode; + + inode->i_nrpages--; + list_del(&page->list); } extern void ___wait_on_page(struct page *); -static inline void wait_on_page(struct page * page) +extern inline void wait_on_page(struct page * page) { if (PageLocked(page)) ___wait_on_page(page); diff --git a/include/linux/pci.h b/include/linux/pci.h index 2fe52c2b4..a71f4c1be 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -406,6 +406,7 @@ struct pci_ops { void pcibios_init(void); void pcibios_fixup_bus(struct pci_bus *); +int pcibios_enable_device(struct pci_dev *); char *pcibios_setup (char *str); void pcibios_update_resource(struct pci_dev *, struct resource *, @@ -460,9 +461,11 @@ int pci_read_config_dword(struct pci_dev *dev, int where, u32 *val); int pci_write_config_byte(struct pci_dev *dev, int where, u8 val); int pci_write_config_word(struct pci_dev *dev, int where, u16 val); int pci_write_config_dword(struct pci_dev *dev, int where, u32 val); +int pci_enable_device(struct pci_dev *dev); void pci_set_master(struct pci_dev *dev); +int pci_set_power_state(struct pci_dev *dev, int state); -/* Helper functions (drivers/pci/setup.c) */ +/* Helper functions for low-level code (drivers/pci/setup.c) */ int pci_claim_resource(struct pci_dev *, int); void pci_assign_unassigned_resources(u32 min_io, u32 min_mem); @@ -471,7 +474,7 @@ void pci_fixup_irqs(u8 (*)(struct pci_dev *, u8 *), int (*)(struct pci_dev *, u8, u8)); /* - * simple PCI probing for drivers + * simple PCI probing for drivers (drivers/pci/helper.c) */ struct pci_simple_probe_entry; @@ -524,8 +527,8 @@ extern inline struct pci_dev *pci_find_subsys(unsigned int vendor, unsigned int unsigned int ss_vendor, unsigned int ss_device, struct pci_dev *from) { return NULL; } -extern inline void pci_set_master(struct pci_dev *dev) -{ return; } +extern inline void pci_set_master(struct pci_dev *dev) { } +extern inline int pci_enable_device(struct pci_dev *dev) { return 0; } extern inline int pci_simple_probe (struct pci_simple_probe_entry *list, size_t match_limit, pci_simple_probe_callback cb, void *drvr_data) diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 062488c0d..e4bca6807 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -100,6 +100,7 @@ #define PCI_DEVICE_ID_COMPAQ_TOKENRING 0x0508 #define PCI_DEVICE_ID_COMPAQ_1280 0x3033 #define PCI_DEVICE_ID_COMPAQ_TRIFLEX 0x4000 +#define PCI_DEVICE_ID_COMPAQ_6010 0x6010 #define PCI_DEVICE_ID_COMPAQ_SMART2P 0xae10 #define PCI_DEVICE_ID_COMPAQ_NETEL100 0xae32 #define PCI_DEVICE_ID_COMPAQ_NETEL10 0xae34 @@ -729,6 +730,10 @@ #define PCI_DEVICE_ID_RENDITION_VERITE 0x0001 #define PCI_DEVICE_ID_RENDITION_VERITE2100 0x2000 +#define PCI_VENDOR_ID_RCC 0x1166 +#define PCI_DEVICE_ID_RCC_HE 0x0008 +#define PCI_DEVICE_ID_RCC_LE 0x0009 + #define PCI_VENDOR_ID_TOSHIBA 0x1179 #define PCI_DEVICE_ID_TOSHIBA_601 0x0601 #define PCI_DEVICE_ID_TOSHIBA_TOPIC95 0x060a diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h index d863aaefe..35f730151 100644 --- a/include/linux/proc_fs.h +++ b/include/linux/proc_fs.h @@ -550,6 +550,7 @@ extern inline int proc_driver_register(const char *module_name) return 0; } +extern struct proc_dir_entry proc_root; #endif /* CONFIG_PROC_FS */ diff --git a/include/linux/sched.h b/include/linux/sched.h index b568a7b7b..c198260e0 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -357,6 +357,10 @@ struct task_struct { struct signal_queue *sigqueue, **sigqueue_tail; unsigned long sas_ss_sp; size_t sas_ss_size; + +/* Thread group tracking */ + u32 parent_exec_id; + u32 self_exec_id; }; /* @@ -422,6 +426,7 @@ struct task_struct { /* files */ &init_files, \ /* mm */ NULL, &init_mm, \ /* signals */ SPIN_LOCK_UNLOCKED, &init_signals, {{0}}, {{0}}, NULL, &init_task.sigqueue, 0, 0, \ +/* exec cts */ 0,0, \ } #ifndef INIT_TASK_SIZE diff --git a/include/linux/shm.h b/include/linux/shm.h index d83787312..6ba237e92 100644 --- a/include/linux/shm.h +++ b/include/linux/shm.h @@ -24,7 +24,7 @@ struct shmid_kernel struct shmid_ds u; /* the following are private */ unsigned long shm_npages; /* size of segment (pages) */ - unsigned long *shm_pages; /* array of ptrs to frames -> SHMMAX */ + pte_t *shm_pages; /* array of ptrs to frames -> SHMMAX */ struct vm_area_struct *attaches; /* descriptors for attaches */ }; @@ -72,7 +72,7 @@ asmlinkage long sys_shmget (key_t key, int size, int flag); asmlinkage long sys_shmat (int shmid, char *shmaddr, int shmflg, unsigned long *addr); asmlinkage long sys_shmdt (char *shmaddr); asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds *buf); -extern void shm_unuse(unsigned long entry, unsigned long page); +extern void shm_unuse(pte_t entry, struct page *page); #endif /* __KERNEL__ */ diff --git a/include/linux/slab.h b/include/linux/slab.h index 3097a8db2..fa344d816 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -45,7 +45,7 @@ typedef struct kmem_cache_s kmem_cache_t; #define SLAB_CTOR_VERIFY 0x004UL /* tell constructor it's a verify call */ /* prototypes */ -extern long kmem_cache_init(long, long); +extern void kmem_cache_init(void); extern void kmem_cache_sizes_init(void); extern kmem_cache_t *kmem_find_general_cachep(size_t); extern kmem_cache_t *kmem_cache_create(const char *, size_t, size_t, unsigned long, diff --git a/include/linux/swap.h b/include/linux/swap.h index 0b0baf1e8..7030b788d 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -35,8 +35,6 @@ union swap_header { #define MAX_SWAP_BADPAGES \ ((__swapoffset(magic.magic) - __swapoffset(info.badpages)) / sizeof(int)) -#undef DEBUG_SWAP - #include <asm/atomic.h> #define SWP_USED 1 @@ -69,7 +67,7 @@ extern struct list_head lru_cache; extern atomic_t nr_async_pages; extern struct inode swapper_inode; extern atomic_t page_cache_size; -extern atomic_t buffermem; +extern atomic_t buffermem_pages; /* Incomplete types for prototype declarations: */ struct task_struct; @@ -87,36 +85,35 @@ extern int try_to_free_pages(unsigned int gfp_mask); /* linux/mm/page_io.c */ extern void rw_swap_page(int, struct page *, int); -extern void rw_swap_page_nolock(int, unsigned long, char *, int); -extern void swap_after_unlock_page (unsigned long entry); +extern void rw_swap_page_nolock(int, pte_t, char *, int); /* linux/mm/page_alloc.c */ /* linux/mm/swap_state.c */ extern void show_swap_cache_info(void); -extern void add_to_swap_cache(struct page *, unsigned long); -extern int swap_duplicate(unsigned long); +extern void add_to_swap_cache(struct page *, pte_t); +extern int swap_duplicate(pte_t); extern int swap_check_entry(unsigned long); -struct page * lookup_swap_cache(unsigned long); -extern struct page * read_swap_cache_async(unsigned long, int); +struct page * lookup_swap_cache(pte_t); +extern struct page * read_swap_cache_async(pte_t, int); #define read_swap_cache(entry) read_swap_cache_async(entry, 1); -extern int FASTCALL(swap_count(unsigned long)); -extern unsigned long acquire_swap_entry(struct page *page); +extern int swap_count(struct page *); +extern pte_t acquire_swap_entry(struct page *page); /* * Make these inline later once they are working properly. */ extern void __delete_from_swap_cache(struct page *page); extern void delete_from_swap_cache(struct page *page); -extern void free_page_and_swap_cache(unsigned long addr); +extern void free_page_and_swap_cache(struct page *page); /* linux/mm/swapfile.c */ extern unsigned int nr_swapfiles; extern struct swap_info_struct swap_info[]; extern int is_swap_partition(kdev_t); void si_swapinfo(struct sysinfo *); -unsigned long get_swap_page(void); -extern void FASTCALL(swap_free(unsigned long)); +pte_t get_swap_page(void); +extern void swap_free(pte_t); struct swap_list_t { int head; /* head of priority-ordered swapfile list */ int next; /* swapfile to be used next */ @@ -158,7 +155,7 @@ static inline int is_page_shared(struct page *page) return 1; count = page_count(page); if (PageSwapCache(page)) - count += swap_count(page->offset) - 2; + count += swap_count(page) - 2; return count > 1; } diff --git a/include/linux/sysv_fs.h b/include/linux/sysv_fs.h index cf9cab749..5168218e0 100644 --- a/include/linux/sysv_fs.h +++ b/include/linux/sysv_fs.h @@ -384,7 +384,6 @@ extern int sysv_new_block(struct super_block * sb); extern void sysv_free_block(struct super_block * sb, unsigned int block); extern unsigned long sysv_count_free_blocks(struct super_block *sb); -extern struct buffer_head * sysv_getblk(struct inode *, unsigned int, int); extern int sysv_get_block(struct inode *, long, struct buffer_head *, int); extern struct buffer_head * sysv_file_bread(struct inode *, int, int); diff --git a/include/linux/tty.h b/include/linux/tty.h index 6cb629d08..69050c8a8 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -344,12 +344,13 @@ extern int fg_console, last_console, want_console; extern int kmsg_redirect; -extern unsigned long con_init(unsigned long); +extern void con_init(void); +extern void console_init(void); extern int rs_init(void); extern int lp_init(void); extern int pty_init(void); -extern int tty_init(void); +extern void tty_init(void); extern int ip2_init(void); extern int pcxe_init(void); extern int pc_init(void); @@ -398,7 +399,7 @@ extern int n_tty_ioctl(struct tty_struct * tty, struct file * file, /* serial.c */ -extern long serial_console_init(long kmem_start, long kmem_end); +extern void serial_console_init(void); /* pcxx.c */ diff --git a/include/net/ip.h b/include/net/ip.h index 4d4dbf7ce..0fb977fbd 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -137,6 +137,7 @@ extern struct linux_mib net_statistics; extern int sysctl_local_port_range[2]; +#ifdef CONFIG_INET extern __inline__ int ip_send(struct sk_buff *skb) { if (skb->len > skb->dst->pmtu) @@ -181,6 +182,7 @@ extern __inline__ void ip_eth_mc_map(u32 addr, char *buf) buf[3]=addr&0x7F; } +#endif extern int ip_call_ra_chain(struct sk_buff *skb); diff --git a/include/pcmcia/cs.h b/include/pcmcia/cs.h index 2325ad3b2..f8e6b9705 100644 --- a/include/pcmcia/cs.h +++ b/include/pcmcia/cs.h @@ -1,5 +1,5 @@ /* - * cs.h 1.67 1999/09/02 18:34:59 + * cs.h 1.68 1999/10/20 18:59:32 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in @@ -292,6 +292,7 @@ typedef struct win_req_t { #define WIN_SHARED 0x0040 #define WIN_FIRST_SHARED 0x0080 #define WIN_USE_WAIT 0x0100 +#define WIN_STRICT_ALIGN 0x0200 #define WIN_MAP_BELOW_1MB 0x0400 #define WIN_PREFETCH 0x0800 #define WIN_CACHEABLE 0x1000 @@ -439,30 +440,6 @@ extern int CardServices(int func, void *a1, void *a2, void *a3); extern int CardServices(int func, ...); #endif -#ifdef __BEOS__ -#define SS_MODULE_NAME(s) ("busses/pcmcia/" s "/v1") -#define MTD_MODULE_NAME(s) ("busses/pcmcia/" s "/v1") -#define CS_CLIENT_MODULE_NAME "bus_managers/pcmcia_cs/client/v1" -typedef struct cs_client_module_info { - bus_manager_info binfo; - int (*_CardServices)(int, ...); - int (*_MTDHelperEntry)(int, ...); - void (*_add_timer)(struct timer_list *); - void (*_del_timer)(struct timer_list *); -} cs_client_module_info; -#define CS_SOCKET_MODULE_NAME "bus_managers/pcmcia_cs/socket/v1" -typedef struct cs_socket_module_info { - bus_manager_info binfo; - int (*_register_ss_entry)(int, ss_entry_t); - void (*_unregister_ss_entry)(ss_entry_t); - void (*_add_timer)(struct timer_list *); - void (*_del_timer)(struct timer_list *); - int (*register_resource)(int, u_long, u_long); - int (*release_resource)(int, u_long, u_long); - int (*check_resource)(int, u_long, u_long); -} cs_socket_module_info; -#endif - #endif /* __KERNEL__ */ #endif /* _LINUX_CS_H */ diff --git a/include/pcmcia/cs_types.h b/include/pcmcia/cs_types.h index 3df641d41..5e2a688a0 100644 --- a/include/pcmcia/cs_types.h +++ b/include/pcmcia/cs_types.h @@ -30,9 +30,7 @@ #ifndef _LINUX_CS_TYPES_H #define _LINUX_CS_TYPES_H -#ifdef __linux__ #include <linux/types.h> -#endif typedef u_short socket_t; typedef u_short ioaddr_t; diff --git a/include/pcmcia/driver_ops.h b/include/pcmcia/driver_ops.h index 395374cc0..88bcaa9dd 100644 --- a/include/pcmcia/driver_ops.h +++ b/include/pcmcia/driver_ops.h @@ -68,15 +68,6 @@ typedef struct driver_operations { int register_driver(struct driver_operations *ops); void unregister_driver(struct driver_operations *ops); -#ifdef __BEOS__ -#define CB_ENABLER_MODULE_NAME "bus_managers/cb_enabler/v1" -typedef struct cb_enabler_module_info { - bus_manager_info binfo; - int (*register_driver)(struct driver_operations *ops); - void (*unregister_driver)(struct driver_operations *ops); -} cb_enabler_module_info; -#endif /* __BEOS__ */ - #endif /* __KERNEL__ */ #endif /* _LINUX_DRIVER_OPS_H */ diff --git a/include/pcmcia/ds.h b/include/pcmcia/ds.h index 06aa7f1cb..2663780b5 100644 --- a/include/pcmcia/ds.h +++ b/include/pcmcia/ds.h @@ -143,21 +143,6 @@ int unregister_pccard_driver(dev_info_t *dev_info); #define register_pcmcia_driver register_pccard_driver #define unregister_pcmcia_driver unregister_pccard_driver -#ifdef __BEOS__ -#define DS_MODULE_NAME "bus_managers/pcmcia_ds/v1" -typedef struct ds_module_info { - bus_manager_info binfo; - int (*_register_pccard_driver)(dev_info_t *, - dev_link_t *(*)(void), - void (*)(dev_link_t *)); - int (*_unregister_pccard_driver)(dev_info_t *); - struct driver_info_t **root_driver; - int *sockets; - struct socket_info_t **socket_table; - sem_id *list_sem; -} ds_module_info; -#endif /* __BEOS__ */ - #endif /* __KERNEL__ */ #endif /* _LINUX_DS_H */ diff --git a/include/pcmcia/version.h b/include/pcmcia/version.h index 0df81b31e..bb0b27e40 100644 --- a/include/pcmcia/version.h +++ b/include/pcmcia/version.h @@ -1,4 +1,4 @@ -/* version.h 1.72 1999/08/05 06:09:53 (David Hinds) */ +/* version.h 1.74 1999/09/29 20:41:44 (David Hinds) */ -#define CS_RELEASE "3.1.0" -#define CS_RELEASE_CODE 0x3100 +#define CS_RELEASE "3.1.2" +#define CS_RELEASE_CODE 0x3102 diff --git a/init/main.c b/init/main.c index f11652338..be06a1d0a 100644 --- a/init/main.c +++ b/init/main.c @@ -24,6 +24,7 @@ #include <linux/blk.h> #include <linux/hdreg.h> #include <linux/iobuf.h> +#include <linux/bootmem.h> #include <asm/io.h> #include <asm/bugs.h> @@ -56,6 +57,10 @@ extern void nubus_init(void); #include <linux/isapnp.h> #endif +#ifdef CONFIG_IRDA +#include <net/irda/irda_device.h> +#endif + /* * Versions of gcc older than that listed below may actually compile * and link okay, but the end product can have subtle run time bugs. @@ -75,7 +80,6 @@ static int init(void *); extern void init_IRQ(void); extern void init_modules(void); -extern long console_init(long, long); extern void init_inventory(void); extern void sock_init(void); extern void fork_init(unsigned long); @@ -115,9 +119,6 @@ extern void dquot_init_hash(void); extern void time_init(void); -static unsigned long memory_start = 0; -static unsigned long memory_end = 0; - int rows, cols; #ifdef CONFIG_BLK_DEV_INITRD @@ -428,7 +429,7 @@ static void __init parse_options(char *line) } -extern void setup_arch(char **, unsigned long *, unsigned long *); +extern void setup_arch(char **); extern void cpu_idle(void); #ifndef __SMP__ @@ -455,15 +456,15 @@ static void __init smp_init(void) asmlinkage void __init start_kernel(void) { char * command_line; - + unsigned long mempages; /* * Interrupts are still disabled. Do necessary setups, then * enable them */ lock_kernel(); printk(linux_banner); - setup_arch(&command_line, &memory_start, &memory_end); - memory_start = paging_init(memory_start,memory_end); + setup_arch(&command_line); + paging_init(); trap_init(); init_IRQ(); sched_init(); @@ -475,23 +476,26 @@ asmlinkage void __init start_kernel(void) * we've done PCI setups etc, and console_init() must be aware of * this. But we do want output early, in case something goes wrong. */ - memory_start = console_init(memory_start,memory_end); + console_init(); #ifdef CONFIG_MODULES init_modules(); #endif if (prof_shift) { - prof_buffer = (unsigned int *) memory_start; + unsigned int size; /* only text is profiled */ prof_len = (unsigned long) &_etext - (unsigned long) &_stext; prof_len >>= prof_shift; - memory_start += prof_len * sizeof(unsigned int); + + size = prof_len * sizeof(unsigned int) + PAGE_SIZE-1; + prof_buffer = (unsigned int *) alloc_bootmem(size); memset(prof_buffer, 0, prof_len * sizeof(unsigned int)); } - memory_start = kmem_cache_init(memory_start, memory_end); + kmem_cache_init(); sti(); calibrate_delay(); #ifdef CONFIG_BLK_DEV_INITRD + // FIXME, use the bootmem.h interface. if (initrd_start && !initrd_below_start_ok && initrd_start < memory_start) { printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - " "disabling it.\n",initrd_start,memory_start); @@ -501,17 +505,19 @@ asmlinkage void __init start_kernel(void) #ifdef CONFIG_BINFMT_IRIX init_inventory(); #endif - mem_init(memory_start,memory_end); + mem_init(); kmem_cache_sizes_init(); #ifdef CONFIG_PROC_FS proc_root_init(); #endif - fork_init(memory_end-memory_start); + mempages = num_physpages; + + fork_init(mempages); filescache_init(); dcache_init(); vma_init(); - buffer_init(memory_end-memory_start); - page_cache_init(memory_end-memory_start); + buffer_init(mempages); + page_cache_init(mempages); kiobuf_init(); signals_init(); inode_init(); @@ -1,13 +1,14 @@ /* * linux/ipc/shm.c * Copyright (C) 1992, 1993 Krishna Balasubramanian - * Many improvements/fixes by Bruno Haible. + * Many improvements/fixes by Bruno Haible. * Replaced `struct shm_desc' by `struct vm_area_struct', July 1994. * Fixed the shm swap deallocation (shm_unuse()), August 1998 Andrea Arcangeli. * * /proc/sysvipc/shm support (c) 1999 Dragos Acostachioaie <dragos@iname.com> * BIGMEM support, Andrea Arcangeli <andrea@suse.de> * SMP thread shm, Jean-Luc Boyard <jean-luc.boyard@siemens.fr> + * HIGHMEM support, Ingo Molnar <mingo@redhat.com> */ #include <linux/config.h> @@ -19,20 +20,19 @@ #include <linux/vmalloc.h> #include <linux/pagemap.h> #include <linux/proc_fs.h> -#include <linux/bigmem.h> +#include <linux/highmem.h> #include <asm/uaccess.h> #include <asm/pgtable.h> extern int ipcperms (struct ipc_perm *ipcp, short shmflg); -extern unsigned long get_swap_page (void); static int findkey (key_t key); static int newseg (key_t key, int shmflg, int size); static int shm_map (struct vm_area_struct *shmd); static void killseg (int id); static void shm_open (struct vm_area_struct *shmd); static void shm_close (struct vm_area_struct *shmd); -static unsigned long shm_nopage(struct vm_area_struct *, unsigned long, int); +static struct page * shm_nopage(struct vm_area_struct *, unsigned long, int); static int shm_swapout(struct vm_area_struct *, struct page *); #ifdef CONFIG_PROC_FS static int sysvipc_shm_read_proc(char *buffer, char **start, off_t offset, int length, int *eof, void *data); @@ -108,7 +108,7 @@ static int newseg (key_t key, int shmflg, int size) { struct shmid_kernel *shp; int numpages = (size + PAGE_SIZE -1) >> PAGE_SHIFT; - int id, i; + int id; if (size < SHMMIN) return -EINVAL; @@ -131,7 +131,7 @@ found: return -ENOMEM; } lock_kernel(); - shp->shm_pages = (ulong *) vmalloc (numpages*sizeof(ulong)); + shp->shm_pages = (pte_t *) vmalloc (numpages*sizeof(pte_t)); unlock_kernel(); if (!shp->shm_pages) { kfree(shp); @@ -141,7 +141,8 @@ found: return -ENOMEM; } - for (i = 0; i < numpages; shp->shm_pages[i++] = 0); + memset(shp->shm_pages, 0, numpages*sizeof(pte_t)); + shp->u.shm_perm.key = key; shp->u.shm_perm.mode = (shmflg & S_IRWXUGO); shp->u.shm_perm.cuid = shp->u.shm_perm.uid = current->euid; @@ -214,33 +215,29 @@ static void killseg (int id) int rss, swp; shp = shm_segs[id]; - if (shp == IPC_NOID || shp == IPC_UNUSED) { - printk ("shm nono: killseg called on unused seg id=%d\n", id); - return; - } + if (shp == IPC_NOID || shp == IPC_UNUSED) + BUG(); shp->u.shm_perm.seq++; /* for shmat */ shm_seq = (shm_seq+1) % ((unsigned)(1<<31)/SHMMNI); /* increment, but avoid overflow */ shm_segs[id] = (struct shmid_kernel *) IPC_UNUSED; used_segs--; if (id == max_shmid) while (max_shmid && (shm_segs[--max_shmid] == IPC_UNUSED)); - if (!shp->shm_pages) { - printk ("shm nono: killseg shp->pages=NULL. id=%d\n", id); - return; - } + if (!shp->shm_pages) + BUG(); spin_unlock(&shm_lock); numpages = shp->shm_npages; for (i = 0, rss = 0, swp = 0; i < numpages ; i++) { pte_t pte; - pte = __pte(shp->shm_pages[i]); + pte = shp->shm_pages[i]; if (pte_none(pte)) continue; if (pte_present(pte)) { - free_page (pte_page(pte)); + __free_page (pte_page(pte)); rss++; } else { lock_kernel(); - swap_free(pte_val(pte)); + swap_free(pte); unlock_kernel(); swp++; } @@ -462,8 +459,10 @@ static int shm_map (struct vm_area_struct *shmd) > (unsigned long) current->rlim[RLIMIT_AS].rlim_cur) return -ENOMEM; current->mm->total_vm += tmp >> PAGE_SHIFT; + vmlist_modify_lock(current->mm); insert_vm_struct(current->mm, shmd); merge_segments(current->mm, shmd->vm_start, shmd->vm_end); + vmlist_modify_unlock(current->mm); return 0; } @@ -482,16 +481,12 @@ asmlinkage long sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *raddr) down(¤t->mm->mmap_sem); spin_lock(&shm_lock); - if (shmid < 0) { - /* printk("shmat() -> EINVAL because shmid = %d < 0\n",shmid); */ + if (shmid < 0) goto out; - } shp = shm_segs[id = (unsigned int) shmid % SHMMNI]; - if (shp == IPC_UNUSED || shp == IPC_NOID) { - /* printk("shmat() -> EINVAL because shmid = %d is invalid\n",shmid); */ + if (shp == IPC_UNUSED || shp == IPC_NOID) goto out; - } if (!(addr = (ulong) shmaddr)) { if (shmflg & SHM_REMAP) @@ -524,16 +519,9 @@ asmlinkage long sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *raddr) */ if (addr < current->mm->start_stack && addr > current->mm->start_stack - PAGE_SIZE*(shp->shm_npages + 4)) - { - /* printk("shmat() -> EINVAL because segment intersects stack\n"); */ goto out; - } - if (!(shmflg & SHM_REMAP)) - if ((shmd = find_vma_intersection(current->mm, addr, addr + shp->u.shm_segsz))) { - /* printk("shmat() -> EINVAL because the interval [0x%lx,0x%lx) intersects an already mapped interval [0x%lx,0x%lx).\n", - addr, addr + shp->shm_segsz, shmd->vm_start, shmd->vm_end); */ - goto out; - } + if (!(shmflg & SHM_REMAP) && find_vma_intersection(current->mm, addr, addr + shp->u.shm_segsz)) + goto out; err = -EACCES; if (ipcperms(&shp->u.shm_perm, shmflg & SHM_RDONLY ? S_IRUGO : S_IRUGO|S_IWUGO)) @@ -566,7 +554,7 @@ asmlinkage long sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *raddr) shmd->vm_offset = 0; shmd->vm_ops = &shm_vm_ops; - shp->u.shm_nattch++; /* prevent destruction */ + shp->u.shm_nattch++; /* prevent destruction */ spin_unlock(&shm_lock); err = shm_map (shmd); spin_lock(&shm_lock); @@ -666,86 +654,76 @@ static int shm_swapout(struct vm_area_struct * vma, struct page * page) /* * page not present ... go through shm_pages */ -static unsigned long shm_nopage(struct vm_area_struct * shmd, unsigned long address, int no_share) +static struct page * shm_nopage(struct vm_area_struct * shmd, unsigned long address, int no_share) { pte_t pte; struct shmid_kernel *shp; unsigned int idx; - unsigned long page; - struct page * page_map; + struct page * page; shp = *(struct shmid_kernel **) shmd->vm_private_data; idx = (address - shmd->vm_start + shmd->vm_offset) >> PAGE_SHIFT; -#ifdef DEBUG_SHM - if (shp == IPC_UNUSED || shp == IPC_NOID) { - printk ("shm_nopage: id=%d invalid. Race.\n", id); - return 0; - } - if (idx >= shp->shm_npages) { - printk ("shm_nopage : too large page index. id=%d\n", id); - return 0; - } -#endif - spin_lock(&shm_lock); - again: - pte = __pte(shp->shm_pages[idx]); +again: + pte = shp->shm_pages[idx]; if (!pte_present(pte)) { if (pte_none(pte)) { spin_unlock(&shm_lock); - page = __get_free_page(GFP_BIGUSER); + page = get_free_highpage(GFP_HIGHUSER); if (!page) goto oom; - clear_bigpage(page); + clear_highpage(page); spin_lock(&shm_lock); - if (pte_val(pte) != shp->shm_pages[idx]) + if (pte_val(pte) != pte_val(shp->shm_pages[idx])) goto changed; } else { - unsigned long entry = pte_val(pte); + pte_t entry = pte; spin_unlock(&shm_lock); - page_map = lookup_swap_cache(entry); - if (!page_map) { + BUG(); + page = lookup_swap_cache(entry); + if (!page) { lock_kernel(); swapin_readahead(entry); - page_map = read_swap_cache(entry); + page = read_swap_cache(entry); unlock_kernel(); - if (!page_map) + if (!page) goto oom; } - delete_from_swap_cache(page_map); - page_map = replace_with_bigmem(page_map); - page = page_address(page_map); + delete_from_swap_cache(page); + page = replace_with_highmem(page); lock_kernel(); swap_free(entry); unlock_kernel(); spin_lock(&shm_lock); shm_swp--; - pte = __pte(shp->shm_pages[idx]); + pte = shp->shm_pages[idx]; if (pte_present(pte)) goto present; } shm_rss++; pte = pte_mkdirty(mk_pte(page, PAGE_SHARED)); - shp->shm_pages[idx] = pte_val(pte); + shp->shm_pages[idx] = pte; } else --current->maj_flt; /* was incremented in do_no_page */ -done: /* pte_val(pte) == shp->shm_pages[idx] */ - get_page(mem_map + MAP_NR(pte_page(pte))); +done: + /* pte_val(pte) == shp->shm_pages[idx] */ + get_page(pte_page(pte)); spin_unlock(&shm_lock); current->min_flt++; return pte_page(pte); changed: - free_page(page); + __free_page(page); goto again; present: - free_page(page); + if (page) + free_page_and_swap_cache(page); goto done; oom: - return -1; + return (struct page *)(-1); } /* @@ -758,7 +736,7 @@ int shm_swap (int prio, int gfp_mask) { pte_t page; struct shmid_kernel *shp; - unsigned long swap_nr; + pte_t swap_entry; unsigned long id, idx; int loop = 0; int counter; @@ -766,7 +744,7 @@ int shm_swap (int prio, int gfp_mask) counter = shm_rss >> prio; lock_kernel(); - if (!counter || !(swap_nr = get_swap_page())) { + if (!counter || !pte_val(swap_entry = get_swap_page())) { unlock_kernel(); return 0; } @@ -793,36 +771,37 @@ int shm_swap (int prio, int gfp_mask) if (idx >= shp->shm_npages) goto next_id; - page = __pte(shp->shm_pages[idx]); + page = shp->shm_pages[idx]; if (!pte_present(page)) goto check_table; - page_map = &mem_map[MAP_NR(pte_page(page))]; + page_map = pte_page(page); if ((gfp_mask & __GFP_DMA) && !PageDMA(page_map)) goto check_table; - if (!(gfp_mask & __GFP_BIGMEM) && PageBIGMEM(page_map)) + if (!(gfp_mask & __GFP_HIGHMEM) && PageHighMem(page_map)) goto check_table; swap_attempts++; if (--counter < 0) { /* failed */ - failed: +failed: spin_unlock(&shm_lock); lock_kernel(); - swap_free (swap_nr); + swap_free(swap_entry); unlock_kernel(); return 0; } - if (page_count(mem_map + MAP_NR(pte_page(page))) != 1) + if (page_count(page_map)) goto check_table; - if (!(page_map = prepare_bigmem_swapout(page_map))) + if (!(page_map = prepare_highmem_swapout(page_map))) goto check_table; - shp->shm_pages[idx] = swap_nr; + shp->shm_pages[idx] = swap_entry; swap_successes++; shm_swp++; shm_rss--; spin_unlock(&shm_lock); + lock_kernel(); - swap_duplicate(swap_nr); - add_to_swap_cache(page_map, swap_nr); + swap_duplicate(swap_entry); + add_to_swap_cache(page_map, swap_entry); rw_swap_page(WRITE, page_map, 0); unlock_kernel(); @@ -834,13 +813,13 @@ int shm_swap (int prio, int gfp_mask) * Free the swap entry and set the new pte for the shm page. */ static void shm_unuse_page(struct shmid_kernel *shp, unsigned long idx, - unsigned long page, unsigned long entry) + pte_t entry, struct page *page) { pte_t pte; pte = pte_mkdirty(mk_pte(page, PAGE_SHARED)); - shp->shm_pages[idx] = pte_val(pte); - get_page(mem_map + MAP_NR(page)); + shp->shm_pages[idx] = pte; + get_page(page); shm_rss++; shm_swp--; @@ -854,20 +833,21 @@ static void shm_unuse_page(struct shmid_kernel *shp, unsigned long idx, /* * unuse_shm() search for an eventually swapped out shm page. */ -void shm_unuse(unsigned long entry, unsigned long page) +void shm_unuse(pte_t entry, struct page *page) { int i, n; spin_lock(&shm_lock); - for (i = 0; i < SHMMNI; i++) - if (shm_segs[i] != IPC_UNUSED && shm_segs[i] != IPC_NOID) - for (n = 0; n < shm_segs[i]->shm_npages; n++) - if (shm_segs[i]->shm_pages[n] == entry) - { - shm_unuse_page(shm_segs[i], n, - page, entry); - return; - } + for (i = 0; i < SHMMNI; i++) { + struct shmid_kernel *seg = shm_segs[i]; + if ((seg == IPC_UNUSED) || (seg == IPC_NOID)) + continue; + for (n = 0; n < seg->shm_npages; n++) + if (pte_val(seg->shm_pages[n]) == pte_val(entry)) { + shm_unuse_page(seg, n, entry, page); + return; + } + } spin_unlock(&shm_lock); } diff --git a/kernel/exit.c b/kernel/exit.c index 39103a683..2ca64035c 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -135,7 +135,9 @@ static inline void forget_original_parent(struct task_struct * father) read_lock(&tasklist_lock); for_each_task(p) { if (p->p_opptr == father) { + /* We dont want people slaying init */ p->exit_signal = SIGCHLD; + p->self_exec_id++; p->p_opptr = child_reaper; /* init */ if (p->pdeath_signal) send_sig(p->pdeath_signal, p, 0); } @@ -288,7 +290,7 @@ void exit_mm(struct task_struct *tsk) */ static void exit_notify(void) { - struct task_struct * p; + struct task_struct * p, *t; forget_original_parent(current); /* @@ -300,15 +302,39 @@ static void exit_notify(void) * and we were the only connection outside, so our pgrp * is about to become orphaned. */ - if ((current->p_pptr->pgrp != current->pgrp) && - (current->p_pptr->session == current->session) && + + t = current->p_pptr; + + if ((t->pgrp != current->pgrp) && + (t->session == current->session) && will_become_orphaned_pgrp(current->pgrp, current) && has_stopped_jobs(current->pgrp)) { kill_pg(current->pgrp,SIGHUP,1); kill_pg(current->pgrp,SIGCONT,1); } - /* Let father know we died */ + /* Let father know we died + * + * Thread signals are configurable, but you aren't going to use + * that to send signals to arbitary processes. + * That stops right now. + * + * If the parent exec id doesn't match the exec id we saved + * when we started then we know the parent has changed security + * domain. + * + * If our self_exec id doesn't match our parent_exec_id then + * we have changed execution domain as these two values started + * the same after a fork. + * + */ + + if(current->exit_signal != SIGCHLD && + ( current->parent_exec_id != t->self_exec_id || + current->self_exec_id != current->parent_exec_id) + && !capable(CAP_KILL)) + current->exit_signal = SIGCHLD; + notify_parent(current, current->exit_signal); /* diff --git a/kernel/fork.c b/kernel/fork.c index e341615f2..9f3d9f507 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -19,8 +19,8 @@ #include <linux/vmalloc.h> #include <asm/pgtable.h> -#include <asm/mmu_context.h> #include <asm/uaccess.h> +#include <asm/mmu_context.h> /* The idle threads do not count.. */ int nr_threads=0; @@ -157,7 +157,7 @@ int alloc_uid(struct task_struct *p) return 0; } -void __init fork_init(unsigned long memsize) +void __init fork_init(unsigned long mempages) { int i; @@ -175,7 +175,7 @@ void __init fork_init(unsigned long memsize) * value: the thread structures can take up at most half * of memory. */ - max_threads = memsize / THREAD_SIZE / 2; + max_threads = mempages / (THREAD_SIZE/PAGE_SIZE) / 2; init_task.rlim[RLIMIT_NPROC].rlim_cur = max_threads/2; init_task.rlim[RLIMIT_NPROC].rlim_max = max_threads/2; @@ -693,6 +693,11 @@ int do_fork(unsigned long clone_flags, unsigned long usp, struct pt_regs *regs) if (retval) goto bad_fork_cleanup_sighand; p->semundo = NULL; + + /* Our parent execution domain becomes current domain + These must match for thread signalling to apply */ + + p->parent_exec_id = p->self_exec_id; /* ok, now we should be set up.. */ p->swappable = 1; diff --git a/kernel/ksyms.c b/kernel/ksyms.c index 910341fc6..d4480e717 100644 --- a/kernel/ksyms.c +++ b/kernel/ksyms.c @@ -36,6 +36,7 @@ #include <linux/swap.h> #include <linux/ctype.h> #include <linux/file.h> +#include <linux/iobuf.h> #include <linux/console.h> #include <linux/poll.h> #include <linux/mm.h> @@ -118,11 +119,13 @@ EXPORT_SYMBOL(in_group_p); EXPORT_SYMBOL(update_atime); EXPORT_SYMBOL(get_super); EXPORT_SYMBOL(get_fs_type); +EXPORT_SYMBOL(get_empty_super); +EXPORT_SYMBOL(remove_vfsmnt); EXPORT_SYMBOL(getname); EXPORT_SYMBOL(_fput); EXPORT_SYMBOL(igrab); EXPORT_SYMBOL(iunique); -EXPORT_SYMBOL(iget); +EXPORT_SYMBOL(iget4); EXPORT_SYMBOL(iput); EXPORT_SYMBOL(__namei); EXPORT_SYMBOL(lookup_dentry); @@ -140,6 +143,9 @@ EXPORT_SYMBOL(d_lookup); EXPORT_SYMBOL(d_path); EXPORT_SYMBOL(__mark_buffer_dirty); EXPORT_SYMBOL(__mark_inode_dirty); +EXPORT_SYMBOL(free_kiovec); +EXPORT_SYMBOL(brw_kiovec); +EXPORT_SYMBOL(alloc_kiovec); EXPORT_SYMBOL(get_empty_filp); EXPORT_SYMBOL(init_private_file); EXPORT_SYMBOL(filp_open); diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 35fa9768d..4945a0223 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -10,7 +10,7 @@ #include <linux/sched.h> #include <linux/errno.h> #include <linux/mm.h> -#include <linux/bigmem.h> +#include <linux/highmem.h> #include <asm/pgtable.h> #include <asm/uaccess.h> @@ -23,7 +23,9 @@ static int access_one_page(struct task_struct * tsk, struct vm_area_struct * vma pgd_t * pgdir; pmd_t * pgmiddle; pte_t * pgtable; - unsigned long page; + unsigned long mapnr; + unsigned long maddr; + struct page *page; repeat: pgdir = pgd_offset(vma->vm_mm, addr); @@ -39,27 +41,25 @@ repeat: pgtable = pte_offset(pgmiddle, addr); if (!pte_present(*pgtable)) goto fault_in_page; - page = pte_page(*pgtable); + mapnr = pte_pagenr(*pgtable); if (write && (!pte_write(*pgtable) || !pte_dirty(*pgtable))) goto fault_in_page; - if (MAP_NR(page) >= max_mapnr) + if (mapnr >= max_mapnr) return 0; + page = mem_map + mapnr; flush_cache_page(vma, addr); - { - void *src = (void *) (page + (addr & ~PAGE_MASK)); - void *dst = buf; - if (write) { - dst = src; - src = buf; - } - src = (void *) kmap((unsigned long) src, KM_READ); - dst = (void *) kmap((unsigned long) dst, KM_WRITE); - memcpy(dst, src, len); - kunmap((unsigned long) src, KM_READ); - kunmap((unsigned long) dst, KM_WRITE); + if (write) { + maddr = kmap(page, KM_WRITE); + memcpy((char *)maddr + (addr & ~PAGE_MASK), buf, len); + flush_page_to_ram(page); + kunmap(maddr, KM_WRITE); + } else { + maddr = kmap(page, KM_READ); + memcpy(buf, (char *)maddr + (addr & ~PAGE_MASK), len); + flush_page_to_ram(page); + kunmap(maddr, KM_READ); } - flush_page_to_ram(page); return len; fault_in_page: @@ -69,23 +69,25 @@ fault_in_page: return 0; bad_pgd: - printk("ptrace: bad pgd in '%s' at %08lx (%08lx)\n", tsk->comm, addr, pgd_val(*pgdir)); + pgd_ERROR(*pgdir); return 0; bad_pmd: - printk("ptrace: bad pmd in '%s' at %08lx (%08lx)\n", tsk->comm, addr, pmd_val(*pgmiddle)); + pmd_ERROR(*pgmiddle); return 0; } int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, int len, int write) { int copied; - struct vm_area_struct * vma = find_extend_vma(tsk, addr); - - if (!vma) - return 0; + struct vm_area_struct * vma; down(&tsk->mm->mmap_sem); + vma = find_extend_vma(tsk, addr); + if (!vma) { + up(&tsk->mm->mmap_sem); + return 0; + } copied = 0; for (;;) { unsigned long offset = addr & ~PAGE_MASK; diff --git a/kernel/time.c b/kernel/time.c index 1517d6d9d..3585e1cc8 100644 --- a/kernel/time.c +++ b/kernel/time.c @@ -54,6 +54,10 @@ void get_fast_time(struct timeval * t) do_get_fast_time(t); } +/* The xtime_lock is not only serializing the xtime read/writes but it's also + serializing all accesses to the global NTP variables now. */ +extern rwlock_t xtime_lock; + #if !defined(__alpha__) && !defined(__ia64__) /* @@ -93,14 +97,14 @@ asmlinkage long sys_stime(int * tptr) return -EPERM; if (get_user(value, tptr)) return -EFAULT; - cli(); + write_lock_irq(&xtime_lock); xtime.tv_sec = value; xtime.tv_usec = 0; time_adjust = 0; /* stop active adjtime() */ time_status |= STA_UNSYNC; time_maxerror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT; - sti(); + write_unlock_irq(&xtime_lock); return 0; } @@ -139,9 +143,9 @@ asmlinkage long sys_gettimeofday(struct timeval *tv, struct timezone *tz) */ inline static void warp_clock(void) { - cli(); + write_lock_irq(&xtime_lock); xtime.tv_sec += sys_tz.tz_minuteswest * 60; - sti(); + write_unlock_irq(&xtime_lock); } /* @@ -222,7 +226,7 @@ void (*hardpps_ptr)(struct timeval *) = (void (*)(struct timeval *))0; int do_adjtimex(struct timex *txc) { long ltemp, mtemp, save_adjust; - int result = time_state; /* mostly `TIME_OK' */ + int result; /* In order to modify anything, you gotta be super-user! */ if (txc->modes && !capable(CAP_SYS_TIME)) @@ -240,7 +244,8 @@ int do_adjtimex(struct timex *txc) if (txc->tick < 900000/HZ || txc->tick > 1100000/HZ) return -EINVAL; - cli(); /* SMP: global cli() is enough protection. */ + write_lock_irq(&xtime_lock); + result = time_state; /* mostly `TIME_OK' */ /* Save for later - semantics of adjtime is to return old value */ save_adjust = time_adjust; @@ -385,7 +390,6 @@ leave: if ((time_status & (STA_UNSYNC|STA_CLOCKERR)) != 0 txc->constant = time_constant; txc->precision = time_precision; txc->tolerance = time_tolerance; - do_gettimeofday(&txc->time); txc->tick = tick; txc->ppsfreq = pps_freq; txc->jitter = pps_jitter >> PPS_AVG; @@ -395,8 +399,8 @@ leave: if ((time_status & (STA_UNSYNC|STA_CLOCKERR)) != 0 txc->calcnt = pps_calcnt; txc->errcnt = pps_errcnt; txc->stbcnt = pps_stbcnt; - - sti(); + write_unlock_irq(&xtime_lock); + do_gettimeofday(&txc->time); return(result); } diff --git a/mm/Makefile b/mm/Makefile index 68404aa67..31c1a6231 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -9,11 +9,11 @@ O_TARGET := mm.o O_OBJS := memory.o mmap.o filemap.o mprotect.o mlock.o mremap.o \ - vmalloc.o slab.o \ - swap.o vmscan.o page_io.o page_alloc.o swap_state.o swapfile.o + vmalloc.o slab.o bootmem.o swap.o vmscan.o page_io.o \ + page_alloc.o swap_state.o swapfile.o -ifeq ($(CONFIG_BIGMEM),y) -O_OBJS += bigmem.o +ifeq ($(CONFIG_HIGHMEM),y) +O_OBJS += highmem.o endif include $(TOPDIR)/Rules.make diff --git a/mm/bigmem.c b/mm/bigmem.c deleted file mode 100644 index af63e860c..000000000 --- a/mm/bigmem.c +++ /dev/null @@ -1,71 +0,0 @@ -/* - * BIGMEM common code and variables. - * - * (C) 1999 Andrea Arcangeli, SuSE GmbH, andrea@suse.de - * Gerhard Wichert, Siemens AG, Gerhard.Wichert@pdb.siemens.de - */ - -#include <linux/mm.h> -#include <linux/pagemap.h> -#include <linux/bigmem.h> - -unsigned long bigmem_mapnr; -int nr_free_bigpages = 0; - -struct page * prepare_bigmem_swapout(struct page * page) -{ - /* if this is a bigmem page so it can't be swapped out directly - otherwise the b_data buffer addresses will break - the lowlevel device drivers. */ - if (PageBIGMEM(page)) { - unsigned long regular_page; - unsigned long vaddr; - - regular_page = __get_free_page(GFP_ATOMIC); - if (!regular_page) - return NULL; - - vaddr = kmap(page_address(page), KM_READ); - copy_page(regular_page, vaddr); - kunmap(vaddr, KM_READ); - - /* ok, we can just forget about our bigmem page since - we stored its data into the new regular_page. */ - __free_page(page); - - page = MAP_NR(regular_page) + mem_map; - } - return page; -} - -struct page * replace_with_bigmem(struct page * page) -{ - if (!PageBIGMEM(page) && nr_free_bigpages) { - unsigned long kaddr; - - kaddr = __get_free_page(GFP_ATOMIC|GFP_BIGMEM); - if (kaddr) { - struct page * bigmem_page; - - bigmem_page = MAP_NR(kaddr) + mem_map; - if (PageBIGMEM(bigmem_page)) { - unsigned long vaddr; - - vaddr = kmap(kaddr, KM_WRITE); - copy_page(vaddr, page_address(page)); - kunmap(vaddr, KM_WRITE); - - /* Preserve the caching of the swap_entry. */ - bigmem_page->offset = page->offset; - - /* We can just forget the old page since - we stored its data into the new - bigmem_page. */ - __free_page(page); - - page = bigmem_page; - } - } - } - return page; -} diff --git a/mm/bootmem.c b/mm/bootmem.c new file mode 100644 index 000000000..e790acc4f --- /dev/null +++ b/mm/bootmem.c @@ -0,0 +1,236 @@ +/* + * linux/mm/initmem.c + * + * Copyright (C) 1999 Ingo Molnar + * + * simple boot-time physical memory area allocator and + * free memory collector. It's used to deal with reserved + * system memory and memory holes as well. + */ + +#include <linux/config.h> +#include <linux/mm.h> +#include <linux/kernel_stat.h> +#include <linux/swap.h> +#include <linux/swapctl.h> +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/bootmem.h> +#include <asm/dma.h> + +/* + * Pointer to a bitmap - the bits represent all physical memory pages + * from physical address 0 to physical address end_mem. + * + * Access to this subsystem has to be serialized externally. (this is + * true for the boot process anyway) + */ +unsigned long max_low_pfn; + +static void * bootmem_map = NULL; + +/* + * Called once to set up the allocator itself. + */ +unsigned long __init init_bootmem (unsigned long start, unsigned long pages) +{ + unsigned long mapsize = (pages+7)/8; + + if (bootmem_map) + BUG(); + bootmem_map = __va(start << PAGE_SHIFT); + max_low_pfn = pages; + + /* + * Initially all pages are reserved - setup_arch() has to + * register free RAM areas explicitly. + */ + memset(bootmem_map, 0xff, mapsize); + + return mapsize; +} + +/* + * Marks a particular physical memory range as usable. Usable RAM + * might be used for boot-time allocations - or it might get added + * to the free page pool later on. + */ +void __init reserve_bootmem (unsigned long addr, unsigned long size) +{ + unsigned long i; + /* + * round up, partially reserved pages are considered + * fully reserved. + */ + unsigned long end = (addr + size + PAGE_SIZE-1)/PAGE_SIZE; + + if (!bootmem_map) BUG(); + if (!size) BUG(); + + if (end > max_low_pfn) + BUG(); + for (i = addr/PAGE_SIZE; i < end; i++) + if (test_and_set_bit(i, bootmem_map)) + BUG(); +} + +void __init free_bootmem (unsigned long addr, unsigned long size) +{ + unsigned long i; + /* + * round down end of usable mem, partially free pages are + * considered reserved. + */ + unsigned long end = (addr + size)/PAGE_SIZE; + + if (!bootmem_map) BUG(); + if (!size) BUG(); + + if (end > max_low_pfn) + BUG(); + for (i = addr/PAGE_SIZE; i < end; i++) { + if (!test_and_clear_bit(i, bootmem_map)) + BUG(); + } +} + +/* + * We 'merge' subsequent allocations to save space. We might 'lose' + * some fraction of a page if allocations cannot be satisfied due to + * size constraints on boxes where there is physical RAM space + * fragmentation - in these cases * (mostly large memory boxes) this + * is not a problem. + * + * On low memory boxes we get it right in 100% of the cases. + */ +static unsigned long last_pos = 0; +static unsigned long last_offset = 0; + +/* + * alignment has to be a power of 2 value. + */ +void * __init __alloc_bootmem (unsigned long size, unsigned long align, unsigned long goal) +{ + int area = 0; + unsigned long i, start = 0, reserved; + void *ret; + unsigned long offset, remaining_size; + unsigned long areasize, preferred; + + if (!bootmem_map) BUG(); + if (!size) BUG(); + + /* + * We try to allocate bootmem pages above 'goal' + * first, then we try to allocate lower pages. + */ + if (goal) { + preferred = goal >> PAGE_SHIFT; + if (preferred >= max_low_pfn) + preferred = 0; + } else + preferred = 0; + + areasize = (size+PAGE_SIZE-1)/PAGE_SIZE; + +restart_scan: + for (i = preferred; i < max_low_pfn; i++) { + reserved = test_bit(i, bootmem_map); + if (!reserved) { + if (!area) { + area = 1; + start = i; + } + if (i - start + 1 == areasize) + goto found; + } else { + area = 0; + start = -1; + } + } + if (preferred) { + preferred = 0; + goto restart_scan; + } + BUG(); +found: + if (start >= max_low_pfn) + BUG(); + + /* + * Is the next page of the previous allocation-end the start + * of this allocation's buffer? If yes then we can 'merge' + * the previous partial page with this allocation. + */ + if (last_offset && (last_pos+1 == start)) { + offset = (last_offset+align-1) & ~(align-1); + if (offset > PAGE_SIZE) + BUG(); + remaining_size = PAGE_SIZE-offset; + if (remaining_size > PAGE_SIZE) + BUG(); + if (size < remaining_size) { + areasize = 0; + // last_pos unchanged + last_offset = offset+size; + ret = __va(last_pos*PAGE_SIZE + offset); + } else { + size -= remaining_size; + areasize = (size+PAGE_SIZE-1)/PAGE_SIZE; + ret = __va(last_pos*PAGE_SIZE + offset); + last_pos = start+areasize-1; + last_offset = size; + } + last_offset &= ~PAGE_MASK; + } else { + last_pos = start + areasize - 1; + last_offset = size & ~PAGE_MASK; + ret = __va(start * PAGE_SIZE); + } + /* + * Reserve the area now: + */ + for (i = start; i < start+areasize; i++) + if (test_and_set_bit(i, bootmem_map)) + BUG(); + + return ret; +} + +unsigned long __init free_all_bootmem (void) +{ + struct page * page; + unsigned long i, count, total = 0; + + if (!bootmem_map) BUG(); + + page = mem_map; + count = 0; + for (i = 0; i < max_low_pfn; i++, page++) { + if (!test_bit(i, bootmem_map)) { + count++; + ClearPageReserved(page); + set_page_count(page, 1); + if (i >= (__pa(MAX_DMA_ADDRESS) >> PAGE_SHIFT)) + clear_bit(PG_DMA, &page->flags); + __free_page(page); + } + } + total += count; + /* + * Now free the allocator bitmap itself, it's not + * needed anymore: + */ + page = mem_map + MAP_NR(bootmem_map); + count = 0; + for (i = 0; i < (max_low_pfn/8 + PAGE_SIZE-1)/PAGE_SIZE; i++,page++) { + count++; + ClearPageReserved(page); + set_page_count(page, 1); + __free_page(page); + } + total += count; + bootmem_map = NULL; + + return total; +} diff --git a/mm/filemap.c b/mm/filemap.c index 51624abcf..7451f46d9 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -21,6 +21,7 @@ #include <linux/swapctl.h> #include <linux/slab.h> #include <linux/init.h> +#include <linux/highmem.h> #include <asm/pgtable.h> #include <asm/uaccess.h> @@ -75,24 +76,6 @@ static void remove_page_from_hash_queue(struct page * page) atomic_dec(&page_cache_size); } -static void remove_page_from_inode_queue(struct page * page) -{ - struct inode * inode = page->inode; - struct page *prev, *next; - - inode->i_nrpages--; - next = page->next; - prev = page->prev; - if (inode->i_pages == page) - inode->i_pages = next; - if (next) - next->prev = prev; - if (prev) - prev->next = next; - page->next = NULL; - page->prev = NULL; -} - /* * Remove a page from the page cache and free it. Caller has to make * sure the page is locked and that nobody else uses it - or that usage @@ -112,49 +95,53 @@ void remove_inode_page(struct page *page) void invalidate_inode_pages(struct inode * inode) { - struct page ** p; + struct list_head *head, *curr; struct page * page; + head = &inode->i_pages; repeat: spin_lock(&pagecache_lock); - p = &inode->i_pages; - while ((page = *p) != NULL) { - get_page(page); - if (TryLockPage(page)) { - spin_unlock(&pagecache_lock); - wait_on_page(page); - page_cache_release(page); - goto repeat; - } - if (page_count(page) != 2) - printk("hm, busy page invalidated? (not necessarily a bug)\n"); + curr = head->next; + + while (curr != head) { + page = list_entry(curr, struct page, list); + curr = curr->next; + + /* We cannot invalidate a locked page */ + if (PageLocked(page)) + continue; + lru_cache_del(page); remove_page_from_inode_queue(page); remove_page_from_hash_queue(page); page->inode = NULL; - UnlockPage(page); - page_cache_release(page); page_cache_release(page); - } spin_unlock(&pagecache_lock); } + /* * Truncate the page cache at a set offset, removing the pages * that are beyond that offset (and zeroing out partial pages). */ void truncate_inode_pages(struct inode * inode, unsigned long start) { - struct page ** p; + struct list_head *head, *curr; + unsigned long offset; struct page * page; int partial = 0; repeat: + head = &inode->i_pages; spin_lock(&pagecache_lock); - p = &inode->i_pages; - while ((page = *p) != NULL) { - unsigned long offset = page->offset; + curr = head->next; + while (curr != head) { + + page = list_entry(curr, struct page, list); + curr = curr->next; + + offset = page->offset; /* page wholly truncated - free it */ if (offset >= start) { @@ -190,7 +177,6 @@ repeat: */ goto repeat; } - p = &page->next; /* * there is only one partial page possible. */ @@ -200,17 +186,14 @@ repeat: offset = start - offset; /* partial truncate, clear end of page */ if (offset < PAGE_CACHE_SIZE) { - unsigned long address; get_page(page); spin_unlock(&pagecache_lock); lock_page(page); partial = 1; - address = page_address(page); - memset((void *) (offset + address), 0, PAGE_CACHE_SIZE - offset); - flush_page_to_ram(address); - + memclear_highpage_flush(page, offset, + PAGE_CACHE_SIZE-offset); if (inode->i_op->flushpage) inode->i_op->flushpage(inode, page, offset); /* @@ -255,7 +238,7 @@ int shrink_mmap(int priority, int gfp_mask) /* don't account passes over not DMA pages */ if ((gfp_mask & __GFP_DMA) && !PageDMA(page)) goto dispose_continue; - if (!(gfp_mask & __GFP_BIGMEM) && PageBIGMEM(page)) + if (!(gfp_mask & __GFP_HIGHMEM) && PageHighMem(page)) goto dispose_continue; count--; @@ -291,7 +274,7 @@ int shrink_mmap(int priority, int gfp_mask) goto unlock_continue; /* page was locked, inode can't go away under us */ if (!page->inode) { - atomic_sub(PAGE_CACHE_SIZE, &buffermem); + atomic_dec(&buffermem_pages); goto made_buffer_progress; } spin_lock(&pagecache_lock); @@ -431,16 +414,18 @@ static int waitfor_one_page(struct page *page) static int do_buffer_fdatasync(struct inode *inode, unsigned long start, unsigned long end, int (*fn)(struct page *)) { - struct page *next; + struct list_head *head, *curr; + struct page *page; int retval = 0; + head = &inode->i_pages; start &= PAGE_MASK; spin_lock(&pagecache_lock); - next = inode->i_pages; - while (next) { - struct page *page = next; - next = page->next; + curr = head->next; + while (curr != head) { + page = list_entry(curr, struct page, list); + curr = curr->next; if (!page->buffers) continue; if (page->offset >= end) @@ -458,7 +443,7 @@ static int do_buffer_fdatasync(struct inode *inode, unsigned long start, unsigne UnlockPage(page); spin_lock(&pagecache_lock); - next = page->next; + curr = page->list.next; page_cache_release(page); } spin_unlock(&pagecache_lock); @@ -487,6 +472,7 @@ static inline void __add_to_page_cache(struct page * page, struct inode * inode, unsigned long offset, struct page **hash) { + struct page *alias; unsigned long flags; flags = page->flags & ~((1 << PG_uptodate) | (1 << PG_error) | (1 << PG_referenced)); @@ -497,6 +483,9 @@ static inline void __add_to_page_cache(struct page * page, add_page_to_inode_queue(inode, page); __add_page_to_hash_queue(page, hash); lru_cache_add(page); + alias = __find_page_nolock(inode, offset, *hash); + if (alias != page) + BUG(); } void add_to_page_cache(struct page * page, struct inode * inode, unsigned long offset) @@ -532,10 +521,9 @@ int add_to_page_cache_unique(struct page * page, */ static inline void page_cache_read(struct file * file, unsigned long offset) { - unsigned long new_page; struct inode *inode = file->f_dentry->d_inode; - struct page ** hash = page_hash(inode, offset); - struct page * page; + struct page **hash = page_hash(inode, offset); + struct page *page; spin_lock(&pagecache_lock); page = __find_page_nolock(inode, offset, *hash); @@ -543,22 +531,20 @@ static inline void page_cache_read(struct file * file, unsigned long offset) if (page) return; - new_page = page_cache_alloc(); - if (!new_page) + page = page_cache_alloc(); + if (!page) return; - page = page_cache_entry(new_page); if (!add_to_page_cache_unique(page, inode, offset, hash)) { inode->i_op->readpage(file, page); page_cache_release(page); return; } - /* * We arrive here in the unlikely event that someone * raced with us and added our page to the cache first. */ - page_cache_free(new_page); + page_cache_free(page); return; } @@ -962,13 +948,13 @@ void do_generic_file_read(struct file * filp, loff_t *ppos, read_descriptor_t * { struct dentry *dentry = filp->f_dentry; struct inode *inode = dentry->d_inode; - size_t pos, pgpos, page_cache; + size_t pos, pgpos; + struct page *cached_page; int reada_ok; int error; int max_readahead = get_max_readahead(inode); - page_cache = 0; - + cached_page = NULL; pos = *ppos; pgpos = pos & PAGE_CACHE_MASK; /* @@ -1051,7 +1037,7 @@ page_ok: * "pos" here (the actor routine has to update the user buffer * pointers and the remaining count). */ - nr = actor(desc, (const char *) (page_address(page) + offset), nr); + nr = actor(desc, page, offset, nr); pos += nr; page_cache_release(page); if (nr && desc->count) @@ -1105,10 +1091,10 @@ no_cached_page: * * We get here with the page cache lock held. */ - if (!page_cache) { + if (!cached_page) { spin_unlock(&pagecache_lock); - page_cache = page_cache_alloc(); - if (!page_cache) { + cached_page = page_cache_alloc(); + if (!cached_page) { desc->error = -ENOMEM; break; } @@ -1126,29 +1112,35 @@ no_cached_page: /* * Ok, add the new page to the hash-queues... */ - page = page_cache_entry(page_cache); + page = cached_page; __add_to_page_cache(page, inode, pos & PAGE_CACHE_MASK, hash); spin_unlock(&pagecache_lock); + cached_page = NULL; - page_cache = 0; goto readpage; } *ppos = pos; filp->f_reada = 1; - if (page_cache) - page_cache_free(page_cache); + if (cached_page) + page_cache_free(cached_page); UPDATE_ATIME(inode); } -static int file_read_actor(read_descriptor_t * desc, const char *area, unsigned long size) +static int file_read_actor(read_descriptor_t * desc, struct page *page, unsigned long offset, unsigned long size) { - unsigned long left; - unsigned long count = desc->count; + unsigned long kaddr; + unsigned long left, count = desc->count; if (size > count) size = count; - left = __copy_to_user(desc->buf, area, size); + /* + * FIXME: We cannot yet sleep with kmaps held. + */ + kaddr = kmap(page, KM_READ); + left = __copy_to_user(desc->buf, (void *)(kaddr+offset), size); + kunmap(kaddr, KM_READ); + if (left) { size -= left; desc->error = -EFAULT; @@ -1187,8 +1179,9 @@ ssize_t generic_file_read(struct file * filp, char * buf, size_t count, loff_t * return retval; } -static int file_send_actor(read_descriptor_t * desc, const char *area, unsigned long size) +static int file_send_actor(read_descriptor_t * desc, struct page *page, unsigned long offset , unsigned long size) { + unsigned long kaddr; ssize_t written; unsigned long count = desc->count; struct file *file = (struct file *) desc->buf; @@ -1198,7 +1191,9 @@ static int file_send_actor(read_descriptor_t * desc, const char *area, unsigned size = count; old_fs = get_fs(); set_fs(KERNEL_DS); - written = file->f_op->write(file, area, size, &file->f_pos); + kaddr = kmap(page, KM_READ); + written = file->f_op->write(file, (char *)kaddr + offset, size, &file->f_pos); + kunmap(kaddr, KM_READ); set_fs(old_fs); if (written < 0) { desc->error = written; @@ -1298,14 +1293,13 @@ out: * XXX - at some point, this should return unique values to indicate to * the caller whether this is EIO, OOM, or SIGBUS. */ -static unsigned long filemap_nopage(struct vm_area_struct * area, +static struct page * filemap_nopage(struct vm_area_struct * area, unsigned long address, int no_share) { - struct file * file = area->vm_file; - struct dentry * dentry = file->f_dentry; - struct inode * inode = dentry->d_inode; - struct page * page, **hash; - unsigned long old_page; + struct file *file = area->vm_file; + struct dentry *dentry = file->f_dentry; + struct inode *inode = dentry->d_inode; + struct page *page, **hash, *old_page; unsigned long offset = address - area->vm_start + area->vm_offset; @@ -1317,7 +1311,7 @@ static unsigned long filemap_nopage(struct vm_area_struct * area, */ if ((offset >= inode->i_size) && (area->vm_flags & VM_SHARED) && (area->vm_mm == current->mm)) - return 0; + return NULL; /* * Do we have something in the page cache already? @@ -1340,12 +1334,14 @@ success: * Found the page and have a reference on it, need to check sharing * and possibly copy it over to another page.. */ - old_page = page_address(page); + old_page = page; if (no_share) { - unsigned long new_page = page_cache_alloc(); + struct page *new_page = page_cache_alloc(); if (new_page) { - copy_page(new_page, old_page); + if (PageHighMem(new_page) || PageHighMem(old_page)) + BUG(); + copy_highpage(new_page, old_page); flush_page_to_ram(new_page); } page_cache_release(page); @@ -1411,7 +1407,7 @@ page_not_uptodate: * mm layer so, possibly freeing the page cache page first. */ page_cache_release(page); - return 0; + return NULL; } /* @@ -1419,12 +1415,11 @@ page_not_uptodate: * if the disk is full. */ static inline int do_write_page(struct inode * inode, struct file * file, - const char * page_addr, unsigned long offset) + struct page * page, unsigned long offset) { int retval; unsigned long size; int (*writepage) (struct file *, struct page *); - struct page * page; size = offset + PAGE_SIZE; /* refuse to extend file size.. */ @@ -1438,7 +1433,6 @@ static inline int do_write_page(struct inode * inode, struct file * file, size -= offset; retval = -EIO; writepage = inode->i_op->writepage; - page = mem_map + MAP_NR(page_addr); lock_page(page); retval = writepage(file, page); @@ -1449,7 +1443,7 @@ static inline int do_write_page(struct inode * inode, struct file * file, static int filemap_write_page(struct vm_area_struct * vma, unsigned long offset, - unsigned long page, + struct page * page, int wait) { int result; @@ -1466,7 +1460,7 @@ static int filemap_write_page(struct vm_area_struct * vma, * and file could be released ... increment the count to be safe. */ get_file(file); - result = do_write_page(inode, file, (const char *) page, offset); + result = do_write_page(inode, file, page, offset); fput(file); return result; } @@ -1480,7 +1474,7 @@ static int filemap_write_page(struct vm_area_struct * vma, extern void wakeup_bdflush(int); int filemap_swapout(struct vm_area_struct * vma, struct page * page) { - int retval = filemap_write_page(vma, page->offset, page_address(page), 0); + int retval = filemap_write_page(vma, page->offset, page, 0); wakeup_bdflush(0); return retval; } @@ -1489,7 +1483,6 @@ static inline int filemap_sync_pte(pte_t * ptep, struct vm_area_struct *vma, unsigned long address, unsigned int flags) { pte_t pte = *ptep; - unsigned long pageaddr; struct page *page; int error; @@ -1502,8 +1495,7 @@ static inline int filemap_sync_pte(pte_t * ptep, struct vm_area_struct *vma, flush_cache_page(vma, address); set_pte(ptep, pte_mkclean(pte)); flush_tlb_page(vma, address); - pageaddr = pte_page(pte); - page = page_cache_entry(pageaddr); + page = pte_page(pte); get_page(page); } else { if (pte_none(pte)) @@ -1512,17 +1504,19 @@ static inline int filemap_sync_pte(pte_t * ptep, struct vm_area_struct *vma, pte_clear(ptep); flush_tlb_page(vma, address); if (!pte_present(pte)) { - swap_free(pte_val(pte)); + swap_free(pte); return 0; } - pageaddr = pte_page(pte); + page = pte_page(pte); if (!pte_dirty(pte) || flags == MS_INVALIDATE) { - page_cache_free(pageaddr); + page_cache_free(page); return 0; } } - error = filemap_write_page(vma, address - vma->vm_start + vma->vm_offset, pageaddr, 1); - page_cache_free(pageaddr); + if (PageHighMem(page)) + BUG(); + error = filemap_write_page(vma, address - vma->vm_start + vma->vm_offset, page, 1); + page_cache_free(page); return error; } @@ -1537,7 +1531,7 @@ static inline int filemap_sync_pte_range(pmd_t * pmd, if (pmd_none(*pmd)) return 0; if (pmd_bad(*pmd)) { - printk("filemap_sync_pte_range: bad pmd (%08lx)\n", pmd_val(*pmd)); + pmd_ERROR(*pmd); pmd_clear(pmd); return 0; } @@ -1552,7 +1546,7 @@ static inline int filemap_sync_pte_range(pmd_t * pmd, error |= filemap_sync_pte(pte, vma, address + offset, flags); address += PAGE_SIZE; pte++; - } while (address < end); + } while (address && (address < end)); return error; } @@ -1567,7 +1561,7 @@ static inline int filemap_sync_pmd_range(pgd_t * pgd, if (pgd_none(*pgd)) return 0; if (pgd_bad(*pgd)) { - printk("filemap_sync_pmd_range: bad pgd (%08lx)\n", pgd_val(*pgd)); + pgd_ERROR(*pgd); pgd_clear(pgd); return 0; } @@ -1582,7 +1576,7 @@ static inline int filemap_sync_pmd_range(pgd_t * pgd, error |= filemap_sync_pte_range(pmd, address, end - address, vma, offset, flags); address = (address + PMD_SIZE) & PMD_MASK; pmd++; - } while (address < end); + } while (address && (address < end)); return error; } @@ -1595,11 +1589,13 @@ static int filemap_sync(struct vm_area_struct * vma, unsigned long address, dir = pgd_offset(vma->vm_mm, address); flush_cache_range(vma->vm_mm, end - size, end); - while (address < end) { + if (address >= end) + BUG(); + do { error |= filemap_sync_pmd_range(dir, address, end - address, vma, flags); address = (address + PGDIR_SIZE) & PGDIR_MASK; dir++; - } + } while (address && (address < end)); flush_tlb_range(vma->vm_mm, end - size, end); return error; } @@ -1775,12 +1771,13 @@ generic_file_write(struct file *file, const char *buf, struct inode *inode = dentry->d_inode; unsigned long pos = *ppos; unsigned long limit = current->rlim[RLIMIT_FSIZE].rlim_cur; - struct page *page, **hash; - unsigned long page_cache = 0; + struct page *page, **hash, *cached_page; unsigned long written; long status; int err; + cached_page = NULL; + down(&inode->i_sem); err = file->f_error; if (err) { @@ -1828,18 +1825,18 @@ generic_file_write(struct file *file, const char *buf, repeat_find: page = __find_lock_page(inode, pgpos, hash); if (!page) { - if (!page_cache) { - page_cache = page_cache_alloc(); - if (page_cache) + if (!cached_page) { + cached_page = page_cache_alloc(); + if (cached_page) goto repeat_find; status = -ENOMEM; break; } - page = page_cache_entry(page_cache); + page = cached_page; if (add_to_page_cache_unique(page,inode,pgpos,hash)) goto repeat_find; - page_cache = 0; + cached_page = NULL; } /* We have exclusive IO access to the page.. */ @@ -1870,8 +1867,8 @@ repeat_find: } *ppos = pos; - if (page_cache) - page_cache_free(page_cache); + if (cached_page) + page_cache_free(cached_page); err = written ? written : status; out: @@ -1897,11 +1894,11 @@ void put_cached_page(unsigned long addr) page_cache_release(page); } -void __init page_cache_init(unsigned long memory_size) +void __init page_cache_init(unsigned long mempages) { unsigned long htable_size, order; - htable_size = memory_size >> PAGE_SHIFT; + htable_size = mempages; htable_size *= sizeof(struct page *); for(order = 0; (PAGE_SIZE << order) < htable_size; order++) ; @@ -1921,5 +1918,5 @@ void __init page_cache_init(unsigned long memory_size) (1 << page_hash_bits), order, (PAGE_SIZE << order)); if (!page_hash_table) panic("Failed to allocate page hash table\n"); - memset(page_hash_table, 0, PAGE_HASH_SIZE * sizeof(struct page *)); + memset((void *)page_hash_table, 0, PAGE_HASH_SIZE * sizeof(struct page *)); } diff --git a/mm/highmem.c b/mm/highmem.c new file mode 100644 index 000000000..7665393cf --- /dev/null +++ b/mm/highmem.c @@ -0,0 +1,81 @@ +/* + * High memory handling common code and variables. + * + * (C) 1999 Andrea Arcangeli, SuSE GmbH, andrea@suse.de + * Gerhard Wichert, Siemens AG, Gerhard.Wichert@pdb.siemens.de + * + * Redesigned the x86 32-bit VM architecture to deal with + * 64-bit physical space. With current x86 CPUs this + * means up to 64 Gigabytes physical RAM. + * + * Copyright (C) 1999 Ingo Molnar <mingo@redhat.com> + */ + +#include <linux/mm.h> +#include <linux/pagemap.h> +#include <linux/highmem.h> + +unsigned long highmem_mapnr; +unsigned long nr_free_highpages = 0; + +struct page * prepare_highmem_swapout(struct page * page) +{ + unsigned long regular_page; + unsigned long vaddr; + /* + * If this is a highmem page so it can't be swapped out directly + * otherwise the b_data buffer addresses will break + * the lowlevel device drivers. + */ + if (!PageHighMem(page)) + return page; + + regular_page = __get_free_page(GFP_ATOMIC); + if (!regular_page) + return NULL; + + vaddr = kmap(page, KM_READ); + copy_page((void *)regular_page, (void *)vaddr); + kunmap(vaddr, KM_READ); + + /* + * ok, we can just forget about our highmem page since + * we stored its data into the new regular_page. + */ + __free_page(page); + + return mem_map + MAP_NR(regular_page); +} + +struct page * replace_with_highmem(struct page * page) +{ + struct page *highpage; + unsigned long vaddr; + + if (PageHighMem(page) || !nr_free_highpages) + return page; + + highpage = get_free_highpage(GFP_ATOMIC|__GFP_HIGHMEM); + if (!highpage) + return page; + if (!PageHighMem(highpage)) { + __free_page(highpage); + return page; + } + + vaddr = kmap(highpage, KM_WRITE); + copy_page((void *)vaddr, (void *)page_address(page)); + kunmap(vaddr, KM_WRITE); + + /* Preserve the caching of the swap_entry. */ + highpage->offset = page->offset; + highpage->inode = page->inode; + + /* + * We can just forget the old page since + * we stored its data into the new highmem-page. + */ + __free_page(page); + + return highpage; +} diff --git a/mm/memory.c b/mm/memory.c index 5498dbcf0..87611db8c 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -43,7 +43,7 @@ #include <linux/smp_lock.h> #include <linux/swapctl.h> #include <linux/iobuf.h> -#include <linux/bigmem.h> +#include <linux/highmem.h> #include <asm/uaccess.h> #include <asm/pgtable.h> @@ -51,19 +51,20 @@ unsigned long max_mapnr = 0; unsigned long num_physpages = 0; void * high_memory = NULL; +struct page *highmem_start_page; /* * We special-case the C-O-W ZERO_PAGE, because it's such * a common occurrence (no need to read the page to know * that it's zero - better for the cache and memory subsystem). */ -static inline void copy_cow_page(unsigned long from, unsigned long to) +static inline void copy_cow_page(struct page * from, struct page * to, unsigned long address) { - if (from == ZERO_PAGE(to)) { - clear_bigpage(to); + if (from == ZERO_PAGE(address)) { + clear_highpage(to); return; } - copy_bigpage(to, from); + copy_highpage(to, from); } mem_map_t * mem_map = NULL; @@ -89,7 +90,7 @@ static inline void free_one_pmd(pmd_t * dir) if (pmd_none(*dir)) return; if (pmd_bad(*dir)) { - printk("free_one_pmd: bad directory entry %08lx\n", pmd_val(*dir)); + pmd_ERROR(*dir); pmd_clear(dir); return; } @@ -106,7 +107,7 @@ static inline void free_one_pgd(pgd_t * dir) if (pgd_none(*dir)) return; if (pgd_bad(*dir)) { - printk("free_one_pgd: bad directory entry %08lx\n", pgd_val(*dir)); + pgd_ERROR(*dir); pgd_clear(dir); return; } @@ -179,11 +180,10 @@ int copy_page_range(struct mm_struct *dst, struct mm_struct *src, if (pgd_none(*src_pgd)) goto skip_copy_pmd_range; if (pgd_bad(*src_pgd)) { - printk("copy_pmd_range: bad pgd (%08lx)\n", - pgd_val(*src_pgd)); + pgd_ERROR(*src_pgd); pgd_clear(src_pgd); skip_copy_pmd_range: address = (address + PGDIR_SIZE) & PGDIR_MASK; - if (address >= end) + if (!address || (address >= end)) goto out; continue; } @@ -203,7 +203,7 @@ skip_copy_pmd_range: address = (address + PGDIR_SIZE) & PGDIR_MASK; if (pmd_none(*src_pmd)) goto skip_copy_pte_range; if (pmd_bad(*src_pmd)) { - printk("copy_pte_range: bad pmd (%08lx)\n", pmd_val(*src_pmd)); + pmd_ERROR(*src_pmd); pmd_clear(src_pmd); skip_copy_pte_range: address = (address + PMD_SIZE) & PMD_MASK; if (address >= end) @@ -227,11 +227,11 @@ skip_copy_pte_range: address = (address + PMD_SIZE) & PMD_MASK; if (pte_none(pte)) goto cont_copy_pte_range; if (!pte_present(pte)) { - swap_duplicate(pte_val(pte)); + swap_duplicate(pte); set_pte(dst_pte, pte); goto cont_copy_pte_range; } - page_nr = MAP_NR(pte_page(pte)); + page_nr = pte_pagenr(pte); if (page_nr >= max_mapnr || PageReserved(mem_map+page_nr)) { set_pte(dst_pte, pte); @@ -272,17 +272,17 @@ nomem: static inline int free_pte(pte_t page) { if (pte_present(page)) { - unsigned long addr = pte_page(page); - if (MAP_NR(addr) >= max_mapnr || PageReserved(mem_map+MAP_NR(addr))) + unsigned long nr = pte_pagenr(page); + if (nr >= max_mapnr || PageReserved(mem_map+nr)) return 0; /* * free_page() used to be able to clear swap cache * entries. We may now have to do it manually. */ - free_page_and_swap_cache(addr); + free_page_and_swap_cache(mem_map+nr); return 1; } - swap_free(pte_val(page)); + swap_free(page); return 0; } @@ -302,7 +302,7 @@ static inline int zap_pte_range(struct mm_struct *mm, pmd_t * pmd, unsigned long if (pmd_none(*pmd)) return 0; if (pmd_bad(*pmd)) { - printk("zap_pte_range: bad pmd (%08lx)\n", pmd_val(*pmd)); + pmd_ERROR(*pmd); pmd_clear(pmd); return 0; } @@ -336,7 +336,7 @@ static inline int zap_pmd_range(struct mm_struct *mm, pgd_t * dir, unsigned long if (pgd_none(*dir)) return 0; if (pgd_bad(*dir)) { - printk("zap_pmd_range: bad pgd (%08lx)\n", pgd_val(*dir)); + pgd_ERROR(*dir); pgd_clear(dir); return 0; } @@ -372,12 +372,14 @@ void zap_page_range(struct mm_struct *mm, unsigned long address, unsigned long s * even if kswapd happened to be looking at this * process we _want_ it to get stuck. */ + if (address >= end) + BUG(); spin_lock(&mm->page_table_lock); - while (address < end) { + do { freed += zap_pmd_range(mm, dir, address, end - address); address = (address + PGDIR_SIZE) & PGDIR_MASK; dir++; - } + } while (address && (address < end)); spin_unlock(&mm->page_table_lock); /* * Update rss for the mm_struct (not necessarily current->mm) @@ -393,7 +395,7 @@ void zap_page_range(struct mm_struct *mm, unsigned long address, unsigned long s /* * Do a quick page-table lookup for a single page. */ -static unsigned long follow_page(unsigned long address) +static struct page * follow_page(unsigned long address) { pgd_t *pgd; pmd_t *pmd; @@ -402,31 +404,27 @@ static unsigned long follow_page(unsigned long address) pmd = pmd_offset(pgd, address); if (pmd) { pte_t * pte = pte_offset(pmd, address); - if (pte && pte_present(*pte)) { + if (pte && pte_present(*pte)) return pte_page(*pte); - } } printk(KERN_ERR "Missing page in follow_page\n"); - return 0; + return NULL; } /* * Given a physical address, is there a useful struct page pointing to it? */ -static struct page * get_page_map(unsigned long page) +struct page * get_page_map(struct page *page, unsigned long vaddr) { - struct page *map; - if (MAP_NR(page) >= max_mapnr) return 0; - if (page == ZERO_PAGE(page)) + if (page == ZERO_PAGE(vaddr)) return 0; - map = mem_map + MAP_NR(page); - if (PageReserved(map)) + if (PageReserved(page)) return 0; - return map; + return page; } /* @@ -441,7 +439,6 @@ int map_user_kiobuf(int rw, struct kiobuf *iobuf, unsigned long va, size_t len) int err; struct mm_struct * mm; struct vm_area_struct * vma = 0; - unsigned long page; struct page * map; int doublepage = 0; int repeat = 0; @@ -482,13 +479,12 @@ int map_user_kiobuf(int rw, struct kiobuf *iobuf, unsigned long va, size_t len) if (handle_mm_fault(current, vma, ptr, (rw==READ)) <= 0) goto out_unlock; spin_lock(&mm->page_table_lock); - page = follow_page(ptr); - if (!page) { + map = follow_page(ptr); + if (!map) { dprintk (KERN_ERR "Missing page in map_user_kiobuf\n"); - map = NULL; goto retry; } - map = get_page_map(page); + map = get_page_map(map, ptr); if (map) { if (TryLockPage(map)) { goto retry; @@ -496,8 +492,6 @@ int map_user_kiobuf(int rw, struct kiobuf *iobuf, unsigned long va, size_t len) atomic_inc(&map->count); } spin_unlock(&mm->page_table_lock); - dprintk ("Installing page %p %p: %d\n", (void *)page, map, i); - iobuf->pagelist[i] = page; iobuf->maplist[i] = map; iobuf->nr_pages = ++i; @@ -585,14 +579,13 @@ static inline void zeromap_pte_range(pte_t * pte, unsigned long address, if (end > PMD_SIZE) end = PMD_SIZE; do { - pte_t zero_pte = pte_wrprotect(mk_pte(ZERO_PAGE(address), - prot)); + pte_t zero_pte = pte_wrprotect(mk_pte(ZERO_PAGE(address), prot)); pte_t oldpage = *pte; set_pte(pte, zero_pte); forget_pte(oldpage); address += PAGE_SIZE; pte++; - } while (address < end); + } while (address && (address < end)); } static inline int zeromap_pmd_range(pmd_t * pmd, unsigned long address, @@ -611,7 +604,7 @@ static inline int zeromap_pmd_range(pmd_t * pmd, unsigned long address, zeromap_pte_range(pte, address, end - address, prot); address = (address + PMD_SIZE) & PMD_MASK; pmd++; - } while (address < end); + } while (address && (address < end)); return 0; } @@ -624,7 +617,9 @@ int zeromap_page_range(unsigned long address, unsigned long size, pgprot_t prot) dir = pgd_offset(current->mm, address); flush_cache_range(current->mm, beg, end); - while (address < end) { + if (address >= end) + BUG(); + do { pmd_t *pmd = pmd_alloc(dir, address); error = -ENOMEM; if (!pmd) @@ -634,7 +629,7 @@ int zeromap_page_range(unsigned long address, unsigned long size, pgprot_t prot) break; address = (address + PGDIR_SIZE) & PGDIR_MASK; dir++; - } + } while (address && (address < end)); flush_tlb_range(current->mm, beg, end); return error; } @@ -665,7 +660,7 @@ static inline void remap_pte_range(pte_t * pte, unsigned long address, unsigned address += PAGE_SIZE; phys_addr += PAGE_SIZE; pte++; - } while (address < end); + } while (address && (address < end)); } static inline int remap_pmd_range(pmd_t * pmd, unsigned long address, unsigned long size, @@ -685,7 +680,7 @@ static inline int remap_pmd_range(pmd_t * pmd, unsigned long address, unsigned l remap_pte_range(pte, address, end - address, address + phys_addr, prot); address = (address + PMD_SIZE) & PMD_MASK; pmd++; - } while (address < end); + } while (address && (address < end)); return 0; } @@ -699,7 +694,9 @@ int remap_page_range(unsigned long from, unsigned long phys_addr, unsigned long phys_addr -= from; dir = pgd_offset(current->mm, from); flush_cache_range(current->mm, beg, end); - while (from < end) { + if (from >= end) + BUG(); + do { pmd_t *pmd = pmd_alloc(dir, from); error = -ENOMEM; if (!pmd) @@ -709,7 +706,7 @@ int remap_page_range(unsigned long from, unsigned long phys_addr, unsigned long break; from = (from + PGDIR_SIZE) & PGDIR_MASK; dir++; - } + } while (from && (from < end)); flush_tlb_range(current->mm, beg, end); return error; } @@ -718,37 +715,35 @@ int remap_page_range(unsigned long from, unsigned long phys_addr, unsigned long * This routine is used to map in a page into an address space: needed by * execve() for the initial stack and environment pages. */ -unsigned long put_dirty_page(struct task_struct * tsk, unsigned long page, unsigned long address) +struct page * put_dirty_page(struct task_struct * tsk, struct page *page, + unsigned long address) { pgd_t * pgd; pmd_t * pmd; pte_t * pte; - if (MAP_NR(page) >= max_mapnr) - printk("put_dirty_page: trying to put page %08lx at %08lx\n",page,address); - if (page_count(mem_map + MAP_NR(page)) != 1) - printk("mem_map disagrees with %08lx at %08lx\n",page,address); - pgd = pgd_offset(tsk->mm,address); + if (page_count(page) != 1) + printk("mem_map disagrees with %p at %08lx\n", page, address); + pgd = pgd_offset(tsk->mm, address); pmd = pmd_alloc(pgd, address); if (!pmd) { - free_page(page); + __free_page(page); oom(tsk); return 0; } pte = pte_alloc(pmd, address); if (!pte) { - free_page(page); + __free_page(page); oom(tsk); return 0; } if (!pte_none(*pte)) { - printk("put_dirty_page: pte %08lx already exists\n", - pte_val(*pte)); - free_page(page); + pte_ERROR(*pte); + __free_page(page); return 0; } flush_page_to_ram(page); - set_pte(pte, pte_mkwrite(pte_mkdirty(mk_pte(page, PAGE_COPY)))); + set_pte(pte, pte_mkwrite(page_pte_prot(page, PAGE_COPY))); /* no need for flush_tlb */ return page; } @@ -776,14 +771,14 @@ unsigned long put_dirty_page(struct task_struct * tsk, unsigned long page, unsig static int do_wp_page(struct task_struct * tsk, struct vm_area_struct * vma, unsigned long address, pte_t *page_table, pte_t pte) { - unsigned long old_page, new_page; - struct page * page; + unsigned long map_nr; + struct page *old_page, *new_page; - old_page = pte_page(pte); - if (MAP_NR(old_page) >= max_mapnr) + map_nr = pte_pagenr(pte); + if (map_nr >= max_mapnr) goto bad_wp_page; tsk->min_flt++; - page = mem_map + MAP_NR(old_page); + old_page = mem_map + map_nr; /* * We can avoid the copy if: @@ -793,13 +788,13 @@ static int do_wp_page(struct task_struct * tsk, struct vm_area_struct * vma, * in which case we can remove the page * from the swap cache. */ - switch (page_count(page)) { + switch (page_count(old_page)) { case 2: - if (!PageSwapCache(page)) + if (!PageSwapCache(old_page)) break; - if (swap_count(page->offset) != 1) + if (swap_count(old_page) != 1) break; - delete_from_swap_cache(page); + delete_from_swap_cache(old_page); /* FallThrough */ case 1: flush_cache_page(vma, address); @@ -813,7 +808,7 @@ static int do_wp_page(struct task_struct * tsk, struct vm_area_struct * vma, * Ok, we need to copy. Oh, well.. */ spin_unlock(&tsk->mm->page_table_lock); - new_page = __get_free_page(GFP_BIGUSER); + new_page = get_free_highpage(GFP_HIGHUSER); if (!new_page) return -1; spin_lock(&tsk->mm->page_table_lock); @@ -822,9 +817,9 @@ static int do_wp_page(struct task_struct * tsk, struct vm_area_struct * vma, * Re-check the pte - we dropped the lock */ if (pte_val(*page_table) == pte_val(pte)) { - if (PageReserved(page)) + if (PageReserved(old_page)) ++vma->vm_mm->rss; - copy_cow_page(old_page,new_page); + copy_cow_page(old_page, new_page, address); flush_page_to_ram(new_page); flush_cache_page(vma, address); set_pte(page_table, pte_mkwrite(pte_mkdirty(mk_pte(new_page, vma->vm_page_prot)))); @@ -834,12 +829,12 @@ static int do_wp_page(struct task_struct * tsk, struct vm_area_struct * vma, new_page = old_page; } spin_unlock(&tsk->mm->page_table_lock); - free_page(new_page); + __free_page(new_page); return 1; bad_wp_page: spin_unlock(&tsk->mm->page_table_lock); - printk("do_wp_page: bogus page at address %08lx (%08lx)\n",address,old_page); + printk("do_wp_page: bogus page at address %08lx (nr %ld)\n",address,map_nr); return -1; } @@ -848,6 +843,8 @@ bad_wp_page: */ static void partial_clear(struct vm_area_struct *vma, unsigned long address) { + unsigned int offset; + struct page *page; pgd_t *page_dir; pmd_t *page_middle; pte_t *page_table, pte; @@ -856,7 +853,7 @@ static void partial_clear(struct vm_area_struct *vma, unsigned long address) if (pgd_none(*page_dir)) return; if (pgd_bad(*page_dir)) { - printk("bad page table directory entry %p:[%lx]\n", page_dir, pgd_val(*page_dir)); + pgd_ERROR(*page_dir); pgd_clear(page_dir); return; } @@ -864,7 +861,7 @@ static void partial_clear(struct vm_area_struct *vma, unsigned long address) if (pmd_none(*page_middle)) return; if (pmd_bad(*page_middle)) { - printk("bad page table directory entry %p:[%lx]\n", page_dir, pgd_val(*page_dir)); + pmd_ERROR(*page_middle); pmd_clear(page_middle); return; } @@ -873,12 +870,11 @@ static void partial_clear(struct vm_area_struct *vma, unsigned long address) if (!pte_present(pte)) return; flush_cache_page(vma, address); - address &= ~PAGE_MASK; - address += pte_page(pte); - if (MAP_NR(address) >= max_mapnr) + page = pte_page(pte); + if (page-mem_map >= max_mapnr) return; - memset((void *) address, 0, PAGE_SIZE - (address & ~PAGE_MASK)); - flush_page_to_ram(pte_page(pte)); + offset = address & ~PAGE_MASK; + memclear_highpage_flush(page, offset, PAGE_SIZE - offset); } /* @@ -939,7 +935,7 @@ out_unlock: * because it doesn't cost us any seek time. We also make sure to queue * the 'original' request together with the readahead ones... */ -void swapin_readahead(unsigned long entry) +void swapin_readahead(pte_t entry) { int i; struct page *new_page; @@ -973,7 +969,7 @@ void swapin_readahead(unsigned long entry) static int do_swap_page(struct task_struct * tsk, struct vm_area_struct * vma, unsigned long address, - pte_t * page_table, unsigned long entry, int write_access) + pte_t * page_table, pte_t entry, int write_access) { struct page *page = lookup_swap_cache(entry); pte_t pte; @@ -986,7 +982,7 @@ static int do_swap_page(struct task_struct * tsk, if (!page) return -1; - flush_page_to_ram(page_address(page)); + flush_page_to_ram(page); } vma->vm_mm->rss++; @@ -995,13 +991,13 @@ static int do_swap_page(struct task_struct * tsk, swap_free(entry); unlock_kernel(); - pte = mk_pte(page_address(page), vma->vm_page_prot); + pte = mk_pte(page, vma->vm_page_prot); set_bit(PG_swap_entry, &page->flags); if (write_access && !is_page_shared(page)) { delete_from_swap_cache(page); - page = replace_with_bigmem(page); - pte = mk_pte(page_address(page), vma->vm_page_prot); + page = replace_with_highmem(page); + pte = mk_pte(page, vma->vm_page_prot); pte = pte_mkwrite(pte_mkdirty(pte)); } set_pte(page_table, pte); @@ -1015,12 +1011,16 @@ static int do_swap_page(struct task_struct * tsk, */ static int do_anonymous_page(struct task_struct * tsk, struct vm_area_struct * vma, pte_t *page_table, int write_access, unsigned long addr) { + int high = 0; + struct page *page = NULL; pte_t entry = pte_wrprotect(mk_pte(ZERO_PAGE(addr), vma->vm_page_prot)); if (write_access) { - unsigned long page = __get_free_page(GFP_BIGUSER); + page = get_free_highpage(GFP_HIGHUSER); if (!page) return -1; - clear_bigpage(page); + if (PageHighMem(page)) + high = 1; + clear_highpage(page); entry = pte_mkwrite(pte_mkdirty(mk_pte(page, vma->vm_page_prot))); vma->vm_mm->rss++; tsk->min_flt++; @@ -1047,7 +1047,7 @@ static int do_anonymous_page(struct task_struct * tsk, struct vm_area_struct * v static int do_no_page(struct task_struct * tsk, struct vm_area_struct * vma, unsigned long address, int write_access, pte_t *page_table) { - unsigned long page; + struct page * new_page; pte_t entry; if (!vma->vm_ops || !vma->vm_ops->nopage) @@ -1058,12 +1058,11 @@ static int do_no_page(struct task_struct * tsk, struct vm_area_struct * vma, * to copy, not share the page even if sharing is possible. It's * essentially an early COW detection. */ - page = vma->vm_ops->nopage(vma, address & PAGE_MASK, (vma->vm_flags & VM_SHARED)?0:write_access); - if (!page) + new_page = vma->vm_ops->nopage(vma, address & PAGE_MASK, (vma->vm_flags & VM_SHARED)?0:write_access); + if (!new_page) return 0; /* SIGBUS - but we _really_ should know whether it is OOM or SIGBUS */ - if (page == -1) + if (new_page == (struct page *)-1) return -1; /* OOM */ - ++tsk->maj_flt; ++vma->vm_mm->rss; /* @@ -1076,11 +1075,11 @@ static int do_no_page(struct task_struct * tsk, struct vm_area_struct * vma, * so we can make it writable and dirty to avoid having to * handle that later. */ - flush_page_to_ram(page); - entry = mk_pte(page, vma->vm_page_prot); + flush_page_to_ram(new_page); + entry = mk_pte(new_page, vma->vm_page_prot); if (write_access) { entry = pte_mkwrite(pte_mkdirty(entry)); - } else if (page_count(mem_map+MAP_NR(page)) > 1 && + } else if (page_count(new_page) > 1 && !(vma->vm_flags & VM_SHARED)) entry = pte_wrprotect(entry); set_pte(page_table, entry); @@ -1117,7 +1116,7 @@ static inline int handle_pte_fault(struct task_struct *tsk, if (!pte_present(entry)) { if (pte_none(entry)) return do_no_page(tsk, vma, address, write_access, pte); - return do_swap_page(tsk, vma, address, pte, pte_val(entry), write_access); + return do_swap_page(tsk, vma, address, pte, entry, write_access); } /* @@ -1148,17 +1147,19 @@ static inline int handle_pte_fault(struct task_struct *tsk, int handle_mm_fault(struct task_struct *tsk, struct vm_area_struct * vma, unsigned long address, int write_access) { + int ret = -1; pgd_t *pgd; pmd_t *pmd; pgd = pgd_offset(vma->vm_mm, address); pmd = pmd_alloc(pgd, address); + if (pmd) { pte_t * pte = pte_alloc(pmd, address); if (pte) - return handle_pte_fault(tsk, vma, address, write_access, pte); + ret = handle_pte_fault(tsk, vma, address, write_access, pte); } - return -1; + return ret; } /* @@ -1172,10 +1173,12 @@ int make_pages_present(unsigned long addr, unsigned long end) vma = find_vma(tsk->mm, addr); write = (vma->vm_flags & VM_WRITE) != 0; - while (addr < end) { + if (addr >= end) + BUG(); + do { if (handle_mm_fault(tsk, vma, addr, write) < 0) return -1; addr += PAGE_SIZE; - } + } while (addr < end); return 0; } diff --git a/mm/mlock.c b/mm/mlock.c index be5e07cbf..9709d1a04 100644 --- a/mm/mlock.c +++ b/mm/mlock.c @@ -13,7 +13,9 @@ static inline int mlock_fixup_all(struct vm_area_struct * vma, int newflags) { + vmlist_modify_lock(vma->vm_mm); vma->vm_flags = newflags; + vmlist_modify_unlock(vma->vm_mm); return 0; } @@ -26,15 +28,17 @@ static inline int mlock_fixup_start(struct vm_area_struct * vma, if (!n) return -EAGAIN; *n = *vma; - vma->vm_start = end; n->vm_end = end; - vma->vm_offset += vma->vm_start - n->vm_start; n->vm_flags = newflags; if (n->vm_file) get_file(n->vm_file); if (n->vm_ops && n->vm_ops->open) n->vm_ops->open(n); + vmlist_modify_lock(vma->vm_mm); + vma->vm_offset += end - vma->vm_start; + vma->vm_start = end; insert_vm_struct(current->mm, n); + vmlist_modify_unlock(vma->vm_mm); return 0; } @@ -47,7 +51,6 @@ static inline int mlock_fixup_end(struct vm_area_struct * vma, if (!n) return -EAGAIN; *n = *vma; - vma->vm_end = start; n->vm_start = start; n->vm_offset += n->vm_start - vma->vm_start; n->vm_flags = newflags; @@ -55,7 +58,10 @@ static inline int mlock_fixup_end(struct vm_area_struct * vma, get_file(n->vm_file); if (n->vm_ops && n->vm_ops->open) n->vm_ops->open(n); + vmlist_modify_lock(vma->vm_mm); + vma->vm_end = start; insert_vm_struct(current->mm, n); + vmlist_modify_unlock(vma->vm_mm); return 0; } @@ -75,10 +81,7 @@ static inline int mlock_fixup_middle(struct vm_area_struct * vma, *left = *vma; *right = *vma; left->vm_end = start; - vma->vm_start = start; - vma->vm_end = end; right->vm_start = end; - vma->vm_offset += vma->vm_start - left->vm_start; right->vm_offset += right->vm_start - left->vm_start; vma->vm_flags = newflags; if (vma->vm_file) @@ -88,8 +91,14 @@ static inline int mlock_fixup_middle(struct vm_area_struct * vma, vma->vm_ops->open(left); vma->vm_ops->open(right); } + vmlist_modify_lock(vma->vm_mm); + vma->vm_offset += start - vma->vm_start; + vma->vm_start = start; + vma->vm_end = end; + vma->vm_flags = newflags; insert_vm_struct(current->mm, left); insert_vm_struct(current->mm, right); + vmlist_modify_unlock(vma->vm_mm); return 0; } @@ -168,7 +177,9 @@ static int do_mlock(unsigned long start, size_t len, int on) break; } } + vmlist_modify_lock(current->mm); merge_segments(current->mm, start, end); + vmlist_modify_unlock(current->mm); return error; } @@ -240,7 +251,9 @@ static int do_mlockall(int flags) if (error) break; } + vmlist_modify_lock(current->mm); merge_segments(current->mm, 0, TASK_SIZE); + vmlist_modify_unlock(current->mm); return error; } @@ -62,7 +62,7 @@ int vm_enough_memory(long pages) if (sysctl_overcommit_memory) return 1; - free = atomic_read(&buffermem) >> PAGE_SHIFT; + free = atomic_read(&buffermem_pages); free += atomic_read(&page_cache_size); free += nr_free_pages; free += nr_swap_pages; @@ -323,8 +323,10 @@ unsigned long do_mmap(struct file * file, unsigned long addr, unsigned long len, */ flags = vma->vm_flags; addr = vma->vm_start; /* can addr have changed?? */ + vmlist_modify_lock(mm); insert_vm_struct(mm, vma); merge_segments(mm, vma->vm_start, vma->vm_end); + vmlist_modify_unlock(mm); mm->total_vm += len >> PAGE_SHIFT; if (flags & VM_LOCKED) { @@ -527,11 +529,13 @@ static struct vm_area_struct * unmap_fixup(struct vm_area_struct *area, } /* Work out to one of the ends. */ - if (end == area->vm_end) + if (end == area->vm_end) { area->vm_end = addr; - else if (addr == area->vm_start) { + vmlist_modify_lock(current->mm); + } else if (addr == area->vm_start) { area->vm_offset += (end - area->vm_start); area->vm_start = end; + vmlist_modify_lock(current->mm); } else { /* Unmapping a hole: area->vm_start < addr <= end < area->vm_end */ /* Add end mapping -- leave beginning for below */ @@ -552,10 +556,12 @@ static struct vm_area_struct * unmap_fixup(struct vm_area_struct *area, if (mpnt->vm_ops && mpnt->vm_ops->open) mpnt->vm_ops->open(mpnt); area->vm_end = addr; /* Truncate area */ + vmlist_modify_lock(current->mm); insert_vm_struct(current->mm, mpnt); } insert_vm_struct(current->mm, area); + vmlist_modify_unlock(current->mm); return extra; } @@ -655,6 +661,7 @@ int do_munmap(unsigned long addr, size_t len) npp = (prev ? &prev->vm_next : &mm->mmap); free = NULL; + vmlist_modify_lock(mm); for ( ; mpnt && mpnt->vm_start < addr+len; mpnt = *npp) { *npp = mpnt->vm_next; mpnt->vm_next = free; @@ -662,6 +669,8 @@ int do_munmap(unsigned long addr, size_t len) if (mm->mmap_avl) avl_remove(mpnt, &mm->mmap_avl); } + mm->mmap_cache = NULL; /* Kill the cache. */ + vmlist_modify_unlock(mm); /* Ok - we have the memory areas we should free on the 'free' list, * so release them, and unmap the page range.. @@ -678,6 +687,11 @@ int do_munmap(unsigned long addr, size_t len) end = end > mpnt->vm_end ? mpnt->vm_end : end; size = end - st; + /* + * The lock_kernel interlocks with kswapd try_to_swap_out + * invoking a driver swapout() method, and being able to + * guarantee vma existance. + */ lock_kernel(); if (mpnt->vm_ops && mpnt->vm_ops->unmap) mpnt->vm_ops->unmap(mpnt, st, size); @@ -702,7 +716,6 @@ int do_munmap(unsigned long addr, size_t len) free_pgtables(mm, prev, addr, addr+len); - mm->mmap_cache = NULL; /* Kill the cache. */ return 0; } @@ -786,8 +799,10 @@ unsigned long do_brk(unsigned long addr, unsigned long len) flags = vma->vm_flags; addr = vma->vm_start; + vmlist_modify_lock(mm); insert_vm_struct(mm, vma); merge_segments(mm, vma->vm_start, vma->vm_end); + vmlist_modify_unlock(mm); mm->total_vm += len >> PAGE_SHIFT; if (flags & VM_LOCKED) { @@ -814,7 +829,9 @@ void exit_mmap(struct mm_struct * mm) release_segments(mm); mpnt = mm->mmap; + vmlist_modify_lock(mm); mm->mmap = mm->mmap_avl = mm->mmap_cache = NULL; + vmlist_modify_unlock(mm); mm->rss = 0; mm->total_vm = 0; mm->locked_vm = 0; @@ -910,6 +927,7 @@ void merge_segments (struct mm_struct * mm, unsigned long start_addr, unsigned l prev = mpnt; mpnt = mpnt->vm_next; } + mm->mmap_cache = NULL; /* Kill the cache. */ /* prev and mpnt cycle through the list, as long as * start_addr < mpnt->vm_end && prev->vm_start < end_addr @@ -946,7 +964,9 @@ void merge_segments (struct mm_struct * mm, unsigned long start_addr, unsigned l if (mpnt->vm_ops && mpnt->vm_ops->close) { mpnt->vm_offset += mpnt->vm_end - mpnt->vm_start; mpnt->vm_start = mpnt->vm_end; + vmlist_modify_unlock(mm); mpnt->vm_ops->close(mpnt); + vmlist_modify_lock(mm); } mm->map_count--; remove_shared_vm_struct(mpnt); @@ -955,7 +975,6 @@ void merge_segments (struct mm_struct * mm, unsigned long start_addr, unsigned l kmem_cache_free(vm_area_cachep, mpnt); mpnt = prev; } - mm->mmap_cache = NULL; /* Kill the cache. */ } void __init vma_init(void) diff --git a/mm/mprotect.c b/mm/mprotect.c index 61ef3116d..56454fc07 100644 --- a/mm/mprotect.c +++ b/mm/mprotect.c @@ -20,7 +20,7 @@ static inline void change_pte_range(pmd_t * pmd, unsigned long address, if (pmd_none(*pmd)) return; if (pmd_bad(*pmd)) { - printk("change_pte_range: bad pmd (%08lx)\n", pmd_val(*pmd)); + pmd_ERROR(*pmd); pmd_clear(pmd); return; } @@ -35,7 +35,7 @@ static inline void change_pte_range(pmd_t * pmd, unsigned long address, set_pte(pte, pte_modify(entry, newprot)); address += PAGE_SIZE; pte++; - } while (address < end); + } while (address && (address < end)); } static inline void change_pmd_range(pgd_t * pgd, unsigned long address, @@ -47,7 +47,7 @@ static inline void change_pmd_range(pgd_t * pgd, unsigned long address, if (pgd_none(*pgd)) return; if (pgd_bad(*pgd)) { - printk("change_pmd_range: bad pgd (%08lx)\n", pgd_val(*pgd)); + pgd_ERROR(*pgd); pgd_clear(pgd); return; } @@ -60,7 +60,7 @@ static inline void change_pmd_range(pgd_t * pgd, unsigned long address, change_pte_range(pmd, address, end - address, newprot); address = (address + PMD_SIZE) & PMD_MASK; pmd++; - } while (address < end); + } while (address && (address < end)); } static void change_protection(unsigned long start, unsigned long end, pgprot_t newprot) @@ -70,11 +70,13 @@ static void change_protection(unsigned long start, unsigned long end, pgprot_t n dir = pgd_offset(current->mm, start); flush_cache_range(current->mm, beg, end); - while (start < end) { + if (start >= end) + BUG(); + do { change_pmd_range(dir, start, end - start, newprot); start = (start + PGDIR_SIZE) & PGDIR_MASK; dir++; - } + } while (start && (start < end)); flush_tlb_range(current->mm, beg, end); return; } @@ -82,8 +84,10 @@ static void change_protection(unsigned long start, unsigned long end, pgprot_t n static inline int mprotect_fixup_all(struct vm_area_struct * vma, int newflags, pgprot_t prot) { + vmlist_modify_lock(vma->vm_mm); vma->vm_flags = newflags; vma->vm_page_prot = prot; + vmlist_modify_unlock(vma->vm_mm); return 0; } @@ -97,16 +101,18 @@ static inline int mprotect_fixup_start(struct vm_area_struct * vma, if (!n) return -ENOMEM; *n = *vma; - vma->vm_start = end; n->vm_end = end; - vma->vm_offset += vma->vm_start - n->vm_start; n->vm_flags = newflags; n->vm_page_prot = prot; if (n->vm_file) get_file(n->vm_file); if (n->vm_ops && n->vm_ops->open) n->vm_ops->open(n); + vmlist_modify_lock(vma->vm_mm); + vma->vm_offset += end - vma->vm_start; + vma->vm_start = end; insert_vm_struct(current->mm, n); + vmlist_modify_unlock(vma->vm_mm); return 0; } @@ -120,7 +126,6 @@ static inline int mprotect_fixup_end(struct vm_area_struct * vma, if (!n) return -ENOMEM; *n = *vma; - vma->vm_end = start; n->vm_start = start; n->vm_offset += n->vm_start - vma->vm_start; n->vm_flags = newflags; @@ -129,7 +134,10 @@ static inline int mprotect_fixup_end(struct vm_area_struct * vma, get_file(n->vm_file); if (n->vm_ops && n->vm_ops->open) n->vm_ops->open(n); + vmlist_modify_lock(vma->vm_mm); + vma->vm_end = start; insert_vm_struct(current->mm, n); + vmlist_modify_unlock(vma->vm_mm); return 0; } @@ -150,21 +158,23 @@ static inline int mprotect_fixup_middle(struct vm_area_struct * vma, *left = *vma; *right = *vma; left->vm_end = start; - vma->vm_start = start; - vma->vm_end = end; right->vm_start = end; - vma->vm_offset += vma->vm_start - left->vm_start; right->vm_offset += right->vm_start - left->vm_start; - vma->vm_flags = newflags; - vma->vm_page_prot = prot; if (vma->vm_file) atomic_add(2,&vma->vm_file->f_count); if (vma->vm_ops && vma->vm_ops->open) { vma->vm_ops->open(left); vma->vm_ops->open(right); } + vmlist_modify_lock(vma->vm_mm); + vma->vm_offset += start - vma->vm_start; + vma->vm_start = start; + vma->vm_end = end; + vma->vm_flags = newflags; + vma->vm_page_prot = prot; insert_vm_struct(current->mm, left); insert_vm_struct(current->mm, right); + vmlist_modify_unlock(vma->vm_mm); return 0; } @@ -246,7 +256,9 @@ asmlinkage long sys_mprotect(unsigned long start, size_t len, unsigned long prot break; } } + vmlist_modify_lock(current->mm); merge_segments(current->mm, start, end); + vmlist_modify_unlock(current->mm); out: up(¤t->mm->mmap_sem); return error; diff --git a/mm/mremap.c b/mm/mremap.c index 95f4b4f90..b73996dc2 100644 --- a/mm/mremap.c +++ b/mm/mremap.c @@ -25,7 +25,7 @@ static inline pte_t *get_one_pte(struct mm_struct *mm, unsigned long addr) if (pgd_none(*pgd)) goto end; if (pgd_bad(*pgd)) { - printk("move_one_page: bad source pgd (%08lx)\n", pgd_val(*pgd)); + pgd_ERROR(*pgd); pgd_clear(pgd); goto end; } @@ -34,7 +34,7 @@ static inline pte_t *get_one_pte(struct mm_struct *mm, unsigned long addr) if (pmd_none(*pmd)) goto end; if (pmd_bad(*pmd)) { - printk("move_one_page: bad source pmd (%08lx)\n", pmd_val(*pmd)); + pmd_ERROR(*pmd); pmd_clear(pmd); goto end; } @@ -141,8 +141,10 @@ static inline unsigned long move_vma(struct vm_area_struct * vma, get_file(new_vma->vm_file); if (new_vma->vm_ops && new_vma->vm_ops->open) new_vma->vm_ops->open(new_vma); + vmlist_modify_lock(current->mm); insert_vm_struct(current->mm, new_vma); merge_segments(current->mm, new_vma->vm_start, new_vma->vm_end); + vmlist_modify_unlock(vma->vm_mm); do_munmap(addr, old_len); current->mm->total_vm += new_len >> PAGE_SHIFT; if (new_vma->vm_flags & VM_LOCKED) { @@ -220,7 +222,9 @@ asmlinkage unsigned long sys_mremap(unsigned long addr, /* can we just expand the current mapping? */ if (max_addr - addr >= new_len) { int pages = (new_len - old_len) >> PAGE_SHIFT; + vmlist_modify_lock(vma->vm_mm); vma->vm_end = addr + new_len; + vmlist_modify_unlock(vma->vm_mm); current->mm->total_vm += pages; if (vma->vm_flags & VM_LOCKED) { current->mm->locked_vm += pages; diff --git a/mm/page_alloc.c b/mm/page_alloc.c index b62783c72..27aa58468 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -14,7 +14,8 @@ #include <linux/interrupt.h> #include <linux/init.h> #include <linux/pagemap.h> -#include <linux/bigmem.h> /* export bigmem vars */ +#include <linux/highmem.h> +#include <linux/bootmem.h> #include <asm/dma.h> #include <asm/uaccess.h> /* for copy_to/from_user */ @@ -40,45 +41,25 @@ LIST_HEAD(lru_cache); #define NR_MEM_LISTS 10 #endif -/* The start of this MUST match the start of "struct page" */ struct free_area_struct { - struct page *next; - struct page *prev; + struct list_head free_list; unsigned int * map; + unsigned long count; }; -#define memory_head(x) ((struct page *)(x)) +#define MEM_TYPE_DMA 0 +#define MEM_TYPE_NORMAL 1 +#define MEM_TYPE_HIGH 2 -#ifdef CONFIG_BIGMEM -#define BIGMEM_LISTS_OFFSET NR_MEM_LISTS -static struct free_area_struct free_area[NR_MEM_LISTS*2]; +static const char *mem_type_strs[] = {"DMA", "Normal", "High"}; + +#ifdef CONFIG_HIGHMEM +#define NR_MEM_TYPES 3 #else -static struct free_area_struct free_area[NR_MEM_LISTS]; +#define NR_MEM_TYPES 2 #endif -static inline void init_mem_queue(struct free_area_struct * head) -{ - head->next = memory_head(head); - head->prev = memory_head(head); -} - -static inline void add_mem_queue(struct free_area_struct * head, struct page * entry) -{ - struct page * next = head->next; - - entry->prev = memory_head(head); - entry->next = next; - next->prev = entry; - head->next = entry; -} - -static inline void remove_mem_queue(struct page * entry) -{ - struct page * next = entry->next; - struct page * prev = entry->prev; - next->prev = prev; - prev->next = next; -} +static struct free_area_struct free_area[NR_MEM_TYPES][NR_MEM_LISTS]; /* * Free_page() adds the page to the free lists. This is optimized for @@ -99,41 +80,76 @@ static inline void remove_mem_queue(struct page * entry) */ spinlock_t page_alloc_lock = SPIN_LOCK_UNLOCKED; -static inline void free_pages_ok(unsigned long map_nr, unsigned long order) +#define memlist_init(x) INIT_LIST_HEAD(x) +#define memlist_add_head list_add +#define memlist_add_tail list_add_tail +#define memlist_del list_del +#define memlist_entry list_entry +#define memlist_next(x) ((x)->next) +#define memlist_prev(x) ((x)->prev) + +static inline void free_pages_ok(struct page *page, unsigned long map_nr, unsigned long order) { - struct free_area_struct *area = free_area + order; + struct free_area_struct *area; unsigned long index = map_nr >> (1 + order); unsigned long mask = (~0UL) << order; unsigned long flags; + struct page *buddy; spin_lock_irqsave(&page_alloc_lock, flags); #define list(x) (mem_map+(x)) -#ifdef CONFIG_BIGMEM - if (map_nr >= bigmem_mapnr) { - area += BIGMEM_LISTS_OFFSET; - nr_free_bigpages -= mask; - } +#ifdef CONFIG_HIGHMEM + if (map_nr >= highmem_mapnr) { + area = free_area[MEM_TYPE_HIGH]; + nr_free_highpages -= mask; + } else #endif + if (PageDMA(page)) + area = free_area[MEM_TYPE_DMA]; + else + area = free_area[MEM_TYPE_NORMAL]; + + area += order; + map_nr &= mask; nr_free_pages -= mask; + while (mask + (1 << (NR_MEM_LISTS-1))) { if (!test_and_change_bit(index, area->map)) + /* + * the buddy page is still allocated. + */ break; - remove_mem_queue(list(map_nr ^ -mask)); + /* + * Move the buddy up one level. + */ + buddy = list(map_nr ^ -mask); + page = list(map_nr); + + area->count--; + memlist_del(&buddy->list); mask <<= 1; area++; index >>= 1; map_nr &= mask; } - add_mem_queue(area, list(map_nr)); - + area->count++; + memlist_add_head(&(list(map_nr))->list, &area->free_list); #undef list spin_unlock_irqrestore(&page_alloc_lock, flags); } +/* + * Some ugly macros to speed up __get_free_pages().. + */ +#define MARK_USED(index, order, area) \ + change_bit((index) >> (1+(order)), (area)->map) +#define CAN_DMA(x) (PageDMA(x)) +#define ADDRESS(x) (PAGE_OFFSET + ((x) << PAGE_SHIFT)) + int __free_page(struct page *page) { if (!PageReserved(page) && put_page_testzero(page)) { @@ -142,7 +158,7 @@ int __free_page(struct page *page) if (PageLocked(page)) PAGE_BUG(page); - free_pages_ok(page - mem_map, 0); + free_pages_ok(page, page-mem_map, 0); return 1; } return 0; @@ -159,154 +175,155 @@ int free_pages(unsigned long addr, unsigned long order) PAGE_BUG(map); if (PageLocked(map)) PAGE_BUG(map); - free_pages_ok(map_nr, order); + free_pages_ok(map, map_nr, order); return 1; } } return 0; } -/* - * Some ugly macros to speed up __get_free_pages().. - */ -#define MARK_USED(index, order, area) \ - change_bit((index) >> (1+(order)), (area)->map) -#define CAN_DMA(x) (PageDMA(x)) -#define ADDRESS(x) (PAGE_OFFSET + ((x) << PAGE_SHIFT)) +static inline unsigned long EXPAND (struct page *map, unsigned long index, + int low, int high, struct free_area_struct * area) +{ + unsigned long size = 1 << high; + + while (high > low) { + area--; + high--; + size >>= 1; + area->count++; + memlist_add_head(&(map)->list, &(area)->free_list); + MARK_USED(index, high, area); + index += size; + map += size; + } + set_page_count(map, 1); + return index; +} + +static inline struct page * rmqueue (int order, unsigned type) +{ + struct free_area_struct * area = free_area[type]+order; + unsigned long curr_order = order, map_nr; + struct page *page; + struct list_head *head, *curr; -#ifdef CONFIG_BIGMEM -#define RMQUEUEBIG(order, gfp_mask) \ -if (gfp_mask & __GFP_BIGMEM) { \ - struct free_area_struct * area = free_area+order+BIGMEM_LISTS_OFFSET; \ - unsigned long new_order = order; \ - do { struct page *prev = memory_head(area), *ret = prev->next; \ - if (memory_head(area) != ret) { \ - unsigned long map_nr; \ - (prev->next = ret->next)->prev = prev; \ - map_nr = ret - mem_map; \ - MARK_USED(map_nr, new_order, area); \ - nr_free_pages -= 1 << order; \ - nr_free_bigpages -= 1 << order; \ - EXPAND(ret, map_nr, order, new_order, area); \ - spin_unlock_irqrestore(&page_alloc_lock, flags); \ - return ADDRESS(map_nr); \ - } \ - new_order++; area++; \ - } while (new_order < NR_MEM_LISTS); \ + do { + head = &area->free_list; + curr = memlist_next(head); + + if (curr != head) { + page = memlist_entry(curr, struct page, list); + memlist_del(curr); + area->count--; + map_nr = page - mem_map; + MARK_USED(map_nr, curr_order, area); + nr_free_pages -= 1 << order; + map_nr = EXPAND(page, map_nr, order, curr_order, area); + page = mem_map + map_nr; + return page; + } + curr_order++; + area++; + } while (curr_order < NR_MEM_LISTS); + + return NULL; } + +static inline int balance_lowmemory (int gfp_mask) +{ + int freed; + static int low_on_memory = 0; + +#ifndef CONFIG_HIGHMEM + if (nr_free_pages > freepages.min) { + if (!low_on_memory) + return 1; + if (nr_free_pages >= freepages.high) { + low_on_memory = 0; + return 1; + } + } + + low_on_memory = 1; +#else + static int low_on_highmemory = 0; + + if (gfp_mask & __GFP_HIGHMEM) + { + if (nr_free_pages > freepages.min) { + if (!low_on_highmemory) { + return 1; + } + if (nr_free_pages >= freepages.high) { + low_on_highmemory = 0; + return 1; + } + } + low_on_highmemory = 1; + } else { + if (nr_free_pages+nr_free_highpages > freepages.min) { + if (!low_on_memory) { + return 1; + } + if (nr_free_pages+nr_free_highpages >= freepages.high) { + low_on_memory = 0; + return 1; + } + } + low_on_memory = 1; + } #endif + current->flags |= PF_MEMALLOC; + freed = try_to_free_pages(gfp_mask); + current->flags &= ~PF_MEMALLOC; -#define RMQUEUE(order, gfp_mask) \ -do { struct free_area_struct * area = free_area+order; \ - unsigned long new_order = order; \ - do { struct page *prev = memory_head(area), *ret = prev->next; \ - while (memory_head(area) != ret) { \ - if (!(gfp_mask & __GFP_DMA) || CAN_DMA(ret)) { \ - unsigned long map_nr; \ - (prev->next = ret->next)->prev = prev; \ - map_nr = ret - mem_map; \ - MARK_USED(map_nr, new_order, area); \ - nr_free_pages -= 1 << order; \ - EXPAND(ret, map_nr, order, new_order, area); \ - spin_unlock_irqrestore(&page_alloc_lock,flags);\ - return ADDRESS(map_nr); \ - } \ - prev = ret; \ - ret = ret->next; \ - } \ - new_order++; area++; \ - } while (new_order < NR_MEM_LISTS); \ -} while (0) - -#define EXPAND(map,index,low,high,area) \ -do { unsigned long size = 1 << high; \ - while (high > low) { \ - area--; high--; size >>= 1; \ - add_mem_queue(area, map); \ - MARK_USED(index, high, area); \ - index += size; \ - map += size; \ - } \ - set_page_count(map, 1); \ -} while (0) + if (!freed && !(gfp_mask & (__GFP_MED | __GFP_HIGH))) + return 0; + return 1; +} -unsigned long __get_free_pages(int gfp_mask, unsigned long order) +struct page * __get_pages(int gfp_mask, unsigned long order) { unsigned long flags; + struct page *page; + unsigned type; if (order >= NR_MEM_LISTS) goto nopage; -#ifdef ATOMIC_MEMORY_DEBUGGING - if ((gfp_mask & __GFP_WAIT) && in_interrupt()) { - static int count = 0; - if (++count < 5) { - printk("gfp called nonatomically from interrupt %p\n", - __builtin_return_address(0)); - } - goto nopage; - } -#endif + /* + * If anyone calls gfp from interrupts nonatomically then it + * will sooner or later tripped up by a schedule(). + */ /* * If this is a recursive call, we'd better * do our best to just allocate things without * further thought. */ - if (!(current->flags & PF_MEMALLOC)) { - int freed; - static int low_on_memory = 0; + if (!(current->flags & PF_MEMALLOC)) + goto lowmemory; -#ifndef CONFIG_BIGMEM - if (nr_free_pages > freepages.min) { - if (!low_on_memory) - goto ok_to_allocate; - if (nr_free_pages >= freepages.high) { - low_on_memory = 0; - goto ok_to_allocate; - } - } - - low_on_memory = 1; -#else - static int low_on_bigmemory = 0; - - if (gfp_mask & __GFP_BIGMEM) - { - if (nr_free_pages > freepages.min) { - if (!low_on_bigmemory) - goto ok_to_allocate; - if (nr_free_pages >= freepages.high) { - low_on_bigmemory = 0; - goto ok_to_allocate; - } - } - low_on_bigmemory = 1; - } else { - if (nr_free_pages-nr_free_bigpages > freepages.min) { - if (!low_on_memory) - goto ok_to_allocate; - if (nr_free_pages-nr_free_bigpages >= freepages.high) { - low_on_memory = 0; - goto ok_to_allocate; - } - } - low_on_memory = 1; - } +ok_to_allocate: +#ifdef CONFIG_HIGHMEM + if (gfp_mask & __GFP_HIGHMEM) + type = MEM_TYPE_HIGH; + else #endif - current->flags |= PF_MEMALLOC; - freed = try_to_free_pages(gfp_mask); - current->flags &= ~PF_MEMALLOC; + if (gfp_mask & __GFP_DMA) + type = MEM_TYPE_DMA; + else + type = MEM_TYPE_NORMAL; - if (!freed && !(gfp_mask & (__GFP_MED | __GFP_HIGH))) - goto nopage; - } -ok_to_allocate: spin_lock_irqsave(&page_alloc_lock, flags); -#ifdef CONFIG_BIGMEM - RMQUEUEBIG(order, gfp_mask); -#endif - RMQUEUE(order, gfp_mask); + do { + page = rmqueue(order, type); + if (page) { + spin_unlock_irqrestore(&page_alloc_lock, flags); + return page; + } + } while (type-- > 0) ; spin_unlock_irqrestore(&page_alloc_lock, flags); /* @@ -320,7 +337,26 @@ ok_to_allocate: } nopage: - return 0; + return NULL; + +lowmemory: + if (balance_lowmemory(gfp_mask)) + goto ok_to_allocate; + goto nopage; +} + +unsigned long __get_free_pages(int gfp_mask, unsigned long order) +{ + struct page *page; + page = __get_pages(gfp_mask, order); + if (!page) + return 0; + return page_address(page); +} + +struct page * get_free_highpage(int gfp_mask) +{ + return __get_pages(gfp_mask, 0); } /* @@ -331,36 +367,32 @@ nopage: void show_free_areas(void) { unsigned long order, flags; - unsigned long total = 0; + unsigned type; - printk("Free pages: %6dkB (%6dkB BigMem)\n ( ", + spin_lock_irqsave(&page_alloc_lock, flags); + printk("Free pages: %6dkB (%6ldkB HighMem)\n", nr_free_pages<<(PAGE_SHIFT-10), - nr_free_bigpages<<(PAGE_SHIFT-10)); - printk("Free: %d, lru_cache: %d (%d %d %d)\n", + nr_free_highpages<<(PAGE_SHIFT-10)); + printk("( Free: %d, lru_cache: %d (%d %d %d) )\n", nr_free_pages, nr_lru_pages, freepages.min, freepages.low, freepages.high); - spin_lock_irqsave(&page_alloc_lock, flags); - for (order=0 ; order < NR_MEM_LISTS; order++) { - struct page * tmp; - unsigned long nr = 0; - for (tmp = free_area[order].next ; tmp != memory_head(free_area+order) ; tmp = tmp->next) { - nr ++; - } -#ifdef CONFIG_BIGMEM - for (tmp = free_area[BIGMEM_LISTS_OFFSET+order].next; - tmp != memory_head(free_area+BIGMEM_LISTS_OFFSET+order); - tmp = tmp->next) { - nr ++; + + for (type = 0; type < NR_MEM_TYPES; type++) { + unsigned long total = 0; + printk(" %s: ", mem_type_strs[type]); + for (order = 0; order < NR_MEM_LISTS; order++) { + unsigned long nr = free_area[type][order].count; + + total += nr * ((PAGE_SIZE>>10) << order); + printk("%lu*%lukB ", nr, (unsigned long)((PAGE_SIZE>>10) << order)); } -#endif - total += nr * ((PAGE_SIZE>>10) << order); - printk("%lu*%lukB ", nr, (unsigned long)((PAGE_SIZE>>10) << order)); + printk("= %lukB)\n", total); } spin_unlock_irqrestore(&page_alloc_lock, flags); - printk("= %lukB)\n", total); + #ifdef SWAP_CACHE_INFO show_swap_cache_info(); #endif @@ -374,11 +406,12 @@ void show_free_areas(void) * - mark all memory queues empty * - clear the memory bitmaps */ -unsigned long __init free_area_init(unsigned long start_mem, unsigned long end_mem) +volatile int data; +void __init free_area_init(unsigned long end_mem_pages) { mem_map_t * p; - unsigned long mask = PAGE_MASK; - unsigned long i; + unsigned long i, j; + unsigned long map_size; /* * Select nr of pages we try to keep free for important stuff @@ -387,7 +420,7 @@ unsigned long __init free_area_init(unsigned long start_mem, unsigned long end_m * This is fairly arbitrary, but based on some behaviour * analysis. */ - i = (end_mem - PAGE_OFFSET) >> (PAGE_SHIFT+7); + i = end_mem_pages >> 7; if (i < 10) i = 10; if (i > 256) @@ -395,36 +428,43 @@ unsigned long __init free_area_init(unsigned long start_mem, unsigned long end_m freepages.min = i; freepages.low = i * 2; freepages.high = i * 3; - mem_map = (mem_map_t *) LONG_ALIGN(start_mem); - p = mem_map + MAP_NR(end_mem); - start_mem = LONG_ALIGN((unsigned long) p); - memset(mem_map, 0, start_mem - (unsigned long) mem_map); - do { - --p; + + /* + * Most architectures just pick 'start_mem'. Some architectures + * (with lots of mem and discontinous memory maps) have to search + * for a good area. + */ + map_size = end_mem_pages*sizeof(struct page); + mem_map = (struct page *) alloc_bootmem(map_size); + memset(mem_map, 0, map_size); + + /* + * Initially all pages are reserved - free ones are freed + * up by free_all_bootmem() once the early boot process is + * done. + */ + for (p = mem_map; p < mem_map + end_mem_pages; p++) { set_page_count(p, 0); - p->flags = (1 << PG_DMA) | (1 << PG_reserved); + p->flags = (1 << PG_DMA); + SetPageReserved(p); init_waitqueue_head(&p->wait); - } while (p > mem_map); - - for (i = 0 ; i < NR_MEM_LISTS ; i++) { - unsigned long bitmap_size; - init_mem_queue(free_area+i); -#ifdef CONFIG_BIGMEM - init_mem_queue(free_area+BIGMEM_LISTS_OFFSET+i); -#endif - mask += mask; - end_mem = (end_mem + ~mask) & mask; - bitmap_size = (end_mem - PAGE_OFFSET) >> (PAGE_SHIFT + i); - bitmap_size = (bitmap_size + 7) >> 3; - bitmap_size = LONG_ALIGN(bitmap_size); - free_area[i].map = (unsigned int *) start_mem; - memset((void *) start_mem, 0, bitmap_size); - start_mem += bitmap_size; -#ifdef CONFIG_BIGMEM - free_area[BIGMEM_LISTS_OFFSET+i].map = (unsigned int *) start_mem; - memset((void *) start_mem, 0, bitmap_size); - start_mem += bitmap_size; -#endif + memlist_init(&p->list); + } + + for (j = 0 ; j < NR_MEM_TYPES ; j++) { + unsigned long mask = -1; + for (i = 0 ; i < NR_MEM_LISTS ; i++) { + unsigned long bitmap_size; + unsigned int * map; + memlist_init(&free_area[j][i].free_list); + mask += mask; + end_mem_pages = (end_mem_pages + ~mask) & mask; + bitmap_size = end_mem_pages >> i; + bitmap_size = (bitmap_size + 7) >> 3; + bitmap_size = LONG_ALIGN(bitmap_size); + map = (unsigned int *) alloc_bootmem(bitmap_size); + free_area[j][i].map = map; + memset((void *) map, 0, bitmap_size); + } } - return start_mem; } diff --git a/mm/page_io.c b/mm/page_io.c index c89416bf9..3ce1a186c 100644 --- a/mm/page_io.c +++ b/mm/page_io.c @@ -33,7 +33,7 @@ * that shared pages stay shared while being swapped. */ -static void rw_swap_page_base(int rw, unsigned long entry, struct page *page, int wait) +static int rw_swap_page_base(int rw, pte_t entry, struct page *page, int wait) { unsigned long type, offset; struct swap_info_struct * p; @@ -42,17 +42,10 @@ static void rw_swap_page_base(int rw, unsigned long entry, struct page *page, in kdev_t dev = 0; int block_size; -#ifdef DEBUG_SWAP - printk ("DebugVM: %s_swap_page entry %08lx, page %p (count %d), %s\n", - (rw == READ) ? "read" : "write", - entry, (char *) page_address(page), page_count(page), - wait ? "wait" : "nowait"); -#endif - type = SWP_TYPE(entry); if (type >= nr_swapfiles) { printk("Internal error: bad swap-device\n"); - return; + return 0; } /* Don't allow too many pending pages in flight.. */ @@ -63,23 +56,16 @@ static void rw_swap_page_base(int rw, unsigned long entry, struct page *page, in offset = SWP_OFFSET(entry); if (offset >= p->max) { printk("rw_swap_page: weirdness\n"); - return; + return 0; } if (p->swap_map && !p->swap_map[offset]) { - printk(KERN_ERR "rw_swap_page: " - "Trying to %s unallocated swap (%08lx)\n", - (rw == READ) ? "read" : "write", entry); - return; + pte_ERROR(entry); + return 0; } if (!(p->flags & SWP_USED)) { printk(KERN_ERR "rw_swap_page: " "Trying to swap to unused swap-device\n"); - return; - } - - if (!PageLocked(page)) { - printk(KERN_ERR "VM: swap page is unlocked\n"); - return; + return 0; } if (rw == READ) { @@ -104,13 +90,13 @@ static void rw_swap_page_base(int rw, unsigned long entry, struct page *page, in for (i=0, j=0; j< PAGE_SIZE ; i++, j += block_size) if (!(zones[i] = bmap(swapf,block++))) { printk("rw_swap_page: bad swap file\n"); - return; + return 0; } zones_used = i; dev = swapf->i_dev; } else { printk(KERN_ERR "rw_swap_page: no swap file or device\n"); - return; + return 0; } if (!wait) { set_bit(PG_decr_after, &page->flags); @@ -124,20 +110,15 @@ static void rw_swap_page_base(int rw, unsigned long entry, struct page *page, in * decrementing the page count, and unlocking the page in the * swap lock map - in the IO completion handler. */ - if (!wait) { - return; - } + if (!wait) + return 1; + wait_on_page(page); /* This shouldn't happen, but check to be sure. */ if (page_count(page) == 0) printk(KERN_ERR "rw_swap_page: page unused while waiting!\n"); -#ifdef DEBUG_SWAP - printk ("DebugVM: %s_swap_page finished on page %p (count %d)\n", - (rw == READ) ? "read" : "write", - (char *) page_address(page), - page_count(page)); -#endif + return 1; } /* @@ -149,7 +130,7 @@ static void rw_swap_page_base(int rw, unsigned long entry, struct page *page, in */ void rw_swap_page(int rw, struct page *page, int wait) { - unsigned long entry = page->offset; + pte_t entry = get_pagecache_pte(page); if (!PageLocked(page)) PAGE_BUG(page); @@ -157,7 +138,8 @@ void rw_swap_page(int rw, struct page *page, int wait) PAGE_BUG(page); if (page->inode != &swapper_inode) PAGE_BUG(page); - rw_swap_page_base(rw, entry, page, wait); + if (!rw_swap_page_base(rw, entry, page, wait)) + UnlockPage(page); } /* @@ -165,7 +147,7 @@ void rw_swap_page(int rw, struct page *page, int wait) * Therefore we can't use it. Later when we can remove the need for the * lock map and we can reduce the number of functions exported. */ -void rw_swap_page_nolock(int rw, unsigned long entry, char *buf, int wait) +void rw_swap_page_nolock(int rw, pte_t entry, char *buf, int wait) { struct page *page = mem_map + MAP_NR(buf); @@ -173,5 +155,6 @@ void rw_swap_page_nolock(int rw, unsigned long entry, char *buf, int wait) PAGE_BUG(page); if (PageSwapCache(page)) PAGE_BUG(page); - rw_swap_page_base(rw, entry, page, wait); + if (!rw_swap_page_base(rw, entry, page, wait)) + UnlockPage(page); } @@ -321,10 +321,10 @@ static int slab_break_gfp_order = SLAB_BREAK_GFP_ORDER_LO; * slab an obj belongs to. With kmalloc(), and kfree(), these are used * to find the cache which an obj belongs to. */ -#define SLAB_SET_PAGE_CACHE(pg, x) ((pg)->next = (struct page *)(x)) -#define SLAB_GET_PAGE_CACHE(pg) ((kmem_cache_t *)(pg)->next) -#define SLAB_SET_PAGE_SLAB(pg, x) ((pg)->prev = (struct page *)(x)) -#define SLAB_GET_PAGE_SLAB(pg) ((kmem_slab_t *)(pg)->prev) +#define SLAB_SET_PAGE_CACHE(pg,x) ((pg)->list.next = (struct list_head *)(x)) +#define SLAB_GET_PAGE_CACHE(pg) ((kmem_cache_t *)(pg)->list.next) +#define SLAB_SET_PAGE_SLAB(pg,x) ((pg)->list.prev = (struct list_head *)(x)) +#define SLAB_GET_PAGE_SLAB(pg) ((kmem_slab_t *)(pg)->list.prev) /* Size description struct for general caches. */ typedef struct cache_sizes { @@ -406,7 +406,7 @@ static kmem_cache_t *cache_slabp = NULL; static unsigned long bufctl_limit = 0; /* Initialisation - setup the `cache' cache. */ -long __init kmem_cache_init(long start, long end) +void __init kmem_cache_init(void) { size_t size, i; @@ -454,7 +454,6 @@ long __init kmem_cache_init(long start, long end) */ if (num_physpages > (32 << 20) >> PAGE_SHIFT) slab_break_gfp_order = SLAB_BREAK_GFP_ORDER_HI; - return start; } /* Initialisation - setup remaining internal and general caches. diff --git a/mm/swap_state.c b/mm/swap_state.c index 3b3a65a71..c3f6b271c 100644 --- a/mm/swap_state.c +++ b/mm/swap_state.c @@ -50,7 +50,10 @@ static struct inode_operations swapper_inode_operations = { NULL /* revalidate */ }; -struct inode swapper_inode = { i_op: &swapper_inode_operations }; +struct inode swapper_inode = { + i_op: &swapper_inode_operations, + i_pages: {&swapper_inode.i_pages,&swapper_inode.i_pages} +}; #ifdef SWAP_CACHE_INFO unsigned long swap_cache_add_total = 0; @@ -67,25 +70,16 @@ void show_swap_cache_info(void) } #endif -void add_to_swap_cache(struct page *page, unsigned long entry) +void add_to_swap_cache(struct page *page, pte_t entry) { #ifdef SWAP_CACHE_INFO swap_cache_add_total++; #endif -#ifdef DEBUG_SWAP - printk("DebugVM: add_to_swap_cache(%08lx count %d, entry %08lx)\n", - page_address(page), page_count(page), entry); -#endif - if (PageTestandSetSwapCache(page)) { - printk(KERN_ERR "swap_cache: replacing non-empty entry %08lx " - "on page %08lx\n", - page->offset, page_address(page)); - } - if (page->inode) { - printk(KERN_ERR "swap_cache: replacing page-cached entry " - "on page %08lx\n", page_address(page)); - } - add_to_page_cache(page, &swapper_inode, entry); + if (PageTestandSetSwapCache(page)) + BUG(); + if (page->inode) + BUG(); + add_to_page_cache(page, &swapper_inode, pte_val(entry)); } /* @@ -94,13 +88,13 @@ void add_to_swap_cache(struct page *page, unsigned long entry) * Note: if swap_map[] reaches SWAP_MAP_MAX the entries are treated as * "permanent", but will be reclaimed by the next swapoff. */ -int swap_duplicate(unsigned long entry) +int swap_duplicate(pte_t entry) { struct swap_info_struct * p; unsigned long offset, type; int result = 0; - if (!entry) + if (!pte_val(entry)) goto out; type = SWP_TYPE(entry); if (type & SHM_SWP_TYPE) @@ -121,41 +115,32 @@ int swap_duplicate(unsigned long entry) else { static int overflow = 0; if (overflow++ < 5) - printk(KERN_WARNING - "swap_duplicate: entry %08lx map count=%d\n", - entry, p->swap_map[offset]); + pte_ERROR(entry); p->swap_map[offset] = SWAP_MAP_MAX; } result = 1; -#ifdef DEBUG_SWAP - printk("DebugVM: swap_duplicate(entry %08lx, count now %d)\n", - entry, p->swap_map[offset]); -#endif out: return result; bad_file: - printk(KERN_ERR - "swap_duplicate: entry %08lx, nonexistent swap file\n", entry); + pte_ERROR(entry); goto out; bad_offset: - printk(KERN_ERR - "swap_duplicate: entry %08lx, offset exceeds max\n", entry); + pte_ERROR(entry); goto out; bad_unused: - printk(KERN_ERR - "swap_duplicate at %8p: entry %08lx, unused page\n", - __builtin_return_address(0), entry); + pte_ERROR(entry); goto out; } -int swap_count(unsigned long entry) +int swap_count(struct page *page) { struct swap_info_struct * p; unsigned long offset, type; + pte_t entry = get_pagecache_pte(page); int retval = 0; - if (!entry) + if (!pte_val(entry)) goto bad_entry; type = SWP_TYPE(entry); if (type & SHM_SWP_TYPE) @@ -169,10 +154,6 @@ int swap_count(unsigned long entry) if (!p->swap_map[offset]) goto bad_unused; retval = p->swap_map[offset]; -#ifdef DEBUG_SWAP - printk("DebugVM: swap_count(entry %08lx, count %d)\n", - entry, retval); -#endif out: return retval; @@ -180,17 +161,13 @@ bad_entry: printk(KERN_ERR "swap_count: null entry!\n"); goto out; bad_file: - printk(KERN_ERR - "swap_count: entry %08lx, nonexistent swap file!\n", entry); + pte_ERROR(entry); goto out; bad_offset: - printk(KERN_ERR - "swap_count: entry %08lx, offset exceeds max!\n", entry); + pte_ERROR(entry); goto out; bad_unused: - printk(KERN_ERR - "swap_count at %8p: entry %08lx, unused page!\n", - __builtin_return_address(0), entry); + pte_ERROR(entry); goto out; } @@ -198,22 +175,13 @@ static inline void remove_from_swap_cache(struct page *page) { struct inode *inode = page->inode; - if (!inode) { - printk ("VM: Removing swap cache page with zero inode hash " - "on page %08lx\n", page_address(page)); - return; - } - if (inode != &swapper_inode) { - printk ("VM: Removing swap cache page with wrong inode hash " - "on page %08lx\n", page_address(page)); - } + if (!inode) + BUG(); + if (inode != &swapper_inode) + BUG(); if (!PageSwapCache(page)) PAGE_BUG(page); -#ifdef DEBUG_SWAP - printk("DebugVM: remove_from_swap_cache(%08lx count %d)\n", - page_address(page), page_count(page)); -#endif PageClearSwapCache(page); remove_inode_page(page); } @@ -224,19 +192,14 @@ static inline void remove_from_swap_cache(struct page *page) */ void __delete_from_swap_cache(struct page *page) { - long entry = page->offset; + pte_t entry = get_pagecache_pte(page); #ifdef SWAP_CACHE_INFO swap_cache_del_total++; #endif -#ifdef DEBUG_SWAP - printk("DebugVM: delete_from_swap_cache(%08lx count %d, " - "entry %08lx)\n", - page_address(page), page_count(page), entry); -#endif - remove_from_swap_cache (page); + remove_from_swap_cache(page); lock_kernel(); - swap_free (entry); + swap_free(entry); unlock_kernel(); } @@ -268,10 +231,8 @@ void delete_from_swap_cache(struct page *page) * this page if it is the last user of the page. */ -void free_page_and_swap_cache(unsigned long addr) +void free_page_and_swap_cache(struct page *page) { - struct page *page = mem_map + MAP_NR(addr); - /* * If we are the only user, then free up the swap cache. */ @@ -295,7 +256,7 @@ void free_page_and_swap_cache(unsigned long addr) * lock before returning. */ -struct page * lookup_swap_cache(unsigned long entry) +struct page * lookup_swap_cache(pte_t entry) { struct page *found; @@ -303,7 +264,10 @@ struct page * lookup_swap_cache(unsigned long entry) swap_cache_find_total++; #endif while (1) { - found = find_lock_page(&swapper_inode, entry); + /* + * Right now the pagecache is 32-bit only. + */ + found = find_lock_page(&swapper_inode, pte_val(entry)); if (!found) return 0; if (found->inode != &swapper_inode || !PageSwapCache(found)) @@ -331,15 +295,11 @@ out_bad: * the swap entry is no longer in use. */ -struct page * read_swap_cache_async(unsigned long entry, int wait) +struct page * read_swap_cache_async(pte_t entry, int wait) { struct page *found_page = 0, *new_page; unsigned long new_page_addr; -#ifdef DEBUG_SWAP - printk("DebugVM: read_swap_cache_async entry %08lx%s\n", - entry, wait ? ", wait" : ""); -#endif /* * Make sure the swap entry is still in use. */ @@ -368,11 +328,6 @@ struct page * read_swap_cache_async(unsigned long entry, int wait) */ add_to_swap_cache(new_page, entry); rw_swap_page(READ, new_page, wait); -#ifdef DEBUG_SWAP - printk("DebugVM: read_swap_cache_async created " - "entry %08lx at %p\n", - entry, (char *) page_address(new_page)); -#endif return new_page; out_free_page: diff --git a/mm/swapfile.c b/mm/swapfile.c index c4ce5377d..76aea7b7e 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -81,17 +81,18 @@ static inline int scan_swap_map(struct swap_info_struct *si) return 0; } -unsigned long get_swap_page(void) +pte_t get_swap_page(void) { struct swap_info_struct * p; - unsigned long offset, entry; + unsigned long offset; + pte_t entry = __pte(0); int type, wrapped = 0; type = swap_list.next; if (type < 0) - return 0; + goto out; if (nr_swap_pages == 0) - return 0; + goto out; while (1) { p = &swap_info[type]; @@ -106,7 +107,7 @@ unsigned long get_swap_page(void) } else { swap_list.next = type; } - return entry; + goto out; } } type = p->next; @@ -115,19 +116,21 @@ unsigned long get_swap_page(void) type = swap_list.head; wrapped = 1; } - } else if (type < 0) { - return 0; /* out of swap space */ - } + } else + if (type < 0) + goto out; /* out of swap space */ } +out: + return entry; } -void swap_free(unsigned long entry) +void swap_free(pte_t entry) { struct swap_info_struct * p; unsigned long offset, type; - if (!entry) + if (!pte_val(entry)) goto out; type = SWP_TYPE(entry); @@ -154,10 +157,6 @@ void swap_free(unsigned long entry) nr_swap_pages++; } } -#ifdef DEBUG_SWAP - printk("DebugVM: swap_free(entry %08lx, count now %d)\n", - entry, p->swap_map[offset]); -#endif out: return; @@ -171,24 +170,24 @@ bad_offset: printk("swap_free: offset exceeds max\n"); goto out; bad_free: - printk("swap_free: swap-space map bad (entry %08lx)\n",entry); + pte_ERROR(entry); goto out; } /* needs the big kernel lock */ -unsigned long acquire_swap_entry(struct page *page) +pte_t acquire_swap_entry(struct page *page) { struct swap_info_struct * p; unsigned long offset, type; - unsigned long entry; + pte_t entry; if (!test_bit(PG_swap_entry, &page->flags)) goto new_swap_entry; /* We have the old entry in the page offset still */ - entry = page->offset; - if (!entry) + if (!page->offset) goto new_swap_entry; + entry = get_pagecache_pte(page); type = SWP_TYPE(entry); if (type & SHM_SWP_TYPE) goto new_swap_entry; @@ -223,7 +222,7 @@ new_swap_entry: * what to do if a write is requested later. */ static inline void unuse_pte(struct vm_area_struct * vma, unsigned long address, - pte_t *dir, unsigned long entry, unsigned long page) + pte_t *dir, pte_t entry, struct page* page) { pte_t pte = *dir; @@ -239,7 +238,7 @@ static inline void unuse_pte(struct vm_area_struct * vma, unsigned long address, set_pte(dir, pte_mkdirty(pte)); return; } - if (pte_val(pte) != entry) + if (pte_val(pte) != pte_val(entry)) return; set_pte(dir, pte_mkdirty(mk_pte(page, vma->vm_page_prot))); swap_free(entry); @@ -249,7 +248,7 @@ static inline void unuse_pte(struct vm_area_struct * vma, unsigned long address, static inline void unuse_pmd(struct vm_area_struct * vma, pmd_t *dir, unsigned long address, unsigned long size, unsigned long offset, - unsigned long entry, unsigned long page) + pte_t entry, struct page* page) { pte_t * pte; unsigned long end; @@ -257,7 +256,7 @@ static inline void unuse_pmd(struct vm_area_struct * vma, pmd_t *dir, if (pmd_none(*dir)) return; if (pmd_bad(*dir)) { - printk("unuse_pmd: bad pmd (%08lx)\n", pmd_val(*dir)); + pmd_ERROR(*dir); pmd_clear(dir); return; } @@ -271,12 +270,12 @@ static inline void unuse_pmd(struct vm_area_struct * vma, pmd_t *dir, unuse_pte(vma, offset+address-vma->vm_start, pte, entry, page); address += PAGE_SIZE; pte++; - } while (address < end); + } while (address && (address < end)); } static inline void unuse_pgd(struct vm_area_struct * vma, pgd_t *dir, unsigned long address, unsigned long size, - unsigned long entry, unsigned long page) + pte_t entry, struct page* page) { pmd_t * pmd; unsigned long offset, end; @@ -284,7 +283,7 @@ static inline void unuse_pgd(struct vm_area_struct * vma, pgd_t *dir, if (pgd_none(*dir)) return; if (pgd_bad(*dir)) { - printk("unuse_pgd: bad pgd (%08lx)\n", pgd_val(*dir)); + pgd_ERROR(*dir); pgd_clear(dir); return; } @@ -294,28 +293,32 @@ static inline void unuse_pgd(struct vm_area_struct * vma, pgd_t *dir, end = address + size; if (end > PGDIR_SIZE) end = PGDIR_SIZE; + if (address >= end) + BUG(); do { unuse_pmd(vma, pmd, address, end - address, offset, entry, page); address = (address + PMD_SIZE) & PMD_MASK; pmd++; - } while (address < end); + } while (address && (address < end)); } static void unuse_vma(struct vm_area_struct * vma, pgd_t *pgdir, - unsigned long entry, unsigned long page) + pte_t entry, struct page* page) { unsigned long start = vma->vm_start, end = vma->vm_end; - while (start < end) { + if (start >= end) + BUG(); + do { unuse_pgd(vma, pgdir, start, end - start, entry, page); start = (start + PGDIR_SIZE) & PGDIR_MASK; pgdir++; - } + } while (start && (start < end)); } -static void unuse_process(struct mm_struct * mm, unsigned long entry, - unsigned long page) +static void unuse_process(struct mm_struct * mm, + pte_t entry, struct page* page) { struct vm_area_struct* vma; @@ -340,8 +343,8 @@ static int try_to_unuse(unsigned int type) { struct swap_info_struct * si = &swap_info[type]; struct task_struct *p; - struct page *page_map; - unsigned long entry, page; + struct page *page; + pte_t entry; int i; while (1) { @@ -361,8 +364,8 @@ static int try_to_unuse(unsigned int type) /* Get a page for the entry, using the existing swap cache page if there is one. Otherwise, get a clean page and read the swap into it. */ - page_map = read_swap_cache(entry); - if (!page_map) { + page = read_swap_cache(entry); + if (!page) { /* * Continue searching if the entry became unused. */ @@ -370,7 +373,6 @@ static int try_to_unuse(unsigned int type) continue; return -ENOMEM; } - page = page_address(page_map); read_lock(&tasklist_lock); for_each_task(p) unuse_process(p->mm, entry, page); @@ -378,17 +380,15 @@ static int try_to_unuse(unsigned int type) shm_unuse(entry, page); /* Now get rid of the extra reference to the temporary page we've been using. */ - if (PageSwapCache(page_map)) - delete_from_swap_cache(page_map); - __free_page(page_map); + if (PageSwapCache(page)) + delete_from_swap_cache(page); + __free_page(page); /* * Check for and clear any overflowed swap map counts. */ if (si->swap_map[i] != 0) { if (si->swap_map[i] != SWAP_MAP_MAX) - printk(KERN_ERR - "try_to_unuse: entry %08lx count=%d\n", - entry, si->swap_map[i]); + pte_ERROR(entry); si->swap_map[i] = 0; nr_swap_pages++; } diff --git a/mm/vmalloc.c b/mm/vmalloc.c index 9bd4142c3..1c436af20 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -20,7 +20,7 @@ static inline void free_area_pte(pmd_t * pmd, unsigned long address, unsigned lo if (pmd_none(*pmd)) return; if (pmd_bad(*pmd)) { - printk("free_area_pte: bad pmd (%08lx)\n", pmd_val(*pmd)); + pmd_ERROR(*pmd); pmd_clear(pmd); return; } @@ -29,7 +29,7 @@ static inline void free_area_pte(pmd_t * pmd, unsigned long address, unsigned lo end = address + size; if (end > PMD_SIZE) end = PMD_SIZE; - while (address < end) { + do { pte_t page = *pte; pte_clear(pte); address += PAGE_SIZE; @@ -37,11 +37,13 @@ static inline void free_area_pte(pmd_t * pmd, unsigned long address, unsigned lo if (pte_none(page)) continue; if (pte_present(page)) { - free_page(pte_page(page)); + unsigned long map_nr = pte_pagenr(page); + if (map_nr < max_mapnr) + __free_page(mem_map + map_nr); continue; } printk("Whee.. Swapped out page in kernel page table\n"); - } + } while (address < end); } static inline void free_area_pmd(pgd_t * dir, unsigned long address, unsigned long size) @@ -52,7 +54,7 @@ static inline void free_area_pmd(pgd_t * dir, unsigned long address, unsigned lo if (pgd_none(*dir)) return; if (pgd_bad(*dir)) { - printk("free_area_pmd: bad pgd (%08lx)\n", pgd_val(*dir)); + pgd_ERROR(*dir); pgd_clear(dir); return; } @@ -61,11 +63,11 @@ static inline void free_area_pmd(pgd_t * dir, unsigned long address, unsigned lo end = address + size; if (end > PGDIR_SIZE) end = PGDIR_SIZE; - while (address < end) { + do { free_area_pte(pmd, address, end - address); address = (address + PMD_SIZE) & PMD_MASK; pmd++; - } + } while (address < end); } void vmfree_area_pages(unsigned long address, unsigned long size) @@ -75,11 +77,11 @@ void vmfree_area_pages(unsigned long address, unsigned long size) dir = pgd_offset_k(address); flush_cache_all(); - while (address < end) { + do { free_area_pmd(dir, address, end - address); address = (address + PGDIR_SIZE) & PGDIR_MASK; dir++; - } + } while (address && (address < end)); flush_tlb_all(); } @@ -91,17 +93,17 @@ static inline int alloc_area_pte(pte_t * pte, unsigned long address, unsigned lo end = address + size; if (end > PMD_SIZE) end = PMD_SIZE; - while (address < end) { - unsigned long page; + do { + struct page * page; if (!pte_none(*pte)) printk("alloc_area_pte: page already exists\n"); - page = __get_free_page(GFP_KERNEL|GFP_BIGMEM); + page = get_free_highpage(GFP_KERNEL|__GFP_HIGHMEM); if (!page) return -ENOMEM; set_pte(pte, mk_pte(page, prot)); address += PAGE_SIZE; pte++; - } + } while (address < end); return 0; } @@ -113,7 +115,7 @@ static inline int alloc_area_pmd(pmd_t * pmd, unsigned long address, unsigned lo end = address + size; if (end > PGDIR_SIZE) end = PGDIR_SIZE; - while (address < end) { + do { pte_t * pte = pte_alloc_kernel(pmd, address); if (!pte) return -ENOMEM; @@ -121,7 +123,7 @@ static inline int alloc_area_pmd(pmd_t * pmd, unsigned long address, unsigned lo return -ENOMEM; address = (address + PMD_SIZE) & PMD_MASK; pmd++; - } + } while (address < end); return 0; } @@ -132,7 +134,7 @@ int vmalloc_area_pages(unsigned long address, unsigned long size, pgprot_t prot) dir = pgd_offset_k(address); flush_cache_all(); - while (address < end) { + do { pmd_t *pmd; pgd_t olddir = *dir; @@ -145,7 +147,7 @@ int vmalloc_area_pages(unsigned long address, unsigned long size, pgprot_t prot) set_pgdir(address, *dir); address = (address + PGDIR_SIZE) & PGDIR_MASK; dir++; - } + } while (address && (address < end)); flush_tlb_all(); return 0; } @@ -202,14 +204,19 @@ void * vmalloc_prot(unsigned long size, pgprot_t prot) struct vm_struct *area; size = PAGE_ALIGN(size); - if (!size || size > (max_mapnr << PAGE_SHIFT)) + if (!size || size > (max_mapnr << PAGE_SHIFT)) { + BUG(); return NULL; + } area = get_vm_area(size); - if (!area) + if (!area) { + BUG(); return NULL; + } addr = area->addr; if (vmalloc_area_pages(VMALLOC_VMADDR(addr), size, prot)) { vfree(addr); + BUG(); return NULL; } return addr; diff --git a/mm/vmscan.c b/mm/vmscan.c index 1ce37062b..83d987a9f 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -17,7 +17,7 @@ #include <linux/smp_lock.h> #include <linux/pagemap.h> #include <linux/init.h> -#include <linux/bigmem.h> +#include <linux/highmem.h> #include <asm/pgtable.h> @@ -34,23 +34,16 @@ */ static int try_to_swap_out(struct vm_area_struct* vma, unsigned long address, pte_t * page_table, int gfp_mask) { - pte_t pte; - unsigned long entry; - unsigned long page_addr; + pte_t pte, entry; struct page * page; pte = *page_table; if (!pte_present(pte)) goto out_failed; - page_addr = pte_page(pte); - if (MAP_NR(page_addr) >= max_mapnr) + page = pte_page(pte); + if (page-mem_map >= max_mapnr) goto out_failed; - page = mem_map + MAP_NR(page_addr); - spin_lock(&vma->vm_mm->page_table_lock); - if (pte_val(pte) != pte_val(*page_table)) - goto out_failed_unlock; - /* Don't look at this pte if it's been accessed recently. */ if (pte_young(pte)) { /* @@ -59,14 +52,14 @@ static int try_to_swap_out(struct vm_area_struct* vma, unsigned long address, pt */ set_pte(page_table, pte_mkold(pte)); set_bit(PG_referenced, &page->flags); - goto out_failed_unlock; + goto out_failed; } if (PageReserved(page) || PageLocked(page) || ((gfp_mask & __GFP_DMA) && !PageDMA(page)) - || (!(gfp_mask & __GFP_BIGMEM) && PageBIGMEM(page))) - goto out_failed_unlock; + || (!(gfp_mask & __GFP_HIGHMEM) && PageHighMem(page))) + goto out_failed; /* * Is the page already in the swap cache? If so, then @@ -77,14 +70,14 @@ static int try_to_swap_out(struct vm_area_struct* vma, unsigned long address, pt * memory, and we should just continue our scan. */ if (PageSwapCache(page)) { - entry = page->offset; + entry = get_pagecache_pte(page); swap_duplicate(entry); - set_pte(page_table, __pte(entry)); + set_pte(page_table, entry); drop_pte: vma->vm_mm->rss--; flush_tlb_page(vma, address); __free_page(page); - goto out_failed_unlock; + goto out_failed; } /* @@ -111,7 +104,7 @@ drop_pte: * locks etc. */ if (!(gfp_mask & __GFP_IO)) - goto out_failed_unlock; + goto out_failed; /* * Ok, it's really dirty. That means that @@ -136,9 +129,9 @@ drop_pte: if (vma->vm_ops && vma->vm_ops->swapout) { int error; pte_clear(page_table); - spin_unlock(&vma->vm_mm->page_table_lock); - flush_tlb_page(vma, address); vma->vm_mm->rss--; + flush_tlb_page(vma, address); + vmlist_access_unlock(vma->vm_mm); error = vma->vm_ops->swapout(vma, page); if (!error) goto out_free_success; @@ -153,15 +146,15 @@ drop_pte: * page with that swap entry. */ entry = acquire_swap_entry(page); - if (!entry) - goto out_failed_unlock; /* No swap space left */ + if (!pte_val(entry)) + goto out_failed; /* No swap space left */ - if (!(page = prepare_bigmem_swapout(page))) - goto out_swap_free_unlock; + if (!(page = prepare_highmem_swapout(page))) + goto out_swap_free; vma->vm_mm->rss--; - set_pte(page_table, __pte(entry)); - spin_unlock(&vma->vm_mm->page_table_lock); + set_pte(page_table, entry); + vmlist_access_unlock(vma->vm_mm); flush_tlb_page(vma, address); swap_duplicate(entry); /* One for the process, one for the swap cache */ @@ -175,13 +168,9 @@ drop_pte: out_free_success: __free_page(page); return 1; -out_failed_unlock: - spin_unlock(&vma->vm_mm->page_table_lock); -out_failed: - return 0; -out_swap_free_unlock: +out_swap_free: swap_free(entry); - spin_unlock(&vma->vm_mm->page_table_lock); +out_failed: return 0; } @@ -208,7 +197,7 @@ static inline int swap_out_pmd(struct vm_area_struct * vma, pmd_t *dir, unsigned if (pmd_none(*dir)) return 0; if (pmd_bad(*dir)) { - printk("swap_out_pmd: bad pmd (%08lx)\n", pmd_val(*dir)); + pmd_ERROR(*dir); pmd_clear(dir); return 0; } @@ -227,7 +216,7 @@ static inline int swap_out_pmd(struct vm_area_struct * vma, pmd_t *dir, unsigned return result; address += PAGE_SIZE; pte++; - } while (address < end); + } while (address && (address < end)); return 0; } @@ -239,7 +228,7 @@ static inline int swap_out_pgd(struct vm_area_struct * vma, pgd_t *dir, unsigned if (pgd_none(*dir)) return 0; if (pgd_bad(*dir)) { - printk("swap_out_pgd: bad pgd (%08lx)\n", pgd_val(*dir)); + pgd_ERROR(*dir); pgd_clear(dir); return 0; } @@ -247,7 +236,7 @@ static inline int swap_out_pgd(struct vm_area_struct * vma, pgd_t *dir, unsigned pmd = pmd_offset(dir, address); pgd_end = (address + PGDIR_SIZE) & PGDIR_MASK; - if (end > pgd_end) + if (pgd_end && (end > pgd_end)) end = pgd_end; do { @@ -256,7 +245,7 @@ static inline int swap_out_pgd(struct vm_area_struct * vma, pgd_t *dir, unsigned return result; address = (address + PMD_SIZE) & PMD_MASK; pmd++; - } while (address < end); + } while (address && (address < end)); return 0; } @@ -272,13 +261,15 @@ static int swap_out_vma(struct vm_area_struct * vma, unsigned long address, int pgdir = pgd_offset(vma->vm_mm, address); end = vma->vm_end; - while (address < end) { + if (address >= end) + BUG(); + do { int result = swap_out_pgd(vma, pgdir, address, end, gfp_mask); if (result) return result; address = (address + PGDIR_SIZE) & PGDIR_MASK; pgdir++; - } + } while (address && (address < end)); return 0; } @@ -293,8 +284,10 @@ static int swap_out_mm(struct mm_struct * mm, int gfp_mask) address = mm->swap_address; /* - * Find the proper vm-area + * Find the proper vm-area after freezing the vma chain + * and ptes. */ + vmlist_access_lock(mm); vma = find_vma(mm, address); if (vma) { if (address < vma->vm_start) @@ -310,6 +303,7 @@ static int swap_out_mm(struct mm_struct * mm, int gfp_mask) address = vma->vm_start; } } + vmlist_access_unlock(mm); /* We didn't find anything for the process */ mm->swap_cnt = 0; @@ -502,8 +496,8 @@ int kswapd(void *unused) */ do { /* kswapd is critical to provide GFP_ATOMIC - allocations (not GFP_BIGMEM ones). */ - if (nr_free_pages - nr_free_bigpages >= freepages.high) + allocations (not GFP_HIGHMEM ones). */ + if (nr_free_pages - nr_free_highpages >= freepages.high) break; if (!do_try_to_free_pages(GFP_KSWAPD)) diff --git a/net/core/dst.c b/net/core/dst.c index 990d86682..50b160a4c 100644 --- a/net/core/dst.c +++ b/net/core/dst.c @@ -5,7 +5,6 @@ * */ -#include <asm/segment.h> #include <asm/system.h> #include <asm/bitops.h> #include <linux/types.h> diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 58aeb6cc9..7c8a332e2 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -237,8 +237,10 @@ void __kfree_skb(struct sk_buff *skb) dst_release(skb->dst); if(skb->destructor) skb->destructor(skb); +#ifdef CONFIG_NET if(skb->rx_dev) dev_put(skb->rx_dev); +#endif skb_headerinit(skb, NULL, 0); /* clean state */ kfree_skbmem(skb); } diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 986868b4f..87ec2d773 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -59,8 +59,6 @@ #include <net/ipv6.h> #include <net/inet_common.h> -#include <asm/segment.h> - #include <linux/inet.h> #include <linux/stddef.h> diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 6ffcc187b..e6df7ac76 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -459,6 +459,7 @@ call_encode(struct rpc_task *task) req->rq_rvec[0].iov_len = bufsiz; req->rq_rlen = bufsiz; req->rq_rnr = 1; + req->rq_damaged = 0; if (task->tk_proc > clnt->cl_maxproc) { printk(KERN_WARNING "%s (vers %d): bad procedure number %d\n", @@ -467,6 +468,9 @@ call_encode(struct rpc_task *task) return; } + /* Zero buffer so we have automatic zero-padding of opaque & string */ + memset(task->tk_buffer, 0, bufsiz); + /* Encode header and provided arguments */ encode = rpcproc_encode(clnt, task->tk_proc); if (!(p = call_header(task))) { diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index 181a7e46c..76c28d7cc 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -75,6 +75,18 @@ static u32 swap_buffer[PAGE_SIZE >> 2]; static int swap_buffer_used = 0; /* + * Make allocation of the swap_buffer SMP-safe + */ +static __inline__ int rpc_lock_swapbuf(void) +{ + return !test_and_set_bit(1, &swap_buffer_used); +} +static __inline__ void rpc_unlock_swapbuf(void) +{ + clear_bit(1, &swap_buffer_used); +} + +/* * Spinlock for wait queues. Access to the latter also has to be * interrupt-safe in order to allow timers to wake up sleeping tasks. */ @@ -614,7 +626,8 @@ rpc_allocate(unsigned int flags, unsigned int size) dprintk("RPC: allocated buffer %p\n", buffer); return buffer; } - if ((flags & RPC_TASK_SWAPPER) && !swap_buffer_used++) { + if ((flags & RPC_TASK_SWAPPER) && size <= sizeof(swap_buffer) + && rpc_lock_swapbuf()) { dprintk("RPC: used last-ditch swap buffer\n"); return swap_buffer; } @@ -634,7 +647,7 @@ rpc_free(void *buffer) kfree(buffer); return; } - swap_buffer_used = 0; + rpc_unlock_swapbuf(); } /* |