MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Microsemi Corporation");
 
+static ulong max_mw_size = SZ_2M;
+module_param(max_mw_size, ulong, 0644);
+MODULE_PARM_DESC(max_mw_size,
+       "Max memory window size reported to the upper layer");
+
 static bool use_lut_mws;
 module_param(use_lut_mws, bool, 0644);
 MODULE_PARM_DESC(use_lut_mws,
 
 static int switchtec_ntb_mw_count(struct ntb_dev *ntb, int pidx)
 {
-       return 0;
+       struct switchtec_ntb *sndev = ntb_sndev(ntb);
+       int nr_direct_mw = sndev->peer_nr_direct_mw;
+       int nr_lut_mw = sndev->peer_nr_lut_mw - 1;
+
+       if (pidx != NTB_DEF_PEER_IDX)
+               return -EINVAL;
+
+       if (!use_lut_mws)
+               nr_lut_mw = 0;
+
+       return nr_direct_mw + nr_lut_mw;
+}
+
+static int lut_index(struct switchtec_ntb *sndev, int mw_idx)
+{
+       return mw_idx - sndev->nr_direct_mw + 1;
+}
+
+static int peer_lut_index(struct switchtec_ntb *sndev, int mw_idx)
+{
+       return mw_idx - sndev->peer_nr_direct_mw + 1;
 }
 
 static int switchtec_ntb_mw_get_align(struct ntb_dev *ntb, int pidx,
                                      resource_size_t *size_align,
                                      resource_size_t *size_max)
 {
+       struct switchtec_ntb *sndev = ntb_sndev(ntb);
+       int lut;
+       resource_size_t size;
+
+       if (pidx != NTB_DEF_PEER_IDX)
+               return -EINVAL;
+
+       lut = widx >= sndev->peer_nr_direct_mw;
+       size = ioread64(&sndev->peer_shared->mw_sizes[widx]);
+
+       if (size == 0)
+               return -EINVAL;
+
+       if (addr_align)
+               *addr_align = lut ? size : SZ_4K;
+
+       if (size_align)
+               *size_align = lut ? size : SZ_4K;
+
+       if (size_max)
+               *size_max = size;
+
        return 0;
 }
 
+static void switchtec_ntb_mw_clr_direct(struct switchtec_ntb *sndev, int idx)
+{
+       struct ntb_ctrl_regs __iomem *ctl = sndev->mmio_peer_ctrl;
+       int bar = sndev->peer_direct_mw_to_bar[idx];
+       u32 ctl_val;
+
+       ctl_val = ioread32(&ctl->bar_entry[bar].ctl);
+       ctl_val &= ~NTB_CTRL_BAR_DIR_WIN_EN;
+       iowrite32(ctl_val, &ctl->bar_entry[bar].ctl);
+       iowrite32(0, &ctl->bar_entry[bar].win_size);
+       iowrite64(sndev->self_partition, &ctl->bar_entry[bar].xlate_addr);
+}
+
+static void switchtec_ntb_mw_clr_lut(struct switchtec_ntb *sndev, int idx)
+{
+       struct ntb_ctrl_regs __iomem *ctl = sndev->mmio_peer_ctrl;
+
+       iowrite64(0, &ctl->lut_entry[peer_lut_index(sndev, idx)]);
+}
+
+static void switchtec_ntb_mw_set_direct(struct switchtec_ntb *sndev, int idx,
+                                       dma_addr_t addr, resource_size_t size)
+{
+       int xlate_pos = ilog2(size);
+       int bar = sndev->peer_direct_mw_to_bar[idx];
+       struct ntb_ctrl_regs __iomem *ctl = sndev->mmio_peer_ctrl;
+       u32 ctl_val;
+
+       ctl_val = ioread32(&ctl->bar_entry[bar].ctl);
+       ctl_val |= NTB_CTRL_BAR_DIR_WIN_EN;
+
+       iowrite32(ctl_val, &ctl->bar_entry[bar].ctl);
+       iowrite32(xlate_pos | size, &ctl->bar_entry[bar].win_size);
+       iowrite64(sndev->self_partition | addr,
+                 &ctl->bar_entry[bar].xlate_addr);
+}
+
+static void switchtec_ntb_mw_set_lut(struct switchtec_ntb *sndev, int idx,
+                                    dma_addr_t addr, resource_size_t size)
+{
+       struct ntb_ctrl_regs __iomem *ctl = sndev->mmio_peer_ctrl;
+
+       iowrite64((NTB_CTRL_LUT_EN | (sndev->self_partition << 1) | addr),
+                 &ctl->lut_entry[peer_lut_index(sndev, idx)]);
+}
+
 static int switchtec_ntb_mw_set_trans(struct ntb_dev *ntb, int pidx, int widx,
                                      dma_addr_t addr, resource_size_t size)
 {
-       return 0;
+       struct switchtec_ntb *sndev = ntb_sndev(ntb);
+       struct ntb_ctrl_regs __iomem *ctl = sndev->mmio_peer_ctrl;
+       int xlate_pos = ilog2(size);
+       int nr_direct_mw = sndev->peer_nr_direct_mw;
+       int rc;
+
+       if (pidx != NTB_DEF_PEER_IDX)
+               return -EINVAL;
+
+       dev_dbg(&sndev->stdev->dev, "MW %d: part %d addr %pad size %pap",
+               widx, pidx, &addr, &size);
+
+       if (widx >= switchtec_ntb_mw_count(ntb, pidx))
+               return -EINVAL;
+
+       if (xlate_pos < 12)
+               return -EINVAL;
+
+       rc = switchtec_ntb_part_op(sndev, ctl, NTB_CTRL_PART_OP_LOCK,
+                                  NTB_CTRL_PART_STATUS_LOCKED);
+       if (rc)
+               return rc;
+
+       if (addr == 0 || size == 0) {
+               if (widx < nr_direct_mw)
+                       switchtec_ntb_mw_clr_direct(sndev, widx);
+               else
+                       switchtec_ntb_mw_clr_lut(sndev, widx);
+       } else {
+               if (widx < nr_direct_mw)
+                       switchtec_ntb_mw_set_direct(sndev, widx, addr, size);
+               else
+                       switchtec_ntb_mw_set_lut(sndev, widx, addr, size);
+       }
+
+       rc = switchtec_ntb_part_op(sndev, ctl, NTB_CTRL_PART_OP_CFG,
+                                  NTB_CTRL_PART_STATUS_NORMAL);
+
+       if (rc == -EIO) {
+               dev_err(&sndev->stdev->dev,
+                       "Hardware reported an error configuring mw %d: %08x",
+                       widx, ioread32(&ctl->bar_error));
+
+               if (widx < nr_direct_mw)
+                       switchtec_ntb_mw_clr_direct(sndev, widx);
+               else
+                       switchtec_ntb_mw_clr_lut(sndev, widx);
+
+               switchtec_ntb_part_op(sndev, ctl, NTB_CTRL_PART_OP_CFG,
+                                     NTB_CTRL_PART_STATUS_NORMAL);
+       }
+
+       return rc;
 }
 
 static int switchtec_ntb_peer_mw_count(struct ntb_dev *ntb)
 {
+       struct switchtec_ntb *sndev = ntb_sndev(ntb);
+
+       return sndev->nr_direct_mw + (use_lut_mws ? sndev->nr_lut_mw - 1 : 0);
+}
+
+static int switchtec_ntb_direct_get_addr(struct switchtec_ntb *sndev,
+                                        int idx, phys_addr_t *base,
+                                        resource_size_t *size)
+{
+       int bar = sndev->direct_mw_to_bar[idx];
+       size_t offset = 0;
+
+       if (bar < 0)
+               return -EINVAL;
+
+       if (idx == 0) {
+               /*
+                * This is the direct BAR shared with the LUTs
+                * which means the actual window will be offset
+                * by the size of all the LUT entries.
+                */
+
+               offset = LUT_SIZE * sndev->nr_lut_mw;
+       }
+
+       if (base)
+               *base = pci_resource_start(sndev->ntb.pdev, bar) + offset;
+
+       if (size) {
+               *size = pci_resource_len(sndev->ntb.pdev, bar) - offset;
+               if (offset && *size > offset)
+                       *size = offset;
+
+               if (*size > max_mw_size)
+                       *size = max_mw_size;
+       }
+
+       return 0;
+}
+
+static int switchtec_ntb_lut_get_addr(struct switchtec_ntb *sndev,
+                                     int idx, phys_addr_t *base,
+                                     resource_size_t *size)
+{
+       int bar = sndev->direct_mw_to_bar[0];
+       int offset;
+
+       offset = LUT_SIZE * lut_index(sndev, idx);
+
+       if (base)
+               *base = pci_resource_start(sndev->ntb.pdev, bar) + offset;
+
+       if (size)
+               *size = LUT_SIZE;
+
        return 0;
 }
 
                                          phys_addr_t *base,
                                          resource_size_t *size)
 {
-       return 0;
+       struct switchtec_ntb *sndev = ntb_sndev(ntb);
+
+       if (idx < sndev->nr_direct_mw)
+               return switchtec_ntb_direct_get_addr(sndev, idx, base, size);
+       else if (idx < switchtec_ntb_peer_mw_count(ntb))
+               return switchtec_ntb_lut_get_addr(sndev, idx, base, size);
+       else
+               return -EINVAL;
 }
 
 static void switchtec_ntb_part_link_speed(struct switchtec_ntb *sndev,