diff options
| author | Ralf Bächle <ralf@linux-mips.org> | 2003-06-22 22:07:17 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@home.transmeta.com> | 2003-06-22 22:07:17 -0700 |
| commit | f6d64aeef94909c98a054590af61906d2ce5acbf (patch) | |
| tree | 38b4b00a94874a5da38eae4c81ab14bcf3b237e6 | |
| parent | 2e7f53ec14475d56559bcdd07acfb737b7bff1e9 (diff) | |
[PATCH] MIPS merge, generic mips bits.
This contains all the generic 32-bit MIPS code, so all arch/mips/ and
include/asm-mips/ stuff.
274 files changed, 22774 insertions, 20746 deletions
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index f79d1fb79351..a4a3d6556f30 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -6,1113 +6,12 @@ config MIPS bool default y -config MMU +config MIPS32 bool default y -config SMP +config MIPS64 bool - ---help--- - This enables support for systems with more than one CPU. If you have - a system with only one CPU, like most personal computers, say N. If - you have a system with more than one CPU, say Y. - - If you say N here, the kernel will run on single and multiprocessor - machines, but will use only one CPU of a multiprocessor machine. If - you say Y here, the kernel will run on many, but not all, - singleprocessor machines. On a singleprocessor machine, the kernel - will run faster if you say N here. - - Note that if you say Y here and choose architecture "586" or - "Pentium" under "Processor family", the kernel will not work on 486 - architectures. Similarly, multiprocessor kernels for the "PPro" - architecture may not work on all Pentium based boards. - - People using multiprocessor machines who say Y here should also say - Y to "Enhanced Real Time Clock Support", below. The "Advanced Power - Management" code will be disabled if you say Y here. - - See also the <file:Documentation/smp.tex>, - <file:Documentation/smp.txt>, <file:Documentation/i386/IO-APIC.txt>, - <file:Documentation/nmi_watchdog.txt> and the SMP-HOWTO available at - <http://www.tldp.org/docs.html#howto>. - - If you don't know what to do here, say N. - -config GENERIC_ISA_DMA - bool - default y - - -mainmenu "Linux Kernel Configuration" - -source "init/Kconfig" - - -menu "Machine selection" - -config ACER_PICA_61 - bool "Support for Acer PICA 1 chipset (EXPERIMENTAL)" - depends on EXPERIMENTAL - help - This is a machine with a R4400 133/150 MHz CPU. To compile a Linux - kernel that runs on these, say Y here. For details about Linux on - the MIPS architecture, check out the Linux/MIPS FAQ on the WWW at - <http://oss.sgi.com/mips/>. - -config ALGOR_P4032 - bool "Support for Algorithmics P4032 (EXPERIMENTAL)" - depends on EXPERIMENTAL - help - This is an evaluation board of the British company Algorithmics. - The board uses the R4300 and a R5230 CPUs. For more information - about this board see <http://www.algor.co.uk/>. - -config BAGET_MIPS - bool "Support for BAGET MIPS series (EXPERIMENTAL)" - depends on EXPERIMENTAL - help - This enables support for the Baget, a Russian embedded system. For - more details about the Baget see the Linux/MIPS FAQ on - <http://oss.sgi.com/mips/>. - -config DECSTATION - bool "Support for DECstations (EXPERIMENTAL)" - depends on EXPERIMENTAL - ---help--- - This enables support for DEC's MIPS based workstations. For details - see the Linux/MIPS FAQ on <http://oss.sgi.com/mips/> and the - DECstation porting pages on <http://decstation.unix-ag.org/>. - - If you have one of the following DECstation Models you definitely - want to choose R4xx0 for the CPU Type: - - DECstation 5000/50 - DECstation 5000/150 - DECstation 5000/260 - DECsystem 5900/260 - - otherwise choose R3000. - -config DDB5074 - bool "Support for NEC DDB Vrc-5074 (EXPERIMENTAL)" - depends on EXPERIMENTAL - help - This enables support for the VR5000-based NEC DDB Vrc-5074 - evaluation board. - -config MIPS_EV96100 - bool "Support for Galileo EV96100 Evaluation board (EXPERIMENTAL)" - depends on EXPERIMENTAL - help - This is an evaluation board based on the Galielo GT-96100 LAN/WAN - communications controllers containing a MIPS R5000 compatible core - running at 83MHz. Their website is <http://www.galileot.com/>. Say Y - here if you wish to build a kernel for this platform. - -config MIPS_EV64120 - bool "Support for Galileo EV64120 Evaluation board (EXPERIMENTAL)" - depends on EXPERIMENTAL - help - This is an evaluation board based on the Galileo GT-64120 - single-chip system controller that contains a MIPS R5000 compatible - core running at 75/100MHz. Their website is located at - <http://www.galileot.com/>. Say Y here if you wish to build a - kernel for this platform. - -config EVB_PCI1 - bool "Enable Second PCI (PCI1)" - depends on MIPS_EV64120 - -choice - prompt "Galileo Chip Clock" - depends on MIPS_EV64120 - -config SYSCLK_75 - bool "75" - -config SYSCLK_83 - bool "83.3" - -config SYSCLK_100_1 - bool "100" - -endchoice - -config MIPS_ATLAS - bool "Support for MIPS Atlas board (EXPERIMENTAL)" - depends on EXPERIMENTAL - help - This enables support for the QED R5231-based MIPS Atlas evaluation - board. - -config MIPS_MALTA - bool "Support for MIPS Malta board (EXPERIMENTAL)" - depends on EXPERIMENTAL - help - This enables support for the VR5000-based MIPS Malta evaluation - board. - -config NINO - bool "Support for Philips Nino (EXPERIMENTAL)" - depends on EXPERIMENTAL - help - Say Y here to select a kernel for the Philips Nino Palm PC. The - website at <http://www.realitydiluted.com/projects/nino/index.html> - will have more information. - -choice - prompt "Nino Model Number" - depends on NINO - -config NINO_4MB - bool "Model-300/301/302/319" - help - Say Y here to build a kernel specifically for Nino Palm PCs with - 4MB of memory. These include models 300/301/302/319. - -config NINO_8MB - bool "Model-200/210/312/320/325/350/390" - help - Say Y here to build a kernel specifically for Nino Palm PCs with - 8MB of memory. These include models 200/210/312/320/325/350/390. - -config NINO_16MB - bool "Model-500/510" - help - Say Y here to build a kernel specifically for Nino 500/501 color - Palm PCs from Philips (INCOMPLETE). - -endchoice - -config MIPS_MAGNUM_4000 - bool "Support for Mips Magnum 4000" - help - This is a machine with a R4000 100 MHz CPU. To compile a Linux - kernel that runs on these, say Y here. For details about Linux on - the MIPS architecture, check out the Linux/MIPS FAQ on the WWW at - <http://oss.sgi.com/mips/>. - -config MOMENCO_OCELOT - bool "Support for Momentum Ocelot board" - help - The Ocelot is a MIPS-based Single Board Computer (SBC) made by - Momentum Computer <http://www.momenco.com/>. - -config DDB5476 - bool "Support for NEC DDB Vrc-5476" - help - This enables support for the R5432-based NEC DDB Vrc-5476 - evaluation board. - - Features : kernel debugging, serial terminal, NFS root fs, on-board - ether port (Need an additional patch at <http://linux.junsun.net/>), - USB, AC97, PCI, PCI VGA card & framebuffer console, IDE controller, - PS2 keyboard, PS2 mouse, etc. - -config DDB5477 - bool "Support for NEC DDB Vrc-5477" - help - This enables support for the R5432-based NEC DDB Vrc-5477 - evaluation board. - - Features : kernel debugging, serial terminal, NFS root fs, on-board - ether port (Need an additional patch at <http://linux.junsun.net/>), - USB, AC97, PCI, etc. - -config OLIVETTI_M700 - bool "Support for Olivetti M700-10" - help - This is a machine with a R4000 100 MHz CPU. To compile a Linux - kernel that runs on these, say Y here. For details about Linux on - the MIPS architecture, check out the Linux/MIPS FAQ on the WWW at - <http://oss.sgi.com/mips/>. - -config SGI_IP22 - bool "Support for SGI IP22" - help - This are the SGI Indy, Challenge S and Indigo2, as well as certain - OEM variants like the Tandem CMN B006S. To compile a Linux kernel - that runs on these, say Y here. - -config SNI_RM200_PCI - bool "Support for SNI RM200 PCI" - help - The SNI RM200 PCI was a MIPS-based platform manufactured by Siemens - Nixdorf Informationssysteme (SNI), parent company of Pyramid - Technology and now in turn merged with Fujitsu. Say Y here to - support this machine type. - -config MIPS_ITE8172 - bool "Support for ITE 8172G board" - help - Ths is an evaluation board made by ITE <http://www.ite.com.tw/> - with ATX form factor that utilizes a MIPS R5000 to work with its - ITE8172G companion internet appliance chip. The MIPS core can be - either a NEC Vr5432 or QED RM5231. Say Y here if you wish to build - a kernel for this platform. - -config IT8172_REVC - bool "Support for older IT8172 (Rev C)" - depends on MIPS_ITE8172 - help - Say Y here to support the older, Revision C version of the Integrated - Technology Express, Inc. ITE8172 SBC. Vendor page at - <http://www.ite.com.tw/ia/brief_it8172bsp.htm>; picture of the - board at <http://www.mvista.com/allies/semiconductor/ite.html>. - -config QTRONIX_KEYBOARD - bool "Enable Qtronix 990P Keyboard Support" - depends on MIPS_IVR || MIPS_ITE8172 - help - Images of Qtronix keyboards are at - <http://www.qtronix.com/keyboard.html>. - -config IT8172_CIR - bool - depends on QTRONIX_KEYBOARD && (MIPS_IVR || MIPS_ITE8172) - default y - -config PC_KEYB - bool "Enable PS2 Keyboard Support" if MIPS_ITE8172 && !QTRONIX_KEYBOARD - default y if NINO || MIPS_ITE8172 || DDB5476 || DDB5074 || SNI_RM200_PCI || SGI_IP22 || ACER_PICA_61 || MIPS_MAGNUM_4000 || OLIVETTI_M700 - -config IT8172_SCR0 - bool "Enable Smart Card Reader 0 Support " - depends on MIPS_IVR || MIPS_ITE8172 - help - Say Y here to support smart-card reader 0 (SCR0) on the Integrated - Technology Express, Inc. ITE8172 SBC. Vendor page at - <http://www.ite.com.tw/ia/brief_it8172bsp.htm>; picture of the - board at <http://www.mvista.com/allies/semiconductor/ite.html>. - -config IT8172_SCR1 - bool "Enable Smart Card Reader 1 Support " - depends on MIPS_ITE8172 - help - Say Y here to support smart-card reader 1 (SCR1) on the Integrated - Technology Express, Inc. ITE8172 SBC. Vendor page at - <http://www.ite.com.tw/ia/brief_it8172bsp.htm>; picture of the - board at <http://www.mvista.com/allies/semiconductor/ite.html>. - -config MIPS_IVR - bool "Support for Globespan IVR board" - help - This is an evaluation board built by Globespan to showcase thir - iVR (Internet Video Recorder) design. It utilizes a QED RM5231 - R5000 MIPS core. More information can be found out their website - located at <http://www.globespan.net/products/product4.html>P. Say Y - here if you wish to build a kernel for this platform. - -config MIPS_PB1000 - bool "Support for Alchemy Semi PB1000 board" - help - This is an evaluation board built by Alchemy Semiconducttor to - showcase their Au1000 Internet Edge Processor. It is SOC design - containing a MIPS32 core running at 266/400/500MHz with many - integrated peripherals. Further information can be found at their - website, <http://www.alchemysemi.com/>. Say Y here if you wish to - build a kernel for this platform. - -config RWSEM_GENERIC_SPINLOCK - bool - default y - -config RWSEM_XCHGADD_ALGORITHM - bool - -# -# Select some configuration options automatically for certain systems. -# -config ISA - bool - depends on DDB5476 || DDB5074 || SNI_RM200_PCI || ACER_PICA_61 || MIPS_MAGNUM_4000 || OLIVETTI_M700 - default y - help - Find out whether you have ISA slots on your motherboard. ISA is the - name of a bus system, i.e. the way the CPU talks to the other stuff - inside your box. Other bus systems are PCI, EISA, MicroChannel - (MCA) or VESA. ISA is an older system, now being displaced by PCI; - newer boards don't support it. If you have ISA, say Y, otherwise N. - -config EISA - bool - depends on ISA - default y - ---help--- - The Extended Industry Standard Architecture (EISA) bus was - developed as an open alternative to the IBM MicroChannel bus. - - The EISA bus provided some of the features of the IBM MicroChannel - bus while maintaining backward compatibility with cards made for - the older ISA bus. The EISA bus saw limited use between 1988 and - 1995 when it was made obsolete by the PCI bus. - - Say Y here if you are building a kernel for an EISA-based machine. - - Otherwise, say N. - -config PCI - bool - depends on MIPS_IVR || MIPS_ITE8172 || DDB5477 || DDB5476 || DDB5074 || SNI_RM200_PCI || MOMENCO_OCELOT || MIPS_MALTA || MIPS_ATLAS || ALGOR_P4032 || MIPS_EV64120 || MIPS_EV96100 - default y - help - Find out whether you have a PCI motherboard. PCI is the name of a - bus system, i.e. the way the CPU talks to the other stuff inside - your box. Other bus systems are ISA, EISA, MicroChannel (MCA) or - VESA. If you have PCI, say Y, otherwise N. - - The PCI-HOWTO, available from - <http://www.tldp.org/docs.html#howto>, contains valuable - information about which PCI hardware does work under Linux and which - doesn't. - -config MCA - bool - help - MicroChannel Architecture is found in some IBM PS/2 machines and - laptops. It is a bus system similar to PCI or ISA. See - <file:Documentation/mca.txt> (and especially the web page given - there) before attempting to build an MCA bus kernel. - -config SBUS - bool - -config I8259 - bool - depends on DDB5074 || SNI_RM200_PCI || MIPS_MALTA || ACER_PICA_61 || MIPS_MAGNUM_4000 || OLIVETTI_M700 - default y - -config MIPS_GT96100 - bool - depends on MIPS_EV96100 - default y - help - Say Y here to support the Galileo Technology GT96100 communications - controller card. There is a web page at <http://www.galileot.com/>. - -config SWAP_IO_SPACE - bool - depends on MOMENCO_OCELOT || MIPS_MALTA || MIPS_ATLAS || MIPS_EV96100 - default y - -config NEW_PCI - bool - depends on MIPS_ITE8172 || MIPS_EV96100 - default y - -config PCI_AUTO - bool - depends on MIPS_ITE8172 || MIPS_EV96100 - default y - -config MIPS_GT64120 - bool - depends on MIPS_EV64120 - default y - -config OLD_TIME_C - bool - depends on DDB5074 || SNI_RM200_PCI || SGI_IP22 || MOMENCO_OCELOT || ACER_PICA_61 || MIPS_MAGNUM_4000 || OLIVETTI_M700 || ALGOR_P4032 || MIPS_EV64120 - default y - -config ARC32 - bool - depends on SNI_RM200_PCI || SGI_IP22 || ACER_PICA_61 || MIPS_MAGNUM_4000 || OLIVETTI_M700 - default y - -config FB - bool - depends on MIPS_MAGNUM_4000 || OLIVETTI_M700 - default y - ---help--- - The frame buffer device provides an abstraction for the graphics - hardware. It represents the frame buffer of some video hardware and - allows application software to access the graphics hardware through - a well-defined interface, so the software doesn't need to know - anything about the low-level (hardware register) stuff. - - Frame buffer devices work identically across the different - architectures supported by Linux and make the implementation of - application programs easier and more portable; at this point, an X - server exists which uses the frame buffer device exclusively. - On several non-X86 architectures, the frame buffer device is the - only way to use the graphics hardware. - - The device is accessed through special device nodes, usually located - in the /dev directory, i.e. /dev/fb*. - - You need an utility program called fbset to make full use of frame - buffer devices. Please read <file:Documentation/fb/framebuffer.txt> - and the Framebuffer-HOWTO at - <http://www.tahallah.demon.co.uk/programming/prog.html> for more - information. - - Say Y here and to the driver for your graphics board below if you - are compiling a kernel for a non-x86 architecture. - - If you are compiling for the x86 architecture, you can say Y if you - want to play with it, but it is not essential. Please note that - running graphical applications that directly touch the hardware - (e.g. an accelerated X server) and that are not frame buffer - device-aware may cause unexpected results. If unsure, say N. - -config FB_G364 - bool - depends on MIPS_MAGNUM_4000 || OLIVETTI_M700 - default y - -config MIPS_JAZZ - bool - depends on ACER_PICA_61 || MIPS_MAGNUM_4000 || OLIVETTI_M700 - default y - -config ROTTEN_IRQ - bool - depends on DDB5476 || DDB5074 || SNI_RM200_PCI || ACER_PICA_61 - default y - -config HAVE_STD_PC_SERIAL_PORT - bool - depends on DDB5476 || DDB5074 || MIPS_MALTA - default y - -config NEW_IRQ - bool - depends on MIPS_PB1000 || DDB5477 || SGI_IP22 || MOMENCO_OCELOT || MIPS_MALTA - default y - -config SYSCLK_100_2 - bool - depends on MOMENCO_OCELOT - default y - -config BOARD_SCACHE - bool - depends on SGI_IP22 - default y - -config SGI - bool - depends on SGI_IP22 - default y - -config NEW_TIME_C - bool - depends on DDB5477 || DDB5476 - default y - -config CPU_LITTLE_ENDIAN - bool - depends on DDB5477 - default y - help - Some MIPS machines can be configured for either little or big endian - byte order. These modes require different kernels. Say Y if your - machine is little endian, N if it's a big endian machine. - -config IT8712 - bool - depends on MIPS_ITE8172 - default y - -config MIPS_AU1000 - bool - depends on MIPS_PB1000 - default y - -config SYSCLK_100 - bool - depends on SYSCLK_100_1 || SYSCLK_100_2 - default y - -endmenu - - -menu "CPU selection" - -choice - prompt "CPU type" - default CPU_R4X00 - -config CPU_R3000 - bool "R3000" - ---help--- - Please make sure to pick the right CPU type. Linux/MIPS is not - designed to be generic, i.e. Kernels compiled for R3000 CPUs will - *not* work on R4000 machines and vice versa. However, since most - of the supported machines have an R4000 (or similar) CPU, R4x00 - might be a safe bet. If the resulting kernel does not work, - try to recompile with R3000. - - R3000 MIPS Technologies R3000-series processors, - including the 3041, 3051, and 3081. - - R6000 MIPS Technologies R6000-series processors, - including the 64474, 64475, 64574 and 64575. - - R4300 MIPS Technologies R4300-series processors. - - R4x00 MIPS Technologies R4000-series processors other than 4300, - including the 4640, 4650, and 4700. - - R5000 MIPS Technologies R5000-series processors other than the - Nevada. - - R52xx MIPS Technologies R52xx-series ("Nevada") processors. - - R10000 MIPS Technologies R10000-series processors. - -config CPU_R6000 - bool "R6000" - help - MIPS Technologies R6000-series processors, including the 64474, - 64475, 64574 and 64575. - -config CPU_VR41XX - bool "R41xx" - help - The options selects support for the NEC VR41xx series of processors. - Only choose this option if you have one of these processors as a - kernel built with this option will not run on any other type of - processor or vice versa. - -config CPU_R4300 - bool "R4300" - help - MIPS Technologies R4300-series processors. - -config CPU_R4X00 - bool "R4x00" - help - MIPS Technologies R4000-series processors other than 4300, including - the 4640, 4650, and 4700. - -config CPU_R5000 - bool "R5000" - help - MIPS Technologies R5000-series processors other than the Nevada. - -config CPU_R5432 - bool "R5432" - -config CPU_RM7000 - bool "RM7000" - -config CPU_NEVADA - bool "R52xx" - help - MIPS Technologies R52x0-series ("Nevada") processors. - -config CPU_R10000 - bool "R10000" - help - MIPS Technologies R10000-series processors. - -config CPU_SB1 - bool "SB1" - -config CPU_MIPS32 - bool "MIPS32" - -config CPU_MIPS64 - bool "MIPS64" - -endchoice - -config CPU_ADVANCED - bool "Override CPU Options" - help - Saying yes here allows you to select support for various features - your CPU may or may not have. Most people should say N here. - -config CPU_HAS_LLSC - bool "ll/sc Instructions available" if CPU_ADVANCED - default y if !CPU_ADVANCED && !CPU_R3000 && !CPU_VR41XX - help - MIPS R4000 series and later provide the Load Linked (ll) - and Store Conditional (sc) instructions. More information is - available at <http://www.go-ecs.com/mips/miptek1.htm>. - - Say Y here if your CPU has the ll and sc instructions. Say Y here - for better performance, N if you don't know. You must say Y here - for multiprocessor machines. - -config CPU_HAS_LLDSCD - bool "lld/scd Instructions available" if CPU_ADVANCED - default y if !CPU_ADVANCED && !CPU_R3000 && !CPU_VR41XX && !CPU_MIPS32 - help - Say Y here if your CPU has the lld and scd instructions, the 64-bit - equivalents of ll and sc. Say Y here for better performance, N if - you don't know. You must say Y here for multiprocessor machines. - -config CPU_HAS_WB - bool "Writeback Buffer available" if CPU_ADVANCED - default y if !CPU_ADVANCED && (CPU_R3000 || CPU_VR41XX) && CONFIG_DECSTATION=y - help - Say N here for slightly better performance. You must say Y here for - machines which require flushing of write buffers in software. Saying - Y is the safe option; N may result in kernel malfunction and crashes. - -endmenu - - -menu "General setup" - -config CPU_LITTLE_ENDIAN - bool "Generate little endian code" if !DECSTATION && !DDB5074 && !DDB5476 && !NINO - default y if DECSTATION || DDB5074 || DDB5476 || NINO - -config KCORE_ELF - bool - depends on PROC_FS - default y - ---help--- - If you enabled support for /proc file system then the file - /proc/kcore will contain the kernel core image. This can be used - in gdb: - - $ cd /usr/src/linux ; gdb vmlinux /proc/kcore - - You have two choices here: ELF and A.OUT. Selecting ELF will make - /proc/kcore appear in ELF core format as defined by the Executable - and Linking Format specification. Selecting A.OUT will choose the - old "a.out" format which may be necessary for some old versions - of binutils or on some architectures. - - This is especially useful if you have compiled the kernel with the - "-g" option to preserve debugging information. It is mainly used - for examining kernel data structures on the live kernel so if you - don't understand what this means or are not a kernel hacker, just - leave it at its default value ELF. - -config ELF_KERNEL - bool - default y - -config BINFMT_IRIX - bool "Include IRIX binary compatibility" - depends on !CPU_LITTLE_ENDIAN - -config FORWARD_KEYBOARD - bool "Include forward keyboard" - depends on !CPU_LITTLE_ENDIAN - -config ARC_CONSOLE - bool "ARC console support" - depends on ARC32 - -source "fs/Kconfig.binfmt" - -source "drivers/pci/Kconfig" - -config HOTPLUG - bool "Support for hot-pluggable devices" - ---help--- - Say Y here if you want to plug devices into your computer while - the system is running, and be able to use them quickly. In many - cases, the devices can likewise be unplugged at any time too. - - One well known example of this is PCMCIA- or PC-cards, credit-card - size devices such as network cards, modems or hard drives which are - plugged into slots found on all modern laptop computers. Another - example, used on modern desktops as well as laptops, is USB. - - Enable HOTPLUG and KMOD, and build a modular kernel. Get agent - software (at <http://linux-hotplug.sourceforge.net/>) and install it. - Then your kernel will automatically call out to a user mode "policy - agent" (/sbin/hotplug) to load modules and set up software needed - to use devices as you hotplug them. - -source "drivers/pcmcia/Kconfig" - -config TC - bool "TURBOchannel support" - depends on DECSTATION - help - TurboChannel is a DEC (now Compaq) bus for Alpha and MIPS processors. - Documentation on writing device drivers for TurboChannel is available at: - <http://www.cs.arizona.edu/computer.help/policy/DIGITAL_unix/AA-PS3HD-TET1_html/TITLE.html>. - -# if [ "$CONFIG_TC" = "y" ]; then -# bool ' Access.Bus support' CONFIG_ACCESSBUS -# fi -endmenu - - -if ISA -source "drivers/pnp/Kconfig" -endif - -source "drivers/base/Kconfig" - -source "drivers/mtd/Kconfig" - -source "drivers/parport/Kconfig" - -source "drivers/block/Kconfig" - -source "drivers/md/Kconfig" - -if !SGI_IP22 && !DECSTATION -source "drivers/ide/Kconfig" -endif - - -menu "SCSI support" - -config SCSI - tristate "SCSI support" - ---help--- - If you want to use a SCSI hard disk, SCSI tape drive, SCSI CD-ROM or - any other SCSI device under Linux, say Y and make sure that you know - the name of your SCSI host adapter (the card inside your computer - that "speaks" the SCSI protocol, also called SCSI controller), - because you will be asked for it. - - You also need to say Y here if you want support for the parallel - port version of the 100 MB IOMEGA ZIP drive. - - 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 scsi_mod. If you want to compile it as - a module, say M here and read <file:Documentation/modules.txt> and - <file:Documentation/scsi/scsi.txt>. However, do not compile this as a - module if your root file system (the one containing the directory /) - is located on a SCSI device. - -source "drivers/scsi/Kconfig" - -endmenu - - -if !DECSTATION && !SGI_IP22 -source "drivers/message/i2o/Kconfig" -endif - -source "net/Kconfig" - -source "net/ax25/Kconfig" - -source "net/irda/Kconfig" - -source "drivers/isdn/Kconfig" - -source "drivers/telephony/Kconfig" - - -menu "Old CD-ROM drivers (not SCSI, not IDE)" - -config CD_NO_IDESCSI - bool "Support non-SCSI/IDE/ATAPI CDROM drives" - ---help--- - If you have a CD-ROM drive that is neither SCSI nor IDE/ATAPI, say Y - here, otherwise N. Read the CD-ROM-HOWTO, available from - <http://www.tldp.org/docs.html#howto>. - - Note that the answer to this question doesn't directly affect the - kernel: saying N will just cause the configurator to skip all - the questions about these CD-ROM drives. If you are unsure what you - have, say Y and find out whether you have one of the following - drives. - - For each of these drivers, a file Documentation/cdrom/{driver_name} - exists. Especially in cases where you do not know exactly which kind - of drive you have you should read there. Most of these drivers use a - file drivers/cdrom/{driver_name}.h where you can define your - interface parameters and switch some internal goodies. - - All these CD-ROM drivers are also usable as a module ( = code which - can be inserted in and removed from the running kernel whenever you - want). If you want to compile them as module, say M instead of Y and - read <file:Documentation/modules.txt>. - - If you want to use any of these CD-ROM drivers, you also have to - answer Y or M to "ISO 9660 CD-ROM file system support" below (this - answer will get "defaulted" for you if you enable any of the Linux - CD-ROM drivers). - -source "drivers/cdrom/Kconfig" - -endmenu - -source "drivers/input/Kconfig" - -source "drivers/char/Kconfig" - -source "drivers/media/Kconfig" - - -menu "DECStation Character devices" - depends on DECSTATION - -config VT - bool "Virtual terminal" - -config VT_CONSOLE - bool "Support for console on virtual terminal" - depends on VT - -config SERIAL - tristate "Standard/generic (dumb) serial support" - ---help--- - This selects whether you want to include the driver for the standard - serial ports. The standard answer is Y. People who might say N - here are those that are setting up dedicated Ethernet WWW/FTP - servers, or users that have one of the various bus mice instead of a - serial mouse and don't intend to use their machine's standard serial - port for anything. (Note that the Cyclades and Stallion multi - serial port drivers do not need this driver built in for them to - work.) - - If you want to compile this driver as a module, say M here and read - <file:Documentation/modules.txt>. The module will be called - serial. - [WARNING: Do not compile this driver as a module if you are using - non-standard serial ports, since the configuration information will - be lost when the driver is unloaded. This limitation may be lifted - in the future.] - - BTW1: If you have a mouseman serial mouse which is not recognized by - the X window system, try running gpm first. - - BTW2: If you intend to use a software modem (also called Winmodem) - under Linux, forget it. These modems are crippled and require - proprietary drivers which are only available under Windows. - - Most people will say Y or M here, so that they can use serial mice, - modems and similar devices connecting to the standard serial ports. - -config DZ - bool "DZ11 Serial Support" - depends on SERIAL=y - help - DZ11-family serial controllers for VAXstations, including the - DC7085, M7814, and M7819. - -config ZS - bool "Z85C30 Serial Support" - depends on SERIAL=y && TC - help - Documentation on the Zilog 85C350 serial communications controller - is downloadable at <http://www.zilog.com/pdfs/serial/z85c30.pdf>. - -config SERIAL_CONSOLE - bool "Support for console on serial port" - depends on SERIAL=y - ---help--- - If you say Y here, it will be possible to use a serial port as the - system console (the system console is the device which receives all - kernel messages and warnings and which allows logins in single user - mode). This could be useful if some terminal or printer is connected - to that serial port. - - Even if you say Y here, the currently visible virtual console - (/dev/tty0) will still be used as the system console by default, but - you can alter that using a kernel command line option such as - "console=ttyS1". (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.) - - If you don't have a VGA card installed and you say Y here, the - kernel will automatically use the first serial line, /dev/ttyS0, as - system console. - - If unsure, say N. - -config UNIX98_PTYS - bool "Unix98 PTY support" - -config UNIX98_PTY_COUNT - int "Maximum number of Unix98 PTYs in use (0-2048)" - depends on UNIX98_PTYS - default "256" - -# if [ "$CONFIG_ACCESSBUS" = "y" ]; then -# bool 'MAXINE Access.Bus mouse (VSXXX-BB/GB) support' CONFIG_DTOP_MOUSE -# fi -config RTC - tristate "Enhanced Real Time Clock Support" - -endmenu - - -menu "SGI Character devices" - depends on SGI_IP22 - -config VT - bool "Virtual terminal" - -config VT_CONSOLE - bool "Support for console on virtual terminal" - depends on VT - -config PSMOUSE - bool "PS/2 mouse support (aka \"auxiliary device\")" - ---help--- - The PS/2 mouse connects to a special mouse port that looks much like - the keyboard port (small circular connector with 6 pins). This way, - the mouse does not use any serial ports. This port can also be used - for other input devices like light pens, tablets, keypads. Compaq, - AST and IBM all use this as their mouse port on currently shipping - machines. The trackballs of some laptops are PS/2 mice also. In - particular, the C&T 82C710 mouse on TI Travelmates is a PS/2 mouse. - - Although PS/2 mice are not technically bus mice, they are explained - in detail in the Busmouse-HOWTO, available from - <http://www.tldp.org/docs.html#howto>. - - When using a PS/2 mouse, you can get problems if you want to use the - mouse both on the Linux console and under X. Using the "-R" option - of the Linux mouse managing program gpm (available from - <ftp://gnu.systemy.it/pub/gpm/>) solves this problem, or you can get - the "mconv2" utility from <ftp://ibiblio.org/pub/Linux/system/mouse/>. - -config MOUSE - bool - depends on PSMOUSE - default y - ---help--- - This is for machines with a mouse which is neither a serial nor a - bus mouse. Examples are PS/2 mice (such as the track balls on some - laptops) and some digitizer pads. Most people have a regular serial - MouseSystem or Microsoft mouse (made by Logitech) that plugs into a - COM port (rectangular with 9 or 25 pins). These people say N here. - If you have something else, read the Busmouse-HOWTO, available from - <http://www.tldp.org/docs.html#howto>. This HOWTO contains - information about all non-serial mice, not just bus mice. - - If you have a laptop, you either have to check the documentation or - experiment a bit to find out whether the trackball is a serial mouse - or not; it's best to say Y here for you. - - Note that the answer to this question won't directly affect the - kernel: saying N will just cause the configurator to skip all - the questions about non-serial mice. If unsure, say Y. - -config UNIX98_PTYS - bool "Unix98 PTY support" - -config UNIX98_PTY_COUNT - int "Maximum number of Unix98 PTYs in use (0-2048)" - depends on UNIX98_PTYS - default "256" - -endmenu - -source "fs/Kconfig" - -source "drivers/video/Kconfig" - -menu "Sound" - depends on !DECSTATION - -config SOUND - tristate "Sound card support" - ---help--- - If you have a sound card in your computer, i.e. if it can say more - than an occasional beep, say Y. Be sure to have all the information - about your sound card and its configuration down (I/O port, - interrupt and DMA channel), because you will be asked for it. - - You want to read the Sound-HOWTO, available from - <http://www.tldp.org/docs.html#howto>. General information about - the modular sound system is contained in the files - <file:Documentation/sound/Introduction>. The file - <file:Documentation/sound/README.OSS> contains some slightly - outdated but still useful information as well. - - If you have a PnP sound card and you want to configure it at boot - time using the ISA PnP tools (read - <http://www.roestock.demon.co.uk/isapnptools/>), then you need to - compile the sound card support as a module ( = code which can be - inserted in and removed from the running kernel whenever you want) - and load that module after the PnP configuration is finished. To do - this, say M here and read <file:Documentation/modules.txt> as well - as <file:Documentation/sound/README.modules>; the module will be - called soundcore. - - I'm told that even without a sound card, you can make your computer - say more than an occasional beep, by programming the PC speaker. - Kernel patches and supporting utilities to do that are in the pcsp - package, available at <ftp://ftp.infradead.org/pub/pcsp/>. - -source "sound/Kconfig" - -endmenu - -source "drivers/sgi/Kconfig" - -source "drivers/usb/Kconfig" - - -menu "Kernel hacking" - -config CROSSCOMPILE - bool "Are you using a crosscompiler" - help - Say Y here if you are compiling the kernel on a different - architecture than the one it is intended to run on. - -config REMOTE_DEBUG - bool "Remote GDB kernel debugging" - depends on SERIAL=y || AU1000_UART - help - If you say Y here, it will be possible to remotely debug the MIPS - kernel using gdb. This enlarges your kernel image disk size by - several megabytes and requires a machine with more than 16 MB, - better 32 MB RAM to avoid excessive linking time. This is only - useful for kernel hackers. If unsure, say N. - -config GDB_CONSOLE - bool "Console output to GDB" - depends on REMOTE_DEBUG - help - If you are using GDB for remote debugging over a serial port and - would like kernel messages to be formatted into GDB $O packets so - that GDB prints them as program output, say 'Y'. - -config LL_DEBUG - bool "Low-level debugging" - depends on SERIAL=y - help - Enable low-level debugging assertion macros in the kernel code. - Currently used only by the time services code in the MIPS port. - Don't turn this on unless you know what you are doing. - -config MAGIC_SYSRQ - bool "Magic SysRq key" - help - If you say Y here, you will have some control over the system even - if the system crashes for example during kernel debugging (e.g., you - will be able to flush the buffer cache to disk, reboot the system - immediately or dump some status information). This is accomplished - by pressing various keys while holding SysRq (Alt+PrintScreen). It - also works on a serial console (on PC hardware at least), if you - send a BREAK and then within 5 seconds a command keypress. The - keys are documented in <file:Documentation/sysrq.txt>. Don't say Y - unless you really know what this hack does. - -config MIPS_UNCACHED - bool "Run uncached" - depends on !SMP - help - If you say Y here there kernel will disable all CPU caches. This will - reduce the system's performance dramatically but can help finding - otherwise hard to track bugs. It can also useful if you're doing - hardware debugging with a logic analyzer and need to see all traffic - on the bus. - -config NR_CPUS - int "Maximum number of CPUs (2-32)" - depends on SMP - default "32" - -endmenu - -source "security/Kconfig" - -source "crypto/Kconfig" - -source "lib/Kconfig" + default n +source "arch/mips/Kconfig-shared" diff --git a/arch/mips/Kconfig-shared b/arch/mips/Kconfig-shared new file mode 100644 index 000000000000..e70459b7d433 --- /dev/null +++ b/arch/mips/Kconfig-shared @@ -0,0 +1,1431 @@ +mainmenu "Linux/MIPS Kernel Configuration" + +source "init/Kconfig" + +menu "Machine selection" + +config ACER_PICA_61 + bool "Support for Acer PICA 1 chipset (EXPERIMENTAL)" + depends on EXPERIMENTAL + help + This is a machine with a R4400 133/150 MHz CPU. To compile a Linux + kernel that runs on these, say Y here. For details about Linux on + the MIPS architecture, check out the Linux/MIPS FAQ on the WWW at + <http://oss.sgi.com/mips/>. + +config BAGET_MIPS + bool "Support for BAGET MIPS series (EXPERIMENTAL)" + depends on MIPS32 && EXPERIMENTAL + help + This enables support for the Baget, a Russian embedded system. For + more details about the Baget see the Linux/MIPS FAQ on + <http://oss.sgi.com/mips/>. + +config CASIO_E55 + bool "Support for CASIO CASSIOPEIA E-10/15/55/65" + +config MIPS_COBALT + bool "Support for Cobalt Server (EXPERIMENTAL)" + depends on EXPERIMENTAL + +config DECSTATION + bool "Support for DECstations" + depends on MIPS32 || EXPERIMENTAL + ---help--- + This enables support for DEC's MIPS based workstations. For details + see the Linux/MIPS FAQ on <http://oss.sgi.com/mips/> and the + DECstation porting pages on <http://decstation.unix-ag.org/>. + + If you have one of the following DECstation Models you definitely + want to choose R4xx0 for the CPU Type: + + DECstation 5000/50 + DECstation 5000/150 + DECstation 5000/260 + DECsystem 5900/260 + + otherwise choose R3000. + +config MIPS_EV64120 + bool "Support for Galileo EV64120 Evaluation board (EXPERIMENTAL)" + depends on EXPERIMENTAL + help + This is an evaluation board based on the Galileo GT-64120 + single-chip system controller that contains a MIPS R5000 compatible + core running at 75/100MHz. Their website is located at + <http://www.galileot.com/>. Say Y here if you wish to build a + kernel for this platform. + +config EVB_PCI1 + bool "Enable Second PCI (PCI1)" + depends on MIPS_EV64120 + +if MOMENCO_OCELOT_G || MOMENCO_OCELOT + +config SYSCLK_100 + bool + default y + +endif +if MIPS_EV64120 + +choice + prompt "Galileo Chip Clock" + default SYSCLK_83 + +config SYSCLK_75 + bool "75" + +config SYSCLK_83 + bool "83.3" + +config SYSCLK_100 + bool "100" if MIPS_EV64120 + +endchoice + +endif + +config MIPS_EV96100 + bool "Support for Galileo EV96100 Evaluation board (EXPERIMENTAL)" + depends on EXPERIMENTAL + help + This is an evaluation board based on the Galielo GT-96100 LAN/WAN + communications controllers containing a MIPS R5000 compatible core + running at 83MHz. Their website is <http://www.galileot.com/>. Say Y + here if you wish to build a kernel for this platform. + +config MIPS_IVR + bool "Support for Globespan IVR board" + help + This is an evaluation board built by Globespan to showcase thir + iVR (Internet Video Recorder) design. It utilizes a QED RM5231 + R5000 MIPS core. More information can be found out their website + located at <http://www.globespan.net/products/product4.html>P. Say Y + here if you wish to build a kernel for this platform. + +config LASAT + bool "Support for LASAT Networks platforms" + +config LASAT_100 + bool "Support for LASAT Networks 100 series" + depends on LASAT + +config LASAT_200 + bool "Support for LASAT Networks 200 series" + depends on LASAT + +config PICVUE + tristate "PICVUE LCD display driver" + depends on LASAT + +config PICVUE_PROC + tristate "PICVUE LCD display driver /proc interface" + depends on PICVUE + +config DS1603 + bool "DS1603 RTC driver" + depends on LASAT + +config LASAT_SYSCTL + bool "LASAT sysctl interface" + depends on LASAT + +config HP_LASERJET + bool "Support for Hewlett Packard LaserJet board" + +config IBM_WORKPAD + bool "Support for IBM WorkPad z50" + +config MIPS_ITE8172 + bool "Support for ITE 8172G board" + help + Ths is an evaluation board made by ITE <http://www.ite.com.tw/> + with ATX form factor that utilizes a MIPS R5000 to work with its + ITE8172G companion internet appliance chip. The MIPS core can be + either a NEC Vr5432 or QED RM5231. Say Y here if you wish to build + a kernel for this platform. + +config IT8172_REVC + bool "Support for older IT8172 (Rev C)" + depends on MIPS_ITE8172 + help + Say Y here to support the older, Revision C version of the Integrated + Technology Express, Inc. ITE8172 SBC. Vendor page at + <http://www.ite.com.tw/ia/brief_it8172bsp.htm>; picture of the + board at <http://www.mvista.com/allies/semiconductor/ite.html>. + +config MIPS_ATLAS + bool "Support for MIPS Atlas board" + help + This enables support for the QED R5231-based MIPS Atlas evaluation + board. + +config MIPS_MAGNUM_4000 + bool "Support for MIPS Magnum 4000" + help + This is a machine with a R4000 100 MHz CPU. To compile a Linux + kernel that runs on these, say Y here. For details about Linux on + the MIPS architecture, check out the Linux/MIPS FAQ on the WWW at + <http://oss.sgi.com/mips/>. + +config MIPS_MALTA + bool "Support for MIPS Malta board" + help + This enables support for the VR5000-based MIPS Malta evaluation + board. + +config MIPS_SEAD + bool "Support for MIPS SEAD board (EXPERIMENTAL)" + depends on EXPERIMENTAL + +config MOMENCO_OCELOT + bool "Support for Momentum Ocelot board" + help + The Ocelot is a MIPS-based Single Board Computer (SBC) made by + Momentum Computer <http://www.momenco.com/>. + +config MOMENCO_OCELOT_G + bool "Support for Momentum Ocelot-G board" + help + The Ocelot is a MIPS-based Single Board Computer (SBC) made by + Momentum Computer <http://www.momenco.com/>. + +config MOMENCO_OCELOT_C + bool "Support for Momentum Ocelot-C board" + help + The Ocelot is a MIPS-based Single Board Computer (SBC) made by + Momentum Computer <http://www.momenco.com/>. + +config DDB5074 + bool "Support for NEC DDB Vrc-5074 (EXPERIMENTAL)" + depends on EXPERIMENTAL + help + This enables support for the VR5000-based NEC DDB Vrc-5074 + evaluation board. + +config DDB5476 + bool "Support for NEC DDB Vrc-5476" + help + This enables support for the R5432-based NEC DDB Vrc-5476 + evaluation board. + + Features : kernel debugging, serial terminal, NFS root fs, on-board + ether port USB, AC97, PCI, PCI VGA card & framebuffer console, + IDE controller, PS2 keyboard, PS2 mouse, etc. + +config DDB5477 + bool "Support for NEC DDB Vrc-5477" + help + This enables support for the R5432-based NEC DDB Vrc-5477, + or Rockhopper/SolutionGear boards with R5432/R5500 CPUs. + + Features : kernel debugging, serial terminal, NFS root fs, on-board + ether port USB, AC97, PCI, etc. + +config DDB5477_BUS_FREQUENCY + int "bus frequency (in kHZ, 0 for auto-detect)" + depends on DDB5477 + default 0 + +config NEC_OSPREY + bool "Support for NEC Osprey board" + +config NEC_EAGLE + bool "Support for NEC Eagle/Hawk board" + +config OLIVETTI_M700 + bool "Support for Olivetti M700-10" + help + This is a machine with a R4000 100 MHz CPU. To compile a Linux + kernel that runs on these, say Y here. For details about Linux on + the MIPS architecture, check out the Linux/MIPS FAQ on the WWW at + <http://oss.sgi.com/mips/>. + +config SGI_IP22 + bool "Support for SGI IP22 (Indy/Indigo2)" + help + This are the SGI Indy, Challenge S and Indigo2, as well as certain + OEM variants like the Tandem CMN B006S. To compile a Linux kernel + that runs on these, say Y here. + +config SGI_IP27 + bool "Support for SGI IP27 (Origin200/2000)" + depends on MIPS64 + help + This are the SGI Origin 200, Origin 2000 and Onyx 2 Graphics + workstations. To compile a Linux kernel that runs on these, say Y + here. + +#config SGI_SN0_XXL +# bool "IP27 XXL" +# depends on SGI_IP27 +# This options adds support for userspace processes upto 16TB size. +# Normally the limit is just .5TB. + +config SGI_SN0_N_MODE + bool "IP27 N-Mode" + depends on SGI_IP27 + help + The nodes of Origin 200, Origin 2000 and Onyx 2 systems can be + configured in either N-Modes which allows for more nodes or M-Mode + which allows for more memory. Your system is most probably + running in M-Mode, so you should say N here. + +config DISCONTIGMEM + bool "Discontiguous Memory Support" + depends on SGI_IP27 + help + Say Y to upport efficient handling of discontiguous physical memory, + for architectures which are either NUMA (Non-Uniform Memory Access) + or have huge holes in the physical address space for other reasons. + See <file:Documentation/vm/numa> for more. + +config NUMA + bool "NUMA Support" + depends on SGI_IP27 + help + Say Y to compile the kernel to support NUMA (Non-Uniform Memory + Access). This option is for configuring high-end multiprocessor + server machines. If in doubt, say N. + +config MAPPED_KERNEL + bool "Mapped kernel support" + depends on SGI_IP27 + help + Change the way a Linux kernel is loaded unto memory on a MIPS64 + machine. This is required in order to support text replication and + NUMA. If you need to undersatand it, read the source code. + +config REPLICATE_KTEXT + bool "Kernel text replication support" + depends on SGI_IP27 + help + Say Y here to enable replicating the kernel text across multiple + nodes in a NUMA cluster. This trades memory for speed. + +config REPLICATE_EXHANDLERS + bool "Exception handler replication support" + depends on SGI_IP27 + help + Say Y here to enable replicating the kernel exception handlers + across multiple nodes in a NUMA cluster. This trades memory for + speed. + +config SGI_IP32 + bool "Support for SGI IP32 (O2) (EXPERIMENTAL)" + depends on EXPERIMENTAL + help + If you want this kernel to run on SGI O2 workstation, say Y here. + +config SOC_AU1X00 + depends on MIPS32 + bool "Support for AMD/Alchemy Au1X00 SOCs" + +choice + prompt "Au1X00 SOC Type" + depends on SOC_AU1X00 + help + Say Y here to enable support for one of three AMD/Alchemy + SOCs. For additional documentation see www.amd.com. + +config SOC_AU1000 + bool "SOC_AU1000" +config SOC_AU1100 + bool "SOC_AU1100" +config SOC_AU1500 + bool "SOC_AU1500" + +endchoice + +choice + prompt "AMD/Alchemy Pb1x and Db1x board support" + depends on SOC_AU1X00 + help + These are evaluation boards built by AMD/Alchemy to + showcase their Au1X00 Internet Edge Processors. The SOC design + is based on the MIPS32 architecture running at 266/400/500MHz + with many integrated peripherals. Further information can be + found at their website, <http://www.amd.com/>. Say Y here if you + wish to build a kernel for this platform. + +config MIPS_PB1000 + bool "PB1000 board" + depends on SOC_AU1000 + +config MIPS_PB1100 + bool "PB1100 board" + depends on SOC_AU1100 + +config MIPS_PB1500 + bool "PB1500 board" + depends on SOC_AU1500 + +config MIPS_DB1000 + bool "DB1000 board" + depends on SOC_AU1000 + +config MIPS_DB1100 + bool "DB1100 board" + depends on SOC_AU1100 + +config MIPS_DB1500 + bool "DB1500 board" + depends on SOC_AU1500 + +endchoice + +config SIBYTE_SB1xxx_SOC + bool "Support for Broadcom BCM1xxx SOCs (EXPERIMENTAL)" + depends on EXPERIMENTAL + +choice + prompt "BCM1xxx SOC Type" + depends on SIBYTE_SB1xxx_SOC + default SIBYTE_SB1250 + +config SIBYTE_SB1250 + bool "BCM1250" + +endchoice + +config SIMULATION + bool "Running under simulation" + depends on SIBYTE_SB1xxx_SOC + +config SIBYTE_CFE + bool "Booting from CFE" + depends on SIBYTE_SB1xxx_SOC + +config SIBYTE_CFE_CONSOLE + bool "Use firmware console" + depends on SIBYTE_CFE + +config SIBYTE_STANDALONE + bool + depends on SIBYTE_SB1xxx_SOC && !SIBYTE_CFE + default y + +config SIBYTE_STANDALONE_RAM_SIZE + int "Memory size (in megabytes)" + depends on SIBYTE_SB1xxx_SOC && !SIBYTE_CFE + default "32" + +config SIBYTE_BUS_WATCHER + bool "Support for Bus Watcher statistics" + depends on SIBYTE_SB1xxx_SOC + +config SIBYTE_SB1250_PROF + bool "Support for SB1/SOC profiling - SB1/SCD perf counters" + depends on SIBYTE_SB1xxx_SOC + +config SIBYTE_TBPROF + bool "Support for ZBbus profiling" + depends on SIBYTE_SB1xxx_SOC + +config SIBYTE_SWARM + bool "Support for SWARM board" + depends on SIBYTE_SB1250 + +config SIBYTE_BOARD + bool + depends on SIBYTE_SWARM + default y + +config SNI_RM200_PCI + bool "Support for SNI RM200 PCI" + help + The SNI RM200 PCI was a MIPS-based platform manufactured by Siemens + Nixdorf Informationssysteme (SNI), parent company of Pyramid + Technology and now in turn merged with Fujitsu. Say Y here to + support this machine type. + +config TANBAC_TB0226 + bool "Support for TANBAC TB0226 (Mbase)" + help + The TANBAC TB0226 (Mbase) is a MIPS-based platform manufactured by TANBAC. + Please refer to <http://www.tanbac.co.jp/> about Mbase. + +config TANBAC_TB0229 + bool "Support for TANBAC TB0229 (VR4131DIMM)" + help + The TANBAC TB0229 (VR4131DIMM) is a MIPS-based platform manufactured by TANBAC. + Please refer to <http://www.tanbac.co.jp/> about VR4131DIMM. + +config TOSHIBA_JMR3927 + bool "Support for Toshiba JMR-TX3927 board" + depends on MIPS32 + +config TOSHIBA_RBTX4927 + bool "Support for Toshiba TBTX49[23]7 board" + depends on MIPS32 + +config VICTOR_MPC30X + bool "Support for Victor MP-C303/304" + +config ZAO_CAPCELLA + bool "Support for ZAO Networks Capcella" + +config RWSEM_GENERIC_SPINLOCK + bool + default y + +config RWSEM_XCHGADD_ALGORITHM + bool + +# +# Select some configuration options automatically based on user selections. +# +config ARC + bool + depends on SNI_RM200_PCI || SGI_IP32 || SGI_IP27 || SGI_IP22 || MIPS_MAGNUM_4000 || OLIVETTI_M700 || ACER_PICA_61 + default y + +config GENERIC_ISA_DMA + bool + depends on SNI_RM200_PCI || MIPS_MAGNUM_4000 || OLIVETTI_M700 || ACER_PICA_61 + default y + +config CONFIG_GT64120 + bool + depends on MIPS_EV64120 || MOMENCO_OCELOT + default y + +config I8259 + bool + depends on SNI_RM200_PCI || DDB5477 || DDB5476 || DDB5074 || MIPS_MALTA || MIPS_MAGNUM_4000 || OLIVETTI_M700 || MIPS_COBALT || ACER_PICA_61 + default y + +config MIPS_JAZZ + bool + depends on MIPS_MAGNUM_4000 || OLIVETTI_M700 || ACER_PICA_61 + default y + +config NONCOHERENT_IO + bool + depends on ZAO_CAPCELLA || VICTOR_MPC30X || TOSHIBA_JMR3927 || TOSHIBA_RBTX4927 || SNI_RM200_PCI || SGI_IP32 || SGI_IP22 || NEC_EAGLE || NEC_OSPREY || DDB5477 || DDB5476 || DDB5074 || MOMENCO_OCELOT || MOMENCO_OCELOT_C || MOMENCO_OCELOT_G || MIPS_SEAD || MIPS_MALTA || MIPS_MAGNUM_4000 || OLIVETTI_M700 || MIPS_ATLAS || LASAT || MIPS_ITE8172 || IBM_WORKPAD || HP_LASERJET || MIPS_IVR || MIPS_EV96100 || MIPS_EV64120 || DECSTATION || MIPS_COBALT || MIPS_PB1500 || MIPS_PB1100 || MIPS_PB1000 || CASIO_E55 || ACER_PICA_61 || TANBAC_TB0226 || TANBAC_TB0229 + default y if ZAO_CAPCELLA || VICTOR_MPC30X || TOSHIBA_JMR3927 || TOSHIBA_RBTX4927 || SNI_RM200_PCI || SGI_IP32 || SGI_IP22 || NEC_EAGLE || NEC_OSPREY || DDB5477 || DDB5476 || DDB5074 || MOMENCO_OCELOT_G || MOMENCO_OCELOT || MIPS_SEAD || MIPS_MALTA || MIPS_MAGNUM_4000 || OLIVETTI_M700 || MIPS_ATLAS || LASAT || MIPS_ITE8172 || IBM_WORKPAD || HP_LASERJET || MIPS_IVR || MIPS_EV96100 || MIPS_EV64120 || DECSTATION || MIPS_COBALT || MIPS_PB1500 || MIPS_PB1100 || MIPS_PB1000 || CASIO_E55 || ACER_PICA_61 || TANBAC_TB0226 || TANBAC_TB0229 + default n if (SIBYTE_SB1250 || SGI_IP27) + +config CPU_LITTLE_ENDIAN + bool "Generate little endian code" + default y if ACER_PICA_61 || CASIO_E55 || DDB5074 || DDB5476 || DDB5477 || DECSTATION || HP_LASERJET || IBM_WORKPAD || LASAT || MIPS_COBALT || MIPS_ITE8172 || MIPS_IVR || MIPS_PB1000 || MIPS_PB1100 || MIPS_PB1500 || NEC_OSPREY || NEC_EAGLE || OLIVETTI_M700 || SNI_RM200_PCI || VICTOR_MPC30X || ZAO_CAPCELLA + default n if BAGET_MIPS || MIPS_EV64120 || MIPS_EV96100 || MOMENCO_OCELOT || MOMENCO_OCELOT_G || SGI_IP22 || SGI_IP27 || SGI_IP32 || TOSHIBA_JMR3927 + help + Some MIPS machines can be configured for either little or big endian + byte order. These modes require different kernels. Say Y if your + machine is little endian, N if it's a big endian machine. + +config IRQ_CPU + bool + depends on ZAO_CAPCELLA || VICTOR_MPC30X || SGI_IP22 || NEC_EAGLE || NEC_OSPREY || DDB5477 || DDB5476 || DDB5074 || IBM_WORKPAD || HP_LASERJET || DECSTATION || CASIO_E55 || TANBAC_TB0226 || TANBAC_TB0229 + default y + +config VR41XX_TIME_C + bool + depends on ZAO_CAPCELLA || VICTOR_MPC30X || NEC_EAGLE || IBM_WORKPAD || CASIO_E55 || TANBAC_TB0226 || TANBAC_TB0229 + default y + +config DUMMY_KEYB + bool + depends on ZAO_CAPCELLA || VICTOR_MPC30X || SIBYTE_SB1250 || NEC_EAGLE || NEC_OSPREY || DDB5477 || IBM_WORKPAD || CASIO_E55 || TANBAC_TB0226 || TANBAC_TB0229 + default y + +config VR41XX_COMMON + bool + depends on NEC_EAGLE || ZAO_CAPCELLA || VICTOR_MPC30X || IBM_WORKPAD || CASIO_E55 || TANBAC_TB0226 || TANBAC_TB0229 + default y + +config VRC4173 + tristate "NEC VRC4173 Support" + depends on NEC_EAGLE || VICTOR_MPC30X + +config DDB5XXX_COMMON + bool + depends on DDB5074 || DDB5476 || DDB5477 + default y + +config MIPS_BOARDS_GEN + bool + depends on MIPS_ATLAS || MIPS_MALTA || MIPS_SEAD + default y + +config ITE_BOARD_GEN + bool + depends on MIPS_IVR || MIPS_ITE8172 + default y + +config NEW_PCI + bool + depends on ZAO_CAPCELLA || VICTOR_MPC30X || TOSHIBA_JMR3927 || TOSHIBA_RBTX4927 || NEC_EAGLE || DDB5477 || DDB5476 || DDB5074 || MIPS_ITE8172 || HP_LASERJET || MIPS_IVR || MIPS_EV96100 || MIPS_PB1500 || MIPS_PB1100 || MIPS_PB1000 || TANBAC_TB0226 || TANBAC_TB0229 + default y + +config SWAP_IO_SPACE + bool "Support for paging of anonymous memory" + depends on TOSHIBA_JMR3927 || TOSHIBA_RBTX4927 || SIBYTE_SB1250 || SGI_IP22 || MOMENCO_OCELOT_C || MOMENCO_OCELOT_G || MOMENCO_OCELOT || MIPS_MALTA || MIPS_ATLAS || MIPS_EV96100 || MIPS_PB1100 || MIPS_PB1000 + default y + help + This option allows you to choose whether you want to have support + for socalled swap devices or swap files in your kernel that are + used to provide more virtual memory than the actual RAM present + in your computer. If unusre say Y. + +config AU1000_USB_DEVICE + bool + depends on MIPS_PB1500 || MIPS_PB1100 || MIPS_PB1000 + default n + +config COBALT_LCD + bool + depends on MIPS_COBALT + default y + +config MIPS_GT64120 + bool + depends on MIPS_EV64120 + default y + +config MIPS_GT96100 + bool + depends on MIPS_EV96100 + default y + help + Say Y here to support the Galileo Technology GT96100 communications + controller card. There is a web page at <http://www.galileot.com/>. + +config IT8172_CIR + bool + depends on MIPS_ITE8172 || MIPS_IVR + default y + +config IT8712 + bool + depends on MIPS_ITE8172 + default y + +config BOOT_ELF32 + bool + depends on DECSTATION || MIPS_ATLAS || MIPS_MALTA || SIBYTE_SB1250 || SGI_IP32 || SGI_IP22 || SNI_RM200_PCI + default y + +config L1_CACHE_SHIFT + int + default "4" if DECSTATION + default "5" if SGI_IP32 || SGI_IP22 || MIPS_SEAD || MIPS_MALTA || MIPS_ATLAS + default "7" if SGI_IP27 + +config ARC32 + bool + depends on SNI_RM200_PCI || SGI_IP32 || SGI_IP22 || MIPS_MAGNUM_4000 || OLIVETTI_M700 + default y + +config FB + bool + depends on MIPS_MAGNUM_4000 || OLIVETTI_M700 + default y + ---help--- + The frame buffer device provides an abstraction for the graphics + hardware. It represents the frame buffer of some video hardware and + allows application software to access the graphics hardware through + a well-defined interface, so the software doesn't need to know + anything about the low-level (hardware register) stuff. + + Frame buffer devices work identically across the different + architectures supported by Linux and make the implementation of + application programs easier and more portable; at this point, an X + server exists which uses the frame buffer device exclusively. + On several non-X86 architectures, the frame buffer device is the + only way to use the graphics hardware. + + The device is accessed through special device nodes, usually located + in the /dev directory, i.e. /dev/fb*. + + You need an utility program called fbset to make full use of frame + buffer devices. Please read <file:Documentation/fb/framebuffer.txt> + and the Framebuffer-HOWTO at + <http://www.tahallah.demon.co.uk/programming/prog.html> for more + information. + + Say Y here and to the driver for your graphics board below if you + are compiling a kernel for a non-x86 architecture. + + If you are compiling for the x86 architecture, you can say Y if you + want to play with it, but it is not essential. Please note that + running graphical applications that directly touch the hardware + (e.g. an accelerated X server) and that are not frame buffer + device-aware may cause unexpected results. If unsure, say N. + +config FB_G364 + bool + depends on MIPS_MAGNUM_4000 || OLIVETTI_M700 + default y + +config HAVE_STD_PC_SERIAL_PORT + bool + depends on DDB5476 || DDB5074 || MIPS_MALTA + default y + +config VR4181 + bool + depends on NEC_OSPREY + default y + +config ARC_CONSOLE + bool "ARC console support" + depends on SGI_IP22 || SNI_RM200_PCI + +config ARC_MEMORY + bool + depends on SNI_RM200_PCI || SGI_IP32 + default y + +config ARC_PROMLIB + bool + depends on SNI_RM200_PCI || SGI_IP32 || SGI_IP22 + default y + +config BOARD_SCACHE + bool + depends on MIPS_EV96100 || MOMENCO_OCELOT || SGI_IP22 + default y + +config ARC64 + bool + depends on SGI_IP27 + default y + +config BOOT_ELF64 + bool + depends on SGI_IP27 + default y + +#config MAPPED_PCI_IO y +# bool +# depends on SGI_IP27 +# default y + +config QL_ISP_A64 + bool + depends on SGI_IP27 + default y + +config TOSHIBA_BOARDS + bool + depends on TOSHIBA_JMR3927 || TOSHIBA_RBTX4927 + default y + +config TANBAC_TB0219 + bool "Added TANBAC TB0219 Base board support" + depends on TANBAC_TB0229 + +endmenu + + +menu "CPU selection" + +choice + prompt "CPU type" + default CPU_R4X00 + +config CPU_MIPS32 + bool "MIPS32" + +config CPU_MIPS64 + bool "MIPS64" + +config CPU_R3000 + bool "R3000" + depends on MIPS32 + help + Please make sure to pick the right CPU type. Linux/MIPS is not + designed to be generic, i.e. Kernels compiled for R3000 CPUs will + *not* work on R4000 machines and vice versa. However, since most + of the supported machines have an R4000 (or similar) CPU, R4x00 + might be a safe bet. If the resulting kernel does not work, + try to recompile with R3000. + +config CPU_TX39XX + bool "R39XX" + depends on MIPS32 + +config CPU_VR41XX + bool "R41xx" + help + The options selects support for the NEC VR41xx series of processors. + Only choose this option if you have one of these processors as a + kernel built with this option will not run on any other type of + processor or vice versa. + +config CPU_R4300 + bool "R4300" + help + MIPS Technologies R4300-series processors. + +config CPU_R4X00 + bool "R4x00" + help + MIPS Technologies R4000-series processors other than 4300, including + the R4000, R4400, R4600, and 4700. + +config CPU_TX49XX + bool "R49XX" + +config CPU_R5000 + bool "R5000" + help + MIPS Technologies R5000-series processors other than the Nevada. + +config CPU_R5432 + bool "R5432" + +config CPU_R6000 + bool "R6000" + depends on MIPS32 && EXPERIMENTAL + help + MIPS Technologies R6000 and R6000A series processors. Note these + processors are extremly rare and the support for them is incomplete. + +config CPU_NEVADA + bool "R52xx" + help + MIPS Technologies R52x0-series ("Nevada") processors. + +config CPU_R8000 + bool "R8000" + depends on MIPS64 && EXPERIMENTAL + help + MIPS Technologies R8000 processors. Note these processors are + uncommon and the support for them is incomplete. + +config CPU_R10000 + bool "R10000" + help + MIPS Technologies R10000-series processors. + +config CPU_RM7000 + bool "RM7000" + +config CPU_SB1 + bool "SB1" + +endchoice + +config R5000_CPU_SCACHE + bool + depends on CPU_NEVADA || CPU_R5000 + default y if SGI_IP32 || LASAT + +config BOARD_SCACHE + bool + depends on CPU_NEVADA || CPU_R4X00 || CPU_R5000 + default y if SGI_IP22 || (SGI_IP32 && CPU_R5000) || R5000_CPU_SCACHE + +config CPU_HAS_PREFETCH + bool "Enable prefetches" if CPU_SB1 && !CPU_SB1_PASS_2 + default y if CPU_RM7000 || CPU_MIPS64 || CPU_MIPS32 + +config VTAG_ICACHE + bool "Support for Virtual Tagged I-cache" if CPU_MIPS64 || CPU_MIPS32 + default y if CPU_SB1 + +choice + prompt "SB1 Pass" + depends on CPU_SB1 + default CPU_SB1_PASS_1 + +config CPU_SB1_PASS_1 + bool "Pass1" + +config CPU_SB1_PASS_2 + bool "Pass2" + +config CPU_SB1_PASS_2_2 + bool "Pass2.2" + +endchoice + +config SB1_PASS_1_WORKAROUNDS + bool + depends on CPU_SB1_PASS_1 + default y + +config SB1_PASS_2_WORKAROUNDS + bool + depends on CPU_SB1 && (CPU_SB1_PASS_2_2 || CPU_SB1_PASS_2) + default y + +# Avoid prefetches on Pass 2 (before 2.2) +# XXXKW for now, let 2.2 use same WORKAROUNDS flag as pre-2.2 +config SB1_CACHE_ERROR + bool "Support for SB1 Cache Error handler" + depends on CPU_SB1 + +config SB1_CERR_IGNORE_RECOVERABLE + bool "Ignore recoverable cache errors" + depends on SB1_CACHE_ERROR + +config SB1_CERR_SPIN + bool "Spin instead of running handler" + depends on SB1_CACHE_ERROR + +config 64BIT_PHYS_ADDR + bool "Support for 64-bit physical address space" + depends on (CPU_R4X00 || CPU_R5000 || CPU_RM7000 || CPU_R10000 || CPU_SB1 || CPU_MIPS32 || CPU_MIPS64) && MIPS32 + +config CPU_ADVANCED + bool "Override CPU Options" + depends on MIPS32 + help + Saying yes here allows you to select support for various features + your CPU may or may not have. Most people should say N here. + +config CPU_HAS_LLSC + bool "ll/sc Instructions available" if CPU_ADVANCED + default y if !CPU_ADVANCED && !CPU_R3000 && !CPU_VR41XX && !CPU_TX39XX + help + MIPS R4000 series and later provide the Load Linked (ll) + and Store Conditional (sc) instructions. More information is + available at <http://www.go-ecs.com/mips/miptek1.htm>. + + Say Y here if your CPU has the ll and sc instructions. Say Y here + for better performance, N if you don't know. You must say Y here + for multiprocessor machines. + +config CPU_HAS_LLDSCD + bool "lld/scd Instructions available" if CPU_ADVANCED + default y if !CPU_ADVANCED && !CPU_R3000 && !CPU_VR41XX && !CPU_TX39XX && !CPU_MIPS32 + help + Say Y here if your CPU has the lld and scd instructions, the 64-bit + equivalents of ll and sc. Say Y here for better performance, N if + you don't know. You must say Y here for multiprocessor machines. + +config CPU_HAS_WB + bool "Writeback Buffer available" if CPU_ADVANCED + default y if !CPU_ADVANCED && (CPU_R3000 || CPU_VR41XX || CPU_TX39XX) && DECSTATION + help + Say N here for slightly better performance. You must say Y here for + machines which require flushing of write buffers in software. Saying + Y is the safe option; N may result in kernel malfunction and crashes. + +config CPU_HAS_SYNC + bool + depends on !CPU_R3000 + default y + +# +# - Highmem only makes sense for the 32-bit kernel. +# - The current highmem code will only work properly on physically indexed +# caches such as R3000, SB1, R7000 or those that look like they're virtually +# indexed such as R4000/R4400 SC and MC versions or R10000. So for the +# moment we protect the user and offer the highmem option only on machines +# where it's known to be safe. This will not offer highmem on a few systems +# such as MIPS32 and MIPS64 CPUs which may have virtual and physically +# indexed CPUs but we're playing safe. +# - We should not offer highmem for system of which we already know that they +# don't have memory configurations that could gain from highmem support in +# the kernel because they don't support configurations with RAM at physical +# addresses > 0x20000000. +# +config HIGHMEM + bool "High Memory Support" + depends on MIPS32 && (CPU_R3000 || CPU_SB1 || CPU_R7000 || CPU_R10000) && !(BAGET_MIPS || DECSTATION) + +config SMP + bool "Multi-Processing support" + depends on SIBYTE_SB1xxx_SOC && SIBYTE_SB1250 && !SIBYTE_STANDALONE || SGI_IP27 + ---help--- + This enables support for systems with more than one CPU. If you have + a system with only one CPU, like most personal computers, say N. If + you have a system with more than one CPU, say Y. + + If you say N here, the kernel will run on single and multiprocessor + machines, but will use only one CPU of a multiprocessor machine. If + you say Y here, the kernel will run on many, but not all, + singleprocessor machines. On a singleprocessor machine, the kernel + will run faster if you say N here. + + People using multiprocessor machines who say Y here should also say + Y to "Enhanced Real Time Clock Support", below. + + See also the <file:Documentation/smp.tex>, + <file:Documentation/smp.txt> and the SMP-HOWTO available at + <http://www.tldp.org/docs.html#howto>. + + If you don't know what to do here, say N. + +config NR_CPUS + int "Maximum number of CPUs (2-32)" + depends on SMP + default "32" + help + This allows you to specify the maximum number of CPUs which this + kernel will support. The maximum supported value is 32 and the + minimum value which makes sense is 2. + + This is purely to save memory - each supported CPU adds + approximately eight kilobytes to the kernel image. + +config PREEMPT + bool "Preemptible Kernel" + help + This option reduces the latency of the kernel when reacting to + real-time or interactive events by allowing a low priority process to + be preempted even if it is in kernel mode executing a system call. + This allows applications to run more reliably even when the system is + under load. + +config KALLSYMS + bool "Load all symbols for debugging/kksymoops" + help + Say Y here to let the kernel print out symbolic crash information and + symbolic stack backtraces. This increases the size of the kernel + somewhat, as all symbols have to be loaded into the kernel image. + +config DEBUG_SPINLOCK_SLEEP + bool "Sleep-inside-spinlock checking" + help + If you say Y here, various routines which may sleep will become very + noisy if they are called with a spinlock held. + +config RTC_DS1742 + bool "DS1742 BRAM/RTC support" + depends on TOSHIBA_JMR3927 || TOSHIBA_RBTX4927 + +config MIPS_INSANE_LARGE + bool "Support for large 64-bit configurations" + depends on CPU_R10000 && MIPS64 + help + MIPS R10000 does support a 44 bit / 16TB address space as opposed to + previous 64-bit processors which only supported 40 bit / 1TB. If you + need processes of more than 1TB virtual address space, say Y here. + This will result in additional memory usage, so it is not + recommended for normal users. + +config RWSEM_GENERIC_SPINLOCK + bool + default y + +endmenu + +menu "Bus options (PCI, PCMCIA, EISA, ISA, TC)" + +config PCI + bool "Support for PCI controller" + depends on MIPS_DB1000 || DDB5074 || DDB5476 || DDB5477 || HP_LASERJET || LASAT || MIPS_IVR || MIPS_ATLAS || MIPS_COBALT || MIPS_EV64120 || MIPS_EV96100 || MIPS_ITE8172 || MIPS_MALTA || MOMENCO_OCELOT || MOMENCO_OCELOT_C || MOMENCO_OCELOT_G || MIPS_PB1000 || MIPS_PB1100 || MIPS_PB1500 || NEC_EAGLE || SGI_IP27 || SGI_IP32 || SIBYTE_SB1250 || SNI_RM200_PCI || TANBAC_TB0226 || TANBAC_TB0229 || TOSHIBA_JMR3927 || TOSHIBA_RBTX4927 || VICTOR_MPC30X || ZAO_CAPCELLA + help + Find out whether you have a PCI motherboard. PCI is the name of a + bus system, i.e. the way the CPU talks to the other stuff inside + your box. Other bus systems are ISA, EISA, or VESA. If you have PCI, + say Y, otherwise N. + + The PCI-HOWTO, available from + <http://www.tldp.org/docs.html#howto>, contains valuable + information about which PCI hardware does work under Linux and which + doesn't. + +source "drivers/pci/Kconfig" + +config ISA + bool "ISA bus support" + depends on ACER_PICA_61 || SGI_IP22 || MIPS_MAGNUM_4000 || OLIVETTI_M700 || SNI_RM200_PCI + default y if TOSHIBA_RBTX4927 || DDB5476 || DDB5074 || IBM_WORKPAD || CASIO_E55 + help + Find out whether you have ISA slots on your motherboard. ISA is the + name of a bus system, i.e. the way the CPU talks to the other stuff + inside your box. Other bus systems are PCI, EISA, or VESA. ISA is + an older system, now being displaced by PCI; newer boards don't + support it. If you have ISA, say Y, otherwise N. + +# +# The SCSI bits are needed to get the SCSI code to link ... +# +config GENERIC_ISA_DMA + bool + default y if ACER_PICA_61 || MIPS_MAGNUM_4000 || OLIVETTI_M700 || SNI_RM200_PCI || SCSI + +config EISA + bool "EISA support" + depends on ISA && (SGI_IP22 || SNI_RM200_PCI) + ---help--- + The Extended Industry Standard Architecture (EISA) bus was + developed as an open alternative to the IBM MicroChannel bus. + + The EISA bus provided some of the features of the IBM MicroChannel + bus while maintaining backward compatibility with cards made for + the older ISA bus. The EISA bus saw limited use between 1988 and + 1995 when it was made obsolete by the PCI bus. + + Say Y here if you are building a kernel for an EISA-based machine. + + Otherwise, say N. + +source "drivers/eisa/Kconfig" + +config TC + bool "TURBOchannel support" + depends on DECSTATION + help + TurboChannel is a DEC (now Compaq (now HP)) bus for Alpha and MIPS + processors. Documentation on writing device drivers for TurboChannel + is available at: + <http://www.cs.arizona.edu/computer.help/policy/DIGITAL_unix/AA-PS3HD-TET1_html/TITLE.html>. + +#config ACCESSBUS +# bool "Access.Bus support" +# depends on TC + +config MMU + bool + default y + +config MCA + bool + +config SBUS + bool + +config HOTPLUG + bool "Support for hot-pluggable devices" + ---help--- + Say Y here if you want to plug devices into your computer while + the system is running, and be able to use them quickly. In many + cases, the devices can likewise be unplugged at any time too. + + One well known example of this is PCMCIA- or PC-cards, credit-card + size devices such as network cards, modems or hard drives which are + plugged into slots found on all modern laptop computers. Another + example, used on modern desktops as well as laptops, is USB. + + Enable HOTPLUG and KMOD, and build a modular kernel. Get agent + software (at <http://linux-hotplug.sourceforge.net/>) and install it. + Then your kernel will automatically call out to a user mode "policy + agent" (/sbin/hotplug) to load modules and set up software needed + to use devices as you hotplug them. + +source "drivers/pcmcia/Kconfig" + +source "drivers/pci/hotplug/Kconfig" + +endmenu + +menu "Executable file formats" + +config KCORE_ELF + bool + default y + ---help--- + If you enabled support for /proc file system then the file + /proc/kcore will contain the kernel core image. This can be used + in gdb: + + $ cd /usr/src/linux ; gdb vmlinux /proc/kcore + + You have two choices here: ELF and A.OUT. Selecting ELF will make + /proc/kcore appear in ELF core format as defined by the Executable + and Linking Format specification. Selecting A.OUT will choose the + old "a.out" format which may be necessary for some old versions + of binutils or on some architectures. + + This is especially useful if you have compiled the kernel with the + "-g" option to preserve debugging information. It is mainly used + for examining kernel data structures on the live kernel so if you + don't understand what this means or are not a kernel hacker, just + leave it at its default value ELF. + +config KCORE_AOUT + bool + +source "fs/Kconfig.binfmt" + +config BINFMT_IRIX + bool "Include IRIX binary compatibility" + depends on !CPU_LITTLE_ENDIAN && MIPS32 + +config MIPS32_COMPAT + bool "Kernel support for Linux/MIPS 32-bit binary compatibility" + depends on MIPS64 + help + Select this option if you want Linux/MIPS 32-bit binary + compatibility. Since all software available for Linux/MIPS is + currently 32-bit you should say Y here. + +config COMPAT + bool + depends on MIPS32_COMPAT + default y + +config MIPS32_O32 + bool "Kernel support for o32 binaries" + depends on MIPS32_COMPAT + help + Select this option if you want to run o32 binaries. These are pure + 32-bit binaries as used by the 32-bit Linux/MIPS port. Most of + existing binaries are in this format. + + If unsure, say Y. + +config MIPS32_N32 + bool "Kernel support for n32 binaries" + depends on MIPS32_COMPAT + help + Select this option if you want to run n32 binaries. These are + 64-bit binaries using 32-bit quantities for addressing and certain + data that would normally be 64-bit. They are used in special + cases. + + If unsure, say N. + +config BINFMT_ELF32 + bool + default y if MIPS32_O32 || MIPS32_N32 + +config PM + bool "Power Management support (EXPERIMENTAL)" + depends on EXPERIMENTAL && SOC_AU1X00 + +endmenu + +source "drivers/mtd/Kconfig" + +source "drivers/parport/Kconfig" + +source "drivers/pnp/Kconfig" + +source "drivers/base/Kconfig" + +source "drivers/block/Kconfig" + + +menu "MIPS initrd options" + depends on BLK_DEV_INITRD + +config EMBEDDED_RAMDISK + bool "Embed root filesystem ramdisk into the kernel" + +config EMBEDDED_RAMDISK_IMAGE + string "Filename of gziped ramdisk image" + depends on EMBEDDED_RAMDISK + default "ramdisk.gz" + help + This is the filename of the ramdisk image to be built into the + kernel. Relative pathnames are relative to arch/mips/ramdisk/. + The ramdisk image is not part of the kernel distribution; you must + provide one yourself. + +endmenu + +source "drivers/ide/Kconfig" + + +menu "SCSI device support" + +config SCSI + tristate "SCSI device support" + ---help--- + If you want to use a SCSI hard disk, SCSI tape drive, SCSI CD-ROM or + any other SCSI device under Linux, say Y and make sure that you know + the name of your SCSI host adapter (the card inside your computer + that "speaks" the SCSI protocol, also called SCSI controller), + because you will be asked for it. + + You also need to say Y here if you want support for the parallel + port version of the 100 MB IOMEGA ZIP drive. + + 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 scsi_mod. If you want to compile it as + a module, say M here and read <file:Documentation/modules.txt> and + <file:Documentation/scsi/scsi.txt>. However, do not compile this as a + module if your root file system (the one containing the directory /) + is located on a SCSI device. + +source "drivers/scsi/Kconfig" + +endmenu + + +menu "Old CD-ROM drivers (not SCSI, not IDE)" + depends on ISA + +config CD_NO_IDESCSI + bool "Support non-SCSI/IDE/ATAPI CDROM drives" + ---help--- + If you have a CD-ROM drive that is neither SCSI nor IDE/ATAPI, say Y + here, otherwise N. Read the CD-ROM-HOWTO, available from + <http://www.tldp.org/docs.html#howto>. + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about these CD-ROM drives. If you are unsure what you + have, say Y and find out whether you have one of the following + drives. + + For each of these drivers, a file Documentation/cdrom/{driver_name} + exists. Especially in cases where you do not know exactly which kind + of drive you have you should read there. Most of these drivers use a + file drivers/cdrom/{driver_name}.h where you can define your + interface parameters and switch some internal goodies. + + All these CD-ROM drivers are also usable as a module ( = code which + can be inserted in and removed from the running kernel whenever you + want). If you want to compile them as module, say M instead of Y and + read <file:Documentation/modules.txt>. + + If you want to use any of these CD-ROM drivers, you also have to + answer Y or M to "ISO 9660 CD-ROM file system support" below (this + answer will get "defaulted" for you if you enable any of the Linux + CD-ROM drivers). + +source "drivers/cdrom/Kconfig" + +endmenu + +source "drivers/md/Kconfig" + +source "drivers/message/fusion/Kconfig" + +source "drivers/ieee1394/Kconfig" + +source "drivers/message/i2o/Kconfig" + +source "net/Kconfig" + +source "net/ax25/Kconfig" + +source "net/irda/Kconfig" + +source "drivers/isdn/Kconfig" + +source "drivers/telephony/Kconfig" + +# +# input before char - char/joystick depends on it. As does USB. +# +source "drivers/input/Kconfig" + +source "drivers/char/Kconfig" + +#source drivers/misc/Config.in + +source "drivers/media/Kconfig" + +source "fs/Kconfig" + +source "drivers/video/Kconfig" + + +menu "Sound" + +config SOUND + tristate "Sound card support" + ---help--- + If you have a sound card in your computer, i.e. if it can say more + than an occasional beep, say Y. Be sure to have all the information + about your sound card and its configuration down (I/O port, + interrupt and DMA channel), because you will be asked for it. + + You want to read the Sound-HOWTO, available from + <http://www.tldp.org/docs.html#howto>. General information about + the modular sound system is contained in the files + <file:Documentation/sound/Introduction>. The file + <file:Documentation/sound/README.OSS> contains some slightly + outdated but still useful information as well. + + If you have a PnP sound card and you want to configure it at boot + time using the ISA PnP tools (read + <http://www.roestock.demon.co.uk/isapnptools/>), then you need to + compile the sound card support as a module ( = code which can be + inserted in and removed from the running kernel whenever you want) + and load that module after the PnP configuration is finished. To do + this, say M here and read <file:Documentation/modules.txt> as well + as <file:Documentation/sound/README.modules>; the module will be + called soundcore. + + I'm told that even without a sound card, you can make your computer + say more than an occasional beep, by programming the PC speaker. + Kernel patches and supporting utilities to do that are in the pcsp + package, available at <ftp://ftp.infradead.org/pub/pcsp/>. + +source "sound/Kconfig" + +endmenu + +source "drivers/usb/Kconfig" + +source "net/bluetooth/Kconfig" + + +menu "Kernel hacking" + +config CROSSCOMPILE + bool "Are you using a crosscompiler" + help + Say Y here if you are compiling the kernel on a different + architecture than the one it is intended to run on. + +config DEBUG_KERNEL + bool "Kernel debugging" + +config KGDB + bool "Remote GDB kernel debugging" + depends on DEBUG_KERNEL + help + If you say Y here, it will be possible to remotely debug the MIPS + kernel using gdb. This enlarges your kernel image disk size by + several megabytes and requires a machine with more than 16 MB, + better 32 MB RAM to avoid excessive linking time. This is only + useful for kernel hackers. If unsure, say N. + +config GDB_CONSOLE + bool "Console output to GDB" + depends on KGDB + help + If you are using GDB for remote debugging over a serial port and + would like kernel messages to be formatted into GDB $O packets so + that GDB prints them as program output, say 'Y'. + +config RUNTIME_DEBUG + bool "Enable run-time debugging" + depends on DEBUG_KERNEL + help + If you say Y here, some debugging macros will do run-time checking. + If you say N here, those macros will mostly turn to no-ops. See + include/asm-mips/debug.h for debuging macros. + If unsure, say N. + + +config MAGIC_SYSRQ + bool "Magic SysRq key" + depends on DEBUG_KERNEL + help + If you say Y here, you will have some control over the system even + if the system crashes for example during kernel debugging (e.g., you + will be able to flush the buffer cache to disk, reboot the system + immediately or dump some status information). This is accomplished + by pressing various keys while holding SysRq (Alt+PrintScreen). It + also works on a serial console (on PC hardware at least), if you + send a BREAK and then within 5 seconds a command keypress. The + keys are documented in <file:Documentation/sysrq.txt>. Don't say Y + unless you really know what this hack does. + +config MIPS_UNCACHED + bool "Run uncached" + depends on DEBUG_KERNEL && !SMP && !SGI_IP27 + help + If you say Y here there kernel will disable all CPU caches. This will + reduce the system's performance dramatically but can help finding + otherwise hard to track bugs. It can also useful if you're doing + hardware debugging with a logic analyzer and need to see all traffic + on the bus. + +config DEBUG_HIGHMEM + bool "Highmem debugging" + depends on DEBUG_KERNEL && HIGHMEM + +endmenu + +source "security/Kconfig" + +source "crypto/Kconfig" + +source "lib/Kconfig" diff --git a/arch/mips/Makefile b/arch/mips/Makefile index 43ce5b7d2a22..317374325aab 100644 --- a/arch/mips/Makefile +++ b/arch/mips/Makefile @@ -5,11 +5,11 @@ # # Copyright (C) 1994, 1995, 1996 by Ralf Baechle # DECStation modifications by Paul M. Antoine, 1996 +# Copyright (C) 2002 Maciej W. Rozycki # # 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 -# this architecture +# for "archclean" cleaning up for this architecture. # # @@ -17,16 +17,20 @@ # ifdef CONFIG_CPU_LITTLE_ENDIAN tool-prefix = mipsel-linux- +JIFFIES32 = jiffies_64 +LDFLAGS_BLOB := --format binary --oformat elf32-tradlittlemips else tool-prefix = mips-linux- +JIFFIES32 = jiffies_64 + 4 +LDFLAGS_BLOB := --format binary --oformat elf32-tradbigmips endif ifdef CONFIG_CROSSCOMPILE -CROSS_COMPILE = $(tool-prefix) +CROSS_COMPILE := $(tool-prefix) endif # -# GCC uses -G0 -mabicalls -fpic as default. We don't want PIC in the kernel +# GCC uses -G 0 -mabicalls -fpic as default. We don't want PIC in the kernel # code since it only slows down the whole thing. At some point we might make # use of global pointer optimizations but their use of $28 conflicts with # the current pointer optimization. @@ -35,218 +39,314 @@ endif # machines may also. Since BFD is incredibly buggy with respect to # crossformat linking we rely on the elf2ecoff tool for format conversion. # -GCCFLAGS := -G 0 -mno-abicalls -fno-pic -LDFLAGS_vmlinux += -static -MODFLAGS += -mlong-calls -LDFLAGS := -G 0 +cflags-y := -I $(TOPDIR)/include/asm/gcc +cflags-y += -G 0 -mno-abicalls -fno-pic -pipe +LDFLAGS_vmalinux += -G 0 -static # -N +MODFLAGS += -mlong-calls -ifdef CONFIG_REMOTE_DEBUG -CFLAGS := $(CFLAGS) -g -endif +cflags-$(CONFIG_KGDB) += -g +cflags-$(CONFIG_SB1XXX_CORELIS) += -mno-sched-prolog -fno-omit-frame-pointer + +check_gcc = $(shell if $(CC) $(1) -S -o /dev/null -xc /dev/null > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi) # # CPU-dependent compiler/assembler options for optimization. # -ifdef CONFIG_CPU_R3000 -GCCFLAGS += -mcpu=r3000 -mips1 -endif -ifdef CONFIG_CPU_R6000 -GCCFLAGS += -mcpu=r6000 -mips2 -Wa,--trap -endif -ifdef CONFIG_CPU_R4300 -GCCFLAGS += -mcpu=r4300 -mips2 -Wa,--trap -endif -ifdef CONFIG_CPU_VR41XX -GCCFLAGS += -mcpu=r4600 -mips2 -Wa,--trap -endif -ifdef CONFIG_CPU_R4X00 -GCCFLAGS += -mcpu=r4600 -mips2 -Wa,--trap -endif -ifdef CONFIG_CPU_MIPS32 -GCCFLAGS += -mcpu=r4600 -mips2 -Wa,--trap -endif -ifdef CONFIG_CPU_MIPS64 -GCCFLAGS += -mcpu=r4600 -mips2 -Wa,--trap -endif -ifdef CONFIG_CPU_R5000 -GCCFLAGS += -mcpu=r5000 -mips2 -Wa,--trap -endif -ifdef CONFIG_CPU_R5432 -GCCFLAGS += -mcpu=r5000 -mips2 -Wa,--trap -endif -ifdef CONFIG_CPU_NEVADA +cflags-$(CONFIG_CPU_R3000) += -mcpu=r3000 -mips1 +cflags-$(CONFIG_CPU_TX39XX) += -mcpu=r3000 -mips1 +cflags-$(CONFIG_CPU_R6000) += -mcpu=r6000 -mips2 -Wa,--trap +cflags-$(CONFIG_CPU_R4300) += -mcpu=r4300 -mips2 -Wa,--trap +cflags-$(CONFIG_CPU_VR41XX) += -mcpu=r4600 -mips2 -Wa,--trap +cflags-$(CONFIG_CPU_R4X00) += -mcpu=r4600 -mips2 -Wa,--trap +cflags-$(CONFIG_CPU_TX49XX) += -mcpu=r4600 -mips2 -Wa,--trap +cflags-$(CONFIG_CPU_MIPS32) += -mcpu=r4600 -mips2 -Wa,--trap +cflags-$(CONFIG_CPU_MIPS64) += -mcpu=r4600 -mips2 -Wa,--trap +cflags-$(CONFIG_CPU_R5000) += -mcpu=r5000 -mips2 -Wa,--trap +cflags-$(CONFIG_CPU_R5432) += -mcpu=r5000 -mips2 -Wa,--trap # Cannot use -mmad with currently recommended tools -GCCFLAGS += -mcpu=r5000 -mips2 -Wa,--trap -endif -ifdef CONFIG_CPU_RM7000 -GCCFLAGS += -mcpu=r5000 -mips2 -Wa,--trap -endif +cflags-$(CONFIG_CPU_NEVADA) += -mcpu=r5000 -mips2 -Wa,--trap +cflags-$(CONFIG_CPU_RM7000) += -mcpu=r5000 -mips2 -Wa,--trap +cflags-$(CONFIG_CPU_RM7000) += $(call check_gcc, -mcpu=r7000, -mcpu=r5000) \ + -mips2 -Wa,--trap +cflags-$(CONFIG_CPU_SB1) += $(call check_gcc, -mcpu=sb1, -mcpu=r8000) \ + -mips2 -Wa,--trap ifdef CONFIG_CPU_SB1 -GCCFLAGS += -mcpu=sb1 -mips2 -Wa,--trap +ifdef CONFIG_SB1_PASS_1_WORKAROUNDS +MODFLAGS += -msb1-pass1-workarounds endif +endif + +AFLAGS += $(cflags-y) +CFLAGS += $(cflags-y) -GCCFLAGS += -pipe -CFLAGS := -I $(TOPDIR)/include/asm/gcc $(CFLAGS) $(GCCFLAGS) -AFLAGS += $(GCCFLAGS) -ASFLAGS += $(GCCFLAGS) +# +# ramdisk/initrd support +# You need a compressed ramdisk image, named ramdisk.gz in +# arch/mips/ramdisk +# +ifdef CONFIG_EMBEDDED_RAMDISK +CORE_FILES += arch/mips/ramdisk/ramdisk.o +SUBDIRS += arch/mips/ramdisk +endif # -# We unconditionally build the math emulator +# Firmware support # -core-y += arch/mips/math-emu/ +libs-$(CONFIG_ARC) += arch/mips/arc/ +libs-$(CONFIG_SIBYTE_CFE) += arch/mips/sibyte/cfe/ # # Board-dependent options and extra files # -ifdef CONFIG_ALGOR_P4032 -core-y += arch/mips/algor/ -LOADADDR += 0x80000000 -endif # -# DECstation family +# Acer PICA 61, Mips Magnum 4000 and Olivetti M700. # -ifdef CONFIG_DECSTATION -core-y += arch/mips/dec/ -libs-y += arch/mips/dec/prom/ -LOADADDR += 0x80040000 -endif +core-$(CONFIG_MIPS_JAZZ) += arch/mips/jazz/ +load-$(CONFIG_MIPS_JAZZ) += 0x80080000 -ifdef CONFIG_MIPS_ATLAS -core-y += arch/mips/mips-boards/atlas/ arch/mips/mips-boards/generic/ -LOADADDR += 0x80100000 -endif -ifdef CONFIG_MIPS_MALTA -core-y += arch/mips/mips-boards/malta/ arch/mips/mips-boards/generic/ -LOADADDR += 0x80100000 -endif +# +# Au1500 (Alchemy Semi PB1500) eval board +# +core-$(CONFIG_MIPS_PB1500) += arch/mips/au1000/common/ +libs-$(CONFIG_MIPS_PB1500) += arch/mips/au1000/pb1500/ +load-$(CONFIG_MIPS_PB1500) += 0x80100000 # -# Acer PICA 61, Mips Magnum 4000 and Olivetti M700. +# Baget/MIPS # -ifdef CONFIG_MIPS_JAZZ -core-y += arch/mips/jazz/ -libs-y += arch/mips/arc/ -LOADADDR += 0x80080000 -endif +libs-$(CONFIG_BAGET_MIPS) += arch/mips/baget/ arch/mips/baget/prom/ +load-$(CONFIG_BAGET_MIPS) += 0x80001000 -ifdef CONFIG_SNI_RM200_PCI -core-y += arch/mips/sni/ -libs-y += arch/mips/arc/ -LOADADDR += 0x80080000 -endif +# +# Cobalt Server +# +core-$(CONFIG_MIPS_COBALT) += arch/mips/cobalt/ +load-$(CONFIG_MIPS_COBALT) += 0x80080000 -ifdef CONFIG_SGI_IP22 -core-y += arch/mips/sgi/kernel/ -libs-y += arch/mips/arc/ # -# Set LOADADDR to >= 0x88069000 if you want to leave space for symmon, -# 0x88002000 for production kernels. Note that the value must be -# 8kb aligned or the handling of the current variable will break. +# DECstation family # -LOADADDR += 0x88002000 -endif +core-$(CONFIG_DECSTATION) += arch/mips/dec/ +libs-$(CONFIG_DECSTATION) += arch/mips/dec/prom/ +load-$(CONFIG_DECSTATION) += 0x80040000 +CLEAN_FILES += drivers/tc/lk201-map.c # -# Baget/MIPS +# Galileo EV64120 Board # -ifdef CONFIG_BAGET_MIPS -core-y += arch/mips/baget/ -libs-y += arch/mips/baget/prom/ -LOADADDR += 0x80001000 -endif +core-$(CONFIG_MIPS_EV64120) += arch/mips/galileo-boards/ev64120/ +load-$(CONFIG_MIPS_EV64120) += 0x80100000 # -# NEC DDB Vrc-5074 +# Galileo EV96100 Board # -ifdef CONFIG_DDB5074 -core-y += arch/mips/ddb5074/ -LOADADDR += 0x80080000 -endif +core-$(CONFIG_MIPS_EV96100) += arch/mips/galileo-boards/generic/ \ + arch/mips/galileo-boards/ev96100/ +load-$(CONFIG_MIPS_EV96100) += 0x80100000 # +# Globespan IVR eval board with QED 5231 CPU # -# NEC DDB Vrc-5476 +core-$(CONFIG_ITE_BOARD_GEN) += arch/mips/ite-boards/generic/ +core-$(CONFIG_MIPS_IVR) += arch/mips/ite-boards/ivr/ +load-$(CONFIG_MIPS_IVR) += 0x80100000 + # -ifdef CONFIG_DDB5476 -core-y += arch/mips/ddb5476/ -LOADADDR += 0x80080000 -endif +# HP LaserJet +# +core-$(CONFIG_HP_LASERJET) += arch/mips/hp-lj/ +load-$(CONFIG_HP_LASERJET) += 0x80030000 # +# ITE 8172 eval board with QED 5231 CPU # -# NEC DDB Vrc-5477 +core-$(CONFIG_MIPS_ITE8172) += arch/mips/ite-boards/qed-4n-s01b/ +load-$(CONFIG_MIPS_ITE8172) += 0x80100000 + # -ifdef CONFIG_DDB5477 -core-y += arch/mips/ddb5xxx/common/ \ - arch/mips/ddb5xxx/ddb5477/ -LOADADDR += 0x80080000 -endif +# MIPS Atlas board +# +core-$(CONFIG_MIPS_BOARDS_GEN) += arch/mips/mips-boards/generic/ +core-$(CONFIG_MIPS_ATLAS) += arch/mips/mips-boards/atlas/ +load-$(CONFIG_MIPS_ATLAS) += 0x80100000 # -# Galileo EV64120 Board +# MIPS Malta board # -ifdef CONFIG_MIPS_EV64120 -core-y += arch/mips/galileo-boards/ev64120/ -LOADADDR += 0x80100000 -endif +core-$(CONFIG_MIPS_MALTA) += arch/mips/mips-boards/malta/ +load-$(CONFIG_MIPS_MALTA) := 0x80100000 # -# Galileo EV96100 Board +# MIPS SEAD board # -ifdef CONFIG_MIPS_EV96100 -core-y += arch/mips/galileo-boards/ev96100/ \ - arch/mips/galileo-boards/generic/ -LOADADDR += 0x80100000 -endif +core-$(CONFIG_MIPS_SEAD) += arch/mips/mips-boards/sead/ +load-$(CONFIG_MIPS_SEAD) += 0x80100000 # # Momentum Ocelot board # -ifdef CONFIG_MOMENCO_OCELOT # The Ocelot setup.o must be linked early - it does the ioremap() for the # mips_io_port_base. -core-y += arch/mips/gt64120/common/ \ - arch/mips/gt64120/momenco_ocelot/ -LOADADDR += 0x80100000 -endif +# +core-$(CONFIG_MOMENCO_OCELOT) += arch/mips/gt64120/common/ \ + arch/mips/gt64120/momenco_ocelot/ +load-$(CONFIG_MOMENCO_OCELOT) += 0x80100000 # -# Philips Nino +# Momentum Ocelot-G board # -ifdef CONFIG_NINO -core-y += arch/mips/philips/nino/ -LOADADDR += 0x80000000 -endif +# The Ocelot-G setup.o must be linked early - it does the ioremap() for the +# mips_io_port_base. +# +core-$(CONFIG_MOMENCO_OCELOT_G) += arch/mips/momentum/ocelot_g/ +load-$(CONFIG_MOMENCO_OCELOT_G) += 0x80100000 # -# ITE 8172 eval board with QED 5231 CPU +# Momentum Ocelot-C and -CS boards # -ifdef CONFIG_MIPS_ITE8172 -core-y += arch/mips/ite-boards/qed-4n-s01b/ \ - arch/mips/ite-boards/generic/ -LOADADDR += 0x80100000 -endif +# The Ocelot-C[S] setup.o must be linked early - it does the ioremap() for the +# mips_io_port_base. +core-$(CONFIG_MOMENCO_OCELOT_C) += arch/mips/momentum/ocelot_c/ +load-$(CONFIG_MOMENCO_OCELOT_C) += 0x80100000 # -# Globespan IVR eval board with QED 5231 CPU +# NEC DDB Vrc-5074 # -ifdef CONFIG_MIPS_IVR -core-y += arch/mips/ite-boards/ivr/ \ - arch/mips/ite-boards/generic/ -LOADADDR += 0x80100000 -endif +core-$(CONFIG_DDB5XXX_COMMON) += arch/mips/ddb5xxx/common/ +core-$(CONFIG_DDB5074) += arch/mips/ddb5xxx/ddb5074/ +load-$(CONFIG_DDB5074) += 0x80080000 # -# Au1000 eval board +# NEC DDB Vrc-5476 # -ifdef CONFIG_MIPS_PB1000 -core-y += arch/mips/au1000/pb1000/ \ - arch/mips/au1000/common/ -LOADADDR += 0x80100000 -endif +core-$(CONFIG_DDB5476) += arch/mips/ddb5xxx/ddb5476/ +load-$(CONFIG_DDB5476) += 0x80080000 + +# +# NEC DDB Vrc-5477 +# +core-$(CONFIG_DDB5477) += arch/mips/ddb5xxx/ddb5477/ +load-$(CONFIG_DDB5477) += 0x80100000 + +core-$(CONFIG_LASAT) += arch/mips/lasat/ +load-$(CONFIG_LASAT) += 0x80000000 + +# +# NEC Osprey (vr4181) board +# +core-$(CONFIG_NEC_OSPREY) += arch/mips/vr4181/common/ \ + arch/mips/vr4181/osprey/ +load-$(CONFIG_NEC_OSPREY) += 0x80002000 + +# +# NEC Eagle/Hawk (VR4122/VR4131) board +# +core-$(CONFIG_VR41XX_COMMON) += arch/mips/vr41xx/common/ +core-$(CONFIG_NEC_EAGLE) += arch/mips/vr41xx/nec-eagle/ +load-$(CONFIG_NEC_EAGLE) += 0x80000000 + +# +# ZAO Networks Capcella (VR4131) +# +core-$(CONFIG_ZAO_CAPCELLA) += arch/mips/vr41xx/zao-capcella/ +load-$(CONFIG_ZAO_CAPCELLA) += 0x80000000 + +# +# Victor MP-C303/304 (VR4122) +# +core-$(CONFIG_VICTOR_MPC30X) += arch/mips/vr41xx/victor-mpc30x/ +load-$(CONFIG_VICTOR_MPC30X) += 0x80001000 + +# +# IBM WorkPad z50 (VR4121) +# +core-$(CONFIG_IBM_WORKPAD) += arch/mips/vr41xx/ibm-workpad/ +load-$(CONFIG_IBM_WORKPAD) += 0x80004000 + +# +# CASIO CASSIPEIA E-55/65 (VR4111) +# +core-$(CONFIG_CASIO_E55) += arch/mips/vr41xx/casio-e55/ +load-$(CONFIG_CASIO_E55) += 0x80004000 + +# +# TANBAC TB0226 Mbase (VR4131) +# +core-$(CONFIG_TANBAC_TB0226) += arch/mips/vr41xx/tanbac-tb0226/ +load-$(CONFIG_TANBAC_TB0226) += 0x80000000 + +# +# TANBAC TB0229 VR4131DIMM (VR4131) +# +core-$(CONFIG_TANBAC_TB0229) += arch/mips/vr41xx/tanbac-tb0229/ +load-$(CONFIG_TANBAC_TB0229) += 0x80000000 + +# +# SGI IP22 (Indy/Indigo2) +# +# Set the load address to >= 0x88069000 if you want to leave space for symmon, +# 0x88002000 for production kernels. Note that the value must be 8kb aligned +# or the handling of the current variable will break. +# +core-$(CONFIG_SGI_IP22) += arch/mips/sgi-ip22/ +load-$(CONFIG_SGI_IP22) += 0x88002000 + +# +# Sibyte SB1250 SOC +# +# This is a LIB so that it links at the end, and initcalls are later +# the sequence; but it is built as an object so that modules don't get +# removed (as happens, even if they have __initcall/module_init) +# +core-$(CONFIG_SIBYTE_BCM112X) += arch/mips/sibyte/sb1250/ +core-$(CONFIG_SIBYTE_SB1250) += arch/mips/sibyte/sb1250/ + +# +# Sibyte BCM91120x (Carmel) board +# Sibyte BCM91120C (CRhine) board +# Sibyte BCM91125C (CRhone) board +# Sibyte BCM91125E (Rhone) board +# Sibyte SWARM board +# +libs-$(CONFIG_SIBYTE_CARMEL) += arch/mips/sibyte/swarm/ +load-$(CONFIG_SIBYTE_CARMEL) := 0x80100000 +libs-$(CONFIG_SIBYTE_CRHINE) += arch/mips/sibyte/swarm/ +load-$(CONFIG_SIBYTE_CRHINE) := 0x80100000 +libs-$(CONFIG_SIBYTE_CRHONE) += arch/mips/sibyte/swarm/ +load-$(CONFIG_SIBYTE_CRHONE) := 0x80100000 +libs-$(CONFIG_SIBYTE_RHONE) += arch/mips/sibyte/swarm/ +load-$(CONFIG_SIBYTE_RHONE) := 0x80100000 +libs-$(CONFIG_SIBYTE_SENTOSA) += arch/mips/sibyte/swarm/ +load-$(CONFIG_SIBYTE_SENTOSA) := 0x80100000 +libs-$(CONFIG_SIBYTE_SWARM) += arch/mips/sibyte/swarm/ +load-$(CONFIG_SIBYTE_SWARM) := 0x80100000 + +# +# SNI RM200 PCI +# +core-$(CONFIG_SNI_RM200_PCI) += arch/mips/sni/ +load-$(CONFIG_SNI_RM200_PCI) += 0x80080000 + +# +# Toshiba JMR-TX3927 board +# +core-$(CONFIG_TOSHIBA_JMR3927) += arch/mips/jmr3927/rbhma3100/ \ + arch/mips/jmr3927/common/ +load-$(CONFIG_TOSHIBA_JMR3927) += 0x80050000 + +# +# Toshiba RBTX4927 board or +# Toshiba RBTX4937 board +# +core-$(CONFIG_TOSHIBA_RBTX4927) += arch/mips/tx4927/toshiba_rbtx4927/ +core-$(CONFIG_TOSHIBA_RBTX4927) += arch/mips/tx4927/common/ +load-$(CONFIG_TOSHIBA_RBTX4927) := 0x80020000 + +drivers-$(CONFIG_PCI) += arch/mips/pci/ + # # Choosing incompatible machines durings configuration will result in @@ -254,42 +354,79 @@ endif # none has been choosen above. # -AFLAGS_vmlinux.lds.o := -DLOADADDR=$(LOADADDR) +AFLAGS_vmlinux.lds.o := -D"LOADADDR=$(load-y)" -D"JIFFIES32=$(JIFFIES32)" head-y := arch/mips/kernel/head.o arch/mips/kernel/init_task.o -SUBDIRS := arch/mips/tools - -core-y += arch/mips/kernel/ arch/mips/mm/ -libs-y += arch/mips/lib/lib.a +libs-y += arch/mips/lib/ +core-y += arch/mips/kernel/ arch/mips/mm/ arch/mips/math-emu/ ifdef CONFIG_BAGET_MIPS BAGETBOOT = $(MAKE) -C arch/$(ARCH)/baget -balo: vmlinux +balo: vmlinux $(BAGETBOOT) balo endif -ifdef CONFIG_MIPS_EV64120 -GALILEOBOOT = $(MAKE) -C arch/$(ARCH)/galileo-boards/ev64120 +ifdef CONFIG_LASAT +rom.bin rom.sw: vmlinux + $(call descend,arch/mips/lasat/image,$@) +endif + +makeboot =$(Q)$(MAKE) -f scripts/Makefile.build obj=arch/mips/boot $(1) -gboot: vmlinux - $(MAKE) -C arch/$(ARCH)/galileo-boards/ev64120/compressed +# +# SNI firmware is f*cked in interesting ways ... +# +ifdef CONFIG_SNI_RM200_PCI +all: vmlinux.rm200 endif -MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot +vmlinux.ecoff vmlinux.rm200: vmlinux + +@$(call makeboot,$@) -vmlinux.ecoff: vmlinux - @$(MAKEBOOT) $@ +CLEAN_FILES += vmlinux.ecoff \ + vmlinux.rm200.tmp \ + vmlinux.rm200 archclean: - @$(MAKEBOOT) clean - rm -f arch/$(ARCH)/ld.script - $(MAKE) -C arch/$(ARCH)/tools clean - $(MAKE) -C arch/mips/baget clean + @$(MAKE) -f scripts/Makefile.clean obj=arch/mips/boot + @$(MAKE) -f scripts/Makefile.clean obj=arch/mips/baget + @$(MAKE) -f scripts/Makefile.clean obj=arch/mips/lasat archmrproper: - @$(MAKEBOOT) mrproper - $(MAKE) -C arch/$(ARCH)/tools mrproper + +# Generate <asm/offset.h +# +# The default rule is suffering from funny problems on MIPS so we using our +# own ... +# +# --------------------------------------------------------------------------- + +define filechk_gen-asm-offset.h + (set -e; \ + echo "#ifndef __ASM_OFFSET_H"; \ + echo "#define __ASM_OFFSET_H"; \ + echo "/*"; \ + echo " * DO NOT MODIFY."; \ + echo " *"; \ + echo " * This file was generated by arch/$(ARCH)/Makefile"; \ + echo " *"; \ + echo " */"; \ + echo ""; \ + sed -ne "/^@@@/s///p"; \ + echo "#endif /* __ASM_OFFSET_H */" ) +endef + +prepare: include/asm-$(ARCH)/offset.h + +arch/$(ARCH)/kernel/offset.s: include/asm include/linux/version.h \ + include/config/MARKER + +include/asm-$(ARCH)/offset.h: arch/$(ARCH)/kernel/offset.s + $(call filechk,gen-asm-offset.h) + +CLEAN_FILES += include/asm-$(ARCH)/offset.h.tmp \ + include/asm-$(ARCH)/offset.h diff --git a/arch/mips/algor/README b/arch/mips/algor/README deleted file mode 100644 index 2ea5b9c79ca9..000000000000 --- a/arch/mips/algor/README +++ /dev/null @@ -1,5 +0,0 @@ -The code for the Algorithmics P4032 evaluation board is currently under -development. I'll release it when it's up to the same strength as -the other ports. - - Ralf diff --git a/arch/mips/boot/Makefile b/arch/mips/boot/Makefile index 9f657295a767..9c8609557ca4 100644 --- a/arch/mips/boot/Makefile +++ b/arch/mips/boot/Makefile @@ -3,11 +3,9 @@ # License. See the file "COPYING" in the main directory of this archive # for more details. # -# Copyright (C) 1995, 1998, 2001 by Ralf Baechle +# Copyright (C) 1995, 1998, 2001, 2002 by Ralf Baechle # -OBJS = milo.o a.out.o - # # Some DECstations need all possible sections of an ECOFF executable # @@ -21,25 +19,33 @@ endif # Drop some uninteresting sections in the kernel. # This is only relevant for ELF kernels but doesn't hurt a.out # -drop-sections = .reginfo .mdebug +drop-sections = .reginfo .mdebug .comment .note strip-flags = $(addprefix --remove-section=,$(drop-sections)) all: vmlinux.ecoff addinitrd -vmlinux.ecoff: elf2ecoff $(TOPDIR)/vmlinux - ./elf2ecoff $(TOPDIR)/vmlinux vmlinux.ecoff $(E2EFLAGS) +vmlinux.rm200: vmlinux + $(OBJCOPY) \ + --change-addresses=0xfffffffc \ + -O elf32-tradlittlemips \ + $(strip-flags) \ + $< $@ + +vmlinux.ecoff: $(obj)/elf2ecoff vmlinux + ./elf2ecoff vmlinux $(obj)/vmlinux.ecoff $(E2EFLAGS) -elf2ecoff: elf2ecoff.c +$(obj)/elf2ecoff: $(obj)/elf2ecoff.c $(HOSTCC) -o $@ $^ -addinitrd: addinitrd.c +$(obj)/addinitrd: $(obj)/addinitrd.c $(HOSTCC) -o $@ $^ -clean: - rm -f vmlinux.ecoff - rm -f zImage zImage.tmp +archhelp: + @echo '* vmlinux.rm200 - Bootable kernel image for RM200C' -mrproper: - rm -f vmlinux.ecoff - rm -f addinitrd - rm -f elf2ecoff +CLEAN_FILES += addinitrd \ + elf2ecoff \ + vmlinux.ecoff \ + vmlinux.rm200 \ + zImage.tmp \ + zImage diff --git a/arch/mips/boot/addinitrd.c b/arch/mips/boot/addinitrd.c index 0ddaf39f0d79..38261f1ae282 100644 --- a/arch/mips/boot/addinitrd.c +++ b/arch/mips/boot/addinitrd.c @@ -8,6 +8,8 @@ #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> +#include <stdio.h> +#include <netinet/in.h> #include "ecoff.h" @@ -44,7 +46,7 @@ int main (int argc, char *argv[]) char buf[1024]; unsigned long loadaddr; unsigned long initrd_header[2]; - int i; + int i,cnt; int swab = 0; if (argc != 4) { @@ -60,7 +62,6 @@ int main (int argc, char *argv[]) die ("read aout header"); if (read (fd_vmlinux, esecs, sizeof esecs) != sizeof esecs) die ("read section headers"); - /* * check whether the file is good for us */ @@ -81,7 +82,7 @@ int main (int argc, char *argv[]) die ("open initrd"); if (fstat (fd_initrd, &st) < 0) die ("fstat initrd"); - loadaddr = ((SWAB(esecs[2].s_vaddr) + SWAB(esecs[2].s_size) + loadaddr = ((SWAB(esecs[2].s_vaddr) + SWAB(esecs[2].s_size) + MIPS_PAGE_SIZE-1) & ~MIPS_PAGE_MASK) - 8; if (loadaddr < (SWAB(esecs[2].s_vaddr) + SWAB(esecs[2].s_size))) loadaddr += MIPS_PAGE_SIZE; @@ -98,9 +99,20 @@ int main (int argc, char *argv[]) die ("write aout header"); if (write (fd_outfile, esecs, sizeof esecs) != sizeof esecs) die ("write section headers"); - while ((i = read (fd_vmlinux, buf, sizeof buf)) > 0) + /* skip padding */ + if(lseek(fd_vmlinux, SWAB(esecs[0].s_scnptr), SEEK_SET) == (off_t)-1) + die ("lseek vmlinux"); + if(lseek(fd_outfile, SWAB(esecs[0].s_scnptr), SEEK_SET) == (off_t)-1) + die ("lseek outfile"); + /* copy text segment */ + cnt = SWAB(eaout.tsize); + while (cnt) { + if ((i = read (fd_vmlinux, buf, sizeof buf)) <= 0) + die ("read vmlinux"); if (write (fd_outfile, buf, i) != i) die ("write vmlinux"); + cnt -= i; + } if (write (fd_outfile, initrd_header, sizeof initrd_header) != sizeof initrd_header) die ("write initrd header"); while ((i = read (fd_initrd, buf, sizeof buf)) > 0) diff --git a/arch/mips/boot/elf2ecoff.c b/arch/mips/boot/elf2ecoff.c index 0a9ad0ce7065..c3543d9eb266 100644 --- a/arch/mips/boot/elf2ecoff.c +++ b/arch/mips/boot/elf2ecoff.c @@ -41,487 +41,114 @@ #include <unistd.h> #include <elf.h> #include <limits.h> +#include <netinet/in.h> +#include <stdlib.h> #include "ecoff.h" /* * Some extra ELF definitions */ -#define PT_MIPS_REGINFO 0x70000000 /* Register usage information */ +#define PT_MIPS_REGINFO 0x70000000 /* Register usage information */ /* -------------------------------------------------------------------- */ struct sect { - unsigned long vaddr; - unsigned long len; + unsigned long vaddr; + unsigned long len; }; -int phcmp (); -char *saveRead (int file, off_t offset, off_t len, char *name); -int copy (int, int, off_t, off_t); -int translate_syms (int, int, off_t, off_t, off_t, off_t); -void convert_elf_hdr (Elf32_Ehdr *); -void convert_elf_phdrs (Elf32_Phdr *, int); -void convert_elf_shdrs (Elf32_Shdr *, int); -void convert_ecoff_filehdr(struct filehdr *); -void convert_ecoff_aouthdr(struct aouthdr *); -void convert_ecoff_esecs(struct scnhdr *, int); -extern int errno; int *symTypeTable; int must_convert_endian = 0; int format_bigendian = 0; -main (int argc, char **argv, char **envp) +static void copy(int out, int in, off_t offset, off_t size) { - Elf32_Ehdr ex; - Elf32_Phdr *ph; - Elf32_Shdr *sh; - Elf32_Sym *symtab; - char *shstrtab; - int strtabix, symtabix; - int i, pad; - struct sect text, data, bss; - struct filehdr efh; - struct aouthdr eah; - struct scnhdr esecs [6]; - int infile, outfile; - unsigned long cur_vma = ULONG_MAX; - int addflag = 0; - int nosecs; - - text.len = data.len = bss.len = 0; - text.vaddr = data.vaddr = bss.vaddr = 0; - - /* Check args... */ - if (argc < 3 || argc > 4) - { - usage: - fprintf (stderr, - "usage: elf2aout <elf executable> <a.out executable> [-a]\n"); - exit (1); - } - if (argc == 4) - { - if (strcmp (argv [3], "-a")) - goto usage; - addflag = 1; - } - - /* Try the input file... */ - if ((infile = open (argv [1], O_RDONLY)) < 0) - { - fprintf (stderr, "Can't open %s for read: %s\n", - argv [1], strerror (errno)); - exit (1); - } - - /* Read the header, which is at the beginning of the file... */ - i = read (infile, &ex, sizeof ex); - if (i != sizeof ex) - { - fprintf (stderr, "ex: %s: %s.\n", - argv [1], i ? strerror (errno) : "End of file reached"); - exit (1); - } - - if (ex.e_ident[EI_DATA] == ELFDATA2MSB) - format_bigendian = 1; - - if (ntohs (0xaa55) == 0xaa55) { - if (!format_bigendian) - must_convert_endian = 1; - } else { - if (format_bigendian) - must_convert_endian = 1; - } - if (must_convert_endian) - convert_elf_hdr (&ex); - - /* Read the program headers... */ - ph = (Elf32_Phdr *)saveRead (infile, ex.e_phoff, - ex.e_phnum * sizeof (Elf32_Phdr), "ph"); - if (must_convert_endian) - convert_elf_phdrs (ph, ex.e_phnum); - /* Read the section headers... */ - sh = (Elf32_Shdr *)saveRead (infile, ex.e_shoff, - ex.e_shnum * sizeof (Elf32_Shdr), "sh"); - if (must_convert_endian) - convert_elf_shdrs (sh, ex.e_shnum); - /* Read in the section string table. */ - shstrtab = saveRead (infile, sh [ex.e_shstrndx].sh_offset, - sh [ex.e_shstrndx].sh_size, "shstrtab"); - - /* Figure out if we can cram the program header into an ECOFF - header... Basically, we can't handle anything but loadable - segments, but we can ignore some kinds of segments. We can't - handle holes in the address space. Segments may be out of order, - so we sort them first. */ - - qsort (ph, ex.e_phnum, sizeof (Elf32_Phdr), phcmp); - - for (i = 0; i < ex.e_phnum; i++) - { - /* Section types we can ignore... */ - if (ph [i].p_type == PT_NULL || ph [i].p_type == PT_NOTE || - ph [i].p_type == PT_PHDR || ph [i].p_type == PT_MIPS_REGINFO) - continue; - /* Section types we can't handle... */ - else if (ph [i].p_type != PT_LOAD) - { - fprintf (stderr, "Program header %d type %d can't be converted.\n"); - exit (1); - } - /* Writable (data) segment? */ - if (ph [i].p_flags & PF_W) - { - struct sect ndata, nbss; + char ibuf[4096]; + int remaining, cur, count; - ndata.vaddr = ph [i].p_vaddr; - ndata.len = ph [i].p_filesz; - nbss.vaddr = ph [i].p_vaddr + ph [i].p_filesz; - nbss.len = ph [i].p_memsz - ph [i].p_filesz; - - combine (&data, &ndata, 0); - combine (&bss, &nbss, 1); + /* Go to the start of the ELF symbol table... */ + if (lseek(in, offset, SEEK_SET) < 0) { + perror("copy: lseek"); + exit(1); } - else - { - struct sect ntxt; - ntxt.vaddr = ph [i].p_vaddr; - ntxt.len = ph [i].p_filesz; - - combine (&text, &ntxt, 0); - } - /* Remember the lowest segment start address. */ - if (ph [i].p_vaddr < cur_vma) - cur_vma = ph [i].p_vaddr; - } - - /* Sections must be in order to be converted... */ - if (text.vaddr > data.vaddr || data.vaddr > bss.vaddr || - text.vaddr + text.len > data.vaddr || data.vaddr + data.len > bss.vaddr) - { - fprintf (stderr, "Sections ordering prevents a.out conversion.\n"); - exit (1); - } - - /* If there's a data section but no text section, then the loader - combined everything into one section. That needs to be the - text section, so just make the data section zero length following - text. */ - if (data.len && !text.len) - { - text = data; - data.vaddr = text.vaddr + text.len; - data.len = 0; - } - - /* If there is a gap between text and data, we'll fill it when we copy - the data, so update the length of the text segment as represented in - a.out to reflect that, since a.out doesn't allow gaps in the program - address space. */ - if (text.vaddr + text.len < data.vaddr) - text.len = data.vaddr - text.vaddr; - - /* We now have enough information to cons up an a.out header... */ - eah.magic = OMAGIC; - eah.vstamp = 200; - eah.tsize = text.len; - eah.dsize = data.len; - eah.bsize = bss.len; - eah.entry = ex.e_entry; - eah.text_start = text.vaddr; - eah.data_start = data.vaddr; - eah.bss_start = bss.vaddr; - eah.gprmask = 0xf3fffffe; - memset (&eah.cprmask, '\0', sizeof eah.cprmask); - eah.gp_value = 0; /* unused. */ - - if (format_bigendian) - efh.f_magic = MIPSEBMAGIC; - else - efh.f_magic = MIPSELMAGIC; - if (addflag) - nosecs = 6; - else - nosecs = 3; - efh.f_nscns = nosecs; - efh.f_timdat = 0; /* bogus */ - efh.f_symptr = 0; - efh.f_nsyms = 0; - efh.f_opthdr = sizeof eah; - efh.f_flags = 0x100f; /* Stripped, not sharable. */ - - memset (esecs, 0, sizeof esecs); - strcpy (esecs [0].s_name, ".text"); - strcpy (esecs [1].s_name, ".data"); - strcpy (esecs [2].s_name, ".bss"); - if (addflag) { - strcpy (esecs [3].s_name, ".rdata"); - strcpy (esecs [4].s_name, ".sdata"); - strcpy (esecs [5].s_name, ".sbss"); - } - esecs [0].s_paddr = esecs [0].s_vaddr = eah.text_start; - esecs [1].s_paddr = esecs [1].s_vaddr = eah.data_start; - esecs [2].s_paddr = esecs [2].s_vaddr = eah.bss_start; - if (addflag) { - esecs [3].s_paddr = esecs [3].s_vaddr = 0; - esecs [4].s_paddr = esecs [4].s_vaddr = 0; - esecs [5].s_paddr = esecs [5].s_vaddr = 0; - } - esecs [0].s_size = eah.tsize; - esecs [1].s_size = eah.dsize; - esecs [2].s_size = eah.bsize; - if (addflag) { - esecs [3].s_size = 0; - esecs [4].s_size = 0; - esecs [5].s_size = 0; - } - esecs [0].s_scnptr = N_TXTOFF (efh, eah); - esecs [1].s_scnptr = N_DATOFF (efh, eah); -#define ECOFF_SEGMENT_ALIGNMENT(a) 0x10 -#define ECOFF_ROUND(s,a) (((s)+(a)-1)&~((a)-1)) - esecs [2].s_scnptr = esecs [1].s_scnptr + - ECOFF_ROUND (esecs [1].s_size, ECOFF_SEGMENT_ALIGNMENT (&eah)); - if (addflag) { - esecs [3].s_scnptr = 0; - esecs [4].s_scnptr = 0; - esecs [5].s_scnptr = 0; - } - esecs [0].s_relptr = esecs [1].s_relptr - = esecs [2].s_relptr = 0; - esecs [0].s_lnnoptr = esecs [1].s_lnnoptr - = esecs [2].s_lnnoptr = 0; - esecs [0].s_nreloc = esecs [1].s_nreloc = esecs [2].s_nreloc = 0; - esecs [0].s_nlnno = esecs [1].s_nlnno = esecs [2].s_nlnno = 0; - if (addflag) { - esecs [3].s_relptr = esecs [4].s_relptr - = esecs [5].s_relptr = 0; - esecs [3].s_lnnoptr = esecs [4].s_lnnoptr - = esecs [5].s_lnnoptr = 0; - esecs [3].s_nreloc = esecs [4].s_nreloc = esecs [5].s_nreloc = 0; - esecs [3].s_nlnno = esecs [4].s_nlnno = esecs [5].s_nlnno = 0; - } - esecs [0].s_flags = 0x20; - esecs [1].s_flags = 0x40; - esecs [2].s_flags = 0x82; - if (addflag) { - esecs [3].s_flags = 0x100; - esecs [4].s_flags = 0x200; - esecs [5].s_flags = 0x400; - } - - /* Make the output file... */ - if ((outfile = open (argv [2], O_WRONLY | O_CREAT, 0777)) < 0) - { - fprintf (stderr, "Unable to create %s: %s\n", argv [2], strerror (errno)); - exit (1); - } - - if (must_convert_endian) - convert_ecoff_filehdr (&efh); - /* Write the headers... */ - i = write (outfile, &efh, sizeof efh); - if (i != sizeof efh) - { - perror ("efh: write"); - exit (1); - - for (i = 0; i < nosecs; i++) - { - printf ("Section %d: %s phys %x size %x file offset %x\n", - i, esecs [i].s_name, esecs [i].s_paddr, - esecs [i].s_size, esecs [i].s_scnptr); - } - } - fprintf (stderr, "wrote %d byte file header.\n", i); - - if (must_convert_endian) - convert_ecoff_aouthdr (&eah); - i = write (outfile, &eah, sizeof eah); - if (i != sizeof eah) - { - perror ("eah: write"); - exit (1); - } - fprintf (stderr, "wrote %d byte a.out header.\n", i); - - if (must_convert_endian) - convert_ecoff_esecs (&esecs[0], nosecs); - i = write (outfile, &esecs, nosecs * sizeof(struct scnhdr)); - if (i != nosecs * sizeof(struct scnhdr)) - { - perror ("esecs: write"); - exit (1); - } - fprintf (stderr, "wrote %d bytes of section headers.\n", i); - - if (pad = ((sizeof efh + sizeof eah + nosecs * sizeof(struct scnhdr)) & 15)) - { - pad = 16 - pad; - i = write (outfile, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0", pad); - if (i < 0) - { - perror ("ipad: write"); - exit (1); - } - fprintf (stderr, "wrote %d byte pad.\n", i); - } - - /* Copy the loadable sections. Zero-fill any gaps less than 64k; - complain about any zero-filling, and die if we're asked to zero-fill - more than 64k. */ - for (i = 0; i < ex.e_phnum; i++) - { - /* Unprocessable sections were handled above, so just verify that - the section can be loaded before copying. */ - if (ph [i].p_type == PT_LOAD && ph [i].p_filesz) - { - if (cur_vma != ph [i].p_vaddr) - { - unsigned long gap = ph [i].p_vaddr - cur_vma; - char obuf [1024]; - if (gap > 65536) - { - fprintf (stderr, "Intersegment gap (%d bytes) too large.\n", - gap); - exit (1); + remaining = size; + while (remaining) { + cur = remaining; + if (cur > sizeof ibuf) + cur = sizeof ibuf; + remaining -= cur; + if ((count = read(in, ibuf, cur)) != cur) { + fprintf(stderr, "copy: read: %s\n", + count ? strerror(errno) : + "premature end of file"); + exit(1); } - fprintf (stderr, "Warning: %d byte intersegment gap.\n", gap); - memset (obuf, 0, sizeof obuf); - while (gap) - { - int count = write (outfile, obuf, (gap > sizeof obuf - ? sizeof obuf : gap)); - if (count < 0) - { - fprintf (stderr, "Error writing gap: %s\n", - strerror (errno)); - exit (1); - } - gap -= count; + if ((count = write(out, ibuf, cur)) != cur) { + perror("copy: write"); + exit(1); } - } -fprintf (stderr, "writing %d bytes...\n", ph [i].p_filesz); - copy (outfile, infile, ph [i].p_offset, ph [i].p_filesz); - cur_vma = ph [i].p_vaddr + ph [i].p_filesz; - } - } - - /* - * Write a page of padding for boot PROMS that read entire pages. - * Without this, they may attempt to read past the end of the - * data section, incur an error, and refuse to boot. - */ - { - char obuf[4096]; - memset(obuf, 0, sizeof obuf); - if (write(outfile, obuf, sizeof(obuf)) != sizeof(obuf)) { - fprintf(stderr, "Error writing PROM padding: %s\n", - strerror(errno)); - exit(1); - } - } - - /* Looks like we won... */ - exit (0); -} - -copy (out, in, offset, size) - int out, in; - off_t offset, size; -{ - char ibuf [4096]; - int remaining, cur, count; - - /* Go to the start of the ELF symbol table... */ - if (lseek (in, offset, SEEK_SET) < 0) - { - perror ("copy: lseek"); - exit (1); - } - - remaining = size; - while (remaining) - { - cur = remaining; - if (cur > sizeof ibuf) - cur = sizeof ibuf; - remaining -= cur; - if ((count = read (in, ibuf, cur)) != cur) - { - fprintf (stderr, "copy: read: %s\n", - count ? strerror (errno) : "premature end of file"); - exit (1); - } - if ((count = write (out, ibuf, cur)) != cur) - { - perror ("copy: write"); - exit (1); } - } } -/* Combine two segments, which must be contiguous. If pad is true, it's - okay for there to be padding between. */ -combine (base, new, pad) - struct sect *base, *new; - int pad; +/* + * Combine two segments, which must be contiguous. If pad is true, it's + * okay for there to be padding between. + */ +static void combine(struct sect *base, struct sect *new, int pad) { - if (!base -> len) - *base = *new; - else if (new -> len) - { - if (base -> vaddr + base -> len != new -> vaddr) - { - if (pad) - base -> len = new -> vaddr - base -> vaddr; - else - { - fprintf (stderr, - "Non-contiguous data can't be converted.\n"); - exit (1); - } + if (!base->len) + *base = *new; + else if (new->len) { + if (base->vaddr + base->len != new->vaddr) { + if (pad) + base->len = new->vaddr - base->vaddr; + else { + fprintf(stderr, + "Non-contiguous data can't be converted.\n"); + exit(1); + } + } + base->len += new->len; } - base -> len += new -> len; - } } -phcmp (h1, h2) - Elf32_Phdr *h1, *h2; +static int phcmp(const void *v1, const void *v2) { - if (h1 -> p_vaddr > h2 -> p_vaddr) - return 1; - else if (h1 -> p_vaddr < h2 -> p_vaddr) - return -1; - else - return 0; + const Elf32_Phdr *h1 = v1; + const Elf32_Phdr *h2 = v2; + + if (h1->p_vaddr > h2->p_vaddr) + return 1; + else if (h1->p_vaddr < h2->p_vaddr) + return -1; + else + return 0; } -char *saveRead (int file, off_t offset, off_t len, char *name) +static char *saveRead(int file, off_t offset, off_t len, char *name) { - char *tmp; - int count; - off_t off; - if ((off = lseek (file, offset, SEEK_SET)) < 0) - { - fprintf (stderr, "%s: fseek: %s\n", name, strerror (errno)); - exit (1); - } - if (!(tmp = (char *)malloc (len))) - { - fprintf (stderr, "%s: Can't allocate %d bytes.\n", name, len); - exit (1); - } - count = read (file, tmp, len); - if (count != len) - { - fprintf (stderr, "%s: read: %s.\n", - name, count ? strerror (errno) : "End of file reached"); - exit (1); - } - return tmp; + char *tmp; + int count; + off_t off; + if ((off = lseek(file, offset, SEEK_SET)) < 0) { + fprintf(stderr, "%s: fseek: %s\n", name, strerror(errno)); + exit(1); + } + if (!(tmp = (char *) malloc(len))) { + fprintf(stderr, "%s: Can't allocate %ld bytes.\n", name, + len); + exit(1); + } + count = read(file, tmp, len); + if (count != len) { + fprintf(stderr, "%s: read: %s.\n", + name, + count ? strerror(errno) : "End of file reached"); + exit(1); + } + return tmp; } #define swab16(x) \ @@ -536,101 +163,454 @@ char *saveRead (int file, off_t offset, off_t len, char *name) (((unsigned int)(x) & (unsigned int)0x00ff0000UL) >> 8) | \ (((unsigned int)(x) & (unsigned int)0xff000000UL) >> 24) )) -void convert_elf_hdr (Elf32_Ehdr *e) +static void convert_elf_hdr(Elf32_Ehdr * e) { - e->e_type = swab16(e->e_type); - e->e_machine = swab16(e->e_machine); - e->e_version = swab32(e->e_version); - e->e_entry = swab32(e->e_entry); - e->e_phoff = swab32(e->e_phoff); - e->e_shoff = swab32(e->e_shoff); - e->e_flags = swab32(e->e_flags); - e->e_ehsize = swab16(e->e_ehsize); + e->e_type = swab16(e->e_type); + e->e_machine = swab16(e->e_machine); + e->e_version = swab32(e->e_version); + e->e_entry = swab32(e->e_entry); + e->e_phoff = swab32(e->e_phoff); + e->e_shoff = swab32(e->e_shoff); + e->e_flags = swab32(e->e_flags); + e->e_ehsize = swab16(e->e_ehsize); e->e_phentsize = swab16(e->e_phentsize); - e->e_phnum = swab16(e->e_phnum); + e->e_phnum = swab16(e->e_phnum); e->e_shentsize = swab16(e->e_shentsize); - e->e_shnum = swab16(e->e_shnum); - e->e_shstrndx = swab16(e->e_shstrndx); + e->e_shnum = swab16(e->e_shnum); + e->e_shstrndx = swab16(e->e_shstrndx); } -void convert_elf_phdrs (Elf32_Phdr *p, int num) +static void convert_elf_phdrs(Elf32_Phdr * p, int num) { int i; - for (i = 0; i < num; i++,p++) { - p->p_type = swab32(p->p_type); + for (i = 0; i < num; i++, p++) { + p->p_type = swab32(p->p_type); p->p_offset = swab32(p->p_offset); - p->p_vaddr = swab32(p->p_vaddr); - p->p_paddr = swab32(p->p_paddr); + p->p_vaddr = swab32(p->p_vaddr); + p->p_paddr = swab32(p->p_paddr); p->p_filesz = swab32(p->p_filesz); - p->p_memsz = swab32(p->p_memsz); - p->p_flags = swab32(p->p_flags); - p->p_align = swab32(p->p_align); + p->p_memsz = swab32(p->p_memsz); + p->p_flags = swab32(p->p_flags); + p->p_align = swab32(p->p_align); } } -void convert_elf_shdrs (Elf32_Shdr *s, int num) +static void convert_elf_shdrs(Elf32_Shdr * s, int num) { int i; - for (i = 0; i < num; i++,s++) { - s->sh_name = swab32(s->sh_name); - s->sh_type = swab32(s->sh_type); - s->sh_flags = swab32(s->sh_flags); - s->sh_addr = swab32(s->sh_addr); - s->sh_offset = swab32(s->sh_offset); - s->sh_size = swab32(s->sh_size); - s->sh_link = swab32(s->sh_link); - s->sh_info = swab32(s->sh_info); + for (i = 0; i < num; i++, s++) { + s->sh_name = swab32(s->sh_name); + s->sh_type = swab32(s->sh_type); + s->sh_flags = swab32(s->sh_flags); + s->sh_addr = swab32(s->sh_addr); + s->sh_offset = swab32(s->sh_offset); + s->sh_size = swab32(s->sh_size); + s->sh_link = swab32(s->sh_link); + s->sh_info = swab32(s->sh_info); s->sh_addralign = swab32(s->sh_addralign); - s->sh_entsize = swab32(s->sh_entsize); + s->sh_entsize = swab32(s->sh_entsize); } } -void convert_ecoff_filehdr(struct filehdr *f) +static void convert_ecoff_filehdr(struct filehdr *f) { - f->f_magic = swab16(f->f_magic); - f->f_nscns = swab16(f->f_nscns); + f->f_magic = swab16(f->f_magic); + f->f_nscns = swab16(f->f_nscns); f->f_timdat = swab32(f->f_timdat); f->f_symptr = swab32(f->f_symptr); - f->f_nsyms = swab32(f->f_nsyms); + f->f_nsyms = swab32(f->f_nsyms); f->f_opthdr = swab16(f->f_opthdr); - f->f_flags = swab16(f->f_flags); + f->f_flags = swab16(f->f_flags); } -void convert_ecoff_aouthdr(struct aouthdr *a) +static void convert_ecoff_aouthdr(struct aouthdr *a) { - a->magic = swab16(a->magic); - a->vstamp = swab16(a->vstamp); - a->tsize = swab32(a->tsize); - a->dsize = swab32(a->dsize); - a->bsize = swab32(a->bsize); - a->entry = swab32(a->entry); + a->magic = swab16(a->magic); + a->vstamp = swab16(a->vstamp); + a->tsize = swab32(a->tsize); + a->dsize = swab32(a->dsize); + a->bsize = swab32(a->bsize); + a->entry = swab32(a->entry); a->text_start = swab32(a->text_start); a->data_start = swab32(a->data_start); - a->bss_start = swab32(a->bss_start); - a->gprmask = swab32(a->gprmask); + a->bss_start = swab32(a->bss_start); + a->gprmask = swab32(a->gprmask); a->cprmask[0] = swab32(a->cprmask[0]); a->cprmask[1] = swab32(a->cprmask[1]); a->cprmask[2] = swab32(a->cprmask[2]); a->cprmask[3] = swab32(a->cprmask[3]); - a->gp_value = swab32(a->gp_value); + a->gp_value = swab32(a->gp_value); } -void convert_ecoff_esecs(struct scnhdr *s, int num) +static void convert_ecoff_esecs(struct scnhdr *s, int num) { int i; for (i = 0; i < num; i++, s++) { - s->s_paddr = swab32(s->s_paddr); - s->s_vaddr = swab32(s->s_vaddr); - s->s_size = swab32(s->s_size); - s->s_scnptr = swab32(s->s_scnptr); - s->s_relptr = swab32(s->s_relptr); + s->s_paddr = swab32(s->s_paddr); + s->s_vaddr = swab32(s->s_vaddr); + s->s_size = swab32(s->s_size); + s->s_scnptr = swab32(s->s_scnptr); + s->s_relptr = swab32(s->s_relptr); s->s_lnnoptr = swab32(s->s_lnnoptr); - s->s_nreloc = swab16(s->s_nreloc); - s->s_nlnno = swab16(s->s_nlnno); - s->s_flags = swab32(s->s_flags); + s->s_nreloc = swab16(s->s_nreloc); + s->s_nlnno = swab16(s->s_nlnno); + s->s_flags = swab32(s->s_flags); + } +} + +int main(int argc, char *argv[]) +{ + Elf32_Ehdr ex; + Elf32_Phdr *ph; + Elf32_Shdr *sh; + char *shstrtab; + int i, pad; + struct sect text, data, bss; + struct filehdr efh; + struct aouthdr eah; + struct scnhdr esecs[6]; + int infile, outfile; + unsigned long cur_vma = ULONG_MAX; + int addflag = 0; + int nosecs; + + text.len = data.len = bss.len = 0; + text.vaddr = data.vaddr = bss.vaddr = 0; + + /* Check args... */ + if (argc < 3 || argc > 4) { + usage: + fprintf(stderr, + "usage: elf2ecoff <elf executable> <ecoff executable> [-a]\n"); + exit(1); + } + if (argc == 4) { + if (strcmp(argv[3], "-a")) + goto usage; + addflag = 1; + } + + /* Try the input file... */ + if ((infile = open(argv[1], O_RDONLY)) < 0) { + fprintf(stderr, "Can't open %s for read: %s\n", + argv[1], strerror(errno)); + exit(1); + } + + /* Read the header, which is at the beginning of the file... */ + i = read(infile, &ex, sizeof ex); + if (i != sizeof ex) { + fprintf(stderr, "ex: %s: %s.\n", + argv[1], + i ? strerror(errno) : "End of file reached"); + exit(1); + } + + if (ex.e_ident[EI_DATA] == ELFDATA2MSB) + format_bigendian = 1; + + if (ntohs(0xaa55) == 0xaa55) { + if (!format_bigendian) + must_convert_endian = 1; + } else { + if (format_bigendian) + must_convert_endian = 1; + } + if (must_convert_endian) + convert_elf_hdr(&ex); + + /* Read the program headers... */ + ph = (Elf32_Phdr *) saveRead(infile, ex.e_phoff, + ex.e_phnum * sizeof(Elf32_Phdr), + "ph"); + if (must_convert_endian) + convert_elf_phdrs(ph, ex.e_phnum); + /* Read the section headers... */ + sh = (Elf32_Shdr *) saveRead(infile, ex.e_shoff, + ex.e_shnum * sizeof(Elf32_Shdr), + "sh"); + if (must_convert_endian) + convert_elf_shdrs(sh, ex.e_shnum); + /* Read in the section string table. */ + shstrtab = saveRead(infile, sh[ex.e_shstrndx].sh_offset, + sh[ex.e_shstrndx].sh_size, "shstrtab"); + + /* Figure out if we can cram the program header into an ECOFF + header... Basically, we can't handle anything but loadable + segments, but we can ignore some kinds of segments. We can't + handle holes in the address space. Segments may be out of order, + so we sort them first. */ + + qsort(ph, ex.e_phnum, sizeof(Elf32_Phdr), phcmp); + + for (i = 0; i < ex.e_phnum; i++) { + /* Section types we can ignore... */ + if (ph[i].p_type == PT_NULL || ph[i].p_type == PT_NOTE || + ph[i].p_type == PT_PHDR + || ph[i].p_type == PT_MIPS_REGINFO) + continue; + /* Section types we can't handle... */ + else if (ph[i].p_type != PT_LOAD) { + fprintf(stderr, + "Program header %d type %d can't be converted.\n", + ex.e_phnum, ph[i].p_type); + exit(1); + } + /* Writable (data) segment? */ + if (ph[i].p_flags & PF_W) { + struct sect ndata, nbss; + + ndata.vaddr = ph[i].p_vaddr; + ndata.len = ph[i].p_filesz; + nbss.vaddr = ph[i].p_vaddr + ph[i].p_filesz; + nbss.len = ph[i].p_memsz - ph[i].p_filesz; + + combine(&data, &ndata, 0); + combine(&bss, &nbss, 1); + } else { + struct sect ntxt; + + ntxt.vaddr = ph[i].p_vaddr; + ntxt.len = ph[i].p_filesz; + + combine(&text, &ntxt, 0); + } + /* Remember the lowest segment start address. */ + if (ph[i].p_vaddr < cur_vma) + cur_vma = ph[i].p_vaddr; + } + + /* Sections must be in order to be converted... */ + if (text.vaddr > data.vaddr || data.vaddr > bss.vaddr || + text.vaddr + text.len > data.vaddr + || data.vaddr + data.len > bss.vaddr) { + fprintf(stderr, + "Sections ordering prevents a.out conversion.\n"); + exit(1); + } + + /* If there's a data section but no text section, then the loader + combined everything into one section. That needs to be the + text section, so just make the data section zero length following + text. */ + if (data.len && !text.len) { + text = data; + data.vaddr = text.vaddr + text.len; + data.len = 0; + } + + /* If there is a gap between text and data, we'll fill it when we copy + the data, so update the length of the text segment as represented in + a.out to reflect that, since a.out doesn't allow gaps in the program + address space. */ + if (text.vaddr + text.len < data.vaddr) + text.len = data.vaddr - text.vaddr; + + /* We now have enough information to cons up an a.out header... */ + eah.magic = OMAGIC; + eah.vstamp = 200; + eah.tsize = text.len; + eah.dsize = data.len; + eah.bsize = bss.len; + eah.entry = ex.e_entry; + eah.text_start = text.vaddr; + eah.data_start = data.vaddr; + eah.bss_start = bss.vaddr; + eah.gprmask = 0xf3fffffe; + memset(&eah.cprmask, '\0', sizeof eah.cprmask); + eah.gp_value = 0; /* unused. */ + + if (format_bigendian) + efh.f_magic = MIPSEBMAGIC; + else + efh.f_magic = MIPSELMAGIC; + if (addflag) + nosecs = 6; + else + nosecs = 3; + efh.f_nscns = nosecs; + efh.f_timdat = 0; /* bogus */ + efh.f_symptr = 0; + efh.f_nsyms = 0; + efh.f_opthdr = sizeof eah; + efh.f_flags = 0x100f; /* Stripped, not sharable. */ + + memset(esecs, 0, sizeof esecs); + strcpy(esecs[0].s_name, ".text"); + strcpy(esecs[1].s_name, ".data"); + strcpy(esecs[2].s_name, ".bss"); + if (addflag) { + strcpy(esecs[3].s_name, ".rdata"); + strcpy(esecs[4].s_name, ".sdata"); + strcpy(esecs[5].s_name, ".sbss"); + } + esecs[0].s_paddr = esecs[0].s_vaddr = eah.text_start; + esecs[1].s_paddr = esecs[1].s_vaddr = eah.data_start; + esecs[2].s_paddr = esecs[2].s_vaddr = eah.bss_start; + if (addflag) { + esecs[3].s_paddr = esecs[3].s_vaddr = 0; + esecs[4].s_paddr = esecs[4].s_vaddr = 0; + esecs[5].s_paddr = esecs[5].s_vaddr = 0; + } + esecs[0].s_size = eah.tsize; + esecs[1].s_size = eah.dsize; + esecs[2].s_size = eah.bsize; + if (addflag) { + esecs[3].s_size = 0; + esecs[4].s_size = 0; + esecs[5].s_size = 0; + } + esecs[0].s_scnptr = N_TXTOFF(efh, eah); + esecs[1].s_scnptr = N_DATOFF(efh, eah); +#define ECOFF_SEGMENT_ALIGNMENT(a) 0x10 +#define ECOFF_ROUND(s,a) (((s)+(a)-1)&~((a)-1)) + esecs[2].s_scnptr = esecs[1].s_scnptr + + ECOFF_ROUND(esecs[1].s_size, ECOFF_SEGMENT_ALIGNMENT(&eah)); + if (addflag) { + esecs[3].s_scnptr = 0; + esecs[4].s_scnptr = 0; + esecs[5].s_scnptr = 0; + } + esecs[0].s_relptr = esecs[1].s_relptr = esecs[2].s_relptr = 0; + esecs[0].s_lnnoptr = esecs[1].s_lnnoptr = esecs[2].s_lnnoptr = 0; + esecs[0].s_nreloc = esecs[1].s_nreloc = esecs[2].s_nreloc = 0; + esecs[0].s_nlnno = esecs[1].s_nlnno = esecs[2].s_nlnno = 0; + if (addflag) { + esecs[3].s_relptr = esecs[4].s_relptr + = esecs[5].s_relptr = 0; + esecs[3].s_lnnoptr = esecs[4].s_lnnoptr + = esecs[5].s_lnnoptr = 0; + esecs[3].s_nreloc = esecs[4].s_nreloc = esecs[5].s_nreloc = + 0; + esecs[3].s_nlnno = esecs[4].s_nlnno = esecs[5].s_nlnno = 0; + } + esecs[0].s_flags = 0x20; + esecs[1].s_flags = 0x40; + esecs[2].s_flags = 0x82; + if (addflag) { + esecs[3].s_flags = 0x100; + esecs[4].s_flags = 0x200; + esecs[5].s_flags = 0x400; + } + + /* Make the output file... */ + if ((outfile = open(argv[2], O_WRONLY | O_CREAT, 0777)) < 0) { + fprintf(stderr, "Unable to create %s: %s\n", argv[2], + strerror(errno)); + exit(1); + } + + if (must_convert_endian) + convert_ecoff_filehdr(&efh); + /* Write the headers... */ + i = write(outfile, &efh, sizeof efh); + if (i != sizeof efh) { + perror("efh: write"); + exit(1); + + for (i = 0; i < nosecs; i++) { + printf + ("Section %d: %s phys %lx size %lx file offset %lx\n", + i, esecs[i].s_name, esecs[i].s_paddr, + esecs[i].s_size, esecs[i].s_scnptr); + } + } + fprintf(stderr, "wrote %d byte file header.\n", i); + + if (must_convert_endian) + convert_ecoff_aouthdr(&eah); + i = write(outfile, &eah, sizeof eah); + if (i != sizeof eah) { + perror("eah: write"); + exit(1); + } + fprintf(stderr, "wrote %d byte a.out header.\n", i); + + if (must_convert_endian) + convert_ecoff_esecs(&esecs[0], nosecs); + i = write(outfile, &esecs, nosecs * sizeof(struct scnhdr)); + if (i != nosecs * sizeof(struct scnhdr)) { + perror("esecs: write"); + exit(1); } + fprintf(stderr, "wrote %d bytes of section headers.\n", i); + + pad = (sizeof(efh) + sizeof(eah) + nosecs * sizeof(struct scnhdr)) & 15; + if (pad) { + pad = 16 - pad; + i = write(outfile, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0", pad); + if (i < 0) { + perror("ipad: write"); + exit(1); + } + fprintf(stderr, "wrote %d byte pad.\n", i); + } + + /* + * Copy the loadable sections. Zero-fill any gaps less than 64k; + * complain about any zero-filling, and die if we're asked to zero-fill + * more than 64k. + */ + for (i = 0; i < ex.e_phnum; i++) { + /* Unprocessable sections were handled above, so just verify that + the section can be loaded before copying. */ + if (ph[i].p_type == PT_LOAD && ph[i].p_filesz) { + if (cur_vma != ph[i].p_vaddr) { + unsigned long gap = + ph[i].p_vaddr - cur_vma; + char obuf[1024]; + if (gap > 65536) { + fprintf(stderr, + "Intersegment gap (%ld bytes) too large.\n", + gap); + exit(1); + } + fprintf(stderr, + "Warning: %ld byte intersegment gap.\n", + gap); + memset(obuf, 0, sizeof obuf); + while (gap) { + int count = + write(outfile, obuf, + (gap > + sizeof obuf ? sizeof + obuf : gap)); + if (count < 0) { + fprintf(stderr, + "Error writing gap: %s\n", + strerror(errno)); + exit(1); + } + gap -= count; + } + } + fprintf(stderr, "writing %d bytes...\n", + ph[i].p_filesz); + copy(outfile, infile, ph[i].p_offset, + ph[i].p_filesz); + cur_vma = ph[i].p_vaddr + ph[i].p_filesz; + } + } + + /* + * Write a page of padding for boot PROMS that read entire pages. + * Without this, they may attempt to read past the end of the + * data section, incur an error, and refuse to boot. + */ + { + char obuf[4096]; + memset(obuf, 0, sizeof obuf); + if (write(outfile, obuf, sizeof(obuf)) != sizeof(obuf)) { + fprintf(stderr, "Error writing PROM padding: %s\n", + strerror(errno)); + exit(1); + } + } + + /* Looks like we won... */ + exit(0); } diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile index d2761db3285c..03ce3434d023 100644 --- a/arch/mips/kernel/Makefile +++ b/arch/mips/kernel/Makefile @@ -2,45 +2,45 @@ # Makefile for the Linux/MIPS kernel. # -# EXTRA_AFLAGS = -mips3 -mcpu=r4000 # not used? +extra-y := head.o init_task.o + +obj-y += branch.o cpu-probe.o process.o signal.o entry.o traps.o \ + ptrace.o irq.o reset.o semaphore.o setup.o syscall.o \ + sysmips.o ipc.o scall_o32.o time.o unaligned.o -extra-y := head.o init_task.o -obj-y += branch.o process.o signal.o entry.o \ - traps.o ptrace.o vm86.o ioport.o reset.o \ - semaphore.o setup.o syscall.o sysmips.o \ - ipc.o scall_o32.o unaligned.o obj-$(CONFIG_MODULES) += mips_ksyms.o -ifdef CONFIG_CPU_R3000 -obj-y += r2300_misc.o r2300_fpu.o r2300_switch.o -else -obj-y += r4k_misc.o r4k_switch.o -ifdef CONFIG_CPU_R6000 -obj-y += r6000_fpu.o -else -obj-y += r4k_fpu.o -endif -endif +obj-$(CONFIG_CPU_R3000) += r2300_fpu.o r2300_switch.o +obj-$(CONFIG_CPU_TX39XX) += r2300_fpu.o r2300_switch.o +obj-$(CONFIG_CPU_TX49XX) += r4k_fpu.o r4k_switch.o +obj-$(CONFIG_CPU_R4000) += r4k_fpu.o r4k_switch.o +obj-$(CONFIG_CPU_VR41XX) += r4k_fpu.o r4k_switch.o +obj-$(CONFIG_CPU_R4300) += r4k_fpu.o r4k_switch.o +obj-$(CONFIG_CPU_R4X00) += r4k_fpu.o r4k_switch.o +obj-$(CONFIG_CPU_R5000) += r4k_fpu.o r4k_switch.o +obj-$(CONFIG_CPU_R5432) += r4k_fpu.o r4k_switch.o +obj-$(CONFIG_CPU_RM7000) += r4k_fpu.o r4k_switch.o +obj-$(CONFIG_CPU_NEVADA) += r4k_fpu.o r4k_switch.o +obj-$(CONFIG_CPU_R10000) += r4k_fpu.o r4k_switch.o +obj-$(CONFIG_CPU_SB1) += r4k_fpu.o r4k_switch.o +obj-$(CONFIG_CPU_MIPS32) += r4k_fpu.o r4k_switch.o +obj-$(CONFIG_CPU_MIPS64) += r4k_fpu.o r4k_switch.o +obj-$(CONFIG_CPU_R6000) += r6000_fpu.o r4k_switch.o obj-$(CONFIG_SMP) += smp.o -# Old style irq support, going to die in 2.5. -obj-$(CONFIG_NEW_IRQ) += irq.o -obj-$(CONFIG_ROTTEN_IRQ) += old-irq.o obj-$(CONFIG_I8259) += i8259.o - -# transition from old time.c to new time.c -# some boards uses old-time.c, some use time.c, and some use their own ones -obj-$(CONFIG_OLD_TIME_C) += old-time.o -obj-$(CONFIG_NEW_TIME_C) += time.o +obj-$(CONFIG_IRQ_CPU) += irq_cpu.o obj-$(CONFIG_BINFMT_IRIX) += irixelf.o irixioctl.o irixsig.o sysirix.o \ irixinv.o -obj-$(CONFIG_REMOTE_DEBUG) += gdb-low.o gdb-stub.o -obj-$(CONFIG_PCI) += pci-dma.o +obj-$(CONFIG_KGDB) += gdb-low.o gdb-stub.o obj-$(CONFIG_PROC_FS) += proc.o -ifdef CONFIG_PCI -obj-$(CONFIG_NEW_PCI) += pci.o -obj-$(CONFIG_PCI_AUTO) += pci_auto.o +ifndef CONFIG_MAPPED_PCI_IO +obj-y += pci-dma.o endif + +obj-$(CONFIG_MODULES) += module.o + +EXTRA_AFLAGS := $(CFLAGS) diff --git a/arch/mips/kernel/branch.c b/arch/mips/kernel/branch.c index 6bba0684b71b..021aff38c946 100644 --- a/arch/mips/kernel/branch.c +++ b/arch/mips/kernel/branch.c @@ -16,7 +16,6 @@ #include <asm/inst.h> #include <asm/ptrace.h> #include <asm/uaccess.h> -#include <asm/bootinfo.h> #include <asm/processor.h> /* @@ -164,10 +163,10 @@ int __compute_return_epc(struct pt_regs *regs) * And now the FPA/cp1 branch instructions. */ case cop1_op: - if(!(mips_cpu.options & MIPS_CPU_FPU)) + if (!cpu_has_fpu) fcr31 = current->thread.fpu.soft.sr; else - asm ("cfc1\t%0,$31":"=r" (fcr31)); + asm volatile("cfc1\t%0,$31" : "=r" (fcr31)); bit = (insn.i_format.rt >> 2); bit += (bit != 0); bit += 23; diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c new file mode 100644 index 000000000000..beba0d1e82f7 --- /dev/null +++ b/arch/mips/kernel/cpu-probe.c @@ -0,0 +1,509 @@ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/stddef.h> +#include <asm/bugs.h> +#include <asm/cpu.h> +#include <asm/fpu.h> +#include <asm/mipsregs.h> + +/* + * Not all of the MIPS CPUs have the "wait" instruction available. Moreover, + * the implementation of the "wait" feature differs between CPU families. This + * points to the function that implements CPU specific wait. + * The wait instruction stops the pipeline and reduces the power consumption of + * the CPU very much. + */ +void (*cpu_wait)(void) = NULL; + +static void r3081_wait(void) +{ + unsigned long cfg = read_c0_conf(); + write_c0_conf(cfg | R30XX_CONF_HALT); +} + +static void r39xx_wait(void) +{ + unsigned long cfg = read_c0_conf(); + write_c0_conf(cfg | TX39_CONF_HALT); +} + +static void r4k_wait(void) +{ + __asm__(".set\tmips3\n\t" + "wait\n\t" + ".set\tmips0"); +} + +void au1k_wait(void) +{ +#ifdef CONFIG_PM + /* using the wait instruction makes CP0 counter unusable */ + __asm__(".set\tmips3\n\t" + "wait\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + ".set\tmips0"); +#else + __asm__("nop\n\t" + "nop"); +#endif +} + +static inline void check_wait(void) +{ + struct cpuinfo_mips *c = ¤t_cpu_data; + + printk("Checking for 'wait' instruction... "); + switch (c->cputype) { + case CPU_R3081: + case CPU_R3081E: + cpu_wait = r3081_wait; + printk(" available.\n"); + break; + case CPU_TX3927: + cpu_wait = r39xx_wait; + printk(" available.\n"); + break; + case CPU_R4200: +/* case CPU_R4300: */ + case CPU_R4600: + case CPU_R4640: + case CPU_R4650: + case CPU_R4700: + case CPU_R5000: + case CPU_NEVADA: + case CPU_RM7000: + case CPU_TX49XX: + case CPU_4KC: + case CPU_4KEC: + case CPU_4KSC: + case CPU_5KC: +/* case CPU_20KC:*/ + cpu_wait = r4k_wait; + printk(" available.\n"); + break; + case CPU_AU1000: + case CPU_AU1100: + case CPU_AU1500: + cpu_wait = au1k_wait; + printk(" available.\n"); + break; + default: + printk(" unavailable.\n"); + break; + } +} + +void __init check_bugs(void) +{ + check_wait(); +} + +/* + * Probe whether cpu has config register by trying to play with + * alternate cache bit and see whether it matters. + * It's used by cpu_probe to distinguish between R3000A and R3081. + */ +static inline int cpu_has_confreg(void) +{ +#ifdef CONFIG_CPU_R3000 + extern unsigned long r3k_cache_size(unsigned long); + unsigned long size1, size2; + unsigned long cfg = read_c0_conf(); + + size1 = r3k_cache_size(ST0_ISC); + write_c0_conf(cfg ^ R30XX_CONF_AC); + size2 = r3k_cache_size(ST0_ISC); + write_c0_conf(cfg); + return size1 != size2; +#else + return 0; +#endif +} + +/* + * Get the FPU Implementation/Revision. + */ +static inline unsigned long cpu_get_fpu_id(void) +{ + unsigned long tmp, fpu_id; + + tmp = read_c0_status(); + __enable_fpu(); + fpu_id = read_32bit_cp1_register(CP1_REVISION); + write_c0_status(tmp); + return fpu_id; +} + +/* + * Check the CPU has an FPU the official way. + */ +static inline int __cpu_has_fpu(void) +{ + return ((cpu_get_fpu_id() & 0xff00) != FPIR_IMP_NONE); +} + +#define R4K_OPTS (MIPS_CPU_TLB | MIPS_CPU_4KEX | MIPS_CPU_4KTLB \ + | MIPS_CPU_COUNTER | MIPS_CPU_CACHE_CDEX) + +__init void cpu_probe(void) +{ + struct cpuinfo_mips *c = ¤t_cpu_data; + unsigned long config0 = read_c0_config(); + unsigned long config1; + + c->processor_id = PRID_IMP_UNKNOWN; + c->fpu_id = FPIR_IMP_NONE; + c->cputype = CPU_UNKNOWN; + + if (config0 & (1 << 31)) { + /* MIPS32 or MIPS64 compliant CPU. Read Config 1 register. */ + c->options = MIPS_CPU_TLB | MIPS_CPU_4KEX | + MIPS_CPU_4KTLB | MIPS_CPU_COUNTER | MIPS_CPU_DIVEC | + MIPS_CPU_LLSC; + config1 = read_c0_config1(); + if (config1 & (1 << 3)) + c->options |= MIPS_CPU_WATCH; + if (config1 & (1 << 2)) + c->options |= MIPS_CPU_MIPS16; + if (config1 & (1 << 1)) + c->options |= MIPS_CPU_EJTAG; + if (config1 & 1) { + c->options |= MIPS_CPU_FPU; + c->options |= MIPS_CPU_32FPR; + } + c->scache.flags = MIPS_CACHE_NOT_PRESENT; + + c->tlbsize = ((config1 >> 25) & 0x3f) + 1; + } + + c->processor_id = read_c0_prid(); + switch (c->processor_id & 0xff0000) { + case PRID_COMP_LEGACY: + switch (c->processor_id & 0xff00) { + case PRID_IMP_R2000: + c->cputype = CPU_R2000; + c->isa_level = MIPS_CPU_ISA_I; + c->options = MIPS_CPU_TLB | MIPS_CPU_NOFPUEX | + MIPS_CPU_LLSC; + if (__cpu_has_fpu()) + c->options |= MIPS_CPU_FPU; + c->tlbsize = 64; + break; + case PRID_IMP_R3000: + if ((c->processor_id & 0xff) == PRID_REV_R3000A) + if (cpu_has_confreg()) + c->cputype = CPU_R3081E; + else + c->cputype = CPU_R3000A; + else + c->cputype = CPU_R3000; + c->isa_level = MIPS_CPU_ISA_I; + c->options = MIPS_CPU_TLB | MIPS_CPU_NOFPUEX | + MIPS_CPU_LLSC; + if (__cpu_has_fpu()) + c->options |= MIPS_CPU_FPU; + c->tlbsize = 64; + break; + case PRID_IMP_R4000: + if ((c->processor_id & 0xff) >= PRID_REV_R4400) + c->cputype = CPU_R4400SC; + else + c->cputype = CPU_R4000SC; + c->isa_level = MIPS_CPU_ISA_III; + c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | + MIPS_CPU_WATCH | MIPS_CPU_VCE | + MIPS_CPU_LLSC; + c->tlbsize = 48; + break; + case PRID_IMP_VR41XX: + switch (c->processor_id & 0xf0) { +#ifndef CONFIG_VR4181 + case PRID_REV_VR4111: + c->cputype = CPU_VR4111; + break; +#else + case PRID_REV_VR4181: + c->cputype = CPU_VR4181; + break; +#endif + case PRID_REV_VR4121: + c->cputype = CPU_VR4121; + break; + case PRID_REV_VR4122: + if ((c->processor_id & 0xf) < 0x3) + c->cputype = CPU_VR4122; + else + c->cputype = CPU_VR4181A; + break; + case PRID_REV_VR4131: + c->cputype = CPU_VR4131; + break; + default: + printk(KERN_INFO "Unexpected CPU of NEC VR4100 series\n"); + c->cputype = CPU_VR41XX; + break; + } + c->isa_level = MIPS_CPU_ISA_III; + c->options = R4K_OPTS; + c->tlbsize = 32; + break; + case PRID_IMP_R4300: + c->cputype = CPU_R4300; + c->isa_level = MIPS_CPU_ISA_III; + c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | + MIPS_CPU_LLSC; + c->tlbsize = 32; + break; + case PRID_IMP_R4600: + c->cputype = CPU_R4600; + c->isa_level = MIPS_CPU_ISA_III; + c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_LLSC; + c->tlbsize = 48; + break; + #if 0 + case PRID_IMP_R4650: + /* + * This processor doesn't have an MMU, so it's not + * "real easy" to run Linux on it. It is left purely + * for documentation. Commented out because it shares + * it's c0_prid id number with the TX3900. + */ + c->cputype = CPU_R4650; + c->isa_level = MIPS_CPU_ISA_III; + c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_LLSC; + c->tlbsize = 48; + break; + #endif + case PRID_IMP_TX39: + c->isa_level = MIPS_CPU_ISA_I; + c->options = MIPS_CPU_TLB; + + if ((c->processor_id & 0xf0) == + (PRID_REV_TX3927 & 0xf0)) { + c->cputype = CPU_TX3927; + c->tlbsize = 64; + } else { + switch (c->processor_id & 0xff) { + case PRID_REV_TX3912: + c->cputype = CPU_TX3912; + c->tlbsize = 32; + break; + case PRID_REV_TX3922: + c->cputype = CPU_TX3922; + c->tlbsize = 64; + break; + default: + c->cputype = CPU_UNKNOWN; + break; + } + } + break; + case PRID_IMP_R4700: + c->cputype = CPU_R4700; + c->isa_level = MIPS_CPU_ISA_III; + c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | + MIPS_CPU_LLSC; + c->tlbsize = 48; + break; + case PRID_IMP_TX49: + c->cputype = CPU_TX49XX; + c->isa_level = MIPS_CPU_ISA_III; + c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | + MIPS_CPU_LLSC; + c->tlbsize = 48; + break; + case PRID_IMP_R5000: + c->cputype = CPU_R5000; + c->isa_level = MIPS_CPU_ISA_IV; + c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | + MIPS_CPU_LLSC; + c->tlbsize = 48; + break; + case PRID_IMP_R5432: + c->cputype = CPU_R5432; + c->isa_level = MIPS_CPU_ISA_IV; + c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | + MIPS_CPU_WATCH | MIPS_CPU_LLSC; + c->tlbsize = 48; + break; + case PRID_IMP_R5500: + c->cputype = CPU_R5500; + c->isa_level = MIPS_CPU_ISA_IV; + c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | + MIPS_CPU_WATCH | MIPS_CPU_LLSC; + c->tlbsize = 48; + break; + case PRID_IMP_NEVADA: + c->cputype = CPU_NEVADA; + c->isa_level = MIPS_CPU_ISA_IV; + c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | + MIPS_CPU_DIVEC | MIPS_CPU_LLSC; + c->tlbsize = 48; + break; + case PRID_IMP_R6000: + c->cputype = CPU_R6000; + c->isa_level = MIPS_CPU_ISA_II; + c->options = MIPS_CPU_TLB | MIPS_CPU_FPU | + MIPS_CPU_LLSC; + c->tlbsize = 32; + break; + case PRID_IMP_R6000A: + c->cputype = CPU_R6000A; + c->isa_level = MIPS_CPU_ISA_II; + c->options = MIPS_CPU_TLB | MIPS_CPU_FPU | + MIPS_CPU_LLSC; + c->tlbsize = 32; + break; + case PRID_IMP_RM7000: + c->cputype = CPU_RM7000; + c->isa_level = MIPS_CPU_ISA_IV; + c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | + MIPS_CPU_LLSC; + /* + * Undocumented RM7000: Bit 29 in the info register of + * the RM7000 v2.0 indicates if the TLB has 48 or 64 + * entries. + * + * 29 1 => 64 entry JTLB + * 0 => 48 entry JTLB + */ + c->tlbsize = (read_c0_info() & (1 << 29)) ? 64 : 48; + break; + case PRID_IMP_R8000: + c->cputype = CPU_R8000; + c->isa_level = MIPS_CPU_ISA_IV; + c->options = MIPS_CPU_TLB | MIPS_CPU_4KEX | + MIPS_CPU_FPU | MIPS_CPU_32FPR | + MIPS_CPU_LLSC; + c->tlbsize = 384; /* has weird TLB: 3-way x 128 */ + break; + case PRID_IMP_R10000: + c->cputype = CPU_R10000; + c->isa_level = MIPS_CPU_ISA_IV; + c->options = MIPS_CPU_TLB | MIPS_CPU_4KEX | + MIPS_CPU_FPU | MIPS_CPU_32FPR | + MIPS_CPU_COUNTER | MIPS_CPU_WATCH | + MIPS_CPU_LLSC; + c->tlbsize = 64; + break; + case PRID_IMP_R12000: + c->cputype = CPU_R12000; + c->isa_level = MIPS_CPU_ISA_IV; + c->options = MIPS_CPU_TLB | MIPS_CPU_4KEX | + MIPS_CPU_FPU | MIPS_CPU_32FPR | + MIPS_CPU_COUNTER | MIPS_CPU_WATCH | + MIPS_CPU_LLSC; + c->tlbsize = 64; + break; + default: + c->cputype = CPU_UNKNOWN; + break; + } + break; + case PRID_COMP_MIPS: + switch (c->processor_id & 0xff00) { + case PRID_IMP_4KC: + c->cputype = CPU_4KC; + c->isa_level = MIPS_CPU_ISA_M32; + break; + case PRID_IMP_4KEC: + c->cputype = CPU_4KEC; + c->isa_level = MIPS_CPU_ISA_M32; + break; + case PRID_IMP_4KSC: + c->cputype = CPU_4KSC; + c->isa_level = MIPS_CPU_ISA_M32; + break; + case PRID_IMP_5KC: + c->cputype = CPU_5KC; + c->isa_level = MIPS_CPU_ISA_M64; + break; + case PRID_IMP_20KC: + c->cputype = CPU_20KC; + c->isa_level = MIPS_CPU_ISA_M64; + break; + default: + c->cputype = CPU_UNKNOWN; + break; + } + break; + case PRID_COMP_ALCHEMY: + switch (c->processor_id & 0xff00) { + case PRID_IMP_AU1_REV1: + case PRID_IMP_AU1_REV2: + switch ((c->processor_id >> 24) & 0xff) { + case 0: + c->cputype = CPU_AU1000; + break; + case 1: + c->cputype = CPU_AU1500; + break; + case 2: + c->cputype = CPU_AU1100; + break; + default: + panic("Unknown Au Core!"); + break; + } + c->isa_level = MIPS_CPU_ISA_M32; + break; + default: + c->cputype = CPU_UNKNOWN; + break; + } + break; + case PRID_COMP_SIBYTE: + switch (c->processor_id & 0xff00) { + case PRID_IMP_SB1: + c->cputype = CPU_SB1; + c->isa_level = MIPS_CPU_ISA_M64; + c->options = MIPS_CPU_TLB | MIPS_CPU_4KEX | + MIPS_CPU_COUNTER | MIPS_CPU_DIVEC | + MIPS_CPU_MCHECK | MIPS_CPU_EJTAG | + MIPS_CPU_WATCH | MIPS_CPU_LLSC; +#ifndef CONFIG_SB1_PASS_1_WORKAROUNDS + /* FPU in pass1 is known to have issues. */ + c->options |= MIPS_CPU_FPU | MIPS_CPU_32FPR; +#endif + break; + default: + c->cputype = CPU_UNKNOWN; + break; + } + break; + + case PRID_COMP_SANDCRAFT: + switch (c->processor_id & 0xff00) { + case PRID_IMP_SR71000: + c->cputype = CPU_SR71000; + c->isa_level = MIPS_CPU_ISA_M64; + c->options = MIPS_CPU_TLB | MIPS_CPU_4KEX | + MIPS_CPU_4KTLB | MIPS_CPU_FPU | + MIPS_CPU_COUNTER | MIPS_CPU_MCHECK; + c->scache.ways = 8; + c->tlbsize = 64; + break; + default: + c->cputype = CPU_UNKNOWN; + break; + } + break; + default: + c->cputype = CPU_UNKNOWN; + c->tlbsize = ((config1 >> 25) & 0x3f) + 1; + } + if (c->options & MIPS_CPU_FPU) + c->fpu_id = cpu_get_fpu_id(); +} + +__init void cpu_report(void) +{ + struct cpuinfo_mips *c = ¤t_cpu_data; + + printk("CPU revision is: %08x\n", c->processor_id); + if (c->options & MIPS_CPU_FPU) + printk("FPU revision is: %08x\n", c->fpu_id); +} diff --git a/arch/mips/kernel/entry.S b/arch/mips/kernel/entry.S index 3d816c8bc385..ddc3eb8e8109 100644 --- a/arch/mips/kernel/entry.S +++ b/arch/mips/kernel/entry.S @@ -1,5 +1,4 @@ /* -/* * 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. @@ -9,79 +8,155 @@ * Copyright (C) 2001 MIPS Technologies, Inc. */ #include <linux/config.h> +#include <linux/init.h> #include <linux/sys.h> #include <asm/addrspace.h> #include <asm/asm.h> -#include <asm/current.h> +#include <asm/cacheops.h> #include <asm/errno.h> #include <asm/mipsregs.h> #include <asm/page.h> -#include <asm/pgtable.h> +#include <asm/pgtable-bits.h> +#include <asm/regdef.h> #include <asm/stackframe.h> #include <asm/processor.h> -#include <asm/regdef.h> #include <asm/fpregdef.h> #include <asm/unistd.h> #include <asm/isadep.h> +#include <asm/thread_info.h> -/* This duplicates the definition from <linux/sched.h> */ -#define PT_TRACESYS 0x00000002 /* tracing system calls */ +#ifdef CONFIG_PREEMPT + .macro preempt_stop + cli + .endm + .macro init_ret_intr temp + mfc0 t0, CP0_STATUS # cli + ori t0, t0, 1 + xori t0, t0, 1 + mtc0 t0, CP0_STATUS + SSNOP; SSNOP; SSNOP + + lw \temp, TI_PRE_COUNT($28) + subu \temp, \temp, 1 + sw \temp, TI_PRE_COUNT($28) + .endm +#else + .macro preempt_stop + .endm + + .macro init_ret_intr + .endm + +#define resume_kernel restore_all +#endif .text - .align 4 + .align 5 .set push .set reorder -EXPORT(ret_from_fork) - move a0, v0 # prev - jal schedule_tail -#error lw t0, TASK_PTRACE($28) # syscall tracing enabled? - andi t0, PT_TRACESYS - bnez t0, tracesys_exit - j ret_from_sys_call - -tracesys_exit: jal syscall_trace - b ret_from_sys_call - -EXPORT(ret_from_irq) -EXPORT(ret_from_exception) +FEXPORT(ret_from_irq) +FEXPORT(ret_from_exception) lw t0, PT_STATUS(sp) # returning to kernel mode? andi t0, t0, KU_USER - bnez t0, ret_from_sys_call + beqz t0, resume_kernel + +FEXPORT(resume_userspace) + mfc0 t0, CP0_STATUS # make sure we dont miss an + ori t0, t0, 1 # interrupt setting need_resched + xori t0, t0, 1 # between sampling and return + mtc0 t0, CP0_STATUS + SSNOP; SSNOP; SSNOP + + LONG_L a2, TI_FLAGS($28) + andi a2, _TIF_WORK_MASK # current->work (ignoring + # syscall_trace) + bnez a2, work_pending j restore_all -reschedule: jal schedule +#ifdef CONFIG_PREEMPT +ENTRY(resume_kernel) + lw t0, TI_PRE_COUNT($28) + bnez t0, restore_all + LONG_L t0, TI_FLAGS($28) + andi t1, t0, _TIF_NEED_RESCHED + beqz restore_all +#ifdef CONFIG_SMP + lw t0, TI_CPU($28) + la t1, irq_stat + sll t0, 5 # *sizeof(irq_cpustat_t) + addu t0, t1 + lw t1, local_bh_count(t0) + addl t0, local_irq_count(t0) +#else + lw t1, irq_stat+local_bh_count + addl t0, irq_stat+local_irq_count +#endif + addu t0, t1 + bnez t0, restore_all + lw t0, TI_PRE_COUNT($28) + addiu t0, 1 + sw t0, TI_PRE_COUNT($28) + sti + movl t0, TI_TASK($28) # ti->task + sw zero, TASK_STATE(t0) # current->state = TASK_RUNNING + jal schedule + j ret_from_intr +#endif -EXPORT(ret_from_sys_call) - .type ret_from_irq,@function +FEXPORT(ret_from_fork) + jal schedule_tail - mfc0 t0, CP0_STATUS # need_resched and signals atomic test - ori t0, t0, 1 - xori t0, t0, 1 +FEXPORT(syscall_exit) + mfc0 t0, CP0_STATUS # make sure need_resched and + ori t0, t0, 1 # signals dont change between + xori t0, t0, 1 # sampling and return mtc0 t0, CP0_STATUS - nop; nop; nop + SSNOP; SSNOP; SSNOP -#error lw v0, TASK_NEED_RESCHED($28) -#error lw v1, TASK_SIGPENDING($28) - bnez v0, reschedule - bnez v1, signal_return -restore_all: .set noat + LONG_L a2, TI_FLAGS($28) # current->work + bnez a2, syscall_exit_work + +FEXPORT(restore_all) + .set noat RESTORE_ALL_AND_RET .set at -/* Put this behind restore_all for the sake of the branch prediction. */ -signal_return: - .type signal_return, @function +FEXPORT(work_pending) + andi t0, a2, _TIF_NEED_RESCHED + bnez t0, work_notifysig +work_resched: + jal schedule - mfc0 t0, CP0_STATUS - ori t0, t0, 1 + mfc0 t0, CP0_STATUS # make sure need_resched and + ori t0, t0, 1 # signals dont change between + xori t0, t0, 1 # sampling and return mtc0 t0, CP0_STATUS + SSNOP; SSNOP; SSNOP + + LONG_L a2, TI_FLAGS($28) + andi a2, _TIF_WORK_MASK # is there any work to be done + # other than syscall tracing? + beqz a2, restore_all + andi t0, a2, _TIF_NEED_RESCHED + bnez t0, work_resched - move a0, zero - move a1, sp -#error jal do_signal - b restore_all +work_notifysig: # deal with pending signals and + # notify-resume requests + move a0, sp + li a1, 0 + jal do_notify_resume # a2 already loaded + j restore_all + +FEXPORT(syscall_exit_work) + LONG_L t0, TI_FLAGS($28) + bgez t0, work_pending # trace bit is set + mfc0 t0, CP0_STATUS # could let do_syscall_trace() + ori t0, t0, 1 # call schedule() instead + mtc0 t0, CP0_STATUS + jal do_syscall_trace + b resume_userspace /* * Common spurious interrupt handler. @@ -93,25 +168,105 @@ LEAF(spurious_interrupt) * Someone tried to fool us by sending an interrupt but we * couldn't find a cause for it. */ - lui t1,%hi(spurious_count) - .set reorder - lw t0,%lo(spurious_count)(t1) - .set noreorder + lui t1,%hi(irq_err_count) + lw t0,%lo(irq_err_count)(t1) addiu t0,1 - sw t0,%lo(spurious_count)(t1) + sw t0,%lo(irq_err_count)(t1) j ret_from_irq END(spurious_interrupt) + __INIT + + .set reorder + + NESTED(except_vec1_generic, 0, sp) + PANIC("Exception vector 1 called") + END(except_vec1_generic) + + /* + * General exception vector. Used for all CPUs except R4000 + * and R4400 SC and MC versions. + */ + NESTED(except_vec3_generic, 0, sp) +#if R5432_CP0_INTERRUPT_WAR + mfc0 k0, CP0_INDEX +#endif + mfc0 k1, CP0_CAUSE + la k0, exception_handlers + andi k1, k1, 0x7c + addu k0, k0, k1 + lw k0, (k0) + jr k0 + END(except_vec3_generic) + .set at + + /* General exception vector R4000 version. */ + NESTED(except_vec3_r4000, 0, sp) + .set push + .set mips3 + .set noat + mfc0 k1, CP0_CAUSE + li k0, 31<<2 + andi k1, k1, 0x7c + .set noreorder + beq k1, k0, handle_vced + li k0, 14<<2 + beq k1, k0, handle_vcei + lui k0, %hi(exception_handlers) + addiu k0, %lo(exception_handlers) + .set reorder + addu k0, k0, k1 + lw k0, (k0) + jr k0 + + /* + * Big shit, we now may have two dirty primary cache lines for + * the same physical address. We can savely invalidate the + * line pointed to by c0_badvaddr because after return from + * this exception handler the load / store will be re-executed. + */ +handle_vced: + mfc0 k0, CP0_BADVADDR + li k1, -4 + and k0, k1 + mtc0 zero, CP0_TAGLO + cache Index_Store_Tag_D,(k0) + cache Hit_Writeback_Inv_SD,(k0) +#ifdef CONFIG_PROC_FS + lui k0, %hi(vced_count) + lw k1, %lo(vced_count)(k0) + addiu k1, 1 + sw k1, %lo(vced_count)(k0) +#endif + eret + +handle_vcei: + mfc0 k0, CP0_BADVADDR + cache Hit_Writeback_Inv_SD, (k0) # also cleans pi +#ifdef CONFIG_PROC_FS + lui k0, %hi(vcei_count) + lw k1, %lo(vcei_count)(k0) + addiu k1, 1 + sw k1, %lo(vcei_count)(k0) +#endif + eret + .set pop + END(except_vec3_r4000) + + __FINIT + /* * Build a default exception handler for the exceptions that don't need * special handlers. If you didn't know yet - I *like* playing games with * the C preprocessor ... */ #define __BUILD_clear_none(exception) -#define __BUILD_clear_sti(exception) \ +#define __BUILD_clear_sti(exception) \ STI -#define __BUILD_clear_cli(exception) \ +#define __BUILD_clear_cli(exception) \ CLI +#define __BUILD_clear_kmode(exception) \ + KMODE #define __BUILD_clear_fpe(exception) \ cfc1 a1,fcr31; \ li a2,~(0x3f<<12); \ @@ -148,6 +303,7 @@ EXPORT(exception_count_##exception); \ NESTED(handle_##exception, PT_SIZE, sp); \ .set noat; \ SAVE_ALL; \ + FEXPORT(handle_##exception##_int); \ __BUILD_clear_##clear(exception); \ .set at; \ __BUILD_##verbose(exception); \ @@ -159,16 +315,18 @@ EXPORT(exception_count_##exception); \ BUILD_HANDLER(adel,ade,ade,silent) /* #4 */ BUILD_HANDLER(ades,ade,ade,silent) /* #5 */ - BUILD_HANDLER(ibe,ibe,cli,verbose) /* #6 */ - BUILD_HANDLER(dbe,dbe,cli,silent) /* #7 */ - BUILD_HANDLER(bp,bp,sti,silent) /* #9 */ - BUILD_HANDLER(ri,ri,sti,silent) /* #10 */ - BUILD_HANDLER(cpu,cpu,sti,silent) /* #11 */ - BUILD_HANDLER(ov,ov,sti,silent) /* #12 */ - BUILD_HANDLER(tr,tr,sti,silent) /* #13 */ + BUILD_HANDLER(ibe,be,cli,silent) /* #6 */ + BUILD_HANDLER(dbe,be,cli,silent) /* #7 */ + BUILD_HANDLER(bp,bp,kmode,silent) /* #9 */ + BUILD_HANDLER(ri,ri,kmode,silent) /* #10 */ + BUILD_HANDLER(cpu,cpu,kmode,silent) /* #11 */ + BUILD_HANDLER(ov,ov,kmode,silent) /* #12 */ + BUILD_HANDLER(tr,tr,kmode,silent) /* #13 */ BUILD_HANDLER(fpe,fpe,fpe,silent) /* #15 */ - BUILD_HANDLER(watch,watch,sti,verbose) /* #23 */ - BUILD_HANDLER(reserved,reserved,sti,verbose) /* others */ + BUILD_HANDLER(mdmx,mdmx,sti,silent) /* #22 */ + BUILD_HANDLER(watch,watch,sti,silent) /* #23 */ + BUILD_HANDLER(mcheck,mcheck,cli,silent) /* #24 */ + BUILD_HANDLER(reserved,reserved,kmode,silent) /* others */ .set pop diff --git a/arch/mips/kernel/gdb-low.S b/arch/mips/kernel/gdb-low.S index 221eeb254e6b..c189294c9c4b 100644 --- a/arch/mips/kernel/gdb-low.S +++ b/arch/mips/kernel/gdb-low.S @@ -14,6 +14,16 @@ #include <asm/gdb-stub.h> /* + * [jsun] We reserves about 2x GDB_FR_SIZE in stack. The lower (addressed) + * part is used to store registers and passed to exception handler. + * The upper part is reserved for "call func" feature where gdb client + * saves some of the regs, setups call frame and passes args. + * + * A trace shows about 200 bytes are used to store about half of all regs. + * The rest should be big enough for frame setup and passing args. + */ + +/* * The low level trap handler */ .align 5 @@ -38,7 +48,7 @@ nop 1: move k0,sp - subu sp,k1,GDB_FR_SIZE + subu sp,k1,GDB_FR_SIZE*2 # see comment above sw k0,GDB_FR_REG29(sp) sw v0,GDB_FR_REG2(sp) @@ -97,7 +107,7 @@ sw ra,GDB_FR_REG31(sp) CLI /* disable interrupts */ - + /* * Followed by the floating point registers */ @@ -145,9 +155,9 @@ * FPU control registers */ - mfc1 v0,CP1_STATUS + cfc1 v0,CP1_STATUS sw v0,GDB_FR_FSR(sp) - mfc1 v0,CP1_REVISION + cfc1 v0,CP1_REVISION sw v0,GDB_FR_FIR(sp) /* @@ -211,7 +221,7 @@ lw v0,GDB_FR_CP0_CONTEXT(sp) mtc0 v1,CP0_INDEX mtc0 v0,CP0_CONTEXT - + /* * Next, the floating point registers @@ -304,7 +314,7 @@ lw v1,GDB_FR_REG3(sp) lw v0,GDB_FR_REG2(sp) lw $1,GDB_FR_REG1(sp) -#ifdef CONFIG_CPU_R3000 +#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX) lw k0, GDB_FR_EPC(sp) lw sp, GDB_FR_REG29(sp) /* Deallocate stack */ jr k0 @@ -321,11 +331,7 @@ END(trap_low) LEAF(kgdb_read_byte) - .set push - .set noreorder - .set nomacro 4: lb t0, (a0) - .set pop sb t0, (a1) li v0, 0 jr ra @@ -335,11 +341,7 @@ LEAF(kgdb_read_byte) END(kgdb_read_byte) LEAF(kgdb_write_byte) - .set push - .set noreorder - .set nomacro 5: sb a0, (a1) - .set pop li v0, 0 jr ra .section __ex_table,"a" @@ -349,6 +351,7 @@ LEAF(kgdb_write_byte) .type kgdbfault@function .ent kgdbfault + kgdbfault: li v0, -EFAULT jr ra .end kgdbfault diff --git a/arch/mips/kernel/gdb-stub.c b/arch/mips/kernel/gdb-stub.c index e279dc48ca8a..a3e698849da9 100644 --- a/arch/mips/kernel/gdb-stub.c +++ b/arch/mips/kernel/gdb-stub.c @@ -11,8 +11,6 @@ * Send complaints, suggestions etc. to <andy@waldorf-gmbh.de> * * Copyright (C) 1995 Andreas Busse - * - * $Id: gdb-stub.c,v 1.6 1999/05/01 22:40:35 ralf Exp $ */ /* @@ -64,7 +62,7 @@ * Host: Reply: * $m0,10#2a +$00010203040506070809101112131415#42 * - * + * * ============== * MORE EXAMPLES: * ============== @@ -74,11 +72,11 @@ * going. In this scenario the host machine was a PC and the * target platform was a Galileo EVB64120A MIPS evaluation * board. - * + * * Step 1: * First download gdb-5.0.tar.gz from the internet. * and then build/install the package. - * + * * Example: * $ tar zxf gdb-5.0.tar.gz * $ cd gdb-5.0 @@ -87,40 +85,39 @@ * $ install * $ which mips-linux-elf-gdb * /usr/local/bin/mips-linux-elf-gdb - * + * * Step 2: * Configure linux for remote debugging and build it. - * + * * Example: * $ cd ~/linux * $ make menuconfig <go to "Kernel Hacking" and turn on remote debugging> * $ make dep; make vmlinux - * + * * Step 3: * Download the kernel to the remote target and start - * the kernel running. It will promptly halt and wait + * the kernel running. It will promptly halt and wait * for the host gdb session to connect. It does this - * since the "Kernel Hacking" option has defined - * CONFIG_REMOTE_DEBUG which in turn enables your calls + * since the "Kernel Hacking" option has defined + * CONFIG_KGDB which in turn enables your calls * to: * set_debug_traps(); * breakpoint(); - * + * * Step 4: * Start the gdb session on the host. - * + * * Example: * $ mips-linux-elf-gdb vmlinux * (gdb) set remotebaud 115200 * (gdb) target remote /dev/ttyS1 - * ...at this point you are connected to + * ...at this point you are connected to * the remote target and can use gdb - * in the normal fasion. Setting + * in the normal fasion. Setting * breakpoints, single stepping, * printing variables, etc. - * */ - +#include <linux/config.h> #include <linux/string.h> #include <linux/kernel.h> #include <linux/signal.h> @@ -128,6 +125,8 @@ #include <linux/mm.h> #include <linux/console.h> #include <linux/init.h> +#include <linux/slab.h> +#include <linux/reboot.h> #include <asm/asm.h> #include <asm/mipsregs.h> @@ -176,8 +175,8 @@ static const char hexchars[]="0123456789abcdef"; /* Used to prevent crashes in memory access. Note that they'll crash anyway if we haven't set up fault handlers yet... */ -int kgdb_read_byte(unsigned *address, unsigned *dest); -int kgdb_write_byte(unsigned val, unsigned *dest); +int kgdb_read_byte(unsigned char *address, unsigned char *dest); +int kgdb_write_byte(unsigned char val, unsigned char *dest); /* * Convert ch from a hex digit to an int @@ -214,7 +213,7 @@ static void getpacket(char *buffer) checksum = 0; xmitcsum = -1; count = 0; - + /* * now, read until a # or end of buffer is found */ @@ -376,10 +375,10 @@ void set_debug_traps(void) unsigned long flags; unsigned char c; - save_and_cli(flags); + local_irq_save(flags); for (ht = hard_trap_info; ht->tt && ht->signo; ht++) saved_vectors[ht->tt] = set_except_vector(ht->tt, trap_low); - + putDebugChar('+'); /* 'hello world' */ /* * In case GDB is started before us, ack any packets @@ -392,7 +391,7 @@ void set_debug_traps(void) putDebugChar('+'); /* ack it */ initialized = 1; - restore_flags(flags); + local_irq_restore(flags); } /* @@ -548,7 +547,7 @@ static void single_step(struct gdb_regs *regs) targ += 4 + (insn.i_format.simmediate << 2); break; } - + if (is_branch) { i = 0; if (is_cond && targ != (regs->cp0_epc + 8)) { @@ -568,7 +567,7 @@ static void single_step(struct gdb_regs *regs) /* * If asynchronously interrupted by gdb, then we need to set a breakpoint - * at the interrupted instruction so that we wind up stopped with a + * at the interrupted instruction so that we wind up stopped with a * reasonable stack frame. */ static struct gdb_bp_save async_bp; @@ -578,7 +577,7 @@ void set_async_breakpoint(unsigned int epc) async_bp.addr = epc; async_bp.val = *(unsigned *)epc; *(unsigned *)epc = BP; - flush_cache_all(); + __flush_cache_all(); } @@ -596,31 +595,11 @@ void handle_exception (struct gdb_regs *regs) char *ptr; unsigned long *stack; -#if 0 - printk("in handle_exception()\n"); - show_gdbregs(regs); -#endif - - /* - * First check trap type. If this is CPU_UNUSABLE and CPU_ID is 1, - * the simply switch the FPU on and return since this is no error - * condition. kernel/traps.c does the same. - * FIXME: This doesn't work yet, so we don't catch CPU_UNUSABLE - * traps for now. - */ - trap = (regs->cp0_cause & 0x7c) >> 2; -/* printk("trap=%d\n",trap); */ - if (trap == 11) { - if (((regs->cp0_cause >> CAUSEB_CE) & 3) == 1) { - regs->cp0_status |= ST0_CU1; - return; - } - } - /* * If we're in breakpoint() increment the PC */ - if (trap == 9 && regs->cp0_epc == (unsigned long)breakinst) + trap = (regs->cp0_cause & 0x7c) >> 2; + if (trap == 9 && regs->cp0_epc == (unsigned long)breakinst) regs->cp0_epc += 4; /* @@ -630,7 +609,7 @@ void handle_exception (struct gdb_regs *regs) if (step_bp[0].addr) { *(unsigned *)step_bp[0].addr = step_bp[0].val; step_bp[0].addr = 0; - + if (step_bp[1].addr) { *(unsigned *)step_bp[1].addr = step_bp[1].val; step_bp[1].addr = 0; @@ -708,6 +687,11 @@ void handle_exception (struct gdb_regs *regs) output_buffer[3] = 0; break; + case 'D': + /* detach; let CPU run */ + putpacket(output_buffer); + return; + case 'd': /* toggle debug flag */ break; @@ -724,29 +708,24 @@ void handle_exception (struct gdb_regs *regs) ptr = mem2hex((char *)®s->frame_ptr, ptr, 2*4, 0); /* frp */ ptr = mem2hex((char *)®s->cp0_index, ptr, 16*4, 0); /* cp0 */ break; - + /* * set the value of the CPU registers - return OK - * FIXME: Needs to be written */ case 'G': { -#if 0 - unsigned long *newsp, psr; - ptr = &input_buffer[1]; - hex2mem(ptr, (char *)registers, 16 * 4, 0); /* G & O regs */ - - /* - * See if the stack pointer has moved. If so, then copy the - * saved locals and ins to the new location. - */ - - newsp = (unsigned long *)registers[SP]; - if (sp != newsp) - sp = memcpy(newsp, sp, 16 * 4); - -#endif + hex2mem(ptr, (char *)®s->reg0, 32*4, 0); + ptr += 32*8; + hex2mem(ptr, (char *)®s->cp0_status, 6*4, 0); + ptr += 6*8; + hex2mem(ptr, (char *)®s->fpr0, 32*4, 0); + ptr += 32*8; + hex2mem(ptr, (char *)®s->cp1_fsr, 2*4, 0); + ptr += 2*8; + hex2mem(ptr, (char *)®s->frame_ptr, 2*4, 0); + ptr += 2*8; + hex2mem(ptr, (char *)®s->cp0_index, 16*4, 0); strcpy(output_buffer,"OK"); } break; @@ -770,7 +749,7 @@ void handle_exception (struct gdb_regs *regs) /* * MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */ - case 'M': + case 'M': ptr = &input_buffer[1]; if (hexToInt(&ptr, &addr) @@ -789,13 +768,13 @@ void handle_exception (struct gdb_regs *regs) /* * cAA..AA Continue at address AA..AA(optional) */ - case 'c': + case 'c': /* try to read optional parameter, pc unchanged if no parm */ ptr = &input_buffer[1]; if (hexToInt(&ptr, &addr)) regs->cp0_epc = addr; - + /* * Need to flush the instruction cache here, as we may * have deposited a breakpoint, and the icache probably @@ -805,26 +784,21 @@ void handle_exception (struct gdb_regs *regs) * NB: We flush both caches, just to be sure... */ - flush_cache_all(); + __flush_cache_all(); return; /* NOTREACHED */ break; /* - * kill the program - */ - case 'k' : - break; /* do nothing */ - - - /* - * Reset the whole machine (FIXME: system dependent) + * kill the program; let us try to restart the machine + * Reset the whole machine. */ + case 'k': case 'r': + machine_restart("kgdb restarts machine"); break; - /* * Step to next instruction */ @@ -834,7 +808,7 @@ void handle_exception (struct gdb_regs *regs) * use breakpoints and continue, instead. */ single_step(regs); - flush_cache_all(); + __flush_cache_all(); return; /* NOTREACHED */ @@ -844,7 +818,7 @@ void handle_exception (struct gdb_regs *regs) */ case 'b': { -#if 0 +#if 0 int baudrate; extern void set_timer_3(); @@ -904,30 +878,43 @@ void breakpoint(void) if (!initialized) return; - __asm__ __volatile__(" - .globl breakinst - .set noreorder - nop -breakinst: break - nop - .set reorder - "); + __asm__ __volatile__( + ".globl breakinst\n\t" + ".set\tnoreorder\n\t" + "nop\n\t" + "breakinst:\tbreak\n\t" + "nop\n\t" + ".set\treorder" + ); } void adel(void) { - __asm__ __volatile__(" - .globl adel - la $8,0x80000001 - lw $9,0($8) - "); + __asm__ __volatile__( + ".globl\tadel\n\t" + "la\t$8,0x80000001\n\t" + "lw\t$9,0($8)\n\t" + ); +} + +/* + * malloc is needed by gdb client in "call func()", even a private one + * will make gdb happy + */ +static void *malloc(size_t size) +{ + return kmalloc(size, GFP_ATOMIC); +} + +static void free(void *where) +{ + kfree(where); } #ifdef CONFIG_GDB_CONSOLE -void gdb_puts(const char *str) +void gdb_putsn(const char *str, int l) { - int l = strlen(str); char outbuf[18]; outbuf[0]='O'; @@ -936,7 +923,7 @@ void gdb_puts(const char *str) int i = (l>8)?8:l; mem2hex((char *)str, &outbuf[1], i, 0); outbuf[(i*2)+1]=0; - putpacket(outbuf); + putpacket(outbuf); str += i; l -= i; } @@ -944,7 +931,7 @@ void gdb_puts(const char *str) static void gdb_console_write(struct console *con, const char *s, unsigned n) { - gdb_puts(s); + gdb_putsn(s, n); } static struct console gdb_console = { @@ -958,5 +945,5 @@ __init void register_gdb_console(void) { register_console(&gdb_console); } - + #endif diff --git a/arch/mips/kernel/head.S b/arch/mips/kernel/head.S index 3714680d6d44..d801659903c9 100644 --- a/arch/mips/kernel/head.S +++ b/arch/mips/kernel/head.S @@ -1,6 +1,4 @@ /* - * arch/mips/kernel/head.S - * * 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. @@ -13,550 +11,224 @@ * Further modifications by David S. Miller and Harald Koerfgen * Copyright (C) 1999 Silicon Graphics, Inc. * - * Head.S contains the MIPS exception handler and startup code. - * - ************************************************************************** - * 9 Nov, 2000. - * Added Cache Error exception handler and SBDDP EJTAG debug exception. - * - * Kevin Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com - * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved. - ************************************************************************** + * Kevin Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com + * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved. */ #include <linux/config.h> +#include <linux/init.h> #include <linux/threads.h> #include <asm/asm.h> -#include <asm/cacheops.h> -#include <asm/current.h> #include <asm/offset.h> +#include <asm/pgtable-bits.h> #include <asm/processor.h> #include <asm/regdef.h> #include <asm/cachectl.h> #include <asm/mipsregs.h> #include <asm/stackframe.h> -#include <asm/bootinfo.h> - - .text - /* - * Reserved space for exception handlers. - * Necessary for machines which link their kernels at KSEG0. - * FIXME: Use the initcode feature to get rid of unused handler - * variants. - */ - .fill 0x280 -/* - * This is space for the interrupt handlers. - * After trap_init() they are located at virtual address KSEG0. - * - * These handlers much be written in a relocatable manner - * because based upon the cpu type an arbitrary one of the - * following pieces of code will be copied to the KSEG0 - * vector location. - */ - /* TLB refill, EXL == 0, R4xx0, non-R4600 version */ - .set noreorder - .set noat - LEAF(except_vec0_r4000) - .set mips3 -#ifdef CONFIG_SMP - mfc0 k1, CP0_CONTEXT - la k0, current_pgd - srl k1, 23 - sll k1, 2 - addu k1, k0, k1 - lw k1, (k1) -#else - lw k1, current_pgd # get pgd pointer -#endif - mfc0 k0, CP0_BADVADDR # Get faulting address - srl k0, k0, 22 # get pgd only bits - - sll k0, k0, 2 - addu k1, k1, k0 # add in pgd offset - mfc0 k0, CP0_CONTEXT # get context reg - lw k1, (k1) -#if defined(CONFIG_CPU_VR41XX) - srl k0, k0, 3 # get pte offset -#else - srl k0, k0, 1 # get pte offset -#endif - and k0, k0, 0xff8 - addu k1, k1, k0 # add in offset - lw k0, 0(k1) # get even pte - lw k1, 4(k1) # get odd pte - srl k0, k0, 6 # convert to entrylo0 - mtc0 k0, CP0_ENTRYLO0 # load it - srl k1, k1, 6 # convert to entrylo1 - mtc0 k1, CP0_ENTRYLO1 # load it - b 1f - tlbwr # write random tlb entry -1: - nop - eret # return from trap - END(except_vec0_r4000) - - /* TLB refill, EXL == 0, R4600 version */ - LEAF(except_vec0_r4600) - .set mips3 - mfc0 k0, CP0_BADVADDR - srl k0, k0, 22 - lw k1, current_pgd # get pgd pointer - sll k0, k0, 2 - addu k1, k1, k0 - mfc0 k0, CP0_CONTEXT - lw k1, (k1) - srl k0, k0, 1 - and k0, k0, 0xff8 - addu k1, k1, k0 - lw k0, 0(k1) - lw k1, 4(k1) - srl k0, k0, 6 - mtc0 k0, CP0_ENTRYLO0 - srl k1, k1, 6 - mtc0 k1, CP0_ENTRYLO1 - nop - tlbwr - nop - eret - END(except_vec0_r4600) - - /* TLB refill, EXL == 0, R52x0 "Nevada" version */ - /* - * This version has a bug workaround for the Nevada. It seems - * as if under certain circumstances the move from cp0_context - * might produce a bogus result when the mfc0 instruction and - * it's consumer are in a different cacheline or a load instruction, - * probably any memory reference, is between them. This is - * potencially slower than the R4000 version, so we use this - * special version. - */ - .set noreorder - .set noat - LEAF(except_vec0_nevada) - .set mips3 - mfc0 k0, CP0_BADVADDR # Get faulting address - srl k0, k0, 22 # get pgd only bits - lw k1, current_pgd # get pgd pointer - sll k0, k0, 2 - addu k1, k1, k0 # add in pgd offset - lw k1, (k1) - mfc0 k0, CP0_CONTEXT # get context reg - srl k0, k0, 1 # get pte offset - and k0, k0, 0xff8 - addu k1, k1, k0 # add in offset - lw k0, 0(k1) # get even pte - lw k1, 4(k1) # get odd pte - srl k0, k0, 6 # convert to entrylo0 - mtc0 k0, CP0_ENTRYLO0 # load it - srl k1, k1, 6 # convert to entrylo1 - mtc0 k1, CP0_ENTRYLO1 # load it - nop # QED specified nops - nop - tlbwr # write random tlb entry - nop # traditional nop - eret # return from trap - END(except_vec0_nevada) - - /* TLB refill, EXL == 0, R4[40]00/R5000 badvaddr hwbug version */ - LEAF(except_vec0_r45k_bvahwbug) - .set mips3 - mfc0 k0, CP0_BADVADDR - srl k0, k0, 22 - lw k1, current_pgd # get pgd pointer - sll k0, k0, 2 - addu k1, k1, k0 - mfc0 k0, CP0_CONTEXT - lw k1, (k1) - srl k0, k0, 1 - and k0, k0, 0xff8 - addu k1, k1, k0 - lw k0, 0(k1) - lw k1, 4(k1) - nop /* XXX */ - tlbp - srl k0, k0, 6 - mtc0 k0, CP0_ENTRYLO0 - srl k1, k1, 6 - mfc0 k0, CP0_INDEX - mtc0 k1, CP0_ENTRYLO1 - bltzl k0, 1f - tlbwr -1: - nop - eret - END(except_vec0_r45k_bvahwbug) - -#ifdef CONFIG_SMP - /* TLB refill, EXL == 0, R4000 MP badvaddr hwbug version */ - LEAF(except_vec0_r4k_mphwbug) - .set mips3 - mfc0 k0, CP0_BADVADDR - srl k0, k0, 22 - lw k1, current_pgd # get pgd pointer - sll k0, k0, 2 - addu k1, k1, k0 - mfc0 k0, CP0_CONTEXT - lw k1, (k1) - srl k0, k0, 1 - and k0, k0, 0xff8 - addu k1, k1, k0 - lw k0, 0(k1) - lw k1, 4(k1) - nop /* XXX */ - tlbp - srl k0, k0, 6 - mtc0 k0, CP0_ENTRYLO0 - srl k1, k1, 6 - mfc0 k0, CP0_INDEX - mtc0 k1, CP0_ENTRYLO1 - bltzl k0, 1f - tlbwr -1: - nop - eret - END(except_vec0_r4k_mphwbug) -#endif - - /* TLB refill, EXL == 0, R4000 UP 250MHZ entrylo[01] hwbug version */ - LEAF(except_vec0_r4k_250MHZhwbug) - .set mips3 - mfc0 k0, CP0_BADVADDR - srl k0, k0, 22 - lw k1, current_pgd # get pgd pointer - sll k0, k0, 2 - addu k1, k1, k0 - mfc0 k0, CP0_CONTEXT - lw k1, (k1) - srl k0, k0, 1 - and k0, k0, 0xff8 - addu k1, k1, k0 - lw k0, 0(k1) - lw k1, 4(k1) - srl k0, k0, 6 - mtc0 zero, CP0_ENTRYLO0 - mtc0 k0, CP0_ENTRYLO0 - srl k1, k1, 6 - mtc0 zero, CP0_ENTRYLO1 - mtc0 k1, CP0_ENTRYLO1 - b 1f - tlbwr -1: - nop - eret - END(except_vec0_r4k_250MHZhwbug) - -#ifdef CONFIG_SMP - /* TLB refill, EXL == 0, R4000 MP 250MHZ entrylo[01]+badvaddr bug version */ - LEAF(except_vec0_r4k_MP250MHZhwbug) - .set mips3 - mfc0 k0, CP0_BADVADDR - srl k0, k0, 22 - lw k1, current_pgd # get pgd pointer - sll k0, k0, 2 - addu k1, k1, k0 - mfc0 k0, CP0_CONTEXT - lw k1, (k1) - srl k0, k0, 1 - and k0, k0, 0xff8 - addu k1, k1, k0 - lw k0, 0(k1) - lw k1, 4(k1) - nop /* XXX */ - tlbp - srl k0, k0, 6 - mtc0 zero, CP0_ENTRYLO0 - mtc0 k0, CP0_ENTRYLO0 - mfc0 k0, CP0_INDEX - srl k1, k1, 6 - mtc0 zero, CP0_ENTRYLO1 - mtc0 k1, CP0_ENTRYLO1 - bltzl k0, 1f - tlbwr -1: - nop - eret - END(except_vec0_r4k_MP250MHZhwbug) -#endif - /* TLB refill, R[23]00 version */ - LEAF(except_vec0_r2300) - .set noat - .set mips1 - mfc0 k0, CP0_BADVADDR - lw k1, current_pgd # get pgd pointer - srl k0, k0, 22 - sll k0, k0, 2 - addu k1, k1, k0 - mfc0 k0, CP0_CONTEXT - lw k1, (k1) - and k0, k0, 0xffc - addu k1, k1, k0 - lw k0, (k1) - nop - mtc0 k0, CP0_ENTRYLO0 - mfc0 k1, CP0_EPC - tlbwr - jr k1 - rfe - END(except_vec0_r2300) - - - /* XTLB refill, EXL == 0, R4xx0 cpus only use this... */ - NESTED(except_vec1_generic, 0, sp) - .set noat - .set mips3 - /* Register saving is delayed as long as we don't know - * which registers really need to be saved. - */ - mfc0 k1, CP0_CONTEXT - dsra k1, 1 - lwu k0, (k1) # May cause another exception - lwu k1, 4(k1) - dsrl k0, 6 # Convert to EntryLo format - dsrl k1, 6 # Convert to EntryLo format - dmtc0 k0, CP0_ENTRYLO0 - dmtc0 k1, CP0_ENTRYLO1 - nop # Needed for R4[04]00 pipeline - tlbwr - nop # Needed for R4[04]00 pipeline - nop - nop - eret - nop /* Workaround for R4000 bug. */ - eret - END(except_vec1_generic) - - /* Cache Error */ - LEAF(except_vec2_generic) - .set noat - .set mips0 - /* - * This is a very bad place to be. Our cache error - * detection has triggered. If we have write-back data - * in the cache, we may not be able to recover. As a - * first-order desperate measure, turn off KSEG0 cacheing. - */ - mfc0 k0,CP0_CONFIG - li k1,~CONF_CM_CMASK - and k0,k0,k1 - ori k0,k0,CONF_CM_UNCACHED - mtc0 k0,CP0_CONFIG - /* Give it a few cycles to sink in... */ - nop - nop - nop - - j cache_parity_error - nop - END(except_vec2_generic) - - /* General exception vector R4000 version. */ - NESTED(except_vec3_r4000, 0, sp) - .set noat - mfc0 k1, CP0_CAUSE - andi k1, k1, 0x7c - li k0, 31<<2 - beq k1, k0, handle_vced - li k0, 14<<2 - beq k1, k0, handle_vcei - la k0, exception_handlers - addu k0, k0, k1 - lw k0, (k0) - nop - jr k0 - nop - -/* - * Big shit, we now may have two dirty primary cache lines for the same - * physical address. We can savely invalidate the line pointed to by - * c0_badvaddr because after return from this exception handler the load / - * store will be re-executed. - */ - .set mips3 -handle_vced: - mfc0 k0, CP0_BADVADDR - li k1, -4 - and k0, k1 - mtc0 zero, CP0_TAGLO - cache Index_Store_Tag_D,(k0) - cache Hit_Writeback_Inv_SD,(k0) -#ifdef CONFIG_PROC_FS - lui k0, %hi(vced_count) - lw k1, %lo(vced_count)(k0) - addiu k1, 1 - sw k1, %lo(vced_count)(k0) -#endif - eret - -handle_vcei: - mfc0 k0, CP0_BADVADDR - cache Hit_Writeback_Inv_SD,(k0) # also cleans pi -#ifdef CONFIG_PROC_FS - lui k0, %hi(vcei_count) - lw k1, %lo(vcei_count)(k0) - addiu k1, 1 - sw k1, %lo(vcei_count)(k0) -#endif - eret - - END(except_vec3_r4000) - .set at - - /* General exception vector. */ - NESTED(except_vec3_generic, 0, sp) - .set noat - .set mips0 - mfc0 k1, CP0_CAUSE - la k0, exception_handlers - andi k1, k1, 0x7c - addu k0, k0, k1 - lw k0, (k0) - nop - jr k0 - nop - END(except_vec3_generic) - .set at - - /* - * Special interrupt vector for embedded MIPS. This is a - * dedicated interrupt vector which reduces interrupt processing - * overhead. The jump instruction will be inserted here at - * initialization time. This handler may only be 8 bytes in size! - */ - NESTED(except_vec4, 0, sp) -1: j 1b /* Dummy, will be replaced */ - nop - END(except_vec4) - - /* - * SBDDP EJTAG debug exception handler. - * The EJTAG debug exception entry point is 0xbfc00480, which - * normally is in the boot PROM, so the boot PROM must do a - * unconditional jump to this vector. - */ - NESTED(except_vec_ejtag_debug, 0, sp) - j ejtag_debug_handler - nop - END(except_vec_ejtag_debug) - - /* - * EJTAG debug exception handler. - */ - NESTED(ejtag_debug_handler, PT_SIZE, sp) - .set noat - .set noreorder - SAVE_ALL - PRINT("SDBBP EJTAG debug exception - not handled yet, just ignored!\n"); - mfc0 k0, $23 # Get EJTAG Debug register. - mfc0 k1, $24 # Get DEPC register. - bgez k0, 1f - addiu k1, k1, 4 # SBDDP inst. in delay slot. - addiu k1, k1, 4 -1: mtc0 k1, $24 - RESTORE_ALL - .word 0x4200001f # deret, return EJTAG debug exception. - nop - .set at - END(ejtag_debug_handler) - - -/* - * Kernel entry point - */ -NESTED(kernel_entry, 16, sp) - .set noreorder - /* The following two symbols are used for kernel profiling. */ - EXPORT(stext) - EXPORT(_stext) - - /* - * Stack for kernel and init, current variable - */ - la $28, init_task_union - addiu t0, $28, KERNEL_STACK_SIZE-32 - subu sp, t0, 4*SZREG - - sw t0, kernelsp - /* The firmware/bootloader passes argc/argp/envp - * to us as arguments. But clear bss first because - * the romvec and other important info is stored there - * by prom_init(). - */ - la t0, _edata - sw zero, (t0) - la t1, (_end - 4) + .text + /* + * Reserved space for exception handlers. + * Necessary for machines which link their kernels at KSEG0. + */ + .fill 0x400 + + /* The following two symbols are used for kernel profiling. */ + EXPORT(stext) + EXPORT(_stext) + + __INIT + + /* Cache Error */ + LEAF(except_vec2_generic) + .set noreorder + .set noat + .set mips0 + /* + * This is a very bad place to be. Our cache error + * detection has triggered. If we have write-back data + * in the cache, we may not be able to recover. As a + * first-order desperate measure, turn off KSEG0 cacheing. + */ + mfc0 k0,CP0_CONFIG + li k1,~CONF_CM_CMASK + and k0,k0,k1 + ori k0,k0,CONF_CM_UNCACHED + mtc0 k0,CP0_CONFIG + /* Give it a few cycles to sink in... */ + nop + nop + nop + + j cache_parity_error + nop + END(except_vec2_generic) + + .set at + + /* + * Special interrupt vector for embedded MIPS. This is a + * dedicated interrupt vector which reduces interrupt processing + * overhead. The jump instruction will be inserted here at + * initialization time. This handler may only be 8 bytes in + * size! + */ + NESTED(except_vec4, 0, sp) +1: j 1b /* Dummy, will be replaced */ + nop + END(except_vec4) + + /* + * EJTAG debug exception handler. + * The EJTAG debug exception entry point is 0xbfc00480, which + * normally is in the boot PROM, so the boot PROM must do a + * unconditional jump to this vector. + */ + NESTED(except_vec_ejtag_debug, 0, sp) + j ejtag_debug_handler + nop + END(except_vec_ejtag_debug) + + __FINIT + + /* + * EJTAG debug exception handler. + */ + NESTED(ejtag_debug_handler, PT_SIZE, sp) + .set noat + .set noreorder + mtc0 k0, CP0_DESAVE + mfc0 k0, CP0_DEBUG + + sll k0, k0, 30 # Check for SDBBP. + bgez k0, ejtag_return + + la k0, ejtag_debug_buffer + sw k1, 0(k0) + SAVE_ALL + jal ejtag_exception_handler + move a0, sp + RESTORE_ALL + la k0, ejtag_debug_buffer + lw k1, 0(k0) + +ejtag_return: + mfc0 k0, CP0_DESAVE + .set mips32 + deret + .set mips0 + nop + .set at + END(ejtag_debug_handler) + + __INIT + + /* + * NMI debug exception handler for MIPS reference boards. + * The NMI debug exception entry point is 0xbfc00000, which + * normally is in the boot PROM, so the boot PROM must do a + * unconditional jump to this vector. + */ + NESTED(except_vec_nmi, 0, sp) + j nmi_handler + nop + END(except_vec_nmi) + + __FINIT + + NESTED(nmi_handler, PT_SIZE, sp) + .set noat + .set noreorder + .set mips3 + SAVE_ALL + jal nmi_exception_handler + move a0, sp + RESTORE_ALL + eret + .set at + .set mips0 + END(nmi_handler) + + __INIT + + /* + * Kernel entry point + */ + NESTED(kernel_entry, 16, sp) + .set noreorder + + /* + * Stack for kernel and init, current variable + */ + la $28, init_thread_union + addiu t0, $28, KERNEL_STACK_SIZE-32 + subu sp, t0, 4*SZREG + sw t0, kernelsp + + /* The firmware/bootloader passes argc/argp/envp + * to us as arguments. But clear bss first because + * the romvec and other important info is stored there + * by prom_init(). + */ + la t0, __bss_start + sw zero, (t0) + la t1, __bss_stop - 4 1: - addiu t0, 4 - bne t0, t1, 1b - sw zero, (t0) + addiu t0, 4 + bne t0, t1, 1b + sw zero, (t0) - jal init_arch - nop - END(kernel_entry) + jal init_arch + nop + END(kernel_entry) #ifdef CONFIG_SMP /* - * SMP slave cpus entry point. Board specific code - * for bootstrap calls this function after setting up - * the stack and gp registers. - */ - LEAF(smp_bootstrap) - .set push - .set noreorder - mtc0 zero, CP0_WIRED - CLI - mfc0 t0, CP0_STATUS - li t1, ~(ST0_CU1|ST0_CU2|ST0_CU3|ST0_BEV); - and t0, t1 - or t0, (ST0_CU0|ST0_KX|ST0_SX|ST0_FR); - addiu a0, zero, 0 - jal start_secondary - mtc0 t0, CP0_STATUS - .set pop - END(smp_bootstrap) -#endif - -/* - * This buffer is reserved for the use of the cache error handler. + * SMP slave cpus entry point. Board specific code for bootstrap calls this + * function after setting up the stack and gp registers. */ - .data - EXPORT(cache_error_buffer) - .fill 32*4,1,0 - -#ifndef CONFIG_SMP -EXPORT(kernelsp) - PTR 0 -EXPORT(current_pgd) - PTR 0 -#else - /* There's almost certainly a better way to do this with the macros...*/ - .globl kernelsp - .comm kernelsp, NR_CPUS * 8, 8 - .globl current_pgd - .comm current_pgd, NR_CPUS * 8, 8 + LEAF(smp_bootstrap) + .set push + .set noreorder + mtc0 zero, CP0_WIRED + CLI + mfc0 t0, CP0_STATUS + li t1, ~(ST0_CU1|ST0_CU2|ST0_CU3|ST0_KX|ST0_SX) + and t0, t1 + or t0, (ST0_CU0); + jal start_secondary + mtc0 t0, CP0_STATUS + .set pop + END(smp_bootstrap) #endif - .text - .org 0x1000 -EXPORT(swapper_pg_dir) - - .org 0x2000 -EXPORT(empty_bad_page) - .org 0x3000 -EXPORT(empty_bad_page_table) + __FINIT - .org 0x4000 -EXPORT(invalid_pte_table) - - .org 0x5000 -/* XXX This label is required to keep GAS trying to be too clever ... - Bug? */ -dummy: -/* - * Align to 8kb boundary for init_task_union which follows in the - * .text segment. - */ - .align 13 + /* + * This buffer is reserved for the use of the EJTAG debug + * handler. + */ + .data + EXPORT(ejtag_debug_buffer) + .fill 4 + + .comm kernelsp, NR_CPUS * 8, 8 + .comm pgd_current, NR_CPUS * 8, 8 + + .macro page name, order=0 + .globl \name +\name: .size \name, (_PAGE_SIZE << \order) + .org . + (_PAGE_SIZE << \order) + .type \name, @object + .endm + + .data + .align PAGE_SHIFT + + page swapper_pg_dir, _PGD_ORDER + page empty_bad_page, 0 + page empty_bad_page_table, 0 + page invalid_pte_table, 0 diff --git a/arch/mips/kernel/i8259.c b/arch/mips/kernel/i8259.c index 8b75f358a4cc..da1722afe716 100644 --- a/arch/mips/kernel/i8259.c +++ b/arch/mips/kernel/i8259.c @@ -11,11 +11,12 @@ #include <linux/delay.h> #include <linux/init.h> #include <linux/ioport.h> -#include <linux/irq.h> #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/spinlock.h> +#include <linux/sysdev.h> +#include <asm/i8259.h> #include <asm/io.h> void enable_8259A_irq(unsigned int irq); @@ -30,11 +31,12 @@ void disable_8259A_irq(unsigned int irq); * moves to arch independent land */ -spinlock_t i8259A_lock = SPIN_LOCK_UNLOCKED; +static spinlock_t i8259A_lock = SPIN_LOCK_UNLOCKED; static void end_8259A_irq (unsigned int irq) { - if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)) && + irq_desc[irq].action) enable_8259A_irq(irq); } @@ -43,7 +45,7 @@ static void end_8259A_irq (unsigned int irq) void mask_and_ack_8259A(unsigned int); static unsigned int startup_8259A_irq(unsigned int irq) -{ +{ enable_8259A_irq(irq); return 0; /* never anything pending */ @@ -69,9 +71,8 @@ static struct hw_interrupt_type i8259A_irq_type = { */ static unsigned int cached_irq_mask = 0xffff; -#define __byte(x,y) (((unsigned char *)&(y))[x]) -#define cached_21 (__byte(0,cached_irq_mask)) -#define cached_A1 (__byte(1,cached_irq_mask)) +#define cached_21 (cached_irq_mask) +#define cached_A1 (cached_irq_mask >> 8) void disable_8259A_irq(unsigned int irq) { @@ -211,7 +212,7 @@ spurious_8259A_irq: printk("spurious 8259A interrupt: IRQ%d.\n", irq); spurious_irq_mask |= irqmask; } - irq_err_count++; + atomic_inc(&irq_err_count); /* * Theoretically we do not have to handle this IRQ, * but in Linux this does not cause problems and is @@ -221,6 +222,32 @@ spurious_8259A_irq: } } +static int i8259A_resume(struct sys_device *dev) +{ + init_8259A(0); + return 0; +} + +static struct sysdev_class i8259_sysdev_class = { + set_kset_name("i8259"), + .resume = i8259A_resume, +}; + +static struct sys_device device_i8259A = { + .id = 0, + .cls = &i8259_sysdev_class, +}; + +static int __init i8259A_init_sysfs(void) +{ + int error = sysdev_class_register(&i8259_sysdev_class); + if (!error) + error = sys_device_register(&device_i8259A); + return error; +} + +device_initcall(i8259A_init_sysfs); + void __init init_8259A(int auto_eoi) { unsigned long flags; @@ -234,7 +261,7 @@ void __init init_8259A(int auto_eoi) * outb_p - this has to work on a wide range of PC hardware. */ outb_p(0x11, 0x20); /* ICW1: select 8259A-1 init */ - outb_p(0x20 + 0, 0x21); /* ICW2: 8259A-1 IR0-7 mapped to 0x20-0x27 */ + outb_p(0x00, 0x21); /* ICW2: 8259A-1 IR0-7 mapped to 0x00-0x07 */ outb_p(0x04, 0x21); /* 8259A-1 (the master) has a slave on IR2 */ if (auto_eoi) outb_p(0x03, 0x21); /* master does Auto EOI */ @@ -242,7 +269,7 @@ void __init init_8259A(int auto_eoi) outb_p(0x01, 0x21); /* master expects normal EOI */ outb_p(0x11, 0xA0); /* ICW1: select 8259A-2 init */ - outb_p(0x20 + 8, 0xA1); /* ICW2: 8259A-2 IR0-7 mapped to 0x28-0x2f */ + outb_p(0x08, 0xA1); /* ICW2: 8259A-2 IR0-7 mapped to 0x08-0x0f */ outb_p(0x02, 0xA1); /* 8259A-2 is a slave on master's IR2 */ outb_p(0x01, 0xA1); /* (slave's support for AEOI in flat mode is to be investigated) */ diff --git a/arch/mips/kernel/init_task.c b/arch/mips/kernel/init_task.c index 0cba234e7181..a3540eaa199d 100644 --- a/arch/mips/kernel/init_task.c +++ b/arch/mips/kernel/init_task.c @@ -1,17 +1,19 @@ #include <linux/mm.h> #include <linux/sched.h> #include <linux/init_task.h> +#include <linux/fs.h> #include <asm/uaccess.h> #include <asm/pgtable.h> static struct fs_struct init_fs = INIT_FS; static struct files_struct init_files = INIT_FILES; -static struct signal_struct init_signals = INIT_SIGNALS; +static struct signal_struct init_signals = INIT_SIGNALS(init_signals); +static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand); struct mm_struct init_mm = INIT_MM(init_mm); /* - * Initial task structure. + * Initial thread structure. * * We need to make sure that this is 8192-byte aligned due to the * way process stacks are handled. This is done by making sure @@ -20,6 +22,13 @@ struct mm_struct init_mm = INIT_MM(init_mm); * * The things we do for performance.. */ -union task_union init_task_union - __attribute__((__section__(".text"))) = - { INIT_TASK(init_task_union.task) }; +union thread_union init_thread_union + __attribute__((__section__(".data.init_task"))) = + { INIT_THREAD_INFO(init_task) }; + +/* + * Initial task structure. + * + * All other task structs will be allocated on slabs in fork.c + */ +struct task_struct init_task = INIT_TASK(init_task); diff --git a/arch/mips/kernel/ioport.c b/arch/mips/kernel/ioport.c deleted file mode 100644 index cc0581038054..000000000000 --- a/arch/mips/kernel/ioport.c +++ /dev/null @@ -1,34 +0,0 @@ -/* - * linux/arch/mips/kernel/ioport.c - */ -#include <linux/sched.h> -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/types.h> -#include <linux/ioport.h> - -/* - * This changes the io permissions bitmap in the current task. - */ -asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int turn_on) -{ - return -ENOSYS; -} - -/* - * sys_iopl has to be used when you want to access the IO ports - * beyond the 0x3ff range: to get the full 65536 ports bitmapped - * you'd need 8kB of bitmaps/process, which is a bit excessive. - * - * Here we just change the eflags value on the stack: we allow - * only the super-user to do it. This depends on the stack-layout - * on system-call entry - see also fork() and the signal handling - * code. - */ -asmlinkage int sys_iopl(long ebx,long ecx,long edx, - long esi, long edi, long ebp, long eax, long ds, - long es, long fs, long gs, long orig_eax, - long eip,long cs,long eflags,long esp,long ss) -{ - return -ENOSYS; -} diff --git a/arch/mips/kernel/ipc.c b/arch/mips/kernel/ipc.c index 4db570ec88f7..6c242f1ebddf 100644 --- a/arch/mips/kernel/ipc.c +++ b/arch/mips/kernel/ipc.c @@ -45,7 +45,7 @@ asmlinkage int sys_ipc (uint call, int first, int second, } case MSGSND: - return sys_msgsnd (first, (struct msgbuf *) ptr, + return sys_msgsnd (first, (struct msgbuf *) ptr, second, third); case MSGRCV: switch (version) { @@ -53,9 +53,9 @@ asmlinkage int sys_ipc (uint call, int first, int second, struct ipc_kludge tmp; if (!ptr) return -EINVAL; - + if (copy_from_user(&tmp, - (struct ipc_kludge *) ptr, + (struct ipc_kludge *) ptr, sizeof (tmp))) return -EFAULT; return sys_msgrcv (first, tmp.msgp, second, @@ -85,7 +85,7 @@ asmlinkage int sys_ipc (uint call, int first, int second, return -EINVAL; return sys_shmat (first, (char *) ptr, second, (ulong *) third); } - case SHMDT: + case SHMDT: return sys_shmdt ((char *)ptr); case SHMGET: return sys_shmget (first, second, third); @@ -93,6 +93,6 @@ asmlinkage int sys_ipc (uint call, int first, int second, return sys_shmctl (first, second, (struct shmid_ds *) ptr); default: - return -EINVAL; + return -ENOSYS; } } diff --git a/arch/mips/kernel/irix5sys.h b/arch/mips/kernel/irix5sys.h index 2d344c3010e0..b0aa94206c93 100644 --- a/arch/mips/kernel/irix5sys.h +++ b/arch/mips/kernel/irix5sys.h @@ -1,5 +1,4 @@ -/* $Id: irix5sys.h,v 1.2 1998/08/17 10:16:25 ralf Exp $ - * +/* * irix5sys.h: 32-bit IRIX5 ABI system call table. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) diff --git a/arch/mips/kernel/irixelf.c b/arch/mips/kernel/irixelf.c index 93d4f7e177fa..e6908ad74566 100644 --- a/arch/mips/kernel/irixelf.c +++ b/arch/mips/kernel/irixelf.c @@ -45,7 +45,6 @@ static int load_irix_binary(struct linux_binprm * bprm, struct pt_regs * regs); static int load_irix_library(struct file *); static int irix_core_dump(long signr, struct pt_regs * regs, struct file *file); -extern int dump_fpu (elf_fpregset_t *); static struct linux_binfmt irix_format = { NULL, THIS_MODULE, load_irix_binary, load_irix_library, @@ -127,7 +126,7 @@ static void set_brk(unsigned long start, unsigned long end) { start = PAGE_ALIGN(start); end = PAGE_ALIGN(end); - if (end <= start) + if (end <= start) return; do_brk(start, end - start); } @@ -157,7 +156,7 @@ unsigned long * create_irix_tables(char * p, int argc, int envc, elf_addr_t *argv; elf_addr_t *envp; elf_addr_t *sp, *csp; - + #ifdef DEBUG_ELF printk("create_irix_tables: p[%p] argc[%d] envc[%d] " "load_addr[%08x] interp_load_addr[%08x]\n", @@ -246,7 +245,7 @@ static unsigned int load_irix_interp(struct elfhdr * interp_elf_ex, elf_bss = 0; last_bss = 0; error = load_addr = 0; - + #ifdef DEBUG_ELF print_elfhdr(interp_elf_ex); #endif @@ -267,7 +266,7 @@ static unsigned int load_irix_interp(struct elfhdr * interp_elf_ex, return 0xffffffff; } - elf_phdata = (struct elf_phdr *) + elf_phdata = (struct elf_phdr *) kmalloc(sizeof(struct elf_phdr) * interp_elf_ex->e_phnum, GFP_KERNEL); @@ -393,7 +392,7 @@ static int verify_binary(struct elfhdr *ehp, struct linux_binprm *bprm) return -ENOEXEC; /* First of all, some simple consistency checks */ - if((ehp->e_type != ET_EXEC && ehp->e_type != ET_DYN) || + if((ehp->e_type != ET_EXEC && ehp->e_type != ET_DYN) || !irix_elf_check_arch(ehp) || !bprm->file->f_op->mmap) { return -ENOEXEC; } @@ -557,7 +556,7 @@ static inline int map_interpreter(struct elf_phdr *epp, struct elfhdr *ihp, } /* - * IRIX maps a page at 0x200000 that holds information about the + * IRIX maps a page at 0x200000 that holds information about the * process and the system, here we map the page and fill the * structure */ @@ -567,20 +566,20 @@ void irix_map_prda_page (void) struct prda *pp; v = do_brk (PRDA_ADDRESS, PAGE_SIZE); - + if (v < 0) return; pp = (struct prda *) v; pp->prda_sys.t_pid = current->pid; - pp->prda_sys.t_prid = read_32bit_cp0_register (CP0_PRID); + pp->prda_sys.t_prid = read_c0_prid(); pp->prda_sys.t_rpid = current->pid; /* We leave the rest set to zero */ } - - + + /* These are the functions used to load ELF style executables and shared * libraries. There is no binary dependent code anywhere else. */ @@ -595,7 +594,7 @@ static int load_irix_binary(struct linux_binprm * bprm, struct pt_regs * regs) int retval, has_interp, has_ephdr, size, i; char *elf_interpreter; mm_segment_t old_fs; - + load_addr = 0; has_interp = has_ephdr = 0; elf_ihdr = elf_ephdr = 0; @@ -684,7 +683,7 @@ static int load_irix_binary(struct linux_binprm * bprm, struct pt_regs * regs) current->mm->mmap = NULL; current->flags &= ~PF_FORKNOEXEC; elf_entry = (unsigned int) elf_ex.e_entry; - + /* Do this so that we can load the interpreter, if need be. We will * change some of these later. */ @@ -723,7 +722,7 @@ static int load_irix_binary(struct linux_binprm * bprm, struct pt_regs * regs) set_binfmt(&irix_format); compute_creds(bprm); current->flags &= ~PF_FORKNOEXEC; - bprm->p = (unsigned long) + bprm->p = (unsigned long) create_irix_tables((char *)bprm->p, bprm->argc, bprm->envc, (elf_interpreter ? &elf_ex : NULL), load_addr, interp_load_addr, regs, elf_ephdr); @@ -811,30 +810,30 @@ static int load_irix_library(struct file *file) if(elf_ex.e_type != ET_EXEC || elf_ex.e_phnum > 2 || !irix_elf_check_arch(&elf_ex) || !file->f_op->mmap) return -ENOEXEC; - + /* Now read in all of the header information. */ if(sizeof(struct elf_phdr) * elf_ex.e_phnum > PAGE_SIZE) return -ENOEXEC; - - elf_phdata = (struct elf_phdr *) + + elf_phdata = (struct elf_phdr *) kmalloc(sizeof(struct elf_phdr) * elf_ex.e_phnum, GFP_KERNEL); if (elf_phdata == NULL) return -ENOMEM; - + retval = kernel_read(file, elf_ex.e_phoff, (char *) elf_phdata, sizeof(struct elf_phdr) * elf_ex.e_phnum); - + j = 0; for(i=0; i<elf_ex.e_phnum; i++) if((elf_phdata + i)->p_type == PT_LOAD) j++; - + if(j != 1) { kfree(elf_phdata); return -ENOEXEC; } - + while(elf_phdata->p_type != PT_LOAD) elf_phdata++; - + /* Now use mmap to map the library into memory. */ down_write(¤t->mm->mmap_sem); error = do_mmap(file, @@ -862,7 +861,7 @@ static int load_irix_library(struct file *file) kfree(elf_phdata); return 0; } - + /* Called through irix_syssgi() to map an elf image given an FD, * a phdr ptr USER_PHDRP in userspace, and a count CNT telling how many * phdrs there are in the USER_PHDRP array. We return the vaddr the @@ -1025,7 +1024,7 @@ static int writenote(struct memelfnote *men, struct file *file) DUMP_SEEK(roundup((unsigned long)file->f_pos, 4)); /* XXX */ DUMP_WRITE(men->data, men->datasz); DUMP_SEEK(roundup((unsigned long)file->f_pos, 4)); /* XXX */ - + return 1; end_coredump: @@ -1071,13 +1070,13 @@ static int irix_core_dump(long signr, struct pt_regs * regs, struct file *file) if (maydump(vma)) { int sz = vma->vm_end-vma->vm_start; - + if (size+sz >= limit) break; else size += sz; } - + segs++; } #ifdef DEBUG @@ -1104,7 +1103,7 @@ static int irix_core_dump(long signr, struct pt_regs * regs, struct file *file) elf.e_shentsize = 0; elf.e_shnum = 0; elf.e_shstrndx = 0; - + fs = get_fs(); set_fs(KERNEL_DS); @@ -1129,24 +1128,24 @@ static int irix_core_dump(long signr, struct pt_regs * regs, struct file *file) prstatus.pr_sigpend = current->pending.signal.sig[0]; prstatus.pr_sighold = current->blocked.sig[0]; psinfo.pr_pid = prstatus.pr_pid = current->pid; - psinfo.pr_ppid = prstatus.pr_ppid = current->p_pptr->pid; + psinfo.pr_ppid = prstatus.pr_ppid = current->parent->pid; psinfo.pr_pgrp = prstatus.pr_pgrp = current->pgrp; psinfo.pr_sid = prstatus.pr_sid = current->session; - prstatus.pr_utime.tv_sec = CT_TO_SECS(current->times.tms_utime); - prstatus.pr_utime.tv_usec = CT_TO_USECS(current->times.tms_utime); - prstatus.pr_stime.tv_sec = CT_TO_SECS(current->times.tms_stime); - prstatus.pr_stime.tv_usec = CT_TO_USECS(current->times.tms_stime); - prstatus.pr_cutime.tv_sec = CT_TO_SECS(current->times.tms_cutime); - prstatus.pr_cutime.tv_usec = CT_TO_USECS(current->times.tms_cutime); - prstatus.pr_cstime.tv_sec = CT_TO_SECS(current->times.tms_cstime); - prstatus.pr_cstime.tv_usec = CT_TO_USECS(current->times.tms_cstime); + prstatus.pr_utime.tv_sec = CT_TO_SECS(current->utime); + prstatus.pr_utime.tv_usec = CT_TO_USECS(current->utime); + prstatus.pr_stime.tv_sec = CT_TO_SECS(current->stime); + prstatus.pr_stime.tv_usec = CT_TO_USECS(current->stime); + prstatus.pr_cutime.tv_sec = CT_TO_SECS(current->cutime); + prstatus.pr_cutime.tv_usec = CT_TO_USECS(current->cutime); + prstatus.pr_cstime.tv_sec = CT_TO_SECS(current->cstime); + prstatus.pr_cstime.tv_usec = CT_TO_USECS(current->cstime); if (sizeof(elf_gregset_t) != sizeof(struct pt_regs)) { printk("sizeof(elf_gregset_t) (%d) != sizeof(struct pt_regs) " "(%d)\n", sizeof(elf_gregset_t), sizeof(struct pt_regs)); } else { *(struct pt_regs *)&prstatus.pr_reg = *regs; } - + notes[1].name = "CORE"; notes[1].type = NT_PRPSINFO; notes[1].datasz = sizeof(psinfo); @@ -1155,7 +1154,7 @@ static int irix_core_dump(long signr, struct pt_regs * regs, struct file *file) psinfo.pr_state = i; psinfo.pr_sname = (i < 0 || i > 5) ? '.' : "RSDZTD"[i]; psinfo.pr_zomb = psinfo.pr_sname == 'Z'; - psinfo.pr_nice = current->nice; + psinfo.pr_nice = task_nice(current); psinfo.pr_flag = current->flags; psinfo.pr_uid = current->uid; psinfo.pr_gid = current->gid; @@ -1163,7 +1162,7 @@ static int irix_core_dump(long signr, struct pt_regs * regs, struct file *file) int i, len; set_fs(fs); - + len = current->mm->arg_end - current->mm->arg_start; len = len >= ELF_PRARGSZ ? ELF_PRARGSZ : len; copy_from_user(&psinfo.pr_psargs, @@ -1183,7 +1182,7 @@ static int irix_core_dump(long signr, struct pt_regs * regs, struct file *file) notes[2].data = current; /* Try to dump the FPU. */ - prstatus.pr_fpvalid = dump_fpu (&fpu); + prstatus.pr_fpvalid = dump_fpu (regs, &fpu); if (!prstatus.pr_fpvalid) { numnote--; } else { @@ -1200,7 +1199,7 @@ static int irix_core_dump(long signr, struct pt_regs * regs, struct file *file) for(i = 0; i < numnote; i++) sz += notesize(¬es[i]); - + phdr.p_type = PT_NOTE; phdr.p_offset = offset; phdr.p_vaddr = 0; @@ -1216,7 +1215,7 @@ static int irix_core_dump(long signr, struct pt_regs * regs, struct file *file) /* Page-align dumped data. */ dataoff = offset = roundup(offset, PAGE_SIZE); - + /* Write program headers for segments dump. */ for(vma = current->mm->mmap, i = 0; i < segs && vma != NULL; vma = vma->vm_next) { @@ -1226,7 +1225,7 @@ static int irix_core_dump(long signr, struct pt_regs * regs, struct file *file) i++; sz = vma->vm_end - vma->vm_start; - + phdr.p_type = PT_LOAD; phdr.p_offset = offset; phdr.p_vaddr = vma->vm_start; @@ -1245,17 +1244,17 @@ static int irix_core_dump(long signr, struct pt_regs * regs, struct file *file) for(i = 0; i < numnote; i++) if (!writenote(¬es[i], file)) goto end_coredump; - + set_fs(fs); DUMP_SEEK(dataoff); - + for(i = 0, vma = current->mm->mmap; i < segs && vma != NULL; vma = vma->vm_next) { unsigned long addr = vma->vm_start; unsigned long len = vma->vm_end - vma->vm_start; - + if (!maydump(vma)) continue; i++; diff --git a/arch/mips/kernel/irixinv.c b/arch/mips/kernel/irixinv.c index 9028a27be9c2..eea2582451e4 100644 --- a/arch/mips/kernel/irixinv.c +++ b/arch/mips/kernel/irixinv.c @@ -1,11 +1,9 @@ /* * Support the inventory interface for IRIX binaries * This is invoked before the mm layer is working, so we do not - * use the linked lists for the inventory yet. + * use the linked lists for the inventory yet. * * Miguel de Icaza, 1997. - * - * $Id: irixinv.c,v 1.3 1998/04/05 11:23:51 ralf Exp $ */ #include <linux/mm.h> #include <linux/init.h> @@ -18,14 +16,13 @@ int inventory_items = 0; static inventory_t inventory [MAX_INVENTORY]; -void -add_to_inventory (int class, int type, int controller, int unit, int state) +void add_to_inventory (int class, int type, int controller, int unit, int state) { inventory_t *ni = &inventory [inventory_items]; if (inventory_items == MAX_INVENTORY) return; - + ni->inv_class = class; ni->inv_type = type; ni->inv_controller = controller; @@ -35,8 +32,7 @@ add_to_inventory (int class, int type, int controller, int unit, int state) inventory_items++; } -int -dump_inventory_to_user (void *userbuf, int size) +int dump_inventory_to_user (void *userbuf, int size) { inventory_t *inv = &inventory [0]; inventory_t *user = userbuf; @@ -53,13 +49,13 @@ dump_inventory_to_user (void *userbuf, int size) return inventory_items * sizeof (inventory_t); } -void __init init_inventory (void) +static int __init init_inventory(void) { - /* gross hack while we put the right bits all over the kernel + /* + * gross hack while we put the right bits all over the kernel * most likely this will not let just anyone run the X server * until we put the right values all over the place */ - add_to_inventory (10, 3, 0, 0, 16400); add_to_inventory (1, 1, 150, -1, 12); add_to_inventory (1, 3, 0, 0, 8976); @@ -78,4 +74,8 @@ void __init init_inventory (void) add_to_inventory (2, 2, 0, 2, 0); add_to_inventory (2, 2, 0, 1, 0); add_to_inventory (7, 14, 0, 0, 6); + + return 0; } + +module_init(init_inventory); diff --git a/arch/mips/kernel/irixioctl.c b/arch/mips/kernel/irixioctl.c index 1ad9ad3399c8..f458c392d8fe 100644 --- a/arch/mips/kernel/irixioctl.c +++ b/arch/mips/kernel/irixioctl.c @@ -10,6 +10,7 @@ #include <linux/mm.h> #include <linux/smp.h> #include <linux/smp_lock.h> +#include <linux/sockios.h> #include <linux/tty.h> #include <linux/file.h> diff --git a/arch/mips/kernel/irixsig.c b/arch/mips/kernel/irixsig.c index 7d68e0e659fc..e6713367ce67 100644 --- a/arch/mips/kernel/irixsig.c +++ b/arch/mips/kernel/irixsig.c @@ -17,7 +17,7 @@ #include <asm/ptrace.h> #include <asm/uaccess.h> -extern asmlinkage void syscall_trace(void); +extern asmlinkage void do_syscall_trace(void); #undef DEBUG_SIG @@ -117,7 +117,8 @@ static void setup_irix_frame(struct k_sigaction *ka, struct pt_regs *regs, regs->regs[5] = 0; /* XXX sigcode XXX */ regs->regs[6] = regs->regs[29] = sp; regs->regs[7] = (unsigned long) ka->sa.sa_handler; - regs->regs[25] = regs->cp0_epc = (unsigned long) ka->sa.sa_restorer; + regs->regs[25] = regs->cp0_epc = (unsigned long) ka->sa_restorer; + return; segv_and_exit: @@ -134,27 +135,11 @@ setup_irix_rt_frame(struct k_sigaction * ka, struct pt_regs *regs, do_exit(SIGSEGV); } -static inline void handle_signal(unsigned long sig, struct k_sigaction *ka, - siginfo_t *info, sigset_t *oldset, struct pt_regs * regs) +static inline void handle_signal(unsigned long sig, siginfo_t *info, + sigset_t *oldset, struct pt_regs * regs) { - if (ka->sa.sa_flags & SA_SIGINFO) - setup_irix_rt_frame(ka, regs, sig, oldset, info); - else - setup_irix_frame(ka, regs, sig, oldset); + struct k_sigaction *ka = ¤t->sighand->action[sig-1]; - if (ka->sa.sa_flags & SA_ONESHOT) - ka->sa.sa_handler = SIG_DFL; - if (!(ka->sa.sa_flags & SA_NODEFER)) { - spin_lock_irq(¤t->sigmask_lock); - sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); - sigaddset(¤t->blocked,sig); - recalc_sigpending(); - spin_unlock_irq(¤t->sigmask_lock); - } -} - -static inline void syscall_restart(struct pt_regs *regs, struct k_sigaction *ka) -{ switch(regs->regs[0]) { case ERESTARTNOHAND: regs->regs[2] = EINTR; @@ -170,111 +155,34 @@ static inline void syscall_restart(struct pt_regs *regs, struct k_sigaction *ka) } regs->regs[0] = 0; /* Don't deal with this again. */ + + if (ka->sa.sa_flags & SA_SIGINFO) + setup_irix_rt_frame(ka, regs, sig, oldset, info); + else + setup_irix_frame(ka, regs, sig, oldset); + + if (ka->sa.sa_flags & SA_ONESHOT) + ka->sa.sa_handler = SIG_DFL; + if (!(ka->sa.sa_flags & SA_NODEFER)) { + spin_lock_irq(¤t->sighand->siglock); + sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + sigaddset(¤t->blocked,sig); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + } } asmlinkage int do_irix_signal(sigset_t *oldset, struct pt_regs *regs) { - struct k_sigaction *ka; siginfo_t info; + int signr; if (!oldset) oldset = ¤t->blocked; - for (;;) { - unsigned long signr; - - spin_lock_irq(¤t->sigmask_lock); - signr = dequeue_signal(current, ¤t->blocked, &info); - spin_unlock_irq(¤t->sigmask_lock); - - if (!signr) - break; - - if ((current->ptrace & PT_PTRACED) && signr != SIGKILL) { - /* Let the debugger run. */ - current->exit_code = signr; - current->state = TASK_STOPPED; - notify_parent(current, SIGCHLD); - schedule(); - - /* We're back. Did the debugger cancel the sig? */ - if (!(signr = current->exit_code)) - continue; - current->exit_code = 0; - - /* The debugger continued. Ignore SIGSTOP. */ - if (signr == SIGSTOP) - continue; - - /* Update the siginfo structure. Is this good? */ - if (signr != info.si_signo) { - info.si_signo = signr; - info.si_errno = 0; - info.si_code = SI_USER; - info.si_pid = current->p_pptr->pid; - info.si_uid = current->p_pptr->uid; - } - - /* If the (new) signal is now blocked, requeue it. */ - if (sigismember(¤t->blocked, signr)) { - send_sig_info(signr, &info, current); - continue; - } - } - - ka = ¤t->sig->action[signr-1]; - if (ka->sa.sa_handler == SIG_IGN) { - if (signr != SIGCHLD) - continue; - /* Check for SIGCHLD: it's special. */ - while (sys_wait4(-1, NULL, WNOHANG, NULL) > 0) - /* nothing */; - continue; - } - - if (ka->sa.sa_handler == SIG_DFL) { - int exit_code = signr; - - /* Init gets no signals it doesn't want. */ - if (current->pid == 1) - continue; - - switch (signr) { - case SIGCONT: case SIGCHLD: case SIGWINCH: - continue; - - case SIGTSTP: case SIGTTIN: case SIGTTOU: - if (is_orphaned_pgrp(current->pgrp)) - continue; - /* FALLTHRU */ - - case SIGSTOP: - current->state = TASK_STOPPED; - current->exit_code = signr; - if (!(current->p_pptr->sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP)) - notify_parent(current, SIGCHLD); - schedule(); - continue; - - case SIGQUIT: case SIGILL: case SIGTRAP: - case SIGABRT: case SIGFPE: case SIGSEGV: - if (do_coredump(signr, regs)) - exit_code |= 0x80; - /* FALLTHRU */ - - default: - sigaddset(¤t->pending.signal, signr); - recalc_sigpending(); - current->flags |= PF_SIGNALED; - do_exit(exit_code); - /* NOTREACHED */ - } - } - - if (regs->regs[0]) - syscall_restart(regs, ka); - /* Whee! Actually deliver the signal. */ - handle_signal(signr, ka, &info, oldset, regs); + signr = get_signal_to_deliver(&info, regs, NULL); + if (signr > 0) { + handle_signal(signr, &info, oldset, regs); return 1; } @@ -343,19 +251,19 @@ irix_sigreturn(struct pt_regs *regs) goto badframe; sigdelsetmask(&blocked, ~_BLOCKABLE); - spin_lock_irq(¤t->sigmask_lock); + spin_lock_irq(¤t->sighand->siglock); current->blocked = blocked; recalc_sigpending(); - spin_unlock_irq(¤t->sigmask_lock); + spin_unlock_irq(¤t->sighand->siglock); /* * Don't let your children do this ... */ - if (current->ptrace & PT_TRACESYS) - syscall_trace(); + if (current_thread_info()->flags & TIF_SYSCALL_TRACE) + do_syscall_trace(); __asm__ __volatile__( "move\t$29,%0\n\t" - "j\tret_from_sys_call" + "j\tsyscall_exit" :/* no outputs */ :"r" (®s)); /* Unreached */ @@ -380,7 +288,7 @@ static inline void dump_sigact_irix5(struct sigact_irix5 *p) } #endif -asmlinkage int +asmlinkage int irix_sigaction(int sig, const struct sigaction *act, struct sigaction *oact, void *trampoline) { @@ -408,7 +316,7 @@ irix_sigaction(int sig, const struct sigaction *act, * value for all invocations of sigaction. Will have to * investigate. POSIX POSIX, die die die... */ - new_ka.sa.sa_restorer = trampoline; + new_ka.sa_restorer = trampoline; } /* XXX Implement SIG_SETMASK32 for IRIX compatibility */ @@ -443,7 +351,7 @@ asmlinkage int irix_sigprocmask(int how, irix_sigset_t *new, irix_sigset_t *old) __copy_from_user(&newbits, new, sizeof(unsigned long)*4); sigdelsetmask(&newbits, ~_BLOCKABLE); - spin_lock_irq(¤t->sigmask_lock); + spin_lock_irq(¤t->sighand->siglock); oldbits = current->blocked; switch(how) { @@ -466,7 +374,7 @@ asmlinkage int irix_sigprocmask(int how, irix_sigset_t *new, irix_sigset_t *old) return -EINVAL; } recalc_sigpending(); - spin_unlock_irq(¤t->sigmask_lock); + spin_unlock_irq(¤t->sighand->siglock); } if(old) { error = verify_area(VERIFY_WRITE, old, sizeof(*old)); @@ -487,11 +395,11 @@ asmlinkage int irix_sigsuspend(struct pt_regs *regs) return -EFAULT; sigdelsetmask(&newset, ~_BLOCKABLE); - spin_lock_irq(¤t->sigmask_lock); + spin_lock_irq(¤t->sighand->siglock); saveset = current->blocked; current->blocked = newset; recalc_sigpending(); - spin_unlock_irq(¤t->sigmask_lock); + spin_unlock_irq(¤t->sighand->siglock); regs->regs[2] = -EINTR; while (1) { @@ -640,7 +548,9 @@ asmlinkage int irix_waitsys(int type, int pid, struct irix5_siginfo *info, { int flag, retval; DECLARE_WAITQUEUE(wait, current); + struct task_struct *tsk; struct task_struct *p; + struct list_head *_p; if (!info) { retval = -EINVAL; @@ -667,7 +577,9 @@ repeat: flag = 0; current->state = TASK_INTERRUPTIBLE; read_lock(&tasklist_lock); - for (p = current->p_cptr; p; p = p->p_osptr) { + tsk = current; + list_for_each(_p,&tsk->children) { + p = list_entry(_p,struct task_struct,sibling); if ((type == P_PID) && p->pid != pid) continue; if ((type == P_PGID) && p->pgrp != pid) @@ -676,50 +588,63 @@ repeat: continue; flag = 1; switch (p->state) { - case TASK_STOPPED: - if (!p->exit_code) - continue; - if (!(options & (W_TRAPPED|W_STOPPED)) && - !(p->ptrace & PT_PTRACED)) - continue; - if (ru != NULL) - getrusage(p, RUSAGE_BOTH, ru); - __put_user(SIGCHLD, &info->sig); - __put_user(0, &info->code); - __put_user(p->pid, &info->stuff.procinfo.pid); - __put_user((p->exit_code >> 8) & 0xff, + case TASK_STOPPED: + if (!p->exit_code) + continue; + if (!(options & (W_TRAPPED|W_STOPPED)) && + !(p->ptrace & PT_PTRACED)) + continue; + read_unlock(&tasklist_lock); + + /* move to end of parent's list to avoid starvation */ + write_lock_irq(&tasklist_lock); + remove_parent(p); + add_parent(p, p->parent); + write_unlock_irq(&tasklist_lock); + retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0; + if (!retval && ru) { + retval |= __put_user(SIGCHLD, &info->sig); + retval |= __put_user(0, &info->code); + retval |= __put_user(p->pid, &info->stuff.procinfo.pid); + retval |= __put_user((p->exit_code >> 8) & 0xff, &info->stuff.procinfo.procdata.child.status); - __put_user(p->times.tms_utime, &info->stuff.procinfo.procdata.child.utime); - __put_user(p->times.tms_stime, &info->stuff.procinfo.procdata.child.stime); + retval |= __put_user(p->utime, &info->stuff.procinfo.procdata.child.utime); + retval |= __put_user(p->stime, &info->stuff.procinfo.procdata.child.stime); + } + if (!retval) { p->exit_code = 0; - retval = 0; - goto end_waitsys; - case TASK_ZOMBIE: - current->times.tms_cutime += p->times.tms_utime + p->times.tms_cutime; - current->times.tms_cstime += p->times.tms_stime + p->times.tms_cstime; - if (ru != NULL) - getrusage(p, RUSAGE_BOTH, ru); - __put_user(SIGCHLD, &info->sig); - __put_user(1, &info->code); /* CLD_EXITED */ - __put_user(p->pid, &info->stuff.procinfo.pid); - __put_user((p->exit_code >> 8) & 0xff, - &info->stuff.procinfo.procdata.child.status); - __put_user(p->times.tms_utime, - &info->stuff.procinfo.procdata.child.utime); - __put_user(p->times.tms_stime, - &info->stuff.procinfo.procdata.child.stime); - retval = 0; - if (p->p_opptr != p->p_pptr) { - REMOVE_LINKS(p); - p->p_pptr = p->p_opptr; - SET_LINKS(p); - notify_parent(p, SIGCHLD); - } else - release_task(p); - goto end_waitsys; - default: - continue; + } + goto end_waitsys; + + case TASK_ZOMBIE: + current->cutime += p->utime + p->cutime; + current->cstime += p->stime + p->cstime; + if (ru != NULL) + getrusage(p, RUSAGE_BOTH, ru); + __put_user(SIGCHLD, &info->sig); + __put_user(1, &info->code); /* CLD_EXITED */ + __put_user(p->pid, &info->stuff.procinfo.pid); + __put_user((p->exit_code >> 8) & 0xff, + &info->stuff.procinfo.procdata.child.status); + __put_user(p->utime, + &info->stuff.procinfo.procdata.child.utime); + __put_user(p->stime, + &info->stuff.procinfo.procdata.child.stime); + retval = 0; + if (p->real_parent != p->parent) { + write_lock_irq(&tasklist_lock); + remove_parent(p); + p->parent = p->real_parent; + add_parent(p, p->parent); + do_notify_parent(p, SIGCHLD); + write_unlock_irq(&tasklist_lock); + } else + release_task(p); + goto end_waitsys; + default: + continue; } + tsk = next_thread(tsk); } read_unlock(&tasklist_lock); if (flag) { diff --git a/arch/mips/kernel/irq.c b/arch/mips/kernel/irq.c index a40469ff4790..3cd759d24bf0 100644 --- a/arch/mips/kernel/irq.c +++ b/arch/mips/kernel/irq.c @@ -8,18 +8,24 @@ * Copyright (C) 1992 Linus Torvalds * Copyright (C) 1994 - 2000 Ralf Baechle */ +#include <linux/config.h> #include <linux/kernel.h> -#include <linux/irq.h> +#include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/kernel_stat.h> +#include <linux/module.h> +#include <linux/proc_fs.h> #include <linux/slab.h> #include <linux/mm.h> #include <linux/random.h> #include <linux/sched.h> #include <linux/seq_file.h> +#include <linux/kallsyms.h> +#include <asm/atomic.h> #include <asm/system.h> +#include <asm/uaccess.h> /* * Controller mappings for all interrupt sources: @@ -31,11 +37,14 @@ irq_desc_t irq_desc[NR_IRQS] __cacheline_aligned = { } }; +static void register_irq_proc (unsigned int irq); + /* * Special irq handlers. */ -void no_action(int cpl, void *dev_id, struct pt_regs *regs) { } +irqreturn_t no_action(int cpl, void *dev_id, struct pt_regs *regs) +{ return IRQ_NONE; } /* * Generic no controller code @@ -68,7 +77,7 @@ struct hw_interrupt_type no_irq_type = { end_none }; -volatile unsigned long irq_err_count, spurious_count; +atomic_t irq_err_count; /* * Generic, controller-independent functions: @@ -76,35 +85,53 @@ volatile unsigned long irq_err_count, spurious_count; int show_interrupts(struct seq_file *p, void *v) { + int i, j; struct irqaction * action; unsigned long flags; - int i; - - seq_puts(p, " "); - for (i=0; i < 1 /*smp_num_cpus*/; i++) - seq_printf(p, "CPU%d ", i); + + seq_printf(p, " "); + for (j=0; j<NR_CPUS; j++) + if (cpu_online(j)) + seq_printf(p, "CPU%d ",j); seq_putc(p, '\n'); for (i = 0 ; i < NR_IRQS ; i++) { spin_lock_irqsave(&irq_desc[i].lock, flags); action = irq_desc[i].action; if (!action) - goto unlock; + goto skip; seq_printf(p, "%3d: ",i); +#ifndef CONFIG_SMP seq_printf(p, "%10u ", kstat_irqs(i)); +#else + for (j = 0; j < NR_CPUS; j++) + if (cpu_online(j)) + seq_printf(p, "%10u ", kstat_cpu(j).irqs[i]); +#endif seq_printf(p, " %14s", irq_desc[i].handler->typename); seq_printf(p, " %s", action->name); for (action=action->next; action; action = action->next) seq_printf(p, ", %s", action->name); + seq_putc(p, '\n'); -unlock: +skip: spin_unlock_irqrestore(&irq_desc[i].lock, flags); } - seq_printf(p, "ERR: %10lu\n", irq_err_count); + seq_putc(p, '\n'); + seq_printf(p, "ERR: %10u\n", atomic_read(&irq_err_count)); + return 0; } +#ifdef CONFIG_SMP +inline void synchronize_irq(unsigned int irq) +{ + while (irq_desc[irq].status & IRQ_INPROGRESS) + cpu_relax(); +} +#endif + /* * This should really return information about whether * we should do bottom half handling etc. Right now we @@ -114,37 +141,112 @@ unlock: */ int handle_IRQ_event(unsigned int irq, struct pt_regs * regs, struct irqaction * action) { - int status; - int cpu = smp_processor_id(); - - irq_enter(cpu, irq); - - status = 1; /* Force the "do bottom halves" bit */ + int status = 1; /* Force the "do bottom halves" bit */ + int retval = 0; if (!(action->flags & SA_INTERRUPT)) local_irq_enable(); do { status |= action->flags; - action->handler(irq, action->dev_id, regs); + retval |= action->handler(irq, action->dev_id, regs); action = action->next; } while (action); if (status & SA_SAMPLE_RANDOM) add_interrupt_randomness(irq); local_irq_disable(); - irq_exit(cpu, irq); + return retval; +} + +static void __report_bad_irq(int irq, irq_desc_t *desc, irqreturn_t action_ret) +{ + struct irqaction *action; + + if (action_ret != IRQ_HANDLED && action_ret != IRQ_NONE) { + printk(KERN_ERR "irq event %d: bogus return value %x\n", + irq, action_ret); + } else { + printk(KERN_ERR "irq %d: nobody cared!\n", irq); + } + dump_stack(); + printk(KERN_ERR "handlers:\n"); + action = desc->action; + do { + printk(KERN_ERR "[<%p>]", action->handler); + print_symbol(" (%s)", + (unsigned long)action->handler); + printk("\n"); + action = action->next; + } while (action); +} + +static void report_bad_irq(int irq, irq_desc_t *desc, irqreturn_t action_ret) +{ + static int count = 100; + + if (count) { + count--; + __report_bad_irq(irq, desc, action_ret); + } +} + +static int noirqdebug; + +static int __init noirqdebug_setup(char *str) +{ + noirqdebug = 1; + printk("IRQ lockup detection disabled\n"); + return 1; +} + +__setup("noirqdebug", noirqdebug_setup); + +/* + * If 99,900 of the previous 100,000 interrupts have not been handled then + * assume that the IRQ is stuck in some manner. Drop a diagnostic and try to + * turn the IRQ off. + * + * (The other 100-of-100,000 interrupts may have been a correctly-functioning + * device sharing an IRQ with the failing one) + * + * Called under desc->lock + */ +static void note_interrupt(int irq, irq_desc_t *desc, irqreturn_t action_ret) +{ + if (action_ret != IRQ_HANDLED) { + desc->irqs_unhandled++; + if (action_ret != IRQ_NONE) + report_bad_irq(irq, desc, action_ret); + } + + desc->irq_count++; + if (desc->irq_count < 100000) + return; - return status; + desc->irq_count = 0; + if (desc->irqs_unhandled > 99900) { + /* + * The interrupt is stuck + */ + __report_bad_irq(irq, desc, action_ret); + /* + * Now kill the IRQ + */ + printk(KERN_EMERG "Disabling IRQ #%d\n", irq); + desc->status |= IRQ_DISABLED; + desc->handler->disable(irq); + } + desc->irqs_unhandled = 0; } /* * Generic enable/disable code: this just calls * down into the PIC-specific version for the actual * hardware disable after having gotten the irq - * controller lock. + * controller lock. */ - + /** * disable_irq_nosync - disable an irq without waiting * @irq: Interrupt to disable @@ -155,7 +257,7 @@ int handle_IRQ_event(unsigned int irq, struct pt_regs * regs, struct irqaction * * * This function may be called from IRQ context. */ - + void inline disable_irq_nosync(unsigned int irq) { irq_desc_t *desc = irq_desc + irq; @@ -181,16 +283,11 @@ void inline disable_irq_nosync(unsigned int irq) * * This function may be called - with care - from IRQ context. */ - + void disable_irq(unsigned int irq) { disable_irq_nosync(irq); - - if (!local_irq_count(smp_processor_id())) { - do { - barrier(); - } while (irq_desc[irq].status & IRQ_INPROGRESS); - } + synchronize_irq(irq); } /** @@ -202,7 +299,7 @@ void disable_irq(unsigned int irq) * * This function may be called from IRQ context. */ - + void enable_irq(unsigned int irq) { irq_desc_t *desc = irq_desc + irq; @@ -237,7 +334,7 @@ void enable_irq(unsigned int irq) */ asmlinkage unsigned int do_IRQ(int irq, struct pt_regs *regs) { - /* + /* * We ack quickly, we don't want the irq controller * thinking we're snobs just because some other CPU has * disabled global interrupts (we have already done the @@ -252,6 +349,7 @@ asmlinkage unsigned int do_IRQ(int irq, struct pt_regs *regs) struct irqaction * action; unsigned int status; + irq_enter(); kstat_cpu(cpu).irqs[irq]++; spin_lock(&desc->lock); desc->handler->ack(irq); @@ -267,7 +365,7 @@ asmlinkage unsigned int do_IRQ(int irq, struct pt_regs *regs) * use the action we have. */ action = NULL; - if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) { + if (likely(!(status & (IRQ_DISABLED | IRQ_INPROGRESS)))) { action = desc->action; status &= ~IRQ_PENDING; /* we commit to handling */ status |= IRQ_INPROGRESS; /* we are handling it */ @@ -280,7 +378,7 @@ asmlinkage unsigned int do_IRQ(int irq, struct pt_regs *regs) a different instance of this same irq, the other processor will take care of it. */ - if (!action) + if (unlikely(!action)) goto out; /* @@ -294,15 +392,19 @@ asmlinkage unsigned int do_IRQ(int irq, struct pt_regs *regs) * SMP environment. */ for (;;) { + irqreturn_t action_ret; + spin_unlock(&desc->lock); - handle_IRQ_event(irq, regs, action); + action_ret = handle_IRQ_event(irq, ®s, action); spin_lock(&desc->lock); - - if (!(desc->status & IRQ_PENDING)) + if (!noirqdebug) + note_interrupt(irq, desc, action_ret); + if (likely(!(desc->status & IRQ_PENDING))) break; desc->status &= ~IRQ_PENDING; } desc->status &= ~IRQ_INPROGRESS; + out: /* * The ->end() handler has to deal with interrupts which got @@ -311,8 +413,8 @@ out: desc->handler->end(irq); spin_unlock(&desc->lock); - if (softirq_pending(cpu)) - do_softirq(); + irq_exit(); + return 1; } @@ -327,7 +429,7 @@ out: * This call allocates interrupt resources and enables the * interrupt line and IRQ handling. From the point this * call is made your handler function may be invoked. Since - * your handler function must clear any interrupt the board + * your handler function must clear any interrupt the board * raises, you must take care both to initialise your hardware * and to set up the interrupt handler in the right order. * @@ -347,10 +449,10 @@ out: * SA_SAMPLE_RANDOM The interrupt can be used for entropy * */ - -int request_irq(unsigned int irq, - void (*handler)(int, void *, struct pt_regs *), - unsigned long irqflags, + +int request_irq(unsigned int irq, + irqreturn_t (*handler)(int, void *, struct pt_regs *), + unsigned long irqflags, const char * devname, void *dev_id) { @@ -376,7 +478,7 @@ int request_irq(unsigned int irq, return -EINVAL; action = (struct irqaction *) - kmalloc(sizeof(struct irqaction), GFP_KERNEL); + kmalloc(sizeof(struct irqaction), GFP_ATOMIC); if (!action) return -ENOMEM; @@ -405,12 +507,9 @@ int request_irq(unsigned int irq, * does not return until any executing interrupts for this IRQ * have completed. * - * This function may be called from interrupt context. - * - * Bugs: Attempting to free an irq in a handler for the same irq hangs - * the machine. + * This function must not be called from interrupt context. */ - + void free_irq(unsigned int irq, void *dev_id) { irq_desc_t *desc; @@ -439,11 +538,8 @@ void free_irq(unsigned int irq, void *dev_id) } spin_unlock_irqrestore(&desc->lock,flags); -#ifdef CONFIG_SMP /* Wait to make sure it's not being used on another CPU */ - while (desc->status & IRQ_INPROGRESS) - barrier(); -#endif + synchronize_irq(irq); kfree(action); return; } @@ -471,7 +567,7 @@ static DECLARE_MUTEX(probe_sem); * and a mask of potential interrupt lines is returned. * */ - + unsigned long probe_irq_on(void) { unsigned int i; @@ -480,22 +576,22 @@ unsigned long probe_irq_on(void) unsigned long delay; down(&probe_sem); - /* + /* * something may have generated an irq long ago and we want to - * flush such a longstanding irq before considering it as spurious. + * flush such a longstanding irq before considering it as spurious. */ for (i = NR_IRQS-1; i > 0; i--) { desc = irq_desc + i; spin_lock_irq(&desc->lock); - if (!irq_desc[i].action) + if (!irq_desc[i].action) irq_desc[i].handler->startup(i); spin_unlock_irq(&desc->lock); } /* Wait for longstanding interrupts to trigger. */ for (delay = jiffies + HZ/50; time_after(delay, jiffies); ) - /* about 20ms delay */ synchronize_irq(); + /* about 20ms delay */ barrier(); /* * enable any unassigned irqs @@ -518,7 +614,7 @@ unsigned long probe_irq_on(void) * Wait for spurious interrupts to trigger */ for (delay = jiffies + HZ/10; time_after(delay, jiffies); ) - /* about 100ms delay */ synchronize_irq(); + /* about 100ms delay */ barrier(); /* * Now filter out any obviously spurious interrupts @@ -550,7 +646,7 @@ unsigned long probe_irq_on(void) * Return a mask of triggered interrupts (this * can handle only legacy ISA interrupts). */ - + /** * probe_irq_mask - scan a bitmap of interrupt lines * @val: mask of interrupts to consider @@ -612,7 +708,7 @@ unsigned int probe_irq_mask(unsigned long val) * nothing prevents two IRQ probe callers from overlapping. The * results of this are non-optimal. */ - + int probe_irq_off(unsigned long val) { int i, irq_found, nr_irqs; @@ -693,12 +789,12 @@ int setup_irq(unsigned int irq, struct irqaction * new) if (!shared) { desc->depth = 0; - desc->status &= ~(IRQ_DISABLED | IRQ_AUTODETECT | IRQ_WAITING); + desc->status &= ~(IRQ_DISABLED | IRQ_AUTODETECT | IRQ_WAITING | IRQ_INPROGRESS); desc->handler->startup(irq); } spin_unlock_irqrestore(&desc->lock,flags); - /* register_irq_proc(irq); */ + register_irq_proc(irq); return 0; } @@ -713,3 +809,176 @@ void __init init_generic_irq(void) irq_desc[i].handler = &no_irq_type; } } + +EXPORT_SYMBOL(disable_irq_nosync); +EXPORT_SYMBOL(disable_irq); +EXPORT_SYMBOL(enable_irq); +EXPORT_SYMBOL(probe_irq_mask); + +static struct proc_dir_entry * root_irq_dir; +static struct proc_dir_entry * irq_dir [NR_IRQS]; + +#define HEX_DIGITS 8 + +static unsigned int parse_hex_value (const char *buffer, + unsigned long count, unsigned long *ret) +{ + unsigned char hexnum [HEX_DIGITS]; + unsigned long value; + int i; + + if (!count) + return -EINVAL; + if (count > HEX_DIGITS) + count = HEX_DIGITS; + if (copy_from_user(hexnum, buffer, count)) + return -EFAULT; + + /* + * Parse the first 8 characters as a hex string, any non-hex char + * is end-of-string. '00e1', 'e1', '00E1', 'E1' are all the same. + */ + value = 0; + + for (i = 0; i < count; i++) { + unsigned int c = hexnum[i]; + + switch (c) { + case '0' ... '9': c -= '0'; break; + case 'a' ... 'f': c -= 'a'-10; break; + case 'A' ... 'F': c -= 'A'-10; break; + default: + goto out; + } + value = (value << 4) | c; + } +out: + *ret = value; + return 0; +} + +#ifdef CONFIG_SMP + +static struct proc_dir_entry * smp_affinity_entry [NR_IRQS]; + +static unsigned long irq_affinity [NR_IRQS] = { [0 ... NR_IRQS-1] = ~0UL }; +static int irq_affinity_read_proc (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + if (count < HEX_DIGITS+1) + return -EINVAL; + return sprintf (page, "%08lx\n", irq_affinity[(long)data]); +} + +static int irq_affinity_write_proc (struct file *file, const char *buffer, + unsigned long count, void *data) +{ + int irq = (long) data, full_count = count, err; + unsigned long new_value; + + if (!irq_desc[irq].handler->set_affinity) + return -EIO; + + err = parse_hex_value(buffer, count, &new_value); + + /* + * Do not allow disabling IRQs completely - it's a too easy + * way to make the system unusable accidentally :-) At least + * one online CPU still has to be targeted. + */ + if (!(new_value & cpu_online_map)) + return -EINVAL; + + irq_affinity[irq] = new_value; + irq_desc[irq].handler->set_affinity(irq, new_value); + + return full_count; +} + +#endif + +static int prof_cpu_mask_read_proc (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + unsigned long *mask = (unsigned long *) data; + if (count < HEX_DIGITS+1) + return -EINVAL; + return sprintf (page, "%08lx\n", *mask); +} + +static int prof_cpu_mask_write_proc (struct file *file, const char *buffer, + unsigned long count, void *data) +{ + unsigned long *mask = (unsigned long *) data, full_count = count, err; + unsigned long new_value; + + err = parse_hex_value(buffer, count, &new_value); + if (err) + return err; + + *mask = new_value; + return full_count; +} + +#define MAX_NAMELEN 10 + +static void register_irq_proc (unsigned int irq) +{ + char name [MAX_NAMELEN]; + + if (!root_irq_dir || (irq_desc[irq].handler == &no_irq_type) || + irq_dir[irq]) + return; + + memset(name, 0, MAX_NAMELEN); + sprintf(name, "%d", irq); + + /* create /proc/irq/1234 */ + irq_dir[irq] = proc_mkdir(name, root_irq_dir); + +#ifdef CONFIG_SMP + { + struct proc_dir_entry *entry; + + /* create /proc/irq/1234/smp_affinity */ + entry = create_proc_entry("smp_affinity", 0600, irq_dir[irq]); + + if (entry) { + entry->nlink = 1; + entry->data = (void *)(long)irq; + entry->read_proc = irq_affinity_read_proc; + entry->write_proc = irq_affinity_write_proc; + } + + smp_affinity_entry[irq] = entry; + } +#endif +} + +unsigned long prof_cpu_mask = -1; + +void init_irq_proc (void) +{ + struct proc_dir_entry *entry; + int i; + + /* create /proc/irq */ + root_irq_dir = proc_mkdir("irq", 0); + + /* create /proc/irq/prof_cpu_mask */ + entry = create_proc_entry("prof_cpu_mask", 0600, root_irq_dir); + + if (!entry) + return; + + entry->nlink = 1; + entry->data = (void *)&prof_cpu_mask; + entry->read_proc = prof_cpu_mask_read_proc; + entry->write_proc = prof_cpu_mask_write_proc; + + /* + * Create entries for all existing IRQs. + */ + for (i = 0; i < NR_IRQS; i++) + register_irq_proc(i); +} diff --git a/arch/mips/kernel/irq_cpu.c b/arch/mips/kernel/irq_cpu.c new file mode 100644 index 000000000000..a5953b523a90 --- /dev/null +++ b/arch/mips/kernel/irq_cpu.c @@ -0,0 +1,117 @@ +/* + * Copyright 2001 MontaVista Software Inc. + * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net + * + * Copyright (C) 2001 Ralf Baechle + * + * This file define the irq handler for MIPS CPU interrupts. + * + * 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. + */ + +/* + * Almost all MIPS CPUs define 8 interrupt sources. They are typically + * level triggered (i.e., cannot be cleared from CPU; must be cleared from + * device). The first two are software interrupts which we don't really + * use or support. The last one is usually the CPU timer interrupt if + * counter register is present or, for CPUs with an external FPU, by + * convention it's the FPU exception interrupt. + * + * Don't even think about using this on SMP. You have been warned. + * + * This file exports one global function: + * void mips_cpu_irq_init(int irq_base); + */ +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> + +#include <asm/irq_cpu.h> +#include <asm/mipsregs.h> +#include <asm/system.h> + +static int mips_cpu_irq_base; + +static inline void unmask_mips_irq(unsigned int irq) +{ + clear_c0_cause(0x100 << (irq - mips_cpu_irq_base)); + set_c0_status(0x100 << (irq - mips_cpu_irq_base)); +} + +static inline void mask_mips_irq(unsigned int irq) +{ + clear_c0_status(0x100 << (irq - mips_cpu_irq_base)); +} + +static inline void mips_cpu_irq_enable(unsigned int irq) +{ + unsigned long flags; + + local_irq_save(flags); + unmask_mips_irq(irq); + local_irq_restore(flags); +} + +static void mips_cpu_irq_disable(unsigned int irq) +{ + unsigned long flags; + + local_irq_save(flags); + mask_mips_irq(irq); + local_irq_restore(flags); +} + +static unsigned int mips_cpu_irq_startup(unsigned int irq) +{ + mips_cpu_irq_enable(irq); + + return 0; +} + +#define mips_cpu_irq_shutdown mips_cpu_irq_disable + +/* + * While we ack the interrupt interrupts are disabled and thus we don't need + * to deal with concurrency issues. Same for mips_cpu_irq_end. + */ +static void mips_cpu_irq_ack(unsigned int irq) +{ + /* Only necessary for soft interrupts */ + clear_c0_cause(1 << (irq - mips_cpu_irq_base + 8)); + + mask_mips_irq(irq); +} + +static void mips_cpu_irq_end(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) + unmask_mips_irq(irq); +} + +static hw_irq_controller mips_cpu_irq_controller = { + "MIPS", + mips_cpu_irq_startup, + mips_cpu_irq_shutdown, + mips_cpu_irq_enable, + mips_cpu_irq_disable, + mips_cpu_irq_ack, + mips_cpu_irq_end, + NULL /* no affinity stuff for UP */ +}; + +void __init mips_cpu_irq_init(int irq_base) +{ + int i; + + for (i = irq_base; i < irq_base + 8; i++) { + irq_desc[i].status = IRQ_DISABLED; + irq_desc[i].action = NULL; + irq_desc[i].depth = 1; + irq_desc[i].handler = &mips_cpu_irq_controller; + } + + mips_cpu_irq_base = irq_base; +} diff --git a/arch/mips/kernel/mips_ksyms.c b/arch/mips/kernel/mips_ksyms.c index 3109b7485a8c..a905d8f334fe 100644 --- a/arch/mips/kernel/mips_ksyms.c +++ b/arch/mips/kernel/mips_ksyms.c @@ -12,9 +12,9 @@ #include <linux/string.h> #include <linux/mm.h> #include <linux/interrupt.h> -#include <asm/irq.h> #include <linux/in6.h> #include <linux/pci.h> +#include <linux/tty.h> #include <linux/ide.h> #include <asm/bootinfo.h> @@ -24,7 +24,7 @@ #include <asm/page.h> #include <asm/pgalloc.h> #include <asm/semaphore.h> -#include <asm/sgi/sgihpc.h> +#include <asm/softirq.h> #include <asm/uaccess.h> #ifdef CONFIG_BLK_DEV_FD #include <asm/floppy.h> @@ -41,7 +41,9 @@ extern long __strnlen_user_nocheck_asm(const char *s); extern long __strnlen_user_asm(const char *s); EXPORT_SYMBOL(mips_machtype); +#ifdef CONFIG_EISA EXPORT_SYMBOL(EISA_bus); +#endif /* * String functions @@ -58,11 +60,8 @@ EXPORT_SYMBOL_NOVERS(strncat); EXPORT_SYMBOL_NOVERS(strnlen); EXPORT_SYMBOL_NOVERS(strrchr); EXPORT_SYMBOL_NOVERS(strstr); -EXPORT_SYMBOL_NOVERS(strsep); EXPORT_SYMBOL(_clear_page); -EXPORT_SYMBOL(enable_irq); -EXPORT_SYMBOL(disable_irq); EXPORT_SYMBOL(kernel_thread); /* @@ -77,15 +76,6 @@ EXPORT_SYMBOL_NOVERS(__strlen_user_asm); EXPORT_SYMBOL_NOVERS(__strnlen_user_nocheck_asm); EXPORT_SYMBOL_NOVERS(__strnlen_user_asm); - -/* - * Functions to control caches. - */ -EXPORT_SYMBOL(_flush_page_to_ram); -EXPORT_SYMBOL(_flush_cache_all); -EXPORT_SYMBOL(_dma_cache_wback_inv); -EXPORT_SYMBOL(_dma_cache_inv); - EXPORT_SYMBOL(invalid_pte_table); /* @@ -97,32 +87,11 @@ EXPORT_SYMBOL(__down_trylock); EXPORT_SYMBOL(__up); /* - * Base address of ports for Intel style I/O. - */ -EXPORT_SYMBOL(mips_io_port_base); - -/* - * Architecture specific stuff. - */ -#ifdef CONFIG_MIPS_JAZZ -EXPORT_SYMBOL(vdma_alloc); -EXPORT_SYMBOL(vdma_free); -EXPORT_SYMBOL(vdma_log2phys); -#endif - -#ifdef CONFIG_SGI_IP22 -EXPORT_SYMBOL(hpc3c0); -#endif - -/* * Kernel hacking ... */ #include <asm/branch.h> #include <linux/sched.h> -int register_fpe(void (*handler)(struct pt_regs *regs, unsigned int fcr31)); -int unregister_fpe(void (*handler)(struct pt_regs *regs, unsigned int fcr31)); - #ifdef CONFIG_VT EXPORT_SYMBOL(screen_info); #endif @@ -132,4 +101,3 @@ EXPORT_SYMBOL(ide_ops); #endif EXPORT_SYMBOL(get_wchan); -EXPORT_SYMBOL(flush_tlb_page); diff --git a/arch/mips/kernel/module.c b/arch/mips/kernel/module.c new file mode 100644 index 000000000000..68d4bf68380d --- /dev/null +++ b/arch/mips/kernel/module.c @@ -0,0 +1,242 @@ +/* Kernel module help for MIPS. + Copyright (C) 2001 Rusty Russell. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#include <linux/moduleloader.h> +#include <linux/elf.h> +#include <linux/vmalloc.h> +#include <linux/slab.h> +#include <linux/fs.h> +#include <linux/string.h> +#include <linux/kernel.h> + +struct mips_hi16 { + struct mips_hi16 *next; + Elf32_Addr *addr; + Elf32_Addr value; +}; + +static struct mips_hi16 *mips_hi16_list; + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(fmt , ...) +#endif + +void *module_alloc(unsigned long size) +{ + if (size == 0) + return NULL; + return vmalloc(size); +} + + +/* Free memory returned from module_alloc */ +void module_free(struct module *mod, void *module_region) +{ + vfree(module_region); + /* FIXME: If module_region == mod->init_region, trim exception + table entries. */ +} + +/* We don't need anything special. */ +long module_core_size(const Elf32_Ehdr *hdr, + const Elf32_Shdr *sechdrs, + const char *secstrings, + struct module *module) +{ + return module->core_size; +} + +long module_init_size(const Elf32_Ehdr *hdr, + const Elf32_Shdr *sechdrs, + const char *secstrings, + struct module *module) +{ + return module->init_size; +} + +int module_frob_arch_sections(Elf_Ehdr *hdr, + Elf_Shdr *sechdrs, + char *secstrings, + struct module *mod) +{ + return 0; +} + +int apply_relocate(Elf32_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *me) +{ + unsigned int i; + Elf32_Rel *rel = (void *)sechdrs[relsec].sh_offset; + Elf32_Sym *sym; + uint32_t *location; + Elf32_Addr v; + + DEBUGP("Applying relocate section %u to %u\n", relsec, + sechdrs[relsec].sh_info); + for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { + /* This is where to make the change */ + location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_offset + + rel[i].r_offset; + /* This is the symbol it is referring to */ + sym = (Elf32_Sym *)sechdrs[symindex].sh_offset + + ELF32_R_SYM(rel[i].r_info); + if (!sym->st_value) { + printk(KERN_WARNING "%s: Unknown symbol %s\n", + me->name, strtab + sym->st_name); + return -ENOENT; + } + + v = sym->st_value; + + switch (ELF32_R_TYPE(rel[i].r_info)) { + case R_MIPS_NONE: + break; + + case R_MIPS_32: + *location += v; + break; + + case R_MIPS_26: + if (v % 4) + printk(KERN_ERR + "module %s: dangerous relocation\n", + me->name); + return -ENOEXEC; + if ((v & 0xf0000000) != + (((unsigned long)location + 4) & 0xf0000000)) + printk(KERN_ERR + "module %s: relocation overflow\n", + me->name); + return -ENOEXEC; + *location = (*location & ~0x03ffffff) | + ((*location + (v >> 2)) & 0x03ffffff); + break; + + case R_MIPS_HI16: { + struct mips_hi16 *n; + + /* + * We cannot relocate this one now because we don't + * know the value of the carry we need to add. Save + * the information, and let LO16 do the actual + * relocation. + */ + n = (struct mips_hi16 *) kmalloc(sizeof *n, GFP_KERNEL); + n->addr = location; + n->value = v; + n->next = mips_hi16_list; + mips_hi16_list = n; + break; + } + + case R_MIPS_LO16: { + unsigned long insnlo = *location; + Elf32_Addr val, vallo; + + /* Sign extend the addend we extract from the lo insn. */ + vallo = ((insnlo & 0xffff) ^ 0x8000) - 0x8000; + + if (mips_hi16_list != NULL) { + struct mips_hi16 *l; + + l = mips_hi16_list; + while (l != NULL) { + struct mips_hi16 *next; + unsigned long insn; + + /* + * The value for the HI16 had best be + * the same. + */ + printk(KERN_ERR "module %s: dangerous " + "relocation\n", me->name); + return -ENOEXEC; + + /* + * Do the HI16 relocation. Note that + * we actually don't need to know + * anything about the LO16 itself, + * except where to find the low 16 bits + * of the addend needed by the LO16. + */ + insn = *l->addr; + val = ((insn & 0xffff) << 16) + vallo; + val += v; + + /* + * Account for the sign extension that + * will happen in the low bits. + */ + val = ((val >> 16) + ((val & 0x8000) != + 0)) & 0xffff; + + insn = (insn & ~0xffff) | val; + *l->addr = insn; + + next = l->next; + kfree(l); + l = next; + } + + mips_hi16_list = NULL; + } + + /* + * Ok, we're done with the HI16 relocs. Now deal with + * the LO16. + */ + val = v + vallo; + insnlo = (insnlo & ~0xffff) | (val & 0xffff); + *location = insnlo; + break; + } + + default: + printk(KERN_ERR "module %s: Unknown relocation: %u\n", + me->name, ELF32_R_TYPE(rel[i].r_info)); + return -ENOEXEC; + } + } + return 0; +} + +int apply_relocate_add(Elf32_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *me) +{ + printk(KERN_ERR "module %s: ADD RELOCATION unsupported\n", + me->name); + return -ENOEXEC; +} + +int module_finalize(const Elf_Ehdr *hdr, + const Elf_Shdr *sechdrs, + struct module *me) +{ + return 0; +} + +void module_arch_cleanup(struct module *mod) +{ +} diff --git a/arch/mips/tools/offset.c b/arch/mips/kernel/offset.c index 110554358e2d..fca626775960 100644 --- a/arch/mips/tools/offset.c +++ b/arch/mips/kernel/offset.c @@ -2,14 +2,16 @@ * offset.c: Calculate pt_regs and task_struct offsets. * * Copyright (C) 1996 David S. Miller - * Copyright (C) 1997, 1998, 1999 Ralf Baechle - * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Ralf Baechle + * Copyright (C) 1999, 2000 Silicon Graphics, Inc. * * Kevin Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com * Copyright (C) 2000 MIPS Technologies, Inc. */ #include <linux/types.h> #include <linux/sched.h> +#include <linux/mm.h> +#include <linux/interrupt.h> #include <asm/ptrace.h> #include <asm/processor.h> @@ -19,16 +21,12 @@ #define offset(string, ptr, member) \ __asm__("\n@@@" string "%0" : : "i" (_offset(ptr, member))) +#define constant(string, member) \ + __asm__("\n@@@" string "%x0" : : "i" (member)) #define size(string, size) \ __asm__("\n@@@" string "%0" : : "i" (sizeof(size))) #define linefeed text("") -text("/* DO NOT TOUCH, AUTOGENERATED BY OFFSET.C */"); -linefeed; -text("#ifndef _MIPS_OFFSET_H"); -text("#define _MIPS_OFFSET_H"); -linefeed; - void output_ptreg_defines(void) { text("/* MIPS pt_regs offsets. */"); @@ -78,15 +76,24 @@ void output_task_defines(void) { text("/* MIPS task_struct offsets. */"); offset("#define TASK_STATE ", struct task_struct, state); + offset("#define TASK_THREAD_INFO ", struct task_struct, thread_info); offset("#define TASK_FLAGS ", struct task_struct, flags); -#error offset("#define TASK_SIGPENDING ", struct task_struct, work.sigpending); -#error offset("#define TASK_NEED_RESCHED ", struct task_struct, work.need_resched); -#error offset("#define TASK_PTRACE ", struct task_struct, ptrace); - offset("#define TASK_COUNTER ", struct task_struct, counter); - offset("#define TASK_NICE ", struct task_struct, nice); offset("#define TASK_MM ", struct task_struct, mm); offset("#define TASK_PID ", struct task_struct, pid); - size("#define TASK_STRUCT_SIZE ", struct task_struct); + size( "#define TASK_STRUCT_SIZE ", struct task_struct); + linefeed; +} + +void output_thread_info_defines(void) +{ + text("/* MIPS thread_info offsets. */"); + offset("#define TI_TASK ", struct thread_info, task); + offset("#define TI_EXEC_DOMAIN ", struct thread_info, exec_domain); + offset("#define TI_FLAGS ", struct thread_info, flags); + offset("#define TI_CPU ", struct thread_info, cpu); + offset("#define TI_PRE_COUNT ", struct thread_info, preempt_count); + offset("#define TI_ADDR_LIMIT ", struct thread_info, addr_limit); + offset("#define TI_RESTART_BLOCK ", struct thread_info, restart_block); linefeed; } @@ -115,16 +122,10 @@ void output_thread_defines(void) thread.error_code); offset("#define THREAD_TRAPNO ", struct task_struct, thread.trap_no); offset("#define THREAD_MFLAGS ", struct task_struct, thread.mflags); - offset("#define THREAD_CURDS ", struct task_struct, \ - thread.current_ds); offset("#define THREAD_TRAMP ", struct task_struct, \ thread.irix_trampoline); offset("#define THREAD_OLDCTX ", struct task_struct, \ thread.irix_oldctx); - offset("#define THREAD_DSEEPC ", struct task_struct, \ - thread.dsemul_epc); - offset("#define THREAD_DSEAERPC ", struct task_struct, \ - thread.dsemul_aerpc); linefeed; } @@ -135,6 +136,10 @@ void output_mm_defines(void) offset("#define MM_PGD ", struct mm_struct, pgd); offset("#define MM_CONTEXT ", struct mm_struct, context); linefeed; + constant("#define _PAGE_SIZE ", PAGE_SIZE); + constant("#define _PGD_ORDER ", PGD_ORDER); + constant("#define _PGDIR_SHIFT ", PGDIR_SHIFT); + linefeed; } void output_sc_defines(void) @@ -146,7 +151,6 @@ void output_sc_defines(void) offset("#define SC_MDLO ", struct sigcontext, sc_mdlo); offset("#define SC_PC ", struct sigcontext, sc_pc); offset("#define SC_STATUS ", struct sigcontext, sc_status); - offset("#define SC_OWNEDFP ", struct sigcontext, sc_ownedfp); offset("#define SC_FPC_CSR ", struct sigcontext, sc_fpc_csr); offset("#define SC_FPC_EIR ", struct sigcontext, sc_fpc_eir); offset("#define SC_CAUSE ", struct sigcontext, sc_cause); @@ -154,4 +158,50 @@ void output_sc_defines(void) linefeed; } -text("#endif /* !(_MIPS_OFFSET_H) */"); +void output_signal_defined(void) +{ + text("/* Linux signal numbers. */"); + constant("#define _SIGHUP ", SIGHUP); + constant("#define _SIGINT ", SIGINT); + constant("#define _SIGQUIT ", SIGQUIT); + constant("#define _SIGILL ", SIGILL); + constant("#define _SIGTRAP ", SIGTRAP); + constant("#define _SIGIOT ", SIGIOT); + constant("#define _SIGABRT ", SIGABRT); + constant("#define _SIGEMT ", SIGEMT); + constant("#define _SIGFPE ", SIGFPE); + constant("#define _SIGKILL ", SIGKILL); + constant("#define _SIGBUS ", SIGBUS); + constant("#define _SIGSEGV ", SIGSEGV); + constant("#define _SIGSYS ", SIGSYS); + constant("#define _SIGPIPE ", SIGPIPE); + constant("#define _SIGALRM ", SIGALRM); + constant("#define _SIGTERM ", SIGTERM); + constant("#define _SIGUSR1 ", SIGUSR1); + constant("#define _SIGUSR2 ", SIGUSR2); + constant("#define _SIGCHLD ", SIGCHLD); + constant("#define _SIGPWR ", SIGPWR); + constant("#define _SIGWINCH ", SIGWINCH); + constant("#define _SIGURG ", SIGURG); + constant("#define _SIGIO ", SIGIO); + constant("#define _SIGSTOP ", SIGSTOP); + constant("#define _SIGTSTP ", SIGTSTP); + constant("#define _SIGCONT ", SIGCONT); + constant("#define _SIGTTIN ", SIGTTIN); + constant("#define _SIGTTOU ", SIGTTOU); + constant("#define _SIGVTALRM ", SIGVTALRM); + constant("#define _SIGPROF ", SIGPROF); + constant("#define _SIGXCPU ", SIGXCPU); + constant("#define _SIGXFSZ ", SIGXFSZ); + linefeed; +} + +void output_irq_cpustat_t_defines(void) +{ + text("/* Linux irq_cpustat_t offsets. */"); + offset("#define IC_SOFTIRQ_PENDING ", irq_cpustat_t, __softirq_pending); + offset("#define IC_SYSCALL_COUNT ", irq_cpustat_t, __syscall_count); + offset("#define IC_KSOFTIRQD_TASK ", irq_cpustat_t, __ksoftirqd_task); + size("#define IC_IRQ_CPUSTAT_T ", irq_cpustat_t); + linefeed; +} diff --git a/arch/mips/kernel/old-irq.c b/arch/mips/kernel/old-irq.c deleted file mode 100644 index 9dedb01a92e5..000000000000 --- a/arch/mips/kernel/old-irq.c +++ /dev/null @@ -1,410 +0,0 @@ -/* - * 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. - * - * Code to handle x86 style IRQs plus some generic interrupt stuff. - * - * Copyright (C) 1992 Linus Torvalds - * Copyright (C) 1994 - 2001 Ralf Baechle - * - * Old rotten IRQ code. To be killed as soon as everybody had converted or - * in 2.5.0, whatever comes first. - */ -#include <linux/config.h> -#include <linux/errno.h> -#include <linux/init.h> -#include <linux/kernel_stat.h> -#include <linux/module.h> -#include <linux/signal.h> -#include <linux/sched.h> -#include <linux/types.h> -#include <linux/interrupt.h> -#include <linux/ioport.h> -#include <linux/timex.h> -#include <linux/slab.h> -#include <linux/random.h> -#include <linux/seq_file.h> - -#include <asm/bitops.h> -#include <asm/bootinfo.h> -#include <asm/io.h> -#include <asm/irq.h> -#include <asm/mipsregs.h> -#include <asm/system.h> -#include <asm/nile4.h> - -/* - * The board specific setup routine sets irq_setup to point to a board - * specific setup routine. - */ -void (*irq_setup)(void); - -/* - * Linux has a controller-independent x86 interrupt architecture. - * every controller has a 'controller-template', that is used - * by the main code to do the right thing. Each driver-visible - * interrupt source is transparently wired to the apropriate - * controller. Thus drivers need not be aware of the - * interrupt-controller. - * - * Various interrupt controllers we handle: 8259 PIC, SMP IO-APIC, - * PIIX4's internal 8259 PIC and SGI's Visual Workstation Cobalt (IO-)APIC. - * (IO-APICs assumed to be messaging to Pentium local-APICs) - * - * the code is designed to be easily extended with new/different - * interrupt controllers, without having to do assembly magic. - */ - -/* - * This contains the irq mask for both 8259A irq controllers, it's an - * int so we can deal with the third PIC in some systems like the RM300. - * (XXX This is broken for big endian.) - */ -static unsigned int cached_irq_mask = 0xffff; - -#define __byte(x,y) (((unsigned char *)&(y))[x]) -#define __word(x,y) (((unsigned short *)&(y))[x]) -#define __long(x,y) (((unsigned int *)&(y))[x]) - -#define cached_21 (__byte(0,cached_irq_mask)) -#define cached_A1 (__byte(1,cached_irq_mask)) - -unsigned long spurious_count = 0; - -/* - * (un)mask_irq, disable_irq() and enable_irq() only handle (E)ISA and - * PCI devices. Other onboard hardware needs specific routines. - */ -static inline void mask_irq(unsigned int irq) -{ - cached_irq_mask |= 1 << irq; - if (irq & 8) { - outb(cached_A1, 0xa1); - } else { - outb(cached_21, 0x21); - } -} - -static inline void unmask_irq(unsigned int irq) -{ - cached_irq_mask &= ~(1 << irq); - if (irq & 8) { - outb(cached_A1, 0xa1); - } else { - outb(cached_21, 0x21); - } -} - -void i8259_disable_irq(unsigned int irq_nr) -{ - unsigned long flags; - - save_and_cli(flags); - mask_irq(irq_nr); - restore_flags(flags); -} - -void i8259_enable_irq(unsigned int irq_nr) -{ - unsigned long flags; - save_and_cli(flags); - unmask_irq(irq_nr); - restore_flags(flags); -} - -static struct irqaction *irq_action[NR_IRQS] = { - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL -}; - -int show_interrupts(struct seq_file *p, void *v) -{ - int i; - struct irqaction * action; - unsigned long flags; - - for (i = 0 ; i < 32 ; i++) { - local_irq_save(flags); - action = irq_action[i]; - if (!action) - goto skip; - seq_printf(p, "%2d: %8d %c %s", - i, kstat_cpu(0).irqs[i], - (action->flags & SA_INTERRUPT) ? '+' : ' ', - action->name); - for (action=action->next; action; action = action->next) { - seq_printf(p, ",%s %s", - (action->flags & SA_INTERRUPT) ? " +" : "", - action->name); - } - seq_putc(p, '\n'); -skip: - local_irq_restore(flags); - } - return 0; -} - -static inline void i8259_mask_and_ack_irq(int irq) -{ - cached_irq_mask |= 1 << irq; - - if (irq & 8) { - inb(0xa1); - outb(cached_A1, 0xa1); - outb(0x62, 0x20); /* Specific EOI to cascade */ - outb(0x20, 0xa0); - } else { - inb(0x21); - outb(cached_21, 0x21); - outb(0x20, 0x20); - } -} - -asmlinkage void i8259_do_irq(int irq, struct pt_regs *regs) -{ - struct irqaction *action; - int do_random, cpu; - - cpu = smp_processor_id(); - irq_enter(cpu, irq); - - if (irq >= 16) - goto out; - - i8259_mask_and_ack_irq(irq); - - kstat_cpu(cpu).irqs[irq]++; - - action = *(irq + irq_action); - if (!action) - goto out; - - if (!(action->flags & SA_INTERRUPT)) - local_irq_enable(); - action = *(irq + irq_action); - do_random = 0; - do { - do_random |= action->flags; - action->handler(irq, action->dev_id, regs); - action = action->next; - } while (action); - if (do_random & SA_SAMPLE_RANDOM) - add_interrupt_randomness(irq); - local_irq_disable(); - unmask_irq (irq); - -out: - irq_exit(cpu, irq); -} - -/* - * do_IRQ handles IRQ's that have been installed without the - * SA_INTERRUPT flag: it uses the full signal-handling return - * and runs with other interrupts enabled. All relatively slow - * IRQ's should use this format: notably the keyboard/timer - * routines. - */ -asmlinkage void do_IRQ(int irq, struct pt_regs * regs) -{ - struct irqaction *action; - int do_random, cpu; - - cpu = smp_processor_id(); - irq_enter(cpu, irq); - kstat_cpu(cpu).irqs[irq]++; - - action = *(irq + irq_action); - if (action) { - if (!(action->flags & SA_INTERRUPT)) - local_irq_enable(); - action = *(irq + irq_action); - do_random = 0; - do { - do_random |= action->flags; - action->handler(irq, action->dev_id, regs); - action = action->next; - } while (action); - if (do_random & SA_SAMPLE_RANDOM) - add_interrupt_randomness(irq); - local_irq_disable(); - } - irq_exit(cpu, irq); - - if (softirq_pending(cpu)) - do_softirq(); - - /* unmasking and bottom half handling is done magically for us. */ -} - -int i8259_setup_irq(int irq, struct irqaction * new) -{ - int shared = 0; - struct irqaction *old, **p; - unsigned long flags; - - p = irq_action + irq; - if ((old = *p) != NULL) { - /* Can't share interrupts unless both agree to */ - if (!(old->flags & new->flags & SA_SHIRQ)) - return -EBUSY; - - /* Can't share interrupts unless both are same type */ - if ((old->flags ^ new->flags) & SA_INTERRUPT) - return -EBUSY; - - /* add new interrupt at end of irq queue */ - do { - p = &old->next; - old = *p; - } while (old); - shared = 1; - } - - if (new->flags & SA_SAMPLE_RANDOM) - rand_initialize_irq(irq); - - save_and_cli(flags); - *p = new; - - if (!shared) { - if (is_i8259_irq(irq)) - unmask_irq(irq); -#if (defined(CONFIG_DDB5074) || defined(CONFIG_DDB5476)) - else - nile4_enable_irq(irq_to_nile4(irq)); -#endif - } - restore_flags(flags); - return 0; -} - -/* - * Request_interrupt and free_interrupt ``sort of'' handle interrupts of - * non i8259 devices. They will have to be replaced by architecture - * specific variants. For now we still use this as broken as it is because - * it used to work ... - */ -int request_irq(unsigned int irq, - void (*handler)(int, void *, struct pt_regs *), - unsigned long irqflags, const char * devname, void *dev_id) -{ - int retval; - struct irqaction * action; - - if (irq >= 32) - return -EINVAL; - if (!handler) - return -EINVAL; - - action = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_KERNEL); - if (!action) - return -ENOMEM; - - action->handler = handler; - action->flags = irqflags; - action->mask = 0; - action->name = devname; - action->next = NULL; - action->dev_id = dev_id; - - retval = i8259_setup_irq(irq, action); - - if (retval) - kfree(action); - return retval; -} - -void free_irq(unsigned int irq, void *dev_id) -{ - struct irqaction * action, **p; - unsigned long flags; - - if (irq > 31) { - printk("Trying to free IRQ%d\n",irq); - return; - } - for (p = irq + irq_action; (action = *p) != NULL; p = &action->next) { - if (action->dev_id != dev_id) - continue; - - /* Found it - now free it */ - save_and_cli(flags); - *p = action->next; - if (!irq[irq_action]) - mask_irq(irq); - restore_flags(flags); - kfree(action); - return; - } - printk("Trying to free free IRQ%d\n",irq); -} - -unsigned long probe_irq_on (void) -{ - unsigned int i, irqs = 0; - unsigned long delay; - - /* first, enable any unassigned (E)ISA irqs */ - for (i = 15; i > 0; i--) { - if (!irq_action[i]) { - i8259_enable_irq(i); - irqs |= (1 << i); - } - } - - /* wait for spurious interrupts to mask themselves out again */ - for (delay = jiffies + HZ/10; time_before(jiffies, delay); ) - /* about 100ms delay */; - - /* now filter out any obviously spurious interrupts */ - return irqs & ~cached_irq_mask; -} - -int probe_irq_off (unsigned long irqs) -{ - unsigned int i; - -#ifdef DEBUG - printk("probe_irq_off: irqs=0x%04x irqmask=0x%04x\n", irqs, irqmask); -#endif - irqs &= cached_irq_mask; - if (!irqs) - return 0; - i = ffz(~irqs); - if (irqs != (irqs & (1 << i))) - i = -i; - return i; -} - -void __init i8259_init(void) -{ - /* Init master interrupt controller */ - outb(0x11, 0x20); /* Start init sequence */ - outb(0x00, 0x21); /* Vector base */ - outb(0x04, 0x21); /* edge tiggered, Cascade (slave) on IRQ2 */ - outb(0x01, 0x21); /* Select 8086 mode */ - outb(0xff, 0x21); /* Mask all */ - - /* Init slave interrupt controller */ - outb(0x11, 0xa0); /* Start init sequence */ - outb(0x08, 0xa1); /* Vector base */ - outb(0x02, 0xa1); /* edge triggered, Cascade (slave) on IRQ2 */ - outb(0x01, 0xa1); /* Select 8086 mode */ - outb(0xff, 0xa1); /* Mask all */ - - outb(cached_A1, 0xa1); - outb(cached_21, 0x21); -} - -void __init init_IRQ(void) -{ - /* i8259_init(); */ - irq_setup(); -} diff --git a/arch/mips/kernel/old-time.c b/arch/mips/kernel/old-time.c deleted file mode 100644 index 4793acd60c19..000000000000 --- a/arch/mips/kernel/old-time.c +++ /dev/null @@ -1,518 +0,0 @@ -/* - * Copyright (C) 1991, 1992, 1995 Linus Torvalds - * Copyright (C) 1996 - 2000 Ralf Baechle - * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips - * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved. - * - * Don't use. Deprecated. Dead meat. - */ -#include <linux/config.h> -#include <linux/errno.h> -#include <linux/init.h> -#include <linux/sched.h> -#include <linux/kernel.h> -#include <linux/param.h> -#include <linux/string.h> -#include <linux/mm.h> -#include <linux/interrupt.h> -#include <linux/kernel_stat.h> -#include <linux/bcd.h> - -#include <asm/bootinfo.h> -#include <asm/cpu.h> -#include <asm/mipsregs.h> -#include <asm/io.h> -#include <asm/irq.h> -#include <asm/ddb5074.h> - -#include <linux/mc146818rtc.h> -#include <linux/timex.h> - -extern volatile unsigned long wall_jiffies; -unsigned long r4k_interval; -extern rwlock_t xtime_lock; - -/* - * Change this if you have some constant time drift - */ -/* This is the value for the PC-style PICs. */ -/* #define USECS_PER_JIFFY (1000020/HZ) */ - -/* This is for machines which generate the exact clock. */ -#define USECS_PER_JIFFY (1000000/HZ) - -/* Cycle counter value at the previous timer interrupt.. */ - -static unsigned int timerhi, timerlo; - -/* - * On MIPS only R4000 and better have a cycle counter. - * - * FIXME: Does playing with the RP bit in c0_status interfere with this code? - */ -static unsigned long do_fast_gettimeoffset(void) -{ - u32 count; - unsigned long res, tmp; - - /* Last jiffy when do_fast_gettimeoffset() was called. */ - static unsigned long last_jiffies; - unsigned long quotient; - - /* - * Cached "1/(clocks per usec)*2^32" value. - * It has to be recalculated once each jiffy. - */ - static unsigned long cached_quotient; - - tmp = jiffies; - - quotient = cached_quotient; - - if (tmp && last_jiffies != tmp) { - last_jiffies = tmp; - __asm__(".set\tnoreorder\n\t" - ".set\tnoat\n\t" - ".set\tmips3\n\t" - "lwu\t%0,%2\n\t" - "dsll32\t$1,%1,0\n\t" - "or\t$1,$1,%0\n\t" - "ddivu\t$0,$1,%3\n\t" - "mflo\t$1\n\t" - "dsll32\t%0,%4,0\n\t" - "nop\n\t" - "ddivu\t$0,%0,$1\n\t" - "mflo\t%0\n\t" - ".set\tmips0\n\t" - ".set\tat\n\t" - ".set\treorder" - :"=&r" (quotient) - :"r" (timerhi), - "m" (timerlo), - "r" (tmp), - "r" (USECS_PER_JIFFY) - :"$1"); - cached_quotient = quotient; - } - - /* Get last timer tick in absolute kernel time */ - count = read_32bit_cp0_register(CP0_COUNT); - - /* .. relative to previous jiffy (32 bits is enough) */ - count -= timerlo; - - __asm__("multu\t%1,%2\n\t" - "mfhi\t%0" - :"=r" (res) - :"r" (count), - "r" (quotient)); - - /* - * Due to possible jiffies inconsistencies, we need to check - * the result so that we'll get a timer that is monotonic. - */ - if (res >= USECS_PER_JIFFY) - res = USECS_PER_JIFFY-1; - - return res; -} - -/* This function must be called with interrupts disabled - * It was inspired by Steve McCanne's microtime-i386 for BSD. -- jrs - * - * However, the pc-audio speaker driver changes the divisor so that - * it gets interrupted rather more often - it loads 64 into the - * counter rather than 11932! This has an adverse impact on - * do_gettimeoffset() -- it stops working! What is also not - * good is that the interval that our timer function gets called - * is no longer 10.0002 ms, but 9.9767 ms. To get around this - * would require using a different timing source. Maybe someone - * could use the RTC - I know that this can interrupt at frequencies - * ranging from 8192Hz to 2Hz. If I had the energy, I'd somehow fix - * it so that at startup, the timer code in sched.c would select - * using either the RTC or the 8253 timer. The decision would be - * based on whether there was any other device around that needed - * to trample on the 8253. I'd set up the RTC to interrupt at 1024 Hz, - * and then do some jiggery to have a version of do_timer that - * advanced the clock by 1/1024 s. Every time that reached over 1/100 - * of a second, then do all the old code. If the time was kept correct - * then do_gettimeoffset could just return 0 - there is no low order - * divider that can be accessed. - * - * Ideally, you would be able to use the RTC for the speaker driver, - * but it appears that the speaker driver really needs interrupt more - * often than every 120 us or so. - * - * Anyway, this needs more thought.... pjsg (1993-08-28) - * - * If you are really that interested, you should be reading - * comp.protocols.time.ntp! - */ - -#define TICK_SIZE tick - -static unsigned long do_slow_gettimeoffset(void) -{ - int count; - - static int count_p = LATCH; /* for the first call after boot */ - static unsigned long jiffies_p; - - /* - * cache volatile jiffies temporarily; we have IRQs turned off. - */ - unsigned long jiffies_t; - - /* timer count may underflow right here */ - outb_p(0x00, 0x43); /* latch the count ASAP */ - - count = inb_p(0x40); /* read the latched count */ - - /* - * We do this guaranteed double memory access instead of a _p - * postfix in the previous port access. Wheee, hackady hack - */ - jiffies_t = jiffies; - - count |= inb_p(0x40) << 8; - - /* - * avoiding timer inconsistencies (they are rare, but they happen)... - * there are two kinds of problems that must be avoided here: - * 1. the timer counter underflows - * 2. hardware problem with the timer, not giving us continuous time, - * the counter does small "jumps" upwards on some Pentium systems, - * (see c't 95/10 page 335 for Neptun bug.) - */ - - if( jiffies_t == jiffies_p ) { - if( count > count_p ) { - /* the nutcase */ - - outb_p(0x0A, 0x20); - - /* assumption about timer being IRQ1 */ - if (inb(0x20) & 0x01) { - /* - * We cannot detect lost timer interrupts ... - * well, that's why we call them lost, don't we? :) - * [hmm, on the Pentium and Alpha we can ... sort of] - */ - count -= LATCH; - } else { - printk("do_slow_gettimeoffset(): hardware timer problem?\n"); - } - } - } else - jiffies_p = jiffies_t; - - count_p = count; - - count = ((LATCH-1) - count) * TICK_SIZE; - count = (count + LATCH/2) / LATCH; - - return count; -} - -static unsigned long (*do_gettimeoffset)(void) = do_slow_gettimeoffset; - -/* - * This version of gettimeofday has near microsecond resolution. - */ -void do_gettimeofday(struct timeval *tv) -{ - unsigned long flags; - - read_lock_irqsave (&xtime_lock, flags); - *tv = xtime; - tv->tv_usec += do_gettimeoffset(); - - /* - * xtime is atomically updated in timer_bh. jiffies - wall_jiffies - * is nonzero if the timer bottom half hasnt executed yet. - */ - if (jiffies - wall_jiffies) - tv->tv_usec += USECS_PER_JIFFY; - - read_unlock_irqrestore (&xtime_lock, flags); - - if (tv->tv_usec >= 1000000) { - tv->tv_usec -= 1000000; - tv->tv_sec++; - } -} - -void do_settimeofday(struct timeval *tv) -{ - write_lock_irq (&xtime_lock); - - /* This is revolting. We need to set the xtime.tv_usec - * correctly. However, the value in this location is - * is value at the last tick. - * Discover what correction gettimeofday - * would have done, and then undo it! - */ - tv->tv_usec -= do_gettimeoffset(); - - if (tv->tv_usec < 0) { - tv->tv_usec += 1000000; - tv->tv_sec--; - } - - xtime = *tv; - time_adjust = 0; /* stop active adjtime() */ - time_status |= STA_UNSYNC; - time_maxerror = NTP_PHASE_LIMIT; - time_esterror = NTP_PHASE_LIMIT; - - write_unlock_irq (&xtime_lock); -} - -/* - * In order to set the CMOS clock precisely, set_rtc_mmss has to be - * called 500 ms after the second nowtime has started, because when - * nowtime is written into the registers of the CMOS clock, it will - * jump to the next second precisely 500 ms later. Check the Motorola - * MC146818A or Dallas DS12887 data sheet for details. - * - * BUG: This routine does not handle hour overflow properly; it just - * sets the minutes. Usually you won't notice until after reboot! - */ -static int set_rtc_mmss(unsigned long nowtime) -{ - int retval = 0; - int real_seconds, real_minutes, cmos_minutes; - unsigned char save_control, save_freq_select; - - save_control = CMOS_READ(RTC_CONTROL); /* tell the clock it's being set */ - CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL); - - save_freq_select = CMOS_READ(RTC_FREQ_SELECT); /* stop and reset prescaler */ - CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); - - cmos_minutes = CMOS_READ(RTC_MINUTES); - if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) - 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) { - if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { - BIN_TO_BCD(real_seconds); - BIN_TO_BCD(real_minutes); - } - CMOS_WRITE(real_seconds,RTC_SECONDS); - CMOS_WRITE(real_minutes,RTC_MINUTES); - } else { - printk(KERN_WARNING - "set_rtc_mmss: can't update from %d to %d\n", - cmos_minutes, real_minutes); - retval = -1; - } - - /* The following flags have to be released exactly in this order, - * otherwise the DS12887 (popular MC146818A clone with integrated - * battery and quartz) will not reset the oscillator and will not - * update precisely 500 ms later. You won't find this mentioned in - * the Dallas Semiconductor data sheets, but who believes data - * sheets anyway ... -- Markus Kuhn - */ - CMOS_WRITE(save_control, RTC_CONTROL); - CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); - - return retval; -} - -/* last time the cmos clock got updated */ -static long last_rtc_update; - -/* - * timer_interrupt() needs to keep up the real-time clock, - * as well as call the "do_timer()" routine every clocktick - */ -static void inline -timer_interrupt(int irq, void *dev_id, struct pt_regs * regs) -{ -#ifdef CONFIG_DDB5074 - static unsigned cnt, period, dist; - - if (cnt == 0 || cnt == dist) - ddb5074_led_d2(1); - else if (cnt == 7 || cnt == dist+7) - ddb5074_led_d2(0); - - if (++cnt > period) { - cnt = 0; - /* The hyperbolic function below modifies the heartbeat period - * length in dependency of the current (5min) load. It goes - * through the points f(0)=126, f(1)=86, f(5)=51, - * f(inf)->30. */ - period = ((672<<FSHIFT)/(5*avenrun[0]+(7<<FSHIFT))) + 30; - dist = period / 4; - } -#endif - if(!user_mode(regs)) { - if (prof_buffer && current->pid) { - extern int _stext; - unsigned long pc = regs->cp0_epc; - - pc -= (unsigned long) &_stext; - pc >>= prof_shift; - /* - * Dont ignore out-of-bounds pc values silently, - * put them into the last histogram slot, so if - * present, they will show up as a sharp peak. - */ - if (pc > prof_len-1) - pc = prof_len-1; - atomic_inc((atomic_t *)&prof_buffer[pc]); - } - } - do_timer(regs); - - /* - * If we have an externally synchronized Linux clock, then update - * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be - * called as close as possible to 500 ms before the new second starts. - */ - read_lock (&xtime_lock); - if ((time_status & STA_UNSYNC) == 0 && - xtime.tv_sec > last_rtc_update + 660 && - xtime.tv_usec >= 500000 - ((unsigned) tick) / 2 && - xtime.tv_usec <= 500000 + ((unsigned) tick) / 2) { - if (set_rtc_mmss(xtime.tv_sec) == 0) - last_rtc_update = xtime.tv_sec; - else - last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */ - } - /* As we return to user mode fire off the other CPU schedulers.. this is - basically because we don't yet share IRQ's around. This message is - rigged to be safe on the 386 - basically it's a hack, so don't look - closely for now.. */ - /*smp_message_pass(MSG_ALL_BUT_SELF, MSG_RESCHEDULE, 0L, 0); */ - read_unlock (&xtime_lock); -} - -static inline void -r4k_timer_interrupt(int irq, void *dev_id, struct pt_regs * regs) -{ - unsigned int count; - - /* - * The cycle counter is only 32 bit which is good for about - * a minute at current count rates of upto 150MHz or so. - */ - count = read_32bit_cp0_register(CP0_COUNT); - timerhi += (count < timerlo); /* Wrap around */ - timerlo = count; - -#ifdef CONFIG_SGI_IP22 - /* Since we don't get anything but r4k timer interrupts, we need to - * set this up so that we'll get one next time. Fortunately since we - * have timerhi/timerlo, we don't care so much if we miss one. So - * we need only ask for the next in r4k_interval counts. On other - * archs we have a real timer, so we don't want this. - */ - write_32bit_cp0_register (CP0_COMPARE, - (unsigned long) (count + r4k_interval)); - kstat_cpu(0).irqs[irq]++; -#endif - - timer_interrupt(irq, dev_id, regs); - - if (!jiffies) - { - /* - * If jiffies has overflowed in this timer_interrupt we must - * update the timer[hi]/[lo] to make do_fast_gettimeoffset() - * quotient calc still valid. -arca - */ - timerhi = timerlo = 0; - } -} - -void indy_r4k_timer_interrupt (struct pt_regs *regs) -{ - static const int INDY_R4K_TIMER_IRQ = 7; - int cpu = smp_processor_id(); - - r4k_timer_interrupt (INDY_R4K_TIMER_IRQ, NULL, regs); - - if (softirq_pending(cpu)) - do_softirq(); -} - -struct irqaction irq0 = { timer_interrupt, SA_INTERRUPT, 0, - "timer", NULL, NULL}; - - -void (*board_time_init)(struct irqaction *irq); - -void __init time_init(void) -{ - unsigned int epoch = 0, year, mon, day, hour, min, sec; - int i; - - /* The Linux interpretation of the CMOS clock register contents: - * When the Update-In-Progress (UIP) flag goes from 1 to 0, the - * RTC registers show the second which has precisely just started. - * Let's hope other operating systems interpret the RTC the same way. - */ - /* read RTC exactly on falling edge of update flag */ - for (i = 0 ; i < 1000000 ; i++) /* may take up to 1 second... */ - if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) - break; - for (i = 0 ; i < 1000000 ; i++) /* must try at least 2.228 ms */ - if (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)) - break; - do { /* Isn't this overkill ? UIP above should guarantee consistency */ - sec = CMOS_READ(RTC_SECONDS); - min = CMOS_READ(RTC_MINUTES); - hour = CMOS_READ(RTC_HOURS); - day = CMOS_READ(RTC_DAY_OF_MONTH); - mon = CMOS_READ(RTC_MONTH); - year = CMOS_READ(RTC_YEAR); - } while (sec != CMOS_READ(RTC_SECONDS)); - if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) - { - BCD_TO_BIN(sec); - BCD_TO_BIN(min); - BCD_TO_BIN(hour); - BCD_TO_BIN(day); - BCD_TO_BIN(mon); - BCD_TO_BIN(year); - } - - /* Attempt to guess the epoch. This is the same heuristic as in rtc.c so - no stupid things will happen to timekeeping. Who knows, maybe Ultrix - also uses 1952 as epoch ... */ - if (year > 10 && year < 44) { - epoch = 1980; - } else if (year < 96) { - epoch = 1952; - } - year += epoch; - - write_lock_irq (&xtime_lock); - xtime.tv_sec = mktime(year, mon, day, hour, min, sec); - xtime.tv_usec = 0; - write_unlock_irq (&xtime_lock); - - if (mips_cpu.options & MIPS_CPU_COUNTER) { - write_32bit_cp0_register(CP0_COUNT, 0); - do_gettimeoffset = do_fast_gettimeoffset; - irq0.handler = r4k_timer_interrupt; - } - - board_time_init(&irq0); -} diff --git a/arch/mips/kernel/pci-dma.c b/arch/mips/kernel/pci-dma.c index 542d529b84ac..a51b59048876 100644 --- a/arch/mips/kernel/pci-dma.c +++ b/arch/mips/kernel/pci-dma.c @@ -10,6 +10,7 @@ #include <linux/config.h> #include <linux/types.h> #include <linux/mm.h> +#include <linux/module.h> #include <linux/string.h> #include <linux/pci.h> @@ -20,18 +21,23 @@ void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, { void *ret; int gfp = GFP_ATOMIC; + struct pci_bus *bus = NULL; +#ifdef CONFIG_ISA if (hwdev == NULL || hwdev->dma_mask != 0xffffffff) gfp |= GFP_DMA; +#endif ret = (void *) __get_free_pages(gfp, get_order(size)); if (ret != NULL) { memset(ret, 0, size); -#ifndef CONFIG_COHERENT_IO + if (hwdev) + bus = hwdev->bus; + *dma_handle = bus_to_baddr(bus, __pa(ret)); +#ifdef CONFIG_NONCOHERENT_IO dma_cache_wback_inv((unsigned long) ret, size); - ret = KSEG1ADDR(ret); + ret = UNCAC_ADDR(ret); #endif - *dma_handle = virt_to_bus(ret); } return ret; @@ -42,8 +48,11 @@ void pci_free_consistent(struct pci_dev *hwdev, size_t size, { unsigned long addr = (unsigned long) vaddr; -#ifndef CONFIG_COHERENT_IO - addr = KSEG0ADDR(addr); +#ifdef CONFIG_NONCOHERENT_IO + addr = CAC_ADDR(addr); #endif free_pages(addr, get_order(size)); } + +EXPORT_SYMBOL(pci_alloc_consistent); +EXPORT_SYMBOL(pci_free_consistent); diff --git a/arch/mips/kernel/pci.c b/arch/mips/kernel/pci.c deleted file mode 100644 index b7b72fd1a267..000000000000 --- a/arch/mips/kernel/pci.c +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright 2001 MontaVista Software Inc. - * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net - * - * Modified to be mips generic, ppopov@mvista.com - * arch/mips/kernel/pci.c - * Common MIPS PCI routines. - * - * 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 file contains common PCI routines meant to be shared for - * all MIPS machines. - * - * Strategies: - * - * . We rely on pci_auto.c file to assign PCI resources (MEM and IO) - * TODO: this should be optional for some machines where they do have - * a real "pcibios" that does resource assignment. - * - * . We then use pci_scan_bus() to "discover" all the resources for - * later use by Linux. - * - * . We finally reply on a board supplied function, pcibios_fixup_irq(), to - * to assign the interrupts. We may use setup-irq.c under drivers/pci - * later. - * - * . Specifically, we will *NOT* use pci_assign_unassigned_resources(), - * because we assume all PCI devices should have the resources correctly - * assigned and recorded. - * - * Limitations: - * - * . We "collapse" all IO and MEM spaces in sub-buses under a top-level bus - * into a contiguous range. - * - * . In the case of Memory space, the rnage is 1:1 mapping with CPU physical - * address space. - * - * . In the case of IO space, it starts from 0, and the beginning address - * is mapped to KSEG0ADDR(mips_io_port) in the CPU physical address. - * - * . These are the current MIPS limitations (by ioremap, etc). In the - * future, we may remove them. - * - * Credits: - * Most of the code are derived from the pci routines from PPC and Alpha, - * which were mostly writtne by - * Cort Dougan, cort@fsmlabs.com - * Matt Porter, mporter@mvista.com - * Dave Rusling david.rusling@reo.mts.dec.com - * David Mosberger davidm@cs.arizona.edu - */ -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/types.h> -#include <linux/pci.h> - -#include <asm/pci_channel.h> - -extern void pcibios_fixup(void); -extern void pcibios_fixup_irqs(void); - -struct pci_fixup pcibios_fixups[] = { - { PCI_FIXUP_HEADER, PCI_ANY_ID, PCI_ANY_ID, pcibios_fixup_resources }, - { 0 } -}; - -extern int pciauto_assign_resources(int busno, struct pci_channel * hose); - -void __init pcibios_init(void) -{ - struct pci_channel *p; - struct pci_bus *bus; - int busno; - -#ifdef CONFIG_PCI_AUTO - /* assign resources */ - busno=0; - for (p= mips_pci_channels; p->pci_ops != NULL; p++) { - busno = pciauto_assign_resources(busno, p) + 1; - } -#endif - - /* scan the buses */ - busno = 0; - for (p= mips_pci_channels; p->pci_ops != NULL; p++) { - bus = pci_scan_bus(busno, p->pci_ops, p); - busno = bus->subordinate+1; - } - - /* machine dependent fixups */ - pcibios_fixup(); - /* fixup irqs (board specific routines) */ - pcibios_fixup_irqs(); -} - -int pcibios_enable_device(struct pci_dev *dev) -{ - /* pciauto_assign_resources() will enable all devices found */ - return 0; -} - -unsigned long __init pci_bridge_check_io(struct pci_dev *bridge) -{ - u16 io; - - pci_read_config_word(bridge, PCI_IO_BASE, &io); - if (!io) { - pci_write_config_word(bridge, PCI_IO_BASE, 0xf0f0); - pci_read_config_word(bridge, PCI_IO_BASE, &io); - pci_write_config_word(bridge, PCI_IO_BASE, 0x0); - } - if (io) - return IORESOURCE_IO; - printk(KERN_WARNING "PCI: bridge %s does not support I/O forwarding!\n", - bridge->name); - return 0; -} - -void __init pcibios_fixup_bus(struct pci_bus *bus) -{ - /* Propogate hose info into the subordinate devices. */ - - struct pci_channel *hose = bus->sysdata; - struct pci_dev *dev = bus->self; - - if (!dev) { - /* Root bus */ - bus->resource[0] = hose->io_resource; - bus->resource[1] = hose->mem_resource; - } else { - /* This is a bridge. Do not care how it's initialized, - just link its resources to the bus ones */ - int i; - - for(i=0; i<3; i++) { - bus->resource[i] = - &dev->resource[PCI_BRIDGE_RESOURCES+i]; - bus->resource[i]->name = bus->name; - } - bus->resource[0]->flags |= pci_bridge_check_io(dev); - bus->resource[1]->flags |= IORESOURCE_MEM; - /* For now, propagate hose limits to the bus; - we'll adjust them later. */ - bus->resource[0]->end = hose->io_resource->end; - bus->resource[1]->end = hose->mem_resource->end; - /* Turn off downstream PF memory address range by default */ - bus->resource[2]->start = 1024*1024; - bus->resource[2]->end = bus->resource[2]->start - 1; - } -} - -char *pcibios_setup(char *str) -{ - return str; -} - -void -pcibios_align_resource(void *data, struct resource *res, - unsigned long size, unsigned long align) -{ - /* this should not be called */ -} diff --git a/arch/mips/kernel/pci_auto.c b/arch/mips/kernel/pci_auto.c deleted file mode 100644 index af9c587a7ad3..000000000000 --- a/arch/mips/kernel/pci_auto.c +++ /dev/null @@ -1,323 +0,0 @@ -/* - * PCI autoconfiguration library - * - * Author: Matt Porter <mporter@mvista.com> - * - * Copyright 2000, 2001 MontaVista Software Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - -/* - * Modified for MIPS by Jun Sun, jsun@mvista.com - * - * . Simplify the interface between pci_auto and the rest: a single function. - * . Assign resources from low address to upper address. - * . change most int to u32. - * - * Further modified to include it as mips generic code, ppopov@mvista.com. - */ - -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/types.h> -#include <linux/pci.h> - -#include <asm/pci_channel.h> - -#define DEBUG -#ifdef DEBUG -#define DBG(x...) printk(x) -#else -#define DBG(x...) -#endif - -/* These are used for config access before all the PCI probing has been done. */ -int early_read_config_byte(struct pci_channel *hose, int bus, int dev_fn, int where, u8 *val); -int early_read_config_word(struct pci_channel *hose, int bus, int dev_fn, int where, u16 *val); -int early_read_config_dword(struct pci_channel *hose, int bus, int dev_fn, int where, u32 *val); -int early_write_config_byte(struct pci_channel *hose, int bus, int dev_fn, int where, u8 val); -int early_write_config_word(struct pci_channel *hose, int bus, int dev_fn, int where, u16 val); -int early_write_config_dword(struct pci_channel *hose, int bus, int dev_fn, int where, u32 val); - -static u32 pciauto_lower_iospc; -static u32 pciauto_upper_iospc; - -static u32 pciauto_lower_memspc; -static u32 pciauto_upper_memspc; - -void __init -pciauto_setup_bars(struct pci_channel *hose, - int current_bus, - int pci_devfn) -{ - u32 bar_response, bar_size, bar_value; - u32 bar, addr_mask, bar_nr = 0; - u32 * upper_limit; - u32 * lower_limit; - int found_mem64 = 0; - - DBG("PCI Autoconfig: Found Bus %d, Device %d, Function %d\n", - current_bus, PCI_SLOT(pci_devfn), PCI_FUNC(pci_devfn) ); - - for (bar = PCI_BASE_ADDRESS_0; bar <= PCI_BASE_ADDRESS_5; bar+=4) { - /* Tickle the BAR and get the response */ - early_write_config_dword(hose, - current_bus, - pci_devfn, - bar, - 0xffffffff); - early_read_config_dword(hose, - current_bus, - pci_devfn, - bar, - &bar_response); - - /* If BAR is not implemented go to the next BAR */ - if (!bar_response) - continue; - - /* Check the BAR type and set our address mask */ - if (bar_response & PCI_BASE_ADDRESS_SPACE) { - addr_mask = PCI_BASE_ADDRESS_IO_MASK; - upper_limit = &pciauto_upper_iospc; - lower_limit = &pciauto_lower_iospc; - DBG("PCI Autoconfig: BAR %d, I/O, ", bar_nr); - } else { - if ((bar_response & PCI_BASE_ADDRESS_MEM_TYPE_MASK) == - PCI_BASE_ADDRESS_MEM_TYPE_64) - found_mem64 = 1; - - addr_mask = PCI_BASE_ADDRESS_MEM_MASK; - upper_limit = &pciauto_upper_memspc; - lower_limit = &pciauto_lower_memspc; - DBG("PCI Autoconfig: BAR %d, Mem, ", bar_nr); - } - - /* Calculate requested size */ - bar_size = ~(bar_response & addr_mask) + 1; - - /* Allocate a base address */ - bar_value = ((*lower_limit - 1) & ~(bar_size - 1)) + bar_size; - - /* Write it out and update our limit */ - early_write_config_dword(hose, current_bus, pci_devfn, - bar, bar_value); - - *lower_limit = bar_value + bar_size; - - /* - * If we are a 64-bit decoder then increment to the - * upper 32 bits of the bar and force it to locate - * in the lower 4GB of memory. - */ - if (found_mem64) { - bar += 4; - early_write_config_dword(hose, - current_bus, - pci_devfn, - bar, - 0x00000000); - } - - bar_nr++; - - DBG("size=0x%x, address=0x%x\n", - bar_size, bar_value); - } - -} - -void __init -pciauto_prescan_setup_bridge(struct pci_channel *hose, - int current_bus, - int pci_devfn, - int sub_bus) -{ - int cmdstat; - - /* Configure bus number registers */ - early_write_config_byte(hose, current_bus, pci_devfn, - PCI_PRIMARY_BUS, current_bus); - early_write_config_byte(hose, current_bus, pci_devfn, - PCI_SECONDARY_BUS, sub_bus + 1); - early_write_config_byte(hose, current_bus, pci_devfn, - PCI_SUBORDINATE_BUS, 0xff); - - /* Round memory allocator to 1MB boundary */ - pciauto_upper_memspc &= ~(0x100000 - 1); - - /* Round I/O allocator to 4KB boundary */ - pciauto_upper_iospc &= ~(0x1000 - 1); - - /* Set up memory and I/O filter limits, assume 32-bit I/O space */ - early_write_config_word(hose, current_bus, pci_devfn, PCI_MEMORY_LIMIT, - ((pciauto_upper_memspc - 1) & 0xfff00000) >> 16); - early_write_config_byte(hose, current_bus, pci_devfn, PCI_IO_LIMIT, - ((pciauto_upper_iospc - 1) & 0x0000f000) >> 8); - early_write_config_word(hose, current_bus, pci_devfn, - PCI_IO_LIMIT_UPPER16, - ((pciauto_upper_iospc - 1) & 0xffff0000) >> 16); - - /* We don't support prefetchable memory for now, so disable */ - early_write_config_word(hose, current_bus, pci_devfn, - PCI_PREF_MEMORY_BASE, 0x1000); - early_write_config_word(hose, current_bus, pci_devfn, - PCI_PREF_MEMORY_LIMIT, 0x1000); - - /* Enable memory and I/O accesses, enable bus master */ - early_read_config_dword(hose, current_bus, pci_devfn, PCI_COMMAND, - &cmdstat); - early_write_config_dword(hose, current_bus, pci_devfn, PCI_COMMAND, - cmdstat | PCI_COMMAND_IO | PCI_COMMAND_MEMORY | - PCI_COMMAND_MASTER); -} - -void __init -pciauto_postscan_setup_bridge(struct pci_channel *hose, - int current_bus, - int pci_devfn, - int sub_bus) -{ - /* Configure bus number registers */ - early_write_config_byte(hose, current_bus, pci_devfn, - PCI_SUBORDINATE_BUS, sub_bus); - - /* Round memory allocator to 1MB boundary */ - pciauto_upper_memspc &= ~(0x100000 - 1); - early_write_config_word(hose, current_bus, pci_devfn, PCI_MEMORY_BASE, - pciauto_upper_memspc >> 16); - - /* Round I/O allocator to 4KB boundary */ - pciauto_upper_iospc &= ~(0x1000 - 1); - early_write_config_byte(hose, current_bus, pci_devfn, PCI_IO_BASE, - (pciauto_upper_iospc & 0x0000f000) >> 8); - early_write_config_word(hose, current_bus, pci_devfn, - PCI_IO_BASE_UPPER16, pciauto_upper_iospc >> 16); -} - -#define PCIAUTO_IDE_MODE_MASK 0x05 - -int __init -pciauto_bus_scan(struct pci_channel *hose, int current_bus) -{ - int sub_bus; - u32 pci_devfn, pci_class, cmdstat, found_multi=0; - unsigned short vid; - unsigned char header_type; - int devfn_start = 0; - int devfn_stop = 0xff; - - sub_bus = current_bus; - - if (hose->first_devfn) - devfn_start = hose->first_devfn; - if (hose->last_devfn) - devfn_stop = hose->last_devfn; - - for (pci_devfn=devfn_start; pci_devfn<devfn_stop; pci_devfn++) { - - if (PCI_FUNC(pci_devfn) && !found_multi) - continue; - - early_read_config_byte(hose, current_bus, pci_devfn, - PCI_HEADER_TYPE, &header_type); - - if (!PCI_FUNC(pci_devfn)) - found_multi = header_type & 0x80; - - early_read_config_word(hose, current_bus, pci_devfn, - PCI_VENDOR_ID, &vid); - - if (vid == 0xffff) continue; - - early_read_config_dword(hose, current_bus, pci_devfn, - PCI_CLASS_REVISION, &pci_class); - if ((pci_class >> 16) == PCI_CLASS_BRIDGE_PCI) { - DBG("PCI Autoconfig: Found P2P bridge, device %d\n", PCI_SLOT(pci_devfn)); - pciauto_prescan_setup_bridge(hose, current_bus, - pci_devfn, sub_bus); - sub_bus = pciauto_bus_scan(hose, sub_bus+1); - pciauto_postscan_setup_bridge(hose, current_bus, - pci_devfn, sub_bus); - - } else if ((pci_class >> 16) == PCI_CLASS_STORAGE_IDE) { - - unsigned char prg_iface; - - early_read_config_byte(hose, current_bus, pci_devfn, - PCI_CLASS_PROG, &prg_iface); - if (!(prg_iface & PCIAUTO_IDE_MODE_MASK)) { - DBG("PCI Autoconfig: Skipping legacy mode IDE controller\n"); - continue; - } - } - - /* - * Found a peripheral, enable some standard - * settings - */ - early_read_config_dword(hose, current_bus, pci_devfn, - PCI_COMMAND, &cmdstat); - early_write_config_dword(hose, current_bus, pci_devfn, - PCI_COMMAND, cmdstat | PCI_COMMAND_IO | - PCI_COMMAND_MEMORY | - PCI_COMMAND_MASTER); - early_write_config_byte(hose, current_bus, pci_devfn, - PCI_LATENCY_TIMER, 0x80); - - /* Allocate PCI I/O and/or memory space */ - pciauto_setup_bars(hose, current_bus, pci_devfn); - } - return sub_bus; -} - -int __init -pciauto_assign_resources(int busno, struct pci_channel *hose) -{ - /* setup resource limits */ - pciauto_lower_iospc = hose->io_resource->start; - pciauto_upper_iospc = hose->io_resource->end + 1; - pciauto_lower_memspc = hose->mem_resource->start; - pciauto_upper_memspc = hose->mem_resource->end + 1; - - return pciauto_bus_scan(hose, busno); -} - - -/* - * These functions are used early on before PCI scanning is done - * and all of the pci_dev and pci_bus structures have been created. - */ -static struct pci_dev *fake_pci_dev(struct pci_channel *hose, int busnr, - int devfn) -{ - static struct pci_dev dev; - static struct pci_bus bus; - - dev.bus = &bus; - dev.sysdata = hose; - dev.devfn = devfn; - bus.number = busnr; - bus.ops = hose->pci_ops; - - return &dev; -} - -#define EARLY_PCI_OP(rw, size, type) \ -int early_##rw##_config_##size(struct pci_channel *hose, int bus, \ - int devfn, int offset, type value) \ -{ \ - return pci_##rw##_config_##size(fake_pci_dev(hose, bus, devfn), \ - offset, value); \ -} - -EARLY_PCI_OP(read, byte, u8 *) -EARLY_PCI_OP(read, word, u16 *) -EARLY_PCI_OP(read, dword, u32 *) -EARLY_PCI_OP(write, byte, u8) -EARLY_PCI_OP(write, word, u16) -EARLY_PCI_OP(write, dword, u32) diff --git a/arch/mips/kernel/proc.c b/arch/mips/kernel/proc.c index bb7f399d9cf8..6d1d5d8baa03 100644 --- a/arch/mips/kernel/proc.c +++ b/arch/mips/kernel/proc.c @@ -8,102 +8,136 @@ #include <linux/delay.h> #include <linux/kernel.h> #include <linux/sched.h> +#include <linux/seq_file.h> #include <asm/bootinfo.h> #include <asm/cpu.h> #include <asm/mipsregs.h> #include <asm/processor.h> #include <asm/watch.h> -extern unsigned long unaligned_instructions; unsigned int vced_count, vcei_count; -#ifndef CONFIG_CPU_HAS_LLSC -unsigned long ll_ops, sc_ops; -#endif -/* - * BUFFER is PAGE_SIZE bytes long. - * - * Currently /proc/cpuinfo is being abused to print data about the - * number of date/instruction cacheflushes. - */ -int get_cpuinfo(char *buffer) +static const char *cpu_name[] = { + [CPU_UNKNOWN] "unknown", + [CPU_R2000] "R2000", + [CPU_R3000] "R3000", + [CPU_R3000A] "R3000A", + [CPU_R3041] "R3041", + [CPU_R3051] "R3051", + [CPU_R3052] "R3052", + [CPU_R3081] "R3081", + [CPU_R3081E] "R3081E", + [CPU_R4000PC] "R4000PC", + [CPU_R4000SC] "R4000SC", + [CPU_R4000MC] "R4000MC", + [CPU_R4200] "R4200", + [CPU_R4400PC] "R4400PC", + [CPU_R4400SC] "R4400SC", + [CPU_R4400MC] "R4400MC", + [CPU_R4600] "R4600", + [CPU_R6000] "R6000", + [CPU_R6000A] "R6000A", + [CPU_R8000] "R8000", + [CPU_R10000] "R10000", + [CPU_R4300] "R4300", + [CPU_R4650] "R4650", + [CPU_R4700] "R4700", + [CPU_R5000] "R5000", + [CPU_R5000A] "R5000A", + [CPU_R4640] "R4640", + [CPU_NEVADA] "Nevada", + [CPU_RM7000] "RM7000", + [CPU_R5432] "R5432", + [CPU_4KC] "MIPS 4Kc", + [CPU_5KC] "MIPS 5Kc", + [CPU_R4310] "R4310", + [CPU_SB1] "SiByte SB1", + [CPU_TX3912] "TX3912", + [CPU_TX3922] "TX3922", + [CPU_TX3927] "TX3927", + [CPU_AU1000] "Au1000", + [CPU_AU1500] "Au1500", + [CPU_4KEC] "MIPS 4KEc", + [CPU_4KSC] "MIPS 4KSc", + [CPU_VR41XX] "NEC Vr41xx", + [CPU_R5500] "R5500", + [CPU_TX49XX] "TX49xx", + [CPU_20KC] "MIPS 20Kc", + [CPU_VR4111] "NEC VR4111", + [CPU_VR4121] "NEC VR4121", + [CPU_VR4122] "NEC VR4122", + [CPU_VR4131] "NEC VR4131", + [CPU_VR4181] "NEC VR4181", + [CPU_VR4181A] "NEC VR4181A", + [CPU_SR71000] "Sandcraft SR71000" +}; + + +static int show_cpuinfo(struct seq_file *m, void *v) { + unsigned int version = current_cpu_data.processor_id; + unsigned int fp_vers = current_cpu_data.fpu_id; + unsigned long n = (unsigned long) v - 1; char fmt [64]; - const char *cpu_name[] = CPU_NAMES; - const char *mach_group_names[] = GROUP_NAMES; - const char *mach_unknown_names[] = GROUP_UNKNOWN_NAMES; - const char *mach_jazz_names[] = GROUP_JAZZ_NAMES; - const char *mach_dec_names[] = GROUP_DEC_NAMES; - const char *mach_arc_names[] = GROUP_ARC_NAMES; - const char *mach_sni_rm_names[] = GROUP_SNI_RM_NAMES; - const char *mach_acn_names[] = GROUP_ACN_NAMES; - const char *mach_sgi_names[] = GROUP_SGI_NAMES; - const char *mach_cobalt_names[] = GROUP_COBALT_NAMES; - const char *mach_nec_ddb_names[] = GROUP_NEC_DDB_NAMES; - const char *mach_baget_names[] = GROUP_BAGET_NAMES; - const char *mach_cosine_names[] = GROUP_COSINE_NAMES; - const char *mach_galileo_names[] = GROUP_GALILEO_NAMES; - const char *mach_momenco_names[] = GROUP_MOMENCO_NAMES; - const char *mach_ite_names[] = GROUP_ITE_NAMES; - const char *mach_philips_names[] = GROUP_PHILIPS_NAMES; - const char *mach_globespan_names[] = GROUP_GLOBESPAN_NAMES; - const char *mach_sibyte_names[] = GROUP_SIBYTE_NAMES; - const char *mach_toshiba_names[] = GROUP_TOSHIBA_NAMES; - const char *mach_alchemy_names[] = GROUP_ALCHEMY_NAMES; - const char **mach_group_to_name[] = { mach_unknown_names, - mach_jazz_names, mach_dec_names, mach_arc_names, - mach_sni_rm_names, mach_acn_names, mach_sgi_names, - mach_cobalt_names, mach_nec_ddb_names, mach_baget_names, - mach_cosine_names, mach_galileo_names, mach_momenco_names, - mach_ite_names, mach_philips_names, mach_globespan_names, - mach_sibyte_names, mach_toshiba_names, mach_alchemy_names}; - unsigned int version = read_32bit_cp0_register(CP0_PRID); - int len; - - len = sprintf(buffer, "cpu\t\t\t: MIPS\n"); - len += sprintf(buffer + len, "cpu model\t\t: %s V%d.%d\n", - cpu_name[mips_cpu.cputype <= CPU_LAST ? - mips_cpu.cputype : CPU_UNKNOWN], - (version >> 4) & 0x0f, version & 0x0f); - len += sprintf(buffer + len, "system type\t\t: %s %s\n", - mach_group_names[mips_machgroup], - mach_group_to_name[mips_machgroup][mips_machtype]); - len += sprintf(buffer + len, "BogoMIPS\t\t: %lu.%02lu\n", - loops_per_jiffy/(500000/HZ), - (loops_per_jiffy/(5000/HZ)) % 100); -#if defined (__MIPSEB__) - len += sprintf(buffer + len, "byteorder\t\t: big endian\n"); -#endif -#if defined (__MIPSEL__) - len += sprintf(buffer + len, "byteorder\t\t: little endian\n"); +#ifdef CONFIG_SMP + if (!CPUMASK_TSTB(cpu_online_map, n)) + return 0; #endif - len += sprintf(buffer + len, "unaligned accesses\t: %lu\n", - unaligned_instructions); - len += sprintf(buffer + len, "wait instruction\t: %s\n", - cpu_wait ? "yes" : "no"); - len += sprintf(buffer + len, "microsecond timers\t: %s\n", - (mips_cpu.options & MIPS_CPU_COUNTER) ? "yes" : "no"); - len += sprintf(buffer + len, "extra interrupt vector\t: %s\n", - (mips_cpu.options & MIPS_CPU_DIVEC) ? "yes" : "no"); - len += sprintf(buffer + len, "hardware watchpoint\t: %s\n", - watch_available ? "yes" : "no"); + + /* + * For the first processor also print the system type + */ + if (n == 0) + seq_printf(m, "system type\t\t: %s\n", get_system_type()); + + seq_printf(m, "processor\t\t: %ld\n", n); + sprintf(fmt, "cpu model\t\t: %%s V%%d.%%d%s\n", + cpu_has_fpu ? " FPU V%d.%d" : ""); + seq_printf(m, fmt, cpu_name[current_cpu_data.cputype <= CPU_LAST ? + current_cpu_data.cputype : CPU_UNKNOWN], + (version >> 4) & 0x0f, version & 0x0f, + (fp_vers >> 4) & 0x0f, fp_vers & 0x0f); + seq_printf(m, "BogoMIPS\t\t: %lu.%02lu\n", + loops_per_jiffy / (500000/HZ), + (loops_per_jiffy / (5000/HZ)) % 100); + seq_printf(m, "wait instruction\t: %s\n", cpu_wait ? "yes" : "no"); + seq_printf(m, "microsecond timers\t: %s\n", + cpu_has_counter ? "yes" : "no"); + seq_printf(m, "tlb_entries\t\t: %d\n", current_cpu_data.tlbsize); + seq_printf(m, "extra interrupt vector\t: %s\n", + cpu_has_divec ? "yes" : "no"); + seq_printf(m, "hardware watchpoint\t: %s\n", + cpu_has_watch ? "yes" : "no"); sprintf(fmt, "VCE%%c exceptions\t\t: %s\n", - (mips_cpu.options & MIPS_CPU_VCE) ? "%d" : "not available"); - len += sprintf(buffer + len, fmt, 'D', vced_count); - len += sprintf(buffer + len, fmt, 'I', vcei_count); + cpu_has_vce ? "%d" : "not available"); + seq_printf(m, fmt, 'D', vced_count); + seq_printf(m, fmt, 'I', vcei_count); -#ifndef CONFIG_CPU_HAS_LLSC - len += sprintf(buffer + len, "ll emulations\t\t: %lu\n", - ll_ops); - len += sprintf(buffer + len, "sc emulations\t\t: %lu\n", - sc_ops); -#endif - return len; + return 0; +} + +static void *c_start(struct seq_file *m, loff_t *pos) +{ + unsigned long i = *pos; + + return i < NR_CPUS ? (void *) (i + 1) : NULL; } -void init_irq_proc(void) +static void *c_next(struct seq_file *m, void *v, loff_t *pos) { - /* Nothing, for now. */ + ++*pos; + return c_start(m, pos); } + +static void c_stop(struct seq_file *m, void *v) +{ +} + +struct seq_operations cpuinfo_op = { + .start = c_start, + .next = c_next, + .stop = c_stop, + .show = show_cpuinfo, +}; diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c index 2511f9d4f630..9720809e9978 100644 --- a/arch/mips/kernel/process.c +++ b/arch/mips/kernel/process.c @@ -6,86 +6,95 @@ * Copyright (C) 1994 - 2000 by Ralf Baechle and others. * Copyright (C) 1999 Silicon Graphics, Inc. */ -#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/personality.h> #include <linux/slab.h> #include <linux/mman.h> #include <linux/sys.h> #include <linux/user.h> #include <linux/a.out.h> +#include <linux/init.h> +#include <linux/completion.h> #include <asm/bootinfo.h> #include <asm/cpu.h> +#include <asm/fpu.h> #include <asm/pgtable.h> #include <asm/system.h> #include <asm/mipsregs.h> #include <asm/processor.h> -#include <asm/stackframe.h> +#include <asm/ptrace.h> #include <asm/uaccess.h> #include <asm/io.h> #include <asm/elf.h> #include <asm/isadep.h> +#include <asm/inst.h> -void cpu_idle(void) +/* + * We use this if we don't have any better idle routine.. + * (This to kill: kernel/platform.c. + */ +void default_idle (void) +{ +} + +/* + * The idle thread. There's no useful work to be done, so just try to conserve + * power and have a low exit latency (ie sit in a loop waiting for somebody to + * say that they'd like to reschedule) + */ +ATTRIB_NORET void cpu_idle(void) { /* endless idle loop with no priority at all */ - current->nice = 20; - init_idle(); while (1) { while (!need_resched()) if (cpu_wait) (*cpu_wait)(); schedule(); - check_pgt_cache(); } } -struct task_struct *last_task_used_math = NULL; - asmlinkage void ret_from_fork(void); +void start_thread(struct pt_regs * regs, unsigned long pc, unsigned long sp) +{ + regs->cp0_status &= ~(ST0_CU0|ST0_KSU|ST0_CU1); + regs->cp0_status |= KU_USER; + current->used_math = 0; + loose_fpu(); + regs->cp0_epc = pc; + regs->regs[29] = sp; + current_thread_info()->addr_limit = USER_DS; +} + void exit_thread(void) { - /* Forget lazy fpu state */ - if (last_task_used_math == current) { - set_cp0_status(ST0_CU1); - __asm__ __volatile__("cfc1\t$0,$31"); - last_task_used_math = NULL; - } } void flush_thread(void) { - /* Forget lazy fpu state */ - if (last_task_used_math == current) { - set_cp0_status(ST0_CU1); - __asm__ __volatile__("cfc1\t$0,$31"); - last_task_used_math = NULL; - } } int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, unsigned long unused, struct task_struct * p, struct pt_regs * regs) { + struct thread_info *ti = p->thread_info; struct pt_regs * childregs; long childksp; - extern void save_fp(void*); - childksp = (unsigned long)p + KERNEL_STACK_SIZE - 32; + childksp = (unsigned long)ti + KERNEL_STACK_SIZE - 32; + + if (is_fpu_owner()) { + save_fp(p); + } - if (last_task_used_math == current) - if (mips_cpu.options & MIPS_CPU_FPU) { - set_cp0_status(ST0_CU1); - save_fp(p); - } /* set up new TSS. */ childregs = (struct pt_regs *) childksp - 1; *childregs = *regs; @@ -101,12 +110,12 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, regs->regs[3] = 0; } if (childregs->cp0_status & ST0_CU0) { - childregs->regs[28] = (unsigned long) p; + childregs->regs[28] = (unsigned long) ti; childregs->regs[29] = childksp; - p->thread.current_ds = KERNEL_DS; + ti->addr_limit = KERNEL_DS; } else { childregs->regs[29] = usp; - p->thread.current_ds = USER_DS; + ti->addr_limit = USER_DS; } p->thread.reg29 = (unsigned long) childregs; p->thread.reg31 = (unsigned long) ret_from_fork; @@ -115,9 +124,10 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, * New tasks lose permission to use the fpu. This accelerates context * switching for most programs since they don't use the fpu. */ - p->thread.cp0_status = read_32bit_cp0_register(CP0_STATUS) & + p->thread.cp0_status = read_c0_status() & ~(ST0_CU2|ST0_CU1|KU_MASK); childregs->cp0_status &= ~(ST0_CU2|ST0_CU1); + p->set_child_tid = p->clear_child_tid = NULL; return 0; } @@ -125,29 +135,8 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, /* Fill in the fpu structure for a core dump.. */ int dump_fpu(struct pt_regs *regs, elf_fpregset_t *r) { - /* We actually store the FPU info in the task->thread - * area. - */ - if(regs->cp0_status & ST0_CU1) { - memcpy(r, ¤t->thread.fpu, sizeof(current->thread.fpu)); - return 1; - } - return 0; /* Task didn't use the fpu at all. */ -} - -/* Fill in the user structure for a core dump.. */ -void dump_thread(struct pt_regs *regs, struct user *dump) -{ - dump->magic = CMAGIC; - dump->start_code = current->mm->start_code; - dump->start_data = current->mm->start_data; - dump->start_stack = regs->regs[29] & ~(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; - memcpy(&dump->regs[0], regs, sizeof(struct pt_regs)); - memcpy(&dump->regs[EF_SIZE/4], ¤t->thread.fpu, sizeof(current->thread.fpu)); + memcpy(r, ¤t->thread.fpu, sizeof(current->thread.fpu)); + return 1; } /* @@ -158,35 +147,107 @@ int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) long retval; __asm__ __volatile__( - ".set noreorder \n" - " move $6,$sp \n" - " move $4,%5 \n" - " li $2,%1 \n" - " syscall \n" - " beq $6,$sp,1f \n" - " subu $sp,32 \n" /* delay slot */ - " jalr %4 \n" - " move $4,%3 \n" /* delay slot */ - " move $4,$2 \n" - " li $2,%2 \n" - " syscall \n" - "1: addiu $sp,32 \n" - " move %0,$2 \n" - ".set reorder" - :"=r" (retval) - :"i" (__NR_clone), "i" (__NR_exit), - "r" (arg), "r" (fn), - "r" (flags | CLONE_VM | CLONE_UNTRACED) + " .set noreorder \n" + " move $6, $sp \n" + " move $4, %5 \n" + " li $2, %1 \n" + " syscall \n" + " beq $6, $sp, 1f \n" + " subu $sp, 32 \n" + " jalr %4 \n" + " move $4, %3 \n" + " move $4, $2 \n" + " li $2, %2 \n" + " syscall \n" + "1: addiu $sp, 32 \n" + " move %0, $2 \n" + " .set reorder" + : "=r" (retval) + : "i" (__NR_clone), "i" (__NR_exit), "r" (arg), "r" (fn), + "r" (flags | CLONE_VM | CLONE_UNTRACED) /* * The called subroutine might have destroyed any of the * at, result, argument or temporary registers ... */ - :"$1", "$2", "$3", "$4", "$5", "$6", "$7", "$8", - "$9","$10","$11","$12","$13","$14","$15","$24","$25"); + : "$2", "$3", "$4", "$5", "$6", "$7", "$8", + "$9","$10","$11","$12","$13","$14","$15","$24","$25", "$31"); return retval; } +struct mips_frame_info { + int frame_offset; + int pc_offset; +}; +static struct mips_frame_info schedule_frame; +static struct mips_frame_info schedule_timeout_frame; +static struct mips_frame_info sleep_on_frame; +static struct mips_frame_info sleep_on_timeout_frame; +static struct mips_frame_info wait_for_completion_frame; +static int mips_frame_info_initialized; +static int __init get_frame_info(struct mips_frame_info *info, void *func) +{ + int i; + union mips_instruction *ip = (union mips_instruction *)func; + info->pc_offset = -1; + info->frame_offset = -1; + for (i = 0; i < 128; i++, ip++) { + /* if jal, jalr, jr, stop. */ + if (ip->j_format.opcode == jal_op || + (ip->r_format.opcode == spec_op && + (ip->r_format.func == jalr_op || + ip->r_format.func == jr_op))) + break; + if (ip->i_format.opcode == sw_op && + ip->i_format.rs == 29) { + /* sw $ra, offset($sp) */ + if (ip->i_format.rt == 31) { + if (info->pc_offset != -1) + break; + info->pc_offset = + ip->i_format.simmediate / sizeof(long); + } + /* sw $s8, offset($sp) */ + if (ip->i_format.rt == 30) { + if (info->frame_offset != -1) + break; + info->frame_offset = + ip->i_format.simmediate / sizeof(long); + } + } + } + if (info->pc_offset == -1 || info->frame_offset == -1) { + printk("Can't analyze prologue code at %p\n", func); + info->pc_offset = -1; + info->frame_offset = -1; + return -1; + } + + return 0; +} +void __init frame_info_init(void) +{ + mips_frame_info_initialized = + !get_frame_info(&schedule_frame, schedule) && + !get_frame_info(&schedule_timeout_frame, schedule_timeout) && + !get_frame_info(&sleep_on_frame, sleep_on) && + !get_frame_info(&sleep_on_timeout_frame, sleep_on_timeout) && + !get_frame_info(&wait_for_completion_frame, wait_for_completion); +} + +unsigned long thread_saved_pc(struct thread_struct *t) +{ + extern void ret_from_fork(void); + + /* New born processes are a special case */ + if (t->reg31 == (unsigned long) ret_from_fork) + return t->reg31; + + if (schedule_frame.pc_offset < 0) + return 0; + return ((unsigned long *)t->reg29)[schedule_frame.pc_offset]; +} + /* * These bracket the sleeping functions.. */ @@ -195,7 +256,7 @@ extern void scheduling_functions_end_here(void); #define first_sched ((unsigned long) scheduling_functions_start_here) #define last_sched ((unsigned long) scheduling_functions_end_here) -/* get_wchan - a maintenance nightmare ... */ +/* get_wchan - a maintenance nightmare^W^Wpain in the ass ... */ unsigned long get_wchan(struct task_struct *p) { unsigned long frame, pc; @@ -203,6 +264,8 @@ unsigned long get_wchan(struct task_struct *p) if (!p || p == current || p->state == TASK_RUNNING) return 0; + if (!mips_frame_info_initialized) + return 0; pc = thread_saved_pc(&p->thread); if (pc < first_sched || pc >= last_sched) { return pc; @@ -216,26 +279,33 @@ unsigned long get_wchan(struct task_struct *p) goto schedule_timeout_caller; if (pc >= (unsigned long)interruptible_sleep_on) goto schedule_caller; + if (pc >= (unsigned long)wait_for_completion) + goto schedule_caller; goto schedule_timeout_caller; schedule_caller: - frame = ((unsigned long *)p->thread.reg30)[9]; - pc = ((unsigned long *)frame)[11]; + frame = ((unsigned long *)p->thread.reg30)[schedule_frame.frame_offset]; + if (pc >= (unsigned long) sleep_on) + pc = ((unsigned long *)frame)[sleep_on_frame.pc_offset]; + else + pc = ((unsigned long *)frame)[wait_for_completion_frame.pc_offset]; return pc; schedule_timeout_caller: - /* Must be schedule_timeout ... */ - pc = ((unsigned long *)p->thread.reg30)[10]; - frame = ((unsigned long *)p->thread.reg30)[9]; + /* + * The schedule_timeout frame + */ + frame = ((unsigned long *)p->thread.reg30)[schedule_frame.frame_offset]; - /* The schedule_timeout frame ... */ - pc = ((unsigned long *)frame)[14]; - frame = ((unsigned long *)frame)[13]; + /* + * frame now points to sleep_on_timeout's frame + */ + pc = ((unsigned long *)frame)[schedule_timeout_frame.pc_offset]; if (pc >= first_sched && pc < last_sched) { - /* schedule_timeout called by interruptible_sleep_on_timeout */ - pc = ((unsigned long *)frame)[11]; - frame = ((unsigned long *)frame)[10]; + /* schedule_timeout called by [interruptible_]sleep_on_timeout */ + frame = ((unsigned long *)frame)[schedule_timeout_frame.frame_offset]; + pc = ((unsigned long *)frame)[sleep_on_timeout_frame.pc_offset]; } return pc; diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index 90601f455433..c76a4351d533 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -11,6 +11,7 @@ * Copyright (C) 1999 MIPS Technologies, Inc. */ #include <linux/config.h> +#include <linux/compiler.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/mm.h> @@ -19,8 +20,8 @@ #include <linux/smp.h> #include <linux/smp_lock.h> #include <linux/user.h> +#include <linux/security.h> -#include <asm/fp.h> #include <asm/mipsregs.h> #include <asm/pgtable.h> #include <asm/page.h> @@ -28,6 +29,7 @@ #include <asm/uaccess.h> #include <asm/bootinfo.h> #include <asm/cpu.h> +#include <asm/fpu.h> /* * Called by kernel/ptrace.c when detaching.. @@ -42,27 +44,27 @@ void ptrace_disable(struct task_struct *child) asmlinkage int sys_ptrace(long request, long pid, long addr, long data) { struct task_struct *child; - int res; - extern void save_fp(struct task_struct *); + int ret; - lock_kernel(); #if 0 printk("ptrace(r=%d,pid=%d,addr=%08lx,data=%08lx)\n", (int) request, (int) pid, (unsigned long) addr, (unsigned long) data); #endif + lock_kernel(); + ret = -EPERM; if (request == PTRACE_TRACEME) { /* are we already being traced? */ - if (current->ptrace & PT_PTRACED) { - res = -EPERM; + if (current->ptrace & PT_PTRACED) + goto out; + if ((ret = security_ptrace(current->parent, current))) goto out; - } /* set the ptrace bit in the process flags. */ current->ptrace |= PT_PTRACED; - res = 0; + ret = 0; goto out; } - res = -ESRCH; + ret = -ESRCH; read_lock(&tasklist_lock); child = find_task_by_pid(pid); if (child) @@ -71,36 +73,31 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) if (!child) goto out; - res = -EPERM; + ret = -EPERM; if (pid == 1) /* you may not mess with init */ - goto out; + goto out_tsk; if (request == PTRACE_ATTACH) { - res = ptrace_attach(child); - goto out_tsk; - } - res = -ESRCH; - if (!(child->ptrace & PT_PTRACED)) + ret = ptrace_attach(child); goto out_tsk; - if (child->state != TASK_STOPPED) { - if (request != PTRACE_KILL) - goto out_tsk; } - if (child->p_pptr != current) + + ret = ptrace_check_attach(child, request == PTRACE_KILL); + if (ret < 0) goto out_tsk; + switch (request) { - case PTRACE_PEEKTEXT: /* read word at location addr. */ + case PTRACE_PEEKTEXT: /* read word at location addr. */ case PTRACE_PEEKDATA: { unsigned long tmp; int copied; copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); - res = -EIO; + ret = -EIO; if (copied != sizeof(tmp)) break; - res = put_user(tmp,(unsigned long *) data); - - goto out; + ret = put_user(tmp,(unsigned long *) data); + break; } /* Read the word at location addr in the USER area. */ @@ -108,7 +105,7 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) struct pt_regs *regs; unsigned long tmp; - regs = (struct pt_regs *) ((unsigned long) child + + regs = (struct pt_regs *) ((unsigned long) child->thread_info + KERNEL_STACK_SIZE - 32 - sizeof(struct pt_regs)); tmp = 0; /* Default return value. */ @@ -118,30 +115,12 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) break; case FPR_BASE ... FPR_BASE + 31: if (child->used_math) { - unsigned long long *fregs - = (unsigned long long *) - &child->thread.fpu.hard.fp_regs[0]; - if(!(mips_cpu.options & MIPS_CPU_FPU)) { - fregs = (unsigned long long *) - child->thread.fpu.soft.regs; - } else - if (last_task_used_math == child) { - enable_cp1(); - save_fp(child); - disable_cp1(); - last_task_used_math = NULL; - regs->cp0_status &= ~ST0_CU1; - } + unsigned long long *fregs = get_fpu_regs(child); /* * The odd registers are actually the high * order bits of the values stored in the even * registers - unless we're using r2k_switch.S. */ -#ifdef CONFIG_CPU_R3000 - if (mips_cpu.options & MIPS_CPU_FPU) - tmp = *(unsigned long *)(fregs + addr); - else -#endif if (addr & 1) tmp = (unsigned long) (fregs[((addr & ~1) - 32)] >> 32); else @@ -166,7 +145,7 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) tmp = regs->lo; break; case FPC_CSR: - if (!(mips_cpu.options & MIPS_CPU_FPU)) + if (!cpu_has_fpu) tmp = child->thread.fpu.soft.sr; else tmp = child->thread.fpu.hard.control; @@ -174,34 +153,37 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) case FPC_EIR: { /* implementation / version register */ unsigned int flags; - local_save_flags(flags); - enable_cp1(); + if (!cpu_has_fpu) + break; + + flags = read_c0_status(); + __enable_fpu(); __asm__ __volatile__("cfc1\t%0,$0": "=r" (tmp)); - local_irq_restore(flags); + write_c0_status(flags); break; } default: tmp = 0; - res = -EIO; - goto out; + ret = -EIO; + goto out_tsk; } - res = put_user(tmp, (unsigned long *) data); - goto out; + ret = put_user(tmp, (unsigned long *) data); + break; } case PTRACE_POKETEXT: /* write the word at location addr. */ case PTRACE_POKEDATA: - res = 0; + ret = 0; if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data)) break; - res = -EIO; - goto out; + ret = -EIO; + break; case PTRACE_POKEUSR: { struct pt_regs *regs; - res = 0; - regs = (struct pt_regs *) ((unsigned long) child + + ret = 0; + regs = (struct pt_regs *) ((unsigned long) child->thread_info + KERNEL_STACK_SIZE - 32 - sizeof(struct pt_regs)); switch (addr) { @@ -210,21 +192,8 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) break; case FPR_BASE ... FPR_BASE + 31: { unsigned long long *fregs; - fregs = (unsigned long long *)&child->thread.fpu.hard.fp_regs[0]; - if (child->used_math) { - if (last_task_used_math == child) { - if(!(mips_cpu.options & MIPS_CPU_FPU)) { - fregs = (unsigned long long *) - child->thread.fpu.soft.regs; - } else { - enable_cp1(); - save_fp(child); - disable_cp1(); - last_task_used_math = NULL; - regs->cp0_status &= ~ST0_CU1; - } - } - } else { + fregs = (unsigned long long *)get_fpu_regs(child); + if (!child->used_math) { /* FP not yet used */ memset(&child->thread.fpu.hard, ~0, sizeof(child->thread.fpu.hard)); @@ -235,11 +204,6 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) * of the values stored in the even registers - unless * we're using r2k_switch.S. */ -#ifdef CONFIG_CPU_R3000 - if (mips_cpu.options & MIPS_CPU_FPU) - *(unsigned long *)(fregs + addr) = data; - else -#endif if (addr & 1) { fregs[(addr & ~1) - FPR_BASE] &= 0xffffffff; fregs[(addr & ~1) - FPR_BASE] |= ((unsigned long long) data) << 32; @@ -259,14 +223,14 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) regs->lo = data; break; case FPC_CSR: - if (!(mips_cpu.options & MIPS_CPU_FPU)) + if (!cpu_has_fpu) child->thread.fpu.soft.sr = data; else child->thread.fpu.hard.control = data; break; default: /* The rest are not allowed. */ - res = -EIO; + ret = -EIO; break; } break; @@ -274,26 +238,28 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ case PTRACE_CONT: { /* restart after signal. */ - res = -EIO; + ret = -EIO; if ((unsigned long) data > _NSIG) break; - if (request == PTRACE_SYSCALL) - child->ptrace |= PT_TRACESYS; - else - child->ptrace &= ~PT_TRACESYS; + if (request == PTRACE_SYSCALL) { + set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + } + else { + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + } child->exit_code = data; wake_up_process(child); - res = 0; + ret = 0; break; } /* - * make the child exit. Best I can do is send it a sigkill. - * perhaps it should be put in the status that it wants to + * make the child exit. Best I can do is send it a sigkill. + * perhaps it should be put in the status that it wants to * exit. */ case PTRACE_KILL: - res = 0; + ret = 0; if (child->state == TASK_ZOMBIE) /* already dead */ break; child->exit_code = SIGKILL; @@ -301,32 +267,40 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) break; case PTRACE_DETACH: /* detach a process that was attached. */ - res = ptrace_detach(child, data); + ret = ptrace_detach(child, data); break; default: - res = ptrace_request(child, request, addr, data); - goto out; + ret = ptrace_request(child, request, addr, data); + break; } out_tsk: - free_task_struct(child); + put_task_struct(child); out: unlock_kernel(); - return res; + return ret; } -asmlinkage void syscall_trace(void) +/* + * Notification of system call entry/exit + * - triggered by current->work.syscall_trace + */ +asmlinkage void do_syscall_trace(void) { - if ((current->ptrace & (PT_PTRACED|PT_TRACESYS)) - != (PT_PTRACED|PT_TRACESYS)) + if (!test_thread_flag(TIF_SYSCALL_TRACE)) return; + if (!(current->ptrace & PT_PTRACED)) + return; + /* The 0x80 provides a way for the tracing parent to distinguish between a syscall stop and SIGTRAP delivery */ current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) ? 0x80 : 0); + preempt_disable(); current->state = TASK_STOPPED; notify_parent(current, SIGCHLD); schedule(); + preempt_enable(); /* * this isn't the same as continuing with a signal, but it will do * for normal use. strace only continues with a signal if the diff --git a/arch/mips/kernel/r2300_switch.S b/arch/mips/kernel/r2300_switch.S index aefdff8645e7..3fa8298329c2 100644 --- a/arch/mips/kernel/r2300_switch.S +++ b/arch/mips/kernel/r2300_switch.S @@ -10,18 +10,17 @@ * Further modifications to make this work: * Copyright (c) 1998-2000 Harald Koerfgen */ +#include <linux/config.h> #include <asm/asm.h> -#include <asm/bootinfo.h> #include <asm/cachectl.h> -#include <asm/current.h> #include <asm/fpregdef.h> #include <asm/mipsregs.h> #include <asm/offset.h> #include <asm/page.h> -#include <asm/pgtable.h> #include <asm/processor.h> #include <asm/regdef.h> #include <asm/stackframe.h> +#include <asm/thread_info.h> #include <asm/asmmacro.h> @@ -29,8 +28,24 @@ .align 5 /* - * task_struct *resume(task_struct *prev, - * task_struct *next) + * Offset to the current process status flags, the first 32 bytes of the + * stack are not used. + */ +#define ST_OFF (KERNEL_STACK_SIZE - 32 - PT_SIZE + PT_STATUS) + +/* + * FPU context is saved iff the process has used it's FPU in the current + * time slice as indicated by TIF_USEDFPU. In any case, the CU1 bit for user + * space STATUS register should be 0, so that a process *always* starts its + * userland with FPU disabled after each context switch. + * + * FPU will be enabled as soon as the process accesses FPU again, through + * do_cpu() trap. + */ + +/* + * task_struct *resume(task_struct *prev, task_struct *next, + * struct thread_info *next_ti) ) */ LEAF(resume) #ifndef CONFIG_CPU_HAS_LLSC @@ -41,84 +56,81 @@ LEAF(resume) CPU_SAVE_NONSCRATCH(a0) sw ra, THREAD_REG31(a0) + /* + * check if we need to save FPU registers + */ + lw t3, TASK_THREAD_INFO(a0) + lw t0, TI_FLAGS(t3) + li t1, TIF_USEDFPU + and t2, t0, t1 + beqz t2, 1f + nor t1, zero, t1 + + and t0, t0, t1 + sw t0, TI_FLAGS(t3) + + /* + * clear saved user stack CU1 bit + */ + lw t0, ST_OFF(t3) + li t1, ~ST0_CU1 + and t0, t0, t1 + sw t0, ST_OFF(t3) + + FPU_SAVE_SINGLE(a0, t0) # clobbers t0 + +1: /* * The order of restoring the registers takes care of the race * updating $28, $29 and kernelsp without disabling ints. */ - move $28, a1 - CPU_RESTORE_NONSCRATCH($28) - addiu t0, $28, KERNEL_STACK_SIZE-32 - sw t0, kernelsp + move $28, a2 + CPU_RESTORE_NONSCRATCH(a1) + + addiu t1, $28, KERNEL_STACK_SIZE-32 + sw t1, kernelsp + mfc0 t1, CP0_STATUS /* Do we really need this? */ li a3, 0xff00 and t1, a3 - lw a2, THREAD_STATUS($28) + lw a2, THREAD_STATUS(a1) nor a3, $0, a3 and a2, a3 or a2, t1 mtc0 a2, CP0_STATUS - .set noreorder + move v0, a0 jr ra - move v0, a0 - .set reorder END(resume) /* - * Do lazy fpu context switch. Saves FPU context to the process in a0 - * and loads the new context of the current process. - */ - -#define ST_OFF (KERNEL_STACK_SIZE - 32 - PT_SIZE + PT_STATUS) - -LEAF(lazy_fpu_switch) - mfc0 t0, CP0_STATUS # enable cp1 - li t3, 0x20000000 - or t0, t3 - mtc0 t0, CP0_STATUS - - .set noreorder - beqz a0, 2f # Save floating point state - nor t3, zero, t3 - .set reorder - lw t1, ST_OFF(a0) # last thread loses fpu - and t1, t3 - sw t1, ST_OFF(a0) - FPU_SAVE_SINGLE(a0, t1) # clobbers t1 - -2: - FPU_RESTORE_SINGLE($28, t0) # clobbers t0 - jr ra - END(lazy_fpu_switch) - -/* * Save a thread's fp context. */ -LEAF(save_fp) +LEAF(_save_fp) FPU_SAVE_SINGLE(a0, t1) # clobbers t1 jr ra - END(save_fp) + END(_save_fp) /* * Restore a thread's fp context. */ -LEAF(restore_fp) +LEAF(_restore_fp) FPU_RESTORE_SINGLE(a0, t1) # clobbers t1 jr ra - END(restore_fp) + END(_restore_fp) /* * Load the FPU with signalling NANS. This bit pattern we're using has * the property that no matter whether considered as single or as double - * precission represents signaling NANS. + * precision represents signaling NANS. * * We initialize fcr31 to rounding to nearest, no exceptions. */ #define FPU_DEFAULT 0x00000000 -LEAF(init_fpu) +LEAF(_init_fpu) mfc0 t0, CP0_STATUS - li t1, 0x20000000 + li t1, ST0_CU1 or t0, t1 mtc0 t0, CP0_STATUS @@ -158,8 +170,6 @@ LEAF(init_fpu) mtc1 t0, $f28 mtc1 t0, $f29 mtc1 t0, $f30 - .set noreorder + mtc1 t0, $f31 jr ra - mtc1 t0, $f31 - .set reorder - END(init_fpu) + END(_init_fpu) diff --git a/arch/mips/kernel/r4k_misc.S b/arch/mips/kernel/r4k_misc.S deleted file mode 100644 index 510b08ca9d16..000000000000 --- a/arch/mips/kernel/r4k_misc.S +++ /dev/null @@ -1,236 +0,0 @@ -/* $Id: r4k_misc.S,v 1.8 1999/10/09 00:00:58 ralf Exp $ - * - * r4k_misc.S: Misc. exception handling code for r4k. - * - * Copyright (C) 1994, 1995, 1996 by Ralf Baechle and Andreas Busse - * - * Multi-cpu abstraction and reworking: - * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) - */ -/************************************************************************** - * 14 Nov, 2000. - * Made support for MIPS32 CPUs. - * - * Carsten Langgaard, carstenl@mips.com - * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved. - *************************************************************************/ -#include <asm/asm.h> -#include <asm/current.h> -#include <asm/offset.h> -#include <asm/bootinfo.h> -#include <asm/cachectl.h> -#include <asm/fpregdef.h> -#include <asm/mipsregs.h> -#include <asm/page.h> -#include <asm/pgtable.h> -#include <asm/processor.h> -#include <asm/regdef.h> -#include <asm/stackframe.h> - -#undef NOTLB_OPTIMIZE /* If you are paranoid, define this. */ - - /* ABUSE of CPP macros 101. */ - - /* After this macro runs, the pte faulted on is - * in register PTE, a ptr into the table in which - * the pte belongs is in PTR. - */ - -#ifdef CONFIG_SMP -#define GET_PGD(scratch, ptr) \ - mfc0 ptr, CP0_CONTEXT; \ - la scratch, current_pgd;\ - srl ptr, 23; \ - sll ptr, 2; \ - addu ptr, scratch, ptr; \ - lw ptr, (ptr); -#else -#define GET_PGD(scratch, ptr) \ - lw ptr, current_pgd; -#endif - - -#define LOAD_PTE(pte, ptr) \ - GET_PGD(pte, ptr) \ - mfc0 pte, CP0_BADVADDR; \ - srl pte, pte, 22; \ - sll pte, pte, 2; \ - addu ptr, ptr, pte; \ - mfc0 pte, CP0_BADVADDR; \ - lw ptr, (ptr); \ - srl pte, pte, 10; \ - and pte, pte, 0xffc; \ - addu ptr, ptr, pte; \ - lw pte, (ptr); - - /* This places the even/odd pte pair in the page - * table at PTR into ENTRYLO0 and ENTRYLO1 using - * TMP as a scratch register. - */ -#define PTE_RELOAD(ptr, tmp) \ - ori ptr, ptr, 0x4; \ - xori ptr, ptr, 0x4; \ - lw tmp, 4(ptr); \ - lw ptr, 0(ptr); \ - srl tmp, tmp, 6; \ - mtc0 tmp, CP0_ENTRYLO1; \ - srl ptr, ptr, 6; \ - mtc0 ptr, CP0_ENTRYLO0; - -#define DO_FAULT(write) \ - .set noat; \ - SAVE_ALL; \ - mfc0 a2, CP0_BADVADDR; \ - STI; \ - .set at; \ - move a0, sp; \ - jal do_page_fault; \ - li a1, write; \ - j ret_from_sys_call; \ - nop; \ - .set noat; - - /* Check is PTE is present, if not then jump to LABEL. - * PTR points to the page table where this PTE is located, - * when the macro is done executing PTE will be restored - * with its original value. - */ -#define PTE_PRESENT(pte, ptr, label) \ - andi pte, pte, (_PAGE_PRESENT | _PAGE_READ); \ - xori pte, pte, (_PAGE_PRESENT | _PAGE_READ); \ - bnez pte, label; \ - lw pte, (ptr); - - /* Make PTE valid, store result in PTR. */ -#define PTE_MAKEVALID(pte, ptr) \ - ori pte, pte, (_PAGE_VALID | _PAGE_ACCESSED); \ - sw pte, (ptr); - - /* Check if PTE can be written to, if not branch to LABEL. - * Regardless restore PTE with value from PTR when done. - */ -#define PTE_WRITABLE(pte, ptr, label) \ - andi pte, pte, (_PAGE_PRESENT | _PAGE_WRITE); \ - xori pte, pte, (_PAGE_PRESENT | _PAGE_WRITE); \ - bnez pte, label; \ - lw pte, (ptr); - - /* Make PTE writable, update software status bits as well, - * then store at PTR. - */ -#define PTE_MAKEWRITE(pte, ptr) \ - ori pte, pte, (_PAGE_ACCESSED | _PAGE_MODIFIED | \ - _PAGE_VALID | _PAGE_DIRTY); \ - sw pte, (ptr); - - .set noreorder - -/* - * From the IDT errata for the QED RM5230 (Nevada), processor revision 1.0: - * 2. A timing hazard exists for the TLBP instruction. - * - * stalling_instruction - * TLBP - * - * The JTLB is being read for the TLBP throughout the stall generated by the - * previous instruction. This is not really correct as the stalling instruction - * can modify the address used to access the JTLB. The failure symptom is that - * the TLBP instruction will use an address created for the stalling instruction - * and not the address held in C0_ENHI and thus report the wrong results. - * - * The software work-around is to not allow the instruction preceding the TLBP - * to stall - make it an NOP or some other instruction guaranteed not to stall. - * - * Errata 2 will not be fixed. This errata is also on the R5000. - * - * As if we MIPS hackers wouldn't know how to nop pipelines happy ... - */ -#define R5K_HAZARD nop - - /* - * Note for many R4k variants tlb probes cannot be executed out - * of the instruction cache else you get bogus results. - */ - .align 5 - NESTED(handle_tlbl, PT_SIZE, sp) - .set noat -invalid_tlbl: -#ifndef NOTLB_OPTIMIZE - /* Test present bit in entry. */ - LOAD_PTE(k0, k1) - R5K_HAZARD - tlbp - PTE_PRESENT(k0, k1, nopage_tlbl) - PTE_MAKEVALID(k0, k1) - PTE_RELOAD(k1, k0) - nop - b 1f - tlbwi -1: - nop - .set mips3 - eret - .set mips0 -#endif - -nopage_tlbl: - DO_FAULT(0) - END(handle_tlbl) - - .align 5 - NESTED(handle_tlbs, PT_SIZE, sp) - .set noat -#ifndef NOTLB_OPTIMIZE - LOAD_PTE(k0, k1) - R5K_HAZARD - tlbp # find faulting entry - PTE_WRITABLE(k0, k1, nopage_tlbs) - PTE_MAKEWRITE(k0, k1) - PTE_RELOAD(k1, k0) - nop - b 1f - tlbwi -1: - nop - .set mips3 - eret - .set mips0 -#endif - -nopage_tlbs: - DO_FAULT(1) - END(handle_tlbs) - - .align 5 - NESTED(handle_mod, PT_SIZE, sp) - .set noat -#ifndef NOTLB_OPTIMIZE - LOAD_PTE(k0, k1) - R5K_HAZARD - tlbp # find faulting entry - andi k0, k0, _PAGE_WRITE - beqz k0, nowrite_mod - lw k0, (k1) - - /* Present and writable bits set, set accessed and dirty bits. */ - PTE_MAKEWRITE(k0, k1) -#if 0 - ori k0, k0, (_PAGE_ACCESSED | _PAGE_DIRTY) - sw k0, (k1) -#endif - - /* Now reload the entry into the tlb. */ - PTE_RELOAD(k1, k0) - nop - b 1f - tlbwi -1: - nop - .set mips3 - eret - .set mips0 -#endif - -nowrite_mod: - DO_FAULT(1) - END(handle_mod) diff --git a/arch/mips/kernel/r4k_switch.S b/arch/mips/kernel/r4k_switch.S index 18d5ae71abe3..67609f0710a8 100644 --- a/arch/mips/kernel/r4k_switch.S +++ b/arch/mips/kernel/r4k_switch.S @@ -3,7 +3,7 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 1994, 1995, 1996, 1998, 1999 by Ralf Baechle + * Copyright (C) 1994, 1995, 1996, 1998, 1999, 2002 by Ralf Baechle * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) * Copyright (C) 1994, 1995, 1996, by Andreas Busse * Copyright (C) 1999 Silicon Graphics, Inc. @@ -12,24 +12,35 @@ */ #include <linux/config.h> #include <asm/asm.h> -#include <asm/bootinfo.h> #include <asm/cachectl.h> -#include <asm/current.h> #include <asm/fpregdef.h> #include <asm/mipsregs.h> #include <asm/offset.h> #include <asm/page.h> -#include <asm/pgtable.h> +#include <asm/pgtable-bits.h> #include <asm/processor.h> #include <asm/regdef.h> #include <asm/stackframe.h> +#include <asm/thread_info.h> #include <asm/asmmacro.h> +#define ST_OFF (KERNEL_STACK_SIZE - 32 - PT_SIZE + PT_STATUS) + +/* + * FPU context is saved iff the process has used it's FPU in the current + * time slice as indicated by TIF_USEDFPU. In any case, the CU1 bit for user + * space STATUS register should be 0, so that a process *always* starts its + * userland with FPU disabled after each context switch. + * + * FPU will be enabled as soon as the process accesses FPU again, through + * do_cpu() trap. + */ + /* - * task_struct *r4xx0_resume(task_struct *prev, task_struct *next) + * task_struct *r4xx0_resume(task_struct *prev, task_struct *next, + * struct thread_info *next_ti) */ - .set noreorder .align 5 LEAF(resume) #ifndef CONFIG_CPU_HAS_LLSC @@ -40,79 +51,76 @@ CPU_SAVE_NONSCRATCH(a0) sw ra, THREAD_REG31(a0) + /* + * check if we need to save FPU registers + */ + lw t3, TASK_THREAD_INFO(a0) + lw t0, TI_FLAGS(t3) + li t1, _TIF_USEDFPU + and t2, t0, t1 + beqz t2, 1f + nor t1, zero, t1 + + and t0, t0, t1 + sw t0, TI_FLAGS(t3) + + /* + * clear saved user stack CU1 bit + */ + lw t0, ST_OFF(t3) + li t1, ~ST0_CU1 + and t0, t0, t1 + sw t0, ST_OFF(t3) + + FPU_SAVE_DOUBLE(a0, t0) # clobbers t0 + +1: /* * The order of restoring the registers takes care of the race * updating $28, $29 and kernelsp without disabling ints. */ - move $28, a1 - CPU_RESTORE_NONSCRATCH($28) + move $28, a2 + CPU_RESTORE_NONSCRATCH(a1) + addiu t0, $28, KERNEL_STACK_SIZE-32 -#ifdef CONFIG_SMP +#ifdef CONFIG_SMP mfc0 a3, CP0_CONTEXT la t1, kernelsp srl a3, 23 sll a3, 2 addu t1, a3, t1 - sw t0, (t1) + sw t0, (t1) #else sw t0, kernelsp -#endif +#endif + mfc0 t1, CP0_STATUS /* Do we really need this? */ li a3, 0xff00 and t1, a3 - lw a2, THREAD_STATUS($28) + lw a2, THREAD_STATUS(a1) nor a3, $0, a3 and a2, a3 or a2, t1 mtc0 a2, CP0_STATUS + move v0, a0 jr ra - move v0, a0 END(resume) /* - * Do lazy fpu context switch. Saves FPU context to the process in a0 - * and loads the new context of the current process. - */ - -#define ST_OFF (KERNEL_STACK_SIZE - 32 - PT_SIZE + PT_STATUS) - -LEAF(lazy_fpu_switch) - mfc0 t0, CP0_STATUS # enable cp1 - li t3, 0x20000000 - or t0, t3 - mtc0 t0, CP0_STATUS - - beqz a0, 2f # Save floating point state - nor t3, zero, t3 - - lw t1, ST_OFF(a0) # last thread loses fpu - and t1, t3 - sw t1, ST_OFF(a0) - - - FPU_SAVE_DOUBLE(a0, t1) # clobbers t1 -2: - - .set reorder - FPU_RESTORE_DOUBLE($28, t0) # clobbers t0 - jr ra - END(lazy_fpu_switch) - -/* * Save a thread's fp context. */ -LEAF(save_fp) +LEAF(_save_fp) FPU_SAVE_DOUBLE(a0, t1) # clobbers t1 jr ra - END(save_fp) + END(_save_fp) /* * Restore a thread's fp context. */ -LEAF(restore_fp) +LEAF(_restore_fp) FPU_RESTORE_DOUBLE(a0, t1) # clobbers t1 jr ra - END(restore_fp) + END(_restore_fp) /* * Load the FPU with signalling NANS. This bit pattern we're using has @@ -124,12 +132,13 @@ LEAF(restore_fp) #define FPU_DEFAULT 0x00000000 -LEAF(init_fpu) +LEAF(_init_fpu) .set mips3 mfc0 t0, CP0_STATUS - li t1, 0x20000000 + li t1, ST0_CU1 or t0, t1 mtc0 t0, CP0_STATUS + FPU_ENABLE_HAZARD li t1, FPU_DEFAULT ctc1 t1, fcr31 @@ -151,9 +160,6 @@ LEAF(init_fpu) dmtc1 t0, $f24 dmtc1 t0, $f26 dmtc1 t0, $f28 - .set noreorder + dmtc1 t0, $f30 jr ra - dmtc1 t0, $f30 - .set reorder - END(init_fpu) - + END(_init_fpu) diff --git a/arch/mips/kernel/scall_o32.S b/arch/mips/kernel/scall_o32.S index 7df9bd8bbbdb..ff27a1bf1eaf 100644 --- a/arch/mips/kernel/scall_o32.S +++ b/arch/mips/kernel/scall_o32.S @@ -3,124 +3,107 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 1997, 1998, 1999, 2000 by Ralf Baechle + * Copyright (C) 1997, 1998, 1999, 2000, 2001 by Ralf Baechle + * Copyright (C) 2001 MIPS Technologies, Inc. */ -#include <asm/asm.h> +#include <linux/config.h> #include <linux/errno.h> -#include <asm/current.h> +#include <asm/asm.h> #include <asm/mipsregs.h> #include <asm/regdef.h> #include <asm/stackframe.h> #include <asm/isadep.h> +#include <asm/sysmips.h> #include <asm/unistd.h> - -/* This duplicates the definition from <linux/sched.h> */ -#define PT_TRACESYS 0x00000002 /* tracing system calls */ - -/* This duplicates the definition from <asm/signal.h> */ -#define SIGILL 4 /* Illegal instruction (ANSI). */ +#include <asm/offset.h> /* Highest syscall used of any syscall flavour */ #define MAX_SYSCALL_NO __NR_Linux + __NR_Linux_syscalls .align 5 NESTED(handle_sys, PT_SIZE, sp) - .set noat - SAVE_SOME - STI - .set at + .set noat + SAVE_SOME + STI + .set at - lw t1, PT_EPC(sp) # skip syscall on return + lw t1, PT_EPC(sp) # skip syscall on return - sltiu t0, v0, MAX_SYSCALL_NO + 1 # check syscall number - addiu t1, 4 # skip to next instruction - beqz t0, illegal_syscall - sw t1, PT_EPC(sp) + sltiu t0, v0, MAX_SYSCALL_NO + 1 # check syscall number + addiu t1, 4 # skip to next instruction + beqz t0, illegal_syscall + sw t1, PT_EPC(sp) - /* XXX Put both in one cacheline, should save a bit. */ - sll t0, v0, 2 - lw t2, sys_call_table(t0) # syscall routine - lbu t3, sys_narg_table(v0) # number of arguments - beqz t2, illegal_syscall; + /* XXX Put both in one cacheline, should save a bit. */ + sll t0, v0, 2 + lw t2, sys_call_table(t0) # syscall routine + lbu t3, sys_narg_table(v0) # number of arguments + beqz t2, illegal_syscall; - subu t0, t3, 5 # 5 or more arguments? - sw a3, PT_R26(sp) # save a3 for syscall restarting - bgez t0, stackargs + subu t0, t3, 5 # 5 or more arguments? + sw a3, PT_R26(sp) # save a3 for syscall restarting + bgez t0, stackargs stack_done: - sw a3, PT_R26(sp) # save for syscall restart -#error lw t0, TASK_PTRACE($28) # syscall tracing enabled? - andi t0, PT_TRACESYS - bnez t0, trace_a_syscall - - jalr t2 # Do The Real Thing (TM) - - li t0, -EMAXERRNO - 1 # error? - sltu t0, t0, v0 - sw t0, PT_R7(sp) # set error flag - beqz t0, 1f - - negu v0 # error - sw v0, PT_R0(sp) # set flag for syscall restarting -1: sw v0, PT_R2(sp) # result - -EXPORT(o32_ret_from_sys_call) - mfc0 t0, CP0_STATUS # need_resched and signals atomic test - ori t0, t0, 1 - xori t0, t0, 1 - mtc0 t0, CP0_STATUS - -#error lw t2, TASK_NEED_RESCHED($28) - bnez t2, o32_reschedule -#error lw v0, TASK_SIGPENDING($28) - bnez v0, signal_return -restore_all: - RESTORE_SOME - RESTORE_SP_AND_RET - -/* Put this behind restore_all for the sake of the branch prediction. */ -signal_return: - .type signal_return, @function - - mfc0 t0, CP0_STATUS - ori t0, t0, 1 - mtc0 t0, CP0_STATUS - - move a0, zero - move a1, sp -#error jal do_signal - b restore_all - -o32_reschedule: - SAVE_STATIC - jal schedule - b o32_ret_from_sys_call + sw a3, PT_R26(sp) # save for syscall restart + LONG_L t0, TI_FLAGS($28) # syscall tracing enabled? + bltz t0, syscall_trace_entry # -> yes + + jalr t2 # Do The Real Thing (TM) + + li t0, -EMAXERRNO - 1 # error? + sltu t0, t0, v0 + sw t0, PT_R7(sp) # set error flag + beqz t0, 1f + + negu v0 # error + sw v0, PT_R0(sp) # set flag for syscall + # restarting +1: sw v0, PT_R2(sp) # result + +EXPORT(o32_syscall_exit) + mfc0 t0, CP0_STATUS # make sure need_resched and + ori t0, t0, 1 # signals dont change between + xori t0, t0, 1 # sampling and return + mtc0 t0, CP0_STATUS + SSNOP; SSNOP; SSNOP + + LONG_L a2, TI_FLAGS($28) # current->work + bnez a2, o32_syscall_exit_work + +o32_restore_all: + RESTORE_SOME + RESTORE_SP_AND_RET + +o32_syscall_exit_work: + SAVE_STATIC + j syscall_exit_work /* ------------------------------------------------------------------------ */ -trace_a_syscall: - SAVE_STATIC - sw t2, PT_R1(sp) -#error jal syscall_trace - lw t2, PT_R1(sp) +syscall_trace_entry: + SAVE_STATIC + sw t2, PT_R1(sp) + jal do_syscall_trace + lw t2, PT_R1(sp) - lw a0, PT_R4(sp) # Restore argument registers - lw a1, PT_R5(sp) - lw a2, PT_R6(sp) - lw a3, PT_R7(sp) - jalr t2 + lw a0, PT_R4(sp) # Restore argument registers + lw a1, PT_R5(sp) + lw a2, PT_R6(sp) + lw a3, PT_R7(sp) + jalr t2 - li t0, -EMAXERRNO - 1 # error? - sltu t0, t0, v0 - sw t0, PT_R7(sp) # set error flag - beqz t0, 1f + li t0, -EMAXERRNO - 1 # error? + sltu t0, t0, v0 + sw t0, PT_R7(sp) # set error flag + beqz t0, 1f - negu v0 # error - sw v0, PT_R0(sp) # set flag for syscall restarting -1: sw v0, PT_R2(sp) # result + negu v0 # error + sw v0, PT_R0(sp) # set flag for syscall + # restarting +1: sw v0, PT_R2(sp) # result -#error jal syscall_trace - j ret_from_sys_call + j syscall_exit /* ------------------------------------------------------------------------ */ @@ -139,7 +122,7 @@ stackargs: bltz t0, bad_stack # -> sp is bad lw t0, PT_R29(sp) # get old user stack pointer - la t1, 3f # copy 1 to 2 arguments + PTR_LA t1, 3f # copy 1 to 2 arguments sll t3, t3, 4 subu t1, t3 jr t1 @@ -153,6 +136,7 @@ stackargs: */ .set push .set noreorder + .set nomacro 1: lw t1, 20(t0) # argument #6 from usp nop sw t1, 20(sp) @@ -161,9 +145,9 @@ stackargs: nop sw t1, 16(sp) nop - .set pop +3: .set pop -3: j stack_done # go back + j stack_done # go back .section __ex_table,"a" PTR 1b,bad_stack @@ -180,7 +164,7 @@ bad_stack: sw v0, PT_R2(sp) li t0, 1 # set error flag sw t0, PT_R7(sp) - j ret_from_sys_call + j o32_syscall_exit /* * The system call does not exist in this kernel @@ -190,5 +174,147 @@ illegal_syscall: sw v0, PT_R2(sp) li t0, 1 # set error flag sw t0, PT_R7(sp) - j ret_from_sys_call + j o32_syscall_exit END(handle_sys) + + LEAF(mips_atomic_set) + andi v0, a1, 3 # must be word aligned + bnez v0, bad_alignment + + lw v1, TI_ADDR_LIMIT($28) # in legal address range? + addiu a0, a1, 4 + or a0, a0, a1 + and a0, a0, v1 + bltz a0, bad_address + +#ifdef CONFIG_CPU_HAS_LLSC + /* Ok, this is the ll/sc case. World is sane :-) */ +1: ll v0, (a1) + move a0, a2 +2: sc a0, (a1) + beqz a0, 1b + + .section __ex_table,"a" + PTR 1b, bad_stack + PTR 2b, bad_stack + .previous +#else + sw a1, 16(sp) + sw a2, 20(sp) + + move a0, sp + move a2, a1 + li a1, 1 + jal do_page_fault + + lw a1, 16(sp) + lw a2, 20(sp) + + /* + * At this point the page should be readable and writable unless + * there was no more memory available. + */ +1: lw v0, (a1) +2: sw a2, (a1) + + .section __ex_table,"a" + PTR 1b, no_mem + PTR 2b, no_mem + .previous +#endif + + sw v0, PT_R2(sp) # result +1: + + /* Success, so skip usual error handling garbage. */ + LONG_L t0, TI_FLAGS($28) # syscall tracing enabled? + bltz t0, 1f + b o32_syscall_exit + +1: SAVE_STATIC + jal do_syscall_trace + li a3, 0 # success + j syscall_exit + +no_mem: li v0, -ENOMEM + jr ra + +bad_address: + li v0, -EFAULT + jr ra + +bad_alignment: + li v0, -EINVAL + jr ra + END(mips_atomic_set) + + LEAF(sys_sysmips) + beq a0, MIPS_ATOMIC_SET, mips_atomic_set + j _sys_sysmips + END(sys_sysmips) + + LEAF(sys_syscall) + lw t0, PT_R29(sp) # user sp + + sltu v0, a0, __NR_Linux + __NR_Linux_syscalls + 1 + beqz v0, enosys + + sll v0, a0, 2 + la v1, sys_syscall + lw t2, sys_call_table(v0) # function pointer + lbu t4, sys_narg_table(a0) # number of arguments + + li v0, -EINVAL + beq t2, v1, out # do not recurse + + beqz t2, enosys # null function pointer? + + andi v0, t0, 0x3 # unaligned stack pointer? + bnez v0, sigsegv + + addu v0, t0, 16 # v0 = usp + 16 + addu t1, v0, 12 # 3 32-bit arguments + lw v1, TI_ADDR_LIMIT($28) + or v0, v0, t1 + and v1, v1, v0 + bltz v1, efault + + move a0, a1 # shift argument registers + move a1, a2 + move a2, a3 + +1: lw a3, 16(t0) +2: lw t3, 20(t0) +3: lw t4, 24(t0) + + .section __ex_table, "a" + .word 1b, efault + .word 2b, efault + .word 3b, efault + .previous + + sw t3, 16(sp) # put into new stackframe + sw t4, 20(sp) + + bnez t4, 1f # zero arguments? + addu a0, sp, 32 # then pass sp in a0 +1: + + sw t3, 16(sp) + sw v1, 20(sp) + jr t2 + /* Unreached */ + +enosys: li v0, -ENOSYS + b out + +sigsegv: + li a0, _SIGSEGV + move a1, $28 + jal force_sig + /* Fall through */ + +efault: li v0, -EFAULT + +out: jr ra + END(sys_syscall) diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c index 9d39a8658f27..809b1b14f3a3 100644 --- a/arch/mips/kernel/setup.c +++ b/arch/mips/kernel/setup.c @@ -4,9 +4,10 @@ * for more details. * * Copyright (C) 1995 Linus Torvalds - * Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000 Ralf Baechle + * Copyright (C) 1995 Waldorf Electronics + * Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001 Ralf Baechle * Copyright (C) 1996 Stoned Elipot - * Copyright (C) 2000 Maciej W. Rozycki + * Copyright (C) 2000, 2001, 2002 Maciej W. Rozycki */ #include <linux/config.h> #include <linux/errno.h> @@ -16,10 +17,10 @@ #include <linux/sched.h> #include <linux/kernel.h> #include <linux/mm.h> +#include <linux/module.h> #include <linux/stddef.h> #include <linux/string.h> #include <linux/unistd.h> -#include <linux/ptrace.h> #include <linux/slab.h> #include <linux/user.h> #include <linux/utsname.h> @@ -29,6 +30,8 @@ #include <linux/initrd.h> #include <linux/ide.h> #include <linux/timex.h> +#include <linux/major.h> +#include <linux/kdev_t.h> #include <linux/root_dev.h> #include <asm/asm.h> @@ -36,23 +39,11 @@ #include <asm/cachectl.h> #include <asm/cpu.h> #include <asm/io.h> -#include <asm/stackframe.h> +#include <asm/ptrace.h> +#include <asm/sections.h> #include <asm/system.h> -#ifdef CONFIG_SGI_IP22 -#include <asm/sgialib.h> -#endif - -struct mips_cpuinfo boot_cpu_data = { 0, NULL, NULL, 0 }; - -/* - * Not all of the MIPS CPUs have the "wait" instruction available. Moreover, - * the implementation of the "wait" feature differs between CPU families. This - * points to the function that implements CPU specific wait. - * The wait instruction stops the pipeline and reduces the power consumption of - * the CPU very much. - */ -void (*cpu_wait)(void) = NULL; +struct cpuinfo_mips cpu_data[NR_CPUS]; /* * There are several bus types available for MIPS machines. "RISC PC" @@ -61,7 +52,9 @@ void (*cpu_wait)(void) = NULL; * boxes ... * This flag is set if a EISA slots are available. */ +#ifdef CONFIG_EISA int EISA_bus = 0; +#endif struct screen_info screen_info; @@ -78,11 +71,6 @@ extern void * __rd_start, * __rd_end; extern struct rtc_ops no_rtc_ops; struct rtc_ops *rtc_ops; -#ifdef CONFIG_PC_KEYB -extern struct kbd_ops no_kbd_ops; -struct kbd_ops *kbd_ops; -#endif - /* * Setup information * @@ -94,333 +82,59 @@ unsigned long mips_machgroup = MACH_GROUP_UNKNOWN; struct boot_mem_map boot_mem_map; unsigned char aux_device_present; -extern char _ftext, _etext, _fdata, _edata, _end; -static char command_line[COMMAND_LINE_SIZE]; - char saved_command_line[COMMAND_LINE_SIZE]; -extern char arcs_cmdline[COMMAND_LINE_SIZE]; +static char command_line[CL_SIZE]; + char saved_command_line[CL_SIZE]; +extern char arcs_cmdline[CL_SIZE]; /* * mips_io_port_base is the begin of the address space to which x86 style * I/O ports are mapped. */ -unsigned long mips_io_port_base; +const unsigned long mips_io_port_base = -1; +EXPORT_SYMBOL(mips_io_port_base); + /* - * isa_slot_offset is the address where E(ISA) busaddress 0 is is mapped + * isa_slot_offset is the address where E(ISA) busaddress 0 is mapped * for the processor. */ unsigned long isa_slot_offset; +EXPORT_SYMBOL(isa_slot_offset); -extern void sgi_sysinit(void); extern void SetUpBootInfo(void); -extern void loadmmu(void); +extern void load_mmu(void); extern asmlinkage void start_kernel(void); extern void prom_init(int, char **, char **, int *); static struct resource code_resource = { "Kernel code" }; static struct resource data_resource = { "Kernel data" }; -/* - * Probe whether cpu has config register by trying to play with - * alternate cache bit and see whether it matters. - * It's used by cpu_probe to distinguish between R3000A and R3081. - */ -static inline int cpu_has_confreg(void) -{ -#ifdef CONFIG_CPU_R3000 - extern unsigned long r3k_cache_size(unsigned long); - unsigned long size1, size2; - unsigned long cfg = read_32bit_cp0_register(CP0_CONF); - - size1 = r3k_cache_size(ST0_ISC); - write_32bit_cp0_register(CP0_CONF, cfg^CONF_AC); - size2 = r3k_cache_size(ST0_ISC); - write_32bit_cp0_register(CP0_CONF, cfg); - return size1 != size2; -#else - return 0; -#endif -} - -/* declaration of the global struct */ -struct mips_cpu mips_cpu = {PRID_IMP_UNKNOWN, CPU_UNKNOWN, 0, 0, 0, - {0,0,0,0}, {0,0,0,0}, {0,0,0,0}, {0,0,0,0}}; - -/* Shortcut for assembler access to mips_cpu.options */ -int *cpuoptions = &mips_cpu.options; - -#define R4K_OPTS (MIPS_CPU_TLB | MIPS_CPU_4KEX | MIPS_CPU_4KTLB \ - | MIPS_CPU_COUNTER | MIPS_CPU_CACHE_CDEX) - -static inline void cpu_probe(void) -{ - -#ifdef CONFIG_CPU_MIPS32 - unsigned long config1; -#endif - - mips_cpu.processor_id = read_32bit_cp0_register(CP0_PRID); - switch (mips_cpu.processor_id & 0xff0000) { - case PRID_COMP_LEGACY: - switch (mips_cpu.processor_id & 0xff00) { - case PRID_IMP_R2000: - mips_cpu.cputype = CPU_R2000; - mips_cpu.isa_level = MIPS_CPU_ISA_I; - mips_cpu.options = MIPS_CPU_TLB; - mips_cpu.tlbsize = 64; - break; - case PRID_IMP_R3000: - if ((mips_cpu.processor_id & 0xff) == PRID_REV_R3000A) - if (cpu_has_confreg()) - mips_cpu.cputype = CPU_R3081E; - else - mips_cpu.cputype = CPU_R3000A; - else - mips_cpu.cputype = CPU_R3000; - mips_cpu.isa_level = MIPS_CPU_ISA_I; - mips_cpu.options = MIPS_CPU_TLB; - mips_cpu.tlbsize = 64; - break; - case PRID_IMP_R4000: - if ((mips_cpu.processor_id & 0xff) == PRID_REV_R4400) - mips_cpu.cputype = CPU_R4400SC; - else - mips_cpu.cputype = CPU_R4000SC; - mips_cpu.isa_level = MIPS_CPU_ISA_III; - mips_cpu.options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | - MIPS_CPU_WATCH | MIPS_CPU_VCE; - mips_cpu.tlbsize = 48; - break; - case PRID_IMP_VR41XX: - mips_cpu.cputype = CPU_VR41XX; - mips_cpu.isa_level = MIPS_CPU_ISA_III; - mips_cpu.options = R4K_OPTS; - mips_cpu.tlbsize = 32; - break; - case PRID_IMP_R4600: - mips_cpu.cputype = CPU_R4600; - mips_cpu.isa_level = MIPS_CPU_ISA_III; - mips_cpu.options = R4K_OPTS | MIPS_CPU_FPU; - mips_cpu.tlbsize = 48; - break; -/* - * This processor doesn't have an MMU, so it's not "real easy" to - * run Linux on it. It is left purely for documentation. - * case PRID_IMP_R4650: - mips_cpu.cputype = CPU_R4650; - mips_cpu.isa_level = MIPS_CPU_ISA_III; - mips_cpu.options = R4K_OPTS | MIPS_CPU_FPU; - mips_cpu.tlbsize = 48; - break; -*/ - case PRID_IMP_TX39: - mips_cpu.isa_level = MIPS_CPU_ISA_I; - mips_cpu.options = MIPS_CPU_TLB; - - switch (mips_cpu.processor_id & 0xff) { - case PRID_REV_TX3912: - mips_cpu.cputype = CPU_TX3912; - mips_cpu.tlbsize = 32; - break; - case PRID_REV_TX3922: - mips_cpu.cputype = CPU_TX3922; - mips_cpu.tlbsize = 64; - break; - case PRID_REV_TX3927: - mips_cpu.cputype = CPU_TX3927; - mips_cpu.tlbsize = 64; - break; - default: - mips_cpu.cputype = CPU_UNKNOWN; - break; - } - break; - case PRID_IMP_R4700: - mips_cpu.cputype = CPU_R4700; - mips_cpu.isa_level = MIPS_CPU_ISA_III; - mips_cpu.options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR; - mips_cpu.tlbsize = 48; - break; - case PRID_IMP_R5000: - mips_cpu.cputype = CPU_R5000; - mips_cpu.isa_level = MIPS_CPU_ISA_IV; - mips_cpu.options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR; - mips_cpu.tlbsize = 48; - break; - case PRID_IMP_R5432: - mips_cpu.cputype = CPU_R5432; - mips_cpu.isa_level = MIPS_CPU_ISA_IV; - mips_cpu.options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR; - mips_cpu.tlbsize = 48; - break; - case PRID_IMP_NEVADA: - mips_cpu.cputype = CPU_NEVADA; - mips_cpu.isa_level = MIPS_CPU_ISA_IV; - mips_cpu.options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | - MIPS_CPU_DIVEC; - mips_cpu.tlbsize = 48; - mips_cpu.icache.ways = 2; - mips_cpu.dcache.ways = 2; - break; - case PRID_IMP_R6000: - mips_cpu.cputype = CPU_R6000; - mips_cpu.isa_level = MIPS_CPU_ISA_II; - mips_cpu.options = MIPS_CPU_TLB | MIPS_CPU_FPU; - mips_cpu.tlbsize = 32; - break; - case PRID_IMP_R6000A: - mips_cpu.cputype = CPU_R6000A; - mips_cpu.isa_level = MIPS_CPU_ISA_II; - mips_cpu.options = MIPS_CPU_TLB | MIPS_CPU_FPU; - mips_cpu.tlbsize = 32; - break; - case PRID_IMP_RM7000: - mips_cpu.cputype = CPU_RM7000; - mips_cpu.isa_level = MIPS_CPU_ISA_IV; - mips_cpu.options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR; - break; - case PRID_IMP_R8000: - mips_cpu.cputype = CPU_R8000; - mips_cpu.isa_level = MIPS_CPU_ISA_IV; - mips_cpu.options = MIPS_CPU_TLB | MIPS_CPU_4KEX | - MIPS_CPU_FPU | MIPS_CPU_32FPR; - mips_cpu.tlbsize = 384; /* has weird TLB: 3-way x 128 */ - break; - case PRID_IMP_R10000: - mips_cpu.cputype = CPU_R10000; - mips_cpu.isa_level = MIPS_CPU_ISA_IV; - mips_cpu.options = MIPS_CPU_TLB | MIPS_CPU_4KEX | - MIPS_CPU_FPU | MIPS_CPU_32FPR | - MIPS_CPU_COUNTER | MIPS_CPU_WATCH; - mips_cpu.tlbsize = 64; - break; - default: - mips_cpu.cputype = CPU_UNKNOWN; - break; - } - break; -#ifdef CONFIG_CPU_MIPS32 - case PRID_COMP_MIPS: - switch (mips_cpu.processor_id & 0xff00) { - case PRID_IMP_4KC: - mips_cpu.cputype = CPU_4KC; - goto cpu_4kc; - case PRID_IMP_4KEC: - mips_cpu.cputype = CPU_4KEC; - goto cpu_4kc; - case PRID_IMP_4KSC: - mips_cpu.cputype = CPU_4KSC; -cpu_4kc: - /* Why do we set all these options by default, THEN query them?? */ - mips_cpu.cputype = MIPS_CPU_ISA_M32; - mips_cpu.options = MIPS_CPU_TLB | MIPS_CPU_4KEX | - MIPS_CPU_4KTLB | MIPS_CPU_COUNTER | - MIPS_CPU_DIVEC | MIPS_CPU_WATCH; - config1 = read_mips32_cp0_config1(); - if (config1 & (1 << 3)) - mips_cpu.options |= MIPS_CPU_WATCH; - if (config1 & (1 << 2)) - mips_cpu.options |= MIPS_CPU_MIPS16; - if (config1 & 1) - mips_cpu.options |= MIPS_CPU_FPU; - mips_cpu.scache.flags = MIPS_CACHE_NOT_PRESENT; - break; - case PRID_IMP_5KC: - mips_cpu.cputype = CPU_5KC; - mips_cpu.cputype = MIPS_CPU_ISA_M64; - /* See comment above about querying options */ - mips_cpu.options = MIPS_CPU_TLB | MIPS_CPU_4KEX | - MIPS_CPU_4KTLB | MIPS_CPU_COUNTER | - MIPS_CPU_DIVEC | MIPS_CPU_WATCH; - config1 = read_mips32_cp0_config1(); - if (config1 & (1 << 3)) - mips_cpu.options |= MIPS_CPU_WATCH; - if (config1 & (1 << 2)) - mips_cpu.options |= MIPS_CPU_MIPS16; - if (config1 & 1) - mips_cpu.options |= MIPS_CPU_FPU; - break; - mips_cpu.scache.flags = MIPS_CACHE_NOT_PRESENT; - default: - mips_cpu.cputype = CPU_UNKNOWN; - break; - } - break; -#endif - case PRID_COMP_ALCHEMY: - switch (mips_cpu.processor_id & 0xff00) { -#ifdef CONFIG_CPU_MIPS32 - case PRID_IMP_AU1000: - mips_cpu.cputype = CPU_AU1000; - mips_cpu.isa_level = MIPS_CPU_ISA_M32; - mips_cpu.options = MIPS_CPU_TLB | MIPS_CPU_4KEX | - MIPS_CPU_4KTLB | MIPS_CPU_COUNTER | - MIPS_CPU_DIVEC | MIPS_CPU_WATCH; - config1 = read_mips32_cp0_config1(); - if (config1 & (1 << 3)) - mips_cpu.options |= MIPS_CPU_WATCH; - if (config1 & (1 << 2)) - mips_cpu.options |= MIPS_CPU_MIPS16; - if (config1 & 1) - mips_cpu.options |= MIPS_CPU_FPU; - mips_cpu.scache.flags = MIPS_CACHE_NOT_PRESENT; - break; -#endif - default: - mips_cpu.cputype = CPU_UNKNOWN; - break; - } - break; - case PRID_COMP_SIBYTE: - switch (mips_cpu.processor_id & 0xff00) { - case PRID_IMP_SB1: - mips_cpu.cputype = CPU_SB1; - mips_cpu.options = MIPS_CPU_TLB | MIPS_CPU_4KEX | - MIPS_CPU_COUNTER | MIPS_CPU_DIVEC | MIPS_CPU_FPU | - MIPS_CPU_VCE; - break; - default: - mips_cpu.cputype = CPU_UNKNOWN; - break; - } - break; - default: - mips_cpu.cputype = CPU_UNKNOWN; - } -} - asmlinkage void __init init_arch(int argc, char **argv, char **envp, int *prom_vec) { - unsigned int s; - /* Determine which MIPS variant we are running on. */ cpu_probe(); prom_init(argc, argv, envp, prom_vec); -#ifdef CONFIG_SGI_IP22 - sgi_sysinit(); -#endif + cpu_report(); /* * Determine the mmu/cache attached to this machine, * then flush the tlb and caches. On the r4xx0 * variants this also sets CP0_WIRED to zero. */ - loadmmu(); + load_mmu(); - /* Disable coprocessors and set FPU for 16 FPRs */ - s = read_32bit_cp0_register(CP0_STATUS); - s &= ~(ST0_CU1|ST0_CU2|ST0_CU3|ST0_KX|ST0_SX|ST0_FR); - s |= ST0_CU0; - write_32bit_cp0_register(CP0_STATUS, s); + /* Disable coprocessors and set FPU for 16/32 FPR register model */ + clear_c0_status(ST0_CU1|ST0_CU2|ST0_CU3|ST0_KX|ST0_SX|ST0_FR); + set_c0_status(ST0_CU0); start_kernel(); } -void __init add_memory_region(unsigned long start, unsigned long size, +void __init add_memory_region(phys_t start, phys_t size, long type) { int x = boot_mem_map.nr_map; @@ -441,8 +155,10 @@ static void __init print_memory_map(void) int i; for (i = 0; i < boot_mem_map.nr_map; i++) { - printk(" memory: %08lx @ %08lx ", - boot_mem_map.map[i].size, boot_mem_map.map[i].addr); + printk(" memory: %08Lx @ %08Lx ", + (u64) boot_mem_map.map[i].size, + (u64) boot_mem_map.map[i].addr); + switch (boot_mem_map.map[i].type) { case BOOT_MEM_RAM: printk("(usable)\n"); @@ -460,7 +176,7 @@ static void __init print_memory_map(void) } } -static inline void parse_mem_cmdline(void) +static inline void parse_cmdline_early(void) { char c = ' ', *to = command_line, *from = saved_command_line; unsigned long start_at, mem_size; @@ -499,7 +215,7 @@ static inline void parse_mem_cmdline(void) c = *(from++); if (!c) break; - if (COMMAND_LINE_SIZE <= ++len) + if (CL_SIZE <= ++len) break; *(to++) = c; } @@ -511,143 +227,41 @@ static inline void parse_mem_cmdline(void) } } -void __init setup_arch(char **cmdline_p) -{ - void atlas_setup(void); - void baget_setup(void); - void ddb_setup(void); - void decstation_setup(void); - void deskstation_setup(void); - void jazz_setup(void); - void sni_rm200_pci_setup(void); - void sgi_setup(void); - void ev96100_setup(void); - void malta_setup(void); - void momenco_ocelot_setup(void); - void nino_setup(void); - - unsigned long bootmap_size; - unsigned long start_pfn, max_pfn, first_usable_pfn; - - int i; -#ifdef CONFIG_BLK_DEV_FD - fd_ops = &no_fd_ops; -#endif +#define PFN_UP(x) (((x) + PAGE_SIZE - 1) >> PAGE_SHIFT) +#define PFN_DOWN(x) ((x) >> PAGE_SHIFT) +#define PFN_PHYS(x) ((x) << PAGE_SHIFT) -#ifdef CONFIG_BLK_DEV_IDE - ide_ops = &no_ide_ops; -#endif +#define MAXMEM HIGHMEM_START +#define MAXMEM_PFN PFN_DOWN(MAXMEM) -#ifdef CONFIG_PC_KEYB - kbd_ops = &no_kbd_ops; +static inline void bootmem_init(void) +{ +#ifdef CONFIG_BLK_DEV_INITRD + unsigned long tmp; + unsigned long *initrd_header; #endif - - rtc_ops = &no_rtc_ops; + unsigned long bootmap_size; + unsigned long start_pfn, max_low_pfn, first_usable_pfn; + int i; - switch(mips_machgroup) - { -#ifdef CONFIG_BAGET_MIPS - case MACH_GROUP_BAGET: - baget_setup(); - break; -#endif -#ifdef CONFIG_DECSTATION - case MACH_GROUP_DEC: - decstation_setup(); - break; -#endif -#ifdef CONFIG_MIPS_ATLAS - case MACH_GROUP_UNKNOWN: - atlas_setup(); - break; -#endif -#ifdef CONFIG_MIPS_JAZZ - case MACH_GROUP_JAZZ: - jazz_setup(); - break; -#endif -#ifdef CONFIG_MIPS_MALTA - case MACH_GROUP_UNKNOWN: - malta_setup(); - break; -#endif -#ifdef CONFIG_MOMENCO_OCELOT - case MACH_GROUP_MOMENCO: - momenco_ocelot_setup(); - break; -#endif -#ifdef CONFIG_SGI_IP22 - /* As of now this is only IP22. */ - case MACH_GROUP_SGI: - sgi_setup(); - break; -#endif -#ifdef CONFIG_SNI_RM200_PCI - case MACH_GROUP_SNI_RM: - sni_rm200_pci_setup(); - break; -#endif -#ifdef CONFIG_DDB5074 - case MACH_GROUP_NEC_DDB: - ddb_setup(); - break; -#endif -#ifdef CONFIG_DDB5476 - case MACH_GROUP_NEC_DDB: - ddb_setup(); - break; -#endif -#ifdef CONFIG_DDB5477 - case MACH_GROUP_NEC_DDB: - ddb_setup(); - break; -#endif -#ifdef CONFIG_MIPS_EV96100 - case MACH_GROUP_GALILEO: - ev96100_setup(); - break; -#endif -#ifdef CONFIG_MIPS_EV64120 - case MACH_GROUP_GALILEO: - ev64120_setup(); - break; -#endif -#if defined(CONFIG_MIPS_IVR) || defined(CONFIG_MIPS_ITE8172) - case MACH_GROUP_ITE: - case MACH_GROUP_GLOBESPAN: - it8172_setup(); - break; -#endif -#ifdef CONFIG_NINO - case MACH_GROUP_PHILIPS: - nino_setup(); - break; -#endif -#ifdef CONFIG_MIPS_PB1000 - case MACH_GROUP_ALCHEMY: - au1000_setup(); - break; -#endif - default: - panic("Unsupported architecture"); +#ifdef CONFIG_BLK_DEV_INITRD + tmp = (((unsigned long)&_end + PAGE_SIZE-1) & PAGE_MASK) - 8; + if (tmp < (unsigned long)&_end) + tmp += PAGE_SIZE; + initrd_header = (unsigned long *)tmp; + if (initrd_header[0] == 0x494E5244) { + initrd_start = (unsigned long)&initrd_header[2]; + initrd_end = initrd_start + initrd_header[1]; } - - strlcpy(command_line, arcs_cmdline, sizeof command_line); - strlcpy(saved_command_line, command_line, sizeof saved_command_line); - *cmdline_p = command_line; - - parse_mem_cmdline(); - -#define PFN_UP(x) (((x) + PAGE_SIZE - 1) >> PAGE_SHIFT) -#define PFN_DOWN(x) ((x) >> PAGE_SHIFT) -#define PFN_PHYS(x) ((x) << PAGE_SHIFT) - + start_pfn = PFN_UP(__pa((&_end)+(initrd_end - initrd_start) + PAGE_SIZE)); +#else /* * Partially used pages are not usable - thus * we are rounding upwards. */ start_pfn = PFN_UP(__pa(&_end)); +#endif /* CONFIG_BLK_DEV_INITRD */ /* Find the highest page frame number we have available. */ max_pfn = 0; @@ -674,9 +288,36 @@ void __init setup_arch(char **cmdline_p) } } } - - /* Initialize the boot-time allocator. */ - bootmap_size = init_bootmem(first_usable_pfn, max_pfn); + + /* + * Determine low and high memory ranges + */ + max_low_pfn = max_pfn; + if (max_low_pfn > MAXMEM_PFN) { + max_low_pfn = MAXMEM_PFN; +#ifndef CONFIG_HIGHMEM + /* Maximum memory usable is what is directly addressable */ + printk(KERN_WARNING "Warning only %dMB will be used.\n", + MAXMEM>>20); + printk(KERN_WARNING "Use a HIGHMEM enabled kernel.\n"); +#endif + } + +#ifdef CONFIG_HIGHMEM + /* + * Crude, we really should make a better attempt at detecting + * highstart_pfn + */ + highstart_pfn = highend_pfn = max_pfn; + if (max_pfn > MAXMEM_PFN) { + highstart_pfn = MAXMEM_PFN; + printk(KERN_NOTICE "%ldMB HIGHMEM available.\n", + (highend_pfn - highstart_pfn) >> (20 - PAGE_SHIFT)); + } +#endif + + /* Initialize the boot-time allocator with low memory only. */ + bootmap_size = init_bootmem(first_usable_pfn, max_low_pfn); /* * Register fully available low RAM pages with the bootmem allocator. @@ -694,7 +335,7 @@ void __init setup_arch(char **cmdline_p) * We are rounding up the start address of usable memory: */ curr_pfn = PFN_UP(boot_mem_map.map[i].addr); - if (curr_pfn >= max_pfn) + if (curr_pfn >= max_low_pfn) continue; if (curr_pfn < start_pfn) curr_pfn = start_pfn; @@ -705,8 +346,19 @@ void __init setup_arch(char **cmdline_p) last_pfn = PFN_DOWN(boot_mem_map.map[i].addr + boot_mem_map.map[i].size); - if (last_pfn > max_pfn) - last_pfn = max_pfn; + if (last_pfn > max_low_pfn) + last_pfn = max_low_pfn; + + /* + * Only register lowmem part of lowmem segment with bootmem. + */ + size = last_pfn - curr_pfn; + if (curr_pfn > PFN_DOWN(HIGHMEM_START)) + continue; + if (curr_pfn + size - 1 > PFN_DOWN(HIGHMEM_START)) + size = PFN_DOWN(HIGHMEM_START) - curr_pfn; + if (!size) + continue; /* * ... finally, did all the rounding and playing @@ -715,7 +367,7 @@ void __init setup_arch(char **cmdline_p) if (last_pfn <= curr_pfn) continue; - size = last_pfn - curr_pfn; + /* Register lowmem ranges */ free_bootmem(PFN_PHYS(curr_pfn), PFN_PHYS(size)); } @@ -725,38 +377,49 @@ void __init setup_arch(char **cmdline_p) #ifdef CONFIG_BLK_DEV_INITRD /* Board specific code should have set up initrd_start and initrd_end */ ROOT_DEV = Root_RAM0; - if( __rd_start != __rd_end ) { + if (&__rd_start != &__rd_end) { initrd_start = (unsigned long)&__rd_start; initrd_end = (unsigned long)&__rd_end; } initrd_below_start_ok = 1; if (initrd_start) { - unsigned long initrd_size = ((unsigned char *)initrd_end) - ((unsigned char *)initrd_start); + unsigned long initrd_size = ((unsigned char *)initrd_end) - ((unsigned char *)initrd_start); printk("Initial ramdisk at: 0x%p (%lu bytes)\n", - (void *)initrd_start, + (void *)initrd_start, initrd_size); - if ((void *)initrd_end > phys_to_virt(PFN_PHYS(max_low_pfn))) { + if (PHYSADDR(initrd_end) > PFN_PHYS(max_low_pfn)) { printk("initrd extends beyond end of memory " - "(0x%lx > 0x%p)\ndisabling initrd\n", - initrd_end, - phys_to_virt(PFN_PHYS(max_low_pfn))); + "(0x%08lx > 0x%08lx)\ndisabling initrd\n", + PHYSADDR(initrd_end), + PFN_PHYS(max_low_pfn)); initrd_start = initrd_end = 0; - } + } } #endif /* CONFIG_BLK_DEV_INITRD */ +} - paging_init(); +static inline void resource_init(void) +{ + int i; - code_resource.start = virt_to_bus(&_ftext); - code_resource.end = virt_to_bus(&_etext) - 1; - data_resource.start = virt_to_bus(&_fdata); - data_resource.end = virt_to_bus(&_edata) - 1; + code_resource.start = virt_to_phys(&_text); + code_resource.end = virt_to_phys(&_etext) - 1; + data_resource.start = virt_to_phys(&_etext); + data_resource.end = virt_to_phys(&_edata) - 1; /* * Request address space for all standard RAM. */ for (i = 0; i < boot_mem_map.nr_map; i++) { struct resource *res; + unsigned long start, end; + + start = boot_mem_map.map[i].addr; + end = boot_mem_map.map[i].addr + boot_mem_map.map[i].size - 1; + if (start >= MAXMEM) + continue; + if (end >= MAXMEM) + end = MAXMEM - 1; res = alloc_bootmem(sizeof(struct resource)); switch (boot_mem_map.map[i].type) { @@ -768,8 +431,10 @@ void __init setup_arch(char **cmdline_p) default: res->name = "reserved"; } - res->start = boot_mem_map.map[i].addr; - res->end = res->start + boot_mem_map.map[i].size - 1; + + res->start = start; + res->end = end; + res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; request_resource(&iomem_resource, res); @@ -783,15 +448,244 @@ void __init setup_arch(char **cmdline_p) } } -void r3081_wait(void) +#undef PFN_UP +#undef PFN_DOWN +#undef PFN_PHYS + +#undef MAXMEM +#undef MAXMEM_PFN + + +void __init setup_arch(char **cmdline_p) { - unsigned long cfg = read_32bit_cp0_register(CP0_CONF); - write_32bit_cp0_register(CP0_CONF, cfg|CONF_HALT); + void atlas_setup(void); + void baget_setup(void); + void cobalt_setup(void); + void lasat_setup(void); + void ddb_setup(void); + void decstation_setup(void); + void deskstation_setup(void); + void jazz_setup(void); + void sni_rm200_pci_setup(void); + void ip22_setup(void); + void ev96100_setup(void); + void malta_setup(void); + void sead_setup(void); + void ikos_setup(void); + void momenco_ocelot_setup(void); + void momenco_ocelot_g_setup(void); + void momenco_ocelot_c_setup(void); + void nec_osprey_setup(void); + void nec_eagle_setup(void); + void zao_capcella_setup(void); + void victor_mpc30x_setup(void); + void ibm_workpad_setup(void); + void casio_e55_setup(void); + void jmr3927_setup(void); + void it8172_setup(void); + void swarm_setup(void); + void hp_setup(void); + void au1x00_setup(void); + void frame_info_init(void); + + frame_info_init(); + +#ifdef CONFIG_BLK_DEV_FD + fd_ops = &no_fd_ops; +#endif + +#ifdef CONFIG_BLK_DEV_IDE + ide_ops = &no_ide_ops; +#endif + + rtc_ops = &no_rtc_ops; + + switch(mips_machgroup) + { +#ifdef CONFIG_BAGET_MIPS + case MACH_GROUP_BAGET: + baget_setup(); + break; +#endif +#ifdef CONFIG_MIPS_COBALT + case MACH_GROUP_COBALT: + cobalt_setup(); + break; +#endif +#ifdef CONFIG_DECSTATION + case MACH_GROUP_DEC: + decstation_setup(); + break; +#endif +#ifdef CONFIG_MIPS_ATLAS + case MACH_GROUP_UNKNOWN: + atlas_setup(); + break; +#endif +#ifdef CONFIG_MIPS_JAZZ + case MACH_GROUP_JAZZ: + jazz_setup(); + break; +#endif +#ifdef CONFIG_MIPS_MALTA + case MACH_GROUP_UNKNOWN: + malta_setup(); + break; +#endif +#ifdef CONFIG_MOMENCO_OCELOT + case MACH_GROUP_MOMENCO: + momenco_ocelot_setup(); + break; +#endif +#ifdef CONFIG_MOMENCO_OCELOT_G + case MACH_GROUP_MOMENCO: + momenco_ocelot_g_setup(); + break; +#endif +#ifdef CONFIG_MOMENCO_OCELOT_C + case MACH_GROUP_MOMENCO: + momenco_ocelot_c_setup(); + break; +#endif +#ifdef CONFIG_MIPS_SEAD + case MACH_GROUP_UNKNOWN: + sead_setup(); + break; +#endif +#ifdef CONFIG_SGI_IP22 + /* As of now this is only IP22. */ + case MACH_GROUP_SGI: + ip22_setup(); + break; +#endif +#ifdef CONFIG_SNI_RM200_PCI + case MACH_GROUP_SNI_RM: + sni_rm200_pci_setup(); + break; +#endif +#ifdef CONFIG_DDB5074 + case MACH_GROUP_NEC_DDB: + ddb_setup(); + break; +#endif +#ifdef CONFIG_DDB5476 + case MACH_GROUP_NEC_DDB: + ddb_setup(); + break; +#endif +#ifdef CONFIG_DDB5477 + case MACH_GROUP_NEC_DDB: + ddb_setup(); + break; +#endif +#ifdef CONFIG_CPU_VR41XX + case MACH_GROUP_NEC_VR41XX: + switch (mips_machtype) { +#ifdef CONFIG_NEC_OSPREY + case MACH_NEC_OSPREY: + nec_osprey_setup(); + break; +#endif +#ifdef CONFIG_NEC_EAGLE + case MACH_NEC_EAGLE: + nec_eagle_setup(); + break; +#endif +#ifdef CONFIG_ZAO_CAPCELLA + case MACH_ZAO_CAPCELLA: + zao_capcella_setup(); + break; +#endif +#ifdef CONFIG_VICTOR_MPC30X + case MACH_VICTOR_MPC30X: + victor_mpc30x_setup(); + break; +#endif +#ifdef CONFIG_IBM_WORKPAD + case MACH_IBM_WORKPAD: + ibm_workpad_setup(); + break; +#endif +#ifdef CONFIG_CASIO_E55 + case MACH_CASIO_E55: + casio_e55_setup(); + break; +#endif +#ifdef CONFIG_TANBAC_TB0229 + case MACH_TANBAC_TB0229: + tanbac_tb0229_setup(); + break; +#endif + } + break; +#endif +#ifdef CONFIG_MIPS_EV96100 + case MACH_GROUP_GALILEO: + ev96100_setup(); + break; +#endif +#ifdef CONFIG_MIPS_EV64120 + case MACH_GROUP_GALILEO: + ev64120_setup(); + break; +#endif +#if defined(CONFIG_MIPS_IVR) || defined(CONFIG_MIPS_ITE8172) + case MACH_GROUP_ITE: + case MACH_GROUP_GLOBESPAN: + it8172_setup(); + break; +#endif +#ifdef CONFIG_LASAT + case MACH_GROUP_LASAT: + lasat_setup(); + break; +#endif +#ifdef CONFIG_SOC_AU1X00 + case MACH_GROUP_ALCHEMY: + au1x00_setup(); + break; +#endif +#ifdef CONFIG_TOSHIBA_JMR3927 + case MACH_GROUP_TOSHIBA: + jmr3927_setup(); + break; +#endif +#ifdef CONFIG_TOSHIBA_RBTX4927 + case MACH_GROUP_TOSHIBA: + tx4927_setup(); + break; +#endif +#ifdef CONFIG_SIBYTE_BOARD + case MACH_GROUP_SIBYTE: + swarm_setup(); + break; +#endif +#ifdef CONFIG_HP_LASERJET + case MACH_GROUP_HP_LJ: + hp_setup(); + break; +#endif + default: + panic("Unsupported architecture"); + } + + strlcpy(command_line, arcs_cmdline, sizeof command_line); + strlcpy(saved_command_line, command_line, sizeof saved_command_line); + *cmdline_p = command_line; + + parse_cmdline_early(); + + bootmem_init(); + + paging_init(); + + resource_init(); } -void r4k_wait(void) +int __init fpu_disable(char *s) { - __asm__(".set\tmips3\n\t" - "wait\n\t" - ".set\tmips0"); + cpu_data[0].options &= ~MIPS_CPU_FPU; + + return 1; } +__setup("nofpu", fpu_disable); diff --git a/arch/mips/kernel/signal.c b/arch/mips/kernel/signal.c index 63342be2b43a..ef9ba8509425 100644 --- a/arch/mips/kernel/signal.c +++ b/arch/mips/kernel/signal.c @@ -10,19 +10,22 @@ #include <linux/config.h> #include <linux/sched.h> #include <linux/mm.h> +#include <linux/personality.h> #include <linux/smp.h> #include <linux/smp_lock.h> #include <linux/kernel.h> #include <linux/signal.h> #include <linux/errno.h> #include <linux/wait.h> -#include <linux/ptrace.h> #include <linux/unistd.h> #include <asm/asm.h> #include <asm/bitops.h> -#include <asm/pgalloc.h> -#include <asm/stackframe.h> +#include <asm/cacheflush.h> +#include <asm/cpu.h> +#include <asm/fpu.h> +#include <asm/offset.h> +#include <asm/ptrace.h> #include <asm/uaccess.h> #include <asm/ucontext.h> @@ -32,17 +35,13 @@ extern asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs); -extern asmlinkage int (*save_fp_context)(struct sigcontext *sc); -extern asmlinkage int (*restore_fp_context)(struct sigcontext *sc); - -extern asmlinkage void syscall_trace(void); +extern asmlinkage void do_syscall_trace(void); /* * Atomically swap in the new signal mask, and wait for a signal. */ save_static_function(sys_sigsuspend); -static_unused int -_sys_sigsuspend(struct pt_regs regs) +static_unused int _sys_sigsuspend(struct pt_regs regs) { sigset_t *uset, saveset, newset; @@ -51,11 +50,11 @@ _sys_sigsuspend(struct pt_regs regs) return -EFAULT; sigdelsetmask(&newset, ~_BLOCKABLE); - spin_lock_irq(¤t->sigmask_lock); + spin_lock_irq(¤t->sighand->siglock); saveset = current->blocked; current->blocked = newset; recalc_sigpending(); - spin_unlock_irq(¤t->sigmask_lock); + spin_unlock_irq(¤t->sighand->siglock); regs.regs[2] = EINTR; regs.regs[7] = 1; @@ -67,10 +66,8 @@ _sys_sigsuspend(struct pt_regs regs) } } - save_static_function(sys_rt_sigsuspend); -static_unused int -_sys_rt_sigsuspend(struct pt_regs regs) +static_unused int _sys_rt_sigsuspend(struct pt_regs regs) { sigset_t *unewset, saveset, newset; size_t sigsetsize; @@ -85,11 +82,11 @@ _sys_rt_sigsuspend(struct pt_regs regs) return -EFAULT; sigdelsetmask(&newset, ~_BLOCKABLE); - spin_lock_irq(¤t->sigmask_lock); + spin_lock_irq(¤t->sighand->siglock); saveset = current->blocked; current->blocked = newset; recalc_sigpending(); - spin_unlock_irq(¤t->sigmask_lock); + spin_unlock_irq(¤t->sighand->siglock); regs.regs[2] = EINTR; regs.regs[7] = 1; @@ -101,8 +98,8 @@ _sys_rt_sigsuspend(struct pt_regs regs) } } -asmlinkage int -sys_sigaction(int sig, const struct sigaction *act, struct sigaction *oact) +asmlinkage int sys_sigaction(int sig, const struct sigaction *act, + struct sigaction *oact) { struct k_sigaction new_ka, old_ka; int ret; @@ -116,7 +113,6 @@ sys_sigaction(int sig, const struct sigaction *act, struct sigaction *oact) err |= __get_user(new_ka.sa.sa_handler, &act->sa_handler); err |= __get_user(new_ka.sa.sa_flags, &act->sa_flags); err |= __get_user(mask, &act->sa_mask.sig[0]); - err |= __get_user(new_ka.sa.sa_restorer, &act->sa_restorer); if (err) return -EFAULT; @@ -134,7 +130,6 @@ sys_sigaction(int sig, const struct sigaction *act, struct sigaction *oact) err |= __put_user(0, &oact->sa_mask.sig[1]); err |= __put_user(0, &oact->sa_mask.sig[2]); err |= __put_user(0, &oact->sa_mask.sig[3]); - err |= __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer); if (err) return -EFAULT; } @@ -142,8 +137,7 @@ sys_sigaction(int sig, const struct sigaction *act, struct sigaction *oact) return ret; } -asmlinkage int -sys_sigaltstack(struct pt_regs regs) +asmlinkage int sys_sigaltstack(struct pt_regs regs) { const stack_t *uss = (const stack_t *) regs.regs[4]; stack_t *uoss = (stack_t *) regs.regs[5]; @@ -152,10 +146,8 @@ sys_sigaltstack(struct pt_regs regs) return do_sigaltstack(uss, uoss, usp); } -asmlinkage int -restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc) +static int restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc) { - int owned_fp; int err = 0; u64 reg; @@ -183,10 +175,15 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc) restore_gp_reg(31); #undef restore_gp_reg - err |= __get_user(owned_fp, &sc->sc_ownedfp); - if (owned_fp) { + err |= __get_user(current->used_math, &sc->sc_used_math); + + if (current->used_math) { + /* restore fpu context if we have used it before */ + own_fpu(); err |= restore_fp_context(sc); - last_task_used_math = current; + } else { + /* signal handler may have used FPU. Give it up. */ + loose_fpu(); } return err; @@ -206,8 +203,7 @@ struct rt_sigframe { struct ucontext rs_uc; }; -asmlinkage void -sys_sigreturn(struct pt_regs regs) +asmlinkage void sys_sigreturn(struct pt_regs regs) { struct sigframe *frame; sigset_t blocked; @@ -219,10 +215,10 @@ sys_sigreturn(struct pt_regs regs) goto badframe; sigdelsetmask(&blocked, ~_BLOCKABLE); - spin_lock_irq(¤t->sigmask_lock); + spin_lock_irq(¤t->sighand->siglock); current->blocked = blocked; recalc_sigpending(); - spin_unlock_irq(¤t->sigmask_lock); + spin_unlock_irq(¤t->sighand->siglock); if (restore_sigcontext(®s, &frame->sf_sc)) goto badframe; @@ -230,11 +226,11 @@ sys_sigreturn(struct pt_regs regs) /* * Don't let your children do this ... */ - if (current->ptrace & PT_TRACESYS) - syscall_trace(); + if (current_thread_info()->flags & TIF_SYSCALL_TRACE) + do_syscall_trace(); __asm__ __volatile__( "move\t$29, %0\n\t" - "j\tret_from_sys_call" + "j\tsyscall_exit" :/* no outputs */ :"r" (®s)); /* Unreached */ @@ -243,8 +239,7 @@ badframe: force_sig(SIGSEGV, current); } -asmlinkage void -sys_rt_sigreturn(struct pt_regs regs) +asmlinkage void sys_rt_sigreturn(struct pt_regs regs) { struct rt_sigframe *frame; sigset_t set; @@ -257,10 +252,10 @@ sys_rt_sigreturn(struct pt_regs regs) goto badframe; sigdelsetmask(&set, ~_BLOCKABLE); - spin_lock_irq(¤t->sigmask_lock); + spin_lock_irq(¤t->sighand->siglock); current->blocked = set; recalc_sigpending(); - spin_unlock_irq(¤t->sigmask_lock); + spin_unlock_irq(¤t->sighand->siglock); if (restore_sigcontext(®s, &frame->rs_uc.uc_mcontext)) goto badframe; @@ -276,7 +271,7 @@ sys_rt_sigreturn(struct pt_regs regs) */ __asm__ __volatile__( "move\t$29, %0\n\t" - "j\tret_from_sys_call" + "j\tsyscall_exit" :/* no outputs */ :"r" (®s)); /* Unreached */ @@ -285,21 +280,20 @@ badframe: force_sig(SIGSEGV, current); } -static inline int -setup_sigcontext(struct pt_regs *regs, struct sigcontext *sc) +static inline int setup_sigcontext(struct pt_regs *regs, struct sigcontext *sc) { - int owned_fp; int err = 0; u64 reg; - err |= __put_user(regs->cp0_epc, &sc->sc_pc); + reg = regs->cp0_epc; err |= __put_user(reg, &sc->sc_pc); err |= __put_user(regs->cp0_status, &sc->sc_status); #define save_gp_reg(i) { \ reg = regs->regs[i]; \ err |= __put_user(reg, &sc->sc_regs[i]); \ } while(0) - __put_user(0, &sc->sc_regs[0]); save_gp_reg(1); save_gp_reg(2); + reg = 0; err |= __put_user(reg, &sc->sc_regs[0]); + save_gp_reg(1); save_gp_reg(2); save_gp_reg(3); save_gp_reg(4); save_gp_reg(5); save_gp_reg(6); save_gp_reg(7); save_gp_reg(8); save_gp_reg(9); save_gp_reg(10); save_gp_reg(11); save_gp_reg(12); save_gp_reg(13); save_gp_reg(14); @@ -310,36 +304,48 @@ setup_sigcontext(struct pt_regs *regs, struct sigcontext *sc) save_gp_reg(31); #undef save_gp_reg - err |= __put_user(regs->hi, &sc->sc_mdhi); - err |= __put_user(regs->lo, &sc->sc_mdlo); + reg = regs->hi; err |= __put_user(reg, &sc->sc_mdhi); + reg = regs->lo; err |= __put_user(reg, &sc->sc_mdlo); err |= __put_user(regs->cp0_cause, &sc->sc_cause); err |= __put_user(regs->cp0_badvaddr, &sc->sc_badvaddr); - owned_fp = (current == last_task_used_math); - err |= __put_user(owned_fp, &sc->sc_ownedfp); + err |= __put_user(current->used_math, &sc->sc_used_math); + + if (!current->used_math) + goto out; - if (current->used_math) { /* fp is active. */ - set_cp0_status(ST0_CU1); - err |= save_fp_context(sc); - last_task_used_math = NULL; - regs->cp0_status &= ~ST0_CU1; - current->used_math = 0; + /* + * Save FPU state to signal context. Signal handler will "inherit" + * current FPU state. + */ + if (!is_fpu_owner()) { + own_fpu(); + restore_fp(current); } + err |= save_fp_context(sc); +out: return err; } /* * Determine which stack to use.. */ -static inline void * -get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size) +static inline void * get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, + size_t frame_size) { unsigned long sp; /* Default to using normal stack */ sp = regs->regs[29]; + /* + * FPU emulator may have it's own trampoline active just + * above the user stack, 16-bytes before the next lowest + * 16 byte boundary. Try to avoid trashing it. + */ + sp -= 32; + /* This is the X/Open sanctioned signal stack switching. */ if ((ka->sa.sa_flags & SA_ONSTACK) && ! on_sig_stack(sp)) sp = current->sas_ss_sp + current->sas_ss_size; @@ -347,9 +353,8 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size) return (void *)((sp - frame_size) & ALMASK); } -static void inline -setup_frame(struct k_sigaction * ka, struct pt_regs *regs, - int signr, sigset_t *set) +static void inline setup_frame(struct k_sigaction * ka, struct pt_regs *regs, + int signr, sigset_t *set) { struct sigframe *frame; int err = 0; @@ -358,23 +363,15 @@ setup_frame(struct k_sigaction * ka, struct pt_regs *regs, if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame))) goto give_sigsegv; - /* Set up to return from userspace. If provided, use a stub already - in userspace. */ - if (ka->sa.sa_flags & SA_RESTORER) - regs->regs[31] = (unsigned long) ka->sa.sa_restorer; - else { - /* - * Set up the return code ... - * - * li v0, __NR_sigreturn - * syscall - */ - err |= __put_user(0x24020000 + __NR_sigreturn, - frame->sf_code + 0); - err |= __put_user(0x0000000c , - frame->sf_code + 1); - flush_cache_sigtramp((unsigned long) frame->sf_code); - } + /* + * Set up the return code ... + * + * li v0, __NR_sigreturn + * syscall + */ + err |= __put_user(0x24020000 + __NR_sigreturn, frame->sf_code + 0); + err |= __put_user(0x0000000c , frame->sf_code + 1); + flush_cache_sigtramp((unsigned long) frame->sf_code); err |= setup_sigcontext(regs, &frame->sf_sc); err |= __copy_to_user(&frame->sf_mask, set, sizeof(*set)); @@ -399,8 +396,9 @@ setup_frame(struct k_sigaction * ka, struct pt_regs *regs, regs->cp0_epc = regs->regs[25] = (unsigned long) ka->sa.sa_handler; #if DEBUG_SIG - printk("SIG deliver (%s:%d): sp=0x%p pc=0x%p ra=0x%p\n", - current->comm, current->pid, frame, regs->cp0_epc, frame->code); + printk("SIG deliver (%s:%d): sp=0x%p pc=0x%lx ra=0x%p\n", + current->comm, current->pid, + frame, regs->cp0_epc, frame->sf_code); #endif return; @@ -410,9 +408,8 @@ give_sigsegv: force_sig(SIGSEGV, current); } -static void inline -setup_rt_frame(struct k_sigaction * ka, struct pt_regs *regs, - int signr, sigset_t *set, siginfo_t *info) +static void inline setup_rt_frame(struct k_sigaction * ka, struct pt_regs *regs, + int signr, sigset_t *set, siginfo_t *info) { struct rt_sigframe *frame; int err = 0; @@ -421,23 +418,15 @@ setup_rt_frame(struct k_sigaction * ka, struct pt_regs *regs, if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame))) goto give_sigsegv; - /* Set up to return from userspace. If provided, use a stub already - in userspace. */ - if (ka->sa.sa_flags & SA_RESTORER) - regs->regs[31] = (unsigned long) ka->sa.sa_restorer; - else { - /* - * Set up the return code ... - * - * li v0, __NR_rt_sigreturn - * syscall - */ - err |= __put_user(0x24020000 + __NR_rt_sigreturn, - frame->rs_code + 0); - err |= __put_user(0x0000000c , - frame->rs_code + 1); - flush_cache_sigtramp((unsigned long) frame->rs_code); - } + /* + * Set up the return code ... + * + * li v0, __NR_rt_sigreturn + * syscall + */ + err |= __put_user(0x24020000 + __NR_rt_sigreturn, frame->rs_code + 0); + err |= __put_user(0x0000000c , frame->rs_code + 1); + flush_cache_sigtramp((unsigned long) frame->rs_code); /* Create siginfo. */ err |= copy_siginfo_to_user(&frame->rs_info, info); @@ -475,8 +464,9 @@ setup_rt_frame(struct k_sigaction * ka, struct pt_regs *regs, regs->cp0_epc = regs->regs[25] = (unsigned long) ka->sa.sa_handler; #if DEBUG_SIG - printk("SIG deliver (%s:%d): sp=0x%p pc=0x%p ra=0x%p\n", - current->comm, current->pid, frame, regs->cp0_epc, frame->code); + printk("SIG deliver (%s:%d): sp=0x%p pc=0x%lx ra=0x%p\n", + current->comm, current->pid, + frame, regs->cp0_epc, frame->rs_code); #endif return; @@ -486,31 +476,11 @@ give_sigsegv: force_sig(SIGSEGV, current); } -static inline void -handle_signal(unsigned long sig, siginfo_t *info, sigset_t *oldset, - struct pt_regs * regs) +static inline void handle_signal(unsigned long sig, siginfo_t *info, + sigset_t *oldset, struct pt_regs * regs) { - struct k_sigaction *ka = ¤t->sig->action[sig-1]; + struct k_sigaction *ka = ¤t->sighand->action[sig-1]; - if (ka->sa.sa_flags & SA_SIGINFO) - setup_rt_frame(ka, regs, sig, oldset, info); - else - setup_frame(ka, regs, sig, oldset); - - if (ka->sa.sa_flags & SA_ONESHOT) - ka->sa.sa_handler = SIG_DFL; - if (!(ka->sa.sa_flags & SA_NODEFER)) { - spin_lock_irq(¤t->sigmask_lock); - sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); - sigaddset(¤t->blocked,sig); - recalc_sigpending(); - spin_unlock_irq(¤t->sigmask_lock); - } -} - -static inline void -syscall_restart(struct pt_regs *regs, struct k_sigaction *ka) -{ switch(regs->regs[0]) { case ERESTARTNOHAND: regs->regs[2] = EINTR; @@ -527,27 +497,33 @@ syscall_restart(struct pt_regs *regs, struct k_sigaction *ka) } regs->regs[0] = 0; /* Don't deal with this again. */ -} -extern int do_irix_signal(sigset_t *oldset, struct pt_regs *regs); + if (ka->sa.sa_flags & SA_SIGINFO) + setup_rt_frame(ka, regs, sig, oldset, info); + else + setup_frame(ka, regs, sig, oldset); + + if (ka->sa.sa_flags & SA_ONESHOT) + ka->sa.sa_handler = SIG_DFL; + if (!(ka->sa.sa_flags & SA_NODEFER)) { + spin_lock_irq(¤t->sighand->siglock); + sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + sigaddset(¤t->blocked,sig); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + } +} asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs) { siginfo_t info; - int sig_nr; - -#ifdef CONFIG_BINFMT_IRIX - if (current->personality != PER_LINUX) - return do_irix_signal(oldset, regs); -#endif + int signr; if (!oldset) oldset = ¤t->blocked; signr = get_signal_to_deliver(&info, regs, NULL); if (signr > 0) { - if (regs->regs[0]) - syscall_restart(regs, ka); /* Whee! Actually deliver the signal. */ handle_signal(signr, &info, oldset, regs); return 1; @@ -568,3 +544,24 @@ asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs) } return 0; } + +extern int do_irix_signal(sigset_t *oldset, struct pt_regs *regs); + +/* + * notification of userspace execution resumption + * - triggered by current->work.notify_resume + */ +asmlinkage void do_notify_resume(struct pt_regs *regs, sigset_t *oldset, + __u32 thread_info_flags) +{ + /* deal with pending signal delivery */ + if (thread_info_flags & _TIF_SIGPENDING) { +#ifdef CONFIG_BINFMT_IRIX + if (unlikely(current->personality != PER_LINUX)) { + do_irix_signal(oldset, regs); + return; + } +#endif + do_signal(oldset, regs); + } +} diff --git a/arch/mips/kernel/smp.c b/arch/mips/kernel/smp.c index 36e55ea4d57e..5532635e2da0 100644 --- a/arch/mips/kernel/smp.c +++ b/arch/mips/kernel/smp.c @@ -1,11 +1,4 @@ /* - * - * arch/mips/kernel/smp.c - * - * Copyright (C) 2000 Sibyte - * - * Written by Justin Carlson (carlson@sibyte.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 @@ -15,229 +8,256 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - */ - - + * Copyright (C) 2000, 2001 Kanoj Sarcar + * Copyright (C) 2000, 2001 Ralf Baechle + * Copyright (C) 2000, 2001 Silicon Graphics, Inc. + * Copyright (C) 2000, 2001 Broadcom Corporation + */ #include <linux/config.h> +#include <linux/cache.h> +#include <linux/delay.h> #include <linux/init.h> +#include <linux/interrupt.h> #include <linux/spinlock.h> #include <linux/threads.h> +#include <linux/module.h> #include <linux/time.h> #include <linux/timex.h> #include <linux/sched.h> -#include <linux/interrupt.h> -#include <linux/cache.h> #include <asm/atomic.h> +#include <asm/cpu.h> #include <asm/processor.h> #include <asm/system.h> #include <asm/hardirq.h> #include <asm/mmu_context.h> -#include <asm/delay.h> #include <asm/smp.h> -/* - * This was written with the BRCM12500 MP SOC in mind, but tries to - * be generic. It's modelled on the mips64 smp.c code, which is - * derived from Sparc, I'm guessing, which is derived from... - * - * It's probably horribly designed for very large ccNUMA systems - * as it doesn't take any node clustering into account. -*/ - - -/* Ze Big Kernel Lock! */ -int smp_threads_ready; /* Not used */ -int smp_num_cpus; -int global_irq_holder = NO_PROC_ID; -spinlock_t global_irq_lock = SPIN_LOCK_UNLOCKED; -struct mips_cpuinfo cpu_data[NR_CPUS]; +int smp_threads_ready; /* Not used */ -struct smp_fn_call_struct smp_fn_call = -{ SPIN_LOCK_UNLOCKED, ATOMIC_INIT(0), NULL, NULL}; - -static atomic_t cpus_booted = ATOMIC_INIT(0); +// static atomic_t cpus_booted = ATOMIC_INIT(0); +atomic_t cpus_booted = ATOMIC_INIT(0); +cpumask_t phys_cpu_present_map; /* Bitmask of physically CPUs */ +cpumask_t cpu_online_map; /* Bitmask of currently online CPUs */ +int __cpu_number_map[NR_CPUS]; +int __cpu_logical_map[NR_CPUS]; /* These are defined by the board-specific code. */ -/* Cause the function described by smp_fn_call - to be executed on the passed cpu. When the function - has finished, increment the finished field of - smp_fn_call. */ - -void core_call_function(int cpu); +/* + * Cause the function described by call_data to be executed on the passed + * cpu. When the function has finished, increment the finished field of + * call_data. + */ +void core_send_ipi(int cpu, unsigned int action); /* * Clear all undefined state in the cpu, set up sp and gp to the passed - * values, and kick the cpu into smp_bootstrap(); + * values, and kick the cpu into smp_bootstrap(); */ void prom_boot_secondary(int cpu, unsigned long sp, unsigned long gp); /* * After we've done initial boot, this function is called to allow the - * board code to clean up state, if needed + * board code to clean up state, if needed */ - void prom_init_secondary(void); +void prom_smp_finish(void); -void cpu_idle(void); - -/* Do whatever setup needs to be done for SMP at the board level. Return - the number of cpus in the system, including this one */ -int prom_setup_smp(void); - -int start_secondary(void *unused) -{ - prom_init_secondary(); - write_32bit_cp0_register(CP0_CONTEXT, smp_processor_id()<<23); - current_pgd[smp_processor_id()] = init_mm.pgd; - printk("Slave cpu booted successfully\n"); - atomic_inc(&cpus_booted); - cpu_idle(); - return 0; -} +cycles_t cacheflush_time; +unsigned long cache_decay_ticks; -void __init smp_boot_cpus(void) +void smp_tune_scheduling (void) { - int i; - - smp_num_cpus = prom_setup_smp(); - init_new_context(current, &init_mm); - current->processor = 0; - atomic_set(&cpus_booted, 1); /* Master CPU is already booted... */ - init_idle(); - for (i = 1; i < smp_num_cpus; i++) { - struct task_struct *p; - struct pt_regs regs; - printk("Starting CPU %d... ", i); - - /* Spawn a new process normally. Grab a pointer to - its task struct so we can mess with it */ - p = do_fork(CLONE_VM|CLONE_IDLETASK, 0, ®s, 0); - - /* Schedule the first task manually */ - p->processor = i; - p->cpus_runnable = 1 << i; /* we schedule the first task manually */ - - /* Attach to the address space of init_task. */ - atomic_inc(&init_mm.mm_count); - p->active_mm = &init_mm; - init_tasks[i] = p; - - del_from_runqueue(p); - unhash_process(p); - - prom_boot_secondary(i, - (unsigned long)p + KERNEL_STACK_SIZE - 32, - (unsigned long)p); + struct cache_desc *cd = ¤t_cpu_data.scache; + unsigned long cachesize; /* kB */ + unsigned long bandwidth = 350; /* MB/s */ + unsigned long cpu_khz; -#if 0 - - /* This is copied from the ip-27 code in the mips64 tree */ - - struct task_struct *p; + /* + * Crude estimate until we actually meassure ... + */ + cpu_khz = loops_per_jiffy * 2 * HZ / 1000; + /* + * Rough estimation for SMP scheduling, this is the number of + * cycles it takes for a fully memory-limited process to flush + * the SMP-local cache. + * + * (For a P5 this pretty much means we will choose another idle + * CPU almost always at wakeup time (this is due to the small + * L1 cache), on PIIs it's around 50-100 usecs, depending on + * the cache size) + */ + if (!cpu_khz) { /* - * The following code is purely to make sure - * Linux can schedule processes on this slave. + * This basically disables processor-affinity scheduling on SMP + * without a cycle counter. Currently all SMP capable MIPS + * processors have a cycle counter. */ - kernel_thread(0, NULL, CLONE_IDLETASK); - p = prev_task(&init_task); - sprintf(p->comm, "%s%d", "Idle", i); - init_tasks[i] = p; - p->processor = i; - p->cpus_runnable = 1 << i; /* we schedule the first task manually */ - del_from_runqueue(p); - unhash_process(p); - /* Attach to the address space of init_task. */ - atomic_inc(&init_mm.mm_count); - p->active_mm = &init_mm; - prom_boot_secondary(i, - (unsigned long)p + KERNEL_STACK_SIZE - 32, - (unsigned long)p); -#endif + cacheflush_time = 0; + return; } - /* Wait for everyone to come up */ - while (atomic_read(&cpus_booted) != smp_num_cpus); + cachesize = cd->linesz * cd->sets * cd->ways; + cacheflush_time = (cpu_khz>>10) * (cachesize<<10) / bandwidth; + cache_decay_ticks = (long)cacheflush_time/cpu_khz * HZ / 1000; + + printk("per-CPU timeslice cutoff: %ld.%02ld usecs.\n", + (long)cacheflush_time/(cpu_khz/1000), + ((long)cacheflush_time*100/(cpu_khz/1000)) % 100); + printk("task migration cache decay timeout: %ld msecs.\n", + (cache_decay_ticks + 1) * 1000 / HZ); } -void __init smp_commence(void) +void __init smp_callin(void) { - /* Not sure what to do here yet */ +#if 0 + calibrate_delay(); + smp_store_cpu_info(cpuid); +#endif } -static void reschedule_this_cpu(void *dummy) +#ifndef CONFIG_SGI_IP27 +/* + * Hook for doing final board-specific setup after the generic smp setup + * is done + */ +asmlinkage void start_secondary(void) { - current->work.need_resched = 1; + unsigned int cpu = smp_processor_id(); + + cpu_probe(); + prom_init_secondary(); + per_cpu_trap_init(); + + /* + * XXX parity protection should be folded in here when it's converted + * to an option instead of something based on .cputype + */ + pgd_current[cpu] = init_mm.pgd; + cpu_data[cpu].udelay_val = loops_per_jiffy; + prom_smp_finish(); + printk("Slave cpu booted successfully\n"); + CPUMASK_SETB(cpu_online_map, cpu); + atomic_inc(&cpus_booted); + cpu_idle(); } +#endif /* CONFIG_SGI_IP27 */ -void FASTCALL(smp_send_reschedule(int cpu)) +/* + * this function sends a 'reschedule' IPI to another CPU. + * it goes straight through and wastes no time serializing + * anything. Worst case is that we lose a reschedule ... + */ +void smp_send_reschedule(int cpu) { - smp_call_function(reschedule_this_cpu, NULL, 0, 0); + core_send_ipi(cpu, SMP_RESCHEDULE_YOURSELF); } +static spinlock_t call_lock = SPIN_LOCK_UNLOCKED; + +struct call_data_struct *call_data; /* - * The caller of this wants the passed function to run on every cpu. If wait - * is set, wait until all cpus have finished the function before returning. - * The lock is here to protect the call structure. + * Run a function on all other CPUs. + * <func> The function to run. This must be fast and non-blocking. + * <info> An arbitrary pointer to pass to the function. + * <retry> If true, keep retrying until ready. + * <wait> If true, wait until function has completed on other CPUs. + * [RETURNS] 0 on success, else a negative status code. + * + * Does not return until remote CPUs are nearly ready to execute <func> + * or are or have executed. + * * You must not call this function with disabled interrupts or from a * hardware interrupt handler or from a bottom half handler. */ -int smp_call_function (void (*func) (void *info), void *info, int retry, +int smp_call_function (void (*func) (void *info), void *info, int retry, int wait) { - int cpus = smp_num_cpus - 1; - int i; + struct call_data_struct data; + int i, cpus = num_online_cpus() - 1; + int cpu = smp_processor_id(); - if (smp_num_cpus < 2) { + if (!cpus) return 0; - } - spin_lock(&smp_fn_call.lock); + data.func = func; + data.info = info; + atomic_set(&data.started, 0); + data.wait = wait; + if (wait) + atomic_set(&data.finished, 0); - atomic_set(&smp_fn_call.finished, 0); - smp_fn_call.fn = func; - smp_fn_call.data = info; + spin_lock(&call_lock); + call_data = &data; - for (i = 0; i < smp_num_cpus; i++) { - if (i != smp_processor_id()) { - /* Call the board specific routine */ - core_call_function(i); - } - } + /* Send a message to all other CPUs and wait for them to respond */ + for (i = 0; i < NR_CPUS; i++) + if (cpu_online(cpu) && cpu != smp_processor_id()) + core_send_ipi(i, SMP_CALL_FUNCTION); - if (wait) { - while(atomic_read(&smp_fn_call.finished) != cpus) {} - } + /* Wait for response */ + /* FIXME: lock-up detection, backtrace on lock-up */ + while (atomic_read(&data.started) != cpus) + barrier(); + + if (wait) + while (atomic_read(&data.finished) != cpus) + barrier(); + spin_unlock(&call_lock); - spin_unlock(&smp_fn_call.lock); return 0; } -void synchronize_irq(void) +void smp_call_function_interrupt(void) { - panic("synchronize_irq"); + void (*func) (void *info) = call_data->func; + void *info = call_data->info; + int wait = call_data->wait; + + irq_enter(); + /* + * Notify initiating CPU that I've grabbed the data and am + * about to execute the function. + */ + mb(); + atomic_inc(&call_data->started); + + /* + * At this point the info structure may be out of scope unless wait==1. + */ + irq_enter(); + (*func)(info); + irq_exit(); + + if (wait) { + mb(); + atomic_inc(&call_data->finished); + } } static void stop_this_cpu(void *dummy) { - printk("Cpu stopping\n"); - for (;;); + /* + * Remove this CPU: + */ + clear_bit(smp_processor_id(), &cpu_online_map); + local_irq_enable(); /* May need to service _machine_restart IPI */ + for (;;); /* Wait if available. */ } void smp_send_stop(void) { smp_call_function(stop_this_cpu, NULL, 1, 0); - smp_num_cpus = 1; } /* Not really SMP stuff ... */ @@ -246,157 +266,142 @@ int setup_profiling_timer(unsigned int multiplier) return 0; } +static void flush_tlb_all_ipi(void *info) +{ + local_flush_tlb_all(); +} -/* - * Most of this code is take from the mips64 tree (ip27-irq.c). It's virtually - * identical to the i386 implentation in arh/i386/irq.c, with translations for - * the interrupt enable bit - */ - -#define MAXCOUNT 100000000 -#define SYNC_OTHER_CORES(x) udelay(x+1) +void flush_tlb_all(void) +{ + on_each_cpu(flush_tlb_all_ipi, 0, 1, 1); +} -static inline void wait_on_irq(int cpu) +static void flush_tlb_mm_ipi(void *mm) { - int count = MAXCOUNT; + local_flush_tlb_mm((struct mm_struct *)mm); +} - for (;;) { +/* + * The following tlb flush calls are invoked when old translations are + * being torn down, or pte attributes are changing. For single threaded + * address spaces, a new context is obtained on the current cpu, and tlb + * context on other cpus are invalidated to force a new context allocation + * at switch_mm time, should the mm ever be used on other cpus. For + * multithreaded address spaces, intercpu interrupts have to be sent. + * Another case where intercpu interrupts are required is when the target + * mm might be active on another cpu (eg debuggers doing the flushes on + * behalf of debugees, kswapd stealing pages from another process etc). + * Kanoj 07/00. + */ - /* - * Wait until all interrupts are gone. Wait - * for bottom half handlers unless we're - * already executing in one.. - */ - if (!irqs_running()) - if (local_bh_count(cpu) || !spin_is_locked(&global_bh_lock)) - break; - - /* Duh, we have to loop. Release the lock to avoid deadlocks */ - spin_unlock(&global_irq_lock); - - for (;;) { - if (!--count) { - printk("Count spun out. Huh?\n"); - count = ~0; - } - local_irq_enable(); - SYNC_OTHER_CORES(cpu); - local_irq_disable(); - if (irqs_running()) - continue; - if (spin_is_locked(&global_irq_lock)) - continue; - if (!local_bh_count(cpu) && spin_is_locked(&global_bh_lock)) - continue; - if (spin_trylock(&global_irq_lock)) - break; - } +void flush_tlb_mm(struct mm_struct *mm) +{ + preempt_disable(); + + if ((atomic_read(&mm->mm_users) != 1) || (current->mm != mm)) { + smp_call_function(flush_tlb_mm_ipi, (void *)mm, 1, 1); + } else { + int i; + for (i = 0; i < num_online_cpus(); i++) + if (smp_processor_id() != i) + cpu_context(i, mm) = 0; } + local_flush_tlb_mm(mm); + + preempt_enable(); } +struct flush_tlb_data { + struct vm_area_struct *vma; + unsigned long addr1; + unsigned long addr2; +}; -static inline void get_irqlock(int cpu) +static void flush_tlb_range_ipi(void *info) { - if (!spin_trylock(&global_irq_lock)) { - /* do we already hold the lock? */ - if ((unsigned char) cpu == global_irq_holder) - return; - /* Uhhuh.. Somebody else got it. Wait.. */ - spin_lock(&global_irq_lock); - } - /* - * We also to make sure that nobody else is running - * in an interrupt context. - */ - wait_on_irq(cpu); + struct flush_tlb_data *fd = (struct flush_tlb_data *)info; - /* - * Ok, finally.. - */ - global_irq_holder = cpu; + local_flush_tlb_range(fd->vma, fd->addr1, fd->addr2); } - -/* - * A global "cli()" while in an interrupt context - * turns into just a local cli(). Interrupts - * should use spinlocks for the (very unlikely) - * case that they ever want to protect against - * each other. - * - * If we already have local interrupts disabled, - * this will not turn a local disable into a - * global one (problems with spinlocks: this makes - * save_flags+cli+sti usable inside a spinlock). - */ -void __global_cli(void) +void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end) { - unsigned int flags; - - local_save_flags(flags); - if (flags & ST0_IE) { - int cpu = smp_processor_id(); - local_irq_disable(); - if (!local_irq_count(cpu)) - get_irqlock(cpu); + struct mm_struct *mm = vma->vm_mm; + + preempt_disable(); + if ((atomic_read(&mm->mm_users) != 1) || (current->mm != mm)) { + struct flush_tlb_data fd; + + fd.vma = vma; + fd.addr1 = start; + fd.addr2 = end; + smp_call_function(flush_tlb_range_ipi, (void *)&fd, 1, 1); + } else { + int i; + for (i = 0; i < num_online_cpus(); i++) + if (smp_processor_id() != i) + cpu_context(i, mm) = 0; } + local_flush_tlb_range(vma, start, end); + preempt_enable(); } -void __global_sti(void) +static void flush_tlb_kernel_range_ipi(void *info) { - int cpu = smp_processor_id(); + struct flush_tlb_data *fd = (struct flush_tlb_data *)info; - if (!local_irq_count(cpu)) - release_irqlock(cpu); - local_irq_enable(); + local_flush_tlb_kernel_range(fd->addr1, fd->addr2); } -/* - * SMP flags value to restore to: - * 0 - global cli - * 1 - global sti - * 2 - local cli - * 3 - local sti - */ -unsigned long __global_save_flags(void) +void flush_tlb_kernel_range(unsigned long start, unsigned long end) { - int retval; - int local_enabled; - unsigned long flags; - int cpu = smp_processor_id(); + struct flush_tlb_data fd; - local_save_flags(flags); - local_enabled = (flags & ST0_IE); - /* default to local */ - retval = 2 + local_enabled; - - /* check for global flags if we're not in an interrupt */ - if (!local_irq_count(cpu)) { - if (local_enabled) - retval = 1; - if (global_irq_holder == cpu) - retval = 0; - } + fd.addr1 = start; + fd.addr2 = end; + on_each_cpu(flush_tlb_kernel_range_ipi, (void *)&fd, 1, 1); +} - return retval; +static void flush_tlb_page_ipi(void *info) +{ + struct flush_tlb_data *fd = (struct flush_tlb_data *)info; + + local_flush_tlb_page(fd->vma, fd->addr1); } -void __global_restore_flags(unsigned long flags) +void flush_tlb_page(struct vm_area_struct *vma, unsigned long page) { - switch (flags) { - case 0: - __global_cli(); - break; - case 1: - __global_sti(); - break; - case 2: - local_irq_disable(); - break; - case 3: - local_irq_enable(); - break; - default: - printk("global_restore_flags: %08lx\n", flags); + preempt_disable(); + if ((atomic_read(&vma->vm_mm->mm_users) != 1) || (current->mm != vma->vm_mm)) { + struct flush_tlb_data fd; + + fd.vma = vma; + fd.addr1 = page; + smp_call_function(flush_tlb_page_ipi, (void *)&fd, 1, 1); + } else { + int i; + for (i = 0; i < num_online_cpus(); i++) + if (smp_processor_id() != i) + cpu_context(i, vma->vm_mm) = 0; } + local_flush_tlb_page(vma, page); + preempt_enable(); } + +static void flush_tlb_one_ipi(void *info) +{ + unsigned long vaddr = (unsigned long) info; + + local_flush_tlb_one(vaddr); +} + +void flush_tlb_one(unsigned long vaddr) +{ + smp_call_function(flush_tlb_one_ipi, (void *) vaddr, 1, 1); + local_flush_tlb_one(vaddr); +} + +EXPORT_SYMBOL(flush_tlb_page); +EXPORT_SYMBOL(flush_tlb_one); +EXPORT_SYMBOL(cpu_data); +EXPORT_SYMBOL(synchronize_irq); diff --git a/arch/mips/kernel/syscall.c b/arch/mips/kernel/syscall.c index afbce4dc94a9..f6ca25bc6483 100644 --- a/arch/mips/kernel/syscall.c +++ b/arch/mips/kernel/syscall.c @@ -14,6 +14,7 @@ #undef CONF_DEBUG_IRIX #include <linux/config.h> +#include <linux/compiler.h> #include <linux/linkage.h> #include <linux/mm.h> #include <linux/smp.h> @@ -28,7 +29,7 @@ #include <asm/offset.h> #include <asm/ptrace.h> #include <asm/signal.h> -#include <asm/stackframe.h> +#include <asm/shmparam.h> #include <asm/uaccess.h> extern asmlinkage void syscall_trace(void); @@ -54,6 +55,61 @@ out: return res; } +unsigned long shm_align_mask = PAGE_SIZE - 1; /* Sane caches */ + +#define COLOUR_ALIGN(addr,pgoff) \ + ((((addr) + shm_align_mask) & ~shm_align_mask) + \ + (((pgoff) << PAGE_SHIFT) & shm_align_mask)) + +unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, + unsigned long len, unsigned long pgoff, unsigned long flags) +{ + struct vm_area_struct * vmm; + int do_color_align; + + if (flags & MAP_FIXED) { + /* + * We do not accept a shared mapping if it would violate + * cache aliasing constraints. + */ + if ((flags & MAP_SHARED) && (addr & shm_align_mask)) + return -EINVAL; + return addr; + } + + if (len > TASK_SIZE) + return -ENOMEM; + do_color_align = 0; + if (filp || (flags & MAP_SHARED)) + do_color_align = 1; + if (addr) { + if (do_color_align) + addr = COLOUR_ALIGN(addr, pgoff); + else + addr = PAGE_ALIGN(addr); + vmm = find_vma(current->mm, addr); + if (TASK_SIZE - len >= addr && + (!vmm || addr + len <= vmm->vm_start)) + return addr; + } + addr = TASK_UNMAPPED_BASE; + if (do_color_align) + addr = COLOUR_ALIGN(addr, pgoff); + else + addr = PAGE_ALIGN(addr); + + for (vmm = find_vma(current->mm, addr); ; vmm = vmm->vm_next) { + /* At this point: (!vmm || addr < vmm->vm_end). */ + if (TASK_SIZE - len < addr) + return -ENOMEM; + if (!vmm || addr + len <= vmm->vm_start) + return addr; + addr = vmm->vm_end; + if (do_color_align) + addr = COLOUR_ALIGN(addr, pgoff); + } +} + /* common code for old and new mmaps */ static inline long do_mmap2(unsigned long addr, unsigned long len, unsigned long prot, @@ -82,7 +138,16 @@ out: asmlinkage unsigned long old_mmap(unsigned long addr, size_t len, int prot, int flags, int fd, off_t offset) { - return do_mmap2(addr, len, prot, flags, fd, offset >> PAGE_SHIFT); + int result; + + result = -EINVAL; + if (offset & ~PAGE_MASK) + goto out; + + result = do_mmap2(addr, len, prot, flags, fd, offset >> PAGE_SHIFT); + +out: + return result; } asmlinkage long @@ -95,10 +160,7 @@ sys_mmap2(unsigned long addr, unsigned long len, unsigned long prot, save_static_function(sys_fork); static_unused int _sys_fork(struct pt_regs regs) { - struct task_struct *p; - - p = do_fork(SIGCHLD, regs.regs[29], ®s, 0); - return IS_ERR(p) ? PTR_ERR(p) : p->pid; + return do_fork(SIGCHLD, regs.regs[29], ®s, 0, NULL, NULL); } @@ -107,14 +169,16 @@ static_unused int _sys_clone(struct pt_regs regs) { unsigned long clone_flags; unsigned long newsp; - struct task_struct *p; + int *parent_tidptr, *child_tidptr; clone_flags = regs.regs[4]; newsp = regs.regs[5]; if (!newsp) newsp = regs.regs[29]; - p = do_fork(clone_flags & ~CLONE_IDLETASK, newsp, ®s, 0); - return IS_ERR(p) ? PTR_ERR(p) : p->pid; + parent_tidptr = (int *) regs.regs[6]; + child_tidptr = (int *) regs.regs[7]; + return do_fork(clone_flags & ~CLONE_IDLETASK, newsp, ®s, 0, + parent_tidptr, child_tidptr); } /* @@ -158,7 +222,7 @@ asmlinkage int sys_olduname(struct oldold_utsname * name) return -EFAULT; if (!access_ok(VERIFY_WRITE,name,sizeof(struct oldold_utsname))) return -EFAULT; - + 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); @@ -175,76 +239,6 @@ asmlinkage int sys_olduname(struct oldold_utsname * name) } /* - * Do the indirect syscall syscall. - * Don't care about kernel locking; the actual syscall will do it. - * - * XXX This is borken. - */ -asmlinkage int sys_syscall(struct pt_regs regs) -{ - syscall_t syscall; - unsigned long syscallnr = regs.regs[4]; - unsigned long a0, a1, a2, a3, a4, a5, a6; - int nargs, errno; - - if (syscallnr > __NR_Linux + __NR_Linux_syscalls) - return -ENOSYS; - - syscall = sys_call_table[syscallnr]; - nargs = sys_narg_table[syscallnr]; - /* - * Prevent stack overflow by recursive - * syscall(__NR_syscall, __NR_syscall,...); - */ - if (syscall == (syscall_t) sys_syscall) { - return -EINVAL; - } - - if (syscall == NULL) { - return -ENOSYS; - } - - if(nargs > 3) { - unsigned long usp = regs.regs[29]; - unsigned long *sp = (unsigned long *) usp; - if(usp & 3) { - printk("unaligned usp -EFAULT\n"); - force_sig(SIGSEGV, current); - return -EFAULT; - } - errno = verify_area(VERIFY_READ, (void *) (usp + 16), - (nargs - 3) * sizeof(unsigned long)); - if(errno) { - return -EFAULT; - } - switch(nargs) { - case 7: - a3 = sp[4]; a4 = sp[5]; a5 = sp[6]; a6 = sp[7]; - break; - case 6: - a3 = sp[4]; a4 = sp[5]; a5 = sp[6]; a6 = 0; - break; - case 5: - a3 = sp[4]; a4 = sp[5]; a5 = a6 = 0; - break; - case 4: - a3 = sp[4]; a4 = a5 = a6 = 0; - break; - - default: - a3 = a4 = a5 = a6 = 0; - break; - } - } else { - a3 = a4 = a5 = a6 = 0; - } - a0 = regs.regs[5]; a1 = regs.regs[6]; a2 = regs.regs[7]; - if(nargs == 0) - a0 = (unsigned long) ®s; - return syscall((void *)a0, a1, a2, a3, a4, a5, a6); -} - -/* * If we ever come here the user sp is bad. Zap the process right away. * Due to the bad stack signaling wouldn't work. * XXX kernel locking??? diff --git a/arch/mips/kernel/syscalls.h b/arch/mips/kernel/syscalls.h index 953186802c1b..7b52a1b197a5 100644 --- a/arch/mips/kernel/syscalls.h +++ b/arch/mips/kernel/syscalls.h @@ -3,7 +3,7 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 1995, 96, 97, 98, 99, 2000 by Ralf Baechle + * Copyright (C) 1995, 96, 97, 98, 99, 2000, 2001, 2002 by Ralf Baechle */ /* @@ -20,7 +20,7 @@ SYS(sys_fork, 0) SYS(sys_read, 3) SYS(sys_write, 3) SYS(sys_open, 3) /* 4005 */ -SYS(sys_close, 3) +SYS(sys_close, 1) SYS(sys_waitpid, 3) SYS(sys_creat, 2) SYS(sys_link, 2) @@ -32,7 +32,7 @@ SYS(sys_mknod, 3) SYS(sys_chmod, 2) /* 4015 */ SYS(sys_lchown, 3) SYS(sys_ni_syscall, 0) -SYS(sys_stat, 2) +SYS(sys_ni_syscall, 0) /* was sys_stat */ SYS(sys_lseek, 3) SYS(sys_getpid, 0) /* 4020 */ SYS(sys_mount, 5) @@ -42,7 +42,7 @@ SYS(sys_getuid, 0) SYS(sys_stime, 1) /* 4025 */ SYS(sys_ptrace, 4) SYS(sys_alarm, 1) -SYS(sys_fstat, 2) +SYS(sys_ni_syscall, 0) /* was sys_fstat */ SYS(sys_pause, 0) SYS(sys_utime, 2) /* 4030 */ SYS(sys_ni_syscall, 0) @@ -96,9 +96,9 @@ SYS(sys_gettimeofday, 2) SYS(sys_settimeofday, 2) SYS(sys_getgroups, 2) /* 4080 */ SYS(sys_setgroups, 2) -SYS(sys_ni_syscall, 0) /* old_select */ +SYS(sys_ni_syscall, 0) /* old_select */ SYS(sys_symlink, 2) -SYS(sys_lstat, 2) +SYS(sys_ni_syscall, 0) /* was sys_lstat */ SYS(sys_readlink, 3) /* 4085 */ SYS(sys_uselib, 1) SYS(sys_swapon, 2) @@ -115,7 +115,7 @@ SYS(sys_setpriority, 3) SYS(sys_ni_syscall, 0) SYS(sys_statfs, 2) SYS(sys_fstatfs, 2) /* 4100 */ -SYS(sys_ioperm, 3) +SYS(sys_ni_syscall, 0) /* was ioperm(2) */ SYS(sys_socketcall, 2) SYS(sys_syslog, 3) SYS(sys_setitimer, 3) @@ -124,10 +124,10 @@ SYS(sys_newstat, 2) SYS(sys_newlstat, 2) SYS(sys_newfstat, 2) SYS(sys_uname, 1) -SYS(sys_iopl, 0) /* Well, actually 17 args ... */ /* 4110 */ +SYS(sys_ni_syscall, 0) /* 4110 was iopl(2) */ SYS(sys_vhangup, 0) -SYS(sys_ni_syscall, 0) /* was sys_idle() */ -SYS(sys_vm86, 1) +SYS(sys_ni_syscall, 0) /* was sys_idle() */ +SYS(sys_ni_syscall, 0) /* was sys_vm86 */ SYS(sys_wait4, 4) SYS(sys_swapoff, 1) /* 4115 */ SYS(sys_sysinfo, 1) @@ -141,10 +141,10 @@ SYS(sys_ni_syscall, 0) /* sys_modify_ldt */ SYS(sys_adjtimex, 1) SYS(sys_mprotect, 3) /* 4125 */ SYS(sys_sigprocmask, 3) -SYS(sys_create_module, 2) +SYS(sys_ni_syscall, 0) /* was create_module */ SYS(sys_init_module, 5) SYS(sys_delete_module, 1) -SYS(sys_get_kernel_syms, 1) /* 4130 */ +SYS(sys_ni_syscall, 0) /* 4130, was get_kernel_syms */ SYS(sys_quotactl, 0) SYS(sys_getpgid, 1) SYS(sys_fchdir, 1) @@ -201,7 +201,7 @@ SYS(sys_socket, 3) SYS(sys_socketpair, 4) SYS(sys_setresuid, 3) /* 4185 */ SYS(sys_getresuid, 3) -SYS(sys_query_module, 5) +SYS(sys_ni_syscall, 0) /* sys_query_module */ SYS(sys_poll, 3) SYS(sys_nfsservctl, 3) SYS(sys_setresgid, 3) /* 4190 */ @@ -214,19 +214,19 @@ SYS(sys_rt_sigpending, 2) SYS(sys_rt_sigtimedwait, 4) SYS(sys_rt_sigqueueinfo, 3) SYS(sys_rt_sigsuspend, 0) -SYS(sys_pread, 6) /* 4200 */ -SYS(sys_pwrite, 6) +SYS(sys_pread64, 6) /* 4200 */ +SYS(sys_pwrite64, 6) SYS(sys_chown, 3) SYS(sys_getcwd, 2) SYS(sys_capget, 2) SYS(sys_capset, 2) /* 4205 */ SYS(sys_sigaltstack, 0) -SYS(sys_sendfile, 3) +SYS(sys_sendfile, 4) SYS(sys_ni_syscall, 0) SYS(sys_ni_syscall, 0) SYS(sys_mmap2, 6) /* 4210 */ -SYS(sys_truncate64, 2) -SYS(sys_ftruncate64, 2) +SYS(sys_truncate64, 4) +SYS(sys_ftruncate64, 4) SYS(sys_stat64, 2) SYS(sys_lstat64, 2) SYS(sys_fstat64, 2) /* 4215 */ @@ -235,5 +235,39 @@ SYS(sys_mincore, 3) SYS(sys_madvise, 3) SYS(sys_getdents64, 3) SYS(sys_fcntl64, 3) /* 4220 */ +SYS(sys_ni_syscall, 0) SYS(sys_gettid, 0) +SYS(sys_readahead, 5) +SYS(sys_setxattr, 5) +SYS(sys_lsetxattr, 5) /* 4225 */ +SYS(sys_fsetxattr, 5) +SYS(sys_getxattr, 4) +SYS(sys_lgetxattr, 4) +SYS(sys_fgetxattr, 4) +SYS(sys_listxattr, 3) /* 4230 */ +SYS(sys_llistxattr, 3) +SYS(sys_flistxattr, 3) +SYS(sys_removexattr, 2) +SYS(sys_lremovexattr, 2) +SYS(sys_fremovexattr, 2) /* 4235 */ SYS(sys_tkill, 2) +SYS(sys_sendfile64, 5) +SYS(sys_futex, 2) +SYS(sys_sched_setaffinity, 3) +SYS(sys_sched_getaffinity, 3) /* 4240 */ +SYS(sys_io_setup, 2) +SYS(sys_io_destroy, 1) +SYS(sys_io_getevents, 5) +SYS(sys_io_submit, 3) +SYS(sys_io_cancel, 3) /* 4245 */ +SYS(sys_exit_group, 1) +SYS(sys_lookup_dcookie, 3) +SYS(sys_epoll_create, 1) +SYS(sys_epoll_ctl, 4) +SYS(sys_epoll_wait, 3) /* 4250 */ +SYS(sys_remap_file_pages, 5) +SYS(sys_set_tid_address, 1) +SYS(sys_restart_syscall, 0) /* XXX */ +SYS(sys_fadvise64, 6) +SYS(sys_statfs64, 3) /* 4255 */ +SYS(sys_fstatfs64, 2) diff --git a/arch/mips/kernel/sysirix.c b/arch/mips/kernel/sysirix.c index 7bdf103d4284..5722c28c1e9d 100644 --- a/arch/mips/kernel/sysirix.c +++ b/arch/mips/kernel/sysirix.c @@ -7,6 +7,8 @@ */ #include <linux/kernel.h> #include <linux/sched.h> +#include <linux/binfmts.h> +#include <linux/highuid.h> #include <linux/pagemap.h> #include <linux/mm.h> #include <linux/mman.h> @@ -24,6 +26,8 @@ #include <linux/utsname.h> #include <linux/file.h> #include <linux/vfs.h> +#include <linux/namei.h> +#include <linux/socket.h> #include <asm/ptrace.h> #include <asm/page.h> @@ -55,7 +59,7 @@ asmlinkage int irix_sysmp(struct pt_regs *regs) break; case MP_NPROCS: case MP_NAPROCS: - error = smp_num_cpus; + error = num_online_cpus(); break; default: printk("SYSMP[%s:%d]: Unsupported opcode %d\n", @@ -111,7 +115,7 @@ asmlinkage int irix_prctl(struct pt_regs *regs) if (error) error = (task->run_list.next != NULL); read_unlock(&tasklist_lock); - /* Can _your_ OS find this out that fast? */ + /* Can _your_ OS find this out that fast? */ break; } @@ -334,7 +338,7 @@ asmlinkage int irix_syssgi(struct pt_regs *regs) current->comm, current->pid, name, value, retval); /* if (retval == PROM_ENOENT) retval = -ENOENT; */ - break; + break; } #endif @@ -507,7 +511,7 @@ asmlinkage int irix_syssgi(struct pt_regs *regs) } break; } - + default: printk("irix_syssgi: Unsupported command %d\n", (int)cmd); retval = -EINVAL; @@ -600,7 +604,7 @@ out: asmlinkage int irix_getpid(struct pt_regs *regs) { - regs->regs[3] = current->p_opptr->pid; + regs->regs[3] = current->real_parent->pid; return current->pid; } @@ -623,9 +627,11 @@ asmlinkage int irix_stime(int value) write_seqlock_irq(&xtime_lock); xtime.tv_sec = value; - xtime.tv_usec = 0; - time_maxerror = MAXPHASE; - time_esterror = MAXPHASE; + xtime.tv_nsec = 0; + time_adjust = 0; /* stop active adjtime() */ + time_status |= STA_UNSYNC; + time_maxerror = NTP_PHASE_LIMIT; + time_esterror = NTP_PHASE_LIMIT; write_sequnlock_irq(&xtime_lock); return 0; @@ -819,10 +825,10 @@ asmlinkage int irix_times(struct tms * tbuf) err = verify_area(VERIFY_WRITE,tbuf,sizeof *tbuf); if (err) return err; - err |= __put_user(current->times.tms_utime,&tbuf->tms_utime); - err |= __put_user(current->times.tms_stime,&tbuf->tms_stime); - err |= __put_user(current->times.tms_cutime,&tbuf->tms_cutime); - err |= __put_user(current->times.tms_cstime,&tbuf->tms_cstime); + err |= __put_user(current->utime, &tbuf->tms_utime); + err |= __put_user(current->stime, &tbuf->tms_stime); + err |= __put_user(current->cutime, &tbuf->tms_cutime); + err |= __put_user(current->cstime, &tbuf->tms_cstime); } return err; @@ -1048,7 +1054,23 @@ asmlinkage int irix_sgikopt(char *istring, char *ostring, int len) asmlinkage int irix_gettimeofday(struct timeval *tv) { - return copy_to_user(tv, &xtime, sizeof(*tv)) ? -EFAULT : 0; + time_t sec; + long nsec, seq; + int err; + + if (verify_area(VERIFY_WRITE, tv, sizeof(struct timeval))) + return -EFAULT; + + do { + seq = read_seqbegin(&xtime_lock); + sec = xtime.tv_sec; + nsec = xtime.tv_nsec; + } while (read_seqretry(&xtime_lock, seq)); + + err = __put_user(sec, &tv->tv_sec); + err |= __put_user((nsec / 1000), &tv->tv_usec); + + return err; } #define IRIX_MAP_AUTOGROW 0x40 @@ -1068,7 +1090,7 @@ asmlinkage unsigned long irix_mmap32(unsigned long addr, size_t len, int prot, if (flags & IRIX_MAP_AUTOGROW) { unsigned long old_pos; long max_size = offset + len; - + if (max_size > file->f_dentry->d_inode->i_size) { old_pos = sys_lseek (fd, max_size - 1, 0); sys_write (fd, "", 1); @@ -1198,14 +1220,14 @@ static inline int irix_xstat32_xlate(struct kstat *stat, void *ubuf) #if BITS_PER_LONG == 32 if (stat->size > MAX_NON_LFS) return -EOVERFLOW; -#endif +#endif ub.st_size = stat->size; - ub.st_atime0 = stat->atime; - ub.st_atime1 = 0; - ub.st_mtime0 = stat->mtime; - ub.st_mtime1 = 0; - ub.st_ctime0 = stat->ctime; - ub.st_ctime1 = 0; + ub.st_atime0 = stat->atime.tv_sec; + ub.st_atime1 = stat->atime.tv_nsec; + ub.st_mtime0 = stat->mtime.tv_sec; + ub.st_mtime1 = stat->atime.tv_nsec; + ub.st_ctime0 = stat->ctime.tv_sec; + ub.st_ctime1 = stat->atime.tv_nsec; ub.st_blksize = stat->blksize; ub.st_blocks = stat->blocks; strcpy (ub.st_fstype, "efs"); @@ -1243,9 +1265,12 @@ static inline void irix_xstat64_xlate(struct kstat *stat, void *ubuf) ks.st_pad3 = 0; /* XXX hackety hack... */ - ks.st_atime.tv_sec = (s32) stat->atime; ks.st_atime.tv_nsec = 0; - ks.st_mtime.tv_sec = (s32) stat->atime; ks.st_mtime.tv_nsec = 0; - ks.st_ctime.tv_sec = (s32) stat->atime; ks.st_ctime.tv_nsec = 0; + ks.st_atime.tv_sec = (s32) stat->atime.tv_sec; + ks.st_atime.tv_nsec = stat->atime.tv_nsec; + ks.st_mtime.tv_sec = (s32) stat->mtime.tv_sec; + ks.st_mtime.tv_nsec = stat->mtime.tv_nsec;; + ks.st_ctime.tv_sec = (s32) stat->ctime.tv_sec; + ks.st_ctime.tv_nsec = stat->ctime.tv_nsec;; ks.st_blksize = (s32) stat->blksize; ks.st_blocks = (long long) stat->blocks; @@ -1812,7 +1837,8 @@ static int irix_filldir32(void *__buf, const char *name, int namlen, return 0; } -asmlinkage int irix_ngetdents(unsigned int fd, void * dirent, unsigned int count, int *eob) +asmlinkage int irix_ngetdents(unsigned int fd, void * dirent, + unsigned int count, int *eob) { struct file *file; struct irix_dirent32 *lastdirent; @@ -1836,6 +1862,7 @@ asmlinkage int irix_ngetdents(unsigned int fd, void * dirent, unsigned int count error = vfs_readdir(file, irix_filldir32, &buf); if (error < 0) goto out_putf; + error = buf.error; lastdirent = buf.previous; if (lastdirent) { @@ -1844,11 +1871,10 @@ asmlinkage int irix_ngetdents(unsigned int fd, void * dirent, unsigned int count } if (put_user(0, eob) < 0) { - error = EFAULT; + error = -EFAULT; goto out_putf; } - #ifdef DEBUG_GETDENTS printk("eob=%d returning %d\n", *eob, count - buf.count); #endif diff --git a/arch/mips/kernel/sysmips.c b/arch/mips/kernel/sysmips.c index 71a59703b223..3157086d24a8 100644 --- a/arch/mips/kernel/sysmips.c +++ b/arch/mips/kernel/sysmips.c @@ -1,13 +1,11 @@ /* - * MIPS specific syscalls - * * 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) 1995, 1996, 1997, 2000 by Ralf Baechle + * Copyright (C) 1995, 1996, 1997, 2000, 2001 by Ralf Baechle + * Copyright (C) 2001 MIPS Technologies, Inc. */ -#include <linux/config.h> #include <linux/errno.h> #include <linux/linkage.h> #include <linux/mm.h> @@ -19,7 +17,7 @@ #include <linux/ptrace.h> #include <asm/cachectl.h> -#include <asm/pgalloc.h> +#include <asm/cacheflush.h> #include <asm/sysmips.h> #include <asm/uaccess.h> @@ -49,11 +47,10 @@ get_max_hostname(unsigned long address) } asmlinkage int -sys_sysmips(int cmd, int arg1, int arg2, int arg3) +_sys_sysmips(int cmd, int arg1, int arg2, int arg3) { - int *p; char *name; - int tmp, len, retval, errno; + int tmp, len, retval; switch(cmd) { case SETNAME: { @@ -65,66 +62,22 @@ sys_sysmips(int cmd, int arg1, int arg2, int arg3) name = (char *) arg1; len = strncpy_from_user(nodename, name, __NEW_UTS_LEN); - if (len < 0) + if (len < 0) return -EFAULT; - nodename[__NEW_UTS_LEN] = '\0'; down_write(&uts_sem); + strncpy(system_utsname.nodename, nodename, len); + nodename[__NEW_UTS_LEN] = '\0'; strlcpy(system_utsname.nodename, nodename, - sizeof(system_utsname.nodename)); + sizeof(system_utsname.nodename)); up_write(&uts_sem); return 0; } - case MIPS_ATOMIC_SET: { -#ifdef CONFIG_CPU_HAS_LLSC - unsigned int tmp; - - p = (int *) arg1; - errno = verify_area(VERIFY_WRITE, p, sizeof(*p)); - if (errno) - return errno; - errno = 0; - - __asm__(".set\tpush\t\t\t# sysmips(MIPS_ATOMIC, ...)\n\t" - ".set\tmips2\n\t" - ".set\tnoat\n\t" - "1:\tll\t%0, %4\n\t" - "move\t$1, %3\n\t" - "2:\tsc\t$1, %1\n\t" - "beqz\t$1, 1b\n\t" - ".set\tpop\n\t" - ".section\t.fixup,\"ax\"\n" - "3:\tli\t%2, 1\t\t\t# error\n\t" - ".previous\n\t" - ".section\t__ex_table,\"a\"\n\t" - ".word\t1b, 3b\n\t" - ".word\t2b, 3b\n\t" - ".previous\n\t" - : "=&r" (tmp), "=o" (* (u32 *) p), "=r" (errno) - : "r" (arg2), "o" (* (u32 *) p), "2" (errno) - : "$1"); - - if (errno) - return -EFAULT; - - /* We're skipping error handling etc. */ - if (current->ptrace & PT_TRACESYS) - syscall_trace(); - - ((struct pt_regs *)&cmd)->regs[2] = tmp; - ((struct pt_regs *)&cmd)->regs[7] = 0; - - __asm__ __volatile__( - "move\t$29, %0\n\t" - "j\to32_ret_from_sys_call" - : /* No outputs */ - : "r" (&cmd)); - /* Unreached */ -#else - printk("sys_sysmips(MIPS_ATOMIC_SET, ...) not ready for !CONFIG_CPU_HAS_LLSC\n"); -#endif - } + case MIPS_ATOMIC_SET: + printk(KERN_CRIT "How did I get here?\n"); + retval = -EINVAL; + goto out; case MIPS_FIXADE: tmp = current->thread.mflags & ~3; @@ -133,7 +86,7 @@ sys_sysmips(int cmd, int arg1, int arg2, int arg3) goto out; case FLUSH_CACHE: - flush_cache_all(); + __flush_cache_all(); retval = 0; goto out; diff --git a/arch/mips/kernel/time.c b/arch/mips/kernel/time.c index f7551d197dfb..36c91fdcda3f 100644 --- a/arch/mips/kernel/time.c +++ b/arch/mips/kernel/time.c @@ -2,8 +2,8 @@ * Copyright 2001 MontaVista Software Inc. * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net * - * Common time service routines for MIPS machines. See - * Documentation/mips/time.README. + * Common time service routines for MIPS machines. See + * Documents/mips/README.txt. * * 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 @@ -21,6 +21,7 @@ #include <linux/kernel_stat.h> #include <linux/spinlock.h> #include <linux/interrupt.h> +#include <linux/module.h> #include <asm/bootinfo.h> #include <asm/cpu.h> @@ -30,7 +31,9 @@ /* This is for machines which generate the exact clock. */ #define USECS_PER_JIFFY (1000000/HZ) -#define USECS_PER_JIFFY_FRAC ((1000000ULL << 32) / HZ & 0xffffffff) +#define USECS_PER_JIFFY_FRAC ((u32)((1000000ULL << 32) / HZ)) + +#define TICK_SIZE (tick_nsec / 1000) u64 jiffies_64 = INITIAL_JIFFIES; @@ -39,6 +42,13 @@ u64 jiffies_64 = INITIAL_JIFFIES; */ extern volatile unsigned long wall_jiffies; +spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED; + +/* + * whether we emulate local_timer_interrupts for SMP machines. + */ +int emulate_local_timer_interrupt; + /* * By default we provide the null RTC ops */ @@ -57,58 +67,64 @@ int (*rtc_set_time)(unsigned long) = null_rtc_set_time; /* - * timeofday services, for syscalls. + * This version of gettimeofday has microsecond resolution and better than + * microsecond precision on fast machines with cycle counter. */ void do_gettimeofday(struct timeval *tv) { - unsigned long flags; unsigned long seq; + unsigned long usec, sec; do { - seq = read_seqbegin_irqsave(&xtime_lock, flags); - - *tv = xtime; - tv->tv_usec += do_gettimeoffset(); - - /* - * xtime is atomically updated in timer_bh. - * jiffies - wall_jiffies - * is nonzero if the timer bottom half hasnt executed yet. - */ - if (jiffies - wall_jiffies) - tv->tv_usec += USECS_PER_JIFFY; - } while (read_seqretry_irqrestore(&xtime_lock, seq, flags)); - + seq = read_seqbegin(&xtime_lock); + usec = do_gettimeoffset(); + { + unsigned long lost = jiffies - wall_jiffies; + if (lost) + usec += lost * (1000000 / HZ); + } + sec = xtime.tv_sec; + usec += (xtime.tv_nsec / 1000); + } while (read_seqretry(&xtime_lock, seq)); - if (tv->tv_usec >= 1000000) { - tv->tv_usec -= 1000000; - tv->tv_sec++; + while (usec >= 1000000) { + usec -= 1000000; + sec++; } + + tv->tv_sec = sec; + tv->tv_usec = usec; } -void do_settimeofday(struct timeval *tv) +int do_settimeofday(struct timespec *tv) { - write_seqlock_irq (&xtime_lock); + if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC) + return -EINVAL; - /* This is revolting. We need to set the xtime.tv_usec - * correctly. However, the value in this location is - * is value at the last tick. - * Discover what correction gettimeofday - * would have done, and then undo it! + write_seqlock_irq(&xtime_lock); + /* + * This is revolting. We need to set "xtime" correctly. However, the + * value in this location is the value at the most recent update of + * wall time. Discover what correction gettimeofday() would have + * made, and then undo it! */ - tv->tv_usec -= do_gettimeoffset(); + tv->tv_nsec -= do_gettimeoffset() * NSEC_PER_USEC; + tv->tv_nsec -= (jiffies - wall_jiffies) * TICK_NSEC; - if (tv->tv_usec < 0) { - tv->tv_usec += 1000000; + while (tv->tv_nsec < 0) { + tv->tv_nsec += NSEC_PER_SEC; tv->tv_sec--; } - xtime = *tv; - time_adjust = 0; /* stop active adjtime() */ + + xtime.tv_sec = tv->tv_sec; + xtime.tv_nsec = tv->tv_nsec; + time_adjust = 0; /* stop active adjtime() */ time_status |= STA_UNSYNC; time_maxerror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT; + write_sequnlock_irq(&xtime_lock); - write_sequnlock_irq (&xtime_lock); + return 0; } @@ -137,6 +153,9 @@ static unsigned long cycles_per_jiffy=0; /* Cycle counter value at the previous timer interrupt.. */ static unsigned int timerhi, timerlo; +/* expirelo is the count value for next CPU timer interrupt */ +static unsigned int expirelo; + /* last time when xtime and rtc are sync'ed up */ static long last_rtc_update; @@ -154,7 +173,7 @@ unsigned long fixed_rate_gettimeoffset(void) unsigned long res; /* Get last timer tick in absolute kernel time */ - count = read_32bit_cp0_register(CP0_COUNT); + count = read_c0_count(); /* .. relative to previous jiffy (32 bits is enough) */ count -= timerlo; @@ -210,7 +229,7 @@ unsigned long calibrate_div32_gettimeoffset(void) } /* Get last timer tick in absolute kernel time */ - count = read_32bit_cp0_register(CP0_COUNT); + count = read_c0_count(); /* .. relative to previous jiffy (32 bits is enough) */ count -= timerlo; @@ -260,13 +279,12 @@ unsigned long calibrate_div64_gettimeoffset(void) :"r" (timerhi), "m" (timerlo), "r" (tmp), - "r" (USECS_PER_JIFFY) - :"$1"); + "r" (USECS_PER_JIFFY)); cached_quotient = quotient; } /* Get last timer tick in absolute kernel time */ - count = read_32bit_cp0_register(CP0_COUNT); + count = read_c0_count(); /* .. relative to previous jiffy (32 bits is enough) */ count -= timerlo; @@ -289,35 +307,18 @@ unsigned long calibrate_div64_gettimeoffset(void) /* - * high-level timer interrupt service routines. This function - * is set as irqaction->handler and is invoked through do_IRQ. + * local_timer_interrupt() does profiling and process accounting + * on a per-CPU basis. + * + * In UP mode, it is invoked from the (global) timer_interrupt. + * + * In SMP mode, it might invoked by per-CPU timer interrupt, or + * a broadcasted inter-processor interrupt which itself is triggered + * by the global timer interrupt. */ -void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +void local_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - unsigned long seq; - - if (mips_cpu.options & MIPS_CPU_COUNTER) { - unsigned int count; - - /* - * The cycle counter is only 32 bit which is good for about - * a minute at current count rates of upto 150MHz or so. - */ - count = read_32bit_cp0_register(CP0_COUNT); - timerhi += (count < timerlo); /* Wrap around */ - timerlo = count; - - /* - * set up for next timer interrupt - no harm if the machine - * is using another timer interrupt source. - * Note that writing to COMPARE register clears the interrupt - */ - write_32bit_cp0_register (CP0_COMPARE, - count + cycles_per_jiffy); - - } - - if(!user_mode(regs)) { + if (!user_mode(regs)) { if (prof_buffer && current->pid) { extern int _stext; unsigned long pc = regs->cp0_epc; @@ -335,6 +336,38 @@ void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) } } +#ifdef CONFIG_SMP + /* in UP mode, update_process_times() is invoked by do_timer() */ + update_process_times(user_mode(regs)); +#endif +} + +/* + * high-level timer interrupt service routines. This function + * is set as irqaction->handler and is invoked through do_IRQ. + */ +irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + if (cpu_has_counter) { + unsigned int count; + + /* ack timer interrupt, and try to set next interrupt */ + expirelo += cycles_per_jiffy; + write_c0_compare(expirelo); + count = read_c0_count(); + + /* check to see if we have missed any timer interrupts */ + if ((count - expirelo) < 0x7fffffff) { + /* missed_timer_count ++; */ + expirelo = count + cycles_per_jiffy; + write_c0_compare(expirelo); + } + + /* Update timerhi/timerlo for intra-jiffy calibration. */ + timerhi += count < timerlo; /* Wrap around */ + timerlo = count; + } + /* * call the generic timer interrupt handling */ @@ -345,21 +378,19 @@ void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) * CMOS clock accordingly every ~11 minutes. rtc_set_time() has to be * called as close as possible to 500 ms before the new second starts. */ - do { - seq = read_seqbegin(&xtime_lock); - - if ((time_status & STA_UNSYNC) == 0 && - xtime.tv_sec > last_rtc_update + 660 && - xtime.tv_usec >= 500000 - ((unsigned) tick) / 2 && - xtime.tv_usec <= 500000 + ((unsigned) tick) / 2) { - if (rtc_set_time(xtime.tv_sec) == 0) { - last_rtc_update = xtime.tv_sec; - } else { - last_rtc_update = xtime.tv_sec - 600; - /* do it again in 60 s */ - } + write_seqlock(&xtime_lock); + if ((time_status & STA_UNSYNC) == 0 && + xtime.tv_sec > last_rtc_update + 660 && + (xtime.tv_nsec / 1000) >= 500000 - ((unsigned) TICK_SIZE) / 2 && + (xtime.tv_nsec / 1000) <= 500000 + ((unsigned) TICK_SIZE) / 2) { + if (rtc_set_time(xtime.tv_sec) == 0) { + last_rtc_update = xtime.tv_sec; + } else { + last_rtc_update = xtime.tv_sec - 600; + /* do it again in 60 s */ } - } while (read_seqretry(&xtime_lock, seq)); + } + write_sequnlock(&xtime_lock); /* * If jiffies has overflowed in this timer_interrupt we must @@ -369,41 +400,83 @@ void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) if (!jiffies) { timerhi = timerlo = 0; } + +#if !defined(CONFIG_SMP) + /* + * In UP mode, we call local_timer_interrupt() to do profiling + * and process accouting. + * + * In SMP mode, local_timer_interrupt() is invoked by appropriate + * low-level local timer interrupt handler. + */ + local_timer_interrupt(0, NULL, regs); + +#else /* CONFIG_SMP */ + + if (emulate_local_timer_interrupt) { + /* + * this is the place where we send out inter-process + * interrupts and let each CPU do its own profiling + * and process accouting. + * + * Obviously we need to call local_timer_interrupt() for + * the current CPU too. + */ + panic("Not implemented yet!!!"); + } +#endif /* CONFIG_SMP */ + + return IRQ_HANDLED; } asmlinkage void ll_timer_interrupt(int irq, struct pt_regs *regs) { int cpu = smp_processor_id(); - irq_enter(cpu, irq); + irq_enter(); kstat_cpu(cpu).irqs[irq]++; /* we keep interrupt disabled all the time */ timer_interrupt(irq, NULL, regs); - - irq_exit(cpu, irq); + + irq_exit(); if (softirq_pending(cpu)) do_softirq(); } +asmlinkage void ll_local_timer_interrupt(int irq, struct pt_regs *regs) +{ + int cpu = smp_processor_id(); + + irq_enter(); + kstat_cpu(cpu).irqs[irq]++; + + /* we keep interrupt disabled all the time */ + local_timer_interrupt(irq, NULL, regs); + + irq_exit(); + + if (softirq_pending(cpu)) + do_softirq(); +} /* * time_init() - it does the following things. * - * 1) board_time_init() - - * a) (optional) set up RTC routines, + * 1) board_time_init() - + * a) (optional) set up RTC routines, * b) (optional) calibrate and set the mips_counter_frequency * (only needed if you intended to use fixed_rate_gettimeoffset * or use cpu counter as timer interrupt source) * 2) setup xtime based on rtc_get_time(). * 3) choose a appropriate gettimeoffset routine. * 4) calculate a couple of cached variables for later usage - * 5) board_timer_setup() - + * 5) board_timer_setup() - * a) (optional) over-write any choices made above by time_init(). * b) machine specific code should setup the timer irqaction. * c) enable the timer interrupt - */ + */ void (*board_time_init)(void) = NULL; void (*board_timer_setup)(struct irqaction *irq) = NULL; @@ -416,7 +489,8 @@ static struct irqaction timer_irqaction = { 0, "timer", NULL, - NULL}; + NULL +}; void __init time_init(void) { @@ -424,18 +498,18 @@ void __init time_init(void) board_time_init(); xtime.tv_sec = rtc_get_time(); - xtime.tv_usec = 0; + xtime.tv_nsec = 0; /* choose appropriate gettimeoffset routine */ - if (!(mips_cpu.options & MIPS_CPU_COUNTER)) { + if (!cpu_has_counter) { /* no cpu counter - sorry */ do_gettimeoffset = null_gettimeoffset; } else if (mips_counter_frequency != 0) { /* we have cpu counter and know counter frequency! */ do_gettimeoffset = fixed_rate_gettimeoffset; - } else if ((mips_cpu.isa_level == MIPS_CPU_ISA_M32) || - (mips_cpu.isa_level == MIPS_CPU_ISA_I) || - (mips_cpu.isa_level == MIPS_CPU_ISA_II) ) { + } else if ((current_cpu_data.isa_level == MIPS_CPU_ISA_M32) || + (current_cpu_data.isa_level == MIPS_CPU_ISA_I) || + (current_cpu_data.isa_level == MIPS_CPU_ISA_II) ) { /* we need to calibrate the counter but we don't have * 64-bit division. */ do_gettimeoffset = calibrate_div32_gettimeoffset; @@ -443,7 +517,7 @@ void __init time_init(void) /* we need to calibrate the counter but we *do* have * 64-bit division. */ do_gettimeoffset = calibrate_div64_gettimeoffset; - } + } /* caclulate cache parameters */ if (mips_counter_frequency) { @@ -454,16 +528,24 @@ void __init time_init(void) sll32_usecs_per_cycle = mips_counter_frequency / 100000; sll32_usecs_per_cycle = 0xffffffff / sll32_usecs_per_cycle; sll32_usecs_per_cycle *= 10; + + /* + * For those using cpu counter as timer, this sets up the + * first interrupt + */ + write_c0_compare(cycles_per_jiffy); + write_c0_count(0); + expirelo = cycles_per_jiffy; } - /* + /* * Call board specific timer interrupt setup. * - * this pointer must be setup in machine setup routine. + * this pointer must be setup in machine setup routine. * * Even if the machine choose to use low-level timer interrupt, * it still needs to setup the timer_irqaction. - * In that case, it might be better to set timer_irqaction.handler + * In that case, it might be better to set timer_irqaction.handler * to be NULL function so that we are sure the high-level code * is not invoked accidentally. */ @@ -484,10 +566,10 @@ static int month_days[12] = { void to_tm(unsigned long tim, struct rtc_time * tm) { - long hms, day; + long hms, day, gday; int i; - day = tim / SECDAY; + gday = day = tim / SECDAY; hms = tim % SECDAY; /* Hours, minutes, seconds are easy */ @@ -506,7 +588,7 @@ void to_tm(unsigned long tim, struct rtc_time * tm) for (i = 1; day >= days_in_month(i); i++) day -= days_in_month(i); days_in_month(FEBRUARY) = 28; - tm->tm_mon = i; + tm->tm_mon = i-1; /* tm_mon starts from 0 to 11 */ /* Days are what is left over (+1) from all that. */ tm->tm_mday = day + 1; @@ -514,5 +596,7 @@ void to_tm(unsigned long tim, struct rtc_time * tm) /* * Determine the day of week */ - tm->tm_wday = (day + 3) % 7; + tm->tm_wday = (gday + 4) % 7; /* 1970/1/1 was Thursday */ } + +EXPORT_SYMBOL(rtc_lock); diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c index f3c333596d00..96ebda32053e 100644 --- a/arch/mips/kernel/traps.c +++ b/arch/mips/kernel/traps.c @@ -4,12 +4,12 @@ * for more details. * * Copyright (C) 1994 - 1999, 2000, 01 Ralf Baechle - * Modified for R3000 by Paul M. Antoine, 1995, 1996 - * Complete output from die() by Ulf Carlsson, 1998 + * Copyright (C) 1995, 1996 Paul M. Antoine + * Copyright (C) 1998 Ulf Carlsson * Copyright (C) 1999 Silicon Graphics, Inc. - * * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com * Copyright (C) 2000, 01 MIPS Technologies, Inc. + * Copyright (C) 2002, 2003 Maciej W. Rozycki */ #include <linux/config.h> #include <linux/init.h> @@ -19,30 +19,23 @@ #include <linux/smp.h> #include <linux/smp_lock.h> #include <linux/spinlock.h> +#include <linux/kallsyms.h> #include <asm/bootinfo.h> #include <asm/branch.h> #include <asm/cpu.h> -#include <asm/cachectl.h> -#include <asm/inst.h> -#include <asm/jazz.h> +#include <asm/fpu.h> #include <asm/module.h> #include <asm/pgtable.h> -#include <asm/io.h> -#include <asm/siginfo.h> -#include <asm/watch.h> +#include <asm/ptrace.h> +#include <asm/sections.h> #include <asm/system.h> +#include <asm/tlbdebug.h> +#include <asm/traps.h> #include <asm/uaccess.h> #include <asm/mmu_context.h> - -/* - * Machine specific interrupt handlers - */ -extern asmlinkage void acer_pica_61_handle_int(void); -extern asmlinkage void decstation_handle_int(void); -extern asmlinkage void deskstation_rpc44_handle_int(void); -extern asmlinkage void deskstation_tyne_handle_int(void); -extern asmlinkage void mips_magnum_4000_handle_int(void); +#include <asm/watch.h> +#include <asm/types.h> extern asmlinkage void handle_mod(void); extern asmlinkage void handle_tlbl(void); @@ -58,16 +51,16 @@ extern asmlinkage void handle_cpu(void); extern asmlinkage void handle_ov(void); extern asmlinkage void handle_tr(void); extern asmlinkage void handle_fpe(void); +extern asmlinkage void handle_mdmx(void); extern asmlinkage void handle_watch(void); extern asmlinkage void handle_mcheck(void); extern asmlinkage void handle_reserved(void); -extern int fpu_emulator_cop1Handler(struct pt_regs *); - -char watch_available = 0; +extern int fpu_emulator_cop1Handler(int xcptno, struct pt_regs *xcp, + struct mips_fpu_soft_struct *ctx); -void (*ibe_board_handler)(struct pt_regs *regs); -void (*dbe_board_handler)(struct pt_regs *regs); +void (*board_be_init)(void); +int (*board_be_handler)(struct pt_regs *regs, int is_fixup); /* * These constant is for searching for possible module text segments. @@ -75,99 +68,74 @@ void (*dbe_board_handler)(struct pt_regs *regs); */ #define MODULE_RANGE (8*1024*1024) -#ifndef CONFIG_CPU_HAS_LLSC -/* - * This stuff is needed for the userland ll-sc emulation for R2300 - */ -void simulate_ll(struct pt_regs *regs, unsigned int opcode); -void simulate_sc(struct pt_regs *regs, unsigned int opcode); - -#define OPCODE 0xfc000000 -#define BASE 0x03e00000 -#define RT 0x001f0000 -#define OFFSET 0x0000ffff -#define LL 0xc0000000 -#define SC 0xe0000000 -#endif - /* * This routine abuses get_user()/put_user() to reference pointers * with at least a bit of error checking ... */ -void show_stack(unsigned int *sp) +void show_stack(struct task_struct *task, unsigned long *sp) { + const int field = 2 * sizeof(unsigned long); + long stackdata; int i; - unsigned int *stack; - - stack = sp; - i = 0; - printk("Stack:"); - while ((unsigned long) stack & (PAGE_SIZE - 1)) { - unsigned long stackdata; + sp = sp ? sp : (unsigned long *) &sp; - if (__get_user(stackdata, stack++)) { - printk(" (Bad stack address)"); + printk("Stack: "); + i = 1; + while ((unsigned long) sp & (PAGE_SIZE - 1)) { + if (i && ((i % (64 / sizeof(unsigned long))) == 0)) + printk("\n "); + if (i > 40) { + printk(" ..."); break; } - printk(" %08lx", stackdata); - - if (++i > 40) { - printk(" ..."); + if (__get_user(stackdata, sp++)) { + printk(" (Bad stack address)"); break; } - if (i % 8 == 0) - printk("\n "); + printk(" %0*lx", field, stackdata); + i++; } + printk("\n"); } -void show_trace(unsigned int *sp) +void show_trace(struct task_struct *task, unsigned long *stack) { - int i; - unsigned int *stack; - unsigned long kernel_start, kernel_end; - unsigned long module_start, module_end; - extern char _stext, _etext; - - stack = sp; - i = 0; - - kernel_start = (unsigned long) &_stext; - kernel_end = (unsigned long) &_etext; - module_start = VMALLOC_START; - module_end = module_start + MODULE_RANGE; - - printk("\nCall Trace:"); + const int field = 2 * sizeof(unsigned long); + unsigned long addr; - while ((unsigned long) stack & (PAGE_SIZE -1)) { - unsigned long addr; + if (!stack) + stack = (unsigned long*)&stack; - if (__get_user(addr, stack++)) { - printk(" (Bad stack address)\n"); - break; + printk("Call Trace:"); +#ifdef CONFIG_KALLSYMS + printk("\n"); +#endif + while (((long) stack & (THREAD_SIZE-1)) != 0) { + addr = *stack++; + if (kernel_text_address(addr)) { + printk(" [<%0*lx>] ", field, addr); + print_symbol("%s\n", addr); } + } + printk("\n"); +} - /* - * If the address is either in the text segment of the - * kernel, or in the region which contains vmalloc'ed - * memory, it *may* be the address of a calling - * routine; if so, print it so that someone tracing - * down the cause of the crash will be able to figure - * out the call path that was taken. - */ +void show_trace_task(struct task_struct *tsk) +{ + show_trace(tsk, (long *)tsk->thread.reg29); +} - if ((addr >= kernel_start && addr < kernel_end) || - (addr >= module_start && addr < module_end)) { +/* + * The architecture-independent dump_stack generator + */ +void dump_stack(void) +{ + unsigned long stack; - printk(" [<%08lx>]", addr); - if (++i > 40) { - printk(" ..."); - break; - } - } - } + show_trace(current, &stack); } void show_code(unsigned int *pc) @@ -177,43 +145,114 @@ void show_code(unsigned int *pc) printk("\nCode:"); for(i = -3 ; i < 6 ; i++) { - unsigned long insn; + unsigned int insn; if (__get_user(insn, pc + i)) { printk(" (Bad address in epc)\n"); break; } - printk("%c%08lx%c",(i?' ':'<'),insn,(i?' ':'>')); + printk("%c%08x%c", (i?' ':'<'), insn, (i?' ':'>')); + } +} + +void show_regs(struct pt_regs *regs) +{ + const int field = 2 * sizeof(unsigned long); + int i; + + printk("Cpu %d\n", smp_processor_id()); + + /* + * Saved main processor registers + */ + for (i = 0; i < 32; i++) { + if ((i % 4) == 0) + printk("$%2d :", i); + if (i == 0) + printk(" %0*lx", field, 0UL); + else if (i == 26 || i == 27) + printk(" %*s", field, ""); + else + printk(" %0*lx", field, regs->regs[i]); + + i++; + if ((i % 4) == 0) + printk("\n"); } + + printk("Hi : %0*lx\n", field, regs->hi); + printk("Lo : %0*lx\n", field, regs->lo); + + /* + * Saved cp0 registers + */ + printk("epc : %0*lx %s\n", field, regs->cp0_epc, print_tainted()); + printk("Status: %0*lx\n", field, regs->cp0_status); + printk("Cause : %0*lx\n", field, regs->cp0_cause); + + if (regs->cp0_status & ST0_KX) + printk("KX "); + if (regs->cp0_status & ST0_SX) + printk("SX "); + if (regs->cp0_status & ST0_UX) + printk("UX "); + switch (regs->cp0_status & ST0_KSU) { + case KSU_USER: + printk("USER "); + break; + case KSU_SUPERVISOR: + printk("SUPERVISOR "); + break; + case KSU_KERNEL: + printk("KERNEL "); + break; + default: + printk("BAD_MODE "); + break; + } + if (regs->cp0_status & ST0_ERL) + printk("ERL "); + if (regs->cp0_status & ST0_EXL) + printk("EXL "); + if (regs->cp0_status & ST0_IE) + printk("IE "); +} + +void show_registers(struct pt_regs *regs) +{ + const int field = 2 * sizeof(unsigned long); + + show_regs(regs); + printk("Process %s (pid: %d, stackpage=%0*lx)\n", + current->comm, current->pid, field, (unsigned long) current); + show_stack(current, (long *) regs->regs[29]); + show_trace(current, (long *) regs->regs[29]); + show_code((unsigned int *) regs->cp0_epc); + printk("\n"); } -spinlock_t die_lock; +static spinlock_t die_lock = SPIN_LOCK_UNLOCKED; -extern void __die(const char * str, struct pt_regs * regs, const char *where, - unsigned long line) +void __die(const char * str, struct pt_regs * regs, const char * file, + const char * func, unsigned long line) { static int die_counter; + console_verbose(); spin_lock_irq(&die_lock); printk("%s", str); - if (where) - printk(" in %s, line %ld", where, line); + if (file && func) + printk(" in %s:%s, line %ld", file, func, line); printk("[#%d]:\n", ++die_counter); - show_regs(regs); - printk("Process %s (pid: %d, stackpage=%08lx)\n", - current->comm, current->pid, (unsigned long) current); - show_stack((unsigned int *) regs->regs[29]); - show_trace((unsigned int *) regs->regs[29]); - show_code((unsigned int *) regs->cp0_epc); - printk("\n"); + show_registers(regs); spin_unlock_irq(&die_lock); do_exit(SIGSEGV); } -void __die_if_kernel(const char * str, struct pt_regs * regs, const char *where, - unsigned long line) +void __die_if_kernel(const char * str, struct pt_regs * regs, + const char * file, const char * func, unsigned long line) { if (!user_mode(regs)) - __die(str, regs, where, line); + __die(str, regs, file, func, line); } extern const struct exception_table_entry __start___dbe_table[]; @@ -227,106 +266,199 @@ void __declare_dbe_table(void) ); } -static inline unsigned long -search_one_table(const struct exception_table_entry *first, - const struct exception_table_entry *last, - unsigned long value) +asmlinkage void do_be(struct pt_regs *regs) { - const struct exception_table_entry *mid; - long diff; - - while (first < last) { - mid = (last - first) / 2 + first; - diff = mid->insn - value; - if (diff < 0) - first = mid + 1; - else - last = mid; - } - return (first == last && first->insn == value) ? first->nextinsn : 0; -} - -extern spinlock_t modlist_lock; - -static inline unsigned long -search_dbe_table(unsigned long addr) -{ - unsigned long ret = 0; + const int field = 2 * sizeof(unsigned long); + const struct exception_table_entry *fixup = NULL; + int data = regs->cp0_cause & 4; + int action = MIPS_BE_FATAL; -#ifndef CONFIG_MODULES - /* There is only the kernel to search. */ - ret = search_one_table(__start___dbe_table, __stop___dbe_table-1, addr); - return ret; -#else - unsigned long flags; + /* XXX For now. Fixme, this searches the wrong table ... */ + if (data && !user_mode(regs)) + fixup = search_exception_tables(regs->cp0_epc); - /* The kernel is the last "module" -- no need to treat it special. */ - struct module *mp; - struct archdata *ap; + if (fixup) + action = MIPS_BE_FIXUP; - spin_lock_irqsave(&modlist_lock, flags); - for (mp = module_list; mp != NULL; mp = mp->next) { - if (!mod_member_present(mp, archdata_end) || - !mod_archdata_member_present(mp, struct archdata, - dbe_table_end)) - continue; - ap = (struct archdata *)(mp->archdata_start); + if (board_be_handler) + action = board_be_handler(regs, fixup != 0); - if (ap->dbe_table_start == NULL || - !(mp->flags & (MOD_RUNNING | MOD_INITIALIZING))) - continue; - ret = search_one_table(ap->dbe_table_start, - ap->dbe_table_end - 1, addr); - if (ret) - break; - } - spin_unlock_irqrestore(&modlist_lock, flags); - return ret; -#endif -} - -static void default_be_board_handler(struct pt_regs *regs) -{ - unsigned long new_epc; - unsigned long fixup; - int data = regs->cp0_cause & 4; - - if (data && !user_mode(regs)) { - fixup = search_dbe_table(regs->cp0_epc); + switch (action) { + case MIPS_BE_DISCARD: + return; + case MIPS_BE_FIXUP: if (fixup) { - new_epc = fixup_exception(dpf_reg, fixup, - regs->cp0_epc); - regs->cp0_epc = new_epc; + regs->cp0_epc = fixup->nextinsn; return; } + break; + default: + break; } /* * Assume it would be too dangerous to continue ... */ - printk(KERN_ALERT "%s bus error, epc == %08lx, ra == %08lx\n", + printk(KERN_ALERT "%s bus error, epc == %0*lx, ra == %0*lx\n", data ? "Data" : "Instruction", - regs->cp0_epc, regs->regs[31]); + field, regs->cp0_epc, field, regs->regs[31]); die_if_kernel("Oops", regs); force_sig(SIGBUS, current); } -asmlinkage void do_ibe(struct pt_regs *regs) +static inline int get_insn_opcode(struct pt_regs *regs, unsigned int *opcode) { - ibe_board_handler(regs); + unsigned int *epc; + + epc = (unsigned int *) regs->cp0_epc + + ((regs->cp0_cause & CAUSEF_BD) != 0); + if (!get_user(*opcode, epc)) + return 0; + + force_sig(SIGSEGV, current); + return 1; } -asmlinkage void do_dbe(struct pt_regs *regs) +/* + * ll/sc emulation + */ + +#define OPCODE 0xfc000000 +#define BASE 0x03e00000 +#define RT 0x001f0000 +#define OFFSET 0x0000ffff +#define LL 0xc0000000 +#define SC 0xe0000000 + +/* + * The ll_bit is cleared by r*_switch.S + */ + +unsigned long ll_bit; + +static struct task_struct *ll_task = NULL; + +static inline void simulate_ll(struct pt_regs *regs, unsigned int opcode) { - dbe_board_handler(regs); + unsigned long value, *vaddr; + long offset; + int signal = 0; + + /* + * analyse the ll instruction that just caused a ri exception + * and put the referenced address to addr. + */ + + /* sign extend offset */ + offset = opcode & OFFSET; + offset <<= 16; + offset >>= 16; + + vaddr = (unsigned long *)((long)(regs->regs[(opcode & BASE) >> 21]) + offset); + + if ((unsigned long)vaddr & 3) { + signal = SIGBUS; + goto sig; + } + if (get_user(value, vaddr)) { + signal = SIGSEGV; + goto sig; + } + + if (ll_task == NULL || ll_task == current) { + ll_bit = 1; + } else { + ll_bit = 0; + } + ll_task = current; + + regs->regs[(opcode & RT) >> 16] = value; + + compute_return_epc(regs); + return; + +sig: + force_sig(signal, current); } -asmlinkage void do_ov(struct pt_regs *regs) +static inline void simulate_sc(struct pt_regs *regs, unsigned int opcode) { - if (compute_return_epc(regs)) + unsigned long *vaddr, reg; + long offset; + int signal = 0; + + /* + * analyse the sc instruction that just caused a ri exception + * and put the referenced address to addr. + */ + + /* sign extend offset */ + offset = opcode & OFFSET; + offset <<= 16; + offset >>= 16; + + vaddr = (unsigned long *)((long)(regs->regs[(opcode & BASE) >> 21]) + offset); + reg = (opcode & RT) >> 16; + + if ((unsigned long)vaddr & 3) { + signal = SIGBUS; + goto sig; + } + if (ll_bit == 0 || ll_task != current) { + regs->regs[reg] = 0; + compute_return_epc(regs); return; + } - force_sig(SIGFPE, current); + if (put_user(regs->regs[reg], vaddr)) { + signal = SIGSEGV; + goto sig; + } + + regs->regs[reg] = 1; + + compute_return_epc(regs); + return; + +sig: + force_sig(signal, current); +} + +/* + * ll uses the opcode of lwc0 and sc uses the opcode of swc0. That is both + * opcodes are supposed to result in coprocessor unusable exceptions if + * executed on ll/sc-less processors. That's the theory. In practice a + * few processors such as NEC's VR4100 throw reserved instruction exceptions + * instead, so we're doing the emulation thing in both exception handlers. + */ +static inline int simulate_llsc(struct pt_regs *regs) +{ + unsigned int opcode; + + if (unlikely(get_insn_opcode(regs, &opcode))) + return -EFAULT; + + if ((opcode & OPCODE) == LL) { + simulate_ll(regs, opcode); + return 0; + } + if ((opcode & OPCODE) == SC) { + simulate_sc(regs, opcode); + return 0; + } + + return -EFAULT; /* Strange things going on ... */ +} + +asmlinkage void do_ov(struct pt_regs *regs) +{ + siginfo_t info; + + info.si_code = FPE_INTOVF; + info.si_signo = SIGFPE; + info.si_errno = 0; + info.si_addr = (void *)regs->cp0_epc; + force_sig_info(SIGFPE, &info, current); } /* @@ -335,30 +467,29 @@ asmlinkage void do_ov(struct pt_regs *regs) asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31) { if (fcr31 & FPU_CSR_UNI_X) { - extern void save_fp(struct task_struct *); - extern void restore_fp(struct task_struct *); int sig; + /* - * Unimplemented operation exception. If we've got the - * full software emulator on-board, let's use it... + * Unimplemented operation exception. If we've got the full + * software emulator on-board, let's use it... * - * Force FPU to dump state into task/thread context. - * We're moving a lot of data here for what is probably - * a single instruction, but the alternative is to - * pre-decode the FP register operands before invoking - * the emulator, which seems a bit extreme for what - * should be an infrequent event. + * Force FPU to dump state into task/thread context. We're + * moving a lot of data here for what is probably a single + * instruction, but the alternative is to pre-decode the FP + * register operands before invoking the emulator, which seems + * a bit extreme for what should be an infrequent event. */ save_fp(current); - + /* Run the emulator */ - sig = fpu_emulator_cop1Handler(regs); + sig = fpu_emulator_cop1Handler (0, regs, + ¤t->thread.fpu.soft); - /* - * We can't allow the emulated instruction to leave the - * Unimplemented Operation bit set in $fcr31. + /* + * We can't allow the emulated instruction to leave any of + * the cause bit set in $fcr31. */ - current->thread.fpu.soft.sr &= ~FPU_CSR_UNI_X; + current->thread.fpu.soft.sr &= ~FPU_CSR_ALL_X; /* Restore the hardware register state */ restore_fp(current); @@ -370,36 +501,18 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31) return; } - if (compute_return_epc(regs)) - return; - force_sig(SIGFPE, current); - printk(KERN_DEBUG "Sent send SIGFPE to %s\n", current->comm); -} - -static inline int get_insn_opcode(struct pt_regs *regs, unsigned int *opcode) -{ - unsigned int *epc; - - epc = (unsigned int *) regs->cp0_epc + - ((regs->cp0_cause & CAUSEF_BD) != 0); - if (!get_user(*opcode, epc)) - return 0; - - force_sig(SIGSEGV, current); - return 1; } asmlinkage void do_bp(struct pt_regs *regs) { - siginfo_t info; unsigned int opcode, bcode; - unsigned int *epc; + siginfo_t info; - epc = (unsigned int *) regs->cp0_epc + - ((regs->cp0_cause & CAUSEF_BD) != 0); - if (get_user(opcode, epc)) - goto sigsegv; + die_if_kernel("Break instruction in kernel code", regs); + + if (get_insn_opcode(regs, &opcode)) + return; /* * There is the ancient bug in the MIPS assemblers that the break @@ -423,241 +536,107 @@ asmlinkage void do_bp(struct pt_regs *regs) info.si_code = FPE_INTOVF; info.si_signo = SIGFPE; info.si_errno = 0; - info.si_addr = (void *)compute_return_epc(regs); + info.si_addr = (void *)regs->cp0_epc; force_sig_info(SIGFPE, &info, current); break; default: force_sig(SIGTRAP, current); } - return; - -sigsegv: - force_sig(SIGSEGV, current); } asmlinkage void do_tr(struct pt_regs *regs) { + unsigned int opcode, tcode = 0; siginfo_t info; - unsigned int opcode, bcode; - unsigned *epc; - epc = (unsigned int *) regs->cp0_epc + - ((regs->cp0_cause & CAUSEF_BD) != 0); - if (get_user(opcode, epc)) - goto sigsegv; + die_if_kernel("Trap instruction in kernel code", regs); + + if (get_insn_opcode(regs, &opcode)) + return; - bcode = ((opcode >> 6) & ((1 << 20) - 1)); + /* Immediate versions don't provide a code. */ + if (!(opcode & OPCODE)) + tcode = ((opcode >> 6) & ((1 << 20) - 1)); /* - * (A short test says that IRIX 5.3 sends SIGTRAP for all break - * insns, even for break codes that indicate arithmetic failures. + * (A short test says that IRIX 5.3 sends SIGTRAP for all trap + * insns, even for trap codes that indicate arithmetic failures. * Weird ...) * But should we continue the brokenness??? --macro */ - switch (bcode) { + switch (tcode) { case 6: case 7: - if (bcode == 7) + if (tcode == 7) info.si_code = FPE_INTDIV; else info.si_code = FPE_INTOVF; info.si_signo = SIGFPE; info.si_errno = 0; - info.si_addr = (void *)compute_return_epc(regs); + info.si_addr = (void *)regs->cp0_epc; force_sig_info(SIGFPE, &info, current); break; default: force_sig(SIGTRAP, current); } - return; - -sigsegv: - force_sig(SIGSEGV, current); } -#ifndef CONFIG_CPU_HAS_LLSC - -#ifdef CONFIG_SMP -#error "ll/sc emulation is not SMP safe" -#endif - -/* - * userland emulation for R2300 CPUs - * needed for the multithreading part of glibc - * - * this implementation can handle only sychronization between 2 or more - * user contexts and is not SMP safe. - */ asmlinkage void do_ri(struct pt_regs *regs) { - unsigned int opcode; - - if (!user_mode(regs)) - BUG(); + die_if_kernel("Reserved instruction in kernel code", regs); - if (!get_insn_opcode(regs, &opcode)) { - if ((opcode & OPCODE) == LL) { - simulate_ll(regs, opcode); - return; - } - if ((opcode & OPCODE) == SC) { - simulate_sc(regs, opcode); + if (!cpu_has_llsc) + if (!simulate_llsc(regs)) return; - } - } - if (compute_return_epc(regs)) - return; force_sig(SIGILL, current); } -/* - * The ll_bit is cleared by r*_switch.S - */ - -unsigned long ll_bit; -#ifdef CONFIG_PROC_FS -extern unsigned long ll_ops; -extern unsigned long sc_ops; -#endif - -static struct task_struct *ll_task = NULL; - -void simulate_ll(struct pt_regs *regp, unsigned int opcode) +asmlinkage void do_cpu(struct pt_regs *regs) { - unsigned long value, *vaddr; - long offset; - int signal = 0; + unsigned int cpid; - /* - * analyse the ll instruction that just caused a ri exception - * and put the referenced address to addr. - */ + die_if_kernel("do_cpu invoked from kernel context!", regs); - /* sign extend offset */ - offset = opcode & OFFSET; - offset <<= 16; - offset >>= 16; + cpid = (regs->cp0_cause >> CAUSEB_CE) & 3; - vaddr = (unsigned long *)((long)(regp->regs[(opcode & BASE) >> 21]) + offset); + switch (cpid) { + case 0: + if (cpu_has_llsc) + break; -#ifdef CONFIG_PROC_FS - ll_ops++; -#endif + if (!simulate_llsc(regs)) + return; + break; - if ((unsigned long)vaddr & 3) - signal = SIGBUS; - else if (get_user(value, vaddr)) - signal = SIGSEGV; - else { - if (ll_task == NULL || ll_task == current) { - ll_bit = 1; - } else { - ll_bit = 0; + case 1: + own_fpu(); + if (current->used_math) { /* Using the FPU again. */ + restore_fp(current); + } else { /* First time FPU user. */ + init_fpu(); + current->used_math = 1; } - ll_task = current; - regp->regs[(opcode & RT) >> 16] = value; - } - if (compute_return_epc(regp)) - return; - if (signal) - send_sig(signal, current, 1); -} - -void simulate_sc(struct pt_regs *regp, unsigned int opcode) -{ - unsigned long *vaddr, reg; - long offset; - int signal = 0; - - /* - * analyse the sc instruction that just caused a ri exception - * and put the referenced address to addr. - */ - - /* sign extend offset */ - offset = opcode & OFFSET; - offset <<= 16; - offset >>= 16; - - vaddr = (unsigned long *)((long)(regp->regs[(opcode & BASE) >> 21]) + offset); - reg = (opcode & RT) >> 16; -#ifdef CONFIG_PROC_FS - sc_ops++; -#endif + if (!cpu_has_fpu) { + int sig = fpu_emulator_cop1Handler(0, regs, + ¤t->thread.fpu.soft); + if (sig) + force_sig(sig, current); + } - if ((unsigned long)vaddr & 3) - signal = SIGBUS; - else if (ll_bit == 0 || ll_task != current) - regp->regs[reg] = 0; - else if (put_user(regp->regs[reg], vaddr)) - signal = SIGSEGV; - else - regp->regs[reg] = 1; - if (compute_return_epc(regp)) return; - if (signal) - send_sig(signal, current, 1); -} - -#else /* MIPS 2 or higher */ -asmlinkage void do_ri(struct pt_regs *regs) -{ - unsigned int opcode; - - get_insn_opcode(regs, &opcode); - if (compute_return_epc(regs)) - return; + case 2: + case 3: + break; + } force_sig(SIGILL, current); } -#endif - -asmlinkage void do_cpu(struct pt_regs *regs) +asmlinkage void do_mdmx(struct pt_regs *regs) { - unsigned int cpid; - extern void lazy_fpu_switch(void *); - extern void init_fpu(void); - void fpu_emulator_init_fpu(void); - int sig; - - cpid = (regs->cp0_cause >> CAUSEB_CE) & 3; - if (cpid != 1) - goto bad_cid; - - if (!(mips_cpu.options & MIPS_CPU_FPU)) - goto fp_emul; - - regs->cp0_status |= ST0_CU1; - if (last_task_used_math == current) - return; - - if (current->used_math) { /* Using the FPU again. */ - lazy_fpu_switch(last_task_used_math); - } else { /* First time FPU user. */ - init_fpu(); - current->used_math = 1; - } - last_task_used_math = current; - return; - -fp_emul: - if (last_task_used_math != current) { - if (!current->used_math) { - fpu_emulator_init_fpu(); - current->used_math = 1; - } - } - sig = fpu_emulator_cop1Handler(regs); - last_task_used_math = current; - if (sig) - force_sig(sig, current); - return; - -bad_cid: force_sig(SIGILL, current); } @@ -667,6 +646,7 @@ asmlinkage void do_watch(struct pt_regs *regs) * We use the watch exception where available to detect stack * overflows. */ + dump_tlb_all(); show_regs(regs); panic("Caught WATCH exception - probably caused by stack overflow."); } @@ -674,8 +654,14 @@ asmlinkage void do_watch(struct pt_regs *regs) asmlinkage void do_mcheck(struct pt_regs *regs) { show_regs(regs); - panic("Caught Machine Check exception - probably caused by multiple " - "matching entries in the TLB."); + dump_tlb_all(); + /* + * Some chips may have other causes of machine check (e.g. SB1 + * graduation timer) + */ + panic("Caught Machine Check exception - %scaused by multiple " + "matching entries in the TLB.", + (regs->cp0_status & ST0_TS) ? "" : "not "); } asmlinkage void do_reserved(struct pt_regs *regs) @@ -686,15 +672,8 @@ asmlinkage void do_reserved(struct pt_regs *regs) * hard/software error. */ show_regs(regs); - panic("Caught reserved exception - should not happen."); -} - -static inline void watch_init(void) -{ - if (mips_cpu.options & MIPS_CPU_WATCH ) { - set_except_vector(23, handle_watch); - watch_available = 1; - } + panic("Caught reserved exception %ld - should not happen.", + (regs->cp0_cause & 0x7f) >> 2); } /* @@ -703,14 +682,12 @@ static inline void watch_init(void) */ static inline void parity_protection_init(void) { - switch (mips_cpu.cputype) { + switch (current_cpu_data.cputype) { case CPU_5KC: - /* Set the PE bit (bit 31) in the CP0_ECC register. */ + /* Set the PE bit (bit 31) in the c0_ecc register. */ printk(KERN_INFO "Enable the cache parity protection for " "MIPS 5KC CPUs.\n"); - write_32bit_cp0_register(CP0_ECC, - read_32bit_cp0_register(CP0_ECC) - | 0x80000000); + write_c0_ecc(read_c0_ecc() | 0x80000000); break; default: break; @@ -719,16 +696,16 @@ static inline void parity_protection_init(void) asmlinkage void cache_parity_error(void) { + const int field = 2 * sizeof(unsigned long); unsigned int reg_val; /* For the moment, report the problem and hang. */ - reg_val = read_32bit_cp0_register(CP0_ERROREPC); printk("Cache error exception:\n"); - printk("cp0_errorepc == %08x\n", reg_val); - reg_val = read_32bit_cp0_register(CP0_CACHEERR); - printk("cp0_cacheerr == %08x\n", reg_val); + printk("cp0_errorepc == %0*lx\n", field, read_c0_errorepc()); + reg_val = read_c0_cacheerr(); + printk("c0_cacheerr == %08x\n", reg_val); - printk("Decoded CP0_CACHEERR: %s cache fault in %s reference.\n", + printk("Decoded c0_cacheerr: %s cache fault in %s reference.\n", reg_val & (1<<30) ? "secondary" : "primary", reg_val & (1<<31) ? "data" : "insn"); printk("Error bits: %s%s%s%s%s%s%s\n", @@ -741,17 +718,63 @@ asmlinkage void cache_parity_error(void) reg_val & (1<<22) ? "E0 " : ""); printk("IDX: 0x%08x\n", reg_val & ((1<<22)-1)); - if (reg_val&(1<<22)) - printk("DErrAddr0: 0x%08x\n", - read_32bit_cp0_set1_register(CP0_S1_DERRADDR0)); +#if defined(CONFIG_CPU_MIPS32) || defined (CONFIG_CPU_MIPS64) + if (reg_val & (1<<22)) + printk("DErrAddr0: 0x%08x\n", read_c0_derraddr0()); - if (reg_val&(1<<23)) - printk("DErrAddr1: 0x%08x\n", - read_32bit_cp0_set1_register(CP0_S1_DERRADDR1)); + if (reg_val & (1<<23)) + printk("DErrAddr1: 0x%08x\n", read_c0_derraddr1()); +#endif panic("Can't handle the cache error!"); } +/* + * SDBBP EJTAG debug exception handler. + * We skip the instruction and return to the next instruction. + */ +void ejtag_exception_handler(struct pt_regs *regs) +{ + const int field = 2 * sizeof(unsigned long); + unsigned long depc, old_epc; + unsigned int debug; + + printk("SDBBP EJTAG debug exception - not handled yet, just ignored!\n"); + depc = read_c0_depc(); + debug = read_c0_debug(); + printk("c0_depc = %0*lx, DEBUG = %08x\n", field, depc, debug); + if (debug & 0x80000000) { + /* + * In branch delay slot. + * We cheat a little bit here and use EPC to calculate the + * debug return address (DEPC). EPC is restored after the + * calculation. + */ + old_epc = regs->cp0_epc; + regs->cp0_epc = depc; + __compute_return_epc(regs); + depc = regs->cp0_epc; + regs->cp0_epc = old_epc; + } else + depc += 4; + write_c0_depc(depc); + +#if 0 + printk("\n\n----- Enable EJTAG single stepping ----\n\n"); + write_c0_debug(debug | 0x100); +#endif +} + +/* + * NMI exception handler. + */ +void nmi_exception_handler(struct pt_regs *regs) +{ + printk("NMI taken!!!!\n"); + die("NMI", regs); + while(1) ; +} + unsigned long exception_handlers[32]; /* @@ -761,11 +784,11 @@ unsigned long exception_handlers[32]; */ void *set_except_vector(int n, void *addr) { - unsigned handler = (unsigned long) addr; - unsigned old_handler = exception_handlers[n]; - exception_handlers[n] = handler; + unsigned long handler = (unsigned long) addr; + unsigned long old_handler = exception_handlers[n]; - if (n == 0 && mips_cpu.options & MIPS_CPU_DIVEC) { + exception_handlers[n] = handler; + if (n == 0 && cpu_has_divec) { *(volatile u32 *)(KSEG0+0x200) = 0x08000000 | (0x03ffffff & (handler >> 2)); flush_icache_range(KSEG0+0x200, KSEG0 + 0x204); @@ -775,56 +798,70 @@ void *set_except_vector(int n, void *addr) asmlinkage int (*save_fp_context)(struct sigcontext *sc); asmlinkage int (*restore_fp_context)(struct sigcontext *sc); + extern asmlinkage int _save_fp_context(struct sigcontext *sc); extern asmlinkage int _restore_fp_context(struct sigcontext *sc); extern asmlinkage int fpu_emulator_save_context(struct sigcontext *sc); extern asmlinkage int fpu_emulator_restore_context(struct sigcontext *sc); +void __init per_cpu_trap_init(void) +{ + unsigned int cpu = smp_processor_id(); + + /* Some firmware leaves the BEV flag set, clear it. */ + clear_c0_status(ST0_CU1|ST0_CU2|ST0_CU3|ST0_BEV); + + /* + * Some MIPS CPUs have a dedicated interrupt vector which reduces the + * interrupt processing overhead. Use it where available. + */ + if (cpu_has_divec) + set_c0_cause(CAUSEF_IV); + + cpu_data[cpu].asid_cache = ASID_FIRST_VERSION; + write_c0_context(cpu << 23); +} + void __init trap_init(void) { - extern char except_vec0_nevada, except_vec0_r4000; - extern char except_vec0_r4600, except_vec0_r2300; - extern char except_vec1_generic, except_vec2_generic; + extern char except_vec1_generic; extern char except_vec3_generic, except_vec3_r4000; - extern char except_vec4; extern char except_vec_ejtag_debug; + extern char except_vec4; unsigned long i; - /* Some firmware leaves the BEV flag set, clear it. */ - clear_cp0_status(ST0_BEV); + per_cpu_trap_init(); /* Copy the generic exception handler code to its final destination. */ memcpy((void *)(KSEG0 + 0x80), &except_vec1_generic, 0x80); - memcpy((void *)(KSEG0 + 0x100), &except_vec2_generic, 0x80); - memcpy((void *)(KSEG0 + 0x180), &except_vec3_generic, 0x80); - flush_icache_range(KSEG0 + 0x80, KSEG0 + 0x200); + /* * Setup default vectors */ for (i = 0; i <= 31; i++) set_except_vector(i, handle_reserved); - /* - * Copy the EJTAG debug exception vector handler code to its final + /* + * Copy the EJTAG debug exception vector handler code to it's final * destination. */ - memcpy((void *)(KSEG0 + 0x300), &except_vec_ejtag_debug, 0x80); + if (cpu_has_ejtag) + memcpy((void *)(KSEG0 + 0x300), &except_vec_ejtag_debug, 0x80); /* * Only some CPUs have the watch exceptions or a dedicated * interrupt vector. */ - watch_init(); + if (cpu_has_watch) + set_except_vector(23, handle_watch); /* * Some MIPS CPUs have a dedicated interrupt vector which reduces the * interrupt processing overhead. Use it where available. */ - if (mips_cpu.options & MIPS_CPU_DIVEC) { - memcpy((void *)(KSEG0 + 0x200), &except_vec4, 8); - set_cp0_cause(CAUSEF_IV); - } + if (cpu_has_divec) + memcpy((void *)(KSEG0 + 0x200), &except_vec4, 0x8); /* * Some CPUs can enable/disable for cache parity detection, but does @@ -832,21 +869,22 @@ void __init trap_init(void) */ parity_protection_init(); + /* + * The Data Bus Errors / Instruction Bus Errors are signaled + * by external hardware. Therefore these two exceptions + * may have board specific handlers. + */ + if (board_be_init) + board_be_init(); + set_except_vector(1, handle_mod); set_except_vector(2, handle_tlbl); set_except_vector(3, handle_tlbs); set_except_vector(4, handle_adel); set_except_vector(5, handle_ades); - /* - * The Data Bus Error/ Instruction Bus Errors are signaled - * by external hardware. Therefore these two expection have - * board specific handlers. - */ set_except_vector(6, handle_ibe); set_except_vector(7, handle_dbe); - ibe_board_handler = default_be_board_handler; - dbe_board_handler = default_be_board_handler; set_except_vector(8, handle_sys); set_except_vector(9, handle_bp); @@ -854,59 +892,23 @@ void __init trap_init(void) set_except_vector(11, handle_cpu); set_except_vector(12, handle_ov); set_except_vector(13, handle_tr); + set_except_vector(22, handle_mdmx); - if (mips_cpu.options & MIPS_CPU_FPU) + if (cpu_has_fpu && !cpu_has_nofpuex) set_except_vector(15, handle_fpe); - /* - * Handling the following exceptions depends mostly of the cpu type - */ - if ((mips_cpu.options & MIPS_CPU_4KEX) - && (mips_cpu.options & MIPS_CPU_4KTLB)) { - if (mips_cpu.cputype == CPU_NEVADA) { - memcpy((void *)KSEG0, &except_vec0_nevada, 0x80); - } else if (mips_cpu.cputype == CPU_R4600) - memcpy((void *)KSEG0, &except_vec0_r4600, 0x80); - else - memcpy((void *)KSEG0, &except_vec0_r4000, 0x80); - - /* Cache error vector already set above. */ - - if (mips_cpu.options & MIPS_CPU_VCE) { - memcpy((void *)(KSEG0 + 0x180), &except_vec3_r4000, - 0x80); - } else { - memcpy((void *)(KSEG0 + 0x180), &except_vec3_generic, - 0x80); - } + if (cpu_has_mcheck) + set_except_vector(24, handle_mcheck); - if (mips_cpu.options & MIPS_CPU_FPU) { - save_fp_context = _save_fp_context; - restore_fp_context = _restore_fp_context; - } else { - save_fp_context = fpu_emulator_save_context; - restore_fp_context = fpu_emulator_restore_context; - } - } else switch (mips_cpu.cputype) { - case CPU_SB1: - /* - * XXX - This should be folded in to the "cleaner" handling, - * above - */ - memcpy((void *)KSEG0, &except_vec0_r4000, 0x80); + if (cpu_has_vce) memcpy((void *)(KSEG0 + 0x180), &except_vec3_r4000, 0x80); - save_fp_context = _save_fp_context; - restore_fp_context = _restore_fp_context; + else if (cpu_has_4kex) + memcpy((void *)(KSEG0 + 0x180), &except_vec3_generic, 0x80); + else + memcpy((void *)(KSEG0 + 0x080), &except_vec3_generic, 0x80); - /* Enable timer interrupt and scd mapped interrupt */ - clear_cp0_status(0xf000); - set_cp0_status(0xc00); - break; - case CPU_R6000: - case CPU_R6000A: - save_fp_context = _save_fp_context; - restore_fp_context = _restore_fp_context; - + if (current_cpu_data.cputype == CPU_R6000 || + current_cpu_data.cputype == CPU_R6000A) { /* * The R6000 is the only R-series CPU that features a machine * check exception (similar to the R4000 cache error) and @@ -917,34 +919,24 @@ void __init trap_init(void) */ //set_except_vector(14, handle_mc); //set_except_vector(15, handle_ndc); - case CPU_R2000: - case CPU_R3000: - case CPU_R3000A: - case CPU_R3041: - case CPU_R3051: - case CPU_R3052: - case CPU_R3081: - case CPU_R3081E: - case CPU_TX3912: - case CPU_TX3922: - case CPU_TX3927: + } + + if (cpu_has_fpu) { save_fp_context = _save_fp_context; restore_fp_context = _restore_fp_context; - memcpy((void *)KSEG0, &except_vec0_r2300, 0x80); - memcpy((void *)(KSEG0 + 0x80), &except_vec3_generic, 0x80); - break; - - case CPU_UNKNOWN: - default: - panic("Unknown CPU type"); + } else { + save_fp_context = fpu_emulator_save_context; + restore_fp_context = fpu_emulator_restore_context; } - flush_icache_range(KSEG0, KSEG0 + 0x200); - if (mips_cpu.isa_level == MIPS_CPU_ISA_IV) - set_cp0_status(ST0_XX); + flush_icache_range(KSEG0, KSEG0 + 0x400); + + if (current_cpu_data.isa_level == MIPS_CPU_ISA_IV) + set_c0_status(ST0_XX); - atomic_inc(&init_mm.mm_count); /* XXX UP? */ + atomic_inc(&init_mm.mm_count); /* XXX UP? */ current->active_mm = &init_mm; - write_32bit_cp0_register(CP0_CONTEXT, smp_processor_id()<<23); - current_pgd[0] = init_mm.pgd; + + /* XXX Must be done for all CPUs */ + TLBMISS_HANDLER_SETUP(); } diff --git a/arch/mips/kernel/unaligned.c b/arch/mips/kernel/unaligned.c index 2c20262b6717..ba891c124afd 100644 --- a/arch/mips/kernel/unaligned.c +++ b/arch/mips/kernel/unaligned.c @@ -5,7 +5,7 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 1996, 1998 by Ralf Baechle + * Copyright (C) 1996, 1998, 1999, 2002 by Ralf Baechle * Copyright (C) 1999 Silicon Graphics, Inc. * * This file contains exception handler for address error exception with the @@ -41,7 +41,7 @@ * * #include <stdio.h> * #include <asm/sysmips.h> - * + * * struct foo { * unsigned char bar[8]; * }; @@ -74,6 +74,7 @@ */ #include <linux/config.h> #include <linux/mm.h> +#include <linux/module.h> #include <linux/signal.h> #include <linux/smp.h> #include <linux/smp_lock.h> @@ -88,23 +89,22 @@ #define STR(x) __STR(x) #define __STR(x) #x -/* - * User code may only access USEG; kernel code may access the - * entire address space. - */ -#define check_axs(pc,a,s) \ - if ((long)(~(pc) & ((a) | ((a)+(s)))) < 0) \ - goto sigbus; +#ifdef CONFIG_PROC_FS +unsigned long unaligned_instructions; +#endif -static inline void -emulate_load_store_insn(struct pt_regs *regs, - unsigned long addr, - unsigned long pc) +static inline int emulate_load_store_insn(struct pt_regs *regs, + void *addr, unsigned long pc, + unsigned long **regptr, unsigned long *newvalue) { union mips_instruction insn; - unsigned long value, fixup; + unsigned long value; + const struct exception_table_entry *fixup; + unsigned int res; regs->regs[0] = 0; + *regptr=NULL; + /* * This load never faults. */ @@ -144,186 +144,295 @@ emulate_load_store_insn(struct pt_regs *regs, * The remaining opcodes are the ones that are really of interest. */ case lh_op: - check_axs(pc, addr, 2); - __asm__( - ".set\tnoat\n" + if (verify_area(VERIFY_READ, addr, 2)) + goto sigbus; + + __asm__ __volatile__ (".set\tnoat\n" #ifdef __BIG_ENDIAN - "1:\tlb\t%0,0(%1)\n" - "2:\tlbu\t$1,1(%1)\n\t" + "1:\tlb\t%0, 0(%2)\n" + "2:\tlbu\t$1, 1(%2)\n\t" #endif #ifdef __LITTLE_ENDIAN - "1:\tlb\t%0,1(%1)\n" - "2:\tlbu\t$1,0(%1)\n\t" + "1:\tlb\t%0, 1(%2)\n" + "2:\tlbu\t$1, 0(%2)\n\t" #endif - "sll\t%0,0x8\n\t" - "or\t%0,$1\n\t" - ".set\tat\n\t" + "sll\t%0, 0x8\n\t" + "or\t%0, $1\n\t" + "li\t%1, 0\n" + "3:\t.set\tat\n\t" + ".section\t.fixup,\"ax\"\n\t" + "4:\tli\t%1, %3\n\t" + "j\t3b\n\t" + ".previous\n\t" ".section\t__ex_table,\"a\"\n\t" - STR(PTR)"\t1b,%2\n\t" - STR(PTR)"\t2b,%2\n\t" + STR(PTR)"\t1b, 4b\n\t" + STR(PTR)"\t2b, 4b\n\t" ".previous" - :"=&r" (value) - :"r" (addr), "i" (&&fault) - :"$1"); - regs->regs[insn.i_format.rt] = value; - return; + : "=&r" (value), "=r" (res) + : "r" (addr), "i" (-EFAULT)); + if (res) + goto fault; + *newvalue = value; + *regptr = ®s->regs[insn.i_format.rt]; + break; case lw_op: - check_axs(pc, addr, 4); - __asm__( + if (verify_area(VERIFY_READ, addr, 4)) + goto sigbus; + + __asm__ __volatile__ ( #ifdef __BIG_ENDIAN - "1:\tlwl\t%0,(%1)\n" - "2:\tlwr\t%0,3(%1)\n\t" + "1:\tlwl\t%0, (%2)\n" + "2:\tlwr\t%0, 3(%2)\n\t" #endif #ifdef __LITTLE_ENDIAN - "1:\tlwl\t%0,3(%1)\n" - "2:\tlwr\t%0,(%1)\n\t" + "1:\tlwl\t%0, 3(%2)\n" + "2:\tlwr\t%0, (%2)\n\t" #endif + "li\t%1, 0\n" + "3:\t.section\t.fixup,\"ax\"\n\t" + "4:\tli\t%1, %3\n\t" + "j\t3b\n\t" + ".previous\n\t" ".section\t__ex_table,\"a\"\n\t" - STR(PTR)"\t1b,%2\n\t" - STR(PTR)"\t2b,%2\n\t" + STR(PTR)"\t1b, 4b\n\t" + STR(PTR)"\t2b, 4b\n\t" ".previous" - :"=&r" (value) - :"r" (addr), "i" (&&fault)); - regs->regs[insn.i_format.rt] = value; - return; + : "=&r" (value), "=r" (res) + : "r" (addr), "i" (-EFAULT)); + if (res) + goto fault; + *newvalue = value; + *regptr = ®s->regs[insn.i_format.rt]; + break; case lhu_op: - check_axs(pc, addr, 2); - __asm__( + if (verify_area(VERIFY_READ, addr, 2)) + goto sigbus; + + __asm__ __volatile__ ( ".set\tnoat\n" #ifdef __BIG_ENDIAN - "1:\tlbu\t%0,0(%1)\n" - "2:\tlbu\t$1,1(%1)\n\t" + "1:\tlbu\t%0, 0(%2)\n" + "2:\tlbu\t$1, 1(%2)\n\t" #endif #ifdef __LITTLE_ENDIAN - "1:\tlbu\t%0,1(%1)\n" - "2:\tlbu\t$1,0(%1)\n\t" + "1:\tlbu\t%0, 1(%2)\n" + "2:\tlbu\t$1, 0(%2)\n\t" #endif - "sll\t%0,0x8\n\t" - "or\t%0,$1\n\t" - ".set\tat\n\t" + "sll\t%0, 0x8\n\t" + "or\t%0, $1\n\t" + "li\t%1, 0\n" + "3:\t.set\tat\n\t" + ".section\t.fixup,\"ax\"\n\t" + "4:\tli\t%1, %3\n\t" + "j\t3b\n\t" + ".previous\n\t" ".section\t__ex_table,\"a\"\n\t" - STR(PTR)"\t1b,%2\n\t" - STR(PTR)"\t2b,%2\n\t" + STR(PTR)"\t1b, 4b\n\t" + STR(PTR)"\t2b, 4b\n\t" ".previous" - :"=&r" (value) - :"r" (addr), "i" (&&fault) - :"$1"); - regs->regs[insn.i_format.rt] = value; - return; + : "=&r" (value), "=r" (res) + : "r" (addr), "i" (-EFAULT)); + if (res) + goto fault; + *newvalue = value; + *regptr = ®s->regs[insn.i_format.rt]; + break; case lwu_op: - check_axs(pc, addr, 4); - __asm__( +#ifdef CONFIG_MIPS64 + /* + * A 32-bit kernel might be running on a 64-bit processor. But + * if we're on a 32-bit processor and an i-cache incoherency + * or race makes us see a 64-bit instruction here the sdl/sdr + * would blow up, so for now we don't handle unaligned 64-bit + * instructions on 32-bit kernels. + */ + if (verify_area(VERIFY_READ, addr, 4)) + goto sigbus; + + __asm__ __volatile__ ( #ifdef __BIG_ENDIAN - "1:\tlwl\t%0,(%1)\n" - "2:\tlwr\t%0,3(%1)\n\t" + "1:\tlwl\t%0, (%2)\n" + "2:\tlwr\t%0, 3(%2)\n\t" #endif #ifdef __LITTLE_ENDIAN - "1:\tlwl\t%0,3(%1)\n" - "2:\tlwr\t%0,(%1)\n\t" + "1:\tlwl\t%0, 3(%2)\n" + "2:\tlwr\t%0, (%2)\n\t" #endif + "dsll\t%0, %0, 32\n\t" + "dsrl\t%0, %0, 32\n\t" + "li\t%1, 0\n" + "3:\t.section\t.fixup,\"ax\"\n\t" + "4:\tli\t%1, %3\n\t" + "j\t3b\n\t" + ".previous\n\t" ".section\t__ex_table,\"a\"\n\t" - STR(PTR)"\t1b,%2\n\t" - STR(PTR)"\t2b,%2\n\t" + STR(PTR)"\t1b, 4b\n\t" + STR(PTR)"\t2b, 4b\n\t" ".previous" - :"=&r" (value) - :"r" (addr), "i" (&&fault)); - value &= 0xffffffff; - regs->regs[insn.i_format.rt] = value; - return; + : "=&r" (value), "=r" (res) + : "r" (addr), "i" (-EFAULT)); + if (res) + goto fault; + *newvalue = value; + *regptr = ®s->regs[insn.i_format.rt]; + break; +#endif /* CONFIG_MIPS64 */ + + /* Cannot handle 64-bit instructions in 32-bit kernel */ + goto sigill; case ld_op: - check_axs(pc, addr, 8); - __asm__( - ".set\tmips3\n" +#ifdef CONFIG_MIPS64 + /* + * A 32-bit kernel might be running on a 64-bit processor. But + * if we're on a 32-bit processor and an i-cache incoherency + * or race makes us see a 64-bit instruction here the sdl/sdr + * would blow up, so for now we don't handle unaligned 64-bit + * instructions on 32-bit kernels. + */ + if (verify_area(VERIFY_READ, addr, 8)) + goto sigbus; + + __asm__ __volatile__ ( #ifdef __BIG_ENDIAN - "1:\tldl\t%0,(%1)\n" - "2:\tldr\t%0,7(%1)\n\t" + "1:\tldl\t%0, (%2)\n" + "2:\tldr\t%0, 7(%2)\n\t" #endif #ifdef __LITTLE_ENDIAN - "1:\tldl\t%0,7(%1)\n" - "2:\tldr\t%0,(%1)\n\t" + "1:\tldl\t%0, 7(%2)\n" + "2:\tldr\t%0, (%2)\n\t" #endif - ".set\tmips0\n\t" + "li\t%1, 0\n" + "3:\t.section\t.fixup,\"ax\"\n\t" + "4:\tli\t%1, %3\n\t" + "j\t3b\n\t" + ".previous\n\t" ".section\t__ex_table,\"a\"\n\t" - STR(PTR)"\t1b,%2\n\t" - STR(PTR)"\t2b,%2\n\t" + STR(PTR)"\t1b, 4b\n\t" + STR(PTR)"\t2b, 4b\n\t" ".previous" - :"=&r" (value) - :"r" (addr), "i" (&&fault)); - regs->regs[insn.i_format.rt] = value; - return; + : "=&r" (value), "=r" (res) + : "r" (addr), "i" (-EFAULT)); + if (res) + goto fault; + *newvalue = value; + *regptr = ®s->regs[insn.i_format.rt]; + break; +#endif /* CONFIG_MIPS64 */ + + /* Cannot handle 64-bit instructions in 32-bit kernel */ + goto sigill; case sh_op: - check_axs(pc, addr, 2); + if (verify_area(VERIFY_WRITE, addr, 2)) + goto sigbus; + value = regs->regs[insn.i_format.rt]; - __asm__( + __asm__ __volatile__ ( #ifdef __BIG_ENDIAN ".set\tnoat\n" - "1:\tsb\t%0,1(%1)\n\t" - "srl\t$1,%0,0x8\n" - "2:\tsb\t$1,0(%1)\n\t" + "1:\tsb\t%1, 1(%2)\n\t" + "srl\t$1, %1, 0x8\n" + "2:\tsb\t$1, 0(%2)\n\t" ".set\tat\n\t" #endif #ifdef __LITTLE_ENDIAN ".set\tnoat\n" - "1:\tsb\t%0,0(%1)\n\t" - "srl\t$1,%0,0x8\n" - "2:\tsb\t$1,1(%1)\n\t" + "1:\tsb\t%1, 0(%2)\n\t" + "srl\t$1,%1, 0x8\n" + "2:\tsb\t$1, 1(%2)\n\t" ".set\tat\n\t" #endif + "li\t%0, 0\n" + "3:\n\t" + ".section\t.fixup,\"ax\"\n\t" + "4:\tli\t%0, %3\n\t" + "j\t3b\n\t" + ".previous\n\t" ".section\t__ex_table,\"a\"\n\t" - STR(PTR)"\t1b,%2\n\t" - STR(PTR)"\t2b,%2\n\t" + STR(PTR)"\t1b, 4b\n\t" + STR(PTR)"\t2b, 4b\n\t" ".previous" - : /* no outputs */ - :"r" (value), "r" (addr), "i" (&&fault) - :"$1"); - return; + : "=r" (res) + : "r" (value), "r" (addr), "i" (-EFAULT)); + if (res) + goto fault; + break; case sw_op: - check_axs(pc, addr, 4); + if (verify_area(VERIFY_WRITE, addr, 4)) + goto sigbus; + value = regs->regs[insn.i_format.rt]; - __asm__( + __asm__ __volatile__ ( #ifdef __BIG_ENDIAN - "1:\tswl\t%0,(%1)\n" - "2:\tswr\t%0,3(%1)\n\t" + "1:\tswl\t%1,(%2)\n" + "2:\tswr\t%1, 3(%2)\n\t" #endif #ifdef __LITTLE_ENDIAN - "1:\tswl\t%0,3(%1)\n" - "2:\tswr\t%0,(%1)\n\t" + "1:\tswl\t%1, 3(%2)\n" + "2:\tswr\t%1, (%2)\n\t" #endif + "li\t%0, 0\n" + "3:\n\t" + ".section\t.fixup,\"ax\"\n\t" + "4:\tli\t%0, %3\n\t" + "j\t3b\n\t" + ".previous\n\t" ".section\t__ex_table,\"a\"\n\t" - STR(PTR)"\t1b,%2\n\t" - STR(PTR)"\t2b,%2\n\t" + STR(PTR)"\t1b, 4b\n\t" + STR(PTR)"\t2b, 4b\n\t" ".previous" - : /* no outputs */ - :"r" (value), "r" (addr), "i" (&&fault)); - return; + : "=r" (res) + : "r" (value), "r" (addr), "i" (-EFAULT)); + if (res) + goto fault; + break; case sd_op: - check_axs(pc, addr, 8); +#ifdef CONFIG_MIPS64 + /* + * A 32-bit kernel might be running on a 64-bit processor. But + * if we're on a 32-bit processor and an i-cache incoherency + * or race makes us see a 64-bit instruction here the sdl/sdr + * would blow up, so for now we don't handle unaligned 64-bit + * instructions on 32-bit kernels. + */ + if (verify_area(VERIFY_WRITE, addr, 8)) + goto sigbus; + value = regs->regs[insn.i_format.rt]; - __asm__( - ".set\tmips3\n" + __asm__ __volatile__ ( #ifdef __BIG_ENDIAN - "1:\tsdl\t%0,(%1)\n" - "2:\tsdr\t%0,7(%1)\n\t" + "1:\tsdl\t%1,(%2)\n" + "2:\tsdr\t%1, 7(%2)\n\t" #endif #ifdef __LITTLE_ENDIAN - "1:\tsdl\t%0,7(%1)\n" - "2:\tsdr\t%0,(%1)\n\t" + "1:\tsdl\t%1, 7(%2)\n" + "2:\tsdr\t%1, (%2)\n\t" #endif - ".set\tmips0\n\t" + "li\t%0, 0\n" + "3:\n\t" + ".section\t.fixup,\"ax\"\n\t" + "4:\tli\t%0, %3\n\t" + "j\t3b\n\t" + ".previous\n\t" ".section\t__ex_table,\"a\"\n\t" - STR(PTR)"\t1b,%2\n\t" - STR(PTR)"\t2b,%2\n\t" + STR(PTR)"\t1b, 4b\n\t" + STR(PTR)"\t2b, 4b\n\t" ".previous" - : /* no outputs */ - :"r" (value), "r" (addr), "i" (&&fault)); - return; + : "=r" (res) + : "r" (value), "r" (addr), "i" (-EFAULT)); + if (res) + goto fault; + break; +#endif /* CONFIG_MIPS64 */ + + /* Cannot handle 64-bit instructions in 32-bit kernel */ + goto sigill; case lwc1_op: case ldc1_op: @@ -352,78 +461,97 @@ emulate_load_store_insn(struct pt_regs *regs, */ goto sigill; } - return; + +#ifdef CONFIG_PROC_FS + unaligned_instructions++; +#endif + + return 0; fault: /* Did we have an exception handler installed? */ - fixup = search_exception_table(regs->cp0_epc); + fixup = search_exception_tables(exception_epc(regs)); if (fixup) { - long new_epc; - new_epc = fixup_exception(dpf_reg, fixup, regs->cp0_epc); + unsigned long new_epc = fixup->nextinsn; printk(KERN_DEBUG "%s: Forwarding exception at [<%lx>] (%lx)\n", current->comm, regs->cp0_epc, new_epc); regs->cp0_epc = new_epc; - return; + return 1; } die_if_kernel ("Unhandled kernel unaligned access", regs); send_sig(SIGSEGV, current, 1); - return; + + return 0; + sigbus: - die_if_kernel ("Unhandled kernel unaligned access", regs); + die_if_kernel("Unhandled kernel unaligned access", regs); send_sig(SIGBUS, current, 1); - return; + + return 0; + sigill: - die_if_kernel ("Unhandled kernel unaligned access or invalid instruction", regs); + die_if_kernel("Unhandled kernel unaligned access or invalid instruction", regs); send_sig(SIGILL, current, 1); - return; -} -#ifdef CONFIG_PROC_FS -unsigned long unaligned_instructions; -#endif + return 0; +} asmlinkage void do_ade(struct pt_regs *regs) { - unsigned long pc; + unsigned long *regptr, newval; extern int do_dsemulret(struct pt_regs *); + mm_segment_t seg; + unsigned long pc; - /* - * Address errors may be deliberately induced - * by the FPU emulator to take retake control - * of the CPU after executing the instruction - * in the delay slot of an emulated branch. + /* + * Address errors may be deliberately induced by the FPU emulator to + * retake control of the CPU after executing the instruction in the + * delay slot of an emulated branch. */ - - if ((unsigned long)regs->cp0_epc == current->thread.dsemul_aerpc) { - do_dsemulret(regs); + /* Terminate if exception was recognized as a delay slot return */ + if (do_dsemulret(regs)) return; - } + + /* Otherwise handle as normal */ /* * Did we catch a fault trying to load an instruction? - * This also catches attempts to activate MIPS16 code on - * CPUs which don't support it. + * Or are we running in MIPS16 mode? */ - if (regs->cp0_badvaddr == regs->cp0_epc) + if ((regs->cp0_badvaddr == regs->cp0_epc) || (regs->cp0_epc & 0x1)) goto sigbus; - pc = regs->cp0_epc + ((regs->cp0_cause & CAUSEF_BD) ? 4 : 0); - if (compute_return_epc(regs)) - return; + pc = exception_epc(regs); if ((current->thread.mflags & MF_FIXADE) == 0) goto sigbus; - emulate_load_store_insn(regs, regs->cp0_badvaddr, pc); -#ifdef CONFIG_PROC_FS - unaligned_instructions++; -#endif + /* + * Do branch emulation only if we didn't forward the exception. + * This is all so but ugly ... + */ + seg = get_fs(); + if (!user_mode(regs)) + set_fs(KERNEL_DS); + if (!emulate_load_store_insn(regs, (void *)regs->cp0_badvaddr, pc, + ®ptr, &newval)) { + compute_return_epc(regs); + /* + * Now that branch is evaluated, update the dest + * register if necessary + */ + if (regptr) + *regptr = newval; + } + set_fs(seg); return; sigbus: - die_if_kernel ("Kernel unaligned instruction access", regs); + die_if_kernel("Kernel unaligned instruction access", regs); force_sig(SIGBUS, current); - return; + /* + * XXX On return from the signal handler we should advance the epc + */ } diff --git a/arch/mips/kernel/vm86.c b/arch/mips/kernel/vm86.c deleted file mode 100644 index c0c775fba676..000000000000 --- a/arch/mips/kernel/vm86.c +++ /dev/null @@ -1,13 +0,0 @@ -/* - * arch/mips/vm86.c - * - * Copyright (C) 1994 Waldorf GMBH, - * written by Ralf Baechle - */ -#include <linux/linkage.h> -#include <linux/errno.h> - -asmlinkage int sys_vm86(void *v86) -{ - return -ENOSYS; -} diff --git a/arch/mips/lib/Makefile b/arch/mips/lib/Makefile index 700eb8e7779e..c719b3b9070e 100644 --- a/arch/mips/lib/Makefile +++ b/arch/mips/lib/Makefile @@ -2,19 +2,17 @@ # Makefile for MIPS-specific library files.. # -EXTRA_AFLAGS := $(CFLAGS) - -lib-y += csum_partial.o csum_partial_copy.o \ - rtc-std.o rtc-no.o memcpy.o memset.o \ - watch.o strlen_user.o strncpy_user.o \ - strnlen_user.o +lib-y += csum_partial.o csum_partial_copy.o memcpy.o \ + memset.o promlib.o rtc-std.o rtc-no.o strlen_user.o \ + strncpy_user.o strnlen_user.o watch.o -ifdef CONFIG_CPU_R3000 - lib-y += r3k_dump_tlb.o +ifeq ($(CONFIG_CPU_R3000)$(CONFIG_CPU_TX39XX),y) + lib-y += r3k_dump_tlb.o else - lib-y += dump_tlb.o + lib-y += dump_tlb.o endif lib-$(CONFIG_BLK_DEV_FD) += floppy-no.o floppy-std.o -lib-$(CONFIG_IDE) += ide-std.o ide-no.o -lib-$(CONFIG_PC_KEYB) += kbd-std.o kbd-no.o +lib-$(subst m,y,$(CONFIG_IDE)) += ide-std.o ide-no.o # needed for ide module + +EXTRA_AFLAGS := $(CFLAGS) diff --git a/arch/mips/lib/csum_partial.S b/arch/mips/lib/csum_partial.S index 66fd03031c4f..ef83e7760693 100644 --- a/arch/mips/lib/csum_partial.S +++ b/arch/mips/lib/csum_partial.S @@ -1,5 +1,4 @@ -/* $Id: csum_partial.S,v 1.1 1998/05/04 09:12: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 * for more details. diff --git a/arch/mips/lib/csum_partial_copy.c b/arch/mips/lib/csum_partial_copy.c index 3827f1af34ee..c6b6488402d1 100644 --- a/arch/mips/lib/csum_partial_copy.c +++ b/arch/mips/lib/csum_partial_copy.c @@ -13,8 +13,6 @@ * 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/19 19:16:17 ralf Exp $ */ #include <net/checksum.h> #include <linux/types.h> @@ -25,8 +23,8 @@ /* * copy while checksumming, otherwise like csum_partial */ -unsigned int csum_partial_copy_nocheck(const char *src, char *dst, - int len, unsigned int sum) +unsigned int csum_partial_copy_nocheck(const char *src, char *dst, + int len, unsigned int sum) { /* * It's 2:30 am and I don't feel like doing it real ... @@ -43,8 +41,7 @@ unsigned int csum_partial_copy_nocheck(const char *src, char *dst, * 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 len, unsigned int sum, int *err_ptr) { int missing; @@ -53,6 +50,6 @@ unsigned int csum_partial_copy_from_user (const char *src, char *dst, memset(dst + len - missing, 0, missing); *err_ptr = -EFAULT; } - + return csum_partial(dst, len, sum); } diff --git a/arch/mips/lib/dump_tlb.c b/arch/mips/lib/dump_tlb.c index 0b7073335b42..80d456c99696 100644 --- a/arch/mips/lib/dump_tlb.c +++ b/arch/mips/lib/dump_tlb.c @@ -4,31 +4,48 @@ * Copyright (C) 1994, 1995 by Waldorf Electronics, written by Ralf Baechle. * Copyright (C) 1999 by Silicon Graphics, Inc. */ +#include <linux/config.h> #include <linux/kernel.h> #include <linux/mm.h> #include <linux/sched.h> #include <linux/string.h> #include <asm/bootinfo.h> +#include <asm/cpu.h> #include <asm/cachectl.h> #include <asm/mipsregs.h> #include <asm/page.h> #include <asm/pgtable.h> -#define mips_tlb_entries 48 +static inline const char *msg2str(unsigned int mask) +{ + switch (mask) { + case PM_4K: return "4kb"; + case PM_16K: return "16kb"; + case PM_64K: return "64kb"; + case PM_256K: return "256kb"; +#ifndef CONFIG_CPU_VR41XX + case PM_1M: return "1Mb"; + case PM_4M: return "4Mb"; + case PM_16M: return "16Mb"; + case PM_64M: return "64Mb"; + case PM_256M: return "256Mb"; +#endif + } +} -void -dump_tlb(int first, int last) +void dump_tlb(int first, int last) { int i; unsigned int pagemask, c0, c1, asid; - unsigned long entryhi, entrylo0, entrylo1; + unsigned long long entrylo0, entrylo1; + unsigned long entryhi; - asid = get_entryhi() & 0xff; + asid = read_c0_entryhi() & 0xff; - for(i=first;i<=last;i++) - { - write_32bit_cp0_register(CP0_INDEX, i); + printk("\n"); + for(i=first;i<=last;i++) { + write_c0_index(i); __asm__ __volatile__( ".set\tmips3\n\t" ".set\tnoreorder\n\t" @@ -37,57 +54,53 @@ dump_tlb(int first, int last) "nop;nop;nop;nop\n\t" ".set\treorder\n\t" ".set\tmips0\n\t"); - pagemask = read_32bit_cp0_register(CP0_PAGEMASK); - entryhi = read_32bit_cp0_register(CP0_ENTRYHI); - entrylo0 = read_32bit_cp0_register(CP0_ENTRYLO0); - entrylo1 = read_32bit_cp0_register(CP0_ENTRYLO1); + pagemask = read_c0_pagemask(); + entryhi = read_c0_entryhi(); + entrylo0 = read_c0_entrylo0(); + entrylo1 = read_c0_entrylo1(); - /* Unused entries have a virtual address of KSEG0. */ - if ((entryhi & 0xffffe000) != 0x80000000 + /* Unused entries have a virtual address in KSEG0. */ + if ((entryhi & 0xf0000000) != 0x80000000 && (entryhi & 0xff) == asid) { /* * Only print entries in use */ - printk("Index: %2d pgmask=%08x ", i, pagemask); + printk("Index: %2d pgmask=%s ", i, msg2str(pagemask)); c0 = (entrylo0 >> 3) & 7; c1 = (entrylo1 >> 3) & 7; - printk("va=%08lx asid=%08lx" - " [pa=%06lx c=%d d=%d v=%d g=%ld]" - " [pa=%06lx c=%d d=%d v=%d g=%ld]", - (entryhi & 0xffffe000), - entryhi & 0xff, - entrylo0 & PAGE_MASK, c0, + printk("va=%08lx asid=%02lx\n", + (entryhi & 0xffffe000), (entryhi & 0xff)); + printk("\t\t\t[pa=%08Lx c=%d d=%d v=%d g=%Ld]\n", + (entrylo0 << 6) & PAGE_MASK, c0, (entrylo0 & 4) ? 1 : 0, (entrylo0 & 2) ? 1 : 0, - (entrylo0 & 1), - entrylo1 & PAGE_MASK, c1, + (entrylo0 & 1)); + printk("\t\t\t[pa=%08Lx c=%d d=%d v=%d g=%Ld]\n", + (entrylo1 << 6) & PAGE_MASK, c1, (entrylo1 & 4) ? 1 : 0, (entrylo1 & 2) ? 1 : 0, (entrylo1 & 1)); - + printk("\n"); } } - printk("\n"); - set_entryhi(asid); + write_c0_entryhi(asid); } -void -dump_tlb_all(void) +void dump_tlb_all(void) { - dump_tlb(0, mips_tlb_entries - 1); + dump_tlb(0, current_cpu_data.tlbsize - 1); } -void -dump_tlb_wired(void) +void dump_tlb_wired(void) { int wired; - wired = read_32bit_cp0_register(CP0_WIRED); + wired = read_c0_wired(); printk("Wired: %d", wired); - dump_tlb(0, read_32bit_cp0_register(CP0_WIRED)); + dump_tlb(0, read_c0_wired()); } #define BARRIER \ @@ -103,14 +116,14 @@ dump_tlb_addr(unsigned long addr) int index; local_irq_save(flags); - oldpid = get_entryhi() & 0xff; + oldpid = read_c0_entryhi() & 0xff; BARRIER; - set_entryhi((addr & PAGE_MASK) | oldpid); + write_c0_entryhi((addr & PAGE_MASK) | oldpid); BARRIER; tlb_probe(); BARRIER; - index = get_index(); - set_entryhi(oldpid); + index = read_c0_index(); + write_c0_entryhi(oldpid); local_irq_restore(flags); if (index < 0) { @@ -125,7 +138,7 @@ dump_tlb_addr(unsigned long addr) void dump_tlb_nonwired(void) { - dump_tlb(read_32bit_cp0_register(CP0_WIRED), mips_tlb_entries - 1); + dump_tlb(read_c0_wired(), current_cpu_data.tlbsize - 1); } void @@ -140,12 +153,20 @@ dump_list_process(struct task_struct *t, void *address) addr = (unsigned int) address; printk("Addr == %08x\n", addr); - printk("tasks->mm.pgd == %08x\n", (unsigned int) t->mm->pgd); - - page_dir = pgd_offset(t->mm, 0); + printk("task == %8p\n", t); + printk("task->mm == %8p\n", t->mm); + //printk("tasks->mm.pgd == %08x\n", (unsigned int) t->mm->pgd); + + if (addr > KSEG0) + page_dir = pgd_offset_k(0); + else + page_dir = pgd_offset(t->mm, 0); printk("page_dir == %08x\n", (unsigned int) page_dir); - pgd = pgd_offset(t->mm, addr); + if (addr > KSEG0) + pgd = pgd_offset_k(addr); + else + pgd = pgd_offset(t->mm, addr); printk("pgd == %08x, ", (unsigned int) pgd); pmd = pmd_offset(pgd, addr); @@ -155,7 +176,11 @@ dump_list_process(struct task_struct *t, void *address) printk("pte == %08x, ", (unsigned int) pte); page = *pte; - printk("page == %08x\n", (unsigned int) pte_val(page)); +#ifdef CONFIG_64BIT_PHYS_ADDR + printk("page == %08Lx\n", pte_val(page)); +#else + printk("page == %08lx\n", pte_val(page)); +#endif val = pte_val(page); if (val & _PAGE_PRESENT) printk("present "); @@ -200,9 +225,7 @@ dump16(unsigned long *p) for(i=0;i<8;i++) { - printk("*%08lx == %08lx, ", - (unsigned long)p, (unsigned long)*p++); - printk("*%08lx == %08lx\n", - (unsigned long)p, (unsigned long)*p++); + printk("*%8p = %08lx, ", p, *p); p++; + printk("*%8p = %08lx\n", p, *p); p++; } } diff --git a/arch/mips/lib/floppy-no.c b/arch/mips/lib/floppy-no.c index fd1b47db14e6..b87110e891bb 100644 --- a/arch/mips/lib/floppy-no.c +++ b/arch/mips/lib/floppy-no.c @@ -1,5 +1,4 @@ -/* $Id: floppy-no.c,v 1.1 1998/05/07 18:38:32 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. diff --git a/arch/mips/lib/floppy-std.c b/arch/mips/lib/floppy-std.c index ada280d55ed0..ee7a895a5d29 100644 --- a/arch/mips/lib/floppy-std.c +++ b/arch/mips/lib/floppy-std.c @@ -1,5 +1,4 @@ -/* $Id: floppy-std.c,v 1.2 1999/01/04 16:03:51 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. @@ -20,7 +19,6 @@ #include <asm/cachectl.h> #include <asm/dma.h> #include <asm/floppy.h> -#include <asm/keyboard.h> #include <asm/io.h> #include <asm/irq.h> #include <asm/mc146818rtc.h> @@ -112,8 +110,8 @@ static unsigned long std_fd_dma_mem_alloc(unsigned long size) } static void std_fd_dma_mem_free(unsigned long addr, unsigned long size) -{ - free_pages(addr, get_order(size)); +{ + free_pages(addr, get_order(size)); } static unsigned long std_fd_drive_type(unsigned long n) diff --git a/arch/mips/lib/ide-no.c b/arch/mips/lib/ide-no.c index d65cc5dde129..4cd4d9475b1f 100644 --- a/arch/mips/lib/ide-no.c +++ b/arch/mips/lib/ide-no.c @@ -1,5 +1,4 @@ -/* $Id: ide-no.c,v 1.2 1998/06/30 00:21:54 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. @@ -13,7 +12,6 @@ #include <linux/kernel.h> #include <linux/ide.h> #include <asm/hdreg.h> -#include <asm/ptrace.h> static int no_ide_default_irq(ide_ioreg_t base) { diff --git a/arch/mips/lib/ide-std.c b/arch/mips/lib/ide-std.c index 34f959439c3c..28e8eee8cb81 100644 --- a/arch/mips/lib/ide-std.c +++ b/arch/mips/lib/ide-std.c @@ -11,7 +11,6 @@ #include <linux/ide.h> #include <linux/ioport.h> #include <linux/hdreg.h> -#include <asm/ptrace.h> #include <asm/hdreg.h> static int std_ide_default_irq(ide_ioreg_t base) @@ -30,16 +29,11 @@ static int std_ide_default_irq(ide_ioreg_t base) static ide_ioreg_t std_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 unsigned long ata_io_base[MAX_HWIFS] = { + 0x1f0, 0x170, 0x1e8, 0x168, 0x1e0, 0x160 + }; + + return ata_io_base[index]; } static void std_ide_init_hwif_ports (hw_regs_t *hw, ide_ioreg_t data_port, diff --git a/arch/mips/lib/kbd-no.c b/arch/mips/lib/kbd-no.c deleted file mode 100644 index c94e8c000648..000000000000 --- a/arch/mips/lib/kbd-no.c +++ /dev/null @@ -1,63 +0,0 @@ -/* $Id: kbd-no.c,v 1.1 1998/10/28 12:38:14 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. - * - * Stub keyboard and psaux routines to keep Linux from crashing on machines - * without a keyboard. - * - * Copyright (C) 1998 by Ralf Baechle - */ -#include <linux/sched.h> -#include <asm/keyboard.h> - -static void no_kbd_request_region(void) -{ - /* No I/O ports are being used on the Indy. */ -} - -static int no_kbd_request_irq(void (*handler)(int, void *, struct pt_regs *)) -{ - return -ENODEV; -} - -static int no_aux_request_irq(void (*handler)(int, void *, struct pt_regs *)) -{ - return -ENODEV; -} - -static void no_aux_free_irq(void) -{ -} - -static unsigned char no_kbd_read_input(void) -{ - return 0; -} - -static void no_kbd_write_output(unsigned char val) -{ -} - -static void no_kbd_write_command(unsigned char val) -{ -} - -static unsigned char no_kbd_read_status(void) -{ - return 0; -} - -struct kbd_ops no_kbd_ops = { - no_kbd_request_region, - no_kbd_request_irq, - - no_aux_request_irq, - no_aux_free_irq, - - no_kbd_read_input, - no_kbd_write_output, - no_kbd_write_command, - no_kbd_read_status -}; diff --git a/arch/mips/lib/kbd-std.c b/arch/mips/lib/kbd-std.c deleted file mode 100644 index ecaa2a0d88ab..000000000000 --- a/arch/mips/lib/kbd-std.c +++ /dev/null @@ -1,85 +0,0 @@ -/* $Id: kbd-std.c,v 1.2 1999/06/11 14:29:45 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. - * - * Routines for standard PC style keyboards accessible via I/O ports. - * - * Copyright (C) 1998, 1999 by Ralf Baechle - */ -#include <linux/ioport.h> -#include <linux/sched.h> -#include <linux/pc_keyb.h> -#include <asm/keyboard.h> -#include <asm/io.h> - -#define KEYBOARD_IRQ 1 -#define AUX_IRQ 12 - -static void std_kbd_request_region(void) -{ -#ifdef CONFIG_MIPS_ITE8172 - request_region(0x14000060, 16, "keyboard"); -#else - request_region(0x60, 16, "keyboard"); -#endif -} - -static int std_kbd_request_irq(void (*handler)(int, void *, struct pt_regs *)) -{ - return request_irq(KEYBOARD_IRQ, handler, 0, "keyboard", NULL); -} - -static int std_aux_request_irq(void (*handler)(int, void *, struct pt_regs *)) -{ - return request_irq(AUX_IRQ, handler, 0, "PS/2 Mouse", NULL); -} - -static void std_aux_free_irq(void) -{ - free_irq(AUX_IRQ, NULL); -} - -static unsigned char std_kbd_read_input(void) -{ - return inb(KBD_DATA_REG); -} - -static void std_kbd_write_output(unsigned char val) -{ - int status; - - do { - status = inb(KBD_CNTL_REG); - } while (status & KBD_STAT_IBF); - outb(val, KBD_DATA_REG); -} - -static void std_kbd_write_command(unsigned char val) -{ - int status; - - do { - status = inb(KBD_CNTL_REG); - } while (status & KBD_STAT_IBF); - outb(val, KBD_CNTL_REG); -} - -static unsigned char std_kbd_read_status(void) -{ - return inb(KBD_STATUS_REG); -} - -struct kbd_ops std_kbd_ops = { - std_kbd_request_region, - std_kbd_request_irq, - - std_aux_request_irq, - std_aux_free_irq, - - std_kbd_read_input, - std_kbd_write_output, - std_kbd_write_command, - std_kbd_read_status -}; diff --git a/arch/mips/lib/memcpy.S b/arch/mips/lib/memcpy.S index 5a225e849077..07145dece56b 100644 --- a/arch/mips/lib/memcpy.S +++ b/arch/mips/lib/memcpy.S @@ -4,407 +4,468 @@ * for more details. * * Unified implementation of memcpy, memmove and the __copy_user backend. - * For __rmemcpy and memmove an exception is always a kernel bug, therefore - * they're not protected. In order to keep the exception fixup routine - * simple all memory accesses in __copy_user to src rsp. dst are stricly - * incremental. The fixup routine depends on $at not being changed. + * + * Copyright (C) 1998, 99, 2000, 01, 2002 Ralf Baechle (ralf@gnu.org) + * Copyright (C) 1999, 2000, 01, 2002 Silicon Graphics, Inc. + * Copyright (C) 2002 Broadcom, Inc. + * memcpy/copy_user author: Mark Vandevoorde + * + * Mnemonic names for arguments to memcpy/__copy_user */ +#include <linux/config.h> #include <asm/asm.h> #include <asm/offset.h> #include <asm/regdef.h> +#define dst a0 +#define src a1 +#define len a2 + /* - * The fixup routine for copy_to_user depends on copying strictly in - * increasing order. Gas expands the ulw/usw macros in the wrong order for - * little endian machines, so we cannot depend on them. + * Spec + * + * memcpy copies len bytes from src to dst and sets v0 to dst. + * It assumes that + * - src and dst don't overlap + * - src is readable + * - dst is writable + * memcpy uses the standard calling convention + * + * __copy_user copies up to len bytes from src to dst and sets a2 (len) to + * the number of uncopied bytes due to an exception caused by a read or write. + * __copy_user assumes that src and dst don't overlap, and that the call is + * implementing one of the following: + * copy_to_user + * - src is readable (no exceptions when reading src) + * copy_from_user + * - dst is writable (no exceptions when writing dst) + * __copy_user uses a non-standard calling convention; see + * include/asm-mips/uaccess.h + * + * When an exception happens on a load, the handler must + # ensure that all of the destination buffer is overwritten to prevent + * leaking information to user mode programs. */ -#ifdef __MIPSEB__ -#define uswL swl -#define uswU swr -#define ulwL lwl -#define ulwU lwr -#endif -#ifdef __MIPSEL__ -#define uswL swr -#define uswU swl -#define ulwL lwr -#define ulwU lwl -#endif -#define EX(insn,reg,addr,handler) \ -9: insn reg, addr; \ - .section __ex_table,"a"; \ - PTR 9b, handler; \ - .previous +/* + * Implementation + */ -#define UEX(insn,reg,addr,handler) \ -9: insn ## L reg, addr; \ -10: insn ## U reg, 3 + addr; \ - .section __ex_table,"a"; \ - PTR 9b, handler; \ - PTR 10b, handler; \ +/* + * The exception handler for loads requires that: + * 1- AT contain the address of the byte just past the end of the source + * of the copy, + * 2- src_entry <= src < AT, and + * 3- (dst - src) == (dst_entry - src_entry), + * The _entry suffix denotes values when __copy_user was called. + * + * (1) is set up up by uaccess.h and maintained by not writing AT in copy_user + * (2) is met by incrementing src by the number of bytes copied + * (3) is met by not doing loads between a pair of increments of dst and src + * + * The exception handlers for stores adjust len (if necessary) and return. + * These handlers do not need to overwrite any data. + * + * For __rmemcpy and memmove an exception is always a kernel bug, therefore + * they're not protected. + */ + +#define EXC(inst_reg,addr,handler) \ +9: inst_reg, addr; \ + .section __ex_table,"a"; \ + PTR 9b, handler; \ .previous -/* ascending order, destination aligned */ -#define MOVE_BIGCHUNK(src, dst, offset, t0, t1, t2, t3) \ - EX(lw, t0, (offset + 0x00)(src), l_fixup); \ - EX(lw, t1, (offset + 0x04)(src), l_fixup); \ - EX(lw, t2, (offset + 0x08)(src), l_fixup); \ - EX(lw, t3, (offset + 0x0c)(src), l_fixup); \ - EX(sw, t0, (offset + 0x00)(dst), s_fixup); \ - EX(sw, t1, (offset + 0x04)(dst), s_fixup); \ - EX(sw, t2, (offset + 0x08)(dst), s_fixup); \ - EX(sw, t3, (offset + 0x0c)(dst), s_fixup); \ - EX(lw, t0, (offset + 0x10)(src), l_fixup); \ - EX(lw, t1, (offset + 0x14)(src), l_fixup); \ - EX(lw, t2, (offset + 0x18)(src), l_fixup); \ - EX(lw, t3, (offset + 0x1c)(src), l_fixup); \ - EX(sw, t0, (offset + 0x10)(dst), s_fixup); \ - EX(sw, t1, (offset + 0x14)(dst), s_fixup); \ - EX(sw, t2, (offset + 0x18)(dst), s_fixup); \ - EX(sw, t3, (offset + 0x1c)(dst), s_fixup) - -/* ascending order, destination unaligned */ -#define UMOVE_BIGCHUNK(src, dst, offset, t0, t1, t2, t3) \ - EX(lw, t0, (offset + 0x00)(src), l_fixup); \ - EX(lw, t1, (offset + 0x04)(src), l_fixup); \ - EX(lw, t2, (offset + 0x08)(src), l_fixup); \ - EX(lw, t3, (offset + 0x0c)(src), l_fixup); \ - UEX(usw, t0, (offset + 0x00)(dst), s_fixup); \ - UEX(usw, t1, (offset + 0x04)(dst), s_fixup); \ - UEX(usw, t2, (offset + 0x08)(dst), s_fixup); \ - UEX(usw, t3, (offset + 0x0c)(dst), s_fixup); \ - EX(lw, t0, (offset + 0x10)(src), l_fixup); \ - EX(lw, t1, (offset + 0x14)(src), l_fixup); \ - EX(lw, t2, (offset + 0x18)(src), l_fixup); \ - EX(lw, t3, (offset + 0x1c)(src), l_fixup); \ - UEX(usw, t0, (offset + 0x10)(dst), s_fixup); \ - UEX(usw, t1, (offset + 0x14)(dst), s_fixup); \ - UEX(usw, t2, (offset + 0x18)(dst), s_fixup); \ - UEX(usw, t3, (offset + 0x1c)(dst), s_fixup) +/* + * Only on the 64-bit kernel we can made use of 64-bit registers. + */ +#ifdef CONFIG_MIPS64 +#define USE_DOUBLE +#endif + +#ifdef USE_DOUBLE + +#define LOAD ld +#define LOADL ldl +#define LOADR ldr +#define STOREL sdl +#define STORER sdr +#define STORE sd +#define ADD daddu +#define SUB dsubu +#define SRL dsrl +#define SRA dsra +#define SLL dsll +#define SLLV dsllv +#define SRLV dsrlv +#define NBYTES 8 +#define LOG_NBYTES 3 + +/* + * As we are sharing code base with the mips32 tree (which use the o32 ABI + * register definitions). We need to redefine the register definitions from + * the n64 ABI register naming to the o32 ABI register naming. + */ +#undef t0 +#undef t1 +#undef t2 +#undef t3 +#define t0 $8 +#define t1 $9 +#define t2 $10 +#define t3 $11 +#define t4 $12 +#define t5 $13 +#define t6 $14 +#define t7 $15 + +#else + +#define LOAD lw +#define LOADL lwl +#define LOADR lwr +#define STOREL swl +#define STORER swr +#define STORE sw +#define ADD addu +#define SUB subu +#define SRL srl +#define SLL sll +#define SRA sra +#define SLLV sllv +#define SRLV srlv +#define NBYTES 4 +#define LOG_NBYTES 2 + +#endif /* USE_DOUBLE */ + +#ifdef CONFIG_CPU_LITTLE_ENDIAN +#define LDFIRST LOADR +#define LDREST LOADL +#define STFIRST STORER +#define STREST STOREL +#define SHIFT_DISCARD SLLV +#else +#define LDFIRST LOADL +#define LDREST LOADR +#define STFIRST STOREL +#define STREST STORER +#define SHIFT_DISCARD SRLV +#endif + +#define FIRST(unit) ((unit)*NBYTES) +#define REST(unit) (FIRST(unit)+NBYTES-1) +#define UNIT(unit) FIRST(unit) + +#define ADDRMASK (NBYTES-1) .text .set noreorder .set noat +/* + * A combined memcpy/__copy_user + * __copy_user sets len to 0 for success; else to an upper bound of + * the number of uncopied bytes. + * memcpy sets v0 to dst. + */ .align 5 LEAF(memcpy) /* a0=dst a1=src a2=len */ - move v0, a0 /* return value */ + move v0, dst /* return value */ __memcpy: -EXPORT(__copy_user) - xor t0, a0, a1 - andi t0, t0, 0x3 - move t7, a0 - beqz t0, can_align - sltiu t8, a2, 0x8 - - b memcpy_u_src # bad alignment - move t2, a2 - -can_align: - bnez t8, small_memcpy # < 8 bytes to copy - move t2, a2 - - beqz a2, out - andi t8, a1, 0x1 - -hword_align: - beqz t8, word_align - andi t8, a1, 0x2 - - EX(lb, t0, (a1), l_fixup) - subu a2, a2, 0x1 - EX(sb, t0, (a0), s_fixup) - addu a1, a1, 0x1 - addu a0, a0, 0x1 - andi t8, a1, 0x2 - -word_align: - beqz t8, dword_align - sltiu t8, a2, 56 - - EX(lh, t0, (a1), l_fixup) - subu a2, a2, 0x2 - EX(sh, t0, (a0), s_fixup) - sltiu t8, a2, 56 - addu a0, a0, 0x2 - addu a1, a1, 0x2 - -dword_align: - bnez t8, do_end_words - move t8, a2 - - andi t8, a1, 0x4 - beqz t8, qword_align - andi t8, a1, 0x8 - - EX(lw, t0, 0x00(a1), l_fixup) - subu a2, a2, 0x4 - EX(sw, t0, 0x00(a0), s_fixup) - addu a1, a1, 0x4 - addu a0, a0, 0x4 - andi t8, a1, 0x8 - -qword_align: - beqz t8, oword_align - andi t8, a1, 0x10 - - EX(lw, t0, 0x00(a1), l_fixup) - EX(lw, t1, 0x04(a1), l_fixup) - subu a2, a2, 0x8 - EX(sw, t0, 0x00(a0), s_fixup) - EX(sw, t1, 0x04(a0), s_fixup) - addu a1, a1, 0x8 - andi t8, a1, 0x10 - addu a0, a0, 0x8 - -oword_align: - beqz t8, begin_movement - srl t8, a2, 0x7 - - EX(lw, t3, 0x00(a1), l_fixup) - EX(lw, t4, 0x04(a1), l_fixup) - EX(lw, t0, 0x08(a1), l_fixup) - EX(lw, t1, 0x0c(a1), l_fixup) - EX(sw, t3, 0x00(a0), s_fixup) - EX(sw, t4, 0x04(a0), s_fixup) - EX(sw, t0, 0x08(a0), s_fixup) - EX(sw, t1, 0x0c(a0), s_fixup) - subu a2, a2, 0x10 - addu a1, a1, 0x10 - srl t8, a2, 0x7 - addu a0, a0, 0x10 - -begin_movement: - beqz t8, 0f - andi t2, a2, 0x40 - -move_128bytes: - MOVE_BIGCHUNK(a1, a0, 0x00, t0, t1, t3, t4) - MOVE_BIGCHUNK(a1, a0, 0x20, t0, t1, t3, t4) - MOVE_BIGCHUNK(a1, a0, 0x40, t0, t1, t3, t4) - MOVE_BIGCHUNK(a1, a0, 0x60, t0, t1, t3, t4) - subu t8, t8, 0x01 - addu a1, a1, 0x80 - bnez t8, move_128bytes - addu a0, a0, 0x80 - -0: - beqz t2, 1f - andi t2, a2, 0x20 - -move_64bytes: - MOVE_BIGCHUNK(a1, a0, 0x00, t0, t1, t3, t4) - MOVE_BIGCHUNK(a1, a0, 0x20, t0, t1, t3, t4) - addu a1, a1, 0x40 - addu a0, a0, 0x40 - +FEXPORT(__copy_user) + /* + * Note: dst & src may be unaligned, len may be 0 + * Temps + */ +#define rem t8 + + /* + * The "issue break"s below are very approximate. + * Issue delays for dcache fills will perturb the schedule, as will + * load queue full replay traps, etc. + * + * If len < NBYTES use byte operations. + */ + PREF( 0, 0(src) ) + PREF( 1, 0(dst) ) + sltu t2, len, NBYTES + and t1, dst, ADDRMASK + PREF( 0, 1*32(src) ) + PREF( 1, 1*32(dst) ) + bnez t2, copy_bytes_checklen + and t0, src, ADDRMASK + PREF( 0, 2*32(src) ) + PREF( 1, 2*32(dst) ) + bnez t1, dst_unaligned + nop + bnez t0, src_unaligned_dst_aligned + /* + * use delay slot for fall-through + * src and dst are aligned; need to compute rem + */ +both_aligned: + SRL t0, len, LOG_NBYTES+3 # +3 for 8 units/iter + beqz t0, cleanup_both_aligned # len < 8*NBYTES + and rem, len, (8*NBYTES-1) # rem = len % (8*NBYTES) + PREF( 0, 3*32(src) ) + PREF( 1, 3*32(dst) ) + .align 4 1: - beqz t2, do_end_words - andi t8, a2, 0x1c - -move_32bytes: - MOVE_BIGCHUNK(a1, a0, 0x00, t0, t1, t3, t4) - andi t8, a2, 0x1c - addu a1, a1, 0x20 - addu a0, a0, 0x20 - -do_end_words: - beqz t8, maybe_end_cruft - srl t8, t8, 0x2 - -end_words: - EX(lw, t0, (a1), l_fixup) - subu t8, t8, 0x1 - EX(sw, t0, (a0), s_fixup) - addu a1, a1, 0x4 - bnez t8, end_words - addu a0, a0, 0x4 - -maybe_end_cruft: - andi t2, a2, 0x3 - -small_memcpy: - beqz t2, out - move a2, t2 - -end_bytes: - EX(lb, t0, (a1), l_fixup) - subu a2, a2, 0x1 - EX(sb, t0, (a0), s_fixup) - addu a1, a1, 0x1 - bnez a2, end_bytes - addu a0, a0, 0x1 - -out: jr ra - move a2, zero - -/* ------------------------------------------------------------------------- */ - -/* Bad, bad. At least try to align the source */ - -memcpy_u_src: - bnez t8, small_memcpy # < 8 bytes? - move t2, a2 - - addiu t0, a1, 7 # t0: how much to align - ori t0, 7 - xori t0, 7 - subu t0, a1 - - UEX(ulw, t1, 0(a1), l_fixup) # dword alignment - UEX(ulw, t2, 4(a1), l_fixup) - UEX(usw, t1, 0(a0), s_fixup) - UEX(usw, t2, 4(a0), s_fixup) - - addu a1, t0 # src - addu a0, t0 # dst - subu a2, t0 # len - - sltiu t8, a2, 56 - bnez t8, u_do_end_words - andi t8, a2, 0x3c - - andi t8, a1, 8 # now qword aligned? - -u_qword_align: - beqz t8, u_oword_align - andi t8, a1, 0x10 - - EX(lw, t0, 0x00(a1), l_fixup) - EX(lw, t1, 0x04(a1), l_fixup) - subu a2, a2, 0x8 - UEX(usw, t0, 0x00(a0), s_fixup) - UEX(usw, t1, 0x04(a0), s_fixup) - addu a1, a1, 0x8 - andi t8, a1, 0x10 - addu a0, a0, 0x8 - -u_oword_align: - beqz t8, u_begin_movement - srl t8, a2, 0x7 - - EX(lw, t3, 0x08(a1), l_fixup) - EX(lw, t4, 0x0c(a1), l_fixup) - EX(lw, t0, 0x00(a1), l_fixup) - EX(lw, t1, 0x04(a1), l_fixup) - UEX(usw, t3, 0x08(a0), s_fixup) - UEX(usw, t4, 0x0c(a0), s_fixup) - UEX(usw, t0, 0x00(a0), s_fixup) - UEX(usw, t1, 0x04(a0), s_fixup) - subu a2, a2, 0x10 - addu a1, a1, 0x10 - srl t8, a2, 0x7 - addu a0, a0, 0x10 - -u_begin_movement: - beqz t8, 0f - andi t2, a2, 0x40 - -u_move_128bytes: - UMOVE_BIGCHUNK(a1, a0, 0x00, t0, t1, t3, t4) - UMOVE_BIGCHUNK(a1, a0, 0x20, t0, t1, t3, t4) - UMOVE_BIGCHUNK(a1, a0, 0x40, t0, t1, t3, t4) - UMOVE_BIGCHUNK(a1, a0, 0x60, t0, t1, t3, t4) - subu t8, t8, 0x01 - addu a1, a1, 0x80 - bnez t8, u_move_128bytes - addu a0, a0, 0x80 - -0: - beqz t2, 1f - andi t2, a2, 0x20 - -u_move_64bytes: - UMOVE_BIGCHUNK(a1, a0, 0x00, t0, t1, t3, t4) - UMOVE_BIGCHUNK(a1, a0, 0x20, t0, t1, t3, t4) - addu a1, a1, 0x40 - addu a0, a0, 0x40 +EXC( LOAD t0, UNIT(0)(src), l_exc) +EXC( LOAD t1, UNIT(1)(src), l_exc_copy) +EXC( LOAD t2, UNIT(2)(src), l_exc_copy) +EXC( LOAD t3, UNIT(3)(src), l_exc_copy) + SUB len, len, 8*NBYTES +EXC( LOAD t4, UNIT(4)(src), l_exc_copy) +EXC( LOAD t7, UNIT(5)(src), l_exc_copy) +EXC( STORE t0, UNIT(0)(dst), s_exc_p8u) +EXC( STORE t1, UNIT(1)(dst), s_exc_p7u) +EXC( LOAD t0, UNIT(6)(src), l_exc_copy) +EXC( LOAD t1, UNIT(7)(src), l_exc_copy) + ADD src, src, 8*NBYTES + ADD dst, dst, 8*NBYTES +EXC( STORE t2, UNIT(-6)(dst), s_exc_p6u) +EXC( STORE t3, UNIT(-5)(dst), s_exc_p5u) +EXC( STORE t4, UNIT(-4)(dst), s_exc_p4u) +EXC( STORE t7, UNIT(-3)(dst), s_exc_p3u) +EXC( STORE t0, UNIT(-2)(dst), s_exc_p2u) +EXC( STORE t1, UNIT(-1)(dst), s_exc_p1u) + PREF( 0, 8*32(src) ) + PREF( 1, 8*32(dst) ) + bne len, rem, 1b + nop + /* + * len == rem == the number of bytes left to copy < 8*NBYTES + */ +cleanup_both_aligned: + beqz len, done + sltu t0, len, 4*NBYTES + bnez t0, less_than_4units + and rem, len, (NBYTES-1) # rem = len % NBYTES + /* + * len >= 4*NBYTES + */ +EXC( LOAD t0, UNIT(0)(src), l_exc) +EXC( LOAD t1, UNIT(1)(src), l_exc_copy) +EXC( LOAD t2, UNIT(2)(src), l_exc_copy) +EXC( LOAD t3, UNIT(3)(src), l_exc_copy) + SUB len, len, 4*NBYTES + ADD src, src, 4*NBYTES +EXC( STORE t0, UNIT(0)(dst), s_exc_p4u) +EXC( STORE t1, UNIT(1)(dst), s_exc_p3u) +EXC( STORE t2, UNIT(2)(dst), s_exc_p2u) +EXC( STORE t3, UNIT(3)(dst), s_exc_p1u) + beqz len, done + ADD dst, dst, 4*NBYTES +less_than_4units: + /* + * rem = len % NBYTES + */ + beq rem, len, copy_bytes + nop 1: - beqz t2, u_do_end_words - andi t8, a2, 0x1c - -u_move_32bytes: - UMOVE_BIGCHUNK(a1, a0, 0x00, t0, t1, t3, t4) - andi t8, a2, 0x1c - addu a1, a1, 0x20 - addu a0, a0, 0x20 - -u_do_end_words: - beqz t8, u_maybe_end_cruft - srl t8, t8, 0x2 - -u_end_words: - EX(lw, t0, 0x00(a1), l_fixup) - subu t8, t8, 0x1 - UEX(usw, t0, 0x00(a0), s_fixup) - addu a1, a1, 0x4 - bnez t8, u_end_words - addu a0, a0, 0x4 - -u_maybe_end_cruft: - andi t2, a2, 0x3 - -u_cannot_optimize: - beqz t2, out - move a2, t2 - -u_end_bytes: - EX(lb, t0, (a1), l_fixup) - subu a2, a2, 0x1 - EX(sb, t0, (a0), s_fixup) - addu a1, a1, 0x1 - bnez a2, u_end_bytes - addu a0, a0, 0x1 - +EXC( LOAD t0, 0(src), l_exc) + ADD src, src, NBYTES + SUB len, len, NBYTES +EXC( STORE t0, 0(dst), s_exc_p1u) + bne rem, len, 1b + ADD dst, dst, NBYTES + + /* + * src and dst are aligned, need to copy rem bytes (rem < NBYTES) + * A loop would do only a byte at a time with possible branch + * mispredicts. Can't do an explicit LOAD dst,mask,or,STORE + * because can't assume read-access to dst. Instead, use + * STREST dst, which doesn't require read access to dst. + * + * This code should perform better than a simple loop on modern, + * wide-issue mips processors because the code has fewer branches and + * more instruction-level parallelism. + */ +#define bits t2 + beqz len, done + ADD t1, dst, len # t1 is just past last byte of dst + li bits, 8*NBYTES + SLL rem, len, 3 # rem = number of bits to keep +EXC( LOAD t0, 0(src), l_exc) + SUB bits, bits, rem # bits = number of bits to discard + SHIFT_DISCARD t0, t0, bits +EXC( STREST t0, -1(t1), s_exc) jr ra - move a2, zero + move len, zero +dst_unaligned: + /* + * dst is unaligned + * t0 = src & ADDRMASK + * t1 = dst & ADDRMASK; T1 > 0 + * len >= NBYTES + * + * Copy enough bytes to align dst + * Set match = (src and dst have same alignment) + */ +#define match rem +EXC( LDFIRST t3, FIRST(0)(src), l_exc) + ADD t2, zero, NBYTES +EXC( LDREST t3, REST(0)(src), l_exc_copy) + SUB t2, t2, t1 # t2 = number of bytes copied + xor match, t0, t1 +EXC( STFIRST t3, FIRST(0)(dst), s_exc) + beq len, t2, done + SUB len, len, t2 + ADD dst, dst, t2 + beqz match, both_aligned + ADD src, src, t2 + +src_unaligned_dst_aligned: + SRL t0, len, LOG_NBYTES+2 # +2 for 4 units/iter + PREF( 0, 3*32(src) ) + beqz t0, cleanup_src_unaligned + and rem, len, (4*NBYTES-1) # rem = len % 4*NBYTES + PREF( 1, 3*32(dst) ) +1: +/* + * Avoid consecutive LD*'s to the same register since some mips + * implementations can't issue them in the same cycle. + * It's OK to load FIRST(N+1) before REST(N) because the two addresses + * are to the same unit (unless src is aligned, but it's not). + */ +EXC( LDFIRST t0, FIRST(0)(src), l_exc) +EXC( LDFIRST t1, FIRST(1)(src), l_exc_copy) + SUB len, len, 4*NBYTES +EXC( LDREST t0, REST(0)(src), l_exc_copy) +EXC( LDREST t1, REST(1)(src), l_exc_copy) +EXC( LDFIRST t2, FIRST(2)(src), l_exc_copy) +EXC( LDFIRST t3, FIRST(3)(src), l_exc_copy) +EXC( LDREST t2, REST(2)(src), l_exc_copy) +EXC( LDREST t3, REST(3)(src), l_exc_copy) + PREF( 0, 9*32(src) ) # 0 is PREF_LOAD (not streamed) + ADD src, src, 4*NBYTES +#ifdef CONFIG_CPU_SB1 + nop # improves slotting +#endif +EXC( STORE t0, UNIT(0)(dst), s_exc_p4u) +EXC( STORE t1, UNIT(1)(dst), s_exc_p3u) +EXC( STORE t2, UNIT(2)(dst), s_exc_p2u) +EXC( STORE t3, UNIT(3)(dst), s_exc_p1u) + PREF( 1, 9*32(dst) ) # 1 is PREF_STORE (not streamed) + bne len, rem, 1b + ADD dst, dst, 4*NBYTES + +cleanup_src_unaligned: + beqz len, done + and rem, len, NBYTES-1 # rem = len % NBYTES + beq rem, len, copy_bytes + nop +1: +EXC( LDFIRST t0, FIRST(0)(src), l_exc) +EXC( LDREST t0, REST(0)(src), l_exc_copy) + ADD src, src, NBYTES + SUB len, len, NBYTES +EXC( STORE t0, 0(dst), s_exc_p1u) + bne len, rem, 1b + ADD dst, dst, NBYTES + +copy_bytes_checklen: + beqz len, done + nop +copy_bytes: + /* 0 < len < NBYTES */ +#define COPY_BYTE(N) \ +EXC( lb t0, N(src), l_exc); \ + SUB len, len, 1; \ + beqz len, done; \ +EXC( sb t0, N(dst), s_exc_p1) + + COPY_BYTE(0) + COPY_BYTE(1) +#ifdef USE_DOUBLE + COPY_BYTE(2) + COPY_BYTE(3) + COPY_BYTE(4) + COPY_BYTE(5) +#endif +EXC( lb t0, NBYTES-2(src), l_exc) + SUB len, len, 1 + jr ra +EXC( sb t0, NBYTES-2(dst), s_exc_p1) +done: + jr ra + nop END(memcpy) -/* descending order, destination aligned */ -#define RMOVE_BIGCHUNK(src, dst, offset, t0, t1, t2, t3) \ - lw t0, (offset + 0x10)(src); \ - lw t1, (offset + 0x14)(src); \ - lw t2, (offset + 0x18)(src); \ - lw t3, (offset + 0x1c)(src); \ - sw t0, (offset + 0x10)(dst); \ - sw t1, (offset + 0x14)(dst); \ - sw t2, (offset + 0x18)(dst); \ - sw t3, (offset + 0x1c)(dst); \ - lw t0, (offset + 0x00)(src); \ - lw t1, (offset + 0x04)(src); \ - lw t2, (offset + 0x08)(src); \ - lw t3, (offset + 0x0c)(src); \ - sw t0, (offset + 0x00)(dst); \ - sw t1, (offset + 0x04)(dst); \ - sw t2, (offset + 0x08)(dst); \ - sw t3, (offset + 0x0c)(dst) - -/* descending order, destination ununaligned */ -#define RUMOVE_BIGCHUNK(src, dst, offset, t0, t1, t2, t3) \ - lw t0, (offset + 0x10)(src); \ - lw t1, (offset + 0x14)(src); \ - lw t2, (offset + 0x18)(src); \ - lw t3, (offset + 0x1c)(src); \ - usw t0, (offset + 0x10)(dst); \ - usw t1, (offset + 0x14)(dst); \ - usw t2, (offset + 0x18)(dst); \ - usw t3, (offset + 0x1c)(dst); \ - lw t0, (offset + 0x00)(src); \ - lw t1, (offset + 0x04)(src); \ - lw t2, (offset + 0x08)(src); \ - lw t3, (offset + 0x0c)(src); \ - usw t0, (offset + 0x00)(dst); \ - usw t1, (offset + 0x04)(dst); \ - usw t2, (offset + 0x08)(dst); \ - usw t3, (offset + 0x0c)(dst) +l_exc_copy: + /* + * Copy bytes from src until faulting load address (or until a + * lb faults) + * + * When reached by a faulting LDFIRST/LDREST, THREAD_BUADDR($28) + * may be more than a byte beyond the last address. + * Hence, the lb below may get an exception. + * + * Assumes src < THREAD_BUADDR($28) + */ + LOAD t0, TI_TASK($28) + LOAD t0, THREAD_BUADDR(t0) +1: +EXC( lb t1, 0(src), l_exc) + ADD src, src, 1 + sb t1, 0(dst) # can't fault -- we're copy_from_user + bne src, t0, 1b + ADD dst, dst, 1 +l_exc: + LOAD t0, THREAD_BUADDR($28) # t0 is just past last good address + LOAD t0, THREAD_BUADDR(t0) + nop + SUB len, AT, t0 # len number of uncopied bytes + /* + * Here's where we rely on src and dst being incremented in tandem, + * See (3) above. + * dst += (fault addr - src) to put dst at first byte to clear + */ + ADD dst, t0 # compute start address in a1 + SUB dst, src + /* + * Clear len bytes starting at dst. Can't call __bzero because it + * might modify len. An inefficient loop for these rare times... + */ + beqz len, done + SUB src, len, 1 +1: sb zero, 0(dst) + ADD dst, dst, 1 + bnez src, 1b + SUB src, src, 1 + jr ra + nop + + +#define SEXC(n) \ +s_exc_p ## n ## u: \ + jr ra; \ + ADD len, len, n*NBYTES + +SEXC(8) +SEXC(7) +SEXC(6) +SEXC(5) +SEXC(4) +SEXC(3) +SEXC(2) +SEXC(1) + +s_exc_p1: + jr ra + ADD len, len, 1 +s_exc: + jr ra + nop .align 5 LEAF(memmove) - addu t0, a0, a2 + ADD t0, a0, a2 + ADD t1, a1, a2 sltu t0, a1, t0 # dst + len <= src -> memcpy - addu t1, a1, a2 sltu t1, a0, t1 # dst >= src + len -> memcpy and t0, t1 beqz t0, __memcpy @@ -412,159 +473,21 @@ LEAF(memmove) beqz a2, r_out END(memmove) + /* fall through to __rmemcpy */ LEAF(__rmemcpy) /* a0=dst a1=src a2=len */ - sltu t0, a1, a0 + sltu t0, a1, a0 beqz t0, r_end_bytes_up # src >= dst nop - addu a0, a2 # dst = dst + len - addu a1, a2 # src = src + len - -#if 0 /* Horror fix */ - xor t0, a0, a1 - andi t0, t0, 0x3 - move t7, a0 - beqz t0, r_can_align - sltiu t8, a2, 0x8 - - b r_memcpy_u_src # bad alignment - move t2, a2 - -r_can_align: - bnez t8, r_small_memcpy # < 8 bytes to copy - move t2, a2 - - beqz a2, r_out - andi t8, a1, 0x1 - -r_hword_align: - beqz t8, r_word_align - andi t8, a1, 0x2 - - lb t0, -1(a1) - subu a2, a2, 0x1 - sb t0, -1(a0) - subu a1, a1, 0x1 - subu a0, a0, 0x1 - andi t8, a1, 0x2 - -r_word_align: - beqz t8, r_dword_align - sltiu t8, a2, 56 - - lh t0, -2(a1) - subu a2, a2, 0x2 - sh t0, -2(a0) - sltiu t8, a2, 56 - subu a0, a0, 0x2 - subu a1, a1, 0x2 - -r_dword_align: - bnez t8, r_do_end_words - move t8, a2 - - andi t8, a1, 0x4 - beqz t8, r_qword_align - andi t8, a1, 0x8 - - lw t0, -4(a1) - subu a2, a2, 0x4 - sw t0, -4(a0) - subu a1, a1, 0x4 - subu a0, a0, 0x4 - andi t8, a1, 0x8 - -r_qword_align: - beqz t8, r_oword_align - andi t8, a1, 0x10 - - subu a1, a1, 0x8 - lw t0, 0x04(a1) - lw t1, 0x00(a1) - subu a0, a0, 0x8 - sw t0, 0x04(a0) - sw t1, 0x00(a0) - subu a2, a2, 0x8 - - andi t8, a1, 0x10 - -r_oword_align: - beqz t8, r_begin_movement - srl t8, a2, 0x7 - - subu a1, a1, 0x10 - lw t3, 0x08(a1) # assumes subblock ordering - lw t4, 0x0c(a1) - lw t0, 0x00(a1) - lw t1, 0x04(a1) - subu a0, a0, 0x10 - sw t3, 0x08(a0) - sw t4, 0x0c(a0) - sw t0, 0x00(a0) - sw t1, 0x04(a0) - subu a2, a2, 0x10 - srl t8, a2, 0x7 - -r_begin_movement: - beqz t8, 0f - andi t2, a2, 0x40 - -r_move_128bytes: - RMOVE_BIGCHUNK(a1, a0, -0x80, t0, t1, t3, t4) - RMOVE_BIGCHUNK(a1, a0, -0x60, t0, t1, t3, t4) - RMOVE_BIGCHUNK(a1, a0, -0x40, t0, t1, t3, t4) - RMOVE_BIGCHUNK(a1, a0, -0x20, t0, t1, t3, t4) - subu t8, t8, 0x01 - subu a1, a1, 0x80 - bnez t8, r_move_128bytes - subu a0, a0, 0x80 - -0: - beqz t2, 1f - andi t2, a2, 0x20 - -r_move_64bytes: - subu a1, a1, 0x40 - subu a0, a0, 0x40 - RMOVE_BIGCHUNK(a1, a0, 0x20, t0, t1, t3, t4) - RMOVE_BIGCHUNK(a1, a0, 0x00, t0, t1, t3, t4) - -1: - beqz t2, r_do_end_words - andi t8, a2, 0x1c - -r_move_32bytes: - subu a1, a1, 0x20 - subu a0, a0, 0x20 - RMOVE_BIGCHUNK(a1, a0, 0x00, t0, t1, t3, t4) - andi t8, a2, 0x1c - -r_do_end_words: - beqz t8, r_maybe_end_cruft - srl t8, t8, 0x2 - -r_end_words: - lw t0, -4(a1) - subu t8, t8, 0x1 - sw t0, -4(a0) - subu a1, a1, 0x4 - bnez t8, r_end_words - subu a0, a0, 0x4 - -r_maybe_end_cruft: - andi t2, a2, 0x3 - -r_small_memcpy: - beqz t2, r_out - move a2, t2 -#endif /* Horror fix */ + ADD a0, a2 # dst = dst + len + ADD a1, a2 # src = src + len r_end_bytes: lb t0, -1(a1) - subu a2, a2, 0x1 + SUB a2, a2, 0x1 sb t0, -1(a0) - subu a1, a1, 0x1 + SUB a1, a1, 0x1 bnez a2, r_end_bytes - subu a0, a0, 0x1 + SUB a0, a0, 0x1 r_out: jr ra @@ -572,148 +495,12 @@ r_out: r_end_bytes_up: lb t0, (a1) - subu a2, a2, 0x1 + SUB a2, a2, 0x1 sb t0, (a0) - addu a1, a1, 0x1 + ADD a1, a1, 0x1 bnez a2, r_end_bytes_up - addu a0, a0, 0x1 + ADD a0, a0, 0x1 jr ra move a2, zero - -#if 0 /* Horror fix */ -/* ------------------------------------------------------------------------- */ - -/* Bad, bad. At least try to align the source */ - -r_memcpy_u_src: - bnez t8, r_small_memcpy # < 8 bytes? - move t2, a2 - - andi t0, a1, 7 # t0: how much to align - - ulw t1, -8(a1) # dword alignment - ulw t2, -4(a1) - usw t1, -8(a0) - usw t2, -4(a0) - - subu a1, t0 # src - subu a0, t0 # dst - subu a2, t0 # len - - sltiu t8, a2, 56 - bnez t8, ru_do_end_words - andi t8, a2, 0x3c - - andi t8, a1, 8 # now qword aligned? - -ru_qword_align: - beqz t8, ru_oword_align - andi t8, a1, 0x10 - - subu a1, a1, 0x8 - lw t0, 0x00(a1) - lw t1, 0x04(a1) - subu a0, a0, 0x8 - usw t0, 0x00(a0) - usw t1, 0x04(a0) - subu a2, a2, 0x8 - - andi t8, a1, 0x10 - -ru_oword_align: - beqz t8, ru_begin_movement - srl t8, a2, 0x7 - - subu a1, a1, 0x10 - lw t3, 0x08(a1) # assumes subblock ordering - lw t4, 0x0c(a1) - lw t0, 0x00(a1) - lw t1, 0x04(a1) - subu a0, a0, 0x10 - usw t3, 0x08(a0) - usw t4, 0x0c(a0) - usw t0, 0x00(a0) - usw t1, 0x04(a0) - subu a2, a2, 0x10 - - srl t8, a2, 0x7 - -ru_begin_movement: - beqz t8, 0f - andi t2, a2, 0x40 - -ru_move_128bytes: - RUMOVE_BIGCHUNK(a1, a0, -0x80, t0, t1, t3, t4) - RUMOVE_BIGCHUNK(a1, a0, -0x60, t0, t1, t3, t4) - RUMOVE_BIGCHUNK(a1, a0, -0x40, t0, t1, t3, t4) - RUMOVE_BIGCHUNK(a1, a0, -0x20, t0, t1, t3, t4) - subu t8, t8, 0x01 - subu a1, a1, 0x80 - bnez t8, ru_move_128bytes - subu a0, a0, 0x80 - -0: - beqz t2, 1f - andi t2, a2, 0x20 - -ru_move_64bytes: - subu a1, a1, 0x40 - subu a0, a0, 0x40 - RUMOVE_BIGCHUNK(a1, a0, 0x20, t0, t1, t3, t4) - RUMOVE_BIGCHUNK(a1, a0, 0x00, t0, t1, t3, t4) - -1: - beqz t2, ru_do_end_words - andi t8, a2, 0x1c - -ru_move_32bytes: - subu a1, a1, 0x20 - subu a0, a0, 0x20 - RUMOVE_BIGCHUNK(a1, a0, 0x00, t0, t1, t3, t4) - andi t8, a2, 0x1c - -ru_do_end_words: - beqz t8, ru_maybe_end_cruft - srl t8, t8, 0x2 - -ru_end_words: - lw t0, -4(a1) - usw t0, -4(a0) - subu t8, t8, 0x1 - subu a1, a1, 0x4 - bnez t8, ru_end_words - subu a0, a0, 0x4 - -ru_maybe_end_cruft: - andi t2, a2, 0x3 - -ru_cannot_optimize: - beqz t2, r_out - move a2, t2 - -ru_end_bytes: - lb t0, -1(a1) - subu a2, a2, 0x1 - sb t0, -1(a0) - subu a1, a1, 0x1 - bnez a2, ru_end_bytes - subu a0, a0, 0x1 - - jr ra - move a2, zero -#endif /* Horror fix */ END(__rmemcpy) - -l_fixup: # clear the rest of the buffer - lw t0, THREAD_BUADDR($28) - nop - subu a2, AT, t0 # a2 bytes to go - addu a0, t0 # compute start address in a1 - subu a0, a1 - j __bzero - move a1, zero - -s_fixup: - jr ra - nop diff --git a/arch/mips/lib/memset.S b/arch/mips/lib/memset.S index 5868db88c8be..594f7ef5b41a 100644 --- a/arch/mips/lib/memset.S +++ b/arch/mips/lib/memset.S @@ -54,7 +54,6 @@ LEAF(memset) 1: EXPORT(__bzero) - .type __bzero, @function sltiu t0, a2, 4 /* very small region? */ bnez t0, small_memset andi t0, a0, 3 /* aligned? */ @@ -84,13 +83,17 @@ EXPORT(__bzero) .set noreorder memset_partial: - la t1, 2f /* where to start */ + PTR_LA t1, 2f /* where to start */ subu t1, t0 jr t1 addu a0, t0 /* dest ptr */ + .set push + .set noreorder + .set nomacro F_FILL64(a0, -64, a1, partial_fixup) /* ... but first do wrds ... */ -2: andi a2, 3 /* 0 <= n <= 3 to go */ +2: .set pop + andi a2, 3 /* 0 <= n <= 3 to go */ beqz a2, 1f addu a0, a2 /* What's left */ @@ -120,14 +123,16 @@ first_fixup: nop fwd_fixup: - lw t0, THREAD_BUADDR($28) + lw t0, TI_TASK($28) + lw t0, THREAD_BUADDR(t0) andi a2, 0x3f addu a2, t1 jr ra subu a2, t0 partial_fixup: - lw t0, THREAD_BUADDR($28) + lw t0, TI_TASK($28) + lw t0, THREAD_BUADDR(t0) andi a2, 3 addu a2, t1 jr ra diff --git a/arch/mips/lib/promlib.c b/arch/mips/lib/promlib.c new file mode 100644 index 000000000000..dddfe98b4ded --- /dev/null +++ b/arch/mips/lib/promlib.c @@ -0,0 +1,24 @@ +#include <stdarg.h> +#include <linux/kernel.h> + +extern void prom_putchar(char); + +void prom_printf(char *fmt, ...) +{ + va_list args; + char ppbuf[1024]; + char *bptr; + + va_start(args, fmt); + vsprintf(ppbuf, fmt, args); + + bptr = ppbuf; + + while (*bptr != 0) { + if (*bptr == '\n') + prom_putchar('\r'); + + prom_putchar(*bptr++); + } + va_end(args); +} diff --git a/arch/mips/lib/r3k_dump_tlb.c b/arch/mips/lib/r3k_dump_tlb.c index d9cda02a1aab..8f0535a70406 100644 --- a/arch/mips/lib/r3k_dump_tlb.c +++ b/arch/mips/lib/r3k_dump_tlb.c @@ -12,11 +12,12 @@ #include <asm/bootinfo.h> #include <asm/cachectl.h> +#include <asm/cpu.h> #include <asm/mipsregs.h> #include <asm/page.h> #include <asm/pgtable.h> -#define mips_tlb_entries 64 +extern int r3k_have_wired_reg; /* defined in tlb-r3k.c */ void dump_tlb(int first, int last) @@ -25,18 +26,18 @@ dump_tlb(int first, int last) unsigned int asid; unsigned long entryhi, entrylo0; - asid = get_entryhi() & 0xfc0; + asid = read_c0_entryhi() & 0xfc0; for(i=first;i<=last;i++) { - write_32bit_cp0_register(CP0_INDEX, i<<8); + write_c0_index(i<<8); __asm__ __volatile__( ".set\tnoreorder\n\t" "tlbr\n\t" "nop\n\t" ".set\treorder"); - entryhi = read_32bit_cp0_register(CP0_ENTRYHI); - entrylo0 = read_32bit_cp0_register(CP0_ENTRYLO0); + entryhi = read_c0_entryhi(); + entrylo0 = read_c0_entrylo0(); /* Unused entries have a virtual address of KSEG0. */ if ((entryhi & 0xffffe000) != 0x80000000 @@ -59,22 +60,22 @@ dump_tlb(int first, int last) } printk("\n"); - set_entryhi(asid); + write_c0_entryhi(asid); } void dump_tlb_all(void) { - dump_tlb(0, mips_tlb_entries - 1); + dump_tlb(0, current_cpu_data.tlbsize - 1); } void dump_tlb_wired(void) { - int wired = 7; + int wired = r3k_have_wired_reg ? read_c0_wired() : 8; printk("Wired: %d", wired); - dump_tlb(0, read_32bit_cp0_register(CP0_WIRED)); + dump_tlb(0, wired - 1); } void @@ -84,11 +85,11 @@ dump_tlb_addr(unsigned long addr) int index; local_irq_save(flags); - oldpid = get_entryhi() & 0xff; - set_entryhi((addr & PAGE_MASK) | oldpid); + oldpid = read_c0_entryhi() & 0xff; + write_c0_entryhi((addr & PAGE_MASK) | oldpid); tlb_probe(); - index = get_index(); - set_entryhi(oldpid); + index = read_c0_index(); + write_c0_entryhi(oldpid); local_irq_restore(flags); if (index < 0) { @@ -103,7 +104,8 @@ dump_tlb_addr(unsigned long addr) void dump_tlb_nonwired(void) { - dump_tlb(8, mips_tlb_entries - 1); + int wired = r3k_have_wired_reg ? read_c0_wired() : 8; + dump_tlb(wired, current_cpu_data.tlbsize - 1); } void diff --git a/arch/mips/lib/strlen_user.S b/arch/mips/lib/strlen_user.S index 0902817c08c5..231179733445 100644 --- a/arch/mips/lib/strlen_user.S +++ b/arch/mips/lib/strlen_user.S @@ -1,5 +1,4 @@ -/* $Id: strlen_user.S,v 1.3 1999/08/21 22:19:11 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. @@ -24,22 +23,18 @@ * Return 0 for error */ LEAF(__strlen_user_asm) - lw v0, THREAD_CURDS($28) # pointer ok? + lw v0, TI_ADDR_LIMIT($28) # pointer ok? and v0, a0 bltz v0, fault -EXPORT(__strlen_user_nocheck_asm) +FEXPORT(__strlen_user_nocheck_asm) move v0, a0 1: EX(lb, t0, (v0), fault) addiu v0, 1 bnez t0, 1b subu v0, a0 jr ra - END(__strlen_user_nocheck_asm) - - .section __ex_table,"a" - PTR 1b, fault - .previous + END(__strlen_user_asm) fault: move v0, zero jr ra diff --git a/arch/mips/lib/strncpy_user.S b/arch/mips/lib/strncpy_user.S index 9c8e0ab85405..88096aa4ff11 100644 --- a/arch/mips/lib/strncpy_user.S +++ b/arch/mips/lib/strncpy_user.S @@ -1,5 +1,4 @@ -/* $Id: strncpy_user.S,v 1.3 1999/08/21 22:19:11 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. @@ -29,7 +28,7 @@ */ LEAF(__strncpy_from_user_asm) - lw v0, THREAD_CURDS($28) # pointer ok? + lw v0, TI_ADDR_LIMIT($28) # pointer ok? and v0, a1 bltz v0, fault diff --git a/arch/mips/lib/strnlen_user.S b/arch/mips/lib/strnlen_user.S index 0ae411834e51..b1faa46c1d70 100644 --- a/arch/mips/lib/strnlen_user.S +++ b/arch/mips/lib/strnlen_user.S @@ -1,5 +1,4 @@ -/* $Id: strnlen_user.S,v 1.2 1999/11/19 20:35:21 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. @@ -28,11 +27,11 @@ * bytes. There's nothing secret there ... */ LEAF(__strnlen_user_asm) - lw v0, THREAD_CURDS($28) # pointer ok? + lw v0, TI_ADDR_LIMIT($28) # pointer ok? and v0, a0 bltz v0, fault -EXPORT(__strnlen_user_nocheck_asm) +FEXPORT(__strnlen_user_nocheck_asm) .type __strnlen_user_nocheck_asm,@function move v0, a0 addu a1, a0 # stop pointer @@ -46,9 +45,5 @@ EXPORT(__strnlen_user_nocheck_asm) jr ra END(__strnlen_user_asm) - .section __ex_table,"a" - PTR 1b, fault - .previous - fault: move v0, zero jr ra diff --git a/arch/mips/lib/tinycon.c b/arch/mips/lib/tinycon.c index ba25982dfa54..72efa45ffa09 100644 --- a/arch/mips/lib/tinycon.c +++ b/arch/mips/lib/tinycon.c @@ -38,7 +38,7 @@ void init_console(void) cursor_y = 0; vram_addr = (unsigned short *)0xb00b8000; - + console_needs_init = 0; } @@ -87,7 +87,7 @@ scroll(void) *(caddr++) = *(caddr + size_x); /* blank last line */ - + caddr = vram_addr + (size_x * (size_y-1)); for(i=0; i<size_x; i++) *(caddr++) = (*caddr & 0xff00) | (unsigned short) ' '; diff --git a/arch/mips/lib/watch.S b/arch/mips/lib/watch.S index ee9559522d1f..f42cf5da000f 100644 --- a/arch/mips/lib/watch.S +++ b/arch/mips/lib/watch.S @@ -1,5 +1,4 @@ -/* $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. diff --git a/arch/mips/math-emu/Makefile b/arch/mips/math-emu/Makefile index 675a22fdcabb..121a848a3594 100644 --- a/arch/mips/math-emu/Makefile +++ b/arch/mips/math-emu/Makefile @@ -8,4 +8,4 @@ obj-y := cp1emu.o ieee754m.o ieee754d.o ieee754dp.o ieee754sp.o ieee754.o \ dp_tint.o dp_fint.o dp_tlong.o dp_flong.o sp_frexp.o sp_modf.o \ sp_div.o sp_mul.o sp_sub.o sp_add.o sp_fdp.o sp_cmp.o sp_logb.o \ sp_scalb.o sp_simple.o sp_tint.o sp_fint.o sp_tlong.o sp_flong.o \ - dp_sqrt.o sp_sqrt.o kernel_linkage.o + dp_sqrt.o sp_sqrt.o kernel_linkage.o dsemul.o diff --git a/arch/mips/math-emu/cp1emu.c b/arch/mips/math-emu/cp1emu.c index 4d3367c651b8..28d4499bd2df 100644 --- a/arch/mips/math-emu/cp1emu.c +++ b/arch/mips/math-emu/cp1emu.c @@ -1,6 +1,6 @@ /* * cp1emu.c: a MIPS coprocessor 1 (fpu) instruction emulator - * + * * MIPS floating point support * Copyright (C) 1994-2000 Algorithmics Ltd. All rights reserved. * http://www.algor.co.uk @@ -23,37 +23,32 @@ * * A complete emulator for MIPS coprocessor 1 instructions. This is * required for #float(switch) or #float(trap), where it catches all - * COP1 instructions via the "CoProcessor Unusable" exception. + * COP1 instructions via the "CoProcessor Unusable" exception. * * More surprisingly it is also required for #float(ieee), to help out * the hardware fpu at the boundaries of the IEEE-754 representation * (denormalised values, infinities, underflow, etc). It is made * quite nasty because emulation of some non-COP1 instructions is * required, e.g. in branch delay slots. - * - * Note if you know that you won't have an fpu, then you'll get much + * + * Note if you know that you won't have an fpu, then you'll get much * better performance by compiling with -msoft-float! */ -#include <linux/mm.h> -#include <linux/signal.h> -#include <linux/smp.h> -#include <linux/smp_lock.h> +#include <linux/sched.h> -#include <asm/asm.h> -#include <asm/branch.h> +#include <asm/inst.h> #include <asm/bootinfo.h> -#include <asm/byteorder.h> #include <asm/cpu.h> -#include <asm/inst.h> -#include <asm/uaccess.h> #include <asm/processor.h> +#include <asm/ptrace.h> +#include <asm/signal.h> #include <asm/mipsregs.h> -#include <asm/system.h> -#include <asm/pgtable.h> - #include <asm/fpu_emulator.h> +#include <asm/uaccess.h> +#include <asm/branch.h> #include "ieee754.h" +#include "dsemul.h" /* Strap kernel emulator for full MIPS IV emulation */ @@ -62,20 +57,14 @@ #endif #define __mips 4 -typedef void *vaddr_t; - -/* Function which emulates the instruction in a branch delay slot. */ - -static int mips_dsemul(struct pt_regs *, mips_instruction, vaddr_t); - /* Function which emulates a floating point instruction. */ static int fpu_emu(struct pt_regs *, struct mips_fpu_soft_struct *, - mips_instruction); + mips_instruction); #if __mips >= 4 && __mips != 32 static int fpux_emu(struct pt_regs *, - struct mips_fpu_soft_struct *, mips_instruction); + struct mips_fpu_soft_struct *, mips_instruction); #endif /* Further private data for which no space exists in mips_fpu_soft_struct */ @@ -107,8 +96,7 @@ static const unsigned int fpucondbit[8] = { #endif - -/* +/* * Redundant with logic already in kernel/branch.c, * embedded in compute_return_epc. At some point, * a single subroutine should be used across both @@ -164,67 +152,45 @@ static int isBranchInstr(mips_instruction * i) return 0; } -#define REG_TO_VA (vaddr_t) -#define VA_TO_REG (unsigned long) - -static unsigned long mips_get_word(struct pt_regs *xcp, void *va, int *perr) -{ - unsigned long temp; - - if (!user_mode(xcp)) { - *perr = 0; - return (*(unsigned long *) va); - } - - *perr = (int) get_user(temp, (unsigned long *) va); - return temp; -} - -static unsigned long long -mips_get_dword(struct pt_regs *xcp, void *va, int *perr) -{ - unsigned long long temp; - - if (!user_mode(xcp)) { - *perr = 0; - return (*(unsigned long long *) va); - } - - *perr = (int) get_user(temp, (unsigned long long *) va); - return temp; -} - -static int mips_put_word(struct pt_regs *xcp, void *va, unsigned long val) -{ - if (!user_mode(xcp)) { - *(unsigned long *) va = val; - return 0; - } - - return put_user(val, (unsigned long *) va); -} - -static int mips_put_dword(struct pt_regs *xcp, void *va, long long val) -{ - if (!user_mode(xcp)) { - *(unsigned long long *) va = val; - return 0; - } - - return put_user(val, (unsigned long long *) va); -} - - /* * In the Linux kernel, we support selection of FPR format on the * basis of the Status.FR bit. This does imply that, if a full 32 * FPRs are desired, there needs to be a flip-flop that can be written - * to one at that bit position. In any case, normal MIPS ABI uses + * to one at that bit position. In any case, O32 MIPS ABI uses * only the even FPRs (Status.FR = 0). */ #define CP0_STATUS_FR_SUPPORT +#ifdef CP0_STATUS_FR_SUPPORT +#define FR_BIT ST0_FR +#else +#define FR_BIT 0 +#endif + +#define SIFROMREG(si,x) ((si) = \ + (xcp->cp0_status & FR_BIT) || !(x & 1) ? \ + (int)ctx->regs[x] : \ + (int)(ctx->regs[x & ~1] >> 32 )) +#define SITOREG(si,x) (ctx->regs[x & ~((xcp->cp0_status & FR_BIT) == 0)] = \ + (xcp->cp0_status & FR_BIT) || !(x & 1) ? \ + ctx->regs[x & ~1] >> 32 << 32 | (u32)(si) : \ + ctx->regs[x & ~1] << 32 >> 32 | (u64)(si) << 32) + +#define DIFROMREG(di,x) ((di) = \ + ctx->regs[x & ~((xcp->cp0_status & FR_BIT) == 0)]) +#define DITOREG(di,x) (ctx->regs[x & ~((xcp->cp0_status & FR_BIT) == 0)] \ + = (di)) +#define DIFROMREG(di,x) ((di) = \ + ctx->regs[x & ~((xcp->cp0_status & FR_BIT) == 0)]) +#define DITOREG(di,x) (ctx->regs[x & ~((xcp->cp0_status & FR_BIT) == 0)] \ + = (di)) + +#define SPFROMREG(sp,x) SIFROMREG((sp).bits,x) +#define SPTOREG(sp,x) SITOREG((sp).bits,x) +#define DPFROMREG(dp,x) DIFROMREG((dp).bits,x) +#define DPTOREG(dp,x) DITOREG((dp).bits,x) + /* * Emulate the single floating point instruction pointed at by EPC. * Two instructions if the instruction is in a branch delay slot. @@ -233,13 +199,10 @@ static int mips_put_dword(struct pt_regs *xcp, void *va, long long val) static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_soft_struct *ctx) { mips_instruction ir; - vaddr_t emulpc; - vaddr_t contpc; + vaddr_t emulpc, contpc; unsigned int cond; - int err = 0; - ir = mips_get_word(xcp, REG_TO_VA xcp->cp0_epc, &err); - if (err) { + if (get_user(ir, (mips_instruction *) xcp->cp0_epc)) { fpuemuprivate.stats.errors++; return SIGBUS; } @@ -252,7 +215,7 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_soft_struct *ctx) /* * The instruction to be emulated is in a branch delay slot * which means that we have to emulate the branch instruction - * BEFORE we do the cop1 instruction. + * BEFORE we do the cop1 instruction. * * This branch could be a COP1 branch, but in that case we * would have had a trap for that instruction, and would not @@ -266,363 +229,201 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_soft_struct *ctx) if (__compute_return_epc(xcp)) { #ifdef CP1DBG printk("failed to emulate branch at %p\n", - REG_TO_VA(xcp->cp0_epc)); + REG_TO_VA(xcp->cp0_epc)); #endif - return SIGILL;; + return SIGILL; } - ir = mips_get_word(xcp, emulpc, &err); - if (err) { + if (get_user(ir, (mips_instruction *) emulpc)) { fpuemuprivate.stats.errors++; return SIGBUS; } + /* __computer_return_epc() will have updated cp0_epc */ contpc = REG_TO_VA xcp->cp0_epc; - } else { + /* In order not to confuse ptrace() et al, tweak context */ + xcp->cp0_epc = VA_TO_REG emulpc - 4; + } + else { emulpc = REG_TO_VA xcp->cp0_epc; - contpc = REG_TO_VA xcp->cp0_epc + 4; + contpc = REG_TO_VA(xcp->cp0_epc + 4); } -emul: + emul: fpuemuprivate.stats.emulated++; switch (MIPSInst_OPCODE(ir)) { -#ifdef CP0_STATUS_FR_SUPPORT - /* R4000+ 64-bit fpu registers */ #ifndef SINGLE_ONLY_FPU - case ldc1_op: - { - void *va = REG_TO_VA(xcp->regs[MIPSInst_RS(ir)]) - + MIPSInst_SIMM(ir); - int ft = MIPSInst_RT(ir); - if (!(xcp->cp0_status & ST0_FR)) - ft &= ~1; - ctx->regs[ft] = mips_get_dword(xcp, va, &err); - fpuemuprivate.stats.loads++; - if (err) { - fpuemuprivate.stats.errors++; - return SIGBUS; - } + case ldc1_op:{ + u64 *va = REG_TO_VA(xcp->regs[MIPSInst_RS(ir)] + + MIPSInst_SIMM(ir)); + u64 val; + + fpuemuprivate.stats.loads++; + if (get_user(val, va)) { + fpuemuprivate.stats.errors++; + return SIGBUS; } + DITOREG(val, MIPSInst_RT(ir)); break; + } - case sdc1_op: - { - void *va = REG_TO_VA(xcp->regs[MIPSInst_RS(ir)]) - + MIPSInst_SIMM(ir); - int ft = MIPSInst_RT(ir); - if (!(xcp->cp0_status & ST0_FR)) - ft &= ~1; - fpuemuprivate.stats.stores++; - if (mips_put_dword(xcp, va, ctx->regs[ft])) { - fpuemuprivate.stats.errors++; - return SIGBUS; - } + case sdc1_op:{ + u64 *va = REG_TO_VA(xcp->regs[MIPSInst_RS(ir)] + + MIPSInst_SIMM(ir)); + u64 val; + + fpuemuprivate.stats.stores++; + DIFROMREG(val, MIPSInst_RT(ir)); + if (put_user(val, va)) { + fpuemuprivate.stats.errors++; + return SIGBUS; } break; + } #endif - case lwc1_op: - { - void *va = REG_TO_VA(xcp->regs[MIPSInst_RS(ir)]) - + MIPSInst_SIMM(ir); - fpureg_t val; - int ft = MIPSInst_RT(ir); - fpuemuprivate.stats.loads++; - val = mips_get_word(xcp, va, &err); - if (err) { - fpuemuprivate.stats.errors++; - return SIGBUS; - } - if (xcp->cp0_status & ST0_FR) { - /* load whole register */ - ctx->regs[ft] = val; - } else if (ft & 1) { - /* load to m.s. 32 bits */ -#ifdef SINGLE_ONLY_FPU - /* illegal register in single-float mode */ - return SIGILL; -#else - ctx->regs[(ft & ~1)] &= 0xffffffff; - ctx->regs[(ft & ~1)] |= val << 32; -#endif - } else { - /* load to l.s. 32 bits */ - ctx->regs[ft] &= ~0xffffffffLL; - ctx->regs[ft] |= val; - } - } - break; + case lwc1_op:{ + u32 *va = REG_TO_VA(xcp->regs[MIPSInst_RS(ir)] + + MIPSInst_SIMM(ir)); + u32 val; - case swc1_op: - { - void *va = REG_TO_VA(xcp->regs[MIPSInst_RS(ir)]) - + MIPSInst_SIMM(ir); - unsigned int val; - int ft = MIPSInst_RT(ir); - fpuemuprivate.stats.stores++; - if (xcp->cp0_status & ST0_FR) { - /* store whole register */ - val = ctx->regs[ft]; - } else if (ft & 1) { -#ifdef SINGLE_ONLY_FPU - /* illegal register in single-float mode */ - return SIGILL; -#else - /* store from m.s. 32 bits */ - val = ctx->regs[(ft & ~1)] >> 32; -#endif - } else { - /* store from l.s. 32 bits */ - val = ctx->regs[ft]; - } - if (mips_put_word(xcp, va, val)) { - fpuemuprivate.stats.errors++; - return SIGBUS; - } - } - break; -#else /* old 32-bit fpu registers */ - case lwc1_op: - { - void *va = REG_TO_VA(xcp->regs[MIPSInst_RS(ir)]) - + MIPSInst_SIMM(ir); - ctx->regs[MIPSInst_RT(ir)] = - mips_get_word(xcp, va, &err); - fpuemuprivate.stats.loads++; - if (err) { - fpuemuprivate.stats.errors++; - return SIGBUS; - } + fpuemuprivate.stats.loads++; + if (get_user(val, va)) { + fpuemuprivate.stats.errors++; + return SIGBUS; } - break; - - case swc1_op: - { - void *va = REG_TO_VA(xcp->regs[MIPSInst_RS(ir)]) - + MIPSInst_SIMM(ir); - fpuemuprivate.stats.stores++; - if (mips_put_word - (xcp, va, ctx->regs[MIPSInst_RT(ir)])) { - fpuemuprivate.stats.errors++; - return SIGBUS; - } +#ifdef SINGLE_ONLY_FPU + if (MIPSInst_RT(ir) & 1) { + /* illegal register in single-float mode */ + return SIGILL; } - break; - case ldc1_op: - { - void *va = REG_TO_VA(xcp->regs[MIPSInst_RS(ir)]) - + MIPSInst_SIMM(ir); - unsigned int rt = MIPSInst_RT(ir) & ~1; - int errs = 0; - fpuemuprivate.stats.loads++; -#if (defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN) || defined(__MIPSEB__) - ctx->regs[rt + 1] = - mips_get_word(xcp, va + 0, &err); - errs += err; - ctx->regs[rt + 0] = - mips_get_word(xcp, va + 4, &err); - errs += err; -#else - ctx->regs[rt + 0] = - mips_get_word(xcp, va + 0, &err); - errs += err; - ctx->regs[rt + 1] = - mips_get_word(xcp, va + 4, &err); - errs += err; #endif - if (err) - return SIGBUS; - } + SITOREG(val, MIPSInst_RT(ir)); break; + } - case sdc1_op: - { - void *va = REG_TO_VA(xcp->regs[MIPSInst_RS(ir)]) - + MIPSInst_SIMM(ir); - unsigned int rt = MIPSInst_RT(ir) & ~1; - fpuemuprivate.stats.stores++; -#if (defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN) || defined(__MIPSEB__) - if (mips_put_word(xcp, va + 0, ctx->regs[rt + 1])) - return SIGBUS; - if (mips_put_word(xcp, va + 4, ctx->regs[rt + 0])) - return SIGBUS; -#else - if (mips_put_word(xcp, va + 0, ctx->regs[rt + 0])) - return SIGBUS; - if (mips_put_word(xcp, va + 4, ctx->regs[rt + 1])) - return SIGBUS; + case swc1_op:{ + u32 *va = REG_TO_VA(xcp->regs[MIPSInst_RS(ir)] + + MIPSInst_SIMM(ir)); + u32 val; + + fpuemuprivate.stats.stores++; +#ifdef SINGLE_ONLY_FPU + if (MIPSInst_RT(ir) & 1) { + /* illegal register in single-float mode */ + return SIGILL; + } #endif + SIFROMREG(val, MIPSInst_RT(ir)); + if (put_user(val, va)) { + fpuemuprivate.stats.errors++; + return SIGBUS; } break; -#endif + } case cop1_op: switch (MIPSInst_RS(ir)) { -#ifdef CP0_STATUS_FR_SUPPORT #if __mips64 && !defined(SINGLE_ONLY_FPU) case dmfc_op: /* copregister fs -> gpr[rt] */ if (MIPSInst_RT(ir) != 0) { - int fs = MIPSInst_RD(ir); - if (!(xcp->cp0_status & ST0_FR)) - fs &= ~1; - xcp->regs[MIPSInst_RT(ir)] = ctx->regs[fs]; + DIFROMREG(xcp->regs[MIPSInst_RT(ir)], + MIPSInst_RD(ir)); } break; - case dmtc_op: { + case dmtc_op: /* copregister fs <- rt */ - fpureg_t value; - int fs = MIPSInst_RD(ir); - if (!(xcp->cp0_status & ST0_FR)) - fs &= ~1; - value = - (MIPSInst_RT(ir) == - 0) ? 0 : xcp->regs[MIPSInst_RT(ir)]; - ctx->regs[fs] = value; + DITOREG(xcp->regs[MIPSInst_RT(ir)], MIPSInst_RD(ir)); break; - } #endif case mfc_op: /* copregister rd -> gpr[rt] */ - if (MIPSInst_RT(ir) != 0) { - /* default value from l.s. 32 bits */ - int value = ctx->regs[MIPSInst_RD(ir)]; - if (MIPSInst_RD(ir) & 1) { #ifdef SINGLE_ONLY_FPU - /* illegal register in single-float mode */ - return SIGILL; -#else - if (!(xcp->cp0_status & ST0_FR)) { - /* move from m.s. 32 bits */ - value = - ctx-> - regs[MIPSInst_RD(ir) & - ~1] >> 32; - } -#endif - } - xcp->regs[MIPSInst_RT(ir)] = value; + if (MIPSInst_RD(ir) & 1) { + /* illegal register in single-float mode */ + return SIGILL; } - break; - - case mtc_op: - /* copregister rd <- rt */ - { - fpureg_t value; - if (MIPSInst_RT(ir) == 0) - value = 0; - else - value = - (unsigned int) xcp-> - regs[MIPSInst_RT(ir)]; - if (MIPSInst_RD(ir) & 1) { -#ifdef SINGLE_ONLY_FPU - /* illegal register in single-float mode */ - return SIGILL; -#else - if (!(xcp->cp0_status & ST0_FR)) { - /* move to m.s. 32 bits */ - ctx-> - regs[ - (MIPSInst_RD(ir) & - ~1)] &= - 0xffffffff; - ctx-> - regs[ - (MIPSInst_RD(ir) & - ~1)] |= - value << 32; - break; - } #endif - } - /* move to l.s. 32 bits */ - ctx->regs[MIPSInst_RD(ir)] &= - ~0xffffffffLL; - ctx->regs[MIPSInst_RD(ir)] |= value; - } - break; -#else - - case mfc_op: - /* copregister rd -> gpr[rt] */ if (MIPSInst_RT(ir) != 0) { - unsigned value = - ctx->regs[MIPSInst_RD(ir)]; - xcp->regs[MIPSInst_RT(ir)] = value; + SIFROMREG(xcp->regs[MIPSInst_RT(ir)], + MIPSInst_RD(ir)); } break; case mtc_op: /* copregister rd <- rt */ - { - unsigned value; - value = - (MIPSInst_RT(ir) == - 0) ? 0 : xcp->regs[MIPSInst_RT(ir)]; - ctx->regs[MIPSInst_RD(ir)] = value; +#ifdef SINGLE_ONLY_FPU + if (MIPSInst_RD(ir) & 1) { + /* illegal register in single-float mode */ + return SIGILL; } - break; #endif + SITOREG(xcp->regs[MIPSInst_RT(ir)], MIPSInst_RD(ir)); + break; - case cfc_op: + case cfc_op:{ /* cop control register rd -> gpr[rt] */ - { - unsigned value; + u32 value; - if (MIPSInst_RD(ir) == FPCREG_CSR) { - value = ctx->sr; + if (ir == CP1UNDEF) { + return do_dsemulret(xcp); + } + if (MIPSInst_RD(ir) == FPCREG_CSR) { + value = ctx->sr; #ifdef CSRTRACE - printk - ("%p gpr[%d]<-csr=%08x\n", - REG_TO_VA(xcp->cp0_epc), - MIPSInst_RT(ir), value); + printk("%p gpr[%d]<-csr=%08x\n", + REG_TO_VA(xcp->cp0_epc), + MIPSInst_RT(ir), value); #endif - } else if (MIPSInst_RD(ir) == FPCREG_RID) - value = 0; - else - value = 0; - if (MIPSInst_RT(ir)) - xcp->regs[MIPSInst_RT(ir)] = value; } + else if (MIPSInst_RD(ir) == FPCREG_RID) + value = 0; + else + value = 0; + if (MIPSInst_RT(ir)) + xcp->regs[MIPSInst_RT(ir)] = value; break; + } - case ctc_op: + case ctc_op:{ /* copregister rd <- rt */ - { - unsigned value; + u32 value; - if (MIPSInst_RT(ir) == 0) - value = 0; - else - value = xcp->regs[MIPSInst_RT(ir)]; + if (MIPSInst_RT(ir) == 0) + value = 0; + else + value = xcp->regs[MIPSInst_RT(ir)]; - /* we only have one writable control reg - */ - if (MIPSInst_RD(ir) == FPCREG_CSR) { + /* we only have one writable control reg + */ + if (MIPSInst_RD(ir) == FPCREG_CSR) { #ifdef CSRTRACE - printk - ("%p gpr[%d]->csr=%08x\n", - REG_TO_VA(xcp->cp0_epc), - MIPSInst_RT(ir), value); + printk("%p gpr[%d]->csr=%08x\n", + REG_TO_VA(xcp->cp0_epc), + MIPSInst_RT(ir), value); #endif - ctx->sr = value; - /* copy new rounding mode to ieee library state! */ - ieee754_csr.rm = - ieee_rm[value & 0x3]; - } + ctx->sr = value; + /* copy new rounding mode and + flush bit to ieee library state! */ + ieee754_csr.nod = (ctx->sr & 0x1000000) != 0; + ieee754_csr.rm = ieee_rm[value & 0x3]; + } + if ((ctx->sr >> 5) & ctx->sr & FPU_CSR_ALL_E) { + return SIGFPE; } break; + } - case bc_op: { + case bc_op:{ int likely = 0; if (xcp->cp0_cause & CAUSEF_BD) return SIGILL; #if __mips >= 4 - cond = ctx-> sr & fpucondbit[MIPSInst_RT(ir) >> 2]; + cond = ctx->sr & fpucondbit[MIPSInst_RT(ir) >> 2]; #else cond = ctx->sr & FPU_CSR_COND; #endif @@ -643,14 +444,16 @@ emul: xcp->cp0_cause |= CAUSEF_BD; if (cond) { - /* branch taken: emulate dslot instruction */ + /* branch taken: emulate dslot + * instruction + */ xcp->cp0_epc += 4; - contpc = REG_TO_VA xcp->cp0_epc + - (MIPSInst_SIMM(ir) << 2); + contpc = REG_TO_VA + (xcp->cp0_epc + + (MIPSInst_SIMM(ir) << 2)); - ir = mips_get_word(xcp, REG_TO_VA(xcp->cp0_epc), - &err); - if (err) { + if (get_user(ir, (mips_instruction *) + REG_TO_VA xcp->cp0_epc)) { fpuemuprivate.stats.errors++; return SIGBUS; } @@ -677,30 +480,36 @@ emul: } /* - * Single step the non-cp1 instruction in the - * dslot + * Single step the non-cp1 + * instruction in the dslot */ - return mips_dsemul(xcp, ir, contpc); - } else { + return mips_dsemul(xcp, ir, VA_TO_REG contpc); + } + else { /* branch not taken */ - if (likely) + if (likely) { /* - * branch likely nullifies dslot if not - * taken + * branch likely nullifies + * dslot if not taken */ xcp->cp0_epc += 4; - /* else continue & execute dslot as normal insn */ + contpc += 4; + /* + * else continue & execute + * dslot as normal insn + */ + } } break; } - default: { - int sig; - + default: if (!(MIPSInst_RS(ir) & 0x10)) return SIGILL; + { + int sig; - /* a real fpu computation instruction */ + /* a real fpu computation instruction */ if ((sig = fpu_emu(xcp, ctx, ir))) return sig; } @@ -708,13 +517,13 @@ emul: break; #if __mips >= 4 && __mips != 32 - case cop1x_op: - { - int sig; - if ((sig = fpux_emu(xcp, ctx, ir))) - return sig; - } + case cop1x_op:{ + int sig; + + if ((sig = fpux_emu(xcp, ctx, ir))) + return sig; break; + } #endif #if __mips >= 4 @@ -722,8 +531,8 @@ emul: if (MIPSInst_FUNC(ir) != movc_op) return SIGILL; cond = fpucondbit[MIPSInst_RT(ir) >> 2]; - if (((ctx->sr & cond) != 0) != - ((MIPSInst_RT(ir) & 1) != 0)) return 0; + if (((ctx->sr & cond) != 0) != ((MIPSInst_RT(ir) & 1) != 0)) + return 0; xcp->regs[MIPSInst_RD(ir)] = xcp->regs[MIPSInst_RS(ir)]; break; #endif @@ -739,141 +548,20 @@ emul: } /* - * Emulate the arbritrary instruction ir at xcp->cp0_epc. Required when - * we have to emulate the instruction in a COP1 branch delay slot. Do - * not change cp0_epc due to the instruction - * - * According to the spec: - * 1) it shouldnt be a branch :-) - * 2) it can be a COP instruction :-( - * 3) if we are tring to run a protected memory space we must take - * special care on memory access instructions :-( - */ - -/* - * "Trampoline" return routine to catch exception following - * execution of delay-slot instruction execution. - */ - -int do_dsemulret(struct pt_regs *xcp) -{ -#ifdef DSEMUL_TRACE - printk("desemulret\n"); -#endif - /* Set EPC to return to post-branch instruction */ - xcp->cp0_epc = current->thread.dsemul_epc; - /* - * Clear the state that got us here. - */ - current->thread.dsemul_aerpc = (unsigned long) 0; - - return 0; -} - - -#define AdELOAD 0x8c000001 /* lw $0,1($0) */ - -static int -mips_dsemul(struct pt_regs *xcp, mips_instruction ir, vaddr_t cpc) -{ - mips_instruction *dsemul_insns; - mips_instruction forcetrap; - extern asmlinkage void handle_dsemulret(void); - - if (ir == 0) { /* a nop is easy */ - xcp->cp0_epc = VA_TO_REG(cpc); - return 0; - } -#ifdef DSEMUL_TRACE - printk("desemul %p %p\n", REG_TO_VA(xcp->cp0_epc), cpc); -#endif - - /* - * The strategy is to push the instruction onto the user stack - * and put a trap after it which we can catch and jump to - * the required address any alternative apart from full - * instruction emulation!!. - */ - dsemul_insns = (mips_instruction *) (xcp->regs[29] & ~3); - dsemul_insns -= 3; /* Two instructions, plus one for luck ;-) */ - - /* Verify that the stack pointer is not competely insane */ - if (verify_area(VERIFY_WRITE, dsemul_insns, - sizeof(mips_instruction) * 2)) - return SIGBUS; - - if (mips_put_word(xcp, &dsemul_insns[0], ir)) { - fpuemuprivate.stats.errors++; - return SIGBUS; - } - - /* - * Algorithmics used a system call instruction, and - * borrowed that vector. MIPS/Linux version is a bit - * more heavyweight in the interests of portability and - * multiprocessor support. We flag the thread for special - * handling in the unaligned access handler and force an - * address error excpetion. - */ - - /* If one is *really* paranoid, one tests for a bad stack pointer */ - if ((xcp->regs[29] & 0x3) == 0x3) - forcetrap = AdELOAD - 1; - else - forcetrap = AdELOAD; - - if (mips_put_word(xcp, &dsemul_insns[1], forcetrap)) { - fpuemuprivate.stats.errors++; - return (SIGBUS); - } - - /* Set thread state to catch and handle the exception */ - current->thread.dsemul_epc = (unsigned long) cpc; - current->thread.dsemul_aerpc = (unsigned long) &dsemul_insns[1]; - xcp->cp0_epc = VA_TO_REG & dsemul_insns[0]; - flush_cache_sigtramp((unsigned long) dsemul_insns); - - return SIGILL; /* force out of emulation loop */ -} - -/* * Conversion table from MIPS compare ops 48-63 - * cond = ieee754dp_cmp(x,y,IEEE754_UN); + * cond = ieee754dp_cmp(x,y,IEEE754_UN,sig); */ static const unsigned char cmptab[8] = { - 0, /* cmp_0 (sig) cmp_sf */ - IEEE754_CUN, /* cmp_un (sig) cmp_ngle */ - IEEE754_CEQ, /* cmp_eq (sig) cmp_seq */ - IEEE754_CEQ | IEEE754_CUN, /* cmp_ueq (sig) cmp_ngl */ - IEEE754_CLT, /* cmp_olt (sig) cmp_lt */ - IEEE754_CLT | IEEE754_CUN, /* cmp_ult (sig) cmp_nge */ - IEEE754_CLT | IEEE754_CEQ, /* cmp_ole (sig) cmp_le */ - IEEE754_CLT | IEEE754_CEQ | IEEE754_CUN, /* cmp_ule (sig) cmp_ngt */ + 0, /* cmp_0 (sig) cmp_sf */ + IEEE754_CUN, /* cmp_un (sig) cmp_ngle */ + IEEE754_CEQ, /* cmp_eq (sig) cmp_seq */ + IEEE754_CEQ | IEEE754_CUN, /* cmp_ueq (sig) cmp_ngl */ + IEEE754_CLT, /* cmp_olt (sig) cmp_lt */ + IEEE754_CLT | IEEE754_CUN, /* cmp_ult (sig) cmp_nge */ + IEEE754_CLT | IEEE754_CEQ, /* cmp_ole (sig) cmp_le */ + IEEE754_CLT | IEEE754_CEQ | IEEE754_CUN, /* cmp_ule (sig) cmp_ngt */ }; -#define SIFROMREG(si,x) ((si) = ctx->regs[x]) -#define SITOREG(si,x) (ctx->regs[x] = (int)(si)) - -#if __mips64 && !defined(SINGLE_ONLY_FPU) -#define DIFROMREG(di,x) ((di) = ctx->regs[x]) -#define DITOREG(di,x) (ctx->regs[x] = (di)) -#endif - -#define SPFROMREG(sp,x) ((sp).bits = ctx->regs[x]) -#define SPTOREG(sp,x) (ctx->regs[x] = (sp).bits) - -#ifdef CP0_STATUS_FR_SUPPORT -#define DPFROMREG(dp,x) ((dp).bits = \ - ctx->regs[(xcp->cp0_status & ST0_FR) ? x : (x & ~1)]) -#define DPTOREG(dp,x) (ctx->regs[(xcp->cp0_status & ST0_FR) ? x : (x & ~1)]\ - = (dp).bits) -#else -/* Beware: MIPS COP1 doubles are always little_word endian in registers */ -#define DPFROMREG(dp,x) \ - ((dp).bits = ((unsigned long long)ctx->regs[(x)+1] << 32) | ctx->regs[x]) -#define DPTOREG(dp,x) \ - (ctx->regs[x] = (dp).bits, ctx->regs[(x)+1] = (dp).bits >> 32) -#endif #if __mips >= 4 && __mips != 32 @@ -881,6 +569,22 @@ static const unsigned char cmptab[8] = { * Additional MIPS4 instructions */ +#define DEF3OP(name, p, f1, f2, f3) \ +static ieee754##p fpemu_##p##_##name (ieee754##p r, ieee754##p s, \ + ieee754##p t) \ +{ \ + struct ieee754_csr ieee754_csr_save; \ + s = f1 (s, t); \ + ieee754_csr_save = ieee754_csr; \ + s = f2 (s, r); \ + ieee754_csr_save.cx |= ieee754_csr.cx; \ + ieee754_csr_save.sx |= ieee754_csr.sx; \ + s = f3 (s); \ + ieee754_csr.cx |= ieee754_csr_save.cx; \ + ieee754_csr.sx |= ieee754_csr_save.sx; \ + return s; \ +} + static ieee754dp fpemu_dp_recip(ieee754dp d) { return ieee754dp_div(ieee754dp_one(0), d); @@ -901,287 +605,184 @@ static ieee754sp fpemu_sp_rsqrt(ieee754sp s) return ieee754sp_div(ieee754sp_one(0), ieee754sp_sqrt(s)); } - -static ieee754dp fpemu_dp_madd(ieee754dp r, ieee754dp s, ieee754dp t) -{ - return ieee754dp_add(ieee754dp_mul(s, t), r); -} - -static ieee754dp fpemu_dp_msub(ieee754dp r, ieee754dp s, ieee754dp t) -{ - return ieee754dp_sub(ieee754dp_mul(s, t), r); -} - -static ieee754dp fpemu_dp_nmadd(ieee754dp r, ieee754dp s, ieee754dp t) -{ - return ieee754dp_neg(ieee754dp_add(ieee754dp_mul(s, t), r)); -} - -static ieee754dp fpemu_dp_nmsub(ieee754dp r, ieee754dp s, ieee754dp t) -{ - return ieee754dp_neg(ieee754dp_sub(ieee754dp_mul(s, t), r)); -} - - -static ieee754sp fpemu_sp_madd(ieee754sp r, ieee754sp s, ieee754sp t) -{ - return ieee754sp_add(ieee754sp_mul(s, t), r); -} - -static ieee754sp fpemu_sp_msub(ieee754sp r, ieee754sp s, ieee754sp t) -{ - return ieee754sp_sub(ieee754sp_mul(s, t), r); -} - -static ieee754sp fpemu_sp_nmadd(ieee754sp r, ieee754sp s, ieee754sp t) -{ - return ieee754sp_neg(ieee754sp_add(ieee754sp_mul(s, t), r)); -} - -static ieee754sp fpemu_sp_nmsub(ieee754sp r, ieee754sp s, ieee754sp t) -{ - return ieee754sp_neg(ieee754sp_sub(ieee754sp_mul(s, t), r)); -} +DEF3OP(madd, sp, ieee754sp_mul, ieee754sp_add,); +DEF3OP(msub, sp, ieee754sp_mul, ieee754sp_sub,); +DEF3OP(nmadd, sp, ieee754sp_mul, ieee754sp_add, ieee754sp_neg); +DEF3OP(nmsub, sp, ieee754sp_mul, ieee754sp_sub, ieee754sp_neg); +DEF3OP(madd, dp, ieee754dp_mul, ieee754dp_add,); +DEF3OP(msub, dp, ieee754dp_mul, ieee754dp_sub,); +DEF3OP(nmadd, dp, ieee754dp_mul, ieee754dp_add, ieee754dp_neg); +DEF3OP(nmsub, dp, ieee754dp_mul, ieee754dp_sub, ieee754dp_neg); static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_soft_struct *ctx, - mips_instruction ir) + mips_instruction ir) { unsigned rcsr = 0; /* resulting csr */ fpuemuprivate.stats.cp1xops++; switch (MIPSInst_FMA_FFMT(ir)) { - case s_fmt: /* 0 */ - { - ieee754sp(*handler) (ieee754sp, ieee754sp, - ieee754sp); - ieee754sp fd, fr, fs, ft; - - switch (MIPSInst_FUNC(ir)) { - case lwxc1_op: - { - void *va = - REG_TO_VA(xcp-> - regs[MIPSInst_FR(ir)] - + - xcp-> - regs[MIPSInst_FT - (ir)]); - fpureg_t val; - int err = 0; - val = mips_get_word(xcp, va, &err); - if (err) { - fpuemuprivate.stats. - errors++; - return SIGBUS; - } - if (xcp->cp0_status & ST0_FR) { - /* load whole register */ - ctx-> - regs[MIPSInst_FD(ir)] = - val; - } else if (MIPSInst_FD(ir) & 1) { - /* load to m.s. 32 bits */ -#if defined(SINGLE_ONLY_FPU) - /* illegal register in single-float mode */ - return SIGILL; -#else - ctx-> - regs[ - (MIPSInst_FD(ir) & - ~1)] &= - 0xffffffff; - ctx-> - regs[ - (MIPSInst_FD(ir) & - ~1)] |= - val << 32; + case s_fmt:{ /* 0 */ + + ieee754sp(*handler) (ieee754sp, ieee754sp, ieee754sp); + ieee754sp fd, fr, fs, ft; + u32 *va; + u32 val; + + switch (MIPSInst_FUNC(ir)) { + case lwxc1_op: + va = REG_TO_VA(xcp->regs[MIPSInst_FR(ir)] + + xcp->regs[MIPSInst_FT(ir)]); + + fpuemuprivate.stats.loads++; + if (get_user(val, va)) { + fpuemuprivate.stats.errors++; + return SIGBUS; + } +#ifdef SINGLE_ONLY_FPU + if (MIPSInst_FD(ir) & 1) { + /* illegal register in single-float + * mode + */ + return SIGILL; + } #endif - } else { - /* load to l.s. 32 bits */ - ctx-> - regs[MIPSInst_FD(ir)] - &= ~0xffffffffLL; - ctx-> - regs[MIPSInst_FD(ir)] - |= val; - } - } - break; + SITOREG(val, MIPSInst_FD(ir)); + break; - case swxc1_op: - { - void *va = - REG_TO_VA(xcp-> - regs[MIPSInst_FR(ir)] - + - xcp-> - regs[MIPSInst_FT - (ir)]); - unsigned int val; - if (xcp->cp0_status & ST0_FR) { - /* store whole register */ - val = - ctx-> - regs[MIPSInst_FS(ir)]; - } else if (MIPSInst_FS(ir) & 1) { -#if defined(SINGLE_ONLY_FPU) - /* illegal register in single-float mode */ - return SIGILL; -#else - /* store from m.s. 32 bits */ - val = - ctx-> - regs[ - (MIPSInst_FS(ir) & - ~1)] >> 32; + case swxc1_op: + va = REG_TO_VA(xcp->regs[MIPSInst_FR(ir)] + + xcp->regs[MIPSInst_FT(ir)]); + + fpuemuprivate.stats.stores++; +#ifdef SINGLE_ONLY_FPU + if (MIPSInst_FS(ir) & 1) { + /* illegal register in single-float + * mode + */ + return SIGILL; + } #endif - } else { - /* store from l.s. 32 bits */ - val = - ctx-> - regs[MIPSInst_FS(ir)]; - } - if (mips_put_word(xcp, va, val)) { - fpuemuprivate.stats. - errors++; - return SIGBUS; - } - } - break; - case madd_s_op: - handler = fpemu_sp_madd; - goto scoptop; - case msub_s_op: - handler = fpemu_sp_msub; - goto scoptop; - case nmadd_s_op: - handler = fpemu_sp_nmadd; - goto scoptop; - case nmsub_s_op: - handler = fpemu_sp_nmsub; - goto scoptop; - - scoptop: - SPFROMREG(fr, MIPSInst_FR(ir)); - SPFROMREG(fs, MIPSInst_FS(ir)); - SPFROMREG(ft, MIPSInst_FT(ir)); - fd = (*handler) (fr, fs, ft); - SPTOREG(fd, MIPSInst_FD(ir)); - - copcsr: - if (ieee754_cxtest(IEEE754_INEXACT)) - rcsr |= - FPU_CSR_INE_X | FPU_CSR_INE_S; - if (ieee754_cxtest(IEEE754_UNDERFLOW)) - rcsr |= - FPU_CSR_UDF_X | FPU_CSR_UDF_S; - if (ieee754_cxtest(IEEE754_OVERFLOW)) - rcsr |= - FPU_CSR_OVF_X | FPU_CSR_OVF_S; - if (ieee754_cxtest - (IEEE754_INVALID_OPERATION)) rcsr |= - FPU_CSR_INV_X | FPU_CSR_INV_S; - - ctx->sr = - (ctx->sr & ~FPU_CSR_ALL_X) | rcsr; - if ((ctx->sr >> 5) & ctx-> - sr & FPU_CSR_ALL_E) { - /*printk ("SIGFPE: fpu csr = %08x\n",ctx->sr); */ - return SIGFPE; - } + SIFROMREG(val, MIPSInst_FS(ir)); + if (put_user(val, va)) { + fpuemuprivate.stats.errors++; + return SIGBUS; + } + break; - break; + case madd_s_op: + handler = fpemu_sp_madd; + goto scoptop; + case msub_s_op: + handler = fpemu_sp_msub; + goto scoptop; + case nmadd_s_op: + handler = fpemu_sp_nmadd; + goto scoptop; + case nmsub_s_op: + handler = fpemu_sp_nmsub; + goto scoptop; + + scoptop: + SPFROMREG(fr, MIPSInst_FR(ir)); + SPFROMREG(fs, MIPSInst_FS(ir)); + SPFROMREG(ft, MIPSInst_FT(ir)); + fd = (*handler) (fr, fs, ft); + SPTOREG(fd, MIPSInst_FD(ir)); - default: - return SIGILL; + copcsr: + if (ieee754_cxtest(IEEE754_INEXACT)) + rcsr |= FPU_CSR_INE_X | FPU_CSR_INE_S; + if (ieee754_cxtest(IEEE754_UNDERFLOW)) + rcsr |= FPU_CSR_UDF_X | FPU_CSR_UDF_S; + if (ieee754_cxtest(IEEE754_OVERFLOW)) + rcsr |= FPU_CSR_OVF_X | FPU_CSR_OVF_S; + if (ieee754_cxtest(IEEE754_INVALID_OPERATION)) + rcsr |= FPU_CSR_INV_X | FPU_CSR_INV_S; + + ctx->sr = (ctx->sr & ~FPU_CSR_ALL_X) | rcsr; + if (ieee754_csr.nod) + ctx->sr |= 0x1000000; + if ((ctx->sr >> 5) & ctx->sr & FPU_CSR_ALL_E) { + /*printk ("SIGFPE: fpu csr = %08x\n", + ctx->sr); */ + return SIGFPE; } + + break; + + default: + return SIGILL; } break; + } -#if !defined(SINGLE_ONLY_FPU) - case d_fmt: /* 1 */ - { - ieee754dp(*handler) (ieee754dp, ieee754dp, - ieee754dp); - ieee754dp fd, fr, fs, ft; - - switch (MIPSInst_FUNC(ir)) { - case ldxc1_op: - { - void *va = - REG_TO_VA(xcp-> - regs[MIPSInst_FR(ir)] - + - xcp-> - regs[MIPSInst_FT - (ir)]); - int err = 0; - ctx->regs[MIPSInst_FD(ir)] = - mips_get_dword(xcp, va, &err); - if (err) { - fpuemuprivate.stats. - errors++; - return SIGBUS; - } - } - break; +#ifndef SINGLE_ONLY_FPU + case d_fmt:{ /* 1 */ + ieee754dp(*handler) (ieee754dp, ieee754dp, ieee754dp); + ieee754dp fd, fr, fs, ft; + u64 *va; + u64 val; - case sdxc1_op: - { - void *va = - REG_TO_VA(xcp-> - regs[MIPSInst_FR(ir)] - + - xcp-> - regs[MIPSInst_FT - (ir)]); - if (mips_put_dword - (xcp, va, - ctx->regs[MIPSInst_FS(ir)])) { - fpuemuprivate.stats. - errors++; - return SIGBUS; - } - } - break; + switch (MIPSInst_FUNC(ir)) { + case ldxc1_op: + va = REG_TO_VA(xcp->regs[MIPSInst_FR(ir)] + + xcp->regs[MIPSInst_FT(ir)]); - case madd_d_op: - handler = fpemu_dp_madd; - goto dcoptop; - case msub_d_op: - handler = fpemu_dp_msub; - goto dcoptop; - case nmadd_d_op: - handler = fpemu_dp_nmadd; - goto dcoptop; - case nmsub_d_op: - handler = fpemu_dp_nmsub; - goto dcoptop; - - dcoptop: - DPFROMREG(fr, MIPSInst_FR(ir)); - DPFROMREG(fs, MIPSInst_FS(ir)); - DPFROMREG(ft, MIPSInst_FT(ir)); - fd = (*handler) (fr, fs, ft); - DPTOREG(fd, MIPSInst_FD(ir)); - goto copcsr; + fpuemuprivate.stats.loads++; + if (get_user(val, va)) { + fpuemuprivate.stats.errors++; + return SIGBUS; + } + DITOREG(val, MIPSInst_FD(ir)); + break; - default: - return SIGILL; + case sdxc1_op: + va = REG_TO_VA(xcp->regs[MIPSInst_FR(ir)] + + xcp->regs[MIPSInst_FT(ir)]); + + fpuemuprivate.stats.stores++; + DIFROMREG(val, MIPSInst_FS(ir)); + if (put_user(val, va)) { + fpuemuprivate.stats.errors++; + return SIGBUS; } + break; + + case madd_d_op: + handler = fpemu_dp_madd; + goto dcoptop; + case msub_d_op: + handler = fpemu_dp_msub; + goto dcoptop; + case nmadd_d_op: + handler = fpemu_dp_nmadd; + goto dcoptop; + case nmsub_d_op: + handler = fpemu_dp_nmsub; + goto dcoptop; + + dcoptop: + DPFROMREG(fr, MIPSInst_FR(ir)); + DPFROMREG(fs, MIPSInst_FS(ir)); + DPFROMREG(ft, MIPSInst_FT(ir)); + fd = (*handler) (fr, fs, ft); + DPTOREG(fd, MIPSInst_FD(ir)); + goto copcsr; + + default: + return SIGILL; } break; + } #endif case 0x7: /* 7 */ - { - if (MIPSInst_FUNC(ir) != pfetch_op) { - return SIGILL; - } - /* ignore prefx operation */ + if (MIPSInst_FUNC(ir) != pfetch_op) { + return SIGILL; } + /* ignore prefx operation */ break; default: @@ -1198,7 +799,7 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_soft_struct *ctx, * Emulate a single COP1 arithmetic instruction. */ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_soft_struct *ctx, - mips_instruction ir) + mips_instruction ir) { int rfmt; /* resulting format */ unsigned rcsr = 0; /* resulting csr */ @@ -1208,49 +809,52 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_soft_struct *ctx, ieee754sp s; int w; #if __mips64 - long long l; + s64 l; #endif } rv; /* resulting value */ fpuemuprivate.stats.cp1ops++; switch (rfmt = (MIPSInst_FFMT(ir) & 0xf)) { - case s_fmt: { /* 0 */ - ieee754sp(*handler) (); + case s_fmt:{ /* 0 */ + union { + ieee754sp(*b) (ieee754sp, ieee754sp); + ieee754sp(*u) (ieee754sp); + } handler; switch (MIPSInst_FUNC(ir)) { /* binary ops */ case fadd_op: - handler = ieee754sp_add; + handler.b = ieee754sp_add; goto scopbop; case fsub_op: - handler = ieee754sp_sub; + handler.b = ieee754sp_sub; goto scopbop; case fmul_op: - handler = ieee754sp_mul; + handler.b = ieee754sp_mul; goto scopbop; case fdiv_op: - handler = ieee754sp_div; + handler.b = ieee754sp_div; goto scopbop; /* unary ops */ #if __mips >= 2 || __mips64 case fsqrt_op: - handler = ieee754sp_sqrt; + handler.u = ieee754sp_sqrt; goto scopuop; #endif #if __mips >= 4 && __mips != 32 case frsqrt_op: - handler = fpemu_sp_rsqrt; + handler.u = fpemu_sp_rsqrt; goto scopuop; case frecip_op: - handler = fpemu_sp_recip; + handler.u = fpemu_sp_recip; goto scopuop; #endif #if __mips >= 4 case fmovc_op: cond = fpucondbit[MIPSInst_FT(ir) >> 2]; if (((ctx->sr & cond) != 0) != - ((MIPSInst_FT(ir) & 1) != 0)) + ((MIPSInst_FT(ir) & 1) != 0)) return 0; SPFROMREG(rv.s, MIPSInst_FS(ir)); break; @@ -1266,35 +870,36 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_soft_struct *ctx, break; #endif case fabs_op: - handler = ieee754sp_abs; + handler.u = ieee754sp_abs; goto scopuop; case fneg_op: - handler = ieee754sp_neg; + handler.u = ieee754sp_neg; goto scopuop; case fmov_op: /* an easy one */ SPFROMREG(rv.s, MIPSInst_FS(ir)); - break; + goto copcsr; + /* binary op on handler */ -scopbop: + scopbop: { ieee754sp fs, ft; SPFROMREG(fs, MIPSInst_FS(ir)); SPFROMREG(ft, MIPSInst_FT(ir)); - rv.s = (*handler) (fs, ft); + rv.s = (*handler.b) (fs, ft); goto copcsr; } -scopuop: + scopuop: { ieee754sp fs; SPFROMREG(fs, MIPSInst_FS(ir)); - rv.s = (*handler) (fs); + rv.s = (*handler.u) (fs); goto copcsr; } -copcsr: + copcsr: if (ieee754_cxtest(IEEE754_INEXACT)) rcsr |= FPU_CSR_INE_X | FPU_CSR_INE_S; if (ieee754_cxtest(IEEE754_UNDERFLOW)) @@ -1303,15 +908,14 @@ copcsr: rcsr |= FPU_CSR_OVF_X | FPU_CSR_OVF_S; if (ieee754_cxtest(IEEE754_ZERO_DIVIDE)) rcsr |= FPU_CSR_DIV_X | FPU_CSR_DIV_S; - if (ieee754_cxtest - (IEEE754_INVALID_OPERATION)) rcsr |= - FPU_CSR_INV_X | FPU_CSR_INV_S; - break; + if (ieee754_cxtest(IEEE754_INVALID_OPERATION)) + rcsr |= FPU_CSR_INV_X | FPU_CSR_INV_S; + break; - /* unary conv ops */ + /* unary conv ops */ case fcvts_op: return SIGILL; /* not defined */ - case fcvtd_op: { + case fcvtd_op:{ #ifdef SINGLE_ONLY_FPU return SIGILL; /* not defined */ #else @@ -1323,7 +927,7 @@ copcsr: goto copcsr; } #endif - case fcvtw_op: { + case fcvtw_op:{ ieee754sp fs; SPFROMREG(fs, MIPSInst_FS(ir)); @@ -1336,7 +940,7 @@ copcsr: case fround_op: case ftrunc_op: case fceil_op: - case ffloor_op: { + case ffloor_op:{ unsigned int oldrm = ieee754_csr.rm; ieee754sp fs; @@ -1350,7 +954,7 @@ copcsr: #endif /* __mips >= 2 */ #if __mips64 && !defined(SINGLE_ONLY_FPU) - case fcvtl_op: { + case fcvtl_op:{ ieee754sp fs; SPFROMREG(fs, MIPSInst_FS(ir)); @@ -1362,7 +966,7 @@ copcsr: case froundl_op: case ftruncl_op: case fceill_op: - case ffloorl_op: { + case ffloorl_op:{ unsigned int oldrm = ieee754_csr.rm; ieee754sp fs; @@ -1382,55 +986,65 @@ copcsr: SPFROMREG(fs, MIPSInst_FS(ir)); SPFROMREG(ft, MIPSInst_FT(ir)); - rv.w = ieee754sp_cmp(fs, ft, cmptab[cmpop & 0x7]); + rv.w = ieee754sp_cmp(fs, ft, + cmptab[cmpop & 0x7], cmpop & 0x8); rfmt = -1; - if ((cmpop & 0x8) && ieee754_cxtest(IEEE754_INVALID_OPERATION)) + if ((cmpop & 0x8) && ieee754_cxtest + (IEEE754_INVALID_OPERATION)) rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S; - } else { - return SIGILL; - } - break; + else + goto copcsr; + + } + else { + return SIGILL; } break; } + break; + } -#if !defined(SINGLE_ONLY_FPU) - case d_fmt: { - ieee754dp(*handler) (); +#ifndef SINGLE_ONLY_FPU + case d_fmt:{ + union { + ieee754dp(*b) (ieee754dp, ieee754dp); + ieee754dp(*u) (ieee754dp); + } handler; switch (MIPSInst_FUNC(ir)) { /* binary ops */ case fadd_op: - handler = ieee754dp_add; + handler.b = ieee754dp_add; goto dcopbop; case fsub_op: - handler = ieee754dp_sub; + handler.b = ieee754dp_sub; goto dcopbop; case fmul_op: - handler = ieee754dp_mul; + handler.b = ieee754dp_mul; goto dcopbop; case fdiv_op: - handler = ieee754dp_div; + handler.b = ieee754dp_div; goto dcopbop; /* unary ops */ #if __mips >= 2 || __mips64 case fsqrt_op: - handler = ieee754dp_sqrt; + handler.u = ieee754dp_sqrt; goto dcopuop; #endif #if __mips >= 4 && __mips != 32 case frsqrt_op: - handler = fpemu_dp_rsqrt; + handler.u = fpemu_dp_rsqrt; goto dcopuop; case frecip_op: - handler = fpemu_dp_recip; + handler.u = fpemu_dp_recip; goto dcopuop; #endif #if __mips >= 4 case fmovc_op: cond = fpucondbit[MIPSInst_FT(ir) >> 2]; - if (((ctx->sr & cond) != 0) != ((MIPSInst_FT(ir) & 1) != 0)) + if (((ctx->sr & cond) != 0) != + ((MIPSInst_FT(ir) & 1) != 0)) return 0; DPFROMREG(rv.d, MIPSInst_FS(ir)); break; @@ -1446,40 +1060,38 @@ copcsr: break; #endif case fabs_op: - handler = ieee754dp_abs; + handler.u = ieee754dp_abs; goto dcopuop; case fneg_op: - handler = ieee754dp_neg; + handler.u = ieee754dp_neg; goto dcopuop; case fmov_op: /* an easy one */ DPFROMREG(rv.d, MIPSInst_FS(ir)); - break; + goto copcsr; /* binary op on handler */ -dcopbop: - { + dcopbop:{ ieee754dp fs, ft; DPFROMREG(fs, MIPSInst_FS(ir)); DPFROMREG(ft, MIPSInst_FT(ir)); - rv.d = (*handler) (fs, ft); + rv.d = (*handler.b) (fs, ft); goto copcsr; } -dcopuop: - { + dcopuop:{ ieee754dp fs; DPFROMREG(fs, MIPSInst_FS(ir)); - rv.d = (*handler) (fs); + rv.d = (*handler.u) (fs); goto copcsr; } - /* unary conv ops */ - case fcvts_op: { + /* unary conv ops */ + case fcvts_op:{ ieee754dp fs; DPFROMREG(fs, MIPSInst_FS(ir)); @@ -1490,7 +1102,7 @@ dcopuop: case fcvtd_op: return SIGILL; /* not defined */ - case fcvtw_op: { + case fcvtw_op:{ ieee754dp fs; DPFROMREG(fs, MIPSInst_FS(ir)); @@ -1503,7 +1115,7 @@ dcopuop: case fround_op: case ftrunc_op: case fceil_op: - case ffloor_op: { + case ffloor_op:{ unsigned int oldrm = ieee754_csr.rm; ieee754dp fs; @@ -1517,7 +1129,7 @@ dcopuop: #endif #if __mips64 && !defined(SINGLE_ONLY_FPU) - case fcvtl_op: { + case fcvtl_op:{ ieee754dp fs; DPFROMREG(fs, MIPSInst_FS(ir)); @@ -1529,7 +1141,7 @@ dcopuop: case froundl_op: case ftruncl_op: case fceill_op: - case ffloorl_op: { + case ffloorl_op:{ unsigned int oldrm = ieee754_csr.rm; ieee754dp fs; @@ -1549,30 +1161,42 @@ dcopuop: DPFROMREG(fs, MIPSInst_FS(ir)); DPFROMREG(ft, MIPSInst_FT(ir)); - rv.w = ieee754dp_cmp(fs, ft, cmptab[cmpop & 0x7]); + rv.w = ieee754dp_cmp(fs, ft, + cmptab[cmpop & 0x7], cmpop & 0x8); rfmt = -1; - if ((cmpop & 0x8) && ieee754_cxtest (IEEE754_INVALID_OPERATION)) + if ((cmpop & 0x8) + && + ieee754_cxtest + (IEEE754_INVALID_OPERATION)) rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S; - } else { + else + goto copcsr; + + } + else { return SIGILL; } break; } break; } -#endif /* !defined(SINGLE_ONLY_FPU) */ +#endif /* ifndef SINGLE_ONLY_FPU */ + + case w_fmt:{ + ieee754sp fs; - case w_fmt: { switch (MIPSInst_FUNC(ir)) { case fcvts_op: /* convert word to single precision real */ - rv.s = ieee754sp_fint(ctx-> regs[MIPSInst_FS(ir)]); + SPFROMREG(fs, MIPSInst_FS(ir)); + rv.s = ieee754sp_fint(fs.bits); rfmt = s_fmt; goto copcsr; -#if !defined(SINGLE_ONLY_FPU) +#ifndef SINGLE_ONLY_FPU case fcvtd_op: /* convert word to double precision real */ - rv.d = ieee754dp_fint(ctx-> regs[MIPSInst_FS(ir)]); + SPFROMREG(fs, MIPSInst_FS(ir)); + rv.d = ieee754dp_fint(fs.bits); rfmt = d_fmt; goto copcsr; #endif @@ -1583,16 +1207,16 @@ dcopuop: } #if __mips64 && !defined(SINGLE_ONLY_FPU) - case l_fmt: { + case l_fmt:{ switch (MIPSInst_FUNC(ir)) { case fcvts_op: /* convert long to single precision real */ - rv.s = ieee754sp_flong(ctx-> regs[MIPSInst_FS(ir)]); + rv.s = ieee754sp_flong(ctx->regs[MIPSInst_FS(ir)]); rfmt = s_fmt; goto copcsr; case fcvtd_op: /* convert long to double precision real */ - rv.d = ieee754dp_flong(ctx-> regs[MIPSInst_FS(ir)]); + rv.d = ieee754dp_flong(ctx->regs[MIPSInst_FS(ir)]); rfmt = d_fmt; goto copcsr; default: @@ -1619,11 +1243,11 @@ dcopuop: return SIGFPE; } - /* + /* * Now we can safely write the result back to the register file. */ switch (rfmt) { - case -1: { + case -1:{ #if __mips >= 4 cond = fpucondbit[MIPSInst_FD(ir) >> 2]; #else @@ -1635,7 +1259,7 @@ dcopuop: ctx->sr &= ~cond; break; } -#if !defined(SINGLE_ONLY_FPU) +#ifndef SINGLE_ONLY_FPU case d_fmt: DPTOREG(rv.d, MIPSInst_FD(ir)); break; @@ -1658,38 +1282,39 @@ dcopuop: return 0; } - -/* - * Emulate the floating point instruction at EPC, and continue to run until we - * hit a non-fp instruction, or a backward branch. This cuts down dramatically - * on the per instruction exception overhead. - */ -int fpu_emulator_cop1Handler(struct pt_regs *xcp) +int fpu_emulator_cop1Handler(int xcptno, struct pt_regs *xcp, + struct mips_fpu_soft_struct *ctx) { - struct mips_fpu_soft_struct *ctx = ¤t->thread.fpu.soft; - unsigned long oldepc, prevepc; - unsigned int insn; + gpreg_t oldepc, prevepc; + mips_instruction insn; int sig = 0; - int err = 0; oldepc = xcp->cp0_epc; do { - cond_resched(); - prevepc = xcp->cp0_epc; - insn = mips_get_word(xcp, REG_TO_VA(xcp->cp0_epc), &err); - if (err) { + + if (get_user(insn, (mips_instruction *) xcp->cp0_epc)) { fpuemuprivate.stats.errors++; return SIGBUS; } - if (insn != 0) - sig = cop1Emulate(xcp, ctx); - else + if (insn == 0) xcp->cp0_epc += 4; /* skip nops */ + else { + /* Update ieee754_csr. Only relevant if we have a + h/w FPU */ + ieee754_csr.nod = (ctx->sr & 0x1000000) != 0; + ieee754_csr.rm = ieee_rm[ctx->sr & 0x3]; + ieee754_csr.cx = (ctx->sr >> 12) & 0x1f; + sig = cop1Emulate(xcp, ctx); + } - if (mips_cpu.options & MIPS_CPU_FPU) + if (cpu_has_fpu) break; - } while (xcp->cp0_epc > prevepc && sig == 0); + if (sig) + break; + + cond_resched(); + } while (xcp->cp0_epc > prevepc); /* SIGILL indicates a non-fpu instruction */ if (sig == SIGILL && xcp->cp0_epc != oldepc) @@ -1698,80 +1323,3 @@ int fpu_emulator_cop1Handler(struct pt_regs *xcp) return sig; } - - -#ifdef NOTDEF -/* - * Patch up the hardware fpu state when an f.p. exception occurs. - */ -static int cop1Patcher(int xcptno, struct pt_regs *xcp) -{ - struct mips_fpu_soft_struct *ctx = ¤t->thread.fpu.soft; - unsigned sr; - int sig; - - /* reenable Cp1, else fpe_save() will get nested exception */ - sr = mips_bissr(ST0_CU1); - - /* get fpu registers and status, then clear pending exceptions */ - fpe_save(ctx); - fpe_setsr(ctx->sr &= ~FPU_CSR_ALL_X); - - /* get current rounding mode for IEEE library, and emulate insn */ - ieee754_csr.rm = ieee_rm[ctx->sr & 0x3]; - sig = cop1Emulate(xcp, ctx); - - /* don't return with f.p. exceptions pending */ - ctx->sr &= ~FPU_CSR_ALL_X; - fpe_restore(ctx); - - mips_setsr(sr); - return sig; -} - -void _cop1_init(int emulate) -{ - extern int _nofpu; - - if (emulate) { - /* - * Install cop1 emulator to handle "coprocessor unusable" exception - */ - xcption(XCPTCPU, cop1Handler); - fpuemuactive = 1; /* tell dbg.c that we are in charge */ - _nofpu = 0; /* tell setjmp() it "has" an fpu */ - } else { - /* - * Install cop1 emulator for floating point exceptions only, - * i.e. denormalised results, underflow, overflow etc, which - * must be emulated in s/w. - */ -#ifdef 1 - /* r4000 or above use dedicate exception */ - xcption(XCPTFPE, cop1Patcher); -#else - /* r3000 et al use interrupt */ - extern int _sbd_getfpuintr(void); - int intno = _sbd_getfpuintr(); - intrupt(intno, cop1Patcher, 0); - mips_bissr(SR_IM0 << intno); -#endif - -#if (#cpu(r4640) || #cpu(r4650)) && !defined(SINGLE_ONLY_FPU) - /* For R4640/R4650 compiled *without* the -msingle-float flag, - then we share responsibility: the h/w handles the single - precision operations, and the trap emulator handles the - double precision. We set fpuemuactive so that dbg.c first - fetches the s/w state before saving the h/w state. */ - fpuemuactive = 1; - { - int i; - /* initialise the unused d.p high order words to be NaN */ - for (i = 0; i < 32; i++) - current->thread.fpu.soft.regs[i] = - 0x7ff80bad00000000LL; - } -#endif /* (r4640 || r4650) && !fpu(single) */ - } -} -#endif diff --git a/arch/mips/math-emu/dp_add.c b/arch/mips/math-emu/dp_add.c index 0c96f9b39de9..30e6551b2048 100644 --- a/arch/mips/math-emu/dp_add.c +++ b/arch/mips/math-emu/dp_add.c @@ -38,27 +38,23 @@ ieee754dp ieee754dp_add(ieee754dp x, ieee754dp y) CLEARCX; - switch (CLPAIR(xc, yc)) { - case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_SNAN): - return ieee754dp_nanxcpt(ieee754dp_bestnan(x, y), "add", x, - y); + FLUSHXDP; + FLUSHYDP; + switch (CLPAIR(xc, yc)) { + case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_QNAN): case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_SNAN): + case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_SNAN): case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_SNAN): case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_SNAN): case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_SNAN): case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_SNAN): - return ieee754dp_nanxcpt(y, "add", x, y); - - case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_QNAN): case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_ZERO): case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_NORM): case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_DNORM): case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_INF): - return ieee754dp_nanxcpt(x, "add", x, y); - - case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN): - return ieee754dp_bestnan(x, y); + SETCX(IEEE754_INVALID_OPERATION); + return ieee754dp_nanxcpt(ieee754dp_indef(), "add", x, y); case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_QNAN): case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_QNAN): @@ -66,6 +62,7 @@ ieee754dp ieee754dp_add(ieee754dp x, ieee754dp y) case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_QNAN): return y; + case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN): case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_ZERO): case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_NORM): case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_DNORM): @@ -73,7 +70,7 @@ ieee754dp ieee754dp_add(ieee754dp x, ieee754dp y) return x; - /* Inifity handling + /* Infinity handling */ case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_INF): diff --git a/arch/mips/math-emu/dp_cmp.c b/arch/mips/math-emu/dp_cmp.c index 34ec4a856ce9..e624e73674ea 100644 --- a/arch/mips/math-emu/dp_cmp.c +++ b/arch/mips/math-emu/dp_cmp.c @@ -27,21 +27,30 @@ #include "ieee754dp.h" -int ieee754dp_cmp(ieee754dp x, ieee754dp y, int cmp) +int ieee754dp_cmp(ieee754dp x, ieee754dp y, int cmp, int sig) { - CLEARCX; + COMPXDP; + COMPYDP; + + EXPLODEXDP; + EXPLODEYDP; + FLUSHXDP; + FLUSHYDP; + CLEARCX; /* Even clear inexact flag here */ if (ieee754dp_isnan(x) || ieee754dp_isnan(y)) { + if (sig || xc == IEEE754_CLASS_SNAN || yc == IEEE754_CLASS_SNAN) + SETCX(IEEE754_INVALID_OPERATION); if (cmp & IEEE754_CUN) return 1; if (cmp & (IEEE754_CLT | IEEE754_CGT)) { - if (SETCX(IEEE754_INVALID_OPERATION)) + if (sig && SETANDTESTCX(IEEE754_INVALID_OPERATION)) return ieee754si_xcpt(0, "fcmpf", x); } return 0; } else { - long long int vx = x.bits; - long long int vy = y.bits; + s64 vx = x.bits; + s64 vy = y.bits; if (vx < 0) vx = -vx ^ DP_SIGN_BIT; diff --git a/arch/mips/math-emu/dp_div.c b/arch/mips/math-emu/dp_div.c index 2d3df92de6f5..373ac99161ec 100644 --- a/arch/mips/math-emu/dp_div.c +++ b/arch/mips/math-emu/dp_div.c @@ -32,32 +32,28 @@ ieee754dp ieee754dp_div(ieee754dp x, ieee754dp y) COMPXDP; COMPYDP; - CLEARCX; - EXPLODEXDP; EXPLODEYDP; - switch (CLPAIR(xc, yc)) { - case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_SNAN): - return ieee754dp_nanxcpt(ieee754dp_bestnan(x, y), "div", x, - y); + CLEARCX; + + FLUSHXDP; + FLUSHYDP; + switch (CLPAIR(xc, yc)) { + case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_QNAN): case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_SNAN): + case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_SNAN): case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_SNAN): case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_SNAN): case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_SNAN): case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_SNAN): - return ieee754dp_nanxcpt(y, "div", x, y); - - case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_QNAN): case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_ZERO): case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_NORM): case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_DNORM): case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_INF): - return ieee754dp_nanxcpt(x, "div", x, y); - - case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN): - return ieee754dp_bestnan(x, y); + SETCX(IEEE754_INVALID_OPERATION); + return ieee754dp_nanxcpt(ieee754dp_indef(), "div", x, y); case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_QNAN): case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_QNAN): @@ -65,6 +61,7 @@ ieee754dp ieee754dp_div(ieee754dp x, ieee754dp y) case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_QNAN): return y; + case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN): case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_ZERO): case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_NORM): case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_DNORM): @@ -129,9 +126,9 @@ ieee754dp ieee754dp_div(ieee754dp x, ieee754dp y) { /* now the dirty work */ - unsigned long long rm = 0; + u64 rm = 0; int re = xe - ye; - unsigned long long bm; + u64 bm; for (bm = DP_MBIT(DP_MBITS + 2); bm; bm >>= 1) { if (xm >= ym) { diff --git a/arch/mips/math-emu/dp_flong.c b/arch/mips/math-emu/dp_flong.c index 7abf5ee30d27..fa5aaff8220e 100644 --- a/arch/mips/math-emu/dp_flong.c +++ b/arch/mips/math-emu/dp_flong.c @@ -27,7 +27,7 @@ #include "ieee754dp.h" -ieee754dp ieee754dp_flong(long long x) +ieee754dp ieee754dp_flong(s64 x) { COMPXDP; @@ -67,9 +67,9 @@ ieee754dp ieee754dp_flong(long long x) DPNORMRET1(xs, xe, xm, "dp_flong", x); } -ieee754dp ieee754dp_fulong(unsigned long long u) +ieee754dp ieee754dp_fulong(u64 u) { - if ((long long) u < 0) + if ((s64) u < 0) return ieee754dp_add(ieee754dp_1e63(), ieee754dp_flong(u & ~(1ULL << 63))); return ieee754dp_flong(u); diff --git a/arch/mips/math-emu/dp_frexp.c b/arch/mips/math-emu/dp_frexp.c index db2ef8543236..7d8c5e6a5153 100644 --- a/arch/mips/math-emu/dp_frexp.c +++ b/arch/mips/math-emu/dp_frexp.c @@ -27,7 +27,7 @@ #include "ieee754dp.h" -/* close to ieeep754dp_logb +/* close to ieeep754dp_logb */ ieee754dp ieee754dp_frexp(ieee754dp x, int *eptr) { diff --git a/arch/mips/math-emu/dp_fsp.c b/arch/mips/math-emu/dp_fsp.c index 7749e11d4ad6..133eb3e1a9ba 100644 --- a/arch/mips/math-emu/dp_fsp.c +++ b/arch/mips/math-emu/dp_fsp.c @@ -31,16 +31,20 @@ ieee754dp ieee754dp_fsp(ieee754sp x) { COMPXSP; + EXPLODEXSP; + CLEARCX; - EXPLODEXSP; + FLUSHXSP; switch (xc) { - case IEEE754_CLASS_QNAN: case IEEE754_CLASS_SNAN: + SETCX(IEEE754_INVALID_OPERATION); + return ieee754dp_nanxcpt(ieee754dp_indef(), "fsp"); + case IEEE754_CLASS_QNAN: return ieee754dp_nanxcpt(builddp(xs, DP_EMAX + 1 + DP_EBIAS, - ((unsigned long long) xm + ((u64) xm << (DP_MBITS - SP_MBITS))), "fsp", x); @@ -66,5 +70,5 @@ ieee754dp ieee754dp_fsp(ieee754sp x) xm &= ~SP_HIDDEN_BIT; return builddp(xs, xe + DP_EBIAS, - (unsigned long long) xm << (DP_MBITS - SP_MBITS)); + (u64) xm << (DP_MBITS - SP_MBITS)); } diff --git a/arch/mips/math-emu/dp_modf.c b/arch/mips/math-emu/dp_modf.c index 6bf0f2f149a0..d77052584c09 100644 --- a/arch/mips/math-emu/dp_modf.c +++ b/arch/mips/math-emu/dp_modf.c @@ -59,7 +59,7 @@ ieee754dp ieee754dp_modf(ieee754dp x, ieee754dp * ip) *ip = x; return ieee754dp_zero(xs); } - /* generate ipart mantissa by clearing bottom bits + /* generate ipart mantissa by clearing bottom bits */ *ip = builddp(xs, xe + DP_EBIAS, ((xm >> (DP_MBITS - xe)) << (DP_MBITS - xe)) & diff --git a/arch/mips/math-emu/dp_mul.c b/arch/mips/math-emu/dp_mul.c index 51fc87ff52b6..e78f88a8c4e3 100644 --- a/arch/mips/math-emu/dp_mul.c +++ b/arch/mips/math-emu/dp_mul.c @@ -32,32 +32,28 @@ ieee754dp ieee754dp_mul(ieee754dp x, ieee754dp y) COMPXDP; COMPYDP; - CLEARCX; - EXPLODEXDP; EXPLODEYDP; - switch (CLPAIR(xc, yc)) { - case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_SNAN): - return ieee754dp_nanxcpt(ieee754dp_bestnan(x, y), "mul", x, - y); + CLEARCX; + + FLUSHXDP; + FLUSHYDP; + switch (CLPAIR(xc, yc)) { + case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_QNAN): case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_SNAN): + case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_SNAN): case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_SNAN): case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_SNAN): case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_SNAN): case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_SNAN): - return ieee754dp_nanxcpt(y, "mul", x, y); - - case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_QNAN): case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_ZERO): case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_NORM): case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_DNORM): case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_INF): - return ieee754dp_nanxcpt(x, "mul", x, y); - - case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN): - return ieee754dp_bestnan(x, y); + SETCX(IEEE754_INVALID_OPERATION); + return ieee754dp_nanxcpt(ieee754dp_indef(), "mul", x, y); case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_QNAN): case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_QNAN): @@ -65,6 +61,7 @@ ieee754dp ieee754dp_mul(ieee754dp x, ieee754dp y) case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_QNAN): return y; + case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN): case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_ZERO): case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_NORM): case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_DNORM): @@ -114,7 +111,7 @@ ieee754dp ieee754dp_mul(ieee754dp x, ieee754dp y) { int re = xe + ye; int rs = xs ^ ys; - unsigned long long rm; + u64 rm; /* shunt to top of word */ xm <<= 64 - (DP_MBITS + 1); @@ -124,23 +121,23 @@ ieee754dp ieee754dp_mul(ieee754dp x, ieee754dp y) */ /* 32 * 32 => 64 */ -#define DPXMULT(x,y) ((unsigned long long)(x) * (unsigned long long)y) +#define DPXMULT(x,y) ((u64)(x) * (u64)y) { unsigned lxm = xm; unsigned hxm = xm >> 32; unsigned lym = ym; unsigned hym = ym >> 32; - unsigned long long lrm; - unsigned long long hrm; + u64 lrm; + u64 hrm; lrm = DPXMULT(lxm, lym); hrm = DPXMULT(hxm, hym); { - unsigned long long t = DPXMULT(lxm, hym); + u64 t = DPXMULT(lxm, hym); { - unsigned long long at = + u64 at = lrm + (t << 32); hrm += at < lrm; lrm = at; @@ -149,9 +146,9 @@ ieee754dp ieee754dp_mul(ieee754dp x, ieee754dp y) } { - unsigned long long t = DPXMULT(hxm, lym); + u64 t = DPXMULT(hxm, lym); { - unsigned long long at = + u64 at = lrm + (t << 32); hrm += at < lrm; lrm = at; @@ -164,7 +161,7 @@ ieee754dp ieee754dp_mul(ieee754dp x, ieee754dp y) /* * sticky shift down to normal rounding precision */ - if ((signed long long) rm < 0) { + if ((s64) rm < 0) { rm = (rm >> (64 - (DP_MBITS + 1 + 3))) | ((rm << (DP_MBITS + 1 + 3)) != 0); diff --git a/arch/mips/math-emu/dp_simple.c b/arch/mips/math-emu/dp_simple.c index f57eee2219ca..5f9d9d122a61 100644 --- a/arch/mips/math-emu/dp_simple.c +++ b/arch/mips/math-emu/dp_simple.c @@ -42,7 +42,16 @@ ieee754dp ieee754dp_copysign(ieee754dp x, ieee754dp y) ieee754dp ieee754dp_neg(ieee754dp x) { + COMPXDP; + + EXPLODEXDP; CLEARCX; + FLUSHXDP; + + if (xc == IEEE754_CLASS_SNAN) { + SETCX(IEEE754_INVALID_OPERATION); + return ieee754dp_nanxcpt(ieee754dp_indef(), "neg"); + } if (ieee754dp_isnan(x)) /* but not infinity */ return ieee754dp_nanxcpt(x, "neg", x); @@ -55,7 +64,16 @@ ieee754dp ieee754dp_neg(ieee754dp x) ieee754dp ieee754dp_abs(ieee754dp x) { + COMPXDP; + + EXPLODEXDP; CLEARCX; + FLUSHXDP; + + if (xc == IEEE754_CLASS_SNAN) { + SETCX(IEEE754_INVALID_OPERATION); + return ieee754dp_nanxcpt(ieee754dp_indef(), "neg"); + } if (ieee754dp_isnan(x)) /* but not infinity */ return ieee754dp_nanxcpt(x, "abs", x); diff --git a/arch/mips/math-emu/dp_sqrt.c b/arch/mips/math-emu/dp_sqrt.c index 1f9fa9cb8f82..d40cbd909d18 100644 --- a/arch/mips/math-emu/dp_sqrt.c +++ b/arch/mips/math-emu/dp_sqrt.c @@ -27,16 +27,6 @@ #include "ieee754dp.h" -static const struct ieee754dp_konst knan = { -#if (defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN) || defined(__MIPSEL__) - 0, 0, DP_EBIAS + DP_EMAX + 1, 0 -#else - 0, DP_EBIAS + DP_EMAX + 1, 0, 0 -#endif -}; - -#define nan ((ieee754dp)knan) - static const unsigned table[] = { 0, 1204, 3062, 5746, 9193, 13348, 18162, 23592, 29598, 36145, 43202, 50740, 58733, 67158, 75992, @@ -53,29 +43,37 @@ ieee754dp ieee754dp_sqrt(ieee754dp x) COMPXDP; EXPLODEXDP; + CLEARCX; + FLUSHXDP; /* x == INF or NAN? */ switch (xc) { case IEEE754_CLASS_QNAN: - case IEEE754_CLASS_SNAN: /* sqrt(Nan) = Nan */ return ieee754dp_nanxcpt(x, "sqrt"); + case IEEE754_CLASS_SNAN: + SETCX(IEEE754_INVALID_OPERATION); + return ieee754dp_nanxcpt(ieee754dp_indef(), "sqrt"); case IEEE754_CLASS_ZERO: /* sqrt(0) = 0 */ return x; case IEEE754_CLASS_INF: - if (xs) + if (xs) { /* sqrt(-Inf) = Nan */ - return ieee754dp_nanxcpt(nan, "sqrt"); + SETCX(IEEE754_INVALID_OPERATION); + return ieee754dp_nanxcpt(ieee754dp_indef(), "sqrt"); + } /* sqrt(+Inf) = Inf */ return x; case IEEE754_CLASS_DNORM: DPDNORMX; /* fall through */ case IEEE754_CLASS_NORM: - if (xs) + if (xs) { /* sqrt(-x) = Nan */ - return ieee754dp_nanxcpt(nan, "sqrt"); + SETCX(IEEE754_INVALID_OPERATION); + return ieee754dp_nanxcpt(ieee754dp_indef(), "sqrt"); + } break; } @@ -101,7 +99,7 @@ ieee754dp ieee754dp_sqrt(ieee754dp x) yh = y.bits >> 32; yh = (yh >> 1) + 0x1ff80000; yh = yh - table[(yh >> 15) & 31]; - y.bits = ((unsigned long long) yh << 32) | (y.bits & 0xffffffff); + y.bits = ((u64) yh << 32) | (y.bits & 0xffffffff); /* Heron's rule once with correction to improve to ~18 sig. bits */ /* t=x/y; y=y+t; py[n0]=py[n0]-0x00100006; py[n1]=0; */ diff --git a/arch/mips/math-emu/dp_sub.c b/arch/mips/math-emu/dp_sub.c index 6af7edaa18ed..b952f0dcc95e 100644 --- a/arch/mips/math-emu/dp_sub.c +++ b/arch/mips/math-emu/dp_sub.c @@ -32,32 +32,28 @@ ieee754dp ieee754dp_sub(ieee754dp x, ieee754dp y) COMPXDP; COMPYDP; - CLEARCX; - EXPLODEXDP; EXPLODEYDP; - switch (CLPAIR(xc, yc)) { - case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_SNAN): - return ieee754dp_nanxcpt(ieee754dp_bestnan(x, y), "sub", x, - y); + CLEARCX; + + FLUSHXDP; + FLUSHYDP; + switch (CLPAIR(xc, yc)) { + case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_QNAN): case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_SNAN): + case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_SNAN): case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_SNAN): case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_SNAN): case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_SNAN): case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_SNAN): - return ieee754dp_nanxcpt(y, "sub", x, y); - - case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_QNAN): case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_ZERO): case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_NORM): case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_DNORM): case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_INF): - return ieee754dp_nanxcpt(x, "sub", x, y); - - case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN): - return ieee754dp_bestnan(x, y); + SETCX(IEEE754_INVALID_OPERATION); + return ieee754dp_nanxcpt(ieee754dp_indef(), "sub", x, y); case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_QNAN): case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_QNAN): @@ -65,6 +61,7 @@ ieee754dp ieee754dp_sub(ieee754dp x, ieee754dp y) case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_QNAN): return y; + case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN): case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_ZERO): case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_NORM): case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_DNORM): @@ -72,7 +69,7 @@ ieee754dp ieee754dp_sub(ieee754dp x, ieee754dp y) return x; - /* Inifity handling + /* Infinity handling */ case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_INF): @@ -183,7 +180,7 @@ ieee754dp ieee754dp_sub(ieee754dp x, ieee754dp y) return ieee754dp_zero(0); /* other round modes => sign = 1 */ } - /* normalize to rounding precision + /* normalize to rounding precision */ while ((xm >> (DP_MBITS + 3)) == 0) { xm <<= 1; diff --git a/arch/mips/math-emu/dp_tint.c b/arch/mips/math-emu/dp_tint.c index c8a151b268d0..84fd730e4698 100644 --- a/arch/mips/math-emu/dp_tint.c +++ b/arch/mips/math-emu/dp_tint.c @@ -35,38 +35,74 @@ int ieee754dp_tint(ieee754dp x) CLEARCX; EXPLODEXDP; + FLUSHXDP; switch (xc) { case IEEE754_CLASS_SNAN: case IEEE754_CLASS_QNAN: - SETCX(IEEE754_INVALID_OPERATION); - return ieee754si_xcpt(ieee754si_indef(), "fixdp", x); case IEEE754_CLASS_INF: - SETCX(IEEE754_OVERFLOW); - return ieee754si_xcpt(ieee754si_indef(), "fixdp", x); + SETCX(IEEE754_INVALID_OPERATION); + return ieee754si_xcpt(ieee754si_indef(), "dp_tint", x); case IEEE754_CLASS_ZERO: return 0; - case IEEE754_CLASS_DNORM: /* much to small */ - SETCX(IEEE754_UNDERFLOW); - return ieee754si_xcpt(0, "fixdp", x); + case IEEE754_CLASS_DNORM: case IEEE754_CLASS_NORM: break; } - if (xe >= 31) { - SETCX(IEEE754_OVERFLOW); - return ieee754si_xcpt(ieee754si_indef(), "fix", x); - } - if (xe < 0) { - SETCX(IEEE754_UNDERFLOW); - return ieee754si_xcpt(0, "fix", x); + if (xe > 31) { + /* Set invalid. We will only use overflow for floating + point overflow */ + SETCX(IEEE754_INVALID_OPERATION); + return ieee754si_xcpt(ieee754si_indef(), "dp_tint", x); } /* oh gawd */ if (xe > DP_MBITS) { xm <<= xe - DP_MBITS; } else if (xe < DP_MBITS) { - /* XXX no rounding - */ - xm >>= DP_MBITS - xe; + u64 residue; + int round; + int sticky; + int odd; + + if (xe < -1) { + residue = xm; + round = 0; + sticky = residue != 0; + xm = 0; + } + else { + residue = xm << (64 - DP_MBITS + xe); + round = (residue >> 63) != 0; + sticky = (residue << 1) != 0; + xm >>= DP_MBITS - xe; + } + /* Note: At this point upper 32 bits of xm are guaranteed + to be zero */ + odd = (xm & 0x1) != 0x0; + switch (ieee754_csr.rm) { + case IEEE754_RN: + if (round && (sticky || odd)) + xm++; + break; + case IEEE754_RZ: + break; + case IEEE754_RU: /* toward +Infinity */ + if ((round || sticky) && !xs) + xm++; + break; + case IEEE754_RD: /* toward -Infinity */ + if ((round || sticky) && xs) + xm++; + break; + } + /* look for valid corner case 0x80000000 */ + if ((xm >> 31) != 0 && (xs == 0 || xm != 0x80000000)) { + /* This can happen after rounding */ + SETCX(IEEE754_INVALID_OPERATION); + return ieee754si_xcpt(ieee754si_indef(), "dp_tint", x); + } + if (round || sticky) + SETCX(IEEE754_INEXACT); } if (xs) return -xm; diff --git a/arch/mips/math-emu/dp_tlong.c b/arch/mips/math-emu/dp_tlong.c index cc8cf0ff9507..5af061f176d4 100644 --- a/arch/mips/math-emu/dp_tlong.c +++ b/arch/mips/math-emu/dp_tlong.c @@ -27,99 +27,85 @@ #include "ieee754dp.h" -long long ieee754dp_tlong(ieee754dp x) +s64 ieee754dp_tlong(ieee754dp x) { COMPXDP; CLEARCX; EXPLODEXDP; + FLUSHXDP; switch (xc) { case IEEE754_CLASS_SNAN: case IEEE754_CLASS_QNAN: - SETCX(IEEE754_INVALID_OPERATION); - return ieee754di_xcpt(ieee754di_indef(), "dp_tlong", x); case IEEE754_CLASS_INF: - SETCX(IEEE754_OVERFLOW); + SETCX(IEEE754_INVALID_OPERATION); return ieee754di_xcpt(ieee754di_indef(), "dp_tlong", x); case IEEE754_CLASS_ZERO: return 0; - case IEEE754_CLASS_DNORM: /* much too small */ - SETCX(IEEE754_UNDERFLOW); - return ieee754di_xcpt(0, "dp_tlong", x); + case IEEE754_CLASS_DNORM: case IEEE754_CLASS_NORM: break; } if (xe >= 63) { - SETCX(IEEE754_OVERFLOW); + /* look for valid corner case */ + if (xe == 63 && xs && xm == DP_HIDDEN_BIT) + return -0x8000000000000000LL; + /* Set invalid. We will only use overflow for floating + point overflow */ + SETCX(IEEE754_INVALID_OPERATION); return ieee754di_xcpt(ieee754di_indef(), "dp_tlong", x); } - if (xe < 0) { - if (ieee754_csr.rm == IEEE754_RU) { - if (xs) { /* Negative */ - return 0x0000000000000000LL; - } else { /* Positive */ - return 0x0000000000000001LL; - } - } else if (ieee754_csr.rm == IEEE754_RD) { - if (xs) { /* Negative , return -1 */ - return 0xffffffffffffffffLL; - } else { /* Positive */ - return 0x0000000000000000LL; - } - } else { - SETCX(IEEE754_UNDERFLOW); - return ieee754di_xcpt(0, "dp_tlong", x); - } - } /* oh gawd */ if (xe > DP_MBITS) { xm <<= xe - DP_MBITS; } else if (xe < DP_MBITS) { - unsigned long long residue; - unsigned long long mask = 0; - int i; + u64 residue; int round; int sticky; int odd; - /* compute mask */ - for (i = 0; i < DP_MBITS - xe; i++) { - mask = mask << 1; - mask = mask | 0x1; + if (xe < -1) { + residue = xm; + round = 0; + sticky = residue != 0; + xm = 0; } - residue = (xm & mask) << (64 - (DP_MBITS - xe)); - round = - ((0x8000000000000000LL & residue) != - 0x0000000000000000LL); - sticky = - ((0x7fffffffffffffffLL & residue) != - 0x0000000000000000LL); - - xm >>= DP_MBITS - xe; - - odd = ((xm & 0x1) != 0x0000000000000000LL); - - /* Do the rounding */ - if (!round && sticky) { - if ((ieee754_csr.rm == IEEE754_RU && !xs) - || (ieee754_csr.rm == IEEE754_RD && xs)) { + else { + /* Shifting a u64 64 times does not work, + * so we do it in two steps. Be aware that xe + * may be -1 */ + residue = xm << (xe + 1); + residue <<= 63 - DP_MBITS; + round = (residue >> 63) != 0; + sticky = (residue << 1) != 0; + xm >>= DP_MBITS - xe; + } + odd = (xm & 0x1) != 0x0; + switch (ieee754_csr.rm) { + case IEEE754_RN: + if (round && (sticky || odd)) xm++; - } - } else if (round && !sticky) { - if ((ieee754_csr.rm == IEEE754_RU && !xs) - || (ieee754_csr.rm == IEEE754_RD && xs) - || (ieee754_csr.rm == IEEE754_RN && odd)) { + break; + case IEEE754_RZ: + break; + case IEEE754_RU: /* toward +Infinity */ + if ((round || sticky) && !xs) xm++; - } - } else if (round && sticky) { - if ((ieee754_csr.rm == IEEE754_RU && !xs) - || (ieee754_csr.rm == IEEE754_RD && xs) - || (ieee754_csr.rm == IEEE754_RN)) { + break; + case IEEE754_RD: /* toward -Infinity */ + if ((round || sticky) && xs) xm++; - } + break; + } + if ((xm >> 63) != 0) { + /* This can happen after rounding */ + SETCX(IEEE754_INVALID_OPERATION); + return ieee754di_xcpt(ieee754di_indef(), "dp_tlong", x); } + if (round || sticky) + SETCX(IEEE754_INEXACT); } if (xs) return -xm; @@ -128,14 +114,14 @@ long long ieee754dp_tlong(ieee754dp x) } -unsigned long long ieee754dp_tulong(ieee754dp x) +u64 ieee754dp_tulong(ieee754dp x) { ieee754dp hb = ieee754dp_1e63(); /* what if x < 0 ?? */ if (ieee754dp_lt(x, hb)) - return (unsigned long long) ieee754dp_tlong(x); + return (u64) ieee754dp_tlong(x); - return (unsigned long long) ieee754dp_tlong(ieee754dp_sub(x, hb)) | + return (u64) ieee754dp_tlong(ieee754dp_sub(x, hb)) | (1ULL << 63); } diff --git a/arch/mips/math-emu/dsemul.c b/arch/mips/math-emu/dsemul.c new file mode 100644 index 000000000000..6a767feaf0d5 --- /dev/null +++ b/arch/mips/math-emu/dsemul.c @@ -0,0 +1,173 @@ +#include <linux/compiler.h> +#include <linux/mm.h> +#include <linux/signal.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> + +#include <asm/asm.h> +#include <asm/bootinfo.h> +#include <asm/byteorder.h> +#include <asm/cpu.h> +#include <asm/inst.h> +#include <asm/processor.h> +#include <asm/uaccess.h> +#include <asm/branch.h> +#include <asm/mipsregs.h> +#include <asm/system.h> +#include <asm/cacheflush.h> + +#include <asm/fpu_emulator.h> + +#include "ieee754.h" +#include "dsemul.h" + +/* Strap kernel emulator for full MIPS IV emulation */ + +#ifdef __mips +#undef __mips +#endif +#define __mips 4 + +extern struct mips_fpu_emulator_private fpuemuprivate; + + +/* + * Emulate the arbritrary instruction ir at xcp->cp0_epc. Required when + * we have to emulate the instruction in a COP1 branch delay slot. Do + * not change cp0_epc due to the instruction + * + * According to the spec: + * 1) it shouldnt be a branch :-) + * 2) it can be a COP instruction :-( + * 3) if we are tring to run a protected memory space we must take + * special care on memory access instructions :-( + */ + +/* + * "Trampoline" return routine to catch exception following + * execution of delay-slot instruction execution. + */ + +struct emuframe { + mips_instruction emul; + mips_instruction badinst; + mips_instruction cookie; + gpreg_t epc; +}; + +int mips_dsemul(struct pt_regs *regs, mips_instruction ir, gpreg_t cpc) +{ + extern asmlinkage void handle_dsemulret(void); + mips_instruction *dsemul_insns; + struct emuframe *fr; + int err; + + if (ir == 0) { /* a nop is easy */ + regs->cp0_epc = cpc; + regs->cp0_cause &= ~CAUSEF_BD; + return 0; + } +#ifdef DSEMUL_TRACE + printk("dsemul %lx %lx\n", regs->cp0_epc, cpc); + +#endif + + /* + * The strategy is to push the instruction onto the user stack + * and put a trap after it which we can catch and jump to + * the required address any alternative apart from full + * instruction emulation!!. + * + * Algorithmics used a system call instruction, and + * borrowed that vector. MIPS/Linux version is a bit + * more heavyweight in the interests of portability and + * multiprocessor support. For Linux we generate a + * an unaligned access and force an address error exception. + * + * For embedded systems (stand-alone) we prefer to use a + * non-existing CP1 instruction. This prevents us from emulating + * branches, but gives us a cleaner interface to the exception + * handler (single entry point). + */ + + /* Ensure that the two instructions are in the same cache line */ + dsemul_insns = (mips_instruction *) REG_TO_VA ((regs->regs[29] - sizeof(struct emuframe)) & ~0x7); + fr = (struct emuframe *) dsemul_insns; + + /* Verify that the stack pointer is not competely insane */ + if (unlikely(verify_area(VERIFY_WRITE, fr, sizeof(struct emuframe)))) + return SIGBUS; + + err = __put_user(ir, &fr->emul); + err |= __put_user((mips_instruction)BADINST, &fr->badinst); + err |= __put_user((mips_instruction)BD_COOKIE, &fr->cookie); + err |= __put_user(cpc, &fr->epc); + + if (unlikely(err)) { + fpuemuprivate.stats.errors++; + return SIGBUS; + } + + regs->cp0_epc = VA_TO_REG & fr->emul; + + flush_cache_sigtramp((unsigned long)&fr->badinst); + + return SIGILL; /* force out of emulation loop */ +} + +int do_dsemulret(struct pt_regs *xcp) +{ + struct emuframe *fr; + gpreg_t epc; + u32 insn, cookie; + int err = 0; + + fr = (struct emuframe *) (xcp->cp0_epc - sizeof(mips_instruction)); + + /* + * If we can't even access the area, something is very wrong, but we'll + * leave that to the default handling + */ + if (verify_area(VERIFY_READ, fr, sizeof(struct emuframe))) + return 0; + + /* + * Do some sanity checking on the stackframe: + * + * - Is the instruction pointed to by the EPC an BADINST? + * - Is the following memory word the BD_COOKIE? + */ + err = __get_user(insn, &fr->badinst); + err |= __get_user(cookie, &fr->cookie); + + if (unlikely(err || (insn != BADINST) || (cookie != BD_COOKIE))) { + fpuemuprivate.stats.errors++; + + return 0; + } + + /* + * At this point, we are satisfied that it's a BD emulation trap. Yes, + * a user might have deliberately put two malformed and useless + * instructions in a row in his program, in which case he's in for a + * nasty surprise - the next instruction will be treated as a + * continuation address! Alas, this seems to be the only way that we + * can handle signals, recursion, and longjmps() in the context of + * emulating the branch delay instruction. + */ + +#ifdef DSEMUL_TRACE + printk("dsemulret\n"); +#endif + if (__get_user(epc, &fr->epc)) { /* Saved EPC */ + /* This is not a good situation to be in */ + force_sig(SIGBUS, current); + + return 0; + } + + /* Set EPC to return to post-branch instruction */ + xcp->cp0_epc = epc; + + return 1; +} diff --git a/arch/mips/math-emu/dsemul.h b/arch/mips/math-emu/dsemul.h new file mode 100644 index 000000000000..dbd85f95268d --- /dev/null +++ b/arch/mips/math-emu/dsemul.h @@ -0,0 +1,23 @@ +typedef long gpreg_t; +typedef void *vaddr_t; + +#define REG_TO_VA (vaddr_t) +#define VA_TO_REG (gpreg_t) + +int mips_dsemul(struct pt_regs *regs, mips_instruction ir, gpreg_t cpc); +int do_dsemulret(struct pt_regs *xcp); + +/* Instruction which will always cause an address error */ +#define AdELOAD 0x8c000001 /* lw $0,1($0) */ +/* Instruction which will plainly cause a CP1 exception when FPU is disabled */ +#define CP1UNDEF 0x44400001 /* cfc1 $0,$0 undef */ + +/* Instruction inserted following the badinst to further tag the sequence */ +#define BD_COOKIE 0x0000bd36 /* tne $0,$0 with baggage */ + +/* Setup which instruction to use for trampoline */ +#ifdef STANDALONE_EMULATOR +#define BADINST CP1UNDEF +#else +#define BADINST AdELOAD +#endif diff --git a/arch/mips/math-emu/ieee754.c b/arch/mips/math-emu/ieee754.c index 3571107088b1..d028a19f41a6 100644 --- a/arch/mips/math-emu/ieee754.c +++ b/arch/mips/math-emu/ieee754.c @@ -50,7 +50,7 @@ const char *const ieee754_cname[] = { "SNaN", }; -/* the control status register +/* the control status register */ struct ieee754_csr ieee754_csr; @@ -77,7 +77,7 @@ const struct ieee754dp_konst __ieee754dp_spcvals[] = { DPSTR(1, 3 + DP_EBIAS, 0x40000, 0), /* - 10.0 */ DPSTR(0, DP_EMAX + 1 + DP_EBIAS, 0, 0), /* + infinity */ DPSTR(1, DP_EMAX + 1 + DP_EBIAS, 0, 0), /* - infinity */ - DPSTR(0, DP_EMAX + 1 + DP_EBIAS, 0x40000, 0), /* + indef quiet Nan */ + DPSTR(0,DP_EMAX+1+DP_EBIAS,0x7FFFF,0xFFFFFFFF), /* + indef quiet Nan */ DPSTR(0, DP_EMAX + DP_EBIAS, 0xFFFFF, 0xFFFFFFFF), /* + max */ DPSTR(1, DP_EMAX + DP_EBIAS, 0xFFFFF, 0xFFFFFFFF), /* - max */ DPSTR(0, DP_EMIN + DP_EBIAS, 0, 0), /* + min normal */ @@ -97,7 +97,7 @@ const struct ieee754sp_konst __ieee754sp_spcvals[] = { SPSTR(1, 3 + SP_EBIAS, 0x200000), /* - 10.0 */ SPSTR(0, SP_EMAX + 1 + SP_EBIAS, 0), /* + infinity */ SPSTR(1, SP_EMAX + 1 + SP_EBIAS, 0), /* - infinity */ - SPSTR(0, SP_EMAX + 1 + SP_EBIAS, 0x200000), /* + indef quiet Nan */ + SPSTR(0,SP_EMAX+1+SP_EBIAS,0x3FFFFF), /* + indef quiet Nan */ SPSTR(0, SP_EMAX + SP_EBIAS, 0x7FFFFF), /* + max normal */ SPSTR(1, SP_EMAX + SP_EBIAS, 0x7FFFFF), /* - max normal */ SPSTR(0, SP_EMIN + SP_EBIAS, 0), /* + min normal */ @@ -123,7 +123,7 @@ int ieee754si_xcpt(int r, const char *op, ...) return ax.rv.si; } -long long ieee754di_xcpt(long long r, const char *op, ...) +s64 ieee754di_xcpt(s64 r, const char *op, ...) { struct ieee754xctx ax; diff --git a/arch/mips/math-emu/ieee754.h b/arch/mips/math-emu/ieee754.h index ad2c91aed0b1..8777a0d161c8 100644 --- a/arch/mips/math-emu/ieee754.h +++ b/arch/mips/math-emu/ieee754.h @@ -26,7 +26,7 @@ /************************************************************************** * Nov 7, 2000 - * Modification to allow integration with Linux kernel + * Modification to allow integration with Linux kernel * * Kevin D. Kissell, kevink@mips.com and Carsten Langgard, carstenl@mips.com * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved. @@ -36,8 +36,8 @@ /* Going from Algorithmics to Linux native environment, add this */ #include <linux/types.h> -/* - * Not very pretty, but the Linux kernel's normal va_list definition +/* + * Not very pretty, but the Linux kernel's normal va_list definition * does not allow it to be used as a structure element, as it is here. */ #ifndef _STDARG_H @@ -71,18 +71,18 @@ struct ieee754sp_konst { typedef union _ieee754dp { struct ieee754dp_konst oparts; struct { - unsigned long long mant:52; + u64 mant:52; unsigned int bexp:11; unsigned int sign:1; } parts; - unsigned long long bits; + u64 bits; double d; } ieee754dp; typedef union _ieee754sp { struct ieee754sp_konst parts; float f; - unsigned long bits; + u32 bits; } ieee754sp; #endif @@ -98,10 +98,10 @@ typedef union _ieee754dp { struct { unsigned int sign:1; unsigned int bexp:11; - unsigned long long mant:52; + u64 mant:52; } parts; double d; - unsigned long long bits; + u64 bits; } ieee754dp; struct ieee754sp_konst { @@ -113,7 +113,7 @@ struct ieee754sp_konst { typedef union _ieee754sp { struct ieee754sp_konst parts; float f; - unsigned long bits; + u32 bits; } ieee754sp; #endif @@ -138,16 +138,16 @@ ieee754sp ieee754sp_div(ieee754sp x, ieee754sp y); ieee754sp ieee754sp_fint(int x); ieee754sp ieee754sp_funs(unsigned x); -ieee754sp ieee754sp_flong(long long x); -ieee754sp ieee754sp_fulong(unsigned long long x); +ieee754sp ieee754sp_flong(s64 x); +ieee754sp ieee754sp_fulong(u64 x); ieee754sp ieee754sp_fdp(ieee754dp x); int ieee754sp_tint(ieee754sp x); unsigned int ieee754sp_tuns(ieee754sp x); -long long ieee754sp_tlong(ieee754sp x); -unsigned long long ieee754sp_tulong(ieee754sp x); +s64 ieee754sp_tlong(ieee754sp x); +u64 ieee754sp_tulong(ieee754sp x); -int ieee754sp_cmp(ieee754sp x, ieee754sp y, int cop); +int ieee754sp_cmp(ieee754sp x, ieee754sp y, int cop, int sig); /* * basic sp math */ @@ -179,14 +179,14 @@ ieee754dp ieee754dp_abs(ieee754dp x); ieee754dp ieee754dp_neg(ieee754dp x); ieee754dp ieee754dp_scalb(ieee754dp x, int); -/* return exponent as integer in floating point format +/* return exponent as integer in floating point format */ ieee754dp ieee754dp_logb(ieee754dp x); ieee754dp ieee754dp_fint(int x); ieee754dp ieee754dp_funs(unsigned x); -ieee754dp ieee754dp_flong(long long x); -ieee754dp ieee754dp_fulong(unsigned long long x); +ieee754dp ieee754dp_flong(s64 x); +ieee754dp ieee754dp_fulong(u64 x); ieee754dp ieee754dp_fsp(ieee754sp x); ieee754dp ieee754dp_ceil(ieee754dp x); @@ -195,10 +195,10 @@ ieee754dp ieee754dp_trunc(ieee754dp x); int ieee754dp_tint(ieee754dp x); unsigned int ieee754dp_tuns(ieee754dp x); -long long ieee754dp_tlong(ieee754dp x); -unsigned long long ieee754dp_tulong(ieee754dp x); +s64 ieee754dp_tlong(ieee754dp x); +u64 ieee754dp_tulong(ieee754dp x); -int ieee754dp_cmp(ieee754dp x, ieee754dp y, int cop); +int ieee754dp_cmp(ieee754dp x, ieee754dp y, int cop, int sig); /* * basic sp math */ @@ -214,7 +214,7 @@ ieee754dp ieee754dp_sqrt(ieee754dp x); -/* 5 types of floating point number +/* 5 types of floating point number */ #define IEEE754_CLASS_NORM 0x00 #define IEEE754_CLASS_ZERO 0x01 @@ -238,7 +238,7 @@ extern const char *const ieee754_cname[]; #define IEEE754_CGT 0x04 #define IEEE754_CUN 0x08 -/* rounding mode +/* rounding mode */ #define IEEE754_RN 0 /* round to nearest */ #define IEEE754_RZ 1 /* round toward zero */ @@ -253,65 +253,65 @@ extern const char *const ieee754_cname[]; */ static __inline int ieee754sp_eq(ieee754sp x, ieee754sp y) { - return ieee754sp_cmp(x, y, IEEE754_CEQ); + return ieee754sp_cmp(x, y, IEEE754_CEQ, 0); } static __inline int ieee754sp_ne(ieee754sp x, ieee754sp y) { return ieee754sp_cmp(x, y, - IEEE754_CLT | IEEE754_CGT | IEEE754_CUN); + IEEE754_CLT | IEEE754_CGT | IEEE754_CUN, 0); } static __inline int ieee754sp_lt(ieee754sp x, ieee754sp y) { - return ieee754sp_cmp(x, y, IEEE754_CLT); + return ieee754sp_cmp(x, y, IEEE754_CLT, 0); } static __inline int ieee754sp_le(ieee754sp x, ieee754sp y) { - return ieee754sp_cmp(x, y, IEEE754_CLT | IEEE754_CEQ); + return ieee754sp_cmp(x, y, IEEE754_CLT | IEEE754_CEQ, 0); } static __inline int ieee754sp_gt(ieee754sp x, ieee754sp y) { - return ieee754sp_cmp(x, y, IEEE754_CGT); + return ieee754sp_cmp(x, y, IEEE754_CGT, 0); } static __inline int ieee754sp_ge(ieee754sp x, ieee754sp y) { - return ieee754sp_cmp(x, y, IEEE754_CGT | IEEE754_CEQ); + return ieee754sp_cmp(x, y, IEEE754_CGT | IEEE754_CEQ, 0); } static __inline int ieee754dp_eq(ieee754dp x, ieee754dp y) { - return ieee754dp_cmp(x, y, IEEE754_CEQ); + return ieee754dp_cmp(x, y, IEEE754_CEQ, 0); } static __inline int ieee754dp_ne(ieee754dp x, ieee754dp y) { return ieee754dp_cmp(x, y, - IEEE754_CLT | IEEE754_CGT | IEEE754_CUN); + IEEE754_CLT | IEEE754_CGT | IEEE754_CUN, 0); } static __inline int ieee754dp_lt(ieee754dp x, ieee754dp y) { - return ieee754dp_cmp(x, y, IEEE754_CLT); + return ieee754dp_cmp(x, y, IEEE754_CLT, 0); } static __inline int ieee754dp_le(ieee754dp x, ieee754dp y) { - return ieee754dp_cmp(x, y, IEEE754_CLT | IEEE754_CEQ); + return ieee754dp_cmp(x, y, IEEE754_CLT | IEEE754_CEQ, 0); } static __inline int ieee754dp_gt(ieee754dp x, ieee754dp y) { - return ieee754dp_cmp(x, y, IEEE754_CGT); + return ieee754dp_cmp(x, y, IEEE754_CGT, 0); } static __inline int ieee754dp_ge(ieee754dp x, ieee754dp y) { - return ieee754dp_cmp(x, y, IEEE754_CGT | IEEE754_CEQ); + return ieee754dp_cmp(x, y, IEEE754_CGT | IEEE754_CEQ, 0); } @@ -321,11 +321,10 @@ ieee754dp ieee754dp_fstr(const char *s, char **endp); char *ieee754dp_tstr(ieee754dp x, int prec, int fmt, int af); -/* the control status register +/* the control status register */ struct ieee754_csr { unsigned pad:13; - unsigned noq:1; /* set 1 for no quiet NaN's */ unsigned nod:1; /* set 1 for no denormalised numbers */ unsigned cx:5; /* exceptions this operation */ unsigned mx:5; /* exception enable mask */ @@ -351,7 +350,7 @@ static __inline unsigned ieee754_getcx(void) return (ieee754_csr.cx); } -/* test for current exception condition +/* test for current exception condition */ static __inline int ieee754_cxtest(unsigned n) { @@ -373,7 +372,7 @@ static __inline unsigned ieee754_clrsx(void) return (ieee754_csr.sx = 0); } -/* test for sticky exception condition +/* test for sticky exception condition */ static __inline int ieee754_sxtest(unsigned n) { @@ -451,13 +450,13 @@ extern const struct ieee754sp_konst __ieee754sp_spcvals[]; #define ieee754sp_1e63() \ (ieee754sp_spcvals[IEEE754_SPCVAL_P1E63]) -/* indefinite integer value +/* indefinite integer value */ -#define ieee754si_indef() INT_MIN -#ifdef LONG_LONG_MIN -#define ieee754di_indef() LONG_LONG_MIN +#define ieee754si_indef() INT_MAX +#ifdef LONG_LONG_MAX +#define ieee754di_indef() LONG_LONG_MAX #else -#define ieee754di_indef() (-9223372036854775807LL-1) +#define ieee754di_indef() ((s64)(~0ULL>>1)) #endif /* IEEE exception context, passed to handler */ @@ -471,7 +470,7 @@ struct ieee754xctx { ieee754xp xp; /* extended precision */ #endif int si; /* standard signed integer (32bits) */ - long long di; /* extended signed integer (64bits) */ + s64 di; /* extended signed integer (64bits) */ } rv; /* default result format implied by op */ va_list ap; }; diff --git a/arch/mips/math-emu/ieee754d.c b/arch/mips/math-emu/ieee754d.c index b25c8c03d003..87b287189b4f 100644 --- a/arch/mips/math-emu/ieee754d.c +++ b/arch/mips/math-emu/ieee754d.c @@ -1,12 +1,11 @@ -/* some debug functions -*/ /* + * Some debug functions + * * MIPS floating point support + * * Copyright (C) 1994-2000 Algorithmics Ltd. All rights reserved. * http://www.algor.co.uk * - * ######################################################################## - * * This program is free software; you can distribute it and/or modify it * under the terms of the GNU General Public License (Version 2) as * published by the Free Software Foundation. @@ -20,17 +19,14 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. * - * ######################################################################## - */ - -/************************************************************************** * Nov 7, 2000 - * Modified to build and operate in Linux kernel environment. + * Modified to build and operate in Linux kernel environment. * * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved. - *************************************************************************/ + */ +#include <linux/kernel.h> #include "ieee754.h" #define DP_EBIAS 1023 @@ -43,12 +39,12 @@ #define SP_EMAX 127 #define SP_FBITS 23 -#define DP_MBIT(x) ((unsigned long long)1 << (x)) +#define DP_MBIT(x) ((u64)1 << (x)) #define DP_HIDDEN_BIT DP_MBIT(DP_FBITS) #define DP_SIGN_BIT DP_MBIT(63) -#define SP_MBIT(x) ((unsigned long)1 << (x)) +#define SP_MBIT(x) ((u32)1 << (x)) #define SP_HIDDEN_BIT SP_MBIT(SP_FBITS) #define SP_SIGN_BIT SP_MBIT(31) diff --git a/arch/mips/math-emu/ieee754dp.c b/arch/mips/math-emu/ieee754dp.c index f1647fb0318c..104fb44d994f 100644 --- a/arch/mips/math-emu/ieee754dp.c +++ b/arch/mips/math-emu/ieee754dp.c @@ -42,9 +42,7 @@ int ieee754dp_isnan(ieee754dp x) int ieee754dp_issnan(ieee754dp x) { assert(ieee754dp_isnan(x)); - if (ieee754_csr.noq) - return 1; - return !(DPMANT(x) & DP_MBIT(DP_MBITS - 1)); + return ((DPMANT(x) & DP_MBIT(DP_MBITS-1)) == DP_MBIT(DP_MBITS-1)); } @@ -71,12 +69,13 @@ ieee754dp ieee754dp_nanxcpt(ieee754dp r, const char *op, ...) if (!ieee754dp_issnan(r)) /* QNAN does not cause invalid op !! */ return r; - if (!SETCX(IEEE754_INVALID_OPERATION)) { + if (!SETANDTESTCX(IEEE754_INVALID_OPERATION)) { /* not enabled convert to a quiet NaN */ - if (ieee754_csr.noq) + DPMANT(r) &= (~DP_MBIT(DP_MBITS-1)); + if (ieee754dp_isnan(r)) return r; - DPMANT(r) |= DP_MBIT(DP_MBITS - 1); - return r; + else + return ieee754dp_indef(); } ax.op = op; @@ -99,12 +98,38 @@ ieee754dp ieee754dp_bestnan(ieee754dp x, ieee754dp y) } +static u64 get_rounding(int sn, u64 xm) +{ + /* inexact must round of 3 bits + */ + if (xm & (DP_MBIT(3) - 1)) { + switch (ieee754_csr.rm) { + case IEEE754_RZ: + break; + case IEEE754_RN: + xm += 0x3 + ((xm >> 3) & 1); + /* xm += (xm&0x8)?0x4:0x3 */ + break; + case IEEE754_RU: /* toward +Infinity */ + if (!sn) /* ?? */ + xm += 0x8; + break; + case IEEE754_RD: /* toward -Infinity */ + if (sn) /* ?? */ + xm += 0x8; + break; + } + } + return xm; +} + + /* generate a normal/denormal number with over,under handling * sn is sign * xe is an unbiased exponent * xm is 3bit extended precision value. */ -ieee754dp ieee754dp_format(int sn, int xe, unsigned long long xm) +ieee754dp ieee754dp_format(int sn, int xe, u64 xm) { assert(xm); /* we don't gen exact zeros (probably should) */ @@ -117,40 +142,59 @@ ieee754dp ieee754dp_format(int sn, int xe, unsigned long long xm) if (ieee754_csr.nod) { SETCX(IEEE754_UNDERFLOW); - return ieee754dp_zero(sn); + SETCX(IEEE754_INEXACT); + + switch(ieee754_csr.rm) { + case IEEE754_RN: + return ieee754dp_zero(sn); + case IEEE754_RZ: + return ieee754dp_zero(sn); + case IEEE754_RU: /* toward +Infinity */ + if(sn == 0) + return ieee754dp_min(0); + else + return ieee754dp_zero(1); + case IEEE754_RD: /* toward -Infinity */ + if(sn == 0) + return ieee754dp_zero(0); + else + return ieee754dp_min(1); + } } - /* sticky right shift es bits - */ - xm = XDPSRS(xm, es); - xe += es; - - assert((xm & (DP_HIDDEN_BIT << 3)) == 0); - assert(xe == DP_EMIN); + if (xe == DP_EMIN - 1 + && get_rounding(sn, xm) >> (DP_MBITS + 1 + 3)) + { + /* Not tiny after rounding */ + SETCX(IEEE754_INEXACT); + xm = get_rounding(sn, xm); + xm >>= 1; + /* Clear grs bits */ + xm &= ~(DP_MBIT(3) - 1); + xe++; + } + else { + /* sticky right shift es bits + */ + xm = XDPSRS(xm, es); + xe += es; + assert((xm & (DP_HIDDEN_BIT << 3)) == 0); + assert(xe == DP_EMIN); + } } if (xm & (DP_MBIT(3) - 1)) { SETCX(IEEE754_INEXACT); - /* inexact must round of 3 bits - */ - switch (ieee754_csr.rm) { - case IEEE754_RZ: - break; - case IEEE754_RN: - xm += 0x3 + ((xm >> 3) & 1); - /* xm += (xm&0x8)?0x4:0x3 */ - break; - case IEEE754_RU: /* toward +Infinity */ - if (!sn) /* ?? */ - xm += 0x8; - break; - case IEEE754_RD: /* toward -Infinity */ - if (sn) /* ?? */ - xm += 0x8; - break; + if ((xm & (DP_HIDDEN_BIT << 3)) == 0) { + SETCX(IEEE754_UNDERFLOW); } - /* adjust exponent for rounding add overflowing + + /* inexact must round of 3 bits */ - if (xm >> (DP_MBITS + 3 + 1)) { /* add causes mantissa overflow */ + xm = get_rounding(sn, xm); + /* adjust exponent for rounding add overflowing + */ + if (xm >> (DP_MBITS + 3 + 1)) { + /* add causes mantissa overflow */ xm >>= 1; xe++; } @@ -163,6 +207,7 @@ ieee754dp ieee754dp_format(int sn, int xe, unsigned long long xm) if (xe > DP_EMAX) { SETCX(IEEE754_OVERFLOW); + SETCX(IEEE754_INEXACT); /* -O can be table indexed by (rm,sn) */ switch (ieee754_csr.rm) { case IEEE754_RN: @@ -186,7 +231,8 @@ ieee754dp ieee754dp_format(int sn, int xe, unsigned long long xm) if ((xm & DP_HIDDEN_BIT) == 0) { /* we underflow (tiny/zero) */ assert(xe == DP_EMIN); - SETCX(IEEE754_UNDERFLOW); + if (ieee754_csr.mx & IEEE754_UNDERFLOW) + SETCX(IEEE754_UNDERFLOW); return builddp(sn, DP_EMIN - 1 + DP_EBIAS, xm); } else { assert((xm >> (DP_MBITS + 1)) == 0); /* no execess */ diff --git a/arch/mips/math-emu/ieee754dp.h b/arch/mips/math-emu/ieee754dp.h index 8ab3e0148ab9..7d5d256827a3 100644 --- a/arch/mips/math-emu/ieee754dp.h +++ b/arch/mips/math-emu/ieee754dp.h @@ -1,4 +1,4 @@ -/* +/* * IEEE754 floating point * double precision internal header file */ @@ -46,7 +46,7 @@ #define DPDNORMX DPDNORMx(xm,xe) #define DPDNORMY DPDNORMx(ym,ye) -static __inline ieee754dp builddp(int s, int bx, unsigned long long m) +static __inline ieee754dp builddp(int s, int bx, u64 m) { ieee754dp r; @@ -64,11 +64,11 @@ static __inline ieee754dp builddp(int s, int bx, unsigned long long m) extern int ieee754dp_isnan(ieee754dp); extern int ieee754dp_issnan(ieee754dp); extern int ieee754si_xcpt(int, const char *, ...); -extern long long ieee754di_xcpt(long long, const char *, ...); +extern s64 ieee754di_xcpt(s64, const char *, ...); extern ieee754dp ieee754dp_xcpt(ieee754dp, const char *, ...); extern ieee754dp ieee754dp_nanxcpt(ieee754dp, const char *, ...); extern ieee754dp ieee754dp_bestnan(ieee754dp, ieee754dp); -extern ieee754dp ieee754dp_format(int, int, unsigned long long); +extern ieee754dp ieee754dp_format(int, int, u64); #define DPNORMRET2(s,e,m,name,a0,a1) \ diff --git a/arch/mips/math-emu/ieee754int.h b/arch/mips/math-emu/ieee754int.h index 6de10131fd32..fa4fd04a38d2 100644 --- a/arch/mips/math-emu/ieee754int.h +++ b/arch/mips/math-emu/ieee754int.h @@ -1,4 +1,4 @@ -/* +/* * IEEE754 floating point * common internal header file */ @@ -38,11 +38,11 @@ #define SP_EMAX 127 #define SP_MBITS 23 -#define DP_MBIT(x) ((unsigned long long)1 << (x)) +#define DP_MBIT(x) ((u64)1 << (x)) #define DP_HIDDEN_BIT DP_MBIT(DP_MBITS) #define DP_SIGN_BIT DP_MBIT(63) -#define SP_MBIT(x) ((unsigned long)1 << (x)) +#define SP_MBIT(x) ((u32)1 << (x)) #define SP_HIDDEN_BIT SP_MBIT(SP_MBITS) #define SP_SIGN_BIT SP_MBIT(31) @@ -61,7 +61,10 @@ (ieee754_csr.cx = 0) #define SETCX(x) \ - (ieee754_csr.cx |= (x),ieee754_csr.sx |= (x),ieee754_csr.mx & (x)) + (ieee754_csr.cx |= (x),ieee754_csr.sx |= (x)) + +#define SETANDTESTCX(x) \ + (SETCX(x),ieee754_csr.mx & (x)) #define TSTX() \ (ieee754_csr.cx & ieee754_csr.mx) @@ -82,9 +85,9 @@ if(vm == 0)\ vc = IEEE754_CLASS_INF;\ else if(vm & SP_MBIT(SP_MBITS-1)) \ - vc = IEEE754_CLASS_QNAN;\ - else \ vc = IEEE754_CLASS_SNAN;\ + else \ + vc = IEEE754_CLASS_QNAN;\ } else if(ve == SP_EMIN-1+SP_EBIAS) {\ if(vm) {\ ve = SP_EMIN;\ @@ -102,10 +105,10 @@ #define COMPXDP \ -unsigned long long xm; int xe; int xs; int xc +u64 xm; int xe; int xs; int xc #define COMPYDP \ -unsigned long long ym; int ye; int ys; int yc +u64 ym; int ye; int ys; int yc #define EXPLODEDP(v,vc,vs,ve,vm) \ {\ @@ -116,9 +119,9 @@ unsigned long long ym; int ye; int ys; int yc if(vm == 0)\ vc = IEEE754_CLASS_INF;\ else if(vm & DP_MBIT(DP_MBITS-1)) \ - vc = IEEE754_CLASS_QNAN;\ - else \ vc = IEEE754_CLASS_SNAN;\ + else \ + vc = IEEE754_CLASS_QNAN;\ } else if(ve == DP_EMIN-1+DP_EBIAS) {\ if(vm) {\ ve = DP_EMIN;\ @@ -133,3 +136,30 @@ unsigned long long ym; int ye; int ys; int yc } #define EXPLODEXDP EXPLODEDP(x,xc,xs,xe,xm) #define EXPLODEYDP EXPLODEDP(y,yc,ys,ye,ym) + +#define FLUSHDP(v,vc,vs,ve,vm) \ + if(vc==IEEE754_CLASS_DNORM) {\ + if(ieee754_csr.nod) {\ + SETCX(IEEE754_INEXACT);\ + vc = IEEE754_CLASS_ZERO;\ + ve = DP_EMIN-1+DP_EBIAS;\ + vm = 0;\ + v = ieee754dp_zero(vs);\ + }\ + } + +#define FLUSHSP(v,vc,vs,ve,vm) \ + if(vc==IEEE754_CLASS_DNORM) {\ + if(ieee754_csr.nod) {\ + SETCX(IEEE754_INEXACT);\ + vc = IEEE754_CLASS_ZERO;\ + ve = SP_EMIN-1+SP_EBIAS;\ + vm = 0;\ + v = ieee754sp_zero(vs);\ + }\ + } + +#define FLUSHXDP FLUSHDP(x,xc,xs,xe,xm) +#define FLUSHYDP FLUSHDP(y,yc,ys,ye,ym) +#define FLUSHXSP FLUSHSP(x,xc,xs,xe,xm) +#define FLUSHYSP FLUSHSP(y,yc,ys,ye,ym) diff --git a/arch/mips/math-emu/ieee754sp.c b/arch/mips/math-emu/ieee754sp.c index 29546fcf0d05..03a6f621d8d9 100644 --- a/arch/mips/math-emu/ieee754sp.c +++ b/arch/mips/math-emu/ieee754sp.c @@ -42,9 +42,7 @@ int ieee754sp_isnan(ieee754sp x) int ieee754sp_issnan(ieee754sp x) { assert(ieee754sp_isnan(x)); - if (ieee754_csr.noq) - return 1; - return !(SPMANT(x) & SP_MBIT(SP_MBITS - 1)); + return (SPMANT(x) & SP_MBIT(SP_MBITS-1)); } @@ -72,12 +70,13 @@ ieee754sp ieee754sp_nanxcpt(ieee754sp r, const char *op, ...) if (!ieee754sp_issnan(r)) /* QNAN does not cause invalid op !! */ return r; - if (!SETCX(IEEE754_INVALID_OPERATION)) { + if (!SETANDTESTCX(IEEE754_INVALID_OPERATION)) { /* not enabled convert to a quiet NaN */ - if (ieee754_csr.noq) + SPMANT(r) &= (~SP_MBIT(SP_MBITS-1)); + if (ieee754sp_isnan(r)) return r; - SPMANT(r) |= SP_MBIT(SP_MBITS - 1); - return r; + else + return ieee754sp_indef(); } ax.op = op; @@ -100,6 +99,32 @@ ieee754sp ieee754sp_bestnan(ieee754sp x, ieee754sp y) } +static unsigned get_rounding(int sn, unsigned xm) +{ + /* inexact must round of 3 bits + */ + if (xm & (SP_MBIT(3) - 1)) { + switch (ieee754_csr.rm) { + case IEEE754_RZ: + break; + case IEEE754_RN: + xm += 0x3 + ((xm >> 3) & 1); + /* xm += (xm&0x8)?0x4:0x3 */ + break; + case IEEE754_RU: /* toward +Infinity */ + if (!sn) /* ?? */ + xm += 0x8; + break; + case IEEE754_RD: /* toward -Infinity */ + if (sn) /* ?? */ + xm += 0x8; + break; + } + } + return xm; +} + + /* generate a normal/denormal number with over,under handling * sn is sign * xe is an unbiased exponent @@ -118,39 +143,58 @@ ieee754sp ieee754sp_format(int sn, int xe, unsigned xm) if (ieee754_csr.nod) { SETCX(IEEE754_UNDERFLOW); - return ieee754sp_zero(sn); + SETCX(IEEE754_INEXACT); + + switch(ieee754_csr.rm) { + case IEEE754_RN: + return ieee754sp_zero(sn); + case IEEE754_RZ: + return ieee754sp_zero(sn); + case IEEE754_RU: /* toward +Infinity */ + if(sn == 0) + return ieee754sp_min(0); + else + return ieee754sp_zero(1); + case IEEE754_RD: /* toward -Infinity */ + if(sn == 0) + return ieee754sp_zero(0); + else + return ieee754sp_min(1); + } } - /* sticky right shift es bits - */ - SPXSRSXn(es); - - assert((xm & (SP_HIDDEN_BIT << 3)) == 0); - assert(xe == SP_EMIN); + if (xe == SP_EMIN - 1 + && get_rounding(sn, xm) >> (SP_MBITS + 1 + 3)) + { + /* Not tiny after rounding */ + SETCX(IEEE754_INEXACT); + xm = get_rounding(sn, xm); + xm >>= 1; + /* Clear grs bits */ + xm &= ~(SP_MBIT(3) - 1); + xe++; + } + else { + /* sticky right shift es bits + */ + SPXSRSXn(es); + assert((xm & (SP_HIDDEN_BIT << 3)) == 0); + assert(xe == SP_EMIN); + } } if (xm & (SP_MBIT(3) - 1)) { SETCX(IEEE754_INEXACT); - /* inexact must round of 3 bits - */ - switch (ieee754_csr.rm) { - case IEEE754_RZ: - break; - case IEEE754_RN: - xm += 0x3 + ((xm >> 3) & 1); - /* xm += (xm&0x8)?0x4:0x3 */ - break; - case IEEE754_RU: /* toward +Infinity */ - if (!sn) /* ?? */ - xm += 0x8; - break; - case IEEE754_RD: /* toward -Infinity */ - if (sn) /* ?? */ - xm += 0x8; - break; + if ((xm & (SP_HIDDEN_BIT << 3)) == 0) { + SETCX(IEEE754_UNDERFLOW); } - /* adjust exponent for rounding add overflowing + + /* inexact must round of 3 bits */ - if (xm >> (SP_MBITS + 1 + 3)) { /* add causes mantissa overflow */ + xm = get_rounding(sn, xm); + /* adjust exponent for rounding add overflowing + */ + if (xm >> (SP_MBITS + 1 + 3)) { + /* add causes mantissa overflow */ xm >>= 1; xe++; } @@ -163,6 +207,7 @@ ieee754sp ieee754sp_format(int sn, int xe, unsigned xm) if (xe > SP_EMAX) { SETCX(IEEE754_OVERFLOW); + SETCX(IEEE754_INEXACT); /* -O can be table indexed by (rm,sn) */ switch (ieee754_csr.rm) { case IEEE754_RN: @@ -186,7 +231,8 @@ ieee754sp ieee754sp_format(int sn, int xe, unsigned xm) if ((xm & SP_HIDDEN_BIT) == 0) { /* we underflow (tiny/zero) */ assert(xe == SP_EMIN); - SETCX(IEEE754_UNDERFLOW); + if (ieee754_csr.mx & IEEE754_UNDERFLOW) + SETCX(IEEE754_UNDERFLOW); return buildsp(sn, SP_EMIN - 1 + SP_EBIAS, xm); } else { assert((xm >> (SP_MBITS + 1)) == 0); /* no execess */ diff --git a/arch/mips/math-emu/ieee754sp.h b/arch/mips/math-emu/ieee754sp.h index b41b2a830f99..20b0b6e9ab07 100644 --- a/arch/mips/math-emu/ieee754sp.h +++ b/arch/mips/math-emu/ieee754sp.h @@ -1,4 +1,4 @@ -/* +/* * IEEE754 floating point * double precision internal header file */ @@ -70,7 +70,7 @@ static __inline ieee754sp buildsp(int s, int bx, unsigned m) extern int ieee754sp_isnan(ieee754sp); extern int ieee754sp_issnan(ieee754sp); extern int ieee754si_xcpt(int, const char *, ...); -extern long long ieee754di_xcpt(long long, const char *, ...); +extern s64 ieee754di_xcpt(s64, const char *, ...); extern ieee754sp ieee754sp_xcpt(ieee754sp, const char *, ...); extern ieee754sp ieee754sp_nanxcpt(ieee754sp, const char *, ...); extern ieee754sp ieee754sp_bestnan(ieee754sp, ieee754sp); diff --git a/arch/mips/math-emu/ieee754xcpt.c b/arch/mips/math-emu/ieee754xcpt.c index 7a4d9486000a..9417fd7000ab 100644 --- a/arch/mips/math-emu/ieee754xcpt.c +++ b/arch/mips/math-emu/ieee754xcpt.c @@ -23,12 +23,13 @@ /************************************************************************** * Nov 7, 2000 - * Added preprocessor hacks to map to Linux kernel diagnostics. + * Added preprocessor hacks to map to Linux kernel diagnostics. * * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved. *************************************************************************/ +#include <linux/kernel.h> #include "ieee754.h" /* @@ -42,7 +43,7 @@ static const char *const rtnames[] = { void ieee754_xcpt(struct ieee754xctx *xcp) { - printk("floating point exception in \"%s\", type=%s\n", + printk(KERN_DEBUG "floating point exception in \"%s\", type=%s\n", xcp->op, rtnames[xcp->rt]); } diff --git a/arch/mips/math-emu/kernel_linkage.c b/arch/mips/math-emu/kernel_linkage.c index c73c42c17d33..ee45415c69e3 100644 --- a/arch/mips/math-emu/kernel_linkage.c +++ b/arch/mips/math-emu/kernel_linkage.c @@ -20,13 +20,10 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. * - *************************************************************************/ -/* * Routines corresponding to Linux kernel FP context * manipulation primitives for the Algorithmics MIPS * FPU Emulator */ - #include <linux/sched.h> #include <asm/processor.h> #include <asm/signal.h> @@ -42,10 +39,10 @@ void fpu_emulator_init_fpu(void) { static int first = 1; int i; - + if (first) { first = 0; - printk("Algorithmics/MIPS FPU Emulator v1.4\n"); + printk("Algorithmics/MIPS FPU Emulator v1.5\n"); } current->thread.fpu.soft.sr = 0; diff --git a/arch/mips/math-emu/sp_add.c b/arch/mips/math-emu/sp_add.c index af192ebb73fb..5229fdc69db0 100644 --- a/arch/mips/math-emu/sp_add.c +++ b/arch/mips/math-emu/sp_add.c @@ -37,27 +37,23 @@ ieee754sp ieee754sp_add(ieee754sp x, ieee754sp y) CLEARCX; - switch (CLPAIR(xc, yc)) { - case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_SNAN): - return ieee754sp_nanxcpt(ieee754sp_bestnan(x, y), "add", x, - y); + FLUSHXSP; + FLUSHYSP; + switch (CLPAIR(xc, yc)) { + case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_QNAN): case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_SNAN): + case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_SNAN): case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_SNAN): case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_SNAN): case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_SNAN): case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_SNAN): - return ieee754sp_nanxcpt(y, "add", x, y); - - case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_QNAN): case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_ZERO): case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_NORM): case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_DNORM): case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_INF): - return ieee754sp_nanxcpt(x, "add", x, y); - - case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN): - return ieee754sp_bestnan(x, y); + SETCX(IEEE754_INVALID_OPERATION); + return ieee754sp_nanxcpt(ieee754sp_indef(), "add", x, y); case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_QNAN): case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_QNAN): @@ -65,6 +61,7 @@ ieee754sp ieee754sp_add(ieee754sp x, ieee754sp y) case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_QNAN): return y; + case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN): case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_ZERO): case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_NORM): case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_DNORM): @@ -72,7 +69,7 @@ ieee754sp ieee754sp_add(ieee754sp x, ieee754sp y) return x; - /* Inifity handling + /* Infinity handling */ case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_INF): diff --git a/arch/mips/math-emu/sp_cmp.c b/arch/mips/math-emu/sp_cmp.c index d8abc9c68c12..0d5a7a1c3070 100644 --- a/arch/mips/math-emu/sp_cmp.c +++ b/arch/mips/math-emu/sp_cmp.c @@ -27,15 +27,24 @@ #include "ieee754sp.h" -int ieee754sp_cmp(ieee754sp x, ieee754sp y, int cmp) +int ieee754sp_cmp(ieee754sp x, ieee754sp y, int cmp, int sig) { - CLEARCX; + COMPXSP; + COMPYSP; + + EXPLODEXSP; + EXPLODEYSP; + FLUSHXSP; + FLUSHYSP; + CLEARCX; /* Even clear inexact flag here */ if (ieee754sp_isnan(x) || ieee754sp_isnan(y)) { + if (sig || xc == IEEE754_CLASS_SNAN || yc == IEEE754_CLASS_SNAN) + SETCX(IEEE754_INVALID_OPERATION); if (cmp & IEEE754_CUN) return 1; if (cmp & (IEEE754_CLT | IEEE754_CGT)) { - if (SETCX(IEEE754_INVALID_OPERATION)) + if (sig && SETANDTESTCX(IEEE754_INVALID_OPERATION)) return ieee754si_xcpt(0, "fcmpf", x); } return 0; diff --git a/arch/mips/math-emu/sp_div.c b/arch/mips/math-emu/sp_div.c index 6023377037f5..cca82fac7d62 100644 --- a/arch/mips/math-emu/sp_div.c +++ b/arch/mips/math-emu/sp_div.c @@ -32,32 +32,28 @@ ieee754sp ieee754sp_div(ieee754sp x, ieee754sp y) COMPXSP; COMPYSP; - CLEARCX; - EXPLODEXSP; EXPLODEYSP; - switch (CLPAIR(xc, yc)) { - case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_SNAN): - return ieee754sp_nanxcpt(ieee754sp_bestnan(x, y), "div", x, - y); + CLEARCX; + + FLUSHXSP; + FLUSHYSP; + switch (CLPAIR(xc, yc)) { + case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_QNAN): case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_SNAN): + case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_SNAN): case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_SNAN): case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_SNAN): case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_SNAN): case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_SNAN): - return ieee754sp_nanxcpt(y, "div", x, y); - - case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_QNAN): case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_ZERO): case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_NORM): case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_DNORM): case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_INF): - return ieee754sp_nanxcpt(x, "div", x, y); - - case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN): - return ieee754sp_bestnan(x, y); + SETCX(IEEE754_INVALID_OPERATION); + return ieee754sp_nanxcpt(ieee754sp_indef(), "div", x, y); case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_QNAN): case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_QNAN): @@ -65,6 +61,7 @@ ieee754sp ieee754sp_div(ieee754sp x, ieee754sp y) case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_QNAN): return y; + case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN): case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_ZERO): case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_NORM): case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_DNORM): diff --git a/arch/mips/math-emu/sp_fdp.c b/arch/mips/math-emu/sp_fdp.c index ea467be52b58..c8d3c3a17c78 100644 --- a/arch/mips/math-emu/sp_fdp.c +++ b/arch/mips/math-emu/sp_fdp.c @@ -30,20 +30,24 @@ ieee754sp ieee754sp_fdp(ieee754dp x) { COMPXDP; + ieee754sp nan; + + EXPLODEXDP; CLEARCX; - EXPLODEXDP; + FLUSHXDP; switch (xc) { - case IEEE754_CLASS_QNAN: case IEEE754_CLASS_SNAN: - return ieee754sp_nanxcpt(buildsp(xs, - SP_EMAX + 1 + SP_EBIAS, - (unsigned long) - (xm >> - (DP_MBITS - SP_MBITS))), - "fdp", x); + SETCX(IEEE754_INVALID_OPERATION); + return ieee754sp_nanxcpt(ieee754sp_indef(), "fdp"); + case IEEE754_CLASS_QNAN: + nan = buildsp(xs, SP_EMAX + 1 + SP_EBIAS, (u32) + (xm >> (DP_MBITS - SP_MBITS))); + if (!ieee754sp_isnan(nan)) + nan = ieee754sp_indef(); + return ieee754sp_nanxcpt(nan, "fdp", x); case IEEE754_CLASS_INF: return ieee754sp_inf(xs); case IEEE754_CLASS_ZERO: @@ -51,15 +55,19 @@ ieee754sp ieee754sp_fdp(ieee754dp x) case IEEE754_CLASS_DNORM: /* can't possibly be sp representable */ SETCX(IEEE754_UNDERFLOW); + SETCX(IEEE754_INEXACT); + if ((ieee754_csr.rm == IEEE754_RU && !xs) || + (ieee754_csr.rm == IEEE754_RD && xs)) + return ieee754sp_xcpt(ieee754sp_mind(xs), "fdp", x); return ieee754sp_xcpt(ieee754sp_zero(xs), "fdp", x); case IEEE754_CLASS_NORM: break; } { - unsigned long rm; + u32 rm; - /* convert from DP_MBITS to SP_MBITS+3 with sticky right shift + /* convert from DP_MBITS to SP_MBITS+3 with sticky right shift */ rm = (xm >> (DP_MBITS - (SP_MBITS + 3))) | ((xm << (64 - (DP_MBITS - (SP_MBITS + 3)))) != 0); diff --git a/arch/mips/math-emu/sp_fint.c b/arch/mips/math-emu/sp_fint.c index 747fd86731de..b52d95b68701 100644 --- a/arch/mips/math-emu/sp_fint.c +++ b/arch/mips/math-emu/sp_fint.c @@ -52,13 +52,13 @@ ieee754sp ieee754sp_fint(int x) xe = SP_MBITS + 3; if (xm >> (SP_MBITS + 1 + 3)) { - /* shunt out overflow bits + /* shunt out overflow bits */ while (xm >> (SP_MBITS + 1 + 3)) { SPXSRSX1(); } } else { - /* normalize in grs extended single precision + /* normalize in grs extended single precision */ while ((xm >> (SP_MBITS + 3)) == 0) { xm <<= 1; diff --git a/arch/mips/math-emu/sp_flong.c b/arch/mips/math-emu/sp_flong.c index 6440572889c4..c2b505d69ee7 100644 --- a/arch/mips/math-emu/sp_flong.c +++ b/arch/mips/math-emu/sp_flong.c @@ -27,7 +27,7 @@ #include "ieee754sp.h" -ieee754sp ieee754sp_flong(long long x) +ieee754sp ieee754sp_flong(s64 x) { COMPXDP; /* <--- need 64-bit mantissa temp */ @@ -52,7 +52,7 @@ ieee754sp ieee754sp_flong(long long x) xe = SP_MBITS + 3; if (xm >> (SP_MBITS + 1 + 3)) { - /* shunt out overflow bits + /* shunt out overflow bits */ while (xm >> (SP_MBITS + 1 + 3)) { SPXSRSX1(); @@ -68,9 +68,9 @@ ieee754sp ieee754sp_flong(long long x) } -ieee754sp ieee754sp_fulong(unsigned long long u) +ieee754sp ieee754sp_fulong(u64 u) { - if ((long long) u < 0) + if ((s64) u < 0) return ieee754sp_add(ieee754sp_1e63(), ieee754sp_flong(u & ~(1ULL << 63))); return ieee754sp_flong(u); diff --git a/arch/mips/math-emu/sp_frexp.c b/arch/mips/math-emu/sp_frexp.c index 968fe30cf5bf..e547bcc87286 100644 --- a/arch/mips/math-emu/sp_frexp.c +++ b/arch/mips/math-emu/sp_frexp.c @@ -27,7 +27,7 @@ #include "ieee754sp.h" -/* close to ieeep754sp_logb +/* close to ieeep754sp_logb */ ieee754sp ieee754sp_frexp(ieee754sp x, int *eptr) { diff --git a/arch/mips/math-emu/sp_modf.c b/arch/mips/math-emu/sp_modf.c index 69d69214899c..305dea24d2d6 100644 --- a/arch/mips/math-emu/sp_modf.c +++ b/arch/mips/math-emu/sp_modf.c @@ -59,7 +59,7 @@ ieee754sp ieee754sp_modf(ieee754sp x, ieee754sp * ip) *ip = x; return ieee754sp_zero(xs); } - /* generate ipart mantissa by clearing bottom bits + /* generate ipart mantissa by clearing bottom bits */ *ip = buildsp(xs, xe + SP_EBIAS, ((xm >> (SP_MBITS - xe)) << (SP_MBITS - xe)) & diff --git a/arch/mips/math-emu/sp_mul.c b/arch/mips/math-emu/sp_mul.c index 9bcd5449be0d..954bd7a1a1e3 100644 --- a/arch/mips/math-emu/sp_mul.c +++ b/arch/mips/math-emu/sp_mul.c @@ -32,32 +32,28 @@ ieee754sp ieee754sp_mul(ieee754sp x, ieee754sp y) COMPXSP; COMPYSP; - CLEARCX; - EXPLODEXSP; EXPLODEYSP; - switch (CLPAIR(xc, yc)) { - case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_SNAN): - return ieee754sp_nanxcpt(ieee754sp_bestnan(x, y), "mul", x, - y); + CLEARCX; + + FLUSHXSP; + FLUSHYSP; + switch (CLPAIR(xc, yc)) { + case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_QNAN): case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_SNAN): + case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_SNAN): case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_SNAN): case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_SNAN): case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_SNAN): case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_SNAN): - return ieee754sp_nanxcpt(y, "mul", x, y); - - case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_QNAN): case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_ZERO): case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_NORM): case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_DNORM): case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_INF): - return ieee754sp_nanxcpt(x, "mul", x, y); - - case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN): - return ieee754sp_bestnan(x, y); + SETCX(IEEE754_INVALID_OPERATION); + return ieee754sp_nanxcpt(ieee754sp_indef(), "mul", x, y); case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_QNAN): case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_QNAN): @@ -65,6 +61,7 @@ ieee754sp ieee754sp_mul(ieee754sp x, ieee754sp y) case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_QNAN): return y; + case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN): case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_ZERO): case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_NORM): case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_DNORM): diff --git a/arch/mips/math-emu/sp_simple.c b/arch/mips/math-emu/sp_simple.c index 467e1065486b..4ddbe6b2285d 100644 --- a/arch/mips/math-emu/sp_simple.c +++ b/arch/mips/math-emu/sp_simple.c @@ -42,7 +42,16 @@ ieee754sp ieee754sp_copysign(ieee754sp x, ieee754sp y) ieee754sp ieee754sp_neg(ieee754sp x) { + COMPXSP; + + EXPLODEXSP; CLEARCX; + FLUSHXSP; + + if (xc == IEEE754_CLASS_SNAN) { + SETCX(IEEE754_INVALID_OPERATION); + return ieee754sp_nanxcpt(ieee754sp_indef(), "neg"); + } if (ieee754sp_isnan(x)) /* but not infinity */ return ieee754sp_nanxcpt(x, "neg", x); @@ -55,7 +64,16 @@ ieee754sp ieee754sp_neg(ieee754sp x) ieee754sp ieee754sp_abs(ieee754sp x) { + COMPXSP; + + EXPLODEXSP; CLEARCX; + FLUSHXSP; + + if (xc == IEEE754_CLASS_SNAN) { + SETCX(IEEE754_INVALID_OPERATION); + return ieee754sp_nanxcpt(ieee754sp_indef(), "abs"); + } if (ieee754sp_isnan(x)) /* but not infinity */ return ieee754sp_nanxcpt(x, "abs", x); diff --git a/arch/mips/math-emu/sp_sqrt.c b/arch/mips/math-emu/sp_sqrt.c index d3eab4fb7a44..e6a4e8f09c01 100644 --- a/arch/mips/math-emu/sp_sqrt.c +++ b/arch/mips/math-emu/sp_sqrt.c @@ -27,43 +27,44 @@ #include "ieee754sp.h" -static const struct ieee754sp_konst knan = { - 0, SP_EBIAS + SP_EMAX + 1, 0 -}; - -#define nan ((ieee754sp)knan) - ieee754sp ieee754sp_sqrt(ieee754sp x) { - int sign = (int) 0x80000000; int ix, s, q, m, t, i; unsigned int r; - COMPXDP; + COMPXSP; /* take care of Inf and NaN */ - EXPLODEXDP; + EXPLODEXSP; + CLEARCX; + FLUSHXSP; /* x == INF or NAN? */ switch (xc) { case IEEE754_CLASS_QNAN: - case IEEE754_CLASS_SNAN: /* sqrt(Nan) = Nan */ return ieee754sp_nanxcpt(x, "sqrt"); + case IEEE754_CLASS_SNAN: + SETCX(IEEE754_INVALID_OPERATION); + return ieee754sp_nanxcpt(ieee754sp_indef(), "sqrt"); case IEEE754_CLASS_ZERO: /* sqrt(0) = 0 */ return x; case IEEE754_CLASS_INF: - if (xs) + if (xs) { /* sqrt(-Inf) = Nan */ - return ieee754sp_nanxcpt(nan, "sqrt"); + SETCX(IEEE754_INVALID_OPERATION); + return ieee754sp_nanxcpt(ieee754sp_indef(), "sqrt"); + } /* sqrt(+Inf) = Inf */ return x; case IEEE754_CLASS_DNORM: case IEEE754_CLASS_NORM: - if (xs) + if (xs) { /* sqrt(-x) = Nan */ - return ieee754sp_nanxcpt(nan, "sqrt"); + SETCX(IEEE754_INVALID_OPERATION); + return ieee754sp_nanxcpt(ieee754sp_indef(), "sqrt"); + } break; } @@ -99,6 +100,7 @@ ieee754sp ieee754sp_sqrt(ieee754sp x) } if (ix != 0) { + SETCX(IEEE754_INEXACT); switch (ieee754_csr.rm) { case IEEE754_RP: q += 2; diff --git a/arch/mips/math-emu/sp_sub.c b/arch/mips/math-emu/sp_sub.c index e87e804f572d..4a24f82a72d8 100644 --- a/arch/mips/math-emu/sp_sub.c +++ b/arch/mips/math-emu/sp_sub.c @@ -32,32 +32,28 @@ ieee754sp ieee754sp_sub(ieee754sp x, ieee754sp y) COMPXSP; COMPYSP; - CLEARCX; - EXPLODEXSP; EXPLODEYSP; - switch (CLPAIR(xc, yc)) { - case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_SNAN): - return ieee754sp_nanxcpt(ieee754sp_bestnan(x, y), "sub", x, - y); + CLEARCX; + FLUSHXSP; + FLUSHYSP; + + switch (CLPAIR(xc, yc)) { + case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_QNAN): case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_SNAN): + case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_SNAN): case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_SNAN): case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_SNAN): case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_SNAN): case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_SNAN): - return ieee754sp_nanxcpt(y, "sub", x, y); - - case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_QNAN): case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_ZERO): case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_NORM): case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_DNORM): case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_INF): - return ieee754sp_nanxcpt(x, "sub", x, y); - - case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN): - return ieee754sp_bestnan(x, y); + SETCX(IEEE754_INVALID_OPERATION); + return ieee754sp_nanxcpt(ieee754sp_indef(), "sub", x, y); case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_QNAN): case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_QNAN): @@ -65,6 +61,7 @@ ieee754sp ieee754sp_sub(ieee754sp x, ieee754sp y) case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_QNAN): return y; + case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN): case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_ZERO): case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_NORM): case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_DNORM): @@ -72,7 +69,7 @@ ieee754sp ieee754sp_sub(ieee754sp x, ieee754sp y) return x; - /* Inifity handling + /* Infinity handling */ case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_INF): @@ -170,13 +167,13 @@ ieee754sp ieee754sp_sub(ieee754sp x, ieee754sp y) xe = xe; xs = ys; } - if (xm == 0) + if (xm == 0) { if (ieee754_csr.rm == IEEE754_RD) return ieee754sp_zero(1); /* round negative inf. => sign = -1 */ else return ieee754sp_zero(0); /* other round modes => sign = 1 */ - - /* normalize to rounding precision + } + /* normalize to rounding precision */ while ((xm >> (SP_MBITS + 3)) == 0) { xm <<= 1; diff --git a/arch/mips/math-emu/sp_tint.c b/arch/mips/math-emu/sp_tint.c index 8f3ed14c6090..d77f4629671d 100644 --- a/arch/mips/math-emu/sp_tint.c +++ b/arch/mips/math-emu/sp_tint.c @@ -35,38 +35,78 @@ int ieee754sp_tint(ieee754sp x) CLEARCX; EXPLODEXSP; + FLUSHXSP; switch (xc) { case IEEE754_CLASS_SNAN: case IEEE754_CLASS_QNAN: - SETCX(IEEE754_INVALID_OPERATION); - return ieee754si_xcpt(ieee754si_indef(), "fixsp", x); case IEEE754_CLASS_INF: - SETCX(IEEE754_OVERFLOW); - return ieee754si_xcpt(ieee754si_indef(), "fixsp", x); + SETCX(IEEE754_INVALID_OPERATION); + return ieee754si_xcpt(ieee754si_indef(), "sp_tint", x); case IEEE754_CLASS_ZERO: return 0; - case IEEE754_CLASS_DNORM: /* much to small */ - SETCX(IEEE754_UNDERFLOW); - return ieee754si_xcpt(0, "fixsp", x); + case IEEE754_CLASS_DNORM: case IEEE754_CLASS_NORM: break; } if (xe >= 31) { - SETCX(IEEE754_OVERFLOW); - return ieee754si_xcpt(ieee754si_indef(), "fix", x); - } - if (xe < 0) { - SETCX(IEEE754_UNDERFLOW); - return ieee754si_xcpt(0, "fix", x); + /* look for valid corner case */ + if (xe == 31 && xs && xm == SP_HIDDEN_BIT) + return -0x80000000; + /* Set invalid. We will only use overflow for floating + point overflow */ + SETCX(IEEE754_INVALID_OPERATION); + return ieee754si_xcpt(ieee754si_indef(), "sp_tint", x); } /* oh gawd */ if (xe > SP_MBITS) { xm <<= xe - SP_MBITS; - } else if (xe < SP_MBITS) { - /* XXX no rounding - */ - xm >>= SP_MBITS - xe; + } else { + u32 residue; + int round; + int sticky; + int odd; + + if (xe < -1) { + residue = xm; + round = 0; + sticky = residue != 0; + xm = 0; + } + else { + /* Shifting a u32 32 times does not work, + * so we do it in two steps. Be aware that xe + * may be -1 */ + residue = xm << (xe + 1); + residue <<= 31 - SP_MBITS; + round = (residue >> 31) != 0; + sticky = (residue << 1) != 0; + xm >>= SP_MBITS - xe; + } + odd = (xm & 0x1) != 0x0; + switch (ieee754_csr.rm) { + case IEEE754_RN: + if (round && (sticky || odd)) + xm++; + break; + case IEEE754_RZ: + break; + case IEEE754_RU: /* toward +Infinity */ + if ((round || sticky) && !xs) + xm++; + break; + case IEEE754_RD: /* toward -Infinity */ + if ((round || sticky) && xs) + xm++; + break; + } + if ((xm >> 31) != 0) { + /* This can happen after rounding */ + SETCX(IEEE754_INVALID_OPERATION); + return ieee754si_xcpt(ieee754si_indef(), "sp_tint", x); + } + if (round || sticky) + SETCX(IEEE754_INEXACT); } if (xs) return -xm; diff --git a/arch/mips/math-emu/sp_tlong.c b/arch/mips/math-emu/sp_tlong.c index a7b0712e32f5..9b1ac8d57e54 100644 --- a/arch/mips/math-emu/sp_tlong.c +++ b/arch/mips/math-emu/sp_tlong.c @@ -27,45 +27,81 @@ #include "ieee754sp.h" -long long ieee754sp_tlong(ieee754sp x) +s64 ieee754sp_tlong(ieee754sp x) { COMPXDP; /* <-- need 64-bit mantissa tmp */ CLEARCX; EXPLODEXSP; + FLUSHXSP; switch (xc) { case IEEE754_CLASS_SNAN: case IEEE754_CLASS_QNAN: - SETCX(IEEE754_INVALID_OPERATION); - return ieee754di_xcpt(ieee754di_indef(), "sp_tlong", x); case IEEE754_CLASS_INF: - SETCX(IEEE754_OVERFLOW); + SETCX(IEEE754_INVALID_OPERATION); return ieee754di_xcpt(ieee754di_indef(), "sp_tlong", x); case IEEE754_CLASS_ZERO: return 0; - case IEEE754_CLASS_DNORM: /* much to small */ - SETCX(IEEE754_UNDERFLOW); - return ieee754di_xcpt(0, "sp_tlong", x); + case IEEE754_CLASS_DNORM: case IEEE754_CLASS_NORM: break; } if (xe >= 63) { - SETCX(IEEE754_OVERFLOW); + /* look for valid corner case */ + if (xe == 63 && xs && xm == SP_HIDDEN_BIT) + return -0x8000000000000000LL; + /* Set invalid. We will only use overflow for floating + point overflow */ + SETCX(IEEE754_INVALID_OPERATION); return ieee754di_xcpt(ieee754di_indef(), "sp_tlong", x); } - if (xe < 0) { - SETCX(IEEE754_UNDERFLOW); - return ieee754di_xcpt(0, "sp_tlong", x); - } /* oh gawd */ if (xe > SP_MBITS) { xm <<= xe - SP_MBITS; } else if (xe < SP_MBITS) { - /* XXX no rounding - */ - xm >>= SP_MBITS - xe; + u32 residue; + int round; + int sticky; + int odd; + + if (xe < -1) { + residue = xm; + round = 0; + sticky = residue != 0; + xm = 0; + } + else { + residue = xm << (32 - SP_MBITS + xe); + round = (residue >> 31) != 0; + sticky = (residue << 1) != 0; + xm >>= SP_MBITS - xe; + } + odd = (xm & 0x1) != 0x0; + switch (ieee754_csr.rm) { + case IEEE754_RN: + if (round && (sticky || odd)) + xm++; + break; + case IEEE754_RZ: + break; + case IEEE754_RU: /* toward +Infinity */ + if ((round || sticky) && !xs) + xm++; + break; + case IEEE754_RD: /* toward -Infinity */ + if ((round || sticky) && xs) + xm++; + break; + } + if ((xm >> 63) != 0) { + /* This can happen after rounding */ + SETCX(IEEE754_INVALID_OPERATION); + return ieee754di_xcpt(ieee754di_indef(), "sp_tlong", x); + } + if (round || sticky) + SETCX(IEEE754_INEXACT); } if (xs) return -xm; @@ -74,14 +110,14 @@ long long ieee754sp_tlong(ieee754sp x) } -unsigned long long ieee754sp_tulong(ieee754sp x) +u64 ieee754sp_tulong(ieee754sp x) { ieee754sp hb = ieee754sp_1e63(); /* what if x < 0 ?? */ if (ieee754sp_lt(x, hb)) - return (unsigned long long) ieee754sp_tlong(x); + return (u64) ieee754sp_tlong(x); - return (unsigned long long) ieee754sp_tlong(ieee754sp_sub(x, hb)) | + return (u64) ieee754sp_tlong(ieee754sp_sub(x, hb)) | (1ULL << 63); } diff --git a/arch/mips/mm/Makefile b/arch/mips/mm/Makefile index ac9a37c624ab..33b31b5aba07 100644 --- a/arch/mips/mm/Makefile +++ b/arch/mips/mm/Makefile @@ -2,17 +2,29 @@ # Makefile for the Linux/MIPS-specific parts of the memory manager. # -obj-y += extable.o init.o ioremap.o fault.o loadmmu.o +obj-y += cache.o extable.o init.o ioremap.o fault.o \ + pgtable.o loadmmu.o -obj-$(CONFIG_CPU_R3000) += r2300.o -obj-$(CONFIG_CPU_R4300) += r4xx0.o -obj-$(CONFIG_CPU_R4X00) += r4xx0.o -obj-$(CONFIG_CPU_VR41XX) += r4xx0.o -obj-$(CONFIG_CPU_R5000) += r4xx0.o -obj-$(CONFIG_CPU_NEVADA) += r4xx0.o -obj-$(CONFIG_CPU_R5432) += r5432.o -obj-$(CONFIG_CPU_RM7000) += rm7k.o -obj-$(CONFIG_CPU_MIPS32) += mips32.o -obj-$(CONFIG_CPU_MIPS64) += mips32.o -obj-$(CONFIG_SGI_IP22) += umap.o -obj-$(CONFIG_BAGET_MIPS) += umap.o +obj-$(CONFIG_HIGHMEM) += highmem.o + +obj-$(CONFIG_CPU_R3000) += pg-r3k.o c-r3k.o tlb-r3k.o tlbex-r3k.o +obj-$(CONFIG_CPU_TX39XX) += pg-r3k.o c-tx39.o tlb-r3k.o tlbex-r3k.o +obj-$(CONFIG_CPU_TX49XX) += pg-r4k.o c-r4k.o tlb-r4k.o tlbex-r4k.o +obj-$(CONFIG_CPU_R4300) += pg-r4k.o c-r4k.o tlb-r4k.o tlbex-r4k.o +obj-$(CONFIG_CPU_R4X00) += pg-r4k.o c-r4k.o tlb-r4k.o tlbex-r4k.o +obj-$(CONFIG_CPU_VR41XX) += pg-r4k.o c-r4k.o tlb-r4k.o tlbex-r4k.o +obj-$(CONFIG_CPU_R5000) += pg-r4k.o c-r4k.o tlb-r4k.o tlbex-r4k.o +obj-$(CONFIG_CPU_NEVADA) += pg-r4k.o c-r4k.o tlb-r4k.o tlbex-r4k.o +obj-$(CONFIG_CPU_R5432) += pg-r4k.o c-r4k.o tlb-r4k.o tlbex-r4k.o +obj-$(CONFIG_CPU_RM7000) += pg-r4k.o c-r4k.o tlb-r4k.o tlbex-r4k.o +obj-$(CONFIG_CPU_R10000) += pg-r4k.o c-r4k.o tlb-r4k.o tlbex-r4k.o +obj-$(CONFIG_CPU_MIPS32) += pg-r4k.o c-r4k.o tlb-r4k.o tlbex-r4k.o +obj-$(CONFIG_CPU_MIPS64) += pg-r4k.o c-r4k.o tlb-r4k.o tlbex-r4k.o +obj-$(CONFIG_CPU_SB1) += c-sb1.o cex-sb1.o cerr-sb1.o pg-sb1.o \ + tlb-sb1.o tlbex-r4k.o + +obj-$(CONFIG_CPU_RM7000) += sc-rm7k.o +obj-$(CONFIG_R5000_CPU_SCACHE) += sc-r5k.o +obj-$(CONFIG_SGI_IP22) += sc-ip22.o + +EXTRA_AFLAGS := $(CFLAGS) diff --git a/arch/mips/mm/andes.c b/arch/mips/mm/andes.c deleted file mode 100644 index 65725ef4fc19..000000000000 --- a/arch/mips/mm/andes.c +++ /dev/null @@ -1,196 +0,0 @@ -/* - * andes.c: MMU and cache operations for the R10000 (ANDES). - * - * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) - */ -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/mm.h> -#include <asm/page.h> -#include <asm/pgtable.h> -#include <asm/system.h> -#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) -{ - /* XXX */ -} - -static void andes_flush_cache_mm(struct mm_struct *mm) -{ - /* XXX */ -} - -static void andes_flush_cache_range(struct vm_area_struct *vma, - unsigned long start, - unsigned long end) -{ - /* XXX */ -} - -static void andes_flush_cache_page(struct vm_area_struct *vma, - unsigned long page) -{ - /* XXX */ -} - -static void andes_flush_page_to_ram(struct page * page) -{ - /* XXX */ -} - -static void __andes_flush_icache_range(unsigned long start, unsigned long end) -{ - /* XXX */ -} - -static void andes_flush_icache_page(struct vm_area_struct *vma, - struct page *page) -{ - /* XXX */ -} - -static void andes_flush_cache_sigtramp(unsigned long page) -{ - protected_writeback_dcache_line(addr & ~(dc_lsize - 1)); - protected_flush_icache_line(addr & ~(ic_lsize - 1)); -} - -/* TLB operations. XXX Write these dave... */ -void flush_tlb_all(void) -{ - /* XXX */ -} - -void flush_tlb_mm(struct mm_struct *mm) -{ - /* XXX */ -} - -void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, - unsigned long end) -{ - /* XXX */ -} - -void flush_tlb_page(struct vm_area_struct *vma, unsigned long page) -{ - /* XXX */ -} - -void pgd_init(unsigned long page) -{ -} - -void add_wired_entry(unsigned long entrylo0, unsigned long entrylo1, - unsigned long entryhi, unsigned long pagemask) -{ - /* XXX */ -} - -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_all = andes_flush_cache_all; - _flush_cache_mm = andes_flush_cache_mm; - _flush_cache_range = andes_flush_cache_range; - _flush_cache_page = andes_flush_cache_page; - _flush_cache_sigtramp = andes_flush_cache_sigtramp; - _flush_page_to_ram = andes_flush_page_to_ram; - _flush_icache_page = andes_flush_icache_page; - _flush_icache_range = andes_flush_icache_range; - - write_32bit_cp0_register(CP0_FRAMEMASK, 0); - - flush_cache_all(); - flush_tlb_all(); - - /* - * The R10k might even work for Linux/MIPS - but we're paranoid - * and refuse to run until this is tested on real silicon - */ - panic("CPU too expensive - making holiday in the ANDES!"); -} diff --git a/arch/mips/mm/c-r3k.c b/arch/mips/mm/c-r3k.c new file mode 100644 index 000000000000..95b937451f6d --- /dev/null +++ b/arch/mips/mm/c-r3k.c @@ -0,0 +1,344 @@ +/* + * r2300.c: R2000 and R3000 specific mmu/cache code. + * + * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * + * with a lot of changes to make this thing work for R3000s + * Tx39XX R4k style caches added. HK + * Copyright (C) 1998, 1999, 2000 Harald Koerfgen + * Copyright (C) 1998 Gleb Raiko & Vladimir Roganov + * Copyright (C) 2001 Maciej W. Rozycki + */ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/mm.h> + +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/mmu_context.h> +#include <asm/system.h> +#include <asm/isadep.h> +#include <asm/io.h> +#include <asm/bootinfo.h> +#include <asm/cpu.h> + +void r3k_clear_page(void * page); +void r3k_copy_page(void * to, void * from); + +static unsigned long icache_size, dcache_size; /* Size in bytes */ +static unsigned long icache_lsize, dcache_lsize; /* Size in bytes */ + +#undef DEBUG_CACHE + +unsigned long __init r3k_cache_size(unsigned long ca_flags) +{ + unsigned long flags, status, dummy, size; + volatile unsigned long *p; + + p = (volatile unsigned long *) KSEG0; + + flags = read_c0_status(); + + /* isolate cache space */ + write_c0_status((ca_flags|flags)&~ST0_IEC); + + *p = 0xa5a55a5a; + dummy = *p; + status = read_c0_status(); + + if (dummy != 0xa5a55a5a || (status & ST0_CM)) { + size = 0; + } else { + for (size = 128; size <= 0x40000; size <<= 1) + *(p + size) = 0; + *p = -1; + for (size = 128; + (size <= 0x40000) && (*(p + size) == 0); + size <<= 1) + ; + if (size > 0x40000) + size = 0; + } + + write_c0_status(flags); + + return size * sizeof(*p); +} + +unsigned long __init r3k_cache_lsize(unsigned long ca_flags) +{ + unsigned long flags, status, lsize, i; + volatile unsigned long *p; + + p = (volatile unsigned long *) KSEG0; + + flags = read_c0_status(); + + /* isolate cache space */ + write_c0_status((ca_flags|flags)&~ST0_IEC); + + for (i = 0; i < 128; i++) + *(p + i) = 0; + *(volatile unsigned char *)p = 0; + for (lsize = 1; lsize < 128; lsize <<= 1) { + *(p + lsize); + status = read_c0_status(); + if (!(status & ST0_CM)) + break; + } + for (i = 0; i < 128; i += lsize) + *(volatile unsigned char *)(p + i) = 0; + + write_c0_status(flags); + + return lsize * sizeof(*p); +} + +static void __init r3k_probe_cache(void) +{ + dcache_size = r3k_cache_size(ST0_ISC); + if (dcache_size) + dcache_lsize = r3k_cache_lsize(ST0_ISC); + + icache_size = r3k_cache_size(ST0_ISC|ST0_SWC); + if (icache_size) + icache_lsize = r3k_cache_lsize(ST0_ISC|ST0_SWC); +} + +static void r3k_flush_icache_range(unsigned long start, unsigned long end) +{ + unsigned long size, i, flags; + volatile unsigned char *p; + + size = end - start; + if (size > icache_size || KSEGX(start) != KSEG0) { + start = KSEG0; + size = icache_size; + } + p = (char *)start; + + flags = read_c0_status(); + + /* isolate cache space */ + write_c0_status((ST0_ISC|ST0_SWC|flags)&~ST0_IEC); + + for (i = 0; i < size; i += 0x080) { + asm ( "sb\t$0, 0x000(%0)\n\t" + "sb\t$0, 0x004(%0)\n\t" + "sb\t$0, 0x008(%0)\n\t" + "sb\t$0, 0x00c(%0)\n\t" + "sb\t$0, 0x010(%0)\n\t" + "sb\t$0, 0x014(%0)\n\t" + "sb\t$0, 0x018(%0)\n\t" + "sb\t$0, 0x01c(%0)\n\t" + "sb\t$0, 0x020(%0)\n\t" + "sb\t$0, 0x024(%0)\n\t" + "sb\t$0, 0x028(%0)\n\t" + "sb\t$0, 0x02c(%0)\n\t" + "sb\t$0, 0x030(%0)\n\t" + "sb\t$0, 0x034(%0)\n\t" + "sb\t$0, 0x038(%0)\n\t" + "sb\t$0, 0x03c(%0)\n\t" + "sb\t$0, 0x040(%0)\n\t" + "sb\t$0, 0x044(%0)\n\t" + "sb\t$0, 0x048(%0)\n\t" + "sb\t$0, 0x04c(%0)\n\t" + "sb\t$0, 0x050(%0)\n\t" + "sb\t$0, 0x054(%0)\n\t" + "sb\t$0, 0x058(%0)\n\t" + "sb\t$0, 0x05c(%0)\n\t" + "sb\t$0, 0x060(%0)\n\t" + "sb\t$0, 0x064(%0)\n\t" + "sb\t$0, 0x068(%0)\n\t" + "sb\t$0, 0x06c(%0)\n\t" + "sb\t$0, 0x070(%0)\n\t" + "sb\t$0, 0x074(%0)\n\t" + "sb\t$0, 0x078(%0)\n\t" + "sb\t$0, 0x07c(%0)\n\t" + : : "r" (p) ); + p += 0x080; + } + + write_c0_status(flags); +} + +static void r3k_flush_dcache_range(unsigned long start, unsigned long end) +{ + unsigned long size, i, flags; + volatile unsigned char *p; + + size = end - start; + if (size > dcache_size || KSEGX(start) != KSEG0) { + start = KSEG0; + size = dcache_size; + } + p = (char *)start; + + flags = read_c0_status(); + + /* isolate cache space */ + write_c0_status((ST0_ISC|flags)&~ST0_IEC); + + for (i = 0; i < size; i += 0x080) { + asm ( "sb\t$0, 0x000(%0)\n\t" + "sb\t$0, 0x004(%0)\n\t" + "sb\t$0, 0x008(%0)\n\t" + "sb\t$0, 0x00c(%0)\n\t" + "sb\t$0, 0x010(%0)\n\t" + "sb\t$0, 0x014(%0)\n\t" + "sb\t$0, 0x018(%0)\n\t" + "sb\t$0, 0x01c(%0)\n\t" + "sb\t$0, 0x020(%0)\n\t" + "sb\t$0, 0x024(%0)\n\t" + "sb\t$0, 0x028(%0)\n\t" + "sb\t$0, 0x02c(%0)\n\t" + "sb\t$0, 0x030(%0)\n\t" + "sb\t$0, 0x034(%0)\n\t" + "sb\t$0, 0x038(%0)\n\t" + "sb\t$0, 0x03c(%0)\n\t" + "sb\t$0, 0x040(%0)\n\t" + "sb\t$0, 0x044(%0)\n\t" + "sb\t$0, 0x048(%0)\n\t" + "sb\t$0, 0x04c(%0)\n\t" + "sb\t$0, 0x050(%0)\n\t" + "sb\t$0, 0x054(%0)\n\t" + "sb\t$0, 0x058(%0)\n\t" + "sb\t$0, 0x05c(%0)\n\t" + "sb\t$0, 0x060(%0)\n\t" + "sb\t$0, 0x064(%0)\n\t" + "sb\t$0, 0x068(%0)\n\t" + "sb\t$0, 0x06c(%0)\n\t" + "sb\t$0, 0x070(%0)\n\t" + "sb\t$0, 0x074(%0)\n\t" + "sb\t$0, 0x078(%0)\n\t" + "sb\t$0, 0x07c(%0)\n\t" + : : "r" (p) ); + p += 0x080; + } + + write_c0_status(flags); +} + +static inline unsigned long get_phys_page (unsigned long addr, + struct mm_struct *mm) +{ + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; + unsigned long physpage; + + pgd = pgd_offset(mm, addr); + pmd = pmd_offset(pgd, addr); + pte = pte_offset(pmd, addr); + + if ((physpage = pte_val(*pte)) & _PAGE_VALID) + return KSEG0ADDR(physpage & PAGE_MASK); + + return 0; +} + +static inline void r3k_flush_cache_all(void) +{ +} + +static inline void r3k___flush_cache_all(void) +{ + r3k_flush_icache_range(KSEG0, KSEG0 + icache_size); +} + +static void r3k_flush_cache_mm(struct mm_struct *mm) +{ +} + +static void r3k_flush_cache_range(struct vm_area_struct *vma, + unsigned long start, unsigned long end) +{ +} + +static void r3k_flush_cache_page(struct vm_area_struct *vma, + unsigned long page) +{ +} + +static void r3k_flush_data_cache_page(unsigned long addr) +{ +} + +static void r3k_flush_icache_page(struct vm_area_struct *vma, struct page *page) +{ + struct mm_struct *mm = vma->vm_mm; + unsigned long physpage; + + if (cpu_context(smp_processor_id(), mm) == 0) + return; + + if (!(vma->vm_flags & VM_EXEC)) + return; + +#ifdef DEBUG_CACHE + printk("cpage[%d,%08lx]", cpu_context(smp_processor_id(), mm), page); +#endif + + physpage = (unsigned long) page_address(page); + if (physpage) + r3k_flush_icache_range(physpage, physpage + PAGE_SIZE); +} + +static void r3k_flush_cache_sigtramp(unsigned long addr) +{ + unsigned long flags; + +#ifdef DEBUG_CACHE + printk("csigtramp[%08lx]", addr); +#endif + + flags = read_c0_status(); + + write_c0_status(flags&~ST0_IEC); + + /* Fill the TLB to avoid an exception with caches isolated. */ + asm ( "lw\t$0, 0x000(%0)\n\t" + "lw\t$0, 0x004(%0)\n\t" + : : "r" (addr) ); + + write_c0_status((ST0_ISC|ST0_SWC|flags)&~ST0_IEC); + + asm ( "sb\t$0, 0x000(%0)\n\t" + "sb\t$0, 0x004(%0)\n\t" + : : "r" (addr) ); + + write_c0_status(flags); +} + +static void r3k_dma_cache_wback_inv(unsigned long start, unsigned long size) +{ + iob(); + r3k_flush_dcache_range(start, start + size); +} + +void __init ld_mmu_r23000(void) +{ + _clear_page = r3k_clear_page; + _copy_page = r3k_copy_page; + + r3k_probe_cache(); + + flush_cache_all = r3k_flush_cache_all; + __flush_cache_all = r3k___flush_cache_all; + flush_cache_mm = r3k_flush_cache_mm; + flush_cache_range = r3k_flush_cache_range; + flush_cache_page = r3k_flush_cache_page; + flush_icache_page = r3k_flush_icache_page; + flush_icache_range = r3k_flush_icache_range; + + flush_cache_sigtramp = r3k_flush_cache_sigtramp; + flush_data_cache_page = r3k_flush_data_cache_page; + + _dma_cache_wback_inv = r3k_dma_cache_wback_inv; + + printk("Primary instruction cache %ldkB, linesize %ld bytes.\n", + icache_size >> 10, icache_lsize); + printk("Primary data cache %ldkB, linesize %ld bytes.\n", + dcache_size >> 10, dcache_lsize); +} diff --git a/arch/mips/mm/c-r4k.c b/arch/mips/mm/c-r4k.c new file mode 100644 index 000000000000..879dc11f8832 --- /dev/null +++ b/arch/mips/mm/c-r4k.c @@ -0,0 +1,1161 @@ +/* + * 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) 1996 David S. Miller (dm@engr.sgi.com) + * Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Ralf Baechle (ralf@gnu.org) + * Copyright (C) 1999, 2000 Silicon Graphics, Inc. + */ +#include <linux/config.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/bitops.h> + +#include <asm/bcache.h> +#include <asm/bootinfo.h> +#include <asm/cpu.h> +#include <asm/io.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/system.h> +#include <asm/mmu_context.h> +#include <asm/war.h> + +/* Primary cache parameters. */ +static unsigned long icache_size, dcache_size, scache_size; +unsigned long icache_way_size, dcache_way_size, scache_way_size; +static unsigned long scache_size; + +#include <asm/cacheops.h> +#include <asm/r4kcache.h> + +extern void andes_clear_page(void * page); +extern void r4k_clear_page32_d16(void * page); +extern void r4k_clear_page32_d32(void * page); +extern void r4k_clear_page_d16(void * page); +extern void r4k_clear_page_d32(void * page); +extern void r4k_clear_page_r4600_v1(void * page); +extern void r4k_clear_page_r4600_v2(void * page); +extern void r4k_clear_page_s16(void * page); +extern void r4k_clear_page_s32(void * page); +extern void r4k_clear_page_s64(void * page); +extern void r4k_clear_page_s128(void * page); +extern void andes_copy_page(void * to, void * from); +extern void r4k_copy_page_d16(void * to, void * from); +extern void r4k_copy_page_d32(void * to, void * from); +extern void r4k_copy_page_r4600_v1(void * to, void * from); +extern void r4k_copy_page_r4600_v2(void * to, void * from); +extern void r4k_copy_page_s16(void * to, void * from); +extern void r4k_copy_page_s32(void * to, void * from); +extern void r4k_copy_page_s64(void * to, void * from); +extern void r4k_copy_page_s128(void * to, void * from); + +/* + * Dummy cache handling routines for machines without boardcaches + */ +static void no_sc_noop(void) {} + +static struct bcache_ops no_sc_ops = { + .bc_enable = (void *)no_sc_noop, + .bc_disable = (void *)no_sc_noop, + .bc_wback_inv = (void *)no_sc_noop, + .bc_inv = (void *)no_sc_noop +}; + +struct bcache_ops *bcops = &no_sc_ops; + +#define R4600_HIT_CACHEOP_WAR_IMPL \ +do { \ + if (R4600_V2_HIT_CACHEOP_WAR && \ + (read_c0_prid() & 0xfff0) == 0x2020) { /* R4600 V2.0 */\ + *(volatile unsigned long *)KSEG1; \ + } \ + if (R4600_V1_HIT_CACHEOP_WAR) \ + __asm__ __volatile__("nop;nop;nop;nop"); \ +} while (0) + +static void r4k_blast_dcache_page(unsigned long addr) +{ + static void *l = &&init; + unsigned long dc_lsize; + + goto *l; + +dc_16: + blast_dcache16_page(addr); + return; + +dc_32: + R4600_HIT_CACHEOP_WAR_IMPL; + blast_dcache32_page(addr); + return; + +init: + dc_lsize = current_cpu_data.dcache.linesz; + + if (dc_lsize == 16) + l = &&dc_16; + else if (dc_lsize == 32) + l = &&dc_32; + goto *l; +} + +static void r4k_blast_dcache_page_indexed(unsigned long addr) +{ + static void *l = &&init; + unsigned long dc_lsize; + + goto *l; + +dc_16: + blast_dcache16_page_indexed(addr); + return; + +dc_32: + blast_dcache32_page_indexed(addr); + return; + +init: + dc_lsize = current_cpu_data.dcache.linesz; + + if (dc_lsize == 16) + l = &&dc_16; + else if (dc_lsize == 32) + l = &&dc_32; + goto *l; +} + +static void r4k_blast_dcache(void) +{ + static void *l = &&init; + unsigned long dc_lsize; + + goto *l; + +dc_16: + blast_dcache16(); + return; + +dc_32: + blast_dcache32(); + return; + +init: + dc_lsize = current_cpu_data.dcache.linesz; + + if (dc_lsize == 16) + l = &&dc_16; + else if (dc_lsize == 32) + l = &&dc_32; + goto *l; +} + +static void r4k_blast_icache_page(unsigned long addr) +{ + unsigned long ic_lsize = current_cpu_data.icache.linesz; + static void *l = &&init; + + goto *l; + +ic_16: + blast_icache16_page(addr); + return; + +ic_32: + blast_icache32_page(addr); + return; + +ic_64: + blast_icache64_page(addr); + return; + +init: + if (ic_lsize == 16) + l = &&ic_16; + else if (ic_lsize == 32) + l = &&ic_32; + else if (ic_lsize == 64) + l = &&ic_64; + goto *l; +} + +static void r4k_blast_icache_page_indexed(unsigned long addr) +{ + unsigned long ic_lsize = current_cpu_data.icache.linesz; + static void *l = &&init; + + goto *l; + +ic_16: + blast_icache16_page_indexed(addr); + return; + +ic_32: + blast_icache32_page_indexed(addr); + return; + +ic_64: + blast_icache64_page_indexed(addr); + return; + +init: + if (ic_lsize == 16) + l = &&ic_16; + else if (ic_lsize == 32) + l = &&ic_32; + else if (ic_lsize == 64) + l = &&ic_64; + goto *l; +} + +static void r4k_blast_icache(void) +{ + unsigned long ic_lsize = current_cpu_data.icache.linesz; + static void *l = &&init; + + goto *l; + +ic_16: + blast_icache16(); + return; + +ic_32: + blast_icache32(); + return; + +ic_64: + blast_icache64(); + return; + +init: + if (ic_lsize == 16) + l = &&ic_16; + else if (ic_lsize == 32) + l = &&ic_32; + else if (ic_lsize == 64) + l = &&ic_64; + goto *l; +} + +static void r4k_blast_scache_page(unsigned long addr) +{ + unsigned long sc_lsize = current_cpu_data.scache.linesz; + static void *l = &&init; + + goto *l; + +sc_16: + blast_scache16_page(addr); + return; + +sc_32: + blast_scache32_page(addr); + return; + +sc_64: + blast_scache64_page(addr); + return; + +sc_128: + blast_scache128_page(addr); + return; + +init: + if (sc_lsize == 16) + l = &&sc_16; + else if (sc_lsize == 32) + l = &&sc_32; + else if (sc_lsize == 64) + l = &&sc_64; + else if (sc_lsize == 128) + l = &&sc_128; + goto *l; +} + +static void r4k_blast_scache(void) +{ + unsigned long sc_lsize = current_cpu_data.scache.linesz; + static void *l = &&init; + + goto *l; + +sc_16: + blast_scache16(); + return; + +sc_32: + blast_scache32(); + return; + +sc_64: + blast_scache64(); + return; + +sc_128: + blast_scache128(); + return; + +init: + if (sc_lsize == 16) + l = &&sc_16; + else if (sc_lsize == 32) + l = &&sc_32; + else if (sc_lsize == 64) + l = &&sc_64; + else if (sc_lsize == 128) + l = &&sc_128; + goto *l; +} + +static void r4k_flush_cache_all(void) +{ + if (!cpu_has_dc_aliases) + return; + + r4k_blast_dcache(); + r4k_blast_icache(); +} + +static void r4k___flush_cache_all(void) +{ + r4k_blast_dcache(); + r4k_blast_icache(); + + switch (current_cpu_data.cputype) { + case CPU_R4000SC: + case CPU_R4000MC: + case CPU_R4400SC: + case CPU_R4400MC: + case CPU_R10000: + case CPU_R12000: + r4k_blast_scache(); + } +} + +static void r4k_flush_cache_range(struct vm_area_struct *vma, + unsigned long start, unsigned long end) +{ + if (cpu_context(smp_processor_id(), vma->vm_mm) != 0) { + r4k_blast_dcache(); + if (vma->vm_flags & VM_EXEC) + r4k_blast_icache(); + } +} + +static void r4k_flush_cache_mm(struct mm_struct *mm) +{ + if (!cpu_has_dc_aliases) + return; + + if (!cpu_context(smp_processor_id(), mm)) + return; + + r4k_blast_dcache(); + r4k_blast_icache(); + + /* + * Kludge alert. For obscure reasons R4000SC and R4400SC go nuts if we + * only flush the primary caches but R10000 and R12000 behave sane ... + */ + if (current_cpu_data.cputype == CPU_R4000SC || + current_cpu_data.cputype == CPU_R4000MC || + current_cpu_data.cputype == CPU_R4400SC || + current_cpu_data.cputype == CPU_R4400MC) + r4k_blast_scache(); +} + +static void r4k_flush_cache_page(struct vm_area_struct *vma, + unsigned long page) +{ + int exec = vma->vm_flags & VM_EXEC; + struct mm_struct *mm = vma->vm_mm; + pgd_t *pgdp; + pmd_t *pmdp; + pte_t *ptep; + + /* + * If ownes no valid ASID yet, cannot possibly have gotten + * this page into the cache. + */ + if (cpu_context(smp_processor_id(), mm) == 0) + return; + + page &= PAGE_MASK; + pgdp = pgd_offset(mm, page); + pmdp = pmd_offset(pgdp, page); + ptep = pte_offset(pmdp, page); + + /* + * If the page isn't marked valid, the page cannot possibly be + * in the cache. + */ + if (!(pte_val(*ptep) & _PAGE_PRESENT)) + return; + + /* + * Doing flushes for another ASID than the current one is + * too difficult since stupid R4k caches do a TLB translation + * for every cache flush operation. So we do indexed flushes + * in that case, which doesn't overly flush the cache too much. + */ + if ((mm == current->active_mm) && (pte_val(*ptep) & _PAGE_VALID)) { + if (cpu_has_dc_aliases || (exec && !cpu_has_ic_fills_f_dc)) + r4k_blast_dcache_page(page); + if (exec) + r4k_blast_icache_page(page); + + return; + } + + /* + * Do indexed flush, too much work to get the (possible) TLB refills + * to work correctly. + */ + page = (KSEG0 + (page & (dcache_size - 1))); + if (cpu_has_dc_aliases || (exec && !cpu_has_ic_fills_f_dc)) + r4k_blast_dcache_page_indexed(page); + if (exec) { + if (cpu_has_vtag_icache) { + int cpu = smp_processor_id(); + + if (cpu_context(cpu, vma->vm_mm) != 0) + drop_mmu_context(vma->vm_mm, cpu); + } else + r4k_blast_icache_page_indexed(page); + } +} + +static void r4k_flush_data_cache_page(unsigned long addr) +{ + r4k_blast_dcache_page(addr); +} + +static void r4k_flush_icache_range(unsigned long start, unsigned long end) +{ + unsigned long dc_lsize = current_cpu_data.dcache.linesz; + unsigned long addr, aend; + + if (!cpu_has_ic_fills_f_dc) { + if (end - start > dcache_size) + r4k_blast_dcache(); + else { + addr = start & ~(dc_lsize - 1); + aend = (end - 1) & ~(dc_lsize - 1); + + while (1) { + /* Hit_Writeback_Inv_D */ + protected_writeback_dcache_line(addr); + if (addr == aend) + break; + addr += dc_lsize; + } + } + } + + if (end - start > icache_size) + r4k_blast_icache(); + else { + addr = start & ~(dc_lsize - 1); + aend = (end - 1) & ~(dc_lsize - 1); + while (1) { + /* Hit_Invalidate_I */ + protected_flush_icache_line(addr); + if (addr == aend) + break; + addr += dc_lsize; + } + } +} + +/* + * Ok, this seriously sucks. We use them to flush a user page but don't + * know the virtual address, so we have to blast away the whole icache + * which is significantly more expensive than the real thing. Otoh we at + * least know the kernel address of the page so we can flush it + * selectivly. + */ +static void r4k_flush_icache_page(struct vm_area_struct *vma, + struct page *page) +{ + /* + * If there's no context yet, or the page isn't executable, no icache + * flush is needed. + */ + if (!(vma->vm_flags & VM_EXEC)) + return; + + /* + * Tricky ... Because we don't know the virtual address we've got the + * choice of either invalidating the entire primary and secondary + * caches or invalidating the secondary caches also. With the subset + * enforcment on R4000SC, R4400SC, R10000 and R12000 invalidating the + * secondary cache will result in any entries in the primary caches + * also getting invalidated which hopefully is a bit more economical. + */ + if (cpu_has_subset_pcaches) { + unsigned long addr = (unsigned long) page_address(page); + r4k_blast_scache_page(addr); + + return; + } + + if (!cpu_has_ic_fills_f_dc) { + unsigned long addr = (unsigned long) page_address(page); + r4k_blast_dcache_page(addr); + } + + /* + * We're not sure of the virtual address(es) involved here, so + * we have to flush the entire I-cache. + */ + if (cpu_has_vtag_icache) { + int cpu = smp_processor_id(); + + if (cpu_context(cpu, vma->vm_mm) != 0) + drop_mmu_context(vma->vm_mm, cpu); + } else + r4k_blast_icache(); +} + +#ifdef CONFIG_NONCOHERENT_IO + +static void r4k_dma_cache_wback_inv(unsigned long addr, unsigned long size) +{ + unsigned long end, a; + + if (cpu_has_subset_pcaches) { + unsigned long sc_lsize = current_cpu_data.scache.linesz; + + if (size >= scache_size) { + r4k_blast_scache(); + return; + } + + a = addr & ~(sc_lsize - 1); + end = (addr + size - 1) & ~(sc_lsize - 1); + while (1) { + flush_scache_line(a); /* Hit_Writeback_Inv_SD */ + if (a == end) + break; + a += sc_lsize; + } + return; + } + + /* + * Either no secondary cache or the available caches don't have the + * subset property so we have to flush the primary caches + * explicitly + */ + if (size >= dcache_size) { + r4k_blast_dcache(); + } else { + unsigned long dc_lsize = current_cpu_data.dcache.linesz; + + R4600_HIT_CACHEOP_WAR_IMPL; + a = addr & ~(dc_lsize - 1); + end = (addr + size - 1) & ~(dc_lsize - 1); + while (1) { + flush_dcache_line(a); /* Hit_Writeback_Inv_D */ + if (a == end) + break; + a += dc_lsize; + } + } + + bc_wback_inv(addr, size); +} + +static void r4k_dma_cache_inv(unsigned long addr, unsigned long size) +{ + unsigned long end, a; + + if (cpu_has_subset_pcaches) { + unsigned long sc_lsize = current_cpu_data.scache.linesz; + + if (size >= scache_size) { + r4k_blast_scache(); + return; + } + + a = addr & ~(sc_lsize - 1); + end = (addr + size - 1) & ~(sc_lsize - 1); + while (1) { + flush_scache_line(a); /* Hit_Writeback_Inv_SD */ + if (a == end) + break; + a += sc_lsize; + } + return; + } + + if (size >= dcache_size) { + r4k_blast_dcache(); + } else { + unsigned long dc_lsize = current_cpu_data.dcache.linesz; + + R4600_HIT_CACHEOP_WAR_IMPL; + a = addr & ~(dc_lsize - 1); + end = (addr + size - 1) & ~(dc_lsize - 1); + while (1) { + flush_dcache_line(a); /* Hit_Writeback_Inv_D */ + if (a == end) + break; + a += dc_lsize; + } + } + + bc_inv(addr, size); +} +#endif /* CONFIG_NONCOHERENT_IO */ + +/* + * While we're protected against bad userland addresses we don't care + * very much about what happens in that case. Usually a segmentation + * fault will dump the process later on anyway ... + */ +static void r4k_flush_cache_sigtramp(unsigned long addr) +{ + unsigned long ic_lsize = current_cpu_data.icache.linesz; + unsigned long dc_lsize = current_cpu_data.dcache.linesz; + + R4600_HIT_CACHEOP_WAR_IMPL; + protected_writeback_dcache_line(addr & ~(dc_lsize - 1)); + protected_flush_icache_line(addr & ~(ic_lsize - 1)); +} + +static void r4k_flush_icache_all(void) +{ + if (cpu_has_vtag_icache) + r4k_blast_icache(); +} + +static inline void rm7k_erratum31(void) +{ + const unsigned long ic_lsize = 32; + unsigned long addr; + + /* RM7000 erratum #31. The icache is screwed at startup. */ + write_c0_taglo(0); + write_c0_taghi(0); + + for (addr = KSEG0; addr <= KSEG0 + 4096; addr += ic_lsize) { + __asm__ __volatile__ ( + ".set noreorder\n\t" + ".set mips3\n\t" + "cache\t%1, 0(%0)\n\t" + "cache\t%1, 0x1000(%0)\n\t" + "cache\t%1, 0x2000(%0)\n\t" + "cache\t%1, 0x3000(%0)\n\t" + "cache\t%2, 0(%0)\n\t" + "cache\t%2, 0x1000(%0)\n\t" + "cache\t%2, 0x2000(%0)\n\t" + "cache\t%2, 0x3000(%0)\n\t" + "cache\t%1, 0(%0)\n\t" + "cache\t%1, 0x1000(%0)\n\t" + "cache\t%1, 0x2000(%0)\n\t" + "cache\t%1, 0x3000(%0)\n\t" + ".set\tmips0\n\t" + ".set\treorder\n\t" + : + : "r" (addr), "i" (Index_Store_Tag_I), "i" (Fill)); + } +} + +static char *way_string[] = { NULL, "direct mapped", "2-way", "3-way", "4-way", + "5-way", "6-way", "7-way", "8-way" +}; + +static void __init probe_pcache(void) +{ + struct cpuinfo_mips *c = ¤t_cpu_data; + unsigned int config = read_c0_config(); + unsigned int prid = read_c0_prid(); + unsigned long config1; + unsigned int lsize; + + switch (current_cpu_data.cputype) { + case CPU_R4600: /* QED style two way caches? */ + case CPU_R4700: + case CPU_R5000: + case CPU_NEVADA: + icache_size = 1 << (12 + ((config & CONF_IC) >> 9)); + c->icache.linesz = 16 << ((config & CONF_IB) >> 5); + c->icache.ways = 2; + c->icache.waybit = ffs(icache_size/2) - 1; + + dcache_size = 1 << (12 + ((config & CONF_DC) >> 6)); + c->dcache.linesz = 16 << ((config & CONF_DB) >> 4); + c->dcache.ways = 2; + c->dcache.waybit= ffs(dcache_size/2) - 1; + break; + + case CPU_R5432: + case CPU_R5500: + icache_size = 1 << (12 + ((config & CONF_IC) >> 9)); + c->icache.linesz = 16 << ((config & CONF_IB) >> 5); + c->icache.ways = 2; + c->icache.waybit= 0; + + dcache_size = 1 << (12 + ((config & CONF_DC) >> 6)); + c->dcache.linesz = 16 << ((config & CONF_DB) >> 4); + c->dcache.ways = 2; + c->dcache.waybit = 0; + break; + + case CPU_TX49XX: + icache_size = 1 << (12 + ((config & CONF_IC) >> 9)); + c->icache.linesz = 16 << ((config & CONF_IB) >> 5); + c->icache.ways = 4; + c->icache.waybit= 0; + + dcache_size = 1 << (12 + ((config & CONF_DC) >> 6)); + c->dcache.linesz = 16 << ((config & CONF_DB) >> 4); + c->dcache.ways = 4; + c->dcache.waybit = 0; + break; + + case CPU_R4000PC: + case CPU_R4000SC: + case CPU_R4000MC: + case CPU_R4400PC: + case CPU_R4400SC: + case CPU_R4400MC: + icache_size = 1 << (12 + ((config & CONF_IC) >> 9)); + c->icache.linesz = 16 << ((config & CONF_IB) >> 5); + c->icache.ways = 1; + c->icache.waybit = 0; /* doesn't matter */ + + dcache_size = 1 << (12 + ((config & CONF_DC) >> 6)); + c->dcache.linesz = 16 << ((config & CONF_DB) >> 4); + c->dcache.ways = 1; + c->dcache.waybit = 0; /* does not matter */ + break; + + case CPU_R10000: + case CPU_R12000: + icache_size = 1 << (12 + ((config & R10K_CONF_IC) >> 29)); + c->icache.linesz = 64; + c->icache.ways = 2; + c->icache.waybit = 0; + + dcache_size = 1 << (12 + ((config & R10K_CONF_DC) >> 26)); + c->dcache.linesz = 32; + c->dcache.ways = 2; + c->dcache.waybit = 0; + break; + + case CPU_VR4131: + icache_size = 1 << (10 + ((config & CONF_IC) >> 9)); + c->icache.linesz = 16 << ((config & CONF_IB) >> 5); + c->icache.ways = 2; + c->icache.waybit = ffs(icache_size/2) - 1; + + dcache_size = 1 << (10 + ((config & CONF_DC) >> 6)); + c->dcache.linesz = 16 << ((config & CONF_DB) >> 4); + c->dcache.ways = 2; + c->dcache.waybit = ffs(dcache_size/2) - 1; + break; + + case CPU_VR41XX: + case CPU_VR4111: + case CPU_VR4121: + case CPU_VR4122: + case CPU_VR4181: + case CPU_VR4181A: + icache_size = 1 << (10 + ((config & CONF_IC) >> 9)); + c->icache.linesz = 16 << ((config & CONF_IB) >> 5); + c->icache.ways = 1; + c->icache.waybit = 0; /* doesn't matter */ + + dcache_size = 1 << (10 + ((config & CONF_DC) >> 6)); + c->dcache.linesz = 16 << ((config & CONF_DB) >> 4); + c->dcache.ways = 1; + c->dcache.waybit = 0; /* does not matter */ + break; + + case CPU_RM7000: + rm7k_erratum31(); + + icache_size = 1 << (12 + ((config & CONF_IC) >> 9)); + c->icache.linesz = 16 << ((config & CONF_IB) >> 5); + c->icache.ways = 4; + c->icache.waybit = ffs(icache_size / c->icache.ways) - 1; + + dcache_size = 1 << (12 + ((config & CONF_DC) >> 6)); + c->dcache.linesz = 16 << ((config & CONF_DB) >> 4); + c->dcache.ways = 4; + c->dcache.waybit = ffs(dcache_size / c->dcache.ways) - 1; + break; + + default: + if (!(config & MIPS_CONF_M)) + panic("Don't know how to probe P-caches on this cpu."); + + /* + * So we seem to be a MIPS32 or MIPS64 CPU + * So let's probe the I-cache ... + */ + config1 = read_c0_config1(); + + if ((lsize = ((config1 >> 19) & 7))) + c->icache.linesz = 2 << lsize; + else + c->icache.linesz = lsize; + c->icache.sets = 64 << ((config1 >> 22) & 7); + c->icache.ways = 1 + ((config1 >> 16) & 7); + + icache_size = c->icache.sets * + c->icache.ways * + c->icache.linesz; + c->icache.waybit = ffs(icache_size/c->icache.ways) - 1; + + /* + * Now probe the MIPS32 / MIPS64 data cache. + */ + c->dcache.flags = 0; + + if ((lsize = ((config1 >> 10) & 7))) + c->dcache.linesz = 2 << lsize; + else + c->dcache.linesz= lsize; + c->dcache.sets = 64 << ((config1 >> 13) & 7); + c->dcache.ways = 1 + ((config1 >> 7) & 7); + + dcache_size = c->dcache.sets * + c->dcache.ways * + c->dcache.linesz; + c->dcache.waybit = ffs(dcache_size/c->dcache.ways) - 1; + break; + } + + /* + * Processor configuration sanity check for the R4000SC erratum + * #5. With page sizes larger than 32kB there is no possibility + * to get a VCE exception anymore so we don't care about this + * misconfiguration. The case is rather theoretical anyway; + * presumably no vendor is shipping his hardware in the "bad" + * configuration. + */ + if ((prid & 0xff00) == PRID_IMP_R4000 && (prid & 0xff) < 0x40 && + !(config & CONF_SC) && c->icache.linesz != 16 && + PAGE_SIZE <= 0x8000) + panic("Improper R4000SC processor configuration detected"); + + /* compute a couple of other cache variables */ + icache_way_size = icache_size / c->icache.ways; + dcache_way_size = dcache_size / c->dcache.ways; + + c->icache.sets = icache_size / (c->icache.linesz * c->icache.ways); + c->dcache.sets = dcache_size / (c->dcache.linesz * c->dcache.ways); + + /* + * R10000 and R12000 P-caches are odd in a positive way. They're 32kB + * 2-way virtually indexed so normally would suffer from aliases. So + * normally they'd suffer from aliases but magic in the hardware deals + * with that for us so we don't need to take care ourselves. + */ + if (current_cpu_data.cputype != CPU_R10000 && + current_cpu_data.cputype != CPU_R12000) + if (dcache_way_size > PAGE_SIZE) + c->dcache.flags |= MIPS_CACHE_ALIASES; + + if (config & 0x8) /* VI bit */ + c->icache.flags |= MIPS_CACHE_VTAG; + + switch (c->cputype) { + case CPU_20KC: + /* + * Some older 20Kc chips doesn't have the 'VI' bit in + * the config register. + */ + c->icache.flags |= MIPS_CACHE_VTAG; + break; + + case CPU_AU1500: + c->icache.flags |= MIPS_CACHE_IC_F_DC; + break; + } + + printk("Primary instruction cache %ldkB, %s, %s, linesize %d bytes.\n", + icache_size >> 10, + cpu_has_vtag_icache ? "virtually tagged" : "physically tagged", + way_string[c->icache.ways], c->icache.linesz); + + printk("Primary data cache %ldkB %s, linesize %d bytes.\n", + dcache_size >> 10, way_string[c->dcache.ways], c->dcache.linesz); +} + +/* + * If you even _breathe_ on this function, look at the gcc output and make sure + * it does not pop things on and off the stack for the cache sizing loop that + * executes in KSEG1 space or else you will crash and burn badly. You have + * been warned. + */ +static int __init probe_scache(void) +{ + extern unsigned long stext; + unsigned long flags, addr, begin, end, pow2; + unsigned int config = read_c0_config(); + struct cpuinfo_mips *c = ¤t_cpu_data; + int tmp; + + if (config & CONF_SC) + return 0; + + begin = (unsigned long) &stext; + begin &= ~((4 * 1024 * 1024) - 1); + end = begin + (4 * 1024 * 1024); + + /* + * This is such a bitch, you'd think they would make it easy to do + * this. Away you daemons of stupidity! + */ + local_irq_save(flags); + + /* Fill each size-multiple cache line with a valid tag. */ + pow2 = (64 * 1024); + for (addr = begin; addr < end; addr = (begin + pow2)) { + unsigned long *p = (unsigned long *) addr; + __asm__ __volatile__("nop" : : "r" (*p)); /* whee... */ + pow2 <<= 1; + } + + /* Load first line with zero (therefore invalid) tag. */ + write_c0_taglo(0); + write_c0_taghi(0); + __asm__ __volatile__("nop; nop; nop; nop;"); /* avoid the hazard */ + cache_op(Index_Store_Tag_I, begin); + cache_op(Index_Store_Tag_D, begin); + cache_op(Index_Store_Tag_SD, begin); + + /* Now search for the wrap around point. */ + pow2 = (128 * 1024); + tmp = 0; + for (addr = begin + (128 * 1024); addr < end; addr = begin + pow2) { + cache_op(Index_Load_Tag_SD, addr); + __asm__ __volatile__("nop; nop; nop; nop;"); /* hazard... */ + if (!read_c0_taglo()) + break; + pow2 <<= 1; + } + local_irq_restore(flags); + addr -= begin; + + c = ¤t_cpu_data; + scache_size = addr; + c->scache.linesz = 16 << ((config & R4K_CONF_SB) >> 22); + c->scache.ways = 1; + c->dcache.waybit = 0; /* does not matter */ + + return 1; +} + +static void __init setup_noscache_funcs(void) +{ + unsigned int prid; + + switch (current_cpu_data.dcache.linesz) { + case 16: + if (cpu_has_64bits) + _clear_page = r4k_clear_page_d16; + else + _clear_page = r4k_clear_page32_d16; + _copy_page = r4k_copy_page_d16; + + break; + case 32: + prid = read_c0_prid() & 0xfff0; + if (prid == 0x2010) { /* R4600 V1.7 */ + _clear_page = r4k_clear_page_r4600_v1; + _copy_page = r4k_copy_page_r4600_v1; + } else if (prid == 0x2020) { /* R4600 V2.0 */ + _clear_page = r4k_clear_page_r4600_v2; + _copy_page = r4k_copy_page_r4600_v2; + } else { + if (cpu_has_64bits) + _clear_page = r4k_clear_page_d32; + else + _clear_page = r4k_clear_page32_d32; + _copy_page = r4k_copy_page_d32; + } + break; + } +} + +static void __init setup_scache_funcs(void) +{ + if (current_cpu_data.dcache.linesz > current_cpu_data.scache.linesz) + panic("Invalid primary cache configuration detected"); + + if (current_cpu_data.cputype == CPU_R10000 || + current_cpu_data.cputype == CPU_R12000) { + _clear_page = andes_clear_page; + _copy_page = andes_copy_page; + return; + } + + switch (current_cpu_data.scache.linesz) { + case 16: + _clear_page = r4k_clear_page_s16; + _copy_page = r4k_copy_page_s16; + break; + case 32: + _clear_page = r4k_clear_page_s32; + _copy_page = r4k_copy_page_s32; + break; + case 64: + _clear_page = r4k_clear_page_s64; + _copy_page = r4k_copy_page_s64; + break; + case 128: + _clear_page = r4k_clear_page_s128; + _copy_page = r4k_copy_page_s128; + break; + } +} + +typedef int (*probe_func_t)(unsigned long); +extern int r5k_sc_init(void); +extern int rm7k_sc_init(void); + +static void __init setup_scache(void) +{ + struct cpuinfo_mips *c = ¤t_cpu_data; + unsigned int config = read_c0_config(); + probe_func_t probe_scache_kseg1; + int sc_present = 0; + + /* + * Do the probing thing on R4000SC and R4400SC processors. Other + * processors don't have a S-cache that would be relevant to the + * Linux memory managment. + */ + switch (current_cpu_data.cputype) { + case CPU_R4000PC: + case CPU_R4000SC: + case CPU_R4000MC: + case CPU_R4400PC: + case CPU_R4400SC: + case CPU_R4400MC: + probe_scache_kseg1 = (probe_func_t) (KSEG1ADDR(&probe_scache)); + sc_present = probe_scache_kseg1(config); + break; + + case CPU_R10000: + case CPU_R12000: + scache_size = 0x80000 << ((config & R10K_CONF_SS) >> 16); + c->scache.linesz = 64 << ((config >> 13) & 1); + c->scache.ways = 2; + c->scache.waybit= 0; + sc_present = 1; + break; + + case CPU_R5000: + case CPU_NEVADA: + setup_noscache_funcs(); +#ifdef CONFIG_R5000_CPU_SCACHE + r5k_sc_init(); +#endif + return; + + case CPU_RM7000: + setup_noscache_funcs(); +#ifdef CONFIG_RM7000_CPU_SCACHE + rm7k_sc_init(); +#endif + return; + + default: + sc_present = 0; + } + + if (!sc_present) { + setup_noscache_funcs(); + return; + } + + if ((current_cpu_data.isa_level == MIPS_CPU_ISA_M32 || + current_cpu_data.isa_level == MIPS_CPU_ISA_M64) && + !(current_cpu_data.scache.flags & MIPS_CACHE_NOT_PRESENT)) + panic("Dunno how to handle MIPS32 / MIPS64 second level cache"); + + printk("Unified secondary cache %ldkB %s, linesize %d bytes.\n", + scache_size >> 10, way_string[c->scache.ways], c->scache.linesz); + + current_cpu_data.options |= MIPS_CPU_SUBSET_CACHES; + setup_scache_funcs(); +} + +static inline void coherency_setup(void) +{ + change_c0_config(CONF_CM_CMASK, CONF_CM_DEFAULT); + + /* + * c0_status.cu=0 specifies that updates by the sc instruction use + * the coherency mode specified by the TLB; 1 means cachable + * coherent update on write will be used. Not all processors have + * this bit and; some wire it to zero, others like Toshiba had the + * silly idea of putting something else there ... + */ + switch (current_cpu_data.cputype) { + case CPU_R4000PC: + case CPU_R4000SC: + case CPU_R4000MC: + case CPU_R4400PC: + case CPU_R4400SC: + case CPU_R4400MC: + clear_c0_config(CONF_CU); + break; + } + +} + +void __init ld_mmu_r4xx0(void) +{ + extern char except_vec2_generic; + + /* Default cache error handler for R4000 and R5000 family */ + memcpy((void *)(KSEG0 + 0x100), &except_vec2_generic, 0x80); + memcpy((void *)(KSEG1 + 0x100), &except_vec2_generic, 0x80); + + probe_pcache(); + setup_scache(); + coherency_setup(); + + if (current_cpu_data.dcache.sets * + current_cpu_data.dcache.ways > PAGE_SIZE) + current_cpu_data.dcache.flags |= MIPS_CACHE_ALIASES; + + /* + * Some MIPS32 and MIPS64 processors have physically indexed caches. + * This code supports virtually indexed processors and will be + * unnecessarily unefficient on physically indexed processors. + */ + shm_align_mask = max_t(unsigned long, + current_cpu_data.dcache.sets * current_cpu_data.dcache.linesz - 1, + PAGE_SIZE - 1); + + flush_cache_all = r4k_flush_cache_all; + __flush_cache_all = r4k___flush_cache_all; + flush_cache_mm = r4k_flush_cache_mm; + flush_cache_page = r4k_flush_cache_page; + flush_icache_page = r4k_flush_icache_page; + flush_cache_range = r4k_flush_cache_range; + + flush_cache_sigtramp = r4k_flush_cache_sigtramp; + flush_icache_all = r4k_flush_icache_all; + flush_data_cache_page = r4k_flush_data_cache_page; + flush_icache_range = r4k_flush_icache_range; + +#ifdef CONFIG_NONCOHERENT_IO + _dma_cache_wback_inv = r4k_dma_cache_wback_inv; + _dma_cache_wback = r4k_dma_cache_wback_inv; + _dma_cache_inv = r4k_dma_cache_inv; +#endif + + __flush_cache_all(); +} diff --git a/arch/mips/mm/c-sb1.c b/arch/mips/mm/c-sb1.c new file mode 100644 index 000000000000..c8f87e12483d --- /dev/null +++ b/arch/mips/mm/c-sb1.c @@ -0,0 +1,611 @@ +/* + * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * Copyright (C) 1997, 2001 Ralf Baechle (ralf@gnu.org) + * Copyright (C) 2000, 2001 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include <linux/config.h> +#include <linux/init.h> +#include <asm/mmu_context.h> +#include <asm/bootinfo.h> +#include <asm/cacheops.h> +#include <asm/cpu.h> +#include <asm/uaccess.h> + +extern void sb1_clear_page(void * page); +extern void sb1_copy_page(void * to, void * from); + +/* These are probed at ld_mmu time */ +static unsigned long icache_size; +static unsigned long dcache_size; + +static unsigned long icache_line_size; +static unsigned long dcache_line_size; + +static unsigned int icache_index_mask; +static unsigned int dcache_index_mask; + +static unsigned long icache_assoc; +static unsigned long dcache_assoc; + +static unsigned int icache_sets; +static unsigned int dcache_sets; + +static unsigned int icache_range_cutoff; +static unsigned int dcache_range_cutoff; + +void pgd_init(unsigned long page) +{ + unsigned long *p = (unsigned long *) page; + int i; + + for (i = 0; i < USER_PTRS_PER_PGD; i+=8) { + p[i + 0] = (unsigned long) invalid_pte_table; + p[i + 1] = (unsigned long) invalid_pte_table; + p[i + 2] = (unsigned long) invalid_pte_table; + p[i + 3] = (unsigned long) invalid_pte_table; + p[i + 4] = (unsigned long) invalid_pte_table; + p[i + 5] = (unsigned long) invalid_pte_table; + p[i + 6] = (unsigned long) invalid_pte_table; + p[i + 7] = (unsigned long) invalid_pte_table; + } +} + +/* + * The dcache is fully coherent to the system, with one + * big caveat: the instruction stream. In other words, + * if we miss in the icache, and have dirty data in the + * L1 dcache, then we'll go out to memory (or the L2) and + * get the not-as-recent data. + * + * So the only time we have to flush the dcache is when + * we're flushing the icache. Since the L2 is fully + * coherent to everything, including I/O, we never have + * to flush it + */ + +/* + * Writeback and invalidate the entire dcache + */ +static inline void __sb1_writeback_inv_dcache_all(void) +{ + __asm__ __volatile__ ( + ".set push \n" + ".set noreorder \n" + ".set noat \n" + ".set mips4 \n" + " move $1, $0 \n" /* Start at index 0 */ + "1: cache %2, 0($1) \n" /* Invalidate this index */ + " cache %2, (1<<13)($1)\n" /* Invalidate this index */ + " cache %2, (2<<13)($1)\n" /* Invalidate this index */ + " cache %2, (3<<13)($1)\n" /* Invalidate this index */ + " addiu %1, %1, -1 \n" /* Decrement loop count */ + " bnez %1, 1b \n" /* loop test */ + " addu $1, $1, %0 \n" /* Next address */ + ".set pop \n" + : + : "r" (dcache_line_size), "r" (dcache_sets), + "i" (Index_Writeback_Inv_D)); +} + +/* + * Writeback and invalidate a range of the dcache. The addresses are + * virtual, and since we're using index ops and bit 12 is part of both + * the virtual frame and physical index, we have to clear both sets + * (bit 12 set and cleared). + */ +static inline void __sb1_writeback_inv_dcache_range(unsigned long start, + unsigned long end) +{ + __asm__ __volatile__ ( + " .set push \n" + " .set noreorder \n" + " .set noat \n" + " .set mips4 \n" + " and $1, %0, %3 \n" /* mask non-index bits */ + "1: cache %4, (0<<13)($1) \n" /* Index-WB-inval this address */ + " cache %4, (1<<13)($1) \n" /* Index-WB-inval this address */ + " cache %4, (2<<13)($1) \n" /* Index-WB-inval this address */ + " cache %4, (3<<13)($1) \n" /* Index-WB-inval this address */ + " xori $1, $1, 1<<12 \n" /* flip bit 12 (va/pa alias) */ + " cache %4, (0<<13)($1) \n" /* Index-WB-inval this address */ + " cache %4, (1<<13)($1) \n" /* Index-WB-inval this address */ + " cache %4, (2<<13)($1) \n" /* Index-WB-inval this address */ + " cache %4, (3<<13)($1) \n" /* Index-WB-inval this address */ + " addu %0, %0, %2 \n" /* next line */ + " bne %0, %1, 1b \n" /* loop test */ + " and $1, %0, %3 \n" /* mask non-index bits */ + " sync \n" + " .set pop \n" + : + : "r" (start & ~(dcache_line_size - 1)), + "r" ((end + dcache_line_size - 1) & ~(dcache_line_size - 1)), + "r" (dcache_line_size), + "r" (dcache_index_mask), + "i" (Index_Writeback_Inv_D)); +} + +/* + * Writeback and invalidate a range of the dcache. With physical + * addresseses, we don't have to worry about possible bit 12 aliasing. + * XXXKW is it worth turning on KX and using hit ops with xkphys? + */ +static inline void __sb1_writeback_inv_dcache_phys_range(unsigned long start, + unsigned long end) +{ + __asm__ __volatile__ ( + " .set push \n" + " .set noreorder \n" + " .set noat \n" + " .set mips4 \n" + " and $1, %0, %3 \n" /* mask non-index bits */ + "1: cache %4, (0<<13)($1) \n" /* Index-WB-inval this address */ + " cache %4, (1<<13)($1) \n" /* Index-WB-inval this address */ + " cache %4, (2<<13)($1) \n" /* Index-WB-inval this address */ + " cache %4, (3<<13)($1) \n" /* Index-WB-inval this address */ + " addu %0, %0, %2 \n" /* next line */ + " bne %0, %1, 1b \n" /* loop test */ + " and $1, %0, %3 \n" /* mask non-index bits */ + " sync \n" + " .set pop \n" + : + : "r" (start & ~(dcache_line_size - 1)), + "r" ((end + dcache_line_size - 1) & ~(dcache_line_size - 1)), + "r" (dcache_line_size), + "r" (dcache_index_mask), + "i" (Index_Writeback_Inv_D)); +} + + +/* + * Invalidate the entire icache + */ +static inline void __sb1_flush_icache_all(void) +{ + __asm__ __volatile__ ( + ".set push \n" + ".set noreorder \n" + ".set noat \n" + ".set mips4 \n" + " move $1, $0 \n" /* Start at index 0 */ + "1: cache %2, 0($1) \n" /* Invalidate this index */ + " cache %2, (1<<13)($1)\n" /* Invalidate this index */ + " cache %2, (2<<13)($1)\n" /* Invalidate this index */ + " cache %2, (3<<13)($1)\n" /* Invalidate this index */ + " addiu %1, %1, -1 \n" /* Decrement loop count */ + " bnez %1, 1b \n" /* loop test */ + " addu $1, $1, %0 \n" /* Next address */ + " bnezl $0, 2f \n" /* Force mispredict */ + " nop \n" + "2: sync \n" + ".set pop \n" + : + : "r" (icache_line_size), "r" (icache_sets), + "i" (Index_Invalidate_I)); +} + +/* + * Flush the icache for a given physical page. Need to writeback the + * dcache first, then invalidate the icache. If the page isn't + * executable, nothing is required. + */ +static void local_sb1_flush_cache_page(struct vm_area_struct *vma, + unsigned long addr) +{ + int cpu = smp_processor_id(); + +#ifndef CONFIG_SMP + if (!(vma->vm_flags & VM_EXEC)) + return; +#endif + + __sb1_writeback_inv_dcache_range(addr, addr + PAGE_SIZE); + + /* + * Bumping the ASID is probably cheaper than the flush ... + */ + if (cpu_context(cpu, vma->vm_mm) != 0) + drop_mmu_context(vma->vm_mm, cpu); +} + +#ifdef CONFIG_SMP +struct flush_cache_page_args { + struct vm_area_struct *vma; + unsigned long addr; +}; + +static void sb1_flush_cache_page_ipi(void *info) +{ + struct flush_cache_page_args *args = info; + + local_sb1_flush_cache_page(args->vma, args->addr); +} + +/* Dirty dcache could be on another CPU, so do the IPIs */ +static void sb1_flush_cache_page(struct vm_area_struct *vma, unsigned long addr) +{ + struct flush_cache_page_args args; + + if (!(vma->vm_flags & VM_EXEC)) + return; + + args.vma = vma; + args.addr = addr; + on_each_cpu(sb1_flush_cache_page_ipi, (void *) &args, 1, 1); +} +#else +void sb1_flush_cache_page(struct vm_area_struct *vma, unsigned long addr); +asm("sb1_flush_cache_page = local_sb1_flush_cache_page"); +#endif + +/* + * Invalidate a range of the icache. The addresses are virtual, and + * the cache is virtually indexed and tagged. However, we don't + * necessarily have the right ASID context, so use index ops instead + * of hit ops. + */ +static inline void __sb1_flush_icache_range(unsigned long start, + unsigned long end) +{ + __asm__ __volatile__ ( + ".set push \n" + ".set noreorder \n" + ".set noat \n" + ".set mips4 \n" + " and $1, %0, %3 \n" /* mask non-index bits */ + "1: cache %4, (0<<13)($1) \n" /* Index-inval this address */ + " cache %4, (1<<13)($1) \n" /* Index-inval this address */ + " cache %4, (2<<13)($1) \n" /* Index-inval this address */ + " cache %4, (3<<13)($1) \n" /* Index-inval this address */ + " addu %0, %0, %2 \n" /* next line */ + " bne %0, %1, 1b \n" /* loop test */ + " and $1, %0, %3 \n" /* mask non-index bits */ + " bnezl $0, 2f \n" /* Force mispredict */ + " nop \n" + "2: sync \n" + ".set pop \n" + : + : "r" (start & ~(icache_line_size - 1)), + "r" ((end + icache_line_size - 1) & ~(icache_line_size - 1)), + "r" (icache_line_size), + "r" (icache_index_mask), + "i" (Index_Invalidate_I)); +} + + +/* + * Invalidate all caches on this CPU + */ +static void local_sb1___flush_cache_all(void) +{ + __sb1_writeback_inv_dcache_all(); + __sb1_flush_icache_all(); +} + +#ifdef CONFIG_SMP +extern void sb1___flush_cache_all_ipi(void *ignored); +asm("sb1___flush_cache_all_ipi = local_sb1___flush_cache_all"); + +static void sb1___flush_cache_all(void) +{ + on_each_cpu(sb1___flush_cache_all_ipi, 0, 1, 1); +} +#else +extern void sb1___flush_cache_all(void); +asm("sb1___flush_cache_all = local_sb1___flush_cache_all"); +#endif + +/* + * When flushing a range in the icache, we have to first writeback + * the dcache for the same range, so new ifetches will see any + * data that was dirty in the dcache. + * + * The start/end arguments are Kseg addresses (possibly mapped Kseg). + */ + +static void local_sb1_flush_icache_range(unsigned long start, + unsigned long end) +{ + /* Just wb-inv the whole dcache if the range is big enough */ + if ((end - start) > dcache_range_cutoff) + __sb1_writeback_inv_dcache_all(); + else + __sb1_writeback_inv_dcache_range(start, end); + + /* Just flush the whole icache if the range is big enough */ + if ((end - start) > icache_range_cutoff) + __sb1_flush_icache_all(); + else + __sb1_flush_icache_range(start, end); +} + +#ifdef CONFIG_SMP +struct flush_icache_range_args { + unsigned long start; + unsigned long end; +}; + +static void sb1_flush_icache_range_ipi(void *info) +{ + struct flush_icache_range_args *args = info; + + local_sb1_flush_icache_range(args->start, args->end); +} + +void sb1_flush_icache_range(unsigned long start, unsigned long end) +{ + struct flush_icache_range_args args; + + args.start = start; + args.end = end; + on_each_cpu(sb1_flush_icache_range_ipi, &args, 1, 1); +} +#else +void sb1_flush_icache_range(unsigned long start, unsigned long end); +asm("sb1_flush_icache_range = local_sb1_flush_icache_range"); +#endif + +/* + * Flush the icache for a given physical page. Need to writeback the + * dcache first, then invalidate the icache. If the page isn't + * executable, nothing is required. + */ +static void local_sb1_flush_icache_page(struct vm_area_struct *vma, + struct page *page) +{ + unsigned long start; + int cpu = smp_processor_id(); + +#ifndef CONFIG_SMP + if (!(vma->vm_flags & VM_EXEC)) + return; +#endif + + /* Need to writeback any dirty data for that page, we have the PA */ + start = (unsigned long)(page-mem_map) << PAGE_SHIFT; + __sb1_writeback_inv_dcache_phys_range(start, start + PAGE_SIZE); + /* + * If there's a context, bump the ASID (cheaper than a flush, + * since we don't know VAs!) + */ + if (cpu_context(cpu, vma->vm_mm) != 0) { + drop_mmu_context(vma->vm_mm, cpu); + } +} + +#ifdef CONFIG_SMP +struct flush_icache_page_args { + struct vm_area_struct *vma; + struct page *page; +}; + +static void sb1_flush_icache_page_ipi(void *info) +{ + struct flush_icache_page_args *args = info; + local_sb1_flush_icache_page(args->vma, args->page); +} + +/* Dirty dcache could be on another CPU, so do the IPIs */ +static void sb1_flush_icache_page(struct vm_area_struct *vma, + struct page *page) +{ + struct flush_icache_page_args args; + + if (!(vma->vm_flags & VM_EXEC)) + return; + args.vma = vma; + args.page = page; + on_each_cpu(sb1_flush_icache_page_ipi, (void *) &args, 1, 1); +} +#else +void sb1_flush_icache_page(struct vm_area_struct *vma, struct page *page); +asm("sb1_flush_icache_page = local_sb1_flush_icache_page"); +#endif + +/* + * A signal trampoline must fit into a single cacheline. + */ +static void local_sb1_flush_cache_sigtramp(unsigned long addr) +{ + __asm__ __volatile__ ( + " .set push \n" + " .set noreorder \n" + " .set noat \n" + " .set mips4 \n" + " cache %2, (0<<13)(%0) \n" /* Index-inval this address */ + " cache %2, (1<<13)(%0) \n" /* Index-inval this address */ + " cache %2, (2<<13)(%0) \n" /* Index-inval this address */ + " cache %2, (3<<13)(%0) \n" /* Index-inval this address */ + " xori $1, %0, 1<<12 \n" /* Flip index bit 12 */ + " cache %2, (0<<13)($1) \n" /* Index-inval this address */ + " cache %2, (1<<13)($1) \n" /* Index-inval this address */ + " cache %2, (2<<13)($1) \n" /* Index-inval this address */ + " cache %2, (3<<13)($1) \n" /* Index-inval this address */ + " cache %3, (0<<13)(%1) \n" /* Index-inval this address */ + " cache %3, (1<<13)(%1) \n" /* Index-inval this address */ + " cache %3, (2<<13)(%1) \n" /* Index-inval this address */ + " cache %3, (3<<13)(%1) \n" /* Index-inval this address */ + " bnezl $0, 1f \n" /* Force mispredict */ + " nop \n" + "1: \n" + " .set pop \n" + : + : "r" (addr & dcache_index_mask), "r" (addr & icache_index_mask), + "i" (Index_Writeback_Inv_D), "i" (Index_Invalidate_I)); +} + +#ifdef CONFIG_SMP +static void sb1_flush_cache_sigtramp_ipi(void *info) +{ + unsigned long iaddr = (unsigned long) info; + local_sb1_flush_cache_sigtramp(iaddr); +} + +static void sb1_flush_cache_sigtramp(unsigned long addr) +{ + on_each_cpu(sb1_flush_cache_sigtramp_ipi, (void *) addr, 1, 1); +} +#else +void sb1_flush_cache_sigtramp(unsigned long addr); +asm("sb1_flush_cache_sigtramp = local_sb1_flush_cache_sigtramp"); +#endif + + +/* + * Anything that just flushes dcache state can be ignored, as we're always + * coherent in dcache space. This is just a dummy function that all the + * nop'ed routines point to + */ +static void sb1_nop(void) +{ +} + +/* + * Cache set values (from the mips64 spec) + * 0 - 64 + * 1 - 128 + * 2 - 256 + * 3 - 512 + * 4 - 1024 + * 5 - 2048 + * 6 - 4096 + * 7 - Reserved + */ + +static unsigned int decode_cache_sets(unsigned int config_field) +{ + if (config_field == 7) { + /* JDCXXX - Find a graceful way to abort. */ + return 0; + } + return (1<<(config_field + 6)); +} + +/* + * Cache line size values (from the mips64 spec) + * 0 - No cache present. + * 1 - 4 bytes + * 2 - 8 bytes + * 3 - 16 bytes + * 4 - 32 bytes + * 5 - 64 bytes + * 6 - 128 bytes + * 7 - Reserved + */ + +static unsigned int decode_cache_line_size(unsigned int config_field) +{ + if (config_field == 0) { + return 0; + } else if (config_field == 7) { + /* JDCXXX - Find a graceful way to abort. */ + return 0; + } + return (1<<(config_field + 1)); +} + +/* + * Relevant bits of the config1 register format (from the MIPS32/MIPS64 specs) + * + * 24:22 Icache sets per way + * 21:19 Icache line size + * 18:16 Icache Associativity + * 15:13 Dcache sets per way + * 12:10 Dcache line size + * 9:7 Dcache Associativity + */ + +static __init void probe_cache_sizes(void) +{ + u32 config1; + + config1 = read_c0_config1(); + icache_line_size = decode_cache_line_size((config1 >> 19) & 0x7); + dcache_line_size = decode_cache_line_size((config1 >> 10) & 0x7); + icache_sets = decode_cache_sets((config1 >> 22) & 0x7); + dcache_sets = decode_cache_sets((config1 >> 13) & 0x7); + icache_assoc = ((config1 >> 16) & 0x7) + 1; + dcache_assoc = ((config1 >> 7) & 0x7) + 1; + icache_size = icache_line_size * icache_sets * icache_assoc; + dcache_size = dcache_line_size * dcache_sets * dcache_assoc; + /* Need to remove non-index bits for index ops */ + icache_index_mask = (icache_sets - 1) * icache_line_size; + dcache_index_mask = (dcache_sets - 1) * dcache_line_size; + /* + * These are for choosing range (index ops) versus all. + * icache flushes all ways for each set, so drop icache_assoc. + * dcache flushes all ways and each setting of bit 12 for each + * index, so drop dcache_assoc and halve the dcache_sets. + */ + icache_range_cutoff = icache_sets * icache_line_size; + dcache_range_cutoff = (dcache_sets / 2) * icache_line_size; +} + +/* + * This is called from loadmmu.c. We have to set up all the + * memory management function pointers, as well as initialize + * the caches and tlbs + */ +void ld_mmu_sb1(void) +{ + extern char except_vec2_sb1; + unsigned long temp; + + /* Special cache error handler for SB1 */ + memcpy((void *)(KSEG0 + 0x100), &except_vec2_sb1, 0x80); + memcpy((void *)(KSEG1 + 0x100), &except_vec2_sb1, 0x80); + + probe_cache_sizes(); + + _clear_page = sb1_clear_page; + _copy_page = sb1_copy_page; + + /* + * None of these are needed for the SB1 - the Dcache is + * physically indexed and tagged, so no virtual aliasing can + * occur + */ + flush_cache_range = (void *) sb1_nop; + flush_cache_page = sb1_flush_cache_page; + flush_cache_mm = (void (*)(struct mm_struct *))sb1_nop; + flush_cache_all = sb1_nop; + + /* These routines are for Icache coherence with the Dcache */ + flush_icache_range = sb1_flush_icache_range; + flush_icache_page = sb1_flush_icache_page; + flush_icache_all = __sb1_flush_icache_all; /* local only */ + + flush_cache_sigtramp = sb1_flush_cache_sigtramp; + flush_data_cache_page = (void *) sb1_nop; + + /* Full flush */ + __flush_cache_all = sb1___flush_cache_all; + + change_c0_config(CONF_CM_CMASK, CONF_CM_DEFAULT); + /* + * This is the only way to force the update of K0 to complete + * before subsequent instruction fetch. + */ + __asm__ __volatile__ ( + " .set push \n" + " .set mips4 \n" + " la %0, 1f \n" + " mtc0 %0, $14 \n" + " eret \n" + "1: .set pop \n" + : "=r" (temp)); + flush_cache_all(); +} diff --git a/arch/mips/mm/c-tx39.c b/arch/mips/mm/c-tx39.c new file mode 100644 index 000000000000..98e94d8b604b --- /dev/null +++ b/arch/mips/mm/c-tx39.c @@ -0,0 +1,495 @@ +/* + * r2300.c: R2000 and R3000 specific mmu/cache code. + * + * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * + * with a lot of changes to make this thing work for R3000s + * Tx39XX R4k style caches added. HK + * Copyright (C) 1998, 1999, 2000 Harald Koerfgen + * Copyright (C) 1998 Gleb Raiko & Vladimir Roganov + */ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/mm.h> + +#include <asm/cacheops.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/mmu_context.h> +#include <asm/system.h> +#include <asm/isadep.h> +#include <asm/io.h> +#include <asm/bootinfo.h> +#include <asm/cpu.h> + +/* For R3000 cores with R4000 style caches */ +static unsigned long icache_size, dcache_size; /* Size in bytes */ +static unsigned long icache_way_size, dcache_way_size; /* Size divided by ways */ +#define scache_size 0 +#define scache_way_size 0 + +#include <asm/r4kcache.h> + +extern void r3k_clear_page(void * page); +extern void r3k_copy_page(void * to, void * from); + +extern int r3k_have_wired_reg; /* in r3k-tlb.c */ + +/* This sequence is required to ensure icache is disabled immediately */ +#define TX39_STOP_STREAMING() \ +__asm__ __volatile__( \ + ".set push\n\t" \ + ".set noreorder\n\t" \ + "b 1f\n\t" \ + "nop\n\t" \ + "1:\n\t" \ + ".set pop" \ + ) + +/* TX39H-style cache flush routines. */ +static void tx39h_flush_icache_all(void) +{ + unsigned long start = KSEG0; + unsigned long end = (start + icache_size); + unsigned long flags, config; + + /* disable icache (set ICE#) */ + local_irq_save(flags); + config = read_c0_conf(); + write_c0_conf(config & ~TX39_CONF_ICE); + TX39_STOP_STREAMING(); + + /* invalidate icache */ + while (start < end) { + cache16_unroll32(start, Index_Invalidate_I); + start += 0x200; + } + + write_c0_conf(config); + local_irq_restore(flags); +} + +static void tx39h_dma_cache_wback_inv(unsigned long addr, unsigned long size) +{ + unsigned long end, a; + unsigned long dc_lsize = current_cpu_data.dcache.linesz; + + iob(); + a = addr & ~(dc_lsize - 1); + end = (addr + size - 1) & ~(dc_lsize - 1); + while (1) { + invalidate_dcache_line(a); /* Hit_Invalidate_D */ + if (a == end) break; + a += dc_lsize; + } +} + + +/* TX39H2,TX39H3 */ +static inline void tx39_blast_dcache_page(unsigned long addr) +{ + if (current_cpu_data.cputype != CPU_TX3912) + blast_dcache16_page(addr); +} + +static inline void tx39_blast_dcache_page_indexed(unsigned long addr) +{ + blast_dcache16_page_indexed(addr); +} + +static inline void tx39_blast_dcache(void) +{ + blast_dcache16(); +} + +static inline void tx39_blast_icache_page(unsigned long addr) +{ + unsigned long flags, config; + /* disable icache (set ICE#) */ + local_irq_save(flags); + config = read_c0_conf(); + write_c0_conf(config & ~TX39_CONF_ICE); + TX39_STOP_STREAMING(); + blast_icache16_page(addr); + write_c0_conf(config); + local_irq_restore(flags); +} + +static inline void tx39_blast_icache_page_indexed(unsigned long addr) +{ + unsigned long flags, config; + /* disable icache (set ICE#) */ + local_irq_save(flags); + config = read_c0_conf(); + write_c0_conf(config & ~TX39_CONF_ICE); + TX39_STOP_STREAMING(); + blast_icache16_page_indexed(addr); + write_c0_conf(config); + local_irq_restore(flags); +} + +static inline void tx39_blast_icache(void) +{ + unsigned long flags, config; + /* disable icache (set ICE#) */ + local_irq_save(flags); + config = read_c0_conf(); + write_c0_conf(config & ~TX39_CONF_ICE); + TX39_STOP_STREAMING(); + blast_icache16(); + write_c0_conf(config); + local_irq_restore(flags); +} + +static inline void tx39_flush_cache_all(void) +{ + if (!cpu_has_dc_aliases) + return; + + tx39_blast_dcache(); + tx39_blast_icache(); +} + +static inline void tx39___flush_cache_all(void) +{ + tx39_blast_dcache(); + tx39_blast_icache(); +} + +static void tx39_flush_cache_mm(struct mm_struct *mm) +{ + if (!cpu_has_dc_aliases) + return; + + if (cpu_context(smp_processor_id(), mm) != 0) { + tx39_flush_cache_all(); + } +} + +static void tx39_flush_cache_range(struct vm_area_struct *vma, + unsigned long start, unsigned long end) +{ + struct mm_struct *mm = vma->vm_mm; + + if (!cpu_has_dc_aliases) + return; + + if (cpu_context(smp_processor_id(), mm) != 0) { + tx39_blast_dcache(); + tx39_blast_icache(); + } +} + +static void tx39_flush_cache_page(struct vm_area_struct *vma, + unsigned long page) +{ + int exec = vma->vm_flags & VM_EXEC; + struct mm_struct *mm = vma->vm_mm; + pgd_t *pgdp; + pmd_t *pmdp; + pte_t *ptep; + + /* + * If ownes no valid ASID yet, cannot possibly have gotten + * this page into the cache. + */ + if (cpu_context(smp_processor_id(), mm) == 0) + return; + + page &= PAGE_MASK; + pgdp = pgd_offset(mm, page); + pmdp = pmd_offset(pgdp, page); + ptep = pte_offset(pmdp, page); + + /* + * If the page isn't marked valid, the page cannot possibly be + * in the cache. + */ + if (!(pte_val(*ptep) & _PAGE_PRESENT)) + return; + + /* + * Doing flushes for another ASID than the current one is + * too difficult since stupid R4k caches do a TLB translation + * for every cache flush operation. So we do indexed flushes + * in that case, which doesn't overly flush the cache too much. + */ + if ((mm == current->active_mm) && (pte_val(*ptep) & _PAGE_VALID)) { + if (cpu_has_dc_aliases || exec) + tx39_blast_dcache_page(page); + if (exec) + tx39_blast_icache_page(page); + + return; + } + + /* + * Do indexed flush, too much work to get the (possible) TLB refills + * to work correctly. + */ + page = (KSEG0 + (page & (dcache_size - 1))); + if (cpu_has_dc_aliases || exec) + tx39_blast_dcache_page_indexed(page); + if (exec) + tx39_blast_icache_page_indexed(page); +} + +static void tx39_flush_data_cache_page(unsigned long addr) +{ + tx39_blast_dcache_page(addr); +} + +static void tx39_flush_icache_range(unsigned long start, unsigned long end) +{ + unsigned long dc_lsize = current_cpu_data.dcache.linesz; + unsigned long addr, aend; + + if (end - start > dcache_size) + tx39_blast_dcache(); + else { + addr = start & ~(dc_lsize - 1); + aend = (end - 1) & ~(dc_lsize - 1); + + while (1) { + /* Hit_Writeback_Inv_D */ + protected_writeback_dcache_line(addr); + if (addr == aend) + break; + addr += dc_lsize; + } + } + + if (end - start > icache_size) + tx39_blast_icache(); + else { + unsigned long flags, config; + addr = start & ~(dc_lsize - 1); + aend = (end - 1) & ~(dc_lsize - 1); + /* disable icache (set ICE#) */ + local_irq_save(flags); + config = read_c0_conf(); + write_c0_conf(config & ~TX39_CONF_ICE); + TX39_STOP_STREAMING(); + while (1) { + /* Hit_Invalidate_I */ + protected_flush_icache_line(addr); + if (addr == aend) + break; + addr += dc_lsize; + } + write_c0_conf(config); + local_irq_restore(flags); + } +} + +/* + * Ok, this seriously sucks. We use them to flush a user page but don't + * know the virtual address, so we have to blast away the whole icache + * which is significantly more expensive than the real thing. Otoh we at + * least know the kernel address of the page so we can flush it + * selectivly. + */ +static void tx39_flush_icache_page(struct vm_area_struct *vma, struct page *page) +{ + unsigned long addr; + /* + * If there's no context yet, or the page isn't executable, no icache + * flush is needed. + */ + if (!(vma->vm_flags & VM_EXEC)) + return; + + addr = (unsigned long) page_address(page); + tx39_blast_dcache_page(addr); + + /* + * We're not sure of the virtual address(es) involved here, so + * we have to flush the entire I-cache. + */ + tx39_blast_icache(); +} + +static void tx39_dma_cache_wback_inv(unsigned long addr, unsigned long size) +{ + unsigned long end, a; + + if (((size | addr) & (PAGE_SIZE - 1)) == 0) { + end = addr + size; + do { + tx39_blast_dcache_page(addr); + addr += PAGE_SIZE; + } while(addr != end); + } else if (size > dcache_size) { + tx39_blast_dcache(); + } else { + unsigned long dc_lsize = current_cpu_data.dcache.linesz; + a = addr & ~(dc_lsize - 1); + end = (addr + size - 1) & ~(dc_lsize - 1); + while (1) { + flush_dcache_line(a); /* Hit_Writeback_Inv_D */ + if (a == end) break; + a += dc_lsize; + } + } +} + +static void tx39_dma_cache_inv(unsigned long addr, unsigned long size) +{ + unsigned long end, a; + + if (((size | addr) & (PAGE_SIZE - 1)) == 0) { + end = addr + size; + do { + tx39_blast_dcache_page(addr); + addr += PAGE_SIZE; + } while(addr != end); + } else if (size > dcache_size) { + tx39_blast_dcache(); + } else { + unsigned long dc_lsize = current_cpu_data.dcache.linesz; + a = addr & ~(dc_lsize - 1); + end = (addr + size - 1) & ~(dc_lsize - 1); + while (1) { + invalidate_dcache_line(a); /* Hit_Invalidate_D */ + if (a == end) break; + a += dc_lsize; + } + } +} + +static void tx39_flush_cache_sigtramp(unsigned long addr) +{ + unsigned long ic_lsize = current_cpu_data.icache.linesz; + unsigned long dc_lsize = current_cpu_data.dcache.linesz; + unsigned long config; + unsigned long flags; + + protected_writeback_dcache_line(addr & ~(dc_lsize - 1)); + + /* disable icache (set ICE#) */ + local_irq_save(flags); + config = read_c0_conf(); + write_c0_conf(config & ~TX39_CONF_ICE); + TX39_STOP_STREAMING(); + protected_flush_icache_line(addr & ~(ic_lsize - 1)); + write_c0_conf(config); + local_irq_restore(flags); +} + +static __init void tx39_probe_cache(void) +{ + unsigned long config; + + config = read_c0_conf(); + + icache_size = 1 << (10 + ((config & TX39_CONF_ICS_MASK) >> + TX39_CONF_ICS_SHIFT)); + dcache_size = 1 << (10 + ((config & TX39_CONF_DCS_MASK) >> + TX39_CONF_DCS_SHIFT)); + + current_cpu_data.icache.linesz = 16; + switch (current_cpu_data.cputype) { + case CPU_TX3912: + current_cpu_data.icache.ways = 1; + current_cpu_data.dcache.ways = 1; + current_cpu_data.dcache.linesz = 4; + break; + + case CPU_TX3927: + current_cpu_data.icache.ways = 2; + current_cpu_data.dcache.ways = 2; + current_cpu_data.dcache.linesz = 16; + break; + + case CPU_TX3922: + default: + current_cpu_data.icache.ways = 1; + current_cpu_data.dcache.ways = 1; + current_cpu_data.dcache.linesz = 16; + break; + } +} + +void __init ld_mmu_tx39(void) +{ + unsigned long config; + + _clear_page = r3k_clear_page; + _copy_page = r3k_copy_page; + + config = read_c0_conf(); + config &= ~TX39_CONF_WBON; + write_c0_conf(config); + + tx39_probe_cache(); + + switch (current_cpu_data.cputype) { + case CPU_TX3912: + /* TX39/H core (writethru direct-map cache) */ + flush_cache_all = tx39h_flush_icache_all; + __flush_cache_all = tx39h_flush_icache_all; + flush_cache_mm = (void *) tx39h_flush_icache_all; + flush_cache_range = (void *) tx39h_flush_icache_all; + flush_cache_page = (void *) tx39h_flush_icache_all; + flush_icache_page = (void *) tx39h_flush_icache_all; + flush_icache_range = (void *) tx39h_flush_icache_all; + + flush_cache_sigtramp = (void *) tx39h_flush_icache_all; + flush_data_cache_page = (void *) tx39h_flush_icache_all; + + _dma_cache_wback_inv = tx39h_dma_cache_wback_inv; + + shm_align_mask = PAGE_SIZE - 1; + + break; + + case CPU_TX3922: + case CPU_TX3927: + default: + /* TX39/H2,H3 core (writeback 2way-set-associative cache) */ + r3k_have_wired_reg = 1; + write_c0_wired(0); /* set 8 on reset... */ + /* board-dependent init code may set WBON */ + + flush_cache_all = tx39_flush_cache_all; + __flush_cache_all = tx39___flush_cache_all; + flush_cache_mm = tx39_flush_cache_mm; + flush_cache_range = tx39_flush_cache_range; + flush_cache_page = tx39_flush_cache_page; + flush_icache_page = tx39_flush_icache_page; + flush_icache_range = tx39_flush_icache_range; + + flush_cache_sigtramp = tx39_flush_cache_sigtramp; + flush_data_cache_page = tx39_flush_data_cache_page; + + _dma_cache_wback_inv = tx39_dma_cache_wback_inv; + _dma_cache_wback = tx39_dma_cache_wback_inv; + _dma_cache_inv = tx39_dma_cache_inv; + + shm_align_mask = max_t(unsigned long, + (dcache_size / current_cpu_data.dcache.ways) - 1, + PAGE_SIZE - 1); + + break; + } + + icache_way_size = icache_size / current_cpu_data.icache.ways; + dcache_way_size = dcache_size / current_cpu_data.dcache.ways; + + current_cpu_data.icache.sets = + icache_way_size / current_cpu_data.icache.linesz; + current_cpu_data.dcache.sets = + dcache_way_size / current_cpu_data.dcache.linesz; + + if (dcache_way_size > PAGE_SIZE) + current_cpu_data.dcache.flags |= MIPS_CACHE_ALIASES; + + current_cpu_data.icache.waybit = 0; + current_cpu_data.dcache.waybit = 0; + + printk("Primary instruction cache %ldkb, linesize %d bytes\n", + icache_size >> 10, current_cpu_data.icache.linesz); + printk("Primary data cache %ldkb, linesize %d bytes\n", + dcache_size >> 10, current_cpu_data.dcache.linesz); +} diff --git a/arch/mips/mm/cache.c b/arch/mips/mm/cache.c new file mode 100644 index 000000000000..04ab866d6b21 --- /dev/null +++ b/arch/mips/mm/cache.c @@ -0,0 +1,60 @@ +/* + * 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 - 2003 by Ralf Baechle + */ +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/mm.h> + +#include <asm/cacheflush.h> + +asmlinkage int sys_cacheflush(void *addr, int bytes, int cache) +{ + /* This should flush more selectivly ... */ + __flush_cache_all(); + + return 0; +} + +void flush_dcache_page(struct page *page) +{ + unsigned long addr; + + if (page->mapping && + list_empty(&page->mapping->i_mmap) && + list_empty(&page->mapping->i_mmap_shared)) { + SetPageDcacheDirty(page); + + return; + } + + /* + * We could delay the flush for the !page->mapping case too. But that + * case is for exec env/arg pages and those are %99 certainly going to + * get faulted into the tlb (and thus flushed) anyways. + */ + addr = (unsigned long) page_address(page); + flush_data_cache_page(addr); +} + +void __update_cache(struct vm_area_struct *vma, unsigned long address, + pte_t pte) +{ + struct page *page; + unsigned long pfn, addr; + + pfn = pte_pfn(pte); + if (pfn_valid(pfn) && (page = pfn_to_page(pfn), page->mapping) && + Page_dcache_dirty(page)) { + if (pages_do_alias((unsigned long)page_address(page), + address & PAGE_MASK)) { + addr = (unsigned long) page_address(page); + flush_data_cache_page(addr); + } + + ClearPageDcacheDirty(page); + } +} diff --git a/arch/mips/mm/cerr-sb1.c b/arch/mips/mm/cerr-sb1.c new file mode 100644 index 000000000000..d4f4a4bbd772 --- /dev/null +++ b/arch/mips/mm/cerr-sb1.c @@ -0,0 +1,542 @@ +/* + * Copyright (C) 2001 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/sched.h> +#include <asm/mipsregs.h> +#include <asm/sibyte/sb1250.h> + +#ifndef CONFIG_SIBYTE_BUS_WATCHER +#include <asm/io.h> +#include <asm/sibyte/sb1250_regs.h> +#include <asm/sibyte/sb1250_scd.h> +#include <asm/sibyte/64bit.h> +#endif + +/* SB1 definitions */ + +/* XXX should come from config1 XXX */ +#define SB1_CACHE_INDEX_MASK 0x1fe0 + +#define CP0_ERRCTL_RECOVERABLE (1 << 31) +#define CP0_ERRCTL_DCACHE (1 << 30) +#define CP0_ERRCTL_ICACHE (1 << 29) +#define CP0_ERRCTL_MULTIBUS (1 << 23) +#define CP0_ERRCTL_MC_TLB (1 << 15) +#define CP0_ERRCTL_MC_TIMEOUT (1 << 14) + +#define CP0_CERRI_TAG_PARITY (1 << 29) +#define CP0_CERRI_DATA_PARITY (1 << 28) +#define CP0_CERRI_EXTERNAL (1 << 26) + +#define CP0_CERRI_IDX_VALID(c) (!((c) & CP0_CERRI_EXTERNAL)) +#define CP0_CERRI_DATA (CP0_CERRI_DATA_PARITY) + +#define CP0_CERRD_MULTIPLE (1 << 31) +#define CP0_CERRD_TAG_STATE (1 << 30) +#define CP0_CERRD_TAG_ADDRESS (1 << 29) +#define CP0_CERRD_DATA_SBE (1 << 28) +#define CP0_CERRD_DATA_DBE (1 << 27) +#define CP0_CERRD_EXTERNAL (1 << 26) +#define CP0_CERRD_LOAD (1 << 25) +#define CP0_CERRD_STORE (1 << 24) +#define CP0_CERRD_FILLWB (1 << 23) +#define CP0_CERRD_COHERENCY (1 << 22) +#define CP0_CERRD_DUPTAG (1 << 21) + +#define CP0_CERRD_DPA_VALID(c) (!((c) & CP0_CERRD_EXTERNAL)) +#define CP0_CERRD_IDX_VALID(c) \ + (((c) & (CP0_CERRD_LOAD | CP0_CERRD_STORE)) ? (!((c) & CP0_CERRD_EXTERNAL)) : 0) +#define CP0_CERRD_CAUSES \ + (CP0_CERRD_LOAD | CP0_CERRD_STORE | CP0_CERRD_FILLWB | CP0_CERRD_COHERENCY | CP0_CERRD_DUPTAG) +#define CP0_CERRD_TYPES \ + (CP0_CERRD_TAG_STATE | CP0_CERRD_TAG_ADDRESS | CP0_CERRD_DATA_SBE | CP0_CERRD_DATA_DBE | CP0_CERRD_EXTERNAL) +#define CP0_CERRD_DATA (CP0_CERRD_DATA_SBE | CP0_CERRD_DATA_DBE) + +static uint32_t extract_ic(unsigned short addr, int data); +static uint32_t extract_dc(unsigned short addr, int data); + +static inline void breakout_errctl(unsigned int val) +{ + if (val & CP0_ERRCTL_RECOVERABLE) + prom_printf(" recoverable"); + if (val & CP0_ERRCTL_DCACHE) + prom_printf(" dcache"); + if (val & CP0_ERRCTL_ICACHE) + prom_printf(" icache"); + if (val & CP0_ERRCTL_MULTIBUS) + prom_printf(" multiple-buserr"); + prom_printf("\n"); +} + +static inline void breakout_cerri(unsigned int val) +{ + if (val & CP0_CERRI_TAG_PARITY) + prom_printf(" tag-parity"); + if (val & CP0_CERRI_DATA_PARITY) + prom_printf(" data-parity"); + if (val & CP0_CERRI_EXTERNAL) + prom_printf(" external"); + prom_printf("\n"); +} + +static inline void breakout_cerrd(unsigned int val) +{ + switch (val & CP0_CERRD_CAUSES) { + case CP0_CERRD_LOAD: + prom_printf(" load,"); + break; + case CP0_CERRD_STORE: + prom_printf(" store,"); + break; + case CP0_CERRD_FILLWB: + prom_printf(" fill/wb,"); + break; + case CP0_CERRD_COHERENCY: + prom_printf(" coherency,"); + break; + case CP0_CERRD_DUPTAG: + prom_printf(" duptags,"); + break; + default: + prom_printf(" NO CAUSE,"); + break; + } + if (!(val & CP0_CERRD_TYPES)) + prom_printf(" NO TYPE"); + else { + if (val & CP0_CERRD_MULTIPLE) + prom_printf(" multi-err"); + if (val & CP0_CERRD_TAG_STATE) + prom_printf(" tag-state"); + if (val & CP0_CERRD_TAG_ADDRESS) + prom_printf(" tag-address"); + if (val & CP0_CERRD_DATA_SBE) + prom_printf(" data-SBE"); + if (val & CP0_CERRD_DATA_DBE) + prom_printf(" data-DBE"); + if (val & CP0_CERRD_EXTERNAL) + prom_printf(" external"); + } + prom_printf("\n"); +} + +#ifndef CONFIG_SIBYTE_BUS_WATCHER + +static void check_bus_watcher(void) +{ + uint32_t status, l2_err, memio_err; + + /* Destructive read, clears register and interrupt */ + status = csr_in32(IO_SPACE_BASE | A_SCD_BUS_ERR_STATUS); + /* Bit 31 is always on, but there's no #define for that */ + if (status & ~(1UL << 31)) { + l2_err = csr_in32(IO_SPACE_BASE | A_BUS_L2_ERRORS); + memio_err = csr_in32(IO_SPACE_BASE | A_BUS_MEM_IO_ERRORS); + prom_printf("Bus watcher error counters: %08x %08x\n", l2_err, memio_err); + prom_printf("\nLast recorded signature:\n"); + prom_printf("Request %02x from %d, answered by %d with Dcode %d\n", + (unsigned int)(G_SCD_BERR_TID(status) & 0x3f), + (int)(G_SCD_BERR_TID(status) >> 6), + (int)G_SCD_BERR_RID(status), + (int)G_SCD_BERR_DCODE(status)); + } else { + prom_printf("Bus watcher indicates no error\n"); + } +} +#else +extern void check_bus_watcher(void); +#endif + +asmlinkage void sb1_cache_error(void) +{ + uint64_t cerr_dpa; + uint32_t errctl, cerr_i, cerr_d, dpalo, dpahi, eepc, res; + + prom_printf("Cache error exception on CPU %x:\n", + (read_c0_prid() >> 25) & 0x7); + + __asm__ __volatile__ ( + " .set push\n\t" + " .set mips64\n\t" + " .set noat\n\t" + " mfc0 %0, $26\n\t" + " mfc0 %1, $27\n\t" + " mfc0 %2, $27, 1\n\t" + " dmfc0 $1, $27, 3\n\t" + " dsrl32 %3, $1, 0 \n\t" + " sll %4, $1, 0 \n\t" + " mfc0 %5, $30\n\t" + " .set pop" + : "=r" (errctl), "=r" (cerr_i), "=r" (cerr_d), + "=r" (dpahi), "=r" (dpalo), "=r" (eepc)); + + cerr_dpa = (((uint64_t)dpahi) << 32) | dpalo; + prom_printf(" cp0_errorepc == %08x\n", eepc); + prom_printf(" cp0_errctl == %08x", errctl); + breakout_errctl(errctl); + if (errctl & CP0_ERRCTL_ICACHE) { + prom_printf(" cp0_cerr_i == %08x", cerr_i); + breakout_cerri(cerr_i); + if (CP0_CERRI_IDX_VALID(cerr_i)) { + if ((eepc & SB1_CACHE_INDEX_MASK) != (cerr_i & SB1_CACHE_INDEX_MASK)) + prom_printf(" cerr_i idx doesn't match eepc\n"); + else { + res = extract_ic(cerr_i & SB1_CACHE_INDEX_MASK, + (cerr_i & CP0_CERRI_DATA) != 0); + if (!(res & cerr_i)) + prom_printf("...didn't see indicated icache problem\n"); + } + } + } + if (errctl & CP0_ERRCTL_DCACHE) { + prom_printf(" cp0_cerr_d == %08x", cerr_d); + breakout_cerrd(cerr_d); + if (CP0_CERRD_DPA_VALID(cerr_d)) { + prom_printf(" cp0_cerr_dpa == %010llx\n", cerr_dpa); + if (!CP0_CERRD_IDX_VALID(cerr_d)) { + res = extract_dc(cerr_dpa & SB1_CACHE_INDEX_MASK, + (cerr_d & CP0_CERRD_DATA) != 0); + if (!(res & cerr_d)) + prom_printf("...didn't see indicated dcache problem\n"); + } else { + if ((cerr_dpa & SB1_CACHE_INDEX_MASK) != (cerr_d & SB1_CACHE_INDEX_MASK)) + prom_printf(" cerr_d idx doesn't match cerr_dpa\n"); + else { + res = extract_dc(cerr_d & SB1_CACHE_INDEX_MASK, + (cerr_d & CP0_CERRD_DATA) != 0); + if (!(res & cerr_d)) + prom_printf("...didn't see indicated problem\n"); + } + } + } + } + + check_bus_watcher(); + + while (1); + /* + * This tends to make things get really ugly; let's just stall instead. + * panic("Can't handle the cache error!"); + */ +} + + +/* Parity lookup table. */ +static const uint8_t parity[256] = { + 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1, + 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0, + 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0, + 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1, + 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0, + 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1, + 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1, + 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0 +}; + +/* Masks to select bits for Hamming parity, mask_72_64[i] for bit[i] */ +static const uint64_t mask_72_64[8] = { + 0x0738C808099264FFL, + 0x38C808099264FF07L, + 0xC808099264FF0738L, + 0x08099264FF0738C8L, + 0x099264FF0738C808L, + 0x9264FF0738C80809L, + 0x64FF0738C8080992L, + 0xFF0738C808099264L +}; + +/* Calculate the parity on a range of bits */ +static char range_parity(uint64_t dword, int max, int min) +{ + char parity = 0; + int i; + dword >>= min; + for (i=max-min; i>=0; i--) { + if (dword & 0x1) + parity = !parity; + dword >>= 1; + } + return parity; +} + +/* Calculate the 4-bit even byte-parity for an instruction */ +static unsigned char inst_parity(uint32_t word) +{ + int i, j; + char parity = 0; + for (j=0; j<4; j++) { + char byte_parity = 0; + for (i=0; i<8; i++) { + if (word & 0x80000000) + byte_parity = !byte_parity; + word <<= 1; + } + parity <<= 1; + parity |= byte_parity; + } + return parity; +} + +static uint32_t extract_ic(unsigned short addr, int data) +{ + unsigned short way; + int valid; + uint64_t taglo, va, tlo_tmp; + uint32_t taghi, taglolo, taglohi; + uint8_t lru; + int res = 0; + + prom_printf("Icache index 0x%04x ", addr); + for (way = 0; way < 4; way++) { + /* Index-load-tag-I */ + __asm__ __volatile__ ( + " .set push \n\t" + " .set noreorder \n\t" + " .set mips64 \n\t" + " .set noat \n\t" + " cache 4, 0(%3) \n\t" + " mfc0 %0, $29 \n\t" + " dmfc0 $1, $28 \n\t" + " dsrl32 %1, $1, 0 \n\t" + " sll %2, $1, 0 \n\t" + " .set pop" + : "=r" (taghi), "=r" (taglohi), "=r" (taglolo) + : "r" ((way << 13) | addr)); + + taglo = ((unsigned long long)taglohi << 32) | taglolo; + if (way == 0) { + lru = (taghi >> 14) & 0xff; + prom_printf("[Bank %d Set 0x%02x] LRU > %d %d %d %d > MRU\n", + ((addr >> 5) & 0x3), /* bank */ + ((addr >> 7) & 0x3f), /* index */ + (lru & 0x3), + ((lru >> 2) & 0x3), + ((lru >> 4) & 0x3), + ((lru >> 6) & 0x3)); + } + va = (taglo & 0xC0000FFFFFFFE000) | addr; + if ((taglo & (1 << 31)) && (((taglo >> 62) & 0x3) == 3)) + va |= 0x3FFFF00000000000; + valid = ((taghi >> 29) & 1); + if (valid) { + tlo_tmp = taglo & 0xfff3ff; + if (((taglo >> 10) & 1) ^ range_parity(tlo_tmp, 23, 0)) { + prom_printf(" ** bad parity in VTag0/G/ASID\n"); + res |= CP0_CERRI_TAG_PARITY; + } + if (((taglo >> 11) & 1) ^ range_parity(taglo, 63, 24)) { + prom_printf(" ** bad parity in R/VTag1\n"); + res |= CP0_CERRI_TAG_PARITY; + } + } + if (valid ^ ((taghi >> 27) & 1)) { + prom_printf(" ** bad parity for valid bit\n"); + res |= CP0_CERRI_TAG_PARITY; + } + prom_printf(" %d [VA %016llx] [Vld? %d] raw tags: %08X-%016llX\n", + way, va, valid, taghi, taglo); + + if (data) { + uint32_t datahi, insta, instb; + uint8_t predecode; + int offset; + + /* (hit all banks and ways) */ + for (offset = 0; offset < 4; offset++) { + /* Index-load-data-I */ + __asm__ __volatile__ ( + " .set push\n\t" + " .set noreorder\n\t" + " .set mips64\n\t" + " .set noat\n\t" + " cache 6, 0(%3) \n\t" + " mfc0 %0, $29, 1\n\t" + " dmfc0 $1, $28, 1\n\t" + " dsrl32 %1, $1, 0 \n\t" + " sll %2, $1, 0 \n\t" + " .set pop \n" + : "=r" (datahi), "=r" (insta), "=r" (instb) + : "r" ((way << 13) | addr | (offset << 3))); + predecode = (datahi >> 8) & 0xff; + if (((datahi >> 16) & 1) != (uint32_t)range_parity(predecode, 7, 0)) { + prom_printf(" ** bad parity in predecode\n"); + res |= CP0_CERRI_DATA_PARITY; + } + /* XXXKW should/could check predecode bits themselves */ + if (((datahi >> 4) & 0xf) ^ inst_parity(insta)) { + prom_printf(" ** bad parity in instruction a\n"); + res |= CP0_CERRI_DATA_PARITY; + } + if ((datahi & 0xf) ^ inst_parity(instb)) { + prom_printf(" ** bad parity in instruction b\n"); + res |= CP0_CERRI_DATA_PARITY; + } + prom_printf(" %05X-%08X%08X", datahi, insta, instb); + } + prom_printf("\n"); + } + } + return res; +} + +/* Compute the ECC for a data doubleword */ +static uint8_t dc_ecc(uint64_t dword) +{ + uint64_t t; + uint32_t w; + uint8_t p; + int i; + + p = 0; + for (i = 7; i >= 0; i--) + { + p <<= 1; + t = dword & mask_72_64[i]; + w = (uint32_t)(t >> 32); + p ^= (parity[w>>24] ^ parity[(w>>16) & 0xFF] + ^ parity[(w>>8) & 0xFF] ^ parity[w & 0xFF]); + w = (uint32_t)(t & 0xFFFFFFFF); + p ^= (parity[w>>24] ^ parity[(w>>16) & 0xFF] + ^ parity[(w>>8) & 0xFF] ^ parity[w & 0xFF]); + } + return p; +} + +struct dc_state { + unsigned char val; + char *name; +}; + +static struct dc_state dc_states[] = { + { 0x00, "INVALID" }, + { 0x0f, "COH-SHD" }, + { 0x13, "NCO-E-C" }, + { 0x19, "NCO-E-D" }, + { 0x16, "COH-E-C" }, + { 0x1c, "COH-E-D" }, + { 0xff, "*ERROR*" } +}; + +#define DC_TAG_VALID(state) \ + (((state) == 0xf) || ((state) == 0x13) || ((state) == 0x19) || ((state == 0x16)) || ((state) == 0x1c)) + +static char *dc_state_str(unsigned char state) +{ + struct dc_state *dsc = dc_states; + while (dsc->val != 0xff) { + if (dsc->val == state) + break; + dsc++; + } + return dsc->name; +} + +static uint32_t extract_dc(unsigned short addr, int data) +{ + int valid, way; + unsigned char state; + uint64_t taglo, pa; + uint32_t taghi, taglolo, taglohi; + uint8_t ecc, lru; + int res = 0; + + prom_printf("Dcache index 0x%04x ", addr); + for (way = 0; way < 4; way++) { + __asm__ __volatile__ ( + " .set push\n\t" + " .set noreorder\n\t" + " .set mips64\n\t" + " .set noat\n\t" + " cache 5, 0(%3)\n\t" /* Index-load-tag-D */ + " mfc0 %0, $29, 2\n\t" + " dmfc0 $1, $28, 2\n\t" + " dsrl32 %1, $1, 0\n\t" + " sll %2, $1, 0\n\t" + " .set pop" + : "=r" (taghi), "=r" (taglohi), "=r" (taglolo) + : "r" ((way << 13) | addr)); + + taglo = ((unsigned long long)taglohi << 32) | taglolo; + pa = (taglo & 0xFFFFFFE000) | addr; + if (way == 0) { + lru = (taghi >> 14) & 0xff; + prom_printf("[Bank %d Set 0x%02x] LRU > %d %d %d %d > MRU\n", + ((addr >> 11) & 0x2) | ((addr >> 5) & 1), /* bank */ + ((addr >> 6) & 0x3f), /* index */ + (lru & 0x3), + ((lru >> 2) & 0x3), + ((lru >> 4) & 0x3), + ((lru >> 6) & 0x3)); + } + state = (taghi >> 25) & 0x1f; + valid = DC_TAG_VALID(state); + prom_printf(" %d [PA %010llx] [state %s (%02x)] raw tags: %08X-%016llX\n", + way, pa, dc_state_str(state), state, taghi, taglo); + if (valid) { + if (((taglo >> 11) & 1) ^ range_parity(taglo, 39, 26)) { + prom_printf(" ** bad parity in PTag1\n"); + res |= CP0_CERRD_TAG_ADDRESS; + } + if (((taglo >> 10) & 1) ^ range_parity(taglo, 25, 13)) { + prom_printf(" ** bad parity in PTag0\n"); + res |= CP0_CERRD_TAG_ADDRESS; + } + } else { + res |= CP0_CERRD_TAG_STATE; + } + + if (data) { + uint64_t datalo; + uint32_t datalohi, datalolo, datahi; + int offset; + + for (offset = 0; offset < 4; offset++) { + /* Index-load-data-D */ + __asm__ __volatile__ ( + " .set push\n\t" + " .set noreorder\n\t" + " .set mips64\n\t" + " .set noat\n\t" + " cache 7, 0(%3)\n\t" /* Index-load-data-D */ + " mfc0 %0, $29, 3\n\t" + " dmfc0 $1, $28, 3\n\t" + " dsrl32 %1, $1, 0 \n\t" + " sll %2, $1, 0 \n\t" + " .set pop" + : "=r" (datahi), "=r" (datalohi), "=r" (datalolo) + : "r" ((way << 13) | addr | (offset << 3))); + datalo = ((unsigned long long)datalohi << 32) | datalolo; + ecc = dc_ecc(datalo); + if (ecc != datahi) { + int bits = 0; + prom_printf(" ** bad ECC (%02x %02x) ->", + datahi, ecc); + ecc ^= datahi; + while (ecc) { + if (ecc & 1) bits++; + ecc >>= 1; + } + res |= (bits == 1) ? CP0_CERRD_DATA_SBE : CP0_CERRD_DATA_DBE; + } + prom_printf(" %02X-%016llX", datahi, datalo); + } + prom_printf("\n"); + } + } + return res; +} diff --git a/arch/mips/mm/cex-sb1.S b/arch/mips/mm/cex-sb1.S new file mode 100644 index 000000000000..58c8282763f6 --- /dev/null +++ b/arch/mips/mm/cex-sb1.S @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2001,2002,2003 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include <linux/config.h> +#include <linux/init.h> + +#include <asm/asm.h> +#include <asm/regdef.h> +#include <asm/mipsregs.h> +#include <asm/stackframe.h> +#include <asm/sibyte/board.h> + + .text + .set noat + .set mips4 + + __INIT + + /* Cache Error handler for SB1 */ + LEAF(except_vec2_sb1) + mfc0 k1, $26 + # check if error was recoverable + bltz k1, leave_cerr +#ifdef CONFIG_SB1_PASS_1_WORKAROUNDS + # look for signature of spurious CErr + lui k0, 0x4000 + bne k0, k1, 1f + .word 0x401Bd801 # mfc0 k1, $27, 1 + lui k0, 0xffe0 + and k1, k0, k1 + lui k0, 0x0200 + beq k0, k1, leave_cerr +1: +#endif + j handle_vec2_sb1 + +leave_cerr: + # clear/unlock the registers + mtc0 zero, $26 + mtc0 zero, $27 + .word 0x4080d801 # mtc0 zero, $27, 1 + .word 0x4080d803 # mtc0 zero, $27, 3 + eret + END(except_vec2_sb1) + + __FINIT + + LEAF(handle_vec2_sb1) + mfc0 k0,CP0_CONFIG + li k1,~CONF_CM_CMASK + and k0,k0,k1 + ori k0,k0,CONF_CM_UNCACHED + mtc0 k0,CP0_CONFIG + + SSNOP + SSNOP + SSNOP + SSNOP + bnezl $0, 1f +1: + mfc0 k0, CP0_STATUS + sll k0, k0, 3 # check CU0 (kernel?) + bltz k0, 2f + GET_SAVED_SP + move sp, k0 # want Kseg SP (so uncached) +2: + j sb1_cache_error + + END(handle_vec2_sb1) diff --git a/arch/mips/mm/extable.c b/arch/mips/mm/extable.c index 4c2cf3b43941..946e7ad6fbcb 100644 --- a/arch/mips/mm/extable.c +++ b/arch/mips/mm/extable.c @@ -1,62 +1,30 @@ /* - * linux/arch/mips/mm/extable.c + * linux/arch/i386/mm/extable.c */ + #include <linux/config.h> #include <linux/module.h> #include <linux/spinlock.h> #include <asm/uaccess.h> -extern const struct exception_table_entry __start___ex_table[]; -extern const struct exception_table_entry __stop___ex_table[]; - -static inline unsigned long -search_one_table(const struct exception_table_entry *first, - const struct exception_table_entry *last, - unsigned long value) +/* Simple binary search */ +const struct exception_table_entry * +search_extable(const struct exception_table_entry *first, + const struct exception_table_entry *last, + unsigned long value) { - while (first <= last) { + while (first <= last) { const struct exception_table_entry *mid; long diff; mid = (last - first) / 2 + first; diff = mid->insn - value; - if (diff == 0) - return mid->nextinsn; - else if (diff < 0) - first = mid+1; - else - last = mid-1; - } - return 0; -} - -extern spinlock_t modlist_lock; - -unsigned long -search_exception_table(unsigned long addr) -{ - unsigned long ret = 0; - -#ifndef CONFIG_MODULES - /* There is only the kernel to search. */ - ret = search_one_table(__start___ex_table, __stop___ex_table-1, addr); - return ret; -#else - unsigned long flags; - - /* The kernel is the last "module" -- no need to treat it special. */ - struct module *mp; - - spin_lock_irqsave(&modlist_lock, flags); - for (mp = module_list; mp != NULL; mp = mp->next) { - if (mp->ex_table_start == NULL || !(mp->flags&(MOD_RUNNING|MOD_INITIALIZING))) - continue; - ret = search_one_table(mp->ex_table_start, - mp->ex_table_end - 1, addr); - if (ret) - break; - } - spin_unlock_irqrestore(&modlist_lock, flags); - return ret; -#endif + if (diff == 0) + return mid; + else if (diff < 0) + first = mid+1; + else + last = mid-1; + } + return NULL; } diff --git a/arch/mips/mm/fault.c b/arch/mips/mm/fault.c index 8e648749ec78..38e2647248f3 100644 --- a/arch/mips/mm/fault.c +++ b/arch/mips/mm/fault.c @@ -5,6 +5,7 @@ * * Copyright (C) 1995 - 2000 by Ralf Baechle */ +#include <linux/config.h> #include <linux/signal.h> #include <linux/sched.h> #include <linux/interrupt.h> @@ -18,23 +19,50 @@ #include <linux/smp.h> #include <linux/smp_lock.h> #include <linux/version.h> +#include <linux/vt_kern.h> /* For unblank_screen() */ +#include <linux/module.h> +#include <asm/branch.h> #include <asm/hardirq.h> #include <asm/pgalloc.h> #include <asm/mmu_context.h> #include <asm/system.h> #include <asm/uaccess.h> +#include <asm/ptrace.h> #define development_version (LINUX_VERSION_CODE & 0x100) -unsigned long asid_cache = ASID_FIRST_VERSION; - /* * Macro for exception fixup code to access integer registers. */ #define dpf_reg(r) (regs->regs[r]) /* + * Unlock any spinlocks which will prevent us from getting the out + */ +void bust_spinlocks(int yes) +{ + int loglevel_save = console_loglevel; + + if (yes) { + oops_in_progress = 1; + return; + } +#ifdef CONFIG_VT + unblank_screen(); +#endif + oops_in_progress = 0; + /* + * OK, the message is on the console. Now we call printk() + * without oops_in_progress set so that printk will give klogd + * a poke. Hold onto your hats... + */ + console_loglevel = 15; /* NMI oopser may have shut the console up */ + printk(" "); + console_loglevel = loglevel_save; +} + +/* * This routine handles page faults. It determines the address, * and the problem, and then passes it off to one of the appropriate * routines. @@ -45,9 +73,14 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long write, struct vm_area_struct * vma; struct task_struct *tsk = current; struct mm_struct *mm = tsk->mm; - unsigned long fixup; + const struct exception_table_entry *fixup; siginfo_t info; +#if 0 + printk("Cpu%d[%s:%d:%08lx:%ld:%08lx]\n", smp_processor_id(), + current->comm, current->pid, address, write, regs->cp0_epc); +#endif + /* * We fault-in kernel-space virtual memory on-demand. The * 'reference' page table is init_mm.pgd. @@ -57,7 +90,7 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long write, * only copy the information from the master page table, * nothing more. */ - if (address >= TASK_SIZE) + if (address >= VMALLOC_START) goto vmalloc_fault; info.si_code = SEGV_MAPERR; @@ -65,12 +98,9 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long write, * If we're in an interrupt or have no user * context, we must not take the fault.. */ - if (in_interrupt() || !mm) + if (in_atomic() || !mm) goto no_context; -#if 0 - printk("[%s:%d:%08lx:%ld:%08lx]\n", current->comm, current->pid, - address, write, regs->cp0_epc); -#endif + down_read(&mm->mmap_sem); vma = find_vma(mm, address); if (!vma) @@ -96,22 +126,25 @@ good_area: goto bad_area; } +survive: /* * If for any reason at all we couldn't handle the fault, * make sure we exit gracefully rather than endlessly redo * the fault. */ switch (handle_mm_fault(mm, vma, address, write)) { - case 1: + case VM_FAULT_MINOR: tsk->min_flt++; break; - case 2: + case VM_FAULT_MAJOR: tsk->maj_flt++; break; - case 0: + case VM_FAULT_SIGBUS: goto do_sigbus; - default: + case VM_FAULT_OOM: goto out_of_memory; + default: + BUG(); } up_read(&mm->mmap_sem); @@ -124,7 +157,6 @@ good_area: bad_area: up_read(&mm->mmap_sem); -bad_area_nosemaphore: /* User mode accesses just cause a SIGSEGV */ if (user_mode(regs)) { tsk->thread.cp0_badvaddr = address; @@ -148,12 +180,11 @@ bad_area_nosemaphore: no_context: /* Are we prepared to handle this kernel fault? */ - fixup = search_exception_table(regs->cp0_epc); + fixup = search_exception_tables(exception_epc(regs)); if (fixup) { - long new_epc; + unsigned long new_epc = fixup->nextinsn; tsk->thread.cp0_baduaddr = address; - new_epc = fixup_exception(dpf_reg, fixup, regs->cp0_epc); if (development_version) printk(KERN_DEBUG "%s: Exception at [<%lx>] (%lx)\n", tsk->comm, regs->cp0_epc, new_epc); @@ -165,11 +196,13 @@ no_context: * Oops. The kernel tried to access some bad page. We'll have to * terminate things with extreme prejudice. */ + + bust_spinlocks(1); + printk(KERN_ALERT "Unable to handle kernel paging request at virtual " "address %08lx, epc == %08lx, ra == %08lx\n", address, regs->cp0_epc, regs->regs[31]); die("Oops", regs); - do_exit(SIGKILL); /* * We ran out of memory, or some other thing happened to us that made @@ -177,6 +210,11 @@ no_context: */ out_of_memory: up_read(&mm->mmap_sem); + if (tsk->pid == 1) { + yield(); + down_read(&mm->mmap_sem); + goto survive; + } printk("VM: killing process %s\n", tsk->comm); if (user_mode(regs)) do_exit(SIGKILL); @@ -207,26 +245,31 @@ vmalloc_fault: /* * Synchronize this task's top level page-table * with the 'reference' page table. + * + * Do _not_ use "tsk" here. We might be inside + * an interrupt in the middle of a task switch.. */ - int offset = pgd_index(address); + int offset = __pgd_offset(address); pgd_t *pgd, *pgd_k; pmd_t *pmd, *pmd_k; + pte_t *pte_k; - pgd = tsk->active_mm->pgd + offset; + pgd = (pgd_t *) pgd_current[smp_processor_id()] + offset; pgd_k = init_mm.pgd + offset; - if (!pgd_present(*pgd)) { - if (!pgd_present(*pgd_k)) - goto bad_area_nosemaphore; - set_pgd(pgd, *pgd_k); - return; - } + if (!pgd_present(*pgd_k)) + goto no_context; + set_pgd(pgd, *pgd_k); pmd = pmd_offset(pgd, address); pmd_k = pmd_offset(pgd_k, address); - - if (pmd_present(*pmd) || !pmd_present(*pmd_k)) - goto bad_area_nosemaphore; + if (!pmd_present(*pmd_k)) + goto no_context; set_pmd(pmd, *pmd_k); + + pte_k = pte_offset_kernel(pmd_k, address); + if (!pte_present(*pte_k)) + goto no_context; + return; } } diff --git a/arch/mips/mm/highmem.c b/arch/mips/mm/highmem.c new file mode 100644 index 000000000000..0ae1245c7010 --- /dev/null +++ b/arch/mips/mm/highmem.c @@ -0,0 +1,101 @@ +#include <linux/module.h> +#include <linux/highmem.h> +#include <asm/tlbflush.h> + +void *kmap(struct page *page) +{ + void *addr; + + if (in_interrupt()) + BUG(); + + if (page < highmem_start_page) + return page_address(page); + addr = kmap_high(page); + flush_tlb_one((unsigned long)addr); + + return addr; +} + +void kunmap(struct page *page) +{ + if (in_interrupt()) + BUG(); + if (page < highmem_start_page) + return; + kunmap_high(page); +} + +/* + * kmap_atomic/kunmap_atomic is significantly faster than kmap/kunmap because + * no global lock is needed and because the kmap code must perform a global TLB + * invalidation when the kmap pool wraps. + * + * However when holding an atomic kmap is is not legal to sleep, so atomic + * kmaps are appropriate for short, tight code paths only. + */ + +void *kmap_atomic(struct page *page, enum km_type type) +{ + enum fixed_addresses idx; + unsigned long vaddr; + + inc_preempt_count(); + if (page < highmem_start_page) + return page_address(page); + + idx = type + KM_TYPE_NR*smp_processor_id(); + vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx); +#ifdef CONFIG_DEBUG_HIGHMEM + if (!pte_none(*(kmap_pte-idx))) + BUG(); +#endif + set_pte(kmap_pte-idx, mk_pte(page, kmap_prot)); + local_flush_tlb_one((unsigned long)vaddr); + + return (void*) vaddr; +} + +void kunmap_atomic(void *kvaddr, enum km_type type) +{ +#ifdef CONFIG_DEBUG_HIGHMEM + unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK; + enum fixed_addresses idx = type + KM_TYPE_NR*smp_processor_id(); + + if (vaddr < FIXADDR_START) { // FIXME + dec_preempt_count(); + return; + } + + if (vaddr != __fix_to_virt(FIX_KMAP_BEGIN+idx)) + BUG(); + + /* + * force other mappings to Oops if they'll try to access + * this pte without first remap it + */ + pte_clear(kmap_pte-idx); + local_flush_tlb_one(vaddr); +#endif + + dec_preempt_count(); +} + +struct page *kmap_atomic_to_page(void *ptr) +{ + unsigned long idx, vaddr = (unsigned long)ptr; + pte_t *pte; + + if (vaddr < FIXADDR_START) + return virt_to_page(ptr); + + idx = virt_to_fix(vaddr); + pte = kmap_pte - (idx - FIX_KMAP_BEGIN); + return pte_page(*pte); +} + +EXPORT_SYMBOL(kmap); +EXPORT_SYMBOL(kunmap); +EXPORT_SYMBOL(kmap_atomic); +EXPORT_SYMBOL(kunmap_atomic); +EXPORT_SYMBOL(kmap_atomic_to_page); diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c index a0cae9182737..b0e8fa664d70 100644 --- a/arch/mips/mm/init.c +++ b/arch/mips/mm/init.c @@ -24,36 +24,23 @@ #include <linux/bootmem.h> #include <linux/highmem.h> #include <linux/swap.h> -#ifdef CONFIG_BLK_DEV_INITRD #include <linux/blk.h> -#endif #include <asm/bootinfo.h> +#include <asm/cacheflush.h> #include <asm/cachectl.h> #include <asm/cpu.h> #include <asm/dma.h> -#include <asm/jazzdma.h> -#include <asm/system.h> -#include <asm/pgtable.h> #include <asm/pgalloc.h> -#ifdef CONFIG_SGI_IP22 -#include <asm/sgialib.h> -#endif #include <asm/mmu_context.h> +#include <asm/sections.h> #include <asm/tlb.h> DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); -extern void prom_free_prom_memory(void); - - -asmlinkage int sys_cacheflush(void *addr, int bytes, int cache) -{ - /* This should flush more selectivly ... */ - __flush_cache_all(); - - return 0; -} +unsigned long highstart_pfn, highend_pfn; +static unsigned long totalram_pages; +static unsigned long totalhigh_pages; /* * We have upto 8 empty zeroed pages so we can map one of the right colour @@ -68,9 +55,10 @@ static inline unsigned long setup_zero_pages(void) { unsigned long order, size; struct page *page; - if(mips_cpu.options & MIPS_CPU_VCE) + + if (cpu_has_vce) order = 3; - else + else order = 0; empty_zero_page = __get_free_pages(GFP_KERNEL, order); @@ -91,69 +79,109 @@ static inline unsigned long setup_zero_pages(void) return 1UL << order; } -int do_check_pgt_cache(int low, int high) +#ifdef CONFIG_HIGHMEM +pte_t *kmap_pte; +pgprot_t kmap_prot; + +#define kmap_get_fixmap_pte(vaddr) \ + pte_offset_kernel(pmd_offset(pgd_offset_k(vaddr), (vaddr)), (vaddr)) + +static void __init kmap_init(void) { - int freed = 0; - - if(pgtable_cache_size > high) { - do { - if(pgd_quicklist) - free_pgd_slow(get_pgd_fast()), freed++; - if(pmd_quicklist) - free_pmd_slow(get_pmd_fast()), freed++; - if(pte_quicklist) - free_pte_slow(get_pte_fast()), freed++; - } while(pgtable_cache_size > low); - } - return freed; + 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; } -void show_mem(void) +#endif /* CONFIG_HIGHMEM */ + +#ifdef CONFIG_HIGHMEM +static void __init fixrange_init (unsigned long start, unsigned long end, + pgd_t *pgd_base) { - int i, free = 0, total = 0, reserved = 0; - int shared = 0, cached = 0; - - printk("Mem-info:\n"); - show_free_areas(); - printk("Free swap: %6dkB\n", nr_swap_pages<<(PAGE_SHIFT-10)); - i = max_mapnr; - while (i-- > 0) { - total++; - if (PageReserved(mem_map+i)) - reserved++; - else if (PageSwapCache(mem_map+i)) - cached++; - else if (!page_count(mem_map + i)) - free++; - else - shared += page_count(mem_map + i) - 1; + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; + int i, j; + unsigned long vaddr; + + vaddr = start; + i = __pgd_offset(vaddr); + j = __pmd_offset(vaddr); + pgd = pgd_base + i; + + for ( ; (i < PTRS_PER_PGD) && (vaddr != end); pgd++, i++) { + pmd = (pmd_t *)pgd; + for (; (j < PTRS_PER_PMD) && (vaddr != end); pmd++, j++) { + if (pmd_none(*pmd)) { + pte = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE); + set_pmd(pmd, __pmd(pte)); + if (pte != pte_offset_kernel(pmd, 0)) + BUG(); + } + vaddr += PMD_SIZE; + } + j = 0; } - printk("%d pages of RAM\n", total); - 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); - printk("%d free pages\n", free); } +#endif -/* References to section boundaries */ +void __init pagetable_init(void) +{ +#ifdef CONFIG_HIGHMEM + unsigned long vaddr; + pgd_t *pgd, *pgd_base; + pmd_t *pmd; + pte_t *pte; +#endif -extern char _ftext, _etext, _fdata, _edata; -extern char __init_begin, __init_end; + /* Initialize the entire pgd. */ + pgd_init((unsigned long)swapper_pg_dir); + pgd_init((unsigned long)swapper_pg_dir + + sizeof(pgd_t ) * USER_PTRS_PER_PGD); + +#ifdef CONFIG_HIGHMEM + pgd_base = swapper_pg_dir; + + /* + * Fixed mappings: + */ + vaddr = __fix_to_virt(__end_of_fixed_addresses - 1) & PMD_MASK; + fixrange_init(vaddr, 0, pgd_base); + + /* + * Permanent kmaps: + */ + vaddr = PKMAP_BASE; + fixrange_init(vaddr, vaddr + PAGE_SIZE*LAST_PKMAP, pgd_base); + + pgd = swapper_pg_dir + __pgd_offset(vaddr); + pmd = pmd_offset(pgd, vaddr); + pte = pte_offset_kernel(pmd, vaddr); + pkmap_page_table = pte; +#endif +} void __init paging_init(void) { unsigned long zones_size[MAX_NR_ZONES] = {0, 0, 0}; - unsigned long max_dma, low; + unsigned long max_dma, high, low; - /* Initialize the entire pgd. */ - pgd_init((unsigned long)swapper_pg_dir); - pgd_init((unsigned long)swapper_pg_dir + PAGE_SIZE / 2); + pagetable_init(); + +#ifdef CONFIG_HIGHMEM + kmap_init(); +#endif max_dma = virt_to_phys((char *)MAX_DMA_ADDRESS) >> PAGE_SHIFT; low = max_low_pfn; + high = highend_pfn; -#if defined(CONFIG_PCI) || defined(CONFIG_ISA) +#ifdef CONFIG_ISA if (low < max_dma) zones_size[ZONE_DMA] = low; else { @@ -163,6 +191,9 @@ void __init paging_init(void) #else zones_size[ZONE_DMA] = low; #endif +#ifdef CONFIG_HIGHMEM + zones_size[ZONE_HIGHMEM] = high - low; +#endif free_area_init(zones_size); } @@ -197,8 +228,17 @@ void __init mem_init(void) unsigned long codesize, reservedpages, datasize, initsize; unsigned long tmp, ram; +#ifdef CONFIG_HIGHMEM + highstart_pfn = (KSEG1 - KSEG0) >> PAGE_SHIFT; + highmem_start_page = mem_map + highstart_pfn; +#ifdef CONFIG_DISCONTIGMEM +#error "CONFIG_HIGHMEM and CONFIG_DISCONTIGMEM dont work together yet" +#endif + max_mapnr = num_physpages = highend_pfn; +#else max_mapnr = num_physpages = max_low_pfn; - high_memory = (void *) __va(max_mapnr << PAGE_SHIFT); +#endif + high_memory = (void *) __va(max_low_pfn * PAGE_SIZE); totalram_pages += free_all_bootmem(); totalram_pages -= setup_zero_pages(); /* Setup zeroed pages. */ @@ -211,25 +251,43 @@ void __init mem_init(void) reservedpages++; } - codesize = (unsigned long) &_etext - (unsigned long) &_ftext; - datasize = (unsigned long) &_edata - (unsigned long) &_fdata; +#ifdef CONFIG_HIGHMEM + for (tmp = highstart_pfn; tmp < highend_pfn; tmp++) { + struct page *page = mem_map + tmp; + + if (!page_is_ram(tmp)) { + SetPageReserved(page); + continue; + } + ClearPageReserved(page); + set_bit(PG_highmem, &page->flags); + atomic_set(&page->count, 1); + __free_page(page); + totalhigh_pages++; + } + totalram_pages += totalhigh_pages; +#endif + + codesize = (unsigned long) &_etext - (unsigned long) &_text; + datasize = (unsigned long) &_edata - (unsigned long) &_etext; initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin; - printk("Memory: %luk/%luk available (%ldk kernel code, %ldk reserved, " - "%ldk data, %ldk init)\n", + printk(KERN_INFO "Memory: %luk/%luk available (%ldk kernel code, " + "%ldk reserved, %ldk data, %ldk init, %ldk highmem)\n", (unsigned long) nr_free_pages() << (PAGE_SHIFT-10), ram << (PAGE_SHIFT-10), codesize >> 10, reservedpages << (PAGE_SHIFT-10), datasize >> 10, - initsize >> 10); + initsize >> 10, + (unsigned long) (totalhigh_pages << (PAGE_SHIFT-10))); } #ifdef CONFIG_BLK_DEV_INITRD void free_initrd_mem(unsigned long start, unsigned long end) { if (start < end) - printk("Freeing initrd memory: %ldk freed\n", + printk(KERN_INFO "Freeing initrd memory: %ldk freed\n", (end - start) >> 10); for (; start < end; start += PAGE_SIZE) { @@ -242,14 +300,14 @@ void free_initrd_mem(unsigned long start, unsigned long end) #endif extern char __init_begin, __init_end; -extern void prom_free_prom_memory(void); +extern void prom_free_prom_memory(void) __init; void free_initmem(void) { unsigned long addr; prom_free_prom_memory (); - + addr = (unsigned long) &__init_begin; while (addr < (unsigned long) &__init_end) { ClearPageReserved(virt_to_page(addr)); @@ -258,6 +316,6 @@ void free_initmem(void) totalram_pages++; addr += PAGE_SIZE; } - printk("Freeing unused kernel memory: %dk freed\n", + printk(KERN_INFO "Freeing unused kernel memory: %dk freed\n", (&__init_end - &__init_begin) >> 10); } diff --git a/arch/mips/mm/ioremap.c b/arch/mips/mm/ioremap.c index dd581b61679e..1dfe2ddfe97f 100644 --- a/arch/mips/mm/ioremap.c +++ b/arch/mips/mm/ioremap.c @@ -4,20 +4,22 @@ * for more details. * * (C) Copyright 1995 1996 Linus Torvalds - * (C) Copyright 2001 Ralf Baechle + * (C) Copyright 2001, 2002 Ralf Baechle */ #include <linux/module.h> #include <asm/addrspace.h> #include <asm/byteorder.h> #include <linux/vmalloc.h> +#include <asm/cacheflush.h> #include <asm/io.h> #include <asm/pgalloc.h> +#include <asm/tlbflush.h> -static inline void remap_area_pte(pte_t * pte, unsigned long address, unsigned long size, - unsigned long phys_addr, unsigned long flags) +static inline void remap_area_pte(pte_t * pte, unsigned long address, + phys_t size, phys_t phys_addr, unsigned long flags) { - unsigned long end; + phys_t end; unsigned long pfn; pgprot_t pgprot = __pgprot(_PAGE_GLOBAL | _PAGE_PRESENT | __READABLE | __WRITEABLE | flags); @@ -41,10 +43,10 @@ static inline void remap_area_pte(pte_t * pte, unsigned long address, unsigned l } while (address && (address < end)); } -static inline int remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size, - unsigned long phys_addr, unsigned long flags) +static inline int remap_area_pmd(pmd_t * pmd, unsigned long address, + phys_t size, phys_t phys_addr, unsigned long flags) { - unsigned long end; + phys_t end; address &= ~PGDIR_MASK; end = address + size; @@ -54,7 +56,7 @@ static inline int remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned lo if (address >= end) BUG(); do { - pte_t * pte = pte_alloc(&init_mm, pmd, address); + pte_t * pte = pte_alloc_kernel(&init_mm, pmd, address); if (!pte) return -ENOMEM; remap_area_pte(pte, address, end - address, address + phys_addr, flags); @@ -64,8 +66,8 @@ static inline int remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned lo return 0; } -static int remap_area_pages(unsigned long address, unsigned long phys_addr, - unsigned long size, unsigned long flags) +static int remap_area_pages(unsigned long address, phys_t phys_addr, + phys_t size, unsigned long flags) { int error; pgd_t * dir; @@ -109,13 +111,14 @@ static int remap_area_pages(unsigned long address, unsigned long phys_addr, * caller shouldn't need to know that small detail. */ -#define IS_LOW512(addr) (!((unsigned long)(addr) & ~0x1fffffffUL)) +#define IS_LOW512(addr) (!((phys_t)(addr) & ~0x1fffffffUL)) -void * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags) +void * __ioremap(phys_t phys_addr, phys_t size, unsigned long flags) { - void * addr; struct vm_struct * area; - unsigned long offset, last_addr; + unsigned long offset; + phys_t last_addr; + void * addr; /* Don't allow wraparound or zero size */ last_addr = phys_addr + size - 1; @@ -123,10 +126,11 @@ void * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flag return NULL; /* - * Map objects in the low 512mb of address space using KSEG1, otherwise - * map using page tables. + * Map uncached objects in the low 512mb of address space using KSEG1, + * otherwise map using page tables. */ - if (IS_LOW512(phys_addr) && IS_LOW512(phys_addr + size - 1)) + if (IS_LOW512(phys_addr) && IS_LOW512(last_addr) && + flags == _CACHE_UNCACHED) return (void *) KSEG1ADDR(phys_addr); /* @@ -138,7 +142,7 @@ void * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flag t_addr = __va(phys_addr); t_end = t_addr + (size - 1); - + for(page = virt_to_page(t_addr); page <= virt_to_page(t_end); page++) if(!PageReserved(page)) return NULL; @@ -159,7 +163,7 @@ void * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flag return NULL; addr = area->addr; if (remap_area_pages(VMALLOC_VMADDR(addr), phys_addr, size, flags)) { - vfree(addr); + vunmap(addr); return NULL; } @@ -170,8 +174,19 @@ void * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flag void iounmap(void *addr) { - if (!IS_KSEG1(addr)) - return vfree((void *) (PAGE_MASK & (unsigned long) addr)); + struct vm_struct *p; + + if (IS_KSEG1(addr)) + return; + + vfree((void *) (PAGE_MASK & (unsigned long) addr)); + p = remove_vm_area((void *) (PAGE_MASK & (unsigned long) addr)); + if (!p) { + printk(KERN_ERR "iounmap: bad address %p\n", addr); + return; + } + + kfree(p); } EXPORT_SYMBOL(__ioremap); diff --git a/arch/mips/mm/loadmmu.c b/arch/mips/mm/loadmmu.c index c0b3d080c545..86f5b976c4c3 100644 --- a/arch/mips/mm/loadmmu.c +++ b/arch/mips/mm/loadmmu.c @@ -1,7 +1,11 @@ /* - * loadmmu.c: Setup cpu/cache specific function ptrs at boot time. + * 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) 1996 David S. Miller (dm@engr.sgi.com) + * Copyright (C) 1997, 1999, 2000, 2001, 2002, 2003 Ralf Baechle (ralf@gnu.org) + * Copyright (C) 1999 Silicon Graphics, Inc. * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved. */ @@ -10,6 +14,7 @@ #include <linux/kernel.h> #include <linux/sched.h> #include <linux/mm.h> +#include <linux/module.h> #include <asm/bootinfo.h> #include <asm/cpu.h> @@ -22,80 +27,93 @@ void (*_clear_page)(void * page); void (*_copy_page)(void * to, void * from); /* Cache operations. */ -void (*_flush_cache_all)(void); -void (*___flush_cache_all)(void); -void (*_flush_cache_mm)(struct mm_struct *mm); -void (*_flush_cache_range)(struct vm_area_struct *vma, 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)(struct page * page); -void (*_flush_icache_range)(unsigned long start, unsigned long end); -void (*_flush_icache_page)(struct vm_area_struct *vma, struct page *page); +void (*flush_cache_all)(void); +void (*__flush_cache_all)(void); +void (*flush_cache_mm)(struct mm_struct *mm); +void (*flush_cache_range)(struct vm_area_struct *vma, unsigned long start, + unsigned long end); +void (*flush_cache_page)(struct vm_area_struct *vma, unsigned long page); +void (*flush_icache_range)(unsigned long start, unsigned long end); +void (*flush_icache_page)(struct vm_area_struct *vma, struct page *page); + +/* MIPS specific cache operations */ +void (*flush_cache_sigtramp)(unsigned long addr); +void (*flush_data_cache_page)(unsigned long addr); +void (*flush_icache_all)(void); + +#ifdef CONFIG_NONCOHERENT_IO /* DMA cache operations. */ void (*_dma_cache_wback_inv)(unsigned long start, unsigned long size); void (*_dma_cache_wback)(unsigned long start, unsigned long size); void (*_dma_cache_inv)(unsigned long start, unsigned long size); +EXPORT_SYMBOL(_dma_cache_wback_inv); +EXPORT_SYMBOL(_dma_cache_wback); +EXPORT_SYMBOL(_dma_cache_inv); + +#endif /* CONFIG_NONCOHERENT_IO */ + extern void ld_mmu_r23000(void); extern void ld_mmu_r4xx0(void); -extern void ld_mmu_r5432(void); +extern void ld_mmu_tx39(void); extern void ld_mmu_r6000(void); -extern void ld_mmu_rm7k(void); extern void ld_mmu_tfp(void); extern void ld_mmu_andes(void); extern void ld_mmu_sb1(void); -extern void ld_mmu_mips32(void); +extern void sb1_tlb_init(void); +extern void r3k_tlb_init(void); +extern void r4k_tlb_init(void); +extern void sb1_tlb_init(void); -void __init loadmmu(void) +void __init load_mmu(void) { - - if (mips_cpu.options & MIPS_CPU_4KTLB) { -#if defined(CONFIG_CPU_R4X00) || defined(CONFIG_CPU_VR41XX) || \ - defined(CONFIG_CPU_R4300) || defined(CONFIG_CPU_R5000) || \ - defined(CONFIG_CPU_NEVADA) - printk("Loading R4000 MMU routines.\n"); + if (cpu_has_4ktlb) { +#if defined(CONFIG_CPU_R4X00) || defined(CONFIG_CPU_VR41XX) || \ + defined(CONFIG_CPU_R4300) || defined(CONFIG_CPU_R5000) || \ + defined(CONFIG_CPU_NEVADA) || defined(CONFIG_CPU_R5432) || \ + defined(CONFIG_CPU_R5500) || defined(CONFIG_CPU_MIPS32) || \ + defined(CONFIG_CPU_MIPS64) || defined(CONFIG_CPU_TX49XX) || \ + defined(CONFIG_CPU_RM7000) ld_mmu_r4xx0(); + r4k_tlb_init(); #endif -#if defined(CONFIG_CPU_RM7000) - printk("Loading RM7000 MMU routines.\n"); - ld_mmu_rm7k(); -#endif -#if defined(CONFIG_CPU_R5432) - printk("Loading R5432 MMU routines.\n"); - ld_mmu_r5432(); -#endif - -#if defined(CONFIG_CPU_MIPS32) || defined(CONFIG_CPU_MIPS64) - printk("Loading MIPS32 MMU routines.\n"); - ld_mmu_mips32(); -#endif - } else switch(mips_cpu.cputype) { + } else switch (current_cpu_data.cputype) { #ifdef CONFIG_CPU_R3000 case CPU_R2000: case CPU_R3000: case CPU_R3000A: + case CPU_R3081E: + ld_mmu_r23000(); + r3k_tlb_init(); + break; +#endif +#ifdef CONFIG_CPU_TX39XX case CPU_TX3912: case CPU_TX3922: case CPU_TX3927: - case CPU_R3081E: - printk("Loading R[23]000 MMU routines.\n"); - ld_mmu_r23000(); + ld_mmu_tx39(); + r3k_tlb_init(); break; #endif #ifdef CONFIG_CPU_R10000 case CPU_R10000: - printk("Loading R10000 MMU routines.\n"); - ld_mmu_andes(); + case CPU_R12000: + ld_mmu_r4xx0(); + andes_tlb_init(); break; #endif #ifdef CONFIG_CPU_SB1 case CPU_SB1: - printk("Loading SB1 MMU routines.\n"); ld_mmu_sb1(); + sb1_tlb_init(); break; #endif + + case CPU_R8000: + panic("R8000 is unsupported"); + break; + default: panic("Yeee, unsupported mmu/cache architecture."); } diff --git a/arch/mips/mm/mips32.c b/arch/mips/mm/mips32.c deleted file mode 100644 index 611061e48623..000000000000 --- a/arch/mips/mm/mips32.c +++ /dev/null @@ -1,1065 +0,0 @@ -/* - * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com - * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved. - * - * This program is free software; you can distribute it and/or modify it - * under the terms of the GNU General Public License (Version 2) as - * published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * MIPS32 CPU variant specific MMU/Cache routines. - */ -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/mm.h> - -#include <asm/bootinfo.h> -#include <asm/cpu.h> -#include <asm/bcache.h> -#include <asm/io.h> -#include <asm/page.h> -#include <asm/pgtable.h> -#include <asm/system.h> -#include <asm/mmu_context.h> - -/* CP0 hazard avoidance. */ -#define BARRIER __asm__ __volatile__(".set noreorder\n\t" \ - "nop; nop; nop; nop; nop; nop;\n\t" \ - ".set reorder\n\t") - -/* Primary cache parameters. */ -static int icache_size, dcache_size; /* Size in bytes */ -static int ic_lsize, dc_lsize; /* LineSize in bytes */ - -/* Secondary cache (if present) parameters. */ -static unsigned int scache_size, sc_lsize; /* Again, in bytes */ - -#include <asm/cacheops.h> -#include <asm/mips32_cache.h> - -#undef DEBUG_CACHE - -/* - * Dummy cache handling routines for machines without boardcaches - */ -static void no_sc_noop(void) {} - -static struct bcache_ops no_sc_ops = { - (void *)no_sc_noop, (void *)no_sc_noop, - (void *)no_sc_noop, (void *)no_sc_noop -}; - -struct bcache_ops *bcops = &no_sc_ops; - - -/* - * Zero an entire page. - */ - -static void mips32_clear_page_dc(unsigned long page) -{ - unsigned long i; - - if (mips_cpu.options & MIPS_CPU_CACHE_CDEX) { - for (i=page; i<page+PAGE_SIZE; i+=dc_lsize) { - __asm__ __volatile__( - ".set\tnoreorder\n\t" - ".set\tnoat\n\t" - ".set\tmips3\n\t" - "cache\t%2,(%0)\n\t" - ".set\tmips0\n\t" - ".set\tat\n\t" - ".set\treorder" - :"=r" (i) - :"0" (i), - "I" (Create_Dirty_Excl_D)); - } - } - for (i=page; i<page+PAGE_SIZE; i+=4) - *(unsigned long *)(i) = 0; -} - -static void mips32_clear_page_sc(unsigned long page) -{ - unsigned long i; - - if (mips_cpu.options & MIPS_CPU_CACHE_CDEX) { - for (i=page; i<page+PAGE_SIZE; i+=sc_lsize) { - __asm__ __volatile__( - ".set\tnoreorder\n\t" - ".set\tnoat\n\t" - ".set\tmips3\n\t" - "cache\t%2,(%0)\n\t" - ".set\tmips0\n\t" - ".set\tat\n\t" - ".set\treorder" - :"=r" (i) - :"0" (i), - "I" (Create_Dirty_Excl_SD)); - } - } - for (i=page; i<page+PAGE_SIZE; i+=4) - *(unsigned long *)(i) = 0; -} - -static void mips32_copy_page_dc(unsigned long to, unsigned long from) -{ - unsigned long i; - - if (mips_cpu.options & MIPS_CPU_CACHE_CDEX) { - for (i=to; i<to+PAGE_SIZE; i+=dc_lsize) { - __asm__ __volatile__( - ".set\tnoreorder\n\t" - ".set\tnoat\n\t" - ".set\tmips3\n\t" - "cache\t%2,(%0)\n\t" - ".set\tmips0\n\t" - ".set\tat\n\t" - ".set\treorder" - :"=r" (i) - :"0" (i), - "I" (Create_Dirty_Excl_D)); - } - } - for (i=0; i<PAGE_SIZE; i+=4) - *(unsigned long *)(to+i) = *(unsigned long *)(from+i); -} - -static void mips32_copy_page_sc(unsigned long to, unsigned long from) -{ - unsigned long i; - - if (mips_cpu.options & MIPS_CPU_CACHE_CDEX) { - for (i=to; i<to+PAGE_SIZE; i+=sc_lsize) { - __asm__ __volatile__( - ".set\tnoreorder\n\t" - ".set\tnoat\n\t" - ".set\tmips3\n\t" - "cache\t%2,(%0)\n\t" - ".set\tmips0\n\t" - ".set\tat\n\t" - ".set\treorder" - :"=r" (i) - :"0" (i), - "I" (Create_Dirty_Excl_SD)); - } - } - for (i=0; i<PAGE_SIZE; i+=4) - *(unsigned long *)(to+i) = *(unsigned long *)(from+i); -} - -static inline void mips32_flush_cache_all_sc(void) -{ - unsigned long flags; - - local_irq_save(flags); - blast_dcache(); blast_icache(); blast_scache(); - local_irq_restore(flags); -} - -static inline void mips32_flush_cache_all_pc(void) -{ - unsigned long flags; - - local_irq_save(flags); - blast_dcache(); blast_icache(); - local_irq_restore(flags); -} - -static void -mips32_flush_cache_range_sc(struct vm_area_struct *vma, - unsigned long start, - unsigned long end) -{ - struct mm_struct *mm = vma->vm_mm; - unsigned long flags; - - if(mm->context == 0) - return; - - start &= PAGE_MASK; -#ifdef DEBUG_CACHE - printk("crange[%d,%08lx,%08lx]", (int)mm->context, start, end); -#endif - if (vma) { - if(mm->context != current->mm->context) { - mips32_flush_cache_all_sc(); - } else { - pgd_t *pgd; - pmd_t *pmd; - pte_t *pte; - - local_irq_save(flags); - while(start < end) { - pgd = pgd_offset(mm, start); - pmd = pmd_offset(pgd, start); - pte = pte_offset(pmd, start); - - if(pte_val(*pte) & _PAGE_VALID) - blast_scache_page(start); - start += PAGE_SIZE; - } - local_irq_restore(flags); - } - } -} - -static void mips32_flush_cache_range_pc(struct vm_area_struct *vma, - unsigned long start, - unsigned long end) -{ - struct mm_struct *mm = vma->vm_mm; - - if(mm->context != 0) { - unsigned long flags; - -#ifdef DEBUG_CACHE - printk("crange[%d,%08lx,%08lx]", (int)mm->context, start, end); -#endif - local_irq_save(flags); - blast_dcache(); blast_icache(); - local_irq_restore(flags); - } -} - -/* - * On architectures like the Sparc, we could get rid of lines in - * the cache created only by a certain context, but on the MIPS - * (and actually certain Sparc's) we cannot. - */ -static void mips32_flush_cache_mm_sc(struct mm_struct *mm) -{ - if(mm->context != 0) { -#ifdef DEBUG_CACHE - printk("cmm[%d]", (int)mm->context); -#endif - mips32_flush_cache_all_sc(); - } -} - -static void mips32_flush_cache_mm_pc(struct mm_struct *mm) -{ - if(mm->context != 0) { -#ifdef DEBUG_CACHE - printk("cmm[%d]", (int)mm->context); -#endif - mips32_flush_cache_all_pc(); - } -} - - - - - -static void mips32_flush_cache_page_sc(struct vm_area_struct *vma, - unsigned long page) -{ - struct mm_struct *mm = vma->vm_mm; - unsigned long flags; - pgd_t *pgdp; - pmd_t *pmdp; - pte_t *ptep; - - /* - * If ownes no valid ASID yet, cannot possibly have gotten - * this page into the cache. - */ - if (mm->context == 0) - return; - -#ifdef DEBUG_CACHE - printk("cpage[%d,%08lx]", (int)mm->context, page); -#endif - local_irq_save(flags); - page &= PAGE_MASK; - pgdp = pgd_offset(mm, page); - pmdp = pmd_offset(pgdp, page); - ptep = pte_offset(pmdp, page); - - /* - * If the page isn't marked valid, the page cannot possibly be - * in the cache. - */ - if (!(pte_val(*ptep) & _PAGE_VALID)) - goto out; - - /* - * Doing flushes for another ASID than the current one is - * too difficult since R4k caches do a TLB translation - * for every cache flush operation. So we do indexed flushes - * in that case, which doesn't overly flush the cache too much. - */ - if (mm->context != current->active_mm->context) { - /* - * Do indexed flush, too much work to get the (possible) - * tlb refills to work correctly. - */ - page = (KSEG0 + (page & (scache_size - 1))); - blast_dcache_page_indexed(page); - blast_scache_page_indexed(page); - } else - blast_scache_page(page); -out: - local_irq_restore(flags); -} - -static void mips32_flush_cache_page_pc(struct vm_area_struct *vma, - unsigned long page) -{ - struct mm_struct *mm = vma->vm_mm; - unsigned long flags; - pgd_t *pgdp; - pmd_t *pmdp; - pte_t *ptep; - - /* - * If ownes no valid ASID yet, cannot possibly have gotten - * this page into the cache. - */ - if (mm->context == 0) - return; - -#ifdef DEBUG_CACHE - printk("cpage[%d,%08lx]", (int)mm->context, page); -#endif - local_irq_save(flags); - page &= PAGE_MASK; - pgdp = pgd_offset(mm, page); - pmdp = pmd_offset(pgdp, page); - ptep = pte_offset(pmdp, page); - - /* - * If the page isn't marked valid, the page cannot possibly be - * in the cache. - */ - if (!(pte_val(*ptep) & _PAGE_VALID)) - goto out; - - /* - * Doing flushes for another ASID than the current one is - * too difficult since Mips32 caches do a TLB translation - * for every cache flush operation. So we do indexed flushes - * in that case, which doesn't overly flush the cache too much. - */ - if (mm == current->active_mm) { - blast_dcache_page(page); - } else { - /* Do indexed flush, too much work to get the (possible) - * tlb refills to work correctly. - */ - page = (KSEG0 + (page & (dcache_size - 1))); - blast_dcache_page_indexed(page); - } -out: - local_irq_restore(flags); -} - -/* 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. - * 3) In KSEG1, no flush necessary. - */ -static void mips32_flush_page_to_ram_sc(struct page *page) -{ - blast_scache_page((unsigned long)page_address(page)); -} - -static void mips32_flush_page_to_ram_pc(struct page *page) -{ - blast_dcache_page((unsigned long)page_address(page)); -} - -static void -mips32_flush_icache_page_s(struct vm_area_struct *vma, struct page *page) -{ - /* - * We did an scache flush therefore PI is already clean. - */ -} - -static void -mips32_flush_icache_range(unsigned long start, unsigned long end) -{ - flush_cache_all(); -} - -static void -mips32_flush_icache_page(struct vm_area_struct *vma, struct page *page) -{ - int address; - - if (!(vma->vm_flags & VM_EXEC)) - return; - - address = KSEG0 + ((unsigned long)page_address(page) & PAGE_MASK & (dcache_size - 1)); - blast_icache_page_indexed(address); -} - -/* - * Writeback and invalidate the primary cache dcache before DMA. - */ -static void -mips32_dma_cache_wback_inv_pc(unsigned long addr, unsigned long size) -{ - unsigned long end, a; - unsigned int flags; - - if (size >= dcache_size) { - flush_cache_all(); - } else { - local_irq_save(flags); - a = addr & ~(dc_lsize - 1); - end = (addr + size) & ~(dc_lsize - 1); - while (1) { - flush_dcache_line(a); /* Hit_Writeback_Inv_D */ - if (a == end) break; - a += dc_lsize; - } - local_irq_restore(flags); - } - bc_wback_inv(addr, size); -} - -static void -mips32_dma_cache_wback_inv_sc(unsigned long addr, unsigned long size) -{ - unsigned long end, a; - - if (size >= scache_size) { - flush_cache_all(); - return; - } - - a = addr & ~(sc_lsize - 1); - end = (addr + size) & ~(sc_lsize - 1); - while (1) { - flush_scache_line(a); /* Hit_Writeback_Inv_SD */ - if (a == end) break; - a += sc_lsize; - } -} - -static void -mips32_dma_cache_inv_pc(unsigned long addr, unsigned long size) -{ - unsigned long end, a; - unsigned int flags; - - if (size >= dcache_size) { - flush_cache_all(); - } else { - local_irq_save(flags); - a = addr & ~(dc_lsize - 1); - end = (addr + size) & ~(dc_lsize - 1); - while (1) { - flush_dcache_line(a); /* Hit_Writeback_Inv_D */ - if (a == end) break; - a += dc_lsize; - } - local_irq_restore(flags); - } - - bc_inv(addr, size); -} - -static void -mips32_dma_cache_inv_sc(unsigned long addr, unsigned long size) -{ - unsigned long end, a; - - if (size >= scache_size) { - flush_cache_all(); - return; - } - - a = addr & ~(sc_lsize - 1); - end = (addr + size) & ~(sc_lsize - 1); - while (1) { - flush_scache_line(a); /* Hit_Writeback_Inv_SD */ - if (a == end) break; - a += sc_lsize; - } -} - -static void -mips32_dma_cache_wback(unsigned long addr, unsigned long size) -{ - panic("mips32_dma_cache called - should not happen.\n"); -} - -/* - * While we're protected against bad userland addresses we don't care - * very much about what happens in that case. Usually a segmentation - * fault will dump the process later on anyway ... - */ -static void mips32_flush_cache_sigtramp(unsigned long addr) -{ - protected_writeback_dcache_line(addr & ~(dc_lsize - 1)); - protected_flush_icache_line(addr & ~(ic_lsize - 1)); -} - -#undef DEBUG_TLB -#undef DEBUG_TLBUPDATE - -void flush_tlb_all(void) -{ - unsigned long flags; - unsigned long old_ctx; - int entry; - -#ifdef DEBUG_TLB - printk("[tlball]"); -#endif - - local_irq_save(flags); - /* Save old context and create impossible VPN2 value */ - old_ctx = (get_entryhi() & 0xff); - set_entryhi(KSEG0); - set_entrylo0(0); - set_entrylo1(0); - BARRIER; - - entry = get_wired(); - - /* Blast 'em all away. */ - while(entry < mips_cpu.tlbsize) { - /* Make sure all entries differ. */ - set_entryhi(KSEG0+entry*0x2000); - set_index(entry); - BARRIER; - tlb_write_indexed(); - BARRIER; - entry++; - } - BARRIER; - set_entryhi(old_ctx); - local_irq_restore(flags); -} - -void flush_tlb_mm(struct mm_struct *mm) -{ - if (mm->context != 0) { - unsigned long flags; - -#ifdef DEBUG_TLB - printk("[tlbmm<%d>]", mm->context); -#endif - local_irq_save(flags); - get_new_mmu_context(mm, asid_cache); - if (mm == current->active_mm) - set_entryhi(mm->context & 0xff); - local_irq_restore(flags); - } -} - -void flush_tlb_range(struct mm_struct *mm, unsigned long start, - unsigned long end) -{ - if(mm->context != 0) { - unsigned long flags; - int size; - -#ifdef DEBUG_TLB - printk("[tlbrange<%02x,%08lx,%08lx>]", (mm->context & 0xff), - start, end); -#endif - local_irq_save(flags); - size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; - size = (size + 1) >> 1; - if(size <= mips_cpu.tlbsize/2) { - int oldpid = (get_entryhi() & 0xff); - int newpid = (mm->context & 0xff); - - start &= (PAGE_MASK << 1); - end += ((PAGE_SIZE << 1) - 1); - end &= (PAGE_MASK << 1); - while(start < end) { - int idx; - - set_entryhi(start | newpid); - start += (PAGE_SIZE << 1); - BARRIER; - tlb_probe(); - BARRIER; - idx = get_index(); - set_entrylo0(0); - set_entrylo1(0); - if(idx < 0) - continue; - /* Make sure all entries differ. */ - set_entryhi(KSEG0+idx*0x2000); - BARRIER; - tlb_write_indexed(); - BARRIER; - } - set_entryhi(oldpid); - } else { - get_new_mmu_context(mm, asid_cache); - if (mm == current->active_mm) - set_entryhi(mm->context & 0xff); - } - local_irq_restore(flags); - } -} - -void flush_tlb_page(struct vm_area_struct *vma, unsigned long page) -{ - if (vma->vm_mm->context != 0) { - unsigned long flags; - int oldpid, newpid, idx; - -#ifdef DEBUG_TLB - printk("[tlbpage<%d,%08lx>]", vma->vm_mm->context, page); -#endif - newpid = (vma->vm_mm->context & 0xff); - page &= (PAGE_MASK << 1); - local_irq_save(flags); - oldpid = (get_entryhi() & 0xff); - set_entryhi(page | newpid); - BARRIER; - tlb_probe(); - BARRIER; - idx = get_index(); - set_entrylo0(0); - set_entrylo1(0); - if(idx < 0) - goto finish; - /* Make sure all entries differ. */ - set_entryhi(KSEG0+idx*0x2000); - BARRIER; - tlb_write_indexed(); - - finish: - BARRIER; - set_entryhi(oldpid); - local_irq_restore(flags); - } -} - -void pgd_init(unsigned long page) -{ - unsigned long *p = (unsigned long *) page; - int i; - - for(i = 0; i < USER_PTRS_PER_PGD; i+=8) { - p[i + 0] = (unsigned long) invalid_pte_table; - p[i + 1] = (unsigned long) invalid_pte_table; - p[i + 2] = (unsigned long) invalid_pte_table; - p[i + 3] = (unsigned long) invalid_pte_table; - p[i + 4] = (unsigned long) invalid_pte_table; - p[i + 5] = (unsigned long) invalid_pte_table; - p[i + 6] = (unsigned long) invalid_pte_table; - p[i + 7] = (unsigned long) invalid_pte_table; - } -} - -/* - * Updates the TLB with the new pte(s). - */ -void update_mmu_cache(struct vm_area_struct * vma, - unsigned long address, pte_t pte) -{ - unsigned long flags; - pgd_t *pgdp; - pmd_t *pmdp; - pte_t *ptep; - int idx, pid; - - /* - * Handle debugger faulting in for debugee. - */ - if (current->active_mm != vma->vm_mm) - return; - - pid = get_entryhi() & 0xff; - -#ifdef DEBUG_TLB - if((pid != (vma->vm_mm->context & 0xff)) || (vma->vm_mm->context == 0)) { - printk("update_mmu_cache: Wheee, bogus tlbpid mmpid=%d tlbpid=%d\n", - (int) (vma->vm_mm->context & 0xff), pid); - } -#endif - - local_irq_save(flags); - address &= (PAGE_MASK << 1); - set_entryhi(address | (pid)); - pgdp = pgd_offset(vma->vm_mm, address); - BARRIER; - tlb_probe(); - BARRIER; - pmdp = pmd_offset(pgdp, address); - idx = get_index(); - ptep = pte_offset(pmdp, address); - BARRIER; - set_entrylo0(pte_val(*ptep++) >> 6); - set_entrylo1(pte_val(*ptep) >> 6); - set_entryhi(address | (pid)); - BARRIER; - if(idx < 0) { - tlb_write_random(); - } else { - tlb_write_indexed(); - } - BARRIER; - set_entryhi(pid); - BARRIER; - local_irq_restore(flags); -} - -void show_regs(struct pt_regs * regs) -{ - /* Saved main processor registers. */ - printk("$0 : %08lx %08lx %08lx %08lx\n", - 0UL, regs->regs[1], regs->regs[2], regs->regs[3]); - printk("$4 : %08lx %08lx %08lx %08lx\n", - regs->regs[4], regs->regs[5], regs->regs[6], regs->regs[7]); - printk("$8 : %08lx %08lx %08lx %08lx\n", - regs->regs[8], regs->regs[9], regs->regs[10], regs->regs[11]); - printk("$12: %08lx %08lx %08lx %08lx\n", - regs->regs[12], regs->regs[13], regs->regs[14], regs->regs[15]); - printk("$16: %08lx %08lx %08lx %08lx\n", - regs->regs[16], regs->regs[17], regs->regs[18], regs->regs[19]); - printk("$20: %08lx %08lx %08lx %08lx\n", - regs->regs[20], regs->regs[21], regs->regs[22], regs->regs[23]); - printk("$24: %08lx %08lx\n", - regs->regs[24], regs->regs[25]); - printk("$28: %08lx %08lx %08lx %08lx\n", - regs->regs[28], regs->regs[29], regs->regs[30], regs->regs[31]); - - /* Saved cp0 registers. */ - printk("epc : %08lx %s\nStatus: %08lx\nCause : %08lx\n", - regs->cp0_epc, print_tainted(), regs->cp0_status, regs->cp0_cause); -} - -void add_wired_entry(unsigned long entrylo0, unsigned long entrylo1, - unsigned long entryhi, unsigned long pagemask) -{ - unsigned long flags; - unsigned long wired; - unsigned long old_pagemask; - unsigned long old_ctx; - - local_irq_save(flags); - /* Save old context and create impossible VPN2 value */ - old_ctx = (get_entryhi() & 0xff); - old_pagemask = get_pagemask(); - wired = get_wired(); - set_wired (wired + 1); - set_index (wired); - BARRIER; - set_pagemask (pagemask); - set_entryhi(entryhi); - set_entrylo0(entrylo0); - set_entrylo1(entrylo1); - BARRIER; - tlb_write_indexed(); - BARRIER; - - set_entryhi(old_ctx); - BARRIER; - set_pagemask (old_pagemask); - flush_tlb_all(); - local_irq_restore(flags); -} - -/* Detect and size the various caches. */ -static void __init probe_icache(unsigned long config) -{ - unsigned long config1; - unsigned int lsize; - - if (!(config & (1 << 31))) { - /* - * Not a MIPS32 complainant CPU. - * Config 1 register not supported, we assume R4k style. - */ - icache_size = 1 << (12 + ((config >> 9) & 7)); - ic_lsize = 16 << ((config >> 5) & 1); - mips_cpu.icache.linesz = ic_lsize; - - /* - * We cannot infer associativity - assume direct map - * unless probe template indicates otherwise - */ - if(!mips_cpu.icache.ways) mips_cpu.icache.ways = 1; - mips_cpu.icache.sets = - (icache_size / ic_lsize) / mips_cpu.icache.ways; - } else { - config1 = read_mips32_cp0_config1(); - - if ((lsize = ((config1 >> 19) & 7))) - mips_cpu.icache.linesz = 2 << lsize; - else - mips_cpu.icache.linesz = lsize; - mips_cpu.icache.sets = 64 << ((config1 >> 22) & 7); - mips_cpu.icache.ways = 1 + ((config1 >> 16) & 7); - - ic_lsize = mips_cpu.icache.linesz; - icache_size = mips_cpu.icache.sets * mips_cpu.icache.ways * - ic_lsize; - } - printk("Primary instruction cache %dkb, linesize %d bytes (%d ways)\n", - icache_size >> 10, ic_lsize, mips_cpu.icache.ways); -} - -static void __init probe_dcache(unsigned long config) -{ - unsigned long config1; - unsigned int lsize; - - if (!(config & (1 << 31))) { - /* - * Not a MIPS32 complainant CPU. - * Config 1 register not supported, we assume R4k style. - */ - dcache_size = 1 << (12 + ((config >> 6) & 7)); - dc_lsize = 16 << ((config >> 4) & 1); - mips_cpu.dcache.linesz = dc_lsize; - /* - * We cannot infer associativity - assume direct map - * unless probe template indicates otherwise - */ - if(!mips_cpu.dcache.ways) mips_cpu.dcache.ways = 1; - mips_cpu.dcache.sets = - (dcache_size / dc_lsize) / mips_cpu.dcache.ways; - } else { - config1 = read_mips32_cp0_config1(); - - if ((lsize = ((config1 >> 10) & 7))) - mips_cpu.dcache.linesz = 2 << lsize; - else - mips_cpu.dcache.linesz= lsize; - mips_cpu.dcache.sets = 64 << ((config1 >> 13) & 7); - mips_cpu.dcache.ways = 1 + ((config1 >> 7) & 7); - - dc_lsize = mips_cpu.dcache.linesz; - dcache_size = - mips_cpu.dcache.sets * mips_cpu.dcache.ways - * dc_lsize; - } - printk("Primary data cache %dkb, linesize %d bytes (%d ways)\n", - dcache_size >> 10, dc_lsize, mips_cpu.dcache.ways); -} - - -/* If you even _breathe_ on this function, look at the gcc output - * and make sure it does not pop things on and off the stack for - * the cache sizing loop that executes in KSEG1 space or else - * you will crash and burn badly. You have been warned. - */ -static int __init probe_scache(unsigned long config) -{ - extern unsigned long stext; - unsigned long flags, addr, begin, end, pow2; - int tmp; - - if (mips_cpu.scache.flags == MIPS_CACHE_NOT_PRESENT) - return 0; - - tmp = ((config >> 17) & 1); - if(tmp) - return 0; - tmp = ((config >> 22) & 3); - switch(tmp) { - case 0: - sc_lsize = 16; - break; - case 1: - sc_lsize = 32; - break; - case 2: - sc_lsize = 64; - break; - case 3: - sc_lsize = 128; - break; - } - - begin = (unsigned long) &stext; - begin &= ~((4 * 1024 * 1024) - 1); - end = begin + (4 * 1024 * 1024); - - /* This is such a bitch, you'd think they would make it - * easy to do this. Away you daemons of stupidity! - */ - local_irq_save(flags); - - /* Fill each size-multiple cache line with a valid tag. */ - pow2 = (64 * 1024); - for(addr = begin; addr < end; addr = (begin + pow2)) { - unsigned long *p = (unsigned long *) addr; - __asm__ __volatile__("nop" : : "r" (*p)); /* whee... */ - pow2 <<= 1; - } - - /* Load first line with zero (therefore invalid) tag. */ - set_taglo(0); - set_taghi(0); - __asm__ __volatile__("nop; nop; nop; nop;"); /* avoid the hazard */ - __asm__ __volatile__("\n\t.set noreorder\n\t" - ".set mips3\n\t" - "cache 8, (%0)\n\t" - ".set mips0\n\t" - ".set reorder\n\t" : : "r" (begin)); - __asm__ __volatile__("\n\t.set noreorder\n\t" - ".set mips3\n\t" - "cache 9, (%0)\n\t" - ".set mips0\n\t" - ".set reorder\n\t" : : "r" (begin)); - __asm__ __volatile__("\n\t.set noreorder\n\t" - ".set mips3\n\t" - "cache 11, (%0)\n\t" - ".set mips0\n\t" - ".set reorder\n\t" : : "r" (begin)); - - /* Now search for the wrap around point. */ - pow2 = (128 * 1024); - tmp = 0; - for(addr = (begin + (128 * 1024)); addr < (end); addr = (begin + pow2)) { - __asm__ __volatile__("\n\t.set noreorder\n\t" - ".set mips3\n\t" - "cache 7, (%0)\n\t" - ".set mips0\n\t" - ".set reorder\n\t" : : "r" (addr)); - __asm__ __volatile__("nop; nop; nop; nop;"); /* hazard... */ - if(!get_taglo()) - break; - pow2 <<= 1; - } - local_irq_restore(flags); - addr -= begin; - printk("Secondary cache sized at %dK linesize %d bytes.\n", - (int) (addr >> 10), sc_lsize); - scache_size = addr; - return 1; -} - -static void __init setup_noscache_funcs(void) -{ - _clear_page = (void *)mips32_clear_page_dc; - _copy_page = (void *)mips32_copy_page_dc; - _flush_cache_all = mips32_flush_cache_all_pc; - ___flush_cache_all = mips32_flush_cache_all_pc; - _flush_cache_mm = mips32_flush_cache_mm_pc; - _flush_cache_range = mips32_flush_cache_range_pc; - _flush_cache_page = mips32_flush_cache_page_pc; - _flush_page_to_ram = mips32_flush_page_to_ram_pc; - - _flush_icache_page = mips32_flush_icache_page; - - _dma_cache_wback_inv = mips32_dma_cache_wback_inv_pc; - _dma_cache_wback = mips32_dma_cache_wback; - _dma_cache_inv = mips32_dma_cache_inv_pc; -} - -static void __init setup_scache_funcs(void) -{ - _flush_cache_all = mips32_flush_cache_all_sc; - ___flush_cache_all = mips32_flush_cache_all_sc; - _flush_cache_mm = mips32_flush_cache_mm_sc; - _flush_cache_range = mips32_flush_cache_range_sc; - _flush_cache_page = mips32_flush_cache_page_sc; - _flush_page_to_ram = mips32_flush_page_to_ram_sc; - _clear_page = (void *)mips32_clear_page_sc; - _copy_page = (void *)mips32_copy_page_sc; - - _flush_icache_page = mips32_flush_icache_page_s; - - _dma_cache_wback_inv = mips32_dma_cache_wback_inv_sc; - _dma_cache_wback = mips32_dma_cache_wback; - _dma_cache_inv = mips32_dma_cache_inv_sc; -} - -typedef int (*probe_func_t)(unsigned long); - -static inline void __init setup_scache(unsigned int config) -{ - probe_func_t probe_scache_kseg1; - int sc_present = 0; - - /* Maybe the cpu knows about a l2 cache? */ - probe_scache_kseg1 = (probe_func_t) (KSEG1ADDR(&probe_scache)); - sc_present = probe_scache_kseg1(config); - - if (sc_present) { - mips_cpu.scache.linesz = sc_lsize; - /* - * We cannot infer associativity - assume direct map - * unless probe template indicates otherwise - */ - if(!mips_cpu.scache.ways) mips_cpu.scache.ways = 1; - mips_cpu.scache.sets = - (scache_size / sc_lsize) / mips_cpu.scache.ways; - - setup_scache_funcs(); - return; - } - - setup_noscache_funcs(); -} - -static void __init probe_tlb(unsigned long config) -{ - unsigned long config1; - - if (!(config & (1 << 31))) { - /* - * Not a MIPS32 complainant CPU. - * Config 1 register not supported, we assume R4k style. - */ - mips_cpu.tlbsize = 48; - } else { - config1 = read_mips32_cp0_config1(); - if (!((config >> 7) & 3)) - panic("No MMU present"); - else - mips_cpu.tlbsize = ((config1 >> 25) & 0x3f) + 1; - } - - printk("Number of TLB entries %d.\n", mips_cpu.tlbsize); -} - -void __init ld_mmu_mips32(void) -{ - unsigned long config = read_32bit_cp0_register(CP0_CONFIG); - - printk("CPU revision is: %08x\n", read_32bit_cp0_register(CP0_PRID)); - -#ifdef CONFIG_MIPS_UNCACHED - change_cp0_config(CONF_CM_CMASK, CONF_CM_UNCACHED); -#else - change_cp0_config(CONF_CM_CMASK, CONF_CM_CACHABLE_NONCOHERENT); -#endif - - probe_icache(config); - probe_dcache(config); - setup_scache(config); - probe_tlb(config); - - _flush_cache_sigtramp = mips32_flush_cache_sigtramp; - _flush_icache_range = mips32_flush_icache_range; /* Ouch */ - - __flush_cache_all(); - write_32bit_cp0_register(CP0_WIRED, 0); - - /* - * You should never change this register: - * - The entire mm handling assumes the c0_pagemask register to - * be set for 4kb pages. - */ - write_32bit_cp0_register(CP0_PAGEMASK, PM_4K); - flush_tlb_all(); -} diff --git a/arch/mips/mm/pg-r3k.c b/arch/mips/mm/pg-r3k.c new file mode 100644 index 000000000000..dd2e86ee0f79 --- /dev/null +++ b/arch/mips/mm/pg-r3k.c @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2001 Ralf Baechle (ralf@gnu.org) + */ +#include <linux/sched.h> +#include <linux/mm.h> + +/* page functions */ +void r3k_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) + : "memory"); +} + +void r3k_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)); +} + +/* + * Initialize new page directory with pointers to invalid ptes + */ +void pgd_init(unsigned long page) +{ + unsigned long dummy1, dummy2; + + /* + * 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:\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" + "sw\t%2, 16(%0)\n\t" + "sw\t%2, 20(%0)\n\t" + "sw\t%2, 24(%0)\n\t" + "sw\t%2, 28(%0)\n\t" + "subu\t%1, 1\n\t" + "bnez\t%1, 1b\n\t" + "addiu\t%0, 32\n\t" + ".set\treorder" + :"=r" (dummy1), "=r" (dummy2) + :"r" ((unsigned long) invalid_pte_table), "0" (page), + "1" (USER_PTRS_PER_PGD / 8)); +} diff --git a/arch/mips/mm/pg-r4k.S b/arch/mips/mm/pg-r4k.S new file mode 100644 index 000000000000..73bfb2643b3b --- /dev/null +++ b/arch/mips/mm/pg-r4k.S @@ -0,0 +1,803 @@ +/* + * 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. + * + * r4xx0.c: R4000 processor variant specific MMU/Cache routines. + * + * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * Copyright (C) 1997, 1998, 1999, 2000 Ralf Baechle ralf@gnu.org + */ +#include <linux/config.h> +#include <asm/addrspace.h> +#include <asm/asm.h> +#include <asm/regdef.h> +#include <asm/cacheops.h> +#include <asm/mipsregs.h> +#include <asm/offset.h> + +#ifdef CONFIG_64BIT_PHYS_ADDR +#define PGD_SIZE 0x2000 +#else +#define PGD_SIZE 0x1000 +#endif + + .text + .set noat + +/* + * Zero an entire page. Basically a simple unrolled loop should do the + * job but we want more performance by saving memory bus bandwidth. We + * have five flavours of the routine available for: + * + * - 16byte cachelines and no second level cache + * - 32byte cachelines second level cache + * - a version which handles the buggy R4600 v1.x + * - a version which handles the buggy R4600 v2.0 + * - Finally a last version without fancy cache games for the SC and MC + * versions of R4000 and R4400. + */ + +LEAF(r4k_clear_page32_d16) + .set mips3 + addiu AT, a0, _PAGE_SIZE +1: cache Create_Dirty_Excl_D, (a0) + sw zero, (a0) + sw zero, 4(a0) + sw zero, 8(a0) + sw zero, 12(a0) + addiu a0, 32 + cache Create_Dirty_Excl_D, -16(a0) + sw zero, -16(a0) + sw zero, -12(a0) + sw zero, -8(a0) + sw zero, -4(a0) + bne AT, a0, 1b + jr ra + END(r4k_clear_page32_d16) + +LEAF(r4k_clear_page32_d32) + .set mips3 + addiu AT, a0, _PAGE_SIZE +1: cache Create_Dirty_Excl_D, (a0) + sw zero, (a0) + sw zero, 4(a0) + sw zero, 8(a0) + sw zero, 12(a0) + addiu a0, 32 + sw zero, -16(a0) + sw zero, -12(a0) + sw zero, -8(a0) + sw zero, -4(a0) + bne AT, a0, 1b + jr ra + END(r4k_clear_page32_d32) + +LEAF(r4k_clear_page_d16) + .set mips3 + addiu AT, a0, _PAGE_SIZE +1: cache Create_Dirty_Excl_D, (a0) + sd zero, (a0) + sd zero, 8(a0) + cache Create_Dirty_Excl_D, 16(a0) + sd zero, 16(a0) + sd zero, 24(a0) + addiu a0, 64 + cache Create_Dirty_Excl_D, -32(a0) + sd zero, -32(a0) + sd zero, -24(a0) + cache Create_Dirty_Excl_D, -16(a0) + sd zero, -16(a0) + sd zero, -8(a0) + bne AT, a0, 1b + jr ra + END(r4k_clear_page_d16) + +LEAF(r4k_clear_page_d32) + .set mips3 + addiu AT, a0, _PAGE_SIZE +1: cache Create_Dirty_Excl_D, (a0) + sd zero, (a0) + sd zero, 8(a0) + sd zero, 16(a0) + sd zero, 24(a0) + addiu a0, 64 + cache Create_Dirty_Excl_D, -32(a0) + sd zero, -32(a0) + sd zero, -24(a0) + sd zero, -16(a0) + sd zero, -8(a0) + bne AT, a0, 1b + jr ra + END(r4k_clear_page_d32) + +/* + * This flavour of r4k_clear_page is for the R4600 V1.x. Cite from the + * IDT R4600 V1.7 errata: + * + * 18. The CACHE instructions Hit_Writeback_Invalidate_D, Hit_Writeback_D, + * Hit_Invalidate_D and Create_Dirty_Excl_D should only be + * executed if there is no other dcache activity. If the dcache is + * accessed for another instruction immeidately preceding when these + * cache instructions are executing, it is possible that the dcache + * tag match outputs used by these cache instructions will be + * incorrect. These cache instructions should be preceded by at least + * four instructions that are not any kind of load or store + * instruction. + * + * This is not allowed: lw + * nop + * nop + * nop + * cache Hit_Writeback_Invalidate_D + * + * This is allowed: lw + * nop + * nop + * nop + * nop + * cache Hit_Writeback_Invalidate_D + */ + +LEAF(r4k_clear_page_r4600_v1) + .set mips3 + addiu AT, a0, _PAGE_SIZE +1: nop + nop + nop + nop + cache Create_Dirty_Excl_D, (a0) + sd zero, (a0) + sd zero, 8(a0) + sd zero, 16(a0) + sd zero, 24(a0) + addiu a0, 64 + nop + nop + nop + cache Create_Dirty_Excl_D, -32(a0) + sd zero, -32(a0) + sd zero, -24(a0) + sd zero, -16(a0) + sd zero, -8(a0) + bne AT, a0, 1b + jr ra + END(r4k_clear_page_r4600_v1) + +LEAF(r4k_clear_page_r4600_v2) + .set mips3 + mfc0 a1, CP0_STATUS + ori AT, a1, 1 + xori AT, 1 + mtc0 AT, CP0_STATUS + nop + nop + nop + + .set volatile + la AT, KSEG1 + lw zero, (AT) + .set novolatile + + addiu AT, a0, _PAGE_SIZE +1: cache Create_Dirty_Excl_D, (a0) + sd zero, (a0) + sd zero, 8(a0) + sd zero, 16(a0) + sd zero, 24(a0) + addiu a0, 64 + cache Create_Dirty_Excl_D, -32(a0) + sd zero, -32(a0) + sd zero, -24(a0) + sd zero, -16(a0) + sd zero, -8(a0) + bne AT, a0, 1b + + mfc0 AT, CP0_STATUS # local_irq_restore + andi a1, 1 + ori AT, 1 + xori AT, 1 + or a1, AT + mtc0 a1, CP0_STATUS + nop + nop + nop + + jr ra + END(r4k_clear_page_r4600_v2) + +/* + * The next 4 versions are optimized for all possible scache configurations + * of the SC / MC versions of R4000 and R4400 ... + * + * Todo: For even better performance we should have a routine optimized for + * every legal combination of dcache / scache linesize. When I (Ralf) tried + * this the kernel crashed shortly after mounting the root filesystem. CPU + * bug? Weirdo cache instruction semantics? + */ + +LEAF(r4k_clear_page_s16) + .set mips3 + addiu AT, a0, _PAGE_SIZE +1: cache Create_Dirty_Excl_SD, (a0) + sd zero, (a0) + sd zero, 8(a0) + cache Create_Dirty_Excl_SD, 16(a0) + sd zero, 16(a0) + sd zero, 24(a0) + addiu a0, 64 + cache Create_Dirty_Excl_SD, -32(a0) + sd zero, -32(a0) + sd zero, -24(a0) + cache Create_Dirty_Excl_SD, -16(a0) + sd zero, -16(a0) + sd zero, -8(a0) + bne AT, a0, 1b + jr ra + END(r4k_clear_page_s16) + +LEAF(r4k_clear_page_s32) + .set mips3 + addiu AT, a0, _PAGE_SIZE +1: cache Create_Dirty_Excl_SD, (a0) + sd zero, (a0) + sd zero, 8(a0) + sd zero, 16(a0) + sd zero, 24(a0) + addiu a0, 64 + cache Create_Dirty_Excl_SD, -32(a0) + sd zero, -32(a0) + sd zero, -24(a0) + sd zero, -16(a0) + sd zero, -8(a0) + bne AT, a0, 1b + jr ra + END(r4k_clear_page_s32) + +LEAF(r4k_clear_page_s64) + .set mips3 + addiu AT, a0, _PAGE_SIZE +1: cache Create_Dirty_Excl_SD, (a0) + sd zero, (a0) + sd zero, 8(a0) + sd zero, 16(a0) + sd zero, 24(a0) + addiu a0, 64 + sd zero, -32(a0) + sd zero, -24(a0) + sd zero, -16(a0) + sd zero, -8(a0) + bne AT, a0, 1b + jr ra + END(r4k_clear_page_s64) + +LEAF(r4k_clear_page_s128) + .set mips3 + addiu AT, a0, _PAGE_SIZE +1: cache Create_Dirty_Excl_SD, (a0) + sd zero, (a0) + sd zero, 8(a0) + sd zero, 16(a0) + sd zero, 24(a0) + sd zero, 32(a0) + sd zero, 40(a0) + sd zero, 48(a0) + sd zero, 56(a0) + addiu a0, 128 + sd zero, -64(a0) + sd zero, -56(a0) + sd zero, -48(a0) + sd zero, -40(a0) + sd zero, -32(a0) + sd zero, -24(a0) + sd zero, -16(a0) + sd zero, -8(a0) + bne AT, a0, 1b + jr ra + END(r4k_clear_page_s128) + +/* + * This is suboptimal for 32-bit kernels; we assume that R10000 is only used + * with 64-bit kernels. The prefetch offsets have been experimentally tuned + * an Origin 200. + */ +LEAF(andes_clear_page) + .set mips4 + LONG_ADDIU AT, a0, _PAGE_SIZE +1: pref 7, 512(a0) + sd zero, 0*SZREG(a0) + sd zero, 1*SZREG(a0) + sd zero, 2*SZREG(a0) + sd zero, 3*SZREG(a0) + LONG_ADDIU a0, a0, 8*SZREG + sd zero, -4*SZREG(a0) + sd zero, -3*SZREG(a0) + sd zero, -2*SZREG(a0) + sd zero, -1*SZREG(a0) + bne AT, a0, 1b + j ra + END(andes_clear_page) + .set mips0 + +/* + * This is still inefficient. We only can do better if we know the + * virtual address where the copy will be accessed. + */ + +LEAF(r4k_copy_page_d16) + .set mips3 + addiu AT, a0, _PAGE_SIZE +1: cache Create_Dirty_Excl_D, (a0) + lw a3, (a1) + lw a2, 4(a1) + lw v1, 8(a1) + lw v0, 12(a1) + sw a3, (a0) + sw a2, 4(a0) + sw v1, 8(a0) + sw v0, 12(a0) + cache Create_Dirty_Excl_D, 16(a0) + lw a3, 16(a1) + lw a2, 20(a1) + lw v1, 24(a1) + lw v0, 28(a1) + sw a3, 16(a0) + sw a2, 20(a0) + sw v1, 24(a0) + sw v0, 28(a0) + cache Create_Dirty_Excl_D, 32(a0) + addiu a0, 64 + addiu a1, 64 + lw a3, -32(a1) + lw a2, -28(a1) + lw v1, -24(a1) + lw v0, -20(a1) + sw a3, -32(a0) + sw a2, -28(a0) + sw v1, -24(a0) + sw v0, -20(a0) + cache Create_Dirty_Excl_D, -16(a0) + lw a3, -16(a1) + lw a2, -12(a1) + lw v1, -8(a1) + lw v0, -4(a1) + sw a3, -16(a0) + sw a2, -12(a0) + sw v1, -8(a0) + sw v0, -4(a0) + bne AT, a0, 1b + jr ra + END(r4k_copy_page_d16) + +LEAF(r4k_copy_page_d32) + .set mips3 + addiu AT, a0, _PAGE_SIZE +1: cache Create_Dirty_Excl_D, (a0) + lw a3, (a1) + lw a2, 4(a1) + lw v1, 8(a1) + lw v0, 12(a1) + sw a3, (a0) + sw a2, 4(a0) + sw v1, 8(a0) + sw v0, 12(a0) + lw a3, 16(a1) + lw a2, 20(a1) + lw v1, 24(a1) + lw v0, 28(a1) + sw a3, 16(a0) + sw a2, 20(a0) + sw v1, 24(a0) + sw v0, 28(a0) + cache Create_Dirty_Excl_D, 32(a0) + addiu a0, 64 + addiu a1, 64 + lw a3, -32(a1) + lw a2, -28(a1) + lw v1, -24(a1) + lw v0, -20(a1) + sw a3, -32(a0) + sw a2, -28(a0) + sw v1, -24(a0) + sw v0, -20(a0) + lw a3, -16(a1) + lw a2, -12(a1) + lw v1, -8(a1) + lw v0, -4(a1) + sw a3, -16(a0) + sw a2, -12(a0) + sw v1, -8(a0) + sw v0, -4(a0) + bne AT, a0, 1b + jr ra + END(r4k_copy_page_d32) + +/* + * Again a special version for the R4600 V1.x + */ + +LEAF(r4k_copy_page_r4600_v1) + .set mips3 + addiu AT, a0, _PAGE_SIZE +1: nop + nop + nop + nop + cache Create_Dirty_Excl_D, (a0) + lw a3, (a1) + lw a2, 4(a1) + lw v1, 8(a1) + lw v0, 12(a1) + sw a3, (a0) + sw a2, 4(a0) + sw v1, 8(a0) + sw v0, 12(a0) + lw a3, 16(a1) + lw a2, 20(a1) + lw v1, 24(a1) + lw v0, 28(a1) + sw a3, 16(a0) + sw a2, 20(a0) + sw v1, 24(a0) + sw v0, 28(a0) + nop + nop + nop + nop + cache Create_Dirty_Excl_D, 32(a0) + addiu a0, 64 + addiu a1, 64 + lw a3, -32(a1) + lw a2, -28(a1) + lw v1, -24(a1) + lw v0, -20(a1) + sw a3, -32(a0) + sw a2, -28(a0) + sw v1, -24(a0) + sw v0, -20(a0) + lw a3, -16(a1) + lw a2, -12(a1) + lw v1, -8(a1) + lw v0, -4(a1) + sw a3, -16(a0) + sw a2, -12(a0) + sw v1, -8(a0) + sw v0, -4(a0) + bne AT, a0, 1b + jr ra + END(r4k_copy_page_r4600_v1) + +LEAF(r4k_copy_page_r4600_v2) + .set mips3 + mfc0 v1, CP0_STATUS + ori AT, v1, 1 + xori AT, 1 + + mtc0 AT, CP0_STATUS + nop + nop + nop + + addiu AT, a0, _PAGE_SIZE +1: nop + nop + nop + nop + cache Create_Dirty_Excl_D, (a0) + lw t1, (a1) + lw t0, 4(a1) + lw a3, 8(a1) + lw a2, 12(a1) + sw t1, (a0) + sw t0, 4(a0) + sw a3, 8(a0) + sw a2, 12(a0) + lw t1, 16(a1) + lw t0, 20(a1) + lw a3, 24(a1) + lw a2, 28(a1) + sw t1, 16(a0) + sw t0, 20(a0) + sw a3, 24(a0) + sw a2, 28(a0) + nop + nop + nop + nop + cache Create_Dirty_Excl_D, 32(a0) + addiu a0, 64 + addiu a1, 64 + lw t1, -32(a1) + lw t0, -28(a1) + lw a3, -24(a1) + lw a2, -20(a1) + sw t1, -32(a0) + sw t0, -28(a0) + sw a3, -24(a0) + sw a2, -20(a0) + lw t1, -16(a1) + lw t0, -12(a1) + lw a3, -8(a1) + lw a2, -4(a1) + sw t1, -16(a0) + sw t0, -12(a0) + sw a3, -8(a0) + sw a2, -4(a0) + bne AT, a0, 1b + + mfc0 AT, CP0_STATUS # local_irq_restore + andi v1, 1 + ori AT, 1 + xori AT, 1 + or v1, AT + mtc0 v1, CP0_STATUS + nop + nop + nop + jr ra + END(r4k_copy_page_r4600_v2) + +/* + * These are for R4000SC / R4400MC + */ + +LEAF(r4k_copy_page_s16) + .set mips3 + addiu AT, a0, _PAGE_SIZE +1: cache Create_Dirty_Excl_SD, (a0) + lw a3, (a1) + lw a2, 4(a1) + lw v1, 8(a1) + lw v0, 12(a1) + sw a3, (a0) + sw a2, 4(a0) + sw v1, 8(a0) + sw v0, 12(a0) + cache Create_Dirty_Excl_SD, 16(a0) + lw a3, 16(a1) + lw a2, 20(a1) + lw v1, 24(a1) + lw v0, 28(a1) + sw a3, 16(a0) + sw a2, 20(a0) + sw v1, 24(a0) + sw v0, 28(a0) + cache Create_Dirty_Excl_SD, 32(a0) + addiu a0, 64 + addiu a1, 64 + lw a3, -32(a1) + lw a2, -28(a1) + lw v1, -24(a1) + lw v0, -20(a1) + sw a3, -32(a0) + sw a2, -28(a0) + sw v1, -24(a0) + sw v0, -20(a0) + cache Create_Dirty_Excl_SD, -16(a0) + lw a3, -16(a1) + lw a2, -12(a1) + lw v1, -8(a1) + lw v0, -4(a1) + sw a3, -16(a0) + sw a2, -12(a0) + sw v1, -8(a0) + sw v0, -4(a0) + bne AT, a0, 1b + jr ra + END(r4k_copy_page_s16) + +LEAF(r4k_copy_page_s32) + .set mips3 + addiu AT, a0, _PAGE_SIZE +1: cache Create_Dirty_Excl_SD, (a0) + lw a3, (a1) + lw a2, 4(a1) + lw v1, 8(a1) + lw v0, 12(a1) + sw a3, (a0) + sw a2, 4(a0) + sw v1, 8(a0) + sw v0, 12(a0) + lw a3, 16(a1) + lw a2, 20(a1) + lw v1, 24(a1) + lw v0, 28(a1) + sw a3, 16(a0) + sw a2, 20(a0) + sw v1, 24(a0) + sw v0, 28(a0) + cache Create_Dirty_Excl_SD, 32(a0) + addiu a0, 64 + addiu a1, 64 + lw a3, -32(a1) + lw a2, -28(a1) + lw v1, -24(a1) + lw v0, -20(a1) + sw a3, -32(a0) + sw a2, -28(a0) + sw v1, -24(a0) + sw v0, -20(a0) + lw a3, -16(a1) + lw a2, -12(a1) + lw v1, -8(a1) + lw v0, -4(a1) + sw a3, -16(a0) + sw a2, -12(a0) + sw v1, -8(a0) + sw v0, -4(a0) + bne AT, a0, 1b + jr ra + END(r4k_copy_page_s32) + +LEAF(r4k_copy_page_s64) + .set mips3 + addiu AT, a0, _PAGE_SIZE +1: cache Create_Dirty_Excl_SD, (a0) + lw a3, (a1) + lw a2, 4(a1) + lw v1, 8(a1) + lw v0, 12(a1) + sw a3, (a0) + sw a2, 4(a0) + sw v1, 8(a0) + sw v0, 12(a0) + lw a3, 16(a1) + lw a2, 20(a1) + lw v1, 24(a1) + lw v0, 28(a1) + sw a3, 16(a0) + sw a2, 20(a0) + sw v1, 24(a0) + sw v0, 28(a0) + addiu a0, 64 + addiu a1, 64 + lw a3, -32(a1) + lw a2, -28(a1) + lw v1, -24(a1) + lw v0, -20(a1) + sw a3, -32(a0) + sw a2, -28(a0) + sw v1, -24(a0) + sw v0, -20(a0) + lw a3, -16(a1) + lw a2, -12(a1) + lw v1, -8(a1) + lw v0, -4(a1) + sw a3, -16(a0) + sw a2, -12(a0) + sw v1, -8(a0) + sw v0, -4(a0) + bne AT, a0, 1b + jr ra + END(r4k_copy_page_s64) + +LEAF(r4k_copy_page_s128) + .set mips3 + addiu AT, a0, _PAGE_SIZE +1: cache Create_Dirty_Excl_SD, (a0) + lw a3, (a1) + lw a2, 4(a1) + lw v1, 8(a1) + lw v0, 12(a1) + sw a3, (a0) + sw a2, 4(a0) + sw v1, 8(a0) + sw v0, 12(a0) + lw a3, 16(a1) + lw a2, 20(a1) + lw v1, 24(a1) + lw v0, 28(a1) + sw a3, 16(a0) + sw a2, 20(a0) + sw v1, 24(a0) + sw v0, 28(a0) + lw a3, 32(a1) + lw a2, 36(a1) + lw v1, 40(a1) + lw v0, 44(a1) + sw a3, 32(a0) + sw a2, 36(a0) + sw v1, 40(a0) + sw v0, 44(a0) + lw a3, 48(a1) + lw a2, 52(a1) + lw v1, 56(a1) + lw v0, 60(a1) + sw a3, 48(a0) + sw a2, 52(a0) + sw v1, 56(a0) + sw v0, 60(a0) + addiu a0, 128 + addiu a1, 128 + lw a3, -64(a1) + lw a2, -60(a1) + lw v1, -56(a1) + lw v0, -52(a1) + sw a3, -64(a0) + sw a2, -60(a0) + sw v1, -56(a0) + sw v0, -52(a0) + lw a3, -48(a1) + lw a2, -44(a1) + lw v1, -40(a1) + lw v0, -36(a1) + sw a3, -48(a0) + sw a2, -44(a0) + sw v1, -40(a0) + sw v0, -36(a0) + lw a3, -32(a1) + lw a2, -28(a1) + lw v1, -24(a1) + lw v0, -20(a1) + sw a3, -32(a0) + sw a2, -28(a0) + sw v1, -24(a0) + sw v0, -20(a0) + lw a3, -16(a1) + lw a2, -12(a1) + lw v1, -8(a1) + lw v0, -4(a1) + sw a3, -16(a0) + sw a2, -12(a0) + sw v1, -8(a0) + sw v0, -4(a0) + bne AT, a0, 1b + jr ra + END(r4k_copy_page_s128) + + + .text + .set mips4 + .set noat + + +/* + * This is suboptimal for 32-bit kernels; we assume that R10000 is only used + * with 64-bit kernels. The prefetch offsets have been experimentally tuned + * an Origin 200. + */ +LEAF(andes_copy_page) + .set mips4 + LONG_ADDIU AT, a0, _PAGE_SIZE +1: pref 0, 2*128(a1) + pref 1, 2*128(a0) + LONG_L a3, 0*SZREG(a1) + LONG_L a2, 1*SZREG(a1) + LONG_L v1, 2*SZREG(a1) + LONG_L v0, 3*SZREG(a1) + LONG_S a3, 0*SZREG(a0) + LONG_S a2, 1*SZREG(a0) + LONG_S v1, 2*SZREG(a0) + LONG_S v0, 3*SZREG(a0) + LONG_ADDIU a0, a0, 8*SZREG + LONG_ADDIU a1, a1, 8*SZREG + LONG_L a3, -4*SZREG(a1) + LONG_L a2, -3*SZREG(a1) + LONG_L v1, -2*SZREG(a1) + LONG_L v0, -1*SZREG(a1) + LONG_S a3, -4*SZREG(a0) + LONG_S a2, -3*SZREG(a0) + LONG_S v1, -2*SZREG(a0) + LONG_S v0, -1*SZREG(a0) + bne AT, a0,1b + j ra + END(andes_copy_page) + .set mips0 + +/* This one still needs to receive cache optimizations */ +LEAF(pgd_init) + .set mips0 + addiu AT, a0, PGD_SIZE / 2 + la v0, invalid_pte_table +1: sw v0, (a0) + sw v0, 4(a0) + sw v0, 8(a0) + sw v0, 12(a0) + addiu a0, 32 + sw v0, -16(a0) + sw v0, -12(a0) + sw v0, -8(a0) + sw v0, -4(a0) + bne AT, a0, 1b + jr ra + END(pgd_init) diff --git a/arch/mips/mm/pg-sb1.c b/arch/mips/mm/pg-sb1.c new file mode 100644 index 000000000000..730fa4914d55 --- /dev/null +++ b/arch/mips/mm/pg-sb1.c @@ -0,0 +1,132 @@ +/* + * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * Copyright (C) 1997, 2001 Ralf Baechle (ralf@gnu.org) + * Copyright (C) 2000 Sibyte + * + * Written by Justin Carlson (carlson@sibyte.com) + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include <linux/config.h> +#include <asm/page.h> + +#ifdef CONFIG_SB1_PASS_1_WORKAROUNDS +#define SB1_PREF_LOAD_STREAMED_HINT "0" +#define SB1_PREF_STORE_STREAMED_HINT "1" +#else +#define SB1_PREF_LOAD_STREAMED_HINT "4" +#define SB1_PREF_STORE_STREAMED_HINT "5" +#endif + +/* These are the functions hooked by the memory management function pointers */ +void sb1_clear_page(void *page) +{ + /* + * JDCXXX - This should be bottlenecked by the write buffer, but these + * things tend to be mildly unpredictable...should check this on the + * performance model + * + * We prefetch 4 lines ahead. We're also "cheating" slightly here... + * since we know we're on an SB1, we force the assembler to take + * 64-bit operands to speed things up + */ + __asm__ __volatile__( + ".set push \n" + ".set noreorder \n" + ".set noat \n" + ".set mips4 \n" + " addiu $1, %0, %2 \n" /* Calculate the end of the page to clear */ +#ifdef CONFIG_CPU_HAS_PREFETCH + " pref " SB1_PREF_STORE_STREAMED_HINT ", 0(%0) \n" /* Prefetch the first 4 lines */ + " pref " SB1_PREF_STORE_STREAMED_HINT ", 32(%0) \n" + " pref " SB1_PREF_STORE_STREAMED_HINT ", 64(%0) \n" + " pref " SB1_PREF_STORE_STREAMED_HINT ", 96(%0) \n" +#endif + "1: sd $0, 0(%0) \n" /* Throw out a cacheline of 0's */ + " sd $0, 8(%0) \n" + " sd $0, 16(%0) \n" + " sd $0, 24(%0) \n" +#ifdef CONFIG_CPU_HAS_PREFETCH + " pref " SB1_PREF_STORE_STREAMED_HINT ",128(%0) \n" /* Prefetch 4 lines ahead */ +#endif + " bne $1, %0, 1b \n" + " addiu %0, %0, 32 \n" /* Next cacheline (This instruction better be short piped!) */ + ".set pop \n" + : "=r" (page) + : "0" (page), "I" (PAGE_SIZE-32) + : "memory"); + +} + +void sb1_copy_page(void *to, void *from) +{ + + /* + * This should be optimized in assembly...can't use ld/sd, though, + * because the top 32 bits could be nuked if we took an interrupt + * during the routine. And this is not a good place to be cli()'ing + * + * The pref's used here are using "streaming" hints, which cause the + * copied data to be kicked out of the cache sooner. A page copy often + * ends up copying a lot more data than is commonly used, so this seems + * to make sense in terms of reducing cache pollution, but I've no real + * performance data to back this up + */ + + __asm__ __volatile__( + ".set push \n" + ".set noreorder \n" + ".set noat \n" + ".set mips4 \n" + " addiu $1, %0, %4 \n" /* Calculate the end of the page to copy */ +#ifdef CONFIG_CPU_HAS_PREFETCH + " pref " SB1_PREF_LOAD_STREAMED_HINT ", 0(%0) \n" /* Prefetch the first 3 lines */ + " pref " SB1_PREF_STORE_STREAMED_HINT ", 0(%1) \n" + " pref " SB1_PREF_LOAD_STREAMED_HINT ", 32(%0) \n" + " pref " SB1_PREF_STORE_STREAMED_HINT ", 32(%1) \n" + " pref " SB1_PREF_LOAD_STREAMED_HINT ", 64(%0) \n" + " pref " SB1_PREF_STORE_STREAMED_HINT ", 64(%1) \n" +#endif + "1: lw $2, 0(%0) \n" /* Block copy a cacheline */ + " lw $3, 4(%0) \n" + " lw $4, 8(%0) \n" + " lw $5, 12(%0) \n" + " lw $6, 16(%0) \n" + " lw $7, 20(%0) \n" + " lw $8, 24(%0) \n" + " lw $9, 28(%0) \n" +#ifdef CONFIG_CPU_HAS_PREFETCH + " pref " SB1_PREF_LOAD_STREAMED_HINT ", 96(%0) \n" /* Prefetch ahead */ + " pref " SB1_PREF_STORE_STREAMED_HINT ", 96(%1) \n" +#endif + " sw $2, 0(%1) \n" + " sw $3, 4(%1) \n" + " sw $4, 8(%1) \n" + " sw $5, 12(%1) \n" + " sw $6, 16(%1) \n" + " sw $7, 20(%1) \n" + " sw $8, 24(%1) \n" + " sw $9, 28(%1) \n" + " addiu %1, %1, 32 \n" /* Next cacheline */ + " nop \n" /* Force next add to short pipe */ + " nop \n" /* Force next add to short pipe */ + " bne $1, %0, 1b \n" + " addiu %0, %0, 32 \n" /* Next cacheline */ + ".set pop \n" + : "=r" (to), "=r" (from) + : "0" (from), "1" (to), "I" (PAGE_SIZE-32) + : "$2","$3","$4","$5","$6","$7","$8","$9","memory"); +} diff --git a/arch/mips/mm/pgtable.c b/arch/mips/mm/pgtable.c new file mode 100644 index 000000000000..5b408bb144b7 --- /dev/null +++ b/arch/mips/mm/pgtable.c @@ -0,0 +1,33 @@ +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/swap.h> + +void show_mem(void) +{ + int pfn, total = 0, reserved = 0; + int shared = 0, cached = 0; + int highmem = 0; + struct page *page; + + printk("Mem-info:\n"); + show_free_areas(); + printk("Free swap: %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10)); + pfn = max_mapnr; + while (pfn-- > 0) { + page = pfn_to_page(pfn); + total++; + if (PageHighMem(page)) + highmem++; + if (PageReserved(page)) + reserved++; + else if (PageSwapCache(page)) + cached++; + else if (page_count(page)) + shared += page_count(page) - 1; + } + 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); +} diff --git a/arch/mips/mm/r2300.c b/arch/mips/mm/r2300.c deleted file mode 100644 index 055a86a8b1e4..000000000000 --- a/arch/mips/mm/r2300.c +++ /dev/null @@ -1,804 +0,0 @@ -/* - * r2300.c: R2000 and R3000 specific mmu/cache code. - * - * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) - * - * with a lot of changes to make this thing work for R3000s - * Tx39XX R4k style caches added. HK - * Copyright (C) 1998, 1999, 2000 Harald Koerfgen - * Copyright (C) 1998 Gleb Raiko & Vladimir Roganov - */ -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/mm.h> - -#include <asm/page.h> -#include <asm/pgtable.h> -#include <asm/mmu_context.h> -#include <asm/system.h> -#include <asm/isadep.h> -#include <asm/io.h> -#include <asm/wbflush.h> -#include <asm/bootinfo.h> -#include <asm/cpu.h> - -/* - * According to the paper written by D. Miller about Linux cache & TLB - * flush implementation, DMA/Driver coherence should be done at the - * driver layer. Thus, normally, we don't need flush dcache for R3000. - * Define this if driver does not handle cache consistency during DMA ops. - */ - -/* For R3000 cores with R4000 style caches */ -static unsigned long icache_size, dcache_size; /* Size in bytes */ -static unsigned long icache_lsize, dcache_lsize; /* Size in bytes */ -static unsigned long scache_size; - -#include <asm/cacheops.h> -#include <asm/r4kcache.h> - -#undef DEBUG_TLB -#undef DEBUG_CACHE - -/* page functions */ -void r3k_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 r3k_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)); -} - -unsigned long __init r3k_cache_size(unsigned long ca_flags) -{ - unsigned long flags, status, dummy, size; - volatile unsigned long *p; - - p = (volatile unsigned long *) KSEG0; - - flags = read_32bit_cp0_register(CP0_STATUS); - - /* isolate cache space */ - write_32bit_cp0_register(CP0_STATUS, (ca_flags|flags)&~ST0_IEC); - - *p = 0xa5a55a5a; - dummy = *p; - status = read_32bit_cp0_register(CP0_STATUS); - - if (dummy != 0xa5a55a5a || (status & ST0_CM)) { - size = 0; - } else { - for (size = 128; size <= 0x40000; size <<= 1) - *(p + size) = 0; - *p = -1; - for (size = 128; - (size <= 0x40000) && (*(p + size) == 0); - size <<= 1) - ; - if (size > 0x40000) - size = 0; - } - - write_32bit_cp0_register(CP0_STATUS, flags); - - return size * sizeof(*p); -} - -unsigned long __init r3k_cache_lsize(unsigned long ca_flags) -{ - unsigned long flags, status, lsize, i, j; - volatile unsigned long *p; - - p = (volatile unsigned long *) KSEG0; - - flags = read_32bit_cp0_register(CP0_STATUS); - - /* isolate cache space */ - write_32bit_cp0_register(CP0_STATUS, (ca_flags|flags)&~ST0_IEC); - - for (i = 0; i < 128; i++) - *(p + i) = 0; - *(volatile unsigned char *)p = 0; - for (lsize = 1; lsize < 128; lsize <<= 1) { - *(p + lsize); - status = read_32bit_cp0_register(CP0_STATUS); - if (!(status & ST0_CM)) - break; - } - for (i = 0; i < 128; i += lsize) - *(volatile unsigned char *)(p + i) = 0; - - write_32bit_cp0_register(CP0_STATUS, flags); - - return lsize * sizeof(*p); -} - -static void __init r3k_probe_cache(void) -{ - dcache_size = r3k_cache_size(ST0_ISC); - if (dcache_size) - dcache_lsize = r3k_cache_lsize(ST0_ISC); - - - icache_size = r3k_cache_size(ST0_ISC|ST0_SWC); - if (icache_size) - icache_lsize = r3k_cache_lsize(ST0_ISC|ST0_SWC); -} - -static void r3k_flush_icache_range(unsigned long start, unsigned long end) -{ - unsigned long size, i, flags; - volatile unsigned char *p = (char *)start; - - size = end - start; - if (size > icache_size) - size = icache_size; - - flags = read_32bit_cp0_register(CP0_STATUS); - - /* isolate cache space */ - write_32bit_cp0_register(CP0_STATUS, (ST0_ISC|ST0_SWC|flags)&~ST0_IEC); - - for (i = 0; i < size; i += 0x080) { - asm ( "sb\t$0,0x000(%0)\n\t" - "sb\t$0,0x004(%0)\n\t" - "sb\t$0,0x008(%0)\n\t" - "sb\t$0,0x00c(%0)\n\t" - "sb\t$0,0x010(%0)\n\t" - "sb\t$0,0x014(%0)\n\t" - "sb\t$0,0x018(%0)\n\t" - "sb\t$0,0x01c(%0)\n\t" - "sb\t$0,0x020(%0)\n\t" - "sb\t$0,0x024(%0)\n\t" - "sb\t$0,0x028(%0)\n\t" - "sb\t$0,0x02c(%0)\n\t" - "sb\t$0,0x030(%0)\n\t" - "sb\t$0,0x034(%0)\n\t" - "sb\t$0,0x038(%0)\n\t" - "sb\t$0,0x03c(%0)\n\t" - "sb\t$0,0x040(%0)\n\t" - "sb\t$0,0x044(%0)\n\t" - "sb\t$0,0x048(%0)\n\t" - "sb\t$0,0x04c(%0)\n\t" - "sb\t$0,0x050(%0)\n\t" - "sb\t$0,0x054(%0)\n\t" - "sb\t$0,0x058(%0)\n\t" - "sb\t$0,0x05c(%0)\n\t" - "sb\t$0,0x060(%0)\n\t" - "sb\t$0,0x064(%0)\n\t" - "sb\t$0,0x068(%0)\n\t" - "sb\t$0,0x06c(%0)\n\t" - "sb\t$0,0x070(%0)\n\t" - "sb\t$0,0x074(%0)\n\t" - "sb\t$0,0x078(%0)\n\t" - "sb\t$0,0x07c(%0)\n\t" - : : "r" (p) ); - p += 0x080; - } - - write_32bit_cp0_register(CP0_STATUS,flags); -} - -static void r3k_flush_dcache_range(unsigned long start, unsigned long end) -{ - unsigned long size, i, flags; - volatile unsigned char *p = (char *)start; - - size = end - start; - if (size > dcache_size) - size = dcache_size; - - flags = read_32bit_cp0_register(CP0_STATUS); - - /* isolate cache space */ - write_32bit_cp0_register(CP0_STATUS, (ST0_ISC|flags)&~ST0_IEC); - - for (i = 0; i < size; i += 0x080) { - asm ( "sb\t$0,0x000(%0)\n\t" - "sb\t$0,0x004(%0)\n\t" - "sb\t$0,0x008(%0)\n\t" - "sb\t$0,0x00c(%0)\n\t" - "sb\t$0,0x010(%0)\n\t" - "sb\t$0,0x014(%0)\n\t" - "sb\t$0,0x018(%0)\n\t" - "sb\t$0,0x01c(%0)\n\t" - "sb\t$0,0x020(%0)\n\t" - "sb\t$0,0x024(%0)\n\t" - "sb\t$0,0x028(%0)\n\t" - "sb\t$0,0x02c(%0)\n\t" - "sb\t$0,0x030(%0)\n\t" - "sb\t$0,0x034(%0)\n\t" - "sb\t$0,0x038(%0)\n\t" - "sb\t$0,0x03c(%0)\n\t" - "sb\t$0,0x040(%0)\n\t" - "sb\t$0,0x044(%0)\n\t" - "sb\t$0,0x048(%0)\n\t" - "sb\t$0,0x04c(%0)\n\t" - "sb\t$0,0x050(%0)\n\t" - "sb\t$0,0x054(%0)\n\t" - "sb\t$0,0x058(%0)\n\t" - "sb\t$0,0x05c(%0)\n\t" - "sb\t$0,0x060(%0)\n\t" - "sb\t$0,0x064(%0)\n\t" - "sb\t$0,0x068(%0)\n\t" - "sb\t$0,0x06c(%0)\n\t" - "sb\t$0,0x070(%0)\n\t" - "sb\t$0,0x074(%0)\n\t" - "sb\t$0,0x078(%0)\n\t" - "sb\t$0,0x07c(%0)\n\t" - : : "r" (p) ); - p += 0x080; - } - - write_32bit_cp0_register(CP0_STATUS,flags); -} - -static inline unsigned long get_phys_page (unsigned long addr, - struct mm_struct *mm) -{ - pgd_t *pgd; - pmd_t *pmd; - pte_t *pte; - unsigned long physpage; - - pgd = pgd_offset(mm, addr); - pmd = pmd_offset(pgd, addr); - pte = pte_offset(pmd, addr); - - if ((physpage = pte_val(*pte)) & _PAGE_VALID) - return KSEG0ADDR(physpage & PAGE_MASK); - - return 0; -} - -static inline void r3k_flush_cache_all(void) -{ - r3k_flush_icache_range(KSEG0, KSEG0 + icache_size); -} - -static void r3k_flush_cache_mm(struct mm_struct *mm) -{ - if (mm->context != 0) { - -#ifdef DEBUG_CACHE - printk("cmm[%d]", (int)mm->context); -#endif - r3k_flush_cache_all(); - } -} - -static void r3k_flush_cache_range(struct vm_area_struct *vma, unsigned long start, - unsigned long end) -{ - struct mm_struct *mm = vma->vm_mm; - - if (mm->context == 0) - return; - - start &= PAGE_MASK; -#ifdef DEBUG_CACHE - printk("crange[%d,%08lx,%08lx]", (int)mm->context, start, end); -#endif - if (mm->context != current->active_mm->context) { - flush_cache_all(); - } else { - unsigned long flags, physpage; - - save_and_cli(flags); - while (start < end) { - if ((physpage = get_phys_page(start, mm))) - r3k_flush_icache_range(physpage, - physpage + PAGE_SIZE); - start += PAGE_SIZE; - } - restore_flags(flags); - } -} - -static void r3k_flush_cache_page(struct vm_area_struct *vma, - unsigned long page) -{ - struct mm_struct *mm = vma->vm_mm; - - if (mm->context == 0) - return; - -#ifdef DEBUG_CACHE - printk("cpage[%d,%08lx]", (int)mm->context, page); -#endif - if (vma->vm_flags & VM_EXEC) { - unsigned long physpage; - - if ((physpage = get_phys_page(page, vma->vm_mm))) - r3k_flush_icache_range(physpage, physpage + PAGE_SIZE); - } -} - -static void r3k_flush_page_to_ram(struct page * page) -{ - /* - * Nothing to be done - */ -} - -static void r3k_flush_icache_page(struct vm_area_struct *vma, struct page *page) -{ - struct mm_struct *mm = vma->vm_mm; - unsigned long physpage; - - if (mm->context == 0) - return; - - if (!(vma->vm_flags & VM_EXEC)) - return; - -#ifdef DEBUG_CACHE - printk("cpage[%d,%08lx]", (int)mm->context, page); -#endif - - physpage = (unsigned long) page_address(page); - if (physpage) - r3k_flush_icache_range(physpage, physpage + PAGE_SIZE); -} - -static void r3k_flush_cache_sigtramp(unsigned long addr) -{ - unsigned long flags; - -#ifdef DEBUG_CACHE - printk("csigtramp[%08lx]", addr); -#endif - - flags = read_32bit_cp0_register(CP0_STATUS); - - write_32bit_cp0_register(CP0_STATUS, flags&~ST0_IEC); - - /* Fill the TLB to avoid an exception with caches isolated. */ - asm ( "lw\t$0,0x000(%0)\n\t" - "lw\t$0,0x004(%0)\n\t" - : : "r" (addr) ); - - write_32bit_cp0_register(CP0_STATUS, (ST0_ISC|ST0_SWC|flags)&~ST0_IEC); - - asm ( "sb\t$0,0x000(%0)\n\t" - "sb\t$0,0x004(%0)\n\t" - : : "r" (addr) ); - - write_32bit_cp0_register(CP0_STATUS, flags); -} - -static void r3k_dma_cache_wback_inv(unsigned long start, unsigned long size) -{ - wbflush(); - r3k_flush_dcache_range(start, start + size); -} - -/* TLB operations. */ -void flush_tlb_all(void) -{ - unsigned long flags; - unsigned long old_ctx; - int entry; - -#ifdef DEBUG_TLB - printk("[tlball]"); -#endif - - save_and_cli(flags); - old_ctx = (get_entryhi() & 0xfc0); - write_32bit_cp0_register(CP0_ENTRYLO0, 0); - for (entry = 8; entry < mips_cpu.tlbsize; entry++) { - write_32bit_cp0_register(CP0_INDEX, entry << 8); - write_32bit_cp0_register(CP0_ENTRYHI, ((entry | 0x80000) << 12)); - __asm__ __volatile__("tlbwi"); - } - set_entryhi(old_ctx); - restore_flags(flags); -} - -void flush_tlb_mm(struct mm_struct *mm) -{ - if (mm->context != 0) { - unsigned long flags; - -#ifdef DEBUG_TLB - printk("[tlbmm<%lu>]", (unsigned long) mm->context); -#endif - save_and_cli(flags); - get_new_mmu_context(mm, asid_cache); - if (mm == current->active_mm) - set_entryhi(mm->context & 0xfc0); - restore_flags(flags); - } -} - -void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, - unsigned long end) -{ - struct mm_struct *mm = vma->vm_mm; - - if (mm->context != 0) { - unsigned long flags; - int size; - -#ifdef DEBUG_TLB - printk("[tlbrange<%lu,0x%08lx,0x%08lx>]", - (mm->context & 0xfc0), start, end); -#endif - save_and_cli(flags); - size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; - if(size <= mips_cpu.tlbsize) { - int oldpid = (get_entryhi() & 0xfc0); - int newpid = (mm->context & 0xfc0); - - start &= PAGE_MASK; - end += (PAGE_SIZE - 1); - end &= PAGE_MASK; - while(start < end) { - int idx; - - set_entryhi(start | newpid); - start += PAGE_SIZE; - tlb_probe(); - idx = get_index(); - set_entrylo0(0); - set_entryhi(KSEG0); - if(idx < 0) - continue; - tlb_write_indexed(); - } - set_entryhi(oldpid); - } else { - get_new_mmu_context(mm, asid_cache); - if (mm == current->active_mm) - set_entryhi(mm->context & 0xfc0); - } - restore_flags(flags); - } -} - -void flush_tlb_page(struct vm_area_struct *vma, unsigned long page) -{ - if(vma->vm_mm->context != 0) { - unsigned long flags; - int oldpid, newpid, idx; - -#ifdef DEBUG_TLB - printk("[tlbpage<%lu,0x%08lx>]", vma->vm_mm->context, page); -#endif - newpid = (vma->vm_mm->context & 0xfc0); - page &= PAGE_MASK; - save_and_cli(flags); - oldpid = (get_entryhi() & 0xfc0); - set_entryhi(page | newpid); - tlb_probe(); - idx = get_index(); - set_entrylo0(0); - set_entryhi(KSEG0); - if(idx < 0) - goto finish; - tlb_write_indexed(); - -finish: - set_entryhi(oldpid); - restore_flags(flags); - } -} - -/* - * Initialize new page directory with pointers to invalid ptes - */ -void pgd_init(unsigned long page) -{ - unsigned long dummy1, dummy2; - - /* - * 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:\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" - "sw\t%2,16(%0)\n\t" - "sw\t%2,20(%0)\n\t" - "sw\t%2,24(%0)\n\t" - "sw\t%2,28(%0)\n\t" - "subu\t%1,1\n\t" - "bnez\t%1,1b\n\t" - "addiu\t%0,32\n\t" - ".set\treorder" - :"=r" (dummy1), - "=r" (dummy2) - :"r" ((unsigned long) invalid_pte_table), - "0" (page), - "1" (PAGE_SIZE/(sizeof(pmd_t)*8))); -} - -void update_mmu_cache(struct vm_area_struct * vma, unsigned long address, - pte_t pte) -{ - unsigned long flags; - pgd_t *pgdp; - pmd_t *pmdp; - pte_t *ptep; - int idx, pid; - - /* - * Handle debugger faulting in for debugee. - */ - if (current->active_mm != vma->vm_mm) - return; - - pid = get_entryhi() & 0xfc0; - -#ifdef DEBUG_TLB - if((pid != (vma->vm_mm->context & 0xfc0)) || (vma->vm_mm->context == 0)) { - printk("update_mmu_cache: Wheee, bogus tlbpid mmpid=%lu tlbpid=%d\n", - (vma->vm_mm->context & 0xfc0), pid); - } -#endif - - save_and_cli(flags); - address &= PAGE_MASK; - set_entryhi(address | (pid)); - pgdp = pgd_offset(vma->vm_mm, address); - tlb_probe(); - pmdp = pmd_offset(pgdp, address); - idx = get_index(); - ptep = pte_offset(pmdp, address); - set_entrylo0(pte_val(*ptep)); - set_entryhi(address | (pid)); - if(idx < 0) { - tlb_write_random(); -#if 0 - printk("[MISS]"); -#endif - } else { - tlb_write_indexed(); -#if 0 - printk("[HIT]"); -#endif - } - set_entryhi(pid); - restore_flags(flags); -} - -void show_regs(struct pt_regs * regs) -{ - /* - * Saved main processor registers - */ - printk("$0 : %08x %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n", - 0, (unsigned long) regs->regs[1], (unsigned long) regs->regs[2], - (unsigned long) regs->regs[3], (unsigned long) regs->regs[4], - (unsigned long) regs->regs[5], (unsigned long) regs->regs[6], - (unsigned long) regs->regs[7]); - printk("$8 : %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n", - (unsigned long) regs->regs[8], (unsigned long) regs->regs[9], - (unsigned long) regs->regs[10], (unsigned long) regs->regs[11], - (unsigned long) regs->regs[12], (unsigned long) regs->regs[13], - (unsigned long) regs->regs[14], (unsigned long) regs->regs[15]); - printk("$16: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n", - (unsigned long) regs->regs[16], (unsigned long) regs->regs[17], - (unsigned long) regs->regs[18], (unsigned long) regs->regs[19], - (unsigned long) regs->regs[20], (unsigned long) regs->regs[21], - (unsigned long) regs->regs[22], (unsigned long) regs->regs[23]); - printk("$24: %08lx %08lx %08lx %08lx %08lx %08lx\n", - (unsigned long) regs->regs[24], (unsigned long) regs->regs[25], - (unsigned long) regs->regs[28], (unsigned long) regs->regs[29], - (unsigned long) regs->regs[30], (unsigned long) regs->regs[31]); - - /* - * Saved cp0 registers - */ - printk("epc : %08lx %s\nStatus: %08x\nCause : %08x\n", - (unsigned long) regs->cp0_epc, - print_tainted(), - (unsigned int) regs->cp0_status, - (unsigned int) regs->cp0_cause); -} - -/* Todo: handle r4k-style TX39 TLB */ -void add_wired_entry(unsigned long entrylo0, unsigned long entrylo1, - unsigned long entryhi, unsigned long pagemask) -{ - unsigned long flags; - unsigned long old_ctx; - static unsigned long wired = 0; - - if (wired < 8) { - save_and_cli(flags); - old_ctx = get_entryhi() & 0xfc0; - set_entrylo0(entrylo0); - set_entryhi(entryhi); - set_index(wired); - wired++; - tlb_write_indexed(); - set_entryhi(old_ctx); - flush_tlb_all(); - restore_flags(flags); - } -} - -static void tx39_flush_icache_all(void ) -{ - - unsigned long start = KSEG0; - unsigned long end = (start + icache_size); - unsigned long dummy = 0; - - /* disable icache and stop streaming */ - __asm__ __volatile__( - ".set\tnoreorder\n\t" - "mfc0\t%0,$3\n\t" - "xori\t%0,32\n\t" - "mtc0\t%0,$3\n\t" - "j\t1f\n\t" - "nop\n\t" - "1:\t.set\treorder\n\t" - : : "r"(dummy)); - - /* invalidate icache */ - while (start < end) { - cache16_unroll32(start,Index_Invalidate_I); - start += 0x200; - } - - /* enable icache */ - __asm__ __volatile__( - ".set\tnoreorder\n\t" - "mfc0\t%0,$3\n\t" - "xori\t%0,32\n\t" - "mtc0\t%0,$3\n\t" - ".set\treorder\n\t" - : : "r"(dummy)); -} - -static __init void tx39_probe_cache(void) -{ - unsigned long config; - - config = read_32bit_cp0_register(CP0_CONF); - - icache_size = 1 << (10 + ((config >> 19) & 3)); - icache_lsize = 16; - - dcache_size = 1 << (10 + ((config >> 16) & 3)); - dcache_lsize = 4; -} - -void __init ld_mmu_r23000(void) -{ - unsigned long config; - - printk("CPU revision is: %08x\n", read_32bit_cp0_register(CP0_PRID)); - - _clear_page = r3k_clear_page; - _copy_page = r3k_copy_page; - - switch (mips_cpu.cputype) { - case CPU_R2000: - case CPU_R3000: - case CPU_R3000A: - case CPU_R3081: - case CPU_R3081E: - - r3k_probe_cache(); - - _flush_cache_all = r3k_flush_cache_all; - ___flush_cache_all = r3k_flush_cache_all; - _flush_cache_mm = r3k_flush_cache_mm; - _flush_cache_range = r3k_flush_cache_range; - _flush_cache_page = r3k_flush_cache_page; - _flush_cache_sigtramp = r3k_flush_cache_sigtramp; - _flush_page_to_ram = r3k_flush_page_to_ram; - _flush_icache_page = r3k_flush_icache_page; - _flush_icache_range = r3k_flush_icache_range; - - _dma_cache_wback_inv = r3k_dma_cache_wback_inv; - break; - - case CPU_TX3912: - case CPU_TX3922: - case CPU_TX3927: - - config=read_32bit_cp0_register(CP0_CONF); - config &= ~TX39_CONF_WBON; - write_32bit_cp0_register(CP0_CONF, config); - - tx39_probe_cache(); - - _flush_cache_all = tx39_flush_icache_all; - ___flush_cache_all = tx39_flush_icache_all; - _flush_cache_mm = tx39_flush_icache_all; - _flush_cache_range = tx39_flush_icache_all; - _flush_cache_page = tx39_flush_icache_all; - _flush_cache_sigtramp = tx39_flush_icache_all; - _flush_page_to_ram = r3k_flush_page_to_ram; - _flush_icache_page = tx39_flush_icache_all; - _flush_icache_range = tx39_flush_icache_all; - - _dma_cache_wback_inv = r3k_dma_cache_wback_inv; - - break; - } - - printk("Primary instruction cache %dkb, linesize %d bytes\n", - (int) (icache_size >> 10), (int) icache_lsize); - printk("Primary data cache %dkb, linesize %d bytes\n", - (int) (dcache_size >> 10), (int) dcache_lsize); - - flush_tlb_all(); -} diff --git a/arch/mips/mm/r4xx0.c b/arch/mips/mm/r4xx0.c deleted file mode 100644 index 95e8c09b43cc..000000000000 --- a/arch/mips/mm/r4xx0.c +++ /dev/null @@ -1,2712 +0,0 @@ -/* - * 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. - * - * r4xx0.c: R4000 processor variant specific MMU/Cache routines. - * - * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) - * Copyright (C) 1997, 1998, 1999, 2000 Ralf Baechle ralf@gnu.org - * - * To do: - * - * - this code is a overbloated pig - * - many of the bug workarounds are not efficient at all, but at - * least they are functional ... - */ -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/mm.h> - -#include <asm/bootinfo.h> -#include <asm/cpu.h> -#include <asm/bcache.h> -#include <asm/io.h> -#include <asm/page.h> -#include <asm/pgtable.h> -#include <asm/system.h> -#include <asm/mmu_context.h> - -/* CP0 hazard avoidance. */ -#define BARRIER __asm__ __volatile__(".set noreorder\n\t" \ - "nop; nop; nop; nop; nop; nop;\n\t" \ - ".set reorder\n\t") - -/* Primary cache parameters. */ -static int icache_size, dcache_size; /* Size in bytes */ -static int ic_lsize, dc_lsize; /* LineSize in bytes */ - -/* Secondary cache (if present) parameters. */ -static unsigned int scache_size, sc_lsize; /* Again, in bytes */ - -#include <asm/cacheops.h> -#include <asm/r4kcache.h> - -#undef DEBUG_CACHE - -/* - * Dummy cache handling routines for machines without boardcaches - */ -static void no_sc_noop(void) {} - -static struct bcache_ops no_sc_ops = { - (void *)no_sc_noop, (void *)no_sc_noop, - (void *)no_sc_noop, (void *)no_sc_noop -}; - -struct bcache_ops *bcops = &no_sc_ops; - -/* - * On processors with QED R4600 style two set assosicative cache - * this is the bit which selects the way in the cache for the - * indexed cachops. - */ -#define icache_waybit (icache_size >> 1) -#define dcache_waybit (dcache_size >> 1) - -/* - * Zero an entire page. Basically a simple unrolled loop should do the - * job but we want more performance by saving memory bus bandwidth. We - * have five flavours of the routine available for: - * - * - 16byte cachelines and no second level cache - * - 32byte cachelines second level cache - * - a version which handles the buggy R4600 v1.x - * - a version which handles the buggy R4600 v2.0 - * - Finally a last version without fancy cache games for the SC and MC - * versions of R4000 and R4400. - */ - -static void r4k_clear_page_d16(void * page) -{ - __asm__ __volatile__( - ".set\tnoreorder\n\t" - ".set\tnoat\n\t" - ".set\tmips3\n\t" - "daddiu\t$1,%0,%2\n" - "1:\tcache\t%3,(%0)\n\t" - "sd\t$0,(%0)\n\t" - "sd\t$0,8(%0)\n\t" - "cache\t%3,16(%0)\n\t" - "sd\t$0,16(%0)\n\t" - "sd\t$0,24(%0)\n\t" - "daddiu\t%0,64\n\t" - "cache\t%3,-32(%0)\n\t" - "sd\t$0,-32(%0)\n\t" - "sd\t$0,-24(%0)\n\t" - "cache\t%3,-16(%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\tmips0\n\t" - ".set\tat\n\t" - ".set\treorder" - :"=r" (page) - :"0" (page), - "I" (PAGE_SIZE), - "i" (Create_Dirty_Excl_D) - :"$1","memory"); -} - -static void r4k_clear_page_d32(void * page) -{ - __asm__ __volatile__( - ".set\tnoreorder\n\t" - ".set\tnoat\n\t" - ".set\tmips3\n\t" - "daddiu\t$1,%0,%2\n" - "1:\tcache\t%3,(%0)\n\t" - "sd\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" - "cache\t%3,-32(%0)\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\tmips0\n\t" - ".set\tat\n\t" - ".set\treorder" - :"=r" (page) - :"0" (page), - "I" (PAGE_SIZE), - "i" (Create_Dirty_Excl_D) - :"$1","memory"); -} - - -/* - * This flavour of r4k_clear_page is for the R4600 V1.x. Cite from the - * IDT R4600 V1.7 errata: - * - * 18. The CACHE instructions Hit_Writeback_Invalidate_D, Hit_Writeback_D, - * Hit_Invalidate_D and Create_Dirty_Excl_D should only be - * executed if there is no other dcache activity. If the dcache is - * accessed for another instruction immeidately preceding when these - * cache instructions are executing, it is possible that the dcache - * tag match outputs used by these cache instructions will be - * incorrect. These cache instructions should be preceded by at least - * four instructions that are not any kind of load or store - * instruction. - * - * This is not allowed: lw - * nop - * nop - * nop - * cache Hit_Writeback_Invalidate_D - * - * This is allowed: lw - * nop - * nop - * nop - * nop - * cache Hit_Writeback_Invalidate_D - */ -static void r4k_clear_page_r4600_v1(void * page) -{ - __asm__ __volatile__( - ".set\tnoreorder\n\t" - ".set\tnoat\n\t" - ".set\tmips3\n\t" - "daddiu\t$1,%0,%2\n" - "1:\tnop\n\t" - "nop\n\t" - "nop\n\t" - "nop\n\t" - "cache\t%3,(%0)\n\t" - "sd\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" - "nop\n\t" - "nop\n\t" - "nop\n\t" - "cache\t%3,-32(%0)\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\tmips0\n\t" - ".set\tat\n\t" - ".set\treorder" - :"=r" (page) - :"0" (page), - "I" (PAGE_SIZE), - "i" (Create_Dirty_Excl_D) - :"$1","memory"); -} - -/* - * And this one is for the R4600 V2.0 - */ -static void r4k_clear_page_r4600_v2(void * page) -{ - unsigned int flags; - - local_irq_save(flags); - *(volatile unsigned int *)KSEG1; - __asm__ __volatile__( - ".set\tnoreorder\n\t" - ".set\tnoat\n\t" - ".set\tmips3\n\t" - "daddiu\t$1,%0,%2\n" - "1:\tcache\t%3,(%0)\n\t" - "sd\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" - "cache\t%3,-32(%0)\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\tmips0\n\t" - ".set\tat\n\t" - ".set\treorder" - :"=r" (page) - :"0" (page), - "I" (PAGE_SIZE), - "i" (Create_Dirty_Excl_D) - :"$1","memory"); - local_irq_restore(flags); -} - -/* - * The next 4 versions are optimized for all possible scache configurations - * of the SC / MC versions of R4000 and R4400 ... - * - * Todo: For even better performance we should have a routine optimized for - * every legal combination of dcache / scache linesize. When I (Ralf) tried - * this the kernel crashed shortly after mounting the root filesystem. CPU - * bug? Weirdo cache instruction semantics? - */ -static void r4k_clear_page_s16(void * page) -{ - __asm__ __volatile__( - ".set\tnoreorder\n\t" - ".set\tnoat\n\t" - ".set\tmips3\n\t" - "daddiu\t$1,%0,%2\n" - "1:\tcache\t%3,(%0)\n\t" - "sd\t$0,(%0)\n\t" - "sd\t$0,8(%0)\n\t" - "cache\t%3,16(%0)\n\t" - "sd\t$0,16(%0)\n\t" - "sd\t$0,24(%0)\n\t" - "daddiu\t%0,64\n\t" - "cache\t%3,-32(%0)\n\t" - "sd\t$0,-32(%0)\n\t" - "sd\t$0,-24(%0)\n\t" - "cache\t%3,-16(%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\tmips0\n\t" - ".set\tat\n\t" - ".set\treorder" - :"=r" (page) - :"0" (page), - "I" (PAGE_SIZE), - "i" (Create_Dirty_Excl_SD) - :"$1","memory"); -} - -static void r4k_clear_page_s32(void * page) -{ - __asm__ __volatile__( - ".set\tnoreorder\n\t" - ".set\tnoat\n\t" - ".set\tmips3\n\t" - "daddiu\t$1,%0,%2\n" - "1:\tcache\t%3,(%0)\n\t" - "sd\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" - "cache\t%3,-32(%0)\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\tmips0\n\t" - ".set\tat\n\t" - ".set\treorder" - :"=r" (page) - :"0" (page), - "I" (PAGE_SIZE), - "i" (Create_Dirty_Excl_SD) - :"$1","memory"); -} - -static void r4k_clear_page_s64(void * page) -{ - __asm__ __volatile__( - ".set\tnoreorder\n\t" - ".set\tnoat\n\t" - ".set\tmips3\n\t" - "daddiu\t$1,%0,%2\n" - "1:\tcache\t%3,(%0)\n\t" - "sd\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\tmips0\n\t" - ".set\tat\n\t" - ".set\treorder" - :"=r" (page) - :"0" (page), - "I" (PAGE_SIZE), - "i" (Create_Dirty_Excl_SD) - :"$1","memory"); -} - -static void r4k_clear_page_s128(void * page) -{ - __asm__ __volatile__( - ".set\tnoreorder\n\t" - ".set\tnoat\n\t" - ".set\tmips3\n\t" - "daddiu\t$1,%0,%2\n" - "1:\tcache\t%3,(%0)\n\t" - "sd\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" - "sd\t$0,32(%0)\n\t" - "sd\t$0,40(%0)\n\t" - "sd\t$0,48(%0)\n\t" - "sd\t$0,56(%0)\n\t" - "daddiu\t%0,128\n\t" - "sd\t$0,-64(%0)\n\t" - "sd\t$0,-56(%0)\n\t" - "sd\t$0,-48(%0)\n\t" - "sd\t$0,-40(%0)\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\tmips0\n\t" - ".set\tat\n\t" - ".set\treorder" - :"=r" (page) - :"0" (page), - "I" (PAGE_SIZE), - "i" (Create_Dirty_Excl_SD) - :"$1","memory"); -} - - -/* - * This is still inefficient. We only can do better if we know the - * virtual address where the copy will be accessed. - */ - -static void r4k_copy_page_d16(void * to, void * from) -{ - unsigned long dummy1, dummy2; - unsigned long reg1, reg2, reg3, reg4; - - __asm__ __volatile__( - ".set\tnoreorder\n\t" - ".set\tnoat\n\t" - ".set\tmips3\n\t" - "daddiu\t$1,%0,%8\n" - "1:\tcache\t%9,(%0)\n\t" - "lw\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" - "cache\t%9,16(%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" - "cache\t%9,32(%0)\n\t" - "daddiu\t%0,64\n\t" - "daddiu\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" - "cache\t%9,-16(%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\tmips0\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), - "i" (Create_Dirty_Excl_D)); -} - -static void r4k_copy_page_d32(void * to, void * from) -{ - unsigned long dummy1, dummy2; - unsigned long reg1, reg2, reg3, reg4; - - __asm__ __volatile__( - ".set\tnoreorder\n\t" - ".set\tnoat\n\t" - ".set\tmips3\n\t" - "daddiu\t$1,%0,%8\n" - "1:\tcache\t%9,(%0)\n\t" - "lw\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" - "cache\t%9,32(%0)\n\t" - "daddiu\t%0,64\n\t" - "daddiu\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\tmips0\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), - "i" (Create_Dirty_Excl_D)); -} - -/* - * Again a special version for the R4600 V1.x - */ -static void r4k_copy_page_r4600_v1(void * to, void * from) -{ - unsigned long dummy1, dummy2; - unsigned long reg1, reg2, reg3, reg4; - - __asm__ __volatile__( - ".set\tnoreorder\n\t" - ".set\tnoat\n\t" - ".set\tmips3\n\t" - "daddiu\t$1,%0,%8\n" - "1:\tnop\n\t" - "nop\n\t" - "nop\n\t" - "nop\n\t" - "\tcache\t%9,(%0)\n\t" - "lw\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" - "nop\n\t" - "nop\n\t" - "nop\n\t" - "nop\n\t" - "cache\t%9,32(%0)\n\t" - "daddiu\t%0,64\n\t" - "daddiu\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\tmips0\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), - "i" (Create_Dirty_Excl_D)); -} - -static void r4k_copy_page_r4600_v2(void * to, void * from) -{ - unsigned long dummy1, dummy2; - unsigned long reg1, reg2, reg3, reg4; - unsigned int flags; - - local_irq_save(flags); - __asm__ __volatile__( - ".set\tnoreorder\n\t" - ".set\tnoat\n\t" - ".set\tmips3\n\t" - "daddiu\t$1,%0,%8\n" - "1:\tnop\n\t" - "nop\n\t" - "nop\n\t" - "nop\n\t" - "\tcache\t%9,(%0)\n\t" - "lw\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" - "nop\n\t" - "nop\n\t" - "nop\n\t" - "nop\n\t" - "cache\t%9,32(%0)\n\t" - "daddiu\t%0,64\n\t" - "daddiu\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\tmips0\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), - "i" (Create_Dirty_Excl_D)); - local_irq_restore(flags); -} - -/* - * These are for R4000SC / R4400MC - */ -static void r4k_copy_page_s16(void * to, void * from) -{ - unsigned long dummy1, dummy2; - unsigned long reg1, reg2, reg3, reg4; - - __asm__ __volatile__( - ".set\tnoreorder\n\t" - ".set\tnoat\n\t" - ".set\tmips3\n\t" - "daddiu\t$1,%0,%8\n" - "1:\tcache\t%9,(%0)\n\t" - "lw\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" - "cache\t%9,16(%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" - "cache\t%9,32(%0)\n\t" - "daddiu\t%0,64\n\t" - "daddiu\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" - "cache\t%9,-16(%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\tmips0\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), - "i" (Create_Dirty_Excl_SD)); -} - -static void r4k_copy_page_s32(void * to, void * from) -{ - unsigned long dummy1, dummy2; - unsigned long reg1, reg2, reg3, reg4; - - __asm__ __volatile__( - ".set\tnoreorder\n\t" - ".set\tnoat\n\t" - ".set\tmips3\n\t" - "daddiu\t$1,%0,%8\n" - "1:\tcache\t%9,(%0)\n\t" - "lw\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" - "cache\t%9,32(%0)\n\t" - "daddiu\t%0,64\n\t" - "daddiu\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\tmips0\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), - "i" (Create_Dirty_Excl_SD)); -} - -static void r4k_copy_page_s64(void * to, void * from) -{ - unsigned long dummy1, dummy2; - unsigned long reg1, reg2, reg3, reg4; - - __asm__ __volatile__( - ".set\tnoreorder\n\t" - ".set\tnoat\n\t" - ".set\tmips3\n\t" - "daddiu\t$1,%0,%8\n" - "1:\tcache\t%9,(%0)\n\t" - "lw\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" - "daddiu\t%0,64\n\t" - "daddiu\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\tmips0\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), - "i" (Create_Dirty_Excl_SD)); -} - -static void r4k_copy_page_s128(void * to, void * from) -{ - unsigned long dummy1, dummy2; - unsigned long reg1, reg2, reg3, reg4; - - __asm__ __volatile__( - ".set\tnoreorder\n\t" - ".set\tnoat\n\t" - ".set\tmips3\n\t" - "daddiu\t$1,%0,%8\n" - "1:\tcache\t%9,(%0)\n\t" - "lw\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" - "lw\t%2,32(%1)\n\t" - "lw\t%3,36(%1)\n\t" - "lw\t%4,40(%1)\n\t" - "lw\t%5,44(%1)\n\t" - "sw\t%2,32(%0)\n\t" - "sw\t%3,36(%0)\n\t" - "sw\t%4,40(%0)\n\t" - "sw\t%5,44(%0)\n\t" - "lw\t%2,48(%1)\n\t" - "lw\t%3,52(%1)\n\t" - "lw\t%4,56(%1)\n\t" - "lw\t%5,60(%1)\n\t" - "sw\t%2,48(%0)\n\t" - "sw\t%3,52(%0)\n\t" - "sw\t%4,56(%0)\n\t" - "sw\t%5,60(%0)\n\t" - "daddiu\t%0,128\n\t" - "daddiu\t%1,128\n\t" - "lw\t%2,-64(%1)\n\t" - "lw\t%3,-60(%1)\n\t" - "lw\t%4,-56(%1)\n\t" - "lw\t%5,-52(%1)\n\t" - "sw\t%2,-64(%0)\n\t" - "sw\t%3,-60(%0)\n\t" - "sw\t%4,-56(%0)\n\t" - "sw\t%5,-52(%0)\n\t" - "lw\t%2,-48(%1)\n\t" - "lw\t%3,-44(%1)\n\t" - "lw\t%4,-40(%1)\n\t" - "lw\t%5,-36(%1)\n\t" - "sw\t%2,-48(%0)\n\t" - "sw\t%3,-44(%0)\n\t" - "sw\t%4,-40(%0)\n\t" - "sw\t%5,-36(%0)\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\tmips0\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), - "i" (Create_Dirty_Excl_SD)); -} - - -/* - * If you think for one second that this stuff coming up is a lot - * of bulky code eating too many kernel cache lines. Think _again_. - * - * Consider: - * 1) Taken branches have a 3 cycle penalty on R4k - * 2) The branch itself is a real dead cycle on even R4600/R5000. - * 3) Only one of the following variants of each type is even used by - * the kernel based upon the cache parameters we detect at boot time. - * - * QED. - */ - -static inline void r4k_flush_cache_all_s16d16i16(void) -{ - unsigned long flags; - - local_irq_save(flags); - blast_dcache16(); blast_icache16(); blast_scache16(); - local_irq_restore(flags); -} - -static inline void r4k_flush_cache_all_s32d16i16(void) -{ - unsigned long flags; - - local_irq_save(flags); - blast_dcache16(); blast_icache16(); blast_scache32(); - local_irq_restore(flags); -} - -static inline void r4k_flush_cache_all_s64d16i16(void) -{ - unsigned long flags; - - local_irq_save(flags); - blast_dcache16(); blast_icache16(); blast_scache64(); - local_irq_restore(flags); -} - -static inline void r4k_flush_cache_all_s128d16i16(void) -{ - unsigned long flags; - - local_irq_save(flags); - blast_dcache16(); blast_icache16(); blast_scache128(); - local_irq_restore(flags); -} - -static inline void r4k_flush_cache_all_s32d32i32(void) -{ - unsigned long flags; - - local_irq_save(flags); - blast_dcache32(); blast_icache32(); blast_scache32(); - local_irq_restore(flags); -} - -static inline void r4k_flush_cache_all_s64d32i32(void) -{ - unsigned long flags; - - local_irq_save(flags); - blast_dcache32(); blast_icache32(); blast_scache64(); - local_irq_restore(flags); -} - -static inline void r4k_flush_cache_all_s128d32i32(void) -{ - unsigned long flags; - - local_irq_save(flags); - blast_dcache32(); blast_icache32(); blast_scache128(); - local_irq_restore(flags); -} - -static inline void r4k_flush_cache_all_d16i16(void) -{ - unsigned long flags; - - local_irq_save(flags); - blast_dcache16(); blast_icache16(); - local_irq_restore(flags); -} - -static inline void r4k_flush_cache_all_d32i32(void) -{ - unsigned long flags; - - local_irq_save(flags); - blast_dcache32(); blast_icache32(); - local_irq_restore(flags); -} - -static void -r4k_flush_cache_range_s16d16i16(struct vm_area_struct *vma, - unsigned long start, - unsigned long end) -{ - struct mm_struct *mm = vma->vm_mm; - unsigned long flags; - - if (mm->context == 0) - return; - - start &= PAGE_MASK; -#ifdef DEBUG_CACHE - printk("crange[%d,%08lx,%08lx]", (int)mm->context, start, end); -#endif - if (vma) { - if (mm->context != current->active_mm->context) { - r4k_flush_cache_all_s16d16i16(); - } else { - pgd_t *pgd; - pmd_t *pmd; - pte_t *pte; - - local_irq_save(flags); - while(start < end) { - pgd = pgd_offset(mm, start); - pmd = pmd_offset(pgd, start); - pte = pte_offset(pmd, start); - - if(pte_val(*pte) & _PAGE_VALID) - blast_scache16_page(start); - start += PAGE_SIZE; - } - local_irq_restore(flags); - } - } -} - -static void -r4k_flush_cache_range_s32d16i16(struct vm_area_struct *vma, - unsigned long start, - unsigned long end) -{ - struct mm_struct *mm = vma->vm_mm; - unsigned long flags; - - if (mm->context == 0) - return; - - start &= PAGE_MASK; -#ifdef DEBUG_CACHE - printk("crange[%d,%08lx,%08lx]", (int)mm->context, start, end); -#endif - if (vma) { - if (mm->context != current->active_mm->context) { - r4k_flush_cache_all_s32d16i16(); - } else { - pgd_t *pgd; - pmd_t *pmd; - pte_t *pte; - - local_irq_save(flags); - while(start < end) { - pgd = pgd_offset(mm, start); - pmd = pmd_offset(pgd, start); - pte = pte_offset(pmd, start); - - if(pte_val(*pte) & _PAGE_VALID) - blast_scache32_page(start); - start += PAGE_SIZE; - } - local_irq_restore(flags); - } - } -} - -static void r4k_flush_cache_range_s64d16i16(struct vm_area_struct *vma, - unsigned long start, - unsigned long end) -{ - struct mm_struct *mm = vma->vm_mm; - unsigned long flags; - - if (mm->context == 0) - return; - - start &= PAGE_MASK; -#ifdef DEBUG_CACHE - printk("crange[%d,%08lx,%08lx]", (int)mm->context, start, end); -#endif - if(vma) { - if (mm->context != current->active_mm->context) { - r4k_flush_cache_all_s64d16i16(); - } else { - pgd_t *pgd; - pmd_t *pmd; - pte_t *pte; - - local_irq_save(flags); - while(start < end) { - pgd = pgd_offset(mm, start); - pmd = pmd_offset(pgd, start); - pte = pte_offset(pmd, start); - - if(pte_val(*pte) & _PAGE_VALID) - blast_scache64_page(start); - start += PAGE_SIZE; - } - local_irq_restore(flags); - } - } -} - -static void r4k_flush_cache_range_s128d16i16(struct vm_area_struct *vma, - unsigned long start, - unsigned long end) -{ - struct mm_struct *mm = vma->vm_mm; - unsigned long flags; - - if (mm->context == 0) - return; - - start &= PAGE_MASK; -#ifdef DEBUG_CACHE - printk("crange[%d,%08lx,%08lx]", (int)mm->context, start, end); -#endif - if (vma) { - if (mm->context != current->active_mm->context) { - r4k_flush_cache_all_s128d16i16(); - } else { - pgd_t *pgd; - pmd_t *pmd; - pte_t *pte; - - local_irq_save(flags); - while(start < end) { - pgd = pgd_offset(mm, start); - pmd = pmd_offset(pgd, start); - pte = pte_offset(pmd, start); - - if(pte_val(*pte) & _PAGE_VALID) - blast_scache128_page(start); - start += PAGE_SIZE; - } - local_irq_restore(flags); - } - } -} - -static void r4k_flush_cache_range_s32d32i32(struct vm_area_struct *vma, - unsigned long start, - unsigned long end) -{ - struct mm_struct *mm = vma->vm_mm; - unsigned long flags; - - if (mm->context == 0) - return; - - start &= PAGE_MASK; -#ifdef DEBUG_CACHE - printk("crange[%d,%08lx,%08lx]", (int)mm->context, start, end); -#endif - if (vma) { - if (mm->context != current->active_mm->context) { - r4k_flush_cache_all_s32d32i32(); - } else { - pgd_t *pgd; - pmd_t *pmd; - pte_t *pte; - - local_irq_save(flags); - while(start < end) { - pgd = pgd_offset(mm, start); - pmd = pmd_offset(pgd, start); - pte = pte_offset(pmd, start); - - if(pte_val(*pte) & _PAGE_VALID) - blast_scache32_page(start); - start += PAGE_SIZE; - } - local_irq_restore(flags); - } - } -} - -static void r4k_flush_cache_range_s64d32i32(struct vm_area_struct *vma, - unsigned long start, - unsigned long end) -{ - struct mm_struct *mm = vma->vm_mm; - unsigned long flags; - - if (mm->context == 0) - return; - - start &= PAGE_MASK; -#ifdef DEBUG_CACHE - printk("crange[%d,%08lx,%08lx]", (int)mm->context, start, end); -#endif - if (vma) { - if (mm->context != current->active_mm->context) { - r4k_flush_cache_all_s64d32i32(); - } else { - pgd_t *pgd; - pmd_t *pmd; - pte_t *pte; - - local_irq_save(flags); - while(start < end) { - pgd = pgd_offset(mm, start); - pmd = pmd_offset(pgd, start); - pte = pte_offset(pmd, start); - - if(pte_val(*pte) & _PAGE_VALID) - blast_scache64_page(start); - start += PAGE_SIZE; - } - local_irq_restore(flags); - } - } -} - -static void r4k_flush_cache_range_s128d32i32(struct vm_area_struct *vma, - unsigned long start, - unsigned long end) -{ - struct mm_struct *mm = vma->vm_mm; - unsigned long flags; - - if (mm->context == 0) - return; - - start &= PAGE_MASK; -#ifdef DEBUG_CACHE - printk("crange[%d,%08lx,%08lx]", (int)mm->context, start, end); -#endif - if (vma) { - if (mm->context != current->active_mm->context) { - r4k_flush_cache_all_s128d32i32(); - } else { - pgd_t *pgd; - pmd_t *pmd; - pte_t *pte; - - local_irq_save(flags); - while(start < end) { - pgd = pgd_offset(mm, start); - pmd = pmd_offset(pgd, start); - pte = pte_offset(pmd, start); - - if(pte_val(*pte) & _PAGE_VALID) - blast_scache128_page(start); - start += PAGE_SIZE; - } - local_irq_restore(flags); - } - } -} - -static void r4k_flush_cache_range_d16i16(struct mm_struct *vma, - unsigned long start, - unsigned long end) -{ - struct mm_struct *mm = vma->vm_mm; - - if (mm->context != 0) { - unsigned long flags; - -#ifdef DEBUG_CACHE - printk("crange[%d,%08lx,%08lx]", (int)mm->context, start, end); -#endif - local_irq_save(flags); - blast_dcache16(); blast_icache16(); - local_irq_restore(flags); - } -} - -static void r4k_flush_cache_range_d32i32(struct vm_area_struct *vma, - unsigned long start, - unsigned long end) -{ - struct mm_struct *mm = vma->vm_mm; - - if (mm->context != 0) { - unsigned long flags; - -#ifdef DEBUG_CACHE - printk("crange[%d,%08lx,%08lx]", (int)mm->context, start, end); -#endif - local_irq_save(flags); - blast_dcache32(); blast_icache32(); - local_irq_restore(flags); - } -} - -/* - * On architectures like the Sparc, we could get rid of lines in - * the cache created only by a certain context, but on the MIPS - * (and actually certain Sparc's) we cannot. - */ -static void r4k_flush_cache_mm_s16d16i16(struct mm_struct *mm) -{ - if (mm->context != 0) { -#ifdef DEBUG_CACHE - printk("cmm[%d]", (int)mm->context); -#endif - r4k_flush_cache_all_s16d16i16(); - } -} - -static void r4k_flush_cache_mm_s32d16i16(struct mm_struct *mm) -{ - if (mm->context != 0) { -#ifdef DEBUG_CACHE - printk("cmm[%d]", (int)mm->context); -#endif - r4k_flush_cache_all_s32d16i16(); - } -} - -static void r4k_flush_cache_mm_s64d16i16(struct mm_struct *mm) -{ - if (mm->context != 0) { -#ifdef DEBUG_CACHE - printk("cmm[%d]", (int)mm->context); -#endif - r4k_flush_cache_all_s64d16i16(); - } -} - -static void r4k_flush_cache_mm_s128d16i16(struct mm_struct *mm) -{ - if (mm->context != 0) { -#ifdef DEBUG_CACHE - printk("cmm[%d]", (int)mm->context); -#endif - r4k_flush_cache_all_s128d16i16(); - } -} - -static void r4k_flush_cache_mm_s32d32i32(struct mm_struct *mm) -{ - if (mm->context != 0) { -#ifdef DEBUG_CACHE - printk("cmm[%d]", (int)mm->context); -#endif - r4k_flush_cache_all_s32d32i32(); - } -} - -static void r4k_flush_cache_mm_s64d32i32(struct mm_struct *mm) -{ - if (mm->context != 0) { -#ifdef DEBUG_CACHE - printk("cmm[%d]", (int)mm->context); -#endif - r4k_flush_cache_all_s64d32i32(); - } -} - -static void r4k_flush_cache_mm_s128d32i32(struct mm_struct *mm) -{ - if (mm->context != 0) { -#ifdef DEBUG_CACHE - printk("cmm[%d]", (int)mm->context); -#endif - r4k_flush_cache_all_s128d32i32(); - } -} - -static void r4k_flush_cache_mm_d16i16(struct mm_struct *mm) -{ - if (mm->context != 0) { -#ifdef DEBUG_CACHE - printk("cmm[%d]", (int)mm->context); -#endif - r4k_flush_cache_all_d16i16(); - } -} - -static void r4k_flush_cache_mm_d32i32(struct mm_struct *mm) -{ - if (mm->context != 0) { -#ifdef DEBUG_CACHE - printk("cmm[%d]", (int)mm->context); -#endif - r4k_flush_cache_all_d32i32(); - } -} - -static void r4k_flush_cache_page_s16d16i16(struct vm_area_struct *vma, - unsigned long page) -{ - struct mm_struct *mm = vma->vm_mm; - unsigned long flags; - pgd_t *pgdp; - pmd_t *pmdp; - pte_t *ptep; - - /* - * If ownes no valid ASID yet, cannot possibly have gotten - * this page into the cache. - */ - if (mm->context == 0) - return; - -#ifdef DEBUG_CACHE - printk("cpage[%d,%08lx]", (int)mm->context, page); -#endif - local_irq_save(flags); - page &= PAGE_MASK; - pgdp = pgd_offset(mm, page); - pmdp = pmd_offset(pgdp, page); - ptep = pte_offset(pmdp, page); - - /* - * If the page isn't marked valid, the page cannot possibly be - * in the cache. - */ - if (!(pte_val(*ptep) & _PAGE_VALID)) - goto out; - - /* - * Doing flushes for another ASID than the current one is - * too difficult since stupid R4k caches do a TLB translation - * for every cache flush operation. So we do indexed flushes - * in that case, which doesn't overly flush the cache too much. - */ - if (mm->context != current->active_mm->context) { - /* - * Do indexed flush, too much work to get the (possible) - * tlb refills to work correctly. - */ - page = (KSEG0 + (page & (scache_size - 1))); - blast_dcache16_page_indexed(page); - blast_scache16_page_indexed(page); - } else - blast_scache16_page(page); -out: - local_irq_restore(flags); -} - -static void r4k_flush_cache_page_s32d16i16(struct vm_area_struct *vma, - unsigned long page) -{ - struct mm_struct *mm = vma->vm_mm; - unsigned long flags; - pgd_t *pgdp; - pmd_t *pmdp; - pte_t *ptep; - - /* - * If ownes no valid ASID yet, cannot possibly have gotten - * this page into the cache. - */ - if (mm->context == 0) - return; - -#ifdef DEBUG_CACHE - printk("cpage[%d,%08lx]", (int)mm->context, page); -#endif - local_irq_save(flags); - page &= PAGE_MASK; - pgdp = pgd_offset(mm, page); - pmdp = pmd_offset(pgdp, page); - ptep = pte_offset(pmdp, page); - - /* If the page isn't marked valid, the page cannot possibly be - * in the cache. - */ - if (!(pte_val(*ptep) & _PAGE_VALID)) - goto out; - - /* - * Doing flushes for another ASID than the current one is - * too difficult since stupid R4k caches do a TLB translation - * for every cache flush operation. So we do indexed flushes - * in that case, which doesn't overly flush the cache too much. - */ - if (mm->context != current->active_mm->context) { - /* - * Do indexed flush, too much work to get the (possible) - * tlb refills to work correctly. - */ - page = (KSEG0 + (page & (scache_size - 1))); - blast_dcache16_page_indexed(page); - blast_scache32_page_indexed(page); - } else - blast_scache32_page(page); -out: - local_irq_restore(flags); -} - -static void r4k_flush_cache_page_s64d16i16(struct vm_area_struct *vma, - unsigned long page) -{ - struct mm_struct *mm = vma->vm_mm; - unsigned long flags; - pgd_t *pgdp; - pmd_t *pmdp; - pte_t *ptep; - - /* - * If ownes no valid ASID yet, cannot possibly have gotten - * this page into the cache. - */ - if (mm->context == 0) - return; - -#ifdef DEBUG_CACHE - printk("cpage[%d,%08lx]", (int)mm->context, page); -#endif - local_irq_save(flags); - page &= PAGE_MASK; - pgdp = pgd_offset(mm, page); - pmdp = pmd_offset(pgdp, page); - ptep = pte_offset(pmdp, page); - - /* If the page isn't marked valid, the page cannot possibly be - * in the cache. - */ - if (!(pte_val(*ptep) & _PAGE_VALID)) - goto out; - - /* - * Doing flushes for another ASID than the current one is - * too difficult since stupid R4k caches do a TLB translation - * for every cache flush operation. So we do indexed flushes - * in that case, which doesn't overly flush the cache too much. - */ - if (mm->context != current->active_mm->context) { - /* - * Do indexed flush, too much work to get the (possible) - * tlb refills to work correctly. - */ - page = (KSEG0 + (page & (scache_size - 1))); - blast_dcache16_page_indexed(page); - blast_scache64_page_indexed(page); - } else - blast_scache64_page(page); -out: - local_irq_restore(flags); -} - -static void r4k_flush_cache_page_s128d16i16(struct vm_area_struct *vma, - unsigned long page) -{ - struct mm_struct *mm = vma->vm_mm; - unsigned long flags; - pgd_t *pgdp; - pmd_t *pmdp; - pte_t *ptep; - - /* - * If ownes no valid ASID yet, cannot possibly have gotten - * this page into the cache. - */ - if (mm->context == 0) - return; - -#ifdef DEBUG_CACHE - printk("cpage[%d,%08lx]", (int)mm->context, page); -#endif - local_irq_save(flags); - page &= PAGE_MASK; - pgdp = pgd_offset(mm, page); - pmdp = pmd_offset(pgdp, page); - ptep = pte_offset(pmdp, page); - - /* - * If the page isn't marked valid, the page cannot possibly be - * in the cache. - */ - if (!(pte_val(*ptep) & _PAGE_VALID)) - goto out; - - /* - * Doing flushes for another ASID than the current one is - * too difficult since stupid R4k caches do a TLB translation - * for every cache flush operation. So we do indexed flushes - * in that case, which doesn't overly flush the cache too much. - */ - if (mm->context != current->active_mm->context) { - /* - * Do indexed flush, too much work to get the (possible) - * tlb refills to work correctly. - */ - page = (KSEG0 + (page & (scache_size - 1))); - blast_dcache16_page_indexed(page); - blast_scache128_page_indexed(page); - } else - blast_scache128_page(page); -out: - local_irq_restore(flags); -} - -static void r4k_flush_cache_page_s32d32i32(struct vm_area_struct *vma, - unsigned long page) -{ - struct mm_struct *mm = vma->vm_mm; - unsigned long flags; - pgd_t *pgdp; - pmd_t *pmdp; - pte_t *ptep; - - /* - * If ownes no valid ASID yet, cannot possibly have gotten - * this page into the cache. - */ - if (mm->context == 0) - return; - -#ifdef DEBUG_CACHE - printk("cpage[%d,%08lx]", (int)mm->context, page); -#endif - local_irq_save(flags); - page &= PAGE_MASK; - pgdp = pgd_offset(mm, page); - pmdp = pmd_offset(pgdp, page); - ptep = pte_offset(pmdp, page); - - /* - * If the page isn't marked valid, the page cannot possibly be - * in the cache. - */ - if (!(pte_val(*ptep) & _PAGE_VALID)) - goto out; - - /* - * Doing flushes for another ASID than the current one is - * too difficult since stupid R4k caches do a TLB translation - * for every cache flush operation. So we do indexed flushes - * in that case, which doesn't overly flush the cache too much. - */ - if (mm->context != current->active_mm->context) { - /* - * Do indexed flush, too much work to get the (possible) - * tlb refills to work correctly. - */ - page = (KSEG0 + (page & (scache_size - 1))); - blast_dcache32_page_indexed(page); - blast_scache32_page_indexed(page); - } else - blast_scache32_page(page); -out: - local_irq_restore(flags); -} - -static void r4k_flush_cache_page_s64d32i32(struct vm_area_struct *vma, - unsigned long page) -{ - struct mm_struct *mm = vma->vm_mm; - unsigned long flags; - pgd_t *pgdp; - pmd_t *pmdp; - pte_t *ptep; - - /* - * If ownes no valid ASID yet, cannot possibly have gotten - * this page into the cache. - */ - if (mm->context == 0) - return; - -#ifdef DEBUG_CACHE - printk("cpage[%d,%08lx]", (int)mm->context, page); -#endif - local_irq_save(flags); - page &= PAGE_MASK; - pgdp = pgd_offset(mm, page); - pmdp = pmd_offset(pgdp, page); - ptep = pte_offset(pmdp, page); - - /* - * If the page isn't marked valid, the page cannot possibly be - * in the cache. - */ - if (!(pte_val(*ptep) & _PAGE_VALID)) - goto out; - - /* - * Doing flushes for another ASID than the current one is - * too difficult since stupid R4k caches do a TLB translation - * for every cache flush operation. So we do indexed flushes - * in that case, which doesn't overly flush the cache too much. - */ - if (mm->context != current->active_mm->context) { - /* - * Do indexed flush, too much work to get the (possible) - * tlb refills to work correctly. - */ - page = (KSEG0 + (page & (scache_size - 1))); - blast_dcache32_page_indexed(page); - blast_scache64_page_indexed(page); - } else - blast_scache64_page(page); -out: - local_irq_restore(flags); -} - -static void r4k_flush_cache_page_s128d32i32(struct vm_area_struct *vma, - unsigned long page) -{ - struct mm_struct *mm = vma->vm_mm; - unsigned long flags; - pgd_t *pgdp; - pmd_t *pmdp; - pte_t *ptep; - - /* - * If ownes no valid ASID yet, cannot possibly have gotten - * this page into the cache. - */ - if (mm->context == 0) - return; - -#ifdef DEBUG_CACHE - printk("cpage[%d,%08lx]", (int)mm->context, page); -#endif - local_irq_save(flags); - page &= PAGE_MASK; - pgdp = pgd_offset(mm, page); - pmdp = pmd_offset(pgdp, page); - ptep = pte_offset(pmdp, page); - - /* - * If the page isn't marked valid, the page cannot possibly be - * in the cache. - */ - if (!(pte_val(*ptep) & _PAGE_VALID)) - goto out; - - /* - * Doing flushes for another ASID than the current one is - * too difficult since stupid R4k caches do a TLB translation - * for every cache flush operation. So we do indexed flushes - * in that case, which doesn't overly flush the cache too much. - */ - if (mm->context != current->active_mm->context) { - /* Do indexed flush, too much work to get the (possible) - * tlb refills to work correctly. - */ - page = (KSEG0 + (page & (scache_size - 1))); - blast_dcache32_page_indexed(page); - blast_scache128_page_indexed(page); - } else - blast_scache128_page(page); -out: - local_irq_restore(flags); -} - -static void r4k_flush_cache_page_d16i16(struct vm_area_struct *vma, - unsigned long page) -{ - struct mm_struct *mm = vma->vm_mm; - unsigned long flags; - pgd_t *pgdp; - pmd_t *pmdp; - pte_t *ptep; - - /* - * If ownes no valid ASID yet, cannot possibly have gotten - * this page into the cache. - */ - if (mm->context == 0) - return; - -#ifdef DEBUG_CACHE - printk("cpage[%d,%08lx]", (int)mm->context, page); -#endif - local_irq_save(flags); - page &= PAGE_MASK; - pgdp = pgd_offset(mm, page); - pmdp = pmd_offset(pgdp, page); - ptep = pte_offset(pmdp, page); - - /* - * If the page isn't marked valid, the page cannot possibly be - * in the cache. - */ - if (!(pte_val(*ptep) & _PAGE_VALID)) - goto out; - - /* - * Doing flushes for another ASID than the current one is - * too difficult since stupid R4k caches do a TLB translation - * for every cache flush operation. So we do indexed flushes - * in that case, which doesn't overly flush the cache too much. - */ - if (mm == current->active_mm) { - blast_dcache16_page(page); - } else { - /* Do indexed flush, too much work to get the (possible) - * tlb refills to work correctly. - */ - page = (KSEG0 + (page & (dcache_size - 1))); - blast_dcache16_page_indexed(page); - } -out: - local_irq_restore(flags); -} - -static void r4k_flush_cache_page_d32i32(struct vm_area_struct *vma, - unsigned long page) -{ - struct mm_struct *mm = vma->vm_mm; - unsigned long flags; - pgd_t *pgdp; - pmd_t *pmdp; - pte_t *ptep; - - /* - * If ownes no valid ASID yet, cannot possibly have gotten - * this page into the cache. - */ - if (mm->context == 0) - return; - -#ifdef DEBUG_CACHE - printk("cpage[%d,%08lx]", (int)mm->context, page); -#endif - local_irq_save(flags); - page &= PAGE_MASK; - pgdp = pgd_offset(mm, page); - pmdp = pmd_offset(pgdp, page); - ptep = pte_offset(pmdp, page); - - /* - * If the page isn't marked valid, the page cannot possibly be - * in the cache. - */ - if (!(pte_val(*ptep) & _PAGE_PRESENT)) - goto out; - - /* - * Doing flushes for another ASID than the current one is - * too difficult since stupid R4k caches do a TLB translation - * for every cache flush operation. So we do indexed flushes - * in that case, which doesn't overly flush the cache too much. - */ - if ((mm == current->active_mm) && (pte_val(*ptep) & _PAGE_VALID)) { - blast_dcache32_page(page); - } else { - /* - * Do indexed flush, too much work to get the (possible) - * tlb refills to work correctly. - */ - page = (KSEG0 + (page & (dcache_size - 1))); - blast_dcache32_page_indexed(page); - } -out: - local_irq_restore(flags); -} - -static void r4k_flush_cache_page_d32i32_r4600(struct vm_area_struct *vma, - unsigned long page) -{ - struct mm_struct *mm = vma->vm_mm; - unsigned long flags; - pgd_t *pgdp; - pmd_t *pmdp; - pte_t *ptep; - - /* - * If ownes no valid ASID yet, cannot possibly have gotten - * this page into the cache. - */ - if (mm->context == 0) - return; - -#ifdef DEBUG_CACHE - printk("cpage[%d,%08lx]", (int)mm->context, page); -#endif - local_irq_save(flags); - page &= PAGE_MASK; - pgdp = pgd_offset(mm, page); - pmdp = pmd_offset(pgdp, page); - ptep = pte_offset(pmdp, page); - - /* - * If the page isn't marked valid, the page cannot possibly be - * in the cache. - */ - if (!(pte_val(*ptep) & _PAGE_PRESENT)) - goto out; - - /* - * Doing flushes for another ASID than the current one is - * too difficult since stupid R4k caches do a TLB translation - * for every cache flush operation. So we do indexed flushes - * in that case, which doesn't overly flush the cache too much. - */ - if ((mm == current->active_mm) && (pte_val(*ptep) & _PAGE_VALID)) { - blast_dcache32_page(page); - } else { - /* Do indexed flush, too much work to get the (possible) - * tlb refills to work correctly. - */ - page = (KSEG0 + (page & (dcache_size - 1))); - blast_dcache32_page_indexed(page); - blast_dcache32_page_indexed(page ^ dcache_waybit); - } -out: - local_irq_restore(flags); -} - -/* 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. - * 3) In KSEG1, no flush necessary. - */ -static void r4k_flush_page_to_ram_s16(struct page *page) -{ - blast_scache16_page((unsigned long)page_address(page)); -} - -static void r4k_flush_page_to_ram_s32(struct page *page) -{ - blast_scache32_page((unsigned long)page_address(page)); -} - -static void r4k_flush_page_to_ram_s64(struct page *page) -{ - blast_scache64_page((unsigned long)page_address(page)); -} - -static void r4k_flush_page_to_ram_s128(struct page *page) -{ - blast_scache128_page((unsigned long)page_address(page)); -} - -static void r4k_flush_page_to_ram_d16(struct page *page) -{ - blast_dcache16_page((unsigned long)page_address(page)); -} - -static void r4k_flush_page_to_ram_d32(struct page *page) -{ - blast_dcache32_page((unsigned long)page_address(page)); -} - -static void r4k_flush_page_to_ram_d32_r4600(struct page *page) -{ - unsigned long flags; - - local_irq_save(flags); /* For R4600 v1.7 bug. */ - blast_dcache32_page((unsigned long)page_address(page)); - local_irq_restore(flags); -} - -static void -r4k_flush_icache_page_s(struct vm_area_struct *vma, struct page *page) -{ - /* - * We did an scache flush therefore PI is already clean. - */ -} - -static void -r4k_flush_icache_range(unsigned long start, unsigned long end) -{ - flush_cache_all(); -} - -/* - * Ok, this seriously sucks. We use them to flush a user page but don't - * know the virtual address, so we have to blast away the whole icache - * which is significantly more expensive than the real thing. - */ -static void -r4k_flush_icache_page_p(struct vm_area_struct *vma, struct page *page) -{ - if (!(vma->vm_flags & VM_EXEC)) - return; - - flush_cache_all(); -} - -/* - * Writeback and invalidate the primary cache dcache before DMA. - * - * R4600 v2.0 bug: "The CACHE instructions Hit_Writeback_Inv_D, - * Hit_Writeback_D, Hit_Invalidate_D and Create_Dirty_Exclusive_D will only - * operate correctly if the internal data cache refill buffer is empty. These - * CACHE instructions should be separated from any potential data cache miss - * by a load instruction to an uncached address to empty the response buffer." - * (Revision 2.0 device errata from IDT available on http://www.idt.com/ - * in .pdf format.) - */ -static void -r4k_dma_cache_wback_inv_pc(unsigned long addr, unsigned long size) -{ - unsigned long end, a; - unsigned int flags; - - if (size >= dcache_size) { - flush_cache_all(); - } else { - /* Workaround for R4600 bug. See comment above. */ - local_irq_save(flags); - *(volatile unsigned long *)KSEG1; - - a = addr & ~(dc_lsize - 1); - end = (addr + size) & ~(dc_lsize - 1); - while (1) { - flush_dcache_line(a); /* Hit_Writeback_Inv_D */ - if (a == end) break; - a += dc_lsize; - } - local_irq_restore(flags); - } - bc_wback_inv(addr, size); -} - -static void -r4k_dma_cache_wback_inv_sc(unsigned long addr, unsigned long size) -{ - unsigned long end, a; - - if (size >= scache_size) { - flush_cache_all(); - return; - } - - a = addr & ~(sc_lsize - 1); - end = (addr + size) & ~(sc_lsize - 1); - while (1) { - flush_scache_line(a); /* Hit_Writeback_Inv_SD */ - if (a == end) break; - a += sc_lsize; - } -} - -static void -r4k_dma_cache_inv_pc(unsigned long addr, unsigned long size) -{ - unsigned long end, a; - unsigned int flags; - - if (size >= dcache_size) { - flush_cache_all(); - } else { - /* Workaround for R4600 bug. See comment above. */ - local_irq_save(flags); - *(volatile unsigned long *)KSEG1; - - a = addr & ~(dc_lsize - 1); - end = (addr + size) & ~(dc_lsize - 1); - while (1) { - flush_dcache_line(a); /* Hit_Writeback_Inv_D */ - if (a == end) break; - a += dc_lsize; - } - local_irq_restore(flags); - } - - bc_inv(addr, size); -} - -static void -r4k_dma_cache_inv_sc(unsigned long addr, unsigned long size) -{ - unsigned long end, a; - - if (size >= scache_size) { - flush_cache_all(); - return; - } - - a = addr & ~(sc_lsize - 1); - end = (addr + size) & ~(sc_lsize - 1); - while (1) { - flush_scache_line(a); /* Hit_Writeback_Inv_SD */ - if (a == end) break; - a += sc_lsize; - } -} - -static void -r4k_dma_cache_wback(unsigned long addr, unsigned long size) -{ - panic("r4k_dma_cache called - should not happen.\n"); -} - -/* - * While we're protected against bad userland addresses we don't care - * very much about what happens in that case. Usually a segmentation - * fault will dump the process later on anyway ... - */ -static void r4k_flush_cache_sigtramp(unsigned long addr) -{ - __asm__ __volatile__("nop;nop;nop;nop"); /* R4600 V1.7 */ - protected_writeback_dcache_line(addr & ~(dc_lsize - 1)); - protected_flush_icache_line(addr & ~(ic_lsize - 1)); -} - -static void r4600v20k_flush_cache_sigtramp(unsigned long addr) -{ - unsigned int flags; - - local_irq_save(flags); - - /* Clear internal cache refill buffer */ - *(volatile unsigned int *)KSEG1; - - protected_writeback_dcache_line(addr & ~(dc_lsize - 1)); - protected_flush_icache_line(addr & ~(ic_lsize - 1)); - - local_irq_restore(flags); -} - -#undef DEBUG_TLB -#undef DEBUG_TLBUPDATE - -void flush_tlb_all(void) -{ - unsigned long flags; - unsigned long old_ctx; - int entry; - -#ifdef DEBUG_TLB - printk("[tlball]"); -#endif - - local_irq_save(flags); - /* Save old context and create impossible VPN2 value */ - old_ctx = (get_entryhi() & 0xff); - set_entryhi(KSEG0); - set_entrylo0(0); - set_entrylo1(0); - BARRIER; - - entry = get_wired(); - - /* Blast 'em all away. */ - while(entry < mips_cpu.tlbsize) { - set_index(entry); - BARRIER; - tlb_write_indexed(); - BARRIER; - entry++; - } - BARRIER; - set_entryhi(old_ctx); - local_irq_restore(flags); -} - -void flush_tlb_mm(struct mm_struct *mm) -{ - if (mm->context != 0) { - unsigned long flags; - -#ifdef DEBUG_TLB - printk("[tlbmm<%d>]", mm->context); -#endif - local_irq_save(flags); - get_new_mmu_context(mm, asid_cache); - if (mm == current->active_mm) - set_entryhi(mm->context & 0xff); - local_irq_restore(flags); - } -} - -void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, - unsigned long end) -{ - struct mm_struct *mm = vma->vm_mm; - - if (mm->context != 0) { - unsigned long flags; - int size; - -#ifdef DEBUG_TLB - printk("[tlbrange<%02x,%08lx,%08lx>]", (mm->context & 0xff), - start, end); -#endif - local_irq_save(flags); - size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; - size = (size + 1) >> 1; - if(size <= mips_cpu.tlbsize/2) { - int oldpid = (get_entryhi() & 0xff); - int newpid = (mm->context & 0xff); - - start &= (PAGE_MASK << 1); - end += ((PAGE_SIZE << 1) - 1); - end &= (PAGE_MASK << 1); - while(start < end) { - int idx; - - set_entryhi(start | newpid); - start += (PAGE_SIZE << 1); - BARRIER; - tlb_probe(); - BARRIER; - idx = get_index(); - set_entrylo0(0); - set_entrylo1(0); - set_entryhi(KSEG0); - BARRIER; - if(idx < 0) - continue; - tlb_write_indexed(); - BARRIER; - } - set_entryhi(oldpid); - } else { - get_new_mmu_context(mm, asid_cache); - if (mm == current->active_mm) - set_entryhi(mm->context & 0xff); - } - local_irq_restore(flags); - } -} - -void flush_tlb_page(struct vm_area_struct *vma, unsigned long page) -{ - if (vma->vm_mm->context != 0) { - unsigned long flags; - int oldpid, newpid, idx; - -#ifdef DEBUG_TLB - printk("[tlbpage<%d,%08lx>]", vma->vm_mm->context, page); -#endif - newpid = (vma->vm_mm->context & 0xff); - page &= (PAGE_MASK << 1); - local_irq_save(flags); - oldpid = (get_entryhi() & 0xff); - set_entryhi(page | newpid); - BARRIER; - tlb_probe(); - BARRIER; - idx = get_index(); - set_entrylo0(0); - set_entrylo1(0); - set_entryhi(KSEG0); - if(idx < 0) - goto finish; - BARRIER; - tlb_write_indexed(); - - finish: - BARRIER; - set_entryhi(oldpid); - local_irq_restore(flags); - } -} - -void pgd_init(unsigned long page) -{ - unsigned long *p = (unsigned long *) page; - int i; - - for(i = 0; i < USER_PTRS_PER_PGD; i+=8) { - p[i + 0] = (unsigned long) invalid_pte_table; - p[i + 1] = (unsigned long) invalid_pte_table; - p[i + 2] = (unsigned long) invalid_pte_table; - p[i + 3] = (unsigned long) invalid_pte_table; - p[i + 4] = (unsigned long) invalid_pte_table; - p[i + 5] = (unsigned long) invalid_pte_table; - p[i + 6] = (unsigned long) invalid_pte_table; - p[i + 7] = (unsigned long) invalid_pte_table; - } -} - -/* We will need multiple versions of update_mmu_cache(), one that just - * updates the TLB with the new pte(s), and another which also checks - * for the R4k "end of page" hardware bug and does the needy. - */ -void update_mmu_cache(struct vm_area_struct * vma, - unsigned long address, pte_t pte) -{ - unsigned long flags; - pgd_t *pgdp; - pmd_t *pmdp; - pte_t *ptep; - int idx, pid; - - /* - * Handle debugger faulting in for debugee. - */ - if (current->active_mm != vma->vm_mm) - return; - - pid = get_entryhi() & 0xff; - -#ifdef DEBUG_TLB - if((pid != (vma->vm_mm->context & 0xff)) || (vma->vm_mm->context == 0)) { - printk("update_mmu_cache: Wheee, bogus tlbpid mmpid=%d tlbpid=%d\n", - (int) (vma->vm_mm->context & 0xff), pid); - } -#endif - - local_irq_save(flags); - address &= (PAGE_MASK << 1); - set_entryhi(address | (pid)); - pgdp = pgd_offset(vma->vm_mm, address); - BARRIER; - tlb_probe(); - BARRIER; - pmdp = pmd_offset(pgdp, address); - idx = get_index(); - ptep = pte_offset(pmdp, address); - BARRIER; - set_entrylo0(pte_val(*ptep++) >> 6); - set_entrylo1(pte_val(*ptep) >> 6); - set_entryhi(address | (pid)); - BARRIER; - if(idx < 0) { - tlb_write_random(); - } else { - tlb_write_indexed(); - } - BARRIER; - set_entryhi(pid); - BARRIER; - local_irq_restore(flags); -} - -#if 0 -static void r4k_update_mmu_cache_hwbug(struct vm_area_struct * vma, - unsigned long address, pte_t pte) -{ - unsigned long flags; - pgd_t *pgdp; - pmd_t *pmdp; - pte_t *ptep; - int idx; - - local_irq_save(flags); - address &= (PAGE_MASK << 1); - set_entryhi(address | (get_entryhi() & 0xff)); - pgdp = pgd_offset(vma->vm_mm, address); - tlb_probe(); - pmdp = pmd_offset(pgdp, address); - idx = get_index(); - ptep = pte_offset(pmdp, address); - set_entrylo0(pte_val(*ptep++) >> 6); - set_entrylo1(pte_val(*ptep) >> 6); - BARRIER; - if(idx < 0) - tlb_write_random(); - else - tlb_write_indexed(); - BARRIER; - local_irq_restore(flags); -} -#endif - -void show_regs(struct pt_regs * regs) -{ - /* Saved main processor registers. */ - printk("$0 : %08lx %08lx %08lx %08lx\n", - 0UL, regs->regs[1], regs->regs[2], regs->regs[3]); - printk("$4 : %08lx %08lx %08lx %08lx\n", - regs->regs[4], regs->regs[5], regs->regs[6], regs->regs[7]); - printk("$8 : %08lx %08lx %08lx %08lx\n", - regs->regs[8], regs->regs[9], regs->regs[10], regs->regs[11]); - printk("$12: %08lx %08lx %08lx %08lx\n", - regs->regs[12], regs->regs[13], regs->regs[14], regs->regs[15]); - printk("$16: %08lx %08lx %08lx %08lx\n", - regs->regs[16], regs->regs[17], regs->regs[18], regs->regs[19]); - printk("$20: %08lx %08lx %08lx %08lx\n", - regs->regs[20], regs->regs[21], regs->regs[22], regs->regs[23]); - printk("$24: %08lx %08lx\n", - regs->regs[24], regs->regs[25]); - printk("$28: %08lx %08lx %08lx %08lx\n", - regs->regs[28], regs->regs[29], regs->regs[30], regs->regs[31]); - - /* Saved cp0 registers. */ - printk("epc : %08lx %s\nStatus: %08lx\nCause : %08lx\n", - regs->cp0_epc, print_tainted(), regs->cp0_status, regs->cp0_cause); -} - -void add_wired_entry(unsigned long entrylo0, unsigned long entrylo1, - unsigned long entryhi, unsigned long pagemask) -{ - unsigned long flags; - unsigned long wired; - unsigned long old_pagemask; - unsigned long old_ctx; - - local_irq_save(flags); - /* Save old context and create impossible VPN2 value */ - old_ctx = (get_entryhi() & 0xff); - old_pagemask = get_pagemask(); - wired = get_wired(); - set_wired (wired + 1); - set_index (wired); - BARRIER; - set_pagemask (pagemask); - set_entryhi(entryhi); - set_entrylo0(entrylo0); - set_entrylo1(entrylo1); - BARRIER; - tlb_write_indexed(); - BARRIER; - - set_entryhi(old_ctx); - BARRIER; - set_pagemask (old_pagemask); - flush_tlb_all(); - local_irq_restore(flags); -} - -/* Detect and size the various r4k caches. */ -static void __init probe_icache(unsigned long config) -{ - switch (mips_cpu.cputype) { - case CPU_VR41XX: - icache_size = 1 << (10 + ((config >> 9) & 7)); - break; - default: - icache_size = 1 << (12 + ((config >> 9) & 7)); - break; - } - ic_lsize = 16 << ((config >> 5) & 1); - - printk("Primary instruction cache %dkb, linesize %d bytes.\n", - icache_size >> 10, ic_lsize); -} - -static void __init probe_dcache(unsigned long config) -{ - switch (mips_cpu.cputype) { - case CPU_VR41XX: - dcache_size = 1 << (10 + ((config >> 6) & 7)); - break; - default: - dcache_size = 1 << (12 + ((config >> 6) & 7)); - break; - } - dc_lsize = 16 << ((config >> 4) & 1); - - printk("Primary data cache %dkb, linesize %d bytes.\n", - dcache_size >> 10, dc_lsize); -} - - -/* If you even _breathe_ on this function, look at the gcc output - * and make sure it does not pop things on and off the stack for - * the cache sizing loop that executes in KSEG1 space or else - * you will crash and burn badly. You have been warned. - */ -static int __init probe_scache(unsigned long config) -{ - extern unsigned long stext; - unsigned long flags, addr, begin, end, pow2; - int tmp; - - tmp = ((config >> 17) & 1); - if(tmp) - return 0; - tmp = ((config >> 22) & 3); - switch(tmp) { - case 0: - sc_lsize = 16; - break; - case 1: - sc_lsize = 32; - break; - case 2: - sc_lsize = 64; - break; - case 3: - sc_lsize = 128; - break; - } - - begin = (unsigned long) &stext; - begin &= ~((4 * 1024 * 1024) - 1); - end = begin + (4 * 1024 * 1024); - - /* This is such a bitch, you'd think they would make it - * easy to do this. Away you daemons of stupidity! - */ - local_irq_save(flags); - - /* Fill each size-multiple cache line with a valid tag. */ - pow2 = (64 * 1024); - for(addr = begin; addr < end; addr = (begin + pow2)) { - unsigned long *p = (unsigned long *) addr; - __asm__ __volatile__("nop" : : "r" (*p)); /* whee... */ - pow2 <<= 1; - } - - /* Load first line with zero (therefore invalid) tag. */ - set_taglo(0); - set_taghi(0); - __asm__ __volatile__("nop; nop; nop; nop;"); /* avoid the hazard */ - __asm__ __volatile__("\n\t.set noreorder\n\t" - ".set mips3\n\t" - "cache 8, (%0)\n\t" - ".set mips0\n\t" - ".set reorder\n\t" : : "r" (begin)); - __asm__ __volatile__("\n\t.set noreorder\n\t" - ".set mips3\n\t" - "cache 9, (%0)\n\t" - ".set mips0\n\t" - ".set reorder\n\t" : : "r" (begin)); - __asm__ __volatile__("\n\t.set noreorder\n\t" - ".set mips3\n\t" - "cache 11, (%0)\n\t" - ".set mips0\n\t" - ".set reorder\n\t" : : "r" (begin)); - - /* Now search for the wrap around point. */ - pow2 = (128 * 1024); - tmp = 0; - for(addr = (begin + (128 * 1024)); addr < (end); addr = (begin + pow2)) { - __asm__ __volatile__("\n\t.set noreorder\n\t" - ".set mips3\n\t" - "cache 7, (%0)\n\t" - ".set mips0\n\t" - ".set reorder\n\t" : : "r" (addr)); - __asm__ __volatile__("nop; nop; nop; nop;"); /* hazard... */ - if(!get_taglo()) - break; - pow2 <<= 1; - } - local_irq_restore(flags); - addr -= begin; - printk("Secondary cache sized at %dK linesize %d bytes.\n", - (int) (addr >> 10), sc_lsize); - scache_size = addr; - return 1; -} - -static void __init setup_noscache_funcs(void) -{ - unsigned int prid; - - switch(dc_lsize) { - case 16: - _clear_page = r4k_clear_page_d16; - _copy_page = r4k_copy_page_d16; - _flush_cache_all = r4k_flush_cache_all_d16i16; - _flush_cache_mm = r4k_flush_cache_mm_d16i16; - _flush_cache_range = r4k_flush_cache_range_d16i16; - _flush_cache_page = r4k_flush_cache_page_d16i16; - _flush_page_to_ram = r4k_flush_page_to_ram_d16; - break; - case 32: - prid = read_32bit_cp0_register(CP0_PRID) & 0xfff0; - if (prid == 0x2010) { /* R4600 V1.7 */ - _clear_page = r4k_clear_page_r4600_v1; - _copy_page = r4k_copy_page_r4600_v1; - _flush_page_to_ram = r4k_flush_page_to_ram_d32_r4600; - } else if (prid == 0x2020) { /* R4600 V2.0 */ - _clear_page = r4k_clear_page_r4600_v2; - _copy_page = r4k_copy_page_r4600_v2; - _flush_page_to_ram = r4k_flush_page_to_ram_d32; - } else { - _clear_page = r4k_clear_page_d32; - _copy_page = r4k_copy_page_d32; - _flush_page_to_ram = r4k_flush_page_to_ram_d32; - } - _flush_cache_all = r4k_flush_cache_all_d32i32; - _flush_cache_mm = r4k_flush_cache_mm_d32i32; - _flush_cache_range = r4k_flush_cache_range_d32i32; - _flush_cache_page = r4k_flush_cache_page_d32i32; - break; - } - ___flush_cache_all = _flush_cache_all; - - _flush_icache_page = r4k_flush_icache_page_p; - - _dma_cache_wback_inv = r4k_dma_cache_wback_inv_pc; - _dma_cache_wback = r4k_dma_cache_wback; - _dma_cache_inv = r4k_dma_cache_inv_pc; -} - -static void __init setup_scache_funcs(void) -{ - switch(sc_lsize) { - case 16: - switch(dc_lsize) { - case 16: - _flush_cache_all = r4k_flush_cache_all_s16d16i16; - _flush_cache_mm = r4k_flush_cache_mm_s16d16i16; - _flush_cache_range = r4k_flush_cache_range_s16d16i16; - _flush_cache_page = r4k_flush_cache_page_s16d16i16; - break; - case 32: - panic("Invalid cache configuration detected"); - }; - _flush_page_to_ram = r4k_flush_page_to_ram_s16; - _clear_page = r4k_clear_page_s16; - _copy_page = r4k_copy_page_s16; - break; - case 32: - switch(dc_lsize) { - case 16: - _flush_cache_all = r4k_flush_cache_all_s32d16i16; - _flush_cache_mm = r4k_flush_cache_mm_s32d16i16; - _flush_cache_range = r4k_flush_cache_range_s32d16i16; - _flush_cache_page = r4k_flush_cache_page_s32d16i16; - break; - case 32: - _flush_cache_all = r4k_flush_cache_all_s32d32i32; - _flush_cache_mm = r4k_flush_cache_mm_s32d32i32; - _flush_cache_range = r4k_flush_cache_range_s32d32i32; - _flush_cache_page = r4k_flush_cache_page_s32d32i32; - break; - }; - _flush_page_to_ram = r4k_flush_page_to_ram_s32; - _clear_page = r4k_clear_page_s32; - _copy_page = r4k_copy_page_s32; - break; - case 64: - switch(dc_lsize) { - case 16: - _flush_cache_all = r4k_flush_cache_all_s64d16i16; - _flush_cache_mm = r4k_flush_cache_mm_s64d16i16; - _flush_cache_range = r4k_flush_cache_range_s64d16i16; - _flush_cache_page = r4k_flush_cache_page_s64d16i16; - break; - case 32: - _flush_cache_all = r4k_flush_cache_all_s64d32i32; - _flush_cache_mm = r4k_flush_cache_mm_s64d32i32; - _flush_cache_range = r4k_flush_cache_range_s64d32i32; - _flush_cache_page = r4k_flush_cache_page_s64d32i32; - break; - }; - _flush_page_to_ram = r4k_flush_page_to_ram_s64; - _clear_page = r4k_clear_page_s64; - _copy_page = r4k_copy_page_s64; - break; - case 128: - switch(dc_lsize) { - case 16: - _flush_cache_all = r4k_flush_cache_all_s128d16i16; - _flush_cache_mm = r4k_flush_cache_mm_s128d16i16; - _flush_cache_range = r4k_flush_cache_range_s128d16i16; - _flush_cache_page = r4k_flush_cache_page_s128d16i16; - break; - case 32: - _flush_cache_all = r4k_flush_cache_all_s128d32i32; - _flush_cache_mm = r4k_flush_cache_mm_s128d32i32; - _flush_cache_range = r4k_flush_cache_range_s128d32i32; - _flush_cache_page = r4k_flush_cache_page_s128d32i32; - break; - }; - _flush_page_to_ram = r4k_flush_page_to_ram_s128; - _clear_page = r4k_clear_page_s128; - _copy_page = r4k_copy_page_s128; - break; - } - ___flush_cache_all = _flush_cache_all; - _flush_icache_page = r4k_flush_icache_page_s; - _dma_cache_wback_inv = r4k_dma_cache_wback_inv_sc; - _dma_cache_wback = r4k_dma_cache_wback; - _dma_cache_inv = r4k_dma_cache_inv_sc; -} - -typedef int (*probe_func_t)(unsigned long); - -static inline void __init setup_scache(unsigned int config) -{ - probe_func_t probe_scache_kseg1; - int sc_present = 0; - - /* Maybe the cpu knows about a l2 cache? */ - probe_scache_kseg1 = (probe_func_t) (KSEG1ADDR(&probe_scache)); - sc_present = probe_scache_kseg1(config); - - if (sc_present) { - setup_scache_funcs(); - return; - } - - setup_noscache_funcs(); -} - -void __init ld_mmu_r4xx0(void) -{ - unsigned long config = read_32bit_cp0_register(CP0_CONFIG); - - printk("CPU revision is: %08x\n", read_32bit_cp0_register(CP0_PRID)); - -#ifdef CONFIG_MIPS_UNCACHED - change_cp0_config(CONF_CM_CMASK, CONF_CM_UNCACHED); -#else - change_cp0_config(CONF_CM_CMASK, CONF_CM_CACHABLE_NONCOHERENT); -#endif - - probe_icache(config); - probe_dcache(config); - setup_scache(config); - - switch(mips_cpu.cputype) { - case CPU_R4600: /* QED style two way caches? */ - case CPU_R4700: - case CPU_R5000: - case CPU_NEVADA: - _flush_cache_page = r4k_flush_cache_page_d32i32_r4600; - } - - _flush_cache_sigtramp = r4k_flush_cache_sigtramp; - _flush_icache_range = r4k_flush_icache_range; /* Ouch */ - if ((read_32bit_cp0_register(CP0_PRID) & 0xfff0) == 0x2020) { - _flush_cache_sigtramp = r4600v20k_flush_cache_sigtramp; - } - - __flush_cache_all(); - write_32bit_cp0_register(CP0_WIRED, 0); - - /* - * You should never change this register: - * - On R4600 1.7 the tlbp never hits for pages smaller than - * the value in the c0_pagemask register. - * - The entire mm handling assumes the c0_pagemask register to - * be set for 4kb pages. - */ - set_pagemask(PM_4K); - flush_tlb_all(); -} diff --git a/arch/mips/mm/r5432.c b/arch/mips/mm/r5432.c deleted file mode 100644 index fad7d7e356f1..000000000000 --- a/arch/mips/mm/r5432.c +++ /dev/null @@ -1,864 +0,0 @@ -/* - * 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. - * - * r5432.c: NEC Vr5432 processor. We cannot use r4xx0.c because of - * its unique way-selection method for indexed operations. - * - * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) - * Copyright (C) 1997, 1998, 1999, 2000 Ralf Baechle (ralf@gnu.org) - * Copyright (C) 2000 Jun Sun (jsun@mvista.com) - * - */ - -/* - * [jsun] - * In a sense, this is really silly. We cannot re-use r4xx0.c because - * the lowest-level indexed cache operation does not take way-selection - * into account. So all what I am doing here is to copy all the funcs - * and macros (in r4kcache.h) relavent to R5432 to this file, and then - * modify a few indexed cache operations. *sigh* - */ - -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/mm.h> - -#include <asm/bcache.h> -#include <asm/io.h> -#include <asm/page.h> -#include <asm/pgtable.h> -#include <asm/system.h> -#include <asm/bootinfo.h> -#include <asm/mmu_context.h> - -/* CP0 hazard avoidance. */ -#define BARRIER __asm__ __volatile__(".set noreorder\n\t" \ - "nop; nop; nop; nop; nop; nop;\n\t" \ - ".set reorder\n\t") - -#include <asm/asm.h> -#include <asm/cacheops.h> - -#undef DEBUG_CACHE - -/* Primary cache parameters. */ -static int icache_size, dcache_size; /* Size in bytes */ -static int ic_lsize, dc_lsize; /* LineSize in bytes */ - - -/* -------------------------------------------------------------------- */ -/* #include <asm/r4kcache.h> */ - -extern inline void flush_icache_line_indexed(unsigned long addr) -{ - __asm__ __volatile__( - ".set noreorder\n\t" - ".set mips3\n\t" - "cache %1, (%0)\n\t" - "cache %1, 1(%0)\n\t" - ".set mips0\n\t" - ".set reorder" - : - : "r" (addr), - "i" (Index_Invalidate_I)); -} - -extern inline void flush_dcache_line_indexed(unsigned long addr) -{ - __asm__ __volatile__( - ".set noreorder\n\t" - ".set mips3\n\t" - "cache %1, (%0)\n\t" - "cache %1, 1(%0)\n\t" - ".set mips0\n\t" - ".set reorder" - : - : "r" (addr), - "i" (Index_Writeback_Inv_D)); -} - -extern inline void flush_icache_line(unsigned long addr) -{ - __asm__ __volatile__( - ".set noreorder\n\t" - ".set mips3\n\t" - "cache %1, (%0)\n\t" - ".set mips0\n\t" - ".set reorder" - : - : "r" (addr), - "i" (Hit_Invalidate_I)); -} - -extern inline void flush_dcache_line(unsigned long addr) -{ - __asm__ __volatile__( - ".set noreorder\n\t" - ".set mips3\n\t" - "cache %1, (%0)\n\t" - ".set mips0\n\t" - ".set reorder" - : - : "r" (addr), - "i" (Hit_Writeback_Inv_D)); -} - -extern inline void invalidate_dcache_line(unsigned long addr) -{ - __asm__ __volatile__( - ".set noreorder\n\t" - ".set mips3\n\t" - "cache %1, (%0)\n\t" - ".set mips0\n\t" - ".set reorder" - : - : "r" (addr), - "i" (Hit_Invalidate_D)); -} - - -/* - * The next two are for badland addresses like signal trampolines. - */ -extern inline void protected_flush_icache_line(unsigned long addr) -{ - __asm__ __volatile__( - ".set noreorder\n\t" - ".set mips3\n" - "1:\tcache %1,(%0)\n" - "2:\t.set mips0\n\t" - ".set reorder\n\t" - ".section\t__ex_table,\"a\"\n\t" - STR(PTR)"\t1b,2b\n\t" - ".previous" - : - : "r" (addr), - "i" (Hit_Invalidate_I)); -} - -extern inline void protected_writeback_dcache_line(unsigned long addr) -{ - __asm__ __volatile__( - ".set noreorder\n\t" - ".set mips3\n" - "1:\tcache %1,(%0)\n" - "2:\t.set mips0\n\t" - ".set reorder\n\t" - ".section\t__ex_table,\"a\"\n\t" - STR(PTR)"\t1b,2b\n\t" - ".previous" - : - : "r" (addr), - "i" (Hit_Writeback_D)); -} - - -#define cache32_unroll32(base,op) \ - __asm__ __volatile__(" \ - .set noreorder; \ - .set mips3; \ - cache %1, 0x000(%0); cache %1, 0x020(%0); \ - cache %1, 0x040(%0); cache %1, 0x060(%0); \ - cache %1, 0x080(%0); cache %1, 0x0a0(%0); \ - cache %1, 0x0c0(%0); cache %1, 0x0e0(%0); \ - cache %1, 0x100(%0); cache %1, 0x120(%0); \ - cache %1, 0x140(%0); cache %1, 0x160(%0); \ - cache %1, 0x180(%0); cache %1, 0x1a0(%0); \ - cache %1, 0x1c0(%0); cache %1, 0x1e0(%0); \ - cache %1, 0x200(%0); cache %1, 0x220(%0); \ - cache %1, 0x240(%0); cache %1, 0x260(%0); \ - cache %1, 0x280(%0); cache %1, 0x2a0(%0); \ - cache %1, 0x2c0(%0); cache %1, 0x2e0(%0); \ - cache %1, 0x300(%0); cache %1, 0x320(%0); \ - cache %1, 0x340(%0); cache %1, 0x360(%0); \ - cache %1, 0x380(%0); cache %1, 0x3a0(%0); \ - cache %1, 0x3c0(%0); cache %1, 0x3e0(%0); \ - .set mips0; \ - .set reorder" \ - : \ - : "r" (base), \ - "i" (op)); - -extern inline void blast_dcache32(void) -{ - unsigned long start = KSEG0; - unsigned long end = (start + dcache_size/2); - - while(start < end) { - cache32_unroll32(start,Index_Writeback_Inv_D); - cache32_unroll32(start+1,Index_Writeback_Inv_D); - start += 0x400; - } -} - -extern inline void blast_dcache32_page(unsigned long page) -{ - unsigned long start = page; - unsigned long end = (start + PAGE_SIZE); - - while(start < end) { - cache32_unroll32(start,Hit_Writeback_Inv_D); - start += 0x400; - } -} - -extern inline void blast_dcache32_page_indexed(unsigned long page) -{ - unsigned long start = page; - unsigned long end = (start + PAGE_SIZE); - - while(start < end) { - cache32_unroll32(start,Index_Writeback_Inv_D); - cache32_unroll32(start+1,Index_Writeback_Inv_D); - start += 0x400; - } -} - -extern inline void blast_icache32(void) -{ - unsigned long start = KSEG0; - unsigned long end = (start + icache_size/2); - - while(start < end) { - cache32_unroll32(start,Index_Invalidate_I); - cache32_unroll32(start+1,Index_Invalidate_I); - start += 0x400; - } -} - -extern inline void blast_icache32_page(unsigned long page) -{ - unsigned long start = page; - unsigned long end = (start + PAGE_SIZE); - - while(start < end) { - cache32_unroll32(start,Hit_Invalidate_I); - start += 0x400; - } -} - -extern inline void blast_icache32_page_indexed(unsigned long page) -{ - unsigned long start = page; - unsigned long end = (start + PAGE_SIZE); - - while(start < end) { - cache32_unroll32(start,Index_Invalidate_I); - cache32_unroll32(start+1,Index_Invalidate_I); - start += 0x400; - } -} - -/* -------------------------------------------------------------------- */ - -static void r5432_clear_page_d32(void * page) -{ - __asm__ __volatile__( - ".set\tnoreorder\n\t" - ".set\tnoat\n\t" - ".set\tmips3\n\t" - "daddiu\t$1,%0,%2\n" - "1:\tcache\t%3,(%0)\n\t" - "sd\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" - "cache\t%3,-32(%0)\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\tmips0\n\t" - ".set\tat\n\t" - ".set\treorder" - :"=r" (page) - :"0" (page), - "I" (PAGE_SIZE), - "i" (Create_Dirty_Excl_D) - :"$1","memory"); -} - - - -/* - * This is still inefficient. We only can do better if we know the - * virtual address where the copy will be accessed. - */ - -static void r5432_copy_page_d32(void * to, void * from) -{ - unsigned long dummy1, dummy2; - unsigned long reg1, reg2, reg3, reg4; - - __asm__ __volatile__( - ".set\tnoreorder\n\t" - ".set\tnoat\n\t" - ".set\tmips3\n\t" - "daddiu\t$1,%0,%8\n" - "1:\tcache\t%9,(%0)\n\t" - "lw\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" - "cache\t%9,32(%0)\n\t" - "daddiu\t%0,64\n\t" - "daddiu\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\tmips0\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), - "i" (Create_Dirty_Excl_D)); -} - - -/* - * If you think for one second that this stuff coming up is a lot - * of bulky code eating too many kernel cache lines. Think _again_. - * - * Consider: - * 1) Taken branches have a 3 cycle penalty on R4k - * 2) The branch itself is a real dead cycle on even R4600/R5000. - * 3) Only one of the following variants of each type is even used by - * the kernel based upon the cache parameters we detect at boot time. - * - * QED. - */ - -static inline void r5432_flush_cache_all_d32i32(void) -{ - blast_dcache32(); blast_icache32(); -} - -static void r5432_flush_cache_range_d32i32(struct vm_area_struct *vma, - unsigned long start, - unsigned long end) -{ - struct mm_struct *mm = vma->vm_mm; - - if (mm->context != 0) { -#ifdef DEBUG_CACHE - printk("crange[%d,%08lx,%08lx]", (int)mm->context, start, end); -#endif - blast_dcache32(); blast_icache32(); - } -} - -/* - * On architectures like the Sparc, we could get rid of lines in - * the cache created only by a certain context, but on the MIPS - * (and actually certain Sparc's) we cannot. - */ -static void r5432_flush_cache_mm_d32i32(struct mm_struct *mm) -{ - if (mm->context != 0) { -#ifdef DEBUG_CACHE - printk("cmm[%d]", (int)mm->context); -#endif - r5432_flush_cache_all_d32i32(); - } -} - -static void r5432_flush_cache_page_d32i32(struct vm_area_struct *vma, - unsigned long page) -{ - struct mm_struct *mm = vma->vm_mm; - pgd_t *pgdp; - pmd_t *pmdp; - pte_t *ptep; - - /* - * If ownes no valid ASID yet, cannot possibly have gotten - * this page into the cache. - */ - if (mm->context == 0) - return; - -#ifdef DEBUG_CACHE - printk("cpage[%d,%08lx]", (int)mm->context, page); -#endif - page &= PAGE_MASK; - pgdp = pgd_offset(mm, page); - pmdp = pmd_offset(pgdp, page); - ptep = pte_offset(pmdp, page); - - /* - * If the page isn't marked valid, the page cannot possibly be - * in the cache. - */ - if (!(pte_val(*ptep) & _PAGE_PRESENT)) - return; - - /* - * Doing flushes for another ASID than the current one is - * too difficult since stupid R4k caches do a TLB translation - * for every cache flush operation. So we do indexed flushes - * in that case, which doesn't overly flush the cache too much. - */ - if ((mm == current->active_mm) && (pte_val(*ptep) & _PAGE_VALID)) { - blast_dcache32_page(page); - } else { - /* - * Do indexed flush, too much work to get the (possible) - * tlb refills to work correctly. - */ - page = (KSEG0 + (page & (dcache_size - 1))); - blast_dcache32_page_indexed(page); - } -} - - -/* 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. - * 3) In KSEG1, no flush necessary. - */ -static void r5432_flush_page_to_ram_d32(struct page *page) -{ - blast_dcache32_page((unsigned long)page_address(page)); -} - -static void -r5432_flush_icache_range(unsigned long start, unsigned long end) -{ - r5432_flush_cache_all_d32i32(); -} - -/* - * Ok, this seriously sucks. We use them to flush a user page but don't - * know the virtual address, so we have to blast away the whole icache - * which is significantly more expensive than the real thing. - */ -static void -r5432_flush_icache_page_i32(struct vm_area_struct *vma, struct page *page) -{ - if (!(vma->vm_flags & VM_EXEC)) - return; - - r5432_flush_cache_all_d32i32(); -} - -/* - * Writeback and invalidate the primary cache dcache before DMA. - */ -static void -r5432_dma_cache_wback_inv_pc(unsigned long addr, unsigned long size) -{ - unsigned long end, a; - - if (size >= dcache_size) { - flush_cache_all(); - } else { - a = addr & ~(dc_lsize - 1); - end = (addr + size) & ~(dc_lsize - 1); - while (1) { - flush_dcache_line(a); /* Hit_Writeback_Inv_D */ - if (a == end) break; - a += dc_lsize; - } - } - bc_wback_inv(addr, size); -} - -static void -r5432_dma_cache_inv_pc(unsigned long addr, unsigned long size) -{ - unsigned long end, a; - - if (size >= dcache_size) { - flush_cache_all(); - } else { - a = addr & ~(dc_lsize - 1); - end = (addr + size) & ~(dc_lsize - 1); - while (1) { - flush_dcache_line(a); /* Hit_Writeback_Inv_D */ - if (a == end) break; - a += dc_lsize; - } - } - - bc_inv(addr, size); -} - -static void -r5432_dma_cache_wback(unsigned long addr, unsigned long size) -{ - panic("r5432_dma_cache called - should not happen.\n"); -} - -/* - * While we're protected against bad userland addresses we don't care - * very much about what happens in that case. Usually a segmentation - * fault will dump the process later on anyway ... - */ -static void r5432_flush_cache_sigtramp(unsigned long addr) -{ - protected_writeback_dcache_line(addr & ~(dc_lsize - 1)); - protected_flush_icache_line(addr & ~(ic_lsize - 1)); -} - -#undef DEBUG_TLB -#undef DEBUG_TLBUPDATE - -#define NTLB_ENTRIES 48 /* Fixed on all R4XX0 variants... */ - -#define NTLB_ENTRIES_HALF 24 /* Fixed on all R4XX0 variants... */ - -void flush_tlb_all(void) -{ - unsigned long old_ctx; - int entry; - unsigned long flags; - -#ifdef DEBUG_TLB - printk("[tlball]"); -#endif - - local_irq_save(flags); - /* Save old context and create impossible VPN2 value */ - old_ctx = (get_entryhi() & 0xff); - set_entryhi(KSEG0); - set_entrylo0(0); - set_entrylo1(0); - BARRIER; - - entry = get_wired(); - - /* Blast 'em all away. */ - while(entry < NTLB_ENTRIES) { - set_index(entry); - BARRIER; - tlb_write_indexed(); - BARRIER; - entry++; - } - BARRIER; - set_entryhi(old_ctx); - local_irq_restore(flags); -} - -void flush_tlb_mm(struct mm_struct *mm) -{ - if (mm->context != 0) { - unsigned long flags; - -#ifdef DEBUG_TLB - printk("[tlbmm<%d>]", mm->context); -#endif - local_irq_save(flags); - get_new_mmu_context(mm, asid_cache); - if (mm == current->active_mm) - set_entryhi(mm->context & 0xff); - local_irq_restore(flags); - } -} - -void flush_tlb_range(struct mm_struct *mm, unsigned long start, - unsigned long end) -{ - if(mm->context != 0) { - unsigned long flags; - int size; - -#ifdef DEBUG_TLB - printk("[tlbrange<%02x,%08lx,%08lx>]", (mm->context & 0xff), - start, end); -#endif - local_irq_save(flags); - size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; - size = (size + 1) >> 1; - if(size <= NTLB_ENTRIES_HALF) { - int oldpid = (get_entryhi() & 0xff); - int newpid = (mm->context & 0xff); - - start &= (PAGE_MASK << 1); - end += ((PAGE_SIZE << 1) - 1); - end &= (PAGE_MASK << 1); - while(start < end) { - int idx; - - set_entryhi(start | newpid); - start += (PAGE_SIZE << 1); - BARRIER; - tlb_probe(); - BARRIER; - idx = get_index(); - set_entrylo0(0); - set_entrylo1(0); - set_entryhi(KSEG0); - BARRIER; - if(idx < 0) - continue; - tlb_write_indexed(); - BARRIER; - } - set_entryhi(oldpid); - } else { - get_new_mmu_context(mm, asid_cache); - if (mm == current->active_mm) - set_entryhi(mm->context & 0xff); - } - local_irq_restore(flags); - } -} - -void flush_tlb_page(struct vm_area_struct *vma, unsigned long page) -{ - if (vma->vm_mm->context != 0) { - unsigned long flags; - int oldpid, newpid, idx; - -#ifdef DEBUG_TLB - printk("[tlbpage<%d,%08lx>]", vma->vm_mm->context, page); -#endif - newpid = (vma->vm_mm->context & 0xff); - page &= (PAGE_MASK << 1); - local_irq_save(flags); - oldpid = (get_entryhi() & 0xff); - set_entryhi(page | newpid); - BARRIER; - tlb_probe(); - BARRIER; - idx = get_index(); - set_entrylo0(0); - set_entrylo1(0); - set_entryhi(KSEG0); - if(idx < 0) - goto finish; - BARRIER; - tlb_write_indexed(); - - finish: - BARRIER; - set_entryhi(oldpid); - local_irq_restore(flags); - } -} - -void pgd_init(unsigned long page) -{ - unsigned long *p = (unsigned long *) page; - int i; - - for(i = 0; i < USER_PTRS_PER_PGD; i+=8) { - p[i + 0] = (unsigned long) invalid_pte_table; - p[i + 1] = (unsigned long) invalid_pte_table; - p[i + 2] = (unsigned long) invalid_pte_table; - p[i + 3] = (unsigned long) invalid_pte_table; - p[i + 4] = (unsigned long) invalid_pte_table; - p[i + 5] = (unsigned long) invalid_pte_table; - p[i + 6] = (unsigned long) invalid_pte_table; - p[i + 7] = (unsigned long) invalid_pte_table; - } -} - -/* We will need multiple versions of update_mmu_cache(), one that just - * updates the TLB with the new pte(s), and another which also checks - * for the R4k "end of page" hardware bug and does the needy. - */ -void update_mmu_cache(struct vm_area_struct * vma, - unsigned long address, pte_t pte) -{ - unsigned long flags; - pgd_t *pgdp; - pmd_t *pmdp; - pte_t *ptep; - int idx, pid; - - /* - * Handle debugger faulting in for debugee. - */ - if (current->active_mm != vma->vm_mm) - return; - - pid = get_entryhi() & 0xff; - -#ifdef DEBUG_TLB - if((pid != (vma->vm_mm->context & 0xff)) || (vma->vm_mm->context == 0)) { - printk("update_mmu_cache: Wheee, bogus tlbpid mmpid=%d tlbpid=%d\n", - (int) (vma->vm_mm->context & 0xff), pid); - } -#endif - - local_irq_save(flags); - address &= (PAGE_MASK << 1); - set_entryhi(address | (pid)); - pgdp = pgd_offset(vma->vm_mm, address); - BARRIER; - tlb_probe(); - BARRIER; - pmdp = pmd_offset(pgdp, address); - idx = get_index(); - ptep = pte_offset(pmdp, address); - BARRIER; - set_entrylo0(pte_val(*ptep++) >> 6); - set_entrylo1(pte_val(*ptep) >> 6); - set_entryhi(address | (pid)); - BARRIER; - if(idx < 0) { - tlb_write_random(); - } else { - tlb_write_indexed(); - } - BARRIER; - set_entryhi(pid); - BARRIER; - local_irq_restore(flags); -} - -void show_regs(struct pt_regs * regs) -{ - /* Saved main processor registers. */ - printk("$0 : %08lx %08lx %08lx %08lx\n", - 0UL, regs->regs[1], regs->regs[2], regs->regs[3]); - printk("$4 : %08lx %08lx %08lx %08lx\n", - regs->regs[4], regs->regs[5], regs->regs[6], regs->regs[7]); - printk("$8 : %08lx %08lx %08lx %08lx\n", - regs->regs[8], regs->regs[9], regs->regs[10], regs->regs[11]); - printk("$12: %08lx %08lx %08lx %08lx\n", - regs->regs[12], regs->regs[13], regs->regs[14], regs->regs[15]); - printk("$16: %08lx %08lx %08lx %08lx\n", - regs->regs[16], regs->regs[17], regs->regs[18], regs->regs[19]); - printk("$20: %08lx %08lx %08lx %08lx\n", - regs->regs[20], regs->regs[21], regs->regs[22], regs->regs[23]); - printk("$24: %08lx %08lx\n", - regs->regs[24], regs->regs[25]); - printk("$28: %08lx %08lx %08lx %08lx\n", - regs->regs[28], regs->regs[29], regs->regs[30], regs->regs[31]); - - /* Saved cp0 registers. */ - printk("epc : %08lx %s\nStatus: %08lx\nCause : %08lx\n", - regs->cp0_epc, print_tainted(), regs->cp0_status, regs->cp0_cause); -} - -void add_wired_entry(unsigned long entrylo0, unsigned long entrylo1, - unsigned long entryhi, unsigned long pagemask) -{ - unsigned long flags; - unsigned long wired; - unsigned long old_pagemask; - unsigned long old_ctx; - - local_irq_save(flags); - /* Save old context and create impossible VPN2 value */ - old_ctx = (get_entryhi() & 0xff); - old_pagemask = get_pagemask(); - wired = get_wired(); - set_wired (wired + 1); - set_index (wired); - BARRIER; - set_pagemask (pagemask); - set_entryhi(entryhi); - set_entrylo0(entrylo0); - set_entrylo1(entrylo1); - BARRIER; - tlb_write_indexed(); - BARRIER; - - set_entryhi(old_ctx); - BARRIER; - set_pagemask (old_pagemask); - flush_tlb_all(); - local_irq_restore(flags); -} - -/* Detect and size the various r4k caches. */ -static void __init probe_icache(unsigned long config) -{ - icache_size = 1 << (12 + ((config >> 9) & 7)); - ic_lsize = 16 << ((config >> 5) & 1); - - printk("Primary instruction cache %dkb, linesize %d bytes.\n", - icache_size >> 10, ic_lsize); -} - -static void __init probe_dcache(unsigned long config) -{ - dcache_size = 1 << (12 + ((config >> 6) & 7)); - dc_lsize = 16 << ((config >> 4) & 1); - - printk("Primary data cache %dkb, linesize %d bytes.\n", - dcache_size >> 10, dc_lsize); -} - - -void __init ld_mmu_r5432(void) -{ - unsigned long config = read_32bit_cp0_register(CP0_CONFIG); - - printk("CPU revision is: %08x\n", read_32bit_cp0_register(CP0_PRID)); - - change_cp0_config(CONF_CM_CMASK, CONF_CM_CACHABLE_NONCOHERENT); - - probe_icache(config); - probe_dcache(config); - - _clear_page = r5432_clear_page_d32; - _copy_page = r5432_copy_page_d32; - _flush_cache_all = r5432_flush_cache_all_d32i32; - ___flush_cache_all = r5432_flush_cache_all_d32i32; - _flush_page_to_ram = r5432_flush_page_to_ram_d32; - _flush_cache_mm = r5432_flush_cache_mm_d32i32; - _flush_cache_range = r5432_flush_cache_range_d32i32; - _flush_cache_page = r5432_flush_cache_page_d32i32; - _flush_icache_page = r5432_flush_icache_page_i32; - _dma_cache_wback_inv = r5432_dma_cache_wback_inv_pc; - _dma_cache_wback = r5432_dma_cache_wback; - _dma_cache_inv = r5432_dma_cache_inv_pc; - - _flush_cache_sigtramp = r5432_flush_cache_sigtramp; - _flush_icache_range = r5432_flush_icache_range; /* Ouch */ - - __flush_cache_all(); - write_32bit_cp0_register(CP0_WIRED, 0); - - /* - * You should never change this register: - * - On R4600 1.7 the tlbp never hits for pages smaller than - * the value in the c0_pagemask register. - * - The entire mm handling assumes the c0_pagemask register to - * be set for 4kb pages. - */ - write_32bit_cp0_register(CP0_PAGEMASK, PM_4K); - flush_tlb_all(); -} diff --git a/arch/mips/mm/rm7k.c b/arch/mips/mm/rm7k.c deleted file mode 100644 index 192abc877f00..000000000000 --- a/arch/mips/mm/rm7k.c +++ /dev/null @@ -1,751 +0,0 @@ -/* - * 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. - * - * r4xx0.c: R4000 processor variant specific MMU/Cache routines. - * - * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) - * Copyright (C) 1997, 1998 Ralf Baechle ralf@gnu.org - * - * To do: - * - * - this code is a overbloated pig - * - many of the bug workarounds are not efficient at all, but at - * least they are functional ... - */ -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/mm.h> - -#include <asm/io.h> -#include <asm/page.h> -#include <asm/pgtable.h> -#include <asm/system.h> -#include <asm/bootinfo.h> -#include <asm/mmu_context.h> - -/* CP0 hazard avoidance. */ -#define BARRIER __asm__ __volatile__(".set noreorder\n\t" \ - "nop; nop; nop; nop; nop; nop;\n\t" \ - ".set reorder\n\t") - -/* Primary cache parameters. */ -static int icache_size, dcache_size; /* Size in bytes */ - -#define ic_lsize 32 /* Fixed to 32 byte on RM7000 */ -#define dc_lsize 32 /* Fixed to 32 byte on RM7000 */ -#define sc_lsize 32 /* Fixed to 32 byte on RM7000 */ -#define tc_pagesize (32*128) - -/* Secondary cache parameters. */ -#define scache_size (256*1024) /* Fixed to 256KiB on RM7000 */ - -#include <asm/cacheops.h> -#include <asm/r4kcache.h> - -int rm7k_tcache_enabled = 0; - -/* - * Not added to asm/r4kcache.h because it seems to be RM7000-specific. - */ -#define Page_Invalidate_T 0x16 - -static inline void invalidate_tcache_page(unsigned long addr) -{ - __asm__ __volatile__( - ".set\tnoreorder\t\t\t# invalidate_tcache_page\n\t" - ".set\tmips3\n\t" - "cache\t%1, (%0)\n\t" - ".set\tmips0\n\t" - ".set\treorder" - : - : "r" (addr), - "i" (Page_Invalidate_T)); -} - -/* - * Zero an entire page. Note that while the RM7000 has a second level cache - * it doesn't have a Create_Dirty_Excl_SD operation. - */ -static void rm7k_clear_page(void * page) -{ - __asm__ __volatile__( - ".set\tnoreorder\n\t" - ".set\tnoat\n\t" - ".set\tmips3\n\t" - "daddiu\t$1,%0,%2\n" - "1:\tcache\t%3,(%0)\n\t" - "sd\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" - "cache\t%3,-32(%0)\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\tmips0\n\t" - ".set\tat\n\t" - ".set\treorder" - :"=r" (page) - :"0" (page), - "I" (PAGE_SIZE), - "i" (Create_Dirty_Excl_D) - :"$1","memory"); -} - - -/* - * Copy an entire page. Note that while the RM7000 has a second level cache - * it doesn't have a Create_Dirty_Excl_SD operation. - */ -static void rm7k_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" - ".set\tmips3\n\t" - "daddiu\t$1,%0,%8\n" - "1:\tcache\t%9,(%0)\n\t" - "lw\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" - "cache\t%9,32(%0)\n\t" - "daddiu\t%0,64\n\t" - "daddiu\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\tmips0\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), - "i" (Create_Dirty_Excl_D)); -} - -static void __flush_cache_all_d32i32(void) -{ - blast_dcache32(); - blast_icache32(); -} - -static inline void rm7k_flush_cache_all_d32i32(void) -{ - /* Yes! Caches that don't suck ... */ -} - -static void rm7k_flush_cache_range_d32i32(struct vm_area_struct *vma, - unsigned long start, - unsigned long end) -{ - /* RM7000 caches are sane ... */ -} - -static void rm7k_flush_cache_mm_d32i32(struct mm_struct *mm) -{ - /* RM7000 caches are sane ... */ -} - -static void rm7k_flush_cache_page_d32i32(struct vm_area_struct *vma, - unsigned long page) -{ - /* RM7000 caches are sane ... */ -} - -static void rm7k_flush_page_to_ram_d32i32(struct page * page) -{ - /* Yes! Caches that don't suck! */ -} - -static void rm7k_flush_icache_range(unsigned long start, unsigned long end) -{ - /* - * FIXME: This is overdoing things and harms performance. - */ - __flush_cache_all_d32i32(); -} - -static void rm7k_flush_icache_page(struct vm_area_struct *vma, - struct page *page) -{ - /* - * FIXME: We should not flush the entire cache but establish some - * temporary mapping and use hit_invalidate operation to flush out - * the line from the cache. - */ - __flush_cache_all_d32i32(); -} - - -/* - * Writeback and invalidate the primary cache dcache before DMA. - * (XXX These need to be fixed ...) - */ -static void -rm7k_dma_cache_wback_inv(unsigned long addr, unsigned long size) -{ - unsigned long end, a; - - a = addr & ~(sc_lsize - 1); - end = (addr + size) & ~(sc_lsize - 1); - while (1) { - flush_dcache_line(a); /* Hit_Writeback_Inv_D */ - flush_icache_line(a); /* Hit_Invalidate_I */ - flush_scache_line(a); /* Hit_Writeback_Inv_SD */ - if (a == end) break; - a += sc_lsize; - } - - if (!rm7k_tcache_enabled) - return; - - a = addr & ~(tc_pagesize - 1); - end = (addr + size) & ~(tc_pagesize - 1); - while(1) { - invalidate_tcache_page(a); /* Page_Invalidate_T */ - if (a == end) break; - a += tc_pagesize; - } -} - -static void -rm7k_dma_cache_inv(unsigned long addr, unsigned long size) -{ - unsigned long end, a; - - a = addr & ~(sc_lsize - 1); - end = (addr + size) & ~(sc_lsize - 1); - while (1) { - invalidate_dcache_line(a); /* Hit_Invalidate_D */ - flush_icache_line(a); /* Hit_Invalidate_I */ - invalidate_scache_line(a); /* Hit_Invalidate_SD */ - if (a == end) break; - a += sc_lsize; - } - - if (!rm7k_tcache_enabled) - return; - - a = addr & ~(tc_pagesize - 1); - end = (addr + size) & ~(tc_pagesize - 1); - while(1) { - invalidate_tcache_page(a); /* Page_Invalidate_T */ - if (a == end) break; - a += tc_pagesize; - } -} - -static void -rm7k_dma_cache_wback(unsigned long addr, unsigned long size) -{ - panic("rm7k_dma_cache_wback called - should not happen.\n"); -} - -/* - * While we're protected against bad userland addresses we don't care - * very much about what happens in that case. Usually a segmentation - * fault will dump the process later on anyway ... - */ -static void rm7k_flush_cache_sigtramp(unsigned long addr) -{ - protected_writeback_dcache_line(addr & ~(dc_lsize - 1)); - protected_flush_icache_line(addr & ~(ic_lsize - 1)); -} - -/* - * Undocumented RM7000: Bit 29 in the info register of the RM7000 v2.0 - * indicates if the TLB has 48 or 64 entries. - * - * 29 1 => 64 entry JTLB - * 0 => 48 entry JTLB - */ -static inline int __attribute__((const)) ntlb_entries(void) -{ - if (get_info() & (1 << 29)) - return 64; - - return 48; -} - -void flush_tlb_all(void) -{ - unsigned long flags; - unsigned long old_ctx; - int entry; - - local_irq_save(flags); - /* Save old context and create impossible VPN2 value */ - old_ctx = get_entryhi() & 0xff; - set_entryhi(KSEG0); - set_entrylo0(0); - set_entrylo1(0); - BARRIER; - - entry = get_wired(); - - /* Blast 'em all away. */ - while (entry < ntlb_entries()) { - set_index(entry); - BARRIER; - tlb_write_indexed(); - BARRIER; - entry++; - } - BARRIER; - set_entryhi(old_ctx); - local_irq_restore(flags); -} - -void flush_tlb_mm(struct mm_struct *mm) -{ - if(mm->context != 0) { - unsigned long flags; - - local_irq_save(flags); - get_new_mmu_context(mm, asid_cache); - if (mm == current->mm) - set_entryhi(mm->context & 0xff); - local_irq_restore(flags); - } -} - -void flush_tlb_range(struct mm_struct *mm, unsigned long start, - unsigned long end) -{ - if(mm->context != 0) { - unsigned long flags; - int size; - - local_irq_save(flags); - size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; - size = (size + 1) >> 1; - if (size <= (ntlb_entries() / 2)) { - int oldpid = (get_entryhi() & 0xff); - int newpid = (mm->context & 0xff); - - start &= (PAGE_MASK << 1); - end += ((PAGE_SIZE << 1) - 1); - end &= (PAGE_MASK << 1); - while(start < end) { - int idx; - - set_entryhi(start | newpid); - start += (PAGE_SIZE << 1); - BARRIER; - tlb_probe(); - BARRIER; - idx = get_index(); - set_entrylo0(0); - set_entrylo1(0); - set_entryhi(KSEG0); - BARRIER; - if(idx < 0) - continue; - tlb_write_indexed(); - BARRIER; - } - set_entryhi(oldpid); - } else { - get_new_mmu_context(mm, asid_cache); - if(mm == current->mm) - set_entryhi(mm->context & 0xff); - } - local_irq_restore(flags); - } -} - -void flush_tlb_page(struct vm_area_struct *vma, unsigned long page) -{ - if(vma->vm_mm->context != 0) { - unsigned long flags; - int oldpid, newpid, idx; - - newpid = (vma->vm_mm->context & 0xff); - page &= (PAGE_MASK << 1); - local_irq_save(flags); - oldpid = (get_entryhi() & 0xff); - set_entryhi(page | newpid); - BARRIER; - tlb_probe(); - BARRIER; - idx = get_index(); - set_entrylo0(0); - set_entrylo1(0); - set_entryhi(KSEG0); - if(idx < 0) - goto finish; - BARRIER; - tlb_write_indexed(); - - finish: - BARRIER; - set_entryhi(oldpid); - local_irq_restore(flags); - } -} - -void pgd_init(unsigned long page) -{ - unsigned long *p = (unsigned long *) page; - int i; - - for (i = 0; i < USER_PTRS_PER_PGD; i+=8) { - p[i + 0] = (unsigned long) invalid_pte_table; - p[i + 1] = (unsigned long) invalid_pte_table; - p[i + 2] = (unsigned long) invalid_pte_table; - p[i + 3] = (unsigned long) invalid_pte_table; - p[i + 4] = (unsigned long) invalid_pte_table; - p[i + 5] = (unsigned long) invalid_pte_table; - p[i + 6] = (unsigned long) invalid_pte_table; - p[i + 7] = (unsigned long) invalid_pte_table; - } -} - -/* - * We will need multiple versions of update_mmu_cache(), one that just - * updates the TLB with the new pte(s), and another which also checks - * for the R4k "end of page" hardware bug and does the needy. - */ -void update_mmu_cache(struct vm_area_struct * vma, - unsigned long address, pte_t pte) -{ - unsigned long flags; - pgd_t *pgdp; - pmd_t *pmdp; - pte_t *ptep; - int idx, pid; - - /* - * Handle debugger faulting in for debugee. - */ - if (current->active_mm != vma->vm_mm) - return; - - pid = get_entryhi() & 0xff; - - local_irq_save(flags); - address &= (PAGE_MASK << 1); - set_entryhi(address | (pid)); - pgdp = pgd_offset(vma->vm_mm, address); - BARRIER; - tlb_probe(); - BARRIER; - pmdp = pmd_offset(pgdp, address); - idx = get_index(); - ptep = pte_offset(pmdp, address); - BARRIER; - set_entrylo0(pte_val(*ptep++) >> 6); - set_entrylo1(pte_val(*ptep) >> 6); - set_entryhi(address | (pid)); - BARRIER; - if (idx < 0) { - tlb_write_random(); - } else { - tlb_write_indexed(); - } - BARRIER; - set_entryhi(pid); - BARRIER; - local_irq_restore(flags); -} - -void show_regs(struct pt_regs * regs) -{ - /* Saved main processor registers. */ - printk(KERN_INFO "$0 : %08lx %08lx %08lx %08lx\n", - 0UL, regs->regs[1], regs->regs[2], regs->regs[3]); - printk(KERN_INFO "$4 : %08lx %08lx %08lx %08lx\n", - regs->regs[4], regs->regs[5], regs->regs[6], regs->regs[7]); - printk(KERN_INFO "$8 : %08lx %08lx %08lx %08lx\n", - regs->regs[8], regs->regs[9], regs->regs[10], regs->regs[11]); - printk(KERN_INFO "$12: %08lx %08lx %08lx %08lx\n", - regs->regs[12], regs->regs[13], regs->regs[14], regs->regs[15]); - printk(KERN_INFO "$16: %08lx %08lx %08lx %08lx\n", - regs->regs[16], regs->regs[17], regs->regs[18], regs->regs[19]); - printk(KERN_INFO "$20: %08lx %08lx %08lx %08lx\n", - regs->regs[20], regs->regs[21], regs->regs[22], regs->regs[23]); - printk(KERN_INFO "$24: %08lx %08lx\n", - regs->regs[24], regs->regs[25]); - printk(KERN_INFO "$28: %08lx %08lx %08lx %08lx\n", - regs->regs[28], regs->regs[29], regs->regs[30], regs->regs[31]); - - /* Saved cp0 registers. */ - printk(KERN_INFO "epc : %08lx %s\nStatus: %08lx\nCause : %08lx\n", - regs->cp0_epc, print_tainted(), regs->cp0_status, regs->cp0_cause); -} - -void add_wired_entry(unsigned long entrylo0, unsigned long entrylo1, - unsigned long entryhi, unsigned long pagemask) -{ - unsigned long flags; - unsigned long wired; - unsigned long old_pagemask; - unsigned long old_ctx; - - local_irq_save(flags); - /* Save old context and create impossible VPN2 value */ - old_ctx = (get_entryhi() & 0xff); - old_pagemask = get_pagemask(); - wired = get_wired(); - set_wired (wired + 1); - set_index (wired); - BARRIER; - set_pagemask (pagemask); - set_entryhi(entryhi); - set_entrylo0(entrylo0); - set_entrylo1(entrylo1); - BARRIER; - tlb_write_indexed(); - BARRIER; - - set_entryhi(old_ctx); - BARRIER; - set_pagemask (old_pagemask); - flush_tlb_all(); - local_irq_restore(flags); -} - -/* Used for loading TLB entries before trap_init() has started, when we - don't actually want to add a wired entry which remains throughout the - lifetime of the system */ - -static int temp_tlb_entry __initdata; - -__init int add_temporary_entry(unsigned long entrylo0, unsigned long entrylo1, - unsigned long entryhi, unsigned long pagemask) -{ - int ret = 0; - unsigned long flags; - unsigned long wired; - unsigned long old_pagemask; - unsigned long old_ctx; - - local_irq_save(flags); - /* Save old context and create impossible VPN2 value */ - old_ctx = (get_entryhi() & 0xff); - old_pagemask = get_pagemask(); - wired = get_wired(); - if (--temp_tlb_entry < wired) { - printk(KERN_WARNING "No TLB space left for add_temporary_entry\n"); - ret = -ENOSPC; - goto out; - } - - set_index (temp_tlb_entry); - BARRIER; - set_pagemask (pagemask); - set_entryhi(entryhi); - set_entrylo0(entrylo0); - set_entrylo1(entrylo1); - BARRIER; - tlb_write_indexed(); - BARRIER; - - set_entryhi(old_ctx); - BARRIER; - set_pagemask (old_pagemask); - out: - local_irq_restore(flags); - return ret; -} - - - -/* Detect and size the caches. */ -static inline void probe_icache(unsigned long config) -{ - icache_size = 1 << (12 + ((config >> 9) & 7)); - - printk(KERN_INFO "Primary instruction cache %dKiB.\n", icache_size >> 10); -} - -static inline void probe_dcache(unsigned long config) -{ - dcache_size = 1 << (12 + ((config >> 6) & 7)); - - printk(KERN_INFO "Primary data cache %dKiB.\n", dcache_size >> 10); -} - - -/* - * This function is executed in the uncached segment KSEG1. - * It must not touch the stack, because the stack pointer still points - * into KSEG0. - * - * Three options: - * - Write it in assembly and guarantee that we don't use the stack. - * - Disable caching for KSEG0 before calling it. - * - Pray that GCC doesn't randomly start using the stack. - * - * This being Linux, we obviously take the least sane of those options - - * following DaveM's lead in r4xx0.c - * - * It seems we get our kicks from relying on unguaranteed behaviour in GCC - */ -static __init void setup_scache(void) -{ - int register i; - - set_cp0_config(1<<3 /* CONF_SE */); - - set_taglo(0); - set_taghi(0); - - for (i=0; i<scache_size; i+=sc_lsize) { - __asm__ __volatile__ ( - ".set noreorder\n\t" - ".set mips3\n\t" - "cache %1, (%0)\n\t" - ".set mips0\n\t" - ".set reorder" - : - : "r" (KSEG0ADDR(i)), - "i" (Index_Store_Tag_SD)); - } - -} - -static inline void probe_scache(unsigned long config) -{ - void (*func)(void) = KSEG1ADDR(&setup_scache); - - if ((config >> 31) & 1) - return; - - printk(KERN_INFO "Secondary cache %dKiB, linesize %d bytes.\n", - (scache_size >> 10), sc_lsize); - - if ((config >> 3) & 1) - return; - - printk(KERN_INFO "Enabling secondary cache..."); - func(); - printk("Done\n"); -} - -static inline void probe_tcache(unsigned long config) -{ - if ((config >> 17) & 1) - return; - - /* We can't enable the L3 cache yet. There may be board-specific - * magic necessary to turn it on, and blindly asking the CPU to - * start using it would may give cache errors. - * - * Also, board-specific knowledge may allow us to use the - * CACHE Flash_Invalidate_T instruction if the tag RAM supports - * it, and may specify the size of the L3 cache so we don't have - * to probe it. - */ - printk(KERN_INFO "Tertiary cache present, %s enabled\n", - config&(1<<12) ? "already" : "not (yet)"); - - if ((config >> 12) & 1) - rm7k_tcache_enabled = 1; -} - -void __init ld_mmu_rm7k(void) -{ - unsigned long config = read_32bit_cp0_register(CP0_CONFIG); - unsigned long addr; - - printk("CPU revision is: %08x\n", read_32bit_cp0_register(CP0_PRID)); - - change_cp0_config(CONF_CM_CMASK, CONF_CM_UNCACHED); - - /* RM7000 erratum #31. The icache is screwed at startup. */ - set_taglo(0); - set_taghi(0); - for (addr = KSEG0; addr <= KSEG0 + 4096; addr += ic_lsize) { - __asm__ __volatile__ ( - ".set noreorder\n\t" - ".set mips3\n\t" - "cache\t%1, 0(%0)\n\t" - "cache\t%1, 0x1000(%0)\n\t" - "cache\t%1, 0x2000(%0)\n\t" - "cache\t%1, 0x3000(%0)\n\t" - "cache\t%2, 0(%0)\n\t" - "cache\t%2, 0x1000(%0)\n\t" - "cache\t%2, 0x2000(%0)\n\t" - "cache\t%2, 0x3000(%0)\n\t" - "cache\t%1, 0(%0)\n\t" - "cache\t%1, 0x1000(%0)\n\t" - "cache\t%1, 0x2000(%0)\n\t" - "cache\t%1, 0x3000(%0)\n\t" - ".set\tmips0\n\t" - ".set\treorder\n\t" - : - : "r" (addr), "i" (Index_Store_Tag_I), "i" (Fill)); - } - -#ifndef CONFIG_MIPS_UNCACHED - change_cp0_config(CONF_CM_CMASK, CONF_CM_CACHABLE_NONCOHERENT); -#endif - - probe_icache(config); - probe_dcache(config); - probe_scache(config); - probe_tcache(config); - - printk("TLB has %d entries.\n", ntlb_entries()); - - _clear_page = rm7k_clear_page; - _copy_page = rm7k_copy_page; - - _flush_cache_all = rm7k_flush_cache_all_d32i32; - ___flush_cache_all = __flush_cache_all_d32i32; - _flush_cache_mm = rm7k_flush_cache_mm_d32i32; - _flush_cache_range = rm7k_flush_cache_range_d32i32; - _flush_cache_page = rm7k_flush_cache_page_d32i32; - _flush_page_to_ram = rm7k_flush_page_to_ram_d32i32; - _flush_cache_sigtramp = rm7k_flush_cache_sigtramp; - _flush_icache_range = rm7k_flush_icache_range; - _flush_icache_page = rm7k_flush_icache_page; - - _dma_cache_wback_inv = rm7k_dma_cache_wback_inv; - _dma_cache_wback = rm7k_dma_cache_wback; - _dma_cache_inv = rm7k_dma_cache_inv; - - __flush_cache_all_d32i32(); - write_32bit_cp0_register(CP0_WIRED, 0); - temp_tlb_entry = ntlb_entries() - 1; - write_32bit_cp0_register(CP0_PAGEMASK, PM_4K); - flush_tlb_all(); -} diff --git a/arch/mips/mm/sb1.c b/arch/mips/mm/sb1.c deleted file mode 100644 index caf21add013d..000000000000 --- a/arch/mips/mm/sb1.c +++ /dev/null @@ -1,436 +0,0 @@ -/* - * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) - * Copyright (C) 1997, 2001 Ralf Baechle (ralf@gnu.org) - * Copyright (C) 2000 Sibyte - * - * Written by Justin Carlson (carlson@sibyte.com) - * - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - - -/* - * In this entire file, I'm not sure what the role of the L2 on the sb1250 - * is. Since it is coherent to the system, we should never need to flush - * it...right?...right??? -JDC - */ - -#include <asm/mmu_context.h> - -/* These are probed at ld_mmu time */ -static unsigned int icache_size; -static unsigned int dcache_size; - -static unsigned int icache_line_size; -static unsigned int dcache_line_size; - -static unsigned int icache_assoc; -static unsigned int dcache_assoc; - -static unsigned int icache_sets; -static unsigned int dcache_sets; -static unsigned int tlb_entries; - -void pgd_init(unsigned long page) -{ - unsigned long *p = (unsigned long *) page; - int i; - - for (i = 0; i < USER_PTRS_PER_PGD; i+=8) { - p[i + 0] = (unsigned long) invalid_pte_table; - p[i + 1] = (unsigned long) invalid_pte_table; - p[i + 2] = (unsigned long) invalid_pte_table; - p[i + 3] = (unsigned long) invalid_pte_table; - p[i + 4] = (unsigned long) invalid_pte_table; - p[i + 5] = (unsigned long) invalid_pte_table; - p[i + 6] = (unsigned long) invalid_pte_table; - p[i + 7] = (unsigned long) invalid_pte_table; - } -} - -void flush_tlb_all(void) -{ - unsigned long flags; - unsigned long old_ctx; - int entry; - - local_irq_save(flags); - /* Save old context and create impossible VPN2 value */ - old_ctx = (get_entryhi() & 0xff); - set_entrylo0(0); - set_entrylo1(0); - for (entry = 0; entry < tlb_entries; entry++) { - set_entryhi(KSEG0 + (PAGE_SIZE << 1) * entry); - set_index(entry); - tlb_write_indexed(); - } - set_entryhi(old_ctx); - local_irq_restore(flags); -} - - - -/* These are the functions hooked by the memory management function pointers */ -static void sb1_clear_page(void *page) -{ - /* JDCXXX - This should be bottlenecked by the write buffer, but these - things tend to be mildly unpredictable...should check this on the - performance model */ - - /* We prefetch 4 lines ahead. We're also "cheating" slightly here... - since we know we're on an SB1, we force the assembler to take - 64-bit operands to speed things up */ - __asm__ __volatile__( - ".set push \n" - ".set noreorder \n" - ".set noat \n" - ".set mips4 \n" - " addiu $1, %0, %2 \n" /* Calculate the end of the page to clear */ - " pref 5, 0(%0) \n" /* Prefetch the first 4 lines */ - " pref 5, 32(%0) \n" - " pref 5, 64(%0) \n" - " pref 5, 96(%0) \n" - "1: sd $0, 0(%0) \n" /* Throw out a cacheline of 0's */ - " sd $0, 8(%0) \n" - " sd $0, 16(%0) \n" - " sd $0, 24(%0) \n" - " pref 5,128(%0) \n" /* Prefetch 4 lines ahead */ - " bne $1, %0, 1b \n" - " addiu %0, %0, 32 \n" /* Next cacheline (This instruction better be short piped!) */ - ".set pop \n" - :"=r" (page) - :"0" (page), - "I" (PAGE_SIZE-32) - :"$1","memory"); - -} - -static void sb1_copy_page(void *to, void *from) -{ - - /* This should be optimized in assembly...can't use ld/sd, though, - * because the top 32 bits could be nuked if we took an interrupt - * during the routine. And this is not a good place to be cli()'ing - */ - - /* The pref's used here are using "streaming" hints, which cause the - * copied data to be kicked out of the cache sooner. A page copy often - * ends up copying a lot more data than is commonly used, so this seems - * to make sense in terms of reducing cache pollution, but I've no real - * performance data to back this up - */ - - __asm__ __volatile__( - ".set push \n" - ".set noreorder \n" - ".set noat \n" - ".set mips4 \n" - " addiu $1, %0, %4 \n" /* Calculate the end of the page to copy */ - " pref 4, 0(%0) \n" /* Prefetch the first 3 lines to be read and copied */ - " pref 5, 0(%1) \n" - " pref 4, 32(%0) \n" - " pref 5, 32(%1) \n" - " pref 4, 64(%0) \n" - " pref 5, 64(%1) \n" - "1: lw $2, 0(%0) \n" /* Block copy a cacheline */ - " lw $3, 4(%0) \n" - " lw $4, 8(%0) \n" - " lw $5, 12(%0) \n" - " lw $6, 16(%0) \n" - " lw $7, 20(%0) \n" - " lw $8, 24(%0) \n" - " lw $9, 28(%0) \n" - " pref 4, 96(%0) \n" /* Prefetch ahead */ - " pref 5, 96(%1) \n" - " sw $2, 0(%1) \n" - " sw $3, 4(%1) \n" - " sw $4, 8(%1) \n" - " sw $5, 12(%1) \n" - " sw $6, 16(%1) \n" - " sw $7, 20(%1) \n" - " sw $8, 24(%1) \n" - " sw $9, 28(%1) \n" - " addiu %1, %1, 32 \n" /* Next cacheline */ - " nop \n" /* Force next add to short pipe */ - " nop \n" /* Force next add to short pipe */ - " bne $1, %0, 1b \n" - " addiu %0, %0, 32 \n" /* Next cacheline */ - ".set pop \n" - :"=r" (to), - "=r" (from) - : - "0" (from), - "1" (to), - "I" (PAGE_SIZE-32) - :"$1","$2","$3","$4","$5","$6","$7","$8","$9","memory"); -/* - unsigned long *src = from; - unsigned long *dest = to; - unsigned long *target = (unsigned long *) (((unsigned long)src) + PAGE_SIZE); - while (src != target) { - *dest++ = *src++; - } -*/ -} - -/* - * The dcache is fully coherent to the system, with one - * big caveat: the instruction stream. In other words, - * if we miss in the icache, and have dirty data in the - * L1 dcache, then we'll go out to memory (or the L2) and - * get the not-as-recent data. - * - * So the only time we have to flush the dcache is when - * we're flushing the icache. Since the L2 is fully - * coherent to everything, including I/O, we never have - * to flush it - */ - -static void sb1_flush_cache_all(void) -{ - - /* - * Haven't worried too much about speed here; given that we're flushing - * the icache, the time to invalidate is dwarfed by the time it's going - * to take to refill it. Register usage: - * - * $1 - moving cache index - * $2 - set count - */ - if (icache_sets) { - __asm__ __volatile__ ( - ".set push \n" - ".set noreorder \n" - ".set noat \n" - ".set mips4 \n" - " move $1, %2 \n" /* Start at index 0 */ - "1: cache 0, 0($1) \n" /* Invalidate this index */ - " addiu %1, %1, -1 \n" /* Decrement loop count */ - " bnez %1, 1b \n" /* loop test */ - " addu $1, $1, %0 \n" /* Next address JDCXXX - Should be short piped */ - ".set pop \n" - ::"r" (icache_line_size), - "r" (icache_sets * icache_assoc), - "r" (KSEG0) - :"$1"); - } - if (dcache_sets) { - __asm__ __volatile__ ( - ".set push \n" - ".set noreorder \n" - ".set noat \n" - ".set mips4 \n" - " move $1, %2 \n" /* Start at index 0 */ - "1: cache 0x1, 0($1) \n" /* WB/Invalidate this index */ - " addiu %1, %1, -1 \n" /* Decrement loop count */ - " bnez %1, 1b \n" /* loop test */ - " addu $1, $1, %0 \n" /* Next address JDCXXX - Should be short piped */ - ".set pop \n" - ::"r" (dcache_line_size), - "r" (dcache_sets * dcache_assoc), - "r" (KSEG0) - :"$1"); - } -} - -/* - * When flushing a range in the icache, we have to first writeback - * the dcache for the same range, so new ifetches will see any - * data that was dirty in the dcache - */ - -static void sb1_flush_icache_range(unsigned long start, unsigned long end) -{ - - /* JDCXXX - Implement me! */ - sb1_flush_cache_all(); -} - -static void sb1_flush_cache_mm(struct mm_struct *mm) -{ - /* Don't need to do this, as the dcache is physically tagged */ -} - -static void sb1_flush_cache_range(struct vm_area_struct *vma, - unsigned long start, - unsigned long end) -{ - /* Don't need to do this, as the dcache is physically tagged */ -} - - -static void sb1_flush_cache_sigtramp(unsigned long page) -{ - /* JDCXXX - Implement me! */ - sb1_flush_cache_all(); -} - - -/* - * This only needs to make sure stores done up to this - * point are visible to other agents outside the CPU. Given - * the coherent nature of the ZBus, all that's required here is - * a sync to make sure the data gets out to the caches and is - * visible to an arbitrary A Phase from an external agent - * - * Actually, I'm not even sure that's necessary; the semantics - * of this function aren't clear. If it's supposed to serve as - * a memory barrier, this is needed. If it's only meant to - * prevent data from being invisible to non-cpu memory accessors - * for some indefinite period of time (e.g. in a non-coherent - * dcache) then this function would be a complete nop. - */ -static void sb1_flush_page_to_ram(struct page *page) -{ - __asm__ __volatile__( - " sync \n" /* Short pipe */ - :::"memory"); -} - - -/* Cribbed from the r2300 code */ -static void sb1_flush_cache_page(struct vm_area_struct *vma, - unsigned long page) -{ - sb1_flush_cache_all(); -#if 0 - struct mm_struct *mm = vma->vm_mm; - unsigned long physpage; - - /* No icache flush needed without context; */ - if (mm->context == 0) - return; - - /* No icache flush needed if the page isn't executable */ - if (!(vma->vm_flags & VM_EXEC)) - return; - - physpage = (unsigned long) page_address(page); - if (physpage) - sb1_flush_icache_range(physpage, physpage + PAGE_SIZE); -#endif -} - - -/* - * Cache set values (from the mips64 spec) - * 0 - 64 - * 1 - 128 - * 2 - 256 - * 3 - 512 - * 4 - 1024 - * 5 - 2048 - * 6 - 4096 - * 7 - Reserved - */ -static unsigned int decode_cache_sets(unsigned int config_field) -{ - if (config_field == 7) { - /* JDCXXX - Find a graceful way to abort. */ - return 0; - } - - return (1<<(config_field + 6)); -} - -/* - * Cache line size values (from the mips64 spec) - * 0 - No cache present. - * 1 - 4 bytes - * 2 - 8 bytes - * 3 - 16 bytes - * 4 - 32 bytes - * 5 - 64 bytes - * 6 - 128 bytes - * 7 - Reserved - */ -static unsigned int decode_cache_line_size(unsigned int config_field) -{ - if (config_field == 0) { - return 0; - } else if (config_field == 7) { - /* JDCXXX - Find a graceful way to abort. */ - return 0; - } - return (1<<(config_field + 1)); -} - -/* - * Relevant bits of the config1 register format (from the MIPS32/MIPS64 specs) - * - * 24:22 Icache sets per way - * 21:19 Icache line size - * 18:16 Icache Associativity - * 15:13 Dcache sets per way - * 12:10 Dcache line size - * 9:7 Dcache Associativity - */ - - -static void probe_cache_sizes(void) -{ - u32 config1; - - __asm__ __volatile__( - ".set push \n" - ".set mips64 \n" - " mfc0 %0, $16, 1 \n" /* Get config1 register */ - ".set pop \n" - :"=r" (config1)); - icache_line_size = decode_cache_line_size((config1 >> 19) & 0x7); - dcache_line_size = decode_cache_line_size((config1 >> 10) & 0x7); - icache_sets = decode_cache_sets((config1 >> 22) & 0x7); - dcache_sets = decode_cache_sets((config1 >> 13) & 0x7); - icache_assoc = ((config1 >> 16) & 0x7) + 1; - dcache_assoc = ((config1 >> 7) & 0x7) + 1; - icache_size = icache_line_size * icache_sets * icache_assoc; - dcache_size = dcache_line_size * dcache_sets * dcache_assoc; - tlb_entries = ((config1 >> 25) & 0x3f) + 1; -} - - -/* This is called from loadmmu.c. We have to set up all the - memory management function pointers, as well as initialize - the caches and tlbs */ -void ld_mmu_sb1(void) -{ - probe_cache_sizes(); - - _clear_page = sb1_clear_page; - _copy_page = sb1_copy_page; - - _flush_cache_all = sb1_flush_cache_all; - _flush_cache_mm = sb1_flush_cache_mm; - _flush_cache_range = sb1_flush_cache_range; - _flush_cache_page = sb1_flush_cache_page; - _flush_cache_sigtramp = sb1_flush_cache_sigtramp; - - _flush_page_to_ram = sb1_flush_page_to_ram; - _flush_icache_page = sb1_flush_cache_page; - _flush_icache_range = sb1_flush_icache_range; - - - /* - * JDCXXX I'm not sure whether these are necessary: is this the right - * place to initialize the tlb? If it is, why is it done - * at this level instead of as common code in loadmmu()? - */ - flush_cache_all(); - flush_tlb_all(); - - /* Turn on caching in kseg0 */ - set_cp0_config(CONF_CM_CMASK, 0); -} diff --git a/arch/mips/mm/sc-ip22.c b/arch/mips/mm/sc-ip22.c new file mode 100644 index 000000000000..507e739c54db --- /dev/null +++ b/arch/mips/mm/sc-ip22.c @@ -0,0 +1,177 @@ +/* + * sc-ip22.c: Indy cache management functions. + * + * Copyright (C) 1997, 2001 Ralf Baechle (ralf@gnu.org), + * derived from r4xx0.c by David S. Miller (dm@engr.sgi.com). + */ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/mm.h> + +#include <asm/bcache.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/system.h> +#include <asm/bootinfo.h> +#include <asm/sgi/ip22.h> +#include <asm/sgi/mc.h> + +/* Secondary cache size in bytes, if present. */ +static unsigned long scache_size; + +#undef DEBUG_CACHE + +#define SC_SIZE 0x00080000 +#define SC_LINE 32 +#define CI_MASK (SC_SIZE - SC_LINE) +#define SC_INDEX(n) ((n) & CI_MASK) + +static inline void indy_sc_wipe(unsigned long first, unsigned long last) +{ + unsigned long tmp; + + __asm__ __volatile__( + ".set\tpush\t\t\t# indy_sc_wipe\n\t" + ".set\tnoreorder\n\t" + ".set\tmips3\n\t" + ".set\tnoat\n\t" + "mfc0\t%2, $12\n\t" + "li\t$1, 0x80\t\t\t# Go 64 bit\n\t" + "mtc0\t$1, $12\n\t" + + "dli\t$1, 0x9000000080000000\n\t" + "or\t%0, $1\t\t\t# first line to flush\n\t" + "or\t%1, $1\t\t\t# last line to flush\n\t" + ".set\tat\n\t" + + "1:\tsw\t$0, 0(%0)\n\t" + "bne\t%0, %1, 1b\n\t" + " daddu\t%0, 32\n\t" + + "mtc0\t%2, $12\t\t\t# Back to 32 bit\n\t" + "nop; nop; nop; nop;\n\t" + ".set\tpop" + : "=r" (first), "=r" (last), "=&r" (tmp) + : "0" (first), "1" (last)); +} + +static void indy_sc_wback_invalidate(unsigned long addr, unsigned long size) +{ + unsigned long first_line, last_line; + unsigned int flags; + +#ifdef DEBUG_CACHE + printk("indy_sc_wback_invalidate[%08lx,%08lx]", addr, size); +#endif + + if (!size) + return; + + /* Which lines to flush? */ + first_line = SC_INDEX(addr); + last_line = SC_INDEX(addr + size - 1); + + local_irq_save(flags); + if (first_line <= last_line) { + indy_sc_wipe(first_line, last_line); + goto out; + } + + indy_sc_wipe(first_line, SC_SIZE - SC_LINE); + indy_sc_wipe(0, last_line); +out: + local_irq_restore(flags); +} + +static void indy_sc_enable(void) +{ + unsigned long addr, tmp1, tmp2; + + /* This is really cool... */ +#ifdef DEBUG_CACHE + printk("Enabling R4600 SCACHE\n"); +#endif + __asm__ __volatile__( + ".set\tpush\n\t" + ".set\tnoreorder\n\t" + ".set\tmips3\n\t" + "mfc0\t%2, $12\n\t" + "nop; nop; nop; nop;\n\t" + "li\t%1, 0x80\n\t" + "mtc0\t%1, $12\n\t" + "nop; nop; nop; nop;\n\t" + "li\t%0, 0x1\n\t" + "dsll\t%0, 31\n\t" + "lui\t%1, 0x9000\n\t" + "dsll32\t%1, 0\n\t" + "or\t%0, %1, %0\n\t" + "sb\t$0, 0(%0)\n\t" + "mtc0\t$0, $12\n\t" + "nop; nop; nop; nop;\n\t" + "mtc0\t%2, $12\n\t" + "nop; nop; nop; nop;\n\t" + ".set\tpop" + : "=r" (tmp1), "=r" (tmp2), "=r" (addr)); +} + +static void indy_sc_disable(void) +{ + unsigned long tmp1, tmp2, tmp3; + +#ifdef DEBUG_CACHE + printk("Disabling R4600 SCACHE\n"); +#endif + __asm__ __volatile__( + ".set\tpush\n\t" + ".set\tnoreorder\n\t" + ".set\tmips3\n\t" + "li\t%0, 0x1\n\t" + "dsll\t%0, 31\n\t" + "lui\t%1, 0x9000\n\t" + "dsll32\t%1, 0\n\t" + "or\t%0, %1, %0\n\t" + "mfc0\t%2, $12\n\t" + "nop; nop; nop; nop\n\t" + "li\t%1, 0x80\n\t" + "mtc0\t%1, $12\n\t" + "nop; nop; nop; nop\n\t" + "sh\t$0, 0(%0)\n\t" + "mtc0\t$0, $12\n\t" + "nop; nop; nop; nop\n\t" + "mtc0\t%2, $12\n\t" + "nop; nop; nop; nop\n\t" + ".set\tpop" + : "=r" (tmp1), "=r" (tmp2), "=r" (tmp3)); +} + +static inline int __init indy_sc_probe(void) +{ + unsigned int size = ip22_eeprom_read(&sgimc->eeprom, 17); + if (size == 0) + return 0; + + size <<= PAGE_SHIFT; + printk(KERN_INFO "R4600/R5000 SCACHE size %ldK, linesize 32 bytes.\n", + size >> 10); + scache_size = size; + + return 1; +} + +/* XXX Check with wje if the Indy caches can differenciate between + writeback + invalidate and just invalidate. */ +struct bcache_ops indy_sc_ops = { + .bc_enable = indy_sc_enable, + .bc_disable = indy_sc_disable, + .bc_wback_inv = indy_sc_wback_invalidate, + .bc_inv = indy_sc_wback_invalidate +}; + +void __init indy_sc_init(void) +{ + if (indy_sc_probe()) { + indy_sc_enable(); + bcops = &indy_sc_ops; + } +} diff --git a/arch/mips/mm/sc-r5k.c b/arch/mips/mm/sc-r5k.c new file mode 100644 index 000000000000..0da8b31cff9c --- /dev/null +++ b/arch/mips/mm/sc-r5k.c @@ -0,0 +1,115 @@ +/* + * Copyright (C) 1997, 2001 Ralf Baechle (ralf@gnu.org), + * derived from r4xx0.c by David S. Miller (dm@engr.sgi.com). + */ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/mm.h> + +#include <asm/mipsregs.h> +#include <asm/bcache.h> +#include <asm/cacheops.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/system.h> +#include <asm/mmu_context.h> + +/* Secondary cache size in bytes, if present. */ +static unsigned long scache_size; + +#define SC_LINE 32 +#define SC_PAGE (128*SC_LINE) + +#define cache_op(base,op) \ +__asm__ __volatile__(" \ + .set noreorder; \ + .set mips3; \ + cache %1, (%0); \ + .set mips0; \ + .set reorder" \ + : \ + : "r" (base), \ + "i" (op)); + +static inline void blast_r5000_scache(void) +{ + unsigned long start = KSEG0; + unsigned long end = KSEG0 + scache_size; + + while(start < end) { + cache_op(start, R5K_Page_Invalidate_S); + start += SC_PAGE; + } +} + +static void r5k_dma_cache_inv_sc(unsigned long addr, unsigned long size) +{ + unsigned long end, a; + + if (size >= scache_size) { + blast_r5000_scache(); + return; + } + + /* On the R5000 secondary cache we cannot + * invalidate less than a page at a time. + * The secondary cache is physically indexed, write-through. + */ + a = addr & ~(SC_PAGE - 1); + end = (addr + size - 1) & ~(SC_PAGE - 1); + while (a <= end) { + cache_op(a, R5K_Page_Invalidate_S); + a += SC_PAGE; + } +} + +static void r5k_sc_enable(void) +{ + unsigned long flags; + + local_irq_save(flags); + change_c0_config(R5K_CONF_SE, R5K_CONF_SE); + blast_r5000_scache(); + local_irq_restore(flags); +} + +static void r5k_sc_disable(void) +{ + unsigned long flags; + + local_irq_save(flags); + blast_r5000_scache(); + change_c0_config(R5K_CONF_SE, 0); + local_irq_restore(flags); +} + +static inline int __init r5k_sc_probe(void) +{ + unsigned long config = read_c0_config(); + + if (config & CONF_SC) + return(0); + + scache_size = (512 * 1024) << ((config & R5K_CONF_SS) >> 20); + + printk("R5000 SCACHE size %ldkB, linesize 32 bytes.\n", + scache_size >> 10); + + return 1; +} + +static struct bcache_ops r5k_sc_ops = { + .bc_enable = r5k_sc_enable, + .bc_disable = r5k_sc_disable, + .bc_wback_inv = r5k_dma_cache_inv_sc, + .bc_inv = r5k_dma_cache_inv_sc +}; + +void __init r5k_sc_init(void) +{ + if (r5k_sc_probe()) { + r5k_sc_enable(); + bcops = &r5k_sc_ops; + } +} diff --git a/arch/mips/mm/sc-rm7k.c b/arch/mips/mm/sc-rm7k.c new file mode 100644 index 000000000000..c17aa41b05d0 --- /dev/null +++ b/arch/mips/mm/sc-rm7k.c @@ -0,0 +1,191 @@ +/* + * sc-rm7k.c: RM7000 cache management functions. + * + * Copyright (C) 1997, 2001, 2003 Ralf Baechle (ralf@gnu.org), + */ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/mm.h> + +#include <asm/addrspace.h> +#include <asm/bcache.h> +#include <asm/cacheops.h> +#include <asm/mipsregs.h> +#include <asm/processor.h> + +/* Primary cache parameters. */ +#define sc_lsize 32 +#define tc_pagesize (32*128) + +/* Secondary cache parameters. */ +#define scache_size (256*1024) /* Fixed to 256KiB on RM7000 */ + +extern unsigned long icache_way_size, dcache_way_size; + +#include <asm/r4kcache.h> + +int rm7k_tcache_enabled; + +/* + * Writeback and invalidate the primary cache dcache before DMA. + * (XXX These need to be fixed ...) + */ +static void rm7k_sc_wback_inv(unsigned long addr, unsigned long size) +{ + unsigned long end, a; + +#ifdef DEBUG_CACHE + printk("rm7k_sc_wback_inv[%08lx,%08lx]", addr, size); +#endif + + a = addr & ~(sc_lsize - 1); + end = (addr + size - 1) & ~(sc_lsize - 1); + while (1) { + flush_scache_line(a); /* Hit_Writeback_Inv_SD */ + if (a == end) + break; + a += sc_lsize; + } + + if (!rm7k_tcache_enabled) + return; + + a = addr & ~(tc_pagesize - 1); + end = (addr + size - 1) & ~(tc_pagesize - 1); + while(1) { + invalidate_tcache_page(a); /* Page_Invalidate_T */ + if (a == end) + break; + a += tc_pagesize; + } +} + +static void rm7k_sc_inv(unsigned long addr, unsigned long size) +{ + unsigned long end, a; + +#ifdef DEBUG_CACHE + printk("rm7k_sc_inv[%08lx,%08lx]", addr, size); +#endif + + a = addr & ~(sc_lsize - 1); + end = (addr + size - 1) & ~(sc_lsize - 1); + while (1) { + invalidate_scache_line(a); /* Hit_Invalidate_SD */ + if (a == end) + break; + a += sc_lsize; + } + + if (!rm7k_tcache_enabled) + return; + + a = addr & ~(tc_pagesize - 1); + end = (addr + size - 1) & ~(tc_pagesize - 1); + while(1) { + invalidate_tcache_page(a); /* Page_Invalidate_T */ + if (a == end) + break; + a += tc_pagesize; + } +} + +/* + * This function is executed in the uncached segment KSEG1. + * It must not touch the stack, because the stack pointer still points + * into KSEG0. + * + * Three options: + * - Write it in assembly and guarantee that we don't use the stack. + * - Disable caching for KSEG0 before calling it. + * - Pray that GCC doesn't randomly start using the stack. + * + * This being Linux, we obviously take the least sane of those options - + * following DaveM's lead in c-r4k.c + * + * It seems we get our kicks from relying on unguaranteed behaviour in GCC + */ +static __init void rm7k_sc_enable(void) +{ + int i; + + set_c0_config(1<<3); /* CONF_SE */ + + write_c0_taglo(0); + write_c0_taghi(0); + + for (i=0; i<scache_size; i+=sc_lsize) { + __asm__ __volatile__ ( + ".set noreorder\n\t" + ".set mips3\n\t" + "cache %1, (%0)\n\t" + ".set mips0\n\t" + ".set reorder" + : + : "r" (KSEG0ADDR(i)), + "i" (Index_Store_Tag_SD)); + } +} + +static void rm7k_sc_disable(void) +{ + set_c0_config(1<<3); /* CONF_SE */ +} + +static inline int __init rm7k_sc_probe(void) +{ + void (*func)(void) = KSEG1ADDR(&rm7k_sc_enable); + unsigned int config = read_c0_config(); + + if ((config >> 31) & 1) + return 0; + + printk(KERN_INFO "Secondary cache size %ldK, linesize 32 bytes.\n", + (scache_size >> 10), sc_lsize); + + if ((config >> 3) & 1) + return; + + printk(KERN_INFO "Enabling secondary cache..."); + func(); + printk(" done\n"); + + /* + * While we're at it let's deal with the tertiary cache. + */ + if ((config >> 17) & 1) + return 1; + + /* + * We can't enable the L3 cache yet. There may be board-specific + * magic necessary to turn it on, and blindly asking the CPU to + * start using it would may give cache errors. + * + * Also, board-specific knowledge may allow us to use the + * CACHE Flash_Invalidate_T instruction if the tag RAM supports + * it, and may specify the size of the L3 cache so we don't have + * to probe it. + */ + printk(KERN_INFO "Tertiary cache present, %s enabled\n", + config&(1<<12) ? "already" : "not (yet)"); + + if ((config >> 12) & 1) + rm7k_tcache_enabled = 1; + + return 1; +} + +struct bcache_ops rm7k_sc_ops = { + .bc_enable = rm7k_sc_enable, + .bc_disable = rm7k_sc_disable, + .bc_wback_inv = rm7k_sc_wback_inv, + .bc_inv = rm7k_sc_inv +}; + +void __init rm7k_sc_init(void) +{ + if (rm7k_sc_probe()) { + rm7k_sc_enable(); + bcops = &rm7k_sc_ops; + } +} diff --git a/arch/mips/mm/tlb-r3k.c b/arch/mips/mm/tlb-r3k.c new file mode 100644 index 000000000000..9ad6f754a2cb --- /dev/null +++ b/arch/mips/mm/tlb-r3k.c @@ -0,0 +1,290 @@ +/* + * r2300.c: R2000 and R3000 specific mmu/cache code. + * + * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * + * with a lot of changes to make this thing work for R3000s + * Tx39XX R4k style caches added. HK + * Copyright (C) 1998, 1999, 2000 Harald Koerfgen + * Copyright (C) 1998 Gleb Raiko & Vladimir Roganov + * Copyright (C) 2002 Ralf Baechle + * Copyright (C) 2002 Maciej W. Rozycki + */ +#include <linux/config.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/mm.h> + +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/mmu_context.h> +#include <asm/system.h> +#include <asm/isadep.h> +#include <asm/io.h> +#include <asm/bootinfo.h> +#include <asm/cpu.h> + +#undef DEBUG_TLB + +extern char except_vec0_r2300; + +/* CP0 hazard avoidance. */ +#define BARRIER \ + __asm__ __volatile__( \ + ".set push\n\t" \ + ".set noreorder\n\t" \ + "nop\n\t" \ + ".set pop\n\t") + +int r3k_have_wired_reg; /* should be in cpu_data? */ + +/* TLB operations. */ +void local_flush_tlb_all(void) +{ + unsigned long flags; + unsigned long old_ctx; + int entry; + +#ifdef DEBUG_TLB + printk("[tlball]"); +#endif + + local_irq_save(flags); + old_ctx = read_c0_entryhi() & ASID_MASK; + write_c0_entrylo0(0); + entry = r3k_have_wired_reg ? read_c0_wired() : 8; + for (; entry < current_cpu_data.tlbsize; entry++) { + write_c0_index(entry << 8); + write_c0_entryhi((entry | 0x80000) << 12); + BARRIER; + tlb_write_indexed(); + } + write_c0_entryhi(old_ctx); + local_irq_restore(flags); +} + +void local_flush_tlb_mm(struct mm_struct *mm) +{ + int cpu = smp_processor_id(); + + if (cpu_context(cpu, mm) != 0) { +#ifdef DEBUG_TLB + printk("[tlbmm<%lu>]", (unsigned long)cpu_context(cpu, mm)); +#endif + drop_mmu_context(mm, cpu); + } +} + +void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, + unsigned long end) +{ + struct mm_struct *mm = vma->vm_mm; + int cpu = smp_processor_id(); + + if (cpu_context(cpu, mm) != 0) { + unsigned long flags; + int size; + +#ifdef DEBUG_TLB + printk("[tlbrange<%lu,0x%08lx,0x%08lx>]", + cpu_context(cpu, mm) & ASID_MASK, start, end); +#endif + local_irq_save(flags); + size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; + if (size <= current_cpu_data.tlbsize) { + int oldpid = read_c0_entryhi() & ASID_MASK; + int newpid = cpu_context(cpu, mm) & ASID_MASK; + + start &= PAGE_MASK; + end += PAGE_SIZE - 1; + end &= PAGE_MASK; + while (start < end) { + int idx; + + write_c0_entryhi(start | newpid); + start += PAGE_SIZE; /* BARRIER */ + tlb_probe(); + idx = read_c0_index(); + write_c0_entrylo0(0); + write_c0_entryhi(KSEG0); + if (idx < 0) /* BARRIER */ + continue; + tlb_write_indexed(); + } + write_c0_entryhi(oldpid); + } else { + drop_mmu_context(mm, cpu); + } + local_irq_restore(flags); + } +} + +void local_flush_tlb_kernel_range(unsigned long start, unsigned long end) +{ + unsigned long flags; + int size; + +#ifdef DEBUG_TLB + printk("[tlbrange<%lu,0x%08lx,0x%08lx>]", start, end); +#endif + local_irq_save(flags); + size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; + if (size <= current_cpu_data.tlbsize) { + int pid = read_c0_entryhi(); + + start &= PAGE_MASK; + end += PAGE_SIZE - 1; + end &= PAGE_MASK; + + while (start < end) { + int idx; + + write_c0_entryhi(start); + start += PAGE_SIZE; /* BARRIER */ + tlb_probe(); + idx = read_c0_index(); + write_c0_entrylo0(0); + write_c0_entryhi(KSEG0); + if (idx < 0) /* BARRIER */ + continue; + tlb_write_indexed(); + } + write_c0_entryhi(pid); + } else { + local_flush_tlb_all(); + } + local_irq_restore(flags); +} + +void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) +{ + int cpu = smp_processor_id(); + + if (!vma || cpu_context(cpu, vma->vm_mm) != 0) { + unsigned long flags; + int oldpid, newpid, idx; + +#ifdef DEBUG_TLB + printk("[tlbpage<%lu,0x%08lx>]", cpu_context(cpu, vma->vm_mm), page); +#endif + newpid = cpu_context(cpu, vma->vm_mm) & ASID_MASK; + page &= PAGE_MASK; + local_irq_save(flags); + oldpid = read_c0_entryhi() & ASID_MASK; + write_c0_entryhi(page | newpid); + BARRIER; + tlb_probe(); + idx = read_c0_index(); + write_c0_entrylo0(0); + write_c0_entryhi(KSEG0); + if (idx < 0) /* BARRIER */ + goto finish; + tlb_write_indexed(); + +finish: + write_c0_entryhi(oldpid); + local_irq_restore(flags); + } +} + +void __update_tlb(struct vm_area_struct *vma, unsigned long address, pte_t pte) +{ + unsigned long flags; + int idx, pid; + + /* + * Handle debugger faulting in for debugee. + */ + if (current->active_mm != vma->vm_mm) + return; + + pid = read_c0_entryhi() & ASID_MASK; + +#ifdef DEBUG_TLB + if ((pid != (cpu_context(cpu, vma->vm_mm) & ASID_MASK)) || (cpu_context(cpu, vma->vm_mm) == 0)) { + printk("update_mmu_cache: Wheee, bogus tlbpid mmpid=%lu tlbpid=%d\n", + (cpu_context(cpu, vma->vm_mm)), pid); + } +#endif + + local_irq_save(flags); + address &= PAGE_MASK; + write_c0_entryhi(address | pid); + BARRIER; + tlb_probe(); + idx = read_c0_index(); + write_c0_entrylo0(pte_val(pte)); + write_c0_entryhi(address | pid); + if (idx < 0) { /* BARRIER */ + tlb_write_random(); + } else { + tlb_write_indexed(); + } + write_c0_entryhi(pid); + local_irq_restore(flags); +} + +void __init add_wired_entry(unsigned long entrylo0, unsigned long entrylo1, + unsigned long entryhi, unsigned long pagemask) +{ + unsigned long flags; + unsigned long old_ctx; + static unsigned long wired = 0; + + if (r3k_have_wired_reg) { /* TX39XX */ + unsigned long old_pagemask; + unsigned long w; + +#ifdef DEBUG_TLB + printk("[tlbwired<entry lo0 %8x, hi %8x\n, pagemask %8x>]\n", + entrylo0, entryhi, pagemask); +#endif + + local_irq_save(flags); + /* Save old context and create impossible VPN2 value */ + old_ctx = read_c0_entryhi() & ASID_MASK; + old_pagemask = read_c0_pagemask(); + w = read_c0_wired(); + write_c0_wired(w + 1); + if (read_c0_wired() != w + 1) { + printk("[tlbwired] No WIRED reg?\n"); + return; + } + write_c0_index(w << 8); + write_c0_pagemask(pagemask); + write_c0_entryhi(entryhi); + write_c0_entrylo0(entrylo0); + BARRIER; + tlb_write_indexed(); + + write_c0_entryhi(old_ctx); + write_c0_pagemask(old_pagemask); + local_flush_tlb_all(); + local_irq_restore(flags); + + } else if (wired < 8) { +#ifdef DEBUG_TLB + printk("[tlbwired<entry lo0 %8x, hi %8x\n>]\n", + entrylo0, entryhi); +#endif + + local_irq_save(flags); + old_ctx = read_c0_entryhi() & ASID_MASK; + write_c0_entrylo0(entrylo0); + write_c0_entryhi(entryhi); + write_c0_index(wired); + wired++; /* BARRIER */ + tlb_write_indexed(); + write_c0_entryhi(old_ctx); + local_flush_tlb_all(); + local_irq_restore(flags); + } +} + +void __init r3k_tlb_init(void) +{ + local_flush_tlb_all(); + memcpy((void *)KSEG0, &except_vec0_r2300, 0x80); + flush_icache_range(KSEG0, KSEG0 + 0x80); +} diff --git a/arch/mips/mm/tlb-r4k.c b/arch/mips/mm/tlb-r4k.c new file mode 100644 index 000000000000..36126b543518 --- /dev/null +++ b/arch/mips/mm/tlb-r4k.c @@ -0,0 +1,459 @@ +/* + * 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. + * + * r4xx0.c: R4000 processor variant specific MMU/Cache routines. + * + * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * Copyright (C) 1997, 1998, 1999, 2000 Ralf Baechle ralf@gnu.org + * + * To do: + * + * - this code is a overbloated pig + * - many of the bug workarounds are not efficient at all, but at + * least they are functional ... + */ +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/mm.h> + +#include <asm/cpu.h> +#include <asm/bootinfo.h> +#include <asm/mmu_context.h> +#include <asm/pgtable.h> +#include <asm/system.h> + +#undef DEBUG_TLB +#undef DEBUG_TLBUPDATE + +extern char except_vec0_nevada, except_vec0_r4000, except_vec0_r4600; + +/* CP0 hazard avoidance. */ +#define BARRIER __asm__ __volatile__(".set noreorder\n\t" \ + "nop; nop; nop; nop; nop; nop;\n\t" \ + ".set reorder\n\t") + +void local_flush_tlb_all(void) +{ + unsigned long flags; + unsigned long old_ctx; + int entry; + +#ifdef DEBUG_TLB + printk("[tlball]"); +#endif + + local_irq_save(flags); + /* Save old context and create impossible VPN2 value */ + old_ctx = (read_c0_entryhi() & ASID_MASK); + write_c0_entrylo0(0); + write_c0_entrylo1(0); + BARRIER; + + entry = read_c0_wired(); + + /* Blast 'em all away. */ + while (entry < current_cpu_data.tlbsize) { + /* + * Make sure all entries differ. If they're not different + * MIPS32 will take revenge ... + */ + write_c0_entryhi(KSEG0 + entry*0x2000); + write_c0_index(entry); + BARRIER; + tlb_write_indexed(); + BARRIER; + entry++; + } + BARRIER; + write_c0_entryhi(old_ctx); + local_irq_restore(flags); +} + +void local_flush_tlb_mm(struct mm_struct *mm) +{ + int cpu = smp_processor_id(); + + if (cpu_context(cpu, mm) != 0) { +#ifdef DEBUG_TLB + printk("[tlbmm<%d>]", cpu_context(cpu, mm)); +#endif + drop_mmu_context(mm,cpu); + } +} + +void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, + unsigned long end) +{ + struct mm_struct *mm = vma->vm_mm; + int cpu = smp_processor_id(); + + if (cpu_context(cpu, mm) != 0) { + unsigned long flags; + int size; + +#ifdef DEBUG_TLB + printk("[tlbrange<%02x,%08lx,%08lx>]", cpu_context(cpu, mm) & ASID_MASK, + start, end); +#endif + local_irq_save(flags); + size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; + size = (size + 1) >> 1; + if (size <= current_cpu_data.tlbsize/2) { + int oldpid = read_c0_entryhi() & ASID_MASK; + int newpid = cpu_context(cpu, mm) & ASID_MASK; + + start &= (PAGE_MASK << 1); + end += ((PAGE_SIZE << 1) - 1); + end &= (PAGE_MASK << 1); + while (start < end) { + int idx; + + write_c0_entryhi(start | newpid); + start += (PAGE_SIZE << 1); + BARRIER; + tlb_probe(); + BARRIER; + idx = read_c0_index(); + write_c0_entrylo0(0); + write_c0_entrylo1(0); + if (idx < 0) + continue; + /* Make sure all entries differ. */ + write_c0_entryhi(KSEG0 + idx*0x2000); + BARRIER; + tlb_write_indexed(); + BARRIER; + } + write_c0_entryhi(oldpid); + } else { + drop_mmu_context(mm, cpu); + } + local_irq_restore(flags); + } +} + +void local_flush_tlb_kernel_range(unsigned long start, unsigned long end) +{ + unsigned long flags; + int size; + +#ifdef DEBUG_TLB + printk("[tlbkernelrange<%02x,%08lx,%08lx>]", start, end); +#endif + local_irq_save(flags); + size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; + size = (size + 1) >> 1; + if (size <= current_cpu_data.tlbsize / 2) { + int pid = read_c0_entryhi(); + + start &= (PAGE_MASK << 1); + end += ((PAGE_SIZE << 1) - 1); + end &= (PAGE_MASK << 1); + + while (start < end) { + int idx; + + write_c0_entryhi(start); + start += (PAGE_SIZE << 1); + BARRIER; + tlb_probe(); + BARRIER; + idx = read_c0_index(); + write_c0_entrylo0(0); + write_c0_entrylo1(0); + if (idx < 0) + continue; + /* Make sure all entries differ. */ + write_c0_entryhi(KSEG0 + idx*0x2000); + BARRIER; + tlb_write_indexed(); + BARRIER; + } + write_c0_entryhi(pid); + } else { + local_flush_tlb_all(); + } + local_irq_restore(flags); +} + +void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) +{ + int cpu = smp_processor_id(); + + if (!vma || cpu_context(cpu, vma->vm_mm) != 0) { + unsigned long flags; + int oldpid, newpid, idx; + +#ifdef DEBUG_TLB + printk("[tlbpage<%d,%08lx>]", cpu_context(cpu, vma->vm_mm), + page); +#endif + newpid = (cpu_context(cpu, vma->vm_mm) & ASID_MASK); + page &= (PAGE_MASK << 1); + local_irq_save(flags); + oldpid = (read_c0_entryhi() & ASID_MASK); + write_c0_entryhi(page | newpid); + BARRIER; + tlb_probe(); + BARRIER; + idx = read_c0_index(); + write_c0_entrylo0(0); + write_c0_entrylo1(0); + if(idx < 0) + goto finish; + /* Make sure all entries differ. */ + write_c0_entryhi(KSEG0+idx*0x2000); + BARRIER; + tlb_write_indexed(); + + finish: + BARRIER; + write_c0_entryhi(oldpid); + local_irq_restore(flags); + } +} + +/* + * This one is only used for pages with the global bit set so we don't care + * much about the ASID. + */ +void local_flush_tlb_one(unsigned long page) +{ + unsigned long flags; + int oldpid, idx; + + local_irq_save(flags); + page &= (PAGE_MASK << 1); + oldpid = read_c0_entryhi() & 0xff; + write_c0_entryhi(page); + BARRIER; + tlb_probe(); + BARRIER; + idx = read_c0_index(); + write_c0_entrylo0(0); + write_c0_entrylo1(0); + if (idx >= 0) { + /* Make sure all entries differ. */ + write_c0_entryhi(KSEG0+(idx<<(PAGE_SHIFT+1))); + BARRIER; + tlb_write_indexed(); + } + BARRIER; + write_c0_entryhi(oldpid); + + local_irq_restore(flags); +} + +/* We will need multiple versions of update_mmu_cache(), one that just + * updates the TLB with the new pte(s), and another which also checks + * for the R4k "end of page" hardware bug and does the needy. + */ +void __update_tlb(struct vm_area_struct * vma, unsigned long address, pte_t pte) +{ + unsigned long flags; + pgd_t *pgdp; + pmd_t *pmdp; + pte_t *ptep; + int idx, pid; + + /* + * Handle debugger faulting in for debugee. + */ + if (current->active_mm != vma->vm_mm) + return; + + pid = read_c0_entryhi() & ASID_MASK; + +#ifdef DEBUG_TLB + if ((pid != cpu_context(cpu, vma->vm_mm) & ASID_MASK) || + (cpu_context(vma->vm_mm) == 0)) { + printk("update_mmu_cache: Wheee, bogus tlbpid mmpid=%d " + "tlbpid=%d\n", + (int) (cpu_context(cpu, vma->vm_mm) & ASID_MASK), pid); + } +#endif + + local_irq_save(flags); + address &= (PAGE_MASK << 1); + write_c0_entryhi(address | pid); + pgdp = pgd_offset(vma->vm_mm, address); + BARRIER; + tlb_probe(); + BARRIER; + pmdp = pmd_offset(pgdp, address); + idx = read_c0_index(); + ptep = pte_offset_map(pmdp, address); + BARRIER; + write_c0_entrylo0(pte_val(*ptep++) >> 6); + write_c0_entrylo1(pte_val(*ptep) >> 6); + write_c0_entryhi(address | pid); + BARRIER; + if (idx < 0) { + tlb_write_random(); + } else { + tlb_write_indexed(); + } + BARRIER; + write_c0_entryhi(pid); + BARRIER; + local_irq_restore(flags); +} + +#if 0 +static void r4k_update_mmu_cache_hwbug(struct vm_area_struct * vma, + unsigned long address, pte_t pte) +{ + unsigned long flags; + pgd_t *pgdp; + pmd_t *pmdp; + pte_t *ptep; + int idx; + + local_irq_save(flags); + address &= (PAGE_MASK << 1); + write_c0_entryhi(address | (read_c0_entryhi() & ASID_MASK)); + pgdp = pgd_offset(vma->vm_mm, address); + tlb_probe(); + pmdp = pmd_offset(pgdp, address); + idx = read_c0_index(); + ptep = pte_offset_map(pmdp, address); + write_c0_entrylo0(pte_val(*ptep++) >> 6); + write_c0_entrylo1(pte_val(*ptep) >> 6); + BARRIER; + if (idx < 0) + tlb_write_random(); + else + tlb_write_indexed(); + BARRIER; + local_irq_restore(flags); +} +#endif + +void __init add_wired_entry(unsigned long entrylo0, unsigned long entrylo1, + unsigned long entryhi, unsigned long pagemask) +{ + unsigned long flags; + unsigned long wired; + unsigned long old_pagemask; + unsigned long old_ctx; + + local_irq_save(flags); + /* Save old context and create impossible VPN2 value */ + old_ctx = read_c0_entryhi() & ASID_MASK; + old_pagemask = read_c0_pagemask(); + wired = read_c0_wired(); + write_c0_wired(wired + 1); + write_c0_index(wired); + BARRIER; + write_c0_pagemask(pagemask); + write_c0_entryhi(entryhi); + write_c0_entrylo0(entrylo0); + write_c0_entrylo1(entrylo1); + BARRIER; + tlb_write_indexed(); + BARRIER; + + write_c0_entryhi(old_ctx); + BARRIER; + write_c0_pagemask(old_pagemask); + local_flush_tlb_all(); + local_irq_restore(flags); +} + +/* + * Used for loading TLB entries before trap_init() has started, when we + * don't actually want to add a wired entry which remains throughout the + * lifetime of the system + */ + +static int temp_tlb_entry __initdata; + +__init int add_temporary_entry(unsigned long entrylo0, unsigned long entrylo1, + unsigned long entryhi, unsigned long pagemask) +{ + int ret = 0; + unsigned long flags; + unsigned long wired; + unsigned long old_pagemask; + unsigned long old_ctx; + + local_irq_save(flags); + /* Save old context and create impossible VPN2 value */ + old_ctx = read_c0_entryhi() & ASID_MASK; + old_pagemask = read_c0_pagemask(); + wired = read_c0_wired(); + if (--temp_tlb_entry < wired) { + printk(KERN_WARNING "No TLB space left for add_temporary_entry\n"); + ret = -ENOSPC; + goto out; + } + + write_c0_index(temp_tlb_entry); + BARRIER; + write_c0_pagemask(pagemask); + write_c0_entryhi(entryhi); + write_c0_entrylo0(entrylo0); + write_c0_entrylo1(entrylo1); + BARRIER; + tlb_write_indexed(); + BARRIER; + + write_c0_entryhi(old_ctx); + BARRIER; + write_c0_pagemask(old_pagemask); +out: + local_irq_restore(flags); + return ret; +} + +static void __init probe_tlb(unsigned long config) +{ + unsigned int prid, config1; + + prid = read_c0_prid() & ASID_MASK; + if (prid == PRID_IMP_RM7000 || !(config & (1 << 31))) + /* + * Not a MIPS32 complianant CPU. Config 1 register not + * supported, we assume R4k style. Cpu probing already figured + * out the number of tlb entries. + */ + return; + +#if defined(CONFIG_CPU_MIPS32) || defined (CONFIG_CPU_MIPS64) + config1 = read_c0_config1(); + if (!((config >> 7) & 3)) + panic("No MMU present"); + else + current_cpu_data.tlbsize = ((config1 >> 25) & 0x3f) + 1; +#endif +} + +void __init r4k_tlb_init(void) +{ + u32 config = read_c0_config(); + + /* + * You should never change this register: + * - On R4600 1.7 the tlbp never hits for pages smaller than + * the value in the c0_pagemask register. + * - The entire mm handling assumes the c0_pagemask register to + * be set for 4kb pages. + */ + probe_tlb(config); + write_c0_pagemask(PM_4K); + write_c0_wired(0); + temp_tlb_entry = current_cpu_data.tlbsize - 1; + local_flush_tlb_all(); + + if (cpu_has_4kex && cpu_has_4ktlb) { + if (current_cpu_data.cputype == CPU_NEVADA) + memcpy((void *)KSEG0, &except_vec0_nevada, 0x80); + else if (current_cpu_data.cputype == CPU_R4600) + memcpy((void *)KSEG0, &except_vec0_r4600, 0x80); + else + memcpy((void *)KSEG0, &except_vec0_r4000, 0x80); + flush_icache_range(KSEG0, KSEG0 + 0x80); + } +} diff --git a/arch/mips/mm/tlb-sb1.c b/arch/mips/mm/tlb-sb1.c new file mode 100644 index 000000000000..e4a7747ac197 --- /dev/null +++ b/arch/mips/mm/tlb-sb1.c @@ -0,0 +1,339 @@ +/* + * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * Copyright (C) 1997, 2001 Ralf Baechle (ralf@gnu.org) + * Copyright (C) 2000, 2001 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include <linux/config.h> +#include <asm/mmu_context.h> +#include <asm/bootinfo.h> +#include <asm/cpu.h> + +extern char except_vec0_sb1[]; + +/* Dump the current entry* and pagemask registers */ +static inline void dump_cur_tlb_regs(void) +{ + unsigned int entryhihi, entryhilo, entrylo0hi, entrylo0lo, entrylo1hi; + unsigned int entrylo1lo, pagemask; + + __asm__ __volatile__ ( + ".set push \n" + ".set noreorder \n" + ".set mips64 \n" + ".set noat \n" + " dmfc0 $1, $10 \n" + " dsrl32 %0, $1, 0 \n" + " sll %1, $1, 0 \n" + " dmfc0 $1, $2 \n" + " dsrl32 %2, $1, 0 \n" + " sll %3, $1, 0 \n" + " dmfc0 $1, $3 \n" + " dsrl32 %4, $1, 0 \n" + " sll %5, $1, 0 \n" + " mfc0 %6, $5 \n" + ".set pop \n" + : "=r" (entryhihi), "=r" (entryhilo), + "=r" (entrylo0hi), "=r" (entrylo0lo), + "=r" (entrylo1hi), "=r" (entrylo1lo), + "=r" (pagemask)); + + printk("%08X%08X %08X%08X %08X%08X %08X", + entryhihi, entryhilo, + entrylo0hi, entrylo0lo, + entrylo1hi, entrylo1lo, + pagemask); +} + +void sb1_dump_tlb(void) +{ + unsigned long old_ctx; + unsigned long flags; + int entry; + local_irq_save(flags); + old_ctx = read_c0_entryhi(); + printk("Current TLB registers state:\n" + " EntryHi EntryLo0 EntryLo1 PageMask Index\n" + "--------------------------------------------------------------------\n"); + dump_cur_tlb_regs(); + printk(" %08X\n", read_c0_index()); + printk("\n\nFull TLB Dump:\n" + "Idx EntryHi EntryLo0 EntryLo1 PageMask\n" + "--------------------------------------------------------------\n"); + for (entry = 0; entry < current_cpu_data.tlbsize; entry++) { + write_c0_index(entry); + printk("\n%02i ", entry); + tlb_read(); + dump_cur_tlb_regs(); + } + printk("\n"); + write_c0_entryhi(old_ctx); + local_irq_restore(flags); +} + +void local_flush_tlb_all(void) +{ + unsigned long flags; + unsigned long old_ctx; + int entry; + + local_irq_save(flags); + /* Save old context and create impossible VPN2 value */ + old_ctx = read_c0_entryhi() & ASID_MASK; + write_c0_entrylo0(0); + write_c0_entrylo1(0); + for (entry = 0; entry < current_cpu_data.tlbsize; entry++) { + write_c0_entryhi(KSEG0 + (PAGE_SIZE << 1) * entry); + write_c0_index(entry); + tlb_write_indexed(); + } + write_c0_entryhi(old_ctx); + local_irq_restore(flags); +} + + +/* + * Use a bogus region of memory (starting at 0) to sanitize the TLB's. + * Use increments of the maximum page size (16MB), and check for duplicate + * entries before doing a given write. Then, when we're safe from collisions + * with the firmware, go back and give all the entries invalid addresses with + * the normal flush routine. + */ +void sb1_sanitize_tlb(void) +{ + int entry; + long addr = 0; + + long inc = 1<<24; /* 16MB */ + /* Save old context and create impossible VPN2 value */ + write_c0_entrylo0(0); + write_c0_entrylo1(0); + for (entry = 0; entry < current_cpu_data.tlbsize; entry++) { + do { + addr += inc; + write_c0_entryhi(addr); + tlb_probe(); + } while ((int)(read_c0_index()) >= 0); + write_c0_index(entry); + tlb_write_indexed(); + } + /* Now that we know we're safe from collisions, we can safely flush + the TLB with the "normal" routine. */ + local_flush_tlb_all(); +} + +void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, + unsigned long end) +{ + struct mm_struct *mm = vma->vm_mm; + unsigned long flags; + int cpu; + + local_irq_save(flags); + cpu = smp_processor_id(); + if (cpu_context(cpu, mm) != 0) { + int size; + size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; + size = (size + 1) >> 1; + if (size <= (current_cpu_data.tlbsize/2)) { + int oldpid = read_c0_entryhi() & ASID_MASK; + int newpid = cpu_asid(cpu, mm); + + start &= (PAGE_MASK << 1); + end += ((PAGE_SIZE << 1) - 1); + end &= (PAGE_MASK << 1); + while (start < end) { + int idx; + + write_c0_entryhi(start | newpid); + start += (PAGE_SIZE << 1); + tlb_probe(); + idx = read_c0_index(); + write_c0_entrylo0(0); + write_c0_entrylo1(0); + write_c0_entryhi(KSEG0 + (idx << (PAGE_SHIFT+1))); + if (idx < 0) + continue; + tlb_write_indexed(); + } + write_c0_entryhi(oldpid); + } else { + drop_mmu_context(mm, cpu); + } + } + local_irq_restore(flags); +} + +void local_flush_tlb_kernel_range(unsigned long start, unsigned long end) +{ + unsigned long flags; + int size; + + size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; + size = (size + 1) >> 1; + + local_irq_save(flags); + if (size <= (current_cpu_data.tlbsize/2)) { + int pid = read_c0_entryhi(); + + start &= (PAGE_MASK << 1); + end += ((PAGE_SIZE << 1) - 1); + end &= (PAGE_MASK << 1); + + while (start < end) { + int idx; + + write_c0_entryhi(start); + start += (PAGE_SIZE << 1); + tlb_probe(); + idx = read_c0_index(); + write_c0_entrylo0(0); + write_c0_entrylo1(0); + write_c0_entryhi(KSEG0 + (idx << (PAGE_SHIFT+1))); + if (idx < 0) + continue; + tlb_write_indexed(); + } + write_c0_entryhi(pid); + } else { + local_flush_tlb_all(); + } + local_irq_restore(flags); +} + +void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) +{ + unsigned long flags; + int cpu = smp_processor_id(); + + local_irq_save(flags); + if (cpu_context(cpu, vma->vm_mm) != 0) { + int oldpid, newpid, idx; + newpid = cpu_asid(cpu, vma->vm_mm); + page &= (PAGE_MASK << 1); + oldpid = read_c0_entryhi() & ASID_MASK; + write_c0_entryhi(page | newpid); + tlb_probe(); + idx = read_c0_index(); + write_c0_entrylo0(0); + write_c0_entrylo1(0); + if(idx < 0) + goto finish; + /* Make sure all entries differ. */ + write_c0_entryhi(KSEG0+(idx<<(PAGE_SHIFT+1))); + tlb_write_indexed(); + finish: + write_c0_entryhi(oldpid); + } + local_irq_restore(flags); +} + +/* + * This one is only used for pages with the global bit set so we don't care + * much about the ASID. + */ +void local_flush_tlb_one(unsigned long page) +{ + unsigned long flags; + int oldpid, idx; + + local_irq_save(flags); + page &= (PAGE_MASK << 1); + oldpid = read_c0_entryhi() & ASID_MASK; + write_c0_entryhi(page); + tlb_probe(); + idx = read_c0_index(); + write_c0_entrylo0(0); + write_c0_entrylo1(0); + if (idx >= 0) { + /* Make sure all entries differ. */ + write_c0_entryhi(KSEG0+(idx<<(PAGE_SHIFT+1))); + tlb_write_indexed(); + } + write_c0_entryhi(oldpid); + + local_irq_restore(flags); +} + +/* All entries common to a mm share an asid. To effectively flush + these entries, we just bump the asid. */ +void local_flush_tlb_mm(struct mm_struct *mm) +{ + int cpu = smp_processor_id(); + if (cpu_context(cpu, mm) != 0) { + drop_mmu_context(mm, cpu); + } +} + +/* Stolen from mips32 routines */ + +void __update_tlb(struct vm_area_struct *vma, unsigned long address, pte_t pte) +{ + unsigned long flags; + pgd_t *pgdp; + pmd_t *pmdp; + pte_t *ptep; + int idx, pid; + + /* + * Handle debugger faulting in for debugee. + */ + if (current->active_mm != vma->vm_mm) + return; + + local_irq_save(flags); + + pid = read_c0_entryhi() & ASID_MASK; + address &= (PAGE_MASK << 1); + write_c0_entryhi(address | (pid)); + pgdp = pgd_offset(vma->vm_mm, address); + tlb_probe(); + pmdp = pmd_offset(pgdp, address); + idx = read_c0_index(); + ptep = pte_offset_map(pmdp, address); + write_c0_entrylo0(pte_val(*ptep++) >> 6); + write_c0_entrylo1(pte_val(*ptep) >> 6); + if (idx < 0) { + tlb_write_random(); + } else { + tlb_write_indexed(); + } + local_irq_restore(flags); +} + +/* + * This is called from loadmmu.c. We have to set up all the + * memory management function pointers, as well as initialize + * the caches and tlbs + */ +void sb1_tlb_init(void) +{ + u32 config1; + + write_c0_pagemask(PM_4K); + config1 = read_c0_config1(); + current_cpu_data.tlbsize = ((config1 >> 25) & 0x3f) + 1; + + /* + * We don't know what state the firmware left the TLB's in, so this is + * the ultra-conservative way to flush the TLB's and avoid machine + * check exceptions due to duplicate TLB entries + */ + sb1_sanitize_tlb(); + + memcpy((void *)KSEG0, except_vec0_sb1, 0x80); + flush_icache_range(KSEG0, KSEG0 + 0x80); +} diff --git a/arch/mips/kernel/r2300_misc.S b/arch/mips/mm/tlbex-r3k.S index 8ef3683f983d..bd28805f7926 100644 --- a/arch/mips/kernel/r2300_misc.S +++ b/arch/mips/mm/tlbex-r3k.S @@ -1,5 +1,5 @@ -/* $Id: r2300_misc.S,v 1.8 1999/12/08 22:05:10 harald Exp $ - * misc.S: Misc. exception handling code for R3000/R2000. +/* + * TLB exception handling code for R2000/R3000. * * Copyright (C) 1994, 1995, 1996 by Ralf Baechle and Andreas Busse * @@ -9,25 +9,51 @@ * Further modifications to make this work: * Copyright (c) 1998 Harald Koerfgen * Copyright (c) 1998, 1999 Gleb Raiko & Vladimir Roganov + * Copyright (c) 2001 Ralf Baechle + * Copyright (c) 2001 MIPS Technologies, Inc. */ +#include <linux/init.h> #include <asm/asm.h> -#include <asm/current.h> -#include <asm/bootinfo.h> #include <asm/cachectl.h> #include <asm/fpregdef.h> #include <asm/mipsregs.h> #include <asm/page.h> -#include <asm/pgtable.h> +#include <asm/pgtable-bits.h> #include <asm/processor.h> #include <asm/regdef.h> -#include <asm/segment.h> #include <asm/stackframe.h> +#define TLB_OPTIMIZE /* If you are paranoid, disable this. */ + .text .set mips1 .set noreorder -#undef NOTLB_OPTIMIZE /* If you are paranoid, define this. */ + __INIT + + /* TLB refill, R[23]00 version */ + LEAF(except_vec0_r2300) + .set noat + .set mips1 + mfc0 k0, CP0_BADVADDR + lw k1, pgd_current # get pgd pointer + srl k0, k0, 22 + sll k0, k0, 2 + addu k1, k1, k0 + mfc0 k0, CP0_CONTEXT + lw k1, (k1) + and k0, k0, 0xffc + addu k1, k1, k0 + lw k0, (k1) + nop + mtc0 k0, CP0_ENTRYLO0 + mfc0 k1, CP0_EPC + tlbwr + jr k1 + rfe + END(except_vec0_r2300) + + __FINIT /* ABUSE of CPP macros 101. */ @@ -37,7 +63,7 @@ */ #define LOAD_PTE(pte, ptr) \ mfc0 pte, CP0_BADVADDR; \ - lw ptr, current_pgd; \ + lw ptr, pgd_current; \ srl pte, pte, 22; \ sll pte, pte, 2; \ addu ptr, ptr, pte; \ @@ -63,12 +89,12 @@ .set macro; \ SAVE_ALL; \ mfc0 a2, CP0_BADVADDR; \ - STI; \ + KMODE; \ .set at; \ move a0, sp; \ jal do_page_fault; \ li a1, write; \ - j ret_from_sys_call; \ + j ret_from_exception; \ nop; \ .set noat; \ .set nomacro; @@ -76,7 +102,7 @@ /* Check is PTE is present, if not then jump to LABEL. * PTR points to the page table where this PTE is located, * when the macro is done executing PTE will be restored - * with its original value. + * with it's original value. */ #define PTE_PRESENT(pte, ptr, label) \ andi pte, pte, (_PAGE_PRESENT | _PAGE_READ); \ @@ -85,7 +111,7 @@ .set push; \ .set reorder; \ lw pte, (ptr); \ - .set pop; + .set pop; /* Make PTE valid, store result in PTR. */ #define PTE_MAKEVALID(pte, ptr) \ @@ -103,7 +129,7 @@ .set reorder; \ lw pte, (ptr); \ .set pop; - + /* Make PTE writable, update software status bits as well, * then store at PTR. @@ -133,14 +159,14 @@ nop; \ jr reg; \ rfe - + .set noreorder .align 5 NESTED(handle_tlbl, PT_SIZE, sp) .set noat -#ifndef NOTLB_OPTIMIZE +#ifdef TLB_OPTIMIZE /* Test present bit in entry. */ LOAD_PTE(k0, k1) tlbp @@ -158,7 +184,7 @@ END(handle_tlbl) NESTED(handle_tlbs, PT_SIZE, sp) .set noat -#ifndef NOTLB_OPTIMIZE +#ifdef TLB_OPTIMIZE LOAD_PTE(k0, k1) tlbp # find faulting entry PTE_WRITABLE(k0, k1, nopage_tlbs) @@ -175,7 +201,7 @@ END(handle_tlbs) .align 5 NESTED(handle_mod, PT_SIZE, sp) .set noat -#ifndef NOTLB_OPTIMIZE +#ifdef TLB_OPTIMIZE LOAD_PTE(k0, k1) tlbp # find faulting entry andi k0, k0, _PAGE_WRITE diff --git a/arch/mips/mm/tlbex-r4k.S b/arch/mips/mm/tlbex-r4k.S new file mode 100644 index 000000000000..ad188a6cda9c --- /dev/null +++ b/arch/mips/mm/tlbex-r4k.S @@ -0,0 +1,532 @@ +/* + * TLB exception handling code for r4k. + * + * Copyright (C) 1994, 1995, 1996 by Ralf Baechle and Andreas Busse + * + * Multi-cpu abstraction and reworking: + * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * + * Carsten Langgaard, carstenl@mips.com + * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved. + */ +#include <linux/init.h> +#include <linux/config.h> + +#include <asm/asm.h> +#include <asm/offset.h> +#include <asm/cachectl.h> +#include <asm/fpregdef.h> +#include <asm/mipsregs.h> +#include <asm/page.h> +#include <asm/pgtable-bits.h> +#include <asm/processor.h> +#include <asm/regdef.h> +#include <asm/stackframe.h> +#include <asm/war.h> + +#define TLB_OPTIMIZE /* If you are paranoid, disable this. */ + +#ifdef CONFIG_64BIT_PHYS_ADDR +#define PTE_L ld +#define PTE_S sd +#define PTE_SRL dsrl +#define P_MTC0 dmtc0 +#define PTE_SIZE 8 +#define PTEP_INDX_MSK 0xff0 +#define PTE_INDX_MSK 0xff8 +#define PTE_INDX_SHIFT 9 +#else +#define PTE_L lw +#define PTE_S sw +#define PTE_SRL srl +#define P_MTC0 mtc0 +#define PTE_SIZE 4 +#define PTEP_INDX_MSK 0xff8 +#define PTE_INDX_MSK 0xffc +#define PTE_INDX_SHIFT 10 +#endif + +/* + * ABUSE of CPP macros 101. + * + * After this macro runs, the pte faulted on is + * in register PTE, a ptr into the table in which + * the pte belongs is in PTR. + */ + +#ifdef CONFIG_SMP +#define GET_PGD(scratch, ptr) \ + mfc0 ptr, CP0_CONTEXT; \ + la scratch, pgd_current;\ + srl ptr, 23; \ + sll ptr, 2; \ + addu ptr, scratch, ptr; \ + lw ptr, (ptr); +#else +#define GET_PGD(scratch, ptr) \ + lw ptr, pgd_current; +#endif + +#define LOAD_PTE(pte, ptr) \ + GET_PGD(pte, ptr) \ + mfc0 pte, CP0_BADVADDR; \ + srl pte, pte, _PGDIR_SHIFT; \ + sll pte, pte, 2; \ + addu ptr, ptr, pte; \ + mfc0 pte, CP0_BADVADDR; \ + lw ptr, (ptr); \ + srl pte, pte, PTE_INDX_SHIFT; \ + and pte, pte, PTE_INDX_MSK; \ + addu ptr, ptr, pte; \ + PTE_L pte, (ptr); + + /* This places the even/odd pte pair in the page + * table at PTR into ENTRYLO0 and ENTRYLO1 using + * TMP as a scratch register. + */ +#define PTE_RELOAD(ptr, tmp) \ + ori ptr, ptr, PTE_SIZE; \ + xori ptr, ptr, PTE_SIZE; \ + PTE_L tmp, PTE_SIZE(ptr); \ + PTE_L ptr, 0(ptr); \ + PTE_SRL tmp, tmp, 6; \ + P_MTC0 tmp, CP0_ENTRYLO1; \ + PTE_SRL ptr, ptr, 6; \ + P_MTC0 ptr, CP0_ENTRYLO0; + +#define DO_FAULT(write) \ + .set noat; \ + SAVE_ALL; \ + mfc0 a2, CP0_BADVADDR; \ + KMODE; \ + .set at; \ + move a0, sp; \ + jal do_page_fault; \ + li a1, write; \ + j ret_from_exception; \ + nop; \ + .set noat; + + /* Check is PTE is present, if not then jump to LABEL. + * PTR points to the page table where this PTE is located, + * when the macro is done executing PTE will be restored + * with it's original value. + */ +#define PTE_PRESENT(pte, ptr, label) \ + andi pte, pte, (_PAGE_PRESENT | _PAGE_READ); \ + xori pte, pte, (_PAGE_PRESENT | _PAGE_READ); \ + bnez pte, label; \ + PTE_L pte, (ptr); + + /* Make PTE valid, store result in PTR. */ +#define PTE_MAKEVALID(pte, ptr) \ + ori pte, pte, (_PAGE_VALID | _PAGE_ACCESSED); \ + PTE_S pte, (ptr); + + /* Check if PTE can be written to, if not branch to LABEL. + * Regardless restore PTE with value from PTR when done. + */ +#define PTE_WRITABLE(pte, ptr, label) \ + andi pte, pte, (_PAGE_PRESENT | _PAGE_WRITE); \ + xori pte, pte, (_PAGE_PRESENT | _PAGE_WRITE); \ + bnez pte, label; \ + PTE_L pte, (ptr); + + /* Make PTE writable, update software status bits as well, + * then store at PTR. + */ +#define PTE_MAKEWRITE(pte, ptr) \ + ori pte, pte, (_PAGE_ACCESSED | _PAGE_MODIFIED | \ + _PAGE_VALID | _PAGE_DIRTY); \ + PTE_S pte, (ptr); + + __INIT + +#ifdef CONFIG_64BIT_PHYS_ADDR +#define GET_PTE_OFF(reg) +#elif CONFIG_CPU_VR41XX +#define GET_PTE_OFF(reg) srl reg, reg, 3 +#else +#define GET_PTE_OFF(reg) srl reg, reg, 1 +#endif + +/* + * These handlers much be written in a relocatable manner + * because based upon the cpu type an arbitrary one of the + * following pieces of code will be copied to the KSEG0 + * vector location. + */ + /* TLB refill, EXL == 0, R4xx0, non-R4600 version */ + .set noreorder + .set noat + LEAF(except_vec0_r4000) + .set mips3 + GET_PGD(k0, k1) # get pgd pointer + mfc0 k0, CP0_BADVADDR # Get faulting address + srl k0, k0, _PGDIR_SHIFT # get pgd only bits + + sll k0, k0, 2 + addu k1, k1, k0 # add in pgd offset + mfc0 k0, CP0_CONTEXT # get context reg + lw k1, (k1) + GET_PTE_OFF(k0) # get pte offset + and k0, k0, PTEP_INDX_MSK + addu k1, k1, k0 # add in offset + PTE_L k0, 0(k1) # get even pte + PTE_L k1, PTE_SIZE(k1) # get odd pte + PTE_SRL k0, k0, 6 # convert to entrylo0 + P_MTC0 k0, CP0_ENTRYLO0 # load it + PTE_SRL k1, k1, 6 # convert to entrylo1 + P_MTC0 k1, CP0_ENTRYLO1 # load it + b 1f + tlbwr # write random tlb entry +1: + nop + eret # return from trap + END(except_vec0_r4000) + + /* TLB refill, EXL == 0, R4600 version */ + LEAF(except_vec0_r4600) + .set mips3 + GET_PGD(k0, k1) # get pgd pointer + mfc0 k0, CP0_BADVADDR + srl k0, k0, _PGDIR_SHIFT + sll k0, k0, 2 # log2(sizeof(pgd_t) + addu k1, k1, k0 + mfc0 k0, CP0_CONTEXT + lw k1, (k1) + GET_PTE_OFF(k0) # get pte offset + and k0, k0, PTEP_INDX_MSK + addu k1, k1, k0 + PTE_L k0, 0(k1) + PTE_L k1, PTE_SIZE(k1) + PTE_SRL k0, k0, 6 + P_MTC0 k0, CP0_ENTRYLO0 + PTE_SRL k1, k1, 6 + P_MTC0 k1, CP0_ENTRYLO1 + nop + tlbwr + nop + eret + END(except_vec0_r4600) + + /* TLB refill, EXL == 0, R52x0 "Nevada" version */ + /* + * This version has a bug workaround for the Nevada. It seems + * as if under certain circumstances the move from cp0_context + * might produce a bogus result when the mfc0 instruction and + * it's consumer are in a different cacheline or a load instruction, + * probably any memory reference, is between them. This is + * potencially slower than the R4000 version, so we use this + * special version. + */ + .set noreorder + .set noat + LEAF(except_vec0_nevada) + .set mips3 + mfc0 k0, CP0_BADVADDR # Get faulting address + srl k0, k0, _PGDIR_SHIFT # get pgd only bits + lw k1, pgd_current # get pgd pointer + sll k0, k0, 2 # log2(sizeof(pgd_t) + addu k1, k1, k0 # add in pgd offset + lw k1, (k1) + mfc0 k0, CP0_CONTEXT # get context reg + GET_PTE_OFF(k0) # get pte offset + and k0, k0, PTEP_INDX_MSK + addu k1, k1, k0 # add in offset + PTE_L k0, 0(k1) # get even pte + PTE_L k1, PTE_SIZE(k1) # get odd pte + PTE_SRL k0, k0, 6 # convert to entrylo0 + P_MTC0 k0, CP0_ENTRYLO0 # load it + PTE_SRL k1, k1, 6 # convert to entrylo1 + P_MTC0 k1, CP0_ENTRYLO1 # load it + nop # QED specified nops + nop + tlbwr # write random tlb entry + nop # traditional nop + eret # return from trap + END(except_vec0_nevada) + + /* TLB refill, EXL == 0, SB1 with M3 errata handling version */ + LEAF(except_vec0_sb1) +#if BCM1250_M3_WAR + mfc0 k0, CP0_BADVADDR + mfc0 k1, CP0_ENTRYHI + xor k0, k1 + srl k0, k0, PAGE_SHIFT+1 + bnez k0, 1f +#endif + GET_PGD(k0, k1) # get pgd pointer + mfc0 k0, CP0_BADVADDR # Get faulting address + srl k0, k0, _PGDIR_SHIFT # get pgd only bits + sll k0, k0, 2 + addu k1, k1, k0 # add in pgd offset + mfc0 k0, CP0_CONTEXT # get context reg + lw k1, (k1) + GET_PTE_OFF(k0) # get pte offset + and k0, k0, PTEP_INDX_MSK + addu k1, k1, k0 # add in offset + PTE_L k0, 0(k1) # get even pte + PTE_L k1, PTE_SIZE(k1) # get odd pte + PTE_SRL k0, k0, 6 # convert to entrylo0 + P_MTC0 k0, CP0_ENTRYLO0 # load it + PTE_SRL k1, k1, 6 # convert to entrylo1 + P_MTC0 k1, CP0_ENTRYLO1 # load it + tlbwr # write random tlb entry +1: eret # return from trap + END(except_vec0_sb1) + + /* TLB refill, EXL == 0, R4[40]00/R5000 badvaddr hwbug version */ + LEAF(except_vec0_r45k_bvahwbug) + .set mips3 + GET_PGD(k0, k1) # get pgd pointer + mfc0 k0, CP0_BADVADDR + srl k0, k0, _PGDIR_SHIFT + sll k0, k0, 2 # log2(sizeof(pgd_t) + addu k1, k1, k0 + mfc0 k0, CP0_CONTEXT + lw k1, (k1) +#ifndef CONFIG_64BIT_PHYS_ADDR + srl k0, k0, 1 +#endif + and k0, k0, PTEP_INDX_MSK + addu k1, k1, k0 + PTE_L k0, 0(k1) + PTE_L k1, PTE_SIZE(k1) + nop /* XXX */ + tlbp + PTE_SRL k0, k0, 6 + P_MTC0 k0, CP0_ENTRYLO0 + PTE_SRL k1, k1, 6 + mfc0 k0, CP0_INDEX + P_MTC0 k1, CP0_ENTRYLO1 + bltzl k0, 1f + tlbwr +1: + nop + eret + END(except_vec0_r45k_bvahwbug) + +#ifdef CONFIG_SMP + /* TLB refill, EXL == 0, R4000 MP badvaddr hwbug version */ + LEAF(except_vec0_r4k_mphwbug) + .set mips3 + GET_PGD(k0, k1) # get pgd pointer + mfc0 k0, CP0_BADVADDR + srl k0, k0, _PGDIR_SHIFT + sll k0, k0, 2 # log2(sizeof(pgd_t) + addu k1, k1, k0 + mfc0 k0, CP0_CONTEXT + lw k1, (k1) +#ifndef CONFIG_64BIT_PHYS_ADDR + srl k0, k0, 1 +#endif + and k0, k0, PTEP_INDX_MSK + addu k1, k1, k0 + PTE_L k0, 0(k1) + PTE_L k1, PTE_SIZE(k1) + nop /* XXX */ + tlbp + PTE_SRL k0, k0, 6 + P_MTC0 k0, CP0_ENTRYLO0 + PTE_SRL k1, k1, 6 + mfc0 k0, CP0_INDEX + P_MTC0 k1, CP0_ENTRYLO1 + bltzl k0, 1f + tlbwr +1: + nop + eret + END(except_vec0_r4k_mphwbug) +#endif + + /* TLB refill, EXL == 0, R4000 UP 250MHZ entrylo[01] hwbug version */ + LEAF(except_vec0_r4k_250MHZhwbug) + .set mips3 + GET_PGD(k0, k1) # get pgd pointer + mfc0 k0, CP0_BADVADDR + srl k0, k0, _PGDIR_SHIFT + sll k0, k0, 2 # log2(sizeof(pgd_t) + addu k1, k1, k0 + mfc0 k0, CP0_CONTEXT + lw k1, (k1) +#ifndef CONFIG_64BIT_PHYS_ADDR + srl k0, k0, 1 +#endif + and k0, k0, PTEP_INDX_MSK + addu k1, k1, k0 + PTE_L k0, 0(k1) + PTE_L k1, PTE_SIZE(k1) + PTE_SRL k0, k0, 6 + P_MTC0 zero, CP0_ENTRYLO0 + P_MTC0 k0, CP0_ENTRYLO0 + PTE_SRL k1, k1, 6 + P_MTC0 zero, CP0_ENTRYLO1 + P_MTC0 k1, CP0_ENTRYLO1 + b 1f + tlbwr +1: + nop + eret + END(except_vec0_r4k_250MHZhwbug) + +#ifdef CONFIG_SMP + /* TLB refill, EXL == 0, R4000 MP 250MHZ entrylo[01]+badvaddr bug version */ + LEAF(except_vec0_r4k_MP250MHZhwbug) + .set mips3 + GET_PGD(k0, k1) # get pgd pointer + mfc0 k0, CP0_BADVADDR + srl k0, k0, _PGDIR_SHIFT + sll k0, k0, 2 # log2(sizeof(pgd_t) + addu k1, k1, k0 + mfc0 k0, CP0_CONTEXT + lw k1, (k1) +#ifndef CONFIG_64BIT_PHYS_ADDR + srl k0, k0, 1 +#endif + and k0, k0, PTEP_INDX_MSK + addu k1, k1, k0 + PTE_L k0, 0(k1) + PTE_L k1, PTE_SIZE(k1) + nop /* XXX */ + tlbp + PTE_SRL k0, k0, 6 + P_MTC0 zero, CP0_ENTRYLO0 + P_MTC0 k0, CP0_ENTRYLO0 + mfc0 k0, CP0_INDEX + PTE_SRL k1, k1, 6 + P_MTC0 zero, CP0_ENTRYLO1 + P_MTC0 k1, CP0_ENTRYLO1 + bltzl k0, 1f + tlbwr +1: + nop + eret + END(except_vec0_r4k_MP250MHZhwbug) +#endif + + __FINIT + + .set noreorder + +/* + * From the IDT errata for the QED RM5230 (Nevada), processor revision 1.0: + * 2. A timing hazard exists for the TLBP instruction. + * + * stalling_instruction + * TLBP + * + * The JTLB is being read for the TLBP throughout the stall generated by the + * previous instruction. This is not really correct as the stalling instruction + * can modify the address used to access the JTLB. The failure symptom is that + * the TLBP instruction will use an address created for the stalling instruction + * and not the address held in C0_ENHI and thus report the wrong results. + * + * The software work-around is to not allow the instruction preceding the TLBP + * to stall - make it an NOP or some other instruction guaranteed not to stall. + * + * Errata 2 will not be fixed. This errata is also on the R5000. + * + * As if we MIPS hackers wouldn't know how to nop pipelines happy ... + */ +#define R5K_HAZARD nop + + /* + * Note for many R4k variants tlb probes cannot be executed out + * of the instruction cache else you get bogus results. + */ + .align 5 + NESTED(handle_tlbl, PT_SIZE, sp) + .set noat +#if BCM1250_M3_WAR + mfc0 k0, CP0_BADVADDR + mfc0 k1, CP0_ENTRYHI + xor k0, k1 + srl k0, k0, PAGE_SHIFT+1 + beqz k0, 1f + nop + .set mips3 + eret + .set mips0 +1: +#endif +invalid_tlbl: +#ifdef TLB_OPTIMIZE + /* Test present bit in entry. */ + LOAD_PTE(k0, k1) + R5K_HAZARD + tlbp + PTE_PRESENT(k0, k1, nopage_tlbl) + PTE_MAKEVALID(k0, k1) + PTE_RELOAD(k1, k0) + nop + b 1f + tlbwi +1: + nop + .set mips3 + eret + .set mips0 +#endif + +nopage_tlbl: + DO_FAULT(0) + END(handle_tlbl) + + .align 5 + NESTED(handle_tlbs, PT_SIZE, sp) + .set noat +#ifdef TLB_OPTIMIZE + .set mips3 + li k0,0 + LOAD_PTE(k0, k1) + R5K_HAZARD + tlbp # find faulting entry + PTE_WRITABLE(k0, k1, nopage_tlbs) + PTE_MAKEWRITE(k0, k1) + PTE_RELOAD(k1, k0) + nop + b 1f + tlbwi +1: + nop + .set mips3 + eret + .set mips0 +#endif + +nopage_tlbs: + DO_FAULT(1) + END(handle_tlbs) + + .align 5 + NESTED(handle_mod, PT_SIZE, sp) + .set noat +#ifdef TLB_OPTIMIZE + .set mips3 + LOAD_PTE(k0, k1) + R5K_HAZARD + tlbp # find faulting entry + andi k0, k0, _PAGE_WRITE + beqz k0, nowrite_mod + PTE_L k0, (k1) + + /* Present and writable bits set, set accessed and dirty bits. */ + PTE_MAKEWRITE(k0, k1) + + /* Now reload the entry into the tlb. */ + PTE_RELOAD(k1, k0) + nop + b 1f + tlbwi +1: + nop + .set mips3 + eret + .set mips0 +#endif + +nowrite_mod: + DO_FAULT(1) + END(handle_mod) + diff --git a/arch/mips/mm/umap.c b/arch/mips/mm/umap.c deleted file mode 100644 index e053fb5ef8ee..000000000000 --- a/arch/mips/mm/umap.c +++ /dev/null @@ -1,222 +0,0 @@ -/* - * (C) Copyright 1994 Linus Torvalds - * - * Changes: - * - * Modified from Linus source to removing active mappings from any - * task. This is required for implementing the virtual graphics - * interface for direct rendering on the SGI - miguel. - * - * Added a routine to map a vmalloc()ed area into user space, this one - * is required by the /dev/shmiq driver - miguel. - */ -#include <linux/stat.h> -#include <linux/sched.h> -#include <linux/kernel.h> -#include <linux/mm.h> -#include <linux/smp.h> -#include <linux/smp_lock.h> -#include <linux/shm.h> -#include <linux/errno.h> -#include <linux/mman.h> -#include <linux/module.h> -#include <linux/string.h> -#include <linux/vmalloc.h> -#include <linux/swap.h> - -#include <asm/system.h> -#include <asm/pgalloc.h> -#include <asm/page.h> - -static inline void -remove_mapping_pte_range (pmd_t *pmd, unsigned long address, unsigned long size) -{ - pte_t *pte; - unsigned long end; - - if (pmd_none (*pmd)) - return; - if (pmd_bad (*pmd)){ - printk ("remove_graphics_pte_range: bad pmd (%08lx)\n", pmd_val (*pmd)); - pmd_clear (pmd); - return; - } - pte = pte_offset (pmd, address); - address &= ~PMD_MASK; - end = address + size; - if (end > PMD_SIZE) - end = PMD_SIZE; - do { - pte_t entry = *pte; - if (pte_present (entry)) - set_pte (pte, pte_modify (entry, PAGE_NONE)); - address += PAGE_SIZE; - pte++; - } while (address < end); - -} - -static inline void -remove_mapping_pmd_range (pgd_t *pgd, unsigned long address, unsigned long size) -{ - pmd_t *pmd; - unsigned long end; - - if (pgd_none (*pgd)) - return; - - if (pgd_bad (*pgd)){ - printk ("remove_graphics_pmd_range: bad pgd (%08lx)\n", pgd_val (*pgd)); - pgd_clear (pgd); - return; - } - pmd = pmd_offset (pgd, address); - address &= ~PGDIR_MASK; - end = address + size; - if (end > PGDIR_SIZE) - end = PGDIR_SIZE; - do { - remove_mapping_pte_range (pmd, address, end - address); - address = (address + PMD_SIZE) & PMD_MASK; - pmd++; - } while (address < end); - -} - -/* - * This routine is called from the page fault handler to remove a - * range of active mappings at this point - */ -void -remove_mapping (struct vm_area_struct *vma, struct task_struct *task, unsigned long start, unsigned long end) -{ - unsigned long beg = start; - pgd_t *dir; - - down_write (&task->mm->mmap_sem); - dir = pgd_offset (task->mm, start); - flush_cache_range (vma, beg, end); - while (start < end){ - remove_mapping_pmd_range (dir, start, end - start); - start = (start + PGDIR_SIZE) & PGDIR_MASK; - dir++; - } - flush_tlb_range (vma, beg, end); - up_write (&task->mm->mmap_sem); -} - -EXPORT_SYMBOL(remove_mapping); - -void *vmalloc_uncached (unsigned long size) -{ - return __vmalloc (size, GFP_KERNEL | __GFP_HIGHMEM, - PAGE_KERNEL_UNCACHED); -} - -static inline void free_pte(pte_t page) -{ - if (pte_present(page)) { - unsigned long pfn = pte_pfn(page); - struct page *ptpage; - if (!pfn_valid(pfn)) - return; - ptpage = pfn_to_page(pfn); - if (PageReserved(ptpage)) - return; - __free_page(ptpage); - if (current->mm->rss <= 0) - return; - current->mm->rss--; - return; - } - swap_free(pte_to_swp_entry(page)); -} - -static inline void forget_pte(pte_t page) -{ - if (!pte_none(page)) { - printk("forget_pte: old mapping existed!\n"); - free_pte(page); - } -} - -/* - * maps a range of vmalloc()ed memory into the requested pages. the old - * mappings are removed. - */ -static inline void -vmap_pte_range (pte_t *pte, unsigned long address, unsigned long size, unsigned long vaddr) -{ - unsigned long end; - pgd_t *vdir; - pmd_t *vpmd; - pte_t *vpte; - - address &= ~PMD_MASK; - end = address + size; - if (end > PMD_SIZE) - end = PMD_SIZE; - do { - pte_t oldpage = *pte; - 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(page, PAGE_USERIO)); - forget_pte(oldpage); - address += PAGE_SIZE; - vaddr += PAGE_SIZE; - pte++; - } while (address < end); -} - -static inline int -vmap_pmd_range (pmd_t *pmd, unsigned long address, unsigned long size, unsigned long vaddr) -{ - unsigned long end; - - address &= ~PGDIR_MASK; - end = address + size; - if (end > PGDIR_SIZE) - end = PGDIR_SIZE; - vaddr -= address; - do { - pte_t * pte = pte_alloc(current->mm, pmd, address); - if (!pte) - return -ENOMEM; - vmap_pte_range(pte, address, end - address, address + vaddr); - address = (address + PMD_SIZE) & PMD_MASK; - pmd++; - } while (address < end); - return 0; -} - -int -vmap_page_range (struct vm_area_struct *vma, unsigned long from, unsigned long size, unsigned long vaddr) -{ - int error = 0; - pgd_t * dir; - unsigned long beg = from; - unsigned long end = from + size; - - vaddr -= from; - dir = pgd_offset(current->mm, from); - flush_cache_range(vma, beg, end); - while (from < end) { - pmd_t *pmd = pmd_alloc(current->mm, dir, from); - error = -ENOMEM; - if (!pmd) - break; - error = vmap_pmd_range(pmd, from, end - from, vaddr + from); - if (error) - break; - from = (from + PGDIR_SIZE) & PGDIR_MASK; - dir++; - } - flush_tlb_range(vma, beg, end); - return error; -} diff --git a/arch/mips/ramdisk/Makefile b/arch/mips/ramdisk/Makefile new file mode 100644 index 000000000000..f5040bfb8eb3 --- /dev/null +++ b/arch/mips/ramdisk/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for a ramdisk image +# + +O_FORMAT = $(shell $(OBJDUMP) -i | head -2 | grep elf32) +img = $(CONFIG_EMBEDDED_RAMDISK_IMAGE) +ramdisk.o: $(subst ",,$(img)) ld.script + echo "O_FORMAT: " $(O_FORMAT) + $(LD) -T ld.script -b binary --oformat $(O_FORMAT) -o $@ $(img) diff --git a/arch/mips/ramdisk/ld.script b/arch/mips/ramdisk/ld.script new file mode 100644 index 000000000000..5172daa021e3 --- /dev/null +++ b/arch/mips/ramdisk/ld.script @@ -0,0 +1,9 @@ +OUTPUT_ARCH(mips) +SECTIONS +{ + .initrd : + { + *(.data) + } +} + diff --git a/arch/mips/tools/Makefile b/arch/mips/tools/Makefile deleted file mode 100644 index cce3918bf825..000000000000 --- a/arch/mips/tools/Makefile +++ /dev/null @@ -1,23 +0,0 @@ -# Makefile for MIPS kernel build tools. -# -# Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) -# Copyright (C) 1997 Ralf Baechle (ralf@gnu.ai.mit.edu) -# -# $Id: Makefile,v 1.2 1997/09/23 06:23:49 ralf Exp $ -# -TARGET := $(TOPDIR)/include/asm-$(ARCH)/offset.h - -$(TARGET): offset.h - cmp -s $^ $@ || (cp $^ $(TARGET).new && mv $(TARGET).new $(TARGET)) - -offset.h: offset.s - sed -n '/^@@@/s///p' $^ >$@ - -offset.s: offset.c $(TOPDIR)/include/linux/autoconf.h - -clean: - rm -f offset.[hs] $(TARGET).new - -mrproper: - rm -f offset.[hs] $(TARGET).new - rm -f $(TARGET) diff --git a/arch/mips/vmlinux.lds.S b/arch/mips/vmlinux.lds.S index 328e87a5face..7fef4b274fee 100644 --- a/arch/mips/vmlinux.lds.S +++ b/arch/mips/vmlinux.lds.S @@ -2,19 +2,20 @@ OUTPUT_ARCH(mips) ENTRY(kernel_entry) +jiffies = JIFFIES32; SECTIONS { - /* Read-only sections, merged into text segment: */ . = LOADADDR; - .init : { *(.init) } =0 - .text : - { - _ftext = . ; + /* read-only */ + _text = .; /* Text and read-only data */ + .text : { *(.text) - /* .gnu.warning sections are handled specially by elf32.em. */ + *(.fixup) *(.gnu.warning) } =0 + _etext = .; /* End of text section */ + . = ALIGN(16); /* Exception table */ __start___ex_table = .; __ex_table : { *(__ex_table) } @@ -25,29 +26,73 @@ SECTIONS __stop___dbe_table = .; RODATA - - _etext = .; - . = ALIGN(8192); - .data.init_task : { *(.data.init_task) } + . = ALIGN(64); + + /* writeable */ + .data : { /* Data */ + *(.data) + + /* Align the initial ramdisk image (INITRD) on page boundaries. */ + . = ALIGN(4096); + __rd_start = .; + *(.initrd) + . = ALIGN(4096); + __rd_end = .; + + CONSTRUCTORS + } + _gp = . + 0x8000; + .lit8 : { *(.lit8) } + .lit4 : { *(.lit4) } + /* We want the small data sections together, so single-instruction offsets + can access them all, and initialized data all before uninitialized, so + we can shorten the on-disk segment size. */ + .sdata : { *(.sdata) } + + . = ALIGN(4096); + __nosave_begin = .; + .data_nosave : { *(.data.nosave) } + . = ALIGN(4096); + __nosave_end = .; - /* Startup code */ . = ALIGN(4096); + .data.page_aligned : { *(.data.idt) } + + . = ALIGN(32); + .data.cacheline_aligned : { *(.data.cacheline_aligned) } + + _edata = .; /* End of data section */ + + . = ALIGN(8192); /* init_task */ + .data.init_task : { *(.data.init_task) } + + /* will be freed after init */ + . = ALIGN(4096); /* Init code and data */ __init_begin = .; - .text.init : { *(.text.init) } - .data.init : { *(.data.init) } + /* /DISCARD/ doesn't work for .reginfo */ + .reginfo : { *(.reginfo) } + .init.text : { + _sinittext = .; + *(.init.text) + _einittext = .; + } + .init.data : { *(.init.data) } . = ALIGN(16); __setup_start = .; - .setup.init : { *(.setup.init) } + .init.setup : { *(.init.setup) } __setup_end = .; + __start___param = .; + __param : { *(__param) } + __stop___param = .; __initcall_start = .; .initcall.init : { - *(.initcall1.init) - *(.initcall2.init) - *(.initcall3.init) - *(.initcall4.init) - *(.initcall5.init) - *(.initcall6.init) + *(.initcall1.init) + *(.initcall2.init) + *(.initcall3.init) + *(.initcall4.init) + *(.initcall5.init) + *(.initcall6.init) *(.initcall7.init) } __initcall_end = .; @@ -55,80 +100,35 @@ SECTIONS .con_initcall.init : { *(.con_initcall.init) } __con_initcall_end = .; SECURITY_INIT - . = ALIGN(4096); /* Align double page for init_task_union */ - __init_end = .; - . = ALIGN(4096); - .data.page_aligned : { *(.data.idt) } - + __initramfs_start = .; + .init.ramfs : { *(.init.ramfs) } + __initramfs_end = .; . = ALIGN(32); - .data.cacheline_aligned : { *(.data.cacheline_aligned) } - - .fini : { *(.fini) } =0 - .reginfo : { *(.reginfo) } - /* Adjust the address for the data segment. We want to adjust up to - the same address within the page on the next page up. It would - be more correct to do this: - . = .; - The current expression does not correctly handle the case of a - text segment ending precisely at the end of a page; it causes the - data segment to skip a page. The above expression does not have - this problem, but it will currently (2/95) cause BFD to allocate - a single segment, combining both text and data, for this case. - This will prevent the text segment from being shared among - multiple executions of the program; I think that is more - important than losing a page of the virtual address space (note - that no actual memory is lost; the page which is skipped can not - be referenced). */ - . = .; - .data : - { - _fdata = . ; - *(.data) - - /* Align the initial ramdisk image (INITRD) on page boundaries. */ - . = ALIGN(4096); - __rd_start = .; - *(.initrd) - __rd_end = .; - . = ALIGN(4096); + __per_cpu_start = .; + .data.percpu : { *(.data.percpu) } + __per_cpu_end = .; + . = ALIGN(4096); + __init_end = .; + /* freed after init ends here */ - CONSTRUCTORS + __bss_start = .; /* BSS */ + .sbss : { + *(.sbss) + *(.scommon) } - .data1 : { *(.data1) } - _gp = . + 0x8000; - .lit8 : { *(.lit8) } - .lit4 : { *(.lit4) } - .ctors : { *(.ctors) } - .dtors : { *(.dtors) } - .got : { *(.got.plt) *(.got) } - .dynamic : { *(.dynamic) } - /* We want the small data sections together, so single-instruction offsets - can access them all, and initialized data all before uninitialized, so - we can shorten the on-disk segment size. */ - .sdata : { *(.sdata) } - . = ALIGN(4); - _edata = .; - PROVIDE (edata = .); - - __bss_start = .; - _fbss = .; - .sbss : { *(.sbss) *(.scommon) } - .bss : - { - *(.dynbss) - *(.bss) - *(COMMON) - . = ALIGN(4); - _end = . ; - PROVIDE (end = .); + .bss : { + *(.bss) + *(COMMON) } + __bss_stop = .; + + _end = . ; /* Sections to be discarded */ - /DISCARD/ : - { - *(.text.exit) - *(.data.exit) + /DISCARD/ : { + *(.exit.text) + *(.exit.data) *(.exitcall.exit) } diff --git a/include/asm-mips/addrspace.h b/include/asm-mips/addrspace.h index 0d1bf3246db0..797a71c0f1cf 100644 --- a/include/asm-mips/addrspace.h +++ b/include/asm-mips/addrspace.h @@ -4,67 +4,100 @@ * for more details. * * Copyright (C) 1996 by Ralf Baechle - * Copyright (C) 2000 by Maciej W. Rozycki + * Copyright (C) 2000, 2002 Maciej W. Rozycki * - * Defitions for the address spaces of the MIPS CPUs. + * Definitions for the address spaces of the MIPS CPUs. */ -#ifndef __ASM_MIPS_ADDRSPACE_H -#define __ASM_MIPS_ADDRSPACE_H +#ifndef __ASM_ADDRSPACE_H +#define __ASM_ADDRSPACE_H + +/* + * Configure language + */ +#ifdef __ASSEMBLY__ +#define _ATYPE_ +#define _ATYPE32_ +#define _ATYPE64_ +#else +#define _ATYPE_ __PTRDIFF_TYPE__ +#define _ATYPE32_ int +#define _ATYPE64_ long long +#endif + +/* + * 32-bit MIPS address spaces + */ +#ifdef __ASSEMBLY__ +#define _ACAST32_ +#define _ACAST64_ +#else +#define _ACAST32_ (_ATYPE_)(_ATYPE32_) /* widen if necessary */ +#define _ACAST64_ (_ATYPE64_) /* do _not_ narrow */ +#endif /* * Memory segments (32bit kernel mode addresses) */ -#define KUSEG 0x00000000 -#define KSEG0 0x80000000 -#define KSEG1 0xa0000000 -#define KSEG2 0xc0000000 -#define KSEG3 0xe0000000 +#define KUSEG 0x00000000 +#define KSEG0 0x80000000 +#define KSEG1 0xa0000000 +#define KSEG2 0xc0000000 +#define KSEG3 0xe0000000 -#define K0BASE KSEG0 +#define K0BASE KSEG0 /* * Returns the kernel segment base of a given address */ -#ifndef __ASSEMBLY__ -#define KSEGX(a) (((unsigned long)(a)) & 0xe0000000) -#else -#define KSEGX(a) ((a) & 0xe0000000) -#endif +#define KSEGX(a) ((_ACAST32_ (a)) & 0xe0000000) /* * Returns the physical address of a KSEG0/KSEG1 address */ -#ifndef __ASSEMBLY__ -#define PHYSADDR(a) (((unsigned long)(a)) & 0x1fffffff) -#else -#define PHYSADDR(a) ((a) & 0x1fffffff) -#endif +#define CPHYSADDR(a) ((_ACAST32_ (a)) & 0x1fffffff) + +#define PHYSADDR(a) CPHYSADDR(a) /* * Map an address to a certain kernel segment */ -#ifndef __ASSEMBLY__ -#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)) -#else -#define KSEG0ADDR(a) (((a) & 0x1fffffff) | KSEG0) -#define KSEG1ADDR(a) (((a) & 0x1fffffff) | KSEG1) -#define KSEG2ADDR(a) (((a) & 0x1fffffff) | KSEG2) -#define KSEG3ADDR(a) (((a) & 0x1fffffff) | KSEG3) -#endif +#define KSEG0ADDR(a) (CPHYSADDR(a) | KSEG0) +#define KSEG1ADDR(a) (CPHYSADDR(a) | KSEG1) +#define KSEG2ADDR(a) (CPHYSADDR(a) | KSEG2) +#define KSEG3ADDR(a) (CPHYSADDR(a) | KSEG3) /* * Memory segments (64bit kernel mode addresses) */ -#define XKUSEG 0x0000000000000000 -#define XKSSEG 0x4000000000000000 -#define XKPHYS 0x8000000000000000 -#define XKSEG 0xc000000000000000 -#define CKSEG0 0xffffffff80000000 -#define CKSEG1 0xffffffffa0000000 -#define CKSSEG 0xffffffffc0000000 -#define CKSEG3 0xffffffffe0000000 +#define XKUSEG 0x0000000000000000 +#define XKSSEG 0x4000000000000000 +#define XKPHYS 0x8000000000000000 +#define XKSEG 0xc000000000000000 +#define CKSEG0 0xffffffff80000000 +#define CKSEG1 0xffffffffa0000000 +#define CKSSEG 0xffffffffc0000000 +#define CKSEG3 0xffffffffe0000000 + +/* + * Cache modes for XKPHYS address conversion macros + */ +#define K_CALG_COH_EXCL1_NOL2 0 +#define K_CALG_COH_SHRL1_NOL2 1 +#define K_CALG_UNCACHED 2 +#define K_CALG_NONCOHERENT 3 +#define K_CALG_COH_EXCL 4 +#define K_CALG_COH_SHAREABLE 5 +#define K_CALG_NOTUSED 6 +#define K_CALG_UNCACHED_ACCEL 7 + +#define TO_PHYS_MASK 0xfffffffffULL /* 36 bit */ + +/* + * 64-bit address conversions + */ +#define PHYS_TO_XKSEG_UNCACHED(p) PHYS_TO_XKPHYS(K_CALG_UNCACHED,(p)) +#define PHYS_TO_XKSEG_CACHED(p) PHYS_TO_XKPHYS(K_CALG_COH_SHAREABLE,(p)) +#define XKPHYS_TO_PHYS(p) ((p) & TO_PHYS_MASK) +#define PHYS_TO_XKPHYS(cm,a) (0x8000000000000000 | ((cm)<<59) | (a)) -#endif /* __ASM_MIPS_ADDRSPACE_H */ +#endif /* __ASM_ADDRSPACE_H */ diff --git a/include/asm-mips/asm.h b/include/asm-mips/asm.h index 22d773b17e0f..db92a744289e 100644 --- a/include/asm-mips/asm.h +++ b/include/asm-mips/asm.h @@ -1,11 +1,10 @@ /* - * include/asm-mips/asm.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. * * Copyright (C) 1995, 1996, 1997 by Ralf Baechle + * Copyright (C) 2002 Maciej W. Rozycki * * Some useful macros for MIPS assembler code * @@ -16,6 +15,7 @@ #ifndef __ASM_ASM_H #define __ASM_ASM_H +#include <linux/config.h> #include <asm/sgidefs.h> #ifndef CAT @@ -28,16 +28,6 @@ #endif /* - * Macros to handle different pointer/register sizes for 32/64-bit code - * - * 64 bit address space isn't used yet, so we may use the R3000 32 bit - * defines for now. - */ -#define PTR .word -#define PTRSIZE 4 -#define PTRLOG 2 - -/* * PIC specific declarations * Not used for the kernel but here seems to be the right place. */ @@ -106,7 +96,7 @@ symbol = value #define PANIC(msg) \ .set push; \ .set reorder; \ - la a0,8f; \ + PTR_LA a0,8f; \ jal panic; \ 9: b 9b; \ .set pop; \ @@ -118,26 +108,26 @@ symbol = value #define PRINT(string) \ .set push; \ .set reorder; \ - la a0,8f; \ + PTR_LA a0,8f; \ jal printk; \ .set pop; \ TEXT(string) #define TEXT(msg) \ - .data; \ + .pushsection .data; \ 8: .asciiz msg; \ - .previous; + .popsection; /* * Build text tables */ #define TTABLE(string) \ - .text; \ + .pushsection .text; \ .word 1f; \ - .previous; \ - .data; \ -1: .asciz string; \ - .previous + .popsection \ + .pushsection .data; \ +1: .asciiz string; \ + .popsection /* * MIPS IV pref instruction. @@ -146,16 +136,26 @@ symbol = value * MIPS IV implementations are free to treat this as a nop. The R5000 * is one of them. So we should have an option not to use this instruction. */ -#if (_MIPS_ISA == _MIPS_ISA_MIPS4 ) || (_MIPS_ISA == _MIPS_ISA_MIPS5) || \ - (_MIPS_ISA == _MIPS_ISA_MIPS64) +#if CONFIG_CPU_HAS_PREFETCH + #define PREF(hint,addr) \ - pref hint,addr + .set push; \ + .set mips4; \ + pref hint,addr; \ + .set pop + #define PREFX(hint,addr) \ - prefx hint,addr -#else -#define PREF -#define PREFX -#endif + .set push; \ + .set mips4; \ + prefx hint,addr; \ + .set pop + +#else /* !CONFIG_CPU_HAS_PREFETCH */ + +#define PREF(hint,addr) +#define PREFX(hint,addr) + +#endif /* !CONFIG_CPU_HAS_PREFETCH */ /* * MIPS ISA IV/V movn/movz instructions and equivalents for older CPUs. @@ -163,16 +163,16 @@ symbol = value #if (_MIPS_ISA == _MIPS_ISA_MIPS1) #define MOVN(rd,rs,rt) \ .set push; \ - .set noreorder; \ + .set reorder; \ beqz rt,9f; \ move rd,rs; \ .set pop; \ 9: #define MOVZ(rd,rs,rt) \ .set push; \ - .set noreorder; \ + .set reorder; \ bnez rt,9f; \ - move rd,rt; \ + move rd,rs; \ .set pop; \ 9: #endif /* _MIPS_ISA == _MIPS_ISA_MIPS1 */ @@ -181,24 +181,24 @@ symbol = value .set push; \ .set noreorder; \ bnezl rt,9f; \ - move rd,rs; \ + move rd,rs; \ .set pop; \ 9: #define MOVZ(rd,rs,rt) \ .set push; \ .set noreorder; \ beqzl rt,9f; \ - movz rd,rs; \ + move rd,rs; \ .set pop; \ 9: #endif /* (_MIPS_ISA == _MIPS_ISA_MIPS2) || (_MIPS_ISA == _MIPS_ISA_MIPS3) */ #if (_MIPS_ISA == _MIPS_ISA_MIPS4 ) || (_MIPS_ISA == _MIPS_ISA_MIPS5) || \ - (_MIPS_ISA == _MIPS_ISA_MIPS64) + (_MIPS_ISA == _MIPS_ISA_MIPS32) || (_MIPS_ISA == _MIPS_ISA_MIPS64) #define MOVN(rd,rs,rt) \ movn rd,rs,rt #define MOVZ(rd,rs,rt) \ movz rd,rs,rt -#endif /* MIPS IV, MIPS V or MIPS64 */ +#endif /* MIPS IV, MIPS V, MIPS32 or MIPS64 */ /* * Stack alignment @@ -215,6 +215,10 @@ symbol = value #endif /* + * Macros to handle different pointer/register sizes for 32/64-bit code + */ + +/* * Size of a register */ #ifdef __mips64 @@ -229,59 +233,54 @@ symbol = value */ #if (_MIPS_ISA == _MIPS_ISA_MIPS1) || (_MIPS_ISA == _MIPS_ISA_MIPS2) || \ (_MIPS_ISA == _MIPS_ISA_MIPS32) -#define REG_S sw -#define REG_L lw -#define PTR_SUBU subu -#define PTR_ADDU addu +#define REG_S sw +#define REG_L lw +#define REG_SUBU subu +#define REG_ADDU addu #endif #if (_MIPS_ISA == _MIPS_ISA_MIPS3) || (_MIPS_ISA == _MIPS_ISA_MIPS4) || \ (_MIPS_ISA == _MIPS_ISA_MIPS5) || (_MIPS_ISA == _MIPS_ISA_MIPS64) -#define REG_S sd -#define REG_L ld -/* We still live in a 32 bit address space ... */ -#define PTR_SUBU subu -#define PTR_ADDU addu +#define REG_S sd +#define REG_L ld +#define REG_SUBU dsubu +#define REG_ADDU daddu #endif /* * How to add/sub/load/store/shift C int variables. */ #if (_MIPS_SZINT == 32) -#define INT_ADD add -#define INT_ADDI addi +#define INT_ADD add #define INT_ADDU addu +#define INT_ADDI addi #define INT_ADDIU addiu -#define INT_SUB add -#define INT_SUBI subi +#define INT_SUB sub #define INT_SUBU subu -#define INT_SUBIU subu #define INT_L lw #define INT_S sw -#define LONG_SLL sll -#define LONG_SLLV sllv -#define LONG_SRL srl -#define LONG_SRLV srlv -#define LONG_SRA sra -#define LONG_SRAV srav +#define INT_SLL sll +#define INT_SLLV sllv +#define INT_SRL srl +#define INT_SRLV srlv +#define INT_SRA sra +#define INT_SRAV srav #endif #if (_MIPS_SZINT == 64) -#define INT_ADD dadd -#define INT_ADDI daddi +#define INT_ADD dadd #define INT_ADDU daddu +#define INT_ADDI daddi #define INT_ADDIU daddiu -#define INT_SUB dadd -#define INT_SUBI dsubi +#define INT_SUB dsub #define INT_SUBU dsubu -#define INT_SUBIU dsubu #define INT_L ld #define INT_S sd -#define LONG_SLL dsll -#define LONG_SLLV dsllv -#define LONG_SRL dsrl -#define LONG_SRLV dsrlv -#define LONG_SRA dsra -#define LONG_SRAV dsrav +#define INT_SLL dsll +#define INT_SLLV dsllv +#define INT_SRL dsrl +#define INT_SRLV dsrlv +#define INT_SRA dsra +#define INT_SRAV dsrav #endif /* @@ -289,13 +288,11 @@ symbol = value */ #if (_MIPS_SZLONG == 32) #define LONG_ADD add -#define LONG_ADDI addi #define LONG_ADDU addu +#define LONG_ADDI addi #define LONG_ADDIU addiu -#define LONG_SUB add -#define LONG_SUBI subi +#define LONG_SUB sub #define LONG_SUBU subu -#define LONG_SUBIU subu #define LONG_L lw #define LONG_S sw #define LONG_SLL sll @@ -308,13 +305,11 @@ symbol = value #if (_MIPS_SZLONG == 64) #define LONG_ADD dadd -#define LONG_ADDI daddi #define LONG_ADDU daddu +#define LONG_ADDI daddi #define LONG_ADDIU daddiu -#define LONG_SUB dadd -#define LONG_SUBI dsubi +#define LONG_SUB dsub #define LONG_SUBU dsubu -#define LONG_SUBIU dsubu #define LONG_L ld #define LONG_S sd #define LONG_SLL dsll @@ -328,17 +323,16 @@ symbol = value /* * How to add/sub/load/store/shift pointers. */ -#if (_MIPS_SZLONG == 32) -#define PTR_ADD add -#define PTR_ADDI addi +#if (_MIPS_SZPTR == 32) +#define PTR_ADD add #define PTR_ADDU addu +#define PTR_ADDI addi #define PTR_ADDIU addiu -#define PTR_SUB add -#define PTR_SUBI subi +#define PTR_SUB sub #define PTR_SUBU subu -#define PTR_SUBIU subu #define PTR_L lw #define PTR_S sw +#define PTR_LA la #define PTR_SLL sll #define PTR_SLLV sllv #define PTR_SRL srl @@ -347,19 +341,22 @@ symbol = value #define PTR_SRAV srav #define PTR_SCALESHIFT 2 + +#define PTR .word +#define PTRSIZE 4 +#define PTRLOG 2 #endif -#if (_MIPS_SZLONG == 64) -#define PTR_ADD dadd -#define PTR_ADDI daddi +#if (_MIPS_SZPTR == 64) +#define PTR_ADD dadd #define PTR_ADDU daddu +#define PTR_ADDI daddi #define PTR_ADDIU daddiu -#define PTR_SUB dadd -#define PTR_SUBI dsubi +#define PTR_SUB dsub #define PTR_SUBU dsubu -#define PTR_SUBIU dsubu #define PTR_L ld #define PTR_S sd +#define PTR_LA dla #define PTR_SLL dsll #define PTR_SLLV dsllv #define PTR_SRL dsrl @@ -368,6 +365,10 @@ symbol = value #define PTR_SRAV dsrav #define PTR_SCALESHIFT 3 + +#define PTR .dword +#define PTRSIZE 8 +#define PTRLOG 3 #endif /* @@ -375,13 +376,15 @@ symbol = value */ #if (_MIPS_ISA == _MIPS_ISA_MIPS1) || (_MIPS_ISA == _MIPS_ISA_MIPS2) || \ (_MIPS_ISA == _MIPS_ISA_MIPS32) -#define MFC0 mfc0 -#define MTC0 mtc0 +#define MFC0 mfc0 +#define MTC0 mtc0 #endif #if (_MIPS_ISA == _MIPS_ISA_MIPS3) || (_MIPS_ISA == _MIPS_ISA_MIPS4) || \ (_MIPS_ISA == _MIPS_ISA_MIPS5) || (_MIPS_ISA == _MIPS_ISA_MIPS64) -#define MFC0 dmfc0 -#define MTC0 dmtc0 +#define MFC0 dmfc0 +#define MTC0 dmtc0 #endif +#define SSNOP sll zero,zero,1 + #endif /* __ASM_ASM_H */ diff --git a/include/asm-mips/asmmacro.h b/include/asm-mips/asmmacro.h index 7becc9bd1ae2..d4cc46162567 100644 --- a/include/asm-mips/asmmacro.h +++ b/include/asm-mips/asmmacro.h @@ -7,8 +7,22 @@ #ifndef _ASM_ASMMACRO_H #define _ASM_ASMMACRO_H +#include <linux/config.h> #include <asm/offset.h> +#ifdef CONFIG_CPU_SB1 +#define FPU_ENABLE_HAZARD \ + .set push; \ + .set noreorder; \ + .set mips2; \ + SSNOP; \ + bnezl $0, .+4; \ + SSNOP; \ + .set pop +#else +#define FPU_ENABLE_HAZARD +#endif + #define FPU_SAVE_DOUBLE(thread, tmp) \ cfc1 tmp, fcr31; \ sdc1 $f0, (THREAD_FPU + 0x000)(thread); \ @@ -29,41 +43,81 @@ sdc1 $f30, (THREAD_FPU + 0x0f0)(thread); \ sw tmp, (THREAD_FPU + 0x100)(thread) +#if defined (__MIPSEL__) #define FPU_SAVE_SINGLE(thread,tmp) \ cfc1 tmp, fcr31; \ swc1 $f0, (THREAD_FPU + 0x000)(thread); \ - swc1 $f1, (THREAD_FPU + 0x008)(thread); \ + swc1 $f1, (THREAD_FPU + 0x004)(thread); \ swc1 $f2, (THREAD_FPU + 0x010)(thread); \ - swc1 $f3, (THREAD_FPU + 0x018)(thread); \ + swc1 $f3, (THREAD_FPU + 0x014)(thread); \ swc1 $f4, (THREAD_FPU + 0x020)(thread); \ - swc1 $f5, (THREAD_FPU + 0x028)(thread); \ + swc1 $f5, (THREAD_FPU + 0x024)(thread); \ swc1 $f6, (THREAD_FPU + 0x030)(thread); \ - swc1 $f7, (THREAD_FPU + 0x038)(thread); \ + swc1 $f7, (THREAD_FPU + 0x034)(thread); \ swc1 $f8, (THREAD_FPU + 0x040)(thread); \ - swc1 $f9, (THREAD_FPU + 0x048)(thread); \ + swc1 $f9, (THREAD_FPU + 0x044)(thread); \ swc1 $f10, (THREAD_FPU + 0x050)(thread); \ - swc1 $f11, (THREAD_FPU + 0x058)(thread); \ + swc1 $f11, (THREAD_FPU + 0x054)(thread); \ swc1 $f12, (THREAD_FPU + 0x060)(thread); \ - swc1 $f13, (THREAD_FPU + 0x068)(thread); \ + swc1 $f13, (THREAD_FPU + 0x064)(thread); \ swc1 $f14, (THREAD_FPU + 0x070)(thread); \ - swc1 $f15, (THREAD_FPU + 0x078)(thread); \ + swc1 $f15, (THREAD_FPU + 0x074)(thread); \ swc1 $f16, (THREAD_FPU + 0x080)(thread); \ - swc1 $f17, (THREAD_FPU + 0x088)(thread); \ + swc1 $f17, (THREAD_FPU + 0x084)(thread); \ swc1 $f18, (THREAD_FPU + 0x090)(thread); \ - swc1 $f19, (THREAD_FPU + 0x098)(thread); \ + swc1 $f19, (THREAD_FPU + 0x094)(thread); \ swc1 $f20, (THREAD_FPU + 0x0a0)(thread); \ - swc1 $f21, (THREAD_FPU + 0x0a8)(thread); \ + swc1 $f21, (THREAD_FPU + 0x0a4)(thread); \ swc1 $f22, (THREAD_FPU + 0x0b0)(thread); \ - swc1 $f23, (THREAD_FPU + 0x0b8)(thread); \ + swc1 $f23, (THREAD_FPU + 0x0b4)(thread); \ swc1 $f24, (THREAD_FPU + 0x0c0)(thread); \ - swc1 $f25, (THREAD_FPU + 0x0c8)(thread); \ + swc1 $f25, (THREAD_FPU + 0x0c4)(thread); \ swc1 $f26, (THREAD_FPU + 0x0d0)(thread); \ - swc1 $f27, (THREAD_FPU + 0x0d8)(thread); \ + swc1 $f27, (THREAD_FPU + 0x0d4)(thread); \ swc1 $f28, (THREAD_FPU + 0x0e0)(thread); \ - swc1 $f29, (THREAD_FPU + 0x0e8)(thread); \ + swc1 $f29, (THREAD_FPU + 0x0e4)(thread); \ swc1 $f30, (THREAD_FPU + 0x0f0)(thread); \ - swc1 $f31, (THREAD_FPU + 0x0f8)(thread); \ + swc1 $f31, (THREAD_FPU + 0x0f4)(thread); \ + sw tmp, (THREAD_FPU + 0x100)(thread) +#elif defined (__MIPSEB__) +#define FPU_SAVE_SINGLE(thread,tmp) \ + cfc1 tmp, fcr31; \ + swc1 $f0, (THREAD_FPU + 0x004)(thread); \ + swc1 $f1, (THREAD_FPU + 0x000)(thread); \ + swc1 $f2, (THREAD_FPU + 0x014)(thread); \ + swc1 $f3, (THREAD_FPU + 0x010)(thread); \ + swc1 $f4, (THREAD_FPU + 0x024)(thread); \ + swc1 $f5, (THREAD_FPU + 0x020)(thread); \ + swc1 $f6, (THREAD_FPU + 0x034)(thread); \ + swc1 $f7, (THREAD_FPU + 0x030)(thread); \ + swc1 $f8, (THREAD_FPU + 0x044)(thread); \ + swc1 $f9, (THREAD_FPU + 0x040)(thread); \ + swc1 $f10, (THREAD_FPU + 0x054)(thread); \ + swc1 $f11, (THREAD_FPU + 0x050)(thread); \ + swc1 $f12, (THREAD_FPU + 0x064)(thread); \ + swc1 $f13, (THREAD_FPU + 0x060)(thread); \ + swc1 $f14, (THREAD_FPU + 0x074)(thread); \ + swc1 $f15, (THREAD_FPU + 0x070)(thread); \ + swc1 $f16, (THREAD_FPU + 0x084)(thread); \ + swc1 $f17, (THREAD_FPU + 0x080)(thread); \ + swc1 $f18, (THREAD_FPU + 0x094)(thread); \ + swc1 $f19, (THREAD_FPU + 0x090)(thread); \ + swc1 $f20, (THREAD_FPU + 0x0a4)(thread); \ + swc1 $f21, (THREAD_FPU + 0x0a0)(thread); \ + swc1 $f22, (THREAD_FPU + 0x0b4)(thread); \ + swc1 $f23, (THREAD_FPU + 0x0b0)(thread); \ + swc1 $f24, (THREAD_FPU + 0x0c4)(thread); \ + swc1 $f25, (THREAD_FPU + 0x0c0)(thread); \ + swc1 $f26, (THREAD_FPU + 0x0d4)(thread); \ + swc1 $f27, (THREAD_FPU + 0x0d0)(thread); \ + swc1 $f28, (THREAD_FPU + 0x0e4)(thread); \ + swc1 $f29, (THREAD_FPU + 0x0e0)(thread); \ + swc1 $f30, (THREAD_FPU + 0x0f4)(thread); \ + swc1 $f31, (THREAD_FPU + 0x0f0)(thread); \ sw tmp, (THREAD_FPU + 0x100)(thread) +#else +#error "MIPS, but neither __MIPSEB__, nor __MIPSEL__???" +#endif #define FPU_RESTORE_DOUBLE(thread, tmp) \ lw tmp, (THREAD_FPU + 0x100)(thread); \ @@ -85,41 +139,81 @@ ldc1 $f30, (THREAD_FPU + 0x0f0)(thread); \ ctc1 tmp, fcr31 +#if defined (__MIPSEL__) #define FPU_RESTORE_SINGLE(thread,tmp) \ lw tmp, (THREAD_FPU + 0x100)(thread); \ lwc1 $f0, (THREAD_FPU + 0x000)(thread); \ - lwc1 $f1, (THREAD_FPU + 0x008)(thread); \ + lwc1 $f1, (THREAD_FPU + 0x004)(thread); \ lwc1 $f2, (THREAD_FPU + 0x010)(thread); \ - lwc1 $f3, (THREAD_FPU + 0x018)(thread); \ + lwc1 $f3, (THREAD_FPU + 0x014)(thread); \ lwc1 $f4, (THREAD_FPU + 0x020)(thread); \ - lwc1 $f5, (THREAD_FPU + 0x028)(thread); \ + lwc1 $f5, (THREAD_FPU + 0x024)(thread); \ lwc1 $f6, (THREAD_FPU + 0x030)(thread); \ - lwc1 $f7, (THREAD_FPU + 0x038)(thread); \ + lwc1 $f7, (THREAD_FPU + 0x034)(thread); \ lwc1 $f8, (THREAD_FPU + 0x040)(thread); \ - lwc1 $f9, (THREAD_FPU + 0x048)(thread); \ + lwc1 $f9, (THREAD_FPU + 0x044)(thread); \ lwc1 $f10, (THREAD_FPU + 0x050)(thread); \ - lwc1 $f11, (THREAD_FPU + 0x058)(thread); \ + lwc1 $f11, (THREAD_FPU + 0x054)(thread); \ lwc1 $f12, (THREAD_FPU + 0x060)(thread); \ - lwc1 $f13, (THREAD_FPU + 0x068)(thread); \ + lwc1 $f13, (THREAD_FPU + 0x064)(thread); \ lwc1 $f14, (THREAD_FPU + 0x070)(thread); \ - lwc1 $f15, (THREAD_FPU + 0x078)(thread); \ + lwc1 $f15, (THREAD_FPU + 0x074)(thread); \ lwc1 $f16, (THREAD_FPU + 0x080)(thread); \ - lwc1 $f17, (THREAD_FPU + 0x088)(thread); \ + lwc1 $f17, (THREAD_FPU + 0x084)(thread); \ lwc1 $f18, (THREAD_FPU + 0x090)(thread); \ - lwc1 $f19, (THREAD_FPU + 0x098)(thread); \ + lwc1 $f19, (THREAD_FPU + 0x094)(thread); \ lwc1 $f20, (THREAD_FPU + 0x0a0)(thread); \ - lwc1 $f21, (THREAD_FPU + 0x0a8)(thread); \ + lwc1 $f21, (THREAD_FPU + 0x0a4)(thread); \ lwc1 $f22, (THREAD_FPU + 0x0b0)(thread); \ - lwc1 $f23, (THREAD_FPU + 0x0b8)(thread); \ + lwc1 $f23, (THREAD_FPU + 0x0b4)(thread); \ lwc1 $f24, (THREAD_FPU + 0x0c0)(thread); \ - lwc1 $f25, (THREAD_FPU + 0x0c8)(thread); \ + lwc1 $f25, (THREAD_FPU + 0x0c4)(thread); \ lwc1 $f26, (THREAD_FPU + 0x0d0)(thread); \ - lwc1 $f27, (THREAD_FPU + 0x0d8)(thread); \ + lwc1 $f27, (THREAD_FPU + 0x0d4)(thread); \ lwc1 $f28, (THREAD_FPU + 0x0e0)(thread); \ - lwc1 $f29, (THREAD_FPU + 0x0e8)(thread); \ + lwc1 $f29, (THREAD_FPU + 0x0e4)(thread); \ lwc1 $f30, (THREAD_FPU + 0x0f0)(thread); \ - lwc1 $f31, (THREAD_FPU + 0x0f8)(thread); \ + lwc1 $f31, (THREAD_FPU + 0x0f4)(thread); \ + ctc1 tmp, fcr31 +#elif defined (__MIPSEB__) +#define FPU_RESTORE_SINGLE(thread,tmp) \ + lw tmp, (THREAD_FPU + 0x100)(thread); \ + lwc1 $f0, (THREAD_FPU + 0x004)(thread); \ + lwc1 $f1, (THREAD_FPU + 0x000)(thread); \ + lwc1 $f2, (THREAD_FPU + 0x014)(thread); \ + lwc1 $f3, (THREAD_FPU + 0x010)(thread); \ + lwc1 $f4, (THREAD_FPU + 0x024)(thread); \ + lwc1 $f5, (THREAD_FPU + 0x020)(thread); \ + lwc1 $f6, (THREAD_FPU + 0x034)(thread); \ + lwc1 $f7, (THREAD_FPU + 0x030)(thread); \ + lwc1 $f8, (THREAD_FPU + 0x044)(thread); \ + lwc1 $f9, (THREAD_FPU + 0x040)(thread); \ + lwc1 $f10, (THREAD_FPU + 0x054)(thread); \ + lwc1 $f11, (THREAD_FPU + 0x050)(thread); \ + lwc1 $f12, (THREAD_FPU + 0x064)(thread); \ + lwc1 $f13, (THREAD_FPU + 0x060)(thread); \ + lwc1 $f14, (THREAD_FPU + 0x074)(thread); \ + lwc1 $f15, (THREAD_FPU + 0x070)(thread); \ + lwc1 $f16, (THREAD_FPU + 0x084)(thread); \ + lwc1 $f17, (THREAD_FPU + 0x080)(thread); \ + lwc1 $f18, (THREAD_FPU + 0x094)(thread); \ + lwc1 $f19, (THREAD_FPU + 0x090)(thread); \ + lwc1 $f20, (THREAD_FPU + 0x0a4)(thread); \ + lwc1 $f21, (THREAD_FPU + 0x0a0)(thread); \ + lwc1 $f22, (THREAD_FPU + 0x0b4)(thread); \ + lwc1 $f23, (THREAD_FPU + 0x0b0)(thread); \ + lwc1 $f24, (THREAD_FPU + 0x0c4)(thread); \ + lwc1 $f25, (THREAD_FPU + 0x0c0)(thread); \ + lwc1 $f26, (THREAD_FPU + 0x0d4)(thread); \ + lwc1 $f27, (THREAD_FPU + 0x0d0)(thread); \ + lwc1 $f28, (THREAD_FPU + 0x0e4)(thread); \ + lwc1 $f29, (THREAD_FPU + 0x0e0)(thread); \ + lwc1 $f30, (THREAD_FPU + 0x0f4)(thread); \ + lwc1 $f31, (THREAD_FPU + 0x0f0)(thread); \ ctc1 tmp, fcr31 +#else +#error "MIPS, but neither __MIPSEB__, nor __MIPSEL__???" +#endif #define CPU_SAVE_NONSCRATCH(thread) \ sw s0, THREAD_REG16(thread); \ diff --git a/include/asm-mips/atomic.h b/include/asm-mips/atomic.h index bb017f91830a..9c8af4c017c1 100644 --- a/include/asm-mips/atomic.h +++ b/include/asm-mips/atomic.h @@ -57,12 +57,11 @@ typedef struct { volatile int counter; } atomic_t; */ extern __inline__ void atomic_add(int i, atomic_t * v) { - int flags; + unsigned long flags; - save_flags(flags); - cli(); + local_irq_save(flags); v->counter += i; - restore_flags(flags); + local_irq_restore(flags); } /* @@ -75,38 +74,37 @@ extern __inline__ void atomic_add(int i, atomic_t * v) */ extern __inline__ void atomic_sub(int i, atomic_t * v) { - int flags; + unsigned long flags; - save_flags(flags); - cli(); + local_irq_save(flags); v->counter -= i; - restore_flags(flags); + local_irq_restore(flags); } extern __inline__ int atomic_add_return(int i, atomic_t * v) { - int temp, flags; + unsigned long flags; + int temp; - save_flags(flags); - cli(); + local_irq_save(flags); temp = v->counter; temp += i; v->counter = temp; - restore_flags(flags); + local_irq_restore(flags); return temp; } extern __inline__ int atomic_sub_return(int i, atomic_t * v) { - int temp, flags; + unsigned long flags; + int temp; - save_flags(flags); - cli(); + local_irq_save(flags); temp = v->counter; temp -= i; v->counter = temp; - restore_flags(flags); + local_irq_restore(flags); return temp; } @@ -175,6 +173,7 @@ extern __inline__ int atomic_add_return(int i, atomic_t * v) " sc %0, %2 \n" " beqz %0, 1b \n" " addu %0, %1, %3 \n" + " sync \n" ".set pop \n" : "=&r" (result), "=&r" (temp), "=m" (v->counter) : "Ir" (i), "m" (v->counter) @@ -195,6 +194,7 @@ extern __inline__ int atomic_sub_return(int i, atomic_t * v) " sc %0, %2 \n" " beqz %0, 1b \n" " subu %0, %1, %3 \n" + " sync \n" ".set pop \n" : "=&r" (result), "=&r" (temp), "=m" (v->counter) : "Ir" (i), "m" (v->counter) @@ -228,7 +228,7 @@ extern __inline__ int atomic_sub_return(int i, atomic_t * v) * other cases. Note that the guaranteed * useful range of an atomic_t is only 24 bits. */ -#define atomic_inc_and_test(v) (atomic_inc_return(1, (v)) == 0) +#define atomic_inc_and_test(v) (atomic_inc_return(v) == 0) /* * atomic_dec_and_test - decrement by 1 and test @@ -268,15 +268,14 @@ extern __inline__ int atomic_sub_return(int i, atomic_t * v) * if the result is negative, or false when * result is greater than or equal to zero. Note that the guaranteed * useful range of an atomic_t is only 24 bits. - * - * Currently not implemented for MIPS. */ +#define atomic_add_negative(i,v) (atomic_add_return(i, (v)) < 0) /* Atomic operations are already serializing */ -#define smp_mb__before_atomic_dec() barrier() -#define smp_mb__after_atomic_dec() barrier() -#define smp_mb__before_atomic_inc() barrier() -#define smp_mb__after_atomic_inc() barrier() +#define smp_mb__before_atomic_dec() smp_mb() +#define smp_mb__after_atomic_dec() smp_mb() +#define smp_mb__before_atomic_inc() smp_mb() +#define smp_mb__after_atomic_inc() smp_mb() #endif /* defined(__KERNEL__) */ diff --git a/include/asm-mips/bcache.h b/include/asm-mips/bcache.h index e7c8071b003e..446102b34f4e 100644 --- a/include/asm-mips/bcache.h +++ b/include/asm-mips/bcache.h @@ -3,8 +3,8 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (c) 1997, 1999, 2000 by Ralf Baechle - * Copyright (c) 2000 by Silicon Graphics, Inc. + * Copyright (c) 1997, 1999 by Ralf Baechle + * Copyright (c) 1999 Silicon Graphics, Inc. */ #ifndef _ASM_BCACHE_H #define _ASM_BCACHE_H @@ -28,22 +28,22 @@ extern void sni_pcimt_sc_init(void); extern struct bcache_ops *bcops; -extern inline void bc_enable(void) +static inline void bc_enable(void) { bcops->bc_enable(); } -extern inline void bc_disable(void) +static inline void bc_disable(void) { bcops->bc_disable(); } -extern inline void bc_wback_inv(unsigned long page, unsigned long size) +static inline void bc_wback_inv(unsigned long page, unsigned long size) { bcops->bc_wback_inv(page, size); } -extern inline void bc_inv(unsigned long page, unsigned long size) +static inline void bc_inv(unsigned long page, unsigned long size) { bcops->bc_inv(page, size); } diff --git a/include/asm-mips/bitops.h b/include/asm-mips/bitops.h index d76387979544..7c9b6f51a9bb 100644 --- a/include/asm-mips/bitops.h +++ b/include/asm-mips/bitops.h @@ -9,42 +9,49 @@ #ifndef _ASM_BITOPS_H #define _ASM_BITOPS_H +#include <linux/config.h> +#include <linux/compiler.h> #include <linux/types.h> #include <asm/byteorder.h> /* sigh ... */ +#if (_MIPS_SZLONG == 32) +#define SZLONG_LOG 5 +#define SZLONG_MASK 31UL +#elif (_MIPS_SZLONG == 64) +#define SZLONG_LOG 6 +#define SZLONG_MASK 63UL +#endif + #ifdef __KERNEL__ #include <asm/sgidefs.h> #include <asm/system.h> -#include <linux/config.h> /* * clear_bit() doesn't provide any barrier for the compiler. */ -#define smp_mb__before_clear_bit() barrier() -#define smp_mb__after_clear_bit() barrier() +#define smp_mb__before_clear_bit() smp_mb() +#define smp_mb__after_clear_bit() smp_mb() /* * Only disable interrupt for kernel mode stuff to keep usermode stuff * that dares to use kernel include files alive. */ -#define __bi_flags unsigned long flags -#define __bi_cli() local_irq_disable() -#define __bi_save_flags(x) local_save_flags(x) -#define __bi_save_and_cli(x) local_irq_save(x) -#define __bi_restore_flags(x) local_irq_restore(x) +#define __bi_flags unsigned long flags +#define __bi_cli() local_irq_disable() +#define __bi_save_flags(x) local_save_flags(x) +#define __bi_local_irq_save(x) local_irq_save(x) +#define __bi_local_irq_restore(x) local_irq_restore(x) #else #define __bi_flags #define __bi_cli() #define __bi_save_flags(x) -#define __bi_save_and_cli(x) -#define __bi_restore_flags(x) +#define __bi_local_irq_save(x) +#define __bi_local_irq_restore(x) #endif /* __KERNEL__ */ #ifdef CONFIG_CPU_HAS_LLSC -#include <asm/mipsregs.h> - /* * These functions for MIPS ISA > 1 are interrupt and SMP proof and * interrupt friendly @@ -60,8 +67,7 @@ * Note that @nr may be almost arbitrarily large; this function is not * restricted to acting on a single-word quantity. */ -extern __inline__ void -set_bit(int nr, volatile void *addr) +static __inline__ void set_bit(int nr, volatile unsigned long *addr) { unsigned long *m = ((unsigned long *) addr) + (nr >> 5); unsigned long temp; @@ -84,7 +90,7 @@ set_bit(int nr, volatile void *addr) * If it's called on the same region of memory simultaneously, the effect * may be that only one operation succeeds. */ -extern __inline__ void __set_bit(int nr, volatile void * addr) +static __inline__ void __set_bit(int nr, volatile unsigned long * addr) { unsigned long * m = ((unsigned long *) addr) + (nr >> 5); @@ -101,8 +107,7 @@ extern __inline__ void __set_bit(int nr, volatile void * addr) * you should call smp_mb__before_clear_bit() and/or smp_mb__after_clear_bit() * in order to ensure changes are visible on other processors. */ -extern __inline__ void -clear_bit(int nr, volatile void *addr) +static __inline__ void clear_bit(int nr, volatile unsigned long *addr) { unsigned long *m = ((unsigned long *) addr) + (nr >> 5); unsigned long temp; @@ -117,6 +122,22 @@ clear_bit(int nr, volatile void *addr) } /* + * __clear_bit - Clears a bit in memory + * @nr: Bit to clear + * @addr: Address to start counting from + * + * Unlike clear_bit(), this function is non-atomic and may be reordered. + * If it's called on the same region of memory simultaneously, the effect + * may be that only one operation succeeds. + */ +static __inline__ void __clear_bit(int nr, volatile unsigned long * addr) +{ + unsigned long * m = ((unsigned long *) addr) + (nr >> 5); + + *m &= ~(1UL << (nr & 31)); +} + +/* * change_bit - Toggle a bit in memory * @nr: Bit to clear * @addr: Address to start counting from @@ -125,8 +146,7 @@ clear_bit(int nr, volatile void *addr) * Note that @nr may be almost arbitrarily large; this function is not * restricted to acting on a single-word quantity. */ -extern __inline__ void -change_bit(int nr, volatile void *addr) +static __inline__ void change_bit(int nr, volatile unsigned long *addr) { unsigned long *m = ((unsigned long *) addr) + (nr >> 5); unsigned long temp; @@ -142,14 +162,14 @@ change_bit(int nr, volatile void *addr) /* * __change_bit - Toggle a bit in memory - * @nr: the bit to set + * @nr: the bit to change * @addr: the address to start counting from * * Unlike change_bit(), this function is non-atomic and may be reordered. * If it's called on the same region of memory simultaneously, the effect * may be that only one operation succeeds. */ -extern __inline__ void __change_bit(int nr, volatile void * addr) +static __inline__ void __change_bit(int nr, volatile unsigned long * addr) { unsigned long * m = ((unsigned long *) addr) + (nr >> 5); @@ -161,14 +181,14 @@ extern __inline__ void __change_bit(int nr, volatile void * addr) * @nr: Bit to set * @addr: Address to count from * - * This operation is atomic and cannot be reordered. + * This operation is atomic and cannot be reordered. * It also implies a memory barrier. */ -extern __inline__ int -test_and_set_bit(int nr, volatile void *addr) +static __inline__ int test_and_set_bit(int nr, volatile unsigned long *addr) { unsigned long *m = ((unsigned long *) addr) + (nr >> 5); - unsigned long temp, res; + unsigned long temp; + int res; __asm__ __volatile__( ".set\tnoreorder\t\t# test_and_set_bit\n" @@ -177,6 +197,9 @@ test_and_set_bit(int nr, volatile void *addr) "sc\t%2, %1\n\t" "beqz\t%2, 1b\n\t" " and\t%2, %0, %3\n\t" +#ifdef CONFIG_SMP + "sync\n\t" +#endif ".set\treorder" : "=&r" (temp), "=m" (*m), "=&r" (res) : "r" (1UL << (nr & 0x1f)), "m" (*m) @@ -190,14 +213,15 @@ test_and_set_bit(int nr, volatile void *addr) * @nr: Bit to set * @addr: Address to count from * - * This operation is non-atomic and can be reordered. + * This operation is non-atomic and can be reordered. * If two examples of this operation race, one can appear to succeed * but actually fail. You must protect multiple accesses with a lock. */ -extern __inline__ int __test_and_set_bit(int nr, volatile void * addr) +static __inline__ int __test_and_set_bit(int nr, volatile unsigned long * addr) { - int mask, retval; - volatile int *a = addr; + volatile unsigned long *a = addr; + unsigned long mask; + int retval; a += nr >> 5; mask = 1 << (nr & 0x1f); @@ -209,14 +233,13 @@ extern __inline__ int __test_and_set_bit(int nr, volatile void * addr) /* * test_and_clear_bit - Clear a bit and return its old value - * @nr: Bit to set + * @nr: Bit to clear * @addr: Address to count from * - * This operation is atomic and cannot be reordered. + * This operation is atomic and cannot be reordered. * It also implies a memory barrier. */ -extern __inline__ int -test_and_clear_bit(int nr, volatile void *addr) +static __inline__ int test_and_clear_bit(int nr, volatile unsigned long *addr) { unsigned long *m = ((unsigned long *) addr) + (nr >> 5); unsigned long temp, res; @@ -229,6 +252,9 @@ test_and_clear_bit(int nr, volatile void *addr) "sc\t%2, %1\n\t" "beqz\t%2, 1b\n\t" " and\t%2, %0, %3\n\t" +#ifdef CONFIG_SMP + "sync\n\t" +#endif ".set\treorder" : "=&r" (temp), "=m" (*m), "=&r" (res) : "r" (1UL << (nr & 0x1f)), "m" (*m) @@ -239,17 +265,18 @@ test_and_clear_bit(int nr, volatile void *addr) /* * __test_and_clear_bit - Clear a bit and return its old value - * @nr: Bit to set + * @nr: Bit to clear * @addr: Address to count from * - * This operation is non-atomic and can be reordered. + * This operation is non-atomic and can be reordered. * If two examples of this operation race, one can appear to succeed * but actually fail. You must protect multiple accesses with a lock. */ -extern __inline__ int __test_and_clear_bit(int nr, volatile void * addr) +static __inline__ int __test_and_clear_bit(int nr, + volatile unsigned long * addr) { - int mask, retval; - volatile int *a = addr; + volatile unsigned long *a = addr; + unsigned long mask, retval; a += nr >> 5; mask = 1 << (nr & 0x1f); @@ -261,14 +288,13 @@ extern __inline__ int __test_and_clear_bit(int nr, volatile void * addr) /* * test_and_change_bit - Change a bit and return its new value - * @nr: Bit to set + * @nr: Bit to change * @addr: Address to count from * - * This operation is atomic and cannot be reordered. + * This operation is atomic and cannot be reordered. * It also implies a memory barrier. */ -extern __inline__ int -test_and_change_bit(int nr, volatile void *addr) +static __inline__ int test_and_change_bit(int nr, volatile unsigned long *addr) { unsigned long *m = ((unsigned long *) addr) + (nr >> 5); unsigned long temp, res; @@ -280,6 +306,9 @@ test_and_change_bit(int nr, volatile void *addr) "sc\t%2, %1\n\t" "beqz\t%2, 1b\n\t" " and\t%2, %0, %3\n\t" +#ifdef CONFIG_SMP + "sync\n\t" +#endif ".set\treorder" : "=&r" (temp), "=m" (*m), "=&r" (res) : "r" (1UL << (nr & 0x1f)), "m" (*m) @@ -290,17 +319,19 @@ test_and_change_bit(int nr, volatile void *addr) /* * __test_and_change_bit - Change a bit and return its old value - * @nr: Bit to set + * @nr: Bit to change * @addr: Address to count from * - * This operation is non-atomic and can be reordered. + * This operation is non-atomic and can be reordered. * If two examples of this operation race, one can appear to succeed * but actually fail. You must protect multiple accesses with a lock. */ -extern __inline__ int __test_and_change_bit(int nr, volatile void * addr) +static __inline__ int __test_and_change_bit(int nr, + volatile unsigned long *addr) { - int mask, retval; - volatile int *a = addr; + volatile unsigned long *a = addr; + unsigned long mask; + int retval; a += nr >> 5; mask = 1 << (nr & 0x1f); @@ -322,17 +353,17 @@ extern __inline__ int __test_and_change_bit(int nr, volatile void * addr) * Note that @nr may be almost arbitrarily large; this function is not * restricted to acting on a single-word quantity. */ -extern __inline__ void set_bit(int nr, volatile void * addr) +static __inline__ void set_bit(int nr, volatile unsigned long * addr) { - int mask; - volatile int *a = addr; + volatile unsigned long *a = addr; + unsigned long mask; __bi_flags; a += nr >> 5; mask = 1 << (nr & 0x1f); - __bi_save_and_cli(flags); + __bi_local_irq_save(flags); *a |= mask; - __bi_restore_flags(flags); + __bi_local_irq_restore(flags); } /* @@ -344,10 +375,10 @@ extern __inline__ void set_bit(int nr, volatile void * addr) * If it's called on the same region of memory simultaneously, the effect * may be that only one operation succeeds. */ -extern __inline__ void __set_bit(int nr, volatile void * addr) +static __inline__ void __set_bit(int nr, volatile unsigned long * addr) { - int mask; - volatile int *a = addr; + volatile unsigned long *a = addr; + unsigned long mask; a += nr >> 5; mask = 1 << (nr & 0x1f); @@ -364,51 +395,61 @@ extern __inline__ void __set_bit(int nr, volatile void * addr) * you should call smp_mb__before_clear_bit() and/or smp_mb__after_clear_bit() * in order to ensure changes are visible on other processors. */ -extern __inline__ void clear_bit(int nr, volatile void * addr) +static __inline__ void clear_bit(int nr, volatile unsigned long * addr) { - int mask; - volatile int *a = addr; + volatile unsigned long *a = addr; + unsigned long mask; __bi_flags; a += nr >> 5; mask = 1 << (nr & 0x1f); - __bi_save_and_cli(flags); + __bi_local_irq_save(flags); + *a &= ~mask; + __bi_local_irq_restore(flags); +} + +static __inline__ void __clear_bit(int nr, volatile unsigned long * addr) +{ + volatile unsigned long *a = addr; + unsigned long mask; + + a += nr >> 5; + mask = 1 << (nr & 0x1f); *a &= ~mask; - __bi_restore_flags(flags); } /* * change_bit - Toggle a bit in memory - * @nr: Bit to clear + * @nr: Bit to change * @addr: Address to start counting from * * change_bit() is atomic and may not be reordered. * Note that @nr may be almost arbitrarily large; this function is not * restricted to acting on a single-word quantity. */ -extern __inline__ void change_bit(int nr, volatile void * addr) +static __inline__ void change_bit(int nr, volatile unsigned long * addr) { - int mask; - volatile int *a = addr; + volatile unsigned long *a = addr; + unsigned long mask; __bi_flags; a += nr >> 5; mask = 1 << (nr & 0x1f); - __bi_save_and_cli(flags); + __bi_local_irq_save(flags); *a ^= mask; - __bi_restore_flags(flags); + __bi_local_irq_restore(flags); } /* * __change_bit - Toggle a bit in memory - * @nr: the bit to set + * @nr: the bit to change * @addr: the address to start counting from * * Unlike change_bit(), this function is non-atomic and may be reordered. * If it's called on the same region of memory simultaneously, the effect * may be that only one operation succeeds. */ -extern __inline__ void __change_bit(int nr, volatile void * addr) +static __inline__ void __change_bit(int nr, volatile unsigned long * addr) { unsigned long * m = ((unsigned long *) addr) + (nr >> 5); @@ -420,21 +461,22 @@ extern __inline__ void __change_bit(int nr, volatile void * addr) * @nr: Bit to set * @addr: Address to count from * - * This operation is atomic and cannot be reordered. + * This operation is atomic and cannot be reordered. * It also implies a memory barrier. */ -extern __inline__ int test_and_set_bit(int nr, volatile void * addr) +static __inline__ int test_and_set_bit(int nr, volatile unsigned long * addr) { - int mask, retval; - volatile int *a = addr; + volatile unsigned long *a = addr; + unsigned long mask; + int retval; __bi_flags; a += nr >> 5; mask = 1 << (nr & 0x1f); - __bi_save_and_cli(flags); + __bi_local_irq_save(flags); retval = (mask & *a) != 0; *a |= mask; - __bi_restore_flags(flags); + __bi_local_irq_restore(flags); return retval; } @@ -444,14 +486,15 @@ extern __inline__ int test_and_set_bit(int nr, volatile void * addr) * @nr: Bit to set * @addr: Address to count from * - * This operation is non-atomic and can be reordered. + * This operation is non-atomic and can be reordered. * If two examples of this operation race, one can appear to succeed * but actually fail. You must protect multiple accesses with a lock. */ -extern __inline__ int __test_and_set_bit(int nr, volatile void * addr) +static __inline__ int __test_and_set_bit(int nr, volatile unsigned long * addr) { - int mask, retval; - volatile int *a = addr; + volatile unsigned long *a = addr; + unsigned long mask; + int retval; a += nr >> 5; mask = 1 << (nr & 0x1f); @@ -463,41 +506,44 @@ extern __inline__ int __test_and_set_bit(int nr, volatile void * addr) /* * test_and_clear_bit - Clear a bit and return its old value - * @nr: Bit to set + * @nr: Bit to clear * @addr: Address to count from * - * This operation is atomic and cannot be reordered. + * This operation is atomic and cannot be reordered. * It also implies a memory barrier. */ -extern __inline__ int test_and_clear_bit(int nr, volatile void * addr) +static __inline__ int test_and_clear_bit(int nr, volatile unsigned long * addr) { - int mask, retval; - volatile int *a = addr; + volatile unsigned long *a = addr; + unsigned long mask; + int retval; __bi_flags; a += nr >> 5; mask = 1 << (nr & 0x1f); - __bi_save_and_cli(flags); + __bi_local_irq_save(flags); retval = (mask & *a) != 0; *a &= ~mask; - __bi_restore_flags(flags); + __bi_local_irq_restore(flags); return retval; } /* * __test_and_clear_bit - Clear a bit and return its old value - * @nr: Bit to set + * @nr: Bit to clear * @addr: Address to count from * - * This operation is non-atomic and can be reordered. + * This operation is non-atomic and can be reordered. * If two examples of this operation race, one can appear to succeed * but actually fail. You must protect multiple accesses with a lock. */ -extern __inline__ int __test_and_clear_bit(int nr, volatile void * addr) +static __inline__ int __test_and_clear_bit(int nr, + volatile unsigned long * addr) { - int mask, retval; - volatile int *a = addr; + volatile unsigned long *a = addr; + unsigned long mask; + int retval; a += nr >> 5; mask = 1 << (nr & 0x1f); @@ -509,41 +555,43 @@ extern __inline__ int __test_and_clear_bit(int nr, volatile void * addr) /* * test_and_change_bit - Change a bit and return its new value - * @nr: Bit to set + * @nr: Bit to change * @addr: Address to count from * - * This operation is atomic and cannot be reordered. + * This operation is atomic and cannot be reordered. * It also implies a memory barrier. */ -extern __inline__ int test_and_change_bit(int nr, volatile void * addr) +static __inline__ int test_and_change_bit(int nr, volatile unsigned long * addr) { - int mask, retval; - volatile int *a = addr; + volatile unsigned long *a = addr; + unsigned long mask, retval; __bi_flags; a += nr >> 5; mask = 1 << (nr & 0x1f); - __bi_save_and_cli(flags); + __bi_local_irq_save(flags); retval = (mask & *a) != 0; *a ^= mask; - __bi_restore_flags(flags); + __bi_local_irq_restore(flags); return retval; } /* * __test_and_change_bit - Change a bit and return its old value - * @nr: Bit to set + * @nr: Bit to change * @addr: Address to count from * - * This operation is non-atomic and can be reordered. + * This operation is non-atomic and can be reordered. * If two examples of this operation race, one can appear to succeed * but actually fail. You must protect multiple accesses with a lock. */ -extern __inline__ int __test_and_change_bit(int nr, volatile void * addr) +static __inline__ int __test_and_change_bit(int nr, + volatile unsigned long * addr) { - int mask, retval; - volatile int *a = addr; + volatile unsigned long *a = addr; + unsigned long mask; + int retval; a += nr >> 5; mask = 1 << (nr & 0x1f); @@ -556,7 +604,7 @@ extern __inline__ int __test_and_change_bit(int nr, volatile void * addr) #undef __bi_flags #undef __bi_cli #undef __bi_save_flags -#undef __bi_restore_flags +#undef __bi_local_irq_restore #endif /* MIPS I */ @@ -565,202 +613,80 @@ extern __inline__ int __test_and_change_bit(int nr, volatile void * addr) * @nr: bit number to test * @addr: Address to start counting from */ -extern __inline__ int test_bit(int nr, volatile void *addr) +static inline int test_bit(int nr, const volatile unsigned long *addr) { - return ((1UL << (nr & 31)) & (((const unsigned int *) addr)[nr >> 5])) != 0; + return 1UL & (((const volatile unsigned long *) addr)[nr >> SZLONG_LOG] >> (nr & SZLONG_MASK)); } -#ifndef __MIPSEB__ - -/* Little endian versions. */ - /* - * find_first_zero_bit - find the first zero bit in a memory region - * @addr: The address to start the search at - * @size: The maximum size to search + * ffz - find first zero in word. + * @word: The word to search * - * Returns the bit-number of the first zero bit, not the number of the byte - * containing a bit. + * Undefined if no zero exists, so code should check against ~0UL first. */ -extern __inline__ int find_first_zero_bit (void *addr, unsigned size) +static __inline__ unsigned long ffz(unsigned long word) { - unsigned long dummy; - int res; + int b = 0, s; - if (!size) - return 0; - - __asm__ (".set\tnoreorder\n\t" - ".set\tnoat\n" - "1:\tsubu\t$1,%6,%0\n\t" - "blez\t$1,2f\n\t" - "lw\t$1,(%5)\n\t" - "addiu\t%5,4\n\t" -#if (_MIPS_ISA == _MIPS_ISA_MIPS2 ) || (_MIPS_ISA == _MIPS_ISA_MIPS3 ) || \ - (_MIPS_ISA == _MIPS_ISA_MIPS4 ) || (_MIPS_ISA == _MIPS_ISA_MIPS5 ) || \ - (_MIPS_ISA == _MIPS_ISA_MIPS32) || (_MIPS_ISA == _MIPS_ISA_MIPS64) - "beql\t%1,$1,1b\n\t" - "addiu\t%0,32\n\t" -#else - "addiu\t%0,32\n\t" - "beq\t%1,$1,1b\n\t" - "nop\n\t" - "subu\t%0,32\n\t" -#endif -#ifdef __MIPSEB__ -#error "Fix this for big endian" -#endif /* __MIPSEB__ */ - "li\t%1,1\n" - "1:\tand\t%2,$1,%1\n\t" - "beqz\t%2,2f\n\t" - "sll\t%1,%1,1\n\t" - "bnez\t%1,1b\n\t" - "add\t%0,%0,1\n\t" - ".set\tat\n\t" - ".set\treorder\n" - "2:" - : "=r" (res), "=r" (dummy), "=r" (addr) - : "0" ((signed int) 0), "1" ((unsigned int) 0xffffffff), - "2" (addr), "r" (size) - : "$1"); - - return res; -} + word = ~word; + s = 16; if (word << 16 != 0) s = 0; b += s; word >>= s; + s = 8; if (word << 24 != 0) s = 0; b += s; word >>= s; + s = 4; if (word << 28 != 0) s = 0; b += s; word >>= s; + s = 2; if (word << 30 != 0) s = 0; b += s; word >>= s; + s = 1; if (word << 31 != 0) s = 0; b += s; -/* - * find_next_zero_bit - find the first zero bit in a memory region - * @addr: The address to base the search on - * @offset: The bitnumber to start searching at - * @size: The maximum size to search - */ -extern __inline__ int find_next_zero_bit (void * addr, int size, int offset) -{ - unsigned int *p = ((unsigned int *) addr) + (offset >> 5); - int set = 0, bit = offset & 31, res; - unsigned long dummy; - - if (bit) { - /* - * Look for zero in first byte - */ -#ifdef __MIPSEB__ -#error "Fix this for big endian byte order" -#endif - __asm__(".set\tnoreorder\n\t" - ".set\tnoat\n" - "1:\tand\t$1,%4,%1\n\t" - "beqz\t$1,1f\n\t" - "sll\t%1,%1,1\n\t" - "bnez\t%1,1b\n\t" - "addiu\t%0,1\n\t" - ".set\tat\n\t" - ".set\treorder\n" - "1:" - : "=r" (set), "=r" (dummy) - : "0" (0), "1" (1 << bit), "r" (*p) - : "$1"); - if (set < (32 - bit)) - return set + offset; - set = 32 - bit; - p++; - } - /* - * No zero yet, search remaining full bytes for a zero - */ - res = find_first_zero_bit(p, size - 32 * (p - (unsigned int *) addr)); - return offset + set + res; + return b; } -#endif /* !(__MIPSEB__) */ - /* - * ffz - find first zero in word. + * __ffs - find first bit in word. * @word: The word to search * - * Undefined if no zero exists, so code should check against ~0UL first. + * Undefined if no bit exists, so code should check against 0 first. */ -extern __inline__ unsigned long ffz(unsigned long word) +static __inline__ unsigned long __ffs(unsigned long word) { - unsigned int __res; - unsigned int mask = 1; - - __asm__ ( - ".set\tnoreorder\n\t" - ".set\tnoat\n\t" - "move\t%0,$0\n" - "1:\tand\t$1,%2,%1\n\t" - "beqz\t$1,2f\n\t" - "sll\t%1,1\n\t" - "bnez\t%1,1b\n\t" - "addiu\t%0,1\n\t" - ".set\tat\n\t" - ".set\treorder\n" - "2:\n\t" - : "=&r" (__res), "=r" (mask) - : "r" (word), "1" (mask) - : "$1"); - - return __res; + return ffz(~word); } -#ifdef __KERNEL__ - -/** - * ffs - find first bit set - * @x: the word to search - * - * This is defined the same way as - * the libc and compiler builtin ffs routines, therefore - * differs in spirit from the above ffz (man ffs). - */ - -#define ffs(x) generic_ffs(x) - /* - * hweightN - returns the hamming weight of a N-bit word - * @x: the word to weigh - * - * The Hamming Weight of a number is the total number of bits set in it. + * fls: find last bit set. */ -#define hweight32(x) generic_hweight32(x) -#define hweight16(x) generic_hweight16(x) -#define hweight8(x) generic_hweight8(x) - -#endif /* __KERNEL__ */ +#define fls(x) generic_fls(x) -#ifdef __MIPSEB__ /* * find_next_zero_bit - find the first zero bit in a memory region * @addr: The address to base the search on * @offset: The bitnumber to start searching at * @size: The maximum size to search */ -extern __inline__ int find_next_zero_bit(void *addr, int size, int offset) +static inline unsigned long find_next_zero_bit(unsigned long *addr, + unsigned long size, unsigned long offset) { - unsigned long *p = ((unsigned long *) addr) + (offset >> 5); - unsigned long result = offset & ~31UL; + unsigned long *p = ((unsigned long *) addr) + (offset >> SZLONG_LOG); + unsigned long result = offset & ~SZLONG_MASK; unsigned long tmp; if (offset >= size) return size; size -= result; - offset &= 31UL; + offset &= SZLONG_MASK; if (offset) { tmp = *(p++); - tmp |= ~0UL >> (32-offset); - if (size < 32) + tmp |= ~0UL >> (_MIPS_SZLONG-offset); + if (size < _MIPS_SZLONG) goto found_first; if (~tmp) goto found_middle; - size -= 32; - result += 32; + size -= _MIPS_SZLONG; + result += _MIPS_SZLONG; } - while (size & ~31UL) { + while (size & ~SZLONG_MASK) { if (~(tmp = *(p++))) goto found_middle; - result += 32; - size -= 32; + result += _MIPS_SZLONG; + size -= _MIPS_SZLONG; } if (!size) return result; @@ -768,158 +694,241 @@ extern __inline__ int find_next_zero_bit(void *addr, int size, int offset) found_first: tmp |= ~0UL << size; + if (tmp == ~0UL) /* Are any bits zero? */ + return result + size; /* Nope. */ found_middle: return result + ffz(tmp); } -/* Linus sez that gcc can optimize the following correctly, we'll see if this - * holds on the Sparc as it does for the ALPHA. +#define find_first_zero_bit(addr, size) \ + find_next_zero_bit((addr), (size), 0) + +/* + * find_next_bit - find the next set bit in a memory region + * @addr: The address to base the search on + * @offset: The bitnumber to start searching at + * @size: The maximum size to search */ +static inline unsigned long find_next_bit(unsigned long *addr, + unsigned long size, unsigned long offset) +{ + unsigned long *p = addr + (offset >> SZLONG_LOG); + unsigned long result = offset & ~SZLONG_MASK; + unsigned long tmp; + + if (offset >= size) + return size; + size -= result; + offset &= SZLONG_MASK; + if (offset) { + tmp = *(p++); + tmp &= ~0UL << offset; + if (size < _MIPS_SZLONG) + goto found_first; + if (tmp) + goto found_middle; + size -= _MIPS_SZLONG; + result += _MIPS_SZLONG; + } + while (size & ~SZLONG_MASK) { + if ((tmp = *(p++))) + goto found_middle; + result += _MIPS_SZLONG; + size -= _MIPS_SZLONG; + } + if (!size) + return result; + tmp = *p; + +found_first: + tmp &= ~0UL >> (_MIPS_SZLONG - size); + if (tmp == 0UL) /* Are any bits set? */ + return result + size; /* Nope. */ +found_middle: + return result + __ffs(tmp); +} -#if 0 /* Fool kernel-doc since it doesn't do macros yet */ /* - * find_first_zero_bit - find the first zero bit in a memory region + * find_first_bit - find the first set bit in a memory region * @addr: The address to start the search at * @size: The maximum size to search * - * Returns the bit-number of the first zero bit, not the number of the byte + * Returns the bit-number of the first set bit, not the number of the byte * containing a bit. */ -extern int find_first_zero_bit (void *addr, unsigned size); -#endif +#define find_first_bit(addr, size) \ + find_next_bit((addr), (size), 0) -#define find_first_zero_bit(addr, size) \ - find_next_zero_bit((addr), (size), 0) +#ifdef __KERNEL__ + +/* + * Every architecture must define this function. It's the fastest + * way of searching a 168-bit bitmap where the first 128 bits are + * unlikely to be set. It's guaranteed that at least one of the 168 + * bits is cleared. + */ +static inline int sched_find_first_bit(unsigned long *b) +{ + if (unlikely(b[0])) + return __ffs(b[0]); + if (unlikely(b[1])) + return __ffs(b[1]) + 32; + if (unlikely(b[2])) + return __ffs(b[2]) + 64; + if (unlikely(b[3])) + return __ffs(b[3]) + 96; + if (b[4]) + return __ffs(b[4]) + 128; + return __ffs(b[5]) + 32 + 128; +} + +/* + * ffs - find first bit set + * @x: the word to search + * + * This is defined the same way as + * the libc and compiler builtin ffs routines, therefore + * differs in spirit from the above ffz (man ffs). + */ -#endif /* (__MIPSEB__) */ +#define ffs(x) generic_ffs(x) -/* Now for the ext2 filesystem bit operations and helper routines. */ +/* + * hweightN - returns the hamming weight of a N-bit word + * @x: the word to weigh + * + * The Hamming Weight of a number is the total number of bits set in it. + */ + +#define hweight32(x) generic_hweight32(x) +#define hweight16(x) generic_hweight16(x) +#define hweight8(x) generic_hweight8(x) -#ifdef __MIPSEB__ -extern __inline__ int ext2_set_bit(int nr, void * addr) +static inline int __test_and_set_le_bit(unsigned long nr, unsigned long *addr) { - int mask, retval, flags; unsigned char *ADDR = (unsigned char *) addr; + int mask, retval; ADDR += nr >> 3; mask = 1 << (nr & 0x07); - save_and_cli(flags); retval = (mask & *ADDR) != 0; *ADDR |= mask; - restore_flags(flags); + return retval; } -extern __inline__ int ext2_clear_bit(int nr, void * addr) +static inline int __test_and_clear_le_bit(unsigned long nr, unsigned long *addr) { - int mask, retval, flags; unsigned char *ADDR = (unsigned char *) addr; + int mask, retval; ADDR += nr >> 3; mask = 1 << (nr & 0x07); - save_and_cli(flags); retval = (mask & *ADDR) != 0; *ADDR &= ~mask; - restore_flags(flags); + return retval; } -#define ext2_set_bit_atomic(lock, nr, addr) \ - ({ \ - int ret; \ - spin_lock(lock); \ - ret = ext2_set_bit((nr), (addr)); \ - spin_unlock(lock); \ - ret; \ - }) - -#define ext2_clear_bit_atomic(lock, nr, addr) \ - ({ \ - int ret; \ - spin_lock(lock); \ - ret = ext2_clear_bit((nr), (addr)); \ - spin_unlock(lock); \ - ret; \ - }) - -extern __inline__ int ext2_test_bit(int nr, const void * addr) +static inline int test_le_bit(unsigned long nr, const unsigned long * addr) { - int mask; const unsigned char *ADDR = (const unsigned char *) addr; + int mask; ADDR += nr >> 3; mask = 1 << (nr & 0x07); + return ((mask & *ADDR) != 0); } -#define ext2_find_first_zero_bit(addr, size) \ - ext2_find_next_zero_bit((addr), (size), 0) +static inline unsigned long ext2_ffz(unsigned int word) +{ + int b = 0, s; + + word = ~word; + s = 16; if (word << 16 != 0) s = 0; b += s; word >>= s; + s = 8; if (word << 24 != 0) s = 0; b += s; word >>= s; + s = 4; if (word << 28 != 0) s = 0; b += s; word >>= s; + s = 2; if (word << 30 != 0) s = 0; b += s; word >>= s; + s = 1; if (word << 31 != 0) s = 0; b += s; -extern __inline__ unsigned long ext2_find_next_zero_bit(void *addr, unsigned long size, unsigned long offset) + return b; +} + +static inline unsigned long find_next_zero_le_bit(unsigned long *addr, + unsigned long size, unsigned long offset) { - unsigned long *p = ((unsigned long *) addr) + (offset >> 5); - unsigned long result = offset & ~31UL; - unsigned long tmp; + unsigned int *p = ((unsigned int *) addr) + (offset >> 5); + unsigned int result = offset & ~31; + unsigned int tmp; if (offset >= size) return size; + size -= result; - offset &= 31UL; - if(offset) { - /* We hold the little endian value in tmp, but then the - * shift is illegal. So we could keep a big endian value - * in tmp, like this: - * - * tmp = __swab32(*(p++)); - * tmp |= ~0UL >> (32-offset); - * - * but this would decrease preformance, so we change the - * shift: - */ - tmp = *(p++); - tmp |= __swab32(~0UL >> (32-offset)); - if(size < 32) + offset &= 31; + if (offset) { + tmp = cpu_to_le32p(p++); + tmp |= ~0U >> (32-offset); /* bug or feature ? */ + if (size < 32) goto found_first; - if(~tmp) + if (tmp != ~0U) goto found_middle; size -= 32; result += 32; } - while(size & ~31UL) { - if(~(tmp = *(p++))) + while (size >= 32) { + if ((tmp = cpu_to_le32p(p++)) != ~0U) goto found_middle; result += 32; size -= 32; } - if(!size) + if (!size) return result; - tmp = *p; + tmp = cpu_to_le32p(p); found_first: - /* tmp is little endian, so we would have to swab the shift, - * see above. But then we have to swab tmp below for ffz, so - * we might as well do this here. - */ - return result + ffz(__swab32(tmp) | (~0UL << size)); + tmp |= ~0 << size; + if (tmp == ~0U) /* Are any bits zero? */ + return result + size; /* Nope. */ + found_middle: - return result + ffz(__swab32(tmp)); + return result + ext2_ffz(tmp); } -#else /* !(__MIPSEB__) */ -/* Native ext2 byte ordering, just collapse using defines. */ -#define ext2_set_bit(nr, addr) test_and_set_bit((nr), (addr)) -#define ext2_set_bit_atomic(lock, nr, addr) test_and_set_bit((nr), (addr)) -#define ext2_clear_bit(nr, addr) test_and_clear_bit((nr), (addr)) -#define ext2_clear_bit_atomic(lock, nr, addr) test_and_clear_bit((nr), (addr)) -#define ext2_test_bit(nr, addr) test_bit((nr), (addr)) -#define ext2_find_first_zero_bit(addr, size) find_first_zero_bit((addr), (size)) -#define ext2_find_next_zero_bit(addr, size, offset) \ - find_next_zero_bit((addr), (size), (offset)) - -#endif /* !(__MIPSEB__) */ +#define find_first_zero_le_bit(addr, size) \ + find_next_zero_le_bit((addr), (size), 0) + +#define ext2_set_bit(nr,addr) \ + __test_and_set_le_bit((nr),(unsigned long*)addr) +#define ext2_clear_bit(nr, addr) \ + __test_and_clear_le_bit((nr),(unsigned long*)addr) + #define ext2_set_bit_atomic(lock, nr, addr) \ +({ \ + int ret; \ + spin_lock(lock); \ + ret = ext2_set_bit((nr), (addr)); \ + spin_unlock(lock); \ + ret; \ +}) + +#define ext2_clear_bit_atomic(lock, nr, addr) \ +({ \ + int ret; \ + spin_lock(lock); \ + ret = ext2_clear_bit((nr), (addr)); \ + spin_unlock(lock); \ + ret; \ +}) +#define ext2_test_bit(nr, addr) test_le_bit((nr),(unsigned long*)addr) +#define ext2_find_first_zero_bit(addr, size) \ + find_first_zero_le_bit((unsigned long*)addr, size) +#define ext2_find_next_zero_bit(addr, size, off) \ + find_next_zero_le_bit((unsigned long*)addr, size, off) /* * Bitmap functions for the minix filesystem. + * * FIXME: These assume that Minix uses the native byte/bitorder. * This limits the Minix filesystem's value for data exchange very much. */ @@ -929,4 +938,6 @@ found_middle: #define minix_test_bit(nr,addr) test_bit(nr,addr) #define minix_find_first_zero_bit(addr,size) find_first_zero_bit(addr,size) +#endif /* __KERNEL__ */ + #endif /* _ASM_BITOPS_H */ diff --git a/include/asm-mips/bootinfo.h b/include/asm-mips/bootinfo.h index 552ddd93164b..16ebdd7d259c 100644 --- a/include/asm-mips/bootinfo.h +++ b/include/asm-mips/bootinfo.h @@ -9,40 +9,39 @@ #ifndef _ASM_BOOTINFO_H #define _ASM_BOOTINFO_H +#include <linux/types.h> + /* * Values for machgroup */ -#define MACH_GROUP_UNKNOWN 0 /* whatever... */ -#define MACH_GROUP_JAZZ 1 /* Jazz */ -#define MACH_GROUP_DEC 2 /* Digital Equipment */ +#define MACH_GROUP_UNKNOWN 0 /* whatever... */ +#define MACH_GROUP_JAZZ 1 /* Jazz */ +#define MACH_GROUP_DEC 2 /* Digital Equipment */ #define MACH_GROUP_ARC 3 /* Wreckstation Tyne, rPC44, possibly other */ -#define MACH_GROUP_SNI_RM 4 /* Siemens Nixdorf RM series */ +#define MACH_GROUP_SNI_RM 4 /* Siemens Nixdorf RM series */ #define MACH_GROUP_ACN 5 -#define MACH_GROUP_SGI 6 /* Silicon Graphics */ -#define MACH_GROUP_COBALT 7 /* Cobalt servers */ -#define MACH_GROUP_NEC_DDB 8 /* NEC DDB */ -#define MACH_GROUP_BAGET 9 /* Baget */ -#define MACH_GROUP_COSINE 10 /* CoSine Orion */ -#define MACH_GROUP_GALILEO 11 /* Galileo Eval Boards */ -#define MACH_GROUP_MOMENCO 12 /* Momentum Boards */ -#define MACH_GROUP_ITE 13 /* ITE Semi Eval Boards */ +#define MACH_GROUP_SGI 6 /* Silicon Graphics */ +#define MACH_GROUP_COBALT 7 /* Cobalt servers */ +#define MACH_GROUP_NEC_DDB 8 /* NEC DDB */ +#define MACH_GROUP_BAGET 9 /* Baget */ +#define MACH_GROUP_COSINE 10 /* CoSine Orion */ +#define MACH_GROUP_GALILEO 11 /* Galileo Eval Boards */ +#define MACH_GROUP_MOMENCO 12 /* Momentum Boards */ +#define MACH_GROUP_ITE 13 /* ITE Semi Eval Boards */ #define MACH_GROUP_PHILIPS 14 -#define MACH_GROUP_GLOBESPAN 15 /* Globespan PVR Referrence Board */ -#define MACH_GROUP_SIBYTE 16 /* Sibyte Eval Boards */ -#define MACH_GROUP_TOSHIBA 17 /* Toshiba Reference Systems TSBREF */ -#define MACH_GROUP_ALCHEMY 18 /* Alchemy Semi Eval Boards*/ - -#define GROUP_NAMES { "unknown", "Jazz", "Digital", "ARC", "SNI", "ACN", \ - "SGI", "Cobalt", "NEC DDB", "Baget", "Cosine", "Galileo", "Momentum", \ - "ITE", "Philips", "Globepspan", "SiByte", "Toshiba", "Alchemy" } +#define MACH_GROUP_GLOBESPAN 15 /* Globespan PVR Referrence Board */ +#define MACH_GROUP_SIBYTE 16 /* Sibyte Eval Boards */ +#define MACH_GROUP_TOSHIBA 17 /* Toshiba Reference Systems TSBREF */ +#define MACH_GROUP_ALCHEMY 18 /* Alchemy Semi Eval Boards */ +#define MACH_GROUP_NEC_VR41XX 19 /* NEC Vr41xx based boards/gadgets */ +#define MACH_GROUP_HP_LJ 20 /* Hewlett Packard LaserJet */ +#define MACH_GROUP_LASAT 21 /* * Valid machtype values for group unknown (low order halfword of mips_machtype) */ #define MACH_UNKNOWN 0 /* whatever... */ -#define GROUP_UNKNOWN_NAMES { "unknown" } - /* * Valid machtype values for group JAZZ */ @@ -50,26 +49,20 @@ #define MACH_MIPS_MAGNUM_4000 1 /* Mips Magnum 4000 "RC4030" */ #define MACH_OLIVETTI_M700 2 /* Olivetti M700-10 (-15 ??) */ -#define GROUP_JAZZ_NAMES { "Acer PICA 61", "Mips Magnum 4000", "Olivetti M700" } - /* - * Valid machtype for group DEC + * Valid machtype for group DEC */ #define MACH_DSUNKNOWN 0 #define MACH_DS23100 1 /* DECstation 2100 or 3100 */ -#define MACH_DS5100 2 /* DECstation 5100 */ +#define MACH_DS5100 2 /* DECsystem 5100 */ #define MACH_DS5000_200 3 /* DECstation 5000/200 */ #define MACH_DS5000_1XX 4 /* DECstation 5000/120, 125, 133, 150 */ #define MACH_DS5000_XX 5 /* DECstation 5000/20, 25, 33, 50 */ #define MACH_DS5000_2X0 6 /* DECstation 5000/240, 260 */ -#define MACH_DS5400 7 /* DECstation 5400 */ -#define MACH_DS5500 8 /* DECstation 5500 */ -#define MACH_DS5800 9 /* DECstation 5800 */ - -#define GROUP_DEC_NAMES { "unknown", "DECstation 2100/3100", "DECstation 5100", \ - "DECstation 5000/200", "DECstation 5000/1xx", "Personal DECstation 5000/xx", \ - "DECstation 5000/2x0", "DECstation 5400", "DECstation 5500", \ - "DECstation 5800" } +#define MACH_DS5400 7 /* DECsystem 5400 */ +#define MACH_DS5500 8 /* DECsystem 5500 */ +#define MACH_DS5800 9 /* DECsystem 5800 */ +#define MACH_DS5900 10 /* DECsystem 5900 */ /* * Valid machtype for group ARC @@ -77,46 +70,37 @@ #define MACH_DESKSTATION_RPC44 0 /* Deskstation rPC44 */ #define MACH_DESKSTATION_TYNE 1 /* Deskstation Tyne */ -#define GROUP_ARC_NAMES { "Deskstation rPC44", "Deskstation Tyne" } - /* * Valid machtype for group SNI_RM */ #define MACH_SNI_RM200_PCI 0 /* RM200/RM300/RM400 PCI series */ -#define GROUP_SNI_RM_NAMES { "RM200 PCI" } - /* * Valid machtype for group ACN */ #define MACH_ACN_MIPS_BOARD 0 /* ACN MIPS single board */ -#define GROUP_ACN_NAMES { "ACN" } - /* * Valid machtype for group SGI */ -#define MACH_SGI_INDY 0 /* R4?K and R5K Indy workstations */ -#define MACH_SGI_CHALLENGE_S 1 /* The Challenge S server */ -#define MACH_SGI_INDIGO2 2 /* The Indigo2 system */ - -#define GROUP_SGI_NAMES { "Indy", "Challenge S", "Indigo2" } +#define MACH_SGI_IP22 0 /* Indy, Indigo2, Challenge S */ +#define MACH_SGI_IP27 1 /* Origin 200, Origin 2000, Onyx 2 */ +#define MACH_SGI_IP28 2 /* Indigo2 Impact */ +#define MACH_SGI_IP32 3 /* O2 */ /* * Valid machtype for group COBALT */ -#define MACH_COBALT_27 0 /* Proto "27" hardware */ - -#define GROUP_COBALT_NAMES { "Microserver 27" } +#define MACH_COBALT_27 0 /* Proto "27" hardware */ /* * Valid machtype for group NEC DDB */ -#define MACH_NEC_DDB5074 0 /* NEC DDB Vrc-5074 */ -#define MACH_NEC_DDB5476 1 /* NEC DDB Vrc-5476 */ -#define MACH_NEC_DDB5477 2 /* NEC DDB Vrc-5477 */ - -#define GROUP_NEC_DDB_NAMES { "Vrc-5074", "Vrc-5476", "Vrc-5477"} +#define MACH_NEC_DDB5074 0 /* NEC DDB Vrc-5074 */ +#define MACH_NEC_DDB5476 1 /* NEC DDB Vrc-5476 */ +#define MACH_NEC_DDB5477 2 /* NEC DDB Vrc-5477 */ +#define MACH_NEC_ROCKHOPPER 3 /* Rockhopper base board */ +#define MACH_NEC_ROCKHOPPERII 4 /* Rockhopper II base board */ /* * Valid machtype for group BAGET @@ -124,44 +108,33 @@ #define MACH_BAGET201 0 /* BT23-201 */ #define MACH_BAGET202 1 /* BT23-202 */ -#define GROUP_BAGET_NAMES { "BT23-201", "BT23-202" } - /* * Cosine boards. */ #define MACH_COSINE_ORION 0 -#define GROUP_COSINE_NAMES { "Orion" } - /* * Valid machtype for group GALILEO */ #define MACH_EV96100 0 /* EV96100 */ #define MACH_EV64120A 1 /* EV64120A */ -#define GROUP_GALILEO_NAMES { "EV96100" , "EV64120A" } - /* * Valid machtype for group MOMENCO */ #define MACH_MOMENCO_OCELOT 0 +#define MACH_MOMENCO_OCELOT_G 1 +#define MACH_MOMENCO_OCELOT_C 2 -#define GROUP_MOMENCO_NAMES { "Ocelot" } - - /* * Valid machtype for group ITE */ #define MACH_QED_4N_S01B 0 /* ITE8172 based eval board */ - -#define GROUP_ITE_NAMES { "QED-4N-S01B" } /* the actual board name */ - + /* * Valid machtype for group Globespan */ -#define MACH_IVR 0 /* IVR eval board */ - -#define GROUP_GLOBESPAN_NAMES { "IVR" } /* the actual board name */ +#define MACH_IVR 0 /* IVR eval board */ /* * Valid machtype for group PHILIPS @@ -169,126 +142,63 @@ #define MACH_PHILIPS_NINO 0 /* Nino */ #define MACH_PHILIPS_VELO 1 /* Velo */ -#define GROUP_PHILIPS_NAMES { "Nino" , "Velo" } - /* * Valid machtype for group SIBYTE */ #define MACH_SWARM 0 -#define GROUP_SIBYTE_NAMES {"SWARM" } - /* * Valid machtypes for group Toshiba */ #define MACH_PALLAS 0 #define MACH_TOPAS 1 #define MACH_JMR 2 +#define MACH_TOSHIBA_JMR3927 3 /* JMR-TX3927 CPU/IO board */ +#define MACH_TOSHIBA_RBTX4927 4 +#define MACH_TOSHIBA_RBTX4937 5 +#define GROUP_TOSHIBA_NAMES { "Pallas", "TopasCE", "JMR", "JMR TX3927", \ + "RBTX4927", "RBTX4937" } -#define GROUP_TOSHIBA_NAMES { "Pallas", "TopasCE", "JMR" } +/* + * Valid machtype for group LASAT + */ +#define MACH_LASAT_100 0 /* Masquerade II/SP100/SP50/SP25 */ +#define MACH_LASAT_200 1 /* Masquerade PRO/SP200 */ /* * Valid machtype for group Alchemy */ -#define MACH_PB1000 0 /* Au1000-based eval board */ - -#define GROUP_ALCHEMY_NAMES { "PB1000" } /* the actual board name */ +#define MACH_PB1000 0 /* Au1000-based eval board */ +#define MACH_PB1100 1 /* Au1100-based eval board */ +#define MACH_PB1500 2 /* Au1500-based eval board */ +#define MACH_DB1000 3 /* Au1000-based eval board */ +#define MACH_DB1100 4 /* Au1100-based eval board */ +#define MACH_DB1500 5 /* Au1500-based eval board */ /* - * Valid cputype values + * Valid machtype for group NEC_VR41XX */ -#define CPU_UNKNOWN 0 -#define CPU_R2000 1 -#define CPU_R3000 2 -#define CPU_R3000A 3 -#define CPU_R3041 4 -#define CPU_R3051 5 -#define CPU_R3052 6 -#define CPU_R3081 7 -#define CPU_R3081E 8 -#define CPU_R4000PC 9 -#define CPU_R4000SC 10 -#define CPU_R4000MC 11 -#define CPU_R4200 12 -#define CPU_R4400PC 13 -#define CPU_R4400SC 14 -#define CPU_R4400MC 15 -#define CPU_R4600 16 -#define CPU_R6000 17 -#define CPU_R6000A 18 -#define CPU_R8000 19 -#define CPU_R10000 20 -#define CPU_R4300 21 -#define CPU_R4650 22 -#define CPU_R4700 23 -#define CPU_R5000 24 -#define CPU_R5000A 25 -#define CPU_R4640 26 -#define CPU_NEVADA 27 /* RM5230, RM5260 */ -#define CPU_RM7000 28 -#define CPU_R5432 29 -#define CPU_4KC 30 -#define CPU_5KC 31 -#define CPU_R4310 32 -#define CPU_SB1 33 -#define CPU_TX3912 34 -#define CPU_TX3922 35 -#define CPU_TX3927 36 -#define CPU_AU1000 37 -#define CPU_4KEC 38 -#define CPU_4KSC 39 -#define CPU_VR41XX 40 -#define CPU_LAST 40 +#define MACH_NEC_OSPREY 0 /* Osprey eval board */ +#define MACH_NEC_EAGLE 1 /* NEC Eagle/Hawk board */ +#define MACH_ZAO_CAPCELLA 2 /* ZAO Networks Capcella */ +#define MACH_VICTOR_MPC30X 3 /* Victor MP-C303/304 */ +#define MACH_IBM_WORKPAD 4 /* IBM WorkPad z50 */ +#define MACH_CASIO_E55 5 /* CASIO CASSIOPEIA E-10/15/55/65 */ +#define MACH_TANBAC_TB0226 6 /* TANBAC TB0226 (Mbase) */ +#define MACH_TANBAC_TB0229 7 /* TANBAC TB0229 (VR4131DIMM) */ +#define CL_SIZE (256) -#define CPU_NAMES { "unknown", "R2000", "R3000", "R3000A", "R3041", "R3051", \ - "R3052", "R3081", "R3081E", "R4000PC", "R4000SC", "R4000MC", \ - "R4200", "R4400PC", "R4400SC", "R4400MC", "R4600", "R6000", \ - "R6000A", "R8000", "R10000", "R4300", "R4650", "R4700", "R5000", \ - "R5000A", "R4640", "Nevada", "RM7000", "R5432", "MIPS 4Kc", \ - "MIPS 5Kc", "R4310", "SiByte SB1", "TX3912", "TX3922", "TX3927", \ - "Au1000", "MIPS 4KEc", "MIPS 4KSc", "NEC Vr41xx" } +const char *get_system_type(void); -#define COMMAND_LINE_SIZE 256 +extern unsigned long mips_machtype; +extern unsigned long mips_machgroup; #define BOOT_MEM_MAP_MAX 32 #define BOOT_MEM_RAM 1 #define BOOT_MEM_ROM_DATA 2 #define BOOT_MEM_RESERVED 3 -#ifndef __ASSEMBLY__ - -/* - * Some machine parameters passed by the bootloaders. - */ - -struct drive_info_struct { - char dummy[32]; -}; - -/* This is the same as in Milo but renamed for the sake of kernel's */ -/* namespace */ -typedef struct mips_arc_DisplayInfo { /* video adapter information */ - unsigned short cursor_x; - unsigned short cursor_y; - unsigned short columns; - unsigned short lines; -} mips_arc_DisplayInfo; - -/* default values for drive info */ -#define DEFAULT_DRIVE_INFO { {0,}} - -/* - * These are the kernel variables initialized from - * the tag. And they have to be initialized to dummy/default - * values in setup.c (or whereever suitable) so they are in - * .data section - */ -extern struct mips_cpu mips_cpu; -extern unsigned long mips_machtype; -extern unsigned long mips_machgroup; -extern unsigned long mips_tlb_entries; - /* * A memory map that's built upon what was determined * or specified on the command line. @@ -296,17 +206,14 @@ extern unsigned long mips_tlb_entries; struct boot_mem_map { int nr_map; struct { - unsigned long addr; /* start of memory segment */ - unsigned long size; /* size of memory segment */ + phys_t addr; /* start of memory segment */ + phys_t size; /* size of memory segment */ long type; /* type of memory segment */ } map[BOOT_MEM_MAP_MAX]; }; extern struct boot_mem_map boot_mem_map; -extern void add_memory_region(unsigned long start, unsigned long size, - long type); - -#endif /* !__ASSEMBLY__ */ +extern void add_memory_region(phys_t start, phys_t size, long type); #endif /* _ASM_BOOTINFO_H */ diff --git a/include/asm-mips/branch.h b/include/asm-mips/branch.h index d8882cfb1bf9..37c6857c8d4a 100644 --- a/include/asm-mips/branch.h +++ b/include/asm-mips/branch.h @@ -1,24 +1,31 @@ /* - * Branch and jump emulation. - * * 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) 1996, 1997, 1998 by Ralf Baechle - * - * $Id: branch.h,v 1.2 1998/04/28 19:37:46 ralf Exp $ + * Copyright (C) 1996, 1997, 1998, 2001 by Ralf Baechle */ +#ifndef _ASM_BRANCH_H +#define _ASM_BRANCH_H + #include <asm/ptrace.h> -extern inline int delay_slot(struct pt_regs *regs) +static inline int delay_slot(struct pt_regs *regs) { return regs->cp0_cause & CAUSEF_BD; } +static inline unsigned long exception_epc(struct pt_regs *regs) +{ + if (!delay_slot(regs)) + return regs->cp0_epc; + + return regs->cp0_epc + 4; +} + extern int __compute_return_epc(struct pt_regs *regs); -extern inline int compute_return_epc(struct pt_regs *regs) +static inline int compute_return_epc(struct pt_regs *regs) { if (!delay_slot(regs)) { regs->cp0_epc += 4; @@ -27,3 +34,5 @@ extern inline int compute_return_epc(struct pt_regs *regs) return __compute_return_epc(regs); } + +#endif /* _ASM_BRANCH_H */ diff --git a/include/asm-mips/break.h b/include/asm-mips/break.h new file mode 100644 index 000000000000..c8d6ab34966c --- /dev/null +++ b/include/asm-mips/break.h @@ -0,0 +1,33 @@ +/* + * 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) 1995, 2003 by Ralf Baechle + * Copyright (C) 1999 Silicon Graphics, Inc. + */ +#ifndef __ASM_BREAK_H +#define __ASM_BREAK_H + +/* + * The following break codes are or were in use for specific purposes in + * other MIPS operating systems. Linux/MIPS doesn't use all of them. The + * unused ones are here as placeholders; we might encounter them in + * non-Linux/MIPS object files or make use of them in the future. + */ +#define BRK_USERBP 0 /* User bp (used by debuggers) */ +#define BRK_KERNELBP 1 /* Break in the kernel */ +#define BRK_ABORT 2 /* Sometimes used by abort(3) to SIGIOT */ +#define BRK_BD_TAKEN 3 /* For bd slot emulation - not implemented */ +#define BRK_BD_NOTTAKEN 4 /* For bd slot emulation - not implemented */ +#define BRK_SSTEPBP 5 /* User bp (used by debuggers) */ +#define BRK_OVERFLOW 6 /* Overflow check */ +#define BRK_DIVZERO 7 /* Divide by zero check */ +#define BRK_RANGE 8 /* Range error check */ +#define BRK_STACKOVERFLOW 9 /* For Ada stackchecking */ +#define BRK_NORLD 10 /* No rld found - not used by Linux/MIPS */ +#define _BRK_THREADBP 11 /* For threads, user bp (used by debuggers) */ +#define BRK_MULOVF 1023 /* Multiply overflow */ +#define BRK_BUG 512 /* Used by BUG() */ + +#endif /* __ASM_BREAK_H */ diff --git a/include/asm-mips/bug.h b/include/asm-mips/bug.h index 76c923dcf903..d34c34f30282 100644 --- a/include/asm-mips/bug.h +++ b/include/asm-mips/bug.h @@ -1,15 +1,20 @@ -/* $Id$ */ #ifndef __ASM_BUG_H #define __ASM_BUG_H -#define BUG() do { printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); *(int *)0=0; } while (0) +#include <asm/break.h> + +#define BUG() \ +do { \ + printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); \ + __asm__ __volatile__("break %0" : : "i" (BRK_BUG)); \ +} while (0) #define BUG_ON(condition) do { if (unlikely((condition)!=0)) BUG(); } while(0) #define PAGE_BUG(page) do { BUG(); } while (0) #define WARN_ON(condition) do { \ if (unlikely((condition)!=0)) { \ printk("Badness in %s at %s:%d\n", __FUNCTION__, __FILE__, __LINE__); \ - dump_stack(); \ + dump_stack(); \ } \ } while (0) diff --git a/include/asm-mips/bugs.h b/include/asm-mips/bugs.h index 17b94e2693cc..78fd2a3d6fb9 100644 --- a/include/asm-mips/bugs.h +++ b/include/asm-mips/bugs.h @@ -1,47 +1,12 @@ /* - * Copyright (C) 1995 Waldorf Electronics - * Copyright (C) 1997, 1999 Ralf Baechle - */ -#include <asm/bootinfo.h> -#include <asm/cpu.h> - -/* * This is included by init/main.c to check for architecture-dependent bugs. * * Needs: * void check_bugs(void); */ +#ifndef __ASM_BUGS_H +#define __ASM_BUGS_H +extern void check_bugs(void); -static inline void check_wait(void) -{ - printk("Checking for 'wait' instruction... "); - switch(mips_cpu.cputype) { - case CPU_R3081: - case CPU_R3081E: - cpu_wait = r3081_wait; - printk(" available.\n"); - break; - case CPU_R4200: - case CPU_R4300: - case CPU_R4600: - case CPU_R4640: - case CPU_R4650: - case CPU_R4700: - case CPU_R5000: - case CPU_NEVADA: - case CPU_RM7000: - cpu_wait = r4k_wait; - printk(" available.\n"); - break; - default: - printk(" unavailable.\n"); - break; - } -} - -static void __init -check_bugs(void) -{ - check_wait(); -} +#endif /* __ASM_BUGS_H */ diff --git a/include/asm-mips/byteorder.h b/include/asm-mips/byteorder.h index b9604cf202aa..445af5ec37eb 100644 --- a/include/asm-mips/byteorder.h +++ b/include/asm-mips/byteorder.h @@ -1,5 +1,4 @@ -/* $Id: byteorder.h,v 1.8 1998/11/02 09:29:32 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. diff --git a/include/asm-mips/cache.h b/include/asm-mips/cache.h index 05fc11d65ca9..4057cb45f744 100644 --- a/include/asm-mips/cache.h +++ b/include/asm-mips/cache.h @@ -3,7 +3,7 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 1997, 98, 99, 2000 Ralf Baechle + * Copyright (C) 1997, 98, 99, 2000, 2003 Ralf Baechle * Copyright (C) 1999 Silicon Graphics, Inc. */ #ifndef _ASM_CACHE_H @@ -11,30 +11,15 @@ #include <linux/config.h> -#ifndef _LANGUAGE_ASSEMBLY -/* - * Descriptor for a cache - */ -struct cache_desc { - int linesz; - int sets; - int ways; - int flags; /* Details like write thru/back, coherent, etc. */ -}; -#endif - -/* - * Flag definitions - */ -#define MIPS_CACHE_NOT_PRESENT 0x00000001 - -#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_R6000) +#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_R6000) || \ + defined(CONFIG_CPU_TX39XX) #define L1_CACHE_BYTES 16 +#define L1_CACHE_SHIFT_MAX 4 /* largest L1 which this arch supports */ #else #define L1_CACHE_BYTES 32 /* A guess */ +#define L1_CACHE_SHIFT_MAX 6 /* largest L1 which this arch supports */ #endif #define SMP_CACHE_BYTES L1_CACHE_BYTES -#define L1_CACHE_SHIFT_MAX 5 /* largest L1 which this arch supports */ #endif /* _ASM_CACHE_H */ diff --git a/include/asm-mips/cacheflush.h b/include/asm-mips/cacheflush.h new file mode 100644 index 000000000000..6e586c25ff13 --- /dev/null +++ b/include/asm-mips/cacheflush.h @@ -0,0 +1,65 @@ +/* + * 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, 95, 96, 97, 98, 99, 2000, 01, 02, 03 by Ralf Baechle + * Copyright (C) 1999, 2000, 2001 Silicon Graphics, Inc. + */ +#ifndef __ASM_CACHEFLUSH_H +#define __ASM_CACHEFLUSH_H + +#include <linux/config.h> + +/* Keep includes the same across arches. */ +#include <linux/mm.h> + +/* Cache flushing: + * + * - flush_cache_all() flushes entire cache + * - flush_cache_mm(mm) flushes the specified mm context's cache lines + * - flush_cache_page(mm, vmaddr) flushes a single page + * - flush_cache_range(vma, start, end) flushes a range of pages + * - flush_icache_range(start, end) flush a range of instructions + * - flush_dcache_page(pg) flushes(wback&invalidates) a page for dcache + * - flush_icache_page(vma, pg) flushes(invalidates) a page for icache + * + * MIPS specific flush operations: + * + * - flush_cache_sigtramp() flush signal trampoline + * - flush_icache_all() flush the entire instruction cache + * - flush_data_cache_page() flushes a page from the data cache + */ +extern void (*flush_cache_all)(void); +extern void (*__flush_cache_all)(void); +extern void (*flush_cache_mm)(struct mm_struct *mm); +extern void (*flush_cache_range)(struct vm_area_struct *vma, + unsigned long start, unsigned long end); +extern void (*flush_cache_page)(struct vm_area_struct *vma, + unsigned long page); +extern void flush_dcache_page(struct page *page); +extern void (*flush_icache_page)(struct vm_area_struct *vma, + struct page *page); +extern void (*flush_icache_range)(unsigned long start, unsigned long end); +#define flush_icache_user_range(vma, page, addr, len) \ + flush_icache_page(vma, page) + + +extern void (*flush_cache_sigtramp)(unsigned long addr); +extern void (*flush_icache_all)(void); +extern void (*flush_data_cache_page)(unsigned long addr); + +/* + * This flag is used to indicate that the page pointed to by a pte + * is dirty and requires cleaning before returning it to the user. + */ +#define PG_dcache_dirty PG_arch_1 + +#define Page_dcache_dirty(page) \ + test_bit(PG_dcache_dirty, &(page)->flags) +#define SetPageDcacheDirty(page) \ + set_bit(PG_dcache_dirty, &(page)->flags) +#define ClearPageDcacheDirty(page) \ + clear_bit(PG_dcache_dirty, &(page)->flags) + +#endif /* __ASM_CACHEFLUSH_H */ diff --git a/include/asm-mips/cacheops.h b/include/asm-mips/cacheops.h index 66b0b361f03e..91a2bed3669a 100644 --- a/include/asm-mips/cacheops.h +++ b/include/asm-mips/cacheops.h @@ -5,43 +5,77 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. * - * (C) Copyright 1996, 1997 by Ralf Baechle + * (C) Copyright 1996, 97, 99, 2002, 03 Ralf Baechle + * (C) Copyright 1999 Silicon Graphics, Inc. */ -#ifndef __ASM_MIPS_CACHEOPS_H -#define __ASM_MIPS_CACHEOPS_H +#ifndef __ASM_CACHEOPS_H +#define __ASM_CACHEOPS_H /* - * Cache Operations + * Cache Operations available on all MIPS processors with R4000-style caches */ #define Index_Invalidate_I 0x00 #define Index_Writeback_Inv_D 0x01 -#define Index_Invalidate_SI 0x02 -#define Index_Writeback_Inv_SD 0x03 #define Index_Load_Tag_I 0x04 #define Index_Load_Tag_D 0x05 -#define Index_Load_Tag_SI 0x06 -#define Index_Load_Tag_SD 0x07 #define Index_Store_Tag_I 0x08 #define Index_Store_Tag_D 0x09 +#define Hit_Invalidate_I 0x10 +#define Hit_Invalidate_D 0x11 +#define Hit_Writeback_Inv_D 0x15 +#define Hit_Writeback_I 0x18 +#define Hit_Writeback_D 0x19 + +/* + * R4000-specific cacheops + */ +#define Create_Dirty_Excl_D 0x0d +#define Fill 0x14 + +/* + * R4000SC and R4400SC-specific cacheops + */ +#define Index_Invalidate_SI 0x02 +#define Index_Writeback_Inv_SD 0x03 +#define Index_Load_Tag_SI 0x06 +#define Index_Load_Tag_SD 0x07 #define Index_Store_Tag_SI 0x0A #define Index_Store_Tag_SD 0x0B -#define Create_Dirty_Excl_D 0x0d #define Create_Dirty_Excl_SD 0x0f -#define Hit_Invalidate_I 0x10 -#define Hit_Invalidate_D 0x11 #define Hit_Invalidate_SI 0x12 #define Hit_Invalidate_SD 0x13 -#define Fill 0x14 -#define Hit_Writeback_Inv_D 0x15 - /* 0x16 is unused */ #define Hit_Writeback_Inv_SD 0x17 -#define Hit_Writeback_I 0x18 -#define Hit_Writeback_D 0x19 - /* 0x1a is unused */ #define Hit_Writeback_SD 0x1b - /* 0x1c is unused */ - /* 0x1e is unused */ #define Hit_Set_Virtual_SI 0x1e #define Hit_Set_Virtual_SD 0x1f -#endif /* __ASM_MIPS_CACHEOPS_H */ +/* + * R5000-specific cacheops + */ +#define R5K_Page_Invalidate_S 0x17 + +/* + * RM7000-specific cacheops + */ +#define Page_Invalidate_T 0x16 + +/* + * R1000-specific cacheops + * + * Cacheops 0x02, 0x06, 0x0a, 0x0c-0x0e, 0x16, 0x1a and 0x1e are unused. + * Most of the _S cacheops are identical to the R4000SC _SD cacheops. + */ +#define Index_Writeback_Inv_S 0x03 +#define Index_Load_Tag_S 0x07 +#define Index_Store_Tag_S 0x0B +#define Hit_Invalidate_S 0x13 +#define Cache_Barrier 0x14 +#define Hit_Writeback_Inv_S 0x17 +#define Index_Load_Data_I 0x18 +#define Index_Load_Data_D 0x19 +#define Index_Load_Data_S 0x1b +#define Index_Store_Data_I 0x1c +#define Index_Store_Data_D 0x1d +#define Index_Store_Data_S 0x1f + +#endif /* __ASM_CACHEOPS_H */ diff --git a/include/asm-mips/checksum.h b/include/asm-mips/checksum.h index 55932940b64b..756e6a6429c8 100644 --- a/include/asm-mips/checksum.h +++ b/include/asm-mips/checksum.h @@ -9,6 +9,7 @@ #define _ASM_CHECKSUM_H #include <asm/uaccess.h> +#include <linux/in6.h> /* * computes the checksum of a memory block at buff, length len, @@ -35,7 +36,7 @@ unsigned int csum_partial_copy_from_user(const char *src, char *dst, int len, * Copy and checksum to user */ #define HAVE_CSUM_COPY_USER -extern inline unsigned int csum_and_copy_to_user (const char *src, char *dst, +static inline unsigned int csum_and_copy_to_user (const char *src, char *dst, int len, int sum, int *err_ptr) { @@ -71,12 +72,11 @@ static inline unsigned short int csum_fold(unsigned int sum) "xori\t%0,0xffff\n\t" ".set\tat" : "=r" (sum) - : "0" (sum) - : "$1"); + : "0" (sum)); - return sum; + return sum; } - + /* * This is a version of ip_compute_csum() optimized for IP headers, * which always checksum on 4 octet boundaries. @@ -124,8 +124,7 @@ static inline unsigned short ip_fast_csum(unsigned char *iph, "2:\t.set\tat\n\t" ".set\treorder" : "=&r" (sum), "=&r" (iph), "=&r" (ihl), "=&r" (dummy) - : "1" (iph), "2" (ihl) - : "$1"); + : "1" (iph), "2" (ihl)); return csum_fold(sum); } @@ -161,8 +160,7 @@ static inline unsigned long csum_tcpudp_nofold(unsigned long saddr, #else "r" (((proto)<<16)+len), #endif - "r" (sum) - : "$1"); + "r" (sum)); return sum; } @@ -194,10 +192,11 @@ static __inline__ unsigned short int csum_ipv6_magic(struct in6_addr *saddr, struct in6_addr *daddr, __u32 len, unsigned short proto, - unsigned int sum) + unsigned int sum) { __asm__( - ".set\tnoreorder\t\t\t# csum_ipv6_magic\n\t" + ".set\tpush\t\t\t# csum_ipv6_magic\n\t" + ".set\tnoreorder\n\t" ".set\tnoat\n\t" "addu\t%0, %5\t\t\t# proto (long in network byte order)\n\t" "sltu\t$1, %0, %5\n\t" @@ -208,48 +207,48 @@ static __inline__ unsigned short int csum_ipv6_magic(struct in6_addr *saddr, "lw\t%1, 0(%2)\t\t\t# four words source address\n\t" "addu\t%0, $1\n\t" "addu\t%0, %1\n\t" - "sltu\t$1, %0, $1\n\t" + "sltu\t$1, %0, %1\n\t" "lw\t%1, 4(%2)\n\t" "addu\t%0, $1\n\t" "addu\t%0, %1\n\t" - "sltu\t$1, %0, $1\n\t" + "sltu\t$1, %0, %1\n\t" "lw\t%1, 8(%2)\n\t" "addu\t%0, $1\n\t" "addu\t%0, %1\n\t" - "sltu\t$1, %0, $1\n\t" + "sltu\t$1, %0, %1\n\t" "lw\t%1, 12(%2)\n\t" "addu\t%0, $1\n\t" "addu\t%0, %1\n\t" - "sltu\t$1, %0, $1\n\t" + "sltu\t$1, %0, %1\n\t" "lw\t%1, 0(%3)\n\t" "addu\t%0, $1\n\t" "addu\t%0, %1\n\t" - "sltu\t$1, %0, $1\n\t" + "sltu\t$1, %0, %1\n\t" "lw\t%1, 4(%3)\n\t" "addu\t%0, $1\n\t" "addu\t%0, %1\n\t" - "sltu\t$1, %0, $1\n\t" + "sltu\t$1, %0, %1\n\t" "lw\t%1, 8(%3)\n\t" "addu\t%0, $1\n\t" "addu\t%0, %1\n\t" - "sltu\t$1, %0, $1\n\t" + "sltu\t$1, %0, %1\n\t" "lw\t%1, 12(%3)\n\t" "addu\t%0, $1\n\t" "addu\t%0, %1\n\t" - "sltu\t$1, %0, $1\n\t" - ".set\tnoat\n\t" - ".set\tnoreorder" + "sltu\t$1, %0, %1\n\t" + + "addu\t%0, $1\t\t\t# Add final carry\n\t" + ".set\tpop" : "=r" (sum), "=r" (proto) : "r" (saddr), "r" (daddr), - "0" (htonl(len)), "1" (htonl(proto)), "r" (sum) - : "$1"); + "0" (htonl(len)), "1" (htonl(proto)), "r" (sum)); return csum_fold(sum); } diff --git a/include/asm-mips/cpu.h b/include/asm-mips/cpu.h index 8f4981b763bc..f2289ddc5432 100644 --- a/include/asm-mips/cpu.h +++ b/include/asm-mips/cpu.h @@ -7,10 +7,10 @@ #ifndef _ASM_CPU_H #define _ASM_CPU_H -#include <asm/cache.h> +#include <linux/cpu.h> -/* Assigned Company values for bits 23:16 of the PRId Register - (CP0 register 15, select 0). As of the MIPS32 and MIPS64 specs from +/* Assigned Company values for bits 23:16 of the PRId Register + (CP0 register 15, select 0). As of the MIPS32 and MIPS64 specs from MTI, the PRId register is defined in this (backwards compatible) way: @@ -21,16 +21,15 @@ I don't have docs for all the previous processors, but my impression is that bits 16-23 have been 0 for all MIPS processors before the MIPS32/64 - spec. + spec. */ #define PRID_COMP_LEGACY 0x000000 #define PRID_COMP_MIPS 0x010000 +#define PRID_COMP_BROADCOM 0x020000 #define PRID_COMP_ALCHEMY 0x030000 -/* - * Don't know who should be here...QED and Sandcraft, maybe? - */ #define PRID_COMP_SIBYTE 0x040000 +#define PRID_COMP_SANDCRAFT 0x050000 /* * Assigned values for the product ID register. In order to detect a @@ -38,7 +37,8 @@ * be examined. These are valid when 23:16 == PRID_COMP_LEGACY */ #define PRID_IMP_R2000 0x0100 -#define PRID_IMP_AU1000 0x0100 +#define PRID_IMP_AU1_REV1 0x0100 +#define PRID_IMP_AU1_REV2 0x0200 #define PRID_IMP_R3000 0x0200 /* Same as R2000A */ #define PRID_IMP_R6000 0x0300 /* Same as R3000A */ #define PRID_IMP_R4000 0x0400 @@ -54,13 +54,16 @@ #define PRID_IMP_R4640 0x2200 #define PRID_IMP_R4650 0x2200 /* Same as R4640 */ #define PRID_IMP_R5000 0x2300 +#define PRID_IMP_TX49 0x2d00 #define PRID_IMP_SONIC 0x2400 #define PRID_IMP_MAGIC 0x2500 #define PRID_IMP_RM7000 0x2700 #define PRID_IMP_NEVADA 0x2800 /* RM5260 ??? */ #define PRID_IMP_R5432 0x5400 +#define PRID_IMP_R5500 0x5500 #define PRID_IMP_4KC 0x8000 #define PRID_IMP_5KC 0x8100 +#define PRID_IMP_20KC 0x8200 #define PRID_IMP_4KEC 0x8400 #define PRID_IMP_4KSC 0x8600 @@ -74,10 +77,18 @@ #define PRID_IMP_SB1 0x0100 /* + * These are the PRID's for when 23:16 == PRID_COMP_SANDCRAFT + */ + +#define PRID_IMP_SR71000 0x0400 + +/* * Definitions for 7:0 on legacy processors */ +#define PRID_REV_TX4927 0x0022 +#define PRID_REV_TX4937 0x0030 #define PRID_REV_R4400 0x0040 #define PRID_REV_R3000A 0x0030 #define PRID_REV_R3000 0x0020 @@ -85,50 +96,116 @@ #define PRID_REV_TX3912 0x0010 #define PRID_REV_TX3922 0x0030 #define PRID_REV_TX3927 0x0040 +#define PRID_REV_VR4111 0x0050 +#define PRID_REV_VR4181 0x0050 /* Same as VR4111 */ +#define PRID_REV_VR4121 0x0060 +#define PRID_REV_VR4122 0x0070 +#define PRID_REV_VR4181A 0x0070 /* Same as VR4122 */ +#define PRID_REV_VR4131 0x0080 -#ifndef _LANGUAGE_ASSEMBLY /* - * Capability and feature descriptor structure for MIPS CPU + * FPU implementation/revision register (CP1 control register 0). + * + * +---------------------------------+----------------+----------------+ + * | 0 | Implementation | Revision | + * +---------------------------------+----------------+----------------+ + * 31 16 15 8 7 0 */ -struct mips_cpu { - unsigned int processor_id; - unsigned int cputype; /* Old "mips_cputype" code */ - int isa_level; - int options; - int tlbsize; - struct cache_desc icache; /* Primary I-cache */ - struct cache_desc dcache; /* Primary D or combined I/D cache */ - struct cache_desc scache; /* Secondary cache */ - struct cache_desc tcache; /* Tertiary/split secondary cache */ -}; - -#endif + +#define FPIR_IMP_NONE 0x0000 + +#define CPU_UNKNOWN 0 +#define CPU_R2000 1 +#define CPU_R3000 2 +#define CPU_R3000A 3 +#define CPU_R3041 4 +#define CPU_R3051 5 +#define CPU_R3052 6 +#define CPU_R3081 7 +#define CPU_R3081E 8 +#define CPU_R4000PC 9 +#define CPU_R4000SC 10 +#define CPU_R4000MC 11 +#define CPU_R4200 12 +#define CPU_R4400PC 13 +#define CPU_R4400SC 14 +#define CPU_R4400MC 15 +#define CPU_R4600 16 +#define CPU_R6000 17 +#define CPU_R6000A 18 +#define CPU_R8000 19 +#define CPU_R10000 20 +#define CPU_R12000 21 +#define CPU_R4300 22 +#define CPU_R4650 23 +#define CPU_R4700 24 +#define CPU_R5000 25 +#define CPU_R5000A 26 +#define CPU_R4640 27 +#define CPU_NEVADA 28 +#define CPU_RM7000 29 +#define CPU_R5432 30 +#define CPU_4KC 31 +#define CPU_5KC 32 +#define CPU_R4310 33 +#define CPU_SB1 34 +#define CPU_TX3912 35 +#define CPU_TX3922 36 +#define CPU_TX3927 37 +#define CPU_AU1000 38 +#define CPU_4KEC 39 +#define CPU_4KSC 40 +#define CPU_VR41XX 41 +#define CPU_R5500 42 +#define CPU_TX49XX 43 +#define CPU_AU1500 44 +#define CPU_20KC 45 +#define CPU_VR4111 46 +#define CPU_VR4121 47 +#define CPU_VR4122 48 +#define CPU_VR4131 49 +#define CPU_VR4181 50 +#define CPU_VR4181A 51 +#define CPU_AU1100 52 +#define CPU_SR71000 53 +#define CPU_LAST 53 /* * ISA Level encodings + * */ #define MIPS_CPU_ISA_I 0x00000001 #define MIPS_CPU_ISA_II 0x00000002 -#define MIPS_CPU_ISA_III 0x00000003 -#define MIPS_CPU_ISA_IV 0x00000004 -#define MIPS_CPU_ISA_V 0x00000005 +#define MIPS_CPU_ISA_III 0x00008003 +#define MIPS_CPU_ISA_IV 0x00008004 +#define MIPS_CPU_ISA_V 0x00008005 #define MIPS_CPU_ISA_M32 0x00000020 -#define MIPS_CPU_ISA_M64 0x00000040 +#define MIPS_CPU_ISA_M64 0x00008040 + +/* + * Bit 15 encodes if an ISA level supports 64-bit operations. + */ +#define MIPS_CPU_ISA_64BIT 0x00008000 /* * CPU Option encodings */ -#define MIPS_CPU_TLB 0x00000001 /* CPU has TLB */ +#define MIPS_CPU_TLB 0x00000001 /* CPU has TLB */ /* Leave a spare bit for variant MMU types... */ -#define MIPS_CPU_4KEX 0x00000004 /* "R4K" exception model */ -#define MIPS_CPU_4KTLB 0x00000008 /* "R4K" TLB handler */ -#define MIPS_CPU_FPU 0x00000010 /* CPU has FPU */ -#define MIPS_CPU_32FPR 0x00000020 /* 32 dbl. prec. FP registers */ -#define MIPS_CPU_COUNTER 0x00000040 /* Cycle count/compare */ -#define MIPS_CPU_WATCH 0x00000080 /* watchpoint registers */ -#define MIPS_CPU_MIPS16 0x00000100 /* code compression */ -#define MIPS_CPU_DIVEC 0x00000200 /* dedicated interrupt vector */ -#define MIPS_CPU_VCE 0x00000400 /* virt. coherence conflict possible */ +#define MIPS_CPU_4KEX 0x00000004 /* "R4K" exception model */ +#define MIPS_CPU_4KTLB 0x00000008 /* "R4K" TLB handler */ +#define MIPS_CPU_FPU 0x00000010 /* CPU has FPU */ +#define MIPS_CPU_32FPR 0x00000020 /* 32 dbl. prec. FP registers */ +#define MIPS_CPU_COUNTER 0x00000040 /* Cycle count/compare */ +#define MIPS_CPU_WATCH 0x00000080 /* watchpoint registers */ +#define MIPS_CPU_MIPS16 0x00000100 /* code compression */ +#define MIPS_CPU_DIVEC 0x00000200 /* dedicated interrupt vector */ +#define MIPS_CPU_VCE 0x00000400 /* virt. coherence conflict possible */ #define MIPS_CPU_CACHE_CDEX 0x00000800 /* Create_Dirty_Exclusive CACHE op */ +#define MIPS_CPU_MCHECK 0x00001000 /* Machine check exception */ +#define MIPS_CPU_EJTAG 0x00002000 /* EJTAG exception */ +#define MIPS_CPU_NOFPUEX 0x00004000 /* no FPU exception */ +#define MIPS_CPU_LLSC 0x00008000 /* CPU has ll/sc instructions */ +#define MIPS_CPU_SUBSET_CACHES 0x00010000 /* P-cache subset enforced */ #endif /* _ASM_CPU_H */ diff --git a/include/asm-mips/current.h b/include/asm-mips/current.h index 2c776757e74a..559db66b9790 100644 --- a/include/asm-mips/current.h +++ b/include/asm-mips/current.h @@ -3,17 +3,21 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 1998 Ralf Baechle + * Copyright (C) 1998, 2002 Ralf Baechle * Copyright (C) 1999 Silicon Graphics, Inc. */ #ifndef _ASM_CURRENT_H #define _ASM_CURRENT_H -#ifdef _LANGUAGE_C +#include <linux/thread_info.h> -/* MIPS rules... */ -register struct task_struct *current asm("$28"); +struct task_struct; -#endif /* _LANGUAGE_C */ +static inline struct task_struct * get_current(void) +{ + return current_thread_info()->task; +} + +#define current get_current() #endif /* _ASM_CURRENT_H */ diff --git a/include/asm-mips/db1x00.h b/include/asm-mips/db1x00.h new file mode 100644 index 000000000000..859614ae1edd --- /dev/null +++ b/include/asm-mips/db1x00.h @@ -0,0 +1,118 @@ +/* + * AMD Alchemy DB1x00 Reference Boards + * + * Copyright 2001 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * ppopov@mvista.com or source@mvista.com + * + * ######################################################################## + * + * This program is free software; you can distribute it and/or modify it + * under the terms of the GNU General Public License (Version 2) as + * published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * ######################################################################## + * + * + */ +#ifndef __ASM_DB1X00_H +#define __ASM_DB1X00_H + + +/* + * Overlay data structure of the Db1x00 board registers. + * Registers located at physical 1E0000xx, KSEG1 0xAE0000xx + */ +typedef volatile struct +{ + /*00*/ unsigned long whoami; + /*04*/ unsigned long status; + /*08*/ unsigned long switches; + /*0C*/ unsigned long resets; + /*10*/ unsigned long pcmcia; + /*14*/ unsigned long specific; + /*18*/ unsigned long leds; + /*1C*/ unsigned long swreset; + +} BCSR; + +/* + * Register/mask bit definitions for the BCSRs + */ +#define BCSR_WHOAMI_DCID 0x000F +#define BCSR_WHOAMI_CPLD 0x00F0 +#define BCSR_WHOAMI_BOARD 0x0F00 + +#define BCSR_STATUS_PC0VS 0x0003 +#define BCSR_STATUS_PC1VS 0x000C +#define BCSR_STATUS_PC0FI 0x0010 +#define BCSR_STATUS_PC1FI 0x0020 +#define BCSR_STATUS_FLASHBUSY 0x0100 +#define BCSR_STATUS_ROMBUSY 0x0400 +#define BCSR_STATUS_SWAPBOOT 0x2000 +#define BCSR_STATUS_FLASHDEN 0xC000 + +#define BCSR_SWITCHES_DIP 0x00FF +#define BCSR_SWITCHES_DIP_1 0x0080 +#define BCSR_SWITCHES_DIP_2 0x0040 +#define BCSR_SWITCHES_DIP_3 0x0020 +#define BCSR_SWITCHES_DIP_4 0x0010 +#define BCSR_SWITCHES_DIP_5 0x0008 +#define BCSR_SWITCHES_DIP_6 0x0004 +#define BCSR_SWITCHES_DIP_7 0x0002 +#define BCSR_SWITCHES_DIP_8 0x0001 +#define BCSR_SWITCHES_ROTARY 0x0F00 + +#define BCSR_RESETS_PHY0 0x0001 +#define BCSR_RESETS_PHY1 0x0002 +#define BCSR_RESETS_DC 0x0004 + +#define BCSR_PCMCIA_PC0VPP 0x0003 +#define BCSR_PCMCIA_PC0VCC 0x000C +#define BCSR_PCMCIA_PC0DRVEN 0x0010 +#define BCSR_PCMCIA_PC0RST 0x0080 +#define BCSR_PCMCIA_PC1VPP 0x0300 +#define BCSR_PCMCIA_PC1VCC 0x0C00 +#define BCSR_PCMCIA_PC1DRVEN 0x1000 +#define BCSR_PCMCIA_PC1RST 0x8000 + +#define BCSR_BOARD_PCIM66EN 0x0001 +#define BCSR_BOARD_PCIM33 0x0100 +#define BCSR_BOARD_GPIO200RST 0x0400 +#define BCSR_BOARD_PCICFG 0x1000 + +#define BCSR_LEDS_DECIMALS 0x0003 +#define BCSR_LEDS_LED0 0x0100 +#define BCSR_LEDS_LED1 0x0200 +#define BCSR_LEDS_LED2 0x0400 +#define BCSR_LEDS_LED3 0x0800 + +#define BCSR_SWRESET_RESET 0x0080 + +/* PCMCIA Db1x00 specific defines */ +#define PCMCIA_MAX_SOCK 1 +#define PCMCIA_NUM_SOCKS (PCMCIA_MAX_SOCK+1) + +/* VPP/VCC */ +#define SET_VCC_VPP(VCC, VPP, SLOT)\ + ((((VCC)<<2) | ((VPP)<<0)) << ((SLOT)*8)) + +/* MTD CONFIG OPTIONS */ +#if defined(CONFIG_MTD_DB1X00_BOOT) && defined(CONFIG_MTD_DB1X00_USER) +#define DB1X00_BOTH_BANKS +#elif defined(CONFIG_MTD_DB1X00_BOOT) && !defined(CONFIG_MTD_DB1X00_USER) +#define DB1X00_BOOT_ONLY +#elif !defined(CONFIG_MTD_DB1X00_BOOT) && defined(CONFIG_MTD_DB1X00_USER) +#define DB1X00_USER_ONLY +#endif + +#endif /* __ASM_DB1X00_H */ diff --git a/include/asm-mips/debug.h b/include/asm-mips/debug.h new file mode 100644 index 000000000000..e5466194cade --- /dev/null +++ b/include/asm-mips/debug.h @@ -0,0 +1,48 @@ +/* + * Debug macros for run-time debugging. Turned on/off with CONFIG_RUNTIME_DEBUG option. + * + * Copyright (C) 2001 MontaVista Software Inc. + * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifndef _ASM_DEBUG_H +#define _ASM_DEBUG_H + +#include <linux/config.h> + +/* + * run-time macros for catching spurious errors. Eable CONFIG_RUNTIME_DEBUG in + * kernel hacking config menu to use them. + * + * Use them as run-time debugging aid. NEVER USE THEM AS ERROR HANDLING CODE!!! + */ + +#ifdef CONFIG_RUNTIME_DEBUG + +#include <linux/kernel.h> + +#define db_assert(x) if (!(x)) { \ + panic("assertion failed at %s:%d: %s\n", __FILE__, __LINE__, #x); } +#define db_warn(x) if (!(x)) { \ + printk(KERN_WARNING "warning at %s:%d: %s\n", __FILE__, __LINE__, #x); } +#define db_verify(x, y) db_assert(x y) +#define db_verify_warn(x, y) db_warn(x y) +#define db_run(x) do { x; } while (0) + +#else + +#define db_assert(x) +#define db_warn(x) +#define db_verify(x, y) x +#define db_verify_warn(x, y) x +#define db_run(x) + +#endif + +#endif /* _ASM_DEBUG_H */ diff --git a/include/asm-mips/delay.h b/include/asm-mips/delay.h index 50024e3481c1..751757079ee0 100644 --- a/include/asm-mips/delay.h +++ b/include/asm-mips/delay.h @@ -10,6 +10,7 @@ #define _ASM_DELAY_H #include <linux/config.h> +#include <linux/param.h> extern unsigned long loops_per_jiffy; @@ -26,7 +27,7 @@ __delay(unsigned long loops) } /* - * division by multiplication: you don't have to worry about + * Division by multiplication: you don't have to worry about * loss of precision. * * Use only for very small delays ( < 1 msec). Should probably use a @@ -39,7 +40,11 @@ extern __inline__ void __udelay(unsigned long usecs, unsigned long lpj) { unsigned long lo; - usecs *= 0x00068db8; /* 2**32 / (1000000 / HZ) */ + /* + * Excessive precission? Probably ... + */ + usecs *= (unsigned long) (((0x8000000000000000ULL / (500000 / HZ)) + + 0x80000000ULL) >> 32); __asm__("multu\t%2,%3" :"=h" (usecs), "=l" (lo) :"r" (usecs),"r" (lpj)); diff --git a/include/asm-mips/div64.h b/include/asm-mips/div64.h index ce2a45d83897..6ef773a65ac5 100644 --- a/include/asm-mips/div64.h +++ b/include/asm-mips/div64.h @@ -1,6 +1,4 @@ /* - * include/asm-mips/div64.h - * * Copyright (C) 2000 Maciej W. Rozycki * * This file is subject to the terms and conditions of the GNU General Public @@ -10,105 +8,69 @@ #ifndef _ASM_DIV64_H #define _ASM_DIV64_H -#include <asm/sgidefs.h> - /* * No traps on overflows for any of these... */ -#if (_MIPS_ISA == _MIPS_ISA_MIPS1 ) || (_MIPS_ISA == _MIPS_ISA_MIPS2) || \ - (_MIPS_ISA == _MIPS_ISA_MIPS32) - #define do_div64_32(res, high, low, base) ({ \ unsigned long __quot, __mod; \ - unsigned long __cf, __tmp, __i; \ + unsigned long __cf, __tmp, __tmp2, __i; \ \ __asm__(".set push\n\t" \ ".set noat\n\t" \ ".set noreorder\n\t" \ + "move %2, $0\n\t" \ + "move %3, $0\n\t" \ "b 1f\n\t" \ - " li %4,0x21\n" \ + " li %4, 0x21\n" \ "0:\n\t" \ - "sll $1,%0,0x1\n\t" \ - "srl %3,%0,0x1f\n\t" \ - "or %0,$1,$2\n\t" \ - "sll %1,%1,0x1\n\t" \ - "sll %2,%2,0x1\n" \ + "sll $1, %0, 0x1\n\t" \ + "srl %3, %0, 0x1f\n\t" \ + "or %0, $1, %5\n\t" \ + "sll %1, %1, 0x1\n\t" \ + "sll %2, %2, 0x1\n" \ "1:\n\t" \ - "bnez %3,2f\n\t" \ - "sltu $2,%0,%z5\n\t" \ - "bnez $2,3f\n\t" \ + "bnez %3, 2f\n\t" \ + " sltu %5, %0, %z6\n\t" \ + "bnez %5, 3f\n" \ "2:\n\t" \ - " addiu %4,%4,-1\n\t" \ - "subu %0,%0,%z5\n\t" \ - "addiu %2,%2,1\n" \ + " addiu %4, %4, -1\n\t" \ + "subu %0, %0, %z6\n\t" \ + "addiu %2, %2, 1\n" \ "3:\n\t" \ - "bnez %4,0b\n\t" \ - " srl $2,%1,0x1f\n\t" \ + "bnez %4, 0b\n\t" \ + " srl %5, %1, 0x1f\n\t" \ ".set pop" \ : "=&r" (__mod), "=&r" (__tmp), "=&r" (__quot), "=&r" (__cf), \ - "=&r" (__i) \ - : "Jr" (base), "0" (high), "1" (low), "2" (0), "3" (0) \ - /* Aarrgh! Ran out of gcc's limit on constraints... */ \ - : "$1", "$2"); \ + "=&r" (__i), "=&r" (__tmp2) \ + : "Jr" (base), "0" (high), "1" (low)); \ \ (res) = __quot; \ __mod; }) #define do_div(n, base) ({ \ unsigned long long __quot; \ - unsigned long __upper, __low, __high, __mod; \ + unsigned long __mod; \ + unsigned long long __div; \ + unsigned long __upper, __low, __high, __base; \ + \ + __div = (n); \ + __base = (base); \ \ - __quot = (n); \ - __high = __quot >> 32; \ - __low = __quot; \ + __high = __div >> 32; \ + __low = __div; \ __upper = __high; \ \ if (__high) \ - __asm__("divu $0,%z2,%z3" \ + __asm__("divu $0, %z2, %z3" \ : "=h" (__upper), "=l" (__high) \ - : "Jr" (__high), "Jr" (base)); \ + : "Jr" (__high), "Jr" (__base)); \ \ - __mod = do_div64_32(__low, __upper, __low, base); \ + __mod = do_div64_32(__low, __upper, __low, __base); \ \ __quot = __high; \ __quot = __quot << 32 | __low; \ (n) = __quot; \ __mod; }) -#else - -#define do_div64_32(res, high, low, base) ({ \ - unsigned long __quot, __mod, __r0; \ - \ - __asm__("dsll32 %2,%z3,0\n\t" \ - "or %2,%2,%z4\n\t" \ - "ddivu $0,%2,%z5" \ - : "=h" (__mod), "=l" (__quot), "=&r" (__r0) \ - : "Jr" (high), "Jr" (low), "Jr" (base)); \ - \ - (res) = __quot; \ - __mod; }) - -#define do_div(n, base) ({ \ - unsigned long long __quot; \ - unsigned long __mod, __r0; \ - \ - __quot = (n); \ - \ - __asm__("dsll32 %2,%M3,0\n\t" \ - "or %2,%2,%L3\n\t" \ - "ddivu $0,%2,%z4\n\t" \ - "mflo %L1\n\t" \ - "dsra32 %M1,%L1,0\n\t" \ - "dsll32 %L1,%L1,0\n\t" \ - "dsra32 %L1,%L1,0" \ - : "=h" (__mod), "=r" (__quot), "=&r" (__r0) \ - : "r" (n), "Jr" (base)); \ - \ - (n) = __quot; \ - __mod; }) - -#endif - #endif /* _ASM_DIV64_H */ diff --git a/include/asm-mips/dma.h b/include/asm-mips/dma.h index 54e938ef42eb..eb21f821b946 100644 --- a/include/asm-mips/dma.h +++ b/include/asm-mips/dma.h @@ -1,4 +1,4 @@ -/* $Id: dma.h,v 1.6 1999/12/30 14:22:47 raiko Exp $ +/* * linux/include/asm/dma.h: Defines for using and allocating dma channels. * Written by Hennus Bergman, 1992. * High DMA channel support & info by Hannu Savolainen @@ -43,7 +43,7 @@ * - page registers for 5-7 don't use data bit 0, represent 128K pages * - page registers for 0-3 use bit 0, represent 64K pages * - * DMA transfers are limited to the lower 16MB of _physical_ memory. + * DMA transfers are limited to the lower 16MB of _physical_ memory. * Note that addresses loaded into registers must be _physical_ addresses, * not logical addresses (which may differ if paging is active). * @@ -53,7 +53,7 @@ * | ... | | ... | | ... | * | ... | | ... | | ... | * | ... | | ... | | ... | - * P7 ... P0 A7 ... A0 A7 ... A0 + * P7 ... P0 A7 ... A0 A7 ... A0 * | Page | Addr MSB | Addr LSB | (DMA registers) * * Address mapping for channels 5-7: @@ -62,7 +62,7 @@ * | ... | \ \ ... \ \ \ ... \ \ * | ... | \ \ ... \ \ \ ... \ (not used) * | ... | \ \ ... \ \ \ ... \ - * P7 ... P1 (0) A7 A6 ... A0 A7 A6 ... A0 + * P7 ... P1 (0) A7 A6 ... A0 A7 A6 ... A0 * | Page | Addr MSB | Addr LSB | (DMA registers) * * Again, channels 5-7 transfer _physical_ words (16 bits), so addresses @@ -71,7 +71,7 @@ * * Transfer count (_not # bytes_) is limited to 64K, represented as actual * count - 1 : 64K => 0xFFFF, 1 => 0x0000. Thus, count is always 1 or more, - * and up to 128K bytes may be transferred on channels 5-7 in one operation. + * and up to 128K bytes may be transferred on channels 5-7 in one operation. * */ @@ -83,7 +83,13 @@ * Deskstations or Acer PICA but not the much more versatile DMA logic used * for the local devices on Acer PICA or Magnums. */ +#ifdef CONFIG_SGI_IP22 +/* Horrible hack to have a correct DMA window on IP22 */ +#include <asm/sgi/mc.h> +#define MAX_DMA_ADDRESS (PAGE_OFFSET + SGIMC_SEG0_BADDR + 0x01000000) +#else #define MAX_DMA_ADDRESS (PAGE_OFFSET + 0x01000000) +#endif /* 8237 DMA controllers */ #define IO_DMA1_BASE 0x00 /* 8 bit slave DMA, channels 0..3 */ @@ -142,6 +148,7 @@ #define DMA_MODE_WRITE 0x48 /* memory to I/O, no autoinit, increment, single mode */ #define DMA_MODE_CASCADE 0xC0 /* pass thru DREQ->HRQ, DACK<-HLDA only */ +#define DMA_AUTOINIT 0x10 extern spinlock_t dma_spin_lock; @@ -247,7 +254,7 @@ static __inline__ void set_dma_addr(unsigned int dmanr, unsigned int a) } -/* Set transfer size (max 64k for DMA1..3, 128k for DMA5..7) for +/* Set transfer size (max 64k for DMA0..3, 128k for DMA5..7) for * a specific DMA channel. * You must ensure the parameters are valid. * NOTE: from a manual: "the number of transfers is one more @@ -286,7 +293,7 @@ static __inline__ int get_dma_residue(unsigned int dmanr) count = 1 + dma_inb(io_port); count += dma_inb(io_port) << 8; - + return (dmanr<=3)? count : (count<<1); } diff --git a/include/asm-mips/ds1286.h b/include/asm-mips/ds1286.h index 92686b110dc9..56af9b10d01b 100644 --- a/include/asm-mips/ds1286.h +++ b/include/asm-mips/ds1286.h @@ -1,5 +1,4 @@ -/* $Id: ds1286.h,v 1.1 1998/07/10 01:14:55 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. diff --git a/include/asm-mips/elf.h b/include/asm-mips/elf.h index 47807ff567e3..2d5e51c1df12 100644 --- a/include/asm-mips/elf.h +++ b/include/asm-mips/elf.h @@ -6,13 +6,32 @@ #ifndef __ASM_ELF_H #define __ASM_ELF_H +/* ELF header e_flags defines. */ +/* MIPS architecture level. */ +#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ +#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ +#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ +#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ +#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ +#define EF_MIPS_ARCH_32 0x50000000 /* MIPS32 code. */ +#define EF_MIPS_ARCH_64 0x60000000 /* MIPS64 code. */ + +/* The ABI of a file. */ +#define EF_MIPS_ABI_O32 0x00001000 /* O32 ABI. */ +#define EF_MIPS_ABI_O64 0x00002000 /* O32 extended for 64 bit. */ + #define PT_MIPS_REGINFO 0x70000000 +#define PT_MIPS_OPTIONS 0x70000001 /* Flags in the e_flags field of the header */ -#define EF_MIPS_NOREORDER 0x00000001 -#define EF_MIPS_PIC 0x00000002 -#define EF_MIPS_CPIC 0x00000004 -#define EF_MIPS_ARCH 0xf0000000 +#define EF_MIPS_NOREORDER 0x00000001 +#define EF_MIPS_PIC 0x00000002 +#define EF_MIPS_CPIC 0x00000004 +#define EF_MIPS_ABI2 0x00000020 +#define EF_MIPS_OPTIONS_FIRST 0x00000080 +#define EF_MIPS_32BITMODE 0x00000100 +#define EF_MIPS_ABI 0x0000f000 +#define EF_MIPS_ARCH 0xf0000000 #define DT_MIPS_RLD_VERSION 0x70000001 #define DT_MIPS_TIME_STAMP 0x70000002 @@ -102,8 +121,7 @@ typedef double elf_fpreg_t; typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG]; /* - * This is used to ensure we don't load something for the wrong architecture - * and also rejects IRIX binaries. + * This is used to ensure we don't load something for the wrong architecture. */ #define elf_check_arch(hdr) \ ({ \ @@ -112,7 +130,12 @@ typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG]; \ if (__h->e_machine != EM_MIPS) \ __res = 0; \ - if (__h->e_flags & EF_MIPS_ARCH) \ + if (__h->e_ident[EI_CLASS] != ELFCLASS32) \ + __res = 0; \ + if ((__h->e_flags & EF_MIPS_ABI2) != 0) \ + __res = 0; \ + if (((__h->e_flags & EF_MIPS_ABI) != 0) && \ + ((__h->e_flags & EF_MIPS_ABI) != EF_MIPS_ABI_O32)) \ __res = 0; \ \ __res; \ diff --git a/include/asm-mips/errno.h b/include/asm-mips/errno.h index bcb808005a31..35d47a882801 100644 --- a/include/asm-mips/errno.h +++ b/include/asm-mips/errno.h @@ -3,7 +3,7 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 1995, 1999, 2001 by Ralf Baechle + * Copyright (C) 1995, 1999, 2001, 2002 by Ralf Baechle */ #ifndef _ASM_ERRNO_H #define _ASM_ERRNO_H diff --git a/include/asm-mips/fcntl.h b/include/asm-mips/fcntl.h index f7a6ada7fffb..29003d919e0b 100644 --- a/include/asm-mips/fcntl.h +++ b/include/asm-mips/fcntl.h @@ -22,8 +22,8 @@ #define O_EXCL 0x0400 /* not fcntl */ #define O_NOCTTY 0x0800 /* not fcntl */ #define FASYNC 0x1000 /* fcntl, for BSD compatibility */ -#define O_LARGEFILE 0x2000 /* allow large file opens - currently ignored */ -#define O_DIRECT 0x8000 /* direct disk access hint - currently ignored */ +#define O_LARGEFILE 0x2000 /* allow large file opens */ +#define O_DIRECT 0x8000 /* direct disk access hint */ #define O_DIRECTORY 0x10000 /* must be a directory */ #define O_NOFOLLOW 0x20000 /* don't follow links */ @@ -65,7 +65,7 @@ /* operations for bsd flock(), also used by the kernel implementation */ #define LOCK_SH 1 /* shared lock */ #define LOCK_EX 2 /* exclusive lock */ -#define LOCK_NB 4 /* or'd with one of the above to prevent XXXXXXXXXXXXXXXXXX +#define LOCK_NB 4 /* or'd with one of the above to prevent blocking */ #define LOCK_UN 8 /* remove lock */ @@ -74,14 +74,21 @@ #define LOCK_WRITE 128 /* ... Which allows concurrent write operations */ #define LOCK_RW 192 /* ... Which allows concurrent read & write ops */ +/* + * The flavours of struct flock. "struct flock" is the ABI compliant + * variant. Finally struct flock64 is the LFS variant of struct flock. As + * a historic accident and inconsistence with the ABI definition it doesn't + * contain all the same fields as struct flock. + */ + typedef struct flock { - short l_type; - short l_whence; + short l_type; + short l_whence; __kernel_off_t l_start; __kernel_off_t l_len; - long l_sysid; /* ABI junk, unused on Linux */ + long l_sysid; __kernel_pid_t l_pid; - long pad[4]; /* ABI junk, unused on Linux */ + long pad[4]; } flock_t; typedef struct flock64 { diff --git a/include/asm-mips/fixmap.h b/include/asm-mips/fixmap.h new file mode 100644 index 000000000000..b86291d8ae80 --- /dev/null +++ b/include/asm-mips/fixmap.h @@ -0,0 +1,111 @@ +/* + * fixmap.h: compile-time virtual memory allocation + * + * 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) 1998 Ingo Molnar + * + * Support of BIGMEM added by Gerhard Wichert, Siemens AG, July 1999 + */ + +#ifndef _ASM_FIXMAP_H +#define _ASM_FIXMAP_H + +#include <linux/config.h> +#include <linux/kernel.h> +#include <asm/page.h> +#ifdef CONFIG_HIGHMEM +#include <linux/threads.h> +#include <asm/kmap_types.h> +#endif + +/* + * Here we define all the compile-time 'special' virtual + * addresses. The point is to have a constant address at + * compile time, but to set the physical address only + * in the boot process. We allocate these special addresses + * from the end of virtual memory (0xfffff000) backwards. + * Also this lets us do fail-safe vmalloc(), we + * can guarantee that these special addresses and + * vmalloc()-ed addresses never overlap. + * + * these 'compile-time allocated' memory buffers are + * fixed-size 4k pages. (or larger if used with an increment + * 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 + * task switches. + */ + +/* + * on UP currently we will have no trace of the fixmap mechanizm, + * no page table allocations, etc. This might change in the + * future, say framebuffers for the console driver(s) could be + * fix-mapped? + */ +enum fixed_addresses { +#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 + __end_of_fixed_addresses +}; + +extern void __set_fixmap (enum fixed_addresses idx, + unsigned long phys, pgprot_t flags); + +#define set_fixmap(idx, phys) \ + __set_fixmap(idx, phys, PAGE_KERNEL) +/* + * Some hardware wants to get fixmapped without caching. + */ +#define set_fixmap_nocache(idx, phys) \ + __set_fixmap(idx, phys, PAGE_KERNEL_NOCACHE) +/* + * used by vmalloc.c. + * + * Leave one empty page between vmalloc'ed areas and + * the start of the fixmap, and leave one page empty + * at the top of mem.. + */ +#define FIXADDR_TOP (0xffffe000UL) +#define FIXADDR_SIZE (__end_of_fixed_addresses << PAGE_SHIFT) +#define FIXADDR_START (FIXADDR_TOP - FIXADDR_SIZE) + +#define __fix_to_virt(x) (FIXADDR_TOP - ((x) << PAGE_SHIFT)) +#define __virt_to_fix(x) ((FIXADDR_TOP - ((x)&PAGE_MASK)) >> PAGE_SHIFT) + +extern void __this_fixmap_does_not_exist(void); + +/* + * 'index to address' translation. If anyone tries to use the idx + * directly without tranlation, we catch the bug with a NULL-deference + * kernel oops. Illegal ranges of incoming indices are caught too. + */ +static inline unsigned long fix_to_virt(const unsigned int idx) +{ + /* + * this branch gets completely eliminated after inlining, + * except when someone tries to use fixaddr indices in an + * illegal way. (such as mixing up address types or using + * out-of-range indices). + * + * If it doesn't get removed, the linker will complain + * loudly with a reasonably clear error message.. + */ + if (idx >= __end_of_fixed_addresses) + __this_fixmap_does_not_exist(); + + return __fix_to_virt(idx); +} + +static inline unsigned long virt_to_fix(const unsigned long vaddr) +{ + BUG_ON(vaddr >= FIXADDR_TOP || vaddr < FIXADDR_START); + return __virt_to_fix(vaddr); +} + +#endif diff --git a/include/asm-mips/floppy.h b/include/asm-mips/floppy.h index 32b37c84eaa8..29b7c27b13a7 100644 --- a/include/asm-mips/floppy.h +++ b/include/asm-mips/floppy.h @@ -10,10 +10,6 @@ #ifndef _ASM_FLOPPY_H #define _ASM_FLOPPY_H -#include <asm/bootinfo.h> -#include <asm/jazz.h> -#include <asm/jazzdma.h> - struct fd_ops { unsigned char (*fd_inb)(unsigned int port); void (*fd_outb)(unsigned char value, unsigned int port); @@ -50,7 +46,7 @@ extern struct fd_ops *fd_ops; #define fd_clear_dma_ff() fd_ops->fd_clear_dma_ff(FLOPPY_DMA) #define fd_set_dma_mode(mode) fd_ops->fd_set_dma_mode(FLOPPY_DMA, mode) #define fd_set_dma_addr(addr) fd_ops->fd_set_dma_addr(FLOPPY_DMA, \ - virt_to_bus(addr)) + isa_virt_to_bus(addr)) #define fd_set_dma_count(count) fd_ops->fd_set_dma_count(FLOPPY_DMA,count) #define fd_get_dma_residue() fd_ops->fd_get_dma_residue(FLOPPY_DMA) @@ -63,7 +59,8 @@ extern struct fd_ops *fd_ops; #define fd_dma_mem_alloc(size) fd_ops->fd_dma_mem_alloc(size) #define fd_dma_mem_free(mem,size) fd_ops->fd_dma_mem_free(mem,size) #define fd_drive_type(n) fd_ops->fd_drive_type(n) -#define fd_cacheflush(addr,size) dma_cache_wback_inv(addr,size) +#define fd_cacheflush(addr,size) \ + dma_cache_wback_inv((unsigned long)(addr),(size)) #define MAX_BUFFER_SECTORS 24 diff --git a/include/asm-mips/fp.h b/include/asm-mips/fp.h deleted file mode 100644 index 30c17dc83e44..000000000000 --- a/include/asm-mips/fp.h +++ /dev/null @@ -1,34 +0,0 @@ -/* $Id: fp.h,v 1.1 1998/07/16 19:10: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 - * for more details. - * - * Copyright (C) 1998 by Ralf Baechle - */ - -/* - * Activate and deactive the floatingpoint accelerator. - */ -#define enable_cp1() \ - __asm__ __volatile__( \ - ".set\tpush\n\t" \ - ".set\tnoat\n\t" \ - ".set\treorder\n\t" \ - "mfc0\t$1,$12\n\t" \ - "or\t$1,%0\n\t" \ - "mtc0\t$1,$12\n\t" \ - ".set\tpop" \ - : : "r" (ST0_CU1)); - -#define disable_cp1() \ - __asm__ __volatile__( \ - ".set\tpush\n\t" \ - ".set\tnoat\n\t" \ - ".set\treorder\n\t" \ - "mfc0\t$1,$12\n\t" \ - "or\t$1,%0\n\t" \ - "xor\t$1,%0\n\t" \ - "mtc0\t$1,$12\n\t" \ - ".set\tpop" \ - : : "r" (ST0_CU1)); diff --git a/include/asm-mips/fpu.h b/include/asm-mips/fpu.h new file mode 100644 index 000000000000..87b5e51b1e52 --- /dev/null +++ b/include/asm-mips/fpu.h @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2002 MontaVista Software Inc. + * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#ifndef _ASM_FPU_H +#define _ASM_FPU_H + +#include <linux/config.h> +#include <linux/sched.h> +#include <linux/thread_info.h> + +#include <asm/mipsregs.h> +#include <asm/cpu.h> +#include <asm/bitops.h> +#include <asm/processor.h> +#include <asm/current.h> + +struct sigcontext; + +extern asmlinkage int (*save_fp_context)(struct sigcontext *sc); +extern asmlinkage int (*restore_fp_context)(struct sigcontext *sc); + +extern void fpu_emulator_init_fpu(void); +extern void _init_fpu(void); +extern void _save_fp(struct task_struct *); +extern void _restore_fp(struct task_struct *); + +#if defined(CONFIG_CPU_SB1) +#define __enable_fpu_hazard() \ +do { \ + asm(".set push \n\t" \ + ".set mips64 \n\t" \ + ".set noreorder \n\t" \ + "ssnop \n\t" \ + "bnezl $0, .+4 \n\t" \ + "ssnop \n\t" \ + ".set pop"); \ +} while (0) +#else +#define __enable_fpu_hazard() \ +do { \ + asm("nop;nop;nop;nop"); /* max. hazard */ \ +} while (0) +#endif + +#define __enable_fpu() \ +do { \ + set_c0_status(ST0_CU1); \ + __enable_fpu_hazard(); \ +} while (0) + +#define __disable_fpu() \ +do { \ + clear_c0_status(ST0_CU1); \ + /* We don't care about the c0 hazard here */ \ +} while (0) + +#define enable_fpu() \ +do { \ + if (cpu_has_fpu) \ + __enable_fpu(); \ +} while (0) + +#define disable_fpu() \ +do { \ + if (cpu_has_fpu) \ + __disable_fpu(); \ +} while (0) + + +#define clear_fpu_owner() clear_thread_flag(TIF_USEDFPU) + +static inline int is_fpu_owner(void) +{ + return cpu_has_fpu && test_thread_flag(TIF_USEDFPU); +} + +static inline void own_fpu(void) +{ + if (cpu_has_fpu) { + __enable_fpu(); + KSTK_STATUS(current) |= ST0_CU1; + set_thread_flag(TIF_USEDFPU); + } +} + +static inline void loose_fpu(void) +{ + if (cpu_has_fpu) { + KSTK_STATUS(current) &= ~ST0_CU1; + clear_thread_flag(TIF_USEDFPU); + __disable_fpu(); + } +} + +static inline void init_fpu(void) +{ + if (cpu_has_fpu) { + _init_fpu(); + } else { + fpu_emulator_init_fpu(); + } +} + +static inline void save_fp(struct task_struct *tsk) +{ + if (cpu_has_fpu) + _save_fp(tsk); +} + +static inline void restore_fp(struct task_struct *tsk) +{ + if (cpu_has_fpu) + _restore_fp(tsk); +} + +static inline unsigned long long *get_fpu_regs(struct task_struct *tsk) +{ + if (cpu_has_fpu) { + if ((tsk == current) && is_fpu_owner()) + _save_fp(current); + return (unsigned long long *)&tsk->thread.fpu.hard.fp_regs[0]; + } else { + return (unsigned long long *)tsk->thread.fpu.soft.regs; + } +} + +#endif /* _ASM_FPU_H */ diff --git a/include/asm-mips/fpu_emulator.h b/include/asm-mips/fpu_emulator.h index 70800480b87e..46972ae2b95d 100644 --- a/include/asm-mips/fpu_emulator.h +++ b/include/asm-mips/fpu_emulator.h @@ -1,15 +1,4 @@ /* - * Definitiona for the Algorithmics FPU Emulator port into MIPS Linux - */ -/************************************************************************** - * - * include/asm-mips/fpu_emulator.h - * - * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com - * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved. - * - * ######################################################################## - * * This program is free software; you can distribute it and/or modify it * under the terms of the GNU General Public License (Version 2) as * published by the Free Software Foundation. @@ -23,13 +12,16 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. * - *************************************************************************/ -/* * Further private data for which no space exists in mips_fpu_soft_struct. * This should be subsumed into the mips_fpu_soft_struct structure as * defined in processor.h as soon as the absurd wired absolute assembler * offsets become dynamic at compile time. + * + * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com + * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved. */ +#ifndef _ASM_FPU_EMULATOR_H +#define _ASM_FPU_EMULATOR_H struct mips_fpu_emulator_private { unsigned int eir; @@ -42,3 +34,5 @@ struct mips_fpu_emulator_private { unsigned int errors; } stats; }; + +#endif /* _ASM_FPU_EMULATOR_H */ diff --git a/include/asm-mips/gdb-stub.h b/include/asm-mips/gdb-stub.h index 0bf2aafb9c30..b326a5ab172d 100644 --- a/include/asm-mips/gdb-stub.h +++ b/include/asm-mips/gdb-stub.h @@ -1,5 +1,4 @@ -/* $Id: gdb-stub.h,v 1.3 1998/07/20 17:52:19 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. @@ -58,7 +57,7 @@ #define GDB_FR_REG29 ((GDB_FR_REG28) + 4) /* 29 */ #define GDB_FR_REG30 ((GDB_FR_REG29) + 4) /* 30 */ #define GDB_FR_REG31 ((GDB_FR_REG30) + 4) /* 31 */ - + /* * Saved special registers */ @@ -133,7 +132,7 @@ #define GDB_FR_SIZE ((((GDB_FR_CP0_PRID) + 4) + (PTRSIZE-1)) & ~(PTRSIZE-1)) -#ifndef _LANGUAGE_ASSEMBLY +#ifndef __ASSEMBLY__ /* * This is the same as above, but for the high-level @@ -181,7 +180,7 @@ struct gdb_regs { */ long frame_ptr; long dummy; /* unused */ - + /* * saved cp0 registers */ @@ -209,5 +208,5 @@ struct gdb_regs { void set_debug_traps(void); -#endif /* _LANGUAGE_ASSEMBLY */ +#endif /* !__ASSEMBLY__ */ #endif /* __ASM_MIPS_GDB_STUB_H */ diff --git a/include/asm-mips/gfx.h b/include/asm-mips/gfx.h index 38b0ad5d0bdf..37235e41a6fd 100644 --- a/include/asm-mips/gfx.h +++ b/include/asm-mips/gfx.h @@ -1,5 +1,4 @@ -/* $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. @@ -7,7 +6,7 @@ * This is the user-visible SGI GFX interface. * * This must be used verbatim into the GNU libc. It does not include - * any kernel-only bits on it. + * any kernel-only bits on it. * * miguel@nuclecu.unam.mx */ diff --git a/include/asm-mips/gt64120.h b/include/asm-mips/gt64120.h index 5d78b6126f40..9cf9edcdd54d 100644 --- a/include/asm-mips/gt64120.h +++ b/include/asm-mips/gt64120.h @@ -60,7 +60,7 @@ #define GT_PCI1M0REMAP_OFS 0x110 #define GT_PCI1M1REMAP_OFS 0x118 -#define GT_SCS0LD_OFS 0x400 +#define GT_SCS0LD_OFS 0x400 #define GT_SCS0HD_OFS 0x404 #define GT_SCS1LD_OFS 0x408 #define GT_SCS1HD_OFS 0x40c @@ -324,7 +324,7 @@ #define GT_PCI0_BARE_SWSCS32DIS_SHF 1 #define GT_PCI0_BARE_SWSCS32DIS_MSK (MSK(1) << GT_PCI0_BARE_SWSCS32DIS_SHF) #define GT_PCI0_BARE_SWSCS32DIS_BIT GT_PCI0_BARE_SWSCS32DIS_MSK - + #define GT_PCI0_BARE_SWSCS10DIS_SHF 2 #define GT_PCI0_BARE_SWSCS10DIS_MSK (MSK(1) << GT_PCI0_BARE_SWSCS10DIS_SHF) #define GT_PCI0_BARE_SWSCS10DIS_BIT GT_PCI0_BARE_SWSCS10DIS_MSK diff --git a/include/asm-mips/hardirq.h b/include/asm-mips/hardirq.h index e66f39b59207..530c7fc80eb4 100644 --- a/include/asm-mips/hardirq.h +++ b/include/asm-mips/hardirq.h @@ -13,12 +13,9 @@ #include <linux/config.h> #include <linux/threads.h> #include <linux/irq.h> -#include <linux/spinlock.h> typedef struct { unsigned int __softirq_pending; - unsigned int __local_irq_count; - unsigned int __local_bh_count; unsigned int __syscall_count; struct task_struct * __ksoftirqd_task; /* waitqueue is too large */ } ____cacheline_aligned irq_cpustat_t; @@ -26,72 +23,83 @@ typedef struct { #include <linux/irq_cpustat.h> /* Standard mappings for irq_cpustat_t above */ /* - * Are we in an interrupt context? Either doing bottom half - * or hardware interrupt processing? + * We put the hardirq and softirq counter into the preemption + * counter. The bitmask has the following meaning: + * + * - bits 0-7 are the preemption count (max preemption depth: 256) + * - bits 8-15 are the softirq count (max # of softirqs: 256) + * - bits 16-23 are the hardirq count (max # of hardirqs: 256) + * + * - ( bit 26 is the PREEMPT_ACTIVE flag. ) + * + * PREEMPT_MASK: 0x000000ff + * SOFTIRQ_MASK: 0x0000ff00 + * HARDIRQ_MASK: 0x00ff0000 */ -#define in_interrupt() ({ int __cpu = smp_processor_id(); \ - (local_irq_count(__cpu) + local_bh_count(__cpu) != 0); }) -#define in_irq() (local_irq_count(smp_processor_id()) != 0) - -#ifndef CONFIG_SMP - -#define hardirq_trylock(cpu) (local_irq_count(cpu) == 0) -#define hardirq_endlock(cpu) do { } while (0) - -#define irq_enter(cpu, irq) (local_irq_count(cpu)++) -#define irq_exit(cpu, irq) (local_irq_count(cpu)--) - -#define synchronize_irq() barrier(); - -#else -#include <asm/atomic.h> -#include <linux/spinlock.h> -#include <asm/smp.h> +#define PREEMPT_BITS 8 +#define SOFTIRQ_BITS 8 +#define HARDIRQ_BITS 8 -extern int global_irq_holder; -extern spinlock_t global_irq_lock; +#define PREEMPT_SHIFT 0 +#define SOFTIRQ_SHIFT (PREEMPT_SHIFT + PREEMPT_BITS) +#define HARDIRQ_SHIFT (SOFTIRQ_SHIFT + SOFTIRQ_BITS) -static inline int irqs_running (void) -{ - int i; +#define __MASK(x) ((1UL << (x))-1) - for (i = 0; i < smp_num_cpus; i++) - if (local_irq_count(i)) - return 1; - return 0; -} +#define PREEMPT_MASK (__MASK(PREEMPT_BITS) << PREEMPT_SHIFT) +#define HARDIRQ_MASK (__MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT) +#define SOFTIRQ_MASK (__MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT) -static inline void release_irqlock(int cpu) -{ - /* if we didn't own the irq lock, just ignore.. */ - if (global_irq_holder == cpu) { - global_irq_holder = NO_PROC_ID; - spin_unlock(&global_irq_lock); - } -} +#define hardirq_count() (preempt_count() & HARDIRQ_MASK) +#define softirq_count() (preempt_count() & SOFTIRQ_MASK) +#define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK)) -static inline int hardirq_trylock(int cpu) -{ - return !local_irq_count(cpu) && !spin_is_locked(&global_irq_lock); -} +#define PREEMPT_OFFSET (1UL << PREEMPT_SHIFT) +#define SOFTIRQ_OFFSET (1UL << SOFTIRQ_SHIFT) +#define HARDIRQ_OFFSET (1UL << HARDIRQ_SHIFT) -#define hardirq_endlock(cpu) do { } while (0) +/* + * The hardirq mask has to be large enough to have + * space for potentially all IRQ sources in the system + * nesting on a single CPU: + */ +#if (1 << HARDIRQ_BITS) < NR_IRQS +# error HARDIRQ_BITS is too low! +#endif -static inline void irq_enter(int cpu, int irq) -{ - ++local_irq_count(cpu); +/* + * Are we doing bottom half or hardware interrupt processing? + * Are we in a softirq context? Interrupt context? + */ +#define in_irq() (hardirq_count()) +#define in_softirq() (softirq_count()) +#define in_interrupt() (irq_count()) - while (spin_is_locked(&global_irq_lock)) - barrier(); -} +#define hardirq_trylock() (!in_interrupt()) +#define hardirq_endlock() do { } while (0) -static inline void irq_exit(int cpu, int irq) -{ - --local_irq_count(cpu); -} +#define irq_enter() (preempt_count() += HARDIRQ_OFFSET) -extern void synchronize_irq(void); +#if CONFIG_PREEMPT +# define in_atomic() (preempt_count() != kernel_locked()) +# define IRQ_EXIT_OFFSET (HARDIRQ_OFFSET-1) +#else +# define in_atomic() (preempt_count() != 0) +# define IRQ_EXIT_OFFSET HARDIRQ_OFFSET +#endif +#define irq_exit() \ +do { \ + preempt_count() -= IRQ_EXIT_OFFSET; \ + if (!in_interrupt() && softirq_pending(smp_processor_id())) \ + do_softirq(); \ + preempt_enable_no_resched(); \ +} while (0) +#ifndef CONFIG_SMP +# define synchronize_irq(irq) barrier() +#else + extern void synchronize_irq(unsigned int irq); #endif /* CONFIG_SMP */ + #endif /* _ASM_HARDIRQ_H */ diff --git a/include/asm-mips/highmem.h b/include/asm-mips/highmem.h new file mode 100644 index 000000000000..36f5da14b1a1 --- /dev/null +++ b/include/asm-mips/highmem.h @@ -0,0 +1,59 @@ +/* + * 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 addresses. + * + * 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 Terabyte 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 + +#ifdef __KERNEL__ + +#include <linux/config.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <asm/kmap_types.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 pte_t *pkmap_page_table; + +/* + * Right now we initialize only a single pte table. It can be extended + * easily, subsequent pte tables have to be allocated in one physical + * chunk of RAM. + */ +#define PKMAP_BASE (0xfe000000UL) +#define LAST_PKMAP 1024 +#define LAST_PKMAP_MASK (LAST_PKMAP-1) +#define PKMAP_NR(virt) ((virt-PKMAP_BASE) >> PAGE_SHIFT) +#define PKMAP_ADDR(nr) (PKMAP_BASE + ((nr) << PAGE_SHIFT)) + +extern void * kmap_high(struct page *page); +extern void kunmap_high(struct page *page); + +extern void *kmap(struct page *page); +extern void kunmap(struct page *page); +extern void *kmap_atomic(struct page *page, enum km_type type); +extern void kunmap_atomic(void *kvaddr, enum km_type type); +extern struct page *kmap_atomic_to_page(void *ptr); + +#endif /* __KERNEL__ */ + +#endif /* _ASM_HIGHMEM_H */ diff --git a/include/asm-mips/hw_irq.h b/include/asm-mips/hw_irq.h index 8dfa57d9be94..8be338b9ac10 100644 --- a/include/asm-mips/hw_irq.h +++ b/include/asm-mips/hw_irq.h @@ -3,14 +3,27 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 2000, 2001 by Ralf Baechle + * Copyright (C) 2000, 2001, 2002 by Ralf Baechle */ -#ifndef _ASM_HW_IRQ_H -#define _ASM_HW_IRQ_H +#ifndef __ASM_HW_IRQ_H +#define __ASM_HW_IRQ_H + +#include <linux/profile.h> +#include <asm/atomic.h> + +extern void mask_irq(unsigned int irq); +extern void unmask_irq(unsigned int irq); +extern void disable_8259A_irq(unsigned int irq); +extern void enable_8259A_irq(unsigned int irq); +extern int i8259A_irq_pending(unsigned int irq); +extern void make_8259A_irq(unsigned int irq); +extern void init_8259A(int aeoi); + +extern atomic_t irq_err_count; /* This may not be apropriate for all machines, we'll see ... */ static inline void hw_resend_irq(struct hw_interrupt_type *h, unsigned int i) { } -#endif /* _ASM_HW_IRQ_H */ +#endif /* __ASM_HW_IRQ_H */ diff --git a/include/asm-mips/i8259.h b/include/asm-mips/i8259.h new file mode 100644 index 000000000000..f58612ec7da7 --- /dev/null +++ b/include/asm-mips/i8259.h @@ -0,0 +1,23 @@ +/* + * include/asm-mips/i8259.h + * + * i8259A interrupt definitions. + * + * Copyright (C) 2003 Maciej W. Rozycki + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#ifndef __ASM_MIPS_I8259_H +#define __ASM_MIPS_I8259_H + +#include <linux/spinlock.h> + +#include <asm/io.h> +#include <asm/system.h> + +extern void init_i8259_irqs(void); + +#endif /* __ASM_MIPS_I8259_H */ diff --git a/include/asm-mips/ide.h b/include/asm-mips/ide.h index 33cd055aea35..f377adcfb1a3 100644 --- a/include/asm-mips/ide.h +++ b/include/asm-mips/ide.h @@ -14,6 +14,7 @@ #ifdef __KERNEL__ #include <linux/config.h> +#include <asm/byteorder.h> #include <asm/io.h> #ifndef MAX_HWIFS @@ -58,11 +59,16 @@ static __inline__ void ide_init_default_hwifs(void) 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); + ide_register_hw(&hw, NULL); } #endif } +#define __ide_mm_insw ide_insw +#define __ide_mm_insl ide_insl +#define __ide_mm_outsw ide_outsw +#define __ide_mm_outsl ide_outsl + #endif /* __KERNEL__ */ #endif /* __ASM_IDE_H */ diff --git a/include/asm-mips/inventory.h b/include/asm-mips/inventory.h index 7ea13dc13c5f..4cd36fe98173 100644 --- a/include/asm-mips/inventory.h +++ b/include/asm-mips/inventory.h @@ -1,12 +1,9 @@ /* - * $Id:$ + * Miguel de Icaza */ -#ifndef __ASM_MIPS_INVENTORY_H -#define __ASM_MIPS_INVENTORY_H +#ifndef __ASM_INVENTORY_H +#define __ASM_INVENTORY_H -#include <linux/config.h> - -#ifdef CONFIG_BINFMT_IRIX typedef struct inventory_s { struct inventory_s *inv_next; int inv_class; @@ -19,10 +16,5 @@ typedef struct inventory_s { extern int inventory_items; void add_to_inventory (int class, int type, int controller, int unit, int state); int dump_inventory_to_user (void *userbuf, int size); -void init_inventory (void); -#else -#define add_to_inventory(c,t,o,u,s) -#define init_inventory() -#endif -#endif /* defined(CONFIG_BINFMT_IRIX) */ +#endif /* __ASM_INVENTORY_H */ diff --git a/include/asm-mips/io.h b/include/asm-mips/io.h index 28a2bc81e4cc..f1f748b9ac5f 100644 --- a/include/asm-mips/io.h +++ b/include/asm-mips/io.h @@ -12,9 +12,22 @@ #define _ASM_IO_H #include <linux/config.h> -#include <linux/pagemap.h> +#include <linux/types.h> + #include <asm/addrspace.h> +#include <asm/pgtable-bits.h> #include <asm/byteorder.h> +#include <asm/mipsregs.h> + +#ifdef CONFIG_SGI_IP27 +extern unsigned long bus_to_baddr[256]; + +#define bus_to_baddr(bus, addr) (bus_to_baddr[(bus)->number] + (addr)) +#define baddr_to_bus(bus, addr) ((addr) - bus_to_baddr[(bus)->number]) +#else +#define bus_to_baddr(bus, addr) (addr) +#define baddr_to_bus(bus, addr) (addr) +#endif /* * Slowdown I/O port space accesses for antique hardware. @@ -28,7 +41,13 @@ #if defined(CONFIG_SWAP_IO_SPACE) && defined(__MIPSEB__) #define __ioswab8(x) (x) +#ifdef CONFIG_SGI_IP22 +/* IP22 seems braindead enough to swap 16bits values in hardware, but + not 32bits. Go figure... Can't tell without documentation. */ +#define __ioswab16(x) (x) +#else #define __ioswab16(x) swab16(x) +#endif #define __ioswab32(x) swab32(x) #else @@ -40,27 +59,17 @@ #endif /* - * This file contains the definitions for the MIPS counterpart of the - * x86 in/out instructions. This heap of macros and C results in much - * better code than the approach of doing it in plain C. The macros - * result in code that is to fast for certain hardware. On the other - * side the performance of the string functions should be improved for - * sake of certain devices like EIDE disks that do highspeed polled I/O. - * - * Ralf - * - * This file contains the definitions for the x86 IO instructions - * inb/inw/inl/outb/outw/outl and the "string versions" of the same - * (insb/insw/insl/outsb/outsw/outsl). You can also use "pausing" - * versions of the single-IO instructions (inb_p/inw_p/..). + * <Bacchus> Historically I wrote this stuff the same way as Linus did + * because I was young and clueless. And now it's so jucky that I + * don't want to put my eyes on it again to get rid of it :-) * - * This file is not meant to be obfuscating: it's just complicated - * to (a) handle it all in a way that makes gcc able to optimize it - * as well as possible and (b) trying to avoid writing the same thing - * over and over again with slight variations and possibly making a - * mistake somewhere. + * I'll do it then, because this code offends both me and my compiler + * - particularly the bits of inline asm which end up doing crap like + * 'lb $2,$2($5)' -- dwmw2 */ +#define IO_SPACE_LIMIT 0xffff + /* * On MIPS I/O ports are memory mapped, so we access them using normal * load/store instructions. mips_io_port_base is the virtual address to @@ -69,7 +78,10 @@ * instruction, so the lower 16 bits must be zero. Should be true on * on any sane architecture; generic code does not use this assumption. */ -extern unsigned long mips_io_port_base; +extern const unsigned long mips_io_port_base; + +#define set_io_port_base(base) \ + do { * (unsigned long *) &mips_io_port_base = (base); } while (0) /* * Thanks to James van Artsdalen for a better timing-fix than @@ -99,49 +111,116 @@ extern unsigned long mips_io_port_base; #endif /* - * Change virtual addresses to physical addresses and vv. - * These are trivial on the 1:1 Linux/MIPS mapping + * virt_to_phys - map virtual addresses to physical + * @address: address to remap + * + * The returned physical address is the physical (CPU) mapping for + * the memory address given. It is only valid to use this function on + * addresses directly mapped or allocated via kmalloc. + * + * This function does not give bus mappings for DMA transfers. In + * almost all conceivable cases a device driver should not be using + * this function */ -extern inline unsigned long virt_to_phys(volatile void * address) +static inline unsigned long virt_to_phys(volatile void * address) { return PHYSADDR(address); } -extern inline void * phys_to_virt(unsigned long address) +/* + * phys_to_virt - map physical address to virtual + * @address: address to remap + * + * The returned virtual address is a current CPU mapping for + * the memory address given. It is only valid to use this function on + * addresses that have a kernel mapping + * + * This function does not handle bus mappings for DMA transfers. In + * almost all conceivable cases a device driver should not be using + * this function + */ +static inline void * phys_to_virt(unsigned long address) { return (void *)KSEG0ADDR(address); } /* - * IO bus memory addresses are also 1:1 with the physical address + * ISA I/O bus memory addresses are 1:1 with the physical address. */ -extern inline unsigned long virt_to_bus(volatile void * address) +static inline unsigned long isa_virt_to_bus(volatile void * address) { return PHYSADDR(address); } -extern inline void * bus_to_virt(unsigned long address) +static inline void * isa_bus_to_virt(unsigned long address) { return (void *)KSEG0ADDR(address); } +#define isa_page_to_bus page_to_phys + +/* + * However PCI ones are not necessarily 1:1 and therefore these interfaces + * are forbidden in portable PCI drivers. + * + * Allow them for x86 for legacy drivers, though. + */ +#define virt_to_bus virt_to_phys +#define bus_to_virt phys_to_virt + /* * isa_slot_offset is the address where E(ISA) busaddress 0 is mapped * for the processor. */ extern unsigned long isa_slot_offset; -extern void * __ioremap(unsigned long offset, unsigned long size, unsigned long flags); +/* + * Change "struct page" to physical address. + */ +#define page_to_phys(page) ((dma_addr_t)page_to_pfn(page) << PAGE_SHIFT) -extern inline void *ioremap(unsigned long offset, unsigned long size) -{ - return __ioremap(offset, size, _CACHE_UNCACHED); -} +extern void * __ioremap(phys_t offset, phys_t size, unsigned long flags); -extern inline void *ioremap_nocache(unsigned long offset, unsigned long size) -{ - return __ioremap(offset, size, _CACHE_UNCACHED); -} + +/* + * ioremap - map bus memory into CPU space + * @offset: bus address of the memory + * @size: size of the resource to map + * + * ioremap performs a platform specific sequence of operations to + * make bus memory CPU accessible via the readb/readw/readl/writeb/ + * writew/writel functions and the other mmio helpers. The returned + * address is not guaranteed to be usable directly as a virtual + * address. + */ +#define ioremap(offset, size) \ + __ioremap((offset), (size), _CACHE_UNCACHED) + +/* + * ioremap_nocache - map bus memory into CPU space + * @offset: bus address of the memory + * @size: size of the resource to map + * + * ioremap_nocache performs a platform specific sequence of operations to + * make bus memory CPU accessible via the readb/readw/readl/writeb/ + * writew/writel functions and the other mmio helpers. The returned + * address is not guaranteed to be usable directly as a virtual + * address. + * + * This version of ioremap ensures that the memory is marked uncachable + * on the CPU as well as honouring existing caching rules from things like + * the PCI bus. Note that there are other caches and buffers on many + * busses. In paticular driver authors should read up on PCI writes + * + * It's useful if some control registers are in such an area and + * write combining or read caching is not desirable: + */ +#define ioremap_nocache(offset, size) \ + __ioremap((offset), (size), _CACHE_UNCACHED) +#define ioremap_cacheable_cow(offset, size) \ + __ioremap((offset), (size), _CACHE_CACHABLE_COW) +#define ioremap_uncached_accelerated(offset, size) \ + __ioremap((offset), (size), _CACHE_UNCACHED_ACCELERATED) extern void iounmap(void *addr); @@ -150,26 +229,26 @@ extern void iounmap(void *addr); * 24-31 on SNI. * XXX more SNI hacks. */ -#define readb(addr) (*(volatile unsigned char *)(addr)) -#define readw(addr) __ioswab16((*(volatile unsigned short *)(addr))) -#define readl(addr) __ioswab32((*(volatile unsigned int *)(addr))) -#define __raw_readb readb -#define __raw_readw readw -#define __raw_readl readl - -#define writeb(b,addr) (*(volatile unsigned char *)(addr)) = (b) -#define writew(b,addr) (*(volatile unsigned short *)(addr)) = (__ioswab16(b)) -#define writel(b,addr) (*(volatile unsigned int *)(addr)) = (__ioswab32(b)) -#define __raw_writeb writeb -#define __raw_writew writew -#define __raw_writel writel +#define readb(addr) (*(volatile unsigned char *)(addr)) +#define readw(addr) __ioswab16((*(volatile unsigned short *)(addr))) +#define readl(addr) __ioswab32((*(volatile unsigned int *)(addr))) + +#define __raw_readb(addr) (*(volatile unsigned char *)(addr)) +#define __raw_readw(addr) (*(volatile unsigned short *)(addr)) +#define __raw_readl(addr) (*(volatile unsigned int *)(addr)) + +#define writeb(b,addr) ((*(volatile unsigned char *)(addr)) = (__ioswab8(b))) +#define writew(b,addr) ((*(volatile unsigned short *)(addr)) = (__ioswab16(b))) +#define writel(b,addr) ((*(volatile unsigned int *)(addr)) = (__ioswab32(b))) + +#define __raw_writeb(b,addr) ((*(volatile unsigned char *)(addr)) = (b)) +#define __raw_writew(w,addr) ((*(volatile unsigned short *)(addr)) = (w)) +#define __raw_writel(l,addr) ((*(volatile unsigned int *)(addr)) = (l)) #define memset_io(a,b,c) memset((void *)(a),(b),(c)) #define memcpy_fromio(a,b,c) memcpy((a),(void *)(b),(c)) #define memcpy_toio(a,b,c) memcpy((void *)(a),(b),(c)) -/* 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 @@ -178,18 +257,17 @@ extern void iounmap(void *addr); * 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) readw(a) -#define isa_readl(a) readl(a) -#define isa_writeb(b,a) writeb(b,a) -#define isa_writew(w,a) writew(w,a) -#define isa_writel(l,a) writel(l,a) - -#define isa_memset_io(a,b,c) memset_io((a),(b),(c)) -#define isa_memcpy_fromio(a,b,c) memcpy_fromio((a),(b),(c)) -#define isa_memcpy_toio(a,b,c) memcpy_toio((a),(b),(c)) +#define __ISA_IO_base ((char *)(isa_slot_offset)) + +#define isa_readb(a) readb(__ISA_IO_base + (a)) +#define isa_readw(a) readw(__ISA_IO_base + (a)) +#define isa_readl(a) readl(__ISA_IO_base + (a)) +#define isa_writeb(b,a) writeb(b,__ISA_IO_base + (a)) +#define isa_writew(w,a) writew(w,__ISA_IO_base + (a)) +#define isa_writel(l,a) writel(l,__ISA_IO_base + (a)) +#define isa_memset_io(a,b,c) memset_io(__ISA_IO_base + (a),(b),(c)) +#define isa_memcpy_fromio(a,b,c) memcpy_fromio((a),__ISA_IO_base + (b),(c)) +#define isa_memcpy_toio(a,b,c) memcpy_toio(__ISA_IO_base + (a),(b),(c)) /* * We don't have csum_partial_copy_fromio() yet, so we cheat here and @@ -198,6 +276,16 @@ extern void iounmap(void *addr); #define eth_io_copy_and_sum(skb,src,len,unused) memcpy_fromio((skb)->data,(src),(len)) #define isa_eth_io_copy_and_sum(a,b,c,d) eth_copy_and_sum((a),(b),(c),(d)) +/* + * check_signature - find BIOS signatures + * @io_addr: mmio address to check + * @signature: signature block + * @length: length of signature + * + * Perform a signature comparison with the mmio address io_addr. This + * address should have been obtained by ioremap. + * Returns 1 on a match. + */ static inline int check_signature(unsigned long io_addr, const unsigned char *signature, int length) { @@ -213,209 +301,160 @@ static inline int check_signature(unsigned long io_addr, out: return retval; } -#define isa_check_signature(io, s, l) check_signature(i,s,l) /* - * Talk about misusing macros.. + * isa_check_signature - find BIOS signatures + * @io_addr: mmio address to check + * @signature: signature block + * @length: length of signature + * + * Perform a signature comparison with the ISA mmio address io_addr. + * Returns 1 on a match. + * + * This function is deprecated. New drivers should use ioremap and + * check_signature. */ +#define isa_check_signature(io, s, l) check_signature(i,s,l) -#define __OUT1(s) \ -extern inline void __out##s(unsigned int value, unsigned int port) { -#define __OUT2(m) \ -__asm__ __volatile__ ("s" #m "\t%0,%1(%2)" +#define outb(val,port) \ +do { \ + *(volatile u8 *)(mips_io_port_base + (port)) = __ioswab8(val); \ +} while(0) + +#define outw(val,port) \ +do { \ + *(volatile u16 *)(mips_io_port_base + (port)) = __ioswab16(val); \ +} while(0) + +#define outl(val,port) \ +do { \ + *(volatile u32 *)(mips_io_port_base + (port)) = __ioswab32(val);\ +} while(0) + +#define outb_p(val,port) \ +do { \ + *(volatile u8 *)(mips_io_port_base + (port)) = __ioswab8(val); \ + SLOW_DOWN_IO; \ +} while(0) + +#define outw_p(val,port) \ +do { \ + *(volatile u16 *)(mips_io_port_base + (port)) = __ioswab16(val);\ + SLOW_DOWN_IO; \ +} while(0) + +#define outl_p(val,port) \ +do { \ + *(volatile u32 *)(mips_io_port_base + (port)) = __ioswab32(val);\ + SLOW_DOWN_IO; \ +} while(0) + +#define inb(port) __inb(port) +#define inw(port) __inw(port) +#define inl(port) __inl(port) +#define inb_p(port) __inb_p(port) +#define inw_p(port) __inw_p(port) +#define inl_p(port) __inl_p(port) + +static inline unsigned char __inb(unsigned long port) +{ + return __ioswab8(*(volatile u8 *)(mips_io_port_base + port)); +} -#define __OUT(m,s,w) \ -__OUT1(s) __OUT2(m) : : "r" (__ioswab##w(value)), "i" (0), "r" (mips_io_port_base+port)); } \ -__OUT1(s##c) __OUT2(m) : : "r" (__ioswab##w(value)), "ir" (port), "r" (mips_io_port_base)); } \ -__OUT1(s##_p) __OUT2(m) : : "r" (__ioswab##w(value)), "i" (0), "r" (mips_io_port_base+port)); \ - SLOW_DOWN_IO; } \ -__OUT1(s##c_p) __OUT2(m) : : "r" (__ioswab##w(value)), "ir" (port), "r" (mips_io_port_base)); \ - SLOW_DOWN_IO; } +static inline unsigned short __inw(unsigned long port) +{ + return __ioswab16(*(volatile u16 *)(mips_io_port_base + port)); +} -#define __IN1(t,s) \ -extern __inline__ t __in##s(unsigned int port) { t _v; +static inline unsigned int __inl(unsigned long port) +{ + return __ioswab32(*(volatile u32 *)(mips_io_port_base + port)); +} -/* - * Required nops will be inserted by the assembler - */ -#define __IN2(m) \ -__asm__ __volatile__ ("l" #m "\t%0,%1(%2)" - -#define __IN(t,m,s,w) \ -__IN1(t,s) __IN2(m) : "=r" (_v) : "i" (0), "r" (mips_io_port_base+port)); return __ioswab##w(_v); } \ -__IN1(t,s##c) __IN2(m) : "=r" (_v) : "ir" (port), "r" (mips_io_port_base)); return __ioswab##w(_v); } \ -__IN1(t,s##_p) __IN2(m) : "=r" (_v) : "i" (0), "r" (mips_io_port_base+port)); SLOW_DOWN_IO; return __ioswab##w(_v); } \ -__IN1(t,s##c_p) __IN2(m) : "=r" (_v) : "ir" (port), "r" (mips_io_port_base)); SLOW_DOWN_IO; return __ioswab##w(_v); } - -#define __INS1(s) \ -extern inline void __ins##s(unsigned int port, void * addr, unsigned long count) { - -#define __INS2(m) \ -if (count) \ -__asm__ __volatile__ ( \ - ".set\tnoreorder\n\t" \ - ".set\tnoat\n" \ - "1:\tl" #m "\t$1,%4(%5)\n\t" \ - "subu\t%1,1\n\t" \ - "s" #m "\t$1,(%0)\n\t" \ - "bne\t$0,%1,1b\n\t" \ - "addiu\t%0,%6\n\t" \ - ".set\tat\n\t" \ - ".set\treorder" - -#define __INS(m,s,i) \ -__INS1(s) __INS2(m) \ - : "=r" (addr), "=r" (count) \ - : "0" (addr), "1" (count), "i" (0), \ - "r" (mips_io_port_base+port), "I" (i) \ - : "$1");} \ -__INS1(s##c) __INS2(m) \ - : "=r" (addr), "=r" (count) \ - : "0" (addr), "1" (count), "ir" (port), \ - "r" (mips_io_port_base), "I" (i) \ - : "$1");} - -#define __OUTS1(s) \ -extern inline void __outs##s(unsigned int port, const void * addr, unsigned long count) { - -#define __OUTS2(m) \ -if (count) \ -__asm__ __volatile__ ( \ - ".set\tnoreorder\n\t" \ - ".set\tnoat\n" \ - "1:\tl" #m "\t$1,(%0)\n\t" \ - "subu\t%1,1\n\t" \ - "s" #m "\t$1,%4(%5)\n\t" \ - "bne\t$0,%1,1b\n\t" \ - "addiu\t%0,%6\n\t" \ - ".set\tat\n\t" \ - ".set\treorder" - -#define __OUTS(m,s,i) \ -__OUTS1(s) __OUTS2(m) \ - : "=r" (addr), "=r" (count) \ - : "0" (addr), "1" (count), "i" (0), "r" (mips_io_port_base+port), "I" (i) \ - : "$1");} \ -__OUTS1(s##c) __OUTS2(m) \ - : "=r" (addr), "=r" (count) \ - : "0" (addr), "1" (count), "ir" (port), "r" (mips_io_port_base), "I" (i) \ - : "$1");} - -__IN(unsigned char,b,b,8) -__IN(unsigned short,h,w,16) -__IN(unsigned int,w,l,32) - -__OUT(b,b,8) -__OUT(h,w,16) -__OUT(w,l,32) - -__INS(b,b,1) -__INS(h,w,2) -__INS(w,l,4) - -__OUTS(b,b,1) -__OUTS(h,w,2) -__OUTS(w,l,4) +static inline unsigned char __inb_p(unsigned long port) +{ + u8 __val; + __val = *(volatile u8 *)(mips_io_port_base + port); + SLOW_DOWN_IO; -/* - * Note that due to the way __builtin_constant_p() works, you - * - can't use it inside an inline function (it will never be true) - * - you don't have to worry about side effects within the __builtin.. - */ -#define outb(val,port) \ -((__builtin_constant_p((port)) && (port) < 32768) ? \ - __outbc((val),(port)) : \ - __outb((val),(port))) - -#define inb(port) \ -((__builtin_constant_p((port)) && (port) < 32768) ? \ - __inbc(port) : \ - __inb(port)) - -#define outb_p(val,port) \ -((__builtin_constant_p((port)) && (port) < 32768) ? \ - __outbc_p((val),(port)) : \ - __outb_p((val),(port))) - -#define inb_p(port) \ -((__builtin_constant_p((port)) && (port) < 32768) ? \ - __inbc_p(port) : \ - __inb_p(port)) - -#define outw(val,port) \ -((__builtin_constant_p((port)) && (port) < 32768) ? \ - __outwc((val),(port)) : \ - __outw((val),(port))) - -#define inw(port) \ -((__builtin_constant_p((port)) && (port) < 32768) ? \ - __inwc(port) : \ - __inw(port)) - -#define outw_p(val,port) \ -((__builtin_constant_p((port)) && (port) < 32768) ? \ - __outwc_p((val),(port)) : \ - __outw_p((val),(port))) - -#define inw_p(port) \ -((__builtin_constant_p((port)) && (port) < 32768) ? \ - __inwc_p(port) : \ - __inw_p(port)) - -#define outl(val,port) \ -((__builtin_constant_p((port)) && (port) < 32768) ? \ - __outlc((val),(port)) : \ - __outl((val),(port))) - -#define inl(port) \ -((__builtin_constant_p((port)) && (port) < 32768) ? \ - __inlc(port) : \ - __inl(port)) - -#define outl_p(val,port) \ -((__builtin_constant_p((port)) && (port) < 32768) ? \ - __outlc_p((val),(port)) : \ - __outl_p((val),(port))) - -#define inl_p(port) \ -((__builtin_constant_p((port)) && (port) < 32768) ? \ - __inlc_p(port) : \ - __inl_p(port)) - - -#define outsb(port,addr,count) \ -((__builtin_constant_p((port)) && (port) < 32768) ? \ - __outsbc((port),(addr),(count)) : \ - __outsb ((port),(addr),(count))) - -#define insb(port,addr,count) \ -((__builtin_constant_p((port)) && (port) < 32768) ? \ - __insbc((port),(addr),(count)) : \ - __insb((port),(addr),(count))) - -#define outsw(port,addr,count) \ -((__builtin_constant_p((port)) && (port) < 32768) ? \ - __outswc((port),(addr),(count)) : \ - __outsw ((port),(addr),(count))) - -#define insw(port,addr,count) \ -((__builtin_constant_p((port)) && (port) < 32768) ? \ - __inswc((port),(addr),(count)) : \ - __insw((port),(addr),(count))) - -#define outsl(port,addr,count) \ -((__builtin_constant_p((port)) && (port) < 32768) ? \ - __outslc((port),(addr),(count)) : \ - __outsl ((port),(addr),(count))) - -#define insl(port,addr,count) \ -((__builtin_constant_p((port)) && (port) < 32768) ? \ - __inslc((port),(addr),(count)) : \ - __insl((port),(addr),(count))) + return __ioswab8(__val); +} -#define IO_SPACE_LIMIT 0xffff +static inline unsigned short __inw_p(unsigned long port) +{ + u16 __val; + + __val = *(volatile u16 *)(mips_io_port_base + port); + SLOW_DOWN_IO; + + return __ioswab16(__val); +} + +static inline unsigned int __inl_p(unsigned long port) +{ + u32 __val; + + __val = *(volatile u32 *)(mips_io_port_base + port); + SLOW_DOWN_IO; + return __ioswab32(__val); +} + +#define outsb(port, addr, count) __outsb(port, addr, count) +#define insb(port, addr, count) __insb(port, addr, count) +#define outsw(port, addr, count) __outsw(port, addr, count) +#define insw(port, addr, count) __insw(port, addr, count) +#define outsl(port, addr, count) __outsl(port, addr, count) +#define insl(port, addr, count) __insl(port, addr, count) + +static inline void __outsb(unsigned long port, void *addr, unsigned int count) +{ + while (count--) { + outb(*(u8 *)addr, port); + addr++; + } +} + +static inline void __insb(unsigned long port, void *addr, unsigned int count) +{ + while (count--) { + *(u8 *)addr = inb(port); + addr++; + } +} + +static inline void __outsw(unsigned long port, void *addr, unsigned int count) +{ + while (count--) { + outw(*(u16 *)addr, port); + addr += 2; + } +} + +static inline void __insw(unsigned long port, void *addr, unsigned int count) +{ + while (count--) { + *(u16 *)addr = inw(port); + addr += 2; + } +} + +static inline void __outsl(unsigned long port, void *addr, unsigned int count) +{ + while (count--) { + outl(*(u32 *)addr, port); + addr += 4; + } +} + +static inline void __insl(unsigned long port, void *addr, unsigned int count) +{ + while (count--) { + *(u32 *)addr = inl(port); + addr += 4; + } +} /* * The caches on some architectures aren't dma-coherent and have need to @@ -435,12 +474,25 @@ __OUTS(w,l,4) * be discarded. This operation is necessary before dma operations * to the memory. */ +#ifdef CONFIG_NONCOHERENT_IO + extern void (*_dma_cache_wback_inv)(unsigned long start, unsigned long size); extern void (*_dma_cache_wback)(unsigned long start, unsigned long size); extern void (*_dma_cache_inv)(unsigned long start, unsigned long size); -#define dma_cache_wback_inv(start,size) _dma_cache_wback_inv(start,size) -#define dma_cache_wback(start,size) _dma_cache_wback(start,size) -#define dma_cache_inv(start,size) _dma_cache_inv(start,size) +#define dma_cache_wback_inv(start, size)_dma_cache_wback_inv(start,size) +#define dma_cache_wback(start, size) _dma_cache_wback(start,size) +#define dma_cache_inv(start, size) _dma_cache_inv(start,size) + +#else /* Sane hardware */ + +#define dma_cache_wback_inv(start,size) \ + do { (void) (start); (void) (size); } while (0) +#define dma_cache_wback(start,size) \ + do { (void) (start); (void) (size); } while (0) +#define dma_cache_inv(start,size) \ + do { (void) (start); (void) (size); } while (0) + +#endif /* CONFIG_NONCOHERENT_IO */ #endif /* _ASM_IO_H */ diff --git a/include/asm-mips/ioctls.h b/include/asm-mips/ioctls.h index 1fd384f2bc13..92f6c36aac4d 100644 --- a/include/asm-mips/ioctls.h +++ b/include/asm-mips/ioctls.h @@ -12,7 +12,7 @@ #include <asm/ioctl.h> #define TCGETA 0x5401 -#define TCSETA 0x5402 +#define TCSETA 0x5402 /* Clashes with SNDCTL_TMR_START sound ioctl */ #define TCSETAW 0x5403 #define TCSETAF 0x5404 @@ -49,7 +49,7 @@ #define TIOCGETD 0x7400 #define FIOCLEX 0x6601 -#define FIONCLEX 0x6602 /* these numbers need to be adjusted. */ +#define FIONCLEX 0x6602 #define FIOASYNC 0x667d #define FIONBIO 0x667e #define FIOQSIZE 0x667f @@ -66,7 +66,7 @@ #define TIOCGETP 0x7408 #define TIOCSETP 0x7409 #define TIOCSETN 0x740a /* TIOCSETP wo flush */ - + /* #define TIOCSETA _IOW('t', 20, struct termios) set termios struct */ /* #define TIOCSETAW _IOW('t', 21, struct termios) drain output, set */ /* #define TIOCSETAF _IOW('t', 22, struct termios) drn out, fls in, set */ diff --git a/include/asm-mips/ipc.h b/include/asm-mips/ipc.h index 006d47307970..eaf4b8631435 100644 --- a/include/asm-mips/ipc.h +++ b/include/asm-mips/ipc.h @@ -1,7 +1,7 @@ #ifndef __ASM_MIPS_IPC_H #define __ASM_MIPS_IPC_H -/* +/* * These are used to wrap system calls on MIPS. * * See arch/mips/kernel/sysmips.c for ugly details.. @@ -15,6 +15,7 @@ struct ipc_kludge { #define SEMOP 1 #define SEMGET 2 #define SEMCTL 3 +#define SEMTIMEDOP 4 #define MSGSND 11 #define MSGRCV 12 #define MSGGET 13 diff --git a/include/asm-mips/ipcbuf.h b/include/asm-mips/ipcbuf.h index 4c898f677cb0..d47d08f264e7 100644 --- a/include/asm-mips/ipcbuf.h +++ b/include/asm-mips/ipcbuf.h @@ -1,7 +1,7 @@ #ifndef _ASM_IPCBUF_H #define _ASM_IPCBUF_H -/* +/* * The ipc64_perm structure for alpha architecture. * Note extra padding because this structure is passed back and forth * between kernel and user space. @@ -18,7 +18,7 @@ struct ipc64_perm __kernel_gid_t gid; __kernel_uid_t cuid; __kernel_gid_t cgid; - __kernel_mode_t mode; + __kernel_mode_t mode; unsigned short seq; unsigned short __pad1; unsigned long __unused1; diff --git a/include/asm-mips/irq.h b/include/asm-mips/irq.h index bea209e24a50..4bb7750583d8 100644 --- a/include/asm-mips/irq.h +++ b/include/asm-mips/irq.h @@ -4,16 +4,17 @@ * for more details. * * Copyright (C) 1994 by Waldorf GMBH, written by Ralf Baechle - * Copyright (C) 1995, 96, 97, 98, 99, 2000, 2001 by Ralf Baechle + * Copyright (C) 1995, 96, 97, 98, 99, 2000, 01, 02 by Ralf Baechle + * Copyright (C) 1999, 2000 Silicon Graphics, Inc. + * Copyright (C) 2001 Kanoj Sarcar */ #ifndef _ASM_IRQ_H #define _ASM_IRQ_H #include <linux/config.h> +#include <linux/linkage.h> -#define NR_IRQS 64 /* Largest number of ints of all machines. */ - -#define TIMER_IRQ 0 +#define NR_IRQS 128 /* Largest number of ints of all machines. */ #ifdef CONFIG_I8259 static inline int irq_canonicalize(int irq) @@ -24,19 +25,16 @@ static inline int irq_canonicalize(int irq) #define irq_canonicalize(irq) (irq) /* Sane hardware, sane code ... */ #endif -struct irqaction; -extern int i8259_setup_irq(int irq, struct irqaction * new); extern void disable_irq(unsigned int); - -#ifndef CONFIG_NEW_IRQ -#define disable_irq_nosync disable_irq -#else extern void disable_irq_nosync(unsigned int); -#endif - extern void enable_irq(unsigned int); +struct pt_regs; +extern asmlinkage unsigned int do_IRQ(int irq, struct pt_regs *regs); + /* Machine specific interrupt initialization */ extern void (*irq_setup)(void); +extern void init_generic_irq(void); + #endif /* _ASM_IRQ_H */ diff --git a/include/asm-mips/irq_cpu.h b/include/asm-mips/irq_cpu.h new file mode 100644 index 000000000000..9baaca62a180 --- /dev/null +++ b/include/asm-mips/irq_cpu.h @@ -0,0 +1,18 @@ +/* + * include/asm-mips/irq_cpu.h + * + * MIPS CPU interrupt definitions. + * + * Copyright (C) 2002 Maciej W. Rozycki + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#ifndef __ASM_MIPS_IRQ_CPU_H +#define __ASM_MIPS_IRQ_CPU_H + +extern void mips_cpu_irq_init(int irq_base); + +#endif /* __ASM_MIPS_IRQ_CPU_H */ diff --git a/include/asm-mips/isadep.h b/include/asm-mips/isadep.h index b3453bb3ba34..7bb003511d9e 100644 --- a/include/asm-mips/isadep.h +++ b/include/asm-mips/isadep.h @@ -10,7 +10,7 @@ #ifndef __ASM_ISADEP_H #define __ASM_ISADEP_H -#if defined(CONFIG_CPU_R3000) +#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX) /* * R2000 or R3000 */ diff --git a/include/asm-mips/keyboard.h b/include/asm-mips/keyboard.h new file mode 100644 index 000000000000..b80dd875e71c --- /dev/null +++ b/include/asm-mips/keyboard.h @@ -0,0 +1,96 @@ +/* + * 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 Ralf Baechle + */ +#ifndef _ASM_KEYBOARD_H +#define _ASM_KEYBOARD_H + +#ifdef __KERNEL__ + +#include <linux/config.h> +#include <linux/delay.h> +#include <linux/ioport.h> +#include <linux/kd.h> +#include <linux/pm.h> + +#define DISABLE_KBD_DURING_INTERRUPTS 0 + +#ifdef CONFIG_PC_KEYB + +extern int pckbd_setkeycode(unsigned int scancode, unsigned int keycode); +extern int pckbd_getkeycode(unsigned int scancode); +extern int pckbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode); +extern char pckbd_unexpected_up(unsigned char keycode); +extern void pckbd_leds(unsigned char leds); +extern void pckbd_init_hw(void); +extern int pckbd_pm_resume(struct pm_dev *, pm_request_t, void *); +extern pm_callback pm_kbd_request_override; +extern unsigned char pckbd_sysrq_xlate[128]; +extern void kbd_forward_char (int ch); + +#define kbd_setkeycode pckbd_setkeycode +#define kbd_getkeycode pckbd_getkeycode +#define kbd_translate pckbd_translate +#define kbd_unexpected_up pckbd_unexpected_up +#define kbd_leds pckbd_leds +#define kbd_init_hw pckbd_init_hw +#define kbd_sysrq_xlate pckbd_sysrq_xlate + +#define SYSRQ_KEY 0x54 + +/* Some stoneage hardware needs delays after some operations. */ +#define kbd_pause() do { } while(0) + +struct kbd_ops { + /* Keyboard driver resource allocation */ + void (*kbd_request_region)(void); + int (*kbd_request_irq)(void (*handler)(int, void *, struct pt_regs *)); + + /* PSaux driver resource management */ + int (*aux_request_irq)(void (*handler)(int, void *, struct pt_regs *)); + void (*aux_free_irq)(void); + + /* Methods to access the keyboard processor's I/O registers */ + unsigned char (*kbd_read_input)(void); + void (*kbd_write_output)(unsigned char val); + void (*kbd_write_command)(unsigned char val); + unsigned char (*kbd_read_status)(void); +}; + +extern struct kbd_ops *kbd_ops; + +/* Do the actual calls via kbd_ops vector */ +#define kbd_request_region() kbd_ops->kbd_request_region() +#define kbd_request_irq(handler) kbd_ops->kbd_request_irq(handler) + +#define aux_request_irq(hand, dev_id) kbd_ops->aux_request_irq(hand) +#define aux_free_irq(dev_id) kbd_ops->aux_free_irq() + +#define kbd_read_input() kbd_ops->kbd_read_input() +#define kbd_write_output(val) kbd_ops->kbd_write_output(val) +#define kbd_write_command(val) kbd_ops->kbd_write_command(val) +#define kbd_read_status() kbd_ops->kbd_read_status() + +#else + +extern int kbd_setkeycode(unsigned int scancode, unsigned int keycode); +extern int kbd_getkeycode(unsigned int scancode); +extern int kbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode); +extern char kbd_unexpected_up(unsigned char keycode); +extern void kbd_leds(unsigned char leds); +extern void kbd_init_hw(void); +extern unsigned char *kbd_sysrq_xlate; + +extern unsigned char kbd_sysrq_key; +#define SYSRQ_KEY kbd_sysrq_key + +#endif + +#endif /* __KERNEL */ + +#endif /* _ASM_KEYBOARD_H */ diff --git a/include/asm-mips/kmap_types.h b/include/asm-mips/kmap_types.h new file mode 100644 index 000000000000..a117c0ade4d0 --- /dev/null +++ b/include/asm-mips/kmap_types.h @@ -0,0 +1,32 @@ +#ifndef _ASM_KMAP_TYPES_H +#define _ASM_KMAP_TYPES_H + +#include <linux/config.h> + +#ifdef CONFIG_DEBUG_HIGHMEM +# define D(n) __KM_FENCE_##n , +#else +# define D(n) +#endif + +enum km_type { +D(0) KM_BOUNCE_READ, +D(1) KM_SKB_SUNRPC_DATA, +D(2) KM_SKB_DATA_SOFTIRQ, +D(3) KM_USER0, +D(4) KM_USER1, +D(5) KM_BIO_SRC_IRQ, +D(6) KM_BIO_DST_IRQ, +D(7) KM_PTE0, +D(8) KM_PTE1, +D(9) KM_PTE2, +D(10) KM_IRQ0, +D(11) KM_IRQ1, +D(12) KM_SOFTIRQ0, +D(13) KM_SOFTIRQ1, +D(14) KM_TYPE_NR +}; + +#undef D + +#endif diff --git a/include/asm-mips/mc146818rtc.h b/include/asm-mips/mc146818rtc.h index 6e23e432f335..cc6eeef7cbad 100644 --- a/include/asm-mips/mc146818rtc.h +++ b/include/asm-mips/mc146818rtc.h @@ -6,33 +6,15 @@ * Machine dependent access functions for RTC registers. * * Copyright (C) 1996, 1997, 1998, 2000 Ralf Baechle + * Copyright (C) 2002 Maciej W. Rozycki */ #ifndef _ASM_MC146818RTC_H #define _ASM_MC146818RTC_H #include <linux/config.h> -#include <asm/io.h> -#ifndef RTC_PORT -#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) -#define RTC_PORT(x) (0x14014800 + (x)) -#else -#define RTC_PORT(x) (0x70 + (x)) -#endif -#endif +#include <asm/io.h> -/* - * The yet supported machines all access the RTC index register via - * an ISA port access but the way to access the date register differs ... - */ -#define CMOS_READ(addr) ({ \ -rtc_ops->rtc_read_data(addr); \ -}) -#define CMOS_WRITE(val, addr) ({ \ -rtc_ops->rtc_write_data(val, addr); \ -}) -#define RTC_ALWAYS_BCD \ -rtc_ops->rtc_bcd_mode() /* * This structure defines how to access various features of @@ -47,15 +29,38 @@ struct rtc_ops { extern struct rtc_ops *rtc_ops; +/* + * Most supported machines access the RTC index register via an ISA + * port access but the way to access the date register differs ... + * The DECstation directly maps the RTC memory in the CPU's address + * space with the chipset generating necessary index write/data access + * cycles automagically. + */ +#define CMOS_READ(addr) ({ \ +rtc_ops->rtc_read_data(addr); \ +}) +#define CMOS_WRITE(val, addr) ({ \ +rtc_ops->rtc_write_data(val, addr); \ +}) +#define RTC_ALWAYS_BCD \ +rtc_ops->rtc_bcd_mode() + + #ifdef CONFIG_DECSTATION -#define RTC_IRQ 0 -#elif defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) -#include <asm/it8172/it8172_int.h> -#define RTC_IRQ IT8172_RTC_IRQ + +#include <asm/dec/rtc-dec.h> + +#elif defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100) + +#define RTC_PORT(x) (0x0c000000 + (x)) +#define RTC_IOMAPPED 0 +#define RTC_IRQ 0 + #else -#define RTC_IRQ 8 -#endif -#define RTC_DEC_YEAR 0x3f /* Where we store the real year on DECs. */ +#define RTC_PORT(x) (0x70 + (x)) +#define RTC_IRQ 8 + +#endif #endif /* _ASM_MC146818RTC_H */ diff --git a/include/asm-mips/mips32_cache.h b/include/asm-mips/mips32_cache.h deleted file mode 100644 index 2de18bd7cb71..000000000000 --- a/include/asm-mips/mips32_cache.h +++ /dev/null @@ -1,288 +0,0 @@ -/* - * mips32_cache.h - * - * Carsten Langgaard, carstenl@mips.com - * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved. - * - * ######################################################################## - * - * This program is free software; you can distribute it and/or modify it - * under the terms of the GNU General Public License (Version 2) as - * published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - * - * ######################################################################## - * - * Inline assembly cache operations. - * - * This file is the original r4cache.c file with modification that makes the - * cache handling more generic. - * - * FIXME: Handle split L2 caches. - * - */ -#ifndef _MIPS_R4KCACHE_H -#define _MIPS_R4KCACHE_H - -#include <asm/asm.h> -#include <asm/cacheops.h> - -extern inline void flush_icache_line_indexed(unsigned long addr) -{ - __asm__ __volatile__( - ".set noreorder\n\t" - ".set mips3\n\t" - "cache %1, (%0)\n\t" - ".set mips0\n\t" - ".set reorder" - : - : "r" (addr), - "i" (Index_Invalidate_I)); -} - -extern inline void flush_dcache_line_indexed(unsigned long addr) -{ - __asm__ __volatile__( - ".set noreorder\n\t" - ".set mips3\n\t" - "cache %1, (%0)\n\t" - ".set mips0\n\t" - ".set reorder" - : - : "r" (addr), - "i" (Index_Writeback_Inv_D)); -} - -extern inline void flush_scache_line_indexed(unsigned long addr) -{ - __asm__ __volatile__( - ".set noreorder\n\t" - ".set mips3\n\t" - "cache %1, (%0)\n\t" - ".set mips0\n\t" - ".set reorder" - : - : "r" (addr), - "i" (Index_Writeback_Inv_SD)); -} - -extern inline void flush_icache_line(unsigned long addr) -{ - __asm__ __volatile__( - ".set noreorder\n\t" - ".set mips3\n\t" - "cache %1, (%0)\n\t" - ".set mips0\n\t" - ".set reorder" - : - : "r" (addr), - "i" (Hit_Invalidate_I)); -} - -extern inline void flush_dcache_line(unsigned long addr) -{ - __asm__ __volatile__( - ".set noreorder\n\t" - ".set mips3\n\t" - "cache %1, (%0)\n\t" - ".set mips0\n\t" - ".set reorder" - : - : "r" (addr), - "i" (Hit_Writeback_Inv_D)); -} - -extern inline void invalidate_dcache_line(unsigned long addr) -{ - __asm__ __volatile__( - ".set noreorder\n\t" - ".set mips3\n\t" - "cache %1, (%0)\n\t" - ".set mips0\n\t" - ".set reorder" - : - : "r" (addr), - "i" (Hit_Invalidate_D)); -} - -extern inline void invalidate_scache_line(unsigned long addr) -{ - __asm__ __volatile__( - ".set noreorder\n\t" - ".set mips3\n\t" - "cache %1, (%0)\n\t" - ".set mips0\n\t" - ".set reorder" - : - : "r" (addr), - "i" (Hit_Invalidate_SD)); -} - -extern inline void flush_scache_line(unsigned long addr) -{ - __asm__ __volatile__( - ".set noreorder\n\t" - ".set mips3\n\t" - "cache %1, (%0)\n\t" - ".set mips0\n\t" - ".set reorder" - : - : "r" (addr), - "i" (Hit_Writeback_Inv_SD)); -} - -/* - * The next two are for badland addresses like signal trampolines. - */ -extern inline void protected_flush_icache_line(unsigned long addr) -{ - __asm__ __volatile__( - ".set noreorder\n\t" - ".set mips3\n" - "1:\tcache %1,(%0)\n" - "2:\t.set mips0\n\t" - ".set reorder\n\t" - ".section\t__ex_table,\"a\"\n\t" - STR(PTR)"\t1b,2b\n\t" - ".previous" - : - : "r" (addr), - "i" (Hit_Invalidate_I)); -} - -extern inline void protected_writeback_dcache_line(unsigned long addr) -{ - __asm__ __volatile__( - ".set noreorder\n\t" - ".set mips3\n" - "1:\tcache %1,(%0)\n" - "2:\t.set mips0\n\t" - ".set reorder\n\t" - ".section\t__ex_table,\"a\"\n\t" - STR(PTR)"\t1b,2b\n\t" - ".previous" - : - : "r" (addr), - "i" (Hit_Writeback_D)); -} - -#define cache_unroll(base,op) \ - __asm__ __volatile__(" \ - .set noreorder; \ - .set mips3; \ - cache %1, (%0); \ - .set mips0; \ - .set reorder" \ - : \ - : "r" (base), \ - "i" (op)); - - -extern inline void blast_dcache(void) -{ - unsigned long start = KSEG0; - unsigned long end = (start + dcache_size); - - while(start < end) { - cache_unroll(start,Index_Writeback_Inv_D); - start += dc_lsize; - } -} - -extern inline void blast_dcache_page(unsigned long page) -{ - unsigned long start = page; - unsigned long end = (start + PAGE_SIZE); - - while(start < end) { - cache_unroll(start,Hit_Writeback_Inv_D); - start += dc_lsize; - } -} - -extern inline void blast_dcache_page_indexed(unsigned long page) -{ - unsigned long start = page; - unsigned long end = (start + PAGE_SIZE); - - while(start < end) { - cache_unroll(start,Index_Writeback_Inv_D); - start += dc_lsize; - } -} - -extern inline void blast_icache(void) -{ - unsigned long start = KSEG0; - unsigned long end = (start + icache_size); - - while(start < end) { - cache_unroll(start,Index_Invalidate_I); - start += ic_lsize; - } -} - -extern inline void blast_icache_page(unsigned long page) -{ - unsigned long start = page; - unsigned long end = (start + PAGE_SIZE); - - while(start < end) { - cache_unroll(start,Hit_Invalidate_I); - start += ic_lsize; - } -} - -extern inline void blast_icache_page_indexed(unsigned long page) -{ - unsigned long start = page; - unsigned long end = (start + PAGE_SIZE); - - while(start < end) { - cache_unroll(start,Index_Invalidate_I); - start += ic_lsize; - } -} - -extern inline void blast_scache(void) -{ - unsigned long start = KSEG0; - unsigned long end = KSEG0 + scache_size; - - while(start < end) { - cache_unroll(start,Index_Writeback_Inv_SD); - start += sc_lsize; - } -} - -extern inline void blast_scache_page(unsigned long page) -{ - unsigned long start = page; - unsigned long end = page + PAGE_SIZE; - - while(start < end) { - cache_unroll(start,Hit_Writeback_Inv_SD); - start += sc_lsize; - } -} - -extern inline void blast_scache_page_indexed(unsigned long page) -{ - unsigned long start = page; - unsigned long end = page + PAGE_SIZE; - - while(start < end) { - cache_unroll(start,Index_Writeback_Inv_SD); - start += sc_lsize; - } -} - -#endif /* !(_MIPS_R4KCACHE_H) */ diff --git a/include/asm-mips/mipsregs.h b/include/asm-mips/mipsregs.h index ea791fc15eb7..6f266efe226c 100644 --- a/include/asm-mips/mipsregs.h +++ b/include/asm-mips/mipsregs.h @@ -8,10 +8,12 @@ * Modified for further R[236]000 support by Paul M. Antoine, 1996. * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved. + * Copyright (C) 2003 Maciej W. Rozycki */ #ifndef _ASM_MIPSREGS_H #define _ASM_MIPSREGS_H +#include <linux/config.h> #include <linux/linkage.h> /* @@ -26,6 +28,15 @@ #endif /* + * Configure language + */ +#ifdef __ASSEMBLY__ +#define _ULCAST_ +#else +#define _ULCAST_ (unsigned long) +#endif + +/* * Coprocessor 0 register names */ #define CP0_INDEX $0 @@ -52,12 +63,15 @@ #define CP0_XCONTEXT $20 #define CP0_FRAMEMASK $21 #define CP0_DIAGNOSTIC $22 +#define CP0_DEBUG $23 +#define CP0_DEPC $24 #define CP0_PERFORMANCE $25 #define CP0_ECC $26 #define CP0_CACHEERR $27 #define CP0_TAGLO $28 #define CP0_TAGHI $29 #define CP0_ERROREPC $30 +#define CP0_DESAVE $31 /* * R4640/R4650 cp0 register names. These registers are listed @@ -73,12 +87,18 @@ #define CP0_IWATCH $18 #define CP0_DWATCH $19 -/* +/* * Coprocessor 0 Set 1 register names */ #define CP0_S1_DERRADDR0 $26 #define CP0_S1_DERRADDR1 $27 #define CP0_S1_INTCONTROL $20 + +/* + * TX39 Series + */ +#define CP0_TX39_CACHE $7 + /* * Coprocessor 1 (FPU) register names */ @@ -108,7 +128,7 @@ * E the exception enable * S the sticky/flag bit */ -#define FPU_CSR_ALL_X 0x0003f000 +#define FPU_CSR_ALL_X 0x0003f000 #define FPU_CSR_UNI_X 0x00020000 #define FPU_CSR_INV_X 0x00010000 #define FPU_CSR_DIV_X 0x00008000 @@ -140,176 +160,66 @@ /* * Values for PageMask register */ -#include <linux/config.h> #ifdef CONFIG_CPU_VR41XX -#define PM_1K 0x00000000 -#define PM_4K 0x00001800 -#define PM_16K 0x00007800 -#define PM_64K 0x0001f800 -#define PM_256K 0x0007f800 -#else -#define PM_4K 0x00000000 -#define PM_16K 0x00006000 -#define PM_64K 0x0001e000 -#define PM_256K 0x0007e000 -#define PM_1M 0x001fe000 -#define PM_4M 0x007fe000 -#define PM_16M 0x01ffe000 -#endif -/* - * Values used for computation of new tlb entries - */ -#define PL_4K 12 -#define PL_16K 14 -#define PL_64K 16 -#define PL_256K 18 -#define PL_1M 20 -#define PL_4M 22 -#define PL_16M 24 +/* Why doesn't stupidity hurt ... */ -/* - * Macros to access the system control coprocessor - */ -#define read_32bit_cp0_register(source) \ -({ int __res; \ - __asm__ __volatile__( \ - ".set\tpush\n\t" \ - ".set\treorder\n\t" \ - "mfc0\t%0,"STR(source)"\n\t" \ - ".set\tpop" \ - : "=r" (__res)); \ - __res;}) - -#define read_32bit_cp0_set1_register(source) \ -({ int __res; \ - __asm__ __volatile__( \ - ".set\tpush\n\t" \ - ".set\treorder\n\t" \ - "cfc0\t%0,"STR(source)"\n\t" \ - ".set\tpop" \ - : "=r" (__res)); \ - __res;}) +#define PM_1K 0x00000000 +#define PM_4K 0x00001800 +#define PM_16K 0x00007800 +#define PM_64K 0x0001f800 +#define PM_256K 0x0007f800 -/* - * For now use this only with interrupts disabled! - */ -#define read_64bit_cp0_register(source) \ -({ int __res; \ - __asm__ __volatile__( \ - ".set\tmips3\n\t" \ - "dmfc0\t%0,"STR(source)"\n\t" \ - ".set\tmips0" \ - : "=r" (__res)); \ - __res;}) +#else -#define write_32bit_cp0_register(register,value) \ - __asm__ __volatile__( \ - "mtc0\t%0,"STR(register)"\n\t" \ - "nop" \ - : : "r" (value)); +#define PM_4K 0x00000000 +#define PM_16K 0x00006000 +#define PM_64K 0x0001e000 +#define PM_256K 0x0007e000 +#define PM_1M 0x001fe000 +#define PM_4M 0x007fe000 +#define PM_16M 0x01ffe000 +#define PM_64M 0x07ffe000 +#define PM_256M 0x1fffe000 -#define write_32bit_cp0_set1_register(register,value) \ - __asm__ __volatile__( \ - "ctc0\t%0,"STR(register)"\n\t" \ - "nop" \ - : : "r" (value)); - -#define write_64bit_cp0_register(register,value) \ - __asm__ __volatile__( \ - ".set\tmips3\n\t" \ - "dmtc0\t%0,"STR(register)"\n\t" \ - ".set\tmips0" \ - : : "r" (value)) +#endif -/* - * This should be changed when we get a compiler that support the MIPS32 ISA. +/* + * Values used for computation of new tlb entries */ -#define read_mips32_cp0_config1() \ -({ int __res; \ - __asm__ __volatile__( \ - ".set\tnoreorder\n\t" \ - ".set\tnoat\n\t" \ - ".word\t0x40018001\n\t" \ - "move\t%0,$1\n\t" \ - ".set\tat\n\t" \ - ".set\treorder" \ - :"=r" (__res)); \ - __res;}) +#define PL_4K 12 +#define PL_16K 14 +#define PL_64K 16 +#define PL_256K 18 +#define PL_1M 20 +#define PL_4M 22 +#define PL_16M 24 +#define PL_64M 26 +#define PL_256M 28 /* * R4x00 interrupt enable / cause bits */ -#define IE_SW0 (1<< 8) -#define IE_SW1 (1<< 9) -#define IE_IRQ0 (1<<10) -#define IE_IRQ1 (1<<11) -#define IE_IRQ2 (1<<12) -#define IE_IRQ3 (1<<13) -#define IE_IRQ4 (1<<14) -#define IE_IRQ5 (1<<15) +#define IE_SW0 (_ULCAST_(1) << 8) +#define IE_SW1 (_ULCAST_(1) << 9) +#define IE_IRQ0 (_ULCAST_(1) << 10) +#define IE_IRQ1 (_ULCAST_(1) << 11) +#define IE_IRQ2 (_ULCAST_(1) << 12) +#define IE_IRQ3 (_ULCAST_(1) << 13) +#define IE_IRQ4 (_ULCAST_(1) << 14) +#define IE_IRQ5 (_ULCAST_(1) << 15) /* * R4x00 interrupt cause bits */ -#define C_SW0 (1<< 8) -#define C_SW1 (1<< 9) -#define C_IRQ0 (1<<10) -#define C_IRQ1 (1<<11) -#define C_IRQ2 (1<<12) -#define C_IRQ3 (1<<13) -#define C_IRQ4 (1<<14) -#define C_IRQ5 (1<<15) - -#ifndef _LANGUAGE_ASSEMBLY -/* - * Manipulate the status register. - * Mostly used to access the interrupt bits. - */ -#define __BUILD_SET_CP0(name,register) \ -extern __inline__ unsigned int \ -set_cp0_##name(unsigned int set) \ -{ \ - unsigned int res; \ - \ - res = read_32bit_cp0_register(register); \ - res |= set; \ - write_32bit_cp0_register(register, res); \ - \ - return res; \ -} \ - \ -extern __inline__ unsigned int \ -clear_cp0_##name(unsigned int clear) \ -{ \ - unsigned int res; \ - \ - res = read_32bit_cp0_register(register); \ - res &= ~clear; \ - write_32bit_cp0_register(register, res); \ - \ - return res; \ -} \ - \ -extern __inline__ unsigned int \ -change_cp0_##name(unsigned int change, unsigned int new) \ -{ \ - unsigned int res; \ - \ - res = read_32bit_cp0_register(register); \ - res &= ~change; \ - res |= (new & change); \ - if(change) \ - write_32bit_cp0_register(register, res); \ - \ - return res; \ -} - -__BUILD_SET_CP0(status,CP0_STATUS) -__BUILD_SET_CP0(cause,CP0_CAUSE) -__BUILD_SET_CP0(config,CP0_CONFIG) - -#endif /* defined (_LANGUAGE_ASSEMBLY) */ +#define C_SW0 (_ULCAST_(1) << 8) +#define C_SW1 (_ULCAST_(1) << 9) +#define C_IRQ0 (_ULCAST_(1) << 10) +#define C_IRQ1 (_ULCAST_(1) << 11) +#define C_IRQ2 (_ULCAST_(1) << 12) +#define C_IRQ3 (_ULCAST_(1) << 13) +#define C_IRQ4 (_ULCAST_(1) << 14) +#define C_IRQ5 (_ULCAST_(1) << 15) /* * Bitfields in the R4xx0 cp0 status register @@ -344,9 +254,9 @@ __BUILD_SET_CP0(config,CP0_CONFIG) /* * Bits specific to the R4640/R4650 */ -#define ST0_UM (1 << 4) -#define ST0_IL (1 << 23) -#define ST0_DL (1 << 24) +#define ST0_UM (_ULCAST_(1) << 4) +#define ST0_IL (_ULCAST_(1) << 23) +#define ST0_DL (_ULCAST_(1) << 24) /* * Bitfields in the TX39 family CP0 Configuration Register 3 @@ -386,39 +296,40 @@ __BUILD_SET_CP0(config,CP0_CONFIG) */ #define ST0_IM 0x0000ff00 #define STATUSB_IP0 8 -#define STATUSF_IP0 (1 << 8) +#define STATUSF_IP0 (_ULCAST_(1) << 8) #define STATUSB_IP1 9 -#define STATUSF_IP1 (1 << 9) +#define STATUSF_IP1 (_ULCAST_(1) << 9) #define STATUSB_IP2 10 -#define STATUSF_IP2 (1 << 10) +#define STATUSF_IP2 (_ULCAST_(1) << 10) #define STATUSB_IP3 11 -#define STATUSF_IP3 (1 << 11) +#define STATUSF_IP3 (_ULCAST_(1) << 11) #define STATUSB_IP4 12 -#define STATUSF_IP4 (1 << 12) +#define STATUSF_IP4 (_ULCAST_(1) << 12) #define STATUSB_IP5 13 -#define STATUSF_IP5 (1 << 13) +#define STATUSF_IP5 (_ULCAST_(1) << 13) #define STATUSB_IP6 14 -#define STATUSF_IP6 (1 << 14) +#define STATUSF_IP6 (_ULCAST_(1) << 14) #define STATUSB_IP7 15 -#define STATUSF_IP7 (1 << 15) +#define STATUSF_IP7 (_ULCAST_(1) << 15) #define STATUSB_IP8 0 -#define STATUSF_IP8 (1 << 0) +#define STATUSF_IP8 (_ULCAST_(1) << 0) #define STATUSB_IP9 1 -#define STATUSF_IP9 (1 << 1) +#define STATUSF_IP9 (_ULCAST_(1) << 1) #define STATUSB_IP10 2 -#define STATUSF_IP10 (1 << 2) +#define STATUSF_IP10 (_ULCAST_(1) << 2) #define STATUSB_IP11 3 -#define STATUSF_IP11 (1 << 3) +#define STATUSF_IP11 (_ULCAST_(1) << 3) #define STATUSB_IP12 4 -#define STATUSF_IP12 (1 << 4) +#define STATUSF_IP12 (_ULCAST_(1) << 4) #define STATUSB_IP13 5 -#define STATUSF_IP13 (1 << 5) +#define STATUSF_IP13 (_ULCAST_(1) << 5) #define STATUSB_IP14 6 -#define STATUSF_IP14 (1 << 6) +#define STATUSF_IP14 (_ULCAST_(1) << 6) #define STATUSB_IP15 7 -#define STATUSF_IP15 (1 << 7) +#define STATUSF_IP15 (_ULCAST_(1) << 7) #define ST0_CH 0x00040000 #define ST0_SR 0x00100000 +#define ST0_TS 0x00200000 #define ST0_BEV 0x00400000 #define ST0_RE 0x02000000 #define ST0_FR 0x04000000 @@ -435,35 +346,36 @@ __BUILD_SET_CP0(config,CP0_CONFIG) * Refer to your MIPS R4xx0 manual, chapter 5 for explanation. */ #define CAUSEB_EXCCODE 2 -#define CAUSEF_EXCCODE (31 << 2) +#define CAUSEF_EXCCODE (_ULCAST_(31) << 2) #define CAUSEB_IP 8 -#define CAUSEF_IP (255 << 8) +#define CAUSEF_IP (_ULCAST_(255) << 8) #define CAUSEB_IP0 8 -#define CAUSEF_IP0 (1 << 8) +#define CAUSEF_IP0 (_ULCAST_(1) << 8) #define CAUSEB_IP1 9 -#define CAUSEF_IP1 (1 << 9) +#define CAUSEF_IP1 (_ULCAST_(1) << 9) #define CAUSEB_IP2 10 -#define CAUSEF_IP2 (1 << 10) +#define CAUSEF_IP2 (_ULCAST_(1) << 10) #define CAUSEB_IP3 11 -#define CAUSEF_IP3 (1 << 11) +#define CAUSEF_IP3 (_ULCAST_(1) << 11) #define CAUSEB_IP4 12 -#define CAUSEF_IP4 (1 << 12) +#define CAUSEF_IP4 (_ULCAST_(1) << 12) #define CAUSEB_IP5 13 -#define CAUSEF_IP5 (1 << 13) +#define CAUSEF_IP5 (_ULCAST_(1) << 13) #define CAUSEB_IP6 14 -#define CAUSEF_IP6 (1 << 14) +#define CAUSEF_IP6 (_ULCAST_(1) << 14) #define CAUSEB_IP7 15 -#define CAUSEF_IP7 (1 << 15) +#define CAUSEF_IP7 (_ULCAST_(1) << 15) #define CAUSEB_IV 23 -#define CAUSEF_IV (1 << 23) +#define CAUSEF_IV (_ULCAST_(1) << 23) #define CAUSEB_CE 28 -#define CAUSEF_CE (3 << 28) +#define CAUSEF_CE (_ULCAST_(3) << 28) #define CAUSEB_BD 31 -#define CAUSEF_BD (1 << 31) +#define CAUSEF_BD (_ULCAST_(1) << 31) /* - * Bits in the coprozessor 0 config register. + * Bits in the coprocessor 0 config register. */ +/* Generic bits. */ #define CONF_CM_CACHABLE_NO_WA 0 #define CONF_CM_CACHABLE_WA 1 #define CONF_CM_UNCACHED 2 @@ -473,11 +385,72 @@ __BUILD_SET_CP0(config,CP0_CONFIG) #define CONF_CM_CACHABLE_CUW 6 #define CONF_CM_CACHABLE_ACCELERATED 7 #define CONF_CM_CMASK 7 -#define CONF_DB (1 << 4) -#define CONF_IB (1 << 5) -#define CONF_SC (1 << 17) -#define CONF_AC (1 << 23) -#define CONF_HALT (1 << 25) +#define CONF_BE (_ULCAST_(1) << 15) + +/* Bits common to various processors. */ +#define CONF_CU (_ULCAST_(1) << 3) +#define CONF_DB (_ULCAST_(1) << 4) +#define CONF_IB (_ULCAST_(1) << 5) +#define CONF_DC (_ULCAST_(7) << 6) +#define CONF_IC (_ULCAST_(7) << 9) +#define CONF_EB (_ULCAST_(1) << 13) +#define CONF_EM (_ULCAST_(1) << 14) +#define CONF_SM (_ULCAST_(1) << 16) +#define CONF_SC (_ULCAST_(1) << 17) +#define CONF_EW (_ULCAST_(3) << 18) +#define CONF_EP (_ULCAST_(15)<< 24) +#define CONF_EC (_ULCAST_(7) << 28) +#define CONF_CM (_ULCAST_(1) << 31) + +/* Bits specific to the R4xx0. */ +#define R4K_CONF_SW (_ULCAST_(1) << 20) +#define R4K_CONF_SS (_ULCAST_(1) << 21) +#define R4K_CONF_SB (_ULCAST_(3) << 22) + +/* Bits specific to the R5000. */ +#define R5K_CONF_SE (_ULCAST_(1) << 12) +#define R5K_CONF_SS (_ULCAST_(3) << 20) + +/* Bits specific to the R10000. */ +#define R10K_CONF_DN (_ULCAST_(3) << 3) +#define R10K_CONF_CT (_ULCAST_(1) << 5) +#define R10K_CONF_PE (_ULCAST_(1) << 6) +#define R10K_CONF_PM (_ULCAST_(3) << 7) +#define R10K_CONF_EC (_ULCAST_(15)<< 9) +#define R10K_CONF_SB (_ULCAST_(1) << 13) +#define R10K_CONF_SK (_ULCAST_(1) << 14) +#define R10K_CONF_SS (_ULCAST_(7) << 16) +#define R10K_CONF_SC (_ULCAST_(7) << 19) +#define R10K_CONF_DC (_ULCAST_(7) << 26) +#define R10K_CONF_IC (_ULCAST_(7) << 29) + +/* Bits specific to the VR41xx. */ +#define VR41_CONF_CS (_ULCAST_(1) << 12) +#define VR41_CONF_M16 (_ULCAST_(1) << 20) +#define VR41_CONF_AD (_ULCAST_(1) << 23) + +/* Bits specific to the R30xx. */ +#define R30XX_CONF_FDM (_ULCAST_(1) << 19) +#define R30XX_CONF_REV (_ULCAST_(1) << 22) +#define R30XX_CONF_AC (_ULCAST_(1) << 23) +#define R30XX_CONF_RF (_ULCAST_(1) << 24) +#define R30XX_CONF_HALT (_ULCAST_(1) << 25) +#define R30XX_CONF_FPINT (_ULCAST_(7) << 26) +#define R30XX_CONF_DBR (_ULCAST_(1) << 29) +#define R30XX_CONF_SB (_ULCAST_(1) << 30) +#define R30XX_CONF_LOCK (_ULCAST_(1) << 31) + +/* Bits specific to the TX49. */ +#define TX49_CONF_DC (_ULCAST_(1) << 16) +#define TX49_CONF_IC (_ULCAST_(1) << 17) /* conflict with CONF_SC */ +#define TX49_CONF_HALT (_ULCAST_(1) << 18) +#define TX49_CONF_CWFON (_ULCAST_(1) << 27) + +/* Bits specific to the MIPS32/64 PRA. */ +#define MIPS_CONF_MT (_ULCAST_(7) << 7) +#define MIPS_CONF_AR (_ULCAST_(7) << 10) +#define MIPS_CONF_AT (_ULCAST_(3) << 13) +#define MIPS_CONF_M (_ULCAST_(1) << 31) /* * R10000 performance counter definitions. @@ -535,4 +508,394 @@ __BUILD_SET_CP0(config,CP0_CONFIG) #define CEB_KERNEL 2 /* Count events in kernel mode EXL = ERL = 0 */ #define CEB_EXL 1 /* Count events with EXL = 1, ERL = 0 */ +#ifndef __ASSEMBLY__ + +/* + * Functions to access the r10k performance counter and control registers + */ +#define read_r10k_perf_cntr(counter) \ +({ unsigned int __res; \ + __asm__ __volatile__( \ + "mfpc\t%0, "STR(counter) \ + : "=r" (__res)); \ + __res;}) + +#define write_r10k_perf_cntr(counter,val) \ + __asm__ __volatile__( \ + "mtpc\t%0, "STR(counter) \ + : : "r" (val)); + +#define read_r10k_perf_cntl(counter) \ +({ unsigned int __res; \ + __asm__ __volatile__( \ + "mfps\t%0, "STR(counter) \ + : "=r" (__res)); \ + __res;}) + +#define write_r10k_perf_cntl(counter,val) \ + __asm__ __volatile__( \ + "mtps\t%0, "STR(counter) \ + : : "r" (val)); + +/* + * Macros to access the system control coprocessor + */ + +#define __read_32bit_c0_register(source, sel) \ +({ int __res; \ + if (sel == 0) \ + __asm__ __volatile__( \ + "mfc0\t%0, " #source "\n\t" \ + : "=r" (__res)); \ + else \ + __asm__ __volatile__( \ + ".set\tmips32\n\t" \ + "mfc0\t%0, " #source ", " #sel "\n\t" \ + ".set\tmips0\n\t" \ + : "=r" (__res)); \ + __res; \ +}) + +#define __read_64bit_c0_register(source, sel) \ +({ unsigned long __res; \ + if (sel == 0) \ + __asm__ __volatile__( \ + ".set\tmips3\n\t" \ + "dmfc0\t%0, " #source "\n\t" \ + ".set\tmips0" \ + : "=r" (__res)); \ + else \ + __asm__ __volatile__( \ + ".set\tmips64\n\t" \ + "dmfc0\t%0, " #source ", " #sel "\n\t" \ + ".set\tmips0" \ + : "=r" (__res)); \ + __res; \ +}) + +#define __write_32bit_c0_register(register, sel, value) \ +do { \ + if (sel == 0) \ + __asm__ __volatile__( \ + "mtc0\t%z0, " #register "\n\t" \ + : : "Jr" (value)); \ + else \ + __asm__ __volatile__( \ + ".set\tmips32\n\t" \ + "mtc0\t%z0, " #register ", " #sel "\n\t" \ + ".set\tmips0" \ + : : "Jr" (value)); \ +} while (0) + +#define __write_64bit_c0_register(register, sel, value) \ +do { \ + if (sel == 0) \ + __asm__ __volatile__( \ + ".set\tmips3\n\t" \ + "dmtc0\t%z0, " #register "\n\t" \ + ".set\tmips0" \ + : : "Jr" (value)); \ + else \ + __asm__ __volatile__( \ + ".set\tmips64\n\t" \ + "dmtc0\t%z0, " #register ", " #sel "\n\t" \ + ".set\tmips0" \ + : : "Jr" (value)); \ +} while (0) + +#define __read_ulong_c0_register(reg, sel) \ + ((sizeof(unsigned long) == 4) ? \ + __read_32bit_c0_register(reg, sel) : \ + __read_64bit_c0_register(reg, sel)) + +#define __write_ulong_c0_register(reg, sel, val) \ +do { \ + if (sizeof(unsigned long) == 4) \ + __write_32bit_c0_register(reg, sel, val); \ + else \ + __write_64bit_c0_register(reg, sel, val); \ +} while (0) + +/* + * These versions are only needed for systems with more than 38 bits of + * physical address space running the 32-bit kernel. That's none atm :-) + */ +#define __read_64bit_c0_split(source, sel) \ +({ \ + unsigned long long val; \ + unsigned long flags; \ + \ + local_irq_save(flags); \ + if (sel == 0) \ + __asm__ __volatile__( \ + ".set\tmips64\n\t" \ + "dmfc0\t%M0, " #source "\n\t" \ + "dsll\t%L0, %M0, 32\n\t" \ + "dsrl\t%M0, %M0, 32\n\t" \ + "dsrl\t%L0, %L0, 32\n\t" \ + ".set\tmips0" \ + : "=r" (val)); \ + else \ + __asm__ __volatile__( \ + ".set\tmips64\n\t" \ + "dmfc0\t%M0, " #source ", " #sel "\n\t" \ + "dsll\t%L0, %M0, 32\n\t" \ + "dsrl\t%M0, %M0, 32\n\t" \ + "dsrl\t%L0, %L0, 32\n\t" \ + ".set\tmips0" \ + : "=r" (val)); \ + local_irq_restore(flags); \ + \ + val; \ +}) + +#define __write_64bit_c0_split(source, sel, val) \ +do { \ + unsigned long flags; \ + \ + local_irq_save(flags); \ + if (sel == 0) \ + __asm__ __volatile__( \ + ".set\tmips64\n\t" \ + "dsll\t%L0, %L0, 32\n\t" \ + "dsrl\t%L0, %L0, 32\n\t" \ + "dsll\t%M0, %M0, 32\n\t" \ + "or\t%L0, %L0, %M0\n\t" \ + "dmtc0\t%L0, " #source "\n\t" \ + ".set\tmips0" \ + : : "r" (val)); \ + else \ + __asm__ __volatile__( \ + ".set\tmips64\n\t" \ + "dsll\t%L0, %L0, 32\n\t" \ + "dsrl\t%L0, %L0, 32\n\t" \ + "dsll\t%M0, %M0, 32\n\t" \ + "or\t%L0, %L0, %M0\n\t" \ + "dmtc0\t%L0, " #source ", " #sel "\n\t" \ + ".set\tmips0" \ + : : "r" (val)); \ + local_irq_restore(flags); \ +} while (0) + +#define read_c0_index() __read_32bit_c0_register($0, 0) +#define write_c0_index(val) __write_32bit_c0_register($0, 0, val) + +#define read_c0_entrylo0() __read_ulong_c0_register($2, 0) +#define write_c0_entrylo0(val) __write_ulong_c0_register($2, 0, val) + +#define read_c0_entrylo1() __read_ulong_c0_register($3, 0) +#define write_c0_entrylo1(val) __write_ulong_c0_register($3, 0, val) + +#define read_c0_conf() __read_32bit_c0_register($3, 0) +#define write_c0_conf(val) __write_32bit_c0_register($3, 0, val) + +#define read_c0_context() __read_ulong_c0_register($4, 0) +#define write_c0_context(val) __write_ulong_c0_register($4, 0, val) + +#define read_c0_pagemask() __read_32bit_c0_register($5, 0) +#define write_c0_pagemask(val) __write_32bit_c0_register($5, 0, val) + +#define read_c0_wired() __read_32bit_c0_register($6, 0) +#define write_c0_wired(val) __write_32bit_c0_register($6, 0, val) + +#define read_c0_info() __read_32bit_c0_register($7, 0) + +#define read_c0_cache() __read_32bit_c0_register($7, 0) /* TX39xx */ +#define write_c0_cache(val) __write_32bit_c0_register($7, 0, val) + +#define read_c0_count() __read_32bit_c0_register($9, 0) +#define write_c0_count(val) __write_32bit_c0_register($9, 0, val) + +#define read_c0_entryhi() __read_ulong_c0_register($10, 0) +#define write_c0_entryhi(val) __write_ulong_c0_register($10, 0, val) + +#define read_c0_compare() __read_32bit_c0_register($11, 0) +#define write_c0_compare(val) __write_32bit_c0_register($11, 0, val) + +#define read_c0_status() __read_32bit_c0_register($12, 0) +#define write_c0_status(val) __write_32bit_c0_register($12, 0, val) + +#define read_c0_cause() __read_32bit_c0_register($13, 0) +#define write_c0_cause(val) __write_32bit_c0_register($13, 0, val) + +#define read_c0_prid() __read_32bit_c0_register($15, 0) + +#define read_c0_config() __read_32bit_c0_register($16, 0) +#define read_c0_config1() __read_32bit_c0_register($16, 1) +#define read_c0_config2() __read_32bit_c0_register($16, 2) +#define read_c0_config3() __read_32bit_c0_register($16, 3) +#define write_c0_config(val) __write_32bit_c0_register($16, 0, val) +#define write_c0_config1(val) __write_32bit_c0_register($16, 1, val) +#define write_c0_config2(val) __write_32bit_c0_register($16, 2, val) +#define write_c0_config3(val) __write_32bit_c0_register($16, 3, val) + +/* + * The WatchLo register. There may be upto 8 of them. + */ +#define read_c0_watchlo0() __read_ulong_c0_register($18, 0) +#define read_c0_watchlo1() __read_ulong_c0_register($18, 1) +#define read_c0_watchlo2() __read_ulong_c0_register($18, 2) +#define read_c0_watchlo3() __read_ulong_c0_register($18, 3) +#define read_c0_watchlo4() __read_ulong_c0_register($18, 4) +#define read_c0_watchlo5() __read_ulong_c0_register($18, 5) +#define read_c0_watchlo6() __read_ulong_c0_register($18, 6) +#define read_c0_watchlo7() __read_ulong_c0_register($18, 7) +#define write_c0_watchlo0(val) __write_ulong_c0_register($18, 0, val) +#define write_c0_watchlo1(val) __write_ulong_c0_register($18, 1, val) +#define write_c0_watchlo2(val) __write_ulong_c0_register($18, 2, val) +#define write_c0_watchlo3(val) __write_ulong_c0_register($18, 3, val) +#define write_c0_watchlo4(val) __write_ulong_c0_register($18, 4, val) +#define write_c0_watchlo5(val) __write_ulong_c0_register($18, 5, val) +#define write_c0_watchlo6(val) __write_ulong_c0_register($18, 6, val) +#define write_c0_watchlo7(val) __write_ulong_c0_register($18, 7, val) + +/* + * The WatchHi register. There may be upto 8 of them. + */ +#define read_c0_watchhi0() __read_32bit_c0_register($19, 0) +#define read_c0_watchhi1() __read_32bit_c0_register($19, 1) +#define read_c0_watchhi2() __read_32bit_c0_register($19, 2) +#define read_c0_watchhi3() __read_32bit_c0_register($19, 3) +#define read_c0_watchhi4() __read_32bit_c0_register($19, 4) +#define read_c0_watchhi5() __read_32bit_c0_register($19, 5) +#define read_c0_watchhi6() __read_32bit_c0_register($19, 6) +#define read_c0_watchhi7() __read_32bit_c0_register($19, 7) + +#define write_c0_watchhi0(val) __write_32bit_c0_register($19, 0, val) +#define write_c0_watchhi1(val) __write_32bit_c0_register($19, 1, val) +#define write_c0_watchhi2(val) __write_32bit_c0_register($19, 2, val) +#define write_c0_watchhi3(val) __write_32bit_c0_register($19, 3, val) +#define write_c0_watchhi4(val) __write_32bit_c0_register($19, 4, val) +#define write_c0_watchhi5(val) __write_32bit_c0_register($19, 5, val) +#define write_c0_watchhi6(val) __write_32bit_c0_register($19, 6, val) +#define write_c0_watchhi7(val) __write_32bit_c0_register($19, 7, val) + +#define read_c0_xcontext() __read_ulong_c0_register($20, 0) +#define write_c0_xcontext(val) __write_ulong_c0_register($20, 0, val) + +#define read_c0_intcontrol() __read_32bit_c0_register($20, 1) +#define write_c0_intcontrol(val) __write_32bit_c0_register($20, 1, val) + +#define read_c0_framemask() __read_32bit_c0_register($21, 0) +#define write_c0_framemask(val) __write_32bit_c0_register($21, 0, val) + +#define read_c0_debug() __read_32bit_c0_register($23, 0) +#define write_c0_debug(val) __write_32bit_c0_register($23, 0, val) + +#define read_c0_depc() __read_ulong_c0_register($24, 0) +#define write_c0_depc(val) __write_ulong_c0_register($24, 0, val) + +#define read_c0_ecc() __read_32bit_c0_register($26, 0) +#define write_c0_ecc(val) __write_32bit_c0_register($26, 0, val) + +#define read_c0_derraddr0() __read_ulong_c0_register($26, 1) +#define write_c0_derraddr0(val) __write_ulong_c0_register($26, 1, val) + +#define read_c0_cacheerr() __read_32bit_c0_register($27, 0) + +#define read_c0_derraddr1() __read_ulong_c0_register($27, 1) +#define write_c0_derraddr1(val) __write_ulong_c0_register($27, 1, val) + +#define read_c0_taglo() __read_32bit_c0_register($28, 0) +#define write_c0_taglo(val) __write_32bit_c0_register($28, 0, val) + +#define read_c0_taghi() __read_32bit_c0_register($29, 0) +#define write_c0_taghi(val) __write_32bit_c0_register($29, 0, val) + +#define read_c0_errorepc() __read_ulong_c0_register($30, 0) +#define write_c0_errorepc(val) __write_ulong_c0_register($30, 0, val) + +/* + * Macros to access the floating point coprocessor control registers + */ +#define read_32bit_cp1_register(source) \ +({ int __res; \ + __asm__ __volatile__( \ + ".set\tpush\n\t" \ + ".set\treorder\n\t" \ + "cfc1\t%0,"STR(source)"\n\t" \ + ".set\tpop" \ + : "=r" (__res)); \ + __res;}) + +/* TLB operations. */ +static inline void tlb_probe(void) +{ + __asm__ __volatile__( + ".set noreorder\n\t" + "tlbp\n\t" + ".set reorder"); +} + +static inline void tlb_read(void) +{ + __asm__ __volatile__( + ".set noreorder\n\t" + "tlbr\n\t" + ".set reorder"); +} + +static inline void tlb_write_indexed(void) +{ + __asm__ __volatile__( + ".set noreorder\n\t" + "tlbwi\n\t" + ".set reorder"); +} + +static inline void tlb_write_random(void) +{ + __asm__ __volatile__( + ".set noreorder\n\t" + "tlbwr\n\t" + ".set reorder"); +} + +/* + * Manipulate bits in a c0 register. + */ +#define __BUILD_SET_C0(name,register) \ +static inline unsigned int \ +set_c0_##name(unsigned int set) \ +{ \ + unsigned int res; \ + \ + res = read_c0_##name(); \ + res |= set; \ + write_c0_##name(res); \ + \ + return res; \ +} \ + \ +static inline unsigned int \ +clear_c0_##name(unsigned int clear) \ +{ \ + unsigned int res; \ + \ + res = read_c0_##name(); \ + res &= ~clear; \ + write_c0_##name(res); \ + \ + return res; \ +} \ + \ +static inline unsigned int \ +change_c0_##name(unsigned int change, unsigned int new) \ +{ \ + unsigned int res; \ + \ + res = read_c0_##name(); \ + res &= ~change; \ + res |= (new & change); \ + write_c0_##name(res); \ + \ + return res; \ +} + +__BUILD_SET_C0(status,CP0_STATUS) +__BUILD_SET_C0(cause,CP0_CAUSE) +__BUILD_SET_C0(config,CP0_CONFIG) + +#endif /* !__ASSEMBLY__ */ + #endif /* _ASM_MIPSREGS_H */ diff --git a/include/asm-mips/mman.h b/include/asm-mips/mman.h index 64dd2c674aed..54b7fb1cb8f3 100644 --- a/include/asm-mips/mman.h +++ b/include/asm-mips/mman.h @@ -1,14 +1,12 @@ /* - * Linux/MIPS memory manager definitions - * * 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) 1995 by Ralf Baechle + * Copyright (C) 1995, 1999, 2002 by Ralf Baechle */ -#ifndef __ASM_MIPS_MMAN_H -#define __ASM_MIPS_MMAN_H +#ifndef _ASM_MMAN_H +#define _ASM_MMAN_H /* * Protections are chosen from these bits, OR'd together. The @@ -16,10 +14,12 @@ * without PROT_READ. The only guarantees are that no writing will be * allowed without PROT_WRITE and no access will be allowed for PROT_NONE. */ -#define PROT_NONE 0x0 /* page can not be accessed */ -#define PROT_READ 0x1 /* page can be read */ -#define PROT_WRITE 0x2 /* page can be written */ -#define PROT_EXEC 0x4 /* page can be executed */ +#define PROT_NONE 0x00 /* page can not be accessed */ +#define PROT_READ 0x01 /* page can be read */ +#define PROT_WRITE 0x02 /* page can be written */ +#define PROT_EXEC 0x04 /* page can be executed */ +/* 0x08 reserved for PROT_EXEC_NOFLUSH */ +#define PROT_SEM 0x10 /* page may be used for atomic ops */ /* * Flags for mmap @@ -42,6 +42,8 @@ #define MAP_DENYWRITE 0x2000 /* ETXTBSY */ #define MAP_EXECUTABLE 0x4000 /* mark it as an executable */ #define MAP_LOCKED 0x8000 /* pages are locked */ +#define MAP_POPULATE 0x10000 /* populate (prefault) pagetables */ +#define MAP_NONBLOCK 0x20000 /* do not block on IO */ /* * Flags for msync @@ -66,4 +68,4 @@ #define MAP_ANON MAP_ANONYMOUS #define MAP_FILE 0 -#endif /* __ASM_MIPS_MMAN_H */ +#endif /* _ASM_MMAN_H */ diff --git a/include/asm-mips/mmu.h b/include/asm-mips/mmu.h index ccd36d26615a..4063edd79623 100644 --- a/include/asm-mips/mmu.h +++ b/include/asm-mips/mmu.h @@ -1,7 +1,6 @@ -#ifndef __MMU_H -#define __MMU_H +#ifndef __ASM_MMU_H +#define __ASM_MMU_H -/* Default "unsigned long" context */ -typedef unsigned long mm_context_t; +typedef unsigned long mm_context_t[NR_CPUS]; -#endif +#endif /* __ASM_MMU_H */ diff --git a/include/asm-mips/mmu_context.h b/include/asm-mips/mmu_context.h index 831ac14231f8..cf19cd768f95 100644 --- a/include/asm-mips/mmu_context.h +++ b/include/asm-mips/mmu_context.h @@ -12,14 +12,28 @@ #define _ASM_MMU_CONTEXT_H #include <linux/config.h> +#include <linux/errno.h> +#include <linux/sched.h> #include <linux/slab.h> -#include <asm/pgalloc.h> +#include <asm/cacheflush.h> +#include <asm/tlbflush.h> -/* Fuck. The f-word is here so you can grep for it :-) */ -extern unsigned long asid_cache; -extern pgd_t *current_pgd[]; +/* + * For the fast tlb miss handlers, we currently keep a per cpu array + * of pointers to the current pgd for each processor. Also, the proc. + * id is stuffed into the context register. This should be changed to + * use the processor id via current->processor, where current is stored + * in watchhi/lo. The context register should be used to contiguously + * map the page tables. + */ +#define TLBMISS_HANDLER_SETUP_PGD(pgd) \ + pgd_current[smp_processor_id()] = (unsigned long)(pgd) +#define TLBMISS_HANDLER_SETUP() \ + write_c0_context((unsigned long) smp_processor_id() << (23 + 3)); \ + TLBMISS_HANDLER_SETUP_PGD(swapper_pg_dir) +extern unsigned long pgd_current[]; -#if defined(CONFIG_CPU_R3000) +#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX) #define ASID_INC 0x40 #define ASID_MASK 0xfc0 @@ -31,6 +45,10 @@ extern pgd_t *current_pgd[]; #endif +#define cpu_context(cpu, mm) ((mm)->context[cpu]) +#define cpu_asid(cpu, mm) (cpu_context((cpu), (mm)) & ASID_MASK) +#define asid_cache(cpu) (cpu_data[cpu].asid_cache) + static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk, unsigned cpu) { } @@ -42,60 +60,67 @@ static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk, #define ASID_VERSION_MASK ((unsigned long)~(ASID_MASK|(ASID_MASK-1))) #define ASID_FIRST_VERSION ((unsigned long)(~ASID_VERSION_MASK) + 1) -extern inline void -get_new_mmu_context(struct mm_struct *mm, unsigned long asid) +static inline void +get_new_mmu_context(struct mm_struct *mm, unsigned long cpu) { + unsigned long asid = asid_cache(cpu); + if (! ((asid += ASID_INC) & ASID_MASK) ) { - flush_tlb_all(); /* start new asid cycle */ - if (!asid) /* fix version if needed */ +#ifdef CONFIG_VTAG_ICACHE + flush_icache_all(); +#endif + local_flush_tlb_all(); /* start new asid cycle */ + if (!asid) /* fix version if needed */ asid = ASID_FIRST_VERSION; } - mm->context = asid_cache = asid; + cpu_context(cpu, mm) = asid_cache(cpu) = asid; } /* * Initialize the context related info for a new mm_struct * instance. */ -extern inline int +static inline int init_new_context(struct task_struct *tsk, struct mm_struct *mm) { -#ifndef CONFIG_SMP - mm->context = 0; -#else - mm->context = (unsigned long)kmalloc(smp_num_cpus * - sizeof(unsigned long), GFP_KERNEL); - /* - * Init the "context" values so that a tlbpid allocation - * happens on the first switch. - */ - if (mm->context == 0) - return -ENOMEM; - memset((void *)mm->context, 0, smp_num_cpus * sizeof(unsigned long)); -#endif + int i; + + for (i = 0; i < num_online_cpus(); i++) + cpu_context(i, mm) = 0; + return 0; } -extern inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, +static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, struct task_struct *tsk, unsigned cpu) { - unsigned long asid = asid_cache; + unsigned long flags; + + local_irq_save(flags); /* Check if our ASID is of an older version and thus invalid */ - if ((next->context ^ asid) & ASID_VERSION_MASK) - get_new_mmu_context(next, asid); + if ((cpu_context(cpu, next) ^ asid_cache(cpu)) & ASID_VERSION_MASK) + get_new_mmu_context(next, cpu); + + write_c0_entryhi(cpu_context(cpu, next)); + TLBMISS_HANDLER_SETUP_PGD(next->pgd); + + /* + * Mark current->active_mm as not "active" anymore. + * We don't want to mislead possible IPI tlb flush routines. + */ + clear_bit(cpu, &prev->cpu_vm_mask); + set_bit(cpu, &next->cpu_vm_mask); - current_pgd[cpu] = next->pgd; - set_entryhi(next->context); + local_irq_restore(flags); } /* * Destroy context related info for an mm_struct that is about * to be put to rest. */ -extern inline void destroy_context(struct mm_struct *mm) +static inline void destroy_context(struct mm_struct *mm) { - /* Nothing to do. */ } #define deactivate_mm(tsk,mm) do { } while (0) @@ -104,14 +129,47 @@ extern inline void destroy_context(struct mm_struct *mm) * After we have set current->mm to a new value, this activates * the context for the new mm so we see the new mappings. */ -extern inline void +static inline void activate_mm(struct mm_struct *prev, struct mm_struct *next) { + unsigned long flags; + int cpu = smp_processor_id(); + + local_irq_save(flags); + /* Unconditionally get a new ASID. */ - get_new_mmu_context(next, asid_cache); + get_new_mmu_context(next, cpu); + + write_c0_entryhi(cpu_context(cpu, next)); + TLBMISS_HANDLER_SETUP_PGD(next->pgd); + + /* mark mmu ownership change */ + clear_bit(cpu, &prev->cpu_vm_mask); + set_bit(cpu, &next->cpu_vm_mask); + + local_irq_restore(flags); +} + +/* + * If mm is currently active_mm, we can't really drop it. Instead, + * we will get a new one for it. + */ +static inline void +drop_mmu_context(struct mm_struct *mm, unsigned cpu) +{ + unsigned long flags; + + local_irq_save(flags); + + if (test_bit(cpu, &mm->cpu_vm_mask)) { + get_new_mmu_context(mm, cpu); + write_c0_entryhi(cpu_asid(cpu, mm)); + } else { + /* will get a new context next time */ + cpu_context(cpu, mm) = 0; + } - current_pgd[smp_processor_id()] = next->pgd; - set_entryhi(next->context); + local_irq_restore(flags); } #endif /* _ASM_MMU_CONTEXT_H */ diff --git a/include/asm-mips/module.h b/include/asm-mips/module.h index 042dae3cbab4..39bcec8134e8 100644 --- a/include/asm-mips/module.h +++ b/include/asm-mips/module.h @@ -1,67 +1,14 @@ -#ifndef _ASM_MIPS_MODULE_H -#define _ASM_MIPS_MODULE_H -/* - * This file contains the mips architecture specific module code. - */ +#ifndef _ASM_MODULE_H +#define _ASM_MODULE_H -#include <linux/module.h> -#include <asm/uaccess.h> - -#define module_map(x) vmalloc(x) -#define module_unmap(x) vfree(x) -#define module_arch_init(x) mips_module_init(x) -#define arch_init_modules(x) mips_init_modules(x) - -/* - * This must match in size and layout the data created by - * modutils/obj/obj-mips.c - */ -struct archdata { +struct mod_arch_specific { + /* Data Bus Error exception tables */ const struct exception_table_entry *dbe_table_start; const struct exception_table_entry *dbe_table_end; }; -static inline int -mips_module_init(struct module *mod) -{ - struct archdata *archdata; - - if (!mod_member_present(mod, archdata_end)) - return 0; - - archdata = (struct archdata *)(mod->archdata_start); - if (!mod_archdata_member_present(mod, struct archdata, dbe_table_end)) - return 0; - - if (archdata->dbe_table_start > archdata->dbe_table_end || - (archdata->dbe_table_start && - !((unsigned long)archdata->dbe_table_start >= - ((unsigned long)mod + mod->size_of_struct) && - ((unsigned long)archdata->dbe_table_end < - (unsigned long)mod + mod->size))) || - (((unsigned long)archdata->dbe_table_start - - (unsigned long)archdata->dbe_table_end) % - sizeof(struct exception_table_entry))) { - printk(KERN_ERR - "module_arch_init: archdata->dbe_table_* invalid.\n"); - return 1; - } - - return 0; -} - -static inline void -mips_init_modules(struct module *mod) -{ - extern const struct exception_table_entry __start___dbe_table[]; - extern const struct exception_table_entry __stop___dbe_table[]; - static struct archdata archdata = { - dbe_table_start: __start___dbe_table, - dbe_table_end: __stop___dbe_table, - }; - - mod->archdata_start = (char *)&archdata; - mod->archdata_end = mod->archdata_start + sizeof(archdata); -} +#define Elf_Shdr Elf32_Shdr +#define Elf_Sym Elf32_Sym +#define Elf_Ehdr Elf32_Ehdr -#endif /* _ASM_MIPS_MODULE_H */ +#endif /* _ASM_MODULE_H */ diff --git a/include/asm-mips/msgbuf.h b/include/asm-mips/msgbuf.h index 81d6e71452cd..1d4d90fb4a5e 100644 --- a/include/asm-mips/msgbuf.h +++ b/include/asm-mips/msgbuf.h @@ -1,7 +1,7 @@ #ifndef _ASM_MSGBUF_H #define _ASM_MSGBUF_H -/* +/* * The msqid64_ds structure for alpha architecture. * Note extra padding because this structure is passed back and forth * between kernel and user space. @@ -13,15 +13,18 @@ struct msqid64_ds { struct ipc64_perm msg_perm; __kernel_time_t msg_stime; /* last msgsnd time */ + unsigned long __unused1; __kernel_time_t msg_rtime; /* last msgrcv time */ + unsigned long __unused2; __kernel_time_t msg_ctime; /* last change time */ + unsigned long __unused3; unsigned long msg_cbytes; /* current number of bytes on queue */ unsigned long msg_qnum; /* number of messages in queue */ unsigned long msg_qbytes; /* max number of bytes on queue */ __kernel_pid_t msg_lspid; /* pid of last msgsnd */ __kernel_pid_t msg_lrpid; /* last receive pid */ - unsigned long __unused1; - unsigned long __unused2; + unsigned long __unused4; + unsigned long __unused5; }; #endif /* _ASM_MSGBUF_H */ diff --git a/include/asm-mips/mv64340.h b/include/asm-mips/mv64340.h new file mode 100644 index 000000000000..d7d89bf0f6dc --- /dev/null +++ b/include/asm-mips/mv64340.h @@ -0,0 +1,1037 @@ +/******************************************************************************* +* mv64340.h - MV-64340 Internal registers definition file. +* +* Copyright 2002 Momentum Computer, Inc. +* Copyright 2002 GALILEO TECHNOLOGY, LTD. +* +* 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. +* +*******************************************************************************/ + +#ifndef __MV64340_H__ +#define __MV64340_H__ + +#include <asm/mv64340_dep.h> + +/****************************************/ +/* Processor Address Space */ +/****************************************/ + +/* DDR SDRAM BAR and size registers */ + +#define MV64340_CS_0_BASE_ADDR 0x008 +#define MV64340_CS_0_SIZE 0x010 +#define MV64340_CS_1_BASE_ADDR 0x208 +#define MV64340_CS_1_SIZE 0x210 +#define MV64340_CS_2_BASE_ADDR 0x018 +#define MV64340_CS_2_SIZE 0x020 +#define MV64340_CS_3_BASE_ADDR 0x218 +#define MV64340_CS_3_SIZE 0x220 + +/* Devices BAR and size registers */ + +#define MV64340_DEV_CS0_BASE_ADDR 0x028 +#define MV64340_DEV_CS0_SIZE 0x030 +#define MV64340_DEV_CS1_BASE_ADDR 0x228 +#define MV64340_DEV_CS1_SIZE 0x230 +#define MV64340_DEV_CS2_BASE_ADDR 0x248 +#define MV64340_DEV_CS2_SIZE 0x250 +#define MV64340_DEV_CS3_BASE_ADDR 0x038 +#define MV64340_DEV_CS3_SIZE 0x040 +#define MV64340_BOOTCS_BASE_ADDR 0x238 +#define MV64340_BOOTCS_SIZE 0x240 + +/* PCI 0 BAR and size registers */ + +#define MV64340_PCI_0_IO_BASE_ADDR 0x048 +#define MV64340_PCI_0_IO_SIZE 0x050 +#define MV64340_PCI_0_MEMORY0_BASE_ADDR 0x058 +#define MV64340_PCI_0_MEMORY0_SIZE 0x060 +#define MV64340_PCI_0_MEMORY1_BASE_ADDR 0x080 +#define MV64340_PCI_0_MEMORY1_SIZE 0x088 +#define MV64340_PCI_0_MEMORY2_BASE_ADDR 0x258 +#define MV64340_PCI_0_MEMORY2_SIZE 0x260 +#define MV64340_PCI_0_MEMORY3_BASE_ADDR 0x280 +#define MV64340_PCI_0_MEMORY3_SIZE 0x288 + +/* PCI 1 BAR and size registers */ +#define MV64340_PCI_1_IO_BASE_ADDR 0x090 +#define MV64340_PCI_1_IO_SIZE 0x098 +#define MV64340_PCI_1_MEMORY0_BASE_ADDR 0x0a0 +#define MV64340_PCI_1_MEMORY0_SIZE 0x0a8 +#define MV64340_PCI_1_MEMORY1_BASE_ADDR 0x0b0 +#define MV64340_PCI_1_MEMORY1_SIZE 0x0b8 +#define MV64340_PCI_1_MEMORY2_BASE_ADDR 0x2a0 +#define MV64340_PCI_1_MEMORY2_SIZE 0x2a8 +#define MV64340_PCI_1_MEMORY3_BASE_ADDR 0x2b0 +#define MV64340_PCI_1_MEMORY3_SIZE 0x2b8 + +/* SRAM base address */ +#define MV64340_INTEGRATED_SRAM_BASE_ADDR 0x268 + +/* internal registers space base address */ +#define MV64340_INTERNAL_SPACE_BASE_ADDR 0x068 + +/* Enables the CS , DEV_CS , PCI 0 and PCI 1 + windows above */ +#define MV64340_BASE_ADDR_ENABLE 0x278 + +/****************************************/ +/* PCI remap registers */ +/****************************************/ + /* PCI 0 */ +#define MV64340_PCI_0_IO_ADDR_REMAP 0x0f0 +#define MV64340_PCI_0_MEMORY0_LOW_ADDR_REMAP 0x0f8 +#define MV64340_PCI_0_MEMORY0_HIGH_ADDR_REMAP 0x320 +#define MV64340_PCI_0_MEMORY1_LOW_ADDR_REMAP 0x100 +#define MV64340_PCI_0_MEMORY1_HIGH_ADDR_REMAP 0x328 +#define MV64340_PCI_0_MEMORY2_LOW_ADDR_REMAP 0x2f8 +#define MV64340_PCI_0_MEMORY2_HIGH_ADDR_REMAP 0x330 +#define MV64340_PCI_0_MEMORY3_LOW_ADDR_REMAP 0x300 +#define MV64340_PCI_0_MEMORY3_HIGH_ADDR_REMAP 0x338 + /* PCI 1 */ +#define MV64340_PCI_1_IO_ADDR_REMAP 0x108 +#define MV64340_PCI_1_MEMORY0_LOW_ADDR_REMAP 0x110 +#define MV64340_PCI_1_MEMORY0_HIGH_ADDR_REMAP 0x340 +#define MV64340_PCI_1_MEMORY1_LOW_ADDR_REMAP 0x118 +#define MV64340_PCI_1_MEMORY1_HIGH_ADDR_REMAP 0x348 +#define MV64340_PCI_1_MEMORY2_LOW_ADDR_REMAP 0x310 +#define MV64340_PCI_1_MEMORY2_HIGH_ADDR_REMAP 0x350 +#define MV64340_PCI_1_MEMORY3_LOW_ADDR_REMAP 0x318 +#define MV64340_PCI_1_MEMORY3_HIGH_ADDR_REMAP 0x358 + +#define MV64340_CPU_PCI_0_HEADERS_RETARGET_CONTROL 0x3b0 +#define MV64340_CPU_PCI_0_HEADERS_RETARGET_BASE 0x3b8 +#define MV64340_CPU_PCI_1_HEADERS_RETARGET_CONTROL 0x3c0 +#define MV64340_CPU_PCI_1_HEADERS_RETARGET_BASE 0x3c8 +#define MV64340_CPU_GE_HEADERS_RETARGET_CONTROL 0x3d0 +#define MV64340_CPU_GE_HEADERS_RETARGET_BASE 0x3d8 +#define MV64340_CPU_IDMA_HEADERS_RETARGET_CONTROL 0x3e0 +#define MV64340_CPU_IDMA_HEADERS_RETARGET_BASE 0x3e8 + +/****************************************/ +/* CPU Control Registers */ +/****************************************/ + +#define MV64340_CPU_CONFIG 0x000 +#define MV64340_CPU_MODE 0x120 +#define MV64340_CPU_MASTER_CONTROL 0x160 +#define MV64340_CPU_CROSS_BAR_CONTROL_LOW 0x150 +#define MV64340_CPU_CROSS_BAR_CONTROL_HIGH 0x158 +#define MV64340_CPU_CROSS_BAR_TIMEOUT 0x168 + +/****************************************/ +/* SMP RegisterS */ +/****************************************/ + +#define MV64340_SMP_WHO_AM_I 0x200 +#define MV64340_SMP_CPU0_DOORBELL 0x214 +#define MV64340_SMP_CPU0_DOORBELL_CLEAR 0x21C +#define MV64340_SMP_CPU1_DOORBELL 0x224 +#define MV64340_SMP_CPU1_DOORBELL_CLEAR 0x22C +#define MV64340_SMP_CPU0_DOORBELL_MASK 0x234 +#define MV64340_SMP_CPU1_DOORBELL_MASK 0x23C +#define MV64340_SMP_SEMAPHOR0 0x244 +#define MV64340_SMP_SEMAPHOR1 0x24c +#define MV64340_SMP_SEMAPHOR2 0x254 +#define MV64340_SMP_SEMAPHOR3 0x25c +#define MV64340_SMP_SEMAPHOR4 0x264 +#define MV64340_SMP_SEMAPHOR5 0x26c +#define MV64340_SMP_SEMAPHOR6 0x274 +#define MV64340_SMP_SEMAPHOR7 0x27c + +/****************************************/ +/* CPU Sync Barrier Register */ +/****************************************/ + +#define MV64340_CPU_0_SYNC_BARRIER_TRIGGER 0x0c0 +#define MV64340_CPU_0_SYNC_BARRIER_VIRTUAL 0x0c8 +#define MV64340_CPU_1_SYNC_BARRIER_TRIGGER 0x0d0 +#define MV64340_CPU_1_SYNC_BARRIER_VIRTUAL 0x0d8 + +/****************************************/ +/* CPU Access Protect */ +/****************************************/ + +#define MV64340_CPU_PROTECT_WINDOW_0_BASE_ADDR 0x180 +#define MV64340_CPU_PROTECT_WINDOW_0_SIZE 0x188 +#define MV64340_CPU_PROTECT_WINDOW_1_BASE_ADDR 0x190 +#define MV64340_CPU_PROTECT_WINDOW_1_SIZE 0x198 +#define MV64340_CPU_PROTECT_WINDOW_2_BASE_ADDR 0x1a0 +#define MV64340_CPU_PROTECT_WINDOW_2_SIZE 0x1a8 +#define MV64340_CPU_PROTECT_WINDOW_3_BASE_ADDR 0x1b0 +#define MV64340_CPU_PROTECT_WINDOW_3_SIZE 0x1b8 + + +/****************************************/ +/* CPU Error Report */ +/****************************************/ + +#define MV64340_CPU_ERROR_ADDR_LOW 0x070 +#define MV64340_CPU_ERROR_ADDR_HIGH 0x078 +#define MV64340_CPU_ERROR_DATA_LOW 0x128 +#define MV64340_CPU_ERROR_DATA_HIGH 0x130 +#define MV64340_CPU_ERROR_PARITY 0x138 +#define MV64340_CPU_ERROR_CAUSE 0x140 +#define MV64340_CPU_ERROR_MASK 0x148 + +/****************************************/ +/* CPU Interface Debug Registers */ +/****************************************/ + +#define MV64340_PUNIT_SLAVE_DEBUG_LOW 0x360 +#define MV64340_PUNIT_SLAVE_DEBUG_HIGH 0x368 +#define MV64340_PUNIT_MASTER_DEBUG_LOW 0x370 +#define MV64340_PUNIT_MASTER_DEBUG_HIGH 0x378 +#define MV64340_PUNIT_MMASK 0x3e4 + +/****************************************/ +/* Integrated SRAM Registers */ +/****************************************/ + +#define MV64340_SRAM_CONFIG 0x380 +#define MV64340_SRAM_TEST_MODE 0X3F4 +#define MV64340_SRAM_ERROR_CAUSE 0x388 +#define MV64340_SRAM_ERROR_ADDR 0x390 +#define MV64340_SRAM_ERROR_ADDR_HIGH 0X3F8 +#define MV64340_SRAM_ERROR_DATA_LOW 0x398 +#define MV64340_SRAM_ERROR_DATA_HIGH 0x3a0 +#define MV64340_SRAM_ERROR_DATA_PARITY 0x3a8 + +/****************************************/ +/* SDRAM Configuration */ +/****************************************/ + +#define MV64340_SDRAM_CONFIG 0x1400 +#define MV64340_D_UNIT_CONTROL_LOW 0x1404 +#define MV64340_D_UNIT_CONTROL_HIGH 0x1424 +#define MV64340_SDRAM_TIMING_CONTROL_LOW 0x1408 +#define MV64340_SDRAM_TIMING_CONTROL_HIGH 0x140c +#define MV64340_SDRAM_ADDR_CONTROL 0x1410 +#define MV64340_SDRAM_OPEN_PAGES_CONTROL 0x1414 +#define MV64340_SDRAM_OPERATION 0x1418 +#define MV64340_SDRAM_MODE 0x141c +#define MV64340_EXTENDED_DRAM_MODE 0x1420 +#define MV64340_SDRAM_CROSS_BAR_CONTROL_LOW 0x1430 +#define MV64340_SDRAM_CROSS_BAR_CONTROL_HIGH 0x1434 +#define MV64340_SDRAM_CROSS_BAR_TIMEOUT 0x1438 +#define MV64340_SDRAM_ADDR_CTRL_PADS_CALIBRATION 0x14c0 +#define MV64340_SDRAM_DATA_PADS_CALIBRATION 0x14c4 + +/****************************************/ +/* SDRAM Error Report */ +/****************************************/ + +#define MV64340_SDRAM_ERROR_DATA_LOW 0x1444 +#define MV64340_SDRAM_ERROR_DATA_HIGH 0x1440 +#define MV64340_SDRAM_ERROR_ADDR 0x1450 +#define MV64340_SDRAM_RECEIVED_ECC 0x1448 +#define MV64340_SDRAM_CALCULATED_ECC 0x144c +#define MV64340_SDRAM_ECC_CONTROL 0x1454 +#define MV64340_SDRAM_ECC_ERROR_COUNTER 0x1458 + +/******************************************/ +/* Controlled Delay Line (CDL) Registers */ +/******************************************/ + +#define MV64340_DFCDL_CONFIG0 0x1480 +#define MV64340_DFCDL_CONFIG1 0x1484 +#define MV64340_DLL_WRITE 0x1488 +#define MV64340_DLL_READ 0x148c +#define MV64340_SRAM_ADDR 0x1490 +#define MV64340_SRAM_DATA0 0x1494 +#define MV64340_SRAM_DATA1 0x1498 +#define MV64340_SRAM_DATA2 0x149c +#define MV64340_DFCL_PROBE 0x14a0 + +/******************************************/ +/* Debug Registers */ +/******************************************/ + +#define MV64340_DUNIT_DEBUG_LOW 0x1460 +#define MV64340_DUNIT_DEBUG_HIGH 0x1464 +#define MV64340_DUNIT_MMASK 0X1b40 + +/****************************************/ +/* Device Parameters */ +/****************************************/ + +#define MV64340_DEVICE_BANK0_PARAMETERS 0x45c +#define MV64340_DEVICE_BANK1_PARAMETERS 0x460 +#define MV64340_DEVICE_BANK2_PARAMETERS 0x464 +#define MV64340_DEVICE_BANK3_PARAMETERS 0x468 +#define MV64340_DEVICE_BOOT_BANK_PARAMETERS 0x46c +#define MV64340_DEVICE_INTERFACE_CONTROL 0x4c0 +#define MV64340_DEVICE_INTERFACE_CROSS_BAR_CONTROL_LOW 0x4c8 +#define MV64340_DEVICE_INTERFACE_CROSS_BAR_CONTROL_HIGH 0x4cc +#define MV64340_DEVICE_INTERFACE_CROSS_BAR_TIMEOUT 0x4c4 + +/****************************************/ +/* Device interrupt registers */ +/****************************************/ + +#define MV64340_DEVICE_INTERRUPT_CAUSE 0x4d0 +#define MV64340_DEVICE_INTERRUPT_MASK 0x4d4 +#define MV64340_DEVICE_ERROR_ADDR 0x4d8 +#define MV64340_DEVICE_ERROR_DATA 0x4dc +#define MV64340_DEVICE_ERROR_PARITY 0x4e0 + +/****************************************/ +/* Device debug registers */ +/****************************************/ + +#define MV64340_DEVICE_DEBUG_LOW 0x4e4 +#define MV64340_DEVICE_DEBUG_HIGH 0x4e8 +#define MV64340_RUNIT_MMASK 0x4f0 + +/****************************************/ +/* PCI Slave Address Decoding registers */ +/****************************************/ + +#define MV64340_PCI_0_CS_0_BANK_SIZE 0xc08 +#define MV64340_PCI_1_CS_0_BANK_SIZE 0xc88 +#define MV64340_PCI_0_CS_1_BANK_SIZE 0xd08 +#define MV64340_PCI_1_CS_1_BANK_SIZE 0xd88 +#define MV64340_PCI_0_CS_2_BANK_SIZE 0xc0c +#define MV64340_PCI_1_CS_2_BANK_SIZE 0xc8c +#define MV64340_PCI_0_CS_3_BANK_SIZE 0xd0c +#define MV64340_PCI_1_CS_3_BANK_SIZE 0xd8c +#define MV64340_PCI_0_DEVCS_0_BANK_SIZE 0xc10 +#define MV64340_PCI_1_DEVCS_0_BANK_SIZE 0xc90 +#define MV64340_PCI_0_DEVCS_1_BANK_SIZE 0xd10 +#define MV64340_PCI_1_DEVCS_1_BANK_SIZE 0xd90 +#define MV64340_PCI_0_DEVCS_2_BANK_SIZE 0xd18 +#define MV64340_PCI_1_DEVCS_2_BANK_SIZE 0xd98 +#define MV64340_PCI_0_DEVCS_3_BANK_SIZE 0xc14 +#define MV64340_PCI_1_DEVCS_3_BANK_SIZE 0xc94 +#define MV64340_PCI_0_DEVCS_BOOT_BANK_SIZE 0xd14 +#define MV64340_PCI_1_DEVCS_BOOT_BANK_SIZE 0xd94 +#define MV64340_PCI_0_P2P_MEM0_BAR_SIZE 0xd1c +#define MV64340_PCI_1_P2P_MEM0_BAR_SIZE 0xd9c +#define MV64340_PCI_0_P2P_MEM1_BAR_SIZE 0xd20 +#define MV64340_PCI_1_P2P_MEM1_BAR_SIZE 0xda0 +#define MV64340_PCI_0_P2P_I_O_BAR_SIZE 0xd24 +#define MV64340_PCI_1_P2P_I_O_BAR_SIZE 0xda4 +#define MV64340_PCI_0_CPU_BAR_SIZE 0xd28 +#define MV64340_PCI_1_CPU_BAR_SIZE 0xda8 +#define MV64340_PCI_0_INTERNAL_SRAM_BAR_SIZE 0xe00 +#define MV64340_PCI_1_INTERNAL_SRAM_BAR_SIZE 0xe80 +#define MV64340_PCI_0_EXPANSION_ROM_BAR_SIZE 0xd2c +#define MV64340_PCI_1_EXPANSION_ROM_BAR_SIZE 0xd9c +#define MV64340_PCI_0_BASE_ADDR_REG_ENABLE 0xc3c +#define MV64340_PCI_1_BASE_ADDR_REG_ENABLE 0xcbc +#define MV64340_PCI_0_CS_0_BASE_ADDR_REMAP 0xc48 +#define MV64340_PCI_1_CS_0_BASE_ADDR_REMAP 0xcc8 +#define MV64340_PCI_0_CS_1_BASE_ADDR_REMAP 0xd48 +#define MV64340_PCI_1_CS_1_BASE_ADDR_REMAP 0xdc8 +#define MV64340_PCI_0_CS_2_BASE_ADDR_REMAP 0xc4c +#define MV64340_PCI_1_CS_2_BASE_ADDR_REMAP 0xccc +#define MV64340_PCI_0_CS_3_BASE_ADDR_REMAP 0xd4c +#define MV64340_PCI_1_CS_3_BASE_ADDR_REMAP 0xdcc +#define MV64340_PCI_0_CS_0_BASE_HIGH_ADDR_REMAP 0xF04 +#define MV64340_PCI_1_CS_0_BASE_HIGH_ADDR_REMAP 0xF84 +#define MV64340_PCI_0_CS_1_BASE_HIGH_ADDR_REMAP 0xF08 +#define MV64340_PCI_1_CS_1_BASE_HIGH_ADDR_REMAP 0xF88 +#define MV64340_PCI_0_CS_2_BASE_HIGH_ADDR_REMAP 0xF0C +#define MV64340_PCI_1_CS_2_BASE_HIGH_ADDR_REMAP 0xF8C +#define MV64340_PCI_0_CS_3_BASE_HIGH_ADDR_REMAP 0xF10 +#define MV64340_PCI_1_CS_3_BASE_HIGH_ADDR_REMAP 0xF90 +#define MV64340_PCI_0_DEVCS_0_BASE_ADDR_REMAP 0xc50 +#define MV64340_PCI_1_DEVCS_0_BASE_ADDR_REMAP 0xcd0 +#define MV64340_PCI_0_DEVCS_1_BASE_ADDR_REMAP 0xd50 +#define MV64340_PCI_1_DEVCS_1_BASE_ADDR_REMAP 0xdd0 +#define MV64340_PCI_0_DEVCS_2_BASE_ADDR_REMAP 0xd58 +#define MV64340_PCI_1_DEVCS_2_BASE_ADDR_REMAP 0xdd8 +#define MV64340_PCI_0_DEVCS_3_BASE_ADDR_REMAP 0xc54 +#define MV64340_PCI_1_DEVCS_3_BASE_ADDR_REMAP 0xcd4 +#define MV64340_PCI_0_DEVCS_BOOTCS_BASE_ADDR_REMAP 0xd54 +#define MV64340_PCI_1_DEVCS_BOOTCS_BASE_ADDR_REMAP 0xdd4 +#define MV64340_PCI_0_P2P_MEM0_BASE_ADDR_REMAP_LOW 0xd5c +#define MV64340_PCI_1_P2P_MEM0_BASE_ADDR_REMAP_LOW 0xddc +#define MV64340_PCI_0_P2P_MEM0_BASE_ADDR_REMAP_HIGH 0xd60 +#define MV64340_PCI_1_P2P_MEM0_BASE_ADDR_REMAP_HIGH 0xde0 +#define MV64340_PCI_0_P2P_MEM1_BASE_ADDR_REMAP_LOW 0xd64 +#define MV64340_PCI_1_P2P_MEM1_BASE_ADDR_REMAP_LOW 0xde4 +#define MV64340_PCI_0_P2P_MEM1_BASE_ADDR_REMAP_HIGH 0xd68 +#define MV64340_PCI_1_P2P_MEM1_BASE_ADDR_REMAP_HIGH 0xde8 +#define MV64340_PCI_0_P2P_I_O_BASE_ADDR_REMAP 0xd6c +#define MV64340_PCI_1_P2P_I_O_BASE_ADDR_REMAP 0xdec +#define MV64340_PCI_0_CPU_BASE_ADDR_REMAP_LOW 0xd70 +#define MV64340_PCI_1_CPU_BASE_ADDR_REMAP_LOW 0xdf0 +#define MV64340_PCI_0_CPU_BASE_ADDR_REMAP_HIGH 0xd74 +#define MV64340_PCI_1_CPU_BASE_ADDR_REMAP_HIGH 0xdf4 +#define MV64340_PCI_0_INTEGRATED_SRAM_BASE_ADDR_REMAP 0xf00 +#define MV64340_PCI_1_INTEGRATED_SRAM_BASE_ADDR_REMAP 0xf80 +#define MV64340_PCI_0_EXPANSION_ROM_BASE_ADDR_REMAP 0xf38 +#define MV64340_PCI_1_EXPANSION_ROM_BASE_ADDR_REMAP 0xfb8 +#define MV64340_PCI_0_ADDR_DECODE_CONTROL 0xd3c +#define MV64340_PCI_1_ADDR_DECODE_CONTROL 0xdbc +#define MV64340_PCI_0_HEADERS_RETARGET_CONTROL 0xF40 +#define MV64340_PCI_1_HEADERS_RETARGET_CONTROL 0xFc0 +#define MV64340_PCI_0_HEADERS_RETARGET_BASE 0xF44 +#define MV64340_PCI_1_HEADERS_RETARGET_BASE 0xFc4 +#define MV64340_PCI_0_HEADERS_RETARGET_HIGH 0xF48 +#define MV64340_PCI_1_HEADERS_RETARGET_HIGH 0xFc8 + +/***********************************/ +/* PCI Control Register Map */ +/***********************************/ + +#define MV64340_PCI_0_DLL_STATUS_AND_COMMAND 0x1d20 +#define MV64340_PCI_1_DLL_STATUS_AND_COMMAND 0x1da0 +#define MV64340_PCI_0_MPP_PADS_DRIVE_CONTROL 0x1d1C +#define MV64340_PCI_1_MPP_PADS_DRIVE_CONTROL 0x1d9C +#define MV64340_PCI_0_COMMAND 0xc00 +#define MV64340_PCI_1_COMMAND 0xc80 +#define MV64340_PCI_0_MODE 0xd00 +#define MV64340_PCI_1_MODE 0xd80 +#define MV64340_PCI_0_RETRY 0xc04 +#define MV64340_PCI_1_RETRY 0xc84 +#define MV64340_PCI_0_READ_BUFFER_DISCARD_TIMER 0xd04 +#define MV64340_PCI_1_READ_BUFFER_DISCARD_TIMER 0xd84 +#define MV64340_PCI_0_MSI_TRIGGER_TIMER 0xc38 +#define MV64340_PCI_1_MSI_TRIGGER_TIMER 0xcb8 +#define MV64340_PCI_0_ARBITER_CONTROL 0x1d00 +#define MV64340_PCI_1_ARBITER_CONTROL 0x1d80 +#define MV64340_PCI_0_CROSS_BAR_CONTROL_LOW 0x1d08 +#define MV64340_PCI_1_CROSS_BAR_CONTROL_LOW 0x1d88 +#define MV64340_PCI_0_CROSS_BAR_CONTROL_HIGH 0x1d0c +#define MV64340_PCI_1_CROSS_BAR_CONTROL_HIGH 0x1d8c +#define MV64340_PCI_0_CROSS_BAR_TIMEOUT 0x1d04 +#define MV64340_PCI_1_CROSS_BAR_TIMEOUT 0x1d84 +#define MV64340_PCI_0_SYNC_BARRIER_TRIGGER_REG 0x1D18 +#define MV64340_PCI_1_SYNC_BARRIER_TRIGGER_REG 0x1D98 +#define MV64340_PCI_0_SYNC_BARRIER_VIRTUAL_REG 0x1d10 +#define MV64340_PCI_1_SYNC_BARRIER_VIRTUAL_REG 0x1d90 +#define MV64340_PCI_0_P2P_CONFIG 0x1d14 +#define MV64340_PCI_1_P2P_CONFIG 0x1d94 + +#define MV64340_PCI_0_ACCESS_CONTROL_BASE_0_LOW 0x1e00 +#define MV64340_PCI_0_ACCESS_CONTROL_BASE_0_HIGH 0x1e04 +#define MV64340_PCI_0_ACCESS_CONTROL_SIZE_0 0x1e08 +#define MV64340_PCI_0_ACCESS_CONTROL_BASE_1_LOW 0x1e10 +#define MV64340_PCI_0_ACCESS_CONTROL_BASE_1_HIGH 0x1e14 +#define MV64340_PCI_0_ACCESS_CONTROL_SIZE_1 0x1e18 +#define MV64340_PCI_0_ACCESS_CONTROL_BASE_2_LOW 0x1e20 +#define MV64340_PCI_0_ACCESS_CONTROL_BASE_2_HIGH 0x1e24 +#define MV64340_PCI_0_ACCESS_CONTROL_SIZE_2 0x1e28 +#define MV64340_PCI_0_ACCESS_CONTROL_BASE_3_LOW 0x1e30 +#define MV64340_PCI_0_ACCESS_CONTROL_BASE_3_HIGH 0x1e34 +#define MV64340_PCI_0_ACCESS_CONTROL_SIZE_3 0x1e38 +#define MV64340_PCI_0_ACCESS_CONTROL_BASE_4_LOW 0x1e40 +#define MV64340_PCI_0_ACCESS_CONTROL_BASE_4_HIGH 0x1e44 +#define MV64340_PCI_0_ACCESS_CONTROL_SIZE_4 0x1e48 +#define MV64340_PCI_0_ACCESS_CONTROL_BASE_5_LOW 0x1e50 +#define MV64340_PCI_0_ACCESS_CONTROL_BASE_5_HIGH 0x1e54 +#define MV64340_PCI_0_ACCESS_CONTROL_SIZE_5 0x1e58 + +#define MV64340_PCI_1_ACCESS_CONTROL_BASE_0_LOW 0x1e80 +#define MV64340_PCI_1_ACCESS_CONTROL_BASE_0_HIGH 0x1e84 +#define MV64340_PCI_1_ACCESS_CONTROL_SIZE_0 0x1e88 +#define MV64340_PCI_1_ACCESS_CONTROL_BASE_1_LOW 0x1e90 +#define MV64340_PCI_1_ACCESS_CONTROL_BASE_1_HIGH 0x1e94 +#define MV64340_PCI_1_ACCESS_CONTROL_SIZE_1 0x1e98 +#define MV64340_PCI_1_ACCESS_CONTROL_BASE_2_LOW 0x1ea0 +#define MV64340_PCI_1_ACCESS_CONTROL_BASE_2_HIGH 0x1ea4 +#define MV64340_PCI_1_ACCESS_CONTROL_SIZE_2 0x1ea8 +#define MV64340_PCI_1_ACCESS_CONTROL_BASE_3_LOW 0x1eb0 +#define MV64340_PCI_1_ACCESS_CONTROL_BASE_3_HIGH 0x1eb4 +#define MV64340_PCI_1_ACCESS_CONTROL_SIZE_3 0x1eb8 +#define MV64340_PCI_1_ACCESS_CONTROL_BASE_4_LOW 0x1ec0 +#define MV64340_PCI_1_ACCESS_CONTROL_BASE_4_HIGH 0x1ec4 +#define MV64340_PCI_1_ACCESS_CONTROL_SIZE_4 0x1ec8 +#define MV64340_PCI_1_ACCESS_CONTROL_BASE_5_LOW 0x1ed0 +#define MV64340_PCI_1_ACCESS_CONTROL_BASE_5_HIGH 0x1ed4 +#define MV64340_PCI_1_ACCESS_CONTROL_SIZE_5 0x1ed8 + +/****************************************/ +/* PCI Configuration Access Registers */ +/****************************************/ + +#define MV64340_PCI_0_CONFIG_ADDR 0xcf8 +#define MV64340_PCI_0_CONFIG_DATA_VIRTUAL_REG 0xcfc +#define MV64340_PCI_1_CONFIG_ADDR 0xc78 +#define MV64340_PCI_1_CONFIG_DATA_VIRTUAL_REG 0xc7c +#define MV64340_PCI_0_INTERRUPT_ACKNOWLEDGE_VIRTUAL_REG 0xc34 +#define MV64340_PCI_1_INTERRUPT_ACKNOWLEDGE_VIRTUAL_REG 0xcb4 + +/****************************************/ +/* PCI Error Report Registers */ +/****************************************/ + +#define MV64340_PCI_0_SERR_MASK 0xc28 +#define MV64340_PCI_1_SERR_MASK 0xca8 +#define MV64340_PCI_0_ERROR_ADDR_LOW 0x1d40 +#define MV64340_PCI_1_ERROR_ADDR_LOW 0x1dc0 +#define MV64340_PCI_0_ERROR_ADDR_HIGH 0x1d44 +#define MV64340_PCI_1_ERROR_ADDR_HIGH 0x1dc4 +#define MV64340_PCI_0_ERROR_ATTRIBUTE 0x1d48 +#define MV64340_PCI_1_ERROR_ATTRIBUTE 0x1dc8 +#define MV64340_PCI_0_ERROR_COMMAND 0x1d50 +#define MV64340_PCI_1_ERROR_COMMAND 0x1dd0 +#define MV64340_PCI_0_ERROR_CAUSE 0x1d58 +#define MV64340_PCI_1_ERROR_CAUSE 0x1dd8 +#define MV64340_PCI_0_ERROR_MASK 0x1d5c +#define MV64340_PCI_1_ERROR_MASK 0x1ddc + +/****************************************/ +/* PCI Debug Registers */ +/****************************************/ + +#define MV64340_PCI_0_MMASK 0X1D24 +#define MV64340_PCI_1_MMASK 0X1DA4 + +/*********************************************/ +/* PCI Configuration, Function 0, Registers */ +/*********************************************/ + +#define MV64340_PCI_DEVICE_AND_VENDOR_ID 0x000 +#define MV64340_PCI_STATUS_AND_COMMAND 0x004 +#define MV64340_PCI_CLASS_CODE_AND_REVISION_ID 0x008 +#define MV64340_PCI_BIST_HEADER_TYPE_LATENCY_TIMER_CACHE_LINE 0x00C + +#define MV64340_PCI_SCS_0_BASE_ADDR_LOW 0x010 +#define MV64340_PCI_SCS_0_BASE_ADDR_HIGH 0x014 +#define MV64340_PCI_SCS_1_BASE_ADDR_LOW 0x018 +#define MV64340_PCI_SCS_1_BASE_ADDR_HIGH 0x01C +#define MV64340_PCI_INTERNAL_REG_MEM_MAPPED_BASE_ADDR_LOW 0x020 +#define MV64340_PCI_INTERNAL_REG_MEM_MAPPED_BASE_ADDR_HIGH 0x024 +#define MV64340_PCI_SUBSYSTEM_ID_AND_SUBSYSTEM_VENDOR_ID 0x02c +#define MV64340_PCI_EXPANSION_ROM_BASE_ADDR_REG 0x030 +#define MV64340_PCI_CAPABILTY_LIST_POINTER 0x034 +#define MV64340_PCI_INTERRUPT_PIN_AND_LINE 0x03C + /* capability list */ +#define MV64340_PCI_POWER_MANAGEMENT_CAPABILITY 0x040 +#define MV64340_PCI_POWER_MANAGEMENT_STATUS_AND_CONTROL 0x044 +#define MV64340_PCI_VPD_ADDR 0x048 +#define MV64340_PCI_VPD_DATA 0x04c +#define MV64340_PCI_MSI_MESSAGE_CONTROL 0x050 +#define MV64340_PCI_MSI_MESSAGE_ADDR 0x054 +#define MV64340_PCI_MSI_MESSAGE_UPPER_ADDR 0x058 +#define MV64340_PCI_MSI_MESSAGE_DATA 0x05c +#define MV64340_PCI_X_COMMAND 0x060 +#define MV64340_PCI_X_STATUS 0x064 +#define MV64340_PCI_COMPACT_PCI_HOT_SWAP 0x068 + +/***********************************************/ +/* PCI Configuration, Function 1, Registers */ +/***********************************************/ + +#define MV64340_PCI_SCS_2_BASE_ADDR_LOW 0x110 +#define MV64340_PCI_SCS_2_BASE_ADDR_HIGH 0x114 +#define MV64340_PCI_SCS_3_BASE_ADDR_LOW 0x118 +#define MV64340_PCI_SCS_3_BASE_ADDR_HIGH 0x11c +#define MV64340_PCI_INTERNAL_SRAM_BASE_ADDR_LOW 0x120 +#define MV64340_PCI_INTERNAL_SRAM_BASE_ADDR_HIGH 0x124 + +/***********************************************/ +/* PCI Configuration, Function 2, Registers */ +/***********************************************/ + +#define MV64340_PCI_DEVCS_0_BASE_ADDR_LOW 0x210 +#define MV64340_PCI_DEVCS_0_BASE_ADDR_HIGH 0x214 +#define MV64340_PCI_DEVCS_1_BASE_ADDR_LOW 0x218 +#define MV64340_PCI_DEVCS_1_BASE_ADDR_HIGH 0x21c +#define MV64340_PCI_DEVCS_2_BASE_ADDR_LOW 0x220 +#define MV64340_PCI_DEVCS_2_BASE_ADDR_HIGH 0x224 + +/***********************************************/ +/* PCI Configuration, Function 3, Registers */ +/***********************************************/ + +#define MV64340_PCI_DEVCS_3_BASE_ADDR_LOW 0x310 +#define MV64340_PCI_DEVCS_3_BASE_ADDR_HIGH 0x314 +#define MV64340_PCI_BOOT_CS_BASE_ADDR_LOW 0x318 +#define MV64340_PCI_BOOT_CS_BASE_ADDR_HIGH 0x31c +#define MV64340_PCI_CPU_BASE_ADDR_LOW 0x220 +#define MV64340_PCI_CPU_BASE_ADDR_HIGH 0x224 + +/***********************************************/ +/* PCI Configuration, Function 4, Registers */ +/***********************************************/ + +#define MV64340_PCI_P2P_MEM0_BASE_ADDR_LOW 0x410 +#define MV64340_PCI_P2P_MEM0_BASE_ADDR_HIGH 0x414 +#define MV64340_PCI_P2P_MEM1_BASE_ADDR_LOW 0x418 +#define MV64340_PCI_P2P_MEM1_BASE_ADDR_HIGH 0x41c +#define MV64340_PCI_P2P_I_O_BASE_ADDR 0x420 +#define MV64340_PCI_INTERNAL_REGS_I_O_MAPPED_BASE_ADDR 0x424 + +/****************************************/ +/* Messaging Unit Registers (I20) */ +/****************************************/ + +#define MV64340_I2O_INBOUND_MESSAGE_REG0_PCI_0_SIDE 0x010 +#define MV64340_I2O_INBOUND_MESSAGE_REG1_PCI_0_SIDE 0x014 +#define MV64340_I2O_OUTBOUND_MESSAGE_REG0_PCI_0_SIDE 0x018 +#define MV64340_I2O_OUTBOUND_MESSAGE_REG1_PCI_0_SIDE 0x01C +#define MV64340_I2O_INBOUND_DOORBELL_REG_PCI_0_SIDE 0x020 +#define MV64340_I2O_INBOUND_INTERRUPT_CAUSE_REG_PCI_0_SIDE 0x024 +#define MV64340_I2O_INBOUND_INTERRUPT_MASK_REG_PCI_0_SIDE 0x028 +#define MV64340_I2O_OUTBOUND_DOORBELL_REG_PCI_0_SIDE 0x02C +#define MV64340_I2O_OUTBOUND_INTERRUPT_CAUSE_REG_PCI_0_SIDE 0x030 +#define MV64340_I2O_OUTBOUND_INTERRUPT_MASK_REG_PCI_0_SIDE 0x034 +#define MV64340_I2O_INBOUND_QUEUE_PORT_VIRTUAL_REG_PCI_0_SIDE 0x040 +#define MV64340_I2O_OUTBOUND_QUEUE_PORT_VIRTUAL_REG_PCI_0_SIDE 0x044 +#define MV64340_I2O_QUEUE_CONTROL_REG_PCI_0_SIDE 0x050 +#define MV64340_I2O_QUEUE_BASE_ADDR_REG_PCI_0_SIDE 0x054 +#define MV64340_I2O_INBOUND_FREE_HEAD_POINTER_REG_PCI_0_SIDE 0x060 +#define MV64340_I2O_INBOUND_FREE_TAIL_POINTER_REG_PCI_0_SIDE 0x064 +#define MV64340_I2O_INBOUND_POST_HEAD_POINTER_REG_PCI_0_SIDE 0x068 +#define MV64340_I2O_INBOUND_POST_TAIL_POINTER_REG_PCI_0_SIDE 0x06C +#define MV64340_I2O_OUTBOUND_FREE_HEAD_POINTER_REG_PCI_0_SIDE 0x070 +#define MV64340_I2O_OUTBOUND_FREE_TAIL_POINTER_REG_PCI_0_SIDE 0x074 +#define MV64340_I2O_OUTBOUND_POST_HEAD_POINTER_REG_PCI_0_SIDE 0x0F8 +#define MV64340_I2O_OUTBOUND_POST_TAIL_POINTER_REG_PCI_0_SIDE 0x0FC + +#define MV64340_I2O_INBOUND_MESSAGE_REG0_PCI_1_SIDE 0x090 +#define MV64340_I2O_INBOUND_MESSAGE_REG1_PCI_1_SIDE 0x094 +#define MV64340_I2O_OUTBOUND_MESSAGE_REG0_PCI_1_SIDE 0x098 +#define MV64340_I2O_OUTBOUND_MESSAGE_REG1_PCI_1_SIDE 0x09C +#define MV64340_I2O_INBOUND_DOORBELL_REG_PCI_1_SIDE 0x0A0 +#define MV64340_I2O_INBOUND_INTERRUPT_CAUSE_REG_PCI_1_SIDE 0x0A4 +#define MV64340_I2O_INBOUND_INTERRUPT_MASK_REG_PCI_1_SIDE 0x0A8 +#define MV64340_I2O_OUTBOUND_DOORBELL_REG_PCI_1_SIDE 0x0AC +#define MV64340_I2O_OUTBOUND_INTERRUPT_CAUSE_REG_PCI_1_SIDE 0x0B0 +#define MV64340_I2O_OUTBOUND_INTERRUPT_MASK_REG_PCI_1_SIDE 0x0B4 +#define MV64340_I2O_INBOUND_QUEUE_PORT_VIRTUAL_REG_PCI_1_SIDE 0x0C0 +#define MV64340_I2O_OUTBOUND_QUEUE_PORT_VIRTUAL_REG_PCI_1_SIDE 0x0C4 +#define MV64340_I2O_QUEUE_CONTROL_REG_PCI_1_SIDE 0x0D0 +#define MV64340_I2O_QUEUE_BASE_ADDR_REG_PCI_1_SIDE 0x0D4 +#define MV64340_I2O_INBOUND_FREE_HEAD_POINTER_REG_PCI_1_SIDE 0x0E0 +#define MV64340_I2O_INBOUND_FREE_TAIL_POINTER_REG_PCI_1_SIDE 0x0E4 +#define MV64340_I2O_INBOUND_POST_HEAD_POINTER_REG_PCI_1_SIDE 0x0E8 +#define MV64340_I2O_INBOUND_POST_TAIL_POINTER_REG_PCI_1_SIDE 0x0EC +#define MV64340_I2O_OUTBOUND_FREE_HEAD_POINTER_REG_PCI_1_SIDE 0x0F0 +#define MV64340_I2O_OUTBOUND_FREE_TAIL_POINTER_REG_PCI_1_SIDE 0x0F4 +#define MV64340_I2O_OUTBOUND_POST_HEAD_POINTER_REG_PCI_1_SIDE 0x078 +#define MV64340_I2O_OUTBOUND_POST_TAIL_POINTER_REG_PCI_1_SIDE 0x07C + +#define MV64340_I2O_INBOUND_MESSAGE_REG0_CPU0_SIDE 0x1C10 +#define MV64340_I2O_INBOUND_MESSAGE_REG1_CPU0_SIDE 0x1C14 +#define MV64340_I2O_OUTBOUND_MESSAGE_REG0_CPU0_SIDE 0x1C18 +#define MV64340_I2O_OUTBOUND_MESSAGE_REG1_CPU0_SIDE 0x1C1C +#define MV64340_I2O_INBOUND_DOORBELL_REG_CPU0_SIDE 0x1C20 +#define MV64340_I2O_INBOUND_INTERRUPT_CAUSE_REG_CPU0_SIDE 0x1C24 +#define MV64340_I2O_INBOUND_INTERRUPT_MASK_REG_CPU0_SIDE 0x1C28 +#define MV64340_I2O_OUTBOUND_DOORBELL_REG_CPU0_SIDE 0x1C2C +#define MV64340_I2O_OUTBOUND_INTERRUPT_CAUSE_REG_CPU0_SIDE 0x1C30 +#define MV64340_I2O_OUTBOUND_INTERRUPT_MASK_REG_CPU0_SIDE 0x1C34 +#define MV64340_I2O_INBOUND_QUEUE_PORT_VIRTUAL_REG_CPU0_SIDE 0x1C40 +#define MV64340_I2O_OUTBOUND_QUEUE_PORT_VIRTUAL_REG_CPU0_SIDE 0x1C44 +#define MV64340_I2O_QUEUE_CONTROL_REG_CPU0_SIDE 0x1C50 +#define MV64340_I2O_QUEUE_BASE_ADDR_REG_CPU0_SIDE 0x1C54 +#define MV64340_I2O_INBOUND_FREE_HEAD_POINTER_REG_CPU0_SIDE 0x1C60 +#define MV64340_I2O_INBOUND_FREE_TAIL_POINTER_REG_CPU0_SIDE 0x1C64 +#define MV64340_I2O_INBOUND_POST_HEAD_POINTER_REG_CPU0_SIDE 0x1C68 +#define MV64340_I2O_INBOUND_POST_TAIL_POINTER_REG_CPU0_SIDE 0x1C6C +#define MV64340_I2O_OUTBOUND_FREE_HEAD_POINTER_REG_CPU0_SIDE 0x1C70 +#define MV64340_I2O_OUTBOUND_FREE_TAIL_POINTER_REG_CPU0_SIDE 0x1C74 +#define MV64340_I2O_OUTBOUND_POST_HEAD_POINTER_REG_CPU0_SIDE 0x1CF8 +#define MV64340_I2O_OUTBOUND_POST_TAIL_POINTER_REG_CPU0_SIDE 0x1CFC +#define MV64340_I2O_INBOUND_MESSAGE_REG0_CPU1_SIDE 0x1C90 +#define MV64340_I2O_INBOUND_MESSAGE_REG1_CPU1_SIDE 0x1C94 +#define MV64340_I2O_OUTBOUND_MESSAGE_REG0_CPU1_SIDE 0x1C98 +#define MV64340_I2O_OUTBOUND_MESSAGE_REG1_CPU1_SIDE 0x1C9C +#define MV64340_I2O_INBOUND_DOORBELL_REG_CPU1_SIDE 0x1CA0 +#define MV64340_I2O_INBOUND_INTERRUPT_CAUSE_REG_CPU1_SIDE 0x1CA4 +#define MV64340_I2O_INBOUND_INTERRUPT_MASK_REG_CPU1_SIDE 0x1CA8 +#define MV64340_I2O_OUTBOUND_DOORBELL_REG_CPU1_SIDE 0x1CAC +#define MV64340_I2O_OUTBOUND_INTERRUPT_CAUSE_REG_CPU1_SIDE 0x1CB0 +#define MV64340_I2O_OUTBOUND_INTERRUPT_MASK_REG_CPU1_SIDE 0x1CB4 +#define MV64340_I2O_INBOUND_QUEUE_PORT_VIRTUAL_REG_CPU1_SIDE 0x1CC0 +#define MV64340_I2O_OUTBOUND_QUEUE_PORT_VIRTUAL_REG_CPU1_SIDE 0x1CC4 +#define MV64340_I2O_QUEUE_CONTROL_REG_CPU1_SIDE 0x1CD0 +#define MV64340_I2O_QUEUE_BASE_ADDR_REG_CPU1_SIDE 0x1CD4 +#define MV64340_I2O_INBOUND_FREE_HEAD_POINTER_REG_CPU1_SIDE 0x1CE0 +#define MV64340_I2O_INBOUND_FREE_TAIL_POINTER_REG_CPU1_SIDE 0x1CE4 +#define MV64340_I2O_INBOUND_POST_HEAD_POINTER_REG_CPU1_SIDE 0x1CE8 +#define MV64340_I2O_INBOUND_POST_TAIL_POINTER_REG_CPU1_SIDE 0x1CEC +#define MV64340_I2O_OUTBOUND_FREE_HEAD_POINTER_REG_CPU1_SIDE 0x1CF0 +#define MV64340_I2O_OUTBOUND_FREE_TAIL_POINTER_REG_CPU1_SIDE 0x1CF4 +#define MV64340_I2O_OUTBOUND_POST_HEAD_POINTER_REG_CPU1_SIDE 0x1C78 +#define MV64340_I2O_OUTBOUND_POST_TAIL_POINTER_REG_CPU1_SIDE 0x1C7C + +/****************************************/ +/* Ethernet Unit Registers */ +/****************************************/ + +#define MV64340_ETH_PHY_ADDR_REG 0x2000 +#define MV64340_ETH_SMI_REG 0x2004 +#define MV64340_ETH_UNIT_DEFAULT_ADDR_REG 0x2008 +#define MV64340_ETH_UNIT_DEFAULTID_REG 0x200c +#define MV64340_ETH_UNIT_INTERRUPT_CAUSE_REG 0x2080 +#define MV64340_ETH_UNIT_INTERRUPT_MASK_REG 0x2084 +#define MV64340_ETH_UNIT_INTERNAL_USE_REG 0x24fc +#define MV64340_ETH_UNIT_ERROR_ADDR_REG 0x2094 +#define MV64340_ETH_BAR_0 0x2200 +#define MV64340_ETH_BAR_1 0x2208 +#define MV64340_ETH_BAR_2 0x2210 +#define MV64340_ETH_BAR_3 0x2218 +#define MV64340_ETH_BAR_4 0x2220 +#define MV64340_ETH_BAR_5 0x2228 +#define MV64340_ETH_SIZE_REG_0 0x2204 +#define MV64340_ETH_SIZE_REG_1 0x220c +#define MV64340_ETH_SIZE_REG_2 0x2214 +#define MV64340_ETH_SIZE_REG_3 0x221c +#define MV64340_ETH_SIZE_REG_4 0x2224 +#define MV64340_ETH_SIZE_REG_5 0x222c +#define MV64340_ETH_HEADERS_RETARGET_BASE_REG 0x2230 +#define MV64340_ETH_HEADERS_RETARGET_CONTROL_REG 0x2234 +#define MV64340_ETH_HIGH_ADDR_REMAP_REG_0 0x2280 +#define MV64340_ETH_HIGH_ADDR_REMAP_REG_1 0x2284 +#define MV64340_ETH_HIGH_ADDR_REMAP_REG_2 0x2288 +#define MV64340_ETH_HIGH_ADDR_REMAP_REG_3 0x228c +#define MV64340_ETH_BASE_ADDR_ENABLE_REG 0x2290 +#define MV64340_ETH_ACCESS_PROTECTION_REG(port) (0x2294 + (port<<2)) +#define MV64340_ETH_MIB_COUNTERS_BASE(port) (0x3000 + (port<<7)) +#define MV64340_ETH_PORT_CONFIG_REG(port) (0x2400 + (port<<10)) +#define MV64340_ETH_PORT_CONFIG_EXTEND_REG(port) (0x2404 + (port<<10)) +#define MV64340_ETH_MII_SERIAL_PARAMETRS_REG(port) (0x2408 + (port<<10)) +#define MV64340_ETH_GMII_SERIAL_PARAMETRS_REG(port) (0x240c + (port<<10)) +#define MV64340_ETH_VLAN_ETHERTYPE_REG(port) (0x2410 + (port<<10)) +#define MV64340_ETH_MAC_ADDR_LOW(port) (0x2414 + (port<<10)) +#define MV64340_ETH_MAC_ADDR_HIGH(port) (0x2418 + (port<<10)) +#define MV64340_ETH_SDMA_CONFIG_REG(port) (0x241c + (port<<10)) +#define MV64340_ETH_DSCP_0(port) (0x2420 + (port<<10)) +#define MV64340_ETH_DSCP_1(port) (0x2424 + (port<<10)) +#define MV64340_ETH_DSCP_2(port) (0x2428 + (port<<10)) +#define MV64340_ETH_DSCP_3(port) (0x242c + (port<<10)) +#define MV64340_ETH_DSCP_4(port) (0x2430 + (port<<10)) +#define MV64340_ETH_DSCP_5(port) (0x2434 + (port<<10)) +#define MV64340_ETH_DSCP_6(port) (0x2438 + (port<<10)) +#define MV64340_ETH_PORT_SERIAL_CONTROL_REG(port) (0x243c + (port<<10)) +#define MV64340_ETH_VLAN_PRIORITY_TAG_TO_PRIORITY(port) (0x2440 + (port<<10)) +#define MV64340_ETH_PORT_STATUS_REG(port) (0x2444 + (port<<10)) +#define MV64340_ETH_TRANSMIT_QUEUE_COMMAND_REG(port) (0x2448 + (port<<10)) +#define MV64340_ETH_TX_QUEUE_FIXED_PRIORITY(port) (0x244c + (port<<10)) +#define MV64340_ETH_PORT_TX_TOKEN_BUCKET_RATE_CONFIG(port) (0x2450 + (port<<10)) +#define MV64340_ETH_MAXIMUM_TRANSMIT_UNIT(port) (0x2458 + (port<<10)) +#define MV64340_ETH_PORT_MAXIMUM_TOKEN_BUCKET_SIZE(port) (0x245c + (port<<10)) +#define MV64340_ETH_INTERRUPT_CAUSE_REG(port) (0x2460 + (port<<10)) +#define MV64340_ETH_INTERRUPT_CAUSE_EXTEND_REG(port) (0x2464 + (port<<10)) +#define MV64340_ETH_INTERRUPT_MASK_REG(port) (0x2468 + (port<<10)) +#define MV64340_ETH_INTERRUPT_EXTEND_MASK_REG(port) (0x246c + (port<<10)) +#define MV64340_ETH_RX_FIFO_URGENT_THRESHOLD_REG(port) (0x2470 + (port<<10)) +#define MV64340_ETH_TX_FIFO_URGENT_THRESHOLD_REG(port) (0x2474 + (port<<10)) +#define MV64340_ETH_RX_MINIMAL_FRAME_SIZE_REG(port) (0x247c + (port<<10)) +#define MV64340_ETH_RX_DISCARDED_FRAMES_COUNTER(port) (0x2484 + (port<<10) +#define MV64340_ETH_PORT_DEBUG_0_REG(port) (0x248c + (port<<10)) +#define MV64340_ETH_PORT_DEBUG_1_REG(port) (0x2490 + (port<<10)) +#define MV64340_ETH_PORT_INTERNAL_ADDR_ERROR_REG(port) (0x2494 + (port<<10)) +#define MV64340_ETH_INTERNAL_USE_REG(port) (0x24fc + (port<<10)) +#define MV64340_ETH_RECEIVE_QUEUE_COMMAND_REG(port) (0x2680 + (port<<10)) +#define MV64340_ETH_CURRENT_SERVED_TX_DESC_PTR(port) (0x2684 + (port<<10)) +#define MV64340_ETH_RX_CURRENT_QUEUE_DESC_PTR_0(port) (0x260c + (port<<10)) +#define MV64340_ETH_RX_CURRENT_QUEUE_DESC_PTR_1(port) (0x261c + (port<<10)) +#define MV64340_ETH_RX_CURRENT_QUEUE_DESC_PTR_2(port) (0x262c + (port<<10)) +#define MV64340_ETH_RX_CURRENT_QUEUE_DESC_PTR_3(port) (0x263c + (port<<10)) +#define MV64340_ETH_RX_CURRENT_QUEUE_DESC_PTR_4(port) (0x264c + (port<<10)) +#define MV64340_ETH_RX_CURRENT_QUEUE_DESC_PTR_5(port) (0x265c + (port<<10)) +#define MV64340_ETH_RX_CURRENT_QUEUE_DESC_PTR_6(port) (0x266c + (port<<10)) +#define MV64340_ETH_RX_CURRENT_QUEUE_DESC_PTR_7(port) (0x267c + (port<<10)) +#define MV64340_ETH_TX_CURRENT_QUEUE_DESC_PTR_0(port) (0x26c0 + (port<<10)) +#define MV64340_ETH_TX_CURRENT_QUEUE_DESC_PTR_1(port) (0x26c4 + (port<<10)) +#define MV64340_ETH_TX_CURRENT_QUEUE_DESC_PTR_2(port) (0x26c8 + (port<<10)) +#define MV64340_ETH_TX_CURRENT_QUEUE_DESC_PTR_3(port) (0x26cc + (port<<10)) +#define MV64340_ETH_TX_CURRENT_QUEUE_DESC_PTR_4(port) (0x26d0 + (port<<10)) +#define MV64340_ETH_TX_CURRENT_QUEUE_DESC_PTR_5(port) (0x26d4 + (port<<10)) +#define MV64340_ETH_TX_CURRENT_QUEUE_DESC_PTR_6(port) (0x26d8 + (port<<10)) +#define MV64340_ETH_TX_CURRENT_QUEUE_DESC_PTR_7(port) (0x26dc + (port<<10)) +#define MV64340_ETH_TX_QUEUE_0_TOKEN_BUCKET_COUNT(port) (0x2700 + (port<<10)) +#define MV64340_ETH_TX_QUEUE_1_TOKEN_BUCKET_COUNT(port) (0x2710 + (port<<10)) +#define MV64340_ETH_TX_QUEUE_2_TOKEN_BUCKET_COUNT(port) (0x2720 + (port<<10)) +#define MV64340_ETH_TX_QUEUE_3_TOKEN_BUCKET_COUNT(port) (0x2730 + (port<<10)) +#define MV64340_ETH_TX_QUEUE_4_TOKEN_BUCKET_COUNT(port) (0x2740 + (port<<10)) +#define MV64340_ETH_TX_QUEUE_5_TOKEN_BUCKET_COUNT(port) (0x2750 + (port<<10)) +#define MV64340_ETH_TX_QUEUE_6_TOKEN_BUCKET_COUNT(port) (0x2760 + (port<<10)) +#define MV64340_ETH_TX_QUEUE_7_TOKEN_BUCKET_COUNT(port) (0x2770 + (port<<10)) +#define MV64340_ETH_TX_QUEUE_0_TOKEN_BUCKET_CONFIG(port) (0x2704 + (port<<10)) +#define MV64340_ETH_TX_QUEUE_1_TOKEN_BUCKET_CONFIG(port) (0x2714 + (port<<10)) +#define MV64340_ETH_TX_QUEUE_2_TOKEN_BUCKET_CONFIG(port) (0x2724 + (port<<10)) +#define MV64340_ETH_TX_QUEUE_3_TOKEN_BUCKET_CONFIG(port) (0x2734 + (port<<10)) +#define MV64340_ETH_TX_QUEUE_4_TOKEN_BUCKET_CONFIG(port) (0x2744 + (port<<10)) +#define MV64340_ETH_TX_QUEUE_5_TOKEN_BUCKET_CONFIG(port) (0x2754 + (port<<10)) +#define MV64340_ETH_TX_QUEUE_6_TOKEN_BUCKET_CONFIG(port) (0x2764 + (port<<10)) +#define MV64340_ETH_TX_QUEUE_7_TOKEN_BUCKET_CONFIG(port) (0x2774 + (port<<10)) +#define MV64340_ETH_TX_QUEUE_0_ARBITER_CONFIG(port) (0x2708 + (port<<10)) +#define MV64340_ETH_TX_QUEUE_1_ARBITER_CONFIG(port) (0x2718 + (port<<10)) +#define MV64340_ETH_TX_QUEUE_2_ARBITER_CONFIG(port) (0x2728 + (port<<10)) +#define MV64340_ETH_TX_QUEUE_3_ARBITER_CONFIG(port) (0x2738 + (port<<10)) +#define MV64340_ETH_TX_QUEUE_4_ARBITER_CONFIG(port) (0x2748 + (port<<10)) +#define MV64340_ETH_TX_QUEUE_5_ARBITER_CONFIG(port) (0x2758 + (port<<10)) +#define MV64340_ETH_TX_QUEUE_6_ARBITER_CONFIG(port) (0x2768 + (port<<10)) +#define MV64340_ETH_TX_QUEUE_7_ARBITER_CONFIG(port) (0x2778 + (port<<10)) +#define MV64340_ETH_PORT_TX_TOKEN_BUCKET_COUNT(port) (0x2780 + (port<<10)) +#define MV64340_ETH_DA_FILTER_SPECIAL_MULTICAST_TABLE_BASE(port) (0x3400 + (port<<10)) +#define MV64340_ETH_DA_FILTER_OTHER_MULTICAST_TABLE_BASE(port) (0x3500 + (port<<10)) +#define MV64340_ETH_DA_FILTER_UNICAST_TABLE_BASE(port) (0x3600 + (port<<10)) + +/*******************************************/ +/* CUNIT Registers */ +/*******************************************/ + + /* Address Decoding Register Map */ + +#define MV64340_CUNIT_BASE_ADDR_REG0 0xf200 +#define MV64340_CUNIT_BASE_ADDR_REG1 0xf208 +#define MV64340_CUNIT_BASE_ADDR_REG2 0xf210 +#define MV64340_CUNIT_BASE_ADDR_REG3 0xf218 +#define MV64340_CUNIT_SIZE0 0xf204 +#define MV64340_CUNIT_SIZE1 0xf20c +#define MV64340_CUNIT_SIZE2 0xf214 +#define MV64340_CUNIT_SIZE3 0xf21c +#define MV64340_CUNIT_HIGH_ADDR_REMAP_REG0 0xf240 +#define MV64340_CUNIT_HIGH_ADDR_REMAP_REG1 0xf244 +#define MV64340_CUNIT_BASE_ADDR_ENABLE_REG 0xf250 +#define MV64340_MPSC0_ACCESS_PROTECTION_REG 0xf254 +#define MV64340_MPSC1_ACCESS_PROTECTION_REG 0xf258 +#define MV64340_CUNIT_INTERNAL_SPACE_BASE_ADDR_REG 0xf25C + + /* Error Report Registers */ + +#define MV64340_CUNIT_INTERRUPT_CAUSE_REG 0xf310 +#define MV64340_CUNIT_INTERRUPT_MASK_REG 0xf314 +#define MV64340_CUNIT_ERROR_ADDR 0xf318 + + /* Cunit Control Registers */ + +#define MV64340_CUNIT_ARBITER_CONTROL_REG 0xf300 +#define MV64340_CUNIT_CONFIG_REG 0xb40c +#define MV64340_CUNIT_CRROSBAR_TIMEOUT_REG 0xf304 + + /* Cunit Debug Registers */ + +#define MV64340_CUNIT_DEBUG_LOW 0xf340 +#define MV64340_CUNIT_DEBUG_HIGH 0xf344 +#define MV64340_CUNIT_MMASK 0xf380 + + /* MPSCs Clocks Routing Registers */ + +#define MV64340_MPSC_ROUTING_REG 0xb400 +#define MV64340_MPSC_RX_CLOCK_ROUTING_REG 0xb404 +#define MV64340_MPSC_TX_CLOCK_ROUTING_REG 0xb408 + + /* MPSCs Interrupts Registers */ + +#define MV64340_MPSC_CAUSE_REG(port) (0xb804 + (port<<3)) +#define MV64340_MPSC_MASK_REG(port) (0xb884 + (port<<3)) + +#define MV64340_MPSC_MAIN_CONFIG_LOW(port) (0x8000 + (port<<12)) +#define MV64340_MPSC_MAIN_CONFIG_HIGH(port) (0x8004 + (port<<12)) +#define MV64340_MPSC_PROTOCOL_CONFIG(port) (0x8008 + (port<<12)) +#define MV64340_MPSC_CHANNEL_REG1(port) (0x800c + (port<<12)) +#define MV64340_MPSC_CHANNEL_REG2(port) (0x8010 + (port<<12)) +#define MV64340_MPSC_CHANNEL_REG3(port) (0x8014 + (port<<12)) +#define MV64340_MPSC_CHANNEL_REG4(port) (0x8018 + (port<<12)) +#define MV64340_MPSC_CHANNEL_REG5(port) (0x801c + (port<<12)) +#define MV64340_MPSC_CHANNEL_REG6(port) (0x8020 + (port<<12)) +#define MV64340_MPSC_CHANNEL_REG7(port) (0x8024 + (port<<12)) +#define MV64340_MPSC_CHANNEL_REG8(port) (0x8028 + (port<<12)) +#define MV64340_MPSC_CHANNEL_REG9(port) (0x802c + (port<<12)) +#define MV64340_MPSC_CHANNEL_REG10(port) (0x8030 + (port<<12)) + + /* MPSC0 Registers */ + + +/***************************************/ +/* SDMA Registers */ +/***************************************/ + +#define MV64340_SDMA_CONFIG_REG(channel) (0x4000 + (channel<<13)) +#define MV64340_SDMA_COMMAND_REG(channel) (0x4008 + (channel<<13)) +#define MV64340_SDMA_CURRENT_RX_DESCRIPTOR_POINTER(channel) (0x4810 + (channel<<13)) +#define MV64340_SDMA_CURRENT_TX_DESCRIPTOR_POINTER(channel) (0x4c10 + (channel<<13)) +#define MV64340_SDMA_FIRST_TX_DESCRIPTOR_POINTER(channel) (0x4c14 + (channel<<13)) + +#define MV64340_SDMA_CAUSE_REG 0xb800 +#define MV64340_SDMA_MASK_REG 0xb880 + +/* BRG Interrupts */ + +#define MV64340_BRG_CONFIG_REG(brg) (0xb200 + (brg<<3)) +#define MV64340_BRG_BAUDE_TUNING_REG(brg) (0xb208 + (brg<<3)) +#define MV64340_BRG_CAUSE_REG 0xb834 +#define MV64340_BRG_MASK_REG 0xb8b4 + +/****************************************/ +/* DMA Channel Control */ +/****************************************/ + +#define MV64340_DMA_CHANNEL0_CONTROL 0x840 +#define MV64340_DMA_CHANNEL0_CONTROL_HIGH 0x880 +#define MV64340_DMA_CHANNEL1_CONTROL 0x844 +#define MV64340_DMA_CHANNEL1_CONTROL_HIGH 0x884 +#define MV64340_DMA_CHANNEL2_CONTROL 0x848 +#define MV64340_DMA_CHANNEL2_CONTROL_HIGH 0x888 +#define MV64340_DMA_CHANNEL3_CONTROL 0x84C +#define MV64340_DMA_CHANNEL3_CONTROL_HIGH 0x88C + + +/****************************************/ +/* IDMA Registers */ +/****************************************/ + +#define MV64340_DMA_CHANNEL0_BYTE_COUNT 0x800 +#define MV64340_DMA_CHANNEL1_BYTE_COUNT 0x804 +#define MV64340_DMA_CHANNEL2_BYTE_COUNT 0x808 +#define MV64340_DMA_CHANNEL3_BYTE_COUNT 0x80C +#define MV64340_DMA_CHANNEL0_SOURCE_ADDR 0x810 +#define MV64340_DMA_CHANNEL1_SOURCE_ADDR 0x814 +#define MV64340_DMA_CHANNEL2_SOURCE_ADDR 0x818 +#define MV64340_DMA_CHANNEL3_SOURCE_ADDR 0x81c +#define MV64340_DMA_CHANNEL0_DESTINATION_ADDR 0x820 +#define MV64340_DMA_CHANNEL1_DESTINATION_ADDR 0x824 +#define MV64340_DMA_CHANNEL2_DESTINATION_ADDR 0x828 +#define MV64340_DMA_CHANNEL3_DESTINATION_ADDR 0x82C +#define MV64340_DMA_CHANNEL0_NEXT_DESCRIPTOR_POINTER 0x830 +#define MV64340_DMA_CHANNEL1_NEXT_DESCRIPTOR_POINTER 0x834 +#define MV64340_DMA_CHANNEL2_NEXT_DESCRIPTOR_POINTER 0x838 +#define MV64340_DMA_CHANNEL3_NEXT_DESCRIPTOR_POINTER 0x83C +#define MV64340_DMA_CHANNEL0_CURRENT_DESCRIPTOR_POINTER 0x870 +#define MV64340_DMA_CHANNEL1_CURRENT_DESCRIPTOR_POINTER 0x874 +#define MV64340_DMA_CHANNEL2_CURRENT_DESCRIPTOR_POINTER 0x878 +#define MV64340_DMA_CHANNEL3_CURRENT_DESCRIPTOR_POINTER 0x87C + + /* IDMA Address Decoding Base Address Registers */ + +#define MV64340_DMA_BASE_ADDR_REG0 0xa00 +#define MV64340_DMA_BASE_ADDR_REG1 0xa08 +#define MV64340_DMA_BASE_ADDR_REG2 0xa10 +#define MV64340_DMA_BASE_ADDR_REG3 0xa18 +#define MV64340_DMA_BASE_ADDR_REG4 0xa20 +#define MV64340_DMA_BASE_ADDR_REG5 0xa28 +#define MV64340_DMA_BASE_ADDR_REG6 0xa30 +#define MV64340_DMA_BASE_ADDR_REG7 0xa38 + + /* IDMA Address Decoding Size Address Register */ + +#define MV64340_DMA_SIZE_REG0 0xa04 +#define MV64340_DMA_SIZE_REG1 0xa0c +#define MV64340_DMA_SIZE_REG2 0xa14 +#define MV64340_DMA_SIZE_REG3 0xa1c +#define MV64340_DMA_SIZE_REG4 0xa24 +#define MV64340_DMA_SIZE_REG5 0xa2c +#define MV64340_DMA_SIZE_REG6 0xa34 +#define MV64340_DMA_SIZE_REG7 0xa3C + + /* IDMA Address Decoding High Address Remap and Access + Protection Registers */ + +#define MV64340_DMA_HIGH_ADDR_REMAP_REG0 0xa60 +#define MV64340_DMA_HIGH_ADDR_REMAP_REG1 0xa64 +#define MV64340_DMA_HIGH_ADDR_REMAP_REG2 0xa68 +#define MV64340_DMA_HIGH_ADDR_REMAP_REG3 0xa6C +#define MV64340_DMA_BASE_ADDR_ENABLE_REG 0xa80 +#define MV64340_DMA_CHANNEL0_ACCESS_PROTECTION_REG 0xa70 +#define MV64340_DMA_CHANNEL1_ACCESS_PROTECTION_REG 0xa74 +#define MV64340_DMA_CHANNEL2_ACCESS_PROTECTION_REG 0xa78 +#define MV64340_DMA_CHANNEL3_ACCESS_PROTECTION_REG 0xa7c +#define MV64340_DMA_ARBITER_CONTROL 0x860 +#define MV64340_DMA_CROSS_BAR_TIMEOUT 0x8d0 + + /* IDMA Headers Retarget Registers */ + +#define MV64340_DMA_HEADERS_RETARGET_CONTROL 0xa84 +#define MV64340_DMA_HEADERS_RETARGET_BASE 0xa88 + + /* IDMA Interrupt Register */ + +#define MV64340_DMA_INTERRUPT_CAUSE_REG 0x8c0 +#define MV64340_DMA_INTERRUPT_CAUSE_MASK 0x8c4 +#define MV64340_DMA_ERROR_ADDR 0x8c8 +#define MV64340_DMA_ERROR_SELECT 0x8cc + + /* IDMA Debug Register ( for internal use ) */ + +#define MV64340_DMA_DEBUG_LOW 0x8e0 +#define MV64340_DMA_DEBUG_HIGH 0x8e4 +#define MV64340_DMA_SPARE 0xA8C + +/****************************************/ +/* Timer_Counter */ +/****************************************/ + +#define MV64340_TIMER_COUNTER0 0x850 +#define MV64340_TIMER_COUNTER1 0x854 +#define MV64340_TIMER_COUNTER2 0x858 +#define MV64340_TIMER_COUNTER3 0x85C +#define MV64340_TIMER_COUNTER_0_3_CONTROL 0x864 +#define MV64340_TIMER_COUNTER_0_3_INTERRUPT_CAUSE 0x868 +#define MV64340_TIMER_COUNTER_0_3_INTERRUPT_MASK 0x86c + +/****************************************/ +/* Watchdog registers */ +/****************************************/ + +#define MV64340_WATCHDOG_CONFIG_REG 0xb410 +#define MV64340_WATCHDOG_VALUE_REG 0xb414 + +/****************************************/ +/* I2C Registers */ +/****************************************/ + +#define MV64340_I2C_SLAVE_ADDR 0xc000 +#define MV64340_I2C_EXTENDED_SLAVE_ADDR 0xc010 +#define MV64340_I2C_DATA 0xc004 +#define MV64340_I2C_CONTROL 0xc008 +#define MV64340_I2C_STATUS_BAUDE_RATE 0xc00C +#define MV64340_I2C_SOFT_RESET 0xc01c + +/****************************************/ +/* GPP Interface Registers */ +/****************************************/ + +#define MV64340_GPP_IO_CONTROL 0xf100 +#define MV64340_GPP_LEVEL_CONTROL 0xf110 +#define MV64340_GPP_VALUE 0xf104 +#define MV64340_GPP_INTERRUPT_CAUSE 0xf108 +#define MV64340_GPP_INTERRUPT_MASK0 0xf10c +#define MV64340_GPP_INTERRUPT_MASK1 0xf114 +#define MV64340_GPP_VALUE_SET 0xf118 +#define MV64340_GPP_VALUE_CLEAR 0xf11c + +/****************************************/ +/* Interrupt Controller Registers */ +/****************************************/ + +/****************************************/ +/* Interrupts */ +/****************************************/ + +#define MV64340_MAIN_INTERRUPT_CAUSE_LOW 0x004 +#define MV64340_MAIN_INTERRUPT_CAUSE_HIGH 0x00c +#define MV64340_CPU_INTERRUPT0_MASK_LOW 0x014 +#define MV64340_CPU_INTERRUPT0_MASK_HIGH 0x01c +#define MV64340_CPU_INTERRUPT0_SELECT_CAUSE 0x024 +#define MV64340_CPU_INTERRUPT1_MASK_LOW 0x034 +#define MV64340_CPU_INTERRUPT1_MASK_HIGH 0x03c +#define MV64340_CPU_INTERRUPT1_SELECT_CAUSE 0x044 +#define MV64340_INTERRUPT0_MASK_0_LOW 0x054 +#define MV64340_INTERRUPT0_MASK_0_HIGH 0x05c +#define MV64340_INTERRUPT0_SELECT_CAUSE 0x064 +#define MV64340_INTERRUPT1_MASK_0_LOW 0x074 +#define MV64340_INTERRUPT1_MASK_0_HIGH 0x07c +#define MV64340_INTERRUPT1_SELECT_CAUSE 0x084 + +/****************************************/ +/* MPP Interface Registers */ +/****************************************/ + +#define MV64340_MPP_CONTROL0 0xf000 +#define MV64340_MPP_CONTROL1 0xf004 +#define MV64340_MPP_CONTROL2 0xf008 +#define MV64340_MPP_CONTROL3 0xf00c + +/****************************************/ +/* Serial Initialization registers */ +/****************************************/ + +#define MV64340_SERIAL_INIT_LAST_DATA 0xf324 +#define MV64340_SERIAL_INIT_CONTROL 0xf328 +#define MV64340_SERIAL_INIT_STATUS 0xf32c + +#endif diff --git a/include/asm-mips/mv64340_dep.h b/include/asm-mips/mv64340_dep.h new file mode 100644 index 000000000000..f550fdfba7fa --- /dev/null +++ b/include/asm-mips/mv64340_dep.h @@ -0,0 +1,51 @@ +/* + * Copyright 2002 Momentum Computer Inc. + * Author: Matthew Dharm <mdharm@momenco.com> + * + * include/asm-mips/mv64340-dep.h + * Board-dependent definitions for MV-64340 chip. + * + * 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. + */ + +#ifndef __MV64340_DEP_H__ +#define __MV64340_DEP_H__ + +#include <asm/addrspace.h> /* for KSEG1ADDR() */ +#include <asm/byteorder.h> /* for cpu_to_le32() */ + +extern unsigned long mv64340_base; + +#define MV64340_BASE (mv64340_base) + +/* + * Because of an error/peculiarity in the Galileo chip, we need to swap the + * bytes when running bigendian. + */ + +#define MV_WRITE(ofs, data) \ + *(volatile u32 *)(MV64340_BASE+(ofs)) = cpu_to_le32(data) +#define MV_READ(ofs, data) \ + *(data) = le32_to_cpu(*(volatile u32 *)(MV64340_BASE+(ofs))) +#define MV_READ_DATA(ofs) \ + le32_to_cpu(*(volatile u32 *)(MV64340_BASE+(ofs))) + +#define MV_WRITE_16(ofs, data) \ + *(volatile u16 *)(MV64340_BASE+(ofs)) = cpu_to_le16(data) +#define MV_READ_16(ofs, data) \ + *(data) = le16_to_cpu(*(volatile u16 *)(MV64340_BASE+(ofs))) + +#define MV_WRITE_8(ofs, data) \ + *(volatile u8 *)(MV64340_BASE+(ofs)) = data +#define MV_READ_8(ofs, data) \ + *(data) = *(volatile u8 *)(MV64340_BASE+(ofs)) + +#define MV_SET_REG_BITS(ofs,bits) \ + (*((volatile u32 *)(MV64340_BASE+(ofs)))) |= ((u32)cpu_to_le32(bits)) +#define MV_RESET_REG_BITS(ofs,bits) \ + (*((volatile u32 *)(MV64340_BASE+(ofs)))) &= ~((u32)cpu_to_le32(bits)) + +#endif diff --git a/include/asm-mips/namei.h b/include/asm-mips/namei.h index 5361d07fdb37..dce62e3018c4 100644 --- a/include/asm-mips/namei.h +++ b/include/asm-mips/namei.h @@ -2,11 +2,9 @@ * linux/include/asm-mips/namei.h * * Included from linux/fs/namei.c - * - * $Id: namei.h,v 1.6 1999/01/04 16:09:23 ralf Exp $ */ -#ifndef __ASM_MIPS_NAMEI_H -#define __ASM_MIPS_NAMEI_H +#ifndef __ASM_NAMEI_H +#define __ASM_NAMEI_H #include <linux/config.h> @@ -28,4 +26,4 @@ static inline char *__emul_prefix(void) #endif /* !defined(CONFIG_BINFMT_IRIX) */ -#endif /* __ASM_MIPS_NAMEI_H */ +#endif /* __ASM_NAMEI_H */ diff --git a/include/asm-mips/ng1.h b/include/asm-mips/ng1.h index 7c7104415001..8c980fed63a9 100644 --- a/include/asm-mips/ng1.h +++ b/include/asm-mips/ng1.h @@ -1,5 +1,4 @@ -/* $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. diff --git a/include/asm-mips/paccess.h b/include/asm-mips/paccess.h index 6f6f4a1d28b8..e67d89c9a238 100644 --- a/include/asm-mips/paccess.h +++ b/include/asm-mips/paccess.h @@ -1,5 +1,4 @@ -/* $Id: paccess.h,v 1.1 2000/04/07 12:55:57 raiko 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. @@ -16,6 +15,9 @@ #include <linux/errno.h> +extern asmlinkage void handle_ibe(void); +extern asmlinkage void handle_dbe(void); + #define put_dbe(x,ptr) __put_dbe((x),(ptr),sizeof(*(ptr))) #define get_dbe(x,ptr) __get_dbe((x),(ptr),sizeof(*(ptr))) @@ -95,4 +97,6 @@ __asm__ __volatile__( \ extern void __put_dbe_unknown(void); +extern unsigned long search_dbe_table(unsigned long addr); + #endif /* _ASM_PACCESS_H */ diff --git a/include/asm-mips/page.h b/include/asm-mips/page.h index 015ebf1f9aab..581975ea7b98 100644 --- a/include/asm-mips/page.h +++ b/include/asm-mips/page.h @@ -1,5 +1,4 @@ -/* $Id: page.h,v 1.9 2000/02/24 00:13:19 ralf Exp $ - * +/* * Definitions for page handling * * This file is subject to the terms and conditions of the GNU General Public @@ -8,8 +7,10 @@ * * Copyright (C) 1994 - 1999 by Ralf Baechle */ -#ifndef __ASM_PAGE_H -#define __ASM_PAGE_H +#ifndef _ASM_PAGE_H +#define _ASM_PAGE_H + +#include <linux/config.h> /* PAGE_SHIFT determines the page size */ #define PAGE_SHIFT 12 @@ -18,27 +19,52 @@ #ifdef __KERNEL__ -#ifndef _LANGUAGE_ASSEMBLY +#ifndef __ASSEMBLY__ extern void (*_clear_page)(void * page); extern void (*_copy_page)(void * to, void * from); -#define clear_page(page) _clear_page(page) -#define copy_page(to, from) _copy_page(to, from) +#define clear_page(addr) _clear_page((void *)(addr)) +#define copy_page(to, from) _copy_page((void *)(to), (void *)(from)) + +extern unsigned long shm_align_mask; + +static inline unsigned long pages_do_alias(unsigned long addr1, + unsigned long addr2) +{ + return (addr1 ^ addr2) & shm_align_mask; +} + +struct page; + +static inline void clear_user_page(void *addr, unsigned long vaddr, + struct page *page) +{ + extern void (*flush_data_cache_page)(unsigned long addr); -#define clear_user_page(addr, vaddr, page) \ - do { clear_page(addr); \ - flush_dcache_page(page); \ - } while (0) -#define copy_user_page(to, from, vaddr, page) \ - do { copy_page(to, from); \ - flush_dcache_page(page); \ - } while (0) + clear_page(addr); + if (pages_do_alias((unsigned long) addr, vaddr)) + flush_data_cache_page((unsigned long)addr); +} + +static inline void copy_user_page(void *vto, void *vfrom, unsigned long vaddr, + struct page *to) +{ + extern void (*flush_data_cache_page)(unsigned long addr); + + copy_page(vto, vfrom); + if (pages_do_alias((unsigned long)vto, vaddr)) + flush_data_cache_page((unsigned long)vto); +} /* * These are used to make use of C type-checking.. */ +#ifdef CONFIG_64BIT_PHYS_ADDR +typedef struct { unsigned long long pte; } pte_t; +#else typedef struct { unsigned long pte; } pte_t; +#endif typedef struct { unsigned long pmd; } pmd_t; typedef struct { unsigned long pgd; } pgd_t; typedef struct { unsigned long pgprot; } pgprot_t; @@ -48,8 +74,10 @@ typedef struct { unsigned long pgprot; } pgprot_t; #define pgd_val(x) ((x).pgd) #define pgprot_val(x) ((x).pgprot) +#define ptep_buddy(x) ((pte_t *)((unsigned long)(x) ^ sizeof(pte_t))) + #define __pte(x) ((pte_t) { (x) } ) -#define __pme(x) ((pme_t) { (x) } ) +#define __pmd(x) ((pmd_t) { (x) } ) #define __pgd(x) ((pgd_t) { (x) } ) #define __pgprot(x) ((pgprot_t) { (x) } ) @@ -67,24 +95,40 @@ extern __inline__ int get_order(unsigned long size) return order; } -#endif /* _LANGUAGE_ASSEMBLY */ +#endif /* !__ASSEMBLY__ */ /* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) +#define PAGE_ALIGN(addr) (((addr) + PAGE_SIZE - 1) & PAGE_MASK) /* * This handles the memory map. * We handle pages at KSEG0 for kernels with 32 bit address space. */ -#define PAGE_OFFSET 0x80000000UL -#define __pa(x) ((unsigned long) (x) - PAGE_OFFSET) -#define __va(x) ((void *)((unsigned long) (x) + PAGE_OFFSET)) -#define virt_to_page(kaddr) (mem_map + (__pa(kaddr) >> PAGE_SHIFT)) -#define VALID_PAGE(page) ((page - mem_map) < max_mapnr) +#define PAGE_OFFSET 0x80000000UL +#define UNCAC_BASE 0xa0000000UL + +#define __pa(x) ((unsigned long) (x) - PAGE_OFFSET) +#define __va(x) ((void *)((unsigned long) (x) + PAGE_OFFSET)) + +#define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT) +#define pfn_to_page(pfn) (mem_map + (pfn)) +#define page_to_pfn(page) ((unsigned long)((page) - mem_map)) +#define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT) + +#define pfn_valid(pfn) ((pfn) < max_mapnr) +#define virt_addr_valid(kaddr) pfn_valid(__pa(kaddr) >> PAGE_SHIFT) #define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \ VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) +#define UNCAC_ADDR(addr) ((addr) - PAGE_OFFSET + UNCAC_BASE) +#define CAC_ADDR(addr) ((addr) - UNCAC_BASE + PAGE_OFFSET) + +/* + * Memory above this physical address will be considered highmem. + */ +#define HIGHMEM_START 0x20000000UL + #endif /* defined (__KERNEL__) */ -#endif /* __ASM_PAGE_H */ +#endif /* _ASM_PAGE_H */ diff --git a/include/asm-mips/param.h b/include/asm-mips/param.h index d4e4c7d73316..854088bb18b4 100644 --- a/include/asm-mips/param.h +++ b/include/asm-mips/param.h @@ -1,15 +1,16 @@ +/* + * 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 1994 - 2000, 2002 Ralf Baechle (ralf@gnu.org) + * Copyright 2000 Silicon Graphics, Inc. + */ #ifndef _ASM_PARAM_H #define _ASM_PARAM_H -#ifndef HZ - #ifdef __KERNEL__ -/* Safeguard against user stupidity */ -#ifdef _SYS_PARAM_H -#error Do not include <asm/param.h> with __KERNEL__ defined! -#endif - #include <linux/config.h> #ifdef CONFIG_DECSTATION @@ -19,38 +20,16 @@ */ # define LOG_2_HZ 7 # define HZ (1 << LOG_2_HZ) - /* - * Ye olde division-by-multiplication trick. - * This works only if 100 / HZ <= 1 - */ -# define QUOTIENT ((1UL << (32 - LOG_2_HZ)) * 100) -# define hz_to_std(a) \ - ({ unsigned int __res; \ - unsigned long __lo; \ - __asm__("multu\t%2,%3\n\t" \ - :"=h" (__res), "=l" (__lo) \ - :"r" (a),"r" (QUOTIENT)); \ - (__typeof__(a)) __res;}) - -#else /* Not a DECstation */ - -/* This is the internal value of HZ, that is the rate at which the jiffies - counter is increasing. This value is independent from the external value - and can be changed in order to suit the hardware and application - requirements. */ -# define HZ 100 -# define hz_to_std(a) (a) - -#endif /* Not a DECstation */ - -#else /* defined(__KERNEL__) */ +#else +# define HZ 1000 /* Internal kernel timer frequency */ +#endif +# define USER_HZ 100 /* .. some user interfaces are in "ticks" */ +# define CLOCKS_PER_SEC (USER_HZ) /* like times() */ +#endif -/* This is the external value of HZ as seen by user programs. Don't change - unless you know what you're doing - changing breaks binary compatibility. */ +#ifndef HZ #define HZ 100 - -#endif /* defined(__KERNEL__) */ -#endif /* defined(HZ) */ +#endif #define EXEC_PAGESIZE 4096 @@ -64,8 +43,4 @@ #define MAXHOSTNAMELEN 64 /* max length of hostname */ -#ifdef __KERNEL__ -# define CLOCKS_PER_SEC 100 /* frequency at which times() counts */ -#endif - #endif /* _ASM_PARAM_H */ diff --git a/include/asm-mips/parport.h b/include/asm-mips/parport.h index 159e2345d9b2..a742e04e82de 100644 --- a/include/asm-mips/parport.h +++ b/include/asm-mips/parport.h @@ -1,5 +1,4 @@ -/* $Id$ - * +/* * Copyright (C) 1999, 2000 Tim Waugh <tim@cyberelk.demon.co.uk> * * This file should only be included by drivers/parport/parport_pc.c. diff --git a/include/asm-mips/pci.h b/include/asm-mips/pci.h index 5cd89d7f84ea..30e68fa8b764 100644 --- a/include/asm-mips/pci.h +++ b/include/asm-mips/pci.h @@ -7,6 +7,7 @@ #define _ASM_PCI_H #include <linux/config.h> +#include <linux/mm.h> #ifdef __KERNEL__ @@ -23,12 +24,12 @@ extern unsigned int pcibios_assign_all_busses(void); #define PCIBIOS_MIN_IO 0x1000 #define PCIBIOS_MIN_MEM 0x10000000 -extern inline void pcibios_set_master(struct pci_dev *dev) +static inline void pcibios_set_master(struct pci_dev *dev) { /* No special bus mastering setup handling */ } -extern inline void pcibios_penalize_isa_irq(int irq) +static inline void pcibios_penalize_isa_irq(int irq) { /* We don't do dynamic PCI IRQ allocation */ } @@ -38,14 +39,13 @@ extern inline void pcibios_penalize_isa_irq(int irq) * MIPS has everything mapped statically. */ -#include <linux/config.h> #include <linux/types.h> #include <linux/slab.h> #include <asm/scatterlist.h> #include <linux/string.h> #include <asm/io.h> -#if (defined(CONFIG_DDB5074) || defined(CONFIG_DDB5476)) +#if defined(CONFIG_DDB5074) || defined(CONFIG_DDB5476) #undef PCIBIOS_MIN_IO #undef PCIBIOS_MIN_MEM #define PCIBIOS_MIN_IO 0x0100000 @@ -55,6 +55,13 @@ extern inline void pcibios_penalize_isa_irq(int irq) struct pci_dev; /* + * The PCI address space does equal the physical memory address space. The + * networking and block device layers use this boolean for bounce buffer + * decisions. + */ +#define PCI_DMA_BUS_IS_PHYS (1) + +/* * Allocate and map kernel buffer using consistent mode DMA for a device. * hwdev should be valid struct pci_dev pointer for PCI devices, * NULL for PCI-like buses (ISA, EISA). @@ -77,6 +84,32 @@ extern void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, extern void pci_free_consistent(struct pci_dev *hwdev, size_t size, void *vaddr, dma_addr_t dma_handle); + +#ifdef CONFIG_MAPPED_PCI_IO + +extern dma_addr_t pci_map_single(struct pci_dev *hwdev, void *ptr, size_t size, + int direction); +extern void pci_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr, + size_t size, int direction); +extern int pci_map_sg(struct pci_dev *hwdev, struct scatterlist *sg, int nents, + int direction); +extern void pci_unmap_sg(struct pci_dev *hwdev, struct scatterlist *sg, + int nents, int direction); +extern void pci_dma_sync_single(struct pci_dev *hwdev, dma_addr_t dma_handle, + size_t size, int direction); +extern void pci_dma_sync_sg(struct pci_dev *hwdev, struct scatterlist *sg, + int nelems, int direction); + +/* pci_unmap_{single,page} is not a nop, thus... */ +#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME) dma_addr_t ADDR_NAME; +#define DECLARE_PCI_UNMAP_LEN(LEN_NAME) __u32 LEN_NAME; +#define pci_unmap_addr(PTR, ADDR_NAME) ((PTR)->ADDR_NAME) +#define pci_unmap_addr_set(PTR, ADDR_NAME, VAL) (((PTR)->ADDR_NAME) = (VAL)) +#define pci_unmap_len(PTR, LEN_NAME) ((PTR)->LEN_NAME) +#define pci_unmap_len_set(PTR, LEN_NAME, VAL) (((PTR)->LEN_NAME) = (VAL)) + +#else /* CONFIG_MAPPED_PCI_IO */ + /* * Map a single buffer of the indicated size for DMA in streaming mode. * The 32-bit bus address to use is returned. @@ -84,17 +117,17 @@ extern void pci_free_consistent(struct pci_dev *hwdev, size_t size, * Once the device is given the dma address, the device owns this memory * until either pci_unmap_single or pci_dma_sync_single is performed. */ -extern inline dma_addr_t pci_map_single(struct pci_dev *hwdev, void *ptr, +static inline dma_addr_t pci_map_single(struct pci_dev *hwdev, void *ptr, size_t size, int direction) { + unsigned long addr = (unsigned long) ptr; + if (direction == PCI_DMA_NONE) BUG(); -#ifndef CONFIG_COHERENT_IO - dma_cache_wback_inv((unsigned long)ptr, size); -#endif + dma_cache_wback_inv(addr, size); - return virt_to_bus(ptr); + return bus_to_baddr(hwdev->bus, __pa(ptr)); } /* @@ -105,13 +138,18 @@ extern inline dma_addr_t pci_map_single(struct pci_dev *hwdev, void *ptr, * After this call, reads by the cpu to the buffer are guaranteed to see * whatever the device wrote there. */ -extern inline void pci_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr, +static inline void pci_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr, size_t size, int direction) { if (direction == PCI_DMA_NONE) BUG(); - /* Nothing to do */ + if (direction != PCI_DMA_TODEVICE) { + unsigned long addr; + + addr = baddr_to_bus(hwdev->bus, dma_addr) + PAGE_OFFSET; + dma_cache_wback_inv(addr, size); + } } /* pci_unmap_{page,single} is a nop so... */ @@ -123,6 +161,39 @@ extern inline void pci_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr, #define pci_unmap_len_set(PTR, LEN_NAME, VAL) do { } while (0) /* + * pci_{map,unmap}_single_page maps a kernel page to a dma_addr_t. identical + * to pci_map_single, but takes a struct page instead of a virtual address + */ +static inline dma_addr_t pci_map_page(struct pci_dev *hwdev, struct page *page, + unsigned long offset, size_t size, + int direction) +{ + unsigned long addr; + + if (direction == PCI_DMA_NONE) + BUG(); + + addr = (unsigned long) page_address(page) + offset; + dma_cache_wback_inv(addr, size); + + return bus_to_baddr(hwdev->bus, page_to_phys(page) + offset); +} + +static inline void pci_unmap_page(struct pci_dev *hwdev, dma_addr_t dma_address, + size_t size, int direction) +{ + if (direction == PCI_DMA_NONE) + BUG(); + + if (direction != PCI_DMA_TODEVICE) { + unsigned long addr; + + addr = baddr_to_bus(hwdev->bus, dma_address) + PAGE_OFFSET; + dma_cache_wback_inv(addr, size); + } +} + +/* * Map a set of buffers described by scatterlist in streaming * mode for DMA. This is the scather-gather version of the * above pci_map_single interface. Here the scatter gather list @@ -138,21 +209,23 @@ extern inline void pci_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr, * Device ownership issues as mentioned above for pci_map_single are * the same here. */ -extern inline int pci_map_sg(struct pci_dev *hwdev, struct scatterlist *sg, +static inline int pci_map_sg(struct pci_dev *hwdev, struct scatterlist *sg, int nents, int direction) { -#ifndef CONFIG_COHERENT_IO int i; -#endif if (direction == PCI_DMA_NONE) BUG(); -#ifndef CONFIG_COHERENT_IO - /* Make sure that gcc doesn't leave the empty loop body. */ - for (i = 0; i < nents; i++, sg++) - dma_cache_wback_inv((unsigned long)sg->address, sg->length); -#endif + for (i = 0; i < nents; i++, sg++) { + unsigned long addr; + + addr = (unsigned long) page_address(sg->page); + if (addr) + dma_cache_wback_inv(addr + sg->offset, sg->length); + sg->dma_address = (dma_addr_t) bus_to_baddr(hwdev->bus, + page_to_phys(sg->page) + sg->offset); + } return nents; } @@ -162,13 +235,27 @@ extern inline int pci_map_sg(struct pci_dev *hwdev, struct scatterlist *sg, * Again, cpu read rules concerning calls here are the same as for * pci_unmap_single() above. */ -extern inline void pci_unmap_sg(struct pci_dev *hwdev, struct scatterlist *sg, +static inline void pci_unmap_sg(struct pci_dev *hwdev, struct scatterlist *sg, int nents, int direction) { + int i; + if (direction == PCI_DMA_NONE) BUG(); - /* Nothing to do */ + if (direction == PCI_DMA_TODEVICE) + return; + + for (i = 0; i < nents; i++, sg++) { + unsigned long addr; + + if (!sg->page) + BUG(); + + addr = (unsigned long) page_address(sg->page); + if (addr) + dma_cache_wback_inv(addr + sg->offset, sg->length); + } } /* @@ -181,16 +268,17 @@ extern inline void pci_unmap_sg(struct pci_dev *hwdev, struct scatterlist *sg, * next point you give the PCI dma address back to the card, the * device again owns the buffer. */ -extern inline void pci_dma_sync_single(struct pci_dev *hwdev, +static inline void pci_dma_sync_single(struct pci_dev *hwdev, dma_addr_t dma_handle, size_t size, int direction) { + unsigned long addr; + if (direction == PCI_DMA_NONE) BUG(); -#ifndef CONFIG_COHERENT_IO - dma_cache_wback_inv((unsigned long)bus_to_virt(dma_handle), size); -#endif + addr = baddr_to_bus(hwdev->bus, dma_handle) + PAGE_OFFSET; + dma_cache_wback_inv(addr, size); } /* @@ -200,11 +288,11 @@ extern inline void pci_dma_sync_single(struct pci_dev *hwdev, * The same as pci_dma_sync_single but for a scatter-gather list, * same rules and usage. */ -extern inline void pci_dma_sync_sg(struct pci_dev *hwdev, +static inline void pci_dma_sync_sg(struct pci_dev *hwdev, struct scatterlist *sg, int nelems, int direction) { -#ifndef CONFIG_COHERENT_IO +#ifdef CONFIG_NONCOHERENT_IO int i; #endif @@ -212,30 +300,71 @@ extern inline void pci_dma_sync_sg(struct pci_dev *hwdev, BUG(); /* Make sure that gcc doesn't leave the empty loop body. */ -#ifndef CONFIG_COHERENT_IO +#ifdef CONFIG_NONCOHERENT_IO for (i = 0; i < nelems; i++, sg++) - dma_cache_wback_inv((unsigned long)sg->address, sg->length); + dma_cache_wback_inv((unsigned long)page_address(sg->page), + sg->length); #endif } +#endif /* CONFIG_MAPPED_PCI_IO */ -/* Return whether the given PCI device DMA address mask can +/* + * Return whether the given PCI device DMA address mask can * be supported properly. For example, if your device can * only drive the low 24-bits during PCI bus mastering, then * you would pass 0x00ffffff as the mask to this function. */ -extern inline int pci_dma_supported(struct pci_dev *hwdev, u64 mask) +static inline int pci_dma_supported(struct pci_dev *hwdev, u64 mask) { /* * we fall back to GFP_DMA when the mask isn't all 1s, * so we can't guarantee allocations that must be * within a tighter range than GFP_DMA.. */ - if (mask < 0x1fffffff) +#ifdef CONFIG_ISA + if (mask < 0x00ffffff) return 0; +#endif return 1; } +/* This is always fine. */ +#define pci_dac_dma_supported(pci_dev, mask) (1) + +static inline dma64_addr_t pci_dac_page_to_dma(struct pci_dev *pdev, + struct page *page, unsigned long offset, int direction) +{ + dma64_addr_t addr = page_to_phys(page) + offset; + + return (dma64_addr_t) bus_to_baddr(pdev->bus, addr); +} + +static inline struct page *pci_dac_dma_to_page(struct pci_dev *pdev, + dma64_addr_t dma_addr) +{ + unsigned long poff = baddr_to_bus(pdev->bus, dma_addr) >> PAGE_SHIFT; + + return mem_map + poff; +} + +static inline unsigned long pci_dac_dma_to_offset(struct pci_dev *pdev, + dma64_addr_t dma_addr) +{ + return dma_addr & ~PAGE_MASK; +} + +static inline void pci_dac_dma_sync_single(struct pci_dev *pdev, + dma64_addr_t dma_addr, size_t len, int direction) +{ + unsigned long addr; + + if (direction == PCI_DMA_NONE) + BUG(); + + addr = baddr_to_bus(pdev->bus, dma_addr) + PAGE_OFFSET; + dma_cache_wback_inv(addr, len); +} /* * These macros should be used after a pci_map_sg call has been done @@ -244,7 +373,7 @@ extern inline int pci_dma_supported(struct pci_dev *hwdev, u64 mask) * returns, or alternatively stop on the first sg_dma_len(sg) which * is 0. */ -#define sg_dma_address(sg) (virt_to_bus((sg)->address)) +#define sg_dma_address(sg) ((sg)->dma_address) #define sg_dma_len(sg) ((sg)->length) #endif /* __KERNEL__ */ diff --git a/include/asm-mips/pci_channel.h b/include/asm-mips/pci_channel.h index 2e64ec5bf8f8..ce1ecaf2625b 100644 --- a/include/asm-mips/pci_channel.h +++ b/include/asm-mips/pci_channel.h @@ -23,7 +23,7 @@ struct pci_channel { int last_devfn; }; -/* +/* * each board defines an array of pci_channels, that ends with all NULL entry */ extern struct pci_channel mips_pci_channels[]; @@ -33,7 +33,7 @@ extern struct pci_channel mips_pci_channels[]; */ extern void pcibios_fixup_irqs(void); -/* +/* * board supplied pci fixup routines */ extern void pcibios_fixup_resources(struct pci_dev *dev); diff --git a/include/asm-mips/percpu.h b/include/asm-mips/percpu.h new file mode 100644 index 000000000000..844e763e9332 --- /dev/null +++ b/include/asm-mips/percpu.h @@ -0,0 +1,6 @@ +#ifndef __ASM_PERCPU_H +#define __ASM_PERCPU_H + +#include <asm-generic/percpu.h> + +#endif /* __ASM_PERCPU_H */ diff --git a/include/asm-mips/pgalloc.h b/include/asm-mips/pgalloc.h index f71b90b1c8e1..447a294d3e6e 100644 --- a/include/asm-mips/pgalloc.h +++ b/include/asm-mips/pgalloc.h @@ -11,46 +11,32 @@ #include <linux/config.h> #include <linux/mm.h> +#include <linux/highmem.h> +#include <asm/fixmap.h> -/* TLB flushing: - * - * - flush_tlb_all() flushes all processes TLB entries - * - flush_tlb_mm(mm) flushes the specified mm context TLB entries - * - flush_tlb_page(vma, vmaddr) flushes a single page - * - flush_tlb_range(vma, start, end) flushes a range of pages - */ -extern void flush_tlb_all(void); -extern void flush_tlb_mm(struct mm_struct *mm); -extern void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, - unsigned long end); -extern void flush_tlb_page(struct vm_area_struct *vma, unsigned long page); - -extern inline void flush_tlb_pgtables(struct mm_struct *mm, - unsigned long start, unsigned long end) +static inline void pmd_populate_kernel(struct mm_struct *mm, pmd_t *pmd, + pte_t *pte) { - /* Nothing to do on MIPS. */ + set_pmd(pmd, __pmd(__pa(pte))); } +static inline void pmd_populate(struct mm_struct *mm, pmd_t *pmd, + struct page *pte) +{ + set_pmd(pmd, __pmd(((unsigned long long)page_to_pfn(pte) << + (unsigned long long) PAGE_SHIFT))); +} -/* - * Allocate and free page tables. - */ - -#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) - -#define pmd_populate(mm, pmd, pte) pmd_set(pmd, pte) +#define pgd_populate(mm, pmd, pte) BUG() /* * Initialize new page directory with pointers to invalid ptes */ extern void pgd_init(unsigned long page); -extern __inline__ pgd_t *get_pgd_slow(void) +static inline pgd_t *pgd_alloc(struct mm_struct *mm) { - pgd_t *ret = (pgd_t *)__get_free_page(GFP_KERNEL), *init; + pgd_t *ret = (pgd_t *)__get_free_pages(GFP_KERNEL, PGD_ORDER), *init; if (ret) { init = pgd_offset(&init_mm, 0); @@ -61,120 +47,59 @@ extern __inline__ pgd_t *get_pgd_slow(void) return ret; } -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++; -} - -extern __inline__ void free_pgd_slow(pgd_t *pgd) -{ - free_page((unsigned long)pgd); -} - -extern pte_t *get_pte_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]; - 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((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) +static inline void pgd_free(pgd_t *pgd) { + free_pages((unsigned long)pgd, PGD_ORDER); } -extern __inline__ void free_pmd_slow(pmd_t *pmd) -{ -} - -extern void __bad_pte(pmd_t *pmd); - -static inline pte_t *pte_alloc_one(struct mm_struct *mm, unsigned long address) +static inline pte_t *pte_alloc_one_kernel(struct mm_struct *mm, + unsigned long address) { pte_t *pte; pte = (pte_t *) __get_free_page(GFP_KERNEL|__GFP_REPEAT); if (pte) clear_page(pte); + return pte; } -static inline pte_t *pte_alloc_one_fast(struct mm_struct *mm, unsigned long address) +static inline struct page *pte_alloc_one(struct mm_struct *mm, + unsigned long address) { - unsigned long *ret; + struct page *pte; - if ((ret = (unsigned long *)pte_quicklist) != NULL) { - pte_quicklist = (unsigned long *)(*ret); - ret[0] = ret[1]; - pgtable_cache_size--; - } - return (pte_t *)ret; +#if CONFIG_HIGHPTE + pte = alloc_pages(GFP_KERNEL | __GFP_HIGHMEM | __GFP_REPEAT, 0); +#else + pte = alloc_pages(GFP_KERNEL | __GFP_REPEAT, 0); +#endif + if (pte) + clear_highpage(pte); + + return pte; } -extern __inline__ void pte_free_fast(pte_t *pte) +static inline void pte_free_kernel(pte_t *pte) { - *(unsigned long *)pte = (unsigned long) pte_quicklist; - pte_quicklist = (unsigned long *) pte; - pgtable_cache_size++; + free_page((unsigned long)pte); } -extern __inline__ void pte_free_slow(pte_t *pte) +static inline void pte_free(struct page *pte) { - free_page((unsigned long)pte); + __free_page(pte); } -#define pte_free(pte) pte_free_slow(pte) -#define pgd_free(pgd) free_pgd_fast(pgd) -#define pgd_alloc(mm) get_pgd_fast() +#define __pte_free_tlb(tlb,pte) tlb_remove_page((tlb),(pte)) /* * allocating and freeing a pmd is trivial: the 1-entry pmd is * inside the pgd, so has no extra memory associated with it. */ -#define pmd_alloc_one_fast(mm, addr) ({ BUG(); ((pmd_t *)1); }) #define pmd_alloc_one(mm, addr) ({ BUG(); ((pmd_t *)2); }) #define pmd_free(x) do { } while (0) -#define pgd_populate(mm, pmd, pte) BUG() +#define __pmd_free_tlb(tlb,x) do { } while (0) -extern int do_check_pgt_cache(int, int); +#define check_pgt_cache() do { } while (0) #endif /* _ASM_PGALLOC_H */ diff --git a/include/asm-mips/pgtable-bits.h b/include/asm-mips/pgtable-bits.h new file mode 100644 index 000000000000..fd79206b2fae --- /dev/null +++ b/include/asm-mips/pgtable-bits.h @@ -0,0 +1,102 @@ +/* + * 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 - 2002 by Ralf Baechle + * Copyright (C) 1999, 2000, 2001 Silicon Graphics, Inc. + * Copyright (C) 2002 Maciej W. Rozycki + */ +#ifndef _ASM_PGTABLE_BITS_H +#define _ASM_PGTABLE_BITS_H + +#include <linux/config.h> + +/* + * Note that we shift the lower 32bits of each EntryLo[01] entry + * 6 bits to the left. That way we can convert the PFN into the + * physical address by a single 'and' operation and gain 6 additional + * bits for storing information which isn't present in a normal + * MIPS page table. + * + * Similar to the Alpha port, we need to keep track of the ref + * and mod bits in software. We have a software "yeah you can read + * from this page" bit, and a hardware one which actually lets the + * process read from the page. On the same token we have a software + * writable bit and the real hardware one which actually lets the + * process write to the page, this keeps a mod bit via the hardware + * dirty bit. + * + * Certain revisions of the R4000 and R5000 have a bug where if a + * certain sequence occurs in the last 3 instructions of an executable + * page, and the following page is not mapped, the cpu can do + * unpredictable things. The code (when it is written) to deal with + * this problem will be in the update_mmu_cache() code for the r4k. + */ +#define _PAGE_PRESENT (1<<0) /* implemented in software */ +#define _PAGE_READ (1<<1) /* implemented in software */ +#define _PAGE_WRITE (1<<2) /* implemented in software */ +#define _PAGE_ACCESSED (1<<3) /* implemented in software */ +#define _PAGE_MODIFIED (1<<4) /* implemented in software */ +#define _PAGE_FILE (1<<4) /* set:pagecache unset:swap */ + +#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX) + +#define _PAGE_GLOBAL (1<<8) +#define _PAGE_VALID (1<<9) +#define _PAGE_SILENT_READ (1<<9) /* synonym */ +#define _PAGE_DIRTY (1<<10) /* The MIPS dirty bit */ +#define _PAGE_SILENT_WRITE (1<<10) +#define _CACHE_UNCACHED (1<<11) +#define _CACHE_MASK (1<<11) +#define _CACHE_CACHABLE_NONCOHERENT 0 + +#else +#define _PAGE_R4KBUG (1<<5) /* workaround for r4k bug */ +#define _PAGE_GLOBAL (1<<6) +#define _PAGE_VALID (1<<7) +#define _PAGE_SILENT_READ (1<<7) /* synonym */ +#define _PAGE_DIRTY (1<<8) /* The MIPS dirty bit */ +#define _PAGE_SILENT_WRITE (1<<8) +#define _CACHE_MASK (7<<9) + +#if defined(CONFIG_CPU_SB1) + +/* No penalty for being coherent on the SB1, so just + use it for "noncoherent" spaces, too. Shouldn't hurt. */ + +#define _CACHE_UNCACHED (2<<9) +#define _CACHE_CACHABLE_COW (5<<9) +#define _CACHE_CACHABLE_NONCOHERENT (5<<9) +#define _CACHE_UNCACHED_ACCELERATED (7<<9) + +#else + +#define _CACHE_CACHABLE_NO_WA (0<<9) /* R4600 only */ +#define _CACHE_CACHABLE_WA (1<<9) /* R4600 only */ +#define _CACHE_UNCACHED (2<<9) /* R4[0246]00 */ +#define _CACHE_CACHABLE_NONCOHERENT (3<<9) /* R4[0246]00 */ +#define _CACHE_CACHABLE_CE (4<<9) /* R4[04]00MC only */ +#define _CACHE_CACHABLE_COW (5<<9) /* R4[04]00MC only */ +#define _CACHE_CACHABLE_CUW (6<<9) /* R4[04]00MC only */ +#define _CACHE_UNCACHED_ACCELERATED (7<<9) /* R10000 only */ + +#endif +#endif + +#define __READABLE (_PAGE_READ | _PAGE_SILENT_READ | _PAGE_ACCESSED) +#define __WRITEABLE (_PAGE_WRITE | _PAGE_SILENT_WRITE | _PAGE_MODIFIED) + +#define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_ACCESSED | _PAGE_MODIFIED | _CACHE_MASK) + +#ifdef CONFIG_MIPS_UNCACHED +#define PAGE_CACHABLE_DEFAULT _CACHE_UNCACHED +#elif defined(CONFIG_NONCOHERENT_IO) +#define PAGE_CACHABLE_DEFAULT _CACHE_CACHABLE_NONCOHERENT +#else +#define PAGE_CACHABLE_DEFAULT _CACHE_CACHABLE_COW +#endif + +#define CONF_CM_DEFAULT (PAGE_CACHABLE_DEFAULT >> 9) + +#endif /* _ASM_PGTABLE_BITS_H */ diff --git a/include/asm-mips/pgtable.h b/include/asm-mips/pgtable.h index 0ba782b65479..6b6117f80d85 100644 --- a/include/asm-mips/pgtable.h +++ b/include/asm-mips/pgtable.h @@ -9,48 +9,13 @@ #ifndef _ASM_PGTABLE_H #define _ASM_PGTABLE_H +#include <linux/config.h> #include <asm/addrspace.h> #include <asm/page.h> -#ifndef _LANGUAGE_ASSEMBLY - #include <linux/linkage.h> #include <asm/cachectl.h> -#include <linux/config.h> - -/* Cache flushing: - * - * - flush_cache_all() flushes entire cache - * - flush_cache_mm(mm) flushes the specified mm context's cache lines - * - flush_cache_page(mm, vmaddr) flushes a single page - * - flush_cache_range(vma, start, end) flushes a range of pages - * - flush_icache_range(start, end) flush a range of instructions - */ -extern void (*_flush_cache_all)(void); -extern void (*___flush_cache_all)(void); -extern void (*_flush_cache_mm)(struct mm_struct *mm); -extern void (*_flush_cache_range)(struct vm_area_struct *vma, 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)(struct page * page); -extern void (*_flush_icache_range)(unsigned long start, unsigned long end); -extern void (*_flush_icache_page)(struct vm_area_struct *vma, - struct page *page); - -#define flush_cache_all() _flush_cache_all() -#define __flush_cache_all() ___flush_cache_all() -#define flush_cache_mm(mm) _flush_cache_mm(mm) -#define flush_cache_range(vma,start,end) _flush_cache_range(vma,start,end) -#define flush_cache_page(vma,page) _flush_cache_page(vma, page) -#define flush_cache_sigtramp(addr) _flush_cache_sigtramp(addr) -#define flush_dcache_page(page) _flush_page_to_ram(page) - -#define flush_icache_range(start, end) _flush_icache_range(start,end) -#define flush_icache_page(vma, page) _flush_icache_page(vma, page) -#define flush_icache_user_range(vma, page, addr, len) \ - _flush_icache_page((vma), (page)) - +#include <asm/fixmap.h> /* * - add_wired_entry() add a fixed TLB entry, and move wired register @@ -61,7 +26,7 @@ extern void add_wired_entry(unsigned long entrylo0, unsigned long entrylo1, /* * - add_temporary_entry() add a temporary TLB entry. We use TLB entries * starting at the top and working down. This is for populating the - * TLB before trap_init() puts the TLB miss handler in place. It + * TLB before trap_init() puts the TLB miss handler in place. It * should be used only for entries matching the actual page tables, * to prevent inconsistencies. */ @@ -76,114 +41,49 @@ extern int add_temporary_entry(unsigned long entrylo0, unsigned long entrylo1, * works even with the cache aliasing problem the R4k and above have. */ -#endif /* !defined (_LANGUAGE_ASSEMBLY) */ - /* PMD_SHIFT determines the size of the area a second-level page table can map */ +#ifdef CONFIG_64BIT_PHYS_ADDR +#define PMD_SHIFT 21 +#else #define PMD_SHIFT 22 +#endif #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_SHIFT PMD_SHIFT #define PGDIR_SIZE (1UL << PGDIR_SHIFT) #define PGDIR_MASK (~(PGDIR_SIZE-1)) -/* Entries per page directory level: we use two-level, so +/* + * Entries per page directory level: we use two-level, so * we don't really have any PMD directory physically. */ +#ifdef CONFIG_64BIT_PHYS_ADDR +#define PTRS_PER_PTE 512 +#define PTRS_PER_PMD 1 +#define PTRS_PER_PGD 2048 +#define PGD_ORDER 1 +#else #define PTRS_PER_PTE 1024 #define PTRS_PER_PMD 1 #define PTRS_PER_PGD 1024 -#define USER_PTRS_PER_PGD (TASK_SIZE/PGDIR_SIZE) +#define PGD_ORDER 0 +#endif + +#define USER_PTRS_PER_PGD (0x80000000UL/PGDIR_SIZE) #define FIRST_USER_PGD_NR 0 #define VMALLOC_START KSEG2 #define VMALLOC_VMADDR(x) ((unsigned long)(x)) -#define VMALLOC_END KSEG3 - -/* Note that we shift the lower 32bits of each EntryLo[01] entry - * 6 bits to the left. That way we can convert the PFN into the - * physical address by a single 'and' operation and gain 6 additional - * bits for storing information which isn't present in a normal - * MIPS page table. - * - * Similar to the Alpha port, we need to keep track of the ref - * and mod bits in software. We have a software "yeah you can read - * from this page" bit, and a hardware one which actually lets the - * process read from the page. On the same token we have a software - * writable bit and the real hardware one which actually lets the - * process write to the page, this keeps a mod bit via the hardware - * dirty bit. - * - * Certain revisions of the R4000 and R5000 have a bug where if a - * certain sequence occurs in the last 3 instructions of an executable - * page, and the following page is not mapped, the cpu can do - * unpredictable things. The code (when it is written) to deal with - * this problem will be in the update_mmu_cache() code for the r4k. - */ -#define _PAGE_PRESENT (1<<0) /* implemented in software */ -#define _PAGE_READ (1<<1) /* implemented in software */ -#define _PAGE_WRITE (1<<2) /* implemented in software */ -#define _PAGE_ACCESSED (1<<3) /* implemented in software */ -#define _PAGE_MODIFIED (1<<4) /* implemented in software */ - -#if defined(CONFIG_CPU_R3000) - -#define _PAGE_GLOBAL (1<<8) -#define _PAGE_VALID (1<<9) -#define _PAGE_SILENT_READ (1<<9) /* synonym */ -#define _PAGE_DIRTY (1<<10) /* The MIPS dirty bit */ -#define _PAGE_SILENT_WRITE (1<<10) -#define _CACHE_UNCACHED (1<<11) /* R4[0246]00 */ -#define _CACHE_MASK (1<<11) -#define _CACHE_CACHABLE_NONCOHERENT 0 +#if CONFIG_HIGHMEM +# define VMALLOC_END (PKMAP_BASE-2*PAGE_SIZE) #else -#define _PAGE_R4KBUG (1<<5) /* workaround for r4k bug */ -#define _PAGE_GLOBAL (1<<6) -#define _PAGE_VALID (1<<7) -#define _PAGE_SILENT_READ (1<<7) /* synonym */ -#define _PAGE_DIRTY (1<<8) /* The MIPS dirty bit */ -#define _PAGE_SILENT_WRITE (1<<8) -#define _CACHE_MASK (7<<9) - -#if defined(CONFIG_CPU_SB1) - -/* No penalty for being coherent on the SB1, so just - use it for "noncoherent" spaces, too. Shouldn't hurt. */ - -#define _CACHE_UNCACHED (2<<9) -#define _CACHE_CACHABLE_COW (5<<9) -#define _CACHE_CACHABLE_NONCOHERENT (5<<9) - -#else - -#define _CACHE_CACHABLE_NO_WA (0<<9) /* R4600 only */ -#define _CACHE_CACHABLE_WA (1<<9) /* R4600 only */ -#define _CACHE_UNCACHED (2<<9) /* R4[0246]00 */ -#define _CACHE_CACHABLE_NONCOHERENT (3<<9) /* R4[0246]00 */ -#define _CACHE_CACHABLE_CE (4<<9) /* R4[04]00 only */ -#define _CACHE_CACHABLE_COW (5<<9) /* R4[04]00 only */ -#define _CACHE_CACHABLE_CUW (6<<9) /* R4[04]00 only */ -#define _CACHE_CACHABLE_ACCELERATED (7<<9) /* R10000 only */ - +# define VMALLOC_END (FIXADDR_START-2*PAGE_SIZE) #endif -#endif - -#define __READABLE (_PAGE_READ | _PAGE_SILENT_READ | _PAGE_ACCESSED) -#define __WRITEABLE (_PAGE_WRITE | _PAGE_SILENT_WRITE | _PAGE_MODIFIED) -#define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_ACCESSED | _PAGE_MODIFIED | _CACHE_MASK) - -#ifdef CONFIG_MIPS_UNCACHED -#define PAGE_CACHABLE_DEFAULT _CACHE_UNCACHED -#else -#ifdef CONFIG_CPU_SB1 -#define PAGE_CACHABLE_DEFAULT _CACHE_CACHABLE_COW -#else -#define PAGE_CACHABLE_DEFAULT _CACHE_CACHABLE_NONCOHERENT -#endif -#endif +#include <asm/pgtable-bits.h> #define PAGE_NONE __pgprot(_PAGE_PRESENT | _CACHE_CACHABLE_NONCOHERENT) #define PAGE_SHARED __pgprot(_PAGE_PRESENT | _PAGE_READ | _PAGE_WRITE | \ @@ -193,11 +93,11 @@ extern int add_temporary_entry(unsigned long entrylo0, unsigned long entrylo1, #define PAGE_READONLY __pgprot(_PAGE_PRESENT | _PAGE_READ | \ PAGE_CACHABLE_DEFAULT) #define PAGE_KERNEL __pgprot(_PAGE_PRESENT | __READABLE | __WRITEABLE | \ - PAGE_CACHABLE_DEFAULT) + _PAGE_GLOBAL | PAGE_CACHABLE_DEFAULT) #define PAGE_USERIO __pgprot(_PAGE_PRESENT | _PAGE_READ | _PAGE_WRITE | \ PAGE_CACHABLE_DEFAULT) -#define PAGE_KERNEL_UNCACHED __pgprot(_PAGE_PRESENT | __READABLE | __WRITEABLE | \ - _CACHE_UNCACHED) +#define PAGE_KERNEL_UNCACHED __pgprot(_PAGE_PRESENT | __READABLE | \ + __WRITEABLE | _PAGE_GLOBAL | _CACHE_UNCACHED) /* * MIPS can't do page protection for execute, and considers that the same like @@ -222,14 +122,22 @@ extern int add_temporary_entry(unsigned long entrylo0, unsigned long entrylo1, #define __S110 PAGE_SHARED #define __S111 PAGE_SHARED -#if !defined (_LANGUAGE_ASSEMBLY) - +#ifdef CONFIG_64BIT_PHYS_ADDR +#define pte_ERROR(e) \ + printk("%s:%d: bad pte %016Lx.\n", __FILE__, __LINE__, pte_val(e)) +#else #define pte_ERROR(e) \ - printk("%s:%d: bad pte %016lx.\n", __FILE__, __LINE__, pte_val(e)) + printk("%s:%d: bad pte %08lx.\n", __FILE__, __LINE__, pte_val(e)) +#endif #define pmd_ERROR(e) \ - printk("%s:%d: bad pmd %016lx.\n", __FILE__, __LINE__, pmd_val(e)) + printk("%s:%d: bad pmd %08lx.\n", __FILE__, __LINE__, pmd_val(e)) #define pgd_ERROR(e) \ - printk("%s:%d: bad pgd %016lx.\n", __FILE__, __LINE__, pgd_val(e)) + printk("%s:%d: bad pgd %08lx.\n", __FILE__, __LINE__, pgd_val(e)) + +/* + * ZERO_PAGE is a global shared page that is always zero; used + * for zero-mapped memory areas etc.. + */ extern unsigned long empty_zero_page; extern unsigned long zero_page_mask; @@ -237,21 +145,6 @@ extern unsigned long zero_page_mask; #define ZERO_PAGE(vaddr) \ (virt_to_page(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)) - -/* 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 load_pgd(unsigned long pg_dir); extern pmd_t invalid_pte_table[PAGE_SIZE/sizeof(pmd_t)]; @@ -260,60 +153,68 @@ extern pmd_t invalid_pte_table[PAGE_SIZE/sizeof(pmd_t)]; * 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 unsigned long pmd_page(pmd_t pmd) -{ - return pmd_val(pmd); -} +#define page_pte(page) page_pte_prot(page, __pgprot(0)) +#define pmd_phys(pmd) (pmd_val(pmd) - PAGE_OFFSET) +#define pmd_page(pmd) (pfn_to_page(pmd_phys(pmd) >> PAGE_SHIFT)) +#define pmd_page_kernel(pmd) pmd_val(pmd) -extern inline void pmd_set(pmd_t * pmdp, pte_t * ptep) -{ - pmd_val(*pmdp) = (((unsigned long) ptep) & PAGE_MASK); -} - -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; } +#define pte_none(pte) (!(pte_val(pte) & ~_PAGE_GLOBAL)) +#define pte_present(pte) (pte_val(pte) & _PAGE_PRESENT) /* Certain architectures need to do special things when pte's * within a page table are directly modified. Thus, the following * hook is made available. */ -extern inline void set_pte(pte_t *ptep, pte_t pteval) +static inline void set_pte(pte_t *ptep, pte_t pteval) { *ptep = pteval; +#if !defined(CONFIG_CPU_R3000) && !defined(CONFIG_CPU_TX39XX) + if (pte_val(pteval) & _PAGE_GLOBAL) { + pte_t *buddy = ptep_buddy(ptep); + /* + * Make sure the buddy is global too (if it's !none, + * it better already be global) + */ + if (pte_none(*buddy)) + pte_val(*buddy) = pte_val(*buddy) | _PAGE_GLOBAL; + } +#endif } -extern inline void pte_clear(pte_t *ptep) +static inline void pte_clear(pte_t *ptep) { - set_pte(ptep, __pte(0)); +#if !defined(CONFIG_CPU_R3000) && !defined(CONFIG_CPU_TX39XX) + /* Preserve global status for the pair */ + if (pte_val(*ptep_buddy(ptep)) & _PAGE_GLOBAL) + set_pte(ptep, __pte(_PAGE_GLOBAL)); + else +#endif + set_pte(ptep, __pte(0)); } /* * (pmds are folded into pgds so this doesn't get actually called, * but the define is needed for a generic inline function.) */ -#define set_pmd(pmdptr, pmdval) (*(pmdptr) = pmdval) -#define set_pgd(pgdptr, pgdval) (*(pgdptr) = pgdval) +#define set_pmd(pmdptr, pmdval) do { *(pmdptr) = (pmdval); } while(0) +#define set_pgd(pgdptr, pgdval) do { *(pgdptr) = (pgdval); } while(0) /* * Empty pgd/pmd entries point to the invalid_pte_table. */ -extern inline int pmd_none(pmd_t pmd) +static inline int pmd_none(pmd_t pmd) { return pmd_val(pmd) == (unsigned long) invalid_pte_table; } -extern inline int pmd_bad(pmd_t pmd) -{ - return ((pmd_page(pmd) > (unsigned long) high_memory) || - (pmd_page(pmd) < PAGE_OFFSET)); -} +#define pmd_bad(pmd) (pmd_val(pmd) & ~PAGE_MASK) -extern inline int pmd_present(pmd_t pmd) +static inline int pmd_present(pmd_t pmd) { return (pmd_val(pmd) != (unsigned long) invalid_pte_table); } -extern inline void pmd_clear(pmd_t *pmdp) +static inline void pmd_clear(pmd_t *pmdp) { pmd_val(*pmdp) = ((unsigned long) invalid_pte_table); } @@ -323,51 +224,79 @@ extern inline void pmd_clear(pmd_t *pmdp) * 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; } -extern inline void pgd_clear(pgd_t *pgdp) { } +static inline int pgd_none(pgd_t pgd) { return 0; } +static inline int pgd_bad(pgd_t pgd) { return 0; } +static inline int pgd_present(pgd_t pgd) { return 1; } +static inline void pgd_clear(pgd_t *pgdp) { } + +#define pte_page(x) pfn_to_page(pte_pfn(x)) +#define pte_pfn(x) ((unsigned long)((x).pte >> PAGE_SHIFT)) +#define pfn_pte(pfn, prot) __pte(((pfn) << PAGE_SHIFT) | pgprot_val(prot)) + +#define PTE_FILE_MAX_BITS 27 + +#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX) + +/* + * Bits 0, 1, 2, 9 and 10 are taken, split up the 27 bits of offset + * into this range: + */ +#define pte_to_pgoff(_pte) \ + ((((_pte).pte >> 3) & 0x3f ) + (((_pte).pte >> 11) << 8 )) + +#define pgoff_to_pte(off) \ + ((pte_t) { (((off) & 0x3f) << 3) + (((off) >> 8) << 11) + _PAGE_FILE }) -#ifdef CONFIG_CPU_VR41XX -#define pte_page(x) (mem_map+(unsigned long)((pte_val(x) >> (PAGE_SHIFT + 2)))) #else -#define pte_page(x) (mem_map+(unsigned long)((pte_val(x) >> PAGE_SHIFT))) + +/* + * Bits 0, 1, 2, 7 and 8 are taken, split up the 27 bits of offset + * into this range: + */ +#define pte_to_pgoff(_pte) \ + ((((_pte).pte >> 3) & 0x1f ) + (((_pte).pte >> 9) << 6 )) + +#define pgoff_to_pte(off) \ + ((pte_t) { (((off) & 0x1f) << 3) + (((off) >> 6) << 9) + _PAGE_FILE }) + #endif /* * The following only work if pte_present() is true. * Undefined behaviour if not.. */ -extern inline int pte_read(pte_t pte) { return pte_val(pte) & _PAGE_READ; } -extern inline int pte_write(pte_t pte) { return pte_val(pte) & _PAGE_WRITE; } -extern inline int pte_dirty(pte_t pte) { return pte_val(pte) & _PAGE_MODIFIED; } -extern inline int pte_young(pte_t pte) { return pte_val(pte) & _PAGE_ACCESSED; } +static inline int pte_user(pte_t pte) { BUG(); return 0; } +static inline int pte_read(pte_t pte) { return pte_val(pte) & _PAGE_READ; } +static inline int pte_write(pte_t pte) { return pte_val(pte) & _PAGE_WRITE; } +static inline int pte_dirty(pte_t pte) { return pte_val(pte) & _PAGE_MODIFIED; } +static inline int pte_young(pte_t pte) { return pte_val(pte) & _PAGE_ACCESSED; } +static inline int pte_file(pte_t pte) { return pte_val(pte) & _PAGE_FILE; } -extern inline pte_t pte_wrprotect(pte_t pte) +static inline pte_t pte_wrprotect(pte_t pte) { pte_val(pte) &= ~(_PAGE_WRITE | _PAGE_SILENT_WRITE); return pte; } -extern inline pte_t pte_rdprotect(pte_t pte) +static inline pte_t pte_rdprotect(pte_t pte) { pte_val(pte) &= ~(_PAGE_READ | _PAGE_SILENT_READ); return pte; } -extern inline pte_t pte_mkclean(pte_t pte) +static inline pte_t pte_mkclean(pte_t pte) { pte_val(pte) &= ~(_PAGE_MODIFIED|_PAGE_SILENT_WRITE); return pte; } -extern inline pte_t pte_mkold(pte_t pte) +static inline pte_t pte_mkold(pte_t pte) { pte_val(pte) &= ~(_PAGE_ACCESSED|_PAGE_SILENT_READ); return pte; } -extern inline pte_t pte_mkwrite(pte_t pte) +static inline pte_t pte_mkwrite(pte_t pte) { pte_val(pte) |= _PAGE_WRITE; if (pte_val(pte) & _PAGE_MODIFIED) @@ -375,7 +304,7 @@ extern inline pte_t pte_mkwrite(pte_t pte) return pte; } -extern inline pte_t pte_mkread(pte_t pte) +static inline pte_t pte_mkread(pte_t pte) { pte_val(pte) |= _PAGE_READ; if (pte_val(pte) & _PAGE_ACCESSED) @@ -383,7 +312,7 @@ extern inline pte_t pte_mkread(pte_t pte) return pte; } -extern inline pte_t pte_mkdirty(pte_t pte) +static inline pte_t pte_mkdirty(pte_t pte) { pte_val(pte) |= _PAGE_MODIFIED; if (pte_val(pte) & _PAGE_WRITE) @@ -391,6 +320,14 @@ extern inline pte_t pte_mkdirty(pte_t pte) return pte; } +static inline pte_t pte_mkyoung(pte_t pte) +{ + pte_val(pte) |= _PAGE_ACCESSED; + if (pte_val(pte) & _PAGE_READ) + pte_val(pte) |= _PAGE_SILENT_READ; + return pte; +} + /* * Macro to make mark a page protection value as "uncacheable". Note * that "protection" is really a misnomer here as the protection value @@ -408,55 +345,20 @@ static inline pgprot_t pgprot_noncached(pgprot_t _prot) return __pgprot(prot); } -extern inline pte_t pte_mkyoung(pte_t pte) -{ - pte_val(pte) |= _PAGE_ACCESSED; - if (pte_val(pte) & _PAGE_READ) - pte_val(pte) |= _PAGE_SILENT_READ; - return 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. */ -#ifdef CONFIG_CPU_VR41XX -#define mk_pte(page, pgprot) \ -({ \ - pte_t __pte; \ - \ - pte_val(__pte) = ((unsigned long)(page - mem_map) << (PAGE_SHIFT + 2)) | \ - pgprot_val(pgprot); \ - \ - __pte; \ -}) -#else -#define mk_pte(page, pgprot) \ -({ \ - pte_t __pte; \ - \ - pte_val(__pte) = ((unsigned long)(page - mem_map) << PAGE_SHIFT) | \ - pgprot_val(pgprot); \ - \ - __pte; \ -}) -#endif +#define mk_pte(page, pgprot) pfn_pte(page_to_pfn(page), (pgprot)) -extern inline pte_t mk_pte_phys(unsigned long physpage, pgprot_t pgprot) -{ -#ifdef CONFIG_CPU_VR41XX - return __pte((physpage << 2) | pgprot_val(pgprot)); -#else - return __pte(physpage | pgprot_val(pgprot)); -#endif -} - -extern inline pte_t pte_modify(pte_t pte, pgprot_t newprot) +static 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(page) page_pte_prot(page, __pgprot(0)) +#define __pgd_offset(address) pgd_index(address) +#define __pmd_offset(address) \ + (((address) >> PMD_SHIFT) & (PTRS_PER_PMD-1)) /* to find an entry in a kernel page-table-directory */ #define pgd_offset_k(address) pgd_offset(&init_mm, address) @@ -464,313 +366,86 @@ extern inline pte_t pte_modify(pte_t pte, pgprot_t newprot) #define pgd_index(address) ((address) >> PGDIR_SHIFT) /* 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 + pgd_index(address); -} +#define pgd_offset(mm,addr) ((mm)->pgd + pgd_index(addr)) /* Find an entry in the second-level page table.. */ -extern inline pmd_t *pmd_offset(pgd_t *dir, unsigned long address) +static inline pmd_t *pmd_offset(pgd_t *dir, unsigned long address) { return (pmd_t *) dir; } -/* Find an entry in the third-level page table.. */ -extern inline pte_t *pte_offset(pmd_t * dir, unsigned long address) -{ - return (pte_t *) (pmd_page(*dir)) + - ((address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1)); -} - -extern int do_check_pgt_cache(int, int); +/* 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_kernel(*dir)) + __pte_offset(address)) +#define pte_offset_kernel(dir, address) \ + ((pte_t *) pmd_page_kernel(*(dir)) + __pte_offset(address)) + +#ifdef CONFIG_HIGHPTE +#define pte_offset_map(dir, address) \ + ((pte_t *)kmap_atomic(pmd_page(*(dir)),KM_PTE0) + __pte_offset(address)) +#define pte_offset_map_nested(dir, address) \ + ((pte_t *)kmap_atomic(pmd_page(*(dir)),KM_PTE1) + __pte_offset(address)) +#define pte_unmap(pte) kunmap_atomic(pte, KM_PTE0) +#define pte_unmap_nested(pte) kunmap_atomic(pte, KM_PTE1) +#else +#define pte_offset_map(dir, address) \ + ((pte_t *)page_address(pmd_page(*(dir))) + __pte_offset(address)) +#define pte_offset_map_nested(dir, address) \ + ((pte_t *)page_address(pmd_page(*(dir))) + __pte_offset(address)) +#define pte_unmap(pte) ((void)(pte)) +#define pte_unmap_nested(pte) ((void)(pte)) +#endif extern pgd_t swapper_pg_dir[1024]; extern void paging_init(void); -extern void update_mmu_cache(struct vm_area_struct *vma, - unsigned long address, pte_t pte); - -#define __swp_type(x) (((x).val >> 1) & 0x3f) -#define __swp_offset(x) ((x).val >> 8) -#define __swp_entry(type,offset) ((swp_entry_t) { ((type) << 1) | ((offset) << 8) }) -#define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) }) -#define __swp_entry_to_pte(x) ((pte_t) { (x).val }) - -#define kern_addr_valid(addr) (1) - -/* TLB operations. */ -extern inline void tlb_probe(void) -{ - __asm__ __volatile__( - ".set push\n\t" - ".set reorder\n\t" - "tlbp\n\t" - ".set pop"); -} - -extern inline void tlb_read(void) -{ - __asm__ __volatile__( - ".set push\n\t" - ".set reorder\n\t" - "tlbr\n\t" - ".set pop"); -} - -extern inline void tlb_write_indexed(void) -{ - __asm__ __volatile__( - ".set push\n\t" - ".set reorder\n\t" - "tlbwi\n\t" - ".set pop"); -} - -extern inline void tlb_write_random(void) -{ - __asm__ __volatile__( - ".set push\n\t" - ".set reorder\n\t" - "tlbwr\n\t" - ".set pop"); -} - -/* Dealing with various CP0 mmu/cache related registers. */ - -/* CP0_PAGEMASK register */ -extern inline unsigned long get_pagemask(void) -{ - unsigned long val; - - __asm__ __volatile__( - ".set push\n\t" - ".set reorder\n\t" - "mfc0 %0, $5\n\t" - ".set pop" - : "=r" (val)); - return val; -} - -extern inline void set_pagemask(unsigned long val) -{ - __asm__ __volatile__( - ".set push\n\t" - ".set reorder\n\t" - "mtc0 %z0, $5\n\t" - ".set pop" - : : "Jr" (val)); -} - -/* CP0_ENTRYLO0 and CP0_ENTRYLO1 registers */ -extern inline unsigned long get_entrylo0(void) -{ - unsigned long val; - - __asm__ __volatile__( - ".set push\n\t" - ".set reorder\n\t" - "mfc0 %0, $2\n\t" - ".set pop" - : "=r" (val)); - return val; -} - -extern inline void set_entrylo0(unsigned long val) -{ - __asm__ __volatile__( - ".set push\n\t" - ".set reorder\n\t" - "mtc0 %z0, $2\n\t" - ".set pop" - : : "Jr" (val)); -} - -extern inline unsigned long get_entrylo1(void) -{ - unsigned long val; - - __asm__ __volatile__( - ".set push\n\t" - ".set reorder\n\t" - "mfc0 %0, $3\n\t" - ".set pop" : "=r" (val)); - - return val; -} - -extern inline void set_entrylo1(unsigned long val) -{ - __asm__ __volatile__( - ".set push\n\t" - ".set reorder\n\t" - "mtc0 %z0, $3\n\t" - ".set pop" - : : "Jr" (val)); -} - -/* CP0_ENTRYHI register */ -extern inline unsigned long get_entryhi(void) -{ - unsigned long val; - - __asm__ __volatile__( - ".set push\n\t" - ".set reorder\n\t" - "mfc0 %0, $10\n\t" - ".set pop" - : "=r" (val)); - - return val; -} - -extern inline void set_entryhi(unsigned long val) -{ - __asm__ __volatile__( - ".set push\n\t" - ".set reorder\n\t" - "mtc0 %z0, $10\n\t" - ".set pop" - : : "Jr" (val)); -} - -/* CP0_INDEX register */ -extern inline unsigned long get_index(void) -{ - unsigned long val; - - __asm__ __volatile__( - ".set push\n\t" - ".set reorder\n\t" - "mfc0 %0, $0\n\t" - ".set pop" - : "=r" (val)); - return val; -} - -extern inline void set_index(unsigned long val) -{ - __asm__ __volatile__( - ".set push\n\t" - ".set reorder\n\t" - "mtc0 %z0, $0\n\t" - ".set pop" - : : "Jr" (val)); -} - -/* CP0_WIRED register */ -extern inline unsigned long get_wired(void) -{ - unsigned long val; - - __asm__ __volatile__( - ".set push\n\t" - ".set reorder\n\t" - "mfc0 %0, $6\n\t" - ".set pop" - : "=r" (val)); - return val; -} - -extern inline void set_wired(unsigned long val) -{ - __asm__ __volatile__( - ".set push\n\t" - ".set reorder\n\t" - "mtc0 %z0, $6\n\t" - ".set pop" - : : "Jr" (val)); -} - -extern inline unsigned long get_info(void) -{ - unsigned long val; - - __asm__( - ".set push\n\t" - ".set reorder\n\t" - "mfc0 %0, $7\n\t" - ".set pop" - : "=r" (val)); - return val; -} - -/* CP0_TAGLO and CP0_TAGHI registers */ -extern inline unsigned long get_taglo(void) -{ - unsigned long val; - - __asm__ __volatile__( - ".set push\n\t" - ".set reorder\n\t" - "mfc0 %0, $28\n\t" - ".set pop" - : "=r" (val)); - return val; -} +extern void __update_tlb(struct vm_area_struct *vma, unsigned long address, + pte_t pte); +extern void __update_cache(struct vm_area_struct *vma, unsigned long address, + pte_t pte); -extern inline void set_taglo(unsigned long val) +static inline void update_mmu_cache(struct vm_area_struct *vma, + unsigned long address, pte_t pte) { - __asm__ __volatile__( - ".set push\n\t" - ".set reorder\n\t" - "mtc0 %z0, $28\n\t" - ".set pop" - : : "Jr" (val)); + __update_tlb(vma, address, pte); + __update_cache(vma, address, pte); } -extern inline unsigned long get_taghi(void) -{ - unsigned long val; - - __asm__ __volatile__( - ".set push\n\t" - ".set reorder\n\t" - "mfc0 %0, $29\n\t" - ".set pop" - : "=r" (val)); - return val; -} +/* Swap entries must have VALID and GLOBAL bits cleared. */ +#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX) -extern inline void set_taghi(unsigned long val) -{ - __asm__ __volatile__( - ".set push\n\t" - ".set reorder\n\t" - "mtc0 %z0, $29\n\t" - ".set pop" - : : "Jr" (val)); -} +#define __swp_type(x) (((x).val >> 1) & 0x7f) +#define __swp_offset(x) ((x).val >> 10) +#define __swp_entry(type,offset) ((swp_entry_t) { ((type) << 1) | ((offset) << 10) }) +#else -/* CP0_CONTEXT register */ -extern inline unsigned long get_context(void) -{ - unsigned long val; +#define __swp_type(x) (((x).val >> 1) & 0x1f) +#define __swp_offset(x) ((x).val >> 8) +#define __swp_entry(type,offset) ((swp_entry_t) { ((type) << 1) | ((offset) << 8) }) +#endif - __asm__ __volatile__( - ".set push\n\t" - ".set reorder\n\t" - "mfc0 %0, $4\n\t" - ".set pop" - : "=r" (val)); +#define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) }) +#define __swp_entry_to_pte(x) ((pte_t) { (x).val }) - return val; -} -extern inline void set_context(unsigned long val) -{ - __asm__ __volatile__( - ".set push\n\t" - ".set reorder\n\t" - "mtc0 %z0, $4\n\t" - ".set pop" - : : "Jr" (val)); -} +/* Needs to be defined here and not in linux/mm.h, as it is arch dependent */ +#define kern_addr_valid(addr) (1) #include <asm-generic/pgtable.h> +#ifdef CONFIG_64BIT_PHYS_ADDR +typedef u64 pte_addr_t; +#else typedef pte_t *pte_addr_t; +#endif -#endif /* !defined (_LANGUAGE_ASSEMBLY) */ +/* + * We provide our own get_unmapped area to cope with the virtual aliasing + * constraints placed on us by the cache architecture. + */ +#define HAVE_ARCH_UNMAPPED_AREA #define io_remap_page_range remap_page_range diff --git a/include/asm-mips/pmc/ev64120.h b/include/asm-mips/pmc/ev64120.h deleted file mode 100644 index 74ad8c5105e3..000000000000 --- a/include/asm-mips/pmc/ev64120.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * This is a direct copy of the ev96100.h file, with a global search and - * replace. The numbers are the same. - * - * The reason I'm duplicating this is so that the 64120/96100 - * defines won't be confusing in the source code. - */ -#ifndef _ASM_PMC_CP7000_H -#define _ASM_PMC_CP7000_H - -#include <asm/addrspace.h> - -/* - * GT64120 config space base address - */ -#define GT64120_BASE (KSEG1ADDR(0x14000000)) -#define MIPS_GT_BASE GT64120_BASE - -/* - * PCI Bus allocation - */ -#define GT_PCI_MEM_BASE 0x12000000 -#define GT_PCI_MEM_SIZE 0x02000000 -#define GT_PCI_IO_BASE 0x10000000 -#define GT_PCI_IO_SIZE 0x02000000 -#define GT_ISA_IO_BASE PCI_IO_BASE - -/* - * Duart I/O ports. - */ -#define EV64120_COM1_BASE_ADDR (0x1d000000 + 0x20) -#define EV64120_COM2_BASE_ADDR (0x1d000000 + 0x00) - - -/* - * EV64120 interrupt controller register base. - */ -#define EV64120_ICTRL_REGS_BASE (KSEG1ADDR(0x1f000000)) - -/* - * EV64120 UART register base. - */ -#define EV64120_UART0_REGS_BASE (KSEG1ADDR(EV64120_COM1_BASE_ADDR)) -#define EV64120_UART1_REGS_BASE (KSEG1ADDR(EV64120_COM2_BASE_ADDR)) -#define EV64120_BASE_BAUD ( 3686400 / 16 ) - - -/* - * Because of an error/peculiarity in the Galileo chip, we need to swap the - * bytes when running bigendian. - */ - -#define GT_WRITE(ofs, data) \ - *(volatile u32 *)(MIPS_GT_BASE+ofs) = cpu_to_le32(data) -#define GT_READ(ofs, data) \ - *data = le32_to_cpu(*(volatile u32 *)(MIPS_GT_BASE+ofs)) - - -#endif /* _ASM_PMC_CP7000_H */ diff --git a/include/asm-mips/pmc/ev64120int.h b/include/asm-mips/pmc/ev64120int.h deleted file mode 100644 index 463f6b39dcaf..000000000000 --- a/include/asm-mips/pmc/ev64120int.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef _ASM_PMC_CP7000INT_H -#define _ASM_PMC_CP7000INT_H - -#define INT_CAUSE_MAIN 0 -#define INT_CAUSE_HIGH 1 - -#define MAX_CAUSE_REGS 4 -#define MAX_CAUSE_REG_WIDTH 32 - -void hook_irq_handler (int int_cause , int bit_num , void *isr_ptr); -int disable_galileo_irq (int int_cause , int bit_num); -int enable_galileo_irq (int int_cause , int bit_num); - -extern struct tq_struct irq_handlers[MAX_CAUSE_REGS][MAX_CAUSE_REG_WIDTH]; - -/* - * PCI interrupts will come in on either the INTA or INTD interrups lines, - * which are mapped to the #2 and #5 interrupt pins of the MIPS. On our - * boards, they all either come in on IntD or they all come in on IntA, they - * aren't mixed. There can be numerous PCI interrupts, so we keep a list of the - * "requested" interrupt numbers and go through the list whenever we get an - * IntA/D. - * - * All PCI interrupts have numbers >= 20 by arbitrary convention. Any - * interrupt < 8 is an interrupt that is maskable on MIPS. - */ - -#define TIMER 4 -#define INTA 2 -#define INTD 5 - -#endif /* _ASM_PMC_CP7000INT_H */ diff --git a/include/asm-mips/poll.h b/include/asm-mips/poll.h index 12c1a5ec3889..a000f1f789e3 100644 --- a/include/asm-mips/poll.h +++ b/include/asm-mips/poll.h @@ -1,5 +1,5 @@ -#ifndef __ASM_MIPS_POLL_H -#define __ASM_MIPS_POLL_H +#ifndef __ASM_POLL_H +#define __ASM_POLL_H #define POLLIN 0x0001 #define POLLPRI 0x0002 @@ -14,8 +14,9 @@ #define POLLWRNORM POLLOUT #define POLLWRBAND 0x0100 -/* XXX This one seems to be more-or-less nonstandard. */ +/* These seem to be more or less nonstandard ... */ #define POLLMSG 0x0400 +#define POLLREMOVE 0x1000 struct pollfd { int fd; @@ -23,4 +24,4 @@ struct pollfd { short revents; }; -#endif /* __ASM_MIPS_POLL_H */ +#endif /* __ASM_POLL_H */ diff --git a/include/asm-mips/posix_types.h b/include/asm-mips/posix_types.h index 21191a76c286..2c6ccfc714b1 100644 --- a/include/asm-mips/posix_types.h +++ b/include/asm-mips/posix_types.h @@ -1,5 +1,4 @@ -/* $Id: posix_types.h,v 1.6 2000/02/04 23:32:54 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. @@ -18,7 +17,7 @@ typedef unsigned int __kernel_dev_t; typedef unsigned long __kernel_ino_t; typedef unsigned int __kernel_mode_t; -typedef int __kernel_nlink_t; +typedef unsigned long __kernel_nlink_t; typedef long __kernel_off_t; typedef int __kernel_pid_t; typedef int __kernel_ipc_pid_t; @@ -30,6 +29,8 @@ typedef int __kernel_ptrdiff_t; typedef long __kernel_time_t; typedef long __kernel_suseconds_t; typedef long __kernel_clock_t; +typedef int __kernel_timer_t; +typedef int __kernel_clockid_t; typedef long __kernel_daddr_t; typedef char * __kernel_caddr_t; @@ -69,7 +70,7 @@ static __inline__ void __FD_CLR(unsigned long __fd, __kernel_fd_set *__fdsetp) #undef __FD_ISSET static __inline__ int __FD_ISSET(unsigned long __fd, const __kernel_fd_set *__p) -{ +{ unsigned long __tmp = __fd / __NFDBITS; unsigned long __rem = __fd % __NFDBITS; return (__p->fds_bits[__tmp] & (1UL<<__rem)) != 0; diff --git a/include/asm-mips/prctl.h b/include/asm-mips/prctl.h index 941f96feace0..4aaaff670361 100644 --- a/include/asm-mips/prctl.h +++ b/include/asm-mips/prctl.h @@ -3,7 +3,6 @@ * * The IRIX kernel maps a page at PRDA_ADDRESS with the * contents of prda and fills it the bits on prda_sys. - * $Id$ */ #ifndef __PRCTL_H__ @@ -13,14 +12,14 @@ #define PRDA ((struct prda *) PRDA_ADDRESS) struct prda_sys { - pid_t t_pid; + pid_t t_pid; u32 t_hint; u32 t_dlactseq; u32 t_fpflags; u32 t_prid; /* processor type, $prid CP0 register */ u32 t_dlendseq; u64 t_unused1[5]; - pid_t t_rpid; + pid_t t_rpid; s32 t_resched; u32 t_unused[8]; u32 t_cpu; /* current/last cpu */ diff --git a/include/asm-mips/processor.h b/include/asm-mips/processor.h index 5b06f8081a62..d7ac86362d79 100644 --- a/include/asm-mips/processor.h +++ b/include/asm-mips/processor.h @@ -4,7 +4,7 @@ * for more details. * * Copyright (C) 1994 Waldorf GMBH - * Copyright (C) 1995, 1996, 1997, 1998, 1999, 2001 Ralf Baechle + * Copyright (C) 1995, 1996, 1997, 1998, 1999, 2001, 2002, 2003 Ralf Baechle * Copyright (C) 1996 Paul M. Antoine * Copyright (C) 1999 Silicon Graphics, Inc. */ @@ -12,8 +12,9 @@ #define _ASM_PROCESSOR_H #include <linux/config.h> - +#include <linux/threads.h> #include <asm/isadep.h> +#include <asm/page.h> /* * Default implementation of macro that returns current @@ -21,59 +22,102 @@ */ #define current_text_addr() ({ __label__ _l; _l: &&_l;}) -#if !defined (_LANGUAGE_ASSEMBLY) +#ifndef __ASSEMBLY__ +#include <linux/cache.h> #include <linux/threads.h> + #include <asm/cachectl.h> #include <asm/mipsregs.h> #include <asm/reg.h> #include <asm/system.h> -struct mips_cpuinfo { - unsigned long udelay_val; - unsigned long *pgd_quick; - unsigned long *pte_quick; - unsigned long pgtable_cache_sz; +/* + * Descriptor for a cache + */ +struct cache_desc { + unsigned short linesz; + unsigned short ways; + unsigned int sets; + unsigned int waybit; /* Bits to select in a cache set */ + unsigned int flags; /* Flags describingcache properties */ }; /* - * System setup and hardware flags.. - * XXX: Should go into mips_cpuinfo. + * Flag definitions */ -extern void (*cpu_wait)(void); /* only available on R4[26]00 and R3081 */ -extern void r3081_wait(void); -extern void r4k_wait(void); -extern char cyclecounter_available; /* only available from R4000 upwards. */ +#define MIPS_CACHE_NOT_PRESENT 0x00000001 +#define MIPS_CACHE_VTAG 0x00000002 /* Virtually tagged cache */ +#define MIPS_CACHE_ALIASES 0x00000004 /* Cache could have aliases */ +#define MIPS_CACHE_IC_F_DC 0x00000008 /* Ic can refill from D-cache */ -extern struct mips_cpuinfo boot_cpu_data; -extern unsigned int vced_count, vcei_count; +struct cpuinfo_mips { + unsigned long udelay_val; + unsigned long asid_cache; + + /* + * Capability and feature descriptor structure for MIPS CPU + */ + unsigned long options; + unsigned int processor_id; + unsigned int fpu_id; + unsigned int cputype; + int isa_level; + int tlbsize; + struct cache_desc icache; /* Primary I-cache */ + struct cache_desc dcache; /* Primary D or combined I/D cache */ + struct cache_desc scache; /* Secondary cache */ + struct cache_desc tcache; /* Tertiary/split secondary cache */ +} __attribute__((__aligned__(SMP_CACHE_BYTES))); -#ifdef CONFIG_SMP -extern struct mips_cpuinfo cpu_data[]; +/* + * Assumption: Options of CPU 0 are a superset of all processors. + * This is true for all known MIPS systems. + */ +#define cpu_has_tlb (cpu_data[0].options & MIPS_CPU_TLB) +#define cpu_has_4kex (cpu_data[0].options & MIPS_CPU_4KEX) +#define cpu_has_4ktlb (cpu_data[0].options & MIPS_CPU_4KTLB) +#define cpu_has_fpu (cpu_data[0].options & MIPS_CPU_FPU) +#define cpu_has_32fpr (cpu_data[0].options & MIPS_CPU_32FPR) +#define cpu_has_counter (cpu_data[0].options & MIPS_CPU_COUNTER) +#define cpu_has_watch (cpu_data[0].options & MIPS_CPU_WATCH) +#define cpu_has_mips16 (cpu_data[0].options & MIPS_CPU_MIPS16) +#define cpu_has_divec (cpu_data[0].options & MIPS_CPU_DIVEC) +#define cpu_has_vce (cpu_data[0].options & MIPS_CPU_VCE) +#define cpu_has_cache_cdex (cpu_data[0].options & MIPS_CPU_CACHE_CDEX) +#define cpu_has_mcheck (cpu_data[0].options & MIPS_CPU_MCHECK) +#define cpu_has_ejtag (cpu_data[0].options & MIPS_CPU_EJTAG) +#define cpu_has_nofpuex (cpu_data[0].options & MIPS_CPU_NOFPUEX) +#define cpu_has_llsc (cpu_data[0].options & MIPS_CPU_LLSC) +#define cpu_has_vtag_icache (cpu_data[0].icache.flags & MIPS_CACHE_VTAG) +#define cpu_has_dc_aliases (cpu_data[0].dcache.flags & MIPS_CACHE_ALIASES) +#define cpu_has_ic_fills_f_dc (cpu_data[0].dcache.flags & MIPS_CACHE_IC_F_DC) +#define cpu_has_64bits (cpu_data[0].isa_level & MIPS_CPU_ISA_64BIT) +#define cpu_has_subset_pcaches (cpu_data[0].options & MIPS_CPU_SUBSET_CACHES) + +extern struct cpuinfo_mips cpu_data[]; #define current_cpu_data cpu_data[smp_processor_id()] -#else -#define cpu_data &boot_cpu_data -#define current_cpu_data boot_cpu_data -#endif + +extern void cpu_probe(void); +extern void cpu_report(void); /* - * Bus types (default is ISA, but people can check others with these..) - * MCA_bus hardcoded to 0 for now. - * - * This needs to be extended since MIPS systems are being delivered with - * numerous different types of bus systems. + * System setup and hardware flags.. */ -extern int EISA_bus; -#define MCA_bus 0 -#define MCA_bus__is_a_macro /* for versions in ksyms.c */ +extern void (*cpu_wait)(void); + +extern unsigned int vced_count, vcei_count; /* - * MIPS has no problems with write protection + * Bus types (default is ISA, but people can check others with these..) */ -#define wp_works_ok 1 -#define wp_works_ok__is_a_macro /* for versions in ksyms.c */ +#ifdef CONFIG_EISA +extern int EISA_bus; +#else +#define EISA_bus (0) +#endif -/* Lazy FPU handling on uni-processor */ -extern struct task_struct *last_task_used_math; +#define MCA_bus 0 +#define MCA_bus__is_a_macro /* for versions in ksyms.c */ /* * User space process size: 2GB. This is hardcoded into a few places, @@ -86,7 +130,7 @@ extern struct task_struct *last_task_used_math; /* This decides where the kernel will search for a free chunk of vm * space during mmap's. */ -#define TASK_UNMAPPED_BASE (TASK_SIZE / 3) +#define TASK_UNMAPPED_BASE (PAGE_ALIGN(TASK_SIZE / 3)) /* * Size of io_bitmap in longwords: 32 is ports 0-0x3ff. @@ -148,27 +192,11 @@ struct thread_struct { #define MF_FIXADE 1 /* Fix address errors in software */ #define MF_LOGADE 2 /* Log address errors to syslog */ unsigned long mflags; - mm_segment_t current_ds; unsigned long irix_trampoline; /* Wheee... */ unsigned long irix_oldctx; - - /* - * These are really only needed if the full FPU emulator is configured. - * Would be made conditional on MIPS_FPU_EMULATOR if it weren't for the - * fact that having offset.h rebuilt differently for different config - * options would be asking for trouble. - * - * Saved EPC during delay-slot emulation (see math-emu/cp1emu.c) - */ - unsigned long dsemul_epc; - - /* - * Pointer to instruction used to induce address error - */ - unsigned long dsemul_aerpc; }; -#endif /* !defined (_LANGUAGE_ASSEMBLY) */ +#endif /* !__ASSEMBLY__ */ #define INIT_THREAD { \ /* \ @@ -191,19 +219,14 @@ struct thread_struct { /* \ * For now the default is to fix address errors \ */ \ - MF_FIXADE, { 0 }, 0, 0, \ - /* \ - * dsemul_epc and dsemul_aerpc should never be used uninitialized, \ - * but... \ - */ \ - 0 ,0 \ + MF_FIXADE, 0, 0 \ } #ifdef __KERNEL__ #define KERNEL_STACK_SIZE 8192 -#if !defined (_LANGUAGE_ASSEMBLY) +#ifndef __ASSEMBLY__ /* Free all resources held by a thread. */ #define release_thread(thread) do { } while(0) @@ -213,54 +236,25 @@ struct thread_struct { extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags); -/* - * Return saved PC of a blocked thread. - */ -extern inline unsigned long thread_saved_pc(struct thread_struct *t) -{ - extern void ret_from_fork(void); - - /* New born processes are a special case */ - if (t->reg31 == (unsigned long) ret_from_fork) - return t->reg31; - - return ((unsigned long *)t->reg29)[10]; -} +extern unsigned long thread_saved_pc(struct thread_struct *t); /* * Do necessary setup to start up a newly executed thread. */ -#define start_thread(regs, new_pc, new_sp) do { \ - /* New thread loses kernel privileges. */ \ - regs->cp0_status = (regs->cp0_status & ~(ST0_CU0|ST0_KSU)) | KU_USER;\ - regs->cp0_epc = new_pc; \ - regs->regs[29] = new_sp; \ - current->thread.current_ds = USER_DS; \ -} while (0) +extern void start_thread(struct pt_regs * regs, unsigned long pc, unsigned long sp); +struct task_struct; unsigned long get_wchan(struct task_struct *p); #define __PT_REG(reg) ((long)&((struct pt_regs *)0)->reg - sizeof(struct pt_regs)) -#define __KSTK_TOS(tsk) ((unsigned long)(tsk) + KERNEL_STACK_SIZE - 32) +#define __KSTK_TOS(tsk) ((unsigned long)(tsk->thread_info) + KERNEL_STACK_SIZE - 32) #define KSTK_EIP(tsk) (*(unsigned long *)(__KSTK_TOS(tsk) + __PT_REG(cp0_epc))) #define KSTK_ESP(tsk) (*(unsigned long *)(__KSTK_TOS(tsk) + __PT_REG(regs[29]))) - -/* Allocation and freeing of basic task resources. */ -/* - * NOTE! The task struct and the stack go together - */ -#define THREAD_SIZE (2*PAGE_SIZE) -#define alloc_task_struct() \ - ((struct task_struct *) __get_free_pages(GFP_KERNEL,1)) -#define free_task_struct(p) free_pages((unsigned long)(p),1) -#define get_task_struct(tsk) atomic_inc(&virt_to_page(tsk)->count) - -#define init_task (init_task_union.task) -#define init_stack (init_task_union.stack) +#define KSTK_STATUS(tsk) (*(unsigned long *)(__KSTK_TOS(tsk) + __PT_REG(cp0_status))) #define cpu_relax() barrier() -#endif /* !defined (_LANGUAGE_ASSEMBLY) */ +#endif /* !__ASSEMBLY__ */ #endif /* __KERNEL__ */ /* diff --git a/include/asm-mips/ptrace.h b/include/asm-mips/ptrace.h index c52592cbbbe0..8e185b9fcd5b 100644 --- a/include/asm-mips/ptrace.h +++ b/include/asm-mips/ptrace.h @@ -12,7 +12,6 @@ #define _ASM_PTRACE_H #include <asm/isadep.h> -#include <linux/types.h> /* 0 - 31 are integer registers, 32 - 63 are fp registers. */ #define FPR_BASE 32 @@ -24,7 +23,7 @@ #define FPC_CSR 69 #define FPC_EIR 70 -#ifndef _LANGUAGE_ASSEMBLY +#ifndef __ASSEMBLY__ /* * This struct defines the way the registers are stored on the stack during a * system call/exception. As usual the registers k0/k1 aren't being saved. @@ -49,7 +48,34 @@ struct pt_regs { unsigned long cp0_cause; }; -#endif /* !(_LANGUAGE_ASSEMBLY) */ +#define __str2(x) #x +#define __str(x) __str2(x) + +#define save_static_function(symbol) \ +__asm__ ( \ + ".text\n\t" \ + ".globl\t" #symbol "\n\t" \ + ".align\t2\n\t" \ + ".type\t" #symbol ", @function\n\t" \ + ".ent\t" #symbol ", 0\n" \ + #symbol":\n\t" \ + ".frame\t$29, 0, $31\n\t" \ + "sw\t$16,"__str(PT_R16)"($29)\t\t\t# save_static_function\n\t" \ + "sw\t$17,"__str(PT_R17)"($29)\n\t" \ + "sw\t$18,"__str(PT_R18)"($29)\n\t" \ + "sw\t$19,"__str(PT_R19)"($29)\n\t" \ + "sw\t$20,"__str(PT_R20)"($29)\n\t" \ + "sw\t$21,"__str(PT_R21)"($29)\n\t" \ + "sw\t$22,"__str(PT_R22)"($29)\n\t" \ + "sw\t$23,"__str(PT_R23)"($29)\n\t" \ + "sw\t$30,"__str(PT_R30)"($29)\n\t" \ + ".end\t" #symbol "\n\t" \ + ".size\t" #symbol",. - " #symbol) + +/* Used in declaration of save_static functions. */ +#define static_unused static __attribute__((unused)) + +#endif /* !__ASSEMBLY__ */ /* Arbitrarily choose the same ptrace numbers as used by the Sparc code. */ /* #define PTRACE_GETREGS 12 */ @@ -61,13 +87,16 @@ struct pt_regs { #define PTRACE_OLDSETOPTIONS 21 -#ifdef _LANGUAGE_ASSEMBLY +#define PTRACE_GET_THREAD_AREA 25 +#define PTRACE_SET_THREAD_AREA 26 + +#ifdef __ASSEMBLY__ #include <asm/offset.h> #endif #ifdef __KERNEL__ -#ifndef _LANGUAGE_ASSEMBLY +#ifndef __ASSEMBLY__ /* * Does the process account for user or for system time? */ @@ -76,7 +105,7 @@ struct pt_regs { #define instruction_pointer(regs) ((regs)->cp0_epc) extern void show_regs(struct pt_regs *); -#endif /* !(_LANGUAGE_ASSEMBLY) */ +#endif /* !__ASSEMBLY__ */ #endif diff --git a/include/asm-mips/r4kcache.h b/include/asm-mips/r4kcache.h index 561be48b836b..b3814e2c262a 100644 --- a/include/asm-mips/r4kcache.h +++ b/include/asm-mips/r4kcache.h @@ -1,155 +1,114 @@ /* - * r4kcache.h: Inline assembly cache operations. + * 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) 1996 David S. Miller (dm@engr.sgi.com) - * - * $Id: r4kcache.h,v 1.7 1997/12/18 13:00:45 ralf Exp $ + * Inline assembly cache operations. * - * FIXME: Handle split L2 caches. + * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * Copyright (C) 1997 - 2002 Ralf Baechle (ralf@gnu.org) */ -#ifndef _MIPS_R4KCACHE_H -#define _MIPS_R4KCACHE_H +#ifndef __ASM_R4KCACHE_H +#define __ASM_R4KCACHE_H #include <asm/asm.h> #include <asm/cacheops.h> -extern inline void flush_icache_line_indexed(unsigned long addr) +#define cache_op(op,addr) \ + __asm__ __volatile__( \ + " .set noreorder \n" \ + " .set mips3\n\t \n" \ + " cache %0, %1 \n" \ + " .set mips0 \n" \ + " .set reorder" \ + : \ + : "i" (op), "m" (*(unsigned char *)(addr))) + +static inline void flush_icache_line_indexed(unsigned long addr) { - __asm__ __volatile__( - ".set noreorder\n\t" - ".set mips3\n\t" - "cache %1, (%0)\n\t" - ".set mips0\n\t" - ".set reorder" - : - : "r" (addr), - "i" (Index_Invalidate_I)); + cache_op(Index_Invalidate_I, addr); } -extern inline void flush_dcache_line_indexed(unsigned long addr) +static inline void flush_dcache_line_indexed(unsigned long addr) { - __asm__ __volatile__( - ".set noreorder\n\t" - ".set mips3\n\t" - "cache %1, (%0)\n\t" - ".set mips0\n\t" - ".set reorder" - : - : "r" (addr), - "i" (Index_Writeback_Inv_D)); + cache_op(Index_Writeback_Inv_D, addr); } -extern inline void flush_scache_line_indexed(unsigned long addr) +static inline void flush_scache_line_indexed(unsigned long addr) { - __asm__ __volatile__( - ".set noreorder\n\t" - ".set mips3\n\t" - "cache %1, (%0)\n\t" - ".set mips0\n\t" - ".set reorder" - : - : "r" (addr), - "i" (Index_Writeback_Inv_SD)); + cache_op(Index_Writeback_Inv_SD, addr); } -extern inline void flush_icache_line(unsigned long addr) +static inline void flush_icache_line(unsigned long addr) { - __asm__ __volatile__( - ".set noreorder\n\t" - ".set mips3\n\t" - "cache %1, (%0)\n\t" - ".set mips0\n\t" - ".set reorder" - : - : "r" (addr), - "i" (Hit_Invalidate_I)); + cache_op(Hit_Invalidate_I, addr); } -extern inline void flush_dcache_line(unsigned long addr) +static inline void flush_dcache_line(unsigned long addr) { - __asm__ __volatile__( - ".set noreorder\n\t" - ".set mips3\n\t" - "cache %1, (%0)\n\t" - ".set mips0\n\t" - ".set reorder" - : - : "r" (addr), - "i" (Hit_Writeback_Inv_D)); + cache_op(Hit_Writeback_Inv_D, addr); } -extern inline void invalidate_dcache_line(unsigned long addr) +static inline void invalidate_dcache_line(unsigned long addr) { - __asm__ __volatile__( - ".set noreorder\n\t" - ".set mips3\n\t" - "cache %1, (%0)\n\t" - ".set mips0\n\t" - ".set reorder" - : - : "r" (addr), - "i" (Hit_Invalidate_D)); + cache_op(Hit_Invalidate_D, addr); } -extern inline void invalidate_scache_line(unsigned long addr) +static inline void invalidate_scache_line(unsigned long addr) { - __asm__ __volatile__( - ".set noreorder\n\t" - ".set mips3\n\t" - "cache %1, (%0)\n\t" - ".set mips0\n\t" - ".set reorder" - : - : "r" (addr), - "i" (Hit_Invalidate_SD)); + cache_op(Hit_Invalidate_SD, addr); } -extern inline void flush_scache_line(unsigned long addr) +static inline void flush_scache_line(unsigned long addr) { - __asm__ __volatile__( - ".set noreorder\n\t" - ".set mips3\n\t" - "cache %1, (%0)\n\t" - ".set mips0\n\t" - ".set reorder" - : - : "r" (addr), - "i" (Hit_Writeback_Inv_SD)); + cache_op(Hit_Writeback_Inv_SD, addr); } /* * The next two are for badland addresses like signal trampolines. */ -extern inline void protected_flush_icache_line(unsigned long addr) +static inline void protected_flush_icache_line(unsigned long addr) { __asm__ __volatile__( ".set noreorder\n\t" ".set mips3\n" - "1:\tcache %1,(%0)\n" + "1:\tcache %0,(%1)\n" "2:\t.set mips0\n\t" ".set reorder\n\t" ".section\t__ex_table,\"a\"\n\t" STR(PTR)"\t1b,2b\n\t" ".previous" : - : "r" (addr), - "i" (Hit_Invalidate_I)); + : "i" (Hit_Invalidate_I), "r" (addr)); } -extern inline void protected_writeback_dcache_line(unsigned long addr) +/* + * R10000 / R12000 hazard - these processors don't support the Hit_Writeback_D + * cacheop so we use Hit_Writeback_Inv_D which is supported by all R4000-style + * caches. We're talking about one cacheline unnecessarily getting invalidated + * here so the penaltiy isn't overly hard. + */ +static inline void protected_writeback_dcache_line(unsigned long addr) { __asm__ __volatile__( ".set noreorder\n\t" ".set mips3\n" - "1:\tcache %1,(%0)\n" + "1:\tcache %0,(%1)\n" "2:\t.set mips0\n\t" ".set reorder\n\t" ".section\t__ex_table,\"a\"\n\t" STR(PTR)"\t1b,2b\n\t" ".previous" : - : "r" (addr), - "i" (Hit_Writeback_D)); + : "i" (Hit_Writeback_Inv_D), "r" (addr)); +} + +/* + * This one is RM7000-specific + */ +static inline void invalidate_tcache_page(unsigned long addr) +{ + cache_op(Page_Invalidate_T, addr); } #define cache16_unroll32(base,op) \ @@ -178,103 +137,121 @@ extern inline void protected_writeback_dcache_line(unsigned long addr) : "r" (base), \ "i" (op)); -extern inline void blast_dcache16(void) +static inline void blast_dcache16(void) { unsigned long start = KSEG0; - unsigned long end = (start + dcache_size); + unsigned long end = start + dcache_way_size; + unsigned long ws_inc = 1UL << current_cpu_data.dcache.waybit; + unsigned long ws_end = current_cpu_data.dcache.ways << + current_cpu_data.dcache.waybit; + unsigned long ws, addr; - while(start < end) { - cache16_unroll32(start,Index_Writeback_Inv_D); - start += 0x200; - } + for (ws = 0; ws < ws_end; ws += ws_inc) + for (addr = start; addr < end; addr += 0x200) + cache16_unroll32(addr|ws,Index_Writeback_Inv_D); } -extern inline void blast_dcache16_page(unsigned long page) +static inline void blast_dcache16_page(unsigned long page) { unsigned long start = page; - unsigned long end = (start + PAGE_SIZE); + unsigned long end = start + PAGE_SIZE; - while(start < end) { + while (start < end) { cache16_unroll32(start,Hit_Writeback_Inv_D); start += 0x200; } } -extern inline void blast_dcache16_page_indexed(unsigned long page) +static inline void blast_dcache16_page_indexed(unsigned long page) { unsigned long start = page; - unsigned long end = (start + PAGE_SIZE); + unsigned long end = start + PAGE_SIZE; + unsigned long ws_inc = 1UL << current_cpu_data.dcache.waybit; + unsigned long ws_end = current_cpu_data.dcache.ways << + current_cpu_data.dcache.waybit; + unsigned long ws, addr; - while(start < end) { - cache16_unroll32(start,Index_Writeback_Inv_D); - start += 0x200; - } + for (ws = 0; ws < ws_end; ws += ws_inc) + for (addr = start; addr < end; addr += 0x200) + cache16_unroll32(addr|ws,Index_Writeback_Inv_D); } -extern inline void blast_icache16(void) +static inline void blast_icache16(void) { unsigned long start = KSEG0; - unsigned long end = (start + icache_size); + unsigned long end = start + icache_way_size; + unsigned long ws_inc = 1UL << current_cpu_data.icache.waybit; + unsigned long ws_end = current_cpu_data.icache.ways << + current_cpu_data.icache.waybit; + unsigned long ws, addr; - while(start < end) { - cache16_unroll32(start,Index_Invalidate_I); - start += 0x200; - } + for (ws = 0; ws < ws_end; ws += ws_inc) + for (addr = start; addr < end; addr += 0x200) + cache16_unroll32(addr|ws,Index_Invalidate_I); } -extern inline void blast_icache16_page(unsigned long page) +static inline void blast_icache16_page(unsigned long page) { unsigned long start = page; - unsigned long end = (start + PAGE_SIZE); + unsigned long end = start + PAGE_SIZE; - while(start < end) { + while (start < end) { cache16_unroll32(start,Hit_Invalidate_I); start += 0x200; } } -extern inline void blast_icache16_page_indexed(unsigned long page) +static inline void blast_icache16_page_indexed(unsigned long page) { unsigned long start = page; - unsigned long end = (start + PAGE_SIZE); + unsigned long end = start + PAGE_SIZE; + unsigned long ws_inc = 1UL << current_cpu_data.icache.waybit; + unsigned long ws_end = current_cpu_data.icache.ways << + current_cpu_data.icache.waybit; + unsigned long ws, addr; - while(start < end) { - cache16_unroll32(start,Index_Invalidate_I); - start += 0x200; - } + for (ws = 0; ws < ws_end; ws += ws_inc) + for (addr = start; addr < end; addr += 0x200) + cache16_unroll32(addr|ws,Index_Invalidate_I); } -extern inline void blast_scache16(void) +static inline void blast_scache16(void) { unsigned long start = KSEG0; - unsigned long end = KSEG0 + scache_size; + unsigned long end = start + scache_way_size; + unsigned long ws_inc = 1UL << current_cpu_data.scache.waybit; + unsigned long ws_end = current_cpu_data.scache.ways << + current_cpu_data.scache.waybit; + unsigned long ws, addr; - while(start < end) { - cache16_unroll32(start,Index_Writeback_Inv_SD); - start += 0x200; - } + for (ws = 0; ws < ws_end; ws += ws_inc) + for (addr = start; addr < end; addr += 0x200) + cache16_unroll32(addr|ws,Index_Writeback_Inv_SD); } -extern inline void blast_scache16_page(unsigned long page) +static inline void blast_scache16_page(unsigned long page) { unsigned long start = page; unsigned long end = page + PAGE_SIZE; - while(start < end) { + while (start < end) { cache16_unroll32(start,Hit_Writeback_Inv_SD); start += 0x200; } } -extern inline void blast_scache16_page_indexed(unsigned long page) +static inline void blast_scache16_page_indexed(unsigned long page) { unsigned long start = page; - unsigned long end = page + PAGE_SIZE; + unsigned long end = start + PAGE_SIZE; + unsigned long ws_inc = 1UL << current_cpu_data.scache.waybit; + unsigned long ws_end = current_cpu_data.scache.ways << + current_cpu_data.scache.waybit; + unsigned long ws, addr; - while(start < end) { - cache16_unroll32(start,Index_Writeback_Inv_SD); - start += 0x200; - } + for (ws = 0; ws < ws_end; ws += ws_inc) + for (addr = start; addr < end; addr += 0x200) + cache16_unroll32(addr|ws,Index_Writeback_Inv_SD); } #define cache32_unroll32(base,op) \ @@ -303,121 +280,121 @@ extern inline void blast_scache16_page_indexed(unsigned long page) : "r" (base), \ "i" (op)); -extern inline void blast_dcache32(void) +static inline void blast_dcache32(void) { unsigned long start = KSEG0; - unsigned long end = (start + dcache_size); + unsigned long end = start + dcache_way_size; + unsigned long ws_inc = 1UL << current_cpu_data.dcache.waybit; + unsigned long ws_end = current_cpu_data.dcache.ways << + current_cpu_data.dcache.waybit; + unsigned long ws, addr; - while(start < end) { - cache32_unroll32(start,Index_Writeback_Inv_D); - start += 0x400; - } + for (ws = 0; ws < ws_end; ws += ws_inc) + for (addr = start; addr < end; addr += 0x400) + cache32_unroll32(addr|ws,Index_Writeback_Inv_D); } -/* - * Call this function only with interrupts disabled or R4600 V2.0 may blow - * up on you. - * - * R4600 v2.0 bug: "The CACHE instructions Hit_Writeback_Inv_D, - * Hit_Writeback_D, Hit_Invalidate_D and Create_Dirty_Excl_D will only - * operate correctly if the internal data cache refill buffer is empty. These - * CACHE instructions should be separated from any potential data cache miss - * by a load instruction to an uncached address to empty the response buffer." - * (Revision 2.0 device errata from IDT available on http://www.idt.com/ - * in .pdf format.) - */ -extern inline void blast_dcache32_page(unsigned long page) +static inline void blast_dcache32_page(unsigned long page) { unsigned long start = page; - unsigned long end = (start + PAGE_SIZE); - - /* - * Sigh ... workaround for R4600 v1.7 bug. Explanation see above. - */ - *(volatile unsigned long *)KSEG1; + unsigned long end = start + PAGE_SIZE; - __asm__ __volatile__("nop;nop;nop;nop"); - while(start < end) { + while (start < end) { cache32_unroll32(start,Hit_Writeback_Inv_D); start += 0x400; } } -extern inline void blast_dcache32_page_indexed(unsigned long page) +static inline void blast_dcache32_page_indexed(unsigned long page) { unsigned long start = page; - unsigned long end = (start + PAGE_SIZE); + unsigned long end = start + PAGE_SIZE; + unsigned long ws_inc = 1UL << current_cpu_data.dcache.waybit; + unsigned long ws_end = current_cpu_data.dcache.ways << + current_cpu_data.dcache.waybit; + unsigned long ws, addr; - while(start < end) { - cache32_unroll32(start,Index_Writeback_Inv_D); - start += 0x400; - } + for (ws = 0; ws < ws_end; ws += ws_inc) + for (addr = start; addr < end; addr += 0x400) + cache32_unroll32(addr|ws,Index_Writeback_Inv_D); } -extern inline void blast_icache32(void) +static inline void blast_icache32(void) { unsigned long start = KSEG0; - unsigned long end = (start + icache_size); + unsigned long end = start + icache_way_size; + unsigned long ws_inc = 1UL << current_cpu_data.icache.waybit; + unsigned long ws_end = current_cpu_data.icache.ways << + current_cpu_data.icache.waybit; + unsigned long ws, addr; - while(start < end) { - cache32_unroll32(start,Index_Invalidate_I); - start += 0x400; - } + for (ws = 0; ws < ws_end; ws += ws_inc) + for (addr = start; addr < end; addr += 0x400) + cache32_unroll32(addr|ws,Index_Invalidate_I); } -extern inline void blast_icache32_page(unsigned long page) +static inline void blast_icache32_page(unsigned long page) { unsigned long start = page; - unsigned long end = (start + PAGE_SIZE); + unsigned long end = start + PAGE_SIZE; - while(start < end) { + while (start < end) { cache32_unroll32(start,Hit_Invalidate_I); start += 0x400; } } -extern inline void blast_icache32_page_indexed(unsigned long page) +static inline void blast_icache32_page_indexed(unsigned long page) { unsigned long start = page; - unsigned long end = (start + PAGE_SIZE); + unsigned long end = start + PAGE_SIZE; + unsigned long ws_inc = 1UL << current_cpu_data.icache.waybit; + unsigned long ws_end = current_cpu_data.icache.ways << + current_cpu_data.icache.waybit; + unsigned long ws, addr; - while(start < end) { - cache32_unroll32(start,Index_Invalidate_I); - start += 0x400; - } + for (ws = 0; ws < ws_end; ws += ws_inc) + for (addr = start; addr < end; addr += 0x400) + cache32_unroll32(addr|ws,Index_Invalidate_I); } -extern inline void blast_scache32(void) +static inline void blast_scache32(void) { unsigned long start = KSEG0; - unsigned long end = KSEG0 + scache_size; + unsigned long end = start + scache_way_size; + unsigned long ws_inc = 1UL << current_cpu_data.scache.waybit; + unsigned long ws_end = current_cpu_data.scache.ways << + current_cpu_data.scache.waybit; + unsigned long ws, addr; - while(start < end) { - cache32_unroll32(start,Index_Writeback_Inv_SD); - start += 0x400; - } + for (ws = 0; ws < ws_end; ws += ws_inc) + for (addr = start; addr < end; addr += 0x400) + cache32_unroll32(addr|ws,Index_Writeback_Inv_SD); } -extern inline void blast_scache32_page(unsigned long page) +static inline void blast_scache32_page(unsigned long page) { unsigned long start = page; unsigned long end = page + PAGE_SIZE; - while(start < end) { + while (start < end) { cache32_unroll32(start,Hit_Writeback_Inv_SD); start += 0x400; } } -extern inline void blast_scache32_page_indexed(unsigned long page) +static inline void blast_scache32_page_indexed(unsigned long page) { unsigned long start = page; - unsigned long end = page + PAGE_SIZE; + unsigned long end = start + PAGE_SIZE; + unsigned long ws_inc = 1UL << current_cpu_data.scache.waybit; + unsigned long ws_end = current_cpu_data.scache.ways << + current_cpu_data.scache.waybit; + unsigned long ws, addr; - while(start < end) { - cache32_unroll32(start,Index_Writeback_Inv_SD); - start += 0x400; - } + for (ws = 0; ws < ws_end; ws += ws_inc) + for (addr = start; addr < end; addr += 0x400) + cache32_unroll32(addr|ws,Index_Writeback_Inv_SD); } #define cache64_unroll32(base,op) \ @@ -446,37 +423,82 @@ extern inline void blast_scache32_page_indexed(unsigned long page) : "r" (base), \ "i" (op)); -extern inline void blast_scache64(void) +static inline void blast_icache64(void) { unsigned long start = KSEG0; - unsigned long end = KSEG0 + scache_size; + unsigned long end = start + icache_way_size; + unsigned long ws_inc = 1UL << current_cpu_data.icache.waybit; + unsigned long ws_end = current_cpu_data.icache.ways << + current_cpu_data.icache.waybit; + unsigned long ws, addr; + + for (ws = 0; ws < ws_end; ws += ws_inc) + for (addr = start; addr < end; addr += 0x800) + cache64_unroll32(addr|ws,Index_Invalidate_I); +} + +static inline void blast_icache64_page(unsigned long page) +{ + unsigned long start = page; + unsigned long end = start + PAGE_SIZE; - while(start < end) { - cache64_unroll32(start,Index_Writeback_Inv_SD); + while (start < end) { + cache64_unroll32(start,Hit_Invalidate_I); start += 0x800; } } -extern inline void blast_scache64_page(unsigned long page) +static inline void blast_icache64_page_indexed(unsigned long page) +{ + unsigned long start = page; + unsigned long end = start + PAGE_SIZE; + unsigned long ws_inc = 1UL << current_cpu_data.icache.waybit; + unsigned long ws_end = current_cpu_data.icache.ways << + current_cpu_data.icache.waybit; + unsigned long ws, addr; + + for (ws = 0; ws < ws_end; ws += ws_inc) + for (addr = start; addr < end; addr += 0x800) + cache64_unroll32(addr|ws,Index_Invalidate_I); +} + +static inline void blast_scache64(void) +{ + unsigned long start = KSEG0; + unsigned long end = start + scache_way_size; + unsigned long ws_inc = 1UL << current_cpu_data.scache.waybit; + unsigned long ws_end = current_cpu_data.scache.ways << + current_cpu_data.scache.waybit; + unsigned long ws, addr; + + for (ws = 0; ws < ws_end; ws += ws_inc) + for (addr = start; addr < end; addr += 0x800) + cache64_unroll32(addr|ws,Index_Writeback_Inv_SD); +} + +static inline void blast_scache64_page(unsigned long page) { unsigned long start = page; unsigned long end = page + PAGE_SIZE; - while(start < end) { + while (start < end) { cache64_unroll32(start,Hit_Writeback_Inv_SD); start += 0x800; } } -extern inline void blast_scache64_page_indexed(unsigned long page) +static inline void blast_scache64_page_indexed(unsigned long page) { unsigned long start = page; - unsigned long end = page + PAGE_SIZE; + unsigned long end = start + PAGE_SIZE; + unsigned long ws_inc = 1UL << current_cpu_data.scache.waybit; + unsigned long ws_end = current_cpu_data.scache.ways << + current_cpu_data.scache.waybit; + unsigned long ws, addr; - while(start < end) { - cache64_unroll32(start,Index_Writeback_Inv_SD); - start += 0x800; - } + for (ws = 0; ws < ws_end; ws += ws_inc) + for (addr = start; addr < end; addr += 0x800) + cache64_unroll32(addr|ws,Index_Writeback_Inv_SD); } #define cache128_unroll32(base,op) \ @@ -505,25 +527,43 @@ extern inline void blast_scache64_page_indexed(unsigned long page) : "r" (base), \ "i" (op)); -extern inline void blast_scache128(void) +static inline void blast_scache128(void) { unsigned long start = KSEG0; - unsigned long end = KSEG0 + scache_size; + unsigned long end = start + scache_way_size; + unsigned long ws_inc = 1UL << current_cpu_data.scache.waybit; + unsigned long ws_end = current_cpu_data.scache.ways << + current_cpu_data.scache.waybit; + unsigned long ws, addr; - while(start < end) { - cache128_unroll32(start,Index_Writeback_Inv_SD); - start += 0x1000; - } + for (ws = 0; ws < ws_end; ws += ws_inc) + for (addr = start; addr < end; addr += 0x1000) + cache128_unroll32(addr|ws,Index_Writeback_Inv_SD); } -extern inline void blast_scache128_page(unsigned long page) +static inline void blast_scache128_page(unsigned long page) { - cache128_unroll32(page,Hit_Writeback_Inv_SD); + unsigned long start = page; + unsigned long end = page + PAGE_SIZE; + + while (start < end) { + cache128_unroll32(start,Hit_Writeback_Inv_SD); + start += 0x1000; + } } -extern inline void blast_scache128_page_indexed(unsigned long page) +static inline void blast_scache128_page_indexed(unsigned long page) { - cache128_unroll32(page,Index_Writeback_Inv_SD); + unsigned long start = page; + unsigned long end = start + PAGE_SIZE; + unsigned long ws_inc = 1UL << current_cpu_data.scache.waybit; + unsigned long ws_end = current_cpu_data.scache.ways << + current_cpu_data.scache.waybit; + unsigned long ws, addr; + + for (ws = 0; ws < ws_end; ws += ws_inc) + for (addr = start; addr < end; addr += 0x1000) + cache128_unroll32(addr|ws,Index_Writeback_Inv_SD); } -#endif /* !(_MIPS_R4KCACHE_H) */ +#endif /* __ASM_R4KCACHE_H */ diff --git a/include/asm-mips/reg.h b/include/asm-mips/reg.h index 35505b70f84b..36181de82a8b 100644 --- a/include/asm-mips/reg.h +++ b/include/asm-mips/reg.h @@ -59,7 +59,7 @@ #define EF_CP0_EPC 40 #define EF_CP0_BADVADDR 41 #define EF_CP0_STATUS 42 -#define EF_CP0_CAUSE 44 +#define EF_CP0_CAUSE 43 #define EF_SIZE 180 /* size in bytes */ diff --git a/include/asm-mips/riscos-syscall.h b/include/asm-mips/riscos-syscall.h index 8cb87df377bb..4d8eb15461eb 100644 --- a/include/asm-mips/riscos-syscall.h +++ b/include/asm-mips/riscos-syscall.h @@ -220,7 +220,7 @@ #define __NR_SVR4_reserved62 (__NR_SVR4 + 199) #define __NR_SVR4_reserved63 (__NR_SVR4 + 200) #define __NR_SVR4_aread (__NR_SVR4 + 201) -#define __NR_SVR4_awrite (__NR_SVR4 + 202) +#define __NR_SVR4_awrite (__NR_SVR4 + 202) #define __NR_SVR4_listio (__NR_SVR4 + 203) #define __NR_SVR4_mips_acancel (__NR_SVR4 + 204) #define __NR_SVR4_astatus (__NR_SVR4 + 205) diff --git a/include/asm-mips/rmap.h b/include/asm-mips/rmap.h index 2dc334a3b5f7..c9efd7b98749 100644 --- a/include/asm-mips/rmap.h +++ b/include/asm-mips/rmap.h @@ -1,7 +1,7 @@ -#ifndef _MIPS_RMAP_H -#define _MIPS_RMAP_H +#ifndef __ASM_RMAP_H +#define __ASM_RMAP_H /* nothing to see, move along */ #include <asm-generic/rmap.h> -#endif +#endif /* __ASM_RMAP_H */ diff --git a/include/asm-mips/rtc.h b/include/asm-mips/rtc.h new file mode 100644 index 000000000000..ffd02109a0e5 --- /dev/null +++ b/include/asm-mips/rtc.h @@ -0,0 +1,10 @@ +#ifndef _I386_RTC_H +#define _I386_RTC_H + +/* + * x86 uses the default access methods for the RTC. + */ + +#include <asm-generic/rtc.h> + +#endif diff --git a/include/asm-mips/scatterlist.h b/include/asm-mips/scatterlist.h index 861c2247f921..1693005bb6a4 100644 --- a/include/asm-mips/scatterlist.h +++ b/include/asm-mips/scatterlist.h @@ -1,21 +1,13 @@ -#ifndef __ASM_MIPS_SCATTERLIST_H -#define __ASM_MIPS_SCATTERLIST_H +#ifndef __ASM_SCATTERLIST_H +#define __ASM_SCATTERLIST_H struct scatterlist { - struct page *page; - unsigned int offset; - unsigned int length; - - __u32 dvma_address; + struct page * page; + unsigned int offset; + dma_addr_t dma_address; + unsigned int length; }; -struct mmu_sglist { - char *addr; - char *__dont_touch; - unsigned int len; - __u32 dvma_addr; -}; - -#define ISA_DMA_THRESHOLD (0x00ffffff) +#define ISA_DMA_THRESHOLD (0x00ffffffUL) -#endif /* __ASM_MIPS_SCATTERLIST_H */ +#endif /* __ASM_SCATTERLIST_H */ diff --git a/include/asm-mips/sections.h b/include/asm-mips/sections.h new file mode 100644 index 000000000000..c1da1c9437c2 --- /dev/null +++ b/include/asm-mips/sections.h @@ -0,0 +1,9 @@ +#ifndef __ASM_SECTIONS_H +#define __ASM_SECTIONS_H + +#include <asm-generic/sections.h> + +extern char _stext, _etext; +extern char _end; + +#endif /* __ASM_SECTIONS_H */ diff --git a/include/asm-mips/semaphore-helper.h b/include/asm-mips/semaphore-helper.h index 3650c32ad182..1151c37e1a16 100644 --- a/include/asm-mips/semaphore-helper.h +++ b/include/asm-mips/semaphore-helper.h @@ -3,14 +3,15 @@ * * Copyright (C) 1996 Linus Torvalds * Copyright (C) 1999 Andrea Arcangeli - * Copyright (C) 1999 Ralf Baechle - * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) 1999, 2001, 2002 Ralf Baechle + * Copyright (C) 1999, 2001 Silicon Graphics, Inc. * Copyright (C) 2000 MIPS Technologies, Inc. */ #ifndef _ASM_SEMAPHORE_HELPER_H #define _ASM_SEMAPHORE_HELPER_H #include <linux/config.h> +#include <linux/errno.h> #define sem_read(a) ((a)->counter) #define sem_inc(a) (((a)->counter)++) @@ -25,20 +26,19 @@ static inline void wake_one_more(struct semaphore * sem) #ifdef CONFIG_CPU_HAS_LLSC -static inline int -waking_non_zero(struct semaphore *sem) +static inline int waking_non_zero(struct semaphore *sem) { int ret, tmp; __asm__ __volatile__( - "1:\tll\t%1, %2\n\t" + "1:\tll\t%1, %2\t\t\t# waking_non_zero\n\t" "blez\t%1, 2f\n\t" "subu\t%0, %1, 1\n\t" "sc\t%0, %2\n\t" - "beqz\t%0, 1b\n\t" + "beqz\t%0, 1b\n" "2:" - : "=r" (ret), "=r" (tmp), "=m" (sem->waking) - : "0"(0)); + : "=r" (ret), "=r" (tmp), "+m" (sem->waking) + : "0" (0)); return ret; } @@ -55,12 +55,12 @@ static inline int waking_non_zero(struct semaphore *sem) unsigned long flags; int ret = 0; - save_and_cli(flags); + local_irq_save(flags); if (sem_read(&sem->waking) > 0) { sem_dec(&sem->waking); ret = 1; } - restore_flags(flags); + local_irq_restore(flags); return ret; } #endif /* !CONFIG_CPU_HAS_LLSC */ @@ -74,22 +74,22 @@ static inline int waking_non_zero(struct semaphore *sem) * -EINTR interrupted * * We must undo the sem->count down_interruptible decrement - * simultaneously and atomicly with the sem->waking adjustment, + * simultaneously and atomically with the sem->waking adjustment, * otherwise we can race with wake_one_more. * - * This is accomplished by doing a 64-bit ll/sc on the 2 32-bit words. + * This is accomplished by doing a 64-bit lld/scd on the 2 32-bit words. * - * This is crazy. Normally it stricly forbidden to use 64-bit operations + * This is crazy. Normally it's strictly forbidden to use 64-bit operations * in the 32-bit MIPS kernel. In this case it's however ok because if an * interrupt has destroyed the upper half of registers sc will fail. - * Note also that this will not work for MIPS32 CPUS! + * Note also that this will not work for MIPS32 CPUs! * * Pseudocode: * * If(sem->waking > 0) { * Decrement(sem->waking) * Return(SUCCESS) - * } else If(segnal_pending(tsk)) { + * } else If(signal_pending(tsk)) { * Increment(sem->count) * Return(-EINTR) * } else { @@ -103,7 +103,7 @@ waking_non_zero_interruptible(struct semaphore *sem, struct task_struct *tsk) long ret, tmp; __asm__ __volatile__( - ".set\tpush\n\t" + ".set\tpush\t\t\t# waking_non_zero_interruptible\n\t" ".set\tmips3\n\t" ".set\tnoat\n" "0:\tlld\t%1, %2\n\t" @@ -127,12 +127,11 @@ waking_non_zero_interruptible(struct semaphore *sem, struct task_struct *tsk) } /* - * waking_non_zero_trylock is unused. we do everything in + * waking_non_zero_trylock is unused. we do everything in * down_trylock and let non-ll/sc hosts bounce around. */ -static inline int -waking_non_zero_trylock(struct semaphore *sem) +static inline int waking_non_zero_trylock(struct semaphore *sem) { #if WAITQUEUE_DEBUG CHECK_MAGIC(sem->__magic); @@ -149,7 +148,7 @@ static inline int waking_non_zero_interruptible(struct semaphore *sem, int ret = 0; unsigned long flags; - save_and_cli(flags); + local_irq_save(flags); if (sem_read(&sem->waking) > 0) { sem_dec(&sem->waking); ret = 1; @@ -157,7 +156,7 @@ static inline int waking_non_zero_interruptible(struct semaphore *sem, sem_inc(&sem->count); ret = -EINTR; } - restore_flags(flags); + local_irq_restore(flags); return ret; } @@ -166,14 +165,14 @@ static inline int waking_non_zero_trylock(struct semaphore *sem) int ret = 1; unsigned long flags; - save_and_cli(flags); + local_irq_save(flags); if (sem_read(&sem->waking) <= 0) sem_inc(&sem->count); else { sem_dec(&sem->waking); ret = 0; } - restore_flags(flags); + local_irq_restore(flags); return ret; } diff --git a/include/asm-mips/semaphore.h b/include/asm-mips/semaphore.h index 3df320abd420..34fc00d60460 100644 --- a/include/asm-mips/semaphore.h +++ b/include/asm-mips/semaphore.h @@ -1,23 +1,21 @@ /* - * SMP- and interrupt-safe semaphores.. - * * 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. * - * (C) Copyright 1996 Linus Torvalds - * (C) Copyright 1998, 99, 2000, 01 Ralf Baechle - * (C) Copyright 1999, 2000 Silicon Graphics, Inc. - * Copyright (C) 2000, 01 MIPS Technologies, Inc. All rights reserved. + * Copyright (C) 1996 Linus Torvalds + * Copyright (C) 1998, 99, 2000, 01 Ralf Baechle + * Copyright (C) 1999, 2000, 01 Silicon Graphics, Inc. + * Copyright (C) 2000, 01 MIPS Technologies, Inc. */ #ifndef _ASM_SEMAPHORE_H #define _ASM_SEMAPHORE_H +#include <linux/config.h> #include <asm/system.h> #include <asm/atomic.h> #include <linux/spinlock.h> #include <linux/wait.h> -#include <linux/config.h> #include <linux/rwsem.h> struct semaphore { @@ -94,6 +92,10 @@ static inline void down(struct semaphore * sem) __down(sem); } +/* + * Interruptible try to acquire a semaphore. If we obtained + * it, return zero. If we were interrupted, returns -EINTR + */ static inline int down_interruptible(struct semaphore * sem) { int ret = 0; @@ -108,6 +110,10 @@ static inline int down_interruptible(struct semaphore * sem) #ifndef CONFIG_CPU_HAS_LLDSCD +/* + * Non-blockingly attempt to down() a semaphore. + * Returns zero if we acquired it + */ static inline int down_trylock(struct semaphore * sem) { int ret = 0; @@ -122,8 +128,7 @@ static inline int down_trylock(struct semaphore * sem) * down_trylock returns 0 on success, 1 if we failed to get the lock. * * We must manipulate count and waking simultaneously and atomically. - * Here, we this by using ll/sc on the pair of 32-bit words. This - * won't work on MIPS32 platforms, however, and must be rewritten. + * Here, we do this by using lld/scd on the pair of 32-bit words. * * Pseudocode: * @@ -158,12 +163,12 @@ static inline int down_trylock(struct semaphore * sem) "sll\t%2, %1, 0\n\t" "blez\t%2, 1f\n\t" "daddiu\t%1, %1, -1\n\t" - "b\t2f\n\t" - "1:\tdaddu\t%1, %1, %3\n" + "b\t2f\n" + "1:\tdaddu\t%1, %1, %3\n\t" "li\t%0, 1\n" "2:\tscd\t%1, %4\n\t" "beqz\t%1, 0b\n\t" - ".set mips0" + ".set\tmips0" : "=&r"(ret), "=&r"(tmp), "=&r"(tmp2), "=&r"(sub) : "m"(*sem) : "memory"); diff --git a/include/asm-mips/sembuf.h b/include/asm-mips/sembuf.h index 31892984e485..7281a4decaa0 100644 --- a/include/asm-mips/sembuf.h +++ b/include/asm-mips/sembuf.h @@ -1,7 +1,7 @@ #ifndef _ASM_SEMBUF_H #define _ASM_SEMBUF_H -/* +/* * The semid64_ds structure for the MIPS architecture. * Note extra padding because this structure is passed back and forth * between kernel and user space. diff --git a/include/asm-mips/serial.h b/include/asm-mips/serial.h index 346b321e4cfc..66a9334c46c8 100644 --- a/include/asm-mips/serial.h +++ b/include/asm-mips/serial.h @@ -7,7 +7,6 @@ * Copyright (C) 1999, 2000 Silicon Graphics, Inc. */ #include <linux/config.h> -#include <asm/bootinfo.h> #include <asm/jazz.h> /* @@ -65,9 +64,9 @@ #ifdef CONFIG_MIPS_JAZZ #define _JAZZ_SERIAL_INIT(int, base) \ - { baud_base: JAZZ_BASE_BAUD, irq: int, flags: STD_COM_FLAGS, \ - iomem_base: (u8 *) base, iomem_reg_shift: 0, \ - io_type: SERIAL_IO_MEM } + { .baud_base = JAZZ_BASE_BAUD, .irq = int, .flags = STD_COM_FLAGS, \ + .iomem_base = (u8 *) base, .iomem_reg_shift = 0, \ + .io_type = SERIAL_IO_MEM } #define JAZZ_SERIAL_PORT_DEFNS \ _JAZZ_SERIAL_INIT(JAZZ_SERIAL1_IRQ, JAZZ_SERIAL1_BASE), \ _JAZZ_SERIAL_INIT(JAZZ_SERIAL2_IRQ, JAZZ_SERIAL2_BASE), @@ -85,6 +84,26 @@ #define ATLAS_SERIAL_PORT_DEFNS #endif +#ifdef CONFIG_MIPS_SEAD +#include <asm/mips-boards/sead.h> +#include <asm/mips-boards/seadint.h> +#define SEAD_SERIAL_PORT_DEFNS \ + /* UART CLK PORT IRQ FLAGS */ \ + { 0, SEAD_BASE_BAUD, SEAD_UART0_REGS_BASE, SEADINT_UART0, STD_COM_FLAGS }, /* ttyS0 */ +#else +#define SEAD_SERIAL_PORT_DEFNS +#endif + +#ifdef CONFIG_MIPS_COBALT +#include <asm/cobalt/cobalt.h> +#define COBALT_BASE_BAUD (18432000 / 16) +#define COBALT_SERIAL_PORT_DEFNS \ + /* UART CLK PORT IRQ FLAGS */ \ + { 0, COBALT_BASE_BAUD, 0xc800000, COBALT_SERIAL_IRQ, STD_COM_FLAGS }, /* ttyS0 */ +#else +#define COBALT_SERIAL_PORT_DEFNS +#endif + /* * Both Galileo boards have the same UART mappings. */ @@ -92,12 +111,14 @@ #include <asm/galileo-boards/ev96100.h> #include <asm/galileo-boards/ev96100int.h> #define EV96100_SERIAL_PORT_DEFNS \ - { baud_base: EV96100_BASE_BAUD, port: EV96100_UART0_REGS_BASE, \ - irq: EV96100INT_UART_0, flags: STD_COM_FLAGS, type: 0x3, \ - iomem_base: EV96100_UART0_REGS_BASE }, \ - { baud_base: EV96100_BASE_BAUD, port: EV96100_UART1_REGS_BASE, \ - irq: EV96100INT_UART_0, flags: STD_COM_FLAGS, type: 0x3, \ - iomem_base: EV96100_UART1_REGS_BASE }, + { .baud_base = EV96100_BASE_BAUD, .irq = EV96100INT_UART_0, \ + .flags = STD_COM_FLAGS, \ + .iomem_base = EV96100_UART0_REGS_BASE, .iomem_reg_shift = 2, \ + .io_type = SERIAL_IO_MEM }, \ + { .baud_base = EV96100_BASE_BAUD, .irq = EV96100INT_UART_0, \ + .flags = STD_COM_FLAGS, \ + .iomem_base = EV96100_UART1_REGS_BASE, .iomem_reg_shift = 2, \ + .io_type = SERIAL_IO_MEM }, #else #define EV96100_SERIAL_PORT_DEFNS #endif @@ -107,16 +128,16 @@ #include <asm/it8172/it8172_int.h> #include <asm/it8712.h> #define ITE_SERIAL_PORT_DEFNS \ - { baud_base: BASE_BAUD, port: (IT8172_PCI_IO_BASE + IT_UART_BASE), \ - irq: IT8172_UART_IRQ, flags: STD_COM_FLAGS, type: 0x3 }, \ - { baud_base: (24000000/(16*13)), port: (IT8172_PCI_IO_BASE + IT8712_UART1_PORT), \ - irq: IT8172_SERIRQ_4, flags: STD_COM_FLAGS, type: 0x3 }, \ + { .baud_base = BASE_BAUD, .port = (IT8172_PCI_IO_BASE + IT_UART_BASE), \ + .irq = IT8172_UART_IRQ, .flags = STD_COM_FLAGS, .type = 0x3 }, \ + { .baud_base = (24000000/(16*13)), .port = (IT8172_PCI_IO_BASE + IT8712_UART1_PORT), \ + .irq = IT8172_SERIRQ_4, .flags = STD_COM_FLAGS, .type = 0x3 }, \ /* Smart Card Reader 0 */ \ - { baud_base: BASE_BAUD, port: (IT8172_PCI_IO_BASE + IT_SCR0_BASE), \ - irq: IT8172_SCR0_IRQ, flags: STD_COM_FLAGS, type: 0x3 }, \ + { .baud_base = BASE_BAUD, .port = (IT8172_PCI_IO_BASE + IT_SCR0_BASE), \ + .irq = IT8172_SCR0_IRQ, .flags = STD_COM_FLAGS, .type = 0x3 }, \ /* Smart Card Reader 1 */ \ - { baud_base: BASE_BAUD, port: (IT8172_PCI_IO_BASE + IT_SCR1_BASE), \ - irq: IT8172_SCR1_IRQ, flags: STD_COM_FLAGS, type: 0x3 }, + { .baud_base = BASE_BAUD, .port = (IT8172_PCI_IO_BASE + IT_SCR1_BASE), \ + .irq = IT8172_SCR1_IRQ, .flags = STD_COM_FLAGS, .type = 0x3 }, #else #define ITE_SERIAL_PORT_DEFNS #endif @@ -125,28 +146,55 @@ #include <asm/it8172/it8172.h> #include <asm/it8172/it8172_int.h> #define IVR_SERIAL_PORT_DEFNS \ - { baud_base: BASE_BAUD, port: (IT8172_PCI_IO_BASE + IT_UART_BASE), \ - irq: IT8172_UART_IRQ, flags: STD_COM_FLAGS, type: 0x3 }, \ + { .baud_base = BASE_BAUD, .port = (IT8172_PCI_IO_BASE + IT_UART_BASE), \ + .irq = IT8172_UART_IRQ, .flags = STD_COM_FLAGS, .type = 0x3 }, \ /* Smart Card Reader 1 */ \ - { baud_base: BASE_BAUD, port: (IT8172_PCI_IO_BASE + IT_SCR1_BASE), \ - irq: IT8172_SCR1_IRQ, flags: STD_COM_FLAGS, type: 0x3 }, + { .baud_base = BASE_BAUD, .port = (IT8172_PCI_IO_BASE + IT_SCR1_BASE), \ + .irq = IT8172_SCR1_IRQ, .flags = STD_COM_FLAGS, .type = 0x3 }, #else #define IVR_SERIAL_PORT_DEFNS #endif -#ifdef CONFIG_AU1000_UART +#ifdef CONFIG_LASAT +#include <asm/lasat/serial.h> +#define LASAT_SERIAL_PORT_DEFNS \ + { .baud_base = LASAT_BASE_BAUD, .irq = LASATINT_UART, \ + .flags = STD_COM_FLAGS, \ + .port = LASAT_UART_REGS_BASE, /* Only for display */ \ + .iomem_base = (u8 *)KSEG1ADDR(LASAT_UART_REGS_BASE), \ + .iomem_reg_shift = LASAT_UART_REGS_SHIFT, .io_type = SERIAL_IO_MEM }, +#else +#define LASAT_SERIAL_PORT_DEFNS +#endif + +#ifdef CONFIG_SERIAL_AU1X00 #include <asm/au1000.h> -#define AU1000_SERIAL_PORT_DEFNS \ - { baud_base: 0, port: UART0_ADDR, irq: AU1000_UART0_INT, \ - flags: STD_COM_FLAGS, type: 1 }, \ - { baud_base: 0, port: UART1_ADDR, irq: AU1000_UART1_INT, \ - flags: STD_COM_FLAGS, type: 1 }, \ - { baud_base: 0, port: UART2_ADDR, irq: AU1000_UART2_INT, \ - flags: STD_COM_FLAGS, type: 1 }, \ - { baud_base: 0, port: UART3_ADDR, irq: AU1000_UART3_INT, \ - flags: STD_COM_FLAGS, type: 1 }, +#define AU1X00_SERIAL_PORT_DEFNS \ + { .baud_base = 0, .iomem_base = (u8 *)UART0_ADDR, \ + .irq = AU1000_UART0_INT, .flags = STD_COM_FLAGS, \ + .iomem_reg_shift = 2, }, \ + { .baud_base = 0, .iomem_base = (u8 *)UART1_ADDR, \ + .irq = AU1000_UART1_INT, .flags = STD_COM_FLAGS, \ + .iomem_reg_shift = 2 }, \ + { .baud_base = 0, .iomem_base = (u8 *)UART2_ADDR, \ + .irq = AU1000_UART2_INT, .flags = STD_COM_FLAGS, \ + .iomem_reg_shift = 2}, \ + { .baud_base = 0, .iomem_base = (u8 *)UART3_ADDR, \ + .irq = AU1000_UART3_INT, .flags = STD_COM_FLAGS, \ + .iomem_reg_shift = 2}, +#else +#define AU1X00_SERIAL_PORT_DEFNS +#endif + +#ifdef CONFIG_TOSHIBA_JMR3927 +#include <asm/jmr3927/jmr3927.h> +#define TXX927_SERIAL_PORT_DEFNS \ + { .baud_base = JMR3927_BASE_BAUD, .port = UART0_ADDR, .irq = UART0_INT, \ + .flags = UART0_FLAGS, .type = 1 }, \ + { .baud_base = JMR3927_BASE_BAUD, .port = UART1_ADDR, .irq = UART1_INT, \ + .flags = UART1_FLAGS, .type = 1 }, #else -#define AU1000_SERIAL_PORT_DEFNS +#define TXX927_SERIAL_PORT_DEFNS #endif #ifdef CONFIG_HAVE_STD_PC_SERIAL_PORT @@ -237,36 +285,85 @@ #define OCELOT_SERIAL1_BASE 0xe0001020 #define _OCELOT_SERIAL_INIT(int, base) \ - { baud_base: OCELOT_BASE_BAUD, irq: int, flags: STD_COM_FLAGS, \ - iomem_base: (u8 *) base, iomem_reg_shift: 2, \ - io_type: SERIAL_IO_MEM } + { .baud_base = OCELOT_BASE_BAUD, .irq = int, .flags = STD_COM_FLAGS, \ + .iomem_base = (u8 *) base, .iomem_reg_shift = 2, \ + .io_type = SERIAL_IO_MEM } #define MOMENCO_OCELOT_SERIAL_PORT_DEFNS \ _OCELOT_SERIAL_INIT(OCELOT_SERIAL1_IRQ, OCELOT_SERIAL1_BASE) #else #define MOMENCO_OCELOT_SERIAL_PORT_DEFNS #endif +#ifdef CONFIG_MOMENCO_OCELOT_G +/* Ordinary NS16552 duart with a 20MHz crystal. */ +#define OCELOT_G_BASE_BAUD ( 20000000 / 16 ) + +#define OCELOT_G_SERIAL1_IRQ 4 +#if 0 +#define OCELOT_G_SERIAL1_BASE 0xe0001020 +#else +#define OCELOT_G_SERIAL1_BASE 0xfd000020 +#endif + +#define _OCELOT_G_SERIAL_INIT(int, base) \ + { .baud_base = OCELOT_G_BASE_BAUD, .irq = int, .flags = STD_COM_FLAGS,\ + .iomem_base = (u8 *) base, .iomem_reg_shift = 2, \ + .io_type = SERIAL_IO_MEM } +#define MOMENCO_OCELOT_G_SERIAL_PORT_DEFNS \ + _OCELOT_G_SERIAL_INIT(OCELOT_G_SERIAL1_IRQ, OCELOT_G_SERIAL1_BASE) +#else +#define MOMENCO_OCELOT_G_SERIAL_PORT_DEFNS +#endif + +#ifdef CONFIG_MOMENCO_OCELOT_C +/* Ordinary NS16552 duart with a 20MHz crystal. */ +#define OCELOT_C_BASE_BAUD ( 20000000 / 16 ) + +#define OCELOT_C_SERIAL1_IRQ 80 +#define OCELOT_C_SERIAL1_BASE 0xfd000020 + +#define OCELOT_C_SERIAL2_IRQ 81 +#define OCELOT_C_SERIAL2_BASE 0xfd000000 + +#define _OCELOT_C_SERIAL_INIT(int, base) \ + { baud_base: OCELOT_C_BASE_BAUD, irq: int, flags: STD_COM_FLAGS,\ + iomem_base: (u8 *) base, iomem_reg_shift: 2, \ + io_type: SERIAL_IO_MEM } +#define MOMENCO_OCELOT_C_SERIAL_PORT_DEFNS \ + _OCELOT_C_SERIAL_INIT(OCELOT_C_SERIAL1_IRQ, OCELOT_C_SERIAL1_BASE), \ + _OCELOT_C_SERIAL_INIT(OCELOT_C_SERIAL2_IRQ, OCELOT_C_SERIAL2_BASE) +#else +#define MOMENCO_OCELOT_C_SERIAL_PORT_DEFNS +#endif + #ifdef CONFIG_DDB5477 +#include <asm/ddb5xxx/ddb5477.h> #define DDB5477_SERIAL_PORT_DEFNS \ - { baud_base: BASE_BAUD, irq: 12, flags: STD_COM_FLAGS, \ - iomem_base: (u8*)0xbfa04200, iomem_reg_shift: 3, \ - io_type: SERIAL_IO_MEM},\ - { baud_base: BASE_BAUD, irq: 28, flags: STD_COM_FLAGS, \ - iomem_base: (u8*)0xbfa04240, iomem_reg_shift: 3, \ - io_type: SERIAL_IO_MEM}, + { .baud_base = BASE_BAUD, .irq = VRC5477_IRQ_UART0, \ + .flags = STD_COM_FLAGS, .iomem_base = (u8*)0xbfa04200, \ + .iomem_reg_shift = 3, .io_type = SERIAL_IO_MEM}, \ + { .baud_base = BASE_BAUD, .irq = VRC5477_IRQ_UART1, \ + .flags = STD_COM_FLAGS, .iomem_base = (u8*)0xbfa04240, \ + .iomem_reg_shift = 3, .io_type = SERIAL_IO_MEM}, #else #define DDB5477_SERIAL_PORT_DEFNS #endif -#define SERIAL_PORT_DFNS \ - IVR_SERIAL_PORT_DEFNS \ - ITE_SERIAL_PORT_DEFNS \ - ATLAS_SERIAL_PORT_DEFNS \ - EV96100_SERIAL_PORT_DEFNS \ - JAZZ_SERIAL_PORT_DEFNS \ - STD_SERIAL_PORT_DEFNS \ - EXTRA_SERIAL_PORT_DEFNS \ - HUB6_SERIAL_PORT_DFNS \ - MOMENCO_OCELOT_SERIAL_PORT_DEFNS\ - AU1000_SERIAL_PORT_DEFNS \ +#define SERIAL_PORT_DFNS \ + IVR_SERIAL_PORT_DEFNS \ + ITE_SERIAL_PORT_DEFNS \ + ATLAS_SERIAL_PORT_DEFNS \ + SEAD_SERIAL_PORT_DEFNS \ + COBALT_SERIAL_PORT_DEFNS \ + LASAT_SERIAL_PORT_DEFNS \ + EV96100_SERIAL_PORT_DEFNS \ + JAZZ_SERIAL_PORT_DEFNS \ + STD_SERIAL_PORT_DEFNS \ + EXTRA_SERIAL_PORT_DEFNS \ + HUB6_SERIAL_PORT_DFNS \ + MOMENCO_OCELOT_SERIAL_PORT_DEFNS \ + MOMENCO_OCELOT_G_SERIAL_PORT_DEFNS \ + MOMENCO_OCELOT_C_SERIAL_PORT_DEFNS \ + AU1X00_SERIAL_PORT_DEFNS \ + TXX927_SERIAL_PORT_DEFNS \ DDB5477_SERIAL_PORT_DEFNS diff --git a/include/asm-mips/shmbuf.h b/include/asm-mips/shmbuf.h index 37274d6d1e74..6d8e211eb738 100644 --- a/include/asm-mips/shmbuf.h +++ b/include/asm-mips/shmbuf.h @@ -1,7 +1,7 @@ #ifndef _ASM_SHMBUF_H #define _ASM_SHMBUF_H -/* +/* * The shmid64_ds structure for the MIPS architecture. * Note extra padding because this structure is passed back and forth * between kernel and user space. diff --git a/include/asm-mips/shmiq.h b/include/asm-mips/shmiq.h index 632d8aa5bf6a..80d3a5c98105 100644 --- a/include/asm-mips/shmiq.h +++ b/include/asm-mips/shmiq.h @@ -5,7 +5,7 @@ * * This also contains some streams and idev bits. * - * They may contain errors, please, refer to the source code of the Linux + * They may contain errors, please, refer to the source code of the Linux * kernel for a definitive answer on what we have implemented * * Miguel. @@ -91,7 +91,7 @@ struct shmqevent { * head is the user index into the events, user can modify this one. * tail is managed by the kernel. * flags is one of SHMIQ_OVERFLOW or SHMIQ_CORRUPTED - * if OVERFLOW is set it seems ioctl QUIOCSERVICED should be called + * if OVERFLOW is set it seems ioctl QUIOCSERVICED should be called * to notify the kernel. * events where the kernel sticks the events. */ @@ -186,14 +186,14 @@ typedef struct { unsigned hwMaxRes; int hwMinVal; int hwMaxVal; - + unsigned char possibleModes; #define IDEV_ABSOLUTE 0x0 #define IDEV_RELATIVE 0x1 #define IDEV_EITHER 0x2 - + unsigned char mode; /* One of: IDEV_ABSOLUTE, IDEV_RELATIVE */ - + unsigned short resolution; int minVal; int maxVal; diff --git a/include/asm-mips/shmparam.h b/include/asm-mips/shmparam.h index 966fcfe762c3..305a08b6b645 100644 --- a/include/asm-mips/shmparam.h +++ b/include/asm-mips/shmparam.h @@ -1,5 +1,4 @@ -/* $Id: shmparam.h,v 1.3 2000/01/28 19:46:32 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. diff --git a/include/asm-mips/sigcontext.h b/include/asm-mips/sigcontext.h index 748e3d5422b0..8ce99dc5e789 100644 --- a/include/asm-mips/sigcontext.h +++ b/include/asm-mips/sigcontext.h @@ -1,5 +1,4 @@ -/* $Id: sigcontext.h,v 1.5 1997/12/16 05:36: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 * for more details. @@ -18,10 +17,11 @@ struct sigcontext { unsigned int sc_status; unsigned long long sc_pc; unsigned long long sc_regs[32]; - unsigned long long sc_fpregs[32]; /* Unused */ - unsigned int sc_ownedfp; - unsigned int sc_fpc_csr; /* Unused */ + unsigned long long sc_fpregs[32]; + unsigned int sc_ownedfp; /* Unused */ + unsigned int sc_fpc_csr; unsigned int sc_fpc_eir; /* Unused */ + unsigned int sc_used_math; unsigned int sc_ssflags; /* Unused */ unsigned long long sc_mdhi; unsigned long long sc_mdlo; diff --git a/include/asm-mips/siginfo.h b/include/asm-mips/siginfo.h index 3f45c60f5948..a9f27578857d 100644 --- a/include/asm-mips/siginfo.h +++ b/include/asm-mips/siginfo.h @@ -1,5 +1,4 @@ -/* $Id: siginfo.h,v 1.5 1999/08/18 23:37:49 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. @@ -13,7 +12,13 @@ #define HAVE_ARCH_SIGINFO_T #define HAVE_ARCH_SIGEVENT_T + +/* + * We duplicate the generic versions - <asm-generic/siginfo.h> is just borked + * by design ... + */ #define HAVE_ARCH_COPY_SIGINFO +struct siginfo; #include <asm-generic/siginfo.h> @@ -66,8 +71,11 @@ typedef struct siginfo { /* POSIX.1b timers */ struct { - unsigned int _timer1; - unsigned int _timer2; + timer_t _tid; /* timer id */ + int _overrun; /* overrun count */ + char _pad[sizeof( __ARCH_SI_UID_T) - sizeof(int)]; + sigval_t _sigval; /* same as below */ + int _sys_private; /* not to be passed to user */ } _timer; /* POSIX.1b signals */ @@ -93,8 +101,8 @@ typedef struct siginfo { /* * sigevent definitions - * - * It seems likely that SIGEV_THREAD will have to be handled from + * + * It seems likely that SIGEV_THREAD will have to be handled from * userspace, libpthread transmuting it to SIGEV_SIGNAL, which the * thread manager then catches and does the appropriate nonsense. * However, everything is written out here so as to not get lost. @@ -109,21 +117,25 @@ typedef struct siginfo { /* XXX This one isn't yet IRIX / ABI compatible. */ typedef struct sigevent { - int sigev_notify; - sigval_t sigev_value; - int sigev_signo; + int sigev_notify; + sigval_t sigev_value; + int sigev_signo; union { - int _pad[SIGEV_PAD_SIZE]; + int _pad[SIGEV_PAD_SIZE]; + int _tid; struct { - void (*_function)(sigval_t); - void *_attribute; /* really pthread_attr_t */ + void (*_function)(sigval_t); + void *_attribute; /* really pthread_attr_t */ } _sigev_thread; } _sigev_un; } sigevent_t; #ifdef __KERNEL__ +/* + * Duplicated here because of <asm-generic/siginfo.h> braindamage ... + */ #include <linux/string.h> static inline void copy_siginfo(struct siginfo *to, struct siginfo *from) diff --git a/include/asm-mips/signal.h b/include/asm-mips/signal.h index 6c5a3dfef907..7547fd224d5f 100644 --- a/include/asm-mips/signal.h +++ b/include/asm-mips/signal.h @@ -1,5 +1,4 @@ -/* $Id: signal.h,v 1.6 1999/08/18 23:37:49 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. @@ -10,6 +9,7 @@ #ifndef _ASM_SIGNAL_H #define _ASM_SIGNAL_H +#include <linux/config.h> #include <linux/types.h> #define _NSIG 128 @@ -89,7 +89,7 @@ typedef unsigned long old_sigset_t; /* at least 32 bits */ #define SA_RESTORER 0x04000000 -/* +/* * sigaltstack controls */ #define SS_ONSTACK 1 @@ -131,12 +131,13 @@ struct sigaction { unsigned int sa_flags; __sighandler_t sa_handler; sigset_t sa_mask; - void (*sa_restorer)(void); - int sa_resv[1]; /* reserved */ }; struct k_sigaction { struct sigaction sa; +#ifdef CONFIG_BINFMT_IRIX + void (*sa_restorer)(void); +#endif }; /* IRIX compatible stack_t */ @@ -168,9 +169,10 @@ typedef struct sigaltstack { #define BRK_NORLD 10 /* No rld found - not used by Linux/MIPS */ #define _BRK_THREADBP 11 /* For threads, user bp (used by debuggers) */ #define BRK_MULOVF 1023 /* Multiply overflow */ +#define BRK_BUG 512 /* Used by BUG() */ #define ptrace_signal_deliver(regs, cookie) do { } while (0) -#endif /* defined (__KERNEL__) */ +#endif /* __KERNEL__ */ #endif /* _ASM_SIGNAL_H */ diff --git a/include/asm-mips/smp.h b/include/asm-mips/smp.h index 1bfcf41e09c2..c99411e8fbc5 100644 --- a/include/asm-mips/smp.h +++ b/include/asm-mips/smp.h @@ -1,37 +1,102 @@ -#ifndef __ASM_MIPS_SMP_H -#define __ASM_MIPS_SMP_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. + * + * Copyright (C) 2000 - 2001 by Kanoj Sarcar (kanoj@sgi.com) + * Copyright (C) 2000 - 2001 by Silicon Graphics, Inc. + * Copyright (C) 2000, 2001, 2002 Ralf Baechle + * Copyright (C) 2000, 2001 Broadcom Corporation + */ +#ifndef __ASM_SMP_H +#define __ASM_SMP_H #include <linux/config.h> #ifdef CONFIG_SMP -#include <asm/spinlock.h> +#include <linux/bitops.h> +#include <linux/threads.h> #include <asm/atomic.h> -#include <asm/current.h> +#define smp_processor_id() (current_thread_info()->cpu) -/* Mappings are straight across. If we want - to add support for disabling cpus and such, - we'll have to do what the mips64 port does here */ -#define cpu_logical_map(cpu) (cpu) -#define cpu_number_map(cpu) (cpu) +/* Map from cpu id to sequential logical cpu number. This will only + not be idempotent when cpus failed to come on-line. */ +extern int __cpu_number_map[NR_CPUS]; +#define cpu_number_map(cpu) __cpu_number_map[cpu] -#define smp_processor_id() (current->processor) - - -/* I've no idea what the real meaning of this is */ -#define PROC_CHANGE_PENALTY 20 +/* The reverse map from sequential logical cpu number to cpu id. */ +extern int __cpu_logical_map[NR_CPUS]; +#define cpu_logical_map(cpu) __cpu_logical_map[cpu] #define NO_PROC_ID (-1) -struct smp_fn_call_struct { - spinlock_t lock; - atomic_t finished; - void (*fn)(void *); - void *data; +struct call_data_struct { + void (*func)(void *); + void *info; + atomic_t started; + atomic_t finished; + int wait; }; -extern struct smp_fn_call_struct smp_fn_call; +extern struct call_data_struct *call_data; + +#define SMP_RESCHEDULE_YOURSELF 0x1 /* XXX braindead */ +#define SMP_CALL_FUNCTION 0x2 + +#if (NR_CPUS <= _MIPS_SZLONG) + +typedef unsigned long cpumask_t; + +#define CPUMASK_CLRALL(p) (p) = 0 +#define CPUMASK_SETB(p, bit) (p) |= 1UL << (bit) +#define CPUMASK_CLRB(p, bit) (p) &= ~(1UL << (bit)) +#define CPUMASK_TSTB(p, bit) ((p) & (1UL << (bit))) + +#elif (NR_CPUS <= 128) + +/* + * The foll should work till 128 cpus. + */ +#define CPUMASK_SIZE (NR_CPUS/_MIPS_SZLONG) +#define CPUMASK_INDEX(bit) ((bit) >> 6) +#define CPUMASK_SHFT(bit) ((bit) & 0x3f) + +typedef struct { + unsigned long _bits[CPUMASK_SIZE]; +} cpumask_t; + +#define CPUMASK_CLRALL(p) (p)._bits[0] = 0, (p)._bits[1] = 0 +#define CPUMASK_SETB(p, bit) (p)._bits[CPUMASK_INDEX(bit)] |= \ + (1UL << CPUMASK_SHFT(bit)) +#define CPUMASK_CLRB(p, bit) (p)._bits[CPUMASK_INDEX(bit)] &= \ + ~(1UL << CPUMASK_SHFT(bit)) +#define CPUMASK_TSTB(p, bit) ((p)._bits[CPUMASK_INDEX(bit)] & \ + (1UL << CPUMASK_SHFT(bit))) + +#else +#error cpumask macros only defined for 128p kernels +#endif + +extern cpumask_t phys_cpu_present_map; +extern cpumask_t cpu_online_map; + +#define cpu_possible(cpu) (phys_cpu_present_map & (1<<(cpu))) +#define cpu_online(cpu) (cpu_online_map & (1<<(cpu))) + +extern inline unsigned int num_online_cpus(void) +{ + return hweight32(cpu_online_map); +} + +extern volatile unsigned long cpu_callout_map; +/* We don't mark CPUs online until __cpu_up(), so we need another measure */ +static inline int num_booting_cpus(void) +{ + return hweight32(cpu_callout_map); +} #endif /* CONFIG_SMP */ -#endif /* __ASM_MIPS_SMP_H */ + +#endif /* __ASM_SMP_H */ diff --git a/include/asm-mips/smplock.h b/include/asm-mips/smplock.h new file mode 100644 index 000000000000..861274910044 --- /dev/null +++ b/include/asm-mips/smplock.h @@ -0,0 +1,67 @@ +/* + * 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. + * + * Default SMP lock implementation + */ +#include <linux/config.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> + +extern spinlock_t kernel_flag; + +#ifdef CONFIG_SMP +#define kernel_locked() spin_is_locked(&kernel_flag) +#else +#ifdef CONFIG_PREEMPT +#define kernel_locked() preempt_count() +#else +#define kernel_locked() 1 +#endif +#endif + +/* + * Release global kernel lock and global interrupt lock + */ +#define release_kernel_lock(task) \ +do { \ + if (unlikely(task->lock_depth >= 0)) \ + spin_unlock(&kernel_flag); \ +} while (0) + +/* + * Re-acquire the kernel lock + */ +#define reacquire_kernel_lock(task) \ +do { \ + if (unlikely(task->lock_depth >= 0)) \ + spin_lock(&kernel_flag); \ +} while (0) + + +/* + * Getting the big kernel lock. + * + * This cannot happen asynchronously, + * so we only need to worry about other + * CPU's. + */ +static __inline__ void lock_kernel(void) +{ +#ifdef CONFIG_PREEMPT + if (current->lock_depth == -1) + spin_lock(&kernel_flag); + ++current->lock_depth; +#else + + if (!++current->lock_depth) + spin_lock(&kernel_flag); +#endif +} + +static __inline__ void unlock_kernel(void) +{ + if (--current->lock_depth < 0) + spin_unlock(&kernel_flag); +} diff --git a/include/asm-mips/softirq.h b/include/asm-mips/softirq.h new file mode 100644 index 000000000000..d944be956a25 --- /dev/null +++ b/include/asm-mips/softirq.h @@ -0,0 +1,20 @@ +#ifndef __ASM_SOFTIRQ_H +#define __ASM_SOFTIRQ_H + +#include <linux/preempt.h> +#include <asm/hardirq.h> + +#define local_bh_disable() \ + do { preempt_count() += SOFTIRQ_OFFSET; barrier(); } while (0) +#define __local_bh_enable() \ + do { barrier(); preempt_count() -= SOFTIRQ_OFFSET; } while (0) + +#define local_bh_enable() \ +do { \ + __local_bh_enable(); \ + if (unlikely(!in_interrupt() && softirq_pending(smp_processor_id()))) \ + do_softirq(); \ + preempt_check_resched(); \ +} while (0) + +#endif /* __ASM_SOFTIRQ_H */ diff --git a/include/asm-mips/spinlock.h b/include/asm-mips/spinlock.h index fb02686f7896..4a46e61ba379 100644 --- a/include/asm-mips/spinlock.h +++ b/include/asm-mips/spinlock.h @@ -31,7 +31,7 @@ typedef struct { * We make no fairness assumptions. They have a cost. */ -static inline void spin_lock(spinlock_t *lock) +static inline void _raw_spin_lock(spinlock_t *lock) { unsigned int tmp; @@ -44,24 +44,41 @@ static inline void spin_lock(spinlock_t *lock) "beqz\t%1, 1b\n\t" " sync\n\t" ".set\treorder" - : "=o" (lock->lock), "=&r" (tmp) - : "o" (lock->lock) + : "=m" (lock->lock), "=&r" (tmp) + : "m" (lock->lock) : "memory"); } -static inline void spin_unlock(spinlock_t *lock) +static inline void _raw_spin_unlock(spinlock_t *lock) { __asm__ __volatile__( ".set\tnoreorder\t\t\t# spin_unlock\n\t" "sync\n\t" "sw\t$0, %0\n\t" - ".set\treorder" - : "=o" (lock->lock) - : "o" (lock->lock) + ".set\treorder" + : "=m" (lock->lock) + : "m" (lock->lock) : "memory"); } -#define spin_trylock(lock) (!test_and_set_bit(0,(lock))) +static inline unsigned int _raw_spin_trylock(spinlock_t *lock) +{ + unsigned int temp, res; + + __asm__ __volatile__( + ".set\tnoreorder\t\t\t# spin_trylock\n\t" + "1:\tll\t%0, %3\n\t" + "ori\t%2, %0, 1\n\t" + "sc\t%2, %1\n\t" + "beqz\t%2, 1b\n\t" + " andi\t%2, %0, 1\n\t" + ".set\treorder" + : "=&r" (temp), "=m" (lock->lock), "=&r" (res) + : "m" (lock->lock) + : "memory"); + + return res == 0; +} /* * Read-write spinlocks, allowing multiple readers but only one writer. @@ -78,7 +95,11 @@ typedef struct { #define RW_LOCK_UNLOCKED (rwlock_t) { 0 } -static inline void read_lock(rwlock_t *rw) +#define rwlock_init(x) do { *(x) = RW_LOCK_UNLOCKED; } while(0) + +#define rwlock_is_locked(x) ((x)->lock) + +static inline void _raw_read_lock(rwlock_t *rw) { unsigned int tmp; @@ -90,16 +111,16 @@ static inline void read_lock(rwlock_t *rw) "sc\t%1, %0\n\t" "beqz\t%1, 1b\n\t" " sync\n\t" - ".set\treorder" - : "=o" (rw->lock), "=&r" (tmp) - : "o" (rw->lock) + ".set\treorder" + : "=m" (rw->lock), "=&r" (tmp) + : "m" (rw->lock) : "memory"); } /* Note the use of sub, not subu which will make the kernel die with an overflow exception if we ever try to unlock an rwlock that is already unlocked or is being held by a writer. */ -static inline void read_unlock(rwlock_t *rw) +static inline void _raw_read_unlock(rwlock_t *rw) { unsigned int tmp; @@ -109,13 +130,14 @@ static inline void read_unlock(rwlock_t *rw) "sub\t%1, 1\n\t" "sc\t%1, %0\n\t" "beqz\t%1, 1b\n\t" - ".set\treorder" - : "=o" (rw->lock), "=&r" (tmp) - : "o" (rw->lock) + " sync\n\t" + ".set\treorder" + : "=m" (rw->lock), "=&r" (tmp) + : "m" (rw->lock) : "memory"); } -static inline void write_lock(rwlock_t *rw) +static inline void _raw_write_lock(rwlock_t *rw) { unsigned int tmp; @@ -127,21 +149,21 @@ static inline void write_lock(rwlock_t *rw) "sc\t%1, %0\n\t" "beqz\t%1, 1b\n\t" " sync\n\t" - ".set\treorder" - : "=o" (rw->lock), "=&r" (tmp) - : "o" (rw->lock) + ".set\treorder" + : "=m" (rw->lock), "=&r" (tmp) + : "m" (rw->lock) : "memory"); } -static inline void write_unlock(rwlock_t *rw) +static inline void _raw_write_unlock(rwlock_t *rw) { __asm__ __volatile__( ".set\tnoreorder\t\t\t# write_unlock\n\t" "sync\n\t" "sw\t$0, %0\n\t" - ".set\treorder" - : "=o" (rw->lock) - : "o" (rw->lock) + ".set\treorder" + : "=m" (rw->lock) + : "m" (rw->lock) : "memory"); } diff --git a/include/asm-mips/stackframe.h b/include/asm-mips/stackframe.h index d46cecef9afc..0d52478b38bb 100644 --- a/include/asm-mips/stackframe.h +++ b/include/asm-mips/stackframe.h @@ -9,282 +9,287 @@ #ifndef __ASM_STACKFRAME_H #define __ASM_STACKFRAME_H +#include <linux/config.h> #include <asm/addrspace.h> #include <asm/mipsregs.h> #include <asm/processor.h> #include <asm/asm.h> #include <asm/offset.h> -#include <linux/config.h> -#define SAVE_AT \ - .set push; \ - .set noat; \ - sw $1, PT_R1(sp); \ + .macro SAVE_AT + .set push + .set noat + sw $1, PT_R1(sp) .set pop + .endm -#define SAVE_TEMP \ - mfhi v1; \ - sw $8, PT_R8(sp); \ - sw $9, PT_R9(sp); \ - sw v1, PT_HI(sp); \ - mflo v1; \ - sw $10,PT_R10(sp); \ - sw $11, PT_R11(sp); \ - sw v1, PT_LO(sp); \ - sw $12, PT_R12(sp); \ - sw $13, PT_R13(sp); \ - sw $14, PT_R14(sp); \ - sw $15, PT_R15(sp); \ + .macro SAVE_TEMP + mfhi v1 + sw $8, PT_R8(sp) + sw $9, PT_R9(sp) + sw v1, PT_HI(sp) + mflo v1 + sw $10,PT_R10(sp) + sw $11, PT_R11(sp) + sw v1, PT_LO(sp) + sw $12, PT_R12(sp) + sw $13, PT_R13(sp) + sw $14, PT_R14(sp) + sw $15, PT_R15(sp) sw $24, PT_R24(sp) + .endm -#define SAVE_STATIC \ - sw $16, PT_R16(sp); \ - sw $17, PT_R17(sp); \ - sw $18, PT_R18(sp); \ - sw $19, PT_R19(sp); \ - sw $20, PT_R20(sp); \ - sw $21, PT_R21(sp); \ - sw $22, PT_R22(sp); \ - sw $23, PT_R23(sp); \ + .macro SAVE_STATIC + sw $16, PT_R16(sp) + sw $17, PT_R17(sp) + sw $18, PT_R18(sp) + sw $19, PT_R19(sp) + sw $20, PT_R20(sp) + sw $21, PT_R21(sp) + sw $22, PT_R22(sp) + sw $23, PT_R23(sp) sw $30, PT_R30(sp) - -#define __str2(x) #x -#define __str(x) __str2(x) - -#define save_static_function(symbol) \ -__asm__ ( \ - ".globl\t" #symbol "\n\t" \ - ".align\t2\n\t" \ - ".type\t" #symbol ", @function\n\t" \ - ".ent\t" #symbol ", 0\n" \ - #symbol":\n\t" \ - ".frame\t$29, 0, $31\n\t" \ - "sw\t$16,"__str(PT_R16)"($29)\t\t\t# save_static_function\n\t" \ - "sw\t$17,"__str(PT_R17)"($29)\n\t" \ - "sw\t$18,"__str(PT_R18)"($29)\n\t" \ - "sw\t$19,"__str(PT_R19)"($29)\n\t" \ - "sw\t$20,"__str(PT_R20)"($29)\n\t" \ - "sw\t$21,"__str(PT_R21)"($29)\n\t" \ - "sw\t$22,"__str(PT_R22)"($29)\n\t" \ - "sw\t$23,"__str(PT_R23)"($29)\n\t" \ - "sw\t$30,"__str(PT_R30)"($29)\n\t" \ - ".end\t" #symbol "\n\t" \ - ".size\t" #symbol",. - " #symbol) - -/* Used in declaration of save_static functions. */ -#define static_unused static __attribute__((unused)) - + .endm #ifdef CONFIG_SMP -# define GET_SAVED_SP \ - mfc0 k0, CP0_CONTEXT; \ - lui k1, %hi(kernelsp); \ - srl k0, k0, 23; \ - sll k0, k0, 2; \ - addu k1, k0; \ - lw k1, %lo(kernelsp)(k1); + .macro GET_SAVED_SP + mfc0 k0, CP0_CONTEXT + lui k1, %hi(kernelsp) + srl k0, k0, 23 + sll k0, k0, 2 + addu k1, k0 + lw k1, %lo(kernelsp)(k1) + .endm #else -# define GET_SAVED_SP \ - lui k1, %hi(kernelsp); \ - lw k1, %lo(kernelsp)(k1); + .macro GET_SAVED_SP + lui k1, %hi(kernelsp) + lw k1, %lo(kernelsp)(k1) + .endm #endif - -#define SAVE_SOME \ - .set push; \ - .set reorder; \ - mfc0 k0, CP0_STATUS; \ - sll k0, 3; /* extract cu0 bit */ \ - .set noreorder; \ - bltz k0, 8f; \ - move k1, sp; \ - .set reorder; \ - /* Called from user mode, new stack. */ \ - GET_SAVED_SP \ -8: \ - move k0, sp; \ - subu sp, k1, PT_SIZE; \ - sw k0, PT_R29(sp); \ - sw $3, PT_R3(sp); \ - sw $0, PT_R0(sp); \ - mfc0 v1, CP0_STATUS; \ - sw $2, PT_R2(sp); \ - sw v1, PT_STATUS(sp); \ - sw $4, PT_R4(sp); \ - mfc0 v1, CP0_CAUSE; \ - sw $5, PT_R5(sp); \ - sw v1, PT_CAUSE(sp); \ - sw $6, PT_R6(sp); \ - mfc0 v1, CP0_EPC; \ - sw $7, PT_R7(sp); \ - sw v1, PT_EPC(sp); \ - sw $25, PT_R25(sp); \ - sw $28, PT_R28(sp); \ - sw $31, PT_R31(sp); \ - ori $28, sp, 0x1fff; \ - xori $28, 0x1fff; \ + +#ifdef CONFIG_PREEMPT + .macro BUMP_LOCK_COUNT + lw t0, TI_PRE_COUNT($28) + addiu t0, t0, 1 + sw t0, TI_PRE_COUNT($28) + .endm +#else + .macro BUMP_LOCK_COUNT + .endm +#endif + + .macro SAVE_SOME + .set push + .set reorder + mfc0 k0, CP0_STATUS + sll k0, 3 /* extract cu0 bit */ + .set noreorder + bltz k0, 8f + move k1, sp + .set reorder + /* Called from user mode, new stack. */ + GET_SAVED_SP +8: + move k0, sp + subu sp, k1, PT_SIZE + sw k0, PT_R29(sp) + sw $3, PT_R3(sp) + sw $0, PT_R0(sp) + mfc0 v1, CP0_STATUS + sw $2, PT_R2(sp) + sw v1, PT_STATUS(sp) + sw $4, PT_R4(sp) + mfc0 v1, CP0_CAUSE + sw $5, PT_R5(sp) + sw v1, PT_CAUSE(sp) + sw $6, PT_R6(sp) + mfc0 v1, CP0_EPC + sw $7, PT_R7(sp) + sw v1, PT_EPC(sp) + sw $25, PT_R25(sp) + sw $28, PT_R28(sp) + sw $31, PT_R31(sp) + ori $28, sp, 0x1fff + xori $28, 0x1fff + BUMP_LOCK_COUNT .set pop + .endm -#define SAVE_ALL \ - SAVE_SOME; \ - SAVE_AT; \ - SAVE_TEMP; \ + .macro SAVE_ALL + SAVE_SOME + SAVE_AT + SAVE_TEMP SAVE_STATIC + .endm -#define RESTORE_AT \ - .set push; \ - .set noat; \ - lw $1, PT_R1(sp); \ - .set pop; + .macro RESTORE_AT + .set push + .set noat + lw $1, PT_R1(sp) + .set pop + .endm -#define RESTORE_TEMP \ - lw $24, PT_LO(sp); \ - lw $8, PT_R8(sp); \ - lw $9, PT_R9(sp); \ - mtlo $24; \ - lw $24, PT_HI(sp); \ - lw $10,PT_R10(sp); \ - lw $11, PT_R11(sp); \ - mthi $24; \ - lw $12, PT_R12(sp); \ - lw $13, PT_R13(sp); \ - lw $14, PT_R14(sp); \ - lw $15, PT_R15(sp); \ + .macro RESTORE_TEMP + lw $24, PT_LO(sp) + lw $8, PT_R8(sp) + lw $9, PT_R9(sp) + mtlo $24 + lw $24, PT_HI(sp) + lw $10,PT_R10(sp) + lw $11, PT_R11(sp) + mthi $24 + lw $12, PT_R12(sp) + lw $13, PT_R13(sp) + lw $14, PT_R14(sp) + lw $15, PT_R15(sp) lw $24, PT_R24(sp) + .endm -#define RESTORE_STATIC \ - lw $16, PT_R16(sp); \ - lw $17, PT_R17(sp); \ - lw $18, PT_R18(sp); \ - lw $19, PT_R19(sp); \ - lw $20, PT_R20(sp); \ - lw $21, PT_R21(sp); \ - lw $22, PT_R22(sp); \ - lw $23, PT_R23(sp); \ + .macro RESTORE_STATIC + lw $16, PT_R16(sp) + lw $17, PT_R17(sp) + lw $18, PT_R18(sp) + lw $19, PT_R19(sp) + lw $20, PT_R20(sp) + lw $21, PT_R21(sp) + lw $22, PT_R22(sp) + lw $23, PT_R23(sp) lw $30, PT_R30(sp) + .endm -#if defined(CONFIG_CPU_R3000) +#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX) -#define RESTORE_SOME \ - .set push; \ - .set reorder; \ - mfc0 t0, CP0_STATUS; \ - .set pop; \ - ori t0, 0x1f; \ - xori t0, 0x1f; \ - mtc0 t0, CP0_STATUS; \ - li v1, 0xff00; \ - and t0, v1; \ - lw v0, PT_STATUS(sp); \ - nor v1, $0, v1; \ - and v0, v1; \ - or v0, t0; \ - mtc0 v0, CP0_STATUS; \ - lw $31, PT_R31(sp); \ - lw $28, PT_R28(sp); \ - lw $25, PT_R25(sp); \ - lw $7, PT_R7(sp); \ - lw $6, PT_R6(sp); \ - lw $5, PT_R5(sp); \ - lw $4, PT_R4(sp); \ - lw $3, PT_R3(sp); \ + .macro RESTORE_SOME + .set push + .set reorder + mfc0 t0, CP0_STATUS + .set pop + ori t0, 0x1f + xori t0, 0x1f + mtc0 t0, CP0_STATUS + li v1, 0xff00 + and t0, v1 + lw v0, PT_STATUS(sp) + nor v1, $0, v1 + and v0, v1 + or v0, t0 + mtc0 v0, CP0_STATUS + lw $31, PT_R31(sp) + lw $28, PT_R28(sp) + lw $25, PT_R25(sp) + lw $7, PT_R7(sp) + lw $6, PT_R6(sp) + lw $5, PT_R5(sp) + lw $4, PT_R4(sp) + lw $3, PT_R3(sp) lw $2, PT_R2(sp) + .endm -#define RESTORE_SP_AND_RET \ - .set push; \ - .set noreorder; \ - lw k0, PT_EPC(sp); \ - lw sp, PT_R29(sp); \ - jr k0; \ - rfe; \ + .macro RESTORE_SP_AND_RET + .set push + .set noreorder + lw k0, PT_EPC(sp) + lw sp, PT_R29(sp) + jr k0 + rfe .set pop + .endm #else -#define RESTORE_SOME \ - .set push; \ - .set reorder; \ - mfc0 t0, CP0_STATUS; \ - .set pop; \ - ori t0, 0x1f; \ - xori t0, 0x1f; \ - mtc0 t0, CP0_STATUS; \ - li v1, 0xff00; \ - and t0, v1; \ - lw v0, PT_STATUS(sp); \ - nor v1, $0, v1; \ - and v0, v1; \ - or v0, t0; \ - mtc0 v0, CP0_STATUS; \ - lw v1, PT_EPC(sp); \ - mtc0 v1, CP0_EPC; \ - lw $31, PT_R31(sp); \ - lw $28, PT_R28(sp); \ - lw $25, PT_R25(sp); \ - lw $7, PT_R7(sp); \ - lw $6, PT_R6(sp); \ - lw $5, PT_R5(sp); \ - lw $4, PT_R4(sp); \ - lw $3, PT_R3(sp); \ + .macro RESTORE_SOME + .set push + .set reorder + mfc0 t0, CP0_STATUS + .set pop + ori t0, 0x1f + xori t0, 0x1f + mtc0 t0, CP0_STATUS + li v1, 0xff00 + and t0, v1 + lw v0, PT_STATUS(sp) + nor v1, $0, v1 + and v0, v1 + or v0, t0 + mtc0 v0, CP0_STATUS + lw v1, PT_EPC(sp) + mtc0 v1, CP0_EPC + lw $31, PT_R31(sp) + lw $28, PT_R28(sp) + lw $25, PT_R25(sp) + lw $7, PT_R7(sp) + lw $6, PT_R6(sp) + lw $5, PT_R5(sp) + lw $4, PT_R4(sp) + lw $3, PT_R3(sp) lw $2, PT_R2(sp) + .endm -#define RESTORE_SP_AND_RET \ - lw sp, PT_R29(sp); \ - .set mips3; \ - eret; \ + .macro RESTORE_SP_AND_RET + lw sp, PT_R29(sp) + .set mips3 + eret .set mips0 + .endm #endif -#define RESTORE_SP \ - lw sp, PT_R29(sp); \ + .macro RESTORE_SP + lw sp, PT_R29(sp) + .endm -#define RESTORE_ALL \ - RESTORE_SOME; \ - RESTORE_AT; \ - RESTORE_TEMP; \ - RESTORE_STATIC; \ + .macro RESTORE_ALL + RESTORE_SOME + RESTORE_AT + RESTORE_TEMP + RESTORE_STATIC RESTORE_SP + .endm -#define RESTORE_ALL_AND_RET \ - RESTORE_SOME; \ - RESTORE_AT; \ - RESTORE_TEMP; \ - RESTORE_STATIC; \ + .macro RESTORE_ALL_AND_RET + RESTORE_SOME + RESTORE_AT + RESTORE_TEMP + RESTORE_STATIC RESTORE_SP_AND_RET + .endm /* * Move to kernel mode and disable interrupts. * Set cp0 enable bit as sign that we're running on the kernel stack */ -#define CLI \ - mfc0 t0,CP0_STATUS; \ - li t1,ST0_CU0|0x1f; \ - or t0,t1; \ - xori t0,0x1f; \ + .macro CLI + mfc0 t0,CP0_STATUS + li t1,ST0_CU0|0x1f + or t0,t1 + xori t0,0x1f mtc0 t0,CP0_STATUS + .endm /* * Move to kernel mode and enable interrupts. * Set cp0 enable bit as sign that we're running on the kernel stack */ -#define STI \ - mfc0 t0,CP0_STATUS; \ - li t1,ST0_CU0|0x1f; \ - or t0,t1; \ - xori t0,0x1e; \ + .macro STI + mfc0 t0,CP0_STATUS + li t1,ST0_CU0|0x1f + or t0,t1 + xori t0,0x1e mtc0 t0,CP0_STATUS + .endm /* * Just move to kernel mode and leave interrupts as they are. * Set cp0 enable bit as sign that we're running on the kernel stack */ -#define KMODE \ - mfc0 t0,CP0_STATUS; \ - li t1,ST0_CU0|0x1e; \ - or t0,t1; \ - xori t0,0x1e; \ + .macro KMODE + mfc0 t0,CP0_STATUS + li t1,ST0_CU0|0x1e + or t0,t1 + xori t0,0x1e mtc0 t0,CP0_STATUS + .endm #endif /* __ASM_STACKFRAME_H */ diff --git a/include/asm-mips/stat.h b/include/asm-mips/stat.h index c1b837973545..693b886fcd60 100644 --- a/include/asm-mips/stat.h +++ b/include/asm-mips/stat.h @@ -3,23 +3,6 @@ #include <linux/types.h> -struct __old_kernel_stat { - unsigned int st_dev; - unsigned int st_ino; - unsigned int st_mode; - unsigned int st_nlink; - unsigned int st_uid; - unsigned int st_gid; - unsigned int st_rdev; - long st_size; - unsigned int st_atime, st_res1; - unsigned int st_mtime, st_res2; - unsigned int st_ctime, st_res3; - unsigned int st_blksize; - int st_blocks; - unsigned int st_unused0[2]; -}; - struct stat { dev_t st_dev; long st_pad1[3]; /* Reserved for network id */ @@ -75,13 +58,13 @@ struct stat64 { * but we don't have it under Linux. */ time_t st_atime; - unsigned long reserved0; /* Reserved for st_atime expansion */ + unsigned long st_atime_nsec; /* Reserved for st_atime expansion */ time_t st_mtime; - unsigned long reserved1; /* Reserved for st_mtime expansion */ + unsigned long st_mtime_nsec; /* Reserved for st_mtime expansion */ time_t st_ctime; - unsigned long reserved2; /* Reserved for st_ctime expansion */ + unsigned long st_ctime_nsec; /* Reserved for st_ctime expansion */ unsigned long st_blksize; unsigned long st_pad2; diff --git a/include/asm-mips/statfs.h b/include/asm-mips/statfs.h index 500f6f1b667b..fb56e50c2cfd 100644 --- a/include/asm-mips/statfs.h +++ b/include/asm-mips/statfs.h @@ -1,14 +1,12 @@ /* - * Definitions for the statfs(2) call. - * * 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) 1995 by Ralf Baechle + * Copyright (C) 1995, 1999 by Ralf Baechle */ -#ifndef __ASM_MIPS_STATFS_H -#define __ASM_MIPS_STATFS_H +#ifndef _ASM_STATFS_H +#define _ASM_STATFS_H #include <linux/posix_types.h> @@ -37,4 +35,21 @@ struct statfs { long f_spare[6]; }; -#endif /* __ASM_MIPS_STATFS_H */ +/* + * Unlike the 32-bit version the 64-bit version has none of the ABI baggage. + */ +struct statfs64 { + __u32 f_type; + __u32 f_bsize; + __u64 f_blocks; + __u64 f_bfree; + __u64 f_bavail; + __u64 f_files; + __u64 f_ffree; + __kernel_fsid_t f_fsid; + __u32 f_namelen; + __u32 f_frsize; + __u32 f_spare[5]; +}; + +#endif /* _ASM_STATFS_H */ diff --git a/include/asm-mips/string.h b/include/asm-mips/string.h index f3f28a26ad82..f3074a3b7d05 100644 --- a/include/asm-mips/string.h +++ b/include/asm-mips/string.h @@ -28,7 +28,7 @@ extern __inline__ char *strcpy(char *__dest, __const__ char *__src) ".set\treorder" : "=r" (__dest), "=r" (__src) : "0" (__dest), "1" (__src) - : "$1","memory"); + : "memory"); return __xdest; } @@ -56,9 +56,9 @@ extern __inline__ char *strncpy(char *__dest, __const__ char *__src, size_t __n) ".set\treorder" : "=r" (__dest), "=r" (__src), "=r" (__n) : "0" (__dest), "1" (__src), "2" (__n) - : "$1","memory"); + : "memory"); - return __dest; + return __xdest; } #define __HAVE_ARCH_STRCMP @@ -84,8 +84,7 @@ extern __inline__ int strcmp(__const__ char *__cs, __const__ char *__ct) "3:\t.set\tat\n\t" ".set\treorder" : "=r" (__cs), "=r" (__ct), "=r" (__res) - : "0" (__cs), "1" (__ct) - : "$1"); + : "0" (__cs), "1" (__ct)); return __res; } @@ -110,14 +109,13 @@ strncmp(__const__ char *__cs, __const__ char *__ct, size_t __count) "2:\n\t" #if defined(CONFIG_CPU_R3000) "nop\n\t" -#endif +#endif "move\t%3,$1\n" "3:\tsubu\t%3,$1\n\t" ".set\tat\n\t" ".set\treorder" : "=r" (__cs), "=r" (__ct), "=r" (__count), "=r" (__res) - : "0" (__cs), "1" (__ct), "2" (__count) - : "$1"); + : "0" (__cs), "1" (__ct), "2" (__count)); return __res; } @@ -138,18 +136,18 @@ extern void *memmove(void *__dest, __const__ void *__src, size_t __n); extern __inline__ void *memscan(void *__addr, int __c, size_t __size) { char *__end = (char *)__addr + __size; + unsigned char __uc = (unsigned char) __c; __asm__(".set\tpush\n\t" ".set\tnoat\n\t" ".set\treorder\n\t" "1:\tbeq\t%0,%1,2f\n\t" "addiu\t%0,1\n\t" - "lb\t$1,-1(%0)\n\t" + "lbu\t$1,-1(%0)\n\t" "bne\t$1,%z4,1b\n" "2:\t.set\tpop" : "=r" (__addr), "=r" (__end) - : "0" (__addr), "1" (__end), "Jr" (__c) - : "$1"); + : "0" (__addr), "1" (__end), "Jr" (__uc)); return __addr; } diff --git a/include/asm-mips/suspend.h b/include/asm-mips/suspend.h new file mode 100644 index 000000000000..2562f8f9be0e --- /dev/null +++ b/include/asm-mips/suspend.h @@ -0,0 +1,6 @@ +#ifndef __ASM_SUSPEND_H +#define __ASM_SUSPEND_H + +/* Somewhen... Maybe :-) */ + +#endif /* __ASM_SUSPEND_H */ diff --git a/include/asm-mips/system.h b/include/asm-mips/system.h index 49dbea6f8c1f..94045c6d30a3 100644 --- a/include/asm-mips/system.h +++ b/include/asm-mips/system.h @@ -8,7 +8,7 @@ * Copyright (C) 1994 - 1999 by Ralf Baechle * * Changed set_except_vector declaration to allow return of previous - * vector address value - necessary for "borrowing" vectors. + * vector address value - necessary for "borrowing" vectors. * * Kevin D. Kissell, kevink@mips.org and Carsten Langgaard, carstenl@mips.com * Copyright (C) 2000 MIPS Technologies, Inc. @@ -18,153 +18,240 @@ #include <linux/config.h> #include <asm/sgidefs.h> -#include <asm/ptrace.h> + #include <linux/kernel.h> -extern __inline__ void -local_irq_enable(void) +#include <asm/addrspace.h> +#include <asm/ptrace.h> + +__asm__ ( + ".macro\tlocal_irq_enable\n\t" + ".set\tpush\n\t" + ".set\treorder\n\t" + ".set\tnoat\n\t" + "mfc0\t$1,$12\n\t" + "ori\t$1,0x1f\n\t" + "xori\t$1,0x1e\n\t" + "mtc0\t$1,$12\n\t" + ".set\tpop\n\t" + ".endm"); + +extern inline void local_irq_enable(void) { __asm__ __volatile__( - ".set\tpush\n\t" - ".set\treorder\n\t" - ".set\tnoat\n\t" - "mfc0\t$1,$12\n\t" - "ori\t$1,0x1f\n\t" - "xori\t$1,0x1e\n\t" - "mtc0\t$1,$12\n\t" - ".set\tpop\n\t" + "local_irq_enable" : /* no outputs */ : /* no inputs */ - : "$1", "memory"); + : "memory"); } /* - * For cli() we have to insert nops to make shure that the new value + * For cli() we have to insert nops to make sure that the new value * has actually arrived in the status register before the end of this * macro. * R4000/R4400 need three nops, the R4600 two nops and the R10000 needs * no nops at all. */ -extern __inline__ void -local_irq_disable(void) +__asm__ ( + ".macro\tlocal_irq_disable\n\t" + ".set\tpush\n\t" + ".set\tnoat\n\t" + "mfc0\t$1,$12\n\t" + "ori\t$1,1\n\t" + "xori\t$1,1\n\t" + ".set\tnoreorder\n\t" + "mtc0\t$1,$12\n\t" + "sll\t$0, $0, 1\t\t\t# nop\n\t" + "sll\t$0, $0, 1\t\t\t# nop\n\t" + "sll\t$0, $0, 1\t\t\t# nop\n\t" + ".set\tpop\n\t" + ".endm"); + +extern inline void local_irq_disable(void) { __asm__ __volatile__( - ".set\tpush\n\t" - ".set\treorder\n\t" - ".set\tnoat\n\t" - "mfc0\t$1,$12\n\t" - "ori\t$1,1\n\t" - "xori\t$1,1\n\t" - ".set\tnoreorder\n\t" - "mtc0\t$1,$12\n\t" - "nop\n\t" - "nop\n\t" - "nop\n\t" - ".set\tpop\n\t" + "local_irq_disable" : /* no outputs */ : /* no inputs */ - : "$1", "memory"); + : "memory"); } +__asm__ ( + ".macro\tlocal_save_flags flags\n\t" + ".set\tpush\n\t" + ".set\treorder\n\t" + "mfc0\t\\flags, $12\n\t" + ".set\tpop\n\t" + ".endm"); + #define local_save_flags(x) \ __asm__ __volatile__( \ - ".set\tpush\n\t" \ - ".set\treorder\n\t" \ - "mfc0\t%0,$12\n\t" \ - ".set\tpop\n\t" \ + "local_save_flags %0" \ : "=r" (x)) +__asm__ ( + ".macro\tlocal_irq_save result\n\t" + ".set\tpush\n\t" + ".set\treorder\n\t" + ".set\tnoat\n\t" + "mfc0\t\\result, $12\n\t" + "ori\t$1, \\result, 1\n\t" + "xori\t$1, 1\n\t" + ".set\tnoreorder\n\t" + "mtc0\t$1, $12\n\t" + "sll\t$0, $0, 1\t\t\t# nop\n\t" + "sll\t$0, $0, 1\t\t\t# nop\n\t" + "sll\t$0, $0, 1\t\t\t# nop\n\t" + ".set\tpop\n\t" + ".endm"); + #define local_irq_save(x) \ __asm__ __volatile__( \ - ".set\tpush\n\t" \ - ".set\treorder\n\t" \ - ".set\tnoat\n\t" \ - "mfc0\t%0,$12\n\t" \ - "ori\t$1,%0,1\n\t" \ - "xori\t$1,1\n\t" \ - ".set\tnoreorder\n\t" \ - "mtc0\t$1,$12\n\t" \ - "nop\n\t" \ - "nop\n\t" \ - "nop\n\t" \ - ".set\tpop\n\t" \ + "local_irq_save\t%0" \ : "=r" (x) \ : /* no inputs */ \ - : "$1", "memory") + : "memory") + +__asm__(".macro\tlocal_irq_restore flags\n\t" + ".set\tnoreorder\n\t" + ".set\tnoat\n\t" + "mfc0\t$1, $12\n\t" + "andi\t\\flags, 1\n\t" + "ori\t$1, 1\n\t" + "xori\t$1, 1\n\t" + "or\t\\flags, $1\n\t" + "mtc0\t\\flags, $12\n\t" + "sll\t$0, $0, 1\t\t\t# nop\n\t" + "sll\t$0, $0, 1\t\t\t# nop\n\t" + "sll\t$0, $0, 1\t\t\t# nop\n\t" + ".set\tat\n\t" + ".set\treorder\n\t" + ".endm"); #define local_irq_restore(flags) \ do { \ unsigned long __tmp1; \ \ __asm__ __volatile__( \ - ".set\tnoreorder\t\t\t# local_irq_restore\n\t" \ - ".set\tnoat\n\t" \ - "mfc0\t$1, $12\n\t" \ - "andi\t%0, 1\n\t" \ - "ori\t$1, 1\n\t" \ - "xori\t$1, 1\n\t" \ - "or\t%0, $1\n\t" \ - "mtc0\t%0, $12\n\t" \ - "nop\n\t" \ - "nop\n\t" \ - "nop\n\t" \ - ".set\tat\n\t" \ - ".set\treorder" \ + "local_irq_restore\t%0" \ : "=r" (__tmp1) \ : "0" (flags) \ - : "$1", "memory"); \ + : "memory"); \ } while(0) -#ifdef CONFIG_SMP +#define irqs_disabled() \ +({ \ + unsigned long flags; \ + local_save_flags(flags); \ + !(flags & 1); \ +}) -extern void __global_sti(void); -extern void __global_cli(void); -extern unsigned long __global_save_flags(void); -extern void __global_restore_flags(unsigned long); -# define sti() __global_sti() -# define cli() __global_cli() -# define save_flags(x) do { x = __global_save_flags(); } while (0) -# define restore_flags(x) __global_restore_flags(x) -# define save_and_cli(x) do { save_flags(x); cli(); } while(0) +/* + * read_barrier_depends - Flush all pending reads that subsequents reads + * depend on. + * + * No data-dependent reads from memory-like regions are ever reordered + * over this barrier. All reads preceding this primitive are guaranteed + * to access memory (but not necessarily other CPUs' caches) before any + * reads following this primitive that depend on the data return by + * any of the preceding reads. This primitive is much lighter weight than + * rmb() on most CPUs, and is never heavier weight than is + * rmb(). + * + * These ordering constraints are respected by both the local CPU + * and the compiler. + * + * Ordering is not guaranteed by anything other than these primitives, + * not even by data dependencies. See the documentation for + * memory_barrier() for examples and URLs to more information. + * + * For example, the following code would force ordering (the initial + * value of "a" is zero, "b" is one, and "p" is "&a"): + * + * <programlisting> + * CPU 0 CPU 1 + * + * b = 2; + * memory_barrier(); + * p = &b; q = p; + * read_barrier_depends(); + * d = *q; + * </programlisting> + * + * because the read of "*q" depends on the read of "p" and these + * two reads are separated by a read_barrier_depends(). However, + * the following code, with the same initial values for "a" and "b": + * + * <programlisting> + * CPU 0 CPU 1 + * + * a = 2; + * memory_barrier(); + * b = 3; y = b; + * read_barrier_depends(); + * x = a; + * </programlisting> + * + * does not enforce ordering, since there is no data dependency between + * the read of "a" and the read of "b". Therefore, on some CPUs, such + * as Alpha, "y" could be set to 3 and "x" to 0. Use rmb() + * in cases like thiswhere there are no data dependencies. + */ -#else /* Single processor */ +#define read_barrier_depends() do { } while(0) -# define sti() local_irq_enable() -# define cli() local_irq_disable() -# define save_flags(x) local_save_flags(x) -# define save_and_cli(x) local_irq_save(x) -# define restore_flags(x) local_irq_restore(x) +#ifdef CONFIG_CPU_HAS_SYNC +#define __sync() \ + __asm__ __volatile__( \ + ".set push\n\t" \ + ".set noreorder\n\t" \ + ".set mips2\n\t" \ + "sync\n\t" \ + ".set pop" \ + : /* no output */ \ + : /* no input */ \ + : "memory") +#else +#define __sync() do { } while(0) +#endif -#endif /* SMP */ +#define __fast_iob() \ + __asm__ __volatile__( \ + ".set push\n\t" \ + ".set noreorder\n\t" \ + "lw $0,%0\n\t" \ + "nop\n\t" \ + ".set pop" \ + : /* no output */ \ + : "m" (*(int *)KSEG1) \ + : "memory") + +#define fast_wmb() __sync() +#define fast_rmb() __sync() +#define fast_mb() __sync() +#define fast_iob() \ + do { \ + __sync(); \ + __fast_iob(); \ + } while (0) -/* - * These are probably defined overly paranoid ... - */ #ifdef CONFIG_CPU_HAS_WB #include <asm/wbflush.h> -#define rmb() do { } while(0) -#define wmb() wbflush() -#define mb() wbflush() -#define read_barrier_depends() do { } while(0) -#else /* CONFIG_CPU_HAS_WB */ - -#define mb() \ -__asm__ __volatile__( \ - "# prevent instructions being moved around\n\t" \ - ".set\tnoreorder\n\t" \ - "# 8 nops to fool the R4400 pipeline\n\t" \ - "nop;nop;nop;nop;nop;nop;nop;nop\n\t" \ - ".set\treorder" \ - : /* no output */ \ - : /* no input */ \ - : "memory") -#define rmb() mb() -#define wmb() mb() -#define read_barrier_depends() do { } while(0) +#define wmb() fast_wmb() +#define rmb() fast_rmb() +#define mb() wbflush(); +#define iob() wbflush(); + +#else /* !CONFIG_CPU_HAS_WB */ -#endif /* CONFIG_CPU_HAS_WB */ +#define wmb() fast_wmb() +#define rmb() fast_rmb() +#define mb() fast_mb() +#define iob() fast_iob() + +#endif /* !CONFIG_CPU_HAS_WB */ #ifdef CONFIG_SMP #define smp_mb() mb() @@ -184,18 +271,17 @@ do { var = value; mb(); } while (0) #define set_wmb(var, value) \ do { var = value; wmb(); } while (0) -#if !defined (_LANGUAGE_ASSEMBLY) /* * switch_to(n) should switch tasks to task nr n, first * checking that n isn't the current task, in which case it does nothing. */ -extern asmlinkage void *resume(void *last, void *next); -#endif /* !defined (_LANGUAGE_ASSEMBLY) */ +extern asmlinkage void *resume(void *last, void *next, void *next_ti); + +struct task_struct; -#define prepare_to_switch() do { } while(0) #define switch_to(prev,next,last) \ do { \ - (last) = resume(prev, next); \ + (last) = resume(prev, next, next->thread_info); \ } while(0) /* @@ -208,28 +294,28 @@ extern __inline__ unsigned long xchg_u32(volatile int * m, unsigned long val) unsigned long dummy; __asm__ __volatile__( - ".set\tnoreorder\t\t\t# xchg_u32\n\t" - ".set\tnoat\n\t" + ".set\tpush\t\t\t\t# xchg_u32\n\t" + ".set\tnoreorder\n\t" + ".set\tnomacro\n\t" "ll\t%0, %3\n" - "1:\tmove\t$1, %2\n\t" - "sc\t$1, %1\n\t" - "beqzl\t$1, 1b\n\t" + "1:\tmove\t%2, %z4\n\t" + "sc\t%2, %1\n\t" + "beqzl\t%2, 1b\n\t" " ll\t%0, %3\n\t" - ".set\tat\n\t" - ".set\treorder" - : "=r" (val), "=o" (*m), "=r" (dummy) - : "o" (*m), "2" (val) + "sync\n\t" + ".set\tpop" + : "=&r" (val), "=m" (*m), "=&r" (dummy) + : "R" (*m), "Jr" (val) : "memory"); return val; #else unsigned long flags, retval; - save_flags(flags); - cli(); + local_irq_save(flags); retval = *m; *m = val; - restore_flags(flags); + local_irq_restore(flags); /* implies memory barrier */ return retval; #endif /* Processor-dependent optimization */ } @@ -248,15 +334,24 @@ __xchg(unsigned long x, volatile void * ptr, int size) } extern void *set_except_vector(int n, void *addr); +extern void per_cpu_trap_init(void); -extern void __die(const char *, struct pt_regs *, const char *where, - unsigned long line) __attribute__((noreturn)); -extern void __die_if_kernel(const char *, struct pt_regs *, const char *where, - unsigned long line); +extern void __die(const char *, struct pt_regs *, const char *file, + const char *func, unsigned long line) __attribute__((noreturn)); +extern void __die_if_kernel(const char *, struct pt_regs *, const char *file, + const char *func, unsigned long line); #define die(msg, regs) \ - __die(msg, regs, __FILE__ ":"__FUNCTION__, __LINE__) + __die(msg, regs, __FILE__ ":", __FUNCTION__, __LINE__) #define die_if_kernel(msg, regs) \ - __die_if_kernel(msg, regs, __FILE__ ":"__FUNCTION__, __LINE__) + __die_if_kernel(msg, regs, __FILE__ ":", __FUNCTION__, __LINE__) + +extern int serial_console; +extern int stop_a_enabled; + +static __inline__ int con_is_present(void) +{ + return serial_console ? 0 : 1; +} #endif /* _ASM_SYSTEM_H */ diff --git a/include/asm-mips/thread_info.h b/include/asm-mips/thread_info.h new file mode 100644 index 000000000000..99ec130a7808 --- /dev/null +++ b/include/asm-mips/thread_info.h @@ -0,0 +1,103 @@ +/* thread_info.h: i386 low-level thread information + * + * Copyright (C) 2002 David Howells (dhowells@redhat.com) + * - Incorporating suggestions made by Linus Torvalds and Dave Miller + */ + +#ifndef _ASM_THREAD_INFO_H +#define _ASM_THREAD_INFO_H + +#ifdef __KERNEL__ + +#ifndef __ASSEMBLY__ + +#include <asm/processor.h> + +/* + * low level task data that entry.S needs immediate access to + * - this struct should fit entirely inside of one cache line + * - this struct shares the supervisor stack pages + * - if the contents of this structure are changed, the assembly constants + * must also be changed + */ +struct thread_info { + struct task_struct *task; /* main task structure */ + struct exec_domain *exec_domain; /* execution domain */ + unsigned long flags; /* low level flags */ + __u32 cpu; /* current CPU */ + __s32 preempt_count; /* 0 => preemptable, <0 => BUG */ + + mm_segment_t addr_limit; /* thread address space: + 0-0xBFFFFFFF for user-thead + 0-0xFFFFFFFF for kernel-thread + */ + struct restart_block restart_block; +}; + +#define PREEMPT_ACTIVE 0x4000000 + +/* + * macros/functions for gaining access to the thread information structure + * + * preempt_count needs to be 1 initially, until the scheduler is functional. + */ +#define INIT_THREAD_INFO(tsk) \ +{ \ + .task = &tsk, \ + .exec_domain = &default_exec_domain, \ + .flags = 0, \ + .cpu = 0, \ + .preempt_count = 1, \ + .addr_limit = KERNEL_DS, \ + .restart_block = { \ + .fn = do_no_restart_syscall, \ + }, \ +} + +#define init_thread_info (init_thread_union.thread_info) +#define init_stack (init_thread_union.stack) + +/* How to get the thread information struct from C. */ +register struct thread_info *__current_thread_info __asm__("$28"); +#define current_thread_info() __current_thread_info + +/* thread information allocation */ +#define THREAD_SIZE_ORDER (1) +#define THREAD_SIZE (PAGE_SIZE << THREAD_SIZE_ORDER) +#define alloc_thread_info(tsk) ((struct thread_info *) \ + __get_free_pages(GFP_KERNEL,THREAD_SIZE_ORDER)) +#define free_thread_info(ti) free_pages((unsigned long) (ti), THREAD_SIZE_ORDER) +#define get_thread_info(ti) get_task_struct((ti)->task) +#define put_thread_info(ti) put_task_struct((ti)->task) + +#endif /* !__ASSEMBLY__ */ + +/* + * thread information flags + * - these are process state flags that various assembly files may need to + * access + * - pending work-to-be-done flags are in LSW + * - other flags in MSW + */ +#define TIF_NOTIFY_RESUME 1 /* resumption notification requested */ +#define TIF_SIGPENDING 2 /* signal pending */ +#define TIF_NEED_RESCHED 3 /* rescheduling necessary */ +#define TIF_USEDFPU 16 /* FPU was used by this task this quantum (SMP) */ +#define TIF_POLLING_NRFLAG 17 /* true if poll_idle() is polling TIF_NEED_RESCHED */ +#define TIF_SYSCALL_TRACE 31 /* syscall trace active */ + +#define _TIF_SYSCALL_TRACE (1<<TIF_SYSCALL_TRACE) +#define _TIF_NOTIFY_RESUME (1<<TIF_NOTIFY_RESUME) +#define _TIF_SIGPENDING (1<<TIF_SIGPENDING) +#define _TIF_NEED_RESCHED (1<<TIF_NEED_RESCHED) +#define _TIF_USEDFPU (1<<TIF_USEDFPU) +#define _TIF_POLLING_NRFLAG (1<<TIF_POLLING_NRFLAG) + +#define _TIF_WORK_MASK 0x0000fffe /* work to do on + interrupt/exception return */ +#define _TIF_ALLWORK_MASK 0x8000fffe /* work to do on any return to + u-space */ + +#endif /* __KERNEL__ */ + +#endif /* _ASM_THREAD_INFO_H */ diff --git a/include/asm-mips/time.h b/include/asm-mips/time.h index bf0cc3f84785..bc969bf65822 100644 --- a/include/asm-mips/time.h +++ b/include/asm-mips/time.h @@ -10,20 +10,17 @@ * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * + * Please refer to Documentation/mips/time.README. */ - -/* - * Please refer to Documentation/MIPS/time.README. - */ - #ifndef _ASM_TIME_H #define _ASM_TIME_H -#include <linux/ptrace.h> /* for struct pt_regs */ -#include <linux/linkage.h> /* for asmlinkage */ -#include <linux/rtc.h> /* for struct rtc_time */ +#include <linux/interrupt.h> +#include <linux/linkage.h> +#include <linux/ptrace.h> +#include <linux/rtc.h> -/* +/* * RTC ops. By default, they point a no-RTC functions. * rtc_get_time - mktime(year, mon, day, hour, min, sec) in seconds. * rtc_set_time - reverse the above translation and set time to RTC. @@ -39,7 +36,7 @@ extern int (*rtc_set_time)(unsigned long); extern void to_tm(unsigned long tim, struct rtc_time * tm); /* - * do_gettimeoffset(). By default, this func pointer points to + * do_gettimeoffset(). By default, this func pointer points to * do_null_gettimeoffset(), which leads to the same resolution as HZ. * Higher resolution versions are vailable, which gives ~1us resolution. */ @@ -53,7 +50,7 @@ extern unsigned long calibrate_div64_gettimeoffset(void); /* * high-level timer interrupt routines. */ -extern void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs); +extern irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs); /* * the corresponding low-level timer interrupt routine. diff --git a/include/asm-mips/timex.h b/include/asm-mips/timex.h index 599336a9a0c7..18254aa29713 100644 --- a/include/asm-mips/timex.h +++ b/include/asm-mips/timex.h @@ -1,5 +1,4 @@ -/* $Id: timex.h,v 1.1 1998/08/25 09:22:03 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. @@ -8,21 +7,22 @@ * * FIXME: For some of the supported machines this is dead wrong. */ -#ifndef __ASM_MIPS_TIMEX_H -#define __ASM_MIPS_TIMEX_H +#ifndef _ASM_TIMEX_H +#define _ASM_TIMEX_H + +#include <asm/mipsregs.h> -#define CLOCK_TICK_RATE 1193180 /* Underlying HZ */ +#define CLOCK_TICK_RATE 1193182 /* Underlying HZ */ #define CLOCK_TICK_FACTOR 20 /* Factor of both 1000000 and CLOCK_TICK_RATE */ #define FINETUNE ((((((long)LATCH * HZ - CLOCK_TICK_RATE) << SHIFT_HZ) * \ (1000000/CLOCK_TICK_FACTOR) / (CLOCK_TICK_RATE/CLOCK_TICK_FACTOR)) \ << (SHIFT_SCALE-SHIFT_HZ)) / HZ) -#ifdef __KERNEL__ /* * Standard way to access the cycle counter. * Currently only used on SMP for scheduling. * - * Only the low 32 bits are available as a continuously counting entity. + * Only the low 32 bits are available as a continuously counting entity. * But this only means we'll force a reschedule every 8 seconds or so, * which isn't an evil thing. * @@ -34,8 +34,7 @@ extern cycles_t cacheflush_time; static inline cycles_t get_cycles (void) { - return read_32bit_cp0_register(CP0_COUNT); + return read_c0_count(); } -#endif /* __KERNEL__ */ -#endif /* __ASM_MIPS_TIMEX_H */ +#endif /* _ASM_TIMEX_H */ diff --git a/include/asm-mips/tlb.h b/include/asm-mips/tlb.h index 69c0faa93194..9376fd6fffc9 100644 --- a/include/asm-mips/tlb.h +++ b/include/asm-mips/tlb.h @@ -1 +1,18 @@ +#ifndef __ASM_TLB_H +#define __ASM_TLB_H + +/* + * MIPS doesn't need any special per-pte or per-vma handling.. + */ +#define tlb_start_vma(tlb, vma) do { } while (0) +#define tlb_end_vma(tlb, vma) do { } while (0) +#define __tlb_remove_tlb_entry(tlb, ptep, address) do { } while (0) + +/* + * .. because we flush the whole mm when it fills up. + */ +#define tlb_flush(tlb) flush_tlb_mm((tlb)->mm) + #include <asm-generic/tlb.h> + +#endif /* __ASM_TLB_H */ diff --git a/include/asm-mips/tlbdebug.h b/include/asm-mips/tlbdebug.h new file mode 100644 index 000000000000..fff7a73e22d0 --- /dev/null +++ b/include/asm-mips/tlbdebug.h @@ -0,0 +1,20 @@ +/* + * 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) 2002 by Ralf Baechle + */ +#ifndef __ASM_TLBDEBUG_H +#define __ASM_TLBDEBUG_H + +/* + * TLB debugging functions: + */ +extern void dump_tlb(int first, int last); +extern void dump_tlb_all(void); +extern void dump_tlb_wired(void); +extern void dump_tlb_addr(unsigned long addr); +extern void dump_tlb_nonwired(void); + +#endif /* __ASM_TLBDEBUG_H */ diff --git a/include/asm-mips/tlbflush.h b/include/asm-mips/tlbflush.h new file mode 100644 index 000000000000..9ce1779d413e --- /dev/null +++ b/include/asm-mips/tlbflush.h @@ -0,0 +1,55 @@ +#ifndef __ASM_TLBFLUSH_H +#define __ASM_TLBFLUSH_H + +#include <linux/config.h> +#include <linux/mm.h> + +/* + * 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(vma, start, end) flushes a range of pages + * - flush_tlb_kernel_range(start, end) flushes a range of kernel pages + * - flush_tlb_pgtables(mm, start, end) flushes a range of page tables + */ +extern void local_flush_tlb_all(void); +extern void local_flush_tlb_mm(struct mm_struct *mm); +extern void local_flush_tlb_range(struct vm_area_struct *vma, + unsigned long start, unsigned long end); +extern void local_flush_tlb_kernel_range(unsigned long start, + unsigned long end); +extern void local_flush_tlb_page(struct vm_area_struct *vma, + unsigned long page); +extern void local_flush_tlb_one(unsigned long vaddr); + +#ifdef CONFIG_SMP + +extern void flush_tlb_all(void); +extern void flush_tlb_mm(struct mm_struct *); +extern void flush_tlb_range(struct vm_area_struct *vma, unsigned long, + unsigned long); +extern void flush_tlb_kernel_range(unsigned long, unsigned long); +extern void flush_tlb_page(struct vm_area_struct *, unsigned long); +extern void flush_tlb_one(unsigned long vaddr); + +#else /* CONFIG_SMP */ + +#define flush_tlb_all() local_flush_tlb_all() +#define flush_tlb_mm(mm) local_flush_tlb_mm(mm) +#define flush_tlb_range(vma,vmaddr,end) local_flush_tlb_range(vma, vmaddr, end) +#define flush_tlb_kernel_range(vmaddr,end) \ + local_flush_tlb_kernel_range(vmaddr, end) +#define flush_tlb_page(vma,page) local_flush_tlb_page(vma, page) +#define flush_tlb_one(vaddr) local_flush_tlb_one(vaddr) + +#endif /* CONFIG_SMP */ + +static inline void flush_tlb_pgtables(struct mm_struct *mm, + unsigned long start, unsigned long end) +{ + /* Nothing to do on MIPS. */ +} + +#endif /* __ASM_TLBFLUSH_H */ diff --git a/include/asm-mips/topology.h b/include/asm-mips/topology.h index cf224a32ba6d..592a9934af02 100644 --- a/include/asm-mips/topology.h +++ b/include/asm-mips/topology.h @@ -1,6 +1,6 @@ -#ifndef _ASM_MIPS_TOPOLOGY_H -#define _ASM_MIPS_TOPOLOGY_H +#ifndef __ASM_TOPOLOGY_H +#define __ASM_TOPOLOGY_H #include <asm-generic/topology.h> -#endif /* _ASM_MIPS_TOPOLOGY_H */ +#endif /* __ASM_TOPOLOGY_H */ diff --git a/include/asm-mips/traps.h b/include/asm-mips/traps.h new file mode 100644 index 000000000000..7f00e31e7c4f --- /dev/null +++ b/include/asm-mips/traps.h @@ -0,0 +1,26 @@ +/* + * include/asm-mips/traps.h + * + * Trap handling definitions. + * + * Copyright (C) 2002, 2003 Maciej W. Rozycki + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#ifndef __ASM_MIPS_TRAPS_H +#define __ASM_MIPS_TRAPS_H + +/* + * Possible status responses for a board_be_handler backend. + */ +#define MIPS_BE_DISCARD 0 /* return with no action */ +#define MIPS_BE_FIXUP 1 /* return to the fixup code */ +#define MIPS_BE_FATAL 2 /* treat as an unrecoverable error */ + +extern void (*board_be_init)(void); +extern int (*board_be_handler)(struct pt_regs *regs, int is_fixup); + +#endif /* __ASM_MIPS_TRAPS_H */ diff --git a/include/asm-mips/types.h b/include/asm-mips/types.h index d4796beca3f5..2f5d4ef94efa 100644 --- a/include/asm-mips/types.h +++ b/include/asm-mips/types.h @@ -1,5 +1,4 @@ -/* $Id: types.h,v 1.3 1999/08/18 23:37:50 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. @@ -10,6 +9,8 @@ #ifndef _ASM_TYPES_H #define _ASM_TYPES_H +#include <linux/config.h> + #ifndef __ASSEMBLY__ typedef unsigned short umode_t; @@ -34,12 +35,12 @@ typedef __signed__ long __s64; typedef unsigned long __u64; #else - + #if defined(__GNUC__) && !defined(__STRICT_ANSI__) typedef __signed__ long long __s64; typedef unsigned long long __u64; #endif - + #endif #endif /* __ASSEMBLY__ */ @@ -76,7 +77,23 @@ typedef unsigned long long u64; #endif -typedef unsigned long dma_addr_t; +#if defined(CONFIG_HIGHMEM) && defined(CONFIG_64BIT_PHYS_ADDR) +typedef u64 dma_addr_t; +#else +typedef u32 dma_addr_t; +#endif +typedef u64 dma64_addr_t; + +#ifdef CONFIG_64BIT_PHYS_ADDR +typedef unsigned long long phys_t; +#else +typedef unsigned long phys_t; +#endif + +#ifdef CONFIG_LBD +typedef u64 sector_t; +#define HAVE_SECTOR_T +#endif #endif /* __ASSEMBLY__ */ diff --git a/include/asm-mips/uaccess.h b/include/asm-mips/uaccess.h index 06fe2da650c6..2a8833afe6dd 100644 --- a/include/asm-mips/uaccess.h +++ b/include/asm-mips/uaccess.h @@ -10,7 +10,7 @@ #define _ASM_UACCESS_H #include <linux/errno.h> -#include <linux/sched.h> +#include <linux/thread_info.h> #define STR(x) __STR(x) #define __STR(x) #x @@ -28,9 +28,9 @@ #define VERIFY_READ 0 #define VERIFY_WRITE 1 -#define get_fs() (current->thread.current_ds) #define get_ds() (KERNEL_DS) -#define set_fs(x) (current->thread.current_ds=(x)) +#define get_fs() (current_thread_info()->addr_limit) +#define set_fs(x) (current_thread_info()->addr_limit = (x)) #define segment_eq(a,b) ((a).seg == (b).seg) @@ -56,7 +56,7 @@ #define access_ok(type,addr,size) \ __access_ok(((unsigned long)(addr)),(size),__access_mask) -extern inline int verify_area(int type, const void * addr, unsigned long size) +static inline int verify_area(int type, const void * addr, unsigned long size) { return access_ok(type,addr,size) ? 0 : -EFAULT; } @@ -269,103 +269,99 @@ extern void __put_user_unknown(void); extern size_t __copy_user(void *__to, const void *__from, size_t __n); -#define __copy_to_user(to,from,n) ({ \ - void *__cu_to; \ - const void *__cu_from; \ - long __cu_len; \ - \ - __cu_to = (to); \ - __cu_from = (from); \ - __cu_len = (n); \ - __asm__ __volatile__( \ - "move\t$4, %1\n\t" \ - "move\t$5, %2\n\t" \ - "move\t$6, %3\n\t" \ - __MODULE_JAL(__copy_user) \ - "move\t%0, $6" \ - : "=r" (__cu_len) \ - : "r" (__cu_to), "r" (__cu_from), "r" (__cu_len) \ - : "$4", "$5", "$6", "$8", "$9", "$10", "$11", "$12", "$15", \ - "$24", "$31","memory"); \ - __cu_len; \ +#define __invoke_copy_to_user(to,from,n) ({ \ + register void *__cu_to_r __asm__ ("$4"); \ + register const void *__cu_from_r __asm__ ("$5"); \ + register long __cu_len_r __asm__ ("$6"); \ + \ + __cu_to_r = (to); \ + __cu_from_r = (from); \ + __cu_len_r = (n); \ + __asm__ __volatile__( \ + __MODULE_JAL(__copy_user) \ + : "+r" (__cu_to_r), "+r" (__cu_from_r), "+r" (__cu_len_r) \ + : \ + : "$8", "$9", "$10", "$11", "$12", "$15", "$24", "$31", \ + "memory"); \ + __cu_len_r; \ }) -#define __copy_from_user(to,from,n) ({ \ - void *__cu_to; \ - const void *__cu_from; \ - long __cu_len; \ - \ - __cu_to = (to); \ - __cu_from = (from); \ - __cu_len = (n); \ - __asm__ __volatile__( \ - "move\t$4, %1\n\t" \ - "move\t$5, %2\n\t" \ - "move\t$6, %3\n\t" \ - ".set\tnoreorder\n\t" \ - __MODULE_JAL(__copy_user) \ - ".set\tnoat\n\t" \ - "addu\t$1, %2, %3\n\t" \ - ".set\tat\n\t" \ - ".set\treorder\n\t" \ - "move\t%0, $6" \ - : "=r" (__cu_len) \ - : "r" (__cu_to), "r" (__cu_from), "r" (__cu_len) \ - : "$4", "$5", "$6", "$8", "$9", "$10", "$11", "$12", "$15", \ - "$24", "$31","memory"); \ - __cu_len; \ +#define __copy_to_user(to,from,n) ({ \ + void *__cu_to; \ + const void *__cu_from; \ + long __cu_len; \ + \ + __cu_to = (to); \ + __cu_from = (from); \ + __cu_len = (n); \ + __cu_len = __invoke_copy_to_user(__cu_to, __cu_from, __cu_len); \ + __cu_len; \ }) -#define copy_to_user(to,from,n) ({ \ - void *__cu_to; \ - const void *__cu_from; \ - long __cu_len; \ - \ - __cu_to = (to); \ - __cu_from = (from); \ - __cu_len = (n); \ - if (access_ok(VERIFY_WRITE, __cu_to, __cu_len)) \ - __asm__ __volatile__( \ - "move\t$4, %1\n\t" \ - "move\t$5, %2\n\t" \ - "move\t$6, %3\n\t" \ - __MODULE_JAL(__copy_user) \ - "move\t%0, $6" \ - : "=r" (__cu_len) \ - : "r" (__cu_to), "r" (__cu_from), "r" (__cu_len) \ - : "$4", "$5", "$6", "$8", "$9", "$10", "$11", "$12", \ - "$15", "$24", "$31","memory"); \ - __cu_len; \ +#define copy_to_user(to,from,n) ({ \ + void *__cu_to; \ + const void *__cu_from; \ + long __cu_len; \ + \ + __cu_to = (to); \ + __cu_from = (from); \ + __cu_len = (n); \ + if (access_ok(VERIFY_WRITE, __cu_to, __cu_len)) \ + __cu_len = __invoke_copy_to_user(__cu_to, __cu_from, \ + __cu_len); \ + __cu_len; \ }) -#define copy_from_user(to,from,n) ({ \ - void *__cu_to; \ - const void *__cu_from; \ - long __cu_len; \ - \ - __cu_to = (to); \ - __cu_from = (from); \ - __cu_len = (n); \ - if (access_ok(VERIFY_READ, __cu_from, __cu_len)) \ - __asm__ __volatile__( \ - "move\t$4, %1\n\t" \ - "move\t$5, %2\n\t" \ - "move\t$6, %3\n\t" \ - ".set\tnoreorder\n\t" \ - __MODULE_JAL(__copy_user) \ - ".set\tnoat\n\t" \ - "addu\t$1, %2, %3\n\t" \ - ".set\tat\n\t" \ - ".set\treorder\n\t" \ - "move\t%0, $6" \ - : "=r" (__cu_len) \ - : "r" (__cu_to), "r" (__cu_from), "r" (__cu_len) \ - : "$4", "$5", "$6", "$8", "$9", "$10", "$11", "$12", \ - "$15", "$24", "$31","memory"); \ - __cu_len; \ +#define __invoke_copy_from_user(to,from,n) ({ \ + register void *__cu_to_r __asm__ ("$4"); \ + register const void *__cu_from_r __asm__ ("$5"); \ + register long __cu_len_r __asm__ ("$6"); \ + \ + __cu_to_r = (to); \ + __cu_from_r = (from); \ + __cu_len_r = (n); \ + __asm__ __volatile__( \ + ".set\tnoreorder\n\t" \ + __MODULE_JAL(__copy_user) \ + ".set\tnoat\n\t" \ + "addu\t$1, %1, %2\n\t" \ + ".set\tat\n\t" \ + ".set\treorder\n\t" \ + : "+r" (__cu_to_r), "+r" (__cu_from_r), "+r" (__cu_len_r) \ + : \ + : "$8", "$9", "$10", "$11", "$12", "$15", "$24", "$31", \ + "memory"); \ + __cu_len_r; \ }) -extern inline __kernel_size_t +#define __copy_from_user(to,from,n) ({ \ + void *__cu_to; \ + const void *__cu_from; \ + long __cu_len; \ + \ + __cu_to = (to); \ + __cu_from = (from); \ + __cu_len = (n); \ + __cu_len = __invoke_copy_from_user(__cu_to, __cu_from, \ + __cu_len); \ + __cu_len; \ +}) + +#define copy_from_user(to,from,n) ({ \ + void *__cu_to; \ + const void *__cu_from; \ + long __cu_len; \ + \ + __cu_to = (to); \ + __cu_from = (from); \ + __cu_len = (n); \ + if (access_ok(VERIFY_READ, __cu_from, __cu_len)) \ + __cu_len = __invoke_copy_from_user(__cu_to, __cu_from, \ + __cu_len); \ + __cu_len; \ +}) + +static inline __kernel_size_t __clear_user(void *addr, __kernel_size_t size) { __kernel_size_t res; @@ -394,7 +390,7 @@ __cl_size; }) * Returns: -EFAULT if exception before terminator, N if the entire * buffer filled, else strlen. */ -extern inline long +static inline long __strncpy_from_user(char *__to, const char *__from, long __len) { long res; @@ -412,7 +408,7 @@ __strncpy_from_user(char *__to, const char *__from, long __len) return res; } -extern inline long +static inline long strncpy_from_user(char *__to, const char *__from, long __len) { long res; @@ -431,7 +427,7 @@ strncpy_from_user(char *__to, const char *__from, long __len) } /* Returns: 0 if bad, string length+1 (memory size) of string if ok */ -extern inline long __strlen_user(const char *s) +static inline long __strlen_user(const char *s) { long res; @@ -446,7 +442,7 @@ extern inline long __strlen_user(const char *s) return res; } -extern inline long strlen_user(const char *s) +static inline long strlen_user(const char *s) { long res; @@ -462,7 +458,7 @@ extern inline long strlen_user(const char *s) } /* Returns: 0 if bad, string length+1 (memory size) of string if ok */ -extern inline long __strnlen_user(const char *s, long n) +static inline long __strnlen_user(const char *s, long n) { long res; @@ -478,7 +474,7 @@ extern inline long __strnlen_user(const char *s, long n) return res; } -extern inline long strnlen_user(const char *s, long n) +static inline long strnlen_user(const char *s, long n) { long res; @@ -500,13 +496,4 @@ struct exception_table_entry unsigned long nextinsn; }; -/* Returns 0 if exception not found and fixup.unit otherwise. */ -extern unsigned long search_exception_table(unsigned long addr); - -/* Returns the new pc */ -#define fixup_exception(map_reg, fixup_unit, pc) \ -({ \ - fixup_unit; \ -}) - #endif /* _ASM_UACCESS_H */ diff --git a/include/asm-mips/ucontext.h b/include/asm-mips/ucontext.h index db501bef0ec4..8a4b20e88b81 100644 --- a/include/asm-mips/ucontext.h +++ b/include/asm-mips/ucontext.h @@ -1,5 +1,4 @@ -/* $Id: ucontext.h,v 1.2 1999/09/27 16:01:40 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. diff --git a/include/asm-mips/umap.h b/include/asm-mips/umap.h deleted file mode 100644 index 940148ee2906..000000000000 --- a/include/asm-mips/umap.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef __MIPS_UMAP_H -#define __MIPS_UMAP_H - -void remove_mapping (struct vm_area_struct *vma, struct task_struct *task, unsigned long start, -unsigned long end); - -#endif - diff --git a/include/asm-mips/unaligned.h b/include/asm-mips/unaligned.h index 2b0174925f22..3bd9d412f1d5 100644 --- a/include/asm-mips/unaligned.h +++ b/include/asm-mips/unaligned.h @@ -18,7 +18,7 @@ extern void __put_unaligned_bad_length(void); * This could have been implemented in plain C like IA64 but egcs 1.0.3a * inflates this to 23 instructions ... */ -extern inline unsigned long long __ldq_u(const unsigned long long * __addr) +static inline unsigned long long __ldq_u(const unsigned long long * __addr) { unsigned long long __res; @@ -33,7 +33,7 @@ extern inline unsigned long long __ldq_u(const unsigned long long * __addr) /* * Load word unaligned. */ -extern inline unsigned long __ldl_u(const unsigned int * __addr) +static inline unsigned long __ldl_u(const unsigned int * __addr) { unsigned long __res; @@ -47,7 +47,7 @@ extern inline unsigned long __ldl_u(const unsigned int * __addr) /* * Load halfword unaligned. */ -extern inline unsigned long __ldw_u(const unsigned short * __addr) +static inline unsigned long __ldw_u(const unsigned short * __addr) { unsigned long __res; @@ -61,7 +61,7 @@ extern inline unsigned long __ldw_u(const unsigned short * __addr) /* * Store doubleword ununaligned. */ -extern inline void __stq_u(unsigned long __val, unsigned long long * __addr) +static inline void __stq_u(unsigned long __val, unsigned long long * __addr) { __asm__("usw\t%1, %0\n\t" "usw\t%D1, 4+%0" @@ -72,7 +72,7 @@ extern inline void __stq_u(unsigned long __val, unsigned long long * __addr) /* * Store long ununaligned. */ -extern inline void __stl_u(unsigned long __val, unsigned int * __addr) +static inline void __stl_u(unsigned long __val, unsigned int * __addr) { __asm__("usw\t%1, %0" : "=m" (*__addr) @@ -82,7 +82,7 @@ extern inline void __stl_u(unsigned long __val, unsigned int * __addr) /* * Store word ununaligned. */ -extern inline void __stw_u(unsigned long __val, unsigned short * __addr) +static inline void __stw_u(unsigned long __val, unsigned short * __addr) { __asm__("ush\t%1, %0" : "=m" (*__addr) @@ -93,8 +93,8 @@ extern inline void __stw_u(unsigned long __val, unsigned short * __addr) * get_unaligned - get value from possibly mis-aligned location * @ptr: pointer to value * - * This macro should be used for accessing values larger in size than - * single bytes at locations that are expected to be improperly aligned, + * This macro should be used for accessing values larger in size than + * single bytes at locations that are expected to be improperly aligned, * e.g. retrieving a u16 value from a location not u16-aligned. * * Note that unaligned accesses can be very expensive on some architectures. @@ -129,8 +129,8 @@ extern inline void __stw_u(unsigned long __val, unsigned short * __addr) * @val: value to place * @ptr: pointer to location * - * This macro should be used for placing values larger in size than - * single bytes at locations that are expected to be improperly aligned, + * This macro should be used for placing values larger in size than + * single bytes at locations that are expected to be improperly aligned, * e.g. writing a u16 value to a location not u16-aligned. * * Note that unaligned accesses can be very expensive on some architectures. diff --git a/include/asm-mips/unistd.h b/include/asm-mips/unistd.h index 293b93991430..cf324a06eaf1 100644 --- a/include/asm-mips/unistd.h +++ b/include/asm-mips/unistd.h @@ -4,9 +4,6 @@ * for more details. * * Copyright (C) 1995, 96, 97, 98, 99, 2000 by Ralf Baechle - * - * Changed system calls macros _syscall5 - _syscall7 to push args 5 to 7 onto - * the stack. Robin Farine for ACN S.A, Copyright (C) 1996 by ACN S.A */ #ifndef _ASM_UNISTD_H #define _ASM_UNISTD_H @@ -30,7 +27,7 @@ #define __NR_chmod (__NR_Linux + 15) #define __NR_lchown (__NR_Linux + 16) #define __NR_break (__NR_Linux + 17) -#define __NR_oldstat (__NR_Linux + 18) +#define __NR_unused18 (__NR_Linux + 18) #define __NR_lseek (__NR_Linux + 19) #define __NR_getpid (__NR_Linux + 20) #define __NR_mount (__NR_Linux + 21) @@ -40,7 +37,7 @@ #define __NR_stime (__NR_Linux + 25) #define __NR_ptrace (__NR_Linux + 26) #define __NR_alarm (__NR_Linux + 27) -#define __NR_oldfstat (__NR_Linux + 28) +#define __NR_unused28 (__NR_Linux + 28) #define __NR_pause (__NR_Linux + 29) #define __NR_utime (__NR_Linux + 30) #define __NR_stty (__NR_Linux + 31) @@ -96,7 +93,7 @@ #define __NR_setgroups (__NR_Linux + 81) #define __NR_reserved82 (__NR_Linux + 82) #define __NR_symlink (__NR_Linux + 83) -#define __NR_oldlstat (__NR_Linux + 84) +#define __NR_unused84 (__NR_Linux + 84) #define __NR_readlink (__NR_Linux + 85) #define __NR_uselib (__NR_Linux + 86) #define __NR_swapon (__NR_Linux + 87) @@ -233,33 +230,70 @@ #define __NR_madvise (__NR_Linux + 218) #define __NR_getdents64 (__NR_Linux + 219) #define __NR_fcntl64 (__NR_Linux + 220) -#define __NR_gettid (__NR_Linux + 221) -#define __NR_tkill (__NR_Linux + 222) +#define __NR_reserved221 (__NR_Linux + 221) +#define __NR_gettid (__NR_Linux + 222) +#define __NR_readahead (__NR_Linux + 223) +#define __NR_setxattr (__NR_Linux + 224) +#define __NR_lsetxattr (__NR_Linux + 225) +#define __NR_fsetxattr (__NR_Linux + 226) +#define __NR_getxattr (__NR_Linux + 227) +#define __NR_lgetxattr (__NR_Linux + 228) +#define __NR_fgetxattr (__NR_Linux + 229) +#define __NR_listxattr (__NR_Linux + 230) +#define __NR_llistxattr (__NR_Linux + 231) +#define __NR_flistxattr (__NR_Linux + 232) +#define __NR_removexattr (__NR_Linux + 233) +#define __NR_lremovexattr (__NR_Linux + 234) +#define __NR_fremovexattr (__NR_Linux + 235) +#define __NR_tkill (__NR_Linux + 236) +#define __NR_sendfile64 (__NR_Linux + 237) +#define __NR_futex (__NR_Linux + 238) +#define __NR_sched_setaffinity (__NR_Linux + 239) +#define __NR_sched_getaffinity (__NR_Linux + 240) +#define __NR_io_setup (__NR_Linux + 241) +#define __NR_io_destroy (__NR_Linux + 242) +#define __NR_io_getevents (__NR_Linux + 243) +#define __NR_io_submit (__NR_Linux + 244) +#define __NR_io_cancel (__NR_Linux + 245) +#define __NR_exit_group (__NR_Linux + 246) +#define __NR_lookup_dcookie (__NR_Linux + 247) +#define __NR_epoll_create (__NR_Linux + 248) +#define __NR_epoll_ctl (__NR_Linux + 249) +#define __NR_epoll_wait (__NR_Linux + 250) +#define __NR_remap_file_pages (__NR_Linux + 251) +#define __NR_set_tid_address (__NR_Linux + 252) +#define __NR_restart_syscall (__NR_Linux + 253) +#define __NR_fadvise64 (__NR_Linux + 254) +#define __NR_statfs64 (__NR_Linux + 255) +#define __NR_fstatfs64 (__NR_Linux + 256) /* * Offset of the last Linux flavoured syscall */ -#define __NR_Linux_syscalls 220 +#define __NR_Linux_syscalls 256 -#ifndef _LANGUAGE_ASSEMBLY +#ifndef __ASSEMBLY__ /* XXX - _foo needs to be __foo, while __NR_bar could be _NR_bar. */ #define _syscall0(type,name) \ type name(void) \ { \ -long __res, __err; \ -__asm__ volatile ("li\t$2,%2\n\t" \ - "syscall\n\t" \ - "move\t%0, $2\n\t" \ - "move\t%1, $7" \ - : "=r" (__res), "=r" (__err) \ - : "i" (__NR_##name) \ - : "$2","$7","$8","$9","$10","$11","$12","$13","$14","$15", \ - "$24"); \ -if (__err == 0) \ - return (type) __res; \ -errno = __res; \ -return -1; \ + register unsigned long __v0 asm("$2") = __NR_##name; \ + register unsigned long __a3 asm("$7"); \ + \ + __asm__ volatile ( \ + ".set\tnoreorder\n\t" \ + "li\t$2, %2\t\t\t# " #name "\n\t" \ + "syscall\n\t" \ + ".set\treorder" \ + : "=&r" (__v0), "=r" (__a3) \ + : "i" (__NR_##name) \ + : "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24"); \ + \ + if (__a3 == 0) \ + return (type) __v0; \ + errno = __v0; \ + return -1; \ } /* @@ -269,189 +303,195 @@ return -1; \ #define _syscall1(type,name,atype,a) \ type name(atype a) \ { \ -long __res, __err; \ -__asm__ volatile ("move\t$4,%3\n\t" \ - "li\t$2,%2\n\t" \ - "syscall\n\t" \ - "move\t%0, $2\n\t" \ - "move\t%1, $7" \ - : "=r" (__res), "=r" (__err) \ - : "i" (__NR_##name),"r" ((long)(a)) \ - : "$2","$4","$7","$8","$9","$10","$11","$12","$13","$14","$15","$24"); \ -if (__err == 0) \ - return (type) __res; \ -errno = __res; \ -return -1; \ + register unsigned long __v0 asm("$2") = __NR_##name; \ + register unsigned long __a0 asm("$4") = (unsigned long) a; \ + register unsigned long __a3 asm("$7"); \ + \ + __asm__ volatile ( \ + ".set\tnoreorder\n\t" \ + "li\t$2, %3\t\t\t# " #name "\n\t" \ + "syscall\n\t" \ + ".set\treorder" \ + : "=&r" (__v0), "=r" (__a3) \ + : "r" (__a0), "i" (__NR_##name) \ + : "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24"); \ + \ + if (__a3 == 0) \ + return (type) __v0; \ + errno = __v0; \ + return -1; \ } #define _syscall2(type,name,atype,a,btype,b) \ -type name(atype a,btype b) \ +type name(atype a, btype b) \ { \ -long __res, __err; \ -__asm__ volatile ("move\t$4,%3\n\t" \ - "move\t$5,%4\n\t" \ - "li\t$2,%2\n\t" \ - "syscall\n\t" \ - "move\t%0, $2\n\t" \ - "move\t%1, $7" \ - : "=r" (__res), "=r" (__err) \ - : "i" (__NR_##name),"r" ((long)(a)), \ - "r" ((long)(b)) \ - : "$2","$4","$5","$7","$8","$9","$10","$11","$12","$13", \ - "$14","$15", "$24"); \ -if (__err == 0) \ - return (type) __res; \ -errno = __res; \ -return -1; \ + register unsigned long __v0 asm("$2") = __NR_##name; \ + register unsigned long __a0 asm("$4") = (unsigned long) a; \ + register unsigned long __a1 asm("$5") = (unsigned long) b; \ + register unsigned long __a3 asm("$7"); \ + \ + __asm__ volatile ( \ + ".set\tnoreorder\n\t" \ + "li\t$2, %4\t\t\t# " #name "\n\t" \ + "syscall\n\t" \ + ".set\treorder" \ + : "=&r" (__v0), "=r" (__a3) \ + : "r" (__a0), "r" (__a1), "i" (__NR_##name) \ + : "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24"); \ + \ + if (__a3 == 0) \ + return (type) __v0; \ + errno = __v0; \ + return -1; \ } #define _syscall3(type,name,atype,a,btype,b,ctype,c) \ -type name (atype a, btype b, ctype c) \ +type name(atype a, btype b, ctype c) \ { \ -long __res, __err; \ -__asm__ volatile ("move\t$4,%3\n\t" \ - "move\t$5,%4\n\t" \ - "move\t$6,%5\n\t" \ - "li\t$2,%2\n\t" \ - "syscall\n\t" \ - "move\t%0, $2\n\t" \ - "move\t%1, $7" \ - : "=r" (__res), "=r" (__err) \ - : "i" (__NR_##name),"r" ((long)(a)), \ - "r" ((long)(b)), \ - "r" ((long)(c)) \ - : "$2","$4","$5","$6","$7","$8","$9","$10","$11","$12", \ - "$13","$14","$15","$24"); \ -if (__err == 0) \ - return (type) __res; \ -errno = __res; \ -return -1; \ + register unsigned long __v0 asm("$2") = __NR_##name; \ + register unsigned long __a0 asm("$4") = (unsigned long) a; \ + register unsigned long __a1 asm("$5") = (unsigned long) b; \ + register unsigned long __a2 asm("$6") = (unsigned long) c; \ + register unsigned long __a3 asm("$7"); \ + \ + __asm__ volatile ( \ + ".set\tnoreorder\n\t" \ + "li\t$2, %5\t\t\t# " #name "\n\t" \ + "syscall\n\t" \ + ".set\treorder" \ + : "=&r" (__v0), "=r" (__a3) \ + : "r" (__a0), "r" (__a1), "r" (__a2), "i" (__NR_##name) \ + : "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24"); \ + \ + if (__a3 == 0) \ + return (type) __v0; \ + errno = __v0; \ + return -1; \ } #define _syscall4(type,name,atype,a,btype,b,ctype,c,dtype,d) \ -type name (atype a, btype b, ctype c, dtype d) \ +type name(atype a, btype b, ctype c, dtype d) \ { \ -long __res, __err; \ -__asm__ volatile ("move\t$4,%3\n\t" \ - "move\t$5,%4\n\t" \ - "move\t$6,%5\n\t" \ - "move\t$7,%6\n\t" \ - "li\t$2,%2\n\t" \ - "syscall\n\t" \ - "move\t%0, $2\n\t" \ - "move\t%1, $7" \ - : "=r" (__res), "=r" (__err) \ - : "i" (__NR_##name),"r" ((long)(a)), \ - "r" ((long)(b)), \ - "r" ((long)(c)), \ - "r" ((long)(d)) \ - : "$2","$4","$5","$6","$7","$8","$9","$10","$11","$12", \ - "$13","$14","$15","$24"); \ -if (__err == 0) \ - return (type) __res; \ -errno = __res; \ -return -1; \ + register unsigned long __v0 asm("$2") = __NR_##name; \ + register unsigned long __a0 asm("$4") = (unsigned long) a; \ + register unsigned long __a1 asm("$5") = (unsigned long) b; \ + register unsigned long __a2 asm("$6") = (unsigned long) c; \ + register unsigned long __a3 asm("$7") = (unsigned long) d; \ + \ + __asm__ volatile ( \ + ".set\tnoreorder\n\t" \ + "li\t$2, %5\t\t\t# " #name "\n\t" \ + "syscall\n\t" \ + ".set\treorder" \ + : "=&r" (__v0), "+r" (__a3) \ + : "r" (__a0), "r" (__a1), "r" (__a2), "i" (__NR_##name) \ + : "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24"); \ + \ + if (__a3 == 0) \ + return (type) __v0; \ + errno = __v0; \ + return -1; \ } +/* + * Using those means your brain needs more than an oil change ;-) + */ + #define _syscall5(type,name,atype,a,btype,b,ctype,c,dtype,d,etype,e) \ -type name (atype a,btype b,ctype c,dtype d,etype e) \ +type name(atype a, btype b, ctype c, dtype d, etype e) \ { \ -long __res, __err; \ -__asm__ volatile ("move\t$4,%3\n\t" \ - "move\t$5,%4\n\t" \ - "move\t$6,%5\n\t" \ - "lw\t$2,%7\n\t" \ - "move\t$7,%6\n\t" \ - "subu\t$29,24\n\t" \ - "sw\t$2,16($29)\n\t" \ - "li\t$2,%2\n\t" \ - "syscall\n\t" \ - "move\t%0, $2\n\t" \ - "move\t%1, $7\n\t" \ - "addiu\t$29,24" \ - : "=r" (__res), "=r" (__err) \ - : "i" (__NR_##name),"r" ((long)(a)), \ - "r" ((long)(b)), \ - "r" ((long)(c)), \ - "r" ((long)(d)), \ - "m" ((long)(e)) \ - : "$2","$4","$5","$6","$7","$8","$9","$10","$11","$12", \ - "$13","$14","$15","$24"); \ -if (__err == 0) \ - return (type) __res; \ -errno = __res; \ -return -1; \ + register unsigned long __v0 asm("$2") = __NR_##name; \ + register unsigned long __a0 asm("$4") = (unsigned long) a; \ + register unsigned long __a1 asm("$5") = (unsigned long) b; \ + register unsigned long __a2 asm("$6") = (unsigned long) c; \ + register unsigned long __a3 asm("$7") = (unsigned long) d; \ + \ + __asm__ volatile ( \ + ".set\tnoreorder\n\t" \ + "lw\t$2, %6\n\t" \ + "subu\t$29, 32\n\t" \ + "sw\t$2, 16($29)\n\t" \ + "li\t$2, %5\t\t\t# " #name "\n\t" \ + "syscall\n\t" \ + "addiu\t$29, 32\n\t" \ + ".set\treorder" \ + : "=&r" (__v0), "+r" (__a3) \ + : "r" (__a0), "r" (__a1), "r" (__a2), "i" (__NR_##name), \ + "m" ((unsigned long)e) \ + : "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24"); \ + \ + if (__a3 == 0) \ + return (type) __v0; \ + errno = __v0; \ + return -1; \ } #define _syscall6(type,name,atype,a,btype,b,ctype,c,dtype,d,etype,e,ftype,f) \ -type name (atype a,btype b,ctype c,dtype d,etype e,ftype f) \ +type name(atype a, btype b, ctype c, dtype d, etype e, ftype f) \ { \ -long __res, __err; \ -__asm__ volatile ("move\t$4,%3\n\t" \ - "move\t$5,%4\n\t" \ - "move\t$6,%5\n\t" \ - "lw\t$2,%7\n\t" \ - "lw\t$3,%8\n\t" \ - "move\t$7,%6\n\t" \ - "subu\t$29,24\n\t" \ - "sw\t$2,16($29)\n\t" \ - "sw\t$3,20($29)\n\t" \ - "li\t$2,%2\n\t" \ - "syscall\n\t" \ - "move\t%0, $2\n\t" \ - "move\t%1, $7\n\t" \ - "addiu\t$29,24" \ - : "=r" (__res), "=r" (__err) \ - : "i" (__NR_##name),"r" ((long)(a)), \ - "r" ((long)(b)), \ - "r" ((long)(c)), \ - "r" ((long)(d)), \ - "m" ((long)(e)), \ - "m" ((long)(f)) \ - : "$2","$3","$4","$5","$6","$7","$8","$9","$10","$11", \ - "$12","$13","$14","$15","$24"); \ -if (__err == 0) \ - return (type) __res; \ -errno = __res; \ -return -1; \ + register unsigned long __v0 asm("$2") = __NR_##name; \ + register unsigned long __a0 asm("$4") = (unsigned long) a; \ + register unsigned long __a1 asm("$5") = (unsigned long) b; \ + register unsigned long __a2 asm("$6") = (unsigned long) c; \ + register unsigned long __a3 asm("$7") = (unsigned long) d; \ + \ + __asm__ volatile ( \ + ".set\tnoreorder\n\t" \ + "lw\t$2, %6\n\t" \ + "lw\t$8, %7\n\t" \ + "subu\t$29, 32\n\t" \ + "sw\t$2, 16($29)\n\t" \ + "sw\t$8, 20($29)\n\t" \ + "li\t$2, %5\t\t\t# " #name "\n\t" \ + "syscall\n\t" \ + "addiu\t$29, 32\n\t" \ + ".set\treorder" \ + : "=&r" (__v0), "+r" (__a3) \ + : "r" (__a0), "r" (__a1), "r" (__a2), "i" (__NR_##name), \ + "m" ((unsigned long)e), "m" ((unsigned long)f) \ + : "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24"); \ + \ + if (__a3 == 0) \ + return (type) __v0; \ + errno = __v0; \ + return -1; \ } #define _syscall7(type,name,atype,a,btype,b,ctype,c,dtype,d,etype,e,ftype,f,gtype,g) \ -type name (atype a,btype b,ctype c,dtype d,etype e,ftype f,gtype g) \ +type name(atype a, btype b, ctype c, dtype d, etype e, ftype f, gtype g) \ { \ -long __res, __err; \ -__asm__ volatile ("move\t$4,%3\n\t" \ - "move\t$5,%4\n\t" \ - "move\t$6,%5\n\t" \ - "lw\t$2,%7\n\t" \ - "lw\t$3,%8\n\t" \ - "move\t$7,%6\n\t" \ - "subu\t$29,32\n\t" \ - "sw\t$2,16($29)\n\t" \ - "lw\t$2,%9\n\t" \ - "sw\t$3,20($29)\n\t" \ - "sw\t$2,24($29)\n\t" \ - "li\t$2,%2\n\t" \ - "syscall\n\t" \ - "move\t%0, $2\n\t" \ - "move\t%1, $7\n\t" \ - "addiu\t$29,32" \ - : "=r" (__res), "=r" (__err) \ - : "i" (__NR_##name),"r" ((long)(a)), \ - "r" ((long)(b)), \ - "r" ((long)(c)), \ - "r" ((long)(d)), \ - "m" ((long)(e)), \ - "m" ((long)(f)), \ - "m" ((long)(g)) \ - : "$2","$3","$4","$5","$6","$7","$8","$9","$10","$11", \ - "$12","$13","$14","$15","$24"); \ -if (__err == 0) \ - return (type) __res; \ -errno = __res; \ -return -1; \ + register unsigned long __v0 asm("$2") = __NR_##name; \ + register unsigned long __a0 asm("$4") = (unsigned long) a; \ + register unsigned long __a1 asm("$5") = (unsigned long) b; \ + register unsigned long __a2 asm("$6") = (unsigned long) c; \ + register unsigned long __a3 asm("$7") = (unsigned long) d; \ + \ + __asm__ volatile ( \ + ".set\tnoreorder\n\t" \ + "lw\t$2, %6\n\t" \ + "lw\t$8, %7\n\t" \ + "lw\t$9, %8\n\t" \ + "subu\t$29, 32\n\t" \ + "sw\t$2, 16($29)\n\t" \ + "sw\t$8, 20($29)\n\t" \ + "sw\t$9, 24($29)\n\t" \ + "li\t$2, %5\t\t\t# " #name "\n\t" \ + "syscall\n\t" \ + "addiu\t$29, 32\n\t" \ + ".set\treorder" \ + : "=&r" (__v0), "+r" (__a3) \ + : "r" (__a0), "r" (__a1), "r" (__a2), "i" (__NR_##name), \ + "m" ((unsigned long)e), "m" ((unsigned long)f), \ + "m" ((unsigned long)g), \ + : "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24"); \ + \ + if (__a3 == 0) \ + return (type) __v0; \ + errno = __v0; \ + return -1; \ } + #ifdef __KERNEL_SYSCALLS__ /* @@ -478,8 +518,8 @@ static inline _syscall1(int,close,int,fd) static inline _syscall1(int,_exit,int,exitcode) static inline _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options) -#endif /* !defined (__KERNEL_SYSCALLS__) */ -#endif /* !defined (_LANGUAGE_ASSEMBLY) */ +#endif /* __KERNEL_SYSCALLS__ */ +#endif /* !__ASSEMBLY__ */ /* * "Conditional" syscalls @@ -487,6 +527,6 @@ static inline _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options) * What we want is __attribute__((weak,alias("sys_ni_syscall"))), * but it doesn't work on all toolchains, so we just do it by hand */ -#define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall"); +#define cond_syscall(x) asm(".weak\t" #x "\n" #x "\t=\tsys_ni_syscall"); #endif /* _ASM_UNISTD_H */ diff --git a/include/asm-mips/user.h b/include/asm-mips/user.h index e3747e88cd19..3e55ed5466dd 100644 --- a/include/asm-mips/user.h +++ b/include/asm-mips/user.h @@ -1,8 +1,6 @@ #ifndef __ASM_MIPS_USER_H #define __ASM_MIPS_USER_H -#include <linux/ptrace.h> - #include <asm/page.h> #include <asm/reg.h> diff --git a/include/asm-mips/vga.h b/include/asm-mips/vga.h index a74df2d2d8b4..6b35cf054c79 100644 --- a/include/asm-mips/vga.h +++ b/include/asm-mips/vga.h @@ -3,18 +3,17 @@ * * (c) 1998 Martin Mares <mj@ucw.cz> */ - -#ifndef _LINUX_ASM_VGA_H_ -#define _LINUX_ASM_VGA_H_ +#ifndef _ASM_VGA_H +#define _ASM_VGA_H /* * On the PC, we can just recalculate addresses and then * access the videoram directly without any black magic. */ -#define VGA_MAP_MEM(x) ((unsigned long)0xb0000000 + (unsigned long)(x)) +#define VGA_MAP_MEM(x) (0xb0000000L + (unsigned long)(x)) -#define vga_readb(x) (*(x)) -#define vga_writeb(x,y) (*(y) = (x)) +#define vga_readb(x) (*(x)) +#define vga_writeb(x,y) (*(y) = (x)) -#endif +#endif /* _ASM_VGA_H */ diff --git a/include/asm-mips/war.h b/include/asm-mips/war.h new file mode 100644 index 000000000000..a62c7d63240e --- /dev/null +++ b/include/asm-mips/war.h @@ -0,0 +1,132 @@ +/* + * 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) 2002 by Ralf Baechle + */ +#ifndef _ASM_WAR_H +#define _ASM_WAR_H + +#include <linux/config.h> + +/* + * Pleassures of the R4600 V1.x. Cite from the IDT R4600 V1.7 errata: + * + * 18. The CACHE instructions Hit_Writeback_Invalidate_D, Hit_Writeback_D, + * Hit_Invalidate_D and Create_Dirty_Excl_D should only be + * executed if there is no other dcache activity. If the dcache is + * accessed for another instruction immeidately preceding when these + * cache instructions are executing, it is possible that the dcache + * tag match outputs used by these cache instructions will be + * incorrect. These cache instructions should be preceded by at least + * four instructions that are not any kind of load or store + * instruction. + * + * This is not allowed: lw + * nop + * nop + * nop + * cache Hit_Writeback_Invalidate_D + * + * This is allowed: lw + * nop + * nop + * nop + * nop + * cache Hit_Writeback_Invalidate_D + * + * #define R4600_V1_HIT_CACHEOP_WAR 1 + */ + + +/* + * Writeback and invalidate the primary cache dcache before DMA. + * + * R4600 v2.0 bug: "The CACHE instructions Hit_Writeback_Inv_D, + * Hit_Writeback_D, Hit_Invalidate_D and Create_Dirty_Exclusive_D will only + * operate correctly if the internal data cache refill buffer is empty. These + * CACHE instructions should be separated from any potential data cache miss + * by a load instruction to an uncached address to empty the response buffer." + * (Revision 2.0 device errata from IDT available on http://www.idt.com/ + * in .pdf format.) + * + * #define R4600_V2_HIT_CACHEOP_WAR 1 + */ + +/* + * R4600 CPU modules for the Indy come with both V1.7 and V2.0 processors. + */ +#ifdef CONFIG_SGI_IP22 + +#define R4600_V1_HIT_CACHEOP_WAR 1 +#define R4600_V2_HIT_CACHEOP_WAR 1 + +#endif + +/* + * But the RM200C seems to have been shipped only with V2.0 R4600s + */ +#ifdef CONFIG_SNI_RM200_PCI + +#define R4600_V2_HIT_CACHEOP_WAR 1 + +#endif + +#ifdef CONFIG_CPU_R5432 + +/* + * When an interrupt happens on a CP0 register read instruction, CPU may + * lock up or read corrupted values of CP0 registers after it enters + * the exception handler. + * + * This workaround makes sure that we read a "safe" CP0 register as the + * first thing in the exception handler, which breaks one of the + * pre-conditions for this problem. + */ +#define R5432_CP0_INTERRUPT_WAR 1 + +#endif + +#if defined(CONFIG_SB1_PASS_1_WORKAROUNDS) || \ + defined(CONFIG_SB1_PASS_2_WORKAROUNDS) + +/* + * Workaround for the Sibyte M3 errata the text of which can be found at + * + * http://sibyte.broadcom.com/hw/bcm1250/docs/pass2errata.txt + * + * This will enable the use of a special TLB refill handler which does a + * consistency check on the information in c0_badvaddr and c0_entryhi and + * will just return and take the exception again if the information was + * found to be inconsistent. + */ +#define BCM1250_M3_WAR 1 + +/* + * This is a DUART workaround related to glitches around register accesses + */ +#define SIBYTE_1956_WAR 1 + +#endif + +/* + * Workarounds default to off + */ +#ifndef R4600_V1_HIT_CACHEOP_WAR +#define R4600_V1_HIT_CACHEOP_WAR 0 +#endif +#ifndef R4600_V2_HIT_CACHEOP_WAR +#define R4600_V2_HIT_CACHEOP_WAR 0 +#endif +#ifndef R5432_CP0_INTERRUPT_WAR +#define R5432_CP0_INTERRUPT_WAR 0 +#endif +#ifndef BCM1250_M3_WAR +#define BCM1250_M3_WAR 0 +#endif +#ifndef SIBYTE_1956_WAR +#define SIBYTE_1956_WAR 0 +#endif + +#endif /* _ASM_WAR_H */ diff --git a/include/asm-mips/watch.h b/include/asm-mips/watch.h index 7945e0b2fef3..337d8af54261 100644 --- a/include/asm-mips/watch.h +++ b/include/asm-mips/watch.h @@ -18,20 +18,18 @@ enum wref_type { wr_load = 2 }; -extern char watch_available; - extern asmlinkage void __watch_set(unsigned long addr, enum wref_type ref); extern asmlinkage void __watch_clear(void); extern asmlinkage void __watch_reenable(void); #define watch_set(addr, ref) \ - if (watch_available) \ + if (cpu_has_watch) \ __watch_set(addr, ref) #define watch_clear() \ - if (watch_available) \ + if (cpu_has_watch) \ __watch_clear() #define watch_reenable() \ - if (watch_available) \ + if (cpu_has_watch) \ __watch_reenable() #endif /* __ASM_WATCH_H */ diff --git a/include/asm-mips/wbflush.h b/include/asm-mips/wbflush.h index 0d9c9a8683e0..8aac7ea7b6fa 100644 --- a/include/asm-mips/wbflush.h +++ b/include/asm-mips/wbflush.h @@ -6,31 +6,30 @@ * for more details. * * Copyright (c) 1998 Harald Koerfgen - * - * $Id: wbflush.h,v 1.2 1999/08/13 17:07:28 harald Exp $ + * Copyright (C) 2002 Maciej W. Rozycki */ #ifndef __ASM_MIPS_WBFLUSH_H #define __ASM_MIPS_WBFLUSH_H #include <linux/config.h> -#if defined(CONFIG_CPU_HAS_WB) -/* - * R2000 or R3000 - */ -extern void (*__wbflush) (void); +#ifdef CONFIG_CPU_HAS_WB + +extern void (*__wbflush)(void); +extern void wbflush_setup(void); -#define wbflush() __wbflush() +#define wbflush() \ + do { \ + __sync(); \ + __wbflush(); \ + } while (0) -#else -/* - * we don't need no stinkin' wbflush - */ +#else /* !CONFIG_CPU_HAS_WB */ -#define wbflush() do { } while(0) +#define wbflush_setup() do { } while (0) -#endif +#define wbflush() fast_iob() -extern void wbflush_setup(void); +#endif /* !CONFIG_CPU_HAS_WB */ #endif /* __ASM_MIPS_WBFLUSH_H */ |
