free_irq(priv->pdev->irq, (void *)priv);
 }
 
+static int
+tsi721_obw_alloc(struct tsi721_device *priv, struct tsi721_obw_bar *pbar,
+                u32 size, int *win_id)
+{
+       u64 win_base;
+       u64 bar_base;
+       u64 bar_end;
+       u32 align;
+       struct tsi721_ob_win *win;
+       struct tsi721_ob_win *new_win = NULL;
+       int new_win_idx = -1;
+       int i = 0;
+
+       bar_base = pbar->base;
+       bar_end =  bar_base + pbar->size;
+       win_base = bar_base;
+       align = size/TSI721_PC2SR_ZONES;
+
+       while (i < TSI721_IBWIN_NUM) {
+               for (i = 0; i < TSI721_IBWIN_NUM; i++) {
+                       if (!priv->ob_win[i].active) {
+                               if (new_win == NULL) {
+                                       new_win = &priv->ob_win[i];
+                                       new_win_idx = i;
+                               }
+                               continue;
+                       }
+
+                       /*
+                        * If this window belongs to the current BAR check it
+                        * for overlap
+                        */
+                       win = &priv->ob_win[i];
+
+                       if (win->base >= bar_base && win->base < bar_end) {
+                               if (win_base < (win->base + win->size) &&
+                                               (win_base + size) > win->base) {
+                                       /* Overlap detected */
+                                       win_base = win->base + win->size;
+                                       win_base = ALIGN(win_base, align);
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       if (win_base + size > bar_end)
+               return -ENOMEM;
+
+       if (!new_win) {
+               dev_err(&priv->pdev->dev, "ERR: OBW count tracking failed\n");
+               return -EIO;
+       }
+
+       new_win->active = true;
+       new_win->base = win_base;
+       new_win->size = size;
+       new_win->pbar = pbar;
+       priv->obwin_cnt--;
+       pbar->free -= size;
+       *win_id = new_win_idx;
+       return 0;
+}
+
+static int tsi721_map_outb_win(struct rio_mport *mport, u16 destid, u64 rstart,
+                       u32 size, u32 flags, dma_addr_t *laddr)
+{
+       struct tsi721_device *priv = mport->priv;
+       int i;
+       struct tsi721_obw_bar *pbar;
+       struct tsi721_ob_win *ob_win;
+       int obw = -1;
+       u32 rval;
+       u64 rio_addr;
+       u32 zsize;
+       int ret = -ENOMEM;
+
+       if (!is_power_of_2(size) || (size < 0x8000) || (rstart & (size - 1)))
+               return -EINVAL;
+
+       if (priv->obwin_cnt == 0)
+               return -EBUSY;
+
+       for (i = 0; i < 2; i++) {
+               if (priv->p2r_bar[i].free >= size) {
+                       pbar = &priv->p2r_bar[i];
+                       ret = tsi721_obw_alloc(priv, pbar, size, &obw);
+                       if (!ret)
+                               break;
+               }
+       }
+
+       if (ret)
+               return ret;
+
+       WARN_ON(obw == -1);
+       ob_win = &priv->ob_win[obw];
+       ob_win->destid = destid;
+       ob_win->rstart = rstart;
+
+       /*
+        * Configure Outbound Window
+        */
+
+       zsize = size/TSI721_PC2SR_ZONES;
+       rio_addr = rstart;
+
+       /*
+        * Program Address Translation Zones:
+        *  This implementation uses all 8 zones associated wit window.
+        */
+       for (i = 0; i < TSI721_PC2SR_ZONES; i++) {
+
+               while (ioread32(priv->regs + TSI721_ZONE_SEL) &
+                       TSI721_ZONE_SEL_GO) {
+                       udelay(1);
+               }
+
+               rval = (u32)(rio_addr & TSI721_LUT_DATA0_ADD) |
+                       TSI721_LUT_DATA0_NREAD | TSI721_LUT_DATA0_NWR;
+               iowrite32(rval, priv->regs + TSI721_LUT_DATA0);
+               rval = (u32)(rio_addr >> 32);
+               iowrite32(rval, priv->regs + TSI721_LUT_DATA1);
+               rval = destid;
+               iowrite32(rval, priv->regs + TSI721_LUT_DATA2);
+
+               rval = TSI721_ZONE_SEL_GO | (obw << 3) | i;
+               iowrite32(rval, priv->regs + TSI721_ZONE_SEL);
+
+               rio_addr += zsize;
+       }
+
+       iowrite32(TSI721_OBWIN_SIZE(size) << 8,
+                 priv->regs + TSI721_OBWINSZ(obw));
+       iowrite32((u32)(ob_win->base >> 32), priv->regs + TSI721_OBWINUB(obw));
+       iowrite32((u32)(ob_win->base & TSI721_OBWINLB_BA) | TSI721_OBWINLB_WEN,
+                 priv->regs + TSI721_OBWINLB(obw));
+
+       *laddr = ob_win->base;
+       return 0;
+}
+
+static void tsi721_unmap_outb_win(struct rio_mport *mport,
+                                 u16 destid, u64 rstart)
+{
+       struct tsi721_device *priv = mport->priv;
+       struct tsi721_ob_win *ob_win;
+       int i;
+
+       for (i = 0; i < TSI721_OBWIN_NUM; i++) {
+               ob_win = &priv->ob_win[i];
+
+               if (ob_win->active &&
+                   ob_win->destid == destid && ob_win->rstart == rstart) {
+                       ob_win->active = false;
+                       iowrite32(0, priv->regs + TSI721_OBWINLB(i));
+                       ob_win->pbar->free += ob_win->size;
+                       priv->obwin_cnt++;
+                       break;
+               }
+       }
+}
+
 /**
  * tsi721_init_pc2sr_mapping - initializes outbound (PCIe->SRIO)
  * translation regions.
  */
 static void tsi721_init_pc2sr_mapping(struct tsi721_device *priv)
 {
-       int i;
+       int i, z;
+       u32 rval;
 
        /* Disable all PC2SR translation windows */
        for (i = 0; i < TSI721_OBWIN_NUM; i++)
                iowrite32(0, priv->regs + TSI721_OBWINLB(i));
+
+       /* Initialize zone lookup tables to avoid ECC errors on reads */
+       iowrite32(0, priv->regs + TSI721_LUT_DATA0);
+       iowrite32(0, priv->regs + TSI721_LUT_DATA1);
+       iowrite32(0, priv->regs + TSI721_LUT_DATA2);
+
+       for (i = 0; i < TSI721_OBWIN_NUM; i++) {
+               for (z = 0; z < TSI721_PC2SR_ZONES; z++) {
+                       while (ioread32(priv->regs + TSI721_ZONE_SEL) &
+                               TSI721_ZONE_SEL_GO) {
+                               udelay(1);
+                       }
+                       rval = TSI721_ZONE_SEL_GO | (i << 3) | z;
+                       iowrite32(rval, priv->regs + TSI721_ZONE_SEL);
+               }
+       }
+
+       if (priv->p2r_bar[0].size == 0 && priv->p2r_bar[1].size == 0) {
+               priv->obwin_cnt = 0;
+               return;
+       }
+
+       priv->p2r_bar[0].free = priv->p2r_bar[0].size;
+       priv->p2r_bar[1].free = priv->p2r_bar[1].size;
+
+       for (i = 0; i < TSI721_OBWIN_NUM; i++)
+               priv->ob_win[i].active = false;
+
+       priv->obwin_cnt = TSI721_OBWIN_NUM;
 }
 
 /**
        .unmap_inb              = tsi721_rio_unmap_inb_mem,
        .pwenable               = tsi721_pw_enable,
        .query_mport            = tsi721_query_mport,
+       .map_outb               = tsi721_map_outb_win,
+       .unmap_outb             = tsi721_unmap_outb_win,
 };
 
 static void tsi721_mport_release(struct device *dev)
         * It may be a good idea to keep them disabled using HW configuration
         * to save PCI memory space.
         */
-       if ((pci_resource_flags(pdev, BAR_2) & IORESOURCE_MEM) &&
-           (pci_resource_flags(pdev, BAR_2) & IORESOURCE_MEM_64)) {
-               dev_info(&pdev->dev, "Outbound BAR2 is not used but enabled.\n");
+
+       priv->p2r_bar[0].size = priv->p2r_bar[1].size = 0;
+
+       if (pci_resource_flags(pdev, BAR_2) & IORESOURCE_MEM_64) {
+               if (pci_resource_flags(pdev, BAR_2) & IORESOURCE_PREFETCH)
+                       dev_info(&pdev->dev,
+                                "Prefetchable OBW BAR2 will not be used\n");
+               else {
+                       priv->p2r_bar[0].base = pci_resource_start(pdev, BAR_2);
+                       priv->p2r_bar[0].size = pci_resource_len(pdev, BAR_2);
+               }
        }
 
-       if ((pci_resource_flags(pdev, BAR_4) & IORESOURCE_MEM) &&
-           (pci_resource_flags(pdev, BAR_4) & IORESOURCE_MEM_64)) {
-               dev_info(&pdev->dev, "Outbound BAR4 is not used but enabled.\n");
+       if (pci_resource_flags(pdev, BAR_4) & IORESOURCE_MEM_64) {
+               if (pci_resource_flags(pdev, BAR_4) & IORESOURCE_PREFETCH)
+                       dev_info(&pdev->dev,
+                                "Prefetchable OBW BAR4 will not be used\n");
+               else {
+                       priv->p2r_bar[1].base = pci_resource_start(pdev, BAR_4);
+                       priv->p2r_bar[1].size = pci_resource_len(pdev, BAR_4);
+               }
        }
 
        err = pci_request_regions(pdev, DRV_NAME);