|
@@ -113,7 +113,6 @@ struct mvebu_pcie {
|
|
|
struct mvebu_pcie_port {
|
|
|
char *name;
|
|
|
void __iomem *base;
|
|
|
- spinlock_t conf_lock;
|
|
|
u32 port;
|
|
|
u32 lane;
|
|
|
int devfn;
|
|
@@ -293,6 +292,60 @@ 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) {
|
|
|
+ phys_addr_t end = base + sz - 1;
|
|
|
+
|
|
|
+ dev_err(&port->pcie->pdev->dev,
|
|
|
+ "Could not create MBus window at [mem %pa-%pa]: %d\n",
|
|
|
+ &base, &end, 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 +357,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 +384,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 +399,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 +417,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);
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -585,7 +639,6 @@ static int mvebu_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
|
|
|
{
|
|
|
struct mvebu_pcie *pcie = sys_to_pcie(bus->sysdata);
|
|
|
struct mvebu_pcie_port *port;
|
|
|
- unsigned long flags;
|
|
|
int ret;
|
|
|
|
|
|
port = mvebu_pcie_find_port(pcie, bus, devfn);
|
|
@@ -611,10 +664,8 @@ static int mvebu_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
|
|
|
return PCIBIOS_DEVICE_NOT_FOUND;
|
|
|
|
|
|
/* Access the real PCIe interface */
|
|
|
- spin_lock_irqsave(&port->conf_lock, flags);
|
|
|
ret = mvebu_pcie_hw_wr_conf(port, bus, devfn,
|
|
|
where, size, val);
|
|
|
- spin_unlock_irqrestore(&port->conf_lock, flags);
|
|
|
|
|
|
return ret;
|
|
|
}
|
|
@@ -625,7 +676,6 @@ static int mvebu_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
|
|
|
{
|
|
|
struct mvebu_pcie *pcie = sys_to_pcie(bus->sysdata);
|
|
|
struct mvebu_pcie_port *port;
|
|
|
- unsigned long flags;
|
|
|
int ret;
|
|
|
|
|
|
port = mvebu_pcie_find_port(pcie, bus, devfn);
|
|
@@ -657,10 +707,8 @@ static int mvebu_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
|
|
|
}
|
|
|
|
|
|
/* Access the real PCIe interface */
|
|
|
- spin_lock_irqsave(&port->conf_lock, flags);
|
|
|
ret = mvebu_pcie_hw_rd_conf(port, bus, devfn,
|
|
|
where, size, val);
|
|
|
- spin_unlock_irqrestore(&port->conf_lock, flags);
|
|
|
|
|
|
return ret;
|
|
|
}
|
|
@@ -743,14 +791,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;
|
|
|
}
|
|
@@ -1000,7 +1055,6 @@ static int mvebu_pcie_probe(struct platform_device *pdev)
|
|
|
mvebu_pcie_set_local_dev_nr(port, 1);
|
|
|
|
|
|
port->dn = child;
|
|
|
- spin_lock_init(&port->conf_lock);
|
|
|
mvebu_sw_pci_bridge_init(port);
|
|
|
i++;
|
|
|
}
|