Skip to content

Commit

Permalink
Merge tag 'tags/mvebu-mbus_pci-fixes-3.15' into pci/host-mvebu
Browse files Browse the repository at this point in the history
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"
  • Loading branch information
Bjorn Helgaas committed Apr 29, 2014
2 parents c9eaa44 + 398f5d5 commit ccfdd7a
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 22 deletions.
22 changes: 16 additions & 6 deletions drivers/bus/mvebu-mbus.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) |
Expand Down Expand Up @@ -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);
Expand Down
92 changes: 76 additions & 16 deletions drivers/pci/host/pci-mvebu.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -304,8 +356,8 @@ 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,
port->iowin_size);
mvebu_pcie_del_windows(port, port->iowin_base,
port->iowin_size);
port->iowin_base = 0;
port->iowin_size = 0;
}
Expand All @@ -331,11 +383,11 @@ 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,
port->iowin_base, port->iowin_size,
iobase);
mvebu_pcie_add_windows(port, port->io_target, port->io_attr,
port->iowin_base, port->iowin_size,
iobase);
}

static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port)
Expand All @@ -346,8 +398,8 @@ 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,
port->memwin_size);
mvebu_pcie_del_windows(port, port->memwin_base,
port->memwin_size);
port->memwin_base = 0;
port->memwin_size = 0;
}
Expand All @@ -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);
}

/*
Expand Down Expand Up @@ -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;
}
Expand Down

0 comments on commit ccfdd7a

Please sign in to comment.