diff options
Diffstat (limited to 'arch/microblaze')
65 files changed, 4481 insertions, 933 deletions
diff --git a/arch/microblaze/Kconfig b/arch/microblaze/Kconfig index b008168ae94..76818f92653 100644 --- a/arch/microblaze/Kconfig +++ b/arch/microblaze/Kconfig @@ -14,6 +14,8 @@ config MICROBLAZE  	select USB_ARCH_HAS_EHCI  	select ARCH_WANT_OPTIONAL_GPIOLIB  	select HAVE_OPROFILE +	select HAVE_DMA_ATTRS +	select HAVE_DMA_API_DEBUG  	select TRACING_SUPPORT  config SWAP @@ -73,12 +75,6 @@ config LOCKDEP_SUPPORT  config HAVE_LATENCYTOP_SUPPORT  	def_bool y -config PCI -	def_bool n - -config NO_DMA -	def_bool y -  config DTC  	def_bool y @@ -146,7 +142,6 @@ menu "Advanced setup"  config ADVANCED_OPTIONS  	bool "Prompt for advanced kernel configuration options" -	depends on MMU  	help  	  This option will enable prompting for a variety of advanced kernel  	  configuration options.  These options can cause the kernel to not @@ -158,6 +153,15 @@ config ADVANCED_OPTIONS  comment "Default settings for advanced configuration options are used"  	depends on !ADVANCED_OPTIONS +config XILINX_UNCACHED_SHADOW +	bool "Are you using uncached shadow for RAM ?" +	depends on ADVANCED_OPTIONS && !MMU +	default n +	help +	  This is needed to be able to allocate uncachable memory regions. +	  The feature requires the design to define the RAM memory controller +	  window to be twice as large as the actual physical memory. +  config HIGHMEM_START_BOOL  	bool "Set high memory pool address"  	depends on ADVANCED_OPTIONS && HIGHMEM @@ -175,7 +179,7 @@ config HIGHMEM_START  config LOWMEM_SIZE_BOOL  	bool "Set maximum low memory" -	depends on ADVANCED_OPTIONS +	depends on ADVANCED_OPTIONS && MMU  	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 @@ -187,7 +191,6 @@ config LOWMEM_SIZE_BOOL  config LOWMEM_SIZE  	hex "Maximum low memory size (in bytes)" if LOWMEM_SIZE_BOOL -	depends on MMU  	default "0x30000000"  config KERNEL_START_BOOL @@ -208,7 +211,7 @@ config KERNEL_START  config TASK_SIZE_BOOL  	bool "Set custom user task size" -	depends on ADVANCED_OPTIONS +	depends on ADVANCED_OPTIONS && MMU  	help  	  This option allows you to set the amount of virtual address space  	  allocated to user tasks.  This can be useful in optimizing the @@ -218,42 +221,34 @@ config TASK_SIZE_BOOL  config TASK_SIZE  	hex "Size of user task space" if TASK_SIZE_BOOL -	depends on MMU  	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. +endmenu -config CONSISTENT_START -	hex "Base virtual address of consistent memory pool" if CONSISTENT_START_BOOL -	depends on MMU -	default "0xff100000" if NOT_COHERENT_CACHE +source "mm/Kconfig" -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. +menu "Exectuable file formats" -config CONSISTENT_SIZE -	hex "Size of consistent memory pool" if CONSISTENT_SIZE_BOOL -	depends on MMU -	default "0x00200000" if NOT_COHERENT_CACHE +source "fs/Kconfig.binfmt"  endmenu -source "mm/Kconfig" +menu "Bus Options" -menu "Exectuable file formats" +config PCI +	bool "PCI support" -source "fs/Kconfig.binfmt" +config PCI_DOMAINS +	def_bool PCI + +config PCI_SYSCALL +	def_bool PCI + +config PCI_XILINX +	bool "Xilinx PCI host bridge support" +	depends on PCI + +source "drivers/pci/Kconfig"  endmenu diff --git a/arch/microblaze/Makefile b/arch/microblaze/Makefile index d2d6cfcb1a3..72f6e858374 100644 --- a/arch/microblaze/Makefile +++ b/arch/microblaze/Makefile @@ -50,6 +50,7 @@ libs-y += $(LIBGCC)  core-y += arch/microblaze/kernel/  core-y += arch/microblaze/mm/  core-y += arch/microblaze/platform/ +core-$(CONFIG_PCI) += arch/microblaze/pci/  drivers-$(CONFIG_OPROFILE) += arch/microblaze/oprofile/ @@ -83,7 +84,7 @@ define archhelp    echo '* linux.bin    - Create raw binary'    echo '  linux.bin.gz - Create compressed raw binary'    echo '  simpleImage.<dt> - ELF image with $(arch)/boot/dts/<dt>.dts linked in' -  echo '                   - stripped elf with fdt blob +  echo '                   - stripped elf with fdt blob'    echo '  simpleImage.<dt>.unstrip - full ELF image with fdt blob'    echo '  *_defconfig      - Select default config from arch/microblaze/configs'    echo '' @@ -93,3 +94,5 @@ define archhelp    echo '  name of a dts file from the arch/microblaze/boot/dts/ directory'    echo '  (minus the .dts extension).'  endef + +MRPROPER_FILES += $(boot)/simpleImage.* diff --git a/arch/microblaze/boot/Makefile b/arch/microblaze/boot/Makefile index 902cf9846c3..57f50c2371c 100644 --- a/arch/microblaze/boot/Makefile +++ b/arch/microblaze/boot/Makefile @@ -23,8 +23,6 @@ $(obj)/system.dtb: $(obj)/$(DTB).dtb  endif  $(obj)/linux.bin: vmlinux FORCE -	[ -n $(CONFIG_INITRAMFS_SOURCE) ] && [ ! -e $(CONFIG_INITRAMFS_SOURCE) ] && \ -	touch $(CONFIG_INITRAMFS_SOURCE) || echo "No CPIO image"  	$(call if_changed,objcopy)  	$(call if_changed,uimage)  	@echo 'Kernel: $@ is ready' ' (#'`cat .version`')' @@ -62,6 +60,4 @@ quiet_cmd_dtc = DTC     $@  $(obj)/%.dtb: $(dtstree)/%.dts FORCE  	$(call if_changed,dtc) -clean-kernel += linux.bin linux.bin.gz simpleImage.* - -clean-files += *.dtb simpleImage.*.unstrip +clean-files += *.dtb simpleImage.*.unstrip linux.bin.ub diff --git a/arch/microblaze/configs/mmu_defconfig b/arch/microblaze/configs/mmu_defconfig index 6fced1fe3bf..3c91cf6192c 100644 --- a/arch/microblaze/configs/mmu_defconfig +++ b/arch/microblaze/configs/mmu_defconfig @@ -1,7 +1,7 @@  #  # Automatically generated make config: don't edit -# Linux kernel version: 2.6.33-rc6 -# Wed Feb  3 10:02:59 2010 +# Linux kernel version: 2.6.34-rc6 +# Thu May  6 11:22:14 2010  #  CONFIG_MICROBLAZE=y  # CONFIG_SWAP is not set @@ -22,8 +22,6 @@ CONFIG_GENERIC_CSUM=y  CONFIG_STACKTRACE_SUPPORT=y  CONFIG_LOCKDEP_SUPPORT=y  CONFIG_HAVE_LATENCYTOP_SUPPORT=y -# CONFIG_PCI is not set -CONFIG_NO_DMA=y  CONFIG_DTC=y  CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"  CONFIG_CONSTRUCTORS=y @@ -56,7 +54,6 @@ CONFIG_RCU_FANOUT=32  CONFIG_IKCONFIG=y  CONFIG_IKCONFIG_PROC=y  CONFIG_LOG_BUF_SHIFT=17 -# CONFIG_GROUP_SCHED is not set  # CONFIG_CGROUPS is not set  CONFIG_SYSFS_DEPRECATED=y  CONFIG_SYSFS_DEPRECATED_V2=y @@ -106,6 +103,8 @@ CONFIG_SLAB=y  # CONFIG_SLOB is not set  # CONFIG_PROFILING is not set  CONFIG_HAVE_OPROFILE=y +CONFIG_HAVE_DMA_ATTRS=y +CONFIG_HAVE_DMA_API_DEBUG=y  #  # GCOV-based kernel profiling @@ -245,13 +244,20 @@ CONFIG_BINFMT_ELF=y  # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set  # CONFIG_HAVE_AOUT is not set  # CONFIG_BINFMT_MISC is not set + +# +# Bus Options +# +# CONFIG_PCI is not set +# CONFIG_PCI_DOMAINS is not set +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set  CONFIG_NET=y  #  # Networking options  #  CONFIG_PACKET=y -# CONFIG_PACKET_MMAP is not set  CONFIG_UNIX=y  CONFIG_XFRM=y  # CONFIG_XFRM_USER is not set @@ -341,7 +347,9 @@ CONFIG_PREVENT_FIRMWARE_BUILD=y  # CONFIG_SYS_HYPERVISOR is not set  # CONFIG_CONNECTOR is not set  # CONFIG_MTD is not set +CONFIG_OF_FLATTREE=y  CONFIG_OF_DEVICE=y +CONFIG_OF_MDIO=y  # CONFIG_PARPORT is not set  CONFIG_BLK_DEV=y  # CONFIG_BLK_DEV_COW_COMMON is not set @@ -370,6 +378,7 @@ CONFIG_MISC_DEVICES=y  #  # SCSI device support  # +CONFIG_SCSI_MOD=y  # CONFIG_RAID_ATTRS is not set  # CONFIG_SCSI is not set  # CONFIG_SCSI_DMA is not set @@ -383,9 +392,30 @@ CONFIG_NETDEVICES=y  # CONFIG_EQUALIZER is not set  # CONFIG_TUN is not set  # CONFIG_VETH is not set -# CONFIG_PHYLIB is not set +CONFIG_PHYLIB=y + +# +# MII PHY device drivers +# +# CONFIG_MARVELL_PHY is not set +# CONFIG_DAVICOM_PHY is not set +# CONFIG_QSEMI_PHY is not set +# CONFIG_LXT_PHY is not set +# CONFIG_CICADA_PHY is not set +# CONFIG_VITESSE_PHY is not set +# CONFIG_SMSC_PHY is not set +# CONFIG_BROADCOM_PHY is not set +# CONFIG_ICPLUS_PHY is not set +# CONFIG_REALTEK_PHY is not set +# CONFIG_NATIONAL_PHY is not set +# CONFIG_STE10XP is not set +# CONFIG_LSI_ET1011C_PHY is not set +# CONFIG_MICREL_PHY is not set +# CONFIG_FIXED_PHY is not set +# CONFIG_MDIO_BITBANG is not set  CONFIG_NET_ETHERNET=y  # CONFIG_MII is not set +# CONFIG_ETHOC is not set  # CONFIG_DNET is not set  # CONFIG_IBM_NEW_EMAC_ZMII is not set  # CONFIG_IBM_NEW_EMAC_RGMII is not set @@ -394,6 +424,7 @@ CONFIG_NET_ETHERNET=y  # CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set  # CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set  # CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set +# CONFIG_B44 is not set  # CONFIG_KS8842 is not set  # CONFIG_KS8851_MLL is not set  CONFIG_XILINX_EMACLITE=y @@ -444,6 +475,7 @@ CONFIG_SERIAL_UARTLITE=y  CONFIG_SERIAL_UARTLITE_CONSOLE=y  CONFIG_SERIAL_CORE=y  CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_TIMBERDALE is not set  # CONFIG_SERIAL_GRLIB_GAISLER_APBUART is not set  CONFIG_UNIX98_PTYS=y  # CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set @@ -471,6 +503,12 @@ CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y  # CONFIG_HWMON is not set  # CONFIG_THERMAL is not set  # CONFIG_WATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set  #  # Multifunction device drivers @@ -502,6 +540,7 @@ CONFIG_USB_ARCH_HAS_EHCI=y  # CONFIG_NEW_LEDS is not set  # CONFIG_ACCESSIBILITY is not set  # CONFIG_RTC_CLASS is not set +# CONFIG_DMADEVICES is not set  # CONFIG_AUXDISPLAY is not set  # CONFIG_UIO is not set @@ -572,6 +611,7 @@ CONFIG_MISC_FILESYSTEMS=y  # CONFIG_BEFS_FS is not set  # CONFIG_BFS_FS is not set  # CONFIG_EFS_FS is not set +# CONFIG_LOGFS is not set  # CONFIG_CRAMFS is not set  # CONFIG_SQUASHFS is not set  # CONFIG_VXFS_FS is not set @@ -595,6 +635,7 @@ CONFIG_SUNRPC=y  # CONFIG_RPCSEC_GSS_KRB5 is not set  # CONFIG_RPCSEC_GSS_SPKM3 is not set  # CONFIG_SMB_FS is not set +# CONFIG_CEPH_FS is not set  CONFIG_CIFS=y  CONFIG_CIFS_STATS=y  CONFIG_CIFS_STATS2=y @@ -696,6 +737,7 @@ CONFIG_SCHED_DEBUG=y  # CONFIG_DEBUG_OBJECTS is not set  CONFIG_DEBUG_SLAB=y  # CONFIG_DEBUG_SLAB_LEAK is not set +# CONFIG_DEBUG_KMEMLEAK is not set  CONFIG_DEBUG_SPINLOCK=y  # CONFIG_DEBUG_MUTEXES is not set  # CONFIG_DEBUG_LOCK_ALLOC is not set @@ -741,6 +783,7 @@ CONFIG_BRANCH_PROFILE_NONE=y  # CONFIG_KMEMTRACE is not set  # CONFIG_WORKQUEUE_TRACER is not set  # CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_DMA_API_DEBUG is not set  # CONFIG_SAMPLES is not set  CONFIG_EARLY_PRINTK=y  # CONFIG_HEART_BEAT is not set @@ -862,5 +905,6 @@ CONFIG_ZLIB_INFLATE=y  CONFIG_DECOMPRESS_GZIP=y  CONFIG_HAS_IOMEM=y  CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y  CONFIG_HAVE_LMB=y  CONFIG_NLATTR=y diff --git a/arch/microblaze/configs/nommu_defconfig b/arch/microblaze/configs/nommu_defconfig index ce2da535246..dd3a494257f 100644 --- a/arch/microblaze/configs/nommu_defconfig +++ b/arch/microblaze/configs/nommu_defconfig @@ -1,7 +1,7 @@  #  # Automatically generated make config: don't edit -# Linux kernel version: 2.6.33-rc6 -# Wed Feb  3 10:03:21 2010 +# Linux kernel version: 2.6.34-rc6 +# Thu May  6 11:25:12 2010  #  CONFIG_MICROBLAZE=y  # CONFIG_SWAP is not set @@ -22,8 +22,6 @@ CONFIG_GENERIC_CSUM=y  CONFIG_STACKTRACE_SUPPORT=y  CONFIG_LOCKDEP_SUPPORT=y  CONFIG_HAVE_LATENCYTOP_SUPPORT=y -# CONFIG_PCI is not set -CONFIG_NO_DMA=y  CONFIG_DTC=y  CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"  CONFIG_CONSTRUCTORS=y @@ -58,7 +56,6 @@ CONFIG_RCU_FANOUT=32  CONFIG_IKCONFIG=y  CONFIG_IKCONFIG_PROC=y  CONFIG_LOG_BUF_SHIFT=17 -# CONFIG_GROUP_SCHED is not set  # CONFIG_CGROUPS is not set  CONFIG_SYSFS_DEPRECATED=y  CONFIG_SYSFS_DEPRECATED_V2=y @@ -96,6 +93,8 @@ CONFIG_SLAB=y  # CONFIG_MMAP_ALLOW_UNINITIALIZED is not set  # CONFIG_PROFILING is not set  CONFIG_HAVE_OPROFILE=y +CONFIG_HAVE_DMA_ATTRS=y +CONFIG_HAVE_DMA_API_DEBUG=y  #  # GCOV-based kernel profiling @@ -209,11 +208,14 @@ CONFIG_PROC_DEVICETREE=y  #  # Advanced setup  # +# CONFIG_ADVANCED_OPTIONS is not set  #  # Default settings for advanced configuration options are used  # +CONFIG_LOWMEM_SIZE=0x30000000  CONFIG_KERNEL_START=0x90000000 +CONFIG_TASK_SIZE=0x80000000  CONFIG_SELECT_MEMORY_MODEL=y  CONFIG_FLATMEM_MANUAL=y  # CONFIG_DISCONTIGMEM_MANUAL is not set @@ -235,13 +237,20 @@ CONFIG_BINFMT_FLAT=y  # CONFIG_BINFMT_SHARED_FLAT is not set  # CONFIG_HAVE_AOUT is not set  # CONFIG_BINFMT_MISC is not set + +# +# Bus Options +# +# CONFIG_PCI is not set +# CONFIG_PCI_DOMAINS is not set +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set  CONFIG_NET=y  #  # Networking options  #  CONFIG_PACKET=y -# CONFIG_PACKET_MMAP is not set  CONFIG_UNIX=y  CONFIG_XFRM=y  # CONFIG_XFRM_USER is not set @@ -413,6 +422,7 @@ CONFIG_MTD_UCLINUX=y  # UBI - Unsorted block images  #  # CONFIG_MTD_UBI is not set +CONFIG_OF_FLATTREE=y  CONFIG_OF_DEVICE=y  # CONFIG_PARPORT is not set  CONFIG_BLK_DEV=y @@ -442,6 +452,7 @@ CONFIG_MISC_DEVICES=y  #  # SCSI device support  # +CONFIG_SCSI_MOD=y  # CONFIG_RAID_ATTRS is not set  # CONFIG_SCSI is not set  # CONFIG_SCSI_DMA is not set @@ -458,6 +469,7 @@ CONFIG_NETDEVICES=y  # CONFIG_PHYLIB is not set  CONFIG_NET_ETHERNET=y  # CONFIG_MII is not set +# CONFIG_ETHOC is not set  # CONFIG_DNET is not set  # CONFIG_IBM_NEW_EMAC_ZMII is not set  # CONFIG_IBM_NEW_EMAC_RGMII is not set @@ -466,6 +478,7 @@ CONFIG_NET_ETHERNET=y  # CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set  # CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set  # CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set +# CONFIG_B44 is not set  # CONFIG_KS8842 is not set  # CONFIG_KS8851_MLL is not set  # CONFIG_XILINX_EMACLITE is not set @@ -516,6 +529,7 @@ CONFIG_SERIAL_UARTLITE=y  CONFIG_SERIAL_UARTLITE_CONSOLE=y  CONFIG_SERIAL_CORE=y  CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_TIMBERDALE is not set  # CONFIG_SERIAL_GRLIB_GAISLER_APBUART is not set  CONFIG_UNIX98_PTYS=y  # CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set @@ -544,6 +558,12 @@ CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y  # CONFIG_HWMON is not set  # CONFIG_THERMAL is not set  # CONFIG_WATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set  #  # Multifunction device drivers @@ -593,6 +613,7 @@ CONFIG_USB_ARCH_HAS_EHCI=y  # CONFIG_NEW_LEDS is not set  # CONFIG_ACCESSIBILITY is not set  # CONFIG_RTC_CLASS is not set +# CONFIG_DMADEVICES is not set  # CONFIG_AUXDISPLAY is not set  # CONFIG_UIO is not set @@ -661,6 +682,7 @@ CONFIG_MISC_FILESYSTEMS=y  # CONFIG_BFS_FS is not set  # CONFIG_EFS_FS is not set  # CONFIG_JFFS2_FS is not set +# CONFIG_LOGFS is not set  CONFIG_CRAMFS=y  # CONFIG_SQUASHFS is not set  # CONFIG_VXFS_FS is not set @@ -689,6 +711,7 @@ CONFIG_SUNRPC=y  # CONFIG_RPCSEC_GSS_KRB5 is not set  # CONFIG_RPCSEC_GSS_SPKM3 is not set  # CONFIG_SMB_FS is not set +# CONFIG_CEPH_FS is not set  # CONFIG_CIFS is not set  # CONFIG_NCP_FS is not set  # CONFIG_CODA_FS is not set @@ -733,6 +756,7 @@ CONFIG_DEBUG_OBJECTS_TIMERS=y  # CONFIG_DEBUG_OBJECTS_WORK is not set  CONFIG_DEBUG_OBJECTS_ENABLE_DEFAULT=1  # CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_KMEMLEAK is not set  # CONFIG_DEBUG_RT_MUTEXES is not set  # CONFIG_RT_MUTEX_TESTER is not set  # CONFIG_DEBUG_SPINLOCK is not set @@ -758,6 +782,7 @@ CONFIG_DEBUG_SG=y  # CONFIG_BACKTRACE_SELF_TEST is not set  # CONFIG_DEBUG_BLOCK_EXT_DEVT is not set  # CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_LKDTM is not set  # CONFIG_FAULT_INJECTION is not set  # CONFIG_LATENCYTOP is not set  CONFIG_SYSCTL_SYSCALL_CHECK=y @@ -782,6 +807,7 @@ CONFIG_BRANCH_PROFILE_NONE=y  # CONFIG_WORKQUEUE_TRACER is not set  # CONFIG_BLK_DEV_IO_TRACE is not set  # CONFIG_DYNAMIC_DEBUG is not set +# CONFIG_DMA_API_DEBUG is not set  # CONFIG_SAMPLES is not set  CONFIG_EARLY_PRINTK=y  # CONFIG_HEART_BEAT is not set @@ -901,5 +927,6 @@ CONFIG_GENERIC_FIND_LAST_BIT=y  CONFIG_ZLIB_INFLATE=y  CONFIG_HAS_IOMEM=y  CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y  CONFIG_HAVE_LMB=y  CONFIG_NLATTR=y diff --git a/arch/microblaze/include/asm/cache.h b/arch/microblaze/include/asm/cache.h index e52210891d7..4efe96a036f 100644 --- a/arch/microblaze/include/asm/cache.h +++ b/arch/microblaze/include/asm/cache.h @@ -15,7 +15,7 @@  #include <asm/registers.h> -#define L1_CACHE_SHIFT	2 +#define L1_CACHE_SHIFT 5  /* word-granular cache in microblaze */  #define L1_CACHE_BYTES	(1 << L1_CACHE_SHIFT) diff --git a/arch/microblaze/include/asm/device.h b/arch/microblaze/include/asm/device.h index 78a038452c0..402b46e630f 100644 --- a/arch/microblaze/include/asm/device.h +++ b/arch/microblaze/include/asm/device.h @@ -14,6 +14,10 @@ struct device_node;  struct dev_archdata {  	/* Optional pointer to an OF device node */  	struct device_node	*of_node; + +	/* DMA operations on that device */ +	struct dma_map_ops	*dma_ops; +	void                    *dma_data;  };  struct pdev_archdata { diff --git a/arch/microblaze/include/asm/dma-mapping.h b/arch/microblaze/include/asm/dma-mapping.h index d00e4009916..18b3731c850 100644 --- a/arch/microblaze/include/asm/dma-mapping.h +++ b/arch/microblaze/include/asm/dma-mapping.h @@ -1 +1,153 @@ -#include <asm-generic/dma-mapping-broken.h> +/* + * Implements the generic device dma API for microblaze and the pci + * + * Copyright (C) 2009-2010 Michal Simek <monstr@monstr.eu> + * Copyright (C) 2009-2010 PetaLogix + * + * 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. + * + * This file is base on powerpc and x86 dma-mapping.h versions + * Copyright (C) 2004 IBM + */ + +#ifndef _ASM_MICROBLAZE_DMA_MAPPING_H +#define _ASM_MICROBLAZE_DMA_MAPPING_H + +/* + * See Documentation/PCI/PCI-DMA-mapping.txt and + * Documentation/DMA-API.txt for documentation. + */ + +#include <linux/types.h> +#include <linux/cache.h> +#include <linux/mm.h> +#include <linux/scatterlist.h> +#include <linux/dma-debug.h> +#include <linux/dma-attrs.h> +#include <asm/io.h> +#include <asm-generic/dma-coherent.h> + +#define DMA_ERROR_CODE		(~(dma_addr_t)0x0) + +#define __dma_alloc_coherent(dev, gfp, size, handle)	NULL +#define __dma_free_coherent(size, addr)		((void)0) +#define __dma_sync(addr, size, rw)		((void)0) + +static inline unsigned long device_to_mask(struct device *dev) +{ +	if (dev->dma_mask && *dev->dma_mask) +		return *dev->dma_mask; +	/* Assume devices without mask can take 32 bit addresses */ +	return 0xfffffffful; +} + +extern struct dma_map_ops *dma_ops; + +/* + * Available generic sets of operations + */ +extern struct dma_map_ops dma_direct_ops; + +static inline struct dma_map_ops *get_dma_ops(struct device *dev) +{ +	/* We don't handle the NULL dev case for ISA for now. We could +	 * do it via an out of line call but it is not needed for now. The +	 * only ISA DMA device we support is the floppy and we have a hack +	 * in the floppy driver directly to get a device for us. +	 */ +	if (unlikely(!dev) || !dev->archdata.dma_ops) +		return NULL; + +	return dev->archdata.dma_ops; +} + +static inline void set_dma_ops(struct device *dev, struct dma_map_ops *ops) +{ +	dev->archdata.dma_ops = ops; +} + +static inline int dma_supported(struct device *dev, u64 mask) +{ +	struct dma_map_ops *ops = get_dma_ops(dev); + +	if (unlikely(!ops)) +		return 0; +	if (!ops->dma_supported) +		return 1; +	return ops->dma_supported(dev, mask); +} + +#ifdef CONFIG_PCI +/* We have our own implementation of pci_set_dma_mask() */ +#define HAVE_ARCH_PCI_SET_DMA_MASK + +#endif + +static inline int dma_set_mask(struct device *dev, u64 dma_mask) +{ +	struct dma_map_ops *ops = get_dma_ops(dev); + +	if (unlikely(ops == NULL)) +		return -EIO; +	if (ops->set_dma_mask) +		return ops->set_dma_mask(dev, dma_mask); +	if (!dev->dma_mask || !dma_supported(dev, dma_mask)) +		return -EIO; +	*dev->dma_mask = dma_mask; +	return 0; +} + +#include <asm-generic/dma-mapping-common.h> + +static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) +{ +	struct dma_map_ops *ops = get_dma_ops(dev); +	if (ops->mapping_error) +		return ops->mapping_error(dev, dma_addr); + +	return (dma_addr == DMA_ERROR_CODE); +} + +#define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f) +#define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h) +#define dma_is_consistent(d, h)	(1) + +static inline void *dma_alloc_coherent(struct device *dev, size_t size, +					dma_addr_t *dma_handle, gfp_t flag) +{ +	struct dma_map_ops *ops = get_dma_ops(dev); +	void *memory; + +	BUG_ON(!ops); + +	memory = ops->alloc_coherent(dev, size, dma_handle, flag); + +	debug_dma_alloc_coherent(dev, size, *dma_handle, memory); +	return memory; +} + +static inline void dma_free_coherent(struct device *dev, size_t size, +				     void *cpu_addr, dma_addr_t dma_handle) +{ +	struct dma_map_ops *ops = get_dma_ops(dev); + +	BUG_ON(!ops); +	debug_dma_free_coherent(dev, size, cpu_addr, dma_handle); +	ops->free_coherent(dev, size, cpu_addr, dma_handle); +} + +static inline int dma_get_cache_alignment(void) +{ +	return L1_CACHE_BYTES; +} + +static inline void dma_cache_sync(struct device *dev, void *vaddr, size_t size, +		enum dma_data_direction direction) +{ +	BUG_ON(direction == DMA_NONE); +	__dma_sync(vaddr, size, (int)direction); +} + +#endif	/* _ASM_MICROBLAZE_DMA_MAPPING_H */ diff --git a/arch/microblaze/include/asm/dma.h b/arch/microblaze/include/asm/dma.h index 08c073badf1..0d73d0c6de3 100644 --- a/arch/microblaze/include/asm/dma.h +++ b/arch/microblaze/include/asm/dma.h @@ -18,4 +18,10 @@  #define MAX_DMA_ADDRESS (CONFIG_KERNEL_START + memory_size - 1)  #endif +#ifdef CONFIG_PCI +extern int isa_dma_bridge_buggy; +#else +#define isa_dma_bridge_buggy     (0) +#endif +  #endif /* _ASM_MICROBLAZE_DMA_H */ diff --git a/arch/microblaze/include/asm/exceptions.h b/arch/microblaze/include/asm/exceptions.h index 90731df9e57..4c7b5d037c8 100644 --- a/arch/microblaze/include/asm/exceptions.h +++ b/arch/microblaze/include/asm/exceptions.h @@ -64,12 +64,6 @@ asmlinkage void full_exception(struct pt_regs *regs, unsigned int type,  void die(const char *str, struct pt_regs *fp, long err);  void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr); -#ifdef CONFIG_MMU -void __bug(const char *file, int line, void *data); -int bad_trap(int trap_num, struct pt_regs *regs); -int debug_trap(struct pt_regs *regs); -#endif /* CONFIG_MMU */ -  #if defined(CONFIG_KGDB)  void (*debugger)(struct pt_regs *regs);  int (*debugger_bpt)(struct pt_regs *regs); diff --git a/arch/microblaze/include/asm/futex.h b/arch/microblaze/include/asm/futex.h index 8dbb6e7a03a..ad3fd61b2fe 100644 --- a/arch/microblaze/include/asm/futex.h +++ b/arch/microblaze/include/asm/futex.h @@ -55,7 +55,7 @@ futex_atomic_op_inuser(int encoded_op, int __user *uaddr)  		__futex_atomic_op("or %1,%0,%4;", ret, oldval, uaddr, oparg);  		break;  	case FUTEX_OP_ANDN: -		__futex_atomic_op("and %1,%0,%4;", ret, oldval, uaddr, oparg); +		__futex_atomic_op("andn %1,%0,%4;", ret, oldval, uaddr, oparg);  		break;  	case FUTEX_OP_XOR:  		__futex_atomic_op("xor %1,%0,%4;", ret, oldval, uaddr, oparg); diff --git a/arch/microblaze/include/asm/io.h b/arch/microblaze/include/asm/io.h index 267c7c779e5..00b5398d08c 100644 --- a/arch/microblaze/include/asm/io.h +++ b/arch/microblaze/include/asm/io.h @@ -15,7 +15,23 @@  #include <asm/page.h>  #include <linux/types.h>  #include <linux/mm.h>          /* Get struct page {...} */ +#include <asm-generic/iomap.h> +#ifndef CONFIG_PCI +#define _IO_BASE	0 +#define _ISA_MEM_BASE	0 +#define PCI_DRAM_OFFSET	0 +#else +#define _IO_BASE	isa_io_base +#define _ISA_MEM_BASE	isa_mem_base +#define PCI_DRAM_OFFSET	pci_dram_offset +#endif + +extern unsigned long isa_io_base; +extern unsigned long pci_io_base; +extern unsigned long pci_dram_offset; + +extern resource_size_t isa_mem_base;  #define IO_SPACE_LIMIT (0xFFFFFFFF) @@ -92,6 +108,11 @@ static inline void writel(unsigned int v, volatile void __iomem *addr)  #define iowrite16(v, addr)	__raw_writew((u16)(v), (u16 *)(addr))  #define iowrite32(v, addr)	__raw_writel((u32)(v), (u32 *)(addr)) +#define ioread16be(addr)	__raw_readw((u16 *)(addr)) +#define ioread32be(addr)	__raw_readl((u32 *)(addr)) +#define iowrite16be(v, addr)	__raw_writew((u16)(v), (u16 *)(addr)) +#define iowrite32be(v, addr)	__raw_writel((u32)(v), (u32 *)(addr)) +  /* These are the definitions for the x86 IO instructions   * inb/inw/inl/outb/outw/outl, the "string" versions   * insb/insw/insl/outsb/outsw/outsl, and the "pausing" versions @@ -118,15 +139,10 @@ static inline void writel(unsigned int v, volatile void __iomem *addr)  #ifdef CONFIG_MMU -#define mm_ptov(addr)		((void *)__phys_to_virt(addr)) -#define mm_vtop(addr)		((unsigned long)__virt_to_phys(addr))  #define phys_to_virt(addr)	((void *)__phys_to_virt(addr))  #define virt_to_phys(addr)	((unsigned long)__virt_to_phys(addr))  #define virt_to_bus(addr)	((unsigned long)__virt_to_phys(addr)) -#define __page_address(page) \ -		(PAGE_OFFSET + (((page) - mem_map) << PAGE_SHIFT)) -#define page_to_phys(page)	virt_to_phys((void *)__page_address(page))  #define page_to_bus(page)	(page_to_phys(page))  #define bus_to_virt(addr)	(phys_to_virt(addr)) @@ -227,15 +243,7 @@ static inline void __iomem *__ioremap(phys_addr_t address, unsigned long size,  #define out_8(a, v) __raw_writeb((v), (a))  #define in_8(a) __raw_readb(a) -/* FIXME */ -static inline void __iomem *ioport_map(unsigned long port, unsigned int len) -{ -	return (void __iomem *) (port); -} - -static inline void ioport_unmap(void __iomem *addr) -{ -	/* Nothing to do */ -} +#define ioport_map(port, nr)	((void __iomem *)(port)) +#define ioport_unmap(addr)  #endif /* _ASM_MICROBLAZE_IO_H */ diff --git a/arch/microblaze/include/asm/irq.h b/arch/microblaze/include/asm/irq.h index 90f050535eb..31a35c33df6 100644 --- a/arch/microblaze/include/asm/irq.h +++ b/arch/microblaze/include/asm/irq.h @@ -14,6 +14,12 @@  #include <linux/interrupt.h> +/* This type is the placeholder for a hardware interrupt number. It has to + * be big enough to enclose whatever representation is used by a given + * platform. + */ +typedef unsigned long irq_hw_number_t; +  extern unsigned int nr_irq;  #define NO_IRQ (-1) @@ -21,7 +27,8 @@ extern unsigned int nr_irq;  struct pt_regs;  extern void do_IRQ(struct pt_regs *regs); -/* irq_of_parse_and_map - Parse and Map an interrupt into linux virq space +/** + * irq_of_parse_and_map - Parse and Map an interrupt into linux virq space   * @device: Device node of the device whose interrupt is to be mapped   * @index: Index of the interrupt to map   * @@ -40,4 +47,32 @@ static inline void irq_dispose_mapping(unsigned int virq)  	return;  } +struct irq_host; + +/** + * irq_create_mapping - Map a hardware interrupt into linux virq space + * @host: host owning this hardware interrupt or NULL for default host + * @hwirq: hardware irq number in that host space + * + * Only one mapping per hardware interrupt is permitted. Returns a linux + * virq number. + * If the sense/trigger is to be specified, set_irq_type() should be called + * on the number returned from that call. + */ +extern unsigned int irq_create_mapping(struct irq_host *host, +					irq_hw_number_t hwirq); + +/** + * irq_create_of_mapping - Map a hardware interrupt into linux virq space + * @controller: Device node of the interrupt controller + * @inspec: Interrupt specifier from the device-tree + * @intsize: Size of the interrupt specifier from the device-tree + * + * This function is identical to irq_create_mapping except that it takes + * as input informations straight from the device-tree (typically the results + * of the of_irq_map_*() functions. + */ +extern unsigned int irq_create_of_mapping(struct device_node *controller, +					u32 *intspec, unsigned int intsize); +  #endif /* _ASM_MICROBLAZE_IRQ_H */ diff --git a/arch/microblaze/include/asm/page.h b/arch/microblaze/include/asm/page.h index 9b66c0fa9a3..de493f86d28 100644 --- a/arch/microblaze/include/asm/page.h +++ b/arch/microblaze/include/asm/page.h @@ -31,6 +31,9 @@  #ifndef __ASSEMBLY__ +/* MS be sure that SLAB allocates aligned objects */ +#define ARCH_KMALLOC_MINALIGN	L1_CACHE_BYTES +  #define PAGE_UP(addr)	(((addr)+((PAGE_SIZE)-1))&(~((PAGE_SIZE)-1)))  #define PAGE_DOWN(addr)	((addr)&(~((PAGE_SIZE)-1))) @@ -62,12 +65,6 @@ extern unsigned int __page_offset;  #define PAGE_OFFSET	CONFIG_KERNEL_START  /* - * MAP_NR -- given an address, calculate the index of the page struct which - * points to the address's page. - */ -#define MAP_NR(addr) (((unsigned long)(addr) - PAGE_OFFSET) >> PAGE_SHIFT) - -/*   * The basic type of a PTE - 32 bit physical addressing.   */  typedef unsigned long pte_basic_t; @@ -76,14 +73,7 @@ typedef unsigned long pte_basic_t;  #endif /* CONFIG_MMU */ -#  ifndef CONFIG_MMU -#  define copy_page(to, from)			memcpy((to), (from), PAGE_SIZE) -#  define get_user_page(vaddr)			__get_free_page(GFP_KERNEL) -#  define free_user_page(page, addr)		free_page(addr) -#  else /* CONFIG_MMU */ -extern void copy_page(void *to, void *from); -#  endif /* CONFIG_MMU */ - +# define copy_page(to, from)			memcpy((to), (from), PAGE_SIZE)  # define clear_page(pgaddr)			memset((pgaddr), 0, PAGE_SIZE)  # define clear_user_page(pgaddr, vaddr, page)	memset((pgaddr), 0, PAGE_SIZE) @@ -154,7 +144,11 @@ extern int page_is_ram(unsigned long pfn);  # define pfn_to_virt(pfn)	__va(pfn_to_phys((pfn)))  #  ifdef CONFIG_MMU -#  define virt_to_page(kaddr) 	(mem_map +  MAP_NR(kaddr)) + +#  define virt_to_page(kaddr)	(pfn_to_page(__pa(kaddr) >> PAGE_SHIFT)) +#  define page_to_virt(page)   __va(page_to_pfn(page) << PAGE_SHIFT) +#  define page_to_phys(page)     (page_to_pfn(page) << PAGE_SHIFT) +  #  else /* CONFIG_MMU */  #  define virt_to_page(vaddr)	(pfn_to_page(virt_to_pfn(vaddr)))  #  define page_to_virt(page)	(pfn_to_virt(page_to_pfn(page))) diff --git a/arch/microblaze/include/asm/pci-bridge.h b/arch/microblaze/include/asm/pci-bridge.h index 7ad28f6f5f1..0c77cda9f5d 100644 --- a/arch/microblaze/include/asm/pci-bridge.h +++ b/arch/microblaze/include/asm/pci-bridge.h @@ -1 +1,196 @@ +#ifndef _ASM_MICROBLAZE_PCI_BRIDGE_H +#define _ASM_MICROBLAZE_PCI_BRIDGE_H +#ifdef __KERNEL__ +/* + * 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/pci.h> +#include <linux/list.h> +#include <linux/ioport.h> + +struct device_node; + +enum { +	/* Force re-assigning all resources (ignore firmware +	 * setup completely) +	 */ +	PCI_REASSIGN_ALL_RSRC	= 0x00000001, + +	/* Re-assign all bus numbers */ +	PCI_REASSIGN_ALL_BUS	= 0x00000002, + +	/* Do not try to assign, just use existing setup */ +	PCI_PROBE_ONLY		= 0x00000004, + +	/* Don't bother with ISA alignment unless the bridge has +	 * ISA forwarding enabled +	 */ +	PCI_CAN_SKIP_ISA_ALIGN	= 0x00000008, + +	/* Enable domain numbers in /proc */ +	PCI_ENABLE_PROC_DOMAINS	= 0x00000010, +	/* ... except for domain 0 */ +	PCI_COMPAT_DOMAIN_0		= 0x00000020, +}; + +/* + * Structure of a PCI controller (host bridge) + */ +struct pci_controller { +	struct pci_bus *bus; +	char is_dynamic; +	struct device_node *dn; +	struct list_head list_node; +	struct device *parent; + +	int first_busno; +	int last_busno; + +	int self_busno; + +	void __iomem *io_base_virt; +	resource_size_t io_base_phys; + +	resource_size_t pci_io_size; + +	/* Some machines (PReP) have a non 1:1 mapping of +	 * the PCI memory space in the CPU bus space +	 */ +	resource_size_t pci_mem_offset; + +	/* Some machines have a special region to forward the ISA +	 * "memory" cycles such as VGA memory regions. Left to 0 +	 * if unsupported +	 */ +	resource_size_t isa_mem_phys; +	resource_size_t isa_mem_size; + +	struct pci_ops *ops; +	unsigned int __iomem *cfg_addr; +	void __iomem *cfg_data; + +	/* +	 * Used for variants of PCI indirect handling and possible quirks: +	 *  SET_CFG_TYPE - used on 4xx or any PHB that does explicit type0/1 +	 *  EXT_REG - provides access to PCI-e extended registers +	 *  SURPRESS_PRIMARY_BUS - we surpress the setting of PCI_PRIMARY_BUS +	 *   on Freescale PCI-e controllers since they used the PCI_PRIMARY_BUS +	 *   to determine which bus number to match on when generating type0 +	 *   config cycles +	 *  NO_PCIE_LINK - the Freescale PCI-e controllers have issues with +	 *   hanging if we don't have link and try to do config cycles to +	 *   anything but the PHB.  Only allow talking to the PHB if this is +	 *   set. +	 *  BIG_ENDIAN - cfg_addr is a big endian register +	 *  BROKEN_MRM - the 440EPx/GRx chips have an errata that causes hangs +	 *   on the PLB4.  Effectively disable MRM commands by setting this. +	 */ +#define INDIRECT_TYPE_SET_CFG_TYPE		0x00000001 +#define INDIRECT_TYPE_EXT_REG		0x00000002 +#define INDIRECT_TYPE_SURPRESS_PRIMARY_BUS	0x00000004 +#define INDIRECT_TYPE_NO_PCIE_LINK		0x00000008 +#define INDIRECT_TYPE_BIG_ENDIAN		0x00000010 +#define INDIRECT_TYPE_BROKEN_MRM		0x00000020 +	u32 indirect_type; + +	/* Currently, we limit ourselves to 1 IO range and 3 mem +	 * ranges since the common pci_bus structure can't handle more +	 */ +	struct resource io_resource; +	struct resource mem_resources[3]; +	int global_number;	/* PCI domain number */ +}; + +static inline struct pci_controller *pci_bus_to_host(const struct pci_bus *bus) +{ +	return bus->sysdata; +} + +static inline int isa_vaddr_is_ioport(void __iomem *address) +{ +	/* No specific ISA handling on ppc32 at this stage, it +	 * all goes through PCI +	 */ +	return 0; +} + +/* These are used for config access before all the PCI probing +   has been done. */ +extern int early_read_config_byte(struct pci_controller *hose, int bus, +			int dev_fn, int where, u8 *val); +extern int early_read_config_word(struct pci_controller *hose, int bus, +			int dev_fn, int where, u16 *val); +extern int early_read_config_dword(struct pci_controller *hose, int bus, +			int dev_fn, int where, u32 *val); +extern int early_write_config_byte(struct pci_controller *hose, int bus, +			int dev_fn, int where, u8 val); +extern int early_write_config_word(struct pci_controller *hose, int bus, +			int dev_fn, int where, u16 val); +extern int early_write_config_dword(struct pci_controller *hose, int bus, +			int dev_fn, int where, u32 val); + +extern int early_find_capability(struct pci_controller *hose, int bus, +				 int dev_fn, int cap); + +extern void setup_indirect_pci(struct pci_controller *hose, +			       resource_size_t cfg_addr, +			       resource_size_t cfg_data, u32 flags); + +/* Get the PCI host controller for an OF device */ +extern struct pci_controller *pci_find_hose_for_OF_device( +			struct device_node *node); + +/* Fill up host controller resources from the OF node */ +extern void pci_process_bridge_OF_ranges(struct pci_controller *hose, +			struct device_node *dev, int primary); + +/* Allocate & free a PCI host bridge structure */ +extern struct pci_controller *pcibios_alloc_controller(struct device_node *dev); +extern void pcibios_free_controller(struct pci_controller *phb); +extern void pcibios_setup_phb_resources(struct pci_controller *hose); + +#ifdef CONFIG_PCI +extern unsigned int pci_flags; + +static inline void pci_set_flags(int flags) +{ +	pci_flags = flags; +} + +static inline void pci_add_flags(int flags) +{ +	pci_flags |= flags; +} + +static inline int pci_has_flag(int flag) +{ +	return pci_flags & flag; +} + +extern struct list_head hose_list; + +extern unsigned long pci_address_to_pio(phys_addr_t address); +extern int pcibios_vaddr_is_ioport(void __iomem *address); +#else +static inline unsigned long pci_address_to_pio(phys_addr_t address) +{ +	return (unsigned long)-1; +} +static inline int pcibios_vaddr_is_ioport(void __iomem *address) +{ +	return 0; +} + +static inline void pci_set_flags(int flags) { } +static inline void pci_add_flags(int flags) { } +static inline int pci_has_flag(int flag) +{ +	return 0; +} +#endif	/* CONFIG_PCI */ + +#endif	/* __KERNEL__ */ +#endif	/* _ASM_MICROBLAZE_PCI_BRIDGE_H */ diff --git a/arch/microblaze/include/asm/pci.h b/arch/microblaze/include/asm/pci.h index 9f0df5faf2c..5a388eeeb28 100644 --- a/arch/microblaze/include/asm/pci.h +++ b/arch/microblaze/include/asm/pci.h @@ -1 +1,169 @@ -#include <asm-generic/pci.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. + * + * Based on powerpc version + */ + +#ifndef __ASM_MICROBLAZE_PCI_H +#define __ASM_MICROBLAZE_PCI_H +#ifdef __KERNEL__ + +#include <linux/types.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/dma-mapping.h> +#include <linux/pci.h> + +#include <asm/scatterlist.h> +#include <asm/io.h> +#include <asm/prom.h> +#include <asm/pci-bridge.h> + +#define PCIBIOS_MIN_IO		0x1000 +#define PCIBIOS_MIN_MEM		0x10000000 + +struct pci_dev; + +/* Values for the `which' argument to sys_pciconfig_iobase syscall.  */ +#define IOBASE_BRIDGE_NUMBER	0 +#define IOBASE_MEMORY		1 +#define IOBASE_IO		2 +#define IOBASE_ISA_IO		3 +#define IOBASE_ISA_MEM		4 + +#define pcibios_scan_all_fns(a, b)	0 + +/* + * Set this to 1 if you want the kernel to re-assign all PCI + * bus numbers (don't do that on ppc64 yet !) + */ +#define pcibios_assign_all_busses() \ +	(pci_has_flag(PCI_REASSIGN_ALL_BUS)) + +static inline void pcibios_set_master(struct pci_dev *dev) +{ +	/* No special bus mastering setup handling */ +} + +static inline void pcibios_penalize_isa_irq(int irq, int active) +{ +	/* We don't do dynamic PCI IRQ allocation */ +} + +#ifdef CONFIG_PCI +extern void set_pci_dma_ops(struct dma_map_ops *dma_ops); +extern struct dma_map_ops *get_pci_dma_ops(void); +#else	/* CONFIG_PCI */ +#define set_pci_dma_ops(d) +#define get_pci_dma_ops()	NULL +#endif + +#ifdef CONFIG_PCI +static inline void pci_dma_burst_advice(struct pci_dev *pdev, +					enum pci_dma_burst_strategy *strat, +					unsigned long *strategy_parameter) +{ +	*strat = PCI_DMA_BURST_INFINITY; +	*strategy_parameter = ~0UL; +} +#endif + +extern int pci_domain_nr(struct pci_bus *bus); + +/* Decide whether to display the domain number in /proc */ +extern int pci_proc_domain(struct pci_bus *bus); + +struct vm_area_struct; +/* Map a range of PCI memory or I/O space for a device into user space */ +int pci_mmap_page_range(struct pci_dev *pdev, struct vm_area_struct *vma, +			enum pci_mmap_state mmap_state, int write_combine); + +/* Tell drivers/pci/proc.c that we have pci_mmap_page_range() */ +#define HAVE_PCI_MMAP	1 + +extern int pci_legacy_read(struct pci_bus *bus, loff_t port, u32 *val, +			   size_t count); +extern int pci_legacy_write(struct pci_bus *bus, loff_t port, u32 val, +			   size_t count); +extern int pci_mmap_legacy_page_range(struct pci_bus *bus, +				      struct vm_area_struct *vma, +				      enum pci_mmap_state mmap_state); + +#define HAVE_PCI_LEGACY	1 + +/* The PCI address space does equal the physical memory + * address space (no IOMMU).  The IDE and SCSI device layers use + * this boolean for bounce buffer decisions. + */ +#define PCI_DMA_BUS_IS_PHYS     (1) + +extern void pcibios_resource_to_bus(struct pci_dev *dev, +			struct pci_bus_region *region, +			struct resource *res); + +extern void pcibios_bus_to_resource(struct pci_dev *dev, +			struct resource *res, +			struct pci_bus_region *region); + +static inline struct resource *pcibios_select_root(struct pci_dev *pdev, +			struct resource *res) +{ +	struct resource *root = NULL; + +	if (res->flags & IORESOURCE_IO) +		root = &ioport_resource; +	if (res->flags & IORESOURCE_MEM) +		root = &iomem_resource; + +	return root; +} + +extern void pcibios_claim_one_bus(struct pci_bus *b); + +extern void pcibios_finish_adding_to_bus(struct pci_bus *bus); + +extern void pcibios_resource_survey(void); + +extern struct pci_controller *init_phb_dynamic(struct device_node *dn); +extern int remove_phb_dynamic(struct pci_controller *phb); + +extern struct pci_dev *of_create_pci_dev(struct device_node *node, +					struct pci_bus *bus, int devfn); + +extern void of_scan_pci_bridge(struct device_node *node, +				struct pci_dev *dev); + +extern void of_scan_bus(struct device_node *node, struct pci_bus *bus); +extern void of_rescan_bus(struct device_node *node, struct pci_bus *bus); + +extern int pci_read_irq_line(struct pci_dev *dev); + +extern int pci_bus_find_capability(struct pci_bus *bus, +						unsigned int devfn, int cap); + +struct file; +extern pgprot_t	pci_phys_mem_access_prot(struct file *file, +					 unsigned long pfn, +					 unsigned long size, +					 pgprot_t prot); + +#define HAVE_ARCH_PCI_RESOURCE_TO_USER +extern void pci_resource_to_user(const struct pci_dev *dev, int bar, +				 const struct resource *rsrc, +				 resource_size_t *start, resource_size_t *end); + +extern void pcibios_setup_bus_devices(struct pci_bus *bus); +extern void pcibios_setup_bus_self(struct pci_bus *bus); + +/* This part of code was originaly in xilinx-pci.h */ +#ifdef CONFIG_PCI_XILINX +extern void __init xilinx_pci_init(void); +#else +static inline void __init xilinx_pci_init(void) { return; } +#endif + +#endif	/* __KERNEL__ */ +#endif /* __ASM_MICROBLAZE_PCI_H */ diff --git a/arch/microblaze/include/asm/pgalloc.h b/arch/microblaze/include/asm/pgalloc.h index 7547f506456..c614a893f8a 100644 --- a/arch/microblaze/include/asm/pgalloc.h +++ b/arch/microblaze/include/asm/pgalloc.h @@ -19,6 +19,7 @@  #include <asm/io.h>  #include <asm/page.h>  #include <asm/cache.h> +#include <asm/pgtable.h>  #define PGDIR_ORDER	0 @@ -107,22 +108,7 @@ extern inline void free_pgd_slow(pgd_t *pgd)  #define pmd_alloc_one_fast(mm, address)	({ BUG(); ((pmd_t *)1); })  #define pmd_alloc_one(mm, address)	({ BUG(); ((pmd_t *)2); }) -static inline 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; -} +extern pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long addr);  static inline struct page *pte_alloc_one(struct mm_struct *mm,  		unsigned long address) diff --git a/arch/microblaze/include/asm/pgtable.h b/arch/microblaze/include/asm/pgtable.h index cc3a4dfc3ea..ca2d9287154 100644 --- a/arch/microblaze/include/asm/pgtable.h +++ b/arch/microblaze/include/asm/pgtable.h @@ -16,6 +16,10 @@  #define io_remap_pfn_range(vma, vaddr, pfn, size, prot)		\  		remap_pfn_range(vma, vaddr, pfn, size, prot) +#ifndef __ASSEMBLY__ +extern int mem_init_done; +#endif +  #ifndef CONFIG_MMU  #define pgd_present(pgd)	(1) /* pages are always present on non MMU */ @@ -51,6 +55,8 @@ static inline int pte_file(pte_t pte) { return 0; }  #define arch_enter_lazy_cpu_mode()	do {} while (0) +#define pgprot_noncached_wc(prot)	prot +  #else /* CONFIG_MMU */  #include <asm-generic/4level-fixup.h> @@ -68,7 +74,6 @@ static inline int pte_file(pte_t pte) { return 0; }  extern unsigned long va_to_phys(unsigned long address);  extern pte_t *va_to_pte(unsigned long address); -extern unsigned long ioremap_bot, ioremap_base;  /*   * The following only work if pte_present() is true. @@ -85,11 +90,25 @@ static inline pte_t pte_mkspecial(pte_t pte)	{ return pte; }  #define VMALLOC_START	(CONFIG_KERNEL_START + \  				max(32 * 1024 * 1024UL, memory_size))  #define VMALLOC_END	ioremap_bot -#define VMALLOC_VMADDR(x) ((unsigned long)(x))  #endif /* __ASSEMBLY__ */  /* + * Macro to mark a page protection value as "uncacheable". + */ + +#define _PAGE_CACHE_CTL	(_PAGE_GUARDED | _PAGE_NO_CACHE | \ +							_PAGE_WRITETHRU) + +#define pgprot_noncached(prot) \ +			(__pgprot((pgprot_val(prot) & ~_PAGE_CACHE_CTL) | \ +					_PAGE_NO_CACHE | _PAGE_GUARDED)) + +#define pgprot_noncached_wc(prot) \ +			 (__pgprot((pgprot_val(prot) & ~_PAGE_CACHE_CTL) | \ +							_PAGE_NO_CACHE)) + +/*   * The MicroBlaze MMU is identical to the PPC-40x MMU, and uses a hash   * table containing PTEs, together with a set of 16 segment registers, to   * define the virtual to physical address mapping. @@ -397,7 +416,7 @@ static inline unsigned long pte_update(pte_t *p, unsigned long clr,  	mts     rmsr, %2\n\  	nop"  	: "=&r" (old), "=&r" (tmp), "=&r" (msr), "=m" (*p) -	: "r" ((unsigned long)(p+1) - 4), "r" (clr), "r" (set), "m" (*p) +	: "r" ((unsigned long)(p + 1) - 4), "r" (clr), "r" (set), "m" (*p)  	: "cc");  	return old; @@ -493,15 +512,6 @@ static inline pmd_t *pmd_offset(pgd_t *dir, unsigned long address)  extern pgd_t swapper_pg_dir[PTRS_PER_PGD];  /* - * When flushing the tlb entry for a page, we also need to flush the hash - * table entry.  flush_hash_page is assembler (for speed) in hashtable.S. - */ -extern int flush_hash_page(unsigned context, unsigned long va, pte_t *ptep); - -/* Add an HPTE to the hash table */ -extern void add_hash_page(unsigned context, unsigned long va, pte_t *ptep); - -/*   * Encode and decode a swap entry.   * Note that the bits we use in a PTE for representing a swap entry   * must not include the _PAGE_PRESENT bit, or the _PAGE_HASHPTE bit @@ -514,15 +524,7 @@ extern void add_hash_page(unsigned context, unsigned long va, pte_t *ptep);  #define __pte_to_swp_entry(pte)	((swp_entry_t) { pte_val(pte) >> 2 })  #define __swp_entry_to_pte(x)	((pte_t) { (x).val << 2 }) - -/* CONFIG_APUS */ -/* For virtual address to physical address conversion */ -extern void cache_clear(__u32 addr, int length); -extern void cache_push(__u32 addr, int length); -extern int mm_end_of_chunk(unsigned long addr, int len);  extern unsigned long iopa(unsigned long addr); -/* extern unsigned long mm_ptov(unsigned long addr) \ -	__attribute__ ((const)); TBD */  /* Values for nocacheflag and cmode */  /* These are not used by the APUS kernel_map, but prevents @@ -533,18 +535,6 @@ extern unsigned long iopa(unsigned long addr);  #define	IOMAP_NOCACHE_NONSER	2  #define	IOMAP_NO_COPYBACK	3 -/* - * Map some physical address range into the kernel address space. - */ -extern unsigned long kernel_map(unsigned long paddr, unsigned long size, -				int nocacheflag, unsigned long *memavailp); - -/* - * Set cache mode of (kernel space) address range. - */ -extern void kernel_set_cachemode(unsigned long address, unsigned long size, -				unsigned int cmode); -  /* Needs to be defined here and not in linux/mm.h, as it is arch dependent */  #define kern_addr_valid(addr)	(1) @@ -558,26 +548,15 @@ extern void kernel_set_cachemode(unsigned long address, unsigned long size,  void do_page_fault(struct pt_regs *regs, unsigned long address,  		   unsigned long error_code); -void __init io_block_mapping(unsigned long virt, phys_addr_t phys, -			     unsigned int size, int flags); - -void __init adjust_total_lowmem(void);  void mapin_ram(void);  int map_page(unsigned long va, phys_addr_t pa, int flags);  extern int mem_init_done; -extern unsigned long ioremap_base; -extern unsigned long ioremap_bot;  asmlinkage void __init mmu_init(void);  void __init *early_get_page(void); -void *consistent_alloc(int gfp, size_t size, dma_addr_t *dma_handle); -void consistent_free(void *vaddr); -void consistent_sync(void *vaddr, size_t size, int direction); -void consistent_sync_page(struct page *page, unsigned long offset, -	size_t size, int direction);  #endif /* __ASSEMBLY__ */  #endif /* __KERNEL__ */ @@ -586,6 +565,14 @@ void consistent_sync_page(struct page *page, unsigned long offset,  #ifndef __ASSEMBLY__  #include <asm-generic/pgtable.h> +extern unsigned long ioremap_bot, ioremap_base; + +void *consistent_alloc(int gfp, size_t size, dma_addr_t *dma_handle); +void consistent_free(size_t size, void *vaddr); +void consistent_sync(void *vaddr, size_t size, int direction); +void consistent_sync_page(struct page *page, unsigned long offset, +	size_t size, int direction); +  void setup_memory(void);  #endif /* __ASSEMBLY__ */ diff --git a/arch/microblaze/include/asm/processor.h b/arch/microblaze/include/asm/processor.h index 563c6b9453f..8eeb09211ec 100644 --- a/arch/microblaze/include/asm/processor.h +++ b/arch/microblaze/include/asm/processor.h @@ -14,7 +14,6 @@  #include <asm/ptrace.h>  #include <asm/setup.h>  #include <asm/registers.h> -#include <asm/segment.h>  #include <asm/entry.h>  #include <asm/current.h> diff --git a/arch/microblaze/include/asm/prom.h b/arch/microblaze/include/asm/prom.h index 03f45a96320..e7d67a329bd 100644 --- a/arch/microblaze/include/asm/prom.h +++ b/arch/microblaze/include/asm/prom.h @@ -31,6 +31,21 @@  /* Other Prototypes */  extern int early_uartlite_console(void); +#ifdef CONFIG_PCI +/* + * PCI <-> OF matching functions + * (XXX should these be here?) + */ +struct pci_bus; +struct pci_dev; +extern int pci_device_from_OF_node(struct device_node *node, +					u8 *bus, u8 *devfn); +extern struct device_node *pci_busdev_to_OF_node(struct pci_bus *bus, +							int devfn); +extern struct device_node *pci_device_to_OF_node(struct pci_dev *dev); +extern void pci_create_OF_bus_map(void); +#endif +  /*   * OF address retreival & translation   */ diff --git a/arch/microblaze/include/asm/segment.h b/arch/microblaze/include/asm/segment.h deleted file mode 100644 index 0e7102c3fb1..00000000000 --- a/arch/microblaze/include/asm/segment.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2008-2009 Michal Simek <monstr@monstr.eu> - * Copyright (C) 2008-2009 PetaLogix - * Copyright (C) 2006 Atmark Techno, Inc. - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - */ - -#ifndef _ASM_MICROBLAZE_SEGMENT_H -#define _ASM_MICROBLAZE_SEGMENT_H - -# ifndef __ASSEMBLY__ - -typedef struct { -	unsigned long seg; -} mm_segment_t; - -/* - * On Microblaze the fs value is actually the top of the corresponding - * address space. - * - * The fs value determines whether argument validity checking should be - * performed or not. If get_fs() == USER_DS, checking is performed, with - * get_fs() == KERNEL_DS, checking is bypassed. - * - * For historical reasons, these macros are grossly misnamed. - * - * For non-MMU arch like Microblaze, KERNEL_DS and USER_DS is equal. - */ -# define MAKE_MM_SEG(s)       ((mm_segment_t) { (s) }) - -#  ifndef CONFIG_MMU -#  define KERNEL_DS	MAKE_MM_SEG(0) -#  define USER_DS	KERNEL_DS -#  else -#  define KERNEL_DS	MAKE_MM_SEG(0xFFFFFFFF) -#  define USER_DS	MAKE_MM_SEG(TASK_SIZE - 1) -#  endif - -# define get_ds()	(KERNEL_DS) -# define get_fs()	(current_thread_info()->addr_limit) -# define set_fs(val)	(current_thread_info()->addr_limit = (val)) - -# define segment_eq(a, b)	((a).seg == (b).seg) - -# endif /* __ASSEMBLY__ */ -#endif /* _ASM_MICROBLAZE_SEGMENT_H */ diff --git a/arch/microblaze/include/asm/system.h b/arch/microblaze/include/asm/system.h index 157970688b2..59efb3fef95 100644 --- a/arch/microblaze/include/asm/system.h +++ b/arch/microblaze/include/asm/system.h @@ -87,6 +87,9 @@ void free_initmem(void);  extern char *klimit;  extern void ret_from_fork(void); +extern void *alloc_maybe_bootmem(size_t size, gfp_t mask); +extern void *zalloc_maybe_bootmem(size_t size, gfp_t mask); +  #ifdef CONFIG_DEBUG_FS  extern struct dentry *of_debugfs_root;  #endif diff --git a/arch/microblaze/include/asm/thread_info.h b/arch/microblaze/include/asm/thread_info.h index 6e92885d381..b2ca80f6464 100644 --- a/arch/microblaze/include/asm/thread_info.h +++ b/arch/microblaze/include/asm/thread_info.h @@ -19,7 +19,6 @@  #ifndef __ASSEMBLY__  # include <linux/types.h>  # include <asm/processor.h> -# include <asm/segment.h>  /*   * low level task data that entry.S needs immediate access to @@ -60,6 +59,10 @@ struct cpu_context {  	__u32	fsr;  }; +typedef struct { +	unsigned long seg; +} mm_segment_t; +  struct thread_info {  	struct task_struct	*task; /* main task structure */  	struct exec_domain	*exec_domain; /* execution domain */ diff --git a/arch/microblaze/include/asm/tlbflush.h b/arch/microblaze/include/asm/tlbflush.h index 10ec70cd873..2e1353c2d18 100644 --- a/arch/microblaze/include/asm/tlbflush.h +++ b/arch/microblaze/include/asm/tlbflush.h @@ -23,7 +23,8 @@  extern void _tlbie(unsigned long address);  extern void _tlbia(void); -#define __tlbia()	_tlbia() +#define __tlbia()	{ preempt_disable(); _tlbia(); preempt_enable(); } +#define __tlbie(x)	{ _tlbie(x); }  static inline void local_flush_tlb_all(void)  	{ __tlbia(); } @@ -31,7 +32,7 @@ static inline void local_flush_tlb_mm(struct mm_struct *mm)  	{ __tlbia(); }  static inline void local_flush_tlb_page(struct vm_area_struct *vma,  				unsigned long vmaddr) -	{ _tlbie(vmaddr); } +	{ __tlbie(vmaddr); }  static inline void local_flush_tlb_range(struct vm_area_struct *vma,  		unsigned long start, unsigned long end)  	{ __tlbia(); } diff --git a/arch/microblaze/include/asm/uaccess.h b/arch/microblaze/include/asm/uaccess.h index 371bd6e56d9..26460d15b33 100644 --- a/arch/microblaze/include/asm/uaccess.h +++ b/arch/microblaze/include/asm/uaccess.h @@ -22,101 +22,73 @@  #include <asm/mmu.h>  #include <asm/page.h>  #include <asm/pgtable.h> -#include <asm/segment.h>  #include <linux/string.h>  #define VERIFY_READ	0  #define VERIFY_WRITE	1 -#define __clear_user(addr, n)	(memset((void *)(addr), 0, (n)), 0) - -#ifndef CONFIG_MMU - -extern int ___range_ok(unsigned long addr, unsigned long size); - -#define __range_ok(addr, size) \ -		___range_ok((unsigned long)(addr), (unsigned long)(size)) - -#define access_ok(type, addr, size) (__range_ok((addr), (size)) == 0) -#define __access_ok(add, size) (__range_ok((addr), (size)) == 0) - -/* Undefined function to trigger linker error */ -extern int bad_user_access_length(void); +/* + * On Microblaze the fs value is actually the top of the corresponding + * address space. + * + * The fs value determines whether argument validity checking should be + * performed or not. If get_fs() == USER_DS, checking is performed, with + * get_fs() == KERNEL_DS, checking is bypassed. + * + * For historical reasons, these macros are grossly misnamed. + * + * For non-MMU arch like Microblaze, KERNEL_DS and USER_DS is equal. + */ +# define MAKE_MM_SEG(s)       ((mm_segment_t) { (s) }) -/* FIXME this is function for optimalization -> memcpy */ -#define __get_user(var, ptr)				\ -({							\ -	int __gu_err = 0;				\ -	switch (sizeof(*(ptr))) {			\ -	case 1:						\ -	case 2:						\ -	case 4:						\ -		(var) = *(ptr);				\ -		break;					\ -	case 8:						\ -		memcpy((void *) &(var), (ptr), 8);	\ -		break;					\ -	default:					\ -		(var) = 0;				\ -		__gu_err = __get_user_bad();		\ -		break;					\ -	}						\ -	__gu_err;					\ -}) +#  ifndef CONFIG_MMU +#  define KERNEL_DS	MAKE_MM_SEG(0) +#  define USER_DS	KERNEL_DS +#  else +#  define KERNEL_DS	MAKE_MM_SEG(0xFFFFFFFF) +#  define USER_DS	MAKE_MM_SEG(TASK_SIZE - 1) +#  endif -#define __get_user_bad()	(bad_user_access_length(), (-EFAULT)) +# define get_ds()	(KERNEL_DS) +# define get_fs()	(current_thread_info()->addr_limit) +# define set_fs(val)	(current_thread_info()->addr_limit = (val)) -/* FIXME is not there defined __pu_val */ -#define __put_user(var, ptr)					\ -({								\ -	int __pu_err = 0;					\ -	switch (sizeof(*(ptr))) {				\ -	case 1:							\ -	case 2:							\ -	case 4:							\ -		*(ptr) = (var);					\ -		break;						\ -	case 8: {						\ -		typeof(*(ptr)) __pu_val = (var);		\ -		memcpy(ptr, &__pu_val, sizeof(__pu_val));	\ -		}						\ -		break;						\ -	default:						\ -		__pu_err = __put_user_bad();			\ -		break;						\ -	}							\ -	__pu_err;						\ -}) +# define segment_eq(a, b)	((a).seg == (b).seg) -#define __put_user_bad()	(bad_user_access_length(), (-EFAULT)) - -#define put_user(x, ptr)	__put_user((x), (ptr)) -#define get_user(x, ptr)	__get_user((x), (ptr)) +/* + * The exception table consists of pairs of addresses: the first is the + * address of an instruction that is allowed to fault, and the second is + * the address at which the program should continue. No registers are + * modified, so it is entirely up to the continuation code to figure out + * what to do. + * + * All the routines below use bits of fixup code that are out of line + * with the main instruction path. This means when everything is well, + * we don't even have to jump over them. Further, they do not intrude + * on our cache or tlb entries. + */ +struct exception_table_entry { +	unsigned long insn, fixup; +}; -#define copy_to_user(to, from, n)	(memcpy((to), (from), (n)), 0) -#define copy_from_user(to, from, n)	(memcpy((to), (from), (n)), 0) +/* Returns 0 if exception not found and fixup otherwise.  */ +extern unsigned long search_exception_table(unsigned long); -#define __copy_to_user(to, from, n)	(copy_to_user((to), (from), (n))) -#define __copy_from_user(to, from, n)	(copy_from_user((to), (from), (n))) -#define __copy_to_user_inatomic(to, from, n) \ -			(__copy_to_user((to), (from), (n))) -#define __copy_from_user_inatomic(to, from, n) \ -			(__copy_from_user((to), (from), (n))) +#ifndef CONFIG_MMU -static inline unsigned long clear_user(void *addr, unsigned long size) +/* Check against bounds of physical memory */ +static inline int ___range_ok(unsigned long addr, unsigned long size)  { -	if (access_ok(VERIFY_WRITE, addr, size)) -		size = __clear_user(addr, size); -	return size; +	return ((addr < memory_start) || +		((addr + size) > memory_end));  } -/* Returns 0 if exception not found and fixup otherwise.  */ -extern unsigned long search_exception_table(unsigned long); +#define __range_ok(addr, size) \ +		___range_ok((unsigned long)(addr), (unsigned long)(size)) -extern long strncpy_from_user(char *dst, const char *src, long count); -extern long strnlen_user(const char *src, long count); +#define access_ok(type, addr, size) (__range_ok((addr), (size)) == 0) -#else /* CONFIG_MMU */ +#else  /*   * Address is valid if: @@ -129,22 +101,119 @@ extern long strnlen_user(const char *src, long count);  /* || printk("access_ok failed for %s at 0x%08lx (size %d), seg 0x%08x\n",   type?"WRITE":"READ",addr,size,get_fs().seg)) */ -/* - * All the __XXX versions macros/functions below do not perform - * access checking. It is assumed that the necessary checks have been - * already performed before the finction (macro) is called. - */ +#endif -#define get_user(x, ptr)						\ -({									\ -	access_ok(VERIFY_READ, (ptr), sizeof(*(ptr)))			\ -		? __get_user((x), (ptr)) : -EFAULT;			\ +#ifdef CONFIG_MMU +# define __FIXUP_SECTION	".section .fixup,\"ax\"\n" +# define __EX_TABLE_SECTION	".section __ex_table,\"a\"\n" +#else +# define __FIXUP_SECTION	".section .discard,\"ax\"\n" +# define __EX_TABLE_SECTION	".section .discard,\"a\"\n" +#endif + +extern unsigned long __copy_tofrom_user(void __user *to, +		const void __user *from, unsigned long size); + +/* Return: number of not copied bytes, i.e. 0 if OK or non-zero if fail. */ +static inline unsigned long __must_check __clear_user(void __user *to, +							unsigned long n) +{ +	/* normal memset with two words to __ex_table */ +	__asm__ __volatile__ (				\ +			"1:	sb	r0, %2, r0;"	\ +			"	addik	%0, %0, -1;"	\ +			"	bneid	%0, 1b;"	\ +			"	addik	%2, %2, 1;"	\ +			"2:			"	\ +			__EX_TABLE_SECTION		\ +			".word	1b,2b;"			\ +			".previous;"			\ +		: "=r"(n)				\ +		: "0"(n), "r"(to) +	); +	return n; +} + +static inline unsigned long __must_check clear_user(void __user *to, +							unsigned long n) +{ +	might_sleep(); +	if (unlikely(!access_ok(VERIFY_WRITE, to, n))) +		return n; + +	return __clear_user(to, n); +} + +/* put_user and get_user macros */ +extern long __user_bad(void); + +#define __get_user_asm(insn, __gu_ptr, __gu_val, __gu_err)	\ +({								\ +	__asm__ __volatile__ (					\ +			"1:"	insn	" %1, %2, r0;"		\ +			"	addk	%0, r0, r0;"		\ +			"2:			"		\ +			__FIXUP_SECTION				\ +			"3:	brid	2b;"			\ +			"	addik	%0, r0, %3;"		\ +			".previous;"				\ +			__EX_TABLE_SECTION			\ +			".word	1b,3b;"				\ +			".previous;"				\ +		: "=&r"(__gu_err), "=r"(__gu_val)		\ +		: "r"(__gu_ptr), "i"(-EFAULT)			\ +	);							\  }) -#define put_user(x, ptr)						\ +/** + * get_user: - Get a simple variable from user space. + * @x:   Variable to store result. + * @ptr: Source address, in user space. + * + * Context: User context only.  This function may sleep. + * + * This macro copies a single simple variable from user space to kernel + * space.  It supports simple types like char and int, but not larger + * data types like structures or arrays. + * + * @ptr must have pointer-to-simple-variable type, and the result of + * dereferencing @ptr must be assignable to @x without a cast. + * + * Returns zero on success, or -EFAULT on error. + * On error, the variable @x is set to zero. + */ +#define get_user(x, ptr)						\ +	__get_user_check((x), (ptr), sizeof(*(ptr))) + +#define __get_user_check(x, ptr, size)					\  ({									\ -	access_ok(VERIFY_WRITE, (ptr), sizeof(*(ptr)))			\ -		? __put_user((x), (ptr)) : -EFAULT;			\ +	unsigned long __gu_val = 0;					\ +	const typeof(*(ptr)) __user *__gu_addr = (ptr);			\ +	int __gu_err = 0;						\ +									\ +	if (access_ok(VERIFY_READ, __gu_addr, size)) {			\ +		switch (size) {						\ +		case 1:							\ +			__get_user_asm("lbu", __gu_addr, __gu_val,	\ +				       __gu_err);			\ +			break;						\ +		case 2:							\ +			__get_user_asm("lhu", __gu_addr, __gu_val,	\ +				       __gu_err);			\ +			break;						\ +		case 4:							\ +			__get_user_asm("lw", __gu_addr, __gu_val,	\ +				       __gu_err);			\ +			break;						\ +		default:						\ +			__gu_err = __user_bad();			\ +			break;						\ +		}							\ +	} else {							\ +		__gu_err = -EFAULT;					\ +	}								\ +	x = (typeof(*(ptr)))__gu_val;					\ +	__gu_err;							\  })  #define __get_user(x, ptr)						\ @@ -163,28 +232,102 @@ extern long strnlen_user(const char *src, long count);  		__get_user_asm("lw", (ptr), __gu_val, __gu_err);	\  		break;							\  	default:							\ -		__gu_val = 0; __gu_err = -EINVAL;			\ +		/* __gu_val = 0; __gu_err = -EINVAL;*/ __gu_err = __user_bad();\  	}								\  	x = (__typeof__(*(ptr))) __gu_val;				\  	__gu_err;							\  }) -#define __get_user_asm(insn, __gu_ptr, __gu_val, __gu_err)		\ + +#define __put_user_asm(insn, __gu_ptr, __gu_val, __gu_err)	\ +({								\ +	__asm__ __volatile__ (					\ +			"1:"	insn	" %1, %2, r0;"		\ +			"	addk	%0, r0, r0;"		\ +			"2:			"		\ +			__FIXUP_SECTION				\ +			"3:	brid	2b;"			\ +			"	addik	%0, r0, %3;"		\ +			".previous;"				\ +			__EX_TABLE_SECTION			\ +			".word	1b,3b;"				\ +			".previous;"				\ +		: "=&r"(__gu_err)				\ +		: "r"(__gu_val), "r"(__gu_ptr), "i"(-EFAULT)	\ +	);							\ +}) + +#define __put_user_asm_8(__gu_ptr, __gu_val, __gu_err)		\ +({								\ +	__asm__ __volatile__ ("	lwi	%0, %1, 0;"		\ +			"1:	swi	%0, %2, 0;"		\ +			"	lwi	%0, %1, 4;"		\ +			"2:	swi	%0, %2, 4;"		\ +			"	addk	%0, r0, r0;"		\ +			"3:			"		\ +			__FIXUP_SECTION				\ +			"4:	brid	3b;"			\ +			"	addik	%0, r0, %3;"		\ +			".previous;"				\ +			__EX_TABLE_SECTION			\ +			".word	1b,4b,2b,4b;"			\ +			".previous;"				\ +		: "=&r"(__gu_err)				\ +		: "r"(&__gu_val), "r"(__gu_ptr), "i"(-EFAULT)	\ +		);						\ +}) + +/** + * put_user: - Write a simple value into user space. + * @x:   Value to copy to user space. + * @ptr: Destination address, in user space. + * + * Context: User context only.  This function may sleep. + * + * This macro copies a single simple value from kernel space to user + * space.  It supports simple types like char and int, but not larger + * data types like structures or arrays. + * + * @ptr must have pointer-to-simple-variable type, and @x must be assignable + * to the result of dereferencing @ptr. + * + * Returns zero on success, or -EFAULT on error. + */ +#define put_user(x, ptr)						\ +	__put_user_check((x), (ptr), sizeof(*(ptr))) + +#define __put_user_check(x, ptr, size)					\  ({									\ -	__asm__ __volatile__ (						\ -			"1:"	insn	" %1, %2, r0;			\ -				addk	%0, r0, r0;			\ -			2:						\ -			.section .fixup,\"ax\";				\ -			3:	brid	2b;				\ -				addik	%0, r0, %3;			\ -			.previous;					\ -			.section __ex_table,\"a\";			\ -			.word	1b,3b;					\ -			.previous;"					\ -		: "=r"(__gu_err), "=r"(__gu_val)			\ -		: "r"(__gu_ptr), "i"(-EFAULT)				\ -	);								\ +	typeof(*(ptr)) __pu_val;					\ +	typeof(*(ptr)) __user *__pu_addr = (ptr);			\ +	int __pu_err = 0;						\ +									\ +	__pu_val = (x);							\ +	if (access_ok(VERIFY_WRITE, __pu_addr, size)) {			\ +		switch (size) {						\ +		case 1:							\ +			__put_user_asm("sb", __pu_addr, __pu_val,	\ +				       __pu_err);			\ +			break;						\ +		case 2:							\ +			__put_user_asm("sh", __pu_addr, __pu_val,	\ +				       __pu_err);			\ +			break;						\ +		case 4:							\ +			__put_user_asm("sw", __pu_addr, __pu_val,	\ +				       __pu_err);			\ +			break;						\ +		case 8:							\ +			__put_user_asm_8(__pu_addr, __pu_val, __pu_err);\ +			break;						\ +		default:						\ +			__pu_err = __user_bad();			\ +			break;						\ +		}							\ +	} else {							\ +		__pu_err = -EFAULT;					\ +	}								\ +	__pu_err;							\  })  #define __put_user(x, ptr)						\ @@ -195,7 +338,7 @@ extern long strnlen_user(const char *src, long count);  	case 1:								\  		__put_user_asm("sb", (ptr), __gu_val, __gu_err);	\  		break;							\ -	case 2: 							\ +	case 2:								\  		__put_user_asm("sh", (ptr), __gu_val, __gu_err);	\  		break;							\  	case 4:								\ @@ -205,121 +348,70 @@ extern long strnlen_user(const char *src, long count);  		__put_user_asm_8((ptr), __gu_val, __gu_err);		\  		break;							\  	default:							\ -		__gu_err = -EINVAL;					\ +		/*__gu_err = -EINVAL;*/	__gu_err = __user_bad();	\  	}								\  	__gu_err;							\  }) -#define __put_user_asm_8(__gu_ptr, __gu_val, __gu_err)	\ -({							\ -__asm__ __volatile__ ("	lwi	%0, %1, 0;		\ -		1:	swi	%0, %2, 0;		\ -			lwi	%0, %1, 4;		\ -		2:	swi	%0, %2, 4;		\ -			addk	%0,r0,r0;		\ -		3:					\ -		.section .fixup,\"ax\";			\ -		4:	brid	3b;			\ -			addik	%0, r0, %3;		\ -		.previous;				\ -		.section __ex_table,\"a\";		\ -		.word	1b,4b,2b,4b;			\ -		.previous;"				\ -	: "=&r"(__gu_err)				\ -	: "r"(&__gu_val),				\ -	"r"(__gu_ptr), "i"(-EFAULT)			\ -	);						\ -}) - -#define __put_user_asm(insn, __gu_ptr, __gu_val, __gu_err)	\ -({								\ -	__asm__ __volatile__ (					\ -			"1:"	insn	" %1, %2, r0;		\ -				addk	%0, r0, r0;		\ -			2:					\ -			.section .fixup,\"ax\";			\ -			3:	brid	2b;			\ -				addik	%0, r0, %3;		\ -			.previous;				\ -			.section __ex_table,\"a\";		\ -			.word	1b,3b;				\ -			.previous;"				\ -		: "=r"(__gu_err)				\ -		: "r"(__gu_val), "r"(__gu_ptr), "i"(-EFAULT)	\ -	);							\ -}) - -/* - * Return: number of not copied bytes, i.e. 0 if OK or non-zero if fail. - */ -static inline int clear_user(char *to, int size) -{ -	if (size && access_ok(VERIFY_WRITE, to, size)) { -		__asm__ __volatile__ ("				\ -				1:				\ -					sb	r0, %2, r0;	\ -					addik	%0, %0, -1;	\ -					bneid	%0, 1b;		\ -					addik	%2, %2, 1;	\ -				2:				\ -				.section __ex_table,\"a\";	\ -				.word	1b,2b;			\ -				.section .text;"		\ -			: "=r"(size)				\ -			: "0"(size), "r"(to) -		); -	} -	return size; -} -#define __copy_from_user(to, from, n)	copy_from_user((to), (from), (n)) +/* copy_to_from_user */ +#define __copy_from_user(to, from, n)	\ +	__copy_tofrom_user((__force void __user *)(to), \ +				(void __user *)(from), (n))  #define __copy_from_user_inatomic(to, from, n) \  		copy_from_user((to), (from), (n)) -#define copy_to_user(to, from, n)					\ -	(access_ok(VERIFY_WRITE, (to), (n)) ?				\ -		__copy_tofrom_user((void __user *)(to),			\ -			(__force const void __user *)(from), (n))	\ -		: -EFAULT) +static inline long copy_from_user(void *to, +		const void __user *from, unsigned long n) +{ +	might_sleep(); +	if (access_ok(VERIFY_READ, from, n)) +		return __copy_from_user(to, from, n); +	return n; +} -#define __copy_to_user(to, from, n)	copy_to_user((to), (from), (n)) +#define __copy_to_user(to, from, n)	\ +		__copy_tofrom_user((void __user *)(to), \ +			(__force const void __user *)(from), (n))  #define __copy_to_user_inatomic(to, from, n)	copy_to_user((to), (from), (n)) -#define copy_from_user(to, from, n)					\ -	(access_ok(VERIFY_READ, (from), (n)) ?				\ -		__copy_tofrom_user((__force void __user *)(to),		\ -			(void __user *)(from), (n))			\ -		: -EFAULT) +static inline long copy_to_user(void __user *to, +		const void *from, unsigned long n) +{ +	might_sleep(); +	if (access_ok(VERIFY_WRITE, to, n)) +		return __copy_to_user(to, from, n); +	return n; +} +/* + * Copy a null terminated string from userspace. + */  extern int __strncpy_user(char *to, const char __user *from, int len); -extern int __strnlen_user(const char __user *sstr, int len); - -#define strncpy_from_user(to, from, len)	\ -		(access_ok(VERIFY_READ, from, 1) ?	\ -			__strncpy_user(to, from, len) : -EFAULT) -#define strnlen_user(str, len)	\ -		(access_ok(VERIFY_READ, str, 1) ? __strnlen_user(str, len) : 0) -#endif /* CONFIG_MMU */ +#define __strncpy_from_user	__strncpy_user -extern unsigned long __copy_tofrom_user(void __user *to, -		const void __user *from, unsigned long size); +static inline long +strncpy_from_user(char *dst, const char __user *src, long count) +{ +	if (!access_ok(VERIFY_READ, src, 1)) +		return -EFAULT; +	return __strncpy_from_user(dst, src, count); +}  /* - * The exception table consists of pairs of addresses: the first is the - * address of an instruction that is allowed to fault, and the second is - * the address at which the program should continue. No registers are - * modified, so it is entirely up to the continuation code to figure out - * what to do. + * Return the size of a string (including the ending 0)   * - * All the routines below use bits of fixup code that are out of line - * with the main instruction path. This means when everything is well, - * we don't even have to jump over them. Further, they do not intrude - * on our cache or tlb entries. + * Return 0 on exception, a value greater than N if too long   */ -struct exception_table_entry { -	unsigned long insn, fixup; -}; +extern int __strnlen_user(const char __user *sstr, int len); + +static inline long strnlen_user(const char __user *src, long n) +{ +	if (!access_ok(VERIFY_READ, src, 1)) +		return 0; +	return __strnlen_user(src, n); +}  #endif  /* __ASSEMBLY__ */  #endif /* __KERNEL__ */ diff --git a/arch/microblaze/kernel/Makefile b/arch/microblaze/kernel/Makefile index b07594eccf9..e51bc152082 100644 --- a/arch/microblaze/kernel/Makefile +++ b/arch/microblaze/kernel/Makefile @@ -14,7 +14,7 @@ endif  extra-y := head.o vmlinux.lds -obj-y += exceptions.o \ +obj-y += dma.o exceptions.o \  	hw_exception_handler.o init_task.o intc.o irq.o of_device.o \  	of_platform.o process.o prom.o prom_parse.o ptrace.o \  	setup.o signal.o sys_microblaze.o timer.o traps.o reset.o diff --git a/arch/microblaze/kernel/asm-offsets.c b/arch/microblaze/kernel/asm-offsets.c index 7bc7b68f97d..c1b459c9757 100644 --- a/arch/microblaze/kernel/asm-offsets.c +++ b/arch/microblaze/kernel/asm-offsets.c @@ -16,6 +16,7 @@  #include <linux/hardirq.h>  #include <linux/thread_info.h>  #include <linux/kbuild.h> +#include <asm/cpuinfo.h>  int main(int argc, char *argv[])  { @@ -90,6 +91,7 @@ int main(int argc, char *argv[])  	DEFINE(TI_FLAGS, offsetof(struct thread_info, flags));  	DEFINE(TI_ADDR_LIMIT, offsetof(struct thread_info, addr_limit));  	DEFINE(TI_CPU_CONTEXT, offsetof(struct thread_info, cpu_context)); +	DEFINE(TI_PREEMPT_COUNT, offsetof(struct thread_info, preempt_count));  	BLANK();  	/* struct cpu_context */ diff --git a/arch/microblaze/kernel/cpu/cache.c b/arch/microblaze/kernel/cpu/cache.c index 2a56bccce4e..109876e8d64 100644 --- a/arch/microblaze/kernel/cpu/cache.c +++ b/arch/microblaze/kernel/cpu/cache.c @@ -15,25 +15,6 @@  #include <asm/cpuinfo.h>  #include <asm/pvr.h> -static inline void __invalidate_flush_icache(unsigned int addr) -{ -	__asm__ __volatile__ ("wic	%0, r0;"	\ -					: : "r" (addr)); -} - -static inline void __flush_dcache(unsigned int addr) -{ -	__asm__ __volatile__ ("wdc.flush	%0, r0;"	\ -					: : "r" (addr)); -} - -static inline void __invalidate_dcache(unsigned int baseaddr, -						unsigned int offset) -{ -	__asm__ __volatile__ ("wdc.clear	%0, %1;"	\ -					: : "r" (baseaddr), "r" (offset)); -} -  static inline void __enable_icache_msr(void)  {  	__asm__ __volatile__ ("	msrset	r0, %0;		\ @@ -115,13 +96,16 @@ static inline void __disable_dcache_nomsr(void)  } -/* Helper macro for computing the limits of cache range loops */ +/* Helper macro for computing the limits of cache range loops + * + * End address can be unaligned which is OK for C implementation. + * ASM implementation align it in ASM macros + */  #define CACHE_LOOP_LIMITS(start, end, cache_line_length, cache_size)	\  do {									\  	int align = ~(cache_line_length - 1);				\  	end = min(start + cache_size, end);				\  	start &= align;							\ -	end = ((end & align) + cache_line_length);			\  } while (0);  /* @@ -130,9 +114,9 @@ do {									\   */  #define CACHE_ALL_LOOP(cache_size, line_length, op)			\  do {									\ -	unsigned int len = cache_size;					\ +	unsigned int len = cache_size - line_length;			\  	int step = -line_length;					\ -	BUG_ON(step >= 0);						\ +	WARN_ON(step >= 0);						\  									\  	__asm__ __volatile__ (" 1:      " #op " %0, r0;			\  					bgtid   %0, 1b;			\ @@ -141,30 +125,26 @@ do {									\  					: "memory");			\  } while (0); - -#define CACHE_ALL_LOOP2(cache_size, line_length, op)			\ -do {									\ -	unsigned int len = cache_size;					\ -	int step = -line_length;					\ -	BUG_ON(step >= 0);						\ -									\ -	__asm__ __volatile__ (" 1:      " #op " r0, %0;			\ -					bgtid   %0, 1b;			\ -					addk    %0, %0, %1;		\ -					" : : "r" (len), "r" (step)	\ -					: "memory");			\ -} while (0); - -/* for wdc.flush/clear */ +/* Used for wdc.flush/clear which can use rB for offset which is not possible + * to use for simple wdc or wic. + * + * start address is cache aligned + * end address is not aligned, if end is aligned then I have to substract + * cacheline length because I can't flush/invalidate the next cacheline. + * If is not, I align it because I will flush/invalidate whole line. + */  #define CACHE_RANGE_LOOP_2(start, end, line_length, op)			\  do {									\  	int step = -line_length;					\ -	int count = end - start;					\ -	BUG_ON(count <= 0);						\ +	int align = ~(line_length - 1);					\ +	int count;							\ +	end = ((end & align) == end) ? end - line_length : end & align;	\ +	count = end - start;						\ +	WARN_ON(count < 0);						\  									\ -	__asm__ __volatile__ (" 1:	" #op " %0, %1;			\ -					bgtid   %1, 1b;			\ -					addk    %1, %1, %2;		\ +	__asm__ __volatile__ (" 1:	" #op "	%0, %1;			\ +					bgtid	%1, 1b;			\ +					addk	%1, %1, %2;		\  					" : : "r" (start), "r" (count),	\  					"r" (step) : "memory");		\  } while (0); @@ -173,9 +153,11 @@ do {									\  #define CACHE_RANGE_LOOP_1(start, end, line_length, op)			\  do {									\  	int volatile temp;						\ -	BUG_ON(end - start <= 0);					\ +	int align = ~(line_length - 1);					\ +	end = ((end & align) == end) ? end - line_length : end & align;	\ +	WARN_ON(end - start < 0);					\  									\ -	__asm__ __volatile__ (" 1:	" #op " %1, r0;			\ +	__asm__ __volatile__ (" 1:	" #op "	%1, r0;			\  					cmpu	%0, %1, %2;		\  					bgtid	%0, 1b;			\  					addk	%1, %1, %3;		\ @@ -183,10 +165,14 @@ do {									\  					"r" (line_length) : "memory");	\  } while (0); +#define ASM_LOOP +  static void __flush_icache_range_msr_irq(unsigned long start, unsigned long end)  {  	unsigned long flags; - +#ifndef ASM_LOOP +	int i; +#endif  	pr_debug("%s: start 0x%x, end 0x%x\n", __func__,  				(unsigned int)start, (unsigned int) end); @@ -196,8 +182,13 @@ static void __flush_icache_range_msr_irq(unsigned long start, unsigned long end)  	local_irq_save(flags);  	__disable_icache_msr(); +#ifdef ASM_LOOP  	CACHE_RANGE_LOOP_1(start, end, cpuinfo.icache_line_length, wic); - +#else +	for (i = start; i < end; i += cpuinfo.icache_line_length) +		__asm__ __volatile__ ("wic	%0, r0;"	\ +				: : "r" (i)); +#endif  	__enable_icache_msr();  	local_irq_restore(flags);  } @@ -206,7 +197,9 @@ static void __flush_icache_range_nomsr_irq(unsigned long start,  				unsigned long end)  {  	unsigned long flags; - +#ifndef ASM_LOOP +	int i; +#endif  	pr_debug("%s: start 0x%x, end 0x%x\n", __func__,  				(unsigned int)start, (unsigned int) end); @@ -216,7 +209,13 @@ static void __flush_icache_range_nomsr_irq(unsigned long start,  	local_irq_save(flags);  	__disable_icache_nomsr(); +#ifdef ASM_LOOP  	CACHE_RANGE_LOOP_1(start, end, cpuinfo.icache_line_length, wic); +#else +	for (i = start; i < end; i += cpuinfo.icache_line_length) +		__asm__ __volatile__ ("wic	%0, r0;"	\ +				: : "r" (i)); +#endif  	__enable_icache_nomsr();  	local_irq_restore(flags); @@ -225,25 +224,41 @@ static void __flush_icache_range_nomsr_irq(unsigned long start,  static void __flush_icache_range_noirq(unsigned long start,  				unsigned long end)  { +#ifndef ASM_LOOP +	int i; +#endif  	pr_debug("%s: start 0x%x, end 0x%x\n", __func__,  				(unsigned int)start, (unsigned int) end);  	CACHE_LOOP_LIMITS(start, end,  			cpuinfo.icache_line_length, cpuinfo.icache_size); +#ifdef ASM_LOOP  	CACHE_RANGE_LOOP_1(start, end, cpuinfo.icache_line_length, wic); +#else +	for (i = start; i < end; i += cpuinfo.icache_line_length) +		__asm__ __volatile__ ("wic	%0, r0;"	\ +				: : "r" (i)); +#endif  }  static void __flush_icache_all_msr_irq(void)  {  	unsigned long flags; - +#ifndef ASM_LOOP +	int i; +#endif  	pr_debug("%s\n", __func__);  	local_irq_save(flags);  	__disable_icache_msr(); - +#ifdef ASM_LOOP  	CACHE_ALL_LOOP(cpuinfo.icache_size, cpuinfo.icache_line_length, wic); - +#else +	for (i = 0; i < cpuinfo.icache_size; +		 i += cpuinfo.icache_line_length) +			__asm__ __volatile__ ("wic	%0, r0;" \ +					: : "r" (i)); +#endif  	__enable_icache_msr();  	local_irq_restore(flags);  } @@ -251,35 +266,59 @@ static void __flush_icache_all_msr_irq(void)  static void __flush_icache_all_nomsr_irq(void)  {  	unsigned long flags; - +#ifndef ASM_LOOP +	int i; +#endif  	pr_debug("%s\n", __func__);  	local_irq_save(flags);  	__disable_icache_nomsr(); - +#ifdef ASM_LOOP  	CACHE_ALL_LOOP(cpuinfo.icache_size, cpuinfo.icache_line_length, wic); - +#else +	for (i = 0; i < cpuinfo.icache_size; +		 i += cpuinfo.icache_line_length) +			__asm__ __volatile__ ("wic	%0, r0;" \ +					: : "r" (i)); +#endif  	__enable_icache_nomsr();  	local_irq_restore(flags);  }  static void __flush_icache_all_noirq(void)  { +#ifndef ASM_LOOP +	int i; +#endif  	pr_debug("%s\n", __func__); +#ifdef ASM_LOOP  	CACHE_ALL_LOOP(cpuinfo.icache_size, cpuinfo.icache_line_length, wic); +#else +	for (i = 0; i < cpuinfo.icache_size; +		 i += cpuinfo.icache_line_length) +			__asm__ __volatile__ ("wic	%0, r0;" \ +					: : "r" (i)); +#endif  }  static void __invalidate_dcache_all_msr_irq(void)  {  	unsigned long flags; - +#ifndef ASM_LOOP +	int i; +#endif  	pr_debug("%s\n", __func__);  	local_irq_save(flags);  	__disable_dcache_msr(); - +#ifdef ASM_LOOP  	CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, wdc); - +#else +	for (i = 0; i < cpuinfo.dcache_size; +		 i += cpuinfo.dcache_line_length) +			__asm__ __volatile__ ("wdc	%0, r0;" \ +					: : "r" (i)); +#endif  	__enable_dcache_msr();  	local_irq_restore(flags);  } @@ -287,60 +326,111 @@ static void __invalidate_dcache_all_msr_irq(void)  static void __invalidate_dcache_all_nomsr_irq(void)  {  	unsigned long flags; - +#ifndef ASM_LOOP +	int i; +#endif  	pr_debug("%s\n", __func__);  	local_irq_save(flags);  	__disable_dcache_nomsr(); - +#ifdef ASM_LOOP  	CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, wdc); - +#else +	for (i = 0; i < cpuinfo.dcache_size; +		 i += cpuinfo.dcache_line_length) +			__asm__ __volatile__ ("wdc	%0, r0;" \ +					: : "r" (i)); +#endif  	__enable_dcache_nomsr();  	local_irq_restore(flags);  }  static void __invalidate_dcache_all_noirq_wt(void)  { +#ifndef ASM_LOOP +	int i; +#endif  	pr_debug("%s\n", __func__); +#ifdef ASM_LOOP  	CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, wdc) +#else +	for (i = 0; i < cpuinfo.dcache_size; +		 i += cpuinfo.dcache_line_length) +			__asm__ __volatile__ ("wdc	%0, r0;" \ +					: : "r" (i)); +#endif  } -/* FIXME this is weird - should be only wdc but not work - * MS: I am getting bus errors and other weird things */ +/* FIXME It is blindly invalidation as is expected + * but can't be called on noMMU in microblaze_cache_init below + * + * MS: noMMU kernel won't boot if simple wdc is used + * The reason should be that there are discared data which kernel needs + */  static void __invalidate_dcache_all_wb(void)  { +#ifndef ASM_LOOP +	int i; +#endif  	pr_debug("%s\n", __func__); -	CACHE_ALL_LOOP2(cpuinfo.dcache_size, cpuinfo.dcache_line_length, -					wdc.clear) +#ifdef ASM_LOOP +	CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, +					wdc) +#else +	for (i = 0; i < cpuinfo.dcache_size; +		 i += cpuinfo.dcache_line_length) +			__asm__ __volatile__ ("wdc	%0, r0;" \ +					: : "r" (i)); +#endif  }  static void __invalidate_dcache_range_wb(unsigned long start,  						unsigned long end)  { +#ifndef ASM_LOOP +	int i; +#endif  	pr_debug("%s: start 0x%x, end 0x%x\n", __func__,  				(unsigned int)start, (unsigned int) end);  	CACHE_LOOP_LIMITS(start, end,  			cpuinfo.dcache_line_length, cpuinfo.dcache_size); +#ifdef ASM_LOOP  	CACHE_RANGE_LOOP_2(start, end, cpuinfo.dcache_line_length, wdc.clear); +#else +	for (i = start; i < end; i += cpuinfo.dcache_line_length) +		__asm__ __volatile__ ("wdc.clear	%0, r0;"	\ +				: : "r" (i)); +#endif  }  static void __invalidate_dcache_range_nomsr_wt(unsigned long start,  							unsigned long end)  { +#ifndef ASM_LOOP +	int i; +#endif  	pr_debug("%s: start 0x%x, end 0x%x\n", __func__,  				(unsigned int)start, (unsigned int) end);  	CACHE_LOOP_LIMITS(start, end,  			cpuinfo.dcache_line_length, cpuinfo.dcache_size); +#ifdef ASM_LOOP  	CACHE_RANGE_LOOP_1(start, end, cpuinfo.dcache_line_length, wdc); +#else +	for (i = start; i < end; i += cpuinfo.dcache_line_length) +		__asm__ __volatile__ ("wdc	%0, r0;"	\ +				: : "r" (i)); +#endif  }  static void __invalidate_dcache_range_msr_irq_wt(unsigned long start,  							unsigned long end)  {  	unsigned long flags; - +#ifndef ASM_LOOP +	int i; +#endif  	pr_debug("%s: start 0x%x, end 0x%x\n", __func__,  				(unsigned int)start, (unsigned int) end);  	CACHE_LOOP_LIMITS(start, end, @@ -349,7 +439,13 @@ static void __invalidate_dcache_range_msr_irq_wt(unsigned long start,  	local_irq_save(flags);  	__disable_dcache_msr(); +#ifdef ASM_LOOP  	CACHE_RANGE_LOOP_1(start, end, cpuinfo.dcache_line_length, wdc); +#else +	for (i = start; i < end; i += cpuinfo.dcache_line_length) +		__asm__ __volatile__ ("wdc	%0, r0;"	\ +				: : "r" (i)); +#endif  	__enable_dcache_msr();  	local_irq_restore(flags); @@ -359,7 +455,9 @@ static void __invalidate_dcache_range_nomsr_irq(unsigned long start,  							unsigned long end)  {  	unsigned long flags; - +#ifndef ASM_LOOP +	int i; +#endif  	pr_debug("%s: start 0x%x, end 0x%x\n", __func__,  				(unsigned int)start, (unsigned int) end); @@ -369,7 +467,13 @@ static void __invalidate_dcache_range_nomsr_irq(unsigned long start,  	local_irq_save(flags);  	__disable_dcache_nomsr(); +#ifdef ASM_LOOP  	CACHE_RANGE_LOOP_1(start, end, cpuinfo.dcache_line_length, wdc); +#else +	for (i = start; i < end; i += cpuinfo.dcache_line_length) +		__asm__ __volatile__ ("wdc	%0, r0;"	\ +				: : "r" (i)); +#endif  	__enable_dcache_nomsr();  	local_irq_restore(flags); @@ -377,19 +481,38 @@ static void __invalidate_dcache_range_nomsr_irq(unsigned long start,  static void __flush_dcache_all_wb(void)  { +#ifndef ASM_LOOP +	int i; +#endif  	pr_debug("%s\n", __func__); +#ifdef ASM_LOOP  	CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length,  				wdc.flush); +#else +	for (i = 0; i < cpuinfo.dcache_size; +		 i += cpuinfo.dcache_line_length) +			__asm__ __volatile__ ("wdc.flush	%0, r0;" \ +					: : "r" (i)); +#endif  }  static void __flush_dcache_range_wb(unsigned long start, unsigned long end)  { +#ifndef ASM_LOOP +	int i; +#endif  	pr_debug("%s: start 0x%x, end 0x%x\n", __func__,  				(unsigned int)start, (unsigned int) end);  	CACHE_LOOP_LIMITS(start, end,  			cpuinfo.dcache_line_length, cpuinfo.dcache_size); +#ifdef ASM_LOOP  	CACHE_RANGE_LOOP_2(start, end, cpuinfo.dcache_line_length, wdc.flush); +#else +	for (i = start; i < end; i += cpuinfo.dcache_line_length) +		__asm__ __volatile__ ("wdc.flush	%0, r0;"	\ +				: : "r" (i)); +#endif  }  /* struct for wb caches and for wt caches */ @@ -493,7 +616,7 @@ const struct scache wt_nomsr_noirq = {  #define CPUVER_7_20_A	0x0c  #define CPUVER_7_20_D	0x0f -#define INFO(s)	printk(KERN_INFO "cache: " s " \n"); +#define INFO(s)	printk(KERN_INFO "cache: " s "\n");  void microblaze_cache_init(void)  { @@ -532,4 +655,13 @@ void microblaze_cache_init(void)  			}  		}  	} +/* FIXME Invalidation is done in U-BOOT + * WT cache: Data is already written to main memory + * WB cache: Discard data on noMMU which caused that kernel doesn't boot + */ +	/* invalidate_dcache(); */ +	enable_dcache(); + +	invalidate_icache(); +	enable_icache();  } diff --git a/arch/microblaze/kernel/cpu/cpuinfo.c b/arch/microblaze/kernel/cpu/cpuinfo.c index 991d71311b0..255ef880351 100644 --- a/arch/microblaze/kernel/cpu/cpuinfo.c +++ b/arch/microblaze/kernel/cpu/cpuinfo.c @@ -9,7 +9,6 @@   */  #include <linux/init.h> -#include <linux/slab.h>  #include <asm/cpuinfo.h>  #include <asm/pvr.h> diff --git a/arch/microblaze/kernel/cpu/mb.c b/arch/microblaze/kernel/cpu/mb.c index 0c912b2a8e0..4216eb1eaa3 100644 --- a/arch/microblaze/kernel/cpu/mb.c +++ b/arch/microblaze/kernel/cpu/mb.c @@ -98,15 +98,17 @@ static int show_cpuinfo(struct seq_file *m, void *v)  	if (cpuinfo.use_icache)  		count += seq_printf(m, -				"Icache:\t\t%ukB\n", -				cpuinfo.icache_size >> 10); +				"Icache:\t\t%ukB\tline length:\t%dB\n", +				cpuinfo.icache_size >> 10, +				cpuinfo.icache_line_length);  	else  		count += seq_printf(m, "Icache:\t\tno\n");  	if (cpuinfo.use_dcache) {  		count += seq_printf(m, -				"Dcache:\t\t%ukB\n", -				cpuinfo.dcache_size >> 10); +				"Dcache:\t\t%ukB\tline length:\t%dB\n", +				cpuinfo.dcache_size >> 10, +				cpuinfo.dcache_line_length);  		if (cpuinfo.dcache_wb)  			count += seq_printf(m, "\t\twrite-back\n");  		else diff --git a/arch/microblaze/kernel/dma.c b/arch/microblaze/kernel/dma.c new file mode 100644 index 00000000000..9dcd90b5df5 --- /dev/null +++ b/arch/microblaze/kernel/dma.c @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2009-2010 PetaLogix + * Copyright (C) 2006 Benjamin Herrenschmidt, IBM Corporation + * + * Provide default implementations of the DMA mapping callbacks for + * directly mapped busses. + */ + +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/gfp.h> +#include <linux/dma-debug.h> +#include <asm/bug.h> +#include <asm/cacheflush.h> + +/* + * Generic direct DMA implementation + * + * This implementation supports a per-device offset that can be applied if + * the address at which memory is visible to devices is not 0. Platform code + * can set archdata.dma_data to an unsigned long holding the offset. By + * default the offset is PCI_DRAM_OFFSET. + */ +static inline void __dma_sync_page(unsigned long paddr, unsigned long offset, +				size_t size, enum dma_data_direction direction) +{ +	switch (direction) { +	case DMA_TO_DEVICE: +		flush_dcache_range(paddr + offset, paddr + offset + size); +		break; +	case DMA_FROM_DEVICE: +		invalidate_dcache_range(paddr + offset, paddr + offset + size); +		break; +	default: +		BUG(); +	} +} + +static unsigned long get_dma_direct_offset(struct device *dev) +{ +	if (likely(dev)) +		return (unsigned long)dev->archdata.dma_data; + +	return PCI_DRAM_OFFSET; /* FIXME Not sure if is correct */ +} + +#define NOT_COHERENT_CACHE + +static void *dma_direct_alloc_coherent(struct device *dev, size_t size, +				dma_addr_t *dma_handle, gfp_t flag) +{ +#ifdef NOT_COHERENT_CACHE +	return consistent_alloc(flag, size, dma_handle); +#else +	void *ret; +	struct page *page; +	int node = dev_to_node(dev); + +	/* ignore region specifiers */ +	flag  &= ~(__GFP_HIGHMEM); + +	page = alloc_pages_node(node, flag, get_order(size)); +	if (page == NULL) +		return NULL; +	ret = page_address(page); +	memset(ret, 0, size); +	*dma_handle = virt_to_phys(ret) + get_dma_direct_offset(dev); + +	return ret; +#endif +} + +static void dma_direct_free_coherent(struct device *dev, size_t size, +			      void *vaddr, dma_addr_t dma_handle) +{ +#ifdef NOT_COHERENT_CACHE +	consistent_free(size, vaddr); +#else +	free_pages((unsigned long)vaddr, get_order(size)); +#endif +} + +static int dma_direct_map_sg(struct device *dev, struct scatterlist *sgl, +			     int nents, enum dma_data_direction direction, +			     struct dma_attrs *attrs) +{ +	struct scatterlist *sg; +	int i; + +	/* FIXME this part of code is untested */ +	for_each_sg(sgl, sg, nents, i) { +		sg->dma_address = sg_phys(sg) + get_dma_direct_offset(dev); +		sg->dma_length = sg->length; +		__dma_sync_page(page_to_phys(sg_page(sg)), sg->offset, +							sg->length, direction); +	} + +	return nents; +} + +static void dma_direct_unmap_sg(struct device *dev, struct scatterlist *sg, +				int nents, enum dma_data_direction direction, +				struct dma_attrs *attrs) +{ +} + +static int dma_direct_dma_supported(struct device *dev, u64 mask) +{ +	return 1; +} + +static inline dma_addr_t dma_direct_map_page(struct device *dev, +					     struct page *page, +					     unsigned long offset, +					     size_t size, +					     enum dma_data_direction direction, +					     struct dma_attrs *attrs) +{ +	__dma_sync_page(page_to_phys(page), offset, size, direction); +	return page_to_phys(page) + offset + get_dma_direct_offset(dev); +} + +static inline void dma_direct_unmap_page(struct device *dev, +					 dma_addr_t dma_address, +					 size_t size, +					 enum dma_data_direction direction, +					 struct dma_attrs *attrs) +{ +/* There is not necessary to do cache cleanup + * + * phys_to_virt is here because in __dma_sync_page is __virt_to_phys and + * dma_address is physical address + */ +	__dma_sync_page(dma_address, 0 , size, direction); +} + +struct dma_map_ops dma_direct_ops = { +	.alloc_coherent	= dma_direct_alloc_coherent, +	.free_coherent	= dma_direct_free_coherent, +	.map_sg		= dma_direct_map_sg, +	.unmap_sg	= dma_direct_unmap_sg, +	.dma_supported	= dma_direct_dma_supported, +	.map_page	= dma_direct_map_page, +	.unmap_page	= dma_direct_unmap_page, +}; +EXPORT_SYMBOL(dma_direct_ops); + +/* Number of entries preallocated for DMA-API debugging */ +#define PREALLOC_DMA_DEBUG_ENTRIES (1 << 16) + +static int __init dma_init(void) +{ +       dma_debug_init(PREALLOC_DMA_DEBUG_ENTRIES); + +       return 0; +} +fs_initcall(dma_init); diff --git a/arch/microblaze/kernel/entry-nommu.S b/arch/microblaze/kernel/entry-nommu.S index 391d6197fc3..8cc18cd2cce 100644 --- a/arch/microblaze/kernel/entry-nommu.S +++ b/arch/microblaze/kernel/entry-nommu.S @@ -476,6 +476,8 @@ ENTRY(ret_from_fork)  	nop  work_pending: +	enable_irq +  	andi	r11, r19, _TIF_NEED_RESCHED  	beqi	r11, 1f  	bralid	r15, schedule diff --git a/arch/microblaze/kernel/entry.S b/arch/microblaze/kernel/entry.S index 3bad4ff4947..c0ede25c5b9 100644 --- a/arch/microblaze/kernel/entry.S +++ b/arch/microblaze/kernel/entry.S @@ -305,7 +305,7 @@ C_ENTRY(_user_exception):  	swi	r11, r1, PTO+PT_R1;		/* Store user SP.  */  	addi	r11, r0, 1;  	swi	r11, r0, TOPHYS(PER_CPU(KM));	/* Now we're in kernel-mode.  */ -2:	lwi	r31, r0, TOPHYS(PER_CPU(CURRENT_SAVE));	/* get saved current */ +2:	lwi	CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE));  	/* Save away the syscall number.  */  	swi	r12, r1, PTO+PT_R0;  	tovirt(r1,r1) @@ -322,8 +322,7 @@ C_ENTRY(_user_exception):  	rtid	r11, 0  	nop  3: -	add	r11, r0, CURRENT_TASK	 /* Get current task ptr into r11 */ -	lwi	r11, r11, TS_THREAD_INFO /* get thread info */ +	lwi	r11, CURRENT_TASK, TS_THREAD_INFO /* get thread info */  	lwi	r11, r11, TI_FLAGS	 /* get flags in thread info */  	andi	r11, r11, _TIF_WORK_SYSCALL_MASK  	beqi	r11, 4f @@ -382,60 +381,50 @@ C_ENTRY(ret_from_trap):  /* See if returning to kernel mode, if so, skip resched &c.  */  	bnei	r11, 2f; +	swi	r3, r1, PTO + PT_R3 +	swi	r4, r1, PTO + PT_R4 +  	/* We're returning to user mode, so check for various conditions that  	 * trigger rescheduling. */ -	# FIXME: Restructure all these flag checks. -	add	r11, r0, CURRENT_TASK;	/* Get current task ptr into r11 */ -	lwi	r11, r11, TS_THREAD_INFO;	/* get thread info */ +	/* FIXME: Restructure all these flag checks. */ +	lwi	r11, CURRENT_TASK, TS_THREAD_INFO;	/* get thread info */  	lwi	r11, r11, TI_FLAGS;		/* get flags in thread info */  	andi	r11, r11, _TIF_WORK_SYSCALL_MASK  	beqi	r11, 1f -	swi	r3, r1, PTO + PT_R3 -	swi	r4, r1, PTO + PT_R4  	brlid	r15, do_syscall_trace_leave  	addik	r5, r1, PTO + PT_R0 -	lwi	r3, r1, PTO + PT_R3 -	lwi	r4, r1, PTO + PT_R4  1: -  	/* We're returning to user mode, so check for various conditions that  	 * trigger rescheduling. */ -	/* Get current task ptr into r11 */ -	add	r11, r0, CURRENT_TASK;	/* Get current task ptr into r11 */ -	lwi	r11, r11, TS_THREAD_INFO;	/* get thread info */ +	/* get thread info from current task */ +	lwi	r11, CURRENT_TASK, TS_THREAD_INFO;  	lwi	r11, r11, TI_FLAGS;		/* get flags in thread info */  	andi	r11, r11, _TIF_NEED_RESCHED;  	beqi	r11, 5f; -	swi	r3, r1, PTO + PT_R3; /* store syscall result */ -	swi	r4, r1, PTO + PT_R4;  	bralid	r15, schedule;	/* Call scheduler */  	nop;				/* delay slot */ -	lwi	r3, r1, PTO + PT_R3; /* restore syscall result */ -	lwi	r4, r1, PTO + PT_R4;  	/* Maybe handle a signal */ -5:	add	r11, r0, CURRENT_TASK; /* Get current task ptr into r11 */ -	lwi	r11, r11, TS_THREAD_INFO;	/* get thread info */ +5:	/* get thread info from current task*/ +	lwi	r11, CURRENT_TASK, TS_THREAD_INFO;  	lwi	r11, r11, TI_FLAGS;	/* get flags in thread info */  	andi	r11, r11, _TIF_SIGPENDING;  	beqi	r11, 1f;		/* Signals to handle, handle them */ -	swi	r3, r1, PTO + PT_R3; /* store syscall result */ -	swi	r4, r1, PTO + PT_R4;  	la	r5, r1, PTO;		/* Arg 1: struct pt_regs *regs */ -	add	r6, r0, r0;		/* Arg 2: sigset_t *oldset */  	addi	r7, r0, 1;		/* Arg 3: int in_syscall */  	bralid	r15, do_signal;	/* Handle any signals */ -	nop; +	add	r6, r0, r0;		/* Arg 2: sigset_t *oldset */ + +/* Finally, return to user state.  */ +1:  	lwi	r3, r1, PTO + PT_R3; /* restore syscall result */  	lwi	r4, r1, PTO + PT_R4; -/* Finally, return to user state.  */ -1:	swi	r0, r0, PER_CPU(KM);	/* Now officially in user state. */ -	add	r11, r0, CURRENT_TASK;	/* Get current task ptr into r11 */ -	swi	r11, r0, PER_CPU(CURRENT_SAVE); /* save current */ +	swi	r0, r0, PER_CPU(KM);	/* Now officially in user state. */ +	swi	CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE); /* save current */  	VM_OFF;  	tophys(r1,r1);  	RESTORE_REGS; @@ -565,7 +554,7 @@ C_ENTRY(sys_rt_sigreturn_wrapper):  	swi	r11, r1, PTO+PT_R1; /* Store user SP.  */		\  	addi	r11, r0, 1;						\  	swi	r11, r0, TOPHYS(PER_CPU(KM)); /* Now we're in kernel-mode.*/\ -2:	lwi	r31, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */\ +2:	lwi	CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE));	\  	/* Save away the syscall number.  */				\  	swi	r0, r1, PTO+PT_R0;					\  	tovirt(r1,r1) @@ -673,9 +662,7 @@ C_ENTRY(ret_from_exc):  	/* We're returning to user mode, so check for various conditions that  	   trigger rescheduling. */ -	/* Get current task ptr into r11 */ -	add	r11, r0, CURRENT_TASK; /* Get current task ptr into r11 */ -	lwi	r11, r11, TS_THREAD_INFO;	/* get thread info */ +	lwi	r11, CURRENT_TASK, TS_THREAD_INFO;	/* get thread info */  	lwi	r11, r11, TI_FLAGS;	/* get flags in thread info */  	andi	r11, r11, _TIF_NEED_RESCHED;  	beqi	r11, 5f; @@ -685,8 +672,7 @@ C_ENTRY(ret_from_exc):  	nop;				/* delay slot */  	/* Maybe handle a signal */ -5:	add	r11, r0, CURRENT_TASK; /* Get current task ptr into r11 */ -	lwi	r11, r11, TS_THREAD_INFO;	/* get thread info */ +5:	lwi	r11, CURRENT_TASK, TS_THREAD_INFO;	/* get thread info */  	lwi	r11, r11, TI_FLAGS;	/* get flags in thread info */  	andi	r11, r11, _TIF_SIGPENDING;  	beqi	r11, 1f;		/* Signals to handle, handle them */ @@ -705,15 +691,13 @@ C_ENTRY(ret_from_exc):  	 * store return registers separately because this macros is use  	 * for others exceptions */  	la	r5, r1, PTO;		/* Arg 1: struct pt_regs *regs */ -	add	r6, r0, r0;		/* Arg 2: sigset_t *oldset */  	addi	r7, r0, 0;		/* Arg 3: int in_syscall */  	bralid	r15, do_signal;	/* Handle any signals */ -	nop; +	add	r6, r0, r0;		/* Arg 2: sigset_t *oldset */  /* Finally, return to user state.  */  1:	swi	r0, r0, PER_CPU(KM);	/* Now officially in user state. */ -	add	r11, r0, CURRENT_TASK; /* Get current task ptr into r11 */ -	swi	r11, r0, PER_CPU(CURRENT_SAVE); /* save current */ +	swi	CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE); /* save current */  	VM_OFF;  	tophys(r1,r1); @@ -802,7 +786,7 @@ C_ENTRY(_interrupt):  	swi	r11, r0, TOPHYS(PER_CPU(KM));  2: -	lwi	r31, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); +	lwi	CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE));  	swi	r0, r1, PTO + PT_R0;  	tovirt(r1,r1)  	la	r5, r1, PTO; @@ -817,8 +801,7 @@ ret_from_irq:  	lwi	r11, r1, PTO + PT_MODE;  	bnei	r11, 2f; -	add	r11, r0, CURRENT_TASK; -	lwi	r11, r11, TS_THREAD_INFO; +	lwi	r11, CURRENT_TASK, TS_THREAD_INFO;  	lwi	r11, r11, TI_FLAGS; /* MS: get flags from thread info */  	andi	r11, r11, _TIF_NEED_RESCHED;  	beqi	r11, 5f @@ -826,8 +809,7 @@ ret_from_irq:  	nop; /* delay slot */      /* Maybe handle a signal */ -5:	add	r11, r0, CURRENT_TASK; -	lwi	r11, r11, TS_THREAD_INFO; /* MS: get thread info */ +5:	lwi	r11, CURRENT_TASK, TS_THREAD_INFO; /* MS: get thread info */  	lwi	r11, r11, TI_FLAGS; /* get flags in thread info */  	andi	r11, r11, _TIF_SIGPENDING;  	beqid	r11, no_intr_resched @@ -842,8 +824,7 @@ no_intr_resched:      /* Disable interrupts, we are now committed to the state restore */  	disable_irq  	swi	r0, r0, PER_CPU(KM); /* MS: Now officially in user state. */ -	add	r11, r0, CURRENT_TASK; -	swi	r11, r0, PER_CPU(CURRENT_SAVE); +	swi	CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE);  	VM_OFF;  	tophys(r1,r1);  	lwi	r3, r1, PTO + PT_R3; /* MS: restore saved r3, r4 registers */ @@ -853,7 +834,28 @@ no_intr_resched:  	lwi	r1, r1, PT_R1 - PT_SIZE;  	bri	6f;  /* MS: Return to kernel state. */ -2:	VM_OFF /* MS: turn off MMU */ +2: +#ifdef CONFIG_PREEMPT +	lwi	r11, CURRENT_TASK, TS_THREAD_INFO; +	/* MS: get preempt_count from thread info */ +	lwi	r5, r11, TI_PREEMPT_COUNT; +	bgti	r5, restore; + +	lwi	r5, r11, TI_FLAGS;		/* get flags in thread info */ +	andi	r5, r5, _TIF_NEED_RESCHED; +	beqi	r5, restore /* if zero jump over */ + +preempt: +	/* interrupts are off that's why I am calling preempt_chedule_irq */ +	bralid	r15, preempt_schedule_irq +	nop +	lwi	r11, CURRENT_TASK, TS_THREAD_INFO;	/* get thread info */ +	lwi	r5, r11, TI_FLAGS;		/* get flags in thread info */ +	andi	r5, r5, _TIF_NEED_RESCHED; +	bnei	r5, preempt /* if non zero jump to resched */ +restore: +#endif +	VM_OFF /* MS: turn off MMU */  	tophys(r1,r1)  	lwi	r3, r1, PTO + PT_R3; /* MS: restore saved r3, r4 registers */  	lwi	r4, r1, PTO + PT_R4; @@ -915,7 +917,7 @@ C_ENTRY(_debug_exception):  	swi	r11, r1, PTO+PT_R1; /* Store user SP.  */  	addi	r11, r0, 1;  	swi	r11, r0, TOPHYS(PER_CPU(KM));	/* Now we're in kernel-mode.  */ -2:	lwi	r31, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */ +2:	lwi	CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE));  	/* Save away the syscall number.  */  	swi	r0, r1, PTO+PT_R0;  	tovirt(r1,r1) @@ -935,8 +937,7 @@ dbtrap_call:	rtbd	r11, 0;  	bnei	r11, 2f;  	/* Get current task ptr into r11 */ -	add	r11, r0, CURRENT_TASK; /* Get current task ptr into r11 */ -	lwi	r11, r11, TS_THREAD_INFO;	/* get thread info */ +	lwi	r11, CURRENT_TASK, TS_THREAD_INFO;	/* get thread info */  	lwi	r11, r11, TI_FLAGS;	/* get flags in thread info */  	andi	r11, r11, _TIF_NEED_RESCHED;  	beqi	r11, 5f; @@ -949,8 +950,7 @@ dbtrap_call:	rtbd	r11, 0;  	/* XXX m68knommu also checks TASK_STATE & TASK_COUNTER here.  */  	/* Maybe handle a signal */ -5:	add	r11, r0, CURRENT_TASK; /* Get current task ptr into r11 */ -	lwi	r11, r11, TS_THREAD_INFO;	/* get thread info */ +5:	lwi	r11, CURRENT_TASK, TS_THREAD_INFO;	/* get thread info */  	lwi	r11, r11, TI_FLAGS;	/* get flags in thread info */  	andi	r11, r11, _TIF_SIGPENDING;  	beqi	r11, 1f;		/* Signals to handle, handle them */ @@ -966,16 +966,14 @@ dbtrap_call:	rtbd	r11, 0;  	   (in a possibly modified form) after do_signal returns.  */  	la	r5, r1, PTO;		/* Arg 1: struct pt_regs *regs */ -	add	r6, r0, r0;		/* Arg 2: sigset_t *oldset */  	addi  r7, r0, 0;	/* Arg 3: int in_syscall */  	bralid	r15, do_signal;	/* Handle any signals */ -	nop; +	add	r6, r0, r0;		/* Arg 2: sigset_t *oldset */  /* Finally, return to user state.  */  1:	swi	r0, r0, PER_CPU(KM);	/* Now officially in user state. */ -	add	r11, r0, CURRENT_TASK; /* Get current task ptr into r11 */ -	swi	r11, r0, PER_CPU(CURRENT_SAVE); /* save current */ +	swi	CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE); /* save current */  	VM_OFF;  	tophys(r1,r1); @@ -1007,7 +1005,7 @@ DBTRAP_return:		/* Make global symbol for debugging */  ENTRY(_switch_to)  	/* prepare return value */ -	addk	r3, r0, r31 +	addk	r3, r0, CURRENT_TASK  	/* save registers in cpu_context */  	/* use r11 and r12, volatile registers, as temp register */ @@ -1051,10 +1049,10 @@ ENTRY(_switch_to)  	nop  	swi	r12, r11, CC_FSR -	/* update r31, the current */ -	lwi	r31, r6, TI_TASK/* give me pointer to task which will be next */ +	/* update r31, the current-give me pointer to task which will be next */ +	lwi	CURRENT_TASK, r6, TI_TASK  	/* stored it to current_save too */ -	swi	r31, r0, PER_CPU(CURRENT_SAVE) +	swi	CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE)  	/* get new process' cpu context and restore */  	/* give me start where start context of next task */ diff --git a/arch/microblaze/kernel/exceptions.c b/arch/microblaze/kernel/exceptions.c index d9f70f83097..02cbdfe5aa8 100644 --- a/arch/microblaze/kernel/exceptions.c +++ b/arch/microblaze/kernel/exceptions.c @@ -121,7 +121,7 @@ asmlinkage void full_exception(struct pt_regs *regs, unsigned int type,  		}  		printk(KERN_WARNING "Divide by zero exception " \  							"in kernel mode.\n"); -		die("Divide by exception", regs, SIGBUS); +		die("Divide by zero exception", regs, SIGBUS);  		break;  	case MICROBLAZE_FPU_EXCEPTION:  		pr_debug(KERN_WARNING "FPU exception\n"); diff --git a/arch/microblaze/kernel/ftrace.c b/arch/microblaze/kernel/ftrace.c index 388b31ca65a..515feb40455 100644 --- a/arch/microblaze/kernel/ftrace.c +++ b/arch/microblaze/kernel/ftrace.c @@ -151,13 +151,10 @@ int ftrace_make_nop(struct module *mod,  	return ret;  } -static int ret_addr; /* initialized as 0 by default */ -  /* I believe that first is called ftrace_make_nop before this function */  int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)  {  	int ret; -	ret_addr = addr; /* saving where the barrier jump is */  	pr_debug("%s: addr:0x%x, rec->ip: 0x%x, imm:0x%x\n",  		__func__, (unsigned int)addr, (unsigned int)rec->ip, imm);  	ret = ftrace_modify_code(rec->ip, imm); @@ -194,12 +191,9 @@ int ftrace_update_ftrace_func(ftrace_func_t func)  	ret = ftrace_modify_code(ip, upper);  	ret += ftrace_modify_code(ip + 4, lower); -	/* We just need to remove the rtsd r15, 8 by NOP */ -	BUG_ON(!ret_addr); -	if (ret_addr) -		ret += ftrace_modify_code(ret_addr, MICROBLAZE_NOP); -	else -		ret = 1; /* fault */ +	/* We just need to replace the rtsd r15, 8 with NOP */ +	ret += ftrace_modify_code((unsigned long)&ftrace_caller, +				  MICROBLAZE_NOP);  	/* All changes are done - lets do caches consistent */  	flush_icache(); diff --git a/arch/microblaze/kernel/head.S b/arch/microblaze/kernel/head.S index 30916193fcc..1bf73988826 100644 --- a/arch/microblaze/kernel/head.S +++ b/arch/microblaze/kernel/head.S @@ -28,6 +28,7 @@   * for more details.   */ +#include <linux/init.h>  #include <linux/linkage.h>  #include <asm/thread_info.h>  #include <asm/page.h> @@ -49,8 +50,14 @@ swapper_pg_dir:  #endif /* CONFIG_MMU */ -	.text +	__HEAD  ENTRY(_start) +#if CONFIG_KERNEL_BASE_ADDR == 0 +	brai	TOPHYS(real_start) +	.org	0x100 +real_start: +#endif +  	mfs	r1, rmsr  	andi	r1, r1, ~2  	mts	rmsr, r1 @@ -99,8 +106,8 @@ no_fdt_arg:  	tophys(r4,r4)			/* convert to phys address */  	ori	r3, r0, COMMAND_LINE_SIZE - 1 /* number of loops */  _copy_command_line: -	lbu	r7, r5, r6 /* r7=r5+r6 - r5 contain pointer to command line */ -	sb	r7, r4, r6		/* addr[r4+r6]= r7*/ +	lbu	r2, r5, r6 /* r2=r5+r6 - r5 contain pointer to command line */ +	sb	r2, r4, r6		/* addr[r4+r6]= r2*/  	addik	r6, r6, 1		/* increment counting */  	bgtid	r3, _copy_command_line	/* loop for all entries       */  	addik	r3, r3, -1		/* descrement loop */ @@ -128,7 +135,7 @@ _copy_bram:  	 * virtual to physical.  	 */  	nop -	addik	r3, r0, 63		/* Invalidate all TLB entries */ +	addik	r3, r0, MICROBLAZE_TLB_SIZE -1	/* Invalidate all TLB entries */  _invalidate:  	mts	rtlbx, r3  	mts	rtlbhi, r0			/* flush: ensure V is clear   */ @@ -136,6 +143,11 @@ _invalidate:  	addik	r3, r3, -1  	/* sync */ +	/* Setup the kernel PID */ +	mts	rpid,r0			/* Load the kernel PID */ +	nop +	bri	4 +  	/*  	 * We should still be executing code at physical address area  	 * RAM_BASEADDR at this point. However, kernel code is at @@ -146,10 +158,6 @@ _invalidate:  	addik	r3,r0, CONFIG_KERNEL_START /* Load the kernel virtual address */  	tophys(r4,r3)			/* Load the kernel physical address */ -	mts	rpid,r0			/* Load the kernel PID */ -	nop -	bri	4 -  	/*  	 * Configure and load two entries into TLB slots 0 and 1.  	 * In case we are pinning TLBs, these are reserved in by the diff --git a/arch/microblaze/kernel/hw_exception_handler.S b/arch/microblaze/kernel/hw_exception_handler.S index 2b86c03aa84..995a2123635 100644 --- a/arch/microblaze/kernel/hw_exception_handler.S +++ b/arch/microblaze/kernel/hw_exception_handler.S @@ -313,13 +313,13 @@ _hw_exception_handler:  	mfs	r5, rmsr;  	nop  	swi	r5, r1, 0; -	mfs	r3, resr +	mfs	r4, resr  	nop -	mfs	r4, rear; +	mfs	r3, rear;  	nop  #ifndef CONFIG_MMU -	andi	r5, r3, 0x1000;		/* Check ESR[DS] */ +	andi	r5, r4, 0x1000;		/* Check ESR[DS] */  	beqi	r5, not_in_delay_slot;	/* Branch if ESR[DS] not set */  	mfs	r17, rbtr;	/* ESR[DS] set - return address in BTR */  	nop @@ -327,13 +327,14 @@ not_in_delay_slot:  	swi	r17, r1, PT_R17  #endif -	andi	r5, r3, 0x1F;		/* Extract ESR[EXC] */ +	andi	r5, r4, 0x1F;		/* Extract ESR[EXC] */  #ifdef CONFIG_MMU  	/* Calculate exception vector offset = r5 << 2 */  	addk	r6, r5, r5; /* << 1 */  	addk	r6, r6, r6; /* << 2 */ +#ifdef DEBUG  /* counting which exception happen */  	lwi	r5, r0, 0x200 + TOPHYS(r0_ram)  	addi	r5, r5, 1 @@ -341,6 +342,7 @@ not_in_delay_slot:  	lwi	r5, r6, 0x200 + TOPHYS(r0_ram)  	addi	r5, r5, 1  	swi	r5, r6, 0x200 + TOPHYS(r0_ram) +#endif  /* end */  	/* Load the HW Exception vector */  	lwi	r6, r6, TOPHYS(_MB_HW_ExceptionVectorTable) @@ -376,7 +378,7 @@ handle_other_ex: /* Handle Other exceptions here */  	swi	r18, r1, PT_R18  	or	r5, r1, r0 -	andi	r6, r3, 0x1F; /* Load ESR[EC] */ +	andi	r6, r4, 0x1F; /* Load ESR[EC] */  	lwi	r7, r0, PER_CPU(KM) /* MS: saving current kernel mode to regs */  	swi	r7, r1, PT_MODE  	mfs	r7, rfsr @@ -426,11 +428,11 @@ handle_other_ex: /* Handle Other exceptions here */   */  handle_unaligned_ex:  	/* Working registers already saved: R3, R4, R5, R6 -	 *  R3 = ESR -	 *  R4 = EAR +	 *  R4 = ESR +	 *  R3 = EAR  	 */  #ifdef CONFIG_MMU -	andi	r6, r3, 0x1000			/* Check ESR[DS] */ +	andi	r6, r4, 0x1000			/* Check ESR[DS] */  	beqi	r6, _no_delayslot		/* Branch if ESR[DS] not set */  	mfs	r17, rbtr;	/* ESR[DS] set - return address in BTR */  	nop @@ -439,7 +441,7 @@ _no_delayslot:  	RESTORE_STATE;  	bri	unaligned_data_trap  #endif -	andi	r6, r3, 0x3E0; /* Mask and extract the register operand */ +	andi	r6, r4, 0x3E0; /* Mask and extract the register operand */  	srl	r6, r6; /* r6 >> 5 */  	srl	r6, r6;  	srl	r6, r6; @@ -448,33 +450,33 @@ _no_delayslot:  	/* Store the register operand in a temporary location */  	sbi	r6, r0, TOPHYS(ex_reg_op); -	andi	r6, r3, 0x400; /* Extract ESR[S] */ +	andi	r6, r4, 0x400; /* Extract ESR[S] */  	bnei	r6, ex_sw;  ex_lw: -	andi	r6, r3, 0x800; /* Extract ESR[W] */ +	andi	r6, r4, 0x800; /* Extract ESR[W] */  	beqi	r6, ex_lhw; -	lbui	r5, r4, 0; /* Exception address in r4 */ +	lbui	r5, r3, 0; /* Exception address in r3 */  	/* Load a word, byte-by-byte from destination address  		and save it in tmp space */  	sbi	r5, r0, TOPHYS(ex_tmp_data_loc_0); -	lbui	r5, r4, 1; +	lbui	r5, r3, 1;  	sbi	r5, r0, TOPHYS(ex_tmp_data_loc_1); -	lbui	r5, r4, 2; +	lbui	r5, r3, 2;  	sbi	r5, r0, TOPHYS(ex_tmp_data_loc_2); -	lbui	r5, r4, 3; +	lbui	r5, r3, 3;  	sbi	r5, r0, TOPHYS(ex_tmp_data_loc_3); -	/* Get the destination register value into r3 */ -	lwi	r3, r0, TOPHYS(ex_tmp_data_loc_0); +	/* Get the destination register value into r4 */ +	lwi	r4, r0, TOPHYS(ex_tmp_data_loc_0);  	bri	ex_lw_tail;  ex_lhw: -	lbui	r5, r4, 0; /* Exception address in r4 */ +	lbui	r5, r3, 0; /* Exception address in r3 */  	/* Load a half-word, byte-by-byte from destination  		address and save it in tmp space */  	sbi	r5, r0, TOPHYS(ex_tmp_data_loc_0); -	lbui	r5, r4, 1; +	lbui	r5, r3, 1;  	sbi	r5, r0, TOPHYS(ex_tmp_data_loc_1); -	/* Get the destination register value into r3 */ -	lhui	r3, r0, TOPHYS(ex_tmp_data_loc_0); +	/* Get the destination register value into r4 */ +	lhui	r4, r0, TOPHYS(ex_tmp_data_loc_0);  ex_lw_tail:  	/* Get the destination register number into r5 */  	lbui	r5, r0, TOPHYS(ex_reg_op); @@ -502,25 +504,25 @@ ex_sw_tail:  	andi	r6, r6, 0x800; /* Extract ESR[W] */  	beqi	r6, ex_shw;  	/* Get the word - delay slot */ -	swi	r3, r0, TOPHYS(ex_tmp_data_loc_0); +	swi	r4, r0, TOPHYS(ex_tmp_data_loc_0);  	/* Store the word, byte-by-byte into destination address */ -	lbui	r3, r0, TOPHYS(ex_tmp_data_loc_0); -	sbi	r3, r4, 0; -	lbui	r3, r0, TOPHYS(ex_tmp_data_loc_1); -	sbi	r3, r4, 1; -	lbui	r3, r0, TOPHYS(ex_tmp_data_loc_2); -	sbi	r3, r4, 2; -	lbui	r3, r0, TOPHYS(ex_tmp_data_loc_3); -	sbi	r3, r4, 3; +	lbui	r4, r0, TOPHYS(ex_tmp_data_loc_0); +	sbi	r4, r3, 0; +	lbui	r4, r0, TOPHYS(ex_tmp_data_loc_1); +	sbi	r4, r3, 1; +	lbui	r4, r0, TOPHYS(ex_tmp_data_loc_2); +	sbi	r4, r3, 2; +	lbui	r4, r0, TOPHYS(ex_tmp_data_loc_3); +	sbi	r4, r3, 3;  	bri	ex_handler_done;  ex_shw:  	/* Store the lower half-word, byte-by-byte into destination address */ -	swi	r3, r0, TOPHYS(ex_tmp_data_loc_0); -	lbui	r3, r0, TOPHYS(ex_tmp_data_loc_2); -	sbi	r3, r4, 0; -	lbui	r3, r0, TOPHYS(ex_tmp_data_loc_3); -	sbi	r3, r4, 1; +	swi	r4, r0, TOPHYS(ex_tmp_data_loc_0); +	lbui	r4, r0, TOPHYS(ex_tmp_data_loc_2); +	sbi	r4, r3, 0; +	lbui	r4, r0, TOPHYS(ex_tmp_data_loc_3); +	sbi	r4, r3, 1;  ex_sw_end: /* Exception handling of store word, ends. */  ex_handler_done: @@ -560,21 +562,16 @@ ex_handler_done:  		 */  		mfs	r11, rpid  		nop -		bri	4 -		mfs	r3, rear		/* Get faulting address */ -		nop  		/* If we are faulting a kernel address, we have to use the  		 * kernel page tables.  		 */ -		ori	r4, r0, CONFIG_KERNEL_START -		cmpu	r4, r3, r4 -		bgti	r4, ex3 +		ori	r5, r0, CONFIG_KERNEL_START +		cmpu	r5, r3, r5 +		bgti	r5, ex3  		/* First, check if it was a zone fault (which means a user  		 * tried to access a kernel or read-protected page - always  		 * a SEGV). All other faults here must be stores, so no  		 * need to check ESR_S as well. */ -		mfs	r4, resr -		nop  		andi	r4, r4, 0x800		/* ESR_Z - zone protection */  		bnei	r4, ex2 @@ -589,8 +586,6 @@ ex_handler_done:  		 * tried to access a kernel or read-protected page - always  		 * a SEGV). All other faults here must be stores, so no  		 * need to check ESR_S as well. */ -		mfs	r4, resr -		nop  		andi	r4, r4, 0x800		/* ESR_Z */  		bnei	r4, ex2  		/* get current task address */ @@ -665,8 +660,6 @@ ex_handler_done:  		 * R3 = ESR  		 */ -		mfs	r3, rear		/* Get faulting address */ -		nop  		RESTORE_STATE;  		bri	page_fault_instr_trap @@ -677,18 +670,15 @@ ex_handler_done:  	 */  	handle_data_tlb_miss_exception:  		/* Working registers already saved: R3, R4, R5, R6 -		 * R3 = ESR +		 * R3 = EAR, R4 = ESR  		 */  		mfs	r11, rpid  		nop -		bri	4 -		mfs	r3, rear		/* Get faulting address */ -		nop  		/* If we are faulting a kernel address, we have to use the  		 * kernel page tables. */ -		ori	r4, r0, CONFIG_KERNEL_START -		cmpu	r4, r3, r4 +		ori	r6, r0, CONFIG_KERNEL_START +		cmpu	r4, r3, r6  		bgti	r4, ex5  		ori	r4, r0, swapper_pg_dir  		mts	rpid, r0		/* TLB will have 0 TID */ @@ -731,9 +721,8 @@ ex_handler_done:  		 * Many of these bits are software only. Bits we don't set  		 * here we (properly should) assume have the appropriate value.  		 */ +		brid	finish_tlb_load  		andni	r4, r4, 0x0ce2		/* Make sure 20, 21 are zero */ - -		bri	finish_tlb_load  	ex7:  		/* The bailout. Restore registers to pre-exception conditions  		 * and call the heavyweights to help us out. @@ -754,9 +743,6 @@ ex_handler_done:  		 */  		mfs	r11, rpid  		nop -		bri	4 -		mfs	r3, rear		/* Get faulting address */ -		nop  		/* If we are faulting a kernel address, we have to use the  		 * kernel page tables. @@ -792,7 +778,7 @@ ex_handler_done:  		lwi	r4, r5, 0		/* Get Linux PTE */  		andi	r6, r4, _PAGE_PRESENT -		beqi	r6, ex7 +		beqi	r6, ex10  		ori	r4, r4, _PAGE_ACCESSED  		swi	r4, r5, 0 @@ -805,9 +791,8 @@ ex_handler_done:  		 * Many of these bits are software only. Bits we don't set  		 * here we (properly should) assume have the appropriate value.  		 */ +		brid	finish_tlb_load  		andni	r4, r4, 0x0ce2		/* Make sure 20, 21 are zero */ - -		bri	finish_tlb_load  	ex10:  		/* The bailout. Restore registers to pre-exception conditions  		 * and call the heavyweights to help us out. @@ -837,9 +822,9 @@ ex_handler_done:  		andi	r5, r5, (MICROBLAZE_TLB_SIZE-1)  		ori	r6, r0, 1  		cmp	r31, r5, r6 -		blti	r31, sem +		blti	r31, ex12  		addik	r5, r6, 1 -	sem: +	ex12:  		/* MS: save back current TLB index */  		swi	r5, r0, TOPHYS(tlb_index) @@ -859,7 +844,6 @@ ex_handler_done:  		nop  		/* Done...restore registers and get out of here. */ -	ex12:  		mts	rpid, r11  		nop  		bri 4 diff --git a/arch/microblaze/kernel/irq.c b/arch/microblaze/kernel/irq.c index 0f06034d1fe..8f120aca123 100644 --- a/arch/microblaze/kernel/irq.c +++ b/arch/microblaze/kernel/irq.c @@ -9,6 +9,7 @@   */  #include <linux/init.h> +#include <linux/ftrace.h>  #include <linux/kernel.h>  #include <linux/hardirq.h>  #include <linux/interrupt.h> @@ -32,7 +33,7 @@ EXPORT_SYMBOL_GPL(irq_of_parse_and_map);  static u32 concurrent_irq; -void do_IRQ(struct pt_regs *regs) +void __irq_entry do_IRQ(struct pt_regs *regs)  {  	unsigned int irq;  	struct pt_regs *old_regs = set_irq_regs(regs); @@ -93,3 +94,18 @@ skip:  	}  	return 0;  } + +/* MS: There is no any advance mapping mechanism. We are using simple 32bit +  intc without any cascades or any connection that's why mapping is 1:1 */ +unsigned int irq_create_mapping(struct irq_host *host, irq_hw_number_t hwirq) +{ +	return hwirq; +} +EXPORT_SYMBOL_GPL(irq_create_mapping); + +unsigned int irq_create_of_mapping(struct device_node *controller, +					u32 *intspec, unsigned int intsize) +{ +	return intspec[0]; +} +EXPORT_SYMBOL_GPL(irq_create_of_mapping); diff --git a/arch/microblaze/kernel/microblaze_ksyms.c b/arch/microblaze/kernel/microblaze_ksyms.c index bc4dcb7d386..ff85f771803 100644 --- a/arch/microblaze/kernel/microblaze_ksyms.c +++ b/arch/microblaze/kernel/microblaze_ksyms.c @@ -52,3 +52,14 @@ EXPORT_SYMBOL_GPL(_ebss);  extern void _mcount(void);  EXPORT_SYMBOL(_mcount);  #endif + +/* + * Assembly functions that may be used (directly or indirectly) by modules + */ +EXPORT_SYMBOL(__copy_tofrom_user); +EXPORT_SYMBOL(__strncpy_user); + +#ifdef CONFIG_OPT_LIB_ASM +EXPORT_SYMBOL(memcpy); +EXPORT_SYMBOL(memmove); +#endif diff --git a/arch/microblaze/kernel/misc.S b/arch/microblaze/kernel/misc.S index df16c6287a8..0fb5fc6c1fc 100644 --- a/arch/microblaze/kernel/misc.S +++ b/arch/microblaze/kernel/misc.S @@ -26,9 +26,10 @@   * We avoid flushing the pinned 0, 1 and possibly 2 entries.   */  .globl _tlbia; +.type  _tlbia, @function  .align 4;  _tlbia: -	addik	r12, r0, 63 /* flush all entries (63 - 3) */ +	addik	r12, r0, MICROBLAZE_TLB_SIZE - 1 /* flush all entries (63 - 3) */  	/* isync */  _tlbia_1:  	mts	rtlbx, r12 @@ -41,11 +42,13 @@ _tlbia_1:  	/* sync */  	rtsd	r15, 8  	nop +	.size  _tlbia, . - _tlbia  /*   * Flush MMU TLB for a particular address (in r5)   */  .globl _tlbie; +.type  _tlbie, @function  .align 4;  _tlbie:  	mts	rtlbsx, r5 /* look up the address in TLB */ @@ -59,17 +62,20 @@ _tlbie_1:  	rtsd	r15, 8  	nop +	.size  _tlbie, . - _tlbie +  /*   * Allocate TLB entry for early console   */  .globl early_console_reg_tlb_alloc; +.type  early_console_reg_tlb_alloc, @function  .align 4;  early_console_reg_tlb_alloc:  	/*  	 * Load a TLB entry for the UART, so that microblaze_progress() can use  	 * the UARTs nice and early.  We use a 4k real==virtual mapping.  	 */ -	ori	r4, r0, 63 +	ori	r4, r0, MICROBLAZE_TLB_SIZE - 1  	mts	rtlbx, r4 /* TLB slot 2 */  	or	r4,r5,r0 @@ -86,35 +92,4 @@ early_console_reg_tlb_alloc:  	rtsd	r15, 8  	nop -/* - * Copy a whole page (4096 bytes). - */ -#define COPY_16_BYTES		\ -	lwi	r7, r6, 0;	\ -	lwi	r8, r6, 4;	\ -	lwi	r9, r6, 8;	\ -	lwi	r10, r6, 12;	\ -	swi	r7, r5, 0;	\ -	swi	r8, r5, 4;	\ -	swi	r9, r5, 8;	\ -	swi	r10, r5, 12 - - -/* FIXME DCACHE_LINE_BYTES (CONFIG_XILINX_MICROBLAZE0_DCACHE_LINE_LEN * 4)*/ -#define DCACHE_LINE_BYTES (4 * 4) - -.globl copy_page; -.align 4; -copy_page: -	ori	r11, r0, (PAGE_SIZE/DCACHE_LINE_BYTES) - 1 -_copy_page_loop: -	COPY_16_BYTES -#if DCACHE_LINE_BYTES >= 32 -	COPY_16_BYTES -#endif -	addik	r6, r6, DCACHE_LINE_BYTES -	addik	r5, r5, DCACHE_LINE_BYTES -	bneid	r11, _copy_page_loop -	addik	r11, r11, -1 -	rtsd	r15, 8 -	nop +	.size  early_console_reg_tlb_alloc, . - early_console_reg_tlb_alloc diff --git a/arch/microblaze/kernel/module.c b/arch/microblaze/kernel/module.c index 5a45b1adfef..0e73f660654 100644 --- a/arch/microblaze/kernel/module.c +++ b/arch/microblaze/kernel/module.c @@ -12,11 +12,11 @@  #include <linux/kernel.h>  #include <linux/elf.h>  #include <linux/vmalloc.h> -#include <linux/slab.h>  #include <linux/fs.h>  #include <linux/string.h>  #include <asm/pgtable.h> +#include <asm/cacheflush.h>  void *module_alloc(unsigned long size)  { @@ -152,6 +152,7 @@ int apply_relocate_add(Elf32_Shdr *sechdrs, const char *strtab,  int module_finalize(const Elf32_Ehdr *hdr, const Elf_Shdr *sechdrs,  		struct module *module)  { +	flush_dcache();  	return 0;  } diff --git a/arch/microblaze/kernel/of_platform.c b/arch/microblaze/kernel/of_platform.c index 1c6d684996d..0dc755286d3 100644 --- a/arch/microblaze/kernel/of_platform.c +++ b/arch/microblaze/kernel/of_platform.c @@ -17,7 +17,6 @@  #include <linux/init.h>  #include <linux/module.h>  #include <linux/mod_devicetable.h> -#include <linux/slab.h>  #include <linux/pci.h>  #include <linux/of.h>  #include <linux/of_device.h> diff --git a/arch/microblaze/kernel/process.c b/arch/microblaze/kernel/process.c index 812f1bf06c9..09bed44dfcd 100644 --- a/arch/microblaze/kernel/process.c +++ b/arch/microblaze/kernel/process.c @@ -15,6 +15,7 @@  #include <linux/bitops.h>  #include <asm/system.h>  #include <asm/pgalloc.h> +#include <asm/uaccess.h> /* for USER_DS macros */  #include <asm/cacheflush.h>  void show_regs(struct pt_regs *regs) @@ -74,7 +75,10 @@ __setup("hlt", hlt_setup);  void default_idle(void)  { -	if (!hlt_counter) { +	if (likely(hlt_counter)) { +		while (!need_resched()) +			cpu_relax(); +	} else {  		clear_thread_flag(TIF_POLLING_NRFLAG);  		smp_mb__after_clear_bit();  		local_irq_disable(); @@ -82,9 +86,7 @@ void default_idle(void)  			cpu_sleep();  		local_irq_enable();  		set_thread_flag(TIF_POLLING_NRFLAG); -	} else -		while (!need_resched()) -			cpu_relax(); +	}  }  void cpu_idle(void) diff --git a/arch/microblaze/kernel/ptrace.c b/arch/microblaze/kernel/ptrace.c index 4b3ac32754d..a4a7770c614 100644 --- a/arch/microblaze/kernel/ptrace.c +++ b/arch/microblaze/kernel/ptrace.c @@ -75,29 +75,8 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)  {  	int rval;  	unsigned long val = 0; -	unsigned long copied;  	switch (request) { -	case PTRACE_PEEKTEXT: /* read word at location addr. */ -	case PTRACE_PEEKDATA: -		pr_debug("PEEKTEXT/PEEKDATA at %08lX\n", addr); -		copied = access_process_vm(child, addr, &val, sizeof(val), 0); -		rval = -EIO; -		if (copied != sizeof(val)) -			break; -		rval = put_user(val, (unsigned long *)data); -		break; - -	case PTRACE_POKETEXT: /* write the word at location addr. */ -	case PTRACE_POKEDATA: -		pr_debug("POKETEXT/POKEDATA to %08lX\n", addr); -		rval = 0; -		if (access_process_vm(child, addr, &data, sizeof(data), 1) -		    == sizeof(data)) -			break; -		rval = -EIO; -		break; -  	/* Read/write the word at location ADDR in the registers. */  	case PTRACE_PEEKUSR:  	case PTRACE_POKEUSR: @@ -130,50 +109,8 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)  		if (rval == 0 && request == PTRACE_PEEKUSR)  			rval = put_user(val, (unsigned long *)data);  		break; -	/* Continue and stop at next (return from) syscall */ -	case PTRACE_SYSCALL: -		pr_debug("PTRACE_SYSCALL\n"); -	case PTRACE_SINGLESTEP: -		pr_debug("PTRACE_SINGLESTEP\n"); -	/* Restart after a signal.  */ -	case PTRACE_CONT: -		pr_debug("PTRACE_CONT\n"); -		rval = -EIO; -		if (!valid_signal(data)) -			break; - -		if (request == PTRACE_SYSCALL) -			set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); -		else -			clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - -		child->exit_code = data; -		pr_debug("wakeup_process\n"); -		wake_up_process(child); -		rval = 0; -		break; - -	/* -	 * make the child exit.  Best I can do is send it a sigkill. -	 * perhaps it should be put in the status that it wants to -	 * exit. -	 */ -	case PTRACE_KILL: -		pr_debug("PTRACE_KILL\n"); -		rval = 0; -		if (child->exit_state == EXIT_ZOMBIE)	/* already dead */ -			break; -		child->exit_code = SIGKILL; -		wake_up_process(child); -		break; - -	case PTRACE_DETACH: /* detach a process that was attached. */ -		pr_debug("PTRACE_DETACH\n"); -		rval = ptrace_detach(child, data); -		break;  	default: -		/* rval = ptrace_request(child, request, addr, data); noMMU */ -		rval = -EIO; +		rval = ptrace_request(child, request, addr, data);  	}  	return rval;  } diff --git a/arch/microblaze/kernel/setup.c b/arch/microblaze/kernel/setup.c index bb8c4b9ccb8..17c98dbcec8 100644 --- a/arch/microblaze/kernel/setup.c +++ b/arch/microblaze/kernel/setup.c @@ -22,7 +22,10 @@  #include <linux/io.h>  #include <linux/bug.h>  #include <linux/param.h> +#include <linux/pci.h>  #include <linux/cache.h> +#include <linux/of_platform.h> +#include <linux/dma-mapping.h>  #include <asm/cacheflush.h>  #include <asm/entry.h>  #include <asm/cpuinfo.h> @@ -54,14 +57,10 @@ void __init setup_arch(char **cmdline_p)  	microblaze_cache_init(); -	invalidate_dcache(); -	enable_dcache(); - -	invalidate_icache(); -	enable_icache(); -  	setup_memory(); +	xilinx_pci_init(); +  #if defined(CONFIG_SELFMOD_INTC) || defined(CONFIG_SELFMOD_TIMER)  	printk(KERN_NOTICE "Self modified code enable\n");  #endif @@ -93,6 +92,12 @@ inline unsigned get_romfs_len(unsigned *addr)  }  #endif	/* CONFIG_MTD_UCLINUX_EBSS */ +#if defined(CONFIG_EARLY_PRINTK) && defined(CONFIG_SERIAL_UARTLITE_CONSOLE) +#define eprintk early_printk +#else +#define eprintk printk +#endif +  void __init machine_early_init(const char *cmdline, unsigned int ram,  		unsigned int fdt, unsigned int msr)  { @@ -140,32 +145,32 @@ void __init machine_early_init(const char *cmdline, unsigned int ram,  	setup_early_printk(NULL);  #endif -	early_printk("Ramdisk addr 0x%08x, ", ram); +	eprintk("Ramdisk addr 0x%08x, ", ram);  	if (fdt) -		early_printk("FDT at 0x%08x\n", fdt); +		eprintk("FDT at 0x%08x\n", fdt);  	else -		early_printk("Compiled-in FDT at 0x%08x\n", +		eprintk("Compiled-in FDT at 0x%08x\n",  					(unsigned int)_fdt_start);  #ifdef CONFIG_MTD_UCLINUX -	early_printk("Found romfs @ 0x%08x (0x%08x)\n", +	eprintk("Found romfs @ 0x%08x (0x%08x)\n",  			romfs_base, romfs_size); -	early_printk("#### klimit %p ####\n", old_klimit); +	eprintk("#### klimit %p ####\n", old_klimit);  	BUG_ON(romfs_size < 0); /* What else can we do? */ -	early_printk("Moved 0x%08x bytes from 0x%08x to 0x%08x\n", +	eprintk("Moved 0x%08x bytes from 0x%08x to 0x%08x\n",  			romfs_size, romfs_base, (unsigned)&_ebss); -	early_printk("New klimit: 0x%08x\n", (unsigned)klimit); +	eprintk("New klimit: 0x%08x\n", (unsigned)klimit);  #endif  #if CONFIG_XILINX_MICROBLAZE0_USE_MSR_INSTR  	if (msr) -		early_printk("!!!Your kernel has setup MSR instruction but " +		eprintk("!!!Your kernel has setup MSR instruction but "  				"CPU don't have it %d\n", msr);  #else  	if (!msr) -		early_printk("!!!Your kernel not setup MSR instruction but " +		eprintk("!!!Your kernel not setup MSR instruction but "  				"CPU have it %d\n", msr);  #endif @@ -188,3 +193,37 @@ static int microblaze_debugfs_init(void)  }  arch_initcall(microblaze_debugfs_init);  #endif + +static int dflt_bus_notify(struct notifier_block *nb, +				unsigned long action, void *data) +{ +	struct device *dev = data; + +	/* We are only intereted in device addition */ +	if (action != BUS_NOTIFY_ADD_DEVICE) +		return 0; + +	set_dma_ops(dev, &dma_direct_ops); + +	return NOTIFY_DONE; +} + +static struct notifier_block dflt_plat_bus_notifier = { +	.notifier_call = dflt_bus_notify, +	.priority = INT_MAX, +}; + +static struct notifier_block dflt_of_bus_notifier = { +	.notifier_call = dflt_bus_notify, +	.priority = INT_MAX, +}; + +static int __init setup_bus_notifier(void) +{ +	bus_register_notifier(&platform_bus_type, &dflt_plat_bus_notifier); +	bus_register_notifier(&of_platform_bus_type, &dflt_of_bus_notifier); + +	return 0; +} + +arch_initcall(setup_bus_notifier); diff --git a/arch/microblaze/kernel/sys_microblaze.c b/arch/microblaze/kernel/sys_microblaze.c index 9f3c205fb75..f4e00b7f125 100644 --- a/arch/microblaze/kernel/sys_microblaze.c +++ b/arch/microblaze/kernel/sys_microblaze.c @@ -30,6 +30,7 @@  #include <linux/semaphore.h>  #include <linux/uaccess.h>  #include <linux/unistd.h> +#include <linux/slab.h>  #include <asm/syscalls.h> diff --git a/arch/microblaze/kernel/traps.c b/arch/microblaze/kernel/traps.c index eaaaf805f31..75e49202a5e 100644 --- a/arch/microblaze/kernel/traps.c +++ b/arch/microblaze/kernel/traps.c @@ -22,13 +22,11 @@ void trap_init(void)  	__enable_hw_exceptions();  } -static int kstack_depth_to_print = 24; +static unsigned long kstack_depth_to_print = 24;  static int __init kstack_setup(char *s)  { -	kstack_depth_to_print = strict_strtoul(s, 0, NULL); - -	return 1; +	return !strict_strtoul(s, 0, &kstack_depth_to_print);  }  __setup("kstack=", kstack_setup); @@ -97,37 +95,3 @@ void dump_stack(void)  	show_stack(NULL, NULL);  }  EXPORT_SYMBOL(dump_stack); - -#ifdef CONFIG_MMU -void __bug(const char *file, int line, void *data) -{ -	if (data) -		printk(KERN_CRIT "kernel BUG at %s:%d (data = %p)!\n", -			file, line, data); -	else -		printk(KERN_CRIT "kernel BUG at %s:%d!\n", file, line); - -	machine_halt(); -} - -int bad_trap(int trap_num, struct pt_regs *regs) -{ -	printk(KERN_CRIT -		"unimplemented trap %d called at 0x%08lx, pid %d!\n", -		trap_num, regs->pc, current->pid); -	return -ENOSYS; -} - -int debug_trap(struct pt_regs *regs) -{ -	int i; -	printk(KERN_CRIT "debug trap\n"); -	for (i = 0; i < 32; i++) { -		/* printk("r%i:%08X\t",i,regs->gpr[i]); */ -		if ((i % 4) == 3) -			printk(KERN_CRIT "\n"); -	} -	printk(KERN_CRIT "pc:%08lX\tmsr:%08lX\n", regs->pc, regs->msr); -	return -ENOSYS; -} -#endif diff --git a/arch/microblaze/kernel/vmlinux.lds.S b/arch/microblaze/kernel/vmlinux.lds.S index 5ef619aad63..db72d712460 100644 --- a/arch/microblaze/kernel/vmlinux.lds.S +++ b/arch/microblaze/kernel/vmlinux.lds.S @@ -24,7 +24,8 @@ SECTIONS {  	.text : AT(ADDR(.text) - LOAD_OFFSET) {  		_text = . ;  		_stext = . ; -		*(.text .text.*) +		HEAD_TEXT +		TEXT_TEXT  		*(.fixup)  		EXIT_TEXT  		EXIT_CALL diff --git a/arch/microblaze/lib/Makefile b/arch/microblaze/lib/Makefile index b579db068c0..4dfe47d3cd9 100644 --- a/arch/microblaze/lib/Makefile +++ b/arch/microblaze/lib/Makefile @@ -10,5 +10,4 @@ else  lib-y += memcpy.o memmove.o  endif -lib-$(CONFIG_NO_MMU) += uaccess.o -lib-$(CONFIG_MMU) += uaccess_old.o +lib-y += uaccess_old.o diff --git a/arch/microblaze/lib/fastcopy.S b/arch/microblaze/lib/fastcopy.S index 02e3ab4eddf..fdc48bb065d 100644 --- a/arch/microblaze/lib/fastcopy.S +++ b/arch/microblaze/lib/fastcopy.S @@ -30,8 +30,9 @@   */  #include <linux/linkage.h> - +	.text  	.globl	memcpy +	.type  memcpy, @function  	.ent	memcpy  memcpy: @@ -345,9 +346,11 @@ a_done:  	rtsd	r15, 8  	nop +.size  memcpy, . - memcpy  .end memcpy  /*----------------------------------------------------------------------------*/  	.globl	memmove +	.type  memmove, @function  	.ent	memmove  memmove: @@ -659,4 +662,5 @@ d_done:  	rtsd	r15, 8  	nop +.size  memmove, . - memmove  .end memmove diff --git a/arch/microblaze/lib/memcpy.c b/arch/microblaze/lib/memcpy.c index cc2108b6b26..014bac92bdf 100644 --- a/arch/microblaze/lib/memcpy.c +++ b/arch/microblaze/lib/memcpy.c @@ -53,7 +53,7 @@ void *memcpy(void *v_dst, const void *v_src, __kernel_size_t c)  	const uint32_t *i_src;  	uint32_t *i_dst; -	if (c >= 4) { +	if (likely(c >= 4)) {  		unsigned  value, buf_hold;  		/* Align the dstination to a word boundry. */ diff --git a/arch/microblaze/lib/memset.c b/arch/microblaze/lib/memset.c index 4df851d41a2..ecfb663e1fc 100644 --- a/arch/microblaze/lib/memset.c +++ b/arch/microblaze/lib/memset.c @@ -33,22 +33,23 @@  #ifdef __HAVE_ARCH_MEMSET  void *memset(void *v_src, int c, __kernel_size_t n)  { -  	char *src = v_src;  #ifdef CONFIG_OPT_LIB_FUNCTION  	uint32_t *i_src; -	uint32_t w32; +	uint32_t w32 = 0;  #endif  	/* Truncate c to 8 bits */  	c = (c & 0xFF);  #ifdef CONFIG_OPT_LIB_FUNCTION -	/* Make a repeating word out of it */ -	w32 = c; -	w32 |= w32 << 8; -	w32 |= w32 << 16; +	if (unlikely(c)) { +		/* Make a repeating word out of it */ +		w32 = c; +		w32 |= w32 << 8; +		w32 |= w32 << 16; +	} -	if (n >= 4) { +	if (likely(n >= 4)) {  		/* Align the destination to a word boundary */  		/* This is done in an endian independant manner */  		switch ((unsigned) src & 3) { diff --git a/arch/microblaze/lib/uaccess.c b/arch/microblaze/lib/uaccess.c deleted file mode 100644 index a853fe089c4..00000000000 --- a/arch/microblaze/lib/uaccess.c +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2006 Atmark Techno, Inc. - * - * 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. - */ - -#include <linux/string.h> -#include <asm/uaccess.h> - -#include <asm/bug.h> - -long strnlen_user(const char __user *src, long count) -{ -	return strlen(src) + 1; -} - -#define __do_strncpy_from_user(dst, src, count, res)			\ -	do {								\ -		char *tmp;						\ -		strncpy(dst, src, count);				\ -		for (tmp = dst; *tmp && count > 0; tmp++, count--)	\ -			;						\ -		res = (tmp - dst);					\ -	} while (0) - -long __strncpy_from_user(char *dst, const char __user *src, long count) -{ -	long res; -	__do_strncpy_from_user(dst, src, count, res); -	return res; -} - -long strncpy_from_user(char *dst, const char __user *src, long count) -{ -	long res = -EFAULT; -	if (access_ok(VERIFY_READ, src, 1)) -		__do_strncpy_from_user(dst, src, count, res); -	return res; -} - -unsigned long __copy_tofrom_user(void __user *to, -		const void __user *from, unsigned long size) -{ -	memcpy(to, from, size); -	return 0; -} diff --git a/arch/microblaze/lib/uaccess_old.S b/arch/microblaze/lib/uaccess_old.S index 67f991c14b8..5810cec54a7 100644 --- a/arch/microblaze/lib/uaccess_old.S +++ b/arch/microblaze/lib/uaccess_old.S @@ -22,6 +22,7 @@  	.text  .globl __strncpy_user; +.type  __strncpy_user, @function  .align 4;  __strncpy_user: @@ -50,7 +51,7 @@ __strncpy_user:  3:  	rtsd	r15,8  	nop - +	.size   __strncpy_user, . - __strncpy_user  	.section	.fixup, "ax"  	.align	2 @@ -72,6 +73,7 @@ __strncpy_user:  	.text  .globl __strnlen_user; +.type  __strnlen_user, @function  .align 4;  __strnlen_user:  	addik	r3,r6,0 @@ -90,7 +92,7 @@ __strnlen_user:  3:  	rtsd	r15,8  	nop - +	.size   __strnlen_user, . - __strnlen_user  	.section	.fixup,"ax"  4: @@ -108,6 +110,7 @@ __strnlen_user:   */  	.text  .globl __copy_tofrom_user; +.type  __copy_tofrom_user, @function  .align 4;  __copy_tofrom_user:  	/* @@ -116,20 +119,34 @@ __copy_tofrom_user:  	 * r7, r3 - count  	 * r4 - tempval  	 */ -	addik	r3,r7,0 -	beqi	r3,3f -1: -	lbu	r4,r6,r0 -	addik	r6,r6,1 -2: -	sb	r4,r5,r0 -	addik	r3,r3,-1 -	bneid	r3,1b -	addik	r5,r5,1		/* delay slot */ +	beqid	r7, 3f /* zero size is not likely */ +	andi	r3, r7, 0x3 /* filter add count */ +	bneid	r3, 4f /* if is odd value then byte copying */ +	or	r3, r5, r6 /* find if is any to/from unaligned */ +	andi	r3, r3, 0x3 /* mask unaligned */ +	bneid	r3, 1f /* it is unaligned -> then jump */ +	or	r3, r0, r0 + +/* at least one 4 byte copy */ +5:	lw	r4, r6, r3 +6:	sw	r4, r5, r3 +	addik	r7, r7, -4 +	bneid	r7, 5b +	addik	r3, r3, 4 +	addik	r3, r7, 0 +	rtsd	r15, 8 +	nop +4:	or	r3, r0, r0 +1:	lbu	r4,r6,r3 +2:	sb	r4,r5,r3 +	addik	r7,r7,-1 +	bneid	r7,1b +	addik	r3,r3,1		/* delay slot */  3: +	addik	r3,r7,0  	rtsd	r15,8  	nop - +	.size   __copy_tofrom_user, . - __copy_tofrom_user  	.section	__ex_table,"a" -	.word	1b,3b,2b,3b +	.word	1b,3b,2b,3b,5b,3b,6b,3b diff --git a/arch/microblaze/mm/Makefile b/arch/microblaze/mm/Makefile index 6c8a924d9e2..09c49ed8723 100644 --- a/arch/microblaze/mm/Makefile +++ b/arch/microblaze/mm/Makefile @@ -2,6 +2,6 @@  # Makefile  # -obj-y := init.o +obj-y := consistent.o init.o  obj-$(CONFIG_MMU) += pgtable.o mmu_context.o fault.o diff --git a/arch/microblaze/mm/consistent.c b/arch/microblaze/mm/consistent.c new file mode 100644 index 00000000000..5a59dad62bd --- /dev/null +++ b/arch/microblaze/mm/consistent.c @@ -0,0 +1,255 @@ +/* + * Microblaze support for cache consistent memory. + * Copyright (C) 2010 Michal Simek <monstr@monstr.eu> + * Copyright (C) 2010 PetaLogix + * Copyright (C) 2005 John Williams <jwilliams@itee.uq.edu.au> + * + * Based on PowerPC version derived from arch/arm/mm/consistent.c + * Copyright (C) 2001 Dan Malek (dmalek@jlc.net) + * Copyright (C) 2000 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/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/bootmem.h> +#include <linux/highmem.h> +#include <linux/pci.h> +#include <linux/interrupt.h> +#include <linux/gfp.h> + +#include <asm/pgalloc.h> +#include <linux/io.h> +#include <linux/hardirq.h> +#include <asm/mmu_context.h> +#include <asm/mmu.h> +#include <linux/uaccess.h> +#include <asm/pgtable.h> +#include <asm/cpuinfo.h> +#include <asm/tlbflush.h> + +#ifndef CONFIG_MMU +/* I have to use dcache values because I can't relate on ram size */ +# define UNCACHED_SHADOW_MASK (cpuinfo.dcache_high - cpuinfo.dcache_base + 1) +#endif + +/* + * Consistent memory allocators. Used for DMA devices that want to + * share uncached memory with the processor core. + * My crufty no-MMU approach is simple. In the HW platform we can optionally + * mirror the DDR up above the processor cacheable region.  So, memory accessed + * in this mirror region will not be cached.  It's alloced from the same + * pool as normal memory, but the handle we return is shifted up into the + * uncached region.  This will no doubt cause big problems if memory allocated + * here is not also freed properly. -- JW + */ +void *consistent_alloc(int gfp, size_t size, dma_addr_t *dma_handle) +{ +	unsigned long order, vaddr; +	void *ret; +	unsigned int i, err = 0; +	struct page *page, *end; + +#ifdef CONFIG_MMU +	phys_addr_t pa; +	struct vm_struct *area; +	unsigned long va; +#endif + +	if (in_interrupt()) +		BUG(); + +	/* Only allocate page size areas. */ +	size = PAGE_ALIGN(size); +	order = get_order(size); + +	vaddr = __get_free_pages(gfp, order); +	if (!vaddr) +		return NULL; + +	/* +	 * we need to ensure that there are no cachelines in use, +	 * or worse dirty in this area. +	 */ +	flush_dcache_range(virt_to_phys((void *)vaddr), +					virt_to_phys((void *)vaddr) + size); + +#ifndef CONFIG_MMU +	ret = (void *)vaddr; +	/* +	 * Here's the magic!  Note if the uncached shadow is not implemented, +	 * it's up to the calling code to also test that condition and make +	 * other arranegments, such as manually flushing the cache and so on. +	 */ +# ifdef CONFIG_XILINX_UNCACHED_SHADOW +	ret = (void *)((unsigned) ret | UNCACHED_SHADOW_MASK); +# endif +	if ((unsigned int)ret > cpuinfo.dcache_base && +				(unsigned int)ret < cpuinfo.dcache_high) +		printk(KERN_WARNING +			"ERROR: Your cache coherent area is CACHED!!!\n"); + +	/* dma_handle is same as physical (shadowed) address */ +	*dma_handle = (dma_addr_t)ret; +#else +	/* Allocate some common virtual space to map the new pages. */ +	area = get_vm_area(size, VM_ALLOC); +	if (!area) { +		free_pages(vaddr, order); +		return NULL; +	} +	va = (unsigned long) area->addr; +	ret = (void *)va; + +	/* This gives us the real physical address of the first page. */ +	*dma_handle = pa = virt_to_bus((void *)vaddr); +#endif + +	/* +	 * free wasted pages.  We skip the first page since we know +	 * that it will have count = 1 and won't require freeing. +	 * We also mark the pages in use as reserved so that +	 * remap_page_range works. +	 */ +	page = virt_to_page(vaddr); +	end = page + (1 << order); + +	split_page(page, order); + +	for (i = 0; i < size && err == 0; i += PAGE_SIZE) { +#ifdef CONFIG_MMU +		/* MS: This is the whole magic - use cache inhibit pages */ +		err = map_page(va + i, pa + i, _PAGE_KERNEL | _PAGE_NO_CACHE); +#endif + +		SetPageReserved(page); +		page++; +	} + +	/* Free the otherwise unused pages. */ +	while (page < end) { +		__free_page(page); +		page++; +	} + +	if (err) { +		free_pages(vaddr, order); +		return NULL; +	} + +	return ret; +} +EXPORT_SYMBOL(consistent_alloc); + +/* + * free page(s) as defined by the above mapping. + */ +void consistent_free(size_t size, void *vaddr) +{ +	struct page *page; + +	if (in_interrupt()) +		BUG(); + +	size = PAGE_ALIGN(size); + +#ifndef CONFIG_MMU +	/* Clear SHADOW_MASK bit in address, and free as per usual */ +# ifdef CONFIG_XILINX_UNCACHED_SHADOW +	vaddr = (void *)((unsigned)vaddr & ~UNCACHED_SHADOW_MASK); +# endif +	page = virt_to_page(vaddr); + +	do { +		ClearPageReserved(page); +		__free_page(page); +		page++; +	} while (size -= PAGE_SIZE); +#else +	do { +		pte_t *ptep; +		unsigned long pfn; + +		ptep = pte_offset_kernel(pmd_offset(pgd_offset_k( +						(unsigned int)vaddr), +					(unsigned int)vaddr), +				(unsigned int)vaddr); +		if (!pte_none(*ptep) && pte_present(*ptep)) { +			pfn = pte_pfn(*ptep); +			pte_clear(&init_mm, (unsigned int)vaddr, ptep); +			if (pfn_valid(pfn)) { +				page = pfn_to_page(pfn); + +				ClearPageReserved(page); +				__free_page(page); +			} +		} +		vaddr += PAGE_SIZE; +	} while (size -= PAGE_SIZE); + +	/* flush tlb */ +	flush_tlb_all(); +#endif +} +EXPORT_SYMBOL(consistent_free); + +/* + * make an area consistent. + */ +void consistent_sync(void *vaddr, size_t size, int direction) +{ +	unsigned long start; +	unsigned long end; + +	start = (unsigned long)vaddr; + +	/* Convert start address back down to unshadowed memory region */ +#ifdef CONFIG_XILINX_UNCACHED_SHADOW +	start &= ~UNCACHED_SHADOW_MASK; +#endif +	end = start + size; + +	switch (direction) { +	case PCI_DMA_NONE: +		BUG(); +	case PCI_DMA_FROMDEVICE:	/* invalidate only */ +		invalidate_dcache_range(start, end); +		break; +	case PCI_DMA_TODEVICE:		/* writeback only */ +		flush_dcache_range(start, end); +		break; +	case PCI_DMA_BIDIRECTIONAL:	/* writeback and invalidate */ +		flush_dcache_range(start, end); +		break; +	} +} +EXPORT_SYMBOL(consistent_sync); + +/* + * consistent_sync_page makes memory consistent. identical + * to consistent_sync, but takes a struct page instead of a + * virtual address + */ +void consistent_sync_page(struct page *page, unsigned long offset, +	size_t size, int direction) +{ +	unsigned long start = (unsigned long)page_address(page) + offset; +	consistent_sync((void *)start, size, direction); +} +EXPORT_SYMBOL(consistent_sync_page); diff --git a/arch/microblaze/mm/fault.c b/arch/microblaze/mm/fault.c index d9d249a66ff..bab92299318 100644 --- a/arch/microblaze/mm/fault.c +++ b/arch/microblaze/mm/fault.c @@ -106,7 +106,7 @@ void do_page_fault(struct pt_regs *regs, unsigned long address,  	regs->esr = error_code;  	/* On a kernel SLB miss we can only check for a valid exception entry */ -	if (kernel_mode(regs) && (address >= TASK_SIZE)) { +	if (unlikely(kernel_mode(regs) && (address >= TASK_SIZE))) {  		printk(KERN_WARNING "kernel task_size exceed");  		_exception(SIGSEGV, regs, code, address);  	} @@ -122,7 +122,7 @@ void do_page_fault(struct pt_regs *regs, unsigned long address,  	}  #endif /* CONFIG_KGDB */ -	if (in_atomic() || !mm) { +	if (unlikely(in_atomic() || !mm)) {  		if (kernel_mode(regs))  			goto bad_area_nosemaphore; @@ -150,7 +150,7 @@ void do_page_fault(struct pt_regs *regs, unsigned long address,  	 * source.  If this is invalid we can skip the address space check,  	 * thus avoiding the deadlock.  	 */ -	if (!down_read_trylock(&mm->mmap_sem)) { +	if (unlikely(!down_read_trylock(&mm->mmap_sem))) {  		if (kernel_mode(regs) && !search_exception_tables(regs->pc))  			goto bad_area_nosemaphore; @@ -158,16 +158,16 @@ void do_page_fault(struct pt_regs *regs, unsigned long address,  	}  	vma = find_vma(mm, address); -	if (!vma) +	if (unlikely(!vma))  		goto bad_area;  	if (vma->vm_start <= address)  		goto good_area; -	if (!(vma->vm_flags & VM_GROWSDOWN)) +	if (unlikely(!(vma->vm_flags & VM_GROWSDOWN)))  		goto bad_area; -	if (!is_write) +	if (unlikely(!is_write))  		goto bad_area;  	/* @@ -179,7 +179,7 @@ void do_page_fault(struct pt_regs *regs, unsigned long address,  	 * before setting the user r1.  Thus we allow the stack to  	 * expand to 1MB without further checks.  	 */ -	if (address + 0x100000 < vma->vm_end) { +	if (unlikely(address + 0x100000 < vma->vm_end)) {  		/* get user regs even if this fault is in kernel mode */  		struct pt_regs *uregs = current->thread.regs; @@ -209,15 +209,15 @@ good_area:  	code = SEGV_ACCERR;  	/* a write */ -	if (is_write) { -		if (!(vma->vm_flags & VM_WRITE)) +	if (unlikely(is_write)) { +		if (unlikely(!(vma->vm_flags & VM_WRITE)))  			goto bad_area;  	/* a read */  	} else {  		/* protection fault */ -		if (error_code & 0x08000000) +		if (unlikely(error_code & 0x08000000))  			goto bad_area; -		if (!(vma->vm_flags & (VM_READ | VM_EXEC))) +		if (unlikely(!(vma->vm_flags & (VM_READ | VM_EXEC))))  			goto bad_area;  	} @@ -235,7 +235,7 @@ survive:  			goto do_sigbus;  		BUG();  	} -	if (fault & VM_FAULT_MAJOR) +	if (unlikely(fault & VM_FAULT_MAJOR))  		current->maj_flt++;  	else  		current->min_flt++; @@ -273,16 +273,11 @@ bad_area_nosemaphore:   * us unable to handle the page fault gracefully.   */  out_of_memory: -	if (current->pid == 1) { -		yield(); -		down_read(&mm->mmap_sem); -		goto survive; -	}  	up_read(&mm->mmap_sem); -	printk(KERN_WARNING "VM: killing process %s\n", current->comm); -	if (user_mode(regs)) -		do_exit(SIGKILL); -	bad_page_fault(regs, address, SIGKILL); +	if (!user_mode(regs)) +		bad_page_fault(regs, address, SIGKILL); +	else +		pagefault_out_of_memory();  	return;  do_sigbus: diff --git a/arch/microblaze/mm/init.c b/arch/microblaze/mm/init.c index a57cedf3671..cca3579d426 100644 --- a/arch/microblaze/mm/init.c +++ b/arch/microblaze/mm/init.c @@ -15,6 +15,7 @@  #include <linux/initrd.h>  #include <linux/pagemap.h>  #include <linux/pfn.h> +#include <linux/slab.h>  #include <linux/swap.h>  #include <asm/page.h> @@ -23,6 +24,9 @@  #include <asm/sections.h>  #include <asm/tlb.h> +/* Use for MMU and noMMU because of PCI generic code */ +int mem_init_done; +  #ifndef CONFIG_MMU  unsigned int __page_offset;  EXPORT_SYMBOL(__page_offset); @@ -30,7 +34,6 @@ EXPORT_SYMBOL(__page_offset);  #else  DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); -int mem_init_done;  static int init_bootmem_done;  #endif /* CONFIG_MMU */ @@ -44,6 +47,7 @@ unsigned long memory_start;  EXPORT_SYMBOL(memory_start);  unsigned long memory_end; /* due to mm/nommu.c */  unsigned long memory_size; +EXPORT_SYMBOL(memory_size);  /*   * paging_init() sets up the page tables - in fact we've already done this. @@ -163,7 +167,6 @@ void free_init_pages(char *what, unsigned long begin, unsigned long end)  	for (addr = begin; addr < end; addr += PAGE_SIZE) {  		ClearPageReserved(virt_to_page(addr));  		init_page_count(virt_to_page(addr)); -		memset((void *)addr, 0xcc, PAGE_SIZE);  		free_page(addr);  		totalram_pages++;  	} @@ -193,12 +196,6 @@ void free_initmem(void)  			(unsigned long)(&__init_end));  } -/* FIXME from arch/powerpc/mm/mem.c*/ -void show_mem(void) -{ -	printk(KERN_NOTICE "%s\n", __func__); -} -  void __init mem_init(void)  {  	high_memory = (void *)__va(memory_end); @@ -208,20 +205,14 @@ void __init mem_init(void)  	printk(KERN_INFO "Memory: %luk/%luk available\n",  	       nr_free_pages() << (PAGE_SHIFT-10),  	       num_physpages << (PAGE_SHIFT-10)); -#ifdef CONFIG_MMU  	mem_init_done = 1; -#endif  }  #ifndef CONFIG_MMU -/* Check against bounds of physical memory */ -int ___range_ok(unsigned long addr, unsigned long size) +int page_is_ram(unsigned long pfn)  { -	return ((addr < memory_start) || -		((addr + size) > memory_end)); +	return __range_ok(pfn, 0);  } -EXPORT_SYMBOL(___range_ok); -  #else  int page_is_ram(unsigned long pfn)  { @@ -349,4 +340,27 @@ void __init *early_get_page(void)  	}  	return p;  } +  #endif /* CONFIG_MMU */ + +void * __init_refok alloc_maybe_bootmem(size_t size, gfp_t mask) +{ +	if (mem_init_done) +		return kmalloc(size, mask); +	else +		return alloc_bootmem(size); +} + +void * __init_refok zalloc_maybe_bootmem(size_t size, gfp_t mask) +{ +	void *p; + +	if (mem_init_done) +		p = kzalloc(size, mask); +	else { +		p = alloc_bootmem(size); +		if (p) +			memset(p, 0, size); +	} +	return p; +} diff --git a/arch/microblaze/mm/pgtable.c b/arch/microblaze/mm/pgtable.c index 2820081b21a..59bf2335a4c 100644 --- a/arch/microblaze/mm/pgtable.c +++ b/arch/microblaze/mm/pgtable.c @@ -42,6 +42,7 @@  unsigned long ioremap_base;  unsigned long ioremap_bot; +EXPORT_SYMBOL(ioremap_bot);  /* The maximum lowmem defaults to 768Mb, but this can be configured to   * another value. @@ -103,7 +104,7 @@ static void __iomem *__ioremap(phys_addr_t addr, unsigned long size,  		area = get_vm_area(size, VM_IOREMAP);  		if (area == NULL)  			return NULL; -		v = VMALLOC_VMADDR(area->addr); +		v = (unsigned long) area->addr;  	} else {  		v = (ioremap_bot -= size);  	} @@ -154,31 +155,13 @@ int map_page(unsigned long va, phys_addr_t pa, int flags)  		err = 0;  		set_pte_at(&init_mm, va, pg, pfn_pte(pa >> PAGE_SHIFT,  				__pgprot(flags))); -		if (mem_init_done) +		if (unlikely(mem_init_done))  			flush_HPTE(0, va, pmd_val(*pd));  			/* flush_HPTE(0, va, pg); */  	}  	return err;  } -void __init adjust_total_lowmem(void) -{ -/* TBD */ -#if 0 -	unsigned long max_low_mem = MAX_LOW_MEM; - -	if (total_lowmem > max_low_mem) { -		total_lowmem = max_low_mem; -#ifndef CONFIG_HIGHMEM -		printk(KERN_INFO "Warning, memory limited to %ld Mb, use " -				"CONFIG_HIGHMEM to reach %ld Mb\n", -				max_low_mem >> 20, total_memory >> 20); -		total_memory = total_lowmem; -#endif /* CONFIG_HIGHMEM */ -	} -#endif -} -  /*   * Map in all of physical memory starting at CONFIG_KERNEL_START.   */ @@ -206,24 +189,6 @@ void __init mapin_ram(void)  /* is x a power of 2? */  #define is_power_of_2(x)	((x) != 0 && (((x) & ((x) - 1)) == 0)) -/* - * 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 > CONFIG_KERNEL_START && virt < ioremap_bot) -		ioremap_bot = ioremap_base = virt; - -	/* 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 @@ -274,3 +239,18 @@ unsigned long iopa(unsigned long addr)  	return pa;  } + +__init_refok pte_t *pte_alloc_one_kernel(struct mm_struct *mm, +		unsigned long address) +{ +	pte_t *pte; +	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; +} diff --git a/arch/microblaze/pci/Makefile b/arch/microblaze/pci/Makefile new file mode 100644 index 00000000000..9889cc2e129 --- /dev/null +++ b/arch/microblaze/pci/Makefile @@ -0,0 +1,6 @@ +# +# Makefile +# + +obj-$(CONFIG_PCI)		+= pci_32.o pci-common.o indirect_pci.o iomap.o +obj-$(CONFIG_PCI_XILINX)	+= xilinx_pci.o diff --git a/arch/microblaze/pci/indirect_pci.c b/arch/microblaze/pci/indirect_pci.c new file mode 100644 index 00000000000..25f18f017f2 --- /dev/null +++ b/arch/microblaze/pci/indirect_pci.c @@ -0,0 +1,163 @@ +/* + * Support for indirect PCI bridges. + * + * Copyright (C) 1998 Gabriel Paubert. + * + * 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/pci.h> +#include <linux/delay.h> +#include <linux/string.h> +#include <linux/init.h> + +#include <asm/io.h> +#include <asm/prom.h> +#include <asm/pci-bridge.h> + +static int +indirect_read_config(struct pci_bus *bus, unsigned int devfn, int offset, +		     int len, u32 *val) +{ +	struct pci_controller *hose = pci_bus_to_host(bus); +	volatile void __iomem *cfg_data; +	u8 cfg_type = 0; +	u32 bus_no, reg; + +	if (hose->indirect_type & INDIRECT_TYPE_NO_PCIE_LINK) { +		if (bus->number != hose->first_busno) +			return PCIBIOS_DEVICE_NOT_FOUND; +		if (devfn != 0) +			return PCIBIOS_DEVICE_NOT_FOUND; +	} + +	if (hose->indirect_type & INDIRECT_TYPE_SET_CFG_TYPE) +		if (bus->number != hose->first_busno) +			cfg_type = 1; + +	bus_no = (bus->number == hose->first_busno) ? +			hose->self_busno : bus->number; + +	if (hose->indirect_type & INDIRECT_TYPE_EXT_REG) +		reg = ((offset & 0xf00) << 16) | (offset & 0xfc); +	else +		reg = offset & 0xfc; /* Only 3 bits for function */ + +	if (hose->indirect_type & INDIRECT_TYPE_BIG_ENDIAN) +		out_be32(hose->cfg_addr, (0x80000000 | (bus_no << 16) | +			 (devfn << 8) | reg | cfg_type)); +	else +		out_le32(hose->cfg_addr, (0x80000000 | (bus_no << 16) | +			 (devfn << 8) | reg | cfg_type)); + +	/* +	 * Note: the caller has already checked that offset is +	 * suitably aligned and that len is 1, 2 or 4. +	 */ +	cfg_data = hose->cfg_data + (offset & 3); /* Only 3 bits for function */ +	switch (len) { +	case 1: +		*val = in_8(cfg_data); +		break; +	case 2: +		*val = in_le16(cfg_data); +		break; +	default: +		*val = in_le32(cfg_data); +		break; +	} +	return PCIBIOS_SUCCESSFUL; +} + +static int +indirect_write_config(struct pci_bus *bus, unsigned int devfn, int offset, +		      int len, u32 val) +{ +	struct pci_controller *hose = pci_bus_to_host(bus); +	volatile void __iomem *cfg_data; +	u8 cfg_type = 0; +	u32 bus_no, reg; + +	if (hose->indirect_type & INDIRECT_TYPE_NO_PCIE_LINK) { +		if (bus->number != hose->first_busno) +			return PCIBIOS_DEVICE_NOT_FOUND; +		if (devfn != 0) +			return PCIBIOS_DEVICE_NOT_FOUND; +	} + +	if (hose->indirect_type & INDIRECT_TYPE_SET_CFG_TYPE) +		if (bus->number != hose->first_busno) +			cfg_type = 1; + +	bus_no = (bus->number == hose->first_busno) ? +			hose->self_busno : bus->number; + +	if (hose->indirect_type & INDIRECT_TYPE_EXT_REG) +		reg = ((offset & 0xf00) << 16) | (offset & 0xfc); +	else +		reg = offset & 0xfc; + +	if (hose->indirect_type & INDIRECT_TYPE_BIG_ENDIAN) +		out_be32(hose->cfg_addr, (0x80000000 | (bus_no << 16) | +			 (devfn << 8) | reg | cfg_type)); +	else +		out_le32(hose->cfg_addr, (0x80000000 | (bus_no << 16) | +			 (devfn << 8) | reg | cfg_type)); + +	/* surpress setting of PCI_PRIMARY_BUS */ +	if (hose->indirect_type & INDIRECT_TYPE_SURPRESS_PRIMARY_BUS) +		if ((offset == PCI_PRIMARY_BUS) && +			(bus->number == hose->first_busno)) +			val &= 0xffffff00; + +	/* Workaround for PCI_28 Errata in 440EPx/GRx */ +	if ((hose->indirect_type & INDIRECT_TYPE_BROKEN_MRM) && +			offset == PCI_CACHE_LINE_SIZE) { +		val = 0; +	} + +	/* +	 * Note: the caller has already checked that offset is +	 * suitably aligned and that len is 1, 2 or 4. +	 */ +	cfg_data = hose->cfg_data + (offset & 3); +	switch (len) { +	case 1: +		out_8(cfg_data, val); +		break; +	case 2: +		out_le16(cfg_data, val); +		break; +	default: +		out_le32(cfg_data, val); +		break; +	} + +	return PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops indirect_pci_ops = { +	.read = indirect_read_config, +	.write = indirect_write_config, +}; + +void __init +setup_indirect_pci(struct pci_controller *hose, +		   resource_size_t cfg_addr, +		   resource_size_t cfg_data, u32 flags) +{ +	resource_size_t base = cfg_addr & PAGE_MASK; +	void __iomem *mbase; + +	mbase = ioremap(base, PAGE_SIZE); +	hose->cfg_addr = mbase + (cfg_addr & ~PAGE_MASK); +	if ((cfg_data & PAGE_MASK) != base) +		mbase = ioremap(cfg_data & PAGE_MASK, PAGE_SIZE); +	hose->cfg_data = mbase + (cfg_data & ~PAGE_MASK); +	hose->ops = &indirect_pci_ops; +	hose->indirect_type = flags; +} diff --git a/arch/microblaze/pci/iomap.c b/arch/microblaze/pci/iomap.c new file mode 100644 index 00000000000..3fbf16f4e16 --- /dev/null +++ b/arch/microblaze/pci/iomap.c @@ -0,0 +1,39 @@ +/* + * ppc64 "iomap" interface implementation. + * + * (C) Copyright 2004 Linus Torvalds + */ +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/mm.h> +#include <asm/io.h> +#include <asm/pci-bridge.h> + +void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max) +{ +	resource_size_t start = pci_resource_start(dev, bar); +	resource_size_t len = pci_resource_len(dev, bar); +	unsigned long flags = pci_resource_flags(dev, bar); + +	if (!len) +		return NULL; +	if (max && len > max) +		len = max; +	if (flags & IORESOURCE_IO) +		return ioport_map(start, len); +	if (flags & IORESOURCE_MEM) +		return ioremap(start, len); +	/* What? */ +	return NULL; +} +EXPORT_SYMBOL(pci_iomap); + +void pci_iounmap(struct pci_dev *dev, void __iomem *addr) +{ +	if (isa_vaddr_is_ioport(addr)) +		return; +	if (pcibios_vaddr_is_ioport(addr)) +		return; +	iounmap(addr); +} +EXPORT_SYMBOL(pci_iounmap); diff --git a/arch/microblaze/pci/pci-common.c b/arch/microblaze/pci/pci-common.c new file mode 100644 index 00000000000..9cb782b8e03 --- /dev/null +++ b/arch/microblaze/pci/pci-common.c @@ -0,0 +1,1642 @@ +/* + * Contains common pci routines for ALL ppc platform + * (based on pci_32.c and pci_64.c) + * + * Port for PPC64 David Engebretsen, IBM Corp. + * Contains common pci routines for ppc64 platform, pSeries and iSeries brands. + * + * Copyright (C) 2003 Anton Blanchard <anton@au.ibm.com>, IBM + *   Rework, based on alpha PCI code. + * + * Common pmac/prep/chrp pci routines. -- Cort + * + * 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/pci.h> +#include <linux/string.h> +#include <linux/init.h> +#include <linux/bootmem.h> +#include <linux/mm.h> +#include <linux/list.h> +#include <linux/syscalls.h> +#include <linux/irq.h> +#include <linux/vmalloc.h> +#include <linux/slab.h> + +#include <asm/processor.h> +#include <asm/io.h> +#include <asm/prom.h> +#include <asm/pci-bridge.h> +#include <asm/byteorder.h> + +static DEFINE_SPINLOCK(hose_spinlock); +LIST_HEAD(hose_list); + +/* XXX kill that some day ... */ +static int global_phb_number;		/* Global phb counter */ + +/* ISA Memory physical address */ +resource_size_t isa_mem_base; + +/* Default PCI flags is 0 on ppc32, modified at boot on ppc64 */ +unsigned int pci_flags; + +static struct dma_map_ops *pci_dma_ops = &dma_direct_ops; + +void set_pci_dma_ops(struct dma_map_ops *dma_ops) +{ +	pci_dma_ops = dma_ops; +} + +struct dma_map_ops *get_pci_dma_ops(void) +{ +	return pci_dma_ops; +} +EXPORT_SYMBOL(get_pci_dma_ops); + +int pci_set_dma_mask(struct pci_dev *dev, u64 mask) +{ +	return dma_set_mask(&dev->dev, mask); +} + +int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask) +{ +	int rc; + +	rc = dma_set_mask(&dev->dev, mask); +	dev->dev.coherent_dma_mask = dev->dma_mask; + +	return rc; +} + +struct pci_controller *pcibios_alloc_controller(struct device_node *dev) +{ +	struct pci_controller *phb; + +	phb = zalloc_maybe_bootmem(sizeof(struct pci_controller), GFP_KERNEL); +	if (!phb) +		return NULL; +	spin_lock(&hose_spinlock); +	phb->global_number = global_phb_number++; +	list_add_tail(&phb->list_node, &hose_list); +	spin_unlock(&hose_spinlock); +	phb->dn = dev; +	phb->is_dynamic = mem_init_done; +	return phb; +} + +void pcibios_free_controller(struct pci_controller *phb) +{ +	spin_lock(&hose_spinlock); +	list_del(&phb->list_node); +	spin_unlock(&hose_spinlock); + +	if (phb->is_dynamic) +		kfree(phb); +} + +static resource_size_t pcibios_io_size(const struct pci_controller *hose) +{ +	return hose->io_resource.end - hose->io_resource.start + 1; +} + +int pcibios_vaddr_is_ioport(void __iomem *address) +{ +	int ret = 0; +	struct pci_controller *hose; +	resource_size_t size; + +	spin_lock(&hose_spinlock); +	list_for_each_entry(hose, &hose_list, list_node) { +		size = pcibios_io_size(hose); +		if (address >= hose->io_base_virt && +		    address < (hose->io_base_virt + size)) { +			ret = 1; +			break; +		} +	} +	spin_unlock(&hose_spinlock); +	return ret; +} + +unsigned long pci_address_to_pio(phys_addr_t address) +{ +	struct pci_controller *hose; +	resource_size_t size; +	unsigned long ret = ~0; + +	spin_lock(&hose_spinlock); +	list_for_each_entry(hose, &hose_list, list_node) { +		size = pcibios_io_size(hose); +		if (address >= hose->io_base_phys && +		    address < (hose->io_base_phys + size)) { +			unsigned long base = +				(unsigned long)hose->io_base_virt - _IO_BASE; +			ret = base + (address - hose->io_base_phys); +			break; +		} +	} +	spin_unlock(&hose_spinlock); + +	return ret; +} +EXPORT_SYMBOL_GPL(pci_address_to_pio); + +/* + * Return the domain number for this bus. + */ +int pci_domain_nr(struct pci_bus *bus) +{ +	struct pci_controller *hose = pci_bus_to_host(bus); + +	return hose->global_number; +} +EXPORT_SYMBOL(pci_domain_nr); + +/* This routine is meant to be used early during boot, when the + * PCI bus numbers have not yet been assigned, and you need to + * issue PCI config cycles to an OF device. + * It could also be used to "fix" RTAS config cycles if you want + * to set pci_assign_all_buses to 1 and still use RTAS for PCI + * config cycles. + */ +struct pci_controller *pci_find_hose_for_OF_device(struct device_node *node) +{ +	while (node) { +		struct pci_controller *hose, *tmp; +		list_for_each_entry_safe(hose, tmp, &hose_list, list_node) +			if (hose->dn == node) +				return hose; +		node = node->parent; +	} +	return NULL; +} + +static ssize_t pci_show_devspec(struct device *dev, +		struct device_attribute *attr, char *buf) +{ +	struct pci_dev *pdev; +	struct device_node *np; + +	pdev = to_pci_dev(dev); +	np = pci_device_to_OF_node(pdev); +	if (np == NULL || np->full_name == NULL) +		return 0; +	return sprintf(buf, "%s", np->full_name); +} +static DEVICE_ATTR(devspec, S_IRUGO, pci_show_devspec, NULL); + +/* Add sysfs properties */ +int pcibios_add_platform_entries(struct pci_dev *pdev) +{ +	return device_create_file(&pdev->dev, &dev_attr_devspec); +} + +char __devinit *pcibios_setup(char *str) +{ +	return str; +} + +/* + * Reads the interrupt pin to determine if interrupt is use by card. + * If the interrupt is used, then gets the interrupt line from the + * openfirmware and sets it in the pci_dev and pci_config line. + */ +int pci_read_irq_line(struct pci_dev *pci_dev) +{ +	struct of_irq oirq; +	unsigned int virq; + +	/* The current device-tree that iSeries generates from the HV +	 * PCI informations doesn't contain proper interrupt routing, +	 * and all the fallback would do is print out crap, so we +	 * don't attempt to resolve the interrupts here at all, some +	 * iSeries specific fixup does it. +	 * +	 * In the long run, we will hopefully fix the generated device-tree +	 * instead. +	 */ +	pr_debug("PCI: Try to map irq for %s...\n", pci_name(pci_dev)); + +#ifdef DEBUG +	memset(&oirq, 0xff, sizeof(oirq)); +#endif +	/* Try to get a mapping from the device-tree */ +	if (of_irq_map_pci(pci_dev, &oirq)) { +		u8 line, pin; + +		/* If that fails, lets fallback to what is in the config +		 * space and map that through the default controller. We +		 * also set the type to level low since that's what PCI +		 * interrupts are. If your platform does differently, then +		 * either provide a proper interrupt tree or don't use this +		 * function. +		 */ +		if (pci_read_config_byte(pci_dev, PCI_INTERRUPT_PIN, &pin)) +			return -1; +		if (pin == 0) +			return -1; +		if (pci_read_config_byte(pci_dev, PCI_INTERRUPT_LINE, &line) || +		    line == 0xff || line == 0) { +			return -1; +		} +		pr_debug(" No map ! Using line %d (pin %d) from PCI config\n", +			 line, pin); + +		virq = irq_create_mapping(NULL, line); +		if (virq != NO_IRQ) +			set_irq_type(virq, IRQ_TYPE_LEVEL_LOW); +	} else { +		pr_debug(" Got one, spec %d cells (0x%08x 0x%08x...) on %s\n", +			 oirq.size, oirq.specifier[0], oirq.specifier[1], +			 oirq.controller ? oirq.controller->full_name : +			 "<default>"); + +		virq = irq_create_of_mapping(oirq.controller, oirq.specifier, +					     oirq.size); +	} +	if (virq == NO_IRQ) { +		pr_debug(" Failed to map !\n"); +		return -1; +	} + +	pr_debug(" Mapped to linux irq %d\n", virq); + +	pci_dev->irq = virq; + +	return 0; +} +EXPORT_SYMBOL(pci_read_irq_line); + +/* + * Platform support for /proc/bus/pci/X/Y mmap()s, + * modelled on the sparc64 implementation by Dave Miller. + *  -- paulus. + */ + +/* + * Adjust vm_pgoff of VMA such that it is the physical page offset + * corresponding to the 32-bit pci bus offset for DEV requested by the user. + * + * Basically, the user finds the base address for his device which he wishes + * to mmap.  They read the 32-bit value from the config space base register, + * add whatever PAGE_SIZE multiple offset they wish, and feed this into the + * offset parameter of mmap on /proc/bus/pci/XXX for that device. + * + * Returns negative error code on failure, zero on success. + */ +static struct resource *__pci_mmap_make_offset(struct pci_dev *dev, +					       resource_size_t *offset, +					       enum pci_mmap_state mmap_state) +{ +	struct pci_controller *hose = pci_bus_to_host(dev->bus); +	unsigned long io_offset = 0; +	int i, res_bit; + +	if (hose == 0) +		return NULL;		/* should never happen */ + +	/* If memory, add on the PCI bridge address offset */ +	if (mmap_state == pci_mmap_mem) { +#if 0 /* See comment in pci_resource_to_user() for why this is disabled */ +		*offset += hose->pci_mem_offset; +#endif +		res_bit = IORESOURCE_MEM; +	} else { +		io_offset = (unsigned long)hose->io_base_virt - _IO_BASE; +		*offset += io_offset; +		res_bit = IORESOURCE_IO; +	} + +	/* +	 * Check that the offset requested corresponds to one of the +	 * resources of the device. +	 */ +	for (i = 0; i <= PCI_ROM_RESOURCE; i++) { +		struct resource *rp = &dev->resource[i]; +		int flags = rp->flags; + +		/* treat ROM as memory (should be already) */ +		if (i == PCI_ROM_RESOURCE) +			flags |= IORESOURCE_MEM; + +		/* Active and same type? */ +		if ((flags & res_bit) == 0) +			continue; + +		/* In the range of this resource? */ +		if (*offset < (rp->start & PAGE_MASK) || *offset > rp->end) +			continue; + +		/* found it! construct the final physical address */ +		if (mmap_state == pci_mmap_io) +			*offset += hose->io_base_phys - io_offset; +		return rp; +	} + +	return NULL; +} + +/* + * Set vm_page_prot of VMA, as appropriate for this architecture, for a pci + * device mapping. + */ +static pgprot_t __pci_mmap_set_pgprot(struct pci_dev *dev, struct resource *rp, +				      pgprot_t protection, +				      enum pci_mmap_state mmap_state, +				      int write_combine) +{ +	pgprot_t prot = protection; + +	/* Write combine is always 0 on non-memory space mappings. On +	 * memory space, if the user didn't pass 1, we check for a +	 * "prefetchable" resource. This is a bit hackish, but we use +	 * this to workaround the inability of /sysfs to provide a write +	 * combine bit +	 */ +	if (mmap_state != pci_mmap_mem) +		write_combine = 0; +	else if (write_combine == 0) { +		if (rp->flags & IORESOURCE_PREFETCH) +			write_combine = 1; +	} + +	return pgprot_noncached(prot); +} + +/* + * This one is used by /dev/mem and fbdev who have no clue about the + * PCI device, it tries to find the PCI device first and calls the + * above routine + */ +pgprot_t pci_phys_mem_access_prot(struct file *file, +				  unsigned long pfn, +				  unsigned long size, +				  pgprot_t prot) +{ +	struct pci_dev *pdev = NULL; +	struct resource *found = NULL; +	resource_size_t offset = ((resource_size_t)pfn) << PAGE_SHIFT; +	int i; + +	if (page_is_ram(pfn)) +		return prot; + +	prot = pgprot_noncached(prot); +	for_each_pci_dev(pdev) { +		for (i = 0; i <= PCI_ROM_RESOURCE; i++) { +			struct resource *rp = &pdev->resource[i]; +			int flags = rp->flags; + +			/* Active and same type? */ +			if ((flags & IORESOURCE_MEM) == 0) +				continue; +			/* In the range of this resource? */ +			if (offset < (rp->start & PAGE_MASK) || +			    offset > rp->end) +				continue; +			found = rp; +			break; +		} +		if (found) +			break; +	} +	if (found) { +		if (found->flags & IORESOURCE_PREFETCH) +			prot = pgprot_noncached_wc(prot); +		pci_dev_put(pdev); +	} + +	pr_debug("PCI: Non-PCI map for %llx, prot: %lx\n", +		 (unsigned long long)offset, pgprot_val(prot)); + +	return prot; +} + +/* + * Perform the actual remap of the pages for a PCI device mapping, as + * appropriate for this architecture.  The region in the process to map + * is described by vm_start and vm_end members of VMA, the base physical + * address is found in vm_pgoff. + * The pci device structure is provided so that architectures may make mapping + * decisions on a per-device or per-bus basis. + * + * Returns a negative error code on failure, zero on success. + */ +int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, +			enum pci_mmap_state mmap_state, int write_combine) +{ +	resource_size_t offset = +		((resource_size_t)vma->vm_pgoff) << PAGE_SHIFT; +	struct resource *rp; +	int ret; + +	rp = __pci_mmap_make_offset(dev, &offset, mmap_state); +	if (rp == NULL) +		return -EINVAL; + +	vma->vm_pgoff = offset >> PAGE_SHIFT; +	vma->vm_page_prot = __pci_mmap_set_pgprot(dev, rp, +						  vma->vm_page_prot, +						  mmap_state, write_combine); + +	ret = remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, +			       vma->vm_end - vma->vm_start, vma->vm_page_prot); + +	return ret; +} + +/* This provides legacy IO read access on a bus */ +int pci_legacy_read(struct pci_bus *bus, loff_t port, u32 *val, size_t size) +{ +	unsigned long offset; +	struct pci_controller *hose = pci_bus_to_host(bus); +	struct resource *rp = &hose->io_resource; +	void __iomem *addr; + +	/* Check if port can be supported by that bus. We only check +	 * the ranges of the PHB though, not the bus itself as the rules +	 * for forwarding legacy cycles down bridges are not our problem +	 * here. So if the host bridge supports it, we do it. +	 */ +	offset = (unsigned long)hose->io_base_virt - _IO_BASE; +	offset += port; + +	if (!(rp->flags & IORESOURCE_IO)) +		return -ENXIO; +	if (offset < rp->start || (offset + size) > rp->end) +		return -ENXIO; +	addr = hose->io_base_virt + port; + +	switch (size) { +	case 1: +		*((u8 *)val) = in_8(addr); +		return 1; +	case 2: +		if (port & 1) +			return -EINVAL; +		*((u16 *)val) = in_le16(addr); +		return 2; +	case 4: +		if (port & 3) +			return -EINVAL; +		*((u32 *)val) = in_le32(addr); +		return 4; +	} +	return -EINVAL; +} + +/* This provides legacy IO write access on a bus */ +int pci_legacy_write(struct pci_bus *bus, loff_t port, u32 val, size_t size) +{ +	unsigned long offset; +	struct pci_controller *hose = pci_bus_to_host(bus); +	struct resource *rp = &hose->io_resource; +	void __iomem *addr; + +	/* Check if port can be supported by that bus. We only check +	 * the ranges of the PHB though, not the bus itself as the rules +	 * for forwarding legacy cycles down bridges are not our problem +	 * here. So if the host bridge supports it, we do it. +	 */ +	offset = (unsigned long)hose->io_base_virt - _IO_BASE; +	offset += port; + +	if (!(rp->flags & IORESOURCE_IO)) +		return -ENXIO; +	if (offset < rp->start || (offset + size) > rp->end) +		return -ENXIO; +	addr = hose->io_base_virt + port; + +	/* WARNING: The generic code is idiotic. It gets passed a pointer +	 * to what can be a 1, 2 or 4 byte quantity and always reads that +	 * as a u32, which means that we have to correct the location of +	 * the data read within those 32 bits for size 1 and 2 +	 */ +	switch (size) { +	case 1: +		out_8(addr, val >> 24); +		return 1; +	case 2: +		if (port & 1) +			return -EINVAL; +		out_le16(addr, val >> 16); +		return 2; +	case 4: +		if (port & 3) +			return -EINVAL; +		out_le32(addr, val); +		return 4; +	} +	return -EINVAL; +} + +/* This provides legacy IO or memory mmap access on a bus */ +int pci_mmap_legacy_page_range(struct pci_bus *bus, +			       struct vm_area_struct *vma, +			       enum pci_mmap_state mmap_state) +{ +	struct pci_controller *hose = pci_bus_to_host(bus); +	resource_size_t offset = +		((resource_size_t)vma->vm_pgoff) << PAGE_SHIFT; +	resource_size_t size = vma->vm_end - vma->vm_start; +	struct resource *rp; + +	pr_debug("pci_mmap_legacy_page_range(%04x:%02x, %s @%llx..%llx)\n", +		 pci_domain_nr(bus), bus->number, +		 mmap_state == pci_mmap_mem ? "MEM" : "IO", +		 (unsigned long long)offset, +		 (unsigned long long)(offset + size - 1)); + +	if (mmap_state == pci_mmap_mem) { +		/* Hack alert ! +		 * +		 * Because X is lame and can fail starting if it gets an error +		 * trying to mmap legacy_mem (instead of just moving on without +		 * legacy memory access) we fake it here by giving it anonymous +		 * memory, effectively behaving just like /dev/zero +		 */ +		if ((offset + size) > hose->isa_mem_size) { +#ifdef CONFIG_MMU +			printk(KERN_DEBUG +				"Process %s (pid:%d) mapped non-existing PCI" +				"legacy memory for 0%04x:%02x\n", +				current->comm, current->pid, pci_domain_nr(bus), +								bus->number); +#endif +			if (vma->vm_flags & VM_SHARED) +				return shmem_zero_setup(vma); +			return 0; +		} +		offset += hose->isa_mem_phys; +	} else { +		unsigned long io_offset = (unsigned long)hose->io_base_virt - \ +								_IO_BASE; +		unsigned long roffset = offset + io_offset; +		rp = &hose->io_resource; +		if (!(rp->flags & IORESOURCE_IO)) +			return -ENXIO; +		if (roffset < rp->start || (roffset + size) > rp->end) +			return -ENXIO; +		offset += hose->io_base_phys; +	} +	pr_debug(" -> mapping phys %llx\n", (unsigned long long)offset); + +	vma->vm_pgoff = offset >> PAGE_SHIFT; +	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); +	return remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, +			       vma->vm_end - vma->vm_start, +			       vma->vm_page_prot); +} + +void pci_resource_to_user(const struct pci_dev *dev, int bar, +			  const struct resource *rsrc, +			  resource_size_t *start, resource_size_t *end) +{ +	struct pci_controller *hose = pci_bus_to_host(dev->bus); +	resource_size_t offset = 0; + +	if (hose == NULL) +		return; + +	if (rsrc->flags & IORESOURCE_IO) +		offset = (unsigned long)hose->io_base_virt - _IO_BASE; + +	/* We pass a fully fixed up address to userland for MMIO instead of +	 * a BAR value because X is lame and expects to be able to use that +	 * to pass to /dev/mem ! +	 * +	 * That means that we'll have potentially 64 bits values where some +	 * userland apps only expect 32 (like X itself since it thinks only +	 * Sparc has 64 bits MMIO) but if we don't do that, we break it on +	 * 32 bits CHRPs :-( +	 * +	 * Hopefully, the sysfs insterface is immune to that gunk. Once X +	 * has been fixed (and the fix spread enough), we can re-enable the +	 * 2 lines below and pass down a BAR value to userland. In that case +	 * we'll also have to re-enable the matching code in +	 * __pci_mmap_make_offset(). +	 * +	 * BenH. +	 */ +#if 0 +	else if (rsrc->flags & IORESOURCE_MEM) +		offset = hose->pci_mem_offset; +#endif + +	*start = rsrc->start - offset; +	*end = rsrc->end - offset; +} + +/** + * pci_process_bridge_OF_ranges - Parse PCI bridge resources from device tree + * @hose: newly allocated pci_controller to be setup + * @dev: device node of the host bridge + * @primary: set if primary bus (32 bits only, soon to be deprecated) + * + * This function will parse the "ranges" property of a PCI host bridge device + * node and setup the resource mapping of a pci controller based on its + * content. + * + * Life would be boring if it wasn't for a few issues that we have to deal + * with here: + * + *   - We can only cope with one IO space range and up to 3 Memory space + *     ranges. However, some machines (thanks Apple !) tend to split their + *     space into lots of small contiguous ranges. So we have to coalesce. + * + *   - We can only cope with all memory ranges having the same offset + *     between CPU addresses and PCI addresses. Unfortunately, some bridges + *     are setup for a large 1:1 mapping along with a small "window" which + *     maps PCI address 0 to some arbitrary high address of the CPU space in + *     order to give access to the ISA memory hole. + *     The way out of here that I've chosen for now is to always set the + *     offset based on the first resource found, then override it if we + *     have a different offset and the previous was set by an ISA hole. + * + *   - Some busses have IO space not starting at 0, which causes trouble with + *     the way we do our IO resource renumbering. The code somewhat deals with + *     it for 64 bits but I would expect problems on 32 bits. + * + *   - Some 32 bits platforms such as 4xx can have physical space larger than + *     32 bits so we need to use 64 bits values for the parsing + */ +void __devinit pci_process_bridge_OF_ranges(struct pci_controller *hose, +					    struct device_node *dev, +					    int primary) +{ +	const u32 *ranges; +	int rlen; +	int pna = of_n_addr_cells(dev); +	int np = pna + 5; +	int memno = 0, isa_hole = -1; +	u32 pci_space; +	unsigned long long pci_addr, cpu_addr, pci_next, cpu_next, size; +	unsigned long long isa_mb = 0; +	struct resource *res; + +	printk(KERN_INFO "PCI host bridge %s %s ranges:\n", +	       dev->full_name, primary ? "(primary)" : ""); + +	/* Get ranges property */ +	ranges = of_get_property(dev, "ranges", &rlen); +	if (ranges == NULL) +		return; + +	/* Parse it */ +	pr_debug("Parsing ranges property...\n"); +	while ((rlen -= np * 4) >= 0) { +		/* Read next ranges element */ +		pci_space = ranges[0]; +		pci_addr = of_read_number(ranges + 1, 2); +		cpu_addr = of_translate_address(dev, ranges + 3); +		size = of_read_number(ranges + pna + 3, 2); + +		pr_debug("pci_space: 0x%08x pci_addr:0x%016llx " +				"cpu_addr:0x%016llx size:0x%016llx\n", +					pci_space, pci_addr, cpu_addr, size); + +		ranges += np; + +		/* If we failed translation or got a zero-sized region +		 * (some FW try to feed us with non sensical zero sized regions +		 * such as power3 which look like some kind of attempt +		 * at exposing the VGA memory hole) +		 */ +		if (cpu_addr == OF_BAD_ADDR || size == 0) +			continue; + +		/* Now consume following elements while they are contiguous */ +		for (; rlen >= np * sizeof(u32); +		     ranges += np, rlen -= np * 4) { +			if (ranges[0] != pci_space) +				break; +			pci_next = of_read_number(ranges + 1, 2); +			cpu_next = of_translate_address(dev, ranges + 3); +			if (pci_next != pci_addr + size || +			    cpu_next != cpu_addr + size) +				break; +			size += of_read_number(ranges + pna + 3, 2); +		} + +		/* Act based on address space type */ +		res = NULL; +		switch ((pci_space >> 24) & 0x3) { +		case 1:		/* PCI IO space */ +			printk(KERN_INFO +			       "  IO 0x%016llx..0x%016llx -> 0x%016llx\n", +			       cpu_addr, cpu_addr + size - 1, pci_addr); + +			/* We support only one IO range */ +			if (hose->pci_io_size) { +				printk(KERN_INFO +				       " \\--> Skipped (too many) !\n"); +				continue; +			} +			/* On 32 bits, limit I/O space to 16MB */ +			if (size > 0x01000000) +				size = 0x01000000; + +			/* 32 bits needs to map IOs here */ +			hose->io_base_virt = ioremap(cpu_addr, size); + +			/* Expect trouble if pci_addr is not 0 */ +			if (primary) +				isa_io_base = +					(unsigned long)hose->io_base_virt; +			/* pci_io_size and io_base_phys always represent IO +			 * space starting at 0 so we factor in pci_addr +			 */ +			hose->pci_io_size = pci_addr + size; +			hose->io_base_phys = cpu_addr - pci_addr; + +			/* Build resource */ +			res = &hose->io_resource; +			res->flags = IORESOURCE_IO; +			res->start = pci_addr; +			break; +		case 2:		/* PCI Memory space */ +		case 3:		/* PCI 64 bits Memory space */ +			printk(KERN_INFO +			       " MEM 0x%016llx..0x%016llx -> 0x%016llx %s\n", +			       cpu_addr, cpu_addr + size - 1, pci_addr, +			       (pci_space & 0x40000000) ? "Prefetch" : ""); + +			/* We support only 3 memory ranges */ +			if (memno >= 3) { +				printk(KERN_INFO +				       " \\--> Skipped (too many) !\n"); +				continue; +			} +			/* Handles ISA memory hole space here */ +			if (pci_addr == 0) { +				isa_mb = cpu_addr; +				isa_hole = memno; +				if (primary || isa_mem_base == 0) +					isa_mem_base = cpu_addr; +				hose->isa_mem_phys = cpu_addr; +				hose->isa_mem_size = size; +			} + +			/* We get the PCI/Mem offset from the first range or +			 * the, current one if the offset came from an ISA +			 * hole. If they don't match, bugger. +			 */ +			if (memno == 0 || +			    (isa_hole >= 0 && pci_addr != 0 && +			     hose->pci_mem_offset == isa_mb)) +				hose->pci_mem_offset = cpu_addr - pci_addr; +			else if (pci_addr != 0 && +				 hose->pci_mem_offset != cpu_addr - pci_addr) { +				printk(KERN_INFO +				       " \\--> Skipped (offset mismatch) !\n"); +				continue; +			} + +			/* Build resource */ +			res = &hose->mem_resources[memno++]; +			res->flags = IORESOURCE_MEM; +			if (pci_space & 0x40000000) +				res->flags |= IORESOURCE_PREFETCH; +			res->start = cpu_addr; +			break; +		} +		if (res != NULL) { +			res->name = dev->full_name; +			res->end = res->start + size - 1; +			res->parent = NULL; +			res->sibling = NULL; +			res->child = NULL; +		} +	} + +	/* If there's an ISA hole and the pci_mem_offset is -not- matching +	 * the ISA hole offset, then we need to remove the ISA hole from +	 * the resource list for that brige +	 */ +	if (isa_hole >= 0 && hose->pci_mem_offset != isa_mb) { +		unsigned int next = isa_hole + 1; +		printk(KERN_INFO " Removing ISA hole at 0x%016llx\n", isa_mb); +		if (next < memno) +			memmove(&hose->mem_resources[isa_hole], +				&hose->mem_resources[next], +				sizeof(struct resource) * (memno - next)); +		hose->mem_resources[--memno].flags = 0; +	} +} + +/* Decide whether to display the domain number in /proc */ +int pci_proc_domain(struct pci_bus *bus) +{ +	struct pci_controller *hose = pci_bus_to_host(bus); + +	if (!(pci_flags & PCI_ENABLE_PROC_DOMAINS)) +		return 0; +	if (pci_flags & PCI_COMPAT_DOMAIN_0) +		return hose->global_number != 0; +	return 1; +} + +void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, +			     struct resource *res) +{ +	resource_size_t offset = 0, mask = (resource_size_t)-1; +	struct pci_controller *hose = pci_bus_to_host(dev->bus); + +	if (!hose) +		return; +	if (res->flags & IORESOURCE_IO) { +		offset = (unsigned long)hose->io_base_virt - _IO_BASE; +		mask = 0xffffffffu; +	} else if (res->flags & IORESOURCE_MEM) +		offset = hose->pci_mem_offset; + +	region->start = (res->start - offset) & mask; +	region->end = (res->end - offset) & mask; +} +EXPORT_SYMBOL(pcibios_resource_to_bus); + +void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, +			     struct pci_bus_region *region) +{ +	resource_size_t offset = 0, mask = (resource_size_t)-1; +	struct pci_controller *hose = pci_bus_to_host(dev->bus); + +	if (!hose) +		return; +	if (res->flags & IORESOURCE_IO) { +		offset = (unsigned long)hose->io_base_virt - _IO_BASE; +		mask = 0xffffffffu; +	} else if (res->flags & IORESOURCE_MEM) +		offset = hose->pci_mem_offset; +	res->start = (region->start + offset) & mask; +	res->end = (region->end + offset) & mask; +} +EXPORT_SYMBOL(pcibios_bus_to_resource); + +/* Fixup a bus resource into a linux resource */ +static void __devinit fixup_resource(struct resource *res, struct pci_dev *dev) +{ +	struct pci_controller *hose = pci_bus_to_host(dev->bus); +	resource_size_t offset = 0, mask = (resource_size_t)-1; + +	if (res->flags & IORESOURCE_IO) { +		offset = (unsigned long)hose->io_base_virt - _IO_BASE; +		mask = 0xffffffffu; +	} else if (res->flags & IORESOURCE_MEM) +		offset = hose->pci_mem_offset; + +	res->start = (res->start + offset) & mask; +	res->end = (res->end + offset) & mask; +} + +/* This header fixup will do the resource fixup for all devices as they are + * probed, but not for bridge ranges + */ +static void __devinit pcibios_fixup_resources(struct pci_dev *dev) +{ +	struct pci_controller *hose = pci_bus_to_host(dev->bus); +	int i; + +	if (!hose) { +		printk(KERN_ERR "No host bridge for PCI dev %s !\n", +		       pci_name(dev)); +		return; +	} +	for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { +		struct resource *res = dev->resource + i; +		if (!res->flags) +			continue; +		/* On platforms that have PCI_PROBE_ONLY set, we don't +		 * consider 0 as an unassigned BAR value. It's technically +		 * a valid value, but linux doesn't like it... so when we can +		 * re-assign things, we do so, but if we can't, we keep it +		 * around and hope for the best... +		 */ +		if (res->start == 0 && !(pci_flags & PCI_PROBE_ONLY)) { +			pr_debug("PCI:%s Resource %d %016llx-%016llx [%x]" \ +							"is unassigned\n", +				 pci_name(dev), i, +				 (unsigned long long)res->start, +				 (unsigned long long)res->end, +				 (unsigned int)res->flags); +			res->end -= res->start; +			res->start = 0; +			res->flags |= IORESOURCE_UNSET; +			continue; +		} + +		pr_debug("PCI:%s Resource %d %016llx-%016llx [%x] fixup...\n", +			 pci_name(dev), i, +			 (unsigned long long)res->start,\ +			 (unsigned long long)res->end, +			 (unsigned int)res->flags); + +		fixup_resource(res, dev); + +		pr_debug("PCI:%s            %016llx-%016llx\n", +			 pci_name(dev), +			 (unsigned long long)res->start, +			 (unsigned long long)res->end); +	} +} +DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pcibios_fixup_resources); + +/* This function tries to figure out if a bridge resource has been initialized + * by the firmware or not. It doesn't have to be absolutely bullet proof, but + * things go more smoothly when it gets it right. It should covers cases such + * as Apple "closed" bridge resources and bare-metal pSeries unassigned bridges + */ +static int __devinit pcibios_uninitialized_bridge_resource(struct pci_bus *bus, +							   struct resource *res) +{ +	struct pci_controller *hose = pci_bus_to_host(bus); +	struct pci_dev *dev = bus->self; +	resource_size_t offset; +	u16 command; +	int i; + +	/* We don't do anything if PCI_PROBE_ONLY is set */ +	if (pci_flags & PCI_PROBE_ONLY) +		return 0; + +	/* Job is a bit different between memory and IO */ +	if (res->flags & IORESOURCE_MEM) { +		/* If the BAR is non-0 (res != pci_mem_offset) then it's +		 * probably been initialized by somebody +		 */ +		if (res->start != hose->pci_mem_offset) +			return 0; + +		/* The BAR is 0, let's check if memory decoding is enabled on +		 * the bridge. If not, we consider it unassigned +		 */ +		pci_read_config_word(dev, PCI_COMMAND, &command); +		if ((command & PCI_COMMAND_MEMORY) == 0) +			return 1; + +		/* Memory decoding is enabled and the BAR is 0. If any of +		 * the bridge resources covers that starting address (0 then +		 * it's good enough for us for memory +		 */ +		for (i = 0; i < 3; i++) { +			if ((hose->mem_resources[i].flags & IORESOURCE_MEM) && +			   hose->mem_resources[i].start == hose->pci_mem_offset) +				return 0; +		} + +		/* Well, it starts at 0 and we know it will collide so we may as +		 * well consider it as unassigned. That covers the Apple case. +		 */ +		return 1; +	} else { +		/* If the BAR is non-0, then we consider it assigned */ +		offset = (unsigned long)hose->io_base_virt - _IO_BASE; +		if (((res->start - offset) & 0xfffffffful) != 0) +			return 0; + +		/* Here, we are a bit different than memory as typically IO +		 * space starting at low addresses -is- valid. What we do +		 * instead if that we consider as unassigned anything that +		 * doesn't have IO enabled in the PCI command register, +		 * and that's it. +		 */ +		pci_read_config_word(dev, PCI_COMMAND, &command); +		if (command & PCI_COMMAND_IO) +			return 0; + +		/* It's starting at 0 and IO is disabled in the bridge, consider +		 * it unassigned +		 */ +		return 1; +	} +} + +/* Fixup resources of a PCI<->PCI bridge */ +static void __devinit pcibios_fixup_bridge(struct pci_bus *bus) +{ +	struct resource *res; +	int i; + +	struct pci_dev *dev = bus->self; + +	pci_bus_for_each_resource(bus, res, i) { +		res = bus->resource[i]; +		if (!res) +			continue; +		if (!res->flags) +			continue; +		if (i >= 3 && bus->self->transparent) +			continue; + +		pr_debug("PCI:%s Bus rsrc %d %016llx-%016llx [%x] fixup...\n", +			 pci_name(dev), i, +			 (unsigned long long)res->start,\ +			 (unsigned long long)res->end, +			 (unsigned int)res->flags); + +		/* Perform fixup */ +		fixup_resource(res, dev); + +		/* Try to detect uninitialized P2P bridge resources, +		 * and clear them out so they get re-assigned later +		 */ +		if (pcibios_uninitialized_bridge_resource(bus, res)) { +			res->flags = 0; +			pr_debug("PCI:%s            (unassigned)\n", +								pci_name(dev)); +		} else { +			pr_debug("PCI:%s            %016llx-%016llx\n", +				 pci_name(dev), +				 (unsigned long long)res->start, +				 (unsigned long long)res->end); +		} +	} +} + +void __devinit pcibios_setup_bus_self(struct pci_bus *bus) +{ +	/* Fix up the bus resources for P2P bridges */ +	if (bus->self != NULL) +		pcibios_fixup_bridge(bus); +} + +void __devinit pcibios_setup_bus_devices(struct pci_bus *bus) +{ +	struct pci_dev *dev; + +	pr_debug("PCI: Fixup bus devices %d (%s)\n", +		 bus->number, bus->self ? pci_name(bus->self) : "PHB"); + +	list_for_each_entry(dev, &bus->devices, bus_list) { +		struct dev_archdata *sd = &dev->dev.archdata; + +		/* Setup OF node pointer in archdata */ +		sd->of_node = pci_device_to_OF_node(dev); + +		/* Fixup NUMA node as it may not be setup yet by the generic +		 * code and is needed by the DMA init +		 */ +		set_dev_node(&dev->dev, pcibus_to_node(dev->bus)); + +		/* Hook up default DMA ops */ +		sd->dma_ops = pci_dma_ops; +		sd->dma_data = (void *)PCI_DRAM_OFFSET; + +		/* Read default IRQs and fixup if necessary */ +		pci_read_irq_line(dev); +	} +} + +void __devinit pcibios_fixup_bus(struct pci_bus *bus) +{ +	/* When called from the generic PCI probe, read PCI<->PCI bridge +	 * bases. This is -not- called when generating the PCI tree from +	 * the OF device-tree. +	 */ +	if (bus->self != NULL) +		pci_read_bridge_bases(bus); + +	/* Now fixup the bus bus */ +	pcibios_setup_bus_self(bus); + +	/* Now fixup devices on that bus */ +	pcibios_setup_bus_devices(bus); +} +EXPORT_SYMBOL(pcibios_fixup_bus); + +static int skip_isa_ioresource_align(struct pci_dev *dev) +{ +	if ((pci_flags & PCI_CAN_SKIP_ISA_ALIGN) && +	    !(dev->bus->bridge_ctl & PCI_BRIDGE_CTL_ISA)) +		return 1; +	return 0; +} + +/* + * We need to avoid collisions with `mirrored' VGA ports + * and other strange ISA hardware, so we always want the + * addresses to be allocated in the 0x000-0x0ff region + * modulo 0x400. + * + * Why? Because some silly external IO cards only decode + * the low 10 bits of the IO address. The 0x00-0xff region + * is reserved for motherboard devices that decode all 16 + * bits, so it's ok to allocate at, say, 0x2800-0x28ff, + * but we want to try to avoid allocating at 0x2900-0x2bff + * which might have be mirrored at 0x0100-0x03ff.. + */ +resource_size_t pcibios_align_resource(void *data, const struct resource *res, +				resource_size_t size, resource_size_t align) +{ +	struct pci_dev *dev = data; +	resource_size_t start = res->start; + +	if (res->flags & IORESOURCE_IO) { +		if (skip_isa_ioresource_align(dev)) +			return start; +		if (start & 0x300) +			start = (start + 0x3ff) & ~0x3ff; +	} + +	return start; +} +EXPORT_SYMBOL(pcibios_align_resource); + +/* + * Reparent resource children of pr that conflict with res + * under res, and make res replace those children. + */ +static int __init reparent_resources(struct resource *parent, +				     struct resource *res) +{ +	struct resource *p, **pp; +	struct resource **firstpp = NULL; + +	for (pp = &parent->child; (p = *pp) != NULL; pp = &p->sibling) { +		if (p->end < res->start) +			continue; +		if (res->end < p->start) +			break; +		if (p->start < res->start || p->end > res->end) +			return -1;	/* not completely contained */ +		if (firstpp == NULL) +			firstpp = pp; +	} +	if (firstpp == NULL) +		return -1;	/* didn't find any conflicting entries? */ +	res->parent = parent; +	res->child = *firstpp; +	res->sibling = *pp; +	*firstpp = res; +	*pp = NULL; +	for (p = res->child; p != NULL; p = p->sibling) { +		p->parent = res; +		pr_debug("PCI: Reparented %s [%llx..%llx] under %s\n", +			 p->name, +			 (unsigned long long)p->start, +			 (unsigned long long)p->end, res->name); +	} +	return 0; +} + +/* + *  Handle resources of PCI devices.  If the world were perfect, we could + *  just allocate all the resource regions and do nothing more.  It isn't. + *  On the other hand, we cannot just re-allocate all devices, as it would + *  require us to know lots of host bridge internals.  So we attempt to + *  keep as much of the original configuration as possible, but tweak it + *  when it's found to be wrong. + * + *  Known BIOS problems we have to work around: + *	- I/O or memory regions not configured + *	- regions configured, but not enabled in the command register + *	- bogus I/O addresses above 64K used + *	- expansion ROMs left enabled (this may sound harmless, but given + *	  the fact the PCI specs explicitly allow address decoders to be + *	  shared between expansion ROMs and other resource regions, it's + *	  at least dangerous) + * + *  Our solution: + *	(1) Allocate resources for all buses behind PCI-to-PCI bridges. + *	    This gives us fixed barriers on where we can allocate. + *	(2) Allocate resources for all enabled devices.  If there is + *	    a collision, just mark the resource as unallocated. Also + *	    disable expansion ROMs during this step. + *	(3) Try to allocate resources for disabled devices.  If the + *	    resources were assigned correctly, everything goes well, + *	    if they weren't, they won't disturb allocation of other + *	    resources. + *	(4) Assign new addresses to resources which were either + *	    not configured at all or misconfigured.  If explicitly + *	    requested by the user, configure expansion ROM address + *	    as well. + */ + +void pcibios_allocate_bus_resources(struct pci_bus *bus) +{ +	struct pci_bus *b; +	int i; +	struct resource *res, *pr; + +	pr_debug("PCI: Allocating bus resources for %04x:%02x...\n", +		 pci_domain_nr(bus), bus->number); + +	pci_bus_for_each_resource(bus, res, i) { +		res = bus->resource[i]; +		if (!res || !res->flags +		    || res->start > res->end || res->parent) +			continue; +		if (bus->parent == NULL) +			pr = (res->flags & IORESOURCE_IO) ? +				&ioport_resource : &iomem_resource; +		else { +			/* Don't bother with non-root busses when +			 * re-assigning all resources. We clear the +			 * resource flags as if they were colliding +			 * and as such ensure proper re-allocation +			 * later. +			 */ +			if (pci_flags & PCI_REASSIGN_ALL_RSRC) +				goto clear_resource; +			pr = pci_find_parent_resource(bus->self, res); +			if (pr == res) { +				/* this happens when the generic PCI +				 * code (wrongly) decides that this +				 * bridge is transparent  -- paulus +				 */ +				continue; +			} +		} + +		pr_debug("PCI: %s (bus %d) bridge rsrc %d: %016llx-%016llx " +			 "[0x%x], parent %p (%s)\n", +			 bus->self ? pci_name(bus->self) : "PHB", +			 bus->number, i, +			 (unsigned long long)res->start, +			 (unsigned long long)res->end, +			 (unsigned int)res->flags, +			 pr, (pr && pr->name) ? pr->name : "nil"); + +		if (pr && !(pr->flags & IORESOURCE_UNSET)) { +			if (request_resource(pr, res) == 0) +				continue; +			/* +			 * Must be a conflict with an existing entry. +			 * Move that entry (or entries) under the +			 * bridge resource and try again. +			 */ +			if (reparent_resources(pr, res) == 0) +				continue; +		} +		printk(KERN_WARNING "PCI: Cannot allocate resource region " +		       "%d of PCI bridge %d, will remap\n", i, bus->number); +clear_resource: +		res->flags = 0; +	} + +	list_for_each_entry(b, &bus->children, node) +		pcibios_allocate_bus_resources(b); +} + +static inline void __devinit alloc_resource(struct pci_dev *dev, int idx) +{ +	struct resource *pr, *r = &dev->resource[idx]; + +	pr_debug("PCI: Allocating %s: Resource %d: %016llx..%016llx [%x]\n", +		 pci_name(dev), idx, +		 (unsigned long long)r->start, +		 (unsigned long long)r->end, +		 (unsigned int)r->flags); + +	pr = pci_find_parent_resource(dev, r); +	if (!pr || (pr->flags & IORESOURCE_UNSET) || +	    request_resource(pr, r) < 0) { +		printk(KERN_WARNING "PCI: Cannot allocate resource region %d" +		       " of device %s, will remap\n", idx, pci_name(dev)); +		if (pr) +			pr_debug("PCI:  parent is %p: %016llx-%016llx [%x]\n", +				 pr, +				 (unsigned long long)pr->start, +				 (unsigned long long)pr->end, +				 (unsigned int)pr->flags); +		/* We'll assign a new address later */ +		r->flags |= IORESOURCE_UNSET; +		r->end -= r->start; +		r->start = 0; +	} +} + +static void __init pcibios_allocate_resources(int pass) +{ +	struct pci_dev *dev = NULL; +	int idx, disabled; +	u16 command; +	struct resource *r; + +	for_each_pci_dev(dev) { +		pci_read_config_word(dev, PCI_COMMAND, &command); +		for (idx = 0; idx <= PCI_ROM_RESOURCE; idx++) { +			r = &dev->resource[idx]; +			if (r->parent)		/* Already allocated */ +				continue; +			if (!r->flags || (r->flags & IORESOURCE_UNSET)) +				continue;	/* Not assigned at all */ +			/* We only allocate ROMs on pass 1 just in case they +			 * have been screwed up by firmware +			 */ +			if (idx == PCI_ROM_RESOURCE) +				disabled = 1; +			if (r->flags & IORESOURCE_IO) +				disabled = !(command & PCI_COMMAND_IO); +			else +				disabled = !(command & PCI_COMMAND_MEMORY); +			if (pass == disabled) +				alloc_resource(dev, idx); +		} +		if (pass) +			continue; +		r = &dev->resource[PCI_ROM_RESOURCE]; +		if (r->flags) { +			/* Turn the ROM off, leave the resource region, +			 * but keep it unregistered. +			 */ +			u32 reg; +			pci_read_config_dword(dev, dev->rom_base_reg, ®); +			if (reg & PCI_ROM_ADDRESS_ENABLE) { +				pr_debug("PCI: Switching off ROM of %s\n", +					 pci_name(dev)); +				r->flags &= ~IORESOURCE_ROM_ENABLE; +				pci_write_config_dword(dev, dev->rom_base_reg, +						reg & ~PCI_ROM_ADDRESS_ENABLE); +			} +		} +	} +} + +static void __init pcibios_reserve_legacy_regions(struct pci_bus *bus) +{ +	struct pci_controller *hose = pci_bus_to_host(bus); +	resource_size_t	offset; +	struct resource *res, *pres; +	int i; + +	pr_debug("Reserving legacy ranges for domain %04x\n", +							pci_domain_nr(bus)); + +	/* Check for IO */ +	if (!(hose->io_resource.flags & IORESOURCE_IO)) +		goto no_io; +	offset = (unsigned long)hose->io_base_virt - _IO_BASE; +	res = kzalloc(sizeof(struct resource), GFP_KERNEL); +	BUG_ON(res == NULL); +	res->name = "Legacy IO"; +	res->flags = IORESOURCE_IO; +	res->start = offset; +	res->end = (offset + 0xfff) & 0xfffffffful; +	pr_debug("Candidate legacy IO: %pR\n", res); +	if (request_resource(&hose->io_resource, res)) { +		printk(KERN_DEBUG +		       "PCI %04x:%02x Cannot reserve Legacy IO %pR\n", +		       pci_domain_nr(bus), bus->number, res); +		kfree(res); +	} + + no_io: +	/* Check for memory */ +	offset = hose->pci_mem_offset; +	pr_debug("hose mem offset: %016llx\n", (unsigned long long)offset); +	for (i = 0; i < 3; i++) { +		pres = &hose->mem_resources[i]; +		if (!(pres->flags & IORESOURCE_MEM)) +			continue; +		pr_debug("hose mem res: %pR\n", pres); +		if ((pres->start - offset) <= 0xa0000 && +		    (pres->end - offset) >= 0xbffff) +			break; +	} +	if (i >= 3) +		return; +	res = kzalloc(sizeof(struct resource), GFP_KERNEL); +	BUG_ON(res == NULL); +	res->name = "Legacy VGA memory"; +	res->flags = IORESOURCE_MEM; +	res->start = 0xa0000 + offset; +	res->end = 0xbffff + offset; +	pr_debug("Candidate VGA memory: %pR\n", res); +	if (request_resource(pres, res)) { +		printk(KERN_DEBUG +		       "PCI %04x:%02x Cannot reserve VGA memory %pR\n", +		       pci_domain_nr(bus), bus->number, res); +		kfree(res); +	} +} + +void __init pcibios_resource_survey(void) +{ +	struct pci_bus *b; + +	/* Allocate and assign resources. If we re-assign everything, then +	 * we skip the allocate phase +	 */ +	list_for_each_entry(b, &pci_root_buses, node) +		pcibios_allocate_bus_resources(b); + +	if (!(pci_flags & PCI_REASSIGN_ALL_RSRC)) { +		pcibios_allocate_resources(0); +		pcibios_allocate_resources(1); +	} + +	/* Before we start assigning unassigned resource, we try to reserve +	 * the low IO area and the VGA memory area if they intersect the +	 * bus available resources to avoid allocating things on top of them +	 */ +	if (!(pci_flags & PCI_PROBE_ONLY)) { +		list_for_each_entry(b, &pci_root_buses, node) +			pcibios_reserve_legacy_regions(b); +	} + +	/* Now, if the platform didn't decide to blindly trust the firmware, +	 * we proceed to assigning things that were left unassigned +	 */ +	if (!(pci_flags & PCI_PROBE_ONLY)) { +		pr_debug("PCI: Assigning unassigned resources...\n"); +		pci_assign_unassigned_resources(); +	} +} + +#ifdef CONFIG_HOTPLUG + +/* This is used by the PCI hotplug driver to allocate resource + * of newly plugged busses. We can try to consolidate with the + * rest of the code later, for now, keep it as-is as our main + * resource allocation function doesn't deal with sub-trees yet. + */ +void __devinit pcibios_claim_one_bus(struct pci_bus *bus) +{ +	struct pci_dev *dev; +	struct pci_bus *child_bus; + +	list_for_each_entry(dev, &bus->devices, bus_list) { +		int i; + +		for (i = 0; i < PCI_NUM_RESOURCES; i++) { +			struct resource *r = &dev->resource[i]; + +			if (r->parent || !r->start || !r->flags) +				continue; + +			pr_debug("PCI: Claiming %s: " +				 "Resource %d: %016llx..%016llx [%x]\n", +				 pci_name(dev), i, +				 (unsigned long long)r->start, +				 (unsigned long long)r->end, +				 (unsigned int)r->flags); + +			pci_claim_resource(dev, i); +		} +	} + +	list_for_each_entry(child_bus, &bus->children, node) +		pcibios_claim_one_bus(child_bus); +} +EXPORT_SYMBOL_GPL(pcibios_claim_one_bus); + + +/* pcibios_finish_adding_to_bus + * + * This is to be called by the hotplug code after devices have been + * added to a bus, this include calling it for a PHB that is just + * being added + */ +void pcibios_finish_adding_to_bus(struct pci_bus *bus) +{ +	pr_debug("PCI: Finishing adding to hotplug bus %04x:%02x\n", +		 pci_domain_nr(bus), bus->number); + +	/* Allocate bus and devices resources */ +	pcibios_allocate_bus_resources(bus); +	pcibios_claim_one_bus(bus); + +	/* Add new devices to global lists.  Register in proc, sysfs. */ +	pci_bus_add_devices(bus); + +	/* Fixup EEH */ +	/* eeh_add_device_tree_late(bus); */ +} +EXPORT_SYMBOL_GPL(pcibios_finish_adding_to_bus); + +#endif /* CONFIG_HOTPLUG */ + +int pcibios_enable_device(struct pci_dev *dev, int mask) +{ +	return pci_enable_resources(dev, mask); +} + +void __devinit pcibios_setup_phb_resources(struct pci_controller *hose) +{ +	struct pci_bus *bus = hose->bus; +	struct resource *res; +	int i; + +	/* Hookup PHB IO resource */ +	bus->resource[0] = res = &hose->io_resource; + +	if (!res->flags) { +		printk(KERN_WARNING "PCI: I/O resource not set for host" +		       " bridge %s (domain %d)\n", +		       hose->dn->full_name, hose->global_number); +		/* Workaround for lack of IO resource only on 32-bit */ +		res->start = (unsigned long)hose->io_base_virt - isa_io_base; +		res->end = res->start + IO_SPACE_LIMIT; +		res->flags = IORESOURCE_IO; +	} + +	pr_debug("PCI: PHB IO resource    = %016llx-%016llx [%lx]\n", +		 (unsigned long long)res->start, +		 (unsigned long long)res->end, +		 (unsigned long)res->flags); + +	/* Hookup PHB Memory resources */ +	for (i = 0; i < 3; ++i) { +		res = &hose->mem_resources[i]; +		if (!res->flags) { +			if (i > 0) +				continue; +			printk(KERN_ERR "PCI: Memory resource 0 not set for " +			       "host bridge %s (domain %d)\n", +			       hose->dn->full_name, hose->global_number); + +			/* Workaround for lack of MEM resource only on 32-bit */ +			res->start = hose->pci_mem_offset; +			res->end = (resource_size_t)-1LL; +			res->flags = IORESOURCE_MEM; + +		} +		bus->resource[i+1] = res; + +		pr_debug("PCI: PHB MEM resource %d = %016llx-%016llx [%lx]\n", +			i, (unsigned long long)res->start, +			(unsigned long long)res->end, +			(unsigned long)res->flags); +	} + +	pr_debug("PCI: PHB MEM offset     = %016llx\n", +		 (unsigned long long)hose->pci_mem_offset); +	pr_debug("PCI: PHB IO  offset     = %08lx\n", +		 (unsigned long)hose->io_base_virt - _IO_BASE); +} + +/* + * Null PCI config access functions, for the case when we can't + * find a hose. + */ +#define NULL_PCI_OP(rw, size, type)					\ +static int								\ +null_##rw##_config_##size(struct pci_dev *dev, int offset, type val)	\ +{									\ +	return PCIBIOS_DEVICE_NOT_FOUND;				\ +} + +static int +null_read_config(struct pci_bus *bus, unsigned int devfn, int offset, +		 int len, u32 *val) +{ +	return PCIBIOS_DEVICE_NOT_FOUND; +} + +static int +null_write_config(struct pci_bus *bus, unsigned int devfn, int offset, +		  int len, u32 val) +{ +	return PCIBIOS_DEVICE_NOT_FOUND; +} + +static struct pci_ops null_pci_ops = { +	.read = null_read_config, +	.write = null_write_config, +}; + +/* + * These functions are used early on before PCI scanning is done + * and all of the pci_dev and pci_bus structures have been created. + */ +static struct pci_bus * +fake_pci_bus(struct pci_controller *hose, int busnr) +{ +	static struct pci_bus bus; + +	if (!hose) +		printk(KERN_ERR "Can't find hose for PCI bus %d!\n", busnr); + +	bus.number = busnr; +	bus.sysdata = hose; +	bus.ops = hose ? hose->ops : &null_pci_ops; +	return &bus; +} + +#define EARLY_PCI_OP(rw, size, type)					\ +int early_##rw##_config_##size(struct pci_controller *hose, int bus,	\ +			       int devfn, int offset, type value)	\ +{									\ +	return pci_bus_##rw##_config_##size(fake_pci_bus(hose, bus),	\ +					    devfn, offset, value);	\ +} + +EARLY_PCI_OP(read, byte, u8 *) +EARLY_PCI_OP(read, word, u16 *) +EARLY_PCI_OP(read, dword, u32 *) +EARLY_PCI_OP(write, byte, u8) +EARLY_PCI_OP(write, word, u16) +EARLY_PCI_OP(write, dword, u32) + +int early_find_capability(struct pci_controller *hose, int bus, int devfn, +			  int cap) +{ +	return pci_bus_find_capability(fake_pci_bus(hose, bus), devfn, cap); +} diff --git a/arch/microblaze/pci/pci_32.c b/arch/microblaze/pci/pci_32.c new file mode 100644 index 00000000000..3c3d808d7ce --- /dev/null +++ b/arch/microblaze/pci/pci_32.c @@ -0,0 +1,431 @@ +/* + * Common pmac/prep/chrp pci routines. -- Cort + */ + +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/string.h> +#include <linux/init.h> +#include <linux/capability.h> +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/bootmem.h> +#include <linux/irq.h> +#include <linux/list.h> +#include <linux/of.h> +#include <linux/slab.h> + +#include <asm/processor.h> +#include <asm/io.h> +#include <asm/prom.h> +#include <asm/sections.h> +#include <asm/pci-bridge.h> +#include <asm/byteorder.h> +#include <asm/uaccess.h> + +#undef DEBUG + +unsigned long isa_io_base; +unsigned long pci_dram_offset; +int pcibios_assign_bus_offset = 1; + +static u8 *pci_to_OF_bus_map; + +/* By default, we don't re-assign bus numbers. We do this only on + * some pmacs + */ +static int pci_assign_all_buses; + +static int pci_bus_count; + +/* + * Functions below are used on OpenFirmware machines. + */ +static void +make_one_node_map(struct device_node *node, u8 pci_bus) +{ +	const int *bus_range; +	int len; + +	if (pci_bus >= pci_bus_count) +		return; +	bus_range = of_get_property(node, "bus-range", &len); +	if (bus_range == NULL || len < 2 * sizeof(int)) { +		printk(KERN_WARNING "Can't get bus-range for %s, " +		       "assuming it starts at 0\n", node->full_name); +		pci_to_OF_bus_map[pci_bus] = 0; +	} else +		pci_to_OF_bus_map[pci_bus] = bus_range[0]; + +	for_each_child_of_node(node, node) { +		struct pci_dev *dev; +		const unsigned int *class_code, *reg; + +		class_code = of_get_property(node, "class-code", NULL); +		if (!class_code || +			((*class_code >> 8) != PCI_CLASS_BRIDGE_PCI && +			(*class_code >> 8) != PCI_CLASS_BRIDGE_CARDBUS)) +			continue; +		reg = of_get_property(node, "reg", NULL); +		if (!reg) +			continue; +		dev = pci_get_bus_and_slot(pci_bus, ((reg[0] >> 8) & 0xff)); +		if (!dev || !dev->subordinate) { +			pci_dev_put(dev); +			continue; +		} +		make_one_node_map(node, dev->subordinate->number); +		pci_dev_put(dev); +	} +} + +void +pcibios_make_OF_bus_map(void) +{ +	int i; +	struct pci_controller *hose, *tmp; +	struct property *map_prop; +	struct device_node *dn; + +	pci_to_OF_bus_map = kmalloc(pci_bus_count, GFP_KERNEL); +	if (!pci_to_OF_bus_map) { +		printk(KERN_ERR "Can't allocate OF bus map !\n"); +		return; +	} + +	/* We fill the bus map with invalid values, that helps +	 * debugging. +	 */ +	for (i = 0; i < pci_bus_count; i++) +		pci_to_OF_bus_map[i] = 0xff; + +	/* For each hose, we begin searching bridges */ +	list_for_each_entry_safe(hose, tmp, &hose_list, list_node) { +		struct device_node *node = hose->dn; + +		if (!node) +			continue; +		make_one_node_map(node, hose->first_busno); +	} +	dn = of_find_node_by_path("/"); +	map_prop = of_find_property(dn, "pci-OF-bus-map", NULL); +	if (map_prop) { +		BUG_ON(pci_bus_count > map_prop->length); +		memcpy(map_prop->value, pci_to_OF_bus_map, pci_bus_count); +	} +	of_node_put(dn); +#ifdef DEBUG +	printk(KERN_INFO "PCI->OF bus map:\n"); +	for (i = 0; i < pci_bus_count; i++) { +		if (pci_to_OF_bus_map[i] == 0xff) +			continue; +		printk(KERN_INFO "%d -> %d\n", i, pci_to_OF_bus_map[i]); +	} +#endif +} + +typedef int (*pci_OF_scan_iterator)(struct device_node *node, void *data); + +static struct device_node *scan_OF_pci_childs(struct device_node *parent, +					pci_OF_scan_iterator filter, void *data) +{ +	struct device_node *node; +	struct device_node *sub_node; + +	for_each_child_of_node(parent, node) { +		const unsigned int *class_code; + +		if (filter(node, data)) { +			of_node_put(node); +			return node; +		} + +		/* For PCI<->PCI bridges or CardBus bridges, we go down +		 * Note: some OFs create a parent node "multifunc-device" as +		 * a fake root for all functions of a multi-function device, +		 * we go down them as well. +		 */ +		class_code = of_get_property(node, "class-code", NULL); +		if ((!class_code || +			((*class_code >> 8) != PCI_CLASS_BRIDGE_PCI && +			(*class_code >> 8) != PCI_CLASS_BRIDGE_CARDBUS)) && +			strcmp(node->name, "multifunc-device")) +			continue; +		sub_node = scan_OF_pci_childs(node, filter, data); +		if (sub_node) { +			of_node_put(node); +			return sub_node; +		} +	} +	return NULL; +} + +static struct device_node *scan_OF_for_pci_dev(struct device_node *parent, +					       unsigned int devfn) +{ +	struct device_node *np, *cnp; +	const u32 *reg; +	unsigned int psize; + +	for_each_child_of_node(parent, np) { +		reg = of_get_property(np, "reg", &psize); +		if (reg && psize >= 4 && ((reg[0] >> 8) & 0xff) == devfn) +			return np; + +		/* Note: some OFs create a parent node "multifunc-device" as +		 * a fake root for all functions of a multi-function device, +		 * we go down them as well. */ +		if (!strcmp(np->name, "multifunc-device")) { +			cnp = scan_OF_for_pci_dev(np, devfn); +			if (cnp) +				return cnp; +		} +	} +	return NULL; +} + + +static struct device_node *scan_OF_for_pci_bus(struct pci_bus *bus) +{ +	struct device_node *parent, *np; + +	/* Are we a root bus ? */ +	if (bus->self == NULL || bus->parent == NULL) { +		struct pci_controller *hose = pci_bus_to_host(bus); +		if (hose == NULL) +			return NULL; +		return of_node_get(hose->dn); +	} + +	/* not a root bus, we need to get our parent */ +	parent = scan_OF_for_pci_bus(bus->parent); +	if (parent == NULL) +		return NULL; + +	/* now iterate for children for a match */ +	np = scan_OF_for_pci_dev(parent, bus->self->devfn); +	of_node_put(parent); + +	return np; +} + +/* + * Scans the OF tree for a device node matching a PCI device + */ +struct device_node * +pci_busdev_to_OF_node(struct pci_bus *bus, int devfn) +{ +	struct device_node *parent, *np; + +	pr_debug("pci_busdev_to_OF_node(%d,0x%x)\n", bus->number, devfn); +	parent = scan_OF_for_pci_bus(bus); +	if (parent == NULL) +		return NULL; +	pr_debug(" parent is %s\n", parent ? parent->full_name : "<NULL>"); +	np = scan_OF_for_pci_dev(parent, devfn); +	of_node_put(parent); +	pr_debug(" result is %s\n", np ? np->full_name : "<NULL>"); + +	/* XXX most callers don't release the returned node +	 * mostly because ppc64 doesn't increase the refcount, +	 * we need to fix that. +	 */ +	return np; +} +EXPORT_SYMBOL(pci_busdev_to_OF_node); + +struct device_node* +pci_device_to_OF_node(struct pci_dev *dev) +{ +	return pci_busdev_to_OF_node(dev->bus, dev->devfn); +} +EXPORT_SYMBOL(pci_device_to_OF_node); + +static int +find_OF_pci_device_filter(struct device_node *node, void *data) +{ +	return ((void *)node == data); +} + +/* + * Returns the PCI device matching a given OF node + */ +int +pci_device_from_OF_node(struct device_node *node, u8 *bus, u8 *devfn) +{ +	const unsigned int *reg; +	struct pci_controller *hose; +	struct pci_dev *dev = NULL; + +	/* Make sure it's really a PCI device */ +	hose = pci_find_hose_for_OF_device(node); +	if (!hose || !hose->dn) +		return -ENODEV; +	if (!scan_OF_pci_childs(hose->dn, +			find_OF_pci_device_filter, (void *)node)) +		return -ENODEV; +	reg = of_get_property(node, "reg", NULL); +	if (!reg) +		return -ENODEV; +	*bus = (reg[0] >> 16) & 0xff; +	*devfn = ((reg[0] >> 8) & 0xff); + +	/* Ok, here we need some tweak. If we have already renumbered +	 * all busses, we can't rely on the OF bus number any more. +	 * the pci_to_OF_bus_map is not enough as several PCI busses +	 * may match the same OF bus number. +	 */ +	if (!pci_to_OF_bus_map) +		return 0; + +	for_each_pci_dev(dev) +		if (pci_to_OF_bus_map[dev->bus->number] == *bus && +				dev->devfn == *devfn) { +			*bus = dev->bus->number; +			pci_dev_put(dev); +			return 0; +		} + +	return -ENODEV; +} +EXPORT_SYMBOL(pci_device_from_OF_node); + +/* We create the "pci-OF-bus-map" property now so it appears in the + * /proc device tree + */ +void __init +pci_create_OF_bus_map(void) +{ +	struct property *of_prop; +	struct device_node *dn; + +	of_prop = (struct property *) alloc_bootmem(sizeof(struct property) + \ +									 256); +	if (!of_prop) +		return; +	dn = of_find_node_by_path("/"); +	if (dn) { +		memset(of_prop, -1, sizeof(struct property) + 256); +		of_prop->name = "pci-OF-bus-map"; +		of_prop->length = 256; +		of_prop->value = &of_prop[1]; +		prom_add_property(dn, of_prop); +		of_node_put(dn); +	} +} + +static void __devinit pcibios_scan_phb(struct pci_controller *hose) +{ +	struct pci_bus *bus; +	struct device_node *node = hose->dn; +	unsigned long io_offset; +	struct resource *res = &hose->io_resource; + +	pr_debug("PCI: Scanning PHB %s\n", +		 node ? node->full_name : "<NO NAME>"); + +	/* Create an empty bus for the toplevel */ +	bus = pci_create_bus(hose->parent, hose->first_busno, hose->ops, hose); +	if (bus == NULL) { +		printk(KERN_ERR "Failed to create bus for PCI domain %04x\n", +		       hose->global_number); +		return; +	} +	bus->secondary = hose->first_busno; +	hose->bus = bus; + +	/* Fixup IO space offset */ +	io_offset = (unsigned long)hose->io_base_virt - isa_io_base; +	res->start = (res->start + io_offset) & 0xffffffffu; +	res->end = (res->end + io_offset) & 0xffffffffu; + +	/* Wire up PHB bus resources */ +	pcibios_setup_phb_resources(hose); + +	/* Scan children */ +	hose->last_busno = bus->subordinate = pci_scan_child_bus(bus); +} + +static int __init pcibios_init(void) +{ +	struct pci_controller *hose, *tmp; +	int next_busno = 0; + +	printk(KERN_INFO "PCI: Probing PCI hardware\n"); + +	if (pci_flags & PCI_REASSIGN_ALL_BUS) { +		printk(KERN_INFO "setting pci_asign_all_busses\n"); +		pci_assign_all_buses = 1; +	} + +	/* Scan all of the recorded PCI controllers.  */ +	list_for_each_entry_safe(hose, tmp, &hose_list, list_node) { +		if (pci_assign_all_buses) +			hose->first_busno = next_busno; +		hose->last_busno = 0xff; +		pcibios_scan_phb(hose); +		printk(KERN_INFO "calling pci_bus_add_devices()\n"); +		pci_bus_add_devices(hose->bus); +		if (pci_assign_all_buses || next_busno <= hose->last_busno) +			next_busno = hose->last_busno + \ +					pcibios_assign_bus_offset; +	} +	pci_bus_count = next_busno; + +	/* OpenFirmware based machines need a map of OF bus +	 * numbers vs. kernel bus numbers since we may have to +	 * remap them. +	 */ +	if (pci_assign_all_buses) +		pcibios_make_OF_bus_map(); + +	/* Call common code to handle resource allocation */ +	pcibios_resource_survey(); + +	return 0; +} + +subsys_initcall(pcibios_init); + +static struct pci_controller* +pci_bus_to_hose(int bus) +{ +	struct pci_controller *hose, *tmp; + +	list_for_each_entry_safe(hose, tmp, &hose_list, list_node) +		if (bus >= hose->first_busno && bus <= hose->last_busno) +			return hose; +	return NULL; +} + +/* Provide information on locations of various I/O regions in physical + * memory.  Do this on a per-card basis so that we choose the right + * root bridge. + * Note that the returned IO or memory base is a physical address + */ + +long sys_pciconfig_iobase(long which, unsigned long bus, unsigned long devfn) +{ +	struct pci_controller *hose; +	long result = -EOPNOTSUPP; + +	hose = pci_bus_to_hose(bus); +	if (!hose) +		return -ENODEV; + +	switch (which) { +	case IOBASE_BRIDGE_NUMBER: +		return (long)hose->first_busno; +	case IOBASE_MEMORY: +		return (long)hose->pci_mem_offset; +	case IOBASE_IO: +		return (long)hose->io_base_phys; +	case IOBASE_ISA_IO: +		return (long)isa_io_base; +	case IOBASE_ISA_MEM: +		return (long)isa_mem_base; +	} + +	return result; +} diff --git a/arch/microblaze/pci/xilinx_pci.c b/arch/microblaze/pci/xilinx_pci.c new file mode 100644 index 00000000000..7869a41b0f9 --- /dev/null +++ b/arch/microblaze/pci/xilinx_pci.c @@ -0,0 +1,168 @@ +/* + * PCI support for Xilinx plbv46_pci soft-core which can be used on + * Xilinx Virtex ML410 / ML510 boards. + * + * Copyright 2009 Roderick Colenbrander + * Copyright 2009 Secret Lab Technologies Ltd. + * + * The pci bridge fixup code was copied from ppc4xx_pci.c and was written + * by Benjamin Herrenschmidt. + * Copyright 2007 Ben. Herrenschmidt <benh@kernel.crashing.org>, IBM Corp. + * + * 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/ioport.h> +#include <linux/of.h> +#include <linux/pci.h> +#include <asm/io.h> + +#define XPLB_PCI_ADDR 0x10c +#define XPLB_PCI_DATA 0x110 +#define XPLB_PCI_BUS  0x114 + +#define PCI_HOST_ENABLE_CMD (PCI_COMMAND_SERR | PCI_COMMAND_PARITY | \ +				PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY) + +static struct of_device_id xilinx_pci_match[] = { +	{ .compatible = "xlnx,plbv46-pci-1.03.a", }, +	{} +}; + +/** + * xilinx_pci_fixup_bridge - Block Xilinx PHB configuration. + */ +static void xilinx_pci_fixup_bridge(struct pci_dev *dev) +{ +	struct pci_controller *hose; +	int i; + +	if (dev->devfn || dev->bus->self) +		return; + +	hose = pci_bus_to_host(dev->bus); +	if (!hose) +		return; + +	if (!of_match_node(xilinx_pci_match, hose->dn)) +		return; + +	/* Hide the PCI host BARs from the kernel as their content doesn't +	 * fit well in the resource management +	 */ +	for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { +		dev->resource[i].start = 0; +		dev->resource[i].end = 0; +		dev->resource[i].flags = 0; +	} + +	dev_info(&dev->dev, "Hiding Xilinx plb-pci host bridge resources %s\n", +		 pci_name(dev)); +} +DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, xilinx_pci_fixup_bridge); + +#ifdef DEBUG +/** + * xilinx_pci_exclude_device - Don't do config access for non-root bus + * + * This is a hack.  Config access to any bus other than bus 0 does not + * currently work on the ML510 so we prevent it here. + */ +static int +xilinx_pci_exclude_device(struct pci_controller *hose, u_char bus, u8 devfn) +{ +	return (bus != 0); +} + +/** + * xilinx_early_pci_scan - List pci config space for available devices + * + * List pci devices in very early phase. + */ +void __init xilinx_early_pci_scan(struct pci_controller *hose) +{ +	u32 bus = 0; +	u32 val, dev, func, offset; + +	/* Currently we have only 2 device connected - up-to 32 devices */ +	for (dev = 0; dev < 2; dev++) { +		/* List only first function number - up-to 8 functions */ +		for (func = 0; func < 1; func++) { +			printk(KERN_INFO "%02x:%02x:%02x", bus, dev, func); +			/* read the first 64 standardized bytes */ +			/* Up-to 192 bytes can be list of capabilities */ +			for (offset = 0; offset < 64; offset += 4) { +				early_read_config_dword(hose, bus, +					PCI_DEVFN(dev, func), offset, &val); +				if (offset == 0 && val == 0xFFFFFFFF) { +					printk(KERN_CONT "\nABSENT"); +					break; +				} +				if (!(offset % 0x10)) +					printk(KERN_CONT "\n%04x:    ", offset); + +				printk(KERN_CONT "%08x  ", val); +			} +			printk(KERN_INFO "\n"); +		} +	} +} +#else +void __init xilinx_early_pci_scan(struct pci_controller *hose) +{ +} +#endif + +/** + * xilinx_pci_init - Find and register a Xilinx PCI host bridge + */ +void __init xilinx_pci_init(void) +{ +	struct pci_controller *hose; +	struct resource r; +	void __iomem *pci_reg; +	struct device_node *pci_node; + +	pci_node = of_find_matching_node(NULL, xilinx_pci_match); +	if (!pci_node) +		return; + +	if (of_address_to_resource(pci_node, 0, &r)) { +		pr_err("xilinx-pci: cannot resolve base address\n"); +		return; +	} + +	hose = pcibios_alloc_controller(pci_node); +	if (!hose) { +		pr_err("xilinx-pci: pcibios_alloc_controller() failed\n"); +		return; +	} + +	/* Setup config space */ +	setup_indirect_pci(hose, r.start + XPLB_PCI_ADDR, +			   r.start + XPLB_PCI_DATA, +			   INDIRECT_TYPE_SET_CFG_TYPE); + +	/* According to the xilinx plbv46_pci documentation the soft-core starts +	 * a self-init when the bus master enable bit is set. Without this bit +	 * set the pci bus can't be scanned. +	 */ +	early_write_config_word(hose, 0, 0, PCI_COMMAND, PCI_HOST_ENABLE_CMD); + +	/* Set the max latency timer to 255 */ +	early_write_config_byte(hose, 0, 0, PCI_LATENCY_TIMER, 0xff); + +	/* Set the max bus number to 255, and bus/subbus no's to 0 */ +	pci_reg = of_iomap(pci_node, 0); +	out_be32(pci_reg + XPLB_PCI_BUS, 0x000000ff); +	iounmap(pci_reg); + +	/* Register the host bridge with the linux kernel! */ +	pci_process_bridge_OF_ranges(hose, pci_node, +					INDIRECT_TYPE_SET_CFG_TYPE); + +	pr_info("xilinx-pci: Registered PCI host bridge\n"); +	xilinx_early_pci_scan(hose); +}  |