Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e8635b48 authored by David Daney's avatar David Daney Committed by Ralf Baechle
Browse files

MIPS: Add Cavium OCTEON PCI support.



This patch adds support for PCI and PCIe to the base Cavium OCTEON
processor support.

Signed-off-by: default avatarDavid Daney <ddaney@caviumnetworks.com>
Signed-off-by: default avatarRalf Baechle <ralf@linux-mips.org>
parent 8860fb82
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -618,6 +618,8 @@ config CAVIUM_OCTEON_REFERENCE_BOARD
	select SYS_HAS_EARLY_PRINTK
	select SYS_HAS_CPU_CAVIUM_OCTEON
	select SWAP_IO_SPACE
	select HW_HAS_PCI
	select ARCH_SUPPORTS_MSI
	help
	  This option supports all of the Octeon reference boards from Cavium
	  Networks. It builds a kernel that dynamically determines the Octeon
+4 −0
Original line number Diff line number Diff line
@@ -14,5 +14,9 @@ obj-y += dma-octeon.o flash_setup.o
obj-y += octeon-memcpy.o

obj-$(CONFIG_SMP)                     += smp.o
obj-$(CONFIG_PCI)                     += pci-common.o
obj-$(CONFIG_PCI)                     += pci.o
obj-$(CONFIG_PCI)                     += pcie.o
obj-$(CONFIG_PCI_MSI)                 += msi.o

EXTRA_CFLAGS += -Werror
+309 −2
Original line number Diff line number Diff line
@@ -13,20 +13,327 @@
 */
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include <linux/scatterlist.h>

#include <linux/cache.h>
#include <linux/io.h>

#include <asm/octeon/octeon.h>
#include <asm/octeon/cvmx-npi-defs.h>
#include <asm/octeon/cvmx-pci-defs.h>

#include <dma-coherence.h>

#ifdef CONFIG_PCI
#include "pci-common.h"
#endif

#define BAR2_PCI_ADDRESS 0x8000000000ul

struct bar1_index_state {
	int16_t ref_count;	/* Number of PCI mappings using this index */
	uint16_t address_bits;	/* Upper bits of physical address. This is
				   shifted 22 bits */
};

#ifdef CONFIG_PCI
static DEFINE_SPINLOCK(bar1_lock);
static struct bar1_index_state bar1_state[32];
#endif

dma_addr_t octeon_map_dma_mem(struct device *dev, void *ptr, size_t size)
{
#ifndef CONFIG_PCI
	/* Without PCI/PCIe this function can be called for Octeon internal
	   devices such as USB. These devices all support 64bit addressing */
	mb();
	return virt_to_phys(ptr);
#else
	unsigned long flags;
	uint64_t dma_mask;
	int64_t start_index;
	dma_addr_t result = -1;
	uint64_t physical = virt_to_phys(ptr);
	int64_t index;

	mb();
	/*
	 * Use the DMA masks to determine the allowed memory
	 * region. For us it doesn't limit the actual memory, just the
	 * address visible over PCI.  Devices with limits need to use
	 * lower indexed Bar1 entries.
	 */
	if (dev) {
		dma_mask = dev->coherent_dma_mask;
		if (dev->dma_mask)
			dma_mask = *dev->dma_mask;
	} else {
		dma_mask = 0xfffffffful;
	}

	/*
	 * Platform devices, such as the internal USB, skip all
	 * translation and use Octeon physical addresses directly.
	 */
	if (!dev || dev->bus == &platform_bus_type)
		return physical;

	switch (octeon_dma_bar_type) {
	case OCTEON_DMA_BAR_TYPE_PCIE:
		if (unlikely(physical < (16ul << 10)))
			panic("dma_map_single: Not allowed to map first 16KB."
			      " It interferes with BAR0 special area\n");
		else if ((physical + size >= (256ul << 20)) &&
			 (physical < (512ul << 20)))
			panic("dma_map_single: Not allowed to map bootbus\n");
		else if ((physical + size >= 0x400000000ull) &&
			 physical < 0x410000000ull)
			panic("dma_map_single: "
			      "Attempt to map illegal memory address 0x%llx\n",
			      physical);
		else if (physical >= 0x420000000ull)
			panic("dma_map_single: "
			      "Attempt to map illegal memory address 0x%llx\n",
			      physical);
		else if ((physical + size >=
			  (4ull<<30) - (OCTEON_PCI_BAR1_HOLE_SIZE<<20))
			 && physical < (4ull<<30))
			pr_warning("dma_map_single: Warning: "
				   "Mapping memory address that might "
				   "conflict with devices 0x%llx-0x%llx\n",
				   physical, physical+size-1);
		/* The 2nd 256MB is mapped at 256<<20 instead of 0x410000000 */
		if ((physical >= 0x410000000ull) && physical < 0x420000000ull)
			result = physical - 0x400000000ull;
		else
			result = physical;
		if (((result+size-1) & dma_mask) != result+size-1)
			panic("dma_map_single: Attempt to map address "
			      "0x%llx-0x%llx, which can't be accessed "
			      "according to the dma mask 0x%llx\n",
			      physical, physical+size-1, dma_mask);
		goto done;

	case OCTEON_DMA_BAR_TYPE_BIG:
#ifdef CONFIG_64BIT
		/* If the device supports 64bit addressing, then use BAR2 */
		if (dma_mask > BAR2_PCI_ADDRESS) {
			result = physical + BAR2_PCI_ADDRESS;
			goto done;
		}
#endif
		if (unlikely(physical < (4ul << 10))) {
			panic("dma_map_single: Not allowed to map first 4KB. "
			      "It interferes with BAR0 special area\n");
		} else if (physical < (256ul << 20)) {
			if (unlikely(physical + size > (256ul << 20)))
				panic("dma_map_single: Requested memory spans "
				      "Bar0 0:256MB and bootbus\n");
			result = physical;
			goto done;
		} else if (unlikely(physical < (512ul << 20))) {
			panic("dma_map_single: Not allowed to map bootbus\n");
		} else if (physical < (2ul << 30)) {
			if (unlikely(physical + size > (2ul << 30)))
				panic("dma_map_single: Requested memory spans "
				      "Bar0 512MB:2GB and BAR1\n");
			result = physical;
			goto done;
		} else if (physical < (2ul << 30) + (128 << 20)) {
			/* Fall through */
		} else if (physical <
			   (4ul << 30) - (OCTEON_PCI_BAR1_HOLE_SIZE << 20)) {
			if (unlikely
			    (physical + size >
			     (4ul << 30) - (OCTEON_PCI_BAR1_HOLE_SIZE << 20)))
				panic("dma_map_single: Requested memory "
				      "extends past Bar1 (4GB-%luMB)\n",
				      OCTEON_PCI_BAR1_HOLE_SIZE);
			result = physical;
			goto done;
		} else if ((physical >= 0x410000000ull) &&
			   (physical < 0x420000000ull)) {
			if (unlikely(physical + size > 0x420000000ull))
				panic("dma_map_single: Requested memory spans "
				      "non existant memory\n");
			/* BAR0 fixed mapping 256MB:512MB ->
			 * 16GB+256MB:16GB+512MB */
			result = physical - 0x400000000ull;
			goto done;
		} else {
			/* Continued below switch statement */
		}
		break;

	case OCTEON_DMA_BAR_TYPE_SMALL:
#ifdef CONFIG_64BIT
		/* If the device supports 64bit addressing, then use BAR2 */
		if (dma_mask > BAR2_PCI_ADDRESS) {
			result = physical + BAR2_PCI_ADDRESS;
			goto done;
		}
#endif
		/* Continued below switch statement */
		break;

	default:
		panic("dma_map_single: Invalid octeon_dma_bar_type\n");
	}

	/* Don't allow mapping to span multiple Bar entries. The hardware guys
	   won't guarantee that DMA across boards work */
	if (unlikely((physical >> 22) != ((physical + size - 1) >> 22)))
		panic("dma_map_single: "
		      "Requested memory spans more than one Bar1 entry\n");

	if (octeon_dma_bar_type == OCTEON_DMA_BAR_TYPE_BIG)
		start_index = 31;
	else if (unlikely(dma_mask < (1ul << 27)))
		start_index = (dma_mask >> 22);
	else
		start_index = 31;

	/* Only one processor can access the Bar register at once */
	spin_lock_irqsave(&bar1_lock, flags);

	/* Look through Bar1 for existing mapping that will work */
	for (index = start_index; index >= 0; index--) {
		if ((bar1_state[index].address_bits == physical >> 22) &&
		    (bar1_state[index].ref_count)) {
			/* An existing mapping will work, use it */
			bar1_state[index].ref_count++;
			if (unlikely(bar1_state[index].ref_count < 0))
				panic("dma_map_single: "
				      "Bar1[%d] reference count overflowed\n",
				      (int) index);
			result = (index << 22) | (physical & ((1 << 22) - 1));
			/* Large BAR1 is offset at 2GB */
			if (octeon_dma_bar_type == OCTEON_DMA_BAR_TYPE_BIG)
				result += 2ul << 30;
			goto done_unlock;
		}
	}

	/* No existing mappings, look for a free entry */
	for (index = start_index; index >= 0; index--) {
		if (unlikely(bar1_state[index].ref_count == 0)) {
			union cvmx_pci_bar1_indexx bar1_index;
			/* We have a free entry, use it */
			bar1_state[index].ref_count = 1;
			bar1_state[index].address_bits = physical >> 22;
			bar1_index.u32 = 0;
			/* Address bits[35:22] sent to L2C */
			bar1_index.s.addr_idx = physical >> 22;
			/* Don't put PCI accesses in L2. */
			bar1_index.s.ca = 1;
			/* Endian Swap Mode */
			bar1_index.s.end_swp = 1;
			/* Set '1' when the selected address range is valid. */
			bar1_index.s.addr_v = 1;
			octeon_npi_write32(CVMX_NPI_PCI_BAR1_INDEXX(index),
					   bar1_index.u32);
			/* An existing mapping will work, use it */
			result = (index << 22) | (physical & ((1 << 22) - 1));
			/* Large BAR1 is offset at 2GB */
			if (octeon_dma_bar_type == OCTEON_DMA_BAR_TYPE_BIG)
				result += 2ul << 30;
			goto done_unlock;
		}
	}

	pr_err("dma_map_single: "
	       "Can't find empty BAR1 index for physical mapping 0x%llx\n",
	       (unsigned long long) physical);

done_unlock:
	spin_unlock_irqrestore(&bar1_lock, flags);
done:
	pr_debug("dma_map_single 0x%llx->0x%llx\n", physical, result);
	return result;
#endif
}

void octeon_unmap_dma_mem(struct device *dev, dma_addr_t dma_addr)
{
	/* Without PCI/PCIe this function can be called for Octeon internal
	 * devices such as USB. These devices all support 64bit addressing */
#ifndef CONFIG_PCI
	/*
	 * Without PCI/PCIe this function can be called for Octeon internal
	 * devices such as USB. These devices all support 64bit addressing.
	 */
	return;
#else
	unsigned long flags;
	uint64_t index;

	/*
	 * Platform devices, such as the internal USB, skip all
	 * translation and use Octeon physical addresses directly.
	 */
	if (dev->bus == &platform_bus_type)
		return;

	switch (octeon_dma_bar_type) {
	case OCTEON_DMA_BAR_TYPE_PCIE:
		/* Nothing to do, all mappings are static */
		goto done;

	case OCTEON_DMA_BAR_TYPE_BIG:
#ifdef CONFIG_64BIT
		/* Nothing to do for addresses using BAR2 */
		if (dma_addr >= BAR2_PCI_ADDRESS)
			goto done;
#endif
		if (unlikely(dma_addr < (4ul << 10)))
			panic("dma_unmap_single: Unexpect DMA address 0x%llx\n",
			      dma_addr);
		else if (dma_addr < (2ul << 30))
			/* Nothing to do for addresses using BAR0 */
			goto done;
		else if (dma_addr < (2ul << 30) + (128ul << 20))
			/* Need to unmap, fall through */
			index = (dma_addr - (2ul << 30)) >> 22;
		else if (dma_addr <
			 (4ul << 30) - (OCTEON_PCI_BAR1_HOLE_SIZE << 20))
			goto done;	/* Nothing to do for the rest of BAR1 */
		else
			panic("dma_unmap_single: Unexpect DMA address 0x%llx\n",
			      dma_addr);
		/* Continued below switch statement */
		break;

	case OCTEON_DMA_BAR_TYPE_SMALL:
#ifdef CONFIG_64BIT
		/* Nothing to do for addresses using BAR2 */
		if (dma_addr >= BAR2_PCI_ADDRESS)
			goto done;
#endif
		index = dma_addr >> 22;
		/* Continued below switch statement */
		break;

	default:
		panic("dma_unmap_single: Invalid octeon_dma_bar_type\n");
	}

	if (unlikely(index > 31))
		panic("dma_unmap_single: "
		      "Attempt to unmap an invalid address (0x%llx)\n",
		      dma_addr);

	spin_lock_irqsave(&bar1_lock, flags);
	bar1_state[index].ref_count--;
	if (bar1_state[index].ref_count == 0)
		octeon_npi_write32(CVMX_NPI_PCI_BAR1_INDEXX(index), 0);
	else if (unlikely(bar1_state[index].ref_count < 0))
		panic("dma_unmap_single: Bar1[%u] reference count < 0\n",
		      (int) index);
	spin_unlock_irqrestore(&bar1_lock, flags);
done:
	pr_debug("dma_unmap_single 0x%llx\n", dma_addr);
	return;
#endif
}
+1 −0
Original line number Diff line number Diff line
@@ -11,3 +11,4 @@

obj-y += cvmx-bootmem.o cvmx-l2c.o cvmx-sysinfo.o octeon-model.o

obj-$(CONFIG_PCI) += cvmx-helper-errata.o cvmx-helper-jtag.o
+70 −0
Original line number Diff line number Diff line
/***********************license start***************
 * Author: Cavium Networks
 *
 * Contact: support@caviumnetworks.com
 * This file is part of the OCTEON SDK
 *
 * Copyright (c) 2003-2008 Cavium Networks
 *
 * This file is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License, Version 2, as
 * published by the Free Software Foundation.
 *
 * This file is distributed in the hope that it will be useful, but
 * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
 * NONINFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this file; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 * or visit http://www.gnu.org/licenses/.
 *
 * This file may also be available under a different license from Cavium.
 * Contact Cavium Networks for more information
 ***********************license end**************************************/

/**
 *
 * Fixes and workaround for Octeon chip errata. This file
 * contains functions called by cvmx-helper to workaround known
 * chip errata. For the most part, code doesn't need to call
 * these functions directly.
 *
 */
#include <asm/octeon/octeon.h>

#include <asm/octeon/cvmx-helper-jtag.h>

/**
 * Due to errata G-720, the 2nd order CDR circuit on CN52XX pass
 * 1 doesn't work properly. The following code disables 2nd order
 * CDR for the specified QLM.
 *
 * @qlm:    QLM to disable 2nd order CDR for.
 */
void __cvmx_helper_errata_qlm_disable_2nd_order_cdr(int qlm)
{
	int lane;
	cvmx_helper_qlm_jtag_init();
	/* We need to load all four lanes of the QLM, a total of 1072 bits */
	for (lane = 0; lane < 4; lane++) {
		/*
		 * Each lane has 268 bits. We need to set
		 * cfg_cdr_incx<67:64> = 3 and cfg_cdr_secord<77> =
		 * 1. All other bits are zero. Bits go in LSB first,
		 * so start off with the zeros for bits <63:0>.
		 */
		cvmx_helper_qlm_jtag_shift_zeros(qlm, 63 - 0 + 1);
		/* cfg_cdr_incx<67:64>=3 */
		cvmx_helper_qlm_jtag_shift(qlm, 67 - 64 + 1, 3);
		/* Zeros for bits <76:68> */
		cvmx_helper_qlm_jtag_shift_zeros(qlm, 76 - 68 + 1);
		/* cfg_cdr_secord<77>=1 */
		cvmx_helper_qlm_jtag_shift(qlm, 77 - 77 + 1, 1);
		/* Zeros for bits <267:78> */
		cvmx_helper_qlm_jtag_shift_zeros(qlm, 267 - 78 + 1);
	}
	cvmx_helper_qlm_jtag_update(qlm);
}
Loading