diff options
| author | Bjorn Helgaas <bhelgaas@google.com> | 2014-04-29 12:36:50 -0400 |
|---|---|---|
| committer | Bjorn Helgaas <bhelgaas@google.com> | 2014-04-29 12:39:08 -0400 |
| commit | ccfdd7aebad1b7a46724196fe1f4557e64eff599 (patch) | |
| tree | d9095233924eb6859bd0a3190a828c2d8d88873a | |
| parent | c9eaa447e77efe77b7fa4c953bd62de8297fd6c5 (diff) | |
| parent | 398f5d5e10b6b917cd9d35ef21d545b0afbada22 (diff) | |
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"
| -rw-r--r-- | drivers/bus/mvebu-mbus.c | 22 | ||||
| -rw-r--r-- | drivers/pci/host/pci-mvebu.c | 92 |
2 files changed, 92 insertions, 22 deletions
diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c index 293e2e0a0a87..00b73448b22e 100644 --- a/drivers/bus/mvebu-mbus.c +++ b/drivers/bus/mvebu-mbus.c | |||
| @@ -56,6 +56,7 @@ | |||
| 56 | #include <linux/of.h> | 56 | #include <linux/of.h> |
| 57 | #include <linux/of_address.h> | 57 | #include <linux/of_address.h> |
| 58 | #include <linux/debugfs.h> | 58 | #include <linux/debugfs.h> |
| 59 | #include <linux/log2.h> | ||
| 59 | 60 | ||
| 60 | /* | 61 | /* |
| 61 | * DDR target is the same on all platforms. | 62 | * DDR target is the same on all platforms. |
| @@ -222,12 +223,6 @@ static int mvebu_mbus_window_conflicts(struct mvebu_mbus_state *mbus, | |||
| 222 | */ | 223 | */ |
| 223 | if ((u64)base < wend && end > wbase) | 224 | if ((u64)base < wend && end > wbase) |
| 224 | return 0; | 225 | return 0; |
| 225 | |||
| 226 | /* | ||
| 227 | * Check if target/attribute conflicts | ||
| 228 | */ | ||
| 229 | if (target == wtarget && attr == wattr) | ||
| 230 | return 0; | ||
| 231 | } | 226 | } |
| 232 | 227 | ||
| 233 | return 1; | 228 | return 1; |
| @@ -266,6 +261,17 @@ static int mvebu_mbus_setup_window(struct mvebu_mbus_state *mbus, | |||
| 266 | mbus->soc->win_cfg_offset(win); | 261 | mbus->soc->win_cfg_offset(win); |
| 267 | u32 ctrl, remap_addr; | 262 | u32 ctrl, remap_addr; |
| 268 | 263 | ||
| 264 | if (!is_power_of_2(size)) { | ||
| 265 | WARN(true, "Invalid MBus window size: 0x%zx\n", size); | ||
| 266 | return -EINVAL; | ||
| 267 | } | ||
| 268 | |||
| 269 | if ((base & (phys_addr_t)(size - 1)) != 0) { | ||
| 270 | WARN(true, "Invalid MBus base/size: %pa len 0x%zx\n", &base, | ||
| 271 | size); | ||
| 272 | return -EINVAL; | ||
| 273 | } | ||
| 274 | |||
| 269 | ctrl = ((size - 1) & WIN_CTRL_SIZE_MASK) | | 275 | ctrl = ((size - 1) & WIN_CTRL_SIZE_MASK) | |
| 270 | (attr << WIN_CTRL_ATTR_SHIFT) | | 276 | (attr << WIN_CTRL_ATTR_SHIFT) | |
| 271 | (target << WIN_CTRL_TGT_SHIFT) | | 277 | (target << WIN_CTRL_TGT_SHIFT) | |
| @@ -413,6 +419,10 @@ static int mvebu_devs_debug_show(struct seq_file *seq, void *v) | |||
| 413 | win, (unsigned long long)wbase, | 419 | win, (unsigned long long)wbase, |
| 414 | (unsigned long long)(wbase + wsize), wtarget, wattr); | 420 | (unsigned long long)(wbase + wsize), wtarget, wattr); |
| 415 | 421 | ||
| 422 | if (!is_power_of_2(wsize) || | ||
| 423 | ((wbase & (u64)(wsize - 1)) != 0)) | ||
| 424 | seq_puts(seq, " (Invalid base/size!!)"); | ||
| 425 | |||
| 416 | if (win < mbus->soc->num_remappable_wins) { | 426 | if (win < mbus->soc->num_remappable_wins) { |
| 417 | seq_printf(seq, " (remap %016llx)\n", | 427 | seq_printf(seq, " (remap %016llx)\n", |
| 418 | (unsigned long long)wremap); | 428 | (unsigned long long)wremap); |
diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c index d3d1cfd51e09..e384e2534594 100644 --- a/drivers/pci/host/pci-mvebu.c +++ b/drivers/pci/host/pci-mvebu.c | |||
| @@ -293,6 +293,58 @@ static int mvebu_pcie_hw_wr_conf(struct mvebu_pcie_port *port, | |||
| 293 | return PCIBIOS_SUCCESSFUL; | 293 | return PCIBIOS_SUCCESSFUL; |
| 294 | } | 294 | } |
| 295 | 295 | ||
| 296 | /* | ||
| 297 | * Remove windows, starting from the largest ones to the smallest | ||
| 298 | * ones. | ||
| 299 | */ | ||
| 300 | static void mvebu_pcie_del_windows(struct mvebu_pcie_port *port, | ||
| 301 | phys_addr_t base, size_t size) | ||
| 302 | { | ||
| 303 | while (size) { | ||
| 304 | size_t sz = 1 << (fls(size) - 1); | ||
| 305 | |||
| 306 | mvebu_mbus_del_window(base, sz); | ||
| 307 | base += sz; | ||
| 308 | size -= sz; | ||
| 309 | } | ||
| 310 | } | ||
| 311 | |||
| 312 | /* | ||
| 313 | * MBus windows can only have a power of two size, but PCI BARs do not | ||
| 314 | * have this constraint. Therefore, we have to split the PCI BAR into | ||
| 315 | * areas each having a power of two size. We start from the largest | ||
| 316 | * one (i.e highest order bit set in the size). | ||
| 317 | */ | ||
| 318 | static void mvebu_pcie_add_windows(struct mvebu_pcie_port *port, | ||
| 319 | unsigned int target, unsigned int attribute, | ||
| 320 | phys_addr_t base, size_t size, | ||
| 321 | phys_addr_t remap) | ||
| 322 | { | ||
| 323 | size_t size_mapped = 0; | ||
| 324 | |||
| 325 | while (size) { | ||
| 326 | size_t sz = 1 << (fls(size) - 1); | ||
| 327 | int ret; | ||
| 328 | |||
| 329 | ret = mvebu_mbus_add_window_remap_by_id(target, attribute, base, | ||
| 330 | sz, remap); | ||
| 331 | if (ret) { | ||
| 332 | dev_err(&port->pcie->pdev->dev, | ||
| 333 | "Could not create MBus window at 0x%x, size 0x%x: %d\n", | ||
| 334 | base, sz, ret); | ||
| 335 | mvebu_pcie_del_windows(port, base - size_mapped, | ||
| 336 | size_mapped); | ||
| 337 | return; | ||
| 338 | } | ||
| 339 | |||
| 340 | size -= sz; | ||
| 341 | size_mapped += sz; | ||
| 342 | base += sz; | ||
| 343 | if (remap != MVEBU_MBUS_NO_REMAP) | ||
| 344 | remap += sz; | ||
| 345 | } | ||
| 346 | } | ||
| 347 | |||
| 296 | static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port) | 348 | static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port) |
| 297 | { | 349 | { |
| 298 | phys_addr_t iobase; | 350 | phys_addr_t iobase; |
| @@ -304,8 +356,8 @@ static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port) | |||
| 304 | 356 | ||
| 305 | /* If a window was configured, remove it */ | 357 | /* If a window was configured, remove it */ |
| 306 | if (port->iowin_base) { | 358 | if (port->iowin_base) { |
| 307 | mvebu_mbus_del_window(port->iowin_base, | 359 | mvebu_pcie_del_windows(port, port->iowin_base, |
| 308 | port->iowin_size); | 360 | port->iowin_size); |
| 309 | port->iowin_base = 0; | 361 | port->iowin_base = 0; |
| 310 | port->iowin_size = 0; | 362 | port->iowin_size = 0; |
| 311 | } | 363 | } |
| @@ -331,11 +383,11 @@ static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port) | |||
| 331 | port->iowin_base = port->pcie->io.start + iobase; | 383 | port->iowin_base = port->pcie->io.start + iobase; |
| 332 | port->iowin_size = ((0xFFF | ((port->bridge.iolimit & 0xF0) << 8) | | 384 | port->iowin_size = ((0xFFF | ((port->bridge.iolimit & 0xF0) << 8) | |
| 333 | (port->bridge.iolimitupper << 16)) - | 385 | (port->bridge.iolimitupper << 16)) - |
| 334 | iobase); | 386 | iobase) + 1; |
| 335 | 387 | ||
| 336 | mvebu_mbus_add_window_remap_by_id(port->io_target, port->io_attr, | 388 | mvebu_pcie_add_windows(port, port->io_target, port->io_attr, |
| 337 | port->iowin_base, port->iowin_size, | 389 | port->iowin_base, port->iowin_size, |
| 338 | iobase); | 390 | iobase); |
| 339 | } | 391 | } |
| 340 | 392 | ||
| 341 | static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port) | 393 | 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) | |||
| 346 | 398 | ||
| 347 | /* If a window was configured, remove it */ | 399 | /* If a window was configured, remove it */ |
| 348 | if (port->memwin_base) { | 400 | if (port->memwin_base) { |
| 349 | mvebu_mbus_del_window(port->memwin_base, | 401 | mvebu_pcie_del_windows(port, port->memwin_base, |
| 350 | port->memwin_size); | 402 | port->memwin_size); |
| 351 | port->memwin_base = 0; | 403 | port->memwin_base = 0; |
| 352 | port->memwin_size = 0; | 404 | port->memwin_size = 0; |
| 353 | } | 405 | } |
| @@ -364,10 +416,11 @@ static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port) | |||
| 364 | port->memwin_base = ((port->bridge.membase & 0xFFF0) << 16); | 416 | port->memwin_base = ((port->bridge.membase & 0xFFF0) << 16); |
| 365 | port->memwin_size = | 417 | port->memwin_size = |
| 366 | (((port->bridge.memlimit & 0xFFF0) << 16) | 0xFFFFF) - | 418 | (((port->bridge.memlimit & 0xFFF0) << 16) | 0xFFFFF) - |
| 367 | port->memwin_base; | 419 | port->memwin_base + 1; |
| 368 | 420 | ||
| 369 | mvebu_mbus_add_window_by_id(port->mem_target, port->mem_attr, | 421 | mvebu_pcie_add_windows(port, port->mem_target, port->mem_attr, |
| 370 | port->memwin_base, port->memwin_size); | 422 | port->memwin_base, port->memwin_size, |
| 423 | MVEBU_MBUS_NO_REMAP); | ||
| 371 | } | 424 | } |
| 372 | 425 | ||
| 373 | /* | 426 | /* |
| @@ -743,14 +796,21 @@ static resource_size_t mvebu_pcie_align_resource(struct pci_dev *dev, | |||
| 743 | 796 | ||
| 744 | /* | 797 | /* |
| 745 | * On the PCI-to-PCI bridge side, the I/O windows must have at | 798 | * On the PCI-to-PCI bridge side, the I/O windows must have at |
| 746 | * least a 64 KB size and be aligned on their size, and the | 799 | * least a 64 KB size and the memory windows must have at |
| 747 | * memory windows must have at least a 1 MB size and be | 800 | * least a 1 MB size. Moreover, MBus windows need to have a |
| 748 | * aligned on their size | 801 | * base address aligned on their size, and their size must be |
| 802 | * a power of two. This means that if the BAR doesn't have a | ||
| 803 | * power of two size, several MBus windows will actually be | ||
| 804 | * created. We need to ensure that the biggest MBus window | ||
| 805 | * (which will be the first one) is aligned on its size, which | ||
| 806 | * explains the rounddown_pow_of_two() being done here. | ||
| 749 | */ | 807 | */ |
| 750 | if (res->flags & IORESOURCE_IO) | 808 | if (res->flags & IORESOURCE_IO) |
| 751 | return round_up(start, max_t(resource_size_t, SZ_64K, size)); | 809 | return round_up(start, max_t(resource_size_t, SZ_64K, |
| 810 | rounddown_pow_of_two(size))); | ||
| 752 | else if (res->flags & IORESOURCE_MEM) | 811 | else if (res->flags & IORESOURCE_MEM) |
| 753 | return round_up(start, max_t(resource_size_t, SZ_1M, size)); | 812 | return round_up(start, max_t(resource_size_t, SZ_1M, |
| 813 | rounddown_pow_of_two(size))); | ||
| 754 | else | 814 | else |
| 755 | return start; | 815 | return start; |
| 756 | } | 816 | } |
