diff options
Diffstat (limited to 'drivers/rapidio/devices/tsi721.c')
-rw-r--r-- | drivers/rapidio/devices/tsi721.c | 163 |
1 files changed, 134 insertions, 29 deletions
diff --git a/drivers/rapidio/devices/tsi721.c b/drivers/rapidio/devices/tsi721.c index b96f0b97dc3a..d463d2cbd0af 100644 --- a/drivers/rapidio/devices/tsi721.c +++ b/drivers/rapidio/devices/tsi721.c @@ -888,71 +888,145 @@ static int tsi721_rio_map_inb_mem(struct rio_mport *mport, dma_addr_t lstart, int i, avail = -1; u32 regval; struct tsi721_ib_win *ib_win; + bool direct = (lstart == rstart); + u64 ibw_size; + dma_addr_t loc_start; + u64 ibw_start; + struct tsi721_ib_win_mapping *map = NULL; int ret = -EBUSY; - if (!is_power_of_2(size) || size < 0x1000 || - ((u64)lstart & (size - 1)) || (rstart & (size - 1))) - return -EINVAL; + if (direct) { + dev_dbg(&priv->pdev->dev, + "Direct (RIO_0x%llx -> PCIe_0x%pad), size=0x%x", + rstart, &lstart, size); + + /* Calculate minimal acceptable window size and base address */ + + ibw_size = roundup_pow_of_two(size); + ibw_start = lstart & ~(ibw_size - 1); + + while ((lstart + size) > (ibw_start + ibw_size)) { + ibw_size *= 2; + ibw_start = lstart & ~(ibw_size - 1); + if (ibw_size > 0x80000000) { /* Limit max size to 2GB */ + return -EBUSY; + } + } + + loc_start = ibw_start; + + map = kzalloc(sizeof(struct tsi721_ib_win_mapping), GFP_ATOMIC); + if (map == NULL) + return -ENOMEM; + + } else { + dev_dbg(&priv->pdev->dev, + "Translated (RIO_0x%llx -> PCIe_0x%pad), size=0x%x", + rstart, &lstart, size); + + if (!is_power_of_2(size) || size < 0x1000 || + ((u64)lstart & (size - 1)) || (rstart & (size - 1))) + return -EINVAL; + if (priv->ibwin_cnt == 0) + return -EBUSY; + ibw_start = rstart; + ibw_size = size; + loc_start = lstart; + } - spin_lock(&priv->win_lock); /* * Scan for overlapping with active regions and mark the first available * IB window at the same time. */ for (i = 0; i < TSI721_IBWIN_NUM; i++) { ib_win = &priv->ib_win[i]; + if (!ib_win->active) { if (avail == -1) { avail = i; ret = 0; } - } else if (rstart < (ib_win->rstart + ib_win->size) && - (rstart + size) > ib_win->rstart) { + } else if (ibw_start < (ib_win->rstart + ib_win->size) && + (ibw_start + ibw_size) > ib_win->rstart) { + /* Return error if address translation involved */ + if (direct && ib_win->xlat) { + ret = -EFAULT; + break; + } + + /* + * Direct mappings usually are larger than originally + * requested fragments - check if this new request fits + * into it. + */ + if (rstart >= ib_win->rstart && + (rstart + size) <= (ib_win->rstart + + ib_win->size)) { + /* We are in - no further mapping required */ + map->lstart = lstart; + list_add_tail(&map->node, &ib_win->mappings); + return 0; + } + ret = -EFAULT; break; } } if (ret) - goto err_out; + goto out; i = avail; /* Sanity check: available IB window must be disabled at this point */ regval = ioread32(priv->regs + TSI721_IBWIN_LB(i)); if (WARN_ON(regval & TSI721_IBWIN_LB_WEN)) { ret = -EIO; - goto err_out; + goto out; } ib_win = &priv->ib_win[i]; ib_win->active = true; - ib_win->rstart = rstart; - ib_win->lstart = lstart; - ib_win->size = size; - spin_unlock(&priv->win_lock); + ib_win->rstart = ibw_start; + ib_win->lstart = loc_start; + ib_win->size = ibw_size; + ib_win->xlat = (lstart != rstart); + INIT_LIST_HEAD(&ib_win->mappings); - iowrite32(TSI721_IBWIN_SIZE(size) << 8, + /* + * When using direct IBW mapping and have larger than requested IBW size + * we can have multiple local memory blocks mapped through the same IBW + * To handle this situation we maintain list of "clients" for such IBWs. + */ + if (direct) { + map->lstart = lstart; + list_add_tail(&map->node, &ib_win->mappings); + } + + iowrite32(TSI721_IBWIN_SIZE(ibw_size) << 8, priv->regs + TSI721_IBWIN_SZ(i)); - iowrite32(((u64)lstart >> 32), priv->regs + TSI721_IBWIN_TUA(i)); - iowrite32(((u64)lstart & TSI721_IBWIN_TLA_ADD), + iowrite32(((u64)loc_start >> 32), priv->regs + TSI721_IBWIN_TUA(i)); + iowrite32(((u64)loc_start & TSI721_IBWIN_TLA_ADD), priv->regs + TSI721_IBWIN_TLA(i)); - iowrite32(rstart >> 32, priv->regs + TSI721_IBWIN_UB(i)); - iowrite32((rstart & TSI721_IBWIN_LB_BA) | TSI721_IBWIN_LB_WEN, + iowrite32(ibw_start >> 32, priv->regs + TSI721_IBWIN_UB(i)); + iowrite32((ibw_start & TSI721_IBWIN_LB_BA) | TSI721_IBWIN_LB_WEN, priv->regs + TSI721_IBWIN_LB(i)); + + priv->ibwin_cnt--; + dev_dbg(&priv->pdev->dev, - "Configured IBWIN%d mapping (RIO_0x%llx -> PCIe_0x%llx)\n", - i, rstart, (unsigned long long)lstart); + "Configured IBWIN%d (RIO_0x%llx -> PCIe_0x%llx), size=0x%llx\n", + i, ibw_start, (unsigned long long)loc_start, ibw_size); return 0; -err_out: - spin_unlock(&priv->win_lock); +out: + kfree(map); return ret; } /** - * fsl_rio_unmap_inb_mem -- Unmapping inbound memory region. + * tsi721_rio_unmap_inb_mem -- Unmapping inbound memory region. * @mport: RapidIO master port * @lstart: Local memory space start address. */ @@ -963,22 +1037,53 @@ static void tsi721_rio_unmap_inb_mem(struct rio_mport *mport, struct tsi721_ib_win *ib_win; int i; + dev_dbg(&priv->pdev->dev, + "Unmap IBW mapped to PCIe_0x%pad", &lstart); + /* Search for matching active inbound translation window */ - spin_lock(&priv->win_lock); for (i = 0; i < TSI721_IBWIN_NUM; i++) { ib_win = &priv->ib_win[i]; - if (ib_win->active && ib_win->lstart == lstart) { + + /* Address translating IBWs must to be an exact march */ + if (!ib_win->active || + (ib_win->xlat && lstart != ib_win->lstart)) + continue; + + if (lstart >= ib_win->lstart && + lstart < (ib_win->lstart + ib_win->size)) { + + if (!ib_win->xlat) { + struct tsi721_ib_win_mapping *map; + int found = 0; + + list_for_each_entry(map, + &ib_win->mappings, node) { + if (map->lstart == lstart) { + list_del(&map->node); + kfree(map); + found = 1; + break; + } + } + + if (!found) + continue; + + if (!list_empty(&ib_win->mappings)) + break; + } + + dev_dbg(&priv->pdev->dev, "Disable IBWIN_%d", i); iowrite32(0, priv->regs + TSI721_IBWIN_LB(i)); ib_win->active = false; + priv->ibwin_cnt++; break; } } - spin_unlock(&priv->win_lock); if (i == TSI721_IBWIN_NUM) - dev_err(&priv->pdev->dev, - "IB window mapped to %llx not found\n", - (unsigned long long)lstart); + dev_dbg(&priv->pdev->dev, + "IB window mapped to %pad not found", &lstart); } /** @@ -995,7 +1100,7 @@ static void tsi721_init_sr2pc_mapping(struct tsi721_device *priv) /* Disable all SR2PC inbound windows */ for (i = 0; i < TSI721_IBWIN_NUM; i++) iowrite32(0, priv->regs + TSI721_IBWIN_LB(i)); - spin_lock_init(&priv->win_lock); + priv->ibwin_cnt = TSI721_IBWIN_NUM; } /** |