xref: /openbmc/linux/drivers/rapidio/devices/tsi721.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
248618fb4SAlexandre Bounine /*
348618fb4SAlexandre Bounine  * RapidIO mport driver for Tsi721 PCIExpress-to-SRIO bridge
448618fb4SAlexandre Bounine  *
548618fb4SAlexandre Bounine  * Copyright 2011 Integrated Device Technology, Inc.
648618fb4SAlexandre Bounine  * Alexandre Bounine <alexandre.bounine@idt.com>
748618fb4SAlexandre Bounine  * Chul Kim <chul.kim@idt.com>
848618fb4SAlexandre Bounine  */
948618fb4SAlexandre Bounine 
1048618fb4SAlexandre Bounine #include <linux/io.h>
1148618fb4SAlexandre Bounine #include <linux/errno.h>
1248618fb4SAlexandre Bounine #include <linux/init.h>
1348618fb4SAlexandre Bounine #include <linux/ioport.h>
1448618fb4SAlexandre Bounine #include <linux/kernel.h>
1548618fb4SAlexandre Bounine #include <linux/module.h>
1648618fb4SAlexandre Bounine #include <linux/pci.h>
1748618fb4SAlexandre Bounine #include <linux/rio.h>
1848618fb4SAlexandre Bounine #include <linux/rio_drv.h>
1948618fb4SAlexandre Bounine #include <linux/dma-mapping.h>
2048618fb4SAlexandre Bounine #include <linux/interrupt.h>
2148618fb4SAlexandre Bounine #include <linux/kfifo.h>
2248618fb4SAlexandre Bounine #include <linux/delay.h>
2348618fb4SAlexandre Bounine 
2448618fb4SAlexandre Bounine #include "tsi721.h"
2548618fb4SAlexandre Bounine 
2672d8a0d2SAlexandre Bounine #ifdef DEBUG
274785603bSRandy Dunlap u32 tsi_dbg_level;
284785603bSRandy Dunlap module_param_named(dbg_level, tsi_dbg_level, uint, S_IWUSR | S_IRUGO);
2972d8a0d2SAlexandre Bounine MODULE_PARM_DESC(dbg_level, "Debugging output level (default 0 = none)");
3072d8a0d2SAlexandre Bounine #endif
3172d8a0d2SAlexandre Bounine 
32cb782cddSAlexandre Bounine static int pcie_mrrs = -1;
33cb782cddSAlexandre Bounine module_param(pcie_mrrs, int, S_IRUGO);
34cb782cddSAlexandre Bounine MODULE_PARM_DESC(pcie_mrrs, "PCIe MRRS override value (0...5)");
35cb782cddSAlexandre Bounine 
36e519685dSAlexandre Bounine static u8 mbox_sel = 0x0f;
37e519685dSAlexandre Bounine module_param(mbox_sel, byte, S_IRUGO);
38e519685dSAlexandre Bounine MODULE_PARM_DESC(mbox_sel,
39e519685dSAlexandre Bounine 		 "RIO Messaging MBOX Selection Mask (default: 0x0f = all)");
40e519685dSAlexandre Bounine 
4131d1e130SIoan Nicu static DEFINE_SPINLOCK(tsi721_maint_lock);
4231d1e130SIoan Nicu 
4348618fb4SAlexandre Bounine static void tsi721_omsg_handler(struct tsi721_device *priv, int ch);
4448618fb4SAlexandre Bounine static void tsi721_imsg_handler(struct tsi721_device *priv, int ch);
4548618fb4SAlexandre Bounine 
4648618fb4SAlexandre Bounine /**
4748618fb4SAlexandre Bounine  * tsi721_lcread - read from local SREP config space
4848618fb4SAlexandre Bounine  * @mport: RapidIO master port info
4948618fb4SAlexandre Bounine  * @index: ID of RapdiIO interface
5048618fb4SAlexandre Bounine  * @offset: Offset into configuration space
5148618fb4SAlexandre Bounine  * @len: Length (in bytes) of the maintenance transaction
5248618fb4SAlexandre Bounine  * @data: Value to be read into
5348618fb4SAlexandre Bounine  *
5448618fb4SAlexandre Bounine  * Generates a local SREP space read. Returns %0 on
5548618fb4SAlexandre Bounine  * success or %-EINVAL on failure.
5648618fb4SAlexandre Bounine  */
tsi721_lcread(struct rio_mport * mport,int index,u32 offset,int len,u32 * data)5748618fb4SAlexandre Bounine static int tsi721_lcread(struct rio_mport *mport, int index, u32 offset,
5848618fb4SAlexandre Bounine 			 int len, u32 *data)
5948618fb4SAlexandre Bounine {
6048618fb4SAlexandre Bounine 	struct tsi721_device *priv = mport->priv;
6148618fb4SAlexandre Bounine 
6248618fb4SAlexandre Bounine 	if (len != sizeof(u32))
6348618fb4SAlexandre Bounine 		return -EINVAL; /* only 32-bit access is supported */
6448618fb4SAlexandre Bounine 
6548618fb4SAlexandre Bounine 	*data = ioread32(priv->regs + offset);
6648618fb4SAlexandre Bounine 
6748618fb4SAlexandre Bounine 	return 0;
6848618fb4SAlexandre Bounine }
6948618fb4SAlexandre Bounine 
7048618fb4SAlexandre Bounine /**
7148618fb4SAlexandre Bounine  * tsi721_lcwrite - write into local SREP config space
7248618fb4SAlexandre Bounine  * @mport: RapidIO master port info
7348618fb4SAlexandre Bounine  * @index: ID of RapdiIO interface
7448618fb4SAlexandre Bounine  * @offset: Offset into configuration space
7548618fb4SAlexandre Bounine  * @len: Length (in bytes) of the maintenance transaction
7648618fb4SAlexandre Bounine  * @data: Value to be written
7748618fb4SAlexandre Bounine  *
7848618fb4SAlexandre Bounine  * Generates a local write into SREP configuration space. Returns %0 on
7948618fb4SAlexandre Bounine  * success or %-EINVAL on failure.
8048618fb4SAlexandre Bounine  */
tsi721_lcwrite(struct rio_mport * mport,int index,u32 offset,int len,u32 data)8148618fb4SAlexandre Bounine static int tsi721_lcwrite(struct rio_mport *mport, int index, u32 offset,
8248618fb4SAlexandre Bounine 			  int len, u32 data)
8348618fb4SAlexandre Bounine {
8448618fb4SAlexandre Bounine 	struct tsi721_device *priv = mport->priv;
8548618fb4SAlexandre Bounine 
8648618fb4SAlexandre Bounine 	if (len != sizeof(u32))
8748618fb4SAlexandre Bounine 		return -EINVAL; /* only 32-bit access is supported */
8848618fb4SAlexandre Bounine 
8948618fb4SAlexandre Bounine 	iowrite32(data, priv->regs + offset);
9048618fb4SAlexandre Bounine 
9148618fb4SAlexandre Bounine 	return 0;
9248618fb4SAlexandre Bounine }
9348618fb4SAlexandre Bounine 
9448618fb4SAlexandre Bounine /**
9548618fb4SAlexandre Bounine  * tsi721_maint_dma - Helper function to generate RapidIO maintenance
9648618fb4SAlexandre Bounine  *                    transactions using designated Tsi721 DMA channel.
9748618fb4SAlexandre Bounine  * @priv: pointer to tsi721 private data
9848618fb4SAlexandre Bounine  * @sys_size: RapdiIO transport system size
9948618fb4SAlexandre Bounine  * @destid: Destination ID of transaction
10048618fb4SAlexandre Bounine  * @hopcount: Number of hops to target device
10148618fb4SAlexandre Bounine  * @offset: Offset into configuration space
10248618fb4SAlexandre Bounine  * @len: Length (in bytes) of the maintenance transaction
10348618fb4SAlexandre Bounine  * @data: Location to be read from or write into
10448618fb4SAlexandre Bounine  * @do_wr: Operation flag (1 == MAINT_WR)
10548618fb4SAlexandre Bounine  *
10648618fb4SAlexandre Bounine  * Generates a RapidIO maintenance transaction (Read or Write).
10748618fb4SAlexandre Bounine  * Returns %0 on success and %-EINVAL or %-EFAULT on failure.
10848618fb4SAlexandre Bounine  */
tsi721_maint_dma(struct tsi721_device * priv,u32 sys_size,u16 destid,u8 hopcount,u32 offset,int len,u32 * data,int do_wr)10948618fb4SAlexandre Bounine static int tsi721_maint_dma(struct tsi721_device *priv, u32 sys_size,
11048618fb4SAlexandre Bounine 			u16 destid, u8 hopcount, u32 offset, int len,
11148618fb4SAlexandre Bounine 			u32 *data, int do_wr)
11248618fb4SAlexandre Bounine {
1139eaa3d9bSAlexandre Bounine 	void __iomem *regs = priv->regs + TSI721_DMAC_BASE(priv->mdma.ch_id);
11448618fb4SAlexandre Bounine 	struct tsi721_dma_desc *bd_ptr;
11548618fb4SAlexandre Bounine 	u32 rd_count, swr_ptr, ch_stat;
11631d1e130SIoan Nicu 	unsigned long flags;
11748618fb4SAlexandre Bounine 	int i, err = 0;
11848618fb4SAlexandre Bounine 	u32 op = do_wr ? MAINT_WR : MAINT_RD;
11948618fb4SAlexandre Bounine 
12048618fb4SAlexandre Bounine 	if (offset > (RIO_MAINT_SPACE_SZ - len) || (len != sizeof(u32)))
12148618fb4SAlexandre Bounine 		return -EINVAL;
12248618fb4SAlexandre Bounine 
12331d1e130SIoan Nicu 	spin_lock_irqsave(&tsi721_maint_lock, flags);
12431d1e130SIoan Nicu 
1259eaa3d9bSAlexandre Bounine 	bd_ptr = priv->mdma.bd_base;
12648618fb4SAlexandre Bounine 
1279eaa3d9bSAlexandre Bounine 	rd_count = ioread32(regs + TSI721_DMAC_DRDCNT);
12848618fb4SAlexandre Bounine 
12948618fb4SAlexandre Bounine 	/* Initialize DMA descriptor */
13048618fb4SAlexandre Bounine 	bd_ptr[0].type_id = cpu_to_le32((DTYPE2 << 29) | (op << 19) | destid);
13148618fb4SAlexandre Bounine 	bd_ptr[0].bcount = cpu_to_le32((sys_size << 26) | 0x04);
13248618fb4SAlexandre Bounine 	bd_ptr[0].raddr_lo = cpu_to_le32((hopcount << 24) | offset);
13348618fb4SAlexandre Bounine 	bd_ptr[0].raddr_hi = 0;
13448618fb4SAlexandre Bounine 	if (do_wr)
13548618fb4SAlexandre Bounine 		bd_ptr[0].data[0] = cpu_to_be32p(data);
13648618fb4SAlexandre Bounine 	else
13748618fb4SAlexandre Bounine 		bd_ptr[0].data[0] = 0xffffffff;
13848618fb4SAlexandre Bounine 
13948618fb4SAlexandre Bounine 	mb();
14048618fb4SAlexandre Bounine 
14148618fb4SAlexandre Bounine 	/* Start DMA operation */
1429eaa3d9bSAlexandre Bounine 	iowrite32(rd_count + 2,	regs + TSI721_DMAC_DWRCNT);
1439eaa3d9bSAlexandre Bounine 	ioread32(regs + TSI721_DMAC_DWRCNT);
14448618fb4SAlexandre Bounine 	i = 0;
14548618fb4SAlexandre Bounine 
14648618fb4SAlexandre Bounine 	/* Wait until DMA transfer is finished */
1479eaa3d9bSAlexandre Bounine 	while ((ch_stat = ioread32(regs + TSI721_DMAC_STS))
1489eaa3d9bSAlexandre Bounine 							& TSI721_DMAC_STS_RUN) {
14948618fb4SAlexandre Bounine 		udelay(1);
15048618fb4SAlexandre Bounine 		if (++i >= 5000000) {
15172d8a0d2SAlexandre Bounine 			tsi_debug(MAINT, &priv->pdev->dev,
15272d8a0d2SAlexandre Bounine 				"DMA[%d] read timeout ch_status=%x",
15372d8a0d2SAlexandre Bounine 				priv->mdma.ch_id, ch_stat);
15448618fb4SAlexandre Bounine 			if (!do_wr)
15548618fb4SAlexandre Bounine 				*data = 0xffffffff;
15648618fb4SAlexandre Bounine 			err = -EIO;
15748618fb4SAlexandre Bounine 			goto err_out;
15848618fb4SAlexandre Bounine 		}
15948618fb4SAlexandre Bounine 	}
16048618fb4SAlexandre Bounine 
16148618fb4SAlexandre Bounine 	if (ch_stat & TSI721_DMAC_STS_ABORT) {
16248618fb4SAlexandre Bounine 		/* If DMA operation aborted due to error,
16348618fb4SAlexandre Bounine 		 * reinitialize DMA channel
16448618fb4SAlexandre Bounine 		 */
16572d8a0d2SAlexandre Bounine 		tsi_debug(MAINT, &priv->pdev->dev, "DMA ABORT ch_stat=%x",
16672d8a0d2SAlexandre Bounine 			  ch_stat);
16772d8a0d2SAlexandre Bounine 		tsi_debug(MAINT, &priv->pdev->dev,
16872d8a0d2SAlexandre Bounine 			  "OP=%d : destid=%x hc=%x off=%x",
16972d8a0d2SAlexandre Bounine 			  do_wr ? MAINT_WR : MAINT_RD,
17072d8a0d2SAlexandre Bounine 			  destid, hopcount, offset);
1719eaa3d9bSAlexandre Bounine 		iowrite32(TSI721_DMAC_INT_ALL, regs + TSI721_DMAC_INT);
1729eaa3d9bSAlexandre Bounine 		iowrite32(TSI721_DMAC_CTL_INIT, regs + TSI721_DMAC_CTL);
17348618fb4SAlexandre Bounine 		udelay(10);
1749eaa3d9bSAlexandre Bounine 		iowrite32(0, regs + TSI721_DMAC_DWRCNT);
17548618fb4SAlexandre Bounine 		udelay(1);
17648618fb4SAlexandre Bounine 		if (!do_wr)
17748618fb4SAlexandre Bounine 			*data = 0xffffffff;
17848618fb4SAlexandre Bounine 		err = -EIO;
17948618fb4SAlexandre Bounine 		goto err_out;
18048618fb4SAlexandre Bounine 	}
18148618fb4SAlexandre Bounine 
18248618fb4SAlexandre Bounine 	if (!do_wr)
18348618fb4SAlexandre Bounine 		*data = be32_to_cpu(bd_ptr[0].data[0]);
18448618fb4SAlexandre Bounine 
18548618fb4SAlexandre Bounine 	/*
18648618fb4SAlexandre Bounine 	 * Update descriptor status FIFO RD pointer.
18748618fb4SAlexandre Bounine 	 * NOTE: Skipping check and clear FIFO entries because we are waiting
18848618fb4SAlexandre Bounine 	 * for transfer to be completed.
18948618fb4SAlexandre Bounine 	 */
1909eaa3d9bSAlexandre Bounine 	swr_ptr = ioread32(regs + TSI721_DMAC_DSWP);
1919eaa3d9bSAlexandre Bounine 	iowrite32(swr_ptr, regs + TSI721_DMAC_DSRP);
19231d1e130SIoan Nicu 
19348618fb4SAlexandre Bounine err_out:
19431d1e130SIoan Nicu 	spin_unlock_irqrestore(&tsi721_maint_lock, flags);
19548618fb4SAlexandre Bounine 
19648618fb4SAlexandre Bounine 	return err;
19748618fb4SAlexandre Bounine }
19848618fb4SAlexandre Bounine 
19948618fb4SAlexandre Bounine /**
20048618fb4SAlexandre Bounine  * tsi721_cread_dma - Generate a RapidIO maintenance read transaction
20148618fb4SAlexandre Bounine  *                    using Tsi721 BDMA engine.
20248618fb4SAlexandre Bounine  * @mport: RapidIO master port control structure
20348618fb4SAlexandre Bounine  * @index: ID of RapdiIO interface
20448618fb4SAlexandre Bounine  * @destid: Destination ID of transaction
20548618fb4SAlexandre Bounine  * @hopcount: Number of hops to target device
20648618fb4SAlexandre Bounine  * @offset: Offset into configuration space
20748618fb4SAlexandre Bounine  * @len: Length (in bytes) of the maintenance transaction
20848618fb4SAlexandre Bounine  * @val: Location to be read into
20948618fb4SAlexandre Bounine  *
21048618fb4SAlexandre Bounine  * Generates a RapidIO maintenance read transaction.
21148618fb4SAlexandre Bounine  * Returns %0 on success and %-EINVAL or %-EFAULT on failure.
21248618fb4SAlexandre Bounine  */
tsi721_cread_dma(struct rio_mport * mport,int index,u16 destid,u8 hopcount,u32 offset,int len,u32 * data)21348618fb4SAlexandre Bounine static int tsi721_cread_dma(struct rio_mport *mport, int index, u16 destid,
21448618fb4SAlexandre Bounine 			u8 hopcount, u32 offset, int len, u32 *data)
21548618fb4SAlexandre Bounine {
21648618fb4SAlexandre Bounine 	struct tsi721_device *priv = mport->priv;
21748618fb4SAlexandre Bounine 
21848618fb4SAlexandre Bounine 	return tsi721_maint_dma(priv, mport->sys_size, destid, hopcount,
21948618fb4SAlexandre Bounine 				offset, len, data, 0);
22048618fb4SAlexandre Bounine }
22148618fb4SAlexandre Bounine 
22248618fb4SAlexandre Bounine /**
22348618fb4SAlexandre Bounine  * tsi721_cwrite_dma - Generate a RapidIO maintenance write transaction
22448618fb4SAlexandre Bounine  *                     using Tsi721 BDMA engine
22548618fb4SAlexandre Bounine  * @mport: RapidIO master port control structure
22648618fb4SAlexandre Bounine  * @index: ID of RapdiIO interface
22748618fb4SAlexandre Bounine  * @destid: Destination ID of transaction
22848618fb4SAlexandre Bounine  * @hopcount: Number of hops to target device
22948618fb4SAlexandre Bounine  * @offset: Offset into configuration space
23048618fb4SAlexandre Bounine  * @len: Length (in bytes) of the maintenance transaction
23148618fb4SAlexandre Bounine  * @val: Value to be written
23248618fb4SAlexandre Bounine  *
23348618fb4SAlexandre Bounine  * Generates a RapidIO maintenance write transaction.
23448618fb4SAlexandre Bounine  * Returns %0 on success and %-EINVAL or %-EFAULT on failure.
23548618fb4SAlexandre Bounine  */
tsi721_cwrite_dma(struct rio_mport * mport,int index,u16 destid,u8 hopcount,u32 offset,int len,u32 data)23648618fb4SAlexandre Bounine static int tsi721_cwrite_dma(struct rio_mport *mport, int index, u16 destid,
23748618fb4SAlexandre Bounine 			 u8 hopcount, u32 offset, int len, u32 data)
23848618fb4SAlexandre Bounine {
23948618fb4SAlexandre Bounine 	struct tsi721_device *priv = mport->priv;
24048618fb4SAlexandre Bounine 	u32 temp = data;
24148618fb4SAlexandre Bounine 
24248618fb4SAlexandre Bounine 	return tsi721_maint_dma(priv, mport->sys_size, destid, hopcount,
24348618fb4SAlexandre Bounine 				offset, len, &temp, 1);
24448618fb4SAlexandre Bounine }
24548618fb4SAlexandre Bounine 
24648618fb4SAlexandre Bounine /**
24748618fb4SAlexandre Bounine  * tsi721_pw_handler - Tsi721 inbound port-write interrupt handler
248748353ccSAlexandre Bounine  * @priv:  tsi721 device private structure
24948618fb4SAlexandre Bounine  *
25048618fb4SAlexandre Bounine  * Handles inbound port-write interrupts. Copies PW message from an internal
25148618fb4SAlexandre Bounine  * buffer into PW message FIFO and schedules deferred routine to process
25248618fb4SAlexandre Bounine  * queued messages.
25348618fb4SAlexandre Bounine  */
25448618fb4SAlexandre Bounine static int
tsi721_pw_handler(struct tsi721_device * priv)255748353ccSAlexandre Bounine tsi721_pw_handler(struct tsi721_device *priv)
25648618fb4SAlexandre Bounine {
25748618fb4SAlexandre Bounine 	u32 pw_stat;
25848618fb4SAlexandre Bounine 	u32 pw_buf[TSI721_RIO_PW_MSG_SIZE/sizeof(u32)];
25948618fb4SAlexandre Bounine 
26048618fb4SAlexandre Bounine 
26148618fb4SAlexandre Bounine 	pw_stat = ioread32(priv->regs + TSI721_RIO_PW_RX_STAT);
26248618fb4SAlexandre Bounine 
26348618fb4SAlexandre Bounine 	if (pw_stat & TSI721_RIO_PW_RX_STAT_PW_VAL) {
26448618fb4SAlexandre Bounine 		pw_buf[0] = ioread32(priv->regs + TSI721_RIO_PW_RX_CAPT(0));
26548618fb4SAlexandre Bounine 		pw_buf[1] = ioread32(priv->regs + TSI721_RIO_PW_RX_CAPT(1));
26648618fb4SAlexandre Bounine 		pw_buf[2] = ioread32(priv->regs + TSI721_RIO_PW_RX_CAPT(2));
26748618fb4SAlexandre Bounine 		pw_buf[3] = ioread32(priv->regs + TSI721_RIO_PW_RX_CAPT(3));
26848618fb4SAlexandre Bounine 
26948618fb4SAlexandre Bounine 		/* Queue PW message (if there is room in FIFO),
27048618fb4SAlexandre Bounine 		 * otherwise discard it.
27148618fb4SAlexandre Bounine 		 */
27248618fb4SAlexandre Bounine 		spin_lock(&priv->pw_fifo_lock);
27348618fb4SAlexandre Bounine 		if (kfifo_avail(&priv->pw_fifo) >= TSI721_RIO_PW_MSG_SIZE)
27448618fb4SAlexandre Bounine 			kfifo_in(&priv->pw_fifo, pw_buf,
27548618fb4SAlexandre Bounine 						TSI721_RIO_PW_MSG_SIZE);
27648618fb4SAlexandre Bounine 		else
27748618fb4SAlexandre Bounine 			priv->pw_discard_count++;
27848618fb4SAlexandre Bounine 		spin_unlock(&priv->pw_fifo_lock);
27948618fb4SAlexandre Bounine 	}
28048618fb4SAlexandre Bounine 
28148618fb4SAlexandre Bounine 	/* Clear pending PW interrupts */
28248618fb4SAlexandre Bounine 	iowrite32(TSI721_RIO_PW_RX_STAT_PW_DISC | TSI721_RIO_PW_RX_STAT_PW_VAL,
28348618fb4SAlexandre Bounine 		  priv->regs + TSI721_RIO_PW_RX_STAT);
28448618fb4SAlexandre Bounine 
28548618fb4SAlexandre Bounine 	schedule_work(&priv->pw_work);
28648618fb4SAlexandre Bounine 
28748618fb4SAlexandre Bounine 	return 0;
28848618fb4SAlexandre Bounine }
28948618fb4SAlexandre Bounine 
tsi721_pw_dpc(struct work_struct * work)29048618fb4SAlexandre Bounine static void tsi721_pw_dpc(struct work_struct *work)
29148618fb4SAlexandre Bounine {
29248618fb4SAlexandre Bounine 	struct tsi721_device *priv = container_of(work, struct tsi721_device,
29348618fb4SAlexandre Bounine 						    pw_work);
2949a0b0627SAlexandre Bounine 	union rio_pw_msg pwmsg;
29548618fb4SAlexandre Bounine 
29648618fb4SAlexandre Bounine 	/*
29748618fb4SAlexandre Bounine 	 * Process port-write messages
29848618fb4SAlexandre Bounine 	 */
2999a0b0627SAlexandre Bounine 	while (kfifo_out_spinlocked(&priv->pw_fifo, (unsigned char *)&pwmsg,
30048618fb4SAlexandre Bounine 			 TSI721_RIO_PW_MSG_SIZE, &priv->pw_fifo_lock)) {
30148618fb4SAlexandre Bounine 		/* Pass the port-write message to RIO core for processing */
3029a0b0627SAlexandre Bounine 		rio_inb_pwrite_handler(&priv->mport, &pwmsg);
30348618fb4SAlexandre Bounine 	}
30448618fb4SAlexandre Bounine }
30548618fb4SAlexandre Bounine 
30648618fb4SAlexandre Bounine /**
30748618fb4SAlexandre Bounine  * tsi721_pw_enable - enable/disable port-write interface init
30848618fb4SAlexandre Bounine  * @mport: Master port implementing the port write unit
30948618fb4SAlexandre Bounine  * @enable:    1=enable; 0=disable port-write message handling
31048618fb4SAlexandre Bounine  */
tsi721_pw_enable(struct rio_mport * mport,int enable)31148618fb4SAlexandre Bounine static int tsi721_pw_enable(struct rio_mport *mport, int enable)
31248618fb4SAlexandre Bounine {
31348618fb4SAlexandre Bounine 	struct tsi721_device *priv = mport->priv;
31448618fb4SAlexandre Bounine 	u32 rval;
31548618fb4SAlexandre Bounine 
31648618fb4SAlexandre Bounine 	rval = ioread32(priv->regs + TSI721_RIO_EM_INT_ENABLE);
31748618fb4SAlexandre Bounine 
31848618fb4SAlexandre Bounine 	if (enable)
31948618fb4SAlexandre Bounine 		rval |= TSI721_RIO_EM_INT_ENABLE_PW_RX;
32048618fb4SAlexandre Bounine 	else
32148618fb4SAlexandre Bounine 		rval &= ~TSI721_RIO_EM_INT_ENABLE_PW_RX;
32248618fb4SAlexandre Bounine 
32348618fb4SAlexandre Bounine 	/* Clear pending PW interrupts */
32448618fb4SAlexandre Bounine 	iowrite32(TSI721_RIO_PW_RX_STAT_PW_DISC | TSI721_RIO_PW_RX_STAT_PW_VAL,
32548618fb4SAlexandre Bounine 		  priv->regs + TSI721_RIO_PW_RX_STAT);
32648618fb4SAlexandre Bounine 	/* Update enable bits */
32748618fb4SAlexandre Bounine 	iowrite32(rval, priv->regs + TSI721_RIO_EM_INT_ENABLE);
32848618fb4SAlexandre Bounine 
32948618fb4SAlexandre Bounine 	return 0;
33048618fb4SAlexandre Bounine }
33148618fb4SAlexandre Bounine 
33248618fb4SAlexandre Bounine /**
33348618fb4SAlexandre Bounine  * tsi721_dsend - Send a RapidIO doorbell
33448618fb4SAlexandre Bounine  * @mport: RapidIO master port info
33548618fb4SAlexandre Bounine  * @index: ID of RapidIO interface
33648618fb4SAlexandre Bounine  * @destid: Destination ID of target device
33748618fb4SAlexandre Bounine  * @data: 16-bit info field of RapidIO doorbell
33848618fb4SAlexandre Bounine  *
33948618fb4SAlexandre Bounine  * Sends a RapidIO doorbell message. Always returns %0.
34048618fb4SAlexandre Bounine  */
tsi721_dsend(struct rio_mport * mport,int index,u16 destid,u16 data)34148618fb4SAlexandre Bounine static int tsi721_dsend(struct rio_mport *mport, int index,
34248618fb4SAlexandre Bounine 			u16 destid, u16 data)
34348618fb4SAlexandre Bounine {
34448618fb4SAlexandre Bounine 	struct tsi721_device *priv = mport->priv;
34548618fb4SAlexandre Bounine 	u32 offset;
34648618fb4SAlexandre Bounine 
34748618fb4SAlexandre Bounine 	offset = (((mport->sys_size) ? RIO_TT_CODE_16 : RIO_TT_CODE_8) << 18) |
34848618fb4SAlexandre Bounine 		 (destid << 2);
34948618fb4SAlexandre Bounine 
35072d8a0d2SAlexandre Bounine 	tsi_debug(DBELL, &priv->pdev->dev,
35172d8a0d2SAlexandre Bounine 		  "Send Doorbell 0x%04x to destID 0x%x", data, destid);
35248618fb4SAlexandre Bounine 	iowrite16be(data, priv->odb_base + offset);
35348618fb4SAlexandre Bounine 
35448618fb4SAlexandre Bounine 	return 0;
35548618fb4SAlexandre Bounine }
35648618fb4SAlexandre Bounine 
35748618fb4SAlexandre Bounine /**
35848618fb4SAlexandre Bounine  * tsi721_dbell_handler - Tsi721 doorbell interrupt handler
359748353ccSAlexandre Bounine  * @priv: tsi721 device-specific data structure
36048618fb4SAlexandre Bounine  *
36148618fb4SAlexandre Bounine  * Handles inbound doorbell interrupts. Copies doorbell entry from an internal
36248618fb4SAlexandre Bounine  * buffer into DB message FIFO and schedules deferred  routine to process
36348618fb4SAlexandre Bounine  * queued DBs.
36448618fb4SAlexandre Bounine  */
36548618fb4SAlexandre Bounine static int
tsi721_dbell_handler(struct tsi721_device * priv)366748353ccSAlexandre Bounine tsi721_dbell_handler(struct tsi721_device *priv)
36748618fb4SAlexandre Bounine {
36848618fb4SAlexandre Bounine 	u32 regval;
36948618fb4SAlexandre Bounine 
37048618fb4SAlexandre Bounine 	/* Disable IDB interrupts */
37148618fb4SAlexandre Bounine 	regval = ioread32(priv->regs + TSI721_SR_CHINTE(IDB_QUEUE));
37248618fb4SAlexandre Bounine 	regval &= ~TSI721_SR_CHINT_IDBQRCV;
37348618fb4SAlexandre Bounine 	iowrite32(regval,
37448618fb4SAlexandre Bounine 		priv->regs + TSI721_SR_CHINTE(IDB_QUEUE));
37548618fb4SAlexandre Bounine 
37648618fb4SAlexandre Bounine 	schedule_work(&priv->idb_work);
37748618fb4SAlexandre Bounine 
37848618fb4SAlexandre Bounine 	return 0;
37948618fb4SAlexandre Bounine }
38048618fb4SAlexandre Bounine 
tsi721_db_dpc(struct work_struct * work)38148618fb4SAlexandre Bounine static void tsi721_db_dpc(struct work_struct *work)
38248618fb4SAlexandre Bounine {
38348618fb4SAlexandre Bounine 	struct tsi721_device *priv = container_of(work, struct tsi721_device,
38448618fb4SAlexandre Bounine 						    idb_work);
38548618fb4SAlexandre Bounine 	struct rio_mport *mport;
38648618fb4SAlexandre Bounine 	struct rio_dbell *dbell;
38748618fb4SAlexandre Bounine 	int found = 0;
38848618fb4SAlexandre Bounine 	u32 wr_ptr, rd_ptr;
38948618fb4SAlexandre Bounine 	u64 *idb_entry;
39048618fb4SAlexandre Bounine 	u32 regval;
39148618fb4SAlexandre Bounine 	union {
39248618fb4SAlexandre Bounine 		u64 msg;
39348618fb4SAlexandre Bounine 		u8  bytes[8];
39448618fb4SAlexandre Bounine 	} idb;
39548618fb4SAlexandre Bounine 
39648618fb4SAlexandre Bounine 	/*
39748618fb4SAlexandre Bounine 	 * Process queued inbound doorbells
39848618fb4SAlexandre Bounine 	 */
399748353ccSAlexandre Bounine 	mport = &priv->mport;
40048618fb4SAlexandre Bounine 
401b24823e6SAlexandre Bounine 	wr_ptr = ioread32(priv->regs + TSI721_IDQ_WP(IDB_QUEUE)) % IDB_QSIZE;
402b24823e6SAlexandre Bounine 	rd_ptr = ioread32(priv->regs + TSI721_IDQ_RP(IDB_QUEUE)) % IDB_QSIZE;
40348618fb4SAlexandre Bounine 
40448618fb4SAlexandre Bounine 	while (wr_ptr != rd_ptr) {
40548618fb4SAlexandre Bounine 		idb_entry = (u64 *)(priv->idb_base +
40648618fb4SAlexandre Bounine 					(TSI721_IDB_ENTRY_SIZE * rd_ptr));
40748618fb4SAlexandre Bounine 		rd_ptr++;
408b24823e6SAlexandre Bounine 		rd_ptr %= IDB_QSIZE;
40948618fb4SAlexandre Bounine 		idb.msg = *idb_entry;
41048618fb4SAlexandre Bounine 		*idb_entry = 0;
41148618fb4SAlexandre Bounine 
41248618fb4SAlexandre Bounine 		/* Process one doorbell */
41348618fb4SAlexandre Bounine 		list_for_each_entry(dbell, &mport->dbells, node) {
41448618fb4SAlexandre Bounine 			if ((dbell->res->start <= DBELL_INF(idb.bytes)) &&
41548618fb4SAlexandre Bounine 			    (dbell->res->end >= DBELL_INF(idb.bytes))) {
41648618fb4SAlexandre Bounine 				found = 1;
41748618fb4SAlexandre Bounine 				break;
41848618fb4SAlexandre Bounine 			}
41948618fb4SAlexandre Bounine 		}
42048618fb4SAlexandre Bounine 
42148618fb4SAlexandre Bounine 		if (found) {
42248618fb4SAlexandre Bounine 			dbell->dinb(mport, dbell->dev_id, DBELL_SID(idb.bytes),
42348618fb4SAlexandre Bounine 				    DBELL_TID(idb.bytes), DBELL_INF(idb.bytes));
42448618fb4SAlexandre Bounine 		} else {
42572d8a0d2SAlexandre Bounine 			tsi_debug(DBELL, &priv->pdev->dev,
42672d8a0d2SAlexandre Bounine 				  "spurious IDB sid %2.2x tid %2.2x info %4.4x",
42772d8a0d2SAlexandre Bounine 				  DBELL_SID(idb.bytes), DBELL_TID(idb.bytes),
42872d8a0d2SAlexandre Bounine 				  DBELL_INF(idb.bytes));
42948618fb4SAlexandre Bounine 		}
4303670e7e1SAlexandre Bounine 
4313670e7e1SAlexandre Bounine 		wr_ptr = ioread32(priv->regs +
4323670e7e1SAlexandre Bounine 				  TSI721_IDQ_WP(IDB_QUEUE)) % IDB_QSIZE;
43348618fb4SAlexandre Bounine 	}
43448618fb4SAlexandre Bounine 
43548618fb4SAlexandre Bounine 	iowrite32(rd_ptr & (IDB_QSIZE - 1),
43648618fb4SAlexandre Bounine 		priv->regs + TSI721_IDQ_RP(IDB_QUEUE));
43748618fb4SAlexandre Bounine 
43848618fb4SAlexandre Bounine 	/* Re-enable IDB interrupts */
43948618fb4SAlexandre Bounine 	regval = ioread32(priv->regs + TSI721_SR_CHINTE(IDB_QUEUE));
44048618fb4SAlexandre Bounine 	regval |= TSI721_SR_CHINT_IDBQRCV;
44148618fb4SAlexandre Bounine 	iowrite32(regval,
44248618fb4SAlexandre Bounine 		priv->regs + TSI721_SR_CHINTE(IDB_QUEUE));
4433670e7e1SAlexandre Bounine 
4443670e7e1SAlexandre Bounine 	wr_ptr = ioread32(priv->regs + TSI721_IDQ_WP(IDB_QUEUE)) % IDB_QSIZE;
4453670e7e1SAlexandre Bounine 	if (wr_ptr != rd_ptr)
4463670e7e1SAlexandre Bounine 		schedule_work(&priv->idb_work);
44748618fb4SAlexandre Bounine }
44848618fb4SAlexandre Bounine 
44948618fb4SAlexandre Bounine /**
45048618fb4SAlexandre Bounine  * tsi721_irqhandler - Tsi721 interrupt handler
45148618fb4SAlexandre Bounine  * @irq: Linux interrupt number
452748353ccSAlexandre Bounine  * @ptr: Pointer to interrupt-specific data (tsi721_device structure)
45348618fb4SAlexandre Bounine  *
45448618fb4SAlexandre Bounine  * Handles Tsi721 interrupts signaled using MSI and INTA. Checks reported
45548618fb4SAlexandre Bounine  * interrupt events and calls an event-specific handler(s).
45648618fb4SAlexandre Bounine  */
tsi721_irqhandler(int irq,void * ptr)45748618fb4SAlexandre Bounine static irqreturn_t tsi721_irqhandler(int irq, void *ptr)
45848618fb4SAlexandre Bounine {
459748353ccSAlexandre Bounine 	struct tsi721_device *priv = (struct tsi721_device *)ptr;
46048618fb4SAlexandre Bounine 	u32 dev_int;
46148618fb4SAlexandre Bounine 	u32 dev_ch_int;
46248618fb4SAlexandre Bounine 	u32 intval;
46348618fb4SAlexandre Bounine 	u32 ch_inte;
46448618fb4SAlexandre Bounine 
4651ccc819dSAlexandre Bounine 	/* For MSI mode disable all device-level interrupts */
4661ccc819dSAlexandre Bounine 	if (priv->flags & TSI721_USING_MSI)
4671ccc819dSAlexandre Bounine 		iowrite32(0, priv->regs + TSI721_DEV_INTE);
4681ccc819dSAlexandre Bounine 
46948618fb4SAlexandre Bounine 	dev_int = ioread32(priv->regs + TSI721_DEV_INT);
47048618fb4SAlexandre Bounine 	if (!dev_int)
47148618fb4SAlexandre Bounine 		return IRQ_NONE;
47248618fb4SAlexandre Bounine 
47348618fb4SAlexandre Bounine 	dev_ch_int = ioread32(priv->regs + TSI721_DEV_CHAN_INT);
47448618fb4SAlexandre Bounine 
47548618fb4SAlexandre Bounine 	if (dev_int & TSI721_DEV_INT_SR2PC_CH) {
47648618fb4SAlexandre Bounine 		/* Service SR2PC Channel interrupts */
47748618fb4SAlexandre Bounine 		if (dev_ch_int & TSI721_INT_SR2PC_CHAN(IDB_QUEUE)) {
47848618fb4SAlexandre Bounine 			/* Service Inbound Doorbell interrupt */
47948618fb4SAlexandre Bounine 			intval = ioread32(priv->regs +
48048618fb4SAlexandre Bounine 						TSI721_SR_CHINT(IDB_QUEUE));
48148618fb4SAlexandre Bounine 			if (intval & TSI721_SR_CHINT_IDBQRCV)
482748353ccSAlexandre Bounine 				tsi721_dbell_handler(priv);
48348618fb4SAlexandre Bounine 			else
48472d8a0d2SAlexandre Bounine 				tsi_info(&priv->pdev->dev,
48572d8a0d2SAlexandre Bounine 					"Unsupported SR_CH_INT %x", intval);
48648618fb4SAlexandre Bounine 
48748618fb4SAlexandre Bounine 			/* Clear interrupts */
48848618fb4SAlexandre Bounine 			iowrite32(intval,
48948618fb4SAlexandre Bounine 				priv->regs + TSI721_SR_CHINT(IDB_QUEUE));
49048618fb4SAlexandre Bounine 			ioread32(priv->regs + TSI721_SR_CHINT(IDB_QUEUE));
49148618fb4SAlexandre Bounine 		}
49248618fb4SAlexandre Bounine 	}
49348618fb4SAlexandre Bounine 
49448618fb4SAlexandre Bounine 	if (dev_int & TSI721_DEV_INT_SMSG_CH) {
49548618fb4SAlexandre Bounine 		int ch;
49648618fb4SAlexandre Bounine 
49748618fb4SAlexandre Bounine 		/*
49848618fb4SAlexandre Bounine 		 * Service channel interrupts from Messaging Engine
49948618fb4SAlexandre Bounine 		 */
50048618fb4SAlexandre Bounine 
50148618fb4SAlexandre Bounine 		if (dev_ch_int & TSI721_INT_IMSG_CHAN_M) { /* Inbound Msg */
50248618fb4SAlexandre Bounine 			/* Disable signaled OB MSG Channel interrupts */
50348618fb4SAlexandre Bounine 			ch_inte = ioread32(priv->regs + TSI721_DEV_CHAN_INTE);
50448618fb4SAlexandre Bounine 			ch_inte &= ~(dev_ch_int & TSI721_INT_IMSG_CHAN_M);
50548618fb4SAlexandre Bounine 			iowrite32(ch_inte, priv->regs + TSI721_DEV_CHAN_INTE);
50648618fb4SAlexandre Bounine 
50748618fb4SAlexandre Bounine 			/*
50848618fb4SAlexandre Bounine 			 * Process Inbound Message interrupt for each MBOX
50948618fb4SAlexandre Bounine 			 */
51048618fb4SAlexandre Bounine 			for (ch = 4; ch < RIO_MAX_MBOX + 4; ch++) {
51148618fb4SAlexandre Bounine 				if (!(dev_ch_int & TSI721_INT_IMSG_CHAN(ch)))
51248618fb4SAlexandre Bounine 					continue;
51348618fb4SAlexandre Bounine 				tsi721_imsg_handler(priv, ch);
51448618fb4SAlexandre Bounine 			}
51548618fb4SAlexandre Bounine 		}
51648618fb4SAlexandre Bounine 
51748618fb4SAlexandre Bounine 		if (dev_ch_int & TSI721_INT_OMSG_CHAN_M) { /* Outbound Msg */
51848618fb4SAlexandre Bounine 			/* Disable signaled OB MSG Channel interrupts */
51948618fb4SAlexandre Bounine 			ch_inte = ioread32(priv->regs + TSI721_DEV_CHAN_INTE);
52048618fb4SAlexandre Bounine 			ch_inte &= ~(dev_ch_int & TSI721_INT_OMSG_CHAN_M);
52148618fb4SAlexandre Bounine 			iowrite32(ch_inte, priv->regs + TSI721_DEV_CHAN_INTE);
52248618fb4SAlexandre Bounine 
52348618fb4SAlexandre Bounine 			/*
52448618fb4SAlexandre Bounine 			 * Process Outbound Message interrupts for each MBOX
52548618fb4SAlexandre Bounine 			 */
52648618fb4SAlexandre Bounine 
52748618fb4SAlexandre Bounine 			for (ch = 0; ch < RIO_MAX_MBOX; ch++) {
52848618fb4SAlexandre Bounine 				if (!(dev_ch_int & TSI721_INT_OMSG_CHAN(ch)))
52948618fb4SAlexandre Bounine 					continue;
53048618fb4SAlexandre Bounine 				tsi721_omsg_handler(priv, ch);
53148618fb4SAlexandre Bounine 			}
53248618fb4SAlexandre Bounine 		}
53348618fb4SAlexandre Bounine 	}
53448618fb4SAlexandre Bounine 
53548618fb4SAlexandre Bounine 	if (dev_int & TSI721_DEV_INT_SRIO) {
53648618fb4SAlexandre Bounine 		/* Service SRIO MAC interrupts */
53748618fb4SAlexandre Bounine 		intval = ioread32(priv->regs + TSI721_RIO_EM_INT_STAT);
53848618fb4SAlexandre Bounine 		if (intval & TSI721_RIO_EM_INT_STAT_PW_RX)
539748353ccSAlexandre Bounine 			tsi721_pw_handler(priv);
54048618fb4SAlexandre Bounine 	}
54148618fb4SAlexandre Bounine 
5429eaa3d9bSAlexandre Bounine #ifdef CONFIG_RAPIDIO_DMA_ENGINE
5439eaa3d9bSAlexandre Bounine 	if (dev_int & TSI721_DEV_INT_BDMA_CH) {
5449eaa3d9bSAlexandre Bounine 		int ch;
5459eaa3d9bSAlexandre Bounine 
5469eaa3d9bSAlexandre Bounine 		if (dev_ch_int & TSI721_INT_BDMA_CHAN_M) {
54772d8a0d2SAlexandre Bounine 			tsi_debug(DMA, &priv->pdev->dev,
54872d8a0d2SAlexandre Bounine 				  "IRQ from DMA channel 0x%08x", dev_ch_int);
5499eaa3d9bSAlexandre Bounine 
5509eaa3d9bSAlexandre Bounine 			for (ch = 0; ch < TSI721_DMA_MAXCH; ch++) {
5519eaa3d9bSAlexandre Bounine 				if (!(dev_ch_int & TSI721_INT_BDMA_CHAN(ch)))
5529eaa3d9bSAlexandre Bounine 					continue;
5539eaa3d9bSAlexandre Bounine 				tsi721_bdma_handler(&priv->bdma[ch]);
5549eaa3d9bSAlexandre Bounine 			}
5559eaa3d9bSAlexandre Bounine 		}
5569eaa3d9bSAlexandre Bounine 	}
5579eaa3d9bSAlexandre Bounine #endif
5581ccc819dSAlexandre Bounine 
5591ccc819dSAlexandre Bounine 	/* For MSI mode re-enable device-level interrupts */
5601ccc819dSAlexandre Bounine 	if (priv->flags & TSI721_USING_MSI) {
5611ccc819dSAlexandre Bounine 		dev_int = TSI721_DEV_INT_SR2PC_CH | TSI721_DEV_INT_SRIO |
5621ccc819dSAlexandre Bounine 			TSI721_DEV_INT_SMSG_CH | TSI721_DEV_INT_BDMA_CH;
5631ccc819dSAlexandre Bounine 		iowrite32(dev_int, priv->regs + TSI721_DEV_INTE);
5641ccc819dSAlexandre Bounine 	}
5651ccc819dSAlexandre Bounine 
56648618fb4SAlexandre Bounine 	return IRQ_HANDLED;
56748618fb4SAlexandre Bounine }
56848618fb4SAlexandre Bounine 
tsi721_interrupts_init(struct tsi721_device * priv)56948618fb4SAlexandre Bounine static void tsi721_interrupts_init(struct tsi721_device *priv)
57048618fb4SAlexandre Bounine {
57148618fb4SAlexandre Bounine 	u32 intr;
57248618fb4SAlexandre Bounine 
57348618fb4SAlexandre Bounine 	/* Enable IDB interrupts */
57448618fb4SAlexandre Bounine 	iowrite32(TSI721_SR_CHINT_ALL,
57548618fb4SAlexandre Bounine 		priv->regs + TSI721_SR_CHINT(IDB_QUEUE));
57648618fb4SAlexandre Bounine 	iowrite32(TSI721_SR_CHINT_IDBQRCV,
57748618fb4SAlexandre Bounine 		priv->regs + TSI721_SR_CHINTE(IDB_QUEUE));
57848618fb4SAlexandre Bounine 
57948618fb4SAlexandre Bounine 	/* Enable SRIO MAC interrupts */
58048618fb4SAlexandre Bounine 	iowrite32(TSI721_RIO_EM_DEV_INT_EN_INT,
58148618fb4SAlexandre Bounine 		priv->regs + TSI721_RIO_EM_DEV_INT_EN);
58248618fb4SAlexandre Bounine 
5839eaa3d9bSAlexandre Bounine 	/* Enable interrupts from channels in use */
5849eaa3d9bSAlexandre Bounine #ifdef CONFIG_RAPIDIO_DMA_ENGINE
5859eaa3d9bSAlexandre Bounine 	intr = TSI721_INT_SR2PC_CHAN(IDB_QUEUE) |
5869eaa3d9bSAlexandre Bounine 		(TSI721_INT_BDMA_CHAN_M &
5879eaa3d9bSAlexandre Bounine 		 ~TSI721_INT_BDMA_CHAN(TSI721_DMACH_MAINT));
5889eaa3d9bSAlexandre Bounine #else
5899eaa3d9bSAlexandre Bounine 	intr = TSI721_INT_SR2PC_CHAN(IDB_QUEUE);
5909eaa3d9bSAlexandre Bounine #endif
5919eaa3d9bSAlexandre Bounine 	iowrite32(intr,	priv->regs + TSI721_DEV_CHAN_INTE);
5929eaa3d9bSAlexandre Bounine 
59348618fb4SAlexandre Bounine 	if (priv->flags & TSI721_USING_MSIX)
59448618fb4SAlexandre Bounine 		intr = TSI721_DEV_INT_SRIO;
59548618fb4SAlexandre Bounine 	else
59648618fb4SAlexandre Bounine 		intr = TSI721_DEV_INT_SR2PC_CH | TSI721_DEV_INT_SRIO |
5979eaa3d9bSAlexandre Bounine 			TSI721_DEV_INT_SMSG_CH | TSI721_DEV_INT_BDMA_CH;
59848618fb4SAlexandre Bounine 
59948618fb4SAlexandre Bounine 	iowrite32(intr, priv->regs + TSI721_DEV_INTE);
60048618fb4SAlexandre Bounine 	ioread32(priv->regs + TSI721_DEV_INTE);
60148618fb4SAlexandre Bounine }
60248618fb4SAlexandre Bounine 
60348618fb4SAlexandre Bounine #ifdef CONFIG_PCI_MSI
60448618fb4SAlexandre Bounine /**
60548618fb4SAlexandre Bounine  * tsi721_omsg_msix - MSI-X interrupt handler for outbound messaging
60648618fb4SAlexandre Bounine  * @irq: Linux interrupt number
607748353ccSAlexandre Bounine  * @ptr: Pointer to interrupt-specific data (tsi721_device structure)
60848618fb4SAlexandre Bounine  *
60948618fb4SAlexandre Bounine  * Handles outbound messaging interrupts signaled using MSI-X.
61048618fb4SAlexandre Bounine  */
tsi721_omsg_msix(int irq,void * ptr)61148618fb4SAlexandre Bounine static irqreturn_t tsi721_omsg_msix(int irq, void *ptr)
61248618fb4SAlexandre Bounine {
613748353ccSAlexandre Bounine 	struct tsi721_device *priv = (struct tsi721_device *)ptr;
61448618fb4SAlexandre Bounine 	int mbox;
61548618fb4SAlexandre Bounine 
61648618fb4SAlexandre Bounine 	mbox = (irq - priv->msix[TSI721_VECT_OMB0_DONE].vector) % RIO_MAX_MBOX;
61748618fb4SAlexandre Bounine 	tsi721_omsg_handler(priv, mbox);
61848618fb4SAlexandre Bounine 	return IRQ_HANDLED;
61948618fb4SAlexandre Bounine }
62048618fb4SAlexandre Bounine 
62148618fb4SAlexandre Bounine /**
62248618fb4SAlexandre Bounine  * tsi721_imsg_msix - MSI-X interrupt handler for inbound messaging
62348618fb4SAlexandre Bounine  * @irq: Linux interrupt number
624748353ccSAlexandre Bounine  * @ptr: Pointer to interrupt-specific data (tsi721_device structure)
62548618fb4SAlexandre Bounine  *
62648618fb4SAlexandre Bounine  * Handles inbound messaging interrupts signaled using MSI-X.
62748618fb4SAlexandre Bounine  */
tsi721_imsg_msix(int irq,void * ptr)62848618fb4SAlexandre Bounine static irqreturn_t tsi721_imsg_msix(int irq, void *ptr)
62948618fb4SAlexandre Bounine {
630748353ccSAlexandre Bounine 	struct tsi721_device *priv = (struct tsi721_device *)ptr;
63148618fb4SAlexandre Bounine 	int mbox;
63248618fb4SAlexandre Bounine 
63348618fb4SAlexandre Bounine 	mbox = (irq - priv->msix[TSI721_VECT_IMB0_RCV].vector) % RIO_MAX_MBOX;
63448618fb4SAlexandre Bounine 	tsi721_imsg_handler(priv, mbox + 4);
63548618fb4SAlexandre Bounine 	return IRQ_HANDLED;
63648618fb4SAlexandre Bounine }
63748618fb4SAlexandre Bounine 
63848618fb4SAlexandre Bounine /**
63948618fb4SAlexandre Bounine  * tsi721_srio_msix - Tsi721 MSI-X SRIO MAC interrupt handler
64048618fb4SAlexandre Bounine  * @irq: Linux interrupt number
641748353ccSAlexandre Bounine  * @ptr: Pointer to interrupt-specific data (tsi721_device structure)
64248618fb4SAlexandre Bounine  *
64348618fb4SAlexandre Bounine  * Handles Tsi721 interrupts from SRIO MAC.
64448618fb4SAlexandre Bounine  */
tsi721_srio_msix(int irq,void * ptr)64548618fb4SAlexandre Bounine static irqreturn_t tsi721_srio_msix(int irq, void *ptr)
64648618fb4SAlexandre Bounine {
647748353ccSAlexandre Bounine 	struct tsi721_device *priv = (struct tsi721_device *)ptr;
64848618fb4SAlexandre Bounine 	u32 srio_int;
64948618fb4SAlexandre Bounine 
65048618fb4SAlexandre Bounine 	/* Service SRIO MAC interrupts */
65148618fb4SAlexandre Bounine 	srio_int = ioread32(priv->regs + TSI721_RIO_EM_INT_STAT);
65248618fb4SAlexandre Bounine 	if (srio_int & TSI721_RIO_EM_INT_STAT_PW_RX)
653748353ccSAlexandre Bounine 		tsi721_pw_handler(priv);
65448618fb4SAlexandre Bounine 
65548618fb4SAlexandre Bounine 	return IRQ_HANDLED;
65648618fb4SAlexandre Bounine }
65748618fb4SAlexandre Bounine 
65848618fb4SAlexandre Bounine /**
65948618fb4SAlexandre Bounine  * tsi721_sr2pc_ch_msix - Tsi721 MSI-X SR2PC Channel interrupt handler
66048618fb4SAlexandre Bounine  * @irq: Linux interrupt number
661748353ccSAlexandre Bounine  * @ptr: Pointer to interrupt-specific data (tsi721_device structure)
66248618fb4SAlexandre Bounine  *
66348618fb4SAlexandre Bounine  * Handles Tsi721 interrupts from SR2PC Channel.
66448618fb4SAlexandre Bounine  * NOTE: At this moment services only one SR2PC channel associated with inbound
66548618fb4SAlexandre Bounine  * doorbells.
66648618fb4SAlexandre Bounine  */
tsi721_sr2pc_ch_msix(int irq,void * ptr)66748618fb4SAlexandre Bounine static irqreturn_t tsi721_sr2pc_ch_msix(int irq, void *ptr)
66848618fb4SAlexandre Bounine {
669748353ccSAlexandre Bounine 	struct tsi721_device *priv = (struct tsi721_device *)ptr;
67048618fb4SAlexandre Bounine 	u32 sr_ch_int;
67148618fb4SAlexandre Bounine 
67248618fb4SAlexandre Bounine 	/* Service Inbound DB interrupt from SR2PC channel */
67348618fb4SAlexandre Bounine 	sr_ch_int = ioread32(priv->regs + TSI721_SR_CHINT(IDB_QUEUE));
67448618fb4SAlexandre Bounine 	if (sr_ch_int & TSI721_SR_CHINT_IDBQRCV)
675748353ccSAlexandre Bounine 		tsi721_dbell_handler(priv);
67648618fb4SAlexandre Bounine 
67748618fb4SAlexandre Bounine 	/* Clear interrupts */
67848618fb4SAlexandre Bounine 	iowrite32(sr_ch_int, priv->regs + TSI721_SR_CHINT(IDB_QUEUE));
67948618fb4SAlexandre Bounine 	/* Read back to ensure that interrupt was cleared */
68048618fb4SAlexandre Bounine 	sr_ch_int = ioread32(priv->regs + TSI721_SR_CHINT(IDB_QUEUE));
68148618fb4SAlexandre Bounine 
68248618fb4SAlexandre Bounine 	return IRQ_HANDLED;
68348618fb4SAlexandre Bounine }
68448618fb4SAlexandre Bounine 
68548618fb4SAlexandre Bounine /**
68648618fb4SAlexandre Bounine  * tsi721_request_msix - register interrupt service for MSI-X mode.
687748353ccSAlexandre Bounine  * @priv: tsi721 device-specific data structure
68848618fb4SAlexandre Bounine  *
68948618fb4SAlexandre Bounine  * Registers MSI-X interrupt service routines for interrupts that are active
69048618fb4SAlexandre Bounine  * immediately after mport initialization. Messaging interrupt service routines
69148618fb4SAlexandre Bounine  * should be registered during corresponding open requests.
69248618fb4SAlexandre Bounine  */
tsi721_request_msix(struct tsi721_device * priv)693748353ccSAlexandre Bounine static int tsi721_request_msix(struct tsi721_device *priv)
69448618fb4SAlexandre Bounine {
69548618fb4SAlexandre Bounine 	int err = 0;
69648618fb4SAlexandre Bounine 
69748618fb4SAlexandre Bounine 	err = request_irq(priv->msix[TSI721_VECT_IDB].vector,
69848618fb4SAlexandre Bounine 			tsi721_sr2pc_ch_msix, 0,
699748353ccSAlexandre Bounine 			priv->msix[TSI721_VECT_IDB].irq_name, (void *)priv);
70048618fb4SAlexandre Bounine 	if (err)
701748353ccSAlexandre Bounine 		return err;
70248618fb4SAlexandre Bounine 
70348618fb4SAlexandre Bounine 	err = request_irq(priv->msix[TSI721_VECT_PWRX].vector,
70448618fb4SAlexandre Bounine 			tsi721_srio_msix, 0,
705748353ccSAlexandre Bounine 			priv->msix[TSI721_VECT_PWRX].irq_name, (void *)priv);
706748353ccSAlexandre Bounine 	if (err) {
707748353ccSAlexandre Bounine 		free_irq(priv->msix[TSI721_VECT_IDB].vector, (void *)priv);
70848618fb4SAlexandre Bounine 		return err;
70948618fb4SAlexandre Bounine 	}
71048618fb4SAlexandre Bounine 
711748353ccSAlexandre Bounine 	return 0;
712748353ccSAlexandre Bounine }
713748353ccSAlexandre Bounine 
71448618fb4SAlexandre Bounine /**
71548618fb4SAlexandre Bounine  * tsi721_enable_msix - Attempts to enable MSI-X support for Tsi721.
71648618fb4SAlexandre Bounine  * @priv: pointer to tsi721 private data
71748618fb4SAlexandre Bounine  *
71848618fb4SAlexandre Bounine  * Configures MSI-X support for Tsi721. Supports only an exact number
71948618fb4SAlexandre Bounine  * of requested vectors.
72048618fb4SAlexandre Bounine  */
tsi721_enable_msix(struct tsi721_device * priv)72148618fb4SAlexandre Bounine static int tsi721_enable_msix(struct tsi721_device *priv)
72248618fb4SAlexandre Bounine {
72348618fb4SAlexandre Bounine 	struct msix_entry entries[TSI721_VECT_MAX];
72448618fb4SAlexandre Bounine 	int err;
72548618fb4SAlexandre Bounine 	int i;
72648618fb4SAlexandre Bounine 
72748618fb4SAlexandre Bounine 	entries[TSI721_VECT_IDB].entry = TSI721_MSIX_SR2PC_IDBQ_RCV(IDB_QUEUE);
72848618fb4SAlexandre Bounine 	entries[TSI721_VECT_PWRX].entry = TSI721_MSIX_SRIO_MAC_INT;
72948618fb4SAlexandre Bounine 
73048618fb4SAlexandre Bounine 	/*
73148618fb4SAlexandre Bounine 	 * Initialize MSI-X entries for Messaging Engine:
73248618fb4SAlexandre Bounine 	 * this driver supports four RIO mailboxes (inbound and outbound)
73348618fb4SAlexandre Bounine 	 * NOTE: Inbound message MBOX 0...4 use IB channels 4...7. Therefore
73448618fb4SAlexandre Bounine 	 * offset +4 is added to IB MBOX number.
73548618fb4SAlexandre Bounine 	 */
73648618fb4SAlexandre Bounine 	for (i = 0; i < RIO_MAX_MBOX; i++) {
73748618fb4SAlexandre Bounine 		entries[TSI721_VECT_IMB0_RCV + i].entry =
73848618fb4SAlexandre Bounine 					TSI721_MSIX_IMSG_DQ_RCV(i + 4);
73948618fb4SAlexandre Bounine 		entries[TSI721_VECT_IMB0_INT + i].entry =
74048618fb4SAlexandre Bounine 					TSI721_MSIX_IMSG_INT(i + 4);
74148618fb4SAlexandre Bounine 		entries[TSI721_VECT_OMB0_DONE + i].entry =
74248618fb4SAlexandre Bounine 					TSI721_MSIX_OMSG_DONE(i);
74348618fb4SAlexandre Bounine 		entries[TSI721_VECT_OMB0_INT + i].entry =
74448618fb4SAlexandre Bounine 					TSI721_MSIX_OMSG_INT(i);
74548618fb4SAlexandre Bounine 	}
74648618fb4SAlexandre Bounine 
7479eaa3d9bSAlexandre Bounine #ifdef CONFIG_RAPIDIO_DMA_ENGINE
7489eaa3d9bSAlexandre Bounine 	/*
7499eaa3d9bSAlexandre Bounine 	 * Initialize MSI-X entries for Block DMA Engine:
7509eaa3d9bSAlexandre Bounine 	 * this driver supports XXX DMA channels
7519eaa3d9bSAlexandre Bounine 	 * (one is reserved for SRIO maintenance transactions)
7529eaa3d9bSAlexandre Bounine 	 */
7539eaa3d9bSAlexandre Bounine 	for (i = 0; i < TSI721_DMA_CHNUM; i++) {
7549eaa3d9bSAlexandre Bounine 		entries[TSI721_VECT_DMA0_DONE + i].entry =
7559eaa3d9bSAlexandre Bounine 					TSI721_MSIX_DMACH_DONE(i);
7569eaa3d9bSAlexandre Bounine 		entries[TSI721_VECT_DMA0_INT + i].entry =
7579eaa3d9bSAlexandre Bounine 					TSI721_MSIX_DMACH_INT(i);
7589eaa3d9bSAlexandre Bounine 	}
7599eaa3d9bSAlexandre Bounine #endif /* CONFIG_RAPIDIO_DMA_ENGINE */
7609eaa3d9bSAlexandre Bounine 
7611c92ab1eSAlexander Gordeev 	err = pci_enable_msix_exact(priv->pdev, entries, ARRAY_SIZE(entries));
76248618fb4SAlexandre Bounine 	if (err) {
76372d8a0d2SAlexandre Bounine 		tsi_err(&priv->pdev->dev,
76472d8a0d2SAlexandre Bounine 			"Failed to enable MSI-X (err=%d)", err);
76548618fb4SAlexandre Bounine 		return err;
76648618fb4SAlexandre Bounine 	}
76748618fb4SAlexandre Bounine 
76848618fb4SAlexandre Bounine 	/*
76948618fb4SAlexandre Bounine 	 * Copy MSI-X vector information into tsi721 private structure
77048618fb4SAlexandre Bounine 	 */
77148618fb4SAlexandre Bounine 	priv->msix[TSI721_VECT_IDB].vector = entries[TSI721_VECT_IDB].vector;
77248618fb4SAlexandre Bounine 	snprintf(priv->msix[TSI721_VECT_IDB].irq_name, IRQ_DEVICE_NAME_MAX,
77348618fb4SAlexandre Bounine 		 DRV_NAME "-idb@pci:%s", pci_name(priv->pdev));
77448618fb4SAlexandre Bounine 	priv->msix[TSI721_VECT_PWRX].vector = entries[TSI721_VECT_PWRX].vector;
77548618fb4SAlexandre Bounine 	snprintf(priv->msix[TSI721_VECT_PWRX].irq_name, IRQ_DEVICE_NAME_MAX,
77648618fb4SAlexandre Bounine 		 DRV_NAME "-pwrx@pci:%s", pci_name(priv->pdev));
77748618fb4SAlexandre Bounine 
77848618fb4SAlexandre Bounine 	for (i = 0; i < RIO_MAX_MBOX; i++) {
77948618fb4SAlexandre Bounine 		priv->msix[TSI721_VECT_IMB0_RCV + i].vector =
78048618fb4SAlexandre Bounine 				entries[TSI721_VECT_IMB0_RCV + i].vector;
78148618fb4SAlexandre Bounine 		snprintf(priv->msix[TSI721_VECT_IMB0_RCV + i].irq_name,
78248618fb4SAlexandre Bounine 			 IRQ_DEVICE_NAME_MAX, DRV_NAME "-imbr%d@pci:%s",
78348618fb4SAlexandre Bounine 			 i, pci_name(priv->pdev));
78448618fb4SAlexandre Bounine 
78548618fb4SAlexandre Bounine 		priv->msix[TSI721_VECT_IMB0_INT + i].vector =
78648618fb4SAlexandre Bounine 				entries[TSI721_VECT_IMB0_INT + i].vector;
78748618fb4SAlexandre Bounine 		snprintf(priv->msix[TSI721_VECT_IMB0_INT + i].irq_name,
78848618fb4SAlexandre Bounine 			 IRQ_DEVICE_NAME_MAX, DRV_NAME "-imbi%d@pci:%s",
78948618fb4SAlexandre Bounine 			 i, pci_name(priv->pdev));
79048618fb4SAlexandre Bounine 
79148618fb4SAlexandre Bounine 		priv->msix[TSI721_VECT_OMB0_DONE + i].vector =
79248618fb4SAlexandre Bounine 				entries[TSI721_VECT_OMB0_DONE + i].vector;
79348618fb4SAlexandre Bounine 		snprintf(priv->msix[TSI721_VECT_OMB0_DONE + i].irq_name,
79448618fb4SAlexandre Bounine 			 IRQ_DEVICE_NAME_MAX, DRV_NAME "-ombd%d@pci:%s",
79548618fb4SAlexandre Bounine 			 i, pci_name(priv->pdev));
79648618fb4SAlexandre Bounine 
79748618fb4SAlexandre Bounine 		priv->msix[TSI721_VECT_OMB0_INT + i].vector =
79848618fb4SAlexandre Bounine 				entries[TSI721_VECT_OMB0_INT + i].vector;
79948618fb4SAlexandre Bounine 		snprintf(priv->msix[TSI721_VECT_OMB0_INT + i].irq_name,
80048618fb4SAlexandre Bounine 			 IRQ_DEVICE_NAME_MAX, DRV_NAME "-ombi%d@pci:%s",
80148618fb4SAlexandre Bounine 			 i, pci_name(priv->pdev));
80248618fb4SAlexandre Bounine 	}
80348618fb4SAlexandre Bounine 
8049eaa3d9bSAlexandre Bounine #ifdef CONFIG_RAPIDIO_DMA_ENGINE
8059eaa3d9bSAlexandre Bounine 	for (i = 0; i < TSI721_DMA_CHNUM; i++) {
8069eaa3d9bSAlexandre Bounine 		priv->msix[TSI721_VECT_DMA0_DONE + i].vector =
8079eaa3d9bSAlexandre Bounine 				entries[TSI721_VECT_DMA0_DONE + i].vector;
8089eaa3d9bSAlexandre Bounine 		snprintf(priv->msix[TSI721_VECT_DMA0_DONE + i].irq_name,
8099eaa3d9bSAlexandre Bounine 			 IRQ_DEVICE_NAME_MAX, DRV_NAME "-dmad%d@pci:%s",
8109eaa3d9bSAlexandre Bounine 			 i, pci_name(priv->pdev));
8119eaa3d9bSAlexandre Bounine 
8129eaa3d9bSAlexandre Bounine 		priv->msix[TSI721_VECT_DMA0_INT + i].vector =
8139eaa3d9bSAlexandre Bounine 				entries[TSI721_VECT_DMA0_INT + i].vector;
8149eaa3d9bSAlexandre Bounine 		snprintf(priv->msix[TSI721_VECT_DMA0_INT + i].irq_name,
8159eaa3d9bSAlexandre Bounine 			 IRQ_DEVICE_NAME_MAX, DRV_NAME "-dmai%d@pci:%s",
8169eaa3d9bSAlexandre Bounine 			 i, pci_name(priv->pdev));
8179eaa3d9bSAlexandre Bounine 	}
8189eaa3d9bSAlexandre Bounine #endif /* CONFIG_RAPIDIO_DMA_ENGINE */
8199eaa3d9bSAlexandre Bounine 
82048618fb4SAlexandre Bounine 	return 0;
82148618fb4SAlexandre Bounine }
82248618fb4SAlexandre Bounine #endif /* CONFIG_PCI_MSI */
82348618fb4SAlexandre Bounine 
tsi721_request_irq(struct tsi721_device * priv)824748353ccSAlexandre Bounine static int tsi721_request_irq(struct tsi721_device *priv)
82548618fb4SAlexandre Bounine {
82648618fb4SAlexandre Bounine 	int err;
82748618fb4SAlexandre Bounine 
82848618fb4SAlexandre Bounine #ifdef CONFIG_PCI_MSI
82948618fb4SAlexandre Bounine 	if (priv->flags & TSI721_USING_MSIX)
830748353ccSAlexandre Bounine 		err = tsi721_request_msix(priv);
83148618fb4SAlexandre Bounine 	else
83248618fb4SAlexandre Bounine #endif
83348618fb4SAlexandre Bounine 		err = request_irq(priv->pdev->irq, tsi721_irqhandler,
83448618fb4SAlexandre Bounine 			  (priv->flags & TSI721_USING_MSI) ? 0 : IRQF_SHARED,
835748353ccSAlexandre Bounine 			  DRV_NAME, (void *)priv);
83648618fb4SAlexandre Bounine 
83748618fb4SAlexandre Bounine 	if (err)
83872d8a0d2SAlexandre Bounine 		tsi_err(&priv->pdev->dev,
83972d8a0d2SAlexandre Bounine 			"Unable to allocate interrupt, err=%d", err);
84048618fb4SAlexandre Bounine 
84148618fb4SAlexandre Bounine 	return err;
84248618fb4SAlexandre Bounine }
84348618fb4SAlexandre Bounine 
tsi721_free_irq(struct tsi721_device * priv)844748353ccSAlexandre Bounine static void tsi721_free_irq(struct tsi721_device *priv)
845748353ccSAlexandre Bounine {
846748353ccSAlexandre Bounine #ifdef CONFIG_PCI_MSI
847748353ccSAlexandre Bounine 	if (priv->flags & TSI721_USING_MSIX) {
848748353ccSAlexandre Bounine 		free_irq(priv->msix[TSI721_VECT_IDB].vector, (void *)priv);
849748353ccSAlexandre Bounine 		free_irq(priv->msix[TSI721_VECT_PWRX].vector, (void *)priv);
850748353ccSAlexandre Bounine 	} else
851748353ccSAlexandre Bounine #endif
852748353ccSAlexandre Bounine 	free_irq(priv->pdev->irq, (void *)priv);
853748353ccSAlexandre Bounine }
854748353ccSAlexandre Bounine 
8551679e8daSAlexandre Bounine static int
tsi721_obw_alloc(struct tsi721_device * priv,struct tsi721_obw_bar * pbar,u32 size,int * win_id)8561679e8daSAlexandre Bounine tsi721_obw_alloc(struct tsi721_device *priv, struct tsi721_obw_bar *pbar,
8571679e8daSAlexandre Bounine 		 u32 size, int *win_id)
8581679e8daSAlexandre Bounine {
8591679e8daSAlexandre Bounine 	u64 win_base;
8601679e8daSAlexandre Bounine 	u64 bar_base;
8611679e8daSAlexandre Bounine 	u64 bar_end;
8621679e8daSAlexandre Bounine 	u32 align;
8631679e8daSAlexandre Bounine 	struct tsi721_ob_win *win;
8641679e8daSAlexandre Bounine 	struct tsi721_ob_win *new_win = NULL;
8651679e8daSAlexandre Bounine 	int new_win_idx = -1;
8661679e8daSAlexandre Bounine 	int i = 0;
8671679e8daSAlexandre Bounine 
8681679e8daSAlexandre Bounine 	bar_base = pbar->base;
8691679e8daSAlexandre Bounine 	bar_end =  bar_base + pbar->size;
8701679e8daSAlexandre Bounine 	win_base = bar_base;
8711679e8daSAlexandre Bounine 	align = size/TSI721_PC2SR_ZONES;
8721679e8daSAlexandre Bounine 
8731679e8daSAlexandre Bounine 	while (i < TSI721_IBWIN_NUM) {
8741679e8daSAlexandre Bounine 		for (i = 0; i < TSI721_IBWIN_NUM; i++) {
8751679e8daSAlexandre Bounine 			if (!priv->ob_win[i].active) {
8761679e8daSAlexandre Bounine 				if (new_win == NULL) {
8771679e8daSAlexandre Bounine 					new_win = &priv->ob_win[i];
8781679e8daSAlexandre Bounine 					new_win_idx = i;
8791679e8daSAlexandre Bounine 				}
8801679e8daSAlexandre Bounine 				continue;
8811679e8daSAlexandre Bounine 			}
8821679e8daSAlexandre Bounine 
8831679e8daSAlexandre Bounine 			/*
8841679e8daSAlexandre Bounine 			 * If this window belongs to the current BAR check it
8851679e8daSAlexandre Bounine 			 * for overlap
8861679e8daSAlexandre Bounine 			 */
8871679e8daSAlexandre Bounine 			win = &priv->ob_win[i];
8881679e8daSAlexandre Bounine 
8891679e8daSAlexandre Bounine 			if (win->base >= bar_base && win->base < bar_end) {
8901679e8daSAlexandre Bounine 				if (win_base < (win->base + win->size) &&
8911679e8daSAlexandre Bounine 						(win_base + size) > win->base) {
8921679e8daSAlexandre Bounine 					/* Overlap detected */
8931679e8daSAlexandre Bounine 					win_base = win->base + win->size;
8941679e8daSAlexandre Bounine 					win_base = ALIGN(win_base, align);
8951679e8daSAlexandre Bounine 					break;
8961679e8daSAlexandre Bounine 				}
8971679e8daSAlexandre Bounine 			}
8981679e8daSAlexandre Bounine 		}
8991679e8daSAlexandre Bounine 	}
9001679e8daSAlexandre Bounine 
9011679e8daSAlexandre Bounine 	if (win_base + size > bar_end)
9021679e8daSAlexandre Bounine 		return -ENOMEM;
9031679e8daSAlexandre Bounine 
9041679e8daSAlexandre Bounine 	if (!new_win) {
90572d8a0d2SAlexandre Bounine 		tsi_err(&priv->pdev->dev, "OBW count tracking failed");
9061679e8daSAlexandre Bounine 		return -EIO;
9071679e8daSAlexandre Bounine 	}
9081679e8daSAlexandre Bounine 
9091679e8daSAlexandre Bounine 	new_win->active = true;
9101679e8daSAlexandre Bounine 	new_win->base = win_base;
9111679e8daSAlexandre Bounine 	new_win->size = size;
9121679e8daSAlexandre Bounine 	new_win->pbar = pbar;
9131679e8daSAlexandre Bounine 	priv->obwin_cnt--;
9141679e8daSAlexandre Bounine 	pbar->free -= size;
9151679e8daSAlexandre Bounine 	*win_id = new_win_idx;
9161679e8daSAlexandre Bounine 	return 0;
9171679e8daSAlexandre Bounine }
9181679e8daSAlexandre Bounine 
tsi721_map_outb_win(struct rio_mport * mport,u16 destid,u64 rstart,u32 size,u32 flags,dma_addr_t * laddr)9191679e8daSAlexandre Bounine static int tsi721_map_outb_win(struct rio_mport *mport, u16 destid, u64 rstart,
9201679e8daSAlexandre Bounine 			u32 size, u32 flags, dma_addr_t *laddr)
9211679e8daSAlexandre Bounine {
9221679e8daSAlexandre Bounine 	struct tsi721_device *priv = mport->priv;
9231679e8daSAlexandre Bounine 	int i;
9241679e8daSAlexandre Bounine 	struct tsi721_obw_bar *pbar;
9251679e8daSAlexandre Bounine 	struct tsi721_ob_win *ob_win;
9261679e8daSAlexandre Bounine 	int obw = -1;
9271679e8daSAlexandre Bounine 	u32 rval;
9281679e8daSAlexandre Bounine 	u64 rio_addr;
9291679e8daSAlexandre Bounine 	u32 zsize;
9301679e8daSAlexandre Bounine 	int ret = -ENOMEM;
9311679e8daSAlexandre Bounine 
93272d8a0d2SAlexandre Bounine 	tsi_debug(OBW, &priv->pdev->dev,
93372d8a0d2SAlexandre Bounine 		  "did=%d ra=0x%llx sz=0x%x", destid, rstart, size);
93472d8a0d2SAlexandre Bounine 
9351679e8daSAlexandre Bounine 	if (!is_power_of_2(size) || (size < 0x8000) || (rstart & (size - 1)))
9361679e8daSAlexandre Bounine 		return -EINVAL;
9371679e8daSAlexandre Bounine 
9381679e8daSAlexandre Bounine 	if (priv->obwin_cnt == 0)
9391679e8daSAlexandre Bounine 		return -EBUSY;
9401679e8daSAlexandre Bounine 
9411679e8daSAlexandre Bounine 	for (i = 0; i < 2; i++) {
9421679e8daSAlexandre Bounine 		if (priv->p2r_bar[i].free >= size) {
9431679e8daSAlexandre Bounine 			pbar = &priv->p2r_bar[i];
9441679e8daSAlexandre Bounine 			ret = tsi721_obw_alloc(priv, pbar, size, &obw);
9451679e8daSAlexandre Bounine 			if (!ret)
9461679e8daSAlexandre Bounine 				break;
9471679e8daSAlexandre Bounine 		}
9481679e8daSAlexandre Bounine 	}
9491679e8daSAlexandre Bounine 
9501679e8daSAlexandre Bounine 	if (ret)
9511679e8daSAlexandre Bounine 		return ret;
9521679e8daSAlexandre Bounine 
9531679e8daSAlexandre Bounine 	WARN_ON(obw == -1);
9541679e8daSAlexandre Bounine 	ob_win = &priv->ob_win[obw];
9551679e8daSAlexandre Bounine 	ob_win->destid = destid;
9561679e8daSAlexandre Bounine 	ob_win->rstart = rstart;
95772d8a0d2SAlexandre Bounine 	tsi_debug(OBW, &priv->pdev->dev,
95872d8a0d2SAlexandre Bounine 		  "allocated OBW%d @%llx", obw, ob_win->base);
9591679e8daSAlexandre Bounine 
9601679e8daSAlexandre Bounine 	/*
9611679e8daSAlexandre Bounine 	 * Configure Outbound Window
9621679e8daSAlexandre Bounine 	 */
9631679e8daSAlexandre Bounine 
9641679e8daSAlexandre Bounine 	zsize = size/TSI721_PC2SR_ZONES;
9651679e8daSAlexandre Bounine 	rio_addr = rstart;
9661679e8daSAlexandre Bounine 
9671679e8daSAlexandre Bounine 	/*
9681679e8daSAlexandre Bounine 	 * Program Address Translation Zones:
9691679e8daSAlexandre Bounine 	 *  This implementation uses all 8 zones associated wit window.
9701679e8daSAlexandre Bounine 	 */
9711679e8daSAlexandre Bounine 	for (i = 0; i < TSI721_PC2SR_ZONES; i++) {
9721679e8daSAlexandre Bounine 
9731679e8daSAlexandre Bounine 		while (ioread32(priv->regs + TSI721_ZONE_SEL) &
9741679e8daSAlexandre Bounine 			TSI721_ZONE_SEL_GO) {
9751679e8daSAlexandre Bounine 			udelay(1);
9761679e8daSAlexandre Bounine 		}
9771679e8daSAlexandre Bounine 
9781679e8daSAlexandre Bounine 		rval = (u32)(rio_addr & TSI721_LUT_DATA0_ADD) |
9791679e8daSAlexandre Bounine 			TSI721_LUT_DATA0_NREAD | TSI721_LUT_DATA0_NWR;
9801679e8daSAlexandre Bounine 		iowrite32(rval, priv->regs + TSI721_LUT_DATA0);
9811679e8daSAlexandre Bounine 		rval = (u32)(rio_addr >> 32);
9821679e8daSAlexandre Bounine 		iowrite32(rval, priv->regs + TSI721_LUT_DATA1);
9831679e8daSAlexandre Bounine 		rval = destid;
9841679e8daSAlexandre Bounine 		iowrite32(rval, priv->regs + TSI721_LUT_DATA2);
9851679e8daSAlexandre Bounine 
9861679e8daSAlexandre Bounine 		rval = TSI721_ZONE_SEL_GO | (obw << 3) | i;
9871679e8daSAlexandre Bounine 		iowrite32(rval, priv->regs + TSI721_ZONE_SEL);
9881679e8daSAlexandre Bounine 
9891679e8daSAlexandre Bounine 		rio_addr += zsize;
9901679e8daSAlexandre Bounine 	}
9911679e8daSAlexandre Bounine 
9921679e8daSAlexandre Bounine 	iowrite32(TSI721_OBWIN_SIZE(size) << 8,
9931679e8daSAlexandre Bounine 		  priv->regs + TSI721_OBWINSZ(obw));
9941679e8daSAlexandre Bounine 	iowrite32((u32)(ob_win->base >> 32), priv->regs + TSI721_OBWINUB(obw));
9951679e8daSAlexandre Bounine 	iowrite32((u32)(ob_win->base & TSI721_OBWINLB_BA) | TSI721_OBWINLB_WEN,
9961679e8daSAlexandre Bounine 		  priv->regs + TSI721_OBWINLB(obw));
9971679e8daSAlexandre Bounine 
9981679e8daSAlexandre Bounine 	*laddr = ob_win->base;
9991679e8daSAlexandre Bounine 	return 0;
10001679e8daSAlexandre Bounine }
10011679e8daSAlexandre Bounine 
tsi721_unmap_outb_win(struct rio_mport * mport,u16 destid,u64 rstart)10021679e8daSAlexandre Bounine static void tsi721_unmap_outb_win(struct rio_mport *mport,
10031679e8daSAlexandre Bounine 				  u16 destid, u64 rstart)
10041679e8daSAlexandre Bounine {
10051679e8daSAlexandre Bounine 	struct tsi721_device *priv = mport->priv;
10061679e8daSAlexandre Bounine 	struct tsi721_ob_win *ob_win;
10071679e8daSAlexandre Bounine 	int i;
10081679e8daSAlexandre Bounine 
100972d8a0d2SAlexandre Bounine 	tsi_debug(OBW, &priv->pdev->dev, "did=%d ra=0x%llx", destid, rstart);
101072d8a0d2SAlexandre Bounine 
10111679e8daSAlexandre Bounine 	for (i = 0; i < TSI721_OBWIN_NUM; i++) {
10121679e8daSAlexandre Bounine 		ob_win = &priv->ob_win[i];
10131679e8daSAlexandre Bounine 
10141679e8daSAlexandre Bounine 		if (ob_win->active &&
10151679e8daSAlexandre Bounine 		    ob_win->destid == destid && ob_win->rstart == rstart) {
101672d8a0d2SAlexandre Bounine 			tsi_debug(OBW, &priv->pdev->dev,
101772d8a0d2SAlexandre Bounine 				  "free OBW%d @%llx", i, ob_win->base);
10181679e8daSAlexandre Bounine 			ob_win->active = false;
10191679e8daSAlexandre Bounine 			iowrite32(0, priv->regs + TSI721_OBWINLB(i));
10201679e8daSAlexandre Bounine 			ob_win->pbar->free += ob_win->size;
10211679e8daSAlexandre Bounine 			priv->obwin_cnt++;
10221679e8daSAlexandre Bounine 			break;
10231679e8daSAlexandre Bounine 		}
10241679e8daSAlexandre Bounine 	}
10251679e8daSAlexandre Bounine }
10261679e8daSAlexandre Bounine 
102748618fb4SAlexandre Bounine /**
102848618fb4SAlexandre Bounine  * tsi721_init_pc2sr_mapping - initializes outbound (PCIe->SRIO)
102948618fb4SAlexandre Bounine  * translation regions.
103048618fb4SAlexandre Bounine  * @priv: pointer to tsi721 private data
103148618fb4SAlexandre Bounine  *
103248618fb4SAlexandre Bounine  * Disables SREP translation regions.
103348618fb4SAlexandre Bounine  */
tsi721_init_pc2sr_mapping(struct tsi721_device * priv)103448618fb4SAlexandre Bounine static void tsi721_init_pc2sr_mapping(struct tsi721_device *priv)
103548618fb4SAlexandre Bounine {
10361679e8daSAlexandre Bounine 	int i, z;
10371679e8daSAlexandre Bounine 	u32 rval;
103848618fb4SAlexandre Bounine 
103948618fb4SAlexandre Bounine 	/* Disable all PC2SR translation windows */
104048618fb4SAlexandre Bounine 	for (i = 0; i < TSI721_OBWIN_NUM; i++)
104148618fb4SAlexandre Bounine 		iowrite32(0, priv->regs + TSI721_OBWINLB(i));
10421679e8daSAlexandre Bounine 
10431679e8daSAlexandre Bounine 	/* Initialize zone lookup tables to avoid ECC errors on reads */
10441679e8daSAlexandre Bounine 	iowrite32(0, priv->regs + TSI721_LUT_DATA0);
10451679e8daSAlexandre Bounine 	iowrite32(0, priv->regs + TSI721_LUT_DATA1);
10461679e8daSAlexandre Bounine 	iowrite32(0, priv->regs + TSI721_LUT_DATA2);
10471679e8daSAlexandre Bounine 
10481679e8daSAlexandre Bounine 	for (i = 0; i < TSI721_OBWIN_NUM; i++) {
10491679e8daSAlexandre Bounine 		for (z = 0; z < TSI721_PC2SR_ZONES; z++) {
10501679e8daSAlexandre Bounine 			while (ioread32(priv->regs + TSI721_ZONE_SEL) &
10511679e8daSAlexandre Bounine 				TSI721_ZONE_SEL_GO) {
10521679e8daSAlexandre Bounine 				udelay(1);
10531679e8daSAlexandre Bounine 			}
10541679e8daSAlexandre Bounine 			rval = TSI721_ZONE_SEL_GO | (i << 3) | z;
10551679e8daSAlexandre Bounine 			iowrite32(rval, priv->regs + TSI721_ZONE_SEL);
10561679e8daSAlexandre Bounine 		}
10571679e8daSAlexandre Bounine 	}
10581679e8daSAlexandre Bounine 
10591679e8daSAlexandre Bounine 	if (priv->p2r_bar[0].size == 0 && priv->p2r_bar[1].size == 0) {
10601679e8daSAlexandre Bounine 		priv->obwin_cnt = 0;
10611679e8daSAlexandre Bounine 		return;
10621679e8daSAlexandre Bounine 	}
10631679e8daSAlexandre Bounine 
10641679e8daSAlexandre Bounine 	priv->p2r_bar[0].free = priv->p2r_bar[0].size;
10651679e8daSAlexandre Bounine 	priv->p2r_bar[1].free = priv->p2r_bar[1].size;
10661679e8daSAlexandre Bounine 
10671679e8daSAlexandre Bounine 	for (i = 0; i < TSI721_OBWIN_NUM; i++)
10681679e8daSAlexandre Bounine 		priv->ob_win[i].active = false;
10691679e8daSAlexandre Bounine 
10701679e8daSAlexandre Bounine 	priv->obwin_cnt = TSI721_OBWIN_NUM;
107148618fb4SAlexandre Bounine }
107248618fb4SAlexandre Bounine 
107348618fb4SAlexandre Bounine /**
107471afe341SAlexandre Bounine  * tsi721_rio_map_inb_mem -- Mapping inbound memory region.
107571afe341SAlexandre Bounine  * @mport: RapidIO master port
107671afe341SAlexandre Bounine  * @lstart: Local memory space start address.
107771afe341SAlexandre Bounine  * @rstart: RapidIO space start address.
107871afe341SAlexandre Bounine  * @size: The mapping region size.
107971afe341SAlexandre Bounine  * @flags: Flags for mapping. 0 for using default flags.
108071afe341SAlexandre Bounine  *
108171afe341SAlexandre Bounine  * Return: 0 -- Success.
108271afe341SAlexandre Bounine  *
108371afe341SAlexandre Bounine  * This function will create the inbound mapping
108471afe341SAlexandre Bounine  * from rstart to lstart.
108571afe341SAlexandre Bounine  */
tsi721_rio_map_inb_mem(struct rio_mport * mport,dma_addr_t lstart,u64 rstart,u64 size,u32 flags)108671afe341SAlexandre Bounine static int tsi721_rio_map_inb_mem(struct rio_mport *mport, dma_addr_t lstart,
1087a057a52eSAlexandre Bounine 		u64 rstart, u64 size, u32 flags)
108871afe341SAlexandre Bounine {
108971afe341SAlexandre Bounine 	struct tsi721_device *priv = mport->priv;
1090ba5d141bSAlexandre Bounine 	int i, avail = -1;
109171afe341SAlexandre Bounine 	u32 regval;
1092ba5d141bSAlexandre Bounine 	struct tsi721_ib_win *ib_win;
10939673b883SAlexandre Bounine 	bool direct = (lstart == rstart);
10949673b883SAlexandre Bounine 	u64 ibw_size;
10959673b883SAlexandre Bounine 	dma_addr_t loc_start;
10969673b883SAlexandre Bounine 	u64 ibw_start;
10979673b883SAlexandre Bounine 	struct tsi721_ib_win_mapping *map = NULL;
1098ba5d141bSAlexandre Bounine 	int ret = -EBUSY;
109971afe341SAlexandre Bounine 
1100a057a52eSAlexandre Bounine 	/* Max IBW size supported by HW is 16GB */
1101a057a52eSAlexandre Bounine 	if (size > 0x400000000UL)
1102a057a52eSAlexandre Bounine 		return -EINVAL;
1103a057a52eSAlexandre Bounine 
11049673b883SAlexandre Bounine 	if (direct) {
11059673b883SAlexandre Bounine 		/* Calculate minimal acceptable window size and base address */
11069673b883SAlexandre Bounine 
11079673b883SAlexandre Bounine 		ibw_size = roundup_pow_of_two(size);
11089673b883SAlexandre Bounine 		ibw_start = lstart & ~(ibw_size - 1);
11099673b883SAlexandre Bounine 
111072d8a0d2SAlexandre Bounine 		tsi_debug(IBW, &priv->pdev->dev,
1111a057a52eSAlexandre Bounine 			"Direct (RIO_0x%llx -> PCIe_%pad), size=0x%llx, ibw_start = 0x%llx",
111272d8a0d2SAlexandre Bounine 			rstart, &lstart, size, ibw_start);
111372d8a0d2SAlexandre Bounine 
11149673b883SAlexandre Bounine 		while ((lstart + size) > (ibw_start + ibw_size)) {
11159673b883SAlexandre Bounine 			ibw_size *= 2;
11169673b883SAlexandre Bounine 			ibw_start = lstart & ~(ibw_size - 1);
1117a057a52eSAlexandre Bounine 			/* Check for crossing IBW max size 16GB */
1118a057a52eSAlexandre Bounine 			if (ibw_size > 0x400000000UL)
11199673b883SAlexandre Bounine 				return -EBUSY;
11209673b883SAlexandre Bounine 		}
11219673b883SAlexandre Bounine 
11229673b883SAlexandre Bounine 		loc_start = ibw_start;
11239673b883SAlexandre Bounine 
11249673b883SAlexandre Bounine 		map = kzalloc(sizeof(struct tsi721_ib_win_mapping), GFP_ATOMIC);
11259673b883SAlexandre Bounine 		if (map == NULL)
11269673b883SAlexandre Bounine 			return -ENOMEM;
11279673b883SAlexandre Bounine 
11289673b883SAlexandre Bounine 	} else {
112972d8a0d2SAlexandre Bounine 		tsi_debug(IBW, &priv->pdev->dev,
1130a057a52eSAlexandre Bounine 			"Translated (RIO_0x%llx -> PCIe_%pad), size=0x%llx",
11319673b883SAlexandre Bounine 			rstart, &lstart, size);
11329673b883SAlexandre Bounine 
113371afe341SAlexandre Bounine 		if (!is_power_of_2(size) || size < 0x1000 ||
113471afe341SAlexandre Bounine 		    ((u64)lstart & (size - 1)) || (rstart & (size - 1)))
113571afe341SAlexandre Bounine 			return -EINVAL;
11369673b883SAlexandre Bounine 		if (priv->ibwin_cnt == 0)
11379673b883SAlexandre Bounine 			return -EBUSY;
11389673b883SAlexandre Bounine 		ibw_start = rstart;
11399673b883SAlexandre Bounine 		ibw_size = size;
11409673b883SAlexandre Bounine 		loc_start = lstart;
11419673b883SAlexandre Bounine 	}
114271afe341SAlexandre Bounine 
1143ba5d141bSAlexandre Bounine 	/*
1144ba5d141bSAlexandre Bounine 	 * Scan for overlapping with active regions and mark the first available
1145ba5d141bSAlexandre Bounine 	 * IB window at the same time.
1146ba5d141bSAlexandre Bounine 	 */
114771afe341SAlexandre Bounine 	for (i = 0; i < TSI721_IBWIN_NUM; i++) {
1148ba5d141bSAlexandre Bounine 		ib_win = &priv->ib_win[i];
11499673b883SAlexandre Bounine 
1150ba5d141bSAlexandre Bounine 		if (!ib_win->active) {
1151ba5d141bSAlexandre Bounine 			if (avail == -1) {
1152ba5d141bSAlexandre Bounine 				avail = i;
1153ba5d141bSAlexandre Bounine 				ret = 0;
1154ba5d141bSAlexandre Bounine 			}
11559673b883SAlexandre Bounine 		} else if (ibw_start < (ib_win->rstart + ib_win->size) &&
11569673b883SAlexandre Bounine 			   (ibw_start + ibw_size) > ib_win->rstart) {
11579673b883SAlexandre Bounine 			/* Return error if address translation involved */
1158b3006929SAlexandre Bounine 			if (!direct || ib_win->xlat) {
11599673b883SAlexandre Bounine 				ret = -EFAULT;
11609673b883SAlexandre Bounine 				break;
11619673b883SAlexandre Bounine 			}
11629673b883SAlexandre Bounine 
11639673b883SAlexandre Bounine 			/*
11649673b883SAlexandre Bounine 			 * Direct mappings usually are larger than originally
11659673b883SAlexandre Bounine 			 * requested fragments - check if this new request fits
11669673b883SAlexandre Bounine 			 * into it.
11679673b883SAlexandre Bounine 			 */
11689673b883SAlexandre Bounine 			if (rstart >= ib_win->rstart &&
11699673b883SAlexandre Bounine 			    (rstart + size) <= (ib_win->rstart +
11709673b883SAlexandre Bounine 							ib_win->size)) {
11719673b883SAlexandre Bounine 				/* We are in - no further mapping required */
11729673b883SAlexandre Bounine 				map->lstart = lstart;
11739673b883SAlexandre Bounine 				list_add_tail(&map->node, &ib_win->mappings);
11749673b883SAlexandre Bounine 				return 0;
11759673b883SAlexandre Bounine 			}
11769673b883SAlexandre Bounine 
1177ba5d141bSAlexandre Bounine 			ret = -EFAULT;
117871afe341SAlexandre Bounine 			break;
117971afe341SAlexandre Bounine 		}
118071afe341SAlexandre Bounine 	}
118171afe341SAlexandre Bounine 
1182ba5d141bSAlexandre Bounine 	if (ret)
11839673b883SAlexandre Bounine 		goto out;
1184ba5d141bSAlexandre Bounine 	i = avail;
1185ba5d141bSAlexandre Bounine 
1186ba5d141bSAlexandre Bounine 	/* Sanity check: available IB window must be disabled at this point */
1187ba5d141bSAlexandre Bounine 	regval = ioread32(priv->regs + TSI721_IBWIN_LB(i));
1188ba5d141bSAlexandre Bounine 	if (WARN_ON(regval & TSI721_IBWIN_LB_WEN)) {
1189ba5d141bSAlexandre Bounine 		ret = -EIO;
11909673b883SAlexandre Bounine 		goto out;
1191ba5d141bSAlexandre Bounine 	}
1192ba5d141bSAlexandre Bounine 
1193ba5d141bSAlexandre Bounine 	ib_win = &priv->ib_win[i];
1194ba5d141bSAlexandre Bounine 	ib_win->active = true;
11959673b883SAlexandre Bounine 	ib_win->rstart = ibw_start;
11969673b883SAlexandre Bounine 	ib_win->lstart = loc_start;
11979673b883SAlexandre Bounine 	ib_win->size = ibw_size;
11989673b883SAlexandre Bounine 	ib_win->xlat = (lstart != rstart);
11999673b883SAlexandre Bounine 	INIT_LIST_HEAD(&ib_win->mappings);
1200ba5d141bSAlexandre Bounine 
12019673b883SAlexandre Bounine 	/*
12029673b883SAlexandre Bounine 	 * When using direct IBW mapping and have larger than requested IBW size
12039673b883SAlexandre Bounine 	 * we can have multiple local memory blocks mapped through the same IBW
12049673b883SAlexandre Bounine 	 * To handle this situation we maintain list of "clients" for such IBWs.
12059673b883SAlexandre Bounine 	 */
12069673b883SAlexandre Bounine 	if (direct) {
12079673b883SAlexandre Bounine 		map->lstart = lstart;
12089673b883SAlexandre Bounine 		list_add_tail(&map->node, &ib_win->mappings);
12099673b883SAlexandre Bounine 	}
12109673b883SAlexandre Bounine 
12119673b883SAlexandre Bounine 	iowrite32(TSI721_IBWIN_SIZE(ibw_size) << 8,
121271afe341SAlexandre Bounine 			priv->regs + TSI721_IBWIN_SZ(i));
121371afe341SAlexandre Bounine 
12149673b883SAlexandre Bounine 	iowrite32(((u64)loc_start >> 32), priv->regs + TSI721_IBWIN_TUA(i));
12159673b883SAlexandre Bounine 	iowrite32(((u64)loc_start & TSI721_IBWIN_TLA_ADD),
121671afe341SAlexandre Bounine 		  priv->regs + TSI721_IBWIN_TLA(i));
121771afe341SAlexandre Bounine 
12189673b883SAlexandre Bounine 	iowrite32(ibw_start >> 32, priv->regs + TSI721_IBWIN_UB(i));
12199673b883SAlexandre Bounine 	iowrite32((ibw_start & TSI721_IBWIN_LB_BA) | TSI721_IBWIN_LB_WEN,
122071afe341SAlexandre Bounine 		priv->regs + TSI721_IBWIN_LB(i));
12219673b883SAlexandre Bounine 
12229673b883SAlexandre Bounine 	priv->ibwin_cnt--;
12239673b883SAlexandre Bounine 
122472d8a0d2SAlexandre Bounine 	tsi_debug(IBW, &priv->pdev->dev,
1225ea87b8e1SJoe Perches 		"Configured IBWIN%d (RIO_0x%llx -> PCIe_%pad), size=0x%llx",
122672d8a0d2SAlexandre Bounine 		i, ibw_start, &loc_start, ibw_size);
122771afe341SAlexandre Bounine 
122871afe341SAlexandre Bounine 	return 0;
12299673b883SAlexandre Bounine out:
12309673b883SAlexandre Bounine 	kfree(map);
1231ba5d141bSAlexandre Bounine 	return ret;
123271afe341SAlexandre Bounine }
123371afe341SAlexandre Bounine 
123471afe341SAlexandre Bounine /**
12359673b883SAlexandre Bounine  * tsi721_rio_unmap_inb_mem -- Unmapping inbound memory region.
123671afe341SAlexandre Bounine  * @mport: RapidIO master port
123771afe341SAlexandre Bounine  * @lstart: Local memory space start address.
123871afe341SAlexandre Bounine  */
tsi721_rio_unmap_inb_mem(struct rio_mport * mport,dma_addr_t lstart)123971afe341SAlexandre Bounine static void tsi721_rio_unmap_inb_mem(struct rio_mport *mport,
124071afe341SAlexandre Bounine 				dma_addr_t lstart)
124171afe341SAlexandre Bounine {
124271afe341SAlexandre Bounine 	struct tsi721_device *priv = mport->priv;
1243ba5d141bSAlexandre Bounine 	struct tsi721_ib_win *ib_win;
124471afe341SAlexandre Bounine 	int i;
124571afe341SAlexandre Bounine 
124672d8a0d2SAlexandre Bounine 	tsi_debug(IBW, &priv->pdev->dev,
1247ea87b8e1SJoe Perches 		"Unmap IBW mapped to PCIe_%pad", &lstart);
12489673b883SAlexandre Bounine 
124971afe341SAlexandre Bounine 	/* Search for matching active inbound translation window */
125071afe341SAlexandre Bounine 	for (i = 0; i < TSI721_IBWIN_NUM; i++) {
1251ba5d141bSAlexandre Bounine 		ib_win = &priv->ib_win[i];
12529673b883SAlexandre Bounine 
12539673b883SAlexandre Bounine 		/* Address translating IBWs must to be an exact march */
12549673b883SAlexandre Bounine 		if (!ib_win->active ||
12559673b883SAlexandre Bounine 		    (ib_win->xlat && lstart != ib_win->lstart))
12569673b883SAlexandre Bounine 			continue;
12579673b883SAlexandre Bounine 
12589673b883SAlexandre Bounine 		if (lstart >= ib_win->lstart &&
12599673b883SAlexandre Bounine 		    lstart < (ib_win->lstart + ib_win->size)) {
12609673b883SAlexandre Bounine 
12619673b883SAlexandre Bounine 			if (!ib_win->xlat) {
12629673b883SAlexandre Bounine 				struct tsi721_ib_win_mapping *map;
12639673b883SAlexandre Bounine 				int found = 0;
12649673b883SAlexandre Bounine 
12659673b883SAlexandre Bounine 				list_for_each_entry(map,
12669673b883SAlexandre Bounine 						    &ib_win->mappings, node) {
12679673b883SAlexandre Bounine 					if (map->lstart == lstart) {
12689673b883SAlexandre Bounine 						list_del(&map->node);
12699673b883SAlexandre Bounine 						kfree(map);
12709673b883SAlexandre Bounine 						found = 1;
127171afe341SAlexandre Bounine 						break;
127271afe341SAlexandre Bounine 					}
127371afe341SAlexandre Bounine 				}
12749673b883SAlexandre Bounine 
12759673b883SAlexandre Bounine 				if (!found)
12769673b883SAlexandre Bounine 					continue;
12779673b883SAlexandre Bounine 
12789673b883SAlexandre Bounine 				if (!list_empty(&ib_win->mappings))
12799673b883SAlexandre Bounine 					break;
12809673b883SAlexandre Bounine 			}
12819673b883SAlexandre Bounine 
128272d8a0d2SAlexandre Bounine 			tsi_debug(IBW, &priv->pdev->dev, "Disable IBWIN_%d", i);
12839673b883SAlexandre Bounine 			iowrite32(0, priv->regs + TSI721_IBWIN_LB(i));
12849673b883SAlexandre Bounine 			ib_win->active = false;
12859673b883SAlexandre Bounine 			priv->ibwin_cnt++;
12869673b883SAlexandre Bounine 			break;
12879673b883SAlexandre Bounine 		}
12889673b883SAlexandre Bounine 	}
1289ba5d141bSAlexandre Bounine 
1290ba5d141bSAlexandre Bounine 	if (i == TSI721_IBWIN_NUM)
129172d8a0d2SAlexandre Bounine 		tsi_debug(IBW, &priv->pdev->dev,
12929673b883SAlexandre Bounine 			"IB window mapped to %pad not found", &lstart);
129371afe341SAlexandre Bounine }
129471afe341SAlexandre Bounine 
129571afe341SAlexandre Bounine /**
129648618fb4SAlexandre Bounine  * tsi721_init_sr2pc_mapping - initializes inbound (SRIO->PCIe)
129748618fb4SAlexandre Bounine  * translation regions.
129848618fb4SAlexandre Bounine  * @priv: pointer to tsi721 private data
129948618fb4SAlexandre Bounine  *
130048618fb4SAlexandre Bounine  * Disables inbound windows.
130148618fb4SAlexandre Bounine  */
tsi721_init_sr2pc_mapping(struct tsi721_device * priv)130248618fb4SAlexandre Bounine static void tsi721_init_sr2pc_mapping(struct tsi721_device *priv)
130348618fb4SAlexandre Bounine {
130448618fb4SAlexandre Bounine 	int i;
130548618fb4SAlexandre Bounine 
130648618fb4SAlexandre Bounine 	/* Disable all SR2PC inbound windows */
130748618fb4SAlexandre Bounine 	for (i = 0; i < TSI721_IBWIN_NUM; i++)
130871afe341SAlexandre Bounine 		iowrite32(0, priv->regs + TSI721_IBWIN_LB(i));
13099673b883SAlexandre Bounine 	priv->ibwin_cnt = TSI721_IBWIN_NUM;
131048618fb4SAlexandre Bounine }
131148618fb4SAlexandre Bounine 
1312748353ccSAlexandre Bounine /*
1313748353ccSAlexandre Bounine  * tsi721_close_sr2pc_mapping - closes all active inbound (SRIO->PCIe)
1314748353ccSAlexandre Bounine  * translation regions.
1315748353ccSAlexandre Bounine  * @priv: pointer to tsi721 device private data
1316748353ccSAlexandre Bounine  */
tsi721_close_sr2pc_mapping(struct tsi721_device * priv)1317748353ccSAlexandre Bounine static void tsi721_close_sr2pc_mapping(struct tsi721_device *priv)
1318748353ccSAlexandre Bounine {
1319748353ccSAlexandre Bounine 	struct tsi721_ib_win *ib_win;
1320748353ccSAlexandre Bounine 	int i;
1321748353ccSAlexandre Bounine 
1322748353ccSAlexandre Bounine 	/* Disable all active SR2PC inbound windows */
1323748353ccSAlexandre Bounine 	for (i = 0; i < TSI721_IBWIN_NUM; i++) {
1324748353ccSAlexandre Bounine 		ib_win = &priv->ib_win[i];
1325748353ccSAlexandre Bounine 		if (ib_win->active) {
1326748353ccSAlexandre Bounine 			iowrite32(0, priv->regs + TSI721_IBWIN_LB(i));
1327748353ccSAlexandre Bounine 			ib_win->active = false;
1328748353ccSAlexandre Bounine 		}
1329748353ccSAlexandre Bounine 	}
1330748353ccSAlexandre Bounine }
1331748353ccSAlexandre Bounine 
133248618fb4SAlexandre Bounine /**
133348618fb4SAlexandre Bounine  * tsi721_port_write_init - Inbound port write interface init
133448618fb4SAlexandre Bounine  * @priv: pointer to tsi721 private data
133548618fb4SAlexandre Bounine  *
133648618fb4SAlexandre Bounine  * Initializes inbound port write handler.
133748618fb4SAlexandre Bounine  * Returns %0 on success or %-ENOMEM on failure.
133848618fb4SAlexandre Bounine  */
tsi721_port_write_init(struct tsi721_device * priv)133948618fb4SAlexandre Bounine static int tsi721_port_write_init(struct tsi721_device *priv)
134048618fb4SAlexandre Bounine {
134148618fb4SAlexandre Bounine 	priv->pw_discard_count = 0;
134248618fb4SAlexandre Bounine 	INIT_WORK(&priv->pw_work, tsi721_pw_dpc);
134348618fb4SAlexandre Bounine 	spin_lock_init(&priv->pw_fifo_lock);
134448618fb4SAlexandre Bounine 	if (kfifo_alloc(&priv->pw_fifo,
134548618fb4SAlexandre Bounine 			TSI721_RIO_PW_MSG_SIZE * 32, GFP_KERNEL)) {
134672d8a0d2SAlexandre Bounine 		tsi_err(&priv->pdev->dev, "PW FIFO allocation failed");
134748618fb4SAlexandre Bounine 		return -ENOMEM;
134848618fb4SAlexandre Bounine 	}
134948618fb4SAlexandre Bounine 
135048618fb4SAlexandre Bounine 	/* Use reliable port-write capture mode */
135148618fb4SAlexandre Bounine 	iowrite32(TSI721_RIO_PW_CTL_PWC_REL, priv->regs + TSI721_RIO_PW_CTL);
135248618fb4SAlexandre Bounine 	return 0;
135348618fb4SAlexandre Bounine }
135448618fb4SAlexandre Bounine 
tsi721_port_write_free(struct tsi721_device * priv)1355748353ccSAlexandre Bounine static void tsi721_port_write_free(struct tsi721_device *priv)
1356748353ccSAlexandre Bounine {
1357748353ccSAlexandre Bounine 	kfifo_free(&priv->pw_fifo);
1358748353ccSAlexandre Bounine }
1359748353ccSAlexandre Bounine 
tsi721_doorbell_init(struct tsi721_device * priv)136048618fb4SAlexandre Bounine static int tsi721_doorbell_init(struct tsi721_device *priv)
136148618fb4SAlexandre Bounine {
136248618fb4SAlexandre Bounine 	/* Outbound Doorbells do not require any setup.
136348618fb4SAlexandre Bounine 	 * Tsi721 uses dedicated PCI BAR1 to generate doorbells.
136448618fb4SAlexandre Bounine 	 * That BAR1 was mapped during the probe routine.
136548618fb4SAlexandre Bounine 	 */
136648618fb4SAlexandre Bounine 
136748618fb4SAlexandre Bounine 	/* Initialize Inbound Doorbell processing DPC and queue */
136848618fb4SAlexandre Bounine 	priv->db_discard_count = 0;
136948618fb4SAlexandre Bounine 	INIT_WORK(&priv->idb_work, tsi721_db_dpc);
137048618fb4SAlexandre Bounine 
137148618fb4SAlexandre Bounine 	/* Allocate buffer for inbound doorbells queue */
1372750afb08SLuis Chamberlain 	priv->idb_base = dma_alloc_coherent(&priv->pdev->dev,
137348618fb4SAlexandre Bounine 					    IDB_QSIZE * TSI721_IDB_ENTRY_SIZE,
137448618fb4SAlexandre Bounine 					    &priv->idb_dma, GFP_KERNEL);
137548618fb4SAlexandre Bounine 	if (!priv->idb_base)
137648618fb4SAlexandre Bounine 		return -ENOMEM;
137748618fb4SAlexandre Bounine 
137872d8a0d2SAlexandre Bounine 	tsi_debug(DBELL, &priv->pdev->dev,
137972d8a0d2SAlexandre Bounine 		  "Allocated IDB buffer @ %p (phys = %pad)",
138072d8a0d2SAlexandre Bounine 		  priv->idb_base, &priv->idb_dma);
138148618fb4SAlexandre Bounine 
138248618fb4SAlexandre Bounine 	iowrite32(TSI721_IDQ_SIZE_VAL(IDB_QSIZE),
138348618fb4SAlexandre Bounine 		priv->regs + TSI721_IDQ_SIZE(IDB_QUEUE));
138448618fb4SAlexandre Bounine 	iowrite32(((u64)priv->idb_dma >> 32),
138548618fb4SAlexandre Bounine 		priv->regs + TSI721_IDQ_BASEU(IDB_QUEUE));
138648618fb4SAlexandre Bounine 	iowrite32(((u64)priv->idb_dma & TSI721_IDQ_BASEL_ADDR),
138748618fb4SAlexandre Bounine 		priv->regs + TSI721_IDQ_BASEL(IDB_QUEUE));
138848618fb4SAlexandre Bounine 	/* Enable accepting all inbound doorbells */
138948618fb4SAlexandre Bounine 	iowrite32(0, priv->regs + TSI721_IDQ_MASK(IDB_QUEUE));
139048618fb4SAlexandre Bounine 
139148618fb4SAlexandre Bounine 	iowrite32(TSI721_IDQ_INIT, priv->regs + TSI721_IDQ_CTL(IDB_QUEUE));
139248618fb4SAlexandre Bounine 
139348618fb4SAlexandre Bounine 	iowrite32(0, priv->regs + TSI721_IDQ_RP(IDB_QUEUE));
139448618fb4SAlexandre Bounine 
139548618fb4SAlexandre Bounine 	return 0;
139648618fb4SAlexandre Bounine }
139748618fb4SAlexandre Bounine 
tsi721_doorbell_free(struct tsi721_device * priv)139848618fb4SAlexandre Bounine static void tsi721_doorbell_free(struct tsi721_device *priv)
139948618fb4SAlexandre Bounine {
140048618fb4SAlexandre Bounine 	if (priv->idb_base == NULL)
140148618fb4SAlexandre Bounine 		return;
140248618fb4SAlexandre Bounine 
140348618fb4SAlexandre Bounine 	/* Free buffer allocated for inbound doorbell queue */
140448618fb4SAlexandre Bounine 	dma_free_coherent(&priv->pdev->dev, IDB_QSIZE * TSI721_IDB_ENTRY_SIZE,
140548618fb4SAlexandre Bounine 			  priv->idb_base, priv->idb_dma);
140648618fb4SAlexandre Bounine 	priv->idb_base = NULL;
140748618fb4SAlexandre Bounine }
140848618fb4SAlexandre Bounine 
14099eaa3d9bSAlexandre Bounine /**
14109eaa3d9bSAlexandre Bounine  * tsi721_bdma_maint_init - Initialize maintenance request BDMA channel.
14119eaa3d9bSAlexandre Bounine  * @priv: pointer to tsi721 private data
14129eaa3d9bSAlexandre Bounine  *
14139eaa3d9bSAlexandre Bounine  * Initialize BDMA channel allocated for RapidIO maintenance read/write
14149eaa3d9bSAlexandre Bounine  * request generation
14159eaa3d9bSAlexandre Bounine  * Returns %0 on success or %-ENOMEM on failure.
14169eaa3d9bSAlexandre Bounine  */
tsi721_bdma_maint_init(struct tsi721_device * priv)14179eaa3d9bSAlexandre Bounine static int tsi721_bdma_maint_init(struct tsi721_device *priv)
141848618fb4SAlexandre Bounine {
141948618fb4SAlexandre Bounine 	struct tsi721_dma_desc *bd_ptr;
142048618fb4SAlexandre Bounine 	u64		*sts_ptr;
142148618fb4SAlexandre Bounine 	dma_addr_t	bd_phys, sts_phys;
142248618fb4SAlexandre Bounine 	int		sts_size;
14239eaa3d9bSAlexandre Bounine 	int		bd_num = 2;
14249eaa3d9bSAlexandre Bounine 	void __iomem	*regs;
142548618fb4SAlexandre Bounine 
142672d8a0d2SAlexandre Bounine 	tsi_debug(MAINT, &priv->pdev->dev,
142772d8a0d2SAlexandre Bounine 		  "Init BDMA_%d Maintenance requests", TSI721_DMACH_MAINT);
142848618fb4SAlexandre Bounine 
142948618fb4SAlexandre Bounine 	/*
143048618fb4SAlexandre Bounine 	 * Initialize DMA channel for maintenance requests
143148618fb4SAlexandre Bounine 	 */
143248618fb4SAlexandre Bounine 
14339eaa3d9bSAlexandre Bounine 	priv->mdma.ch_id = TSI721_DMACH_MAINT;
14349eaa3d9bSAlexandre Bounine 	regs = priv->regs + TSI721_DMAC_BASE(TSI721_DMACH_MAINT);
14359eaa3d9bSAlexandre Bounine 
143648618fb4SAlexandre Bounine 	/* Allocate space for DMA descriptors */
1437750afb08SLuis Chamberlain 	bd_ptr = dma_alloc_coherent(&priv->pdev->dev,
143848618fb4SAlexandre Bounine 				    bd_num * sizeof(struct tsi721_dma_desc),
143948618fb4SAlexandre Bounine 				    &bd_phys, GFP_KERNEL);
144048618fb4SAlexandre Bounine 	if (!bd_ptr)
144148618fb4SAlexandre Bounine 		return -ENOMEM;
144248618fb4SAlexandre Bounine 
14439eaa3d9bSAlexandre Bounine 	priv->mdma.bd_num = bd_num;
14449eaa3d9bSAlexandre Bounine 	priv->mdma.bd_phys = bd_phys;
14459eaa3d9bSAlexandre Bounine 	priv->mdma.bd_base = bd_ptr;
144648618fb4SAlexandre Bounine 
144772d8a0d2SAlexandre Bounine 	tsi_debug(MAINT, &priv->pdev->dev, "DMA descriptors @ %p (phys = %pad)",
144872d8a0d2SAlexandre Bounine 		  bd_ptr, &bd_phys);
144948618fb4SAlexandre Bounine 
145048618fb4SAlexandre Bounine 	/* Allocate space for descriptor status FIFO */
145148618fb4SAlexandre Bounine 	sts_size = (bd_num >= TSI721_DMA_MINSTSSZ) ?
145248618fb4SAlexandre Bounine 					bd_num : TSI721_DMA_MINSTSSZ;
145348618fb4SAlexandre Bounine 	sts_size = roundup_pow_of_two(sts_size);
1454750afb08SLuis Chamberlain 	sts_ptr = dma_alloc_coherent(&priv->pdev->dev,
145548618fb4SAlexandre Bounine 				     sts_size * sizeof(struct tsi721_dma_sts),
145648618fb4SAlexandre Bounine 				     &sts_phys, GFP_KERNEL);
145748618fb4SAlexandre Bounine 	if (!sts_ptr) {
145848618fb4SAlexandre Bounine 		/* Free space allocated for DMA descriptors */
145948618fb4SAlexandre Bounine 		dma_free_coherent(&priv->pdev->dev,
146048618fb4SAlexandre Bounine 				  bd_num * sizeof(struct tsi721_dma_desc),
146148618fb4SAlexandre Bounine 				  bd_ptr, bd_phys);
14629eaa3d9bSAlexandre Bounine 		priv->mdma.bd_base = NULL;
146348618fb4SAlexandre Bounine 		return -ENOMEM;
146448618fb4SAlexandre Bounine 	}
146548618fb4SAlexandre Bounine 
14669eaa3d9bSAlexandre Bounine 	priv->mdma.sts_phys = sts_phys;
14679eaa3d9bSAlexandre Bounine 	priv->mdma.sts_base = sts_ptr;
14689eaa3d9bSAlexandre Bounine 	priv->mdma.sts_size = sts_size;
146948618fb4SAlexandre Bounine 
147072d8a0d2SAlexandre Bounine 	tsi_debug(MAINT, &priv->pdev->dev,
147172d8a0d2SAlexandre Bounine 		"desc status FIFO @ %p (phys = %pad) size=0x%x",
147272d8a0d2SAlexandre Bounine 		sts_ptr, &sts_phys, sts_size);
147348618fb4SAlexandre Bounine 
147448618fb4SAlexandre Bounine 	/* Initialize DMA descriptors ring */
147548618fb4SAlexandre Bounine 	bd_ptr[bd_num - 1].type_id = cpu_to_le32(DTYPE3 << 29);
147648618fb4SAlexandre Bounine 	bd_ptr[bd_num - 1].next_lo = cpu_to_le32((u64)bd_phys &
147748618fb4SAlexandre Bounine 						 TSI721_DMAC_DPTRL_MASK);
147848618fb4SAlexandre Bounine 	bd_ptr[bd_num - 1].next_hi = cpu_to_le32((u64)bd_phys >> 32);
147948618fb4SAlexandre Bounine 
148048618fb4SAlexandre Bounine 	/* Setup DMA descriptor pointers */
14819eaa3d9bSAlexandre Bounine 	iowrite32(((u64)bd_phys >> 32),	regs + TSI721_DMAC_DPTRH);
148248618fb4SAlexandre Bounine 	iowrite32(((u64)bd_phys & TSI721_DMAC_DPTRL_MASK),
14839eaa3d9bSAlexandre Bounine 		regs + TSI721_DMAC_DPTRL);
148448618fb4SAlexandre Bounine 
148548618fb4SAlexandre Bounine 	/* Setup descriptor status FIFO */
14869eaa3d9bSAlexandre Bounine 	iowrite32(((u64)sts_phys >> 32), regs + TSI721_DMAC_DSBH);
148748618fb4SAlexandre Bounine 	iowrite32(((u64)sts_phys & TSI721_DMAC_DSBL_MASK),
14889eaa3d9bSAlexandre Bounine 		regs + TSI721_DMAC_DSBL);
148948618fb4SAlexandre Bounine 	iowrite32(TSI721_DMAC_DSSZ_SIZE(sts_size),
14909eaa3d9bSAlexandre Bounine 		regs + TSI721_DMAC_DSSZ);
149148618fb4SAlexandre Bounine 
149248618fb4SAlexandre Bounine 	/* Clear interrupt bits */
14939eaa3d9bSAlexandre Bounine 	iowrite32(TSI721_DMAC_INT_ALL, regs + TSI721_DMAC_INT);
149448618fb4SAlexandre Bounine 
14959eaa3d9bSAlexandre Bounine 	ioread32(regs + TSI721_DMAC_INT);
149648618fb4SAlexandre Bounine 
149748618fb4SAlexandre Bounine 	/* Toggle DMA channel initialization */
14989eaa3d9bSAlexandre Bounine 	iowrite32(TSI721_DMAC_CTL_INIT,	regs + TSI721_DMAC_CTL);
14999eaa3d9bSAlexandre Bounine 	ioread32(regs + TSI721_DMAC_CTL);
150048618fb4SAlexandre Bounine 	udelay(10);
150148618fb4SAlexandre Bounine 
150248618fb4SAlexandre Bounine 	return 0;
150348618fb4SAlexandre Bounine }
150448618fb4SAlexandre Bounine 
tsi721_bdma_maint_free(struct tsi721_device * priv)15059eaa3d9bSAlexandre Bounine static int tsi721_bdma_maint_free(struct tsi721_device *priv)
150648618fb4SAlexandre Bounine {
150748618fb4SAlexandre Bounine 	u32 ch_stat;
15089eaa3d9bSAlexandre Bounine 	struct tsi721_bdma_maint *mdma = &priv->mdma;
15099eaa3d9bSAlexandre Bounine 	void __iomem *regs = priv->regs + TSI721_DMAC_BASE(mdma->ch_id);
151048618fb4SAlexandre Bounine 
15119eaa3d9bSAlexandre Bounine 	if (mdma->bd_base == NULL)
151248618fb4SAlexandre Bounine 		return 0;
151348618fb4SAlexandre Bounine 
151448618fb4SAlexandre Bounine 	/* Check if DMA channel still running */
15159eaa3d9bSAlexandre Bounine 	ch_stat = ioread32(regs + TSI721_DMAC_STS);
151648618fb4SAlexandre Bounine 	if (ch_stat & TSI721_DMAC_STS_RUN)
151748618fb4SAlexandre Bounine 		return -EFAULT;
151848618fb4SAlexandre Bounine 
151948618fb4SAlexandre Bounine 	/* Put DMA channel into init state */
15209eaa3d9bSAlexandre Bounine 	iowrite32(TSI721_DMAC_CTL_INIT,	regs + TSI721_DMAC_CTL);
152148618fb4SAlexandre Bounine 
152248618fb4SAlexandre Bounine 	/* Free space allocated for DMA descriptors */
152348618fb4SAlexandre Bounine 	dma_free_coherent(&priv->pdev->dev,
15249eaa3d9bSAlexandre Bounine 		mdma->bd_num * sizeof(struct tsi721_dma_desc),
15259eaa3d9bSAlexandre Bounine 		mdma->bd_base, mdma->bd_phys);
15269eaa3d9bSAlexandre Bounine 	mdma->bd_base = NULL;
152748618fb4SAlexandre Bounine 
152848618fb4SAlexandre Bounine 	/* Free space allocated for status FIFO */
152948618fb4SAlexandre Bounine 	dma_free_coherent(&priv->pdev->dev,
15309eaa3d9bSAlexandre Bounine 		mdma->sts_size * sizeof(struct tsi721_dma_sts),
15319eaa3d9bSAlexandre Bounine 		mdma->sts_base, mdma->sts_phys);
15329eaa3d9bSAlexandre Bounine 	mdma->sts_base = NULL;
153348618fb4SAlexandre Bounine 	return 0;
153448618fb4SAlexandre Bounine }
153548618fb4SAlexandre Bounine 
153648618fb4SAlexandre Bounine /* Enable Inbound Messaging Interrupts */
153748618fb4SAlexandre Bounine static void
tsi721_imsg_interrupt_enable(struct tsi721_device * priv,int ch,u32 inte_mask)153848618fb4SAlexandre Bounine tsi721_imsg_interrupt_enable(struct tsi721_device *priv, int ch,
153948618fb4SAlexandre Bounine 				  u32 inte_mask)
154048618fb4SAlexandre Bounine {
154148618fb4SAlexandre Bounine 	u32 rval;
154248618fb4SAlexandre Bounine 
154348618fb4SAlexandre Bounine 	if (!inte_mask)
154448618fb4SAlexandre Bounine 		return;
154548618fb4SAlexandre Bounine 
154648618fb4SAlexandre Bounine 	/* Clear pending Inbound Messaging interrupts */
154748618fb4SAlexandre Bounine 	iowrite32(inte_mask, priv->regs + TSI721_IBDMAC_INT(ch));
154848618fb4SAlexandre Bounine 
154948618fb4SAlexandre Bounine 	/* Enable Inbound Messaging interrupts */
155048618fb4SAlexandre Bounine 	rval = ioread32(priv->regs + TSI721_IBDMAC_INTE(ch));
155148618fb4SAlexandre Bounine 	iowrite32(rval | inte_mask, priv->regs + TSI721_IBDMAC_INTE(ch));
155248618fb4SAlexandre Bounine 
155348618fb4SAlexandre Bounine 	if (priv->flags & TSI721_USING_MSIX)
155448618fb4SAlexandre Bounine 		return; /* Finished if we are in MSI-X mode */
155548618fb4SAlexandre Bounine 
155648618fb4SAlexandre Bounine 	/*
155748618fb4SAlexandre Bounine 	 * For MSI and INTA interrupt signalling we need to enable next levels
155848618fb4SAlexandre Bounine 	 */
155948618fb4SAlexandre Bounine 
156048618fb4SAlexandre Bounine 	/* Enable Device Channel Interrupt */
156148618fb4SAlexandre Bounine 	rval = ioread32(priv->regs + TSI721_DEV_CHAN_INTE);
156248618fb4SAlexandre Bounine 	iowrite32(rval | TSI721_INT_IMSG_CHAN(ch),
156348618fb4SAlexandre Bounine 		  priv->regs + TSI721_DEV_CHAN_INTE);
156448618fb4SAlexandre Bounine }
156548618fb4SAlexandre Bounine 
156648618fb4SAlexandre Bounine /* Disable Inbound Messaging Interrupts */
156748618fb4SAlexandre Bounine static void
tsi721_imsg_interrupt_disable(struct tsi721_device * priv,int ch,u32 inte_mask)156848618fb4SAlexandre Bounine tsi721_imsg_interrupt_disable(struct tsi721_device *priv, int ch,
156948618fb4SAlexandre Bounine 				   u32 inte_mask)
157048618fb4SAlexandre Bounine {
157148618fb4SAlexandre Bounine 	u32 rval;
157248618fb4SAlexandre Bounine 
157348618fb4SAlexandre Bounine 	if (!inte_mask)
157448618fb4SAlexandre Bounine 		return;
157548618fb4SAlexandre Bounine 
157648618fb4SAlexandre Bounine 	/* Clear pending Inbound Messaging interrupts */
157748618fb4SAlexandre Bounine 	iowrite32(inte_mask, priv->regs + TSI721_IBDMAC_INT(ch));
157848618fb4SAlexandre Bounine 
157948618fb4SAlexandre Bounine 	/* Disable Inbound Messaging interrupts */
158048618fb4SAlexandre Bounine 	rval = ioread32(priv->regs + TSI721_IBDMAC_INTE(ch));
158148618fb4SAlexandre Bounine 	rval &= ~inte_mask;
158248618fb4SAlexandre Bounine 	iowrite32(rval, priv->regs + TSI721_IBDMAC_INTE(ch));
158348618fb4SAlexandre Bounine 
158448618fb4SAlexandre Bounine 	if (priv->flags & TSI721_USING_MSIX)
158548618fb4SAlexandre Bounine 		return; /* Finished if we are in MSI-X mode */
158648618fb4SAlexandre Bounine 
158748618fb4SAlexandre Bounine 	/*
158848618fb4SAlexandre Bounine 	 * For MSI and INTA interrupt signalling we need to disable next levels
158948618fb4SAlexandre Bounine 	 */
159048618fb4SAlexandre Bounine 
159148618fb4SAlexandre Bounine 	/* Disable Device Channel Interrupt */
159248618fb4SAlexandre Bounine 	rval = ioread32(priv->regs + TSI721_DEV_CHAN_INTE);
159348618fb4SAlexandre Bounine 	rval &= ~TSI721_INT_IMSG_CHAN(ch);
159448618fb4SAlexandre Bounine 	iowrite32(rval, priv->regs + TSI721_DEV_CHAN_INTE);
159548618fb4SAlexandre Bounine }
159648618fb4SAlexandre Bounine 
159748618fb4SAlexandre Bounine /* Enable Outbound Messaging interrupts */
159848618fb4SAlexandre Bounine static void
tsi721_omsg_interrupt_enable(struct tsi721_device * priv,int ch,u32 inte_mask)159948618fb4SAlexandre Bounine tsi721_omsg_interrupt_enable(struct tsi721_device *priv, int ch,
160048618fb4SAlexandre Bounine 				  u32 inte_mask)
160148618fb4SAlexandre Bounine {
160248618fb4SAlexandre Bounine 	u32 rval;
160348618fb4SAlexandre Bounine 
160448618fb4SAlexandre Bounine 	if (!inte_mask)
160548618fb4SAlexandre Bounine 		return;
160648618fb4SAlexandre Bounine 
160748618fb4SAlexandre Bounine 	/* Clear pending Outbound Messaging interrupts */
160848618fb4SAlexandre Bounine 	iowrite32(inte_mask, priv->regs + TSI721_OBDMAC_INT(ch));
160948618fb4SAlexandre Bounine 
161048618fb4SAlexandre Bounine 	/* Enable Outbound Messaging channel interrupts */
161148618fb4SAlexandre Bounine 	rval = ioread32(priv->regs + TSI721_OBDMAC_INTE(ch));
161248618fb4SAlexandre Bounine 	iowrite32(rval | inte_mask, priv->regs + TSI721_OBDMAC_INTE(ch));
161348618fb4SAlexandre Bounine 
161448618fb4SAlexandre Bounine 	if (priv->flags & TSI721_USING_MSIX)
161548618fb4SAlexandre Bounine 		return; /* Finished if we are in MSI-X mode */
161648618fb4SAlexandre Bounine 
161748618fb4SAlexandre Bounine 	/*
161848618fb4SAlexandre Bounine 	 * For MSI and INTA interrupt signalling we need to enable next levels
161948618fb4SAlexandre Bounine 	 */
162048618fb4SAlexandre Bounine 
162148618fb4SAlexandre Bounine 	/* Enable Device Channel Interrupt */
162248618fb4SAlexandre Bounine 	rval = ioread32(priv->regs + TSI721_DEV_CHAN_INTE);
162348618fb4SAlexandre Bounine 	iowrite32(rval | TSI721_INT_OMSG_CHAN(ch),
162448618fb4SAlexandre Bounine 		  priv->regs + TSI721_DEV_CHAN_INTE);
162548618fb4SAlexandre Bounine }
162648618fb4SAlexandre Bounine 
162748618fb4SAlexandre Bounine /* Disable Outbound Messaging interrupts */
162848618fb4SAlexandre Bounine static void
tsi721_omsg_interrupt_disable(struct tsi721_device * priv,int ch,u32 inte_mask)162948618fb4SAlexandre Bounine tsi721_omsg_interrupt_disable(struct tsi721_device *priv, int ch,
163048618fb4SAlexandre Bounine 				   u32 inte_mask)
163148618fb4SAlexandre Bounine {
163248618fb4SAlexandre Bounine 	u32 rval;
163348618fb4SAlexandre Bounine 
163448618fb4SAlexandre Bounine 	if (!inte_mask)
163548618fb4SAlexandre Bounine 		return;
163648618fb4SAlexandre Bounine 
163748618fb4SAlexandre Bounine 	/* Clear pending Outbound Messaging interrupts */
163848618fb4SAlexandre Bounine 	iowrite32(inte_mask, priv->regs + TSI721_OBDMAC_INT(ch));
163948618fb4SAlexandre Bounine 
164048618fb4SAlexandre Bounine 	/* Disable Outbound Messaging interrupts */
164148618fb4SAlexandre Bounine 	rval = ioread32(priv->regs + TSI721_OBDMAC_INTE(ch));
164248618fb4SAlexandre Bounine 	rval &= ~inte_mask;
164348618fb4SAlexandre Bounine 	iowrite32(rval, priv->regs + TSI721_OBDMAC_INTE(ch));
164448618fb4SAlexandre Bounine 
164548618fb4SAlexandre Bounine 	if (priv->flags & TSI721_USING_MSIX)
164648618fb4SAlexandre Bounine 		return; /* Finished if we are in MSI-X mode */
164748618fb4SAlexandre Bounine 
164848618fb4SAlexandre Bounine 	/*
164948618fb4SAlexandre Bounine 	 * For MSI and INTA interrupt signalling we need to disable next levels
165048618fb4SAlexandre Bounine 	 */
165148618fb4SAlexandre Bounine 
165248618fb4SAlexandre Bounine 	/* Disable Device Channel Interrupt */
165348618fb4SAlexandre Bounine 	rval = ioread32(priv->regs + TSI721_DEV_CHAN_INTE);
165448618fb4SAlexandre Bounine 	rval &= ~TSI721_INT_OMSG_CHAN(ch);
165548618fb4SAlexandre Bounine 	iowrite32(rval, priv->regs + TSI721_DEV_CHAN_INTE);
165648618fb4SAlexandre Bounine }
165748618fb4SAlexandre Bounine 
165848618fb4SAlexandre Bounine /**
165948618fb4SAlexandre Bounine  * tsi721_add_outb_message - Add message to the Tsi721 outbound message queue
166048618fb4SAlexandre Bounine  * @mport: Master port with outbound message queue
166148618fb4SAlexandre Bounine  * @rdev: Target of outbound message
166248618fb4SAlexandre Bounine  * @mbox: Outbound mailbox
166348618fb4SAlexandre Bounine  * @buffer: Message to add to outbound queue
166448618fb4SAlexandre Bounine  * @len: Length of message
166548618fb4SAlexandre Bounine  */
166648618fb4SAlexandre Bounine static int
tsi721_add_outb_message(struct rio_mport * mport,struct rio_dev * rdev,int mbox,void * buffer,size_t len)166748618fb4SAlexandre Bounine tsi721_add_outb_message(struct rio_mport *mport, struct rio_dev *rdev, int mbox,
166848618fb4SAlexandre Bounine 			void *buffer, size_t len)
166948618fb4SAlexandre Bounine {
167048618fb4SAlexandre Bounine 	struct tsi721_device *priv = mport->priv;
167148618fb4SAlexandre Bounine 	struct tsi721_omsg_desc *desc;
167248618fb4SAlexandre Bounine 	u32 tx_slot;
16732ece1cafSAlexandre Bounine 	unsigned long flags;
167448618fb4SAlexandre Bounine 
167548618fb4SAlexandre Bounine 	if (!priv->omsg_init[mbox] ||
167648618fb4SAlexandre Bounine 	    len > TSI721_MSG_MAX_SIZE || len < 8)
167748618fb4SAlexandre Bounine 		return -EINVAL;
167848618fb4SAlexandre Bounine 
16792ece1cafSAlexandre Bounine 	spin_lock_irqsave(&priv->omsg_ring[mbox].lock, flags);
16802ece1cafSAlexandre Bounine 
168148618fb4SAlexandre Bounine 	tx_slot = priv->omsg_ring[mbox].tx_slot;
168248618fb4SAlexandre Bounine 
168348618fb4SAlexandre Bounine 	/* Copy copy message into transfer buffer */
168448618fb4SAlexandre Bounine 	memcpy(priv->omsg_ring[mbox].omq_base[tx_slot], buffer, len);
168548618fb4SAlexandre Bounine 
168648618fb4SAlexandre Bounine 	if (len & 0x7)
168748618fb4SAlexandre Bounine 		len += 8;
168848618fb4SAlexandre Bounine 
168948618fb4SAlexandre Bounine 	/* Build descriptor associated with buffer */
169048618fb4SAlexandre Bounine 	desc = priv->omsg_ring[mbox].omd_base;
169148618fb4SAlexandre Bounine 	desc[tx_slot].type_id = cpu_to_le32((DTYPE4 << 29) | rdev->destid);
16922ece1cafSAlexandre Bounine #ifdef TSI721_OMSG_DESC_INT
16932ece1cafSAlexandre Bounine 	/* Request IOF_DONE interrupt generation for each N-th frame in queue */
169448618fb4SAlexandre Bounine 	if (tx_slot % 4 == 0)
169548618fb4SAlexandre Bounine 		desc[tx_slot].type_id |= cpu_to_le32(TSI721_OMD_IOF);
16962ece1cafSAlexandre Bounine #endif
169748618fb4SAlexandre Bounine 	desc[tx_slot].msg_info =
169848618fb4SAlexandre Bounine 		cpu_to_le32((mport->sys_size << 26) | (mbox << 22) |
169948618fb4SAlexandre Bounine 			    (0xe << 12) | (len & 0xff8));
170048618fb4SAlexandre Bounine 	desc[tx_slot].bufptr_lo =
170148618fb4SAlexandre Bounine 		cpu_to_le32((u64)priv->omsg_ring[mbox].omq_phys[tx_slot] &
170248618fb4SAlexandre Bounine 			    0xffffffff);
170348618fb4SAlexandre Bounine 	desc[tx_slot].bufptr_hi =
170448618fb4SAlexandre Bounine 		cpu_to_le32((u64)priv->omsg_ring[mbox].omq_phys[tx_slot] >> 32);
170548618fb4SAlexandre Bounine 
170648618fb4SAlexandre Bounine 	priv->omsg_ring[mbox].wr_count++;
170748618fb4SAlexandre Bounine 
170848618fb4SAlexandre Bounine 	/* Go to next descriptor */
170948618fb4SAlexandre Bounine 	if (++priv->omsg_ring[mbox].tx_slot == priv->omsg_ring[mbox].size) {
171048618fb4SAlexandre Bounine 		priv->omsg_ring[mbox].tx_slot = 0;
171148618fb4SAlexandre Bounine 		/* Move through the ring link descriptor at the end */
171248618fb4SAlexandre Bounine 		priv->omsg_ring[mbox].wr_count++;
171348618fb4SAlexandre Bounine 	}
171448618fb4SAlexandre Bounine 
171548618fb4SAlexandre Bounine 	mb();
171648618fb4SAlexandre Bounine 
171748618fb4SAlexandre Bounine 	/* Set new write count value */
171848618fb4SAlexandre Bounine 	iowrite32(priv->omsg_ring[mbox].wr_count,
171948618fb4SAlexandre Bounine 		priv->regs + TSI721_OBDMAC_DWRCNT(mbox));
172048618fb4SAlexandre Bounine 	ioread32(priv->regs + TSI721_OBDMAC_DWRCNT(mbox));
172148618fb4SAlexandre Bounine 
17222ece1cafSAlexandre Bounine 	spin_unlock_irqrestore(&priv->omsg_ring[mbox].lock, flags);
17232ece1cafSAlexandre Bounine 
172448618fb4SAlexandre Bounine 	return 0;
172548618fb4SAlexandre Bounine }
172648618fb4SAlexandre Bounine 
172748618fb4SAlexandre Bounine /**
172848618fb4SAlexandre Bounine  * tsi721_omsg_handler - Outbound Message Interrupt Handler
172948618fb4SAlexandre Bounine  * @priv: pointer to tsi721 private data
173048618fb4SAlexandre Bounine  * @ch:   number of OB MSG channel to service
173148618fb4SAlexandre Bounine  *
173248618fb4SAlexandre Bounine  * Services channel interrupts from outbound messaging engine.
173348618fb4SAlexandre Bounine  */
tsi721_omsg_handler(struct tsi721_device * priv,int ch)173448618fb4SAlexandre Bounine static void tsi721_omsg_handler(struct tsi721_device *priv, int ch)
173548618fb4SAlexandre Bounine {
173648618fb4SAlexandre Bounine 	u32 omsg_int;
1737748353ccSAlexandre Bounine 	struct rio_mport *mport = &priv->mport;
17382ece1cafSAlexandre Bounine 	void *dev_id = NULL;
17392ece1cafSAlexandre Bounine 	u32 tx_slot = 0xffffffff;
17402ece1cafSAlexandre Bounine 	int do_callback = 0;
174148618fb4SAlexandre Bounine 
174248618fb4SAlexandre Bounine 	spin_lock(&priv->omsg_ring[ch].lock);
174348618fb4SAlexandre Bounine 
174448618fb4SAlexandre Bounine 	omsg_int = ioread32(priv->regs + TSI721_OBDMAC_INT(ch));
174548618fb4SAlexandre Bounine 
174648618fb4SAlexandre Bounine 	if (omsg_int & TSI721_OBDMAC_INT_ST_FULL)
174772d8a0d2SAlexandre Bounine 		tsi_info(&priv->pdev->dev,
174872d8a0d2SAlexandre Bounine 			"OB MBOX%d: Status FIFO is full", ch);
174948618fb4SAlexandre Bounine 
175048618fb4SAlexandre Bounine 	if (omsg_int & (TSI721_OBDMAC_INT_DONE | TSI721_OBDMAC_INT_IOF_DONE)) {
175148618fb4SAlexandre Bounine 		u32 srd_ptr;
175248618fb4SAlexandre Bounine 		u64 *sts_ptr, last_ptr = 0, prev_ptr = 0;
175348618fb4SAlexandre Bounine 		int i, j;
175448618fb4SAlexandre Bounine 
175548618fb4SAlexandre Bounine 		/*
175648618fb4SAlexandre Bounine 		 * Find last successfully processed descriptor
175748618fb4SAlexandre Bounine 		 */
175848618fb4SAlexandre Bounine 
175948618fb4SAlexandre Bounine 		/* Check and clear descriptor status FIFO entries */
176048618fb4SAlexandre Bounine 		srd_ptr = priv->omsg_ring[ch].sts_rdptr;
176148618fb4SAlexandre Bounine 		sts_ptr = priv->omsg_ring[ch].sts_base;
176248618fb4SAlexandre Bounine 		j = srd_ptr * 8;
176348618fb4SAlexandre Bounine 		while (sts_ptr[j]) {
176448618fb4SAlexandre Bounine 			for (i = 0; i < 8 && sts_ptr[j]; i++, j++) {
176548618fb4SAlexandre Bounine 				prev_ptr = last_ptr;
176648618fb4SAlexandre Bounine 				last_ptr = le64_to_cpu(sts_ptr[j]);
176748618fb4SAlexandre Bounine 				sts_ptr[j] = 0;
176848618fb4SAlexandre Bounine 			}
176948618fb4SAlexandre Bounine 
177048618fb4SAlexandre Bounine 			++srd_ptr;
177148618fb4SAlexandre Bounine 			srd_ptr %= priv->omsg_ring[ch].sts_size;
177248618fb4SAlexandre Bounine 			j = srd_ptr * 8;
177348618fb4SAlexandre Bounine 		}
177448618fb4SAlexandre Bounine 
177548618fb4SAlexandre Bounine 		if (last_ptr == 0)
177648618fb4SAlexandre Bounine 			goto no_sts_update;
177748618fb4SAlexandre Bounine 
177848618fb4SAlexandre Bounine 		priv->omsg_ring[ch].sts_rdptr = srd_ptr;
177948618fb4SAlexandre Bounine 		iowrite32(srd_ptr, priv->regs + TSI721_OBDMAC_DSRP(ch));
178048618fb4SAlexandre Bounine 
1781748353ccSAlexandre Bounine 		if (!mport->outb_msg[ch].mcback)
178248618fb4SAlexandre Bounine 			goto no_sts_update;
178348618fb4SAlexandre Bounine 
178448618fb4SAlexandre Bounine 		/* Inform upper layer about transfer completion */
178548618fb4SAlexandre Bounine 
178648618fb4SAlexandre Bounine 		tx_slot = (last_ptr - (u64)priv->omsg_ring[ch].omd_phys)/
178748618fb4SAlexandre Bounine 						sizeof(struct tsi721_omsg_desc);
178848618fb4SAlexandre Bounine 
178948618fb4SAlexandre Bounine 		/*
179048618fb4SAlexandre Bounine 		 * Check if this is a Link Descriptor (LD).
179148618fb4SAlexandre Bounine 		 * If yes, ignore LD and use descriptor processed
179248618fb4SAlexandre Bounine 		 * before LD.
179348618fb4SAlexandre Bounine 		 */
179448618fb4SAlexandre Bounine 		if (tx_slot == priv->omsg_ring[ch].size) {
179548618fb4SAlexandre Bounine 			if (prev_ptr)
179648618fb4SAlexandre Bounine 				tx_slot = (prev_ptr -
179748618fb4SAlexandre Bounine 					(u64)priv->omsg_ring[ch].omd_phys)/
179848618fb4SAlexandre Bounine 						sizeof(struct tsi721_omsg_desc);
179948618fb4SAlexandre Bounine 			else
180048618fb4SAlexandre Bounine 				goto no_sts_update;
180148618fb4SAlexandre Bounine 		}
180248618fb4SAlexandre Bounine 
18032ece1cafSAlexandre Bounine 		if (tx_slot >= priv->omsg_ring[ch].size)
180472d8a0d2SAlexandre Bounine 			tsi_debug(OMSG, &priv->pdev->dev,
18052ece1cafSAlexandre Bounine 				  "OB_MSG tx_slot=%x > size=%x",
18062ece1cafSAlexandre Bounine 				  tx_slot, priv->omsg_ring[ch].size);
18072ece1cafSAlexandre Bounine 		WARN_ON(tx_slot >= priv->omsg_ring[ch].size);
18082ece1cafSAlexandre Bounine 
180948618fb4SAlexandre Bounine 		/* Move slot index to the next message to be sent */
181048618fb4SAlexandre Bounine 		++tx_slot;
181148618fb4SAlexandre Bounine 		if (tx_slot == priv->omsg_ring[ch].size)
181248618fb4SAlexandre Bounine 			tx_slot = 0;
18132ece1cafSAlexandre Bounine 
18142ece1cafSAlexandre Bounine 		dev_id = priv->omsg_ring[ch].dev_id;
18152ece1cafSAlexandre Bounine 		do_callback = 1;
181648618fb4SAlexandre Bounine 	}
181748618fb4SAlexandre Bounine 
181848618fb4SAlexandre Bounine no_sts_update:
181948618fb4SAlexandre Bounine 
182048618fb4SAlexandre Bounine 	if (omsg_int & TSI721_OBDMAC_INT_ERROR) {
182148618fb4SAlexandre Bounine 		/*
182248618fb4SAlexandre Bounine 		* Outbound message operation aborted due to error,
182348618fb4SAlexandre Bounine 		* reinitialize OB MSG channel
182448618fb4SAlexandre Bounine 		*/
182548618fb4SAlexandre Bounine 
182672d8a0d2SAlexandre Bounine 		tsi_debug(OMSG, &priv->pdev->dev, "OB MSG ABORT ch_stat=%x",
182748618fb4SAlexandre Bounine 			  ioread32(priv->regs + TSI721_OBDMAC_STS(ch)));
182848618fb4SAlexandre Bounine 
182948618fb4SAlexandre Bounine 		iowrite32(TSI721_OBDMAC_INT_ERROR,
183048618fb4SAlexandre Bounine 				priv->regs + TSI721_OBDMAC_INT(ch));
18312ece1cafSAlexandre Bounine 		iowrite32(TSI721_OBDMAC_CTL_RETRY_THR | TSI721_OBDMAC_CTL_INIT,
183248618fb4SAlexandre Bounine 				priv->regs + TSI721_OBDMAC_CTL(ch));
183348618fb4SAlexandre Bounine 		ioread32(priv->regs + TSI721_OBDMAC_CTL(ch));
183448618fb4SAlexandre Bounine 
183548618fb4SAlexandre Bounine 		/* Inform upper level to clear all pending tx slots */
18362ece1cafSAlexandre Bounine 		dev_id = priv->omsg_ring[ch].dev_id;
18372ece1cafSAlexandre Bounine 		tx_slot = priv->omsg_ring[ch].tx_slot;
18382ece1cafSAlexandre Bounine 		do_callback = 1;
18392ece1cafSAlexandre Bounine 
184048618fb4SAlexandre Bounine 		/* Synch tx_slot tracking */
184148618fb4SAlexandre Bounine 		iowrite32(priv->omsg_ring[ch].tx_slot,
184248618fb4SAlexandre Bounine 			priv->regs + TSI721_OBDMAC_DRDCNT(ch));
184348618fb4SAlexandre Bounine 		ioread32(priv->regs + TSI721_OBDMAC_DRDCNT(ch));
184448618fb4SAlexandre Bounine 		priv->omsg_ring[ch].wr_count = priv->omsg_ring[ch].tx_slot;
184548618fb4SAlexandre Bounine 		priv->omsg_ring[ch].sts_rdptr = 0;
184648618fb4SAlexandre Bounine 	}
184748618fb4SAlexandre Bounine 
184848618fb4SAlexandre Bounine 	/* Clear channel interrupts */
184948618fb4SAlexandre Bounine 	iowrite32(omsg_int, priv->regs + TSI721_OBDMAC_INT(ch));
185048618fb4SAlexandre Bounine 
185148618fb4SAlexandre Bounine 	if (!(priv->flags & TSI721_USING_MSIX)) {
185248618fb4SAlexandre Bounine 		u32 ch_inte;
185348618fb4SAlexandre Bounine 
185448618fb4SAlexandre Bounine 		/* Re-enable channel interrupts */
185548618fb4SAlexandre Bounine 		ch_inte = ioread32(priv->regs + TSI721_DEV_CHAN_INTE);
185648618fb4SAlexandre Bounine 		ch_inte |= TSI721_INT_OMSG_CHAN(ch);
185748618fb4SAlexandre Bounine 		iowrite32(ch_inte, priv->regs + TSI721_DEV_CHAN_INTE);
185848618fb4SAlexandre Bounine 	}
185948618fb4SAlexandre Bounine 
186048618fb4SAlexandre Bounine 	spin_unlock(&priv->omsg_ring[ch].lock);
18612ece1cafSAlexandre Bounine 
18622ece1cafSAlexandre Bounine 	if (mport->outb_msg[ch].mcback && do_callback)
18632ece1cafSAlexandre Bounine 		mport->outb_msg[ch].mcback(mport, dev_id, ch, tx_slot);
186448618fb4SAlexandre Bounine }
186548618fb4SAlexandre Bounine 
186648618fb4SAlexandre Bounine /**
186748618fb4SAlexandre Bounine  * tsi721_open_outb_mbox - Initialize Tsi721 outbound mailbox
186848618fb4SAlexandre Bounine  * @mport: Master port implementing Outbound Messaging Engine
186948618fb4SAlexandre Bounine  * @dev_id: Device specific pointer to pass on event
187048618fb4SAlexandre Bounine  * @mbox: Mailbox to open
187148618fb4SAlexandre Bounine  * @entries: Number of entries in the outbound mailbox ring
187248618fb4SAlexandre Bounine  */
tsi721_open_outb_mbox(struct rio_mport * mport,void * dev_id,int mbox,int entries)187348618fb4SAlexandre Bounine static int tsi721_open_outb_mbox(struct rio_mport *mport, void *dev_id,
187448618fb4SAlexandre Bounine 				 int mbox, int entries)
187548618fb4SAlexandre Bounine {
187648618fb4SAlexandre Bounine 	struct tsi721_device *priv = mport->priv;
187748618fb4SAlexandre Bounine 	struct tsi721_omsg_desc *bd_ptr;
187848618fb4SAlexandre Bounine 	int i, rc = 0;
187948618fb4SAlexandre Bounine 
188048618fb4SAlexandre Bounine 	if ((entries < TSI721_OMSGD_MIN_RING_SIZE) ||
188148618fb4SAlexandre Bounine 	    (entries > (TSI721_OMSGD_RING_SIZE)) ||
188248618fb4SAlexandre Bounine 	    (!is_power_of_2(entries)) || mbox >= RIO_MAX_MBOX) {
188348618fb4SAlexandre Bounine 		rc = -EINVAL;
188448618fb4SAlexandre Bounine 		goto out;
188548618fb4SAlexandre Bounine 	}
188648618fb4SAlexandre Bounine 
1887e519685dSAlexandre Bounine 	if ((mbox_sel & (1 << mbox)) == 0) {
1888e519685dSAlexandre Bounine 		rc = -ENODEV;
1889e519685dSAlexandre Bounine 		goto out;
1890e519685dSAlexandre Bounine 	}
1891e519685dSAlexandre Bounine 
189248618fb4SAlexandre Bounine 	priv->omsg_ring[mbox].dev_id = dev_id;
189348618fb4SAlexandre Bounine 	priv->omsg_ring[mbox].size = entries;
189448618fb4SAlexandre Bounine 	priv->omsg_ring[mbox].sts_rdptr = 0;
189548618fb4SAlexandre Bounine 	spin_lock_init(&priv->omsg_ring[mbox].lock);
189648618fb4SAlexandre Bounine 
189748618fb4SAlexandre Bounine 	/* Outbound Msg Buffer allocation based on
189848618fb4SAlexandre Bounine 	   the number of maximum descriptor entries */
189948618fb4SAlexandre Bounine 	for (i = 0; i < entries; i++) {
190048618fb4SAlexandre Bounine 		priv->omsg_ring[mbox].omq_base[i] =
190148618fb4SAlexandre Bounine 			dma_alloc_coherent(
190248618fb4SAlexandre Bounine 				&priv->pdev->dev, TSI721_MSG_BUFFER_SIZE,
190348618fb4SAlexandre Bounine 				&priv->omsg_ring[mbox].omq_phys[i],
190448618fb4SAlexandre Bounine 				GFP_KERNEL);
190548618fb4SAlexandre Bounine 		if (priv->omsg_ring[mbox].omq_base[i] == NULL) {
190672d8a0d2SAlexandre Bounine 			tsi_debug(OMSG, &priv->pdev->dev,
190772d8a0d2SAlexandre Bounine 				  "ENOMEM for OB_MSG_%d data buffer", mbox);
190848618fb4SAlexandre Bounine 			rc = -ENOMEM;
190948618fb4SAlexandre Bounine 			goto out_buf;
191048618fb4SAlexandre Bounine 		}
191148618fb4SAlexandre Bounine 	}
191248618fb4SAlexandre Bounine 
191348618fb4SAlexandre Bounine 	/* Outbound message descriptor allocation */
191448618fb4SAlexandre Bounine 	priv->omsg_ring[mbox].omd_base = dma_alloc_coherent(
191548618fb4SAlexandre Bounine 				&priv->pdev->dev,
191648618fb4SAlexandre Bounine 				(entries + 1) * sizeof(struct tsi721_omsg_desc),
191748618fb4SAlexandre Bounine 				&priv->omsg_ring[mbox].omd_phys, GFP_KERNEL);
191848618fb4SAlexandre Bounine 	if (priv->omsg_ring[mbox].omd_base == NULL) {
191972d8a0d2SAlexandre Bounine 		tsi_debug(OMSG, &priv->pdev->dev,
192072d8a0d2SAlexandre Bounine 			"ENOMEM for OB_MSG_%d descriptor memory", mbox);
192148618fb4SAlexandre Bounine 		rc = -ENOMEM;
192248618fb4SAlexandre Bounine 		goto out_buf;
192348618fb4SAlexandre Bounine 	}
192448618fb4SAlexandre Bounine 
192548618fb4SAlexandre Bounine 	priv->omsg_ring[mbox].tx_slot = 0;
192648618fb4SAlexandre Bounine 
192748618fb4SAlexandre Bounine 	/* Outbound message descriptor status FIFO allocation */
192848618fb4SAlexandre Bounine 	priv->omsg_ring[mbox].sts_size = roundup_pow_of_two(entries + 1);
1929750afb08SLuis Chamberlain 	priv->omsg_ring[mbox].sts_base = dma_alloc_coherent(&priv->pdev->dev,
1930750afb08SLuis Chamberlain 							    priv->omsg_ring[mbox].sts_size * sizeof(struct tsi721_dma_sts),
1931750afb08SLuis Chamberlain 							    &priv->omsg_ring[mbox].sts_phys,
1932750afb08SLuis Chamberlain 							    GFP_KERNEL);
193348618fb4SAlexandre Bounine 	if (priv->omsg_ring[mbox].sts_base == NULL) {
193472d8a0d2SAlexandre Bounine 		tsi_debug(OMSG, &priv->pdev->dev,
193572d8a0d2SAlexandre Bounine 			"ENOMEM for OB_MSG_%d status FIFO", mbox);
193648618fb4SAlexandre Bounine 		rc = -ENOMEM;
193748618fb4SAlexandre Bounine 		goto out_desc;
193848618fb4SAlexandre Bounine 	}
193948618fb4SAlexandre Bounine 
194048618fb4SAlexandre Bounine 	/*
194148618fb4SAlexandre Bounine 	 * Configure Outbound Messaging Engine
194248618fb4SAlexandre Bounine 	 */
194348618fb4SAlexandre Bounine 
194448618fb4SAlexandre Bounine 	/* Setup Outbound Message descriptor pointer */
194548618fb4SAlexandre Bounine 	iowrite32(((u64)priv->omsg_ring[mbox].omd_phys >> 32),
194648618fb4SAlexandre Bounine 			priv->regs + TSI721_OBDMAC_DPTRH(mbox));
194748618fb4SAlexandre Bounine 	iowrite32(((u64)priv->omsg_ring[mbox].omd_phys &
194848618fb4SAlexandre Bounine 					TSI721_OBDMAC_DPTRL_MASK),
194948618fb4SAlexandre Bounine 			priv->regs + TSI721_OBDMAC_DPTRL(mbox));
195048618fb4SAlexandre Bounine 
195148618fb4SAlexandre Bounine 	/* Setup Outbound Message descriptor status FIFO */
195248618fb4SAlexandre Bounine 	iowrite32(((u64)priv->omsg_ring[mbox].sts_phys >> 32),
195348618fb4SAlexandre Bounine 			priv->regs + TSI721_OBDMAC_DSBH(mbox));
195448618fb4SAlexandre Bounine 	iowrite32(((u64)priv->omsg_ring[mbox].sts_phys &
195548618fb4SAlexandre Bounine 					TSI721_OBDMAC_DSBL_MASK),
195648618fb4SAlexandre Bounine 			priv->regs + TSI721_OBDMAC_DSBL(mbox));
195748618fb4SAlexandre Bounine 	iowrite32(TSI721_DMAC_DSSZ_SIZE(priv->omsg_ring[mbox].sts_size),
195848618fb4SAlexandre Bounine 		priv->regs + (u32)TSI721_OBDMAC_DSSZ(mbox));
195948618fb4SAlexandre Bounine 
196048618fb4SAlexandre Bounine 	/* Enable interrupts */
196148618fb4SAlexandre Bounine 
196248618fb4SAlexandre Bounine #ifdef CONFIG_PCI_MSI
196348618fb4SAlexandre Bounine 	if (priv->flags & TSI721_USING_MSIX) {
1964748353ccSAlexandre Bounine 		int idx = TSI721_VECT_OMB0_DONE + mbox;
1965748353ccSAlexandre Bounine 
196648618fb4SAlexandre Bounine 		/* Request interrupt service if we are in MSI-X mode */
1967748353ccSAlexandre Bounine 		rc = request_irq(priv->msix[idx].vector, tsi721_omsg_msix, 0,
1968748353ccSAlexandre Bounine 				 priv->msix[idx].irq_name, (void *)priv);
196948618fb4SAlexandre Bounine 
197048618fb4SAlexandre Bounine 		if (rc) {
197172d8a0d2SAlexandre Bounine 			tsi_debug(OMSG, &priv->pdev->dev,
197272d8a0d2SAlexandre Bounine 				"Unable to get MSI-X IRQ for OBOX%d-DONE",
197372d8a0d2SAlexandre Bounine 				mbox);
197448618fb4SAlexandre Bounine 			goto out_stat;
197548618fb4SAlexandre Bounine 		}
197648618fb4SAlexandre Bounine 
1977748353ccSAlexandre Bounine 		idx = TSI721_VECT_OMB0_INT + mbox;
1978748353ccSAlexandre Bounine 		rc = request_irq(priv->msix[idx].vector, tsi721_omsg_msix, 0,
1979748353ccSAlexandre Bounine 				 priv->msix[idx].irq_name, (void *)priv);
198048618fb4SAlexandre Bounine 
198148618fb4SAlexandre Bounine 		if (rc)	{
198272d8a0d2SAlexandre Bounine 			tsi_debug(OMSG, &priv->pdev->dev,
198372d8a0d2SAlexandre Bounine 				"Unable to get MSI-X IRQ for MBOX%d-INT", mbox);
1984748353ccSAlexandre Bounine 			idx = TSI721_VECT_OMB0_DONE + mbox;
1985748353ccSAlexandre Bounine 			free_irq(priv->msix[idx].vector, (void *)priv);
198648618fb4SAlexandre Bounine 			goto out_stat;
198748618fb4SAlexandre Bounine 		}
198848618fb4SAlexandre Bounine 	}
198948618fb4SAlexandre Bounine #endif /* CONFIG_PCI_MSI */
199048618fb4SAlexandre Bounine 
199148618fb4SAlexandre Bounine 	tsi721_omsg_interrupt_enable(priv, mbox, TSI721_OBDMAC_INT_ALL);
199248618fb4SAlexandre Bounine 
199348618fb4SAlexandre Bounine 	/* Initialize Outbound Message descriptors ring */
199448618fb4SAlexandre Bounine 	bd_ptr = priv->omsg_ring[mbox].omd_base;
199548618fb4SAlexandre Bounine 	bd_ptr[entries].type_id = cpu_to_le32(DTYPE5 << 29);
199648618fb4SAlexandre Bounine 	bd_ptr[entries].msg_info = 0;
199748618fb4SAlexandre Bounine 	bd_ptr[entries].next_lo =
199848618fb4SAlexandre Bounine 		cpu_to_le32((u64)priv->omsg_ring[mbox].omd_phys &
199948618fb4SAlexandre Bounine 		TSI721_OBDMAC_DPTRL_MASK);
200048618fb4SAlexandre Bounine 	bd_ptr[entries].next_hi =
200148618fb4SAlexandre Bounine 		cpu_to_le32((u64)priv->omsg_ring[mbox].omd_phys >> 32);
200248618fb4SAlexandre Bounine 	priv->omsg_ring[mbox].wr_count = 0;
200348618fb4SAlexandre Bounine 	mb();
200448618fb4SAlexandre Bounine 
200548618fb4SAlexandre Bounine 	/* Initialize Outbound Message engine */
20062ece1cafSAlexandre Bounine 	iowrite32(TSI721_OBDMAC_CTL_RETRY_THR | TSI721_OBDMAC_CTL_INIT,
20072ece1cafSAlexandre Bounine 		  priv->regs + TSI721_OBDMAC_CTL(mbox));
200848618fb4SAlexandre Bounine 	ioread32(priv->regs + TSI721_OBDMAC_DWRCNT(mbox));
200948618fb4SAlexandre Bounine 	udelay(10);
201048618fb4SAlexandre Bounine 
201148618fb4SAlexandre Bounine 	priv->omsg_init[mbox] = 1;
201248618fb4SAlexandre Bounine 
201348618fb4SAlexandre Bounine 	return 0;
201448618fb4SAlexandre Bounine 
201548618fb4SAlexandre Bounine #ifdef CONFIG_PCI_MSI
201648618fb4SAlexandre Bounine out_stat:
201748618fb4SAlexandre Bounine 	dma_free_coherent(&priv->pdev->dev,
201848618fb4SAlexandre Bounine 		priv->omsg_ring[mbox].sts_size * sizeof(struct tsi721_dma_sts),
201948618fb4SAlexandre Bounine 		priv->omsg_ring[mbox].sts_base,
202048618fb4SAlexandre Bounine 		priv->omsg_ring[mbox].sts_phys);
202148618fb4SAlexandre Bounine 
202248618fb4SAlexandre Bounine 	priv->omsg_ring[mbox].sts_base = NULL;
202348618fb4SAlexandre Bounine #endif /* CONFIG_PCI_MSI */
202448618fb4SAlexandre Bounine 
202548618fb4SAlexandre Bounine out_desc:
202648618fb4SAlexandre Bounine 	dma_free_coherent(&priv->pdev->dev,
202748618fb4SAlexandre Bounine 		(entries + 1) * sizeof(struct tsi721_omsg_desc),
202848618fb4SAlexandre Bounine 		priv->omsg_ring[mbox].omd_base,
202948618fb4SAlexandre Bounine 		priv->omsg_ring[mbox].omd_phys);
203048618fb4SAlexandre Bounine 
203148618fb4SAlexandre Bounine 	priv->omsg_ring[mbox].omd_base = NULL;
203248618fb4SAlexandre Bounine 
203348618fb4SAlexandre Bounine out_buf:
203448618fb4SAlexandre Bounine 	for (i = 0; i < priv->omsg_ring[mbox].size; i++) {
203548618fb4SAlexandre Bounine 		if (priv->omsg_ring[mbox].omq_base[i]) {
203648618fb4SAlexandre Bounine 			dma_free_coherent(&priv->pdev->dev,
203748618fb4SAlexandre Bounine 				TSI721_MSG_BUFFER_SIZE,
203848618fb4SAlexandre Bounine 				priv->omsg_ring[mbox].omq_base[i],
203948618fb4SAlexandre Bounine 				priv->omsg_ring[mbox].omq_phys[i]);
204048618fb4SAlexandre Bounine 
204148618fb4SAlexandre Bounine 			priv->omsg_ring[mbox].omq_base[i] = NULL;
204248618fb4SAlexandre Bounine 		}
204348618fb4SAlexandre Bounine 	}
204448618fb4SAlexandre Bounine 
204548618fb4SAlexandre Bounine out:
204648618fb4SAlexandre Bounine 	return rc;
204748618fb4SAlexandre Bounine }
204848618fb4SAlexandre Bounine 
204948618fb4SAlexandre Bounine /**
205048618fb4SAlexandre Bounine  * tsi721_close_outb_mbox - Close Tsi721 outbound mailbox
205148618fb4SAlexandre Bounine  * @mport: Master port implementing the outbound message unit
205248618fb4SAlexandre Bounine  * @mbox: Mailbox to close
205348618fb4SAlexandre Bounine  */
tsi721_close_outb_mbox(struct rio_mport * mport,int mbox)205448618fb4SAlexandre Bounine static void tsi721_close_outb_mbox(struct rio_mport *mport, int mbox)
205548618fb4SAlexandre Bounine {
205648618fb4SAlexandre Bounine 	struct tsi721_device *priv = mport->priv;
205748618fb4SAlexandre Bounine 	u32 i;
205848618fb4SAlexandre Bounine 
205948618fb4SAlexandre Bounine 	if (!priv->omsg_init[mbox])
206048618fb4SAlexandre Bounine 		return;
206148618fb4SAlexandre Bounine 	priv->omsg_init[mbox] = 0;
206248618fb4SAlexandre Bounine 
206348618fb4SAlexandre Bounine 	/* Disable Interrupts */
206448618fb4SAlexandre Bounine 
206548618fb4SAlexandre Bounine 	tsi721_omsg_interrupt_disable(priv, mbox, TSI721_OBDMAC_INT_ALL);
206648618fb4SAlexandre Bounine 
206748618fb4SAlexandre Bounine #ifdef CONFIG_PCI_MSI
206848618fb4SAlexandre Bounine 	if (priv->flags & TSI721_USING_MSIX) {
206948618fb4SAlexandre Bounine 		free_irq(priv->msix[TSI721_VECT_OMB0_DONE + mbox].vector,
2070748353ccSAlexandre Bounine 			 (void *)priv);
207148618fb4SAlexandre Bounine 		free_irq(priv->msix[TSI721_VECT_OMB0_INT + mbox].vector,
2072748353ccSAlexandre Bounine 			 (void *)priv);
207348618fb4SAlexandre Bounine 	}
207448618fb4SAlexandre Bounine #endif /* CONFIG_PCI_MSI */
207548618fb4SAlexandre Bounine 
207648618fb4SAlexandre Bounine 	/* Free OMSG Descriptor Status FIFO */
207748618fb4SAlexandre Bounine 	dma_free_coherent(&priv->pdev->dev,
207848618fb4SAlexandre Bounine 		priv->omsg_ring[mbox].sts_size * sizeof(struct tsi721_dma_sts),
207948618fb4SAlexandre Bounine 		priv->omsg_ring[mbox].sts_base,
208048618fb4SAlexandre Bounine 		priv->omsg_ring[mbox].sts_phys);
208148618fb4SAlexandre Bounine 
208248618fb4SAlexandre Bounine 	priv->omsg_ring[mbox].sts_base = NULL;
208348618fb4SAlexandre Bounine 
208448618fb4SAlexandre Bounine 	/* Free OMSG descriptors */
208548618fb4SAlexandre Bounine 	dma_free_coherent(&priv->pdev->dev,
208648618fb4SAlexandre Bounine 		(priv->omsg_ring[mbox].size + 1) *
208748618fb4SAlexandre Bounine 			sizeof(struct tsi721_omsg_desc),
208848618fb4SAlexandre Bounine 		priv->omsg_ring[mbox].omd_base,
208948618fb4SAlexandre Bounine 		priv->omsg_ring[mbox].omd_phys);
209048618fb4SAlexandre Bounine 
209148618fb4SAlexandre Bounine 	priv->omsg_ring[mbox].omd_base = NULL;
209248618fb4SAlexandre Bounine 
209348618fb4SAlexandre Bounine 	/* Free message buffers */
209448618fb4SAlexandre Bounine 	for (i = 0; i < priv->omsg_ring[mbox].size; i++) {
209548618fb4SAlexandre Bounine 		if (priv->omsg_ring[mbox].omq_base[i]) {
209648618fb4SAlexandre Bounine 			dma_free_coherent(&priv->pdev->dev,
209748618fb4SAlexandre Bounine 				TSI721_MSG_BUFFER_SIZE,
209848618fb4SAlexandre Bounine 				priv->omsg_ring[mbox].omq_base[i],
209948618fb4SAlexandre Bounine 				priv->omsg_ring[mbox].omq_phys[i]);
210048618fb4SAlexandre Bounine 
210148618fb4SAlexandre Bounine 			priv->omsg_ring[mbox].omq_base[i] = NULL;
210248618fb4SAlexandre Bounine 		}
210348618fb4SAlexandre Bounine 	}
210448618fb4SAlexandre Bounine }
210548618fb4SAlexandre Bounine 
210648618fb4SAlexandre Bounine /**
210748618fb4SAlexandre Bounine  * tsi721_imsg_handler - Inbound Message Interrupt Handler
210848618fb4SAlexandre Bounine  * @priv: pointer to tsi721 private data
210948618fb4SAlexandre Bounine  * @ch: inbound message channel number to service
211048618fb4SAlexandre Bounine  *
211148618fb4SAlexandre Bounine  * Services channel interrupts from inbound messaging engine.
211248618fb4SAlexandre Bounine  */
tsi721_imsg_handler(struct tsi721_device * priv,int ch)211348618fb4SAlexandre Bounine static void tsi721_imsg_handler(struct tsi721_device *priv, int ch)
211448618fb4SAlexandre Bounine {
211548618fb4SAlexandre Bounine 	u32 mbox = ch - 4;
211648618fb4SAlexandre Bounine 	u32 imsg_int;
2117748353ccSAlexandre Bounine 	struct rio_mport *mport = &priv->mport;
211848618fb4SAlexandre Bounine 
211948618fb4SAlexandre Bounine 	spin_lock(&priv->imsg_ring[mbox].lock);
212048618fb4SAlexandre Bounine 
212148618fb4SAlexandre Bounine 	imsg_int = ioread32(priv->regs + TSI721_IBDMAC_INT(ch));
212248618fb4SAlexandre Bounine 
212348618fb4SAlexandre Bounine 	if (imsg_int & TSI721_IBDMAC_INT_SRTO)
212472d8a0d2SAlexandre Bounine 		tsi_info(&priv->pdev->dev, "IB MBOX%d SRIO timeout", mbox);
212548618fb4SAlexandre Bounine 
212648618fb4SAlexandre Bounine 	if (imsg_int & TSI721_IBDMAC_INT_PC_ERROR)
212772d8a0d2SAlexandre Bounine 		tsi_info(&priv->pdev->dev, "IB MBOX%d PCIe error", mbox);
212848618fb4SAlexandre Bounine 
212948618fb4SAlexandre Bounine 	if (imsg_int & TSI721_IBDMAC_INT_FQ_LOW)
213072d8a0d2SAlexandre Bounine 		tsi_info(&priv->pdev->dev, "IB MBOX%d IB free queue low", mbox);
213148618fb4SAlexandre Bounine 
213248618fb4SAlexandre Bounine 	/* Clear IB channel interrupts */
213348618fb4SAlexandre Bounine 	iowrite32(imsg_int, priv->regs + TSI721_IBDMAC_INT(ch));
213448618fb4SAlexandre Bounine 
213548618fb4SAlexandre Bounine 	/* If an IB Msg is received notify the upper layer */
213648618fb4SAlexandre Bounine 	if (imsg_int & TSI721_IBDMAC_INT_DQ_RCV &&
2137748353ccSAlexandre Bounine 		mport->inb_msg[mbox].mcback)
2138748353ccSAlexandre Bounine 		mport->inb_msg[mbox].mcback(mport,
213948618fb4SAlexandre Bounine 				priv->imsg_ring[mbox].dev_id, mbox, -1);
214048618fb4SAlexandre Bounine 
214148618fb4SAlexandre Bounine 	if (!(priv->flags & TSI721_USING_MSIX)) {
214248618fb4SAlexandre Bounine 		u32 ch_inte;
214348618fb4SAlexandre Bounine 
214448618fb4SAlexandre Bounine 		/* Re-enable channel interrupts */
214548618fb4SAlexandre Bounine 		ch_inte = ioread32(priv->regs + TSI721_DEV_CHAN_INTE);
214648618fb4SAlexandre Bounine 		ch_inte |= TSI721_INT_IMSG_CHAN(ch);
214748618fb4SAlexandre Bounine 		iowrite32(ch_inte, priv->regs + TSI721_DEV_CHAN_INTE);
214848618fb4SAlexandre Bounine 	}
214948618fb4SAlexandre Bounine 
215048618fb4SAlexandre Bounine 	spin_unlock(&priv->imsg_ring[mbox].lock);
215148618fb4SAlexandre Bounine }
215248618fb4SAlexandre Bounine 
215348618fb4SAlexandre Bounine /**
215448618fb4SAlexandre Bounine  * tsi721_open_inb_mbox - Initialize Tsi721 inbound mailbox
215548618fb4SAlexandre Bounine  * @mport: Master port implementing the Inbound Messaging Engine
215648618fb4SAlexandre Bounine  * @dev_id: Device specific pointer to pass on event
215748618fb4SAlexandre Bounine  * @mbox: Mailbox to open
215848618fb4SAlexandre Bounine  * @entries: Number of entries in the inbound mailbox ring
215948618fb4SAlexandre Bounine  */
tsi721_open_inb_mbox(struct rio_mport * mport,void * dev_id,int mbox,int entries)216048618fb4SAlexandre Bounine static int tsi721_open_inb_mbox(struct rio_mport *mport, void *dev_id,
216148618fb4SAlexandre Bounine 				int mbox, int entries)
216248618fb4SAlexandre Bounine {
216348618fb4SAlexandre Bounine 	struct tsi721_device *priv = mport->priv;
216448618fb4SAlexandre Bounine 	int ch = mbox + 4;
216548618fb4SAlexandre Bounine 	int i;
216648618fb4SAlexandre Bounine 	u64 *free_ptr;
216748618fb4SAlexandre Bounine 	int rc = 0;
216848618fb4SAlexandre Bounine 
216948618fb4SAlexandre Bounine 	if ((entries < TSI721_IMSGD_MIN_RING_SIZE) ||
217048618fb4SAlexandre Bounine 	    (entries > TSI721_IMSGD_RING_SIZE) ||
217148618fb4SAlexandre Bounine 	    (!is_power_of_2(entries)) || mbox >= RIO_MAX_MBOX) {
217248618fb4SAlexandre Bounine 		rc = -EINVAL;
217348618fb4SAlexandre Bounine 		goto out;
217448618fb4SAlexandre Bounine 	}
217548618fb4SAlexandre Bounine 
2176e519685dSAlexandre Bounine 	if ((mbox_sel & (1 << mbox)) == 0) {
2177e519685dSAlexandre Bounine 		rc = -ENODEV;
2178e519685dSAlexandre Bounine 		goto out;
2179e519685dSAlexandre Bounine 	}
2180e519685dSAlexandre Bounine 
218148618fb4SAlexandre Bounine 	/* Initialize IB Messaging Ring */
218248618fb4SAlexandre Bounine 	priv->imsg_ring[mbox].dev_id = dev_id;
218348618fb4SAlexandre Bounine 	priv->imsg_ring[mbox].size = entries;
218448618fb4SAlexandre Bounine 	priv->imsg_ring[mbox].rx_slot = 0;
218548618fb4SAlexandre Bounine 	priv->imsg_ring[mbox].desc_rdptr = 0;
218648618fb4SAlexandre Bounine 	priv->imsg_ring[mbox].fq_wrptr = 0;
218748618fb4SAlexandre Bounine 	for (i = 0; i < priv->imsg_ring[mbox].size; i++)
218848618fb4SAlexandre Bounine 		priv->imsg_ring[mbox].imq_base[i] = NULL;
218948618fb4SAlexandre Bounine 	spin_lock_init(&priv->imsg_ring[mbox].lock);
219048618fb4SAlexandre Bounine 
219148618fb4SAlexandre Bounine 	/* Allocate buffers for incoming messages */
219248618fb4SAlexandre Bounine 	priv->imsg_ring[mbox].buf_base =
219348618fb4SAlexandre Bounine 		dma_alloc_coherent(&priv->pdev->dev,
219448618fb4SAlexandre Bounine 				   entries * TSI721_MSG_BUFFER_SIZE,
219548618fb4SAlexandre Bounine 				   &priv->imsg_ring[mbox].buf_phys,
219648618fb4SAlexandre Bounine 				   GFP_KERNEL);
219748618fb4SAlexandre Bounine 
219848618fb4SAlexandre Bounine 	if (priv->imsg_ring[mbox].buf_base == NULL) {
219972d8a0d2SAlexandre Bounine 		tsi_err(&priv->pdev->dev,
220072d8a0d2SAlexandre Bounine 			"Failed to allocate buffers for IB MBOX%d", mbox);
220148618fb4SAlexandre Bounine 		rc = -ENOMEM;
220248618fb4SAlexandre Bounine 		goto out;
220348618fb4SAlexandre Bounine 	}
220448618fb4SAlexandre Bounine 
220548618fb4SAlexandre Bounine 	/* Allocate memory for circular free list */
220648618fb4SAlexandre Bounine 	priv->imsg_ring[mbox].imfq_base =
220748618fb4SAlexandre Bounine 		dma_alloc_coherent(&priv->pdev->dev,
220848618fb4SAlexandre Bounine 				   entries * 8,
220948618fb4SAlexandre Bounine 				   &priv->imsg_ring[mbox].imfq_phys,
221048618fb4SAlexandre Bounine 				   GFP_KERNEL);
221148618fb4SAlexandre Bounine 
221248618fb4SAlexandre Bounine 	if (priv->imsg_ring[mbox].imfq_base == NULL) {
221372d8a0d2SAlexandre Bounine 		tsi_err(&priv->pdev->dev,
221472d8a0d2SAlexandre Bounine 			"Failed to allocate free queue for IB MBOX%d", mbox);
221548618fb4SAlexandre Bounine 		rc = -ENOMEM;
221648618fb4SAlexandre Bounine 		goto out_buf;
221748618fb4SAlexandre Bounine 	}
221848618fb4SAlexandre Bounine 
221948618fb4SAlexandre Bounine 	/* Allocate memory for Inbound message descriptors */
222048618fb4SAlexandre Bounine 	priv->imsg_ring[mbox].imd_base =
222148618fb4SAlexandre Bounine 		dma_alloc_coherent(&priv->pdev->dev,
222248618fb4SAlexandre Bounine 				   entries * sizeof(struct tsi721_imsg_desc),
222348618fb4SAlexandre Bounine 				   &priv->imsg_ring[mbox].imd_phys, GFP_KERNEL);
222448618fb4SAlexandre Bounine 
222548618fb4SAlexandre Bounine 	if (priv->imsg_ring[mbox].imd_base == NULL) {
222672d8a0d2SAlexandre Bounine 		tsi_err(&priv->pdev->dev,
222772d8a0d2SAlexandre Bounine 			"Failed to allocate descriptor memory for IB MBOX%d",
222848618fb4SAlexandre Bounine 			mbox);
222948618fb4SAlexandre Bounine 		rc = -ENOMEM;
223048618fb4SAlexandre Bounine 		goto out_dma;
223148618fb4SAlexandre Bounine 	}
223248618fb4SAlexandre Bounine 
223348618fb4SAlexandre Bounine 	/* Fill free buffer pointer list */
223448618fb4SAlexandre Bounine 	free_ptr = priv->imsg_ring[mbox].imfq_base;
223548618fb4SAlexandre Bounine 	for (i = 0; i < entries; i++)
223648618fb4SAlexandre Bounine 		free_ptr[i] = cpu_to_le64(
223748618fb4SAlexandre Bounine 				(u64)(priv->imsg_ring[mbox].buf_phys) +
223848618fb4SAlexandre Bounine 				i * 0x1000);
223948618fb4SAlexandre Bounine 
224048618fb4SAlexandre Bounine 	mb();
224148618fb4SAlexandre Bounine 
224248618fb4SAlexandre Bounine 	/*
224348618fb4SAlexandre Bounine 	 * For mapping of inbound SRIO Messages into appropriate queues we need
224448618fb4SAlexandre Bounine 	 * to set Inbound Device ID register in the messaging engine. We do it
224548618fb4SAlexandre Bounine 	 * once when first inbound mailbox is requested.
224648618fb4SAlexandre Bounine 	 */
224748618fb4SAlexandre Bounine 	if (!(priv->flags & TSI721_IMSGID_SET)) {
2248748353ccSAlexandre Bounine 		iowrite32((u32)priv->mport.host_deviceid,
224948618fb4SAlexandre Bounine 			priv->regs + TSI721_IB_DEVID);
225048618fb4SAlexandre Bounine 		priv->flags |= TSI721_IMSGID_SET;
225148618fb4SAlexandre Bounine 	}
225248618fb4SAlexandre Bounine 
225348618fb4SAlexandre Bounine 	/*
225448618fb4SAlexandre Bounine 	 * Configure Inbound Messaging channel (ch = mbox + 4)
225548618fb4SAlexandre Bounine 	 */
225648618fb4SAlexandre Bounine 
225748618fb4SAlexandre Bounine 	/* Setup Inbound Message free queue */
225848618fb4SAlexandre Bounine 	iowrite32(((u64)priv->imsg_ring[mbox].imfq_phys >> 32),
225948618fb4SAlexandre Bounine 		priv->regs + TSI721_IBDMAC_FQBH(ch));
226048618fb4SAlexandre Bounine 	iowrite32(((u64)priv->imsg_ring[mbox].imfq_phys &
226148618fb4SAlexandre Bounine 			TSI721_IBDMAC_FQBL_MASK),
226248618fb4SAlexandre Bounine 		priv->regs+TSI721_IBDMAC_FQBL(ch));
226348618fb4SAlexandre Bounine 	iowrite32(TSI721_DMAC_DSSZ_SIZE(entries),
226448618fb4SAlexandre Bounine 		priv->regs + TSI721_IBDMAC_FQSZ(ch));
226548618fb4SAlexandre Bounine 
226648618fb4SAlexandre Bounine 	/* Setup Inbound Message descriptor queue */
226748618fb4SAlexandre Bounine 	iowrite32(((u64)priv->imsg_ring[mbox].imd_phys >> 32),
226848618fb4SAlexandre Bounine 		priv->regs + TSI721_IBDMAC_DQBH(ch));
226948618fb4SAlexandre Bounine 	iowrite32(((u32)priv->imsg_ring[mbox].imd_phys &
227048618fb4SAlexandre Bounine 		   (u32)TSI721_IBDMAC_DQBL_MASK),
227148618fb4SAlexandre Bounine 		priv->regs+TSI721_IBDMAC_DQBL(ch));
227248618fb4SAlexandre Bounine 	iowrite32(TSI721_DMAC_DSSZ_SIZE(entries),
227348618fb4SAlexandre Bounine 		priv->regs + TSI721_IBDMAC_DQSZ(ch));
227448618fb4SAlexandre Bounine 
227548618fb4SAlexandre Bounine 	/* Enable interrupts */
227648618fb4SAlexandre Bounine 
227748618fb4SAlexandre Bounine #ifdef CONFIG_PCI_MSI
227848618fb4SAlexandre Bounine 	if (priv->flags & TSI721_USING_MSIX) {
2279748353ccSAlexandre Bounine 		int idx = TSI721_VECT_IMB0_RCV + mbox;
2280748353ccSAlexandre Bounine 
228148618fb4SAlexandre Bounine 		/* Request interrupt service if we are in MSI-X mode */
2282748353ccSAlexandre Bounine 		rc = request_irq(priv->msix[idx].vector, tsi721_imsg_msix, 0,
2283748353ccSAlexandre Bounine 				 priv->msix[idx].irq_name, (void *)priv);
228448618fb4SAlexandre Bounine 
228548618fb4SAlexandre Bounine 		if (rc) {
228672d8a0d2SAlexandre Bounine 			tsi_debug(IMSG, &priv->pdev->dev,
228772d8a0d2SAlexandre Bounine 				"Unable to get MSI-X IRQ for IBOX%d-DONE",
228872d8a0d2SAlexandre Bounine 				mbox);
228948618fb4SAlexandre Bounine 			goto out_desc;
229048618fb4SAlexandre Bounine 		}
229148618fb4SAlexandre Bounine 
2292748353ccSAlexandre Bounine 		idx = TSI721_VECT_IMB0_INT + mbox;
2293748353ccSAlexandre Bounine 		rc = request_irq(priv->msix[idx].vector, tsi721_imsg_msix, 0,
2294748353ccSAlexandre Bounine 				 priv->msix[idx].irq_name, (void *)priv);
229548618fb4SAlexandre Bounine 
229648618fb4SAlexandre Bounine 		if (rc)	{
229772d8a0d2SAlexandre Bounine 			tsi_debug(IMSG, &priv->pdev->dev,
229872d8a0d2SAlexandre Bounine 				"Unable to get MSI-X IRQ for IBOX%d-INT", mbox);
229948618fb4SAlexandre Bounine 			free_irq(
230048618fb4SAlexandre Bounine 				priv->msix[TSI721_VECT_IMB0_RCV + mbox].vector,
2301748353ccSAlexandre Bounine 				(void *)priv);
230248618fb4SAlexandre Bounine 			goto out_desc;
230348618fb4SAlexandre Bounine 		}
230448618fb4SAlexandre Bounine 	}
230548618fb4SAlexandre Bounine #endif /* CONFIG_PCI_MSI */
230648618fb4SAlexandre Bounine 
230748618fb4SAlexandre Bounine 	tsi721_imsg_interrupt_enable(priv, ch, TSI721_IBDMAC_INT_ALL);
230848618fb4SAlexandre Bounine 
230948618fb4SAlexandre Bounine 	/* Initialize Inbound Message Engine */
231048618fb4SAlexandre Bounine 	iowrite32(TSI721_IBDMAC_CTL_INIT, priv->regs + TSI721_IBDMAC_CTL(ch));
231148618fb4SAlexandre Bounine 	ioread32(priv->regs + TSI721_IBDMAC_CTL(ch));
231248618fb4SAlexandre Bounine 	udelay(10);
231348618fb4SAlexandre Bounine 	priv->imsg_ring[mbox].fq_wrptr = entries - 1;
231448618fb4SAlexandre Bounine 	iowrite32(entries - 1, priv->regs + TSI721_IBDMAC_FQWP(ch));
231548618fb4SAlexandre Bounine 
231648618fb4SAlexandre Bounine 	priv->imsg_init[mbox] = 1;
231748618fb4SAlexandre Bounine 	return 0;
231848618fb4SAlexandre Bounine 
231948618fb4SAlexandre Bounine #ifdef CONFIG_PCI_MSI
232048618fb4SAlexandre Bounine out_desc:
232148618fb4SAlexandre Bounine 	dma_free_coherent(&priv->pdev->dev,
232248618fb4SAlexandre Bounine 		priv->imsg_ring[mbox].size * sizeof(struct tsi721_imsg_desc),
232348618fb4SAlexandre Bounine 		priv->imsg_ring[mbox].imd_base,
232448618fb4SAlexandre Bounine 		priv->imsg_ring[mbox].imd_phys);
232548618fb4SAlexandre Bounine 
232648618fb4SAlexandre Bounine 	priv->imsg_ring[mbox].imd_base = NULL;
232748618fb4SAlexandre Bounine #endif /* CONFIG_PCI_MSI */
232848618fb4SAlexandre Bounine 
232948618fb4SAlexandre Bounine out_dma:
233048618fb4SAlexandre Bounine 	dma_free_coherent(&priv->pdev->dev,
233148618fb4SAlexandre Bounine 		priv->imsg_ring[mbox].size * 8,
233248618fb4SAlexandre Bounine 		priv->imsg_ring[mbox].imfq_base,
233348618fb4SAlexandre Bounine 		priv->imsg_ring[mbox].imfq_phys);
233448618fb4SAlexandre Bounine 
233548618fb4SAlexandre Bounine 	priv->imsg_ring[mbox].imfq_base = NULL;
233648618fb4SAlexandre Bounine 
233748618fb4SAlexandre Bounine out_buf:
233848618fb4SAlexandre Bounine 	dma_free_coherent(&priv->pdev->dev,
233948618fb4SAlexandre Bounine 		priv->imsg_ring[mbox].size * TSI721_MSG_BUFFER_SIZE,
234048618fb4SAlexandre Bounine 		priv->imsg_ring[mbox].buf_base,
234148618fb4SAlexandre Bounine 		priv->imsg_ring[mbox].buf_phys);
234248618fb4SAlexandre Bounine 
234348618fb4SAlexandre Bounine 	priv->imsg_ring[mbox].buf_base = NULL;
234448618fb4SAlexandre Bounine 
234548618fb4SAlexandre Bounine out:
234648618fb4SAlexandre Bounine 	return rc;
234748618fb4SAlexandre Bounine }
234848618fb4SAlexandre Bounine 
234948618fb4SAlexandre Bounine /**
235048618fb4SAlexandre Bounine  * tsi721_close_inb_mbox - Shut down Tsi721 inbound mailbox
235148618fb4SAlexandre Bounine  * @mport: Master port implementing the Inbound Messaging Engine
235248618fb4SAlexandre Bounine  * @mbox: Mailbox to close
235348618fb4SAlexandre Bounine  */
tsi721_close_inb_mbox(struct rio_mport * mport,int mbox)235448618fb4SAlexandre Bounine static void tsi721_close_inb_mbox(struct rio_mport *mport, int mbox)
235548618fb4SAlexandre Bounine {
235648618fb4SAlexandre Bounine 	struct tsi721_device *priv = mport->priv;
235748618fb4SAlexandre Bounine 	u32 rx_slot;
235848618fb4SAlexandre Bounine 	int ch = mbox + 4;
235948618fb4SAlexandre Bounine 
236048618fb4SAlexandre Bounine 	if (!priv->imsg_init[mbox]) /* mbox isn't initialized yet */
236148618fb4SAlexandre Bounine 		return;
236248618fb4SAlexandre Bounine 	priv->imsg_init[mbox] = 0;
236348618fb4SAlexandre Bounine 
236448618fb4SAlexandre Bounine 	/* Disable Inbound Messaging Engine */
236548618fb4SAlexandre Bounine 
236648618fb4SAlexandre Bounine 	/* Disable Interrupts */
236748618fb4SAlexandre Bounine 	tsi721_imsg_interrupt_disable(priv, ch, TSI721_OBDMAC_INT_MASK);
236848618fb4SAlexandre Bounine 
236948618fb4SAlexandre Bounine #ifdef CONFIG_PCI_MSI
237048618fb4SAlexandre Bounine 	if (priv->flags & TSI721_USING_MSIX) {
237148618fb4SAlexandre Bounine 		free_irq(priv->msix[TSI721_VECT_IMB0_RCV + mbox].vector,
2372748353ccSAlexandre Bounine 				(void *)priv);
237348618fb4SAlexandre Bounine 		free_irq(priv->msix[TSI721_VECT_IMB0_INT + mbox].vector,
2374748353ccSAlexandre Bounine 				(void *)priv);
237548618fb4SAlexandre Bounine 	}
237648618fb4SAlexandre Bounine #endif /* CONFIG_PCI_MSI */
237748618fb4SAlexandre Bounine 
237848618fb4SAlexandre Bounine 	/* Clear Inbound Buffer Queue */
237948618fb4SAlexandre Bounine 	for (rx_slot = 0; rx_slot < priv->imsg_ring[mbox].size; rx_slot++)
238048618fb4SAlexandre Bounine 		priv->imsg_ring[mbox].imq_base[rx_slot] = NULL;
238148618fb4SAlexandre Bounine 
238248618fb4SAlexandre Bounine 	/* Free memory allocated for message buffers */
238348618fb4SAlexandre Bounine 	dma_free_coherent(&priv->pdev->dev,
238448618fb4SAlexandre Bounine 		priv->imsg_ring[mbox].size * TSI721_MSG_BUFFER_SIZE,
238548618fb4SAlexandre Bounine 		priv->imsg_ring[mbox].buf_base,
238648618fb4SAlexandre Bounine 		priv->imsg_ring[mbox].buf_phys);
238748618fb4SAlexandre Bounine 
238848618fb4SAlexandre Bounine 	priv->imsg_ring[mbox].buf_base = NULL;
238948618fb4SAlexandre Bounine 
239048618fb4SAlexandre Bounine 	/* Free memory allocated for free pointr list */
239148618fb4SAlexandre Bounine 	dma_free_coherent(&priv->pdev->dev,
239248618fb4SAlexandre Bounine 		priv->imsg_ring[mbox].size * 8,
239348618fb4SAlexandre Bounine 		priv->imsg_ring[mbox].imfq_base,
239448618fb4SAlexandre Bounine 		priv->imsg_ring[mbox].imfq_phys);
239548618fb4SAlexandre Bounine 
239648618fb4SAlexandre Bounine 	priv->imsg_ring[mbox].imfq_base = NULL;
239748618fb4SAlexandre Bounine 
239848618fb4SAlexandre Bounine 	/* Free memory allocated for RX descriptors */
239948618fb4SAlexandre Bounine 	dma_free_coherent(&priv->pdev->dev,
240048618fb4SAlexandre Bounine 		priv->imsg_ring[mbox].size * sizeof(struct tsi721_imsg_desc),
240148618fb4SAlexandre Bounine 		priv->imsg_ring[mbox].imd_base,
240248618fb4SAlexandre Bounine 		priv->imsg_ring[mbox].imd_phys);
240348618fb4SAlexandre Bounine 
240448618fb4SAlexandre Bounine 	priv->imsg_ring[mbox].imd_base = NULL;
240548618fb4SAlexandre Bounine }
240648618fb4SAlexandre Bounine 
240748618fb4SAlexandre Bounine /**
240848618fb4SAlexandre Bounine  * tsi721_add_inb_buffer - Add buffer to the Tsi721 inbound message queue
240948618fb4SAlexandre Bounine  * @mport: Master port implementing the Inbound Messaging Engine
241048618fb4SAlexandre Bounine  * @mbox: Inbound mailbox number
241148618fb4SAlexandre Bounine  * @buf: Buffer to add to inbound queue
241248618fb4SAlexandre Bounine  */
tsi721_add_inb_buffer(struct rio_mport * mport,int mbox,void * buf)241348618fb4SAlexandre Bounine static int tsi721_add_inb_buffer(struct rio_mport *mport, int mbox, void *buf)
241448618fb4SAlexandre Bounine {
241548618fb4SAlexandre Bounine 	struct tsi721_device *priv = mport->priv;
241648618fb4SAlexandre Bounine 	u32 rx_slot;
241748618fb4SAlexandre Bounine 	int rc = 0;
241848618fb4SAlexandre Bounine 
241948618fb4SAlexandre Bounine 	rx_slot = priv->imsg_ring[mbox].rx_slot;
242048618fb4SAlexandre Bounine 	if (priv->imsg_ring[mbox].imq_base[rx_slot]) {
242172d8a0d2SAlexandre Bounine 		tsi_err(&priv->pdev->dev,
242272d8a0d2SAlexandre Bounine 			"Error adding inbound buffer %d, buffer exists",
242348618fb4SAlexandre Bounine 			rx_slot);
242448618fb4SAlexandre Bounine 		rc = -EINVAL;
242548618fb4SAlexandre Bounine 		goto out;
242648618fb4SAlexandre Bounine 	}
242748618fb4SAlexandre Bounine 
242848618fb4SAlexandre Bounine 	priv->imsg_ring[mbox].imq_base[rx_slot] = buf;
242948618fb4SAlexandre Bounine 
243048618fb4SAlexandre Bounine 	if (++priv->imsg_ring[mbox].rx_slot == priv->imsg_ring[mbox].size)
243148618fb4SAlexandre Bounine 		priv->imsg_ring[mbox].rx_slot = 0;
243248618fb4SAlexandre Bounine 
243348618fb4SAlexandre Bounine out:
243448618fb4SAlexandre Bounine 	return rc;
243548618fb4SAlexandre Bounine }
243648618fb4SAlexandre Bounine 
243748618fb4SAlexandre Bounine /**
243848618fb4SAlexandre Bounine  * tsi721_get_inb_message - Fetch inbound message from the Tsi721 MSG Queue
243948618fb4SAlexandre Bounine  * @mport: Master port implementing the Inbound Messaging Engine
244048618fb4SAlexandre Bounine  * @mbox: Inbound mailbox number
244148618fb4SAlexandre Bounine  *
244248618fb4SAlexandre Bounine  * Returns pointer to the message on success or NULL on failure.
244348618fb4SAlexandre Bounine  */
tsi721_get_inb_message(struct rio_mport * mport,int mbox)244448618fb4SAlexandre Bounine static void *tsi721_get_inb_message(struct rio_mport *mport, int mbox)
244548618fb4SAlexandre Bounine {
244648618fb4SAlexandre Bounine 	struct tsi721_device *priv = mport->priv;
244748618fb4SAlexandre Bounine 	struct tsi721_imsg_desc *desc;
244848618fb4SAlexandre Bounine 	u32 rx_slot;
244948618fb4SAlexandre Bounine 	void *rx_virt = NULL;
245048618fb4SAlexandre Bounine 	u64 rx_phys;
245148618fb4SAlexandre Bounine 	void *buf = NULL;
245248618fb4SAlexandre Bounine 	u64 *free_ptr;
245348618fb4SAlexandre Bounine 	int ch = mbox + 4;
245448618fb4SAlexandre Bounine 	int msg_size;
245548618fb4SAlexandre Bounine 
245648618fb4SAlexandre Bounine 	if (!priv->imsg_init[mbox])
245748618fb4SAlexandre Bounine 		return NULL;
245848618fb4SAlexandre Bounine 
245948618fb4SAlexandre Bounine 	desc = priv->imsg_ring[mbox].imd_base;
246048618fb4SAlexandre Bounine 	desc += priv->imsg_ring[mbox].desc_rdptr;
246148618fb4SAlexandre Bounine 
246248618fb4SAlexandre Bounine 	if (!(le32_to_cpu(desc->msg_info) & TSI721_IMD_HO))
246348618fb4SAlexandre Bounine 		goto out;
246448618fb4SAlexandre Bounine 
246548618fb4SAlexandre Bounine 	rx_slot = priv->imsg_ring[mbox].rx_slot;
246648618fb4SAlexandre Bounine 	while (priv->imsg_ring[mbox].imq_base[rx_slot] == NULL) {
246748618fb4SAlexandre Bounine 		if (++rx_slot == priv->imsg_ring[mbox].size)
246848618fb4SAlexandre Bounine 			rx_slot = 0;
246948618fb4SAlexandre Bounine 	}
247048618fb4SAlexandre Bounine 
247148618fb4SAlexandre Bounine 	rx_phys = ((u64)le32_to_cpu(desc->bufptr_hi) << 32) |
247248618fb4SAlexandre Bounine 			le32_to_cpu(desc->bufptr_lo);
247348618fb4SAlexandre Bounine 
247448618fb4SAlexandre Bounine 	rx_virt = priv->imsg_ring[mbox].buf_base +
247548618fb4SAlexandre Bounine 		  (rx_phys - (u64)priv->imsg_ring[mbox].buf_phys);
247648618fb4SAlexandre Bounine 
247748618fb4SAlexandre Bounine 	buf = priv->imsg_ring[mbox].imq_base[rx_slot];
247848618fb4SAlexandre Bounine 	msg_size = le32_to_cpu(desc->msg_info) & TSI721_IMD_BCOUNT;
247948618fb4SAlexandre Bounine 	if (msg_size == 0)
248048618fb4SAlexandre Bounine 		msg_size = RIO_MAX_MSG_SIZE;
248148618fb4SAlexandre Bounine 
248248618fb4SAlexandre Bounine 	memcpy(buf, rx_virt, msg_size);
248348618fb4SAlexandre Bounine 	priv->imsg_ring[mbox].imq_base[rx_slot] = NULL;
248448618fb4SAlexandre Bounine 
248548618fb4SAlexandre Bounine 	desc->msg_info &= cpu_to_le32(~TSI721_IMD_HO);
248648618fb4SAlexandre Bounine 	if (++priv->imsg_ring[mbox].desc_rdptr == priv->imsg_ring[mbox].size)
248748618fb4SAlexandre Bounine 		priv->imsg_ring[mbox].desc_rdptr = 0;
248848618fb4SAlexandre Bounine 
248948618fb4SAlexandre Bounine 	iowrite32(priv->imsg_ring[mbox].desc_rdptr,
249048618fb4SAlexandre Bounine 		priv->regs + TSI721_IBDMAC_DQRP(ch));
249148618fb4SAlexandre Bounine 
249248618fb4SAlexandre Bounine 	/* Return free buffer into the pointer list */
249348618fb4SAlexandre Bounine 	free_ptr = priv->imsg_ring[mbox].imfq_base;
249448618fb4SAlexandre Bounine 	free_ptr[priv->imsg_ring[mbox].fq_wrptr] = cpu_to_le64(rx_phys);
249548618fb4SAlexandre Bounine 
249648618fb4SAlexandre Bounine 	if (++priv->imsg_ring[mbox].fq_wrptr == priv->imsg_ring[mbox].size)
249748618fb4SAlexandre Bounine 		priv->imsg_ring[mbox].fq_wrptr = 0;
249848618fb4SAlexandre Bounine 
249948618fb4SAlexandre Bounine 	iowrite32(priv->imsg_ring[mbox].fq_wrptr,
250048618fb4SAlexandre Bounine 		priv->regs + TSI721_IBDMAC_FQWP(ch));
250148618fb4SAlexandre Bounine out:
250248618fb4SAlexandre Bounine 	return buf;
250348618fb4SAlexandre Bounine }
250448618fb4SAlexandre Bounine 
250548618fb4SAlexandre Bounine /**
250648618fb4SAlexandre Bounine  * tsi721_messages_init - Initialization of Messaging Engine
250748618fb4SAlexandre Bounine  * @priv: pointer to tsi721 private data
250848618fb4SAlexandre Bounine  *
250948618fb4SAlexandre Bounine  * Configures Tsi721 messaging engine.
251048618fb4SAlexandre Bounine  */
tsi721_messages_init(struct tsi721_device * priv)251148618fb4SAlexandre Bounine static int tsi721_messages_init(struct tsi721_device *priv)
251248618fb4SAlexandre Bounine {
251348618fb4SAlexandre Bounine 	int	ch;
251448618fb4SAlexandre Bounine 
251548618fb4SAlexandre Bounine 	iowrite32(0, priv->regs + TSI721_SMSG_ECC_LOG);
251648618fb4SAlexandre Bounine 	iowrite32(0, priv->regs + TSI721_RETRY_GEN_CNT);
251748618fb4SAlexandre Bounine 	iowrite32(0, priv->regs + TSI721_RETRY_RX_CNT);
251848618fb4SAlexandre Bounine 
251948618fb4SAlexandre Bounine 	/* Set SRIO Message Request/Response Timeout */
252048618fb4SAlexandre Bounine 	iowrite32(TSI721_RQRPTO_VAL, priv->regs + TSI721_RQRPTO);
252148618fb4SAlexandre Bounine 
252248618fb4SAlexandre Bounine 	/* Initialize Inbound Messaging Engine Registers */
252348618fb4SAlexandre Bounine 	for (ch = 0; ch < TSI721_IMSG_CHNUM; ch++) {
252448618fb4SAlexandre Bounine 		/* Clear interrupt bits */
252548618fb4SAlexandre Bounine 		iowrite32(TSI721_IBDMAC_INT_MASK,
252648618fb4SAlexandre Bounine 			priv->regs + TSI721_IBDMAC_INT(ch));
252748618fb4SAlexandre Bounine 		/* Clear Status */
252848618fb4SAlexandre Bounine 		iowrite32(0, priv->regs + TSI721_IBDMAC_STS(ch));
252948618fb4SAlexandre Bounine 
253048618fb4SAlexandre Bounine 		iowrite32(TSI721_SMSG_ECC_COR_LOG_MASK,
253148618fb4SAlexandre Bounine 				priv->regs + TSI721_SMSG_ECC_COR_LOG(ch));
253248618fb4SAlexandre Bounine 		iowrite32(TSI721_SMSG_ECC_NCOR_MASK,
253348618fb4SAlexandre Bounine 				priv->regs + TSI721_SMSG_ECC_NCOR(ch));
253448618fb4SAlexandre Bounine 	}
253548618fb4SAlexandre Bounine 
253648618fb4SAlexandre Bounine 	return 0;
253748618fb4SAlexandre Bounine }
253848618fb4SAlexandre Bounine 
253948618fb4SAlexandre Bounine /**
2540dbe74afeSAlexandre Bounine  * tsi721_query_mport - Fetch inbound message from the Tsi721 MSG Queue
2541dbe74afeSAlexandre Bounine  * @mport: Master port implementing the Inbound Messaging Engine
2542dbe74afeSAlexandre Bounine  * @mbox: Inbound mailbox number
2543dbe74afeSAlexandre Bounine  *
2544dbe74afeSAlexandre Bounine  * Returns pointer to the message on success or NULL on failure.
2545dbe74afeSAlexandre Bounine  */
tsi721_query_mport(struct rio_mport * mport,struct rio_mport_attr * attr)2546dbe74afeSAlexandre Bounine static int tsi721_query_mport(struct rio_mport *mport,
2547dbe74afeSAlexandre Bounine 			      struct rio_mport_attr *attr)
2548dbe74afeSAlexandre Bounine {
2549dbe74afeSAlexandre Bounine 	struct tsi721_device *priv = mport->priv;
2550dbe74afeSAlexandre Bounine 	u32 rval;
2551dbe74afeSAlexandre Bounine 
25521ae842deSAlexandre Bounine 	rval = ioread32(priv->regs + 0x100 + RIO_PORT_N_ERR_STS_CSR(0, 0));
2553dbe74afeSAlexandre Bounine 	if (rval & RIO_PORT_N_ERR_STS_PORT_OK) {
25541ae842deSAlexandre Bounine 		rval = ioread32(priv->regs + 0x100 + RIO_PORT_N_CTL2_CSR(0, 0));
2555dbe74afeSAlexandre Bounine 		attr->link_speed = (rval & RIO_PORT_N_CTL2_SEL_BAUD) >> 28;
25561ae842deSAlexandre Bounine 		rval = ioread32(priv->regs + 0x100 + RIO_PORT_N_CTL_CSR(0, 0));
2557dbe74afeSAlexandre Bounine 		attr->link_width = (rval & RIO_PORT_N_CTL_IPW) >> 27;
2558dbe74afeSAlexandre Bounine 	} else
2559dbe74afeSAlexandre Bounine 		attr->link_speed = RIO_LINK_DOWN;
2560dbe74afeSAlexandre Bounine 
2561dbe74afeSAlexandre Bounine #ifdef CONFIG_RAPIDIO_DMA_ENGINE
2562dbe74afeSAlexandre Bounine 	attr->flags = RIO_MPORT_DMA | RIO_MPORT_DMA_SG;
2563dbe74afeSAlexandre Bounine 	attr->dma_max_sge = 0;
2564dbe74afeSAlexandre Bounine 	attr->dma_max_size = TSI721_BDMA_MAX_BCOUNT;
2565dbe74afeSAlexandre Bounine 	attr->dma_align = 0;
2566dbe74afeSAlexandre Bounine #else
2567dbe74afeSAlexandre Bounine 	attr->flags = 0;
2568dbe74afeSAlexandre Bounine #endif
2569dbe74afeSAlexandre Bounine 	return 0;
2570dbe74afeSAlexandre Bounine }
2571dbe74afeSAlexandre Bounine 
2572dbe74afeSAlexandre Bounine /**
257348618fb4SAlexandre Bounine  * tsi721_disable_ints - disables all device interrupts
257448618fb4SAlexandre Bounine  * @priv: pointer to tsi721 private data
257548618fb4SAlexandre Bounine  */
tsi721_disable_ints(struct tsi721_device * priv)257648618fb4SAlexandre Bounine static void tsi721_disable_ints(struct tsi721_device *priv)
257748618fb4SAlexandre Bounine {
257848618fb4SAlexandre Bounine 	int ch;
257948618fb4SAlexandre Bounine 
258048618fb4SAlexandre Bounine 	/* Disable all device level interrupts */
258148618fb4SAlexandre Bounine 	iowrite32(0, priv->regs + TSI721_DEV_INTE);
258248618fb4SAlexandre Bounine 
258348618fb4SAlexandre Bounine 	/* Disable all Device Channel interrupts */
258448618fb4SAlexandre Bounine 	iowrite32(0, priv->regs + TSI721_DEV_CHAN_INTE);
258548618fb4SAlexandre Bounine 
258648618fb4SAlexandre Bounine 	/* Disable all Inbound Msg Channel interrupts */
258748618fb4SAlexandre Bounine 	for (ch = 0; ch < TSI721_IMSG_CHNUM; ch++)
258848618fb4SAlexandre Bounine 		iowrite32(0, priv->regs + TSI721_IBDMAC_INTE(ch));
258948618fb4SAlexandre Bounine 
259048618fb4SAlexandre Bounine 	/* Disable all Outbound Msg Channel interrupts */
259148618fb4SAlexandre Bounine 	for (ch = 0; ch < TSI721_OMSG_CHNUM; ch++)
259248618fb4SAlexandre Bounine 		iowrite32(0, priv->regs + TSI721_OBDMAC_INTE(ch));
259348618fb4SAlexandre Bounine 
259448618fb4SAlexandre Bounine 	/* Disable all general messaging interrupts */
259548618fb4SAlexandre Bounine 	iowrite32(0, priv->regs + TSI721_SMSG_INTE);
259648618fb4SAlexandre Bounine 
259748618fb4SAlexandre Bounine 	/* Disable all BDMA Channel interrupts */
259848618fb4SAlexandre Bounine 	for (ch = 0; ch < TSI721_DMA_MAXCH; ch++)
25999eaa3d9bSAlexandre Bounine 		iowrite32(0,
26009eaa3d9bSAlexandre Bounine 			priv->regs + TSI721_DMAC_BASE(ch) + TSI721_DMAC_INTE);
260148618fb4SAlexandre Bounine 
260248618fb4SAlexandre Bounine 	/* Disable all general BDMA interrupts */
260348618fb4SAlexandre Bounine 	iowrite32(0, priv->regs + TSI721_BDMA_INTE);
260448618fb4SAlexandre Bounine 
260548618fb4SAlexandre Bounine 	/* Disable all SRIO Channel interrupts */
260648618fb4SAlexandre Bounine 	for (ch = 0; ch < TSI721_SRIO_MAXCH; ch++)
260748618fb4SAlexandre Bounine 		iowrite32(0, priv->regs + TSI721_SR_CHINTE(ch));
260848618fb4SAlexandre Bounine 
260948618fb4SAlexandre Bounine 	/* Disable all general SR2PC interrupts */
261048618fb4SAlexandre Bounine 	iowrite32(0, priv->regs + TSI721_SR2PC_GEN_INTE);
261148618fb4SAlexandre Bounine 
261248618fb4SAlexandre Bounine 	/* Disable all PC2SR interrupts */
261348618fb4SAlexandre Bounine 	iowrite32(0, priv->regs + TSI721_PC2SR_INTE);
261448618fb4SAlexandre Bounine 
261548618fb4SAlexandre Bounine 	/* Disable all I2C interrupts */
261648618fb4SAlexandre Bounine 	iowrite32(0, priv->regs + TSI721_I2C_INT_ENABLE);
261748618fb4SAlexandre Bounine 
261848618fb4SAlexandre Bounine 	/* Disable SRIO MAC interrupts */
261948618fb4SAlexandre Bounine 	iowrite32(0, priv->regs + TSI721_RIO_EM_INT_ENABLE);
262048618fb4SAlexandre Bounine 	iowrite32(0, priv->regs + TSI721_RIO_EM_DEV_INT_EN);
262148618fb4SAlexandre Bounine }
262248618fb4SAlexandre Bounine 
2623748353ccSAlexandre Bounine static struct rio_ops tsi721_rio_ops = {
2624748353ccSAlexandre Bounine 	.lcread			= tsi721_lcread,
2625748353ccSAlexandre Bounine 	.lcwrite		= tsi721_lcwrite,
2626748353ccSAlexandre Bounine 	.cread			= tsi721_cread_dma,
2627748353ccSAlexandre Bounine 	.cwrite			= tsi721_cwrite_dma,
2628748353ccSAlexandre Bounine 	.dsend			= tsi721_dsend,
2629748353ccSAlexandre Bounine 	.open_inb_mbox		= tsi721_open_inb_mbox,
2630748353ccSAlexandre Bounine 	.close_inb_mbox		= tsi721_close_inb_mbox,
2631748353ccSAlexandre Bounine 	.open_outb_mbox		= tsi721_open_outb_mbox,
2632748353ccSAlexandre Bounine 	.close_outb_mbox	= tsi721_close_outb_mbox,
2633748353ccSAlexandre Bounine 	.add_outb_message	= tsi721_add_outb_message,
2634748353ccSAlexandre Bounine 	.add_inb_buffer		= tsi721_add_inb_buffer,
2635748353ccSAlexandre Bounine 	.get_inb_message	= tsi721_get_inb_message,
2636748353ccSAlexandre Bounine 	.map_inb		= tsi721_rio_map_inb_mem,
2637748353ccSAlexandre Bounine 	.unmap_inb		= tsi721_rio_unmap_inb_mem,
2638748353ccSAlexandre Bounine 	.pwenable		= tsi721_pw_enable,
2639748353ccSAlexandre Bounine 	.query_mport		= tsi721_query_mport,
26401679e8daSAlexandre Bounine 	.map_outb		= tsi721_map_outb_win,
26411679e8daSAlexandre Bounine 	.unmap_outb		= tsi721_unmap_outb_win,
2642748353ccSAlexandre Bounine };
2643748353ccSAlexandre Bounine 
tsi721_mport_release(struct device * dev)2644748353ccSAlexandre Bounine static void tsi721_mport_release(struct device *dev)
2645748353ccSAlexandre Bounine {
2646748353ccSAlexandre Bounine 	struct rio_mport *mport = to_rio_mport(dev);
2647748353ccSAlexandre Bounine 
264872d8a0d2SAlexandre Bounine 	tsi_debug(EXIT, dev, "%s id=%d", mport->name, mport->id);
2649748353ccSAlexandre Bounine }
2650748353ccSAlexandre Bounine 
265148618fb4SAlexandre Bounine /**
265248618fb4SAlexandre Bounine  * tsi721_setup_mport - Setup Tsi721 as RapidIO subsystem master port
265348618fb4SAlexandre Bounine  * @priv: pointer to tsi721 private data
265448618fb4SAlexandre Bounine  *
265548618fb4SAlexandre Bounine  * Configures Tsi721 as RapidIO master port.
265648618fb4SAlexandre Bounine  */
tsi721_setup_mport(struct tsi721_device * priv)2657305c891eSBill Pemberton static int tsi721_setup_mport(struct tsi721_device *priv)
265848618fb4SAlexandre Bounine {
265948618fb4SAlexandre Bounine 	struct pci_dev *pdev = priv->pdev;
266048618fb4SAlexandre Bounine 	int err = 0;
2661748353ccSAlexandre Bounine 	struct rio_mport *mport = &priv->mport;
266248618fb4SAlexandre Bounine 
2663748353ccSAlexandre Bounine 	err = rio_mport_initialize(mport);
2664748353ccSAlexandre Bounine 	if (err)
2665748353ccSAlexandre Bounine 		return err;
266648618fb4SAlexandre Bounine 
2667748353ccSAlexandre Bounine 	mport->ops = &tsi721_rio_ops;
266848618fb4SAlexandre Bounine 	mport->index = 0;
266948618fb4SAlexandre Bounine 	mport->sys_size = 0; /* small system */
267048618fb4SAlexandre Bounine 	mport->priv = (void *)priv;
267148618fb4SAlexandre Bounine 	mport->phys_efptr = 0x100;
26721ae842deSAlexandre Bounine 	mport->phys_rmap = 1;
26732aaf308bSAlexandre Bounine 	mport->dev.parent = &pdev->dev;
2674748353ccSAlexandre Bounine 	mport->dev.release = tsi721_mport_release;
267548618fb4SAlexandre Bounine 
267648618fb4SAlexandre Bounine 	INIT_LIST_HEAD(&mport->dbells);
267748618fb4SAlexandre Bounine 
267848618fb4SAlexandre Bounine 	rio_init_dbell_res(&mport->riores[RIO_DOORBELL_RESOURCE], 0, 0xffff);
2679b439e66fSAlexandre Bounine 	rio_init_mbox_res(&mport->riores[RIO_INB_MBOX_RESOURCE], 0, 3);
2680b439e66fSAlexandre Bounine 	rio_init_mbox_res(&mport->riores[RIO_OUTB_MBOX_RESOURCE], 0, 3);
2681ed43f44fSAlexandre Bounine 	snprintf(mport->name, RIO_MAX_MPORT_NAME, "%s(%s)",
2682ed43f44fSAlexandre Bounine 		 dev_driver_string(&pdev->dev), dev_name(&pdev->dev));
268348618fb4SAlexandre Bounine 
268448618fb4SAlexandre Bounine 	/* Hook up interrupt handler */
268548618fb4SAlexandre Bounine 
268648618fb4SAlexandre Bounine #ifdef CONFIG_PCI_MSI
268748618fb4SAlexandre Bounine 	if (!tsi721_enable_msix(priv))
268848618fb4SAlexandre Bounine 		priv->flags |= TSI721_USING_MSIX;
268948618fb4SAlexandre Bounine 	else if (!pci_enable_msi(pdev))
269048618fb4SAlexandre Bounine 		priv->flags |= TSI721_USING_MSI;
269148618fb4SAlexandre Bounine 	else
269272d8a0d2SAlexandre Bounine 		tsi_debug(MPORT, &pdev->dev,
269372d8a0d2SAlexandre Bounine 			 "MSI/MSI-X is not available. Using legacy INTx.");
269448618fb4SAlexandre Bounine #endif /* CONFIG_PCI_MSI */
269548618fb4SAlexandre Bounine 
2696748353ccSAlexandre Bounine 	err = tsi721_request_irq(priv);
269748618fb4SAlexandre Bounine 
2698748353ccSAlexandre Bounine 	if (err) {
269972d8a0d2SAlexandre Bounine 		tsi_err(&pdev->dev, "Unable to get PCI IRQ %02X (err=0x%x)",
270072d8a0d2SAlexandre Bounine 			pdev->irq, err);
2701748353ccSAlexandre Bounine 		return err;
27029eaa3d9bSAlexandre Bounine 	}
270348618fb4SAlexandre Bounine 
27049eaa3d9bSAlexandre Bounine #ifdef CONFIG_RAPIDIO_DMA_ENGINE
2705748353ccSAlexandre Bounine 	err = tsi721_register_dma(priv);
2706748353ccSAlexandre Bounine 	if (err)
2707748353ccSAlexandre Bounine 		goto err_exit;
27089eaa3d9bSAlexandre Bounine #endif
270948618fb4SAlexandre Bounine 	/* Enable SRIO link */
271048618fb4SAlexandre Bounine 	iowrite32(ioread32(priv->regs + TSI721_DEVCTL) |
271148618fb4SAlexandre Bounine 		  TSI721_DEVCTL_SRBOOT_CMPL,
271248618fb4SAlexandre Bounine 		  priv->regs + TSI721_DEVCTL);
271348618fb4SAlexandre Bounine 
271448618fb4SAlexandre Bounine 	if (mport->host_deviceid >= 0)
271548618fb4SAlexandre Bounine 		iowrite32(RIO_PORT_GEN_HOST | RIO_PORT_GEN_MASTER |
271648618fb4SAlexandre Bounine 			  RIO_PORT_GEN_DISCOVERED,
271748618fb4SAlexandre Bounine 			  priv->regs + (0x100 + RIO_PORT_GEN_CTL_CSR));
271848618fb4SAlexandre Bounine 	else
271948618fb4SAlexandre Bounine 		iowrite32(0, priv->regs + (0x100 + RIO_PORT_GEN_CTL_CSR));
272048618fb4SAlexandre Bounine 
2721748353ccSAlexandre Bounine 	err = rio_register_mport(mport);
2722748353ccSAlexandre Bounine 	if (err) {
2723748353ccSAlexandre Bounine 		tsi721_unregister_dma(priv);
2724748353ccSAlexandre Bounine 		goto err_exit;
2725748353ccSAlexandre Bounine 	}
2726748353ccSAlexandre Bounine 
272748618fb4SAlexandre Bounine 	return 0;
27289eaa3d9bSAlexandre Bounine 
27299eaa3d9bSAlexandre Bounine err_exit:
2730748353ccSAlexandre Bounine 	tsi721_free_irq(priv);
27319eaa3d9bSAlexandre Bounine 	return err;
273248618fb4SAlexandre Bounine }
273348618fb4SAlexandre Bounine 
tsi721_probe(struct pci_dev * pdev,const struct pci_device_id * id)2734305c891eSBill Pemberton static int tsi721_probe(struct pci_dev *pdev,
273548618fb4SAlexandre Bounine 				  const struct pci_device_id *id)
273648618fb4SAlexandre Bounine {
273748618fb4SAlexandre Bounine 	struct tsi721_device *priv;
273848618fb4SAlexandre Bounine 	int err;
273948618fb4SAlexandre Bounine 
274048618fb4SAlexandre Bounine 	priv = kzalloc(sizeof(struct tsi721_device), GFP_KERNEL);
274172d8a0d2SAlexandre Bounine 	if (!priv) {
274248618fb4SAlexandre Bounine 		err = -ENOMEM;
274348618fb4SAlexandre Bounine 		goto err_exit;
274448618fb4SAlexandre Bounine 	}
274548618fb4SAlexandre Bounine 
274648618fb4SAlexandre Bounine 	err = pci_enable_device(pdev);
274748618fb4SAlexandre Bounine 	if (err) {
274872d8a0d2SAlexandre Bounine 		tsi_err(&pdev->dev, "Failed to enable PCI device");
274948618fb4SAlexandre Bounine 		goto err_clean;
275048618fb4SAlexandre Bounine 	}
275148618fb4SAlexandre Bounine 
275248618fb4SAlexandre Bounine 	priv->pdev = pdev;
275348618fb4SAlexandre Bounine 
275448618fb4SAlexandre Bounine #ifdef DEBUG
27559a9a9a7aSAlexandre Bounine 	{
27569a9a9a7aSAlexandre Bounine 		int i;
275772d8a0d2SAlexandre Bounine 
2758c9c13ba4SDenis Efremov 		for (i = 0; i < PCI_STD_NUM_BARS; i++) {
275972d8a0d2SAlexandre Bounine 			tsi_debug(INIT, &pdev->dev, "res%d %pR",
276072d8a0d2SAlexandre Bounine 				  i, &pdev->resource[i]);
276148618fb4SAlexandre Bounine 		}
27629a9a9a7aSAlexandre Bounine 	}
276348618fb4SAlexandre Bounine #endif
276448618fb4SAlexandre Bounine 	/*
276548618fb4SAlexandre Bounine 	 * Verify BAR configuration
276648618fb4SAlexandre Bounine 	 */
276748618fb4SAlexandre Bounine 
276848618fb4SAlexandre Bounine 	/* BAR_0 (registers) must be 512KB+ in 32-bit address space */
276948618fb4SAlexandre Bounine 	if (!(pci_resource_flags(pdev, BAR_0) & IORESOURCE_MEM) ||
277048618fb4SAlexandre Bounine 	    pci_resource_flags(pdev, BAR_0) & IORESOURCE_MEM_64 ||
277148618fb4SAlexandre Bounine 	    pci_resource_len(pdev, BAR_0) < TSI721_REG_SPACE_SIZE) {
277272d8a0d2SAlexandre Bounine 		tsi_err(&pdev->dev, "Missing or misconfigured CSR BAR0");
277348618fb4SAlexandre Bounine 		err = -ENODEV;
277448618fb4SAlexandre Bounine 		goto err_disable_pdev;
277548618fb4SAlexandre Bounine 	}
277648618fb4SAlexandre Bounine 
277748618fb4SAlexandre Bounine 	/* BAR_1 (outbound doorbells) must be 16MB+ in 32-bit address space */
277848618fb4SAlexandre Bounine 	if (!(pci_resource_flags(pdev, BAR_1) & IORESOURCE_MEM) ||
277948618fb4SAlexandre Bounine 	    pci_resource_flags(pdev, BAR_1) & IORESOURCE_MEM_64 ||
278048618fb4SAlexandre Bounine 	    pci_resource_len(pdev, BAR_1) < TSI721_DB_WIN_SIZE) {
278172d8a0d2SAlexandre Bounine 		tsi_err(&pdev->dev, "Missing or misconfigured Doorbell BAR1");
278248618fb4SAlexandre Bounine 		err = -ENODEV;
278348618fb4SAlexandre Bounine 		goto err_disable_pdev;
278448618fb4SAlexandre Bounine 	}
278548618fb4SAlexandre Bounine 
278648618fb4SAlexandre Bounine 	/*
278748618fb4SAlexandre Bounine 	 * BAR_2 and BAR_4 (outbound translation) must be in 64-bit PCIe address
278848618fb4SAlexandre Bounine 	 * space.
278948618fb4SAlexandre Bounine 	 * NOTE: BAR_2 and BAR_4 are not used by this version of driver.
279048618fb4SAlexandre Bounine 	 * It may be a good idea to keep them disabled using HW configuration
279148618fb4SAlexandre Bounine 	 * to save PCI memory space.
279248618fb4SAlexandre Bounine 	 */
27931679e8daSAlexandre Bounine 
27941679e8daSAlexandre Bounine 	priv->p2r_bar[0].size = priv->p2r_bar[1].size = 0;
27951679e8daSAlexandre Bounine 
27961679e8daSAlexandre Bounine 	if (pci_resource_flags(pdev, BAR_2) & IORESOURCE_MEM_64) {
27971679e8daSAlexandre Bounine 		if (pci_resource_flags(pdev, BAR_2) & IORESOURCE_PREFETCH)
279872d8a0d2SAlexandre Bounine 			tsi_debug(INIT, &pdev->dev,
279972d8a0d2SAlexandre Bounine 				 "Prefetchable OBW BAR2 will not be used");
28001679e8daSAlexandre Bounine 		else {
28011679e8daSAlexandre Bounine 			priv->p2r_bar[0].base = pci_resource_start(pdev, BAR_2);
28021679e8daSAlexandre Bounine 			priv->p2r_bar[0].size = pci_resource_len(pdev, BAR_2);
28031679e8daSAlexandre Bounine 		}
280448618fb4SAlexandre Bounine 	}
280548618fb4SAlexandre Bounine 
28061679e8daSAlexandre Bounine 	if (pci_resource_flags(pdev, BAR_4) & IORESOURCE_MEM_64) {
28071679e8daSAlexandre Bounine 		if (pci_resource_flags(pdev, BAR_4) & IORESOURCE_PREFETCH)
280872d8a0d2SAlexandre Bounine 			tsi_debug(INIT, &pdev->dev,
280972d8a0d2SAlexandre Bounine 				 "Prefetchable OBW BAR4 will not be used");
28101679e8daSAlexandre Bounine 		else {
28111679e8daSAlexandre Bounine 			priv->p2r_bar[1].base = pci_resource_start(pdev, BAR_4);
28121679e8daSAlexandre Bounine 			priv->p2r_bar[1].size = pci_resource_len(pdev, BAR_4);
28131679e8daSAlexandre Bounine 		}
281448618fb4SAlexandre Bounine 	}
281548618fb4SAlexandre Bounine 
281648618fb4SAlexandre Bounine 	err = pci_request_regions(pdev, DRV_NAME);
281748618fb4SAlexandre Bounine 	if (err) {
281872d8a0d2SAlexandre Bounine 		tsi_err(&pdev->dev, "Unable to obtain PCI resources");
281948618fb4SAlexandre Bounine 		goto err_disable_pdev;
282048618fb4SAlexandre Bounine 	}
282148618fb4SAlexandre Bounine 
282248618fb4SAlexandre Bounine 	pci_set_master(pdev);
282348618fb4SAlexandre Bounine 
282448618fb4SAlexandre Bounine 	priv->regs = pci_ioremap_bar(pdev, BAR_0);
282548618fb4SAlexandre Bounine 	if (!priv->regs) {
282672d8a0d2SAlexandre Bounine 		tsi_err(&pdev->dev, "Unable to map device registers space");
282748618fb4SAlexandre Bounine 		err = -ENOMEM;
282848618fb4SAlexandre Bounine 		goto err_free_res;
282948618fb4SAlexandre Bounine 	}
283048618fb4SAlexandre Bounine 
283148618fb4SAlexandre Bounine 	priv->odb_base = pci_ioremap_bar(pdev, BAR_1);
283248618fb4SAlexandre Bounine 	if (!priv->odb_base) {
283372d8a0d2SAlexandre Bounine 		tsi_err(&pdev->dev, "Unable to map outbound doorbells space");
283448618fb4SAlexandre Bounine 		err = -ENOMEM;
283548618fb4SAlexandre Bounine 		goto err_unmap_bars;
283648618fb4SAlexandre Bounine 	}
283748618fb4SAlexandre Bounine 
283848618fb4SAlexandre Bounine 	/* Configure DMA attributes. */
28398c155674SChristophe JAILLET 	if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(64))) {
28408c155674SChristophe JAILLET 		err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
284118f6287fSPeter Senna Tschudin 		if (err) {
284272d8a0d2SAlexandre Bounine 			tsi_err(&pdev->dev, "Unable to set DMA mask");
284348618fb4SAlexandre Bounine 			goto err_unmap_bars;
284448618fb4SAlexandre Bounine 		}
284548618fb4SAlexandre Bounine 
28468c155674SChristophe JAILLET 		if (dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)))
284772d8a0d2SAlexandre Bounine 			tsi_info(&pdev->dev, "Unable to set consistent DMA mask");
284848618fb4SAlexandre Bounine 	} else {
28498c155674SChristophe JAILLET 		err = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64));
285048618fb4SAlexandre Bounine 		if (err)
285172d8a0d2SAlexandre Bounine 			tsi_info(&pdev->dev, "Unable to set consistent DMA mask");
285248618fb4SAlexandre Bounine 	}
285348618fb4SAlexandre Bounine 
28545cdaaf8aSJiang Liu 	BUG_ON(!pci_is_pcie(pdev));
28551cee22b7SAlexandre Bounine 
2856174f1a71SAlexandre Bounine 	/* Clear "no snoop" and "relaxed ordering" bits. */
28575cdaaf8aSJiang Liu 	pcie_capability_clear_and_set_word(pdev, PCI_EXP_DEVCTL,
2858174f1a71SAlexandre Bounine 		PCI_EXP_DEVCTL_RELAX_EN | PCI_EXP_DEVCTL_NOSNOOP_EN, 0);
28591cee22b7SAlexandre Bounine 
2860cb782cddSAlexandre Bounine 	/* Override PCIe Maximum Read Request Size setting if requested */
2861cb782cddSAlexandre Bounine 	if (pcie_mrrs >= 0) {
2862cb782cddSAlexandre Bounine 		if (pcie_mrrs <= 5)
2863cb782cddSAlexandre Bounine 			pcie_capability_clear_and_set_word(pdev, PCI_EXP_DEVCTL,
2864cb782cddSAlexandre Bounine 					PCI_EXP_DEVCTL_READRQ, pcie_mrrs << 12);
2865cb782cddSAlexandre Bounine 		else
2866cb782cddSAlexandre Bounine 			tsi_info(&pdev->dev,
2867cb782cddSAlexandre Bounine 				 "Invalid MRRS override value %d", pcie_mrrs);
2868cb782cddSAlexandre Bounine 	}
2869cb782cddSAlexandre Bounine 
28707babfa5bSBjorn Helgaas 	/* Set PCIe completion timeout to 1-10ms */
28717babfa5bSBjorn Helgaas 	pcie_capability_clear_and_set_word(pdev, PCI_EXP_DEVCTL2,
28727babfa5bSBjorn Helgaas 					   PCI_EXP_DEVCTL2_COMP_TIMEOUT, 0x2);
287348618fb4SAlexandre Bounine 
287448618fb4SAlexandre Bounine 	/*
287548618fb4SAlexandre Bounine 	 * FIXUP: correct offsets of MSI-X tables in the MSI-X Capability Block
287648618fb4SAlexandre Bounine 	 */
287748618fb4SAlexandre Bounine 	pci_write_config_dword(pdev, TSI721_PCIECFG_EPCTL, 0x01);
287848618fb4SAlexandre Bounine 	pci_write_config_dword(pdev, TSI721_PCIECFG_MSIXTBL,
287948618fb4SAlexandre Bounine 						TSI721_MSIXTBL_OFFSET);
288048618fb4SAlexandre Bounine 	pci_write_config_dword(pdev, TSI721_PCIECFG_MSIXPBA,
288148618fb4SAlexandre Bounine 						TSI721_MSIXPBA_OFFSET);
288248618fb4SAlexandre Bounine 	pci_write_config_dword(pdev, TSI721_PCIECFG_EPCTL, 0);
288348618fb4SAlexandre Bounine 	/* End of FIXUP */
288448618fb4SAlexandre Bounine 
288548618fb4SAlexandre Bounine 	tsi721_disable_ints(priv);
288648618fb4SAlexandre Bounine 
288748618fb4SAlexandre Bounine 	tsi721_init_pc2sr_mapping(priv);
288848618fb4SAlexandre Bounine 	tsi721_init_sr2pc_mapping(priv);
288948618fb4SAlexandre Bounine 
28909eaa3d9bSAlexandre Bounine 	if (tsi721_bdma_maint_init(priv)) {
289172d8a0d2SAlexandre Bounine 		tsi_err(&pdev->dev, "BDMA initialization failed");
289248618fb4SAlexandre Bounine 		err = -ENOMEM;
289348618fb4SAlexandre Bounine 		goto err_unmap_bars;
289448618fb4SAlexandre Bounine 	}
289548618fb4SAlexandre Bounine 
289648618fb4SAlexandre Bounine 	err = tsi721_doorbell_init(priv);
289748618fb4SAlexandre Bounine 	if (err)
289848618fb4SAlexandre Bounine 		goto err_free_bdma;
289948618fb4SAlexandre Bounine 
290048618fb4SAlexandre Bounine 	tsi721_port_write_init(priv);
290148618fb4SAlexandre Bounine 
290248618fb4SAlexandre Bounine 	err = tsi721_messages_init(priv);
290348618fb4SAlexandre Bounine 	if (err)
290448618fb4SAlexandre Bounine 		goto err_free_consistent;
290548618fb4SAlexandre Bounine 
290648618fb4SAlexandre Bounine 	err = tsi721_setup_mport(priv);
290748618fb4SAlexandre Bounine 	if (err)
290848618fb4SAlexandre Bounine 		goto err_free_consistent;
290948618fb4SAlexandre Bounine 
2910e3dd8cd4SAlexandre Bounine 	pci_set_drvdata(pdev, priv);
2911748353ccSAlexandre Bounine 	tsi721_interrupts_init(priv);
2912e3dd8cd4SAlexandre Bounine 
291348618fb4SAlexandre Bounine 	return 0;
291448618fb4SAlexandre Bounine 
291548618fb4SAlexandre Bounine err_free_consistent:
2916748353ccSAlexandre Bounine 	tsi721_port_write_free(priv);
291748618fb4SAlexandre Bounine 	tsi721_doorbell_free(priv);
291848618fb4SAlexandre Bounine err_free_bdma:
29199eaa3d9bSAlexandre Bounine 	tsi721_bdma_maint_free(priv);
292048618fb4SAlexandre Bounine err_unmap_bars:
292148618fb4SAlexandre Bounine 	if (priv->regs)
292248618fb4SAlexandre Bounine 		iounmap(priv->regs);
292348618fb4SAlexandre Bounine 	if (priv->odb_base)
292448618fb4SAlexandre Bounine 		iounmap(priv->odb_base);
292548618fb4SAlexandre Bounine err_free_res:
292648618fb4SAlexandre Bounine 	pci_release_regions(pdev);
292748618fb4SAlexandre Bounine err_disable_pdev:
292848618fb4SAlexandre Bounine 	pci_disable_device(pdev);
292948618fb4SAlexandre Bounine err_clean:
293048618fb4SAlexandre Bounine 	kfree(priv);
293148618fb4SAlexandre Bounine err_exit:
293248618fb4SAlexandre Bounine 	return err;
293348618fb4SAlexandre Bounine }
293448618fb4SAlexandre Bounine 
tsi721_remove(struct pci_dev * pdev)2935748353ccSAlexandre Bounine static void tsi721_remove(struct pci_dev *pdev)
2936748353ccSAlexandre Bounine {
2937748353ccSAlexandre Bounine 	struct tsi721_device *priv = pci_get_drvdata(pdev);
2938748353ccSAlexandre Bounine 
293972d8a0d2SAlexandre Bounine 	tsi_debug(EXIT, &pdev->dev, "enter");
2940748353ccSAlexandre Bounine 
2941748353ccSAlexandre Bounine 	tsi721_disable_ints(priv);
2942748353ccSAlexandre Bounine 	tsi721_free_irq(priv);
2943*7960546cSTetsuo Handa 	flush_work(&priv->idb_work);
2944*7960546cSTetsuo Handa 	flush_work(&priv->pw_work);
2945748353ccSAlexandre Bounine 	rio_unregister_mport(&priv->mport);
2946748353ccSAlexandre Bounine 
2947748353ccSAlexandre Bounine 	tsi721_unregister_dma(priv);
2948748353ccSAlexandre Bounine 	tsi721_bdma_maint_free(priv);
2949748353ccSAlexandre Bounine 	tsi721_doorbell_free(priv);
2950748353ccSAlexandre Bounine 	tsi721_port_write_free(priv);
2951748353ccSAlexandre Bounine 	tsi721_close_sr2pc_mapping(priv);
2952748353ccSAlexandre Bounine 
2953748353ccSAlexandre Bounine 	if (priv->regs)
2954748353ccSAlexandre Bounine 		iounmap(priv->regs);
2955748353ccSAlexandre Bounine 	if (priv->odb_base)
2956748353ccSAlexandre Bounine 		iounmap(priv->odb_base);
2957748353ccSAlexandre Bounine #ifdef CONFIG_PCI_MSI
2958748353ccSAlexandre Bounine 	if (priv->flags & TSI721_USING_MSIX)
2959748353ccSAlexandre Bounine 		pci_disable_msix(priv->pdev);
2960748353ccSAlexandre Bounine 	else if (priv->flags & TSI721_USING_MSI)
2961748353ccSAlexandre Bounine 		pci_disable_msi(priv->pdev);
2962748353ccSAlexandre Bounine #endif
2963748353ccSAlexandre Bounine 	pci_release_regions(pdev);
2964748353ccSAlexandre Bounine 	pci_disable_device(pdev);
2965748353ccSAlexandre Bounine 	pci_set_drvdata(pdev, NULL);
2966748353ccSAlexandre Bounine 	kfree(priv);
296772d8a0d2SAlexandre Bounine 	tsi_debug(EXIT, &pdev->dev, "exit");
2968748353ccSAlexandre Bounine }
2969748353ccSAlexandre Bounine 
tsi721_shutdown(struct pci_dev * pdev)2970e3dd8cd4SAlexandre Bounine static void tsi721_shutdown(struct pci_dev *pdev)
2971e3dd8cd4SAlexandre Bounine {
2972e3dd8cd4SAlexandre Bounine 	struct tsi721_device *priv = pci_get_drvdata(pdev);
2973e3dd8cd4SAlexandre Bounine 
297472d8a0d2SAlexandre Bounine 	tsi_debug(EXIT, &pdev->dev, "enter");
2975e3dd8cd4SAlexandre Bounine 
2976e3dd8cd4SAlexandre Bounine 	tsi721_disable_ints(priv);
2977e3dd8cd4SAlexandre Bounine 	tsi721_dma_stop_all(priv);
2978e3dd8cd4SAlexandre Bounine 	pci_disable_device(pdev);
2979e3dd8cd4SAlexandre Bounine }
2980e3dd8cd4SAlexandre Bounine 
29819baa3c34SBenoit Taine static const struct pci_device_id tsi721_pci_tbl[] = {
298248618fb4SAlexandre Bounine 	{ PCI_DEVICE(PCI_VENDOR_ID_IDT, PCI_DEVICE_ID_TSI721) },
298348618fb4SAlexandre Bounine 	{ 0, }	/* terminate list */
298448618fb4SAlexandre Bounine };
298548618fb4SAlexandre Bounine 
298648618fb4SAlexandre Bounine MODULE_DEVICE_TABLE(pci, tsi721_pci_tbl);
298748618fb4SAlexandre Bounine 
298848618fb4SAlexandre Bounine static struct pci_driver tsi721_driver = {
298948618fb4SAlexandre Bounine 	.name		= "tsi721",
299048618fb4SAlexandre Bounine 	.id_table	= tsi721_pci_tbl,
299148618fb4SAlexandre Bounine 	.probe		= tsi721_probe,
2992748353ccSAlexandre Bounine 	.remove		= tsi721_remove,
2993e3dd8cd4SAlexandre Bounine 	.shutdown	= tsi721_shutdown,
299448618fb4SAlexandre Bounine };
299548618fb4SAlexandre Bounine 
2996748353ccSAlexandre Bounine module_pci_driver(tsi721_driver);
299794d9bd45SAlexandre Bounine 
299894d9bd45SAlexandre Bounine MODULE_DESCRIPTION("IDT Tsi721 PCIExpress-to-SRIO bridge driver");
299994d9bd45SAlexandre Bounine MODULE_AUTHOR("Integrated Device Technology, Inc.");
300094d9bd45SAlexandre Bounine MODULE_LICENSE("GPL");
3001