mirror of https://gitee.com/openkylin/linux.git
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 -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQIcBAABAgAGBQJTX6KOAAoJEP45WPkGe8ZnK14QAJoddpkgzp7sgANXpE3SlpA/ FycxazhA8s4giZKiPcFy93sl8SYEvpB8l9UDRhvImTlbifrB+PpBBQf77RyAH3w9 vJIwuLC2BwLJEaY4CzA0ahIvhhik/nextB4btVQoh0iqxix9Elk+BZh7QspCxYkC vkGdFoEiLuI73SxMv2qNMhfz24UbrctZwZlMxRB1Lbo0YCY8SkQMeK8d4ZwzNHhx 10jyGcpINQcUb5Z5vmyGrdvOEFTmOWcn5gflNpj1keKfHbdzDRomrPzX99udWMll 0tAuCcK8iW7kG2w5RiBU9IPd7eVgi3lf6tZmUB4JEYak8W7C/TcjxfRSdMNU7G/k dwDHcmD67UOvLlOwoZW5eCG1tmyUfAeTwKxZKAR97tkiXdcEK49EsY348LEWzQuo ZG2OLvjXwBo8y5kOHIOd3cZAdmTh9HSt1HgZkzU+vZFZWQAcM8PRKaR7/dyovTq8 gUUifjjQ1HHzlOAJ12j3C98jUDts64sJCB54CXDTJlWZn3fw3cYiZJhrJJtpdGn5 94GGwUvLX1dd9uSV1rq6/1tJc7q7IpgdWSjiPUE2zyXL91PLmElB7wjrsyphBAcJ 0QWx1j8V6fBDtveYfjwzMDhYDjZGqkJHV1L5k+NEy2VsgrT3aX9EkbHnYRsS6jNb 9fmP83qeahAZusCEqQ2v =b3o7 -----END PGP SIGNATURE----- Merge tag 'mvebu-mbus_pci-fixes-3.15' of git://git.infradead.org/linux-mvebu into 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 * tag 'mvebu-mbus_pci-fixes-3.15' of git://git.infradead.org/linux-mvebu: PCI: mvebu: split PCIe BARs into multiple MBus windows when needed bus: mvebu-mbus: allow several windows with the same target/attribute bus: mvebu-mbus: Avoid setting an undefined window size PCI: mvebu: fix off-by-one in the computed size of the mbus windows Signed-off-by: Olof Johansson <olof@lixom.net>
This commit is contained in:
commit
1a7adf2e23
|
@ -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);
|
||||
|
|
|
@ -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,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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue