powerpc: Merge enough to start building in arch/powerpc.
This creates the directory structure under arch/powerpc and a bunch of Kconfig files. It does a first-cut merge of arch/powerpc/mm, arch/powerpc/lib and arch/powerpc/platforms/powermac. This is enough to build a 32-bit powermac kernel with ARCH=powerpc. For now we are getting some unmerged files from arch/ppc/kernel and arch/ppc/syslib, or arch/ppc64/kernel. This makes some minor changes to files in those directories and files outside arch/powerpc. The boot directory is still not merged. That's going to be interesting. Signed-off-by: Paul Mackerras <paulus@samba.org>
This commit is contained in:
		
							
								
								
									
										861
									
								
								arch/powerpc/Kconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										861
									
								
								arch/powerpc/Kconfig
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,861 @@
 | 
			
		||||
# For a description of the syntax of this configuration file,
 | 
			
		||||
# see Documentation/kbuild/kconfig-language.txt.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
mainmenu "Linux/PowerPC Kernel Configuration"
 | 
			
		||||
 | 
			
		||||
config PPC64
 | 
			
		||||
	bool "64-bit kernel"
 | 
			
		||||
	default n
 | 
			
		||||
	help
 | 
			
		||||
	  This option selects whether a 32-bit or a 64-bit kernel
 | 
			
		||||
	  will be built.
 | 
			
		||||
 | 
			
		||||
config PPC32
 | 
			
		||||
	bool
 | 
			
		||||
	default y if !PPC64
 | 
			
		||||
 | 
			
		||||
config 64BIT
 | 
			
		||||
	bool
 | 
			
		||||
	default y if PPC64
 | 
			
		||||
 | 
			
		||||
config PPC_MERGE
 | 
			
		||||
	def_bool y
 | 
			
		||||
 | 
			
		||||
config MMU
 | 
			
		||||
	bool
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config UID16
 | 
			
		||||
	bool
 | 
			
		||||
 | 
			
		||||
config GENERIC_HARDIRQS
 | 
			
		||||
	bool
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config RWSEM_GENERIC_SPINLOCK
 | 
			
		||||
	bool
 | 
			
		||||
 | 
			
		||||
config RWSEM_XCHGADD_ALGORITHM
 | 
			
		||||
	bool
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config GENERIC_CALIBRATE_DELAY
 | 
			
		||||
	bool
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config PPC
 | 
			
		||||
	bool
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config EARLY_PRINTK
 | 
			
		||||
	bool
 | 
			
		||||
	default y if PPC64
 | 
			
		||||
 | 
			
		||||
config COMPAT
 | 
			
		||||
	bool
 | 
			
		||||
	default y if PPC64
 | 
			
		||||
 | 
			
		||||
config SYSVIPC_COMPAT
 | 
			
		||||
	bool
 | 
			
		||||
	depends on COMPAT && SYSVIPC
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
# All PPC32s use generic nvram driver through ppc_md
 | 
			
		||||
config GENERIC_NVRAM
 | 
			
		||||
	bool
 | 
			
		||||
	default y if PPC32
 | 
			
		||||
 | 
			
		||||
config SCHED_NO_NO_OMIT_FRAME_POINTER
 | 
			
		||||
	bool
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config ARCH_MAY_HAVE_PC_FDC
 | 
			
		||||
	bool
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
menu "Processor support"
 | 
			
		||||
choice
 | 
			
		||||
	prompt "Processor Type"
 | 
			
		||||
	depends on PPC32
 | 
			
		||||
	default 6xx
 | 
			
		||||
 | 
			
		||||
config 6xx
 | 
			
		||||
	bool "6xx/7xx/74xx"
 | 
			
		||||
	select PPC_FPU
 | 
			
		||||
	help
 | 
			
		||||
	  There are four families of PowerPC chips supported.  The more common
 | 
			
		||||
	  types (601, 603, 604, 740, 750, 7400), the Motorola embedded
 | 
			
		||||
	  versions (821, 823, 850, 855, 860, 52xx, 82xx, 83xx), the AMCC
 | 
			
		||||
	  embedded versions (403 and 405) and the high end 64 bit Power
 | 
			
		||||
	  processors (POWER 3, POWER4, and IBM PPC970 also known as G5).
 | 
			
		||||
	  
 | 
			
		||||
	  Unless you are building a kernel for one of the embedded processor
 | 
			
		||||
	  systems, 64 bit IBM RS/6000 or an Apple G5, choose 6xx.
 | 
			
		||||
	  Note that the kernel runs in 32-bit mode even on 64-bit chips.
 | 
			
		||||
 | 
			
		||||
config PPC_52xx
 | 
			
		||||
	bool "Freescale 52xx"
 | 
			
		||||
	
 | 
			
		||||
config PPC_82xx
 | 
			
		||||
	bool "Freescale 82xx"
 | 
			
		||||
 | 
			
		||||
config PPC_83xx
 | 
			
		||||
	bool "Freescale 83xx"
 | 
			
		||||
 | 
			
		||||
config 40x
 | 
			
		||||
	bool "AMCC 40x"
 | 
			
		||||
 | 
			
		||||
config 44x
 | 
			
		||||
	bool "AMCC 44x"
 | 
			
		||||
 | 
			
		||||
config PPC64BRIDGE
 | 
			
		||||
	select PPC_FPU
 | 
			
		||||
	bool "POWER3, POWER4 and PPC970 (G5)"
 | 
			
		||||
 | 
			
		||||
config 8xx
 | 
			
		||||
	bool "Freescale 8xx"
 | 
			
		||||
 | 
			
		||||
config E200
 | 
			
		||||
	bool "Freescale e200"
 | 
			
		||||
 | 
			
		||||
config E500
 | 
			
		||||
	bool "Freescale e500"
 | 
			
		||||
endchoice
 | 
			
		||||
 | 
			
		||||
config POWER4_ONLY
 | 
			
		||||
	bool "Optimize for POWER4"
 | 
			
		||||
	depends on PPC64 || PPC64BRIDGE
 | 
			
		||||
	default n
 | 
			
		||||
	---help---
 | 
			
		||||
	  Cause the compiler to optimize for POWER4/POWER5/PPC970 processors.
 | 
			
		||||
	  The resulting binary will not work on POWER3 or RS64 processors
 | 
			
		||||
	  when compiled with binutils 2.15 or later.
 | 
			
		||||
 | 
			
		||||
config POWER3
 | 
			
		||||
	bool
 | 
			
		||||
	depends on PPC64 || PPC64BRIDGE
 | 
			
		||||
	default y if !POWER4_ONLY
 | 
			
		||||
 | 
			
		||||
config POWER4
 | 
			
		||||
	depends on PPC64 || PPC64BRIDGE
 | 
			
		||||
	def_bool y
 | 
			
		||||
 | 
			
		||||
config PPC_FPU
 | 
			
		||||
	bool
 | 
			
		||||
	default y if PPC64
 | 
			
		||||
 | 
			
		||||
config BOOKE
 | 
			
		||||
	bool
 | 
			
		||||
	depends on E200 || E500
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config FSL_BOOKE
 | 
			
		||||
	bool
 | 
			
		||||
	depends on E200 || E500
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config PTE_64BIT
 | 
			
		||||
	bool
 | 
			
		||||
	depends on 44x || E500
 | 
			
		||||
	default y if 44x
 | 
			
		||||
	default y if E500 && PHYS_64BIT
 | 
			
		||||
 | 
			
		||||
config PHYS_64BIT
 | 
			
		||||
	bool 'Large physical address support' if E500
 | 
			
		||||
	depends on 44x || E500
 | 
			
		||||
	default y if 44x
 | 
			
		||||
	---help---
 | 
			
		||||
	  This option enables kernel support for larger than 32-bit physical
 | 
			
		||||
	  addresses.  This features is not be available on all e500 cores.
 | 
			
		||||
 | 
			
		||||
	  If in doubt, say N here.
 | 
			
		||||
 | 
			
		||||
config ALTIVEC
 | 
			
		||||
	bool "AltiVec Support"
 | 
			
		||||
	depends on 6xx || POWER4
 | 
			
		||||
	---help---
 | 
			
		||||
	  This option enables kernel support for the Altivec extensions to the
 | 
			
		||||
	  PowerPC processor. The kernel currently supports saving and restoring
 | 
			
		||||
	  altivec registers, and turning on the 'altivec enable' bit so user
 | 
			
		||||
	  processes can execute altivec instructions.
 | 
			
		||||
 | 
			
		||||
	  This option is only usefully if you have a processor that supports
 | 
			
		||||
	  altivec (G4, otherwise known as 74xx series), but does not have
 | 
			
		||||
	  any affect on a non-altivec cpu (it does, however add code to the
 | 
			
		||||
	  kernel).
 | 
			
		||||
 | 
			
		||||
	  If in doubt, say Y here.
 | 
			
		||||
 | 
			
		||||
config SPE
 | 
			
		||||
	bool "SPE Support"
 | 
			
		||||
	depends on E200 || E500
 | 
			
		||||
	---help---
 | 
			
		||||
	  This option enables kernel support for the Signal Processing
 | 
			
		||||
	  Extensions (SPE) to the PowerPC processor. The kernel currently
 | 
			
		||||
	  supports saving and restoring SPE registers, and turning on the
 | 
			
		||||
	  'spe enable' bit so user processes can execute SPE instructions.
 | 
			
		||||
 | 
			
		||||
	  This option is only useful if you have a processor that supports
 | 
			
		||||
	  SPE (e500, otherwise known as 85xx series), but does not have any
 | 
			
		||||
	  effect on a non-spe cpu (it does, however add code to the kernel).
 | 
			
		||||
 | 
			
		||||
	  If in doubt, say Y here.
 | 
			
		||||
 | 
			
		||||
config PPC_STD_MMU
 | 
			
		||||
	bool
 | 
			
		||||
	depends on 6xx || POWER3 || POWER4 || PPC64
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config PPC_STD_MMU_32
 | 
			
		||||
	def_bool y
 | 
			
		||||
	depends on PPC_STD_MMU && PPC32
 | 
			
		||||
 | 
			
		||||
config SMP
 | 
			
		||||
	depends on PPC_STD_MMU
 | 
			
		||||
	bool "Symmetric multi-processing support"
 | 
			
		||||
	---help---
 | 
			
		||||
	  This enables support for systems with more than one CPU. If you have
 | 
			
		||||
	  a system with only one CPU, say N. If you have a system with more
 | 
			
		||||
	  than one CPU, say Y.  Note that the kernel does not currently
 | 
			
		||||
	  support SMP machines with 603/603e/603ev or PPC750 ("G3") processors
 | 
			
		||||
	  since they have inadequate hardware support for multiprocessor
 | 
			
		||||
	  operation.
 | 
			
		||||
 | 
			
		||||
	  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 single-processor machines.
 | 
			
		||||
	  On a single-processor machine, the kernel will run faster if you say
 | 
			
		||||
	  N here.
 | 
			
		||||
 | 
			
		||||
	  If you don't know what to do here, say N.
 | 
			
		||||
 | 
			
		||||
config NR_CPUS
 | 
			
		||||
	int "Maximum number of CPUs (2-32)"
 | 
			
		||||
	range 2 128
 | 
			
		||||
	depends on SMP
 | 
			
		||||
	default "32" if PPC64
 | 
			
		||||
	default "4"
 | 
			
		||||
 | 
			
		||||
config NOT_COHERENT_CACHE
 | 
			
		||||
	bool
 | 
			
		||||
	depends on 4xx || 8xx || E200
 | 
			
		||||
	default y
 | 
			
		||||
endmenu
 | 
			
		||||
 | 
			
		||||
source "init/Kconfig"
 | 
			
		||||
 | 
			
		||||
menu "Platform support"
 | 
			
		||||
	depends on PPC64 || 6xx
 | 
			
		||||
 | 
			
		||||
choice
 | 
			
		||||
	prompt "Machine type"
 | 
			
		||||
	default PPC_MULTIPLATFORM
 | 
			
		||||
 | 
			
		||||
config PPC_MULTIPLATFORM
 | 
			
		||||
	bool "Generic desktop/server/laptop"
 | 
			
		||||
	help
 | 
			
		||||
	  Select this option if configuring for an IBM pSeries or
 | 
			
		||||
	  RS/6000 machine, an Apple machine, or a PReP, CHRP,
 | 
			
		||||
	  Maple or Cell-based machine.
 | 
			
		||||
 | 
			
		||||
config PPC_ISERIES
 | 
			
		||||
	bool "IBM Legacy iSeries"
 | 
			
		||||
	depends on PPC64
 | 
			
		||||
 | 
			
		||||
config EMBEDDED6xx
 | 
			
		||||
	bool "Embedded 6xx/7xx/7xxx-based board"
 | 
			
		||||
	depends on PPC32
 | 
			
		||||
 | 
			
		||||
config APUS
 | 
			
		||||
	bool "Amiga-APUS"
 | 
			
		||||
	depends on PPC32 && BROKEN
 | 
			
		||||
	help
 | 
			
		||||
	  Select APUS if configuring for a PowerUP Amiga.
 | 
			
		||||
	  More information is available at:
 | 
			
		||||
	  <http://linux-apus.sourceforge.net/>.
 | 
			
		||||
endchoice
 | 
			
		||||
 | 
			
		||||
config PPC_PSERIES
 | 
			
		||||
	depends on PPC_MULTIPLATFORM && PPC64
 | 
			
		||||
	bool "  IBM pSeries & new (POWER5-based) iSeries"
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config PPC_CHRP
 | 
			
		||||
	bool "  Common Hardware Reference Platform (CHRP) based machines"
 | 
			
		||||
	depends on PPC_MULTIPLATFORM && PPC32
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config PPC_PMAC
 | 
			
		||||
	bool "  Apple PowerMac based machines"
 | 
			
		||||
	depends on PPC_MULTIPLATFORM
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config PPC_PMAC64
 | 
			
		||||
	bool
 | 
			
		||||
	depends on PPC_PMAC && POWER4
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config PPC_PREP
 | 
			
		||||
	bool "  PowerPC Reference Platform (PReP) based machines"
 | 
			
		||||
	depends on PPC_MULTIPLATFORM && PPC32
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config PPC_MAPLE
 | 
			
		||||
	depends on PPC_MULTIPLATFORM && PPC64
 | 
			
		||||
	bool "  Maple 970FX Evaluation Board"
 | 
			
		||||
	select U3_DART
 | 
			
		||||
	select MPIC_BROKEN_U3
 | 
			
		||||
	default n
 | 
			
		||||
	help
 | 
			
		||||
          This option enables support for the Maple 970FX Evaluation Board.
 | 
			
		||||
	  For more informations, refer to <http://www.970eval.com>
 | 
			
		||||
 | 
			
		||||
config PPC_BPA
 | 
			
		||||
	bool "  Broadband Processor Architecture"
 | 
			
		||||
	depends on PPC_MULTIPLATFORM && PPC64
 | 
			
		||||
 | 
			
		||||
config PPC_OF
 | 
			
		||||
	bool
 | 
			
		||||
	depends on PPC_MULTIPLATFORM	# for now
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config XICS
 | 
			
		||||
	depends on PPC_PSERIES
 | 
			
		||||
	bool
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config U3_DART
 | 
			
		||||
	bool 
 | 
			
		||||
	depends on PPC_MULTIPLATFORM && PPC64
 | 
			
		||||
	default n
 | 
			
		||||
 | 
			
		||||
config MPIC
 | 
			
		||||
	depends on PPC_PSERIES || PPC_PMAC || PPC_MAPLE
 | 
			
		||||
	bool
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config MPIC_BROKEN_U3
 | 
			
		||||
	bool
 | 
			
		||||
	depends on PPC_MAPLE
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config BPA_IIC
 | 
			
		||||
	depends on PPC_BPA
 | 
			
		||||
	bool
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config IBMVIO
 | 
			
		||||
	depends on PPC_PSERIES || PPC_ISERIES
 | 
			
		||||
	bool
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
source "drivers/cpufreq/Kconfig"
 | 
			
		||||
 | 
			
		||||
config CPU_FREQ_PMAC
 | 
			
		||||
	bool "Support for Apple PowerBooks"
 | 
			
		||||
	depends on CPU_FREQ && ADB_PMU && PPC32
 | 
			
		||||
	select CPU_FREQ_TABLE
 | 
			
		||||
	help
 | 
			
		||||
	  This adds support for frequency switching on Apple PowerBooks,
 | 
			
		||||
	  this currently includes some models of iBook & Titanium
 | 
			
		||||
	  PowerBook.
 | 
			
		||||
 | 
			
		||||
config PPC601_SYNC_FIX
 | 
			
		||||
	bool "Workarounds for PPC601 bugs"
 | 
			
		||||
	depends on 6xx && (PPC_PREP || PPC_PMAC)
 | 
			
		||||
	help
 | 
			
		||||
	  Some versions of the PPC601 (the first PowerPC chip) have bugs which
 | 
			
		||||
	  mean that extra synchronization instructions are required near
 | 
			
		||||
	  certain instructions, typically those that make major changes to the
 | 
			
		||||
	  CPU state.  These extra instructions reduce performance slightly.
 | 
			
		||||
	  If you say N here, these extra instructions will not be included,
 | 
			
		||||
	  resulting in a kernel which will run faster but may not run at all
 | 
			
		||||
	  on some systems with the PPC601 chip.
 | 
			
		||||
 | 
			
		||||
	  If in doubt, say Y here.
 | 
			
		||||
 | 
			
		||||
config TAU
 | 
			
		||||
	bool "Thermal Management Support"
 | 
			
		||||
	depends on 6xx
 | 
			
		||||
	help
 | 
			
		||||
	  G3 and G4 processors have an on-chip temperature sensor called the
 | 
			
		||||
	  'Thermal Assist Unit (TAU)', which, in theory, can measure the on-die
 | 
			
		||||
	  temperature within 2-4 degrees Celsius. This option shows the current
 | 
			
		||||
	  on-die temperature in /proc/cpuinfo if the cpu supports it.
 | 
			
		||||
 | 
			
		||||
	  Unfortunately, on some chip revisions, this sensor is very inaccurate
 | 
			
		||||
	  and in some cases, does not work at all, so don't assume the cpu
 | 
			
		||||
	  temp is actually what /proc/cpuinfo says it is.
 | 
			
		||||
 | 
			
		||||
config TAU_INT
 | 
			
		||||
	bool "Interrupt driven TAU driver (DANGEROUS)"
 | 
			
		||||
	depends on TAU
 | 
			
		||||
	---help---
 | 
			
		||||
	  The TAU supports an interrupt driven mode which causes an interrupt
 | 
			
		||||
	  whenever the temperature goes out of range. This is the fastest way
 | 
			
		||||
	  to get notified the temp has exceeded a range. With this option off,
 | 
			
		||||
	  a timer is used to re-check the temperature periodically.
 | 
			
		||||
 | 
			
		||||
	  However, on some cpus it appears that the TAU interrupt hardware
 | 
			
		||||
	  is buggy and can cause a situation which would lead unexplained hard
 | 
			
		||||
	  lockups.
 | 
			
		||||
 | 
			
		||||
	  Unless you are extending the TAU driver, or enjoy kernel/hardware
 | 
			
		||||
	  debugging, leave this option off.
 | 
			
		||||
 | 
			
		||||
config TAU_AVERAGE
 | 
			
		||||
	bool "Average high and low temp"
 | 
			
		||||
	depends on TAU
 | 
			
		||||
	---help---
 | 
			
		||||
	  The TAU hardware can compare the temperature to an upper and lower
 | 
			
		||||
	  bound.  The default behavior is to show both the upper and lower
 | 
			
		||||
	  bound in /proc/cpuinfo. If the range is large, the temperature is
 | 
			
		||||
	  either changing a lot, or the TAU hardware is broken (likely on some
 | 
			
		||||
	  G4's). If the range is small (around 4 degrees), the temperature is
 | 
			
		||||
	  relatively stable.  If you say Y here, a single temperature value,
 | 
			
		||||
	  halfway between the upper and lower bounds, will be reported in
 | 
			
		||||
	  /proc/cpuinfo.
 | 
			
		||||
 | 
			
		||||
	  If in doubt, say N here.
 | 
			
		||||
endmenu
 | 
			
		||||
 | 
			
		||||
source arch/powerpc/platforms/embedded6xx/Kconfig
 | 
			
		||||
source arch/powerpc/platforms/4xx/Kconfig
 | 
			
		||||
source arch/powerpc/platforms/85xx/Kconfig
 | 
			
		||||
source arch/powerpc/platforms/8xx/Kconfig
 | 
			
		||||
 | 
			
		||||
menu "Kernel options"
 | 
			
		||||
 | 
			
		||||
config HIGHMEM
 | 
			
		||||
	bool "High memory support"
 | 
			
		||||
	depends on PPC32
 | 
			
		||||
 | 
			
		||||
source kernel/Kconfig.hz
 | 
			
		||||
source kernel/Kconfig.preempt
 | 
			
		||||
source "fs/Kconfig.binfmt"
 | 
			
		||||
 | 
			
		||||
# We optimistically allocate largepages from the VM, so make the limit
 | 
			
		||||
# large enough (16MB). This badly named config option is actually
 | 
			
		||||
# max order + 1
 | 
			
		||||
config FORCE_MAX_ZONEORDER
 | 
			
		||||
	int
 | 
			
		||||
	depends on PPC64
 | 
			
		||||
	default "13"
 | 
			
		||||
 | 
			
		||||
config MATH_EMULATION
 | 
			
		||||
	bool "Math emulation"
 | 
			
		||||
	depends on 4xx || 8xx || E200 || E500
 | 
			
		||||
	---help---
 | 
			
		||||
	  Some PowerPC chips designed for embedded applications do not have
 | 
			
		||||
	  a floating-point unit and therefore do not implement the
 | 
			
		||||
	  floating-point instructions in the PowerPC instruction set.  If you
 | 
			
		||||
	  say Y here, the kernel will include code to emulate a floating-point
 | 
			
		||||
	  unit, which will allow programs that use floating-point
 | 
			
		||||
	  instructions to run.
 | 
			
		||||
 | 
			
		||||
config IOMMU_VMERGE
 | 
			
		||||
	bool "Enable IOMMU virtual merging (EXPERIMENTAL)"
 | 
			
		||||
	depends on EXPERIMENTAL && PPC64
 | 
			
		||||
	default n
 | 
			
		||||
	help
 | 
			
		||||
	  Cause IO segments sent to a device for DMA to be merged virtually
 | 
			
		||||
	  by the IOMMU when they happen to have been allocated contiguously.
 | 
			
		||||
	  This doesn't add pressure to the IOMMU allocator. However, some
 | 
			
		||||
	  drivers don't support getting large merged segments coming back
 | 
			
		||||
	  from *_map_sg(). Say Y if you know the drivers you are using are
 | 
			
		||||
	  properly handling this case.
 | 
			
		||||
 | 
			
		||||
config HOTPLUG_CPU
 | 
			
		||||
	bool "Support for enabling/disabling CPUs"
 | 
			
		||||
	depends on SMP && HOTPLUG && EXPERIMENTAL && (PPC_PSERIES || PPC_PMAC)
 | 
			
		||||
	---help---
 | 
			
		||||
	  Say Y here to be able to disable and re-enable individual
 | 
			
		||||
	  CPUs at runtime on SMP machines.
 | 
			
		||||
 | 
			
		||||
	  Say N if you are unsure.
 | 
			
		||||
 | 
			
		||||
config KEXEC
 | 
			
		||||
	bool "kexec system call (EXPERIMENTAL)"
 | 
			
		||||
	depends on PPC_MULTIPLATFORM && EXPERIMENTAL
 | 
			
		||||
	help
 | 
			
		||||
	  kexec is a system call that implements the ability to shutdown your
 | 
			
		||||
	  current kernel, and to start another kernel.  It is like a reboot
 | 
			
		||||
	  but it is indepedent of the system firmware.   And like a reboot
 | 
			
		||||
	  you can start any kernel with it, not just Linux.
 | 
			
		||||
 | 
			
		||||
	  The name comes from the similiarity to the exec system call.
 | 
			
		||||
 | 
			
		||||
	  It is an ongoing process to be certain the hardware in a machine
 | 
			
		||||
	  is properly shutdown, so do not be surprised if this code does not
 | 
			
		||||
	  initially work for you.  It may help to enable device hotplugging
 | 
			
		||||
	  support.  As of this writing the exact hardware interface is
 | 
			
		||||
	  strongly in flux, so no good recommendation can be made.
 | 
			
		||||
 | 
			
		||||
config EMBEDDEDBOOT
 | 
			
		||||
	bool
 | 
			
		||||
	depends on 8xx || 8260
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config PC_KEYBOARD
 | 
			
		||||
	bool "PC PS/2 style Keyboard"
 | 
			
		||||
	depends on 4xx || CPM2
 | 
			
		||||
 | 
			
		||||
config PPCBUG_NVRAM
 | 
			
		||||
	bool "Enable reading PPCBUG NVRAM during boot" if PPLUS || LOPEC
 | 
			
		||||
	default y if PPC_PREP
 | 
			
		||||
 | 
			
		||||
config IRQ_ALL_CPUS
 | 
			
		||||
	bool "Distribute interrupts on all CPUs by default"
 | 
			
		||||
	depends on SMP && !MV64360
 | 
			
		||||
	help
 | 
			
		||||
	  This option gives the kernel permission to distribute IRQs across
 | 
			
		||||
	  multiple CPUs.  Saying N here will route all IRQs to the first
 | 
			
		||||
	  CPU.  Generally saying Y is safe, although some problems have been
 | 
			
		||||
	  reported with SMP Power Macintoshes with this option enabled.
 | 
			
		||||
 | 
			
		||||
source "arch/powerpc/platforms/pseries/Kconfig"
 | 
			
		||||
 | 
			
		||||
config ARCH_SELECT_MEMORY_MODEL
 | 
			
		||||
	def_bool y
 | 
			
		||||
	depends on PPC64
 | 
			
		||||
 | 
			
		||||
config ARCH_FLATMEM_ENABLE
 | 
			
		||||
       def_bool y
 | 
			
		||||
       depends on PPC64 && !NUMA
 | 
			
		||||
 | 
			
		||||
config ARCH_DISCONTIGMEM_ENABLE
 | 
			
		||||
	def_bool y
 | 
			
		||||
	depends on SMP && PPC_PSERIES
 | 
			
		||||
 | 
			
		||||
config ARCH_DISCONTIGMEM_DEFAULT
 | 
			
		||||
	def_bool y
 | 
			
		||||
	depends on ARCH_DISCONTIGMEM_ENABLE
 | 
			
		||||
 | 
			
		||||
config ARCH_FLATMEM_ENABLE
 | 
			
		||||
	def_bool y
 | 
			
		||||
	depends on PPC64
 | 
			
		||||
 | 
			
		||||
config ARCH_SPARSEMEM_ENABLE
 | 
			
		||||
	def_bool y
 | 
			
		||||
	depends on ARCH_DISCONTIGMEM_ENABLE
 | 
			
		||||
 | 
			
		||||
source "mm/Kconfig"
 | 
			
		||||
 | 
			
		||||
config HAVE_ARCH_EARLY_PFN_TO_NID
 | 
			
		||||
	def_bool y
 | 
			
		||||
	depends on NEED_MULTIPLE_NODES
 | 
			
		||||
 | 
			
		||||
# Some NUMA nodes have memory ranges that span
 | 
			
		||||
# other nodes.  Even though a pfn is valid and
 | 
			
		||||
# between a node's start and end pfns, it may not
 | 
			
		||||
# reside on that node.
 | 
			
		||||
#
 | 
			
		||||
# This is a relatively temporary hack that should
 | 
			
		||||
# be able to go away when sparsemem is fully in
 | 
			
		||||
# place
 | 
			
		||||
 | 
			
		||||
config NODES_SPAN_OTHER_NODES
 | 
			
		||||
	def_bool y
 | 
			
		||||
	depends on NEED_MULTIPLE_NODES
 | 
			
		||||
 | 
			
		||||
config NUMA
 | 
			
		||||
	bool "NUMA support"
 | 
			
		||||
	default y if DISCONTIGMEM || SPARSEMEM
 | 
			
		||||
 | 
			
		||||
config SCHED_SMT
 | 
			
		||||
	bool "SMT (Hyperthreading) scheduler support"
 | 
			
		||||
	depends on PPC64 && SMP
 | 
			
		||||
	default off
 | 
			
		||||
	help
 | 
			
		||||
	  SMT scheduler support improves the CPU scheduler's decision making
 | 
			
		||||
	  when dealing with POWER5 cpus at a cost of slightly increased
 | 
			
		||||
	  overhead in some places. If unsure say N here.
 | 
			
		||||
 | 
			
		||||
config PROC_DEVICETREE
 | 
			
		||||
	bool "Support for Open Firmware device tree in /proc"
 | 
			
		||||
	depends on PPC_OF && PROC_FS
 | 
			
		||||
	help
 | 
			
		||||
	  This option adds a device-tree directory under /proc which contains
 | 
			
		||||
	  an image of the device tree that the kernel copies from Open
 | 
			
		||||
	  Firmware. If unsure, say Y here.
 | 
			
		||||
 | 
			
		||||
source "arch/powerpc/platforms/prep/Kconfig"
 | 
			
		||||
 | 
			
		||||
config CMDLINE_BOOL
 | 
			
		||||
	bool "Default bootloader kernel arguments"
 | 
			
		||||
	depends on !PPC_ISERIES
 | 
			
		||||
 | 
			
		||||
config CMDLINE
 | 
			
		||||
	string "Initial kernel command string"
 | 
			
		||||
	depends on CMDLINE_BOOL
 | 
			
		||||
	default "console=ttyS0,9600 console=tty0 root=/dev/sda2"
 | 
			
		||||
	help
 | 
			
		||||
	  On some platforms, there is currently no way for the boot loader to
 | 
			
		||||
	  pass arguments to the kernel. For these platforms, you can supply
 | 
			
		||||
	  some command-line options at build time by entering them here.  In
 | 
			
		||||
	  most cases you will need to specify the root device here.
 | 
			
		||||
 | 
			
		||||
if !44x || BROKEN
 | 
			
		||||
source kernel/power/Kconfig
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
config SECCOMP
 | 
			
		||||
	bool "Enable seccomp to safely compute untrusted bytecode"
 | 
			
		||||
	depends on PROC_FS
 | 
			
		||||
	default y
 | 
			
		||||
	help
 | 
			
		||||
	  This kernel feature is useful for number crunching applications
 | 
			
		||||
	  that may need to compute untrusted bytecode during their
 | 
			
		||||
	  execution. By using pipes or other transports made available to
 | 
			
		||||
	  the process as file descriptors supporting the read/write
 | 
			
		||||
	  syscalls, it's possible to isolate those applications in
 | 
			
		||||
	  their own address space using seccomp. Once seccomp is
 | 
			
		||||
	  enabled via /proc/<pid>/seccomp, it cannot be disabled
 | 
			
		||||
	  and the task is only allowed to execute a few safe syscalls
 | 
			
		||||
	  defined by each seccomp mode.
 | 
			
		||||
 | 
			
		||||
	  If unsure, say Y. Only embedded should say N here.
 | 
			
		||||
 | 
			
		||||
endmenu
 | 
			
		||||
 | 
			
		||||
config ISA_DMA_API
 | 
			
		||||
	bool
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
menu "Bus options"
 | 
			
		||||
 | 
			
		||||
config ISA
 | 
			
		||||
	bool "Support for ISA-bus hardware"
 | 
			
		||||
	depends on PPC_PREP || PPC_CHRP
 | 
			
		||||
	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.  If you have an Apple machine, say N here; if you
 | 
			
		||||
	  have an IBM RS/6000 or pSeries machine or a PReP machine, say Y.  If
 | 
			
		||||
	  you have an embedded board, consult your board documentation.
 | 
			
		||||
 | 
			
		||||
config GENERIC_ISA_DMA
 | 
			
		||||
	bool
 | 
			
		||||
	depends on PPC64 || POWER4 || 6xx && !CPM2
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config EISA
 | 
			
		||||
	bool
 | 
			
		||||
 | 
			
		||||
config SBUS
 | 
			
		||||
	bool
 | 
			
		||||
 | 
			
		||||
# Yes MCA RS/6000s exist but Linux-PPC does not currently support any
 | 
			
		||||
config MCA
 | 
			
		||||
	bool
 | 
			
		||||
 | 
			
		||||
config PCI
 | 
			
		||||
	bool "PCI support" if 40x || CPM2 || 83xx || 85xx || PPC_MPC52xx || (EMBEDDED && PPC_ISERIES)
 | 
			
		||||
	default y if !40x && !CPM2 && !8xx && !APUS && !83xx && !85xx
 | 
			
		||||
	default PCI_PERMEDIA if !4xx && !CPM2 && !8xx && APUS
 | 
			
		||||
	default PCI_QSPAN if !4xx && !CPM2 && 8xx
 | 
			
		||||
	help
 | 
			
		||||
	  Find out whether your system includes a PCI bus. PCI is the name of
 | 
			
		||||
	  a bus system, i.e. the way the CPU talks to the other stuff inside
 | 
			
		||||
	  your box.  If you say Y here, the kernel will include drivers and
 | 
			
		||||
	  infrastructure code to support PCI bus devices.
 | 
			
		||||
 | 
			
		||||
config PCI_DOMAINS
 | 
			
		||||
	bool
 | 
			
		||||
	default PCI
 | 
			
		||||
 | 
			
		||||
config MPC83xx_PCI2
 | 
			
		||||
	bool "  Supprt for 2nd PCI host controller"
 | 
			
		||||
	depends on PCI && MPC834x
 | 
			
		||||
	default y if MPC834x_SYS
 | 
			
		||||
 | 
			
		||||
config PCI_QSPAN
 | 
			
		||||
	bool "QSpan PCI"
 | 
			
		||||
	depends on !4xx && !CPM2 && 8xx
 | 
			
		||||
	help
 | 
			
		||||
	  Say Y here if you have a system based on a Motorola 8xx-series
 | 
			
		||||
	  embedded processor with a QSPAN PCI interface, otherwise say N.
 | 
			
		||||
 | 
			
		||||
config PCI_8260
 | 
			
		||||
	bool
 | 
			
		||||
	depends on PCI && 8260
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config 8260_PCI9
 | 
			
		||||
	bool "  Enable workaround for MPC826x erratum PCI 9"
 | 
			
		||||
	depends on PCI_8260 && !ADS8272
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
choice
 | 
			
		||||
	prompt "  IDMA channel for PCI 9 workaround"
 | 
			
		||||
	depends on 8260_PCI9
 | 
			
		||||
 | 
			
		||||
config 8260_PCI9_IDMA1
 | 
			
		||||
	bool "IDMA1"
 | 
			
		||||
 | 
			
		||||
config 8260_PCI9_IDMA2
 | 
			
		||||
	bool "IDMA2"
 | 
			
		||||
 | 
			
		||||
config 8260_PCI9_IDMA3
 | 
			
		||||
	bool "IDMA3"
 | 
			
		||||
 | 
			
		||||
config 8260_PCI9_IDMA4
 | 
			
		||||
	bool "IDMA4"
 | 
			
		||||
 | 
			
		||||
endchoice
 | 
			
		||||
 | 
			
		||||
source "drivers/pci/Kconfig"
 | 
			
		||||
 | 
			
		||||
source "drivers/pcmcia/Kconfig"
 | 
			
		||||
 | 
			
		||||
source "drivers/pci/hotplug/Kconfig"
 | 
			
		||||
 | 
			
		||||
endmenu
 | 
			
		||||
 | 
			
		||||
menu "Advanced setup"
 | 
			
		||||
	depends on PPC32
 | 
			
		||||
 | 
			
		||||
config ADVANCED_OPTIONS
 | 
			
		||||
	bool "Prompt for advanced kernel configuration options"
 | 
			
		||||
	help
 | 
			
		||||
	  This option will enable prompting for a variety of advanced kernel
 | 
			
		||||
	  configuration options.  These options can cause the kernel to not
 | 
			
		||||
	  work if they are set incorrectly, but can be used to optimize certain
 | 
			
		||||
	  aspects of kernel memory management.
 | 
			
		||||
 | 
			
		||||
	  Unless you know what you are doing, say N here.
 | 
			
		||||
 | 
			
		||||
comment "Default settings for advanced configuration options are used"
 | 
			
		||||
	depends on !ADVANCED_OPTIONS
 | 
			
		||||
 | 
			
		||||
config HIGHMEM_START_BOOL
 | 
			
		||||
	bool "Set high memory pool address"
 | 
			
		||||
	depends on ADVANCED_OPTIONS && HIGHMEM
 | 
			
		||||
	help
 | 
			
		||||
	  This option allows you to set the base address of the kernel virtual
 | 
			
		||||
	  area used to map high memory pages.  This can be useful in
 | 
			
		||||
	  optimizing the layout of kernel virtual memory.
 | 
			
		||||
 | 
			
		||||
	  Say N here unless you know what you are doing.
 | 
			
		||||
 | 
			
		||||
config HIGHMEM_START
 | 
			
		||||
	hex "Virtual start address of high memory pool" if HIGHMEM_START_BOOL
 | 
			
		||||
	default "0xfe000000"
 | 
			
		||||
 | 
			
		||||
config LOWMEM_SIZE_BOOL
 | 
			
		||||
	bool "Set maximum low memory"
 | 
			
		||||
	depends on ADVANCED_OPTIONS
 | 
			
		||||
	help
 | 
			
		||||
	  This option allows you to set the maximum amount of memory which
 | 
			
		||||
	  will be used as "low memory", that is, memory which the kernel can
 | 
			
		||||
	  access directly, without having to set up a kernel virtual mapping.
 | 
			
		||||
	  This can be useful in optimizing the layout of kernel virtual
 | 
			
		||||
	  memory.
 | 
			
		||||
 | 
			
		||||
	  Say N here unless you know what you are doing.
 | 
			
		||||
 | 
			
		||||
config LOWMEM_SIZE
 | 
			
		||||
	hex "Maximum low memory size (in bytes)" if LOWMEM_SIZE_BOOL
 | 
			
		||||
	default "0x30000000"
 | 
			
		||||
 | 
			
		||||
config KERNEL_START_BOOL
 | 
			
		||||
	bool "Set custom kernel base address"
 | 
			
		||||
	depends on ADVANCED_OPTIONS
 | 
			
		||||
	help
 | 
			
		||||
	  This option allows you to set the kernel virtual address at which
 | 
			
		||||
	  the kernel will map low memory (the kernel image will be linked at
 | 
			
		||||
	  this address).  This can be useful in optimizing the virtual memory
 | 
			
		||||
	  layout of the system.
 | 
			
		||||
 | 
			
		||||
	  Say N here unless you know what you are doing.
 | 
			
		||||
 | 
			
		||||
config KERNEL_START
 | 
			
		||||
	hex "Virtual address of kernel base" if KERNEL_START_BOOL
 | 
			
		||||
	default "0xc0000000"
 | 
			
		||||
 | 
			
		||||
config TASK_SIZE_BOOL
 | 
			
		||||
	bool "Set custom user task size"
 | 
			
		||||
	depends on ADVANCED_OPTIONS
 | 
			
		||||
	help
 | 
			
		||||
	  This option allows you to set the amount of virtual address space
 | 
			
		||||
	  allocated to user tasks.  This can be useful in optimizing the
 | 
			
		||||
	  virtual memory layout of the system.
 | 
			
		||||
 | 
			
		||||
	  Say N here unless you know what you are doing.
 | 
			
		||||
 | 
			
		||||
config TASK_SIZE
 | 
			
		||||
	hex "Size of user task space" if TASK_SIZE_BOOL
 | 
			
		||||
	default "0x80000000"
 | 
			
		||||
 | 
			
		||||
config CONSISTENT_START_BOOL
 | 
			
		||||
	bool "Set custom consistent memory pool address"
 | 
			
		||||
	depends on ADVANCED_OPTIONS && NOT_COHERENT_CACHE
 | 
			
		||||
	help
 | 
			
		||||
	  This option allows you to set the base virtual address
 | 
			
		||||
	  of the the consistent memory pool.  This pool of virtual
 | 
			
		||||
	  memory is used to make consistent memory allocations.
 | 
			
		||||
 | 
			
		||||
config CONSISTENT_START
 | 
			
		||||
	hex "Base virtual address of consistent memory pool" if CONSISTENT_START_BOOL
 | 
			
		||||
	default "0xff100000" if NOT_COHERENT_CACHE
 | 
			
		||||
 | 
			
		||||
config CONSISTENT_SIZE_BOOL
 | 
			
		||||
	bool "Set custom consistent memory pool size"
 | 
			
		||||
	depends on ADVANCED_OPTIONS && NOT_COHERENT_CACHE
 | 
			
		||||
	help
 | 
			
		||||
	  This option allows you to set the size of the the
 | 
			
		||||
	  consistent memory pool.  This pool of virtual memory
 | 
			
		||||
	  is used to make consistent memory allocations.
 | 
			
		||||
 | 
			
		||||
config CONSISTENT_SIZE
 | 
			
		||||
	hex "Size of consistent memory pool" if CONSISTENT_SIZE_BOOL
 | 
			
		||||
	default "0x00200000" if NOT_COHERENT_CACHE
 | 
			
		||||
 | 
			
		||||
config BOOT_LOAD_BOOL
 | 
			
		||||
	bool "Set the boot link/load address"
 | 
			
		||||
	depends on ADVANCED_OPTIONS && !PPC_MULTIPLATFORM
 | 
			
		||||
	help
 | 
			
		||||
	  This option allows you to set the initial load address of the zImage
 | 
			
		||||
	  or zImage.initrd file.  This can be useful if you are on a board
 | 
			
		||||
	  which has a small amount of memory.
 | 
			
		||||
 | 
			
		||||
	  Say N here unless you know what you are doing.
 | 
			
		||||
 | 
			
		||||
config BOOT_LOAD
 | 
			
		||||
	hex "Link/load address for booting" if BOOT_LOAD_BOOL
 | 
			
		||||
	default "0x00400000" if 40x || 8xx || 8260
 | 
			
		||||
	default "0x01000000" if 44x
 | 
			
		||||
	default "0x00800000"
 | 
			
		||||
 | 
			
		||||
config PIN_TLB
 | 
			
		||||
	bool "Pinned Kernel TLBs (860 ONLY)"
 | 
			
		||||
	depends on ADVANCED_OPTIONS && 8xx
 | 
			
		||||
endmenu
 | 
			
		||||
 | 
			
		||||
source "net/Kconfig"
 | 
			
		||||
 | 
			
		||||
source "drivers/Kconfig"
 | 
			
		||||
 | 
			
		||||
source "fs/Kconfig"
 | 
			
		||||
 | 
			
		||||
# XXX source "arch/ppc/8xx_io/Kconfig"
 | 
			
		||||
 | 
			
		||||
# XXX source "arch/ppc/8260_io/Kconfig"
 | 
			
		||||
 | 
			
		||||
source "arch/powerpc/platforms/iseries/Kconfig"
 | 
			
		||||
 | 
			
		||||
source "lib/Kconfig"
 | 
			
		||||
 | 
			
		||||
source "arch/powerpc/oprofile/Kconfig"
 | 
			
		||||
 | 
			
		||||
source "arch/powerpc/Kconfig.debug"
 | 
			
		||||
 | 
			
		||||
source "security/Kconfig"
 | 
			
		||||
 | 
			
		||||
config KEYS_COMPAT
 | 
			
		||||
	bool
 | 
			
		||||
	depends on COMPAT && KEYS
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
source "crypto/Kconfig"
 | 
			
		||||
							
								
								
									
										73
									
								
								arch/powerpc/Kconfig.debug
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								arch/powerpc/Kconfig.debug
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,73 @@
 | 
			
		||||
menu "Kernel hacking"
 | 
			
		||||
 | 
			
		||||
source "lib/Kconfig.debug"
 | 
			
		||||
 | 
			
		||||
config KGDB
 | 
			
		||||
	bool "Include kgdb kernel debugger"
 | 
			
		||||
	depends on DEBUG_KERNEL && (BROKEN || PPC_GEN550 || 4xx)
 | 
			
		||||
	select DEBUG_INFO
 | 
			
		||||
	help
 | 
			
		||||
	  Include in-kernel hooks for kgdb, the Linux kernel source level
 | 
			
		||||
	  debugger.  See <http://kgdb.sourceforge.net/> for more information.
 | 
			
		||||
	  Unless you are intending to debug the kernel, say N here.
 | 
			
		||||
 | 
			
		||||
choice
 | 
			
		||||
	prompt "Serial Port"
 | 
			
		||||
	depends on KGDB
 | 
			
		||||
	default KGDB_TTYS1
 | 
			
		||||
 | 
			
		||||
config KGDB_TTYS0
 | 
			
		||||
	bool "ttyS0"
 | 
			
		||||
 | 
			
		||||
config KGDB_TTYS1
 | 
			
		||||
	bool "ttyS1"
 | 
			
		||||
 | 
			
		||||
config KGDB_TTYS2
 | 
			
		||||
	bool "ttyS2"
 | 
			
		||||
 | 
			
		||||
config KGDB_TTYS3
 | 
			
		||||
	bool "ttyS3"
 | 
			
		||||
 | 
			
		||||
endchoice
 | 
			
		||||
 | 
			
		||||
config KGDB_CONSOLE
 | 
			
		||||
	bool "Enable serial console thru kgdb port"
 | 
			
		||||
	depends on KGDB && 8xx || CPM2
 | 
			
		||||
	help
 | 
			
		||||
	  If you enable this, all serial console messages will be sent
 | 
			
		||||
	  over the gdb stub.
 | 
			
		||||
	  If unsure, say N.
 | 
			
		||||
 | 
			
		||||
config XMON
 | 
			
		||||
	bool "Include xmon kernel debugger"
 | 
			
		||||
	depends on DEBUG_KERNEL
 | 
			
		||||
	help
 | 
			
		||||
	  Include in-kernel hooks for the xmon kernel monitor/debugger.
 | 
			
		||||
	  Unless you are intending to debug the kernel, say N here.
 | 
			
		||||
 | 
			
		||||
config BDI_SWITCH
 | 
			
		||||
	bool "Include BDI-2000 user context switcher"
 | 
			
		||||
	depends on DEBUG_KERNEL
 | 
			
		||||
	help
 | 
			
		||||
	  Include in-kernel support for the Abatron BDI2000 debugger.
 | 
			
		||||
	  Unless you are intending to debug the kernel with one of these
 | 
			
		||||
	  machines, say N here.
 | 
			
		||||
 | 
			
		||||
config BOOTX_TEXT
 | 
			
		||||
	bool "Support for early boot text console (BootX or OpenFirmware only)"
 | 
			
		||||
	depends PPC_OF
 | 
			
		||||
	help
 | 
			
		||||
	  Say Y here to see progress messages from the boot firmware in text
 | 
			
		||||
	  mode. Requires either BootX or Open Firmware.
 | 
			
		||||
 | 
			
		||||
config SERIAL_TEXT_DEBUG
 | 
			
		||||
	bool "Support for early boot texts over serial port"
 | 
			
		||||
	depends on 4xx || LOPEC || MV64X60 || PPLUS || PRPMC800 || \
 | 
			
		||||
		PPC_GEN550 || PPC_MPC52xx
 | 
			
		||||
 | 
			
		||||
config PPC_OCP
 | 
			
		||||
	bool
 | 
			
		||||
	depends on IBM_OCP || XILINX_OCP
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
endmenu
 | 
			
		||||
							
								
								
									
										222
									
								
								arch/powerpc/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										222
									
								
								arch/powerpc/Makefile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,222 @@
 | 
			
		||||
# 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.
 | 
			
		||||
#
 | 
			
		||||
# 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 by Linus Torvalds
 | 
			
		||||
# Changes for PPC by Gary Thomas
 | 
			
		||||
# Rewritten by Cort Dougan and Paul Mackerras
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
# This must match PAGE_OFFSET in include/asm-powerpc/page.h.
 | 
			
		||||
KERNELLOAD	:= $(CONFIG_KERNEL_START)
 | 
			
		||||
 | 
			
		||||
HAS_BIARCH	:= $(call cc-option-yn, -m32)
 | 
			
		||||
 | 
			
		||||
ifeq ($(CONFIG_PPC64),y)
 | 
			
		||||
SZ	:= 64
 | 
			
		||||
 | 
			
		||||
# Set default 32 bits cross compilers for vdso and boot wrapper
 | 
			
		||||
CROSS32_COMPILE ?=
 | 
			
		||||
 | 
			
		||||
CROSS32CC		:= $(CROSS32_COMPILE)gcc
 | 
			
		||||
CROSS32AS		:= $(CROSS32_COMPILE)as
 | 
			
		||||
CROSS32LD		:= $(CROSS32_COMPILE)ld
 | 
			
		||||
CROSS32OBJCOPY		:= $(CROSS32_COMPILE)objcopy
 | 
			
		||||
 | 
			
		||||
ifeq ($(HAS_BIARCH),y)
 | 
			
		||||
ifeq ($(CROSS32_COMPILE),)
 | 
			
		||||
CROSS32CC	:= $(CC) -m32
 | 
			
		||||
CROSS32AS	:= $(AS) -a32
 | 
			
		||||
CROSS32LD	:= $(LD) -m elf32ppc
 | 
			
		||||
CROSS32OBJCOPY	:= $(OBJCOPY)
 | 
			
		||||
endif
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
export CROSS32CC CROSS32AS CROSS32LD CROSS32OBJCOPY
 | 
			
		||||
 | 
			
		||||
new_nm := $(shell if $(NM) --help 2>&1 | grep -- '--synthetic' > /dev/null; then echo y; else echo n; fi)
 | 
			
		||||
 | 
			
		||||
ifeq ($(new_nm),y)
 | 
			
		||||
NM		:= $(NM) --synthetic
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
else
 | 
			
		||||
SZ	:= 32
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifeq ($(HAS_BIARCH),y)
 | 
			
		||||
override AS	+= -a$(SZ)
 | 
			
		||||
override LD	+= -m elf$(SZ)ppc
 | 
			
		||||
override CC	+= -m$(SZ)
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
LDFLAGS_vmlinux	:= -Ttext $(KERNELLOAD) -Bstatic -e $(KERNELLOAD)
 | 
			
		||||
 | 
			
		||||
# The -Iarch/$(ARCH)/include is temporary while we are merging
 | 
			
		||||
CPPFLAGS	+= -Iarch/$(ARCH) -Iarch/$(ARCH)/include
 | 
			
		||||
AFLAGS		+= -Iarch/$(ARCH)
 | 
			
		||||
CFLAGS		+= -Iarch/$(ARCH) -msoft-float -pipe
 | 
			
		||||
ifeq ($(CONFIG_PPC64),y)
 | 
			
		||||
CFLAGS		+= -mminimal-toc -mtraceback=none  -mcall-aixdesc
 | 
			
		||||
else
 | 
			
		||||
CFLAGS		+= -ffixed-r2 -mmultiple
 | 
			
		||||
endif
 | 
			
		||||
CPP		= $(CC) -E $(CFLAGS)
 | 
			
		||||
# Temporary hack until we have migrated to asm-powerpc
 | 
			
		||||
LINUXINCLUDE    += -Iarch/$(ARCH)/include
 | 
			
		||||
 | 
			
		||||
CHECKFLAGS	+= -m$(SZ) -D__powerpc__ -D__powerpc$(SZ)__
 | 
			
		||||
 | 
			
		||||
ifeq ($(CONFIG_PPC64),y)
 | 
			
		||||
GCC_VERSION     := $(call cc-version)
 | 
			
		||||
GCC_BROKEN_VEC	:= $(shell if [ $(GCC_VERSION) -lt 0400 ] ; then echo "y"; fi)
 | 
			
		||||
 | 
			
		||||
ifeq ($(CONFIG_POWER4_ONLY),y)
 | 
			
		||||
ifeq ($(CONFIG_ALTIVEC),y)
 | 
			
		||||
ifeq ($(GCC_BROKEN_VEC),y)
 | 
			
		||||
	CFLAGS += $(call cc-option,-mcpu=970)
 | 
			
		||||
else
 | 
			
		||||
	CFLAGS += $(call cc-option,-mcpu=power4)
 | 
			
		||||
endif
 | 
			
		||||
else
 | 
			
		||||
	CFLAGS += $(call cc-option,-mcpu=power4)
 | 
			
		||||
endif
 | 
			
		||||
else
 | 
			
		||||
	CFLAGS += $(call cc-option,-mtune=power4)
 | 
			
		||||
endif
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
# Enable unit-at-a-time mode when possible. It shrinks the
 | 
			
		||||
# kernel considerably.
 | 
			
		||||
CFLAGS += $(call cc-option,-funit-at-a-time)
 | 
			
		||||
 | 
			
		||||
ifndef CONFIG_FSL_BOOKE
 | 
			
		||||
CFLAGS		+= -mstring
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
cpu-as-$(CONFIG_PPC64BRIDGE)	+= -Wa,-mppc64bridge
 | 
			
		||||
cpu-as-$(CONFIG_4xx)		+= -Wa,-m405
 | 
			
		||||
cpu-as-$(CONFIG_6xx)		+= -Wa,-maltivec
 | 
			
		||||
cpu-as-$(CONFIG_POWER4)		+= -Wa,-maltivec
 | 
			
		||||
cpu-as-$(CONFIG_E500)		+= -Wa,-me500
 | 
			
		||||
cpu-as-$(CONFIG_E200)		+= -Wa,-me200
 | 
			
		||||
 | 
			
		||||
AFLAGS += $(cpu-as-y)
 | 
			
		||||
CFLAGS += $(cpu-as-y)
 | 
			
		||||
 | 
			
		||||
# Default to the common case.
 | 
			
		||||
KBUILD_DEFCONFIG := common_defconfig
 | 
			
		||||
 | 
			
		||||
head-y				:= arch/powerpc/kernel/head.o
 | 
			
		||||
head-$(CONFIG_PPC64)		:= arch/powerpc/kernel/head_64.o
 | 
			
		||||
head-$(CONFIG_8xx)		:= arch/powerpc/kernel/head_8xx.o
 | 
			
		||||
head-$(CONFIG_4xx)		:= arch/powerpc/kernel/head_4xx.o
 | 
			
		||||
head-$(CONFIG_44x)		:= arch/powerpc/kernel/head_44x.o
 | 
			
		||||
head-$(CONFIG_FSL_BOOKE)	:= arch/powerpc/kernel/head_fsl_booke.o
 | 
			
		||||
 | 
			
		||||
ifeq ($(CONFIG_PPC32),y)
 | 
			
		||||
head-$(CONFIG_6xx)		+= arch/powerpc/kernel/idle_6xx.o
 | 
			
		||||
head-$(CONFIG_POWER4)		+= arch/powerpc/kernel/idle_power4.o
 | 
			
		||||
head-$(CONFIG_PPC_FPU)		+= arch/powerpc/kernel/fpu.o
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
core-y				+= arch/powerpc/kernel/ \
 | 
			
		||||
				   arch/powerpc/mm/ \
 | 
			
		||||
				   arch/powerpc/lib/ \
 | 
			
		||||
				   arch/powerpc/sysdev/
 | 
			
		||||
core-$(CONFIG_PPC32)		+= arch/ppc/kernel/ \
 | 
			
		||||
				   arch/ppc/syslib/
 | 
			
		||||
core-$(CONFIG_PPC64)		+= arch/ppc64/kernel/
 | 
			
		||||
core-$(CONFIG_PPC_PMAC)		+= arch/powerpc/platforms/powermac/
 | 
			
		||||
core-$(CONFIG_4xx)		+= arch/ppc/platforms/4xx/
 | 
			
		||||
core-$(CONFIG_83xx)		+= arch/ppc/platforms/83xx/
 | 
			
		||||
core-$(CONFIG_85xx)		+= arch/ppc/platforms/85xx/
 | 
			
		||||
core-$(CONFIG_MATH_EMULATION)	+= arch/ppc/math-emu/
 | 
			
		||||
core-$(CONFIG_XMON)		+= arch/powerpc/xmon/
 | 
			
		||||
core-$(CONFIG_APUS)		+= arch/ppc/amiga/
 | 
			
		||||
drivers-$(CONFIG_8xx)		+= arch/ppc/8xx_io/
 | 
			
		||||
drivers-$(CONFIG_4xx)		+= arch/ppc/4xx_io/
 | 
			
		||||
drivers-$(CONFIG_CPM2)		+= arch/ppc/8260_io/
 | 
			
		||||
 | 
			
		||||
drivers-$(CONFIG_OPROFILE)	+= arch/powerpc/oprofile/
 | 
			
		||||
 | 
			
		||||
BOOT_TARGETS = zImage zImage.initrd znetboot znetboot.initrd vmlinux.sm
 | 
			
		||||
 | 
			
		||||
.PHONY: $(BOOT_TARGETS)
 | 
			
		||||
 | 
			
		||||
all: uImage zImage
 | 
			
		||||
 | 
			
		||||
CPPFLAGS_vmlinux.lds	:= -Upowerpc
 | 
			
		||||
 | 
			
		||||
# All the instructions talk about "make bzImage".
 | 
			
		||||
bzImage: zImage
 | 
			
		||||
 | 
			
		||||
boot := arch/$(ARCH)/boot
 | 
			
		||||
 | 
			
		||||
$(BOOT_TARGETS): vmlinux
 | 
			
		||||
	$(Q)$(MAKE) $(build)=$(boot) $@
 | 
			
		||||
 | 
			
		||||
uImage: vmlinux
 | 
			
		||||
	$(Q)$(MAKE) $(build)=$(boot)/images $(boot)/images/$@
 | 
			
		||||
 | 
			
		||||
define archhelp
 | 
			
		||||
  @echo '* zImage          - Compressed kernel image (arch/$(ARCH)/boot/images/zImage.*)'
 | 
			
		||||
  @echo '  uImage          - Create a bootable image for U-Boot / PPCBoot'
 | 
			
		||||
  @echo '  install         - Install kernel using'
 | 
			
		||||
  @echo '                    (your) ~/bin/installkernel or'
 | 
			
		||||
  @echo '                    (distribution) /sbin/installkernel or'
 | 
			
		||||
  @echo '                    install to $$(INSTALL_PATH) and run lilo'
 | 
			
		||||
  @echo '  *_defconfig     - Select default config from arch/$(ARCH)/ppc/configs'
 | 
			
		||||
endef
 | 
			
		||||
 | 
			
		||||
archclean:
 | 
			
		||||
	$(Q)$(MAKE) $(clean)=arch/ppc/boot
 | 
			
		||||
	# Temporary hack until we have migrated to asm-powerpc
 | 
			
		||||
	$(Q)rm -rf arch/$(ARCH)/include
 | 
			
		||||
 | 
			
		||||
archprepare: checkbin
 | 
			
		||||
 | 
			
		||||
# Temporary hack until we have migrated to asm-powerpc
 | 
			
		||||
ifeq ($(CONFIG_PPC64),y)
 | 
			
		||||
include/asm: arch/$(ARCH)/include/asm
 | 
			
		||||
arch/$(ARCH)/include/asm:
 | 
			
		||||
	$(Q)if [ ! -d arch/$(ARCH)/include ]; then mkdir -p arch/$(ARCH)/include; fi
 | 
			
		||||
	$(Q)ln -fsn $(srctree)/include/asm-ppc64 arch/$(ARCH)/include/asm
 | 
			
		||||
else
 | 
			
		||||
include/asm: arch/$(ARCH)/include/asm
 | 
			
		||||
arch/$(ARCH)/include/asm:
 | 
			
		||||
	$(Q)if [ ! -d arch/$(ARCH)/include ]; then mkdir -p arch/$(ARCH)/include; fi
 | 
			
		||||
	$(Q)ln -fsn $(srctree)/include/asm-ppc arch/$(ARCH)/include/asm
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
# Use the file '.tmp_gas_check' for binutils tests, as gas won't output
 | 
			
		||||
# to stdout and these checks are run even on install targets.
 | 
			
		||||
TOUT	:= .tmp_gas_check
 | 
			
		||||
# Ensure this is binutils 2.12.1 (or 2.12.90.0.7) or later for altivec
 | 
			
		||||
# instructions.
 | 
			
		||||
# gcc-3.4 and binutils-2.14 are a fatal combination.
 | 
			
		||||
GCC_VERSION	:= $(call cc-version)
 | 
			
		||||
 | 
			
		||||
checkbin:
 | 
			
		||||
	@if test "$(GCC_VERSION)" = "0304" ; then \
 | 
			
		||||
		if ! /bin/echo mftb 5 | $(AS) -v -mppc -many -o $(TOUT) >/dev/null 2>&1 ; then \
 | 
			
		||||
			echo -n '*** ${VERSION}.${PATCHLEVEL} kernels no longer build '; \
 | 
			
		||||
			echo 'correctly with gcc-3.4 and your version of binutils.'; \
 | 
			
		||||
			echo '*** Please upgrade your binutils or downgrade your gcc'; \
 | 
			
		||||
			false; \
 | 
			
		||||
		fi ; \
 | 
			
		||||
	fi
 | 
			
		||||
	@if ! /bin/echo dssall | $(AS) -many -o $(TOUT) >/dev/null 2>&1 ; then \
 | 
			
		||||
		echo -n '*** ${VERSION}.${PATCHLEVEL} kernels no longer build ' ; \
 | 
			
		||||
		echo 'correctly with old versions of binutils.' ; \
 | 
			
		||||
		echo '*** Please upgrade your binutils to 2.12.1 or newer' ; \
 | 
			
		||||
		false ; \
 | 
			
		||||
	fi
 | 
			
		||||
 | 
			
		||||
CLEAN_FILES += $(TOUT)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										18
									
								
								arch/powerpc/kernel/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								arch/powerpc/kernel/Makefile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
#
 | 
			
		||||
# Makefile for the linux kernel.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
extra-$(CONFIG_PPC_STD_MMU)	:= head.o
 | 
			
		||||
extra_$(CONFIG_PPC64)		:= head_64.o
 | 
			
		||||
extra-$(CONFIG_40x)		:= head_4xx.o
 | 
			
		||||
extra-$(CONFIG_44x)		:= head_44x.o
 | 
			
		||||
extra-$(CONFIG_FSL_BOOKE)	:= head_fsl_booke.o
 | 
			
		||||
extra-$(CONFIG_8xx)		:= head_8xx.o
 | 
			
		||||
extra-$(CONFIG_6xx)		+= idle_6xx.o
 | 
			
		||||
extra-$(CONFIG_POWER4)		+= idle_power4.o
 | 
			
		||||
extra-$(CONFIG_PPC_FPU)		+= fpu.o
 | 
			
		||||
extra-y				+= vmlinux.lds
 | 
			
		||||
 | 
			
		||||
obj-y				:= semaphore.o traps.o process.o
 | 
			
		||||
 | 
			
		||||
obj-$(CONFIG_ALTIVEC)		+= vecemu.o vector.o
 | 
			
		||||
							
								
								
									
										262
									
								
								arch/powerpc/kernel/asm-offsets.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										262
									
								
								arch/powerpc/kernel/asm-offsets.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,262 @@
 | 
			
		||||
/*
 | 
			
		||||
 * This program is used to generate definitions needed by
 | 
			
		||||
 * assembly language modules.
 | 
			
		||||
 *
 | 
			
		||||
 * We use the technique used in the OSF Mach kernel code:
 | 
			
		||||
 * generate asm statements containing #defines,
 | 
			
		||||
 * compile this file to assembler, and then extract the
 | 
			
		||||
 * #defines from the assembly-language output.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version
 | 
			
		||||
 * 2 of the License, or (at your option) any later version.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/config.h>
 | 
			
		||||
#include <linux/signal.h>
 | 
			
		||||
#include <linux/sched.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/errno.h>
 | 
			
		||||
#include <linux/string.h>
 | 
			
		||||
#include <linux/types.h>
 | 
			
		||||
#include <linux/ptrace.h>
 | 
			
		||||
#include <linux/suspend.h>
 | 
			
		||||
#include <linux/mman.h>
 | 
			
		||||
#include <linux/mm.h>
 | 
			
		||||
#include <linux/time.h>
 | 
			
		||||
#include <linux/hardirq.h>
 | 
			
		||||
#include <asm/io.h>
 | 
			
		||||
#include <asm/page.h>
 | 
			
		||||
#include <asm/pgtable.h>
 | 
			
		||||
#include <asm/processor.h>
 | 
			
		||||
 | 
			
		||||
#include <asm/cputable.h>
 | 
			
		||||
#include <asm/thread_info.h>
 | 
			
		||||
#ifdef CONFIG_PPC64
 | 
			
		||||
#include <asm/paca.h>
 | 
			
		||||
#include <asm/lppaca.h>
 | 
			
		||||
#include <asm/iSeries/HvLpEvent.h>
 | 
			
		||||
#include <asm/rtas.h>
 | 
			
		||||
#include <asm/cache.h>
 | 
			
		||||
#include <asm/systemcfg.h>
 | 
			
		||||
#include <asm/compat.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define DEFINE(sym, val) \
 | 
			
		||||
	asm volatile("\n->" #sym " %0 " #val : : "i" (val))
 | 
			
		||||
 | 
			
		||||
#define BLANK() asm volatile("\n->" : : )
 | 
			
		||||
 | 
			
		||||
int main(void)
 | 
			
		||||
{
 | 
			
		||||
	/* thread struct on stack */
 | 
			
		||||
	DEFINE(TI_FLAGS, offsetof(struct thread_info, flags));
 | 
			
		||||
	DEFINE(TI_PREEMPT, offsetof(struct thread_info, preempt_count));
 | 
			
		||||
	DEFINE(TI_CPU, offsetof(struct thread_info, cpu));
 | 
			
		||||
#ifdef CONFIG_PPC32
 | 
			
		||||
	DEFINE(TI_LOCAL_FLAGS, offsetof(struct thread_info, local_flags));
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef CONFIG_PPC64
 | 
			
		||||
	DEFINE(TI_SC_NOERR, offsetof(struct thread_info, syscall_noerror));
 | 
			
		||||
	DEFINE(THREAD_SHIFT, THREAD_SHIFT);
 | 
			
		||||
#endif
 | 
			
		||||
	DEFINE(THREAD_SIZE, THREAD_SIZE);
 | 
			
		||||
 | 
			
		||||
	/* task_struct->thread */
 | 
			
		||||
	DEFINE(THREAD, offsetof(struct task_struct, thread));
 | 
			
		||||
	DEFINE(THREAD_INFO, offsetof(struct task_struct, thread_info));
 | 
			
		||||
	DEFINE(MM, offsetof(struct task_struct, mm));
 | 
			
		||||
	DEFINE(PTRACE, offsetof(struct task_struct, ptrace));
 | 
			
		||||
	DEFINE(KSP, offsetof(struct thread_struct, ksp));
 | 
			
		||||
	DEFINE(PGDIR, offsetof(struct thread_struct, pgdir));
 | 
			
		||||
	DEFINE(LAST_SYSCALL, offsetof(struct thread_struct, last_syscall));
 | 
			
		||||
	DEFINE(PT_REGS, offsetof(struct thread_struct, regs));
 | 
			
		||||
	DEFINE(THREAD_FPEXC_MODE, offsetof(struct thread_struct, fpexc_mode));
 | 
			
		||||
	DEFINE(THREAD_FPR0, offsetof(struct thread_struct, fpr[0]));
 | 
			
		||||
	DEFINE(THREAD_FPSCR, offsetof(struct thread_struct, fpscr));
 | 
			
		||||
#if defined(CONFIG_4xx) || defined(CONFIG_BOOKE)
 | 
			
		||||
	DEFINE(THREAD_DBCR0, offsetof(struct thread_struct, dbcr0));
 | 
			
		||||
	DEFINE(PT_PTRACED, PT_PTRACED);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef CONFIG_PPC64
 | 
			
		||||
	DEFINE(KSP_VSID, offsetof(struct thread_struct, ksp_vsid));
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_ALTIVEC
 | 
			
		||||
	DEFINE(THREAD_VR0, offsetof(struct thread_struct, vr[0]));
 | 
			
		||||
	DEFINE(THREAD_VRSAVE, offsetof(struct thread_struct, vrsave));
 | 
			
		||||
	DEFINE(THREAD_VSCR, offsetof(struct thread_struct, vscr));
 | 
			
		||||
	DEFINE(THREAD_USED_VR, offsetof(struct thread_struct, used_vr));
 | 
			
		||||
#endif /* CONFIG_ALTIVEC */
 | 
			
		||||
#ifdef CONFIG_SPE
 | 
			
		||||
	DEFINE(THREAD_EVR0, offsetof(struct thread_struct, evr[0]));
 | 
			
		||||
	DEFINE(THREAD_ACC, offsetof(struct thread_struct, acc));
 | 
			
		||||
	DEFINE(THREAD_SPEFSCR, offsetof(struct thread_struct, spefscr));
 | 
			
		||||
	DEFINE(THREAD_USED_SPE, offsetof(struct thread_struct, used_spe));
 | 
			
		||||
#endif /* CONFIG_SPE */
 | 
			
		||||
	/* Interrupt register frame */
 | 
			
		||||
	DEFINE(STACK_FRAME_OVERHEAD, STACK_FRAME_OVERHEAD);
 | 
			
		||||
#ifndef CONFIG_PPC64
 | 
			
		||||
	DEFINE(INT_FRAME_SIZE, STACK_FRAME_OVERHEAD + sizeof(struct pt_regs));
 | 
			
		||||
#else
 | 
			
		||||
	DEFINE(SWITCH_FRAME_SIZE, STACK_FRAME_OVERHEAD + sizeof(struct pt_regs));
 | 
			
		||||
 | 
			
		||||
	/* 288 = # of volatile regs, int & fp, for leaf routines */
 | 
			
		||||
	/* which do not stack a frame.  See the PPC64 ABI.       */
 | 
			
		||||
	DEFINE(INT_FRAME_SIZE, STACK_FRAME_OVERHEAD + sizeof(struct pt_regs) + 288);
 | 
			
		||||
#endif
 | 
			
		||||
	/* in fact we only use gpr0 - gpr9 and gpr20 - gpr23 */
 | 
			
		||||
	DEFINE(GPR0, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[0]));
 | 
			
		||||
	DEFINE(GPR1, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[1]));
 | 
			
		||||
	DEFINE(GPR2, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[2]));
 | 
			
		||||
	DEFINE(GPR3, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[3]));
 | 
			
		||||
	DEFINE(GPR4, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[4]));
 | 
			
		||||
	DEFINE(GPR5, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[5]));
 | 
			
		||||
	DEFINE(GPR6, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[6]));
 | 
			
		||||
	DEFINE(GPR7, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[7]));
 | 
			
		||||
	DEFINE(GPR8, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[8]));
 | 
			
		||||
	DEFINE(GPR9, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[9]));
 | 
			
		||||
	DEFINE(GPR10, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[10]));
 | 
			
		||||
	DEFINE(GPR11, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[11]));
 | 
			
		||||
	DEFINE(GPR12, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[12]));
 | 
			
		||||
	DEFINE(GPR13, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[13]));
 | 
			
		||||
	DEFINE(GPR14, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[14]));
 | 
			
		||||
	DEFINE(GPR15, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[15]));
 | 
			
		||||
	DEFINE(GPR16, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[16]));
 | 
			
		||||
	DEFINE(GPR17, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[17]));
 | 
			
		||||
	DEFINE(GPR18, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[18]));
 | 
			
		||||
	DEFINE(GPR19, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[19]));
 | 
			
		||||
	DEFINE(GPR20, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[20]));
 | 
			
		||||
	DEFINE(GPR21, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[21]));
 | 
			
		||||
	DEFINE(GPR22, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[22]));
 | 
			
		||||
	DEFINE(GPR23, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[23]));
 | 
			
		||||
	DEFINE(GPR24, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[24]));
 | 
			
		||||
	DEFINE(GPR25, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[25]));
 | 
			
		||||
	DEFINE(GPR26, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[26]));
 | 
			
		||||
	DEFINE(GPR27, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[27]));
 | 
			
		||||
	DEFINE(GPR28, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[28]));
 | 
			
		||||
	DEFINE(GPR29, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[29]));
 | 
			
		||||
	DEFINE(GPR30, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[30]));
 | 
			
		||||
	DEFINE(GPR31, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[31]));
 | 
			
		||||
	/*
 | 
			
		||||
	 * Note: these symbols include _ because they overlap with special
 | 
			
		||||
	 * register names
 | 
			
		||||
	 */
 | 
			
		||||
	DEFINE(_NIP, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, nip));
 | 
			
		||||
	DEFINE(_MSR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, msr));
 | 
			
		||||
	DEFINE(_CTR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, ctr));
 | 
			
		||||
	DEFINE(_LINK, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, link));
 | 
			
		||||
	DEFINE(_CCR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, ccr));
 | 
			
		||||
	DEFINE(_MQ, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, mq));
 | 
			
		||||
	DEFINE(_XER, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, xer));
 | 
			
		||||
	DEFINE(_DAR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, dar));
 | 
			
		||||
	DEFINE(_DSISR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, dsisr));
 | 
			
		||||
	/* The PowerPC 400-class & Book-E processors have neither the DAR nor the DSISR
 | 
			
		||||
	 * SPRs. Hence, we overload them to hold the similar DEAR and ESR SPRs
 | 
			
		||||
	 * for such processors.  For critical interrupts we use them to
 | 
			
		||||
	 * hold SRR0 and SRR1.
 | 
			
		||||
	 */
 | 
			
		||||
	DEFINE(_DEAR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, dar));
 | 
			
		||||
	DEFINE(_ESR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, dsisr));
 | 
			
		||||
	DEFINE(ORIG_GPR3, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, orig_gpr3));
 | 
			
		||||
	DEFINE(RESULT, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, result));
 | 
			
		||||
	DEFINE(TRAP, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, trap));
 | 
			
		||||
	DEFINE(CLONE_VM, CLONE_VM);
 | 
			
		||||
	DEFINE(CLONE_UNTRACED, CLONE_UNTRACED);
 | 
			
		||||
	DEFINE(MM_PGD, offsetof(struct mm_struct, pgd));
 | 
			
		||||
 | 
			
		||||
	/* About the CPU features table */
 | 
			
		||||
	DEFINE(CPU_SPEC_ENTRY_SIZE, sizeof(struct cpu_spec));
 | 
			
		||||
	DEFINE(CPU_SPEC_PVR_MASK, offsetof(struct cpu_spec, pvr_mask));
 | 
			
		||||
	DEFINE(CPU_SPEC_PVR_VALUE, offsetof(struct cpu_spec, pvr_value));
 | 
			
		||||
	DEFINE(CPU_SPEC_FEATURES, offsetof(struct cpu_spec, cpu_features));
 | 
			
		||||
	DEFINE(CPU_SPEC_SETUP, offsetof(struct cpu_spec, cpu_setup));
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_PPC64
 | 
			
		||||
	DEFINE(MM, offsetof(struct task_struct, mm));
 | 
			
		||||
	DEFINE(AUDITCONTEXT, offsetof(struct task_struct, audit_context));
 | 
			
		||||
 | 
			
		||||
	DEFINE(DCACHEL1LINESIZE, offsetof(struct ppc64_caches, dline_size));
 | 
			
		||||
	DEFINE(DCACHEL1LOGLINESIZE, offsetof(struct ppc64_caches, log_dline_size));
 | 
			
		||||
	DEFINE(DCACHEL1LINESPERPAGE, offsetof(struct ppc64_caches, dlines_per_page));
 | 
			
		||||
	DEFINE(ICACHEL1LINESIZE, offsetof(struct ppc64_caches, iline_size));
 | 
			
		||||
	DEFINE(ICACHEL1LOGLINESIZE, offsetof(struct ppc64_caches, log_iline_size));
 | 
			
		||||
	DEFINE(ICACHEL1LINESPERPAGE, offsetof(struct ppc64_caches, ilines_per_page));
 | 
			
		||||
	DEFINE(PLATFORM, offsetof(struct systemcfg, platform));
 | 
			
		||||
 | 
			
		||||
	/* paca */
 | 
			
		||||
        DEFINE(PACA_SIZE, sizeof(struct paca_struct));
 | 
			
		||||
        DEFINE(PACAPACAINDEX, offsetof(struct paca_struct, paca_index));
 | 
			
		||||
        DEFINE(PACAPROCSTART, offsetof(struct paca_struct, cpu_start));
 | 
			
		||||
        DEFINE(PACAKSAVE, offsetof(struct paca_struct, kstack));
 | 
			
		||||
	DEFINE(PACACURRENT, offsetof(struct paca_struct, __current));
 | 
			
		||||
        DEFINE(PACASAVEDMSR, offsetof(struct paca_struct, saved_msr));
 | 
			
		||||
        DEFINE(PACASTABREAL, offsetof(struct paca_struct, stab_real));
 | 
			
		||||
        DEFINE(PACASTABVIRT, offsetof(struct paca_struct, stab_addr));
 | 
			
		||||
	DEFINE(PACASTABRR, offsetof(struct paca_struct, stab_rr));
 | 
			
		||||
        DEFINE(PACAR1, offsetof(struct paca_struct, saved_r1));
 | 
			
		||||
	DEFINE(PACATOC, offsetof(struct paca_struct, kernel_toc));
 | 
			
		||||
	DEFINE(PACAPROCENABLED, offsetof(struct paca_struct, proc_enabled));
 | 
			
		||||
	DEFINE(PACASLBCACHE, offsetof(struct paca_struct, slb_cache));
 | 
			
		||||
	DEFINE(PACASLBCACHEPTR, offsetof(struct paca_struct, slb_cache_ptr));
 | 
			
		||||
	DEFINE(PACACONTEXTID, offsetof(struct paca_struct, context.id));
 | 
			
		||||
#ifdef CONFIG_HUGETLB_PAGE
 | 
			
		||||
	DEFINE(PACALOWHTLBAREAS, offsetof(struct paca_struct, context.low_htlb_areas));
 | 
			
		||||
	DEFINE(PACAHIGHHTLBAREAS, offsetof(struct paca_struct, context.high_htlb_areas));
 | 
			
		||||
#endif /* CONFIG_HUGETLB_PAGE */
 | 
			
		||||
	DEFINE(PACADEFAULTDECR, offsetof(struct paca_struct, default_decr));
 | 
			
		||||
        DEFINE(PACA_EXGEN, offsetof(struct paca_struct, exgen));
 | 
			
		||||
        DEFINE(PACA_EXMC, offsetof(struct paca_struct, exmc));
 | 
			
		||||
        DEFINE(PACA_EXSLB, offsetof(struct paca_struct, exslb));
 | 
			
		||||
        DEFINE(PACA_EXDSI, offsetof(struct paca_struct, exdsi));
 | 
			
		||||
        DEFINE(PACAEMERGSP, offsetof(struct paca_struct, emergency_sp));
 | 
			
		||||
	DEFINE(PACALPPACA, offsetof(struct paca_struct, lppaca));
 | 
			
		||||
	DEFINE(PACAHWCPUID, offsetof(struct paca_struct, hw_cpu_id));
 | 
			
		||||
	DEFINE(LPPACASRR0, offsetof(struct lppaca, saved_srr0));
 | 
			
		||||
	DEFINE(LPPACASRR1, offsetof(struct lppaca, saved_srr1));
 | 
			
		||||
	DEFINE(LPPACAANYINT, offsetof(struct lppaca, int_dword.any_int));
 | 
			
		||||
	DEFINE(LPPACADECRINT, offsetof(struct lppaca, int_dword.fields.decr_int));
 | 
			
		||||
 | 
			
		||||
	/* RTAS */
 | 
			
		||||
	DEFINE(RTASBASE, offsetof(struct rtas_t, base));
 | 
			
		||||
	DEFINE(RTASENTRY, offsetof(struct rtas_t, entry));
 | 
			
		||||
 | 
			
		||||
	DEFINE(_TRAP, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, trap));
 | 
			
		||||
	DEFINE(SOFTE, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, softe));
 | 
			
		||||
 | 
			
		||||
	/* Create extra stack space for SRR0 and SRR1 when calling prom/rtas. */
 | 
			
		||||
	DEFINE(PROM_FRAME_SIZE, STACK_FRAME_OVERHEAD + sizeof(struct pt_regs) + 16);
 | 
			
		||||
	DEFINE(RTAS_FRAME_SIZE, STACK_FRAME_OVERHEAD + sizeof(struct pt_regs) + 16);
 | 
			
		||||
 | 
			
		||||
	/* These _only_ to be used with {PROM,RTAS}_FRAME_SIZE!!! */
 | 
			
		||||
	DEFINE(_SRR0, STACK_FRAME_OVERHEAD+sizeof(struct pt_regs));
 | 
			
		||||
	DEFINE(_SRR1, STACK_FRAME_OVERHEAD+sizeof(struct pt_regs)+8);
 | 
			
		||||
 | 
			
		||||
	/* systemcfg offsets for use by vdso */
 | 
			
		||||
	DEFINE(CFG_TB_ORIG_STAMP, offsetof(struct systemcfg, tb_orig_stamp));
 | 
			
		||||
	DEFINE(CFG_TB_TICKS_PER_SEC, offsetof(struct systemcfg, tb_ticks_per_sec));
 | 
			
		||||
	DEFINE(CFG_TB_TO_XS, offsetof(struct systemcfg, tb_to_xs));
 | 
			
		||||
	DEFINE(CFG_STAMP_XSEC, offsetof(struct systemcfg, stamp_xsec));
 | 
			
		||||
	DEFINE(CFG_TB_UPDATE_COUNT, offsetof(struct systemcfg, tb_update_count));
 | 
			
		||||
	DEFINE(CFG_TZ_MINUTEWEST, offsetof(struct systemcfg, tz_minuteswest));
 | 
			
		||||
	DEFINE(CFG_TZ_DSTTIME, offsetof(struct systemcfg, tz_dsttime));
 | 
			
		||||
	DEFINE(CFG_SYSCALL_MAP32, offsetof(struct systemcfg, syscall_map_32));
 | 
			
		||||
	DEFINE(CFG_SYSCALL_MAP64, offsetof(struct systemcfg, syscall_map_64));
 | 
			
		||||
 | 
			
		||||
	/* timeval/timezone offsets for use by vdso */
 | 
			
		||||
	DEFINE(TVAL64_TV_SEC, offsetof(struct timeval, tv_sec));
 | 
			
		||||
	DEFINE(TVAL64_TV_USEC, offsetof(struct timeval, tv_usec));
 | 
			
		||||
	DEFINE(TVAL32_TV_SEC, offsetof(struct compat_timeval, tv_sec));
 | 
			
		||||
	DEFINE(TVAL32_TV_USEC, offsetof(struct compat_timeval, tv_usec));
 | 
			
		||||
	DEFINE(TZONE_TZ_MINWEST, offsetof(struct timezone, tz_minuteswest));
 | 
			
		||||
	DEFINE(TZONE_TZ_DSTTIME, offsetof(struct timezone, tz_dsttime));
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	DEFINE(pbe_address, offsetof(struct pbe, address));
 | 
			
		||||
	DEFINE(pbe_orig_address, offsetof(struct pbe, orig_address));
 | 
			
		||||
	DEFINE(pbe_next, offsetof(struct pbe, next));
 | 
			
		||||
 | 
			
		||||
	DEFINE(NUM_USER_SEGMENTS, TASK_SIZE>>28);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										133
									
								
								arch/powerpc/kernel/fpu.S
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								arch/powerpc/kernel/fpu.S
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,133 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  FPU support code, moved here from head.S so that it can be used
 | 
			
		||||
 *  by chips which use other head-whatever.S files.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or
 | 
			
		||||
 *  modify it under the terms of the GNU General Public License
 | 
			
		||||
 *  as published by the Free Software Foundation; either version
 | 
			
		||||
 *  2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/config.h>
 | 
			
		||||
#include <asm/processor.h>
 | 
			
		||||
#include <asm/page.h>
 | 
			
		||||
#include <asm/mmu.h>
 | 
			
		||||
#include <asm/pgtable.h>
 | 
			
		||||
#include <asm/cputable.h>
 | 
			
		||||
#include <asm/cache.h>
 | 
			
		||||
#include <asm/thread_info.h>
 | 
			
		||||
#include <asm/ppc_asm.h>
 | 
			
		||||
#include <asm/asm-offsets.h>
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * This task wants to use the FPU now.
 | 
			
		||||
 * On UP, disable FP for the task which had the FPU previously,
 | 
			
		||||
 * and save its floating-point registers in its thread_struct.
 | 
			
		||||
 * Load up this task's FP registers from its thread_struct,
 | 
			
		||||
 * enable the FPU for the current task and return to the task.
 | 
			
		||||
 */
 | 
			
		||||
	.globl	load_up_fpu
 | 
			
		||||
load_up_fpu:
 | 
			
		||||
	mfmsr	r5
 | 
			
		||||
	ori	r5,r5,MSR_FP
 | 
			
		||||
#ifdef CONFIG_PPC64BRIDGE
 | 
			
		||||
	clrldi	r5,r5,1			/* turn off 64-bit mode */
 | 
			
		||||
#endif /* CONFIG_PPC64BRIDGE */
 | 
			
		||||
	SYNC
 | 
			
		||||
	MTMSRD(r5)			/* enable use of fpu now */
 | 
			
		||||
	isync
 | 
			
		||||
/*
 | 
			
		||||
 * For SMP, we don't do lazy FPU switching because it just gets too
 | 
			
		||||
 * horrendously complex, especially when a task switches from one CPU
 | 
			
		||||
 * to another.  Instead we call giveup_fpu in switch_to.
 | 
			
		||||
 */
 | 
			
		||||
#ifndef CONFIG_SMP
 | 
			
		||||
	tophys(r6,0)			/* get __pa constant */
 | 
			
		||||
	addis	r3,r6,last_task_used_math@ha
 | 
			
		||||
	lwz	r4,last_task_used_math@l(r3)
 | 
			
		||||
	cmpwi	0,r4,0
 | 
			
		||||
	beq	1f
 | 
			
		||||
	add	r4,r4,r6
 | 
			
		||||
	addi	r4,r4,THREAD		/* want last_task_used_math->thread */
 | 
			
		||||
	SAVE_32FPRS(0, r4)
 | 
			
		||||
	mffs	fr0
 | 
			
		||||
	stfd	fr0,THREAD_FPSCR-4(r4)
 | 
			
		||||
	lwz	r5,PT_REGS(r4)
 | 
			
		||||
	add	r5,r5,r6
 | 
			
		||||
	lwz	r4,_MSR-STACK_FRAME_OVERHEAD(r5)
 | 
			
		||||
	li	r10,MSR_FP|MSR_FE0|MSR_FE1
 | 
			
		||||
	andc	r4,r4,r10		/* disable FP for previous task */
 | 
			
		||||
	stw	r4,_MSR-STACK_FRAME_OVERHEAD(r5)
 | 
			
		||||
1:
 | 
			
		||||
#endif /* CONFIG_SMP */
 | 
			
		||||
	/* enable use of FP after return */
 | 
			
		||||
	mfspr	r5,SPRN_SPRG3		/* current task's THREAD (phys) */
 | 
			
		||||
	lwz	r4,THREAD_FPEXC_MODE(r5)
 | 
			
		||||
	ori	r9,r9,MSR_FP		/* enable FP for current */
 | 
			
		||||
	or	r9,r9,r4
 | 
			
		||||
	lfd	fr0,THREAD_FPSCR-4(r5)
 | 
			
		||||
	mtfsf	0xff,fr0
 | 
			
		||||
	REST_32FPRS(0, r5)
 | 
			
		||||
#ifndef CONFIG_SMP
 | 
			
		||||
	subi	r4,r5,THREAD
 | 
			
		||||
	sub	r4,r4,r6
 | 
			
		||||
	stw	r4,last_task_used_math@l(r3)
 | 
			
		||||
#endif /* CONFIG_SMP */
 | 
			
		||||
	/* restore registers and return */
 | 
			
		||||
	/* we haven't used ctr or xer or lr */
 | 
			
		||||
	b	fast_exception_return
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * FP unavailable trap from kernel - print a message, but let
 | 
			
		||||
 * the task use FP in the kernel until it returns to user mode.
 | 
			
		||||
 */
 | 
			
		||||
 	.globl	KernelFP
 | 
			
		||||
KernelFP:
 | 
			
		||||
	lwz	r3,_MSR(r1)
 | 
			
		||||
	ori	r3,r3,MSR_FP
 | 
			
		||||
	stw	r3,_MSR(r1)		/* enable use of FP after return */
 | 
			
		||||
	lis	r3,86f@h
 | 
			
		||||
	ori	r3,r3,86f@l
 | 
			
		||||
	mr	r4,r2			/* current */
 | 
			
		||||
	lwz	r5,_NIP(r1)
 | 
			
		||||
	bl	printk
 | 
			
		||||
	b	ret_from_except
 | 
			
		||||
86:	.string	"floating point used in kernel (task=%p, pc=%x)\n"
 | 
			
		||||
	.align	4,0
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * giveup_fpu(tsk)
 | 
			
		||||
 * Disable FP for the task given as the argument,
 | 
			
		||||
 * and save the floating-point registers in its thread_struct.
 | 
			
		||||
 * Enables the FPU for use in the kernel on return.
 | 
			
		||||
 */
 | 
			
		||||
	.globl	giveup_fpu
 | 
			
		||||
giveup_fpu:
 | 
			
		||||
	mfmsr	r5
 | 
			
		||||
	ori	r5,r5,MSR_FP
 | 
			
		||||
	SYNC_601
 | 
			
		||||
	ISYNC_601
 | 
			
		||||
	MTMSRD(r5)			/* enable use of fpu now */
 | 
			
		||||
	SYNC_601
 | 
			
		||||
	isync
 | 
			
		||||
	cmpwi	0,r3,0
 | 
			
		||||
	beqlr-				/* if no previous owner, done */
 | 
			
		||||
	addi	r3,r3,THREAD	        /* want THREAD of task */
 | 
			
		||||
	lwz	r5,PT_REGS(r3)
 | 
			
		||||
	cmpwi	0,r5,0
 | 
			
		||||
	SAVE_32FPRS(0, r3)
 | 
			
		||||
	mffs	fr0
 | 
			
		||||
	stfd	fr0,THREAD_FPSCR-4(r3)
 | 
			
		||||
	beq	1f
 | 
			
		||||
	lwz	r4,_MSR-STACK_FRAME_OVERHEAD(r5)
 | 
			
		||||
	li	r3,MSR_FP|MSR_FE0|MSR_FE1
 | 
			
		||||
	andc	r4,r4,r3		/* disable FP for previous task */
 | 
			
		||||
	stw	r4,_MSR-STACK_FRAME_OVERHEAD(r5)
 | 
			
		||||
1:
 | 
			
		||||
#ifndef CONFIG_SMP
 | 
			
		||||
	li	r5,0
 | 
			
		||||
	lis	r4,last_task_used_math@ha
 | 
			
		||||
	stw	r5,last_task_used_math@l(r4)
 | 
			
		||||
#endif /* CONFIG_SMP */
 | 
			
		||||
	blr
 | 
			
		||||
							
								
								
									
										1545
									
								
								arch/powerpc/kernel/head.S
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1545
									
								
								arch/powerpc/kernel/head.S
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										778
									
								
								arch/powerpc/kernel/head_44x.S
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										778
									
								
								arch/powerpc/kernel/head_44x.S
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,778 @@
 | 
			
		||||
/*
 | 
			
		||||
 * arch/ppc/kernel/head_44x.S
 | 
			
		||||
 *
 | 
			
		||||
 * Kernel execution entry point code.
 | 
			
		||||
 *
 | 
			
		||||
 *    Copyright (c) 1995-1996 Gary Thomas <gdt@linuxppc.org>
 | 
			
		||||
 *      Initial PowerPC version.
 | 
			
		||||
 *    Copyright (c) 1996 Cort Dougan <cort@cs.nmt.edu>
 | 
			
		||||
 *      Rewritten for PReP
 | 
			
		||||
 *    Copyright (c) 1996 Paul Mackerras <paulus@cs.anu.edu.au>
 | 
			
		||||
 *      Low-level exception handers, MMU support, and rewrite.
 | 
			
		||||
 *    Copyright (c) 1997 Dan Malek <dmalek@jlc.net>
 | 
			
		||||
 *      PowerPC 8xx modifications.
 | 
			
		||||
 *    Copyright (c) 1998-1999 TiVo, Inc.
 | 
			
		||||
 *      PowerPC 403GCX modifications.
 | 
			
		||||
 *    Copyright (c) 1999 Grant Erickson <grant@lcse.umn.edu>
 | 
			
		||||
 *      PowerPC 403GCX/405GP modifications.
 | 
			
		||||
 *    Copyright 2000 MontaVista Software Inc.
 | 
			
		||||
 *	PPC405 modifications
 | 
			
		||||
 *      PowerPC 403GCX/405GP modifications.
 | 
			
		||||
 * 	Author: MontaVista Software, Inc.
 | 
			
		||||
 *         	frank_rowand@mvista.com or source@mvista.com
 | 
			
		||||
 * 	   	debbie_chu@mvista.com
 | 
			
		||||
 *    Copyright 2002-2005 MontaVista Software, Inc.
 | 
			
		||||
 *      PowerPC 44x support, Matt Porter <mporter@kernel.crashing.org>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute  it and/or modify it
 | 
			
		||||
 * under  the terms of  the GNU General  Public License as published by the
 | 
			
		||||
 * Free Software Foundation;  either version 2 of the  License, or (at your
 | 
			
		||||
 * option) any later version.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/config.h>
 | 
			
		||||
#include <asm/processor.h>
 | 
			
		||||
#include <asm/page.h>
 | 
			
		||||
#include <asm/mmu.h>
 | 
			
		||||
#include <asm/pgtable.h>
 | 
			
		||||
#include <asm/ibm4xx.h>
 | 
			
		||||
#include <asm/ibm44x.h>
 | 
			
		||||
#include <asm/cputable.h>
 | 
			
		||||
#include <asm/thread_info.h>
 | 
			
		||||
#include <asm/ppc_asm.h>
 | 
			
		||||
#include <asm/asm-offsets.h>
 | 
			
		||||
#include "head_booke.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* As with the other PowerPC ports, it is expected that when code
 | 
			
		||||
 * execution begins here, the following registers contain valid, yet
 | 
			
		||||
 * optional, information:
 | 
			
		||||
 *
 | 
			
		||||
 *   r3 - Board info structure pointer (DRAM, frequency, MAC address, etc.)
 | 
			
		||||
 *   r4 - Starting address of the init RAM disk
 | 
			
		||||
 *   r5 - Ending address of the init RAM disk
 | 
			
		||||
 *   r6 - Start of kernel command line string (e.g. "mem=128")
 | 
			
		||||
 *   r7 - End of kernel command line string
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
	.text
 | 
			
		||||
_GLOBAL(_stext)
 | 
			
		||||
_GLOBAL(_start)
 | 
			
		||||
	/*
 | 
			
		||||
	 * Reserve a word at a fixed location to store the address
 | 
			
		||||
	 * of abatron_pteptrs
 | 
			
		||||
	 */
 | 
			
		||||
	nop
 | 
			
		||||
/*
 | 
			
		||||
 * Save parameters we are passed
 | 
			
		||||
 */
 | 
			
		||||
	mr	r31,r3
 | 
			
		||||
	mr	r30,r4
 | 
			
		||||
	mr	r29,r5
 | 
			
		||||
	mr	r28,r6
 | 
			
		||||
	mr	r27,r7
 | 
			
		||||
	li	r24,0		/* CPU number */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Set up the initial MMU state
 | 
			
		||||
 *
 | 
			
		||||
 * We are still executing code at the virtual address
 | 
			
		||||
 * mappings set by the firmware for the base of RAM.
 | 
			
		||||
 *
 | 
			
		||||
 * We first invalidate all TLB entries but the one
 | 
			
		||||
 * we are running from.  We then load the KERNELBASE
 | 
			
		||||
 * mappings so we can begin to use kernel addresses
 | 
			
		||||
 * natively and so the interrupt vector locations are
 | 
			
		||||
 * permanently pinned (necessary since Book E
 | 
			
		||||
 * implementations always have translation enabled).
 | 
			
		||||
 *
 | 
			
		||||
 * TODO: Use the known TLB entry we are running from to
 | 
			
		||||
 *	 determine which physical region we are located
 | 
			
		||||
 *	 in.  This can be used to determine where in RAM
 | 
			
		||||
 *	 (on a shared CPU system) or PCI memory space
 | 
			
		||||
 *	 (on a DRAMless system) we are located.
 | 
			
		||||
 *       For now, we assume a perfect world which means
 | 
			
		||||
 *	 we are located at the base of DRAM (physical 0).
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Search TLB for entry that we are currently using.
 | 
			
		||||
 * Invalidate all entries but the one we are using.
 | 
			
		||||
 */
 | 
			
		||||
	/* Load our current PID->MMUCR TID and MSR IS->MMUCR STS */
 | 
			
		||||
	mfspr	r3,SPRN_PID			/* Get PID */
 | 
			
		||||
	mfmsr	r4				/* Get MSR */
 | 
			
		||||
	andi.	r4,r4,MSR_IS@l			/* TS=1? */
 | 
			
		||||
	beq	wmmucr				/* If not, leave STS=0 */
 | 
			
		||||
	oris	r3,r3,PPC44x_MMUCR_STS@h	/* Set STS=1 */
 | 
			
		||||
wmmucr:	mtspr	SPRN_MMUCR,r3			/* Put MMUCR */
 | 
			
		||||
	sync
 | 
			
		||||
 | 
			
		||||
	bl	invstr				/* Find our address */
 | 
			
		||||
invstr:	mflr	r5				/* Make it accessible */
 | 
			
		||||
	tlbsx	r23,0,r5			/* Find entry we are in */
 | 
			
		||||
	li	r4,0				/* Start at TLB entry 0 */
 | 
			
		||||
	li	r3,0				/* Set PAGEID inval value */
 | 
			
		||||
1:	cmpw	r23,r4				/* Is this our entry? */
 | 
			
		||||
	beq	skpinv				/* If so, skip the inval */
 | 
			
		||||
	tlbwe	r3,r4,PPC44x_TLB_PAGEID		/* If not, inval the entry */
 | 
			
		||||
skpinv:	addi	r4,r4,1				/* Increment */
 | 
			
		||||
	cmpwi	r4,64				/* Are we done? */
 | 
			
		||||
	bne	1b				/* If not, repeat */
 | 
			
		||||
	isync					/* If so, context change */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Configure and load pinned entry into TLB slot 63.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
	lis	r3,KERNELBASE@h		/* Load the kernel virtual address */
 | 
			
		||||
	ori	r3,r3,KERNELBASE@l
 | 
			
		||||
 | 
			
		||||
	/* Kernel is at the base of RAM */
 | 
			
		||||
	li r4, 0			/* Load the kernel physical address */
 | 
			
		||||
 | 
			
		||||
	/* Load the kernel PID = 0 */
 | 
			
		||||
	li	r0,0
 | 
			
		||||
	mtspr	SPRN_PID,r0
 | 
			
		||||
	sync
 | 
			
		||||
 | 
			
		||||
	/* Initialize MMUCR */
 | 
			
		||||
	li	r5,0
 | 
			
		||||
	mtspr	SPRN_MMUCR,r5
 | 
			
		||||
	sync
 | 
			
		||||
 | 
			
		||||
 	/* pageid fields */
 | 
			
		||||
	clrrwi	r3,r3,10		/* Mask off the effective page number */
 | 
			
		||||
	ori	r3,r3,PPC44x_TLB_VALID | PPC44x_TLB_256M
 | 
			
		||||
 | 
			
		||||
	/* xlat fields */
 | 
			
		||||
	clrrwi	r4,r4,10		/* Mask off the real page number */
 | 
			
		||||
					/* ERPN is 0 for first 4GB page */
 | 
			
		||||
 | 
			
		||||
	/* attrib fields */
 | 
			
		||||
	/* Added guarded bit to protect against speculative loads/stores */
 | 
			
		||||
	li	r5,0
 | 
			
		||||
	ori	r5,r5,(PPC44x_TLB_SW | PPC44x_TLB_SR | PPC44x_TLB_SX | PPC44x_TLB_G)
 | 
			
		||||
 | 
			
		||||
        li      r0,63                    /* TLB slot 63 */
 | 
			
		||||
 | 
			
		||||
	tlbwe	r3,r0,PPC44x_TLB_PAGEID	/* Load the pageid fields */
 | 
			
		||||
	tlbwe	r4,r0,PPC44x_TLB_XLAT	/* Load the translation fields */
 | 
			
		||||
	tlbwe	r5,r0,PPC44x_TLB_ATTRIB	/* Load the attrib/access fields */
 | 
			
		||||
 | 
			
		||||
	/* Force context change */
 | 
			
		||||
	mfmsr	r0
 | 
			
		||||
	mtspr	SPRN_SRR1, r0
 | 
			
		||||
	lis	r0,3f@h
 | 
			
		||||
	ori	r0,r0,3f@l
 | 
			
		||||
	mtspr	SPRN_SRR0,r0
 | 
			
		||||
	sync
 | 
			
		||||
	rfi
 | 
			
		||||
 | 
			
		||||
	/* If necessary, invalidate original entry we used */
 | 
			
		||||
3:	cmpwi	r23,63
 | 
			
		||||
	beq	4f
 | 
			
		||||
	li	r6,0
 | 
			
		||||
	tlbwe   r6,r23,PPC44x_TLB_PAGEID
 | 
			
		||||
	isync
 | 
			
		||||
 | 
			
		||||
4:
 | 
			
		||||
#ifdef CONFIG_SERIAL_TEXT_DEBUG
 | 
			
		||||
	/*
 | 
			
		||||
	 * Add temporary UART mapping for early debug.
 | 
			
		||||
	 * We can map UART registers wherever we want as long as they don't
 | 
			
		||||
	 * interfere with other system mappings (e.g. with pinned entries).
 | 
			
		||||
	 * For an example of how we handle this - see ocotea.h.       --ebs
 | 
			
		||||
	 */
 | 
			
		||||
 	/* pageid fields */
 | 
			
		||||
	lis	r3,UART0_IO_BASE@h
 | 
			
		||||
	ori	r3,r3,PPC44x_TLB_VALID | PPC44x_TLB_4K
 | 
			
		||||
 | 
			
		||||
	/* xlat fields */
 | 
			
		||||
	lis	r4,UART0_PHYS_IO_BASE@h		/* RPN depends on SoC */
 | 
			
		||||
#ifndef CONFIG_440EP
 | 
			
		||||
	ori	r4,r4,0x0001		/* ERPN is 1 for second 4GB page */
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	/* attrib fields */
 | 
			
		||||
	li	r5,0
 | 
			
		||||
	ori	r5,r5,(PPC44x_TLB_SW | PPC44x_TLB_SR | PPC44x_TLB_I | PPC44x_TLB_G)
 | 
			
		||||
 | 
			
		||||
        li      r0,0                    /* TLB slot 0 */
 | 
			
		||||
 | 
			
		||||
	tlbwe	r3,r0,PPC44x_TLB_PAGEID	/* Load the pageid fields */
 | 
			
		||||
	tlbwe	r4,r0,PPC44x_TLB_XLAT	/* Load the translation fields */
 | 
			
		||||
	tlbwe	r5,r0,PPC44x_TLB_ATTRIB	/* Load the attrib/access fields */
 | 
			
		||||
 | 
			
		||||
	/* Force context change */
 | 
			
		||||
	isync
 | 
			
		||||
#endif /* CONFIG_SERIAL_TEXT_DEBUG */
 | 
			
		||||
 | 
			
		||||
	/* Establish the interrupt vector offsets */
 | 
			
		||||
	SET_IVOR(0,  CriticalInput);
 | 
			
		||||
	SET_IVOR(1,  MachineCheck);
 | 
			
		||||
	SET_IVOR(2,  DataStorage);
 | 
			
		||||
	SET_IVOR(3,  InstructionStorage);
 | 
			
		||||
	SET_IVOR(4,  ExternalInput);
 | 
			
		||||
	SET_IVOR(5,  Alignment);
 | 
			
		||||
	SET_IVOR(6,  Program);
 | 
			
		||||
	SET_IVOR(7,  FloatingPointUnavailable);
 | 
			
		||||
	SET_IVOR(8,  SystemCall);
 | 
			
		||||
	SET_IVOR(9,  AuxillaryProcessorUnavailable);
 | 
			
		||||
	SET_IVOR(10, Decrementer);
 | 
			
		||||
	SET_IVOR(11, FixedIntervalTimer);
 | 
			
		||||
	SET_IVOR(12, WatchdogTimer);
 | 
			
		||||
	SET_IVOR(13, DataTLBError);
 | 
			
		||||
	SET_IVOR(14, InstructionTLBError);
 | 
			
		||||
	SET_IVOR(15, Debug);
 | 
			
		||||
 | 
			
		||||
	/* Establish the interrupt vector base */
 | 
			
		||||
	lis	r4,interrupt_base@h	/* IVPR only uses the high 16-bits */
 | 
			
		||||
	mtspr	SPRN_IVPR,r4
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_440EP
 | 
			
		||||
	/* Clear DAPUIB flag in CCR0 (enable APU between CPU and FPU) */
 | 
			
		||||
	mfspr	r2,SPRN_CCR0
 | 
			
		||||
	lis	r3,0xffef
 | 
			
		||||
	ori	r3,r3,0xffff
 | 
			
		||||
	and	r2,r2,r3
 | 
			
		||||
	mtspr	SPRN_CCR0,r2
 | 
			
		||||
	isync
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * This is where the main kernel code starts.
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
	/* ptr to current */
 | 
			
		||||
	lis	r2,init_task@h
 | 
			
		||||
	ori	r2,r2,init_task@l
 | 
			
		||||
 | 
			
		||||
	/* ptr to current thread */
 | 
			
		||||
	addi	r4,r2,THREAD	/* init task's THREAD */
 | 
			
		||||
	mtspr	SPRN_SPRG3,r4
 | 
			
		||||
 | 
			
		||||
	/* stack */
 | 
			
		||||
	lis	r1,init_thread_union@h
 | 
			
		||||
	ori	r1,r1,init_thread_union@l
 | 
			
		||||
	li	r0,0
 | 
			
		||||
	stwu	r0,THREAD_SIZE-STACK_FRAME_OVERHEAD(r1)
 | 
			
		||||
 | 
			
		||||
	bl	early_init
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Decide what sort of machine this is and initialize the MMU.
 | 
			
		||||
 */
 | 
			
		||||
	mr	r3,r31
 | 
			
		||||
	mr	r4,r30
 | 
			
		||||
	mr	r5,r29
 | 
			
		||||
	mr	r6,r28
 | 
			
		||||
	mr	r7,r27
 | 
			
		||||
	bl	machine_init
 | 
			
		||||
	bl	MMU_init
 | 
			
		||||
 | 
			
		||||
	/* Setup PTE pointers for the Abatron bdiGDB */
 | 
			
		||||
	lis	r6, swapper_pg_dir@h
 | 
			
		||||
	ori	r6, r6, swapper_pg_dir@l
 | 
			
		||||
	lis	r5, abatron_pteptrs@h
 | 
			
		||||
	ori	r5, r5, abatron_pteptrs@l
 | 
			
		||||
	lis	r4, KERNELBASE@h
 | 
			
		||||
	ori	r4, r4, KERNELBASE@l
 | 
			
		||||
	stw	r5, 0(r4)	/* Save abatron_pteptrs at a fixed location */
 | 
			
		||||
	stw	r6, 0(r5)
 | 
			
		||||
 | 
			
		||||
	/* Let's move on */
 | 
			
		||||
	lis	r4,start_kernel@h
 | 
			
		||||
	ori	r4,r4,start_kernel@l
 | 
			
		||||
	lis	r3,MSR_KERNEL@h
 | 
			
		||||
	ori	r3,r3,MSR_KERNEL@l
 | 
			
		||||
	mtspr	SPRN_SRR0,r4
 | 
			
		||||
	mtspr	SPRN_SRR1,r3
 | 
			
		||||
	rfi			/* change context and jump to start_kernel */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Interrupt vector entry code
 | 
			
		||||
 *
 | 
			
		||||
 * The Book E MMUs are always on so we don't need to handle
 | 
			
		||||
 * interrupts in real mode as with previous PPC processors. In
 | 
			
		||||
 * this case we handle interrupts in the kernel virtual address
 | 
			
		||||
 * space.
 | 
			
		||||
 *
 | 
			
		||||
 * Interrupt vectors are dynamically placed relative to the
 | 
			
		||||
 * interrupt prefix as determined by the address of interrupt_base.
 | 
			
		||||
 * The interrupt vectors offsets are programmed using the labels
 | 
			
		||||
 * for each interrupt vector entry.
 | 
			
		||||
 *
 | 
			
		||||
 * Interrupt vectors must be aligned on a 16 byte boundary.
 | 
			
		||||
 * We align on a 32 byte cache line boundary for good measure.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
interrupt_base:
 | 
			
		||||
	/* Critical Input Interrupt */
 | 
			
		||||
	CRITICAL_EXCEPTION(0x0100, CriticalInput, UnknownException)
 | 
			
		||||
 | 
			
		||||
	/* Machine Check Interrupt */
 | 
			
		||||
#ifdef CONFIG_440A
 | 
			
		||||
	MCHECK_EXCEPTION(0x0200, MachineCheck, MachineCheckException)
 | 
			
		||||
#else
 | 
			
		||||
	CRITICAL_EXCEPTION(0x0200, MachineCheck, MachineCheckException)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	/* Data Storage Interrupt */
 | 
			
		||||
	START_EXCEPTION(DataStorage)
 | 
			
		||||
	mtspr	SPRN_SPRG0, r10		/* Save some working registers */
 | 
			
		||||
	mtspr	SPRN_SPRG1, r11
 | 
			
		||||
	mtspr	SPRN_SPRG4W, r12
 | 
			
		||||
	mtspr	SPRN_SPRG5W, r13
 | 
			
		||||
	mfcr	r11
 | 
			
		||||
	mtspr	SPRN_SPRG7W, r11
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Check if it was a store fault, if not then bail
 | 
			
		||||
	 * because a user tried to access a kernel or
 | 
			
		||||
	 * read-protected page.  Otherwise, get the
 | 
			
		||||
	 * offending address and handle it.
 | 
			
		||||
	 */
 | 
			
		||||
	mfspr	r10, SPRN_ESR
 | 
			
		||||
	andis.	r10, r10, ESR_ST@h
 | 
			
		||||
	beq	2f
 | 
			
		||||
 | 
			
		||||
	mfspr	r10, SPRN_DEAR		/* Get faulting address */
 | 
			
		||||
 | 
			
		||||
	/* If we are faulting a kernel address, we have to use the
 | 
			
		||||
	 * kernel page tables.
 | 
			
		||||
	 */
 | 
			
		||||
	lis	r11, TASK_SIZE@h
 | 
			
		||||
	cmplw	r10, r11
 | 
			
		||||
	blt+	3f
 | 
			
		||||
	lis	r11, swapper_pg_dir@h
 | 
			
		||||
	ori	r11, r11, swapper_pg_dir@l
 | 
			
		||||
 | 
			
		||||
	mfspr   r12,SPRN_MMUCR
 | 
			
		||||
	rlwinm	r12,r12,0,0,23		/* Clear TID */
 | 
			
		||||
 | 
			
		||||
	b	4f
 | 
			
		||||
 | 
			
		||||
	/* Get the PGD for the current thread */
 | 
			
		||||
3:
 | 
			
		||||
	mfspr	r11,SPRN_SPRG3
 | 
			
		||||
	lwz	r11,PGDIR(r11)
 | 
			
		||||
 | 
			
		||||
	/* Load PID into MMUCR TID */
 | 
			
		||||
	mfspr	r12,SPRN_MMUCR		/* Get MMUCR */
 | 
			
		||||
	mfspr   r13,SPRN_PID		/* Get PID */
 | 
			
		||||
	rlwimi	r12,r13,0,24,31		/* Set TID */
 | 
			
		||||
 | 
			
		||||
4:
 | 
			
		||||
	mtspr   SPRN_MMUCR,r12
 | 
			
		||||
 | 
			
		||||
	rlwinm  r12, r10, 13, 19, 29    /* Compute pgdir/pmd offset */
 | 
			
		||||
	lwzx    r11, r12, r11           /* Get pgd/pmd entry */
 | 
			
		||||
	rlwinm. r12, r11, 0, 0, 20      /* Extract pt base address */
 | 
			
		||||
	beq     2f                      /* Bail if no table */
 | 
			
		||||
 | 
			
		||||
	rlwimi  r12, r10, 23, 20, 28    /* Compute pte address */
 | 
			
		||||
	lwz     r11, 4(r12)             /* Get pte entry */
 | 
			
		||||
 | 
			
		||||
	andi.	r13, r11, _PAGE_RW	/* Is it writeable? */
 | 
			
		||||
	beq	2f			/* Bail if not */
 | 
			
		||||
 | 
			
		||||
	/* Update 'changed'.
 | 
			
		||||
	*/
 | 
			
		||||
	ori	r11, r11, _PAGE_DIRTY|_PAGE_ACCESSED|_PAGE_HWWRITE
 | 
			
		||||
	stw	r11, 4(r12)		/* Update Linux page table */
 | 
			
		||||
 | 
			
		||||
	li	r13, PPC44x_TLB_SR@l	/* Set SR */
 | 
			
		||||
	rlwimi	r13, r11, 29, 29, 29	/* SX = _PAGE_HWEXEC */
 | 
			
		||||
	rlwimi	r13, r11, 0, 30, 30	/* SW = _PAGE_RW */
 | 
			
		||||
	rlwimi	r13, r11, 29, 28, 28	/* UR = _PAGE_USER */
 | 
			
		||||
	rlwimi	r12, r11, 31, 26, 26	/* (_PAGE_USER>>1)->r12 */
 | 
			
		||||
	rlwimi	r12, r11, 29, 30, 30	/* (_PAGE_USER>>3)->r12 */
 | 
			
		||||
	and	r12, r12, r11		/* HWEXEC/RW & USER */
 | 
			
		||||
	rlwimi	r13, r12, 0, 26, 26	/* UX = HWEXEC & USER */
 | 
			
		||||
	rlwimi	r13, r12, 3, 27, 27	/* UW = RW & USER */
 | 
			
		||||
 | 
			
		||||
	rlwimi	r11,r13,0,26,31		/* Insert static perms */
 | 
			
		||||
 | 
			
		||||
	rlwinm	r11,r11,0,20,15		/* Clear U0-U3 */
 | 
			
		||||
 | 
			
		||||
	/* find the TLB index that caused the fault.  It has to be here. */
 | 
			
		||||
	tlbsx	r10, 0, r10
 | 
			
		||||
 | 
			
		||||
	tlbwe	r11, r10, PPC44x_TLB_ATTRIB	/* Write ATTRIB */
 | 
			
		||||
 | 
			
		||||
	/* Done...restore registers and get out of here.
 | 
			
		||||
	*/
 | 
			
		||||
	mfspr	r11, SPRN_SPRG7R
 | 
			
		||||
	mtcr	r11
 | 
			
		||||
	mfspr	r13, SPRN_SPRG5R
 | 
			
		||||
	mfspr	r12, SPRN_SPRG4R
 | 
			
		||||
 | 
			
		||||
	mfspr	r11, SPRN_SPRG1
 | 
			
		||||
	mfspr	r10, SPRN_SPRG0
 | 
			
		||||
	rfi			/* Force context change */
 | 
			
		||||
 | 
			
		||||
2:
 | 
			
		||||
	/*
 | 
			
		||||
	 * The bailout.  Restore registers to pre-exception conditions
 | 
			
		||||
	 * and call the heavyweights to help us out.
 | 
			
		||||
	 */
 | 
			
		||||
	mfspr	r11, SPRN_SPRG7R
 | 
			
		||||
	mtcr	r11
 | 
			
		||||
	mfspr	r13, SPRN_SPRG5R
 | 
			
		||||
	mfspr	r12, SPRN_SPRG4R
 | 
			
		||||
 | 
			
		||||
	mfspr	r11, SPRN_SPRG1
 | 
			
		||||
	mfspr	r10, SPRN_SPRG0
 | 
			
		||||
	b	data_access
 | 
			
		||||
 | 
			
		||||
	/* Instruction Storage Interrupt */
 | 
			
		||||
	INSTRUCTION_STORAGE_EXCEPTION
 | 
			
		||||
 | 
			
		||||
	/* External Input Interrupt */
 | 
			
		||||
	EXCEPTION(0x0500, ExternalInput, do_IRQ, EXC_XFER_LITE)
 | 
			
		||||
 | 
			
		||||
	/* Alignment Interrupt */
 | 
			
		||||
	ALIGNMENT_EXCEPTION
 | 
			
		||||
 | 
			
		||||
	/* Program Interrupt */
 | 
			
		||||
	PROGRAM_EXCEPTION
 | 
			
		||||
 | 
			
		||||
	/* Floating Point Unavailable Interrupt */
 | 
			
		||||
#ifdef CONFIG_PPC_FPU
 | 
			
		||||
	FP_UNAVAILABLE_EXCEPTION
 | 
			
		||||
#else
 | 
			
		||||
	EXCEPTION(0x2010, FloatingPointUnavailable, UnknownException, EXC_XFER_EE)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	/* System Call Interrupt */
 | 
			
		||||
	START_EXCEPTION(SystemCall)
 | 
			
		||||
	NORMAL_EXCEPTION_PROLOG
 | 
			
		||||
	EXC_XFER_EE_LITE(0x0c00, DoSyscall)
 | 
			
		||||
 | 
			
		||||
	/* Auxillary Processor Unavailable Interrupt */
 | 
			
		||||
	EXCEPTION(0x2020, AuxillaryProcessorUnavailable, UnknownException, EXC_XFER_EE)
 | 
			
		||||
 | 
			
		||||
	/* Decrementer Interrupt */
 | 
			
		||||
	DECREMENTER_EXCEPTION
 | 
			
		||||
 | 
			
		||||
	/* Fixed Internal Timer Interrupt */
 | 
			
		||||
	/* TODO: Add FIT support */
 | 
			
		||||
	EXCEPTION(0x1010, FixedIntervalTimer, UnknownException, EXC_XFER_EE)
 | 
			
		||||
 | 
			
		||||
	/* Watchdog Timer Interrupt */
 | 
			
		||||
	/* TODO: Add watchdog support */
 | 
			
		||||
#ifdef CONFIG_BOOKE_WDT
 | 
			
		||||
	CRITICAL_EXCEPTION(0x1020, WatchdogTimer, WatchdogException)
 | 
			
		||||
#else
 | 
			
		||||
	CRITICAL_EXCEPTION(0x1020, WatchdogTimer, UnknownException)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	/* Data TLB Error Interrupt */
 | 
			
		||||
	START_EXCEPTION(DataTLBError)
 | 
			
		||||
	mtspr	SPRN_SPRG0, r10		/* Save some working registers */
 | 
			
		||||
	mtspr	SPRN_SPRG1, r11
 | 
			
		||||
	mtspr	SPRN_SPRG4W, r12
 | 
			
		||||
	mtspr	SPRN_SPRG5W, r13
 | 
			
		||||
	mfcr	r11
 | 
			
		||||
	mtspr	SPRN_SPRG7W, r11
 | 
			
		||||
	mfspr	r10, SPRN_DEAR		/* Get faulting address */
 | 
			
		||||
 | 
			
		||||
	/* If we are faulting a kernel address, we have to use the
 | 
			
		||||
	 * kernel page tables.
 | 
			
		||||
	 */
 | 
			
		||||
	lis	r11, TASK_SIZE@h
 | 
			
		||||
	cmplw	r10, r11
 | 
			
		||||
	blt+	3f
 | 
			
		||||
	lis	r11, swapper_pg_dir@h
 | 
			
		||||
	ori	r11, r11, swapper_pg_dir@l
 | 
			
		||||
 | 
			
		||||
	mfspr	r12,SPRN_MMUCR
 | 
			
		||||
	rlwinm	r12,r12,0,0,23		/* Clear TID */
 | 
			
		||||
 | 
			
		||||
	b	4f
 | 
			
		||||
 | 
			
		||||
	/* Get the PGD for the current thread */
 | 
			
		||||
3:
 | 
			
		||||
	mfspr	r11,SPRN_SPRG3
 | 
			
		||||
	lwz	r11,PGDIR(r11)
 | 
			
		||||
 | 
			
		||||
	/* Load PID into MMUCR TID */
 | 
			
		||||
	mfspr	r12,SPRN_MMUCR
 | 
			
		||||
	mfspr   r13,SPRN_PID		/* Get PID */
 | 
			
		||||
	rlwimi	r12,r13,0,24,31		/* Set TID */
 | 
			
		||||
 | 
			
		||||
4:
 | 
			
		||||
	mtspr	SPRN_MMUCR,r12
 | 
			
		||||
 | 
			
		||||
	rlwinm 	r12, r10, 13, 19, 29	/* Compute pgdir/pmd offset */
 | 
			
		||||
	lwzx	r11, r12, r11		/* Get pgd/pmd entry */
 | 
			
		||||
	rlwinm.	r12, r11, 0, 0, 20	/* Extract pt base address */
 | 
			
		||||
	beq	2f			/* Bail if no table */
 | 
			
		||||
 | 
			
		||||
	rlwimi	r12, r10, 23, 20, 28	/* Compute pte address */
 | 
			
		||||
	lwz	r11, 4(r12)		/* Get pte entry */
 | 
			
		||||
	andi.	r13, r11, _PAGE_PRESENT	/* Is the page present? */
 | 
			
		||||
	beq	2f			/* Bail if not present */
 | 
			
		||||
 | 
			
		||||
	ori	r11, r11, _PAGE_ACCESSED
 | 
			
		||||
	stw	r11, 4(r12)
 | 
			
		||||
 | 
			
		||||
	 /* Jump to common tlb load */
 | 
			
		||||
	b	finish_tlb_load
 | 
			
		||||
 | 
			
		||||
2:
 | 
			
		||||
	/* The bailout.  Restore registers to pre-exception conditions
 | 
			
		||||
	 * and call the heavyweights to help us out.
 | 
			
		||||
	 */
 | 
			
		||||
	mfspr	r11, SPRN_SPRG7R
 | 
			
		||||
	mtcr	r11
 | 
			
		||||
	mfspr	r13, SPRN_SPRG5R
 | 
			
		||||
	mfspr	r12, SPRN_SPRG4R
 | 
			
		||||
	mfspr	r11, SPRN_SPRG1
 | 
			
		||||
	mfspr	r10, SPRN_SPRG0
 | 
			
		||||
	b	data_access
 | 
			
		||||
 | 
			
		||||
	/* Instruction TLB Error Interrupt */
 | 
			
		||||
	/*
 | 
			
		||||
	 * Nearly the same as above, except we get our
 | 
			
		||||
	 * information from different registers and bailout
 | 
			
		||||
	 * to a different point.
 | 
			
		||||
	 */
 | 
			
		||||
	START_EXCEPTION(InstructionTLBError)
 | 
			
		||||
	mtspr	SPRN_SPRG0, r10		/* Save some working registers */
 | 
			
		||||
	mtspr	SPRN_SPRG1, r11
 | 
			
		||||
	mtspr	SPRN_SPRG4W, r12
 | 
			
		||||
	mtspr	SPRN_SPRG5W, r13
 | 
			
		||||
	mfcr	r11
 | 
			
		||||
	mtspr	SPRN_SPRG7W, r11
 | 
			
		||||
	mfspr	r10, SPRN_SRR0		/* Get faulting address */
 | 
			
		||||
 | 
			
		||||
	/* If we are faulting a kernel address, we have to use the
 | 
			
		||||
	 * kernel page tables.
 | 
			
		||||
	 */
 | 
			
		||||
	lis	r11, TASK_SIZE@h
 | 
			
		||||
	cmplw	r10, r11
 | 
			
		||||
	blt+	3f
 | 
			
		||||
	lis	r11, swapper_pg_dir@h
 | 
			
		||||
	ori	r11, r11, swapper_pg_dir@l
 | 
			
		||||
 | 
			
		||||
	mfspr	r12,SPRN_MMUCR
 | 
			
		||||
	rlwinm	r12,r12,0,0,23		/* Clear TID */
 | 
			
		||||
 | 
			
		||||
	b	4f
 | 
			
		||||
 | 
			
		||||
	/* Get the PGD for the current thread */
 | 
			
		||||
3:
 | 
			
		||||
	mfspr	r11,SPRN_SPRG3
 | 
			
		||||
	lwz	r11,PGDIR(r11)
 | 
			
		||||
 | 
			
		||||
	/* Load PID into MMUCR TID */
 | 
			
		||||
	mfspr	r12,SPRN_MMUCR
 | 
			
		||||
	mfspr   r13,SPRN_PID		/* Get PID */
 | 
			
		||||
	rlwimi	r12,r13,0,24,31		/* Set TID */
 | 
			
		||||
 | 
			
		||||
4:
 | 
			
		||||
	mtspr	SPRN_MMUCR,r12
 | 
			
		||||
 | 
			
		||||
	rlwinm	r12, r10, 13, 19, 29	/* Compute pgdir/pmd offset */
 | 
			
		||||
	lwzx	r11, r12, r11		/* Get pgd/pmd entry */
 | 
			
		||||
	rlwinm.	r12, r11, 0, 0, 20	/* Extract pt base address */
 | 
			
		||||
	beq	2f			/* Bail if no table */
 | 
			
		||||
 | 
			
		||||
	rlwimi	r12, r10, 23, 20, 28	/* Compute pte address */
 | 
			
		||||
	lwz	r11, 4(r12)		/* Get pte entry */
 | 
			
		||||
	andi.	r13, r11, _PAGE_PRESENT	/* Is the page present? */
 | 
			
		||||
	beq	2f			/* Bail if not present */
 | 
			
		||||
 | 
			
		||||
	ori	r11, r11, _PAGE_ACCESSED
 | 
			
		||||
	stw	r11, 4(r12)
 | 
			
		||||
 | 
			
		||||
	/* Jump to common TLB load point */
 | 
			
		||||
	b	finish_tlb_load
 | 
			
		||||
 | 
			
		||||
2:
 | 
			
		||||
	/* The bailout.  Restore registers to pre-exception conditions
 | 
			
		||||
	 * and call the heavyweights to help us out.
 | 
			
		||||
	 */
 | 
			
		||||
	mfspr	r11, SPRN_SPRG7R
 | 
			
		||||
	mtcr	r11
 | 
			
		||||
	mfspr	r13, SPRN_SPRG5R
 | 
			
		||||
	mfspr	r12, SPRN_SPRG4R
 | 
			
		||||
	mfspr	r11, SPRN_SPRG1
 | 
			
		||||
	mfspr	r10, SPRN_SPRG0
 | 
			
		||||
	b	InstructionStorage
 | 
			
		||||
 | 
			
		||||
	/* Debug Interrupt */
 | 
			
		||||
	DEBUG_EXCEPTION
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local functions
 | 
			
		||||
 */
 | 
			
		||||
	/*
 | 
			
		||||
	 * Data TLB exceptions will bail out to this point
 | 
			
		||||
	 * if they can't resolve the lightweight TLB fault.
 | 
			
		||||
	 */
 | 
			
		||||
data_access:
 | 
			
		||||
	NORMAL_EXCEPTION_PROLOG
 | 
			
		||||
	mfspr	r5,SPRN_ESR		/* Grab the ESR, save it, pass arg3 */
 | 
			
		||||
	stw	r5,_ESR(r11)
 | 
			
		||||
	mfspr	r4,SPRN_DEAR		/* Grab the DEAR, save it, pass arg2 */
 | 
			
		||||
	EXC_XFER_EE_LITE(0x0300, handle_page_fault)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
 * Both the instruction and data TLB miss get to this
 | 
			
		||||
 * point to load the TLB.
 | 
			
		||||
 * 	r10 - EA of fault
 | 
			
		||||
 * 	r11 - available to use
 | 
			
		||||
 *	r12 - Pointer to the 64-bit PTE
 | 
			
		||||
 *	r13 - available to use
 | 
			
		||||
 *	MMUCR - loaded with proper value when we get here
 | 
			
		||||
 *	Upon exit, we reload everything and RFI.
 | 
			
		||||
 */
 | 
			
		||||
finish_tlb_load:
 | 
			
		||||
	/*
 | 
			
		||||
	 * We set execute, because we don't have the granularity to
 | 
			
		||||
	 * properly set this at the page level (Linux problem).
 | 
			
		||||
	 * If shared is set, we cause a zero PID->TID load.
 | 
			
		||||
	 * Many of these bits are software only.  Bits we don't set
 | 
			
		||||
	 * here we (properly should) assume have the appropriate value.
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
	/* Load the next available TLB index */
 | 
			
		||||
	lis	r13, tlb_44x_index@ha
 | 
			
		||||
	lwz	r13, tlb_44x_index@l(r13)
 | 
			
		||||
	/* Load the TLB high watermark */
 | 
			
		||||
	lis	r11, tlb_44x_hwater@ha
 | 
			
		||||
	lwz	r11, tlb_44x_hwater@l(r11)
 | 
			
		||||
 | 
			
		||||
	/* Increment, rollover, and store TLB index */
 | 
			
		||||
	addi	r13, r13, 1
 | 
			
		||||
	cmpw	0, r13, r11			/* reserve entries */
 | 
			
		||||
	ble	7f
 | 
			
		||||
	li	r13, 0
 | 
			
		||||
7:
 | 
			
		||||
	/* Store the next available TLB index */
 | 
			
		||||
	lis	r11, tlb_44x_index@ha
 | 
			
		||||
	stw	r13, tlb_44x_index@l(r11)
 | 
			
		||||
 | 
			
		||||
	lwz	r11, 0(r12)			/* Get MS word of PTE */
 | 
			
		||||
	lwz	r12, 4(r12)			/* Get LS word of PTE */
 | 
			
		||||
	rlwimi	r11, r12, 0, 0 , 19		/* Insert RPN */
 | 
			
		||||
	tlbwe	r11, r13, PPC44x_TLB_XLAT	/* Write XLAT */
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Create PAGEID. This is the faulting address,
 | 
			
		||||
	 * page size, and valid flag.
 | 
			
		||||
	 */
 | 
			
		||||
	li	r11, PPC44x_TLB_VALID | PPC44x_TLB_4K
 | 
			
		||||
	rlwimi	r10, r11, 0, 20, 31		/* Insert valid and page size */
 | 
			
		||||
	tlbwe	r10, r13, PPC44x_TLB_PAGEID	/* Write PAGEID */
 | 
			
		||||
 | 
			
		||||
	li	r10, PPC44x_TLB_SR@l		/* Set SR */
 | 
			
		||||
	rlwimi	r10, r12, 0, 30, 30		/* Set SW = _PAGE_RW */
 | 
			
		||||
	rlwimi	r10, r12, 29, 29, 29		/* SX = _PAGE_HWEXEC */
 | 
			
		||||
	rlwimi	r10, r12, 29, 28, 28		/* UR = _PAGE_USER */
 | 
			
		||||
	rlwimi	r11, r12, 31, 26, 26		/* (_PAGE_USER>>1)->r12 */
 | 
			
		||||
	and	r11, r12, r11			/* HWEXEC & USER */
 | 
			
		||||
	rlwimi	r10, r11, 0, 26, 26		/* UX = HWEXEC & USER */
 | 
			
		||||
 | 
			
		||||
	rlwimi	r12, r10, 0, 26, 31		/* Insert static perms */
 | 
			
		||||
	rlwinm	r12, r12, 0, 20, 15		/* Clear U0-U3 */
 | 
			
		||||
	tlbwe	r12, r13, PPC44x_TLB_ATTRIB	/* Write ATTRIB */
 | 
			
		||||
 | 
			
		||||
	/* Done...restore registers and get out of here.
 | 
			
		||||
	*/
 | 
			
		||||
	mfspr	r11, SPRN_SPRG7R
 | 
			
		||||
	mtcr	r11
 | 
			
		||||
	mfspr	r13, SPRN_SPRG5R
 | 
			
		||||
	mfspr	r12, SPRN_SPRG4R
 | 
			
		||||
	mfspr	r11, SPRN_SPRG1
 | 
			
		||||
	mfspr	r10, SPRN_SPRG0
 | 
			
		||||
	rfi					/* Force context change */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Global functions
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * extern void giveup_altivec(struct task_struct *prev)
 | 
			
		||||
 *
 | 
			
		||||
 * The 44x core does not have an AltiVec unit.
 | 
			
		||||
 */
 | 
			
		||||
_GLOBAL(giveup_altivec)
 | 
			
		||||
	blr
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * extern void giveup_fpu(struct task_struct *prev)
 | 
			
		||||
 *
 | 
			
		||||
 * The 44x core does not have an FPU.
 | 
			
		||||
 */
 | 
			
		||||
#ifndef CONFIG_PPC_FPU
 | 
			
		||||
_GLOBAL(giveup_fpu)
 | 
			
		||||
	blr
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * extern void abort(void)
 | 
			
		||||
 *
 | 
			
		||||
 * At present, this routine just applies a system reset.
 | 
			
		||||
 */
 | 
			
		||||
_GLOBAL(abort)
 | 
			
		||||
        mfspr   r13,SPRN_DBCR0
 | 
			
		||||
        oris    r13,r13,DBCR0_RST_SYSTEM@h
 | 
			
		||||
        mtspr   SPRN_DBCR0,r13
 | 
			
		||||
 | 
			
		||||
_GLOBAL(set_context)
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_BDI_SWITCH
 | 
			
		||||
	/* Context switch the PTE pointer for the Abatron BDI2000.
 | 
			
		||||
	 * The PGDIR is the second parameter.
 | 
			
		||||
	 */
 | 
			
		||||
	lis	r5, abatron_pteptrs@h
 | 
			
		||||
	ori	r5, r5, abatron_pteptrs@l
 | 
			
		||||
	stw	r4, 0x4(r5)
 | 
			
		||||
#endif
 | 
			
		||||
	mtspr	SPRN_PID,r3
 | 
			
		||||
	isync			/* Force context change */
 | 
			
		||||
	blr
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * We put a few things here that have to be page-aligned. This stuff
 | 
			
		||||
 * goes at the beginning of the data segment, which is page-aligned.
 | 
			
		||||
 */
 | 
			
		||||
	.data
 | 
			
		||||
_GLOBAL(sdata)
 | 
			
		||||
_GLOBAL(empty_zero_page)
 | 
			
		||||
	.space	4096
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * To support >32-bit physical addresses, we use an 8KB pgdir.
 | 
			
		||||
 */
 | 
			
		||||
_GLOBAL(swapper_pg_dir)
 | 
			
		||||
	.space	8192
 | 
			
		||||
 | 
			
		||||
/* Reserved 4k for the critical exception stack & 4k for the machine
 | 
			
		||||
 * check stack per CPU for kernel mode exceptions */
 | 
			
		||||
	.section .bss
 | 
			
		||||
        .align 12
 | 
			
		||||
exception_stack_bottom:
 | 
			
		||||
	.space	BOOKE_EXCEPTION_STACK_SIZE
 | 
			
		||||
_GLOBAL(exception_stack_top)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * This space gets a copy of optional info passed to us by the bootstrap
 | 
			
		||||
 * which is used to pass parameters into the kernel like root=/dev/sda1, etc.
 | 
			
		||||
 */
 | 
			
		||||
_GLOBAL(cmd_line)
 | 
			
		||||
	.space	512
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Room for two PTE pointers, usually the kernel and current user pointers
 | 
			
		||||
 * to their respective root page table.
 | 
			
		||||
 */
 | 
			
		||||
abatron_pteptrs:
 | 
			
		||||
	.space	8
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1016
									
								
								arch/powerpc/kernel/head_4xx.S
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1016
									
								
								arch/powerpc/kernel/head_4xx.S
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2011
									
								
								arch/powerpc/kernel/head_64.S
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2011
									
								
								arch/powerpc/kernel/head_64.S
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										860
									
								
								arch/powerpc/kernel/head_8xx.S
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										860
									
								
								arch/powerpc/kernel/head_8xx.S
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,860 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  arch/ppc/kernel/except_8xx.S
 | 
			
		||||
 *
 | 
			
		||||
 *  PowerPC version
 | 
			
		||||
 *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
 | 
			
		||||
 *  Rewritten by Cort Dougan (cort@cs.nmt.edu) for PReP
 | 
			
		||||
 *    Copyright (C) 1996 Cort Dougan <cort@cs.nmt.edu>
 | 
			
		||||
 *  Low-level exception handlers and MMU support
 | 
			
		||||
 *  rewritten by Paul Mackerras.
 | 
			
		||||
 *    Copyright (C) 1996 Paul Mackerras.
 | 
			
		||||
 *  MPC8xx modifications by Dan Malek
 | 
			
		||||
 *    Copyright (C) 1997 Dan Malek (dmalek@jlc.net).
 | 
			
		||||
 *
 | 
			
		||||
 *  This file contains low-level support and setup for PowerPC 8xx
 | 
			
		||||
 *  embedded processors, including trap and interrupt dispatch.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or
 | 
			
		||||
 *  modify it under the terms of the GNU General Public License
 | 
			
		||||
 *  as published by the Free Software Foundation; either version
 | 
			
		||||
 *  2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/config.h>
 | 
			
		||||
#include <asm/processor.h>
 | 
			
		||||
#include <asm/page.h>
 | 
			
		||||
#include <asm/mmu.h>
 | 
			
		||||
#include <asm/cache.h>
 | 
			
		||||
#include <asm/pgtable.h>
 | 
			
		||||
#include <asm/cputable.h>
 | 
			
		||||
#include <asm/thread_info.h>
 | 
			
		||||
#include <asm/ppc_asm.h>
 | 
			
		||||
#include <asm/asm-offsets.h>
 | 
			
		||||
 | 
			
		||||
/* Macro to make the code more readable. */
 | 
			
		||||
#ifdef CONFIG_8xx_CPU6
 | 
			
		||||
#define DO_8xx_CPU6(val, reg)	\
 | 
			
		||||
	li	reg, val;	\
 | 
			
		||||
	stw	reg, 12(r0);	\
 | 
			
		||||
	lwz	reg, 12(r0);
 | 
			
		||||
#else
 | 
			
		||||
#define DO_8xx_CPU6(val, reg)
 | 
			
		||||
#endif
 | 
			
		||||
	.text
 | 
			
		||||
	.globl	_stext
 | 
			
		||||
_stext:
 | 
			
		||||
	.text
 | 
			
		||||
	.globl	_start
 | 
			
		||||
_start:
 | 
			
		||||
 | 
			
		||||
/* MPC8xx
 | 
			
		||||
 * This port was done on an MBX board with an 860.  Right now I only
 | 
			
		||||
 * support an ELF compressed (zImage) boot from EPPC-Bug because the
 | 
			
		||||
 * code there loads up some registers before calling us:
 | 
			
		||||
 *   r3: ptr to board info data
 | 
			
		||||
 *   r4: initrd_start or if no initrd then 0
 | 
			
		||||
 *   r5: initrd_end - unused if r4 is 0
 | 
			
		||||
 *   r6: Start of command line string
 | 
			
		||||
 *   r7: End of command line string
 | 
			
		||||
 *
 | 
			
		||||
 * I decided to use conditional compilation instead of checking PVR and
 | 
			
		||||
 * adding more processor specific branches around code I don't need.
 | 
			
		||||
 * Since this is an embedded processor, I also appreciate any memory
 | 
			
		||||
 * savings I can get.
 | 
			
		||||
 *
 | 
			
		||||
 * The MPC8xx does not have any BATs, but it supports large page sizes.
 | 
			
		||||
 * We first initialize the MMU to support 8M byte pages, then load one
 | 
			
		||||
 * entry into each of the instruction and data TLBs to map the first
 | 
			
		||||
 * 8M 1:1.  I also mapped an additional I/O space 1:1 so we can get to
 | 
			
		||||
 * the "internal" processor registers before MMU_init is called.
 | 
			
		||||
 *
 | 
			
		||||
 * The TLB code currently contains a major hack.  Since I use the condition
 | 
			
		||||
 * code register, I have to save and restore it.  I am out of registers, so
 | 
			
		||||
 * I just store it in memory location 0 (the TLB handlers are not reentrant).
 | 
			
		||||
 * To avoid making any decisions, I need to use the "segment" valid bit
 | 
			
		||||
 * in the first level table, but that would require many changes to the
 | 
			
		||||
 * Linux page directory/table functions that I don't want to do right now.
 | 
			
		||||
 *
 | 
			
		||||
 * I used to use SPRG2 for a temporary register in the TLB handler, but it
 | 
			
		||||
 * has since been put to other uses.  I now use a hack to save a register
 | 
			
		||||
 * and the CCR at memory location 0.....Someday I'll fix this.....
 | 
			
		||||
 *	-- Dan
 | 
			
		||||
 */
 | 
			
		||||
	.globl	__start
 | 
			
		||||
__start:
 | 
			
		||||
	mr	r31,r3			/* save parameters */
 | 
			
		||||
	mr	r30,r4
 | 
			
		||||
	mr	r29,r5
 | 
			
		||||
	mr	r28,r6
 | 
			
		||||
	mr	r27,r7
 | 
			
		||||
 | 
			
		||||
	/* We have to turn on the MMU right away so we get cache modes
 | 
			
		||||
	 * set correctly.
 | 
			
		||||
	 */
 | 
			
		||||
	bl	initial_mmu
 | 
			
		||||
 | 
			
		||||
/* We now have the lower 8 Meg mapped into TLB entries, and the caches
 | 
			
		||||
 * ready to work.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
turn_on_mmu:
 | 
			
		||||
	mfmsr	r0
 | 
			
		||||
	ori	r0,r0,MSR_DR|MSR_IR
 | 
			
		||||
	mtspr	SPRN_SRR1,r0
 | 
			
		||||
	lis	r0,start_here@h
 | 
			
		||||
	ori	r0,r0,start_here@l
 | 
			
		||||
	mtspr	SPRN_SRR0,r0
 | 
			
		||||
	SYNC
 | 
			
		||||
	rfi				/* enables MMU */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Exception entry code.  This code runs with address translation
 | 
			
		||||
 * turned off, i.e. using physical addresses.
 | 
			
		||||
 * We assume sprg3 has the physical address of the current
 | 
			
		||||
 * task's thread_struct.
 | 
			
		||||
 */
 | 
			
		||||
#define EXCEPTION_PROLOG	\
 | 
			
		||||
	mtspr	SPRN_SPRG0,r10;	\
 | 
			
		||||
	mtspr	SPRN_SPRG1,r11;	\
 | 
			
		||||
	mfcr	r10;		\
 | 
			
		||||
	EXCEPTION_PROLOG_1;	\
 | 
			
		||||
	EXCEPTION_PROLOG_2
 | 
			
		||||
 | 
			
		||||
#define EXCEPTION_PROLOG_1	\
 | 
			
		||||
	mfspr	r11,SPRN_SRR1;		/* check whether user or kernel */ \
 | 
			
		||||
	andi.	r11,r11,MSR_PR;	\
 | 
			
		||||
	tophys(r11,r1);			/* use tophys(r1) if kernel */ \
 | 
			
		||||
	beq	1f;		\
 | 
			
		||||
	mfspr	r11,SPRN_SPRG3;	\
 | 
			
		||||
	lwz	r11,THREAD_INFO-THREAD(r11);	\
 | 
			
		||||
	addi	r11,r11,THREAD_SIZE;	\
 | 
			
		||||
	tophys(r11,r11);	\
 | 
			
		||||
1:	subi	r11,r11,INT_FRAME_SIZE	/* alloc exc. frame */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define EXCEPTION_PROLOG_2	\
 | 
			
		||||
	CLR_TOP32(r11);		\
 | 
			
		||||
	stw	r10,_CCR(r11);		/* save registers */ \
 | 
			
		||||
	stw	r12,GPR12(r11);	\
 | 
			
		||||
	stw	r9,GPR9(r11);	\
 | 
			
		||||
	mfspr	r10,SPRN_SPRG0;	\
 | 
			
		||||
	stw	r10,GPR10(r11);	\
 | 
			
		||||
	mfspr	r12,SPRN_SPRG1;	\
 | 
			
		||||
	stw	r12,GPR11(r11);	\
 | 
			
		||||
	mflr	r10;		\
 | 
			
		||||
	stw	r10,_LINK(r11);	\
 | 
			
		||||
	mfspr	r12,SPRN_SRR0;	\
 | 
			
		||||
	mfspr	r9,SPRN_SRR1;	\
 | 
			
		||||
	stw	r1,GPR1(r11);	\
 | 
			
		||||
	stw	r1,0(r11);	\
 | 
			
		||||
	tovirt(r1,r11);			/* set new kernel sp */	\
 | 
			
		||||
	li	r10,MSR_KERNEL & ~(MSR_IR|MSR_DR); /* can take exceptions */ \
 | 
			
		||||
	MTMSRD(r10);			/* (except for mach check in rtas) */ \
 | 
			
		||||
	stw	r0,GPR0(r11);	\
 | 
			
		||||
	SAVE_4GPRS(3, r11);	\
 | 
			
		||||
	SAVE_2GPRS(7, r11)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Note: code which follows this uses cr0.eq (set if from kernel),
 | 
			
		||||
 * r11, r12 (SRR0), and r9 (SRR1).
 | 
			
		||||
 *
 | 
			
		||||
 * Note2: once we have set r1 we are in a position to take exceptions
 | 
			
		||||
 * again, and we could thus set MSR:RI at that point.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Exception vectors.
 | 
			
		||||
 */
 | 
			
		||||
#define EXCEPTION(n, label, hdlr, xfer)		\
 | 
			
		||||
	. = n;					\
 | 
			
		||||
label:						\
 | 
			
		||||
	EXCEPTION_PROLOG;			\
 | 
			
		||||
	addi	r3,r1,STACK_FRAME_OVERHEAD;	\
 | 
			
		||||
	xfer(n, hdlr)
 | 
			
		||||
 | 
			
		||||
#define EXC_XFER_TEMPLATE(n, hdlr, trap, copyee, tfer, ret)	\
 | 
			
		||||
	li	r10,trap;					\
 | 
			
		||||
	stw	r10,TRAP(r11);					\
 | 
			
		||||
	li	r10,MSR_KERNEL;					\
 | 
			
		||||
	copyee(r10, r9);					\
 | 
			
		||||
	bl	tfer;						\
 | 
			
		||||
i##n:								\
 | 
			
		||||
	.long	hdlr;						\
 | 
			
		||||
	.long	ret
 | 
			
		||||
 | 
			
		||||
#define COPY_EE(d, s)		rlwimi d,s,0,16,16
 | 
			
		||||
#define NOCOPY(d, s)
 | 
			
		||||
 | 
			
		||||
#define EXC_XFER_STD(n, hdlr)		\
 | 
			
		||||
	EXC_XFER_TEMPLATE(n, hdlr, n, NOCOPY, transfer_to_handler_full,	\
 | 
			
		||||
			  ret_from_except_full)
 | 
			
		||||
 | 
			
		||||
#define EXC_XFER_LITE(n, hdlr)		\
 | 
			
		||||
	EXC_XFER_TEMPLATE(n, hdlr, n+1, NOCOPY, transfer_to_handler, \
 | 
			
		||||
			  ret_from_except)
 | 
			
		||||
 | 
			
		||||
#define EXC_XFER_EE(n, hdlr)		\
 | 
			
		||||
	EXC_XFER_TEMPLATE(n, hdlr, n, COPY_EE, transfer_to_handler_full, \
 | 
			
		||||
			  ret_from_except_full)
 | 
			
		||||
 | 
			
		||||
#define EXC_XFER_EE_LITE(n, hdlr)	\
 | 
			
		||||
	EXC_XFER_TEMPLATE(n, hdlr, n+1, COPY_EE, transfer_to_handler, \
 | 
			
		||||
			  ret_from_except)
 | 
			
		||||
 | 
			
		||||
/* System reset */
 | 
			
		||||
	EXCEPTION(0x100, Reset, UnknownException, EXC_XFER_STD)
 | 
			
		||||
 | 
			
		||||
/* Machine check */
 | 
			
		||||
	. = 0x200
 | 
			
		||||
MachineCheck:
 | 
			
		||||
	EXCEPTION_PROLOG
 | 
			
		||||
	mfspr r4,SPRN_DAR
 | 
			
		||||
	stw r4,_DAR(r11)
 | 
			
		||||
	mfspr r5,SPRN_DSISR
 | 
			
		||||
	stw r5,_DSISR(r11)
 | 
			
		||||
	addi r3,r1,STACK_FRAME_OVERHEAD
 | 
			
		||||
	EXC_XFER_STD(0x200, MachineCheckException)
 | 
			
		||||
 | 
			
		||||
/* Data access exception.
 | 
			
		||||
 * This is "never generated" by the MPC8xx.  We jump to it for other
 | 
			
		||||
 * translation errors.
 | 
			
		||||
 */
 | 
			
		||||
	. = 0x300
 | 
			
		||||
DataAccess:
 | 
			
		||||
	EXCEPTION_PROLOG
 | 
			
		||||
	mfspr	r10,SPRN_DSISR
 | 
			
		||||
	stw	r10,_DSISR(r11)
 | 
			
		||||
	mr	r5,r10
 | 
			
		||||
	mfspr	r4,SPRN_DAR
 | 
			
		||||
	EXC_XFER_EE_LITE(0x300, handle_page_fault)
 | 
			
		||||
 | 
			
		||||
/* Instruction access exception.
 | 
			
		||||
 * This is "never generated" by the MPC8xx.  We jump to it for other
 | 
			
		||||
 * translation errors.
 | 
			
		||||
 */
 | 
			
		||||
	. = 0x400
 | 
			
		||||
InstructionAccess:
 | 
			
		||||
	EXCEPTION_PROLOG
 | 
			
		||||
	mr	r4,r12
 | 
			
		||||
	mr	r5,r9
 | 
			
		||||
	EXC_XFER_EE_LITE(0x400, handle_page_fault)
 | 
			
		||||
 | 
			
		||||
/* External interrupt */
 | 
			
		||||
	EXCEPTION(0x500, HardwareInterrupt, do_IRQ, EXC_XFER_LITE)
 | 
			
		||||
 | 
			
		||||
/* Alignment exception */
 | 
			
		||||
	. = 0x600
 | 
			
		||||
Alignment:
 | 
			
		||||
	EXCEPTION_PROLOG
 | 
			
		||||
	mfspr	r4,SPRN_DAR
 | 
			
		||||
	stw	r4,_DAR(r11)
 | 
			
		||||
	mfspr	r5,SPRN_DSISR
 | 
			
		||||
	stw	r5,_DSISR(r11)
 | 
			
		||||
	addi	r3,r1,STACK_FRAME_OVERHEAD
 | 
			
		||||
	EXC_XFER_EE(0x600, AlignmentException)
 | 
			
		||||
 | 
			
		||||
/* Program check exception */
 | 
			
		||||
	EXCEPTION(0x700, ProgramCheck, ProgramCheckException, EXC_XFER_STD)
 | 
			
		||||
 | 
			
		||||
/* No FPU on MPC8xx.  This exception is not supposed to happen.
 | 
			
		||||
*/
 | 
			
		||||
	EXCEPTION(0x800, FPUnavailable, UnknownException, EXC_XFER_STD)
 | 
			
		||||
 | 
			
		||||
/* Decrementer */
 | 
			
		||||
	EXCEPTION(0x900, Decrementer, timer_interrupt, EXC_XFER_LITE)
 | 
			
		||||
 | 
			
		||||
	EXCEPTION(0xa00, Trap_0a, UnknownException, EXC_XFER_EE)
 | 
			
		||||
	EXCEPTION(0xb00, Trap_0b, UnknownException, EXC_XFER_EE)
 | 
			
		||||
 | 
			
		||||
/* System call */
 | 
			
		||||
	. = 0xc00
 | 
			
		||||
SystemCall:
 | 
			
		||||
	EXCEPTION_PROLOG
 | 
			
		||||
	EXC_XFER_EE_LITE(0xc00, DoSyscall)
 | 
			
		||||
 | 
			
		||||
/* Single step - not used on 601 */
 | 
			
		||||
	EXCEPTION(0xd00, SingleStep, SingleStepException, EXC_XFER_STD)
 | 
			
		||||
	EXCEPTION(0xe00, Trap_0e, UnknownException, EXC_XFER_EE)
 | 
			
		||||
	EXCEPTION(0xf00, Trap_0f, UnknownException, EXC_XFER_EE)
 | 
			
		||||
 | 
			
		||||
/* On the MPC8xx, this is a software emulation interrupt.  It occurs
 | 
			
		||||
 * for all unimplemented and illegal instructions.
 | 
			
		||||
 */
 | 
			
		||||
	EXCEPTION(0x1000, SoftEmu, SoftwareEmulation, EXC_XFER_STD)
 | 
			
		||||
 | 
			
		||||
	. = 0x1100
 | 
			
		||||
/*
 | 
			
		||||
 * For the MPC8xx, this is a software tablewalk to load the instruction
 | 
			
		||||
 * TLB.  It is modelled after the example in the Motorola manual.  The task
 | 
			
		||||
 * switch loads the M_TWB register with the pointer to the first level table.
 | 
			
		||||
 * If we discover there is no second level table (value is zero) or if there
 | 
			
		||||
 * is an invalid pte, we load that into the TLB, which causes another fault
 | 
			
		||||
 * into the TLB Error interrupt where we can handle such problems.
 | 
			
		||||
 * We have to use the MD_xxx registers for the tablewalk because the
 | 
			
		||||
 * equivalent MI_xxx registers only perform the attribute functions.
 | 
			
		||||
 */
 | 
			
		||||
InstructionTLBMiss:
 | 
			
		||||
#ifdef CONFIG_8xx_CPU6
 | 
			
		||||
	stw	r3, 8(r0)
 | 
			
		||||
#endif
 | 
			
		||||
	DO_8xx_CPU6(0x3f80, r3)
 | 
			
		||||
	mtspr	SPRN_M_TW, r10	/* Save a couple of working registers */
 | 
			
		||||
	mfcr	r10
 | 
			
		||||
	stw	r10, 0(r0)
 | 
			
		||||
	stw	r11, 4(r0)
 | 
			
		||||
	mfspr	r10, SPRN_SRR0	/* Get effective address of fault */
 | 
			
		||||
	DO_8xx_CPU6(0x3780, r3)
 | 
			
		||||
	mtspr	SPRN_MD_EPN, r10	/* Have to use MD_EPN for walk, MI_EPN can't */
 | 
			
		||||
	mfspr	r10, SPRN_M_TWB	/* Get level 1 table entry address */
 | 
			
		||||
 | 
			
		||||
	/* If we are faulting a kernel address, we have to use the
 | 
			
		||||
	 * kernel page tables.
 | 
			
		||||
	 */
 | 
			
		||||
	andi.	r11, r10, 0x0800	/* Address >= 0x80000000 */
 | 
			
		||||
	beq	3f
 | 
			
		||||
	lis	r11, swapper_pg_dir@h
 | 
			
		||||
	ori	r11, r11, swapper_pg_dir@l
 | 
			
		||||
	rlwimi	r10, r11, 0, 2, 19
 | 
			
		||||
3:
 | 
			
		||||
	lwz	r11, 0(r10)	/* Get the level 1 entry */
 | 
			
		||||
	rlwinm.	r10, r11,0,0,19	/* Extract page descriptor page address */
 | 
			
		||||
	beq	2f		/* If zero, don't try to find a pte */
 | 
			
		||||
 | 
			
		||||
	/* We have a pte table, so load the MI_TWC with the attributes
 | 
			
		||||
	 * for this "segment."
 | 
			
		||||
	 */
 | 
			
		||||
	ori	r11,r11,1		/* Set valid bit */
 | 
			
		||||
	DO_8xx_CPU6(0x2b80, r3)
 | 
			
		||||
	mtspr	SPRN_MI_TWC, r11	/* Set segment attributes */
 | 
			
		||||
	DO_8xx_CPU6(0x3b80, r3)
 | 
			
		||||
	mtspr	SPRN_MD_TWC, r11	/* Load pte table base address */
 | 
			
		||||
	mfspr	r11, SPRN_MD_TWC	/* ....and get the pte address */
 | 
			
		||||
	lwz	r10, 0(r11)	/* Get the pte */
 | 
			
		||||
 | 
			
		||||
	ori	r10, r10, _PAGE_ACCESSED
 | 
			
		||||
	stw	r10, 0(r11)
 | 
			
		||||
 | 
			
		||||
	/* The Linux PTE won't go exactly into the MMU TLB.
 | 
			
		||||
	 * Software indicator bits 21, 22 and 28 must be clear.
 | 
			
		||||
	 * Software indicator bits 24, 25, 26, and 27 must be
 | 
			
		||||
	 * set.  All other Linux PTE bits control the behavior
 | 
			
		||||
	 * of the MMU.
 | 
			
		||||
	 */
 | 
			
		||||
2:	li	r11, 0x00f0
 | 
			
		||||
	rlwimi	r10, r11, 0, 24, 28	/* Set 24-27, clear 28 */
 | 
			
		||||
	DO_8xx_CPU6(0x2d80, r3)
 | 
			
		||||
	mtspr	SPRN_MI_RPN, r10	/* Update TLB entry */
 | 
			
		||||
 | 
			
		||||
	mfspr	r10, SPRN_M_TW	/* Restore registers */
 | 
			
		||||
	lwz	r11, 0(r0)
 | 
			
		||||
	mtcr	r11
 | 
			
		||||
	lwz	r11, 4(r0)
 | 
			
		||||
#ifdef CONFIG_8xx_CPU6
 | 
			
		||||
	lwz	r3, 8(r0)
 | 
			
		||||
#endif
 | 
			
		||||
	rfi
 | 
			
		||||
 | 
			
		||||
	. = 0x1200
 | 
			
		||||
DataStoreTLBMiss:
 | 
			
		||||
#ifdef CONFIG_8xx_CPU6
 | 
			
		||||
	stw	r3, 8(r0)
 | 
			
		||||
#endif
 | 
			
		||||
	DO_8xx_CPU6(0x3f80, r3)
 | 
			
		||||
	mtspr	SPRN_M_TW, r10	/* Save a couple of working registers */
 | 
			
		||||
	mfcr	r10
 | 
			
		||||
	stw	r10, 0(r0)
 | 
			
		||||
	stw	r11, 4(r0)
 | 
			
		||||
	mfspr	r10, SPRN_M_TWB	/* Get level 1 table entry address */
 | 
			
		||||
 | 
			
		||||
	/* If we are faulting a kernel address, we have to use the
 | 
			
		||||
	 * kernel page tables.
 | 
			
		||||
	 */
 | 
			
		||||
	andi.	r11, r10, 0x0800
 | 
			
		||||
	beq	3f
 | 
			
		||||
	lis	r11, swapper_pg_dir@h
 | 
			
		||||
	ori	r11, r11, swapper_pg_dir@l
 | 
			
		||||
	rlwimi	r10, r11, 0, 2, 19
 | 
			
		||||
3:
 | 
			
		||||
	lwz	r11, 0(r10)	/* Get the level 1 entry */
 | 
			
		||||
	rlwinm.	r10, r11,0,0,19	/* Extract page descriptor page address */
 | 
			
		||||
	beq	2f		/* If zero, don't try to find a pte */
 | 
			
		||||
 | 
			
		||||
	/* We have a pte table, so load fetch the pte from the table.
 | 
			
		||||
	 */
 | 
			
		||||
	ori	r11, r11, 1	/* Set valid bit in physical L2 page */
 | 
			
		||||
	DO_8xx_CPU6(0x3b80, r3)
 | 
			
		||||
	mtspr	SPRN_MD_TWC, r11	/* Load pte table base address */
 | 
			
		||||
	mfspr	r10, SPRN_MD_TWC	/* ....and get the pte address */
 | 
			
		||||
	lwz	r10, 0(r10)	/* Get the pte */
 | 
			
		||||
 | 
			
		||||
	/* Insert the Guarded flag into the TWC from the Linux PTE.
 | 
			
		||||
	 * It is bit 27 of both the Linux PTE and the TWC (at least
 | 
			
		||||
	 * I got that right :-).  It will be better when we can put
 | 
			
		||||
	 * this into the Linux pgd/pmd and load it in the operation
 | 
			
		||||
	 * above.
 | 
			
		||||
	 */
 | 
			
		||||
	rlwimi	r11, r10, 0, 27, 27
 | 
			
		||||
	DO_8xx_CPU6(0x3b80, r3)
 | 
			
		||||
	mtspr	SPRN_MD_TWC, r11
 | 
			
		||||
 | 
			
		||||
	mfspr	r11, SPRN_MD_TWC	/* get the pte address again */
 | 
			
		||||
	ori	r10, r10, _PAGE_ACCESSED
 | 
			
		||||
	stw	r10, 0(r11)
 | 
			
		||||
 | 
			
		||||
	/* The Linux PTE won't go exactly into the MMU TLB.
 | 
			
		||||
	 * Software indicator bits 21, 22 and 28 must be clear.
 | 
			
		||||
	 * Software indicator bits 24, 25, 26, and 27 must be
 | 
			
		||||
	 * set.  All other Linux PTE bits control the behavior
 | 
			
		||||
	 * of the MMU.
 | 
			
		||||
	 */
 | 
			
		||||
2:	li	r11, 0x00f0
 | 
			
		||||
	rlwimi	r10, r11, 0, 24, 28	/* Set 24-27, clear 28 */
 | 
			
		||||
	DO_8xx_CPU6(0x3d80, r3)
 | 
			
		||||
	mtspr	SPRN_MD_RPN, r10	/* Update TLB entry */
 | 
			
		||||
 | 
			
		||||
	mfspr	r10, SPRN_M_TW	/* Restore registers */
 | 
			
		||||
	lwz	r11, 0(r0)
 | 
			
		||||
	mtcr	r11
 | 
			
		||||
	lwz	r11, 4(r0)
 | 
			
		||||
#ifdef CONFIG_8xx_CPU6
 | 
			
		||||
	lwz	r3, 8(r0)
 | 
			
		||||
#endif
 | 
			
		||||
	rfi
 | 
			
		||||
 | 
			
		||||
/* This is an instruction TLB error on the MPC8xx.  This could be due
 | 
			
		||||
 * to many reasons, such as executing guarded memory or illegal instruction
 | 
			
		||||
 * addresses.  There is nothing to do but handle a big time error fault.
 | 
			
		||||
 */
 | 
			
		||||
	. = 0x1300
 | 
			
		||||
InstructionTLBError:
 | 
			
		||||
	b	InstructionAccess
 | 
			
		||||
 | 
			
		||||
/* This is the data TLB error on the MPC8xx.  This could be due to
 | 
			
		||||
 * many reasons, including a dirty update to a pte.  We can catch that
 | 
			
		||||
 * one here, but anything else is an error.  First, we track down the
 | 
			
		||||
 * Linux pte.  If it is valid, write access is allowed, but the
 | 
			
		||||
 * page dirty bit is not set, we will set it and reload the TLB.  For
 | 
			
		||||
 * any other case, we bail out to a higher level function that can
 | 
			
		||||
 * handle it.
 | 
			
		||||
 */
 | 
			
		||||
	. = 0x1400
 | 
			
		||||
DataTLBError:
 | 
			
		||||
#ifdef CONFIG_8xx_CPU6
 | 
			
		||||
	stw	r3, 8(r0)
 | 
			
		||||
#endif
 | 
			
		||||
	DO_8xx_CPU6(0x3f80, r3)
 | 
			
		||||
	mtspr	SPRN_M_TW, r10	/* Save a couple of working registers */
 | 
			
		||||
	mfcr	r10
 | 
			
		||||
	stw	r10, 0(r0)
 | 
			
		||||
	stw	r11, 4(r0)
 | 
			
		||||
 | 
			
		||||
	/* First, make sure this was a store operation.
 | 
			
		||||
	*/
 | 
			
		||||
	mfspr	r10, SPRN_DSISR
 | 
			
		||||
	andis.	r11, r10, 0x0200	/* If set, indicates store op */
 | 
			
		||||
	beq	2f
 | 
			
		||||
 | 
			
		||||
	/* The EA of a data TLB miss is automatically stored in the MD_EPN
 | 
			
		||||
	 * register.  The EA of a data TLB error is automatically stored in
 | 
			
		||||
	 * the DAR, but not the MD_EPN register.  We must copy the 20 most
 | 
			
		||||
	 * significant bits of the EA from the DAR to MD_EPN before we
 | 
			
		||||
	 * start walking the page tables.  We also need to copy the CASID
 | 
			
		||||
	 * value from the M_CASID register.
 | 
			
		||||
	 * Addendum:  The EA of a data TLB error is _supposed_ to be stored
 | 
			
		||||
	 * in DAR, but it seems that this doesn't happen in some cases, such
 | 
			
		||||
	 * as when the error is due to a dcbi instruction to a page with a
 | 
			
		||||
	 * TLB that doesn't have the changed bit set.  In such cases, there
 | 
			
		||||
	 * does not appear to be any way  to recover the EA of the error
 | 
			
		||||
	 * since it is neither in DAR nor MD_EPN.  As a workaround, the
 | 
			
		||||
	 * _PAGE_HWWRITE bit is set for all kernel data pages when the PTEs
 | 
			
		||||
	 * are initialized in mapin_ram().  This will avoid the problem,
 | 
			
		||||
	 * assuming we only use the dcbi instruction on kernel addresses.
 | 
			
		||||
	 */
 | 
			
		||||
	mfspr	r10, SPRN_DAR
 | 
			
		||||
	rlwinm	r11, r10, 0, 0, 19
 | 
			
		||||
	ori	r11, r11, MD_EVALID
 | 
			
		||||
	mfspr	r10, SPRN_M_CASID
 | 
			
		||||
	rlwimi	r11, r10, 0, 28, 31
 | 
			
		||||
	DO_8xx_CPU6(0x3780, r3)
 | 
			
		||||
	mtspr	SPRN_MD_EPN, r11
 | 
			
		||||
 | 
			
		||||
	mfspr	r10, SPRN_M_TWB	/* Get level 1 table entry address */
 | 
			
		||||
 | 
			
		||||
	/* If we are faulting a kernel address, we have to use the
 | 
			
		||||
	 * kernel page tables.
 | 
			
		||||
	 */
 | 
			
		||||
	andi.	r11, r10, 0x0800
 | 
			
		||||
	beq	3f
 | 
			
		||||
	lis	r11, swapper_pg_dir@h
 | 
			
		||||
	ori	r11, r11, swapper_pg_dir@l
 | 
			
		||||
	rlwimi	r10, r11, 0, 2, 19
 | 
			
		||||
3:
 | 
			
		||||
	lwz	r11, 0(r10)	/* Get the level 1 entry */
 | 
			
		||||
	rlwinm.	r10, r11,0,0,19	/* Extract page descriptor page address */
 | 
			
		||||
	beq	2f		/* If zero, bail */
 | 
			
		||||
 | 
			
		||||
	/* We have a pte table, so fetch the pte from the table.
 | 
			
		||||
	 */
 | 
			
		||||
	ori	r11, r11, 1		/* Set valid bit in physical L2 page */
 | 
			
		||||
	DO_8xx_CPU6(0x3b80, r3)
 | 
			
		||||
	mtspr	SPRN_MD_TWC, r11		/* Load pte table base address */
 | 
			
		||||
	mfspr	r11, SPRN_MD_TWC		/* ....and get the pte address */
 | 
			
		||||
	lwz	r10, 0(r11)		/* Get the pte */
 | 
			
		||||
 | 
			
		||||
	andi.	r11, r10, _PAGE_RW	/* Is it writeable? */
 | 
			
		||||
	beq	2f			/* Bail out if not */
 | 
			
		||||
 | 
			
		||||
	/* Update 'changed', among others.
 | 
			
		||||
	*/
 | 
			
		||||
	ori	r10, r10, _PAGE_DIRTY|_PAGE_ACCESSED|_PAGE_HWWRITE
 | 
			
		||||
	mfspr	r11, SPRN_MD_TWC		/* Get pte address again */
 | 
			
		||||
	stw	r10, 0(r11)		/* and update pte in table */
 | 
			
		||||
 | 
			
		||||
	/* The Linux PTE won't go exactly into the MMU TLB.
 | 
			
		||||
	 * Software indicator bits 21, 22 and 28 must be clear.
 | 
			
		||||
	 * Software indicator bits 24, 25, 26, and 27 must be
 | 
			
		||||
	 * set.  All other Linux PTE bits control the behavior
 | 
			
		||||
	 * of the MMU.
 | 
			
		||||
	 */
 | 
			
		||||
	li	r11, 0x00f0
 | 
			
		||||
	rlwimi	r10, r11, 0, 24, 28	/* Set 24-27, clear 28 */
 | 
			
		||||
	DO_8xx_CPU6(0x3d80, r3)
 | 
			
		||||
	mtspr	SPRN_MD_RPN, r10	/* Update TLB entry */
 | 
			
		||||
 | 
			
		||||
	mfspr	r10, SPRN_M_TW	/* Restore registers */
 | 
			
		||||
	lwz	r11, 0(r0)
 | 
			
		||||
	mtcr	r11
 | 
			
		||||
	lwz	r11, 4(r0)
 | 
			
		||||
#ifdef CONFIG_8xx_CPU6
 | 
			
		||||
	lwz	r3, 8(r0)
 | 
			
		||||
#endif
 | 
			
		||||
	rfi
 | 
			
		||||
2:
 | 
			
		||||
	mfspr	r10, SPRN_M_TW	/* Restore registers */
 | 
			
		||||
	lwz	r11, 0(r0)
 | 
			
		||||
	mtcr	r11
 | 
			
		||||
	lwz	r11, 4(r0)
 | 
			
		||||
#ifdef CONFIG_8xx_CPU6
 | 
			
		||||
	lwz	r3, 8(r0)
 | 
			
		||||
#endif
 | 
			
		||||
	b	DataAccess
 | 
			
		||||
 | 
			
		||||
	EXCEPTION(0x1500, Trap_15, UnknownException, EXC_XFER_EE)
 | 
			
		||||
	EXCEPTION(0x1600, Trap_16, UnknownException, EXC_XFER_EE)
 | 
			
		||||
	EXCEPTION(0x1700, Trap_17, UnknownException, EXC_XFER_EE)
 | 
			
		||||
	EXCEPTION(0x1800, Trap_18, UnknownException, EXC_XFER_EE)
 | 
			
		||||
	EXCEPTION(0x1900, Trap_19, UnknownException, EXC_XFER_EE)
 | 
			
		||||
	EXCEPTION(0x1a00, Trap_1a, UnknownException, EXC_XFER_EE)
 | 
			
		||||
	EXCEPTION(0x1b00, Trap_1b, UnknownException, EXC_XFER_EE)
 | 
			
		||||
 | 
			
		||||
/* On the MPC8xx, these next four traps are used for development
 | 
			
		||||
 * support of breakpoints and such.  Someday I will get around to
 | 
			
		||||
 * using them.
 | 
			
		||||
 */
 | 
			
		||||
	EXCEPTION(0x1c00, Trap_1c, UnknownException, EXC_XFER_EE)
 | 
			
		||||
	EXCEPTION(0x1d00, Trap_1d, UnknownException, EXC_XFER_EE)
 | 
			
		||||
	EXCEPTION(0x1e00, Trap_1e, UnknownException, EXC_XFER_EE)
 | 
			
		||||
	EXCEPTION(0x1f00, Trap_1f, UnknownException, EXC_XFER_EE)
 | 
			
		||||
 | 
			
		||||
	. = 0x2000
 | 
			
		||||
 | 
			
		||||
	.globl	giveup_fpu
 | 
			
		||||
giveup_fpu:
 | 
			
		||||
	blr
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * This is where the main kernel code starts.
 | 
			
		||||
 */
 | 
			
		||||
start_here:
 | 
			
		||||
	/* ptr to current */
 | 
			
		||||
	lis	r2,init_task@h
 | 
			
		||||
	ori	r2,r2,init_task@l
 | 
			
		||||
 | 
			
		||||
	/* ptr to phys current thread */
 | 
			
		||||
	tophys(r4,r2)
 | 
			
		||||
	addi	r4,r4,THREAD	/* init task's THREAD */
 | 
			
		||||
	mtspr	SPRN_SPRG3,r4
 | 
			
		||||
	li	r3,0
 | 
			
		||||
	mtspr	SPRN_SPRG2,r3	/* 0 => r1 has kernel sp */
 | 
			
		||||
 | 
			
		||||
	/* stack */
 | 
			
		||||
	lis	r1,init_thread_union@ha
 | 
			
		||||
	addi	r1,r1,init_thread_union@l
 | 
			
		||||
	li	r0,0
 | 
			
		||||
	stwu	r0,THREAD_SIZE-STACK_FRAME_OVERHEAD(r1)
 | 
			
		||||
 | 
			
		||||
	bl	early_init	/* We have to do this with MMU on */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Decide what sort of machine this is and initialize the MMU.
 | 
			
		||||
 */
 | 
			
		||||
	mr	r3,r31
 | 
			
		||||
	mr	r4,r30
 | 
			
		||||
	mr	r5,r29
 | 
			
		||||
	mr	r6,r28
 | 
			
		||||
	mr	r7,r27
 | 
			
		||||
	bl	machine_init
 | 
			
		||||
	bl	MMU_init
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Go back to running unmapped so we can load up new values
 | 
			
		||||
 * and change to using our exception vectors.
 | 
			
		||||
 * On the 8xx, all we have to do is invalidate the TLB to clear
 | 
			
		||||
 * the old 8M byte TLB mappings and load the page table base register.
 | 
			
		||||
 */
 | 
			
		||||
	/* The right way to do this would be to track it down through
 | 
			
		||||
	 * init's THREAD like the context switch code does, but this is
 | 
			
		||||
	 * easier......until someone changes init's static structures.
 | 
			
		||||
	 */
 | 
			
		||||
	lis	r6, swapper_pg_dir@h
 | 
			
		||||
	ori	r6, r6, swapper_pg_dir@l
 | 
			
		||||
	tophys(r6,r6)
 | 
			
		||||
#ifdef CONFIG_8xx_CPU6
 | 
			
		||||
	lis	r4, cpu6_errata_word@h
 | 
			
		||||
	ori	r4, r4, cpu6_errata_word@l
 | 
			
		||||
	li	r3, 0x3980
 | 
			
		||||
	stw	r3, 12(r4)
 | 
			
		||||
	lwz	r3, 12(r4)
 | 
			
		||||
#endif
 | 
			
		||||
	mtspr	SPRN_M_TWB, r6
 | 
			
		||||
	lis	r4,2f@h
 | 
			
		||||
	ori	r4,r4,2f@l
 | 
			
		||||
	tophys(r4,r4)
 | 
			
		||||
	li	r3,MSR_KERNEL & ~(MSR_IR|MSR_DR)
 | 
			
		||||
	mtspr	SPRN_SRR0,r4
 | 
			
		||||
	mtspr	SPRN_SRR1,r3
 | 
			
		||||
	rfi
 | 
			
		||||
/* Load up the kernel context */
 | 
			
		||||
2:
 | 
			
		||||
	SYNC			/* Force all PTE updates to finish */
 | 
			
		||||
	tlbia			/* Clear all TLB entries */
 | 
			
		||||
	sync			/* wait for tlbia/tlbie to finish */
 | 
			
		||||
	TLBSYNC			/* ... on all CPUs */
 | 
			
		||||
 | 
			
		||||
	/* set up the PTE pointers for the Abatron bdiGDB.
 | 
			
		||||
	*/
 | 
			
		||||
	tovirt(r6,r6)
 | 
			
		||||
	lis	r5, abatron_pteptrs@h
 | 
			
		||||
	ori	r5, r5, abatron_pteptrs@l
 | 
			
		||||
	stw	r5, 0xf0(r0)	/* Must match your Abatron config file */
 | 
			
		||||
	tophys(r5,r5)
 | 
			
		||||
	stw	r6, 0(r5)
 | 
			
		||||
 | 
			
		||||
/* Now turn on the MMU for real! */
 | 
			
		||||
	li	r4,MSR_KERNEL
 | 
			
		||||
	lis	r3,start_kernel@h
 | 
			
		||||
	ori	r3,r3,start_kernel@l
 | 
			
		||||
	mtspr	SPRN_SRR0,r3
 | 
			
		||||
	mtspr	SPRN_SRR1,r4
 | 
			
		||||
	rfi			/* enable MMU and jump to start_kernel */
 | 
			
		||||
 | 
			
		||||
/* Set up the initial MMU state so we can do the first level of
 | 
			
		||||
 * kernel initialization.  This maps the first 8 MBytes of memory 1:1
 | 
			
		||||
 * virtual to physical.  Also, set the cache mode since that is defined
 | 
			
		||||
 * by TLB entries and perform any additional mapping (like of the IMMR).
 | 
			
		||||
 * If configured to pin some TLBs, we pin the first 8 Mbytes of kernel,
 | 
			
		||||
 * 24 Mbytes of data, and the 8M IMMR space.  Anything not covered by
 | 
			
		||||
 * these mappings is mapped by page tables.
 | 
			
		||||
 */
 | 
			
		||||
initial_mmu:
 | 
			
		||||
	tlbia			/* Invalidate all TLB entries */
 | 
			
		||||
#ifdef CONFIG_PIN_TLB
 | 
			
		||||
	lis	r8, MI_RSV4I@h
 | 
			
		||||
	ori	r8, r8, 0x1c00
 | 
			
		||||
#else
 | 
			
		||||
	li	r8, 0
 | 
			
		||||
#endif
 | 
			
		||||
	mtspr	SPRN_MI_CTR, r8	/* Set instruction MMU control */
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_PIN_TLB
 | 
			
		||||
	lis	r10, (MD_RSV4I | MD_RESETVAL)@h
 | 
			
		||||
	ori	r10, r10, 0x1c00
 | 
			
		||||
	mr	r8, r10
 | 
			
		||||
#else
 | 
			
		||||
	lis	r10, MD_RESETVAL@h
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef CONFIG_8xx_COPYBACK
 | 
			
		||||
	oris	r10, r10, MD_WTDEF@h
 | 
			
		||||
#endif
 | 
			
		||||
	mtspr	SPRN_MD_CTR, r10	/* Set data TLB control */
 | 
			
		||||
 | 
			
		||||
	/* Now map the lower 8 Meg into the TLBs.  For this quick hack,
 | 
			
		||||
	 * we can load the instruction and data TLB registers with the
 | 
			
		||||
	 * same values.
 | 
			
		||||
	 */
 | 
			
		||||
	lis	r8, KERNELBASE@h	/* Create vaddr for TLB */
 | 
			
		||||
	ori	r8, r8, MI_EVALID	/* Mark it valid */
 | 
			
		||||
	mtspr	SPRN_MI_EPN, r8
 | 
			
		||||
	mtspr	SPRN_MD_EPN, r8
 | 
			
		||||
	li	r8, MI_PS8MEG		/* Set 8M byte page */
 | 
			
		||||
	ori	r8, r8, MI_SVALID	/* Make it valid */
 | 
			
		||||
	mtspr	SPRN_MI_TWC, r8
 | 
			
		||||
	mtspr	SPRN_MD_TWC, r8
 | 
			
		||||
	li	r8, MI_BOOTINIT		/* Create RPN for address 0 */
 | 
			
		||||
	mtspr	SPRN_MI_RPN, r8		/* Store TLB entry */
 | 
			
		||||
	mtspr	SPRN_MD_RPN, r8
 | 
			
		||||
	lis	r8, MI_Kp@h		/* Set the protection mode */
 | 
			
		||||
	mtspr	SPRN_MI_AP, r8
 | 
			
		||||
	mtspr	SPRN_MD_AP, r8
 | 
			
		||||
 | 
			
		||||
	/* Map another 8 MByte at the IMMR to get the processor
 | 
			
		||||
	 * internal registers (among other things).
 | 
			
		||||
	 */
 | 
			
		||||
#ifdef CONFIG_PIN_TLB
 | 
			
		||||
	addi	r10, r10, 0x0100
 | 
			
		||||
	mtspr	SPRN_MD_CTR, r10
 | 
			
		||||
#endif
 | 
			
		||||
	mfspr	r9, 638			/* Get current IMMR */
 | 
			
		||||
	andis.	r9, r9, 0xff80		/* Get 8Mbyte boundary */
 | 
			
		||||
 | 
			
		||||
	mr	r8, r9			/* Create vaddr for TLB */
 | 
			
		||||
	ori	r8, r8, MD_EVALID	/* Mark it valid */
 | 
			
		||||
	mtspr	SPRN_MD_EPN, r8
 | 
			
		||||
	li	r8, MD_PS8MEG		/* Set 8M byte page */
 | 
			
		||||
	ori	r8, r8, MD_SVALID	/* Make it valid */
 | 
			
		||||
	mtspr	SPRN_MD_TWC, r8
 | 
			
		||||
	mr	r8, r9			/* Create paddr for TLB */
 | 
			
		||||
	ori	r8, r8, MI_BOOTINIT|0x2 /* Inhibit cache -- Cort */
 | 
			
		||||
	mtspr	SPRN_MD_RPN, r8
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_PIN_TLB
 | 
			
		||||
	/* Map two more 8M kernel data pages.
 | 
			
		||||
	*/
 | 
			
		||||
	addi	r10, r10, 0x0100
 | 
			
		||||
	mtspr	SPRN_MD_CTR, r10
 | 
			
		||||
 | 
			
		||||
	lis	r8, KERNELBASE@h	/* Create vaddr for TLB */
 | 
			
		||||
	addis	r8, r8, 0x0080		/* Add 8M */
 | 
			
		||||
	ori	r8, r8, MI_EVALID	/* Mark it valid */
 | 
			
		||||
	mtspr	SPRN_MD_EPN, r8
 | 
			
		||||
	li	r9, MI_PS8MEG		/* Set 8M byte page */
 | 
			
		||||
	ori	r9, r9, MI_SVALID	/* Make it valid */
 | 
			
		||||
	mtspr	SPRN_MD_TWC, r9
 | 
			
		||||
	li	r11, MI_BOOTINIT	/* Create RPN for address 0 */
 | 
			
		||||
	addis	r11, r11, 0x0080	/* Add 8M */
 | 
			
		||||
	mtspr	SPRN_MD_RPN, r8
 | 
			
		||||
 | 
			
		||||
	addis	r8, r8, 0x0080		/* Add 8M */
 | 
			
		||||
	mtspr	SPRN_MD_EPN, r8
 | 
			
		||||
	mtspr	SPRN_MD_TWC, r9
 | 
			
		||||
	addis	r11, r11, 0x0080	/* Add 8M */
 | 
			
		||||
	mtspr	SPRN_MD_RPN, r8
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	/* Since the cache is enabled according to the information we
 | 
			
		||||
	 * just loaded into the TLB, invalidate and enable the caches here.
 | 
			
		||||
	 * We should probably check/set other modes....later.
 | 
			
		||||
	 */
 | 
			
		||||
	lis	r8, IDC_INVALL@h
 | 
			
		||||
	mtspr	SPRN_IC_CST, r8
 | 
			
		||||
	mtspr	SPRN_DC_CST, r8
 | 
			
		||||
	lis	r8, IDC_ENABLE@h
 | 
			
		||||
	mtspr	SPRN_IC_CST, r8
 | 
			
		||||
#ifdef CONFIG_8xx_COPYBACK
 | 
			
		||||
	mtspr	SPRN_DC_CST, r8
 | 
			
		||||
#else
 | 
			
		||||
	/* For a debug option, I left this here to easily enable
 | 
			
		||||
	 * the write through cache mode
 | 
			
		||||
	 */
 | 
			
		||||
	lis	r8, DC_SFWT@h
 | 
			
		||||
	mtspr	SPRN_DC_CST, r8
 | 
			
		||||
	lis	r8, IDC_ENABLE@h
 | 
			
		||||
	mtspr	SPRN_DC_CST, r8
 | 
			
		||||
#endif
 | 
			
		||||
	blr
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Set up to use a given MMU context.
 | 
			
		||||
 * r3 is context number, r4 is PGD pointer.
 | 
			
		||||
 *
 | 
			
		||||
 * We place the physical address of the new task page directory loaded
 | 
			
		||||
 * into the MMU base register, and set the ASID compare register with
 | 
			
		||||
 * the new "context."
 | 
			
		||||
 */
 | 
			
		||||
_GLOBAL(set_context)
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_BDI_SWITCH
 | 
			
		||||
	/* Context switch the PTE pointer for the Abatron BDI2000.
 | 
			
		||||
	 * The PGDIR is passed as second argument.
 | 
			
		||||
	 */
 | 
			
		||||
	lis	r5, KERNELBASE@h
 | 
			
		||||
	lwz	r5, 0xf0(r5)
 | 
			
		||||
	stw	r4, 0x4(r5)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_8xx_CPU6
 | 
			
		||||
	lis	r6, cpu6_errata_word@h
 | 
			
		||||
	ori	r6, r6, cpu6_errata_word@l
 | 
			
		||||
	tophys	(r4, r4)
 | 
			
		||||
	li	r7, 0x3980
 | 
			
		||||
	stw	r7, 12(r6)
 | 
			
		||||
	lwz	r7, 12(r6)
 | 
			
		||||
        mtspr   SPRN_M_TWB, r4               /* Update MMU base address */
 | 
			
		||||
	li	r7, 0x3380
 | 
			
		||||
	stw	r7, 12(r6)
 | 
			
		||||
	lwz	r7, 12(r6)
 | 
			
		||||
        mtspr   SPRN_M_CASID, r3             /* Update context */
 | 
			
		||||
#else
 | 
			
		||||
        mtspr   SPRN_M_CASID,r3		/* Update context */
 | 
			
		||||
	tophys	(r4, r4)
 | 
			
		||||
	mtspr	SPRN_M_TWB, r4		/* and pgd */
 | 
			
		||||
#endif
 | 
			
		||||
	SYNC
 | 
			
		||||
	blr
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_8xx_CPU6
 | 
			
		||||
/* It's here because it is unique to the 8xx.
 | 
			
		||||
 * It is important we get called with interrupts disabled.  I used to
 | 
			
		||||
 * do that, but it appears that all code that calls this already had
 | 
			
		||||
 * interrupt disabled.
 | 
			
		||||
 */
 | 
			
		||||
	.globl	set_dec_cpu6
 | 
			
		||||
set_dec_cpu6:
 | 
			
		||||
	lis	r7, cpu6_errata_word@h
 | 
			
		||||
	ori	r7, r7, cpu6_errata_word@l
 | 
			
		||||
	li	r4, 0x2c00
 | 
			
		||||
	stw	r4, 8(r7)
 | 
			
		||||
	lwz	r4, 8(r7)
 | 
			
		||||
        mtspr   22, r3		/* Update Decrementer */
 | 
			
		||||
	SYNC
 | 
			
		||||
	blr
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * We put a few things here that have to be page-aligned.
 | 
			
		||||
 * This stuff goes at the beginning of the data segment,
 | 
			
		||||
 * which is page-aligned.
 | 
			
		||||
 */
 | 
			
		||||
	.data
 | 
			
		||||
	.globl	sdata
 | 
			
		||||
sdata:
 | 
			
		||||
	.globl	empty_zero_page
 | 
			
		||||
empty_zero_page:
 | 
			
		||||
	.space	4096
 | 
			
		||||
 | 
			
		||||
	.globl	swapper_pg_dir
 | 
			
		||||
swapper_pg_dir:
 | 
			
		||||
	.space	4096
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * This space gets a copy of optional info passed to us by the bootstrap
 | 
			
		||||
 * Used to pass parameters into the kernel like root=/dev/sda1, etc.
 | 
			
		||||
 */
 | 
			
		||||
	.globl	cmd_line
 | 
			
		||||
cmd_line:
 | 
			
		||||
	.space	512
 | 
			
		||||
 | 
			
		||||
/* Room for two PTE table poiners, usually the kernel and current user
 | 
			
		||||
 * pointer to their respective root page table (pgdir).
 | 
			
		||||
 */
 | 
			
		||||
abatron_pteptrs:
 | 
			
		||||
	.space	8
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_8xx_CPU6
 | 
			
		||||
	.globl	cpu6_errata_word
 | 
			
		||||
cpu6_errata_word:
 | 
			
		||||
	.space	16
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1058
									
								
								arch/powerpc/kernel/head_fsl_booke.S
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1058
									
								
								arch/powerpc/kernel/head_fsl_booke.S
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										233
									
								
								arch/powerpc/kernel/idle_6xx.S
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										233
									
								
								arch/powerpc/kernel/idle_6xx.S
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,233 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  This file contains the power_save function for 6xx & 7xxx CPUs
 | 
			
		||||
 *  rewritten in assembler
 | 
			
		||||
 *
 | 
			
		||||
 *  Warning ! This code assumes that if your machine has a 750fx
 | 
			
		||||
 *  it will have PLL 1 set to low speed mode (used during NAP/DOZE).
 | 
			
		||||
 *  if this is not the case some additional changes will have to
 | 
			
		||||
 *  be done to check a runtime var (a bit like powersave-nap)
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or
 | 
			
		||||
 *  modify it under the terms of the GNU General Public License
 | 
			
		||||
 *  as published by the Free Software Foundation; either version
 | 
			
		||||
 *  2 of the License, or (at your option) any later version.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/config.h>
 | 
			
		||||
#include <linux/threads.h>
 | 
			
		||||
#include <asm/processor.h>
 | 
			
		||||
#include <asm/page.h>
 | 
			
		||||
#include <asm/cputable.h>
 | 
			
		||||
#include <asm/thread_info.h>
 | 
			
		||||
#include <asm/ppc_asm.h>
 | 
			
		||||
#include <asm/asm-offsets.h>
 | 
			
		||||
 | 
			
		||||
#undef DEBUG
 | 
			
		||||
 | 
			
		||||
	.text
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Init idle, called at early CPU setup time from head.S for each CPU
 | 
			
		||||
 * Make sure no rest of NAP mode remains in HID0, save default
 | 
			
		||||
 * values for some CPU specific registers. Called with r24
 | 
			
		||||
 * containing CPU number and r3 reloc offset
 | 
			
		||||
 */
 | 
			
		||||
_GLOBAL(init_idle_6xx)
 | 
			
		||||
BEGIN_FTR_SECTION
 | 
			
		||||
	mfspr	r4,SPRN_HID0
 | 
			
		||||
	rlwinm	r4,r4,0,10,8	/* Clear NAP */
 | 
			
		||||
	mtspr	SPRN_HID0, r4
 | 
			
		||||
	b	1f
 | 
			
		||||
END_FTR_SECTION_IFSET(CPU_FTR_CAN_NAP)
 | 
			
		||||
	blr
 | 
			
		||||
1:
 | 
			
		||||
	slwi	r5,r24,2
 | 
			
		||||
	add	r5,r5,r3
 | 
			
		||||
BEGIN_FTR_SECTION
 | 
			
		||||
	mfspr	r4,SPRN_MSSCR0
 | 
			
		||||
	addis	r6,r5, nap_save_msscr0@ha
 | 
			
		||||
	stw	r4,nap_save_msscr0@l(r6)
 | 
			
		||||
END_FTR_SECTION_IFSET(CPU_FTR_NAP_DISABLE_L2_PR)
 | 
			
		||||
BEGIN_FTR_SECTION
 | 
			
		||||
	mfspr	r4,SPRN_HID1
 | 
			
		||||
	addis	r6,r5,nap_save_hid1@ha
 | 
			
		||||
	stw	r4,nap_save_hid1@l(r6)
 | 
			
		||||
END_FTR_SECTION_IFSET(CPU_FTR_DUAL_PLL_750FX)
 | 
			
		||||
	blr
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Here is the power_save_6xx function. This could eventually be
 | 
			
		||||
 * split into several functions & changing the function pointer
 | 
			
		||||
 * depending on the various features.
 | 
			
		||||
 */
 | 
			
		||||
_GLOBAL(ppc6xx_idle)
 | 
			
		||||
	/* Check if we can nap or doze, put HID0 mask in r3
 | 
			
		||||
	 */
 | 
			
		||||
	lis	r3, 0
 | 
			
		||||
BEGIN_FTR_SECTION
 | 
			
		||||
	lis	r3,HID0_DOZE@h
 | 
			
		||||
END_FTR_SECTION_IFSET(CPU_FTR_CAN_DOZE)
 | 
			
		||||
BEGIN_FTR_SECTION
 | 
			
		||||
	/* We must dynamically check for the NAP feature as it
 | 
			
		||||
	 * can be cleared by CPU init after the fixups are done
 | 
			
		||||
	 */
 | 
			
		||||
	lis	r4,cur_cpu_spec@ha
 | 
			
		||||
	lwz	r4,cur_cpu_spec@l(r4)
 | 
			
		||||
	lwz	r4,CPU_SPEC_FEATURES(r4)
 | 
			
		||||
	andi.	r0,r4,CPU_FTR_CAN_NAP
 | 
			
		||||
	beq	1f
 | 
			
		||||
	/* Now check if user or arch enabled NAP mode */
 | 
			
		||||
	lis	r4,powersave_nap@ha
 | 
			
		||||
	lwz	r4,powersave_nap@l(r4)
 | 
			
		||||
	cmpwi	0,r4,0
 | 
			
		||||
	beq	1f
 | 
			
		||||
	lis	r3,HID0_NAP@h
 | 
			
		||||
1:	
 | 
			
		||||
END_FTR_SECTION_IFSET(CPU_FTR_CAN_NAP)
 | 
			
		||||
	cmpwi	0,r3,0
 | 
			
		||||
	beqlr
 | 
			
		||||
 | 
			
		||||
	/* Clear MSR:EE */
 | 
			
		||||
	mfmsr	r7
 | 
			
		||||
	rlwinm	r0,r7,0,17,15
 | 
			
		||||
	mtmsr	r0
 | 
			
		||||
 | 
			
		||||
	/* Check current_thread_info()->flags */
 | 
			
		||||
	rlwinm	r4,r1,0,0,18
 | 
			
		||||
	lwz	r4,TI_FLAGS(r4)
 | 
			
		||||
	andi.	r0,r4,_TIF_NEED_RESCHED
 | 
			
		||||
	beq	1f
 | 
			
		||||
	mtmsr	r7	/* out of line this ? */
 | 
			
		||||
	blr
 | 
			
		||||
1:	
 | 
			
		||||
	/* Some pre-nap cleanups needed on some CPUs */
 | 
			
		||||
	andis.	r0,r3,HID0_NAP@h
 | 
			
		||||
	beq	2f
 | 
			
		||||
BEGIN_FTR_SECTION
 | 
			
		||||
	/* Disable L2 prefetch on some 745x and try to ensure
 | 
			
		||||
	 * L2 prefetch engines are idle. As explained by errata
 | 
			
		||||
	 * text, we can't be sure they are, we just hope very hard
 | 
			
		||||
	 * that well be enough (sic !). At least I noticed Apple
 | 
			
		||||
	 * doesn't even bother doing the dcbf's here...
 | 
			
		||||
	 */
 | 
			
		||||
	mfspr	r4,SPRN_MSSCR0
 | 
			
		||||
	rlwinm	r4,r4,0,0,29
 | 
			
		||||
	sync
 | 
			
		||||
	mtspr	SPRN_MSSCR0,r4
 | 
			
		||||
	sync
 | 
			
		||||
	isync
 | 
			
		||||
	lis	r4,KERNELBASE@h
 | 
			
		||||
	dcbf	0,r4
 | 
			
		||||
	dcbf	0,r4
 | 
			
		||||
	dcbf	0,r4
 | 
			
		||||
	dcbf	0,r4
 | 
			
		||||
END_FTR_SECTION_IFSET(CPU_FTR_NAP_DISABLE_L2_PR)
 | 
			
		||||
#ifdef DEBUG
 | 
			
		||||
	lis	r6,nap_enter_count@ha
 | 
			
		||||
	lwz	r4,nap_enter_count@l(r6)
 | 
			
		||||
	addi	r4,r4,1
 | 
			
		||||
	stw	r4,nap_enter_count@l(r6)
 | 
			
		||||
#endif	
 | 
			
		||||
2:
 | 
			
		||||
BEGIN_FTR_SECTION
 | 
			
		||||
	/* Go to low speed mode on some 750FX */
 | 
			
		||||
	lis	r4,powersave_lowspeed@ha
 | 
			
		||||
	lwz	r4,powersave_lowspeed@l(r4)
 | 
			
		||||
	cmpwi	0,r4,0
 | 
			
		||||
	beq	1f
 | 
			
		||||
	mfspr	r4,SPRN_HID1
 | 
			
		||||
	oris	r4,r4,0x0001
 | 
			
		||||
	mtspr	SPRN_HID1,r4
 | 
			
		||||
1:	
 | 
			
		||||
END_FTR_SECTION_IFSET(CPU_FTR_DUAL_PLL_750FX)
 | 
			
		||||
 | 
			
		||||
	/* Go to NAP or DOZE now */	
 | 
			
		||||
	mfspr	r4,SPRN_HID0
 | 
			
		||||
	lis	r5,(HID0_NAP|HID0_SLEEP)@h
 | 
			
		||||
BEGIN_FTR_SECTION
 | 
			
		||||
	oris	r5,r5,HID0_DOZE@h
 | 
			
		||||
END_FTR_SECTION_IFSET(CPU_FTR_CAN_DOZE)
 | 
			
		||||
	andc	r4,r4,r5
 | 
			
		||||
	or	r4,r4,r3
 | 
			
		||||
BEGIN_FTR_SECTION
 | 
			
		||||
	oris	r4,r4,HID0_DPM@h	/* that should be done once for all  */
 | 
			
		||||
END_FTR_SECTION_IFCLR(CPU_FTR_NO_DPM)
 | 
			
		||||
	mtspr	SPRN_HID0,r4
 | 
			
		||||
BEGIN_FTR_SECTION
 | 
			
		||||
	DSSALL
 | 
			
		||||
	sync
 | 
			
		||||
END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC)
 | 
			
		||||
	ori	r7,r7,MSR_EE /* Could be ommited (already set) */
 | 
			
		||||
	oris	r7,r7,MSR_POW@h
 | 
			
		||||
	sync
 | 
			
		||||
	isync
 | 
			
		||||
	mtmsr	r7
 | 
			
		||||
	isync
 | 
			
		||||
	sync
 | 
			
		||||
	blr
 | 
			
		||||
	
 | 
			
		||||
/*
 | 
			
		||||
 * Return from NAP/DOZE mode, restore some CPU specific registers,
 | 
			
		||||
 * we are called with DR/IR still off and r2 containing physical
 | 
			
		||||
 * address of current.
 | 
			
		||||
 */
 | 
			
		||||
_GLOBAL(power_save_6xx_restore)
 | 
			
		||||
	mfspr	r11,SPRN_HID0
 | 
			
		||||
	rlwinm.	r11,r11,0,10,8	/* Clear NAP & copy NAP bit !state to cr1 EQ */
 | 
			
		||||
	cror	4*cr1+eq,4*cr0+eq,4*cr0+eq
 | 
			
		||||
BEGIN_FTR_SECTION
 | 
			
		||||
	rlwinm	r11,r11,0,9,7	/* Clear DOZE */
 | 
			
		||||
END_FTR_SECTION_IFSET(CPU_FTR_CAN_DOZE)
 | 
			
		||||
	mtspr	SPRN_HID0, r11
 | 
			
		||||
 | 
			
		||||
#ifdef DEBUG
 | 
			
		||||
	beq	cr1,1f
 | 
			
		||||
	lis	r11,(nap_return_count-KERNELBASE)@ha
 | 
			
		||||
	lwz	r9,nap_return_count@l(r11)
 | 
			
		||||
	addi	r9,r9,1
 | 
			
		||||
	stw	r9,nap_return_count@l(r11)
 | 
			
		||||
1:
 | 
			
		||||
#endif
 | 
			
		||||
	
 | 
			
		||||
	rlwinm	r9,r1,0,0,18
 | 
			
		||||
	tophys(r9,r9)
 | 
			
		||||
	lwz	r11,TI_CPU(r9)
 | 
			
		||||
	slwi	r11,r11,2
 | 
			
		||||
	/* Todo make sure all these are in the same page
 | 
			
		||||
	 * and load r22 (@ha part + CPU offset) only once
 | 
			
		||||
	 */
 | 
			
		||||
BEGIN_FTR_SECTION
 | 
			
		||||
	beq	cr1,1f
 | 
			
		||||
	addis	r9,r11,(nap_save_msscr0-KERNELBASE)@ha
 | 
			
		||||
	lwz	r9,nap_save_msscr0@l(r9)
 | 
			
		||||
	mtspr	SPRN_MSSCR0, r9
 | 
			
		||||
	sync
 | 
			
		||||
	isync
 | 
			
		||||
1:
 | 
			
		||||
END_FTR_SECTION_IFSET(CPU_FTR_NAP_DISABLE_L2_PR)
 | 
			
		||||
BEGIN_FTR_SECTION
 | 
			
		||||
	addis	r9,r11,(nap_save_hid1-KERNELBASE)@ha
 | 
			
		||||
	lwz	r9,nap_save_hid1@l(r9)
 | 
			
		||||
	mtspr	SPRN_HID1, r9
 | 
			
		||||
END_FTR_SECTION_IFSET(CPU_FTR_DUAL_PLL_750FX)
 | 
			
		||||
	b	transfer_to_handler_cont
 | 
			
		||||
 | 
			
		||||
	.data
 | 
			
		||||
 | 
			
		||||
_GLOBAL(nap_save_msscr0)
 | 
			
		||||
	.space	4*NR_CPUS
 | 
			
		||||
 | 
			
		||||
_GLOBAL(nap_save_hid1)
 | 
			
		||||
	.space	4*NR_CPUS
 | 
			
		||||
 | 
			
		||||
_GLOBAL(powersave_nap)
 | 
			
		||||
	.long	0
 | 
			
		||||
_GLOBAL(powersave_lowspeed)
 | 
			
		||||
	.long	0
 | 
			
		||||
 | 
			
		||||
#ifdef DEBUG
 | 
			
		||||
_GLOBAL(nap_enter_count)
 | 
			
		||||
	.space	4
 | 
			
		||||
_GLOBAL(nap_return_count)
 | 
			
		||||
	.space	4
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										724
									
								
								arch/powerpc/kernel/process.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										724
									
								
								arch/powerpc/kernel/process.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,724 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  arch/ppc/kernel/process.c
 | 
			
		||||
 *
 | 
			
		||||
 *  Derived from "arch/i386/kernel/process.c"
 | 
			
		||||
 *    Copyright (C) 1995  Linus Torvalds
 | 
			
		||||
 *
 | 
			
		||||
 *  Updated and modified by Cort Dougan (cort@cs.nmt.edu) and
 | 
			
		||||
 *  Paul Mackerras (paulus@cs.anu.edu.au)
 | 
			
		||||
 *
 | 
			
		||||
 *  PowerPC version
 | 
			
		||||
 *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or
 | 
			
		||||
 *  modify it under the terms of the GNU General Public License
 | 
			
		||||
 *  as published by the Free Software Foundation; either version
 | 
			
		||||
 *  2 of the License, or (at your option) any later version.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/config.h>
 | 
			
		||||
#include <linux/errno.h>
 | 
			
		||||
#include <linux/sched.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/mm.h>
 | 
			
		||||
#include <linux/smp.h>
 | 
			
		||||
#include <linux/smp_lock.h>
 | 
			
		||||
#include <linux/stddef.h>
 | 
			
		||||
#include <linux/unistd.h>
 | 
			
		||||
#include <linux/ptrace.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
#include <linux/user.h>
 | 
			
		||||
#include <linux/elf.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/prctl.h>
 | 
			
		||||
#include <linux/init_task.h>
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/kallsyms.h>
 | 
			
		||||
#include <linux/mqueue.h>
 | 
			
		||||
#include <linux/hardirq.h>
 | 
			
		||||
 | 
			
		||||
#include <asm/pgtable.h>
 | 
			
		||||
#include <asm/uaccess.h>
 | 
			
		||||
#include <asm/system.h>
 | 
			
		||||
#include <asm/io.h>
 | 
			
		||||
#include <asm/processor.h>
 | 
			
		||||
#include <asm/mmu.h>
 | 
			
		||||
#include <asm/prom.h>
 | 
			
		||||
 | 
			
		||||
extern unsigned long _get_SP(void);
 | 
			
		||||
 | 
			
		||||
#ifndef CONFIG_SMP
 | 
			
		||||
struct task_struct *last_task_used_math = NULL;
 | 
			
		||||
struct task_struct *last_task_used_altivec = NULL;
 | 
			
		||||
struct task_struct *last_task_used_spe = NULL;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static struct fs_struct init_fs = INIT_FS;
 | 
			
		||||
static struct files_struct init_files = INIT_FILES;
 | 
			
		||||
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);
 | 
			
		||||
EXPORT_SYMBOL(init_mm);
 | 
			
		||||
 | 
			
		||||
/* this is 8kB-aligned so we can get to the thread_info struct
 | 
			
		||||
   at the base of it from the stack pointer with 1 integer instruction. */
 | 
			
		||||
union thread_union init_thread_union
 | 
			
		||||
	__attribute__((__section__(".data.init_task"))) =
 | 
			
		||||
{ INIT_THREAD_INFO(init_task) };
 | 
			
		||||
 | 
			
		||||
/* initial task structure */
 | 
			
		||||
struct task_struct init_task = INIT_TASK(init_task);
 | 
			
		||||
EXPORT_SYMBOL(init_task);
 | 
			
		||||
 | 
			
		||||
/* only used to get secondary processor up */
 | 
			
		||||
struct task_struct *current_set[NR_CPUS] = {&init_task, };
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Make sure the floating-point register state in the
 | 
			
		||||
 * the thread_struct is up to date for task tsk.
 | 
			
		||||
 */
 | 
			
		||||
void flush_fp_to_thread(struct task_struct *tsk)
 | 
			
		||||
{
 | 
			
		||||
	if (tsk->thread.regs) {
 | 
			
		||||
		/*
 | 
			
		||||
		 * We need to disable preemption here because if we didn't,
 | 
			
		||||
		 * another process could get scheduled after the regs->msr
 | 
			
		||||
		 * test but before we have finished saving the FP registers
 | 
			
		||||
		 * to the thread_struct.  That process could take over the
 | 
			
		||||
		 * FPU, and then when we get scheduled again we would store
 | 
			
		||||
		 * bogus values for the remaining FP registers.
 | 
			
		||||
		 */
 | 
			
		||||
		preempt_disable();
 | 
			
		||||
		if (tsk->thread.regs->msr & MSR_FP) {
 | 
			
		||||
#ifdef CONFIG_SMP
 | 
			
		||||
			/*
 | 
			
		||||
			 * This should only ever be called for current or
 | 
			
		||||
			 * for a stopped child process.  Since we save away
 | 
			
		||||
			 * the FP register state on context switch on SMP,
 | 
			
		||||
			 * there is something wrong if a stopped child appears
 | 
			
		||||
			 * to still have its FP state in the CPU registers.
 | 
			
		||||
			 */
 | 
			
		||||
			BUG_ON(tsk != current);
 | 
			
		||||
#endif
 | 
			
		||||
			giveup_fpu(current);
 | 
			
		||||
		}
 | 
			
		||||
		preempt_enable();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void enable_kernel_fp(void)
 | 
			
		||||
{
 | 
			
		||||
	WARN_ON(preemptible());
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_SMP
 | 
			
		||||
	if (current->thread.regs && (current->thread.regs->msr & MSR_FP))
 | 
			
		||||
		giveup_fpu(current);
 | 
			
		||||
	else
 | 
			
		||||
		giveup_fpu(NULL);	/* just enables FP for kernel */
 | 
			
		||||
#else
 | 
			
		||||
	giveup_fpu(last_task_used_math);
 | 
			
		||||
#endif /* CONFIG_SMP */
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(enable_kernel_fp);
 | 
			
		||||
 | 
			
		||||
int dump_task_fpu(struct task_struct *tsk, elf_fpregset_t *fpregs)
 | 
			
		||||
{
 | 
			
		||||
	if (!tsk->thread.regs)
 | 
			
		||||
		return 0;
 | 
			
		||||
	flush_fp_to_thread(current);
 | 
			
		||||
 | 
			
		||||
	memcpy(fpregs, &tsk->thread.fpr[0], sizeof(*fpregs));
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_ALTIVEC
 | 
			
		||||
void enable_kernel_altivec(void)
 | 
			
		||||
{
 | 
			
		||||
	WARN_ON(preemptible());
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_SMP
 | 
			
		||||
	if (current->thread.regs && (current->thread.regs->msr & MSR_VEC))
 | 
			
		||||
		giveup_altivec(current);
 | 
			
		||||
	else
 | 
			
		||||
		giveup_altivec(NULL);	/* just enable AltiVec for kernel - force */
 | 
			
		||||
#else
 | 
			
		||||
	giveup_altivec(last_task_used_altivec);
 | 
			
		||||
#endif /* CONFIG_SMP */
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(enable_kernel_altivec);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Make sure the VMX/Altivec register state in the
 | 
			
		||||
 * the thread_struct is up to date for task tsk.
 | 
			
		||||
 */
 | 
			
		||||
void flush_altivec_to_thread(struct task_struct *tsk)
 | 
			
		||||
{
 | 
			
		||||
	if (tsk->thread.regs) {
 | 
			
		||||
		preempt_disable();
 | 
			
		||||
		if (tsk->thread.regs->msr & MSR_VEC) {
 | 
			
		||||
#ifdef CONFIG_SMP
 | 
			
		||||
			BUG_ON(tsk != current);
 | 
			
		||||
#endif
 | 
			
		||||
			giveup_altivec(current);
 | 
			
		||||
		}
 | 
			
		||||
		preempt_enable();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int dump_task_altivec(struct pt_regs *regs, elf_vrregset_t *vrregs)
 | 
			
		||||
{
 | 
			
		||||
	flush_altivec_to_thread(current);
 | 
			
		||||
	memcpy(vrregs, ¤t->thread.vr[0], sizeof(*vrregs));
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
#endif /* CONFIG_ALTIVEC */
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_SPE
 | 
			
		||||
 | 
			
		||||
void enable_kernel_spe(void)
 | 
			
		||||
{
 | 
			
		||||
	WARN_ON(preemptible());
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_SMP
 | 
			
		||||
	if (current->thread.regs && (current->thread.regs->msr & MSR_SPE))
 | 
			
		||||
		giveup_spe(current);
 | 
			
		||||
	else
 | 
			
		||||
		giveup_spe(NULL);	/* just enable SPE for kernel - force */
 | 
			
		||||
#else
 | 
			
		||||
	giveup_spe(last_task_used_spe);
 | 
			
		||||
#endif /* __SMP __ */
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(enable_kernel_spe);
 | 
			
		||||
 | 
			
		||||
void flush_spe_to_thread(struct task_struct *tsk)
 | 
			
		||||
{
 | 
			
		||||
	if (tsk->thread.regs) {
 | 
			
		||||
		preempt_disable();
 | 
			
		||||
		if (tsk->thread.regs->msr & MSR_SPE) {
 | 
			
		||||
#ifdef CONFIG_SMP
 | 
			
		||||
			BUG_ON(tsk != current);
 | 
			
		||||
#endif
 | 
			
		||||
			giveup_spe(current);
 | 
			
		||||
		}
 | 
			
		||||
		preempt_enable();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int dump_spe(struct pt_regs *regs, elf_vrregset_t *evrregs)
 | 
			
		||||
{
 | 
			
		||||
	flush_spe_to_thread(current);
 | 
			
		||||
	/* We copy u32 evr[32] + u64 acc + u32 spefscr -> 35 */
 | 
			
		||||
	memcpy(evrregs, ¤t->thread.evr[0], sizeof(u32) * 35);
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
#endif /* CONFIG_SPE */
 | 
			
		||||
 | 
			
		||||
static void set_dabr_spr(unsigned long val)
 | 
			
		||||
{
 | 
			
		||||
	mtspr(SPRN_DABR, val);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int set_dabr(unsigned long dabr)
 | 
			
		||||
{
 | 
			
		||||
	int ret = 0;
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_PPC64
 | 
			
		||||
	if (firmware_has_feature(FW_FEATURE_XDABR)) {
 | 
			
		||||
		/* We want to catch accesses from kernel and userspace */
 | 
			
		||||
		unsigned long flags = H_DABRX_KERNEL|H_DABRX_USER;
 | 
			
		||||
		ret = plpar_set_xdabr(dabr, flags);
 | 
			
		||||
	} else if (firmware_has_feature(FW_FEATURE_DABR)) {
 | 
			
		||||
		ret = plpar_set_dabr(dabr);
 | 
			
		||||
	} else
 | 
			
		||||
#endif
 | 
			
		||||
		set_dabr_spr(dabr);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static DEFINE_PER_CPU(unsigned long, current_dabr);
 | 
			
		||||
 | 
			
		||||
struct task_struct *__switch_to(struct task_struct *prev,
 | 
			
		||||
	struct task_struct *new)
 | 
			
		||||
{
 | 
			
		||||
	struct thread_struct *new_thread, *old_thread;
 | 
			
		||||
	unsigned long flags;
 | 
			
		||||
	struct task_struct *last;
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_SMP
 | 
			
		||||
	/* avoid complexity of lazy save/restore of fpu
 | 
			
		||||
	 * by just saving it every time we switch out if
 | 
			
		||||
	 * this task used the fpu during the last quantum.
 | 
			
		||||
	 *
 | 
			
		||||
	 * If it tries to use the fpu again, it'll trap and
 | 
			
		||||
	 * reload its fp regs.  So we don't have to do a restore
 | 
			
		||||
	 * every switch, just a save.
 | 
			
		||||
	 *  -- Cort
 | 
			
		||||
	 */
 | 
			
		||||
	if (prev->thread.regs && (prev->thread.regs->msr & MSR_FP))
 | 
			
		||||
		giveup_fpu(prev);
 | 
			
		||||
#ifdef CONFIG_ALTIVEC
 | 
			
		||||
	/*
 | 
			
		||||
	 * If the previous thread used altivec in the last quantum
 | 
			
		||||
	 * (thus changing altivec regs) then save them.
 | 
			
		||||
	 * We used to check the VRSAVE register but not all apps
 | 
			
		||||
	 * set it, so we don't rely on it now (and in fact we need
 | 
			
		||||
	 * to save & restore VSCR even if VRSAVE == 0).  -- paulus
 | 
			
		||||
	 *
 | 
			
		||||
	 * On SMP we always save/restore altivec regs just to avoid the
 | 
			
		||||
	 * complexity of changing processors.
 | 
			
		||||
	 *  -- Cort
 | 
			
		||||
	 */
 | 
			
		||||
	if (prev->thread.regs && (prev->thread.regs->msr & MSR_VEC))
 | 
			
		||||
		giveup_altivec(prev);
 | 
			
		||||
	/* Avoid the trap.  On smp this this never happens since
 | 
			
		||||
	 * we don't set last_task_used_altivec -- Cort
 | 
			
		||||
	 */
 | 
			
		||||
	if (new->thread.regs && last_task_used_altivec == new)
 | 
			
		||||
		new->thread.regs->msr |= MSR_VEC;
 | 
			
		||||
#endif /* CONFIG_ALTIVEC */
 | 
			
		||||
#ifdef CONFIG_SPE
 | 
			
		||||
	/*
 | 
			
		||||
	 * If the previous thread used spe in the last quantum
 | 
			
		||||
	 * (thus changing spe regs) then save them.
 | 
			
		||||
	 *
 | 
			
		||||
	 * On SMP we always save/restore spe regs just to avoid the
 | 
			
		||||
	 * complexity of changing processors.
 | 
			
		||||
	 */
 | 
			
		||||
	if ((prev->thread.regs && (prev->thread.regs->msr & MSR_SPE)))
 | 
			
		||||
		giveup_spe(prev);
 | 
			
		||||
	/* Avoid the trap.  On smp this this never happens since
 | 
			
		||||
	 * we don't set last_task_used_spe
 | 
			
		||||
	 */
 | 
			
		||||
	if (new->thread.regs && last_task_used_spe == new)
 | 
			
		||||
		new->thread.regs->msr |= MSR_SPE;
 | 
			
		||||
#endif /* CONFIG_SPE */
 | 
			
		||||
#endif /* CONFIG_SMP */
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_PPC64	/* for now */
 | 
			
		||||
	if (unlikely(__get_cpu_var(current_dabr) != new->thread.dabr)) {
 | 
			
		||||
		set_dabr(new->thread.dabr);
 | 
			
		||||
		__get_cpu_var(current_dabr) = new->thread.dabr;
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	new_thread = &new->thread;
 | 
			
		||||
	old_thread = ¤t->thread;
 | 
			
		||||
	local_irq_save(flags);
 | 
			
		||||
	last = _switch(old_thread, new_thread);
 | 
			
		||||
 | 
			
		||||
	local_irq_restore(flags);
 | 
			
		||||
 | 
			
		||||
	return last;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void show_regs(struct pt_regs * regs)
 | 
			
		||||
{
 | 
			
		||||
	int i, trap;
 | 
			
		||||
 | 
			
		||||
	printk("NIP: %08lX LR: %08lX SP: %08lX REGS: %p TRAP: %04lx    %s\n",
 | 
			
		||||
	       regs->nip, regs->link, regs->gpr[1], regs, regs->trap,
 | 
			
		||||
	       print_tainted());
 | 
			
		||||
	printk("MSR: %08lx EE: %01x PR: %01x FP: %01x ME: %01x IR/DR: %01x%01x\n",
 | 
			
		||||
	       regs->msr, regs->msr&MSR_EE ? 1 : 0, regs->msr&MSR_PR ? 1 : 0,
 | 
			
		||||
	       regs->msr & MSR_FP ? 1 : 0,regs->msr&MSR_ME ? 1 : 0,
 | 
			
		||||
	       regs->msr&MSR_IR ? 1 : 0,
 | 
			
		||||
	       regs->msr&MSR_DR ? 1 : 0);
 | 
			
		||||
	trap = TRAP(regs);
 | 
			
		||||
	if (trap == 0x300 || trap == 0x600)
 | 
			
		||||
		printk("DAR: %08lX, DSISR: %08lX\n", regs->dar, regs->dsisr);
 | 
			
		||||
	printk("TASK = %p[%d] '%s' THREAD: %p\n",
 | 
			
		||||
	       current, current->pid, current->comm, current->thread_info);
 | 
			
		||||
	printk("Last syscall: %ld ", current->thread.last_syscall);
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_SMP
 | 
			
		||||
	printk(" CPU: %d", smp_processor_id());
 | 
			
		||||
#endif /* CONFIG_SMP */
 | 
			
		||||
 | 
			
		||||
	for (i = 0;  i < 32;  i++) {
 | 
			
		||||
		long r;
 | 
			
		||||
		if ((i % 8) == 0)
 | 
			
		||||
			printk("\n" KERN_INFO "GPR%02d: ", i);
 | 
			
		||||
		if (__get_user(r, ®s->gpr[i]))
 | 
			
		||||
			break;
 | 
			
		||||
		printk("%08lX ", r);
 | 
			
		||||
		if (i == 12 && !FULL_REGS(regs))
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
	printk("\n");
 | 
			
		||||
#ifdef CONFIG_KALLSYMS
 | 
			
		||||
	/*
 | 
			
		||||
	 * Lookup NIP late so we have the best change of getting the
 | 
			
		||||
	 * above info out without failing
 | 
			
		||||
	 */
 | 
			
		||||
	printk("NIP [%08lx] ", regs->nip);
 | 
			
		||||
	print_symbol("%s\n", regs->nip);
 | 
			
		||||
	printk("LR [%08lx] ", regs->link);
 | 
			
		||||
	print_symbol("%s\n", regs->link);
 | 
			
		||||
#endif
 | 
			
		||||
	show_stack(current, (unsigned long *) regs->gpr[1]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void exit_thread(void)
 | 
			
		||||
{
 | 
			
		||||
#ifndef CONFIG_SMP
 | 
			
		||||
	if (last_task_used_math == current)
 | 
			
		||||
		last_task_used_math = NULL;
 | 
			
		||||
#ifdef CONFIG_ALTIVEC
 | 
			
		||||
	if (last_task_used_altivec == current)
 | 
			
		||||
		last_task_used_altivec = NULL;
 | 
			
		||||
#endif /* CONFIG_ALTIVEC */
 | 
			
		||||
#ifdef CONFIG_SPE
 | 
			
		||||
	if (last_task_used_spe == current)
 | 
			
		||||
		last_task_used_spe = NULL;
 | 
			
		||||
#endif
 | 
			
		||||
#endif /* CONFIG_SMP */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void flush_thread(void)
 | 
			
		||||
{
 | 
			
		||||
#ifndef CONFIG_SMP
 | 
			
		||||
	if (last_task_used_math == current)
 | 
			
		||||
		last_task_used_math = NULL;
 | 
			
		||||
#ifdef CONFIG_ALTIVEC
 | 
			
		||||
	if (last_task_used_altivec == current)
 | 
			
		||||
		last_task_used_altivec = NULL;
 | 
			
		||||
#endif /* CONFIG_ALTIVEC */
 | 
			
		||||
#ifdef CONFIG_SPE
 | 
			
		||||
	if (last_task_used_spe == current)
 | 
			
		||||
		last_task_used_spe = NULL;
 | 
			
		||||
#endif
 | 
			
		||||
#endif /* CONFIG_SMP */
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_PPC64	/* for now */
 | 
			
		||||
	if (current->thread.dabr) {
 | 
			
		||||
		current->thread.dabr = 0;
 | 
			
		||||
		set_dabr(0);
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
release_thread(struct task_struct *t)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * This gets called before we allocate a new thread and copy
 | 
			
		||||
 * the current task into it.
 | 
			
		||||
 */
 | 
			
		||||
void prepare_to_copy(struct task_struct *tsk)
 | 
			
		||||
{
 | 
			
		||||
	flush_fp_to_thread(current);
 | 
			
		||||
	flush_altivec_to_thread(current);
 | 
			
		||||
	flush_spe_to_thread(current);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Copy a thread..
 | 
			
		||||
 */
 | 
			
		||||
int
 | 
			
		||||
copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
 | 
			
		||||
	    unsigned long unused,
 | 
			
		||||
	    struct task_struct *p, struct pt_regs *regs)
 | 
			
		||||
{
 | 
			
		||||
	struct pt_regs *childregs, *kregs;
 | 
			
		||||
	extern void ret_from_fork(void);
 | 
			
		||||
	unsigned long sp = (unsigned long)p->thread_info + THREAD_SIZE;
 | 
			
		||||
	unsigned long childframe;
 | 
			
		||||
 | 
			
		||||
	CHECK_FULL_REGS(regs);
 | 
			
		||||
	/* Copy registers */
 | 
			
		||||
	sp -= sizeof(struct pt_regs);
 | 
			
		||||
	childregs = (struct pt_regs *) sp;
 | 
			
		||||
	*childregs = *regs;
 | 
			
		||||
	if ((childregs->msr & MSR_PR) == 0) {
 | 
			
		||||
		/* for kernel thread, set `current' and stackptr in new task */
 | 
			
		||||
		childregs->gpr[1] = sp + sizeof(struct pt_regs);
 | 
			
		||||
		childregs->gpr[2] = (unsigned long) p;
 | 
			
		||||
		p->thread.regs = NULL;	/* no user register state */
 | 
			
		||||
	} else {
 | 
			
		||||
		childregs->gpr[1] = usp;
 | 
			
		||||
		p->thread.regs = childregs;
 | 
			
		||||
		if (clone_flags & CLONE_SETTLS)
 | 
			
		||||
			childregs->gpr[2] = childregs->gpr[6];
 | 
			
		||||
	}
 | 
			
		||||
	childregs->gpr[3] = 0;  /* Result from fork() */
 | 
			
		||||
	sp -= STACK_FRAME_OVERHEAD;
 | 
			
		||||
	childframe = sp;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * The way this works is that at some point in the future
 | 
			
		||||
	 * some task will call _switch to switch to the new task.
 | 
			
		||||
	 * That will pop off the stack frame created below and start
 | 
			
		||||
	 * the new task running at ret_from_fork.  The new task will
 | 
			
		||||
	 * do some house keeping and then return from the fork or clone
 | 
			
		||||
	 * system call, using the stack frame created above.
 | 
			
		||||
	 */
 | 
			
		||||
	sp -= sizeof(struct pt_regs);
 | 
			
		||||
	kregs = (struct pt_regs *) sp;
 | 
			
		||||
	sp -= STACK_FRAME_OVERHEAD;
 | 
			
		||||
	p->thread.ksp = sp;
 | 
			
		||||
	kregs->nip = (unsigned long)ret_from_fork;
 | 
			
		||||
 | 
			
		||||
	p->thread.last_syscall = -1;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Set up a thread for executing a new program
 | 
			
		||||
 */
 | 
			
		||||
void start_thread(struct pt_regs *regs, unsigned long nip, unsigned long sp)
 | 
			
		||||
{
 | 
			
		||||
	set_fs(USER_DS);
 | 
			
		||||
	memset(regs->gpr, 0, sizeof(regs->gpr));
 | 
			
		||||
	regs->ctr = 0;
 | 
			
		||||
	regs->link = 0;
 | 
			
		||||
	regs->xer = 0;
 | 
			
		||||
	regs->ccr = 0;
 | 
			
		||||
	regs->mq = 0;
 | 
			
		||||
	regs->nip = nip;
 | 
			
		||||
	regs->gpr[1] = sp;
 | 
			
		||||
	regs->msr = MSR_USER;
 | 
			
		||||
#ifndef CONFIG_SMP
 | 
			
		||||
	if (last_task_used_math == current)
 | 
			
		||||
		last_task_used_math = NULL;
 | 
			
		||||
#ifdef CONFIG_ALTIVEC
 | 
			
		||||
	if (last_task_used_altivec == current)
 | 
			
		||||
		last_task_used_altivec = NULL;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef CONFIG_SPE
 | 
			
		||||
	if (last_task_used_spe == current)
 | 
			
		||||
		last_task_used_spe = NULL;
 | 
			
		||||
#endif
 | 
			
		||||
#endif /* CONFIG_SMP */
 | 
			
		||||
	memset(current->thread.fpr, 0, sizeof(current->thread.fpr));
 | 
			
		||||
	current->thread.fpscr = 0;
 | 
			
		||||
#ifdef CONFIG_ALTIVEC
 | 
			
		||||
	memset(current->thread.vr, 0, sizeof(current->thread.vr));
 | 
			
		||||
	memset(¤t->thread.vscr, 0, sizeof(current->thread.vscr));
 | 
			
		||||
	current->thread.vrsave = 0;
 | 
			
		||||
	current->thread.used_vr = 0;
 | 
			
		||||
#endif /* CONFIG_ALTIVEC */
 | 
			
		||||
#ifdef CONFIG_SPE
 | 
			
		||||
	memset(current->thread.evr, 0, sizeof(current->thread.evr));
 | 
			
		||||
	current->thread.acc = 0;
 | 
			
		||||
	current->thread.spefscr = 0;
 | 
			
		||||
	current->thread.used_spe = 0;
 | 
			
		||||
#endif /* CONFIG_SPE */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define PR_FP_ALL_EXCEPT (PR_FP_EXC_DIV | PR_FP_EXC_OVF | PR_FP_EXC_UND \
 | 
			
		||||
		| PR_FP_EXC_RES | PR_FP_EXC_INV)
 | 
			
		||||
 | 
			
		||||
int set_fpexc_mode(struct task_struct *tsk, unsigned int val)
 | 
			
		||||
{
 | 
			
		||||
	struct pt_regs *regs = tsk->thread.regs;
 | 
			
		||||
 | 
			
		||||
	/* This is a bit hairy.  If we are an SPE enabled  processor
 | 
			
		||||
	 * (have embedded fp) we store the IEEE exception enable flags in
 | 
			
		||||
	 * fpexc_mode.  fpexc_mode is also used for setting FP exception
 | 
			
		||||
	 * mode (asyn, precise, disabled) for 'Classic' FP. */
 | 
			
		||||
	if (val & PR_FP_EXC_SW_ENABLE) {
 | 
			
		||||
#ifdef CONFIG_SPE
 | 
			
		||||
		tsk->thread.fpexc_mode = val &
 | 
			
		||||
			(PR_FP_EXC_SW_ENABLE | PR_FP_ALL_EXCEPT);
 | 
			
		||||
#else
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
#endif
 | 
			
		||||
	} else {
 | 
			
		||||
		/* on a CONFIG_SPE this does not hurt us.  The bits that
 | 
			
		||||
		 * __pack_fe01 use do not overlap with bits used for
 | 
			
		||||
		 * PR_FP_EXC_SW_ENABLE.  Additionally, the MSR[FE0,FE1] bits
 | 
			
		||||
		 * on CONFIG_SPE implementations are reserved so writing to
 | 
			
		||||
		 * them does not change anything */
 | 
			
		||||
		if (val > PR_FP_EXC_PRECISE)
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		tsk->thread.fpexc_mode = __pack_fe01(val);
 | 
			
		||||
		if (regs != NULL && (regs->msr & MSR_FP) != 0)
 | 
			
		||||
			regs->msr = (regs->msr & ~(MSR_FE0|MSR_FE1))
 | 
			
		||||
				| tsk->thread.fpexc_mode;
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int get_fpexc_mode(struct task_struct *tsk, unsigned long adr)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int val;
 | 
			
		||||
 | 
			
		||||
	if (tsk->thread.fpexc_mode & PR_FP_EXC_SW_ENABLE)
 | 
			
		||||
#ifdef CONFIG_SPE
 | 
			
		||||
		val = tsk->thread.fpexc_mode;
 | 
			
		||||
#else
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
#endif
 | 
			
		||||
	else
 | 
			
		||||
		val = __unpack_fe01(tsk->thread.fpexc_mode);
 | 
			
		||||
	return put_user(val, (unsigned int __user *) adr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int sys_clone(unsigned long clone_flags, unsigned long usp,
 | 
			
		||||
	      int __user *parent_tidp, void __user *child_threadptr,
 | 
			
		||||
	      int __user *child_tidp, int p6,
 | 
			
		||||
	      struct pt_regs *regs)
 | 
			
		||||
{
 | 
			
		||||
	CHECK_FULL_REGS(regs);
 | 
			
		||||
	if (usp == 0)
 | 
			
		||||
		usp = regs->gpr[1];	/* stack pointer for child */
 | 
			
		||||
 	return do_fork(clone_flags, usp, regs, 0, parent_tidp, child_tidp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int sys_fork(unsigned long p1, unsigned long p2, unsigned long p3,
 | 
			
		||||
	     unsigned long p4, unsigned long p5, unsigned long p6,
 | 
			
		||||
	     struct pt_regs *regs)
 | 
			
		||||
{
 | 
			
		||||
	CHECK_FULL_REGS(regs);
 | 
			
		||||
	return do_fork(SIGCHLD, regs->gpr[1], regs, 0, NULL, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int sys_vfork(unsigned long p1, unsigned long p2, unsigned long p3,
 | 
			
		||||
	      unsigned long p4, unsigned long p5, unsigned long p6,
 | 
			
		||||
	      struct pt_regs *regs)
 | 
			
		||||
{
 | 
			
		||||
	CHECK_FULL_REGS(regs);
 | 
			
		||||
	return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->gpr[1],
 | 
			
		||||
			regs, 0, NULL, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int sys_execve(unsigned long a0, unsigned long a1, unsigned long a2,
 | 
			
		||||
	       unsigned long a3, unsigned long a4, unsigned long a5,
 | 
			
		||||
	       struct pt_regs *regs)
 | 
			
		||||
{
 | 
			
		||||
	int error;
 | 
			
		||||
	char * filename;
 | 
			
		||||
 | 
			
		||||
	filename = getname((char __user *) a0);
 | 
			
		||||
	error = PTR_ERR(filename);
 | 
			
		||||
	if (IS_ERR(filename))
 | 
			
		||||
		goto out;
 | 
			
		||||
	flush_fp_to_thread(current);
 | 
			
		||||
	flush_altivec_to_thread(current);
 | 
			
		||||
	flush_spe_to_thread(current);
 | 
			
		||||
	if (error == 0) {
 | 
			
		||||
		task_lock(current);
 | 
			
		||||
		current->ptrace &= ~PT_DTRACE;
 | 
			
		||||
		task_unlock(current);
 | 
			
		||||
	}
 | 
			
		||||
	putname(filename);
 | 
			
		||||
out:
 | 
			
		||||
	return error;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int validate_sp(unsigned long sp, struct task_struct *p,
 | 
			
		||||
		       unsigned long nbytes)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long stack_page = (unsigned long)p->thread_info;
 | 
			
		||||
 | 
			
		||||
	if (sp >= stack_page + sizeof(struct thread_struct)
 | 
			
		||||
	    && sp <= stack_page + THREAD_SIZE - nbytes)
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_IRQSTACKS
 | 
			
		||||
	stack_page = (unsigned long) hardirq_ctx[task_cpu(p)];
 | 
			
		||||
	if (sp >= stack_page + sizeof(struct thread_struct)
 | 
			
		||||
	    && sp <= stack_page + THREAD_SIZE - nbytes)
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
	stack_page = (unsigned long) softirq_ctx[task_cpu(p)];
 | 
			
		||||
	if (sp >= stack_page + sizeof(struct thread_struct)
 | 
			
		||||
	    && sp <= stack_page + THREAD_SIZE - nbytes)
 | 
			
		||||
		return 1;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dump_stack(void)
 | 
			
		||||
{
 | 
			
		||||
	show_stack(current, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORT_SYMBOL(dump_stack);
 | 
			
		||||
 | 
			
		||||
void show_stack(struct task_struct *tsk, unsigned long *stack)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long sp, stack_top, prev_sp, ret;
 | 
			
		||||
	int count = 0;
 | 
			
		||||
	unsigned long next_exc = 0;
 | 
			
		||||
	struct pt_regs *regs;
 | 
			
		||||
	extern char ret_from_except, ret_from_except_full, ret_from_syscall;
 | 
			
		||||
 | 
			
		||||
	sp = (unsigned long) stack;
 | 
			
		||||
	if (tsk == NULL)
 | 
			
		||||
		tsk = current;
 | 
			
		||||
	if (sp == 0) {
 | 
			
		||||
		if (tsk == current)
 | 
			
		||||
			asm("mr %0,1" : "=r" (sp));
 | 
			
		||||
		else
 | 
			
		||||
			sp = tsk->thread.ksp;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	prev_sp = (unsigned long) (tsk->thread_info + 1);
 | 
			
		||||
	stack_top = (unsigned long) tsk->thread_info + THREAD_SIZE;
 | 
			
		||||
	while (count < 16 && sp > prev_sp && sp < stack_top && (sp & 3) == 0) {
 | 
			
		||||
		if (count == 0) {
 | 
			
		||||
			printk("Call trace:");
 | 
			
		||||
#ifdef CONFIG_KALLSYMS
 | 
			
		||||
			printk("\n");
 | 
			
		||||
#endif
 | 
			
		||||
		} else {
 | 
			
		||||
			if (next_exc) {
 | 
			
		||||
				ret = next_exc;
 | 
			
		||||
				next_exc = 0;
 | 
			
		||||
			} else
 | 
			
		||||
				ret = *(unsigned long *)(sp + 4);
 | 
			
		||||
			printk(" [%08lx] ", ret);
 | 
			
		||||
#ifdef CONFIG_KALLSYMS
 | 
			
		||||
			print_symbol("%s", ret);
 | 
			
		||||
			printk("\n");
 | 
			
		||||
#endif
 | 
			
		||||
			if (ret == (unsigned long) &ret_from_except
 | 
			
		||||
			    || ret == (unsigned long) &ret_from_except_full
 | 
			
		||||
			    || ret == (unsigned long) &ret_from_syscall) {
 | 
			
		||||
				/* sp + 16 points to an exception frame */
 | 
			
		||||
				regs = (struct pt_regs *) (sp + 16);
 | 
			
		||||
				if (sp + 16 + sizeof(*regs) <= stack_top)
 | 
			
		||||
					next_exc = regs->nip;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		++count;
 | 
			
		||||
		sp = *(unsigned long *)sp;
 | 
			
		||||
	}
 | 
			
		||||
#ifndef CONFIG_KALLSYMS
 | 
			
		||||
	if (count > 0)
 | 
			
		||||
		printk("\n");
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsigned long get_wchan(struct task_struct *p)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long ip, sp;
 | 
			
		||||
	int count = 0;
 | 
			
		||||
 | 
			
		||||
	if (!p || p == current || p->state == TASK_RUNNING)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	sp = p->thread.ksp;
 | 
			
		||||
	if (!validate_sp(sp, p, 16))
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	do {
 | 
			
		||||
		sp = *(unsigned long *)sp;
 | 
			
		||||
		if (!validate_sp(sp, p, 16))
 | 
			
		||||
			return 0;
 | 
			
		||||
		if (count > 0) {
 | 
			
		||||
			ip = *(unsigned long *)(sp + 4);
 | 
			
		||||
			if (!in_sched_functions(ip))
 | 
			
		||||
				return ip;
 | 
			
		||||
		}
 | 
			
		||||
	} while (count++ < 16);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(get_wchan);
 | 
			
		||||
							
								
								
									
										135
									
								
								arch/powerpc/kernel/semaphore.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								arch/powerpc/kernel/semaphore.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,135 @@
 | 
			
		||||
/*
 | 
			
		||||
 * PowerPC-specific semaphore code.
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 1999 Cort Dougan <cort@cs.nmt.edu>
 | 
			
		||||
 *
 | 
			
		||||
 * 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.
 | 
			
		||||
 *
 | 
			
		||||
 * April 2001 - Reworked by Paul Mackerras <paulus@samba.org>
 | 
			
		||||
 * to eliminate the SMP races in the old version between the updates
 | 
			
		||||
 * of `count' and `waking'.  Now we use negative `count' values to
 | 
			
		||||
 * indicate that some process(es) are waiting for the semaphore.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/sched.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
 | 
			
		||||
#include <asm/atomic.h>
 | 
			
		||||
#include <asm/semaphore.h>
 | 
			
		||||
#include <asm/errno.h>
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Atomically update sem->count.
 | 
			
		||||
 * This does the equivalent of the following:
 | 
			
		||||
 *
 | 
			
		||||
 *	old_count = sem->count;
 | 
			
		||||
 *	tmp = MAX(old_count, 0) + incr;
 | 
			
		||||
 *	sem->count = tmp;
 | 
			
		||||
 *	return old_count;
 | 
			
		||||
 */
 | 
			
		||||
static inline int __sem_update_count(struct semaphore *sem, int incr)
 | 
			
		||||
{
 | 
			
		||||
	int old_count, tmp;
 | 
			
		||||
 | 
			
		||||
	__asm__ __volatile__("\n"
 | 
			
		||||
"1:	lwarx	%0,0,%3\n"
 | 
			
		||||
"	srawi	%1,%0,31\n"
 | 
			
		||||
"	andc	%1,%0,%1\n"
 | 
			
		||||
"	add	%1,%1,%4\n"
 | 
			
		||||
	PPC405_ERR77(0,%3)
 | 
			
		||||
"	stwcx.	%1,0,%3\n"
 | 
			
		||||
"	bne	1b"
 | 
			
		||||
	: "=&r" (old_count), "=&r" (tmp), "=m" (sem->count)
 | 
			
		||||
	: "r" (&sem->count), "r" (incr), "m" (sem->count)
 | 
			
		||||
	: "cc");
 | 
			
		||||
 | 
			
		||||
	return old_count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void __up(struct semaphore *sem)
 | 
			
		||||
{
 | 
			
		||||
	/*
 | 
			
		||||
	 * Note that we incremented count in up() before we came here,
 | 
			
		||||
	 * but that was ineffective since the result was <= 0, and
 | 
			
		||||
	 * any negative value of count is equivalent to 0.
 | 
			
		||||
	 * This ends up setting count to 1, unless count is now > 0
 | 
			
		||||
	 * (i.e. because some other cpu has called up() in the meantime),
 | 
			
		||||
	 * in which case we just increment count.
 | 
			
		||||
	 */
 | 
			
		||||
	__sem_update_count(sem, 1);
 | 
			
		||||
	wake_up(&sem->wait);
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(__up);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Note that when we come in to __down or __down_interruptible,
 | 
			
		||||
 * we have already decremented count, but that decrement was
 | 
			
		||||
 * ineffective since the result was < 0, and any negative value
 | 
			
		||||
 * of count is equivalent to 0.
 | 
			
		||||
 * Thus it is only when we decrement count from some value > 0
 | 
			
		||||
 * that we have actually got the semaphore.
 | 
			
		||||
 */
 | 
			
		||||
void __sched __down(struct semaphore *sem)
 | 
			
		||||
{
 | 
			
		||||
	struct task_struct *tsk = current;
 | 
			
		||||
	DECLARE_WAITQUEUE(wait, tsk);
 | 
			
		||||
 | 
			
		||||
	__set_task_state(tsk, TASK_UNINTERRUPTIBLE);
 | 
			
		||||
	add_wait_queue_exclusive(&sem->wait, &wait);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Try to get the semaphore.  If the count is > 0, then we've
 | 
			
		||||
	 * got the semaphore; we decrement count and exit the loop.
 | 
			
		||||
	 * If the count is 0 or negative, we set it to -1, indicating
 | 
			
		||||
	 * that we are asleep, and then sleep.
 | 
			
		||||
	 */
 | 
			
		||||
	while (__sem_update_count(sem, -1) <= 0) {
 | 
			
		||||
		schedule();
 | 
			
		||||
		set_task_state(tsk, TASK_UNINTERRUPTIBLE);
 | 
			
		||||
	}
 | 
			
		||||
	remove_wait_queue(&sem->wait, &wait);
 | 
			
		||||
	__set_task_state(tsk, TASK_RUNNING);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * If there are any more sleepers, wake one of them up so
 | 
			
		||||
	 * that it can either get the semaphore, or set count to -1
 | 
			
		||||
	 * indicating that there are still processes sleeping.
 | 
			
		||||
	 */
 | 
			
		||||
	wake_up(&sem->wait);
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(__down);
 | 
			
		||||
 | 
			
		||||
int __sched __down_interruptible(struct semaphore * sem)
 | 
			
		||||
{
 | 
			
		||||
	int retval = 0;
 | 
			
		||||
	struct task_struct *tsk = current;
 | 
			
		||||
	DECLARE_WAITQUEUE(wait, tsk);
 | 
			
		||||
 | 
			
		||||
	__set_task_state(tsk, TASK_INTERRUPTIBLE);
 | 
			
		||||
	add_wait_queue_exclusive(&sem->wait, &wait);
 | 
			
		||||
 | 
			
		||||
	while (__sem_update_count(sem, -1) <= 0) {
 | 
			
		||||
		if (signal_pending(current)) {
 | 
			
		||||
			/*
 | 
			
		||||
			 * A signal is pending - give up trying.
 | 
			
		||||
			 * Set sem->count to 0 if it is negative,
 | 
			
		||||
			 * since we are no longer sleeping.
 | 
			
		||||
			 */
 | 
			
		||||
			__sem_update_count(sem, 0);
 | 
			
		||||
			retval = -EINTR;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		schedule();
 | 
			
		||||
		set_task_state(tsk, TASK_INTERRUPTIBLE);
 | 
			
		||||
	}
 | 
			
		||||
	remove_wait_queue(&sem->wait, &wait);
 | 
			
		||||
	__set_task_state(tsk, TASK_RUNNING);
 | 
			
		||||
 | 
			
		||||
	wake_up(&sem->wait);
 | 
			
		||||
	return retval;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(__down_interruptible);
 | 
			
		||||
							
								
								
									
										1047
									
								
								arch/powerpc/kernel/traps.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1047
									
								
								arch/powerpc/kernel/traps.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										197
									
								
								arch/powerpc/kernel/vector.S
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										197
									
								
								arch/powerpc/kernel/vector.S
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,197 @@
 | 
			
		||||
#include <linux/config.h>
 | 
			
		||||
#include <asm/ppc_asm.h>
 | 
			
		||||
#include <asm/processor.h>
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * The routines below are in assembler so we can closely control the
 | 
			
		||||
 * usage of floating-point registers.  These routines must be called
 | 
			
		||||
 * with preempt disabled.
 | 
			
		||||
 */
 | 
			
		||||
#ifdef CONFIG_PPC32
 | 
			
		||||
	.data
 | 
			
		||||
fpzero:
 | 
			
		||||
	.long	0
 | 
			
		||||
fpone:
 | 
			
		||||
	.long	0x3f800000	/* 1.0 in single-precision FP */
 | 
			
		||||
fphalf:
 | 
			
		||||
	.long	0x3f000000	/* 0.5 in single-precision FP */
 | 
			
		||||
 | 
			
		||||
#define LDCONST(fr, name)	\
 | 
			
		||||
	lis	r11,name@ha;	\
 | 
			
		||||
	lfs	fr,name@l(r11)
 | 
			
		||||
#else
 | 
			
		||||
 | 
			
		||||
	.section ".toc","aw"
 | 
			
		||||
fpzero:
 | 
			
		||||
	.tc	FD_0_0[TC],0
 | 
			
		||||
fpone:
 | 
			
		||||
	.tc	FD_3ff00000_0[TC],0x3ff0000000000000	/* 1.0 */
 | 
			
		||||
fphalf:
 | 
			
		||||
	.tc	FD_3fe00000_0[TC],0x3fe0000000000000	/* 0.5 */
 | 
			
		||||
 | 
			
		||||
#define LDCONST(fr, name)	\
 | 
			
		||||
	lfd	fr,name@toc(r2)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	.text
 | 
			
		||||
/*
 | 
			
		||||
 * Internal routine to enable floating point and set FPSCR to 0.
 | 
			
		||||
 * Don't call it from C; it doesn't use the normal calling convention.
 | 
			
		||||
 */
 | 
			
		||||
fpenable:
 | 
			
		||||
#ifdef CONFIG_PPC32
 | 
			
		||||
	stwu	r1,-64(r1)
 | 
			
		||||
#else
 | 
			
		||||
	stdu	r1,-64(r1)
 | 
			
		||||
#endif
 | 
			
		||||
	mfmsr	r10
 | 
			
		||||
	ori	r11,r10,MSR_FP
 | 
			
		||||
	mtmsr	r11
 | 
			
		||||
	isync
 | 
			
		||||
	stfd	fr0,24(r1)
 | 
			
		||||
	stfd	fr1,16(r1)
 | 
			
		||||
	stfd	fr31,8(r1)
 | 
			
		||||
	LDCONST(fr1, fpzero)
 | 
			
		||||
	mffs	fr31
 | 
			
		||||
	mtfsf	0xff,fr1
 | 
			
		||||
	blr
 | 
			
		||||
 | 
			
		||||
fpdisable:
 | 
			
		||||
	mtlr	r12
 | 
			
		||||
	mtfsf	0xff,fr31
 | 
			
		||||
	lfd	fr31,8(r1)
 | 
			
		||||
	lfd	fr1,16(r1)
 | 
			
		||||
	lfd	fr0,24(r1)
 | 
			
		||||
	mtmsr	r10
 | 
			
		||||
	isync
 | 
			
		||||
	addi	r1,r1,64
 | 
			
		||||
	blr
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Vector add, floating point.
 | 
			
		||||
 */
 | 
			
		||||
_GLOBAL(vaddfp)
 | 
			
		||||
	mflr	r12
 | 
			
		||||
	bl	fpenable
 | 
			
		||||
	li	r0,4
 | 
			
		||||
	mtctr	r0
 | 
			
		||||
	li	r6,0
 | 
			
		||||
1:	lfsx	fr0,r4,r6
 | 
			
		||||
	lfsx	fr1,r5,r6
 | 
			
		||||
	fadds	fr0,fr0,fr1
 | 
			
		||||
	stfsx	fr0,r3,r6
 | 
			
		||||
	addi	r6,r6,4
 | 
			
		||||
	bdnz	1b
 | 
			
		||||
	b	fpdisable
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Vector subtract, floating point.
 | 
			
		||||
 */
 | 
			
		||||
_GLOBAL(vsubfp)
 | 
			
		||||
	mflr	r12
 | 
			
		||||
	bl	fpenable
 | 
			
		||||
	li	r0,4
 | 
			
		||||
	mtctr	r0
 | 
			
		||||
	li	r6,0
 | 
			
		||||
1:	lfsx	fr0,r4,r6
 | 
			
		||||
	lfsx	fr1,r5,r6
 | 
			
		||||
	fsubs	fr0,fr0,fr1
 | 
			
		||||
	stfsx	fr0,r3,r6
 | 
			
		||||
	addi	r6,r6,4
 | 
			
		||||
	bdnz	1b
 | 
			
		||||
	b	fpdisable
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Vector multiply and add, floating point.
 | 
			
		||||
 */
 | 
			
		||||
_GLOBAL(vmaddfp)
 | 
			
		||||
	mflr	r12
 | 
			
		||||
	bl	fpenable
 | 
			
		||||
	stfd	fr2,32(r1)
 | 
			
		||||
	li	r0,4
 | 
			
		||||
	mtctr	r0
 | 
			
		||||
	li	r7,0
 | 
			
		||||
1:	lfsx	fr0,r4,r7
 | 
			
		||||
	lfsx	fr1,r5,r7
 | 
			
		||||
	lfsx	fr2,r6,r7
 | 
			
		||||
	fmadds	fr0,fr0,fr2,fr1
 | 
			
		||||
	stfsx	fr0,r3,r7
 | 
			
		||||
	addi	r7,r7,4
 | 
			
		||||
	bdnz	1b
 | 
			
		||||
	lfd	fr2,32(r1)
 | 
			
		||||
	b	fpdisable
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Vector negative multiply and subtract, floating point.
 | 
			
		||||
 */
 | 
			
		||||
_GLOBAL(vnmsubfp)
 | 
			
		||||
	mflr	r12
 | 
			
		||||
	bl	fpenable
 | 
			
		||||
	stfd	fr2,32(r1)
 | 
			
		||||
	li	r0,4
 | 
			
		||||
	mtctr	r0
 | 
			
		||||
	li	r7,0
 | 
			
		||||
1:	lfsx	fr0,r4,r7
 | 
			
		||||
	lfsx	fr1,r5,r7
 | 
			
		||||
	lfsx	fr2,r6,r7
 | 
			
		||||
	fnmsubs	fr0,fr0,fr2,fr1
 | 
			
		||||
	stfsx	fr0,r3,r7
 | 
			
		||||
	addi	r7,r7,4
 | 
			
		||||
	bdnz	1b
 | 
			
		||||
	lfd	fr2,32(r1)
 | 
			
		||||
	b	fpdisable
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Vector reciprocal estimate.  We just compute 1.0/x.
 | 
			
		||||
 * r3 -> destination, r4 -> source.
 | 
			
		||||
 */
 | 
			
		||||
_GLOBAL(vrefp)
 | 
			
		||||
	mflr	r12
 | 
			
		||||
	bl	fpenable
 | 
			
		||||
	li	r0,4
 | 
			
		||||
	LDCONST(fr1, fpone)
 | 
			
		||||
	mtctr	r0
 | 
			
		||||
	li	r6,0
 | 
			
		||||
1:	lfsx	fr0,r4,r6
 | 
			
		||||
	fdivs	fr0,fr1,fr0
 | 
			
		||||
	stfsx	fr0,r3,r6
 | 
			
		||||
	addi	r6,r6,4
 | 
			
		||||
	bdnz	1b
 | 
			
		||||
	b	fpdisable
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Vector reciprocal square-root estimate, floating point.
 | 
			
		||||
 * We use the frsqrte instruction for the initial estimate followed
 | 
			
		||||
 * by 2 iterations of Newton-Raphson to get sufficient accuracy.
 | 
			
		||||
 * r3 -> destination, r4 -> source.
 | 
			
		||||
 */
 | 
			
		||||
_GLOBAL(vrsqrtefp)
 | 
			
		||||
	mflr	r12
 | 
			
		||||
	bl	fpenable
 | 
			
		||||
	stfd	fr2,32(r1)
 | 
			
		||||
	stfd	fr3,40(r1)
 | 
			
		||||
	stfd	fr4,48(r1)
 | 
			
		||||
	stfd	fr5,56(r1)
 | 
			
		||||
	li	r0,4
 | 
			
		||||
	LDCONST(fr4, fpone)
 | 
			
		||||
	LDCONST(fr5, fphalf)
 | 
			
		||||
	mtctr	r0
 | 
			
		||||
	li	r6,0
 | 
			
		||||
1:	lfsx	fr0,r4,r6
 | 
			
		||||
	frsqrte	fr1,fr0		/* r = frsqrte(s) */
 | 
			
		||||
	fmuls	fr3,fr1,fr0	/* r * s */
 | 
			
		||||
	fmuls	fr2,fr1,fr5	/* r * 0.5 */
 | 
			
		||||
	fnmsubs	fr3,fr1,fr3,fr4	/* 1 - s * r * r */
 | 
			
		||||
	fmadds	fr1,fr2,fr3,fr1	/* r = r + 0.5 * r * (1 - s * r * r) */
 | 
			
		||||
	fmuls	fr3,fr1,fr0	/* r * s */
 | 
			
		||||
	fmuls	fr2,fr1,fr5	/* r * 0.5 */
 | 
			
		||||
	fnmsubs	fr3,fr1,fr3,fr4	/* 1 - s * r * r */
 | 
			
		||||
	fmadds	fr1,fr2,fr3,fr1	/* r = r + 0.5 * r * (1 - s * r * r) */
 | 
			
		||||
	stfsx	fr1,r3,r6
 | 
			
		||||
	addi	r6,r6,4
 | 
			
		||||
	bdnz	1b
 | 
			
		||||
	lfd	fr5,56(r1)
 | 
			
		||||
	lfd	fr4,48(r1)
 | 
			
		||||
	lfd	fr3,40(r1)
 | 
			
		||||
	lfd	fr2,32(r1)
 | 
			
		||||
	b	fpdisable
 | 
			
		||||
							
								
								
									
										174
									
								
								arch/powerpc/kernel/vmlinux.lds
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								arch/powerpc/kernel/vmlinux.lds
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,174 @@
 | 
			
		||||
/* Align . to a 8 byte boundary equals to maximum function alignment. */
 | 
			
		||||
/* sched.text is aling to function alignment to secure we have same
 | 
			
		||||
 * address even at second ld pass when generating System.map */
 | 
			
		||||
/* spinlock.text is aling to function alignment to secure we have same
 | 
			
		||||
 * address even at second ld pass when generating System.map */
 | 
			
		||||
  /* DWARF debug sections.
 | 
			
		||||
		Symbols in the DWARF debugging sections are relative to
 | 
			
		||||
		the beginning of the section so we begin them at 0.  */
 | 
			
		||||
  /* Stabs debugging sections.  */
 | 
			
		||||
OUTPUT_ARCH(powerpc:common)
 | 
			
		||||
jiffies = jiffies_64 + 4;
 | 
			
		||||
SECTIONS
 | 
			
		||||
{
 | 
			
		||||
  /* Read-only sections, merged into text segment: */
 | 
			
		||||
  . = + SIZEOF_HEADERS;
 | 
			
		||||
  .interp : { *(.interp) }
 | 
			
		||||
  .hash : { *(.hash) }
 | 
			
		||||
  .dynsym : { *(.dynsym) }
 | 
			
		||||
  .dynstr : { *(.dynstr) }
 | 
			
		||||
  .rel.text : { *(.rel.text) }
 | 
			
		||||
  .rela.text : { *(.rela.text) }
 | 
			
		||||
  .rel.data : { *(.rel.data) }
 | 
			
		||||
  .rela.data : { *(.rela.data) }
 | 
			
		||||
  .rel.rodata : { *(.rel.rodata) }
 | 
			
		||||
  .rela.rodata : { *(.rela.rodata) }
 | 
			
		||||
  .rel.got : { *(.rel.got) }
 | 
			
		||||
  .rela.got : { *(.rela.got) }
 | 
			
		||||
  .rel.ctors : { *(.rel.ctors) }
 | 
			
		||||
  .rela.ctors : { *(.rela.ctors) }
 | 
			
		||||
  .rel.dtors : { *(.rel.dtors) }
 | 
			
		||||
  .rela.dtors : { *(.rela.dtors) }
 | 
			
		||||
  .rel.bss : { *(.rel.bss) }
 | 
			
		||||
  .rela.bss : { *(.rela.bss) }
 | 
			
		||||
  .rel.plt : { *(.rel.plt) }
 | 
			
		||||
  .rela.plt : { *(.rela.plt) }
 | 
			
		||||
/*  .init          : { *(.init)	} =0*/
 | 
			
		||||
  .plt : { *(.plt) }
 | 
			
		||||
  .text :
 | 
			
		||||
  {
 | 
			
		||||
    *(.text)
 | 
			
		||||
    . = ALIGN(8); __sched_text_start = .; *(.sched.text) __sched_text_end = .;
 | 
			
		||||
    . = ALIGN(8); __lock_text_start = .; *(.spinlock.text) __lock_text_end = .;
 | 
			
		||||
    *(.fixup)
 | 
			
		||||
    *(.got1)
 | 
			
		||||
    __got2_start = .;
 | 
			
		||||
    *(.got2)
 | 
			
		||||
    __got2_end = .;
 | 
			
		||||
  }
 | 
			
		||||
  _etext = .;
 | 
			
		||||
  PROVIDE (etext = .);
 | 
			
		||||
  .rodata : AT(ADDR(.rodata) - 0) { *(.rodata) *(.rodata.*) *(__vermagic) } .rodata1 : AT(ADDR(.rodata1) - 0) { *(.rodata1) } .pci_fixup : AT(ADDR(.pci_fixup) - 0) { __start_pci_fixups_early = .; *(.pci_fixup_early) __end_pci_fixups_early = .; __start_pci_fixups_header = .; *(.pci_fixup_header) __end_pci_fixups_header = .; __start_pci_fixups_final = .; *(.pci_fixup_final) __end_pci_fixups_final = .; __start_pci_fixups_enable = .; *(.pci_fixup_enable) __end_pci_fixups_enable = .; } __ksymtab : AT(ADDR(__ksymtab) - 0) { __start___ksymtab = .; *(__ksymtab) __stop___ksymtab = .; } __ksymtab_gpl : AT(ADDR(__ksymtab_gpl) - 0) { __start___ksymtab_gpl = .; *(__ksymtab_gpl) __stop___ksymtab_gpl = .; } __kcrctab : AT(ADDR(__kcrctab) - 0) { __start___kcrctab = .; *(__kcrctab) __stop___kcrctab = .; } __kcrctab_gpl : AT(ADDR(__kcrctab_gpl) - 0) { __start___kcrctab_gpl = .; *(__kcrctab_gpl) __stop___kcrctab_gpl = .; } __ksymtab_strings : AT(ADDR(__ksymtab_strings) - 0) { *(__ksymtab_strings) } __param : AT(ADDR(__param) - 0) { __start___param = .; *(__param) __stop___param = .; }
 | 
			
		||||
  .fini : { *(.fini) } =0
 | 
			
		||||
  .ctors : { *(.ctors) }
 | 
			
		||||
  .dtors : { *(.dtors) }
 | 
			
		||||
  .fixup : { *(.fixup) }
 | 
			
		||||
 __ex_table : {
 | 
			
		||||
  __start___ex_table = .;
 | 
			
		||||
  *(__ex_table)
 | 
			
		||||
  __stop___ex_table = .;
 | 
			
		||||
 }
 | 
			
		||||
 __bug_table : {
 | 
			
		||||
  __start___bug_table = .;
 | 
			
		||||
  *(__bug_table)
 | 
			
		||||
  __stop___bug_table = .;
 | 
			
		||||
 }
 | 
			
		||||
  /* Read-write section, merged into data segment: */
 | 
			
		||||
  . = ALIGN(4096);
 | 
			
		||||
  .data :
 | 
			
		||||
  {
 | 
			
		||||
    *(.data)
 | 
			
		||||
    *(.data1)
 | 
			
		||||
    *(.sdata)
 | 
			
		||||
    *(.sdata2)
 | 
			
		||||
    *(.got.plt) *(.got)
 | 
			
		||||
    *(.dynamic)
 | 
			
		||||
    CONSTRUCTORS
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  . = ALIGN(4096);
 | 
			
		||||
  __nosave_begin = .;
 | 
			
		||||
  .data_nosave : { *(.data.nosave) }
 | 
			
		||||
  . = ALIGN(4096);
 | 
			
		||||
  __nosave_end = .;
 | 
			
		||||
 | 
			
		||||
  . = ALIGN(32);
 | 
			
		||||
  .data.cacheline_aligned : { *(.data.cacheline_aligned) }
 | 
			
		||||
 | 
			
		||||
  _edata = .;
 | 
			
		||||
  PROVIDE (edata = .);
 | 
			
		||||
 | 
			
		||||
  . = ALIGN(8192);
 | 
			
		||||
  .data.init_task : { *(.data.init_task) }
 | 
			
		||||
 | 
			
		||||
  . = ALIGN(4096);
 | 
			
		||||
  __init_begin = .;
 | 
			
		||||
  .init.text : {
 | 
			
		||||
 _sinittext = .;
 | 
			
		||||
 *(.init.text)
 | 
			
		||||
 _einittext = .;
 | 
			
		||||
  }
 | 
			
		||||
  /* .exit.text is discarded at runtime, not link time,
 | 
			
		||||
     to deal with references from __bug_table */
 | 
			
		||||
  .exit.text : { *(.exit.text) }
 | 
			
		||||
  .init.data : {
 | 
			
		||||
    *(.init.data);
 | 
			
		||||
    __vtop_table_begin = .;
 | 
			
		||||
    *(.vtop_fixup);
 | 
			
		||||
    __vtop_table_end = .;
 | 
			
		||||
    __ptov_table_begin = .;
 | 
			
		||||
    *(.ptov_fixup);
 | 
			
		||||
    __ptov_table_end = .;
 | 
			
		||||
  }
 | 
			
		||||
  . = ALIGN(16);
 | 
			
		||||
  __setup_start = .;
 | 
			
		||||
  .init.setup : { *(.init.setup) }
 | 
			
		||||
  __setup_end = .;
 | 
			
		||||
  __initcall_start = .;
 | 
			
		||||
  .initcall.init : {
 | 
			
		||||
 *(.initcall1.init)
 | 
			
		||||
 *(.initcall2.init)
 | 
			
		||||
 *(.initcall3.init)
 | 
			
		||||
 *(.initcall4.init)
 | 
			
		||||
 *(.initcall5.init)
 | 
			
		||||
 *(.initcall6.init)
 | 
			
		||||
 *(.initcall7.init)
 | 
			
		||||
  }
 | 
			
		||||
  __initcall_end = .;
 | 
			
		||||
 | 
			
		||||
  __con_initcall_start = .;
 | 
			
		||||
  .con_initcall.init : { *(.con_initcall.init) }
 | 
			
		||||
  __con_initcall_end = .;
 | 
			
		||||
 | 
			
		||||
  .security_initcall.init : AT(ADDR(.security_initcall.init) - 0) { __security_initcall_start = .; *(.security_initcall.init) __security_initcall_end = .; }
 | 
			
		||||
 | 
			
		||||
  __start___ftr_fixup = .;
 | 
			
		||||
  __ftr_fixup : { *(__ftr_fixup) }
 | 
			
		||||
  __stop___ftr_fixup = .;
 | 
			
		||||
 | 
			
		||||
  . = ALIGN(32);
 | 
			
		||||
  __per_cpu_start = .;
 | 
			
		||||
  .data.percpu : { *(.data.percpu) }
 | 
			
		||||
  __per_cpu_end = .;
 | 
			
		||||
 | 
			
		||||
  . = ALIGN(4096);
 | 
			
		||||
  __initramfs_start = .;
 | 
			
		||||
  .init.ramfs : { *(.init.ramfs) }
 | 
			
		||||
  __initramfs_end = .;
 | 
			
		||||
 | 
			
		||||
  . = ALIGN(4096);
 | 
			
		||||
  __init_end = .;
 | 
			
		||||
 | 
			
		||||
  . = ALIGN(4096);
 | 
			
		||||
  _sextratext = .;
 | 
			
		||||
  _eextratext = .;
 | 
			
		||||
 | 
			
		||||
  __bss_start = .;
 | 
			
		||||
  .bss :
 | 
			
		||||
  {
 | 
			
		||||
   *(.sbss) *(.scommon)
 | 
			
		||||
   *(.dynbss)
 | 
			
		||||
   *(.bss)
 | 
			
		||||
   *(COMMON)
 | 
			
		||||
  }
 | 
			
		||||
  __bss_stop = .;
 | 
			
		||||
 | 
			
		||||
  _end = . ;
 | 
			
		||||
  PROVIDE (end = .);
 | 
			
		||||
 | 
			
		||||
  /* Sections to be discarded. */
 | 
			
		||||
  /DISCARD/ : {
 | 
			
		||||
    *(.exitcall.exit)
 | 
			
		||||
    *(.exit.data)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										172
									
								
								arch/powerpc/kernel/vmlinux.lds.S
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										172
									
								
								arch/powerpc/kernel/vmlinux.lds.S
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,172 @@
 | 
			
		||||
#include <asm-generic/vmlinux.lds.h>
 | 
			
		||||
 | 
			
		||||
OUTPUT_ARCH(powerpc:common)
 | 
			
		||||
jiffies = jiffies_64 + 4;
 | 
			
		||||
SECTIONS
 | 
			
		||||
{
 | 
			
		||||
  /* Read-only sections, merged into text segment: */
 | 
			
		||||
  . = + SIZEOF_HEADERS;
 | 
			
		||||
  .interp : { *(.interp) }
 | 
			
		||||
  .hash          : { *(.hash)		}
 | 
			
		||||
  .dynsym        : { *(.dynsym)		}
 | 
			
		||||
  .dynstr        : { *(.dynstr)		}
 | 
			
		||||
  .rel.text      : { *(.rel.text)		}
 | 
			
		||||
  .rela.text     : { *(.rela.text) 	}
 | 
			
		||||
  .rel.data      : { *(.rel.data)		}
 | 
			
		||||
  .rela.data     : { *(.rela.data) 	}
 | 
			
		||||
  .rel.rodata    : { *(.rel.rodata) 	}
 | 
			
		||||
  .rela.rodata   : { *(.rela.rodata) 	}
 | 
			
		||||
  .rel.got       : { *(.rel.got)		}
 | 
			
		||||
  .rela.got      : { *(.rela.got)		}
 | 
			
		||||
  .rel.ctors     : { *(.rel.ctors)	}
 | 
			
		||||
  .rela.ctors    : { *(.rela.ctors)	}
 | 
			
		||||
  .rel.dtors     : { *(.rel.dtors)	}
 | 
			
		||||
  .rela.dtors    : { *(.rela.dtors)	}
 | 
			
		||||
  .rel.bss       : { *(.rel.bss)		}
 | 
			
		||||
  .rela.bss      : { *(.rela.bss)		}
 | 
			
		||||
  .rel.plt       : { *(.rel.plt)		}
 | 
			
		||||
  .rela.plt      : { *(.rela.plt)		}
 | 
			
		||||
/*  .init          : { *(.init)	} =0*/
 | 
			
		||||
  .plt : { *(.plt) }
 | 
			
		||||
  .text      :
 | 
			
		||||
  {
 | 
			
		||||
    *(.text)
 | 
			
		||||
    SCHED_TEXT
 | 
			
		||||
    LOCK_TEXT
 | 
			
		||||
    *(.fixup)
 | 
			
		||||
    *(.got1)
 | 
			
		||||
    __got2_start = .;
 | 
			
		||||
    *(.got2)
 | 
			
		||||
    __got2_end = .;
 | 
			
		||||
  }
 | 
			
		||||
  _etext = .;
 | 
			
		||||
  PROVIDE (etext = .);
 | 
			
		||||
 | 
			
		||||
  RODATA
 | 
			
		||||
  .fini      : { *(.fini)    } =0
 | 
			
		||||
  .ctors     : { *(.ctors)   }
 | 
			
		||||
  .dtors     : { *(.dtors)   }
 | 
			
		||||
 | 
			
		||||
  .fixup   : { *(.fixup) }
 | 
			
		||||
 | 
			
		||||
	__ex_table : {
 | 
			
		||||
		__start___ex_table = .;
 | 
			
		||||
		*(__ex_table)
 | 
			
		||||
		__stop___ex_table = .;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	__bug_table : {
 | 
			
		||||
		__start___bug_table = .;
 | 
			
		||||
		*(__bug_table)
 | 
			
		||||
		__stop___bug_table = .;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
  /* Read-write section, merged into data segment: */
 | 
			
		||||
  . = ALIGN(4096);
 | 
			
		||||
  .data    :
 | 
			
		||||
  {
 | 
			
		||||
    *(.data)
 | 
			
		||||
    *(.data1)
 | 
			
		||||
    *(.sdata)
 | 
			
		||||
    *(.sdata2)
 | 
			
		||||
    *(.got.plt) *(.got)
 | 
			
		||||
    *(.dynamic)
 | 
			
		||||
    CONSTRUCTORS
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  . = ALIGN(4096);
 | 
			
		||||
  __nosave_begin = .;
 | 
			
		||||
  .data_nosave : { *(.data.nosave) }
 | 
			
		||||
  . = ALIGN(4096);
 | 
			
		||||
  __nosave_end = .;
 | 
			
		||||
 | 
			
		||||
  . = ALIGN(32);
 | 
			
		||||
  .data.cacheline_aligned : { *(.data.cacheline_aligned) }
 | 
			
		||||
 | 
			
		||||
  _edata  =  .;
 | 
			
		||||
  PROVIDE (edata = .);
 | 
			
		||||
 | 
			
		||||
  . = ALIGN(8192);
 | 
			
		||||
  .data.init_task : { *(.data.init_task) }
 | 
			
		||||
 | 
			
		||||
  . = ALIGN(4096);
 | 
			
		||||
  __init_begin = .;
 | 
			
		||||
  .init.text : {
 | 
			
		||||
	_sinittext = .;
 | 
			
		||||
	*(.init.text)
 | 
			
		||||
	_einittext = .;
 | 
			
		||||
  }
 | 
			
		||||
  /* .exit.text is discarded at runtime, not link time,
 | 
			
		||||
     to deal with references from __bug_table */
 | 
			
		||||
  .exit.text : { *(.exit.text) }
 | 
			
		||||
  .init.data : {
 | 
			
		||||
    *(.init.data);
 | 
			
		||||
    __vtop_table_begin = .;
 | 
			
		||||
    *(.vtop_fixup);
 | 
			
		||||
    __vtop_table_end = .;
 | 
			
		||||
    __ptov_table_begin = .;
 | 
			
		||||
    *(.ptov_fixup);
 | 
			
		||||
    __ptov_table_end = .;
 | 
			
		||||
  }
 | 
			
		||||
  . = ALIGN(16);
 | 
			
		||||
  __setup_start = .;
 | 
			
		||||
  .init.setup : { *(.init.setup) }
 | 
			
		||||
  __setup_end = .;
 | 
			
		||||
  __initcall_start = .;
 | 
			
		||||
  .initcall.init : {
 | 
			
		||||
	*(.initcall1.init)
 | 
			
		||||
	*(.initcall2.init)
 | 
			
		||||
	*(.initcall3.init)
 | 
			
		||||
	*(.initcall4.init)
 | 
			
		||||
	*(.initcall5.init)
 | 
			
		||||
	*(.initcall6.init)
 | 
			
		||||
	*(.initcall7.init)
 | 
			
		||||
  }
 | 
			
		||||
  __initcall_end = .;
 | 
			
		||||
 | 
			
		||||
  __con_initcall_start = .;
 | 
			
		||||
  .con_initcall.init : { *(.con_initcall.init) }
 | 
			
		||||
  __con_initcall_end = .;
 | 
			
		||||
 | 
			
		||||
  SECURITY_INIT
 | 
			
		||||
 | 
			
		||||
  __start___ftr_fixup = .;
 | 
			
		||||
  __ftr_fixup : { *(__ftr_fixup) }
 | 
			
		||||
  __stop___ftr_fixup = .;
 | 
			
		||||
 | 
			
		||||
  . = ALIGN(32);
 | 
			
		||||
  __per_cpu_start = .;
 | 
			
		||||
  .data.percpu  : { *(.data.percpu) }
 | 
			
		||||
  __per_cpu_end = .;
 | 
			
		||||
 | 
			
		||||
  . = ALIGN(4096);
 | 
			
		||||
  __initramfs_start = .;
 | 
			
		||||
  .init.ramfs : { *(.init.ramfs) }
 | 
			
		||||
  __initramfs_end = .;
 | 
			
		||||
 | 
			
		||||
  . = ALIGN(4096);
 | 
			
		||||
  __init_end = .;
 | 
			
		||||
 | 
			
		||||
  . = ALIGN(4096);
 | 
			
		||||
  _sextratext = .;
 | 
			
		||||
  _eextratext = .;
 | 
			
		||||
 | 
			
		||||
  __bss_start = .;
 | 
			
		||||
  .bss       :
 | 
			
		||||
  {
 | 
			
		||||
   *(.sbss) *(.scommon)
 | 
			
		||||
   *(.dynbss)
 | 
			
		||||
   *(.bss)
 | 
			
		||||
   *(COMMON)
 | 
			
		||||
  }
 | 
			
		||||
  __bss_stop = .;
 | 
			
		||||
 | 
			
		||||
  _end = . ;
 | 
			
		||||
  PROVIDE (end = .);
 | 
			
		||||
 | 
			
		||||
  /* Sections to be discarded. */
 | 
			
		||||
  /DISCARD/ : {
 | 
			
		||||
    *(.exitcall.exit)
 | 
			
		||||
    *(.exit.data)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								arch/powerpc/lib/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								arch/powerpc/lib/Makefile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
#
 | 
			
		||||
# Makefile for ppc-specific library files..
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
obj-y			:= strcase.o string.o
 | 
			
		||||
obj-$(CONFIG_PPC32)	+= div64.o copy32.o checksum.o
 | 
			
		||||
obj-$(CONFIG_PPC64)	+= copypage.o copyuser.o memcpy.o usercopy.o \
 | 
			
		||||
			   sstep.o checksum64.o
 | 
			
		||||
obj-$(CONFIG_PPC_ISERIES) += e2a.o
 | 
			
		||||
							
								
								
									
										225
									
								
								arch/powerpc/lib/checksum.S
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										225
									
								
								arch/powerpc/lib/checksum.S
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,225 @@
 | 
			
		||||
/*
 | 
			
		||||
 * This file contains assembly-language implementations
 | 
			
		||||
 * of IP-style 1's complement checksum routines.
 | 
			
		||||
 *	
 | 
			
		||||
 *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or
 | 
			
		||||
 *  modify it under the terms of the GNU General Public License
 | 
			
		||||
 *  as published by the Free Software Foundation; either version
 | 
			
		||||
 *  2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * Severely hacked about by Paul Mackerras (paulus@cs.anu.edu.au).
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/sys.h>
 | 
			
		||||
#include <asm/processor.h>
 | 
			
		||||
#include <asm/errno.h>
 | 
			
		||||
#include <asm/ppc_asm.h>
 | 
			
		||||
 | 
			
		||||
	.text
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * ip_fast_csum(buf, len) -- Optimized for IP header
 | 
			
		||||
 * len is in words and is always >= 5.
 | 
			
		||||
 */
 | 
			
		||||
_GLOBAL(ip_fast_csum)
 | 
			
		||||
	lwz	r0,0(r3)
 | 
			
		||||
	lwzu	r5,4(r3)
 | 
			
		||||
	addic.	r4,r4,-2
 | 
			
		||||
	addc	r0,r0,r5
 | 
			
		||||
	mtctr	r4
 | 
			
		||||
	blelr-
 | 
			
		||||
1:	lwzu	r4,4(r3)
 | 
			
		||||
	adde	r0,r0,r4
 | 
			
		||||
	bdnz	1b
 | 
			
		||||
	addze	r0,r0		/* add in final carry */
 | 
			
		||||
	rlwinm	r3,r0,16,0,31	/* fold two halves together */
 | 
			
		||||
	add	r3,r0,r3
 | 
			
		||||
	not	r3,r3
 | 
			
		||||
	srwi	r3,r3,16
 | 
			
		||||
	blr
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Compute checksum of TCP or UDP pseudo-header:
 | 
			
		||||
 *   csum_tcpudp_magic(saddr, daddr, len, proto, sum)
 | 
			
		||||
 */	
 | 
			
		||||
_GLOBAL(csum_tcpudp_magic)
 | 
			
		||||
	rlwimi	r5,r6,16,0,15	/* put proto in upper half of len */
 | 
			
		||||
	addc	r0,r3,r4	/* add 4 32-bit words together */
 | 
			
		||||
	adde	r0,r0,r5
 | 
			
		||||
	adde	r0,r0,r7
 | 
			
		||||
	addze	r0,r0		/* add in final carry */
 | 
			
		||||
	rlwinm	r3,r0,16,0,31	/* fold two halves together */
 | 
			
		||||
	add	r3,r0,r3
 | 
			
		||||
	not	r3,r3
 | 
			
		||||
	srwi	r3,r3,16
 | 
			
		||||
	blr
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * computes the checksum of a memory block at buff, length len,
 | 
			
		||||
 * and adds in "sum" (32-bit)
 | 
			
		||||
 *
 | 
			
		||||
 * csum_partial(buff, len, sum)
 | 
			
		||||
 */
 | 
			
		||||
_GLOBAL(csum_partial)
 | 
			
		||||
	addic	r0,r5,0
 | 
			
		||||
	subi	r3,r3,4
 | 
			
		||||
	srwi.	r6,r4,2
 | 
			
		||||
	beq	3f		/* if we're doing < 4 bytes */
 | 
			
		||||
	andi.	r5,r3,2		/* Align buffer to longword boundary */
 | 
			
		||||
	beq+	1f
 | 
			
		||||
	lhz	r5,4(r3)	/* do 2 bytes to get aligned */
 | 
			
		||||
	addi	r3,r3,2
 | 
			
		||||
	subi	r4,r4,2
 | 
			
		||||
	addc	r0,r0,r5
 | 
			
		||||
	srwi.	r6,r4,2		/* # words to do */
 | 
			
		||||
	beq	3f
 | 
			
		||||
1:	mtctr	r6
 | 
			
		||||
2:	lwzu	r5,4(r3)	/* the bdnz has zero overhead, so it should */
 | 
			
		||||
	adde	r0,r0,r5	/* be unnecessary to unroll this loop */
 | 
			
		||||
	bdnz	2b
 | 
			
		||||
	andi.	r4,r4,3
 | 
			
		||||
3:	cmpwi	0,r4,2
 | 
			
		||||
	blt+	4f
 | 
			
		||||
	lhz	r5,4(r3)
 | 
			
		||||
	addi	r3,r3,2
 | 
			
		||||
	subi	r4,r4,2
 | 
			
		||||
	adde	r0,r0,r5
 | 
			
		||||
4:	cmpwi	0,r4,1
 | 
			
		||||
	bne+	5f
 | 
			
		||||
	lbz	r5,4(r3)
 | 
			
		||||
	slwi	r5,r5,8		/* Upper byte of word */
 | 
			
		||||
	adde	r0,r0,r5
 | 
			
		||||
5:	addze	r3,r0		/* add in final carry */
 | 
			
		||||
	blr
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Computes the checksum of a memory block at src, length len,
 | 
			
		||||
 * and adds in "sum" (32-bit), while copying the block to dst.
 | 
			
		||||
 * If an access exception occurs on src or dst, it stores -EFAULT
 | 
			
		||||
 * to *src_err or *dst_err respectively, and (for an error on
 | 
			
		||||
 * src) zeroes the rest of dst.
 | 
			
		||||
 *
 | 
			
		||||
 * csum_partial_copy_generic(src, dst, len, sum, src_err, dst_err)
 | 
			
		||||
 */
 | 
			
		||||
_GLOBAL(csum_partial_copy_generic)
 | 
			
		||||
	addic	r0,r6,0
 | 
			
		||||
	subi	r3,r3,4
 | 
			
		||||
	subi	r4,r4,4
 | 
			
		||||
	srwi.	r6,r5,2
 | 
			
		||||
	beq	3f		/* if we're doing < 4 bytes */
 | 
			
		||||
	andi.	r9,r4,2		/* Align dst to longword boundary */
 | 
			
		||||
	beq+	1f
 | 
			
		||||
81:	lhz	r6,4(r3)	/* do 2 bytes to get aligned */
 | 
			
		||||
	addi	r3,r3,2
 | 
			
		||||
	subi	r5,r5,2
 | 
			
		||||
91:	sth	r6,4(r4)
 | 
			
		||||
	addi	r4,r4,2
 | 
			
		||||
	addc	r0,r0,r6
 | 
			
		||||
	srwi.	r6,r5,2		/* # words to do */
 | 
			
		||||
	beq	3f
 | 
			
		||||
1:	srwi.	r6,r5,4		/* # groups of 4 words to do */
 | 
			
		||||
	beq	10f
 | 
			
		||||
	mtctr	r6
 | 
			
		||||
71:	lwz	r6,4(r3)
 | 
			
		||||
72:	lwz	r9,8(r3)
 | 
			
		||||
73:	lwz	r10,12(r3)
 | 
			
		||||
74:	lwzu	r11,16(r3)
 | 
			
		||||
	adde	r0,r0,r6
 | 
			
		||||
75:	stw	r6,4(r4)
 | 
			
		||||
	adde	r0,r0,r9
 | 
			
		||||
76:	stw	r9,8(r4)
 | 
			
		||||
	adde	r0,r0,r10
 | 
			
		||||
77:	stw	r10,12(r4)
 | 
			
		||||
	adde	r0,r0,r11
 | 
			
		||||
78:	stwu	r11,16(r4)
 | 
			
		||||
	bdnz	71b
 | 
			
		||||
10:	rlwinm.	r6,r5,30,30,31	/* # words left to do */
 | 
			
		||||
	beq	13f
 | 
			
		||||
	mtctr	r6
 | 
			
		||||
82:	lwzu	r9,4(r3)
 | 
			
		||||
92:	stwu	r9,4(r4)
 | 
			
		||||
	adde	r0,r0,r9
 | 
			
		||||
	bdnz	82b
 | 
			
		||||
13:	andi.	r5,r5,3
 | 
			
		||||
3:	cmpwi	0,r5,2
 | 
			
		||||
	blt+	4f
 | 
			
		||||
83:	lhz	r6,4(r3)
 | 
			
		||||
	addi	r3,r3,2
 | 
			
		||||
	subi	r5,r5,2
 | 
			
		||||
93:	sth	r6,4(r4)
 | 
			
		||||
	addi	r4,r4,2
 | 
			
		||||
	adde	r0,r0,r6
 | 
			
		||||
4:	cmpwi	0,r5,1
 | 
			
		||||
	bne+	5f
 | 
			
		||||
84:	lbz	r6,4(r3)
 | 
			
		||||
94:	stb	r6,4(r4)
 | 
			
		||||
	slwi	r6,r6,8		/* Upper byte of word */
 | 
			
		||||
	adde	r0,r0,r6
 | 
			
		||||
5:	addze	r3,r0		/* add in final carry */
 | 
			
		||||
	blr
 | 
			
		||||
 | 
			
		||||
/* These shouldn't go in the fixup section, since that would
 | 
			
		||||
   cause the ex_table addresses to get out of order. */
 | 
			
		||||
 | 
			
		||||
src_error_4:
 | 
			
		||||
	mfctr	r6		/* update # bytes remaining from ctr */
 | 
			
		||||
	rlwimi	r5,r6,4,0,27
 | 
			
		||||
	b	79f
 | 
			
		||||
src_error_1:
 | 
			
		||||
	li	r6,0
 | 
			
		||||
	subi	r5,r5,2
 | 
			
		||||
95:	sth	r6,4(r4)
 | 
			
		||||
	addi	r4,r4,2
 | 
			
		||||
79:	srwi.	r6,r5,2
 | 
			
		||||
	beq	3f
 | 
			
		||||
	mtctr	r6
 | 
			
		||||
src_error_2:
 | 
			
		||||
	li	r6,0
 | 
			
		||||
96:	stwu	r6,4(r4)
 | 
			
		||||
	bdnz	96b
 | 
			
		||||
3:	andi.	r5,r5,3
 | 
			
		||||
	beq	src_error
 | 
			
		||||
src_error_3:
 | 
			
		||||
	li	r6,0
 | 
			
		||||
	mtctr	r5
 | 
			
		||||
	addi	r4,r4,3
 | 
			
		||||
97:	stbu	r6,1(r4)
 | 
			
		||||
	bdnz	97b
 | 
			
		||||
src_error:
 | 
			
		||||
	cmpwi	0,r7,0
 | 
			
		||||
	beq	1f
 | 
			
		||||
	li	r6,-EFAULT
 | 
			
		||||
	stw	r6,0(r7)
 | 
			
		||||
1:	addze	r3,r0
 | 
			
		||||
	blr
 | 
			
		||||
 | 
			
		||||
dst_error:
 | 
			
		||||
	cmpwi	0,r8,0
 | 
			
		||||
	beq	1f
 | 
			
		||||
	li	r6,-EFAULT
 | 
			
		||||
	stw	r6,0(r8)
 | 
			
		||||
1:	addze	r3,r0
 | 
			
		||||
	blr
 | 
			
		||||
 | 
			
		||||
.section __ex_table,"a"
 | 
			
		||||
	.long	81b,src_error_1
 | 
			
		||||
	.long	91b,dst_error
 | 
			
		||||
	.long	71b,src_error_4
 | 
			
		||||
	.long	72b,src_error_4
 | 
			
		||||
	.long	73b,src_error_4
 | 
			
		||||
	.long	74b,src_error_4
 | 
			
		||||
	.long	75b,dst_error
 | 
			
		||||
	.long	76b,dst_error
 | 
			
		||||
	.long	77b,dst_error
 | 
			
		||||
	.long	78b,dst_error
 | 
			
		||||
	.long	82b,src_error_2
 | 
			
		||||
	.long	92b,dst_error
 | 
			
		||||
	.long	83b,src_error_3
 | 
			
		||||
	.long	93b,dst_error
 | 
			
		||||
	.long	84b,src_error_3
 | 
			
		||||
	.long	94b,dst_error
 | 
			
		||||
	.long	95b,dst_error
 | 
			
		||||
	.long	96b,dst_error
 | 
			
		||||
	.long	97b,dst_error
 | 
			
		||||
							
								
								
									
										229
									
								
								arch/powerpc/lib/checksum64.S
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										229
									
								
								arch/powerpc/lib/checksum64.S
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,229 @@
 | 
			
		||||
/*
 | 
			
		||||
 * This file contains assembly-language implementations
 | 
			
		||||
 * of IP-style 1's complement checksum routines.
 | 
			
		||||
 *	
 | 
			
		||||
 *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or
 | 
			
		||||
 *  modify it under the terms of the GNU General Public License
 | 
			
		||||
 *  as published by the Free Software Foundation; either version
 | 
			
		||||
 *  2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * Severely hacked about by Paul Mackerras (paulus@cs.anu.edu.au).
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/sys.h>
 | 
			
		||||
#include <asm/processor.h>
 | 
			
		||||
#include <asm/errno.h>
 | 
			
		||||
#include <asm/ppc_asm.h>
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * ip_fast_csum(r3=buf, r4=len) -- Optimized for IP header
 | 
			
		||||
 * len is in words and is always >= 5.
 | 
			
		||||
 *
 | 
			
		||||
 * In practice len == 5, but this is not guaranteed.  So this code does not
 | 
			
		||||
 * attempt to use doubleword instructions.
 | 
			
		||||
 */
 | 
			
		||||
_GLOBAL(ip_fast_csum)
 | 
			
		||||
	lwz	r0,0(r3)
 | 
			
		||||
	lwzu	r5,4(r3)
 | 
			
		||||
	addic.	r4,r4,-2
 | 
			
		||||
	addc	r0,r0,r5
 | 
			
		||||
	mtctr	r4
 | 
			
		||||
	blelr-
 | 
			
		||||
1:	lwzu	r4,4(r3)
 | 
			
		||||
	adde	r0,r0,r4
 | 
			
		||||
	bdnz	1b
 | 
			
		||||
	addze	r0,r0		/* add in final carry */
 | 
			
		||||
        rldicl  r4,r0,32,0      /* fold two 32-bit halves together */
 | 
			
		||||
        add     r0,r0,r4
 | 
			
		||||
        srdi    r0,r0,32
 | 
			
		||||
	rlwinm	r3,r0,16,0,31	/* fold two halves together */
 | 
			
		||||
	add	r3,r0,r3
 | 
			
		||||
	not	r3,r3
 | 
			
		||||
	srwi	r3,r3,16
 | 
			
		||||
	blr
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Compute checksum of TCP or UDP pseudo-header:
 | 
			
		||||
 *   csum_tcpudp_magic(r3=saddr, r4=daddr, r5=len, r6=proto, r7=sum)
 | 
			
		||||
 * No real gain trying to do this specially for 64 bit, but
 | 
			
		||||
 * the 32 bit addition may spill into the upper bits of
 | 
			
		||||
 * the doubleword so we still must fold it down from 64.
 | 
			
		||||
 */	
 | 
			
		||||
_GLOBAL(csum_tcpudp_magic)
 | 
			
		||||
	rlwimi	r5,r6,16,0,15	/* put proto in upper half of len */
 | 
			
		||||
	addc	r0,r3,r4	/* add 4 32-bit words together */
 | 
			
		||||
	adde	r0,r0,r5
 | 
			
		||||
	adde	r0,r0,r7
 | 
			
		||||
        rldicl  r4,r0,32,0      /* fold 64 bit value */
 | 
			
		||||
        add     r0,r4,r0
 | 
			
		||||
        srdi    r0,r0,32
 | 
			
		||||
	rlwinm	r3,r0,16,0,31	/* fold two halves together */
 | 
			
		||||
	add	r3,r0,r3
 | 
			
		||||
	not	r3,r3
 | 
			
		||||
	srwi	r3,r3,16
 | 
			
		||||
	blr
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Computes the checksum of a memory block at buff, length len,
 | 
			
		||||
 * and adds in "sum" (32-bit).
 | 
			
		||||
 *
 | 
			
		||||
 * This code assumes at least halfword alignment, though the length
 | 
			
		||||
 * can be any number of bytes.  The sum is accumulated in r5.
 | 
			
		||||
 *
 | 
			
		||||
 * csum_partial(r3=buff, r4=len, r5=sum)
 | 
			
		||||
 */
 | 
			
		||||
_GLOBAL(csum_partial)
 | 
			
		||||
        subi	r3,r3,8		/* we'll offset by 8 for the loads */
 | 
			
		||||
        srdi.	r6,r4,3         /* divide by 8 for doubleword count */
 | 
			
		||||
        addic   r5,r5,0         /* clear carry */
 | 
			
		||||
        beq	3f              /* if we're doing < 8 bytes */
 | 
			
		||||
        andi.	r0,r3,2         /* aligned on a word boundary already? */
 | 
			
		||||
        beq+	1f
 | 
			
		||||
        lhz     r6,8(r3)        /* do 2 bytes to get aligned */
 | 
			
		||||
        addi    r3,r3,2
 | 
			
		||||
        subi    r4,r4,2
 | 
			
		||||
        addc    r5,r5,r6
 | 
			
		||||
        srdi.   r6,r4,3         /* recompute number of doublewords */
 | 
			
		||||
        beq     3f              /* any left? */
 | 
			
		||||
1:      mtctr   r6
 | 
			
		||||
2:      ldu     r6,8(r3)        /* main sum loop */
 | 
			
		||||
        adde    r5,r5,r6
 | 
			
		||||
        bdnz    2b
 | 
			
		||||
        andi.	r4,r4,7         /* compute bytes left to sum after doublewords */
 | 
			
		||||
3:	cmpwi	0,r4,4		/* is at least a full word left? */
 | 
			
		||||
	blt	4f
 | 
			
		||||
	lwz	r6,8(r3)	/* sum this word */
 | 
			
		||||
	addi	r3,r3,4
 | 
			
		||||
	subi	r4,r4,4
 | 
			
		||||
	adde	r5,r5,r6
 | 
			
		||||
4:	cmpwi	0,r4,2		/* is at least a halfword left? */
 | 
			
		||||
        blt+	5f
 | 
			
		||||
        lhz     r6,8(r3)        /* sum this halfword */
 | 
			
		||||
        addi    r3,r3,2
 | 
			
		||||
        subi    r4,r4,2
 | 
			
		||||
        adde    r5,r5,r6
 | 
			
		||||
5:	cmpwi	0,r4,1		/* is at least a byte left? */
 | 
			
		||||
        bne+    6f
 | 
			
		||||
        lbz     r6,8(r3)        /* sum this byte */
 | 
			
		||||
        slwi    r6,r6,8         /* this byte is assumed to be the upper byte of a halfword */
 | 
			
		||||
        adde    r5,r5,r6
 | 
			
		||||
6:      addze	r5,r5		/* add in final carry */
 | 
			
		||||
	rldicl  r4,r5,32,0      /* fold two 32-bit halves together */
 | 
			
		||||
        add     r3,r4,r5
 | 
			
		||||
        srdi    r3,r3,32
 | 
			
		||||
        blr
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Computes the checksum of a memory block at src, length len,
 | 
			
		||||
 * and adds in "sum" (32-bit), while copying the block to dst.
 | 
			
		||||
 * If an access exception occurs on src or dst, it stores -EFAULT
 | 
			
		||||
 * to *src_err or *dst_err respectively, and (for an error on
 | 
			
		||||
 * src) zeroes the rest of dst.
 | 
			
		||||
 *
 | 
			
		||||
 * This code needs to be reworked to take advantage of 64 bit sum+copy.
 | 
			
		||||
 * However, due to tokenring halfword alignment problems this will be very
 | 
			
		||||
 * tricky.  For now we'll leave it until we instrument it somehow.
 | 
			
		||||
 *
 | 
			
		||||
 * csum_partial_copy_generic(r3=src, r4=dst, r5=len, r6=sum, r7=src_err, r8=dst_err)
 | 
			
		||||
 */
 | 
			
		||||
_GLOBAL(csum_partial_copy_generic)
 | 
			
		||||
	addic	r0,r6,0
 | 
			
		||||
	subi	r3,r3,4
 | 
			
		||||
	subi	r4,r4,4
 | 
			
		||||
	srwi.	r6,r5,2
 | 
			
		||||
	beq	3f		/* if we're doing < 4 bytes */
 | 
			
		||||
	andi.	r9,r4,2		/* Align dst to longword boundary */
 | 
			
		||||
	beq+	1f
 | 
			
		||||
81:	lhz	r6,4(r3)	/* do 2 bytes to get aligned */
 | 
			
		||||
	addi	r3,r3,2
 | 
			
		||||
	subi	r5,r5,2
 | 
			
		||||
91:	sth	r6,4(r4)
 | 
			
		||||
	addi	r4,r4,2
 | 
			
		||||
	addc	r0,r0,r6
 | 
			
		||||
	srwi.	r6,r5,2		/* # words to do */
 | 
			
		||||
	beq	3f
 | 
			
		||||
1:	mtctr	r6
 | 
			
		||||
82:	lwzu	r6,4(r3)	/* the bdnz has zero overhead, so it should */
 | 
			
		||||
92:	stwu	r6,4(r4)	/* be unnecessary to unroll this loop */
 | 
			
		||||
	adde	r0,r0,r6
 | 
			
		||||
	bdnz	82b
 | 
			
		||||
	andi.	r5,r5,3
 | 
			
		||||
3:	cmpwi	0,r5,2
 | 
			
		||||
	blt+	4f
 | 
			
		||||
83:	lhz	r6,4(r3)
 | 
			
		||||
	addi	r3,r3,2
 | 
			
		||||
	subi	r5,r5,2
 | 
			
		||||
93:	sth	r6,4(r4)
 | 
			
		||||
	addi	r4,r4,2
 | 
			
		||||
	adde	r0,r0,r6
 | 
			
		||||
4:	cmpwi	0,r5,1
 | 
			
		||||
	bne+	5f
 | 
			
		||||
84:	lbz	r6,4(r3)
 | 
			
		||||
94:	stb	r6,4(r4)
 | 
			
		||||
	slwi	r6,r6,8		/* Upper byte of word */
 | 
			
		||||
	adde	r0,r0,r6
 | 
			
		||||
5:	addze	r3,r0		/* add in final carry (unlikely with 64-bit regs) */
 | 
			
		||||
        rldicl  r4,r3,32,0      /* fold 64 bit value */
 | 
			
		||||
        add     r3,r4,r3
 | 
			
		||||
        srdi    r3,r3,32
 | 
			
		||||
	blr
 | 
			
		||||
 | 
			
		||||
/* These shouldn't go in the fixup section, since that would
 | 
			
		||||
   cause the ex_table addresses to get out of order. */
 | 
			
		||||
 | 
			
		||||
	.globl src_error_1
 | 
			
		||||
src_error_1:
 | 
			
		||||
	li	r6,0
 | 
			
		||||
	subi	r5,r5,2
 | 
			
		||||
95:	sth	r6,4(r4)
 | 
			
		||||
	addi	r4,r4,2
 | 
			
		||||
	srwi.	r6,r5,2
 | 
			
		||||
	beq	3f
 | 
			
		||||
	mtctr	r6
 | 
			
		||||
	.globl src_error_2
 | 
			
		||||
src_error_2:
 | 
			
		||||
	li	r6,0
 | 
			
		||||
96:	stwu	r6,4(r4)
 | 
			
		||||
	bdnz	96b
 | 
			
		||||
3:	andi.	r5,r5,3
 | 
			
		||||
	beq	src_error
 | 
			
		||||
	.globl src_error_3
 | 
			
		||||
src_error_3:
 | 
			
		||||
	li	r6,0
 | 
			
		||||
	mtctr	r5
 | 
			
		||||
	addi	r4,r4,3
 | 
			
		||||
97:	stbu	r6,1(r4)
 | 
			
		||||
	bdnz	97b
 | 
			
		||||
	.globl src_error
 | 
			
		||||
src_error:
 | 
			
		||||
	cmpdi	0,r7,0
 | 
			
		||||
	beq	1f
 | 
			
		||||
	li	r6,-EFAULT
 | 
			
		||||
	stw	r6,0(r7)
 | 
			
		||||
1:	addze	r3,r0
 | 
			
		||||
	blr
 | 
			
		||||
 | 
			
		||||
	.globl dst_error
 | 
			
		||||
dst_error:
 | 
			
		||||
	cmpdi	0,r8,0
 | 
			
		||||
	beq	1f
 | 
			
		||||
	li	r6,-EFAULT
 | 
			
		||||
	stw	r6,0(r8)
 | 
			
		||||
1:	addze	r3,r0
 | 
			
		||||
	blr
 | 
			
		||||
 | 
			
		||||
.section __ex_table,"a"
 | 
			
		||||
	.align  3
 | 
			
		||||
	.llong	81b,src_error_1
 | 
			
		||||
	.llong	91b,dst_error
 | 
			
		||||
	.llong	82b,src_error_2
 | 
			
		||||
	.llong	92b,dst_error
 | 
			
		||||
	.llong	83b,src_error_3
 | 
			
		||||
	.llong	93b,dst_error
 | 
			
		||||
	.llong	84b,src_error_3
 | 
			
		||||
	.llong	94b,dst_error
 | 
			
		||||
	.llong	95b,dst_error
 | 
			
		||||
	.llong	96b,dst_error
 | 
			
		||||
	.llong	97b,dst_error
 | 
			
		||||
							
								
								
									
										543
									
								
								arch/powerpc/lib/copy32.S
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										543
									
								
								arch/powerpc/lib/copy32.S
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,543 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Memory copy functions for 32-bit PowerPC.
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 1996-2005 Paul Mackerras.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version
 | 
			
		||||
 * 2 of the License, or (at your option) any later version.
 | 
			
		||||
 */
 | 
			
		||||
#include <linux/config.h>
 | 
			
		||||
#include <asm/processor.h>
 | 
			
		||||
#include <asm/cache.h>
 | 
			
		||||
#include <asm/errno.h>
 | 
			
		||||
#include <asm/ppc_asm.h>
 | 
			
		||||
 | 
			
		||||
#define COPY_16_BYTES		\
 | 
			
		||||
	lwz	r7,4(r4);	\
 | 
			
		||||
	lwz	r8,8(r4);	\
 | 
			
		||||
	lwz	r9,12(r4);	\
 | 
			
		||||
	lwzu	r10,16(r4);	\
 | 
			
		||||
	stw	r7,4(r6);	\
 | 
			
		||||
	stw	r8,8(r6);	\
 | 
			
		||||
	stw	r9,12(r6);	\
 | 
			
		||||
	stwu	r10,16(r6)
 | 
			
		||||
 | 
			
		||||
#define COPY_16_BYTES_WITHEX(n)	\
 | 
			
		||||
8 ## n ## 0:			\
 | 
			
		||||
	lwz	r7,4(r4);	\
 | 
			
		||||
8 ## n ## 1:			\
 | 
			
		||||
	lwz	r8,8(r4);	\
 | 
			
		||||
8 ## n ## 2:			\
 | 
			
		||||
	lwz	r9,12(r4);	\
 | 
			
		||||
8 ## n ## 3:			\
 | 
			
		||||
	lwzu	r10,16(r4);	\
 | 
			
		||||
8 ## n ## 4:			\
 | 
			
		||||
	stw	r7,4(r6);	\
 | 
			
		||||
8 ## n ## 5:			\
 | 
			
		||||
	stw	r8,8(r6);	\
 | 
			
		||||
8 ## n ## 6:			\
 | 
			
		||||
	stw	r9,12(r6);	\
 | 
			
		||||
8 ## n ## 7:			\
 | 
			
		||||
	stwu	r10,16(r6)
 | 
			
		||||
 | 
			
		||||
#define COPY_16_BYTES_EXCODE(n)			\
 | 
			
		||||
9 ## n ## 0:					\
 | 
			
		||||
	addi	r5,r5,-(16 * n);		\
 | 
			
		||||
	b	104f;				\
 | 
			
		||||
9 ## n ## 1:					\
 | 
			
		||||
	addi	r5,r5,-(16 * n);		\
 | 
			
		||||
	b	105f;				\
 | 
			
		||||
.section __ex_table,"a";			\
 | 
			
		||||
	.align	2;				\
 | 
			
		||||
	.long	8 ## n ## 0b,9 ## n ## 0b;	\
 | 
			
		||||
	.long	8 ## n ## 1b,9 ## n ## 0b;	\
 | 
			
		||||
	.long	8 ## n ## 2b,9 ## n ## 0b;	\
 | 
			
		||||
	.long	8 ## n ## 3b,9 ## n ## 0b;	\
 | 
			
		||||
	.long	8 ## n ## 4b,9 ## n ## 1b;	\
 | 
			
		||||
	.long	8 ## n ## 5b,9 ## n ## 1b;	\
 | 
			
		||||
	.long	8 ## n ## 6b,9 ## n ## 1b;	\
 | 
			
		||||
	.long	8 ## n ## 7b,9 ## n ## 1b;	\
 | 
			
		||||
	.text
 | 
			
		||||
 | 
			
		||||
	.text
 | 
			
		||||
	.stabs	"arch/powerpc/lib/",N_SO,0,0,0f
 | 
			
		||||
	.stabs	"copy32.S",N_SO,0,0,0f
 | 
			
		||||
0:
 | 
			
		||||
 | 
			
		||||
CACHELINE_BYTES = L1_CACHE_LINE_SIZE
 | 
			
		||||
LG_CACHELINE_BYTES = LG_L1_CACHE_LINE_SIZE
 | 
			
		||||
CACHELINE_MASK = (L1_CACHE_LINE_SIZE-1)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Use dcbz on the complete cache lines in the destination
 | 
			
		||||
 * to set them to zero.  This requires that the destination
 | 
			
		||||
 * area is cacheable.  -- paulus
 | 
			
		||||
 */
 | 
			
		||||
_GLOBAL(cacheable_memzero)
 | 
			
		||||
	mr	r5,r4
 | 
			
		||||
	li	r4,0
 | 
			
		||||
	addi	r6,r3,-4
 | 
			
		||||
	cmplwi	0,r5,4
 | 
			
		||||
	blt	7f
 | 
			
		||||
	stwu	r4,4(r6)
 | 
			
		||||
	beqlr
 | 
			
		||||
	andi.	r0,r6,3
 | 
			
		||||
	add	r5,r0,r5
 | 
			
		||||
	subf	r6,r0,r6
 | 
			
		||||
	clrlwi	r7,r6,32-LG_CACHELINE_BYTES
 | 
			
		||||
	add	r8,r7,r5
 | 
			
		||||
	srwi	r9,r8,LG_CACHELINE_BYTES
 | 
			
		||||
	addic.	r9,r9,-1	/* total number of complete cachelines */
 | 
			
		||||
	ble	2f
 | 
			
		||||
	xori	r0,r7,CACHELINE_MASK & ~3
 | 
			
		||||
	srwi.	r0,r0,2
 | 
			
		||||
	beq	3f
 | 
			
		||||
	mtctr	r0
 | 
			
		||||
4:	stwu	r4,4(r6)
 | 
			
		||||
	bdnz	4b
 | 
			
		||||
3:	mtctr	r9
 | 
			
		||||
	li	r7,4
 | 
			
		||||
#if !defined(CONFIG_8xx)
 | 
			
		||||
10:	dcbz	r7,r6
 | 
			
		||||
#else
 | 
			
		||||
10:	stw	r4, 4(r6)
 | 
			
		||||
	stw	r4, 8(r6)
 | 
			
		||||
	stw	r4, 12(r6)
 | 
			
		||||
	stw	r4, 16(r6)
 | 
			
		||||
#if CACHE_LINE_SIZE >= 32
 | 
			
		||||
	stw	r4, 20(r6)
 | 
			
		||||
	stw	r4, 24(r6)
 | 
			
		||||
	stw	r4, 28(r6)
 | 
			
		||||
	stw	r4, 32(r6)
 | 
			
		||||
#endif /* CACHE_LINE_SIZE */
 | 
			
		||||
#endif
 | 
			
		||||
	addi	r6,r6,CACHELINE_BYTES
 | 
			
		||||
	bdnz	10b
 | 
			
		||||
	clrlwi	r5,r8,32-LG_CACHELINE_BYTES
 | 
			
		||||
	addi	r5,r5,4
 | 
			
		||||
2:	srwi	r0,r5,2
 | 
			
		||||
	mtctr	r0
 | 
			
		||||
	bdz	6f
 | 
			
		||||
1:	stwu	r4,4(r6)
 | 
			
		||||
	bdnz	1b
 | 
			
		||||
6:	andi.	r5,r5,3
 | 
			
		||||
7:	cmpwi	0,r5,0
 | 
			
		||||
	beqlr
 | 
			
		||||
	mtctr	r5
 | 
			
		||||
	addi	r6,r6,3
 | 
			
		||||
8:	stbu	r4,1(r6)
 | 
			
		||||
	bdnz	8b
 | 
			
		||||
	blr
 | 
			
		||||
 | 
			
		||||
_GLOBAL(memset)
 | 
			
		||||
	rlwimi	r4,r4,8,16,23
 | 
			
		||||
	rlwimi	r4,r4,16,0,15
 | 
			
		||||
	addi	r6,r3,-4
 | 
			
		||||
	cmplwi	0,r5,4
 | 
			
		||||
	blt	7f
 | 
			
		||||
	stwu	r4,4(r6)
 | 
			
		||||
	beqlr
 | 
			
		||||
	andi.	r0,r6,3
 | 
			
		||||
	add	r5,r0,r5
 | 
			
		||||
	subf	r6,r0,r6
 | 
			
		||||
	srwi	r0,r5,2
 | 
			
		||||
	mtctr	r0
 | 
			
		||||
	bdz	6f
 | 
			
		||||
1:	stwu	r4,4(r6)
 | 
			
		||||
	bdnz	1b
 | 
			
		||||
6:	andi.	r5,r5,3
 | 
			
		||||
7:	cmpwi	0,r5,0
 | 
			
		||||
	beqlr
 | 
			
		||||
	mtctr	r5
 | 
			
		||||
	addi	r6,r6,3
 | 
			
		||||
8:	stbu	r4,1(r6)
 | 
			
		||||
	bdnz	8b
 | 
			
		||||
	blr
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * This version uses dcbz on the complete cache lines in the
 | 
			
		||||
 * destination area to reduce memory traffic.  This requires that
 | 
			
		||||
 * the destination area is cacheable.
 | 
			
		||||
 * We only use this version if the source and dest don't overlap.
 | 
			
		||||
 * -- paulus.
 | 
			
		||||
 */
 | 
			
		||||
_GLOBAL(cacheable_memcpy)
 | 
			
		||||
	add	r7,r3,r5		/* test if the src & dst overlap */
 | 
			
		||||
	add	r8,r4,r5
 | 
			
		||||
	cmplw	0,r4,r7
 | 
			
		||||
	cmplw	1,r3,r8
 | 
			
		||||
	crand	0,0,4			/* cr0.lt &= cr1.lt */
 | 
			
		||||
	blt	memcpy			/* if regions overlap */
 | 
			
		||||
 | 
			
		||||
	addi	r4,r4,-4
 | 
			
		||||
	addi	r6,r3,-4
 | 
			
		||||
	neg	r0,r3
 | 
			
		||||
	andi.	r0,r0,CACHELINE_MASK	/* # bytes to start of cache line */
 | 
			
		||||
	beq	58f
 | 
			
		||||
 | 
			
		||||
	cmplw	0,r5,r0			/* is this more than total to do? */
 | 
			
		||||
	blt	63f			/* if not much to do */
 | 
			
		||||
	andi.	r8,r0,3			/* get it word-aligned first */
 | 
			
		||||
	subf	r5,r0,r5
 | 
			
		||||
	mtctr	r8
 | 
			
		||||
	beq+	61f
 | 
			
		||||
70:	lbz	r9,4(r4)		/* do some bytes */
 | 
			
		||||
	stb	r9,4(r6)
 | 
			
		||||
	addi	r4,r4,1
 | 
			
		||||
	addi	r6,r6,1
 | 
			
		||||
	bdnz	70b
 | 
			
		||||
61:	srwi.	r0,r0,2
 | 
			
		||||
	mtctr	r0
 | 
			
		||||
	beq	58f
 | 
			
		||||
72:	lwzu	r9,4(r4)		/* do some words */
 | 
			
		||||
	stwu	r9,4(r6)
 | 
			
		||||
	bdnz	72b
 | 
			
		||||
 | 
			
		||||
58:	srwi.	r0,r5,LG_CACHELINE_BYTES /* # complete cachelines */
 | 
			
		||||
	clrlwi	r5,r5,32-LG_CACHELINE_BYTES
 | 
			
		||||
	li	r11,4
 | 
			
		||||
	mtctr	r0
 | 
			
		||||
	beq	63f
 | 
			
		||||
53:
 | 
			
		||||
#if !defined(CONFIG_8xx)
 | 
			
		||||
	dcbz	r11,r6
 | 
			
		||||
#endif
 | 
			
		||||
	COPY_16_BYTES
 | 
			
		||||
#if L1_CACHE_LINE_SIZE >= 32
 | 
			
		||||
	COPY_16_BYTES
 | 
			
		||||
#if L1_CACHE_LINE_SIZE >= 64
 | 
			
		||||
	COPY_16_BYTES
 | 
			
		||||
	COPY_16_BYTES
 | 
			
		||||
#if L1_CACHE_LINE_SIZE >= 128
 | 
			
		||||
	COPY_16_BYTES
 | 
			
		||||
	COPY_16_BYTES
 | 
			
		||||
	COPY_16_BYTES
 | 
			
		||||
	COPY_16_BYTES
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
	bdnz	53b
 | 
			
		||||
 | 
			
		||||
63:	srwi.	r0,r5,2
 | 
			
		||||
	mtctr	r0
 | 
			
		||||
	beq	64f
 | 
			
		||||
30:	lwzu	r0,4(r4)
 | 
			
		||||
	stwu	r0,4(r6)
 | 
			
		||||
	bdnz	30b
 | 
			
		||||
 | 
			
		||||
64:	andi.	r0,r5,3
 | 
			
		||||
	mtctr	r0
 | 
			
		||||
	beq+	65f
 | 
			
		||||
40:	lbz	r0,4(r4)
 | 
			
		||||
	stb	r0,4(r6)
 | 
			
		||||
	addi	r4,r4,1
 | 
			
		||||
	addi	r6,r6,1
 | 
			
		||||
	bdnz	40b
 | 
			
		||||
65:	blr
 | 
			
		||||
 | 
			
		||||
_GLOBAL(memmove)
 | 
			
		||||
	cmplw	0,r3,r4
 | 
			
		||||
	bgt	backwards_memcpy
 | 
			
		||||
	/* fall through */
 | 
			
		||||
 | 
			
		||||
_GLOBAL(memcpy)
 | 
			
		||||
	srwi.	r7,r5,3
 | 
			
		||||
	addi	r6,r3,-4
 | 
			
		||||
	addi	r4,r4,-4
 | 
			
		||||
	beq	2f			/* if less than 8 bytes to do */
 | 
			
		||||
	andi.	r0,r6,3			/* get dest word aligned */
 | 
			
		||||
	mtctr	r7
 | 
			
		||||
	bne	5f
 | 
			
		||||
1:	lwz	r7,4(r4)
 | 
			
		||||
	lwzu	r8,8(r4)
 | 
			
		||||
	stw	r7,4(r6)
 | 
			
		||||
	stwu	r8,8(r6)
 | 
			
		||||
	bdnz	1b
 | 
			
		||||
	andi.	r5,r5,7
 | 
			
		||||
2:	cmplwi	0,r5,4
 | 
			
		||||
	blt	3f
 | 
			
		||||
	lwzu	r0,4(r4)
 | 
			
		||||
	addi	r5,r5,-4
 | 
			
		||||
	stwu	r0,4(r6)
 | 
			
		||||
3:	cmpwi	0,r5,0
 | 
			
		||||
	beqlr
 | 
			
		||||
	mtctr	r5
 | 
			
		||||
	addi	r4,r4,3
 | 
			
		||||
	addi	r6,r6,3
 | 
			
		||||
4:	lbzu	r0,1(r4)
 | 
			
		||||
	stbu	r0,1(r6)
 | 
			
		||||
	bdnz	4b
 | 
			
		||||
	blr
 | 
			
		||||
5:	subfic	r0,r0,4
 | 
			
		||||
	mtctr	r0
 | 
			
		||||
6:	lbz	r7,4(r4)
 | 
			
		||||
	addi	r4,r4,1
 | 
			
		||||
	stb	r7,4(r6)
 | 
			
		||||
	addi	r6,r6,1
 | 
			
		||||
	bdnz	6b
 | 
			
		||||
	subf	r5,r0,r5
 | 
			
		||||
	rlwinm.	r7,r5,32-3,3,31
 | 
			
		||||
	beq	2b
 | 
			
		||||
	mtctr	r7
 | 
			
		||||
	b	1b
 | 
			
		||||
 | 
			
		||||
_GLOBAL(backwards_memcpy)
 | 
			
		||||
	rlwinm.	r7,r5,32-3,3,31		/* r0 = r5 >> 3 */
 | 
			
		||||
	add	r6,r3,r5
 | 
			
		||||
	add	r4,r4,r5
 | 
			
		||||
	beq	2f
 | 
			
		||||
	andi.	r0,r6,3
 | 
			
		||||
	mtctr	r7
 | 
			
		||||
	bne	5f
 | 
			
		||||
1:	lwz	r7,-4(r4)
 | 
			
		||||
	lwzu	r8,-8(r4)
 | 
			
		||||
	stw	r7,-4(r6)
 | 
			
		||||
	stwu	r8,-8(r6)
 | 
			
		||||
	bdnz	1b
 | 
			
		||||
	andi.	r5,r5,7
 | 
			
		||||
2:	cmplwi	0,r5,4
 | 
			
		||||
	blt	3f
 | 
			
		||||
	lwzu	r0,-4(r4)
 | 
			
		||||
	subi	r5,r5,4
 | 
			
		||||
	stwu	r0,-4(r6)
 | 
			
		||||
3:	cmpwi	0,r5,0
 | 
			
		||||
	beqlr
 | 
			
		||||
	mtctr	r5
 | 
			
		||||
4:	lbzu	r0,-1(r4)
 | 
			
		||||
	stbu	r0,-1(r6)
 | 
			
		||||
	bdnz	4b
 | 
			
		||||
	blr
 | 
			
		||||
5:	mtctr	r0
 | 
			
		||||
6:	lbzu	r7,-1(r4)
 | 
			
		||||
	stbu	r7,-1(r6)
 | 
			
		||||
	bdnz	6b
 | 
			
		||||
	subf	r5,r0,r5
 | 
			
		||||
	rlwinm.	r7,r5,32-3,3,31
 | 
			
		||||
	beq	2b
 | 
			
		||||
	mtctr	r7
 | 
			
		||||
	b	1b
 | 
			
		||||
 | 
			
		||||
_GLOBAL(__copy_tofrom_user)
 | 
			
		||||
	addi	r4,r4,-4
 | 
			
		||||
	addi	r6,r3,-4
 | 
			
		||||
	neg	r0,r3
 | 
			
		||||
	andi.	r0,r0,CACHELINE_MASK	/* # bytes to start of cache line */
 | 
			
		||||
	beq	58f
 | 
			
		||||
 | 
			
		||||
	cmplw	0,r5,r0			/* is this more than total to do? */
 | 
			
		||||
	blt	63f			/* if not much to do */
 | 
			
		||||
	andi.	r8,r0,3			/* get it word-aligned first */
 | 
			
		||||
	mtctr	r8
 | 
			
		||||
	beq+	61f
 | 
			
		||||
70:	lbz	r9,4(r4)		/* do some bytes */
 | 
			
		||||
71:	stb	r9,4(r6)
 | 
			
		||||
	addi	r4,r4,1
 | 
			
		||||
	addi	r6,r6,1
 | 
			
		||||
	bdnz	70b
 | 
			
		||||
61:	subf	r5,r0,r5
 | 
			
		||||
	srwi.	r0,r0,2
 | 
			
		||||
	mtctr	r0
 | 
			
		||||
	beq	58f
 | 
			
		||||
72:	lwzu	r9,4(r4)		/* do some words */
 | 
			
		||||
73:	stwu	r9,4(r6)
 | 
			
		||||
	bdnz	72b
 | 
			
		||||
 | 
			
		||||
	.section __ex_table,"a"
 | 
			
		||||
	.align	2
 | 
			
		||||
	.long	70b,100f
 | 
			
		||||
	.long	71b,101f
 | 
			
		||||
	.long	72b,102f
 | 
			
		||||
	.long	73b,103f
 | 
			
		||||
	.text
 | 
			
		||||
 | 
			
		||||
58:	srwi.	r0,r5,LG_CACHELINE_BYTES /* # complete cachelines */
 | 
			
		||||
	clrlwi	r5,r5,32-LG_CACHELINE_BYTES
 | 
			
		||||
	li	r11,4
 | 
			
		||||
	beq	63f
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_8xx
 | 
			
		||||
	/* Don't use prefetch on 8xx */
 | 
			
		||||
	mtctr	r0
 | 
			
		||||
	li	r0,0
 | 
			
		||||
53:	COPY_16_BYTES_WITHEX(0)
 | 
			
		||||
	bdnz	53b
 | 
			
		||||
 | 
			
		||||
#else /* not CONFIG_8xx */
 | 
			
		||||
	/* Here we decide how far ahead to prefetch the source */
 | 
			
		||||
	li	r3,4
 | 
			
		||||
	cmpwi	r0,1
 | 
			
		||||
	li	r7,0
 | 
			
		||||
	ble	114f
 | 
			
		||||
	li	r7,1
 | 
			
		||||
#if MAX_COPY_PREFETCH > 1
 | 
			
		||||
	/* Heuristically, for large transfers we prefetch
 | 
			
		||||
	   MAX_COPY_PREFETCH cachelines ahead.  For small transfers
 | 
			
		||||
	   we prefetch 1 cacheline ahead. */
 | 
			
		||||
	cmpwi	r0,MAX_COPY_PREFETCH
 | 
			
		||||
	ble	112f
 | 
			
		||||
	li	r7,MAX_COPY_PREFETCH
 | 
			
		||||
112:	mtctr	r7
 | 
			
		||||
111:	dcbt	r3,r4
 | 
			
		||||
	addi	r3,r3,CACHELINE_BYTES
 | 
			
		||||
	bdnz	111b
 | 
			
		||||
#else
 | 
			
		||||
	dcbt	r3,r4
 | 
			
		||||
	addi	r3,r3,CACHELINE_BYTES
 | 
			
		||||
#endif /* MAX_COPY_PREFETCH > 1 */
 | 
			
		||||
 | 
			
		||||
114:	subf	r8,r7,r0
 | 
			
		||||
	mr	r0,r7
 | 
			
		||||
	mtctr	r8
 | 
			
		||||
 | 
			
		||||
53:	dcbt	r3,r4
 | 
			
		||||
54:	dcbz	r11,r6
 | 
			
		||||
	.section __ex_table,"a"
 | 
			
		||||
	.align	2
 | 
			
		||||
	.long	54b,105f
 | 
			
		||||
	.text
 | 
			
		||||
/* the main body of the cacheline loop */
 | 
			
		||||
	COPY_16_BYTES_WITHEX(0)
 | 
			
		||||
#if L1_CACHE_LINE_SIZE >= 32
 | 
			
		||||
	COPY_16_BYTES_WITHEX(1)
 | 
			
		||||
#if L1_CACHE_LINE_SIZE >= 64
 | 
			
		||||
	COPY_16_BYTES_WITHEX(2)
 | 
			
		||||
	COPY_16_BYTES_WITHEX(3)
 | 
			
		||||
#if L1_CACHE_LINE_SIZE >= 128
 | 
			
		||||
	COPY_16_BYTES_WITHEX(4)
 | 
			
		||||
	COPY_16_BYTES_WITHEX(5)
 | 
			
		||||
	COPY_16_BYTES_WITHEX(6)
 | 
			
		||||
	COPY_16_BYTES_WITHEX(7)
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
	bdnz	53b
 | 
			
		||||
	cmpwi	r0,0
 | 
			
		||||
	li	r3,4
 | 
			
		||||
	li	r7,0
 | 
			
		||||
	bne	114b
 | 
			
		||||
#endif /* CONFIG_8xx */
 | 
			
		||||
 | 
			
		||||
63:	srwi.	r0,r5,2
 | 
			
		||||
	mtctr	r0
 | 
			
		||||
	beq	64f
 | 
			
		||||
30:	lwzu	r0,4(r4)
 | 
			
		||||
31:	stwu	r0,4(r6)
 | 
			
		||||
	bdnz	30b
 | 
			
		||||
 | 
			
		||||
64:	andi.	r0,r5,3
 | 
			
		||||
	mtctr	r0
 | 
			
		||||
	beq+	65f
 | 
			
		||||
40:	lbz	r0,4(r4)
 | 
			
		||||
41:	stb	r0,4(r6)
 | 
			
		||||
	addi	r4,r4,1
 | 
			
		||||
	addi	r6,r6,1
 | 
			
		||||
	bdnz	40b
 | 
			
		||||
65:	li	r3,0
 | 
			
		||||
	blr
 | 
			
		||||
 | 
			
		||||
/* read fault, initial single-byte copy */
 | 
			
		||||
100:	li	r9,0
 | 
			
		||||
	b	90f
 | 
			
		||||
/* write fault, initial single-byte copy */
 | 
			
		||||
101:	li	r9,1
 | 
			
		||||
90:	subf	r5,r8,r5
 | 
			
		||||
	li	r3,0
 | 
			
		||||
	b	99f
 | 
			
		||||
/* read fault, initial word copy */
 | 
			
		||||
102:	li	r9,0
 | 
			
		||||
	b	91f
 | 
			
		||||
/* write fault, initial word copy */
 | 
			
		||||
103:	li	r9,1
 | 
			
		||||
91:	li	r3,2
 | 
			
		||||
	b	99f
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * this stuff handles faults in the cacheline loop and branches to either
 | 
			
		||||
 * 104f (if in read part) or 105f (if in write part), after updating r5
 | 
			
		||||
 */
 | 
			
		||||
	COPY_16_BYTES_EXCODE(0)
 | 
			
		||||
#if L1_CACHE_LINE_SIZE >= 32
 | 
			
		||||
	COPY_16_BYTES_EXCODE(1)
 | 
			
		||||
#if L1_CACHE_LINE_SIZE >= 64
 | 
			
		||||
	COPY_16_BYTES_EXCODE(2)
 | 
			
		||||
	COPY_16_BYTES_EXCODE(3)
 | 
			
		||||
#if L1_CACHE_LINE_SIZE >= 128
 | 
			
		||||
	COPY_16_BYTES_EXCODE(4)
 | 
			
		||||
	COPY_16_BYTES_EXCODE(5)
 | 
			
		||||
	COPY_16_BYTES_EXCODE(6)
 | 
			
		||||
	COPY_16_BYTES_EXCODE(7)
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* read fault in cacheline loop */
 | 
			
		||||
104:	li	r9,0
 | 
			
		||||
	b	92f
 | 
			
		||||
/* fault on dcbz (effectively a write fault) */
 | 
			
		||||
/* or write fault in cacheline loop */
 | 
			
		||||
105:	li	r9,1
 | 
			
		||||
92:	li	r3,LG_CACHELINE_BYTES
 | 
			
		||||
	mfctr	r8
 | 
			
		||||
	add	r0,r0,r8
 | 
			
		||||
	b	106f
 | 
			
		||||
/* read fault in final word loop */
 | 
			
		||||
108:	li	r9,0
 | 
			
		||||
	b	93f
 | 
			
		||||
/* write fault in final word loop */
 | 
			
		||||
109:	li	r9,1
 | 
			
		||||
93:	andi.	r5,r5,3
 | 
			
		||||
	li	r3,2
 | 
			
		||||
	b	99f
 | 
			
		||||
/* read fault in final byte loop */
 | 
			
		||||
110:	li	r9,0
 | 
			
		||||
	b	94f
 | 
			
		||||
/* write fault in final byte loop */
 | 
			
		||||
111:	li	r9,1
 | 
			
		||||
94:	li	r5,0
 | 
			
		||||
	li	r3,0
 | 
			
		||||
/*
 | 
			
		||||
 * At this stage the number of bytes not copied is
 | 
			
		||||
 * r5 + (ctr << r3), and r9 is 0 for read or 1 for write.
 | 
			
		||||
 */
 | 
			
		||||
99:	mfctr	r0
 | 
			
		||||
106:	slw	r3,r0,r3
 | 
			
		||||
	add.	r3,r3,r5
 | 
			
		||||
	beq	120f			/* shouldn't happen */
 | 
			
		||||
	cmpwi	0,r9,0
 | 
			
		||||
	bne	120f
 | 
			
		||||
/* for a read fault, first try to continue the copy one byte at a time */
 | 
			
		||||
	mtctr	r3
 | 
			
		||||
130:	lbz	r0,4(r4)
 | 
			
		||||
131:	stb	r0,4(r6)
 | 
			
		||||
	addi	r4,r4,1
 | 
			
		||||
	addi	r6,r6,1
 | 
			
		||||
	bdnz	130b
 | 
			
		||||
/* then clear out the destination: r3 bytes starting at 4(r6) */
 | 
			
		||||
132:	mfctr	r3
 | 
			
		||||
	srwi.	r0,r3,2
 | 
			
		||||
	li	r9,0
 | 
			
		||||
	mtctr	r0
 | 
			
		||||
	beq	113f
 | 
			
		||||
112:	stwu	r9,4(r6)
 | 
			
		||||
	bdnz	112b
 | 
			
		||||
113:	andi.	r0,r3,3
 | 
			
		||||
	mtctr	r0
 | 
			
		||||
	beq	120f
 | 
			
		||||
114:	stb	r9,4(r6)
 | 
			
		||||
	addi	r6,r6,1
 | 
			
		||||
	bdnz	114b
 | 
			
		||||
120:	blr
 | 
			
		||||
 | 
			
		||||
	.section __ex_table,"a"
 | 
			
		||||
	.align	2
 | 
			
		||||
	.long	30b,108b
 | 
			
		||||
	.long	31b,109b
 | 
			
		||||
	.long	40b,110b
 | 
			
		||||
	.long	41b,111b
 | 
			
		||||
	.long	130b,132b
 | 
			
		||||
	.long	131b,120b
 | 
			
		||||
	.long	112b,120b
 | 
			
		||||
	.long	114b,120b
 | 
			
		||||
	.text
 | 
			
		||||
							
								
								
									
										121
									
								
								arch/powerpc/lib/copypage.S
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								arch/powerpc/lib/copypage.S
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,121 @@
 | 
			
		||||
/*
 | 
			
		||||
 * arch/ppc64/lib/copypage.S
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2002 Paul Mackerras, IBM Corp.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version
 | 
			
		||||
 * 2 of the License, or (at your option) any later version.
 | 
			
		||||
 */
 | 
			
		||||
#include <asm/processor.h>
 | 
			
		||||
#include <asm/ppc_asm.h>
 | 
			
		||||
 | 
			
		||||
_GLOBAL(copy_page)
 | 
			
		||||
	std	r31,-8(1)
 | 
			
		||||
	std	r30,-16(1)
 | 
			
		||||
	std	r29,-24(1)
 | 
			
		||||
	std	r28,-32(1)
 | 
			
		||||
	std	r27,-40(1)
 | 
			
		||||
	std	r26,-48(1)
 | 
			
		||||
	std	r25,-56(1)
 | 
			
		||||
	std	r24,-64(1)
 | 
			
		||||
	std	r23,-72(1)
 | 
			
		||||
	std	r22,-80(1)
 | 
			
		||||
	std	r21,-88(1)
 | 
			
		||||
	std	r20,-96(1)
 | 
			
		||||
	li	r5,4096/32 - 1
 | 
			
		||||
	addi	r3,r3,-8
 | 
			
		||||
	li	r12,5
 | 
			
		||||
0:	addi	r5,r5,-24
 | 
			
		||||
	mtctr	r12
 | 
			
		||||
	ld	r22,640(4)
 | 
			
		||||
	ld	r21,512(4)
 | 
			
		||||
	ld	r20,384(4)
 | 
			
		||||
	ld	r11,256(4)
 | 
			
		||||
	ld	r9,128(4)
 | 
			
		||||
	ld	r7,0(4)
 | 
			
		||||
	ld	r25,648(4)
 | 
			
		||||
	ld	r24,520(4)
 | 
			
		||||
	ld	r23,392(4)
 | 
			
		||||
	ld	r10,264(4)
 | 
			
		||||
	ld	r8,136(4)
 | 
			
		||||
	ldu	r6,8(4)
 | 
			
		||||
	cmpwi	r5,24
 | 
			
		||||
1:	std	r22,648(3)
 | 
			
		||||
	std	r21,520(3)
 | 
			
		||||
	std	r20,392(3)
 | 
			
		||||
	std	r11,264(3)
 | 
			
		||||
	std	r9,136(3)
 | 
			
		||||
	std	r7,8(3)
 | 
			
		||||
	ld	r28,648(4)
 | 
			
		||||
	ld	r27,520(4)
 | 
			
		||||
	ld	r26,392(4)
 | 
			
		||||
	ld	r31,264(4)
 | 
			
		||||
	ld	r30,136(4)
 | 
			
		||||
	ld	r29,8(4)
 | 
			
		||||
	std	r25,656(3)
 | 
			
		||||
	std	r24,528(3)
 | 
			
		||||
	std	r23,400(3)
 | 
			
		||||
	std	r10,272(3)
 | 
			
		||||
	std	r8,144(3)
 | 
			
		||||
	std	r6,16(3)
 | 
			
		||||
	ld	r22,656(4)
 | 
			
		||||
	ld	r21,528(4)
 | 
			
		||||
	ld	r20,400(4)
 | 
			
		||||
	ld	r11,272(4)
 | 
			
		||||
	ld	r9,144(4)
 | 
			
		||||
	ld	r7,16(4)
 | 
			
		||||
	std	r28,664(3)
 | 
			
		||||
	std	r27,536(3)
 | 
			
		||||
	std	r26,408(3)
 | 
			
		||||
	std	r31,280(3)
 | 
			
		||||
	std	r30,152(3)
 | 
			
		||||
	stdu	r29,24(3)
 | 
			
		||||
	ld	r25,664(4)
 | 
			
		||||
	ld	r24,536(4)
 | 
			
		||||
	ld	r23,408(4)
 | 
			
		||||
	ld	r10,280(4)
 | 
			
		||||
	ld	r8,152(4)
 | 
			
		||||
	ldu	r6,24(4)
 | 
			
		||||
	bdnz	1b
 | 
			
		||||
	std	r22,648(3)
 | 
			
		||||
	std	r21,520(3)
 | 
			
		||||
	std	r20,392(3)
 | 
			
		||||
	std	r11,264(3)
 | 
			
		||||
	std	r9,136(3)
 | 
			
		||||
	std	r7,8(3)
 | 
			
		||||
	addi	r4,r4,640
 | 
			
		||||
	addi	r3,r3,648
 | 
			
		||||
	bge	0b
 | 
			
		||||
	mtctr	r5
 | 
			
		||||
	ld	r7,0(4)
 | 
			
		||||
	ld	r8,8(4)
 | 
			
		||||
	ldu	r9,16(4)
 | 
			
		||||
3:	ld	r10,8(4)
 | 
			
		||||
	std	r7,8(3)
 | 
			
		||||
	ld	r7,16(4)
 | 
			
		||||
	std	r8,16(3)
 | 
			
		||||
	ld	r8,24(4)
 | 
			
		||||
	std	r9,24(3)
 | 
			
		||||
	ldu	r9,32(4)
 | 
			
		||||
	stdu	r10,32(3)
 | 
			
		||||
	bdnz	3b
 | 
			
		||||
4:	ld	r10,8(4)
 | 
			
		||||
	std	r7,8(3)
 | 
			
		||||
	std	r8,16(3)
 | 
			
		||||
	std	r9,24(3)
 | 
			
		||||
	std	r10,32(3)
 | 
			
		||||
9:	ld	r20,-96(1)
 | 
			
		||||
	ld	r21,-88(1)
 | 
			
		||||
	ld	r22,-80(1)
 | 
			
		||||
	ld	r23,-72(1)
 | 
			
		||||
	ld	r24,-64(1)
 | 
			
		||||
	ld	r25,-56(1)
 | 
			
		||||
	ld	r26,-48(1)
 | 
			
		||||
	ld	r27,-40(1)
 | 
			
		||||
	ld	r28,-32(1)
 | 
			
		||||
	ld	r29,-24(1)
 | 
			
		||||
	ld	r30,-16(1)
 | 
			
		||||
	ld	r31,-8(1)
 | 
			
		||||
	blr
 | 
			
		||||
							
								
								
									
										576
									
								
								arch/powerpc/lib/copyuser.S
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										576
									
								
								arch/powerpc/lib/copyuser.S
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,576 @@
 | 
			
		||||
/*
 | 
			
		||||
 * arch/ppc64/lib/copyuser.S
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2002 Paul Mackerras, IBM Corp.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version
 | 
			
		||||
 * 2 of the License, or (at your option) any later version.
 | 
			
		||||
 */
 | 
			
		||||
#include <asm/processor.h>
 | 
			
		||||
#include <asm/ppc_asm.h>
 | 
			
		||||
 | 
			
		||||
	.align	7
 | 
			
		||||
_GLOBAL(__copy_tofrom_user)
 | 
			
		||||
	/* first check for a whole page copy on a page boundary */
 | 
			
		||||
	cmpldi	cr1,r5,16
 | 
			
		||||
	cmpdi	cr6,r5,4096
 | 
			
		||||
	or	r0,r3,r4
 | 
			
		||||
	neg	r6,r3		/* LS 3 bits = # bytes to 8-byte dest bdry */
 | 
			
		||||
	andi.	r0,r0,4095
 | 
			
		||||
	std	r3,-24(r1)
 | 
			
		||||
	crand	cr0*4+2,cr0*4+2,cr6*4+2
 | 
			
		||||
	std	r4,-16(r1)
 | 
			
		||||
	std	r5,-8(r1)
 | 
			
		||||
	dcbt	0,r4
 | 
			
		||||
	beq	.Lcopy_page
 | 
			
		||||
	andi.	r6,r6,7
 | 
			
		||||
	mtcrf	0x01,r5
 | 
			
		||||
	blt	cr1,.Lshort_copy
 | 
			
		||||
	bne	.Ldst_unaligned
 | 
			
		||||
.Ldst_aligned:
 | 
			
		||||
	andi.	r0,r4,7
 | 
			
		||||
	addi	r3,r3,-16
 | 
			
		||||
	bne	.Lsrc_unaligned
 | 
			
		||||
	srdi	r7,r5,4
 | 
			
		||||
20:	ld	r9,0(r4)
 | 
			
		||||
	addi	r4,r4,-8
 | 
			
		||||
	mtctr	r7
 | 
			
		||||
	andi.	r5,r5,7
 | 
			
		||||
	bf	cr7*4+0,22f
 | 
			
		||||
	addi	r3,r3,8
 | 
			
		||||
	addi	r4,r4,8
 | 
			
		||||
	mr	r8,r9
 | 
			
		||||
	blt	cr1,72f
 | 
			
		||||
21:	ld	r9,8(r4)
 | 
			
		||||
70:	std	r8,8(r3)
 | 
			
		||||
22:	ldu	r8,16(r4)
 | 
			
		||||
71:	stdu	r9,16(r3)
 | 
			
		||||
	bdnz	21b
 | 
			
		||||
72:	std	r8,8(r3)
 | 
			
		||||
	beq+	3f
 | 
			
		||||
	addi	r3,r3,16
 | 
			
		||||
23:	ld	r9,8(r4)
 | 
			
		||||
.Ldo_tail:
 | 
			
		||||
	bf	cr7*4+1,1f
 | 
			
		||||
	rotldi	r9,r9,32
 | 
			
		||||
73:	stw	r9,0(r3)
 | 
			
		||||
	addi	r3,r3,4
 | 
			
		||||
1:	bf	cr7*4+2,2f
 | 
			
		||||
	rotldi	r9,r9,16
 | 
			
		||||
74:	sth	r9,0(r3)
 | 
			
		||||
	addi	r3,r3,2
 | 
			
		||||
2:	bf	cr7*4+3,3f
 | 
			
		||||
	rotldi	r9,r9,8
 | 
			
		||||
75:	stb	r9,0(r3)
 | 
			
		||||
3:	li	r3,0
 | 
			
		||||
	blr
 | 
			
		||||
 | 
			
		||||
.Lsrc_unaligned:
 | 
			
		||||
	srdi	r6,r5,3
 | 
			
		||||
	addi	r5,r5,-16
 | 
			
		||||
	subf	r4,r0,r4
 | 
			
		||||
	srdi	r7,r5,4
 | 
			
		||||
	sldi	r10,r0,3
 | 
			
		||||
	cmpldi	cr6,r6,3
 | 
			
		||||
	andi.	r5,r5,7
 | 
			
		||||
	mtctr	r7
 | 
			
		||||
	subfic	r11,r10,64
 | 
			
		||||
	add	r5,r5,r0
 | 
			
		||||
	bt	cr7*4+0,28f
 | 
			
		||||
 | 
			
		||||
24:	ld	r9,0(r4)	/* 3+2n loads, 2+2n stores */
 | 
			
		||||
25:	ld	r0,8(r4)
 | 
			
		||||
	sld	r6,r9,r10
 | 
			
		||||
26:	ldu	r9,16(r4)
 | 
			
		||||
	srd	r7,r0,r11
 | 
			
		||||
	sld	r8,r0,r10
 | 
			
		||||
	or	r7,r7,r6
 | 
			
		||||
	blt	cr6,79f
 | 
			
		||||
27:	ld	r0,8(r4)
 | 
			
		||||
	b	2f
 | 
			
		||||
 | 
			
		||||
28:	ld	r0,0(r4)	/* 4+2n loads, 3+2n stores */
 | 
			
		||||
29:	ldu	r9,8(r4)
 | 
			
		||||
	sld	r8,r0,r10
 | 
			
		||||
	addi	r3,r3,-8
 | 
			
		||||
	blt	cr6,5f
 | 
			
		||||
30:	ld	r0,8(r4)
 | 
			
		||||
	srd	r12,r9,r11
 | 
			
		||||
	sld	r6,r9,r10
 | 
			
		||||
31:	ldu	r9,16(r4)
 | 
			
		||||
	or	r12,r8,r12
 | 
			
		||||
	srd	r7,r0,r11
 | 
			
		||||
	sld	r8,r0,r10
 | 
			
		||||
	addi	r3,r3,16
 | 
			
		||||
	beq	cr6,78f
 | 
			
		||||
 | 
			
		||||
1:	or	r7,r7,r6
 | 
			
		||||
32:	ld	r0,8(r4)
 | 
			
		||||
76:	std	r12,8(r3)
 | 
			
		||||
2:	srd	r12,r9,r11
 | 
			
		||||
	sld	r6,r9,r10
 | 
			
		||||
33:	ldu	r9,16(r4)
 | 
			
		||||
	or	r12,r8,r12
 | 
			
		||||
77:	stdu	r7,16(r3)
 | 
			
		||||
	srd	r7,r0,r11
 | 
			
		||||
	sld	r8,r0,r10
 | 
			
		||||
	bdnz	1b
 | 
			
		||||
 | 
			
		||||
78:	std	r12,8(r3)
 | 
			
		||||
	or	r7,r7,r6
 | 
			
		||||
79:	std	r7,16(r3)
 | 
			
		||||
5:	srd	r12,r9,r11
 | 
			
		||||
	or	r12,r8,r12
 | 
			
		||||
80:	std	r12,24(r3)
 | 
			
		||||
	bne	6f
 | 
			
		||||
	li	r3,0
 | 
			
		||||
	blr
 | 
			
		||||
6:	cmpwi	cr1,r5,8
 | 
			
		||||
	addi	r3,r3,32
 | 
			
		||||
	sld	r9,r9,r10
 | 
			
		||||
	ble	cr1,.Ldo_tail
 | 
			
		||||
34:	ld	r0,8(r4)
 | 
			
		||||
	srd	r7,r0,r11
 | 
			
		||||
	or	r9,r7,r9
 | 
			
		||||
	b	.Ldo_tail
 | 
			
		||||
 | 
			
		||||
.Ldst_unaligned:
 | 
			
		||||
	mtcrf	0x01,r6		/* put #bytes to 8B bdry into cr7 */
 | 
			
		||||
	subf	r5,r6,r5
 | 
			
		||||
	li	r7,0
 | 
			
		||||
	cmpldi	r1,r5,16
 | 
			
		||||
	bf	cr7*4+3,1f
 | 
			
		||||
35:	lbz	r0,0(r4)
 | 
			
		||||
81:	stb	r0,0(r3)
 | 
			
		||||
	addi	r7,r7,1
 | 
			
		||||
1:	bf	cr7*4+2,2f
 | 
			
		||||
36:	lhzx	r0,r7,r4
 | 
			
		||||
82:	sthx	r0,r7,r3
 | 
			
		||||
	addi	r7,r7,2
 | 
			
		||||
2:	bf	cr7*4+1,3f
 | 
			
		||||
37:	lwzx	r0,r7,r4
 | 
			
		||||
83:	stwx	r0,r7,r3
 | 
			
		||||
3:	mtcrf	0x01,r5
 | 
			
		||||
	add	r4,r6,r4
 | 
			
		||||
	add	r3,r6,r3
 | 
			
		||||
	b	.Ldst_aligned
 | 
			
		||||
 | 
			
		||||
.Lshort_copy:
 | 
			
		||||
	bf	cr7*4+0,1f
 | 
			
		||||
38:	lwz	r0,0(r4)
 | 
			
		||||
39:	lwz	r9,4(r4)
 | 
			
		||||
	addi	r4,r4,8
 | 
			
		||||
84:	stw	r0,0(r3)
 | 
			
		||||
85:	stw	r9,4(r3)
 | 
			
		||||
	addi	r3,r3,8
 | 
			
		||||
1:	bf	cr7*4+1,2f
 | 
			
		||||
40:	lwz	r0,0(r4)
 | 
			
		||||
	addi	r4,r4,4
 | 
			
		||||
86:	stw	r0,0(r3)
 | 
			
		||||
	addi	r3,r3,4
 | 
			
		||||
2:	bf	cr7*4+2,3f
 | 
			
		||||
41:	lhz	r0,0(r4)
 | 
			
		||||
	addi	r4,r4,2
 | 
			
		||||
87:	sth	r0,0(r3)
 | 
			
		||||
	addi	r3,r3,2
 | 
			
		||||
3:	bf	cr7*4+3,4f
 | 
			
		||||
42:	lbz	r0,0(r4)
 | 
			
		||||
88:	stb	r0,0(r3)
 | 
			
		||||
4:	li	r3,0
 | 
			
		||||
	blr
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * exception handlers follow
 | 
			
		||||
 * we have to return the number of bytes not copied
 | 
			
		||||
 * for an exception on a load, we set the rest of the destination to 0
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
136:
 | 
			
		||||
137:
 | 
			
		||||
	add	r3,r3,r7
 | 
			
		||||
	b	1f
 | 
			
		||||
130:
 | 
			
		||||
131:
 | 
			
		||||
	addi	r3,r3,8
 | 
			
		||||
120:
 | 
			
		||||
122:
 | 
			
		||||
124:
 | 
			
		||||
125:
 | 
			
		||||
126:
 | 
			
		||||
127:
 | 
			
		||||
128:
 | 
			
		||||
129:
 | 
			
		||||
133:
 | 
			
		||||
	addi	r3,r3,8
 | 
			
		||||
121:
 | 
			
		||||
132:
 | 
			
		||||
	addi	r3,r3,8
 | 
			
		||||
123:
 | 
			
		||||
134:
 | 
			
		||||
135:
 | 
			
		||||
138:
 | 
			
		||||
139:
 | 
			
		||||
140:
 | 
			
		||||
141:
 | 
			
		||||
142:
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * here we have had a fault on a load and r3 points to the first
 | 
			
		||||
 * unmodified byte of the destination
 | 
			
		||||
 */
 | 
			
		||||
1:	ld	r6,-24(r1)
 | 
			
		||||
	ld	r4,-16(r1)
 | 
			
		||||
	ld	r5,-8(r1)
 | 
			
		||||
	subf	r6,r6,r3
 | 
			
		||||
	add	r4,r4,r6
 | 
			
		||||
	subf	r5,r6,r5	/* #bytes left to go */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * first see if we can copy any more bytes before hitting another exception
 | 
			
		||||
 */
 | 
			
		||||
	mtctr	r5
 | 
			
		||||
43:	lbz	r0,0(r4)
 | 
			
		||||
	addi	r4,r4,1
 | 
			
		||||
89:	stb	r0,0(r3)
 | 
			
		||||
	addi	r3,r3,1
 | 
			
		||||
	bdnz	43b
 | 
			
		||||
	li	r3,0		/* huh? all copied successfully this time? */
 | 
			
		||||
	blr
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * here we have trapped again, need to clear ctr bytes starting at r3
 | 
			
		||||
 */
 | 
			
		||||
143:	mfctr	r5
 | 
			
		||||
	li	r0,0
 | 
			
		||||
	mr	r4,r3
 | 
			
		||||
	mr	r3,r5		/* return the number of bytes not copied */
 | 
			
		||||
1:	andi.	r9,r4,7
 | 
			
		||||
	beq	3f
 | 
			
		||||
90:	stb	r0,0(r4)
 | 
			
		||||
	addic.	r5,r5,-1
 | 
			
		||||
	addi	r4,r4,1
 | 
			
		||||
	bne	1b
 | 
			
		||||
	blr
 | 
			
		||||
3:	cmpldi	cr1,r5,8
 | 
			
		||||
	srdi	r9,r5,3
 | 
			
		||||
	andi.	r5,r5,7
 | 
			
		||||
	blt	cr1,93f
 | 
			
		||||
	mtctr	r9
 | 
			
		||||
91:	std	r0,0(r4)
 | 
			
		||||
	addi	r4,r4,8
 | 
			
		||||
	bdnz	91b
 | 
			
		||||
93:	beqlr
 | 
			
		||||
	mtctr	r5	
 | 
			
		||||
92:	stb	r0,0(r4)
 | 
			
		||||
	addi	r4,r4,1
 | 
			
		||||
	bdnz	92b
 | 
			
		||||
	blr
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * exception handlers for stores: we just need to work
 | 
			
		||||
 * out how many bytes weren't copied
 | 
			
		||||
 */
 | 
			
		||||
182:
 | 
			
		||||
183:
 | 
			
		||||
	add	r3,r3,r7
 | 
			
		||||
	b	1f
 | 
			
		||||
180:
 | 
			
		||||
	addi	r3,r3,8
 | 
			
		||||
171:
 | 
			
		||||
177:
 | 
			
		||||
	addi	r3,r3,8
 | 
			
		||||
170:
 | 
			
		||||
172:
 | 
			
		||||
176:
 | 
			
		||||
178:
 | 
			
		||||
	addi	r3,r3,4
 | 
			
		||||
185:
 | 
			
		||||
	addi	r3,r3,4
 | 
			
		||||
173:
 | 
			
		||||
174:
 | 
			
		||||
175:
 | 
			
		||||
179:
 | 
			
		||||
181:
 | 
			
		||||
184:
 | 
			
		||||
186:
 | 
			
		||||
187:
 | 
			
		||||
188:
 | 
			
		||||
189:	
 | 
			
		||||
1:
 | 
			
		||||
	ld	r6,-24(r1)
 | 
			
		||||
	ld	r5,-8(r1)
 | 
			
		||||
	add	r6,r6,r5
 | 
			
		||||
	subf	r3,r3,r6	/* #bytes not copied */
 | 
			
		||||
190:
 | 
			
		||||
191:
 | 
			
		||||
192:
 | 
			
		||||
	blr			/* #bytes not copied in r3 */
 | 
			
		||||
 | 
			
		||||
	.section __ex_table,"a"
 | 
			
		||||
	.align	3
 | 
			
		||||
	.llong	20b,120b
 | 
			
		||||
	.llong	21b,121b
 | 
			
		||||
	.llong	70b,170b
 | 
			
		||||
	.llong	22b,122b
 | 
			
		||||
	.llong	71b,171b
 | 
			
		||||
	.llong	72b,172b
 | 
			
		||||
	.llong	23b,123b
 | 
			
		||||
	.llong	73b,173b
 | 
			
		||||
	.llong	74b,174b
 | 
			
		||||
	.llong	75b,175b
 | 
			
		||||
	.llong	24b,124b
 | 
			
		||||
	.llong	25b,125b
 | 
			
		||||
	.llong	26b,126b
 | 
			
		||||
	.llong	27b,127b
 | 
			
		||||
	.llong	28b,128b
 | 
			
		||||
	.llong	29b,129b
 | 
			
		||||
	.llong	30b,130b
 | 
			
		||||
	.llong	31b,131b
 | 
			
		||||
	.llong	32b,132b
 | 
			
		||||
	.llong	76b,176b
 | 
			
		||||
	.llong	33b,133b
 | 
			
		||||
	.llong	77b,177b
 | 
			
		||||
	.llong	78b,178b
 | 
			
		||||
	.llong	79b,179b
 | 
			
		||||
	.llong	80b,180b
 | 
			
		||||
	.llong	34b,134b
 | 
			
		||||
	.llong	35b,135b
 | 
			
		||||
	.llong	81b,181b
 | 
			
		||||
	.llong	36b,136b
 | 
			
		||||
	.llong	82b,182b
 | 
			
		||||
	.llong	37b,137b
 | 
			
		||||
	.llong	83b,183b
 | 
			
		||||
	.llong	38b,138b
 | 
			
		||||
	.llong	39b,139b
 | 
			
		||||
	.llong	84b,184b
 | 
			
		||||
	.llong	85b,185b
 | 
			
		||||
	.llong	40b,140b
 | 
			
		||||
	.llong	86b,186b
 | 
			
		||||
	.llong	41b,141b
 | 
			
		||||
	.llong	87b,187b
 | 
			
		||||
	.llong	42b,142b
 | 
			
		||||
	.llong	88b,188b
 | 
			
		||||
	.llong	43b,143b
 | 
			
		||||
	.llong	89b,189b
 | 
			
		||||
	.llong	90b,190b
 | 
			
		||||
	.llong	91b,191b
 | 
			
		||||
	.llong	92b,192b
 | 
			
		||||
	
 | 
			
		||||
	.text
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Routine to copy a whole page of data, optimized for POWER4.
 | 
			
		||||
 * On POWER4 it is more than 50% faster than the simple loop
 | 
			
		||||
 * above (following the .Ldst_aligned label) but it runs slightly
 | 
			
		||||
 * slower on POWER3.
 | 
			
		||||
 */
 | 
			
		||||
.Lcopy_page:
 | 
			
		||||
	std	r31,-32(1)
 | 
			
		||||
	std	r30,-40(1)
 | 
			
		||||
	std	r29,-48(1)
 | 
			
		||||
	std	r28,-56(1)
 | 
			
		||||
	std	r27,-64(1)
 | 
			
		||||
	std	r26,-72(1)
 | 
			
		||||
	std	r25,-80(1)
 | 
			
		||||
	std	r24,-88(1)
 | 
			
		||||
	std	r23,-96(1)
 | 
			
		||||
	std	r22,-104(1)
 | 
			
		||||
	std	r21,-112(1)
 | 
			
		||||
	std	r20,-120(1)
 | 
			
		||||
	li	r5,4096/32 - 1
 | 
			
		||||
	addi	r3,r3,-8
 | 
			
		||||
	li	r0,5
 | 
			
		||||
0:	addi	r5,r5,-24
 | 
			
		||||
	mtctr	r0
 | 
			
		||||
20:	ld	r22,640(4)
 | 
			
		||||
21:	ld	r21,512(4)
 | 
			
		||||
22:	ld	r20,384(4)
 | 
			
		||||
23:	ld	r11,256(4)
 | 
			
		||||
24:	ld	r9,128(4)
 | 
			
		||||
25:	ld	r7,0(4)
 | 
			
		||||
26:	ld	r25,648(4)
 | 
			
		||||
27:	ld	r24,520(4)
 | 
			
		||||
28:	ld	r23,392(4)
 | 
			
		||||
29:	ld	r10,264(4)
 | 
			
		||||
30:	ld	r8,136(4)
 | 
			
		||||
31:	ldu	r6,8(4)
 | 
			
		||||
	cmpwi	r5,24
 | 
			
		||||
1:
 | 
			
		||||
32:	std	r22,648(3)
 | 
			
		||||
33:	std	r21,520(3)
 | 
			
		||||
34:	std	r20,392(3)
 | 
			
		||||
35:	std	r11,264(3)
 | 
			
		||||
36:	std	r9,136(3)
 | 
			
		||||
37:	std	r7,8(3)
 | 
			
		||||
38:	ld	r28,648(4)
 | 
			
		||||
39:	ld	r27,520(4)
 | 
			
		||||
40:	ld	r26,392(4)
 | 
			
		||||
41:	ld	r31,264(4)
 | 
			
		||||
42:	ld	r30,136(4)
 | 
			
		||||
43:	ld	r29,8(4)
 | 
			
		||||
44:	std	r25,656(3)
 | 
			
		||||
45:	std	r24,528(3)
 | 
			
		||||
46:	std	r23,400(3)
 | 
			
		||||
47:	std	r10,272(3)
 | 
			
		||||
48:	std	r8,144(3)
 | 
			
		||||
49:	std	r6,16(3)
 | 
			
		||||
50:	ld	r22,656(4)
 | 
			
		||||
51:	ld	r21,528(4)
 | 
			
		||||
52:	ld	r20,400(4)
 | 
			
		||||
53:	ld	r11,272(4)
 | 
			
		||||
54:	ld	r9,144(4)
 | 
			
		||||
55:	ld	r7,16(4)
 | 
			
		||||
56:	std	r28,664(3)
 | 
			
		||||
57:	std	r27,536(3)
 | 
			
		||||
58:	std	r26,408(3)
 | 
			
		||||
59:	std	r31,280(3)
 | 
			
		||||
60:	std	r30,152(3)
 | 
			
		||||
61:	stdu	r29,24(3)
 | 
			
		||||
62:	ld	r25,664(4)
 | 
			
		||||
63:	ld	r24,536(4)
 | 
			
		||||
64:	ld	r23,408(4)
 | 
			
		||||
65:	ld	r10,280(4)
 | 
			
		||||
66:	ld	r8,152(4)
 | 
			
		||||
67:	ldu	r6,24(4)
 | 
			
		||||
	bdnz	1b
 | 
			
		||||
68:	std	r22,648(3)
 | 
			
		||||
69:	std	r21,520(3)
 | 
			
		||||
70:	std	r20,392(3)
 | 
			
		||||
71:	std	r11,264(3)
 | 
			
		||||
72:	std	r9,136(3)
 | 
			
		||||
73:	std	r7,8(3)
 | 
			
		||||
74:	addi	r4,r4,640
 | 
			
		||||
75:	addi	r3,r3,648
 | 
			
		||||
	bge	0b
 | 
			
		||||
	mtctr	r5
 | 
			
		||||
76:	ld	r7,0(4)
 | 
			
		||||
77:	ld	r8,8(4)
 | 
			
		||||
78:	ldu	r9,16(4)
 | 
			
		||||
3:
 | 
			
		||||
79:	ld	r10,8(4)
 | 
			
		||||
80:	std	r7,8(3)
 | 
			
		||||
81:	ld	r7,16(4)
 | 
			
		||||
82:	std	r8,16(3)
 | 
			
		||||
83:	ld	r8,24(4)
 | 
			
		||||
84:	std	r9,24(3)
 | 
			
		||||
85:	ldu	r9,32(4)
 | 
			
		||||
86:	stdu	r10,32(3)
 | 
			
		||||
	bdnz	3b
 | 
			
		||||
4:
 | 
			
		||||
87:	ld	r10,8(4)
 | 
			
		||||
88:	std	r7,8(3)
 | 
			
		||||
89:	std	r8,16(3)
 | 
			
		||||
90:	std	r9,24(3)
 | 
			
		||||
91:	std	r10,32(3)
 | 
			
		||||
9:	ld	r20,-120(1)
 | 
			
		||||
	ld	r21,-112(1)
 | 
			
		||||
	ld	r22,-104(1)
 | 
			
		||||
	ld	r23,-96(1)
 | 
			
		||||
	ld	r24,-88(1)
 | 
			
		||||
	ld	r25,-80(1)
 | 
			
		||||
	ld	r26,-72(1)
 | 
			
		||||
	ld	r27,-64(1)
 | 
			
		||||
	ld	r28,-56(1)
 | 
			
		||||
	ld	r29,-48(1)
 | 
			
		||||
	ld	r30,-40(1)
 | 
			
		||||
	ld	r31,-32(1)
 | 
			
		||||
	li	r3,0
 | 
			
		||||
	blr
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * on an exception, reset to the beginning and jump back into the
 | 
			
		||||
 * standard __copy_tofrom_user
 | 
			
		||||
 */
 | 
			
		||||
100:	ld	r20,-120(1)
 | 
			
		||||
	ld	r21,-112(1)
 | 
			
		||||
	ld	r22,-104(1)
 | 
			
		||||
	ld	r23,-96(1)
 | 
			
		||||
	ld	r24,-88(1)
 | 
			
		||||
	ld	r25,-80(1)
 | 
			
		||||
	ld	r26,-72(1)
 | 
			
		||||
	ld	r27,-64(1)
 | 
			
		||||
	ld	r28,-56(1)
 | 
			
		||||
	ld	r29,-48(1)
 | 
			
		||||
	ld	r30,-40(1)
 | 
			
		||||
	ld	r31,-32(1)
 | 
			
		||||
	ld	r3,-24(r1)
 | 
			
		||||
	ld	r4,-16(r1)
 | 
			
		||||
	li	r5,4096
 | 
			
		||||
	b	.Ldst_aligned
 | 
			
		||||
 | 
			
		||||
	.section __ex_table,"a"
 | 
			
		||||
	.align	3
 | 
			
		||||
	.llong	20b,100b
 | 
			
		||||
	.llong	21b,100b
 | 
			
		||||
	.llong	22b,100b
 | 
			
		||||
	.llong	23b,100b
 | 
			
		||||
	.llong	24b,100b
 | 
			
		||||
	.llong	25b,100b
 | 
			
		||||
	.llong	26b,100b
 | 
			
		||||
	.llong	27b,100b
 | 
			
		||||
	.llong	28b,100b
 | 
			
		||||
	.llong	29b,100b
 | 
			
		||||
	.llong	30b,100b
 | 
			
		||||
	.llong	31b,100b
 | 
			
		||||
	.llong	32b,100b
 | 
			
		||||
	.llong	33b,100b
 | 
			
		||||
	.llong	34b,100b
 | 
			
		||||
	.llong	35b,100b
 | 
			
		||||
	.llong	36b,100b
 | 
			
		||||
	.llong	37b,100b
 | 
			
		||||
	.llong	38b,100b
 | 
			
		||||
	.llong	39b,100b
 | 
			
		||||
	.llong	40b,100b
 | 
			
		||||
	.llong	41b,100b
 | 
			
		||||
	.llong	42b,100b
 | 
			
		||||
	.llong	43b,100b
 | 
			
		||||
	.llong	44b,100b
 | 
			
		||||
	.llong	45b,100b
 | 
			
		||||
	.llong	46b,100b
 | 
			
		||||
	.llong	47b,100b
 | 
			
		||||
	.llong	48b,100b
 | 
			
		||||
	.llong	49b,100b
 | 
			
		||||
	.llong	50b,100b
 | 
			
		||||
	.llong	51b,100b
 | 
			
		||||
	.llong	52b,100b
 | 
			
		||||
	.llong	53b,100b
 | 
			
		||||
	.llong	54b,100b
 | 
			
		||||
	.llong	55b,100b
 | 
			
		||||
	.llong	56b,100b
 | 
			
		||||
	.llong	57b,100b
 | 
			
		||||
	.llong	58b,100b
 | 
			
		||||
	.llong	59b,100b
 | 
			
		||||
	.llong	60b,100b
 | 
			
		||||
	.llong	61b,100b
 | 
			
		||||
	.llong	62b,100b
 | 
			
		||||
	.llong	63b,100b
 | 
			
		||||
	.llong	64b,100b
 | 
			
		||||
	.llong	65b,100b
 | 
			
		||||
	.llong	66b,100b
 | 
			
		||||
	.llong	67b,100b
 | 
			
		||||
	.llong	68b,100b
 | 
			
		||||
	.llong	69b,100b
 | 
			
		||||
	.llong	70b,100b
 | 
			
		||||
	.llong	71b,100b
 | 
			
		||||
	.llong	72b,100b
 | 
			
		||||
	.llong	73b,100b
 | 
			
		||||
	.llong	74b,100b
 | 
			
		||||
	.llong	75b,100b
 | 
			
		||||
	.llong	76b,100b
 | 
			
		||||
	.llong	77b,100b
 | 
			
		||||
	.llong	78b,100b
 | 
			
		||||
	.llong	79b,100b
 | 
			
		||||
	.llong	80b,100b
 | 
			
		||||
	.llong	81b,100b
 | 
			
		||||
	.llong	82b,100b
 | 
			
		||||
	.llong	83b,100b
 | 
			
		||||
	.llong	84b,100b
 | 
			
		||||
	.llong	85b,100b
 | 
			
		||||
	.llong	86b,100b
 | 
			
		||||
	.llong	87b,100b
 | 
			
		||||
	.llong	88b,100b
 | 
			
		||||
	.llong	89b,100b
 | 
			
		||||
	.llong	90b,100b
 | 
			
		||||
	.llong	91b,100b
 | 
			
		||||
							
								
								
									
										58
									
								
								arch/powerpc/lib/div64.S
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								arch/powerpc/lib/div64.S
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,58 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Divide a 64-bit unsigned number by a 32-bit unsigned number.
 | 
			
		||||
 * This routine assumes that the top 32 bits of the dividend are
 | 
			
		||||
 * non-zero to start with.
 | 
			
		||||
 * On entry, r3 points to the dividend, which get overwritten with
 | 
			
		||||
 * the 64-bit quotient, and r4 contains the divisor.
 | 
			
		||||
 * On exit, r3 contains the remainder.
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2002 Paul Mackerras, IBM Corp.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version
 | 
			
		||||
 * 2 of the License, or (at your option) any later version.
 | 
			
		||||
 */
 | 
			
		||||
#include <asm/ppc_asm.h>
 | 
			
		||||
#include <asm/processor.h>
 | 
			
		||||
 | 
			
		||||
_GLOBAL(__div64_32)
 | 
			
		||||
	lwz	r5,0(r3)	# get the dividend into r5/r6
 | 
			
		||||
	lwz	r6,4(r3)
 | 
			
		||||
	cmplw	r5,r4
 | 
			
		||||
	li	r7,0
 | 
			
		||||
	li	r8,0
 | 
			
		||||
	blt	1f
 | 
			
		||||
	divwu	r7,r5,r4	# if dividend.hi >= divisor,
 | 
			
		||||
	mullw	r0,r7,r4	# quotient.hi = dividend.hi / divisor
 | 
			
		||||
	subf.	r5,r0,r5	# dividend.hi %= divisor
 | 
			
		||||
	beq	3f
 | 
			
		||||
1:	mr	r11,r5		# here dividend.hi != 0
 | 
			
		||||
	andis.	r0,r5,0xc000
 | 
			
		||||
	bne	2f
 | 
			
		||||
	cntlzw	r0,r5		# we are shifting the dividend right
 | 
			
		||||
	li	r10,-1		# to make it < 2^32, and shifting
 | 
			
		||||
	srw	r10,r10,r0	# the divisor right the same amount,
 | 
			
		||||
	add	r9,r4,r10	# rounding up (so the estimate cannot
 | 
			
		||||
	andc	r11,r6,r10	# ever be too large, only too small)
 | 
			
		||||
	andc	r9,r9,r10
 | 
			
		||||
	or	r11,r5,r11
 | 
			
		||||
	rotlw	r9,r9,r0
 | 
			
		||||
	rotlw	r11,r11,r0
 | 
			
		||||
	divwu	r11,r11,r9	# then we divide the shifted quantities
 | 
			
		||||
2:	mullw	r10,r11,r4	# to get an estimate of the quotient,
 | 
			
		||||
	mulhwu	r9,r11,r4	# multiply the estimate by the divisor,
 | 
			
		||||
	subfc	r6,r10,r6	# take the product from the divisor,
 | 
			
		||||
	add	r8,r8,r11	# and add the estimate to the accumulated
 | 
			
		||||
	subfe.	r5,r9,r5	# quotient
 | 
			
		||||
	bne	1b
 | 
			
		||||
3:	cmplw	r6,r4
 | 
			
		||||
	blt	4f
 | 
			
		||||
	divwu	r0,r6,r4	# perform the remaining 32-bit division
 | 
			
		||||
	mullw	r10,r0,r4	# and get the remainder
 | 
			
		||||
	add	r8,r8,r0
 | 
			
		||||
	subf	r6,r10,r6
 | 
			
		||||
4:	stw	r7,0(r3)	# return the quotient in *r3
 | 
			
		||||
	stw	r8,4(r3)
 | 
			
		||||
	mr	r3,r6		# return the remainder in r3
 | 
			
		||||
	blr
 | 
			
		||||
							
								
								
									
										108
									
								
								arch/powerpc/lib/e2a.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								arch/powerpc/lib/e2a.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,108 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  arch/ppc64/lib/e2a.c
 | 
			
		||||
 *
 | 
			
		||||
 *  EBCDIC to ASCII conversion
 | 
			
		||||
 *
 | 
			
		||||
 * This function moved here from arch/ppc64/kernel/viopath.c
 | 
			
		||||
 *
 | 
			
		||||
 * (C) Copyright 2000-2004 IBM 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) anyu 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/module.h>
 | 
			
		||||
 | 
			
		||||
unsigned char e2a(unsigned char x)
 | 
			
		||||
{
 | 
			
		||||
	switch (x) {
 | 
			
		||||
	case 0xF0:
 | 
			
		||||
		return '0';
 | 
			
		||||
	case 0xF1:
 | 
			
		||||
		return '1';
 | 
			
		||||
	case 0xF2:
 | 
			
		||||
		return '2';
 | 
			
		||||
	case 0xF3:
 | 
			
		||||
		return '3';
 | 
			
		||||
	case 0xF4:
 | 
			
		||||
		return '4';
 | 
			
		||||
	case 0xF5:
 | 
			
		||||
		return '5';
 | 
			
		||||
	case 0xF6:
 | 
			
		||||
		return '6';
 | 
			
		||||
	case 0xF7:
 | 
			
		||||
		return '7';
 | 
			
		||||
	case 0xF8:
 | 
			
		||||
		return '8';
 | 
			
		||||
	case 0xF9:
 | 
			
		||||
		return '9';
 | 
			
		||||
	case 0xC1:
 | 
			
		||||
		return 'A';
 | 
			
		||||
	case 0xC2:
 | 
			
		||||
		return 'B';
 | 
			
		||||
	case 0xC3:
 | 
			
		||||
		return 'C';
 | 
			
		||||
	case 0xC4:
 | 
			
		||||
		return 'D';
 | 
			
		||||
	case 0xC5:
 | 
			
		||||
		return 'E';
 | 
			
		||||
	case 0xC6:
 | 
			
		||||
		return 'F';
 | 
			
		||||
	case 0xC7:
 | 
			
		||||
		return 'G';
 | 
			
		||||
	case 0xC8:
 | 
			
		||||
		return 'H';
 | 
			
		||||
	case 0xC9:
 | 
			
		||||
		return 'I';
 | 
			
		||||
	case 0xD1:
 | 
			
		||||
		return 'J';
 | 
			
		||||
	case 0xD2:
 | 
			
		||||
		return 'K';
 | 
			
		||||
	case 0xD3:
 | 
			
		||||
		return 'L';
 | 
			
		||||
	case 0xD4:
 | 
			
		||||
		return 'M';
 | 
			
		||||
	case 0xD5:
 | 
			
		||||
		return 'N';
 | 
			
		||||
	case 0xD6:
 | 
			
		||||
		return 'O';
 | 
			
		||||
	case 0xD7:
 | 
			
		||||
		return 'P';
 | 
			
		||||
	case 0xD8:
 | 
			
		||||
		return 'Q';
 | 
			
		||||
	case 0xD9:
 | 
			
		||||
		return 'R';
 | 
			
		||||
	case 0xE2:
 | 
			
		||||
		return 'S';
 | 
			
		||||
	case 0xE3:
 | 
			
		||||
		return 'T';
 | 
			
		||||
	case 0xE4:
 | 
			
		||||
		return 'U';
 | 
			
		||||
	case 0xE5:
 | 
			
		||||
		return 'V';
 | 
			
		||||
	case 0xE6:
 | 
			
		||||
		return 'W';
 | 
			
		||||
	case 0xE7:
 | 
			
		||||
		return 'X';
 | 
			
		||||
	case 0xE8:
 | 
			
		||||
		return 'Y';
 | 
			
		||||
	case 0xE9:
 | 
			
		||||
		return 'Z';
 | 
			
		||||
	}
 | 
			
		||||
	return ' ';
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(e2a);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										172
									
								
								arch/powerpc/lib/memcpy.S
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										172
									
								
								arch/powerpc/lib/memcpy.S
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,172 @@
 | 
			
		||||
/*
 | 
			
		||||
 * arch/ppc64/lib/memcpy.S
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2002 Paul Mackerras, IBM Corp.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version
 | 
			
		||||
 * 2 of the License, or (at your option) any later version.
 | 
			
		||||
 */
 | 
			
		||||
#include <asm/processor.h>
 | 
			
		||||
#include <asm/ppc_asm.h>
 | 
			
		||||
 | 
			
		||||
	.align	7
 | 
			
		||||
_GLOBAL(memcpy)
 | 
			
		||||
	mtcrf	0x01,r5
 | 
			
		||||
	cmpldi	cr1,r5,16
 | 
			
		||||
	neg	r6,r3		# LS 3 bits = # bytes to 8-byte dest bdry
 | 
			
		||||
	andi.	r6,r6,7
 | 
			
		||||
	dcbt	0,r4
 | 
			
		||||
	blt	cr1,.Lshort_copy
 | 
			
		||||
	bne	.Ldst_unaligned
 | 
			
		||||
.Ldst_aligned:
 | 
			
		||||
	andi.	r0,r4,7
 | 
			
		||||
	addi	r3,r3,-16
 | 
			
		||||
	bne	.Lsrc_unaligned
 | 
			
		||||
	srdi	r7,r5,4
 | 
			
		||||
	ld	r9,0(r4)
 | 
			
		||||
	addi	r4,r4,-8
 | 
			
		||||
	mtctr	r7
 | 
			
		||||
	andi.	r5,r5,7
 | 
			
		||||
	bf	cr7*4+0,2f
 | 
			
		||||
	addi	r3,r3,8
 | 
			
		||||
	addi	r4,r4,8
 | 
			
		||||
	mr	r8,r9
 | 
			
		||||
	blt	cr1,3f
 | 
			
		||||
1:	ld	r9,8(r4)
 | 
			
		||||
	std	r8,8(r3)
 | 
			
		||||
2:	ldu	r8,16(r4)
 | 
			
		||||
	stdu	r9,16(r3)
 | 
			
		||||
	bdnz	1b
 | 
			
		||||
3:	std	r8,8(r3)
 | 
			
		||||
	beqlr
 | 
			
		||||
	addi	r3,r3,16
 | 
			
		||||
	ld	r9,8(r4)
 | 
			
		||||
.Ldo_tail:
 | 
			
		||||
	bf	cr7*4+1,1f
 | 
			
		||||
	rotldi	r9,r9,32
 | 
			
		||||
	stw	r9,0(r3)
 | 
			
		||||
	addi	r3,r3,4
 | 
			
		||||
1:	bf	cr7*4+2,2f
 | 
			
		||||
	rotldi	r9,r9,16
 | 
			
		||||
	sth	r9,0(r3)
 | 
			
		||||
	addi	r3,r3,2
 | 
			
		||||
2:	bf	cr7*4+3,3f
 | 
			
		||||
	rotldi	r9,r9,8
 | 
			
		||||
	stb	r9,0(r3)
 | 
			
		||||
3:	blr
 | 
			
		||||
 | 
			
		||||
.Lsrc_unaligned:
 | 
			
		||||
	srdi	r6,r5,3
 | 
			
		||||
	addi	r5,r5,-16
 | 
			
		||||
	subf	r4,r0,r4
 | 
			
		||||
	srdi	r7,r5,4
 | 
			
		||||
	sldi	r10,r0,3
 | 
			
		||||
	cmpdi	cr6,r6,3
 | 
			
		||||
	andi.	r5,r5,7
 | 
			
		||||
	mtctr	r7
 | 
			
		||||
	subfic	r11,r10,64
 | 
			
		||||
	add	r5,r5,r0
 | 
			
		||||
 | 
			
		||||
	bt	cr7*4+0,0f
 | 
			
		||||
 | 
			
		||||
	ld	r9,0(r4)	# 3+2n loads, 2+2n stores
 | 
			
		||||
	ld	r0,8(r4)
 | 
			
		||||
	sld	r6,r9,r10
 | 
			
		||||
	ldu	r9,16(r4)
 | 
			
		||||
	srd	r7,r0,r11
 | 
			
		||||
	sld	r8,r0,r10
 | 
			
		||||
	or	r7,r7,r6
 | 
			
		||||
	blt	cr6,4f
 | 
			
		||||
	ld	r0,8(r4)
 | 
			
		||||
	# s1<< in r8, d0=(s0<<|s1>>) in r7, s3 in r0, s2 in r9, nix in r6 & r12
 | 
			
		||||
	b	2f
 | 
			
		||||
 | 
			
		||||
0:	ld	r0,0(r4)	# 4+2n loads, 3+2n stores
 | 
			
		||||
	ldu	r9,8(r4)
 | 
			
		||||
	sld	r8,r0,r10
 | 
			
		||||
	addi	r3,r3,-8
 | 
			
		||||
	blt	cr6,5f
 | 
			
		||||
	ld	r0,8(r4)
 | 
			
		||||
	srd	r12,r9,r11
 | 
			
		||||
	sld	r6,r9,r10
 | 
			
		||||
	ldu	r9,16(r4)
 | 
			
		||||
	or	r12,r8,r12
 | 
			
		||||
	srd	r7,r0,r11
 | 
			
		||||
	sld	r8,r0,r10
 | 
			
		||||
	addi	r3,r3,16
 | 
			
		||||
	beq	cr6,3f
 | 
			
		||||
 | 
			
		||||
	# d0=(s0<<|s1>>) in r12, s1<< in r6, s2>> in r7, s2<< in r8, s3 in r9
 | 
			
		||||
1:	or	r7,r7,r6
 | 
			
		||||
	ld	r0,8(r4)
 | 
			
		||||
	std	r12,8(r3)
 | 
			
		||||
2:	srd	r12,r9,r11
 | 
			
		||||
	sld	r6,r9,r10
 | 
			
		||||
	ldu	r9,16(r4)
 | 
			
		||||
	or	r12,r8,r12
 | 
			
		||||
	stdu	r7,16(r3)
 | 
			
		||||
	srd	r7,r0,r11
 | 
			
		||||
	sld	r8,r0,r10
 | 
			
		||||
	bdnz	1b
 | 
			
		||||
 | 
			
		||||
3:	std	r12,8(r3)
 | 
			
		||||
	or	r7,r7,r6
 | 
			
		||||
4:	std	r7,16(r3)
 | 
			
		||||
5:	srd	r12,r9,r11
 | 
			
		||||
	or	r12,r8,r12
 | 
			
		||||
	std	r12,24(r3)
 | 
			
		||||
	beqlr
 | 
			
		||||
	cmpwi	cr1,r5,8
 | 
			
		||||
	addi	r3,r3,32
 | 
			
		||||
	sld	r9,r9,r10
 | 
			
		||||
	ble	cr1,.Ldo_tail
 | 
			
		||||
	ld	r0,8(r4)
 | 
			
		||||
	srd	r7,r0,r11
 | 
			
		||||
	or	r9,r7,r9
 | 
			
		||||
	b	.Ldo_tail
 | 
			
		||||
 | 
			
		||||
.Ldst_unaligned:
 | 
			
		||||
	mtcrf	0x01,r6		# put #bytes to 8B bdry into cr7
 | 
			
		||||
	subf	r5,r6,r5
 | 
			
		||||
	li	r7,0
 | 
			
		||||
	cmpldi	r1,r5,16
 | 
			
		||||
	bf	cr7*4+3,1f
 | 
			
		||||
	lbz	r0,0(r4)
 | 
			
		||||
	stb	r0,0(r3)
 | 
			
		||||
	addi	r7,r7,1
 | 
			
		||||
1:	bf	cr7*4+2,2f
 | 
			
		||||
	lhzx	r0,r7,r4
 | 
			
		||||
	sthx	r0,r7,r3
 | 
			
		||||
	addi	r7,r7,2
 | 
			
		||||
2:	bf	cr7*4+1,3f
 | 
			
		||||
	lwzx	r0,r7,r4
 | 
			
		||||
	stwx	r0,r7,r3
 | 
			
		||||
3:	mtcrf	0x01,r5
 | 
			
		||||
	add	r4,r6,r4
 | 
			
		||||
	add	r3,r6,r3
 | 
			
		||||
	b	.Ldst_aligned
 | 
			
		||||
 | 
			
		||||
.Lshort_copy:
 | 
			
		||||
	bf	cr7*4+0,1f
 | 
			
		||||
	lwz	r0,0(r4)
 | 
			
		||||
	lwz	r9,4(r4)
 | 
			
		||||
	addi	r4,r4,8
 | 
			
		||||
	stw	r0,0(r3)
 | 
			
		||||
	stw	r9,4(r3)
 | 
			
		||||
	addi	r3,r3,8
 | 
			
		||||
1:	bf	cr7*4+1,2f
 | 
			
		||||
	lwz	r0,0(r4)
 | 
			
		||||
	addi	r4,r4,4
 | 
			
		||||
	stw	r0,0(r3)
 | 
			
		||||
	addi	r3,r3,4
 | 
			
		||||
2:	bf	cr7*4+2,3f
 | 
			
		||||
	lhz	r0,0(r4)
 | 
			
		||||
	addi	r4,r4,2
 | 
			
		||||
	sth	r0,0(r3)
 | 
			
		||||
	addi	r3,r3,2
 | 
			
		||||
3:	bf	cr7*4+3,4f
 | 
			
		||||
	lbz	r0,0(r4)
 | 
			
		||||
	stb	r0,0(r3)
 | 
			
		||||
4:	blr
 | 
			
		||||
							
								
								
									
										693
									
								
								arch/powerpc/lib/rheap.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										693
									
								
								arch/powerpc/lib/rheap.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,693 @@
 | 
			
		||||
/*
 | 
			
		||||
 * arch/ppc/syslib/rheap.c
 | 
			
		||||
 *
 | 
			
		||||
 * A Remote Heap.  Remote means that we don't touch the memory that the
 | 
			
		||||
 * heap points to. Normal heap implementations use the memory they manage
 | 
			
		||||
 * to place their list. We cannot do that because the memory we manage may
 | 
			
		||||
 * have special properties, for example it is uncachable or of different
 | 
			
		||||
 * endianess.
 | 
			
		||||
 *
 | 
			
		||||
 * Author: Pantelis Antoniou <panto@intracom.gr>
 | 
			
		||||
 *
 | 
			
		||||
 * 2004 (c) INTRACOM S.A. Greece. This file is licensed under
 | 
			
		||||
 * the terms of the GNU General Public License version 2. This program
 | 
			
		||||
 * is licensed "as is" without any warranty of any kind, whether express
 | 
			
		||||
 * or implied.
 | 
			
		||||
 */
 | 
			
		||||
#include <linux/types.h>
 | 
			
		||||
#include <linux/errno.h>
 | 
			
		||||
#include <linux/mm.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
 | 
			
		||||
#include <asm/rheap.h>
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Fixup a list_head, needed when copying lists.  If the pointers fall
 | 
			
		||||
 * between s and e, apply the delta.  This assumes that
 | 
			
		||||
 * sizeof(struct list_head *) == sizeof(unsigned long *).
 | 
			
		||||
 */
 | 
			
		||||
static inline void fixup(unsigned long s, unsigned long e, int d,
 | 
			
		||||
			 struct list_head *l)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long *pp;
 | 
			
		||||
 | 
			
		||||
	pp = (unsigned long *)&l->next;
 | 
			
		||||
	if (*pp >= s && *pp < e)
 | 
			
		||||
		*pp += d;
 | 
			
		||||
 | 
			
		||||
	pp = (unsigned long *)&l->prev;
 | 
			
		||||
	if (*pp >= s && *pp < e)
 | 
			
		||||
		*pp += d;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Grow the allocated blocks */
 | 
			
		||||
static int grow(rh_info_t * info, int max_blocks)
 | 
			
		||||
{
 | 
			
		||||
	rh_block_t *block, *blk;
 | 
			
		||||
	int i, new_blocks;
 | 
			
		||||
	int delta;
 | 
			
		||||
	unsigned long blks, blke;
 | 
			
		||||
 | 
			
		||||
	if (max_blocks <= info->max_blocks)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	new_blocks = max_blocks - info->max_blocks;
 | 
			
		||||
 | 
			
		||||
	block = kmalloc(sizeof(rh_block_t) * max_blocks, GFP_KERNEL);
 | 
			
		||||
	if (block == NULL)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	if (info->max_blocks > 0) {
 | 
			
		||||
 | 
			
		||||
		/* copy old block area */
 | 
			
		||||
		memcpy(block, info->block,
 | 
			
		||||
		       sizeof(rh_block_t) * info->max_blocks);
 | 
			
		||||
 | 
			
		||||
		delta = (char *)block - (char *)info->block;
 | 
			
		||||
 | 
			
		||||
		/* and fixup list pointers */
 | 
			
		||||
		blks = (unsigned long)info->block;
 | 
			
		||||
		blke = (unsigned long)(info->block + info->max_blocks);
 | 
			
		||||
 | 
			
		||||
		for (i = 0, blk = block; i < info->max_blocks; i++, blk++)
 | 
			
		||||
			fixup(blks, blke, delta, &blk->list);
 | 
			
		||||
 | 
			
		||||
		fixup(blks, blke, delta, &info->empty_list);
 | 
			
		||||
		fixup(blks, blke, delta, &info->free_list);
 | 
			
		||||
		fixup(blks, blke, delta, &info->taken_list);
 | 
			
		||||
 | 
			
		||||
		/* free the old allocated memory */
 | 
			
		||||
		if ((info->flags & RHIF_STATIC_BLOCK) == 0)
 | 
			
		||||
			kfree(info->block);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	info->block = block;
 | 
			
		||||
	info->empty_slots += new_blocks;
 | 
			
		||||
	info->max_blocks = max_blocks;
 | 
			
		||||
	info->flags &= ~RHIF_STATIC_BLOCK;
 | 
			
		||||
 | 
			
		||||
	/* add all new blocks to the free list */
 | 
			
		||||
	for (i = 0, blk = block + info->max_blocks; i < new_blocks; i++, blk++)
 | 
			
		||||
		list_add(&blk->list, &info->empty_list);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Assure at least the required amount of empty slots.  If this function
 | 
			
		||||
 * causes a grow in the block area then all pointers kept to the block
 | 
			
		||||
 * area are invalid!
 | 
			
		||||
 */
 | 
			
		||||
static int assure_empty(rh_info_t * info, int slots)
 | 
			
		||||
{
 | 
			
		||||
	int max_blocks;
 | 
			
		||||
 | 
			
		||||
	/* This function is not meant to be used to grow uncontrollably */
 | 
			
		||||
	if (slots >= 4)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	/* Enough space */
 | 
			
		||||
	if (info->empty_slots >= slots)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	/* Next 16 sized block */
 | 
			
		||||
	max_blocks = ((info->max_blocks + slots) + 15) & ~15;
 | 
			
		||||
 | 
			
		||||
	return grow(info, max_blocks);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static rh_block_t *get_slot(rh_info_t * info)
 | 
			
		||||
{
 | 
			
		||||
	rh_block_t *blk;
 | 
			
		||||
 | 
			
		||||
	/* If no more free slots, and failure to extend. */
 | 
			
		||||
	/* XXX: You should have called assure_empty before */
 | 
			
		||||
	if (info->empty_slots == 0) {
 | 
			
		||||
		printk(KERN_ERR "rh: out of slots; crash is imminent.\n");
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Get empty slot to use */
 | 
			
		||||
	blk = list_entry(info->empty_list.next, rh_block_t, list);
 | 
			
		||||
	list_del_init(&blk->list);
 | 
			
		||||
	info->empty_slots--;
 | 
			
		||||
 | 
			
		||||
	/* Initialize */
 | 
			
		||||
	blk->start = NULL;
 | 
			
		||||
	blk->size = 0;
 | 
			
		||||
	blk->owner = NULL;
 | 
			
		||||
 | 
			
		||||
	return blk;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void release_slot(rh_info_t * info, rh_block_t * blk)
 | 
			
		||||
{
 | 
			
		||||
	list_add(&blk->list, &info->empty_list);
 | 
			
		||||
	info->empty_slots++;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void attach_free_block(rh_info_t * info, rh_block_t * blkn)
 | 
			
		||||
{
 | 
			
		||||
	rh_block_t *blk;
 | 
			
		||||
	rh_block_t *before;
 | 
			
		||||
	rh_block_t *after;
 | 
			
		||||
	rh_block_t *next;
 | 
			
		||||
	int size;
 | 
			
		||||
	unsigned long s, e, bs, be;
 | 
			
		||||
	struct list_head *l;
 | 
			
		||||
 | 
			
		||||
	/* We assume that they are aligned properly */
 | 
			
		||||
	size = blkn->size;
 | 
			
		||||
	s = (unsigned long)blkn->start;
 | 
			
		||||
	e = s + size;
 | 
			
		||||
 | 
			
		||||
	/* Find the blocks immediately before and after the given one
 | 
			
		||||
	 * (if any) */
 | 
			
		||||
	before = NULL;
 | 
			
		||||
	after = NULL;
 | 
			
		||||
	next = NULL;
 | 
			
		||||
 | 
			
		||||
	list_for_each(l, &info->free_list) {
 | 
			
		||||
		blk = list_entry(l, rh_block_t, list);
 | 
			
		||||
 | 
			
		||||
		bs = (unsigned long)blk->start;
 | 
			
		||||
		be = bs + blk->size;
 | 
			
		||||
 | 
			
		||||
		if (next == NULL && s >= bs)
 | 
			
		||||
			next = blk;
 | 
			
		||||
 | 
			
		||||
		if (be == s)
 | 
			
		||||
			before = blk;
 | 
			
		||||
 | 
			
		||||
		if (e == bs)
 | 
			
		||||
			after = blk;
 | 
			
		||||
 | 
			
		||||
		/* If both are not null, break now */
 | 
			
		||||
		if (before != NULL && after != NULL)
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Now check if they are really adjacent */
 | 
			
		||||
	if (before != NULL && s != (unsigned long)before->start + before->size)
 | 
			
		||||
		before = NULL;
 | 
			
		||||
 | 
			
		||||
	if (after != NULL && e != (unsigned long)after->start)
 | 
			
		||||
		after = NULL;
 | 
			
		||||
 | 
			
		||||
	/* No coalescing; list insert and return */
 | 
			
		||||
	if (before == NULL && after == NULL) {
 | 
			
		||||
 | 
			
		||||
		if (next != NULL)
 | 
			
		||||
			list_add(&blkn->list, &next->list);
 | 
			
		||||
		else
 | 
			
		||||
			list_add(&blkn->list, &info->free_list);
 | 
			
		||||
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* We don't need it anymore */
 | 
			
		||||
	release_slot(info, blkn);
 | 
			
		||||
 | 
			
		||||
	/* Grow the before block */
 | 
			
		||||
	if (before != NULL && after == NULL) {
 | 
			
		||||
		before->size += size;
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Grow the after block backwards */
 | 
			
		||||
	if (before == NULL && after != NULL) {
 | 
			
		||||
		after->start = (int8_t *)after->start - size;
 | 
			
		||||
		after->size += size;
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Grow the before block, and release the after block */
 | 
			
		||||
	before->size += size + after->size;
 | 
			
		||||
	list_del(&after->list);
 | 
			
		||||
	release_slot(info, after);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void attach_taken_block(rh_info_t * info, rh_block_t * blkn)
 | 
			
		||||
{
 | 
			
		||||
	rh_block_t *blk;
 | 
			
		||||
	struct list_head *l;
 | 
			
		||||
 | 
			
		||||
	/* Find the block immediately before the given one (if any) */
 | 
			
		||||
	list_for_each(l, &info->taken_list) {
 | 
			
		||||
		blk = list_entry(l, rh_block_t, list);
 | 
			
		||||
		if (blk->start > blkn->start) {
 | 
			
		||||
			list_add_tail(&blkn->list, &blk->list);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	list_add_tail(&blkn->list, &info->taken_list);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Create a remote heap dynamically.  Note that no memory for the blocks
 | 
			
		||||
 * are allocated.  It will upon the first allocation
 | 
			
		||||
 */
 | 
			
		||||
rh_info_t *rh_create(unsigned int alignment)
 | 
			
		||||
{
 | 
			
		||||
	rh_info_t *info;
 | 
			
		||||
 | 
			
		||||
	/* Alignment must be a power of two */
 | 
			
		||||
	if ((alignment & (alignment - 1)) != 0)
 | 
			
		||||
		return ERR_PTR(-EINVAL);
 | 
			
		||||
 | 
			
		||||
	info = kmalloc(sizeof(*info), GFP_KERNEL);
 | 
			
		||||
	if (info == NULL)
 | 
			
		||||
		return ERR_PTR(-ENOMEM);
 | 
			
		||||
 | 
			
		||||
	info->alignment = alignment;
 | 
			
		||||
 | 
			
		||||
	/* Initially everything as empty */
 | 
			
		||||
	info->block = NULL;
 | 
			
		||||
	info->max_blocks = 0;
 | 
			
		||||
	info->empty_slots = 0;
 | 
			
		||||
	info->flags = 0;
 | 
			
		||||
 | 
			
		||||
	INIT_LIST_HEAD(&info->empty_list);
 | 
			
		||||
	INIT_LIST_HEAD(&info->free_list);
 | 
			
		||||
	INIT_LIST_HEAD(&info->taken_list);
 | 
			
		||||
 | 
			
		||||
	return info;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Destroy a dynamically created remote heap.  Deallocate only if the areas
 | 
			
		||||
 * are not static
 | 
			
		||||
 */
 | 
			
		||||
void rh_destroy(rh_info_t * info)
 | 
			
		||||
{
 | 
			
		||||
	if ((info->flags & RHIF_STATIC_BLOCK) == 0 && info->block != NULL)
 | 
			
		||||
		kfree(info->block);
 | 
			
		||||
 | 
			
		||||
	if ((info->flags & RHIF_STATIC_INFO) == 0)
 | 
			
		||||
		kfree(info);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Initialize in place a remote heap info block.  This is needed to support
 | 
			
		||||
 * operation very early in the startup of the kernel, when it is not yet safe
 | 
			
		||||
 * to call kmalloc.
 | 
			
		||||
 */
 | 
			
		||||
void rh_init(rh_info_t * info, unsigned int alignment, int max_blocks,
 | 
			
		||||
	     rh_block_t * block)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	rh_block_t *blk;
 | 
			
		||||
 | 
			
		||||
	/* Alignment must be a power of two */
 | 
			
		||||
	if ((alignment & (alignment - 1)) != 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	info->alignment = alignment;
 | 
			
		||||
 | 
			
		||||
	/* Initially everything as empty */
 | 
			
		||||
	info->block = block;
 | 
			
		||||
	info->max_blocks = max_blocks;
 | 
			
		||||
	info->empty_slots = max_blocks;
 | 
			
		||||
	info->flags = RHIF_STATIC_INFO | RHIF_STATIC_BLOCK;
 | 
			
		||||
 | 
			
		||||
	INIT_LIST_HEAD(&info->empty_list);
 | 
			
		||||
	INIT_LIST_HEAD(&info->free_list);
 | 
			
		||||
	INIT_LIST_HEAD(&info->taken_list);
 | 
			
		||||
 | 
			
		||||
	/* Add all new blocks to the free list */
 | 
			
		||||
	for (i = 0, blk = block; i < max_blocks; i++, blk++)
 | 
			
		||||
		list_add(&blk->list, &info->empty_list);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Attach a free memory region, coalesces regions if adjuscent */
 | 
			
		||||
int rh_attach_region(rh_info_t * info, void *start, int size)
 | 
			
		||||
{
 | 
			
		||||
	rh_block_t *blk;
 | 
			
		||||
	unsigned long s, e, m;
 | 
			
		||||
	int r;
 | 
			
		||||
 | 
			
		||||
	/* The region must be aligned */
 | 
			
		||||
	s = (unsigned long)start;
 | 
			
		||||
	e = s + size;
 | 
			
		||||
	m = info->alignment - 1;
 | 
			
		||||
 | 
			
		||||
	/* Round start up */
 | 
			
		||||
	s = (s + m) & ~m;
 | 
			
		||||
 | 
			
		||||
	/* Round end down */
 | 
			
		||||
	e = e & ~m;
 | 
			
		||||
 | 
			
		||||
	/* Take final values */
 | 
			
		||||
	start = (void *)s;
 | 
			
		||||
	size = (int)(e - s);
 | 
			
		||||
 | 
			
		||||
	/* Grow the blocks, if needed */
 | 
			
		||||
	r = assure_empty(info, 1);
 | 
			
		||||
	if (r < 0)
 | 
			
		||||
		return r;
 | 
			
		||||
 | 
			
		||||
	blk = get_slot(info);
 | 
			
		||||
	blk->start = start;
 | 
			
		||||
	blk->size = size;
 | 
			
		||||
	blk->owner = NULL;
 | 
			
		||||
 | 
			
		||||
	attach_free_block(info, blk);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Detatch given address range, splits free block if needed. */
 | 
			
		||||
void *rh_detach_region(rh_info_t * info, void *start, int size)
 | 
			
		||||
{
 | 
			
		||||
	struct list_head *l;
 | 
			
		||||
	rh_block_t *blk, *newblk;
 | 
			
		||||
	unsigned long s, e, m, bs, be;
 | 
			
		||||
 | 
			
		||||
	/* Validate size */
 | 
			
		||||
	if (size <= 0)
 | 
			
		||||
		return ERR_PTR(-EINVAL);
 | 
			
		||||
 | 
			
		||||
	/* The region must be aligned */
 | 
			
		||||
	s = (unsigned long)start;
 | 
			
		||||
	e = s + size;
 | 
			
		||||
	m = info->alignment - 1;
 | 
			
		||||
 | 
			
		||||
	/* Round start up */
 | 
			
		||||
	s = (s + m) & ~m;
 | 
			
		||||
 | 
			
		||||
	/* Round end down */
 | 
			
		||||
	e = e & ~m;
 | 
			
		||||
 | 
			
		||||
	if (assure_empty(info, 1) < 0)
 | 
			
		||||
		return ERR_PTR(-ENOMEM);
 | 
			
		||||
 | 
			
		||||
	blk = NULL;
 | 
			
		||||
	list_for_each(l, &info->free_list) {
 | 
			
		||||
		blk = list_entry(l, rh_block_t, list);
 | 
			
		||||
		/* The range must lie entirely inside one free block */
 | 
			
		||||
		bs = (unsigned long)blk->start;
 | 
			
		||||
		be = (unsigned long)blk->start + blk->size;
 | 
			
		||||
		if (s >= bs && e <= be)
 | 
			
		||||
			break;
 | 
			
		||||
		blk = NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (blk == NULL)
 | 
			
		||||
		return ERR_PTR(-ENOMEM);
 | 
			
		||||
 | 
			
		||||
	/* Perfect fit */
 | 
			
		||||
	if (bs == s && be == e) {
 | 
			
		||||
		/* Delete from free list, release slot */
 | 
			
		||||
		list_del(&blk->list);
 | 
			
		||||
		release_slot(info, blk);
 | 
			
		||||
		return (void *)s;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* blk still in free list, with updated start and/or size */
 | 
			
		||||
	if (bs == s || be == e) {
 | 
			
		||||
		if (bs == s)
 | 
			
		||||
			blk->start = (int8_t *)blk->start + size;
 | 
			
		||||
		blk->size -= size;
 | 
			
		||||
 | 
			
		||||
	} else {
 | 
			
		||||
		/* The front free fragment */
 | 
			
		||||
		blk->size = s - bs;
 | 
			
		||||
 | 
			
		||||
		/* the back free fragment */
 | 
			
		||||
		newblk = get_slot(info);
 | 
			
		||||
		newblk->start = (void *)e;
 | 
			
		||||
		newblk->size = be - e;
 | 
			
		||||
 | 
			
		||||
		list_add(&newblk->list, &blk->list);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return (void *)s;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void *rh_alloc(rh_info_t * info, int size, const char *owner)
 | 
			
		||||
{
 | 
			
		||||
	struct list_head *l;
 | 
			
		||||
	rh_block_t *blk;
 | 
			
		||||
	rh_block_t *newblk;
 | 
			
		||||
	void *start;
 | 
			
		||||
 | 
			
		||||
	/* Validate size */
 | 
			
		||||
	if (size <= 0)
 | 
			
		||||
		return ERR_PTR(-EINVAL);
 | 
			
		||||
 | 
			
		||||
	/* Align to configured alignment */
 | 
			
		||||
	size = (size + (info->alignment - 1)) & ~(info->alignment - 1);
 | 
			
		||||
 | 
			
		||||
	if (assure_empty(info, 1) < 0)
 | 
			
		||||
		return ERR_PTR(-ENOMEM);
 | 
			
		||||
 | 
			
		||||
	blk = NULL;
 | 
			
		||||
	list_for_each(l, &info->free_list) {
 | 
			
		||||
		blk = list_entry(l, rh_block_t, list);
 | 
			
		||||
		if (size <= blk->size)
 | 
			
		||||
			break;
 | 
			
		||||
		blk = NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (blk == NULL)
 | 
			
		||||
		return ERR_PTR(-ENOMEM);
 | 
			
		||||
 | 
			
		||||
	/* Just fits */
 | 
			
		||||
	if (blk->size == size) {
 | 
			
		||||
		/* Move from free list to taken list */
 | 
			
		||||
		list_del(&blk->list);
 | 
			
		||||
		blk->owner = owner;
 | 
			
		||||
		start = blk->start;
 | 
			
		||||
 | 
			
		||||
		attach_taken_block(info, blk);
 | 
			
		||||
 | 
			
		||||
		return start;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	newblk = get_slot(info);
 | 
			
		||||
	newblk->start = blk->start;
 | 
			
		||||
	newblk->size = size;
 | 
			
		||||
	newblk->owner = owner;
 | 
			
		||||
 | 
			
		||||
	/* blk still in free list, with updated start, size */
 | 
			
		||||
	blk->start = (int8_t *)blk->start + size;
 | 
			
		||||
	blk->size -= size;
 | 
			
		||||
 | 
			
		||||
	start = newblk->start;
 | 
			
		||||
 | 
			
		||||
	attach_taken_block(info, newblk);
 | 
			
		||||
 | 
			
		||||
	return start;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* allocate at precisely the given address */
 | 
			
		||||
void *rh_alloc_fixed(rh_info_t * info, void *start, int size, const char *owner)
 | 
			
		||||
{
 | 
			
		||||
	struct list_head *l;
 | 
			
		||||
	rh_block_t *blk, *newblk1, *newblk2;
 | 
			
		||||
	unsigned long s, e, m, bs, be;
 | 
			
		||||
 | 
			
		||||
	/* Validate size */
 | 
			
		||||
	if (size <= 0)
 | 
			
		||||
		return ERR_PTR(-EINVAL);
 | 
			
		||||
 | 
			
		||||
	/* The region must be aligned */
 | 
			
		||||
	s = (unsigned long)start;
 | 
			
		||||
	e = s + size;
 | 
			
		||||
	m = info->alignment - 1;
 | 
			
		||||
 | 
			
		||||
	/* Round start up */
 | 
			
		||||
	s = (s + m) & ~m;
 | 
			
		||||
 | 
			
		||||
	/* Round end down */
 | 
			
		||||
	e = e & ~m;
 | 
			
		||||
 | 
			
		||||
	if (assure_empty(info, 2) < 0)
 | 
			
		||||
		return ERR_PTR(-ENOMEM);
 | 
			
		||||
 | 
			
		||||
	blk = NULL;
 | 
			
		||||
	list_for_each(l, &info->free_list) {
 | 
			
		||||
		blk = list_entry(l, rh_block_t, list);
 | 
			
		||||
		/* The range must lie entirely inside one free block */
 | 
			
		||||
		bs = (unsigned long)blk->start;
 | 
			
		||||
		be = (unsigned long)blk->start + blk->size;
 | 
			
		||||
		if (s >= bs && e <= be)
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (blk == NULL)
 | 
			
		||||
		return ERR_PTR(-ENOMEM);
 | 
			
		||||
 | 
			
		||||
	/* Perfect fit */
 | 
			
		||||
	if (bs == s && be == e) {
 | 
			
		||||
		/* Move from free list to taken list */
 | 
			
		||||
		list_del(&blk->list);
 | 
			
		||||
		blk->owner = owner;
 | 
			
		||||
 | 
			
		||||
		start = blk->start;
 | 
			
		||||
		attach_taken_block(info, blk);
 | 
			
		||||
 | 
			
		||||
		return start;
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* blk still in free list, with updated start and/or size */
 | 
			
		||||
	if (bs == s || be == e) {
 | 
			
		||||
		if (bs == s)
 | 
			
		||||
			blk->start = (int8_t *)blk->start + size;
 | 
			
		||||
		blk->size -= size;
 | 
			
		||||
 | 
			
		||||
	} else {
 | 
			
		||||
		/* The front free fragment */
 | 
			
		||||
		blk->size = s - bs;
 | 
			
		||||
 | 
			
		||||
		/* The back free fragment */
 | 
			
		||||
		newblk2 = get_slot(info);
 | 
			
		||||
		newblk2->start = (void *)e;
 | 
			
		||||
		newblk2->size = be - e;
 | 
			
		||||
 | 
			
		||||
		list_add(&newblk2->list, &blk->list);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	newblk1 = get_slot(info);
 | 
			
		||||
	newblk1->start = (void *)s;
 | 
			
		||||
	newblk1->size = e - s;
 | 
			
		||||
	newblk1->owner = owner;
 | 
			
		||||
 | 
			
		||||
	start = newblk1->start;
 | 
			
		||||
	attach_taken_block(info, newblk1);
 | 
			
		||||
 | 
			
		||||
	return start;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int rh_free(rh_info_t * info, void *start)
 | 
			
		||||
{
 | 
			
		||||
	rh_block_t *blk, *blk2;
 | 
			
		||||
	struct list_head *l;
 | 
			
		||||
	int size;
 | 
			
		||||
 | 
			
		||||
	/* Linear search for block */
 | 
			
		||||
	blk = NULL;
 | 
			
		||||
	list_for_each(l, &info->taken_list) {
 | 
			
		||||
		blk2 = list_entry(l, rh_block_t, list);
 | 
			
		||||
		if (start < blk2->start)
 | 
			
		||||
			break;
 | 
			
		||||
		blk = blk2;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (blk == NULL || start > (blk->start + blk->size))
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	/* Remove from taken list */
 | 
			
		||||
	list_del(&blk->list);
 | 
			
		||||
 | 
			
		||||
	/* Get size of freed block */
 | 
			
		||||
	size = blk->size;
 | 
			
		||||
	attach_free_block(info, blk);
 | 
			
		||||
 | 
			
		||||
	return size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int rh_get_stats(rh_info_t * info, int what, int max_stats, rh_stats_t * stats)
 | 
			
		||||
{
 | 
			
		||||
	rh_block_t *blk;
 | 
			
		||||
	struct list_head *l;
 | 
			
		||||
	struct list_head *h;
 | 
			
		||||
	int nr;
 | 
			
		||||
 | 
			
		||||
	switch (what) {
 | 
			
		||||
 | 
			
		||||
	case RHGS_FREE:
 | 
			
		||||
		h = &info->free_list;
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case RHGS_TAKEN:
 | 
			
		||||
		h = &info->taken_list;
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Linear search for block */
 | 
			
		||||
	nr = 0;
 | 
			
		||||
	list_for_each(l, h) {
 | 
			
		||||
		blk = list_entry(l, rh_block_t, list);
 | 
			
		||||
		if (stats != NULL && nr < max_stats) {
 | 
			
		||||
			stats->start = blk->start;
 | 
			
		||||
			stats->size = blk->size;
 | 
			
		||||
			stats->owner = blk->owner;
 | 
			
		||||
			stats++;
 | 
			
		||||
		}
 | 
			
		||||
		nr++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int rh_set_owner(rh_info_t * info, void *start, const char *owner)
 | 
			
		||||
{
 | 
			
		||||
	rh_block_t *blk, *blk2;
 | 
			
		||||
	struct list_head *l;
 | 
			
		||||
	int size;
 | 
			
		||||
 | 
			
		||||
	/* Linear search for block */
 | 
			
		||||
	blk = NULL;
 | 
			
		||||
	list_for_each(l, &info->taken_list) {
 | 
			
		||||
		blk2 = list_entry(l, rh_block_t, list);
 | 
			
		||||
		if (start < blk2->start)
 | 
			
		||||
			break;
 | 
			
		||||
		blk = blk2;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (blk == NULL || start > (blk->start + blk->size))
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	blk->owner = owner;
 | 
			
		||||
	size = blk->size;
 | 
			
		||||
 | 
			
		||||
	return size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void rh_dump(rh_info_t * info)
 | 
			
		||||
{
 | 
			
		||||
	static rh_stats_t st[32];	/* XXX maximum 32 blocks */
 | 
			
		||||
	int maxnr;
 | 
			
		||||
	int i, nr;
 | 
			
		||||
 | 
			
		||||
	maxnr = sizeof(st) / sizeof(st[0]);
 | 
			
		||||
 | 
			
		||||
	printk(KERN_INFO
 | 
			
		||||
	       "info @0x%p (%d slots empty / %d max)\n",
 | 
			
		||||
	       info, info->empty_slots, info->max_blocks);
 | 
			
		||||
 | 
			
		||||
	printk(KERN_INFO "  Free:\n");
 | 
			
		||||
	nr = rh_get_stats(info, RHGS_FREE, maxnr, st);
 | 
			
		||||
	if (nr > maxnr)
 | 
			
		||||
		nr = maxnr;
 | 
			
		||||
	for (i = 0; i < nr; i++)
 | 
			
		||||
		printk(KERN_INFO
 | 
			
		||||
		       "    0x%p-0x%p (%u)\n",
 | 
			
		||||
		       st[i].start, (int8_t *) st[i].start + st[i].size,
 | 
			
		||||
		       st[i].size);
 | 
			
		||||
	printk(KERN_INFO "\n");
 | 
			
		||||
 | 
			
		||||
	printk(KERN_INFO "  Taken:\n");
 | 
			
		||||
	nr = rh_get_stats(info, RHGS_TAKEN, maxnr, st);
 | 
			
		||||
	if (nr > maxnr)
 | 
			
		||||
		nr = maxnr;
 | 
			
		||||
	for (i = 0; i < nr; i++)
 | 
			
		||||
		printk(KERN_INFO
 | 
			
		||||
		       "    0x%p-0x%p (%u) %s\n",
 | 
			
		||||
		       st[i].start, (int8_t *) st[i].start + st[i].size,
 | 
			
		||||
		       st[i].size, st[i].owner != NULL ? st[i].owner : "");
 | 
			
		||||
	printk(KERN_INFO "\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void rh_dump_blk(rh_info_t * info, rh_block_t * blk)
 | 
			
		||||
{
 | 
			
		||||
	printk(KERN_INFO
 | 
			
		||||
	       "blk @0x%p: 0x%p-0x%p (%u)\n",
 | 
			
		||||
	       blk, blk->start, (int8_t *) blk->start + blk->size, blk->size);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										141
									
								
								arch/powerpc/lib/sstep.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								arch/powerpc/lib/sstep.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,141 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Single-step support.
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2004 Paul Mackerras <paulus@au.ibm.com>, IBM
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version
 | 
			
		||||
 * 2 of the License, or (at your option) any later version.
 | 
			
		||||
 */
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/ptrace.h>
 | 
			
		||||
#include <asm/sstep.h>
 | 
			
		||||
#include <asm/processor.h>
 | 
			
		||||
 | 
			
		||||
extern char system_call_common[];
 | 
			
		||||
 | 
			
		||||
/* Bits in SRR1 that are copied from MSR */
 | 
			
		||||
#define MSR_MASK	0xffffffff87c0ffff
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Determine whether a conditional branch instruction would branch.
 | 
			
		||||
 */
 | 
			
		||||
static int branch_taken(unsigned int instr, struct pt_regs *regs)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int bo = (instr >> 21) & 0x1f;
 | 
			
		||||
	unsigned int bi;
 | 
			
		||||
 | 
			
		||||
	if ((bo & 4) == 0) {
 | 
			
		||||
		/* decrement counter */
 | 
			
		||||
		--regs->ctr;
 | 
			
		||||
		if (((bo >> 1) & 1) ^ (regs->ctr == 0))
 | 
			
		||||
			return 0;
 | 
			
		||||
	}
 | 
			
		||||
	if ((bo & 0x10) == 0) {
 | 
			
		||||
		/* check bit from CR */
 | 
			
		||||
		bi = (instr >> 16) & 0x1f;
 | 
			
		||||
		if (((regs->ccr >> (31 - bi)) & 1) != ((bo >> 3) & 1))
 | 
			
		||||
			return 0;
 | 
			
		||||
	}
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Emulate instructions that cause a transfer of control.
 | 
			
		||||
 * Returns 1 if the step was emulated, 0 if not,
 | 
			
		||||
 * or -1 if the instruction is one that should not be stepped,
 | 
			
		||||
 * such as an rfid, or a mtmsrd that would clear MSR_RI.
 | 
			
		||||
 */
 | 
			
		||||
int emulate_step(struct pt_regs *regs, unsigned int instr)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int opcode, rd;
 | 
			
		||||
	unsigned long int imm;
 | 
			
		||||
 | 
			
		||||
	opcode = instr >> 26;
 | 
			
		||||
	switch (opcode) {
 | 
			
		||||
	case 16:	/* bc */
 | 
			
		||||
		imm = (signed short)(instr & 0xfffc);
 | 
			
		||||
		if ((instr & 2) == 0)
 | 
			
		||||
			imm += regs->nip;
 | 
			
		||||
		regs->nip += 4;
 | 
			
		||||
		if ((regs->msr & MSR_SF) == 0)
 | 
			
		||||
			regs->nip &= 0xffffffffUL;
 | 
			
		||||
		if (instr & 1)
 | 
			
		||||
			regs->link = regs->nip;
 | 
			
		||||
		if (branch_taken(instr, regs))
 | 
			
		||||
			regs->nip = imm;
 | 
			
		||||
		return 1;
 | 
			
		||||
	case 17:	/* sc */
 | 
			
		||||
		/*
 | 
			
		||||
		 * N.B. this uses knowledge about how the syscall
 | 
			
		||||
		 * entry code works.  If that is changed, this will
 | 
			
		||||
		 * need to be changed also.
 | 
			
		||||
		 */
 | 
			
		||||
		regs->gpr[9] = regs->gpr[13];
 | 
			
		||||
		regs->gpr[11] = regs->nip + 4;
 | 
			
		||||
		regs->gpr[12] = regs->msr & MSR_MASK;
 | 
			
		||||
		regs->gpr[13] = (unsigned long) get_paca();
 | 
			
		||||
		regs->nip = (unsigned long) &system_call_common;
 | 
			
		||||
		regs->msr = MSR_KERNEL;
 | 
			
		||||
		return 1;
 | 
			
		||||
	case 18:	/* b */
 | 
			
		||||
		imm = instr & 0x03fffffc;
 | 
			
		||||
		if (imm & 0x02000000)
 | 
			
		||||
			imm -= 0x04000000;
 | 
			
		||||
		if ((instr & 2) == 0)
 | 
			
		||||
			imm += regs->nip;
 | 
			
		||||
		if (instr & 1) {
 | 
			
		||||
			regs->link = regs->nip + 4;
 | 
			
		||||
			if ((regs->msr & MSR_SF) == 0)
 | 
			
		||||
				regs->link &= 0xffffffffUL;
 | 
			
		||||
		}
 | 
			
		||||
		if ((regs->msr & MSR_SF) == 0)
 | 
			
		||||
			imm &= 0xffffffffUL;
 | 
			
		||||
		regs->nip = imm;
 | 
			
		||||
		return 1;
 | 
			
		||||
	case 19:
 | 
			
		||||
		switch (instr & 0x7fe) {
 | 
			
		||||
		case 0x20:	/* bclr */
 | 
			
		||||
		case 0x420:	/* bcctr */
 | 
			
		||||
			imm = (instr & 0x400)? regs->ctr: regs->link;
 | 
			
		||||
			regs->nip += 4;
 | 
			
		||||
			if ((regs->msr & MSR_SF) == 0) {
 | 
			
		||||
				regs->nip &= 0xffffffffUL;
 | 
			
		||||
				imm &= 0xffffffffUL;
 | 
			
		||||
			}
 | 
			
		||||
			if (instr & 1)
 | 
			
		||||
				regs->link = regs->nip;
 | 
			
		||||
			if (branch_taken(instr, regs))
 | 
			
		||||
				regs->nip = imm;
 | 
			
		||||
			return 1;
 | 
			
		||||
		case 0x24:	/* rfid, scary */
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
	case 31:
 | 
			
		||||
		rd = (instr >> 21) & 0x1f;
 | 
			
		||||
		switch (instr & 0x7fe) {
 | 
			
		||||
		case 0xa6:	/* mfmsr */
 | 
			
		||||
			regs->gpr[rd] = regs->msr & MSR_MASK;
 | 
			
		||||
			regs->nip += 4;
 | 
			
		||||
			if ((regs->msr & MSR_SF) == 0)
 | 
			
		||||
				regs->nip &= 0xffffffffUL;
 | 
			
		||||
			return 1;
 | 
			
		||||
		case 0x164:	/* mtmsrd */
 | 
			
		||||
			/* only MSR_EE and MSR_RI get changed if bit 15 set */
 | 
			
		||||
			/* mtmsrd doesn't change MSR_HV and MSR_ME */
 | 
			
		||||
			imm = (instr & 0x10000)? 0x8002: 0xefffffffffffefffUL;
 | 
			
		||||
			imm = (regs->msr & MSR_MASK & ~imm)
 | 
			
		||||
				| (regs->gpr[rd] & imm);
 | 
			
		||||
			if ((imm & MSR_RI) == 0)
 | 
			
		||||
				/* can't step mtmsrd that would clear MSR_RI */
 | 
			
		||||
				return -1;
 | 
			
		||||
			regs->msr = imm;
 | 
			
		||||
			regs->nip += 4;
 | 
			
		||||
			if ((imm & MSR_SF) == 0)
 | 
			
		||||
				regs->nip &= 0xffffffffUL;
 | 
			
		||||
			return 1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										23
									
								
								arch/powerpc/lib/strcase.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								arch/powerpc/lib/strcase.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
#include <linux/ctype.h>
 | 
			
		||||
 | 
			
		||||
int strcasecmp(const char *s1, const char *s2)
 | 
			
		||||
{
 | 
			
		||||
	int c1, c2;
 | 
			
		||||
 | 
			
		||||
	do {
 | 
			
		||||
		c1 = tolower(*s1++);
 | 
			
		||||
		c2 = tolower(*s2++);
 | 
			
		||||
	} while (c1 == c2 && c1 != 0);
 | 
			
		||||
	return c1 - c2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int strncasecmp(const char *s1, const char *s2, int n)
 | 
			
		||||
{
 | 
			
		||||
	int c1, c2;
 | 
			
		||||
 | 
			
		||||
	do {
 | 
			
		||||
		c1 = tolower(*s1++);
 | 
			
		||||
		c2 = tolower(*s2++);
 | 
			
		||||
	} while ((--n > 0) && c1 == c2 && c1 != 0);
 | 
			
		||||
	return c1 - c2;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										203
									
								
								arch/powerpc/lib/string.S
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										203
									
								
								arch/powerpc/lib/string.S
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,203 @@
 | 
			
		||||
/*
 | 
			
		||||
 * String handling functions for PowerPC.
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 1996 Paul Mackerras.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version
 | 
			
		||||
 * 2 of the License, or (at your option) any later version.
 | 
			
		||||
 */
 | 
			
		||||
#include <linux/config.h>
 | 
			
		||||
#include <asm/processor.h>
 | 
			
		||||
#include <asm/errno.h>
 | 
			
		||||
#include <asm/ppc_asm.h>
 | 
			
		||||
 | 
			
		||||
	.text
 | 
			
		||||
	.stabs	"arch/powerpc/lib/",N_SO,0,0,0f
 | 
			
		||||
	.stabs	"string.S",N_SO,0,0,0f
 | 
			
		||||
0:
 | 
			
		||||
 | 
			
		||||
	.section __ex_table,"a"
 | 
			
		||||
#ifdef CONFIG_PPC64
 | 
			
		||||
	.align	3
 | 
			
		||||
#define EXTBL	.llong
 | 
			
		||||
#else
 | 
			
		||||
	.align	2
 | 
			
		||||
#define EXTBL	.long
 | 
			
		||||
#endif
 | 
			
		||||
	.text
 | 
			
		||||
	
 | 
			
		||||
_GLOBAL(strcpy)
 | 
			
		||||
	addi	r5,r3,-1
 | 
			
		||||
	addi	r4,r4,-1
 | 
			
		||||
1:	lbzu	r0,1(r4)
 | 
			
		||||
	cmpwi	0,r0,0
 | 
			
		||||
	stbu	r0,1(r5)
 | 
			
		||||
	bne	1b
 | 
			
		||||
	blr
 | 
			
		||||
 | 
			
		||||
/* This clears out any unused part of the destination buffer,
 | 
			
		||||
   just as the libc version does.  -- paulus */
 | 
			
		||||
_GLOBAL(strncpy)
 | 
			
		||||
	cmpwi	0,r5,0
 | 
			
		||||
	beqlr
 | 
			
		||||
	mtctr	r5
 | 
			
		||||
	addi	r6,r3,-1
 | 
			
		||||
	addi	r4,r4,-1
 | 
			
		||||
1:	lbzu	r0,1(r4)
 | 
			
		||||
	cmpwi	0,r0,0
 | 
			
		||||
	stbu	r0,1(r6)
 | 
			
		||||
	bdnzf	2,1b		/* dec ctr, branch if ctr != 0 && !cr0.eq */
 | 
			
		||||
	bnelr			/* if we didn't hit a null char, we're done */
 | 
			
		||||
	mfctr	r5
 | 
			
		||||
	cmpwi	0,r5,0		/* any space left in destination buffer? */
 | 
			
		||||
	beqlr			/* we know r0 == 0 here */
 | 
			
		||||
2:	stbu	r0,1(r6)	/* clear it out if so */
 | 
			
		||||
	bdnz	2b
 | 
			
		||||
	blr
 | 
			
		||||
 | 
			
		||||
_GLOBAL(strcat)
 | 
			
		||||
	addi	r5,r3,-1
 | 
			
		||||
	addi	r4,r4,-1
 | 
			
		||||
1:	lbzu	r0,1(r5)
 | 
			
		||||
	cmpwi	0,r0,0
 | 
			
		||||
	bne	1b
 | 
			
		||||
	addi	r5,r5,-1
 | 
			
		||||
1:	lbzu	r0,1(r4)
 | 
			
		||||
	cmpwi	0,r0,0
 | 
			
		||||
	stbu	r0,1(r5)
 | 
			
		||||
	bne	1b
 | 
			
		||||
	blr
 | 
			
		||||
 | 
			
		||||
_GLOBAL(strcmp)
 | 
			
		||||
	addi	r5,r3,-1
 | 
			
		||||
	addi	r4,r4,-1
 | 
			
		||||
1:	lbzu	r3,1(r5)
 | 
			
		||||
	cmpwi	1,r3,0
 | 
			
		||||
	lbzu	r0,1(r4)
 | 
			
		||||
	subf.	r3,r0,r3
 | 
			
		||||
	beqlr	1
 | 
			
		||||
	beq	1b
 | 
			
		||||
	blr
 | 
			
		||||
 | 
			
		||||
_GLOBAL(strlen)
 | 
			
		||||
	addi	r4,r3,-1
 | 
			
		||||
1:	lbzu	r0,1(r4)
 | 
			
		||||
	cmpwi	0,r0,0
 | 
			
		||||
	bne	1b
 | 
			
		||||
	subf	r3,r3,r4
 | 
			
		||||
	blr
 | 
			
		||||
 | 
			
		||||
_GLOBAL(memcmp)
 | 
			
		||||
	cmpwi	0,r5,0
 | 
			
		||||
	ble-	2f
 | 
			
		||||
	mtctr	r5
 | 
			
		||||
	addi	r6,r3,-1
 | 
			
		||||
	addi	r4,r4,-1
 | 
			
		||||
1:	lbzu	r3,1(r6)
 | 
			
		||||
	lbzu	r0,1(r4)
 | 
			
		||||
	subf.	r3,r0,r3
 | 
			
		||||
	bdnzt	2,1b
 | 
			
		||||
	blr
 | 
			
		||||
2:	li	r3,0
 | 
			
		||||
	blr
 | 
			
		||||
 | 
			
		||||
_GLOBAL(memchr)
 | 
			
		||||
	cmpwi	0,r5,0
 | 
			
		||||
	ble-	2f
 | 
			
		||||
	mtctr	r5
 | 
			
		||||
	addi	r3,r3,-1
 | 
			
		||||
1:	lbzu	r0,1(r3)
 | 
			
		||||
	cmpw	0,r0,r4
 | 
			
		||||
	bdnzf	2,1b
 | 
			
		||||
	beqlr
 | 
			
		||||
2:	li	r3,0
 | 
			
		||||
	blr
 | 
			
		||||
 | 
			
		||||
_GLOBAL(__clear_user)
 | 
			
		||||
	addi	r6,r3,-4
 | 
			
		||||
	li	r3,0
 | 
			
		||||
	li	r5,0
 | 
			
		||||
	cmplwi	0,r4,4
 | 
			
		||||
	blt	7f
 | 
			
		||||
	/* clear a single word */
 | 
			
		||||
11:	stwu	r5,4(r6)
 | 
			
		||||
	beqlr
 | 
			
		||||
	/* clear word sized chunks */
 | 
			
		||||
	andi.	r0,r6,3
 | 
			
		||||
	add	r4,r0,r4
 | 
			
		||||
	subf	r6,r0,r6
 | 
			
		||||
	srwi	r0,r4,2
 | 
			
		||||
	andi.	r4,r4,3
 | 
			
		||||
	mtctr	r0
 | 
			
		||||
	bdz	7f
 | 
			
		||||
1:	stwu	r5,4(r6)
 | 
			
		||||
	bdnz	1b
 | 
			
		||||
	/* clear byte sized chunks */
 | 
			
		||||
7:	cmpwi	0,r4,0
 | 
			
		||||
	beqlr
 | 
			
		||||
	mtctr	r4
 | 
			
		||||
	addi	r6,r6,3
 | 
			
		||||
8:	stbu	r5,1(r6)
 | 
			
		||||
	bdnz	8b
 | 
			
		||||
	blr
 | 
			
		||||
90:	mr	r3,r4
 | 
			
		||||
	blr
 | 
			
		||||
91:	mfctr	r3
 | 
			
		||||
	slwi	r3,r3,2
 | 
			
		||||
	add	r3,r3,r4
 | 
			
		||||
	blr
 | 
			
		||||
92:	mfctr	r3
 | 
			
		||||
	blr
 | 
			
		||||
 | 
			
		||||
	.section __ex_table,"a"
 | 
			
		||||
	EXTBL	11b,90b
 | 
			
		||||
	EXTBL	1b,91b
 | 
			
		||||
	EXTBL	8b,92b
 | 
			
		||||
	.text
 | 
			
		||||
 | 
			
		||||
_GLOBAL(__strncpy_from_user)
 | 
			
		||||
	addi	r6,r3,-1
 | 
			
		||||
	addi	r4,r4,-1
 | 
			
		||||
	cmpwi	0,r5,0
 | 
			
		||||
	beq	2f
 | 
			
		||||
	mtctr	r5
 | 
			
		||||
1:	lbzu	r0,1(r4)
 | 
			
		||||
	cmpwi	0,r0,0
 | 
			
		||||
	stbu	r0,1(r6)
 | 
			
		||||
	bdnzf	2,1b		/* dec ctr, branch if ctr != 0 && !cr0.eq */
 | 
			
		||||
	beq	3f
 | 
			
		||||
2:	addi	r6,r6,1
 | 
			
		||||
3:	subf	r3,r3,r6
 | 
			
		||||
	blr
 | 
			
		||||
99:	li	r3,-EFAULT
 | 
			
		||||
	blr
 | 
			
		||||
 | 
			
		||||
	.section __ex_table,"a"
 | 
			
		||||
	EXTBL	1b,99b
 | 
			
		||||
	.text
 | 
			
		||||
 | 
			
		||||
/* r3 = str, r4 = len (> 0), r5 = top (highest addr) */
 | 
			
		||||
_GLOBAL(__strnlen_user)
 | 
			
		||||
	addi	r7,r3,-1
 | 
			
		||||
	subf	r6,r7,r5	/* top+1 - str */
 | 
			
		||||
	cmplw	0,r4,r6
 | 
			
		||||
	bge	0f
 | 
			
		||||
	mr	r6,r4
 | 
			
		||||
0:	mtctr	r6		/* ctr = min(len, top - str) */
 | 
			
		||||
1:	lbzu	r0,1(r7)	/* get next byte */
 | 
			
		||||
	cmpwi	0,r0,0
 | 
			
		||||
	bdnzf	2,1b		/* loop if --ctr != 0 && byte != 0 */
 | 
			
		||||
	addi	r7,r7,1
 | 
			
		||||
	subf	r3,r3,r7	/* number of bytes we have looked at */
 | 
			
		||||
	beqlr			/* return if we found a 0 byte */
 | 
			
		||||
	cmpw	0,r3,r4		/* did we look at all len bytes? */
 | 
			
		||||
	blt	99f		/* if not, must have hit top */
 | 
			
		||||
	addi	r3,r4,1		/* return len + 1 to indicate no null found */
 | 
			
		||||
	blr
 | 
			
		||||
99:	li	r3,0		/* bad address, return 0 */
 | 
			
		||||
	blr
 | 
			
		||||
 | 
			
		||||
	.section __ex_table,"a"
 | 
			
		||||
	EXTBL	1b,99b
 | 
			
		||||
							
								
								
									
										41
									
								
								arch/powerpc/lib/usercopy.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								arch/powerpc/lib/usercopy.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Functions which are too large to be inlined.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version
 | 
			
		||||
 * 2 of the License, or (at your option) any later version.
 | 
			
		||||
 */
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <asm/uaccess.h>
 | 
			
		||||
 | 
			
		||||
unsigned long copy_from_user(void *to, const void __user *from, unsigned long n)
 | 
			
		||||
{
 | 
			
		||||
	if (likely(access_ok(VERIFY_READ, from, n)))
 | 
			
		||||
		n = __copy_from_user(to, from, n);
 | 
			
		||||
	else
 | 
			
		||||
		memset(to, 0, n);
 | 
			
		||||
	return n;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsigned long copy_to_user(void __user *to, const void *from, unsigned long n)
 | 
			
		||||
{
 | 
			
		||||
	if (likely(access_ok(VERIFY_WRITE, to, n)))
 | 
			
		||||
		n = __copy_to_user(to, from, n);
 | 
			
		||||
	return n;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsigned long copy_in_user(void __user *to, const void __user *from,
 | 
			
		||||
			   unsigned long n)
 | 
			
		||||
{
 | 
			
		||||
	might_sleep();
 | 
			
		||||
	if (likely(access_ok(VERIFY_READ, from, n) &&
 | 
			
		||||
	    access_ok(VERIFY_WRITE, to, n)))
 | 
			
		||||
		n =__copy_tofrom_user(to, from, n);
 | 
			
		||||
	return n;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORT_SYMBOL(copy_from_user);
 | 
			
		||||
EXPORT_SYMBOL(copy_to_user);
 | 
			
		||||
EXPORT_SYMBOL(copy_in_user);
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										120
									
								
								arch/powerpc/mm/44x_mmu.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								arch/powerpc/mm/44x_mmu.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,120 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Modifications by Matt Porter (mporter@mvista.com) to support
 | 
			
		||||
 * PPC44x Book E processors.
 | 
			
		||||
 *
 | 
			
		||||
 * This file contains the routines for initializing the MMU
 | 
			
		||||
 * on the 4xx series of chips.
 | 
			
		||||
 *  -- paulus
 | 
			
		||||
 *
 | 
			
		||||
 *  Derived from arch/ppc/mm/init.c:
 | 
			
		||||
 *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
 | 
			
		||||
 *
 | 
			
		||||
 *  Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au)
 | 
			
		||||
 *  and Cort Dougan (PReP) (cort@cs.nmt.edu)
 | 
			
		||||
 *    Copyright (C) 1996 Paul Mackerras
 | 
			
		||||
 *  Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk).
 | 
			
		||||
 *
 | 
			
		||||
 *  Derived from "arch/i386/mm/init.c"
 | 
			
		||||
 *    Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or
 | 
			
		||||
 *  modify it under the terms of the GNU General Public License
 | 
			
		||||
 *  as published by the Free Software Foundation; either version
 | 
			
		||||
 *  2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/config.h>
 | 
			
		||||
#include <linux/signal.h>
 | 
			
		||||
#include <linux/sched.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/errno.h>
 | 
			
		||||
#include <linux/string.h>
 | 
			
		||||
#include <linux/types.h>
 | 
			
		||||
#include <linux/ptrace.h>
 | 
			
		||||
#include <linux/mman.h>
 | 
			
		||||
#include <linux/mm.h>
 | 
			
		||||
#include <linux/swap.h>
 | 
			
		||||
#include <linux/stddef.h>
 | 
			
		||||
#include <linux/vmalloc.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/delay.h>
 | 
			
		||||
#include <linux/highmem.h>
 | 
			
		||||
 | 
			
		||||
#include <asm/pgalloc.h>
 | 
			
		||||
#include <asm/prom.h>
 | 
			
		||||
#include <asm/io.h>
 | 
			
		||||
#include <asm/mmu_context.h>
 | 
			
		||||
#include <asm/pgtable.h>
 | 
			
		||||
#include <asm/mmu.h>
 | 
			
		||||
#include <asm/uaccess.h>
 | 
			
		||||
#include <asm/smp.h>
 | 
			
		||||
#include <asm/bootx.h>
 | 
			
		||||
#include <asm/machdep.h>
 | 
			
		||||
#include <asm/setup.h>
 | 
			
		||||
 | 
			
		||||
#include "mmu_decl.h"
 | 
			
		||||
 | 
			
		||||
extern char etext[], _stext[];
 | 
			
		||||
 | 
			
		||||
/* Used by the 44x TLB replacement exception handler.
 | 
			
		||||
 * Just needed it declared someplace.
 | 
			
		||||
 */
 | 
			
		||||
unsigned int tlb_44x_index = 0;
 | 
			
		||||
unsigned int tlb_44x_hwater = 62;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * "Pins" a 256MB TLB entry in AS0 for kernel lowmem
 | 
			
		||||
 */
 | 
			
		||||
static void __init
 | 
			
		||||
ppc44x_pin_tlb(int slot, unsigned int virt, unsigned int phys)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long attrib = 0;
 | 
			
		||||
 | 
			
		||||
	__asm__ __volatile__("\
 | 
			
		||||
	clrrwi	%2,%2,10\n\
 | 
			
		||||
	ori	%2,%2,%4\n\
 | 
			
		||||
	clrrwi	%1,%1,10\n\
 | 
			
		||||
	li	%0,0\n\
 | 
			
		||||
	ori	%0,%0,%5\n\
 | 
			
		||||
	tlbwe	%2,%3,%6\n\
 | 
			
		||||
	tlbwe	%1,%3,%7\n\
 | 
			
		||||
	tlbwe	%0,%3,%8"
 | 
			
		||||
	:
 | 
			
		||||
	: "r" (attrib), "r" (phys), "r" (virt), "r" (slot),
 | 
			
		||||
	  "i" (PPC44x_TLB_VALID | PPC44x_TLB_256M),
 | 
			
		||||
	  "i" (PPC44x_TLB_SW | PPC44x_TLB_SR | PPC44x_TLB_SX | PPC44x_TLB_G),
 | 
			
		||||
	  "i" (PPC44x_TLB_PAGEID),
 | 
			
		||||
	  "i" (PPC44x_TLB_XLAT),
 | 
			
		||||
	  "i" (PPC44x_TLB_ATTRIB));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * MMU_init_hw does the chip-specific initialization of the MMU hardware.
 | 
			
		||||
 */
 | 
			
		||||
void __init MMU_init_hw(void)
 | 
			
		||||
{
 | 
			
		||||
	flush_instruction_cache();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsigned long __init mmu_mapin_ram(void)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int pinned_tlbs = 1;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	/* Determine number of entries necessary to cover lowmem */
 | 
			
		||||
	pinned_tlbs = (unsigned int)
 | 
			
		||||
		(_ALIGN(total_lowmem, PPC44x_PIN_SIZE) >> PPC44x_PIN_SHIFT);
 | 
			
		||||
 | 
			
		||||
	/* Write upper watermark to save location */
 | 
			
		||||
	tlb_44x_hwater = PPC44x_LOW_SLOT - pinned_tlbs;
 | 
			
		||||
 | 
			
		||||
	/* If necessary, set additional pinned TLBs */
 | 
			
		||||
	if (pinned_tlbs > 1)
 | 
			
		||||
		for (i = (PPC44x_LOW_SLOT-(pinned_tlbs-1)); i < PPC44x_LOW_SLOT; i++) {
 | 
			
		||||
			unsigned int phys_addr = (PPC44x_LOW_SLOT-i) * PPC44x_PIN_SIZE;
 | 
			
		||||
			ppc44x_pin_tlb(i, phys_addr+PAGE_OFFSET, phys_addr);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	return total_lowmem;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										141
									
								
								arch/powerpc/mm/4xx_mmu.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								arch/powerpc/mm/4xx_mmu.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,141 @@
 | 
			
		||||
/*
 | 
			
		||||
 * This file contains the routines for initializing the MMU
 | 
			
		||||
 * on the 4xx series of chips.
 | 
			
		||||
 *  -- paulus
 | 
			
		||||
 *
 | 
			
		||||
 *  Derived from arch/ppc/mm/init.c:
 | 
			
		||||
 *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
 | 
			
		||||
 *
 | 
			
		||||
 *  Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au)
 | 
			
		||||
 *  and Cort Dougan (PReP) (cort@cs.nmt.edu)
 | 
			
		||||
 *    Copyright (C) 1996 Paul Mackerras
 | 
			
		||||
 *  Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk).
 | 
			
		||||
 *
 | 
			
		||||
 *  Derived from "arch/i386/mm/init.c"
 | 
			
		||||
 *    Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or
 | 
			
		||||
 *  modify it under the terms of the GNU General Public License
 | 
			
		||||
 *  as published by the Free Software Foundation; either version
 | 
			
		||||
 *  2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/config.h>
 | 
			
		||||
#include <linux/signal.h>
 | 
			
		||||
#include <linux/sched.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/errno.h>
 | 
			
		||||
#include <linux/string.h>
 | 
			
		||||
#include <linux/types.h>
 | 
			
		||||
#include <linux/ptrace.h>
 | 
			
		||||
#include <linux/mman.h>
 | 
			
		||||
#include <linux/mm.h>
 | 
			
		||||
#include <linux/swap.h>
 | 
			
		||||
#include <linux/stddef.h>
 | 
			
		||||
#include <linux/vmalloc.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/delay.h>
 | 
			
		||||
#include <linux/highmem.h>
 | 
			
		||||
 | 
			
		||||
#include <asm/pgalloc.h>
 | 
			
		||||
#include <asm/prom.h>
 | 
			
		||||
#include <asm/io.h>
 | 
			
		||||
#include <asm/mmu_context.h>
 | 
			
		||||
#include <asm/pgtable.h>
 | 
			
		||||
#include <asm/mmu.h>
 | 
			
		||||
#include <asm/uaccess.h>
 | 
			
		||||
#include <asm/smp.h>
 | 
			
		||||
#include <asm/bootx.h>
 | 
			
		||||
#include <asm/machdep.h>
 | 
			
		||||
#include <asm/setup.h>
 | 
			
		||||
#include "mmu_decl.h"
 | 
			
		||||
 | 
			
		||||
extern int __map_without_ltlbs;
 | 
			
		||||
/*
 | 
			
		||||
 * MMU_init_hw does the chip-specific initialization of the MMU hardware.
 | 
			
		||||
 */
 | 
			
		||||
void __init MMU_init_hw(void)
 | 
			
		||||
{
 | 
			
		||||
	/*
 | 
			
		||||
	 * The Zone Protection Register (ZPR) defines how protection will
 | 
			
		||||
	 * be applied to every page which is a member of a given zone. At
 | 
			
		||||
	 * present, we utilize only two of the 4xx's zones.
 | 
			
		||||
	 * The zone index bits (of ZSEL) in the PTE are used for software
 | 
			
		||||
	 * indicators, except the LSB.  For user access, zone 1 is used,
 | 
			
		||||
	 * for kernel access, zone 0 is used.  We set all but zone 1
 | 
			
		||||
	 * to zero, allowing only kernel access as indicated in the PTE.
 | 
			
		||||
	 * For zone 1, we set a 01 binary (a value of 10 will not work)
 | 
			
		||||
	 * to allow user access as indicated in the PTE.  This also allows
 | 
			
		||||
	 * kernel access as indicated in the PTE.
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
        mtspr(SPRN_ZPR, 0x10000000);
 | 
			
		||||
 | 
			
		||||
	flush_instruction_cache();
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Set up the real-mode cache parameters for the exception vector
 | 
			
		||||
	 * handlers (which are run in real-mode).
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
        mtspr(SPRN_DCWR, 0x00000000);	/* All caching is write-back */
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
	 * Cache instruction and data space where the exception
 | 
			
		||||
	 * vectors and the kernel live in real-mode.
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
        mtspr(SPRN_DCCR, 0xF0000000);	/* 512 MB of data space at 0x0. */
 | 
			
		||||
        mtspr(SPRN_ICCR, 0xF0000000);	/* 512 MB of instr. space at 0x0. */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define LARGE_PAGE_SIZE_16M	(1<<24)
 | 
			
		||||
#define LARGE_PAGE_SIZE_4M	(1<<22)
 | 
			
		||||
 | 
			
		||||
unsigned long __init mmu_mapin_ram(void)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long v, s;
 | 
			
		||||
	phys_addr_t p;
 | 
			
		||||
 | 
			
		||||
	v = KERNELBASE;
 | 
			
		||||
	p = PPC_MEMSTART;
 | 
			
		||||
	s = 0;
 | 
			
		||||
 | 
			
		||||
	if (__map_without_ltlbs) {
 | 
			
		||||
		return s;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	while (s <= (total_lowmem - LARGE_PAGE_SIZE_16M)) {
 | 
			
		||||
		pmd_t *pmdp;
 | 
			
		||||
		unsigned long val = p | _PMD_SIZE_16M | _PAGE_HWEXEC | _PAGE_HWWRITE;
 | 
			
		||||
 | 
			
		||||
		spin_lock(&init_mm.page_table_lock);
 | 
			
		||||
		pmdp = pmd_offset(pgd_offset_k(v), v);
 | 
			
		||||
		pmd_val(*pmdp++) = val;
 | 
			
		||||
		pmd_val(*pmdp++) = val;
 | 
			
		||||
		pmd_val(*pmdp++) = val;
 | 
			
		||||
		pmd_val(*pmdp++) = val;
 | 
			
		||||
		spin_unlock(&init_mm.page_table_lock);
 | 
			
		||||
 | 
			
		||||
		v += LARGE_PAGE_SIZE_16M;
 | 
			
		||||
		p += LARGE_PAGE_SIZE_16M;
 | 
			
		||||
		s += LARGE_PAGE_SIZE_16M;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	while (s <= (total_lowmem - LARGE_PAGE_SIZE_4M)) {
 | 
			
		||||
		pmd_t *pmdp;
 | 
			
		||||
		unsigned long val = p | _PMD_SIZE_4M | _PAGE_HWEXEC | _PAGE_HWWRITE;
 | 
			
		||||
 | 
			
		||||
		spin_lock(&init_mm.page_table_lock);
 | 
			
		||||
		pmdp = pmd_offset(pgd_offset_k(v), v);
 | 
			
		||||
		pmd_val(*pmdp) = val;
 | 
			
		||||
		spin_unlock(&init_mm.page_table_lock);
 | 
			
		||||
 | 
			
		||||
		v += LARGE_PAGE_SIZE_4M;
 | 
			
		||||
		p += LARGE_PAGE_SIZE_4M;
 | 
			
		||||
		s += LARGE_PAGE_SIZE_4M;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return s;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								arch/powerpc/mm/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								arch/powerpc/mm/Makefile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
#
 | 
			
		||||
# Makefile for the linux ppc-specific parts of the memory manager.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
obj-y				:= fault.o mem.o
 | 
			
		||||
obj-$(CONFIG_PPC32)		+= init.o pgtable.o mmu_context.o \
 | 
			
		||||
				   mem_pieces.o tlb.o
 | 
			
		||||
obj-$(CONFIG_PPC64)		+= init64.o pgtable64.o mmu_context64.o
 | 
			
		||||
obj-$(CONFIG_PPC_STD_MMU_32)	+= ppc_mmu.o hash_32.o
 | 
			
		||||
obj-$(CONFIG_40x)		+= 4xx_mmu.o
 | 
			
		||||
obj-$(CONFIG_44x)		+= 44x_mmu.o
 | 
			
		||||
obj-$(CONFIG_FSL_BOOKE)		+= fsl_booke_mmu.o
 | 
			
		||||
							
								
								
									
										391
									
								
								arch/powerpc/mm/fault.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										391
									
								
								arch/powerpc/mm/fault.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,391 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  arch/ppc/mm/fault.c
 | 
			
		||||
 *
 | 
			
		||||
 *  PowerPC version
 | 
			
		||||
 *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
 | 
			
		||||
 *
 | 
			
		||||
 *  Derived from "arch/i386/mm/fault.c"
 | 
			
		||||
 *    Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
 | 
			
		||||
 *
 | 
			
		||||
 *  Modified by Cort Dougan and Paul Mackerras.
 | 
			
		||||
 *
 | 
			
		||||
 *  Modified for PPC64 by Dave Engebretsen (engebret@ibm.com)
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or
 | 
			
		||||
 *  modify it under the terms of the GNU General Public License
 | 
			
		||||
 *  as published by the Free Software Foundation; either version
 | 
			
		||||
 *  2 of the License, or (at your option) any later version.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/config.h>
 | 
			
		||||
#include <linux/signal.h>
 | 
			
		||||
#include <linux/sched.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/errno.h>
 | 
			
		||||
#include <linux/string.h>
 | 
			
		||||
#include <linux/types.h>
 | 
			
		||||
#include <linux/ptrace.h>
 | 
			
		||||
#include <linux/mman.h>
 | 
			
		||||
#include <linux/mm.h>
 | 
			
		||||
#include <linux/interrupt.h>
 | 
			
		||||
#include <linux/highmem.h>
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/kprobes.h>
 | 
			
		||||
 | 
			
		||||
#include <asm/page.h>
 | 
			
		||||
#include <asm/pgtable.h>
 | 
			
		||||
#include <asm/mmu.h>
 | 
			
		||||
#include <asm/mmu_context.h>
 | 
			
		||||
#include <asm/system.h>
 | 
			
		||||
#include <asm/uaccess.h>
 | 
			
		||||
#include <asm/tlbflush.h>
 | 
			
		||||
#include <asm/kdebug.h>
 | 
			
		||||
#include <asm/siginfo.h>
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Check whether the instruction at regs->nip is a store using
 | 
			
		||||
 * an update addressing form which will update r1.
 | 
			
		||||
 */
 | 
			
		||||
static int store_updates_sp(struct pt_regs *regs)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int inst;
 | 
			
		||||
 | 
			
		||||
	if (get_user(inst, (unsigned int __user *)regs->nip))
 | 
			
		||||
		return 0;
 | 
			
		||||
	/* check for 1 in the rA field */
 | 
			
		||||
	if (((inst >> 16) & 0x1f) != 1)
 | 
			
		||||
		return 0;
 | 
			
		||||
	/* check major opcode */
 | 
			
		||||
	switch (inst >> 26) {
 | 
			
		||||
	case 37:	/* stwu */
 | 
			
		||||
	case 39:	/* stbu */
 | 
			
		||||
	case 45:	/* sthu */
 | 
			
		||||
	case 53:	/* stfsu */
 | 
			
		||||
	case 55:	/* stfdu */
 | 
			
		||||
		return 1;
 | 
			
		||||
	case 62:	/* std or stdu */
 | 
			
		||||
		return (inst & 3) == 1;
 | 
			
		||||
	case 31:
 | 
			
		||||
		/* check minor opcode */
 | 
			
		||||
		switch ((inst >> 1) & 0x3ff) {
 | 
			
		||||
		case 181:	/* stdux */
 | 
			
		||||
		case 183:	/* stwux */
 | 
			
		||||
		case 247:	/* stbux */
 | 
			
		||||
		case 439:	/* sthux */
 | 
			
		||||
		case 695:	/* stfsux */
 | 
			
		||||
		case 759:	/* stfdux */
 | 
			
		||||
			return 1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void do_dabr(struct pt_regs *regs, unsigned long error_code)
 | 
			
		||||
{
 | 
			
		||||
	siginfo_t info;
 | 
			
		||||
 | 
			
		||||
	if (notify_die(DIE_DABR_MATCH, "dabr_match", regs, error_code,
 | 
			
		||||
			11, SIGSEGV) == NOTIFY_STOP)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (debugger_dabr_match(regs))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/* Clear the DABR */
 | 
			
		||||
	set_dabr(0);
 | 
			
		||||
 | 
			
		||||
	/* Deliver the signal to userspace */
 | 
			
		||||
	info.si_signo = SIGTRAP;
 | 
			
		||||
	info.si_errno = 0;
 | 
			
		||||
	info.si_code = TRAP_HWBKPT;
 | 
			
		||||
	info.si_addr = (void __user *)regs->nip;
 | 
			
		||||
	force_sig_info(SIGTRAP, &info, current);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * For 600- and 800-family processors, the error_code parameter is DSISR
 | 
			
		||||
 * for a data fault, SRR1 for an instruction fault. For 400-family processors
 | 
			
		||||
 * the error_code parameter is ESR for a data fault, 0 for an instruction
 | 
			
		||||
 * fault.
 | 
			
		||||
 * For 64-bit processors, the error_code parameter is
 | 
			
		||||
 *  - DSISR for a non-SLB data access fault,
 | 
			
		||||
 *  - SRR1 & 0x08000000 for a non-SLB instruction access fault
 | 
			
		||||
 *  - 0 any SLB fault.
 | 
			
		||||
 *
 | 
			
		||||
 * The return value is 0 if the fault was handled, or the signal
 | 
			
		||||
 * number if this is a kernel fault that can't be handled here.
 | 
			
		||||
 */
 | 
			
		||||
int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address,
 | 
			
		||||
			    unsigned long error_code)
 | 
			
		||||
{
 | 
			
		||||
	struct vm_area_struct * vma;
 | 
			
		||||
	struct mm_struct *mm = current->mm;
 | 
			
		||||
	siginfo_t info;
 | 
			
		||||
	int code = SEGV_MAPERR;
 | 
			
		||||
	int is_write = 0;
 | 
			
		||||
	int trap = TRAP(regs);
 | 
			
		||||
 	int is_exec = trap == 0x400;
 | 
			
		||||
 | 
			
		||||
#if !(defined(CONFIG_4xx) || defined(CONFIG_BOOKE))
 | 
			
		||||
	/*
 | 
			
		||||
	 * Fortunately the bit assignments in SRR1 for an instruction
 | 
			
		||||
	 * fault and DSISR for a data fault are mostly the same for the
 | 
			
		||||
	 * bits we are interested in.  But there are some bits which
 | 
			
		||||
	 * indicate errors in DSISR but can validly be set in SRR1.
 | 
			
		||||
	 */
 | 
			
		||||
	if (trap == 0x400)
 | 
			
		||||
		error_code &= 0x48200000;
 | 
			
		||||
	else
 | 
			
		||||
		is_write = error_code & DSISR_ISSTORE;
 | 
			
		||||
#else
 | 
			
		||||
	is_write = error_code & ESR_DST;
 | 
			
		||||
#endif /* CONFIG_4xx || CONFIG_BOOKE */
 | 
			
		||||
 | 
			
		||||
	if (notify_die(DIE_PAGE_FAULT, "page_fault", regs, error_code,
 | 
			
		||||
				11, SIGSEGV) == NOTIFY_STOP)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	if (trap == 0x300) {
 | 
			
		||||
		if (debugger_fault_handler(regs))
 | 
			
		||||
			return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* On a kernel SLB miss we can only check for a valid exception entry */
 | 
			
		||||
	if (!user_mode(regs) && (address >= TASK_SIZE))
 | 
			
		||||
		return SIGSEGV;
 | 
			
		||||
 | 
			
		||||
#if !(defined(CONFIG_4xx) || defined(CONFIG_BOOKE))
 | 
			
		||||
  	if (error_code & DSISR_DABRMATCH) {
 | 
			
		||||
		/* DABR match */
 | 
			
		||||
		do_dabr(regs, error_code);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
#endif /* !(CONFIG_4xx || CONFIG_BOOKE)*/
 | 
			
		||||
 | 
			
		||||
	if (in_atomic() || mm == NULL) {
 | 
			
		||||
		if (!user_mode(regs))
 | 
			
		||||
			return SIGSEGV;
 | 
			
		||||
		/* in_atomic() in user mode is really bad,
 | 
			
		||||
		   as is current->mm == NULL. */
 | 
			
		||||
		printk(KERN_EMERG "Page fault in user mode with"
 | 
			
		||||
		       "in_atomic() = %d mm = %p\n", in_atomic(), mm);
 | 
			
		||||
		printk(KERN_EMERG "NIP = %lx  MSR = %lx\n",
 | 
			
		||||
		       regs->nip, regs->msr);
 | 
			
		||||
		die("Weird page fault", regs, SIGSEGV);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* When running in the kernel we expect faults to occur only to
 | 
			
		||||
	 * addresses in user space.  All other faults represent errors in the
 | 
			
		||||
	 * kernel and should generate an OOPS.  Unfortunatly, in the case of an
 | 
			
		||||
	 * erroneous fault occuring in a code path which already holds mmap_sem
 | 
			
		||||
	 * we will deadlock attempting to validate the fault against the
 | 
			
		||||
	 * address space.  Luckily the kernel only validly references user
 | 
			
		||||
	 * space from well defined areas of code, which are listed in the
 | 
			
		||||
	 * exceptions table.
 | 
			
		||||
	 *
 | 
			
		||||
	 * As the vast majority of faults will be valid we will only perform
 | 
			
		||||
	 * the source reference check when there is a possibilty of a deadlock.
 | 
			
		||||
	 * Attempt to lock the address space, if we cannot we then validate the
 | 
			
		||||
	 * source.  If this is invalid we can skip the address space check,
 | 
			
		||||
	 * thus avoiding the deadlock.
 | 
			
		||||
	 */
 | 
			
		||||
	if (!down_read_trylock(&mm->mmap_sem)) {
 | 
			
		||||
		if (!user_mode(regs) && !search_exception_tables(regs->nip))
 | 
			
		||||
			goto bad_area_nosemaphore;
 | 
			
		||||
 | 
			
		||||
		down_read(&mm->mmap_sem);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	vma = find_vma(mm, address);
 | 
			
		||||
	if (!vma)
 | 
			
		||||
		goto bad_area;
 | 
			
		||||
	if (vma->vm_start <= address)
 | 
			
		||||
		goto good_area;
 | 
			
		||||
	if (!(vma->vm_flags & VM_GROWSDOWN))
 | 
			
		||||
		goto bad_area;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * N.B. The POWER/Open ABI allows programs to access up to
 | 
			
		||||
	 * 288 bytes below the stack pointer.
 | 
			
		||||
	 * The kernel signal delivery code writes up to about 1.5kB
 | 
			
		||||
	 * below the stack pointer (r1) before decrementing it.
 | 
			
		||||
	 * The exec code can write slightly over 640kB to the stack
 | 
			
		||||
	 * before setting the user r1.  Thus we allow the stack to
 | 
			
		||||
	 * expand to 1MB without further checks.
 | 
			
		||||
	 */
 | 
			
		||||
	if (address + 0x100000 < vma->vm_end) {
 | 
			
		||||
		/* get user regs even if this fault is in kernel mode */
 | 
			
		||||
		struct pt_regs *uregs = current->thread.regs;
 | 
			
		||||
		if (uregs == NULL)
 | 
			
		||||
			goto bad_area;
 | 
			
		||||
 | 
			
		||||
		/*
 | 
			
		||||
		 * A user-mode access to an address a long way below
 | 
			
		||||
		 * the stack pointer is only valid if the instruction
 | 
			
		||||
		 * is one which would update the stack pointer to the
 | 
			
		||||
		 * address accessed if the instruction completed,
 | 
			
		||||
		 * i.e. either stwu rs,n(r1) or stwux rs,r1,rb
 | 
			
		||||
		 * (or the byte, halfword, float or double forms).
 | 
			
		||||
		 *
 | 
			
		||||
		 * If we don't check this then any write to the area
 | 
			
		||||
		 * between the last mapped region and the stack will
 | 
			
		||||
		 * expand the stack rather than segfaulting.
 | 
			
		||||
		 */
 | 
			
		||||
		if (address + 2048 < uregs->gpr[1]
 | 
			
		||||
		    && (!user_mode(regs) || !store_updates_sp(regs)))
 | 
			
		||||
			goto bad_area;
 | 
			
		||||
	}
 | 
			
		||||
	if (expand_stack(vma, address))
 | 
			
		||||
		goto bad_area;
 | 
			
		||||
 | 
			
		||||
good_area:
 | 
			
		||||
	code = SEGV_ACCERR;
 | 
			
		||||
#if defined(CONFIG_6xx)
 | 
			
		||||
	if (error_code & 0x95700000)
 | 
			
		||||
		/* an error such as lwarx to I/O controller space,
 | 
			
		||||
		   address matching DABR, eciwx, etc. */
 | 
			
		||||
		goto bad_area;
 | 
			
		||||
#endif /* CONFIG_6xx */
 | 
			
		||||
#if defined(CONFIG_8xx)
 | 
			
		||||
        /* The MPC8xx seems to always set 0x80000000, which is
 | 
			
		||||
         * "undefined".  Of those that can be set, this is the only
 | 
			
		||||
         * one which seems bad.
 | 
			
		||||
         */
 | 
			
		||||
	if (error_code & 0x10000000)
 | 
			
		||||
                /* Guarded storage error. */
 | 
			
		||||
		goto bad_area;
 | 
			
		||||
#endif /* CONFIG_8xx */
 | 
			
		||||
 | 
			
		||||
	if (is_exec) {
 | 
			
		||||
#ifdef CONFIG_PPC64
 | 
			
		||||
		/* protection fault */
 | 
			
		||||
		if (error_code & DSISR_PROTFAULT)
 | 
			
		||||
			goto bad_area;
 | 
			
		||||
		if (!(vma->vm_flags & VM_EXEC))
 | 
			
		||||
			goto bad_area;
 | 
			
		||||
#endif
 | 
			
		||||
#if defined(CONFIG_4xx) || defined(CONFIG_BOOKE)
 | 
			
		||||
		pte_t *ptep;
 | 
			
		||||
 | 
			
		||||
		/* Since 4xx/Book-E supports per-page execute permission,
 | 
			
		||||
		 * we lazily flush dcache to icache. */
 | 
			
		||||
		ptep = NULL;
 | 
			
		||||
		if (get_pteptr(mm, address, &ptep) && pte_present(*ptep)) {
 | 
			
		||||
			struct page *page = pte_page(*ptep);
 | 
			
		||||
 | 
			
		||||
			if (! test_bit(PG_arch_1, &page->flags)) {
 | 
			
		||||
				flush_dcache_icache_page(page);
 | 
			
		||||
				set_bit(PG_arch_1, &page->flags);
 | 
			
		||||
			}
 | 
			
		||||
			pte_update(ptep, 0, _PAGE_HWEXEC);
 | 
			
		||||
			_tlbie(address);
 | 
			
		||||
			pte_unmap(ptep);
 | 
			
		||||
			up_read(&mm->mmap_sem);
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
		if (ptep != NULL)
 | 
			
		||||
			pte_unmap(ptep);
 | 
			
		||||
#endif
 | 
			
		||||
	/* a write */
 | 
			
		||||
	} else if (is_write) {
 | 
			
		||||
		if (!(vma->vm_flags & VM_WRITE))
 | 
			
		||||
			goto bad_area;
 | 
			
		||||
	/* a read */
 | 
			
		||||
	} else {
 | 
			
		||||
		/* protection fault */
 | 
			
		||||
		if (error_code & 0x08000000)
 | 
			
		||||
			goto bad_area;
 | 
			
		||||
		if (!(vma->vm_flags & (VM_READ | VM_EXEC)))
 | 
			
		||||
			goto bad_area;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * If for any reason at all we couldn't handle the fault,
 | 
			
		||||
	 * make sure we exit gracefully rather than endlessly redo
 | 
			
		||||
	 * the fault.
 | 
			
		||||
	 */
 | 
			
		||||
 survive:
 | 
			
		||||
	switch (handle_mm_fault(mm, vma, address, is_write)) {
 | 
			
		||||
 | 
			
		||||
	case VM_FAULT_MINOR:
 | 
			
		||||
		current->min_flt++;
 | 
			
		||||
		break;
 | 
			
		||||
	case VM_FAULT_MAJOR:
 | 
			
		||||
		current->maj_flt++;
 | 
			
		||||
		break;
 | 
			
		||||
	case VM_FAULT_SIGBUS:
 | 
			
		||||
		goto do_sigbus;
 | 
			
		||||
	case VM_FAULT_OOM:
 | 
			
		||||
		goto out_of_memory;
 | 
			
		||||
	default:
 | 
			
		||||
		BUG();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	up_read(&mm->mmap_sem);
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
bad_area:
 | 
			
		||||
	up_read(&mm->mmap_sem);
 | 
			
		||||
 | 
			
		||||
bad_area_nosemaphore:
 | 
			
		||||
	/* User mode accesses cause a SIGSEGV */
 | 
			
		||||
	if (user_mode(regs)) {
 | 
			
		||||
		_exception(SIGSEGV, regs, code, address);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (is_exec && (error_code & DSISR_PROTFAULT)
 | 
			
		||||
	    && printk_ratelimit())
 | 
			
		||||
		printk(KERN_CRIT "kernel tried to execute NX-protected"
 | 
			
		||||
		       " page (%lx) - exploit attempt? (uid: %d)\n",
 | 
			
		||||
		       address, current->uid);
 | 
			
		||||
 | 
			
		||||
	return SIGSEGV;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * We ran out of memory, or some other thing happened to us that made
 | 
			
		||||
 * us unable to handle the page fault gracefully.
 | 
			
		||||
 */
 | 
			
		||||
out_of_memory:
 | 
			
		||||
	up_read(&mm->mmap_sem);
 | 
			
		||||
	if (current->pid == 1) {
 | 
			
		||||
		yield();
 | 
			
		||||
		down_read(&mm->mmap_sem);
 | 
			
		||||
		goto survive;
 | 
			
		||||
	}
 | 
			
		||||
	printk("VM: killing process %s\n", current->comm);
 | 
			
		||||
	if (user_mode(regs))
 | 
			
		||||
		do_exit(SIGKILL);
 | 
			
		||||
	return SIGKILL;
 | 
			
		||||
 | 
			
		||||
do_sigbus:
 | 
			
		||||
	up_read(&mm->mmap_sem);
 | 
			
		||||
	if (user_mode(regs)) {
 | 
			
		||||
		info.si_signo = SIGBUS;
 | 
			
		||||
		info.si_errno = 0;
 | 
			
		||||
		info.si_code = BUS_ADRERR;
 | 
			
		||||
		info.si_addr = (void __user *)address;
 | 
			
		||||
		force_sig_info(SIGBUS, &info, current);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	return SIGBUS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * bad_page_fault is called when we have a bad access from the kernel.
 | 
			
		||||
 * It is called from the DSI and ISI handlers in head.S and from some
 | 
			
		||||
 * of the procedures in traps.c.
 | 
			
		||||
 */
 | 
			
		||||
void bad_page_fault(struct pt_regs *regs, unsigned long address, int sig)
 | 
			
		||||
{
 | 
			
		||||
	const struct exception_table_entry *entry;
 | 
			
		||||
 | 
			
		||||
	/* Are we prepared to handle this fault?  */
 | 
			
		||||
	if ((entry = search_exception_tables(regs->nip)) != NULL) {
 | 
			
		||||
		regs->nip = entry->fixup;
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* kernel has accessed a bad area */
 | 
			
		||||
	die("Kernel access of bad area", regs, sig);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										237
									
								
								arch/powerpc/mm/fsl_booke_mmu.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										237
									
								
								arch/powerpc/mm/fsl_booke_mmu.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,237 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Modifications by Kumar Gala (kumar.gala@freescale.com) to support
 | 
			
		||||
 * E500 Book E processors.
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright 2004 Freescale Semiconductor, Inc
 | 
			
		||||
 *
 | 
			
		||||
 * This file contains the routines for initializing the MMU
 | 
			
		||||
 * on the 4xx series of chips.
 | 
			
		||||
 *  -- paulus
 | 
			
		||||
 *
 | 
			
		||||
 *  Derived from arch/ppc/mm/init.c:
 | 
			
		||||
 *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
 | 
			
		||||
 *
 | 
			
		||||
 *  Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au)
 | 
			
		||||
 *  and Cort Dougan (PReP) (cort@cs.nmt.edu)
 | 
			
		||||
 *    Copyright (C) 1996 Paul Mackerras
 | 
			
		||||
 *  Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk).
 | 
			
		||||
 *
 | 
			
		||||
 *  Derived from "arch/i386/mm/init.c"
 | 
			
		||||
 *    Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or
 | 
			
		||||
 *  modify it under the terms of the GNU General Public License
 | 
			
		||||
 *  as published by the Free Software Foundation; either version
 | 
			
		||||
 *  2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/config.h>
 | 
			
		||||
#include <linux/signal.h>
 | 
			
		||||
#include <linux/sched.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/errno.h>
 | 
			
		||||
#include <linux/string.h>
 | 
			
		||||
#include <linux/types.h>
 | 
			
		||||
#include <linux/ptrace.h>
 | 
			
		||||
#include <linux/mman.h>
 | 
			
		||||
#include <linux/mm.h>
 | 
			
		||||
#include <linux/swap.h>
 | 
			
		||||
#include <linux/stddef.h>
 | 
			
		||||
#include <linux/vmalloc.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/delay.h>
 | 
			
		||||
#include <linux/highmem.h>
 | 
			
		||||
 | 
			
		||||
#include <asm/pgalloc.h>
 | 
			
		||||
#include <asm/prom.h>
 | 
			
		||||
#include <asm/io.h>
 | 
			
		||||
#include <asm/mmu_context.h>
 | 
			
		||||
#include <asm/pgtable.h>
 | 
			
		||||
#include <asm/mmu.h>
 | 
			
		||||
#include <asm/uaccess.h>
 | 
			
		||||
#include <asm/smp.h>
 | 
			
		||||
#include <asm/bootx.h>
 | 
			
		||||
#include <asm/machdep.h>
 | 
			
		||||
#include <asm/setup.h>
 | 
			
		||||
 | 
			
		||||
extern void loadcam_entry(unsigned int index);
 | 
			
		||||
unsigned int tlbcam_index;
 | 
			
		||||
unsigned int num_tlbcam_entries;
 | 
			
		||||
static unsigned long __cam0, __cam1, __cam2;
 | 
			
		||||
extern unsigned long total_lowmem;
 | 
			
		||||
extern unsigned long __max_low_memory;
 | 
			
		||||
#define MAX_LOW_MEM	CONFIG_LOWMEM_SIZE
 | 
			
		||||
 | 
			
		||||
#define NUM_TLBCAMS	(16)
 | 
			
		||||
 | 
			
		||||
struct tlbcam {
 | 
			
		||||
   	u32	MAS0;
 | 
			
		||||
	u32	MAS1;
 | 
			
		||||
	u32	MAS2;
 | 
			
		||||
	u32	MAS3;
 | 
			
		||||
	u32	MAS7;
 | 
			
		||||
} TLBCAM[NUM_TLBCAMS];
 | 
			
		||||
 | 
			
		||||
struct tlbcamrange {
 | 
			
		||||
   	unsigned long start;
 | 
			
		||||
	unsigned long limit;
 | 
			
		||||
	phys_addr_t phys;
 | 
			
		||||
} tlbcam_addrs[NUM_TLBCAMS];
 | 
			
		||||
 | 
			
		||||
extern unsigned int tlbcam_index;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Return PA for this VA if it is mapped by a CAM, or 0
 | 
			
		||||
 */
 | 
			
		||||
unsigned long v_mapped_by_tlbcam(unsigned long va)
 | 
			
		||||
{
 | 
			
		||||
	int b;
 | 
			
		||||
	for (b = 0; b < tlbcam_index; ++b)
 | 
			
		||||
		if (va >= tlbcam_addrs[b].start && va < tlbcam_addrs[b].limit)
 | 
			
		||||
			return tlbcam_addrs[b].phys + (va - tlbcam_addrs[b].start);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Return VA for a given PA or 0 if not mapped
 | 
			
		||||
 */
 | 
			
		||||
unsigned long p_mapped_by_tlbcam(unsigned long pa)
 | 
			
		||||
{
 | 
			
		||||
	int b;
 | 
			
		||||
	for (b = 0; b < tlbcam_index; ++b)
 | 
			
		||||
		if (pa >= tlbcam_addrs[b].phys
 | 
			
		||||
	    	    && pa < (tlbcam_addrs[b].limit-tlbcam_addrs[b].start)
 | 
			
		||||
		              +tlbcam_addrs[b].phys)
 | 
			
		||||
			return tlbcam_addrs[b].start+(pa-tlbcam_addrs[b].phys);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Set up one of the I/D BAT (block address translation) register pairs.
 | 
			
		||||
 * The parameters are not checked; in particular size must be a power
 | 
			
		||||
 * of 4 between 4k and 256M.
 | 
			
		||||
 */
 | 
			
		||||
void settlbcam(int index, unsigned long virt, phys_addr_t phys,
 | 
			
		||||
		unsigned int size, int flags, unsigned int pid)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int tsize, lz;
 | 
			
		||||
 | 
			
		||||
	asm ("cntlzw %0,%1" : "=r" (lz) : "r" (size));
 | 
			
		||||
	tsize = (21 - lz) / 2;
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_SMP
 | 
			
		||||
	if ((flags & _PAGE_NO_CACHE) == 0)
 | 
			
		||||
		flags |= _PAGE_COHERENT;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	TLBCAM[index].MAS0 = MAS0_TLBSEL(1) | MAS0_ESEL(index) | MAS0_NV(index+1);
 | 
			
		||||
	TLBCAM[index].MAS1 = MAS1_VALID | MAS1_IPROT | MAS1_TSIZE(tsize) | MAS1_TID(pid);
 | 
			
		||||
	TLBCAM[index].MAS2 = virt & PAGE_MASK;
 | 
			
		||||
 | 
			
		||||
	TLBCAM[index].MAS2 |= (flags & _PAGE_WRITETHRU) ? MAS2_W : 0;
 | 
			
		||||
	TLBCAM[index].MAS2 |= (flags & _PAGE_NO_CACHE) ? MAS2_I : 0;
 | 
			
		||||
	TLBCAM[index].MAS2 |= (flags & _PAGE_COHERENT) ? MAS2_M : 0;
 | 
			
		||||
	TLBCAM[index].MAS2 |= (flags & _PAGE_GUARDED) ? MAS2_G : 0;
 | 
			
		||||
	TLBCAM[index].MAS2 |= (flags & _PAGE_ENDIAN) ? MAS2_E : 0;
 | 
			
		||||
 | 
			
		||||
	TLBCAM[index].MAS3 = (phys & PAGE_MASK) | MAS3_SX | MAS3_SR;
 | 
			
		||||
	TLBCAM[index].MAS3 |= ((flags & _PAGE_RW) ? MAS3_SW : 0);
 | 
			
		||||
 | 
			
		||||
#ifndef CONFIG_KGDB /* want user access for breakpoints */
 | 
			
		||||
	if (flags & _PAGE_USER) {
 | 
			
		||||
	   TLBCAM[index].MAS3 |= MAS3_UX | MAS3_UR;
 | 
			
		||||
	   TLBCAM[index].MAS3 |= ((flags & _PAGE_RW) ? MAS3_UW : 0);
 | 
			
		||||
	}
 | 
			
		||||
#else
 | 
			
		||||
	TLBCAM[index].MAS3 |= MAS3_UX | MAS3_UR;
 | 
			
		||||
	TLBCAM[index].MAS3 |= ((flags & _PAGE_RW) ? MAS3_UW : 0);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	tlbcam_addrs[index].start = virt;
 | 
			
		||||
	tlbcam_addrs[index].limit = virt + size - 1;
 | 
			
		||||
	tlbcam_addrs[index].phys = phys;
 | 
			
		||||
 | 
			
		||||
	loadcam_entry(index);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void invalidate_tlbcam_entry(int index)
 | 
			
		||||
{
 | 
			
		||||
	TLBCAM[index].MAS0 = MAS0_TLBSEL(1) | MAS0_ESEL(index);
 | 
			
		||||
	TLBCAM[index].MAS1 = ~MAS1_VALID;
 | 
			
		||||
 | 
			
		||||
	loadcam_entry(index);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void __init cam_mapin_ram(unsigned long cam0, unsigned long cam1,
 | 
			
		||||
		unsigned long cam2)
 | 
			
		||||
{
 | 
			
		||||
	settlbcam(0, KERNELBASE, PPC_MEMSTART, cam0, _PAGE_KERNEL, 0);
 | 
			
		||||
	tlbcam_index++;
 | 
			
		||||
	if (cam1) {
 | 
			
		||||
		tlbcam_index++;
 | 
			
		||||
		settlbcam(1, KERNELBASE+cam0, PPC_MEMSTART+cam0, cam1, _PAGE_KERNEL, 0);
 | 
			
		||||
	}
 | 
			
		||||
	if (cam2) {
 | 
			
		||||
		tlbcam_index++;
 | 
			
		||||
		settlbcam(2, KERNELBASE+cam0+cam1, PPC_MEMSTART+cam0+cam1, cam2, _PAGE_KERNEL, 0);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * MMU_init_hw does the chip-specific initialization of the MMU hardware.
 | 
			
		||||
 */
 | 
			
		||||
void __init MMU_init_hw(void)
 | 
			
		||||
{
 | 
			
		||||
	flush_instruction_cache();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsigned long __init mmu_mapin_ram(void)
 | 
			
		||||
{
 | 
			
		||||
	cam_mapin_ram(__cam0, __cam1, __cam2);
 | 
			
		||||
 | 
			
		||||
	return __cam0 + __cam1 + __cam2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void __init
 | 
			
		||||
adjust_total_lowmem(void)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long max_low_mem = MAX_LOW_MEM;
 | 
			
		||||
	unsigned long cam_max = 0x10000000;
 | 
			
		||||
	unsigned long ram;
 | 
			
		||||
 | 
			
		||||
	/* adjust CAM size to max_low_mem */
 | 
			
		||||
	if (max_low_mem < cam_max)
 | 
			
		||||
		cam_max = max_low_mem;
 | 
			
		||||
 | 
			
		||||
	/* adjust lowmem size to max_low_mem */
 | 
			
		||||
	if (max_low_mem < total_lowmem)
 | 
			
		||||
		ram = max_low_mem;
 | 
			
		||||
	else
 | 
			
		||||
		ram = total_lowmem;
 | 
			
		||||
 | 
			
		||||
	/* Calculate CAM values */
 | 
			
		||||
	__cam0 = 1UL << 2 * (__ilog2(ram) / 2);
 | 
			
		||||
	if (__cam0 > cam_max)
 | 
			
		||||
		__cam0 = cam_max;
 | 
			
		||||
	ram -= __cam0;
 | 
			
		||||
	if (ram) {
 | 
			
		||||
		__cam1 = 1UL << 2 * (__ilog2(ram) / 2);
 | 
			
		||||
		if (__cam1 > cam_max)
 | 
			
		||||
			__cam1 = cam_max;
 | 
			
		||||
		ram -= __cam1;
 | 
			
		||||
	}
 | 
			
		||||
	if (ram) {
 | 
			
		||||
		__cam2 = 1UL << 2 * (__ilog2(ram) / 2);
 | 
			
		||||
		if (__cam2 > cam_max)
 | 
			
		||||
			__cam2 = cam_max;
 | 
			
		||||
		ram -= __cam2;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	printk(KERN_INFO "Memory CAM mapping: CAM0=%ldMb, CAM1=%ldMb,"
 | 
			
		||||
			" CAM2=%ldMb residual: %ldMb\n",
 | 
			
		||||
			__cam0 >> 20, __cam1 >> 20, __cam2 >> 20,
 | 
			
		||||
			(total_lowmem - __cam0 - __cam1 - __cam2) >> 20);
 | 
			
		||||
	__max_low_memory = max_low_mem = __cam0 + __cam1 + __cam2;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										618
									
								
								arch/powerpc/mm/hash_32.S
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										618
									
								
								arch/powerpc/mm/hash_32.S
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,618 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  arch/ppc/kernel/hashtable.S
 | 
			
		||||
 *
 | 
			
		||||
 *  $Id: hashtable.S,v 1.6 1999/10/08 01:56:15 paulus Exp $
 | 
			
		||||
 *
 | 
			
		||||
 *  PowerPC version
 | 
			
		||||
 *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
 | 
			
		||||
 *  Rewritten by Cort Dougan (cort@cs.nmt.edu) for PReP
 | 
			
		||||
 *    Copyright (C) 1996 Cort Dougan <cort@cs.nmt.edu>
 | 
			
		||||
 *  Adapted for Power Macintosh by Paul Mackerras.
 | 
			
		||||
 *  Low-level exception handlers and MMU support
 | 
			
		||||
 *  rewritten by Paul Mackerras.
 | 
			
		||||
 *    Copyright (C) 1996 Paul Mackerras.
 | 
			
		||||
 *
 | 
			
		||||
 *  This file contains low-level assembler routines for managing
 | 
			
		||||
 *  the PowerPC MMU hash table.  (PPC 8xx processors don't use a
 | 
			
		||||
 *  hash table, so this file is not used on them.)
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or
 | 
			
		||||
 *  modify it under the terms of the GNU General Public License
 | 
			
		||||
 *  as published by the Free Software Foundation; either version
 | 
			
		||||
 *  2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/config.h>
 | 
			
		||||
#include <asm/processor.h>
 | 
			
		||||
#include <asm/page.h>
 | 
			
		||||
#include <asm/pgtable.h>
 | 
			
		||||
#include <asm/cputable.h>
 | 
			
		||||
#include <asm/ppc_asm.h>
 | 
			
		||||
#include <asm/thread_info.h>
 | 
			
		||||
#include <asm/asm-offsets.h>
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_SMP
 | 
			
		||||
	.comm	mmu_hash_lock,4
 | 
			
		||||
#endif /* CONFIG_SMP */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Sync CPUs with hash_page taking & releasing the hash
 | 
			
		||||
 * table lock
 | 
			
		||||
 */
 | 
			
		||||
#ifdef CONFIG_SMP
 | 
			
		||||
	.text
 | 
			
		||||
_GLOBAL(hash_page_sync)
 | 
			
		||||
	lis	r8,mmu_hash_lock@h
 | 
			
		||||
	ori	r8,r8,mmu_hash_lock@l
 | 
			
		||||
	lis	r0,0x0fff
 | 
			
		||||
	b	10f
 | 
			
		||||
11:	lwz	r6,0(r8)
 | 
			
		||||
	cmpwi	0,r6,0
 | 
			
		||||
	bne	11b
 | 
			
		||||
10:	lwarx	r6,0,r8
 | 
			
		||||
	cmpwi	0,r6,0
 | 
			
		||||
	bne-	11b
 | 
			
		||||
	stwcx.	r0,0,r8
 | 
			
		||||
	bne-	10b
 | 
			
		||||
	isync
 | 
			
		||||
	eieio
 | 
			
		||||
	li	r0,0
 | 
			
		||||
	stw	r0,0(r8)
 | 
			
		||||
	blr	
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Load a PTE into the hash table, if possible.
 | 
			
		||||
 * The address is in r4, and r3 contains an access flag:
 | 
			
		||||
 * _PAGE_RW (0x400) if a write.
 | 
			
		||||
 * r9 contains the SRR1 value, from which we use the MSR_PR bit.
 | 
			
		||||
 * SPRG3 contains the physical address of the current task's thread.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns to the caller if the access is illegal or there is no
 | 
			
		||||
 * mapping for the address.  Otherwise it places an appropriate PTE
 | 
			
		||||
 * in the hash table and returns from the exception.
 | 
			
		||||
 * Uses r0, r3 - r8, ctr, lr.
 | 
			
		||||
 */
 | 
			
		||||
	.text
 | 
			
		||||
_GLOBAL(hash_page)
 | 
			
		||||
#ifdef CONFIG_PPC64BRIDGE
 | 
			
		||||
	mfmsr	r0
 | 
			
		||||
	clrldi	r0,r0,1		/* make sure it's in 32-bit mode */
 | 
			
		||||
	MTMSRD(r0)
 | 
			
		||||
	isync
 | 
			
		||||
#endif
 | 
			
		||||
	tophys(r7,0)			/* gets -KERNELBASE into r7 */
 | 
			
		||||
#ifdef CONFIG_SMP
 | 
			
		||||
	addis	r8,r7,mmu_hash_lock@h
 | 
			
		||||
	ori	r8,r8,mmu_hash_lock@l
 | 
			
		||||
	lis	r0,0x0fff
 | 
			
		||||
	b	10f
 | 
			
		||||
11:	lwz	r6,0(r8)
 | 
			
		||||
	cmpwi	0,r6,0
 | 
			
		||||
	bne	11b
 | 
			
		||||
10:	lwarx	r6,0,r8
 | 
			
		||||
	cmpwi	0,r6,0
 | 
			
		||||
	bne-	11b
 | 
			
		||||
	stwcx.	r0,0,r8
 | 
			
		||||
	bne-	10b
 | 
			
		||||
	isync
 | 
			
		||||
#endif
 | 
			
		||||
	/* Get PTE (linux-style) and check access */
 | 
			
		||||
	lis	r0,KERNELBASE@h		/* check if kernel address */
 | 
			
		||||
	cmplw	0,r4,r0
 | 
			
		||||
	mfspr	r8,SPRN_SPRG3		/* current task's THREAD (phys) */
 | 
			
		||||
	ori	r3,r3,_PAGE_USER|_PAGE_PRESENT /* test low addresses as user */
 | 
			
		||||
	lwz	r5,PGDIR(r8)		/* virt page-table root */
 | 
			
		||||
	blt+	112f			/* assume user more likely */
 | 
			
		||||
	lis	r5,swapper_pg_dir@ha	/* if kernel address, use */
 | 
			
		||||
	addi	r5,r5,swapper_pg_dir@l	/* kernel page table */
 | 
			
		||||
	rlwimi	r3,r9,32-12,29,29	/* MSR_PR -> _PAGE_USER */
 | 
			
		||||
112:	add	r5,r5,r7		/* convert to phys addr */
 | 
			
		||||
	rlwimi	r5,r4,12,20,29		/* insert top 10 bits of address */
 | 
			
		||||
	lwz	r8,0(r5)		/* get pmd entry */
 | 
			
		||||
	rlwinm.	r8,r8,0,0,19		/* extract address of pte page */
 | 
			
		||||
#ifdef CONFIG_SMP
 | 
			
		||||
	beq-	hash_page_out		/* return if no mapping */
 | 
			
		||||
#else
 | 
			
		||||
	/* XXX it seems like the 601 will give a machine fault on the
 | 
			
		||||
	   rfi if its alignment is wrong (bottom 4 bits of address are
 | 
			
		||||
	   8 or 0xc) and we have had a not-taken conditional branch
 | 
			
		||||
	   to the address following the rfi. */
 | 
			
		||||
	beqlr-
 | 
			
		||||
#endif
 | 
			
		||||
	rlwimi	r8,r4,22,20,29		/* insert next 10 bits of address */
 | 
			
		||||
	rlwinm	r0,r3,32-3,24,24	/* _PAGE_RW access -> _PAGE_DIRTY */
 | 
			
		||||
	ori	r0,r0,_PAGE_ACCESSED|_PAGE_HASHPTE
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Update the linux PTE atomically.  We do the lwarx up-front
 | 
			
		||||
	 * because almost always, there won't be a permission violation
 | 
			
		||||
	 * and there won't already be an HPTE, and thus we will have
 | 
			
		||||
	 * to update the PTE to set _PAGE_HASHPTE.  -- paulus.
 | 
			
		||||
	 */
 | 
			
		||||
retry:
 | 
			
		||||
	lwarx	r6,0,r8			/* get linux-style pte */
 | 
			
		||||
	andc.	r5,r3,r6		/* check access & ~permission */
 | 
			
		||||
#ifdef CONFIG_SMP
 | 
			
		||||
	bne-	hash_page_out		/* return if access not permitted */
 | 
			
		||||
#else
 | 
			
		||||
	bnelr-
 | 
			
		||||
#endif
 | 
			
		||||
	or	r5,r0,r6		/* set accessed/dirty bits */
 | 
			
		||||
	stwcx.	r5,0,r8			/* attempt to update PTE */
 | 
			
		||||
	bne-	retry			/* retry if someone got there first */
 | 
			
		||||
 | 
			
		||||
	mfsrin	r3,r4			/* get segment reg for segment */
 | 
			
		||||
	mfctr	r0
 | 
			
		||||
	stw	r0,_CTR(r11)
 | 
			
		||||
	bl	create_hpte		/* add the hash table entry */
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_SMP
 | 
			
		||||
	eieio
 | 
			
		||||
	addis	r8,r7,mmu_hash_lock@ha
 | 
			
		||||
	li	r0,0
 | 
			
		||||
	stw	r0,mmu_hash_lock@l(r8)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	/* Return from the exception */
 | 
			
		||||
	lwz	r5,_CTR(r11)
 | 
			
		||||
	mtctr	r5
 | 
			
		||||
	lwz	r0,GPR0(r11)
 | 
			
		||||
	lwz	r7,GPR7(r11)
 | 
			
		||||
	lwz	r8,GPR8(r11)
 | 
			
		||||
	b	fast_exception_return
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_SMP
 | 
			
		||||
hash_page_out:
 | 
			
		||||
	eieio
 | 
			
		||||
	addis	r8,r7,mmu_hash_lock@ha
 | 
			
		||||
	li	r0,0
 | 
			
		||||
	stw	r0,mmu_hash_lock@l(r8)
 | 
			
		||||
	blr
 | 
			
		||||
#endif /* CONFIG_SMP */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Add an entry for a particular page to the hash table.
 | 
			
		||||
 *
 | 
			
		||||
 * add_hash_page(unsigned context, unsigned long va, unsigned long pmdval)
 | 
			
		||||
 *
 | 
			
		||||
 * We assume any necessary modifications to the pte (e.g. setting
 | 
			
		||||
 * the accessed bit) have already been done and that there is actually
 | 
			
		||||
 * a hash table in use (i.e. we're not on a 603).
 | 
			
		||||
 */
 | 
			
		||||
_GLOBAL(add_hash_page)
 | 
			
		||||
	mflr	r0
 | 
			
		||||
	stw	r0,4(r1)
 | 
			
		||||
 | 
			
		||||
	/* Convert context and va to VSID */
 | 
			
		||||
	mulli	r3,r3,897*16		/* multiply context by context skew */
 | 
			
		||||
	rlwinm	r0,r4,4,28,31		/* get ESID (top 4 bits of va) */
 | 
			
		||||
	mulli	r0,r0,0x111		/* multiply by ESID skew */
 | 
			
		||||
	add	r3,r3,r0		/* note create_hpte trims to 24 bits */
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_SMP
 | 
			
		||||
	rlwinm	r8,r1,0,0,18		/* use cpu number to make tag */
 | 
			
		||||
	lwz	r8,TI_CPU(r8)		/* to go in mmu_hash_lock */
 | 
			
		||||
	oris	r8,r8,12
 | 
			
		||||
#endif /* CONFIG_SMP */
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * We disable interrupts here, even on UP, because we don't
 | 
			
		||||
	 * want to race with hash_page, and because we want the
 | 
			
		||||
	 * _PAGE_HASHPTE bit to be a reliable indication of whether
 | 
			
		||||
	 * the HPTE exists (or at least whether one did once).
 | 
			
		||||
	 * We also turn off the MMU for data accesses so that we
 | 
			
		||||
	 * we can't take a hash table miss (assuming the code is
 | 
			
		||||
	 * covered by a BAT).  -- paulus
 | 
			
		||||
	 */
 | 
			
		||||
	mfmsr	r10
 | 
			
		||||
	SYNC
 | 
			
		||||
	rlwinm	r0,r10,0,17,15		/* clear bit 16 (MSR_EE) */
 | 
			
		||||
	rlwinm	r0,r0,0,28,26		/* clear MSR_DR */
 | 
			
		||||
	mtmsr	r0
 | 
			
		||||
	SYNC_601
 | 
			
		||||
	isync
 | 
			
		||||
 | 
			
		||||
	tophys(r7,0)
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_SMP
 | 
			
		||||
	addis	r9,r7,mmu_hash_lock@ha
 | 
			
		||||
	addi	r9,r9,mmu_hash_lock@l
 | 
			
		||||
10:	lwarx	r0,0,r9			/* take the mmu_hash_lock */
 | 
			
		||||
	cmpi	0,r0,0
 | 
			
		||||
	bne-	11f
 | 
			
		||||
	stwcx.	r8,0,r9
 | 
			
		||||
	beq+	12f
 | 
			
		||||
11:	lwz	r0,0(r9)
 | 
			
		||||
	cmpi	0,r0,0
 | 
			
		||||
	beq	10b
 | 
			
		||||
	b	11b
 | 
			
		||||
12:	isync
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Fetch the linux pte and test and set _PAGE_HASHPTE atomically.
 | 
			
		||||
	 * If _PAGE_HASHPTE was already set, we don't replace the existing
 | 
			
		||||
	 * HPTE, so we just unlock and return.
 | 
			
		||||
	 */
 | 
			
		||||
	mr	r8,r5
 | 
			
		||||
	rlwimi	r8,r4,22,20,29
 | 
			
		||||
1:	lwarx	r6,0,r8
 | 
			
		||||
	andi.	r0,r6,_PAGE_HASHPTE
 | 
			
		||||
	bne	9f			/* if HASHPTE already set, done */
 | 
			
		||||
	ori	r5,r6,_PAGE_HASHPTE
 | 
			
		||||
	stwcx.	r5,0,r8
 | 
			
		||||
	bne-	1b
 | 
			
		||||
 | 
			
		||||
	bl	create_hpte
 | 
			
		||||
 | 
			
		||||
9:
 | 
			
		||||
#ifdef CONFIG_SMP
 | 
			
		||||
	eieio
 | 
			
		||||
	li	r0,0
 | 
			
		||||
	stw	r0,0(r9)		/* clear mmu_hash_lock */
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	/* reenable interrupts and DR */
 | 
			
		||||
	mtmsr	r10
 | 
			
		||||
	SYNC_601
 | 
			
		||||
	isync
 | 
			
		||||
 | 
			
		||||
	lwz	r0,4(r1)
 | 
			
		||||
	mtlr	r0
 | 
			
		||||
	blr
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * This routine adds a hardware PTE to the hash table.
 | 
			
		||||
 * It is designed to be called with the MMU either on or off.
 | 
			
		||||
 * r3 contains the VSID, r4 contains the virtual address,
 | 
			
		||||
 * r5 contains the linux PTE, r6 contains the old value of the
 | 
			
		||||
 * linux PTE (before setting _PAGE_HASHPTE) and r7 contains the
 | 
			
		||||
 * offset to be added to addresses (0 if the MMU is on,
 | 
			
		||||
 * -KERNELBASE if it is off).
 | 
			
		||||
 * On SMP, the caller should have the mmu_hash_lock held.
 | 
			
		||||
 * We assume that the caller has (or will) set the _PAGE_HASHPTE
 | 
			
		||||
 * bit in the linux PTE in memory.  The value passed in r6 should
 | 
			
		||||
 * be the old linux PTE value; if it doesn't have _PAGE_HASHPTE set
 | 
			
		||||
 * this routine will skip the search for an existing HPTE.
 | 
			
		||||
 * This procedure modifies r0, r3 - r6, r8, cr0.
 | 
			
		||||
 *  -- paulus.
 | 
			
		||||
 *
 | 
			
		||||
 * For speed, 4 of the instructions get patched once the size and
 | 
			
		||||
 * physical address of the hash table are known.  These definitions
 | 
			
		||||
 * of Hash_base and Hash_bits below are just an example.
 | 
			
		||||
 */
 | 
			
		||||
Hash_base = 0xc0180000
 | 
			
		||||
Hash_bits = 12				/* e.g. 256kB hash table */
 | 
			
		||||
Hash_msk = (((1 << Hash_bits) - 1) * 64)
 | 
			
		||||
 | 
			
		||||
#ifndef CONFIG_PPC64BRIDGE
 | 
			
		||||
/* defines for the PTE format for 32-bit PPCs */
 | 
			
		||||
#define PTE_SIZE	8
 | 
			
		||||
#define PTEG_SIZE	64
 | 
			
		||||
#define LG_PTEG_SIZE	6
 | 
			
		||||
#define LDPTEu		lwzu
 | 
			
		||||
#define STPTE		stw
 | 
			
		||||
#define CMPPTE		cmpw
 | 
			
		||||
#define PTE_H		0x40
 | 
			
		||||
#define PTE_V		0x80000000
 | 
			
		||||
#define TST_V(r)	rlwinm. r,r,0,0,0
 | 
			
		||||
#define SET_V(r)	oris r,r,PTE_V@h
 | 
			
		||||
#define CLR_V(r,t)	rlwinm r,r,0,1,31
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
/* defines for the PTE format for 64-bit PPCs */
 | 
			
		||||
#define PTE_SIZE	16
 | 
			
		||||
#define PTEG_SIZE	128
 | 
			
		||||
#define LG_PTEG_SIZE	7
 | 
			
		||||
#define LDPTEu		ldu
 | 
			
		||||
#define STPTE		std
 | 
			
		||||
#define CMPPTE		cmpd
 | 
			
		||||
#define PTE_H		2
 | 
			
		||||
#define PTE_V		1
 | 
			
		||||
#define TST_V(r)	andi. r,r,PTE_V
 | 
			
		||||
#define SET_V(r)	ori r,r,PTE_V
 | 
			
		||||
#define CLR_V(r,t)	li t,PTE_V; andc r,r,t
 | 
			
		||||
#endif /* CONFIG_PPC64BRIDGE */
 | 
			
		||||
 | 
			
		||||
#define HASH_LEFT	31-(LG_PTEG_SIZE+Hash_bits-1)
 | 
			
		||||
#define HASH_RIGHT	31-LG_PTEG_SIZE
 | 
			
		||||
 | 
			
		||||
_GLOBAL(create_hpte)
 | 
			
		||||
	/* Convert linux-style PTE (r5) to low word of PPC-style PTE (r8) */
 | 
			
		||||
	rlwinm	r8,r5,32-10,31,31	/* _PAGE_RW -> PP lsb */
 | 
			
		||||
	rlwinm	r0,r5,32-7,31,31	/* _PAGE_DIRTY -> PP lsb */
 | 
			
		||||
	and	r8,r8,r0		/* writable if _RW & _DIRTY */
 | 
			
		||||
	rlwimi	r5,r5,32-1,30,30	/* _PAGE_USER -> PP msb */
 | 
			
		||||
	rlwimi	r5,r5,32-2,31,31	/* _PAGE_USER -> PP lsb */
 | 
			
		||||
	ori	r8,r8,0xe14		/* clear out reserved bits and M */
 | 
			
		||||
	andc	r8,r5,r8		/* PP = user? (rw&dirty? 2: 3): 0 */
 | 
			
		||||
BEGIN_FTR_SECTION
 | 
			
		||||
	ori	r8,r8,_PAGE_COHERENT	/* set M (coherence required) */
 | 
			
		||||
END_FTR_SECTION_IFSET(CPU_FTR_NEED_COHERENT)
 | 
			
		||||
 | 
			
		||||
	/* Construct the high word of the PPC-style PTE (r5) */
 | 
			
		||||
#ifndef CONFIG_PPC64BRIDGE
 | 
			
		||||
	rlwinm	r5,r3,7,1,24		/* put VSID in 0x7fffff80 bits */
 | 
			
		||||
	rlwimi	r5,r4,10,26,31		/* put in API (abbrev page index) */
 | 
			
		||||
#else /* CONFIG_PPC64BRIDGE */
 | 
			
		||||
	clrlwi	r3,r3,8			/* reduce vsid to 24 bits */
 | 
			
		||||
	sldi	r5,r3,12		/* shift vsid into position */
 | 
			
		||||
	rlwimi	r5,r4,16,20,24		/* put in API (abbrev page index) */
 | 
			
		||||
#endif /* CONFIG_PPC64BRIDGE */
 | 
			
		||||
	SET_V(r5)			/* set V (valid) bit */
 | 
			
		||||
 | 
			
		||||
	/* Get the address of the primary PTE group in the hash table (r3) */
 | 
			
		||||
_GLOBAL(hash_page_patch_A)
 | 
			
		||||
	addis	r0,r7,Hash_base@h	/* base address of hash table */
 | 
			
		||||
	rlwimi	r0,r3,LG_PTEG_SIZE,HASH_LEFT,HASH_RIGHT    /* VSID -> hash */
 | 
			
		||||
	rlwinm	r3,r4,20+LG_PTEG_SIZE,HASH_LEFT,HASH_RIGHT /* PI -> hash */
 | 
			
		||||
	xor	r3,r3,r0		/* make primary hash */
 | 
			
		||||
	li	r0,8			/* PTEs/group */
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Test the _PAGE_HASHPTE bit in the old linux PTE, and skip the search
 | 
			
		||||
	 * if it is clear, meaning that the HPTE isn't there already...
 | 
			
		||||
	 */
 | 
			
		||||
	andi.	r6,r6,_PAGE_HASHPTE
 | 
			
		||||
	beq+	10f			/* no PTE: go look for an empty slot */
 | 
			
		||||
	tlbie	r4
 | 
			
		||||
 | 
			
		||||
	addis	r4,r7,htab_hash_searches@ha
 | 
			
		||||
	lwz	r6,htab_hash_searches@l(r4)
 | 
			
		||||
	addi	r6,r6,1			/* count how many searches we do */
 | 
			
		||||
	stw	r6,htab_hash_searches@l(r4)
 | 
			
		||||
 | 
			
		||||
	/* Search the primary PTEG for a PTE whose 1st (d)word matches r5 */
 | 
			
		||||
	mtctr	r0
 | 
			
		||||
	addi	r4,r3,-PTE_SIZE
 | 
			
		||||
1:	LDPTEu	r6,PTE_SIZE(r4)		/* get next PTE */
 | 
			
		||||
	CMPPTE	0,r6,r5
 | 
			
		||||
	bdnzf	2,1b			/* loop while ctr != 0 && !cr0.eq */
 | 
			
		||||
	beq+	found_slot
 | 
			
		||||
 | 
			
		||||
	/* Search the secondary PTEG for a matching PTE */
 | 
			
		||||
	ori	r5,r5,PTE_H		/* set H (secondary hash) bit */
 | 
			
		||||
_GLOBAL(hash_page_patch_B)
 | 
			
		||||
	xoris	r4,r3,Hash_msk>>16	/* compute secondary hash */
 | 
			
		||||
	xori	r4,r4,(-PTEG_SIZE & 0xffff)
 | 
			
		||||
	addi	r4,r4,-PTE_SIZE
 | 
			
		||||
	mtctr	r0
 | 
			
		||||
2:	LDPTEu	r6,PTE_SIZE(r4)
 | 
			
		||||
	CMPPTE	0,r6,r5
 | 
			
		||||
	bdnzf	2,2b
 | 
			
		||||
	beq+	found_slot
 | 
			
		||||
	xori	r5,r5,PTE_H		/* clear H bit again */
 | 
			
		||||
 | 
			
		||||
	/* Search the primary PTEG for an empty slot */
 | 
			
		||||
10:	mtctr	r0
 | 
			
		||||
	addi	r4,r3,-PTE_SIZE		/* search primary PTEG */
 | 
			
		||||
1:	LDPTEu	r6,PTE_SIZE(r4)		/* get next PTE */
 | 
			
		||||
	TST_V(r6)			/* test valid bit */
 | 
			
		||||
	bdnzf	2,1b			/* loop while ctr != 0 && !cr0.eq */
 | 
			
		||||
	beq+	found_empty
 | 
			
		||||
 | 
			
		||||
	/* update counter of times that the primary PTEG is full */
 | 
			
		||||
	addis	r4,r7,primary_pteg_full@ha
 | 
			
		||||
	lwz	r6,primary_pteg_full@l(r4)
 | 
			
		||||
	addi	r6,r6,1
 | 
			
		||||
	stw	r6,primary_pteg_full@l(r4)
 | 
			
		||||
 | 
			
		||||
	/* Search the secondary PTEG for an empty slot */
 | 
			
		||||
	ori	r5,r5,PTE_H		/* set H (secondary hash) bit */
 | 
			
		||||
_GLOBAL(hash_page_patch_C)
 | 
			
		||||
	xoris	r4,r3,Hash_msk>>16	/* compute secondary hash */
 | 
			
		||||
	xori	r4,r4,(-PTEG_SIZE & 0xffff)
 | 
			
		||||
	addi	r4,r4,-PTE_SIZE
 | 
			
		||||
	mtctr	r0
 | 
			
		||||
2:	LDPTEu	r6,PTE_SIZE(r4)
 | 
			
		||||
	TST_V(r6)
 | 
			
		||||
	bdnzf	2,2b
 | 
			
		||||
	beq+	found_empty
 | 
			
		||||
	xori	r5,r5,PTE_H		/* clear H bit again */
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Choose an arbitrary slot in the primary PTEG to overwrite.
 | 
			
		||||
	 * Since both the primary and secondary PTEGs are full, and we
 | 
			
		||||
	 * have no information that the PTEs in the primary PTEG are
 | 
			
		||||
	 * more important or useful than those in the secondary PTEG,
 | 
			
		||||
	 * and we know there is a definite (although small) speed
 | 
			
		||||
	 * advantage to putting the PTE in the primary PTEG, we always
 | 
			
		||||
	 * put the PTE in the primary PTEG.
 | 
			
		||||
	 */
 | 
			
		||||
	addis	r4,r7,next_slot@ha
 | 
			
		||||
	lwz	r6,next_slot@l(r4)
 | 
			
		||||
	addi	r6,r6,PTE_SIZE
 | 
			
		||||
	andi.	r6,r6,7*PTE_SIZE
 | 
			
		||||
	stw	r6,next_slot@l(r4)
 | 
			
		||||
	add	r4,r3,r6
 | 
			
		||||
 | 
			
		||||
#ifndef CONFIG_SMP
 | 
			
		||||
	/* Store PTE in PTEG */
 | 
			
		||||
found_empty:
 | 
			
		||||
	STPTE	r5,0(r4)
 | 
			
		||||
found_slot:
 | 
			
		||||
	STPTE	r8,PTE_SIZE/2(r4)
 | 
			
		||||
 | 
			
		||||
#else /* CONFIG_SMP */
 | 
			
		||||
/*
 | 
			
		||||
 * Between the tlbie above and updating the hash table entry below,
 | 
			
		||||
 * another CPU could read the hash table entry and put it in its TLB.
 | 
			
		||||
 * There are 3 cases:
 | 
			
		||||
 * 1. using an empty slot
 | 
			
		||||
 * 2. updating an earlier entry to change permissions (i.e. enable write)
 | 
			
		||||
 * 3. taking over the PTE for an unrelated address
 | 
			
		||||
 *
 | 
			
		||||
 * In each case it doesn't really matter if the other CPUs have the old
 | 
			
		||||
 * PTE in their TLB.  So we don't need to bother with another tlbie here,
 | 
			
		||||
 * which is convenient as we've overwritten the register that had the
 | 
			
		||||
 * address. :-)  The tlbie above is mainly to make sure that this CPU comes
 | 
			
		||||
 * and gets the new PTE from the hash table.
 | 
			
		||||
 *
 | 
			
		||||
 * We do however have to make sure that the PTE is never in an invalid
 | 
			
		||||
 * state with the V bit set.
 | 
			
		||||
 */
 | 
			
		||||
found_empty:
 | 
			
		||||
found_slot:
 | 
			
		||||
	CLR_V(r5,r0)		/* clear V (valid) bit in PTE */
 | 
			
		||||
	STPTE	r5,0(r4)
 | 
			
		||||
	sync
 | 
			
		||||
	TLBSYNC
 | 
			
		||||
	STPTE	r8,PTE_SIZE/2(r4) /* put in correct RPN, WIMG, PP bits */
 | 
			
		||||
	sync
 | 
			
		||||
	SET_V(r5)
 | 
			
		||||
	STPTE	r5,0(r4)	/* finally set V bit in PTE */
 | 
			
		||||
#endif /* CONFIG_SMP */
 | 
			
		||||
 | 
			
		||||
	sync		/* make sure pte updates get to memory */
 | 
			
		||||
	blr
 | 
			
		||||
 | 
			
		||||
	.comm	next_slot,4
 | 
			
		||||
	.comm	primary_pteg_full,4
 | 
			
		||||
	.comm	htab_hash_searches,4
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Flush the entry for a particular page from the hash table.
 | 
			
		||||
 *
 | 
			
		||||
 * flush_hash_pages(unsigned context, unsigned long va, unsigned long pmdval,
 | 
			
		||||
 *		    int count)
 | 
			
		||||
 *
 | 
			
		||||
 * We assume that there is a hash table in use (Hash != 0).
 | 
			
		||||
 */
 | 
			
		||||
_GLOBAL(flush_hash_pages)
 | 
			
		||||
	tophys(r7,0)
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * We disable interrupts here, even on UP, because we want
 | 
			
		||||
	 * the _PAGE_HASHPTE bit to be a reliable indication of
 | 
			
		||||
	 * whether the HPTE exists (or at least whether one did once).
 | 
			
		||||
	 * We also turn off the MMU for data accesses so that we
 | 
			
		||||
	 * we can't take a hash table miss (assuming the code is
 | 
			
		||||
	 * covered by a BAT).  -- paulus
 | 
			
		||||
	 */
 | 
			
		||||
	mfmsr	r10
 | 
			
		||||
	SYNC
 | 
			
		||||
	rlwinm	r0,r10,0,17,15		/* clear bit 16 (MSR_EE) */
 | 
			
		||||
	rlwinm	r0,r0,0,28,26		/* clear MSR_DR */
 | 
			
		||||
	mtmsr	r0
 | 
			
		||||
	SYNC_601
 | 
			
		||||
	isync
 | 
			
		||||
 | 
			
		||||
	/* First find a PTE in the range that has _PAGE_HASHPTE set */
 | 
			
		||||
	rlwimi	r5,r4,22,20,29
 | 
			
		||||
1:	lwz	r0,0(r5)
 | 
			
		||||
	cmpwi	cr1,r6,1
 | 
			
		||||
	andi.	r0,r0,_PAGE_HASHPTE
 | 
			
		||||
	bne	2f
 | 
			
		||||
	ble	cr1,19f
 | 
			
		||||
	addi	r4,r4,0x1000
 | 
			
		||||
	addi	r5,r5,4
 | 
			
		||||
	addi	r6,r6,-1
 | 
			
		||||
	b	1b
 | 
			
		||||
 | 
			
		||||
	/* Convert context and va to VSID */
 | 
			
		||||
2:	mulli	r3,r3,897*16		/* multiply context by context skew */
 | 
			
		||||
	rlwinm	r0,r4,4,28,31		/* get ESID (top 4 bits of va) */
 | 
			
		||||
	mulli	r0,r0,0x111		/* multiply by ESID skew */
 | 
			
		||||
	add	r3,r3,r0		/* note code below trims to 24 bits */
 | 
			
		||||
 | 
			
		||||
	/* Construct the high word of the PPC-style PTE (r11) */
 | 
			
		||||
#ifndef CONFIG_PPC64BRIDGE
 | 
			
		||||
	rlwinm	r11,r3,7,1,24		/* put VSID in 0x7fffff80 bits */
 | 
			
		||||
	rlwimi	r11,r4,10,26,31		/* put in API (abbrev page index) */
 | 
			
		||||
#else /* CONFIG_PPC64BRIDGE */
 | 
			
		||||
	clrlwi	r3,r3,8			/* reduce vsid to 24 bits */
 | 
			
		||||
	sldi	r11,r3,12		/* shift vsid into position */
 | 
			
		||||
	rlwimi	r11,r4,16,20,24		/* put in API (abbrev page index) */
 | 
			
		||||
#endif /* CONFIG_PPC64BRIDGE */
 | 
			
		||||
	SET_V(r11)			/* set V (valid) bit */
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_SMP
 | 
			
		||||
	addis	r9,r7,mmu_hash_lock@ha
 | 
			
		||||
	addi	r9,r9,mmu_hash_lock@l
 | 
			
		||||
	rlwinm	r8,r1,0,0,18
 | 
			
		||||
	add	r8,r8,r7
 | 
			
		||||
	lwz	r8,TI_CPU(r8)
 | 
			
		||||
	oris	r8,r8,9
 | 
			
		||||
10:	lwarx	r0,0,r9
 | 
			
		||||
	cmpi	0,r0,0
 | 
			
		||||
	bne-	11f
 | 
			
		||||
	stwcx.	r8,0,r9
 | 
			
		||||
	beq+	12f
 | 
			
		||||
11:	lwz	r0,0(r9)
 | 
			
		||||
	cmpi	0,r0,0
 | 
			
		||||
	beq	10b
 | 
			
		||||
	b	11b
 | 
			
		||||
12:	isync
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Check the _PAGE_HASHPTE bit in the linux PTE.  If it is
 | 
			
		||||
	 * already clear, we're done (for this pte).  If not,
 | 
			
		||||
	 * clear it (atomically) and proceed.  -- paulus.
 | 
			
		||||
	 */
 | 
			
		||||
33:	lwarx	r8,0,r5			/* fetch the pte */
 | 
			
		||||
	andi.	r0,r8,_PAGE_HASHPTE
 | 
			
		||||
	beq	8f			/* done if HASHPTE is already clear */
 | 
			
		||||
	rlwinm	r8,r8,0,31,29		/* clear HASHPTE bit */
 | 
			
		||||
	stwcx.	r8,0,r5			/* update the pte */
 | 
			
		||||
	bne-	33b
 | 
			
		||||
 | 
			
		||||
	/* Get the address of the primary PTE group in the hash table (r3) */
 | 
			
		||||
_GLOBAL(flush_hash_patch_A)
 | 
			
		||||
	addis	r8,r7,Hash_base@h	/* base address of hash table */
 | 
			
		||||
	rlwimi	r8,r3,LG_PTEG_SIZE,HASH_LEFT,HASH_RIGHT    /* VSID -> hash */
 | 
			
		||||
	rlwinm	r0,r4,20+LG_PTEG_SIZE,HASH_LEFT,HASH_RIGHT /* PI -> hash */
 | 
			
		||||
	xor	r8,r0,r8		/* make primary hash */
 | 
			
		||||
 | 
			
		||||
	/* Search the primary PTEG for a PTE whose 1st (d)word matches r5 */
 | 
			
		||||
	li	r0,8			/* PTEs/group */
 | 
			
		||||
	mtctr	r0
 | 
			
		||||
	addi	r12,r8,-PTE_SIZE
 | 
			
		||||
1:	LDPTEu	r0,PTE_SIZE(r12)	/* get next PTE */
 | 
			
		||||
	CMPPTE	0,r0,r11
 | 
			
		||||
	bdnzf	2,1b			/* loop while ctr != 0 && !cr0.eq */
 | 
			
		||||
	beq+	3f
 | 
			
		||||
 | 
			
		||||
	/* Search the secondary PTEG for a matching PTE */
 | 
			
		||||
	ori	r11,r11,PTE_H		/* set H (secondary hash) bit */
 | 
			
		||||
	li	r0,8			/* PTEs/group */
 | 
			
		||||
_GLOBAL(flush_hash_patch_B)
 | 
			
		||||
	xoris	r12,r8,Hash_msk>>16	/* compute secondary hash */
 | 
			
		||||
	xori	r12,r12,(-PTEG_SIZE & 0xffff)
 | 
			
		||||
	addi	r12,r12,-PTE_SIZE
 | 
			
		||||
	mtctr	r0
 | 
			
		||||
2:	LDPTEu	r0,PTE_SIZE(r12)
 | 
			
		||||
	CMPPTE	0,r0,r11
 | 
			
		||||
	bdnzf	2,2b
 | 
			
		||||
	xori	r11,r11,PTE_H		/* clear H again */
 | 
			
		||||
	bne-	4f			/* should rarely fail to find it */
 | 
			
		||||
 | 
			
		||||
3:	li	r0,0
 | 
			
		||||
	STPTE	r0,0(r12)		/* invalidate entry */
 | 
			
		||||
4:	sync
 | 
			
		||||
	tlbie	r4			/* in hw tlb too */
 | 
			
		||||
	sync
 | 
			
		||||
 | 
			
		||||
8:	ble	cr1,9f			/* if all ptes checked */
 | 
			
		||||
81:	addi	r6,r6,-1
 | 
			
		||||
	addi	r5,r5,4			/* advance to next pte */
 | 
			
		||||
	addi	r4,r4,0x1000
 | 
			
		||||
	lwz	r0,0(r5)		/* check next pte */
 | 
			
		||||
	cmpwi	cr1,r6,1
 | 
			
		||||
	andi.	r0,r0,_PAGE_HASHPTE
 | 
			
		||||
	bne	33b
 | 
			
		||||
	bgt	cr1,81b
 | 
			
		||||
 | 
			
		||||
9:
 | 
			
		||||
#ifdef CONFIG_SMP
 | 
			
		||||
	TLBSYNC
 | 
			
		||||
	li	r0,0
 | 
			
		||||
	stw	r0,0(r9)		/* clear mmu_hash_lock */
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
19:	mtmsr	r10
 | 
			
		||||
	SYNC_601
 | 
			
		||||
	isync
 | 
			
		||||
	blr
 | 
			
		||||
							
								
								
									
										581
									
								
								arch/powerpc/mm/init.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										581
									
								
								arch/powerpc/mm/init.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,581 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  PowerPC version
 | 
			
		||||
 *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
 | 
			
		||||
 *
 | 
			
		||||
 *  Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au)
 | 
			
		||||
 *  and Cort Dougan (PReP) (cort@cs.nmt.edu)
 | 
			
		||||
 *    Copyright (C) 1996 Paul Mackerras
 | 
			
		||||
 *  Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk).
 | 
			
		||||
 *  PPC44x/36-bit changes by Matt Porter (mporter@mvista.com)
 | 
			
		||||
 *
 | 
			
		||||
 *  Derived from "arch/i386/mm/init.c"
 | 
			
		||||
 *    Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or
 | 
			
		||||
 *  modify it under the terms of the GNU General Public License
 | 
			
		||||
 *  as published by the Free Software Foundation; either version
 | 
			
		||||
 *  2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/config.h>
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/sched.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/errno.h>
 | 
			
		||||
#include <linux/string.h>
 | 
			
		||||
#include <linux/types.h>
 | 
			
		||||
#include <linux/mm.h>
 | 
			
		||||
#include <linux/stddef.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/bootmem.h>
 | 
			
		||||
#include <linux/highmem.h>
 | 
			
		||||
#include <linux/initrd.h>
 | 
			
		||||
#include <linux/pagemap.h>
 | 
			
		||||
 | 
			
		||||
#include <asm/pgalloc.h>
 | 
			
		||||
#include <asm/prom.h>
 | 
			
		||||
#include <asm/io.h>
 | 
			
		||||
#include <asm/mmu_context.h>
 | 
			
		||||
#include <asm/pgtable.h>
 | 
			
		||||
#include <asm/mmu.h>
 | 
			
		||||
#include <asm/smp.h>
 | 
			
		||||
#include <asm/machdep.h>
 | 
			
		||||
#include <asm/btext.h>
 | 
			
		||||
#include <asm/tlb.h>
 | 
			
		||||
#include <asm/bootinfo.h>
 | 
			
		||||
#include <asm/prom.h>
 | 
			
		||||
 | 
			
		||||
#include "mem_pieces.h"
 | 
			
		||||
#include "mmu_decl.h"
 | 
			
		||||
 | 
			
		||||
#if defined(CONFIG_KERNEL_START_BOOL) || defined(CONFIG_LOWMEM_SIZE_BOOL)
 | 
			
		||||
/* The ammount of lowmem must be within 0xF0000000 - KERNELBASE. */
 | 
			
		||||
#if (CONFIG_LOWMEM_SIZE > (0xF0000000 - KERNELBASE))
 | 
			
		||||
#error "You must adjust CONFIG_LOWMEM_SIZE or CONFIG_START_KERNEL"
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
#define MAX_LOW_MEM	CONFIG_LOWMEM_SIZE
 | 
			
		||||
 | 
			
		||||
DEFINE_PER_CPU(struct mmu_gather, mmu_gathers);
 | 
			
		||||
 | 
			
		||||
unsigned long total_memory;
 | 
			
		||||
unsigned long total_lowmem;
 | 
			
		||||
 | 
			
		||||
unsigned long ppc_memstart;
 | 
			
		||||
unsigned long ppc_memoffset = PAGE_OFFSET;
 | 
			
		||||
 | 
			
		||||
int mem_init_done;
 | 
			
		||||
int init_bootmem_done;
 | 
			
		||||
int boot_mapsize;
 | 
			
		||||
#ifdef CONFIG_PPC_PMAC
 | 
			
		||||
unsigned long agp_special_page;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
extern char _end[];
 | 
			
		||||
extern char etext[], _stext[];
 | 
			
		||||
extern char __init_begin, __init_end;
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_HIGHMEM
 | 
			
		||||
pte_t *kmap_pte;
 | 
			
		||||
pgprot_t kmap_prot;
 | 
			
		||||
 | 
			
		||||
EXPORT_SYMBOL(kmap_prot);
 | 
			
		||||
EXPORT_SYMBOL(kmap_pte);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void MMU_init(void);
 | 
			
		||||
void set_phys_avail(unsigned long total_ram);
 | 
			
		||||
 | 
			
		||||
/* XXX should be in current.h  -- paulus */
 | 
			
		||||
extern struct task_struct *current_set[NR_CPUS];
 | 
			
		||||
 | 
			
		||||
char *klimit = _end;
 | 
			
		||||
struct mem_pieces phys_avail;
 | 
			
		||||
struct device_node *memory_node;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * this tells the system to map all of ram with the segregs
 | 
			
		||||
 * (i.e. page tables) instead of the bats.
 | 
			
		||||
 * -- Cort
 | 
			
		||||
 */
 | 
			
		||||
int __map_without_bats;
 | 
			
		||||
int __map_without_ltlbs;
 | 
			
		||||
 | 
			
		||||
/* max amount of RAM to use */
 | 
			
		||||
unsigned long __max_memory;
 | 
			
		||||
/* max amount of low RAM to map in */
 | 
			
		||||
unsigned long __max_low_memory = MAX_LOW_MEM;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Read in a property describing some pieces of memory.
 | 
			
		||||
 */
 | 
			
		||||
static int __init get_mem_prop(char *name, struct mem_pieces *mp)
 | 
			
		||||
{
 | 
			
		||||
	struct reg_property *rp;
 | 
			
		||||
	int i, s;
 | 
			
		||||
	unsigned int *ip;
 | 
			
		||||
	int nac = prom_n_addr_cells(memory_node);
 | 
			
		||||
	int nsc = prom_n_size_cells(memory_node);
 | 
			
		||||
 | 
			
		||||
	ip = (unsigned int *) get_property(memory_node, name, &s);
 | 
			
		||||
	if (ip == NULL) {
 | 
			
		||||
		printk(KERN_ERR "error: couldn't get %s property on /memory\n",
 | 
			
		||||
		       name);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	s /= (nsc + nac) * 4;
 | 
			
		||||
	rp = mp->regions;
 | 
			
		||||
	for (i = 0; i < s; ++i, ip += nac+nsc) {
 | 
			
		||||
		if (nac >= 2 && ip[nac-2] != 0)
 | 
			
		||||
			continue;
 | 
			
		||||
		rp->address = ip[nac-1];
 | 
			
		||||
		if (nsc >= 2 && ip[nac+nsc-2] != 0)
 | 
			
		||||
			rp->size = ~0U;
 | 
			
		||||
		else
 | 
			
		||||
			rp->size = ip[nac+nsc-1];
 | 
			
		||||
		++rp;
 | 
			
		||||
	}
 | 
			
		||||
	mp->n_regions = rp - mp->regions;
 | 
			
		||||
 | 
			
		||||
	/* Make sure the pieces are sorted. */
 | 
			
		||||
	mem_pieces_sort(mp);
 | 
			
		||||
	mem_pieces_coalesce(mp);
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Collect information about physical RAM and which pieces are
 | 
			
		||||
 * already in use from the device tree.
 | 
			
		||||
 */
 | 
			
		||||
unsigned long __init find_end_of_memory(void)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long a, total;
 | 
			
		||||
	struct mem_pieces phys_mem;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Find out where physical memory is, and check that it
 | 
			
		||||
	 * starts at 0 and is contiguous.  It seems that RAM is
 | 
			
		||||
	 * always physically contiguous on Power Macintoshes.
 | 
			
		||||
	 *
 | 
			
		||||
	 * Supporting discontiguous physical memory isn't hard,
 | 
			
		||||
	 * it just makes the virtual <-> physical mapping functions
 | 
			
		||||
	 * more complicated (or else you end up wasting space
 | 
			
		||||
	 * in mem_map).
 | 
			
		||||
	 */
 | 
			
		||||
	memory_node = find_devices("memory");
 | 
			
		||||
	if (memory_node == NULL || !get_mem_prop("reg", &phys_mem)
 | 
			
		||||
	    || phys_mem.n_regions == 0)
 | 
			
		||||
		panic("No RAM??");
 | 
			
		||||
	a = phys_mem.regions[0].address;
 | 
			
		||||
	if (a != 0)
 | 
			
		||||
		panic("RAM doesn't start at physical address 0");
 | 
			
		||||
	total = phys_mem.regions[0].size;
 | 
			
		||||
 | 
			
		||||
	if (phys_mem.n_regions > 1) {
 | 
			
		||||
		printk("RAM starting at 0x%x is not contiguous\n",
 | 
			
		||||
		       phys_mem.regions[1].address);
 | 
			
		||||
		printk("Using RAM from 0 to 0x%lx\n", total-1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return total;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Check for command-line options that affect what MMU_init will do.
 | 
			
		||||
 */
 | 
			
		||||
void MMU_setup(void)
 | 
			
		||||
{
 | 
			
		||||
	/* Check for nobats option (used in mapin_ram). */
 | 
			
		||||
	if (strstr(cmd_line, "nobats")) {
 | 
			
		||||
		__map_without_bats = 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (strstr(cmd_line, "noltlbs")) {
 | 
			
		||||
		__map_without_ltlbs = 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Look for mem= option on command line */
 | 
			
		||||
	if (strstr(cmd_line, "mem=")) {
 | 
			
		||||
		char *p, *q;
 | 
			
		||||
		unsigned long maxmem = 0;
 | 
			
		||||
 | 
			
		||||
		for (q = cmd_line; (p = strstr(q, "mem=")) != 0; ) {
 | 
			
		||||
			q = p + 4;
 | 
			
		||||
			if (p > cmd_line && p[-1] != ' ')
 | 
			
		||||
				continue;
 | 
			
		||||
			maxmem = simple_strtoul(q, &q, 0);
 | 
			
		||||
			if (*q == 'k' || *q == 'K') {
 | 
			
		||||
				maxmem <<= 10;
 | 
			
		||||
				++q;
 | 
			
		||||
			} else if (*q == 'm' || *q == 'M') {
 | 
			
		||||
				maxmem <<= 20;
 | 
			
		||||
				++q;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		__max_memory = maxmem;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * MMU_init sets up the basic memory mappings for the kernel,
 | 
			
		||||
 * including both RAM and possibly some I/O regions,
 | 
			
		||||
 * and sets up the page tables and the MMU hardware ready to go.
 | 
			
		||||
 */
 | 
			
		||||
void __init MMU_init(void)
 | 
			
		||||
{
 | 
			
		||||
	if (ppc_md.progress)
 | 
			
		||||
		ppc_md.progress("MMU:enter", 0x111);
 | 
			
		||||
 | 
			
		||||
	/* parse args from command line */
 | 
			
		||||
	MMU_setup();
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Figure out how much memory we have, how much
 | 
			
		||||
	 * is lowmem, and how much is highmem.  If we were
 | 
			
		||||
	 * passed the total memory size from the bootloader,
 | 
			
		||||
	 * just use it.
 | 
			
		||||
	 */
 | 
			
		||||
	if (boot_mem_size)
 | 
			
		||||
		total_memory = boot_mem_size;
 | 
			
		||||
	else
 | 
			
		||||
		total_memory = ppc_md.find_end_of_memory();
 | 
			
		||||
 | 
			
		||||
	if (__max_memory && total_memory > __max_memory)
 | 
			
		||||
		total_memory = __max_memory;
 | 
			
		||||
	total_lowmem = total_memory;
 | 
			
		||||
#ifdef CONFIG_FSL_BOOKE
 | 
			
		||||
	/* Freescale Book-E parts expect lowmem to be mapped by fixed TLB
 | 
			
		||||
	 * entries, so we need to adjust lowmem to match the amount we can map
 | 
			
		||||
	 * in the fixed entries */
 | 
			
		||||
	adjust_total_lowmem();
 | 
			
		||||
#endif /* CONFIG_FSL_BOOKE */
 | 
			
		||||
	if (total_lowmem > __max_low_memory) {
 | 
			
		||||
		total_lowmem = __max_low_memory;
 | 
			
		||||
#ifndef CONFIG_HIGHMEM
 | 
			
		||||
		total_memory = total_lowmem;
 | 
			
		||||
#endif /* CONFIG_HIGHMEM */
 | 
			
		||||
	}
 | 
			
		||||
	set_phys_avail(total_lowmem);
 | 
			
		||||
 | 
			
		||||
	/* Initialize the MMU hardware */
 | 
			
		||||
	if (ppc_md.progress)
 | 
			
		||||
		ppc_md.progress("MMU:hw init", 0x300);
 | 
			
		||||
	MMU_init_hw();
 | 
			
		||||
 | 
			
		||||
	/* Map in all of RAM starting at KERNELBASE */
 | 
			
		||||
	if (ppc_md.progress)
 | 
			
		||||
		ppc_md.progress("MMU:mapin", 0x301);
 | 
			
		||||
	mapin_ram();
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_HIGHMEM
 | 
			
		||||
	ioremap_base = PKMAP_BASE;
 | 
			
		||||
#else
 | 
			
		||||
	ioremap_base = 0xfe000000UL;	/* for now, could be 0xfffff000 */
 | 
			
		||||
#endif /* CONFIG_HIGHMEM */
 | 
			
		||||
	ioremap_bot = ioremap_base;
 | 
			
		||||
 | 
			
		||||
	/* Map in I/O resources */
 | 
			
		||||
	if (ppc_md.progress)
 | 
			
		||||
		ppc_md.progress("MMU:setio", 0x302);
 | 
			
		||||
	if (ppc_md.setup_io_mappings)
 | 
			
		||||
		ppc_md.setup_io_mappings();
 | 
			
		||||
 | 
			
		||||
	/* Initialize the context management stuff */
 | 
			
		||||
	mmu_context_init();
 | 
			
		||||
 | 
			
		||||
	if (ppc_md.progress)
 | 
			
		||||
		ppc_md.progress("MMU:exit", 0x211);
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_BOOTX_TEXT
 | 
			
		||||
	/* By default, we are no longer mapped */
 | 
			
		||||
       	boot_text_mapped = 0;
 | 
			
		||||
	/* Must be done last, or ppc_md.progress will die. */
 | 
			
		||||
	map_boot_text();
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* This is only called until mem_init is done. */
 | 
			
		||||
void __init *early_get_page(void)
 | 
			
		||||
{
 | 
			
		||||
	void *p;
 | 
			
		||||
 | 
			
		||||
	if (init_bootmem_done) {
 | 
			
		||||
		p = alloc_bootmem_pages(PAGE_SIZE);
 | 
			
		||||
	} else {
 | 
			
		||||
		p = mem_pieces_find(PAGE_SIZE, PAGE_SIZE);
 | 
			
		||||
	}
 | 
			
		||||
	return p;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Free up now-unused memory */
 | 
			
		||||
static void free_sec(unsigned long start, unsigned long end, const char *name)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long cnt = 0;
 | 
			
		||||
 | 
			
		||||
	while (start < end) {
 | 
			
		||||
		ClearPageReserved(virt_to_page(start));
 | 
			
		||||
		set_page_count(virt_to_page(start), 1);
 | 
			
		||||
		free_page(start);
 | 
			
		||||
		cnt++;
 | 
			
		||||
		start += PAGE_SIZE;
 | 
			
		||||
 	}
 | 
			
		||||
	if (cnt) {
 | 
			
		||||
		printk(" %ldk %s", cnt << (PAGE_SHIFT - 10), name);
 | 
			
		||||
		totalram_pages += cnt;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void free_initmem(void)
 | 
			
		||||
{
 | 
			
		||||
#define FREESEC(TYPE) \
 | 
			
		||||
	free_sec((unsigned long)(&__ ## TYPE ## _begin), \
 | 
			
		||||
		 (unsigned long)(&__ ## TYPE ## _end), \
 | 
			
		||||
		 #TYPE);
 | 
			
		||||
 | 
			
		||||
	printk ("Freeing unused kernel memory:");
 | 
			
		||||
	FREESEC(init);
 | 
			
		||||
 	printk("\n");
 | 
			
		||||
	ppc_md.progress = NULL;
 | 
			
		||||
#undef FREESEC
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#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", (end - start) >> 10);
 | 
			
		||||
	for (; start < end; start += PAGE_SIZE) {
 | 
			
		||||
		ClearPageReserved(virt_to_page(start));
 | 
			
		||||
		set_page_count(virt_to_page(start), 1);
 | 
			
		||||
		free_page(start);
 | 
			
		||||
		totalram_pages++;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Initialize the bootmem system and give it all the memory we
 | 
			
		||||
 * have available.
 | 
			
		||||
 */
 | 
			
		||||
void __init do_init_bootmem(void)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long start, size;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Find an area to use for the bootmem bitmap.
 | 
			
		||||
	 * We look for the first area which is at least
 | 
			
		||||
	 * 128kB in length (128kB is enough for a bitmap
 | 
			
		||||
	 * for 4GB of memory, using 4kB pages), plus 1 page
 | 
			
		||||
	 * (in case the address isn't page-aligned).
 | 
			
		||||
	 */
 | 
			
		||||
	start = 0;
 | 
			
		||||
	size = 0;
 | 
			
		||||
	for (i = 0; i < phys_avail.n_regions; ++i) {
 | 
			
		||||
		unsigned long a = phys_avail.regions[i].address;
 | 
			
		||||
		unsigned long s = phys_avail.regions[i].size;
 | 
			
		||||
		if (s <= size)
 | 
			
		||||
			continue;
 | 
			
		||||
		start = a;
 | 
			
		||||
		size = s;
 | 
			
		||||
		if (s >= 33 * PAGE_SIZE)
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
	start = PAGE_ALIGN(start);
 | 
			
		||||
 | 
			
		||||
	min_low_pfn = start >> PAGE_SHIFT;
 | 
			
		||||
	max_low_pfn = (PPC_MEMSTART + total_lowmem) >> PAGE_SHIFT;
 | 
			
		||||
	max_pfn = (PPC_MEMSTART + total_memory) >> PAGE_SHIFT;
 | 
			
		||||
	boot_mapsize = init_bootmem_node(&contig_page_data, min_low_pfn,
 | 
			
		||||
					 PPC_MEMSTART >> PAGE_SHIFT,
 | 
			
		||||
					 max_low_pfn);
 | 
			
		||||
 | 
			
		||||
	/* remove the bootmem bitmap from the available memory */
 | 
			
		||||
	mem_pieces_remove(&phys_avail, start, boot_mapsize, 1);
 | 
			
		||||
 | 
			
		||||
	/* add everything in phys_avail into the bootmem map */
 | 
			
		||||
	for (i = 0; i < phys_avail.n_regions; ++i)
 | 
			
		||||
		free_bootmem(phys_avail.regions[i].address,
 | 
			
		||||
			     phys_avail.regions[i].size);
 | 
			
		||||
 | 
			
		||||
	init_bootmem_done = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * paging_init() sets up the page tables - in fact we've already done this.
 | 
			
		||||
 */
 | 
			
		||||
void __init paging_init(void)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long zones_size[MAX_NR_ZONES], i;
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_HIGHMEM
 | 
			
		||||
	map_page(PKMAP_BASE, 0, 0);	/* XXX gross */
 | 
			
		||||
	pkmap_page_table = pte_offset_kernel(pmd_offset(pgd_offset_k
 | 
			
		||||
			(PKMAP_BASE), PKMAP_BASE), PKMAP_BASE);
 | 
			
		||||
	map_page(KMAP_FIX_BEGIN, 0, 0);	/* XXX gross */
 | 
			
		||||
	kmap_pte = pte_offset_kernel(pmd_offset(pgd_offset_k
 | 
			
		||||
			(KMAP_FIX_BEGIN), KMAP_FIX_BEGIN), KMAP_FIX_BEGIN);
 | 
			
		||||
	kmap_prot = PAGE_KERNEL;
 | 
			
		||||
#endif /* CONFIG_HIGHMEM */
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * All pages are DMA-able so we put them all in the DMA zone.
 | 
			
		||||
	 */
 | 
			
		||||
	zones_size[ZONE_DMA] = total_lowmem >> PAGE_SHIFT;
 | 
			
		||||
	for (i = 1; i < MAX_NR_ZONES; i++)
 | 
			
		||||
		zones_size[i] = 0;
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_HIGHMEM
 | 
			
		||||
	zones_size[ZONE_HIGHMEM] = (total_memory - total_lowmem) >> PAGE_SHIFT;
 | 
			
		||||
#endif /* CONFIG_HIGHMEM */
 | 
			
		||||
 | 
			
		||||
	free_area_init(zones_size);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void __init mem_init(void)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long addr;
 | 
			
		||||
	int codepages = 0;
 | 
			
		||||
	int datapages = 0;
 | 
			
		||||
	int initpages = 0;
 | 
			
		||||
#ifdef CONFIG_HIGHMEM
 | 
			
		||||
	unsigned long highmem_mapnr;
 | 
			
		||||
 | 
			
		||||
	highmem_mapnr = total_lowmem >> PAGE_SHIFT;
 | 
			
		||||
#endif /* CONFIG_HIGHMEM */
 | 
			
		||||
	max_mapnr = total_memory >> PAGE_SHIFT;
 | 
			
		||||
 | 
			
		||||
	high_memory = (void *) __va(PPC_MEMSTART + total_lowmem);
 | 
			
		||||
	num_physpages = max_mapnr;	/* RAM is assumed contiguous */
 | 
			
		||||
 | 
			
		||||
	totalram_pages += free_all_bootmem();
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_BLK_DEV_INITRD
 | 
			
		||||
	/* if we are booted from BootX with an initial ramdisk,
 | 
			
		||||
	   make sure the ramdisk pages aren't reserved. */
 | 
			
		||||
	if (initrd_start) {
 | 
			
		||||
		for (addr = initrd_start; addr < initrd_end; addr += PAGE_SIZE)
 | 
			
		||||
			ClearPageReserved(virt_to_page(addr));
 | 
			
		||||
	}
 | 
			
		||||
#endif /* CONFIG_BLK_DEV_INITRD */
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_PPC_OF
 | 
			
		||||
	/* mark the RTAS pages as reserved */
 | 
			
		||||
	if ( rtas_data )
 | 
			
		||||
		for (addr = (ulong)__va(rtas_data);
 | 
			
		||||
		     addr < PAGE_ALIGN((ulong)__va(rtas_data)+rtas_size) ;
 | 
			
		||||
		     addr += PAGE_SIZE)
 | 
			
		||||
			SetPageReserved(virt_to_page(addr));
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef CONFIG_PPC_PMAC
 | 
			
		||||
	if (agp_special_page)
 | 
			
		||||
		SetPageReserved(virt_to_page(agp_special_page));
 | 
			
		||||
#endif
 | 
			
		||||
	for (addr = PAGE_OFFSET; addr < (unsigned long)high_memory;
 | 
			
		||||
	     addr += PAGE_SIZE) {
 | 
			
		||||
		if (!PageReserved(virt_to_page(addr)))
 | 
			
		||||
			continue;
 | 
			
		||||
		if (addr < (ulong) etext)
 | 
			
		||||
			codepages++;
 | 
			
		||||
		else if (addr >= (unsigned long)&__init_begin
 | 
			
		||||
			 && addr < (unsigned long)&__init_end)
 | 
			
		||||
			initpages++;
 | 
			
		||||
		else if (addr < (ulong) klimit)
 | 
			
		||||
			datapages++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_HIGHMEM
 | 
			
		||||
	{
 | 
			
		||||
		unsigned long pfn;
 | 
			
		||||
 | 
			
		||||
		for (pfn = highmem_mapnr; pfn < max_mapnr; ++pfn) {
 | 
			
		||||
			struct page *page = mem_map + pfn;
 | 
			
		||||
 | 
			
		||||
			ClearPageReserved(page);
 | 
			
		||||
			set_page_count(page, 1);
 | 
			
		||||
			__free_page(page);
 | 
			
		||||
			totalhigh_pages++;
 | 
			
		||||
		}
 | 
			
		||||
		totalram_pages += totalhigh_pages;
 | 
			
		||||
	}
 | 
			
		||||
#endif /* CONFIG_HIGHMEM */
 | 
			
		||||
 | 
			
		||||
        printk("Memory: %luk available (%dk kernel code, %dk data, %dk init, %ldk highmem)\n",
 | 
			
		||||
	       (unsigned long)nr_free_pages()<< (PAGE_SHIFT-10),
 | 
			
		||||
	       codepages<< (PAGE_SHIFT-10), datapages<< (PAGE_SHIFT-10),
 | 
			
		||||
	       initpages<< (PAGE_SHIFT-10),
 | 
			
		||||
	       (unsigned long) (totalhigh_pages << (PAGE_SHIFT-10)));
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_PPC_PMAC
 | 
			
		||||
	if (agp_special_page)
 | 
			
		||||
		printk(KERN_INFO "AGP special page: 0x%08lx\n", agp_special_page);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	mem_init_done = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Set phys_avail to the amount of physical memory,
 | 
			
		||||
 * less the kernel text/data/bss.
 | 
			
		||||
 */
 | 
			
		||||
void __init
 | 
			
		||||
set_phys_avail(unsigned long total_memory)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long kstart, ksize;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Initially, available physical memory is equivalent to all
 | 
			
		||||
	 * physical memory.
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
	phys_avail.regions[0].address = PPC_MEMSTART;
 | 
			
		||||
	phys_avail.regions[0].size = total_memory;
 | 
			
		||||
	phys_avail.n_regions = 1;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Map out the kernel text/data/bss from the available physical
 | 
			
		||||
	 * memory.
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
	kstart = __pa(_stext);	/* should be 0 */
 | 
			
		||||
	ksize = PAGE_ALIGN(klimit - _stext);
 | 
			
		||||
 | 
			
		||||
	mem_pieces_remove(&phys_avail, kstart, ksize, 0);
 | 
			
		||||
	mem_pieces_remove(&phys_avail, 0, 0x4000, 0);
 | 
			
		||||
 | 
			
		||||
#if defined(CONFIG_BLK_DEV_INITRD)
 | 
			
		||||
	/* Remove the init RAM disk from the available memory. */
 | 
			
		||||
	if (initrd_start) {
 | 
			
		||||
		mem_pieces_remove(&phys_avail, __pa(initrd_start),
 | 
			
		||||
				  initrd_end - initrd_start, 1);
 | 
			
		||||
	}
 | 
			
		||||
#endif /* CONFIG_BLK_DEV_INITRD */
 | 
			
		||||
#ifdef CONFIG_PPC_OF
 | 
			
		||||
	/* remove the RTAS pages from the available memory */
 | 
			
		||||
	if (rtas_data)
 | 
			
		||||
		mem_pieces_remove(&phys_avail, rtas_data, rtas_size, 1);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef CONFIG_PPC_PMAC
 | 
			
		||||
	/* Because of some uninorth weirdness, we need a page of
 | 
			
		||||
	 * memory as high as possible (it must be outside of the
 | 
			
		||||
	 * bus address seen as the AGP aperture). It will be used
 | 
			
		||||
	 * by the r128 DRM driver
 | 
			
		||||
	 *
 | 
			
		||||
	 * FIXME: We need to make sure that page doesn't overlap any of the\
 | 
			
		||||
	 * above. This could be done by improving mem_pieces_find to be able
 | 
			
		||||
	 * to do a backward search from the end of the list.
 | 
			
		||||
	 */
 | 
			
		||||
	if (_machine == _MACH_Pmac && find_devices("uni-north-agp")) {
 | 
			
		||||
		agp_special_page = (total_memory - PAGE_SIZE);
 | 
			
		||||
		mem_pieces_remove(&phys_avail, agp_special_page, PAGE_SIZE, 0);
 | 
			
		||||
		agp_special_page = (unsigned long)__va(agp_special_page);
 | 
			
		||||
	}
 | 
			
		||||
#endif /* CONFIG_PPC_PMAC */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Mark some memory as reserved by removing it from phys_avail. */
 | 
			
		||||
void __init reserve_phys_mem(unsigned long start, unsigned long size)
 | 
			
		||||
{
 | 
			
		||||
	mem_pieces_remove(&phys_avail, start, size, 1);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										385
									
								
								arch/powerpc/mm/init64.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										385
									
								
								arch/powerpc/mm/init64.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,385 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  PowerPC version
 | 
			
		||||
 *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
 | 
			
		||||
 *
 | 
			
		||||
 *  Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au)
 | 
			
		||||
 *  and Cort Dougan (PReP) (cort@cs.nmt.edu)
 | 
			
		||||
 *    Copyright (C) 1996 Paul Mackerras
 | 
			
		||||
 *  Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk).
 | 
			
		||||
 *
 | 
			
		||||
 *  Derived from "arch/i386/mm/init.c"
 | 
			
		||||
 *    Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
 | 
			
		||||
 *
 | 
			
		||||
 *  Dave Engebretsen <engebret@us.ibm.com>
 | 
			
		||||
 *      Rework for PPC64 port.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or
 | 
			
		||||
 *  modify it under the terms of the GNU General Public License
 | 
			
		||||
 *  as published by the Free Software Foundation; either version
 | 
			
		||||
 *  2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/config.h>
 | 
			
		||||
#include <linux/signal.h>
 | 
			
		||||
#include <linux/sched.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/errno.h>
 | 
			
		||||
#include <linux/string.h>
 | 
			
		||||
#include <linux/types.h>
 | 
			
		||||
#include <linux/mman.h>
 | 
			
		||||
#include <linux/mm.h>
 | 
			
		||||
#include <linux/swap.h>
 | 
			
		||||
#include <linux/stddef.h>
 | 
			
		||||
#include <linux/vmalloc.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/delay.h>
 | 
			
		||||
#include <linux/bootmem.h>
 | 
			
		||||
#include <linux/highmem.h>
 | 
			
		||||
#include <linux/idr.h>
 | 
			
		||||
#include <linux/nodemask.h>
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
 | 
			
		||||
#include <asm/pgalloc.h>
 | 
			
		||||
#include <asm/page.h>
 | 
			
		||||
#include <asm/prom.h>
 | 
			
		||||
#include <asm/lmb.h>
 | 
			
		||||
#include <asm/rtas.h>
 | 
			
		||||
#include <asm/io.h>
 | 
			
		||||
#include <asm/mmu_context.h>
 | 
			
		||||
#include <asm/pgtable.h>
 | 
			
		||||
#include <asm/mmu.h>
 | 
			
		||||
#include <asm/uaccess.h>
 | 
			
		||||
#include <asm/smp.h>
 | 
			
		||||
#include <asm/machdep.h>
 | 
			
		||||
#include <asm/tlb.h>
 | 
			
		||||
#include <asm/eeh.h>
 | 
			
		||||
#include <asm/processor.h>
 | 
			
		||||
#include <asm/mmzone.h>
 | 
			
		||||
#include <asm/cputable.h>
 | 
			
		||||
#include <asm/ppcdebug.h>
 | 
			
		||||
#include <asm/sections.h>
 | 
			
		||||
#include <asm/system.h>
 | 
			
		||||
#include <asm/iommu.h>
 | 
			
		||||
#include <asm/abs_addr.h>
 | 
			
		||||
#include <asm/vdso.h>
 | 
			
		||||
#include <asm/imalloc.h>
 | 
			
		||||
 | 
			
		||||
#if PGTABLE_RANGE > USER_VSID_RANGE
 | 
			
		||||
#warning Limited user VSID range means pagetable space is wasted
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if (TASK_SIZE_USER64 < PGTABLE_RANGE) && (TASK_SIZE_USER64 < USER_VSID_RANGE)
 | 
			
		||||
#warning TASK_SIZE is smaller than it needs to be.
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
int mem_init_done;
 | 
			
		||||
unsigned long ioremap_bot = IMALLOC_BASE;
 | 
			
		||||
static unsigned long phbs_io_bot = PHBS_IO_BASE;
 | 
			
		||||
 | 
			
		||||
extern pgd_t swapper_pg_dir[];
 | 
			
		||||
extern struct task_struct *current_set[NR_CPUS];
 | 
			
		||||
 | 
			
		||||
unsigned long klimit = (unsigned long)_end;
 | 
			
		||||
 | 
			
		||||
unsigned long _SDR1=0;
 | 
			
		||||
unsigned long _ASR=0;
 | 
			
		||||
 | 
			
		||||
/* max amount of RAM to use */
 | 
			
		||||
unsigned long __max_memory;
 | 
			
		||||
 | 
			
		||||
/* info on what we think the IO hole is */
 | 
			
		||||
unsigned long 	io_hole_start;
 | 
			
		||||
unsigned long	io_hole_size;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Do very early mm setup.
 | 
			
		||||
 */
 | 
			
		||||
void __init mm_init_ppc64(void)
 | 
			
		||||
{
 | 
			
		||||
#ifndef CONFIG_PPC_ISERIES
 | 
			
		||||
	unsigned long i;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	ppc64_boot_msg(0x100, "MM Init");
 | 
			
		||||
 | 
			
		||||
	/* This is the story of the IO hole... please, keep seated,
 | 
			
		||||
	 * unfortunately, we are out of oxygen masks at the moment.
 | 
			
		||||
	 * So we need some rough way to tell where your big IO hole
 | 
			
		||||
	 * is. On pmac, it's between 2G and 4G, on POWER3, it's around
 | 
			
		||||
	 * that area as well, on POWER4 we don't have one, etc...
 | 
			
		||||
	 * We need that as a "hint" when sizing the TCE table on POWER3
 | 
			
		||||
	 * So far, the simplest way that seem work well enough for us it
 | 
			
		||||
	 * to just assume that the first discontinuity in our physical
 | 
			
		||||
	 * RAM layout is the IO hole. That may not be correct in the future
 | 
			
		||||
	 * (and isn't on iSeries but then we don't care ;)
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
#ifndef CONFIG_PPC_ISERIES
 | 
			
		||||
	for (i = 1; i < lmb.memory.cnt; i++) {
 | 
			
		||||
		unsigned long base, prevbase, prevsize;
 | 
			
		||||
 | 
			
		||||
		prevbase = lmb.memory.region[i-1].base;
 | 
			
		||||
		prevsize = lmb.memory.region[i-1].size;
 | 
			
		||||
		base = lmb.memory.region[i].base;
 | 
			
		||||
		if (base > (prevbase + prevsize)) {
 | 
			
		||||
			io_hole_start = prevbase + prevsize;
 | 
			
		||||
			io_hole_size = base  - (prevbase + prevsize);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
#endif /* CONFIG_PPC_ISERIES */
 | 
			
		||||
	if (io_hole_start)
 | 
			
		||||
		printk("IO Hole assumed to be %lx -> %lx\n",
 | 
			
		||||
		       io_hole_start, io_hole_start + io_hole_size - 1);
 | 
			
		||||
 | 
			
		||||
	ppc64_boot_msg(0x100, "MM Init Done");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void free_initmem(void)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long addr;
 | 
			
		||||
 | 
			
		||||
	addr = (unsigned long)__init_begin;
 | 
			
		||||
	for (; addr < (unsigned long)__init_end; addr += PAGE_SIZE) {
 | 
			
		||||
		memset((void *)addr, 0xcc, PAGE_SIZE);
 | 
			
		||||
		ClearPageReserved(virt_to_page(addr));
 | 
			
		||||
		set_page_count(virt_to_page(addr), 1);
 | 
			
		||||
		free_page(addr);
 | 
			
		||||
		totalram_pages++;
 | 
			
		||||
	}
 | 
			
		||||
	printk ("Freeing unused kernel memory: %luk freed\n",
 | 
			
		||||
		((unsigned long)__init_end - (unsigned long)__init_begin) >> 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", (end - start) >> 10);
 | 
			
		||||
	for (; start < end; start += PAGE_SIZE) {
 | 
			
		||||
		ClearPageReserved(virt_to_page(start));
 | 
			
		||||
		set_page_count(virt_to_page(start), 1);
 | 
			
		||||
		free_page(start);
 | 
			
		||||
		totalram_pages++;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Initialize the bootmem system and give it all the memory we
 | 
			
		||||
 * have available.
 | 
			
		||||
 */
 | 
			
		||||
#ifndef CONFIG_NEED_MULTIPLE_NODES
 | 
			
		||||
void __init do_init_bootmem(void)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long i;
 | 
			
		||||
	unsigned long start, bootmap_pages;
 | 
			
		||||
	unsigned long total_pages = lmb_end_of_DRAM() >> PAGE_SHIFT;
 | 
			
		||||
	int boot_mapsize;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Find an area to use for the bootmem bitmap.  Calculate the size of
 | 
			
		||||
	 * bitmap required as (Total Memory) / PAGE_SIZE / BITS_PER_BYTE.
 | 
			
		||||
	 * Add 1 additional page in case the address isn't page-aligned.
 | 
			
		||||
	 */
 | 
			
		||||
	bootmap_pages = bootmem_bootmap_pages(total_pages);
 | 
			
		||||
 | 
			
		||||
	start = lmb_alloc(bootmap_pages<<PAGE_SHIFT, PAGE_SIZE);
 | 
			
		||||
	BUG_ON(!start);
 | 
			
		||||
 | 
			
		||||
	boot_mapsize = init_bootmem(start >> PAGE_SHIFT, total_pages);
 | 
			
		||||
 | 
			
		||||
	max_pfn = max_low_pfn;
 | 
			
		||||
 | 
			
		||||
	/* Add all physical memory to the bootmem map, mark each area
 | 
			
		||||
	 * present.
 | 
			
		||||
	 */
 | 
			
		||||
	for (i=0; i < lmb.memory.cnt; i++)
 | 
			
		||||
		free_bootmem(lmb.memory.region[i].base,
 | 
			
		||||
			     lmb_size_bytes(&lmb.memory, i));
 | 
			
		||||
 | 
			
		||||
	/* reserve the sections we're already using */
 | 
			
		||||
	for (i=0; i < lmb.reserved.cnt; i++)
 | 
			
		||||
		reserve_bootmem(lmb.reserved.region[i].base,
 | 
			
		||||
				lmb_size_bytes(&lmb.reserved, i));
 | 
			
		||||
 | 
			
		||||
	for (i=0; i < lmb.memory.cnt; i++)
 | 
			
		||||
		memory_present(0, lmb_start_pfn(&lmb.memory, i),
 | 
			
		||||
			       lmb_end_pfn(&lmb.memory, i));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * paging_init() sets up the page tables - in fact we've already done this.
 | 
			
		||||
 */
 | 
			
		||||
void __init paging_init(void)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long zones_size[MAX_NR_ZONES];
 | 
			
		||||
	unsigned long zholes_size[MAX_NR_ZONES];
 | 
			
		||||
	unsigned long total_ram = lmb_phys_mem_size();
 | 
			
		||||
	unsigned long top_of_ram = lmb_end_of_DRAM();
 | 
			
		||||
 | 
			
		||||
	printk(KERN_INFO "Top of RAM: 0x%lx, Total RAM: 0x%lx\n",
 | 
			
		||||
	       top_of_ram, total_ram);
 | 
			
		||||
	printk(KERN_INFO "Memory hole size: %ldMB\n",
 | 
			
		||||
	       (top_of_ram - total_ram) >> 20);
 | 
			
		||||
	/*
 | 
			
		||||
	 * All pages are DMA-able so we put them all in the DMA zone.
 | 
			
		||||
	 */
 | 
			
		||||
	memset(zones_size, 0, sizeof(zones_size));
 | 
			
		||||
	memset(zholes_size, 0, sizeof(zholes_size));
 | 
			
		||||
 | 
			
		||||
	zones_size[ZONE_DMA] = top_of_ram >> PAGE_SHIFT;
 | 
			
		||||
	zholes_size[ZONE_DMA] = (top_of_ram - total_ram) >> PAGE_SHIFT;
 | 
			
		||||
 | 
			
		||||
	free_area_init_node(0, NODE_DATA(0), zones_size,
 | 
			
		||||
			    __pa(PAGE_OFFSET) >> PAGE_SHIFT, zholes_size);
 | 
			
		||||
}
 | 
			
		||||
#endif /* ! CONFIG_NEED_MULTIPLE_NODES */
 | 
			
		||||
 | 
			
		||||
static struct kcore_list kcore_vmem;
 | 
			
		||||
 | 
			
		||||
static int __init setup_kcore(void)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	for (i=0; i < lmb.memory.cnt; i++) {
 | 
			
		||||
		unsigned long base, size;
 | 
			
		||||
		struct kcore_list *kcore_mem;
 | 
			
		||||
 | 
			
		||||
		base = lmb.memory.region[i].base;
 | 
			
		||||
		size = lmb.memory.region[i].size;
 | 
			
		||||
 | 
			
		||||
		/* GFP_ATOMIC to avoid might_sleep warnings during boot */
 | 
			
		||||
		kcore_mem = kmalloc(sizeof(struct kcore_list), GFP_ATOMIC);
 | 
			
		||||
		if (!kcore_mem)
 | 
			
		||||
			panic("mem_init: kmalloc failed\n");
 | 
			
		||||
 | 
			
		||||
		kclist_add(kcore_mem, __va(base), size);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	kclist_add(&kcore_vmem, (void *)VMALLOC_START, VMALLOC_END-VMALLOC_START);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
module_init(setup_kcore);
 | 
			
		||||
 | 
			
		||||
void __init mem_init(void)
 | 
			
		||||
{
 | 
			
		||||
#ifdef CONFIG_NEED_MULTIPLE_NODES
 | 
			
		||||
	int nid;
 | 
			
		||||
#endif
 | 
			
		||||
	pg_data_t *pgdat;
 | 
			
		||||
	unsigned long i;
 | 
			
		||||
	struct page *page;
 | 
			
		||||
	unsigned long reservedpages = 0, codesize, initsize, datasize, bsssize;
 | 
			
		||||
 | 
			
		||||
	num_physpages = max_low_pfn;	/* RAM is assumed contiguous */
 | 
			
		||||
	high_memory = (void *) __va(max_low_pfn * PAGE_SIZE);
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_NEED_MULTIPLE_NODES
 | 
			
		||||
        for_each_online_node(nid) {
 | 
			
		||||
		if (NODE_DATA(nid)->node_spanned_pages != 0) {
 | 
			
		||||
			printk("freeing bootmem node %x\n", nid);
 | 
			
		||||
			totalram_pages +=
 | 
			
		||||
				free_all_bootmem_node(NODE_DATA(nid));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
#else
 | 
			
		||||
	max_mapnr = num_physpages;
 | 
			
		||||
	totalram_pages += free_all_bootmem();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	for_each_pgdat(pgdat) {
 | 
			
		||||
		for (i = 0; i < pgdat->node_spanned_pages; i++) {
 | 
			
		||||
			page = pgdat_page_nr(pgdat, i);
 | 
			
		||||
			if (PageReserved(page))
 | 
			
		||||
				reservedpages++;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	codesize = (unsigned long)&_etext - (unsigned long)&_stext;
 | 
			
		||||
	initsize = (unsigned long)&__init_end - (unsigned long)&__init_begin;
 | 
			
		||||
	datasize = (unsigned long)&_edata - (unsigned long)&__init_end;
 | 
			
		||||
	bsssize = (unsigned long)&__bss_stop - (unsigned long)&__bss_start;
 | 
			
		||||
 | 
			
		||||
	printk(KERN_INFO "Memory: %luk/%luk available (%luk kernel code, "
 | 
			
		||||
	       "%luk reserved, %luk data, %luk bss, %luk init)\n",
 | 
			
		||||
		(unsigned long)nr_free_pages() << (PAGE_SHIFT-10),
 | 
			
		||||
		num_physpages << (PAGE_SHIFT-10),
 | 
			
		||||
		codesize >> 10,
 | 
			
		||||
		reservedpages << (PAGE_SHIFT-10),
 | 
			
		||||
		datasize >> 10,
 | 
			
		||||
		bsssize >> 10,
 | 
			
		||||
		initsize >> 10);
 | 
			
		||||
 | 
			
		||||
	mem_init_done = 1;
 | 
			
		||||
 | 
			
		||||
	/* Initialize the vDSO */
 | 
			
		||||
	vdso_init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void __iomem * reserve_phb_iospace(unsigned long size)
 | 
			
		||||
{
 | 
			
		||||
	void __iomem *virt_addr;
 | 
			
		||||
		
 | 
			
		||||
	if (phbs_io_bot >= IMALLOC_BASE) 
 | 
			
		||||
		panic("reserve_phb_iospace(): phb io space overflow\n");
 | 
			
		||||
			
 | 
			
		||||
	virt_addr = (void __iomem *) phbs_io_bot;
 | 
			
		||||
	phbs_io_bot += size;
 | 
			
		||||
 | 
			
		||||
	return virt_addr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void zero_ctor(void *addr, kmem_cache_t *cache, unsigned long flags)
 | 
			
		||||
{
 | 
			
		||||
	memset(addr, 0, kmem_cache_size(cache));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const int pgtable_cache_size[2] = {
 | 
			
		||||
	PTE_TABLE_SIZE, PMD_TABLE_SIZE
 | 
			
		||||
};
 | 
			
		||||
static const char *pgtable_cache_name[ARRAY_SIZE(pgtable_cache_size)] = {
 | 
			
		||||
	"pgd_pte_cache", "pud_pmd_cache",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
kmem_cache_t *pgtable_cache[ARRAY_SIZE(pgtable_cache_size)];
 | 
			
		||||
 | 
			
		||||
void pgtable_cache_init(void)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	BUILD_BUG_ON(PTE_TABLE_SIZE != pgtable_cache_size[PTE_CACHE_NUM]);
 | 
			
		||||
	BUILD_BUG_ON(PMD_TABLE_SIZE != pgtable_cache_size[PMD_CACHE_NUM]);
 | 
			
		||||
	BUILD_BUG_ON(PUD_TABLE_SIZE != pgtable_cache_size[PUD_CACHE_NUM]);
 | 
			
		||||
	BUILD_BUG_ON(PGD_TABLE_SIZE != pgtable_cache_size[PGD_CACHE_NUM]);
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(pgtable_cache_size); i++) {
 | 
			
		||||
		int size = pgtable_cache_size[i];
 | 
			
		||||
		const char *name = pgtable_cache_name[i];
 | 
			
		||||
 | 
			
		||||
		pgtable_cache[i] = kmem_cache_create(name,
 | 
			
		||||
						     size, size,
 | 
			
		||||
						     SLAB_HWCACHE_ALIGN
 | 
			
		||||
						     | SLAB_MUST_HWCACHE_ALIGN,
 | 
			
		||||
						     zero_ctor,
 | 
			
		||||
						     NULL);
 | 
			
		||||
		if (! pgtable_cache[i])
 | 
			
		||||
			panic("pgtable_cache_init(): could not create %s!\n",
 | 
			
		||||
			      name);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pgprot_t phys_mem_access_prot(struct file *file, unsigned long addr,
 | 
			
		||||
			      unsigned long size, pgprot_t vma_prot)
 | 
			
		||||
{
 | 
			
		||||
	if (ppc_md.phys_mem_access_prot)
 | 
			
		||||
		return ppc_md.phys_mem_access_prot(file, addr, size, vma_prot);
 | 
			
		||||
 | 
			
		||||
	if (!page_is_ram(addr >> PAGE_SHIFT))
 | 
			
		||||
		vma_prot = __pgprot(pgprot_val(vma_prot)
 | 
			
		||||
				    | _PAGE_GUARDED | _PAGE_NO_CACHE);
 | 
			
		||||
	return vma_prot;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(phys_mem_access_prot);
 | 
			
		||||
							
								
								
									
										299
									
								
								arch/powerpc/mm/mem.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										299
									
								
								arch/powerpc/mm/mem.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,299 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  PowerPC version
 | 
			
		||||
 *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
 | 
			
		||||
 *
 | 
			
		||||
 *  Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au)
 | 
			
		||||
 *  and Cort Dougan (PReP) (cort@cs.nmt.edu)
 | 
			
		||||
 *    Copyright (C) 1996 Paul Mackerras
 | 
			
		||||
 *  Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk).
 | 
			
		||||
 *  PPC44x/36-bit changes by Matt Porter (mporter@mvista.com)
 | 
			
		||||
 *
 | 
			
		||||
 *  Derived from "arch/i386/mm/init.c"
 | 
			
		||||
 *    Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or
 | 
			
		||||
 *  modify it under the terms of the GNU General Public License
 | 
			
		||||
 *  as published by the Free Software Foundation; either version
 | 
			
		||||
 *  2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/config.h>
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/sched.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/errno.h>
 | 
			
		||||
#include <linux/string.h>
 | 
			
		||||
#include <linux/types.h>
 | 
			
		||||
#include <linux/mm.h>
 | 
			
		||||
#include <linux/stddef.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/bootmem.h>
 | 
			
		||||
#include <linux/highmem.h>
 | 
			
		||||
#include <linux/initrd.h>
 | 
			
		||||
#include <linux/pagemap.h>
 | 
			
		||||
 | 
			
		||||
#include <asm/pgalloc.h>
 | 
			
		||||
#include <asm/prom.h>
 | 
			
		||||
#include <asm/io.h>
 | 
			
		||||
#include <asm/mmu_context.h>
 | 
			
		||||
#include <asm/pgtable.h>
 | 
			
		||||
#include <asm/mmu.h>
 | 
			
		||||
#include <asm/smp.h>
 | 
			
		||||
#include <asm/machdep.h>
 | 
			
		||||
#include <asm/btext.h>
 | 
			
		||||
#include <asm/tlb.h>
 | 
			
		||||
#include <asm/bootinfo.h>
 | 
			
		||||
#include <asm/prom.h>
 | 
			
		||||
 | 
			
		||||
#include "mem_pieces.h"
 | 
			
		||||
#include "mmu_decl.h"
 | 
			
		||||
 | 
			
		||||
#ifndef CPU_FTR_COHERENT_ICACHE
 | 
			
		||||
#define CPU_FTR_COHERENT_ICACHE	0	/* XXX for now */
 | 
			
		||||
#define CPU_FTR_NOEXECUTE	0
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * This is called by /dev/mem to know if a given address has to
 | 
			
		||||
 * be mapped non-cacheable or not
 | 
			
		||||
 */
 | 
			
		||||
int page_is_ram(unsigned long pfn)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long paddr = (pfn << PAGE_SHIFT);
 | 
			
		||||
 | 
			
		||||
#ifndef CONFIG_PPC64	/* XXX for now */
 | 
			
		||||
	return paddr < __pa(high_memory);
 | 
			
		||||
#else
 | 
			
		||||
	int i;
 | 
			
		||||
	for (i=0; i < lmb.memory.cnt; i++) {
 | 
			
		||||
		unsigned long base;
 | 
			
		||||
 | 
			
		||||
		base = lmb.memory.region[i].base;
 | 
			
		||||
 | 
			
		||||
		if ((paddr >= base) &&
 | 
			
		||||
			(paddr < (base + lmb.memory.region[i].size))) {
 | 
			
		||||
			return 1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(page_is_ram);
 | 
			
		||||
 | 
			
		||||
pgprot_t phys_mem_access_prot(struct file *file, unsigned long addr,
 | 
			
		||||
			      unsigned long size, pgprot_t vma_prot)
 | 
			
		||||
{
 | 
			
		||||
	if (ppc_md.phys_mem_access_prot)
 | 
			
		||||
		return ppc_md.phys_mem_access_prot(file, addr, size, vma_prot);
 | 
			
		||||
 | 
			
		||||
	if (!page_is_ram(addr >> PAGE_SHIFT))
 | 
			
		||||
		vma_prot = __pgprot(pgprot_val(vma_prot)
 | 
			
		||||
				    | _PAGE_GUARDED | _PAGE_NO_CACHE);
 | 
			
		||||
	return vma_prot;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(phys_mem_access_prot);
 | 
			
		||||
 | 
			
		||||
void show_mem(void)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long total = 0, reserved = 0;
 | 
			
		||||
	unsigned long shared = 0, cached = 0;
 | 
			
		||||
	unsigned long highmem = 0;
 | 
			
		||||
	struct page *page;
 | 
			
		||||
	pg_data_t *pgdat;
 | 
			
		||||
	unsigned long i;
 | 
			
		||||
 | 
			
		||||
	printk("Mem-info:\n");
 | 
			
		||||
	show_free_areas();
 | 
			
		||||
	printk("Free swap:       %6ldkB\n", nr_swap_pages<<(PAGE_SHIFT-10));
 | 
			
		||||
	for_each_pgdat(pgdat) {
 | 
			
		||||
		for (i = 0; i < pgdat->node_spanned_pages; i++) {
 | 
			
		||||
			page = pgdat_page_nr(pgdat, i);
 | 
			
		||||
			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("%ld pages of RAM\n", total);
 | 
			
		||||
#ifdef CONFIG_HIGHMEM
 | 
			
		||||
	printk("%ld pages of HIGHMEM\n", highmem);
 | 
			
		||||
#endif
 | 
			
		||||
	printk("%ld reserved pages\n", reserved);
 | 
			
		||||
	printk("%ld pages shared\n", shared);
 | 
			
		||||
	printk("%ld pages swap cached\n", cached);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * This is called when a page has been modified by the kernel.
 | 
			
		||||
 * It just marks the page as not i-cache clean.  We do the i-cache
 | 
			
		||||
 * flush later when the page is given to a user process, if necessary.
 | 
			
		||||
 */
 | 
			
		||||
void flush_dcache_page(struct page *page)
 | 
			
		||||
{
 | 
			
		||||
	if (cpu_has_feature(CPU_FTR_COHERENT_ICACHE))
 | 
			
		||||
		return;
 | 
			
		||||
	/* avoid an atomic op if possible */
 | 
			
		||||
	if (test_bit(PG_arch_1, &page->flags))
 | 
			
		||||
		clear_bit(PG_arch_1, &page->flags);
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(flush_dcache_page);
 | 
			
		||||
 | 
			
		||||
void flush_dcache_icache_page(struct page *page)
 | 
			
		||||
{
 | 
			
		||||
#ifdef CONFIG_BOOKE
 | 
			
		||||
	void *start = kmap_atomic(page, KM_PPC_SYNC_ICACHE);
 | 
			
		||||
	__flush_dcache_icache(start);
 | 
			
		||||
	kunmap_atomic(start, KM_PPC_SYNC_ICACHE);
 | 
			
		||||
#elif defined(CONFIG_8xx)
 | 
			
		||||
	/* On 8xx there is no need to kmap since highmem is not supported */
 | 
			
		||||
	__flush_dcache_icache(page_address(page)); 
 | 
			
		||||
#else
 | 
			
		||||
	__flush_dcache_icache_phys(page_to_pfn(page) << PAGE_SHIFT);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
void clear_user_page(void *page, unsigned long vaddr, struct page *pg)
 | 
			
		||||
{
 | 
			
		||||
	clear_page(page);
 | 
			
		||||
 | 
			
		||||
	if (cpu_has_feature(CPU_FTR_COHERENT_ICACHE))
 | 
			
		||||
		return;
 | 
			
		||||
	/*
 | 
			
		||||
	 * We shouldnt have to do this, but some versions of glibc
 | 
			
		||||
	 * require it (ld.so assumes zero filled pages are icache clean)
 | 
			
		||||
	 * - Anton
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
	/* avoid an atomic op if possible */
 | 
			
		||||
	if (test_bit(PG_arch_1, &pg->flags))
 | 
			
		||||
		clear_bit(PG_arch_1, &pg->flags);
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(clear_user_page);
 | 
			
		||||
 | 
			
		||||
void copy_user_page(void *vto, void *vfrom, unsigned long vaddr,
 | 
			
		||||
		    struct page *pg)
 | 
			
		||||
{
 | 
			
		||||
	copy_page(vto, vfrom);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * We should be able to use the following optimisation, however
 | 
			
		||||
	 * there are two problems.
 | 
			
		||||
	 * Firstly a bug in some versions of binutils meant PLT sections
 | 
			
		||||
	 * were not marked executable.
 | 
			
		||||
	 * Secondly the first word in the GOT section is blrl, used
 | 
			
		||||
	 * to establish the GOT address. Until recently the GOT was
 | 
			
		||||
	 * not marked executable.
 | 
			
		||||
	 * - Anton
 | 
			
		||||
	 */
 | 
			
		||||
#if 0
 | 
			
		||||
	if (!vma->vm_file && ((vma->vm_flags & VM_EXEC) == 0))
 | 
			
		||||
		return;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	if (cpu_has_feature(CPU_FTR_COHERENT_ICACHE))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/* avoid an atomic op if possible */
 | 
			
		||||
	if (test_bit(PG_arch_1, &pg->flags))
 | 
			
		||||
		clear_bit(PG_arch_1, &pg->flags);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void flush_icache_user_range(struct vm_area_struct *vma, struct page *page,
 | 
			
		||||
			     unsigned long addr, int len)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long maddr;
 | 
			
		||||
 | 
			
		||||
	maddr = (unsigned long) kmap(page) + (addr & ~PAGE_MASK);
 | 
			
		||||
	flush_icache_range(maddr, maddr + len);
 | 
			
		||||
	kunmap(page);
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(flush_icache_user_range);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * This is called at the end of handling a user page fault, when the
 | 
			
		||||
 * fault has been handled by updating a PTE in the linux page tables.
 | 
			
		||||
 * We use it to preload an HPTE into the hash table corresponding to
 | 
			
		||||
 * the updated linux PTE.
 | 
			
		||||
 * 
 | 
			
		||||
 * This must always be called with the mm->page_table_lock held
 | 
			
		||||
 */
 | 
			
		||||
void update_mmu_cache(struct vm_area_struct *vma, unsigned long address,
 | 
			
		||||
		      pte_t pte)
 | 
			
		||||
{
 | 
			
		||||
	/* handle i-cache coherency */
 | 
			
		||||
	unsigned long pfn = pte_pfn(pte);
 | 
			
		||||
#ifdef CONFIG_PPC32
 | 
			
		||||
	pmd_t *pmd;
 | 
			
		||||
#else
 | 
			
		||||
	unsigned long vsid;
 | 
			
		||||
	void *pgdir;
 | 
			
		||||
	pte_t *ptep;
 | 
			
		||||
	int local = 0;
 | 
			
		||||
	cpumask_t tmp;
 | 
			
		||||
	unsigned long flags;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	/* handle i-cache coherency */
 | 
			
		||||
	if (!cpu_has_feature(CPU_FTR_COHERENT_ICACHE) &&
 | 
			
		||||
	    !cpu_has_feature(CPU_FTR_NOEXECUTE) &&
 | 
			
		||||
	    pfn_valid(pfn)) {
 | 
			
		||||
		struct page *page = pfn_to_page(pfn);
 | 
			
		||||
		if (!PageReserved(page)
 | 
			
		||||
		    && !test_bit(PG_arch_1, &page->flags)) {
 | 
			
		||||
			if (vma->vm_mm == current->active_mm) {
 | 
			
		||||
#ifdef CONFIG_8xx
 | 
			
		||||
			/* On 8xx, cache control instructions (particularly 
 | 
			
		||||
		 	 * "dcbst" from flush_dcache_icache) fault as write 
 | 
			
		||||
			 * operation if there is an unpopulated TLB entry 
 | 
			
		||||
			 * for the address in question. To workaround that, 
 | 
			
		||||
			 * we invalidate the TLB here, thus avoiding dcbst 
 | 
			
		||||
			 * misbehaviour.
 | 
			
		||||
			 */
 | 
			
		||||
				_tlbie(address);
 | 
			
		||||
#endif
 | 
			
		||||
				__flush_dcache_icache((void *) address);
 | 
			
		||||
			} else
 | 
			
		||||
				flush_dcache_icache_page(page);
 | 
			
		||||
			set_bit(PG_arch_1, &page->flags);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_PPC_STD_MMU
 | 
			
		||||
	/* We only want HPTEs for linux PTEs that have _PAGE_ACCESSED set */
 | 
			
		||||
	if (!pte_young(pte) || address >= TASK_SIZE)
 | 
			
		||||
		return;
 | 
			
		||||
#ifdef CONFIG_PPC32
 | 
			
		||||
	if (Hash == 0)
 | 
			
		||||
		return;
 | 
			
		||||
	pmd = pmd_offset(pgd_offset(vma->vm_mm, address), address);
 | 
			
		||||
	if (!pmd_none(*pmd))
 | 
			
		||||
		add_hash_page(vma->vm_mm->context, address, pmd_val(*pmd));
 | 
			
		||||
#else
 | 
			
		||||
	pgdir = vma->vm_mm->pgd;
 | 
			
		||||
	if (pgdir == NULL)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	ptep = find_linux_pte(pgdir, ea);
 | 
			
		||||
	if (!ptep)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	vsid = get_vsid(vma->vm_mm->context.id, ea);
 | 
			
		||||
 | 
			
		||||
	local_irq_save(flags);
 | 
			
		||||
	tmp = cpumask_of_cpu(smp_processor_id());
 | 
			
		||||
	if (cpus_equal(vma->vm_mm->cpu_vm_mask, tmp))
 | 
			
		||||
		local = 1;
 | 
			
		||||
 | 
			
		||||
	__hash_page(ea, pte_val(pte) & (_PAGE_USER|_PAGE_RW), vsid, ptep,
 | 
			
		||||
		    0x300, local);
 | 
			
		||||
	local_irq_restore(flags);
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										259
									
								
								arch/powerpc/mm/mem64.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										259
									
								
								arch/powerpc/mm/mem64.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,259 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  PowerPC version 
 | 
			
		||||
 *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
 | 
			
		||||
 *
 | 
			
		||||
 *  Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au)
 | 
			
		||||
 *  and Cort Dougan (PReP) (cort@cs.nmt.edu)
 | 
			
		||||
 *    Copyright (C) 1996 Paul Mackerras
 | 
			
		||||
 *  Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk).
 | 
			
		||||
 *
 | 
			
		||||
 *  Derived from "arch/i386/mm/init.c"
 | 
			
		||||
 *    Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
 | 
			
		||||
 *
 | 
			
		||||
 *  Dave Engebretsen <engebret@us.ibm.com>
 | 
			
		||||
 *      Rework for PPC64 port.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or
 | 
			
		||||
 *  modify it under the terms of the GNU General Public License
 | 
			
		||||
 *  as published by the Free Software Foundation; either version
 | 
			
		||||
 *  2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/config.h>
 | 
			
		||||
#include <linux/signal.h>
 | 
			
		||||
#include <linux/sched.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/errno.h>
 | 
			
		||||
#include <linux/string.h>
 | 
			
		||||
#include <linux/types.h>
 | 
			
		||||
#include <linux/mman.h>
 | 
			
		||||
#include <linux/mm.h>
 | 
			
		||||
#include <linux/swap.h>
 | 
			
		||||
#include <linux/stddef.h>
 | 
			
		||||
#include <linux/vmalloc.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/delay.h>
 | 
			
		||||
#include <linux/bootmem.h>
 | 
			
		||||
#include <linux/highmem.h>
 | 
			
		||||
#include <linux/idr.h>
 | 
			
		||||
#include <linux/nodemask.h>
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
 | 
			
		||||
#include <asm/pgalloc.h>
 | 
			
		||||
#include <asm/page.h>
 | 
			
		||||
#include <asm/prom.h>
 | 
			
		||||
#include <asm/lmb.h>
 | 
			
		||||
#include <asm/rtas.h>
 | 
			
		||||
#include <asm/io.h>
 | 
			
		||||
#include <asm/mmu_context.h>
 | 
			
		||||
#include <asm/pgtable.h>
 | 
			
		||||
#include <asm/mmu.h>
 | 
			
		||||
#include <asm/uaccess.h>
 | 
			
		||||
#include <asm/smp.h>
 | 
			
		||||
#include <asm/machdep.h>
 | 
			
		||||
#include <asm/tlb.h>
 | 
			
		||||
#include <asm/eeh.h>
 | 
			
		||||
#include <asm/processor.h>
 | 
			
		||||
#include <asm/mmzone.h>
 | 
			
		||||
#include <asm/cputable.h>
 | 
			
		||||
#include <asm/ppcdebug.h>
 | 
			
		||||
#include <asm/sections.h>
 | 
			
		||||
#include <asm/system.h>
 | 
			
		||||
#include <asm/iommu.h>
 | 
			
		||||
#include <asm/abs_addr.h>
 | 
			
		||||
#include <asm/vdso.h>
 | 
			
		||||
#include <asm/imalloc.h>
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * This is called by /dev/mem to know if a given address has to
 | 
			
		||||
 * be mapped non-cacheable or not
 | 
			
		||||
 */
 | 
			
		||||
int page_is_ram(unsigned long pfn)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	unsigned long paddr = (pfn << PAGE_SHIFT);
 | 
			
		||||
 | 
			
		||||
	for (i=0; i < lmb.memory.cnt; i++) {
 | 
			
		||||
		unsigned long base;
 | 
			
		||||
 | 
			
		||||
		base = lmb.memory.region[i].base;
 | 
			
		||||
 | 
			
		||||
		if ((paddr >= base) &&
 | 
			
		||||
			(paddr < (base + lmb.memory.region[i].size))) {
 | 
			
		||||
			return 1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(page_is_ram);
 | 
			
		||||
 | 
			
		||||
pgprot_t phys_mem_access_prot(struct file *file, unsigned long addr,
 | 
			
		||||
			      unsigned long size, pgprot_t vma_prot)
 | 
			
		||||
{
 | 
			
		||||
	if (ppc_md.phys_mem_access_prot)
 | 
			
		||||
		return ppc_md.phys_mem_access_prot(file, addr, size, vma_prot);
 | 
			
		||||
 | 
			
		||||
	if (!page_is_ram(addr >> PAGE_SHIFT))
 | 
			
		||||
		vma_prot = __pgprot(pgprot_val(vma_prot)
 | 
			
		||||
				    | _PAGE_GUARDED | _PAGE_NO_CACHE);
 | 
			
		||||
	return vma_prot;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(phys_mem_access_prot);
 | 
			
		||||
 | 
			
		||||
void show_mem(void)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long total = 0, reserved = 0;
 | 
			
		||||
	unsigned long shared = 0, cached = 0;
 | 
			
		||||
	struct page *page;
 | 
			
		||||
	pg_data_t *pgdat;
 | 
			
		||||
	unsigned long i;
 | 
			
		||||
 | 
			
		||||
	printk("Mem-info:\n");
 | 
			
		||||
	show_free_areas();
 | 
			
		||||
	printk("Free swap:       %6ldkB\n", nr_swap_pages<<(PAGE_SHIFT-10));
 | 
			
		||||
	for_each_pgdat(pgdat) {
 | 
			
		||||
		for (i = 0; i < pgdat->node_spanned_pages; i++) {
 | 
			
		||||
			page = pgdat_page_nr(pgdat, i);
 | 
			
		||||
			total++;
 | 
			
		||||
			if (PageReserved(page))
 | 
			
		||||
				reserved++;
 | 
			
		||||
			else if (PageSwapCache(page))
 | 
			
		||||
				cached++;
 | 
			
		||||
			else if (page_count(page))
 | 
			
		||||
				shared += page_count(page) - 1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	printk("%ld pages of RAM\n", total);
 | 
			
		||||
	printk("%ld reserved pages\n", reserved);
 | 
			
		||||
	printk("%ld pages shared\n", shared);
 | 
			
		||||
	printk("%ld pages swap cached\n", cached);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * This is called when a page has been modified by the kernel.
 | 
			
		||||
 * It just marks the page as not i-cache clean.  We do the i-cache
 | 
			
		||||
 * flush later when the page is given to a user process, if necessary.
 | 
			
		||||
 */
 | 
			
		||||
void flush_dcache_page(struct page *page)
 | 
			
		||||
{
 | 
			
		||||
	if (cpu_has_feature(CPU_FTR_COHERENT_ICACHE))
 | 
			
		||||
		return;
 | 
			
		||||
	/* avoid an atomic op if possible */
 | 
			
		||||
	if (test_bit(PG_arch_1, &page->flags))
 | 
			
		||||
		clear_bit(PG_arch_1, &page->flags);
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(flush_dcache_page);
 | 
			
		||||
 | 
			
		||||
void clear_user_page(void *page, unsigned long vaddr, struct page *pg)
 | 
			
		||||
{
 | 
			
		||||
	clear_page(page);
 | 
			
		||||
 | 
			
		||||
	if (cpu_has_feature(CPU_FTR_COHERENT_ICACHE))
 | 
			
		||||
		return;
 | 
			
		||||
	/*
 | 
			
		||||
	 * We shouldnt have to do this, but some versions of glibc
 | 
			
		||||
	 * require it (ld.so assumes zero filled pages are icache clean)
 | 
			
		||||
	 * - Anton
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
	/* avoid an atomic op if possible */
 | 
			
		||||
	if (test_bit(PG_arch_1, &pg->flags))
 | 
			
		||||
		clear_bit(PG_arch_1, &pg->flags);
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(clear_user_page);
 | 
			
		||||
 | 
			
		||||
void copy_user_page(void *vto, void *vfrom, unsigned long vaddr,
 | 
			
		||||
		    struct page *pg)
 | 
			
		||||
{
 | 
			
		||||
	copy_page(vto, vfrom);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * We should be able to use the following optimisation, however
 | 
			
		||||
	 * there are two problems.
 | 
			
		||||
	 * Firstly a bug in some versions of binutils meant PLT sections
 | 
			
		||||
	 * were not marked executable.
 | 
			
		||||
	 * Secondly the first word in the GOT section is blrl, used
 | 
			
		||||
	 * to establish the GOT address. Until recently the GOT was
 | 
			
		||||
	 * not marked executable.
 | 
			
		||||
	 * - Anton
 | 
			
		||||
	 */
 | 
			
		||||
#if 0
 | 
			
		||||
	if (!vma->vm_file && ((vma->vm_flags & VM_EXEC) == 0))
 | 
			
		||||
		return;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	if (cpu_has_feature(CPU_FTR_COHERENT_ICACHE))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/* avoid an atomic op if possible */
 | 
			
		||||
	if (test_bit(PG_arch_1, &pg->flags))
 | 
			
		||||
		clear_bit(PG_arch_1, &pg->flags);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void flush_icache_user_range(struct vm_area_struct *vma, struct page *page,
 | 
			
		||||
			     unsigned long addr, int len)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long maddr;
 | 
			
		||||
 | 
			
		||||
	maddr = (unsigned long)page_address(page) + (addr & ~PAGE_MASK);
 | 
			
		||||
	flush_icache_range(maddr, maddr + len);
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(flush_icache_user_range);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * This is called at the end of handling a user page fault, when the
 | 
			
		||||
 * fault has been handled by updating a PTE in the linux page tables.
 | 
			
		||||
 * We use it to preload an HPTE into the hash table corresponding to
 | 
			
		||||
 * the updated linux PTE.
 | 
			
		||||
 * 
 | 
			
		||||
 * This must always be called with the mm->page_table_lock held
 | 
			
		||||
 */
 | 
			
		||||
void update_mmu_cache(struct vm_area_struct *vma, unsigned long ea,
 | 
			
		||||
		      pte_t pte)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long vsid;
 | 
			
		||||
	void *pgdir;
 | 
			
		||||
	pte_t *ptep;
 | 
			
		||||
	int local = 0;
 | 
			
		||||
	cpumask_t tmp;
 | 
			
		||||
	unsigned long flags;
 | 
			
		||||
 | 
			
		||||
	/* handle i-cache coherency */
 | 
			
		||||
	if (!cpu_has_feature(CPU_FTR_COHERENT_ICACHE) &&
 | 
			
		||||
	    !cpu_has_feature(CPU_FTR_NOEXECUTE)) {
 | 
			
		||||
		unsigned long pfn = pte_pfn(pte);
 | 
			
		||||
		if (pfn_valid(pfn)) {
 | 
			
		||||
			struct page *page = pfn_to_page(pfn);
 | 
			
		||||
			if (!PageReserved(page)
 | 
			
		||||
			    && !test_bit(PG_arch_1, &page->flags)) {
 | 
			
		||||
				__flush_dcache_icache(page_address(page));
 | 
			
		||||
				set_bit(PG_arch_1, &page->flags);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* We only want HPTEs for linux PTEs that have _PAGE_ACCESSED set */
 | 
			
		||||
	if (!pte_young(pte))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	pgdir = vma->vm_mm->pgd;
 | 
			
		||||
	if (pgdir == NULL)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	ptep = find_linux_pte(pgdir, ea);
 | 
			
		||||
	if (!ptep)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	vsid = get_vsid(vma->vm_mm->context.id, ea);
 | 
			
		||||
 | 
			
		||||
	local_irq_save(flags);
 | 
			
		||||
	tmp = cpumask_of_cpu(smp_processor_id());
 | 
			
		||||
	if (cpus_equal(vma->vm_mm->cpu_vm_mask, tmp))
 | 
			
		||||
		local = 1;
 | 
			
		||||
 | 
			
		||||
	__hash_page(ea, pte_val(pte) & (_PAGE_USER|_PAGE_RW), vsid, ptep,
 | 
			
		||||
		    0x300, local);
 | 
			
		||||
	local_irq_restore(flags);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										163
									
								
								arch/powerpc/mm/mem_pieces.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								arch/powerpc/mm/mem_pieces.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,163 @@
 | 
			
		||||
/*
 | 
			
		||||
 *    Copyright (c) 1996 Paul Mackerras <paulus@cs.anu.edu.au>
 | 
			
		||||
 *      Changes to accommodate Power Macintoshes.
 | 
			
		||||
 *    Cort Dougan <cort@cs.nmt.edu>
 | 
			
		||||
 *      Rewrites.
 | 
			
		||||
 *    Grant Erickson <grant@lcse.umn.edu>
 | 
			
		||||
 *      General rework and split from mm/init.c.
 | 
			
		||||
 *
 | 
			
		||||
 *    Module name: mem_pieces.c
 | 
			
		||||
 *
 | 
			
		||||
 *    Description:
 | 
			
		||||
 *      Routines and data structures for manipulating and representing
 | 
			
		||||
 *      phyiscal memory extents (i.e. address/length pairs).
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/config.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/stddef.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <asm/page.h>
 | 
			
		||||
 | 
			
		||||
#include "mem_pieces.h"
 | 
			
		||||
 | 
			
		||||
extern struct mem_pieces phys_avail;
 | 
			
		||||
 | 
			
		||||
static void mem_pieces_print(struct mem_pieces *);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Scan a region for a piece of a given size with the required alignment.
 | 
			
		||||
 */
 | 
			
		||||
void __init *
 | 
			
		||||
mem_pieces_find(unsigned int size, unsigned int align)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	unsigned a, e;
 | 
			
		||||
	struct mem_pieces *mp = &phys_avail;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < mp->n_regions; ++i) {
 | 
			
		||||
		a = mp->regions[i].address;
 | 
			
		||||
		e = a + mp->regions[i].size;
 | 
			
		||||
		a = (a + align - 1) & -align;
 | 
			
		||||
		if (a + size <= e) {
 | 
			
		||||
			mem_pieces_remove(mp, a, size, 1);
 | 
			
		||||
			return (void *) __va(a);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	panic("Couldn't find %u bytes at %u alignment\n", size, align);
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Remove some memory from an array of pieces
 | 
			
		||||
 */
 | 
			
		||||
void __init
 | 
			
		||||
mem_pieces_remove(struct mem_pieces *mp, unsigned int start, unsigned int size,
 | 
			
		||||
		  int must_exist)
 | 
			
		||||
{
 | 
			
		||||
	int i, j;
 | 
			
		||||
	unsigned int end, rs, re;
 | 
			
		||||
	struct reg_property *rp;
 | 
			
		||||
 | 
			
		||||
	end = start + size;
 | 
			
		||||
	for (i = 0, rp = mp->regions; i < mp->n_regions; ++i, ++rp) {
 | 
			
		||||
		if (end > rp->address && start < rp->address + rp->size)
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
	if (i >= mp->n_regions) {
 | 
			
		||||
		if (must_exist)
 | 
			
		||||
			printk("mem_pieces_remove: [%x,%x) not in any region\n",
 | 
			
		||||
			       start, end);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	for (; i < mp->n_regions && end > rp->address; ++i, ++rp) {
 | 
			
		||||
		rs = rp->address;
 | 
			
		||||
		re = rs + rp->size;
 | 
			
		||||
		if (must_exist && (start < rs || end > re)) {
 | 
			
		||||
			printk("mem_pieces_remove: bad overlap [%x,%x) with",
 | 
			
		||||
			       start, end);
 | 
			
		||||
			mem_pieces_print(mp);
 | 
			
		||||
			must_exist = 0;
 | 
			
		||||
		}
 | 
			
		||||
		if (start > rs) {
 | 
			
		||||
			rp->size = start - rs;
 | 
			
		||||
			if (end < re) {
 | 
			
		||||
				/* need to split this entry */
 | 
			
		||||
				if (mp->n_regions >= MEM_PIECES_MAX)
 | 
			
		||||
					panic("eek... mem_pieces overflow");
 | 
			
		||||
				for (j = mp->n_regions; j > i + 1; --j)
 | 
			
		||||
					mp->regions[j] = mp->regions[j-1];
 | 
			
		||||
				++mp->n_regions;
 | 
			
		||||
				rp[1].address = end;
 | 
			
		||||
				rp[1].size = re - end;
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			if (end < re) {
 | 
			
		||||
				rp->address = end;
 | 
			
		||||
				rp->size = re - end;
 | 
			
		||||
			} else {
 | 
			
		||||
				/* need to delete this entry */
 | 
			
		||||
				for (j = i; j < mp->n_regions - 1; ++j)
 | 
			
		||||
					mp->regions[j] = mp->regions[j+1];
 | 
			
		||||
				--mp->n_regions;
 | 
			
		||||
				--i;
 | 
			
		||||
				--rp;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void __init
 | 
			
		||||
mem_pieces_print(struct mem_pieces *mp)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < mp->n_regions; ++i)
 | 
			
		||||
		printk(" [%x, %x)", mp->regions[i].address,
 | 
			
		||||
		       mp->regions[i].address + mp->regions[i].size);
 | 
			
		||||
	printk("\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void __init
 | 
			
		||||
mem_pieces_sort(struct mem_pieces *mp)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long a, s;
 | 
			
		||||
	int i, j;
 | 
			
		||||
 | 
			
		||||
	for (i = 1; i < mp->n_regions; ++i) {
 | 
			
		||||
		a = mp->regions[i].address;
 | 
			
		||||
		s = mp->regions[i].size;
 | 
			
		||||
		for (j = i - 1; j >= 0; --j) {
 | 
			
		||||
			if (a >= mp->regions[j].address)
 | 
			
		||||
				break;
 | 
			
		||||
			mp->regions[j+1] = mp->regions[j];
 | 
			
		||||
		}
 | 
			
		||||
		mp->regions[j+1].address = a;
 | 
			
		||||
		mp->regions[j+1].size = s;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void __init
 | 
			
		||||
mem_pieces_coalesce(struct mem_pieces *mp)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long a, s, ns;
 | 
			
		||||
	int i, j, d;
 | 
			
		||||
 | 
			
		||||
	d = 0;
 | 
			
		||||
	for (i = 0; i < mp->n_regions; i = j) {
 | 
			
		||||
		a = mp->regions[i].address;
 | 
			
		||||
		s = mp->regions[i].size;
 | 
			
		||||
		for (j = i + 1; j < mp->n_regions
 | 
			
		||||
			     && mp->regions[j].address - a <= s; ++j) {
 | 
			
		||||
			ns = mp->regions[j].address + mp->regions[j].size - a;
 | 
			
		||||
			if (ns > s)
 | 
			
		||||
				s = ns;
 | 
			
		||||
		}
 | 
			
		||||
		mp->regions[d].address = a;
 | 
			
		||||
		mp->regions[d].size = s;
 | 
			
		||||
		++d;
 | 
			
		||||
	}
 | 
			
		||||
	mp->n_regions = d;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										48
									
								
								arch/powerpc/mm/mem_pieces.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								arch/powerpc/mm/mem_pieces.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
			
		||||
/*
 | 
			
		||||
 *    Copyright (c) 1996 Paul Mackerras <paulus@cs.anu.edu.au>
 | 
			
		||||
 *      Changes to accommodate Power Macintoshes.
 | 
			
		||||
 *    Cort Dougan <cort@cs.nmt.edu>
 | 
			
		||||
 *      Rewrites.
 | 
			
		||||
 *    Grant Erickson <grant@lcse.umn.edu>
 | 
			
		||||
 *      General rework and split from mm/init.c.
 | 
			
		||||
 *
 | 
			
		||||
 *    Module name: mem_pieces.h
 | 
			
		||||
 *
 | 
			
		||||
 *    Description:
 | 
			
		||||
 *      Routines and data structures for manipulating and representing
 | 
			
		||||
 *      phyiscal memory extents (i.e. address/length pairs).
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef __MEM_PIECES_H__
 | 
			
		||||
#define	__MEM_PIECES_H__
 | 
			
		||||
 | 
			
		||||
#include <asm/prom.h>
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Type Definitions */
 | 
			
		||||
 | 
			
		||||
#define	MEM_PIECES_MAX	32
 | 
			
		||||
 | 
			
		||||
struct mem_pieces {
 | 
			
		||||
    int n_regions;
 | 
			
		||||
    struct reg_property regions[MEM_PIECES_MAX];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Function Prototypes */
 | 
			
		||||
 | 
			
		||||
extern void	*mem_pieces_find(unsigned int size, unsigned int align);
 | 
			
		||||
extern void	 mem_pieces_remove(struct mem_pieces *mp, unsigned int start,
 | 
			
		||||
				   unsigned int size, int must_exist);
 | 
			
		||||
extern void	 mem_pieces_coalesce(struct mem_pieces *mp);
 | 
			
		||||
extern void	 mem_pieces_sort(struct mem_pieces *mp);
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif /* __MEM_PIECES_H__ */
 | 
			
		||||
							
								
								
									
										86
									
								
								arch/powerpc/mm/mmu_context.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								arch/powerpc/mm/mmu_context.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,86 @@
 | 
			
		||||
/*
 | 
			
		||||
 * This file contains the routines for handling the MMU on those
 | 
			
		||||
 * PowerPC implementations where the MMU substantially follows the
 | 
			
		||||
 * architecture specification.  This includes the 6xx, 7xx, 7xxx,
 | 
			
		||||
 * 8260, and POWER3 implementations but excludes the 8xx and 4xx.
 | 
			
		||||
 *  -- paulus
 | 
			
		||||
 *
 | 
			
		||||
 *  Derived from arch/ppc/mm/init.c:
 | 
			
		||||
 *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
 | 
			
		||||
 *
 | 
			
		||||
 *  Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au)
 | 
			
		||||
 *  and Cort Dougan (PReP) (cort@cs.nmt.edu)
 | 
			
		||||
 *    Copyright (C) 1996 Paul Mackerras
 | 
			
		||||
 *  Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk).
 | 
			
		||||
 *
 | 
			
		||||
 *  Derived from "arch/i386/mm/init.c"
 | 
			
		||||
 *    Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or
 | 
			
		||||
 *  modify it under the terms of the GNU General Public License
 | 
			
		||||
 *  as published by the Free Software Foundation; either version
 | 
			
		||||
 *  2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/config.h>
 | 
			
		||||
#include <linux/mm.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
 | 
			
		||||
#include <asm/mmu_context.h>
 | 
			
		||||
#include <asm/tlbflush.h>
 | 
			
		||||
 | 
			
		||||
mm_context_t next_mmu_context;
 | 
			
		||||
unsigned long context_map[LAST_CONTEXT / BITS_PER_LONG + 1];
 | 
			
		||||
#ifdef FEW_CONTEXTS
 | 
			
		||||
atomic_t nr_free_contexts;
 | 
			
		||||
struct mm_struct *context_mm[LAST_CONTEXT+1];
 | 
			
		||||
void steal_context(void);
 | 
			
		||||
#endif /* FEW_CONTEXTS */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Initialize the context management stuff.
 | 
			
		||||
 */
 | 
			
		||||
void __init
 | 
			
		||||
mmu_context_init(void)
 | 
			
		||||
{
 | 
			
		||||
	/*
 | 
			
		||||
	 * Some processors have too few contexts to reserve one for
 | 
			
		||||
	 * init_mm, and require using context 0 for a normal task.
 | 
			
		||||
	 * Other processors reserve the use of context zero for the kernel.
 | 
			
		||||
	 * This code assumes FIRST_CONTEXT < 32.
 | 
			
		||||
	 */
 | 
			
		||||
	context_map[0] = (1 << FIRST_CONTEXT) - 1;
 | 
			
		||||
	next_mmu_context = FIRST_CONTEXT;
 | 
			
		||||
#ifdef FEW_CONTEXTS
 | 
			
		||||
	atomic_set(&nr_free_contexts, LAST_CONTEXT - FIRST_CONTEXT + 1);
 | 
			
		||||
#endif /* FEW_CONTEXTS */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef FEW_CONTEXTS
 | 
			
		||||
/*
 | 
			
		||||
 * Steal a context from a task that has one at the moment.
 | 
			
		||||
 * This is only used on 8xx and 4xx and we presently assume that
 | 
			
		||||
 * they don't do SMP.  If they do then this will have to check
 | 
			
		||||
 * whether the MM we steal is in use.
 | 
			
		||||
 * We also assume that this is only used on systems that don't
 | 
			
		||||
 * use an MMU hash table - this is true for 8xx and 4xx.
 | 
			
		||||
 * This isn't an LRU system, it just frees up each context in
 | 
			
		||||
 * turn (sort-of pseudo-random replacement :).  This would be the
 | 
			
		||||
 * place to implement an LRU scheme if anyone was motivated to do it.
 | 
			
		||||
 *  -- paulus
 | 
			
		||||
 */
 | 
			
		||||
void
 | 
			
		||||
steal_context(void)
 | 
			
		||||
{
 | 
			
		||||
	struct mm_struct *mm;
 | 
			
		||||
 | 
			
		||||
	/* free up context `next_mmu_context' */
 | 
			
		||||
	/* if we shouldn't free context 0, don't... */
 | 
			
		||||
	if (next_mmu_context < FIRST_CONTEXT)
 | 
			
		||||
		next_mmu_context = FIRST_CONTEXT;
 | 
			
		||||
	mm = context_mm[next_mmu_context];
 | 
			
		||||
	flush_tlb_mm(mm);
 | 
			
		||||
	destroy_context(mm);
 | 
			
		||||
}
 | 
			
		||||
#endif /* FEW_CONTEXTS */
 | 
			
		||||
							
								
								
									
										63
									
								
								arch/powerpc/mm/mmu_context64.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								arch/powerpc/mm/mmu_context64.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  MMU context allocation for 64-bit kernels.
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2004 Anton Blanchard, IBM Corp. <anton@samba.org>
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or
 | 
			
		||||
 *  modify it under the terms of the GNU General Public License
 | 
			
		||||
 *  as published by the Free Software Foundation; either version
 | 
			
		||||
 *  2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/config.h>
 | 
			
		||||
#include <linux/sched.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/errno.h>
 | 
			
		||||
#include <linux/string.h>
 | 
			
		||||
#include <linux/types.h>
 | 
			
		||||
#include <linux/mm.h>
 | 
			
		||||
#include <linux/spinlock.h>
 | 
			
		||||
#include <linux/idr.h>
 | 
			
		||||
 | 
			
		||||
#include <asm/mmu_context.h>
 | 
			
		||||
 | 
			
		||||
static DEFINE_SPINLOCK(mmu_context_lock);
 | 
			
		||||
static DEFINE_IDR(mmu_context_idr);
 | 
			
		||||
 | 
			
		||||
int init_new_context(struct task_struct *tsk, struct mm_struct *mm)
 | 
			
		||||
{
 | 
			
		||||
	int index;
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
again:
 | 
			
		||||
	if (!idr_pre_get(&mmu_context_idr, GFP_KERNEL))
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	spin_lock(&mmu_context_lock);
 | 
			
		||||
	err = idr_get_new_above(&mmu_context_idr, NULL, 1, &index);
 | 
			
		||||
	spin_unlock(&mmu_context_lock);
 | 
			
		||||
 | 
			
		||||
	if (err == -EAGAIN)
 | 
			
		||||
		goto again;
 | 
			
		||||
	else if (err)
 | 
			
		||||
		return err;
 | 
			
		||||
 | 
			
		||||
	if (index > MAX_CONTEXT) {
 | 
			
		||||
		idr_remove(&mmu_context_idr, index);
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mm->context.id = index;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void destroy_context(struct mm_struct *mm)
 | 
			
		||||
{
 | 
			
		||||
	spin_lock(&mmu_context_lock);
 | 
			
		||||
	idr_remove(&mmu_context_idr, mm->context.id);
 | 
			
		||||
	spin_unlock(&mmu_context_lock);
 | 
			
		||||
 | 
			
		||||
	mm->context.id = NO_CONTEXT;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										85
									
								
								arch/powerpc/mm/mmu_decl.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								arch/powerpc/mm/mmu_decl.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,85 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Declarations of procedures and variables shared between files
 | 
			
		||||
 * in arch/ppc/mm/.
 | 
			
		||||
 *
 | 
			
		||||
 *  Derived from arch/ppc/mm/init.c:
 | 
			
		||||
 *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
 | 
			
		||||
 *
 | 
			
		||||
 *  Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au)
 | 
			
		||||
 *  and Cort Dougan (PReP) (cort@cs.nmt.edu)
 | 
			
		||||
 *    Copyright (C) 1996 Paul Mackerras
 | 
			
		||||
 *  Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk).
 | 
			
		||||
 *
 | 
			
		||||
 *  Derived from "arch/i386/mm/init.c"
 | 
			
		||||
 *    Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or
 | 
			
		||||
 *  modify it under the terms of the GNU General Public License
 | 
			
		||||
 *  as published by the Free Software Foundation; either version
 | 
			
		||||
 *  2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
#include <asm/tlbflush.h>
 | 
			
		||||
#include <asm/mmu.h>
 | 
			
		||||
 | 
			
		||||
extern void mapin_ram(void);
 | 
			
		||||
extern int map_page(unsigned long va, phys_addr_t pa, int flags);
 | 
			
		||||
extern void setbat(int index, unsigned long virt, unsigned long phys,
 | 
			
		||||
		   unsigned int size, int flags);
 | 
			
		||||
extern void reserve_phys_mem(unsigned long start, unsigned long size);
 | 
			
		||||
extern void settlbcam(int index, unsigned long virt, phys_addr_t phys,
 | 
			
		||||
		      unsigned int size, int flags, unsigned int pid);
 | 
			
		||||
extern void invalidate_tlbcam_entry(int index);
 | 
			
		||||
 | 
			
		||||
extern int __map_without_bats;
 | 
			
		||||
extern unsigned long ioremap_base;
 | 
			
		||||
extern unsigned long ioremap_bot;
 | 
			
		||||
extern unsigned int rtas_data, rtas_size;
 | 
			
		||||
 | 
			
		||||
extern unsigned long total_memory;
 | 
			
		||||
extern unsigned long total_lowmem;
 | 
			
		||||
extern int mem_init_done;
 | 
			
		||||
 | 
			
		||||
extern PTE *Hash, *Hash_end;
 | 
			
		||||
extern unsigned long Hash_size, Hash_mask;
 | 
			
		||||
 | 
			
		||||
extern unsigned int num_tlbcam_entries;
 | 
			
		||||
 | 
			
		||||
/* ...and now those things that may be slightly different between processor
 | 
			
		||||
 * architectures.  -- Dan
 | 
			
		||||
 */
 | 
			
		||||
#if defined(CONFIG_8xx)
 | 
			
		||||
#define flush_HPTE(X, va, pg)	_tlbie(va)
 | 
			
		||||
#define MMU_init_hw()		do { } while(0)
 | 
			
		||||
#define mmu_mapin_ram()		(0UL)
 | 
			
		||||
 | 
			
		||||
#elif defined(CONFIG_4xx)
 | 
			
		||||
#define flush_HPTE(X, va, pg)	_tlbie(va)
 | 
			
		||||
extern void MMU_init_hw(void);
 | 
			
		||||
extern unsigned long mmu_mapin_ram(void);
 | 
			
		||||
 | 
			
		||||
#elif defined(CONFIG_FSL_BOOKE)
 | 
			
		||||
#define flush_HPTE(X, va, pg)	_tlbie(va)
 | 
			
		||||
extern void MMU_init_hw(void);
 | 
			
		||||
extern unsigned long mmu_mapin_ram(void);
 | 
			
		||||
extern void adjust_total_lowmem(void);
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
/* anything except 4xx or 8xx */
 | 
			
		||||
extern void MMU_init_hw(void);
 | 
			
		||||
extern unsigned long mmu_mapin_ram(void);
 | 
			
		||||
 | 
			
		||||
/* Be careful....this needs to be updated if we ever encounter 603 SMPs,
 | 
			
		||||
 * which includes all new 82xx processors.  We need tlbie/tlbsync here
 | 
			
		||||
 * in that case (I think). -- Dan.
 | 
			
		||||
 */
 | 
			
		||||
static inline void flush_HPTE(unsigned context, unsigned long va,
 | 
			
		||||
			      unsigned long pdval)
 | 
			
		||||
{
 | 
			
		||||
	if ((Hash != 0) &&
 | 
			
		||||
	    cpu_has_feature(CPU_FTR_HPTE_TABLE))
 | 
			
		||||
		flush_hash_pages(0, va, pdval, 1);
 | 
			
		||||
	else
 | 
			
		||||
		_tlbie(va);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										470
									
								
								arch/powerpc/mm/pgtable.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										470
									
								
								arch/powerpc/mm/pgtable.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,470 @@
 | 
			
		||||
/*
 | 
			
		||||
 * This file contains the routines setting up the linux page tables.
 | 
			
		||||
 *  -- paulus
 | 
			
		||||
 *
 | 
			
		||||
 *  Derived from arch/ppc/mm/init.c:
 | 
			
		||||
 *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
 | 
			
		||||
 *
 | 
			
		||||
 *  Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au)
 | 
			
		||||
 *  and Cort Dougan (PReP) (cort@cs.nmt.edu)
 | 
			
		||||
 *    Copyright (C) 1996 Paul Mackerras
 | 
			
		||||
 *  Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk).
 | 
			
		||||
 *
 | 
			
		||||
 *  Derived from "arch/i386/mm/init.c"
 | 
			
		||||
 *    Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or
 | 
			
		||||
 *  modify it under the terms of the GNU General Public License
 | 
			
		||||
 *  as published by the Free Software Foundation; either version
 | 
			
		||||
 *  2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/config.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/types.h>
 | 
			
		||||
#include <linux/mm.h>
 | 
			
		||||
#include <linux/vmalloc.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/highmem.h>
 | 
			
		||||
 | 
			
		||||
#include <asm/pgtable.h>
 | 
			
		||||
#include <asm/pgalloc.h>
 | 
			
		||||
#include <asm/io.h>
 | 
			
		||||
 | 
			
		||||
#include "mmu_decl.h"
 | 
			
		||||
 | 
			
		||||
unsigned long ioremap_base;
 | 
			
		||||
unsigned long ioremap_bot;
 | 
			
		||||
int io_bat_index;
 | 
			
		||||
 | 
			
		||||
#if defined(CONFIG_6xx) || defined(CONFIG_POWER3)
 | 
			
		||||
#define HAVE_BATS	1
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if defined(CONFIG_FSL_BOOKE)
 | 
			
		||||
#define HAVE_TLBCAM	1
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
extern char etext[], _stext[];
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_SMP
 | 
			
		||||
extern void hash_page_sync(void);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_BATS
 | 
			
		||||
extern unsigned long v_mapped_by_bats(unsigned long va);
 | 
			
		||||
extern unsigned long p_mapped_by_bats(unsigned long pa);
 | 
			
		||||
void setbat(int index, unsigned long virt, unsigned long phys,
 | 
			
		||||
	    unsigned int size, int flags);
 | 
			
		||||
 | 
			
		||||
#else /* !HAVE_BATS */
 | 
			
		||||
#define v_mapped_by_bats(x)	(0UL)
 | 
			
		||||
#define p_mapped_by_bats(x)	(0UL)
 | 
			
		||||
#endif /* HAVE_BATS */
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_TLBCAM
 | 
			
		||||
extern unsigned int tlbcam_index;
 | 
			
		||||
extern unsigned long v_mapped_by_tlbcam(unsigned long va);
 | 
			
		||||
extern unsigned long p_mapped_by_tlbcam(unsigned long pa);
 | 
			
		||||
#else /* !HAVE_TLBCAM */
 | 
			
		||||
#define v_mapped_by_tlbcam(x)	(0UL)
 | 
			
		||||
#define p_mapped_by_tlbcam(x)	(0UL)
 | 
			
		||||
#endif /* HAVE_TLBCAM */
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_PTE_64BIT
 | 
			
		||||
/* 44x uses an 8kB pgdir because it has 8-byte Linux PTEs. */
 | 
			
		||||
#define PGDIR_ORDER	1
 | 
			
		||||
#else
 | 
			
		||||
#define PGDIR_ORDER	0
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
pgd_t *pgd_alloc(struct mm_struct *mm)
 | 
			
		||||
{
 | 
			
		||||
	pgd_t *ret;
 | 
			
		||||
 | 
			
		||||
	ret = (pgd_t *)__get_free_pages(GFP_KERNEL|__GFP_ZERO, PGDIR_ORDER);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void pgd_free(pgd_t *pgd)
 | 
			
		||||
{
 | 
			
		||||
	free_pages((unsigned long)pgd, PGDIR_ORDER);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address)
 | 
			
		||||
{
 | 
			
		||||
	pte_t *pte;
 | 
			
		||||
	extern int mem_init_done;
 | 
			
		||||
	extern void *early_get_page(void);
 | 
			
		||||
 | 
			
		||||
	if (mem_init_done) {
 | 
			
		||||
		pte = (pte_t *)__get_free_page(GFP_KERNEL|__GFP_REPEAT|__GFP_ZERO);
 | 
			
		||||
	} else {
 | 
			
		||||
		pte = (pte_t *)early_get_page();
 | 
			
		||||
		if (pte)
 | 
			
		||||
			clear_page(pte);
 | 
			
		||||
	}
 | 
			
		||||
	return pte;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct page *pte_alloc_one(struct mm_struct *mm, unsigned long address)
 | 
			
		||||
{
 | 
			
		||||
	struct page *ptepage;
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_HIGHPTE
 | 
			
		||||
	int flags = GFP_KERNEL | __GFP_HIGHMEM | __GFP_REPEAT;
 | 
			
		||||
#else
 | 
			
		||||
	int flags = GFP_KERNEL | __GFP_REPEAT;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	ptepage = alloc_pages(flags, 0);
 | 
			
		||||
	if (ptepage)
 | 
			
		||||
		clear_highpage(ptepage);
 | 
			
		||||
	return ptepage;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void pte_free_kernel(pte_t *pte)
 | 
			
		||||
{
 | 
			
		||||
#ifdef CONFIG_SMP
 | 
			
		||||
	hash_page_sync();
 | 
			
		||||
#endif
 | 
			
		||||
	free_page((unsigned long)pte);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void pte_free(struct page *ptepage)
 | 
			
		||||
{
 | 
			
		||||
#ifdef CONFIG_SMP
 | 
			
		||||
	hash_page_sync();
 | 
			
		||||
#endif
 | 
			
		||||
	__free_page(ptepage);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifndef CONFIG_PHYS_64BIT
 | 
			
		||||
void __iomem *
 | 
			
		||||
ioremap(phys_addr_t addr, unsigned long size)
 | 
			
		||||
{
 | 
			
		||||
	return __ioremap(addr, size, _PAGE_NO_CACHE);
 | 
			
		||||
}
 | 
			
		||||
#else /* CONFIG_PHYS_64BIT */
 | 
			
		||||
void __iomem *
 | 
			
		||||
ioremap64(unsigned long long addr, unsigned long size)
 | 
			
		||||
{
 | 
			
		||||
	return __ioremap(addr, size, _PAGE_NO_CACHE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void __iomem *
 | 
			
		||||
ioremap(phys_addr_t addr, unsigned long size)
 | 
			
		||||
{
 | 
			
		||||
	phys_addr_t addr64 = fixup_bigphys_addr(addr, size);
 | 
			
		||||
 | 
			
		||||
	return ioremap64(addr64, size);
 | 
			
		||||
}
 | 
			
		||||
#endif /* CONFIG_PHYS_64BIT */
 | 
			
		||||
 | 
			
		||||
void __iomem *
 | 
			
		||||
__ioremap(phys_addr_t addr, unsigned long size, unsigned long flags)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long v, i;
 | 
			
		||||
	phys_addr_t p;
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Choose an address to map it to.
 | 
			
		||||
	 * Once the vmalloc system is running, we use it.
 | 
			
		||||
	 * Before then, we use space going down from ioremap_base
 | 
			
		||||
	 * (ioremap_bot records where we're up to).
 | 
			
		||||
	 */
 | 
			
		||||
	p = addr & PAGE_MASK;
 | 
			
		||||
	size = PAGE_ALIGN(addr + size) - p;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * If the address lies within the first 16 MB, assume it's in ISA
 | 
			
		||||
	 * memory space
 | 
			
		||||
	 */
 | 
			
		||||
	if (p < 16*1024*1024)
 | 
			
		||||
		p += _ISA_MEM_BASE;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Don't allow anybody to remap normal RAM that we're using.
 | 
			
		||||
	 * mem_init() sets high_memory so only do the check after that.
 | 
			
		||||
	 */
 | 
			
		||||
	if ( mem_init_done && (p < virt_to_phys(high_memory)) )
 | 
			
		||||
	{
 | 
			
		||||
		printk("__ioremap(): phys addr "PHYS_FMT" is RAM lr %p\n", p,
 | 
			
		||||
		       __builtin_return_address(0));
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (size == 0)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Is it already mapped?  Perhaps overlapped by a previous
 | 
			
		||||
	 * BAT mapping.  If the whole area is mapped then we're done,
 | 
			
		||||
	 * otherwise remap it since we want to keep the virt addrs for
 | 
			
		||||
	 * each request contiguous.
 | 
			
		||||
	 *
 | 
			
		||||
	 * We make the assumption here that if the bottom and top
 | 
			
		||||
	 * of the range we want are mapped then it's mapped to the
 | 
			
		||||
	 * same virt address (and this is contiguous).
 | 
			
		||||
	 *  -- Cort
 | 
			
		||||
	 */
 | 
			
		||||
	if ((v = p_mapped_by_bats(p)) /*&& p_mapped_by_bats(p+size-1)*/ )
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	if ((v = p_mapped_by_tlbcam(p)))
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	if (mem_init_done) {
 | 
			
		||||
		struct vm_struct *area;
 | 
			
		||||
		area = get_vm_area(size, VM_IOREMAP);
 | 
			
		||||
		if (area == 0)
 | 
			
		||||
			return NULL;
 | 
			
		||||
		v = (unsigned long) area->addr;
 | 
			
		||||
	} else {
 | 
			
		||||
		v = (ioremap_bot -= size);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ((flags & _PAGE_PRESENT) == 0)
 | 
			
		||||
		flags |= _PAGE_KERNEL;
 | 
			
		||||
	if (flags & _PAGE_NO_CACHE)
 | 
			
		||||
		flags |= _PAGE_GUARDED;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Should check if it is a candidate for a BAT mapping
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
	err = 0;
 | 
			
		||||
	for (i = 0; i < size && err == 0; i += PAGE_SIZE)
 | 
			
		||||
		err = map_page(v+i, p+i, flags);
 | 
			
		||||
	if (err) {
 | 
			
		||||
		if (mem_init_done)
 | 
			
		||||
			vunmap((void *)v);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
	return (void __iomem *) (v + ((unsigned long)addr & ~PAGE_MASK));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void iounmap(volatile void __iomem *addr)
 | 
			
		||||
{
 | 
			
		||||
	/*
 | 
			
		||||
	 * If mapped by BATs then there is nothing to do.
 | 
			
		||||
	 * Calling vfree() generates a benign warning.
 | 
			
		||||
	 */
 | 
			
		||||
	if (v_mapped_by_bats((unsigned long)addr)) return;
 | 
			
		||||
 | 
			
		||||
	if (addr > high_memory && (unsigned long) addr < ioremap_bot)
 | 
			
		||||
		vunmap((void *) (PAGE_MASK & (unsigned long)addr));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void __iomem *ioport_map(unsigned long port, unsigned int len)
 | 
			
		||||
{
 | 
			
		||||
	return (void __iomem *) (port + _IO_BASE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ioport_unmap(void __iomem *addr)
 | 
			
		||||
{
 | 
			
		||||
	/* Nothing to do */
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(ioport_map);
 | 
			
		||||
EXPORT_SYMBOL(ioport_unmap);
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
map_page(unsigned long va, phys_addr_t pa, int flags)
 | 
			
		||||
{
 | 
			
		||||
	pmd_t *pd;
 | 
			
		||||
	pte_t *pg;
 | 
			
		||||
	int err = -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	spin_lock(&init_mm.page_table_lock);
 | 
			
		||||
	/* Use upper 10 bits of VA to index the first level map */
 | 
			
		||||
	pd = pmd_offset(pgd_offset_k(va), va);
 | 
			
		||||
	/* Use middle 10 bits of VA to index the second-level map */
 | 
			
		||||
	pg = pte_alloc_kernel(&init_mm, pd, va);
 | 
			
		||||
	if (pg != 0) {
 | 
			
		||||
		err = 0;
 | 
			
		||||
		set_pte_at(&init_mm, va, pg, pfn_pte(pa >> PAGE_SHIFT, __pgprot(flags)));
 | 
			
		||||
		if (mem_init_done)
 | 
			
		||||
			flush_HPTE(0, va, pmd_val(*pd));
 | 
			
		||||
	}
 | 
			
		||||
	spin_unlock(&init_mm.page_table_lock);
 | 
			
		||||
	return err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Map in all of physical memory starting at KERNELBASE.
 | 
			
		||||
 */
 | 
			
		||||
void __init mapin_ram(void)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long v, p, s, f;
 | 
			
		||||
 | 
			
		||||
	s = mmu_mapin_ram();
 | 
			
		||||
	v = KERNELBASE + s;
 | 
			
		||||
	p = PPC_MEMSTART + s;
 | 
			
		||||
	for (; s < total_lowmem; s += PAGE_SIZE) {
 | 
			
		||||
		if ((char *) v >= _stext && (char *) v < etext)
 | 
			
		||||
			f = _PAGE_RAM_TEXT;
 | 
			
		||||
		else
 | 
			
		||||
			f = _PAGE_RAM;
 | 
			
		||||
		map_page(v, p, f);
 | 
			
		||||
		v += PAGE_SIZE;
 | 
			
		||||
		p += PAGE_SIZE;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* is x a power of 2? */
 | 
			
		||||
#define is_power_of_2(x)	((x) != 0 && (((x) & ((x) - 1)) == 0))
 | 
			
		||||
 | 
			
		||||
/* is x a power of 4? */
 | 
			
		||||
#define is_power_of_4(x)	((x) != 0 && (((x) & (x-1)) == 0) && (ffs(x) & 1))
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Set up a mapping for a block of I/O.
 | 
			
		||||
 * virt, phys, size must all be page-aligned.
 | 
			
		||||
 * This should only be called before ioremap is called.
 | 
			
		||||
 */
 | 
			
		||||
void __init io_block_mapping(unsigned long virt, phys_addr_t phys,
 | 
			
		||||
			     unsigned int size, int flags)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	if (virt > KERNELBASE && virt < ioremap_bot)
 | 
			
		||||
		ioremap_bot = ioremap_base = virt;
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_BATS
 | 
			
		||||
	/*
 | 
			
		||||
	 * Use a BAT for this if possible...
 | 
			
		||||
	 */
 | 
			
		||||
	if (io_bat_index < 2 && is_power_of_2(size)
 | 
			
		||||
	    && (virt & (size - 1)) == 0 && (phys & (size - 1)) == 0) {
 | 
			
		||||
		setbat(io_bat_index, virt, phys, size, flags);
 | 
			
		||||
		++io_bat_index;
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
#endif /* HAVE_BATS */
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_TLBCAM
 | 
			
		||||
	/*
 | 
			
		||||
	 * Use a CAM for this if possible...
 | 
			
		||||
	 */
 | 
			
		||||
	if (tlbcam_index < num_tlbcam_entries && is_power_of_4(size)
 | 
			
		||||
	    && (virt & (size - 1)) == 0 && (phys & (size - 1)) == 0) {
 | 
			
		||||
		settlbcam(tlbcam_index, virt, phys, size, flags, 0);
 | 
			
		||||
		++tlbcam_index;
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
#endif /* HAVE_TLBCAM */
 | 
			
		||||
 | 
			
		||||
	/* No BATs available, put it in the page tables. */
 | 
			
		||||
	for (i = 0; i < size; i += PAGE_SIZE)
 | 
			
		||||
		map_page(virt + i, phys + i, flags);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Scan the real Linux page tables and return a PTE pointer for
 | 
			
		||||
 * a virtual address in a context.
 | 
			
		||||
 * Returns true (1) if PTE was found, zero otherwise.  The pointer to
 | 
			
		||||
 * the PTE pointer is unmodified if PTE is not found.
 | 
			
		||||
 */
 | 
			
		||||
int
 | 
			
		||||
get_pteptr(struct mm_struct *mm, unsigned long addr, pte_t **ptep)
 | 
			
		||||
{
 | 
			
		||||
        pgd_t	*pgd;
 | 
			
		||||
        pmd_t	*pmd;
 | 
			
		||||
        pte_t	*pte;
 | 
			
		||||
        int     retval = 0;
 | 
			
		||||
 | 
			
		||||
        pgd = pgd_offset(mm, addr & PAGE_MASK);
 | 
			
		||||
        if (pgd) {
 | 
			
		||||
                pmd = pmd_offset(pgd, addr & PAGE_MASK);
 | 
			
		||||
                if (pmd_present(*pmd)) {
 | 
			
		||||
                        pte = pte_offset_map(pmd, addr & PAGE_MASK);
 | 
			
		||||
                        if (pte) {
 | 
			
		||||
				retval = 1;
 | 
			
		||||
				*ptep = pte;
 | 
			
		||||
				/* XXX caller needs to do pte_unmap, yuck */
 | 
			
		||||
                        }
 | 
			
		||||
                }
 | 
			
		||||
        }
 | 
			
		||||
        return(retval);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Find physical address for this virtual address.  Normally used by
 | 
			
		||||
 * I/O functions, but anyone can call it.
 | 
			
		||||
 */
 | 
			
		||||
unsigned long iopa(unsigned long addr)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long pa;
 | 
			
		||||
 | 
			
		||||
	/* I don't know why this won't work on PMacs or CHRP.  It
 | 
			
		||||
	 * appears there is some bug, or there is some implicit
 | 
			
		||||
	 * mapping done not properly represented by BATs or in page
 | 
			
		||||
	 * tables.......I am actively working on resolving this, but
 | 
			
		||||
	 * can't hold up other stuff.  -- Dan
 | 
			
		||||
	 */
 | 
			
		||||
	pte_t *pte;
 | 
			
		||||
	struct mm_struct *mm;
 | 
			
		||||
 | 
			
		||||
	/* Check the BATs */
 | 
			
		||||
	pa = v_mapped_by_bats(addr);
 | 
			
		||||
	if (pa)
 | 
			
		||||
		return pa;
 | 
			
		||||
 | 
			
		||||
	/* Allow mapping of user addresses (within the thread)
 | 
			
		||||
	 * for DMA if necessary.
 | 
			
		||||
	 */
 | 
			
		||||
	if (addr < TASK_SIZE)
 | 
			
		||||
		mm = current->mm;
 | 
			
		||||
	else
 | 
			
		||||
		mm = &init_mm;
 | 
			
		||||
 | 
			
		||||
	pa = 0;
 | 
			
		||||
	if (get_pteptr(mm, addr, &pte)) {
 | 
			
		||||
		pa = (pte_val(*pte) & PAGE_MASK) | (addr & ~PAGE_MASK);
 | 
			
		||||
		pte_unmap(pte);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return(pa);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* This is will find the virtual address for a physical one....
 | 
			
		||||
 * Swiped from APUS, could be dangerous :-).
 | 
			
		||||
 * This is only a placeholder until I really find a way to make this
 | 
			
		||||
 * work.  -- Dan
 | 
			
		||||
 */
 | 
			
		||||
unsigned long
 | 
			
		||||
mm_ptov (unsigned long paddr)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long ret;
 | 
			
		||||
#if 0
 | 
			
		||||
	if (paddr < 16*1024*1024)
 | 
			
		||||
		ret = ZTWO_VADDR(paddr);
 | 
			
		||||
	else {
 | 
			
		||||
		int i;
 | 
			
		||||
 | 
			
		||||
		for (i = 0; i < kmap_chunk_count;){
 | 
			
		||||
			unsigned long phys = kmap_chunks[i++];
 | 
			
		||||
			unsigned long size = kmap_chunks[i++];
 | 
			
		||||
			unsigned long virt = kmap_chunks[i++];
 | 
			
		||||
			if (paddr >= phys
 | 
			
		||||
			    && paddr < (phys + size)){
 | 
			
		||||
				ret = virt + paddr - phys;
 | 
			
		||||
				goto exit;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	
 | 
			
		||||
		ret = (unsigned long) __va(paddr);
 | 
			
		||||
	}
 | 
			
		||||
exit:
 | 
			
		||||
#ifdef DEBUGPV
 | 
			
		||||
	printk ("PTOV(%lx)=%lx\n", paddr, ret);
 | 
			
		||||
#endif
 | 
			
		||||
#else
 | 
			
		||||
	ret = (unsigned long)paddr + KERNELBASE;
 | 
			
		||||
#endif
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										357
									
								
								arch/powerpc/mm/pgtable64.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										357
									
								
								arch/powerpc/mm/pgtable64.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,357 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  This file contains ioremap and related functions for 64-bit machines.
 | 
			
		||||
 *
 | 
			
		||||
 *  Derived from arch/ppc64/mm/init.c
 | 
			
		||||
 *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
 | 
			
		||||
 *
 | 
			
		||||
 *  Modifications by Paul Mackerras (PowerMac) (paulus@samba.org)
 | 
			
		||||
 *  and Cort Dougan (PReP) (cort@cs.nmt.edu)
 | 
			
		||||
 *    Copyright (C) 1996 Paul Mackerras
 | 
			
		||||
 *  Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk).
 | 
			
		||||
 *
 | 
			
		||||
 *  Derived from "arch/i386/mm/init.c"
 | 
			
		||||
 *    Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
 | 
			
		||||
 *
 | 
			
		||||
 *  Dave Engebretsen <engebret@us.ibm.com>
 | 
			
		||||
 *      Rework for PPC64 port.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or
 | 
			
		||||
 *  modify it under the terms of the GNU General Public License
 | 
			
		||||
 *  as published by the Free Software Foundation; either version
 | 
			
		||||
 *  2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/config.h>
 | 
			
		||||
#include <linux/signal.h>
 | 
			
		||||
#include <linux/sched.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/errno.h>
 | 
			
		||||
#include <linux/string.h>
 | 
			
		||||
#include <linux/types.h>
 | 
			
		||||
#include <linux/mman.h>
 | 
			
		||||
#include <linux/mm.h>
 | 
			
		||||
#include <linux/swap.h>
 | 
			
		||||
#include <linux/stddef.h>
 | 
			
		||||
#include <linux/vmalloc.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/delay.h>
 | 
			
		||||
#include <linux/bootmem.h>
 | 
			
		||||
#include <linux/highmem.h>
 | 
			
		||||
#include <linux/idr.h>
 | 
			
		||||
#include <linux/nodemask.h>
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
 | 
			
		||||
#include <asm/pgalloc.h>
 | 
			
		||||
#include <asm/page.h>
 | 
			
		||||
#include <asm/prom.h>
 | 
			
		||||
#include <asm/lmb.h>
 | 
			
		||||
#include <asm/rtas.h>
 | 
			
		||||
#include <asm/io.h>
 | 
			
		||||
#include <asm/mmu_context.h>
 | 
			
		||||
#include <asm/pgtable.h>
 | 
			
		||||
#include <asm/mmu.h>
 | 
			
		||||
#include <asm/uaccess.h>
 | 
			
		||||
#include <asm/smp.h>
 | 
			
		||||
#include <asm/machdep.h>
 | 
			
		||||
#include <asm/tlb.h>
 | 
			
		||||
#include <asm/eeh.h>
 | 
			
		||||
#include <asm/processor.h>
 | 
			
		||||
#include <asm/mmzone.h>
 | 
			
		||||
#include <asm/cputable.h>
 | 
			
		||||
#include <asm/ppcdebug.h>
 | 
			
		||||
#include <asm/sections.h>
 | 
			
		||||
#include <asm/system.h>
 | 
			
		||||
#include <asm/iommu.h>
 | 
			
		||||
#include <asm/abs_addr.h>
 | 
			
		||||
#include <asm/vdso.h>
 | 
			
		||||
#include <asm/imalloc.h>
 | 
			
		||||
 | 
			
		||||
#if PGTABLE_RANGE > USER_VSID_RANGE
 | 
			
		||||
#warning Limited user VSID range means pagetable space is wasted
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if (TASK_SIZE_USER64 < PGTABLE_RANGE) && (TASK_SIZE_USER64 < USER_VSID_RANGE)
 | 
			
		||||
#warning TASK_SIZE is smaller than it needs to be.
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
int mem_init_done;
 | 
			
		||||
unsigned long ioremap_bot = IMALLOC_BASE;
 | 
			
		||||
static unsigned long phbs_io_bot = PHBS_IO_BASE;
 | 
			
		||||
 | 
			
		||||
extern pgd_t swapper_pg_dir[];
 | 
			
		||||
extern struct task_struct *current_set[NR_CPUS];
 | 
			
		||||
 | 
			
		||||
unsigned long klimit = (unsigned long)_end;
 | 
			
		||||
 | 
			
		||||
/* max amount of RAM to use */
 | 
			
		||||
unsigned long __max_memory;
 | 
			
		||||
 | 
			
		||||
/* info on what we think the IO hole is */
 | 
			
		||||
unsigned long 	io_hole_start;
 | 
			
		||||
unsigned long	io_hole_size;
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_PPC_ISERIES
 | 
			
		||||
 | 
			
		||||
void __iomem *ioremap(unsigned long addr, unsigned long size)
 | 
			
		||||
{
 | 
			
		||||
	return (void __iomem *)addr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extern void __iomem *__ioremap(unsigned long addr, unsigned long size,
 | 
			
		||||
		       unsigned long flags)
 | 
			
		||||
{
 | 
			
		||||
	return (void __iomem *)addr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void iounmap(volatile void __iomem *addr)
 | 
			
		||||
{
 | 
			
		||||
	return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * map_io_page currently only called by __ioremap
 | 
			
		||||
 * map_io_page adds an entry to the ioremap page table
 | 
			
		||||
 * and adds an entry to the HPT, possibly bolting it
 | 
			
		||||
 */
 | 
			
		||||
static int map_io_page(unsigned long ea, unsigned long pa, int flags)
 | 
			
		||||
{
 | 
			
		||||
	pgd_t *pgdp;
 | 
			
		||||
	pud_t *pudp;
 | 
			
		||||
	pmd_t *pmdp;
 | 
			
		||||
	pte_t *ptep;
 | 
			
		||||
	unsigned long vsid;
 | 
			
		||||
 | 
			
		||||
	if (mem_init_done) {
 | 
			
		||||
		spin_lock(&init_mm.page_table_lock);
 | 
			
		||||
		pgdp = pgd_offset_k(ea);
 | 
			
		||||
		pudp = pud_alloc(&init_mm, pgdp, ea);
 | 
			
		||||
		if (!pudp)
 | 
			
		||||
			return -ENOMEM;
 | 
			
		||||
		pmdp = pmd_alloc(&init_mm, pudp, ea);
 | 
			
		||||
		if (!pmdp)
 | 
			
		||||
			return -ENOMEM;
 | 
			
		||||
		ptep = pte_alloc_kernel(&init_mm, pmdp, ea);
 | 
			
		||||
		if (!ptep)
 | 
			
		||||
			return -ENOMEM;
 | 
			
		||||
		set_pte_at(&init_mm, ea, ptep, pfn_pte(pa >> PAGE_SHIFT,
 | 
			
		||||
							  __pgprot(flags)));
 | 
			
		||||
		spin_unlock(&init_mm.page_table_lock);
 | 
			
		||||
	} else {
 | 
			
		||||
		unsigned long va, vpn, hash, hpteg;
 | 
			
		||||
 | 
			
		||||
		/*
 | 
			
		||||
		 * If the mm subsystem is not fully up, we cannot create a
 | 
			
		||||
		 * linux page table entry for this mapping.  Simply bolt an
 | 
			
		||||
		 * entry in the hardware page table.
 | 
			
		||||
		 */
 | 
			
		||||
		vsid = get_kernel_vsid(ea);
 | 
			
		||||
		va = (vsid << 28) | (ea & 0xFFFFFFF);
 | 
			
		||||
		vpn = va >> PAGE_SHIFT;
 | 
			
		||||
 | 
			
		||||
		hash = hpt_hash(vpn, 0);
 | 
			
		||||
 | 
			
		||||
		hpteg = ((hash & htab_hash_mask) * HPTES_PER_GROUP);
 | 
			
		||||
 | 
			
		||||
		/* Panic if a pte grpup is full */
 | 
			
		||||
		if (ppc_md.hpte_insert(hpteg, va, pa >> PAGE_SHIFT,
 | 
			
		||||
				       HPTE_V_BOLTED,
 | 
			
		||||
				       _PAGE_NO_CACHE|_PAGE_GUARDED|PP_RWXX)
 | 
			
		||||
		    == -1) {
 | 
			
		||||
			panic("map_io_page: could not insert mapping");
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void __iomem * __ioremap_com(unsigned long addr, unsigned long pa,
 | 
			
		||||
			    unsigned long ea, unsigned long size,
 | 
			
		||||
			    unsigned long flags)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long i;
 | 
			
		||||
 | 
			
		||||
	if ((flags & _PAGE_PRESENT) == 0)
 | 
			
		||||
		flags |= pgprot_val(PAGE_KERNEL);
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < size; i += PAGE_SIZE)
 | 
			
		||||
		if (map_io_page(ea+i, pa+i, flags))
 | 
			
		||||
			return NULL;
 | 
			
		||||
 | 
			
		||||
	return (void __iomem *) (ea + (addr & ~PAGE_MASK));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void __iomem *
 | 
			
		||||
ioremap(unsigned long addr, unsigned long size)
 | 
			
		||||
{
 | 
			
		||||
	return __ioremap(addr, size, _PAGE_NO_CACHE | _PAGE_GUARDED);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void __iomem * __ioremap(unsigned long addr, unsigned long size,
 | 
			
		||||
			 unsigned long flags)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long pa, ea;
 | 
			
		||||
	void __iomem *ret;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Choose an address to map it to.
 | 
			
		||||
	 * Once the imalloc system is running, we use it.
 | 
			
		||||
	 * Before that, we map using addresses going
 | 
			
		||||
	 * up from ioremap_bot.  imalloc will use
 | 
			
		||||
	 * the addresses from ioremap_bot through
 | 
			
		||||
	 * IMALLOC_END
 | 
			
		||||
	 * 
 | 
			
		||||
	 */
 | 
			
		||||
	pa = addr & PAGE_MASK;
 | 
			
		||||
	size = PAGE_ALIGN(addr + size) - pa;
 | 
			
		||||
 | 
			
		||||
	if (size == 0)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	if (mem_init_done) {
 | 
			
		||||
		struct vm_struct *area;
 | 
			
		||||
		area = im_get_free_area(size);
 | 
			
		||||
		if (area == NULL)
 | 
			
		||||
			return NULL;
 | 
			
		||||
		ea = (unsigned long)(area->addr);
 | 
			
		||||
		ret = __ioremap_com(addr, pa, ea, size, flags);
 | 
			
		||||
		if (!ret)
 | 
			
		||||
			im_free(area->addr);
 | 
			
		||||
	} else {
 | 
			
		||||
		ea = ioremap_bot;
 | 
			
		||||
		ret = __ioremap_com(addr, pa, ea, size, flags);
 | 
			
		||||
		if (ret)
 | 
			
		||||
			ioremap_bot += size;
 | 
			
		||||
	}
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define IS_PAGE_ALIGNED(_val) ((_val) == ((_val) & PAGE_MASK))
 | 
			
		||||
 | 
			
		||||
int __ioremap_explicit(unsigned long pa, unsigned long ea,
 | 
			
		||||
		       unsigned long size, unsigned long flags)
 | 
			
		||||
{
 | 
			
		||||
	struct vm_struct *area;
 | 
			
		||||
	void __iomem *ret;
 | 
			
		||||
	
 | 
			
		||||
	/* For now, require page-aligned values for pa, ea, and size */
 | 
			
		||||
	if (!IS_PAGE_ALIGNED(pa) || !IS_PAGE_ALIGNED(ea) ||
 | 
			
		||||
	    !IS_PAGE_ALIGNED(size)) {
 | 
			
		||||
		printk(KERN_ERR	"unaligned value in %s\n", __FUNCTION__);
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	if (!mem_init_done) {
 | 
			
		||||
		/* Two things to consider in this case:
 | 
			
		||||
		 * 1) No records will be kept (imalloc, etc) that the region
 | 
			
		||||
		 *    has been remapped
 | 
			
		||||
		 * 2) It won't be easy to iounmap() the region later (because
 | 
			
		||||
		 *    of 1)
 | 
			
		||||
		 */
 | 
			
		||||
		;
 | 
			
		||||
	} else {
 | 
			
		||||
		area = im_get_area(ea, size,
 | 
			
		||||
			IM_REGION_UNUSED|IM_REGION_SUBSET|IM_REGION_EXISTS);
 | 
			
		||||
		if (area == NULL) {
 | 
			
		||||
			/* Expected when PHB-dlpar is in play */
 | 
			
		||||
			return 1;
 | 
			
		||||
		}
 | 
			
		||||
		if (ea != (unsigned long) area->addr) {
 | 
			
		||||
			printk(KERN_ERR "unexpected addr return from "
 | 
			
		||||
			       "im_get_area\n");
 | 
			
		||||
			return 1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	ret = __ioremap_com(pa, pa, ea, size, flags);
 | 
			
		||||
	if (ret == NULL) {
 | 
			
		||||
		printk(KERN_ERR "ioremap_explicit() allocation failure !\n");
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
	if (ret != (void *) ea) {
 | 
			
		||||
		printk(KERN_ERR "__ioremap_com() returned unexpected addr\n");
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*  
 | 
			
		||||
 * Unmap an IO region and remove it from imalloc'd list.
 | 
			
		||||
 * Access to IO memory should be serialized by driver.
 | 
			
		||||
 * This code is modeled after vmalloc code - unmap_vm_area()
 | 
			
		||||
 *
 | 
			
		||||
 * XXX	what about calls before mem_init_done (ie python_countermeasures())
 | 
			
		||||
 */
 | 
			
		||||
void iounmap(volatile void __iomem *token)
 | 
			
		||||
{
 | 
			
		||||
	void *addr;
 | 
			
		||||
 | 
			
		||||
	if (!mem_init_done)
 | 
			
		||||
		return;
 | 
			
		||||
	
 | 
			
		||||
	addr = (void *) ((unsigned long __force) token & PAGE_MASK);
 | 
			
		||||
 | 
			
		||||
	im_free(addr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int iounmap_subset_regions(unsigned long addr, unsigned long size)
 | 
			
		||||
{
 | 
			
		||||
	struct vm_struct *area;
 | 
			
		||||
 | 
			
		||||
	/* Check whether subsets of this region exist */
 | 
			
		||||
	area = im_get_area(addr, size, IM_REGION_SUPERSET);
 | 
			
		||||
	if (area == NULL)
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
	while (area) {
 | 
			
		||||
		iounmap((void __iomem *) area->addr);
 | 
			
		||||
		area = im_get_area(addr, size,
 | 
			
		||||
				IM_REGION_SUPERSET);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int iounmap_explicit(volatile void __iomem *start, unsigned long size)
 | 
			
		||||
{
 | 
			
		||||
	struct vm_struct *area;
 | 
			
		||||
	unsigned long addr;
 | 
			
		||||
	int rc;
 | 
			
		||||
	
 | 
			
		||||
	addr = (unsigned long __force) start & PAGE_MASK;
 | 
			
		||||
 | 
			
		||||
	/* Verify that the region either exists or is a subset of an existing
 | 
			
		||||
	 * region.  In the latter case, split the parent region to create 
 | 
			
		||||
	 * the exact region 
 | 
			
		||||
	 */
 | 
			
		||||
	area = im_get_area(addr, size, 
 | 
			
		||||
			    IM_REGION_EXISTS | IM_REGION_SUBSET);
 | 
			
		||||
	if (area == NULL) {
 | 
			
		||||
		/* Determine whether subset regions exist.  If so, unmap */
 | 
			
		||||
		rc = iounmap_subset_regions(addr, size);
 | 
			
		||||
		if (rc) {
 | 
			
		||||
			printk(KERN_ERR
 | 
			
		||||
			       "%s() cannot unmap nonexistent range 0x%lx\n",
 | 
			
		||||
 				__FUNCTION__, addr);
 | 
			
		||||
			return 1;
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		iounmap((void __iomem *) area->addr);
 | 
			
		||||
	}
 | 
			
		||||
	/*
 | 
			
		||||
	 * FIXME! This can't be right:
 | 
			
		||||
	iounmap(area->addr);
 | 
			
		||||
	 * Maybe it should be "iounmap(area);"
 | 
			
		||||
	 */
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
EXPORT_SYMBOL(ioremap);
 | 
			
		||||
EXPORT_SYMBOL(__ioremap);
 | 
			
		||||
EXPORT_SYMBOL(iounmap);
 | 
			
		||||
							
								
								
									
										296
									
								
								arch/powerpc/mm/ppc_mmu.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										296
									
								
								arch/powerpc/mm/ppc_mmu.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,296 @@
 | 
			
		||||
/*
 | 
			
		||||
 * This file contains the routines for handling the MMU on those
 | 
			
		||||
 * PowerPC implementations where the MMU substantially follows the
 | 
			
		||||
 * architecture specification.  This includes the 6xx, 7xx, 7xxx,
 | 
			
		||||
 * 8260, and POWER3 implementations but excludes the 8xx and 4xx.
 | 
			
		||||
 *  -- paulus
 | 
			
		||||
 *
 | 
			
		||||
 *  Derived from arch/ppc/mm/init.c:
 | 
			
		||||
 *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
 | 
			
		||||
 *
 | 
			
		||||
 *  Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au)
 | 
			
		||||
 *  and Cort Dougan (PReP) (cort@cs.nmt.edu)
 | 
			
		||||
 *    Copyright (C) 1996 Paul Mackerras
 | 
			
		||||
 *  Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk).
 | 
			
		||||
 *
 | 
			
		||||
 *  Derived from "arch/i386/mm/init.c"
 | 
			
		||||
 *    Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or
 | 
			
		||||
 *  modify it under the terms of the GNU General Public License
 | 
			
		||||
 *  as published by the Free Software Foundation; either version
 | 
			
		||||
 *  2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/config.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/mm.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/highmem.h>
 | 
			
		||||
 | 
			
		||||
#include <asm/prom.h>
 | 
			
		||||
#include <asm/mmu.h>
 | 
			
		||||
#include <asm/machdep.h>
 | 
			
		||||
 | 
			
		||||
#include "mmu_decl.h"
 | 
			
		||||
#include "mem_pieces.h"
 | 
			
		||||
 | 
			
		||||
PTE *Hash, *Hash_end;
 | 
			
		||||
unsigned long Hash_size, Hash_mask;
 | 
			
		||||
unsigned long _SDR1;
 | 
			
		||||
 | 
			
		||||
union ubat {			/* BAT register values to be loaded */
 | 
			
		||||
	BAT	bat;
 | 
			
		||||
#ifdef CONFIG_PPC64BRIDGE
 | 
			
		||||
	u64	word[2];
 | 
			
		||||
#else
 | 
			
		||||
	u32	word[2];
 | 
			
		||||
#endif
 | 
			
		||||
} BATS[4][2];			/* 4 pairs of IBAT, DBAT */
 | 
			
		||||
 | 
			
		||||
struct batrange {		/* stores address ranges mapped by BATs */
 | 
			
		||||
	unsigned long start;
 | 
			
		||||
	unsigned long limit;
 | 
			
		||||
	unsigned long phys;
 | 
			
		||||
} bat_addrs[4];
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Return PA for this VA if it is mapped by a BAT, or 0
 | 
			
		||||
 */
 | 
			
		||||
unsigned long v_mapped_by_bats(unsigned long va)
 | 
			
		||||
{
 | 
			
		||||
	int b;
 | 
			
		||||
	for (b = 0; b < 4; ++b)
 | 
			
		||||
		if (va >= bat_addrs[b].start && va < bat_addrs[b].limit)
 | 
			
		||||
			return bat_addrs[b].phys + (va - bat_addrs[b].start);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Return VA for a given PA or 0 if not mapped
 | 
			
		||||
 */
 | 
			
		||||
unsigned long p_mapped_by_bats(unsigned long pa)
 | 
			
		||||
{
 | 
			
		||||
	int b;
 | 
			
		||||
	for (b = 0; b < 4; ++b)
 | 
			
		||||
		if (pa >= bat_addrs[b].phys
 | 
			
		||||
	    	    && pa < (bat_addrs[b].limit-bat_addrs[b].start)
 | 
			
		||||
		              +bat_addrs[b].phys)
 | 
			
		||||
			return bat_addrs[b].start+(pa-bat_addrs[b].phys);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsigned long __init mmu_mapin_ram(void)
 | 
			
		||||
{
 | 
			
		||||
#ifdef CONFIG_POWER4
 | 
			
		||||
	return 0;
 | 
			
		||||
#else
 | 
			
		||||
	unsigned long tot, bl, done;
 | 
			
		||||
	unsigned long max_size = (256<<20);
 | 
			
		||||
	unsigned long align;
 | 
			
		||||
 | 
			
		||||
	if (__map_without_bats)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	/* Set up BAT2 and if necessary BAT3 to cover RAM. */
 | 
			
		||||
 | 
			
		||||
	/* Make sure we don't map a block larger than the
 | 
			
		||||
	   smallest alignment of the physical address. */
 | 
			
		||||
	/* alignment of PPC_MEMSTART */
 | 
			
		||||
	align = ~(PPC_MEMSTART-1) & PPC_MEMSTART;
 | 
			
		||||
	/* set BAT block size to MIN(max_size, align) */
 | 
			
		||||
	if (align && align < max_size)
 | 
			
		||||
		max_size = align;
 | 
			
		||||
 | 
			
		||||
	tot = total_lowmem;
 | 
			
		||||
	for (bl = 128<<10; bl < max_size; bl <<= 1) {
 | 
			
		||||
		if (bl * 2 > tot)
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	setbat(2, KERNELBASE, PPC_MEMSTART, bl, _PAGE_RAM);
 | 
			
		||||
	done = (unsigned long)bat_addrs[2].limit - KERNELBASE + 1;
 | 
			
		||||
	if ((done < tot) && !bat_addrs[3].limit) {
 | 
			
		||||
		/* use BAT3 to cover a bit more */
 | 
			
		||||
		tot -= done;
 | 
			
		||||
		for (bl = 128<<10; bl < max_size; bl <<= 1)
 | 
			
		||||
			if (bl * 2 > tot)
 | 
			
		||||
				break;
 | 
			
		||||
		setbat(3, KERNELBASE+done, PPC_MEMSTART+done, bl, _PAGE_RAM);
 | 
			
		||||
		done = (unsigned long)bat_addrs[3].limit - KERNELBASE + 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return done;
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Set up one of the I/D BAT (block address translation) register pairs.
 | 
			
		||||
 * The parameters are not checked; in particular size must be a power
 | 
			
		||||
 * of 2 between 128k and 256M.
 | 
			
		||||
 */
 | 
			
		||||
void __init setbat(int index, unsigned long virt, unsigned long phys,
 | 
			
		||||
		   unsigned int size, int flags)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int bl;
 | 
			
		||||
	int wimgxpp;
 | 
			
		||||
	union ubat *bat = BATS[index];
 | 
			
		||||
 | 
			
		||||
	if (((flags & _PAGE_NO_CACHE) == 0) &&
 | 
			
		||||
	    cpu_has_feature(CPU_FTR_NEED_COHERENT))
 | 
			
		||||
		flags |= _PAGE_COHERENT;
 | 
			
		||||
 | 
			
		||||
	bl = (size >> 17) - 1;
 | 
			
		||||
	if (PVR_VER(mfspr(SPRN_PVR)) != 1) {
 | 
			
		||||
		/* 603, 604, etc. */
 | 
			
		||||
		/* Do DBAT first */
 | 
			
		||||
		wimgxpp = flags & (_PAGE_WRITETHRU | _PAGE_NO_CACHE
 | 
			
		||||
				   | _PAGE_COHERENT | _PAGE_GUARDED);
 | 
			
		||||
		wimgxpp |= (flags & _PAGE_RW)? BPP_RW: BPP_RX;
 | 
			
		||||
		bat[1].word[0] = virt | (bl << 2) | 2; /* Vs=1, Vp=0 */
 | 
			
		||||
		bat[1].word[1] = phys | wimgxpp;
 | 
			
		||||
#ifndef CONFIG_KGDB /* want user access for breakpoints */
 | 
			
		||||
		if (flags & _PAGE_USER)
 | 
			
		||||
#endif
 | 
			
		||||
			bat[1].bat.batu.vp = 1;
 | 
			
		||||
		if (flags & _PAGE_GUARDED) {
 | 
			
		||||
			/* G bit must be zero in IBATs */
 | 
			
		||||
			bat[0].word[0] = bat[0].word[1] = 0;
 | 
			
		||||
		} else {
 | 
			
		||||
			/* make IBAT same as DBAT */
 | 
			
		||||
			bat[0] = bat[1];
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		/* 601 cpu */
 | 
			
		||||
		if (bl > BL_8M)
 | 
			
		||||
			bl = BL_8M;
 | 
			
		||||
		wimgxpp = flags & (_PAGE_WRITETHRU | _PAGE_NO_CACHE
 | 
			
		||||
				   | _PAGE_COHERENT);
 | 
			
		||||
		wimgxpp |= (flags & _PAGE_RW)?
 | 
			
		||||
			((flags & _PAGE_USER)? PP_RWRW: PP_RWXX): PP_RXRX;
 | 
			
		||||
		bat->word[0] = virt | wimgxpp | 4;	/* Ks=0, Ku=1 */
 | 
			
		||||
		bat->word[1] = phys | bl | 0x40;	/* V=1 */
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bat_addrs[index].start = virt;
 | 
			
		||||
	bat_addrs[index].limit = virt + ((bl + 1) << 17) - 1;
 | 
			
		||||
	bat_addrs[index].phys = phys;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Initialize the hash table and patch the instructions in hashtable.S.
 | 
			
		||||
 */
 | 
			
		||||
void __init MMU_init_hw(void)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int hmask, mb, mb2;
 | 
			
		||||
	unsigned int n_hpteg, lg_n_hpteg;
 | 
			
		||||
 | 
			
		||||
	extern unsigned int hash_page_patch_A[];
 | 
			
		||||
	extern unsigned int hash_page_patch_B[], hash_page_patch_C[];
 | 
			
		||||
	extern unsigned int hash_page[];
 | 
			
		||||
	extern unsigned int flush_hash_patch_A[], flush_hash_patch_B[];
 | 
			
		||||
 | 
			
		||||
	if (!cpu_has_feature(CPU_FTR_HPTE_TABLE)) {
 | 
			
		||||
		/*
 | 
			
		||||
		 * Put a blr (procedure return) instruction at the
 | 
			
		||||
		 * start of hash_page, since we can still get DSI
 | 
			
		||||
		 * exceptions on a 603.
 | 
			
		||||
		 */
 | 
			
		||||
		hash_page[0] = 0x4e800020;
 | 
			
		||||
		flush_icache_range((unsigned long) &hash_page[0],
 | 
			
		||||
				   (unsigned long) &hash_page[1]);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ( ppc_md.progress ) ppc_md.progress("hash:enter", 0x105);
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_PPC64BRIDGE
 | 
			
		||||
#define LG_HPTEG_SIZE	7		/* 128 bytes per HPTEG */
 | 
			
		||||
#define SDR1_LOW_BITS	(lg_n_hpteg - 11)
 | 
			
		||||
#define MIN_N_HPTEG	2048		/* min 256kB hash table */
 | 
			
		||||
#else
 | 
			
		||||
#define LG_HPTEG_SIZE	6		/* 64 bytes per HPTEG */
 | 
			
		||||
#define SDR1_LOW_BITS	((n_hpteg - 1) >> 10)
 | 
			
		||||
#define MIN_N_HPTEG	1024		/* min 64kB hash table */
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_POWER4
 | 
			
		||||
	/* The hash table has already been allocated and initialized
 | 
			
		||||
	   in prom.c */
 | 
			
		||||
	n_hpteg = Hash_size >> LG_HPTEG_SIZE;
 | 
			
		||||
	lg_n_hpteg = __ilog2(n_hpteg);
 | 
			
		||||
 | 
			
		||||
	/* Remove the hash table from the available memory */
 | 
			
		||||
	if (Hash)
 | 
			
		||||
		reserve_phys_mem(__pa(Hash), Hash_size);
 | 
			
		||||
 | 
			
		||||
#else /* CONFIG_POWER4 */
 | 
			
		||||
	/*
 | 
			
		||||
	 * Allow 1 HPTE (1/8 HPTEG) for each page of memory.
 | 
			
		||||
	 * This is less than the recommended amount, but then
 | 
			
		||||
	 * Linux ain't AIX.
 | 
			
		||||
	 */
 | 
			
		||||
	n_hpteg = total_memory / (PAGE_SIZE * 8);
 | 
			
		||||
	if (n_hpteg < MIN_N_HPTEG)
 | 
			
		||||
		n_hpteg = MIN_N_HPTEG;
 | 
			
		||||
	lg_n_hpteg = __ilog2(n_hpteg);
 | 
			
		||||
	if (n_hpteg & (n_hpteg - 1)) {
 | 
			
		||||
		++lg_n_hpteg;		/* round up if not power of 2 */
 | 
			
		||||
		n_hpteg = 1 << lg_n_hpteg;
 | 
			
		||||
	}
 | 
			
		||||
	Hash_size = n_hpteg << LG_HPTEG_SIZE;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Find some memory for the hash table.
 | 
			
		||||
	 */
 | 
			
		||||
	if ( ppc_md.progress ) ppc_md.progress("hash:find piece", 0x322);
 | 
			
		||||
	Hash = mem_pieces_find(Hash_size, Hash_size);
 | 
			
		||||
	cacheable_memzero(Hash, Hash_size);
 | 
			
		||||
	_SDR1 = __pa(Hash) | SDR1_LOW_BITS;
 | 
			
		||||
#endif /* CONFIG_POWER4 */
 | 
			
		||||
 | 
			
		||||
	Hash_end = (PTE *) ((unsigned long)Hash + Hash_size);
 | 
			
		||||
 | 
			
		||||
	printk("Total memory = %ldMB; using %ldkB for hash table (at %p)\n",
 | 
			
		||||
	       total_memory >> 20, Hash_size >> 10, Hash);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Patch up the instructions in hashtable.S:create_hpte
 | 
			
		||||
	 */
 | 
			
		||||
	if ( ppc_md.progress ) ppc_md.progress("hash:patch", 0x345);
 | 
			
		||||
	Hash_mask = n_hpteg - 1;
 | 
			
		||||
	hmask = Hash_mask >> (16 - LG_HPTEG_SIZE);
 | 
			
		||||
	mb2 = mb = 32 - LG_HPTEG_SIZE - lg_n_hpteg;
 | 
			
		||||
	if (lg_n_hpteg > 16)
 | 
			
		||||
		mb2 = 16 - LG_HPTEG_SIZE;
 | 
			
		||||
 | 
			
		||||
	hash_page_patch_A[0] = (hash_page_patch_A[0] & ~0xffff)
 | 
			
		||||
		| ((unsigned int)(Hash) >> 16);
 | 
			
		||||
	hash_page_patch_A[1] = (hash_page_patch_A[1] & ~0x7c0) | (mb << 6);
 | 
			
		||||
	hash_page_patch_A[2] = (hash_page_patch_A[2] & ~0x7c0) | (mb2 << 6);
 | 
			
		||||
	hash_page_patch_B[0] = (hash_page_patch_B[0] & ~0xffff) | hmask;
 | 
			
		||||
	hash_page_patch_C[0] = (hash_page_patch_C[0] & ~0xffff) | hmask;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Ensure that the locations we've patched have been written
 | 
			
		||||
	 * out from the data cache and invalidated in the instruction
 | 
			
		||||
	 * cache, on those machines with split caches.
 | 
			
		||||
	 */
 | 
			
		||||
	flush_icache_range((unsigned long) &hash_page_patch_A[0],
 | 
			
		||||
			   (unsigned long) &hash_page_patch_C[1]);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Patch up the instructions in hashtable.S:flush_hash_page
 | 
			
		||||
	 */
 | 
			
		||||
	flush_hash_patch_A[0] = (flush_hash_patch_A[0] & ~0xffff)
 | 
			
		||||
		| ((unsigned int)(Hash) >> 16);
 | 
			
		||||
	flush_hash_patch_A[1] = (flush_hash_patch_A[1] & ~0x7c0) | (mb << 6);
 | 
			
		||||
	flush_hash_patch_A[2] = (flush_hash_patch_A[2] & ~0x7c0) | (mb2 << 6);
 | 
			
		||||
	flush_hash_patch_B[0] = (flush_hash_patch_B[0] & ~0xffff) | hmask;
 | 
			
		||||
	flush_icache_range((unsigned long) &flush_hash_patch_A[0],
 | 
			
		||||
			   (unsigned long) &flush_hash_patch_B[1]);
 | 
			
		||||
 | 
			
		||||
	if ( ppc_md.progress ) ppc_md.progress("hash:done", 0x205);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										183
									
								
								arch/powerpc/mm/tlb.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										183
									
								
								arch/powerpc/mm/tlb.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,183 @@
 | 
			
		||||
/*
 | 
			
		||||
 * This file contains the routines for TLB flushing.
 | 
			
		||||
 * On machines where the MMU uses a hash table to store virtual to
 | 
			
		||||
 * physical translations, these routines flush entries from the
 | 
			
		||||
 * hash table also.
 | 
			
		||||
 *  -- paulus
 | 
			
		||||
 *
 | 
			
		||||
 *  Derived from arch/ppc/mm/init.c:
 | 
			
		||||
 *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
 | 
			
		||||
 *
 | 
			
		||||
 *  Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au)
 | 
			
		||||
 *  and Cort Dougan (PReP) (cort@cs.nmt.edu)
 | 
			
		||||
 *    Copyright (C) 1996 Paul Mackerras
 | 
			
		||||
 *  Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk).
 | 
			
		||||
 *
 | 
			
		||||
 *  Derived from "arch/i386/mm/init.c"
 | 
			
		||||
 *    Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or
 | 
			
		||||
 *  modify it under the terms of the GNU General Public License
 | 
			
		||||
 *  as published by the Free Software Foundation; either version
 | 
			
		||||
 *  2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/config.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/mm.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/highmem.h>
 | 
			
		||||
#include <asm/tlbflush.h>
 | 
			
		||||
#include <asm/tlb.h>
 | 
			
		||||
 | 
			
		||||
#include "mmu_decl.h"
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Called when unmapping pages to flush entries from the TLB/hash table.
 | 
			
		||||
 */
 | 
			
		||||
void flush_hash_entry(struct mm_struct *mm, pte_t *ptep, unsigned long addr)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long ptephys;
 | 
			
		||||
 | 
			
		||||
	if (Hash != 0) {
 | 
			
		||||
		ptephys = __pa(ptep) & PAGE_MASK;
 | 
			
		||||
		flush_hash_pages(mm->context, addr, ptephys, 1);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Called by ptep_set_access_flags, must flush on CPUs for which the
 | 
			
		||||
 * DSI handler can't just "fixup" the TLB on a write fault
 | 
			
		||||
 */
 | 
			
		||||
void flush_tlb_page_nohash(struct vm_area_struct *vma, unsigned long addr)
 | 
			
		||||
{
 | 
			
		||||
	if (Hash != 0)
 | 
			
		||||
		return;
 | 
			
		||||
	_tlbie(addr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Called at the end of a mmu_gather operation to make sure the
 | 
			
		||||
 * TLB flush is completely done.
 | 
			
		||||
 */
 | 
			
		||||
void tlb_flush(struct mmu_gather *tlb)
 | 
			
		||||
{
 | 
			
		||||
	if (Hash == 0) {
 | 
			
		||||
		/*
 | 
			
		||||
		 * 603 needs to flush the whole TLB here since
 | 
			
		||||
		 * it doesn't use a hash table.
 | 
			
		||||
		 */
 | 
			
		||||
		_tlbia();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * TLB flushing:
 | 
			
		||||
 *
 | 
			
		||||
 *  - 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 kernel pages
 | 
			
		||||
 *
 | 
			
		||||
 * since the hardware hash table functions as an extension of the
 | 
			
		||||
 * tlb as far as the linux tables are concerned, flush it too.
 | 
			
		||||
 *    -- Cort
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * 750 SMP is a Bad Idea because the 750 doesn't broadcast all
 | 
			
		||||
 * the cache operations on the bus.  Hence we need to use an IPI
 | 
			
		||||
 * to get the other CPU(s) to invalidate their TLBs.
 | 
			
		||||
 */
 | 
			
		||||
#ifdef CONFIG_SMP_750
 | 
			
		||||
#define FINISH_FLUSH	smp_send_tlb_invalidate(0)
 | 
			
		||||
#else
 | 
			
		||||
#define FINISH_FLUSH	do { } while (0)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static void flush_range(struct mm_struct *mm, unsigned long start,
 | 
			
		||||
			unsigned long end)
 | 
			
		||||
{
 | 
			
		||||
	pmd_t *pmd;
 | 
			
		||||
	unsigned long pmd_end;
 | 
			
		||||
	int count;
 | 
			
		||||
	unsigned int ctx = mm->context;
 | 
			
		||||
 | 
			
		||||
	if (Hash == 0) {
 | 
			
		||||
		_tlbia();
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	start &= PAGE_MASK;
 | 
			
		||||
	if (start >= end)
 | 
			
		||||
		return;
 | 
			
		||||
	end = (end - 1) | ~PAGE_MASK;
 | 
			
		||||
	pmd = pmd_offset(pgd_offset(mm, start), start);
 | 
			
		||||
	for (;;) {
 | 
			
		||||
		pmd_end = ((start + PGDIR_SIZE) & PGDIR_MASK) - 1;
 | 
			
		||||
		if (pmd_end > end)
 | 
			
		||||
			pmd_end = end;
 | 
			
		||||
		if (!pmd_none(*pmd)) {
 | 
			
		||||
			count = ((pmd_end - start) >> PAGE_SHIFT) + 1;
 | 
			
		||||
			flush_hash_pages(ctx, start, pmd_val(*pmd), count);
 | 
			
		||||
		}
 | 
			
		||||
		if (pmd_end == end)
 | 
			
		||||
			break;
 | 
			
		||||
		start = pmd_end + 1;
 | 
			
		||||
		++pmd;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Flush kernel TLB entries in the given range
 | 
			
		||||
 */
 | 
			
		||||
void flush_tlb_kernel_range(unsigned long start, unsigned long end)
 | 
			
		||||
{
 | 
			
		||||
	flush_range(&init_mm, start, end);
 | 
			
		||||
	FINISH_FLUSH;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Flush all the (user) entries for the address space described by mm.
 | 
			
		||||
 */
 | 
			
		||||
void flush_tlb_mm(struct mm_struct *mm)
 | 
			
		||||
{
 | 
			
		||||
	struct vm_area_struct *mp;
 | 
			
		||||
 | 
			
		||||
	if (Hash == 0) {
 | 
			
		||||
		_tlbia();
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (mp = mm->mmap; mp != NULL; mp = mp->vm_next)
 | 
			
		||||
		flush_range(mp->vm_mm, mp->vm_start, mp->vm_end);
 | 
			
		||||
	FINISH_FLUSH;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr)
 | 
			
		||||
{
 | 
			
		||||
	struct mm_struct *mm;
 | 
			
		||||
	pmd_t *pmd;
 | 
			
		||||
 | 
			
		||||
	if (Hash == 0) {
 | 
			
		||||
		_tlbie(vmaddr);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	mm = (vmaddr < TASK_SIZE)? vma->vm_mm: &init_mm;
 | 
			
		||||
	pmd = pmd_offset(pgd_offset(mm, vmaddr), vmaddr);
 | 
			
		||||
	if (!pmd_none(*pmd))
 | 
			
		||||
		flush_hash_pages(mm->context, vmaddr, pmd_val(*pmd), 1);
 | 
			
		||||
	FINISH_FLUSH;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * For each address in the range, find the pte for the address
 | 
			
		||||
 * and check _PAGE_HASHPTE bit; if it is set, find and destroy
 | 
			
		||||
 * the corresponding HPTE.
 | 
			
		||||
 */
 | 
			
		||||
void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
 | 
			
		||||
		     unsigned long end)
 | 
			
		||||
{
 | 
			
		||||
	flush_range(vma->vm_mm, start, end);
 | 
			
		||||
	FINISH_FLUSH;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										280
									
								
								arch/powerpc/platforms/4xx/Kconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										280
									
								
								arch/powerpc/platforms/4xx/Kconfig
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,280 @@
 | 
			
		||||
config 4xx
 | 
			
		||||
	bool
 | 
			
		||||
	depends on 40x || 44x
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config WANT_EARLY_SERIAL
 | 
			
		||||
	bool
 | 
			
		||||
	select SERIAL_8250
 | 
			
		||||
	default n
 | 
			
		||||
 | 
			
		||||
menu "AMCC 4xx options"
 | 
			
		||||
	depends on 4xx
 | 
			
		||||
 | 
			
		||||
choice
 | 
			
		||||
	prompt "Machine Type"
 | 
			
		||||
	depends on 40x
 | 
			
		||||
	default WALNUT
 | 
			
		||||
 | 
			
		||||
config BUBINGA
 | 
			
		||||
	bool "Bubinga"
 | 
			
		||||
	select WANT_EARLY_SERIAL
 | 
			
		||||
	help
 | 
			
		||||
	  This option enables support for the IBM 405EP evaluation board.
 | 
			
		||||
 | 
			
		||||
config CPCI405
 | 
			
		||||
	bool "CPCI405"
 | 
			
		||||
	help
 | 
			
		||||
	  This option enables support for the CPCI405 board.
 | 
			
		||||
 | 
			
		||||
config EP405
 | 
			
		||||
	bool "EP405/EP405PC"
 | 
			
		||||
	help
 | 
			
		||||
	  This option enables support for the EP405/EP405PC boards.
 | 
			
		||||
 | 
			
		||||
config REDWOOD_5
 | 
			
		||||
	bool "Redwood-5"
 | 
			
		||||
	help
 | 
			
		||||
	  This option enables support for the IBM STB04 evaluation board.
 | 
			
		||||
 | 
			
		||||
config REDWOOD_6
 | 
			
		||||
	bool "Redwood-6"
 | 
			
		||||
	help
 | 
			
		||||
	  This option enables support for the IBM STBx25xx evaluation board.
 | 
			
		||||
 | 
			
		||||
config SYCAMORE
 | 
			
		||||
	bool "Sycamore"
 | 
			
		||||
	help
 | 
			
		||||
	  This option enables support for the IBM PPC405GPr evaluation board.
 | 
			
		||||
 | 
			
		||||
config WALNUT
 | 
			
		||||
	bool "Walnut"
 | 
			
		||||
	help
 | 
			
		||||
	  This option enables support for the IBM PPC405GP evaluation board.
 | 
			
		||||
 | 
			
		||||
config XILINX_ML300
 | 
			
		||||
	bool "Xilinx-ML300"
 | 
			
		||||
	help
 | 
			
		||||
	  This option enables support for the Xilinx ML300 evaluation board.
 | 
			
		||||
 | 
			
		||||
endchoice
 | 
			
		||||
 | 
			
		||||
choice
 | 
			
		||||
	prompt "Machine Type"
 | 
			
		||||
	depends on 44x
 | 
			
		||||
	default EBONY
 | 
			
		||||
 | 
			
		||||
config BAMBOO
 | 
			
		||||
	bool "Bamboo"
 | 
			
		||||
	select WANT_EARLY_SERIAL
 | 
			
		||||
	help
 | 
			
		||||
	  This option enables support for the IBM PPC440EP evaluation board.
 | 
			
		||||
 | 
			
		||||
config EBONY
 | 
			
		||||
	bool "Ebony"
 | 
			
		||||
	select WANT_EARLY_SERIAL
 | 
			
		||||
	help
 | 
			
		||||
	  This option enables support for the IBM PPC440GP evaluation board.
 | 
			
		||||
 | 
			
		||||
config LUAN
 | 
			
		||||
	bool "Luan"
 | 
			
		||||
	select WANT_EARLY_SERIAL
 | 
			
		||||
	help
 | 
			
		||||
	  This option enables support for the IBM PPC440SP evaluation board.
 | 
			
		||||
 | 
			
		||||
config OCOTEA
 | 
			
		||||
	bool "Ocotea"
 | 
			
		||||
	select WANT_EARLY_SERIAL
 | 
			
		||||
	help
 | 
			
		||||
	  This option enables support for the IBM PPC440GX evaluation board.
 | 
			
		||||
 | 
			
		||||
endchoice
 | 
			
		||||
 | 
			
		||||
config EP405PC
 | 
			
		||||
	bool "EP405PC Support"
 | 
			
		||||
	depends on EP405
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# It's often necessary to know the specific 4xx processor type.
 | 
			
		||||
# Fortunately, it is impled (so far) from the board type, so we
 | 
			
		||||
# don't need to ask more redundant questions.
 | 
			
		||||
config NP405H
 | 
			
		||||
	bool
 | 
			
		||||
	depends on ASH
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config 440EP
 | 
			
		||||
	bool
 | 
			
		||||
	depends on BAMBOO
 | 
			
		||||
	select PPC_FPU
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config 440GP
 | 
			
		||||
	bool
 | 
			
		||||
	depends on EBONY
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config 440GX
 | 
			
		||||
	bool
 | 
			
		||||
	depends on OCOTEA
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config 440SP
 | 
			
		||||
	bool
 | 
			
		||||
	depends on LUAN
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config 440
 | 
			
		||||
	bool
 | 
			
		||||
	depends on 440GP || 440SP || 440EP
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config 440A
 | 
			
		||||
	bool
 | 
			
		||||
	depends on 440GX
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config IBM440EP_ERR42
 | 
			
		||||
	bool
 | 
			
		||||
	depends on 440EP
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
# All 405-based cores up until the 405GPR and 405EP have this errata.
 | 
			
		||||
config IBM405_ERR77
 | 
			
		||||
	bool
 | 
			
		||||
	depends on 40x && !403GCX && !405GPR && !405EP
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
# All 40x-based cores, up until the 405GPR and 405EP have this errata.
 | 
			
		||||
config IBM405_ERR51
 | 
			
		||||
	bool
 | 
			
		||||
	depends on 40x && !405GPR && !405EP
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config BOOKE
 | 
			
		||||
	bool
 | 
			
		||||
	depends on 44x
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config IBM_OCP
 | 
			
		||||
	bool
 | 
			
		||||
	depends on ASH || BAMBOO || BUBINGA || CPCI405 || EBONY || EP405 || LUAN || OCOTEA || REDWOOD_5 || REDWOOD_6 || SYCAMORE || WALNUT
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config XILINX_OCP
 | 
			
		||||
	bool
 | 
			
		||||
	depends on XILINX_ML300
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config IBM_EMAC4
 | 
			
		||||
	bool
 | 
			
		||||
	depends on 440GX || 440SP
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config BIOS_FIXUP
 | 
			
		||||
	bool
 | 
			
		||||
	depends on BUBINGA || EP405 || SYCAMORE || WALNUT
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
# OAK doesn't exist but wanted to keep this around for any future 403GCX boards
 | 
			
		||||
config 403GCX
 | 
			
		||||
	bool
 | 
			
		||||
	depends OAK
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config 405EP
 | 
			
		||||
	bool
 | 
			
		||||
	depends on BUBINGA
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config 405GP
 | 
			
		||||
	bool
 | 
			
		||||
	depends on CPCI405 || EP405 || WALNUT
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config 405GPR
 | 
			
		||||
	bool
 | 
			
		||||
	depends on SYCAMORE
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config VIRTEX_II_PRO
 | 
			
		||||
	bool
 | 
			
		||||
	depends on XILINX_ML300
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config STB03xxx
 | 
			
		||||
	bool
 | 
			
		||||
	depends on REDWOOD_5 || REDWOOD_6
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config EMBEDDEDBOOT
 | 
			
		||||
	bool
 | 
			
		||||
	depends on EP405 || XILINX_ML300
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config IBM_OPENBIOS
 | 
			
		||||
	bool
 | 
			
		||||
	depends on ASH || BUBINGA || REDWOOD_5 || REDWOOD_6 || SYCAMORE || WALNUT
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config PPC4xx_DMA
 | 
			
		||||
	bool "PPC4xx DMA controller support"
 | 
			
		||||
	depends on 4xx
 | 
			
		||||
 | 
			
		||||
config PPC4xx_EDMA
 | 
			
		||||
	bool
 | 
			
		||||
	depends on !STB03xxx && PPC4xx_DMA
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config PPC_GEN550
 | 
			
		||||
	bool
 | 
			
		||||
	depends on 4xx
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
choice
 | 
			
		||||
	prompt "TTYS0 device and default console"
 | 
			
		||||
	depends on 40x
 | 
			
		||||
	default UART0_TTYS0
 | 
			
		||||
 | 
			
		||||
config UART0_TTYS0
 | 
			
		||||
	bool "UART0"
 | 
			
		||||
 | 
			
		||||
config UART0_TTYS1
 | 
			
		||||
	bool "UART1"
 | 
			
		||||
 | 
			
		||||
endchoice
 | 
			
		||||
 | 
			
		||||
config SERIAL_SICC
 | 
			
		||||
	bool "SICC Serial port support"
 | 
			
		||||
	depends on STB03xxx
 | 
			
		||||
 | 
			
		||||
config UART1_DFLT_CONSOLE
 | 
			
		||||
	bool
 | 
			
		||||
	depends on SERIAL_SICC && UART0_TTYS1
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config SERIAL_SICC_CONSOLE
 | 
			
		||||
	bool
 | 
			
		||||
	depends on SERIAL_SICC && UART0_TTYS1
 | 
			
		||||
	default y
 | 
			
		||||
endmenu
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
menu "IBM 40x options"
 | 
			
		||||
	depends on 40x
 | 
			
		||||
 | 
			
		||||
config SERIAL_SICC
 | 
			
		||||
	bool "SICC Serial port"
 | 
			
		||||
	depends on STB03xxx
 | 
			
		||||
 | 
			
		||||
config UART1_DFLT_CONSOLE
 | 
			
		||||
	bool
 | 
			
		||||
	depends on SERIAL_SICC && UART0_TTYS1
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config SERIAL_SICC_CONSOLE
 | 
			
		||||
	bool
 | 
			
		||||
	depends on SERIAL_SICC && UART0_TTYS1
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
endmenu
 | 
			
		||||
							
								
								
									
										86
									
								
								arch/powerpc/platforms/85xx/Kconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								arch/powerpc/platforms/85xx/Kconfig
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,86 @@
 | 
			
		||||
config 85xx
 | 
			
		||||
	bool
 | 
			
		||||
	depends on E500
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config PPC_INDIRECT_PCI_BE
 | 
			
		||||
	bool
 | 
			
		||||
	depends on 85xx
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
menu "Freescale 85xx options"
 | 
			
		||||
	depends on E500
 | 
			
		||||
 | 
			
		||||
choice
 | 
			
		||||
	prompt "Machine Type"
 | 
			
		||||
	depends on 85xx
 | 
			
		||||
	default MPC8540_ADS
 | 
			
		||||
 | 
			
		||||
config MPC8540_ADS
 | 
			
		||||
	bool "Freescale MPC8540 ADS"
 | 
			
		||||
	help
 | 
			
		||||
	  This option enables support for the MPC 8540 ADS evaluation board.
 | 
			
		||||
 | 
			
		||||
config MPC8548_CDS
 | 
			
		||||
	bool "Freescale MPC8548 CDS"
 | 
			
		||||
	help
 | 
			
		||||
	  This option enablese support for the MPC8548 CDS evaluation board.
 | 
			
		||||
 | 
			
		||||
config MPC8555_CDS
 | 
			
		||||
	bool "Freescale MPC8555 CDS"
 | 
			
		||||
	help
 | 
			
		||||
	  This option enablese support for the MPC8555 CDS evaluation board.
 | 
			
		||||
 | 
			
		||||
config MPC8560_ADS
 | 
			
		||||
	bool "Freescale MPC8560 ADS"
 | 
			
		||||
	help
 | 
			
		||||
	  This option enables support for the MPC 8560 ADS evaluation board.
 | 
			
		||||
 | 
			
		||||
config SBC8560
 | 
			
		||||
	bool "WindRiver PowerQUICC III SBC8560"
 | 
			
		||||
	help
 | 
			
		||||
	  This option enables support for the WindRiver PowerQUICC III 
 | 
			
		||||
	  SBC8560 board.
 | 
			
		||||
 | 
			
		||||
config STX_GP3
 | 
			
		||||
	bool "Silicon Turnkey Express GP3"
 | 
			
		||||
	help
 | 
			
		||||
	  This option enables support for the Silicon Turnkey Express GP3
 | 
			
		||||
	  board.
 | 
			
		||||
 | 
			
		||||
endchoice
 | 
			
		||||
 | 
			
		||||
# It's often necessary to know the specific 85xx processor type.
 | 
			
		||||
# Fortunately, it is implied (so far) from the board type, so we
 | 
			
		||||
# don't need to ask more redundant questions.
 | 
			
		||||
config MPC8540
 | 
			
		||||
	bool
 | 
			
		||||
	depends on MPC8540_ADS
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config MPC8548
 | 
			
		||||
	bool
 | 
			
		||||
	depends on MPC8548_CDS
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config MPC8555
 | 
			
		||||
	bool
 | 
			
		||||
	depends on MPC8555_CDS
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config MPC8560
 | 
			
		||||
	bool
 | 
			
		||||
	depends on SBC8560 || MPC8560_ADS || STX_GP3
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config 85xx_PCI2
 | 
			
		||||
	bool "Supprt for 2nd PCI host controller"
 | 
			
		||||
	depends on MPC8555_CDS
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config PPC_GEN550
 | 
			
		||||
	bool
 | 
			
		||||
	depends on MPC8540 || SBC8560 || MPC8555
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
endmenu
 | 
			
		||||
							
								
								
									
										352
									
								
								arch/powerpc/platforms/8xx/Kconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										352
									
								
								arch/powerpc/platforms/8xx/Kconfig
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,352 @@
 | 
			
		||||
config FADS
 | 
			
		||||
	bool
 | 
			
		||||
 | 
			
		||||
choice
 | 
			
		||||
	prompt "8xx Machine Type"
 | 
			
		||||
	depends on 8xx
 | 
			
		||||
	default RPXLITE
 | 
			
		||||
 | 
			
		||||
config RPXLITE
 | 
			
		||||
	bool "RPX-Lite"
 | 
			
		||||
	---help---
 | 
			
		||||
	  Single-board computers based around the PowerPC MPC8xx chips and
 | 
			
		||||
	  intended for embedded applications.  The following types are
 | 
			
		||||
	  supported:
 | 
			
		||||
 | 
			
		||||
	  RPX-Lite:
 | 
			
		||||
	  Embedded Planet RPX Lite. PC104 form-factor SBC based on the MPC823.
 | 
			
		||||
 | 
			
		||||
	  RPX-Classic:
 | 
			
		||||
	  Embedded Planet RPX Classic Low-fat. Credit-card-size SBC based on
 | 
			
		||||
	  the MPC 860
 | 
			
		||||
 | 
			
		||||
	  BSE-IP:
 | 
			
		||||
	  Bright Star Engineering ip-Engine.
 | 
			
		||||
 | 
			
		||||
	  TQM823L:
 | 
			
		||||
	  TQM850L:
 | 
			
		||||
	  TQM855L:
 | 
			
		||||
	  TQM860L:
 | 
			
		||||
	  MPC8xx based family of mini modules, half credit card size,
 | 
			
		||||
	  up to 64 MB of RAM, 8 MB Flash, (Fast) Ethernet, 2 x serial ports,
 | 
			
		||||
	  2 x CAN bus interface, ...
 | 
			
		||||
	  Manufacturer: TQ Components, www.tq-group.de
 | 
			
		||||
	  Date of Release: October (?) 1999
 | 
			
		||||
	  End of Life: not yet :-)
 | 
			
		||||
	  URL:
 | 
			
		||||
	  - module: <http://www.denx.de/PDF/TQM8xxLHWM201.pdf>
 | 
			
		||||
	  - starter kit: <http://www.denx.de/PDF/STK8xxLHWM201.pdf>
 | 
			
		||||
	  - images: <http://www.denx.de/embedded-ppc-en.html>
 | 
			
		||||
 | 
			
		||||
	  FPS850L:
 | 
			
		||||
	  FingerPrint Sensor System (based on TQM850L)
 | 
			
		||||
	  Manufacturer: IKENDI AG, <http://www.ikendi.com/>
 | 
			
		||||
	  Date of Release: November 1999
 | 
			
		||||
	  End of life: end 2000 ?
 | 
			
		||||
	  URL: see TQM850L
 | 
			
		||||
 | 
			
		||||
	  IVMS8:
 | 
			
		||||
	  MPC860 based board used in the "Integrated Voice Mail System",
 | 
			
		||||
	  Small Version (8 voice channels)
 | 
			
		||||
	  Manufacturer: Speech Design, <http://www.speech-design.de/>
 | 
			
		||||
	  Date of Release: December 2000 (?)
 | 
			
		||||
	  End of life: -
 | 
			
		||||
	  URL: <http://www.speech-design.de/>
 | 
			
		||||
 | 
			
		||||
	  IVML24:
 | 
			
		||||
	  MPC860 based board used in the "Integrated Voice Mail System",
 | 
			
		||||
	  Large Version (24 voice channels)
 | 
			
		||||
	  Manufacturer: Speech Design, <http://www.speech-design.de/>
 | 
			
		||||
	  Date of Release: March 2001  (?)
 | 
			
		||||
	  End of life: -
 | 
			
		||||
	  URL: <http://www.speech-design.de/>
 | 
			
		||||
 | 
			
		||||
	  HERMES:
 | 
			
		||||
	  Hermes-Pro ISDN/LAN router with integrated 8 x hub
 | 
			
		||||
	  Manufacturer: Multidata Gesellschaft fur Datentechnik und Informatik
 | 
			
		||||
	  <http://www.multidata.de/>
 | 
			
		||||
	  Date of Release: 2000 (?)
 | 
			
		||||
	  End of life: -
 | 
			
		||||
	  URL: <http://www.multidata.de/english/products/hpro.htm>
 | 
			
		||||
 | 
			
		||||
	  IP860:
 | 
			
		||||
	  VMEBus IP (Industry Pack) carrier board with MPC860
 | 
			
		||||
	  Manufacturer: MicroSys GmbH, <http://www.microsys.de/>
 | 
			
		||||
	  Date of Release: ?
 | 
			
		||||
	  End of life: -
 | 
			
		||||
	  URL: <http://www.microsys.de/html/ip860.html>
 | 
			
		||||
 | 
			
		||||
	  PCU_E:
 | 
			
		||||
	  PCU = Peripheral Controller Unit, Extended
 | 
			
		||||
	  Manufacturer: Siemens AG, ICN (Information and Communication Networks)
 | 
			
		||||
	  	<http://www.siemens.de/page/1,3771,224315-1-999_2_226207-0,00.html>
 | 
			
		||||
	  Date of Release: April 2001
 | 
			
		||||
	  End of life: August 2001
 | 
			
		||||
	  URL: n. a.
 | 
			
		||||
 | 
			
		||||
config RPXCLASSIC
 | 
			
		||||
	bool "RPX-Classic"
 | 
			
		||||
	help
 | 
			
		||||
	  The RPX-Classic is a single-board computer based on the Motorola
 | 
			
		||||
	  MPC860.  It features 16MB of DRAM and a variable amount of flash,
 | 
			
		||||
	  I2C EEPROM, thermal monitoring, a PCMCIA slot, a DIP switch and two
 | 
			
		||||
	  LEDs.  Variants with Ethernet ports exist.  Say Y here to support it
 | 
			
		||||
	  directly.
 | 
			
		||||
 | 
			
		||||
config BSEIP
 | 
			
		||||
	bool "BSE-IP"
 | 
			
		||||
	help
 | 
			
		||||
	  Say Y here to support the Bright Star Engineering ipEngine SBC.
 | 
			
		||||
	  This is a credit-card-sized device featuring a MPC823 processor,
 | 
			
		||||
	  26MB DRAM, 4MB flash, Ethernet, a 16K-gate FPGA, USB, an LCD/video
 | 
			
		||||
	  controller, and two RS232 ports.
 | 
			
		||||
 | 
			
		||||
config MPC8XXFADS
 | 
			
		||||
	bool "FADS"
 | 
			
		||||
	select FADS
 | 
			
		||||
 | 
			
		||||
config MPC86XADS
 | 
			
		||||
	bool "MPC86XADS"
 | 
			
		||||
	help
 | 
			
		||||
	  MPC86x Application Development System by Freescale Semiconductor.
 | 
			
		||||
	  The MPC86xADS is meant to serve as a platform for s/w and h/w
 | 
			
		||||
	  development around the MPC86X processor families.
 | 
			
		||||
	select FADS
 | 
			
		||||
 | 
			
		||||
config MPC885ADS
 | 
			
		||||
	bool "MPC885ADS"
 | 
			
		||||
	help
 | 
			
		||||
	  Freescale Semiconductor MPC885 Application Development System (ADS).
 | 
			
		||||
	  Also known as DUET.
 | 
			
		||||
	  The MPC885ADS is meant to serve as a platform for s/w and h/w
 | 
			
		||||
	  development around the MPC885 processor family.
 | 
			
		||||
 | 
			
		||||
config TQM823L
 | 
			
		||||
	bool "TQM823L"
 | 
			
		||||
	help
 | 
			
		||||
	  Say Y here to support the TQM823L, one of an MPC8xx-based family of
 | 
			
		||||
	  mini SBCs (half credit-card size) from TQ Components first released
 | 
			
		||||
	  in late 1999.  Technical references are at
 | 
			
		||||
	  <http://www.denx.de/PDF/TQM8xxLHWM201.pdf>, and
 | 
			
		||||
	  <http://www.denx.de/PDF/STK8xxLHWM201.pdf>, and an image at
 | 
			
		||||
	  <http://www.denx.de/embedded-ppc-en.html>.
 | 
			
		||||
 | 
			
		||||
config TQM850L
 | 
			
		||||
	bool "TQM850L"
 | 
			
		||||
	help
 | 
			
		||||
	  Say Y here to support the TQM850L, one of an MPC8xx-based family of
 | 
			
		||||
	  mini SBCs (half credit-card size) from TQ Components first released
 | 
			
		||||
	  in late 1999.  Technical references are at
 | 
			
		||||
	  <http://www.denx.de/PDF/TQM8xxLHWM201.pdf>, and
 | 
			
		||||
	  <http://www.denx.de/PDF/STK8xxLHWM201.pdf>, and an image at
 | 
			
		||||
	  <http://www.denx.de/embedded-ppc-en.html>.
 | 
			
		||||
 | 
			
		||||
config TQM855L
 | 
			
		||||
	bool "TQM855L"
 | 
			
		||||
	help
 | 
			
		||||
	  Say Y here to support the TQM855L, one of an MPC8xx-based family of
 | 
			
		||||
	  mini SBCs (half credit-card size) from TQ Components first released
 | 
			
		||||
	  in late 1999.  Technical references are at
 | 
			
		||||
	  <http://www.denx.de/PDF/TQM8xxLHWM201.pdf>, and
 | 
			
		||||
	  <http://www.denx.de/PDF/STK8xxLHWM201.pdf>, and an image at
 | 
			
		||||
	  <http://www.denx.de/embedded-ppc-en.html>.
 | 
			
		||||
 | 
			
		||||
config TQM860L
 | 
			
		||||
	bool "TQM860L"
 | 
			
		||||
	help
 | 
			
		||||
	  Say Y here to support the TQM860L, one of an MPC8xx-based family of
 | 
			
		||||
	  mini SBCs (half credit-card size) from TQ Components first released
 | 
			
		||||
	  in late 1999.  Technical references are at
 | 
			
		||||
	  <http://www.denx.de/PDF/TQM8xxLHWM201.pdf>, and
 | 
			
		||||
	  <http://www.denx.de/PDF/STK8xxLHWM201.pdf>, and an image at
 | 
			
		||||
	  <http://www.denx.de/embedded-ppc-en.html>.
 | 
			
		||||
 | 
			
		||||
config FPS850L
 | 
			
		||||
	bool "FPS850L"
 | 
			
		||||
 | 
			
		||||
config IVMS8
 | 
			
		||||
	bool "IVMS8"
 | 
			
		||||
	help
 | 
			
		||||
	  Say Y here to support the Integrated Voice-Mail Small 8-channel SBC
 | 
			
		||||
	  from Speech Design, released March 2001.  The manufacturer's website
 | 
			
		||||
	  is at <http://www.speech-design.de/>.
 | 
			
		||||
 | 
			
		||||
config IVML24
 | 
			
		||||
	bool "IVML24"
 | 
			
		||||
	help
 | 
			
		||||
	  Say Y here to support the Integrated Voice-Mail Large 24-channel SBC
 | 
			
		||||
	  from Speech Design, released March 2001.  The manufacturer's website
 | 
			
		||||
	  is at <http://www.speech-design.de/>.
 | 
			
		||||
 | 
			
		||||
config HERMES_PRO
 | 
			
		||||
	bool "HERMES"
 | 
			
		||||
 | 
			
		||||
config IP860
 | 
			
		||||
	bool "IP860"
 | 
			
		||||
 | 
			
		||||
config LWMON
 | 
			
		||||
	bool "LWMON"
 | 
			
		||||
 | 
			
		||||
config PCU_E
 | 
			
		||||
	bool "PCU_E"
 | 
			
		||||
 | 
			
		||||
config CCM
 | 
			
		||||
	bool "CCM"
 | 
			
		||||
 | 
			
		||||
config LANTEC
 | 
			
		||||
	bool "LANTEC"
 | 
			
		||||
 | 
			
		||||
config MBX
 | 
			
		||||
	bool "MBX"
 | 
			
		||||
	help
 | 
			
		||||
	  MBX is a line of Motorola single-board computer based around the
 | 
			
		||||
	  MPC821 and MPC860 processors, and intended for embedded-controller
 | 
			
		||||
	  applications.  Say Y here to support these boards directly.
 | 
			
		||||
 | 
			
		||||
config WINCEPT
 | 
			
		||||
	bool "WinCept"
 | 
			
		||||
	help
 | 
			
		||||
	  The Wincept 100/110 is a Motorola single-board computer based on the
 | 
			
		||||
	  MPC821 PowerPC, introduced in 1998 and designed to be used in
 | 
			
		||||
	  thin-client machines.  Say Y to support it directly.
 | 
			
		||||
 | 
			
		||||
endchoice
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# MPC8xx Communication options
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
menu "MPC8xx CPM Options"
 | 
			
		||||
	depends on 8xx
 | 
			
		||||
 | 
			
		||||
config SCC_ENET
 | 
			
		||||
	bool "CPM SCC Ethernet"
 | 
			
		||||
	depends on NET_ETHERNET
 | 
			
		||||
	help
 | 
			
		||||
	  Enable Ethernet support via the Motorola MPC8xx serial
 | 
			
		||||
	  communications controller.
 | 
			
		||||
 | 
			
		||||
choice
 | 
			
		||||
	prompt "SCC used for Ethernet"
 | 
			
		||||
	depends on SCC_ENET
 | 
			
		||||
	default SCC1_ENET
 | 
			
		||||
 | 
			
		||||
config SCC1_ENET
 | 
			
		||||
	bool "SCC1"
 | 
			
		||||
	help
 | 
			
		||||
	  Use MPC8xx serial communications controller 1 to drive Ethernet
 | 
			
		||||
	  (default).
 | 
			
		||||
 | 
			
		||||
config SCC2_ENET
 | 
			
		||||
	bool "SCC2"
 | 
			
		||||
	help
 | 
			
		||||
	  Use MPC8xx serial communications controller 2 to drive Ethernet.
 | 
			
		||||
 | 
			
		||||
config SCC3_ENET
 | 
			
		||||
	bool "SCC3"
 | 
			
		||||
	help
 | 
			
		||||
	  Use MPC8xx serial communications controller 3 to drive Ethernet.
 | 
			
		||||
 | 
			
		||||
endchoice
 | 
			
		||||
 | 
			
		||||
config FEC_ENET
 | 
			
		||||
	bool "860T FEC Ethernet"
 | 
			
		||||
	depends on NET_ETHERNET
 | 
			
		||||
	help
 | 
			
		||||
	  Enable Ethernet support via the Fast Ethernet Controller (FCC) on
 | 
			
		||||
	  the Motorola MPC8260.
 | 
			
		||||
 | 
			
		||||
config USE_MDIO
 | 
			
		||||
	bool "Use MDIO for PHY configuration"
 | 
			
		||||
	depends on FEC_ENET
 | 
			
		||||
	help
 | 
			
		||||
	  On some boards the hardware configuration of the ethernet PHY can be
 | 
			
		||||
	  used without any software interaction over the MDIO interface, so
 | 
			
		||||
	  all MII code can be omitted. Say N here if unsure or if you don't
 | 
			
		||||
	  need link status reports.
 | 
			
		||||
 | 
			
		||||
config  FEC_AM79C874
 | 
			
		||||
	bool "Support AMD79C874 PHY"
 | 
			
		||||
	depends on USE_MDIO
 | 
			
		||||
 | 
			
		||||
config FEC_LXT970
 | 
			
		||||
	bool "Support LXT970 PHY"
 | 
			
		||||
	depends on USE_MDIO
 | 
			
		||||
 | 
			
		||||
config FEC_LXT971
 | 
			
		||||
	bool "Support LXT971 PHY"
 | 
			
		||||
	depends on USE_MDIO
 | 
			
		||||
	
 | 
			
		||||
config FEC_QS6612
 | 
			
		||||
	bool "Support QS6612 PHY"
 | 
			
		||||
	depends on USE_MDIO
 | 
			
		||||
	
 | 
			
		||||
config ENET_BIG_BUFFERS
 | 
			
		||||
	bool "Use Big CPM Ethernet Buffers"
 | 
			
		||||
	depends on SCC_ENET || FEC_ENET
 | 
			
		||||
	help
 | 
			
		||||
	  Allocate large buffers for MPC8xx Ethernet. Increases throughput
 | 
			
		||||
	  and decreases the likelihood of dropped packets, but costs memory.
 | 
			
		||||
 | 
			
		||||
config HTDMSOUND
 | 
			
		||||
	bool "Embedded Planet HIOX Audio"
 | 
			
		||||
	depends on SOUND=y
 | 
			
		||||
 | 
			
		||||
# This doesn't really belong here, but it is convenient to ask
 | 
			
		||||
# 8xx specific questions.
 | 
			
		||||
comment "Generic MPC8xx Options"
 | 
			
		||||
 | 
			
		||||
config 8xx_COPYBACK
 | 
			
		||||
	bool "Copy-Back Data Cache (else Writethrough)"
 | 
			
		||||
	help
 | 
			
		||||
	  Saying Y here will cause the cache on an MPC8xx processor to be used
 | 
			
		||||
	  in Copy-Back mode.  If you say N here, it is used in Writethrough
 | 
			
		||||
	  mode.
 | 
			
		||||
 | 
			
		||||
	  If in doubt, say Y here.
 | 
			
		||||
 | 
			
		||||
config 8xx_CPU6
 | 
			
		||||
	bool "CPU6 Silicon Errata (860 Pre Rev. C)"
 | 
			
		||||
	help
 | 
			
		||||
	  MPC860 CPUs, prior to Rev C have some bugs in the silicon, which
 | 
			
		||||
	  require workarounds for Linux (and most other OSes to work).  If you
 | 
			
		||||
	  get a BUG() very early in boot, this might fix the problem.  For
 | 
			
		||||
	  more details read the document entitled "MPC860 Family Device Errata
 | 
			
		||||
	  Reference" on Motorola's website.  This option also incurs a
 | 
			
		||||
	  performance hit.
 | 
			
		||||
 | 
			
		||||
	  If in doubt, say N here.
 | 
			
		||||
 | 
			
		||||
choice
 | 
			
		||||
	prompt "Microcode patch selection"
 | 
			
		||||
	default NO_UCODE_PATCH
 | 
			
		||||
	help
 | 
			
		||||
	  Help not implemented yet, coming soon.
 | 
			
		||||
 | 
			
		||||
config NO_UCODE_PATCH
 | 
			
		||||
	bool "None"
 | 
			
		||||
 | 
			
		||||
config USB_SOF_UCODE_PATCH
 | 
			
		||||
	bool "USB SOF patch"
 | 
			
		||||
	help
 | 
			
		||||
	  Help not implemented yet, coming soon.
 | 
			
		||||
 | 
			
		||||
config I2C_SPI_UCODE_PATCH
 | 
			
		||||
	bool "I2C/SPI relocation patch"
 | 
			
		||||
	help
 | 
			
		||||
	  Help not implemented yet, coming soon.
 | 
			
		||||
 | 
			
		||||
config I2C_SPI_SMC1_UCODE_PATCH
 | 
			
		||||
	bool "I2C/SPI/SMC1 relocation patch"
 | 
			
		||||
	help
 | 
			
		||||
	  Help not implemented yet, coming soon.
 | 
			
		||||
 | 
			
		||||
endchoice
 | 
			
		||||
 | 
			
		||||
config UCODE_PATCH
 | 
			
		||||
	bool
 | 
			
		||||
	default y
 | 
			
		||||
	depends on !NO_UCODE_PATCH
 | 
			
		||||
 | 
			
		||||
endmenu
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										130
									
								
								arch/powerpc/platforms/apus/Kconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								arch/powerpc/platforms/apus/Kconfig
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,130 @@
 | 
			
		||||
 | 
			
		||||
config AMIGA
 | 
			
		||||
	bool
 | 
			
		||||
	depends on APUS
 | 
			
		||||
	default y
 | 
			
		||||
	help
 | 
			
		||||
	  This option enables support for the Amiga series of computers.
 | 
			
		||||
 | 
			
		||||
config ZORRO
 | 
			
		||||
	bool
 | 
			
		||||
	depends on APUS
 | 
			
		||||
	default y
 | 
			
		||||
	help
 | 
			
		||||
	  This enables support for the Zorro bus in the Amiga. If you have
 | 
			
		||||
	  expansion cards in your Amiga that conform to the Amiga
 | 
			
		||||
	  AutoConfig(tm) specification, say Y, otherwise N. Note that even
 | 
			
		||||
	  expansion cards that do not fit in the Zorro slots but fit in e.g.
 | 
			
		||||
	  the CPU slot may fall in this category, so you have to say Y to let
 | 
			
		||||
	  Linux use these.
 | 
			
		||||
 | 
			
		||||
config ABSTRACT_CONSOLE
 | 
			
		||||
	bool
 | 
			
		||||
	depends on APUS
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config APUS_FAST_EXCEPT
 | 
			
		||||
	bool
 | 
			
		||||
	depends on APUS
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config AMIGA_PCMCIA
 | 
			
		||||
	bool "Amiga 1200/600 PCMCIA support"
 | 
			
		||||
	depends on APUS && EXPERIMENTAL
 | 
			
		||||
	help
 | 
			
		||||
	  Include support in the kernel for pcmcia on Amiga 1200 and Amiga
 | 
			
		||||
	  600. If you intend to use pcmcia cards say Y; otherwise say N.
 | 
			
		||||
 | 
			
		||||
config AMIGA_BUILTIN_SERIAL
 | 
			
		||||
	tristate "Amiga builtin serial support"
 | 
			
		||||
	depends on APUS
 | 
			
		||||
	help
 | 
			
		||||
	  If you want to use your Amiga's built-in serial port in Linux,
 | 
			
		||||
	  answer Y.
 | 
			
		||||
 | 
			
		||||
	  To compile this driver as a module, choose M here.
 | 
			
		||||
 | 
			
		||||
config GVPIOEXT
 | 
			
		||||
	tristate "GVP IO-Extender support"
 | 
			
		||||
	depends on APUS
 | 
			
		||||
	help
 | 
			
		||||
	  If you want to use a GVP IO-Extender serial card in Linux, say Y.
 | 
			
		||||
	  Otherwise, say N.
 | 
			
		||||
 | 
			
		||||
config GVPIOEXT_LP
 | 
			
		||||
	tristate "GVP IO-Extender parallel printer support"
 | 
			
		||||
	depends on GVPIOEXT
 | 
			
		||||
	help
 | 
			
		||||
	  Say Y to enable driving a printer from the parallel port on your
 | 
			
		||||
	  GVP IO-Extender card, N otherwise.
 | 
			
		||||
 | 
			
		||||
config GVPIOEXT_PLIP
 | 
			
		||||
	tristate "GVP IO-Extender PLIP support"
 | 
			
		||||
	depends on GVPIOEXT
 | 
			
		||||
	help
 | 
			
		||||
	  Say Y to enable doing IP over the parallel port on your GVP
 | 
			
		||||
	  IO-Extender card, N otherwise.
 | 
			
		||||
 | 
			
		||||
config MULTIFACE_III_TTY
 | 
			
		||||
	tristate "Multiface Card III serial support"
 | 
			
		||||
	depends on APUS
 | 
			
		||||
	help
 | 
			
		||||
	  If you want to use a Multiface III card's serial port in Linux,
 | 
			
		||||
	  answer Y.
 | 
			
		||||
 | 
			
		||||
	  To compile this driver as a module, choose M here.
 | 
			
		||||
 | 
			
		||||
config A2232
 | 
			
		||||
	tristate "Commodore A2232 serial support (EXPERIMENTAL)"
 | 
			
		||||
	depends on EXPERIMENTAL && APUS
 | 
			
		||||
	---help---
 | 
			
		||||
	  This option supports the 2232 7-port serial card shipped with the
 | 
			
		||||
	  Amiga 2000 and other Zorro-bus machines, dating from 1989.  At
 | 
			
		||||
	  a max of 19,200 bps, the ports are served by a 6551 ACIA UART chip
 | 
			
		||||
	  each, plus a 8520 CIA, and a master 6502 CPU and buffer as well. The
 | 
			
		||||
	  ports were connected with 8 pin DIN connectors on the card bracket,
 | 
			
		||||
	  for which 8 pin to DB25 adapters were supplied. The card also had
 | 
			
		||||
	  jumpers internally to toggle various pinning configurations.
 | 
			
		||||
 | 
			
		||||
	  This driver can be built as a module; but then "generic_serial"
 | 
			
		||||
	  will also be built as a module. This has to be loaded before
 | 
			
		||||
	  "ser_a2232". If you want to do this, answer M here.
 | 
			
		||||
 | 
			
		||||
config WHIPPET_SERIAL
 | 
			
		||||
	tristate "Hisoft Whippet PCMCIA serial support"
 | 
			
		||||
	depends on AMIGA_PCMCIA
 | 
			
		||||
	help
 | 
			
		||||
	  HiSoft has a web page at <http://www.hisoft.co.uk/>, but there
 | 
			
		||||
	  is no listing for the Whippet in their Amiga section.
 | 
			
		||||
 | 
			
		||||
config APNE
 | 
			
		||||
	tristate "PCMCIA NE2000 support"
 | 
			
		||||
	depends on AMIGA_PCMCIA
 | 
			
		||||
	help
 | 
			
		||||
	  If you have a PCMCIA NE2000 compatible adapter, say Y.  Otherwise,
 | 
			
		||||
	  say N.
 | 
			
		||||
 | 
			
		||||
	  To compile this driver as a module, choose M here: the
 | 
			
		||||
	  module will be called apne.
 | 
			
		||||
 | 
			
		||||
config SERIAL_CONSOLE
 | 
			
		||||
	bool "Support for serial port console"
 | 
			
		||||
	depends on APUS && (AMIGA_BUILTIN_SERIAL=y || GVPIOEXT=y || MULTIFACE_III_TTY=y)
 | 
			
		||||
 | 
			
		||||
config HEARTBEAT
 | 
			
		||||
	bool "Use power LED as a heartbeat"
 | 
			
		||||
	depends on APUS
 | 
			
		||||
	help
 | 
			
		||||
	  Use the power-on LED on your machine as a load meter.  The exact
 | 
			
		||||
	  behavior is platform-dependent, but normally the flash frequency is
 | 
			
		||||
	  a hyperbolic function of the 5-minute load average.
 | 
			
		||||
 | 
			
		||||
config PROC_HARDWARE
 | 
			
		||||
	bool "/proc/hardware support"
 | 
			
		||||
	depends on APUS
 | 
			
		||||
 | 
			
		||||
source "drivers/zorro/Kconfig"
 | 
			
		||||
 | 
			
		||||
config PCI_PERMEDIA
 | 
			
		||||
	bool "PCI for Permedia2"
 | 
			
		||||
	depends on !4xx && !8xx && APUS
 | 
			
		||||
							
								
								
									
										313
									
								
								arch/powerpc/platforms/embedded6xx/Kconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										313
									
								
								arch/powerpc/platforms/embedded6xx/Kconfig
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,313 @@
 | 
			
		||||
choice
 | 
			
		||||
	prompt "Machine Type"
 | 
			
		||||
	depends on EMBEDDED6xx
 | 
			
		||||
 | 
			
		||||
config APUS
 | 
			
		||||
	bool "Amiga-APUS"
 | 
			
		||||
	depends on BROKEN
 | 
			
		||||
	help
 | 
			
		||||
	  Select APUS if configuring for a PowerUP Amiga.
 | 
			
		||||
	  More information is available at:
 | 
			
		||||
	  <http://linux-apus.sourceforge.net/>.
 | 
			
		||||
 | 
			
		||||
config KATANA
 | 
			
		||||
	bool "Artesyn-Katana"
 | 
			
		||||
	help
 | 
			
		||||
	  Select KATANA if configuring an Artesyn KATANA 750i or 3750
 | 
			
		||||
	  cPCI board.
 | 
			
		||||
 | 
			
		||||
config WILLOW
 | 
			
		||||
	bool "Cogent-Willow"
 | 
			
		||||
 | 
			
		||||
config CPCI690
 | 
			
		||||
	bool "Force-CPCI690"
 | 
			
		||||
	help
 | 
			
		||||
	  Select CPCI690 if configuring a Force CPCI690 cPCI board.
 | 
			
		||||
 | 
			
		||||
config POWERPMC250
 | 
			
		||||
	bool "Force-PowerPMC250"
 | 
			
		||||
 | 
			
		||||
config CHESTNUT
 | 
			
		||||
	bool "IBM 750FX Eval board or 750GX Eval board"
 | 
			
		||||
	help
 | 
			
		||||
	  Select CHESTNUT if configuring an IBM 750FX Eval Board or a
 | 
			
		||||
	  IBM 750GX Eval board.
 | 
			
		||||
 | 
			
		||||
config SPRUCE
 | 
			
		||||
	bool "IBM-Spruce"
 | 
			
		||||
 | 
			
		||||
config HDPU
 | 
			
		||||
	bool "Sky-HDPU"
 | 
			
		||||
	help
 | 
			
		||||
	  Select HDPU if configuring a Sky Computers Compute Blade.
 | 
			
		||||
 | 
			
		||||
config HDPU_FEATURES
 | 
			
		||||
	depends HDPU
 | 
			
		||||
	tristate "HDPU-Features"
 | 
			
		||||
	help
 | 
			
		||||
	  Select to enable HDPU enhanced features.
 | 
			
		||||
 | 
			
		||||
config EV64260
 | 
			
		||||
	bool "Marvell-EV64260BP"
 | 
			
		||||
	help
 | 
			
		||||
	  Select EV64260 if configuring a Marvell (formerly Galileo)
 | 
			
		||||
	  EV64260BP Evaluation platform.
 | 
			
		||||
 | 
			
		||||
config LOPEC
 | 
			
		||||
	bool "Motorola-LoPEC"
 | 
			
		||||
 | 
			
		||||
config MVME5100
 | 
			
		||||
	bool "Motorola-MVME5100"
 | 
			
		||||
 | 
			
		||||
config PPLUS
 | 
			
		||||
	bool "Motorola-PowerPlus"
 | 
			
		||||
 | 
			
		||||
config PRPMC750
 | 
			
		||||
	bool "Motorola-PrPMC750"
 | 
			
		||||
 | 
			
		||||
config PRPMC800
 | 
			
		||||
	bool "Motorola-PrPMC800"
 | 
			
		||||
 | 
			
		||||
config SANDPOINT
 | 
			
		||||
	bool "Motorola-Sandpoint"
 | 
			
		||||
	help
 | 
			
		||||
	  Select SANDPOINT if configuring for a Motorola Sandpoint X3
 | 
			
		||||
	  (any flavor).
 | 
			
		||||
 | 
			
		||||
config RADSTONE_PPC7D
 | 
			
		||||
	bool "Radstone Technology PPC7D board"
 | 
			
		||||
 | 
			
		||||
config PAL4
 | 
			
		||||
	bool "SBS-Palomar4"
 | 
			
		||||
 | 
			
		||||
config GEMINI
 | 
			
		||||
	bool "Synergy-Gemini"
 | 
			
		||||
	depends on BROKEN
 | 
			
		||||
	help
 | 
			
		||||
	  Select Gemini if configuring for a Synergy Microsystems' Gemini
 | 
			
		||||
	  series Single Board Computer.  More information is available at:
 | 
			
		||||
	  <http://www.synergymicro.com/PressRel/97_10_15.html>.
 | 
			
		||||
 | 
			
		||||
config EST8260
 | 
			
		||||
	bool "EST8260"
 | 
			
		||||
	---help---
 | 
			
		||||
	  The EST8260 is a single-board computer manufactured by Wind River
 | 
			
		||||
	  Systems, Inc. (formerly Embedded Support Tools Corp.) and based on
 | 
			
		||||
	  the MPC8260.  Wind River Systems has a website at
 | 
			
		||||
	  <http://www.windriver.com/>, but the EST8260 cannot be found on it
 | 
			
		||||
	  and has probably been discontinued or rebadged.
 | 
			
		||||
 | 
			
		||||
config SBC82xx
 | 
			
		||||
	bool "SBC82xx"
 | 
			
		||||
	---help---
 | 
			
		||||
	  SBC PowerQUICC II, single-board computer with MPC82xx CPU
 | 
			
		||||
	  Manufacturer: Wind River Systems, Inc.
 | 
			
		||||
	  Date of Release: May 2003
 | 
			
		||||
	  End of Life: -
 | 
			
		||||
	  URL: <http://www.windriver.com/>
 | 
			
		||||
 | 
			
		||||
config SBS8260
 | 
			
		||||
	bool "SBS8260"
 | 
			
		||||
 | 
			
		||||
config RPX8260
 | 
			
		||||
	bool "RPXSUPER"
 | 
			
		||||
 | 
			
		||||
config TQM8260
 | 
			
		||||
	bool "TQM8260"
 | 
			
		||||
	---help---
 | 
			
		||||
	  MPC8260 based module, little larger than credit card,
 | 
			
		||||
	  up to 128 MB global + 64 MB local RAM, 32 MB Flash,
 | 
			
		||||
	  32 kB EEPROM, 256 kB L@ Cache, 10baseT + 100baseT Ethernet,
 | 
			
		||||
	  2 x serial ports, ...
 | 
			
		||||
	  Manufacturer: TQ Components, www.tq-group.de
 | 
			
		||||
	  Date of Release: June 2001
 | 
			
		||||
	  End of Life: not yet :-)
 | 
			
		||||
	  URL: <http://www.denx.de/PDF/TQM82xx_SPEC_Rev005.pdf>
 | 
			
		||||
 | 
			
		||||
config ADS8272
 | 
			
		||||
	bool "ADS8272"
 | 
			
		||||
 | 
			
		||||
config PQ2FADS
 | 
			
		||||
	bool "Freescale-PQ2FADS"
 | 
			
		||||
	help
 | 
			
		||||
	  Select PQ2FADS if you wish to configure for a Freescale
 | 
			
		||||
	  PQ2FADS board (-VR or -ZU).
 | 
			
		||||
 | 
			
		||||
config LITE5200
 | 
			
		||||
	bool "Freescale LITE5200 / (IceCube)"
 | 
			
		||||
	select PPC_MPC52xx
 | 
			
		||||
	help
 | 
			
		||||
	  Support for the LITE5200 dev board for the MPC5200 from Freescale.
 | 
			
		||||
	  This is for the LITE5200 version 2.0 board. Don't know if it changes
 | 
			
		||||
	  much but it's only been tested on this board version. I think this
 | 
			
		||||
	  board is also known as IceCube.
 | 
			
		||||
 | 
			
		||||
config MPC834x_SYS
 | 
			
		||||
	bool "Freescale MPC834x SYS"
 | 
			
		||||
	help
 | 
			
		||||
	  This option enables support for the MPC 834x SYS evaluation board.
 | 
			
		||||
 | 
			
		||||
	  Be aware that PCI buses can only function when SYS board is plugged
 | 
			
		||||
	  into the PIB (Platform IO Board) board from Freescale which provide
 | 
			
		||||
	  3 PCI slots.  The PIBs PCI initialization is the bootloader's
 | 
			
		||||
	  responsiblilty.
 | 
			
		||||
 | 
			
		||||
config EV64360
 | 
			
		||||
	bool "Marvell-EV64360BP"
 | 
			
		||||
	help
 | 
			
		||||
	  Select EV64360 if configuring a Marvell EV64360BP Evaluation
 | 
			
		||||
	  platform.
 | 
			
		||||
endchoice
 | 
			
		||||
 | 
			
		||||
config PQ2ADS
 | 
			
		||||
	bool
 | 
			
		||||
	depends on ADS8272
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config TQM8xxL
 | 
			
		||||
	bool
 | 
			
		||||
	depends on 8xx && (TQM823L || TQM850L || FPS850L || TQM855L || TQM860L)
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config PPC_MPC52xx
 | 
			
		||||
	bool
 | 
			
		||||
 | 
			
		||||
config 8260
 | 
			
		||||
	bool "CPM2 Support" if WILLOW
 | 
			
		||||
	depends on 6xx
 | 
			
		||||
	default y if TQM8260 || RPX8260 || EST8260 || SBS8260 || SBC82xx || PQ2FADS
 | 
			
		||||
	help
 | 
			
		||||
	  The MPC8260 is a typical embedded CPU made by Motorola.  Selecting
 | 
			
		||||
	  this option means that you wish to build a kernel for a machine with
 | 
			
		||||
	  an 8260 class CPU.
 | 
			
		||||
 | 
			
		||||
config 8272
 | 
			
		||||
	bool
 | 
			
		||||
	depends on 6xx
 | 
			
		||||
	default y if ADS8272
 | 
			
		||||
	select 8260
 | 
			
		||||
	help
 | 
			
		||||
	  The MPC8272 CPM has a different internal dpram setup than other CPM2
 | 
			
		||||
	  devices
 | 
			
		||||
 | 
			
		||||
config 83xx
 | 
			
		||||
	bool
 | 
			
		||||
	default y if MPC834x_SYS
 | 
			
		||||
 | 
			
		||||
config MPC834x
 | 
			
		||||
	bool
 | 
			
		||||
	default y if MPC834x_SYS
 | 
			
		||||
 | 
			
		||||
config CPM2
 | 
			
		||||
	bool
 | 
			
		||||
	depends on 8260 || MPC8560 || MPC8555
 | 
			
		||||
	default y
 | 
			
		||||
	help
 | 
			
		||||
	  The CPM2 (Communications Processor Module) is a coprocessor on
 | 
			
		||||
	  embedded CPUs made by Motorola.  Selecting this option means that
 | 
			
		||||
	  you wish to build a kernel for a machine with a CPM2 coprocessor
 | 
			
		||||
	  on it (826x, 827x, 8560).
 | 
			
		||||
 | 
			
		||||
config PPC_GEN550
 | 
			
		||||
	bool
 | 
			
		||||
	depends on SANDPOINT || SPRUCE || PPLUS || \
 | 
			
		||||
		PRPMC750 || PRPMC800 || LOPEC || \
 | 
			
		||||
		(EV64260 && !SERIAL_MPSC) || CHESTNUT || RADSTONE_PPC7D || \
 | 
			
		||||
		83xx
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config FORCE
 | 
			
		||||
	bool
 | 
			
		||||
	depends on 6xx && POWERPMC250
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config GT64260
 | 
			
		||||
	bool
 | 
			
		||||
	depends on EV64260 || CPCI690
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config MV64360		# Really MV64360 & MV64460
 | 
			
		||||
	bool
 | 
			
		||||
	depends on CHESTNUT || KATANA || RADSTONE_PPC7D || HDPU || EV64360
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config MV64X60
 | 
			
		||||
	bool
 | 
			
		||||
	depends on (GT64260 || MV64360)
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
menu "Set bridge options"
 | 
			
		||||
	depends on MV64X60
 | 
			
		||||
 | 
			
		||||
config NOT_COHERENT_CACHE
 | 
			
		||||
	bool "Turn off Cache Coherency"
 | 
			
		||||
	default n
 | 
			
		||||
	help
 | 
			
		||||
	  Some 64x60 bridges lock up when trying to enforce cache coherency.
 | 
			
		||||
	  When this option is selected, cache coherency will be turned off.
 | 
			
		||||
	  Note that this can cause other problems (e.g., stale data being
 | 
			
		||||
	  speculatively loaded via a cached mapping).  Use at your own risk.
 | 
			
		||||
 | 
			
		||||
config MV64X60_BASE
 | 
			
		||||
	hex "Set bridge base used by firmware"
 | 
			
		||||
	default "0xf1000000"
 | 
			
		||||
	help
 | 
			
		||||
	  A firmware can leave the base address of the bridge's registers at
 | 
			
		||||
	  a non-standard location.  If so, set this value to reflect the
 | 
			
		||||
	  address of that non-standard location.
 | 
			
		||||
 | 
			
		||||
config MV64X60_NEW_BASE
 | 
			
		||||
	hex "Set bridge base used by kernel"
 | 
			
		||||
	default "0xf1000000"
 | 
			
		||||
	help
 | 
			
		||||
	  If the current base address of the bridge's registers is not where
 | 
			
		||||
	  you want it, set this value to the address that you want it moved to.
 | 
			
		||||
 | 
			
		||||
endmenu
 | 
			
		||||
 | 
			
		||||
config NONMONARCH_SUPPORT
 | 
			
		||||
	bool "Enable Non-Monarch Support"
 | 
			
		||||
	depends on PRPMC800
 | 
			
		||||
 | 
			
		||||
config HARRIER
 | 
			
		||||
	bool
 | 
			
		||||
	depends on PRPMC800
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config EPIC_SERIAL_MODE
 | 
			
		||||
	bool
 | 
			
		||||
	depends on 6xx && (LOPEC || SANDPOINT)
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config MPC10X_BRIDGE
 | 
			
		||||
	bool
 | 
			
		||||
	depends on POWERPMC250 || LOPEC || SANDPOINT
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config MPC10X_OPENPIC
 | 
			
		||||
	bool
 | 
			
		||||
	depends on POWERPMC250 || LOPEC || SANDPOINT
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config MPC10X_STORE_GATHERING
 | 
			
		||||
	bool "Enable MPC10x store gathering"
 | 
			
		||||
	depends on MPC10X_BRIDGE
 | 
			
		||||
 | 
			
		||||
config SANDPOINT_ENABLE_UART1
 | 
			
		||||
	bool "Enable DUART mode on Sandpoint"
 | 
			
		||||
	depends on SANDPOINT
 | 
			
		||||
	help
 | 
			
		||||
	  If this option is enabled then the MPC824x processor will run
 | 
			
		||||
	  in DUART mode instead of UART mode.
 | 
			
		||||
 | 
			
		||||
config HARRIER_STORE_GATHERING
 | 
			
		||||
	bool "Enable Harrier store gathering"
 | 
			
		||||
	depends on HARRIER
 | 
			
		||||
 | 
			
		||||
config MVME5100_IPMC761_PRESENT
 | 
			
		||||
	bool "MVME5100 configured with an IPMC761"
 | 
			
		||||
	depends on MVME5100
 | 
			
		||||
 | 
			
		||||
config SPRUCE_BAUD_33M
 | 
			
		||||
	bool "Spruce baud clock support"
 | 
			
		||||
	depends on SPRUCE
 | 
			
		||||
							
								
								
									
										31
									
								
								arch/powerpc/platforms/iseries/Kconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								arch/powerpc/platforms/iseries/Kconfig
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
 | 
			
		||||
menu "iSeries device drivers"
 | 
			
		||||
	depends on PPC_ISERIES
 | 
			
		||||
 | 
			
		||||
config VIOCONS
 | 
			
		||||
	tristate "iSeries Virtual Console Support"
 | 
			
		||||
 | 
			
		||||
config VIODASD
 | 
			
		||||
	tristate "iSeries Virtual I/O disk support"
 | 
			
		||||
	help
 | 
			
		||||
	  If you are running on an iSeries system and you want to use
 | 
			
		||||
 	  virtual disks created and managed by OS/400, say Y.
 | 
			
		||||
 | 
			
		||||
config VIOCD
 | 
			
		||||
	tristate "iSeries Virtual I/O CD support"
 | 
			
		||||
	help
 | 
			
		||||
	  If you are running Linux on an IBM iSeries system and you want to
 | 
			
		||||
	  read a CD drive owned by OS/400, say Y here.
 | 
			
		||||
 | 
			
		||||
config VIOTAPE
 | 
			
		||||
	tristate "iSeries Virtual Tape Support"
 | 
			
		||||
	help
 | 
			
		||||
	  If you are running Linux on an iSeries system and you want Linux
 | 
			
		||||
	  to read and/or write a tape drive owned by OS/400, say Y here.
 | 
			
		||||
 | 
			
		||||
endmenu
 | 
			
		||||
 | 
			
		||||
config VIOPATH
 | 
			
		||||
	bool
 | 
			
		||||
	depends on VIOCONS || VIODASD || VIOCD || VIOTAPE || VETH
 | 
			
		||||
	default y
 | 
			
		||||
							
								
								
									
										9
									
								
								arch/powerpc/platforms/powermac/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								arch/powerpc/platforms/powermac/Makefile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
obj-$(CONFIG_PPC_PMAC)		+= pmac_pic.o pmac_setup.o pmac_time.o \
 | 
			
		||||
				   pmac_feature.o pmac_pci.o pmac_sleep.o \
 | 
			
		||||
				   pmac_low_i2c.o pmac_cache.o
 | 
			
		||||
obj-$(CONFIG_PMAC_BACKLIGHT)	+= pmac_backlight.o
 | 
			
		||||
obj-$(CONFIG_CPU_FREQ_PMAC)	+= pmac_cpufreq.o
 | 
			
		||||
ifeq ($(CONFIG_PPC_PMAC),y)
 | 
			
		||||
obj-$(CONFIG_NVRAM)		+= pmac_nvram.o
 | 
			
		||||
obj-$(CONFIG_SMP)		+= pmac_smp.o
 | 
			
		||||
endif
 | 
			
		||||
							
								
								
									
										31
									
								
								arch/powerpc/platforms/powermac/pmac.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								arch/powerpc/platforms/powermac/pmac.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
#ifndef __PMAC_H__
 | 
			
		||||
#define __PMAC_H__
 | 
			
		||||
 | 
			
		||||
#include <linux/pci.h>
 | 
			
		||||
#include <linux/ide.h>
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Declaration for the various functions exported by the
 | 
			
		||||
 * pmac_* files. Mostly for use by pmac_setup
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
extern void pmac_get_boot_time(struct rtc_time *tm);
 | 
			
		||||
extern void pmac_get_rtc_time(struct rtc_time *tm);
 | 
			
		||||
extern int  pmac_set_rtc_time(struct rtc_time *tm);
 | 
			
		||||
extern void pmac_read_rtc_time(void);
 | 
			
		||||
extern void pmac_calibrate_decr(void);
 | 
			
		||||
 | 
			
		||||
extern void pmac_pcibios_fixup(void);
 | 
			
		||||
extern void pmac_pci_init(void);
 | 
			
		||||
extern void pmac_setup_pci_dma(void);
 | 
			
		||||
extern void pmac_check_ht_link(void);
 | 
			
		||||
 | 
			
		||||
extern void pmac_setup_smp(void);
 | 
			
		||||
 | 
			
		||||
extern unsigned long pmac_ide_get_base(int index);
 | 
			
		||||
extern void pmac_ide_init_hwif_ports(hw_regs_t *hw,
 | 
			
		||||
	unsigned long data_port, unsigned long ctrl_port, int *irq);
 | 
			
		||||
 | 
			
		||||
extern void pmac_nvram_init(void);
 | 
			
		||||
 | 
			
		||||
#endif /* __PMAC_H__ */
 | 
			
		||||
							
								
								
									
										202
									
								
								arch/powerpc/platforms/powermac/pmac_backlight.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								arch/powerpc/platforms/powermac/pmac_backlight.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,202 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Miscellaneous procedures for dealing with the PowerMac hardware.
 | 
			
		||||
 * Contains support for the backlight.
 | 
			
		||||
 *
 | 
			
		||||
 *   Copyright (C) 2000 Benjamin Herrenschmidt
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/config.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/stddef.h>
 | 
			
		||||
#include <linux/reboot.h>
 | 
			
		||||
#include <linux/nvram.h>
 | 
			
		||||
#include <linux/console.h>
 | 
			
		||||
#include <asm/sections.h>
 | 
			
		||||
#include <asm/ptrace.h>
 | 
			
		||||
#include <asm/io.h>
 | 
			
		||||
#include <asm/pgtable.h>
 | 
			
		||||
#include <asm/system.h>
 | 
			
		||||
#include <asm/prom.h>
 | 
			
		||||
#include <asm/machdep.h>
 | 
			
		||||
#include <asm/nvram.h>
 | 
			
		||||
#include <asm/backlight.h>
 | 
			
		||||
 | 
			
		||||
#include <linux/adb.h>
 | 
			
		||||
#include <linux/pmu.h>
 | 
			
		||||
 | 
			
		||||
static struct backlight_controller *backlighter;
 | 
			
		||||
static void* backlighter_data;
 | 
			
		||||
static int backlight_autosave;
 | 
			
		||||
static int backlight_level = BACKLIGHT_MAX;
 | 
			
		||||
static int backlight_enabled = 1;
 | 
			
		||||
static int backlight_req_level = -1;
 | 
			
		||||
static int backlight_req_enable = -1;
 | 
			
		||||
 | 
			
		||||
static void backlight_callback(void *);
 | 
			
		||||
static DECLARE_WORK(backlight_work, backlight_callback, NULL);
 | 
			
		||||
 | 
			
		||||
void register_backlight_controller(struct backlight_controller *ctrler,
 | 
			
		||||
					  void *data, char *type)
 | 
			
		||||
{
 | 
			
		||||
	struct device_node* bk_node;
 | 
			
		||||
	char *prop;
 | 
			
		||||
	int valid = 0;
 | 
			
		||||
 | 
			
		||||
	/* There's already a matching controller, bail out */
 | 
			
		||||
	if (backlighter != NULL)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	bk_node = find_devices("backlight");
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_ADB_PMU
 | 
			
		||||
	/* Special case for the old PowerBook since I can't test on it */
 | 
			
		||||
	backlight_autosave = machine_is_compatible("AAPL,3400/2400")
 | 
			
		||||
		|| machine_is_compatible("AAPL,3500");
 | 
			
		||||
	if ((backlight_autosave
 | 
			
		||||
	     || machine_is_compatible("AAPL,PowerBook1998")
 | 
			
		||||
	     || machine_is_compatible("PowerBook1,1"))
 | 
			
		||||
	    && !strcmp(type, "pmu"))
 | 
			
		||||
		valid = 1;
 | 
			
		||||
#endif
 | 
			
		||||
	if (bk_node) {
 | 
			
		||||
		prop = get_property(bk_node, "backlight-control", NULL);
 | 
			
		||||
		if (prop && !strncmp(prop, type, strlen(type)))
 | 
			
		||||
			valid = 1;
 | 
			
		||||
	}
 | 
			
		||||
	if (!valid)
 | 
			
		||||
		return;
 | 
			
		||||
	backlighter = ctrler;
 | 
			
		||||
	backlighter_data = data;
 | 
			
		||||
 | 
			
		||||
	if (bk_node && !backlight_autosave)
 | 
			
		||||
		prop = get_property(bk_node, "bklt", NULL);
 | 
			
		||||
	else
 | 
			
		||||
		prop = NULL;
 | 
			
		||||
	if (prop) {
 | 
			
		||||
		backlight_level = ((*prop)+1) >> 1;
 | 
			
		||||
		if (backlight_level > BACKLIGHT_MAX)
 | 
			
		||||
			backlight_level = BACKLIGHT_MAX;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_ADB_PMU
 | 
			
		||||
	if (backlight_autosave) {
 | 
			
		||||
		struct adb_request req;
 | 
			
		||||
		pmu_request(&req, NULL, 2, 0xd9, 0);
 | 
			
		||||
		while (!req.complete)
 | 
			
		||||
			pmu_poll();
 | 
			
		||||
		backlight_level = req.reply[0] >> 4;
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
	acquire_console_sem();
 | 
			
		||||
	if (!backlighter->set_enable(1, backlight_level, data))
 | 
			
		||||
		backlight_enabled = 1;
 | 
			
		||||
	release_console_sem();
 | 
			
		||||
 | 
			
		||||
	printk(KERN_INFO "Registered \"%s\" backlight controller,"
 | 
			
		||||
	       "level: %d/15\n", type, backlight_level);
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(register_backlight_controller);
 | 
			
		||||
 | 
			
		||||
void unregister_backlight_controller(struct backlight_controller
 | 
			
		||||
					    *ctrler, void *data)
 | 
			
		||||
{
 | 
			
		||||
	/* We keep the current backlight level (for now) */
 | 
			
		||||
	if (ctrler == backlighter && data == backlighter_data)
 | 
			
		||||
		backlighter = NULL;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(unregister_backlight_controller);
 | 
			
		||||
 | 
			
		||||
static int __set_backlight_enable(int enable)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	if (!backlighter)
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
	acquire_console_sem();
 | 
			
		||||
	rc = backlighter->set_enable(enable, backlight_level,
 | 
			
		||||
				     backlighter_data);
 | 
			
		||||
	if (!rc)
 | 
			
		||||
		backlight_enabled = enable;
 | 
			
		||||
	release_console_sem();
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
int set_backlight_enable(int enable)
 | 
			
		||||
{
 | 
			
		||||
	if (!backlighter)
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
	backlight_req_enable = enable;
 | 
			
		||||
	schedule_work(&backlight_work);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORT_SYMBOL(set_backlight_enable);
 | 
			
		||||
 | 
			
		||||
int get_backlight_enable(void)
 | 
			
		||||
{
 | 
			
		||||
	if (!backlighter)
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
	return backlight_enabled;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(get_backlight_enable);
 | 
			
		||||
 | 
			
		||||
static int __set_backlight_level(int level)
 | 
			
		||||
{
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
 | 
			
		||||
	if (!backlighter)
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
	if (level < BACKLIGHT_MIN)
 | 
			
		||||
		level = BACKLIGHT_OFF;
 | 
			
		||||
	if (level > BACKLIGHT_MAX)
 | 
			
		||||
		level = BACKLIGHT_MAX;
 | 
			
		||||
	acquire_console_sem();
 | 
			
		||||
	if (backlight_enabled)
 | 
			
		||||
		rc = backlighter->set_level(level, backlighter_data);
 | 
			
		||||
	if (!rc)
 | 
			
		||||
		backlight_level = level;
 | 
			
		||||
	release_console_sem();
 | 
			
		||||
	if (!rc && !backlight_autosave) {
 | 
			
		||||
		level <<=1;
 | 
			
		||||
		if (level & 0x10)
 | 
			
		||||
			level |= 0x01;
 | 
			
		||||
		// -- todo: save to property "bklt"
 | 
			
		||||
	}
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
int set_backlight_level(int level)
 | 
			
		||||
{
 | 
			
		||||
	if (!backlighter)
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
	backlight_req_level = level;
 | 
			
		||||
	schedule_work(&backlight_work);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORT_SYMBOL(set_backlight_level);
 | 
			
		||||
 | 
			
		||||
int get_backlight_level(void)
 | 
			
		||||
{
 | 
			
		||||
	if (!backlighter)
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
	return backlight_level;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(get_backlight_level);
 | 
			
		||||
 | 
			
		||||
static void backlight_callback(void *dummy)
 | 
			
		||||
{
 | 
			
		||||
	int level, enable;
 | 
			
		||||
 | 
			
		||||
	do {
 | 
			
		||||
		level = backlight_req_level;
 | 
			
		||||
		enable = backlight_req_enable;
 | 
			
		||||
		mb();
 | 
			
		||||
 | 
			
		||||
		if (level >= 0)
 | 
			
		||||
			__set_backlight_level(level);
 | 
			
		||||
		if (enable >= 0)
 | 
			
		||||
			__set_backlight_enable(enable);
 | 
			
		||||
	} while(cmpxchg(&backlight_req_level, level, -1) != level ||
 | 
			
		||||
		cmpxchg(&backlight_req_enable, enable, -1) != enable);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										359
									
								
								arch/powerpc/platforms/powermac/pmac_cache.S
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										359
									
								
								arch/powerpc/platforms/powermac/pmac_cache.S
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,359 @@
 | 
			
		||||
/*
 | 
			
		||||
 * This file contains low-level cache management functions
 | 
			
		||||
 * used for sleep and CPU speed changes on Apple machines.
 | 
			
		||||
 * (In fact the only thing that is Apple-specific is that we assume
 | 
			
		||||
 * that we can read from ROM at physical address 0xfff00000.)
 | 
			
		||||
 *
 | 
			
		||||
 *    Copyright (C) 2004 Paul Mackerras (paulus@samba.org) and
 | 
			
		||||
 *                       Benjamin Herrenschmidt (benh@kernel.crashing.org)
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version
 | 
			
		||||
 * 2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/config.h>
 | 
			
		||||
#include <asm/processor.h>
 | 
			
		||||
#include <asm/ppc_asm.h>
 | 
			
		||||
#include <asm/cputable.h>
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Flush and disable all data caches (dL1, L2, L3). This is used
 | 
			
		||||
 * when going to sleep, when doing a PMU based cpufreq transition,
 | 
			
		||||
 * or when "offlining" a CPU on SMP machines. This code is over
 | 
			
		||||
 * paranoid, but I've had enough issues with various CPU revs and
 | 
			
		||||
 * bugs that I decided it was worth beeing over cautious
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
_GLOBAL(flush_disable_caches)
 | 
			
		||||
#ifndef CONFIG_6xx
 | 
			
		||||
	blr
 | 
			
		||||
#else
 | 
			
		||||
BEGIN_FTR_SECTION
 | 
			
		||||
	b	flush_disable_745x
 | 
			
		||||
END_FTR_SECTION_IFSET(CPU_FTR_SPEC7450)
 | 
			
		||||
BEGIN_FTR_SECTION
 | 
			
		||||
	b	flush_disable_75x
 | 
			
		||||
END_FTR_SECTION_IFSET(CPU_FTR_L2CR)
 | 
			
		||||
	b	__flush_disable_L1
 | 
			
		||||
 | 
			
		||||
/* This is the code for G3 and 74[01]0 */
 | 
			
		||||
flush_disable_75x:
 | 
			
		||||
	mflr	r10
 | 
			
		||||
 | 
			
		||||
	/* Turn off EE and DR in MSR */
 | 
			
		||||
	mfmsr	r11
 | 
			
		||||
	rlwinm	r0,r11,0,~MSR_EE
 | 
			
		||||
	rlwinm	r0,r0,0,~MSR_DR
 | 
			
		||||
	sync
 | 
			
		||||
	mtmsr	r0
 | 
			
		||||
	isync
 | 
			
		||||
 | 
			
		||||
	/* Stop DST streams */
 | 
			
		||||
BEGIN_FTR_SECTION
 | 
			
		||||
	DSSALL
 | 
			
		||||
	sync
 | 
			
		||||
END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC)
 | 
			
		||||
 | 
			
		||||
	/* Stop DPM */
 | 
			
		||||
	mfspr	r8,SPRN_HID0		/* Save SPRN_HID0 in r8 */
 | 
			
		||||
	rlwinm	r4,r8,0,12,10		/* Turn off HID0[DPM] */
 | 
			
		||||
	sync
 | 
			
		||||
	mtspr	SPRN_HID0,r4		/* Disable DPM */
 | 
			
		||||
	sync
 | 
			
		||||
 | 
			
		||||
	/* Disp-flush L1. We have a weird problem here that I never
 | 
			
		||||
	 * totally figured out. On 750FX, using the ROM for the flush
 | 
			
		||||
	 * results in a non-working flush. We use that workaround for
 | 
			
		||||
	 * now until I finally understand what's going on. --BenH
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
	/* ROM base by default */
 | 
			
		||||
	lis	r4,0xfff0
 | 
			
		||||
	mfpvr	r3
 | 
			
		||||
	srwi	r3,r3,16
 | 
			
		||||
	cmplwi	cr0,r3,0x7000
 | 
			
		||||
	bne+	1f
 | 
			
		||||
	/* RAM base on 750FX */
 | 
			
		||||
	li	r4,0
 | 
			
		||||
1:	li	r4,0x4000
 | 
			
		||||
	mtctr	r4
 | 
			
		||||
1:	lwz	r0,0(r4)
 | 
			
		||||
	addi	r4,r4,32
 | 
			
		||||
	bdnz	1b
 | 
			
		||||
	sync
 | 
			
		||||
	isync
 | 
			
		||||
 | 
			
		||||
	/* Disable / invalidate / enable L1 data */
 | 
			
		||||
	mfspr	r3,SPRN_HID0
 | 
			
		||||
	rlwinm	r3,r3,0,~(HID0_DCE | HID0_ICE)
 | 
			
		||||
	mtspr	SPRN_HID0,r3
 | 
			
		||||
	sync
 | 
			
		||||
	isync
 | 
			
		||||
	ori	r3,r3,(HID0_DCE|HID0_DCI|HID0_ICE|HID0_ICFI)
 | 
			
		||||
	sync
 | 
			
		||||
	isync
 | 
			
		||||
	mtspr	SPRN_HID0,r3
 | 
			
		||||
	xori	r3,r3,(HID0_DCI|HID0_ICFI)
 | 
			
		||||
	mtspr	SPRN_HID0,r3
 | 
			
		||||
	sync
 | 
			
		||||
 | 
			
		||||
	/* Get the current enable bit of the L2CR into r4 */
 | 
			
		||||
	mfspr	r5,SPRN_L2CR
 | 
			
		||||
	/* Set to data-only (pre-745x bit) */
 | 
			
		||||
	oris	r3,r5,L2CR_L2DO@h
 | 
			
		||||
	b	2f
 | 
			
		||||
	/* When disabling L2, code must be in L1 */
 | 
			
		||||
	.balign 32
 | 
			
		||||
1:	mtspr	SPRN_L2CR,r3
 | 
			
		||||
3:	sync
 | 
			
		||||
	isync
 | 
			
		||||
	b	1f
 | 
			
		||||
2:	b	3f
 | 
			
		||||
3:	sync
 | 
			
		||||
	isync
 | 
			
		||||
	b	1b
 | 
			
		||||
1:	/* disp-flush L2. The interesting thing here is that the L2 can be
 | 
			
		||||
	 * up to 2Mb ... so using the ROM, we'll end up wrapping back to memory
 | 
			
		||||
	 * but that is probbaly fine. We disp-flush over 4Mb to be safe
 | 
			
		||||
	 */
 | 
			
		||||
	lis	r4,2
 | 
			
		||||
	mtctr	r4
 | 
			
		||||
	lis	r4,0xfff0
 | 
			
		||||
1:	lwz	r0,0(r4)
 | 
			
		||||
	addi	r4,r4,32
 | 
			
		||||
	bdnz	1b
 | 
			
		||||
	sync
 | 
			
		||||
	isync
 | 
			
		||||
	lis	r4,2
 | 
			
		||||
	mtctr	r4
 | 
			
		||||
	lis	r4,0xfff0
 | 
			
		||||
1:	dcbf	0,r4
 | 
			
		||||
	addi	r4,r4,32
 | 
			
		||||
	bdnz	1b
 | 
			
		||||
	sync
 | 
			
		||||
	isync
 | 
			
		||||
 | 
			
		||||
	/* now disable L2 */
 | 
			
		||||
	rlwinm	r5,r5,0,~L2CR_L2E
 | 
			
		||||
	b	2f
 | 
			
		||||
	/* When disabling L2, code must be in L1 */
 | 
			
		||||
	.balign 32
 | 
			
		||||
1:	mtspr	SPRN_L2CR,r5
 | 
			
		||||
3:	sync
 | 
			
		||||
	isync
 | 
			
		||||
	b	1f
 | 
			
		||||
2:	b	3f
 | 
			
		||||
3:	sync
 | 
			
		||||
	isync
 | 
			
		||||
	b	1b
 | 
			
		||||
1:	sync
 | 
			
		||||
	isync
 | 
			
		||||
	/* Invalidate L2. This is pre-745x, we clear the L2I bit ourselves */
 | 
			
		||||
	oris	r4,r5,L2CR_L2I@h
 | 
			
		||||
	mtspr	SPRN_L2CR,r4
 | 
			
		||||
	sync
 | 
			
		||||
	isync
 | 
			
		||||
 | 
			
		||||
	/* Wait for the invalidation to complete */
 | 
			
		||||
1:	mfspr	r3,SPRN_L2CR
 | 
			
		||||
	rlwinm.	r0,r3,0,31,31
 | 
			
		||||
	bne	1b
 | 
			
		||||
 | 
			
		||||
	/* Clear L2I */
 | 
			
		||||
	xoris	r4,r4,L2CR_L2I@h
 | 
			
		||||
	sync
 | 
			
		||||
	mtspr	SPRN_L2CR,r4
 | 
			
		||||
	sync
 | 
			
		||||
 | 
			
		||||
	/* now disable the L1 data cache */
 | 
			
		||||
	mfspr	r0,SPRN_HID0
 | 
			
		||||
	rlwinm	r0,r0,0,~(HID0_DCE|HID0_ICE)
 | 
			
		||||
	mtspr	SPRN_HID0,r0
 | 
			
		||||
	sync
 | 
			
		||||
	isync
 | 
			
		||||
 | 
			
		||||
	/* Restore HID0[DPM] to whatever it was before */
 | 
			
		||||
	sync
 | 
			
		||||
	mfspr	r0,SPRN_HID0
 | 
			
		||||
	rlwimi	r0,r8,0,11,11		/* Turn back HID0[DPM] */
 | 
			
		||||
	mtspr	SPRN_HID0,r0
 | 
			
		||||
	sync
 | 
			
		||||
 | 
			
		||||
	/* restore DR and EE */
 | 
			
		||||
	sync
 | 
			
		||||
	mtmsr	r11
 | 
			
		||||
	isync
 | 
			
		||||
 | 
			
		||||
	mtlr	r10
 | 
			
		||||
	blr
 | 
			
		||||
 | 
			
		||||
/* This code is for 745x processors */
 | 
			
		||||
flush_disable_745x:
 | 
			
		||||
	/* Turn off EE and DR in MSR */
 | 
			
		||||
	mfmsr	r11
 | 
			
		||||
	rlwinm	r0,r11,0,~MSR_EE
 | 
			
		||||
	rlwinm	r0,r0,0,~MSR_DR
 | 
			
		||||
	sync
 | 
			
		||||
	mtmsr	r0
 | 
			
		||||
	isync
 | 
			
		||||
 | 
			
		||||
	/* Stop prefetch streams */
 | 
			
		||||
	DSSALL
 | 
			
		||||
	sync
 | 
			
		||||
 | 
			
		||||
	/* Disable L2 prefetching */
 | 
			
		||||
	mfspr	r0,SPRN_MSSCR0
 | 
			
		||||
	rlwinm	r0,r0,0,0,29
 | 
			
		||||
	mtspr	SPRN_MSSCR0,r0
 | 
			
		||||
	sync
 | 
			
		||||
	isync
 | 
			
		||||
	lis	r4,0
 | 
			
		||||
	dcbf	0,r4
 | 
			
		||||
	dcbf	0,r4
 | 
			
		||||
	dcbf	0,r4
 | 
			
		||||
	dcbf	0,r4
 | 
			
		||||
	dcbf	0,r4
 | 
			
		||||
	dcbf	0,r4
 | 
			
		||||
	dcbf	0,r4
 | 
			
		||||
	dcbf	0,r4
 | 
			
		||||
 | 
			
		||||
	/* Due to a bug with the HW flush on some CPU revs, we occasionally
 | 
			
		||||
	 * experience data corruption. I'm adding a displacement flush along
 | 
			
		||||
	 * with a dcbf loop over a few Mb to "help". The problem isn't totally
 | 
			
		||||
	 * fixed by this in theory, but at least, in practice, I couldn't reproduce
 | 
			
		||||
	 * it even with a big hammer...
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
        lis     r4,0x0002
 | 
			
		||||
        mtctr   r4
 | 
			
		||||
 	li      r4,0
 | 
			
		||||
1:
 | 
			
		||||
        lwz     r0,0(r4)
 | 
			
		||||
        addi    r4,r4,32                /* Go to start of next cache line */
 | 
			
		||||
        bdnz    1b
 | 
			
		||||
        isync
 | 
			
		||||
 | 
			
		||||
        /* Now, flush the first 4MB of memory */
 | 
			
		||||
        lis     r4,0x0002
 | 
			
		||||
        mtctr   r4
 | 
			
		||||
	li      r4,0
 | 
			
		||||
        sync
 | 
			
		||||
1:
 | 
			
		||||
        dcbf    0,r4
 | 
			
		||||
        addi    r4,r4,32                /* Go to start of next cache line */
 | 
			
		||||
        bdnz    1b
 | 
			
		||||
 | 
			
		||||
	/* Flush and disable the L1 data cache */
 | 
			
		||||
	mfspr	r6,SPRN_LDSTCR
 | 
			
		||||
	lis	r3,0xfff0	/* read from ROM for displacement flush */
 | 
			
		||||
	li	r4,0xfe		/* start with only way 0 unlocked */
 | 
			
		||||
	li	r5,128		/* 128 lines in each way */
 | 
			
		||||
1:	mtctr	r5
 | 
			
		||||
	rlwimi	r6,r4,0,24,31
 | 
			
		||||
	mtspr	SPRN_LDSTCR,r6
 | 
			
		||||
	sync
 | 
			
		||||
	isync
 | 
			
		||||
2:	lwz	r0,0(r3)	/* touch each cache line */
 | 
			
		||||
	addi	r3,r3,32
 | 
			
		||||
	bdnz	2b
 | 
			
		||||
	rlwinm	r4,r4,1,24,30	/* move on to the next way */
 | 
			
		||||
	ori	r4,r4,1
 | 
			
		||||
	cmpwi	r4,0xff		/* all done? */
 | 
			
		||||
	bne	1b
 | 
			
		||||
	/* now unlock the L1 data cache */
 | 
			
		||||
	li	r4,0
 | 
			
		||||
	rlwimi	r6,r4,0,24,31
 | 
			
		||||
	sync
 | 
			
		||||
	mtspr	SPRN_LDSTCR,r6
 | 
			
		||||
	sync
 | 
			
		||||
	isync
 | 
			
		||||
 | 
			
		||||
	/* Flush the L2 cache using the hardware assist */
 | 
			
		||||
	mfspr	r3,SPRN_L2CR
 | 
			
		||||
	cmpwi	r3,0		/* check if it is enabled first */
 | 
			
		||||
	bge	4f
 | 
			
		||||
	oris	r0,r3,(L2CR_L2IO_745x|L2CR_L2DO_745x)@h
 | 
			
		||||
	b	2f
 | 
			
		||||
	/* When disabling/locking L2, code must be in L1 */
 | 
			
		||||
	.balign 32
 | 
			
		||||
1:	mtspr	SPRN_L2CR,r0	/* lock the L2 cache */
 | 
			
		||||
3:	sync
 | 
			
		||||
	isync
 | 
			
		||||
	b	1f
 | 
			
		||||
2:	b	3f
 | 
			
		||||
3:	sync
 | 
			
		||||
	isync
 | 
			
		||||
	b	1b
 | 
			
		||||
1:	sync
 | 
			
		||||
	isync
 | 
			
		||||
	ori	r0,r3,L2CR_L2HWF_745x
 | 
			
		||||
	sync
 | 
			
		||||
	mtspr	SPRN_L2CR,r0	/* set the hardware flush bit */
 | 
			
		||||
3:	mfspr	r0,SPRN_L2CR	/* wait for it to go to 0 */
 | 
			
		||||
	andi.	r0,r0,L2CR_L2HWF_745x
 | 
			
		||||
	bne	3b
 | 
			
		||||
	sync
 | 
			
		||||
	rlwinm	r3,r3,0,~L2CR_L2E
 | 
			
		||||
	b	2f
 | 
			
		||||
	/* When disabling L2, code must be in L1 */
 | 
			
		||||
	.balign 32
 | 
			
		||||
1:	mtspr	SPRN_L2CR,r3	/* disable the L2 cache */
 | 
			
		||||
3:	sync
 | 
			
		||||
	isync
 | 
			
		||||
	b	1f
 | 
			
		||||
2:	b	3f
 | 
			
		||||
3:	sync
 | 
			
		||||
	isync
 | 
			
		||||
	b	1b
 | 
			
		||||
1:	sync
 | 
			
		||||
	isync
 | 
			
		||||
	oris	r4,r3,L2CR_L2I@h
 | 
			
		||||
	mtspr	SPRN_L2CR,r4
 | 
			
		||||
	sync
 | 
			
		||||
	isync
 | 
			
		||||
1:	mfspr	r4,SPRN_L2CR
 | 
			
		||||
	andis.	r0,r4,L2CR_L2I@h
 | 
			
		||||
	bne	1b
 | 
			
		||||
	sync
 | 
			
		||||
 | 
			
		||||
BEGIN_FTR_SECTION
 | 
			
		||||
	/* Flush the L3 cache using the hardware assist */
 | 
			
		||||
4:	mfspr	r3,SPRN_L3CR
 | 
			
		||||
	cmpwi	r3,0		/* check if it is enabled */
 | 
			
		||||
	bge	6f
 | 
			
		||||
	oris	r0,r3,L3CR_L3IO@h
 | 
			
		||||
	ori	r0,r0,L3CR_L3DO
 | 
			
		||||
	sync
 | 
			
		||||
	mtspr	SPRN_L3CR,r0	/* lock the L3 cache */
 | 
			
		||||
	sync
 | 
			
		||||
	isync
 | 
			
		||||
	ori	r0,r0,L3CR_L3HWF
 | 
			
		||||
	sync
 | 
			
		||||
	mtspr	SPRN_L3CR,r0	/* set the hardware flush bit */
 | 
			
		||||
5:	mfspr	r0,SPRN_L3CR	/* wait for it to go to zero */
 | 
			
		||||
	andi.	r0,r0,L3CR_L3HWF
 | 
			
		||||
	bne	5b
 | 
			
		||||
	rlwinm	r3,r3,0,~L3CR_L3E
 | 
			
		||||
	sync
 | 
			
		||||
	mtspr	SPRN_L3CR,r3	/* disable the L3 cache */
 | 
			
		||||
	sync
 | 
			
		||||
	ori	r4,r3,L3CR_L3I
 | 
			
		||||
	mtspr	SPRN_L3CR,r4
 | 
			
		||||
1:	mfspr	r4,SPRN_L3CR
 | 
			
		||||
	andi.	r0,r4,L3CR_L3I
 | 
			
		||||
	bne	1b
 | 
			
		||||
	sync
 | 
			
		||||
END_FTR_SECTION_IFSET(CPU_FTR_L3CR)
 | 
			
		||||
 | 
			
		||||
6:	mfspr	r0,SPRN_HID0	/* now disable the L1 data cache */
 | 
			
		||||
	rlwinm	r0,r0,0,~HID0_DCE
 | 
			
		||||
	mtspr	SPRN_HID0,r0
 | 
			
		||||
	sync
 | 
			
		||||
	isync
 | 
			
		||||
	mtmsr	r11		/* restore DR and EE */
 | 
			
		||||
	isync
 | 
			
		||||
	blr
 | 
			
		||||
#endif	/* CONFIG_6xx */
 | 
			
		||||
							
								
								
									
										728
									
								
								arch/powerpc/platforms/powermac/pmac_cpufreq.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										728
									
								
								arch/powerpc/platforms/powermac/pmac_cpufreq.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,728 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  arch/ppc/platforms/pmac_cpufreq.c
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2002 - 2005 Benjamin Herrenschmidt <benh@kernel.crashing.org>
 | 
			
		||||
 *  Copyright (C) 2004        John Steele Scott <toojays@toojays.net>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
 * published by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 * TODO: Need a big cleanup here. Basically, we need to have different
 | 
			
		||||
 * cpufreq_driver structures for the different type of HW instead of the
 | 
			
		||||
 * current mess. We also need to better deal with the detection of the
 | 
			
		||||
 * type of machine.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/config.h>
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/types.h>
 | 
			
		||||
#include <linux/errno.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/delay.h>
 | 
			
		||||
#include <linux/sched.h>
 | 
			
		||||
#include <linux/adb.h>
 | 
			
		||||
#include <linux/pmu.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
#include <linux/cpufreq.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/sysdev.h>
 | 
			
		||||
#include <linux/i2c.h>
 | 
			
		||||
#include <linux/hardirq.h>
 | 
			
		||||
#include <asm/prom.h>
 | 
			
		||||
#include <asm/machdep.h>
 | 
			
		||||
#include <asm/irq.h>
 | 
			
		||||
#include <asm/pmac_feature.h>
 | 
			
		||||
#include <asm/mmu_context.h>
 | 
			
		||||
#include <asm/sections.h>
 | 
			
		||||
#include <asm/cputable.h>
 | 
			
		||||
#include <asm/time.h>
 | 
			
		||||
#include <asm/system.h>
 | 
			
		||||
#include <asm/mpic.h>
 | 
			
		||||
#include <asm/keylargo.h>
 | 
			
		||||
 | 
			
		||||
/* WARNING !!! This will cause calibrate_delay() to be called,
 | 
			
		||||
 * but this is an __init function ! So you MUST go edit
 | 
			
		||||
 * init/main.c to make it non-init before enabling DEBUG_FREQ
 | 
			
		||||
 */
 | 
			
		||||
#undef DEBUG_FREQ
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * There is a problem with the core cpufreq code on SMP kernels,
 | 
			
		||||
 * it won't recalculate the Bogomips properly
 | 
			
		||||
 */
 | 
			
		||||
#ifdef CONFIG_SMP
 | 
			
		||||
#warning "WARNING, CPUFREQ not recommended on SMP kernels"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
extern void low_choose_7447a_dfs(int dfs);
 | 
			
		||||
extern void low_choose_750fx_pll(int pll);
 | 
			
		||||
extern void low_sleep_handler(void);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Currently, PowerMac cpufreq supports only high & low frequencies
 | 
			
		||||
 * that are set by the firmware
 | 
			
		||||
 */
 | 
			
		||||
static unsigned int low_freq;
 | 
			
		||||
static unsigned int hi_freq;
 | 
			
		||||
static unsigned int cur_freq;
 | 
			
		||||
static unsigned int sleep_freq;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Different models uses different mecanisms to switch the frequency
 | 
			
		||||
 */
 | 
			
		||||
static int (*set_speed_proc)(int low_speed);
 | 
			
		||||
static unsigned int (*get_speed_proc)(void);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Some definitions used by the various speedprocs
 | 
			
		||||
 */
 | 
			
		||||
static u32 voltage_gpio;
 | 
			
		||||
static u32 frequency_gpio;
 | 
			
		||||
static u32 slew_done_gpio;
 | 
			
		||||
static int no_schedule;
 | 
			
		||||
static int has_cpu_l2lve;
 | 
			
		||||
static int is_pmu_based;
 | 
			
		||||
 | 
			
		||||
/* There are only two frequency states for each processor. Values
 | 
			
		||||
 * are in kHz for the time being.
 | 
			
		||||
 */
 | 
			
		||||
#define CPUFREQ_HIGH                  0
 | 
			
		||||
#define CPUFREQ_LOW                   1
 | 
			
		||||
 | 
			
		||||
static struct cpufreq_frequency_table pmac_cpu_freqs[] = {
 | 
			
		||||
	{CPUFREQ_HIGH, 		0},
 | 
			
		||||
	{CPUFREQ_LOW,		0},
 | 
			
		||||
	{0,			CPUFREQ_TABLE_END},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct freq_attr* pmac_cpu_freqs_attr[] = {
 | 
			
		||||
	&cpufreq_freq_attr_scaling_available_freqs,
 | 
			
		||||
	NULL,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static inline void local_delay(unsigned long ms)
 | 
			
		||||
{
 | 
			
		||||
	if (no_schedule)
 | 
			
		||||
		mdelay(ms);
 | 
			
		||||
	else
 | 
			
		||||
		msleep(ms);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void wakeup_decrementer(void)
 | 
			
		||||
{
 | 
			
		||||
	set_dec(tb_ticks_per_jiffy);
 | 
			
		||||
	/* No currently-supported powerbook has a 601,
 | 
			
		||||
	 * so use get_tbl, not native
 | 
			
		||||
	 */
 | 
			
		||||
	last_jiffy_stamp(0) = tb_last_stamp = get_tbl();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef DEBUG_FREQ
 | 
			
		||||
static inline void debug_calc_bogomips(void)
 | 
			
		||||
{
 | 
			
		||||
	/* This will cause a recalc of bogomips and display the
 | 
			
		||||
	 * result. We backup/restore the value to avoid affecting the
 | 
			
		||||
	 * core cpufreq framework's own calculation.
 | 
			
		||||
	 */
 | 
			
		||||
	extern void calibrate_delay(void);
 | 
			
		||||
 | 
			
		||||
	unsigned long save_lpj = loops_per_jiffy;
 | 
			
		||||
	calibrate_delay();
 | 
			
		||||
	loops_per_jiffy = save_lpj;
 | 
			
		||||
}
 | 
			
		||||
#endif /* DEBUG_FREQ */
 | 
			
		||||
 | 
			
		||||
/* Switch CPU speed under 750FX CPU control
 | 
			
		||||
 */
 | 
			
		||||
static int cpu_750fx_cpu_speed(int low_speed)
 | 
			
		||||
{
 | 
			
		||||
	u32 hid2;
 | 
			
		||||
 | 
			
		||||
	if (low_speed == 0) {
 | 
			
		||||
		/* ramping up, set voltage first */
 | 
			
		||||
		pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x05);
 | 
			
		||||
		/* Make sure we sleep for at least 1ms */
 | 
			
		||||
		local_delay(10);
 | 
			
		||||
 | 
			
		||||
		/* tweak L2 for high voltage */
 | 
			
		||||
		if (has_cpu_l2lve) {
 | 
			
		||||
			hid2 = mfspr(SPRN_HID2);
 | 
			
		||||
			hid2 &= ~0x2000;
 | 
			
		||||
			mtspr(SPRN_HID2, hid2);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
#ifdef CONFIG_6xx
 | 
			
		||||
	low_choose_750fx_pll(low_speed);
 | 
			
		||||
#endif
 | 
			
		||||
	if (low_speed == 1) {
 | 
			
		||||
		/* tweak L2 for low voltage */
 | 
			
		||||
		if (has_cpu_l2lve) {
 | 
			
		||||
			hid2 = mfspr(SPRN_HID2);
 | 
			
		||||
			hid2 |= 0x2000;
 | 
			
		||||
			mtspr(SPRN_HID2, hid2);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* ramping down, set voltage last */
 | 
			
		||||
		pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x04);
 | 
			
		||||
		local_delay(10);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static unsigned int cpu_750fx_get_cpu_speed(void)
 | 
			
		||||
{
 | 
			
		||||
	if (mfspr(SPRN_HID1) & HID1_PS)
 | 
			
		||||
		return low_freq;
 | 
			
		||||
	else
 | 
			
		||||
		return hi_freq;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Switch CPU speed using DFS */
 | 
			
		||||
static int dfs_set_cpu_speed(int low_speed)
 | 
			
		||||
{
 | 
			
		||||
	if (low_speed == 0) {
 | 
			
		||||
		/* ramping up, set voltage first */
 | 
			
		||||
		pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x05);
 | 
			
		||||
		/* Make sure we sleep for at least 1ms */
 | 
			
		||||
		local_delay(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* set frequency */
 | 
			
		||||
#ifdef CONFIG_6xx
 | 
			
		||||
	low_choose_7447a_dfs(low_speed);
 | 
			
		||||
#endif
 | 
			
		||||
	udelay(100);
 | 
			
		||||
 | 
			
		||||
	if (low_speed == 1) {
 | 
			
		||||
		/* ramping down, set voltage last */
 | 
			
		||||
		pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x04);
 | 
			
		||||
		local_delay(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static unsigned int dfs_get_cpu_speed(void)
 | 
			
		||||
{
 | 
			
		||||
	if (mfspr(SPRN_HID1) & HID1_DFS)
 | 
			
		||||
		return low_freq;
 | 
			
		||||
	else
 | 
			
		||||
		return hi_freq;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Switch CPU speed using slewing GPIOs
 | 
			
		||||
 */
 | 
			
		||||
static int gpios_set_cpu_speed(int low_speed)
 | 
			
		||||
{
 | 
			
		||||
	int gpio, timeout = 0;
 | 
			
		||||
 | 
			
		||||
	/* If ramping up, set voltage first */
 | 
			
		||||
	if (low_speed == 0) {
 | 
			
		||||
		pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x05);
 | 
			
		||||
		/* Delay is way too big but it's ok, we schedule */
 | 
			
		||||
		local_delay(10);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Set frequency */
 | 
			
		||||
	gpio = 	pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, frequency_gpio, 0);
 | 
			
		||||
	if (low_speed == ((gpio & 0x01) == 0))
 | 
			
		||||
		goto skip;
 | 
			
		||||
 | 
			
		||||
	pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, frequency_gpio,
 | 
			
		||||
			  low_speed ? 0x04 : 0x05);
 | 
			
		||||
	udelay(200);
 | 
			
		||||
	do {
 | 
			
		||||
		if (++timeout > 100)
 | 
			
		||||
			break;
 | 
			
		||||
		local_delay(1);
 | 
			
		||||
		gpio = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, slew_done_gpio, 0);
 | 
			
		||||
	} while((gpio & 0x02) == 0);
 | 
			
		||||
 skip:
 | 
			
		||||
	/* If ramping down, set voltage last */
 | 
			
		||||
	if (low_speed == 1) {
 | 
			
		||||
		pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x04);
 | 
			
		||||
		/* Delay is way too big but it's ok, we schedule */
 | 
			
		||||
		local_delay(10);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#ifdef DEBUG_FREQ
 | 
			
		||||
	debug_calc_bogomips();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Switch CPU speed under PMU control
 | 
			
		||||
 */
 | 
			
		||||
static int pmu_set_cpu_speed(int low_speed)
 | 
			
		||||
{
 | 
			
		||||
	struct adb_request req;
 | 
			
		||||
	unsigned long save_l2cr;
 | 
			
		||||
	unsigned long save_l3cr;
 | 
			
		||||
	unsigned int pic_prio;
 | 
			
		||||
	unsigned long flags;
 | 
			
		||||
 | 
			
		||||
	preempt_disable();
 | 
			
		||||
 | 
			
		||||
#ifdef DEBUG_FREQ
 | 
			
		||||
	printk(KERN_DEBUG "HID1, before: %x\n", mfspr(SPRN_HID1));
 | 
			
		||||
#endif
 | 
			
		||||
	pmu_suspend();
 | 
			
		||||
 | 
			
		||||
	/* Disable all interrupt sources on openpic */
 | 
			
		||||
 	pic_prio = mpic_cpu_get_priority();
 | 
			
		||||
	mpic_cpu_set_priority(0xf);
 | 
			
		||||
 | 
			
		||||
	/* Make sure the decrementer won't interrupt us */
 | 
			
		||||
	asm volatile("mtdec %0" : : "r" (0x7fffffff));
 | 
			
		||||
	/* Make sure any pending DEC interrupt occuring while we did
 | 
			
		||||
	 * the above didn't re-enable the DEC */
 | 
			
		||||
	mb();
 | 
			
		||||
	asm volatile("mtdec %0" : : "r" (0x7fffffff));
 | 
			
		||||
 | 
			
		||||
	/* We can now disable MSR_EE */
 | 
			
		||||
	local_irq_save(flags);
 | 
			
		||||
 | 
			
		||||
	/* Giveup the FPU & vec */
 | 
			
		||||
	enable_kernel_fp();
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_ALTIVEC
 | 
			
		||||
	if (cpu_has_feature(CPU_FTR_ALTIVEC))
 | 
			
		||||
		enable_kernel_altivec();
 | 
			
		||||
#endif /* CONFIG_ALTIVEC */
 | 
			
		||||
 | 
			
		||||
	/* Save & disable L2 and L3 caches */
 | 
			
		||||
	save_l3cr = _get_L3CR();	/* (returns -1 if not available) */
 | 
			
		||||
	save_l2cr = _get_L2CR();	/* (returns -1 if not available) */
 | 
			
		||||
 | 
			
		||||
	/* Send the new speed command. My assumption is that this command
 | 
			
		||||
	 * will cause PLL_CFG[0..3] to be changed next time CPU goes to sleep
 | 
			
		||||
	 */
 | 
			
		||||
	pmu_request(&req, NULL, 6, PMU_CPU_SPEED, 'W', 'O', 'O', 'F', low_speed);
 | 
			
		||||
	while (!req.complete)
 | 
			
		||||
		pmu_poll();
 | 
			
		||||
 | 
			
		||||
	/* Prepare the northbridge for the speed transition */
 | 
			
		||||
	pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,1,1);
 | 
			
		||||
 | 
			
		||||
	/* Call low level code to backup CPU state and recover from
 | 
			
		||||
	 * hardware reset
 | 
			
		||||
	 */
 | 
			
		||||
	low_sleep_handler();
 | 
			
		||||
 | 
			
		||||
	/* Restore the northbridge */
 | 
			
		||||
	pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,1,0);
 | 
			
		||||
 | 
			
		||||
	/* Restore L2 cache */
 | 
			
		||||
	if (save_l2cr != 0xffffffff && (save_l2cr & L2CR_L2E) != 0)
 | 
			
		||||
 		_set_L2CR(save_l2cr);
 | 
			
		||||
	/* Restore L3 cache */
 | 
			
		||||
	if (save_l3cr != 0xffffffff && (save_l3cr & L3CR_L3E) != 0)
 | 
			
		||||
 		_set_L3CR(save_l3cr);
 | 
			
		||||
 | 
			
		||||
	/* Restore userland MMU context */
 | 
			
		||||
	set_context(current->active_mm->context, current->active_mm->pgd);
 | 
			
		||||
 | 
			
		||||
#ifdef DEBUG_FREQ
 | 
			
		||||
	printk(KERN_DEBUG "HID1, after: %x\n", mfspr(SPRN_HID1));
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	/* Restore low level PMU operations */
 | 
			
		||||
	pmu_unlock();
 | 
			
		||||
 | 
			
		||||
	/* Restore decrementer */
 | 
			
		||||
	wakeup_decrementer();
 | 
			
		||||
 | 
			
		||||
	/* Restore interrupts */
 | 
			
		||||
 	mpic_cpu_set_priority(pic_prio);
 | 
			
		||||
 | 
			
		||||
	/* Let interrupts flow again ... */
 | 
			
		||||
	local_irq_restore(flags);
 | 
			
		||||
 | 
			
		||||
#ifdef DEBUG_FREQ
 | 
			
		||||
	debug_calc_bogomips();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	pmu_resume();
 | 
			
		||||
 | 
			
		||||
	preempt_enable();
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int do_set_cpu_speed(int speed_mode, int notify)
 | 
			
		||||
{
 | 
			
		||||
	struct cpufreq_freqs freqs;
 | 
			
		||||
	unsigned long l3cr;
 | 
			
		||||
	static unsigned long prev_l3cr;
 | 
			
		||||
 | 
			
		||||
	freqs.old = cur_freq;
 | 
			
		||||
	freqs.new = (speed_mode == CPUFREQ_HIGH) ? hi_freq : low_freq;
 | 
			
		||||
	freqs.cpu = smp_processor_id();
 | 
			
		||||
 | 
			
		||||
	if (freqs.old == freqs.new)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	if (notify)
 | 
			
		||||
		cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
 | 
			
		||||
	if (speed_mode == CPUFREQ_LOW &&
 | 
			
		||||
	    cpu_has_feature(CPU_FTR_L3CR)) {
 | 
			
		||||
		l3cr = _get_L3CR();
 | 
			
		||||
		if (l3cr & L3CR_L3E) {
 | 
			
		||||
			prev_l3cr = l3cr;
 | 
			
		||||
			_set_L3CR(0);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	set_speed_proc(speed_mode == CPUFREQ_LOW);
 | 
			
		||||
	if (speed_mode == CPUFREQ_HIGH &&
 | 
			
		||||
	    cpu_has_feature(CPU_FTR_L3CR)) {
 | 
			
		||||
		l3cr = _get_L3CR();
 | 
			
		||||
		if ((prev_l3cr & L3CR_L3E) && l3cr != prev_l3cr)
 | 
			
		||||
			_set_L3CR(prev_l3cr);
 | 
			
		||||
	}
 | 
			
		||||
	if (notify)
 | 
			
		||||
		cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
 | 
			
		||||
	cur_freq = (speed_mode == CPUFREQ_HIGH) ? hi_freq : low_freq;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static unsigned int pmac_cpufreq_get_speed(unsigned int cpu)
 | 
			
		||||
{
 | 
			
		||||
	return cur_freq;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int pmac_cpufreq_verify(struct cpufreq_policy *policy)
 | 
			
		||||
{
 | 
			
		||||
	return cpufreq_frequency_table_verify(policy, pmac_cpu_freqs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int pmac_cpufreq_target(	struct cpufreq_policy *policy,
 | 
			
		||||
					unsigned int target_freq,
 | 
			
		||||
					unsigned int relation)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int    newstate = 0;
 | 
			
		||||
 | 
			
		||||
	if (cpufreq_frequency_table_target(policy, pmac_cpu_freqs,
 | 
			
		||||
			target_freq, relation, &newstate))
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	return do_set_cpu_speed(newstate, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsigned int pmac_get_one_cpufreq(int i)
 | 
			
		||||
{
 | 
			
		||||
	/* Supports only one CPU for now */
 | 
			
		||||
	return (i == 0) ? cur_freq : 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int pmac_cpufreq_cpu_init(struct cpufreq_policy *policy)
 | 
			
		||||
{
 | 
			
		||||
	if (policy->cpu != 0)
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
 | 
			
		||||
	policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
 | 
			
		||||
	policy->cpuinfo.transition_latency	= CPUFREQ_ETERNAL;
 | 
			
		||||
	policy->cur = cur_freq;
 | 
			
		||||
 | 
			
		||||
	cpufreq_frequency_table_get_attr(pmac_cpu_freqs, policy->cpu);
 | 
			
		||||
	return cpufreq_frequency_table_cpuinfo(policy, pmac_cpu_freqs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static u32 read_gpio(struct device_node *np)
 | 
			
		||||
{
 | 
			
		||||
	u32 *reg = (u32 *)get_property(np, "reg", NULL);
 | 
			
		||||
	u32 offset;
 | 
			
		||||
 | 
			
		||||
	if (reg == NULL)
 | 
			
		||||
		return 0;
 | 
			
		||||
	/* That works for all keylargos but shall be fixed properly
 | 
			
		||||
	 * some day... The problem is that it seems we can't rely
 | 
			
		||||
	 * on the "reg" property of the GPIO nodes, they are either
 | 
			
		||||
	 * relative to the base of KeyLargo or to the base of the
 | 
			
		||||
	 * GPIO space, and the device-tree doesn't help.
 | 
			
		||||
	 */
 | 
			
		||||
	offset = *reg;
 | 
			
		||||
	if (offset < KEYLARGO_GPIO_LEVELS0)
 | 
			
		||||
		offset += KEYLARGO_GPIO_LEVELS0;
 | 
			
		||||
	return offset;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int pmac_cpufreq_suspend(struct cpufreq_policy *policy, pm_message_t pmsg)
 | 
			
		||||
{
 | 
			
		||||
	/* Ok, this could be made a bit smarter, but let's be robust for now. We
 | 
			
		||||
	 * always force a speed change to high speed before sleep, to make sure
 | 
			
		||||
	 * we have appropriate voltage and/or bus speed for the wakeup process,
 | 
			
		||||
	 * and to make sure our loops_per_jiffies are "good enough", that is will
 | 
			
		||||
	 * not cause too short delays if we sleep in low speed and wake in high
 | 
			
		||||
	 * speed..
 | 
			
		||||
	 */
 | 
			
		||||
	no_schedule = 1;
 | 
			
		||||
	sleep_freq = cur_freq;
 | 
			
		||||
	if (cur_freq == low_freq && !is_pmu_based)
 | 
			
		||||
		do_set_cpu_speed(CPUFREQ_HIGH, 0);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int pmac_cpufreq_resume(struct cpufreq_policy *policy)
 | 
			
		||||
{
 | 
			
		||||
	/* If we resume, first check if we have a get() function */
 | 
			
		||||
	if (get_speed_proc)
 | 
			
		||||
		cur_freq = get_speed_proc();
 | 
			
		||||
	else
 | 
			
		||||
		cur_freq = 0;
 | 
			
		||||
 | 
			
		||||
	/* We don't, hrm... we don't really know our speed here, best
 | 
			
		||||
	 * is that we force a switch to whatever it was, which is
 | 
			
		||||
	 * probably high speed due to our suspend() routine
 | 
			
		||||
	 */
 | 
			
		||||
	do_set_cpu_speed(sleep_freq == low_freq ?
 | 
			
		||||
			 CPUFREQ_LOW : CPUFREQ_HIGH, 0);
 | 
			
		||||
 | 
			
		||||
	no_schedule = 0;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct cpufreq_driver pmac_cpufreq_driver = {
 | 
			
		||||
	.verify 	= pmac_cpufreq_verify,
 | 
			
		||||
	.target 	= pmac_cpufreq_target,
 | 
			
		||||
	.get		= pmac_cpufreq_get_speed,
 | 
			
		||||
	.init		= pmac_cpufreq_cpu_init,
 | 
			
		||||
	.suspend	= pmac_cpufreq_suspend,
 | 
			
		||||
	.resume		= pmac_cpufreq_resume,
 | 
			
		||||
	.flags		= CPUFREQ_PM_NO_WARN,
 | 
			
		||||
	.attr		= pmac_cpu_freqs_attr,
 | 
			
		||||
	.name		= "powermac",
 | 
			
		||||
	.owner		= THIS_MODULE,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int pmac_cpufreq_init_MacRISC3(struct device_node *cpunode)
 | 
			
		||||
{
 | 
			
		||||
	struct device_node *volt_gpio_np = of_find_node_by_name(NULL,
 | 
			
		||||
								"voltage-gpio");
 | 
			
		||||
	struct device_node *freq_gpio_np = of_find_node_by_name(NULL,
 | 
			
		||||
								"frequency-gpio");
 | 
			
		||||
	struct device_node *slew_done_gpio_np = of_find_node_by_name(NULL,
 | 
			
		||||
								     "slewing-done");
 | 
			
		||||
	u32 *value;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Check to see if it's GPIO driven or PMU only
 | 
			
		||||
	 *
 | 
			
		||||
	 * The way we extract the GPIO address is slightly hackish, but it
 | 
			
		||||
	 * works well enough for now. We need to abstract the whole GPIO
 | 
			
		||||
	 * stuff sooner or later anyway
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
	if (volt_gpio_np)
 | 
			
		||||
		voltage_gpio = read_gpio(volt_gpio_np);
 | 
			
		||||
	if (freq_gpio_np)
 | 
			
		||||
		frequency_gpio = read_gpio(freq_gpio_np);
 | 
			
		||||
	if (slew_done_gpio_np)
 | 
			
		||||
		slew_done_gpio = read_gpio(slew_done_gpio_np);
 | 
			
		||||
 | 
			
		||||
	/* If we use the frequency GPIOs, calculate the min/max speeds based
 | 
			
		||||
	 * on the bus frequencies
 | 
			
		||||
	 */
 | 
			
		||||
	if (frequency_gpio && slew_done_gpio) {
 | 
			
		||||
		int lenp, rc;
 | 
			
		||||
		u32 *freqs, *ratio;
 | 
			
		||||
 | 
			
		||||
		freqs = (u32 *)get_property(cpunode, "bus-frequencies", &lenp);
 | 
			
		||||
		lenp /= sizeof(u32);
 | 
			
		||||
		if (freqs == NULL || lenp != 2) {
 | 
			
		||||
			printk(KERN_ERR "cpufreq: bus-frequencies incorrect or missing\n");
 | 
			
		||||
			return 1;
 | 
			
		||||
		}
 | 
			
		||||
		ratio = (u32 *)get_property(cpunode, "processor-to-bus-ratio*2", NULL);
 | 
			
		||||
		if (ratio == NULL) {
 | 
			
		||||
			printk(KERN_ERR "cpufreq: processor-to-bus-ratio*2 missing\n");
 | 
			
		||||
			return 1;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* Get the min/max bus frequencies */
 | 
			
		||||
		low_freq = min(freqs[0], freqs[1]);
 | 
			
		||||
		hi_freq = max(freqs[0], freqs[1]);
 | 
			
		||||
 | 
			
		||||
		/* Grrrr.. It _seems_ that the device-tree is lying on the low bus
 | 
			
		||||
		 * frequency, it claims it to be around 84Mhz on some models while
 | 
			
		||||
		 * it appears to be approx. 101Mhz on all. Let's hack around here...
 | 
			
		||||
		 * fortunately, we don't need to be too precise
 | 
			
		||||
		 */
 | 
			
		||||
		if (low_freq < 98000000)
 | 
			
		||||
			low_freq = 101000000;
 | 
			
		||||
			
 | 
			
		||||
		/* Convert those to CPU core clocks */
 | 
			
		||||
		low_freq = (low_freq * (*ratio)) / 2000;
 | 
			
		||||
		hi_freq = (hi_freq * (*ratio)) / 2000;
 | 
			
		||||
 | 
			
		||||
		/* Now we get the frequencies, we read the GPIO to see what is out current
 | 
			
		||||
		 * speed
 | 
			
		||||
		 */
 | 
			
		||||
		rc = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, frequency_gpio, 0);
 | 
			
		||||
		cur_freq = (rc & 0x01) ? hi_freq : low_freq;
 | 
			
		||||
 | 
			
		||||
		set_speed_proc = gpios_set_cpu_speed;
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* If we use the PMU, look for the min & max frequencies in the
 | 
			
		||||
	 * device-tree
 | 
			
		||||
	 */
 | 
			
		||||
	value = (u32 *)get_property(cpunode, "min-clock-frequency", NULL);
 | 
			
		||||
	if (!value)
 | 
			
		||||
		return 1;
 | 
			
		||||
	low_freq = (*value) / 1000;
 | 
			
		||||
	/* The PowerBook G4 12" (PowerBook6,1) has an error in the device-tree
 | 
			
		||||
	 * here */
 | 
			
		||||
	if (low_freq < 100000)
 | 
			
		||||
		low_freq *= 10;
 | 
			
		||||
 | 
			
		||||
	value = (u32 *)get_property(cpunode, "max-clock-frequency", NULL);
 | 
			
		||||
	if (!value)
 | 
			
		||||
		return 1;
 | 
			
		||||
	hi_freq = (*value) / 1000;
 | 
			
		||||
	set_speed_proc = pmu_set_cpu_speed;
 | 
			
		||||
	is_pmu_based = 1;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int pmac_cpufreq_init_7447A(struct device_node *cpunode)
 | 
			
		||||
{
 | 
			
		||||
	struct device_node *volt_gpio_np;
 | 
			
		||||
 | 
			
		||||
	if (get_property(cpunode, "dynamic-power-step", NULL) == NULL)
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
	volt_gpio_np = of_find_node_by_name(NULL, "cpu-vcore-select");
 | 
			
		||||
	if (volt_gpio_np)
 | 
			
		||||
		voltage_gpio = read_gpio(volt_gpio_np);
 | 
			
		||||
	if (!voltage_gpio){
 | 
			
		||||
		printk(KERN_ERR "cpufreq: missing cpu-vcore-select gpio\n");
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* OF only reports the high frequency */
 | 
			
		||||
	hi_freq = cur_freq;
 | 
			
		||||
	low_freq = cur_freq/2;
 | 
			
		||||
 | 
			
		||||
	/* Read actual frequency from CPU */
 | 
			
		||||
	cur_freq = dfs_get_cpu_speed();
 | 
			
		||||
	set_speed_proc = dfs_set_cpu_speed;
 | 
			
		||||
	get_speed_proc = dfs_get_cpu_speed;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int pmac_cpufreq_init_750FX(struct device_node *cpunode)
 | 
			
		||||
{
 | 
			
		||||
	struct device_node *volt_gpio_np;
 | 
			
		||||
	u32 pvr, *value;
 | 
			
		||||
 | 
			
		||||
	if (get_property(cpunode, "dynamic-power-step", NULL) == NULL)
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
	hi_freq = cur_freq;
 | 
			
		||||
	value = (u32 *)get_property(cpunode, "reduced-clock-frequency", NULL);
 | 
			
		||||
	if (!value)
 | 
			
		||||
		return 1;
 | 
			
		||||
	low_freq = (*value) / 1000;
 | 
			
		||||
 | 
			
		||||
	volt_gpio_np = of_find_node_by_name(NULL, "cpu-vcore-select");
 | 
			
		||||
	if (volt_gpio_np)
 | 
			
		||||
		voltage_gpio = read_gpio(volt_gpio_np);
 | 
			
		||||
 | 
			
		||||
	pvr = mfspr(SPRN_PVR);
 | 
			
		||||
	has_cpu_l2lve = !((pvr & 0xf00) == 0x100);
 | 
			
		||||
 | 
			
		||||
	set_speed_proc = cpu_750fx_cpu_speed;
 | 
			
		||||
	get_speed_proc = cpu_750fx_get_cpu_speed;
 | 
			
		||||
	cur_freq = cpu_750fx_get_cpu_speed();
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Currently, we support the following machines:
 | 
			
		||||
 *
 | 
			
		||||
 *  - Titanium PowerBook 1Ghz (PMU based, 667Mhz & 1Ghz)
 | 
			
		||||
 *  - Titanium PowerBook 800 (PMU based, 667Mhz & 800Mhz)
 | 
			
		||||
 *  - Titanium PowerBook 400 (PMU based, 300Mhz & 400Mhz)
 | 
			
		||||
 *  - Titanium PowerBook 500 (PMU based, 300Mhz & 500Mhz)
 | 
			
		||||
 *  - iBook2 500/600 (PMU based, 400Mhz & 500/600Mhz)
 | 
			
		||||
 *  - iBook2 700 (CPU based, 400Mhz & 700Mhz, support low voltage)
 | 
			
		||||
 *  - Recent MacRISC3 laptops
 | 
			
		||||
 *  - All new machines with 7447A CPUs
 | 
			
		||||
 */
 | 
			
		||||
static int __init pmac_cpufreq_setup(void)
 | 
			
		||||
{
 | 
			
		||||
	struct device_node	*cpunode;
 | 
			
		||||
	u32			*value;
 | 
			
		||||
 | 
			
		||||
	if (strstr(cmd_line, "nocpufreq"))
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	/* Assume only one CPU */
 | 
			
		||||
	cpunode = find_type_devices("cpu");
 | 
			
		||||
	if (!cpunode)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	/* Get current cpu clock freq */
 | 
			
		||||
	value = (u32 *)get_property(cpunode, "clock-frequency", NULL);
 | 
			
		||||
	if (!value)
 | 
			
		||||
		goto out;
 | 
			
		||||
	cur_freq = (*value) / 1000;
 | 
			
		||||
 | 
			
		||||
	/*  Check for 7447A based MacRISC3 */
 | 
			
		||||
	if (machine_is_compatible("MacRISC3") &&
 | 
			
		||||
	    get_property(cpunode, "dynamic-power-step", NULL) &&
 | 
			
		||||
	    PVR_VER(mfspr(SPRN_PVR)) == 0x8003) {
 | 
			
		||||
		pmac_cpufreq_init_7447A(cpunode);
 | 
			
		||||
	/* Check for other MacRISC3 machines */
 | 
			
		||||
	} else if (machine_is_compatible("PowerBook3,4") ||
 | 
			
		||||
		   machine_is_compatible("PowerBook3,5") ||
 | 
			
		||||
		   machine_is_compatible("MacRISC3")) {
 | 
			
		||||
		pmac_cpufreq_init_MacRISC3(cpunode);
 | 
			
		||||
	/* Else check for iBook2 500/600 */
 | 
			
		||||
	} else if (machine_is_compatible("PowerBook4,1")) {
 | 
			
		||||
		hi_freq = cur_freq;
 | 
			
		||||
		low_freq = 400000;
 | 
			
		||||
		set_speed_proc = pmu_set_cpu_speed;
 | 
			
		||||
		is_pmu_based = 1;
 | 
			
		||||
	}
 | 
			
		||||
	/* Else check for TiPb 400 & 500 */
 | 
			
		||||
	else if (machine_is_compatible("PowerBook3,2")) {
 | 
			
		||||
		/* We only know about the 400 MHz and the 500Mhz model
 | 
			
		||||
		 * they both have 300 MHz as low frequency
 | 
			
		||||
		 */
 | 
			
		||||
		if (cur_freq < 350000 || cur_freq > 550000)
 | 
			
		||||
			goto out;
 | 
			
		||||
		hi_freq = cur_freq;
 | 
			
		||||
		low_freq = 300000;
 | 
			
		||||
		set_speed_proc = pmu_set_cpu_speed;
 | 
			
		||||
		is_pmu_based = 1;
 | 
			
		||||
	}
 | 
			
		||||
	/* Else check for 750FX */
 | 
			
		||||
	else if (PVR_VER(mfspr(SPRN_PVR)) == 0x7000)
 | 
			
		||||
		pmac_cpufreq_init_750FX(cpunode);
 | 
			
		||||
out:
 | 
			
		||||
	if (set_speed_proc == NULL)
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
 | 
			
		||||
	pmac_cpu_freqs[CPUFREQ_LOW].frequency = low_freq;
 | 
			
		||||
	pmac_cpu_freqs[CPUFREQ_HIGH].frequency = hi_freq;
 | 
			
		||||
 | 
			
		||||
	printk(KERN_INFO "Registering PowerMac CPU frequency driver\n");
 | 
			
		||||
	printk(KERN_INFO "Low: %d Mhz, High: %d Mhz, Boot: %d Mhz\n",
 | 
			
		||||
	       low_freq/1000, hi_freq/1000, cur_freq/1000);
 | 
			
		||||
 | 
			
		||||
	return cpufreq_register_driver(&pmac_cpufreq_driver);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module_init(pmac_cpufreq_setup);
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										3062
									
								
								arch/powerpc/platforms/powermac/pmac_feature.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3062
									
								
								arch/powerpc/platforms/powermac/pmac_feature.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										523
									
								
								arch/powerpc/platforms/powermac/pmac_low_i2c.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										523
									
								
								arch/powerpc/platforms/powermac/pmac_low_i2c.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,523 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  arch/ppc/platforms/pmac_low_i2c.c
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2003 Ben. Herrenschmidt (benh@kernel.crashing.org)
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or
 | 
			
		||||
 *  modify it under the terms of the GNU General Public License
 | 
			
		||||
 *  as published by the Free Software Foundation; either version
 | 
			
		||||
 *  2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 *  This file contains some low-level i2c access routines that
 | 
			
		||||
 *  need to be used by various bits of the PowerMac platform code
 | 
			
		||||
 *  at times where the real asynchronous & interrupt driven driver
 | 
			
		||||
 *  cannot be used. The API borrows some semantics from the darwin
 | 
			
		||||
 *  driver in order to ease the implementation of the platform
 | 
			
		||||
 *  properties parser
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#undef DEBUG
 | 
			
		||||
 | 
			
		||||
#include <linux/config.h>
 | 
			
		||||
#include <linux/types.h>
 | 
			
		||||
#include <linux/sched.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/adb.h>
 | 
			
		||||
#include <linux/pmu.h>
 | 
			
		||||
#include <asm/keylargo.h>
 | 
			
		||||
#include <asm/uninorth.h>
 | 
			
		||||
#include <asm/io.h>
 | 
			
		||||
#include <asm/prom.h>
 | 
			
		||||
#include <asm/machdep.h>
 | 
			
		||||
#include <asm/pmac_low_i2c.h>
 | 
			
		||||
 | 
			
		||||
#define MAX_LOW_I2C_HOST	4
 | 
			
		||||
 | 
			
		||||
#ifdef DEBUG
 | 
			
		||||
#define DBG(x...) do {\
 | 
			
		||||
		printk(KERN_DEBUG "KW:" x);	\
 | 
			
		||||
	} while(0)
 | 
			
		||||
#else
 | 
			
		||||
#define DBG(x...)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
struct low_i2c_host;
 | 
			
		||||
 | 
			
		||||
typedef int (*low_i2c_func_t)(struct low_i2c_host *host, u8 addr, u8 sub, u8 *data, int len);
 | 
			
		||||
 | 
			
		||||
struct low_i2c_host
 | 
			
		||||
{
 | 
			
		||||
	struct device_node	*np;		/* OF device node */
 | 
			
		||||
	struct semaphore	mutex;		/* Access mutex for use by i2c-keywest */
 | 
			
		||||
	low_i2c_func_t		func;		/* Access function */
 | 
			
		||||
	unsigned int		is_open : 1;	/* Poor man's access control */
 | 
			
		||||
	int			mode;		/* Current mode */
 | 
			
		||||
	int			channel;	/* Current channel */
 | 
			
		||||
	int			num_channels;	/* Number of channels */
 | 
			
		||||
	void __iomem		*base;		/* For keywest-i2c, base address */
 | 
			
		||||
	int			bsteps;		/* And register stepping */
 | 
			
		||||
	int			speed;		/* And speed */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct low_i2c_host	low_i2c_hosts[MAX_LOW_I2C_HOST];
 | 
			
		||||
 | 
			
		||||
/* No locking is necessary on allocation, we are running way before
 | 
			
		||||
 * anything can race with us
 | 
			
		||||
 */
 | 
			
		||||
static struct low_i2c_host *find_low_i2c_host(struct device_node *np)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < MAX_LOW_I2C_HOST; i++)
 | 
			
		||||
		if (low_i2c_hosts[i].np == np)
 | 
			
		||||
			return &low_i2c_hosts[i];
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 *
 | 
			
		||||
 * i2c-keywest implementation (UniNorth, U2, U3, Keylargo's)
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Keywest i2c definitions borrowed from drivers/i2c/i2c-keywest.h,
 | 
			
		||||
 * should be moved somewhere in include/asm-ppc/
 | 
			
		||||
 */
 | 
			
		||||
/* Register indices */
 | 
			
		||||
typedef enum {
 | 
			
		||||
	reg_mode = 0,
 | 
			
		||||
	reg_control,
 | 
			
		||||
	reg_status,
 | 
			
		||||
	reg_isr,
 | 
			
		||||
	reg_ier,
 | 
			
		||||
	reg_addr,
 | 
			
		||||
	reg_subaddr,
 | 
			
		||||
	reg_data
 | 
			
		||||
} reg_t;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Mode register */
 | 
			
		||||
#define KW_I2C_MODE_100KHZ	0x00
 | 
			
		||||
#define KW_I2C_MODE_50KHZ	0x01
 | 
			
		||||
#define KW_I2C_MODE_25KHZ	0x02
 | 
			
		||||
#define KW_I2C_MODE_DUMB	0x00
 | 
			
		||||
#define KW_I2C_MODE_STANDARD	0x04
 | 
			
		||||
#define KW_I2C_MODE_STANDARDSUB	0x08
 | 
			
		||||
#define KW_I2C_MODE_COMBINED	0x0C
 | 
			
		||||
#define KW_I2C_MODE_MODE_MASK	0x0C
 | 
			
		||||
#define KW_I2C_MODE_CHAN_MASK	0xF0
 | 
			
		||||
 | 
			
		||||
/* Control register */
 | 
			
		||||
#define KW_I2C_CTL_AAK		0x01
 | 
			
		||||
#define KW_I2C_CTL_XADDR	0x02
 | 
			
		||||
#define KW_I2C_CTL_STOP		0x04
 | 
			
		||||
#define KW_I2C_CTL_START	0x08
 | 
			
		||||
 | 
			
		||||
/* Status register */
 | 
			
		||||
#define KW_I2C_STAT_BUSY	0x01
 | 
			
		||||
#define KW_I2C_STAT_LAST_AAK	0x02
 | 
			
		||||
#define KW_I2C_STAT_LAST_RW	0x04
 | 
			
		||||
#define KW_I2C_STAT_SDA		0x08
 | 
			
		||||
#define KW_I2C_STAT_SCL		0x10
 | 
			
		||||
 | 
			
		||||
/* IER & ISR registers */
 | 
			
		||||
#define KW_I2C_IRQ_DATA		0x01
 | 
			
		||||
#define KW_I2C_IRQ_ADDR		0x02
 | 
			
		||||
#define KW_I2C_IRQ_STOP		0x04
 | 
			
		||||
#define KW_I2C_IRQ_START	0x08
 | 
			
		||||
#define KW_I2C_IRQ_MASK		0x0F
 | 
			
		||||
 | 
			
		||||
/* State machine states */
 | 
			
		||||
enum {
 | 
			
		||||
	state_idle,
 | 
			
		||||
	state_addr,
 | 
			
		||||
	state_read,
 | 
			
		||||
	state_write,
 | 
			
		||||
	state_stop,
 | 
			
		||||
	state_dead
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define WRONG_STATE(name) do {\
 | 
			
		||||
		printk(KERN_DEBUG "KW: wrong state. Got %s, state: %s (isr: %02x)\n", \
 | 
			
		||||
		       name, __kw_state_names[state], isr); \
 | 
			
		||||
	} while(0)
 | 
			
		||||
 | 
			
		||||
static const char *__kw_state_names[] = {
 | 
			
		||||
	"state_idle",
 | 
			
		||||
	"state_addr",
 | 
			
		||||
	"state_read",
 | 
			
		||||
	"state_write",
 | 
			
		||||
	"state_stop",
 | 
			
		||||
	"state_dead"
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static inline u8 __kw_read_reg(struct low_i2c_host *host, reg_t reg)
 | 
			
		||||
{
 | 
			
		||||
	return readb(host->base + (((unsigned int)reg) << host->bsteps));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void __kw_write_reg(struct low_i2c_host *host, reg_t reg, u8 val)
 | 
			
		||||
{
 | 
			
		||||
	writeb(val, host->base + (((unsigned)reg) << host->bsteps));
 | 
			
		||||
	(void)__kw_read_reg(host, reg_subaddr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define kw_write_reg(reg, val)	__kw_write_reg(host, reg, val) 
 | 
			
		||||
#define kw_read_reg(reg)	__kw_read_reg(host, reg) 
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Don't schedule, the g5 fan controller is too
 | 
			
		||||
 * timing sensitive
 | 
			
		||||
 */
 | 
			
		||||
static u8 kw_wait_interrupt(struct low_i2c_host* host)
 | 
			
		||||
{
 | 
			
		||||
	int i, j;
 | 
			
		||||
	u8 isr;
 | 
			
		||||
	
 | 
			
		||||
	for (i = 0; i < 100000; i++) {
 | 
			
		||||
		isr = kw_read_reg(reg_isr) & KW_I2C_IRQ_MASK;
 | 
			
		||||
		if (isr != 0)
 | 
			
		||||
			return isr;
 | 
			
		||||
 | 
			
		||||
		/* This code is used with the timebase frozen, we cannot rely
 | 
			
		||||
		 * on udelay ! For now, just use a bogus loop
 | 
			
		||||
		 */
 | 
			
		||||
		for (j = 1; j < 10000; j++)
 | 
			
		||||
			mb();
 | 
			
		||||
	}
 | 
			
		||||
	return isr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int kw_handle_interrupt(struct low_i2c_host *host, int state, int rw, int *rc, u8 **data, int *len, u8 isr)
 | 
			
		||||
{
 | 
			
		||||
	u8 ack;
 | 
			
		||||
 | 
			
		||||
	DBG("kw_handle_interrupt(%s, isr: %x)\n", __kw_state_names[state], isr);
 | 
			
		||||
 | 
			
		||||
	if (isr == 0) {
 | 
			
		||||
		if (state != state_stop) {
 | 
			
		||||
			DBG("KW: Timeout !\n");
 | 
			
		||||
			*rc = -EIO;
 | 
			
		||||
			goto stop;
 | 
			
		||||
		}
 | 
			
		||||
		if (state == state_stop) {
 | 
			
		||||
			ack = kw_read_reg(reg_status);
 | 
			
		||||
			if (!(ack & KW_I2C_STAT_BUSY)) {
 | 
			
		||||
				state = state_idle;
 | 
			
		||||
				kw_write_reg(reg_ier, 0x00);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return state;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (isr & KW_I2C_IRQ_ADDR) {
 | 
			
		||||
		ack = kw_read_reg(reg_status);
 | 
			
		||||
		if (state != state_addr) {
 | 
			
		||||
			kw_write_reg(reg_isr, KW_I2C_IRQ_ADDR);
 | 
			
		||||
			WRONG_STATE("KW_I2C_IRQ_ADDR"); 
 | 
			
		||||
			*rc = -EIO;
 | 
			
		||||
			goto stop;
 | 
			
		||||
		}
 | 
			
		||||
		if ((ack & KW_I2C_STAT_LAST_AAK) == 0) {			
 | 
			
		||||
			*rc = -ENODEV;
 | 
			
		||||
			DBG("KW: NAK on address\n");
 | 
			
		||||
			return state_stop;		     
 | 
			
		||||
		} else {
 | 
			
		||||
			if (rw) {
 | 
			
		||||
				state = state_read;
 | 
			
		||||
				if (*len > 1)
 | 
			
		||||
					kw_write_reg(reg_control, KW_I2C_CTL_AAK);
 | 
			
		||||
			} else {
 | 
			
		||||
				state = state_write;
 | 
			
		||||
				kw_write_reg(reg_data, **data);
 | 
			
		||||
				(*data)++; (*len)--;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		kw_write_reg(reg_isr, KW_I2C_IRQ_ADDR);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (isr & KW_I2C_IRQ_DATA) {
 | 
			
		||||
		if (state == state_read) {
 | 
			
		||||
			**data = kw_read_reg(reg_data);
 | 
			
		||||
			(*data)++; (*len)--;
 | 
			
		||||
			kw_write_reg(reg_isr, KW_I2C_IRQ_DATA);
 | 
			
		||||
			if ((*len) == 0)
 | 
			
		||||
				state = state_stop;
 | 
			
		||||
			else if ((*len) == 1)
 | 
			
		||||
				kw_write_reg(reg_control, 0);
 | 
			
		||||
		} else if (state == state_write) {
 | 
			
		||||
			ack = kw_read_reg(reg_status);
 | 
			
		||||
			if ((ack & KW_I2C_STAT_LAST_AAK) == 0) {
 | 
			
		||||
				DBG("KW: nack on data write\n");
 | 
			
		||||
				*rc = -EIO;
 | 
			
		||||
				goto stop;
 | 
			
		||||
			} else if (*len) {
 | 
			
		||||
				kw_write_reg(reg_data, **data);
 | 
			
		||||
				(*data)++; (*len)--;
 | 
			
		||||
			} else {
 | 
			
		||||
				kw_write_reg(reg_control, KW_I2C_CTL_STOP);
 | 
			
		||||
				state = state_stop;
 | 
			
		||||
				*rc = 0;
 | 
			
		||||
			}
 | 
			
		||||
			kw_write_reg(reg_isr, KW_I2C_IRQ_DATA);
 | 
			
		||||
		} else {
 | 
			
		||||
			kw_write_reg(reg_isr, KW_I2C_IRQ_DATA);
 | 
			
		||||
			WRONG_STATE("KW_I2C_IRQ_DATA"); 
 | 
			
		||||
			if (state != state_stop) {
 | 
			
		||||
				*rc = -EIO;
 | 
			
		||||
				goto stop;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (isr & KW_I2C_IRQ_STOP) {
 | 
			
		||||
		kw_write_reg(reg_isr, KW_I2C_IRQ_STOP);
 | 
			
		||||
		if (state != state_stop) {
 | 
			
		||||
			WRONG_STATE("KW_I2C_IRQ_STOP");
 | 
			
		||||
			*rc = -EIO;
 | 
			
		||||
		}
 | 
			
		||||
		return state_idle;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (isr & KW_I2C_IRQ_START)
 | 
			
		||||
		kw_write_reg(reg_isr, KW_I2C_IRQ_START);
 | 
			
		||||
 | 
			
		||||
	return state;
 | 
			
		||||
 | 
			
		||||
 stop:
 | 
			
		||||
	kw_write_reg(reg_control, KW_I2C_CTL_STOP);	
 | 
			
		||||
	return state_stop;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int keywest_low_i2c_func(struct low_i2c_host *host, u8 addr, u8 subaddr, u8 *data, int len)
 | 
			
		||||
{
 | 
			
		||||
	u8 mode_reg = host->speed;
 | 
			
		||||
	int state = state_addr;
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
 | 
			
		||||
	/* Setup mode & subaddress if any */
 | 
			
		||||
	switch(host->mode) {
 | 
			
		||||
	case pmac_low_i2c_mode_dumb:
 | 
			
		||||
		printk(KERN_ERR "low_i2c: Dumb mode not supported !\n");
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	case pmac_low_i2c_mode_std:
 | 
			
		||||
		mode_reg |= KW_I2C_MODE_STANDARD;
 | 
			
		||||
		break;
 | 
			
		||||
	case pmac_low_i2c_mode_stdsub:
 | 
			
		||||
		mode_reg |= KW_I2C_MODE_STANDARDSUB;
 | 
			
		||||
		break;
 | 
			
		||||
	case pmac_low_i2c_mode_combined:
 | 
			
		||||
		mode_reg |= KW_I2C_MODE_COMBINED;
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Setup channel & clear pending irqs */
 | 
			
		||||
	kw_write_reg(reg_isr, kw_read_reg(reg_isr));
 | 
			
		||||
	kw_write_reg(reg_mode, mode_reg | (host->channel << 4));
 | 
			
		||||
	kw_write_reg(reg_status, 0);
 | 
			
		||||
 | 
			
		||||
	/* Set up address and r/w bit */
 | 
			
		||||
	kw_write_reg(reg_addr, addr);
 | 
			
		||||
 | 
			
		||||
	/* Set up the sub address */
 | 
			
		||||
	if ((mode_reg & KW_I2C_MODE_MODE_MASK) == KW_I2C_MODE_STANDARDSUB
 | 
			
		||||
	    || (mode_reg & KW_I2C_MODE_MODE_MASK) == KW_I2C_MODE_COMBINED)
 | 
			
		||||
		kw_write_reg(reg_subaddr, subaddr);
 | 
			
		||||
 | 
			
		||||
	/* Start sending address & disable interrupt*/
 | 
			
		||||
	kw_write_reg(reg_ier, 0 /*KW_I2C_IRQ_MASK*/);
 | 
			
		||||
	kw_write_reg(reg_control, KW_I2C_CTL_XADDR);
 | 
			
		||||
 | 
			
		||||
	/* State machine, to turn into an interrupt handler */
 | 
			
		||||
	while(state != state_idle) {
 | 
			
		||||
		u8 isr = kw_wait_interrupt(host);
 | 
			
		||||
		state = kw_handle_interrupt(host, state, addr & 1, &rc, &data, &len, isr);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void keywest_low_i2c_add(struct device_node *np)
 | 
			
		||||
{
 | 
			
		||||
	struct low_i2c_host	*host = find_low_i2c_host(NULL);
 | 
			
		||||
	u32			*psteps, *prate, steps, aoffset = 0;
 | 
			
		||||
	struct device_node	*parent;
 | 
			
		||||
 | 
			
		||||
	if (host == NULL) {
 | 
			
		||||
		printk(KERN_ERR "low_i2c: Can't allocate host for %s\n",
 | 
			
		||||
		       np->full_name);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	memset(host, 0, sizeof(*host));
 | 
			
		||||
 | 
			
		||||
	init_MUTEX(&host->mutex);
 | 
			
		||||
	host->np = of_node_get(np);	
 | 
			
		||||
	psteps = (u32 *)get_property(np, "AAPL,address-step", NULL);
 | 
			
		||||
	steps = psteps ? (*psteps) : 0x10;
 | 
			
		||||
	for (host->bsteps = 0; (steps & 0x01) == 0; host->bsteps++)
 | 
			
		||||
		steps >>= 1;
 | 
			
		||||
	parent = of_get_parent(np);
 | 
			
		||||
	host->num_channels = 1;
 | 
			
		||||
	if (parent && parent->name[0] == 'u') {
 | 
			
		||||
		host->num_channels = 2;
 | 
			
		||||
		aoffset = 3;
 | 
			
		||||
	}
 | 
			
		||||
	/* Select interface rate */
 | 
			
		||||
	host->speed = KW_I2C_MODE_100KHZ;
 | 
			
		||||
	prate = (u32 *)get_property(np, "AAPL,i2c-rate", NULL);
 | 
			
		||||
	if (prate) switch(*prate) {
 | 
			
		||||
	case 100:
 | 
			
		||||
		host->speed = KW_I2C_MODE_100KHZ;
 | 
			
		||||
		break;
 | 
			
		||||
	case 50:
 | 
			
		||||
		host->speed = KW_I2C_MODE_50KHZ;
 | 
			
		||||
		break;
 | 
			
		||||
	case 25:
 | 
			
		||||
		host->speed = KW_I2C_MODE_25KHZ;
 | 
			
		||||
		break;
 | 
			
		||||
	}	
 | 
			
		||||
 | 
			
		||||
	host->mode = pmac_low_i2c_mode_std;
 | 
			
		||||
	host->base = ioremap(np->addrs[0].address + aoffset,
 | 
			
		||||
						np->addrs[0].size);
 | 
			
		||||
	host->func = keywest_low_i2c_func;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 *
 | 
			
		||||
 * PMU implementation
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_ADB_PMU
 | 
			
		||||
 | 
			
		||||
static int pmu_low_i2c_func(struct low_i2c_host *host, u8 addr, u8 sub, u8 *data, int len)
 | 
			
		||||
{
 | 
			
		||||
	// TODO
 | 
			
		||||
	return -ENODEV;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void pmu_low_i2c_add(struct device_node *np)
 | 
			
		||||
{
 | 
			
		||||
	struct low_i2c_host	*host = find_low_i2c_host(NULL);
 | 
			
		||||
 | 
			
		||||
	if (host == NULL) {
 | 
			
		||||
		printk(KERN_ERR "low_i2c: Can't allocate host for %s\n",
 | 
			
		||||
		       np->full_name);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	memset(host, 0, sizeof(*host));
 | 
			
		||||
 | 
			
		||||
	init_MUTEX(&host->mutex);
 | 
			
		||||
	host->np = of_node_get(np);	
 | 
			
		||||
	host->num_channels = 3;
 | 
			
		||||
	host->mode = pmac_low_i2c_mode_std;
 | 
			
		||||
	host->func = pmu_low_i2c_func;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif /* CONFIG_ADB_PMU */
 | 
			
		||||
 | 
			
		||||
void __init pmac_init_low_i2c(void)
 | 
			
		||||
{
 | 
			
		||||
	struct device_node *np;
 | 
			
		||||
 | 
			
		||||
	/* Probe keywest-i2c busses */
 | 
			
		||||
	np = of_find_compatible_node(NULL, "i2c", "keywest-i2c");
 | 
			
		||||
	while(np) {
 | 
			
		||||
		keywest_low_i2c_add(np);
 | 
			
		||||
		np = of_find_compatible_node(np, "i2c", "keywest-i2c");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_ADB_PMU
 | 
			
		||||
	/* Probe PMU busses */
 | 
			
		||||
	np = of_find_node_by_name(NULL, "via-pmu");
 | 
			
		||||
	if (np)
 | 
			
		||||
		pmu_low_i2c_add(np);
 | 
			
		||||
#endif /* CONFIG_ADB_PMU */
 | 
			
		||||
 | 
			
		||||
	/* TODO: Add CUDA support as well */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int pmac_low_i2c_lock(struct device_node *np)
 | 
			
		||||
{
 | 
			
		||||
	struct low_i2c_host *host = find_low_i2c_host(np);
 | 
			
		||||
 | 
			
		||||
	if (!host)
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
	down(&host->mutex);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(pmac_low_i2c_lock);
 | 
			
		||||
 | 
			
		||||
int pmac_low_i2c_unlock(struct device_node *np)
 | 
			
		||||
{
 | 
			
		||||
	struct low_i2c_host *host = find_low_i2c_host(np);
 | 
			
		||||
 | 
			
		||||
	if (!host)
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
	up(&host->mutex);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(pmac_low_i2c_unlock);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int pmac_low_i2c_open(struct device_node *np, int channel)
 | 
			
		||||
{
 | 
			
		||||
	struct low_i2c_host *host = find_low_i2c_host(np);
 | 
			
		||||
 | 
			
		||||
	if (!host)
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
 | 
			
		||||
	if (channel >= host->num_channels)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	down(&host->mutex);
 | 
			
		||||
	host->is_open = 1;
 | 
			
		||||
	host->channel = channel;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(pmac_low_i2c_open);
 | 
			
		||||
 | 
			
		||||
int pmac_low_i2c_close(struct device_node *np)
 | 
			
		||||
{
 | 
			
		||||
	struct low_i2c_host *host = find_low_i2c_host(np);
 | 
			
		||||
 | 
			
		||||
	if (!host)
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
 | 
			
		||||
	host->is_open = 0;
 | 
			
		||||
	up(&host->mutex);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(pmac_low_i2c_close);
 | 
			
		||||
 | 
			
		||||
int pmac_low_i2c_setmode(struct device_node *np, int mode)
 | 
			
		||||
{
 | 
			
		||||
	struct low_i2c_host *host = find_low_i2c_host(np);
 | 
			
		||||
 | 
			
		||||
	if (!host)
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
	WARN_ON(!host->is_open);
 | 
			
		||||
	host->mode = mode;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(pmac_low_i2c_setmode);
 | 
			
		||||
 | 
			
		||||
int pmac_low_i2c_xfer(struct device_node *np, u8 addrdir, u8 subaddr, u8 *data, int len)
 | 
			
		||||
{
 | 
			
		||||
	struct low_i2c_host *host = find_low_i2c_host(np);
 | 
			
		||||
 | 
			
		||||
	if (!host)
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
	WARN_ON(!host->is_open);
 | 
			
		||||
 | 
			
		||||
	return host->func(host, addrdir, subaddr, data, len);
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(pmac_low_i2c_xfer);
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										584
									
								
								arch/powerpc/platforms/powermac/pmac_nvram.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										584
									
								
								arch/powerpc/platforms/powermac/pmac_nvram.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,584 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  arch/ppc/platforms/pmac_nvram.c
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2002 Benjamin Herrenschmidt (benh@kernel.crashing.org)
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or
 | 
			
		||||
 *  modify it under the terms of the GNU General Public License
 | 
			
		||||
 *  as published by the Free Software Foundation; either version
 | 
			
		||||
 *  2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 *  Todo: - add support for the OF persistent properties
 | 
			
		||||
 */
 | 
			
		||||
#include <linux/config.h>
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/stddef.h>
 | 
			
		||||
#include <linux/string.h>
 | 
			
		||||
#include <linux/nvram.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
#include <linux/delay.h>
 | 
			
		||||
#include <linux/errno.h>
 | 
			
		||||
#include <linux/adb.h>
 | 
			
		||||
#include <linux/pmu.h>
 | 
			
		||||
#include <linux/bootmem.h>
 | 
			
		||||
#include <linux/completion.h>
 | 
			
		||||
#include <linux/spinlock.h>
 | 
			
		||||
#include <asm/sections.h>
 | 
			
		||||
#include <asm/io.h>
 | 
			
		||||
#include <asm/system.h>
 | 
			
		||||
#include <asm/prom.h>
 | 
			
		||||
#include <asm/machdep.h>
 | 
			
		||||
#include <asm/nvram.h>
 | 
			
		||||
 | 
			
		||||
#define DEBUG
 | 
			
		||||
 | 
			
		||||
#ifdef DEBUG
 | 
			
		||||
#define DBG(x...) printk(x)
 | 
			
		||||
#else
 | 
			
		||||
#define DBG(x...)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define NVRAM_SIZE		0x2000	/* 8kB of non-volatile RAM */
 | 
			
		||||
 | 
			
		||||
#define CORE99_SIGNATURE	0x5a
 | 
			
		||||
#define CORE99_ADLER_START	0x14
 | 
			
		||||
 | 
			
		||||
/* On Core99, nvram is either a sharp, a micron or an AMD flash */
 | 
			
		||||
#define SM_FLASH_STATUS_DONE	0x80
 | 
			
		||||
#define SM_FLASH_STATUS_ERR		0x38
 | 
			
		||||
#define SM_FLASH_CMD_ERASE_CONFIRM	0xd0
 | 
			
		||||
#define SM_FLASH_CMD_ERASE_SETUP	0x20
 | 
			
		||||
#define SM_FLASH_CMD_RESET		0xff
 | 
			
		||||
#define SM_FLASH_CMD_WRITE_SETUP	0x40
 | 
			
		||||
#define SM_FLASH_CMD_CLEAR_STATUS	0x50
 | 
			
		||||
#define SM_FLASH_CMD_READ_STATUS	0x70
 | 
			
		||||
 | 
			
		||||
/* CHRP NVRAM header */
 | 
			
		||||
struct chrp_header {
 | 
			
		||||
  u8		signature;
 | 
			
		||||
  u8		cksum;
 | 
			
		||||
  u16		len;
 | 
			
		||||
  char          name[12];
 | 
			
		||||
  u8		data[0];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct core99_header {
 | 
			
		||||
  struct chrp_header	hdr;
 | 
			
		||||
  u32			adler;
 | 
			
		||||
  u32			generation;
 | 
			
		||||
  u32			reserved[2];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Read and write the non-volatile RAM on PowerMacs and CHRP machines.
 | 
			
		||||
 */
 | 
			
		||||
static int nvram_naddrs;
 | 
			
		||||
static volatile unsigned char *nvram_addr;
 | 
			
		||||
static volatile unsigned char *nvram_data;
 | 
			
		||||
static int nvram_mult, is_core_99;
 | 
			
		||||
static int core99_bank = 0;
 | 
			
		||||
static int nvram_partitions[3];
 | 
			
		||||
static DEFINE_SPINLOCK(nv_lock);
 | 
			
		||||
 | 
			
		||||
extern int pmac_newworld;
 | 
			
		||||
extern int system_running;
 | 
			
		||||
 | 
			
		||||
static int (*core99_write_bank)(int bank, u8* datas);
 | 
			
		||||
static int (*core99_erase_bank)(int bank);
 | 
			
		||||
 | 
			
		||||
static char *nvram_image;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static unsigned char core99_nvram_read_byte(int addr)
 | 
			
		||||
{
 | 
			
		||||
	if (nvram_image == NULL)
 | 
			
		||||
		return 0xff;
 | 
			
		||||
	return nvram_image[addr];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void core99_nvram_write_byte(int addr, unsigned char val)
 | 
			
		||||
{
 | 
			
		||||
	if (nvram_image == NULL)
 | 
			
		||||
		return;
 | 
			
		||||
	nvram_image[addr] = val;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static unsigned char direct_nvram_read_byte(int addr)
 | 
			
		||||
{
 | 
			
		||||
	return in_8(&nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void direct_nvram_write_byte(int addr, unsigned char val)
 | 
			
		||||
{
 | 
			
		||||
	out_8(&nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult], val);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static unsigned char indirect_nvram_read_byte(int addr)
 | 
			
		||||
{
 | 
			
		||||
	unsigned char val;
 | 
			
		||||
	unsigned long flags;
 | 
			
		||||
 | 
			
		||||
	spin_lock_irqsave(&nv_lock, flags);
 | 
			
		||||
	out_8(nvram_addr, addr >> 5);
 | 
			
		||||
	val = in_8(&nvram_data[(addr & 0x1f) << 4]);
 | 
			
		||||
	spin_unlock_irqrestore(&nv_lock, flags);
 | 
			
		||||
 | 
			
		||||
	return val;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void indirect_nvram_write_byte(int addr, unsigned char val)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long flags;
 | 
			
		||||
 | 
			
		||||
	spin_lock_irqsave(&nv_lock, flags);
 | 
			
		||||
	out_8(nvram_addr, addr >> 5);
 | 
			
		||||
	out_8(&nvram_data[(addr & 0x1f) << 4], val);
 | 
			
		||||
	spin_unlock_irqrestore(&nv_lock, flags);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_ADB_PMU
 | 
			
		||||
 | 
			
		||||
static void pmu_nvram_complete(struct adb_request *req)
 | 
			
		||||
{
 | 
			
		||||
	if (req->arg)
 | 
			
		||||
		complete((struct completion *)req->arg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static unsigned char pmu_nvram_read_byte(int addr)
 | 
			
		||||
{
 | 
			
		||||
	struct adb_request req;
 | 
			
		||||
	DECLARE_COMPLETION(req_complete); 
 | 
			
		||||
	
 | 
			
		||||
	req.arg = system_state == SYSTEM_RUNNING ? &req_complete : NULL;
 | 
			
		||||
	if (pmu_request(&req, pmu_nvram_complete, 3, PMU_READ_NVRAM,
 | 
			
		||||
			(addr >> 8) & 0xff, addr & 0xff))
 | 
			
		||||
		return 0xff;
 | 
			
		||||
	if (system_state == SYSTEM_RUNNING)
 | 
			
		||||
		wait_for_completion(&req_complete);
 | 
			
		||||
	while (!req.complete)
 | 
			
		||||
		pmu_poll();
 | 
			
		||||
	return req.reply[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void pmu_nvram_write_byte(int addr, unsigned char val)
 | 
			
		||||
{
 | 
			
		||||
	struct adb_request req;
 | 
			
		||||
	DECLARE_COMPLETION(req_complete); 
 | 
			
		||||
	
 | 
			
		||||
	req.arg = system_state == SYSTEM_RUNNING ? &req_complete : NULL;
 | 
			
		||||
	if (pmu_request(&req, pmu_nvram_complete, 4, PMU_WRITE_NVRAM,
 | 
			
		||||
			(addr >> 8) & 0xff, addr & 0xff, val))
 | 
			
		||||
		return;
 | 
			
		||||
	if (system_state == SYSTEM_RUNNING)
 | 
			
		||||
		wait_for_completion(&req_complete);
 | 
			
		||||
	while (!req.complete)
 | 
			
		||||
		pmu_poll();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif /* CONFIG_ADB_PMU */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static u8 chrp_checksum(struct chrp_header* hdr)
 | 
			
		||||
{
 | 
			
		||||
	u8 *ptr;
 | 
			
		||||
	u16 sum = hdr->signature;
 | 
			
		||||
	for (ptr = (u8 *)&hdr->len; ptr < hdr->data; ptr++)
 | 
			
		||||
		sum += *ptr;
 | 
			
		||||
	while (sum > 0xFF)
 | 
			
		||||
		sum = (sum & 0xFF) + (sum>>8);
 | 
			
		||||
	return sum;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static u32 core99_calc_adler(u8 *buffer)
 | 
			
		||||
{
 | 
			
		||||
	int cnt;
 | 
			
		||||
	u32 low, high;
 | 
			
		||||
 | 
			
		||||
   	buffer += CORE99_ADLER_START;
 | 
			
		||||
	low = 1;
 | 
			
		||||
	high = 0;
 | 
			
		||||
	for (cnt=0; cnt<(NVRAM_SIZE-CORE99_ADLER_START); cnt++) {
 | 
			
		||||
		if ((cnt % 5000) == 0) {
 | 
			
		||||
			high  %= 65521UL;
 | 
			
		||||
			high %= 65521UL;
 | 
			
		||||
		}
 | 
			
		||||
		low += buffer[cnt];
 | 
			
		||||
		high += low;
 | 
			
		||||
	}
 | 
			
		||||
	low  %= 65521UL;
 | 
			
		||||
	high %= 65521UL;
 | 
			
		||||
 | 
			
		||||
	return (high << 16) | low;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static u32 core99_check(u8* datas)
 | 
			
		||||
{
 | 
			
		||||
	struct core99_header* hdr99 = (struct core99_header*)datas;
 | 
			
		||||
 | 
			
		||||
	if (hdr99->hdr.signature != CORE99_SIGNATURE) {
 | 
			
		||||
		DBG("Invalid signature\n");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	if (hdr99->hdr.cksum != chrp_checksum(&hdr99->hdr)) {
 | 
			
		||||
		DBG("Invalid checksum\n");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	if (hdr99->adler != core99_calc_adler(datas)) {
 | 
			
		||||
		DBG("Invalid adler\n");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	return hdr99->generation;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int sm_erase_bank(int bank)
 | 
			
		||||
{
 | 
			
		||||
	int stat, i;
 | 
			
		||||
	unsigned long timeout;
 | 
			
		||||
 | 
			
		||||
	u8* base = (u8 *)nvram_data + core99_bank*NVRAM_SIZE;
 | 
			
		||||
 | 
			
		||||
       	DBG("nvram: Sharp/Micron Erasing bank %d...\n", bank);
 | 
			
		||||
 | 
			
		||||
	out_8(base, SM_FLASH_CMD_ERASE_SETUP);
 | 
			
		||||
	out_8(base, SM_FLASH_CMD_ERASE_CONFIRM);
 | 
			
		||||
	timeout = 0;
 | 
			
		||||
	do {
 | 
			
		||||
		if (++timeout > 1000000) {
 | 
			
		||||
			printk(KERN_ERR "nvram: Sharp/Miron flash erase timeout !\n");
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		out_8(base, SM_FLASH_CMD_READ_STATUS);
 | 
			
		||||
		stat = in_8(base);
 | 
			
		||||
	} while (!(stat & SM_FLASH_STATUS_DONE));
 | 
			
		||||
 | 
			
		||||
	out_8(base, SM_FLASH_CMD_CLEAR_STATUS);
 | 
			
		||||
	out_8(base, SM_FLASH_CMD_RESET);
 | 
			
		||||
 | 
			
		||||
	for (i=0; i<NVRAM_SIZE; i++)
 | 
			
		||||
		if (base[i] != 0xff) {
 | 
			
		||||
			printk(KERN_ERR "nvram: Sharp/Micron flash erase failed !\n");
 | 
			
		||||
			return -ENXIO;
 | 
			
		||||
		}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int sm_write_bank(int bank, u8* datas)
 | 
			
		||||
{
 | 
			
		||||
	int i, stat = 0;
 | 
			
		||||
	unsigned long timeout;
 | 
			
		||||
 | 
			
		||||
	u8* base = (u8 *)nvram_data + core99_bank*NVRAM_SIZE;
 | 
			
		||||
 | 
			
		||||
       	DBG("nvram: Sharp/Micron Writing bank %d...\n", bank);
 | 
			
		||||
 | 
			
		||||
	for (i=0; i<NVRAM_SIZE; i++) {
 | 
			
		||||
		out_8(base+i, SM_FLASH_CMD_WRITE_SETUP);
 | 
			
		||||
		udelay(1);
 | 
			
		||||
		out_8(base+i, datas[i]);
 | 
			
		||||
		timeout = 0;
 | 
			
		||||
		do {
 | 
			
		||||
			if (++timeout > 1000000) {
 | 
			
		||||
				printk(KERN_ERR "nvram: Sharp/Micron flash write timeout !\n");
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
			out_8(base, SM_FLASH_CMD_READ_STATUS);
 | 
			
		||||
			stat = in_8(base);
 | 
			
		||||
		} while (!(stat & SM_FLASH_STATUS_DONE));
 | 
			
		||||
		if (!(stat & SM_FLASH_STATUS_DONE))
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
	out_8(base, SM_FLASH_CMD_CLEAR_STATUS);
 | 
			
		||||
	out_8(base, SM_FLASH_CMD_RESET);
 | 
			
		||||
	for (i=0; i<NVRAM_SIZE; i++)
 | 
			
		||||
		if (base[i] != datas[i]) {
 | 
			
		||||
			printk(KERN_ERR "nvram: Sharp/Micron flash write failed !\n");
 | 
			
		||||
			return -ENXIO;
 | 
			
		||||
		}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int amd_erase_bank(int bank)
 | 
			
		||||
{
 | 
			
		||||
	int i, stat = 0;
 | 
			
		||||
	unsigned long timeout;
 | 
			
		||||
 | 
			
		||||
	u8* base = (u8 *)nvram_data + core99_bank*NVRAM_SIZE;
 | 
			
		||||
 | 
			
		||||
       	DBG("nvram: AMD Erasing bank %d...\n", bank);
 | 
			
		||||
 | 
			
		||||
	/* Unlock 1 */
 | 
			
		||||
	out_8(base+0x555, 0xaa);
 | 
			
		||||
	udelay(1);
 | 
			
		||||
	/* Unlock 2 */
 | 
			
		||||
	out_8(base+0x2aa, 0x55);
 | 
			
		||||
	udelay(1);
 | 
			
		||||
 | 
			
		||||
	/* Sector-Erase */
 | 
			
		||||
	out_8(base+0x555, 0x80);
 | 
			
		||||
	udelay(1);
 | 
			
		||||
	out_8(base+0x555, 0xaa);
 | 
			
		||||
	udelay(1);
 | 
			
		||||
	out_8(base+0x2aa, 0x55);
 | 
			
		||||
	udelay(1);
 | 
			
		||||
	out_8(base, 0x30);
 | 
			
		||||
	udelay(1);
 | 
			
		||||
 | 
			
		||||
	timeout = 0;
 | 
			
		||||
	do {
 | 
			
		||||
		if (++timeout > 1000000) {
 | 
			
		||||
			printk(KERN_ERR "nvram: AMD flash erase timeout !\n");
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		stat = in_8(base) ^ in_8(base);
 | 
			
		||||
	} while (stat != 0);
 | 
			
		||||
	
 | 
			
		||||
	/* Reset */
 | 
			
		||||
	out_8(base, 0xf0);
 | 
			
		||||
	udelay(1);
 | 
			
		||||
	
 | 
			
		||||
	for (i=0; i<NVRAM_SIZE; i++)
 | 
			
		||||
		if (base[i] != 0xff) {
 | 
			
		||||
			printk(KERN_ERR "nvram: AMD flash erase failed !\n");
 | 
			
		||||
			return -ENXIO;
 | 
			
		||||
		}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int amd_write_bank(int bank, u8* datas)
 | 
			
		||||
{
 | 
			
		||||
	int i, stat = 0;
 | 
			
		||||
	unsigned long timeout;
 | 
			
		||||
 | 
			
		||||
	u8* base = (u8 *)nvram_data + core99_bank*NVRAM_SIZE;
 | 
			
		||||
 | 
			
		||||
       	DBG("nvram: AMD Writing bank %d...\n", bank);
 | 
			
		||||
 | 
			
		||||
	for (i=0; i<NVRAM_SIZE; i++) {
 | 
			
		||||
		/* Unlock 1 */
 | 
			
		||||
		out_8(base+0x555, 0xaa);
 | 
			
		||||
		udelay(1);
 | 
			
		||||
		/* Unlock 2 */
 | 
			
		||||
		out_8(base+0x2aa, 0x55);
 | 
			
		||||
		udelay(1);
 | 
			
		||||
 | 
			
		||||
		/* Write single word */
 | 
			
		||||
		out_8(base+0x555, 0xa0);
 | 
			
		||||
		udelay(1);
 | 
			
		||||
		out_8(base+i, datas[i]);
 | 
			
		||||
		
 | 
			
		||||
		timeout = 0;
 | 
			
		||||
		do {
 | 
			
		||||
			if (++timeout > 1000000) {
 | 
			
		||||
				printk(KERN_ERR "nvram: AMD flash write timeout !\n");
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
			stat = in_8(base) ^ in_8(base);
 | 
			
		||||
		} while (stat != 0);
 | 
			
		||||
		if (stat != 0)
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Reset */
 | 
			
		||||
	out_8(base, 0xf0);
 | 
			
		||||
	udelay(1);
 | 
			
		||||
 | 
			
		||||
	for (i=0; i<NVRAM_SIZE; i++)
 | 
			
		||||
		if (base[i] != datas[i]) {
 | 
			
		||||
			printk(KERN_ERR "nvram: AMD flash write failed !\n");
 | 
			
		||||
			return -ENXIO;
 | 
			
		||||
		}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void __init lookup_partitions(void)
 | 
			
		||||
{
 | 
			
		||||
	u8 buffer[17];
 | 
			
		||||
	int i, offset;
 | 
			
		||||
	struct chrp_header* hdr;
 | 
			
		||||
 | 
			
		||||
	if (pmac_newworld) {
 | 
			
		||||
		nvram_partitions[pmac_nvram_OF] = -1;
 | 
			
		||||
		nvram_partitions[pmac_nvram_XPRAM] = -1;
 | 
			
		||||
		nvram_partitions[pmac_nvram_NR] = -1;
 | 
			
		||||
		hdr = (struct chrp_header *)buffer;
 | 
			
		||||
 | 
			
		||||
		offset = 0;
 | 
			
		||||
		buffer[16] = 0;
 | 
			
		||||
		do {
 | 
			
		||||
			for (i=0;i<16;i++)
 | 
			
		||||
				buffer[i] = nvram_read_byte(offset+i);
 | 
			
		||||
			if (!strcmp(hdr->name, "common"))
 | 
			
		||||
				nvram_partitions[pmac_nvram_OF] = offset + 0x10;
 | 
			
		||||
			if (!strcmp(hdr->name, "APL,MacOS75")) {
 | 
			
		||||
				nvram_partitions[pmac_nvram_XPRAM] = offset + 0x10;
 | 
			
		||||
				nvram_partitions[pmac_nvram_NR] = offset + 0x110;
 | 
			
		||||
			}
 | 
			
		||||
			offset += (hdr->len * 0x10);
 | 
			
		||||
		} while(offset < NVRAM_SIZE);
 | 
			
		||||
	} else {
 | 
			
		||||
		nvram_partitions[pmac_nvram_OF] = 0x1800;
 | 
			
		||||
		nvram_partitions[pmac_nvram_XPRAM] = 0x1300;
 | 
			
		||||
		nvram_partitions[pmac_nvram_NR] = 0x1400;
 | 
			
		||||
	}
 | 
			
		||||
	DBG("nvram: OF partition at 0x%x\n", nvram_partitions[pmac_nvram_OF]);
 | 
			
		||||
	DBG("nvram: XP partition at 0x%x\n", nvram_partitions[pmac_nvram_XPRAM]);
 | 
			
		||||
	DBG("nvram: NR partition at 0x%x\n", nvram_partitions[pmac_nvram_NR]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void core99_nvram_sync(void)
 | 
			
		||||
{
 | 
			
		||||
	struct core99_header* hdr99;
 | 
			
		||||
	unsigned long flags;
 | 
			
		||||
 | 
			
		||||
	if (!is_core_99 || !nvram_data || !nvram_image)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	spin_lock_irqsave(&nv_lock, flags);
 | 
			
		||||
	if (!memcmp(nvram_image, (u8*)nvram_data + core99_bank*NVRAM_SIZE,
 | 
			
		||||
		NVRAM_SIZE))
 | 
			
		||||
		goto bail;
 | 
			
		||||
 | 
			
		||||
	DBG("Updating nvram...\n");
 | 
			
		||||
 | 
			
		||||
	hdr99 = (struct core99_header*)nvram_image;
 | 
			
		||||
	hdr99->generation++;
 | 
			
		||||
	hdr99->hdr.signature = CORE99_SIGNATURE;
 | 
			
		||||
	hdr99->hdr.cksum = chrp_checksum(&hdr99->hdr);
 | 
			
		||||
	hdr99->adler = core99_calc_adler(nvram_image);
 | 
			
		||||
	core99_bank = core99_bank ? 0 : 1;
 | 
			
		||||
	if (core99_erase_bank)
 | 
			
		||||
		if (core99_erase_bank(core99_bank)) {
 | 
			
		||||
			printk("nvram: Error erasing bank %d\n", core99_bank);
 | 
			
		||||
			goto bail;
 | 
			
		||||
		}
 | 
			
		||||
	if (core99_write_bank)
 | 
			
		||||
		if (core99_write_bank(core99_bank, nvram_image))
 | 
			
		||||
			printk("nvram: Error writing bank %d\n", core99_bank);
 | 
			
		||||
 bail:
 | 
			
		||||
	spin_unlock_irqrestore(&nv_lock, flags);
 | 
			
		||||
 | 
			
		||||
#ifdef DEBUG
 | 
			
		||||
       	mdelay(2000);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void __init pmac_nvram_init(void)
 | 
			
		||||
{
 | 
			
		||||
	struct device_node *dp;
 | 
			
		||||
 | 
			
		||||
	nvram_naddrs = 0;
 | 
			
		||||
 | 
			
		||||
	dp = find_devices("nvram");
 | 
			
		||||
	if (dp == NULL) {
 | 
			
		||||
		printk(KERN_ERR "Can't find NVRAM device\n");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	nvram_naddrs = dp->n_addrs;
 | 
			
		||||
	is_core_99 = device_is_compatible(dp, "nvram,flash");
 | 
			
		||||
	if (is_core_99) {
 | 
			
		||||
		int i;
 | 
			
		||||
		u32 gen_bank0, gen_bank1;
 | 
			
		||||
 | 
			
		||||
		if (nvram_naddrs < 1) {
 | 
			
		||||
			printk(KERN_ERR "nvram: no address\n");
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		nvram_image = alloc_bootmem(NVRAM_SIZE);
 | 
			
		||||
		if (nvram_image == NULL) {
 | 
			
		||||
			printk(KERN_ERR "nvram: can't allocate ram image\n");
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		nvram_data = ioremap(dp->addrs[0].address, NVRAM_SIZE*2);
 | 
			
		||||
		nvram_naddrs = 1; /* Make sure we get the correct case */
 | 
			
		||||
 | 
			
		||||
		DBG("nvram: Checking bank 0...\n");
 | 
			
		||||
 | 
			
		||||
		gen_bank0 = core99_check((u8 *)nvram_data);
 | 
			
		||||
		gen_bank1 = core99_check((u8 *)nvram_data + NVRAM_SIZE);
 | 
			
		||||
		core99_bank = (gen_bank0 < gen_bank1) ? 1 : 0;
 | 
			
		||||
 | 
			
		||||
		DBG("nvram: gen0=%d, gen1=%d\n", gen_bank0, gen_bank1);
 | 
			
		||||
		DBG("nvram: Active bank is: %d\n", core99_bank);
 | 
			
		||||
 | 
			
		||||
		for (i=0; i<NVRAM_SIZE; i++)
 | 
			
		||||
			nvram_image[i] = nvram_data[i + core99_bank*NVRAM_SIZE];
 | 
			
		||||
 | 
			
		||||
		ppc_md.nvram_read_val	= core99_nvram_read_byte;
 | 
			
		||||
		ppc_md.nvram_write_val	= core99_nvram_write_byte;
 | 
			
		||||
		ppc_md.nvram_sync	= core99_nvram_sync;
 | 
			
		||||
		/* 
 | 
			
		||||
		 * Maybe we could be smarter here though making an exclusive list
 | 
			
		||||
		 * of known flash chips is a bit nasty as older OF didn't provide us
 | 
			
		||||
		 * with a useful "compatible" entry. A solution would be to really
 | 
			
		||||
		 * identify the chip using flash id commands and base ourselves on
 | 
			
		||||
		 * a list of known chips IDs
 | 
			
		||||
		 */
 | 
			
		||||
		if (device_is_compatible(dp, "amd-0137")) {
 | 
			
		||||
			core99_erase_bank = amd_erase_bank;
 | 
			
		||||
			core99_write_bank = amd_write_bank;
 | 
			
		||||
		} else {
 | 
			
		||||
			core99_erase_bank = sm_erase_bank;
 | 
			
		||||
			core99_write_bank = sm_write_bank;
 | 
			
		||||
		}
 | 
			
		||||
	} else if (_machine == _MACH_chrp && nvram_naddrs == 1) {
 | 
			
		||||
		nvram_data = ioremap(dp->addrs[0].address + isa_mem_base,
 | 
			
		||||
				     dp->addrs[0].size);
 | 
			
		||||
		nvram_mult = 1;
 | 
			
		||||
		ppc_md.nvram_read_val	= direct_nvram_read_byte;
 | 
			
		||||
		ppc_md.nvram_write_val	= direct_nvram_write_byte;
 | 
			
		||||
	} else if (nvram_naddrs == 1) {
 | 
			
		||||
		nvram_data = ioremap(dp->addrs[0].address, dp->addrs[0].size);
 | 
			
		||||
		nvram_mult = (dp->addrs[0].size + NVRAM_SIZE - 1) / NVRAM_SIZE;
 | 
			
		||||
		ppc_md.nvram_read_val	= direct_nvram_read_byte;
 | 
			
		||||
		ppc_md.nvram_write_val	= direct_nvram_write_byte;
 | 
			
		||||
	} else if (nvram_naddrs == 2) {
 | 
			
		||||
		nvram_addr = ioremap(dp->addrs[0].address, dp->addrs[0].size);
 | 
			
		||||
		nvram_data = ioremap(dp->addrs[1].address, dp->addrs[1].size);
 | 
			
		||||
		ppc_md.nvram_read_val	= indirect_nvram_read_byte;
 | 
			
		||||
		ppc_md.nvram_write_val	= indirect_nvram_write_byte;
 | 
			
		||||
	} else if (nvram_naddrs == 0 && sys_ctrler == SYS_CTRLER_PMU) {
 | 
			
		||||
#ifdef CONFIG_ADB_PMU
 | 
			
		||||
		nvram_naddrs = -1;
 | 
			
		||||
		ppc_md.nvram_read_val	= pmu_nvram_read_byte;
 | 
			
		||||
		ppc_md.nvram_write_val	= pmu_nvram_write_byte;
 | 
			
		||||
#endif /* CONFIG_ADB_PMU */
 | 
			
		||||
	} else {
 | 
			
		||||
		printk(KERN_ERR "Don't know how to access NVRAM with %d addresses\n",
 | 
			
		||||
		       nvram_naddrs);
 | 
			
		||||
	}
 | 
			
		||||
	lookup_partitions();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int pmac_get_partition(int partition)
 | 
			
		||||
{
 | 
			
		||||
	return nvram_partitions[partition];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
u8 pmac_xpram_read(int xpaddr)
 | 
			
		||||
{
 | 
			
		||||
	int offset = nvram_partitions[pmac_nvram_XPRAM];
 | 
			
		||||
 | 
			
		||||
	if (offset < 0)
 | 
			
		||||
		return 0xff;
 | 
			
		||||
 | 
			
		||||
	return ppc_md.nvram_read_val(xpaddr + offset);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void pmac_xpram_write(int xpaddr, u8 data)
 | 
			
		||||
{
 | 
			
		||||
	int offset = nvram_partitions[pmac_nvram_XPRAM];
 | 
			
		||||
 | 
			
		||||
	if (offset < 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	ppc_md.nvram_write_val(xpaddr + offset, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORT_SYMBOL(pmac_get_partition);
 | 
			
		||||
EXPORT_SYMBOL(pmac_xpram_read);
 | 
			
		||||
EXPORT_SYMBOL(pmac_xpram_write);
 | 
			
		||||
							
								
								
									
										1341
									
								
								arch/powerpc/platforms/powermac/pmac_pci.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1341
									
								
								arch/powerpc/platforms/powermac/pmac_pci.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										655
									
								
								arch/powerpc/platforms/powermac/pmac_pic.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										655
									
								
								arch/powerpc/platforms/powermac/pmac_pic.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,655 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  Support for the interrupt controllers found on Power Macintosh,
 | 
			
		||||
 *  currently Apple's "Grand Central" interrupt controller in all
 | 
			
		||||
 *  it's incarnations. OpenPIC support used on newer machines is
 | 
			
		||||
 *  in a separate file
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 1997 Paul Mackerras (paulus@samba.org)
 | 
			
		||||
 *
 | 
			
		||||
 *  Maintained by Benjamin Herrenschmidt (benh@kernel.crashing.org)
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or
 | 
			
		||||
 *  modify it under the terms of the GNU General Public License
 | 
			
		||||
 *  as published by the Free Software Foundation; either version
 | 
			
		||||
 *  2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/config.h>
 | 
			
		||||
#include <linux/stddef.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/sched.h>
 | 
			
		||||
#include <linux/signal.h>
 | 
			
		||||
#include <linux/pci.h>
 | 
			
		||||
#include <linux/interrupt.h>
 | 
			
		||||
#include <linux/sysdev.h>
 | 
			
		||||
#include <linux/adb.h>
 | 
			
		||||
#include <linux/pmu.h>
 | 
			
		||||
 | 
			
		||||
#include <asm/sections.h>
 | 
			
		||||
#include <asm/io.h>
 | 
			
		||||
#include <asm/smp.h>
 | 
			
		||||
#include <asm/prom.h>
 | 
			
		||||
#include <asm/pci-bridge.h>
 | 
			
		||||
#include <asm/time.h>
 | 
			
		||||
#include <asm/open_pic.h>
 | 
			
		||||
#include <asm/xmon.h>
 | 
			
		||||
#include <asm/pmac_feature.h>
 | 
			
		||||
#include <asm/mpic.h>
 | 
			
		||||
 | 
			
		||||
#include "pmac_pic.h"
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * XXX this should be in xmon.h, but putting it there means xmon.h
 | 
			
		||||
 * has to include <linux/interrupt.h> (to get irqreturn_t), which
 | 
			
		||||
 * causes all sorts of problems.  -- paulus
 | 
			
		||||
 */
 | 
			
		||||
extern irqreturn_t xmon_irq(int, void *, struct pt_regs *);
 | 
			
		||||
 | 
			
		||||
struct pmac_irq_hw {
 | 
			
		||||
        unsigned int    event;
 | 
			
		||||
        unsigned int    enable;
 | 
			
		||||
        unsigned int    ack;
 | 
			
		||||
        unsigned int    level;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Default addresses */
 | 
			
		||||
static volatile struct pmac_irq_hw *pmac_irq_hw[4] = {
 | 
			
		||||
        (struct pmac_irq_hw *) 0xf3000020,
 | 
			
		||||
        (struct pmac_irq_hw *) 0xf3000010,
 | 
			
		||||
        (struct pmac_irq_hw *) 0xf4000020,
 | 
			
		||||
        (struct pmac_irq_hw *) 0xf4000010,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define GC_LEVEL_MASK		0x3ff00000
 | 
			
		||||
#define OHARE_LEVEL_MASK	0x1ff00000
 | 
			
		||||
#define HEATHROW_LEVEL_MASK	0x1ff00000
 | 
			
		||||
 | 
			
		||||
static int max_irqs;
 | 
			
		||||
static int max_real_irqs;
 | 
			
		||||
static u32 level_mask[4];
 | 
			
		||||
 | 
			
		||||
static DEFINE_SPINLOCK(pmac_pic_lock);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define GATWICK_IRQ_POOL_SIZE        10
 | 
			
		||||
static struct interrupt_info gatwick_int_pool[GATWICK_IRQ_POOL_SIZE];
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Mark an irq as "lost".  This is only used on the pmac
 | 
			
		||||
 * since it can lose interrupts (see pmac_set_irq_mask).
 | 
			
		||||
 * -- Cort
 | 
			
		||||
 */
 | 
			
		||||
void
 | 
			
		||||
__set_lost(unsigned long irq_nr, int nokick)
 | 
			
		||||
{
 | 
			
		||||
	if (!test_and_set_bit(irq_nr, ppc_lost_interrupts)) {
 | 
			
		||||
		atomic_inc(&ppc_n_lost_interrupts);
 | 
			
		||||
		if (!nokick)
 | 
			
		||||
			set_dec(1);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
pmac_mask_and_ack_irq(unsigned int irq_nr)
 | 
			
		||||
{
 | 
			
		||||
        unsigned long bit = 1UL << (irq_nr & 0x1f);
 | 
			
		||||
        int i = irq_nr >> 5;
 | 
			
		||||
        unsigned long flags;
 | 
			
		||||
 | 
			
		||||
        if ((unsigned)irq_nr >= max_irqs)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
        clear_bit(irq_nr, ppc_cached_irq_mask);
 | 
			
		||||
        if (test_and_clear_bit(irq_nr, ppc_lost_interrupts))
 | 
			
		||||
                atomic_dec(&ppc_n_lost_interrupts);
 | 
			
		||||
	spin_lock_irqsave(&pmac_pic_lock, flags);
 | 
			
		||||
        out_le32(&pmac_irq_hw[i]->enable, ppc_cached_irq_mask[i]);
 | 
			
		||||
        out_le32(&pmac_irq_hw[i]->ack, bit);
 | 
			
		||||
        do {
 | 
			
		||||
                /* make sure ack gets to controller before we enable
 | 
			
		||||
                   interrupts */
 | 
			
		||||
                mb();
 | 
			
		||||
        } while((in_le32(&pmac_irq_hw[i]->enable) & bit)
 | 
			
		||||
                != (ppc_cached_irq_mask[i] & bit));
 | 
			
		||||
	spin_unlock_irqrestore(&pmac_pic_lock, flags);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void pmac_set_irq_mask(unsigned int irq_nr, int nokicklost)
 | 
			
		||||
{
 | 
			
		||||
        unsigned long bit = 1UL << (irq_nr & 0x1f);
 | 
			
		||||
        int i = irq_nr >> 5;
 | 
			
		||||
        unsigned long flags;
 | 
			
		||||
 | 
			
		||||
        if ((unsigned)irq_nr >= max_irqs)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
	spin_lock_irqsave(&pmac_pic_lock, flags);
 | 
			
		||||
        /* enable unmasked interrupts */
 | 
			
		||||
        out_le32(&pmac_irq_hw[i]->enable, ppc_cached_irq_mask[i]);
 | 
			
		||||
 | 
			
		||||
        do {
 | 
			
		||||
                /* make sure mask gets to controller before we
 | 
			
		||||
                   return to user */
 | 
			
		||||
                mb();
 | 
			
		||||
        } while((in_le32(&pmac_irq_hw[i]->enable) & bit)
 | 
			
		||||
                != (ppc_cached_irq_mask[i] & bit));
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
         * Unfortunately, setting the bit in the enable register
 | 
			
		||||
         * when the device interrupt is already on *doesn't* set
 | 
			
		||||
         * the bit in the flag register or request another interrupt.
 | 
			
		||||
         */
 | 
			
		||||
        if (bit & ppc_cached_irq_mask[i] & in_le32(&pmac_irq_hw[i]->level))
 | 
			
		||||
		__set_lost((ulong)irq_nr, nokicklost);
 | 
			
		||||
	spin_unlock_irqrestore(&pmac_pic_lock, flags);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* When an irq gets requested for the first client, if it's an
 | 
			
		||||
 * edge interrupt, we clear any previous one on the controller
 | 
			
		||||
 */
 | 
			
		||||
static unsigned int pmac_startup_irq(unsigned int irq_nr)
 | 
			
		||||
{
 | 
			
		||||
        unsigned long bit = 1UL << (irq_nr & 0x1f);
 | 
			
		||||
        int i = irq_nr >> 5;
 | 
			
		||||
 | 
			
		||||
	if ((irq_desc[irq_nr].status & IRQ_LEVEL) == 0)
 | 
			
		||||
		out_le32(&pmac_irq_hw[i]->ack, bit);
 | 
			
		||||
        set_bit(irq_nr, ppc_cached_irq_mask);
 | 
			
		||||
        pmac_set_irq_mask(irq_nr, 0);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void pmac_mask_irq(unsigned int irq_nr)
 | 
			
		||||
{
 | 
			
		||||
        clear_bit(irq_nr, ppc_cached_irq_mask);
 | 
			
		||||
        pmac_set_irq_mask(irq_nr, 0);
 | 
			
		||||
        mb();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void pmac_unmask_irq(unsigned int irq_nr)
 | 
			
		||||
{
 | 
			
		||||
        set_bit(irq_nr, ppc_cached_irq_mask);
 | 
			
		||||
        pmac_set_irq_mask(irq_nr, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void pmac_end_irq(unsigned int irq_nr)
 | 
			
		||||
{
 | 
			
		||||
	if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS))
 | 
			
		||||
	    && irq_desc[irq_nr].action) {
 | 
			
		||||
        	set_bit(irq_nr, ppc_cached_irq_mask);
 | 
			
		||||
	        pmac_set_irq_mask(irq_nr, 1);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
struct hw_interrupt_type pmac_pic = {
 | 
			
		||||
	.typename	= " PMAC-PIC ",
 | 
			
		||||
	.startup	= pmac_startup_irq,
 | 
			
		||||
	.enable		= pmac_unmask_irq,
 | 
			
		||||
	.disable	= pmac_mask_irq,
 | 
			
		||||
	.ack		= pmac_mask_and_ack_irq,
 | 
			
		||||
	.end		= pmac_end_irq,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct hw_interrupt_type gatwick_pic = {
 | 
			
		||||
	.typename	= " GATWICK  ",
 | 
			
		||||
	.startup	= pmac_startup_irq,
 | 
			
		||||
	.enable		= pmac_unmask_irq,
 | 
			
		||||
	.disable	= pmac_mask_irq,
 | 
			
		||||
	.ack		= pmac_mask_and_ack_irq,
 | 
			
		||||
	.end		= pmac_end_irq,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static irqreturn_t gatwick_action(int cpl, void *dev_id, struct pt_regs *regs)
 | 
			
		||||
{
 | 
			
		||||
	int irq, bits;
 | 
			
		||||
 | 
			
		||||
	for (irq = max_irqs; (irq -= 32) >= max_real_irqs; ) {
 | 
			
		||||
		int i = irq >> 5;
 | 
			
		||||
		bits = in_le32(&pmac_irq_hw[i]->event) | ppc_lost_interrupts[i];
 | 
			
		||||
		/* We must read level interrupts from the level register */
 | 
			
		||||
		bits |= (in_le32(&pmac_irq_hw[i]->level) & level_mask[i]);
 | 
			
		||||
		bits &= ppc_cached_irq_mask[i];
 | 
			
		||||
		if (bits == 0)
 | 
			
		||||
			continue;
 | 
			
		||||
		irq += __ilog2(bits);
 | 
			
		||||
		__do_IRQ(irq, regs);
 | 
			
		||||
		return IRQ_HANDLED;
 | 
			
		||||
	}
 | 
			
		||||
	printk("gatwick irq not from gatwick pic\n");
 | 
			
		||||
	return IRQ_NONE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
pmac_get_irq(struct pt_regs *regs)
 | 
			
		||||
{
 | 
			
		||||
	int irq;
 | 
			
		||||
	unsigned long bits = 0;
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_SMP
 | 
			
		||||
	void psurge_smp_message_recv(struct pt_regs *);
 | 
			
		||||
 | 
			
		||||
       	/* IPI's are a hack on the powersurge -- Cort */
 | 
			
		||||
       	if ( smp_processor_id() != 0 ) {
 | 
			
		||||
		psurge_smp_message_recv(regs);
 | 
			
		||||
		return -2;	/* ignore, already handled */
 | 
			
		||||
        }
 | 
			
		||||
#endif /* CONFIG_SMP */
 | 
			
		||||
	for (irq = max_real_irqs; (irq -= 32) >= 0; ) {
 | 
			
		||||
		int i = irq >> 5;
 | 
			
		||||
		bits = in_le32(&pmac_irq_hw[i]->event) | ppc_lost_interrupts[i];
 | 
			
		||||
		/* We must read level interrupts from the level register */
 | 
			
		||||
		bits |= (in_le32(&pmac_irq_hw[i]->level) & level_mask[i]);
 | 
			
		||||
		bits &= ppc_cached_irq_mask[i];
 | 
			
		||||
		if (bits == 0)
 | 
			
		||||
			continue;
 | 
			
		||||
		irq += __ilog2(bits);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return irq;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* This routine will fix some missing interrupt values in the device tree
 | 
			
		||||
 * on the gatwick mac-io controller used by some PowerBooks
 | 
			
		||||
 */
 | 
			
		||||
static void __init
 | 
			
		||||
pmac_fix_gatwick_interrupts(struct device_node *gw, int irq_base)
 | 
			
		||||
{
 | 
			
		||||
	struct device_node *node;
 | 
			
		||||
	int count;
 | 
			
		||||
 | 
			
		||||
	memset(gatwick_int_pool, 0, sizeof(gatwick_int_pool));
 | 
			
		||||
	node = gw->child;
 | 
			
		||||
	count = 0;
 | 
			
		||||
	while(node)
 | 
			
		||||
	{
 | 
			
		||||
		/* Fix SCC */
 | 
			
		||||
		if (strcasecmp(node->name, "escc") == 0)
 | 
			
		||||
			if (node->child) {
 | 
			
		||||
				if (node->child->n_intrs < 3) {
 | 
			
		||||
					node->child->intrs = &gatwick_int_pool[count];
 | 
			
		||||
					count += 3;
 | 
			
		||||
				}
 | 
			
		||||
				node->child->n_intrs = 3;
 | 
			
		||||
				node->child->intrs[0].line = 15+irq_base;
 | 
			
		||||
				node->child->intrs[1].line =  4+irq_base;
 | 
			
		||||
				node->child->intrs[2].line =  5+irq_base;
 | 
			
		||||
				printk(KERN_INFO "irq: fixed SCC on second controller (%d,%d,%d)\n",
 | 
			
		||||
					node->child->intrs[0].line,
 | 
			
		||||
					node->child->intrs[1].line,
 | 
			
		||||
					node->child->intrs[2].line);
 | 
			
		||||
			}
 | 
			
		||||
		/* Fix media-bay & left SWIM */
 | 
			
		||||
		if (strcasecmp(node->name, "media-bay") == 0) {
 | 
			
		||||
			struct device_node* ya_node;
 | 
			
		||||
 | 
			
		||||
			if (node->n_intrs == 0)
 | 
			
		||||
				node->intrs = &gatwick_int_pool[count++];
 | 
			
		||||
			node->n_intrs = 1;
 | 
			
		||||
			node->intrs[0].line = 29+irq_base;
 | 
			
		||||
			printk(KERN_INFO "irq: fixed media-bay on second controller (%d)\n",
 | 
			
		||||
					node->intrs[0].line);
 | 
			
		||||
 | 
			
		||||
			ya_node = node->child;
 | 
			
		||||
			while(ya_node)
 | 
			
		||||
			{
 | 
			
		||||
				if (strcasecmp(ya_node->name, "floppy") == 0) {
 | 
			
		||||
					if (ya_node->n_intrs < 2) {
 | 
			
		||||
						ya_node->intrs = &gatwick_int_pool[count];
 | 
			
		||||
						count += 2;
 | 
			
		||||
					}
 | 
			
		||||
					ya_node->n_intrs = 2;
 | 
			
		||||
					ya_node->intrs[0].line = 19+irq_base;
 | 
			
		||||
					ya_node->intrs[1].line =  1+irq_base;
 | 
			
		||||
					printk(KERN_INFO "irq: fixed floppy on second controller (%d,%d)\n",
 | 
			
		||||
						ya_node->intrs[0].line, ya_node->intrs[1].line);
 | 
			
		||||
				}
 | 
			
		||||
				if (strcasecmp(ya_node->name, "ata4") == 0) {
 | 
			
		||||
					if (ya_node->n_intrs < 2) {
 | 
			
		||||
						ya_node->intrs = &gatwick_int_pool[count];
 | 
			
		||||
						count += 2;
 | 
			
		||||
					}
 | 
			
		||||
					ya_node->n_intrs = 2;
 | 
			
		||||
					ya_node->intrs[0].line = 14+irq_base;
 | 
			
		||||
					ya_node->intrs[1].line =  3+irq_base;
 | 
			
		||||
					printk(KERN_INFO "irq: fixed ide on second controller (%d,%d)\n",
 | 
			
		||||
						ya_node->intrs[0].line, ya_node->intrs[1].line);
 | 
			
		||||
				}
 | 
			
		||||
				ya_node = ya_node->sibling;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		node = node->sibling;
 | 
			
		||||
	}
 | 
			
		||||
	if (count > 10) {
 | 
			
		||||
		printk("WARNING !! Gatwick interrupt pool overflow\n");
 | 
			
		||||
		printk("  GATWICK_IRQ_POOL_SIZE = %d\n", GATWICK_IRQ_POOL_SIZE);
 | 
			
		||||
		printk("              requested = %d\n", count);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * The PowerBook 3400/2400/3500 can have a combo ethernet/modem
 | 
			
		||||
 * card which includes an ohare chip that acts as a second interrupt
 | 
			
		||||
 * controller.  If we find this second ohare, set it up and fix the
 | 
			
		||||
 * interrupt value in the device tree for the ethernet chip.
 | 
			
		||||
 */
 | 
			
		||||
static int __init enable_second_ohare(void)
 | 
			
		||||
{
 | 
			
		||||
	unsigned char bus, devfn;
 | 
			
		||||
	unsigned short cmd;
 | 
			
		||||
        unsigned long addr;
 | 
			
		||||
	struct device_node *irqctrler = find_devices("pci106b,7");
 | 
			
		||||
	struct device_node *ether;
 | 
			
		||||
 | 
			
		||||
	if (irqctrler == NULL || irqctrler->n_addrs <= 0)
 | 
			
		||||
		return -1;
 | 
			
		||||
	addr = (unsigned long) ioremap(irqctrler->addrs[0].address, 0x40);
 | 
			
		||||
	pmac_irq_hw[1] = (volatile struct pmac_irq_hw *)(addr + 0x20);
 | 
			
		||||
	max_irqs = 64;
 | 
			
		||||
	if (pci_device_from_OF_node(irqctrler, &bus, &devfn) == 0) {
 | 
			
		||||
		struct pci_controller* hose = pci_find_hose_for_OF_device(irqctrler);
 | 
			
		||||
		if (!hose)
 | 
			
		||||
		    printk(KERN_ERR "Can't find PCI hose for OHare2 !\n");
 | 
			
		||||
		else {
 | 
			
		||||
		    early_read_config_word(hose, bus, devfn, PCI_COMMAND, &cmd);
 | 
			
		||||
		    cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
 | 
			
		||||
	  	    cmd &= ~PCI_COMMAND_IO;
 | 
			
		||||
		    early_write_config_word(hose, bus, devfn, PCI_COMMAND, cmd);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Fix interrupt for the modem/ethernet combo controller. The number
 | 
			
		||||
	   in the device tree (27) is bogus (correct for the ethernet-only
 | 
			
		||||
	   board but not the combo ethernet/modem board).
 | 
			
		||||
	   The real interrupt is 28 on the second controller -> 28+32 = 60.
 | 
			
		||||
	*/
 | 
			
		||||
	ether = find_devices("pci1011,14");
 | 
			
		||||
	if (ether && ether->n_intrs > 0) {
 | 
			
		||||
		ether->intrs[0].line = 60;
 | 
			
		||||
		printk(KERN_INFO "irq: Fixed ethernet IRQ to %d\n",
 | 
			
		||||
		       ether->intrs[0].line);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Return the interrupt number of the cascade */
 | 
			
		||||
	return irqctrler->intrs[0].line;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int pmac_u3_cascade(struct pt_regs *regs, void *data)
 | 
			
		||||
{
 | 
			
		||||
	return mpic_get_one_irq((struct mpic *)data, regs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_XMON
 | 
			
		||||
static struct irqaction xmon_action = {
 | 
			
		||||
	.handler	= xmon_irq,
 | 
			
		||||
	.flags		= 0,
 | 
			
		||||
	.mask		= CPU_MASK_NONE,
 | 
			
		||||
	.name		= "NMI - XMON"
 | 
			
		||||
};
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static struct irqaction gatwick_cascade_action = {
 | 
			
		||||
	.handler	= gatwick_action,
 | 
			
		||||
	.flags		= SA_INTERRUPT,
 | 
			
		||||
	.mask		= CPU_MASK_NONE,
 | 
			
		||||
	.name		= "cascade",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void __init pmac_pic_init(void)
 | 
			
		||||
{
 | 
			
		||||
        int i;
 | 
			
		||||
        struct device_node *irqctrler  = NULL;
 | 
			
		||||
        struct device_node *irqctrler2 = NULL;
 | 
			
		||||
	struct device_node *np;
 | 
			
		||||
        unsigned long addr;
 | 
			
		||||
	int irq_cascade = -1;
 | 
			
		||||
	struct mpic *mpic1, *mpic2;
 | 
			
		||||
 | 
			
		||||
	/* We first try to detect Apple's new Core99 chipset, since mac-io
 | 
			
		||||
	 * is quite different on those machines and contains an IBM MPIC2.
 | 
			
		||||
	 */
 | 
			
		||||
	np = find_type_devices("open-pic");
 | 
			
		||||
	while (np) {
 | 
			
		||||
		if (np->parent && !strcmp(np->parent->name, "u3"))
 | 
			
		||||
			irqctrler2 = np;
 | 
			
		||||
		else
 | 
			
		||||
			irqctrler = np;
 | 
			
		||||
		np = np->next;
 | 
			
		||||
	}
 | 
			
		||||
	if (irqctrler != NULL && irqctrler->n_addrs > 0) {
 | 
			
		||||
		unsigned char senses[128];
 | 
			
		||||
 | 
			
		||||
		printk(KERN_INFO "PowerMac using OpenPIC irq controller at 0x%08x\n",
 | 
			
		||||
		       (unsigned int)irqctrler->addrs[0].address);
 | 
			
		||||
 | 
			
		||||
		prom_get_irq_senses(senses, 0, 128);
 | 
			
		||||
		mpic1 = mpic_alloc(irqctrler->addrs[0].address,
 | 
			
		||||
				   MPIC_PRIMARY | MPIC_WANTS_RESET,
 | 
			
		||||
				   0, 0, 128, 256, senses, 128, " K2-MPIC  ");
 | 
			
		||||
		BUG_ON(mpic1 == NULL);
 | 
			
		||||
		mpic_init(mpic1);		
 | 
			
		||||
 | 
			
		||||
		if (irqctrler2 != NULL && irqctrler2->n_intrs > 0 &&
 | 
			
		||||
		    irqctrler2->n_addrs > 0) {
 | 
			
		||||
			printk(KERN_INFO "Slave OpenPIC at 0x%08x hooked on IRQ %d\n",
 | 
			
		||||
			       (u32)irqctrler2->addrs[0].address,
 | 
			
		||||
			       irqctrler2->intrs[0].line);
 | 
			
		||||
 | 
			
		||||
			pmac_call_feature(PMAC_FTR_ENABLE_MPIC, irqctrler2, 0, 0);
 | 
			
		||||
			prom_get_irq_senses(senses, 128, 128 + 128);
 | 
			
		||||
 | 
			
		||||
			/* We don't need to set MPIC_BROKEN_U3 here since we don't have
 | 
			
		||||
			 * hypertransport interrupts routed to it
 | 
			
		||||
			 */
 | 
			
		||||
			mpic2 = mpic_alloc(irqctrler2->addrs[0].address,
 | 
			
		||||
					   MPIC_BIG_ENDIAN | MPIC_WANTS_RESET,
 | 
			
		||||
					   0, 128, 128, 0, senses, 128, " U3-MPIC  ");
 | 
			
		||||
			BUG_ON(mpic2 == NULL);
 | 
			
		||||
			mpic_init(mpic2);
 | 
			
		||||
			mpic_setup_cascade(irqctrler2->intrs[0].line,
 | 
			
		||||
					   pmac_u3_cascade, mpic2);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Get the level/edge settings, assume if it's not
 | 
			
		||||
	 * a Grand Central nor an OHare, then it's an Heathrow
 | 
			
		||||
	 * (or Paddington).
 | 
			
		||||
	 */
 | 
			
		||||
	if (find_devices("gc"))
 | 
			
		||||
		level_mask[0] = GC_LEVEL_MASK;
 | 
			
		||||
	else if (find_devices("ohare")) {
 | 
			
		||||
		level_mask[0] = OHARE_LEVEL_MASK;
 | 
			
		||||
		/* We might have a second cascaded ohare */
 | 
			
		||||
		level_mask[1] = OHARE_LEVEL_MASK;
 | 
			
		||||
	} else {
 | 
			
		||||
		level_mask[0] = HEATHROW_LEVEL_MASK;
 | 
			
		||||
		level_mask[1] = 0;
 | 
			
		||||
		/* We might have a second cascaded heathrow */
 | 
			
		||||
		level_mask[2] = HEATHROW_LEVEL_MASK;
 | 
			
		||||
		level_mask[3] = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * G3 powermacs and 1999 G3 PowerBooks have 64 interrupts,
 | 
			
		||||
	 * 1998 G3 Series PowerBooks have 128,
 | 
			
		||||
	 * other powermacs have 32.
 | 
			
		||||
	 * The combo ethernet/modem card for the Powerstar powerbooks
 | 
			
		||||
	 * (2400/3400/3500, ohare based) has a second ohare chip
 | 
			
		||||
	 * effectively making a total of 64.
 | 
			
		||||
	 */
 | 
			
		||||
	max_irqs = max_real_irqs = 32;
 | 
			
		||||
	irqctrler = find_devices("mac-io");
 | 
			
		||||
	if (irqctrler)
 | 
			
		||||
	{
 | 
			
		||||
		max_real_irqs = 64;
 | 
			
		||||
		if (irqctrler->next)
 | 
			
		||||
			max_irqs = 128;
 | 
			
		||||
		else
 | 
			
		||||
			max_irqs = 64;
 | 
			
		||||
	}
 | 
			
		||||
	for ( i = 0; i < max_real_irqs ; i++ )
 | 
			
		||||
		irq_desc[i].handler = &pmac_pic;
 | 
			
		||||
 | 
			
		||||
	/* get addresses of first controller */
 | 
			
		||||
	if (irqctrler) {
 | 
			
		||||
		if  (irqctrler->n_addrs > 0) {
 | 
			
		||||
			addr = (unsigned long)
 | 
			
		||||
				ioremap(irqctrler->addrs[0].address, 0x40);
 | 
			
		||||
			for (i = 0; i < 2; ++i)
 | 
			
		||||
				pmac_irq_hw[i] = (volatile struct pmac_irq_hw*)
 | 
			
		||||
					(addr + (2 - i) * 0x10);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* get addresses of second controller */
 | 
			
		||||
		irqctrler = irqctrler->next;
 | 
			
		||||
		if (irqctrler && irqctrler->n_addrs > 0) {
 | 
			
		||||
			addr = (unsigned long)
 | 
			
		||||
				ioremap(irqctrler->addrs[0].address, 0x40);
 | 
			
		||||
			for (i = 2; i < 4; ++i)
 | 
			
		||||
				pmac_irq_hw[i] = (volatile struct pmac_irq_hw*)
 | 
			
		||||
					(addr + (4 - i) * 0x10);
 | 
			
		||||
			irq_cascade = irqctrler->intrs[0].line;
 | 
			
		||||
			if (device_is_compatible(irqctrler, "gatwick"))
 | 
			
		||||
				pmac_fix_gatwick_interrupts(irqctrler, max_real_irqs);
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		/* older powermacs have a GC (grand central) or ohare at
 | 
			
		||||
		   f3000000, with interrupt control registers at f3000020. */
 | 
			
		||||
		addr = (unsigned long) ioremap(0xf3000000, 0x40);
 | 
			
		||||
		pmac_irq_hw[0] = (volatile struct pmac_irq_hw *) (addr + 0x20);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* PowerBooks 3400 and 3500 can have a second controller in a second
 | 
			
		||||
	   ohare chip, on the combo ethernet/modem card */
 | 
			
		||||
	if (machine_is_compatible("AAPL,3400/2400")
 | 
			
		||||
	     || machine_is_compatible("AAPL,3500"))
 | 
			
		||||
		irq_cascade = enable_second_ohare();
 | 
			
		||||
 | 
			
		||||
	/* disable all interrupts in all controllers */
 | 
			
		||||
	for (i = 0; i * 32 < max_irqs; ++i)
 | 
			
		||||
		out_le32(&pmac_irq_hw[i]->enable, 0);
 | 
			
		||||
	/* mark level interrupts */
 | 
			
		||||
	for (i = 0; i < max_irqs; i++)
 | 
			
		||||
		if (level_mask[i >> 5] & (1UL << (i & 0x1f)))
 | 
			
		||||
			irq_desc[i].status = IRQ_LEVEL;
 | 
			
		||||
 | 
			
		||||
	/* get interrupt line of secondary interrupt controller */
 | 
			
		||||
	if (irq_cascade >= 0) {
 | 
			
		||||
		printk(KERN_INFO "irq: secondary controller on irq %d\n",
 | 
			
		||||
			(int)irq_cascade);
 | 
			
		||||
		for ( i = max_real_irqs ; i < max_irqs ; i++ )
 | 
			
		||||
			irq_desc[i].handler = &gatwick_pic;
 | 
			
		||||
		setup_irq(irq_cascade, &gatwick_cascade_action);
 | 
			
		||||
	}
 | 
			
		||||
	printk("System has %d possible interrupts\n", max_irqs);
 | 
			
		||||
	if (max_irqs != max_real_irqs)
 | 
			
		||||
		printk(KERN_DEBUG "%d interrupts on main controller\n",
 | 
			
		||||
			max_real_irqs);
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_XMON
 | 
			
		||||
	setup_irq(20, &xmon_action);
 | 
			
		||||
#endif	/* CONFIG_XMON */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_PM
 | 
			
		||||
/*
 | 
			
		||||
 * These procedures are used in implementing sleep on the powerbooks.
 | 
			
		||||
 * sleep_save_intrs() saves the states of all interrupt enables
 | 
			
		||||
 * and disables all interrupts except for the nominated one.
 | 
			
		||||
 * sleep_restore_intrs() restores the states of all interrupt enables.
 | 
			
		||||
 */
 | 
			
		||||
unsigned long sleep_save_mask[2];
 | 
			
		||||
 | 
			
		||||
/* This used to be passed by the PMU driver but that link got
 | 
			
		||||
 * broken with the new driver model. We use this tweak for now...
 | 
			
		||||
 */
 | 
			
		||||
static int pmacpic_find_viaint(void)
 | 
			
		||||
{
 | 
			
		||||
	int viaint = -1;
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_ADB_PMU
 | 
			
		||||
	struct device_node *np;
 | 
			
		||||
 | 
			
		||||
	if (pmu_get_model() != PMU_OHARE_BASED)
 | 
			
		||||
		goto not_found;
 | 
			
		||||
	np = of_find_node_by_name(NULL, "via-pmu");
 | 
			
		||||
	if (np == NULL)
 | 
			
		||||
		goto not_found;
 | 
			
		||||
	viaint = np->intrs[0].line;
 | 
			
		||||
#endif /* CONFIG_ADB_PMU */
 | 
			
		||||
 | 
			
		||||
not_found:
 | 
			
		||||
	return viaint;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int pmacpic_suspend(struct sys_device *sysdev, pm_message_t state)
 | 
			
		||||
{
 | 
			
		||||
	int viaint = pmacpic_find_viaint();
 | 
			
		||||
 | 
			
		||||
	sleep_save_mask[0] = ppc_cached_irq_mask[0];
 | 
			
		||||
	sleep_save_mask[1] = ppc_cached_irq_mask[1];
 | 
			
		||||
	ppc_cached_irq_mask[0] = 0;
 | 
			
		||||
	ppc_cached_irq_mask[1] = 0;
 | 
			
		||||
	if (viaint > 0)
 | 
			
		||||
		set_bit(viaint, ppc_cached_irq_mask);
 | 
			
		||||
	out_le32(&pmac_irq_hw[0]->enable, ppc_cached_irq_mask[0]);
 | 
			
		||||
	if (max_real_irqs > 32)
 | 
			
		||||
		out_le32(&pmac_irq_hw[1]->enable, ppc_cached_irq_mask[1]);
 | 
			
		||||
	(void)in_le32(&pmac_irq_hw[0]->event);
 | 
			
		||||
	/* make sure mask gets to controller before we return to caller */
 | 
			
		||||
	mb();
 | 
			
		||||
        (void)in_le32(&pmac_irq_hw[0]->enable);
 | 
			
		||||
 | 
			
		||||
        return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int pmacpic_resume(struct sys_device *sysdev)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	out_le32(&pmac_irq_hw[0]->enable, 0);
 | 
			
		||||
	if (max_real_irqs > 32)
 | 
			
		||||
		out_le32(&pmac_irq_hw[1]->enable, 0);
 | 
			
		||||
	mb();
 | 
			
		||||
	for (i = 0; i < max_real_irqs; ++i)
 | 
			
		||||
		if (test_bit(i, sleep_save_mask))
 | 
			
		||||
			pmac_unmask_irq(i);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif /* CONFIG_PM */
 | 
			
		||||
 | 
			
		||||
static struct sysdev_class pmacpic_sysclass = {
 | 
			
		||||
	set_kset_name("pmac_pic"),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct sys_device device_pmacpic = {
 | 
			
		||||
	.id		= 0,
 | 
			
		||||
	.cls		= &pmacpic_sysclass,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct sysdev_driver driver_pmacpic = {
 | 
			
		||||
#ifdef CONFIG_PM
 | 
			
		||||
	.suspend	= &pmacpic_suspend,
 | 
			
		||||
	.resume		= &pmacpic_resume,
 | 
			
		||||
#endif /* CONFIG_PM */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int __init init_pmacpic_sysfs(void)
 | 
			
		||||
{
 | 
			
		||||
	if (max_irqs == 0)
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
 | 
			
		||||
	printk(KERN_DEBUG "Registering pmac pic with sysfs...\n");
 | 
			
		||||
	sysdev_class_register(&pmacpic_sysclass);
 | 
			
		||||
	sysdev_register(&device_pmacpic);
 | 
			
		||||
	sysdev_driver_register(&pmacpic_sysclass, &driver_pmacpic);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
subsys_initcall(init_pmacpic_sysfs);
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										11
									
								
								arch/powerpc/platforms/powermac/pmac_pic.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								arch/powerpc/platforms/powermac/pmac_pic.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
#ifndef __PPC_PLATFORMS_PMAC_PIC_H
 | 
			
		||||
#define __PPC_PLATFORMS_PMAC_PIC_H
 | 
			
		||||
 | 
			
		||||
#include <linux/irq.h>
 | 
			
		||||
 | 
			
		||||
extern struct hw_interrupt_type pmac_pic;
 | 
			
		||||
 | 
			
		||||
void pmac_pic_init(void);
 | 
			
		||||
int pmac_get_irq(struct pt_regs *regs);
 | 
			
		||||
 | 
			
		||||
#endif /* __PPC_PLATFORMS_PMAC_PIC_H */
 | 
			
		||||
							
								
								
									
										662
									
								
								arch/powerpc/platforms/powermac/pmac_setup.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										662
									
								
								arch/powerpc/platforms/powermac/pmac_setup.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,662 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  arch/ppc/platforms/setup.c
 | 
			
		||||
 *
 | 
			
		||||
 *  PowerPC version
 | 
			
		||||
 *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
 | 
			
		||||
 *
 | 
			
		||||
 *  Adapted for Power Macintosh by Paul Mackerras
 | 
			
		||||
 *    Copyright (C) 1996 Paul Mackerras (paulus@cs.anu.edu.au)
 | 
			
		||||
 *
 | 
			
		||||
 *  Derived from "arch/alpha/kernel/setup.c"
 | 
			
		||||
 *    Copyright (C) 1995 Linus Torvalds
 | 
			
		||||
 *
 | 
			
		||||
 *  Maintained by Benjamin Herrenschmidt (benh@kernel.crashing.org)
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or
 | 
			
		||||
 *  modify it under the terms of the GNU General Public License
 | 
			
		||||
 *  as published by the Free Software Foundation; either version
 | 
			
		||||
 *  2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * bootup setup stuff..
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/config.h>
 | 
			
		||||
#include <linux/init.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/slab.h>
 | 
			
		||||
#include <linux/user.h>
 | 
			
		||||
#include <linux/a.out.h>
 | 
			
		||||
#include <linux/tty.h>
 | 
			
		||||
#include <linux/string.h>
 | 
			
		||||
#include <linux/delay.h>
 | 
			
		||||
#include <linux/ioport.h>
 | 
			
		||||
#include <linux/major.h>
 | 
			
		||||
#include <linux/initrd.h>
 | 
			
		||||
#include <linux/vt_kern.h>
 | 
			
		||||
#include <linux/console.h>
 | 
			
		||||
#include <linux/ide.h>
 | 
			
		||||
#include <linux/pci.h>
 | 
			
		||||
#include <linux/adb.h>
 | 
			
		||||
#include <linux/cuda.h>
 | 
			
		||||
#include <linux/pmu.h>
 | 
			
		||||
#include <linux/irq.h>
 | 
			
		||||
#include <linux/seq_file.h>
 | 
			
		||||
#include <linux/root_dev.h>
 | 
			
		||||
#include <linux/bitops.h>
 | 
			
		||||
#include <linux/suspend.h>
 | 
			
		||||
 | 
			
		||||
#include <asm/reg.h>
 | 
			
		||||
#include <asm/sections.h>
 | 
			
		||||
#include <asm/prom.h>
 | 
			
		||||
#include <asm/system.h>
 | 
			
		||||
#include <asm/pgtable.h>
 | 
			
		||||
#include <asm/io.h>
 | 
			
		||||
#include <asm/pci-bridge.h>
 | 
			
		||||
#include <asm/ohare.h>
 | 
			
		||||
#include <asm/mediabay.h>
 | 
			
		||||
#include <asm/machdep.h>
 | 
			
		||||
#include <asm/dma.h>
 | 
			
		||||
#include <asm/bootx.h>
 | 
			
		||||
#include <asm/cputable.h>
 | 
			
		||||
#include <asm/btext.h>
 | 
			
		||||
#include <asm/pmac_feature.h>
 | 
			
		||||
#include <asm/time.h>
 | 
			
		||||
#include <asm/of_device.h>
 | 
			
		||||
#include <asm/mmu_context.h>
 | 
			
		||||
 | 
			
		||||
#include "pmac_pic.h"
 | 
			
		||||
 | 
			
		||||
#undef SHOW_GATWICK_IRQS
 | 
			
		||||
 | 
			
		||||
extern long pmac_time_init(void);
 | 
			
		||||
extern unsigned long pmac_get_rtc_time(void);
 | 
			
		||||
extern int pmac_set_rtc_time(unsigned long nowtime);
 | 
			
		||||
extern void pmac_read_rtc_time(void);
 | 
			
		||||
extern void pmac_calibrate_decr(void);
 | 
			
		||||
extern void pmac_pcibios_fixup(void);
 | 
			
		||||
extern void pmac_find_bridges(void);
 | 
			
		||||
extern unsigned long pmac_ide_get_base(int index);
 | 
			
		||||
extern void pmac_ide_init_hwif_ports(hw_regs_t *hw,
 | 
			
		||||
	unsigned long data_port, unsigned long ctrl_port, int *irq);
 | 
			
		||||
 | 
			
		||||
extern void pmac_nvram_update(void);
 | 
			
		||||
extern unsigned char pmac_nvram_read_byte(int addr);
 | 
			
		||||
extern void pmac_nvram_write_byte(int addr, unsigned char val);
 | 
			
		||||
extern int pmac_pci_enable_device_hook(struct pci_dev *dev, int initial);
 | 
			
		||||
extern void pmac_pcibios_after_init(void);
 | 
			
		||||
extern int of_show_percpuinfo(struct seq_file *m, int i);
 | 
			
		||||
 | 
			
		||||
unsigned char drive_info;
 | 
			
		||||
 | 
			
		||||
int ppc_override_l2cr = 0;
 | 
			
		||||
int ppc_override_l2cr_value;
 | 
			
		||||
int has_l2cache = 0;
 | 
			
		||||
 | 
			
		||||
static int current_root_goodness = -1;
 | 
			
		||||
 | 
			
		||||
extern int pmac_newworld;
 | 
			
		||||
 | 
			
		||||
#define DEFAULT_ROOT_DEVICE Root_SDA1	/* sda1 - slightly silly choice */
 | 
			
		||||
 | 
			
		||||
extern void zs_kgdb_hook(int tty_num);
 | 
			
		||||
static void ohare_init(void);
 | 
			
		||||
#ifdef CONFIG_BOOTX_TEXT
 | 
			
		||||
static void pmac_progress(char *s, unsigned short hex);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
sys_ctrler_t sys_ctrler = SYS_CTRLER_UNKNOWN;
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_SMP
 | 
			
		||||
extern struct smp_ops_t psurge_smp_ops;
 | 
			
		||||
extern struct smp_ops_t core99_smp_ops;
 | 
			
		||||
#endif /* CONFIG_SMP */
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
pmac_show_cpuinfo(struct seq_file *m)
 | 
			
		||||
{
 | 
			
		||||
	struct device_node *np;
 | 
			
		||||
	char *pp;
 | 
			
		||||
	int plen;
 | 
			
		||||
	int mbmodel = pmac_call_feature(PMAC_FTR_GET_MB_INFO,
 | 
			
		||||
		NULL, PMAC_MB_INFO_MODEL, 0);
 | 
			
		||||
	unsigned int mbflags = (unsigned int)pmac_call_feature(PMAC_FTR_GET_MB_INFO,
 | 
			
		||||
		NULL, PMAC_MB_INFO_FLAGS, 0);
 | 
			
		||||
	char* mbname;
 | 
			
		||||
 | 
			
		||||
	if (pmac_call_feature(PMAC_FTR_GET_MB_INFO, NULL, PMAC_MB_INFO_NAME, (int)&mbname) != 0)
 | 
			
		||||
		mbname = "Unknown";
 | 
			
		||||
 | 
			
		||||
	/* find motherboard type */
 | 
			
		||||
	seq_printf(m, "machine\t\t: ");
 | 
			
		||||
	np = find_devices("device-tree");
 | 
			
		||||
	if (np != NULL) {
 | 
			
		||||
		pp = (char *) get_property(np, "model", NULL);
 | 
			
		||||
		if (pp != NULL)
 | 
			
		||||
			seq_printf(m, "%s\n", pp);
 | 
			
		||||
		else
 | 
			
		||||
			seq_printf(m, "PowerMac\n");
 | 
			
		||||
		pp = (char *) get_property(np, "compatible", &plen);
 | 
			
		||||
		if (pp != NULL) {
 | 
			
		||||
			seq_printf(m, "motherboard\t:");
 | 
			
		||||
			while (plen > 0) {
 | 
			
		||||
				int l = strlen(pp) + 1;
 | 
			
		||||
				seq_printf(m, " %s", pp);
 | 
			
		||||
				plen -= l;
 | 
			
		||||
				pp += l;
 | 
			
		||||
			}
 | 
			
		||||
			seq_printf(m, "\n");
 | 
			
		||||
		}
 | 
			
		||||
	} else
 | 
			
		||||
		seq_printf(m, "PowerMac\n");
 | 
			
		||||
 | 
			
		||||
	/* print parsed model */
 | 
			
		||||
	seq_printf(m, "detected as\t: %d (%s)\n", mbmodel, mbname);
 | 
			
		||||
	seq_printf(m, "pmac flags\t: %08x\n", mbflags);
 | 
			
		||||
 | 
			
		||||
	/* find l2 cache info */
 | 
			
		||||
	np = find_devices("l2-cache");
 | 
			
		||||
	if (np == 0)
 | 
			
		||||
		np = find_type_devices("cache");
 | 
			
		||||
	if (np != 0) {
 | 
			
		||||
		unsigned int *ic = (unsigned int *)
 | 
			
		||||
			get_property(np, "i-cache-size", NULL);
 | 
			
		||||
		unsigned int *dc = (unsigned int *)
 | 
			
		||||
			get_property(np, "d-cache-size", NULL);
 | 
			
		||||
		seq_printf(m, "L2 cache\t:");
 | 
			
		||||
		has_l2cache = 1;
 | 
			
		||||
		if (get_property(np, "cache-unified", NULL) != 0 && dc) {
 | 
			
		||||
			seq_printf(m, " %dK unified", *dc / 1024);
 | 
			
		||||
		} else {
 | 
			
		||||
			if (ic)
 | 
			
		||||
				seq_printf(m, " %dK instruction", *ic / 1024);
 | 
			
		||||
			if (dc)
 | 
			
		||||
				seq_printf(m, "%s %dK data",
 | 
			
		||||
					   (ic? " +": ""), *dc / 1024);
 | 
			
		||||
		}
 | 
			
		||||
		pp = get_property(np, "ram-type", NULL);
 | 
			
		||||
		if (pp)
 | 
			
		||||
			seq_printf(m, " %s", pp);
 | 
			
		||||
		seq_printf(m, "\n");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* find ram info */
 | 
			
		||||
	np = find_devices("memory");
 | 
			
		||||
	if (np != 0) {
 | 
			
		||||
		int n;
 | 
			
		||||
		struct reg_property *reg = (struct reg_property *)
 | 
			
		||||
			get_property(np, "reg", &n);
 | 
			
		||||
 | 
			
		||||
		if (reg != 0) {
 | 
			
		||||
			unsigned long total = 0;
 | 
			
		||||
 | 
			
		||||
			for (n /= sizeof(struct reg_property); n > 0; --n)
 | 
			
		||||
				total += (reg++)->size;
 | 
			
		||||
			seq_printf(m, "memory\t\t: %luMB\n", total >> 20);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Checks "l2cr-value" property in the registry */
 | 
			
		||||
	np = find_devices("cpus");
 | 
			
		||||
	if (np == 0)
 | 
			
		||||
		np = find_type_devices("cpu");
 | 
			
		||||
	if (np != 0) {
 | 
			
		||||
		unsigned int *l2cr = (unsigned int *)
 | 
			
		||||
			get_property(np, "l2cr-value", NULL);
 | 
			
		||||
		if (l2cr != 0) {
 | 
			
		||||
			seq_printf(m, "l2cr override\t: 0x%x\n", *l2cr);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Indicate newworld/oldworld */
 | 
			
		||||
	seq_printf(m, "pmac-generation\t: %s\n",
 | 
			
		||||
		   pmac_newworld ? "NewWorld" : "OldWorld");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
pmac_show_percpuinfo(struct seq_file *m, int i)
 | 
			
		||||
{
 | 
			
		||||
#ifdef CONFIG_CPU_FREQ_PMAC
 | 
			
		||||
	extern unsigned int pmac_get_one_cpufreq(int i);
 | 
			
		||||
	unsigned int freq = pmac_get_one_cpufreq(i);
 | 
			
		||||
	if (freq != 0) {
 | 
			
		||||
		seq_printf(m, "clock\t\t: %dMHz\n", freq/1000);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
#endif /* CONFIG_CPU_FREQ_PMAC */
 | 
			
		||||
	return of_show_percpuinfo(m, i);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static volatile u32 *sysctrl_regs;
 | 
			
		||||
 | 
			
		||||
void __init
 | 
			
		||||
pmac_setup_arch(void)
 | 
			
		||||
{
 | 
			
		||||
	struct device_node *cpu;
 | 
			
		||||
	int *fp;
 | 
			
		||||
	unsigned long pvr;
 | 
			
		||||
 | 
			
		||||
	pvr = PVR_VER(mfspr(SPRN_PVR));
 | 
			
		||||
 | 
			
		||||
	/* Set loops_per_jiffy to a half-way reasonable value,
 | 
			
		||||
	   for use until calibrate_delay gets called. */
 | 
			
		||||
	cpu = find_type_devices("cpu");
 | 
			
		||||
	if (cpu != 0) {
 | 
			
		||||
		fp = (int *) get_property(cpu, "clock-frequency", NULL);
 | 
			
		||||
		if (fp != 0) {
 | 
			
		||||
			if (pvr == 4 || pvr >= 8)
 | 
			
		||||
				/* 604, G3, G4 etc. */
 | 
			
		||||
				loops_per_jiffy = *fp / HZ;
 | 
			
		||||
			else
 | 
			
		||||
				/* 601, 603, etc. */
 | 
			
		||||
				loops_per_jiffy = *fp / (2*HZ);
 | 
			
		||||
		} else
 | 
			
		||||
			loops_per_jiffy = 50000000 / HZ;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* this area has the CPU identification register
 | 
			
		||||
	   and some registers used by smp boards */
 | 
			
		||||
	sysctrl_regs = (volatile u32 *) ioremap(0xf8000000, 0x1000);
 | 
			
		||||
	ohare_init();
 | 
			
		||||
 | 
			
		||||
	/* Lookup PCI hosts */
 | 
			
		||||
	pmac_find_bridges();
 | 
			
		||||
 | 
			
		||||
	/* Checks "l2cr-value" property in the registry */
 | 
			
		||||
	if (cpu_has_feature(CPU_FTR_L2CR)) {
 | 
			
		||||
		struct device_node *np = find_devices("cpus");
 | 
			
		||||
		if (np == 0)
 | 
			
		||||
			np = find_type_devices("cpu");
 | 
			
		||||
		if (np != 0) {
 | 
			
		||||
			unsigned int *l2cr = (unsigned int *)
 | 
			
		||||
				get_property(np, "l2cr-value", NULL);
 | 
			
		||||
			if (l2cr != 0) {
 | 
			
		||||
				ppc_override_l2cr = 1;
 | 
			
		||||
				ppc_override_l2cr_value = *l2cr;
 | 
			
		||||
				_set_L2CR(0);
 | 
			
		||||
				_set_L2CR(ppc_override_l2cr_value);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (ppc_override_l2cr)
 | 
			
		||||
		printk(KERN_INFO "L2CR overriden (0x%x), backside cache is %s\n",
 | 
			
		||||
			ppc_override_l2cr_value, (ppc_override_l2cr_value & 0x80000000)
 | 
			
		||||
				? "enabled" : "disabled");
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_KGDB
 | 
			
		||||
	zs_kgdb_hook(0);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_ADB_CUDA
 | 
			
		||||
	find_via_cuda();
 | 
			
		||||
#else
 | 
			
		||||
	if (find_devices("via-cuda")) {
 | 
			
		||||
		printk("WARNING ! Your machine is Cuda based but your kernel\n");
 | 
			
		||||
		printk("          wasn't compiled with CONFIG_ADB_CUDA option !\n");
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef CONFIG_ADB_PMU
 | 
			
		||||
	find_via_pmu();
 | 
			
		||||
#else
 | 
			
		||||
	if (find_devices("via-pmu")) {
 | 
			
		||||
		printk("WARNING ! Your machine is PMU based but your kernel\n");
 | 
			
		||||
		printk("          wasn't compiled with CONFIG_ADB_PMU option !\n");
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef CONFIG_NVRAM
 | 
			
		||||
	pmac_nvram_init();
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef CONFIG_BLK_DEV_INITRD
 | 
			
		||||
	if (initrd_start)
 | 
			
		||||
		ROOT_DEV = Root_RAM0;
 | 
			
		||||
	else
 | 
			
		||||
#endif
 | 
			
		||||
		ROOT_DEV = DEFAULT_ROOT_DEVICE;
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_SMP
 | 
			
		||||
	/* Check for Core99 */
 | 
			
		||||
	if (find_devices("uni-n") || find_devices("u3"))
 | 
			
		||||
		ppc_md.smp_ops = &core99_smp_ops;
 | 
			
		||||
	else
 | 
			
		||||
		ppc_md.smp_ops = &psurge_smp_ops;
 | 
			
		||||
#endif /* CONFIG_SMP */
 | 
			
		||||
 | 
			
		||||
	pci_create_OF_bus_map();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void __init ohare_init(void)
 | 
			
		||||
{
 | 
			
		||||
	/*
 | 
			
		||||
	 * Turn on the L2 cache.
 | 
			
		||||
	 * We assume that we have a PSX memory controller iff
 | 
			
		||||
	 * we have an ohare I/O controller.
 | 
			
		||||
	 */
 | 
			
		||||
	if (find_devices("ohare") != NULL) {
 | 
			
		||||
		if (((sysctrl_regs[2] >> 24) & 0xf) >= 3) {
 | 
			
		||||
			if (sysctrl_regs[4] & 0x10)
 | 
			
		||||
				sysctrl_regs[4] |= 0x04000020;
 | 
			
		||||
			else
 | 
			
		||||
				sysctrl_regs[4] |= 0x04000000;
 | 
			
		||||
			if(has_l2cache)
 | 
			
		||||
				printk(KERN_INFO "Level 2 cache enabled\n");
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extern char *bootpath;
 | 
			
		||||
extern char *bootdevice;
 | 
			
		||||
void *boot_host;
 | 
			
		||||
int boot_target;
 | 
			
		||||
int boot_part;
 | 
			
		||||
extern dev_t boot_dev;
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_SCSI
 | 
			
		||||
void __init
 | 
			
		||||
note_scsi_host(struct device_node *node, void *host)
 | 
			
		||||
{
 | 
			
		||||
	int l;
 | 
			
		||||
	char *p;
 | 
			
		||||
 | 
			
		||||
	l = strlen(node->full_name);
 | 
			
		||||
	if (bootpath != NULL && bootdevice != NULL
 | 
			
		||||
	    && strncmp(node->full_name, bootdevice, l) == 0
 | 
			
		||||
	    && (bootdevice[l] == '/' || bootdevice[l] == 0)) {
 | 
			
		||||
		boot_host = host;
 | 
			
		||||
		/*
 | 
			
		||||
		 * There's a bug in OF 1.0.5.  (Why am I not surprised.)
 | 
			
		||||
		 * If you pass a path like scsi/sd@1:0 to canon, it returns
 | 
			
		||||
		 * something like /bandit@F2000000/gc@10/53c94@10000/sd@0,0
 | 
			
		||||
		 * That is, the scsi target number doesn't get preserved.
 | 
			
		||||
		 * So we pick the target number out of bootpath and use that.
 | 
			
		||||
		 */
 | 
			
		||||
		p = strstr(bootpath, "/sd@");
 | 
			
		||||
		if (p != NULL) {
 | 
			
		||||
			p += 4;
 | 
			
		||||
			boot_target = simple_strtoul(p, NULL, 10);
 | 
			
		||||
			p = strchr(p, ':');
 | 
			
		||||
			if (p != NULL)
 | 
			
		||||
				boot_part = simple_strtoul(p + 1, NULL, 10);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if defined(CONFIG_BLK_DEV_IDE) && defined(CONFIG_BLK_DEV_IDE_PMAC)
 | 
			
		||||
static dev_t __init
 | 
			
		||||
find_ide_boot(void)
 | 
			
		||||
{
 | 
			
		||||
	char *p;
 | 
			
		||||
	int n;
 | 
			
		||||
	dev_t __init pmac_find_ide_boot(char *bootdevice, int n);
 | 
			
		||||
 | 
			
		||||
	if (bootdevice == NULL)
 | 
			
		||||
		return 0;
 | 
			
		||||
	p = strrchr(bootdevice, '/');
 | 
			
		||||
	if (p == NULL)
 | 
			
		||||
		return 0;
 | 
			
		||||
	n = p - bootdevice;
 | 
			
		||||
 | 
			
		||||
	return pmac_find_ide_boot(bootdevice, n);
 | 
			
		||||
}
 | 
			
		||||
#endif /* CONFIG_BLK_DEV_IDE && CONFIG_BLK_DEV_IDE_PMAC */
 | 
			
		||||
 | 
			
		||||
static void __init
 | 
			
		||||
find_boot_device(void)
 | 
			
		||||
{
 | 
			
		||||
#if defined(CONFIG_BLK_DEV_IDE) && defined(CONFIG_BLK_DEV_IDE_PMAC)
 | 
			
		||||
	boot_dev = find_ide_boot();
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int initializing = 1;
 | 
			
		||||
/* TODO: Merge the suspend-to-ram with the common code !!!
 | 
			
		||||
 * currently, this is a stub implementation for suspend-to-disk
 | 
			
		||||
 * only
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_SOFTWARE_SUSPEND
 | 
			
		||||
 | 
			
		||||
static int pmac_pm_prepare(suspend_state_t state)
 | 
			
		||||
{
 | 
			
		||||
	printk(KERN_DEBUG "%s(%d)\n", __FUNCTION__, state);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int pmac_pm_enter(suspend_state_t state)
 | 
			
		||||
{
 | 
			
		||||
	printk(KERN_DEBUG "%s(%d)\n", __FUNCTION__, state);
 | 
			
		||||
 | 
			
		||||
	/* Giveup the lazy FPU & vec so we don't have to back them
 | 
			
		||||
	 * up from the low level code
 | 
			
		||||
	 */
 | 
			
		||||
	enable_kernel_fp();
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_ALTIVEC
 | 
			
		||||
	if (cur_cpu_spec[0]->cpu_features & CPU_FTR_ALTIVEC)
 | 
			
		||||
		enable_kernel_altivec();
 | 
			
		||||
#endif /* CONFIG_ALTIVEC */
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int pmac_pm_finish(suspend_state_t state)
 | 
			
		||||
{
 | 
			
		||||
	printk(KERN_DEBUG "%s(%d)\n", __FUNCTION__, state);
 | 
			
		||||
 | 
			
		||||
	/* Restore userland MMU context */
 | 
			
		||||
	set_context(current->active_mm->context, current->active_mm->pgd);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct pm_ops pmac_pm_ops = {
 | 
			
		||||
	.pm_disk_mode	= PM_DISK_SHUTDOWN,
 | 
			
		||||
	.prepare	= pmac_pm_prepare,
 | 
			
		||||
	.enter		= pmac_pm_enter,
 | 
			
		||||
	.finish		= pmac_pm_finish,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif /* CONFIG_SOFTWARE_SUSPEND */
 | 
			
		||||
 | 
			
		||||
static int pmac_late_init(void)
 | 
			
		||||
{
 | 
			
		||||
	initializing = 0;
 | 
			
		||||
#ifdef CONFIG_SOFTWARE_SUSPEND
 | 
			
		||||
	pm_set_ops(&pmac_pm_ops);
 | 
			
		||||
#endif /* CONFIG_SOFTWARE_SUSPEND */
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
late_initcall(pmac_late_init);
 | 
			
		||||
 | 
			
		||||
/* can't be __init - can be called whenever a disk is first accessed */
 | 
			
		||||
void
 | 
			
		||||
note_bootable_part(dev_t dev, int part, int goodness)
 | 
			
		||||
{
 | 
			
		||||
	static int found_boot = 0;
 | 
			
		||||
	char *p;
 | 
			
		||||
 | 
			
		||||
	if (!initializing)
 | 
			
		||||
		return;
 | 
			
		||||
	if ((goodness <= current_root_goodness) &&
 | 
			
		||||
	    ROOT_DEV != DEFAULT_ROOT_DEVICE)
 | 
			
		||||
		return;
 | 
			
		||||
	p = strstr(saved_command_line, "root=");
 | 
			
		||||
	if (p != NULL && (p == saved_command_line || p[-1] == ' '))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (!found_boot) {
 | 
			
		||||
		find_boot_device();
 | 
			
		||||
		found_boot = 1;
 | 
			
		||||
	}
 | 
			
		||||
	if (!boot_dev || dev == boot_dev) {
 | 
			
		||||
		ROOT_DEV = dev + part;
 | 
			
		||||
		boot_dev = 0;
 | 
			
		||||
		current_root_goodness = goodness;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
pmac_restart(char *cmd)
 | 
			
		||||
{
 | 
			
		||||
#ifdef CONFIG_ADB_CUDA
 | 
			
		||||
	struct adb_request req;
 | 
			
		||||
#endif /* CONFIG_ADB_CUDA */
 | 
			
		||||
 | 
			
		||||
	switch (sys_ctrler) {
 | 
			
		||||
#ifdef CONFIG_ADB_CUDA
 | 
			
		||||
	case SYS_CTRLER_CUDA:
 | 
			
		||||
		cuda_request(&req, NULL, 2, CUDA_PACKET,
 | 
			
		||||
			     CUDA_RESET_SYSTEM);
 | 
			
		||||
		for (;;)
 | 
			
		||||
			cuda_poll();
 | 
			
		||||
		break;
 | 
			
		||||
#endif /* CONFIG_ADB_CUDA */
 | 
			
		||||
#ifdef CONFIG_ADB_PMU
 | 
			
		||||
	case SYS_CTRLER_PMU:
 | 
			
		||||
		pmu_restart();
 | 
			
		||||
		break;
 | 
			
		||||
#endif /* CONFIG_ADB_PMU */
 | 
			
		||||
	default: ;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
pmac_power_off(void)
 | 
			
		||||
{
 | 
			
		||||
#ifdef CONFIG_ADB_CUDA
 | 
			
		||||
	struct adb_request req;
 | 
			
		||||
#endif /* CONFIG_ADB_CUDA */
 | 
			
		||||
 | 
			
		||||
	switch (sys_ctrler) {
 | 
			
		||||
#ifdef CONFIG_ADB_CUDA
 | 
			
		||||
	case SYS_CTRLER_CUDA:
 | 
			
		||||
		cuda_request(&req, NULL, 2, CUDA_PACKET,
 | 
			
		||||
			     CUDA_POWERDOWN);
 | 
			
		||||
		for (;;)
 | 
			
		||||
			cuda_poll();
 | 
			
		||||
		break;
 | 
			
		||||
#endif /* CONFIG_ADB_CUDA */
 | 
			
		||||
#ifdef CONFIG_ADB_PMU
 | 
			
		||||
	case SYS_CTRLER_PMU:
 | 
			
		||||
		pmu_shutdown();
 | 
			
		||||
		break;
 | 
			
		||||
#endif /* CONFIG_ADB_PMU */
 | 
			
		||||
	default: ;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
pmac_halt(void)
 | 
			
		||||
{
 | 
			
		||||
	pmac_power_off();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void __init
 | 
			
		||||
pmac_init(unsigned long r3, unsigned long r4, unsigned long r5,
 | 
			
		||||
	  unsigned long r6, unsigned long r7)
 | 
			
		||||
{
 | 
			
		||||
	/* isa_io_base gets set in pmac_find_bridges */
 | 
			
		||||
	isa_mem_base = PMAC_ISA_MEM_BASE;
 | 
			
		||||
	pci_dram_offset = PMAC_PCI_DRAM_OFFSET;
 | 
			
		||||
	ISA_DMA_THRESHOLD = ~0L;
 | 
			
		||||
	DMA_MODE_READ = 1;
 | 
			
		||||
	DMA_MODE_WRITE = 2;
 | 
			
		||||
 | 
			
		||||
	ppc_md.setup_arch     = pmac_setup_arch;
 | 
			
		||||
	ppc_md.show_cpuinfo   = pmac_show_cpuinfo;
 | 
			
		||||
	ppc_md.show_percpuinfo = pmac_show_percpuinfo;
 | 
			
		||||
	ppc_md.irq_canonicalize = NULL;
 | 
			
		||||
	ppc_md.init_IRQ       = pmac_pic_init;
 | 
			
		||||
	ppc_md.get_irq        = pmac_get_irq; /* Changed later on ... */
 | 
			
		||||
 | 
			
		||||
	ppc_md.pcibios_fixup  = pmac_pcibios_fixup;
 | 
			
		||||
	ppc_md.pcibios_enable_device_hook = pmac_pci_enable_device_hook;
 | 
			
		||||
	ppc_md.pcibios_after_init = pmac_pcibios_after_init;
 | 
			
		||||
	ppc_md.phys_mem_access_prot = pci_phys_mem_access_prot;
 | 
			
		||||
 | 
			
		||||
	ppc_md.restart        = pmac_restart;
 | 
			
		||||
	ppc_md.power_off      = pmac_power_off;
 | 
			
		||||
	ppc_md.halt           = pmac_halt;
 | 
			
		||||
 | 
			
		||||
	ppc_md.time_init      = pmac_time_init;
 | 
			
		||||
	ppc_md.set_rtc_time   = pmac_set_rtc_time;
 | 
			
		||||
	ppc_md.get_rtc_time   = pmac_get_rtc_time;
 | 
			
		||||
	ppc_md.calibrate_decr = pmac_calibrate_decr;
 | 
			
		||||
 | 
			
		||||
	ppc_md.feature_call   = pmac_do_feature_call;
 | 
			
		||||
 | 
			
		||||
#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
 | 
			
		||||
#ifdef CONFIG_BLK_DEV_IDE_PMAC
 | 
			
		||||
        ppc_ide_md.ide_init_hwif	= pmac_ide_init_hwif_ports;
 | 
			
		||||
        ppc_ide_md.default_io_base	= pmac_ide_get_base;
 | 
			
		||||
#endif /* CONFIG_BLK_DEV_IDE_PMAC */
 | 
			
		||||
#endif /* defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) */
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_BOOTX_TEXT
 | 
			
		||||
	ppc_md.progress = pmac_progress;
 | 
			
		||||
#endif /* CONFIG_BOOTX_TEXT */
 | 
			
		||||
 | 
			
		||||
	if (ppc_md.progress) ppc_md.progress("pmac_init(): exit", 0);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_BOOTX_TEXT
 | 
			
		||||
static void __init
 | 
			
		||||
pmac_progress(char *s, unsigned short hex)
 | 
			
		||||
{
 | 
			
		||||
	if (boot_text_mapped) {
 | 
			
		||||
		btext_drawstring(s);
 | 
			
		||||
		btext_drawchar('\n');
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
#endif /* CONFIG_BOOTX_TEXT */
 | 
			
		||||
 | 
			
		||||
static int __init
 | 
			
		||||
pmac_declare_of_platform_devices(void)
 | 
			
		||||
{
 | 
			
		||||
	struct device_node *np;
 | 
			
		||||
 | 
			
		||||
	np = find_devices("uni-n");
 | 
			
		||||
	if (np) {
 | 
			
		||||
		for (np = np->child; np != NULL; np = np->sibling)
 | 
			
		||||
			if (strncmp(np->name, "i2c", 3) == 0) {
 | 
			
		||||
				of_platform_device_create(np, "uni-n-i2c",
 | 
			
		||||
							  NULL);
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
	}
 | 
			
		||||
	np = find_devices("u3");
 | 
			
		||||
	if (np) {
 | 
			
		||||
		for (np = np->child; np != NULL; np = np->sibling)
 | 
			
		||||
			if (strncmp(np->name, "i2c", 3) == 0) {
 | 
			
		||||
				of_platform_device_create(np, "u3-i2c",
 | 
			
		||||
							  NULL);
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	np = find_devices("valkyrie");
 | 
			
		||||
	if (np)
 | 
			
		||||
		of_platform_device_create(np, "valkyrie", NULL);
 | 
			
		||||
	np = find_devices("platinum");
 | 
			
		||||
	if (np)
 | 
			
		||||
		of_platform_device_create(np, "platinum", NULL);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
device_initcall(pmac_declare_of_platform_devices);
 | 
			
		||||
							
								
								
									
										396
									
								
								arch/powerpc/platforms/powermac/pmac_sleep.S
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										396
									
								
								arch/powerpc/platforms/powermac/pmac_sleep.S
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,396 @@
 | 
			
		||||
/*
 | 
			
		||||
 * This file contains sleep low-level functions for PowerBook G3.
 | 
			
		||||
 *    Copyright (C) 1999 Benjamin Herrenschmidt (benh@kernel.crashing.org)
 | 
			
		||||
 *    and Paul Mackerras (paulus@samba.org).
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version
 | 
			
		||||
 * 2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/config.h>
 | 
			
		||||
#include <asm/processor.h>
 | 
			
		||||
#include <asm/page.h>
 | 
			
		||||
#include <asm/ppc_asm.h>
 | 
			
		||||
#include <asm/cputable.h>
 | 
			
		||||
#include <asm/cache.h>
 | 
			
		||||
#include <asm/thread_info.h>
 | 
			
		||||
#include <asm/asm-offsets.h>
 | 
			
		||||
 | 
			
		||||
#define MAGIC	0x4c617273	/* 'Lars' */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Structure for storing CPU registers on the stack.
 | 
			
		||||
 */
 | 
			
		||||
#define SL_SP		0
 | 
			
		||||
#define SL_PC		4
 | 
			
		||||
#define SL_MSR		8
 | 
			
		||||
#define SL_SDR1		0xc
 | 
			
		||||
#define SL_SPRG0	0x10	/* 4 sprg's */
 | 
			
		||||
#define SL_DBAT0	0x20
 | 
			
		||||
#define SL_IBAT0	0x28
 | 
			
		||||
#define SL_DBAT1	0x30
 | 
			
		||||
#define SL_IBAT1	0x38
 | 
			
		||||
#define SL_DBAT2	0x40
 | 
			
		||||
#define SL_IBAT2	0x48
 | 
			
		||||
#define SL_DBAT3	0x50
 | 
			
		||||
#define SL_IBAT3	0x58
 | 
			
		||||
#define SL_TB		0x60
 | 
			
		||||
#define SL_R2		0x68
 | 
			
		||||
#define SL_CR		0x6c
 | 
			
		||||
#define SL_R12		0x70	/* r12 to r31 */
 | 
			
		||||
#define SL_SIZE		(SL_R12 + 80)
 | 
			
		||||
 | 
			
		||||
	.section .text
 | 
			
		||||
	.align	5
 | 
			
		||||
 | 
			
		||||
#if defined(CONFIG_PM) || defined(CONFIG_CPU_FREQ_PMAC)
 | 
			
		||||
 | 
			
		||||
/* This gets called by via-pmu.c late during the sleep process.
 | 
			
		||||
 * The PMU was already send the sleep command and will shut us down
 | 
			
		||||
 * soon. We need to save all that is needed and setup the wakeup
 | 
			
		||||
 * vector that will be called by the ROM on wakeup
 | 
			
		||||
 */
 | 
			
		||||
_GLOBAL(low_sleep_handler)
 | 
			
		||||
#ifndef CONFIG_6xx
 | 
			
		||||
	blr
 | 
			
		||||
#else
 | 
			
		||||
	mflr	r0
 | 
			
		||||
	stw	r0,4(r1)
 | 
			
		||||
	stwu	r1,-SL_SIZE(r1)
 | 
			
		||||
	mfcr	r0
 | 
			
		||||
	stw	r0,SL_CR(r1)
 | 
			
		||||
	stw	r2,SL_R2(r1)
 | 
			
		||||
	stmw	r12,SL_R12(r1)
 | 
			
		||||
 | 
			
		||||
	/* Save MSR & SDR1 */
 | 
			
		||||
	mfmsr	r4
 | 
			
		||||
	stw	r4,SL_MSR(r1)
 | 
			
		||||
	mfsdr1	r4
 | 
			
		||||
	stw	r4,SL_SDR1(r1)
 | 
			
		||||
 | 
			
		||||
	/* Get a stable timebase and save it */
 | 
			
		||||
1:	mftbu	r4
 | 
			
		||||
	stw	r4,SL_TB(r1)
 | 
			
		||||
	mftb	r5
 | 
			
		||||
	stw	r5,SL_TB+4(r1)
 | 
			
		||||
	mftbu	r3
 | 
			
		||||
	cmpw	r3,r4
 | 
			
		||||
	bne	1b
 | 
			
		||||
 | 
			
		||||
	/* Save SPRGs */
 | 
			
		||||
	mfsprg	r4,0
 | 
			
		||||
	stw	r4,SL_SPRG0(r1)
 | 
			
		||||
	mfsprg	r4,1
 | 
			
		||||
	stw	r4,SL_SPRG0+4(r1)
 | 
			
		||||
	mfsprg	r4,2
 | 
			
		||||
	stw	r4,SL_SPRG0+8(r1)
 | 
			
		||||
	mfsprg	r4,3
 | 
			
		||||
	stw	r4,SL_SPRG0+12(r1)
 | 
			
		||||
 | 
			
		||||
	/* Save BATs */
 | 
			
		||||
	mfdbatu	r4,0
 | 
			
		||||
	stw	r4,SL_DBAT0(r1)
 | 
			
		||||
	mfdbatl	r4,0
 | 
			
		||||
	stw	r4,SL_DBAT0+4(r1)
 | 
			
		||||
	mfdbatu	r4,1
 | 
			
		||||
	stw	r4,SL_DBAT1(r1)
 | 
			
		||||
	mfdbatl	r4,1
 | 
			
		||||
	stw	r4,SL_DBAT1+4(r1)
 | 
			
		||||
	mfdbatu	r4,2
 | 
			
		||||
	stw	r4,SL_DBAT2(r1)
 | 
			
		||||
	mfdbatl	r4,2
 | 
			
		||||
	stw	r4,SL_DBAT2+4(r1)
 | 
			
		||||
	mfdbatu	r4,3
 | 
			
		||||
	stw	r4,SL_DBAT3(r1)
 | 
			
		||||
	mfdbatl	r4,3
 | 
			
		||||
	stw	r4,SL_DBAT3+4(r1)
 | 
			
		||||
	mfibatu	r4,0
 | 
			
		||||
	stw	r4,SL_IBAT0(r1)
 | 
			
		||||
	mfibatl	r4,0
 | 
			
		||||
	stw	r4,SL_IBAT0+4(r1)
 | 
			
		||||
	mfibatu	r4,1
 | 
			
		||||
	stw	r4,SL_IBAT1(r1)
 | 
			
		||||
	mfibatl	r4,1
 | 
			
		||||
	stw	r4,SL_IBAT1+4(r1)
 | 
			
		||||
	mfibatu	r4,2
 | 
			
		||||
	stw	r4,SL_IBAT2(r1)
 | 
			
		||||
	mfibatl	r4,2
 | 
			
		||||
	stw	r4,SL_IBAT2+4(r1)
 | 
			
		||||
	mfibatu	r4,3
 | 
			
		||||
	stw	r4,SL_IBAT3(r1)
 | 
			
		||||
	mfibatl	r4,3
 | 
			
		||||
	stw	r4,SL_IBAT3+4(r1)
 | 
			
		||||
 | 
			
		||||
	/* Backup various CPU config stuffs */
 | 
			
		||||
	bl	__save_cpu_setup
 | 
			
		||||
 | 
			
		||||
	/* The ROM can wake us up via 2 different vectors:
 | 
			
		||||
	 *  - On wallstreet & lombard, we must write a magic
 | 
			
		||||
	 *    value 'Lars' at address 4 and a pointer to a
 | 
			
		||||
	 *    memory location containing the PC to resume from
 | 
			
		||||
	 *    at address 0.
 | 
			
		||||
	 *  - On Core99, we must store the wakeup vector at
 | 
			
		||||
	 *    address 0x80 and eventually it's parameters
 | 
			
		||||
	 *    at address 0x84. I've have some trouble with those
 | 
			
		||||
	 *    parameters however and I no longer use them.
 | 
			
		||||
	 */
 | 
			
		||||
	lis	r5,grackle_wake_up@ha
 | 
			
		||||
	addi	r5,r5,grackle_wake_up@l
 | 
			
		||||
	tophys(r5,r5)
 | 
			
		||||
	stw	r5,SL_PC(r1)
 | 
			
		||||
	lis	r4,KERNELBASE@h
 | 
			
		||||
	tophys(r5,r1)
 | 
			
		||||
	addi	r5,r5,SL_PC
 | 
			
		||||
	lis	r6,MAGIC@ha
 | 
			
		||||
	addi	r6,r6,MAGIC@l
 | 
			
		||||
	stw	r5,0(r4)
 | 
			
		||||
	stw	r6,4(r4)
 | 
			
		||||
	/* Setup stuffs at 0x80-0x84 for Core99 */
 | 
			
		||||
	lis	r3,core99_wake_up@ha
 | 
			
		||||
	addi	r3,r3,core99_wake_up@l
 | 
			
		||||
	tophys(r3,r3)
 | 
			
		||||
	stw	r3,0x80(r4)
 | 
			
		||||
	stw	r5,0x84(r4)
 | 
			
		||||
	/* Store a pointer to our backup storage into
 | 
			
		||||
	 * a kernel global
 | 
			
		||||
	 */
 | 
			
		||||
	lis r3,sleep_storage@ha
 | 
			
		||||
	addi r3,r3,sleep_storage@l
 | 
			
		||||
	stw r5,0(r3)
 | 
			
		||||
 | 
			
		||||
	.globl	low_cpu_die
 | 
			
		||||
low_cpu_die:
 | 
			
		||||
	/* Flush & disable all caches */
 | 
			
		||||
	bl	flush_disable_caches
 | 
			
		||||
 | 
			
		||||
	/* Turn off data relocation. */
 | 
			
		||||
	mfmsr	r3		/* Save MSR in r7 */
 | 
			
		||||
	rlwinm	r3,r3,0,28,26	/* Turn off DR bit */
 | 
			
		||||
	sync
 | 
			
		||||
	mtmsr	r3
 | 
			
		||||
	isync
 | 
			
		||||
 | 
			
		||||
BEGIN_FTR_SECTION
 | 
			
		||||
	/* Flush any pending L2 data prefetches to work around HW bug */
 | 
			
		||||
	sync
 | 
			
		||||
	lis	r3,0xfff0
 | 
			
		||||
	lwz	r0,0(r3)	/* perform cache-inhibited load to ROM */
 | 
			
		||||
	sync			/* (caches are disabled at this point) */
 | 
			
		||||
END_FTR_SECTION_IFSET(CPU_FTR_SPEC7450)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Set the HID0 and MSR for sleep.
 | 
			
		||||
 */
 | 
			
		||||
	mfspr	r2,SPRN_HID0
 | 
			
		||||
	rlwinm	r2,r2,0,10,7	/* clear doze, nap */
 | 
			
		||||
	oris	r2,r2,HID0_SLEEP@h
 | 
			
		||||
	sync
 | 
			
		||||
	isync
 | 
			
		||||
	mtspr	SPRN_HID0,r2
 | 
			
		||||
	sync
 | 
			
		||||
 | 
			
		||||
/* This loop puts us back to sleep in case we have a spurrious
 | 
			
		||||
 * wakeup so that the host bridge properly stays asleep. The
 | 
			
		||||
 * CPU will be turned off, either after a known time (about 1
 | 
			
		||||
 * second) on wallstreet & lombard, or as soon as the CPU enters
 | 
			
		||||
 * SLEEP mode on core99
 | 
			
		||||
 */
 | 
			
		||||
	mfmsr	r2
 | 
			
		||||
	oris	r2,r2,MSR_POW@h
 | 
			
		||||
1:	sync
 | 
			
		||||
	mtmsr	r2
 | 
			
		||||
	isync
 | 
			
		||||
	b	1b
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Here is the resume code.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Core99 machines resume here
 | 
			
		||||
 * r4 has the physical address of SL_PC(sp) (unused)
 | 
			
		||||
 */
 | 
			
		||||
_GLOBAL(core99_wake_up)
 | 
			
		||||
	/* Make sure HID0 no longer contains any sleep bit and that data cache
 | 
			
		||||
	 * is disabled
 | 
			
		||||
	 */
 | 
			
		||||
	mfspr	r3,SPRN_HID0
 | 
			
		||||
	rlwinm	r3,r3,0,11,7		/* clear SLEEP, NAP, DOZE bits */
 | 
			
		||||
	rlwinm	3,r3,0,18,15		/* clear DCE, ICE */
 | 
			
		||||
	mtspr	SPRN_HID0,r3
 | 
			
		||||
	sync
 | 
			
		||||
	isync
 | 
			
		||||
 | 
			
		||||
	/* sanitize MSR */
 | 
			
		||||
	mfmsr	r3
 | 
			
		||||
	ori	r3,r3,MSR_EE|MSR_IP
 | 
			
		||||
	xori	r3,r3,MSR_EE|MSR_IP
 | 
			
		||||
	sync
 | 
			
		||||
	isync
 | 
			
		||||
	mtmsr	r3
 | 
			
		||||
	sync
 | 
			
		||||
	isync
 | 
			
		||||
 | 
			
		||||
	/* Recover sleep storage */
 | 
			
		||||
	lis	r3,sleep_storage@ha
 | 
			
		||||
	addi	r3,r3,sleep_storage@l
 | 
			
		||||
	tophys(r3,r3)
 | 
			
		||||
	lwz	r1,0(r3)
 | 
			
		||||
 | 
			
		||||
	/* Pass thru to older resume code ... */
 | 
			
		||||
/*
 | 
			
		||||
 * Here is the resume code for older machines.
 | 
			
		||||
 * r1 has the physical address of SL_PC(sp).
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
grackle_wake_up:
 | 
			
		||||
 | 
			
		||||
	/* Restore the kernel's segment registers before
 | 
			
		||||
	 * we do any r1 memory access as we are not sure they
 | 
			
		||||
	 * are in a sane state above the first 256Mb region
 | 
			
		||||
	 */
 | 
			
		||||
	li	r0,16		/* load up segment register values */
 | 
			
		||||
	mtctr	r0		/* for context 0 */
 | 
			
		||||
	lis	r3,0x2000	/* Ku = 1, VSID = 0 */
 | 
			
		||||
	li	r4,0
 | 
			
		||||
3:	mtsrin	r3,r4
 | 
			
		||||
	addi	r3,r3,0x111	/* increment VSID */
 | 
			
		||||
	addis	r4,r4,0x1000	/* address of next segment */
 | 
			
		||||
	bdnz	3b
 | 
			
		||||
	sync
 | 
			
		||||
	isync
 | 
			
		||||
 | 
			
		||||
	subi	r1,r1,SL_PC
 | 
			
		||||
 | 
			
		||||
	/* Restore various CPU config stuffs */
 | 
			
		||||
	bl	__restore_cpu_setup
 | 
			
		||||
 | 
			
		||||
	/* Make sure all FPRs have been initialized */
 | 
			
		||||
	bl	reloc_offset
 | 
			
		||||
	bl	__init_fpu_registers
 | 
			
		||||
 | 
			
		||||
	/* Invalidate & enable L1 cache, we don't care about
 | 
			
		||||
	 * whatever the ROM may have tried to write to memory
 | 
			
		||||
	 */
 | 
			
		||||
	bl	__inval_enable_L1
 | 
			
		||||
 | 
			
		||||
	/* Restore the BATs, and SDR1.  Then we can turn on the MMU. */
 | 
			
		||||
	lwz	r4,SL_SDR1(r1)
 | 
			
		||||
	mtsdr1	r4
 | 
			
		||||
	lwz	r4,SL_SPRG0(r1)
 | 
			
		||||
	mtsprg	0,r4
 | 
			
		||||
	lwz	r4,SL_SPRG0+4(r1)
 | 
			
		||||
	mtsprg	1,r4
 | 
			
		||||
	lwz	r4,SL_SPRG0+8(r1)
 | 
			
		||||
	mtsprg	2,r4
 | 
			
		||||
	lwz	r4,SL_SPRG0+12(r1)
 | 
			
		||||
	mtsprg	3,r4
 | 
			
		||||
 | 
			
		||||
	lwz	r4,SL_DBAT0(r1)
 | 
			
		||||
	mtdbatu	0,r4
 | 
			
		||||
	lwz	r4,SL_DBAT0+4(r1)
 | 
			
		||||
	mtdbatl	0,r4
 | 
			
		||||
	lwz	r4,SL_DBAT1(r1)
 | 
			
		||||
	mtdbatu	1,r4
 | 
			
		||||
	lwz	r4,SL_DBAT1+4(r1)
 | 
			
		||||
	mtdbatl	1,r4
 | 
			
		||||
	lwz	r4,SL_DBAT2(r1)
 | 
			
		||||
	mtdbatu	2,r4
 | 
			
		||||
	lwz	r4,SL_DBAT2+4(r1)
 | 
			
		||||
	mtdbatl	2,r4
 | 
			
		||||
	lwz	r4,SL_DBAT3(r1)
 | 
			
		||||
	mtdbatu	3,r4
 | 
			
		||||
	lwz	r4,SL_DBAT3+4(r1)
 | 
			
		||||
	mtdbatl	3,r4
 | 
			
		||||
	lwz	r4,SL_IBAT0(r1)
 | 
			
		||||
	mtibatu	0,r4
 | 
			
		||||
	lwz	r4,SL_IBAT0+4(r1)
 | 
			
		||||
	mtibatl	0,r4
 | 
			
		||||
	lwz	r4,SL_IBAT1(r1)
 | 
			
		||||
	mtibatu	1,r4
 | 
			
		||||
	lwz	r4,SL_IBAT1+4(r1)
 | 
			
		||||
	mtibatl	1,r4
 | 
			
		||||
	lwz	r4,SL_IBAT2(r1)
 | 
			
		||||
	mtibatu	2,r4
 | 
			
		||||
	lwz	r4,SL_IBAT2+4(r1)
 | 
			
		||||
	mtibatl	2,r4
 | 
			
		||||
	lwz	r4,SL_IBAT3(r1)
 | 
			
		||||
	mtibatu	3,r4
 | 
			
		||||
	lwz	r4,SL_IBAT3+4(r1)
 | 
			
		||||
	mtibatl	3,r4
 | 
			
		||||
 | 
			
		||||
BEGIN_FTR_SECTION
 | 
			
		||||
	li	r4,0
 | 
			
		||||
	mtspr	SPRN_DBAT4U,r4
 | 
			
		||||
	mtspr	SPRN_DBAT4L,r4
 | 
			
		||||
	mtspr	SPRN_DBAT5U,r4
 | 
			
		||||
	mtspr	SPRN_DBAT5L,r4
 | 
			
		||||
	mtspr	SPRN_DBAT6U,r4
 | 
			
		||||
	mtspr	SPRN_DBAT6L,r4
 | 
			
		||||
	mtspr	SPRN_DBAT7U,r4
 | 
			
		||||
	mtspr	SPRN_DBAT7L,r4
 | 
			
		||||
	mtspr	SPRN_IBAT4U,r4
 | 
			
		||||
	mtspr	SPRN_IBAT4L,r4
 | 
			
		||||
	mtspr	SPRN_IBAT5U,r4
 | 
			
		||||
	mtspr	SPRN_IBAT5L,r4
 | 
			
		||||
	mtspr	SPRN_IBAT6U,r4
 | 
			
		||||
	mtspr	SPRN_IBAT6L,r4
 | 
			
		||||
	mtspr	SPRN_IBAT7U,r4
 | 
			
		||||
	mtspr	SPRN_IBAT7L,r4
 | 
			
		||||
END_FTR_SECTION_IFSET(CPU_FTR_HAS_HIGH_BATS)
 | 
			
		||||
 | 
			
		||||
	/* Flush all TLBs */
 | 
			
		||||
	lis	r4,0x1000
 | 
			
		||||
1:	addic.	r4,r4,-0x1000
 | 
			
		||||
	tlbie	r4
 | 
			
		||||
	blt	1b
 | 
			
		||||
	sync
 | 
			
		||||
 | 
			
		||||
	/* restore the MSR and turn on the MMU */
 | 
			
		||||
	lwz	r3,SL_MSR(r1)
 | 
			
		||||
	bl	turn_on_mmu
 | 
			
		||||
 | 
			
		||||
	/* get back the stack pointer */
 | 
			
		||||
	tovirt(r1,r1)
 | 
			
		||||
 | 
			
		||||
	/* Restore TB */
 | 
			
		||||
	li	r3,0
 | 
			
		||||
	mttbl	r3
 | 
			
		||||
	lwz	r3,SL_TB(r1)
 | 
			
		||||
	lwz	r4,SL_TB+4(r1)
 | 
			
		||||
	mttbu	r3
 | 
			
		||||
	mttbl	r4
 | 
			
		||||
 | 
			
		||||
	/* Restore the callee-saved registers and return */
 | 
			
		||||
	lwz	r0,SL_CR(r1)
 | 
			
		||||
	mtcr	r0
 | 
			
		||||
	lwz	r2,SL_R2(r1)
 | 
			
		||||
	lmw	r12,SL_R12(r1)
 | 
			
		||||
	addi	r1,r1,SL_SIZE
 | 
			
		||||
	lwz	r0,4(r1)
 | 
			
		||||
	mtlr	r0
 | 
			
		||||
	blr
 | 
			
		||||
 | 
			
		||||
turn_on_mmu:
 | 
			
		||||
	mflr	r4
 | 
			
		||||
	tovirt(r4,r4)
 | 
			
		||||
	mtsrr0	r4
 | 
			
		||||
	mtsrr1	r3
 | 
			
		||||
	sync
 | 
			
		||||
	isync
 | 
			
		||||
	rfi
 | 
			
		||||
 | 
			
		||||
#endif /* defined(CONFIG_PM) || defined(CONFIG_CPU_FREQ) */
 | 
			
		||||
 | 
			
		||||
	.section .data
 | 
			
		||||
	.balign	L1_CACHE_LINE_SIZE
 | 
			
		||||
sleep_storage:
 | 
			
		||||
	.long 0
 | 
			
		||||
	.balign	L1_CACHE_LINE_SIZE, 0
 | 
			
		||||
 | 
			
		||||
#endif /* CONFIG_6xx */
 | 
			
		||||
	.section .text
 | 
			
		||||
							
								
								
									
										716
									
								
								arch/powerpc/platforms/powermac/pmac_smp.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										716
									
								
								arch/powerpc/platforms/powermac/pmac_smp.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,716 @@
 | 
			
		||||
/*
 | 
			
		||||
 * SMP support for power macintosh.
 | 
			
		||||
 *
 | 
			
		||||
 * We support both the old "powersurge" SMP architecture
 | 
			
		||||
 * and the current Core99 (G4 PowerMac) machines.
 | 
			
		||||
 *
 | 
			
		||||
 * Note that we don't support the very first rev. of
 | 
			
		||||
 * Apple/DayStar 2 CPUs board, the one with the funky
 | 
			
		||||
 * watchdog. Hopefully, none of these should be there except
 | 
			
		||||
 * maybe internally to Apple. I should probably still add some
 | 
			
		||||
 * code to detect this card though and disable SMP. --BenH.
 | 
			
		||||
 *
 | 
			
		||||
 * Support Macintosh G4 SMP by Troy Benjegerdes (hozer@drgw.net)
 | 
			
		||||
 * and Ben Herrenschmidt <benh@kernel.crashing.org>.
 | 
			
		||||
 *
 | 
			
		||||
 * Support for DayStar quad CPU cards
 | 
			
		||||
 * Copyright (C) XLR8, Inc. 1994-2000
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or
 | 
			
		||||
 *  modify it under the terms of the GNU General Public License
 | 
			
		||||
 *  as published by the Free Software Foundation; either version
 | 
			
		||||
 *  2 of the License, or (at your option) any later version.
 | 
			
		||||
 */
 | 
			
		||||
#include <linux/config.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/sched.h>
 | 
			
		||||
#include <linux/smp.h>
 | 
			
		||||
#include <linux/smp_lock.h>
 | 
			
		||||
#include <linux/interrupt.h>
 | 
			
		||||
#include <linux/kernel_stat.h>
 | 
			
		||||
#include <linux/delay.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/spinlock.h>
 | 
			
		||||
#include <linux/errno.h>
 | 
			
		||||
#include <linux/hardirq.h>
 | 
			
		||||
#include <linux/cpu.h>
 | 
			
		||||
 | 
			
		||||
#include <asm/ptrace.h>
 | 
			
		||||
#include <asm/atomic.h>
 | 
			
		||||
#include <asm/irq.h>
 | 
			
		||||
#include <asm/page.h>
 | 
			
		||||
#include <asm/pgtable.h>
 | 
			
		||||
#include <asm/sections.h>
 | 
			
		||||
#include <asm/io.h>
 | 
			
		||||
#include <asm/prom.h>
 | 
			
		||||
#include <asm/smp.h>
 | 
			
		||||
#include <asm/residual.h>
 | 
			
		||||
#include <asm/machdep.h>
 | 
			
		||||
#include <asm/pmac_feature.h>
 | 
			
		||||
#include <asm/time.h>
 | 
			
		||||
#include <asm/open_pic.h>
 | 
			
		||||
#include <asm/cacheflush.h>
 | 
			
		||||
#include <asm/keylargo.h>
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Powersurge (old powermac SMP) support.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
extern void __secondary_start_pmac_0(void);
 | 
			
		||||
 | 
			
		||||
/* Addresses for powersurge registers */
 | 
			
		||||
#define HAMMERHEAD_BASE		0xf8000000
 | 
			
		||||
#define HHEAD_CONFIG		0x90
 | 
			
		||||
#define HHEAD_SEC_INTR		0xc0
 | 
			
		||||
 | 
			
		||||
/* register for interrupting the primary processor on the powersurge */
 | 
			
		||||
/* N.B. this is actually the ethernet ROM! */
 | 
			
		||||
#define PSURGE_PRI_INTR		0xf3019000
 | 
			
		||||
 | 
			
		||||
/* register for storing the start address for the secondary processor */
 | 
			
		||||
/* N.B. this is the PCI config space address register for the 1st bridge */
 | 
			
		||||
#define PSURGE_START		0xf2800000
 | 
			
		||||
 | 
			
		||||
/* Daystar/XLR8 4-CPU card */
 | 
			
		||||
#define PSURGE_QUAD_REG_ADDR	0xf8800000
 | 
			
		||||
 | 
			
		||||
#define PSURGE_QUAD_IRQ_SET	0
 | 
			
		||||
#define PSURGE_QUAD_IRQ_CLR	1
 | 
			
		||||
#define PSURGE_QUAD_IRQ_PRIMARY	2
 | 
			
		||||
#define PSURGE_QUAD_CKSTOP_CTL	3
 | 
			
		||||
#define PSURGE_QUAD_PRIMARY_ARB	4
 | 
			
		||||
#define PSURGE_QUAD_BOARD_ID	6
 | 
			
		||||
#define PSURGE_QUAD_WHICH_CPU	7
 | 
			
		||||
#define PSURGE_QUAD_CKSTOP_RDBK	8
 | 
			
		||||
#define PSURGE_QUAD_RESET_CTL	11
 | 
			
		||||
 | 
			
		||||
#define PSURGE_QUAD_OUT(r, v)	(out_8(quad_base + ((r) << 4) + 4, (v)))
 | 
			
		||||
#define PSURGE_QUAD_IN(r)	(in_8(quad_base + ((r) << 4) + 4) & 0x0f)
 | 
			
		||||
#define PSURGE_QUAD_BIS(r, v)	(PSURGE_QUAD_OUT((r), PSURGE_QUAD_IN(r) | (v)))
 | 
			
		||||
#define PSURGE_QUAD_BIC(r, v)	(PSURGE_QUAD_OUT((r), PSURGE_QUAD_IN(r) & ~(v)))
 | 
			
		||||
 | 
			
		||||
/* virtual addresses for the above */
 | 
			
		||||
static volatile u8 __iomem *hhead_base;
 | 
			
		||||
static volatile u8 __iomem *quad_base;
 | 
			
		||||
static volatile u32 __iomem *psurge_pri_intr;
 | 
			
		||||
static volatile u8 __iomem *psurge_sec_intr;
 | 
			
		||||
static volatile u32 __iomem *psurge_start;
 | 
			
		||||
 | 
			
		||||
/* values for psurge_type */
 | 
			
		||||
#define PSURGE_NONE		-1
 | 
			
		||||
#define PSURGE_DUAL		0
 | 
			
		||||
#define PSURGE_QUAD_OKEE	1
 | 
			
		||||
#define PSURGE_QUAD_COTTON	2
 | 
			
		||||
#define PSURGE_QUAD_ICEGRASS	3
 | 
			
		||||
 | 
			
		||||
/* what sort of powersurge board we have */
 | 
			
		||||
static int psurge_type = PSURGE_NONE;
 | 
			
		||||
 | 
			
		||||
/* L2 and L3 cache settings to pass from CPU0 to CPU1 */
 | 
			
		||||
volatile static long int core99_l2_cache;
 | 
			
		||||
volatile static long int core99_l3_cache;
 | 
			
		||||
 | 
			
		||||
/* Timebase freeze GPIO */
 | 
			
		||||
static unsigned int core99_tb_gpio;
 | 
			
		||||
 | 
			
		||||
/* Sync flag for HW tb sync */
 | 
			
		||||
static volatile int sec_tb_reset = 0;
 | 
			
		||||
static unsigned int pri_tb_hi, pri_tb_lo;
 | 
			
		||||
static unsigned int pri_tb_stamp;
 | 
			
		||||
 | 
			
		||||
static void __devinit core99_init_caches(int cpu)
 | 
			
		||||
{
 | 
			
		||||
	if (!cpu_has_feature(CPU_FTR_L2CR))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (cpu == 0) {
 | 
			
		||||
		core99_l2_cache = _get_L2CR();
 | 
			
		||||
		printk("CPU0: L2CR is %lx\n", core99_l2_cache);
 | 
			
		||||
	} else {
 | 
			
		||||
		printk("CPU%d: L2CR was %lx\n", cpu, _get_L2CR());
 | 
			
		||||
		_set_L2CR(0);
 | 
			
		||||
		_set_L2CR(core99_l2_cache);
 | 
			
		||||
		printk("CPU%d: L2CR set to %lx\n", cpu, core99_l2_cache);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!cpu_has_feature(CPU_FTR_L3CR))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (cpu == 0){
 | 
			
		||||
		core99_l3_cache = _get_L3CR();
 | 
			
		||||
		printk("CPU0: L3CR is %lx\n", core99_l3_cache);
 | 
			
		||||
	} else {
 | 
			
		||||
		printk("CPU%d: L3CR was %lx\n", cpu, _get_L3CR());
 | 
			
		||||
		_set_L3CR(0);
 | 
			
		||||
		_set_L3CR(core99_l3_cache);
 | 
			
		||||
		printk("CPU%d: L3CR set to %lx\n", cpu, core99_l3_cache);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Set and clear IPIs for powersurge.
 | 
			
		||||
 */
 | 
			
		||||
static inline void psurge_set_ipi(int cpu)
 | 
			
		||||
{
 | 
			
		||||
	if (psurge_type == PSURGE_NONE)
 | 
			
		||||
		return;
 | 
			
		||||
	if (cpu == 0)
 | 
			
		||||
		in_be32(psurge_pri_intr);
 | 
			
		||||
	else if (psurge_type == PSURGE_DUAL)
 | 
			
		||||
		out_8(psurge_sec_intr, 0);
 | 
			
		||||
	else
 | 
			
		||||
		PSURGE_QUAD_OUT(PSURGE_QUAD_IRQ_SET, 1 << cpu);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void psurge_clr_ipi(int cpu)
 | 
			
		||||
{
 | 
			
		||||
	if (cpu > 0) {
 | 
			
		||||
		switch(psurge_type) {
 | 
			
		||||
		case PSURGE_DUAL:
 | 
			
		||||
			out_8(psurge_sec_intr, ~0);
 | 
			
		||||
		case PSURGE_NONE:
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			PSURGE_QUAD_OUT(PSURGE_QUAD_IRQ_CLR, 1 << cpu);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * On powersurge (old SMP powermac architecture) we don't have
 | 
			
		||||
 * separate IPIs for separate messages like openpic does.  Instead
 | 
			
		||||
 * we have a bitmap for each processor, where a 1 bit means that
 | 
			
		||||
 * the corresponding message is pending for that processor.
 | 
			
		||||
 * Ideally each cpu's entry would be in a different cache line.
 | 
			
		||||
 *  -- paulus.
 | 
			
		||||
 */
 | 
			
		||||
static unsigned long psurge_smp_message[NR_CPUS];
 | 
			
		||||
 | 
			
		||||
void psurge_smp_message_recv(struct pt_regs *regs)
 | 
			
		||||
{
 | 
			
		||||
	int cpu = smp_processor_id();
 | 
			
		||||
	int msg;
 | 
			
		||||
 | 
			
		||||
	/* clear interrupt */
 | 
			
		||||
	psurge_clr_ipi(cpu);
 | 
			
		||||
 | 
			
		||||
	if (num_online_cpus() < 2)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/* make sure there is a message there */
 | 
			
		||||
	for (msg = 0; msg < 4; msg++)
 | 
			
		||||
		if (test_and_clear_bit(msg, &psurge_smp_message[cpu]))
 | 
			
		||||
			smp_message_recv(msg, regs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
irqreturn_t psurge_primary_intr(int irq, void *d, struct pt_regs *regs)
 | 
			
		||||
{
 | 
			
		||||
	psurge_smp_message_recv(regs);
 | 
			
		||||
	return IRQ_HANDLED;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void smp_psurge_message_pass(int target, int msg, unsigned long data,
 | 
			
		||||
					   int wait)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	if (num_online_cpus() < 2)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < NR_CPUS; i++) {
 | 
			
		||||
		if (!cpu_online(i))
 | 
			
		||||
			continue;
 | 
			
		||||
		if (target == MSG_ALL
 | 
			
		||||
		    || (target == MSG_ALL_BUT_SELF && i != smp_processor_id())
 | 
			
		||||
		    || target == i) {
 | 
			
		||||
			set_bit(msg, &psurge_smp_message[i]);
 | 
			
		||||
			psurge_set_ipi(i);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Determine a quad card presence. We read the board ID register, we
 | 
			
		||||
 * force the data bus to change to something else, and we read it again.
 | 
			
		||||
 * It it's stable, then the register probably exist (ugh !)
 | 
			
		||||
 */
 | 
			
		||||
static int __init psurge_quad_probe(void)
 | 
			
		||||
{
 | 
			
		||||
	int type;
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
 | 
			
		||||
	type = PSURGE_QUAD_IN(PSURGE_QUAD_BOARD_ID);
 | 
			
		||||
	if (type < PSURGE_QUAD_OKEE || type > PSURGE_QUAD_ICEGRASS
 | 
			
		||||
	    || type != PSURGE_QUAD_IN(PSURGE_QUAD_BOARD_ID))
 | 
			
		||||
		return PSURGE_DUAL;
 | 
			
		||||
 | 
			
		||||
	/* looks OK, try a slightly more rigorous test */
 | 
			
		||||
	/* bogus is not necessarily cacheline-aligned,
 | 
			
		||||
	   though I don't suppose that really matters.  -- paulus */
 | 
			
		||||
	for (i = 0; i < 100; i++) {
 | 
			
		||||
		volatile u32 bogus[8];
 | 
			
		||||
		bogus[(0+i)%8] = 0x00000000;
 | 
			
		||||
		bogus[(1+i)%8] = 0x55555555;
 | 
			
		||||
		bogus[(2+i)%8] = 0xFFFFFFFF;
 | 
			
		||||
		bogus[(3+i)%8] = 0xAAAAAAAA;
 | 
			
		||||
		bogus[(4+i)%8] = 0x33333333;
 | 
			
		||||
		bogus[(5+i)%8] = 0xCCCCCCCC;
 | 
			
		||||
		bogus[(6+i)%8] = 0xCCCCCCCC;
 | 
			
		||||
		bogus[(7+i)%8] = 0x33333333;
 | 
			
		||||
		wmb();
 | 
			
		||||
		asm volatile("dcbf 0,%0" : : "r" (bogus) : "memory");
 | 
			
		||||
		mb();
 | 
			
		||||
		if (type != PSURGE_QUAD_IN(PSURGE_QUAD_BOARD_ID))
 | 
			
		||||
			return PSURGE_DUAL;
 | 
			
		||||
	}
 | 
			
		||||
	return type;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void __init psurge_quad_init(void)
 | 
			
		||||
{
 | 
			
		||||
	int procbits;
 | 
			
		||||
 | 
			
		||||
	if (ppc_md.progress) ppc_md.progress("psurge_quad_init", 0x351);
 | 
			
		||||
	procbits = ~PSURGE_QUAD_IN(PSURGE_QUAD_WHICH_CPU);
 | 
			
		||||
	if (psurge_type == PSURGE_QUAD_ICEGRASS)
 | 
			
		||||
		PSURGE_QUAD_BIS(PSURGE_QUAD_RESET_CTL, procbits);
 | 
			
		||||
	else
 | 
			
		||||
		PSURGE_QUAD_BIC(PSURGE_QUAD_CKSTOP_CTL, procbits);
 | 
			
		||||
	mdelay(33);
 | 
			
		||||
	out_8(psurge_sec_intr, ~0);
 | 
			
		||||
	PSURGE_QUAD_OUT(PSURGE_QUAD_IRQ_CLR, procbits);
 | 
			
		||||
	PSURGE_QUAD_BIS(PSURGE_QUAD_RESET_CTL, procbits);
 | 
			
		||||
	if (psurge_type != PSURGE_QUAD_ICEGRASS)
 | 
			
		||||
		PSURGE_QUAD_BIS(PSURGE_QUAD_CKSTOP_CTL, procbits);
 | 
			
		||||
	PSURGE_QUAD_BIC(PSURGE_QUAD_PRIMARY_ARB, procbits);
 | 
			
		||||
	mdelay(33);
 | 
			
		||||
	PSURGE_QUAD_BIC(PSURGE_QUAD_RESET_CTL, procbits);
 | 
			
		||||
	mdelay(33);
 | 
			
		||||
	PSURGE_QUAD_BIS(PSURGE_QUAD_PRIMARY_ARB, procbits);
 | 
			
		||||
	mdelay(33);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int __init smp_psurge_probe(void)
 | 
			
		||||
{
 | 
			
		||||
	int i, ncpus;
 | 
			
		||||
 | 
			
		||||
	/* We don't do SMP on the PPC601 -- paulus */
 | 
			
		||||
	if (PVR_VER(mfspr(SPRN_PVR)) == 1)
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * The powersurge cpu board can be used in the generation
 | 
			
		||||
	 * of powermacs that have a socket for an upgradeable cpu card,
 | 
			
		||||
	 * including the 7500, 8500, 9500, 9600.
 | 
			
		||||
	 * The device tree doesn't tell you if you have 2 cpus because
 | 
			
		||||
	 * OF doesn't know anything about the 2nd processor.
 | 
			
		||||
	 * Instead we look for magic bits in magic registers,
 | 
			
		||||
	 * in the hammerhead memory controller in the case of the
 | 
			
		||||
	 * dual-cpu powersurge board.  -- paulus.
 | 
			
		||||
	 */
 | 
			
		||||
	if (find_devices("hammerhead") == NULL)
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
	hhead_base = ioremap(HAMMERHEAD_BASE, 0x800);
 | 
			
		||||
	quad_base = ioremap(PSURGE_QUAD_REG_ADDR, 1024);
 | 
			
		||||
	psurge_sec_intr = hhead_base + HHEAD_SEC_INTR;
 | 
			
		||||
 | 
			
		||||
	psurge_type = psurge_quad_probe();
 | 
			
		||||
	if (psurge_type != PSURGE_DUAL) {
 | 
			
		||||
		psurge_quad_init();
 | 
			
		||||
		/* All released cards using this HW design have 4 CPUs */
 | 
			
		||||
		ncpus = 4;
 | 
			
		||||
	} else {
 | 
			
		||||
		iounmap(quad_base);
 | 
			
		||||
		if ((in_8(hhead_base + HHEAD_CONFIG) & 0x02) == 0) {
 | 
			
		||||
			/* not a dual-cpu card */
 | 
			
		||||
			iounmap(hhead_base);
 | 
			
		||||
			psurge_type = PSURGE_NONE;
 | 
			
		||||
			return 1;
 | 
			
		||||
		}
 | 
			
		||||
		ncpus = 2;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	psurge_start = ioremap(PSURGE_START, 4);
 | 
			
		||||
	psurge_pri_intr = ioremap(PSURGE_PRI_INTR, 4);
 | 
			
		||||
 | 
			
		||||
	/* this is not actually strictly necessary -- paulus. */
 | 
			
		||||
	for (i = 1; i < ncpus; ++i)
 | 
			
		||||
		smp_hw_index[i] = i;
 | 
			
		||||
 | 
			
		||||
	if (ppc_md.progress) ppc_md.progress("smp_psurge_probe - done", 0x352);
 | 
			
		||||
 | 
			
		||||
	return ncpus;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void __init smp_psurge_kick_cpu(int nr)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long start = __pa(__secondary_start_pmac_0) + nr * 8;
 | 
			
		||||
	unsigned long a;
 | 
			
		||||
 | 
			
		||||
	/* may need to flush here if secondary bats aren't setup */
 | 
			
		||||
	for (a = KERNELBASE; a < KERNELBASE + 0x800000; a += 32)
 | 
			
		||||
		asm volatile("dcbf 0,%0" : : "r" (a) : "memory");
 | 
			
		||||
	asm volatile("sync");
 | 
			
		||||
 | 
			
		||||
	if (ppc_md.progress) ppc_md.progress("smp_psurge_kick_cpu", 0x353);
 | 
			
		||||
 | 
			
		||||
	out_be32(psurge_start, start);
 | 
			
		||||
	mb();
 | 
			
		||||
 | 
			
		||||
	psurge_set_ipi(nr);
 | 
			
		||||
	udelay(10);
 | 
			
		||||
	psurge_clr_ipi(nr);
 | 
			
		||||
 | 
			
		||||
	if (ppc_md.progress) ppc_md.progress("smp_psurge_kick_cpu - done", 0x354);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * With the dual-cpu powersurge board, the decrementers and timebases
 | 
			
		||||
 * of both cpus are frozen after the secondary cpu is started up,
 | 
			
		||||
 * until we give the secondary cpu another interrupt.  This routine
 | 
			
		||||
 * uses this to get the timebases synchronized.
 | 
			
		||||
 *  -- paulus.
 | 
			
		||||
 */
 | 
			
		||||
static void __init psurge_dual_sync_tb(int cpu_nr)
 | 
			
		||||
{
 | 
			
		||||
	int t;
 | 
			
		||||
 | 
			
		||||
	set_dec(tb_ticks_per_jiffy);
 | 
			
		||||
	set_tb(0, 0);
 | 
			
		||||
	last_jiffy_stamp(cpu_nr) = 0;
 | 
			
		||||
 | 
			
		||||
	if (cpu_nr > 0) {
 | 
			
		||||
		mb();
 | 
			
		||||
		sec_tb_reset = 1;
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* wait for the secondary to have reset its TB before proceeding */
 | 
			
		||||
	for (t = 10000000; t > 0 && !sec_tb_reset; --t)
 | 
			
		||||
		;
 | 
			
		||||
 | 
			
		||||
	/* now interrupt the secondary, starting both TBs */
 | 
			
		||||
	psurge_set_ipi(1);
 | 
			
		||||
 | 
			
		||||
	smp_tb_synchronized = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct irqaction psurge_irqaction = {
 | 
			
		||||
	.handler = psurge_primary_intr,
 | 
			
		||||
	.flags = SA_INTERRUPT,
 | 
			
		||||
	.mask = CPU_MASK_NONE,
 | 
			
		||||
	.name = "primary IPI",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void __init smp_psurge_setup_cpu(int cpu_nr)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	if (cpu_nr == 0) {
 | 
			
		||||
		/* If we failed to start the second CPU, we should still
 | 
			
		||||
		 * send it an IPI to start the timebase & DEC or we might
 | 
			
		||||
		 * have them stuck.
 | 
			
		||||
		 */
 | 
			
		||||
		if (num_online_cpus() < 2) {
 | 
			
		||||
			if (psurge_type == PSURGE_DUAL)
 | 
			
		||||
				psurge_set_ipi(1);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		/* reset the entry point so if we get another intr we won't
 | 
			
		||||
		 * try to startup again */
 | 
			
		||||
		out_be32(psurge_start, 0x100);
 | 
			
		||||
		if (setup_irq(30, &psurge_irqaction))
 | 
			
		||||
			printk(KERN_ERR "Couldn't get primary IPI interrupt");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (psurge_type == PSURGE_DUAL)
 | 
			
		||||
		psurge_dual_sync_tb(cpu_nr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void __init smp_psurge_take_timebase(void)
 | 
			
		||||
{
 | 
			
		||||
	/* Dummy implementation */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void __init smp_psurge_give_timebase(void)
 | 
			
		||||
{
 | 
			
		||||
	/* Dummy implementation */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int __init smp_core99_probe(void)
 | 
			
		||||
{
 | 
			
		||||
#ifdef CONFIG_6xx
 | 
			
		||||
	extern int powersave_nap;
 | 
			
		||||
#endif
 | 
			
		||||
	struct device_node *cpus, *firstcpu;
 | 
			
		||||
	int i, ncpus = 0, boot_cpu = -1;
 | 
			
		||||
	u32 *tbprop = NULL;
 | 
			
		||||
 | 
			
		||||
	if (ppc_md.progress) ppc_md.progress("smp_core99_probe", 0x345);
 | 
			
		||||
	cpus = firstcpu = find_type_devices("cpu");
 | 
			
		||||
	while(cpus != NULL) {
 | 
			
		||||
		u32 *regprop = (u32 *)get_property(cpus, "reg", NULL);
 | 
			
		||||
		char *stateprop = (char *)get_property(cpus, "state", NULL);
 | 
			
		||||
		if (regprop != NULL && stateprop != NULL &&
 | 
			
		||||
		    !strncmp(stateprop, "running", 7))
 | 
			
		||||
			boot_cpu = *regprop;
 | 
			
		||||
		++ncpus;
 | 
			
		||||
		cpus = cpus->next;
 | 
			
		||||
	}
 | 
			
		||||
	if (boot_cpu == -1)
 | 
			
		||||
		printk(KERN_WARNING "Couldn't detect boot CPU !\n");
 | 
			
		||||
	if (boot_cpu != 0)
 | 
			
		||||
		printk(KERN_WARNING "Boot CPU is %d, unsupported setup !\n", boot_cpu);
 | 
			
		||||
 | 
			
		||||
	if (machine_is_compatible("MacRISC4")) {
 | 
			
		||||
		extern struct smp_ops_t core99_smp_ops;
 | 
			
		||||
 | 
			
		||||
		core99_smp_ops.take_timebase = smp_generic_take_timebase;
 | 
			
		||||
		core99_smp_ops.give_timebase = smp_generic_give_timebase;
 | 
			
		||||
	} else {
 | 
			
		||||
		if (firstcpu != NULL)
 | 
			
		||||
			tbprop = (u32 *)get_property(firstcpu, "timebase-enable", NULL);
 | 
			
		||||
		if (tbprop)
 | 
			
		||||
			core99_tb_gpio = *tbprop;
 | 
			
		||||
		else
 | 
			
		||||
			core99_tb_gpio = KL_GPIO_TB_ENABLE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (ncpus > 1) {
 | 
			
		||||
		mpic_request_ipis();
 | 
			
		||||
		for (i = 1; i < ncpus; ++i)
 | 
			
		||||
			smp_hw_index[i] = i;
 | 
			
		||||
#ifdef CONFIG_6xx
 | 
			
		||||
		powersave_nap = 0;
 | 
			
		||||
#endif
 | 
			
		||||
		core99_init_caches(0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ncpus;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void __devinit smp_core99_kick_cpu(int nr)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long save_vector, new_vector;
 | 
			
		||||
	unsigned long flags;
 | 
			
		||||
 | 
			
		||||
	volatile unsigned long *vector
 | 
			
		||||
		 = ((volatile unsigned long *)(KERNELBASE+0x100));
 | 
			
		||||
	if (nr < 0 || nr > 3)
 | 
			
		||||
		return;
 | 
			
		||||
	if (ppc_md.progress) ppc_md.progress("smp_core99_kick_cpu", 0x346);
 | 
			
		||||
 | 
			
		||||
	local_irq_save(flags);
 | 
			
		||||
	local_irq_disable();
 | 
			
		||||
 | 
			
		||||
	/* Save reset vector */
 | 
			
		||||
	save_vector = *vector;
 | 
			
		||||
 | 
			
		||||
	/* Setup fake reset vector that does	
 | 
			
		||||
	 *   b __secondary_start_pmac_0 + nr*8 - KERNELBASE
 | 
			
		||||
	 */
 | 
			
		||||
	new_vector = (unsigned long) __secondary_start_pmac_0 + nr * 8;
 | 
			
		||||
	*vector = 0x48000002 + new_vector - KERNELBASE;
 | 
			
		||||
 | 
			
		||||
	/* flush data cache and inval instruction cache */
 | 
			
		||||
	flush_icache_range((unsigned long) vector, (unsigned long) vector + 4);
 | 
			
		||||
 | 
			
		||||
	/* Put some life in our friend */
 | 
			
		||||
	pmac_call_feature(PMAC_FTR_RESET_CPU, NULL, nr, 0);
 | 
			
		||||
 | 
			
		||||
	/* FIXME: We wait a bit for the CPU to take the exception, I should
 | 
			
		||||
	 * instead wait for the entry code to set something for me. Well,
 | 
			
		||||
	 * ideally, all that crap will be done in prom.c and the CPU left
 | 
			
		||||
	 * in a RAM-based wait loop like CHRP.
 | 
			
		||||
	 */
 | 
			
		||||
	mdelay(1);
 | 
			
		||||
 | 
			
		||||
	/* Restore our exception vector */
 | 
			
		||||
	*vector = save_vector;
 | 
			
		||||
	flush_icache_range((unsigned long) vector, (unsigned long) vector + 4);
 | 
			
		||||
 | 
			
		||||
	local_irq_restore(flags);
 | 
			
		||||
	if (ppc_md.progress) ppc_md.progress("smp_core99_kick_cpu done", 0x347);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void __devinit smp_core99_setup_cpu(int cpu_nr)
 | 
			
		||||
{
 | 
			
		||||
	/* Setup L2/L3 */
 | 
			
		||||
	if (cpu_nr != 0)
 | 
			
		||||
		core99_init_caches(cpu_nr);
 | 
			
		||||
 | 
			
		||||
	/* Setup openpic */
 | 
			
		||||
	mpic_setup_this_cpu();
 | 
			
		||||
 | 
			
		||||
	if (cpu_nr == 0) {
 | 
			
		||||
#ifdef CONFIG_POWER4
 | 
			
		||||
		extern void g5_phy_disable_cpu1(void);
 | 
			
		||||
 | 
			
		||||
		/* If we didn't start the second CPU, we must take
 | 
			
		||||
		 * it off the bus
 | 
			
		||||
		 */
 | 
			
		||||
		if (machine_is_compatible("MacRISC4") &&
 | 
			
		||||
		    num_online_cpus() < 2)		
 | 
			
		||||
			g5_phy_disable_cpu1();
 | 
			
		||||
#endif /* CONFIG_POWER4 */
 | 
			
		||||
		if (ppc_md.progress) ppc_md.progress("core99_setup_cpu 0 done", 0x349);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* not __init, called in sleep/wakeup code */
 | 
			
		||||
void smp_core99_take_timebase(void)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long flags;
 | 
			
		||||
 | 
			
		||||
	/* tell the primary we're here */
 | 
			
		||||
	sec_tb_reset = 1;
 | 
			
		||||
	mb();
 | 
			
		||||
 | 
			
		||||
	/* wait for the primary to set pri_tb_hi/lo */
 | 
			
		||||
	while (sec_tb_reset < 2)
 | 
			
		||||
		mb();
 | 
			
		||||
 | 
			
		||||
	/* set our stuff the same as the primary */
 | 
			
		||||
	local_irq_save(flags);
 | 
			
		||||
	set_dec(1);
 | 
			
		||||
	set_tb(pri_tb_hi, pri_tb_lo);
 | 
			
		||||
	last_jiffy_stamp(smp_processor_id()) = pri_tb_stamp;
 | 
			
		||||
	mb();
 | 
			
		||||
 | 
			
		||||
	/* tell the primary we're done */
 | 
			
		||||
       	sec_tb_reset = 0;
 | 
			
		||||
	mb();
 | 
			
		||||
	local_irq_restore(flags);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* not __init, called in sleep/wakeup code */
 | 
			
		||||
void smp_core99_give_timebase(void)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long flags;
 | 
			
		||||
	unsigned int t;
 | 
			
		||||
 | 
			
		||||
	/* wait for the secondary to be in take_timebase */
 | 
			
		||||
	for (t = 100000; t > 0 && !sec_tb_reset; --t)
 | 
			
		||||
		udelay(10);
 | 
			
		||||
	if (!sec_tb_reset) {
 | 
			
		||||
		printk(KERN_WARNING "Timeout waiting sync on second CPU\n");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* freeze the timebase and read it */
 | 
			
		||||
	/* disable interrupts so the timebase is disabled for the
 | 
			
		||||
	   shortest possible time */
 | 
			
		||||
	local_irq_save(flags);
 | 
			
		||||
	pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, core99_tb_gpio, 4);
 | 
			
		||||
	pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, core99_tb_gpio, 0);
 | 
			
		||||
	mb();
 | 
			
		||||
	pri_tb_hi = get_tbu();
 | 
			
		||||
	pri_tb_lo = get_tbl();
 | 
			
		||||
	pri_tb_stamp = last_jiffy_stamp(smp_processor_id());
 | 
			
		||||
	mb();
 | 
			
		||||
 | 
			
		||||
	/* tell the secondary we're ready */
 | 
			
		||||
	sec_tb_reset = 2;
 | 
			
		||||
	mb();
 | 
			
		||||
 | 
			
		||||
	/* wait for the secondary to have taken it */
 | 
			
		||||
	for (t = 100000; t > 0 && sec_tb_reset; --t)
 | 
			
		||||
		udelay(10);
 | 
			
		||||
	if (sec_tb_reset)
 | 
			
		||||
		printk(KERN_WARNING "Timeout waiting sync(2) on second CPU\n");
 | 
			
		||||
	else
 | 
			
		||||
		smp_tb_synchronized = 1;
 | 
			
		||||
 | 
			
		||||
	/* Now, restart the timebase by leaving the GPIO to an open collector */
 | 
			
		||||
       	pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, core99_tb_gpio, 0);
 | 
			
		||||
        pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, core99_tb_gpio, 0);
 | 
			
		||||
	local_irq_restore(flags);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void smp_core99_message_pass(int target, int msg, unsigned long data, int wait)
 | 
			
		||||
{
 | 
			
		||||
	cpumask_t mask = CPU_MASK_ALL;
 | 
			
		||||
	/* make sure we're sending something that translates to an IPI */
 | 
			
		||||
	if (msg > 0x3) {
 | 
			
		||||
		printk("SMP %d: smp_message_pass: unknown msg %d\n",
 | 
			
		||||
		       smp_processor_id(), msg);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	switch (target) {
 | 
			
		||||
	case MSG_ALL:
 | 
			
		||||
		mpic_send_ipi(msg, mask);
 | 
			
		||||
		break;
 | 
			
		||||
	case MSG_ALL_BUT_SELF:
 | 
			
		||||
		cpu_clear(smp_processor_id(), mask);
 | 
			
		||||
		mpic_send_ipi(msg, mask);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		mpic_send_ipi(msg, cpumask_of_cpu(target));
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* PowerSurge-style Macs */
 | 
			
		||||
struct smp_ops_t psurge_smp_ops = {
 | 
			
		||||
	.message_pass	= smp_psurge_message_pass,
 | 
			
		||||
	.probe		= smp_psurge_probe,
 | 
			
		||||
	.kick_cpu	= smp_psurge_kick_cpu,
 | 
			
		||||
	.setup_cpu	= smp_psurge_setup_cpu,
 | 
			
		||||
	.give_timebase	= smp_psurge_give_timebase,
 | 
			
		||||
	.take_timebase	= smp_psurge_take_timebase,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Core99 Macs (dual G4s) */
 | 
			
		||||
struct smp_ops_t core99_smp_ops = {
 | 
			
		||||
	.message_pass	= smp_core99_message_pass,
 | 
			
		||||
	.probe		= smp_core99_probe,
 | 
			
		||||
	.kick_cpu	= smp_core99_kick_cpu,
 | 
			
		||||
	.setup_cpu	= smp_core99_setup_cpu,
 | 
			
		||||
	.give_timebase	= smp_core99_give_timebase,
 | 
			
		||||
	.take_timebase	= smp_core99_take_timebase,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_HOTPLUG_CPU
 | 
			
		||||
 | 
			
		||||
int __cpu_disable(void)
 | 
			
		||||
{
 | 
			
		||||
	cpu_clear(smp_processor_id(), cpu_online_map);
 | 
			
		||||
 | 
			
		||||
	/* XXX reset cpu affinity here */
 | 
			
		||||
	openpic_set_priority(0xf);
 | 
			
		||||
	asm volatile("mtdec %0" : : "r" (0x7fffffff));
 | 
			
		||||
	mb();
 | 
			
		||||
	udelay(20);
 | 
			
		||||
	asm volatile("mtdec %0" : : "r" (0x7fffffff));
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extern void low_cpu_die(void) __attribute__((noreturn)); /* in pmac_sleep.S */
 | 
			
		||||
static int cpu_dead[NR_CPUS];
 | 
			
		||||
 | 
			
		||||
void cpu_die(void)
 | 
			
		||||
{
 | 
			
		||||
	local_irq_disable();
 | 
			
		||||
	cpu_dead[smp_processor_id()] = 1;
 | 
			
		||||
	mb();
 | 
			
		||||
	low_cpu_die();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void __cpu_die(unsigned int cpu)
 | 
			
		||||
{
 | 
			
		||||
	int timeout;
 | 
			
		||||
 | 
			
		||||
	timeout = 1000;
 | 
			
		||||
	while (!cpu_dead[cpu]) {
 | 
			
		||||
		if (--timeout == 0) {
 | 
			
		||||
			printk("CPU %u refused to die!\n", cpu);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		msleep(1);
 | 
			
		||||
	}
 | 
			
		||||
	cpu_callin_map[cpu] = 0;
 | 
			
		||||
	cpu_dead[cpu] = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										291
									
								
								arch/powerpc/platforms/powermac/pmac_time.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										291
									
								
								arch/powerpc/platforms/powermac/pmac_time.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,291 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Support for periodic interrupts (100 per second) and for getting
 | 
			
		||||
 * the current time from the RTC on Power Macintoshes.
 | 
			
		||||
 *
 | 
			
		||||
 * We use the decrementer register for our periodic interrupts.
 | 
			
		||||
 *
 | 
			
		||||
 * Paul Mackerras	August 1996.
 | 
			
		||||
 * Copyright (C) 1996 Paul Mackerras.
 | 
			
		||||
 */
 | 
			
		||||
#include <linux/config.h>
 | 
			
		||||
#include <linux/errno.h>
 | 
			
		||||
#include <linux/sched.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/param.h>
 | 
			
		||||
#include <linux/string.h>
 | 
			
		||||
#include <linux/mm.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/time.h>
 | 
			
		||||
#include <linux/adb.h>
 | 
			
		||||
#include <linux/cuda.h>
 | 
			
		||||
#include <linux/pmu.h>
 | 
			
		||||
#include <linux/hardirq.h>
 | 
			
		||||
 | 
			
		||||
#include <asm/sections.h>
 | 
			
		||||
#include <asm/prom.h>
 | 
			
		||||
#include <asm/system.h>
 | 
			
		||||
#include <asm/io.h>
 | 
			
		||||
#include <asm/pgtable.h>
 | 
			
		||||
#include <asm/machdep.h>
 | 
			
		||||
#include <asm/time.h>
 | 
			
		||||
#include <asm/nvram.h>
 | 
			
		||||
 | 
			
		||||
/* Apparently the RTC stores seconds since 1 Jan 1904 */
 | 
			
		||||
#define RTC_OFFSET	2082844800
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Calibrate the decrementer frequency with the VIA timer 1.
 | 
			
		||||
 */
 | 
			
		||||
#define VIA_TIMER_FREQ_6	4700000	/* time 1 frequency * 6 */
 | 
			
		||||
 | 
			
		||||
/* VIA registers */
 | 
			
		||||
#define RS		0x200		/* skip between registers */
 | 
			
		||||
#define T1CL		(4*RS)		/* Timer 1 ctr/latch (low 8 bits) */
 | 
			
		||||
#define T1CH		(5*RS)		/* Timer 1 counter (high 8 bits) */
 | 
			
		||||
#define T1LL		(6*RS)		/* Timer 1 latch (low 8 bits) */
 | 
			
		||||
#define T1LH		(7*RS)		/* Timer 1 latch (high 8 bits) */
 | 
			
		||||
#define ACR		(11*RS)		/* Auxiliary control register */
 | 
			
		||||
#define IFR		(13*RS)		/* Interrupt flag register */
 | 
			
		||||
 | 
			
		||||
/* Bits in ACR */
 | 
			
		||||
#define T1MODE		0xc0		/* Timer 1 mode */
 | 
			
		||||
#define T1MODE_CONT	0x40		/*  continuous interrupts */
 | 
			
		||||
 | 
			
		||||
/* Bits in IFR and IER */
 | 
			
		||||
#define T1_INT		0x40		/* Timer 1 interrupt */
 | 
			
		||||
 | 
			
		||||
extern struct timezone sys_tz;
 | 
			
		||||
 | 
			
		||||
long __init
 | 
			
		||||
pmac_time_init(void)
 | 
			
		||||
{
 | 
			
		||||
#ifdef CONFIG_NVRAM
 | 
			
		||||
	s32 delta = 0;
 | 
			
		||||
	int dst;
 | 
			
		||||
	
 | 
			
		||||
	delta = ((s32)pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0x9)) << 16;
 | 
			
		||||
	delta |= ((s32)pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0xa)) << 8;
 | 
			
		||||
	delta |= pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0xb);
 | 
			
		||||
	if (delta & 0x00800000UL)
 | 
			
		||||
		delta |= 0xFF000000UL;
 | 
			
		||||
	dst = ((pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0x8) & 0x80) != 0);
 | 
			
		||||
	printk("GMT Delta read from XPRAM: %d minutes, DST: %s\n", delta/60,
 | 
			
		||||
		dst ? "on" : "off");
 | 
			
		||||
	return delta;
 | 
			
		||||
#else
 | 
			
		||||
	return 0;
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsigned long
 | 
			
		||||
pmac_get_rtc_time(void)
 | 
			
		||||
{
 | 
			
		||||
#if defined(CONFIG_ADB_CUDA) || defined(CONFIG_ADB_PMU)
 | 
			
		||||
	struct adb_request req;
 | 
			
		||||
	unsigned long now;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	/* Get the time from the RTC */
 | 
			
		||||
	switch (sys_ctrler) {
 | 
			
		||||
#ifdef CONFIG_ADB_CUDA
 | 
			
		||||
	case SYS_CTRLER_CUDA:
 | 
			
		||||
		if (cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_GET_TIME) < 0)
 | 
			
		||||
			return 0;
 | 
			
		||||
		while (!req.complete)
 | 
			
		||||
			cuda_poll();
 | 
			
		||||
		if (req.reply_len != 7)
 | 
			
		||||
			printk(KERN_ERR "pmac_get_rtc_time: got %d byte reply\n",
 | 
			
		||||
			       req.reply_len);
 | 
			
		||||
		now = (req.reply[3] << 24) + (req.reply[4] << 16)
 | 
			
		||||
			+ (req.reply[5] << 8) + req.reply[6];
 | 
			
		||||
		return now - RTC_OFFSET;
 | 
			
		||||
#endif /* CONFIG_ADB_CUDA */
 | 
			
		||||
#ifdef CONFIG_ADB_PMU
 | 
			
		||||
	case SYS_CTRLER_PMU:
 | 
			
		||||
		if (pmu_request(&req, NULL, 1, PMU_READ_RTC) < 0)
 | 
			
		||||
			return 0;
 | 
			
		||||
		while (!req.complete)
 | 
			
		||||
			pmu_poll();
 | 
			
		||||
		if (req.reply_len != 4)
 | 
			
		||||
			printk(KERN_ERR "pmac_get_rtc_time: got %d byte reply\n",
 | 
			
		||||
			       req.reply_len);
 | 
			
		||||
		now = (req.reply[0] << 24) + (req.reply[1] << 16)
 | 
			
		||||
			+ (req.reply[2] << 8) + req.reply[3];
 | 
			
		||||
		return now - RTC_OFFSET;
 | 
			
		||||
#endif /* CONFIG_ADB_PMU */
 | 
			
		||||
	default: ;
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
pmac_set_rtc_time(unsigned long nowtime)
 | 
			
		||||
{
 | 
			
		||||
#if defined(CONFIG_ADB_CUDA) || defined(CONFIG_ADB_PMU)
 | 
			
		||||
	struct adb_request req;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	nowtime += RTC_OFFSET;
 | 
			
		||||
 | 
			
		||||
	switch (sys_ctrler) {
 | 
			
		||||
#ifdef CONFIG_ADB_CUDA
 | 
			
		||||
	case SYS_CTRLER_CUDA:
 | 
			
		||||
		if (cuda_request(&req, NULL, 6, CUDA_PACKET, CUDA_SET_TIME,
 | 
			
		||||
				 nowtime >> 24, nowtime >> 16, nowtime >> 8, nowtime) < 0)
 | 
			
		||||
			return 0;
 | 
			
		||||
		while (!req.complete)
 | 
			
		||||
			cuda_poll();
 | 
			
		||||
		if ((req.reply_len != 3) && (req.reply_len != 7))
 | 
			
		||||
			printk(KERN_ERR "pmac_set_rtc_time: got %d byte reply\n",
 | 
			
		||||
			       req.reply_len);
 | 
			
		||||
		return 1;
 | 
			
		||||
#endif /* CONFIG_ADB_CUDA */
 | 
			
		||||
#ifdef CONFIG_ADB_PMU
 | 
			
		||||
	case SYS_CTRLER_PMU:
 | 
			
		||||
		if (pmu_request(&req, NULL, 5, PMU_SET_RTC,
 | 
			
		||||
				nowtime >> 24, nowtime >> 16, nowtime >> 8, nowtime) < 0)
 | 
			
		||||
			return 0;
 | 
			
		||||
		while (!req.complete)
 | 
			
		||||
			pmu_poll();
 | 
			
		||||
		if (req.reply_len != 0)
 | 
			
		||||
			printk(KERN_ERR "pmac_set_rtc_time: got %d byte reply\n",
 | 
			
		||||
			       req.reply_len);
 | 
			
		||||
		return 1;
 | 
			
		||||
#endif /* CONFIG_ADB_PMU */
 | 
			
		||||
	default:
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Calibrate the decrementer register using VIA timer 1.
 | 
			
		||||
 * This is used both on powermacs and CHRP machines.
 | 
			
		||||
 */
 | 
			
		||||
int __init
 | 
			
		||||
via_calibrate_decr(void)
 | 
			
		||||
{
 | 
			
		||||
	struct device_node *vias;
 | 
			
		||||
	volatile unsigned char __iomem *via;
 | 
			
		||||
	int count = VIA_TIMER_FREQ_6 / 100;
 | 
			
		||||
	unsigned int dstart, dend;
 | 
			
		||||
 | 
			
		||||
	vias = find_devices("via-cuda");
 | 
			
		||||
	if (vias == 0)
 | 
			
		||||
		vias = find_devices("via-pmu");
 | 
			
		||||
	if (vias == 0)
 | 
			
		||||
		vias = find_devices("via");
 | 
			
		||||
	if (vias == 0 || vias->n_addrs == 0)
 | 
			
		||||
		return 0;
 | 
			
		||||
	via = ioremap(vias->addrs[0].address, vias->addrs[0].size);
 | 
			
		||||
 | 
			
		||||
	/* set timer 1 for continuous interrupts */
 | 
			
		||||
	out_8(&via[ACR], (via[ACR] & ~T1MODE) | T1MODE_CONT);
 | 
			
		||||
	/* set the counter to a small value */
 | 
			
		||||
	out_8(&via[T1CH], 2);
 | 
			
		||||
	/* set the latch to `count' */
 | 
			
		||||
	out_8(&via[T1LL], count);
 | 
			
		||||
	out_8(&via[T1LH], count >> 8);
 | 
			
		||||
	/* wait until it hits 0 */
 | 
			
		||||
	while ((in_8(&via[IFR]) & T1_INT) == 0)
 | 
			
		||||
		;
 | 
			
		||||
	dstart = get_dec();
 | 
			
		||||
	/* clear the interrupt & wait until it hits 0 again */
 | 
			
		||||
	in_8(&via[T1CL]);
 | 
			
		||||
	while ((in_8(&via[IFR]) & T1_INT) == 0)
 | 
			
		||||
		;
 | 
			
		||||
	dend = get_dec();
 | 
			
		||||
 | 
			
		||||
	tb_ticks_per_jiffy = (dstart - dend) / (6 * (HZ/100));
 | 
			
		||||
	tb_to_us = mulhwu_scale_factor(dstart - dend, 60000);
 | 
			
		||||
 | 
			
		||||
	printk(KERN_INFO "via_calibrate_decr: ticks per jiffy = %u (%u ticks)\n",
 | 
			
		||||
	       tb_ticks_per_jiffy, dstart - dend);
 | 
			
		||||
 | 
			
		||||
	iounmap(via);
 | 
			
		||||
	
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_PM
 | 
			
		||||
/*
 | 
			
		||||
 * Reset the time after a sleep.
 | 
			
		||||
 */
 | 
			
		||||
static int
 | 
			
		||||
time_sleep_notify(struct pmu_sleep_notifier *self, int when)
 | 
			
		||||
{
 | 
			
		||||
	static unsigned long time_diff;
 | 
			
		||||
	unsigned long flags;
 | 
			
		||||
	unsigned long seq;
 | 
			
		||||
 | 
			
		||||
	switch (when) {
 | 
			
		||||
	case PBOOK_SLEEP_NOW:
 | 
			
		||||
		do {
 | 
			
		||||
			seq = read_seqbegin_irqsave(&xtime_lock, flags);
 | 
			
		||||
			time_diff = xtime.tv_sec - pmac_get_rtc_time();
 | 
			
		||||
		} while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
 | 
			
		||||
		break;
 | 
			
		||||
	case PBOOK_WAKE:
 | 
			
		||||
		write_seqlock_irqsave(&xtime_lock, flags);
 | 
			
		||||
		xtime.tv_sec = pmac_get_rtc_time() + time_diff;
 | 
			
		||||
		xtime.tv_nsec = 0;
 | 
			
		||||
		last_rtc_update = xtime.tv_sec;
 | 
			
		||||
		write_sequnlock_irqrestore(&xtime_lock, flags);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	return PBOOK_SLEEP_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct pmu_sleep_notifier time_sleep_notifier = {
 | 
			
		||||
	time_sleep_notify, SLEEP_LEVEL_MISC,
 | 
			
		||||
};
 | 
			
		||||
#endif /* CONFIG_PM */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Query the OF and get the decr frequency.
 | 
			
		||||
 * This was taken from the pmac time_init() when merging the prep/pmac
 | 
			
		||||
 * time functions.
 | 
			
		||||
 */
 | 
			
		||||
void __init
 | 
			
		||||
pmac_calibrate_decr(void)
 | 
			
		||||
{
 | 
			
		||||
	struct device_node *cpu;
 | 
			
		||||
	unsigned int freq, *fp;
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_PM
 | 
			
		||||
	pmu_register_sleep_notifier(&time_sleep_notifier);
 | 
			
		||||
#endif /* CONFIG_PM */
 | 
			
		||||
 | 
			
		||||
	/* We assume MacRISC2 machines have correct device-tree
 | 
			
		||||
	 * calibration. That's better since the VIA itself seems
 | 
			
		||||
	 * to be slightly off. --BenH
 | 
			
		||||
	 */
 | 
			
		||||
	if (!machine_is_compatible("MacRISC2") &&
 | 
			
		||||
	    !machine_is_compatible("MacRISC3") &&
 | 
			
		||||
	    !machine_is_compatible("MacRISC4"))
 | 
			
		||||
		if (via_calibrate_decr())
 | 
			
		||||
			return;
 | 
			
		||||
 | 
			
		||||
	/* Special case: QuickSilver G4s seem to have a badly calibrated
 | 
			
		||||
	 * timebase-frequency in OF, VIA is much better on these. We should
 | 
			
		||||
	 * probably implement calibration based on the KL timer on these
 | 
			
		||||
	 * machines anyway... -BenH
 | 
			
		||||
	 */
 | 
			
		||||
	if (machine_is_compatible("PowerMac3,5"))
 | 
			
		||||
		if (via_calibrate_decr())
 | 
			
		||||
			return;
 | 
			
		||||
	/*
 | 
			
		||||
	 * The cpu node should have a timebase-frequency property
 | 
			
		||||
	 * to tell us the rate at which the decrementer counts.
 | 
			
		||||
	 */
 | 
			
		||||
	cpu = find_type_devices("cpu");
 | 
			
		||||
	if (cpu == 0)
 | 
			
		||||
		panic("can't find cpu node in time_init");
 | 
			
		||||
	fp = (unsigned int *) get_property(cpu, "timebase-frequency", NULL);
 | 
			
		||||
	if (fp == 0)
 | 
			
		||||
		panic("can't get cpu timebase frequency");
 | 
			
		||||
	freq = *fp;
 | 
			
		||||
	printk("time_init: decrementer frequency = %u.%.6u MHz\n",
 | 
			
		||||
	       freq/1000000, freq%1000000);
 | 
			
		||||
	tb_ticks_per_jiffy = freq / HZ;
 | 
			
		||||
	tb_to_us = mulhwu_scale_factor(freq, 1000000);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										22
									
								
								arch/powerpc/platforms/prep/Kconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								arch/powerpc/platforms/prep/Kconfig
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
 | 
			
		||||
config PREP_RESIDUAL
 | 
			
		||||
	bool "Support for PReP Residual Data"
 | 
			
		||||
	depends on PPC_PREP
 | 
			
		||||
	help
 | 
			
		||||
	  Some PReP systems have residual data passed to the kernel by the
 | 
			
		||||
	  firmware.  This allows detection of memory size, devices present and
 | 
			
		||||
	  other useful pieces of information.  Sometimes this information is
 | 
			
		||||
	  not present or incorrect, in which case it could lead to the machine 
 | 
			
		||||
	  behaving incorrectly.  If this happens, either disable PREP_RESIDUAL
 | 
			
		||||
	  or pass the 'noresidual' option to the kernel.
 | 
			
		||||
 | 
			
		||||
	  If you are running a PReP system, say Y here, otherwise say N.
 | 
			
		||||
 | 
			
		||||
config PROC_PREPRESIDUAL
 | 
			
		||||
	bool "Support for reading of PReP Residual Data in /proc"
 | 
			
		||||
	depends on PREP_RESIDUAL && PROC_FS
 | 
			
		||||
	help
 | 
			
		||||
	  Enabling this option will create a /proc/residual file which allows
 | 
			
		||||
	  you to get at the residual data on PReP systems.  You will need a tool
 | 
			
		||||
	  (lsresidual) to parse it.  If you aren't on a PReP system, you don't
 | 
			
		||||
	  want this.
 | 
			
		||||
							
								
								
									
										47
									
								
								arch/powerpc/platforms/pseries/Kconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								arch/powerpc/platforms/pseries/Kconfig
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
			
		||||
 | 
			
		||||
config PPC_SPLPAR
 | 
			
		||||
	depends on PPC_PSERIES
 | 
			
		||||
	bool "Support for shared-processor logical partitions"
 | 
			
		||||
	default n
 | 
			
		||||
	help
 | 
			
		||||
	  Enabling this option will make the kernel run more efficiently
 | 
			
		||||
	  on logically-partitioned pSeries systems which use shared
 | 
			
		||||
	  processors, that is, which share physical processors between
 | 
			
		||||
	  two or more partitions.
 | 
			
		||||
 | 
			
		||||
config HMT
 | 
			
		||||
	bool "Hardware multithreading"
 | 
			
		||||
	depends on SMP && PPC_PSERIES && BROKEN
 | 
			
		||||
	help
 | 
			
		||||
	  This option enables hardware multithreading on RS64 cpus.
 | 
			
		||||
	  pSeries systems p620 and p660 have such a cpu type.
 | 
			
		||||
 | 
			
		||||
config EEH
 | 
			
		||||
	bool "PCI Extended Error Handling (EEH)" if EMBEDDED
 | 
			
		||||
	depends on PPC_PSERIES
 | 
			
		||||
	default y if !EMBEDDED
 | 
			
		||||
 | 
			
		||||
config PPC_RTAS
 | 
			
		||||
	bool
 | 
			
		||||
	depends on PPC_PSERIES || PPC_BPA
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config RTAS_PROC
 | 
			
		||||
	bool "Proc interface to RTAS"
 | 
			
		||||
	depends on PPC_RTAS
 | 
			
		||||
	default y
 | 
			
		||||
 | 
			
		||||
config RTAS_FLASH
 | 
			
		||||
	tristate "Firmware flash interface"
 | 
			
		||||
	depends on PPC64 && RTAS_PROC
 | 
			
		||||
 | 
			
		||||
config SCANLOG
 | 
			
		||||
	tristate "Scanlog dump interface"
 | 
			
		||||
	depends on RTAS_PROC && PPC_PSERIES
 | 
			
		||||
 | 
			
		||||
config LPARCFG
 | 
			
		||||
	tristate "LPAR Configuration Data"
 | 
			
		||||
	depends on PPC_PSERIES || PPC_ISERIES
 | 
			
		||||
	help
 | 
			
		||||
	Provide system capacity information via human readable
 | 
			
		||||
	<key word>=<value> pairs through a /proc/ppc64/lparcfg interface.
 | 
			
		||||
							
								
								
									
										1
									
								
								arch/powerpc/sysdev/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								arch/powerpc/sysdev/Makefile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
obj-$(CONFIG_MPIC)	+= mpic.o
 | 
			
		||||
							
								
								
									
										904
									
								
								arch/powerpc/sysdev/mpic.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										904
									
								
								arch/powerpc/sysdev/mpic.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,904 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  arch/powerpc/kernel/mpic.c
 | 
			
		||||
 *
 | 
			
		||||
 *  Driver for interrupt controllers following the OpenPIC standard, the
 | 
			
		||||
 *  common implementation beeing IBM's MPIC. This driver also can deal
 | 
			
		||||
 *  with various broken implementations of this HW.
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2004 Benjamin Herrenschmidt, IBM Corp.
 | 
			
		||||
 *
 | 
			
		||||
 *  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.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#undef DEBUG
 | 
			
		||||
 | 
			
		||||
#include <linux/config.h>
 | 
			
		||||
#include <linux/types.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/irq.h>
 | 
			
		||||
#include <linux/smp.h>
 | 
			
		||||
#include <linux/interrupt.h>
 | 
			
		||||
#include <linux/bootmem.h>
 | 
			
		||||
#include <linux/spinlock.h>
 | 
			
		||||
#include <linux/pci.h>
 | 
			
		||||
 | 
			
		||||
#include <asm/ptrace.h>
 | 
			
		||||
#include <asm/signal.h>
 | 
			
		||||
#include <asm/io.h>
 | 
			
		||||
#include <asm/pgtable.h>
 | 
			
		||||
#include <asm/irq.h>
 | 
			
		||||
#include <asm/machdep.h>
 | 
			
		||||
#include <asm/mpic.h>
 | 
			
		||||
#include <asm/smp.h>
 | 
			
		||||
 | 
			
		||||
#ifdef DEBUG
 | 
			
		||||
#define DBG(fmt...) printk(fmt)
 | 
			
		||||
#else
 | 
			
		||||
#define DBG(fmt...)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static struct mpic *mpics;
 | 
			
		||||
static struct mpic *mpic_primary;
 | 
			
		||||
static DEFINE_SPINLOCK(mpic_lock);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Register accessor functions
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static inline u32 _mpic_read(unsigned int be, volatile u32 __iomem *base,
 | 
			
		||||
			    unsigned int reg)
 | 
			
		||||
{
 | 
			
		||||
	if (be)
 | 
			
		||||
		return in_be32(base + (reg >> 2));
 | 
			
		||||
	else
 | 
			
		||||
		return in_le32(base + (reg >> 2));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void _mpic_write(unsigned int be, volatile u32 __iomem *base,
 | 
			
		||||
			      unsigned int reg, u32 value)
 | 
			
		||||
{
 | 
			
		||||
	if (be)
 | 
			
		||||
		out_be32(base + (reg >> 2), value);
 | 
			
		||||
	else
 | 
			
		||||
		out_le32(base + (reg >> 2), value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline u32 _mpic_ipi_read(struct mpic *mpic, unsigned int ipi)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int be = (mpic->flags & MPIC_BIG_ENDIAN) != 0;
 | 
			
		||||
	unsigned int offset = MPIC_GREG_IPI_VECTOR_PRI_0 + (ipi * 0x10);
 | 
			
		||||
 | 
			
		||||
	if (mpic->flags & MPIC_BROKEN_IPI)
 | 
			
		||||
		be = !be;
 | 
			
		||||
	return _mpic_read(be, mpic->gregs, offset);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void _mpic_ipi_write(struct mpic *mpic, unsigned int ipi, u32 value)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int offset = MPIC_GREG_IPI_VECTOR_PRI_0 + (ipi * 0x10);
 | 
			
		||||
 | 
			
		||||
	_mpic_write(mpic->flags & MPIC_BIG_ENDIAN, mpic->gregs, offset, value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline u32 _mpic_cpu_read(struct mpic *mpic, unsigned int reg)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int cpu = 0;
 | 
			
		||||
 | 
			
		||||
	if (mpic->flags & MPIC_PRIMARY)
 | 
			
		||||
		cpu = hard_smp_processor_id();
 | 
			
		||||
 | 
			
		||||
	return _mpic_read(mpic->flags & MPIC_BIG_ENDIAN, mpic->cpuregs[cpu], reg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void _mpic_cpu_write(struct mpic *mpic, unsigned int reg, u32 value)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int cpu = 0;
 | 
			
		||||
 | 
			
		||||
	if (mpic->flags & MPIC_PRIMARY)
 | 
			
		||||
		cpu = hard_smp_processor_id();
 | 
			
		||||
 | 
			
		||||
	_mpic_write(mpic->flags & MPIC_BIG_ENDIAN, mpic->cpuregs[cpu], reg, value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline u32 _mpic_irq_read(struct mpic *mpic, unsigned int src_no, unsigned int reg)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int	isu = src_no >> mpic->isu_shift;
 | 
			
		||||
	unsigned int	idx = src_no & mpic->isu_mask;
 | 
			
		||||
 | 
			
		||||
	return _mpic_read(mpic->flags & MPIC_BIG_ENDIAN, mpic->isus[isu],
 | 
			
		||||
			  reg + (idx * MPIC_IRQ_STRIDE));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void _mpic_irq_write(struct mpic *mpic, unsigned int src_no,
 | 
			
		||||
				   unsigned int reg, u32 value)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int	isu = src_no >> mpic->isu_shift;
 | 
			
		||||
	unsigned int	idx = src_no & mpic->isu_mask;
 | 
			
		||||
 | 
			
		||||
	_mpic_write(mpic->flags & MPIC_BIG_ENDIAN, mpic->isus[isu],
 | 
			
		||||
		    reg + (idx * MPIC_IRQ_STRIDE), value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define mpic_read(b,r)		_mpic_read(mpic->flags & MPIC_BIG_ENDIAN,(b),(r))
 | 
			
		||||
#define mpic_write(b,r,v)	_mpic_write(mpic->flags & MPIC_BIG_ENDIAN,(b),(r),(v))
 | 
			
		||||
#define mpic_ipi_read(i)	_mpic_ipi_read(mpic,(i))
 | 
			
		||||
#define mpic_ipi_write(i,v)	_mpic_ipi_write(mpic,(i),(v))
 | 
			
		||||
#define mpic_cpu_read(i)	_mpic_cpu_read(mpic,(i))
 | 
			
		||||
#define mpic_cpu_write(i,v)	_mpic_cpu_write(mpic,(i),(v))
 | 
			
		||||
#define mpic_irq_read(s,r)	_mpic_irq_read(mpic,(s),(r))
 | 
			
		||||
#define mpic_irq_write(s,r,v)	_mpic_irq_write(mpic,(s),(r),(v))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Low level utility functions
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Check if we have one of those nice broken MPICs with a flipped endian on
 | 
			
		||||
 * reads from IPI registers
 | 
			
		||||
 */
 | 
			
		||||
static void __init mpic_test_broken_ipi(struct mpic *mpic)
 | 
			
		||||
{
 | 
			
		||||
	u32 r;
 | 
			
		||||
 | 
			
		||||
	mpic_write(mpic->gregs, MPIC_GREG_IPI_VECTOR_PRI_0, MPIC_VECPRI_MASK);
 | 
			
		||||
	r = mpic_read(mpic->gregs, MPIC_GREG_IPI_VECTOR_PRI_0);
 | 
			
		||||
 | 
			
		||||
	if (r == le32_to_cpu(MPIC_VECPRI_MASK)) {
 | 
			
		||||
		printk(KERN_INFO "mpic: Detected reversed IPI registers\n");
 | 
			
		||||
		mpic->flags |= MPIC_BROKEN_IPI;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_MPIC_BROKEN_U3
 | 
			
		||||
 | 
			
		||||
/* Test if an interrupt is sourced from HyperTransport (used on broken U3s)
 | 
			
		||||
 * to force the edge setting on the MPIC and do the ack workaround.
 | 
			
		||||
 */
 | 
			
		||||
static inline int mpic_is_ht_interrupt(struct mpic *mpic, unsigned int source_no)
 | 
			
		||||
{
 | 
			
		||||
	if (source_no >= 128 || !mpic->fixups)
 | 
			
		||||
		return 0;
 | 
			
		||||
	return mpic->fixups[source_no].base != NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void mpic_apic_end_irq(struct mpic *mpic, unsigned int source_no)
 | 
			
		||||
{
 | 
			
		||||
	struct mpic_irq_fixup *fixup = &mpic->fixups[source_no];
 | 
			
		||||
	u32 tmp;
 | 
			
		||||
 | 
			
		||||
	spin_lock(&mpic->fixup_lock);
 | 
			
		||||
	writeb(0x11 + 2 * fixup->irq, fixup->base);
 | 
			
		||||
	tmp = readl(fixup->base + 2);
 | 
			
		||||
	writel(tmp | 0x80000000ul, fixup->base + 2);
 | 
			
		||||
	/* config writes shouldn't be posted but let's be safe ... */
 | 
			
		||||
	(void)readl(fixup->base + 2);
 | 
			
		||||
	spin_unlock(&mpic->fixup_lock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void __init mpic_amd8111_read_irq(struct mpic *mpic, u8 __iomem *devbase)
 | 
			
		||||
{
 | 
			
		||||
	int i, irq;
 | 
			
		||||
	u32 tmp;
 | 
			
		||||
 | 
			
		||||
	printk(KERN_INFO "mpic:    - Workarounds on AMD 8111 @ %p\n", devbase);
 | 
			
		||||
 | 
			
		||||
	for (i=0; i < 24; i++) {
 | 
			
		||||
		writeb(0x10 + 2*i, devbase + 0xf2);
 | 
			
		||||
		tmp = readl(devbase + 0xf4);
 | 
			
		||||
		if ((tmp & 0x1) || !(tmp & 0x20))
 | 
			
		||||
			continue;
 | 
			
		||||
		irq = (tmp >> 16) & 0xff;
 | 
			
		||||
		mpic->fixups[irq].irq = i;
 | 
			
		||||
		mpic->fixups[irq].base = devbase + 0xf2;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
static void __init mpic_amd8131_read_irq(struct mpic *mpic, u8 __iomem *devbase)
 | 
			
		||||
{
 | 
			
		||||
	int i, irq;
 | 
			
		||||
	u32 tmp;
 | 
			
		||||
 | 
			
		||||
	printk(KERN_INFO "mpic:    - Workarounds on AMD 8131 @ %p\n", devbase);
 | 
			
		||||
 | 
			
		||||
	for (i=0; i < 4; i++) {
 | 
			
		||||
		writeb(0x10 + 2*i, devbase + 0xba);
 | 
			
		||||
		tmp = readl(devbase + 0xbc);
 | 
			
		||||
		if ((tmp & 0x1) || !(tmp & 0x20))
 | 
			
		||||
			continue;
 | 
			
		||||
		irq = (tmp >> 16) & 0xff;
 | 
			
		||||
		mpic->fixups[irq].irq = i;
 | 
			
		||||
		mpic->fixups[irq].base = devbase + 0xba;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
static void __init mpic_scan_ioapics(struct mpic *mpic)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int devfn;
 | 
			
		||||
	u8 __iomem *cfgspace;
 | 
			
		||||
 | 
			
		||||
	printk(KERN_INFO "mpic: Setting up IO-APICs workarounds for U3\n");
 | 
			
		||||
 | 
			
		||||
	/* Allocate fixups array */
 | 
			
		||||
	mpic->fixups = alloc_bootmem(128 * sizeof(struct mpic_irq_fixup));
 | 
			
		||||
	BUG_ON(mpic->fixups == NULL);
 | 
			
		||||
	memset(mpic->fixups, 0, 128 * sizeof(struct mpic_irq_fixup));
 | 
			
		||||
 | 
			
		||||
	/* Init spinlock */
 | 
			
		||||
	spin_lock_init(&mpic->fixup_lock);
 | 
			
		||||
 | 
			
		||||
	/* Map u3 config space. We assume all IO-APICs are on the primary bus
 | 
			
		||||
	 * and slot will never be above "0xf" so we only need to map 32k
 | 
			
		||||
	 */
 | 
			
		||||
	cfgspace = (unsigned char __iomem *)ioremap(0xf2000000, 0x8000);
 | 
			
		||||
	BUG_ON(cfgspace == NULL);
 | 
			
		||||
 | 
			
		||||
	/* Now we scan all slots. We do a very quick scan, we read the header type,
 | 
			
		||||
	 * vendor ID and device ID only, that's plenty enough
 | 
			
		||||
	 */
 | 
			
		||||
	for (devfn = 0; devfn < PCI_DEVFN(0x10,0); devfn ++) {
 | 
			
		||||
		u8 __iomem *devbase = cfgspace + (devfn << 8);
 | 
			
		||||
		u8 hdr_type = readb(devbase + PCI_HEADER_TYPE);
 | 
			
		||||
		u32 l = readl(devbase + PCI_VENDOR_ID);
 | 
			
		||||
		u16 vendor_id, device_id;
 | 
			
		||||
		int multifunc = 0;
 | 
			
		||||
 | 
			
		||||
		DBG("devfn %x, l: %x\n", devfn, l);
 | 
			
		||||
 | 
			
		||||
		/* If no device, skip */
 | 
			
		||||
		if (l == 0xffffffff || l == 0x00000000 ||
 | 
			
		||||
		    l == 0x0000ffff || l == 0xffff0000)
 | 
			
		||||
			goto next;
 | 
			
		||||
 | 
			
		||||
		/* Check if it's a multifunction device (only really used
 | 
			
		||||
		 * to function 0 though
 | 
			
		||||
		 */
 | 
			
		||||
		multifunc = !!(hdr_type & 0x80);
 | 
			
		||||
		vendor_id = l & 0xffff;
 | 
			
		||||
		device_id = (l >> 16) & 0xffff;
 | 
			
		||||
 | 
			
		||||
		/* If a known device, go to fixup setup code */
 | 
			
		||||
		if (vendor_id == PCI_VENDOR_ID_AMD && device_id == 0x7460)
 | 
			
		||||
			mpic_amd8111_read_irq(mpic, devbase);
 | 
			
		||||
		if (vendor_id == PCI_VENDOR_ID_AMD && device_id == 0x7450)
 | 
			
		||||
			mpic_amd8131_read_irq(mpic, devbase);
 | 
			
		||||
	next:
 | 
			
		||||
		/* next device, if function 0 */
 | 
			
		||||
		if ((PCI_FUNC(devfn) == 0) && !multifunc)
 | 
			
		||||
			devfn += 7;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif /* CONFIG_MPIC_BROKEN_U3 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Find an mpic associated with a given linux interrupt */
 | 
			
		||||
static struct mpic *mpic_find(unsigned int irq, unsigned int *is_ipi)
 | 
			
		||||
{
 | 
			
		||||
	struct mpic *mpic = mpics;
 | 
			
		||||
 | 
			
		||||
	while(mpic) {
 | 
			
		||||
		/* search IPIs first since they may override the main interrupts */
 | 
			
		||||
		if (irq >= mpic->ipi_offset && irq < (mpic->ipi_offset + 4)) {
 | 
			
		||||
			if (is_ipi)
 | 
			
		||||
				*is_ipi = 1;
 | 
			
		||||
			return mpic;
 | 
			
		||||
		}
 | 
			
		||||
		if (irq >= mpic->irq_offset &&
 | 
			
		||||
		    irq < (mpic->irq_offset + mpic->irq_count)) {
 | 
			
		||||
			if (is_ipi)
 | 
			
		||||
				*is_ipi = 0;
 | 
			
		||||
			return mpic;
 | 
			
		||||
		}
 | 
			
		||||
		mpic = mpic -> next;
 | 
			
		||||
	}
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Convert a cpu mask from logical to physical cpu numbers. */
 | 
			
		||||
static inline u32 mpic_physmask(u32 cpumask)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	u32 mask = 0;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < NR_CPUS; ++i, cpumask >>= 1)
 | 
			
		||||
		mask |= (cpumask & 1) << get_hard_smp_processor_id(i);
 | 
			
		||||
	return mask;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_SMP
 | 
			
		||||
/* Get the mpic structure from the IPI number */
 | 
			
		||||
static inline struct mpic * mpic_from_ipi(unsigned int ipi)
 | 
			
		||||
{
 | 
			
		||||
	return container_of(irq_desc[ipi].handler, struct mpic, hc_ipi);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Get the mpic structure from the irq number */
 | 
			
		||||
static inline struct mpic * mpic_from_irq(unsigned int irq)
 | 
			
		||||
{
 | 
			
		||||
	return container_of(irq_desc[irq].handler, struct mpic, hc_irq);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Send an EOI */
 | 
			
		||||
static inline void mpic_eoi(struct mpic *mpic)
 | 
			
		||||
{
 | 
			
		||||
	mpic_cpu_write(MPIC_CPU_EOI, 0);
 | 
			
		||||
	(void)mpic_cpu_read(MPIC_CPU_WHOAMI);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_SMP
 | 
			
		||||
static irqreturn_t mpic_ipi_action(int irq, void *dev_id, struct pt_regs *regs)
 | 
			
		||||
{
 | 
			
		||||
	struct mpic *mpic = dev_id;
 | 
			
		||||
 | 
			
		||||
	smp_message_recv(irq - mpic->ipi_offset, regs);
 | 
			
		||||
	return IRQ_HANDLED;
 | 
			
		||||
}
 | 
			
		||||
#endif /* CONFIG_SMP */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Linux descriptor level callbacks
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void mpic_enable_irq(unsigned int irq)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int loops = 100000;
 | 
			
		||||
	struct mpic *mpic = mpic_from_irq(irq);
 | 
			
		||||
	unsigned int src = irq - mpic->irq_offset;
 | 
			
		||||
 | 
			
		||||
	DBG("%s: enable_irq: %d (src %d)\n", mpic->name, irq, src);
 | 
			
		||||
 | 
			
		||||
	mpic_irq_write(src, MPIC_IRQ_VECTOR_PRI,
 | 
			
		||||
		       mpic_irq_read(src, MPIC_IRQ_VECTOR_PRI) & ~MPIC_VECPRI_MASK);
 | 
			
		||||
 | 
			
		||||
	/* make sure mask gets to controller before we return to user */
 | 
			
		||||
	do {
 | 
			
		||||
		if (!loops--) {
 | 
			
		||||
			printk(KERN_ERR "mpic_enable_irq timeout\n");
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	} while(mpic_irq_read(src, MPIC_IRQ_VECTOR_PRI) & MPIC_VECPRI_MASK);	
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mpic_disable_irq(unsigned int irq)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int loops = 100000;
 | 
			
		||||
	struct mpic *mpic = mpic_from_irq(irq);
 | 
			
		||||
	unsigned int src = irq - mpic->irq_offset;
 | 
			
		||||
 | 
			
		||||
	DBG("%s: disable_irq: %d (src %d)\n", mpic->name, irq, src);
 | 
			
		||||
 | 
			
		||||
	mpic_irq_write(src, MPIC_IRQ_VECTOR_PRI,
 | 
			
		||||
		       mpic_irq_read(src, MPIC_IRQ_VECTOR_PRI) | MPIC_VECPRI_MASK);
 | 
			
		||||
 | 
			
		||||
	/* make sure mask gets to controller before we return to user */
 | 
			
		||||
	do {
 | 
			
		||||
		if (!loops--) {
 | 
			
		||||
			printk(KERN_ERR "mpic_enable_irq timeout\n");
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	} while(!(mpic_irq_read(src, MPIC_IRQ_VECTOR_PRI) & MPIC_VECPRI_MASK));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mpic_end_irq(unsigned int irq)
 | 
			
		||||
{
 | 
			
		||||
	struct mpic *mpic = mpic_from_irq(irq);
 | 
			
		||||
 | 
			
		||||
	DBG("%s: end_irq: %d\n", mpic->name, irq);
 | 
			
		||||
 | 
			
		||||
	/* We always EOI on end_irq() even for edge interrupts since that
 | 
			
		||||
	 * should only lower the priority, the MPIC should have properly
 | 
			
		||||
	 * latched another edge interrupt coming in anyway
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_MPIC_BROKEN_U3
 | 
			
		||||
	if (mpic->flags & MPIC_BROKEN_U3) {
 | 
			
		||||
		unsigned int src = irq - mpic->irq_offset;
 | 
			
		||||
		if (mpic_is_ht_interrupt(mpic, src))
 | 
			
		||||
			mpic_apic_end_irq(mpic, src);
 | 
			
		||||
	}
 | 
			
		||||
#endif /* CONFIG_MPIC_BROKEN_U3 */
 | 
			
		||||
 | 
			
		||||
	mpic_eoi(mpic);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_SMP
 | 
			
		||||
 | 
			
		||||
static void mpic_enable_ipi(unsigned int irq)
 | 
			
		||||
{
 | 
			
		||||
	struct mpic *mpic = mpic_from_ipi(irq);
 | 
			
		||||
	unsigned int src = irq - mpic->ipi_offset;
 | 
			
		||||
 | 
			
		||||
	DBG("%s: enable_ipi: %d (ipi %d)\n", mpic->name, irq, src);
 | 
			
		||||
	mpic_ipi_write(src, mpic_ipi_read(src) & ~MPIC_VECPRI_MASK);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mpic_disable_ipi(unsigned int irq)
 | 
			
		||||
{
 | 
			
		||||
	/* NEVER disable an IPI... that's just plain wrong! */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mpic_end_ipi(unsigned int irq)
 | 
			
		||||
{
 | 
			
		||||
	struct mpic *mpic = mpic_from_ipi(irq);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * IPIs are marked IRQ_PER_CPU. This has the side effect of
 | 
			
		||||
	 * preventing the IRQ_PENDING/IRQ_INPROGRESS logic from
 | 
			
		||||
	 * applying to them. We EOI them late to avoid re-entering.
 | 
			
		||||
	 * We mark IPI's with SA_INTERRUPT as they must run with
 | 
			
		||||
	 * irqs disabled.
 | 
			
		||||
	 */
 | 
			
		||||
	mpic_eoi(mpic);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif /* CONFIG_SMP */
 | 
			
		||||
 | 
			
		||||
static void mpic_set_affinity(unsigned int irq, cpumask_t cpumask)
 | 
			
		||||
{
 | 
			
		||||
	struct mpic *mpic = mpic_from_irq(irq);
 | 
			
		||||
 | 
			
		||||
	cpumask_t tmp;
 | 
			
		||||
 | 
			
		||||
	cpus_and(tmp, cpumask, cpu_online_map);
 | 
			
		||||
 | 
			
		||||
	mpic_irq_write(irq - mpic->irq_offset, MPIC_IRQ_DESTINATION,
 | 
			
		||||
		       mpic_physmask(cpus_addr(tmp)[0]));	
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Exported functions
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
struct mpic * __init mpic_alloc(unsigned long phys_addr,
 | 
			
		||||
				unsigned int flags,
 | 
			
		||||
				unsigned int isu_size,
 | 
			
		||||
				unsigned int irq_offset,
 | 
			
		||||
				unsigned int irq_count,
 | 
			
		||||
				unsigned int ipi_offset,
 | 
			
		||||
				unsigned char *senses,
 | 
			
		||||
				unsigned int senses_count,
 | 
			
		||||
				const char *name)
 | 
			
		||||
{
 | 
			
		||||
	struct mpic	*mpic;
 | 
			
		||||
	u32		reg;
 | 
			
		||||
	const char	*vers;
 | 
			
		||||
	int		i;
 | 
			
		||||
 | 
			
		||||
	mpic = alloc_bootmem(sizeof(struct mpic));
 | 
			
		||||
	if (mpic == NULL)
 | 
			
		||||
		return NULL;
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
	memset(mpic, 0, sizeof(struct mpic));
 | 
			
		||||
	mpic->name = name;
 | 
			
		||||
 | 
			
		||||
	mpic->hc_irq.typename = name;
 | 
			
		||||
	mpic->hc_irq.enable = mpic_enable_irq;
 | 
			
		||||
	mpic->hc_irq.disable = mpic_disable_irq;
 | 
			
		||||
	mpic->hc_irq.end = mpic_end_irq;
 | 
			
		||||
	if (flags & MPIC_PRIMARY)
 | 
			
		||||
		mpic->hc_irq.set_affinity = mpic_set_affinity;
 | 
			
		||||
#ifdef CONFIG_SMP
 | 
			
		||||
	mpic->hc_ipi.typename = name;
 | 
			
		||||
	mpic->hc_ipi.enable = mpic_enable_ipi;
 | 
			
		||||
	mpic->hc_ipi.disable = mpic_disable_ipi;
 | 
			
		||||
	mpic->hc_ipi.end = mpic_end_ipi;
 | 
			
		||||
#endif /* CONFIG_SMP */
 | 
			
		||||
 | 
			
		||||
	mpic->flags = flags;
 | 
			
		||||
	mpic->isu_size = isu_size;
 | 
			
		||||
	mpic->irq_offset = irq_offset;
 | 
			
		||||
	mpic->irq_count = irq_count;
 | 
			
		||||
	mpic->ipi_offset = ipi_offset;
 | 
			
		||||
	mpic->num_sources = 0; /* so far */
 | 
			
		||||
	mpic->senses = senses;
 | 
			
		||||
	mpic->senses_count = senses_count;
 | 
			
		||||
 | 
			
		||||
	/* Map the global registers */
 | 
			
		||||
	mpic->gregs = ioremap(phys_addr + MPIC_GREG_BASE, 0x1000);
 | 
			
		||||
	mpic->tmregs = mpic->gregs + (MPIC_TIMER_BASE >> 2);
 | 
			
		||||
	BUG_ON(mpic->gregs == NULL);
 | 
			
		||||
 | 
			
		||||
	/* Reset */
 | 
			
		||||
	if (flags & MPIC_WANTS_RESET) {
 | 
			
		||||
		mpic_write(mpic->gregs, MPIC_GREG_GLOBAL_CONF_0,
 | 
			
		||||
			   mpic_read(mpic->gregs, MPIC_GREG_GLOBAL_CONF_0)
 | 
			
		||||
			   | MPIC_GREG_GCONF_RESET);
 | 
			
		||||
		while( mpic_read(mpic->gregs, MPIC_GREG_GLOBAL_CONF_0)
 | 
			
		||||
		       & MPIC_GREG_GCONF_RESET)
 | 
			
		||||
			mb();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Read feature register, calculate num CPUs and, for non-ISU
 | 
			
		||||
	 * MPICs, num sources as well. On ISU MPICs, sources are counted
 | 
			
		||||
	 * as ISUs are added
 | 
			
		||||
	 */
 | 
			
		||||
	reg = mpic_read(mpic->gregs, MPIC_GREG_FEATURE_0);
 | 
			
		||||
	mpic->num_cpus = ((reg & MPIC_GREG_FEATURE_LAST_CPU_MASK)
 | 
			
		||||
			  >> MPIC_GREG_FEATURE_LAST_CPU_SHIFT) + 1;
 | 
			
		||||
	if (isu_size == 0)
 | 
			
		||||
		mpic->num_sources = ((reg & MPIC_GREG_FEATURE_LAST_SRC_MASK)
 | 
			
		||||
				     >> MPIC_GREG_FEATURE_LAST_SRC_SHIFT) + 1;
 | 
			
		||||
 | 
			
		||||
	/* Map the per-CPU registers */
 | 
			
		||||
	for (i = 0; i < mpic->num_cpus; i++) {
 | 
			
		||||
		mpic->cpuregs[i] = ioremap(phys_addr + MPIC_CPU_BASE +
 | 
			
		||||
					   i * MPIC_CPU_STRIDE, 0x1000);
 | 
			
		||||
		BUG_ON(mpic->cpuregs[i] == NULL);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Initialize main ISU if none provided */
 | 
			
		||||
	if (mpic->isu_size == 0) {
 | 
			
		||||
		mpic->isu_size = mpic->num_sources;
 | 
			
		||||
		mpic->isus[0] = ioremap(phys_addr + MPIC_IRQ_BASE,
 | 
			
		||||
					MPIC_IRQ_STRIDE * mpic->isu_size);
 | 
			
		||||
		BUG_ON(mpic->isus[0] == NULL);
 | 
			
		||||
	}
 | 
			
		||||
	mpic->isu_shift = 1 + __ilog2(mpic->isu_size - 1);
 | 
			
		||||
	mpic->isu_mask = (1 << mpic->isu_shift) - 1;
 | 
			
		||||
 | 
			
		||||
	/* Display version */
 | 
			
		||||
	switch (reg & MPIC_GREG_FEATURE_VERSION_MASK) {
 | 
			
		||||
	case 1:
 | 
			
		||||
		vers = "1.0";
 | 
			
		||||
		break;
 | 
			
		||||
	case 2:
 | 
			
		||||
		vers = "1.2";
 | 
			
		||||
		break;
 | 
			
		||||
	case 3:
 | 
			
		||||
		vers = "1.3";
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		vers = "<unknown>";
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	printk(KERN_INFO "mpic: Setting up MPIC \"%s\" version %s at %lx, max %d CPUs\n",
 | 
			
		||||
	       name, vers, phys_addr, mpic->num_cpus);
 | 
			
		||||
	printk(KERN_INFO "mpic: ISU size: %d, shift: %d, mask: %x\n", mpic->isu_size,
 | 
			
		||||
	       mpic->isu_shift, mpic->isu_mask);
 | 
			
		||||
 | 
			
		||||
	mpic->next = mpics;
 | 
			
		||||
	mpics = mpic;
 | 
			
		||||
 | 
			
		||||
	if (flags & MPIC_PRIMARY)
 | 
			
		||||
		mpic_primary = mpic;
 | 
			
		||||
 | 
			
		||||
	return mpic;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void __init mpic_assign_isu(struct mpic *mpic, unsigned int isu_num,
 | 
			
		||||
			    unsigned long phys_addr)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int isu_first = isu_num * mpic->isu_size;
 | 
			
		||||
 | 
			
		||||
	BUG_ON(isu_num >= MPIC_MAX_ISU);
 | 
			
		||||
 | 
			
		||||
	mpic->isus[isu_num] = ioremap(phys_addr, MPIC_IRQ_STRIDE * mpic->isu_size);
 | 
			
		||||
	if ((isu_first + mpic->isu_size) > mpic->num_sources)
 | 
			
		||||
		mpic->num_sources = isu_first + mpic->isu_size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void __init mpic_setup_cascade(unsigned int irq, mpic_cascade_t handler,
 | 
			
		||||
			       void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct mpic *mpic = mpic_find(irq, NULL);
 | 
			
		||||
	unsigned long flags;
 | 
			
		||||
 | 
			
		||||
	/* Synchronization here is a bit dodgy, so don't try to replace cascade
 | 
			
		||||
	 * interrupts on the fly too often ... but normally it's set up at boot.
 | 
			
		||||
	 */
 | 
			
		||||
	spin_lock_irqsave(&mpic_lock, flags);
 | 
			
		||||
	if (mpic->cascade)	       
 | 
			
		||||
		mpic_disable_irq(mpic->cascade_vec + mpic->irq_offset);
 | 
			
		||||
	mpic->cascade = NULL;
 | 
			
		||||
	wmb();
 | 
			
		||||
	mpic->cascade_vec = irq - mpic->irq_offset;
 | 
			
		||||
	mpic->cascade_data = data;
 | 
			
		||||
	wmb();
 | 
			
		||||
	mpic->cascade = handler;
 | 
			
		||||
	mpic_enable_irq(irq);
 | 
			
		||||
	spin_unlock_irqrestore(&mpic_lock, flags);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void __init mpic_init(struct mpic *mpic)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	BUG_ON(mpic->num_sources == 0);
 | 
			
		||||
 | 
			
		||||
	printk(KERN_INFO "mpic: Initializing for %d sources\n", mpic->num_sources);
 | 
			
		||||
 | 
			
		||||
	/* Set current processor priority to max */
 | 
			
		||||
	mpic_cpu_write(MPIC_CPU_CURRENT_TASK_PRI, 0xf);
 | 
			
		||||
 | 
			
		||||
	/* Initialize timers: just disable them all */
 | 
			
		||||
	for (i = 0; i < 4; i++) {
 | 
			
		||||
		mpic_write(mpic->tmregs,
 | 
			
		||||
			   i * MPIC_TIMER_STRIDE + MPIC_TIMER_DESTINATION, 0);
 | 
			
		||||
		mpic_write(mpic->tmregs,
 | 
			
		||||
			   i * MPIC_TIMER_STRIDE + MPIC_TIMER_VECTOR_PRI,
 | 
			
		||||
			   MPIC_VECPRI_MASK |
 | 
			
		||||
			   (MPIC_VEC_TIMER_0 + i));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Initialize IPIs to our reserved vectors and mark them disabled for now */
 | 
			
		||||
	mpic_test_broken_ipi(mpic);
 | 
			
		||||
	for (i = 0; i < 4; i++) {
 | 
			
		||||
		mpic_ipi_write(i,
 | 
			
		||||
			       MPIC_VECPRI_MASK |
 | 
			
		||||
			       (10 << MPIC_VECPRI_PRIORITY_SHIFT) |
 | 
			
		||||
			       (MPIC_VEC_IPI_0 + i));
 | 
			
		||||
#ifdef CONFIG_SMP
 | 
			
		||||
		if (!(mpic->flags & MPIC_PRIMARY))
 | 
			
		||||
			continue;
 | 
			
		||||
		irq_desc[mpic->ipi_offset+i].status |= IRQ_PER_CPU;
 | 
			
		||||
		irq_desc[mpic->ipi_offset+i].handler = &mpic->hc_ipi;
 | 
			
		||||
		
 | 
			
		||||
#endif /* CONFIG_SMP */
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Initialize interrupt sources */
 | 
			
		||||
	if (mpic->irq_count == 0)
 | 
			
		||||
		mpic->irq_count = mpic->num_sources;
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_MPIC_BROKEN_U3
 | 
			
		||||
	/* Do the ioapic fixups on U3 broken mpic */
 | 
			
		||||
	DBG("MPIC flags: %x\n", mpic->flags);
 | 
			
		||||
	if ((mpic->flags & MPIC_BROKEN_U3) && (mpic->flags & MPIC_PRIMARY))
 | 
			
		||||
		mpic_scan_ioapics(mpic);
 | 
			
		||||
#endif /* CONFIG_MPIC_BROKEN_U3 */
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < mpic->num_sources; i++) {
 | 
			
		||||
		/* start with vector = source number, and masked */
 | 
			
		||||
		u32 vecpri = MPIC_VECPRI_MASK | i | (8 << MPIC_VECPRI_PRIORITY_SHIFT);
 | 
			
		||||
		int level = 0;
 | 
			
		||||
		
 | 
			
		||||
		/* if it's an IPI, we skip it */
 | 
			
		||||
		if ((mpic->irq_offset + i) >= (mpic->ipi_offset + i) &&
 | 
			
		||||
		    (mpic->irq_offset + i) <  (mpic->ipi_offset + i + 4))
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		/* do senses munging */
 | 
			
		||||
		if (mpic->senses && i < mpic->senses_count) {
 | 
			
		||||
			if (mpic->senses[i] & IRQ_SENSE_LEVEL)
 | 
			
		||||
				vecpri |= MPIC_VECPRI_SENSE_LEVEL;
 | 
			
		||||
			if (mpic->senses[i] & IRQ_POLARITY_POSITIVE)
 | 
			
		||||
				vecpri |= MPIC_VECPRI_POLARITY_POSITIVE;
 | 
			
		||||
		} else
 | 
			
		||||
			vecpri |= MPIC_VECPRI_SENSE_LEVEL;
 | 
			
		||||
 | 
			
		||||
		/* remember if it was a level interrupts */
 | 
			
		||||
		level = (vecpri & MPIC_VECPRI_SENSE_LEVEL);
 | 
			
		||||
 | 
			
		||||
		/* deal with broken U3 */
 | 
			
		||||
		if (mpic->flags & MPIC_BROKEN_U3) {
 | 
			
		||||
#ifdef CONFIG_MPIC_BROKEN_U3
 | 
			
		||||
			if (mpic_is_ht_interrupt(mpic, i)) {
 | 
			
		||||
				vecpri &= ~(MPIC_VECPRI_SENSE_MASK |
 | 
			
		||||
					    MPIC_VECPRI_POLARITY_MASK);
 | 
			
		||||
				vecpri |= MPIC_VECPRI_POLARITY_POSITIVE;
 | 
			
		||||
			}
 | 
			
		||||
#else
 | 
			
		||||
			printk(KERN_ERR "mpic: BROKEN_U3 set, but CONFIG doesn't match\n");
 | 
			
		||||
#endif
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		DBG("setup source %d, vecpri: %08x, level: %d\n", i, vecpri,
 | 
			
		||||
		    (level != 0));
 | 
			
		||||
 | 
			
		||||
		/* init hw */
 | 
			
		||||
		mpic_irq_write(i, MPIC_IRQ_VECTOR_PRI, vecpri);
 | 
			
		||||
		mpic_irq_write(i, MPIC_IRQ_DESTINATION,
 | 
			
		||||
			       1 << hard_smp_processor_id());
 | 
			
		||||
 | 
			
		||||
		/* init linux descriptors */
 | 
			
		||||
		if (i < mpic->irq_count) {
 | 
			
		||||
			irq_desc[mpic->irq_offset+i].status = level ? IRQ_LEVEL : 0;
 | 
			
		||||
			irq_desc[mpic->irq_offset+i].handler = &mpic->hc_irq;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	/* Init spurrious vector */
 | 
			
		||||
	mpic_write(mpic->gregs, MPIC_GREG_SPURIOUS, MPIC_VEC_SPURRIOUS);
 | 
			
		||||
 | 
			
		||||
	/* Disable 8259 passthrough */
 | 
			
		||||
	mpic_write(mpic->gregs, MPIC_GREG_GLOBAL_CONF_0,
 | 
			
		||||
		   mpic_read(mpic->gregs, MPIC_GREG_GLOBAL_CONF_0)
 | 
			
		||||
		   | MPIC_GREG_GCONF_8259_PTHROU_DIS);
 | 
			
		||||
 | 
			
		||||
	/* Set current processor priority to 0 */
 | 
			
		||||
	mpic_cpu_write(MPIC_CPU_CURRENT_TASK_PRI, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void mpic_irq_set_priority(unsigned int irq, unsigned int pri)
 | 
			
		||||
{
 | 
			
		||||
	int is_ipi;
 | 
			
		||||
	struct mpic *mpic = mpic_find(irq, &is_ipi);
 | 
			
		||||
	unsigned long flags;
 | 
			
		||||
	u32 reg;
 | 
			
		||||
 | 
			
		||||
	spin_lock_irqsave(&mpic_lock, flags);
 | 
			
		||||
	if (is_ipi) {
 | 
			
		||||
		reg = mpic_ipi_read(irq - mpic->ipi_offset) & MPIC_VECPRI_PRIORITY_MASK;
 | 
			
		||||
		mpic_ipi_write(irq - mpic->ipi_offset,
 | 
			
		||||
			       reg | (pri << MPIC_VECPRI_PRIORITY_SHIFT));
 | 
			
		||||
	} else {
 | 
			
		||||
		reg = mpic_irq_read(irq - mpic->irq_offset, MPIC_IRQ_VECTOR_PRI)
 | 
			
		||||
			& MPIC_VECPRI_PRIORITY_MASK;
 | 
			
		||||
		mpic_irq_write(irq - mpic->irq_offset, MPIC_IRQ_VECTOR_PRI,
 | 
			
		||||
			       reg | (pri << MPIC_VECPRI_PRIORITY_SHIFT));
 | 
			
		||||
	}
 | 
			
		||||
	spin_unlock_irqrestore(&mpic_lock, flags);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsigned int mpic_irq_get_priority(unsigned int irq)
 | 
			
		||||
{
 | 
			
		||||
	int is_ipi;
 | 
			
		||||
	struct mpic *mpic = mpic_find(irq, &is_ipi);
 | 
			
		||||
	unsigned long flags;
 | 
			
		||||
	u32 reg;
 | 
			
		||||
 | 
			
		||||
	spin_lock_irqsave(&mpic_lock, flags);
 | 
			
		||||
	if (is_ipi)
 | 
			
		||||
		reg = mpic_ipi_read(irq - mpic->ipi_offset);
 | 
			
		||||
	else
 | 
			
		||||
		reg = mpic_irq_read(irq - mpic->irq_offset, MPIC_IRQ_VECTOR_PRI);
 | 
			
		||||
	spin_unlock_irqrestore(&mpic_lock, flags);
 | 
			
		||||
	return (reg & MPIC_VECPRI_PRIORITY_MASK) >> MPIC_VECPRI_PRIORITY_SHIFT;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mpic_setup_this_cpu(void)
 | 
			
		||||
{
 | 
			
		||||
#ifdef CONFIG_SMP
 | 
			
		||||
	struct mpic *mpic = mpic_primary;
 | 
			
		||||
	unsigned long flags;
 | 
			
		||||
	u32 msk = 1 << hard_smp_processor_id();
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
 | 
			
		||||
	BUG_ON(mpic == NULL);
 | 
			
		||||
 | 
			
		||||
	DBG("%s: setup_this_cpu(%d)\n", mpic->name, hard_smp_processor_id());
 | 
			
		||||
 | 
			
		||||
	spin_lock_irqsave(&mpic_lock, flags);
 | 
			
		||||
 | 
			
		||||
 	/* let the mpic know we want intrs. default affinity is 0xffffffff
 | 
			
		||||
	 * until changed via /proc. That's how it's done on x86. If we want
 | 
			
		||||
	 * it differently, then we should make sure we also change the default
 | 
			
		||||
	 * values of irq_affinity in irq.c.
 | 
			
		||||
 	 */
 | 
			
		||||
	if (distribute_irqs) {
 | 
			
		||||
	 	for (i = 0; i < mpic->num_sources ; i++)
 | 
			
		||||
			mpic_irq_write(i, MPIC_IRQ_DESTINATION,
 | 
			
		||||
				mpic_irq_read(i, MPIC_IRQ_DESTINATION) | msk);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Set current processor priority to 0 */
 | 
			
		||||
	mpic_cpu_write(MPIC_CPU_CURRENT_TASK_PRI, 0);
 | 
			
		||||
 | 
			
		||||
	spin_unlock_irqrestore(&mpic_lock, flags);
 | 
			
		||||
#endif /* CONFIG_SMP */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int mpic_cpu_get_priority(void)
 | 
			
		||||
{
 | 
			
		||||
	struct mpic *mpic = mpic_primary;
 | 
			
		||||
 | 
			
		||||
	return mpic_cpu_read(MPIC_CPU_CURRENT_TASK_PRI);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mpic_cpu_set_priority(int prio)
 | 
			
		||||
{
 | 
			
		||||
	struct mpic *mpic = mpic_primary;
 | 
			
		||||
 | 
			
		||||
	prio &= MPIC_CPU_TASKPRI_MASK;
 | 
			
		||||
	mpic_cpu_write(MPIC_CPU_CURRENT_TASK_PRI, prio);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * XXX: someone who knows mpic should check this.
 | 
			
		||||
 * do we need to eoi the ipi including for kexec cpu here (see xics comments)?
 | 
			
		||||
 * or can we reset the mpic in the new kernel?
 | 
			
		||||
 */
 | 
			
		||||
void mpic_teardown_this_cpu(int secondary)
 | 
			
		||||
{
 | 
			
		||||
	struct mpic *mpic = mpic_primary;
 | 
			
		||||
	unsigned long flags;
 | 
			
		||||
	u32 msk = 1 << hard_smp_processor_id();
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
 | 
			
		||||
	BUG_ON(mpic == NULL);
 | 
			
		||||
 | 
			
		||||
	DBG("%s: teardown_this_cpu(%d)\n", mpic->name, hard_smp_processor_id());
 | 
			
		||||
	spin_lock_irqsave(&mpic_lock, flags);
 | 
			
		||||
 | 
			
		||||
	/* let the mpic know we don't want intrs.  */
 | 
			
		||||
	for (i = 0; i < mpic->num_sources ; i++)
 | 
			
		||||
		mpic_irq_write(i, MPIC_IRQ_DESTINATION,
 | 
			
		||||
			mpic_irq_read(i, MPIC_IRQ_DESTINATION) & ~msk);
 | 
			
		||||
 | 
			
		||||
	/* Set current processor priority to max */
 | 
			
		||||
	mpic_cpu_write(MPIC_CPU_CURRENT_TASK_PRI, 0xf);
 | 
			
		||||
 | 
			
		||||
	spin_unlock_irqrestore(&mpic_lock, flags);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void mpic_send_ipi(unsigned int ipi_no, unsigned int cpu_mask)
 | 
			
		||||
{
 | 
			
		||||
	struct mpic *mpic = mpic_primary;
 | 
			
		||||
 | 
			
		||||
	BUG_ON(mpic == NULL);
 | 
			
		||||
 | 
			
		||||
	DBG("%s: send_ipi(ipi_no: %d)\n", mpic->name, ipi_no);
 | 
			
		||||
 | 
			
		||||
	mpic_cpu_write(MPIC_CPU_IPI_DISPATCH_0 + ipi_no * 0x10,
 | 
			
		||||
		       mpic_physmask(cpu_mask & cpus_addr(cpu_online_map)[0]));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int mpic_get_one_irq(struct mpic *mpic, struct pt_regs *regs)
 | 
			
		||||
{
 | 
			
		||||
	u32 irq;
 | 
			
		||||
 | 
			
		||||
	irq = mpic_cpu_read(MPIC_CPU_INTACK) & MPIC_VECPRI_VECTOR_MASK;
 | 
			
		||||
	DBG("%s: get_one_irq(): %d\n", mpic->name, irq);
 | 
			
		||||
 | 
			
		||||
	if (mpic->cascade && irq == mpic->cascade_vec) {
 | 
			
		||||
		DBG("%s: cascading ...\n", mpic->name);
 | 
			
		||||
		irq = mpic->cascade(regs, mpic->cascade_data);
 | 
			
		||||
		mpic_eoi(mpic);
 | 
			
		||||
		return irq;
 | 
			
		||||
	}
 | 
			
		||||
	if (unlikely(irq == MPIC_VEC_SPURRIOUS))
 | 
			
		||||
		return -1;
 | 
			
		||||
	if (irq < MPIC_VEC_IPI_0) 
 | 
			
		||||
		return irq + mpic->irq_offset;
 | 
			
		||||
       	DBG("%s: ipi %d !\n", mpic->name, irq - MPIC_VEC_IPI_0);
 | 
			
		||||
	return irq - MPIC_VEC_IPI_0 + mpic->ipi_offset;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int mpic_get_irq(struct pt_regs *regs)
 | 
			
		||||
{
 | 
			
		||||
	struct mpic *mpic = mpic_primary;
 | 
			
		||||
 | 
			
		||||
	BUG_ON(mpic == NULL);
 | 
			
		||||
 | 
			
		||||
	return mpic_get_one_irq(mpic, regs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_SMP
 | 
			
		||||
void mpic_request_ipis(void)
 | 
			
		||||
{
 | 
			
		||||
	struct mpic *mpic = mpic_primary;
 | 
			
		||||
 | 
			
		||||
	BUG_ON(mpic == NULL);
 | 
			
		||||
	
 | 
			
		||||
	printk("requesting IPIs ... \n");
 | 
			
		||||
 | 
			
		||||
	/* IPIs are marked SA_INTERRUPT as they must run with irqs disabled */
 | 
			
		||||
	request_irq(mpic->ipi_offset+0, mpic_ipi_action, SA_INTERRUPT,
 | 
			
		||||
		    "IPI0 (call function)", mpic);
 | 
			
		||||
	request_irq(mpic->ipi_offset+1, mpic_ipi_action, SA_INTERRUPT,
 | 
			
		||||
		   "IPI1 (reschedule)", mpic);
 | 
			
		||||
	request_irq(mpic->ipi_offset+2, mpic_ipi_action, SA_INTERRUPT,
 | 
			
		||||
		   "IPI2 (unused)", mpic);
 | 
			
		||||
	request_irq(mpic->ipi_offset+3, mpic_ipi_action, SA_INTERRUPT,
 | 
			
		||||
		   "IPI3 (debugger break)", mpic);
 | 
			
		||||
 | 
			
		||||
	printk("IPIs requested... \n");
 | 
			
		||||
}
 | 
			
		||||
#endif /* CONFIG_SMP */
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
#
 | 
			
		||||
# Makefile for the linux kernel.
 | 
			
		||||
#
 | 
			
		||||
ifneq ($(CONFIG_PPC_MERGE),y)
 | 
			
		||||
 | 
			
		||||
extra-$(CONFIG_PPC_STD_MMU)	:= head.o
 | 
			
		||||
extra-$(CONFIG_40x)		:= head_4xx.o
 | 
			
		||||
@@ -37,3 +38,23 @@ endif
 | 
			
		||||
 | 
			
		||||
# These are here while we do the architecture merge
 | 
			
		||||
vecemu-y			+= ../../powerpc/kernel/vecemu.o
 | 
			
		||||
 | 
			
		||||
else
 | 
			
		||||
obj-y				:= entry.o irq.o idle.o time.o misc.o \
 | 
			
		||||
					signal.o ptrace.o align.o \
 | 
			
		||||
					syscalls.o setup.o \
 | 
			
		||||
					cputable.o perfmon.o
 | 
			
		||||
obj-$(CONFIG_6xx)		+= l2cr.o cpu_setup_6xx.o
 | 
			
		||||
obj-$(CONFIG_SOFTWARE_SUSPEND)	+= swsusp.o
 | 
			
		||||
obj-$(CONFIG_POWER4)		+= cpu_setup_power4.o
 | 
			
		||||
obj-$(CONFIG_MODULES)		+= module.o ppc_ksyms.o
 | 
			
		||||
obj-$(CONFIG_NOT_COHERENT_CACHE)	+= dma-mapping.o
 | 
			
		||||
obj-$(CONFIG_PCI)		+= pci.o
 | 
			
		||||
obj-$(CONFIG_KGDB)		+= ppc-stub.o
 | 
			
		||||
obj-$(CONFIG_SMP)		+= smp.o smp-tbsync.o
 | 
			
		||||
obj-$(CONFIG_TAU)		+= temp.o
 | 
			
		||||
ifndef CONFIG_E200
 | 
			
		||||
obj-$(CONFIG_FSL_BOOKE)		+= perfmon_fsl_booke.o
 | 
			
		||||
endif
 | 
			
		||||
obj-$(CONFIG_KEXEC)		+= machine_kexec.o relocate_kernel.o
 | 
			
		||||
endif
 | 
			
		||||
 
 | 
			
		||||
@@ -83,6 +83,8 @@ extern void pmac_init(unsigned long r3, unsigned long r4,
 | 
			
		||||
		unsigned long r5, unsigned long r6, unsigned long r7);
 | 
			
		||||
extern void chrp_init(unsigned long r3, unsigned long r4,
 | 
			
		||||
		unsigned long r5, unsigned long r6, unsigned long r7);
 | 
			
		||||
 | 
			
		||||
dev_t boot_dev;
 | 
			
		||||
#endif /* CONFIG_PPC_MULTIPLATFORM */
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_MAGIC_SYSRQ
 | 
			
		||||
@@ -405,11 +407,13 @@ platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
 | 
			
		||||
			_machine = _MACH_prep;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_PPC_PREP
 | 
			
		||||
	/* not much more to do here, if prep */
 | 
			
		||||
	if (_machine == _MACH_prep) {
 | 
			
		||||
		prep_init(r3, r4, r5, r6, r7);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	/* prom_init has already been called from __start */
 | 
			
		||||
	if (boot_infos)
 | 
			
		||||
@@ -480,12 +484,16 @@ platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
 | 
			
		||||
#endif /* CONFIG_ADB */
 | 
			
		||||
 | 
			
		||||
	switch (_machine) {
 | 
			
		||||
#ifdef CONFIG_PPC_PMAC
 | 
			
		||||
	case _MACH_Pmac:
 | 
			
		||||
		pmac_init(r3, r4, r5, r6, r7);
 | 
			
		||||
		break;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef CONFIG_PPC_CHRP
 | 
			
		||||
	case _MACH_chrp:
 | 
			
		||||
		chrp_init(r3, r4, r5, r6, r7);
 | 
			
		||||
		break;
 | 
			
		||||
#endif
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -89,9 +89,6 @@ extern void prep_tiger1_setup_pci(char *irq_edge_mask_lo, char *irq_edge_mask_hi
 | 
			
		||||
#define cached_21	(((char *)(ppc_cached_irq_mask))[3])
 | 
			
		||||
#define cached_A1	(((char *)(ppc_cached_irq_mask))[2])
 | 
			
		||||
 | 
			
		||||
/* for the mac fs */
 | 
			
		||||
dev_t boot_dev;
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_SOUND_CS4232
 | 
			
		||||
long ppc_cs4232_dma, ppc_cs4232_dma2;
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@
 | 
			
		||||
CFLAGS_prom_init.o      += -fPIC
 | 
			
		||||
CFLAGS_btext.o          += -fPIC
 | 
			
		||||
 | 
			
		||||
ifneq ($(CONFIG_PPC_MERGE),y)
 | 
			
		||||
wdt-mpc8xx-$(CONFIG_8xx_WDT)	+= m8xx_wdt.o
 | 
			
		||||
 | 
			
		||||
obj-$(CONFIG_PPCBUG_NVRAM)	+= prep_nvram.o
 | 
			
		||||
@@ -109,3 +110,16 @@ obj-$(CONFIG_PPC_MPC52xx)	+= mpc52xx_setup.o mpc52xx_pic.o \
 | 
			
		||||
ifeq ($(CONFIG_PPC_MPC52xx),y)
 | 
			
		||||
obj-$(CONFIG_PCI)		+= mpc52xx_pci.o
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
else
 | 
			
		||||
# Stuff still needed by the merged powerpc sources
 | 
			
		||||
 | 
			
		||||
obj-$(CONFIG_PPCBUG_NVRAM)	+= prep_nvram.o
 | 
			
		||||
obj-$(CONFIG_PPC_OF)		+= prom_init.o prom.o of_device.o
 | 
			
		||||
obj-$(CONFIG_PPC_PMAC)		+= indirect_pci.o
 | 
			
		||||
obj-$(CONFIG_PPC_CHRP)		+= indirect_pci.o i8259.o
 | 
			
		||||
obj-$(CONFIG_PPC_PREP)		+= indirect_pci.o i8259.o todc_time.o
 | 
			
		||||
obj-$(CONFIG_BOOTX_TEXT)	+= btext.o
 | 
			
		||||
obj-$(CONFIG_MPC10X_BRIDGE)	+= mpc10x_common.o indirect_pci.o ppc_sys.o
 | 
			
		||||
 | 
			
		||||
endif
 | 
			
		||||
 
 | 
			
		||||
@@ -405,7 +405,7 @@ static int __init via_pmu_start(void)
 | 
			
		||||
	bright_req_2.complete = 1;
 | 
			
		||||
	batt_req.complete = 1;
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_PPC32
 | 
			
		||||
#if defined(CONFIG_PPC32) && !defined(CONFIG_PPC_MERGE)
 | 
			
		||||
	if (pmu_kind == PMU_KEYLARGO_BASED)
 | 
			
		||||
		openpic_set_irq_priority(vias->intrs[0].line,
 | 
			
		||||
					 OPENPIC_PRIORITY_DEFAULT + 1);
 | 
			
		||||
 
 | 
			
		||||
@@ -629,12 +629,4 @@ void __init proc_misc_init(void)
 | 
			
		||||
	if (entry)
 | 
			
		||||
		entry->proc_fops = &proc_sysrq_trigger_operations;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef CONFIG_PPC32
 | 
			
		||||
	{
 | 
			
		||||
		extern struct file_operations ppc_htab_operations;
 | 
			
		||||
		entry = create_proc_entry("ppc_htab", S_IRUGO|S_IWUSR, NULL);
 | 
			
		||||
		if (entry)
 | 
			
		||||
			entry->proc_fops = &ppc_htab_operations;
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										42
									
								
								include/asm-powerpc/kdebug.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								include/asm-powerpc/kdebug.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
			
		||||
#ifndef _POWERPC_KDEBUG_H
 | 
			
		||||
#define _POWERPC_KDEBUG_H 1
 | 
			
		||||
 | 
			
		||||
/* nearly identical to x86_64/i386 code */
 | 
			
		||||
 | 
			
		||||
#include <linux/notifier.h>
 | 
			
		||||
 | 
			
		||||
struct pt_regs;
 | 
			
		||||
 | 
			
		||||
struct die_args {
 | 
			
		||||
	struct pt_regs *regs;
 | 
			
		||||
	const char *str;
 | 
			
		||||
	long err;
 | 
			
		||||
	int trapnr;
 | 
			
		||||
	int signr;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
   Note - you should never unregister because that can race with NMIs.
 | 
			
		||||
   If you really want to do it first unregister - then synchronize_sched -
 | 
			
		||||
   then free.
 | 
			
		||||
 */
 | 
			
		||||
int register_die_notifier(struct notifier_block *nb);
 | 
			
		||||
extern struct notifier_block *powerpc_die_chain;
 | 
			
		||||
 | 
			
		||||
/* Grossly misnamed. */
 | 
			
		||||
enum die_val {
 | 
			
		||||
	DIE_OOPS = 1,
 | 
			
		||||
	DIE_IABR_MATCH,
 | 
			
		||||
	DIE_DABR_MATCH,
 | 
			
		||||
	DIE_BPT,
 | 
			
		||||
	DIE_SSTEP,
 | 
			
		||||
	DIE_PAGE_FAULT,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static inline int notify_die(enum die_val val,char *str,struct pt_regs *regs,long err,int trap, int sig)
 | 
			
		||||
{
 | 
			
		||||
	struct die_args args = { .regs=regs, .str=str, .err=err, .trapnr=trap,.signr=sig };
 | 
			
		||||
	return notifier_call_chain(&powerpc_die_chain, val, &args);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										67
									
								
								include/asm-powerpc/kprobes.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								include/asm-powerpc/kprobes.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,67 @@
 | 
			
		||||
#ifndef _ASM_KPROBES_H
 | 
			
		||||
#define _ASM_KPROBES_H
 | 
			
		||||
/*
 | 
			
		||||
 *  Kernel Probes (KProbes)
 | 
			
		||||
 *  include/asm-ppc64/kprobes.h
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 2 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) IBM Corporation, 2002, 2004
 | 
			
		||||
 *
 | 
			
		||||
 * 2002-Oct	Created by Vamsi Krishna S <vamsi_krishna@in.ibm.com> Kernel
 | 
			
		||||
 *		Probes initial implementation ( includes suggestions from
 | 
			
		||||
 *		Rusty Russell).
 | 
			
		||||
 * 2004-Nov	Modified for PPC64 by Ananth N Mavinakayanahalli
 | 
			
		||||
 *		<ananth@in.ibm.com>
 | 
			
		||||
 */
 | 
			
		||||
#include <linux/types.h>
 | 
			
		||||
#include <linux/ptrace.h>
 | 
			
		||||
 | 
			
		||||
struct pt_regs;
 | 
			
		||||
 | 
			
		||||
typedef unsigned int kprobe_opcode_t;
 | 
			
		||||
#define BREAKPOINT_INSTRUCTION	0x7fe00008	/* trap */
 | 
			
		||||
#define MAX_INSN_SIZE 1
 | 
			
		||||
 | 
			
		||||
#define IS_TW(instr)		(((instr) & 0xfc0007fe) == 0x7c000008)
 | 
			
		||||
#define IS_TD(instr)		(((instr) & 0xfc0007fe) == 0x7c000088)
 | 
			
		||||
#define IS_TDI(instr)		(((instr) & 0xfc000000) == 0x08000000)
 | 
			
		||||
#define IS_TWI(instr)		(((instr) & 0xfc000000) == 0x0c000000)
 | 
			
		||||
 | 
			
		||||
#define JPROBE_ENTRY(pentry)	(kprobe_opcode_t *)((func_descr_t *)pentry)
 | 
			
		||||
 | 
			
		||||
#define is_trap(instr)	(IS_TW(instr) || IS_TD(instr) || \
 | 
			
		||||
			IS_TWI(instr) || IS_TDI(instr))
 | 
			
		||||
 | 
			
		||||
#define ARCH_SUPPORTS_KRETPROBES
 | 
			
		||||
void kretprobe_trampoline(void);
 | 
			
		||||
 | 
			
		||||
/* Architecture specific copy of original instruction */
 | 
			
		||||
struct arch_specific_insn {
 | 
			
		||||
	/* copy of original instruction */
 | 
			
		||||
	kprobe_opcode_t *insn;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_KPROBES
 | 
			
		||||
extern int kprobe_exceptions_notify(struct notifier_block *self,
 | 
			
		||||
				    unsigned long val, void *data);
 | 
			
		||||
#else				/* !CONFIG_KPROBES */
 | 
			
		||||
static inline int kprobe_exceptions_notify(struct notifier_block *self,
 | 
			
		||||
					   unsigned long val, void *data)
 | 
			
		||||
{
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#endif				/* _ASM_KPROBES_H */
 | 
			
		||||
							
								
								
									
										279
									
								
								include/asm-powerpc/mpic.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										279
									
								
								include/asm-powerpc/mpic.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,279 @@
 | 
			
		||||
#include <linux/irq.h>
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Global registers
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define MPIC_GREG_BASE			0x01000
 | 
			
		||||
 | 
			
		||||
#define MPIC_GREG_FEATURE_0		0x00000
 | 
			
		||||
#define		MPIC_GREG_FEATURE_LAST_SRC_MASK		0x07ff0000
 | 
			
		||||
#define		MPIC_GREG_FEATURE_LAST_SRC_SHIFT	16
 | 
			
		||||
#define		MPIC_GREG_FEATURE_LAST_CPU_MASK		0x00001f00
 | 
			
		||||
#define		MPIC_GREG_FEATURE_LAST_CPU_SHIFT	8
 | 
			
		||||
#define		MPIC_GREG_FEATURE_VERSION_MASK		0xff
 | 
			
		||||
#define MPIC_GREG_FEATURE_1		0x00010
 | 
			
		||||
#define MPIC_GREG_GLOBAL_CONF_0		0x00020
 | 
			
		||||
#define		MPIC_GREG_GCONF_RESET			0x80000000
 | 
			
		||||
#define		MPIC_GREG_GCONF_8259_PTHROU_DIS		0x20000000
 | 
			
		||||
#define		MPIC_GREG_GCONF_BASE_MASK		0x000fffff
 | 
			
		||||
#define MPIC_GREG_GLOBAL_CONF_1		0x00030
 | 
			
		||||
#define MPIC_GREG_VENDOR_0		0x00040
 | 
			
		||||
#define MPIC_GREG_VENDOR_1		0x00050
 | 
			
		||||
#define MPIC_GREG_VENDOR_2		0x00060
 | 
			
		||||
#define MPIC_GREG_VENDOR_3		0x00070
 | 
			
		||||
#define MPIC_GREG_VENDOR_ID		0x00080
 | 
			
		||||
#define 	MPIC_GREG_VENDOR_ID_STEPPING_MASK	0x00ff0000
 | 
			
		||||
#define 	MPIC_GREG_VENDOR_ID_STEPPING_SHIFT	16
 | 
			
		||||
#define 	MPIC_GREG_VENDOR_ID_DEVICE_ID_MASK	0x0000ff00
 | 
			
		||||
#define 	MPIC_GREG_VENDOR_ID_DEVICE_ID_SHIFT	8
 | 
			
		||||
#define 	MPIC_GREG_VENDOR_ID_VENDOR_ID_MASK	0x000000ff
 | 
			
		||||
#define MPIC_GREG_PROCESSOR_INIT	0x00090
 | 
			
		||||
#define MPIC_GREG_IPI_VECTOR_PRI_0	0x000a0
 | 
			
		||||
#define MPIC_GREG_IPI_VECTOR_PRI_1	0x000b0
 | 
			
		||||
#define MPIC_GREG_IPI_VECTOR_PRI_2	0x000c0
 | 
			
		||||
#define MPIC_GREG_IPI_VECTOR_PRI_3	0x000d0
 | 
			
		||||
#define MPIC_GREG_SPURIOUS		0x000e0
 | 
			
		||||
#define MPIC_GREG_TIMER_FREQ		0x000f0
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 *
 | 
			
		||||
 * Timer registers
 | 
			
		||||
 */
 | 
			
		||||
#define MPIC_TIMER_BASE			0x01100
 | 
			
		||||
#define MPIC_TIMER_STRIDE		0x40
 | 
			
		||||
 | 
			
		||||
#define MPIC_TIMER_CURRENT_CNT		0x00000
 | 
			
		||||
#define MPIC_TIMER_BASE_CNT		0x00010
 | 
			
		||||
#define MPIC_TIMER_VECTOR_PRI		0x00020
 | 
			
		||||
#define MPIC_TIMER_DESTINATION		0x00030
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Per-Processor registers
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define MPIC_CPU_THISBASE		0x00000
 | 
			
		||||
#define MPIC_CPU_BASE			0x20000
 | 
			
		||||
#define MPIC_CPU_STRIDE			0x01000
 | 
			
		||||
 | 
			
		||||
#define MPIC_CPU_IPI_DISPATCH_0		0x00040
 | 
			
		||||
#define MPIC_CPU_IPI_DISPATCH_1		0x00050
 | 
			
		||||
#define MPIC_CPU_IPI_DISPATCH_2		0x00060
 | 
			
		||||
#define MPIC_CPU_IPI_DISPATCH_3		0x00070
 | 
			
		||||
#define MPIC_CPU_CURRENT_TASK_PRI	0x00080
 | 
			
		||||
#define 	MPIC_CPU_TASKPRI_MASK			0x0000000f
 | 
			
		||||
#define MPIC_CPU_WHOAMI			0x00090
 | 
			
		||||
#define 	MPIC_CPU_WHOAMI_MASK			0x0000001f
 | 
			
		||||
#define MPIC_CPU_INTACK			0x000a0
 | 
			
		||||
#define MPIC_CPU_EOI			0x000b0
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Per-source registers
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define MPIC_IRQ_BASE			0x10000
 | 
			
		||||
#define MPIC_IRQ_STRIDE			0x00020
 | 
			
		||||
#define MPIC_IRQ_VECTOR_PRI		0x00000
 | 
			
		||||
#define 	MPIC_VECPRI_MASK			0x80000000
 | 
			
		||||
#define 	MPIC_VECPRI_ACTIVITY			0x40000000	/* Read Only */
 | 
			
		||||
#define 	MPIC_VECPRI_PRIORITY_MASK		0x000f0000
 | 
			
		||||
#define 	MPIC_VECPRI_PRIORITY_SHIFT		16
 | 
			
		||||
#define 	MPIC_VECPRI_VECTOR_MASK			0x000007ff
 | 
			
		||||
#define 	MPIC_VECPRI_POLARITY_POSITIVE		0x00800000
 | 
			
		||||
#define 	MPIC_VECPRI_POLARITY_NEGATIVE		0x00000000
 | 
			
		||||
#define 	MPIC_VECPRI_POLARITY_MASK		0x00800000
 | 
			
		||||
#define 	MPIC_VECPRI_SENSE_LEVEL			0x00400000
 | 
			
		||||
#define 	MPIC_VECPRI_SENSE_EDGE			0x00000000
 | 
			
		||||
#define 	MPIC_VECPRI_SENSE_MASK			0x00400000
 | 
			
		||||
#define MPIC_IRQ_DESTINATION		0x00010
 | 
			
		||||
 | 
			
		||||
#define MPIC_MAX_IRQ_SOURCES	2048
 | 
			
		||||
#define MPIC_MAX_CPUS		32
 | 
			
		||||
#define MPIC_MAX_ISU		32
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Special vector numbers (internal use only)
 | 
			
		||||
 */
 | 
			
		||||
#define MPIC_VEC_SPURRIOUS	255
 | 
			
		||||
#define MPIC_VEC_IPI_3		254
 | 
			
		||||
#define MPIC_VEC_IPI_2		253
 | 
			
		||||
#define MPIC_VEC_IPI_1		252
 | 
			
		||||
#define MPIC_VEC_IPI_0		251
 | 
			
		||||
 | 
			
		||||
/* unused */
 | 
			
		||||
#define MPIC_VEC_TIMER_3	250
 | 
			
		||||
#define MPIC_VEC_TIMER_2	249
 | 
			
		||||
#define MPIC_VEC_TIMER_1	248
 | 
			
		||||
#define MPIC_VEC_TIMER_0	247
 | 
			
		||||
 | 
			
		||||
/* Type definition of the cascade handler */
 | 
			
		||||
typedef int (*mpic_cascade_t)(struct pt_regs *regs, void *data);
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_MPIC_BROKEN_U3
 | 
			
		||||
/* Fixup table entry */
 | 
			
		||||
struct mpic_irq_fixup
 | 
			
		||||
{
 | 
			
		||||
	u8 __iomem	*base;
 | 
			
		||||
	unsigned int   irq;
 | 
			
		||||
};
 | 
			
		||||
#endif /* CONFIG_MPIC_BROKEN_U3 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* The instance data of a given MPIC */
 | 
			
		||||
struct mpic
 | 
			
		||||
{
 | 
			
		||||
	/* The "linux" controller struct */
 | 
			
		||||
	hw_irq_controller	hc_irq;
 | 
			
		||||
#ifdef CONFIG_SMP
 | 
			
		||||
	hw_irq_controller	hc_ipi;
 | 
			
		||||
#endif
 | 
			
		||||
	const char		*name;
 | 
			
		||||
	/* Flags */
 | 
			
		||||
	unsigned int		flags;
 | 
			
		||||
	/* How many irq sources in a given ISU */
 | 
			
		||||
	unsigned int		isu_size;
 | 
			
		||||
	unsigned int		isu_shift;
 | 
			
		||||
	unsigned int		isu_mask;
 | 
			
		||||
	/* Offset of irq vector numbers */
 | 
			
		||||
	unsigned int		irq_offset;	
 | 
			
		||||
	unsigned int		irq_count;
 | 
			
		||||
	/* Offset of ipi vector numbers */
 | 
			
		||||
	unsigned int		ipi_offset;
 | 
			
		||||
	/* Number of sources */
 | 
			
		||||
	unsigned int		num_sources;
 | 
			
		||||
	/* Number of CPUs */
 | 
			
		||||
	unsigned int		num_cpus;
 | 
			
		||||
	/* cascade handler */
 | 
			
		||||
	mpic_cascade_t		cascade;
 | 
			
		||||
	void			*cascade_data;
 | 
			
		||||
	unsigned int		cascade_vec;
 | 
			
		||||
	/* senses array */
 | 
			
		||||
	unsigned char		*senses;
 | 
			
		||||
	unsigned int		senses_count;
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_MPIC_BROKEN_U3
 | 
			
		||||
	/* The fixup table */
 | 
			
		||||
	struct mpic_irq_fixup	*fixups;
 | 
			
		||||
	spinlock_t		fixup_lock;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	/* The various ioremap'ed bases */
 | 
			
		||||
	volatile u32 __iomem	*gregs;
 | 
			
		||||
	volatile u32 __iomem	*tmregs;
 | 
			
		||||
	volatile u32 __iomem	*cpuregs[MPIC_MAX_CPUS];
 | 
			
		||||
	volatile u32 __iomem	*isus[MPIC_MAX_ISU];
 | 
			
		||||
 | 
			
		||||
	/* link */
 | 
			
		||||
	struct mpic		*next;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* This is the primary controller, only that one has IPIs and
 | 
			
		||||
 * has afinity control. A non-primary MPIC always uses CPU0
 | 
			
		||||
 * registers only
 | 
			
		||||
 */
 | 
			
		||||
#define MPIC_PRIMARY			0x00000001
 | 
			
		||||
/* Set this for a big-endian MPIC */
 | 
			
		||||
#define MPIC_BIG_ENDIAN			0x00000002
 | 
			
		||||
/* Broken U3 MPIC */
 | 
			
		||||
#define MPIC_BROKEN_U3			0x00000004
 | 
			
		||||
/* Broken IPI registers (autodetected) */
 | 
			
		||||
#define MPIC_BROKEN_IPI			0x00000008
 | 
			
		||||
/* MPIC wants a reset */
 | 
			
		||||
#define MPIC_WANTS_RESET		0x00000010
 | 
			
		||||
 | 
			
		||||
/* Allocate the controller structure and setup the linux irq descs
 | 
			
		||||
 * for the range if interrupts passed in. No HW initialization is
 | 
			
		||||
 * actually performed.
 | 
			
		||||
 * 
 | 
			
		||||
 * @phys_addr:	physial base address of the MPIC
 | 
			
		||||
 * @flags:	flags, see constants above
 | 
			
		||||
 * @isu_size:	number of interrupts in an ISU. Use 0 to use a
 | 
			
		||||
 *              standard ISU-less setup (aka powermac)
 | 
			
		||||
 * @irq_offset: first irq number to assign to this mpic
 | 
			
		||||
 * @irq_count:  number of irqs to use with this mpic IRQ sources. Pass 0
 | 
			
		||||
 *	        to match the number of sources
 | 
			
		||||
 * @ipi_offset: first irq number to assign to this mpic IPI sources,
 | 
			
		||||
 *		used only on primary mpic
 | 
			
		||||
 * @senses:	array of sense values
 | 
			
		||||
 * @senses_num: number of entries in the array
 | 
			
		||||
 *
 | 
			
		||||
 * Note about the sense array. If none is passed, all interrupts are
 | 
			
		||||
 * setup to be level negative unless MPIC_BROKEN_U3 is set in which
 | 
			
		||||
 * case they are edge positive (and the array is ignored anyway).
 | 
			
		||||
 * The values in the array start at the first source of the MPIC,
 | 
			
		||||
 * that is senses[0] correspond to linux irq "irq_offset".
 | 
			
		||||
 */
 | 
			
		||||
extern struct mpic *mpic_alloc(unsigned long phys_addr,
 | 
			
		||||
			       unsigned int flags,
 | 
			
		||||
			       unsigned int isu_size,
 | 
			
		||||
			       unsigned int irq_offset,
 | 
			
		||||
			       unsigned int irq_count,
 | 
			
		||||
			       unsigned int ipi_offset,
 | 
			
		||||
			       unsigned char *senses,
 | 
			
		||||
			       unsigned int senses_num,
 | 
			
		||||
			       const char *name);
 | 
			
		||||
 | 
			
		||||
/* Assign ISUs, to call before mpic_init()
 | 
			
		||||
 *
 | 
			
		||||
 * @mpic:	controller structure as returned by mpic_alloc()
 | 
			
		||||
 * @isu_num:	ISU number
 | 
			
		||||
 * @phys_addr:	physical address of the ISU
 | 
			
		||||
 */
 | 
			
		||||
extern void mpic_assign_isu(struct mpic *mpic, unsigned int isu_num,
 | 
			
		||||
			    unsigned long phys_addr);
 | 
			
		||||
 | 
			
		||||
/* Initialize the controller. After this has been called, none of the above
 | 
			
		||||
 * should be called again for this mpic
 | 
			
		||||
 */
 | 
			
		||||
extern void mpic_init(struct mpic *mpic);
 | 
			
		||||
 | 
			
		||||
/* Setup a cascade. Currently, only one cascade is supported this
 | 
			
		||||
 * way, though you can always do a normal request_irq() and add
 | 
			
		||||
 * other cascades this way. You should call this _after_ having
 | 
			
		||||
 * added all the ISUs
 | 
			
		||||
 *
 | 
			
		||||
 * @irq_no:	"linux" irq number of the cascade (that is offset'ed vector)
 | 
			
		||||
 * @handler:	cascade handler function
 | 
			
		||||
 */
 | 
			
		||||
extern void mpic_setup_cascade(unsigned int irq_no, mpic_cascade_t hanlder,
 | 
			
		||||
			       void *data);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * All of the following functions must only be used after the
 | 
			
		||||
 * ISUs have been assigned and the controller fully initialized
 | 
			
		||||
 * with mpic_init()
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Change/Read the priority of an interrupt. Default is 8 for irqs and
 | 
			
		||||
 * 10 for IPIs. You can call this on both IPIs and IRQ numbers, but the
 | 
			
		||||
 * IPI number is then the offset'ed (linux irq number mapped to the IPI)
 | 
			
		||||
 */
 | 
			
		||||
extern void mpic_irq_set_priority(unsigned int irq, unsigned int pri);
 | 
			
		||||
extern unsigned int mpic_irq_get_priority(unsigned int irq);
 | 
			
		||||
 | 
			
		||||
/* Setup a non-boot CPU */
 | 
			
		||||
extern void mpic_setup_this_cpu(void);
 | 
			
		||||
 | 
			
		||||
/* Clean up for kexec (or cpu offline or ...) */
 | 
			
		||||
extern void mpic_teardown_this_cpu(int secondary);
 | 
			
		||||
 | 
			
		||||
/* Get the current cpu priority for this cpu (0..15) */
 | 
			
		||||
extern int mpic_cpu_get_priority(void);
 | 
			
		||||
 | 
			
		||||
/* Set the current cpu priority for this cpu */
 | 
			
		||||
extern void mpic_cpu_set_priority(int prio);
 | 
			
		||||
 | 
			
		||||
/* Request IPIs on primary mpic */
 | 
			
		||||
extern void mpic_request_ipis(void);
 | 
			
		||||
 | 
			
		||||
/* Send an IPI (non offseted number 0..3) */
 | 
			
		||||
extern void mpic_send_ipi(unsigned int ipi_no, unsigned int cpu_mask);
 | 
			
		||||
 | 
			
		||||
/* Fetch interrupt from a given mpic */
 | 
			
		||||
extern int mpic_get_one_irq(struct mpic *mpic, struct pt_regs *regs);
 | 
			
		||||
/* This one gets to the primary mpic */
 | 
			
		||||
extern int mpic_get_irq(struct pt_regs *regs);
 | 
			
		||||
 | 
			
		||||
/* global mpic for pSeries */
 | 
			
		||||
extern struct mpic *pSeries_mpic;
 | 
			
		||||
							
								
								
									
										446
									
								
								include/asm-powerpc/reg.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										446
									
								
								include/asm-powerpc/reg.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,446 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Contains the definition of registers common to all PowerPC variants.
 | 
			
		||||
 * If a register definition has been changed in a different PowerPC
 | 
			
		||||
 * variant, we will case it in #ifndef XXX ... #endif, and have the
 | 
			
		||||
 * number used in the Programming Environments Manual For 32-Bit
 | 
			
		||||
 * Implementations of the PowerPC Architecture (a.k.a. Green Book) here.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifdef __KERNEL__
 | 
			
		||||
#ifndef __ASM_PPC_REGS_H__
 | 
			
		||||
#define __ASM_PPC_REGS_H__
 | 
			
		||||
 | 
			
		||||
#include <linux/stringify.h>
 | 
			
		||||
 | 
			
		||||
/* Pickup Book E specific registers. */
 | 
			
		||||
#if defined(CONFIG_BOOKE) || defined(CONFIG_40x)
 | 
			
		||||
#include <asm/reg_booke.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Machine State Register (MSR) Fields */
 | 
			
		||||
#define MSR_SF		(1<<63)
 | 
			
		||||
#define MSR_ISF		(1<<61)
 | 
			
		||||
#define MSR_VEC		(1<<25)		/* Enable AltiVec */
 | 
			
		||||
#define MSR_POW		(1<<18)		/* Enable Power Management */
 | 
			
		||||
#define MSR_WE		(1<<18)		/* Wait State Enable */
 | 
			
		||||
#define MSR_TGPR	(1<<17)		/* TLB Update registers in use */
 | 
			
		||||
#define MSR_CE		(1<<17)		/* Critical Interrupt Enable */
 | 
			
		||||
#define MSR_ILE		(1<<16)		/* Interrupt Little Endian */
 | 
			
		||||
#define MSR_EE		(1<<15)		/* External Interrupt Enable */
 | 
			
		||||
#define MSR_PR		(1<<14)		/* Problem State / Privilege Level */
 | 
			
		||||
#define MSR_FP		(1<<13)		/* Floating Point enable */
 | 
			
		||||
#define MSR_ME		(1<<12)		/* Machine Check Enable */
 | 
			
		||||
#define MSR_FE0		(1<<11)		/* Floating Exception mode 0 */
 | 
			
		||||
#define MSR_SE		(1<<10)		/* Single Step */
 | 
			
		||||
#define MSR_BE		(1<<9)		/* Branch Trace */
 | 
			
		||||
#define MSR_DE		(1<<9)		/* Debug Exception Enable */
 | 
			
		||||
#define MSR_FE1		(1<<8)		/* Floating Exception mode 1 */
 | 
			
		||||
#define MSR_IP		(1<<6)		/* Exception prefix 0x000/0xFFF */
 | 
			
		||||
#define MSR_IR		(1<<5)		/* Instruction Relocate */
 | 
			
		||||
#define MSR_DR		(1<<4)		/* Data Relocate */
 | 
			
		||||
#define MSR_PE		(1<<3)		/* Protection Enable */
 | 
			
		||||
#define MSR_PX		(1<<2)		/* Protection Exclusive Mode */
 | 
			
		||||
#define MSR_RI		(1<<1)		/* Recoverable Exception */
 | 
			
		||||
#define MSR_LE		(1<<0)		/* Little Endian */
 | 
			
		||||
 | 
			
		||||
/* Default MSR for kernel mode. */
 | 
			
		||||
#ifdef CONFIG_APUS_FAST_EXCEPT
 | 
			
		||||
#define MSR_KERNEL	(MSR_ME|MSR_IP|MSR_RI|MSR_IR|MSR_DR)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef MSR_KERNEL
 | 
			
		||||
#define MSR_KERNEL	(MSR_ME|MSR_RI|MSR_IR|MSR_DR)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define MSR_USER	(MSR_KERNEL|MSR_PR|MSR_EE)
 | 
			
		||||
 | 
			
		||||
/* Floating Point Status and Control Register (FPSCR) Fields */
 | 
			
		||||
#define FPSCR_FX	0x80000000	/* FPU exception summary */
 | 
			
		||||
#define FPSCR_FEX	0x40000000	/* FPU enabled exception summary */
 | 
			
		||||
#define FPSCR_VX	0x20000000	/* Invalid operation summary */
 | 
			
		||||
#define FPSCR_OX	0x10000000	/* Overflow exception summary */
 | 
			
		||||
#define FPSCR_UX	0x08000000	/* Underflow exception summary */
 | 
			
		||||
#define FPSCR_ZX	0x04000000	/* Zero-divide exception summary */
 | 
			
		||||
#define FPSCR_XX	0x02000000	/* Inexact exception summary */
 | 
			
		||||
#define FPSCR_VXSNAN	0x01000000	/* Invalid op for SNaN */
 | 
			
		||||
#define FPSCR_VXISI	0x00800000	/* Invalid op for Inv - Inv */
 | 
			
		||||
#define FPSCR_VXIDI	0x00400000	/* Invalid op for Inv / Inv */
 | 
			
		||||
#define FPSCR_VXZDZ	0x00200000	/* Invalid op for Zero / Zero */
 | 
			
		||||
#define FPSCR_VXIMZ	0x00100000	/* Invalid op for Inv * Zero */
 | 
			
		||||
#define FPSCR_VXVC	0x00080000	/* Invalid op for Compare */
 | 
			
		||||
#define FPSCR_FR	0x00040000	/* Fraction rounded */
 | 
			
		||||
#define FPSCR_FI	0x00020000	/* Fraction inexact */
 | 
			
		||||
#define FPSCR_FPRF	0x0001f000	/* FPU Result Flags */
 | 
			
		||||
#define FPSCR_FPCC	0x0000f000	/* FPU Condition Codes */
 | 
			
		||||
#define FPSCR_VXSOFT	0x00000400	/* Invalid op for software request */
 | 
			
		||||
#define FPSCR_VXSQRT	0x00000200	/* Invalid op for square root */
 | 
			
		||||
#define FPSCR_VXCVI	0x00000100	/* Invalid op for integer convert */
 | 
			
		||||
#define FPSCR_VE	0x00000080	/* Invalid op exception enable */
 | 
			
		||||
#define FPSCR_OE	0x00000040	/* IEEE overflow exception enable */
 | 
			
		||||
#define FPSCR_UE	0x00000020	/* IEEE underflow exception enable */
 | 
			
		||||
#define FPSCR_ZE	0x00000010	/* IEEE zero divide exception enable */
 | 
			
		||||
#define FPSCR_XE	0x00000008	/* FP inexact exception enable */
 | 
			
		||||
#define FPSCR_NI	0x00000004	/* FPU non IEEE-Mode */
 | 
			
		||||
#define FPSCR_RN	0x00000003	/* FPU rounding control */
 | 
			
		||||
 | 
			
		||||
/* Special Purpose Registers (SPRNs)*/
 | 
			
		||||
#define SPRN_CTR	0x009	/* Count Register */
 | 
			
		||||
#define SPRN_DABR	0x3F5	/* Data Address Breakpoint Register */
 | 
			
		||||
#define   DABR_TRANSLATION	(1UL << 2)
 | 
			
		||||
#define SPRN_DAR	0x013	/* Data Address Register */
 | 
			
		||||
#define	SPRN_DSISR	0x012	/* Data Storage Interrupt Status Register */
 | 
			
		||||
#define   DSISR_NOHPTE		0x40000000	/* no translation found */
 | 
			
		||||
#define   DSISR_PROTFAULT	0x08000000	/* protection fault */
 | 
			
		||||
#define   DSISR_ISSTORE		0x02000000	/* access was a store */
 | 
			
		||||
#define   DSISR_DABRMATCH	0x00400000	/* hit data breakpoint */
 | 
			
		||||
#define   DSISR_NOSEGMENT	0x00200000	/* STAB/SLB miss */
 | 
			
		||||
#define SPRN_TBRL	0x10C	/* Time Base Read Lower Register (user, R/O) */
 | 
			
		||||
#define SPRN_TBRU	0x10D	/* Time Base Read Upper Register (user, R/O) */
 | 
			
		||||
#define SPRN_TBWL	0x11C	/* Time Base Lower Register (super, R/W) */
 | 
			
		||||
#define SPRN_TBWU	0x11D	/* Time Base Upper Register (super, R/W) */
 | 
			
		||||
#define SPRN_HIOR	0x137	/* 970 Hypervisor interrupt offset */
 | 
			
		||||
#define SPRN_DBAT0L	0x219	/* Data BAT 0 Lower Register */
 | 
			
		||||
#define SPRN_DBAT0U	0x218	/* Data BAT 0 Upper Register */
 | 
			
		||||
#define SPRN_DBAT1L	0x21B	/* Data BAT 1 Lower Register */
 | 
			
		||||
#define SPRN_DBAT1U	0x21A	/* Data BAT 1 Upper Register */
 | 
			
		||||
#define SPRN_DBAT2L	0x21D	/* Data BAT 2 Lower Register */
 | 
			
		||||
#define SPRN_DBAT2U	0x21C	/* Data BAT 2 Upper Register */
 | 
			
		||||
#define SPRN_DBAT3L	0x21F	/* Data BAT 3 Lower Register */
 | 
			
		||||
#define SPRN_DBAT3U	0x21E	/* Data BAT 3 Upper Register */
 | 
			
		||||
#define SPRN_DBAT4L	0x239	/* Data BAT 4 Lower Register */
 | 
			
		||||
#define SPRN_DBAT4U	0x238	/* Data BAT 4 Upper Register */
 | 
			
		||||
#define SPRN_DBAT5L	0x23B	/* Data BAT 5 Lower Register */
 | 
			
		||||
#define SPRN_DBAT5U	0x23A	/* Data BAT 5 Upper Register */
 | 
			
		||||
#define SPRN_DBAT6L	0x23D	/* Data BAT 6 Lower Register */
 | 
			
		||||
#define SPRN_DBAT6U	0x23C	/* Data BAT 6 Upper Register */
 | 
			
		||||
#define SPRN_DBAT7L	0x23F	/* Data BAT 7 Lower Register */
 | 
			
		||||
#define SPRN_DBAT7U	0x23E	/* Data BAT 7 Upper Register */
 | 
			
		||||
 | 
			
		||||
#define SPRN_DEC	0x016		/* Decrement Register */
 | 
			
		||||
#define SPRN_DER	0x095		/* Debug Enable Regsiter */
 | 
			
		||||
#define DER_RSTE	0x40000000	/* Reset Interrupt */
 | 
			
		||||
#define DER_CHSTPE	0x20000000	/* Check Stop */
 | 
			
		||||
#define DER_MCIE	0x10000000	/* Machine Check Interrupt */
 | 
			
		||||
#define DER_EXTIE	0x02000000	/* External Interrupt */
 | 
			
		||||
#define DER_ALIE	0x01000000	/* Alignment Interrupt */
 | 
			
		||||
#define DER_PRIE	0x00800000	/* Program Interrupt */
 | 
			
		||||
#define DER_FPUVIE	0x00400000	/* FP Unavailable Interrupt */
 | 
			
		||||
#define DER_DECIE	0x00200000	/* Decrementer Interrupt */
 | 
			
		||||
#define DER_SYSIE	0x00040000	/* System Call Interrupt */
 | 
			
		||||
#define DER_TRE		0x00020000	/* Trace Interrupt */
 | 
			
		||||
#define DER_SEIE	0x00004000	/* FP SW Emulation Interrupt */
 | 
			
		||||
#define DER_ITLBMSE	0x00002000	/* Imp. Spec. Instruction TLB Miss */
 | 
			
		||||
#define DER_ITLBERE	0x00001000	/* Imp. Spec. Instruction TLB Error */
 | 
			
		||||
#define DER_DTLBMSE	0x00000800	/* Imp. Spec. Data TLB Miss */
 | 
			
		||||
#define DER_DTLBERE	0x00000400	/* Imp. Spec. Data TLB Error */
 | 
			
		||||
#define DER_LBRKE	0x00000008	/* Load/Store Breakpoint Interrupt */
 | 
			
		||||
#define DER_IBRKE	0x00000004	/* Instruction Breakpoint Interrupt */
 | 
			
		||||
#define DER_EBRKE	0x00000002	/* External Breakpoint Interrupt */
 | 
			
		||||
#define DER_DPIE	0x00000001	/* Dev. Port Nonmaskable Request */
 | 
			
		||||
#define SPRN_DMISS	0x3D0		/* Data TLB Miss Register */
 | 
			
		||||
#define SPRN_EAR	0x11A		/* External Address Register */
 | 
			
		||||
#define SPRN_HASH1	0x3D2		/* Primary Hash Address Register */
 | 
			
		||||
#define SPRN_HASH2	0x3D3		/* Secondary Hash Address Resgister */
 | 
			
		||||
#define SPRN_HID0	0x3F0		/* Hardware Implementation Register 0 */
 | 
			
		||||
#define HID0_EMCP	(1<<31)		/* Enable Machine Check pin */
 | 
			
		||||
#define HID0_EBA	(1<<29)		/* Enable Bus Address Parity */
 | 
			
		||||
#define HID0_EBD	(1<<28)		/* Enable Bus Data Parity */
 | 
			
		||||
#define HID0_SBCLK	(1<<27)
 | 
			
		||||
#define HID0_EICE	(1<<26)
 | 
			
		||||
#define HID0_TBEN	(1<<26)		/* Timebase enable - 745x */
 | 
			
		||||
#define HID0_ECLK	(1<<25)
 | 
			
		||||
#define HID0_PAR	(1<<24)
 | 
			
		||||
#define HID0_STEN	(1<<24)		/* Software table search enable - 745x */
 | 
			
		||||
#define HID0_HIGH_BAT	(1<<23)		/* Enable high BATs - 7455 */
 | 
			
		||||
#define HID0_DOZE	(1<<23)
 | 
			
		||||
#define HID0_NAP	(1<<22)
 | 
			
		||||
#define HID0_SLEEP	(1<<21)
 | 
			
		||||
#define HID0_DPM	(1<<20)
 | 
			
		||||
#define HID0_BHTCLR	(1<<18)		/* Clear branch history table - 7450 */
 | 
			
		||||
#define HID0_XAEN	(1<<17)		/* Extended addressing enable - 7450 */
 | 
			
		||||
#define HID0_NHR	(1<<16)		/* Not hard reset (software bit-7450)*/
 | 
			
		||||
#define HID0_ICE	(1<<15)		/* Instruction Cache Enable */
 | 
			
		||||
#define HID0_DCE	(1<<14)		/* Data Cache Enable */
 | 
			
		||||
#define HID0_ILOCK	(1<<13)		/* Instruction Cache Lock */
 | 
			
		||||
#define HID0_DLOCK	(1<<12)		/* Data Cache Lock */
 | 
			
		||||
#define HID0_ICFI	(1<<11)		/* Instr. Cache Flash Invalidate */
 | 
			
		||||
#define HID0_DCI	(1<<10)		/* Data Cache Invalidate */
 | 
			
		||||
#define HID0_SPD	(1<<9)		/* Speculative disable */
 | 
			
		||||
#define HID0_DAPUEN	(1<<8)		/* Debug APU enable */
 | 
			
		||||
#define HID0_SGE	(1<<7)		/* Store Gathering Enable */
 | 
			
		||||
#define HID0_SIED	(1<<7)		/* Serial Instr. Execution [Disable] */
 | 
			
		||||
#define HID0_DFCA	(1<<6)		/* Data Cache Flush Assist */
 | 
			
		||||
#define HID0_LRSTK	(1<<4)		/* Link register stack - 745x */
 | 
			
		||||
#define HID0_BTIC	(1<<5)		/* Branch Target Instr Cache Enable */
 | 
			
		||||
#define HID0_ABE	(1<<3)		/* Address Broadcast Enable */
 | 
			
		||||
#define HID0_FOLD	(1<<3)		/* Branch Folding enable - 745x */
 | 
			
		||||
#define HID0_BHTE	(1<<2)		/* Branch History Table Enable */
 | 
			
		||||
#define HID0_BTCD	(1<<1)		/* Branch target cache disable */
 | 
			
		||||
#define HID0_NOPDST	(1<<1)		/* No-op dst, dstt, etc. instr. */
 | 
			
		||||
#define HID0_NOPTI	(1<<0)		/* No-op dcbt and dcbst instr. */
 | 
			
		||||
 | 
			
		||||
#define SPRN_HID1	0x3F1		/* Hardware Implementation Register 1 */
 | 
			
		||||
#define HID1_EMCP	(1<<31)		/* 7450 Machine Check Pin Enable */
 | 
			
		||||
#define HID1_DFS	(1<<22)		/* 7447A Dynamic Frequency Scaling */
 | 
			
		||||
#define HID1_PC0	(1<<16)		/* 7450 PLL_CFG[0] */
 | 
			
		||||
#define HID1_PC1	(1<<15)		/* 7450 PLL_CFG[1] */
 | 
			
		||||
#define HID1_PC2	(1<<14)		/* 7450 PLL_CFG[2] */
 | 
			
		||||
#define HID1_PC3	(1<<13)		/* 7450 PLL_CFG[3] */
 | 
			
		||||
#define HID1_SYNCBE	(1<<11)		/* 7450 ABE for sync, eieio */
 | 
			
		||||
#define HID1_ABE	(1<<10)		/* 7450 Address Broadcast Enable */
 | 
			
		||||
#define HID1_PS		(1<<16)		/* 750FX PLL selection */
 | 
			
		||||
#define SPRN_HID2	0x3F8		/* Hardware Implementation Register 2 */
 | 
			
		||||
#define SPRN_IABR	0x3F2	/* Instruction Address Breakpoint Register */
 | 
			
		||||
#define SPRN_HID4	0x3F4		/* 970 HID4 */
 | 
			
		||||
#define SPRN_HID5	0x3F6		/* 970 HID5 */
 | 
			
		||||
#if !defined(SPRN_IAC1) && !defined(SPRN_IAC2)
 | 
			
		||||
#define SPRN_IAC1	0x3F4		/* Instruction Address Compare 1 */
 | 
			
		||||
#define SPRN_IAC2	0x3F5		/* Instruction Address Compare 2 */
 | 
			
		||||
#endif
 | 
			
		||||
#define SPRN_IBAT0L	0x211		/* Instruction BAT 0 Lower Register */
 | 
			
		||||
#define SPRN_IBAT0U	0x210		/* Instruction BAT 0 Upper Register */
 | 
			
		||||
#define SPRN_IBAT1L	0x213		/* Instruction BAT 1 Lower Register */
 | 
			
		||||
#define SPRN_IBAT1U	0x212		/* Instruction BAT 1 Upper Register */
 | 
			
		||||
#define SPRN_IBAT2L	0x215		/* Instruction BAT 2 Lower Register */
 | 
			
		||||
#define SPRN_IBAT2U	0x214		/* Instruction BAT 2 Upper Register */
 | 
			
		||||
#define SPRN_IBAT3L	0x217		/* Instruction BAT 3 Lower Register */
 | 
			
		||||
#define SPRN_IBAT3U	0x216		/* Instruction BAT 3 Upper Register */
 | 
			
		||||
#define SPRN_IBAT4L	0x231		/* Instruction BAT 4 Lower Register */
 | 
			
		||||
#define SPRN_IBAT4U	0x230		/* Instruction BAT 4 Upper Register */
 | 
			
		||||
#define SPRN_IBAT5L	0x233		/* Instruction BAT 5 Lower Register */
 | 
			
		||||
#define SPRN_IBAT5U	0x232		/* Instruction BAT 5 Upper Register */
 | 
			
		||||
#define SPRN_IBAT6L	0x235		/* Instruction BAT 6 Lower Register */
 | 
			
		||||
#define SPRN_IBAT6U	0x234		/* Instruction BAT 6 Upper Register */
 | 
			
		||||
#define SPRN_IBAT7L	0x237		/* Instruction BAT 7 Lower Register */
 | 
			
		||||
#define SPRN_IBAT7U	0x236		/* Instruction BAT 7 Upper Register */
 | 
			
		||||
#define SPRN_ICMP	0x3D5		/* Instruction TLB Compare Register */
 | 
			
		||||
#define SPRN_ICTC	0x3FB	/* Instruction Cache Throttling Control Reg */
 | 
			
		||||
#define SPRN_ICTRL	0x3F3	/* 1011 7450 icache and interrupt ctrl */
 | 
			
		||||
#define ICTRL_EICE	0x08000000	/* enable icache parity errs */
 | 
			
		||||
#define ICTRL_EDC	0x04000000	/* enable dcache parity errs */
 | 
			
		||||
#define ICTRL_EICP	0x00000100	/* enable icache par. check */
 | 
			
		||||
#define SPRN_IMISS	0x3D4		/* Instruction TLB Miss Register */
 | 
			
		||||
#define SPRN_IMMR	0x27E		/* Internal Memory Map Register */
 | 
			
		||||
#define SPRN_L2CR	0x3F9		/* Level 2 Cache Control Regsiter */
 | 
			
		||||
#define SPRN_L2CR2	0x3f8
 | 
			
		||||
#define L2CR_L2E		0x80000000	/* L2 enable */
 | 
			
		||||
#define L2CR_L2PE		0x40000000	/* L2 parity enable */
 | 
			
		||||
#define L2CR_L2SIZ_MASK		0x30000000	/* L2 size mask */
 | 
			
		||||
#define L2CR_L2SIZ_256KB	0x10000000	/* L2 size 256KB */
 | 
			
		||||
#define L2CR_L2SIZ_512KB	0x20000000	/* L2 size 512KB */
 | 
			
		||||
#define L2CR_L2SIZ_1MB		0x30000000	/* L2 size 1MB */
 | 
			
		||||
#define L2CR_L2CLK_MASK		0x0e000000	/* L2 clock mask */
 | 
			
		||||
#define L2CR_L2CLK_DISABLED	0x00000000	/* L2 clock disabled */
 | 
			
		||||
#define L2CR_L2CLK_DIV1		0x02000000	/* L2 clock / 1 */
 | 
			
		||||
#define L2CR_L2CLK_DIV1_5	0x04000000	/* L2 clock / 1.5 */
 | 
			
		||||
#define L2CR_L2CLK_DIV2		0x08000000	/* L2 clock / 2 */
 | 
			
		||||
#define L2CR_L2CLK_DIV2_5	0x0a000000	/* L2 clock / 2.5 */
 | 
			
		||||
#define L2CR_L2CLK_DIV3		0x0c000000	/* L2 clock / 3 */
 | 
			
		||||
#define L2CR_L2RAM_MASK		0x01800000	/* L2 RAM type mask */
 | 
			
		||||
#define L2CR_L2RAM_FLOW		0x00000000	/* L2 RAM flow through */
 | 
			
		||||
#define L2CR_L2RAM_PIPE		0x01000000	/* L2 RAM pipelined */
 | 
			
		||||
#define L2CR_L2RAM_PIPE_LW	0x01800000	/* L2 RAM pipelined latewr */
 | 
			
		||||
#define L2CR_L2DO		0x00400000	/* L2 data only */
 | 
			
		||||
#define L2CR_L2I		0x00200000	/* L2 global invalidate */
 | 
			
		||||
#define L2CR_L2CTL		0x00100000	/* L2 RAM control */
 | 
			
		||||
#define L2CR_L2WT		0x00080000	/* L2 write-through */
 | 
			
		||||
#define L2CR_L2TS		0x00040000	/* L2 test support */
 | 
			
		||||
#define L2CR_L2OH_MASK		0x00030000	/* L2 output hold mask */
 | 
			
		||||
#define L2CR_L2OH_0_5		0x00000000	/* L2 output hold 0.5 ns */
 | 
			
		||||
#define L2CR_L2OH_1_0		0x00010000	/* L2 output hold 1.0 ns */
 | 
			
		||||
#define L2CR_L2SL		0x00008000	/* L2 DLL slow */
 | 
			
		||||
#define L2CR_L2DF		0x00004000	/* L2 differential clock */
 | 
			
		||||
#define L2CR_L2BYP		0x00002000	/* L2 DLL bypass */
 | 
			
		||||
#define L2CR_L2IP		0x00000001	/* L2 GI in progress */
 | 
			
		||||
#define L2CR_L2IO_745x		0x00100000	/* L2 instr. only (745x) */
 | 
			
		||||
#define L2CR_L2DO_745x		0x00010000	/* L2 data only (745x) */
 | 
			
		||||
#define L2CR_L2REP_745x		0x00001000	/* L2 repl. algorithm (745x) */
 | 
			
		||||
#define L2CR_L2HWF_745x		0x00000800	/* L2 hardware flush (745x) */
 | 
			
		||||
#define SPRN_L3CR		0x3FA	/* Level 3 Cache Control Regsiter */
 | 
			
		||||
#define L3CR_L3E		0x80000000	/* L3 enable */
 | 
			
		||||
#define L3CR_L3PE		0x40000000	/* L3 data parity enable */
 | 
			
		||||
#define L3CR_L3APE		0x20000000	/* L3 addr parity enable */
 | 
			
		||||
#define L3CR_L3SIZ		0x10000000	/* L3 size */
 | 
			
		||||
#define L3CR_L3CLKEN		0x08000000	/* L3 clock enable */
 | 
			
		||||
#define L3CR_L3RES		0x04000000	/* L3 special reserved bit */
 | 
			
		||||
#define L3CR_L3CLKDIV		0x03800000	/* L3 clock divisor */
 | 
			
		||||
#define L3CR_L3IO		0x00400000	/* L3 instruction only */
 | 
			
		||||
#define L3CR_L3SPO		0x00040000	/* L3 sample point override */
 | 
			
		||||
#define L3CR_L3CKSP		0x00030000	/* L3 clock sample point */
 | 
			
		||||
#define L3CR_L3PSP		0x0000e000	/* L3 P-clock sample point */
 | 
			
		||||
#define L3CR_L3REP		0x00001000	/* L3 replacement algorithm */
 | 
			
		||||
#define L3CR_L3HWF		0x00000800	/* L3 hardware flush */
 | 
			
		||||
#define L3CR_L3I		0x00000400	/* L3 global invalidate */
 | 
			
		||||
#define L3CR_L3RT		0x00000300	/* L3 SRAM type */
 | 
			
		||||
#define L3CR_L3NIRCA		0x00000080	/* L3 non-integer ratio clock adj. */
 | 
			
		||||
#define L3CR_L3DO		0x00000040	/* L3 data only mode */
 | 
			
		||||
#define L3CR_PMEN		0x00000004	/* L3 private memory enable */
 | 
			
		||||
#define L3CR_PMSIZ		0x00000001	/* L3 private memory size */
 | 
			
		||||
#define SPRN_MSSCR0	0x3f6	/* Memory Subsystem Control Register 0 */
 | 
			
		||||
#define SPRN_MSSSR0	0x3f7	/* Memory Subsystem Status Register 1 */
 | 
			
		||||
#define SPRN_LDSTCR	0x3f8	/* Load/Store control register */
 | 
			
		||||
#define SPRN_LDSTDB	0x3f4	/* */
 | 
			
		||||
#define SPRN_LR		0x008	/* Link Register */
 | 
			
		||||
#define SPRN_MMCR0	0x3B8	/* Monitor Mode Control Register 0 */
 | 
			
		||||
#define SPRN_MMCR1	0x3BC	/* Monitor Mode Control Register 1 */
 | 
			
		||||
#ifndef SPRN_PIR
 | 
			
		||||
#define SPRN_PIR	0x3FF	/* Processor Identification Register */
 | 
			
		||||
#endif
 | 
			
		||||
#define SPRN_PMC1	0x3B9	/* Performance Counter Register 1 */
 | 
			
		||||
#define SPRN_PMC2	0x3BA	/* Performance Counter Register 2 */
 | 
			
		||||
#define SPRN_PMC3	0x3BD	/* Performance Counter Register 3 */
 | 
			
		||||
#define SPRN_PMC4	0x3BE	/* Performance Counter Register 4 */
 | 
			
		||||
#define SPRN_PTEHI	0x3D5	/* 981 7450 PTE HI word (S/W TLB load) */
 | 
			
		||||
#define SPRN_PTELO	0x3D6	/* 982 7450 PTE LO word (S/W TLB load) */
 | 
			
		||||
#define SPRN_PVR	0x11F	/* Processor Version Register */
 | 
			
		||||
#define SPRN_RPA	0x3D6	/* Required Physical Address Register */
 | 
			
		||||
#define SPRN_SDA	0x3BF	/* Sampled Data Address Register */
 | 
			
		||||
#define SPRN_SDR1	0x019	/* MMU Hash Base Register */
 | 
			
		||||
#define SPRN_SIA	0x3BB	/* Sampled Instruction Address Register */
 | 
			
		||||
#define SPRN_SPRG0	0x110	/* Special Purpose Register General 0 */
 | 
			
		||||
#define SPRN_SPRG1	0x111	/* Special Purpose Register General 1 */
 | 
			
		||||
#define SPRN_SPRG2	0x112	/* Special Purpose Register General 2 */
 | 
			
		||||
#define SPRN_SPRG3	0x113	/* Special Purpose Register General 3 */
 | 
			
		||||
#define SPRN_SPRG4	0x114	/* Special Purpose Register General 4 */
 | 
			
		||||
#define SPRN_SPRG5	0x115	/* Special Purpose Register General 5 */
 | 
			
		||||
#define SPRN_SPRG6	0x116	/* Special Purpose Register General 6 */
 | 
			
		||||
#define SPRN_SPRG7	0x117	/* Special Purpose Register General 7 */
 | 
			
		||||
#define SPRN_SRR0	0x01A	/* Save/Restore Register 0 */
 | 
			
		||||
#define SPRN_SRR1	0x01B	/* Save/Restore Register 1 */
 | 
			
		||||
#ifndef SPRN_SVR
 | 
			
		||||
#define SPRN_SVR	0x11E	/* System Version Register */
 | 
			
		||||
#endif
 | 
			
		||||
#define SPRN_THRM1	0x3FC		/* Thermal Management Register 1 */
 | 
			
		||||
/* these bits were defined in inverted endian sense originally, ugh, confusing */
 | 
			
		||||
#define THRM1_TIN	(1 << 31)
 | 
			
		||||
#define THRM1_TIV	(1 << 30)
 | 
			
		||||
#define THRM1_THRES(x)	((x&0x7f)<<23)
 | 
			
		||||
#define THRM3_SITV(x)	((x&0x3fff)<<1)
 | 
			
		||||
#define THRM1_TID	(1<<2)
 | 
			
		||||
#define THRM1_TIE	(1<<1)
 | 
			
		||||
#define THRM1_V		(1<<0)
 | 
			
		||||
#define SPRN_THRM2	0x3FD		/* Thermal Management Register 2 */
 | 
			
		||||
#define SPRN_THRM3	0x3FE		/* Thermal Management Register 3 */
 | 
			
		||||
#define THRM3_E		(1<<0)
 | 
			
		||||
#define SPRN_TLBMISS	0x3D4		/* 980 7450 TLB Miss Register */
 | 
			
		||||
#define SPRN_UMMCR0	0x3A8	/* User Monitor Mode Control Register 0 */
 | 
			
		||||
#define SPRN_UMMCR1	0x3AC	/* User Monitor Mode Control Register 0 */
 | 
			
		||||
#define SPRN_UPMC1	0x3A9	/* User Performance Counter Register 1 */
 | 
			
		||||
#define SPRN_UPMC2	0x3AA	/* User Performance Counter Register 2 */
 | 
			
		||||
#define SPRN_UPMC3	0x3AD	/* User Performance Counter Register 3 */
 | 
			
		||||
#define SPRN_UPMC4	0x3AE	/* User Performance Counter Register 4 */
 | 
			
		||||
#define SPRN_USIA	0x3AB	/* User Sampled Instruction Address Register */
 | 
			
		||||
#define SPRN_VRSAVE	0x100	/* Vector Register Save Register */
 | 
			
		||||
#define SPRN_XER	0x001	/* Fixed Point Exception Register */
 | 
			
		||||
 | 
			
		||||
/* Bit definitions for MMCR0 and PMC1 / PMC2. */
 | 
			
		||||
#define MMCR0_PMC1_CYCLES	(1 << 7)
 | 
			
		||||
#define MMCR0_PMC1_ICACHEMISS	(5 << 7)
 | 
			
		||||
#define MMCR0_PMC1_DTLB		(6 << 7)
 | 
			
		||||
#define MMCR0_PMC2_DCACHEMISS	0x6
 | 
			
		||||
#define MMCR0_PMC2_CYCLES	0x1
 | 
			
		||||
#define MMCR0_PMC2_ITLB		0x7
 | 
			
		||||
#define MMCR0_PMC2_LOADMISSTIME	0x5
 | 
			
		||||
#define MMCR0_PMXE	(1 << 26)
 | 
			
		||||
 | 
			
		||||
/* Processor Version Register */
 | 
			
		||||
 | 
			
		||||
/* Processor Version Register (PVR) field extraction */
 | 
			
		||||
 | 
			
		||||
#define PVR_VER(pvr)	(((pvr) >>  16) & 0xFFFF)	/* Version field */
 | 
			
		||||
#define PVR_REV(pvr)	(((pvr) >>   0) & 0xFFFF)	/* Revison field */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * IBM has further subdivided the standard PowerPC 16-bit version and
 | 
			
		||||
 * revision subfields of the PVR for the PowerPC 403s into the following:
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define PVR_FAM(pvr)	(((pvr) >> 20) & 0xFFF)	/* Family field */
 | 
			
		||||
#define PVR_MEM(pvr)	(((pvr) >> 16) & 0xF)	/* Member field */
 | 
			
		||||
#define PVR_CORE(pvr)	(((pvr) >> 12) & 0xF)	/* Core field */
 | 
			
		||||
#define PVR_CFG(pvr)	(((pvr) >>  8) & 0xF)	/* Configuration field */
 | 
			
		||||
#define PVR_MAJ(pvr)	(((pvr) >>  4) & 0xF)	/* Major revision field */
 | 
			
		||||
#define PVR_MIN(pvr)	(((pvr) >>  0) & 0xF)	/* Minor revision field */
 | 
			
		||||
 | 
			
		||||
/* Processor Version Numbers */
 | 
			
		||||
 | 
			
		||||
#define PVR_403GA	0x00200000
 | 
			
		||||
#define PVR_403GB	0x00200100
 | 
			
		||||
#define PVR_403GC	0x00200200
 | 
			
		||||
#define PVR_403GCX	0x00201400
 | 
			
		||||
#define PVR_405GP	0x40110000
 | 
			
		||||
#define PVR_STB03XXX	0x40310000
 | 
			
		||||
#define PVR_NP405H	0x41410000
 | 
			
		||||
#define PVR_NP405L	0x41610000
 | 
			
		||||
#define PVR_601		0x00010000
 | 
			
		||||
#define PVR_602		0x00050000
 | 
			
		||||
#define PVR_603		0x00030000
 | 
			
		||||
#define PVR_603e	0x00060000
 | 
			
		||||
#define PVR_603ev	0x00070000
 | 
			
		||||
#define PVR_603r	0x00071000
 | 
			
		||||
#define PVR_604		0x00040000
 | 
			
		||||
#define PVR_604e	0x00090000
 | 
			
		||||
#define PVR_604r	0x000A0000
 | 
			
		||||
#define PVR_620		0x00140000
 | 
			
		||||
#define PVR_740		0x00080000
 | 
			
		||||
#define PVR_750		PVR_740
 | 
			
		||||
#define PVR_740P	0x10080000
 | 
			
		||||
#define PVR_750P	PVR_740P
 | 
			
		||||
#define PVR_7400	0x000C0000
 | 
			
		||||
#define PVR_7410	0x800C0000
 | 
			
		||||
#define PVR_7450	0x80000000
 | 
			
		||||
#define PVR_8540	0x80200000
 | 
			
		||||
#define PVR_8560	0x80200000
 | 
			
		||||
/*
 | 
			
		||||
 * For the 8xx processors, all of them report the same PVR family for
 | 
			
		||||
 * the PowerPC core. The various versions of these processors must be
 | 
			
		||||
 * differentiated by the version number in the Communication Processor
 | 
			
		||||
 * Module (CPM).
 | 
			
		||||
 */
 | 
			
		||||
#define PVR_821		0x00500000
 | 
			
		||||
#define PVR_823		PVR_821
 | 
			
		||||
#define PVR_850		PVR_821
 | 
			
		||||
#define PVR_860		PVR_821
 | 
			
		||||
#define PVR_8240	0x00810100
 | 
			
		||||
#define PVR_8245	0x80811014
 | 
			
		||||
#define PVR_8260	PVR_8240
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
/* Segment Registers */
 | 
			
		||||
#define SR0	0
 | 
			
		||||
#define SR1	1
 | 
			
		||||
#define SR2	2
 | 
			
		||||
#define SR3	3
 | 
			
		||||
#define SR4	4
 | 
			
		||||
#define SR5	5
 | 
			
		||||
#define SR6	6
 | 
			
		||||
#define SR7	7
 | 
			
		||||
#define SR8	8
 | 
			
		||||
#define SR9	9
 | 
			
		||||
#define SR10	10
 | 
			
		||||
#define SR11	11
 | 
			
		||||
#define SR12	12
 | 
			
		||||
#define SR13	13
 | 
			
		||||
#define SR14	14
 | 
			
		||||
#define SR15	15
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Macros for setting and retrieving special purpose registers */
 | 
			
		||||
#ifndef __ASSEMBLY__
 | 
			
		||||
#define mfmsr()		({unsigned int rval; \
 | 
			
		||||
			asm volatile("mfmsr %0" : "=r" (rval)); rval;})
 | 
			
		||||
#define mtmsr(v)	asm volatile("mtmsr %0" : : "r" (v))
 | 
			
		||||
 | 
			
		||||
#define mfspr(rn)	({unsigned int rval; \
 | 
			
		||||
			asm volatile("mfspr %0," __stringify(rn) \
 | 
			
		||||
				: "=r" (rval)); rval;})
 | 
			
		||||
#define mtspr(rn, v)	asm volatile("mtspr " __stringify(rn) ",%0" : : "r" (v))
 | 
			
		||||
 | 
			
		||||
#define mfsrin(v)	({unsigned int rval; \
 | 
			
		||||
			asm volatile("mfsrin %0,%1" : "=r" (rval) : "r" (v)); \
 | 
			
		||||
					rval;})
 | 
			
		||||
 | 
			
		||||
#define proc_trap()	asm volatile("trap")
 | 
			
		||||
#endif /* __ASSEMBLY__ */
 | 
			
		||||
#endif /* __ASM_PPC_REGS_H__ */
 | 
			
		||||
#endif /* __KERNEL__ */
 | 
			
		||||
							
								
								
									
										350
									
								
								include/asm-powerpc/system.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										350
									
								
								include/asm-powerpc/system.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,350 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 1999 Cort Dougan <cort@cs.nmt.edu>
 | 
			
		||||
 */
 | 
			
		||||
#ifndef __PPC_SYSTEM_H
 | 
			
		||||
#define __PPC_SYSTEM_H
 | 
			
		||||
 | 
			
		||||
#include <linux/config.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
 | 
			
		||||
#include <asm/hw_irq.h>
 | 
			
		||||
#include <asm/ppc_asm.h>
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Memory barrier.
 | 
			
		||||
 * The sync instruction guarantees that all memory accesses initiated
 | 
			
		||||
 * by this processor have been performed (with respect to all other
 | 
			
		||||
 * mechanisms that access memory).  The eieio instruction is a barrier
 | 
			
		||||
 * providing an ordering (separately) for (a) cacheable stores and (b)
 | 
			
		||||
 * loads and stores to non-cacheable memory (e.g. I/O devices).
 | 
			
		||||
 *
 | 
			
		||||
 * mb() prevents loads and stores being reordered across this point.
 | 
			
		||||
 * rmb() prevents loads being reordered across this point.
 | 
			
		||||
 * wmb() prevents stores being reordered across this point.
 | 
			
		||||
 * read_barrier_depends() prevents data-dependent loads being reordered
 | 
			
		||||
 *	across this point (nop on PPC).
 | 
			
		||||
 *
 | 
			
		||||
 * We have to use the sync instructions for mb(), since lwsync doesn't
 | 
			
		||||
 * order loads with respect to previous stores.  Lwsync is fine for
 | 
			
		||||
 * rmb(), though.  Note that lwsync is interpreted as sync by
 | 
			
		||||
 * 32-bit and older 64-bit CPUs.
 | 
			
		||||
 *
 | 
			
		||||
 * For wmb(), we use sync since wmb is used in drivers to order
 | 
			
		||||
 * stores to system memory with respect to writes to the device.
 | 
			
		||||
 * However, smp_wmb() can be a lighter-weight eieio barrier on
 | 
			
		||||
 * SMP since it is only used to order updates to system memory.
 | 
			
		||||
 */
 | 
			
		||||
#define mb()   __asm__ __volatile__ ("sync" : : : "memory")
 | 
			
		||||
#define rmb()  __asm__ __volatile__ ("lwsync" : : : "memory")
 | 
			
		||||
#define wmb()  __asm__ __volatile__ ("sync" : : : "memory")
 | 
			
		||||
#define read_barrier_depends()  do { } while(0)
 | 
			
		||||
 | 
			
		||||
#define set_mb(var, value)	do { var = value; mb(); } while (0)
 | 
			
		||||
#define set_wmb(var, value)	do { var = value; wmb(); } while (0)
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_SMP
 | 
			
		||||
#define smp_mb()	mb()
 | 
			
		||||
#define smp_rmb()	rmb()
 | 
			
		||||
#define smp_wmb()	__asm__ __volatile__ ("eieio" : : : "memory")
 | 
			
		||||
#define smp_read_barrier_depends()	read_barrier_depends()
 | 
			
		||||
#else
 | 
			
		||||
#define smp_mb()	barrier()
 | 
			
		||||
#define smp_rmb()	barrier()
 | 
			
		||||
#define smp_wmb()	barrier()
 | 
			
		||||
#define smp_read_barrier_depends()	do { } while(0)
 | 
			
		||||
#endif /* CONFIG_SMP */
 | 
			
		||||
 | 
			
		||||
#ifdef __KERNEL__
 | 
			
		||||
struct task_struct;
 | 
			
		||||
struct pt_regs;
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_DEBUGGER
 | 
			
		||||
 | 
			
		||||
extern int (*__debugger)(struct pt_regs *regs);
 | 
			
		||||
extern int (*__debugger_ipi)(struct pt_regs *regs);
 | 
			
		||||
extern int (*__debugger_bpt)(struct pt_regs *regs);
 | 
			
		||||
extern int (*__debugger_sstep)(struct pt_regs *regs);
 | 
			
		||||
extern int (*__debugger_iabr_match)(struct pt_regs *regs);
 | 
			
		||||
extern int (*__debugger_dabr_match)(struct pt_regs *regs);
 | 
			
		||||
extern int (*__debugger_fault_handler)(struct pt_regs *regs);
 | 
			
		||||
 | 
			
		||||
#define DEBUGGER_BOILERPLATE(__NAME) \
 | 
			
		||||
static inline int __NAME(struct pt_regs *regs) \
 | 
			
		||||
{ \
 | 
			
		||||
	if (unlikely(__ ## __NAME)) \
 | 
			
		||||
		return __ ## __NAME(regs); \
 | 
			
		||||
	return 0; \
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEBUGGER_BOILERPLATE(debugger)
 | 
			
		||||
DEBUGGER_BOILERPLATE(debugger_ipi)
 | 
			
		||||
DEBUGGER_BOILERPLATE(debugger_bpt)
 | 
			
		||||
DEBUGGER_BOILERPLATE(debugger_sstep)
 | 
			
		||||
DEBUGGER_BOILERPLATE(debugger_iabr_match)
 | 
			
		||||
DEBUGGER_BOILERPLATE(debugger_dabr_match)
 | 
			
		||||
DEBUGGER_BOILERPLATE(debugger_fault_handler)
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_XMON
 | 
			
		||||
extern void xmon_init(int enable);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
static inline int debugger(struct pt_regs *regs) { return 0; }
 | 
			
		||||
static inline int debugger_ipi(struct pt_regs *regs) { return 0; }
 | 
			
		||||
static inline int debugger_bpt(struct pt_regs *regs) { return 0; }
 | 
			
		||||
static inline int debugger_sstep(struct pt_regs *regs) { return 0; }
 | 
			
		||||
static inline int debugger_iabr_match(struct pt_regs *regs) { return 0; }
 | 
			
		||||
static inline int debugger_dabr_match(struct pt_regs *regs) { return 0; }
 | 
			
		||||
static inline int debugger_fault_handler(struct pt_regs *regs) { return 0; }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
extern int set_dabr(unsigned long dabr);
 | 
			
		||||
extern void print_backtrace(unsigned long *);
 | 
			
		||||
extern void show_regs(struct pt_regs * regs);
 | 
			
		||||
extern void flush_instruction_cache(void);
 | 
			
		||||
extern void hard_reset_now(void);
 | 
			
		||||
extern void poweroff_now(void);
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_6xx
 | 
			
		||||
extern long _get_L2CR(void);
 | 
			
		||||
extern long _get_L3CR(void);
 | 
			
		||||
extern void _set_L2CR(unsigned long);
 | 
			
		||||
extern void _set_L3CR(unsigned long);
 | 
			
		||||
#else
 | 
			
		||||
#define _get_L2CR()	0L
 | 
			
		||||
#define _get_L3CR()	0L
 | 
			
		||||
#define _set_L2CR(val)	do { } while(0)
 | 
			
		||||
#define _set_L3CR(val)	do { } while(0)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
extern void via_cuda_init(void);
 | 
			
		||||
extern void pmac_nvram_init(void);
 | 
			
		||||
extern void read_rtc_time(void);
 | 
			
		||||
extern void pmac_find_display(void);
 | 
			
		||||
extern void giveup_fpu(struct task_struct *);
 | 
			
		||||
extern void enable_kernel_fp(void);
 | 
			
		||||
extern void flush_fp_to_thread(struct task_struct *);
 | 
			
		||||
extern void enable_kernel_altivec(void);
 | 
			
		||||
extern void giveup_altivec(struct task_struct *);
 | 
			
		||||
extern void load_up_altivec(struct task_struct *);
 | 
			
		||||
extern void giveup_spe(struct task_struct *);
 | 
			
		||||
extern void load_up_spe(struct task_struct *);
 | 
			
		||||
extern int fix_alignment(struct pt_regs *);
 | 
			
		||||
extern void cvt_fd(float *from, double *to, unsigned long *fpscr);
 | 
			
		||||
extern void cvt_df(double *from, float *to, unsigned long *fpscr);
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_ALTIVEC
 | 
			
		||||
extern void flush_altivec_to_thread(struct task_struct *);
 | 
			
		||||
#else
 | 
			
		||||
static inline void flush_altivec_to_thread(struct task_struct *t)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_SPE
 | 
			
		||||
extern void flush_spe_to_thread(struct task_struct *);
 | 
			
		||||
#else
 | 
			
		||||
static inline void flush_spe_to_thread(struct task_struct *t)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
extern int call_rtas(const char *, int, int, unsigned long *, ...);
 | 
			
		||||
extern void cacheable_memzero(void *p, unsigned int nb);
 | 
			
		||||
extern void *cacheable_memcpy(void *, const void *, unsigned int);
 | 
			
		||||
extern int do_page_fault(struct pt_regs *, unsigned long, unsigned long);
 | 
			
		||||
extern void bad_page_fault(struct pt_regs *, unsigned long, int);
 | 
			
		||||
extern int die(const char *, struct pt_regs *, long);
 | 
			
		||||
extern void _exception(int, struct pt_regs *, int, unsigned long);
 | 
			
		||||
#ifdef CONFIG_BOOKE_WDT
 | 
			
		||||
extern u32 booke_wdt_enabled;
 | 
			
		||||
extern u32 booke_wdt_period;
 | 
			
		||||
#endif /* CONFIG_BOOKE_WDT */
 | 
			
		||||
 | 
			
		||||
/* EBCDIC -> ASCII conversion for [0-9A-Z] on iSeries */
 | 
			
		||||
extern unsigned char e2a(unsigned char);
 | 
			
		||||
 | 
			
		||||
struct device_node;
 | 
			
		||||
extern void note_scsi_host(struct device_node *, void *);
 | 
			
		||||
 | 
			
		||||
extern struct task_struct *__switch_to(struct task_struct *,
 | 
			
		||||
	struct task_struct *);
 | 
			
		||||
#define switch_to(prev, next, last)	((last) = __switch_to((prev), (next)))
 | 
			
		||||
 | 
			
		||||
struct thread_struct;
 | 
			
		||||
extern struct task_struct *_switch(struct thread_struct *prev,
 | 
			
		||||
				   struct thread_struct *next);
 | 
			
		||||
 | 
			
		||||
extern unsigned int rtas_data;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Atomic exchange
 | 
			
		||||
 *
 | 
			
		||||
 * Changes the memory location '*ptr' to be val and returns
 | 
			
		||||
 * the previous value stored there.
 | 
			
		||||
 */
 | 
			
		||||
static __inline__ unsigned long
 | 
			
		||||
__xchg_u32(volatile void *p, unsigned long val)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long prev;
 | 
			
		||||
 | 
			
		||||
	__asm__ __volatile__(
 | 
			
		||||
	EIEIO_ON_SMP
 | 
			
		||||
"1:	lwarx	%0,0,%2 \n"
 | 
			
		||||
	PPC405_ERR77(0,%2)
 | 
			
		||||
"	stwcx.	%3,0,%2 \n\
 | 
			
		||||
	bne-	1b"
 | 
			
		||||
	ISYNC_ON_SMP
 | 
			
		||||
	: "=&r" (prev), "=m" (*(volatile unsigned int *)p)
 | 
			
		||||
	: "r" (p), "r" (val), "m" (*(volatile unsigned int *)p)
 | 
			
		||||
	: "cc", "memory");
 | 
			
		||||
 | 
			
		||||
	return prev;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_PPC64
 | 
			
		||||
static __inline__ unsigned long
 | 
			
		||||
__xchg_u64(volatile void *p, unsigned long val)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long prev;
 | 
			
		||||
 | 
			
		||||
	__asm__ __volatile__(
 | 
			
		||||
	EIEIO_ON_SMP
 | 
			
		||||
"1:	ldarx	%0,0,%2 \n"
 | 
			
		||||
	PPC405_ERR77(0,%2)
 | 
			
		||||
"	stdcx.	%3,0,%2 \n\
 | 
			
		||||
	bne-	1b"
 | 
			
		||||
	ISYNC_ON_SMP
 | 
			
		||||
	: "=&r" (prev), "=m" (*(volatile unsigned long *)p)
 | 
			
		||||
	: "r" (p), "r" (val), "m" (*(volatile unsigned long *)p)
 | 
			
		||||
	: "cc", "memory");
 | 
			
		||||
 | 
			
		||||
	return prev;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * This function doesn't exist, so you'll get a linker error
 | 
			
		||||
 * if something tries to do an invalid xchg().
 | 
			
		||||
 */
 | 
			
		||||
extern void __xchg_called_with_bad_pointer(void);
 | 
			
		||||
 | 
			
		||||
static __inline__ unsigned long
 | 
			
		||||
__xchg(volatile void *ptr, unsigned long x, unsigned int size)
 | 
			
		||||
{
 | 
			
		||||
	switch (size) {
 | 
			
		||||
	case 4:
 | 
			
		||||
		return __xchg_u32(ptr, x);
 | 
			
		||||
#ifdef CONFIG_PPC64
 | 
			
		||||
	case 8:
 | 
			
		||||
		return __xchg_u64(ptr, x);
 | 
			
		||||
#endif
 | 
			
		||||
	}
 | 
			
		||||
	__xchg_called_with_bad_pointer();
 | 
			
		||||
	return x;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define xchg(ptr,x)							     \
 | 
			
		||||
  ({									     \
 | 
			
		||||
     __typeof__(*(ptr)) _x_ = (x);					     \
 | 
			
		||||
     (__typeof__(*(ptr))) __xchg((ptr), (unsigned long)_x_, sizeof(*(ptr))); \
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
#define tas(ptr) (xchg((ptr),1))
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Compare and exchange - if *p == old, set it to new,
 | 
			
		||||
 * and return the old value of *p.
 | 
			
		||||
 */
 | 
			
		||||
#define __HAVE_ARCH_CMPXCHG	1
 | 
			
		||||
 | 
			
		||||
static __inline__ unsigned long
 | 
			
		||||
__cmpxchg_u32(volatile unsigned int *p, unsigned long old, unsigned long new)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int prev;
 | 
			
		||||
 | 
			
		||||
	__asm__ __volatile__ (
 | 
			
		||||
	EIEIO_ON_SMP
 | 
			
		||||
"1:	lwarx	%0,0,%2		# __cmpxchg_u32\n\
 | 
			
		||||
	cmpw	0,%0,%3\n\
 | 
			
		||||
	bne-	2f\n"
 | 
			
		||||
	PPC405_ERR77(0,%2)
 | 
			
		||||
"	stwcx.	%4,0,%2\n\
 | 
			
		||||
	bne-	1b"
 | 
			
		||||
	ISYNC_ON_SMP
 | 
			
		||||
	"\n\
 | 
			
		||||
2:"
 | 
			
		||||
	: "=&r" (prev), "=m" (*p)
 | 
			
		||||
	: "r" (p), "r" (old), "r" (new), "m" (*p)
 | 
			
		||||
	: "cc", "memory");
 | 
			
		||||
 | 
			
		||||
	return prev;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_PPC64
 | 
			
		||||
static __inline__ unsigned long
 | 
			
		||||
__cmpxchg_u64(volatile long *p, unsigned long old, unsigned long new)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long prev;
 | 
			
		||||
 | 
			
		||||
	__asm__ __volatile__ (
 | 
			
		||||
	EIEIO_ON_SMP
 | 
			
		||||
"1:	ldarx	%0,0,%2		# __cmpxchg_u64\n\
 | 
			
		||||
	cmpd	0,%0,%3\n\
 | 
			
		||||
	bne-	2f\n\
 | 
			
		||||
	stdcx.	%4,0,%2\n\
 | 
			
		||||
	bne-	1b"
 | 
			
		||||
	ISYNC_ON_SMP
 | 
			
		||||
	"\n\
 | 
			
		||||
2:"
 | 
			
		||||
	: "=&r" (prev), "=m" (*p)
 | 
			
		||||
	: "r" (p), "r" (old), "r" (new), "m" (*p)
 | 
			
		||||
	: "cc", "memory");
 | 
			
		||||
 | 
			
		||||
	return prev;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* This function doesn't exist, so you'll get a linker error
 | 
			
		||||
   if something tries to do an invalid cmpxchg().  */
 | 
			
		||||
extern void __cmpxchg_called_with_bad_pointer(void);
 | 
			
		||||
 | 
			
		||||
static __inline__ unsigned long
 | 
			
		||||
__cmpxchg(volatile void *ptr, unsigned long old, unsigned long new,
 | 
			
		||||
	  unsigned int size)
 | 
			
		||||
{
 | 
			
		||||
	switch (size) {
 | 
			
		||||
	case 4:
 | 
			
		||||
		return __cmpxchg_u32(ptr, old, new);
 | 
			
		||||
#ifdef CONFIG_PPC64
 | 
			
		||||
	case 8:
 | 
			
		||||
		return __cmpxchg_u64(ptr, old, new);
 | 
			
		||||
#endif
 | 
			
		||||
	}
 | 
			
		||||
	__cmpxchg_called_with_bad_pointer();
 | 
			
		||||
	return old;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define cmpxchg(ptr,o,n)						 \
 | 
			
		||||
  ({									 \
 | 
			
		||||
     __typeof__(*(ptr)) _o_ = (o);					 \
 | 
			
		||||
     __typeof__(*(ptr)) _n_ = (n);					 \
 | 
			
		||||
     (__typeof__(*(ptr))) __cmpxchg((ptr), (unsigned long)_o_,		 \
 | 
			
		||||
				    (unsigned long)_n_, sizeof(*(ptr))); \
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_PPC64
 | 
			
		||||
/*
 | 
			
		||||
 * We handle most unaligned accesses in hardware. On the other hand 
 | 
			
		||||
 * unaligned DMA can be very expensive on some ppc64 IO chips (it does
 | 
			
		||||
 * powers of 2 writes until it reaches sufficient alignment).
 | 
			
		||||
 *
 | 
			
		||||
 * Based on this we disable the IP header alignment in network drivers.
 | 
			
		||||
 */
 | 
			
		||||
#define NET_IP_ALIGN   0
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define arch_align_stack(x) (x)
 | 
			
		||||
 | 
			
		||||
#endif /* __KERNEL__ */
 | 
			
		||||
#endif /* __PPC_SYSTEM_H */
 | 
			
		||||
@@ -45,30 +45,21 @@ extern int __cpu_disable(void);
 | 
			
		||||
extern void __cpu_die(unsigned int cpu);
 | 
			
		||||
extern void cpu_die(void) __attribute__((noreturn));
 | 
			
		||||
 | 
			
		||||
#define NO_PROC_ID		0xFF            /* No processor magic marker */
 | 
			
		||||
#define PROC_CHANGE_PENALTY	20
 | 
			
		||||
 | 
			
		||||
#define raw_smp_processor_id()	(current_thread_info()->cpu)
 | 
			
		||||
 | 
			
		||||
extern int __cpu_up(unsigned int cpu);
 | 
			
		||||
 | 
			
		||||
extern int smp_hw_index[];
 | 
			
		||||
#define hard_smp_processor_id() (smp_hw_index[smp_processor_id()])
 | 
			
		||||
 | 
			
		||||
struct klock_info_struct {
 | 
			
		||||
	unsigned long kernel_flag;
 | 
			
		||||
	unsigned char akp;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern struct klock_info_struct klock_info;
 | 
			
		||||
#define KLOCK_HELD       0xffffffff
 | 
			
		||||
#define KLOCK_CLEAR      0x0
 | 
			
		||||
#define hard_smp_processor_id() 	(smp_hw_index[smp_processor_id()])
 | 
			
		||||
#define get_hard_smp_processor_id(cpu)	(smp_hw_index[(cpu)])
 | 
			
		||||
 | 
			
		||||
#endif /* __ASSEMBLY__ */
 | 
			
		||||
 | 
			
		||||
#else /* !(CONFIG_SMP) */
 | 
			
		||||
 | 
			
		||||
static inline void cpu_die(void) { }
 | 
			
		||||
#define get_hard_smp_processor_id(cpu) 0
 | 
			
		||||
#define hard_smp_processor_id() 0
 | 
			
		||||
 | 
			
		||||
#endif /* !(CONFIG_SMP) */
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user