diff options
72 files changed, 11233 insertions, 399 deletions
diff --git a/arch/tile/Kconfig b/arch/tile/Kconfig index fe128816c44..932e4430f7f 100644 --- a/arch/tile/Kconfig +++ b/arch/tile/Kconfig @@ -3,6 +3,8 @@  config TILE  	def_bool y +	select HAVE_DMA_ATTRS +	select HAVE_DMA_API_DEBUG  	select HAVE_KVM if !TILEGX  	select GENERIC_FIND_FIRST_BIT  	select USE_GENERIC_SMP_HELPERS @@ -79,6 +81,9 @@ config ARCH_DMA_ADDR_T_64BIT  config NEED_DMA_MAP_STATE  	def_bool y +config ARCH_HAS_DMA_SET_COHERENT_MASK +	bool +  config LOCKDEP_SUPPORT  	def_bool y @@ -212,6 +217,22 @@ config HIGHMEM  	  If unsure, say "true". +config ZONE_DMA +	def_bool y + +config IOMMU_HELPER +	bool + +config NEED_SG_DMA_LENGTH +	bool + +config SWIOTLB +	bool +	default TILEGX +	select IOMMU_HELPER +	select NEED_SG_DMA_LENGTH +	select ARCH_HAS_DMA_SET_COHERENT_MASK +  # We do not currently support disabling NUMA.  config NUMA  	bool # "NUMA Memory Allocation and Scheduler Support" @@ -345,6 +366,8 @@ config KERNEL_PL  	  kernel will be built to run at.  Generally you should use  	  the default value here. +source "arch/tile/gxio/Kconfig" +  endmenu  # Tilera-specific configuration  menu "Bus options" @@ -354,6 +377,9 @@ config PCI  	default y  	select PCI_DOMAINS  	select GENERIC_PCI_IOMAP +	select TILE_GXIO_TRIO if TILEGX +	select ARCH_SUPPORTS_MSI if TILEGX +	select PCI_MSI if TILEGX  	---help---  	  Enable PCI root complex support, so PCIe endpoint devices can  	  be attached to the Tile chip.  Many, but not all, PCI devices @@ -370,6 +396,22 @@ config NO_IOPORT  source "drivers/pci/Kconfig" +config TILE_USB +	tristate "Tilera USB host adapter support" +	default y +	depends on USB +	depends on TILEGX +	select TILE_GXIO_USB_HOST +	---help--- +	  Provides USB host adapter support for the built-in EHCI and OHCI +	  interfaces on TILE-Gx chips. + +# USB OHCI needs the bounce pool since tilegx will often have more +# than 4GB of memory, but we don't currently use the IOTLB to present +# a 32-bit address to OHCI.  So we need to use a bounce pool instead. +config NEED_BOUNCE_POOL +	def_bool USB_OHCI_HCD +  config HOTPLUG  	bool "Support for hot-pluggable devices"  	---help--- diff --git a/arch/tile/Makefile b/arch/tile/Makefile index e20b0a0b64a..55640cf9259 100644 --- a/arch/tile/Makefile +++ b/arch/tile/Makefile @@ -59,6 +59,8 @@ libs-y		+= $(LIBGCC_PATH)  # See arch/tile/Kbuild for content of core part of the kernel  core-y		+= arch/tile/ +core-$(CONFIG_TILE_GXIO) += arch/tile/gxio/ +  ifdef TILERA_ROOT  INSTALL_PATH ?= $(TILERA_ROOT)/tile/boot  endif diff --git a/arch/tile/gxio/Kconfig b/arch/tile/gxio/Kconfig new file mode 100644 index 00000000000..d221f8d6de8 --- /dev/null +++ b/arch/tile/gxio/Kconfig @@ -0,0 +1,28 @@ +# Support direct access to TILE-Gx hardware from user space, via the +# gxio library, or from kernel space, via kernel IORPC support. +config TILE_GXIO +	bool +	depends on TILEGX + +# Support direct access to the common I/O DMA facility within the +# TILE-Gx mPIPE and Trio hardware from kernel space. +config TILE_GXIO_DMA +	bool +	select TILE_GXIO + +# Support direct access to the TILE-Gx mPIPE hardware from kernel space. +config TILE_GXIO_MPIPE +	bool +	select TILE_GXIO +	select TILE_GXIO_DMA + +# Support direct access to the TILE-Gx TRIO hardware from kernel space. +config TILE_GXIO_TRIO +	bool +	select TILE_GXIO +	select TILE_GXIO_DMA + +# Support direct access to the TILE-Gx USB hardware from kernel space. +config TILE_GXIO_USB_HOST +	bool +	select TILE_GXIO diff --git a/arch/tile/gxio/Makefile b/arch/tile/gxio/Makefile new file mode 100644 index 00000000000..8684bcaa74e --- /dev/null +++ b/arch/tile/gxio/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for the Tile-Gx device access support. +# + +obj-$(CONFIG_TILE_GXIO) += iorpc_globals.o kiorpc.o +obj-$(CONFIG_TILE_GXIO_DMA) += dma_queue.o +obj-$(CONFIG_TILE_GXIO_MPIPE) += mpipe.o iorpc_mpipe.o iorpc_mpipe_info.o +obj-$(CONFIG_TILE_GXIO_TRIO) += trio.o iorpc_trio.o +obj-$(CONFIG_TILE_GXIO_USB_HOST) += usb_host.o iorpc_usb_host.o diff --git a/arch/tile/gxio/dma_queue.c b/arch/tile/gxio/dma_queue.c new file mode 100644 index 00000000000..baa60357f8b --- /dev/null +++ b/arch/tile/gxio/dma_queue.c @@ -0,0 +1,176 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ + +#include <linux/io.h> +#include <linux/atomic.h> +#include <linux/module.h> +#include <gxio/dma_queue.h> + +/* Wait for a memory read to complete. */ +#define wait_for_value(val)                             \ +  __asm__ __volatile__("move %0, %0" :: "r"(val)) + +/* The index is in the low 16. */ +#define DMA_QUEUE_INDEX_MASK ((1 << 16) - 1) + +/* + * The hardware descriptor-ring type. + * This matches the types used by mpipe (MPIPE_EDMA_POST_REGION_VAL_t) + * and trio (TRIO_PUSH_DMA_REGION_VAL_t or TRIO_PULL_DMA_REGION_VAL_t). + * See those types for more documentation on the individual fields. + */ +typedef union { +	struct { +#ifndef __BIG_ENDIAN__ +		uint64_t ring_idx:16; +		uint64_t count:16; +		uint64_t gen:1; +		uint64_t __reserved:31; +#else +		uint64_t __reserved:31; +		uint64_t gen:1; +		uint64_t count:16; +		uint64_t ring_idx:16; +#endif +	}; +	uint64_t word; +} __gxio_ring_t; + +void __gxio_dma_queue_init(__gxio_dma_queue_t *dma_queue, +			   void *post_region_addr, unsigned int num_entries) +{ +	/* +	 * Limit 65536 entry rings to 65535 credits because we only have a +	 * 16 bit completion counter. +	 */ +	int64_t credits = (num_entries < 65536) ? num_entries : 65535; + +	memset(dma_queue, 0, sizeof(*dma_queue)); + +	dma_queue->post_region_addr = post_region_addr; +	dma_queue->hw_complete_count = 0; +	dma_queue->credits_and_next_index = credits << DMA_QUEUE_CREDIT_SHIFT; +} + +EXPORT_SYMBOL_GPL(__gxio_dma_queue_init); + +void __gxio_dma_queue_update_credits(__gxio_dma_queue_t *dma_queue) +{ +	__gxio_ring_t val; +	uint64_t count; +	uint64_t delta; +	uint64_t new_count; + +	/* +	 * Read the 64-bit completion count without touching the cache, so +	 * we later avoid having to evict any sharers of this cache line +	 * when we update it below. +	 */ +	uint64_t orig_hw_complete_count = +		cmpxchg(&dma_queue->hw_complete_count, +			-1, -1); + +	/* Make sure the load completes before we access the hardware. */ +	wait_for_value(orig_hw_complete_count); + +	/* Read the 16-bit count of how many packets it has completed. */ +	val.word = __gxio_mmio_read(dma_queue->post_region_addr); +	count = val.count; + +	/* +	 * Calculate the number of completions since we last updated the +	 * 64-bit counter.  It's safe to ignore the high bits because the +	 * maximum credit value is 65535. +	 */ +	delta = (count - orig_hw_complete_count) & 0xffff; +	if (delta == 0) +		return; + +	/* +	 * Try to write back the count, advanced by delta.  If we race with +	 * another thread, this might fail, in which case we return +	 * immediately on the assumption that some credits are (or at least +	 * were) available. +	 */ +	new_count = orig_hw_complete_count + delta; +	if (cmpxchg(&dma_queue->hw_complete_count, +		    orig_hw_complete_count, +		    new_count) != orig_hw_complete_count) +		return; + +	/* +	 * We succeeded in advancing the completion count; add back the +	 * corresponding number of egress credits. +	 */ +	__insn_fetchadd(&dma_queue->credits_and_next_index, +			(delta << DMA_QUEUE_CREDIT_SHIFT)); +} + +EXPORT_SYMBOL_GPL(__gxio_dma_queue_update_credits); + +/* + * A separate 'blocked' method for put() so that backtraces and + * profiles will clearly indicate that we're wasting time spinning on + * egress availability rather than actually posting commands. + */ +int64_t __gxio_dma_queue_wait_for_credits(__gxio_dma_queue_t *dma_queue, +					  int64_t modifier) +{ +	int backoff = 16; +	int64_t old; + +	do { +		int i; +		/* Back off to avoid spamming memory networks. */ +		for (i = backoff; i > 0; i--) +			__insn_mfspr(SPR_PASS); + +		/* Check credits again. */ +		__gxio_dma_queue_update_credits(dma_queue); +		old = __insn_fetchaddgez(&dma_queue->credits_and_next_index, +					 modifier); + +		/* Calculate bounded exponential backoff for next iteration. */ +		if (backoff < 256) +			backoff *= 2; +	} while (old + modifier < 0); + +	return old; +} + +EXPORT_SYMBOL_GPL(__gxio_dma_queue_wait_for_credits); + +int64_t __gxio_dma_queue_reserve_aux(__gxio_dma_queue_t *dma_queue, +				     unsigned int num, int wait) +{ +	return __gxio_dma_queue_reserve(dma_queue, num, wait != 0, true); +} + +EXPORT_SYMBOL_GPL(__gxio_dma_queue_reserve_aux); + +int __gxio_dma_queue_is_complete(__gxio_dma_queue_t *dma_queue, +				 int64_t completion_slot, int update) +{ +	if (update) { +		if (ACCESS_ONCE(dma_queue->hw_complete_count) > +		    completion_slot) +			return 1; + +		__gxio_dma_queue_update_credits(dma_queue); +	} + +	return ACCESS_ONCE(dma_queue->hw_complete_count) > completion_slot; +} + +EXPORT_SYMBOL_GPL(__gxio_dma_queue_is_complete); diff --git a/arch/tile/gxio/iorpc_globals.c b/arch/tile/gxio/iorpc_globals.c new file mode 100644 index 00000000000..e178e90805a --- /dev/null +++ b/arch/tile/gxio/iorpc_globals.c @@ -0,0 +1,89 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ + +/* This file is machine-generated; DO NOT EDIT! */ +#include "gxio/iorpc_globals.h" + +struct arm_pollfd_param { +	union iorpc_pollfd pollfd; +}; + +int __iorpc_arm_pollfd(int fd, int pollfd_cookie) +{ +	struct arm_pollfd_param temp; +	struct arm_pollfd_param *params = &temp; + +	params->pollfd.kernel.cookie = pollfd_cookie; + +	return hv_dev_pwrite(fd, 0, (HV_VirtAddr) params, sizeof(*params), +			     IORPC_OP_ARM_POLLFD); +} + +EXPORT_SYMBOL(__iorpc_arm_pollfd); + +struct close_pollfd_param { +	union iorpc_pollfd pollfd; +}; + +int __iorpc_close_pollfd(int fd, int pollfd_cookie) +{ +	struct close_pollfd_param temp; +	struct close_pollfd_param *params = &temp; + +	params->pollfd.kernel.cookie = pollfd_cookie; + +	return hv_dev_pwrite(fd, 0, (HV_VirtAddr) params, sizeof(*params), +			     IORPC_OP_CLOSE_POLLFD); +} + +EXPORT_SYMBOL(__iorpc_close_pollfd); + +struct get_mmio_base_param { +	HV_PTE base; +}; + +int __iorpc_get_mmio_base(int fd, HV_PTE *base) +{ +	int __result; +	struct get_mmio_base_param temp; +	struct get_mmio_base_param *params = &temp; + +	__result = +	    hv_dev_pread(fd, 0, (HV_VirtAddr) params, sizeof(*params), +			 IORPC_OP_GET_MMIO_BASE); +	*base = params->base; + +	return __result; +} + +EXPORT_SYMBOL(__iorpc_get_mmio_base); + +struct check_mmio_offset_param { +	unsigned long offset; +	unsigned long size; +}; + +int __iorpc_check_mmio_offset(int fd, unsigned long offset, unsigned long size) +{ +	struct check_mmio_offset_param temp; +	struct check_mmio_offset_param *params = &temp; + +	params->offset = offset; +	params->size = size; + +	return hv_dev_pwrite(fd, 0, (HV_VirtAddr) params, sizeof(*params), +			     IORPC_OP_CHECK_MMIO_OFFSET); +} + +EXPORT_SYMBOL(__iorpc_check_mmio_offset); diff --git a/arch/tile/gxio/iorpc_mpipe.c b/arch/tile/gxio/iorpc_mpipe.c new file mode 100644 index 00000000000..31b87bf8c02 --- /dev/null +++ b/arch/tile/gxio/iorpc_mpipe.c @@ -0,0 +1,529 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ + +/* This file is machine-generated; DO NOT EDIT! */ +#include "gxio/iorpc_mpipe.h" + +struct alloc_buffer_stacks_param { +	unsigned int count; +	unsigned int first; +	unsigned int flags; +}; + +int gxio_mpipe_alloc_buffer_stacks(gxio_mpipe_context_t * context, +				   unsigned int count, unsigned int first, +				   unsigned int flags) +{ +	struct alloc_buffer_stacks_param temp; +	struct alloc_buffer_stacks_param *params = &temp; + +	params->count = count; +	params->first = first; +	params->flags = flags; + +	return hv_dev_pwrite(context->fd, 0, (HV_VirtAddr) params, +			     sizeof(*params), +			     GXIO_MPIPE_OP_ALLOC_BUFFER_STACKS); +} + +EXPORT_SYMBOL(gxio_mpipe_alloc_buffer_stacks); + +struct init_buffer_stack_aux_param { +	union iorpc_mem_buffer buffer; +	unsigned int stack; +	unsigned int buffer_size_enum; +}; + +int gxio_mpipe_init_buffer_stack_aux(gxio_mpipe_context_t * context, +				     void *mem_va, size_t mem_size, +				     unsigned int mem_flags, unsigned int stack, +				     unsigned int buffer_size_enum) +{ +	int __result; +	unsigned long long __cpa; +	pte_t __pte; +	struct init_buffer_stack_aux_param temp; +	struct init_buffer_stack_aux_param *params = &temp; + +	__result = va_to_cpa_and_pte(mem_va, &__cpa, &__pte); +	if (__result != 0) +		return __result; +	params->buffer.kernel.cpa = __cpa; +	params->buffer.kernel.size = mem_size; +	params->buffer.kernel.pte = __pte; +	params->buffer.kernel.flags = mem_flags; +	params->stack = stack; +	params->buffer_size_enum = buffer_size_enum; + +	return hv_dev_pwrite(context->fd, 0, (HV_VirtAddr) params, +			     sizeof(*params), +			     GXIO_MPIPE_OP_INIT_BUFFER_STACK_AUX); +} + +EXPORT_SYMBOL(gxio_mpipe_init_buffer_stack_aux); + + +struct alloc_notif_rings_param { +	unsigned int count; +	unsigned int first; +	unsigned int flags; +}; + +int gxio_mpipe_alloc_notif_rings(gxio_mpipe_context_t * context, +				 unsigned int count, unsigned int first, +				 unsigned int flags) +{ +	struct alloc_notif_rings_param temp; +	struct alloc_notif_rings_param *params = &temp; + +	params->count = count; +	params->first = first; +	params->flags = flags; + +	return hv_dev_pwrite(context->fd, 0, (HV_VirtAddr) params, +			     sizeof(*params), GXIO_MPIPE_OP_ALLOC_NOTIF_RINGS); +} + +EXPORT_SYMBOL(gxio_mpipe_alloc_notif_rings); + +struct init_notif_ring_aux_param { +	union iorpc_mem_buffer buffer; +	unsigned int ring; +}; + +int gxio_mpipe_init_notif_ring_aux(gxio_mpipe_context_t * context, void *mem_va, +				   size_t mem_size, unsigned int mem_flags, +				   unsigned int ring) +{ +	int __result; +	unsigned long long __cpa; +	pte_t __pte; +	struct init_notif_ring_aux_param temp; +	struct init_notif_ring_aux_param *params = &temp; + +	__result = va_to_cpa_and_pte(mem_va, &__cpa, &__pte); +	if (__result != 0) +		return __result; +	params->buffer.kernel.cpa = __cpa; +	params->buffer.kernel.size = mem_size; +	params->buffer.kernel.pte = __pte; +	params->buffer.kernel.flags = mem_flags; +	params->ring = ring; + +	return hv_dev_pwrite(context->fd, 0, (HV_VirtAddr) params, +			     sizeof(*params), +			     GXIO_MPIPE_OP_INIT_NOTIF_RING_AUX); +} + +EXPORT_SYMBOL(gxio_mpipe_init_notif_ring_aux); + +struct request_notif_ring_interrupt_param { +	union iorpc_interrupt interrupt; +	unsigned int ring; +}; + +int gxio_mpipe_request_notif_ring_interrupt(gxio_mpipe_context_t * context, +					    int inter_x, int inter_y, +					    int inter_ipi, int inter_event, +					    unsigned int ring) +{ +	struct request_notif_ring_interrupt_param temp; +	struct request_notif_ring_interrupt_param *params = &temp; + +	params->interrupt.kernel.x = inter_x; +	params->interrupt.kernel.y = inter_y; +	params->interrupt.kernel.ipi = inter_ipi; +	params->interrupt.kernel.event = inter_event; +	params->ring = ring; + +	return hv_dev_pwrite(context->fd, 0, (HV_VirtAddr) params, +			     sizeof(*params), +			     GXIO_MPIPE_OP_REQUEST_NOTIF_RING_INTERRUPT); +} + +EXPORT_SYMBOL(gxio_mpipe_request_notif_ring_interrupt); + +struct enable_notif_ring_interrupt_param { +	unsigned int ring; +}; + +int gxio_mpipe_enable_notif_ring_interrupt(gxio_mpipe_context_t * context, +					   unsigned int ring) +{ +	struct enable_notif_ring_interrupt_param temp; +	struct enable_notif_ring_interrupt_param *params = &temp; + +	params->ring = ring; + +	return hv_dev_pwrite(context->fd, 0, (HV_VirtAddr) params, +			     sizeof(*params), +			     GXIO_MPIPE_OP_ENABLE_NOTIF_RING_INTERRUPT); +} + +EXPORT_SYMBOL(gxio_mpipe_enable_notif_ring_interrupt); + +struct alloc_notif_groups_param { +	unsigned int count; +	unsigned int first; +	unsigned int flags; +}; + +int gxio_mpipe_alloc_notif_groups(gxio_mpipe_context_t * context, +				  unsigned int count, unsigned int first, +				  unsigned int flags) +{ +	struct alloc_notif_groups_param temp; +	struct alloc_notif_groups_param *params = &temp; + +	params->count = count; +	params->first = first; +	params->flags = flags; + +	return hv_dev_pwrite(context->fd, 0, (HV_VirtAddr) params, +			     sizeof(*params), GXIO_MPIPE_OP_ALLOC_NOTIF_GROUPS); +} + +EXPORT_SYMBOL(gxio_mpipe_alloc_notif_groups); + +struct init_notif_group_param { +	unsigned int group; +	gxio_mpipe_notif_group_bits_t bits; +}; + +int gxio_mpipe_init_notif_group(gxio_mpipe_context_t * context, +				unsigned int group, +				gxio_mpipe_notif_group_bits_t bits) +{ +	struct init_notif_group_param temp; +	struct init_notif_group_param *params = &temp; + +	params->group = group; +	params->bits = bits; + +	return hv_dev_pwrite(context->fd, 0, (HV_VirtAddr) params, +			     sizeof(*params), GXIO_MPIPE_OP_INIT_NOTIF_GROUP); +} + +EXPORT_SYMBOL(gxio_mpipe_init_notif_group); + +struct alloc_buckets_param { +	unsigned int count; +	unsigned int first; +	unsigned int flags; +}; + +int gxio_mpipe_alloc_buckets(gxio_mpipe_context_t * context, unsigned int count, +			     unsigned int first, unsigned int flags) +{ +	struct alloc_buckets_param temp; +	struct alloc_buckets_param *params = &temp; + +	params->count = count; +	params->first = first; +	params->flags = flags; + +	return hv_dev_pwrite(context->fd, 0, (HV_VirtAddr) params, +			     sizeof(*params), GXIO_MPIPE_OP_ALLOC_BUCKETS); +} + +EXPORT_SYMBOL(gxio_mpipe_alloc_buckets); + +struct init_bucket_param { +	unsigned int bucket; +	MPIPE_LBL_INIT_DAT_BSTS_TBL_t bucket_info; +}; + +int gxio_mpipe_init_bucket(gxio_mpipe_context_t * context, unsigned int bucket, +			   MPIPE_LBL_INIT_DAT_BSTS_TBL_t bucket_info) +{ +	struct init_bucket_param temp; +	struct init_bucket_param *params = &temp; + +	params->bucket = bucket; +	params->bucket_info = bucket_info; + +	return hv_dev_pwrite(context->fd, 0, (HV_VirtAddr) params, +			     sizeof(*params), GXIO_MPIPE_OP_INIT_BUCKET); +} + +EXPORT_SYMBOL(gxio_mpipe_init_bucket); + +struct alloc_edma_rings_param { +	unsigned int count; +	unsigned int first; +	unsigned int flags; +}; + +int gxio_mpipe_alloc_edma_rings(gxio_mpipe_context_t * context, +				unsigned int count, unsigned int first, +				unsigned int flags) +{ +	struct alloc_edma_rings_param temp; +	struct alloc_edma_rings_param *params = &temp; + +	params->count = count; +	params->first = first; +	params->flags = flags; + +	return hv_dev_pwrite(context->fd, 0, (HV_VirtAddr) params, +			     sizeof(*params), GXIO_MPIPE_OP_ALLOC_EDMA_RINGS); +} + +EXPORT_SYMBOL(gxio_mpipe_alloc_edma_rings); + +struct init_edma_ring_aux_param { +	union iorpc_mem_buffer buffer; +	unsigned int ring; +	unsigned int channel; +}; + +int gxio_mpipe_init_edma_ring_aux(gxio_mpipe_context_t * context, void *mem_va, +				  size_t mem_size, unsigned int mem_flags, +				  unsigned int ring, unsigned int channel) +{ +	int __result; +	unsigned long long __cpa; +	pte_t __pte; +	struct init_edma_ring_aux_param temp; +	struct init_edma_ring_aux_param *params = &temp; + +	__result = va_to_cpa_and_pte(mem_va, &__cpa, &__pte); +	if (__result != 0) +		return __result; +	params->buffer.kernel.cpa = __cpa; +	params->buffer.kernel.size = mem_size; +	params->buffer.kernel.pte = __pte; +	params->buffer.kernel.flags = mem_flags; +	params->ring = ring; +	params->channel = channel; + +	return hv_dev_pwrite(context->fd, 0, (HV_VirtAddr) params, +			     sizeof(*params), GXIO_MPIPE_OP_INIT_EDMA_RING_AUX); +} + +EXPORT_SYMBOL(gxio_mpipe_init_edma_ring_aux); + + +int gxio_mpipe_commit_rules(gxio_mpipe_context_t * context, const void *blob, +			    size_t blob_size) +{ +	const void *params = blob; + +	return hv_dev_pwrite(context->fd, 0, (HV_VirtAddr) params, blob_size, +			     GXIO_MPIPE_OP_COMMIT_RULES); +} + +EXPORT_SYMBOL(gxio_mpipe_commit_rules); + +struct register_client_memory_param { +	unsigned int iotlb; +	HV_PTE pte; +	unsigned int flags; +}; + +int gxio_mpipe_register_client_memory(gxio_mpipe_context_t * context, +				      unsigned int iotlb, HV_PTE pte, +				      unsigned int flags) +{ +	struct register_client_memory_param temp; +	struct register_client_memory_param *params = &temp; + +	params->iotlb = iotlb; +	params->pte = pte; +	params->flags = flags; + +	return hv_dev_pwrite(context->fd, 0, (HV_VirtAddr) params, +			     sizeof(*params), +			     GXIO_MPIPE_OP_REGISTER_CLIENT_MEMORY); +} + +EXPORT_SYMBOL(gxio_mpipe_register_client_memory); + +struct link_open_aux_param { +	_gxio_mpipe_link_name_t name; +	unsigned int flags; +}; + +int gxio_mpipe_link_open_aux(gxio_mpipe_context_t * context, +			     _gxio_mpipe_link_name_t name, unsigned int flags) +{ +	struct link_open_aux_param temp; +	struct link_open_aux_param *params = &temp; + +	params->name = name; +	params->flags = flags; + +	return hv_dev_pwrite(context->fd, 0, (HV_VirtAddr) params, +			     sizeof(*params), GXIO_MPIPE_OP_LINK_OPEN_AUX); +} + +EXPORT_SYMBOL(gxio_mpipe_link_open_aux); + +struct link_close_aux_param { +	int mac; +}; + +int gxio_mpipe_link_close_aux(gxio_mpipe_context_t * context, int mac) +{ +	struct link_close_aux_param temp; +	struct link_close_aux_param *params = &temp; + +	params->mac = mac; + +	return hv_dev_pwrite(context->fd, 0, (HV_VirtAddr) params, +			     sizeof(*params), GXIO_MPIPE_OP_LINK_CLOSE_AUX); +} + +EXPORT_SYMBOL(gxio_mpipe_link_close_aux); + + +struct get_timestamp_aux_param { +	uint64_t sec; +	uint64_t nsec; +	uint64_t cycles; +}; + +int gxio_mpipe_get_timestamp_aux(gxio_mpipe_context_t * context, uint64_t * sec, +				 uint64_t * nsec, uint64_t * cycles) +{ +	int __result; +	struct get_timestamp_aux_param temp; +	struct get_timestamp_aux_param *params = &temp; + +	__result = +	    hv_dev_pread(context->fd, 0, (HV_VirtAddr) params, sizeof(*params), +			 GXIO_MPIPE_OP_GET_TIMESTAMP_AUX); +	*sec = params->sec; +	*nsec = params->nsec; +	*cycles = params->cycles; + +	return __result; +} + +EXPORT_SYMBOL(gxio_mpipe_get_timestamp_aux); + +struct set_timestamp_aux_param { +	uint64_t sec; +	uint64_t nsec; +	uint64_t cycles; +}; + +int gxio_mpipe_set_timestamp_aux(gxio_mpipe_context_t * context, uint64_t sec, +				 uint64_t nsec, uint64_t cycles) +{ +	struct set_timestamp_aux_param temp; +	struct set_timestamp_aux_param *params = &temp; + +	params->sec = sec; +	params->nsec = nsec; +	params->cycles = cycles; + +	return hv_dev_pwrite(context->fd, 0, (HV_VirtAddr) params, +			     sizeof(*params), GXIO_MPIPE_OP_SET_TIMESTAMP_AUX); +} + +EXPORT_SYMBOL(gxio_mpipe_set_timestamp_aux); + +struct adjust_timestamp_aux_param { +	int64_t nsec; +}; + +int gxio_mpipe_adjust_timestamp_aux(gxio_mpipe_context_t * context, +				    int64_t nsec) +{ +	struct adjust_timestamp_aux_param temp; +	struct adjust_timestamp_aux_param *params = &temp; + +	params->nsec = nsec; + +	return hv_dev_pwrite(context->fd, 0, (HV_VirtAddr) params, +			     sizeof(*params), +			     GXIO_MPIPE_OP_ADJUST_TIMESTAMP_AUX); +} + +EXPORT_SYMBOL(gxio_mpipe_adjust_timestamp_aux); + +struct arm_pollfd_param { +	union iorpc_pollfd pollfd; +}; + +int gxio_mpipe_arm_pollfd(gxio_mpipe_context_t * context, int pollfd_cookie) +{ +	struct arm_pollfd_param temp; +	struct arm_pollfd_param *params = &temp; + +	params->pollfd.kernel.cookie = pollfd_cookie; + +	return hv_dev_pwrite(context->fd, 0, (HV_VirtAddr) params, +			     sizeof(*params), GXIO_MPIPE_OP_ARM_POLLFD); +} + +EXPORT_SYMBOL(gxio_mpipe_arm_pollfd); + +struct close_pollfd_param { +	union iorpc_pollfd pollfd; +}; + +int gxio_mpipe_close_pollfd(gxio_mpipe_context_t * context, int pollfd_cookie) +{ +	struct close_pollfd_param temp; +	struct close_pollfd_param *params = &temp; + +	params->pollfd.kernel.cookie = pollfd_cookie; + +	return hv_dev_pwrite(context->fd, 0, (HV_VirtAddr) params, +			     sizeof(*params), GXIO_MPIPE_OP_CLOSE_POLLFD); +} + +EXPORT_SYMBOL(gxio_mpipe_close_pollfd); + +struct get_mmio_base_param { +	HV_PTE base; +}; + +int gxio_mpipe_get_mmio_base(gxio_mpipe_context_t * context, HV_PTE *base) +{ +	int __result; +	struct get_mmio_base_param temp; +	struct get_mmio_base_param *params = &temp; + +	__result = +	    hv_dev_pread(context->fd, 0, (HV_VirtAddr) params, sizeof(*params), +			 GXIO_MPIPE_OP_GET_MMIO_BASE); +	*base = params->base; + +	return __result; +} + +EXPORT_SYMBOL(gxio_mpipe_get_mmio_base); + +struct check_mmio_offset_param { +	unsigned long offset; +	unsigned long size; +}; + +int gxio_mpipe_check_mmio_offset(gxio_mpipe_context_t * context, +				 unsigned long offset, unsigned long size) +{ +	struct check_mmio_offset_param temp; +	struct check_mmio_offset_param *params = &temp; + +	params->offset = offset; +	params->size = size; + +	return hv_dev_pwrite(context->fd, 0, (HV_VirtAddr) params, +			     sizeof(*params), GXIO_MPIPE_OP_CHECK_MMIO_OFFSET); +} + +EXPORT_SYMBOL(gxio_mpipe_check_mmio_offset); diff --git a/arch/tile/gxio/iorpc_mpipe_info.c b/arch/tile/gxio/iorpc_mpipe_info.c new file mode 100644 index 00000000000..d0254aa60cb --- /dev/null +++ b/arch/tile/gxio/iorpc_mpipe_info.c @@ -0,0 +1,85 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ + +/* This file is machine-generated; DO NOT EDIT! */ +#include "gxio/iorpc_mpipe_info.h" + + +struct enumerate_aux_param { +	_gxio_mpipe_link_name_t name; +	_gxio_mpipe_link_mac_t mac; +}; + +int gxio_mpipe_info_enumerate_aux(gxio_mpipe_info_context_t * context, +				  unsigned int idx, +				  _gxio_mpipe_link_name_t * name, +				  _gxio_mpipe_link_mac_t * mac) +{ +	int __result; +	struct enumerate_aux_param temp; +	struct enumerate_aux_param *params = &temp; + +	__result = +	    hv_dev_pread(context->fd, 0, (HV_VirtAddr) params, sizeof(*params), +			 (((uint64_t) idx << 32) | +			  GXIO_MPIPE_INFO_OP_ENUMERATE_AUX)); +	*name = params->name; +	*mac = params->mac; + +	return __result; +} + +EXPORT_SYMBOL(gxio_mpipe_info_enumerate_aux); + +struct get_mmio_base_param { +	HV_PTE base; +}; + +int gxio_mpipe_info_get_mmio_base(gxio_mpipe_info_context_t * context, +				  HV_PTE *base) +{ +	int __result; +	struct get_mmio_base_param temp; +	struct get_mmio_base_param *params = &temp; + +	__result = +	    hv_dev_pread(context->fd, 0, (HV_VirtAddr) params, sizeof(*params), +			 GXIO_MPIPE_INFO_OP_GET_MMIO_BASE); +	*base = params->base; + +	return __result; +} + +EXPORT_SYMBOL(gxio_mpipe_info_get_mmio_base); + +struct check_mmio_offset_param { +	unsigned long offset; +	unsigned long size; +}; + +int gxio_mpipe_info_check_mmio_offset(gxio_mpipe_info_context_t * context, +				      unsigned long offset, unsigned long size) +{ +	struct check_mmio_offset_param temp; +	struct check_mmio_offset_param *params = &temp; + +	params->offset = offset; +	params->size = size; + +	return hv_dev_pwrite(context->fd, 0, (HV_VirtAddr) params, +			     sizeof(*params), +			     GXIO_MPIPE_INFO_OP_CHECK_MMIO_OFFSET); +} + +EXPORT_SYMBOL(gxio_mpipe_info_check_mmio_offset); diff --git a/arch/tile/gxio/iorpc_trio.c b/arch/tile/gxio/iorpc_trio.c new file mode 100644 index 00000000000..cef4b2209cd --- /dev/null +++ b/arch/tile/gxio/iorpc_trio.c @@ -0,0 +1,327 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ + +/* This file is machine-generated; DO NOT EDIT! */ +#include "gxio/iorpc_trio.h" + +struct alloc_asids_param { +	unsigned int count; +	unsigned int first; +	unsigned int flags; +}; + +int gxio_trio_alloc_asids(gxio_trio_context_t * context, unsigned int count, +			  unsigned int first, unsigned int flags) +{ +	struct alloc_asids_param temp; +	struct alloc_asids_param *params = &temp; + +	params->count = count; +	params->first = first; +	params->flags = flags; + +	return hv_dev_pwrite(context->fd, 0, (HV_VirtAddr) params, +			     sizeof(*params), GXIO_TRIO_OP_ALLOC_ASIDS); +} + +EXPORT_SYMBOL(gxio_trio_alloc_asids); + + +struct alloc_memory_maps_param { +	unsigned int count; +	unsigned int first; +	unsigned int flags; +}; + +int gxio_trio_alloc_memory_maps(gxio_trio_context_t * context, +				unsigned int count, unsigned int first, +				unsigned int flags) +{ +	struct alloc_memory_maps_param temp; +	struct alloc_memory_maps_param *params = &temp; + +	params->count = count; +	params->first = first; +	params->flags = flags; + +	return hv_dev_pwrite(context->fd, 0, (HV_VirtAddr) params, +			     sizeof(*params), GXIO_TRIO_OP_ALLOC_MEMORY_MAPS); +} + +EXPORT_SYMBOL(gxio_trio_alloc_memory_maps); + + +struct alloc_pio_regions_param { +	unsigned int count; +	unsigned int first; +	unsigned int flags; +}; + +int gxio_trio_alloc_pio_regions(gxio_trio_context_t * context, +				unsigned int count, unsigned int first, +				unsigned int flags) +{ +	struct alloc_pio_regions_param temp; +	struct alloc_pio_regions_param *params = &temp; + +	params->count = count; +	params->first = first; +	params->flags = flags; + +	return hv_dev_pwrite(context->fd, 0, (HV_VirtAddr) params, +			     sizeof(*params), GXIO_TRIO_OP_ALLOC_PIO_REGIONS); +} + +EXPORT_SYMBOL(gxio_trio_alloc_pio_regions); + +struct init_pio_region_aux_param { +	unsigned int pio_region; +	unsigned int mac; +	uint32_t bus_address_hi; +	unsigned int flags; +}; + +int gxio_trio_init_pio_region_aux(gxio_trio_context_t * context, +				  unsigned int pio_region, unsigned int mac, +				  uint32_t bus_address_hi, unsigned int flags) +{ +	struct init_pio_region_aux_param temp; +	struct init_pio_region_aux_param *params = &temp; + +	params->pio_region = pio_region; +	params->mac = mac; +	params->bus_address_hi = bus_address_hi; +	params->flags = flags; + +	return hv_dev_pwrite(context->fd, 0, (HV_VirtAddr) params, +			     sizeof(*params), GXIO_TRIO_OP_INIT_PIO_REGION_AUX); +} + +EXPORT_SYMBOL(gxio_trio_init_pio_region_aux); + + +struct init_memory_map_mmu_aux_param { +	unsigned int map; +	unsigned long va; +	uint64_t size; +	unsigned int asid; +	unsigned int mac; +	uint64_t bus_address; +	unsigned int node; +	unsigned int order_mode; +}; + +int gxio_trio_init_memory_map_mmu_aux(gxio_trio_context_t * context, +				      unsigned int map, unsigned long va, +				      uint64_t size, unsigned int asid, +				      unsigned int mac, uint64_t bus_address, +				      unsigned int node, +				      unsigned int order_mode) +{ +	struct init_memory_map_mmu_aux_param temp; +	struct init_memory_map_mmu_aux_param *params = &temp; + +	params->map = map; +	params->va = va; +	params->size = size; +	params->asid = asid; +	params->mac = mac; +	params->bus_address = bus_address; +	params->node = node; +	params->order_mode = order_mode; + +	return hv_dev_pwrite(context->fd, 0, (HV_VirtAddr) params, +			     sizeof(*params), +			     GXIO_TRIO_OP_INIT_MEMORY_MAP_MMU_AUX); +} + +EXPORT_SYMBOL(gxio_trio_init_memory_map_mmu_aux); + +struct get_port_property_param { +	struct pcie_trio_ports_property trio_ports; +}; + +int gxio_trio_get_port_property(gxio_trio_context_t * context, +				struct pcie_trio_ports_property *trio_ports) +{ +	int __result; +	struct get_port_property_param temp; +	struct get_port_property_param *params = &temp; + +	__result = +	    hv_dev_pread(context->fd, 0, (HV_VirtAddr) params, sizeof(*params), +			 GXIO_TRIO_OP_GET_PORT_PROPERTY); +	*trio_ports = params->trio_ports; + +	return __result; +} + +EXPORT_SYMBOL(gxio_trio_get_port_property); + +struct config_legacy_intr_param { +	union iorpc_interrupt interrupt; +	unsigned int mac; +	unsigned int intx; +}; + +int gxio_trio_config_legacy_intr(gxio_trio_context_t * context, int inter_x, +				 int inter_y, int inter_ipi, int inter_event, +				 unsigned int mac, unsigned int intx) +{ +	struct config_legacy_intr_param temp; +	struct config_legacy_intr_param *params = &temp; + +	params->interrupt.kernel.x = inter_x; +	params->interrupt.kernel.y = inter_y; +	params->interrupt.kernel.ipi = inter_ipi; +	params->interrupt.kernel.event = inter_event; +	params->mac = mac; +	params->intx = intx; + +	return hv_dev_pwrite(context->fd, 0, (HV_VirtAddr) params, +			     sizeof(*params), GXIO_TRIO_OP_CONFIG_LEGACY_INTR); +} + +EXPORT_SYMBOL(gxio_trio_config_legacy_intr); + +struct config_msi_intr_param { +	union iorpc_interrupt interrupt; +	unsigned int mac; +	unsigned int mem_map; +	uint64_t mem_map_base; +	uint64_t mem_map_limit; +	unsigned int asid; +}; + +int gxio_trio_config_msi_intr(gxio_trio_context_t * context, int inter_x, +			      int inter_y, int inter_ipi, int inter_event, +			      unsigned int mac, unsigned int mem_map, +			      uint64_t mem_map_base, uint64_t mem_map_limit, +			      unsigned int asid) +{ +	struct config_msi_intr_param temp; +	struct config_msi_intr_param *params = &temp; + +	params->interrupt.kernel.x = inter_x; +	params->interrupt.kernel.y = inter_y; +	params->interrupt.kernel.ipi = inter_ipi; +	params->interrupt.kernel.event = inter_event; +	params->mac = mac; +	params->mem_map = mem_map; +	params->mem_map_base = mem_map_base; +	params->mem_map_limit = mem_map_limit; +	params->asid = asid; + +	return hv_dev_pwrite(context->fd, 0, (HV_VirtAddr) params, +			     sizeof(*params), GXIO_TRIO_OP_CONFIG_MSI_INTR); +} + +EXPORT_SYMBOL(gxio_trio_config_msi_intr); + + +struct set_mps_mrs_param { +	uint16_t mps; +	uint16_t mrs; +	unsigned int mac; +}; + +int gxio_trio_set_mps_mrs(gxio_trio_context_t * context, uint16_t mps, +			  uint16_t mrs, unsigned int mac) +{ +	struct set_mps_mrs_param temp; +	struct set_mps_mrs_param *params = &temp; + +	params->mps = mps; +	params->mrs = mrs; +	params->mac = mac; + +	return hv_dev_pwrite(context->fd, 0, (HV_VirtAddr) params, +			     sizeof(*params), GXIO_TRIO_OP_SET_MPS_MRS); +} + +EXPORT_SYMBOL(gxio_trio_set_mps_mrs); + +struct force_rc_link_up_param { +	unsigned int mac; +}; + +int gxio_trio_force_rc_link_up(gxio_trio_context_t * context, unsigned int mac) +{ +	struct force_rc_link_up_param temp; +	struct force_rc_link_up_param *params = &temp; + +	params->mac = mac; + +	return hv_dev_pwrite(context->fd, 0, (HV_VirtAddr) params, +			     sizeof(*params), GXIO_TRIO_OP_FORCE_RC_LINK_UP); +} + +EXPORT_SYMBOL(gxio_trio_force_rc_link_up); + +struct force_ep_link_up_param { +	unsigned int mac; +}; + +int gxio_trio_force_ep_link_up(gxio_trio_context_t * context, unsigned int mac) +{ +	struct force_ep_link_up_param temp; +	struct force_ep_link_up_param *params = &temp; + +	params->mac = mac; + +	return hv_dev_pwrite(context->fd, 0, (HV_VirtAddr) params, +			     sizeof(*params), GXIO_TRIO_OP_FORCE_EP_LINK_UP); +} + +EXPORT_SYMBOL(gxio_trio_force_ep_link_up); + +struct get_mmio_base_param { +	HV_PTE base; +}; + +int gxio_trio_get_mmio_base(gxio_trio_context_t * context, HV_PTE *base) +{ +	int __result; +	struct get_mmio_base_param temp; +	struct get_mmio_base_param *params = &temp; + +	__result = +	    hv_dev_pread(context->fd, 0, (HV_VirtAddr) params, sizeof(*params), +			 GXIO_TRIO_OP_GET_MMIO_BASE); +	*base = params->base; + +	return __result; +} + +EXPORT_SYMBOL(gxio_trio_get_mmio_base); + +struct check_mmio_offset_param { +	unsigned long offset; +	unsigned long size; +}; + +int gxio_trio_check_mmio_offset(gxio_trio_context_t * context, +				unsigned long offset, unsigned long size) +{ +	struct check_mmio_offset_param temp; +	struct check_mmio_offset_param *params = &temp; + +	params->offset = offset; +	params->size = size; + +	return hv_dev_pwrite(context->fd, 0, (HV_VirtAddr) params, +			     sizeof(*params), GXIO_TRIO_OP_CHECK_MMIO_OFFSET); +} + +EXPORT_SYMBOL(gxio_trio_check_mmio_offset); diff --git a/arch/tile/gxio/iorpc_usb_host.c b/arch/tile/gxio/iorpc_usb_host.c new file mode 100644 index 00000000000..cf3c3cc1220 --- /dev/null +++ b/arch/tile/gxio/iorpc_usb_host.c @@ -0,0 +1,99 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ + +/* This file is machine-generated; DO NOT EDIT! */ +#include "gxio/iorpc_usb_host.h" + +struct cfg_interrupt_param { +	union iorpc_interrupt interrupt; +}; + +int gxio_usb_host_cfg_interrupt(gxio_usb_host_context_t * context, int inter_x, +				int inter_y, int inter_ipi, int inter_event) +{ +	struct cfg_interrupt_param temp; +	struct cfg_interrupt_param *params = &temp; + +	params->interrupt.kernel.x = inter_x; +	params->interrupt.kernel.y = inter_y; +	params->interrupt.kernel.ipi = inter_ipi; +	params->interrupt.kernel.event = inter_event; + +	return hv_dev_pwrite(context->fd, 0, (HV_VirtAddr) params, +			     sizeof(*params), GXIO_USB_HOST_OP_CFG_INTERRUPT); +} + +EXPORT_SYMBOL(gxio_usb_host_cfg_interrupt); + +struct register_client_memory_param { +	HV_PTE pte; +	unsigned int flags; +}; + +int gxio_usb_host_register_client_memory(gxio_usb_host_context_t * context, +					 HV_PTE pte, unsigned int flags) +{ +	struct register_client_memory_param temp; +	struct register_client_memory_param *params = &temp; + +	params->pte = pte; +	params->flags = flags; + +	return hv_dev_pwrite(context->fd, 0, (HV_VirtAddr) params, +			     sizeof(*params), +			     GXIO_USB_HOST_OP_REGISTER_CLIENT_MEMORY); +} + +EXPORT_SYMBOL(gxio_usb_host_register_client_memory); + +struct get_mmio_base_param { +	HV_PTE base; +}; + +int gxio_usb_host_get_mmio_base(gxio_usb_host_context_t * context, HV_PTE *base) +{ +	int __result; +	struct get_mmio_base_param temp; +	struct get_mmio_base_param *params = &temp; + +	__result = +	    hv_dev_pread(context->fd, 0, (HV_VirtAddr) params, sizeof(*params), +			 GXIO_USB_HOST_OP_GET_MMIO_BASE); +	*base = params->base; + +	return __result; +} + +EXPORT_SYMBOL(gxio_usb_host_get_mmio_base); + +struct check_mmio_offset_param { +	unsigned long offset; +	unsigned long size; +}; + +int gxio_usb_host_check_mmio_offset(gxio_usb_host_context_t * context, +				    unsigned long offset, unsigned long size) +{ +	struct check_mmio_offset_param temp; +	struct check_mmio_offset_param *params = &temp; + +	params->offset = offset; +	params->size = size; + +	return hv_dev_pwrite(context->fd, 0, (HV_VirtAddr) params, +			     sizeof(*params), +			     GXIO_USB_HOST_OP_CHECK_MMIO_OFFSET); +} + +EXPORT_SYMBOL(gxio_usb_host_check_mmio_offset); diff --git a/arch/tile/gxio/kiorpc.c b/arch/tile/gxio/kiorpc.c new file mode 100644 index 00000000000..c8096aa5a3f --- /dev/null +++ b/arch/tile/gxio/kiorpc.c @@ -0,0 +1,61 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + * + * TILE-Gx IORPC support for kernel I/O drivers. + */ + +#include <linux/mmzone.h> +#include <linux/module.h> +#include <linux/io.h> +#include <gxio/iorpc_globals.h> +#include <gxio/kiorpc.h> + +#ifdef DEBUG_IORPC +#define TRACE(FMT, ...) pr_info(SIMPLE_MSG_LINE FMT, ## __VA_ARGS__) +#else +#define TRACE(...) +#endif + +/* Create kernel-VA-space MMIO mapping for an on-chip IO device. */ +void __iomem *iorpc_ioremap(int hv_fd, resource_size_t offset, +			    unsigned long size) +{ +	pgprot_t mmio_base, prot = { 0 }; +	unsigned long pfn; +	int err; + +	/* Look up the shim's lotar and base PA. */ +	err = __iorpc_get_mmio_base(hv_fd, &mmio_base); +	if (err) { +		TRACE("get_mmio_base() failure: %d\n", err); +		return NULL; +	} + +	/* Make sure the HV driver approves of our offset and size. */ +	err = __iorpc_check_mmio_offset(hv_fd, offset, size); +	if (err) { +		TRACE("check_mmio_offset() failure: %d\n", err); +		return NULL; +	} + +	/* +	 * mmio_base contains a base pfn and homing coordinates.  Turn +	 * it into an MMIO pgprot and offset pfn. +	 */ +	prot = hv_pte_set_lotar(prot, hv_pte_get_lotar(mmio_base)); +	pfn = pte_pfn(mmio_base) + PFN_DOWN(offset); + +	return ioremap_prot(PFN_PHYS(pfn), size, prot); +} + +EXPORT_SYMBOL(iorpc_ioremap); diff --git a/arch/tile/gxio/mpipe.c b/arch/tile/gxio/mpipe.c new file mode 100644 index 00000000000..e71c63390ac --- /dev/null +++ b/arch/tile/gxio/mpipe.c @@ -0,0 +1,545 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ + +/* + * Implementation of mpipe gxio calls. + */ + +#include <linux/errno.h> +#include <linux/io.h> +#include <linux/module.h> + +#include <gxio/iorpc_globals.h> +#include <gxio/iorpc_mpipe.h> +#include <gxio/iorpc_mpipe_info.h> +#include <gxio/kiorpc.h> +#include <gxio/mpipe.h> + +/* HACK: Avoid pointless "shadow" warnings. */ +#define link link_shadow + +int gxio_mpipe_init(gxio_mpipe_context_t *context, unsigned int mpipe_index) +{ +	char file[32]; + +	int fd; +	int i; + +	snprintf(file, sizeof(file), "mpipe/%d/iorpc", mpipe_index); +	fd = hv_dev_open((HV_VirtAddr) file, 0); +	if (fd < 0) { +		if (fd >= GXIO_ERR_MIN && fd <= GXIO_ERR_MAX) +			return fd; +		else +			return -ENODEV; +	} + +	context->fd = fd; + +	/* Map in the MMIO space. */ +	context->mmio_cfg_base = (void __force *) +		iorpc_ioremap(fd, HV_MPIPE_CONFIG_MMIO_OFFSET, +			      HV_MPIPE_CONFIG_MMIO_SIZE); +	if (context->mmio_cfg_base == NULL) +		goto cfg_failed; + +	context->mmio_fast_base = (void __force *) +		iorpc_ioremap(fd, HV_MPIPE_FAST_MMIO_OFFSET, +			      HV_MPIPE_FAST_MMIO_SIZE); +	if (context->mmio_fast_base == NULL) +		goto fast_failed; + +	/* Initialize the stacks. */ +	for (i = 0; i < 8; i++) +		context->__stacks.stacks[i] = 255; + +	return 0; + +      fast_failed: +	iounmap((void __force __iomem *)(context->mmio_cfg_base)); +      cfg_failed: +	hv_dev_close(context->fd); +	return -ENODEV; +} + +EXPORT_SYMBOL_GPL(gxio_mpipe_init); + +int gxio_mpipe_destroy(gxio_mpipe_context_t *context) +{ +	iounmap((void __force __iomem *)(context->mmio_cfg_base)); +	iounmap((void __force __iomem *)(context->mmio_fast_base)); +	return hv_dev_close(context->fd); +} + +EXPORT_SYMBOL_GPL(gxio_mpipe_destroy); + +static int16_t gxio_mpipe_buffer_sizes[8] = +	{ 128, 256, 512, 1024, 1664, 4096, 10368, 16384 }; + +gxio_mpipe_buffer_size_enum_t gxio_mpipe_buffer_size_to_buffer_size_enum(size_t +									 size) +{ +	int i; +	for (i = 0; i < 7; i++) +		if (size <= gxio_mpipe_buffer_sizes[i]) +			break; +	return i; +} + +EXPORT_SYMBOL_GPL(gxio_mpipe_buffer_size_to_buffer_size_enum); + +size_t gxio_mpipe_buffer_size_enum_to_buffer_size(gxio_mpipe_buffer_size_enum_t +						  buffer_size_enum) +{ +	if (buffer_size_enum > 7) +		buffer_size_enum = 7; + +	return gxio_mpipe_buffer_sizes[buffer_size_enum]; +} + +EXPORT_SYMBOL_GPL(gxio_mpipe_buffer_size_enum_to_buffer_size); + +size_t gxio_mpipe_calc_buffer_stack_bytes(unsigned long buffers) +{ +	const int BUFFERS_PER_LINE = 12; + +	/* Count the number of cachlines. */ +	unsigned long lines = +		(buffers + BUFFERS_PER_LINE - 1) / BUFFERS_PER_LINE; + +	/* Convert to bytes. */ +	return lines * CHIP_L2_LINE_SIZE(); +} + +EXPORT_SYMBOL_GPL(gxio_mpipe_calc_buffer_stack_bytes); + +int gxio_mpipe_init_buffer_stack(gxio_mpipe_context_t *context, +				 unsigned int stack, +				 gxio_mpipe_buffer_size_enum_t +				 buffer_size_enum, void *mem, size_t mem_size, +				 unsigned int mem_flags) +{ +	int result; + +	memset(mem, 0, mem_size); + +	result = gxio_mpipe_init_buffer_stack_aux(context, mem, mem_size, +						  mem_flags, stack, +						  buffer_size_enum); +	if (result < 0) +		return result; + +	/* Save the stack. */ +	context->__stacks.stacks[buffer_size_enum] = stack; + +	return 0; +} + +EXPORT_SYMBOL_GPL(gxio_mpipe_init_buffer_stack); + +int gxio_mpipe_init_notif_ring(gxio_mpipe_context_t *context, +			       unsigned int ring, +			       void *mem, size_t mem_size, +			       unsigned int mem_flags) +{ +	return gxio_mpipe_init_notif_ring_aux(context, mem, mem_size, +					      mem_flags, ring); +} + +EXPORT_SYMBOL_GPL(gxio_mpipe_init_notif_ring); + +int gxio_mpipe_init_notif_group_and_buckets(gxio_mpipe_context_t *context, +					    unsigned int group, +					    unsigned int ring, +					    unsigned int num_rings, +					    unsigned int bucket, +					    unsigned int num_buckets, +					    gxio_mpipe_bucket_mode_t mode) +{ +	int i; +	int result; + +	gxio_mpipe_bucket_info_t bucket_info = { { +						  .group = group, +						  .mode = mode, +						  } +	}; + +	gxio_mpipe_notif_group_bits_t bits = { {0} }; + +	for (i = 0; i < num_rings; i++) +		gxio_mpipe_notif_group_add_ring(&bits, ring + i); + +	result = gxio_mpipe_init_notif_group(context, group, bits); +	if (result != 0) +		return result; + +	for (i = 0; i < num_buckets; i++) { +		bucket_info.notifring = ring + (i % num_rings); + +		result = gxio_mpipe_init_bucket(context, bucket + i, +						bucket_info); +		if (result != 0) +			return result; +	} + +	return 0; +} + +EXPORT_SYMBOL_GPL(gxio_mpipe_init_notif_group_and_buckets); + +int gxio_mpipe_init_edma_ring(gxio_mpipe_context_t *context, +			      unsigned int ring, unsigned int channel, +			      void *mem, size_t mem_size, +			      unsigned int mem_flags) +{ +	memset(mem, 0, mem_size); + +	return gxio_mpipe_init_edma_ring_aux(context, mem, mem_size, mem_flags, +					     ring, channel); +} + +EXPORT_SYMBOL_GPL(gxio_mpipe_init_edma_ring); + +void gxio_mpipe_rules_init(gxio_mpipe_rules_t *rules, +			   gxio_mpipe_context_t *context) +{ +	rules->context = context; +	memset(&rules->list, 0, sizeof(rules->list)); +} + +EXPORT_SYMBOL_GPL(gxio_mpipe_rules_init); + +int gxio_mpipe_rules_begin(gxio_mpipe_rules_t *rules, +			   unsigned int bucket, unsigned int num_buckets, +			   gxio_mpipe_rules_stacks_t *stacks) +{ +	int i; +	int stack = 255; + +	gxio_mpipe_rules_list_t *list = &rules->list; + +	/* Current rule. */ +	gxio_mpipe_rules_rule_t *rule = +		(gxio_mpipe_rules_rule_t *) (list->rules + list->head); + +	unsigned int head = list->tail; + +	/* +	 * Align next rule properly. +	 *Note that "dmacs_and_vlans" will also be aligned. +	 */ +	unsigned int pad = 0; +	while (((head + pad) % __alignof__(gxio_mpipe_rules_rule_t)) != 0) +		pad++; + +	/* +	 * Verify room. +	 * ISSUE: Mark rules as broken on error? +	 */ +	if (head + pad + sizeof(*rule) >= sizeof(list->rules)) +		return GXIO_MPIPE_ERR_RULES_FULL; + +	/* Verify num_buckets is a power of 2. */ +	if (__builtin_popcount(num_buckets) != 1) +		return GXIO_MPIPE_ERR_RULES_INVALID; + +	/* Add padding to previous rule. */ +	rule->size += pad; + +	/* Start a new rule. */ +	list->head = head + pad; + +	rule = (gxio_mpipe_rules_rule_t *) (list->rules + list->head); + +	/* Default some values. */ +	rule->headroom = 2; +	rule->tailroom = 0; +	rule->capacity = 16384; + +	/* Save the bucket info. */ +	rule->bucket_mask = num_buckets - 1; +	rule->bucket_first = bucket; + +	for (i = 8 - 1; i >= 0; i--) { +		int maybe = +			stacks ? stacks->stacks[i] : rules->context->__stacks. +			stacks[i]; +		if (maybe != 255) +			stack = maybe; +		rule->stacks.stacks[i] = stack; +	} + +	if (stack == 255) +		return GXIO_MPIPE_ERR_RULES_INVALID; + +	/* NOTE: Only entries at the end of the array can be 255. */ +	for (i = 8 - 1; i > 0; i--) { +		if (rule->stacks.stacks[i] == 255) { +			rule->stacks.stacks[i] = stack; +			rule->capacity = +				gxio_mpipe_buffer_size_enum_to_buffer_size(i - +									   1); +		} +	} + +	rule->size = sizeof(*rule); +	list->tail = list->head + rule->size; + +	return 0; +} + +EXPORT_SYMBOL_GPL(gxio_mpipe_rules_begin); + +int gxio_mpipe_rules_add_channel(gxio_mpipe_rules_t *rules, +				 unsigned int channel) +{ +	gxio_mpipe_rules_list_t *list = &rules->list; + +	gxio_mpipe_rules_rule_t *rule = +		(gxio_mpipe_rules_rule_t *) (list->rules + list->head); + +	/* Verify channel. */ +	if (channel >= 32) +		return GXIO_MPIPE_ERR_RULES_INVALID; + +	/* Verify begun. */ +	if (list->tail == 0) +		return GXIO_MPIPE_ERR_RULES_EMPTY; + +	rule->channel_bits |= (1UL << channel); + +	return 0; +} + +EXPORT_SYMBOL_GPL(gxio_mpipe_rules_add_channel); + +int gxio_mpipe_rules_set_headroom(gxio_mpipe_rules_t *rules, uint8_t headroom) +{ +	gxio_mpipe_rules_list_t *list = &rules->list; + +	gxio_mpipe_rules_rule_t *rule = +		(gxio_mpipe_rules_rule_t *) (list->rules + list->head); + +	/* Verify begun. */ +	if (list->tail == 0) +		return GXIO_MPIPE_ERR_RULES_EMPTY; + +	rule->headroom = headroom; + +	return 0; +} + +EXPORT_SYMBOL_GPL(gxio_mpipe_rules_set_headroom); + +int gxio_mpipe_rules_commit(gxio_mpipe_rules_t *rules) +{ +	gxio_mpipe_rules_list_t *list = &rules->list; +	unsigned int size = +		offsetof(gxio_mpipe_rules_list_t, rules) + list->tail; +	return gxio_mpipe_commit_rules(rules->context, list, size); +} + +EXPORT_SYMBOL_GPL(gxio_mpipe_rules_commit); + +int gxio_mpipe_iqueue_init(gxio_mpipe_iqueue_t *iqueue, +			   gxio_mpipe_context_t *context, +			   unsigned int ring, +			   void *mem, size_t mem_size, unsigned int mem_flags) +{ +	/* The init call below will verify that "mem_size" is legal. */ +	unsigned int num_entries = mem_size / sizeof(gxio_mpipe_idesc_t); + +	iqueue->context = context; +	iqueue->idescs = (gxio_mpipe_idesc_t *)mem; +	iqueue->ring = ring; +	iqueue->num_entries = num_entries; +	iqueue->mask_num_entries = num_entries - 1; +	iqueue->log2_num_entries = __builtin_ctz(num_entries); +	iqueue->head = 1; +#ifdef __BIG_ENDIAN__ +	iqueue->swapped = 0; +#endif + +	/* Initialize the "tail". */ +	__gxio_mmio_write(mem, iqueue->head); + +	return gxio_mpipe_init_notif_ring(context, ring, mem, mem_size, +					  mem_flags); +} + +EXPORT_SYMBOL_GPL(gxio_mpipe_iqueue_init); + +int gxio_mpipe_equeue_init(gxio_mpipe_equeue_t *equeue, +			   gxio_mpipe_context_t *context, +			   unsigned int edma_ring_id, +			   unsigned int channel, +			   void *mem, unsigned int mem_size, +			   unsigned int mem_flags) +{ +	/* The init call below will verify that "mem_size" is legal. */ +	unsigned int num_entries = mem_size / sizeof(gxio_mpipe_edesc_t); + +	/* Offset used to read number of completed commands. */ +	MPIPE_EDMA_POST_REGION_ADDR_t offset; + +	int result = gxio_mpipe_init_edma_ring(context, edma_ring_id, channel, +					       mem, mem_size, mem_flags); +	if (result < 0) +		return result; + +	memset(equeue, 0, sizeof(*equeue)); + +	offset.word = 0; +	offset.region = +		MPIPE_MMIO_ADDR__REGION_VAL_EDMA - +		MPIPE_MMIO_ADDR__REGION_VAL_IDMA; +	offset.ring = edma_ring_id; + +	__gxio_dma_queue_init(&equeue->dma_queue, +			      context->mmio_fast_base + offset.word, +			      num_entries); +	equeue->edescs = mem; +	equeue->mask_num_entries = num_entries - 1; +	equeue->log2_num_entries = __builtin_ctz(num_entries); + +	return 0; +} + +EXPORT_SYMBOL_GPL(gxio_mpipe_equeue_init); + +int gxio_mpipe_set_timestamp(gxio_mpipe_context_t *context, +			     const struct timespec *ts) +{ +	cycles_t cycles = get_cycles(); +	return gxio_mpipe_set_timestamp_aux(context, (uint64_t)ts->tv_sec, +					    (uint64_t)ts->tv_nsec, +					    (uint64_t)cycles); +} + +int gxio_mpipe_get_timestamp(gxio_mpipe_context_t *context, +			     struct timespec *ts) +{ +	int ret; +	cycles_t cycles_prev, cycles_now, clock_rate; +	cycles_prev = get_cycles(); +	ret = gxio_mpipe_get_timestamp_aux(context, (uint64_t *)&ts->tv_sec, +					   (uint64_t *)&ts->tv_nsec, +					   (uint64_t *)&cycles_now); +	if (ret < 0) { +		return ret; +	} + +	clock_rate = get_clock_rate(); +	ts->tv_nsec -= (cycles_now - cycles_prev) * 1000000000LL / clock_rate; +	if (ts->tv_nsec < 0) { +		ts->tv_nsec += 1000000000LL; +		ts->tv_sec -= 1; +	} +	return ret; +} + +int gxio_mpipe_adjust_timestamp(gxio_mpipe_context_t *context, int64_t delta) +{ +	return gxio_mpipe_adjust_timestamp_aux(context, delta); +} + +/* Get our internal context used for link name access.  This context is + *  special in that it is not associated with an mPIPE service domain. + */ +static gxio_mpipe_context_t *_gxio_get_link_context(void) +{ +	static gxio_mpipe_context_t context; +	static gxio_mpipe_context_t *contextp; +	static int tried_open = 0; +	static DEFINE_MUTEX(mutex); + +	mutex_lock(&mutex); + +	if (!tried_open) { +		int i = 0; +		tried_open = 1; + +		/* +		 * "4" here is the maximum possible number of mPIPE shims; it's +		 * an exaggeration but we shouldn't ever go beyond 2 anyway. +		 */ +		for (i = 0; i < 4; i++) { +			char file[80]; + +			snprintf(file, sizeof(file), "mpipe/%d/iorpc_info", i); +			context.fd = hv_dev_open((HV_VirtAddr) file, 0); +			if (context.fd < 0) +				continue; + +			contextp = &context; +			break; +		} +	} + +	mutex_unlock(&mutex); + +	return contextp; +} + +int gxio_mpipe_link_enumerate_mac(int idx, char *link_name, uint8_t *link_mac) +{ +	int rv; +	_gxio_mpipe_link_name_t name; +	_gxio_mpipe_link_mac_t mac; + +	gxio_mpipe_context_t *context = _gxio_get_link_context(); +	if (!context) +		return GXIO_ERR_NO_DEVICE; + +	rv = gxio_mpipe_info_enumerate_aux(context, idx, &name, &mac); +	if (rv >= 0) { +		strncpy(link_name, name.name, sizeof(name.name)); +		memcpy(link_mac, mac.mac, sizeof(mac.mac)); +	} + +	return rv; +} + +EXPORT_SYMBOL_GPL(gxio_mpipe_link_enumerate_mac); + +int gxio_mpipe_link_open(gxio_mpipe_link_t *link, +			 gxio_mpipe_context_t *context, const char *link_name, +			 unsigned int flags) +{ +	_gxio_mpipe_link_name_t name; +	int rv; + +	strncpy(name.name, link_name, sizeof(name.name)); +	name.name[GXIO_MPIPE_LINK_NAME_LEN - 1] = '\0'; + +	rv = gxio_mpipe_link_open_aux(context, name, flags); +	if (rv < 0) +		return rv; + +	link->context = context; +	link->channel = rv >> 8; +	link->mac = rv & 0xFF; + +	return 0; +} + +EXPORT_SYMBOL_GPL(gxio_mpipe_link_open); + +int gxio_mpipe_link_close(gxio_mpipe_link_t *link) +{ +	return gxio_mpipe_link_close_aux(link->context, link->mac); +} + +EXPORT_SYMBOL_GPL(gxio_mpipe_link_close); diff --git a/arch/tile/gxio/trio.c b/arch/tile/gxio/trio.c new file mode 100644 index 00000000000..69f0b8df3ce --- /dev/null +++ b/arch/tile/gxio/trio.c @@ -0,0 +1,49 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ + +/* + * Implementation of trio gxio calls. + */ + +#include <linux/errno.h> +#include <linux/io.h> +#include <linux/module.h> + +#include <gxio/trio.h> +#include <gxio/iorpc_globals.h> +#include <gxio/iorpc_trio.h> +#include <gxio/kiorpc.h> + +int gxio_trio_init(gxio_trio_context_t *context, unsigned int trio_index) +{ +	char file[32]; +	int fd; + +	snprintf(file, sizeof(file), "trio/%d/iorpc", trio_index); +	fd = hv_dev_open((HV_VirtAddr) file, 0); +	if (fd < 0) { +		context->fd = -1; + +		if (fd >= GXIO_ERR_MIN && fd <= GXIO_ERR_MAX) +			return fd; +		else +			return -ENODEV; +	} + +	context->fd = fd; + +	return 0; +} + +EXPORT_SYMBOL_GPL(gxio_trio_init); diff --git a/arch/tile/gxio/usb_host.c b/arch/tile/gxio/usb_host.c new file mode 100644 index 00000000000..66b002f54ec --- /dev/null +++ b/arch/tile/gxio/usb_host.c @@ -0,0 +1,91 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ + +/* + * + * Implementation of USB gxio calls. + */ + +#include <linux/io.h> +#include <linux/errno.h> +#include <linux/module.h> + +#include <gxio/iorpc_globals.h> +#include <gxio/iorpc_usb_host.h> +#include <gxio/kiorpc.h> +#include <gxio/usb_host.h> + +int gxio_usb_host_init(gxio_usb_host_context_t * context, int usb_index, +		       int is_ehci) +{ +	char file[32]; +	int fd; + +	if (is_ehci) +		snprintf(file, sizeof(file), "usb_host/%d/iorpc/ehci", +			 usb_index); +	else +		snprintf(file, sizeof(file), "usb_host/%d/iorpc/ohci", +			 usb_index); + +	fd = hv_dev_open((HV_VirtAddr) file, 0); +	if (fd < 0) { +		if (fd >= GXIO_ERR_MIN && fd <= GXIO_ERR_MAX) +			return fd; +		else +			return -ENODEV; +	} + +	context->fd = fd; + +	// Map in the MMIO space. +	context->mmio_base = +		(void __force *)iorpc_ioremap(fd, 0, HV_USB_HOST_MMIO_SIZE); + +	if (context->mmio_base == NULL) { +		hv_dev_close(context->fd); +		return -ENODEV; +	} + +	return 0; +} + +EXPORT_SYMBOL_GPL(gxio_usb_host_init); + +int gxio_usb_host_destroy(gxio_usb_host_context_t * context) +{ +	iounmap((void __force __iomem *)(context->mmio_base)); +	hv_dev_close(context->fd); + +	context->mmio_base = NULL; +	context->fd = -1; + +	return 0; +} + +EXPORT_SYMBOL_GPL(gxio_usb_host_destroy); + +void *gxio_usb_host_get_reg_start(gxio_usb_host_context_t * context) +{ +	return context->mmio_base; +} + +EXPORT_SYMBOL_GPL(gxio_usb_host_get_reg_start); + +size_t gxio_usb_host_get_reg_len(gxio_usb_host_context_t * context) +{ +	return HV_USB_HOST_MMIO_SIZE; +} + +EXPORT_SYMBOL_GPL(gxio_usb_host_get_reg_len); diff --git a/arch/tile/include/arch/mpipe.h b/arch/tile/include/arch/mpipe.h new file mode 100644 index 00000000000..8a33912fd6c --- /dev/null +++ b/arch/tile/include/arch/mpipe.h @@ -0,0 +1,359 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ + +/* Machine-generated file; do not edit. */ + +#ifndef __ARCH_MPIPE_H__ +#define __ARCH_MPIPE_H__ + +#include <arch/abi.h> +#include <arch/mpipe_def.h> + +#ifndef __ASSEMBLER__ + +/* + * MMIO Ingress DMA Release Region Address. + * This is a description of the physical addresses used to manipulate ingress + * credit counters.  Accesses to this address space should use an address of + * this form and a value like that specified in IDMA_RELEASE_REGION_VAL. + */ + +__extension__ +typedef union +{ +  struct +  { +#ifndef __BIG_ENDIAN__ +    /* Reserved. */ +    uint_reg_t __reserved_0  : 3; +    /* NotifRing to be released */ +    uint_reg_t ring          : 8; +    /* Bucket to be released */ +    uint_reg_t bucket        : 13; +    /* Enable NotifRing release */ +    uint_reg_t ring_enable   : 1; +    /* Enable Bucket release */ +    uint_reg_t bucket_enable : 1; +    /* +     * This field of the address selects the region (address space) to be +     * accessed.  For the iDMA release region, this field must be 4. +     */ +    uint_reg_t region        : 3; +    /* Reserved. */ +    uint_reg_t __reserved_1  : 6; +    /* This field of the address indexes the 32 entry service domain table. */ +    uint_reg_t svc_dom       : 5; +    /* Reserved. */ +    uint_reg_t __reserved_2  : 24; +#else   /* __BIG_ENDIAN__ */ +    uint_reg_t __reserved_2  : 24; +    uint_reg_t svc_dom       : 5; +    uint_reg_t __reserved_1  : 6; +    uint_reg_t region        : 3; +    uint_reg_t bucket_enable : 1; +    uint_reg_t ring_enable   : 1; +    uint_reg_t bucket        : 13; +    uint_reg_t ring          : 8; +    uint_reg_t __reserved_0  : 3; +#endif +  }; + +  uint_reg_t word; +} MPIPE_IDMA_RELEASE_REGION_ADDR_t; + +/* + * MMIO Ingress DMA Release Region Value - Release NotifRing and/or Bucket. + * Provides release of the associated NotifRing.  The address of the MMIO + * operation is described in IDMA_RELEASE_REGION_ADDR. + */ + +__extension__ +typedef union +{ +  struct +  { +#ifndef __BIG_ENDIAN__ +    /* +     * Number of packets being released.  The load balancer's count of +     * inflight packets will be decremented by this amount for the associated +     * Bucket and/or NotifRing +     */ +    uint_reg_t count      : 16; +    /* Reserved. */ +    uint_reg_t __reserved : 48; +#else   /* __BIG_ENDIAN__ */ +    uint_reg_t __reserved : 48; +    uint_reg_t count      : 16; +#endif +  }; + +  uint_reg_t word; +} MPIPE_IDMA_RELEASE_REGION_VAL_t; + +/* + * MMIO Buffer Stack Manager Region Address. + * This MMIO region is used for posting or fetching buffers to/from the + * buffer stack manager.  On an MMIO load, this pops a buffer descriptor from + * the top of stack if one is available.  On an MMIO store, this pushes a + * buffer to the stack.  The value read or written is described in + * BSM_REGION_VAL. + */ + +__extension__ +typedef union +{ +  struct +  { +#ifndef __BIG_ENDIAN__ +    /* Reserved. */ +    uint_reg_t __reserved_0 : 3; +    /* BufferStack being accessed. */ +    uint_reg_t stack        : 5; +    /* Reserved. */ +    uint_reg_t __reserved_1 : 18; +    /* +     * This field of the address selects the region (address space) to be +     * accessed.  For the buffer stack manager region, this field must be 6. +     */ +    uint_reg_t region       : 3; +    /* Reserved. */ +    uint_reg_t __reserved_2 : 6; +    /* This field of the address indexes the 32 entry service domain table. */ +    uint_reg_t svc_dom      : 5; +    /* Reserved. */ +    uint_reg_t __reserved_3 : 24; +#else   /* __BIG_ENDIAN__ */ +    uint_reg_t __reserved_3 : 24; +    uint_reg_t svc_dom      : 5; +    uint_reg_t __reserved_2 : 6; +    uint_reg_t region       : 3; +    uint_reg_t __reserved_1 : 18; +    uint_reg_t stack        : 5; +    uint_reg_t __reserved_0 : 3; +#endif +  }; + +  uint_reg_t word; +} MPIPE_BSM_REGION_ADDR_t; + +/* + * MMIO Buffer Stack Manager Region Value. + * This MMIO region is used for posting or fetching buffers to/from the + * buffer stack manager.  On an MMIO load, this pops a buffer descriptor from + * the top of stack if one is available. On an MMIO store, this pushes a + * buffer to the stack.  The address of the MMIO operation is described in + * BSM_REGION_ADDR. + */ + +__extension__ +typedef union +{ +  struct +  { +#ifndef __BIG_ENDIAN__ +    /* Reserved. */ +    uint_reg_t __reserved_0 : 7; +    /* +     * Base virtual address of the buffer.  Must be sign extended by consumer. +     */ +    int_reg_t va           : 35; +    /* Reserved. */ +    uint_reg_t __reserved_1 : 6; +    /* +     * Index of the buffer stack to which this buffer belongs.  Ignored on +     * writes since the offset bits specify the stack being accessed. +     */ +    uint_reg_t stack_idx    : 5; +    /* Reserved. */ +    uint_reg_t __reserved_2 : 5; +    /* +     * Reads as one to indicate that this is a hardware managed buffer. +     * Ignored on writes since all buffers on a given stack are the same size. +     */ +    uint_reg_t hwb          : 1; +    /* +     * Encoded size of buffer (ignored on writes): +     * 0 = 128 bytes +     * 1 = 256 bytes +     * 2 = 512 bytes +     * 3 = 1024 bytes +     * 4 = 1664 bytes +     * 5 = 4096 bytes +     * 6 = 10368 bytes +     * 7 = 16384 bytes +     */ +    uint_reg_t size         : 3; +    /* +     * Valid indication for the buffer.  Ignored on writes. +     * 0 : Valid buffer descriptor popped from stack. +     * 3 : Could not pop a buffer from the stack.  Either the stack is empty, +     * or the hardware's prefetch buffer is empty for this stack. +     */ +    uint_reg_t c            : 2; +#else   /* __BIG_ENDIAN__ */ +    uint_reg_t c            : 2; +    uint_reg_t size         : 3; +    uint_reg_t hwb          : 1; +    uint_reg_t __reserved_2 : 5; +    uint_reg_t stack_idx    : 5; +    uint_reg_t __reserved_1 : 6; +    int_reg_t va           : 35; +    uint_reg_t __reserved_0 : 7; +#endif +  }; + +  uint_reg_t word; +} MPIPE_BSM_REGION_VAL_t; + +/* + * MMIO Egress DMA Post Region Address. + * Used to post descriptor locations to the eDMA descriptor engine.  The + * value to be written is described in EDMA_POST_REGION_VAL + */ + +__extension__ +typedef union +{ +  struct +  { +#ifndef __BIG_ENDIAN__ +    /* Reserved. */ +    uint_reg_t __reserved_0 : 3; +    /* eDMA ring being accessed */ +    uint_reg_t ring         : 5; +    /* Reserved. */ +    uint_reg_t __reserved_1 : 18; +    /* +     * This field of the address selects the region (address space) to be +     * accessed.  For the egress DMA post region, this field must be 5. +     */ +    uint_reg_t region       : 3; +    /* Reserved. */ +    uint_reg_t __reserved_2 : 6; +    /* This field of the address indexes the 32 entry service domain table. */ +    uint_reg_t svc_dom      : 5; +    /* Reserved. */ +    uint_reg_t __reserved_3 : 24; +#else   /* __BIG_ENDIAN__ */ +    uint_reg_t __reserved_3 : 24; +    uint_reg_t svc_dom      : 5; +    uint_reg_t __reserved_2 : 6; +    uint_reg_t region       : 3; +    uint_reg_t __reserved_1 : 18; +    uint_reg_t ring         : 5; +    uint_reg_t __reserved_0 : 3; +#endif +  }; + +  uint_reg_t word; +} MPIPE_EDMA_POST_REGION_ADDR_t; + +/* + * MMIO Egress DMA Post Region Value. + * Used to post descriptor locations to the eDMA descriptor engine.  The + * address is described in EDMA_POST_REGION_ADDR. + */ + +__extension__ +typedef union +{ +  struct +  { +#ifndef __BIG_ENDIAN__ +    /* +     * For writes, this specifies the current ring tail pointer prior to any +     * post.  For example, to post 1 or more descriptors starting at location +     * 23, this would contain 23 (not 24).  On writes, this index must be +     * masked based on the ring size.  The new tail pointer after this post +     * is COUNT+RING_IDX (masked by the ring size). +     * +     * For reads, this provides the hardware descriptor fetcher's head +     * pointer.  The descriptors prior to the head pointer, however, may not +     * yet have been processed so this indicator is only used to determine +     * how full the ring is and if software may post more descriptors. +     */ +    uint_reg_t ring_idx   : 16; +    /* +     * For writes, this specifies number of contiguous descriptors that are +     * being posted.  Software may post up to RingSize descriptors with a +     * single MMIO store.  A zero in this field on a write will "wake up" an +     * eDMA ring and cause it fetch descriptors regardless of the hardware's +     * current view of the state of the tail pointer. +     * +     * For reads, this field provides a rolling count of the number of +     * descriptors that have been completely processed.  This may be used by +     * software to determine when buffers associated with a descriptor may be +     * returned or reused.  When the ring's flush bit is cleared by software +     * (after having been set by HW or SW), the COUNT will be cleared. +     */ +    uint_reg_t count      : 16; +    /* +     * For writes, this specifies the generation number of the tail being +     * posted. Note that if tail+cnt wraps to the beginning of the ring, the +     * eDMA hardware assumes that the descriptors posted at the beginning of +     * the ring are also valid so it is okay to post around the wrap point. +     * +     * For reads, this is the current generation number.  Valid descriptors +     * will have the inverse of this generation number. +     */ +    uint_reg_t gen        : 1; +    /* Reserved. */ +    uint_reg_t __reserved : 31; +#else   /* __BIG_ENDIAN__ */ +    uint_reg_t __reserved : 31; +    uint_reg_t gen        : 1; +    uint_reg_t count      : 16; +    uint_reg_t ring_idx   : 16; +#endif +  }; + +  uint_reg_t word; +} MPIPE_EDMA_POST_REGION_VAL_t; + +/* + * Load Balancer Bucket Status Data. + * Read/Write data for load balancer Bucket-Status Table. 4160 entries + * indexed by LBL_INIT_CTL.IDX when LBL_INIT_CTL.STRUCT_SEL is BSTS_TBL + */ + +__extension__ +typedef union +{ +  struct +  { +#ifndef __BIG_ENDIAN__ +    /* NotifRing currently assigned to this bucket. */ +    uint_reg_t notifring  : 8; +    /* Current reference count. */ +    uint_reg_t count      : 16; +    /* Group associated with this bucket. */ +    uint_reg_t group      : 5; +    /* Mode select for this bucket. */ +    uint_reg_t mode       : 3; +    /* Reserved. */ +    uint_reg_t __reserved : 32; +#else   /* __BIG_ENDIAN__ */ +    uint_reg_t __reserved : 32; +    uint_reg_t mode       : 3; +    uint_reg_t group      : 5; +    uint_reg_t count      : 16; +    uint_reg_t notifring  : 8; +#endif +  }; + +  uint_reg_t word; +} MPIPE_LBL_INIT_DAT_BSTS_TBL_t; +#endif /* !defined(__ASSEMBLER__) */ + +#endif /* !defined(__ARCH_MPIPE_H__) */ diff --git a/arch/tile/include/arch/mpipe_constants.h b/arch/tile/include/arch/mpipe_constants.h new file mode 100644 index 00000000000..410a0400e05 --- /dev/null +++ b/arch/tile/include/arch/mpipe_constants.h @@ -0,0 +1,42 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ + + +#ifndef __ARCH_MPIPE_CONSTANTS_H__ +#define __ARCH_MPIPE_CONSTANTS_H__ + +#define MPIPE_NUM_CLASSIFIERS 10 +#define MPIPE_CLS_MHZ 1200 + +#define MPIPE_NUM_EDMA_RINGS 32 + +#define MPIPE_NUM_SGMII_MACS 16 +#define MPIPE_NUM_XAUI_MACS 4 +#define MPIPE_NUM_LOOPBACK_CHANNELS 4 +#define MPIPE_NUM_NON_LB_CHANNELS 28 + +#define MPIPE_NUM_IPKT_BLOCKS 1536 + +#define MPIPE_NUM_BUCKETS 4160 + +#define MPIPE_NUM_NOTIF_RINGS 256 + +#define MPIPE_NUM_NOTIF_GROUPS 32 + +#define MPIPE_NUM_TLBS_PER_ASID 16 +#define MPIPE_TLB_IDX_WIDTH 4 + +#define MPIPE_MMIO_NUM_SVC_DOM 32 + +#endif /* __ARCH_MPIPE_CONSTANTS_H__ */ diff --git a/arch/tile/include/arch/mpipe_def.h b/arch/tile/include/arch/mpipe_def.h new file mode 100644 index 00000000000..c3d30217fc6 --- /dev/null +++ b/arch/tile/include/arch/mpipe_def.h @@ -0,0 +1,39 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ + +/* Machine-generated file; do not edit. */ + +#ifndef __ARCH_MPIPE_DEF_H__ +#define __ARCH_MPIPE_DEF_H__ +#define MPIPE_MMIO_ADDR__REGION_SHIFT 26 +#define MPIPE_MMIO_ADDR__REGION_VAL_CFG 0x0 +#define MPIPE_MMIO_ADDR__REGION_VAL_IDMA 0x4 +#define MPIPE_MMIO_ADDR__REGION_VAL_EDMA 0x5 +#define MPIPE_MMIO_ADDR__REGION_VAL_BSM 0x6 +#define MPIPE_BSM_REGION_VAL__VA_SHIFT 7 +#define MPIPE_BSM_INIT_DAT_1__SIZE_VAL_BSZ_128 0x0 +#define MPIPE_BSM_INIT_DAT_1__SIZE_VAL_BSZ_256 0x1 +#define MPIPE_BSM_INIT_DAT_1__SIZE_VAL_BSZ_512 0x2 +#define MPIPE_BSM_INIT_DAT_1__SIZE_VAL_BSZ_1024 0x3 +#define MPIPE_BSM_INIT_DAT_1__SIZE_VAL_BSZ_1664 0x4 +#define MPIPE_BSM_INIT_DAT_1__SIZE_VAL_BSZ_4096 0x5 +#define MPIPE_BSM_INIT_DAT_1__SIZE_VAL_BSZ_10368 0x6 +#define MPIPE_BSM_INIT_DAT_1__SIZE_VAL_BSZ_16384 0x7 +#define MPIPE_LBL_INIT_DAT_BSTS_TBL__MODE_VAL_DFA 0x0 +#define MPIPE_LBL_INIT_DAT_BSTS_TBL__MODE_VAL_FIXED 0x1 +#define MPIPE_LBL_INIT_DAT_BSTS_TBL__MODE_VAL_ALWAYS_PICK 0x2 +#define MPIPE_LBL_INIT_DAT_BSTS_TBL__MODE_VAL_STICKY 0x3 +#define MPIPE_LBL_INIT_DAT_BSTS_TBL__MODE_VAL_STICKY_RAND 0x7 +#define MPIPE_LBL_NR_STATE__FIRST_WORD 0x2138 +#endif /* !defined(__ARCH_MPIPE_DEF_H__) */ diff --git a/arch/tile/include/arch/mpipe_shm.h b/arch/tile/include/arch/mpipe_shm.h new file mode 100644 index 00000000000..f2e9e122818 --- /dev/null +++ b/arch/tile/include/arch/mpipe_shm.h @@ -0,0 +1,509 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ + +/* Machine-generated file; do not edit. */ + + +#ifndef __ARCH_MPIPE_SHM_H__ +#define __ARCH_MPIPE_SHM_H__ + +#include <arch/abi.h> +#include <arch/mpipe_shm_def.h> + +#ifndef __ASSEMBLER__ +/** + * MPIPE eDMA Descriptor. + * The eDMA descriptor is written by software and consumed by hardware.  It + * is used to specify the location of egress packet data to be sent out of + * the chip via one of the packet interfaces. + */ + +__extension__ +typedef union +{ +  struct +  { +    /* Word 0 */ + +#ifndef __BIG_ENDIAN__ +    /** +     * Generation number.  Used to indicate a valid descriptor in ring.  When +     * a new descriptor is written into the ring, software must toggle this +     * bit.  The net effect is that the GEN bit being written into new +     * descriptors toggles each time the ring tail pointer wraps. +     */ +    uint_reg_t gen        : 1; +    /** Reserved.  Must be zero. */ +    uint_reg_t r0         : 7; +    /** Checksum generation enabled for this transfer. */ +    uint_reg_t csum       : 1; +    /** +     * Nothing to be sent.  Used, for example, when software has dropped a +     * packet but still wishes to return all of the associated buffers. +     */ +    uint_reg_t ns         : 1; +    /** +     * Notification interrupt will be delivered when packet has been egressed. +     */ +    uint_reg_t notif      : 1; +    /** +     * Boundary indicator.  When 1, this transfer includes the EOP for this +     * command.  Must be clear on all but the last descriptor for an egress +     * packet. +     */ +    uint_reg_t bound      : 1; +    /** Reserved.  Must be zero. */ +    uint_reg_t r1         : 4; +    /** +     * Number of bytes to be sent for this descriptor.  When zero, no data +     * will be moved and the buffer descriptor will be ignored.  If the +     * buffer descriptor indicates that it is chained, the low 7 bits of the +     * VA indicate the offset within the first buffer (e.g. 127 bytes is the +     * maximum offset into the first buffer).  If the size exceeds a single +     * buffer, subsequent buffer descriptors will be fetched prior to +     * processing the next eDMA descriptor in the ring. +     */ +    uint_reg_t xfer_size  : 14; +    /** Reserved.  Must be zero. */ +    uint_reg_t r2         : 2; +    /** +     * Destination of checksum relative to CSUM_START relative to the first +     * byte moved by this descriptor.  Must be zero if CSUM=0 in this +     * descriptor.  Must be less than XFER_SIZE (e.g. the first byte of the +     * CSUM_DEST must be within the span of this descriptor). +     */ +    uint_reg_t csum_dest  : 8; +    /** +     * Start byte of checksum relative to the first byte moved by this +     * descriptor.  If this is not the first descriptor for the egress +     * packet, CSUM_START is still relative to the first byte in this +     * descriptor.  Must be zero if CSUM=0 in this descriptor. +     */ +    uint_reg_t csum_start : 8; +    /** +     * Initial value for 16-bit 1's compliment checksum if enabled via CSUM. +     * Specified in network order.  That is, bits[7:0] will be added to the +     * byte pointed to by CSUM_START and bits[15:8] will be added to the byte +     * pointed to by CSUM_START+1 (with appropriate 1's compliment carries). +     * Must be zero if CSUM=0 in this descriptor. +     */ +    uint_reg_t csum_seed  : 16; +#else   /* __BIG_ENDIAN__ */ +    uint_reg_t csum_seed  : 16; +    uint_reg_t csum_start : 8; +    uint_reg_t csum_dest  : 8; +    uint_reg_t r2         : 2; +    uint_reg_t xfer_size  : 14; +    uint_reg_t r1         : 4; +    uint_reg_t bound      : 1; +    uint_reg_t notif      : 1; +    uint_reg_t ns         : 1; +    uint_reg_t csum       : 1; +    uint_reg_t r0         : 7; +    uint_reg_t gen        : 1; +#endif + +    /* Word 1 */ + +#ifndef __BIG_ENDIAN__ +    /** Virtual address.  Must be sign extended by consumer. */ +    int_reg_t va           : 42; +    /** Reserved. */ +    uint_reg_t __reserved_0 : 6; +    /** Index of the buffer stack to which this buffer belongs. */ +    uint_reg_t stack_idx    : 5; +    /** Reserved. */ +    uint_reg_t __reserved_1 : 3; +    /** +     * Instance ID.  For devices that support more than one mPIPE instance, +     * this field indicates the buffer owner.  If the INST field does not +     * match the mPIPE's instance number when a packet is egressed, buffers +     * with HWB set will be returned to the other mPIPE instance. +     */ +    uint_reg_t inst         : 1; +    /** Reserved. */ +    uint_reg_t __reserved_2 : 1; +    /** +     * Always set to one by hardware in iDMA packet descriptors.  For eDMA, +     * indicates whether the buffer will be released to the buffer stack +     * manager.  When 0, software is responsible for releasing the buffer. +     */ +    uint_reg_t hwb          : 1; +    /** +     * Encoded size of buffer.  Set by the ingress hardware for iDMA packet +     * descriptors.  For eDMA descriptors, indicates the buffer size if .c +     * indicates a chained packet.  If an eDMA descriptor is not chained and +     * the .hwb bit is not set, this field is ignored and the size is +     * specified by the .xfer_size field. +     * 0 = 128 bytes +     * 1 = 256 bytes +     * 2 = 512 bytes +     * 3 = 1024 bytes +     * 4 = 1664 bytes +     * 5 = 4096 bytes +     * 6 = 10368 bytes +     * 7 = 16384 bytes +     */ +    uint_reg_t size         : 3; +    /** +     * Chaining configuration for the buffer.  Indicates that an ingress +     * packet or egress command is chained across multiple buffers, with each +     * buffer's size indicated by the .size field. +     */ +    uint_reg_t c            : 2; +#else   /* __BIG_ENDIAN__ */ +    uint_reg_t c            : 2; +    uint_reg_t size         : 3; +    uint_reg_t hwb          : 1; +    uint_reg_t __reserved_2 : 1; +    uint_reg_t inst         : 1; +    uint_reg_t __reserved_1 : 3; +    uint_reg_t stack_idx    : 5; +    uint_reg_t __reserved_0 : 6; +    int_reg_t va           : 42; +#endif + +  }; + +  /** Word access */ +  uint_reg_t words[2]; +} MPIPE_EDMA_DESC_t; + +/** + * MPIPE Packet Descriptor. + * The packet descriptor is filled by the mPIPE's classification, + * load-balancing, and buffer management services.  Some fields are consumed + * by mPIPE hardware, and others are consumed by Tile software. + */ + +__extension__ +typedef union +{ +  struct +  { +    /* Word 0 */ + +#ifndef __BIG_ENDIAN__ +    /** +     * Notification ring into which this packet descriptor is written. +     * Typically written by load balancer, but can be overridden by +     * classification program if NR is asserted. +     */ +    uint_reg_t notif_ring   : 8; +    /** Source channel for this packet.  Written by mPIPE DMA hardware. */ +    uint_reg_t channel      : 5; +    /** Reserved. */ +    uint_reg_t __reserved_0 : 1; +    /** +     * MAC Error. +     * Generated by the MAC interface.  Asserted if there was an overrun of +     * the MAC's receive FIFO.  This condition generally only occurs if the +     * mPIPE clock is running too slowly. +     */ +    uint_reg_t me           : 1; +    /** +     * Truncation Error. +     * Written by the iDMA hardware.  Asserted if packet was truncated due to +     * insufficient space in iPkt buffer +     */ +    uint_reg_t tr           : 1; +    /** +     * Written by the iDMA hardware.  Indicates the number of bytes written +     * to Tile memory.  In general, this is the actual size of the packet as +     * received from the MAC.  But if the packet is truncated due to running +     * out of buffers or due to the iPkt buffer filling up, then the L2_SIZE +     * will be reduced to reflect the actual number of valid bytes written to +     * Tile memory. +     */ +    uint_reg_t l2_size      : 14; +    /** +     * CRC Error. +     * Generated by the MAC.  Asserted if MAC indicated an L2 CRC error or +     * other L2 error (bad length etc.) on the packet. +     */ +    uint_reg_t ce           : 1; +    /** +     * Cut Through. +     * Written by the iDMA hardware.  Asserted if packet was not completely +     * received before being sent to classifier.  L2_Size will indicate +     * number of bytes received so far. +     */ +    uint_reg_t ct           : 1; +    /** +     * Written by the classification program.  Used by the load balancer to +     * select the ring into which this packet descriptor is written. +     */ +    uint_reg_t bucket_id    : 13; +    /** Reserved. */ +    uint_reg_t __reserved_1 : 3; +    /** +     * Checksum. +     * Written by classification program.  When 1, the checksum engine will +     * perform checksum based on the CSUM_SEED, CSUM_START, and CSUM_BYTES +     * fields.  The result will be placed in CSUM_VAL. +     */ +    uint_reg_t cs           : 1; +    /** +     * Notification Ring Select. +     * Written by the classification program.  When 1, the NotifRingIDX is +     * set by classification program rather than being set by load balancer. +     */ +    uint_reg_t nr           : 1; +    /** +     * Written by classification program.  Indicates whether packet and +     * descriptor should both be dropped, both be delivered, or only the +     * descriptor should be delivered. +     */ +    uint_reg_t dest         : 2; +    /** +     * General Purpose Sequence Number Enable. +     * Written by the classification program.  When 1, the GP_SQN_SEL field +     * contains the sequence number selector and the GP_SQN field will be +     * replaced with the associated sequence number.  When clear, the GP_SQN +     * field is left intact and be used as "Custom" bytes. +     */ +    uint_reg_t sq           : 1; +    /** +     * TimeStamp Enable. +     * Enable TimeStamp insertion.  When clear, timestamp field may be filled +     * with custom data by classifier.  When set, hardware inserts the +     * timestamp when the start of packet is received from the MAC. +     */ +    uint_reg_t ts           : 1; +    /** +     * Packet Sequence Number Enable. +     * Enable PacketSQN insertion.  When clear, PacketSQN field may be filled +     * with custom data by classifier.  When set, hardware inserts the packet +     * sequence number when the packet descriptor is written to a +     * notification ring. +     */ +    uint_reg_t ps           : 1; +    /** +     * Buffer Error. +     * Written by the iDMA hardware.  Asserted if iDMA ran out of buffers +     * while writing the packet. Software must still return any buffer +     * descriptors whose C field indicates a valid descriptor was consumed. +     */ +    uint_reg_t be           : 1; +    /** +     * Written by  the classification program.  The associated counter is +     * incremented when the packet is sent. +     */ +    uint_reg_t ctr0         : 5; +    /** Reserved. */ +    uint_reg_t __reserved_2 : 3; +#else   /* __BIG_ENDIAN__ */ +    uint_reg_t __reserved_2 : 3; +    uint_reg_t ctr0         : 5; +    uint_reg_t be           : 1; +    uint_reg_t ps           : 1; +    uint_reg_t ts           : 1; +    uint_reg_t sq           : 1; +    uint_reg_t dest         : 2; +    uint_reg_t nr           : 1; +    uint_reg_t cs           : 1; +    uint_reg_t __reserved_1 : 3; +    uint_reg_t bucket_id    : 13; +    uint_reg_t ct           : 1; +    uint_reg_t ce           : 1; +    uint_reg_t l2_size      : 14; +    uint_reg_t tr           : 1; +    uint_reg_t me           : 1; +    uint_reg_t __reserved_0 : 1; +    uint_reg_t channel      : 5; +    uint_reg_t notif_ring   : 8; +#endif + +    /* Word 1 */ + +#ifndef __BIG_ENDIAN__ +    /** +     * Written by  the classification program.  The associated counter is +     * incremented when the packet is sent. +     */ +    uint_reg_t ctr1          : 5; +    /** Reserved. */ +    uint_reg_t __reserved_3  : 3; +    /** +     * Written by classification program.  Indicates the start byte for +     * checksum.  Relative to 1st byte received from MAC. +     */ +    uint_reg_t csum_start    : 8; +    /** +     * Checksum seed written by classification program.  Overwritten with +     * resultant checksum if CS bit is asserted.  The endianness of the CSUM +     * value bits when viewed by Tile software match the packet byte order. +     * That is, bits[7:0] of the resulting checksum value correspond to +     * earlier (more significant) bytes in the packet.  To avoid classifier +     * software from having to byte swap the CSUM_SEED, the iDMA checksum +     * engine byte swaps the classifier's result before seeding the checksum +     * calculation.  Thus, the CSUM_START byte of packet data is added to +     * bits[15:8] of the CSUM_SEED field generated by the classifier.  This +     * byte swap will be visible to Tile software if the CS bit is clear. +     */ +    uint_reg_t csum_seed_val : 16; +    /** +     * Written by  the classification program.  Not interpreted by mPIPE +     * hardware. +     */ +    uint_reg_t custom0       : 32; +#else   /* __BIG_ENDIAN__ */ +    uint_reg_t custom0       : 32; +    uint_reg_t csum_seed_val : 16; +    uint_reg_t csum_start    : 8; +    uint_reg_t __reserved_3  : 3; +    uint_reg_t ctr1          : 5; +#endif + +    /* Word 2 */ + +#ifndef __BIG_ENDIAN__ +    /** +     * Written by  the classification program.  Not interpreted by mPIPE +     * hardware. +     */ +    uint_reg_t custom1 : 64; +#else   /* __BIG_ENDIAN__ */ +    uint_reg_t custom1 : 64; +#endif + +    /* Word 3 */ + +#ifndef __BIG_ENDIAN__ +    /** +     * Written by  the classification program.  Not interpreted by mPIPE +     * hardware. +     */ +    uint_reg_t custom2 : 64; +#else   /* __BIG_ENDIAN__ */ +    uint_reg_t custom2 : 64; +#endif + +    /* Word 4 */ + +#ifndef __BIG_ENDIAN__ +    /** +     * Written by  the classification program.  Not interpreted by mPIPE +     * hardware. +     */ +    uint_reg_t custom3 : 64; +#else   /* __BIG_ENDIAN__ */ +    uint_reg_t custom3 : 64; +#endif + +    /* Word 5 */ + +#ifndef __BIG_ENDIAN__ +    /** +     * Sequence number applied when packet is distributed.   Classifier +     * selects which sequence number is to be applied by writing the 13-bit +     * SQN-selector into this field. +     */ +    uint_reg_t gp_sqn     : 16; +    /** +     * Written by notification hardware.  The packet sequence number is +     * incremented for each packet that wasn't dropped. +     */ +    uint_reg_t packet_sqn : 48; +#else   /* __BIG_ENDIAN__ */ +    uint_reg_t packet_sqn : 48; +    uint_reg_t gp_sqn     : 16; +#endif + +    /* Word 6 */ + +#ifndef __BIG_ENDIAN__ +    /** +     * Written by hardware when the start-of-packet is received by the mPIPE +     * from the MAC.  This is the nanoseconds part of the packet timestamp. +     */ +    uint_reg_t time_stamp_ns  : 32; +    /** +     * Written by hardware when the start-of-packet is received by the mPIPE +     * from the MAC.  This is the seconds part of the packet timestamp. +     */ +    uint_reg_t time_stamp_sec : 32; +#else   /* __BIG_ENDIAN__ */ +    uint_reg_t time_stamp_sec : 32; +    uint_reg_t time_stamp_ns  : 32; +#endif + +    /* Word 7 */ + +#ifndef __BIG_ENDIAN__ +    /** Virtual address.  Must be sign extended by consumer. */ +    int_reg_t va           : 42; +    /** Reserved. */ +    uint_reg_t __reserved_4 : 6; +    /** Index of the buffer stack to which this buffer belongs. */ +    uint_reg_t stack_idx    : 5; +    /** Reserved. */ +    uint_reg_t __reserved_5 : 3; +    /** +     * Instance ID.  For devices that support more than one mPIPE instance, +     * this field indicates the buffer owner.  If the INST field does not +     * match the mPIPE's instance number when a packet is egressed, buffers +     * with HWB set will be returned to the other mPIPE instance. +     */ +    uint_reg_t inst         : 1; +    /** Reserved. */ +    uint_reg_t __reserved_6 : 1; +    /** +     * Always set to one by hardware in iDMA packet descriptors.  For eDMA, +     * indicates whether the buffer will be released to the buffer stack +     * manager.  When 0, software is responsible for releasing the buffer. +     */ +    uint_reg_t hwb          : 1; +    /** +     * Encoded size of buffer.  Set by the ingress hardware for iDMA packet +     * descriptors.  For eDMA descriptors, indicates the buffer size if .c +     * indicates a chained packet.  If an eDMA descriptor is not chained and +     * the .hwb bit is not set, this field is ignored and the size is +     * specified by the .xfer_size field. +     * 0 = 128 bytes +     * 1 = 256 bytes +     * 2 = 512 bytes +     * 3 = 1024 bytes +     * 4 = 1664 bytes +     * 5 = 4096 bytes +     * 6 = 10368 bytes +     * 7 = 16384 bytes +     */ +    uint_reg_t size         : 3; +    /** +     * Chaining configuration for the buffer.  Indicates that an ingress +     * packet or egress command is chained across multiple buffers, with each +     * buffer's size indicated by the .size field. +     */ +    uint_reg_t c            : 2; +#else   /* __BIG_ENDIAN__ */ +    uint_reg_t c            : 2; +    uint_reg_t size         : 3; +    uint_reg_t hwb          : 1; +    uint_reg_t __reserved_6 : 1; +    uint_reg_t inst         : 1; +    uint_reg_t __reserved_5 : 3; +    uint_reg_t stack_idx    : 5; +    uint_reg_t __reserved_4 : 6; +    int_reg_t va           : 42; +#endif + +  }; + +  /** Word access */ +  uint_reg_t words[8]; +} MPIPE_PDESC_t; +#endif /* !defined(__ASSEMBLER__) */ + +#endif /* !defined(__ARCH_MPIPE_SHM_H__) */ diff --git a/arch/tile/include/arch/mpipe_shm_def.h b/arch/tile/include/arch/mpipe_shm_def.h new file mode 100644 index 00000000000..6124d39c831 --- /dev/null +++ b/arch/tile/include/arch/mpipe_shm_def.h @@ -0,0 +1,23 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ + +/* Machine-generated file; do not edit. */ + +#ifndef __ARCH_MPIPE_SHM_DEF_H__ +#define __ARCH_MPIPE_SHM_DEF_H__ +#define MPIPE_EDMA_DESC_WORD1__C_VAL_UNCHAINED 0x0 +#define MPIPE_EDMA_DESC_WORD1__C_VAL_CHAINED 0x1 +#define MPIPE_EDMA_DESC_WORD1__C_VAL_NOT_RDY 0x2 +#define MPIPE_EDMA_DESC_WORD1__C_VAL_INVALID 0x3 +#endif /* !defined(__ARCH_MPIPE_SHM_DEF_H__) */ diff --git a/arch/tile/include/arch/trio.h b/arch/tile/include/arch/trio.h new file mode 100644 index 00000000000..d3000a871a2 --- /dev/null +++ b/arch/tile/include/arch/trio.h @@ -0,0 +1,72 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ + +/* Machine-generated file; do not edit. */ + +#ifndef __ARCH_TRIO_H__ +#define __ARCH_TRIO_H__ + +#include <arch/abi.h> +#include <arch/trio_def.h> + +#ifndef __ASSEMBLER__ + +/* + * Tile PIO Region Configuration - CFG Address Format. + * This register describes the address format for PIO accesses when the + * associated region is setup with TYPE=CFG. + */ + +__extension__ +typedef union +{ +  struct +  { +#ifndef __BIG_ENDIAN__ +    /* Register Address (full byte address). */ +    uint_reg_t reg_addr     : 12; +    /* Function Number */ +    uint_reg_t fn           : 3; +    /* Device Number */ +    uint_reg_t dev          : 5; +    /* BUS Number */ +    uint_reg_t bus          : 8; +    /* Config Type: 0 for access to directly-attached device.  1 otherwise. */ +    uint_reg_t type         : 1; +    /* Reserved. */ +    uint_reg_t __reserved_0 : 1; +    /* +     * MAC select.  This must match the configuration in +     * TILE_PIO_REGION_SETUP.MAC. +     */ +    uint_reg_t mac          : 2; +    /* Reserved. */ +    uint_reg_t __reserved_1 : 32; +#else   /* __BIG_ENDIAN__ */ +    uint_reg_t __reserved_1 : 32; +    uint_reg_t mac          : 2; +    uint_reg_t __reserved_0 : 1; +    uint_reg_t type         : 1; +    uint_reg_t bus          : 8; +    uint_reg_t dev          : 5; +    uint_reg_t fn           : 3; +    uint_reg_t reg_addr     : 12; +#endif +  }; + +  uint_reg_t word; +} TRIO_TILE_PIO_REGION_SETUP_CFG_ADDR_t; +#endif /* !defined(__ASSEMBLER__) */ + +#endif /* !defined(__ARCH_TRIO_H__) */ diff --git a/arch/tile/include/arch/trio_constants.h b/arch/tile/include/arch/trio_constants.h new file mode 100644 index 00000000000..628b045436b --- /dev/null +++ b/arch/tile/include/arch/trio_constants.h @@ -0,0 +1,36 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ + + +#ifndef __ARCH_TRIO_CONSTANTS_H__ +#define __ARCH_TRIO_CONSTANTS_H__ + +#define TRIO_NUM_ASIDS 16 +#define TRIO_NUM_TLBS_PER_ASID 16 + +#define TRIO_NUM_TPIO_REGIONS 8 +#define TRIO_LOG2_NUM_TPIO_REGIONS 3 + +#define TRIO_NUM_MAP_MEM_REGIONS 16 +#define TRIO_LOG2_NUM_MAP_MEM_REGIONS 4 +#define TRIO_NUM_MAP_SQ_REGIONS 8 +#define TRIO_LOG2_NUM_MAP_SQ_REGIONS 3 + +#define TRIO_LOG2_NUM_SQ_FIFO_ENTRIES 6 + +#define TRIO_NUM_PUSH_DMA_RINGS 32 + +#define TRIO_NUM_PULL_DMA_RINGS 32 + +#endif /* __ARCH_TRIO_CONSTANTS_H__ */ diff --git a/arch/tile/include/arch/trio_def.h b/arch/tile/include/arch/trio_def.h new file mode 100644 index 00000000000..e80500317dc --- /dev/null +++ b/arch/tile/include/arch/trio_def.h @@ -0,0 +1,41 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ + +/* Machine-generated file; do not edit. */ + +#ifndef __ARCH_TRIO_DEF_H__ +#define __ARCH_TRIO_DEF_H__ +#define TRIO_CFG_REGION_ADDR__REG_SHIFT 0 +#define TRIO_CFG_REGION_ADDR__INTFC_SHIFT 16 +#define TRIO_CFG_REGION_ADDR__INTFC_VAL_TRIO 0x0 +#define TRIO_CFG_REGION_ADDR__INTFC_VAL_MAC_INTERFACE 0x1 +#define TRIO_CFG_REGION_ADDR__INTFC_VAL_MAC_STANDARD 0x2 +#define TRIO_CFG_REGION_ADDR__INTFC_VAL_MAC_PROTECTED 0x3 +#define TRIO_CFG_REGION_ADDR__MAC_SEL_SHIFT 18 +#define TRIO_CFG_REGION_ADDR__PROT_SHIFT 20 +#define TRIO_PIO_REGIONS_ADDR__REGION_SHIFT 32 +#define TRIO_MAP_MEM_REG_INT0 0x1000000000 +#define TRIO_MAP_MEM_REG_INT1 0x1000000008 +#define TRIO_MAP_MEM_REG_INT2 0x1000000010 +#define TRIO_MAP_MEM_REG_INT3 0x1000000018 +#define TRIO_MAP_MEM_REG_INT4 0x1000000020 +#define TRIO_MAP_MEM_REG_INT5 0x1000000028 +#define TRIO_MAP_MEM_REG_INT6 0x1000000030 +#define TRIO_MAP_MEM_REG_INT7 0x1000000038 +#define TRIO_MAP_MEM_LIM__ADDR_SHIFT 12 +#define TRIO_MAP_MEM_SETUP__ORDER_MODE_VAL_UNORDERED 0x0 +#define TRIO_MAP_MEM_SETUP__ORDER_MODE_VAL_STRICT 0x1 +#define TRIO_MAP_MEM_SETUP__ORDER_MODE_VAL_REL_ORD 0x2 +#define TRIO_TILE_PIO_REGION_SETUP_CFG_ADDR__MAC_SHIFT 30 +#endif /* !defined(__ARCH_TRIO_DEF_H__) */ diff --git a/arch/tile/include/arch/trio_pcie_intfc.h b/arch/tile/include/arch/trio_pcie_intfc.h new file mode 100644 index 00000000000..0487fdb9d58 --- /dev/null +++ b/arch/tile/include/arch/trio_pcie_intfc.h @@ -0,0 +1,229 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ + +/* Machine-generated file; do not edit. */ + +#ifndef __ARCH_TRIO_PCIE_INTFC_H__ +#define __ARCH_TRIO_PCIE_INTFC_H__ + +#include <arch/abi.h> +#include <arch/trio_pcie_intfc_def.h> + +#ifndef __ASSEMBLER__ + +/* + * Port Configuration. + * Configuration of the PCIe Port + */ + +__extension__ +typedef union +{ +  struct +  { +#ifndef __BIG_ENDIAN__ +    /* Provides the state of the strapping pins for this port. */ +    uint_reg_t strap_state      : 3; +    /* Reserved. */ +    uint_reg_t __reserved_0     : 1; +    /* +     * When 1, the device type will be overridden using OVD_DEV_TYPE_VAL. +     * When 0, the device type is determined based on the STRAP_STATE. +     */ +    uint_reg_t ovd_dev_type     : 1; +    /* Provides the device type when OVD_DEV_TYPE is 1. */ +    uint_reg_t ovd_dev_type_val : 4; +    /* Determines how link is trained. */ +    uint_reg_t train_mode       : 2; +    /* Reserved. */ +    uint_reg_t __reserved_1     : 1; +    /* +     * For PCIe, used to flip physical RX lanes that were not properly wired. +     *  This is not the same as lane reversal which is handled automatically +     * during link training.  When 0, RX Lane0 must be wired to the link +     * partner (either to its Lane0 or it's LaneN).  When RX_LANE_FLIP is 1, +     * the highest numbered lane for this port becomes Lane0 and Lane0 does +     * NOT have to be wired to the link partner. +     */ +    uint_reg_t rx_lane_flip     : 1; +    /* +     * For PCIe, used to flip physical TX lanes that were not properly wired. +     *  This is not the same as lane reversal which is handled automatically +     * during link training.  When 0, TX Lane0 must be wired to the link +     * partner (either to its Lane0 or it's LaneN).  When TX_LANE_FLIP is 1, +     * the highest numbered lane for this port becomes Lane0 and Lane0 does +     * NOT have to be wired to the link partner. +     */ +    uint_reg_t tx_lane_flip     : 1; +    /* +     * For StreamIO port, configures the width of the port when TRAIN_MODE is +     * not STRAP. +     */ +    uint_reg_t stream_width     : 2; +    /* +     * For StreamIO port, configures the rate of the port when TRAIN_MODE is +     * not STRAP. +     */ +    uint_reg_t stream_rate      : 2; +    /* Reserved. */ +    uint_reg_t __reserved_2     : 46; +#else   /* __BIG_ENDIAN__ */ +    uint_reg_t __reserved_2     : 46; +    uint_reg_t stream_rate      : 2; +    uint_reg_t stream_width     : 2; +    uint_reg_t tx_lane_flip     : 1; +    uint_reg_t rx_lane_flip     : 1; +    uint_reg_t __reserved_1     : 1; +    uint_reg_t train_mode       : 2; +    uint_reg_t ovd_dev_type_val : 4; +    uint_reg_t ovd_dev_type     : 1; +    uint_reg_t __reserved_0     : 1; +    uint_reg_t strap_state      : 3; +#endif +  }; + +  uint_reg_t word; +} TRIO_PCIE_INTFC_PORT_CONFIG_t; + +/* + * Port Status. + * Status of the PCIe Port.  This register applies to the StreamIO port when + * StreamIO is enabled. + */ + +__extension__ +typedef union +{ +  struct +  { +#ifndef __BIG_ENDIAN__ +    /* +     * Indicates the DL state of the port.  When 1, the port is up and ready +     * to receive traffic. +     */ +    uint_reg_t dl_up        : 1; +    /* +     * Indicates the number of times the link has gone down.  Clears on read. +     */ +    uint_reg_t dl_down_cnt  : 7; +    /* Indicates the SERDES PLL has spun up and is providing a valid clock. */ +    uint_reg_t clock_ready  : 1; +    /* Reserved. */ +    uint_reg_t __reserved_0 : 7; +    /* Device revision ID. */ +    uint_reg_t device_rev   : 8; +    /* Link state (PCIe). */ +    uint_reg_t ltssm_state  : 6; +    /* Link power management state (PCIe). */ +    uint_reg_t pm_state     : 3; +    /* Reserved. */ +    uint_reg_t __reserved_1 : 31; +#else   /* __BIG_ENDIAN__ */ +    uint_reg_t __reserved_1 : 31; +    uint_reg_t pm_state     : 3; +    uint_reg_t ltssm_state  : 6; +    uint_reg_t device_rev   : 8; +    uint_reg_t __reserved_0 : 7; +    uint_reg_t clock_ready  : 1; +    uint_reg_t dl_down_cnt  : 7; +    uint_reg_t dl_up        : 1; +#endif +  }; + +  uint_reg_t word; +} TRIO_PCIE_INTFC_PORT_STATUS_t; + +/* + * Transmit FIFO Control. + * Contains TX FIFO thresholds.  These registers are for diagnostics purposes + * only.  Changing these values causes undefined behavior. + */ + +__extension__ +typedef union +{ +  struct +  { +#ifndef __BIG_ENDIAN__ +    /* +     * Almost-Empty level for TX0 data.  Typically set to at least +     * roundup(38.0*M/N) where N=tclk frequency and M=MAC symbol rate in MHz +     * for a x4 port (250MHz). +     */ +    uint_reg_t tx0_data_ae_lvl : 7; +    /* Reserved. */ +    uint_reg_t __reserved_0    : 1; +    /* Almost-Empty level for TX1 data. */ +    uint_reg_t tx1_data_ae_lvl : 7; +    /* Reserved. */ +    uint_reg_t __reserved_1    : 1; +    /* Almost-Full level for TX0 data. */ +    uint_reg_t tx0_data_af_lvl : 7; +    /* Reserved. */ +    uint_reg_t __reserved_2    : 1; +    /* Almost-Full level for TX1 data. */ +    uint_reg_t tx1_data_af_lvl : 7; +    /* Reserved. */ +    uint_reg_t __reserved_3    : 1; +    /* Almost-Full level for TX0 info. */ +    uint_reg_t tx0_info_af_lvl : 5; +    /* Reserved. */ +    uint_reg_t __reserved_4    : 3; +    /* Almost-Full level for TX1 info. */ +    uint_reg_t tx1_info_af_lvl : 5; +    /* Reserved. */ +    uint_reg_t __reserved_5    : 3; +    /* +     * This register provides performance adjustment for high bandwidth +     * flows.  The MAC will assert almost-full to TRIO if non-posted credits +     * fall below this level.  Note that setting this larger than the initial +     * PORT_CREDIT.NPH value will cause READS to never be sent.  If the +     * initial credit value from the link partner is smaller than this value +     * when the link comes up, the value will be reset to the initial credit +     * value to prevent lockup. +     */ +    uint_reg_t min_np_credits  : 8; +    /* +     * This register provides performance adjustment for high bandwidth +     * flows.  The MAC will assert almost-full to TRIO if posted credits fall +     * below this level.  Note that setting this larger than the initial +     * PORT_CREDIT.PH value will cause WRITES to never be sent.  If the +     * initial credit value from the link partner is smaller than this value +     * when the link comes up, the value will be reset to the initial credit +     * value to prevent lockup. +     */ +    uint_reg_t min_p_credits   : 8; +#else   /* __BIG_ENDIAN__ */ +    uint_reg_t min_p_credits   : 8; +    uint_reg_t min_np_credits  : 8; +    uint_reg_t __reserved_5    : 3; +    uint_reg_t tx1_info_af_lvl : 5; +    uint_reg_t __reserved_4    : 3; +    uint_reg_t tx0_info_af_lvl : 5; +    uint_reg_t __reserved_3    : 1; +    uint_reg_t tx1_data_af_lvl : 7; +    uint_reg_t __reserved_2    : 1; +    uint_reg_t tx0_data_af_lvl : 7; +    uint_reg_t __reserved_1    : 1; +    uint_reg_t tx1_data_ae_lvl : 7; +    uint_reg_t __reserved_0    : 1; +    uint_reg_t tx0_data_ae_lvl : 7; +#endif +  }; + +  uint_reg_t word; +} TRIO_PCIE_INTFC_TX_FIFO_CTL_t; +#endif /* !defined(__ASSEMBLER__) */ + +#endif /* !defined(__ARCH_TRIO_PCIE_INTFC_H__) */ diff --git a/arch/tile/include/arch/trio_pcie_intfc_def.h b/arch/tile/include/arch/trio_pcie_intfc_def.h new file mode 100644 index 00000000000..d3fd6781fb2 --- /dev/null +++ b/arch/tile/include/arch/trio_pcie_intfc_def.h @@ -0,0 +1,32 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ + +/* Machine-generated file; do not edit. */ + +#ifndef __ARCH_TRIO_PCIE_INTFC_DEF_H__ +#define __ARCH_TRIO_PCIE_INTFC_DEF_H__ +#define TRIO_PCIE_INTFC_MAC_INT_STS 0x0000 +#define TRIO_PCIE_INTFC_MAC_INT_STS__INT_LEVEL_MASK  0xf000 +#define TRIO_PCIE_INTFC_PORT_CONFIG 0x0018 +#define TRIO_PCIE_INTFC_PORT_CONFIG__STRAP_STATE_VAL_DISABLED 0x0 +#define TRIO_PCIE_INTFC_PORT_CONFIG__STRAP_STATE_VAL_AUTO_CONFIG_ENDPOINT 0x1 +#define TRIO_PCIE_INTFC_PORT_CONFIG__STRAP_STATE_VAL_AUTO_CONFIG_RC 0x2 +#define TRIO_PCIE_INTFC_PORT_CONFIG__STRAP_STATE_VAL_AUTO_CONFIG_ENDPOINT_G1 0x3 +#define TRIO_PCIE_INTFC_PORT_CONFIG__STRAP_STATE_VAL_AUTO_CONFIG_RC_G1 0x4 +#define TRIO_PCIE_INTFC_PORT_CONFIG__STRAP_STATE_VAL_AUTO_XLINK 0x5 +#define TRIO_PCIE_INTFC_PORT_CONFIG__STRAP_STATE_VAL_STREAM_X1 0x6 +#define TRIO_PCIE_INTFC_PORT_CONFIG__STRAP_STATE_VAL_STREAM_X4 0x7 +#define TRIO_PCIE_INTFC_PORT_STATUS 0x0020 +#define TRIO_PCIE_INTFC_TX_FIFO_CTL 0x0050 +#endif /* !defined(__ARCH_TRIO_PCIE_INTFC_DEF_H__) */ diff --git a/arch/tile/include/arch/trio_pcie_rc.h b/arch/tile/include/arch/trio_pcie_rc.h new file mode 100644 index 00000000000..6a25d0aca85 --- /dev/null +++ b/arch/tile/include/arch/trio_pcie_rc.h @@ -0,0 +1,156 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ + +/* Machine-generated file; do not edit. */ + +#ifndef __ARCH_TRIO_PCIE_RC_H__ +#define __ARCH_TRIO_PCIE_RC_H__ + +#include <arch/abi.h> +#include <arch/trio_pcie_rc_def.h> + +#ifndef __ASSEMBLER__ + +/* Device Capabilities Register. */ + +__extension__ +typedef union +{ +  struct +  { +#ifndef __BIG_ENDIAN__ +    /* +     * Max_Payload_Size Supported, writablethrough the MAC_STANDARD interface +     */ +    uint_reg_t mps_sup                    : 3; +    /* +     * This field is writable through the MAC_STANDARD interface.  However, +     * Phantom Function is not  supported. Therefore, the application must +     * not write any value other than 0x0 to this  field. +     */ +    uint_reg_t phantom_function_supported : 2; +    /* This bit is writable through the MAC_STANDARD interface. */ +    uint_reg_t ext_tag_field_supported    : 1; +    /* Reserved. */ +    uint_reg_t __reserved_0               : 3; +    /* Endpoint L1 Acceptable Latency Must be 0x0 for non-Endpoint devices. */ +    uint_reg_t l1_lat                     : 3; +    /* +     * Undefined since PCI Express 1.1 (Was Attention Button Present for PCI +     * Express 1.0a) +     */ +    uint_reg_t r1                         : 1; +    /* +     * Undefined since PCI Express 1.1 (Was Attention Indicator Present for +     * PCI  Express 1.0a) +     */ +    uint_reg_t r2                         : 1; +    /* +     * Undefined since PCI Express 1.1 (Was Power Indicator Present for PCI +     * Express 1.0a) +     */ +    uint_reg_t r3                         : 1; +    /* +     * Role-Based Error Reporting, writable through the MAC_STANDARD +     * interface.  Required to be set for device compliant to 1.1  spec and +     * later. +     */ +    uint_reg_t rer                        : 1; +    /* Reserved. */ +    uint_reg_t __reserved_1               : 2; +    /* Captured Slot Power Limit Value Upstream port only. */ +    uint_reg_t slot_pwr_lim               : 8; +    /* Captured Slot Power Limit Scale Upstream port only. */ +    uint_reg_t slot_pwr_scale             : 2; +    /* Reserved. */ +    uint_reg_t __reserved_2               : 4; +    /* Endpoint L0s Acceptable LatencyMust be 0x0 for non-Endpoint devices. */ +    uint_reg_t l0s_lat                    : 1; +    /* Reserved. */ +    uint_reg_t __reserved_3               : 31; +#else   /* __BIG_ENDIAN__ */ +    uint_reg_t __reserved_3               : 31; +    uint_reg_t l0s_lat                    : 1; +    uint_reg_t __reserved_2               : 4; +    uint_reg_t slot_pwr_scale             : 2; +    uint_reg_t slot_pwr_lim               : 8; +    uint_reg_t __reserved_1               : 2; +    uint_reg_t rer                        : 1; +    uint_reg_t r3                         : 1; +    uint_reg_t r2                         : 1; +    uint_reg_t r1                         : 1; +    uint_reg_t l1_lat                     : 3; +    uint_reg_t __reserved_0               : 3; +    uint_reg_t ext_tag_field_supported    : 1; +    uint_reg_t phantom_function_supported : 2; +    uint_reg_t mps_sup                    : 3; +#endif +  }; + +  uint_reg_t word; +} TRIO_PCIE_RC_DEVICE_CAP_t; + +/* Device Control Register. */ + +__extension__ +typedef union +{ +  struct +  { +#ifndef __BIG_ENDIAN__ +    /* Correctable Error Reporting Enable */ +    uint_reg_t cor_err_ena      : 1; +    /* Non-Fatal Error Reporting Enable */ +    uint_reg_t nf_err_ena       : 1; +    /* Fatal Error Reporting Enable */ +    uint_reg_t fatal_err_ena    : 1; +    /* Unsupported Request Reporting Enable */ +    uint_reg_t ur_ena           : 1; +    /* Relaxed orderring enable */ +    uint_reg_t ro_ena           : 1; +    /* Max Payload Size */ +    uint_reg_t max_payload_size : 3; +    /* Extended Tag Field Enable */ +    uint_reg_t ext_tag          : 1; +    /* Phantom Function Enable */ +    uint_reg_t ph_fn_ena        : 1; +    /* AUX Power PM Enable */ +    uint_reg_t aux_pm_ena       : 1; +    /* Enable NoSnoop */ +    uint_reg_t no_snoop         : 1; +    /* Max read request size */ +    uint_reg_t max_read_req_sz  : 3; +    /* Reserved. */ +    uint_reg_t __reserved       : 49; +#else   /* __BIG_ENDIAN__ */ +    uint_reg_t __reserved       : 49; +    uint_reg_t max_read_req_sz  : 3; +    uint_reg_t no_snoop         : 1; +    uint_reg_t aux_pm_ena       : 1; +    uint_reg_t ph_fn_ena        : 1; +    uint_reg_t ext_tag          : 1; +    uint_reg_t max_payload_size : 3; +    uint_reg_t ro_ena           : 1; +    uint_reg_t ur_ena           : 1; +    uint_reg_t fatal_err_ena    : 1; +    uint_reg_t nf_err_ena       : 1; +    uint_reg_t cor_err_ena      : 1; +#endif +  }; + +  uint_reg_t word; +} TRIO_PCIE_RC_DEVICE_CONTROL_t; +#endif /* !defined(__ASSEMBLER__) */ + +#endif /* !defined(__ARCH_TRIO_PCIE_RC_H__) */ diff --git a/arch/tile/include/arch/trio_pcie_rc_def.h b/arch/tile/include/arch/trio_pcie_rc_def.h new file mode 100644 index 00000000000..74081a65b6f --- /dev/null +++ b/arch/tile/include/arch/trio_pcie_rc_def.h @@ -0,0 +1,24 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ + +/* Machine-generated file; do not edit. */ + +#ifndef __ARCH_TRIO_PCIE_RC_DEF_H__ +#define __ARCH_TRIO_PCIE_RC_DEF_H__ +#define TRIO_PCIE_RC_DEVICE_CAP 0x0074 +#define TRIO_PCIE_RC_DEVICE_CONTROL 0x0078 +#define TRIO_PCIE_RC_DEVICE_ID_VEN_ID 0x0000 +#define TRIO_PCIE_RC_DEVICE_ID_VEN_ID__DEV_ID_SHIFT 16 +#define TRIO_PCIE_RC_REVISION_ID 0x0008 +#endif /* !defined(__ARCH_TRIO_PCIE_RC_DEF_H__) */ diff --git a/arch/tile/include/arch/trio_shm.h b/arch/tile/include/arch/trio_shm.h new file mode 100644 index 00000000000..3382e38245a --- /dev/null +++ b/arch/tile/include/arch/trio_shm.h @@ -0,0 +1,125 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ + +/* Machine-generated file; do not edit. */ + + +#ifndef __ARCH_TRIO_SHM_H__ +#define __ARCH_TRIO_SHM_H__ + +#include <arch/abi.h> +#include <arch/trio_shm_def.h> + +#ifndef __ASSEMBLER__ +/** + * TRIO DMA Descriptor. + * The TRIO DMA descriptor is written by software and consumed by hardware. + * It is used to specify the location of transaction data in the IO and Tile + * domains. + */ + +__extension__ +typedef union +{ +  struct +  { +    /* Word 0 */ + +#ifndef __BIG_ENDIAN__ +    /** Tile side virtual address. */ +    int_reg_t va           : 42; +    /** +     * Encoded size of buffer used on push DMA when C=1: +     * 0 = 128 bytes +     * 1 = 256 bytes +     * 2 = 512 bytes +     * 3 = 1024 bytes +     * 4 = 1664 bytes +     * 5 = 4096 bytes +     * 6 = 10368 bytes +     * 7 = 16384 bytes +     */ +    uint_reg_t bsz          : 3; +    /** +     * Chaining designation.  Always zero for pull DMA +     * 0 : Unchained buffer pointer +     * 1 : Chained buffer pointer.  Next buffer descriptor (e.g. VA) stored +     * in 1st 8-bytes in buffer.  For chained buffers, first 8-bytes of each +     * buffer contain the next buffer descriptor formatted exactly like a PDE +     * buffer descriptor.  This allows a chained PDE buffer to be sent using +     * push DMA. +     */ +    uint_reg_t c            : 1; +    /** +     * Notification interrupt will be delivered when the transaction has +     * completed (all data has been read from or written to the Tile-side +     * buffer). +     */ +    uint_reg_t notif        : 1; +    /** +     * When 0, the XSIZE field specifies the total byte count for the +     * transaction.  When 1, the XSIZE field is encoded as 2^(N+14) for N in +     * {0..6}: +     * 0 = 16KB +     * 1 = 32KB +     * 2 = 64KB +     * 3 = 128KB +     * 4 = 256KB +     * 5 = 512KB +     * 6 = 1MB +     * All other encodings of the XSIZE field are reserved when SMOD=1 +     */ +    uint_reg_t smod         : 1; +    /** +     * Total number of bytes to move for this transaction.   When SMOD=1, +     * this field is encoded - see SMOD description. +     */ +    uint_reg_t xsize        : 14; +    /** Reserved. */ +    uint_reg_t __reserved_0 : 1; +    /** +     * Generation number.  Used to indicate a valid descriptor in ring.  When +     * a new descriptor is written into the ring, software must toggle this +     * bit.  The net effect is that the GEN bit being written into new +     * descriptors toggles each time the ring tail pointer wraps. +     */ +    uint_reg_t gen          : 1; +#else   /* __BIG_ENDIAN__ */ +    uint_reg_t gen          : 1; +    uint_reg_t __reserved_0 : 1; +    uint_reg_t xsize        : 14; +    uint_reg_t smod         : 1; +    uint_reg_t notif        : 1; +    uint_reg_t c            : 1; +    uint_reg_t bsz          : 3; +    int_reg_t va           : 42; +#endif + +    /* Word 1 */ + +#ifndef __BIG_ENDIAN__ +    /** IO-side address */ +    uint_reg_t io_address : 64; +#else   /* __BIG_ENDIAN__ */ +    uint_reg_t io_address : 64; +#endif + +  }; + +  /** Word access */ +  uint_reg_t words[2]; +} TRIO_DMA_DESC_t; +#endif /* !defined(__ASSEMBLER__) */ + +#endif /* !defined(__ARCH_TRIO_SHM_H__) */ diff --git a/arch/tile/include/arch/trio_shm_def.h b/arch/tile/include/arch/trio_shm_def.h new file mode 100644 index 00000000000..72a59c88b06 --- /dev/null +++ b/arch/tile/include/arch/trio_shm_def.h @@ -0,0 +1,19 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ + +/* Machine-generated file; do not edit. */ + +#ifndef __ARCH_TRIO_SHM_DEF_H__ +#define __ARCH_TRIO_SHM_DEF_H__ +#endif /* !defined(__ARCH_TRIO_SHM_DEF_H__) */ diff --git a/arch/tile/include/arch/usb_host.h b/arch/tile/include/arch/usb_host.h new file mode 100644 index 00000000000..d09f3268396 --- /dev/null +++ b/arch/tile/include/arch/usb_host.h @@ -0,0 +1,26 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ + +/* Machine-generated file; do not edit. */ + +#ifndef __ARCH_USB_HOST_H__ +#define __ARCH_USB_HOST_H__ + +#include <arch/abi.h> +#include <arch/usb_host_def.h> + +#ifndef __ASSEMBLER__ +#endif /* !defined(__ASSEMBLER__) */ + +#endif /* !defined(__ARCH_USB_HOST_H__) */ diff --git a/arch/tile/include/arch/usb_host_def.h b/arch/tile/include/arch/usb_host_def.h new file mode 100644 index 00000000000..aeed7753e8e --- /dev/null +++ b/arch/tile/include/arch/usb_host_def.h @@ -0,0 +1,19 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ + +/* Machine-generated file; do not edit. */ + +#ifndef __ARCH_USB_HOST_DEF_H__ +#define __ARCH_USB_HOST_DEF_H__ +#endif /* !defined(__ARCH_USB_HOST_DEF_H__) */ diff --git a/arch/tile/include/asm/Kbuild b/arch/tile/include/asm/Kbuild index 143473e3a0b..fb7c65ae8de 100644 --- a/arch/tile/include/asm/Kbuild +++ b/arch/tile/include/asm/Kbuild @@ -9,7 +9,6 @@ header-y += hardwall.h  generic-y += bug.h  generic-y += bugs.h  generic-y += cputime.h -generic-y += device.h  generic-y += div64.h  generic-y += emergency-restart.h  generic-y += errno.h diff --git a/arch/tile/include/asm/cache.h b/arch/tile/include/asm/cache.h index 392e5333dd8..a9a529964e0 100644 --- a/arch/tile/include/asm/cache.h +++ b/arch/tile/include/asm/cache.h @@ -27,11 +27,17 @@  #define L2_CACHE_ALIGN(x)	(((x)+(L2_CACHE_BYTES-1)) & -L2_CACHE_BYTES)  /* - * TILE-Gx is fully coherent so we don't need to define ARCH_DMA_MINALIGN. + * TILEPro I/O is not always coherent (networking typically uses coherent + * I/O, but PCI traffic does not) and setting ARCH_DMA_MINALIGN to the + * L2 cacheline size helps ensure that kernel heap allocations are aligned. + * TILE-Gx I/O is always coherent when used on hash-for-home pages. + * + * However, it's possible at runtime to request not to use hash-for-home + * for the kernel heap, in which case the kernel will use flush-and-inval + * to manage coherence.  As a result, we use L2_CACHE_BYTES for the + * DMA minimum alignment to avoid false sharing in the kernel heap.   */ -#ifndef __tilegx__  #define ARCH_DMA_MINALIGN	L2_CACHE_BYTES -#endif  /* use the cache line size for the L2, which is where it counts */  #define SMP_CACHE_BYTES_SHIFT	L2_CACHE_SHIFT diff --git a/arch/tile/include/asm/checksum.h b/arch/tile/include/asm/checksum.h index a120766c726..b21a2fdec9f 100644 --- a/arch/tile/include/asm/checksum.h +++ b/arch/tile/include/asm/checksum.h @@ -21,4 +21,22 @@  __wsum do_csum(const unsigned char *buff, int len);  #define do_csum do_csum +/* + * Return the sum of all the 16-bit subwords in a long. + * This sums two subwords on a 32-bit machine, and four on 64 bits. + * The implementation does two vector adds to capture any overflow. + */ +static inline unsigned int csum_long(unsigned long x) +{ +	unsigned long ret; +#ifdef __tilegx__ +	ret = __insn_v2sadu(x, 0); +	ret = __insn_v2sadu(ret, 0); +#else +	ret = __insn_sadh_u(x, 0); +	ret = __insn_sadh_u(ret, 0); +#endif +	return ret; +} +  #endif /* _ASM_TILE_CHECKSUM_H */ diff --git a/arch/tile/include/asm/device.h b/arch/tile/include/asm/device.h new file mode 100644 index 00000000000..5182705bd05 --- /dev/null +++ b/arch/tile/include/asm/device.h @@ -0,0 +1,33 @@ +/* + * Copyright 2010 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + * Arch specific extensions to struct device + */ + +#ifndef _ASM_TILE_DEVICE_H +#define _ASM_TILE_DEVICE_H + +struct dev_archdata { +	/* DMA operations on that device */ +        struct dma_map_ops	*dma_ops; + +	/* Offset of the DMA address from the PA. */ +	dma_addr_t		dma_offset; + +	/* Highest DMA address that can be generated by this device. */ +	dma_addr_t		max_direct_dma_addr; +}; + +struct pdev_archdata { +}; + +#endif /* _ASM_TILE_DEVICE_H */ diff --git a/arch/tile/include/asm/dma-mapping.h b/arch/tile/include/asm/dma-mapping.h index eaa06d175b3..4b6247d1a31 100644 --- a/arch/tile/include/asm/dma-mapping.h +++ b/arch/tile/include/asm/dma-mapping.h @@ -20,69 +20,80 @@  #include <linux/cache.h>  #include <linux/io.h> -/* - * Note that on x86 and powerpc, there is a "struct dma_mapping_ops" - * that is used for all the DMA operations.  For now, we don't have an - * equivalent on tile, because we only have a single way of doing DMA. - * (Tilera bug 7994 to use dma_mapping_ops.) - */ +extern struct dma_map_ops *tile_dma_map_ops; +extern struct dma_map_ops *gx_pci_dma_map_ops; +extern struct dma_map_ops *gx_legacy_pci_dma_map_ops; + +static inline struct dma_map_ops *get_dma_ops(struct device *dev) +{ +	if (dev && dev->archdata.dma_ops) +		return dev->archdata.dma_ops; +	else +		return tile_dma_map_ops; +} + +static inline dma_addr_t get_dma_offset(struct device *dev) +{ +	return dev->archdata.dma_offset; +} + +static inline void set_dma_offset(struct device *dev, dma_addr_t off) +{ +	dev->archdata.dma_offset = off; +} -#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) +static inline dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr) +{ +	return paddr + get_dma_offset(dev); +} + +static inline phys_addr_t dma_to_phys(struct device *dev, dma_addr_t daddr) +{ +	return daddr - get_dma_offset(dev); +} -extern dma_addr_t dma_map_single(struct device *dev, void *ptr, size_t size, -			  enum dma_data_direction); -extern void dma_unmap_single(struct device *dev, dma_addr_t dma_addr, -			     size_t size, enum dma_data_direction); -extern int dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, -	       enum dma_data_direction); -extern void dma_unmap_sg(struct device *dev, struct scatterlist *sg, -			 int nhwentries, enum dma_data_direction); -extern dma_addr_t dma_map_page(struct device *dev, struct page *page, -			       unsigned long offset, size_t size, -			       enum dma_data_direction); -extern void dma_unmap_page(struct device *dev, dma_addr_t dma_address, -			   size_t size, enum dma_data_direction); -extern void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, -				int nelems, enum dma_data_direction); -extern void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, -				   int nelems, enum dma_data_direction); +static inline void dma_mark_clean(void *addr, size_t size) {} +#include <asm-generic/dma-mapping-common.h> -void *dma_alloc_coherent(struct device *dev, size_t size, -			   dma_addr_t *dma_handle, gfp_t flag); +static inline void set_dma_ops(struct device *dev, struct dma_map_ops *ops) +{ +	dev->archdata.dma_ops = ops; +} -void dma_free_coherent(struct device *dev, size_t size, -			 void *vaddr, dma_addr_t dma_handle); +static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size) +{ +	if (!dev->dma_mask) +		return 0; -extern void dma_sync_single_for_cpu(struct device *, dma_addr_t, size_t, -				    enum dma_data_direction); -extern void dma_sync_single_for_device(struct device *, dma_addr_t, -				       size_t, enum dma_data_direction); -extern void dma_sync_single_range_for_cpu(struct device *, dma_addr_t, -					  unsigned long offset, size_t, -					  enum dma_data_direction); -extern void dma_sync_single_range_for_device(struct device *, dma_addr_t, -					     unsigned long offset, size_t, -					     enum dma_data_direction); -extern void dma_cache_sync(struct device *dev, void *vaddr, size_t, -			   enum dma_data_direction); +	return addr + size - 1 <= *dev->dma_mask; +}  static inline int  dma_mapping_error(struct device *dev, dma_addr_t dma_addr)  { -	return 0; +	return get_dma_ops(dev)->mapping_error(dev, dma_addr);  }  static inline int  dma_supported(struct device *dev, u64 mask)  { -	return 1; +	return get_dma_ops(dev)->dma_supported(dev, mask);  }  static inline int  dma_set_mask(struct device *dev, u64 mask)  { +	struct dma_map_ops *dma_ops = get_dma_ops(dev); + +	/* Handle legacy PCI devices with limited memory addressability. */ +	if ((dma_ops == gx_pci_dma_map_ops) && (mask <= DMA_BIT_MASK(32))) { +		set_dma_ops(dev, gx_legacy_pci_dma_map_ops); +		set_dma_offset(dev, 0); +		if (mask > dev->archdata.max_direct_dma_addr) +			mask = dev->archdata.max_direct_dma_addr; +	} +  	if (!dev->dma_mask || !dma_supported(dev, mask))  		return -EIO; @@ -91,4 +102,43 @@ dma_set_mask(struct device *dev, u64 mask)  	return 0;  } +static inline void *dma_alloc_attrs(struct device *dev, size_t size, +				    dma_addr_t *dma_handle, gfp_t flag, +				    struct dma_attrs *attrs) +{ +	struct dma_map_ops *dma_ops = get_dma_ops(dev); +	void *cpu_addr; + +	cpu_addr = dma_ops->alloc(dev, size, dma_handle, flag, attrs); + +	debug_dma_alloc_coherent(dev, size, *dma_handle, cpu_addr); + +	return cpu_addr; +} + +static inline void dma_free_attrs(struct device *dev, size_t size, +				  void *cpu_addr, dma_addr_t dma_handle, +				  struct dma_attrs *attrs) +{ +	struct dma_map_ops *dma_ops = get_dma_ops(dev); + +	debug_dma_free_coherent(dev, size, cpu_addr, dma_handle); + +	dma_ops->free(dev, size, cpu_addr, dma_handle, attrs); +} + +#define dma_alloc_coherent(d, s, h, f) dma_alloc_attrs(d, s, h, f, NULL) +#define dma_alloc_noncoherent(d, s, h, f) dma_alloc_attrs(d, s, h, f, NULL) +#define dma_free_coherent(d, s, v, h) dma_free_attrs(d, s, v, h, NULL) +#define dma_free_noncoherent(d, s, v, h) dma_free_attrs(d, s, v, h, NULL) + +/* + * dma_alloc_noncoherent() is #defined to return coherent memory, + * so there's no need to do any flushing here. + */ +static inline void dma_cache_sync(struct device *dev, void *vaddr, size_t size, +				  enum dma_data_direction direction) +{ +} +  #endif /* _ASM_TILE_DMA_MAPPING_H */ diff --git a/arch/tile/include/asm/fixmap.h b/arch/tile/include/asm/fixmap.h index c66f7933bea..e16dbf929cb 100644 --- a/arch/tile/include/asm/fixmap.h +++ b/arch/tile/include/asm/fixmap.h @@ -45,15 +45,23 @@   *   * TLB entries of such buffers will not be flushed across   * task switches. - * - * We don't bother with a FIX_HOLE since above the fixmaps - * is unmapped memory in any case.   */  enum fixed_addresses { +#ifdef __tilegx__ +	/* +	 * TILEPro has unmapped memory above so the hole isn't needed, +	 * and in any case the hole pushes us over a single 16MB pmd. +	 */ +	FIX_HOLE, +#endif  #ifdef CONFIG_HIGHMEM  	FIX_KMAP_BEGIN,	/* reserved pte's for temporary kernel mappings */  	FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1,  #endif +#ifdef __tilegx__  /* see homecache.c */ +	FIX_HOMECACHE_BEGIN, +	FIX_HOMECACHE_END = FIX_HOMECACHE_BEGIN+(NR_CPUS)-1, +#endif  	__end_of_permanent_fixed_addresses,  	/* diff --git a/arch/tile/include/asm/homecache.h b/arch/tile/include/asm/homecache.h index a8243865d49..7b777132864 100644 --- a/arch/tile/include/asm/homecache.h +++ b/arch/tile/include/asm/homecache.h @@ -79,10 +79,17 @@ extern void homecache_change_page_home(struct page *, int order, int home);  /*   * Flush a page out of whatever cache(s) it is in.   * This is more than just finv, since it properly handles waiting - * for the data to reach memory on tilepro, but it can be quite - * heavyweight, particularly on hash-for-home memory. + * for the data to reach memory, but it can be quite + * heavyweight, particularly on incoherent or immutable memory.   */ -extern void homecache_flush_cache(struct page *, int order); +extern void homecache_finv_page(struct page *); + +/* + * Flush a page out of the specified home cache. + * Note that the specified home need not be the actual home of the page, + * as for example might be the case when coordinating with I/O devices. + */ +extern void homecache_finv_map_page(struct page *, int home);  /*   * Allocate a page with the given GFP flags, home, and optionally @@ -104,10 +111,10 @@ extern struct page *homecache_alloc_pages_node(int nid, gfp_t gfp_mask,   * routines use homecache_change_page_home() to reset the home   * back to the default before returning the page to the allocator.   */ +void __homecache_free_pages(struct page *, unsigned int order);  void homecache_free_pages(unsigned long addr, unsigned int order); -#define homecache_free_page(page) \ -  homecache_free_pages((page), 0) - +#define __homecache_free_page(page) __homecache_free_pages((page), 0) +#define homecache_free_page(page) homecache_free_pages((page), 0)  /* diff --git a/arch/tile/include/asm/io.h b/arch/tile/include/asm/io.h index d2152deb1f3..2a9b293fece 100644 --- a/arch/tile/include/asm/io.h +++ b/arch/tile/include/asm/io.h @@ -62,6 +62,92 @@ extern void iounmap(volatile void __iomem *addr);  #define mm_ptov(addr)		((void *)phys_to_virt(addr))  #define mm_vtop(addr)		((unsigned long)virt_to_phys(addr)) +#if CHIP_HAS_MMIO() + +/* + * We use inline assembly to guarantee that the compiler does not + * split an access into multiple byte-sized accesses as it might + * sometimes do if a register data structure is marked "packed". + * Obviously on tile we can't tolerate such an access being + * actually unaligned, but we want to avoid the case where the + * compiler conservatively would generate multiple accesses even + * for an aligned read or write. + */ + +static inline u8 __raw_readb(const volatile void __iomem *addr) +{ +	return *(const volatile u8 __force *)addr; +} + +static inline u16 __raw_readw(const volatile void __iomem *addr) +{ +	u16 ret; +	asm volatile("ld2u %0, %1" : "=r" (ret) : "r" (addr)); +	barrier(); +	return le16_to_cpu(ret); +} + +static inline u32 __raw_readl(const volatile void __iomem *addr) +{ +	u32 ret; +	/* Sign-extend to conform to u32 ABI sign-extension convention. */ +	asm volatile("ld4s %0, %1" : "=r" (ret) : "r" (addr)); +	barrier(); +	return le32_to_cpu(ret); +} + +static inline u64 __raw_readq(const volatile void __iomem *addr) +{ +	u64 ret; +	asm volatile("ld %0, %1" : "=r" (ret) : "r" (addr)); +	barrier(); +	return le64_to_cpu(ret); +} + +static inline void __raw_writeb(u8 val, volatile void __iomem *addr) +{ +	*(volatile u8 __force *)addr = val; +} + +static inline void __raw_writew(u16 val, volatile void __iomem *addr) +{ +	asm volatile("st2 %0, %1" :: "r" (addr), "r" (cpu_to_le16(val))); +} + +static inline void __raw_writel(u32 val, volatile void __iomem *addr) +{ +	asm volatile("st4 %0, %1" :: "r" (addr), "r" (cpu_to_le32(val))); +} + +static inline void __raw_writeq(u64 val, volatile void __iomem *addr) +{ +	asm volatile("st %0, %1" :: "r" (addr), "r" (cpu_to_le64(val))); +} + +/* + * The on-chip I/O hardware on tilegx is configured with VA=PA for the + * kernel's PA range.  The low-level APIs and field names use "va" and + * "void *" nomenclature, to be consistent with the general notion + * that the addresses in question are virtualizable, but in the kernel + * context we are actually manipulating PA values.  (In other contexts, + * e.g. access from user space, we do in fact use real virtual addresses + * in the va fields.)  To allow readers of the code to understand what's + * happening, we direct their attention to this comment by using the + * following two functions that just duplicate __va() and __pa(). + */ +typedef unsigned long tile_io_addr_t; +static inline tile_io_addr_t va_to_tile_io_addr(void *va) +{ +	BUILD_BUG_ON(sizeof(phys_addr_t) != sizeof(tile_io_addr_t)); +	return __pa(va); +} +static inline void *tile_io_addr_to_va(tile_io_addr_t tile_io_addr) +{ +	return __va(tile_io_addr); +} + +#else /* CHIP_HAS_MMIO() */ +  #ifdef CONFIG_PCI  extern u8 _tile_readb(unsigned long addr); @@ -73,10 +159,19 @@ extern void _tile_writew(u16 val, unsigned long addr);  extern void _tile_writel(u32 val, unsigned long addr);  extern void _tile_writeq(u64 val, unsigned long addr); -#else +#define __raw_readb(addr) _tile_readb((unsigned long)addr) +#define __raw_readw(addr) _tile_readw((unsigned long)addr) +#define __raw_readl(addr) _tile_readl((unsigned long)addr) +#define __raw_readq(addr) _tile_readq((unsigned long)addr) +#define __raw_writeb(val, addr) _tile_writeb(val, (unsigned long)addr) +#define __raw_writew(val, addr) _tile_writew(val, (unsigned long)addr) +#define __raw_writel(val, addr) _tile_writel(val, (unsigned long)addr) +#define __raw_writeq(val, addr) _tile_writeq(val, (unsigned long)addr) + +#else /* CONFIG_PCI */  /* - * The Tile architecture does not support IOMEM unless PCI is enabled. + * The tilepro architecture does not support IOMEM unless PCI is enabled.   * Unfortunately we can't yet simply not declare these methods,   * since some generic code that compiles into the kernel, but   * we never run, uses them unconditionally. @@ -88,65 +183,58 @@ static inline int iomem_panic(void)  	return 0;  } -static inline u8 _tile_readb(unsigned long addr) +static inline u8 readb(unsigned long addr)  {  	return iomem_panic();  } -static inline u16 _tile_readw(unsigned long addr) +static inline u16 _readw(unsigned long addr)  {  	return iomem_panic();  } -static inline u32 _tile_readl(unsigned long addr) +static inline u32 readl(unsigned long addr)  {  	return iomem_panic();  } -static inline u64 _tile_readq(unsigned long addr) +static inline u64 readq(unsigned long addr)  {  	return iomem_panic();  } -static inline void _tile_writeb(u8  val, unsigned long addr) +static inline void writeb(u8  val, unsigned long addr)  {  	iomem_panic();  } -static inline void _tile_writew(u16 val, unsigned long addr) +static inline void writew(u16 val, unsigned long addr)  {  	iomem_panic();  } -static inline void _tile_writel(u32 val, unsigned long addr) +static inline void writel(u32 val, unsigned long addr)  {  	iomem_panic();  } -static inline void _tile_writeq(u64 val, unsigned long addr) +static inline void writeq(u64 val, unsigned long addr)  {  	iomem_panic();  } -#endif +#endif /* CONFIG_PCI */ -#define readb(addr) _tile_readb((unsigned long)addr) -#define readw(addr) _tile_readw((unsigned long)addr) -#define readl(addr) _tile_readl((unsigned long)addr) -#define readq(addr) _tile_readq((unsigned long)addr) -#define writeb(val, addr) _tile_writeb(val, (unsigned long)addr) -#define writew(val, addr) _tile_writew(val, (unsigned long)addr) -#define writel(val, addr) _tile_writel(val, (unsigned long)addr) -#define writeq(val, addr) _tile_writeq(val, (unsigned long)addr) +#endif /* CHIP_HAS_MMIO() */ -#define __raw_readb readb -#define __raw_readw readw -#define __raw_readl readl -#define __raw_readq readq -#define __raw_writeb writeb -#define __raw_writew writew -#define __raw_writel writel -#define __raw_writeq writeq +#define readb __raw_readb +#define readw __raw_readw +#define readl __raw_readl +#define readq __raw_readq +#define writeb __raw_writeb +#define writew __raw_writew +#define writel __raw_writel +#define writeq __raw_writeq  #define readb_relaxed readb  #define readw_relaxed readw diff --git a/arch/tile/include/asm/memprof.h b/arch/tile/include/asm/memprof.h deleted file mode 100644 index 359949be28c..00000000000 --- a/arch/tile/include/asm/memprof.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2010 Tilera Corporation. All Rights Reserved. - * - *   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, version 2. - * - *   This program is distributed in the hope that it will be useful, but - *   WITHOUT ANY WARRANTY; without even the implied warranty of - *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or - *   NON INFRINGEMENT.  See the GNU General Public License for - *   more details. - * - * The hypervisor's memory controller profiling infrastructure allows - * the programmer to find out what fraction of the available memory - * bandwidth is being consumed at each memory controller.  The - * profiler provides start, stop, and clear operations to allows - * profiling over a specific time window, as well as an interface for - * reading the most recent profile values. - * - * This header declares IOCTL codes necessary to control memprof. - */ -#ifndef _ASM_TILE_MEMPROF_H -#define _ASM_TILE_MEMPROF_H - -#include <linux/ioctl.h> - -#define MEMPROF_IOCTL_TYPE 0xB4 -#define MEMPROF_IOCTL_START _IO(MEMPROF_IOCTL_TYPE, 0) -#define MEMPROF_IOCTL_STOP _IO(MEMPROF_IOCTL_TYPE, 1) -#define MEMPROF_IOCTL_CLEAR _IO(MEMPROF_IOCTL_TYPE, 2) - -#endif /* _ASM_TILE_MEMPROF_H */ diff --git a/arch/tile/include/asm/page.h b/arch/tile/include/asm/page.h index 9d9131e5c55..dd033a4fd62 100644 --- a/arch/tile/include/asm/page.h +++ b/arch/tile/include/asm/page.h @@ -174,7 +174,9 @@ static inline __attribute_const__ int get_order(unsigned long size)  #define MEM_LOW_END		(HALF_VA_SPACE - 1)         /* low half */  #define MEM_HIGH_START		(-HALF_VA_SPACE)            /* high half */  #define PAGE_OFFSET		MEM_HIGH_START -#define _VMALLOC_START		_AC(0xfffffff500000000, UL) /* 4 GB */ +#define FIXADDR_BASE		_AC(0xfffffff400000000, UL) /* 4 GB */ +#define FIXADDR_TOP		_AC(0xfffffff500000000, UL) /* 4 GB */ +#define _VMALLOC_START		FIXADDR_TOP  #define HUGE_VMAP_BASE		_AC(0xfffffff600000000, UL) /* 4 GB */  #define MEM_SV_START		_AC(0xfffffff700000000, UL) /* 256 MB */  #define MEM_SV_INTRPT		MEM_SV_START @@ -185,9 +187,6 @@ static inline __attribute_const__ int get_order(unsigned long size)  /* Highest DTLB address we will use */  #define KERNEL_HIGH_VADDR	MEM_SV_START -/* Since we don't currently provide any fixmaps, we use an impossible VA. */ -#define FIXADDR_TOP             MEM_HV_START -  #else /* !__tilegx__ */  /* diff --git a/arch/tile/include/asm/pci.h b/arch/tile/include/asm/pci.h index 32e6cbe8dff..302cdf71cee 100644 --- a/arch/tile/include/asm/pci.h +++ b/arch/tile/include/asm/pci.h @@ -15,9 +15,13 @@  #ifndef _ASM_TILE_PCI_H  #define _ASM_TILE_PCI_H +#include <linux/dma-mapping.h>  #include <linux/pci.h> +#include <linux/numa.h>  #include <asm-generic/pci_iomap.h> +#ifndef __tilegx__ +  /*   * Structure of a PCI controller (host bridge)   */ @@ -41,21 +45,151 @@ struct pci_controller {  };  /* + * This flag tells if the platform is TILEmpower that needs + * special configuration for the PLX switch chip. + */ +extern int tile_plx_gen1; + +static inline void pci_iounmap(struct pci_dev *dev, void __iomem *addr) {} + +#define	TILE_NUM_PCIE	2 + +/*   * The hypervisor maps the entirety of CPA-space as bus addresses, so   * bus addresses are physical addresses.  The networking and block   * device layers use this boolean for bounce buffer decisions.   */  #define PCI_DMA_BUS_IS_PHYS     1 +/* generic pci stuff */ +#include <asm-generic/pci.h> + +#else + +#include <asm/page.h> +#include <gxio/trio.h> + +/** + * We reserve the hugepage-size address range at the top of the 64-bit address + * space to serve as the PCI window, emulating the BAR0 space of an endpoint + * device. This window is used by the chip-to-chip applications running on + * the RC node. The reason for carving out this window is that Mem-Maps that + * back up this window will not overlap with those that map the real physical + * memory. + */ +#define PCIE_HOST_BAR0_SIZE		HPAGE_SIZE +#define PCIE_HOST_BAR0_START		HPAGE_MASK + +/** + * The first PAGE_SIZE of the above "BAR" window is mapped to the + * gxpci_host_regs structure. + */ +#define PCIE_HOST_REGS_SIZE		PAGE_SIZE + +/* + * This is the PCI address where the Mem-Map interrupt regions start. + * We use the 2nd to the last huge page of the 64-bit address space. + * The last huge page is used for the rootcomplex "bar", for C2C purpose. + */ +#define	MEM_MAP_INTR_REGIONS_BASE	(HPAGE_MASK - HPAGE_SIZE) + +/* + * Each Mem-Map interrupt region occupies 4KB. + */ +#define	MEM_MAP_INTR_REGION_SIZE	(1 << TRIO_MAP_MEM_LIM__ADDR_SHIFT) + +/* + * Allocate the PCI BAR window right below 4GB. + */ +#define	TILE_PCI_BAR_WINDOW_TOP		(1ULL << 32) + +/* + * Allocate 1GB for the PCI BAR window. + */ +#define	TILE_PCI_BAR_WINDOW_SIZE	(1 << 30) + +/* + * This is the highest bus address targeting the host memory that + * can be generated by legacy PCI devices with 32-bit or less + * DMA capability, dictated by the BAR window size and location. + */ +#define	TILE_PCI_MAX_DIRECT_DMA_ADDRESS \ +	(TILE_PCI_BAR_WINDOW_TOP - TILE_PCI_BAR_WINDOW_SIZE - 1) + +/* + * We shift the PCI bus range for all the physical memory up by the whole PA + * range. The corresponding CPA of an incoming PCI request will be the PCI + * address minus TILE_PCI_MEM_MAP_BASE_OFFSET. This also implies + * that the 64-bit capable devices will be given DMA addresses as + * the CPA plus TILE_PCI_MEM_MAP_BASE_OFFSET. To support 32-bit + * devices, we create a separate map region that handles the low + * 4GB. + */ +#define	TILE_PCI_MEM_MAP_BASE_OFFSET	(1ULL << CHIP_PA_WIDTH()) + +/* + * Start of the PCI memory resource, which starts at the end of the + * maximum system physical RAM address. + */ +#define	TILE_PCI_MEM_START	(1ULL << CHIP_PA_WIDTH()) + +/* + * Structure of a PCI controller (host bridge) on Gx. + */ +struct pci_controller { + +	/* Pointer back to the TRIO that this PCIe port is connected to. */ +	gxio_trio_context_t *trio; +	int mac;		/* PCIe mac index on the TRIO shim */ +	int trio_index;		/* Index of TRIO shim that contains the MAC. */ + +	int pio_mem_index;	/* PIO region index for memory access */ + +	/* +	 * Mem-Map regions for all the memory controllers so that Linux can +	 * map all of its physical memory space to the PCI bus. +	 */ +	int mem_maps[MAX_NUMNODES]; + +	int index;		/* PCI domain number */ +	struct pci_bus *root_bus; + +	/* PCI memory space resource for this controller. */ +	struct resource mem_space; +	char mem_space_name[32]; + +	uint64_t mem_offset;	/* cpu->bus memory mapping offset. */ + +	int first_busno; + +	struct pci_ops *ops; + +	/* Table that maps the INTx numbers to Linux irq numbers. */ +	int irq_intx_table[4]; + +	/* Address ranges that are routed to this controller/bridge. */ +	struct resource mem_resources[3]; +}; + +extern struct pci_controller pci_controllers[TILEGX_NUM_TRIO * TILEGX_TRIO_PCIES]; +extern gxio_trio_context_t trio_contexts[TILEGX_NUM_TRIO]; + +extern void pci_iounmap(struct pci_dev *dev, void __iomem *); + +/* + * The PCI address space does not equal the physical memory address + * space (we have an IOMMU). The IDE and SCSI device layers use this + * boolean for bounce buffer decisions. + */ +#define PCI_DMA_BUS_IS_PHYS     0 + +#endif /* __tilegx__ */ +  int __init tile_pci_init(void);  int __init pcibios_init(void); -static inline void pci_iounmap(struct pci_dev *dev, void __iomem *addr) {} -  void __devinit pcibios_fixup_bus(struct pci_bus *bus); -#define	TILE_NUM_PCIE	2 -  #define pci_domain_nr(bus) (((struct pci_controller *)(bus)->sysdata)->index)  /* @@ -79,19 +213,10 @@ static inline int pcibios_assign_all_busses(void)  #define PCIBIOS_MIN_MEM		0  #define PCIBIOS_MIN_IO		0 -/* - * This flag tells if the platform is TILEmpower that needs - * special configuration for the PLX switch chip. - */ -extern int tile_plx_gen1; -  /* Use any cpu for PCI. */  #define cpumask_of_pcibus(bus) cpu_online_mask  /* implement the pci_ DMA API in terms of the generic device dma_ one */  #include <asm-generic/pci-dma-compat.h> -/* generic pci stuff */ -#include <asm-generic/pci.h> -  #endif /* _ASM_TILE_PCI_H */ diff --git a/arch/tile/include/gxio/common.h b/arch/tile/include/gxio/common.h new file mode 100644 index 00000000000..724595a24d0 --- /dev/null +++ b/arch/tile/include/gxio/common.h @@ -0,0 +1,40 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ + +#ifndef _GXIO_COMMON_H_ +#define _GXIO_COMMON_H_ + +/* + * Routines shared between the various GXIO device components. + */ + +#include <hv/iorpc.h> + +#include <linux/types.h> +#include <linux/compiler.h> +#include <linux/io.h> + +/* Define the standard gxio MMIO functions using kernel functions. */ +#define __gxio_mmio_read8(addr)		readb(addr) +#define __gxio_mmio_read16(addr)	readw(addr) +#define __gxio_mmio_read32(addr)	readl(addr) +#define __gxio_mmio_read64(addr)	readq(addr) +#define __gxio_mmio_write8(addr, val)	writeb((val), (addr)) +#define __gxio_mmio_write16(addr, val)	writew((val), (addr)) +#define __gxio_mmio_write32(addr, val)	writel((val), (addr)) +#define __gxio_mmio_write64(addr, val)	writeq((val), (addr)) +#define __gxio_mmio_read(addr)		__gxio_mmio_read64(addr) +#define __gxio_mmio_write(addr, val)	__gxio_mmio_write64((addr), (val)) + +#endif /* !_GXIO_COMMON_H_ */ diff --git a/arch/tile/include/gxio/dma_queue.h b/arch/tile/include/gxio/dma_queue.h new file mode 100644 index 00000000000..00654feb7db --- /dev/null +++ b/arch/tile/include/gxio/dma_queue.h @@ -0,0 +1,161 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ + +#ifndef _GXIO_DMA_QUEUE_H_ +#define _GXIO_DMA_QUEUE_H_ + +/* + * DMA queue management APIs shared between TRIO and mPIPE. + */ + +#include "common.h" + +/* The credit counter lives in the high 32 bits. */ +#define DMA_QUEUE_CREDIT_SHIFT 32 + +/* + * State object that tracks a DMA queue's head and tail indices, as + * well as the number of commands posted and completed.  The + * structure is accessed via a thread-safe, lock-free algorithm. + */ +typedef struct { +	/* +	 * Address of a MPIPE_EDMA_POST_REGION_VAL_t, +	 * TRIO_PUSH_DMA_REGION_VAL_t, or TRIO_PULL_DMA_REGION_VAL_t +	 * register.  These register have identical encodings and provide +	 * information about how many commands have been processed. +	 */ +	void *post_region_addr; + +	/* +	 * A lazily-updated count of how many edescs the hardware has +	 * completed. +	 */ +	uint64_t hw_complete_count __attribute__ ((aligned(64))); + +	/* +	 * High 32 bits are a count of available egress command credits, +	 * low 24 bits are the next egress "slot". +	 */ +	int64_t credits_and_next_index; + +} __gxio_dma_queue_t; + +/* Initialize a dma queue. */ +extern void __gxio_dma_queue_init(__gxio_dma_queue_t *dma_queue, +				  void *post_region_addr, +				  unsigned int num_entries); + +/* + * Update the "credits_and_next_index" and "hw_complete_count" fields + * based on pending hardware completions.  Note that some other thread + * may have already done this and, importantly, may still be in the + * process of updating "credits_and_next_index". + */ +extern void __gxio_dma_queue_update_credits(__gxio_dma_queue_t *dma_queue); + +/* Wait for credits to become available. */ +extern int64_t __gxio_dma_queue_wait_for_credits(__gxio_dma_queue_t *dma_queue, +						 int64_t modifier); + +/* Reserve slots in the queue, optionally waiting for slots to become + * available, and optionally returning a "completion_slot" suitable for + * direct comparison to "hw_complete_count". + */ +static inline int64_t __gxio_dma_queue_reserve(__gxio_dma_queue_t *dma_queue, +					       unsigned int num, bool wait, +					       bool completion) +{ +	uint64_t slot; + +	/* +	 * Try to reserve 'num' egress command slots.  We do this by +	 * constructing a constant that subtracts N credits and adds N to +	 * the index, and using fetchaddgez to only apply it if the credits +	 * count doesn't go negative. +	 */ +	int64_t modifier = (((int64_t)(-num)) << DMA_QUEUE_CREDIT_SHIFT) | num; +	int64_t old = +		__insn_fetchaddgez(&dma_queue->credits_and_next_index, +				   modifier); + +	if (unlikely(old + modifier < 0)) { +		/* +		 * We're out of credits.  Try once to get more by checking for +		 * completed egress commands.  If that fails, wait or fail. +		 */ +		__gxio_dma_queue_update_credits(dma_queue); +		old = __insn_fetchaddgez(&dma_queue->credits_and_next_index, +					 modifier); +		if (old + modifier < 0) { +			if (wait) +				old = __gxio_dma_queue_wait_for_credits +					(dma_queue, modifier); +			else +				return GXIO_ERR_DMA_CREDITS; +		} +	} + +	/* The bottom 24 bits of old encode the "slot". */ +	slot = (old & 0xffffff); + +	if (completion) { +		/* +		 * A "completion_slot" is a "slot" which can be compared to +		 * "hw_complete_count" at any time in the future.  To convert +		 * "slot" into a "completion_slot", we access "hw_complete_count" +		 * once (knowing that we have reserved a slot, and thus, it will +		 * be "basically" accurate), and combine its high 40 bits with +		 * the 24 bit "slot", and handle "wrapping" by adding "1 << 24" +		 * if the result is LESS than "hw_complete_count". +		 */ +		uint64_t complete; +		complete = ACCESS_ONCE(dma_queue->hw_complete_count); +		slot |= (complete & 0xffffffffff000000); +		if (slot < complete) +			slot += 0x1000000; +	} + +	/* +	 * If any of our slots mod 256 were equivalent to 0, go ahead and +	 * collect some egress credits, and update "hw_complete_count", and +	 * make sure the index doesn't overflow into the credits. +	 */ +	if (unlikely(((old + num) & 0xff) < num)) { +		__gxio_dma_queue_update_credits(dma_queue); + +		/* Make sure the index doesn't overflow into the credits. */ +#ifdef __BIG_ENDIAN__ +		*(((uint8_t *)&dma_queue->credits_and_next_index) + 4) = 0; +#else +		*(((uint8_t *)&dma_queue->credits_and_next_index) + 3) = 0; +#endif +	} + +	return slot; +} + +/* Non-inlinable "__gxio_dma_queue_reserve(..., true)". */ +extern int64_t __gxio_dma_queue_reserve_aux(__gxio_dma_queue_t *dma_queue, +					    unsigned int num, int wait); + +/* Check whether a particular "completion slot" has completed. + * + * Note that this function requires a "completion slot", and thus + * cannot be used with the result of any "reserve_fast" function. + */ +extern int __gxio_dma_queue_is_complete(__gxio_dma_queue_t *dma_queue, +					int64_t completion_slot, int update); + +#endif /* !_GXIO_DMA_QUEUE_H_ */ diff --git a/arch/tile/include/gxio/iorpc_globals.h b/arch/tile/include/gxio/iorpc_globals.h new file mode 100644 index 00000000000..52c721f8dad --- /dev/null +++ b/arch/tile/include/gxio/iorpc_globals.h @@ -0,0 +1,38 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ + +/* This file is machine-generated; DO NOT EDIT! */ +#ifndef __IORPC_LINUX_RPC_H__ +#define __IORPC_LINUX_RPC_H__ + +#include <hv/iorpc.h> + +#include <linux/string.h> +#include <linux/module.h> +#include <asm/pgtable.h> + +#define IORPC_OP_ARM_POLLFD            IORPC_OPCODE(IORPC_FORMAT_KERNEL_POLLFD, 0x9000) +#define IORPC_OP_CLOSE_POLLFD          IORPC_OPCODE(IORPC_FORMAT_KERNEL_POLLFD, 0x9001) +#define IORPC_OP_GET_MMIO_BASE         IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x8000) +#define IORPC_OP_CHECK_MMIO_OFFSET     IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x8001) + +int __iorpc_arm_pollfd(int fd, int pollfd_cookie); + +int __iorpc_close_pollfd(int fd, int pollfd_cookie); + +int __iorpc_get_mmio_base(int fd, HV_PTE *base); + +int __iorpc_check_mmio_offset(int fd, unsigned long offset, unsigned long size); + +#endif /* !__IORPC_LINUX_RPC_H__ */ diff --git a/arch/tile/include/gxio/iorpc_mpipe.h b/arch/tile/include/gxio/iorpc_mpipe.h new file mode 100644 index 00000000000..9d50fce1b1a --- /dev/null +++ b/arch/tile/include/gxio/iorpc_mpipe.h @@ -0,0 +1,136 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ + +/* This file is machine-generated; DO NOT EDIT! */ +#ifndef __GXIO_MPIPE_LINUX_RPC_H__ +#define __GXIO_MPIPE_LINUX_RPC_H__ + +#include <hv/iorpc.h> + +#include <hv/drv_mpipe_intf.h> +#include <asm/page.h> +#include <gxio/kiorpc.h> +#include <gxio/mpipe.h> +#include <linux/string.h> +#include <linux/module.h> +#include <asm/pgtable.h> + +#define GXIO_MPIPE_OP_ALLOC_BUFFER_STACKS IORPC_OPCODE(IORPC_FORMAT_NONE, 0x1200) +#define GXIO_MPIPE_OP_INIT_BUFFER_STACK_AUX IORPC_OPCODE(IORPC_FORMAT_KERNEL_MEM, 0x1201) + +#define GXIO_MPIPE_OP_ALLOC_NOTIF_RINGS IORPC_OPCODE(IORPC_FORMAT_NONE, 0x1203) +#define GXIO_MPIPE_OP_INIT_NOTIF_RING_AUX IORPC_OPCODE(IORPC_FORMAT_KERNEL_MEM, 0x1204) +#define GXIO_MPIPE_OP_REQUEST_NOTIF_RING_INTERRUPT IORPC_OPCODE(IORPC_FORMAT_KERNEL_INTERRUPT, 0x1205) +#define GXIO_MPIPE_OP_ENABLE_NOTIF_RING_INTERRUPT IORPC_OPCODE(IORPC_FORMAT_NONE, 0x1206) +#define GXIO_MPIPE_OP_ALLOC_NOTIF_GROUPS IORPC_OPCODE(IORPC_FORMAT_NONE, 0x1207) +#define GXIO_MPIPE_OP_INIT_NOTIF_GROUP IORPC_OPCODE(IORPC_FORMAT_NONE, 0x1208) +#define GXIO_MPIPE_OP_ALLOC_BUCKETS    IORPC_OPCODE(IORPC_FORMAT_NONE, 0x1209) +#define GXIO_MPIPE_OP_INIT_BUCKET      IORPC_OPCODE(IORPC_FORMAT_NONE, 0x120a) +#define GXIO_MPIPE_OP_ALLOC_EDMA_RINGS IORPC_OPCODE(IORPC_FORMAT_NONE, 0x120b) +#define GXIO_MPIPE_OP_INIT_EDMA_RING_AUX IORPC_OPCODE(IORPC_FORMAT_KERNEL_MEM, 0x120c) + +#define GXIO_MPIPE_OP_COMMIT_RULES     IORPC_OPCODE(IORPC_FORMAT_NONE, 0x120f) +#define GXIO_MPIPE_OP_REGISTER_CLIENT_MEMORY IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x1210) +#define GXIO_MPIPE_OP_LINK_OPEN_AUX    IORPC_OPCODE(IORPC_FORMAT_NONE, 0x1211) +#define GXIO_MPIPE_OP_LINK_CLOSE_AUX   IORPC_OPCODE(IORPC_FORMAT_NONE, 0x1212) + +#define GXIO_MPIPE_OP_GET_TIMESTAMP_AUX IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x121e) +#define GXIO_MPIPE_OP_SET_TIMESTAMP_AUX IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x121f) +#define GXIO_MPIPE_OP_ADJUST_TIMESTAMP_AUX IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x1220) +#define GXIO_MPIPE_OP_ARM_POLLFD       IORPC_OPCODE(IORPC_FORMAT_KERNEL_POLLFD, 0x9000) +#define GXIO_MPIPE_OP_CLOSE_POLLFD     IORPC_OPCODE(IORPC_FORMAT_KERNEL_POLLFD, 0x9001) +#define GXIO_MPIPE_OP_GET_MMIO_BASE    IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x8000) +#define GXIO_MPIPE_OP_CHECK_MMIO_OFFSET IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x8001) + +int gxio_mpipe_alloc_buffer_stacks(gxio_mpipe_context_t * context, +				   unsigned int count, unsigned int first, +				   unsigned int flags); + +int gxio_mpipe_init_buffer_stack_aux(gxio_mpipe_context_t * context, +				     void *mem_va, size_t mem_size, +				     unsigned int mem_flags, unsigned int stack, +				     unsigned int buffer_size_enum); + + +int gxio_mpipe_alloc_notif_rings(gxio_mpipe_context_t * context, +				 unsigned int count, unsigned int first, +				 unsigned int flags); + +int gxio_mpipe_init_notif_ring_aux(gxio_mpipe_context_t * context, void *mem_va, +				   size_t mem_size, unsigned int mem_flags, +				   unsigned int ring); + +int gxio_mpipe_request_notif_ring_interrupt(gxio_mpipe_context_t * context, +					    int inter_x, int inter_y, +					    int inter_ipi, int inter_event, +					    unsigned int ring); + +int gxio_mpipe_enable_notif_ring_interrupt(gxio_mpipe_context_t * context, +					   unsigned int ring); + +int gxio_mpipe_alloc_notif_groups(gxio_mpipe_context_t * context, +				  unsigned int count, unsigned int first, +				  unsigned int flags); + +int gxio_mpipe_init_notif_group(gxio_mpipe_context_t * context, +				unsigned int group, +				gxio_mpipe_notif_group_bits_t bits); + +int gxio_mpipe_alloc_buckets(gxio_mpipe_context_t * context, unsigned int count, +			     unsigned int first, unsigned int flags); + +int gxio_mpipe_init_bucket(gxio_mpipe_context_t * context, unsigned int bucket, +			   MPIPE_LBL_INIT_DAT_BSTS_TBL_t bucket_info); + +int gxio_mpipe_alloc_edma_rings(gxio_mpipe_context_t * context, +				unsigned int count, unsigned int first, +				unsigned int flags); + +int gxio_mpipe_init_edma_ring_aux(gxio_mpipe_context_t * context, void *mem_va, +				  size_t mem_size, unsigned int mem_flags, +				  unsigned int ring, unsigned int channel); + + +int gxio_mpipe_commit_rules(gxio_mpipe_context_t * context, const void *blob, +			    size_t blob_size); + +int gxio_mpipe_register_client_memory(gxio_mpipe_context_t * context, +				      unsigned int iotlb, HV_PTE pte, +				      unsigned int flags); + +int gxio_mpipe_link_open_aux(gxio_mpipe_context_t * context, +			     _gxio_mpipe_link_name_t name, unsigned int flags); + +int gxio_mpipe_link_close_aux(gxio_mpipe_context_t * context, int mac); + + +int gxio_mpipe_get_timestamp_aux(gxio_mpipe_context_t * context, uint64_t * sec, +				 uint64_t * nsec, uint64_t * cycles); + +int gxio_mpipe_set_timestamp_aux(gxio_mpipe_context_t * context, uint64_t sec, +				 uint64_t nsec, uint64_t cycles); + +int gxio_mpipe_adjust_timestamp_aux(gxio_mpipe_context_t * context, +				    int64_t nsec); + +int gxio_mpipe_arm_pollfd(gxio_mpipe_context_t * context, int pollfd_cookie); + +int gxio_mpipe_close_pollfd(gxio_mpipe_context_t * context, int pollfd_cookie); + +int gxio_mpipe_get_mmio_base(gxio_mpipe_context_t * context, HV_PTE *base); + +int gxio_mpipe_check_mmio_offset(gxio_mpipe_context_t * context, +				 unsigned long offset, unsigned long size); + +#endif /* !__GXIO_MPIPE_LINUX_RPC_H__ */ diff --git a/arch/tile/include/gxio/iorpc_mpipe_info.h b/arch/tile/include/gxio/iorpc_mpipe_info.h new file mode 100644 index 00000000000..0bcf3f71ce8 --- /dev/null +++ b/arch/tile/include/gxio/iorpc_mpipe_info.h @@ -0,0 +1,46 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ + +/* This file is machine-generated; DO NOT EDIT! */ +#ifndef __GXIO_MPIPE_INFO_LINUX_RPC_H__ +#define __GXIO_MPIPE_INFO_LINUX_RPC_H__ + +#include <hv/iorpc.h> + +#include <hv/drv_mpipe_intf.h> +#include <asm/page.h> +#include <gxio/kiorpc.h> +#include <gxio/mpipe.h> +#include <linux/string.h> +#include <linux/module.h> +#include <asm/pgtable.h> + + +#define GXIO_MPIPE_INFO_OP_ENUMERATE_AUX IORPC_OPCODE(IORPC_FORMAT_NONE, 0x1251) +#define GXIO_MPIPE_INFO_OP_GET_MMIO_BASE IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x8000) +#define GXIO_MPIPE_INFO_OP_CHECK_MMIO_OFFSET IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x8001) + + +int gxio_mpipe_info_enumerate_aux(gxio_mpipe_info_context_t * context, +				  unsigned int idx, +				  _gxio_mpipe_link_name_t * name, +				  _gxio_mpipe_link_mac_t * mac); + +int gxio_mpipe_info_get_mmio_base(gxio_mpipe_info_context_t * context, +				  HV_PTE *base); + +int gxio_mpipe_info_check_mmio_offset(gxio_mpipe_info_context_t * context, +				      unsigned long offset, unsigned long size); + +#endif /* !__GXIO_MPIPE_INFO_LINUX_RPC_H__ */ diff --git a/arch/tile/include/gxio/iorpc_trio.h b/arch/tile/include/gxio/iorpc_trio.h new file mode 100644 index 00000000000..15fb7799208 --- /dev/null +++ b/arch/tile/include/gxio/iorpc_trio.h @@ -0,0 +1,97 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ + +/* This file is machine-generated; DO NOT EDIT! */ +#ifndef __GXIO_TRIO_LINUX_RPC_H__ +#define __GXIO_TRIO_LINUX_RPC_H__ + +#include <hv/iorpc.h> + +#include <hv/drv_trio_intf.h> +#include <gxio/trio.h> +#include <gxio/kiorpc.h> +#include <linux/string.h> +#include <linux/module.h> +#include <asm/pgtable.h> + +#define GXIO_TRIO_OP_ALLOC_ASIDS       IORPC_OPCODE(IORPC_FORMAT_NONE, 0x1400) + +#define GXIO_TRIO_OP_ALLOC_MEMORY_MAPS IORPC_OPCODE(IORPC_FORMAT_NONE, 0x1402) + +#define GXIO_TRIO_OP_ALLOC_PIO_REGIONS IORPC_OPCODE(IORPC_FORMAT_NONE, 0x140e) +#define GXIO_TRIO_OP_INIT_PIO_REGION_AUX IORPC_OPCODE(IORPC_FORMAT_NONE, 0x140f) + +#define GXIO_TRIO_OP_INIT_MEMORY_MAP_MMU_AUX IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x1417) +#define GXIO_TRIO_OP_GET_PORT_PROPERTY IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x1418) +#define GXIO_TRIO_OP_CONFIG_LEGACY_INTR IORPC_OPCODE(IORPC_FORMAT_KERNEL_INTERRUPT, 0x1419) +#define GXIO_TRIO_OP_CONFIG_MSI_INTR   IORPC_OPCODE(IORPC_FORMAT_KERNEL_INTERRUPT, 0x141a) + +#define GXIO_TRIO_OP_SET_MPS_MRS       IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x141c) +#define GXIO_TRIO_OP_FORCE_RC_LINK_UP  IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x141d) +#define GXIO_TRIO_OP_FORCE_EP_LINK_UP  IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x141e) +#define GXIO_TRIO_OP_GET_MMIO_BASE     IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x8000) +#define GXIO_TRIO_OP_CHECK_MMIO_OFFSET IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x8001) + +int gxio_trio_alloc_asids(gxio_trio_context_t * context, unsigned int count, +			  unsigned int first, unsigned int flags); + + +int gxio_trio_alloc_memory_maps(gxio_trio_context_t * context, +				unsigned int count, unsigned int first, +				unsigned int flags); + + +int gxio_trio_alloc_pio_regions(gxio_trio_context_t * context, +				unsigned int count, unsigned int first, +				unsigned int flags); + +int gxio_trio_init_pio_region_aux(gxio_trio_context_t * context, +				  unsigned int pio_region, unsigned int mac, +				  uint32_t bus_address_hi, unsigned int flags); + + +int gxio_trio_init_memory_map_mmu_aux(gxio_trio_context_t * context, +				      unsigned int map, unsigned long va, +				      uint64_t size, unsigned int asid, +				      unsigned int mac, uint64_t bus_address, +				      unsigned int node, +				      unsigned int order_mode); + +int gxio_trio_get_port_property(gxio_trio_context_t * context, +				struct pcie_trio_ports_property *trio_ports); + +int gxio_trio_config_legacy_intr(gxio_trio_context_t * context, int inter_x, +				 int inter_y, int inter_ipi, int inter_event, +				 unsigned int mac, unsigned int intx); + +int gxio_trio_config_msi_intr(gxio_trio_context_t * context, int inter_x, +			      int inter_y, int inter_ipi, int inter_event, +			      unsigned int mac, unsigned int mem_map, +			      uint64_t mem_map_base, uint64_t mem_map_limit, +			      unsigned int asid); + + +int gxio_trio_set_mps_mrs(gxio_trio_context_t * context, uint16_t mps, +			  uint16_t mrs, unsigned int mac); + +int gxio_trio_force_rc_link_up(gxio_trio_context_t * context, unsigned int mac); + +int gxio_trio_force_ep_link_up(gxio_trio_context_t * context, unsigned int mac); + +int gxio_trio_get_mmio_base(gxio_trio_context_t * context, HV_PTE *base); + +int gxio_trio_check_mmio_offset(gxio_trio_context_t * context, +				unsigned long offset, unsigned long size); + +#endif /* !__GXIO_TRIO_LINUX_RPC_H__ */ diff --git a/arch/tile/include/gxio/iorpc_usb_host.h b/arch/tile/include/gxio/iorpc_usb_host.h new file mode 100644 index 00000000000..8622e7d126a --- /dev/null +++ b/arch/tile/include/gxio/iorpc_usb_host.h @@ -0,0 +1,46 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ + +/* This file is machine-generated; DO NOT EDIT! */ +#ifndef __GXIO_USB_HOST_LINUX_RPC_H__ +#define __GXIO_USB_HOST_LINUX_RPC_H__ + +#include <hv/iorpc.h> + +#include <hv/drv_usb_host_intf.h> +#include <asm/page.h> +#include <gxio/kiorpc.h> +#include <gxio/usb_host.h> +#include <linux/string.h> +#include <linux/module.h> +#include <asm/pgtable.h> + +#define GXIO_USB_HOST_OP_CFG_INTERRUPT IORPC_OPCODE(IORPC_FORMAT_KERNEL_INTERRUPT, 0x1800) +#define GXIO_USB_HOST_OP_REGISTER_CLIENT_MEMORY IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x1801) +#define GXIO_USB_HOST_OP_GET_MMIO_BASE IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x8000) +#define GXIO_USB_HOST_OP_CHECK_MMIO_OFFSET IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x8001) + +int gxio_usb_host_cfg_interrupt(gxio_usb_host_context_t * context, int inter_x, +				int inter_y, int inter_ipi, int inter_event); + +int gxio_usb_host_register_client_memory(gxio_usb_host_context_t * context, +					 HV_PTE pte, unsigned int flags); + +int gxio_usb_host_get_mmio_base(gxio_usb_host_context_t * context, +				HV_PTE *base); + +int gxio_usb_host_check_mmio_offset(gxio_usb_host_context_t * context, +				    unsigned long offset, unsigned long size); + +#endif /* !__GXIO_USB_HOST_LINUX_RPC_H__ */ diff --git a/arch/tile/include/gxio/kiorpc.h b/arch/tile/include/gxio/kiorpc.h new file mode 100644 index 00000000000..ee5820979ff --- /dev/null +++ b/arch/tile/include/gxio/kiorpc.h @@ -0,0 +1,29 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + * + * Support routines for kernel IORPC drivers. + */ + +#ifndef _GXIO_KIORPC_H +#define _GXIO_KIORPC_H + +#include <linux/types.h> +#include <asm/page.h> +#include <arch/chip.h> + +#if CHIP_HAS_MMIO() +void __iomem *iorpc_ioremap(int hv_fd, resource_size_t offset, +			    unsigned long size); +#endif + +#endif /* _GXIO_KIORPC_H */ diff --git a/arch/tile/include/gxio/mpipe.h b/arch/tile/include/gxio/mpipe.h new file mode 100644 index 00000000000..78c598618c9 --- /dev/null +++ b/arch/tile/include/gxio/mpipe.h @@ -0,0 +1,1736 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ + +#ifndef _GXIO_MPIPE_H_ +#define _GXIO_MPIPE_H_ + +/* + * + * An API for allocating, configuring, and manipulating mPIPE hardware + * resources. + */ + +#include "common.h" +#include "dma_queue.h" + +#include <linux/time.h> + +#include <arch/mpipe_def.h> +#include <arch/mpipe_shm.h> + +#include <hv/drv_mpipe_intf.h> +#include <hv/iorpc.h> + +/* + * + * The TILE-Gx mPIPE&tm; shim provides Ethernet connectivity, packet + * classification, and packet load balancing services.  The + * gxio_mpipe_ API, declared in <gxio/mpipe.h>, allows applications to + * allocate mPIPE IO channels, configure packet distribution + * parameters, and send and receive Ethernet packets.  The API is + * designed to be a minimal wrapper around the mPIPE hardware, making + * system calls only where necessary to preserve inter-process + * protection guarantees. + * + * The APIs described below allow the programmer to allocate and + * configure mPIPE resources.  As described below, the mPIPE is a + * single shared hardware device that provides partitionable resources + * that are shared between all applications in the system.  The + * gxio_mpipe_ API allows userspace code to make resource request + * calls to the hypervisor, which in turns keeps track of the + * resources in use by all applications, maintains protection + * guarantees, and resets resources upon application shutdown. + * + * We strongly recommend reading the mPIPE section of the IO Device + * Guide (UG404) before working with this API.  Most functions in the + * gxio_mpipe_ API are directly analogous to hardware interfaces and + * the documentation assumes that the reader understands those + * hardware interfaces. + * + * @section mpipe__ingress mPIPE Ingress Hardware Resources + * + * The mPIPE ingress hardware provides extensive hardware offload for + * tasks like packet header parsing, load balancing, and memory + * management.  This section provides a brief introduction to the + * hardware components and the gxio_mpipe_ calls used to manage them; + * see the IO Device Guide for a much more detailed description of the + * mPIPE's capabilities. + * + * When a packet arrives at one of the mPIPE's Ethernet MACs, it is + * assigned a channel number indicating which MAC received it.  It + * then proceeds through the following hardware pipeline: + * + * @subsection mpipe__classification Classification + * + * A set of classification processors run header parsing code on each + * incoming packet, extracting information including the destination + * MAC address, VLAN, Ethernet type, and five-tuple hash.  Some of + * this information is then used to choose which buffer stack will be + * used to hold the packet, and which bucket will be used by the load + * balancer to determine which application will receive the packet. + * + * The rules by which the buffer stack and bucket are chosen can be + * configured via the @ref gxio_mpipe_classifier API.  A given app can + * specify multiple rules, each one specifying a bucket range, and a + * set of buffer stacks, to be used for packets matching the rule. + * Each rule can optionally specify a restricted set of channels, + * VLANs, and/or dMACs, in which it is interested.  By default, a + * given rule starts out matching all channels associated with the + * mPIPE context's set of open links; all VLANs; and all dMACs. + * Subsequent restrictions can then be added. + * + * @subsection mpipe__load_balancing Load Balancing + * + * The mPIPE load balancer is responsible for choosing the NotifRing + * to which the packet will be delivered.  This decision is based on + * the bucket number indicated by the classification program.  In + * general, the bucket number is based on some number of low bits of + * the packet's flow hash (applications that aren't interested in flow + * hashing use a single bucket).  Each load balancer bucket keeps a + * record of the NotifRing to which packets directed to that bucket + * are currently being delivered.  Based on the bucket's load + * balancing mode (@ref gxio_mpipe_bucket_mode_t), the load balancer + * either forwards the packet to the previously assigned NotifRing or + * decides to choose a new NotifRing.  If a new NotifRing is required, + * the load balancer chooses the least loaded ring in the NotifGroup + * associated with the bucket. + * + * The load balancer is a shared resource.  Each application needs to + * explicitly allocate NotifRings, NotifGroups, and buckets, using + * gxio_mpipe_alloc_notif_rings(), gxio_mpipe_alloc_notif_groups(), + * and gxio_mpipe_alloc_buckets().  Then the application needs to + * configure them using gxio_mpipe_init_notif_ring() and + * gxio_mpipe_init_notif_group_and_buckets(). + * + * @subsection mpipe__buffers Buffer Selection and Packet Delivery + * + * Once the load balancer has chosen the destination NotifRing, the + * mPIPE DMA engine pops at least one buffer off of the 'buffer stack' + * chosen by the classification program and DMAs the packet data into + * that buffer.  Each buffer stack provides a hardware-accelerated + * stack of data buffers with the same size.  If the packet data is + * larger than the buffers provided by the chosen buffer stack, the + * mPIPE hardware pops off multiple buffers and chains the packet data + * through a multi-buffer linked list.  Once the packet data is + * delivered to the buffer(s), the mPIPE hardware writes the + * ::gxio_mpipe_idesc_t metadata object (calculated by the classifier) + * into the NotifRing and increments the number of packets delivered + * to that ring. + * + * Applications can push buffers onto a buffer stack by calling + * gxio_mpipe_push_buffer() or by egressing a packet with the + * ::gxio_mpipe_edesc_t::hwb bit set, indicating that the egressed + * buffers should be returned to the stack. + * + * Applications can allocate and initialize buffer stacks with the + * gxio_mpipe_alloc_buffer_stacks() and gxio_mpipe_init_buffer_stack() + * APIs. + * + * The application must also register the memory pages that will hold + * packets.  This requires calling gxio_mpipe_register_page() for each + * memory page that will hold packets allocated by the application for + * a given buffer stack.  Since each buffer stack is limited to 16 + * registered pages, it may be necessary to use huge pages, or even + * extremely huge pages, to hold all the buffers. + * + * @subsection mpipe__iqueue NotifRings + * + * Each NotifRing is a region of shared memory, allocated by the + * application, to which the mPIPE delivers packet descriptors + * (::gxio_mpipe_idesc_t).  The application can allocate them via + * gxio_mpipe_alloc_notif_rings().  The application can then either + * explicitly initialize them with gxio_mpipe_init_notif_ring() and + * then read from them manually, or can make use of the convenience + * wrappers provided by @ref gxio_mpipe_wrappers. + * + * @section mpipe__egress mPIPE Egress Hardware + * + * Applications use eDMA rings to queue packets for egress.  The + * application can allocate them via gxio_mpipe_alloc_edma_rings(). + * The application can then either explicitly initialize them with + * gxio_mpipe_init_edma_ring() and then write to them manually, or + * can make use of the convenience wrappers provided by + * @ref gxio_mpipe_wrappers. + * + * @section gxio__shortcomings Plans for Future API Revisions + * + * The API defined here is only an initial version of the mPIPE API. + * Future plans include: + * + * - Higher level wrapper functions to provide common initialization + * patterns.  This should help users start writing mPIPE programs + * without having to learn the details of the hardware. + * + * - Support for reset and deallocation of resources, including + * cleanup upon application shutdown. + * + * - Support for calling these APIs in the BME. + * + * - Support for IO interrupts. + * + * - Clearer definitions of thread safety guarantees. + * + * @section gxio__mpipe_examples Examples + * + * See the following mPIPE example programs for more information about + * allocating mPIPE resources and using them in real applications: + * + * - @ref mpipe/ingress/app.c : Receiving packets. + * + * - @ref mpipe/forward/app.c : Forwarding packets. + * + * Note that there are several more examples. + */ + +/* Flags that can be passed to resource allocation functions. */ +enum gxio_mpipe_alloc_flags_e { +	/* Require an allocation to start at a specified resource index. */ +	GXIO_MPIPE_ALLOC_FIXED = HV_MPIPE_ALLOC_FIXED, +}; + +/* Flags that can be passed to memory registration functions. */ +enum gxio_mpipe_mem_flags_e { +	/* Do not fill L3 when writing, and invalidate lines upon egress. */ +	GXIO_MPIPE_MEM_FLAG_NT_HINT = IORPC_MEM_BUFFER_FLAG_NT_HINT, + +	/* L3 cache fills should only populate IO cache ways. */ +	GXIO_MPIPE_MEM_FLAG_IO_PIN = IORPC_MEM_BUFFER_FLAG_IO_PIN, +}; + +/* An ingress packet descriptor.  When a packet arrives, the mPIPE + * hardware generates this structure and writes it into a NotifRing. + */ +typedef MPIPE_PDESC_t gxio_mpipe_idesc_t; + +/* An egress command descriptor.  Applications write this structure + * into eDMA rings and the hardware performs the indicated operation + * (normally involving egressing some bytes).  Note that egressing a + * single packet may involve multiple egress command descriptors. + */ +typedef MPIPE_EDMA_DESC_t gxio_mpipe_edesc_t; + +/* Get the "va" field from an "idesc". + * + * This is the address at which the ingress hardware copied the first + * byte of the packet. + * + * If the classifier detected a custom header, then this will point to + * the custom header, and gxio_mpipe_idesc_get_l2_start() will point + * to the actual L2 header. + * + * Note that this value may be misleading if "idesc->be" is set. + * + * @param idesc An ingress packet descriptor. + */ +static inline unsigned char *gxio_mpipe_idesc_get_va(gxio_mpipe_idesc_t *idesc) +{ +	return (unsigned char *)(long)idesc->va; +} + +/* Get the "xfer_size" from an "idesc". + * + * This is the actual number of packet bytes transferred into memory + * by the hardware. + * + * Note that this value may be misleading if "idesc->be" is set. + * + * @param idesc An ingress packet descriptor. + * + * ISSUE: Is this the best name for this? + * FIXME: Add more docs about chaining, clipping, etc. + */ +static inline unsigned int gxio_mpipe_idesc_get_xfer_size(gxio_mpipe_idesc_t +							  *idesc) +{ +	return idesc->l2_size; +} + +/* Get the "l2_offset" from an "idesc". + * + * Extremely customized classifiers might not support this function. + * + * This is the number of bytes between the "va" and the L2 header. + * + * The L2 header consists of a destination mac address, a source mac + * address, and an initial ethertype.  Various initial ethertypes + * allow encoding extra information in the L2 header, often including + * a vlan, and/or a new ethertype. + * + * Note that the "l2_offset" will be non-zero if (and only if) the + * classifier processed a custom header for the packet. + * + * @param idesc An ingress packet descriptor. + */ +static inline uint8_t gxio_mpipe_idesc_get_l2_offset(gxio_mpipe_idesc_t *idesc) +{ +	return (idesc->custom1 >> 32) & 0xFF; +} + +/* Get the "l2_start" from an "idesc". + * + * This is simply gxio_mpipe_idesc_get_va() plus + * gxio_mpipe_idesc_get_l2_offset(). + * + * @param idesc An ingress packet descriptor. + */ +static inline unsigned char *gxio_mpipe_idesc_get_l2_start(gxio_mpipe_idesc_t +							   *idesc) +{ +	unsigned char *va = gxio_mpipe_idesc_get_va(idesc); +	return va + gxio_mpipe_idesc_get_l2_offset(idesc); +} + +/* Get the "l2_length" from an "idesc". + * + * This is simply gxio_mpipe_idesc_get_xfer_size() minus + * gxio_mpipe_idesc_get_l2_offset(). + * + * @param idesc An ingress packet descriptor. + */ +static inline unsigned int gxio_mpipe_idesc_get_l2_length(gxio_mpipe_idesc_t +							  *idesc) +{ +	unsigned int xfer_size = idesc->l2_size; +	return xfer_size - gxio_mpipe_idesc_get_l2_offset(idesc); +} + +/* A context object used to manage mPIPE hardware resources. */ +typedef struct { + +	/* File descriptor for calling up to Linux (and thus the HV). */ +	int fd; + +	/* The VA at which configuration registers are mapped. */ +	char *mmio_cfg_base; + +	/* The VA at which IDMA, EDMA, and buffer manager are mapped. */ +	char *mmio_fast_base; + +	/* The "initialized" buffer stacks. */ +	gxio_mpipe_rules_stacks_t __stacks; + +} gxio_mpipe_context_t; + +/* This is only used internally, but it's most easily made visible here. */ +typedef gxio_mpipe_context_t gxio_mpipe_info_context_t; + +/* Initialize an mPIPE context. + * + * This function allocates an mPIPE "service domain" and maps the MMIO + * registers into the caller's VA space. + * + * @param context Context object to be initialized. + * @param mpipe_instance Instance number of mPIPE shim to be controlled via + *  context. + */ +extern int gxio_mpipe_init(gxio_mpipe_context_t *context, +			   unsigned int mpipe_instance); + +/* Destroy an mPIPE context. + * + * This function frees the mPIPE "service domain" and unmaps the MMIO + * registers from the caller's VA space. + * + * If a user process exits without calling this routine, the kernel + * will destroy the mPIPE context as part of process teardown. + * + * @param context Context object to be destroyed. + */ +extern int gxio_mpipe_destroy(gxio_mpipe_context_t *context); + +/***************************************************************** + *                         Buffer Stacks                          * + ******************************************************************/ + +/* Allocate a set of buffer stacks. + * + * The return value is NOT interesting if count is zero. + * + * @param context An initialized mPIPE context. + * @param count Number of stacks required. + * @param first Index of first stack if ::GXIO_MPIPE_ALLOC_FIXED flag is set, + *   otherwise ignored. + * @param flags Flag bits from ::gxio_mpipe_alloc_flags_e. + * @return Index of first allocated buffer stack, or + * ::GXIO_MPIPE_ERR_NO_BUFFER_STACK if allocation failed. + */ +extern int gxio_mpipe_alloc_buffer_stacks(gxio_mpipe_context_t *context, +					  unsigned int count, +					  unsigned int first, +					  unsigned int flags); + +/* Enum codes for buffer sizes supported by mPIPE. */ +typedef enum { +	/* 128 byte packet data buffer. */ +	GXIO_MPIPE_BUFFER_SIZE_128 = MPIPE_BSM_INIT_DAT_1__SIZE_VAL_BSZ_128, +	/* 256 byte packet data buffer. */ +	GXIO_MPIPE_BUFFER_SIZE_256 = MPIPE_BSM_INIT_DAT_1__SIZE_VAL_BSZ_256, +	/* 512 byte packet data buffer. */ +	GXIO_MPIPE_BUFFER_SIZE_512 = MPIPE_BSM_INIT_DAT_1__SIZE_VAL_BSZ_512, +	/* 1024 byte packet data buffer. */ +	GXIO_MPIPE_BUFFER_SIZE_1024 = MPIPE_BSM_INIT_DAT_1__SIZE_VAL_BSZ_1024, +	/* 1664 byte packet data buffer. */ +	GXIO_MPIPE_BUFFER_SIZE_1664 = MPIPE_BSM_INIT_DAT_1__SIZE_VAL_BSZ_1664, +	/* 4096 byte packet data buffer. */ +	GXIO_MPIPE_BUFFER_SIZE_4096 = MPIPE_BSM_INIT_DAT_1__SIZE_VAL_BSZ_4096, +	/* 10368 byte packet data buffer. */ +	GXIO_MPIPE_BUFFER_SIZE_10368 = +		MPIPE_BSM_INIT_DAT_1__SIZE_VAL_BSZ_10368, +	/* 16384 byte packet data buffer. */ +	GXIO_MPIPE_BUFFER_SIZE_16384 = MPIPE_BSM_INIT_DAT_1__SIZE_VAL_BSZ_16384 +} gxio_mpipe_buffer_size_enum_t; + +/* Convert a buffer size in bytes into a buffer size enum. */ +extern gxio_mpipe_buffer_size_enum_t +gxio_mpipe_buffer_size_to_buffer_size_enum(size_t size); + +/* Convert a buffer size enum into a buffer size in bytes. */ +extern size_t +gxio_mpipe_buffer_size_enum_to_buffer_size(gxio_mpipe_buffer_size_enum_t +					   buffer_size_enum); + +/* Calculate the number of bytes required to store a given number of + * buffers in the memory registered with a buffer stack via + * gxio_mpipe_init_buffer_stack(). + */ +extern size_t gxio_mpipe_calc_buffer_stack_bytes(unsigned long buffers); + +/* Initialize a buffer stack.  This function binds a region of memory + * to be used by the hardware for storing buffer addresses pushed via + * gxio_mpipe_push_buffer() or as the result of sending a buffer out + * the egress with the 'push to stack when done' bit set.  Once this + * function returns, the memory region's contents may be arbitrarily + * modified by the hardware at any time and software should not access + * the memory region again. + * + * @param context An initialized mPIPE context. + * @param stack The buffer stack index. + * @param buffer_size_enum The size of each buffer in the buffer stack, + * as an enum. + * @param mem The address of the buffer stack.  This memory must be + * physically contiguous and aligned to a 64kB boundary. + * @param mem_size The size of the buffer stack, in bytes. + * @param mem_flags ::gxio_mpipe_mem_flags_e memory flags. + * @return Zero on success, ::GXIO_MPIPE_ERR_INVAL_BUFFER_SIZE if + * buffer_size_enum is invalid, ::GXIO_MPIPE_ERR_BAD_BUFFER_STACK if + * stack has not been allocated. + */ +extern int gxio_mpipe_init_buffer_stack(gxio_mpipe_context_t *context, +					unsigned int stack, +					gxio_mpipe_buffer_size_enum_t +					buffer_size_enum, void *mem, +					size_t mem_size, +					unsigned int mem_flags); + +/* Push a buffer onto a previously initialized buffer stack. + * + * The size of the buffer being pushed must match the size that was + * registered with gxio_mpipe_init_buffer_stack().  All packet buffer + * addresses are 128-byte aligned; the low 7 bits of the specified + * buffer address will be ignored. + * + * @param context An initialized mPIPE context. + * @param stack The buffer stack index. + * @param buffer The buffer (the low seven bits are ignored). + */ +static inline void gxio_mpipe_push_buffer(gxio_mpipe_context_t *context, +					  unsigned int stack, void *buffer) +{ +	MPIPE_BSM_REGION_ADDR_t offset = { {0} }; +	MPIPE_BSM_REGION_VAL_t val = { {0} }; + +	/* +	 * The mmio_fast_base region starts at the IDMA region, so subtract +	 * off that initial offset. +	 */ +	offset.region = +		MPIPE_MMIO_ADDR__REGION_VAL_BSM - +		MPIPE_MMIO_ADDR__REGION_VAL_IDMA; +	offset.stack = stack; + +#if __SIZEOF_POINTER__ == 4 +	val.va = ((ulong) buffer) >> MPIPE_BSM_REGION_VAL__VA_SHIFT; +#else +	val.va = ((long)buffer) >> MPIPE_BSM_REGION_VAL__VA_SHIFT; +#endif + +	__gxio_mmio_write(context->mmio_fast_base + offset.word, val.word); +} + +/* Pop a buffer off of a previously initialized buffer stack. + * + * @param context An initialized mPIPE context. + * @param stack The buffer stack index. + * @return The buffer, or NULL if the stack is empty. + */ +static inline void *gxio_mpipe_pop_buffer(gxio_mpipe_context_t *context, +					  unsigned int stack) +{ +	MPIPE_BSM_REGION_ADDR_t offset = { {0} }; + +	/* +	 * The mmio_fast_base region starts at the IDMA region, so subtract +	 * off that initial offset. +	 */ +	offset.region = +		MPIPE_MMIO_ADDR__REGION_VAL_BSM - +		MPIPE_MMIO_ADDR__REGION_VAL_IDMA; +	offset.stack = stack; + +	while (1) { +		/* +		 * Case 1: val.c == ..._UNCHAINED, va is non-zero. +		 * Case 2: val.c == ..._INVALID, va is zero. +		 * Case 3: val.c == ..._NOT_RDY, va is zero. +		 */ +		MPIPE_BSM_REGION_VAL_t val; +		val.word = +			__gxio_mmio_read(context->mmio_fast_base + +					 offset.word); + +		/* +		 * Handle case 1 and 2 by returning the buffer (or NULL). +		 * Handle case 3 by waiting for the prefetch buffer to refill. +		 */ +		if (val.c != MPIPE_EDMA_DESC_WORD1__C_VAL_NOT_RDY) +			return (void *)((unsigned long)val. +					va << MPIPE_BSM_REGION_VAL__VA_SHIFT); +	} +} + +/***************************************************************** + *                          NotifRings                            * + ******************************************************************/ + +/* Allocate a set of NotifRings. + * + * The return value is NOT interesting if count is zero. + * + * Note that NotifRings are allocated in chunks, so allocating one at + * a time is much less efficient than allocating several at once. + * + * @param context An initialized mPIPE context. + * @param count Number of NotifRings required. + * @param first Index of first NotifRing if ::GXIO_MPIPE_ALLOC_FIXED flag + *   is set, otherwise ignored. + * @param flags Flag bits from ::gxio_mpipe_alloc_flags_e. + * @return Index of first allocated buffer NotifRing, or + * ::GXIO_MPIPE_ERR_NO_NOTIF_RING if allocation failed. + */ +extern int gxio_mpipe_alloc_notif_rings(gxio_mpipe_context_t *context, +					unsigned int count, unsigned int first, +					unsigned int flags); + +/* Initialize a NotifRing, using the given memory and size. + * + * @param context An initialized mPIPE context. + * @param ring The NotifRing index. + * @param mem A physically contiguous region of memory to be filled + * with a ring of ::gxio_mpipe_idesc_t structures. + * @param mem_size Number of bytes in the ring.  Must be 128, 512, + * 2048, or 65536 * sizeof(gxio_mpipe_idesc_t). + * @param mem_flags ::gxio_mpipe_mem_flags_e memory flags. + * + * @return 0 on success, ::GXIO_MPIPE_ERR_BAD_NOTIF_RING or + * ::GXIO_ERR_INVAL_MEMORY_SIZE on failure. + */ +extern int gxio_mpipe_init_notif_ring(gxio_mpipe_context_t *context, +				      unsigned int ring, +				      void *mem, size_t mem_size, +				      unsigned int mem_flags); + +/* Configure an interrupt to be sent to a tile on incoming NotifRing + *  traffic.  Once an interrupt is sent for a particular ring, no more + *  will be sent until gxio_mica_enable_notif_ring_interrupt() is called. + * + * @param context An initialized mPIPE context. + * @param x X coordinate of interrupt target tile. + * @param y Y coordinate of interrupt target tile. + * @param i Index of the IPI register which will receive the interrupt. + * @param e Specific event which will be set in the target IPI register when + * the interrupt occurs. + * @param ring The NotifRing index. + * @return Zero on success, GXIO_ERR_INVAL if params are out of range. + */ +extern int gxio_mpipe_request_notif_ring_interrupt(gxio_mpipe_context_t +						   *context, int x, int y, +						   int i, int e, +						   unsigned int ring); + +/* Enable an interrupt on incoming NotifRing traffic. + * + * @param context An initialized mPIPE context. + * @param ring The NotifRing index. + * @return Zero on success, GXIO_ERR_INVAL if params are out of range. + */ +extern int gxio_mpipe_enable_notif_ring_interrupt(gxio_mpipe_context_t +						  *context, unsigned int ring); + +/* Map all of a client's memory via the given IOTLB. + * @param context An initialized mPIPE context. + * @param iotlb IOTLB index. + * @param pte Page table entry. + * @param flags Flags. + * @return Zero on success, or a negative error code. + */ +extern int gxio_mpipe_register_client_memory(gxio_mpipe_context_t *context, +					     unsigned int iotlb, HV_PTE pte, +					     unsigned int flags); + +/***************************************************************** + *                        Notif Groups                            * + ******************************************************************/ + +/* Allocate a set of NotifGroups. + * + * The return value is NOT interesting if count is zero. + * + * @param context An initialized mPIPE context. + * @param count Number of NotifGroups required. + * @param first Index of first NotifGroup if ::GXIO_MPIPE_ALLOC_FIXED flag + *   is set, otherwise ignored. + * @param flags Flag bits from ::gxio_mpipe_alloc_flags_e. + * @return Index of first allocated buffer NotifGroup, or + * ::GXIO_MPIPE_ERR_NO_NOTIF_GROUP if allocation failed. + */ +extern int gxio_mpipe_alloc_notif_groups(gxio_mpipe_context_t *context, +					 unsigned int count, +					 unsigned int first, +					 unsigned int flags); + +/* Add a NotifRing to a NotifGroup.  This only sets a bit in the + * application's 'group' object; the hardware NotifGroup can be + * initialized by passing 'group' to gxio_mpipe_init_notif_group() or + * gxio_mpipe_init_notif_group_and_buckets(). + */ +static inline void +gxio_mpipe_notif_group_add_ring(gxio_mpipe_notif_group_bits_t *bits, int ring) +{ +	bits->ring_mask[ring / 64] |= (1ull << (ring % 64)); +} + +/* Set a particular NotifGroup bitmask.  Since the load balancer + * makes decisions based on both bucket and NotifGroup state, most + * applications should use gxio_mpipe_init_notif_group_and_buckets() + * rather than using this function to configure just a NotifGroup. + */ +extern int gxio_mpipe_init_notif_group(gxio_mpipe_context_t *context, +				       unsigned int group, +				       gxio_mpipe_notif_group_bits_t bits); + +/***************************************************************** + *                         Load Balancer                          * + ******************************************************************/ + +/* Allocate a set of load balancer buckets. + * + * The return value is NOT interesting if count is zero. + * + * Note that buckets are allocated in chunks, so allocating one at + * a time is much less efficient than allocating several at once. + * + * Note that the buckets are actually divided into two sub-ranges, of + * different sizes, and different chunk sizes, and the range you get + * by default is determined by the size of the request.  Allocations + * cannot span the two sub-ranges. + * + * @param context An initialized mPIPE context. + * @param count Number of buckets required. + * @param first Index of first bucket if ::GXIO_MPIPE_ALLOC_FIXED flag is set, + *   otherwise ignored. + * @param flags Flag bits from ::gxio_mpipe_alloc_flags_e. + * @return Index of first allocated buffer bucket, or + * ::GXIO_MPIPE_ERR_NO_BUCKET if allocation failed. + */ +extern int gxio_mpipe_alloc_buckets(gxio_mpipe_context_t *context, +				    unsigned int count, unsigned int first, +				    unsigned int flags); + +/* The legal modes for gxio_mpipe_bucket_info_t and + * gxio_mpipe_init_notif_group_and_buckets(). + * + * All modes except ::GXIO_MPIPE_BUCKET_ROUND_ROBIN expect that the user + * will allocate a power-of-two number of buckets and initialize them + * to the same mode.  The classifier program then uses the appropriate + * number of low bits from the incoming packet's flow hash to choose a + * load balancer bucket.  Based on that bucket's load balancing mode, + * reference count, and currently active NotifRing, the load balancer + * chooses the NotifRing to which the packet will be delivered. + */ +typedef enum { +	/* All packets for a bucket go to the same NotifRing unless the +	 * NotifRing gets full, in which case packets will be dropped.  If +	 * the bucket reference count ever reaches zero, a new NotifRing may +	 * be chosen. +	 */ +	GXIO_MPIPE_BUCKET_DYNAMIC_FLOW_AFFINITY = +		MPIPE_LBL_INIT_DAT_BSTS_TBL__MODE_VAL_DFA, + +	/* All packets for a bucket always go to the same NotifRing. +	 */ +	GXIO_MPIPE_BUCKET_STATIC_FLOW_AFFINITY = +		MPIPE_LBL_INIT_DAT_BSTS_TBL__MODE_VAL_FIXED, + +	/* All packets for a bucket go to the least full NotifRing in the +	 * group, providing load balancing round robin behavior. +	 */ +	GXIO_MPIPE_BUCKET_ROUND_ROBIN = +		MPIPE_LBL_INIT_DAT_BSTS_TBL__MODE_VAL_ALWAYS_PICK, + +	/* All packets for a bucket go to the same NotifRing unless the +	 * NotifRing gets full, at which point the bucket starts using the +	 * least full NotifRing in the group.  If all NotifRings in the +	 * group are full, packets will be dropped. +	 */ +	GXIO_MPIPE_BUCKET_STICKY_FLOW_LOCALITY = +		MPIPE_LBL_INIT_DAT_BSTS_TBL__MODE_VAL_STICKY, + +	/* All packets for a bucket go to the same NotifRing unless the +	 * NotifRing gets full, or a random timer fires, at which point the +	 * bucket starts using the least full NotifRing in the group.  If +	 * all NotifRings in the group are full, packets will be dropped. +	 * WARNING: This mode is BROKEN on chips with fewer than 64 tiles. +	 */ +	GXIO_MPIPE_BUCKET_PREFER_FLOW_LOCALITY = +		MPIPE_LBL_INIT_DAT_BSTS_TBL__MODE_VAL_STICKY_RAND, + +} gxio_mpipe_bucket_mode_t; + +/* Copy a set of bucket initialization values into the mPIPE + * hardware.  Since the load balancer makes decisions based on both + * bucket and NotifGroup state, most applications should use + * gxio_mpipe_init_notif_group_and_buckets() rather than using this + * function to configure a single bucket. + * + * @param context An initialized mPIPE context. + * @param bucket Bucket index to be initialized. + * @param bucket_info Initial reference count, NotifRing index, and mode. + * @return 0 on success, ::GXIO_MPIPE_ERR_BAD_BUCKET on failure. + */ +extern int gxio_mpipe_init_bucket(gxio_mpipe_context_t *context, +				  unsigned int bucket, +				  gxio_mpipe_bucket_info_t bucket_info); + +/* Initializes a group and range of buckets and range of rings such + * that the load balancer runs a particular load balancing function. + * + * First, the group is initialized with the given rings. + * + * Second, each bucket is initialized with the mode and group, and a + * ring chosen round-robin from the given rings. + * + * Normally, the classifier picks a bucket, and then the load balancer + * picks a ring, based on the bucket's mode, group, and current ring, + * possibly updating the bucket's ring. + * + * @param context An initialized mPIPE context. + * @param group The group. + * @param ring The first ring. + * @param num_rings The number of rings. + * @param bucket The first bucket. + * @param num_buckets The number of buckets. + * @param mode The load balancing mode. + * + * @return 0 on success, ::GXIO_MPIPE_ERR_BAD_BUCKET, + * ::GXIO_MPIPE_ERR_BAD_NOTIF_GROUP, or + * ::GXIO_MPIPE_ERR_BAD_NOTIF_RING on failure. + */ +extern int gxio_mpipe_init_notif_group_and_buckets(gxio_mpipe_context_t +						   *context, +						   unsigned int group, +						   unsigned int ring, +						   unsigned int num_rings, +						   unsigned int bucket, +						   unsigned int num_buckets, +						   gxio_mpipe_bucket_mode_t +						   mode); + +/* Return credits to a NotifRing and/or bucket. + * + * @param context An initialized mPIPE context. + * @param ring The NotifRing index, or -1. + * @param bucket The bucket, or -1. + * @param count The number of credits to return. + */ +static inline void gxio_mpipe_credit(gxio_mpipe_context_t *context, +				     int ring, int bucket, unsigned int count) +{ +	/* NOTE: Fancy struct initialization would break "C89" header test. */ + +	MPIPE_IDMA_RELEASE_REGION_ADDR_t offset = { {0} }; +	MPIPE_IDMA_RELEASE_REGION_VAL_t val = { {0} }; + +	/* +	 * The mmio_fast_base region starts at the IDMA region, so subtract +	 * off that initial offset. +	 */ +	offset.region = +		MPIPE_MMIO_ADDR__REGION_VAL_IDMA - +		MPIPE_MMIO_ADDR__REGION_VAL_IDMA; +	offset.ring = ring; +	offset.bucket = bucket; +	offset.ring_enable = (ring >= 0); +	offset.bucket_enable = (bucket >= 0); +	val.count = count; + +	__gxio_mmio_write(context->mmio_fast_base + offset.word, val.word); +} + +/***************************************************************** + *                         Egress Rings                           * + ******************************************************************/ + +/* Allocate a set of eDMA rings. + * + * The return value is NOT interesting if count is zero. + * + * @param context An initialized mPIPE context. + * @param count Number of eDMA rings required. + * @param first Index of first eDMA ring if ::GXIO_MPIPE_ALLOC_FIXED flag + *   is set, otherwise ignored. + * @param flags Flag bits from ::gxio_mpipe_alloc_flags_e. + * @return Index of first allocated buffer eDMA ring, or + * ::GXIO_MPIPE_ERR_NO_EDMA_RING if allocation failed. + */ +extern int gxio_mpipe_alloc_edma_rings(gxio_mpipe_context_t *context, +				       unsigned int count, unsigned int first, +				       unsigned int flags); + +/* Initialize an eDMA ring, using the given memory and size. + * + * @param context An initialized mPIPE context. + * @param ring The eDMA ring index. + * @param channel The channel to use.  This must be one of the channels + * associated with the context's set of open links. + * @param mem A physically contiguous region of memory to be filled + * with a ring of ::gxio_mpipe_edesc_t structures. + * @param mem_size Number of bytes in the ring.  Must be 512, 2048, + * 8192 or 65536, times 16 (i.e. sizeof(gxio_mpipe_edesc_t)). + * @param mem_flags ::gxio_mpipe_mem_flags_e memory flags. + * + * @return 0 on success, ::GXIO_MPIPE_ERR_BAD_EDMA_RING or + * ::GXIO_ERR_INVAL_MEMORY_SIZE on failure. + */ +extern int gxio_mpipe_init_edma_ring(gxio_mpipe_context_t *context, +				     unsigned int ring, unsigned int channel, +				     void *mem, size_t mem_size, +				     unsigned int mem_flags); + +/***************************************************************** + *                      Classifier Program                        * + ******************************************************************/ + +/* + * + * Functions for loading or configuring the mPIPE classifier program. + * + * The mPIPE classification processors all run a special "classifier" + * program which, for each incoming packet, parses the packet headers, + * encodes some packet metadata in the "idesc", and either drops the + * packet, or picks a notif ring to handle the packet, and a buffer + * stack to contain the packet, usually based on the channel, VLAN, + * dMAC, flow hash, and packet size, under the guidance of the "rules" + * API described below. + * + * @section gxio_mpipe_classifier_default Default Classifier + * + * The MDE provides a simple "default" classifier program.  It is + * shipped as source in "$TILERA_ROOT/src/sys/mpipe/classifier.c", + * which serves as its official documentation.  It is shipped as a + * binary program in "$TILERA_ROOT/tile/boot/classifier", which is + * automatically included in bootroms created by "tile-monitor", and + * is automatically loaded by the hypervisor at boot time. + * + * The L2 analysis handles LLC packets, SNAP packets, and "VLAN + * wrappers" (keeping the outer VLAN). + * + * The L3 analysis handles IPv4 and IPv6, dropping packets with bad + * IPv4 header checksums, requesting computation of a TCP/UDP checksum + * if appropriate, and hashing the dest and src IP addresses, plus the + * ports for TCP/UDP packets, into the flow hash.  No special analysis + * is done for "fragmented" packets or "tunneling" protocols.  Thus, + * the first fragment of a fragmented TCP/UDP packet is hashed using + * src/dest IP address and ports and all subsequent fragments are only + * hashed according to src/dest IP address. + * + * The L3 analysis handles other packets too, hashing the dMAC + * smac into a flow hash. + * + * The channel, VLAN, and dMAC used to pick a "rule" (see the + * "rules" APIs below), which in turn is used to pick a buffer stack + * (based on the packet size) and a bucket (based on the flow hash). + * + * To receive traffic matching a particular (channel/VLAN/dMAC + * pattern, an application should allocate its own buffer stacks and + * load balancer buckets, and map traffic to those stacks and buckets, + * as decribed by the "rules" API below. + * + * Various packet metadata is encoded in the idesc.  The flow hash is + * four bytes at 0x0C.  The VLAN is two bytes at 0x10.  The ethtype is + * two bytes at 0x12.  The l3 start is one byte at 0x14.  The l4 start + * is one byte at 0x15 for IPv4 and IPv6 packets, and otherwise zero. + * The protocol is one byte at 0x16 for IPv4 and IPv6 packets, and + * otherwise zero. + * + * @section gxio_mpipe_classifier_custom Custom Classifiers. + * + * A custom classifier may be created using "tile-mpipe-cc" with a + * customized version of the default classifier sources. + * + * The custom classifier may be included in bootroms using the + * "--classifier" option to "tile-monitor", or loaded dynamically + * using gxio_mpipe_classifier_load_from_file(). + * + * Be aware that "extreme" customizations may break the assumptions of + * the "rules" APIs described below, but simple customizations, such + * as adding new packet metadata, should be fine. + */ + +/* A set of classifier rules, plus a context. */ +typedef struct { + +	/* The context. */ +	gxio_mpipe_context_t *context; + +	/* The actual rules. */ +	gxio_mpipe_rules_list_t list; + +} gxio_mpipe_rules_t; + +/* Initialize a classifier program rules list. + * + * This function can be called on a previously initialized rules list + * to discard any previously added rules. + * + * @param rules Rules list to initialize. + * @param context An initialized mPIPE context. + */ +extern void gxio_mpipe_rules_init(gxio_mpipe_rules_t *rules, +				  gxio_mpipe_context_t *context); + +/* Begin a new rule on the indicated rules list. + * + * Note that an empty rule matches all packets, but an empty rule list + * matches no packets. + * + * @param rules Rules list to which new rule is appended. + * @param bucket First load balancer bucket to which packets will be + * delivered. + * @param num_buckets Number of buckets (must be a power of two) across + * which packets will be distributed based on the "flow hash". + * @param stacks Either NULL, to assign each packet to the smallest + * initialized buffer stack which does not induce chaining (and to + * drop packets which exceed the largest initialized buffer stack + * buffer size), or an array, with each entry indicating which buffer + * stack should be used for packets up to that size (with 255 + * indicating that those packets should be dropped). + * @return 0 on success, or a negative error code on failure. + */ +extern int gxio_mpipe_rules_begin(gxio_mpipe_rules_t *rules, +				  unsigned int bucket, +				  unsigned int num_buckets, +				  gxio_mpipe_rules_stacks_t *stacks); + +/* Set the headroom of the current rule. + * + * @param rules Rules list whose current rule will be modified. + * @param headroom The headroom. + * @return 0 on success, or a negative error code on failure. + */ +extern int gxio_mpipe_rules_set_headroom(gxio_mpipe_rules_t *rules, +					 uint8_t headroom); + +/* Indicate that packets from a particular channel can be delivered + * to the buckets and buffer stacks associated with the current rule. + * + * Channels added must be associated with links opened by the mPIPE context + * used in gxio_mpipe_rules_init().  A rule with no channels is equivalent + * to a rule naming all such associated channels. + * + * @param rules Rules list whose current rule will be modified. + * @param channel The channel to add. + * @return 0 on success, or a negative error code on failure. + */ +extern int gxio_mpipe_rules_add_channel(gxio_mpipe_rules_t *rules, +					unsigned int channel); + +/* Commit rules. + * + * The rules are sent to the hypervisor, where they are combined with + * the rules from other apps, and used to program the hardware classifier. + * + * Note that if this function returns an error, then the rules will NOT + * have been committed, even if the error is due to interactions with + * rules from another app. + * + * @param rules Rules list to commit. + * @return 0 on success, or a negative error code on failure. + */ +extern int gxio_mpipe_rules_commit(gxio_mpipe_rules_t *rules); + +/***************************************************************** + *                     Ingress Queue Wrapper                      * + ******************************************************************/ + +/* + * + * Convenience functions for receiving packets from a NotifRing and + * sending packets via an eDMA ring. + * + * The mpipe ingress and egress hardware uses shared memory packet + * descriptors to describe packets that have arrived on ingress or + * are destined for egress.  These descriptors are stored in shared + * memory ring buffers and written or read by hardware as necessary. + * The gxio library provides wrapper functions that manage the head and + * tail pointers for these rings, allowing the user to easily read or + * write packet descriptors. + * + * The initialization interface for ingress and egress rings is quite + * similar.  For example, to create an ingress queue, the user passes + * a ::gxio_mpipe_iqueue_t state object, a ring number from + * gxio_mpipe_alloc_notif_rings(), and the address of memory to hold a + * ring buffer to the gxio_mpipe_iqueue_init() function.  The function + * returns success when the state object has been initialized and the + * hardware configured to deliver packets to the specified ring + * buffer.  Similarly, gxio_mpipe_equeue_init() takes a + * ::gxio_mpipe_equeue_t state object, a ring number from + * gxio_mpipe_alloc_edma_rings(), and a shared memory buffer. + * + * @section gxio_mpipe_iqueue Working with Ingress Queues + * + * Once initialized, the gxio_mpipe_iqueue_t API provides two flows + * for getting the ::gxio_mpipe_idesc_t packet descriptor associated + * with incoming packets.  The simplest is to call + * gxio_mpipe_iqueue_get() or gxio_mpipe_iqueue_try_get().  These + * functions copy the oldest packet descriptor out of the NotifRing and + * into a descriptor provided by the caller.  They also immediately + * inform the hardware that a descriptor has been processed. + * + * For applications with stringent performance requirements, higher + * efficiency can be achieved by avoiding the packet descriptor copy + * and processing multiple descriptors at once.  The + * gxio_mpipe_iqueue_peek() and gxio_mpipe_iqueue_try_peek() functions + * allow such optimizations.  These functions provide a pointer to the + * next valid ingress descriptor in the NotifRing's shared memory ring + * buffer, and a count of how many contiguous descriptors are ready to + * be processed.  The application can then process any number of those + * descriptors in place, calling gxio_mpipe_iqueue_consume() to inform + * the hardware after each one has been processed. + * + * @section gxio_mpipe_equeue Working with Egress Queues + * + * Similarly, the egress queue API provides a high-performance + * interface plus a simple wrapper for use in posting + * ::gxio_mpipe_edesc_t egress packet descriptors.  The simple + * version, gxio_mpipe_equeue_put(), allows the programmer to wait for + * an eDMA ring slot to become available and write a single descriptor + * into the ring. + * + * Alternatively, you can reserve slots in the eDMA ring using + * gxio_mpipe_equeue_reserve() or gxio_mpipe_equeue_try_reserve(), and + * then fill in each slot using gxio_mpipe_equeue_put_at().  This + * capability can be used to amortize the cost of reserving slots + * across several packets.  It also allows gather operations to be + * performed on a shared equeue, by ensuring that the edescs for all + * the fragments are all contiguous in the eDMA ring. + * + * The gxio_mpipe_equeue_reserve() and gxio_mpipe_equeue_try_reserve() + * functions return a 63-bit "completion slot", which is actually a + * sequence number, the low bits of which indicate the ring buffer + * index and the high bits the number of times the application has + * gone around the egress ring buffer.  The extra bits allow an + * application to check for egress completion by calling + * gxio_mpipe_equeue_is_complete() to see whether a particular 'slot' + * number has finished.  Given the maximum packet rates of the Gx + * processor, the 63-bit slot number will never wrap. + * + * In practice, most applications use the ::gxio_mpipe_edesc_t::hwb + * bit to indicate that the buffers containing egress packet data + * should be pushed onto a buffer stack when egress is complete.  Such + * applications generally do not need to know when an egress operation + * completes (since there is no need to free a buffer post-egress), + * and thus can use the optimized gxio_mpipe_equeue_reserve_fast() or + * gxio_mpipe_equeue_try_reserve_fast() functions, which return a 24 + * bit "slot", instead of a 63-bit "completion slot". + * + * Once a slot has been "reserved", it MUST be filled.  If the + * application reserves a slot and then decides that it does not + * actually need it, it can set the ::gxio_mpipe_edesc_t::ns (no send) + * bit on the descriptor passed to gxio_mpipe_equeue_put_at() to + * indicate that no data should be sent.  This technique can also be + * used to drop an incoming packet, instead of forwarding it, since + * any buffer will still be pushed onto the buffer stack when the + * egress descriptor is processed. + */ + +/* A convenient interface to a NotifRing, for use by a single thread. + */ +typedef struct { + +	/* The context. */ +	gxio_mpipe_context_t *context; + +	/* The actual NotifRing. */ +	gxio_mpipe_idesc_t *idescs; + +	/* The number of entries. */ +	unsigned long num_entries; + +	/* The number of entries minus one. */ +	unsigned long mask_num_entries; + +	/* The log2() of the number of entries. */ +	unsigned long log2_num_entries; + +	/* The next entry. */ +	unsigned int head; + +	/* The NotifRing id. */ +	unsigned int ring; + +#ifdef __BIG_ENDIAN__ +	/* The number of byteswapped entries. */ +	unsigned int swapped; +#endif + +} gxio_mpipe_iqueue_t; + +/* Initialize an "iqueue". + * + * Takes the iqueue plus the same args as gxio_mpipe_init_notif_ring(). + */ +extern int gxio_mpipe_iqueue_init(gxio_mpipe_iqueue_t *iqueue, +				  gxio_mpipe_context_t *context, +				  unsigned int ring, +				  void *mem, size_t mem_size, +				  unsigned int mem_flags); + +/* Advance over some old entries in an iqueue. + * + * Please see the documentation for gxio_mpipe_iqueue_consume(). + * + * @param iqueue An ingress queue initialized via gxio_mpipe_iqueue_init(). + * @param count The number of entries to advance over. + */ +static inline void gxio_mpipe_iqueue_advance(gxio_mpipe_iqueue_t *iqueue, +					     int count) +{ +	/* Advance with proper wrap. */ +	int head = iqueue->head + count; +	iqueue->head = +		(head & iqueue->mask_num_entries) + +		(head >> iqueue->log2_num_entries); + +#ifdef __BIG_ENDIAN__ +	/* HACK: Track swapped entries. */ +	iqueue->swapped -= count; +#endif +} + +/* Release the ring and bucket for an old entry in an iqueue. + * + * Releasing the ring allows more packets to be delivered to the ring. + * + * Releasing the bucket allows flows using the bucket to be moved to a + * new ring when using GXIO_MPIPE_BUCKET_DYNAMIC_FLOW_AFFINITY. + * + * This function is shorthand for "gxio_mpipe_credit(iqueue->context, + * iqueue->ring, idesc->bucket_id, 1)", and it may be more convenient + * to make that underlying call, using those values, instead of + * tracking the entire "idesc". + * + * If packet processing is deferred, optimal performance requires that + * the releasing be deferred as well. + * + * Please see the documentation for gxio_mpipe_iqueue_consume(). + * + * @param iqueue An ingress queue initialized via gxio_mpipe_iqueue_init(). + * @param idesc The descriptor which was processed. + */ +static inline void gxio_mpipe_iqueue_release(gxio_mpipe_iqueue_t *iqueue, +					     gxio_mpipe_idesc_t *idesc) +{ +	gxio_mpipe_credit(iqueue->context, iqueue->ring, idesc->bucket_id, 1); +} + +/* Consume a packet from an "iqueue". + * + * After processing packets peeked at via gxio_mpipe_iqueue_peek() + * or gxio_mpipe_iqueue_try_peek(), you must call this function, or + * gxio_mpipe_iqueue_advance() plus gxio_mpipe_iqueue_release(), to + * advance over those entries, and release their rings and buckets. + * + * You may call this function as each packet is processed, or you can + * wait until several packets have been processed. + * + * Note that if you are using a single bucket, and you are handling + * batches of N packets, then you can replace several calls to this + * function with calls to "gxio_mpipe_iqueue_advance(iqueue, N)" and + * "gxio_mpipe_credit(iqueue->context, iqueue->ring, bucket, N)". + * + * Note that if your classifier sets "idesc->nr", then you should + * explicitly call "gxio_mpipe_iqueue_advance(iqueue, idesc)" plus + * "gxio_mpipe_credit(iqueue->context, iqueue->ring, -1, 1)", to + * avoid incorrectly crediting the (unused) bucket. + * + * @param iqueue An ingress queue initialized via gxio_mpipe_iqueue_init(). + * @param idesc The descriptor which was processed. + */ +static inline void gxio_mpipe_iqueue_consume(gxio_mpipe_iqueue_t *iqueue, +					     gxio_mpipe_idesc_t *idesc) +{ +	gxio_mpipe_iqueue_advance(iqueue, 1); +	gxio_mpipe_iqueue_release(iqueue, idesc); +} + +/* Peek at the next packet(s) in an "iqueue", without waiting. + * + * If no packets are available, fills idesc_ref with NULL, and then + * returns ::GXIO_MPIPE_ERR_IQUEUE_EMPTY.  Otherwise, fills idesc_ref + * with the address of the next valid packet descriptor, and returns + * the maximum number of valid descriptors which can be processed. + * You may process fewer descriptors if desired. + * + * Call gxio_mpipe_iqueue_consume() on each packet once it has been + * processed (or dropped), to allow more packets to be delivered. + * + * @param iqueue An ingress queue initialized via gxio_mpipe_iqueue_init(). + * @param idesc_ref A pointer to a packet descriptor pointer. + * @return The (positive) number of packets which can be processed, + * or ::GXIO_MPIPE_ERR_IQUEUE_EMPTY if no packets are available. + */ +static inline int gxio_mpipe_iqueue_try_peek(gxio_mpipe_iqueue_t *iqueue, +					     gxio_mpipe_idesc_t **idesc_ref) +{ +	gxio_mpipe_idesc_t *next; + +	uint64_t head = iqueue->head; +	uint64_t tail = __gxio_mmio_read(iqueue->idescs); + +	/* Available entries. */ +	uint64_t avail = +		(tail >= head) ? (tail - head) : (iqueue->num_entries - head); + +	if (avail == 0) { +		*idesc_ref = NULL; +		return GXIO_MPIPE_ERR_IQUEUE_EMPTY; +	} + +	next = &iqueue->idescs[head]; + +	/* ISSUE: Is this helpful? */ +	__insn_prefetch(next); + +#ifdef __BIG_ENDIAN__ +	/* HACK: Swap new entries directly in memory. */ +	{ +		int i, j; +		for (i = iqueue->swapped; i < avail; i++) { +			for (j = 0; j < 8; j++) +				next[i].words[j] = +					__builtin_bswap64(next[i].words[j]); +		} +		iqueue->swapped = avail; +	} +#endif + +	*idesc_ref = next; + +	return avail; +} + +/* Drop a packet by pushing its buffer (if appropriate). + * + * NOTE: The caller must still call gxio_mpipe_iqueue_consume() if idesc + * came from gxio_mpipe_iqueue_try_peek() or gxio_mpipe_iqueue_peek(). + * + * @param iqueue An ingress queue initialized via gxio_mpipe_iqueue_init(). + * @param idesc A packet descriptor. + */ +static inline void gxio_mpipe_iqueue_drop(gxio_mpipe_iqueue_t *iqueue, +					  gxio_mpipe_idesc_t *idesc) +{ +	/* FIXME: Handle "chaining" properly. */ + +	if (!idesc->be) { +		unsigned char *va = gxio_mpipe_idesc_get_va(idesc); +		gxio_mpipe_push_buffer(iqueue->context, idesc->stack_idx, va); +	} +} + +/***************************************************************** + *                      Egress Queue Wrapper                      * + ******************************************************************/ + +/* A convenient, thread-safe interface to an eDMA ring. */ +typedef struct { + +	/* State object for tracking head and tail pointers. */ +	__gxio_dma_queue_t dma_queue; + +	/* The ring entries. */ +	gxio_mpipe_edesc_t *edescs; + +	/* The number of entries minus one. */ +	unsigned long mask_num_entries; + +	/* The log2() of the number of entries. */ +	unsigned long log2_num_entries; + +} gxio_mpipe_equeue_t; + +/* Initialize an "equeue". + * + * Takes the equeue plus the same args as gxio_mpipe_init_edma_ring(). + */ +extern int gxio_mpipe_equeue_init(gxio_mpipe_equeue_t *equeue, +				  gxio_mpipe_context_t *context, +				  unsigned int edma_ring_id, +				  unsigned int channel, +				  void *mem, unsigned int mem_size, +				  unsigned int mem_flags); + +/* Reserve completion slots for edescs. + * + * Use gxio_mpipe_equeue_put_at() to actually populate the slots. + * + * This function is slower than gxio_mpipe_equeue_reserve_fast(), but + * returns a full 64 bit completion slot, which can be used with + * gxio_mpipe_equeue_is_complete(). + * + * @param equeue An egress queue initialized via gxio_mpipe_equeue_init(). + * @param num Number of slots to reserve (must be non-zero). + * @return The first reserved completion slot, or a negative error code. + */ +static inline int64_t gxio_mpipe_equeue_reserve(gxio_mpipe_equeue_t *equeue, +						unsigned int num) +{ +	return __gxio_dma_queue_reserve_aux(&equeue->dma_queue, num, true); +} + +/* Reserve completion slots for edescs, if possible. + * + * Use gxio_mpipe_equeue_put_at() to actually populate the slots. + * + * This function is slower than gxio_mpipe_equeue_try_reserve_fast(), + * but returns a full 64 bit completion slot, which can be used with + * gxio_mpipe_equeue_is_complete(). + * + * @param equeue An egress queue initialized via gxio_mpipe_equeue_init(). + * @param num Number of slots to reserve (must be non-zero). + * @return The first reserved completion slot, or a negative error code. + */ +static inline int64_t gxio_mpipe_equeue_try_reserve(gxio_mpipe_equeue_t +						    *equeue, unsigned int num) +{ +	return __gxio_dma_queue_reserve_aux(&equeue->dma_queue, num, false); +} + +/* Reserve slots for edescs. + * + * Use gxio_mpipe_equeue_put_at() to actually populate the slots. + * + * This function is faster than gxio_mpipe_equeue_reserve(), but + * returns a 24 bit slot (instead of a 64 bit completion slot), which + * thus cannot be used with gxio_mpipe_equeue_is_complete(). + * + * @param equeue An egress queue initialized via gxio_mpipe_equeue_init(). + * @param num Number of slots to reserve (should be non-zero). + * @return The first reserved slot, or a negative error code. + */ +static inline int64_t gxio_mpipe_equeue_reserve_fast(gxio_mpipe_equeue_t +						     *equeue, unsigned int num) +{ +	return __gxio_dma_queue_reserve(&equeue->dma_queue, num, true, false); +} + +/* Reserve slots for edescs, if possible. + * + * Use gxio_mpipe_equeue_put_at() to actually populate the slots. + * + * This function is faster than gxio_mpipe_equeue_try_reserve(), but + * returns a 24 bit slot (instead of a 64 bit completion slot), which + * thus cannot be used with gxio_mpipe_equeue_is_complete(). + * + * @param equeue An egress queue initialized via gxio_mpipe_equeue_init(). + * @param num Number of slots to reserve (should be non-zero). + * @return The first reserved slot, or a negative error code. + */ +static inline int64_t gxio_mpipe_equeue_try_reserve_fast(gxio_mpipe_equeue_t +							 *equeue, +							 unsigned int num) +{ +	return __gxio_dma_queue_reserve(&equeue->dma_queue, num, false, false); +} + +/* + * HACK: This helper function tricks gcc 4.6 into avoiding saving + * a copy of "edesc->words[0]" on the stack for no obvious reason. + */ + +static inline void gxio_mpipe_equeue_put_at_aux(gxio_mpipe_equeue_t *equeue, +						uint_reg_t ew[2], +						unsigned long slot) +{ +	unsigned long edma_slot = slot & equeue->mask_num_entries; +	gxio_mpipe_edesc_t *edesc_p = &equeue->edescs[edma_slot]; + +	/* +	 * ISSUE: Could set eDMA ring to be on generation 1 at start, which +	 * would avoid the negation here, perhaps allowing "__insn_bfins()". +	 */ +	ew[0] |= !((slot >> equeue->log2_num_entries) & 1); + +	/* +	 * NOTE: We use "__gxio_mpipe_write()", plus the fact that the eDMA +	 * queue alignment restrictions ensure that these two words are on +	 * the same cacheline, to force proper ordering between the stores. +	 */ +	__gxio_mmio_write64(&edesc_p->words[1], ew[1]); +	__gxio_mmio_write64(&edesc_p->words[0], ew[0]); +} + +/* Post an edesc to a given slot in an equeue. + * + * This function copies the supplied edesc into entry "slot mod N" in + * the underlying ring, setting the "gen" bit to the appropriate value + * based on "(slot mod N*2)", where "N" is the size of the ring.  Note + * that the higher bits of slot are unused, and thus, this function + * can handle "slots" as well as "completion slots". + * + * Normally this function is used to fill in slots reserved by + * gxio_mpipe_equeue_try_reserve(), gxio_mpipe_equeue_reserve(), + * gxio_mpipe_equeue_try_reserve_fast(), or + * gxio_mpipe_equeue_reserve_fast(), + * + * This function can also be used without "reserving" slots, if the + * application KNOWS that the ring can never overflow, for example, by + * pushing fewer buffers into the buffer stacks than there are total + * slots in the equeue, but this is NOT recommended. + * + * @param equeue An egress queue initialized via gxio_mpipe_equeue_init(). + * @param edesc The egress descriptor to be posted. + * @param slot An egress slot (only the low bits are actually used). + */ +static inline void gxio_mpipe_equeue_put_at(gxio_mpipe_equeue_t *equeue, +					    gxio_mpipe_edesc_t edesc, +					    unsigned long slot) +{ +	gxio_mpipe_equeue_put_at_aux(equeue, edesc.words, slot); +} + +/* Post an edesc to the next slot in an equeue. + * + * This is a convenience wrapper around + * gxio_mpipe_equeue_reserve_fast() and gxio_mpipe_equeue_put_at(). + * + * @param equeue An egress queue initialized via gxio_mpipe_equeue_init(). + * @param edesc The egress descriptor to be posted. + * @return 0 on success. + */ +static inline int gxio_mpipe_equeue_put(gxio_mpipe_equeue_t *equeue, +					gxio_mpipe_edesc_t edesc) +{ +	int64_t slot = gxio_mpipe_equeue_reserve_fast(equeue, 1); +	if (slot < 0) +		return (int)slot; + +	gxio_mpipe_equeue_put_at(equeue, edesc, slot); + +	return 0; +} + +/* Ask the mPIPE hardware to egress outstanding packets immediately. + * + * This call is not necessary, but may slightly reduce overall latency. + * + * Technically, you should flush all gxio_mpipe_equeue_put_at() writes + * to memory before calling this function, to ensure the descriptors + * are visible in memory before the mPIPE hardware actually looks for + * them.  But this should be very rare, and the only side effect would + * be increased latency, so it is up to the caller to decide whether + * or not to flush memory. + * + * @param equeue An egress queue initialized via gxio_mpipe_equeue_init(). + */ +static inline void gxio_mpipe_equeue_flush(gxio_mpipe_equeue_t *equeue) +{ +	/* Use "ring_idx = 0" and "count = 0" to "wake up" the eDMA ring. */ +	MPIPE_EDMA_POST_REGION_VAL_t val = { {0} }; +	/* Flush the write buffers. */ +	__insn_flushwb(); +	__gxio_mmio_write(equeue->dma_queue.post_region_addr, val.word); +} + +/* Determine if a given edesc has been completed. + * + * Note that this function requires a "completion slot", and thus may + * NOT be used with a "slot" from gxio_mpipe_equeue_reserve_fast() or + * gxio_mpipe_equeue_try_reserve_fast(). + * + * @param equeue An egress queue initialized via gxio_mpipe_equeue_init(). + * @param completion_slot The completion slot used by the edesc. + * @param update If true, and the desc does not appear to have completed + * yet, then update any software cache of the hardware completion counter, + * and check again.  This should normally be true. + * @return True iff the given edesc has been completed. + */ +static inline int gxio_mpipe_equeue_is_complete(gxio_mpipe_equeue_t *equeue, +						int64_t completion_slot, +						int update) +{ +	return __gxio_dma_queue_is_complete(&equeue->dma_queue, +					    completion_slot, update); +} + +/***************************************************************** + *                        Link Management                         * + ******************************************************************/ + +/* + * + * Functions for manipulating and sensing the state and configuration + * of physical network links. + * + * @section gxio_mpipe_link_perm Link Permissions + * + * Opening a link (with gxio_mpipe_link_open()) requests a set of link + * permissions, which control what may be done with the link, and potentially + * what permissions may be granted to other processes. + * + * Data permission allows the process to receive packets from the link by + * specifying the link's channel number in mPIPE packet distribution rules, + * and to send packets to the link by using the link's channel number as + * the target for an eDMA ring. + * + * Stats permission allows the process to retrieve link attributes (such as + * the speeds it is capable of running at, or whether it is currently up), and + * to read and write certain statistics-related registers in the link's MAC. + * + * Control permission allows the process to retrieve and modify link attributes + * (so that it may, for example, bring the link up and take it down), and + * read and write many registers in the link's MAC and PHY. + * + * Any permission may be requested as shared, which allows other processes + * to also request shared permission, or exclusive, which prevents other + * processes from requesting it.  In keeping with GXIO's typical usage in + * an embedded environment, the defaults for all permissions are shared. + * + * Permissions are granted on a first-come, first-served basis, so if two + * applications request an exclusive permission on the same link, the one + * to run first will win.  Note, however, that some system components, like + * the kernel Ethernet driver, may get an opportunity to open links before + * any applications run. + * + * @section gxio_mpipe_link_names Link Names + * + * Link names are of the form gbe<em>number</em> (for Gigabit Ethernet), + * xgbe<em>number</em> (for 10 Gigabit Ethernet), loop<em>number</em> (for + * internal mPIPE loopback), or ilk<em>number</em>/<em>channel</em> + * (for Interlaken links); for instance, gbe0, xgbe1, loop3, and + * ilk0/12 are all possible link names.  The correspondence between + * the link name and an mPIPE instance number or mPIPE channel number is + * system-dependent; all links will not exist on all systems, and the set + * of numbers used for a particular link type may not start at zero and may + * not be contiguous.  Use gxio_mpipe_link_enumerate() to retrieve the set of + * links which exist on a system, and always use gxio_mpipe_link_instance() + * to determine which mPIPE controls a particular link. + * + * Note that in some cases, links may share hardware, such as PHYs, or + * internal mPIPE buffers; in these cases, only one of the links may be + * opened at a time.  This is especially common with xgbe and gbe ports, + * since each xgbe port uses 4 SERDES lanes, each of which may also be + * configured as one gbe port. + * + * @section gxio_mpipe_link_states Link States + * + * The mPIPE link management model revolves around three different states, + * which are maintained for each link: + * + * 1. The <em>current</em> link state: is the link up now, and if so, at + *    what speed? + * + * 2. The <em>desired</em> link state: what do we want the link state to be? + *    The system is always working to make this state the current state; + *    thus, if the desired state is up, and the link is down, we'll be + *    constantly trying to bring it up, automatically. + * + * 3. The <em>possible</em> link state: what speeds are valid for this + *    particular link?  Or, in other words, what are the capabilities of + *    the link hardware? + * + * These link states are not, strictly speaking, related to application + * state; they may be manipulated at any time, whether or not the link + * is currently being used for data transfer.  However, for convenience, + * gxio_mpipe_link_open() and gxio_mpipe_link_close() (or application exit) + * can affect the link state.  These implicit link management operations + * may be modified or disabled by the use of link open flags. + * + * From an application, you can use gxio_mpipe_link_get_attr() + * and gxio_mpipe_link_set_attr() to manipulate the link states. + * gxio_mpipe_link_get_attr() with ::GXIO_MPIPE_LINK_POSSIBLE_STATE + * gets you the possible link state.  gxio_mpipe_link_get_attr() with + * ::GXIO_MPIPE_LINK_CURRENT_STATE gets you the current link state. + * Finally, gxio_mpipe_link_set_attr() and gxio_mpipe_link_get_attr() + * with ::GXIO_MPIPE_LINK_DESIRED_STATE allow you to modify or retrieve + * the desired link state. + * + * If you want to manage a link from a part of your application which isn't + * involved in packet processing, you can use the ::GXIO_MPIPE_LINK_NO_DATA + * flags on a gxio_mpipe_link_open() call.  This opens the link, but does + * not request data permission, so it does not conflict with any exclusive + * permissions which may be held by other processes.  You can then can use + * gxio_mpipe_link_get_attr() and gxio_mpipe_link_set_attr() on this link + * object to bring up or take down the link. + * + * Some links support link state bits which support various loopback + * modes. ::GXIO_MPIPE_LINK_LOOP_MAC tests datapaths within the Tile + * Processor itself; ::GXIO_MPIPE_LINK_LOOP_PHY tests the datapath between + * the Tile Processor and the external physical layer interface chip; and + * ::GXIO_MPIPE_LINK_LOOP_EXT tests the entire network datapath with the + * aid of an external loopback connector.  In addition to enabling hardware + * testing, such configuration can be useful for software testing, as well. + * + * When LOOP_MAC or LOOP_PHY is enabled, packets transmitted on a channel + * will be received by that channel, instead of being emitted on the + * physical link, and packets received on the physical link will be ignored. + * Other than that, all standard GXIO operations work as you might expect. + * Note that loopback operation requires that the link be brought up using + * one or more of the GXIO_MPIPE_LINK_SPEED_xxx link state bits. + * + * Those familiar with previous versions of the MDE on TILEPro hardware + * will notice significant similarities between the NetIO link management + * model and the mPIPE link management model.  However, the NetIO model + * was developed in stages, and some of its features -- for instance, + * the default setting of certain flags -- were shaped by the need to be + * compatible with previous versions of NetIO.  Since the features provided + * by the mPIPE hardware and the mPIPE GXIO library are significantly + * different than those provided by NetIO, in some cases, we have made + * different choices in the mPIPE link management API.  Thus, please read + * this documentation carefully before assuming that mPIPE link management + * operations are exactly equivalent to their NetIO counterparts. + */ + +/* An object used to manage mPIPE link state and resources. */ +typedef struct { +	/* The overall mPIPE context. */ +	gxio_mpipe_context_t *context; + +	/* The channel number used by this link. */ +	uint8_t channel; + +	/* The MAC index used by this link. */ +	uint8_t mac; +} gxio_mpipe_link_t; + +/* Retrieve one of this system's legal link names, and its MAC address. + * + * @param index Link name index.  If a system supports N legal link names, + *  then indices between 0 and N - 1, inclusive, each correspond to one of + *  those names.  Thus, to retrieve all of a system's legal link names, + *  call this function in a loop, starting with an index of zero, and + *  incrementing it once per iteration until -1 is returned. + * @param link_name Pointer to the buffer which will receive the retrieved + *  link name.  The buffer should contain space for at least + *  ::GXIO_MPIPE_LINK_NAME_LEN bytes; the returned name, including the + *  terminating null byte, will be no longer than that. + * @param link_name Pointer to the buffer which will receive the retrieved + *  MAC address.  The buffer should contain space for at least 6 bytes. + * @return Zero if a link name was successfully retrieved; -1 if one was + *  not. + */ +extern int gxio_mpipe_link_enumerate_mac(int index, char *link_name, +					 uint8_t *mac_addr); + +/* Open an mPIPE link. + * + *  A link must be opened before it may be used to send or receive packets, + *  and before its state may be examined or changed.  Depending up on the + *  link's intended use, one or more link permissions may be requested via + *  the flags parameter; see @ref gxio_mpipe_link_perm.  In addition, flags + *  may request that the link's state be modified at open time.  See @ref + *  gxio_mpipe_link_states and @ref gxio_mpipe_link_open_flags for more detail. + * + * @param link A link state object, which will be initialized if this + *  function completes successfully. + * @param context An initialized mPIPE context. + * @param link_name Name of the link. + * @param flags Zero or more @ref gxio_mpipe_link_open_flags, ORed together. + * @return 0 if the link was successfully opened, or a negative error code. + * + */ +extern int gxio_mpipe_link_open(gxio_mpipe_link_t *link, +				gxio_mpipe_context_t *context, +				const char *link_name, unsigned int flags); + +/* Close an mPIPE link. + * + *  Closing a link makes it available for use by other processes.  Once + *  a link has been closed, packets may no longer be sent on or received + *  from the link, and its state may not be examined or changed. + * + * @param link A link state object, which will no longer be initialized + *  if this function completes successfully. + * @return 0 if the link was successfully closed, or a negative error code. + * + */ +extern int gxio_mpipe_link_close(gxio_mpipe_link_t *link); + +/* Return a link's channel number. + * + * @param link A properly initialized link state object. + * @return The channel number for the link. + */ +static inline int gxio_mpipe_link_channel(gxio_mpipe_link_t *link) +{ +	return link->channel; +} + +/////////////////////////////////////////////////////////////////// +//                             Timestamp                         // +/////////////////////////////////////////////////////////////////// + +/* Get the timestamp of mPIPE when this routine is called. + * + * @param context An initialized mPIPE context. + * @param ts A timespec structure to store the current clock. + * @return If the call was successful, zero; otherwise, a negative error + *  code. + */ +extern int gxio_mpipe_get_timestamp(gxio_mpipe_context_t *context, +				    struct timespec *ts); + +/* Set the timestamp of mPIPE. + * + * @param context An initialized mPIPE context. + * @param ts A timespec structure to store the requested clock. + * @return If the call was successful, zero; otherwise, a negative error + *  code. + */ +extern int gxio_mpipe_set_timestamp(gxio_mpipe_context_t *context, +				    const struct timespec *ts); + +/* Adjust the timestamp of mPIPE. + * + * @param context An initialized mPIPE context. + * @param delta A signed time offset to adjust, in nanoseconds. + * The absolute value of this parameter must be less than or + * equal to 1000000000. + * @return If the call was successful, zero; otherwise, a negative error + *  code. + */ +extern int gxio_mpipe_adjust_timestamp(gxio_mpipe_context_t *context, +				       int64_t delta); + +#endif /* !_GXIO_MPIPE_H_ */ diff --git a/arch/tile/include/gxio/trio.h b/arch/tile/include/gxio/trio.h new file mode 100644 index 00000000000..77b80cdd46d --- /dev/null +++ b/arch/tile/include/gxio/trio.h @@ -0,0 +1,298 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ + +/* + * + * An API for allocating, configuring, and manipulating TRIO hardware + * resources + */ + +/* + * + * The TILE-Gx TRIO shim provides connections to external devices via + * PCIe or other transaction IO standards.  The gxio_trio_ API, + * declared in <gxio/trio.h>, allows applications to allocate and + * configure TRIO IO resources like DMA command rings, memory map + * windows, and device interrupts.  The following sections introduce + * the various components of the API.  We strongly recommend reading + * the TRIO section of the IO Device Guide (UG404) before working with + * this API. + * + * @section trio__ingress TRIO Ingress Hardware Resources + * + * The TRIO ingress hardware is responsible for examining incoming + * PCIe or StreamIO packets and choosing a processing mechanism based + * on the packets' bus address.  The gxio_trio_ API can be used to + * configure different handlers for different ranges of bus address + * space.  The user can configure "mapped memory" and "scatter queue" + * regions to match incoming packets within 4kB-aligned ranges of bus + * addresses.  Each range specifies a different set of mapping + * parameters to be applied when handling the ingress packet.  The + * following sections describe how to work with MapMem and scatter + * queue regions. + * + * @subsection trio__mapmem TRIO MapMem Regions + * + * TRIO mapped memory (or MapMem) regions allow the user to map + * incoming read and write requests directly to the application's + * memory space.  MapMem regions are allocated via + * gxio_trio_alloc_memory_maps().  Given an integer MapMem number, + * applications can use gxio_trio_init_memory_map() to specify the + * range of bus addresses that will match the region and the range of + * virtual addresses to which those packets will be applied. + * + * As with many other gxio APIs, the programmer must be sure to + * register memory pages that will be used with MapMem regions.  Pages + * can be registered with TRIO by allocating an ASID (address space + * identifier) and then using gxio_trio_register_page() to register up to + * 16 pages with the hardware.  The initialization functions for + * resources that require registered memory (MapMem, scatter queues, + * push DMA, and pull DMA) then take an 'asid' parameter in order to + * configure which set of registered pages is used by each resource. + * + * @subsection trio__scatter_queue TRIO Scatter Queues + * + * The TRIO shim's scatter queue regions allow users to dynamically + * map buffers from a large address space into a small range of bus + * addresses.  This is particularly helpful for PCIe endpoint devices, + * where the host generally limits the size of BARs to tens of + * megabytes. + * + * Each scatter queue consists of a memory map region, a queue of + * tile-side buffer VAs to be mapped to that region, and a bus-mapped + * "doorbell" register that the remote endpoint can write to trigger a + * dequeue of the current buffer VA, thus swapping in a new buffer. + * The VAs pushed onto a scatter queue must be 4kB aligned, so + * applications may need to use higher-level protocols to inform + * remote entities that they should apply some additional, sub-4kB + * offset when reading or writing the scatter queue region.  For more + * information, see the IO Device Guide (UG404). + * + * @section trio__egress TRIO Egress Hardware Resources + * + * The TRIO shim supports two mechanisms for egress packet generation: + * programmed IO (PIO) and push/pull DMA.  PIO allows applications to + * create MMIO mappings for PCIe or StreamIO address space, such that + * the application can generate word-sized read or write transactions + * by issuing load or store instructions.  Push and pull DMA are tuned + * for larger transactions; they use specialized hardware engines to + * transfer large blocks of data at line rate. + * + * @subsection trio__pio TRIO Programmed IO + * + * Programmed IO allows applications to create MMIO mappings for PCIe + * or StreamIO address space.  The hardware PIO regions support access + * to PCIe configuration, IO, and memory space, but the gxio_trio API + * only supports memory space accesses.  PIO regions are allocated + * with gxio_trio_alloc_pio_regions() and initialized via + * gxio_trio_init_pio_region().  Once a region is bound to a range of + * bus address via the initialization function, the application can + * use gxio_trio_map_pio_region() to create MMIO mappings from its VA + * space onto the range of bus addresses supported by the PIO region. + * + * @subsection trio_dma TRIO Push and Pull DMA + * + * The TRIO push and pull DMA engines allow users to copy blocks of + * data between application memory and the bus.  Push DMA generates + * write packets that copy from application memory to the bus and pull + * DMA generates read packets that copy from the bus into application + * memory.  The DMA engines are managed via an API that is very + * similar to the mPIPE eDMA interface.  For a detailed explanation of + * the eDMA queue API, see @ref gxio_mpipe_wrappers. + * + * Push and pull DMA queues are allocated via + * gxio_trio_alloc_push_dma_ring() / gxio_trio_alloc_pull_dma_ring(). + * Once allocated, users generally use a ::gxio_trio_dma_queue_t + * object to manage the queue, providing easy wrappers for reserving + * command slots in the DMA command ring, filling those slots, and + * waiting for commands to complete.  DMA queues can be initialized + * via gxio_trio_init_push_dma_queue() or + * gxio_trio_init_pull_dma_queue(). + * + * See @ref trio/push_dma/app.c for an example of how to use push DMA. + * + * @section trio_shortcomings Plans for Future API Revisions + * + * The simulation framework is incomplete.  Future features include: + * + * - Support for reset and deallocation of resources. + * + * - Support for pull DMA. + * + * - Support for interrupt regions and user-space interrupt delivery. + * + * - Support for getting BAR mappings and reserving regions of BAR + *   address space. + */ +#ifndef _GXIO_TRIO_H_ +#define _GXIO_TRIO_H_ + +#include <linux/types.h> + +#include "common.h" +#include "dma_queue.h" + +#include <arch/trio_constants.h> +#include <arch/trio.h> +#include <arch/trio_pcie_intfc.h> +#include <arch/trio_pcie_rc.h> +#include <arch/trio_shm.h> +#include <hv/drv_trio_intf.h> +#include <hv/iorpc.h> + +/* A context object used to manage TRIO hardware resources. */ +typedef struct { + +	/* File descriptor for calling up to Linux (and thus the HV). */ +	int fd; + +	/* The VA at which the MAC MMIO registers are mapped. */ +	char *mmio_base_mac; + +	/* The VA at which the PIO config space are mapped for each PCIe MAC. +	   Gx36 has max 3 PCIe MACs per TRIO shim. */ +	char *mmio_base_pio_cfg[TILEGX_TRIO_PCIES]; + +#ifdef USE_SHARED_PCIE_CONFIG_REGION +	/* Index of the shared PIO region for PCI config access. */ +	int pio_cfg_index; +#else +	/* Index of the PIO region for PCI config access per MAC. */ +	int pio_cfg_index[TILEGX_TRIO_PCIES]; +#endif + +	/*  The VA at which the push DMA MMIO registers are mapped. */ +	char *mmio_push_dma[TRIO_NUM_PUSH_DMA_RINGS]; + +	/*  The VA at which the pull DMA MMIO registers are mapped. */ +	char *mmio_pull_dma[TRIO_NUM_PUSH_DMA_RINGS]; + +	/* Application space ID. */ +	unsigned int asid; + +} gxio_trio_context_t; + +/* Command descriptor for push or pull DMA. */ +typedef TRIO_DMA_DESC_t gxio_trio_dma_desc_t; + +/* A convenient, thread-safe interface to an eDMA ring. */ +typedef struct { + +	/* State object for tracking head and tail pointers. */ +	__gxio_dma_queue_t dma_queue; + +	/* The ring entries. */ +	gxio_trio_dma_desc_t *dma_descs; + +	/* The number of entries minus one. */ +	unsigned long mask_num_entries; + +	/* The log2() of the number of entries. */ +	unsigned int log2_num_entries; + +} gxio_trio_dma_queue_t; + +/* Initialize a TRIO context. + * + * This function allocates a TRIO "service domain" and maps the MMIO + * registers into the the caller's VA space. + * + * @param trio_index Which TRIO shim; Gx36 must pass 0. + * @param context Context object to be initialized. + */ +extern int gxio_trio_init(gxio_trio_context_t *context, +			  unsigned int trio_index); + +/* This indicates that an ASID hasn't been allocated. */ +#define GXIO_ASID_NULL -1 + +/* Ordering modes for map memory regions and scatter queue regions. */ +typedef enum gxio_trio_order_mode_e { +	/* Writes are not ordered.  Reads always wait for previous writes. */ +	GXIO_TRIO_ORDER_MODE_UNORDERED = +		TRIO_MAP_MEM_SETUP__ORDER_MODE_VAL_UNORDERED, +	/* Both writes and reads wait for previous transactions to complete. */ +	GXIO_TRIO_ORDER_MODE_STRICT = +		TRIO_MAP_MEM_SETUP__ORDER_MODE_VAL_STRICT, +	/* Writes are ordered unless the incoming packet has the +	   relaxed-ordering attributes set. */ +	GXIO_TRIO_ORDER_MODE_OBEY_PACKET = +		TRIO_MAP_MEM_SETUP__ORDER_MODE_VAL_REL_ORD +} gxio_trio_order_mode_t; + +/* Initialize a memory mapping region. + * + * @param context An initialized TRIO context. + * @param map A Memory map region allocated by gxio_trio_alloc_memory_map(). + * @param target_mem VA of backing memory, should be registered via + *   gxio_trio_register_page() and aligned to 4kB. + * @param target_size Length of the memory mapping, must be a multiple + * of 4kB. + * @param asid ASID to be used for Tile-side address translation. + * @param mac MAC number. + * @param bus_address Bus address at which the mapping starts. + * @param order_mode Memory ordering mode for this mapping. + * @return Zero on success, else ::GXIO_TRIO_ERR_BAD_MEMORY_MAP, + * GXIO_TRIO_ERR_BAD_ASID, or ::GXIO_TRIO_ERR_BAD_BUS_RANGE. + */ +extern int gxio_trio_init_memory_map(gxio_trio_context_t *context, +				     unsigned int map, void *target_mem, +				     size_t target_size, unsigned int asid, +				     unsigned int mac, uint64_t bus_address, +				     gxio_trio_order_mode_t order_mode); + +/* Flags that can be passed to resource allocation functions. */ +enum gxio_trio_alloc_flags_e { +	GXIO_TRIO_ALLOC_FIXED = HV_TRIO_ALLOC_FIXED, +}; + +/* Flags that can be passed to memory registration functions. */ +enum gxio_trio_mem_flags_e { +	/* Do not fill L3 when writing, and invalidate lines upon egress. */ +	GXIO_TRIO_MEM_FLAG_NT_HINT = IORPC_MEM_BUFFER_FLAG_NT_HINT, + +	/* L3 cache fills should only populate IO cache ways. */ +	GXIO_TRIO_MEM_FLAG_IO_PIN = IORPC_MEM_BUFFER_FLAG_IO_PIN, +}; + +/* Flag indicating a request generator uses a special traffic +    class. */ +#define GXIO_TRIO_FLAG_TRAFFIC_CLASS(N) HV_TRIO_FLAG_TC(N) + +/* Flag indicating a request generator uses a virtual function +    number. */ +#define GXIO_TRIO_FLAG_VFUNC(N) HV_TRIO_FLAG_VFUNC(N) + +/***************************************************************** + *                       Memory Registration                      * + ******************************************************************/ + +/* Allocate Application Space Identifiers (ASIDs).  Each ASID can + * register up to 16 page translations.  ASIDs are used by memory map + * regions, scatter queues, and DMA queues to translate application + * VAs into memory system PAs. + * + * @param context An initialized TRIO context. + * @param count Number of ASIDs required. + * @param first Index of first ASID if ::GXIO_TRIO_ALLOC_FIXED flag + *   is set, otherwise ignored. + * @param flags Flag bits, including bits from ::gxio_trio_alloc_flags_e. + * @return Index of first ASID, or ::GXIO_TRIO_ERR_NO_ASID if allocation + *   failed. + */ +extern int gxio_trio_alloc_asids(gxio_trio_context_t *context, +				 unsigned int count, unsigned int first, +				 unsigned int flags); + +#endif /* ! _GXIO_TRIO_H_ */ diff --git a/arch/tile/include/gxio/usb_host.h b/arch/tile/include/gxio/usb_host.h new file mode 100644 index 00000000000..a60a126e456 --- /dev/null +++ b/arch/tile/include/gxio/usb_host.h @@ -0,0 +1,87 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ +#ifndef _GXIO_USB_H_ +#define _GXIO_USB_H_ + +#include "common.h" + +#include <hv/drv_usb_host_intf.h> +#include <hv/iorpc.h> + +/* + * + * An API for manipulating general-purpose I/O pins. + */ + +/* + * + * The USB shim allows access to the processor's Universal Serial Bus + * connections. + */ + +/* A context object used to manage USB hardware resources. */ +typedef struct { + +	/* File descriptor for calling up to the hypervisor. */ +	int fd; + +	/* The VA at which our MMIO registers are mapped. */ +	char *mmio_base; +} gxio_usb_host_context_t; + +/* Initialize a USB context. + * + *  A properly initialized context must be obtained before any of the other + *  gxio_usb_host routines may be used. + * + * @param context Pointer to a gxio_usb_host_context_t, which will be + *  initialized by this routine, if it succeeds. + * @param usb_index Index of the USB shim to use. + * @param is_ehci Nonzero to use the EHCI interface; zero to use the OHCI + *  intereface. + * @return Zero if the context was successfully initialized, else a + *  GXIO_ERR_xxx error code. + */ +extern int gxio_usb_host_init(gxio_usb_host_context_t * context, int usb_index, +			      int is_ehci); + +/* Destroy a USB context. + * + *  Once destroyed, a context may not be used with any gxio_usb_host routines + *  other than gxio_usb_host_init().  After this routine returns, no further + *  interrupts or signals requested on this context will be delivered.  The + *  state and configuration of the pins which had been attached to this + *  context are unchanged by this operation. + * + * @param context Pointer to a gxio_usb_host_context_t. + * @return Zero if the context was successfully destroyed, else a + *  GXIO_ERR_xxx error code. + */ +extern int gxio_usb_host_destroy(gxio_usb_host_context_t * context); + +/* Retrieve the address of the shim's MMIO registers. + * + * @param context Pointer to a properly initialized gxio_usb_host_context_t. + * @return The address of the shim's MMIO registers. + */ +extern void *gxio_usb_host_get_reg_start(gxio_usb_host_context_t * context); + +/* Retrieve the length of the shim's MMIO registers. + * + * @param context Pointer to a properly initialized gxio_usb_host_context_t. + * @return The length of the shim's MMIO registers. + */ +extern size_t gxio_usb_host_get_reg_len(gxio_usb_host_context_t * context); + +#endif /* _GXIO_USB_H_ */ diff --git a/arch/tile/include/hv/drv_mpipe_intf.h b/arch/tile/include/hv/drv_mpipe_intf.h new file mode 100644 index 00000000000..6cdae3bf046 --- /dev/null +++ b/arch/tile/include/hv/drv_mpipe_intf.h @@ -0,0 +1,602 @@ +/* + * Copyright 2011 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ + +/** + * Interface definitions for the mpipe driver. + */ + +#ifndef _SYS_HV_DRV_MPIPE_INTF_H +#define _SYS_HV_DRV_MPIPE_INTF_H + +#include <arch/mpipe.h> +#include <arch/mpipe_constants.h> + + +/** Number of buffer stacks (32). */ +#define HV_MPIPE_NUM_BUFFER_STACKS \ +  (MPIPE_MMIO_INIT_DAT_GX36_1__BUFFER_STACK_MASK_WIDTH) + +/** Number of NotifRings (256). */ +#define HV_MPIPE_NUM_NOTIF_RINGS (MPIPE_NUM_NOTIF_RINGS) + +/** Number of NotifGroups (32). */ +#define HV_MPIPE_NUM_NOTIF_GROUPS (MPIPE_NUM_NOTIF_GROUPS) + +/** Number of buckets (4160). */ +#define HV_MPIPE_NUM_BUCKETS (MPIPE_NUM_BUCKETS) + +/** Number of "lo" buckets (4096). */ +#define HV_MPIPE_NUM_LO_BUCKETS 4096 + +/** Number of "hi" buckets (64). */ +#define HV_MPIPE_NUM_HI_BUCKETS \ +  (HV_MPIPE_NUM_BUCKETS - HV_MPIPE_NUM_LO_BUCKETS) + +/** Number of edma rings (24). */ +#define HV_MPIPE_NUM_EDMA_RINGS \ +  (MPIPE_MMIO_INIT_DAT_GX36_1__EDMA_POST_MASK_WIDTH) + + + + +/** A flag bit indicating a fixed resource allocation. */ +#define HV_MPIPE_ALLOC_FIXED 0x01 + +/** Offset for the config register MMIO region. */ +#define HV_MPIPE_CONFIG_MMIO_OFFSET \ +  (MPIPE_MMIO_ADDR__REGION_VAL_CFG << MPIPE_MMIO_ADDR__REGION_SHIFT) + +/** Size of the config register MMIO region. */ +#define HV_MPIPE_CONFIG_MMIO_SIZE (64 * 1024) + +/** Offset for the config register MMIO region. */ +#define HV_MPIPE_FAST_MMIO_OFFSET \ +  (MPIPE_MMIO_ADDR__REGION_VAL_IDMA << MPIPE_MMIO_ADDR__REGION_SHIFT) + +/** Size of the fast register MMIO region (IDMA, EDMA, buffer stack). */ +#define HV_MPIPE_FAST_MMIO_SIZE \ +  ((MPIPE_MMIO_ADDR__REGION_VAL_BSM + 1 - MPIPE_MMIO_ADDR__REGION_VAL_IDMA) \ +   << MPIPE_MMIO_ADDR__REGION_SHIFT) + + +/* + * Each type of resource allocation comes in quantized chunks, where + * XXX_BITS is the number of chunks, and XXX_RES_PER_BIT is the number + * of resources in each chunk. + */ + +/** Number of buffer stack chunks available (32). */ +#define HV_MPIPE_ALLOC_BUFFER_STACKS_BITS \ +  MPIPE_MMIO_INIT_DAT_GX36_1__BUFFER_STACK_MASK_WIDTH + +/** Granularity of buffer stack allocation (1). */ +#define HV_MPIPE_ALLOC_BUFFER_STACKS_RES_PER_BIT \ +  (HV_MPIPE_NUM_BUFFER_STACKS / HV_MPIPE_ALLOC_BUFFER_STACKS_BITS) + +/** Number of NotifRing chunks available (32). */ +#define HV_MPIPE_ALLOC_NOTIF_RINGS_BITS \ +  MPIPE_MMIO_INIT_DAT_GX36_0__NOTIF_RING_MASK_WIDTH + +/** Granularity of NotifRing allocation (8). */ +#define HV_MPIPE_ALLOC_NOTIF_RINGS_RES_PER_BIT \ +  (HV_MPIPE_NUM_NOTIF_RINGS / HV_MPIPE_ALLOC_NOTIF_RINGS_BITS) + +/** Number of NotifGroup chunks available (32). */ +#define HV_MPIPE_ALLOC_NOTIF_GROUPS_BITS \ +  HV_MPIPE_NUM_NOTIF_GROUPS + +/** Granularity of NotifGroup allocation (1). */ +#define HV_MPIPE_ALLOC_NOTIF_GROUPS_RES_PER_BIT \ +  (HV_MPIPE_NUM_NOTIF_GROUPS / HV_MPIPE_ALLOC_NOTIF_GROUPS_BITS) + +/** Number of lo bucket chunks available (16). */ +#define HV_MPIPE_ALLOC_LO_BUCKETS_BITS \ +  MPIPE_MMIO_INIT_DAT_GX36_0__BUCKET_RELEASE_MASK_LO_WIDTH + +/** Granularity of lo bucket allocation (256). */ +#define HV_MPIPE_ALLOC_LO_BUCKETS_RES_PER_BIT \ +  (HV_MPIPE_NUM_LO_BUCKETS / HV_MPIPE_ALLOC_LO_BUCKETS_BITS) + +/** Number of hi bucket chunks available (16). */ +#define HV_MPIPE_ALLOC_HI_BUCKETS_BITS \ +  MPIPE_MMIO_INIT_DAT_GX36_0__BUCKET_RELEASE_MASK_HI_WIDTH + +/** Granularity of hi bucket allocation (4). */ +#define HV_MPIPE_ALLOC_HI_BUCKETS_RES_PER_BIT \ +  (HV_MPIPE_NUM_HI_BUCKETS / HV_MPIPE_ALLOC_HI_BUCKETS_BITS) + +/** Number of eDMA ring chunks available (24). */ +#define HV_MPIPE_ALLOC_EDMA_RINGS_BITS \ +  MPIPE_MMIO_INIT_DAT_GX36_1__EDMA_POST_MASK_WIDTH + +/** Granularity of eDMA ring allocation (1). */ +#define HV_MPIPE_ALLOC_EDMA_RINGS_RES_PER_BIT \ +  (HV_MPIPE_NUM_EDMA_RINGS / HV_MPIPE_ALLOC_EDMA_RINGS_BITS) + + + + +/** Bit vector encoding which NotifRings are in a NotifGroup. */ +typedef struct +{ +  /** The actual bits. */ +  uint64_t ring_mask[4]; + +} gxio_mpipe_notif_group_bits_t; + + +/** Another name for MPIPE_LBL_INIT_DAT_BSTS_TBL_t. */ +typedef MPIPE_LBL_INIT_DAT_BSTS_TBL_t gxio_mpipe_bucket_info_t; + + + +/** Eight buffer stack ids. */ +typedef struct +{ +  /** The stacks. */ +  uint8_t stacks[8]; + +} gxio_mpipe_rules_stacks_t; + + +/** A destination mac address. */ +typedef struct +{ +  /** The octets. */ +  uint8_t octets[6]; + +} gxio_mpipe_rules_dmac_t; + + +/** A vlan. */ +typedef uint16_t gxio_mpipe_rules_vlan_t; + + + +/** Maximum number of characters in a link name. */ +#define GXIO_MPIPE_LINK_NAME_LEN  32 + + +/** Structure holding a link name.  Only needed, and only typedef'ed, + *  because the IORPC stub generator only handles types which are single + *  words coming before the parameter name. */ +typedef struct +{ +  /** The name itself. */ +  char name[GXIO_MPIPE_LINK_NAME_LEN]; +} +_gxio_mpipe_link_name_t; + +/** Maximum number of characters in a symbol name. */ +#define GXIO_MPIPE_SYMBOL_NAME_LEN  128 + + +/** Structure holding a symbol name.  Only needed, and only typedef'ed, + *  because the IORPC stub generator only handles types which are single + *  words coming before the parameter name. */ +typedef struct +{ +  /** The name itself. */ +  char name[GXIO_MPIPE_SYMBOL_NAME_LEN]; +} +_gxio_mpipe_symbol_name_t; + + +/** Structure holding a MAC address. */ +typedef struct +{ +  /** The address. */ +  uint8_t mac[6]; +} +_gxio_mpipe_link_mac_t; + + + +/** Request shared data permission -- that is, the ability to send and + *  receive packets -- on the specified link.  Other processes may also + *  request shared data permission on the same link. + * + *  No more than one of ::GXIO_MPIPE_LINK_DATA, ::GXIO_MPIPE_LINK_NO_DATA, + *  or ::GXIO_MPIPE_LINK_EXCL_DATA may be specifed in a gxio_mpipe_link_open() + *  call.  If none are specified, ::GXIO_MPIPE_LINK_DATA is assumed. + */ +#define GXIO_MPIPE_LINK_DATA               0x00000001UL + +/** Do not request data permission on the specified link. + * + *  No more than one of ::GXIO_MPIPE_LINK_DATA, ::GXIO_MPIPE_LINK_NO_DATA, + *  or ::GXIO_MPIPE_LINK_EXCL_DATA may be specifed in a gxio_mpipe_link_open() + *  call.  If none are specified, ::GXIO_MPIPE_LINK_DATA is assumed. + */ +#define GXIO_MPIPE_LINK_NO_DATA            0x00000002UL + +/** Request exclusive data permission -- that is, the ability to send and + *  receive packets -- on the specified link.  No other processes may + *  request data permission on this link, and if any process already has + *  data permission on it, this open will fail. + * + *  No more than one of ::GXIO_MPIPE_LINK_DATA, ::GXIO_MPIPE_LINK_NO_DATA, + *  or ::GXIO_MPIPE_LINK_EXCL_DATA may be specifed in a gxio_mpipe_link_open() + *  call.  If none are specified, ::GXIO_MPIPE_LINK_DATA is assumed. + */ +#define GXIO_MPIPE_LINK_EXCL_DATA          0x00000004UL + +/** Request shared stats permission -- that is, the ability to read and write + *  registers which contain link statistics, and to get link attributes -- + *  on the specified link.  Other processes may also request shared stats + *  permission on the same link. + * + *  No more than one of ::GXIO_MPIPE_LINK_STATS, ::GXIO_MPIPE_LINK_NO_STATS, + *  or ::GXIO_MPIPE_LINK_EXCL_STATS may be specifed in a gxio_mpipe_link_open() + *  call.  If none are specified, ::GXIO_MPIPE_LINK_STATS is assumed. + */ +#define GXIO_MPIPE_LINK_STATS              0x00000008UL + +/** Do not request stats permission on the specified link. + * + *  No more than one of ::GXIO_MPIPE_LINK_STATS, ::GXIO_MPIPE_LINK_NO_STATS, + *  or ::GXIO_MPIPE_LINK_EXCL_STATS may be specifed in a gxio_mpipe_link_open() + *  call.  If none are specified, ::GXIO_MPIPE_LINK_STATS is assumed. + */ +#define GXIO_MPIPE_LINK_NO_STATS           0x00000010UL + +/** Request exclusive stats permission -- that is, the ability to read and + *  write registers which contain link statistics, and to get link + *  attributes -- on the specified link.  No other processes may request + *  stats permission on this link, and if any process already + *  has stats permission on it, this open will fail. + * + *  Requesting exclusive stats permission is normally a very bad idea, since + *  it prevents programs like mpipe-stat from providing information on this + *  link.  Applications should only do this if they use MAC statistics + *  registers, and cannot tolerate any of the clear-on-read registers being + *  reset by other statistics programs. + * + *  No more than one of ::GXIO_MPIPE_LINK_STATS, ::GXIO_MPIPE_LINK_NO_STATS, + *  or ::GXIO_MPIPE_LINK_EXCL_STATS may be specifed in a gxio_mpipe_link_open() + *  call.  If none are specified, ::GXIO_MPIPE_LINK_STATS is assumed. + */ +#define GXIO_MPIPE_LINK_EXCL_STATS         0x00000020UL + +/** Request shared control permission -- that is, the ability to modify link + *  attributes, and read and write MAC and MDIO registers -- on the + *  specified link.  Other processes may also request shared control + *  permission on the same link. + * + *  No more than one of ::GXIO_MPIPE_LINK_CTL, ::GXIO_MPIPE_LINK_NO_CTL, + *  or ::GXIO_MPIPE_LINK_EXCL_CTL may be specifed in a gxio_mpipe_link_open() + *  call.  If none are specified, ::GXIO_MPIPE_LINK_CTL is assumed. + */ +#define GXIO_MPIPE_LINK_CTL                0x00000040UL + +/** Do not request control permission on the specified link. + * + *  No more than one of ::GXIO_MPIPE_LINK_CTL, ::GXIO_MPIPE_LINK_NO_CTL, + *  or ::GXIO_MPIPE_LINK_EXCL_CTL may be specifed in a gxio_mpipe_link_open() + *  call.  If none are specified, ::GXIO_MPIPE_LINK_CTL is assumed. + */ +#define GXIO_MPIPE_LINK_NO_CTL             0x00000080UL + +/** Request exclusive control permission -- that is, the ability to modify + *  link attributes, and read and write MAC and MDIO registers -- on the + *  specified link.  No other processes may request control permission on + *  this link, and if any process already has control permission on it, + *  this open will fail. + * + *  Requesting exclusive control permission is not always a good idea, since + *  it prevents programs like mpipe-link from configuring the link. + * + *  No more than one of ::GXIO_MPIPE_LINK_CTL, ::GXIO_MPIPE_LINK_NO_CTL, + *  or ::GXIO_MPIPE_LINK_EXCL_CTL may be specifed in a gxio_mpipe_link_open() + *  call.  If none are specified, ::GXIO_MPIPE_LINK_CTL is assumed. + */ +#define GXIO_MPIPE_LINK_EXCL_CTL           0x00000100UL + +/** Set the desired state of the link to up, allowing any speeds which are + *  supported by the link hardware, as part of this open operation; do not + *  change the desired state of the link when it is closed or the process + *  exits.  No more than one of ::GXIO_MPIPE_LINK_AUTO_UP, + *  ::GXIO_MPIPE_LINK_AUTO_UPDOWN, ::GXIO_MPIPE_LINK_AUTO_DOWN, or + *  ::GXIO_MPIPE_LINK_AUTO_NONE may be specifed in a gxio_mpipe_link_open() + *  call.  If none are specified, ::GXIO_MPIPE_LINK_AUTO_UPDOWN is assumed. + */ +#define GXIO_MPIPE_LINK_AUTO_UP            0x00000200UL + +/** Set the desired state of the link to up, allowing any speeds which are + *  supported by the link hardware, as part of this open operation; when the + *  link is closed or this process exits, if no other process has the link + *  open, set the desired state of the link to down.  No more than one of + *  ::GXIO_MPIPE_LINK_AUTO_UP, ::GXIO_MPIPE_LINK_AUTO_UPDOWN, + *  ::GXIO_MPIPE_LINK_AUTO_DOWN, or ::GXIO_MPIPE_LINK_AUTO_NONE may be + *  specifed in a gxio_mpipe_link_open() call.  If none are specified, + *  ::GXIO_MPIPE_LINK_AUTO_UPDOWN is assumed. + */ +#define GXIO_MPIPE_LINK_AUTO_UPDOWN        0x00000400UL + +/** Do not change the desired state of the link as part of the open + *  operation; when the link is closed or this process exits, if no other + *  process has the link open, set the desired state of the link to down. + *  No more than one of ::GXIO_MPIPE_LINK_AUTO_UP, + *  ::GXIO_MPIPE_LINK_AUTO_UPDOWN, ::GXIO_MPIPE_LINK_AUTO_DOWN, or + *  ::GXIO_MPIPE_LINK_AUTO_NONE may be specifed in a gxio_mpipe_link_open() + *  call.  If none are specified, ::GXIO_MPIPE_LINK_AUTO_UPDOWN is assumed. + */ +#define GXIO_MPIPE_LINK_AUTO_DOWN          0x00000800UL + +/** Do not change the desired state of the link as part of the open + *  operation; do not change the desired state of the link when it is + *  closed or the process exits.  No more than one of + *  ::GXIO_MPIPE_LINK_AUTO_UP, ::GXIO_MPIPE_LINK_AUTO_UPDOWN, + *  ::GXIO_MPIPE_LINK_AUTO_DOWN, or ::GXIO_MPIPE_LINK_AUTO_NONE may be + *  specifed in a gxio_mpipe_link_open() call.  If none are specified, + *  ::GXIO_MPIPE_LINK_AUTO_UPDOWN is assumed. + */ +#define GXIO_MPIPE_LINK_AUTO_NONE          0x00001000UL + +/** Request that this open call not complete until the network link is up. + *  The process will wait as long as necessary for this to happen; + *  applications which wish to abandon waiting for the link after a + *  specific time period should not specify this flag when opening a link, + *  but should instead call gxio_mpipe_link_wait() afterward.  The link + *  must be opened with stats permission.  Note that this flag by itself + *  does not change the desired link state; if other open flags or previous + *  link state changes have not requested a desired state of up, the open + *  call will never complete.  This flag is not available to kernel + *  clients. + */ +#define GXIO_MPIPE_LINK_WAIT               0x00002000UL + + +/* + * Note: link attributes must fit in 24 bits, since we use the top 8 bits + * of the IORPC offset word for the channel number. + */ + +/** Determine whether jumbo frames may be received.  If this attribute's + *  value value is nonzero, the MAC will accept frames of up to 10240 bytes. + *  If the value is zero, the MAC will only accept frames of up to 1544 + *  bytes.  The default value is zero. */ +#define GXIO_MPIPE_LINK_RECEIVE_JUMBO      0x010000 + +/** Determine whether to send pause frames on this link if the mPIPE packet + *  FIFO is nearly full.  If the value is zero, pause frames are not sent. + *  If the value is nonzero, it is the delay value which will be sent in any + *  pause frames which are output, in units of 512 bit times. + * + *  Bear in mind that in almost all circumstances, the mPIPE packet FIFO + *  will never fill up, since mPIPE will empty it as fast as or faster than + *  the incoming data rate, by either delivering or dropping packets.  The + *  only situation in which this is not true is if the memory and cache + *  subsystem is extremely heavily loaded, and mPIPE cannot perform DMA of + *  packet data to memory in a timely fashion.  In particular, pause frames + *  will <em>not</em> be sent if packets cannot be delivered because + *  NotifRings are full, buckets are full, or buffers are not available in + *  a buffer stack. */ +#define GXIO_MPIPE_LINK_SEND_PAUSE         0x020000 + +/** Determine whether to suspend output on the receipt of pause frames. + *  If the value is nonzero, mPIPE shim will suspend output on the link's + *  channel when a pause frame is received.  If the value is zero, pause + *  frames will be ignored.  The default value is zero. */ +#define GXIO_MPIPE_LINK_RECEIVE_PAUSE      0x030000 + +/** Interface MAC address.  The value is a 6-byte MAC address, in the least + *  significant 48 bits of the value; in other words, an address which would + *  be printed as '12:34:56:78:90:AB' in IEEE 802 canonical format would + *  be returned as 0x12345678ab. + * + *  Depending upon the overall system design, a MAC address may or may not + *  be available for each interface.  Note that the interface's MAC address + *  does not limit the packets received on its channel, although the + *  classifier's rules could be configured to do that.  Similarly, the MAC + *  address is not used when transmitting packets, although applications + *  could certainly decide to use the assigned address as a source MAC + *  address when doing so.  This attribute may only be retrieved with + *  gxio_mpipe_link_get_attr(); it may not be modified. + */ +#define GXIO_MPIPE_LINK_MAC                0x040000 + +/** Determine whether to discard egress packets on link down. If this value + *  is nonzero, packets sent on this link while the link is down will be + *  discarded.  If this value is zero, no packets will be sent on this link + *  while it is down.  The default value is one. */ +#define GXIO_MPIPE_LINK_DISCARD_IF_DOWN    0x050000 + +/** Possible link state.  The value is a combination of link state flags, + *  ORed together, that indicate link modes which are actually supported by + *  the hardware.  This attribute may only be retrieved with + *  gxio_mpipe_link_get_attr(); it may not be modified. */ +#define GXIO_MPIPE_LINK_POSSIBLE_STATE     0x060000 + +/** Current link state.  The value is a combination of link state flags, + *  ORed together, that indicate the current state of the hardware.  If the + *  link is down, the value ANDed with ::GXIO_MPIPE_LINK_SPEED will be zero; + *  if the link is up, the value ANDed with ::GXIO_MPIPE_LINK_SPEED will + *  result in exactly one of the speed values, indicating the current speed. + *  This attribute may only be retrieved with gxio_mpipe_link_get_attr(); it + *  may not be modified. */ +#define GXIO_MPIPE_LINK_CURRENT_STATE      0x070000 + +/** Desired link state. The value is a conbination of flags, which specify + *  the desired state for the link.  With gxio_mpipe_link_set_attr(), this + *  will, in the background, attempt to bring up the link using whichever of + *  the requested flags are reasonable, or take down the link if the flags + *  are zero.  The actual link up or down operation may happen after this + *  call completes.  If the link state changes in the future, the system + *  will continue to try to get back to the desired link state; for + *  instance, if the link is brought up successfully, and then the network + *  cable is disconnected, the link will go down.  However, the desired + *  state of the link is still up, so if the cable is reconnected, the link + *  will be brought up again. + * + *  With gxio_mpipe_link_set_attr(), this will indicate the desired state + *  for the link, as set with a previous gxio_mpipe_link_set_attr() call, + *  or implicitly by a gxio_mpipe_link_open() or link close operation. + *  This may not reflect the current state of the link; to get that, use + *  ::GXIO_MPIPE_LINK_CURRENT_STATE. + */ +#define GXIO_MPIPE_LINK_DESIRED_STATE      0x080000 + + + +/** Link can run, should run, or is running at 10 Mbps. */ +#define GXIO_MPIPE_LINK_10M        0x0000000000000001UL + +/** Link can run, should run, or is running at 100 Mbps. */ +#define GXIO_MPIPE_LINK_100M       0x0000000000000002UL + +/** Link can run, should run, or is running at 1 Gbps. */ +#define GXIO_MPIPE_LINK_1G         0x0000000000000004UL + +/** Link can run, should run, or is running at 10 Gbps. */ +#define GXIO_MPIPE_LINK_10G        0x0000000000000008UL + +/** Link can run, should run, or is running at 20 Gbps. */ +#define GXIO_MPIPE_LINK_20G        0x0000000000000010UL + +/** Link can run, should run, or is running at 25 Gbps. */ +#define GXIO_MPIPE_LINK_25G        0x0000000000000020UL + +/** Link can run, should run, or is running at 50 Gbps. */ +#define GXIO_MPIPE_LINK_50G        0x0000000000000040UL + +/** Link should run at the highest speed supported by the link and by + *  the device connected to the link.  Only usable as a value for + *  the link's desired state; never returned as a value for the current + *  or possible states. */ +#define GXIO_MPIPE_LINK_ANYSPEED   0x0000000000000800UL + +/** All legal link speeds.  This value is provided for use in extracting + *  the speed-related subset of the link state flags; it is not intended + *  to be set directly as a value for one of the GXIO_MPIPE_LINK_xxx_STATE + *  attributes.  A link is up or is requested to be up if its current or + *  desired state, respectively, ANDED with this value, is nonzero. */ +#define GXIO_MPIPE_LINK_SPEED_MASK 0x0000000000000FFFUL + +/** Link can run, should run, or is running in MAC loopback mode.  This + *  loops transmitted packets back to the receiver, inside the Tile + *  Processor. */ +#define GXIO_MPIPE_LINK_LOOP_MAC   0x0000000000001000UL + +/** Link can run, should run, or is running in PHY loopback mode.  This + *  loops transmitted packets back to the receiver, inside the external + *  PHY chip. */ +#define GXIO_MPIPE_LINK_LOOP_PHY   0x0000000000002000UL + +/** Link can run, should run, or is running in external loopback mode. + *  This requires that an external loopback plug be installed on the + *  Ethernet port.  Note that only some links require that this be + *  configured via the gxio_mpipe_link routines; other links can do + *  external loopack with the plug and no special configuration. */ +#define GXIO_MPIPE_LINK_LOOP_EXT   0x0000000000004000UL + +/** All legal loopback types. */ +#define GXIO_MPIPE_LINK_LOOP_MASK  0x000000000000F000UL + +/** Link can run, should run, or is running in full-duplex mode. + *  If neither ::GXIO_MPIPE_LINK_FDX nor ::GXIO_MPIPE_LINK_HDX are + *  specified in a set of desired state flags, both are assumed. */ +#define GXIO_MPIPE_LINK_FDX        0x0000000000010000UL + +/** Link can run, should run, or is running in half-duplex mode. + *  If neither ::GXIO_MPIPE_LINK_FDX nor ::GXIO_MPIPE_LINK_HDX are + *  specified in a set of desired state flags, both are assumed. */ +#define GXIO_MPIPE_LINK_HDX        0x0000000000020000UL + + +/** An individual rule. */ +typedef struct +{ +  /** The total size. */ +  uint16_t size; + +  /** The priority. */ +  int16_t priority; + +  /** The "headroom" in each buffer. */ +  uint8_t headroom; + +  /** The "tailroom" in each buffer. */ +  uint8_t tailroom; + +  /** The "capacity" of the largest buffer. */ +  uint16_t capacity; + +  /** The mask for converting a flow hash into a bucket. */ +  uint16_t bucket_mask; + +  /** The offset for converting a flow hash into a bucket. */ +  uint16_t bucket_first; + +  /** The buffer stack ids. */ +  gxio_mpipe_rules_stacks_t stacks; + +  /** The actual channels. */ +  uint32_t channel_bits; + +  /** The number of dmacs. */ +  uint16_t num_dmacs; + +  /** The number of vlans. */ +  uint16_t num_vlans; + +  /** The actual dmacs and vlans. */ +  uint8_t dmacs_and_vlans[]; + +} gxio_mpipe_rules_rule_t; + + +/** A list of classifier rules. */ +typedef struct +{ +  /** The offset to the end of the current rule. */ +  uint16_t tail; + +  /** The offset to the start of the current rule. */ +  uint16_t head; + +  /** The actual rules. */ +  uint8_t rules[4096 - 4]; + +} gxio_mpipe_rules_list_t; + + + + +/** mPIPE statistics structure. These counters include all relevant + *  events occurring on all links within the mPIPE shim. */ +typedef struct +{ +  /** Number of ingress packets dropped for any reason. */ +  uint64_t ingress_drops; +  /** Number of ingress packets dropped because a buffer stack was empty. */ +  uint64_t ingress_drops_no_buf; +  /** Number of ingress packets dropped or truncated due to lack of space in +   *  the iPkt buffer. */ +  uint64_t ingress_drops_ipkt; +  /** Number of ingress packets dropped by the classifier or load balancer */ +  uint64_t ingress_drops_cls_lb; +  /** Total number of ingress packets. */ +  uint64_t ingress_packets; +  /** Total number of egress packets. */ +  uint64_t egress_packets; +  /** Total number of ingress bytes. */ +  uint64_t ingress_bytes; +  /** Total number of egress bytes. */ +  uint64_t egress_bytes; +} +gxio_mpipe_stats_t; + + +#endif /* _SYS_HV_DRV_MPIPE_INTF_H */ diff --git a/arch/tile/include/hv/drv_trio_intf.h b/arch/tile/include/hv/drv_trio_intf.h new file mode 100644 index 00000000000..ef9f3f52ee2 --- /dev/null +++ b/arch/tile/include/hv/drv_trio_intf.h @@ -0,0 +1,195 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ + +/** + * Interface definitions for the trio driver. + */ + +#ifndef _SYS_HV_DRV_TRIO_INTF_H +#define _SYS_HV_DRV_TRIO_INTF_H + +#include <arch/trio.h> + +/** The vendor ID for all Tilera processors. */ +#define TILERA_VENDOR_ID 0x1a41 + +/** The device ID for the Gx36 processor. */ +#define TILERA_GX36_DEV_ID 0x0200 + +/** Device ID for our internal bridge when running as RC. */ +#define TILERA_GX36_RC_DEV_ID 0x2000 + +/** Maximum number of TRIO interfaces. */ +#define TILEGX_NUM_TRIO         2 + +/** Gx36 has max 3 PCIe MACs per TRIO interface. */ +#define TILEGX_TRIO_PCIES       3 + +/** Specify port properties for a PCIe MAC. */ +struct pcie_port_property +{ +  /** If true, the link can be configured in PCIe root complex mode. */ +  uint8_t allow_rc: 1; + +  /** If true, the link can be configured in PCIe endpoint mode. */ +  uint8_t allow_ep: 1; + +  /** If true, the link can be configured in StreamIO mode. */ +  uint8_t allow_sio: 1; + +  /** If true, the link is allowed to support 1-lane operation. Software +   *  will not consider it an error if the link comes up as a x1 link. */ +  uint8_t allow_x1: 1; + +  /** If true, the link is allowed to support 2-lane operation. Software +   *  will not consider it an error if the link comes up as a x2 link. */ +  uint8_t allow_x2: 1; + +  /** If true, the link is allowed to support 4-lane operation. Software +   *  will not consider it an error if the link comes up as a x4 link. */ +  uint8_t allow_x4: 1; + +  /** If true, the link is allowed to support 8-lane operation. Software +   *  will not consider it an error if the link comes up as a x8 link. */ +  uint8_t allow_x8: 1; + +  /** Reserved. */ +  uint8_t reserved: 1; + +}; + +/** Configurations can be issued to configure a char stream interrupt. */ +typedef enum pcie_stream_intr_config_sel_e +{ +  /** Interrupt configuration for memory map regions. */ +  MEM_MAP_SEL, + +  /** Interrupt configuration for push DMAs. */ +  PUSH_DMA_SEL, + +  /** Interrupt configuration for pull DMAs. */ +  PULL_DMA_SEL, +} +pcie_stream_intr_config_sel_t; + + +/** The mmap file offset (PA) of the TRIO config region. */ +#define HV_TRIO_CONFIG_OFFSET                                        \ +  ((unsigned long long)TRIO_MMIO_ADDRESS_SPACE__REGION_VAL_CFG <<   \ +    TRIO_MMIO_ADDRESS_SPACE__REGION_SHIFT) + +/** The maximum size of the TRIO config region. */ +#define HV_TRIO_CONFIG_SIZE                                 \ +  (1ULL << TRIO_CFG_REGION_ADDR__REGION_SHIFT) + +/** Size of the config region mapped into client. We can't use + *  TRIO_MMIO_ADDRESS_SPACE__OFFSET_WIDTH because it + *  will require the kernel to allocate 4GB VA space + *  from the VMALLOC region which has a total range + *  of 4GB. + */ +#define HV_TRIO_CONFIG_IOREMAP_SIZE                            \ +  ((uint64_t) 1 << TRIO_CFG_REGION_ADDR__PROT_SHIFT) + +/** The mmap file offset (PA) of a scatter queue region. */ +#define HV_TRIO_SQ_OFFSET(queue)                                        \ +  (((unsigned long long)TRIO_MMIO_ADDRESS_SPACE__REGION_VAL_MAP_SQ <<   \ +    TRIO_MMIO_ADDRESS_SPACE__REGION_SHIFT) |                            \ +   ((queue) << TRIO_MAP_SQ_REGION_ADDR__SQ_SEL_SHIFT)) + +/** The maximum size of a scatter queue region. */ +#define HV_TRIO_SQ_SIZE                                 \ +  (1ULL << TRIO_MAP_SQ_REGION_ADDR__SQ_SEL_SHIFT) + + +/** The "hardware MMIO region" of the first PIO region. */ +#define HV_TRIO_FIRST_PIO_REGION 8 + +/** The mmap file offset (PA) of a PIO region. */ +#define HV_TRIO_PIO_OFFSET(region)                           \ +  (((unsigned long long)(region) + HV_TRIO_FIRST_PIO_REGION) \ +   << TRIO_PIO_REGIONS_ADDR__REGION_SHIFT) + +/** The maximum size of a PIO region. */ +#define HV_TRIO_PIO_SIZE (1ULL << TRIO_PIO_REGIONS_ADDR__ADDR_WIDTH) + + +/** The mmap file offset (PA) of a push DMA region. */ +#define HV_TRIO_PUSH_DMA_OFFSET(ring)                                   \ +  (((unsigned long long)TRIO_MMIO_ADDRESS_SPACE__REGION_VAL_PUSH_DMA << \ +    TRIO_MMIO_ADDRESS_SPACE__REGION_SHIFT) |                            \ +   ((ring) << TRIO_PUSH_DMA_REGION_ADDR__RING_SEL_SHIFT)) + +/** The mmap file offset (PA) of a pull DMA region. */ +#define HV_TRIO_PULL_DMA_OFFSET(ring)                                   \ +  (((unsigned long long)TRIO_MMIO_ADDRESS_SPACE__REGION_VAL_PULL_DMA << \ +    TRIO_MMIO_ADDRESS_SPACE__REGION_SHIFT) |                            \ +   ((ring) << TRIO_PULL_DMA_REGION_ADDR__RING_SEL_SHIFT)) + +/** The maximum size of a DMA region. */ +#define HV_TRIO_DMA_REGION_SIZE                         \ +  (1ULL << TRIO_PUSH_DMA_REGION_ADDR__RING_SEL_SHIFT) + + +/** The mmap file offset (PA) of a Mem-Map interrupt region. */ +#define HV_TRIO_MEM_MAP_INTR_OFFSET(map)                                 \ +  (((unsigned long long)TRIO_MMIO_ADDRESS_SPACE__REGION_VAL_MAP_MEM <<   \ +    TRIO_MMIO_ADDRESS_SPACE__REGION_SHIFT) |                            \ +   ((map) << TRIO_MAP_MEM_REGION_ADDR__MAP_SEL_SHIFT)) + +/** The maximum size of a Mem-Map interrupt region. */ +#define HV_TRIO_MEM_MAP_INTR_SIZE                                 \ +  (1ULL << TRIO_MAP_MEM_REGION_ADDR__MAP_SEL_SHIFT) + + +/** A flag bit indicating a fixed resource allocation. */ +#define HV_TRIO_ALLOC_FIXED 0x01 + +/** TRIO requires that all mappings have 4kB aligned start addresses. */ +#define HV_TRIO_PAGE_SHIFT 12 + +/** TRIO requires that all mappings have 4kB aligned start addresses. */ +#define HV_TRIO_PAGE_SIZE (1ull << HV_TRIO_PAGE_SHIFT) + + +/* Specify all PCIe port properties for a TRIO. */ +struct pcie_trio_ports_property +{ +  struct pcie_port_property ports[TILEGX_TRIO_PCIES]; +}; + +/* Flags indicating traffic class. */ +#define HV_TRIO_FLAG_TC_SHIFT 4 +#define HV_TRIO_FLAG_TC_RMASK 0xf +#define HV_TRIO_FLAG_TC(N) \ +  ((((N) & HV_TRIO_FLAG_TC_RMASK) + 1) << HV_TRIO_FLAG_TC_SHIFT) + +/* Flags indicating virtual functions. */ +#define HV_TRIO_FLAG_VFUNC_SHIFT 8 +#define HV_TRIO_FLAG_VFUNC_RMASK 0xff +#define HV_TRIO_FLAG_VFUNC(N) \ +  ((((N) & HV_TRIO_FLAG_VFUNC_RMASK) + 1) << HV_TRIO_FLAG_VFUNC_SHIFT) + + +/* Flag indicating an ordered PIO region. */ +#define HV_TRIO_PIO_FLAG_ORDERED (1 << 16) + +/* Flags indicating special types of PIO regions. */ +#define HV_TRIO_PIO_FLAG_SPACE_SHIFT 17 +#define HV_TRIO_PIO_FLAG_SPACE_MASK (0x3 << HV_TRIO_PIO_FLAG_SPACE_SHIFT) +#define HV_TRIO_PIO_FLAG_CONFIG_SPACE (0x1 << HV_TRIO_PIO_FLAG_SPACE_SHIFT) +#define HV_TRIO_PIO_FLAG_IO_SPACE (0x2 << HV_TRIO_PIO_FLAG_SPACE_SHIFT) + + +#endif /* _SYS_HV_DRV_TRIO_INTF_H */ diff --git a/arch/tile/include/hv/drv_usb_host_intf.h b/arch/tile/include/hv/drv_usb_host_intf.h new file mode 100644 index 00000000000..24ce774a3f1 --- /dev/null +++ b/arch/tile/include/hv/drv_usb_host_intf.h @@ -0,0 +1,39 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ + +/** + * Interface definitions for the USB host driver. + */ + +#ifndef _SYS_HV_DRV_USB_HOST_INTF_H +#define _SYS_HV_DRV_USB_HOST_INTF_H + +#include <arch/usb_host.h> + + +/** Offset for the EHCI register MMIO region. */ +#define HV_USB_HOST_MMIO_OFFSET_EHCI ((uint64_t) USB_HOST_HCCAPBASE_REG) + +/** Offset for the OHCI register MMIO region. */ +#define HV_USB_HOST_MMIO_OFFSET_OHCI ((uint64_t) USB_HOST_OHCD_HC_REVISION_REG) + +/** Size of the register MMIO region.  This turns out to be the same for + *  both EHCI and OHCI. */ +#define HV_USB_HOST_MMIO_SIZE ((uint64_t) 0x1000) + +/** The number of service domains supported by the USB host shim. */ +#define HV_USB_HOST_NUM_SVC_DOM 1 + + +#endif /* _SYS_HV_DRV_USB_HOST_INTF_H */ diff --git a/arch/tile/include/hv/iorpc.h b/arch/tile/include/hv/iorpc.h new file mode 100644 index 00000000000..89c72a5d934 --- /dev/null +++ b/arch/tile/include/hv/iorpc.h @@ -0,0 +1,714 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ +#ifndef _HV_IORPC_H_ +#define _HV_IORPC_H_ + +/** + * + * Error codes and struct definitions for the IO RPC library. + * + * The hypervisor's IO RPC component provides a convenient way for + * driver authors to proxy system calls between user space, linux, and + * the hypervisor driver.  The core of the system is a set of Python + * files that take ".idl" files as input and generates the following + * source code: + * + * - _rpc_call() routines for use in userspace IO libraries.  These + * routines take an argument list specified in the .idl file, pack the + * arguments in to a buffer, and read or write that buffer via the + * Linux iorpc driver. + * + * - dispatch_read() and dispatch_write() routines that hypervisor + * drivers can use to implement most of their dev_pread() and + * dev_pwrite() methods.  These routines decode the incoming parameter + * blob, permission check and translate parameters where appropriate, + * and then invoke a callback routine for whichever RPC call has + * arrived.  The driver simply implements the set of callback + * routines. + * + * The IO RPC system also includes the Linux 'iorpc' driver, which + * proxies calls between the userspace library and the hypervisor + * driver.  The Linux driver is almost entirely device agnostic; it + * watches for special flags indicating cases where a memory buffer + * address might need to be translated, etc.  As a result, driver + * writers can avoid many of the problem cases related to registering + * hardware resources like memory pages or interrupts.  However, the + * drivers must be careful to obey the conventions documented below in + * order to work properly with the generic Linux iorpc driver. + * + * @section iorpc_domains Service Domains + * + * All iorpc-based drivers must support a notion of service domains. + * A service domain is basically an application context - state + * indicating resources that are allocated to that particular app + * which it may access and (perhaps) other applications may not + * access.  Drivers can support any number of service domains they + * choose.  In some cases the design is limited by a number of service + * domains supported by the IO hardware; in other cases the service + * domains are a purely software concept and the driver chooses a + * maximum number of domains based on how much state memory it is + * willing to preallocate. + * + * For example, the mPIPE driver only supports as many service domains + * as are supported by the mPIPE hardware.  This limitation is + * required because the hardware implements its own MMIO protection + * scheme to allow large MMIO mappings while still protecting small + * register ranges within the page that should only be accessed by the + * hypervisor. + * + * In contrast, drivers with no hardware service domain limitations + * (for instance the TRIO shim) can implement an arbitrary number of + * service domains.  In these cases, each service domain is limited to + * a carefully restricted set of legal MMIO addresses if necessary to + * keep one application from corrupting another application's state. + * + * @section iorpc_conventions System Call Conventions + * + * The driver's open routine is responsible for allocating a new + * service domain for each hv_dev_open() call.  By convention, the + * return value from open() should be the service domain number on + * success, or GXIO_ERR_NO_SVC_DOM if no more service domains are + * available. + * + * The implementations of hv_dev_pread() and hv_dev_pwrite() are + * responsible for validating the devhdl value passed up by the + * client.  Since the device handle returned by hv_dev_open() should + * embed the positive service domain number, drivers should make sure + * that DRV_HDL2BITS(devhdl) is a legal service domain.  If the client + * passes an illegal service domain number, the routine should return + * GXIO_ERR_INVAL_SVC_DOM.  Once the service domain number has been + * validated, the driver can copy to/from the client buffer and call + * the dispatch_read() or dispatch_write() methods created by the RPC + * generator. + * + * The hv_dev_close() implementation should reset all service domain + * state and put the service domain back on a free list for + * reallocation by a future application.  In most cases, this will + * require executing a hardware reset or drain flow and denying any + * MMIO regions that were created for the service domain. + * + * @section iorpc_data Special Data Types + * + * The .idl file syntax allows the creation of syscalls with special + * parameters that require permission checks or translations as part + * of the system call path.  Because of limitations in the code + * generator, APIs are generally limited to just one of these special + * parameters per system call, and they are sometimes required to be + * the first or last parameter to the call.  Special parameters + * include: + * + * @subsection iorpc_mem_buffer MEM_BUFFER + * + * The MEM_BUFFER() datatype allows user space to "register" memory + * buffers with a device.  Registering memory accomplishes two tasks: + * Linux keeps track of all buffers that might be modified by a + * hardware device, and the hardware device drivers bind registered + * buffers to particular hardware resources like ingress NotifRings. + * The MEM_BUFFER() idl syntax can take extra flags like ALIGN_64KB, + * ALIGN_SELF_SIZE, and FLAGS indicating that memory buffers must have + * certain alignment or that the user should be able to pass a "memory + * flags" word specifying attributes like nt_hint or IO cache pinning. + * The parser will accept multiple MEM_BUFFER() flags. + * + * Implementations must obey the following conventions when + * registering memory buffers via the iorpc flow.  These rules are a + * result of the Linux driver implementation, which needs to keep + * track of how many times a particular page has been registered with + * the hardware so that it can release the page when all those + * registrations are cleared. + * + * - Memory registrations that refer to a resource which has already + * been bound must return GXIO_ERR_ALREADY_INIT.  Thus, it is an + * error to register memory twice without resetting (i.e. closing) the + * resource in between.  This convention keeps the Linux driver from + * having to track which particular devices a page is bound to. + * + * - At present, a memory registration is only cleared when the + * service domain is reset.  In this case, the Linux driver simply + * closes the HV device file handle and then decrements the reference + * counts of all pages that were previously registered with the + * device. + * + * - In the future, we may add a mechanism for unregistering memory. + * One possible implementation would require that the user specify + * which buffer is currently registered.  The HV would then verify + * that that page was actually the one currently mapped and return + * success or failure to Linux, which would then only decrement the + * page reference count if the addresses were mapped.  Another scheme + * might allow Linux to pass a token to the HV to be returned when the + * resource is unmapped. + * + * @subsection iorpc_interrupt INTERRUPT + * + * The INTERRUPT .idl datatype allows the client to bind hardware + * interrupts to a particular combination of IPI parameters - CPU, IPI + * PL, and event bit number.  This data is passed via a special + * datatype so that the Linux driver can validate the CPU and PL and + * the HV generic iorpc code can translate client CPUs to real CPUs. + * + * @subsection iorpc_pollfd_setup POLLFD_SETUP + * + * The POLLFD_SETUP .idl datatype allows the client to set up hardware + * interrupt bindings which are received by Linux but which are made + * visible to user processes as state transitions on a file descriptor; + * this allows user processes to use Linux primitives, such as poll(), to + * await particular hardware events.  This data is passed via a special + * datatype so that the Linux driver may recognize the pollable file + * descriptor and translate it to a set of interrupt target information, + * and so that the HV generic iorpc code can translate client CPUs to real + * CPUs. + * + * @subsection iorpc_pollfd POLLFD + * + * The POLLFD .idl datatype allows manipulation of hardware interrupt + * bindings set up via the POLLFD_SETUP datatype; common operations are + * resetting the state of the requested interrupt events, and unbinding any + * bound interrupts.  This data is passed via a special datatype so that + * the Linux driver may recognize the pollable file descriptor and + * translate it to an interrupt identifier previously supplied by the + * hypervisor as the result of an earlier pollfd_setup operation. + * + * @subsection iorpc_blob BLOB + * + * The BLOB .idl datatype allows the client to write an arbitrary + * length string of bytes up to the hypervisor driver.  This can be + * useful for passing up large, arbitrarily structured data like + * classifier programs.  The iorpc stack takes care of validating the + * buffer VA and CPA as the data passes up to the hypervisor.  Unlike + * MEM_BUFFER(), the buffer is not registered - Linux does not bump + * page refcounts and the HV driver should not reuse the buffer once + * the system call is complete. + * + * @section iorpc_translation Translating User Space Calls + * + * The ::iorpc_offset structure describes the formatting of the offset + * that is passed to pread() or pwrite() as part of the generated RPC code. + * When the user calls up to Linux, the rpc code fills in all the fields of + * the offset, including a 16-bit opcode, a 16 bit format indicator, and 32 + * bits of user-specified "sub-offset".  The opcode indicates which syscall + * is being requested.  The format indicates whether there is a "prefix + * struct" at the start of the memory buffer passed to pwrite(), and if so + * what data is in that prefix struct.  These prefix structs are used to + * implement special datatypes like MEM_BUFFER() and INTERRUPT - we arrange + * to put data that needs translation and permission checks at the start of + * the buffer so that the Linux driver and generic portions of the HV iorpc + * code can easily access the data.  The 32 bits of user-specified + * "sub-offset" are most useful for pread() calls where the user needs to + * also pass in a few bits indicating which register to read, etc. + * + * The Linux iorpc driver watches for system calls that contain prefix + * structs so that it can translate parameters and bump reference + * counts as appropriate.  It does not (currently) have any knowledge + * of the per-device opcodes - it doesn't care what operation you're + * doing to mPIPE, so long as it can do all the generic book-keeping. + * The hv/iorpc.h header file defines all of the generic encoding bits + * needed to translate iorpc calls without knowing which particular + * opcode is being issued. + * + * @section iorpc_globals Global iorpc Calls + * + * Implementing mmap() required adding some special iorpc syscalls + * that are only called by the Linux driver, never by userspace. + * These include get_mmio_base() and check_mmio_offset().  These + * routines are described in globals.idl and must be included in every + * iorpc driver.  By providing these routines in every driver, Linux's + * mmap implementation can easily get the PTE bits it needs and + * validate the PA offset without needing to know the per-device + * opcodes to perform those tasks. + * + * @section iorpc_kernel Supporting gxio APIs in the Kernel + * + * The iorpc code generator also supports generation of kernel code + * implementing the gxio APIs.  This capability is currently used by + * the mPIPE network driver, and will likely be used by the TRIO root + * complex and endpoint drivers and perhaps an in-kernel crypto + * driver.  Each driver that wants to instantiate iorpc calls in the + * kernel needs to generate a kernel version of the generate rpc code + * and (probably) copy any related gxio source files into the kernel. + * The mPIPE driver provides a good example of this pattern. + */ + +#ifdef __KERNEL__ +#include <linux/stddef.h> +#else +#include <stddef.h> +#endif + +#if defined(__HV__) +#include <hv/hypervisor.h> +#elif defined(__KERNEL__) +#include "hypervisor.h" +#include <linux/types.h> +#else +#include <stdint.h> +#endif + + +/** Code indicating translation services required within the RPC path. + * These indicate whether there is a translatable struct at the start + * of the RPC buffer and what information that struct contains. + */ +enum iorpc_format_e +{ +  /** No translation required, no prefix struct. */ +  IORPC_FORMAT_NONE, + +  /** No translation required, no prefix struct, no access to this +   *  operation from user space. */ +  IORPC_FORMAT_NONE_NOUSER, + +  /** Prefix struct contains user VA and size. */ +  IORPC_FORMAT_USER_MEM, + +  /** Prefix struct contains CPA, size, and homing bits. */ +  IORPC_FORMAT_KERNEL_MEM, + +  /** Prefix struct contains interrupt. */ +  IORPC_FORMAT_KERNEL_INTERRUPT, + +  /** Prefix struct contains user-level interrupt. */ +  IORPC_FORMAT_USER_INTERRUPT, + +  /** Prefix struct contains pollfd_setup (interrupt information). */ +  IORPC_FORMAT_KERNEL_POLLFD_SETUP, + +  /** Prefix struct contains user-level pollfd_setup (file descriptor). */ +  IORPC_FORMAT_USER_POLLFD_SETUP, + +  /** Prefix struct contains pollfd (interrupt cookie). */ +  IORPC_FORMAT_KERNEL_POLLFD, + +  /** Prefix struct contains user-level pollfd (file descriptor). */ +  IORPC_FORMAT_USER_POLLFD, +}; + + +/** Generate an opcode given format and code. */ +#define IORPC_OPCODE(FORMAT, CODE) (((FORMAT) << 16) | (CODE)) + +/** The offset passed through the read() and write() system calls +    combines an opcode with 32 bits of user-specified offset. */ +union iorpc_offset +{ +#ifndef __BIG_ENDIAN__ +  uint64_t offset;              /**< All bits. */ + +  struct +  { +    uint16_t code;              /**< RPC code. */ +    uint16_t format;            /**< iorpc_format_e */ +    uint32_t sub_offset;        /**< caller-specified offset. */ +  }; + +  uint32_t opcode;              /**< Opcode combines code & format. */ +#else +  uint64_t offset;              /**< All bits. */ + +  struct +  { +    uint32_t sub_offset;        /**< caller-specified offset. */ +    uint16_t format;            /**< iorpc_format_e */ +    uint16_t code;              /**< RPC code. */ +  }; + +  struct +  { +    uint32_t padding; +    uint32_t opcode;              /**< Opcode combines code & format. */ +  }; +#endif +}; + + +/** Homing and cache hinting bits that can be used by IO devices. */ +struct iorpc_mem_attr +{ +  unsigned int lotar_x:4;       /**< lotar X bits (or Gx page_mask). */ +  unsigned int lotar_y:4;       /**< lotar Y bits (or Gx page_offset). */ +  unsigned int hfh:1;           /**< Uses hash-for-home. */ +  unsigned int nt_hint:1;       /**< Non-temporal hint. */ +  unsigned int io_pin:1;        /**< Only fill 'IO' cache ways. */ +}; + +/** Set the nt_hint bit. */ +#define IORPC_MEM_BUFFER_FLAG_NT_HINT (1 << 0) + +/** Set the IO pin bit. */ +#define IORPC_MEM_BUFFER_FLAG_IO_PIN (1 << 1) + + +/** A structure used to describe memory registration.  Different +    protection levels describe memory differently, so this union +    contains all the different possible descriptions.  As a request +    moves up the call chain, each layer translates from one +    description format to the next.  In particular, the Linux iorpc +    driver translates user VAs into CPAs and homing parameters. */ +union iorpc_mem_buffer +{ +  struct +  { +    uint64_t va;                /**< User virtual address. */ +    uint64_t size;              /**< Buffer size. */ +    unsigned int flags;         /**< nt_hint, IO pin. */ +  } +  user;                         /**< Buffer as described by user apps. */ + +  struct +  { +    unsigned long long cpa;     /**< Client physical address. */ +#if defined(__KERNEL__) || defined(__HV__) +    size_t size;                /**< Buffer size. */ +    HV_PTE pte;                 /**< PTE describing memory homing. */ +#else +    uint64_t size; +    uint64_t pte; +#endif +    unsigned int flags;         /**< nt_hint, IO pin. */ +  } +  kernel;                       /**< Buffer as described by kernel. */ + +  struct +  { +    unsigned long long pa;      /**< Physical address. */ +    size_t size;                /**< Buffer size. */ +    struct iorpc_mem_attr attr;      /**< Homing and locality hint bits. */ +  } +  hv;                           /**< Buffer parameters for HV driver. */ +}; + + +/** A structure used to describe interrupts.  The format differs slightly + *  for user and kernel interrupts.  As with the mem_buffer_t, translation + *  between the formats is done at each level. */ +union iorpc_interrupt +{ +  struct +  { +    int cpu;   /**< CPU. */ +    int event; /**< evt_num */ +  } +  user;        /**< Interrupt as described by user applications. */ + +  struct +  { +    int x;     /**< X coord. */ +    int y;     /**< Y coord. */ +    int ipi;   /**< int_num */ +    int event; /**< evt_num */ +  } +  kernel;      /**< Interrupt as described by the kernel. */ + +}; + + +/** A structure used to describe interrupts used with poll().  The format + *  differs significantly for requests from user to kernel, and kernel to + *  hypervisor.  As with the mem_buffer_t, translation between the formats + *  is done at each level. */ +union iorpc_pollfd_setup +{ +  struct +  { +    int fd;    /**< Pollable file descriptor. */ +  } +  user;        /**< pollfd_setup as described by user applications. */ + +  struct +  { +    int x;     /**< X coord. */ +    int y;     /**< Y coord. */ +    int ipi;   /**< int_num */ +    int event; /**< evt_num */ +  } +  kernel;      /**< pollfd_setup as described by the kernel. */ + +}; + + +/** A structure used to describe previously set up interrupts used with + *  poll().  The format differs significantly for requests from user to + *  kernel, and kernel to hypervisor.  As with the mem_buffer_t, translation + *  between the formats is done at each level. */ +union iorpc_pollfd +{ +  struct +  { +    int fd;    /**< Pollable file descriptor. */ +  } +  user;        /**< pollfd as described by user applications. */ + +  struct +  { +    int cookie; /**< hv cookie returned by the pollfd_setup operation. */ +  } +  kernel;      /**< pollfd as described by the kernel. */ + +}; + + +/** The various iorpc devices use error codes from -1100 to -1299. + * + * This range is distinct from netio (-700 to -799), the hypervisor + * (-800 to -899), tilepci (-900 to -999), ilib (-1000 to -1099), + * gxcr (-1300 to -1399) and gxpci (-1400 to -1499). + */ +enum gxio_err_e { + +  /** Largest iorpc error number. */ +  GXIO_ERR_MAX = -1101, + + +  /********************************************************/ +  /*                   Generic Error Codes                */ +  /********************************************************/ + +  /** Bad RPC opcode - possible version incompatibility. */ +  GXIO_ERR_OPCODE = -1101, + +  /** Invalid parameter. */ +  GXIO_ERR_INVAL = -1102, + +  /** Memory buffer did not meet alignment requirements. */ +  GXIO_ERR_ALIGNMENT = -1103, + +  /** Memory buffers must be coherent and cacheable. */ +  GXIO_ERR_COHERENCE = -1104, + +  /** Resource already initialized. */ +  GXIO_ERR_ALREADY_INIT = -1105, + +  /** No service domains available. */ +  GXIO_ERR_NO_SVC_DOM = -1106, + +  /** Illegal service domain number. */ +  GXIO_ERR_INVAL_SVC_DOM = -1107, + +  /** Illegal MMIO address. */ +  GXIO_ERR_MMIO_ADDRESS = -1108, + +  /** Illegal interrupt binding. */ +  GXIO_ERR_INTERRUPT = -1109, + +  /** Unreasonable client memory. */ +  GXIO_ERR_CLIENT_MEMORY = -1110, + +  /** No more IOTLB entries. */ +  GXIO_ERR_IOTLB_ENTRY = -1111, + +  /** Invalid memory size. */ +  GXIO_ERR_INVAL_MEMORY_SIZE = -1112, + +  /** Unsupported operation. */ +  GXIO_ERR_UNSUPPORTED_OP = -1113, + +  /** Insufficient DMA credits. */ +  GXIO_ERR_DMA_CREDITS = -1114, + +  /** Operation timed out. */ +  GXIO_ERR_TIMEOUT = -1115, + +  /** No such device or object. */ +  GXIO_ERR_NO_DEVICE = -1116, + +  /** Device or resource busy. */ +  GXIO_ERR_BUSY = -1117, + +  /** I/O error. */ +  GXIO_ERR_IO = -1118, + +  /** Permissions error. */ +  GXIO_ERR_PERM = -1119, + + + +  /********************************************************/ +  /*                 Test Device Error Codes              */ +  /********************************************************/ + +  /** Illegal register number. */ +  GXIO_TEST_ERR_REG_NUMBER = -1120, + +  /** Illegal buffer slot. */ +  GXIO_TEST_ERR_BUFFER_SLOT = -1121, + + +  /********************************************************/ +  /*                    MPIPE Error Codes                 */ +  /********************************************************/ + + +  /** Invalid buffer size. */ +  GXIO_MPIPE_ERR_INVAL_BUFFER_SIZE = -1131, + +  /** Cannot allocate buffer stack. */ +  GXIO_MPIPE_ERR_NO_BUFFER_STACK = -1140, + +  /** Invalid buffer stack number. */ +  GXIO_MPIPE_ERR_BAD_BUFFER_STACK = -1141, + +  /** Cannot allocate NotifRing. */ +  GXIO_MPIPE_ERR_NO_NOTIF_RING = -1142, + +  /** Invalid NotifRing number. */ +  GXIO_MPIPE_ERR_BAD_NOTIF_RING = -1143, + +  /** Cannot allocate NotifGroup. */ +  GXIO_MPIPE_ERR_NO_NOTIF_GROUP = -1144, + +  /** Invalid NotifGroup number. */ +  GXIO_MPIPE_ERR_BAD_NOTIF_GROUP = -1145, + +  /** Cannot allocate bucket. */ +  GXIO_MPIPE_ERR_NO_BUCKET = -1146, + +  /** Invalid bucket number. */ +  GXIO_MPIPE_ERR_BAD_BUCKET = -1147, + +  /** Cannot allocate eDMA ring. */ +  GXIO_MPIPE_ERR_NO_EDMA_RING = -1148, + +  /** Invalid eDMA ring number. */ +  GXIO_MPIPE_ERR_BAD_EDMA_RING = -1149, + +  /** Invalid channel number. */ +  GXIO_MPIPE_ERR_BAD_CHANNEL = -1150, + +  /** Bad configuration. */ +  GXIO_MPIPE_ERR_BAD_CONFIG = -1151, + +  /** Empty iqueue. */ +  GXIO_MPIPE_ERR_IQUEUE_EMPTY = -1152, + +  /** Empty rules. */ +  GXIO_MPIPE_ERR_RULES_EMPTY = -1160, + +  /** Full rules. */ +  GXIO_MPIPE_ERR_RULES_FULL = -1161, + +  /** Corrupt rules. */ +  GXIO_MPIPE_ERR_RULES_CORRUPT = -1162, + +  /** Invalid rules. */ +  GXIO_MPIPE_ERR_RULES_INVALID = -1163, + +  /** Classifier is too big. */ +  GXIO_MPIPE_ERR_CLASSIFIER_TOO_BIG = -1170, + +  /** Classifier is too complex. */ +  GXIO_MPIPE_ERR_CLASSIFIER_TOO_COMPLEX = -1171, + +  /** Classifier has bad header. */ +  GXIO_MPIPE_ERR_CLASSIFIER_BAD_HEADER = -1172, + +  /** Classifier has bad contents. */ +  GXIO_MPIPE_ERR_CLASSIFIER_BAD_CONTENTS = -1173, + +  /** Classifier encountered invalid symbol. */ +  GXIO_MPIPE_ERR_CLASSIFIER_INVAL_SYMBOL = -1174, + +  /** Classifier encountered invalid bounds. */ +  GXIO_MPIPE_ERR_CLASSIFIER_INVAL_BOUNDS = -1175, + +  /** Classifier encountered invalid relocation. */ +  GXIO_MPIPE_ERR_CLASSIFIER_INVAL_RELOCATION = -1176, + +  /** Classifier encountered undefined symbol. */ +  GXIO_MPIPE_ERR_CLASSIFIER_UNDEF_SYMBOL = -1177, + + +  /********************************************************/ +  /*                    TRIO  Error Codes                 */ +  /********************************************************/ + +  /** Cannot allocate memory map region. */ +  GXIO_TRIO_ERR_NO_MEMORY_MAP = -1180, + +  /** Invalid memory map region number. */ +  GXIO_TRIO_ERR_BAD_MEMORY_MAP = -1181, + +  /** Cannot allocate scatter queue. */ +  GXIO_TRIO_ERR_NO_SCATTER_QUEUE = -1182, + +  /** Invalid scatter queue number. */ +  GXIO_TRIO_ERR_BAD_SCATTER_QUEUE = -1183, + +  /** Cannot allocate push DMA ring. */ +  GXIO_TRIO_ERR_NO_PUSH_DMA_RING = -1184, + +  /** Invalid push DMA ring index. */ +  GXIO_TRIO_ERR_BAD_PUSH_DMA_RING = -1185, + +  /** Cannot allocate pull DMA ring. */ +  GXIO_TRIO_ERR_NO_PULL_DMA_RING = -1186, + +  /** Invalid pull DMA ring index. */ +  GXIO_TRIO_ERR_BAD_PULL_DMA_RING = -1187, + +  /** Cannot allocate PIO region. */ +  GXIO_TRIO_ERR_NO_PIO = -1188, + +  /** Invalid PIO region index. */ +  GXIO_TRIO_ERR_BAD_PIO = -1189, + +  /** Cannot allocate ASID. */ +  GXIO_TRIO_ERR_NO_ASID = -1190, + +  /** Invalid ASID. */ +  GXIO_TRIO_ERR_BAD_ASID = -1191, + + +  /********************************************************/ +  /*                    MICA Error Codes                  */ +  /********************************************************/ + +  /** No such accelerator type. */ +  GXIO_MICA_ERR_BAD_ACCEL_TYPE = -1220, + +  /** Cannot allocate context. */ +  GXIO_MICA_ERR_NO_CONTEXT = -1221, + +  /** PKA command queue is full, can't add another command. */ +  GXIO_MICA_ERR_PKA_CMD_QUEUE_FULL = -1222, + +  /** PKA result queue is empty, can't get a result from the queue. */ +  GXIO_MICA_ERR_PKA_RESULT_QUEUE_EMPTY = -1223, + +  /********************************************************/ +  /*                    GPIO Error Codes                  */ +  /********************************************************/ + +  /** Pin not available.  Either the physical pin does not exist, or +   *  it is reserved by the hypervisor for system usage. */ +  GXIO_GPIO_ERR_PIN_UNAVAILABLE = -1240, + +  /** Pin busy.  The pin exists, and is available for use via GXIO, but +   *  it has been attached by some other process or driver. */ +  GXIO_GPIO_ERR_PIN_BUSY = -1241, + +  /** Cannot access unattached pin.  One or more of the pins being +   *  manipulated by this call are not attached to the requesting +   *  context. */ +  GXIO_GPIO_ERR_PIN_UNATTACHED = -1242, + +  /** Invalid I/O mode for pin.  The wiring of the pin in the system +   *  is such that the I/O mode or electrical control parameters +   *  requested could cause damage. */ +  GXIO_GPIO_ERR_PIN_INVALID_MODE = -1243, + +  /** Smallest iorpc error number. */ +  GXIO_ERR_MIN = -1299 +}; + + +#endif /* !_HV_IORPC_H_ */ diff --git a/arch/tile/kernel/Makefile b/arch/tile/kernel/Makefile index 5de99248d8d..5334be8e253 100644 --- a/arch/tile/kernel/Makefile +++ b/arch/tile/kernel/Makefile @@ -14,4 +14,9 @@ obj-$(CONFIG_SMP)		+= smpboot.o smp.o tlb.o  obj-$(CONFIG_MODULES)		+= module.o  obj-$(CONFIG_EARLY_PRINTK)	+= early_printk.o  obj-$(CONFIG_KEXEC)		+= machine_kexec.o relocate_kernel_$(BITS).o +ifdef CONFIG_TILEGX +obj-$(CONFIG_PCI)		+= pci_gx.o +else  obj-$(CONFIG_PCI)		+= pci.o +endif +obj-$(CONFIG_TILE_USB)		+= usb.o diff --git a/arch/tile/kernel/pci-dma.c b/arch/tile/kernel/pci-dma.c index b3ed19f8779..b9fe80ec108 100644 --- a/arch/tile/kernel/pci-dma.c +++ b/arch/tile/kernel/pci-dma.c @@ -14,6 +14,7 @@  #include <linux/mm.h>  #include <linux/dma-mapping.h> +#include <linux/swiotlb.h>  #include <linux/vmalloc.h>  #include <linux/export.h>  #include <asm/tlbflush.h> @@ -22,13 +23,18 @@  /* Generic DMA mapping functions: */  /* - * Allocate what Linux calls "coherent" memory, which for us just - * means uncached. + * Allocate what Linux calls "coherent" memory.  On TILEPro this is + * uncached memory; on TILE-Gx it is hash-for-home memory.   */ -void *dma_alloc_coherent(struct device *dev, -			 size_t size, -			 dma_addr_t *dma_handle, -			 gfp_t gfp) +#ifdef __tilepro__ +#define PAGE_HOME_DMA PAGE_HOME_UNCACHED +#else +#define PAGE_HOME_DMA PAGE_HOME_HASH +#endif + +static void *tile_dma_alloc_coherent(struct device *dev, size_t size, +				     dma_addr_t *dma_handle, gfp_t gfp, +				     struct dma_attrs *attrs)  {  	u64 dma_mask = dev->coherent_dma_mask ?: DMA_BIT_MASK(32);  	int node = dev_to_node(dev); @@ -39,39 +45,42 @@ void *dma_alloc_coherent(struct device *dev,  	gfp |= __GFP_ZERO;  	/* -	 * By forcing NUMA node 0 for 32-bit masks we ensure that the -	 * high 32 bits of the resulting PA will be zero.  If the mask -	 * size is, e.g., 24, we may still not be able to guarantee a -	 * suitable memory address, in which case we will return NULL. -	 * But such devices are uncommon. +	 * If the mask specifies that the memory be in the first 4 GB, then +	 * we force the allocation to come from the DMA zone.  We also +	 * force the node to 0 since that's the only node where the DMA +	 * zone isn't empty.  If the mask size is smaller than 32 bits, we +	 * may still not be able to guarantee a suitable memory address, in +	 * which case we will return NULL.  But such devices are uncommon.  	 */ -	if (dma_mask <= DMA_BIT_MASK(32)) +	if (dma_mask <= DMA_BIT_MASK(32)) { +		gfp |= GFP_DMA;  		node = 0; +	} -	pg = homecache_alloc_pages_node(node, gfp, order, PAGE_HOME_UNCACHED); +	pg = homecache_alloc_pages_node(node, gfp, order, PAGE_HOME_DMA);  	if (pg == NULL)  		return NULL;  	addr = page_to_phys(pg);  	if (addr + size > dma_mask) { -		homecache_free_pages(addr, order); +		__homecache_free_pages(pg, order);  		return NULL;  	}  	*dma_handle = addr; +  	return page_address(pg);  } -EXPORT_SYMBOL(dma_alloc_coherent);  /* - * Free memory that was allocated with dma_alloc_coherent. + * Free memory that was allocated with tile_dma_alloc_coherent.   */ -void dma_free_coherent(struct device *dev, size_t size, -		  void *vaddr, dma_addr_t dma_handle) +static void tile_dma_free_coherent(struct device *dev, size_t size, +				   void *vaddr, dma_addr_t dma_handle, +				   struct dma_attrs *attrs)  {  	homecache_free_pages((unsigned long)vaddr, get_order(size));  } -EXPORT_SYMBOL(dma_free_coherent);  /*   * The map routines "map" the specified address range for DMA @@ -87,52 +96,285 @@ EXPORT_SYMBOL(dma_free_coherent);   * can count on nothing having been touched.   */ -/* Flush a PA range from cache page by page. */ -static void __dma_map_pa_range(dma_addr_t dma_addr, size_t size) +/* Set up a single page for DMA access. */ +static void __dma_prep_page(struct page *page, unsigned long offset, +			    size_t size, enum dma_data_direction direction) +{ +	/* +	 * Flush the page from cache if necessary. +	 * On tilegx, data is delivered to hash-for-home L3; on tilepro, +	 * data is delivered direct to memory. +	 * +	 * NOTE: If we were just doing DMA_TO_DEVICE we could optimize +	 * this to be a "flush" not a "finv" and keep some of the +	 * state in cache across the DMA operation, but it doesn't seem +	 * worth creating the necessary flush_buffer_xxx() infrastructure. +	 */ +	int home = page_home(page); +	switch (home) { +	case PAGE_HOME_HASH: +#ifdef __tilegx__ +		return; +#endif +		break; +	case PAGE_HOME_UNCACHED: +#ifdef __tilepro__ +		return; +#endif +		break; +	case PAGE_HOME_IMMUTABLE: +		/* Should be going to the device only. */ +		BUG_ON(direction == DMA_FROM_DEVICE || +		       direction == DMA_BIDIRECTIONAL); +		return; +	case PAGE_HOME_INCOHERENT: +		/* Incoherent anyway, so no need to work hard here. */ +		return; +	default: +		BUG_ON(home < 0 || home >= NR_CPUS); +		break; +	} +	homecache_finv_page(page); + +#ifdef DEBUG_ALIGNMENT +	/* Warn if the region isn't cacheline aligned. */ +	if (offset & (L2_CACHE_BYTES - 1) || (size & (L2_CACHE_BYTES - 1))) +		pr_warn("Unaligned DMA to non-hfh memory: PA %#llx/%#lx\n", +			PFN_PHYS(page_to_pfn(page)) + offset, size); +#endif +} + +/* Make the page ready to be read by the core. */ +static void __dma_complete_page(struct page *page, unsigned long offset, +				size_t size, enum dma_data_direction direction) +{ +#ifdef __tilegx__ +	switch (page_home(page)) { +	case PAGE_HOME_HASH: +		/* I/O device delivered data the way the cpu wanted it. */ +		break; +	case PAGE_HOME_INCOHERENT: +		/* Incoherent anyway, so no need to work hard here. */ +		break; +	case PAGE_HOME_IMMUTABLE: +		/* Extra read-only copies are not a problem. */ +		break; +	default: +		/* Flush the bogus hash-for-home I/O entries to memory. */ +		homecache_finv_map_page(page, PAGE_HOME_HASH); +		break; +	} +#endif +} + +static void __dma_prep_pa_range(dma_addr_t dma_addr, size_t size, +				enum dma_data_direction direction)  {  	struct page *page = pfn_to_page(PFN_DOWN(dma_addr)); -	size_t bytesleft = PAGE_SIZE - (dma_addr & (PAGE_SIZE - 1)); +	unsigned long offset = dma_addr & (PAGE_SIZE - 1); +	size_t bytes = min(size, (size_t)(PAGE_SIZE - offset)); -	while ((ssize_t)size > 0) { -		/* Flush the page. */ -		homecache_flush_cache(page++, 0); +	while (size != 0) { +		__dma_prep_page(page, offset, bytes, direction); +		size -= bytes; +		++page; +		offset = 0; +		bytes = min((size_t)PAGE_SIZE, size); +	} +} + +static void __dma_complete_pa_range(dma_addr_t dma_addr, size_t size, +				    enum dma_data_direction direction) +{ +	struct page *page = pfn_to_page(PFN_DOWN(dma_addr)); +	unsigned long offset = dma_addr & (PAGE_SIZE - 1); +	size_t bytes = min(size, (size_t)(PAGE_SIZE - offset)); -		/* Figure out if we need to continue on the next page. */ -		size -= bytesleft; -		bytesleft = PAGE_SIZE; +	while (size != 0) { +		__dma_complete_page(page, offset, bytes, direction); +		size -= bytes; +		++page; +		offset = 0; +		bytes = min((size_t)PAGE_SIZE, size);  	}  } -/* - * dma_map_single can be passed any memory address, and there appear - * to be no alignment constraints. - * - * There is a chance that the start of the buffer will share a cache - * line with some other data that has been touched in the meantime. - */ -dma_addr_t dma_map_single(struct device *dev, void *ptr, size_t size, -	       enum dma_data_direction direction) +static int tile_dma_map_sg(struct device *dev, struct scatterlist *sglist, +			   int nents, enum dma_data_direction direction, +			   struct dma_attrs *attrs)  { -	dma_addr_t dma_addr = __pa(ptr); +	struct scatterlist *sg; +	int i;  	BUG_ON(!valid_dma_direction(direction)); -	WARN_ON(size == 0); -	__dma_map_pa_range(dma_addr, size); +	WARN_ON(nents == 0 || sglist->length == 0); + +	for_each_sg(sglist, sg, nents, i) { +		sg->dma_address = sg_phys(sg); +		__dma_prep_pa_range(sg->dma_address, sg->length, direction); +#ifdef CONFIG_NEED_SG_DMA_LENGTH +		sg->dma_length = sg->length; +#endif +	} -	return dma_addr; +	return nents;  } -EXPORT_SYMBOL(dma_map_single); -void dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size, -		 enum dma_data_direction direction) +static void tile_dma_unmap_sg(struct device *dev, struct scatterlist *sglist, +			      int nents, enum dma_data_direction direction, +			      struct dma_attrs *attrs)  { +	struct scatterlist *sg; +	int i; +  	BUG_ON(!valid_dma_direction(direction)); +	for_each_sg(sglist, sg, nents, i) { +		sg->dma_address = sg_phys(sg); +		__dma_complete_pa_range(sg->dma_address, sg->length, +					direction); +	}  } -EXPORT_SYMBOL(dma_unmap_single); -int dma_map_sg(struct device *dev, struct scatterlist *sglist, int nents, -	   enum dma_data_direction direction) +static dma_addr_t tile_dma_map_page(struct device *dev, struct page *page, +				    unsigned long offset, size_t size, +				    enum dma_data_direction direction, +				    struct dma_attrs *attrs) +{ +	BUG_ON(!valid_dma_direction(direction)); + +	BUG_ON(offset + size > PAGE_SIZE); +	__dma_prep_page(page, offset, size, direction); + +	return page_to_pa(page) + offset; +} + +static void tile_dma_unmap_page(struct device *dev, dma_addr_t dma_address, +				size_t size, enum dma_data_direction direction, +				struct dma_attrs *attrs) +{ +	BUG_ON(!valid_dma_direction(direction)); + +	__dma_complete_page(pfn_to_page(PFN_DOWN(dma_address)), +			    dma_address & PAGE_OFFSET, size, direction); +} + +static void tile_dma_sync_single_for_cpu(struct device *dev, +					 dma_addr_t dma_handle, +					 size_t size, +					 enum dma_data_direction direction) +{ +	BUG_ON(!valid_dma_direction(direction)); + +	__dma_complete_pa_range(dma_handle, size, direction); +} + +static void tile_dma_sync_single_for_device(struct device *dev, +					    dma_addr_t dma_handle, size_t size, +					    enum dma_data_direction direction) +{ +	__dma_prep_pa_range(dma_handle, size, direction); +} + +static void tile_dma_sync_sg_for_cpu(struct device *dev, +				     struct scatterlist *sglist, int nelems, +				     enum dma_data_direction direction) +{ +	struct scatterlist *sg; +	int i; + +	BUG_ON(!valid_dma_direction(direction)); +	WARN_ON(nelems == 0 || sglist->length == 0); + +	for_each_sg(sglist, sg, nelems, i) { +		dma_sync_single_for_cpu(dev, sg->dma_address, +					sg_dma_len(sg), direction); +	} +} + +static void tile_dma_sync_sg_for_device(struct device *dev, +					struct scatterlist *sglist, int nelems, +					enum dma_data_direction direction) +{ +	struct scatterlist *sg; +	int i; + +	BUG_ON(!valid_dma_direction(direction)); +	WARN_ON(nelems == 0 || sglist->length == 0); + +	for_each_sg(sglist, sg, nelems, i) { +		dma_sync_single_for_device(dev, sg->dma_address, +					   sg_dma_len(sg), direction); +	} +} + +static inline int +tile_dma_mapping_error(struct device *dev, dma_addr_t dma_addr) +{ +	return 0; +} + +static inline int +tile_dma_supported(struct device *dev, u64 mask) +{ +	return 1; +} + +static struct dma_map_ops tile_default_dma_map_ops = { +	.alloc = tile_dma_alloc_coherent, +	.free = tile_dma_free_coherent, +	.map_page = tile_dma_map_page, +	.unmap_page = tile_dma_unmap_page, +	.map_sg = tile_dma_map_sg, +	.unmap_sg = tile_dma_unmap_sg, +	.sync_single_for_cpu = tile_dma_sync_single_for_cpu, +	.sync_single_for_device = tile_dma_sync_single_for_device, +	.sync_sg_for_cpu = tile_dma_sync_sg_for_cpu, +	.sync_sg_for_device = tile_dma_sync_sg_for_device, +	.mapping_error = tile_dma_mapping_error, +	.dma_supported = tile_dma_supported +}; + +struct dma_map_ops *tile_dma_map_ops = &tile_default_dma_map_ops; +EXPORT_SYMBOL(tile_dma_map_ops); + +/* Generic PCI DMA mapping functions */ + +static void *tile_pci_dma_alloc_coherent(struct device *dev, size_t size, +					 dma_addr_t *dma_handle, gfp_t gfp, +					 struct dma_attrs *attrs) +{ +	int node = dev_to_node(dev); +	int order = get_order(size); +	struct page *pg; +	dma_addr_t addr; + +	gfp |= __GFP_ZERO; + +	pg = homecache_alloc_pages_node(node, gfp, order, PAGE_HOME_DMA); +	if (pg == NULL) +		return NULL; + +	addr = page_to_phys(pg); + +	*dma_handle = phys_to_dma(dev, addr); + +	return page_address(pg); +} + +/* + * Free memory that was allocated with tile_pci_dma_alloc_coherent. + */ +static void tile_pci_dma_free_coherent(struct device *dev, size_t size, +				       void *vaddr, dma_addr_t dma_handle, +				       struct dma_attrs *attrs) +{ +	homecache_free_pages((unsigned long)vaddr, get_order(size)); +} + +static int tile_pci_dma_map_sg(struct device *dev, struct scatterlist *sglist, +			       int nents, enum dma_data_direction direction, +			       struct dma_attrs *attrs)  {  	struct scatterlist *sg;  	int i; @@ -143,73 +385,103 @@ int dma_map_sg(struct device *dev, struct scatterlist *sglist, int nents,  	for_each_sg(sglist, sg, nents, i) {  		sg->dma_address = sg_phys(sg); -		__dma_map_pa_range(sg->dma_address, sg->length); +		__dma_prep_pa_range(sg->dma_address, sg->length, direction); + +		sg->dma_address = phys_to_dma(dev, sg->dma_address); +#ifdef CONFIG_NEED_SG_DMA_LENGTH +		sg->dma_length = sg->length; +#endif  	}  	return nents;  } -EXPORT_SYMBOL(dma_map_sg); -void dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nhwentries, -	     enum dma_data_direction direction) +static void tile_pci_dma_unmap_sg(struct device *dev, +				  struct scatterlist *sglist, int nents, +				  enum dma_data_direction direction, +				  struct dma_attrs *attrs)  { +	struct scatterlist *sg; +	int i; +  	BUG_ON(!valid_dma_direction(direction)); +	for_each_sg(sglist, sg, nents, i) { +		sg->dma_address = sg_phys(sg); +		__dma_complete_pa_range(sg->dma_address, sg->length, +					direction); +	}  } -EXPORT_SYMBOL(dma_unmap_sg); -dma_addr_t dma_map_page(struct device *dev, struct page *page, -			unsigned long offset, size_t size, -			enum dma_data_direction direction) +static dma_addr_t tile_pci_dma_map_page(struct device *dev, struct page *page, +					unsigned long offset, size_t size, +					enum dma_data_direction direction, +					struct dma_attrs *attrs)  {  	BUG_ON(!valid_dma_direction(direction));  	BUG_ON(offset + size > PAGE_SIZE); -	homecache_flush_cache(page, 0); +	__dma_prep_page(page, offset, size, direction); -	return page_to_pa(page) + offset; +	return phys_to_dma(dev, page_to_pa(page) + offset);  } -EXPORT_SYMBOL(dma_map_page); -void dma_unmap_page(struct device *dev, dma_addr_t dma_address, size_t size, -	       enum dma_data_direction direction) +static void tile_pci_dma_unmap_page(struct device *dev, dma_addr_t dma_address, +				    size_t size, +				    enum dma_data_direction direction, +				    struct dma_attrs *attrs)  {  	BUG_ON(!valid_dma_direction(direction)); + +	dma_address = dma_to_phys(dev, dma_address); + +	__dma_complete_page(pfn_to_page(PFN_DOWN(dma_address)), +			    dma_address & PAGE_OFFSET, size, direction);  } -EXPORT_SYMBOL(dma_unmap_page); -void dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, -			     size_t size, enum dma_data_direction direction) +static void tile_pci_dma_sync_single_for_cpu(struct device *dev, +					     dma_addr_t dma_handle, +					     size_t size, +					     enum dma_data_direction direction)  {  	BUG_ON(!valid_dma_direction(direction)); + +	dma_handle = dma_to_phys(dev, dma_handle); + +	__dma_complete_pa_range(dma_handle, size, direction);  } -EXPORT_SYMBOL(dma_sync_single_for_cpu); -void dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, -				size_t size, enum dma_data_direction direction) +static void tile_pci_dma_sync_single_for_device(struct device *dev, +						dma_addr_t dma_handle, +						size_t size, +						enum dma_data_direction +						direction)  { -	unsigned long start = PFN_DOWN(dma_handle); -	unsigned long end = PFN_DOWN(dma_handle + size - 1); -	unsigned long i; +	dma_handle = dma_to_phys(dev, dma_handle); -	BUG_ON(!valid_dma_direction(direction)); -	for (i = start; i <= end; ++i) -		homecache_flush_cache(pfn_to_page(i), 0); +	__dma_prep_pa_range(dma_handle, size, direction);  } -EXPORT_SYMBOL(dma_sync_single_for_device); -void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nelems, -		    enum dma_data_direction direction) +static void tile_pci_dma_sync_sg_for_cpu(struct device *dev, +					 struct scatterlist *sglist, +					 int nelems, +					 enum dma_data_direction direction)  { +	struct scatterlist *sg; +	int i; +  	BUG_ON(!valid_dma_direction(direction)); -	WARN_ON(nelems == 0 || sg[0].length == 0); +	WARN_ON(nelems == 0 || sglist->length == 0); + +	for_each_sg(sglist, sg, nelems, i) { +		dma_sync_single_for_cpu(dev, sg->dma_address, +					sg_dma_len(sg), direction); +	}  } -EXPORT_SYMBOL(dma_sync_sg_for_cpu); -/* - * Flush and invalidate cache for scatterlist. - */ -void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sglist, -			    int nelems, enum dma_data_direction direction) +static void tile_pci_dma_sync_sg_for_device(struct device *dev, +					    struct scatterlist *sglist, +					    int nelems, +					    enum dma_data_direction direction)  {  	struct scatterlist *sg;  	int i; @@ -222,31 +494,93 @@ void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sglist,  					   sg_dma_len(sg), direction);  	}  } -EXPORT_SYMBOL(dma_sync_sg_for_device); -void dma_sync_single_range_for_cpu(struct device *dev, dma_addr_t dma_handle, -				   unsigned long offset, size_t size, -				   enum dma_data_direction direction) +static inline int +tile_pci_dma_mapping_error(struct device *dev, dma_addr_t dma_addr)  { -	dma_sync_single_for_cpu(dev, dma_handle + offset, size, direction); +	return 0;  } -EXPORT_SYMBOL(dma_sync_single_range_for_cpu); -void dma_sync_single_range_for_device(struct device *dev, -				      dma_addr_t dma_handle, -				      unsigned long offset, size_t size, -				      enum dma_data_direction direction) +static inline int +tile_pci_dma_supported(struct device *dev, u64 mask)  { -	dma_sync_single_for_device(dev, dma_handle + offset, size, direction); +	return 1;  } -EXPORT_SYMBOL(dma_sync_single_range_for_device); -/* - * dma_alloc_noncoherent() returns non-cacheable memory, so there's no - * need to do any flushing here. - */ -void dma_cache_sync(struct device *dev, void *vaddr, size_t size, -		    enum dma_data_direction direction) +static struct dma_map_ops tile_pci_default_dma_map_ops = { +	.alloc = tile_pci_dma_alloc_coherent, +	.free = tile_pci_dma_free_coherent, +	.map_page = tile_pci_dma_map_page, +	.unmap_page = tile_pci_dma_unmap_page, +	.map_sg = tile_pci_dma_map_sg, +	.unmap_sg = tile_pci_dma_unmap_sg, +	.sync_single_for_cpu = tile_pci_dma_sync_single_for_cpu, +	.sync_single_for_device = tile_pci_dma_sync_single_for_device, +	.sync_sg_for_cpu = tile_pci_dma_sync_sg_for_cpu, +	.sync_sg_for_device = tile_pci_dma_sync_sg_for_device, +	.mapping_error = tile_pci_dma_mapping_error, +	.dma_supported = tile_pci_dma_supported +}; + +struct dma_map_ops *gx_pci_dma_map_ops = &tile_pci_default_dma_map_ops; +EXPORT_SYMBOL(gx_pci_dma_map_ops); + +/* PCI DMA mapping functions for legacy PCI devices */ + +#ifdef CONFIG_SWIOTLB +static void *tile_swiotlb_alloc_coherent(struct device *dev, size_t size, +					 dma_addr_t *dma_handle, gfp_t gfp, +					 struct dma_attrs *attrs)  { +	gfp |= GFP_DMA; +	return swiotlb_alloc_coherent(dev, size, dma_handle, gfp); +} + +static void tile_swiotlb_free_coherent(struct device *dev, size_t size, +				       void *vaddr, dma_addr_t dma_addr, +				       struct dma_attrs *attrs) +{ +	swiotlb_free_coherent(dev, size, vaddr, dma_addr); +} + +static struct dma_map_ops pci_swiotlb_dma_ops = { +	.alloc = tile_swiotlb_alloc_coherent, +	.free = tile_swiotlb_free_coherent, +	.map_page = swiotlb_map_page, +	.unmap_page = swiotlb_unmap_page, +	.map_sg = swiotlb_map_sg_attrs, +	.unmap_sg = swiotlb_unmap_sg_attrs, +	.sync_single_for_cpu = swiotlb_sync_single_for_cpu, +	.sync_single_for_device = swiotlb_sync_single_for_device, +	.sync_sg_for_cpu = swiotlb_sync_sg_for_cpu, +	.sync_sg_for_device = swiotlb_sync_sg_for_device, +	.dma_supported = swiotlb_dma_supported, +	.mapping_error = swiotlb_dma_mapping_error, +}; + +struct dma_map_ops *gx_legacy_pci_dma_map_ops = &pci_swiotlb_dma_ops; +#else +struct dma_map_ops *gx_legacy_pci_dma_map_ops; +#endif +EXPORT_SYMBOL(gx_legacy_pci_dma_map_ops); + +#ifdef CONFIG_ARCH_HAS_DMA_SET_COHERENT_MASK +int dma_set_coherent_mask(struct device *dev, u64 mask) +{ +	struct dma_map_ops *dma_ops = get_dma_ops(dev); + +	/* Handle legacy PCI devices with limited memory addressability. */ +	if (((dma_ops == gx_pci_dma_map_ops) || +	    (dma_ops == gx_legacy_pci_dma_map_ops)) && +	    (mask <= DMA_BIT_MASK(32))) { +		if (mask > dev->archdata.max_direct_dma_addr) +			mask = dev->archdata.max_direct_dma_addr; +	} + +	if (!dma_supported(dev, mask)) +		return -EIO; +	dev->coherent_dma_mask = mask; +	return 0;  } -EXPORT_SYMBOL(dma_cache_sync); +EXPORT_SYMBOL(dma_set_coherent_mask); +#endif diff --git a/arch/tile/kernel/pci_gx.c b/arch/tile/kernel/pci_gx.c new file mode 100644 index 00000000000..fa75264a82a --- /dev/null +++ b/arch/tile/kernel/pci_gx.c @@ -0,0 +1,1543 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ + +#include <linux/kernel.h> +#include <linux/mmzone.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/irq.h> +#include <linux/msi.h> +#include <linux/io.h> +#include <linux/uaccess.h> +#include <linux/ctype.h> + +#include <asm/processor.h> +#include <asm/sections.h> +#include <asm/byteorder.h> + +#include <gxio/iorpc_globals.h> +#include <gxio/kiorpc.h> +#include <gxio/trio.h> +#include <gxio/iorpc_trio.h> +#include <hv/drv_trio_intf.h> + +#include <arch/sim.h> + +/* + * This file containes the routines to search for PCI buses, + * enumerate the buses, and configure any attached devices. + */ + +#define DEBUG_PCI_CFG	0 + +#if DEBUG_PCI_CFG +#define TRACE_CFG_WR(size, val, bus, dev, func, offset) \ +	pr_info("CFG WR %d-byte VAL %#x to bus %d dev %d func %d addr %u\n", \ +		size, val, bus, dev, func, offset & 0xFFF); +#define TRACE_CFG_RD(size, val, bus, dev, func, offset) \ +	pr_info("CFG RD %d-byte VAL %#x from bus %d dev %d func %d addr %u\n", \ +		size, val, bus, dev, func, offset & 0xFFF); +#else +#define TRACE_CFG_WR(...) +#define TRACE_CFG_RD(...) +#endif + +static int __devinitdata pci_probe = 1; + +/* Information on the PCIe RC ports configuration. */ +static int __devinitdata pcie_rc[TILEGX_NUM_TRIO][TILEGX_TRIO_PCIES]; + +/* + * On some platforms with one or more Gx endpoint ports, we need to + * delay the PCIe RC port probe for a few seconds to work around + * a HW PCIe link-training bug. The exact delay is specified with + * a kernel boot argument in the form of "pcie_rc_delay=T,P,S", + * where T is the TRIO instance number, P is the port number and S is + * the delay in seconds. If the delay is not provided, the value + * will be DEFAULT_RC_DELAY. + */ +static int __devinitdata rc_delay[TILEGX_NUM_TRIO][TILEGX_TRIO_PCIES]; + +/* Default number of seconds that the PCIe RC port probe can be delayed. */ +#define DEFAULT_RC_DELAY	10 + +/* Max number of seconds that the PCIe RC port probe can be delayed. */ +#define MAX_RC_DELAY		20 + +/* Array of the PCIe ports configuration info obtained from the BIB. */ +struct pcie_port_property pcie_ports[TILEGX_NUM_TRIO][TILEGX_TRIO_PCIES]; + +/* All drivers share the TRIO contexts defined here. */ +gxio_trio_context_t trio_contexts[TILEGX_NUM_TRIO]; + +/* Pointer to an array of PCIe RC controllers. */ +struct pci_controller pci_controllers[TILEGX_NUM_TRIO * TILEGX_TRIO_PCIES]; +int num_rc_controllers; +static int num_ep_controllers; + +static struct pci_ops tile_cfg_ops; + +/* Mask of CPUs that should receive PCIe interrupts. */ +static struct cpumask intr_cpus_map; + +/* + * We don't need to worry about the alignment of resources. + */ +resource_size_t pcibios_align_resource(void *data, const struct resource *res, +				resource_size_t size, resource_size_t align) +{ +	return res->start; +} +EXPORT_SYMBOL(pcibios_align_resource); + + +/* + * Pick a CPU to receive and handle the PCIe interrupts, based on the IRQ #. + * For now, we simply send interrupts to non-dataplane CPUs. + * We may implement methods to allow user to specify the target CPUs, + * e.g. via boot arguments. + */ +static int tile_irq_cpu(int irq) +{ +	unsigned int count; +	int i = 0; +	int cpu; + +	count = cpumask_weight(&intr_cpus_map); +	if (unlikely(count == 0)) { +		pr_warning("intr_cpus_map empty, interrupts will be" +			   " delievered to dataplane tiles\n"); +		return irq % (smp_height * smp_width); +	} + +	count = irq % count; +	for_each_cpu(cpu, &intr_cpus_map) { +		if (i++ == count) +			break; +	} +	return cpu; +} + +/* + * Open a file descriptor to the TRIO shim. + */ +static int __devinit tile_pcie_open(int trio_index) +{ +	gxio_trio_context_t *context = &trio_contexts[trio_index]; +	int ret; + +	/* +	 * This opens a file descriptor to the TRIO shim. +	 */ +	ret = gxio_trio_init(context, trio_index); +	if (ret < 0) +		return ret; + +	/* +	 * Allocate an ASID for the kernel. +	 */ +	ret = gxio_trio_alloc_asids(context, 1, 0, 0); +	if (ret < 0) { +		pr_err("PCI: ASID alloc failure on TRIO %d, give up\n", +			trio_index); +		goto asid_alloc_failure; +	} + +	context->asid = ret; + +#ifdef USE_SHARED_PCIE_CONFIG_REGION +	/* +	 * Alloc a PIO region for config access, shared by all MACs per TRIO. +	 * This shouldn't fail since the kernel is supposed to the first +	 * client of the TRIO's PIO regions. +	 */ +	ret = gxio_trio_alloc_pio_regions(context, 1, 0, 0); +	if (ret < 0) { +		pr_err("PCI: CFG PIO alloc failure on TRIO %d, give up\n", +			trio_index); +		goto pio_alloc_failure; +	} + +	context->pio_cfg_index = ret; + +	/* +	 * For PIO CFG, the bus_address_hi parameter is 0. The mac parameter +	 * is also 0 because it is specified in PIO_REGION_SETUP_CFG_ADDR. +	 */ +	ret = gxio_trio_init_pio_region_aux(context, context->pio_cfg_index, +		0, 0, HV_TRIO_PIO_FLAG_CONFIG_SPACE); +	if (ret < 0) { +		pr_err("PCI: CFG PIO init failure on TRIO %d, give up\n", +			trio_index); +		goto pio_alloc_failure; +	} +#endif + +	return ret; + +asid_alloc_failure: +#ifdef USE_SHARED_PCIE_CONFIG_REGION +pio_alloc_failure: +#endif +	hv_dev_close(context->fd); + +	return ret; +} + +static void +tilegx_legacy_irq_ack(struct irq_data *d) +{ +	__insn_mtspr(SPR_IPI_EVENT_RESET_K, 1UL << d->irq); +} + +static void +tilegx_legacy_irq_mask(struct irq_data *d) +{ +	__insn_mtspr(SPR_IPI_MASK_SET_K, 1UL << d->irq); +} + +static void +tilegx_legacy_irq_unmask(struct irq_data *d) +{ +	__insn_mtspr(SPR_IPI_MASK_RESET_K, 1UL << d->irq); +} + +static struct irq_chip tilegx_legacy_irq_chip = { +	.name			= "tilegx_legacy_irq", +	.irq_ack		= tilegx_legacy_irq_ack, +	.irq_mask		= tilegx_legacy_irq_mask, +	.irq_unmask		= tilegx_legacy_irq_unmask, + +	/* TBD: support set_affinity. */ +}; + +/* + * This is a wrapper function of the kernel level-trigger interrupt + * handler handle_level_irq() for PCI legacy interrupts. The TRIO + * is configured such that only INTx Assert interrupts are proxied + * to Linux which just calls handle_level_irq() after clearing the + * MAC INTx Assert status bit associated with this interrupt. + */ +static void +trio_handle_level_irq(unsigned int irq, struct irq_desc *desc) +{ +	struct pci_controller *controller = irq_desc_get_handler_data(desc); +	gxio_trio_context_t *trio_context = controller->trio; +	uint64_t intx = (uint64_t)irq_desc_get_chip_data(desc); +	int mac = controller->mac; +	unsigned int reg_offset; +	uint64_t level_mask; + +	handle_level_irq(irq, desc); + +	/* +	 * Clear the INTx Level status, otherwise future interrupts are +	 * not sent. +	 */ +	reg_offset = (TRIO_PCIE_INTFC_MAC_INT_STS << +		TRIO_CFG_REGION_ADDR__REG_SHIFT) | +		(TRIO_CFG_REGION_ADDR__INTFC_VAL_MAC_INTERFACE << +		TRIO_CFG_REGION_ADDR__INTFC_SHIFT ) | +		(mac << TRIO_CFG_REGION_ADDR__MAC_SEL_SHIFT); + +	level_mask = TRIO_PCIE_INTFC_MAC_INT_STS__INT_LEVEL_MASK << intx; + +	__gxio_mmio_write(trio_context->mmio_base_mac + reg_offset, level_mask); +} + +/* + * Create kernel irqs and set up the handlers for the legacy interrupts. + * Also some minimum initialization for the MSI support. + */ +static int __devinit tile_init_irqs(struct pci_controller *controller) +{ +	int i; +	int j; +	int irq; +	int result; + +	cpumask_copy(&intr_cpus_map, cpu_online_mask); + + +	for (i = 0; i < 4; i++) { +		gxio_trio_context_t *context = controller->trio; +		int cpu; + +		/* Ask the kernel to allocate an IRQ. */ +		irq = create_irq(); +		if (irq < 0) { +			pr_err("PCI: no free irq vectors, failed for %d\n", i); + +			goto free_irqs; +		} +		controller->irq_intx_table[i] = irq; + +		/* Distribute the 4 IRQs to different tiles. */ +		cpu = tile_irq_cpu(irq); + +		/* Configure the TRIO intr binding for this IRQ. */ +		result = gxio_trio_config_legacy_intr(context, cpu_x(cpu), +						      cpu_y(cpu), KERNEL_PL, +						      irq, controller->mac, i); +		if (result < 0) { +			pr_err("PCI: MAC intx config failed for %d\n", i); + +			goto free_irqs; +		} + +		/* +		 * Register the IRQ handler with the kernel. +		 */ +		irq_set_chip_and_handler(irq, &tilegx_legacy_irq_chip, +					trio_handle_level_irq); +		irq_set_chip_data(irq, (void *)(uint64_t)i); +		irq_set_handler_data(irq, controller); +	} + +	return 0; + +free_irqs: +	for (j = 0; j < i; j++) +		destroy_irq(controller->irq_intx_table[j]); + +	return -1; +} + +/* + * Find valid controllers and fill in pci_controller structs for each + * of them. + * + * Returns the number of controllers discovered. + */ +int __init tile_pci_init(void) +{ +	int num_trio_shims = 0; +	int ctl_index = 0; +	int i, j; + +	if (!pci_probe) { +		pr_info("PCI: disabled by boot argument\n"); +		return 0; +	} + +	pr_info("PCI: Searching for controllers...\n"); + +	/* +	 * We loop over all the TRIO shims. +	 */ +	for (i = 0; i < TILEGX_NUM_TRIO; i++) { +		int ret; + +		ret = tile_pcie_open(i); +		if (ret < 0) +			continue; + +		num_trio_shims++; +	} + +	if (num_trio_shims == 0 || sim_is_simulator()) +		return 0; + +	/* +	 * Now determine which PCIe ports are configured to operate in RC mode. +	 * We look at the Board Information Block first and then see if there +	 * are any overriding configuration by the HW strapping pin. +	 */ +	for (i = 0; i < TILEGX_NUM_TRIO; i++) { +		gxio_trio_context_t *context = &trio_contexts[i]; +		int ret; + +		if (context->fd < 0) +			continue; + +		ret = hv_dev_pread(context->fd, 0, +			(HV_VirtAddr)&pcie_ports[i][0], +			sizeof(struct pcie_port_property) * TILEGX_TRIO_PCIES, +			GXIO_TRIO_OP_GET_PORT_PROPERTY); +		if (ret < 0) { +			pr_err("PCI: PCIE_GET_PORT_PROPERTY failure, error %d," +				" on TRIO %d\n", ret, i); +			continue; +		} + +		for (j = 0; j < TILEGX_TRIO_PCIES; j++) { +			if (pcie_ports[i][j].allow_rc) { +				pcie_rc[i][j] = 1; +				num_rc_controllers++; +			} +			else if (pcie_ports[i][j].allow_ep) { +				num_ep_controllers++; +			} +		} +	} + +	/* +	 * Return if no PCIe ports are configured to operate in RC mode. +	 */ +	if (num_rc_controllers == 0) +		return 0; + +	/* +	 * Set the TRIO pointer and MAC index for each PCIe RC port. +	 */ +	for (i = 0; i < TILEGX_NUM_TRIO; i++) { +		for (j = 0; j < TILEGX_TRIO_PCIES; j++) { +			if (pcie_rc[i][j]) { +				pci_controllers[ctl_index].trio = +					&trio_contexts[i]; +				pci_controllers[ctl_index].mac = j; +				pci_controllers[ctl_index].trio_index = i; +				ctl_index++; +				if (ctl_index == num_rc_controllers) +					goto out; +			} +		} +	} + +out: +	/* +	 * Configure each PCIe RC port. +	 */ +	for (i = 0; i < num_rc_controllers; i++) { +		/* +		 * Configure the PCIe MAC to run in RC mode. +		 */ + +		struct pci_controller *controller = &pci_controllers[i]; + +		controller->index = i; +		controller->ops = &tile_cfg_ops; + +		/* +		 * The PCI memory resource is located above the PA space. +		 * For every host bridge, the BAR window or the MMIO aperture +		 * is in range [3GB, 4GB - 1] of a 4GB space beyond the +		 * PA space. +		 */ + +		controller->mem_offset = TILE_PCI_MEM_START + +			(i * TILE_PCI_BAR_WINDOW_TOP); +		controller->mem_space.start = controller->mem_offset + +			TILE_PCI_BAR_WINDOW_TOP - TILE_PCI_BAR_WINDOW_SIZE; +		controller->mem_space.end = controller->mem_offset + +			TILE_PCI_BAR_WINDOW_TOP - 1; +		controller->mem_space.flags = IORESOURCE_MEM; +		snprintf(controller->mem_space_name, +			 sizeof(controller->mem_space_name), +			 "PCI mem domain %d", i); +		controller->mem_space.name = controller->mem_space_name; +	} + +	return num_rc_controllers; +} + +/* + * (pin - 1) converts from the PCI standard's [1:4] convention to + * a normal [0:3] range. + */ +static int tile_map_irq(const struct pci_dev *dev, u8 device, u8 pin) +{ +	struct pci_controller *controller = +		(struct pci_controller *)dev->sysdata; +	return controller->irq_intx_table[pin - 1]; +} + + +static void __devinit fixup_read_and_payload_sizes(struct pci_controller * +						controller) +{ +	gxio_trio_context_t *trio_context = controller->trio; +	struct pci_bus *root_bus = controller->root_bus; +	TRIO_PCIE_RC_DEVICE_CONTROL_t dev_control; +	TRIO_PCIE_RC_DEVICE_CAP_t rc_dev_cap; +	unsigned int reg_offset; +	struct pci_bus *child; +	int mac; +	int err; + +	mac = controller->mac; + +	/* +	 * Set our max read request size to be 4KB. +	 */ +	reg_offset = +		(TRIO_PCIE_RC_DEVICE_CONTROL << +			TRIO_CFG_REGION_ADDR__REG_SHIFT) | +		(TRIO_CFG_REGION_ADDR__INTFC_VAL_MAC_STANDARD << +			TRIO_CFG_REGION_ADDR__INTFC_SHIFT ) | +		(mac << TRIO_CFG_REGION_ADDR__MAC_SEL_SHIFT); + +	dev_control.word = __gxio_mmio_read32(trio_context->mmio_base_mac + +						reg_offset); +	dev_control.max_read_req_sz = 5; +	__gxio_mmio_write32(trio_context->mmio_base_mac + reg_offset, +						dev_control.word); + +	/* +	 * Set the max payload size supported by this Gx PCIe MAC. +	 * Though Gx PCIe supports Max Payload Size of up to 1024 bytes, +	 * experiments have shown that setting MPS to 256 yields the +	 * best performance. +	 */ +	reg_offset = +		(TRIO_PCIE_RC_DEVICE_CAP << +			TRIO_CFG_REGION_ADDR__REG_SHIFT) | +		(TRIO_CFG_REGION_ADDR__INTFC_VAL_MAC_STANDARD << +			TRIO_CFG_REGION_ADDR__INTFC_SHIFT ) | +		(mac << TRIO_CFG_REGION_ADDR__MAC_SEL_SHIFT); + +	rc_dev_cap.word = __gxio_mmio_read32(trio_context->mmio_base_mac + +						reg_offset); +	rc_dev_cap.mps_sup = 1; +	__gxio_mmio_write32(trio_context->mmio_base_mac + reg_offset, +						rc_dev_cap.word); + +	/* Configure PCI Express MPS setting. */ +	list_for_each_entry(child, &root_bus->children, node) { +		struct pci_dev *self = child->self; +		if (!self) +			continue; + +		pcie_bus_configure_settings(child, self->pcie_mpss); +	} + +	/* +	 * Set the mac_config register in trio based on the MPS/MRS of the link. +	 */ +	reg_offset = +		(TRIO_PCIE_RC_DEVICE_CONTROL << +			TRIO_CFG_REGION_ADDR__REG_SHIFT) | +		(TRIO_CFG_REGION_ADDR__INTFC_VAL_MAC_STANDARD << +			TRIO_CFG_REGION_ADDR__INTFC_SHIFT ) | +		(mac << TRIO_CFG_REGION_ADDR__MAC_SEL_SHIFT); + +	dev_control.word = __gxio_mmio_read32(trio_context->mmio_base_mac + +						reg_offset); + +	err = gxio_trio_set_mps_mrs(trio_context, +				    dev_control.max_payload_size, +				    dev_control.max_read_req_sz, +				    mac); +        if (err < 0) { +		pr_err("PCI: PCIE_CONFIGURE_MAC_MPS_MRS failure, " +			"MAC %d on TRIO %d\n", +			mac, controller->trio_index); +	} +} + +static int __devinit setup_pcie_rc_delay(char *str) +{ +	unsigned long delay = 0; +	unsigned long trio_index; +	unsigned long mac; + +	if (str == NULL || !isdigit(*str)) +		return -EINVAL; +	trio_index = simple_strtoul(str, (char **)&str, 10); +	if (trio_index >= TILEGX_NUM_TRIO) +		return -EINVAL; + +	if (*str != ',') +		return -EINVAL; + +	str++; +	if (!isdigit(*str)) +		return -EINVAL; +	mac = simple_strtoul(str, (char **)&str, 10); +	if (mac >= TILEGX_TRIO_PCIES) +		return -EINVAL; + +	if (*str != '\0') { +		if (*str != ',') +			return -EINVAL; + +		str++; +		if (!isdigit(*str)) +			return -EINVAL; +		delay = simple_strtoul(str, (char **)&str, 10); +		if (delay > MAX_RC_DELAY) +			return -EINVAL; +	} + +	rc_delay[trio_index][mac] = delay ? : DEFAULT_RC_DELAY; +	pr_info("Delaying PCIe RC link training for %u sec" +		" on MAC %lu on TRIO %lu\n", rc_delay[trio_index][mac], +		mac, trio_index); +	return 0; +} +early_param("pcie_rc_delay", setup_pcie_rc_delay); + +/* + * PCI initialization entry point, called by subsys_initcall. + */ +int __init pcibios_init(void) +{ +	resource_size_t offset; +	LIST_HEAD(resources); +	int next_busno; +	int i; + +	tile_pci_init(); + +	if (num_rc_controllers == 0 && num_ep_controllers == 0) +		return 0; + +	/* +	 * We loop over all the TRIO shims and set up the MMIO mappings. +	 */ +	for (i = 0; i < TILEGX_NUM_TRIO; i++) { +		gxio_trio_context_t *context = &trio_contexts[i]; + +		if (context->fd < 0) +			continue; + +		/* +		 * Map in the MMIO space for the MAC. +		 */ +		offset = 0; +		context->mmio_base_mac = +			iorpc_ioremap(context->fd, offset, +				      HV_TRIO_CONFIG_IOREMAP_SIZE); +		if (context->mmio_base_mac == NULL) { +			pr_err("PCI: MAC map failure on TRIO %d\n", i); + +			hv_dev_close(context->fd); +			context->fd = -1; +			continue; +		} +	} + +	/* +	 * Delay a bit in case devices aren't ready.  Some devices are +	 * known to require at least 20ms here, but we use a more +	 * conservative value. +	 */ +	msleep(250); + +	/* Scan all of the recorded PCI controllers.  */ +	for (next_busno = 0, i = 0; i < num_rc_controllers; i++) { +		struct pci_controller *controller = &pci_controllers[i]; +		gxio_trio_context_t *trio_context = controller->trio; +		TRIO_PCIE_INTFC_PORT_CONFIG_t port_config; +		TRIO_PCIE_INTFC_PORT_STATUS_t port_status; +		TRIO_PCIE_INTFC_TX_FIFO_CTL_t tx_fifo_ctl; +		struct pci_bus *bus; +		unsigned int reg_offset; +		unsigned int class_code_revision; +		int trio_index; +		int mac; +		int ret; + +		if (trio_context->fd < 0) +			continue; + +		trio_index = controller->trio_index; +		mac = controller->mac; + +		/* +		 * Check the port strap state which will override the BIB +		 * setting. +		 */ + +		reg_offset = +			(TRIO_PCIE_INTFC_PORT_CONFIG << +				TRIO_CFG_REGION_ADDR__REG_SHIFT) | +			(TRIO_CFG_REGION_ADDR__INTFC_VAL_MAC_INTERFACE << +				TRIO_CFG_REGION_ADDR__INTFC_SHIFT ) | +			(mac << TRIO_CFG_REGION_ADDR__MAC_SEL_SHIFT); + +		port_config.word = +			__gxio_mmio_read(trio_context->mmio_base_mac + +					 reg_offset); + +		if ((port_config.strap_state != +			TRIO_PCIE_INTFC_PORT_CONFIG__STRAP_STATE_VAL_AUTO_CONFIG_RC) && +			(port_config.strap_state != +			TRIO_PCIE_INTFC_PORT_CONFIG__STRAP_STATE_VAL_AUTO_CONFIG_RC_G1)) { +			/* +			 * If this is really intended to be an EP port, +			 * record it so that the endpoint driver will know about it. +			 */ +			if (port_config.strap_state == +			TRIO_PCIE_INTFC_PORT_CONFIG__STRAP_STATE_VAL_AUTO_CONFIG_ENDPOINT || +			port_config.strap_state == +			TRIO_PCIE_INTFC_PORT_CONFIG__STRAP_STATE_VAL_AUTO_CONFIG_ENDPOINT_G1) +				pcie_ports[trio_index][mac].allow_ep = 1; + +			continue; +		} + +		/* +		 * Delay the RC link training if needed. +		 */ +		if (rc_delay[trio_index][mac]) +			msleep(rc_delay[trio_index][mac] * 1000); + +		ret = gxio_trio_force_rc_link_up(trio_context, mac); +		if (ret < 0) +			pr_err("PCI: PCIE_FORCE_LINK_UP failure, " +				"MAC %d on TRIO %d\n", mac, trio_index); + +		pr_info("PCI: Found PCI controller #%d on TRIO %d MAC %d\n", i, +			trio_index, controller->mac); + +		/* +		 * Wait a bit here because some EP devices take longer +		 * to come up. +		 */ +		msleep(1000); + +		/* +		 * Check for PCIe link-up status. +		 */ + +		reg_offset = +			(TRIO_PCIE_INTFC_PORT_STATUS << +				TRIO_CFG_REGION_ADDR__REG_SHIFT) | +			(TRIO_CFG_REGION_ADDR__INTFC_VAL_MAC_INTERFACE << +				TRIO_CFG_REGION_ADDR__INTFC_SHIFT ) | +			(mac << TRIO_CFG_REGION_ADDR__MAC_SEL_SHIFT); + +		port_status.word = +			__gxio_mmio_read(trio_context->mmio_base_mac + +					 reg_offset); +		if (!port_status.dl_up) { +			pr_err("PCI: link is down, MAC %d on TRIO %d\n", +				mac, trio_index); +			continue; +		} + +		/* +		 * Ensure that the link can come out of L1 power down state. +		 * Strictly speaking, this is needed only in the case of +		 * heavy RC-initiated DMAs. +		 */ +		reg_offset = +			(TRIO_PCIE_INTFC_TX_FIFO_CTL << +				TRIO_CFG_REGION_ADDR__REG_SHIFT) | +			(TRIO_CFG_REGION_ADDR__INTFC_VAL_MAC_INTERFACE << +				TRIO_CFG_REGION_ADDR__INTFC_SHIFT ) | +			(mac << TRIO_CFG_REGION_ADDR__MAC_SEL_SHIFT); +		tx_fifo_ctl.word = +			__gxio_mmio_read(trio_context->mmio_base_mac + +					 reg_offset); +		tx_fifo_ctl.min_p_credits = 0; +		__gxio_mmio_write(trio_context->mmio_base_mac + reg_offset, +				  tx_fifo_ctl.word); + +		/* +		 * Change the device ID so that Linux bus crawl doesn't confuse +		 * the internal bridge with any Tilera endpoints. +		 */ + +		reg_offset = +			(TRIO_PCIE_RC_DEVICE_ID_VEN_ID << +				TRIO_CFG_REGION_ADDR__REG_SHIFT) | +			(TRIO_CFG_REGION_ADDR__INTFC_VAL_MAC_STANDARD << +				TRIO_CFG_REGION_ADDR__INTFC_SHIFT ) | +			(mac << TRIO_CFG_REGION_ADDR__MAC_SEL_SHIFT); + +		__gxio_mmio_write32(trio_context->mmio_base_mac + reg_offset, +				    (TILERA_GX36_RC_DEV_ID << +				    TRIO_PCIE_RC_DEVICE_ID_VEN_ID__DEV_ID_SHIFT) | +				    TILERA_VENDOR_ID); + +		/* +		 * Set the internal P2P bridge class code. +		 */ + +		reg_offset = +			(TRIO_PCIE_RC_REVISION_ID << +				TRIO_CFG_REGION_ADDR__REG_SHIFT) | +			(TRIO_CFG_REGION_ADDR__INTFC_VAL_MAC_STANDARD << +				TRIO_CFG_REGION_ADDR__INTFC_SHIFT ) | +			(mac << TRIO_CFG_REGION_ADDR__MAC_SEL_SHIFT); + +		class_code_revision = +			__gxio_mmio_read32(trio_context->mmio_base_mac + +					   reg_offset); +		class_code_revision = (class_code_revision & 0xff ) | +					(PCI_CLASS_BRIDGE_PCI << 16); + +		__gxio_mmio_write32(trio_context->mmio_base_mac + +				    reg_offset, class_code_revision); + +#ifdef USE_SHARED_PCIE_CONFIG_REGION + +		/* +		 * Map in the MMIO space for the PIO region. +		 */ +		offset = HV_TRIO_PIO_OFFSET(trio_context->pio_cfg_index) | +			(((unsigned long long)mac) << +			TRIO_TILE_PIO_REGION_SETUP_CFG_ADDR__MAC_SHIFT); + +#else + +		/* +		 * Alloc a PIO region for PCI config access per MAC. +		 */ +		ret = gxio_trio_alloc_pio_regions(trio_context, 1, 0, 0); +		if (ret < 0) { +			pr_err("PCI: PCI CFG PIO alloc failure for mac %d " +				"on TRIO %d, give up\n", mac, trio_index); + +			continue; +		} + +		trio_context->pio_cfg_index[mac] = ret; + +		/* +		 * For PIO CFG, the bus_address_hi parameter is 0. +		 */ +		ret = gxio_trio_init_pio_region_aux(trio_context, +			trio_context->pio_cfg_index[mac], +			mac, 0, HV_TRIO_PIO_FLAG_CONFIG_SPACE); +		if (ret < 0) { +			pr_err("PCI: PCI CFG PIO init failure for mac %d " +				"on TRIO %d, give up\n", mac, trio_index); + +			continue; +		} + +		offset = HV_TRIO_PIO_OFFSET(trio_context->pio_cfg_index[mac]) | +			(((unsigned long long)mac) << +			TRIO_TILE_PIO_REGION_SETUP_CFG_ADDR__MAC_SHIFT); + +#endif + +		trio_context->mmio_base_pio_cfg[mac] = +			iorpc_ioremap(trio_context->fd, offset, +			(1 << TRIO_TILE_PIO_REGION_SETUP_CFG_ADDR__MAC_SHIFT)); +		if (trio_context->mmio_base_pio_cfg[mac] == NULL) { +			pr_err("PCI: PIO map failure for mac %d on TRIO %d\n", +				mac, trio_index); + +			continue; +		} + +		/* +		 * Initialize the PCIe interrupts. +		 */ +		if (tile_init_irqs(controller)) { +			pr_err("PCI: IRQs init failure for mac %d on TRIO %d\n", +				mac, trio_index); + +			continue; +		} + +		/* +		 * The PCI memory resource is located above the PA space. +		 * The memory range for the PCI root bus should not overlap +		 * with the physical RAM +		 */ +		pci_add_resource_offset(&resources, &controller->mem_space, +					controller->mem_offset); + +		controller->first_busno = next_busno; +		bus = pci_scan_root_bus(NULL, next_busno, controller->ops, +					controller, &resources); +		controller->root_bus = bus; +		next_busno = bus->subordinate + 1; + +	} + +	/* Do machine dependent PCI interrupt routing */ +	pci_fixup_irqs(pci_common_swizzle, tile_map_irq); + +	/* +	 * This comes from the generic Linux PCI driver. +	 * +	 * It allocates all of the resources (I/O memory, etc) +	 * associated with the devices read in above. +	 */ + +	pci_assign_unassigned_resources(); + +	/* Record the I/O resources in the PCI controller structure. */ +	for (i = 0; i < num_rc_controllers; i++) { +		struct pci_controller *controller = &pci_controllers[i]; +		gxio_trio_context_t *trio_context = controller->trio; +		struct pci_bus *root_bus = pci_controllers[i].root_bus; +		struct pci_bus *next_bus; +		uint32_t bus_address_hi; +		struct pci_dev *dev; +		int ret; +		int j; + +		/* +		 * Skip controllers that are not properly initialized or +		 * have down links. +		 */ +		if (root_bus == NULL) +			continue; + +		/* Configure the max_payload_size values for this domain. */ +		fixup_read_and_payload_sizes(controller); + +		list_for_each_entry(dev, &root_bus->devices, bus_list) { +			/* Find the PCI host controller, ie. the 1st bridge. */ +			if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI && +				(PCI_SLOT(dev->devfn) == 0)) { +				next_bus = dev->subordinate; +				pci_controllers[i].mem_resources[0] = +					*next_bus->resource[0]; +				pci_controllers[i].mem_resources[1] = +					 *next_bus->resource[1]; +				pci_controllers[i].mem_resources[2] = +					 *next_bus->resource[2]; + +				break; +			} +		} + +		if (pci_controllers[i].mem_resources[1].flags & IORESOURCE_MEM) +			bus_address_hi = +				pci_controllers[i].mem_resources[1].start >> 32; +		else if (pci_controllers[i].mem_resources[2].flags & IORESOURCE_PREFETCH) +			bus_address_hi = +				pci_controllers[i].mem_resources[2].start >> 32; +		else { +			/* This is unlikely. */ +			pr_err("PCI: no memory resources on TRIO %d mac %d\n", +				controller->trio_index, controller->mac); +			continue; +		} + +		/* +		 * Alloc a PIO region for PCI memory access for each RC port. +		 */ +		ret = gxio_trio_alloc_pio_regions(trio_context, 1, 0, 0); +		if (ret < 0) { +			pr_err("PCI: MEM PIO alloc failure on TRIO %d mac %d, " +				"give up\n", controller->trio_index, +				controller->mac); + +			continue; +		} + +		controller->pio_mem_index = ret; + +		/* +		 * For PIO MEM, the bus_address_hi parameter is hard-coded 0 +		 * because we always assign 32-bit PCI bus BAR ranges. +		 */ +		ret = gxio_trio_init_pio_region_aux(trio_context, +						    controller->pio_mem_index, +						    controller->mac, +						    0, +						    0); +		if (ret < 0) { +			pr_err("PCI: MEM PIO init failure on TRIO %d mac %d, " +				"give up\n", controller->trio_index, +				controller->mac); + +			continue; +		} + +		/* +		 * Configure a Mem-Map region for each memory controller so +		 * that Linux can map all of its PA space to the PCI bus. +		 * Use the IOMMU to handle hash-for-home memory. +		 */ +		for_each_online_node(j) { +			unsigned long start_pfn = node_start_pfn[j]; +			unsigned long end_pfn = node_end_pfn[j]; +			unsigned long nr_pages = end_pfn - start_pfn; + +			ret = gxio_trio_alloc_memory_maps(trio_context, 1, 0, +							  0); +			if (ret < 0) { +				pr_err("PCI: Mem-Map alloc failure on TRIO %d " +					"mac %d for MC %d, give up\n", +					controller->trio_index, +					controller->mac, j); + +				goto alloc_mem_map_failed; +			} + +			controller->mem_maps[j] = ret; + +			/* +			 * Initialize the Mem-Map and the I/O MMU so that all +			 * the physical memory can be accessed by the endpoint +			 * devices. The base bus address is set to the base CPA +			 * of this memory controller plus an offset (see pci.h). +			 * The region's base VA is set to the base CPA. The +			 * I/O MMU table essentially translates the CPA to +			 * the real PA. Implicitly, for node 0, we create +			 * a separate Mem-Map region that serves as the inbound +			 * window for legacy 32-bit devices. This is a direct +			 * map of the low 4GB CPA space. +			 */ +			ret = gxio_trio_init_memory_map_mmu_aux(trio_context, +				controller->mem_maps[j], +				start_pfn << PAGE_SHIFT, +				nr_pages << PAGE_SHIFT, +				trio_context->asid, +				controller->mac, +				(start_pfn << PAGE_SHIFT) + +				TILE_PCI_MEM_MAP_BASE_OFFSET, +				j, +				GXIO_TRIO_ORDER_MODE_UNORDERED); +			if (ret < 0) { +				pr_err("PCI: Mem-Map init failure on TRIO %d " +					"mac %d for MC %d, give up\n", +					controller->trio_index, +					controller->mac, j); + +				goto alloc_mem_map_failed; +			} +			continue; + +alloc_mem_map_failed: +			break; +		} + +	} + +	return 0; +} +subsys_initcall(pcibios_init); + +/* Note: to be deleted after Linux 3.6 merge. */ +void __devinit pcibios_fixup_bus(struct pci_bus *bus) +{ +} + +/* + * This can be called from the generic PCI layer, but doesn't need to + * do anything. + */ +char __devinit *pcibios_setup(char *str) +{ +	if (!strcmp(str, "off")) { +		pci_probe = 0; +		return NULL; +	} +	return str; +} + +/* + * This is called from the generic Linux layer. + */ +void __devinit pcibios_update_irq(struct pci_dev *dev, int irq) +{ +	pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); +} + +/* + * Enable memory address decoding, as appropriate, for the + * device described by the 'dev' struct. The I/O decoding + * is disabled, though the TILE-Gx supports I/O addressing. + * + * This is called from the generic PCI layer, and can be called + * for bridges or endpoints. + */ +int pcibios_enable_device(struct pci_dev *dev, int mask) +{ +	return pci_enable_resources(dev, mask); +} + +/* Called for each device after PCI setup is done. */ +static void __init +pcibios_fixup_final(struct pci_dev *pdev) +{ +	set_dma_ops(&pdev->dev, gx_pci_dma_map_ops); +	set_dma_offset(&pdev->dev, TILE_PCI_MEM_MAP_BASE_OFFSET); +	pdev->dev.archdata.max_direct_dma_addr = +		TILE_PCI_MAX_DIRECT_DMA_ADDRESS; +} +DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, pcibios_fixup_final); + +/* Map a PCI MMIO bus address into VA space. */ +void __iomem *ioremap(resource_size_t phys_addr, unsigned long size) +{ +	struct pci_controller *controller = NULL; +	resource_size_t bar_start; +	resource_size_t bar_end; +	resource_size_t offset; +	resource_size_t start; +	resource_size_t end; +	int trio_fd; +	int i, j; + +	start = phys_addr; +	end = phys_addr + size - 1; + +	/* +	 * In the following, each PCI controller's mem_resources[1] +	 * represents its (non-prefetchable) PCI memory resource and +	 * mem_resources[2] refers to its prefetchable PCI memory resource. +	 * By searching phys_addr in each controller's mem_resources[], we can +	 * determine the controller that should accept the PCI memory access. +	 */ + +	for (i = 0; i < num_rc_controllers; i++) { +		/* +		 * Skip controllers that are not properly initialized or +		 * have down links. +		 */ +		if (pci_controllers[i].root_bus == NULL) +			continue; + +		for (j = 1; j < 3; j++) { +			bar_start = +				pci_controllers[i].mem_resources[j].start; +			bar_end = +				pci_controllers[i].mem_resources[j].end; + +			if ((start >= bar_start) && (end <= bar_end)) { + +				controller = &pci_controllers[i]; + +				goto got_it; +			} +		} +	} + +	if (controller == NULL) +		return NULL; + +got_it: +	trio_fd = controller->trio->fd; + +	/* Convert the resource start to the bus address offset. */ +	start = phys_addr - controller->mem_offset; + +	offset = HV_TRIO_PIO_OFFSET(controller->pio_mem_index) + start; + +	/* +	 * We need to keep the PCI bus address's in-page offset in the VA. +	 */ +	return iorpc_ioremap(trio_fd, offset, size) + +		(phys_addr & (PAGE_SIZE - 1)); +} +EXPORT_SYMBOL(ioremap); + +void pci_iounmap(struct pci_dev *dev, void __iomem *addr) +{ +	iounmap(addr); +} +EXPORT_SYMBOL(pci_iounmap); + +/**************************************************************** + * + * Tile PCI config space read/write routines + * + ****************************************************************/ + +/* + * These are the normal read and write ops + * These are expanded with macros from  pci_bus_read_config_byte() etc. + * + * devfn is the combined PCI device & function. + * + * offset is in bytes, from the start of config space for the + * specified bus & device. + */ + +static int __devinit tile_cfg_read(struct pci_bus *bus, +				   unsigned int devfn, +				   int offset, +				   int size, +				   u32 *val) +{ +	struct pci_controller *controller = bus->sysdata; +	gxio_trio_context_t *trio_context = controller->trio; +	int busnum = bus->number & 0xff; +	int device = PCI_SLOT(devfn); +	int function = PCI_FUNC(devfn); +	int config_type = 1; +	TRIO_TILE_PIO_REGION_SETUP_CFG_ADDR_t cfg_addr; +	void *mmio_addr; + +	/* +	 * Map all accesses to the local device on root bus into the +	 * MMIO space of the MAC. Accesses to the downstream devices +	 * go to the PIO space. +	 */ +	if (pci_is_root_bus(bus)) { +		if (device == 0) { +			/* +			 * This is the internal downstream P2P bridge, +			 * access directly. +			 */ +			unsigned int reg_offset; + +			reg_offset = ((offset & 0xFFF) << +				TRIO_CFG_REGION_ADDR__REG_SHIFT) | +				(TRIO_CFG_REGION_ADDR__INTFC_VAL_MAC_PROTECTED +				<< TRIO_CFG_REGION_ADDR__INTFC_SHIFT ) | +				(controller->mac << +					TRIO_CFG_REGION_ADDR__MAC_SEL_SHIFT); + +			mmio_addr = trio_context->mmio_base_mac + reg_offset; + +			goto valid_device; + +		} else { +			/* +			 * We fake an empty device for (device > 0), +			 * since there is only one device on bus 0. +			 */ +			goto invalid_device; +		} +	} + +	/* +	 * Accesses to the directly attached device have to be +	 * sent as type-0 configs. +	 */ + +	if (busnum == (controller->first_busno + 1)) { +		/* +		 * There is only one device off of our built-in P2P bridge. +		 */ +		if (device != 0) +			goto invalid_device; + +		config_type = 0; +	} + +	cfg_addr.word = 0; +	cfg_addr.reg_addr = (offset & 0xFFF); +	cfg_addr.fn = function; +	cfg_addr.dev = device; +	cfg_addr.bus = busnum; +	cfg_addr.type = config_type; + +	/* +	 * Note that we don't set the mac field in cfg_addr because the +	 * mapping is per port. +	 */ + +	mmio_addr = trio_context->mmio_base_pio_cfg[controller->mac] + +			cfg_addr.word; + +valid_device: + +	switch (size) { +	case 4: +		*val = __gxio_mmio_read32(mmio_addr); +		break; + +	case 2: +		*val = __gxio_mmio_read16(mmio_addr); +		break; + +	case 1: +		*val = __gxio_mmio_read8(mmio_addr); +		break; + +	default: +		return PCIBIOS_FUNC_NOT_SUPPORTED; +	} + +	TRACE_CFG_RD(size, *val, busnum, device, function, offset); + +	return 0; + +invalid_device: + +	switch (size) { +	case 4: +		*val = 0xFFFFFFFF; +		break; + +	case 2: +		*val = 0xFFFF; +		break; + +	case 1: +		*val = 0xFF; +		break; + +	default: +		return PCIBIOS_FUNC_NOT_SUPPORTED; +	} + +	return 0; +} + + +/* + * See tile_cfg_read() for relevent comments. + * Note that "val" is the value to write, not a pointer to that value. + */ +static int __devinit tile_cfg_write(struct pci_bus *bus, +				    unsigned int devfn, +				    int offset, +				    int size, +				    u32 val) +{ +	struct pci_controller *controller = bus->sysdata; +	gxio_trio_context_t *trio_context = controller->trio; +	int busnum = bus->number & 0xff; +	int device = PCI_SLOT(devfn); +	int function = PCI_FUNC(devfn); +	int config_type = 1; +	TRIO_TILE_PIO_REGION_SETUP_CFG_ADDR_t cfg_addr; +	void *mmio_addr; +	u32 val_32 = (u32)val; +	u16 val_16 = (u16)val; +	u8 val_8 = (u8)val; + +	/* +	 * Map all accesses to the local device on root bus into the +	 * MMIO space of the MAC. Accesses to the downstream devices +	 * go to the PIO space. +	 */ +	if (pci_is_root_bus(bus)) { +		if (device == 0) { +			/* +			 * This is the internal downstream P2P bridge, +			 * access directly. +			 */ +			unsigned int reg_offset; + +			reg_offset = ((offset & 0xFFF) << +				TRIO_CFG_REGION_ADDR__REG_SHIFT) | +				(TRIO_CFG_REGION_ADDR__INTFC_VAL_MAC_PROTECTED +				<< TRIO_CFG_REGION_ADDR__INTFC_SHIFT ) | +				(controller->mac << +					TRIO_CFG_REGION_ADDR__MAC_SEL_SHIFT); + +			mmio_addr = trio_context->mmio_base_mac + reg_offset; + +			goto valid_device; + +		} else { +			/* +			 * We fake an empty device for (device > 0), +			 * since there is only one device on bus 0. +			 */ +			goto invalid_device; +		} +	} + +	/* +	 * Accesses to the directly attached device have to be +	 * sent as type-0 configs. +	 */ + +	if (busnum == (controller->first_busno + 1)) { +		/* +		 * There is only one device off of our built-in P2P bridge. +		 */ +		if (device != 0) +			goto invalid_device; + +		config_type = 0; +	} + +	cfg_addr.word = 0; +	cfg_addr.reg_addr = (offset & 0xFFF); +	cfg_addr.fn = function; +	cfg_addr.dev = device; +	cfg_addr.bus = busnum; +	cfg_addr.type = config_type; + +	/* +	 * Note that we don't set the mac field in cfg_addr because the +	 * mapping is per port. +	 */ + +	mmio_addr = trio_context->mmio_base_pio_cfg[controller->mac] + +			cfg_addr.word; + +valid_device: + +	switch (size) { +	case 4: +		__gxio_mmio_write32(mmio_addr, val_32); +		TRACE_CFG_WR(size, val_32, busnum, device, function, offset); +		break; + +	case 2: +		__gxio_mmio_write16(mmio_addr, val_16); +		TRACE_CFG_WR(size, val_16, busnum, device, function, offset); +		break; + +	case 1: +		__gxio_mmio_write8(mmio_addr, val_8); +		TRACE_CFG_WR(size, val_8, busnum, device, function, offset); +		break; + +	default: +		return PCIBIOS_FUNC_NOT_SUPPORTED; +	} + +invalid_device: + +	return 0; +} + + +static struct pci_ops tile_cfg_ops = { +	.read =         tile_cfg_read, +	.write =        tile_cfg_write, +}; + + +/* + * MSI support starts here. + */ +static unsigned int +tilegx_msi_startup(struct irq_data *d) +{ +	if (d->msi_desc) +		unmask_msi_irq(d); + +	return 0; +} + +static void +tilegx_msi_ack(struct irq_data *d) +{ +	__insn_mtspr(SPR_IPI_EVENT_RESET_K, 1UL << d->irq); +} + +static void +tilegx_msi_mask(struct irq_data *d) +{ +	mask_msi_irq(d); +	__insn_mtspr(SPR_IPI_MASK_SET_K, 1UL << d->irq); +} + +static void +tilegx_msi_unmask(struct irq_data *d) +{ +	__insn_mtspr(SPR_IPI_MASK_RESET_K, 1UL << d->irq); +	unmask_msi_irq(d); +} + +static struct irq_chip tilegx_msi_chip = { +	.name			= "tilegx_msi", +	.irq_startup		= tilegx_msi_startup, +	.irq_ack		= tilegx_msi_ack, +	.irq_mask		= tilegx_msi_mask, +	.irq_unmask		= tilegx_msi_unmask, + +	/* TBD: support set_affinity. */ +}; + +int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc) +{ +	struct pci_controller *controller; +	gxio_trio_context_t *trio_context; +	struct msi_msg msg; +	int default_irq; +	uint64_t mem_map_base; +	uint64_t mem_map_limit; +	u64 msi_addr; +	int mem_map; +	int cpu; +	int irq; +	int ret; + +	irq = create_irq(); +	if (irq < 0) +		return irq; + +	/* +	 * Since we use a 64-bit Mem-Map to accept the MSI write, we fail +	 * devices that are not capable of generating a 64-bit message address. +	 * These devices will fall back to using the legacy interrupts. +	 * Most PCIe endpoint devices do support 64-bit message addressing. +	 */ +	if (desc->msi_attrib.is_64 == 0) { +		dev_printk(KERN_INFO, &pdev->dev, +			"64-bit MSI message address not supported, " +			"falling back to legacy interrupts.\n"); + +		ret = -ENOMEM; +		goto is_64_failure; +	} + +	default_irq = desc->msi_attrib.default_irq; +	controller = irq_get_handler_data(default_irq); + +	BUG_ON(!controller); + +	trio_context = controller->trio; + +	/* +	 * Allocate the Mem-Map that will accept the MSI write and +	 * trigger the TILE-side interrupts. +	 */ +	mem_map = gxio_trio_alloc_memory_maps(trio_context, 1, 0, 0); +	if (mem_map < 0) { +		dev_printk(KERN_INFO, &pdev->dev, +			"%s Mem-Map alloc failure. " +			"Failed to initialize MSI interrupts. " +			"Falling back to legacy interrupts.\n", +			desc->msi_attrib.is_msix ? "MSI-X" : "MSI"); + +		ret = -ENOMEM; +		goto msi_mem_map_alloc_failure; +	} + +	/* We try to distribute different IRQs to different tiles. */ +	cpu = tile_irq_cpu(irq); + +	/* +	 * Now call up to the HV to configure the Mem-Map interrupt and +	 * set up the IPI binding. +	 */ +	mem_map_base = MEM_MAP_INTR_REGIONS_BASE + +		mem_map * MEM_MAP_INTR_REGION_SIZE; +	mem_map_limit = mem_map_base + MEM_MAP_INTR_REGION_SIZE - 1; + +	ret = gxio_trio_config_msi_intr(trio_context, cpu_x(cpu), cpu_y(cpu), +					KERNEL_PL, irq, controller->mac, +					mem_map, mem_map_base, mem_map_limit, +					trio_context->asid); +	if (ret < 0) { +		dev_printk(KERN_INFO, &pdev->dev, "HV MSI config failed.\n"); + +		goto hv_msi_config_failure; +	} + +	irq_set_msi_desc(irq, desc); + +	msi_addr = mem_map_base + TRIO_MAP_MEM_REG_INT3 - TRIO_MAP_MEM_REG_INT0; + +	msg.address_hi = msi_addr >> 32; +	msg.address_lo = msi_addr & 0xffffffff; + +	msg.data = mem_map; + +	write_msi_msg(irq, &msg); +	irq_set_chip_and_handler(irq, &tilegx_msi_chip, handle_level_irq); +	irq_set_handler_data(irq, controller); + +	return 0; + +hv_msi_config_failure: +	/* Free mem-map */ +msi_mem_map_alloc_failure: +is_64_failure: +	destroy_irq(irq); +	return ret; +} + +void arch_teardown_msi_irq(unsigned int irq) +{ +	destroy_irq(irq); +} diff --git a/arch/tile/kernel/setup.c b/arch/tile/kernel/setup.c index dd87f342039..6a649a4462d 100644 --- a/arch/tile/kernel/setup.c +++ b/arch/tile/kernel/setup.c @@ -23,6 +23,7 @@  #include <linux/irq.h>  #include <linux/kexec.h>  #include <linux/pci.h> +#include <linux/swiotlb.h>  #include <linux/initrd.h>  #include <linux/io.h>  #include <linux/highmem.h> @@ -109,7 +110,7 @@ static unsigned int __initdata maxnodemem_pfn[MAX_NUMNODES] = {  };  static nodemask_t __initdata isolnodes; -#ifdef CONFIG_PCI +#if defined(CONFIG_PCI) && !defined(__tilegx__)  enum { DEFAULT_PCI_RESERVE_MB = 64 };  static unsigned int __initdata pci_reserve_mb = DEFAULT_PCI_RESERVE_MB;  unsigned long __initdata pci_reserve_start_pfn = -1U; @@ -160,7 +161,7 @@ static int __init setup_isolnodes(char *str)  }  early_param("isolnodes", setup_isolnodes); -#ifdef CONFIG_PCI +#if defined(CONFIG_PCI) && !defined(__tilegx__)  static int __init setup_pci_reserve(char* str)  {  	unsigned long mb; @@ -171,7 +172,7 @@ static int __init setup_pci_reserve(char* str)  	pci_reserve_mb = mb;  	pr_info("Reserving %dMB for PCIE root complex mappings\n", -	       pci_reserve_mb); +		pci_reserve_mb);  	return 0;  }  early_param("pci_reserve", setup_pci_reserve); @@ -411,7 +412,7 @@ static void __init setup_memory(void)  			continue;  		}  #endif -#ifdef CONFIG_PCI +#if defined(CONFIG_PCI) && !defined(__tilegx__)  		/*  		 * Blocks that overlap the pci reserved region must  		 * have enough space to hold the maximum percpu data @@ -604,11 +605,9 @@ static void __init setup_bootmem_allocator_node(int i)  	/* Free all the space back into the allocator. */  	free_bootmem(PFN_PHYS(start), PFN_PHYS(end - start)); -#if defined(CONFIG_PCI) +#if defined(CONFIG_PCI) && !defined(__tilegx__)  	/* -	 * Throw away any memory aliased by the PCI region.  FIXME: this -	 * is a temporary hack to work around bug 10502, and needs to be -	 * fixed properly. +	 * Throw away any memory aliased by the PCI region.  	 */  	if (pci_reserve_start_pfn < end && pci_reserve_end_pfn > start)  		reserve_bootmem(PFN_PHYS(pci_reserve_start_pfn), @@ -658,6 +657,8 @@ static void __init zone_sizes_init(void)  	unsigned long zones_size[MAX_NR_ZONES] = { 0 };  	int size = percpu_size();  	int num_cpus = smp_height * smp_width; +	const unsigned long dma_end = (1UL << (32 - PAGE_SHIFT)); +  	int i;  	for (i = 0; i < num_cpus; ++i) @@ -729,6 +730,14 @@ static void __init zone_sizes_init(void)  		zones_size[ZONE_NORMAL] = end - start;  #endif +		if (start < dma_end) { +			zones_size[ZONE_DMA] = min(zones_size[ZONE_NORMAL], +						   dma_end - start); +			zones_size[ZONE_NORMAL] -= zones_size[ZONE_DMA]; +		} else { +			zones_size[ZONE_DMA] = 0; +		} +  		/* Take zone metadata from controller 0 if we're isolnode. */  		if (node_isset(i, isolnodes))  			NODE_DATA(i)->bdata = &bootmem_node_data[0]; @@ -738,7 +747,7 @@ static void __init zone_sizes_init(void)  		       PFN_UP(node_percpu[i]));  		/* Track the type of memory on each node */ -		if (zones_size[ZONE_NORMAL]) +		if (zones_size[ZONE_NORMAL] || zones_size[ZONE_DMA])  			node_set_state(i, N_NORMAL_MEMORY);  #ifdef CONFIG_HIGHMEM  		if (end != start) @@ -1343,7 +1352,7 @@ void __init setup_arch(char **cmdline_p)  	setup_cpu_maps(); -#ifdef CONFIG_PCI +#if defined(CONFIG_PCI) && !defined(__tilegx__)  	/*  	 * Initialize the PCI structures.  This is done before memory  	 * setup so that we know whether or not a pci_reserve region @@ -1372,6 +1381,10 @@ void __init setup_arch(char **cmdline_p)  	 * any memory using the bootmem allocator.  	 */ +#ifdef CONFIG_SWIOTLB +	swiotlb_init(0); +#endif +  	paging_init();  	setup_numa_mapping();  	zone_sizes_init(); @@ -1522,11 +1535,10 @@ static struct resource code_resource = {  };  /* - * We reserve all resources above 4GB so that PCI won't try to put - * mappings above 4GB; the standard allows that for some devices but - * the probing code trunates values to 32 bits. + * On Pro, we reserve all resources above 4GB so that PCI won't try to put + * mappings above 4GB.   */ -#ifdef CONFIG_PCI +#if defined(CONFIG_PCI) && !defined(__tilegx__)  static struct resource* __init  insert_non_bus_resource(void)  { @@ -1571,8 +1583,7 @@ static int __init request_standard_resources(void)  	int i;  	enum { CODE_DELTA = MEM_SV_INTRPT - PAGE_OFFSET }; -	iomem_resource.end = -1LL; -#ifdef CONFIG_PCI +#if defined(CONFIG_PCI) && !defined(__tilegx__)  	insert_non_bus_resource();  #endif @@ -1580,7 +1591,7 @@ static int __init request_standard_resources(void)  		u64 start_pfn = node_start_pfn[i];  		u64 end_pfn = node_end_pfn[i]; -#ifdef CONFIG_PCI +#if defined(CONFIG_PCI) && !defined(__tilegx__)  		if (start_pfn <= pci_reserve_start_pfn &&  		    end_pfn > pci_reserve_start_pfn) {  			if (end_pfn > pci_reserve_end_pfn) diff --git a/arch/tile/kernel/usb.c b/arch/tile/kernel/usb.c new file mode 100644 index 00000000000..5af8debc6a7 --- /dev/null +++ b/arch/tile/kernel/usb.c @@ -0,0 +1,69 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + * + * Register the Tile-Gx USB interfaces as platform devices. + * + * The actual USB driver is just some glue (in + * drivers/usb/host/[eo]hci-tilegx.c) which makes the registers available + * to the standard kernel EHCI and OHCI drivers. + */ + +#include <linux/dma-mapping.h> +#include <linux/platform_device.h> +#include <linux/usb/tilegx.h> +#include <linux/types.h> + +static u64 ehci_dmamask = DMA_BIT_MASK(32); + +#define USB_HOST_DEF(unit, type, dmamask) \ +	static struct \ +	    tilegx_usb_platform_data tilegx_usb_platform_data_ ## type ## \ +		hci ## unit = { \ +		.dev_index = unit, \ +	}; \ +	\ +	static struct platform_device tilegx_usb_ ## type ## hci ## unit = { \ +		.name		= "tilegx-" #type "hci", \ +		.id		= unit, \ +		.dev = { \ +			.dma_mask		= dmamask, \ +			.coherent_dma_mask	= DMA_BIT_MASK(32), \ +			.platform_data = \ +				&tilegx_usb_platform_data_ ## type ## hci ## \ +				unit, \ +		}, \ +	}; + +USB_HOST_DEF(0, e, &ehci_dmamask) +USB_HOST_DEF(0, o, NULL) +USB_HOST_DEF(1, e, &ehci_dmamask) +USB_HOST_DEF(1, o, NULL) + +#undef USB_HOST_DEF + +static struct platform_device *tilegx_usb_devices[] __initdata = { +	&tilegx_usb_ehci0, +	&tilegx_usb_ehci1, +	&tilegx_usb_ohci0, +	&tilegx_usb_ohci1, +}; + +/** Add our set of possible USB devices. */ +static int __init tilegx_usb_init(void) +{ +	platform_add_devices(tilegx_usb_devices, +			     ARRAY_SIZE(tilegx_usb_devices)); + +	return 0; +} +arch_initcall(tilegx_usb_init); diff --git a/arch/tile/lib/checksum.c b/arch/tile/lib/checksum.c index e4bab5bd3f3..c3ca3e64d9d 100644 --- a/arch/tile/lib/checksum.c +++ b/arch/tile/lib/checksum.c @@ -16,19 +16,6 @@  #include <net/checksum.h>  #include <linux/module.h> -static inline unsigned int longto16(unsigned long x) -{ -	unsigned long ret; -#ifdef __tilegx__ -	ret = __insn_v2sadu(x, 0); -	ret = __insn_v2sadu(ret, 0); -#else -	ret = __insn_sadh_u(x, 0); -	ret = __insn_sadh_u(ret, 0); -#endif -	return ret; -} -  __wsum do_csum(const unsigned char *buff, int len)  {  	int odd, count; @@ -94,7 +81,7 @@ __wsum do_csum(const unsigned char *buff, int len)  	}  	if (len & 1)  		result += *buff; -	result = longto16(result); +	result = csum_long(result);  	if (odd)  		result = swab16(result);  out: diff --git a/arch/tile/mm/homecache.c b/arch/tile/mm/homecache.c index dbcbdf7b8aa..5f7868dcd6d 100644 --- a/arch/tile/mm/homecache.c +++ b/arch/tile/mm/homecache.c @@ -64,10 +64,6 @@ early_param("noallocl2", set_noallocl2);  #endif -/* Provide no-op versions of these routines to keep flush_remote() cleaner. */ -#define mark_caches_evicted_start() 0 -#define mark_caches_evicted_finish(mask, timestamp) do {} while (0) -  /*   * Update the irq_stat for cpus that we are going to interrupt @@ -107,7 +103,6 @@ static void hv_flush_update(const struct cpumask *cache_cpumask,   *    there's never any good reason for hv_flush_remote() to fail.   *  - Accepts a 32-bit PFN rather than a 64-bit PA, which generally   *    is the type that Linux wants to pass around anyway. - *  - Centralizes the mark_caches_evicted() handling.   *  - Canonicalizes that lengths of zero make cpumasks NULL.   *  - Handles deferring TLB flushes for dataplane tiles.   *  - Tracks remote interrupts in the per-cpu irq_cpustat_t. @@ -126,7 +121,6 @@ void flush_remote(unsigned long cache_pfn, unsigned long cache_control,  		  HV_Remote_ASID *asids, int asidcount)  {  	int rc; -	int timestamp = 0;  /* happy compiler */  	struct cpumask cache_cpumask_copy, tlb_cpumask_copy;  	struct cpumask *cache_cpumask, *tlb_cpumask;  	HV_PhysAddr cache_pa; @@ -157,15 +151,11 @@ void flush_remote(unsigned long cache_pfn, unsigned long cache_control,  	hv_flush_update(cache_cpumask, tlb_cpumask, tlb_va, tlb_length,  			asids, asidcount);  	cache_pa = (HV_PhysAddr)cache_pfn << PAGE_SHIFT; -	if (cache_control & HV_FLUSH_EVICT_L2) -		timestamp = mark_caches_evicted_start();  	rc = hv_flush_remote(cache_pa, cache_control,  			     cpumask_bits(cache_cpumask),  			     tlb_va, tlb_length, tlb_pgsize,  			     cpumask_bits(tlb_cpumask),  			     asids, asidcount); -	if (cache_control & HV_FLUSH_EVICT_L2) -		mark_caches_evicted_finish(cache_cpumask, timestamp);  	if (rc == 0)  		return;  	cpumask_scnprintf(cache_buf, sizeof(cache_buf), &cache_cpumask_copy); @@ -180,85 +170,86 @@ void flush_remote(unsigned long cache_pfn, unsigned long cache_control,  	panic("Unsafe to continue.");  } -void flush_remote_page(struct page *page, int order) +static void homecache_finv_page_va(void* va, int home)  { -	int i, pages = (1 << order); -	for (i = 0; i < pages; ++i, ++page) { -		void *p = kmap_atomic(page); -		int hfh = 0; -		int home = page_home(page); -#if CHIP_HAS_CBOX_HOME_MAP() -		if (home == PAGE_HOME_HASH) -			hfh = 1; -		else -#endif -			BUG_ON(home < 0 || home >= NR_CPUS); -		finv_buffer_remote(p, PAGE_SIZE, hfh); -		kunmap_atomic(p); +	if (home == smp_processor_id()) { +		finv_buffer_local(va, PAGE_SIZE); +	} else if (home == PAGE_HOME_HASH) { +		finv_buffer_remote(va, PAGE_SIZE, 1); +	} else { +		BUG_ON(home < 0 || home >= NR_CPUS); +		finv_buffer_remote(va, PAGE_SIZE, 0);  	}  } -void homecache_evict(const struct cpumask *mask) +void homecache_finv_map_page(struct page *page, int home)  { -	flush_remote(0, HV_FLUSH_EVICT_L2, mask, 0, 0, 0, NULL, NULL, 0); +	unsigned long flags; +	unsigned long va; +	pte_t *ptep; +	pte_t pte; + +	if (home == PAGE_HOME_UNCACHED) +		return; +	local_irq_save(flags); +#ifdef CONFIG_HIGHMEM +	va = __fix_to_virt(FIX_KMAP_BEGIN + kmap_atomic_idx_push() + +			   (KM_TYPE_NR * smp_processor_id())); +#else +	va = __fix_to_virt(FIX_HOMECACHE_BEGIN + smp_processor_id()); +#endif +	ptep = virt_to_pte(NULL, (unsigned long)va); +	pte = pfn_pte(page_to_pfn(page), PAGE_KERNEL); +	__set_pte(ptep, pte_set_home(pte, home)); +	homecache_finv_page_va((void *)va, home); +	__pte_clear(ptep); +	hv_flush_page(va, PAGE_SIZE); +#ifdef CONFIG_HIGHMEM +	kmap_atomic_idx_pop(); +#endif +	local_irq_restore(flags);  } -/* - * Return a mask of the cpus whose caches currently own these pages. - * The return value is whether the pages are all coherently cached - * (i.e. none are immutable, incoherent, or uncached). - */ -static int homecache_mask(struct page *page, int pages, -			  struct cpumask *home_mask) +static void homecache_finv_page_home(struct page *page, int home)  { -	int i; -	int cached_coherently = 1; -	cpumask_clear(home_mask); -	for (i = 0; i < pages; ++i) { -		int home = page_home(&page[i]); -		if (home == PAGE_HOME_IMMUTABLE || -		    home == PAGE_HOME_INCOHERENT) { -			cpumask_copy(home_mask, cpu_possible_mask); -			return 0; -		} -#if CHIP_HAS_CBOX_HOME_MAP() -		if (home == PAGE_HOME_HASH) { -			cpumask_or(home_mask, home_mask, &hash_for_home_map); -			continue; -		} -#endif -		if (home == PAGE_HOME_UNCACHED) { -			cached_coherently = 0; -			continue; -		} -		BUG_ON(home < 0 || home >= NR_CPUS); -		cpumask_set_cpu(home, home_mask); -	} -	return cached_coherently; +	if (!PageHighMem(page) && home == page_home(page)) +		homecache_finv_page_va(page_address(page), home); +	else +		homecache_finv_map_page(page, home);  } -/* - * Return the passed length, or zero if it's long enough that we - * believe we should evict the whole L2 cache. - */ -static unsigned long cache_flush_length(unsigned long length) +static inline bool incoherent_home(int home)  { -	return (length >= CHIP_L2_CACHE_SIZE()) ? HV_FLUSH_EVICT_L2 : length; +	return home == PAGE_HOME_IMMUTABLE || home == PAGE_HOME_INCOHERENT;  } -/* Flush a page out of whatever cache(s) it is in. */ -void homecache_flush_cache(struct page *page, int order) +static void homecache_finv_page_internal(struct page *page, int force_map)  { -	int pages = 1 << order; -	int length = cache_flush_length(pages * PAGE_SIZE); -	unsigned long pfn = page_to_pfn(page); -	struct cpumask home_mask; +	int home = page_home(page); +	if (home == PAGE_HOME_UNCACHED) +		return; +	if (incoherent_home(home)) { +		int cpu; +		for_each_cpu(cpu, &cpu_cacheable_map) +			homecache_finv_map_page(page, cpu); +	} else if (force_map) { +		/* Force if, e.g., the normal mapping is migrating. */ +		homecache_finv_map_page(page, home); +	} else { +		homecache_finv_page_home(page, home); +	} +	sim_validate_lines_evicted(PFN_PHYS(page_to_pfn(page)), PAGE_SIZE); +} -	homecache_mask(page, pages, &home_mask); -	flush_remote(pfn, length, &home_mask, 0, 0, 0, NULL, NULL, 0); -	sim_validate_lines_evicted(PFN_PHYS(pfn), pages * PAGE_SIZE); +void homecache_finv_page(struct page *page) +{ +	homecache_finv_page_internal(page, 0);  } +void homecache_evict(const struct cpumask *mask) +{ +	flush_remote(0, HV_FLUSH_EVICT_L2, mask, 0, 0, 0, NULL, NULL, 0); +}  /* Report the home corresponding to a given PTE. */  static int pte_to_home(pte_t pte) @@ -441,15 +432,8 @@ struct page *homecache_alloc_pages_node(int nid, gfp_t gfp_mask,  	return page;  } -void homecache_free_pages(unsigned long addr, unsigned int order) +void __homecache_free_pages(struct page *page, unsigned int order)  { -	struct page *page; - -	if (addr == 0) -		return; - -	VM_BUG_ON(!virt_addr_valid((void *)addr)); -	page = virt_to_page((void *)addr);  	if (put_page_testzero(page)) {  		homecache_change_page_home(page, order, initial_page_home());  		if (order == 0) { @@ -460,3 +444,13 @@ void homecache_free_pages(unsigned long addr, unsigned int order)  		}  	}  } +EXPORT_SYMBOL(__homecache_free_pages); + +void homecache_free_pages(unsigned long addr, unsigned int order) +{ +	if (addr != 0) { +		VM_BUG_ON(!virt_addr_valid((void *)addr)); +		__homecache_free_pages(virt_to_page((void *)addr), order); +	} +} +EXPORT_SYMBOL(homecache_free_pages); diff --git a/arch/tile/mm/init.c b/arch/tile/mm/init.c index 630dd2ce2af..ef29d6c5e10 100644 --- a/arch/tile/mm/init.c +++ b/arch/tile/mm/init.c @@ -150,7 +150,21 @@ void __init shatter_pmd(pmd_t *pmd)  	assign_pte(pmd, pte);  } -#ifdef CONFIG_HIGHMEM +#ifdef __tilegx__ +static pmd_t *__init get_pmd(pgd_t pgtables[], unsigned long va) +{ +	pud_t *pud = pud_offset(&pgtables[pgd_index(va)], va); +	if (pud_none(*pud)) +		assign_pmd(pud, alloc_pmd()); +	return pmd_offset(pud, va); +} +#else +static pmd_t *__init get_pmd(pgd_t pgtables[], unsigned long va) +{ +	return pmd_offset(pud_offset(&pgtables[pgd_index(va)], va), va); +} +#endif +  /*   * This function initializes a certain range of kernel virtual memory   * with new bootmem page tables, everywhere page tables are missing in @@ -163,24 +177,17 @@ void __init shatter_pmd(pmd_t *pmd)   * checking the pgd every time.   */  static void __init page_table_range_init(unsigned long start, -					 unsigned long end, pgd_t *pgd_base) +					 unsigned long end, pgd_t *pgd)  { -	pgd_t *pgd; -	int pgd_idx;  	unsigned long vaddr; - -	vaddr = start; -	pgd_idx = pgd_index(vaddr); -	pgd = pgd_base + pgd_idx; - -	for ( ; (pgd_idx < PTRS_PER_PGD) && (vaddr != end); pgd++, pgd_idx++) { -		pmd_t *pmd = pmd_offset(pud_offset(pgd, vaddr), vaddr); +	start = round_down(start, PMD_SIZE); +	end = round_up(end, PMD_SIZE); +	for (vaddr = start; vaddr < end; vaddr += PMD_SIZE) { +		pmd_t *pmd = get_pmd(pgd, vaddr);  		if (pmd_none(*pmd))  			assign_pte(pmd, alloc_pte()); -		vaddr += PMD_SIZE;  	}  } -#endif /* CONFIG_HIGHMEM */  #if CHIP_HAS_CBOX_HOME_MAP() @@ -404,21 +411,6 @@ static inline pgprot_t ktext_set_nocache(pgprot_t prot)  	return prot;  } -#ifndef __tilegx__ -static pmd_t *__init get_pmd(pgd_t pgtables[], unsigned long va) -{ -	return pmd_offset(pud_offset(&pgtables[pgd_index(va)], va), va); -} -#else -static pmd_t *__init get_pmd(pgd_t pgtables[], unsigned long va) -{ -	pud_t *pud = pud_offset(&pgtables[pgd_index(va)], va); -	if (pud_none(*pud)) -		assign_pmd(pud, alloc_pmd()); -	return pmd_offset(pud, va); -} -#endif -  /* Temporary page table we use for staging. */  static pgd_t pgtables[PTRS_PER_PGD]   __attribute__((aligned(HV_PAGE_TABLE_ALIGN))); @@ -741,16 +733,15 @@ static void __init set_non_bootmem_pages_init(void)  	for_each_zone(z) {  		unsigned long start, end;  		int nid = z->zone_pgdat->node_id; +#ifdef CONFIG_HIGHMEM  		int idx = zone_idx(z); +#endif  		start = z->zone_start_pfn; -		if (start == 0) -			continue;  /* bootmem */  		end = start + z->spanned_pages; -		if (idx == ZONE_NORMAL) { -			BUG_ON(start != node_start_pfn[nid]); -			start = node_free_pfn[nid]; -		} +		start = max(start, node_free_pfn[nid]); +		start = max(start, max_low_pfn); +  #ifdef CONFIG_HIGHMEM  		if (idx == ZONE_HIGHMEM)  			totalhigh_pages += z->spanned_pages; @@ -779,9 +770,6 @@ static void __init set_non_bootmem_pages_init(void)   */  void __init paging_init(void)  { -#ifdef CONFIG_HIGHMEM -	unsigned long vaddr, end; -#endif  #ifdef __tilegx__  	pud_t *pud;  #endif @@ -789,14 +777,14 @@ void __init paging_init(void)  	kernel_physical_mapping_init(pgd_base); -#ifdef CONFIG_HIGHMEM  	/*  	 * Fixed mappings, only the page table structure has to be  	 * created - mappings will be set by set_fixmap():  	 */ -	vaddr = __fix_to_virt(__end_of_fixed_addresses - 1) & PMD_MASK; -	end = (FIXADDR_TOP + PMD_SIZE - 1) & PMD_MASK; -	page_table_range_init(vaddr, end, pgd_base); +	page_table_range_init(fix_to_virt(__end_of_fixed_addresses - 1), +			      FIXADDR_TOP, pgd_base); + +#ifdef CONFIG_HIGHMEM  	permanent_kmaps_init(pgd_base);  #endif diff --git a/arch/tile/mm/pgtable.c b/arch/tile/mm/pgtable.c index 345edfed9fc..de0de0c0e8a 100644 --- a/arch/tile/mm/pgtable.c +++ b/arch/tile/mm/pgtable.c @@ -575,13 +575,6 @@ void __iomem *ioremap_prot(resource_size_t phys_addr, unsigned long size,  }  EXPORT_SYMBOL(ioremap_prot); -/* Map a PCI MMIO bus address into VA space. */ -void __iomem *ioremap(resource_size_t phys_addr, unsigned long size) -{ -	panic("ioremap for PCI MMIO is not supported"); -} -EXPORT_SYMBOL(ioremap); -  /* Unmap an MMIO VA mapping. */  void iounmap(volatile void __iomem *addr_in)  { diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 2a752167754..d41d4c52296 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2143,9 +2143,9 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82865_HB,  DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82875_HB,  			quirk_unhide_mch_dev6); -#ifdef CONFIG_TILE +#ifdef CONFIG_TILEPRO  /* - * The Tilera TILEmpower platform needs to set the link speed + * The Tilera TILEmpower tilepro platform needs to set the link speed   * to 2.5GT(Giga-Transfers)/s (Gen 1). The default link speed   * setting is 5GT/s (Gen 2). 0x98 is the Link Control2 PCIe   * capability register of the PEX8624 PCIe switch. The switch @@ -2160,7 +2160,7 @@ static void __devinit quirk_tile_plx_gen1(struct pci_dev *dev)  	}  }  DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_PLX, 0x8624, quirk_tile_plx_gen1); -#endif /* CONFIG_TILE */ +#endif /* CONFIG_TILEPRO */  #ifdef CONFIG_PCI_MSI  /* Some chipsets do not support MSI. We cannot easily rely on setting diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 800be38c78b..1d9401e0990 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1349,6 +1349,11 @@ MODULE_LICENSE ("GPL");  #define PLATFORM_DRIVER		ehci_msm_driver  #endif +#ifdef CONFIG_TILE_USB +#include "ehci-tilegx.c" +#define	PLATFORM_DRIVER		ehci_hcd_tilegx_driver +#endif +  #ifdef CONFIG_USB_EHCI_HCD_PMC_MSP  #include "ehci-pmcmsp.c"  #define	PLATFORM_DRIVER		ehci_hcd_msp_driver diff --git a/drivers/usb/host/ehci-tilegx.c b/drivers/usb/host/ehci-tilegx.c new file mode 100644 index 00000000000..1d215cdb9de --- /dev/null +++ b/drivers/usb/host/ehci-tilegx.c @@ -0,0 +1,214 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ + +/* + * Tilera TILE-Gx USB EHCI host controller driver. + */ + +#include <linux/irq.h> +#include <linux/platform_device.h> +#include <linux/usb/tilegx.h> +#include <linux/usb.h> + +#include <asm/homecache.h> + +#include <gxio/iorpc_usb_host.h> +#include <gxio/usb_host.h> + +static void tilegx_start_ehc(void) +{ +} + +static void tilegx_stop_ehc(void) +{ +} + +static int tilegx_ehci_setup(struct usb_hcd *hcd) +{ +	int ret = ehci_init(hcd); + +	/* +	 * Some drivers do: +	 * +	 *   struct ehci_hcd *ehci = hcd_to_ehci(hcd); +	 *   ehci->need_io_watchdog = 0; +	 * +	 * here, but since this is a new driver we're going to leave the +	 * watchdog enabled.  Later we may try to turn it off and see +	 * whether we run into any problems. +	 */ + +	return ret; +} + +static const struct hc_driver ehci_tilegx_hc_driver = { +	.description		= hcd_name, +	.product_desc		= "Tile-Gx EHCI", +	.hcd_priv_size		= sizeof(struct ehci_hcd), + +	/* +	 * Generic hardware linkage. +	 */ +	.irq			= ehci_irq, +	.flags			= HCD_MEMORY | HCD_USB2, + +	/* +	 * Basic lifecycle operations. +	 */ +	.reset			= tilegx_ehci_setup, +	.start			= ehci_run, +	.stop			= ehci_stop, +	.shutdown		= ehci_shutdown, + +	/* +	 * Managing I/O requests and associated device resources. +	 */ +	.urb_enqueue		= ehci_urb_enqueue, +	.urb_dequeue		= ehci_urb_dequeue, +	.endpoint_disable	= ehci_endpoint_disable, +	.endpoint_reset		= ehci_endpoint_reset, + +	/* +	 * Scheduling support. +	 */ +	.get_frame_number	= ehci_get_frame, + +	/* +	 * Root hub support. +	 */ +	.hub_status_data	= ehci_hub_status_data, +	.hub_control		= ehci_hub_control, +	.bus_suspend		= ehci_bus_suspend, +	.bus_resume		= ehci_bus_resume, +	.relinquish_port	= ehci_relinquish_port, +	.port_handed_over	= ehci_port_handed_over, + +	.clear_tt_buffer_complete	= ehci_clear_tt_buffer_complete, +}; + +static int ehci_hcd_tilegx_drv_probe(struct platform_device *pdev) +{ +	struct usb_hcd *hcd; +	struct ehci_hcd *ehci; +	struct tilegx_usb_platform_data *pdata = pdev->dev.platform_data; +	pte_t pte = { 0 }; +	int my_cpu = smp_processor_id(); +	int ret; + +	if (usb_disabled()) +		return -ENODEV; + +	/* +	 * Try to initialize our GXIO context; if we can't, the device +	 * doesn't exist. +	 */ +	if (gxio_usb_host_init(&pdata->usb_ctx, pdata->dev_index, 1) != 0) +		return -ENXIO; + +	hcd = usb_create_hcd(&ehci_tilegx_hc_driver, &pdev->dev, +			     dev_name(&pdev->dev)); +	if (!hcd) +		return -ENOMEM; + +	/* +	 * We don't use rsrc_start to map in our registers, but seems like +	 * we ought to set it to something, so we use the register VA. +	 */ +	hcd->rsrc_start = +		(ulong) gxio_usb_host_get_reg_start(&pdata->usb_ctx); +	hcd->rsrc_len = gxio_usb_host_get_reg_len(&pdata->usb_ctx); +	hcd->regs = gxio_usb_host_get_reg_start(&pdata->usb_ctx); + +	tilegx_start_ehc(); + +	ehci = hcd_to_ehci(hcd); +	ehci->caps = hcd->regs; +	ehci->regs = +		hcd->regs + HC_LENGTH(ehci, readl(&ehci->caps->hc_capbase)); +	/* cache this readonly data; minimize chip reads */ +	ehci->hcs_params = readl(&ehci->caps->hcs_params); + +	/* Create our IRQs and register them. */ +	pdata->irq = create_irq(); +	if (pdata->irq < 0) { +		ret = -ENXIO; +		goto err_no_irq; +	} + +	tile_irq_activate(pdata->irq, TILE_IRQ_PERCPU); + +	/* Configure interrupts. */ +	ret = gxio_usb_host_cfg_interrupt(&pdata->usb_ctx, +					  cpu_x(my_cpu), cpu_y(my_cpu), +					  KERNEL_PL, pdata->irq); +	if (ret) { +		ret = -ENXIO; +		goto err_have_irq; +	} + +	/* Register all of our memory. */ +	pte = pte_set_home(pte, PAGE_HOME_HASH); +	ret = gxio_usb_host_register_client_memory(&pdata->usb_ctx, pte, 0); +	if (ret) { +		ret = -ENXIO; +		goto err_have_irq; +	} + +	ret = usb_add_hcd(hcd, pdata->irq, IRQF_SHARED); +	if (ret == 0) { +		platform_set_drvdata(pdev, hcd); +		return ret; +	} + +err_have_irq: +	destroy_irq(pdata->irq); +err_no_irq: +	tilegx_stop_ehc(); +	usb_put_hcd(hcd); +	gxio_usb_host_destroy(&pdata->usb_ctx); +	return ret; +} + +static int ehci_hcd_tilegx_drv_remove(struct platform_device *pdev) +{ +	struct usb_hcd *hcd = platform_get_drvdata(pdev); +	struct tilegx_usb_platform_data *pdata = pdev->dev.platform_data; + +	usb_remove_hcd(hcd); +	usb_put_hcd(hcd); +	tilegx_stop_ehc(); +	gxio_usb_host_destroy(&pdata->usb_ctx); +	destroy_irq(pdata->irq); +	platform_set_drvdata(pdev, NULL); + +	return 0; +} + +static void ehci_hcd_tilegx_drv_shutdown(struct platform_device *pdev) +{ +	usb_hcd_platform_shutdown(pdev); +	ehci_hcd_tilegx_drv_remove(pdev); +} + +static struct platform_driver ehci_hcd_tilegx_driver = { +	.probe		= ehci_hcd_tilegx_drv_probe, +	.remove		= ehci_hcd_tilegx_drv_remove, +	.shutdown	= ehci_hcd_tilegx_drv_shutdown, +	.driver = { +		.name	= "tilegx-ehci", +		.owner	= THIS_MODULE, +	} +}; + +MODULE_ALIAS("platform:tilegx-ehci"); diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index e0adf5c0cf5..2b1e8d84c87 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -1100,6 +1100,11 @@ MODULE_LICENSE ("GPL");  #define PLATFORM_DRIVER		ohci_octeon_driver  #endif +#ifdef CONFIG_TILE_USB +#include "ohci-tilegx.c" +#define PLATFORM_DRIVER		ohci_hcd_tilegx_driver +#endif +  #ifdef CONFIG_USB_CNS3XXX_OHCI  #include "ohci-cns3xxx.c"  #define PLATFORM_DRIVER		ohci_hcd_cns3xxx_driver diff --git a/drivers/usb/host/ohci-tilegx.c b/drivers/usb/host/ohci-tilegx.c new file mode 100644 index 00000000000..1ae7b28a71c --- /dev/null +++ b/drivers/usb/host/ohci-tilegx.c @@ -0,0 +1,203 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ + +/* + * Tilera TILE-Gx USB OHCI host controller driver. + */ + +#include <linux/irq.h> +#include <linux/platform_device.h> +#include <linux/usb/tilegx.h> +#include <linux/usb.h> + +#include <asm/homecache.h> + +#include <gxio/iorpc_usb_host.h> +#include <gxio/usb_host.h> + +static void tilegx_start_ohc(void) +{ +} + +static void tilegx_stop_ohc(void) +{ +} + +static int tilegx_ohci_start(struct usb_hcd *hcd) +{ +	struct ohci_hcd *ohci = hcd_to_ohci(hcd); +	int ret; + +	ret = ohci_init(ohci); +	if (ret < 0) +		return ret; + +	ret = ohci_run(ohci); +	if (ret < 0) { +		dev_err(hcd->self.controller, "can't start %s\n", +			hcd->self.bus_name); +		ohci_stop(hcd); +		return ret; +	} + +	return 0; +} + +static const struct hc_driver ohci_tilegx_hc_driver = { +	.description		= hcd_name, +	.product_desc		= "Tile-Gx OHCI", +	.hcd_priv_size		= sizeof(struct ohci_hcd), + +	/* +	 * Generic hardware linkage. +	 */ +	.irq			= ohci_irq, +	.flags			= HCD_MEMORY | HCD_LOCAL_MEM | HCD_USB11, + +	/* +	 * Basic lifecycle operations. +	 */ +	.start			= tilegx_ohci_start, +	.stop			= ohci_stop, +	.shutdown		= ohci_shutdown, + +	/* +	 * Managing I/O requests and associated device resources. +	 */ +	.urb_enqueue		= ohci_urb_enqueue, +	.urb_dequeue		= ohci_urb_dequeue, +	.endpoint_disable	= ohci_endpoint_disable, + +	/* +	 * Scheduling support. +	 */ +	.get_frame_number	= ohci_get_frame, + +	/* +	 * Root hub support. +	 */ +	.hub_status_data	= ohci_hub_status_data, +	.hub_control		= ohci_hub_control, +	.start_port_reset	= ohci_start_port_reset, +}; + +static int ohci_hcd_tilegx_drv_probe(struct platform_device *pdev) +{ +	struct usb_hcd *hcd; +	struct tilegx_usb_platform_data *pdata = pdev->dev.platform_data; +	pte_t pte = { 0 }; +	int my_cpu = smp_processor_id(); +	int ret; + +	if (usb_disabled()) +		return -ENODEV; + +	/* +	 * Try to initialize our GXIO context; if we can't, the device +	 * doesn't exist. +	 */ +	if (gxio_usb_host_init(&pdata->usb_ctx, pdata->dev_index, 0) != 0) +		return -ENXIO; + +	hcd = usb_create_hcd(&ohci_tilegx_hc_driver, &pdev->dev, +			     dev_name(&pdev->dev)); +	if (!hcd) +		return -ENOMEM; + +	/* +	 * We don't use rsrc_start to map in our registers, but seems like +	 * we ought to set it to something, so we use the register VA. +	 */ +	hcd->rsrc_start = +		(ulong) gxio_usb_host_get_reg_start(&pdata->usb_ctx); +	hcd->rsrc_len = gxio_usb_host_get_reg_len(&pdata->usb_ctx); +	hcd->regs = gxio_usb_host_get_reg_start(&pdata->usb_ctx); + +	tilegx_start_ohc(); + +	/* Create our IRQs and register them. */ +	pdata->irq = create_irq(); +	if (pdata->irq < 0) { +		ret = -ENXIO; +		goto err_no_irq; +	} + +	tile_irq_activate(pdata->irq, TILE_IRQ_PERCPU); + +	/* Configure interrupts. */ +	ret = gxio_usb_host_cfg_interrupt(&pdata->usb_ctx, +					  cpu_x(my_cpu), cpu_y(my_cpu), +					  KERNEL_PL, pdata->irq); +	if (ret) { +		ret = -ENXIO; +		goto err_have_irq; +	} + +	/* Register all of our memory. */ +	pte = pte_set_home(pte, PAGE_HOME_HASH); +	ret = gxio_usb_host_register_client_memory(&pdata->usb_ctx, pte, 0); +	if (ret) { +		ret = -ENXIO; +		goto err_have_irq; +	} + +	ohci_hcd_init(hcd_to_ohci(hcd)); + +	ret = usb_add_hcd(hcd, pdata->irq, IRQF_SHARED); +	if (ret == 0) { +		platform_set_drvdata(pdev, hcd); +		return ret; +	} + +err_have_irq: +	destroy_irq(pdata->irq); +err_no_irq: +	tilegx_stop_ohc(); +	usb_put_hcd(hcd); +	gxio_usb_host_destroy(&pdata->usb_ctx); +	return ret; +} + +static int ohci_hcd_tilegx_drv_remove(struct platform_device *pdev) +{ +	struct usb_hcd *hcd = platform_get_drvdata(pdev); +	struct tilegx_usb_platform_data* pdata = pdev->dev.platform_data; + +	usb_remove_hcd(hcd); +	usb_put_hcd(hcd); +	tilegx_stop_ohc(); +	gxio_usb_host_destroy(&pdata->usb_ctx); +	destroy_irq(pdata->irq); +	platform_set_drvdata(pdev, NULL); + +	return 0; +} + +static void ohci_hcd_tilegx_drv_shutdown(struct platform_device *pdev) +{ +	usb_hcd_platform_shutdown(pdev); +	ohci_hcd_tilegx_drv_remove(pdev); +} + +static struct platform_driver ohci_hcd_tilegx_driver = { +	.probe		= ohci_hcd_tilegx_drv_probe, +	.remove		= ohci_hcd_tilegx_drv_remove, +	.shutdown	= ohci_hcd_tilegx_drv_shutdown, +	.driver = { +		.name	= "tilegx-ohci", +		.owner	= THIS_MODULE, +	} +}; + +MODULE_ALIAS("platform:tilegx-ohci"); diff --git a/include/linux/usb/tilegx.h b/include/linux/usb/tilegx.h new file mode 100644 index 00000000000..2d65e343568 --- /dev/null +++ b/include/linux/usb/tilegx.h @@ -0,0 +1,34 @@ +/* + * Copyright 2012 Tilera Corporation. All Rights Reserved. + * + *   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, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + * + * Structure to contain platform-specific data related to Tile-Gx USB + * controllers. + */ + +#ifndef _LINUX_USB_TILEGX_H +#define _LINUX_USB_TILEGX_H + +#include <gxio/usb_host.h> + +struct tilegx_usb_platform_data { +	/* GXIO device index. */ +	int dev_index; + +	/* GXIO device context. */ +	gxio_usb_host_context_t usb_ctx; + +	/* Device IRQ. */ +	unsigned int irq; +}; + +#endif /* _LINUX_USB_TILEGX_H */ diff --git a/mm/bounce.c b/mm/bounce.c index d1be02ca188..04208677556 100644 --- a/mm/bounce.c +++ b/mm/bounce.c @@ -24,23 +24,25 @@  static mempool_t *page_pool, *isa_page_pool; -#ifdef CONFIG_HIGHMEM +#if defined(CONFIG_HIGHMEM) || defined(CONFIG_NEED_BOUNCE_POOL)  static __init int init_emergency_pool(void)  { -#ifndef CONFIG_MEMORY_HOTPLUG +#if defined(CONFIG_HIGHMEM) && !defined(CONFIG_MEMORY_HOTPLUG)  	if (max_pfn <= max_low_pfn)  		return 0;  #endif  	page_pool = mempool_create_page_pool(POOL_SIZE, 0);  	BUG_ON(!page_pool); -	printk("highmem bounce pool size: %d pages\n", POOL_SIZE); +	printk("bounce pool size: %d pages\n", POOL_SIZE);  	return 0;  }  __initcall(init_emergency_pool); +#endif +#ifdef CONFIG_HIGHMEM  /*   * highmem version, map in to vec   */  |