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

Commit ccfdd7ae authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

Merge tag 'tags/mvebu-mbus_pci-fixes-3.15' into pci/host-mvebu

Pull mvebu fixes from Jason Cooper:

 "mvebu drivers (mbus and pci) fixes for v3.15

   - pci

      - fix off-by-one for mbus window size
      - split BARs into multiple mbus windows when needed

   - mbus

      - avoid setting undefined window size
      - allow several windows with the same target/attr"
parents c9eaa447 398f5d5e
Loading
Loading
Loading
Loading
+16 −6
Original line number Diff line number Diff line
@@ -56,6 +56,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/debugfs.h>
#include <linux/log2.h>

/*
 * DDR target is the same on all platforms.
@@ -222,12 +223,6 @@ static int mvebu_mbus_window_conflicts(struct mvebu_mbus_state *mbus,
		 */
		if ((u64)base < wend && end > wbase)
			return 0;

		/*
		 * Check if target/attribute conflicts
		 */
		if (target == wtarget && attr == wattr)
			return 0;
	}

	return 1;
@@ -266,6 +261,17 @@ static int mvebu_mbus_setup_window(struct mvebu_mbus_state *mbus,
		mbus->soc->win_cfg_offset(win);
	u32 ctrl, remap_addr;

	if (!is_power_of_2(size)) {
		WARN(true, "Invalid MBus window size: 0x%zx\n", size);
		return -EINVAL;
	}

	if ((base & (phys_addr_t)(size - 1)) != 0) {
		WARN(true, "Invalid MBus base/size: %pa len 0x%zx\n", &base,
		     size);
		return -EINVAL;
	}

	ctrl = ((size - 1) & WIN_CTRL_SIZE_MASK) |
		(attr << WIN_CTRL_ATTR_SHIFT)    |
		(target << WIN_CTRL_TGT_SHIFT)   |
@@ -413,6 +419,10 @@ static int mvebu_devs_debug_show(struct seq_file *seq, void *v)
			   win, (unsigned long long)wbase,
			   (unsigned long long)(wbase + wsize), wtarget, wattr);

		if (!is_power_of_2(wsize) ||
		    ((wbase & (u64)(wsize - 1)) != 0))
			seq_puts(seq, " (Invalid base/size!!)");

		if (win < mbus->soc->num_remappable_wins) {
			seq_printf(seq, " (remap %016llx)\n",
				   (unsigned long long)wremap);
+76 −16
Original line number Diff line number Diff line
@@ -293,6 +293,58 @@ static int mvebu_pcie_hw_wr_conf(struct mvebu_pcie_port *port,
	return PCIBIOS_SUCCESSFUL;
}

/*
 * Remove windows, starting from the largest ones to the smallest
 * ones.
 */
static void mvebu_pcie_del_windows(struct mvebu_pcie_port *port,
				   phys_addr_t base, size_t size)
{
	while (size) {
		size_t sz = 1 << (fls(size) - 1);

		mvebu_mbus_del_window(base, sz);
		base += sz;
		size -= sz;
	}
}

/*
 * MBus windows can only have a power of two size, but PCI BARs do not
 * have this constraint. Therefore, we have to split the PCI BAR into
 * areas each having a power of two size. We start from the largest
 * one (i.e highest order bit set in the size).
 */
static void mvebu_pcie_add_windows(struct mvebu_pcie_port *port,
				   unsigned int target, unsigned int attribute,
				   phys_addr_t base, size_t size,
				   phys_addr_t remap)
{
	size_t size_mapped = 0;

	while (size) {
		size_t sz = 1 << (fls(size) - 1);
		int ret;

		ret = mvebu_mbus_add_window_remap_by_id(target, attribute, base,
							sz, remap);
		if (ret) {
			dev_err(&port->pcie->pdev->dev,
				"Could not create MBus window at 0x%x, size 0x%x: %d\n",
				base, sz, ret);
			mvebu_pcie_del_windows(port, base - size_mapped,
					       size_mapped);
			return;
		}

		size -= sz;
		size_mapped += sz;
		base += sz;
		if (remap != MVEBU_MBUS_NO_REMAP)
			remap += sz;
	}
}

static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)
{
	phys_addr_t iobase;
@@ -304,7 +356,7 @@ static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)

		/* If a window was configured, remove it */
		if (port->iowin_base) {
			mvebu_mbus_del_window(port->iowin_base,
			mvebu_pcie_del_windows(port, port->iowin_base,
					       port->iowin_size);
			port->iowin_base = 0;
			port->iowin_size = 0;
@@ -331,9 +383,9 @@ static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)
	port->iowin_base = port->pcie->io.start + iobase;
	port->iowin_size = ((0xFFF | ((port->bridge.iolimit & 0xF0) << 8) |
			    (port->bridge.iolimitupper << 16)) -
			    iobase);
			    iobase) + 1;

	mvebu_mbus_add_window_remap_by_id(port->io_target, port->io_attr,
	mvebu_pcie_add_windows(port, port->io_target, port->io_attr,
			       port->iowin_base, port->iowin_size,
			       iobase);
}
@@ -346,7 +398,7 @@ static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port)

		/* If a window was configured, remove it */
		if (port->memwin_base) {
			mvebu_mbus_del_window(port->memwin_base,
			mvebu_pcie_del_windows(port, port->memwin_base,
					       port->memwin_size);
			port->memwin_base = 0;
			port->memwin_size = 0;
@@ -364,10 +416,11 @@ static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port)
	port->memwin_base  = ((port->bridge.membase & 0xFFF0) << 16);
	port->memwin_size  =
		(((port->bridge.memlimit & 0xFFF0) << 16) | 0xFFFFF) -
		port->memwin_base;
		port->memwin_base + 1;

	mvebu_mbus_add_window_by_id(port->mem_target, port->mem_attr,
				    port->memwin_base, port->memwin_size);
	mvebu_pcie_add_windows(port, port->mem_target, port->mem_attr,
			       port->memwin_base, port->memwin_size,
			       MVEBU_MBUS_NO_REMAP);
}

/*
@@ -743,14 +796,21 @@ static resource_size_t mvebu_pcie_align_resource(struct pci_dev *dev,

	/*
	 * On the PCI-to-PCI bridge side, the I/O windows must have at
	 * least a 64 KB size and be aligned on their size, and the
	 * memory windows must have at least a 1 MB size and be
	 * aligned on their size
	 * least a 64 KB size and the memory windows must have at
	 * least a 1 MB size. Moreover, MBus windows need to have a
	 * base address aligned on their size, and their size must be
	 * a power of two. This means that if the BAR doesn't have a
	 * power of two size, several MBus windows will actually be
	 * created. We need to ensure that the biggest MBus window
	 * (which will be the first one) is aligned on its size, which
	 * explains the rounddown_pow_of_two() being done here.
	 */
	if (res->flags & IORESOURCE_IO)
		return round_up(start, max_t(resource_size_t, SZ_64K, size));
		return round_up(start, max_t(resource_size_t, SZ_64K,
					     rounddown_pow_of_two(size)));
	else if (res->flags & IORESOURCE_MEM)
		return round_up(start, max_t(resource_size_t, SZ_1M, size));
		return round_up(start, max_t(resource_size_t, SZ_1M,
					     rounddown_pow_of_two(size)));
	else
		return start;
}