Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1679e8da authored by Alexandre Bounine's avatar Alexandre Bounine Committed by Linus Torvalds
Browse files

rapidio/tsi721: add outbound windows mapping support



Add device-specific callback functions to support outbound windows
mapping and release.

Signed-off-by: default avatarAlexandre Bounine <alexandre.bounine@idt.com>
Cc: Matt Porter <mporter@kernel.crashing.org>
Cc: Aurelien Jacquiot <a-jacquiot@ti.com>
Cc: Andre van Herk <andre.van.herk@prodrive-technologies.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 93bdaca5
Loading
Loading
Loading
Loading
+215 −7
Original line number Original line Diff line number Diff line
@@ -841,6 +841,169 @@ static void tsi721_free_irq(struct tsi721_device *priv)
	free_irq(priv->pdev->irq, (void *)priv);
	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)
 * tsi721_init_pc2sr_mapping - initializes outbound (PCIe->SRIO)
 * translation regions.
 * translation regions.
@@ -850,11 +1013,41 @@ static void tsi721_free_irq(struct tsi721_device *priv)
 */
 */
static void tsi721_init_pc2sr_mapping(struct tsi721_device *priv)
static void tsi721_init_pc2sr_mapping(struct tsi721_device *priv)
{
{
	int i;
	int i, z;
	u32 rval;


	/* Disable all PC2SR translation windows */
	/* Disable all PC2SR translation windows */
	for (i = 0; i < TSI721_OBWIN_NUM; i++)
	for (i = 0; i < TSI721_OBWIN_NUM; i++)
		iowrite32(0, priv->regs + TSI721_OBWINLB(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;
}
}


/**
/**
@@ -2418,6 +2611,8 @@ static struct rio_ops tsi721_rio_ops = {
	.unmap_inb		= tsi721_rio_unmap_inb_mem,
	.unmap_inb		= tsi721_rio_unmap_inb_mem,
	.pwenable		= tsi721_pw_enable,
	.pwenable		= tsi721_pw_enable,
	.query_mport		= tsi721_query_mport,
	.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)
static void tsi721_mport_release(struct device *dev)
@@ -2573,14 +2768,27 @@ static int tsi721_probe(struct pci_dev *pdev,
	 * It may be a good idea to keep them disabled using HW configuration
	 * It may be a good idea to keep them disabled using HW configuration
	 * to save PCI memory space.
	 * to save PCI memory space.
	 */
	 */
	if ((pci_resource_flags(pdev, BAR_2) & IORESOURCE_MEM) &&

	    (pci_resource_flags(pdev, BAR_2) & IORESOURCE_MEM_64)) {
	priv->p2r_bar[0].size = priv->p2r_bar[1].size = 0;
		dev_info(&pdev->dev, "Outbound BAR2 is not used but enabled.\n");

	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) &&
	if (pci_resource_flags(pdev, BAR_4) & IORESOURCE_MEM_64) {
	    (pci_resource_flags(pdev, BAR_4) & IORESOURCE_MEM_64)) {
		if (pci_resource_flags(pdev, BAR_4) & IORESOURCE_PREFETCH)
		dev_info(&pdev->dev, "Outbound BAR4 is not used but enabled.\n");
			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);
	err = pci_request_regions(pdev, DRV_NAME);
+20 −0
Original line number Original line Diff line number Diff line
@@ -822,6 +822,21 @@ struct tsi721_ib_win {
	struct list_head mappings;
	struct list_head mappings;
};
};


struct tsi721_obw_bar {
	u64		base;
	u64		size;
	u64		free;
};

struct tsi721_ob_win {
	u64		base;
	u32		size;
	u16		destid;
	u64		rstart;
	bool		active;
	struct tsi721_obw_bar *pbar;
};

struct tsi721_device {
struct tsi721_device {
	struct pci_dev	*pdev;
	struct pci_dev	*pdev;
	struct rio_mport mport;
	struct rio_mport mport;
@@ -861,6 +876,11 @@ struct tsi721_device {
	/* Inbound Mapping Windows */
	/* Inbound Mapping Windows */
	struct tsi721_ib_win ib_win[TSI721_IBWIN_NUM];
	struct tsi721_ib_win ib_win[TSI721_IBWIN_NUM];
	int		ibwin_cnt;
	int		ibwin_cnt;

	/* Outbound Mapping Windows */
	struct tsi721_obw_bar p2r_bar[2];
	struct tsi721_ob_win  ob_win[TSI721_OBWIN_NUM];
	int		obwin_cnt;
};
};


#ifdef CONFIG_RAPIDIO_DMA_ENGINE
#ifdef CONFIG_RAPIDIO_DMA_ENGINE