162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * RapidIO mport driver for Tsi721 PCIExpress-to-SRIO bridge
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2011 Integrated Device Technology, Inc.
662306a36Sopenharmony_ci * Alexandre Bounine <alexandre.bounine@idt.com>
762306a36Sopenharmony_ci * Chul Kim <chul.kim@idt.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/io.h>
1162306a36Sopenharmony_ci#include <linux/errno.h>
1262306a36Sopenharmony_ci#include <linux/init.h>
1362306a36Sopenharmony_ci#include <linux/ioport.h>
1462306a36Sopenharmony_ci#include <linux/kernel.h>
1562306a36Sopenharmony_ci#include <linux/module.h>
1662306a36Sopenharmony_ci#include <linux/pci.h>
1762306a36Sopenharmony_ci#include <linux/rio.h>
1862306a36Sopenharmony_ci#include <linux/rio_drv.h>
1962306a36Sopenharmony_ci#include <linux/dma-mapping.h>
2062306a36Sopenharmony_ci#include <linux/interrupt.h>
2162306a36Sopenharmony_ci#include <linux/kfifo.h>
2262306a36Sopenharmony_ci#include <linux/delay.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include "tsi721.h"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#ifdef DEBUG
2762306a36Sopenharmony_ciu32 tsi_dbg_level;
2862306a36Sopenharmony_cimodule_param_named(dbg_level, tsi_dbg_level, uint, S_IWUSR | S_IRUGO);
2962306a36Sopenharmony_ciMODULE_PARM_DESC(dbg_level, "Debugging output level (default 0 = none)");
3062306a36Sopenharmony_ci#endif
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic int pcie_mrrs = -1;
3362306a36Sopenharmony_cimodule_param(pcie_mrrs, int, S_IRUGO);
3462306a36Sopenharmony_ciMODULE_PARM_DESC(pcie_mrrs, "PCIe MRRS override value (0...5)");
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic u8 mbox_sel = 0x0f;
3762306a36Sopenharmony_cimodule_param(mbox_sel, byte, S_IRUGO);
3862306a36Sopenharmony_ciMODULE_PARM_DESC(mbox_sel,
3962306a36Sopenharmony_ci		 "RIO Messaging MBOX Selection Mask (default: 0x0f = all)");
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic DEFINE_SPINLOCK(tsi721_maint_lock);
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic void tsi721_omsg_handler(struct tsi721_device *priv, int ch);
4462306a36Sopenharmony_cistatic void tsi721_imsg_handler(struct tsi721_device *priv, int ch);
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci/**
4762306a36Sopenharmony_ci * tsi721_lcread - read from local SREP config space
4862306a36Sopenharmony_ci * @mport: RapidIO master port info
4962306a36Sopenharmony_ci * @index: ID of RapdiIO interface
5062306a36Sopenharmony_ci * @offset: Offset into configuration space
5162306a36Sopenharmony_ci * @len: Length (in bytes) of the maintenance transaction
5262306a36Sopenharmony_ci * @data: Value to be read into
5362306a36Sopenharmony_ci *
5462306a36Sopenharmony_ci * Generates a local SREP space read. Returns %0 on
5562306a36Sopenharmony_ci * success or %-EINVAL on failure.
5662306a36Sopenharmony_ci */
5762306a36Sopenharmony_cistatic int tsi721_lcread(struct rio_mport *mport, int index, u32 offset,
5862306a36Sopenharmony_ci			 int len, u32 *data)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	struct tsi721_device *priv = mport->priv;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	if (len != sizeof(u32))
6362306a36Sopenharmony_ci		return -EINVAL; /* only 32-bit access is supported */
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	*data = ioread32(priv->regs + offset);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	return 0;
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci/**
7162306a36Sopenharmony_ci * tsi721_lcwrite - write into local SREP config space
7262306a36Sopenharmony_ci * @mport: RapidIO master port info
7362306a36Sopenharmony_ci * @index: ID of RapdiIO interface
7462306a36Sopenharmony_ci * @offset: Offset into configuration space
7562306a36Sopenharmony_ci * @len: Length (in bytes) of the maintenance transaction
7662306a36Sopenharmony_ci * @data: Value to be written
7762306a36Sopenharmony_ci *
7862306a36Sopenharmony_ci * Generates a local write into SREP configuration space. Returns %0 on
7962306a36Sopenharmony_ci * success or %-EINVAL on failure.
8062306a36Sopenharmony_ci */
8162306a36Sopenharmony_cistatic int tsi721_lcwrite(struct rio_mport *mport, int index, u32 offset,
8262306a36Sopenharmony_ci			  int len, u32 data)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	struct tsi721_device *priv = mport->priv;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	if (len != sizeof(u32))
8762306a36Sopenharmony_ci		return -EINVAL; /* only 32-bit access is supported */
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	iowrite32(data, priv->regs + offset);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	return 0;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci/**
9562306a36Sopenharmony_ci * tsi721_maint_dma - Helper function to generate RapidIO maintenance
9662306a36Sopenharmony_ci *                    transactions using designated Tsi721 DMA channel.
9762306a36Sopenharmony_ci * @priv: pointer to tsi721 private data
9862306a36Sopenharmony_ci * @sys_size: RapdiIO transport system size
9962306a36Sopenharmony_ci * @destid: Destination ID of transaction
10062306a36Sopenharmony_ci * @hopcount: Number of hops to target device
10162306a36Sopenharmony_ci * @offset: Offset into configuration space
10262306a36Sopenharmony_ci * @len: Length (in bytes) of the maintenance transaction
10362306a36Sopenharmony_ci * @data: Location to be read from or write into
10462306a36Sopenharmony_ci * @do_wr: Operation flag (1 == MAINT_WR)
10562306a36Sopenharmony_ci *
10662306a36Sopenharmony_ci * Generates a RapidIO maintenance transaction (Read or Write).
10762306a36Sopenharmony_ci * Returns %0 on success and %-EINVAL or %-EFAULT on failure.
10862306a36Sopenharmony_ci */
10962306a36Sopenharmony_cistatic int tsi721_maint_dma(struct tsi721_device *priv, u32 sys_size,
11062306a36Sopenharmony_ci			u16 destid, u8 hopcount, u32 offset, int len,
11162306a36Sopenharmony_ci			u32 *data, int do_wr)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	void __iomem *regs = priv->regs + TSI721_DMAC_BASE(priv->mdma.ch_id);
11462306a36Sopenharmony_ci	struct tsi721_dma_desc *bd_ptr;
11562306a36Sopenharmony_ci	u32 rd_count, swr_ptr, ch_stat;
11662306a36Sopenharmony_ci	unsigned long flags;
11762306a36Sopenharmony_ci	int i, err = 0;
11862306a36Sopenharmony_ci	u32 op = do_wr ? MAINT_WR : MAINT_RD;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	if (offset > (RIO_MAINT_SPACE_SZ - len) || (len != sizeof(u32)))
12162306a36Sopenharmony_ci		return -EINVAL;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	spin_lock_irqsave(&tsi721_maint_lock, flags);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	bd_ptr = priv->mdma.bd_base;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	rd_count = ioread32(regs + TSI721_DMAC_DRDCNT);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	/* Initialize DMA descriptor */
13062306a36Sopenharmony_ci	bd_ptr[0].type_id = cpu_to_le32((DTYPE2 << 29) | (op << 19) | destid);
13162306a36Sopenharmony_ci	bd_ptr[0].bcount = cpu_to_le32((sys_size << 26) | 0x04);
13262306a36Sopenharmony_ci	bd_ptr[0].raddr_lo = cpu_to_le32((hopcount << 24) | offset);
13362306a36Sopenharmony_ci	bd_ptr[0].raddr_hi = 0;
13462306a36Sopenharmony_ci	if (do_wr)
13562306a36Sopenharmony_ci		bd_ptr[0].data[0] = cpu_to_be32p(data);
13662306a36Sopenharmony_ci	else
13762306a36Sopenharmony_ci		bd_ptr[0].data[0] = 0xffffffff;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	mb();
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	/* Start DMA operation */
14262306a36Sopenharmony_ci	iowrite32(rd_count + 2,	regs + TSI721_DMAC_DWRCNT);
14362306a36Sopenharmony_ci	ioread32(regs + TSI721_DMAC_DWRCNT);
14462306a36Sopenharmony_ci	i = 0;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	/* Wait until DMA transfer is finished */
14762306a36Sopenharmony_ci	while ((ch_stat = ioread32(regs + TSI721_DMAC_STS))
14862306a36Sopenharmony_ci							& TSI721_DMAC_STS_RUN) {
14962306a36Sopenharmony_ci		udelay(1);
15062306a36Sopenharmony_ci		if (++i >= 5000000) {
15162306a36Sopenharmony_ci			tsi_debug(MAINT, &priv->pdev->dev,
15262306a36Sopenharmony_ci				"DMA[%d] read timeout ch_status=%x",
15362306a36Sopenharmony_ci				priv->mdma.ch_id, ch_stat);
15462306a36Sopenharmony_ci			if (!do_wr)
15562306a36Sopenharmony_ci				*data = 0xffffffff;
15662306a36Sopenharmony_ci			err = -EIO;
15762306a36Sopenharmony_ci			goto err_out;
15862306a36Sopenharmony_ci		}
15962306a36Sopenharmony_ci	}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	if (ch_stat & TSI721_DMAC_STS_ABORT) {
16262306a36Sopenharmony_ci		/* If DMA operation aborted due to error,
16362306a36Sopenharmony_ci		 * reinitialize DMA channel
16462306a36Sopenharmony_ci		 */
16562306a36Sopenharmony_ci		tsi_debug(MAINT, &priv->pdev->dev, "DMA ABORT ch_stat=%x",
16662306a36Sopenharmony_ci			  ch_stat);
16762306a36Sopenharmony_ci		tsi_debug(MAINT, &priv->pdev->dev,
16862306a36Sopenharmony_ci			  "OP=%d : destid=%x hc=%x off=%x",
16962306a36Sopenharmony_ci			  do_wr ? MAINT_WR : MAINT_RD,
17062306a36Sopenharmony_ci			  destid, hopcount, offset);
17162306a36Sopenharmony_ci		iowrite32(TSI721_DMAC_INT_ALL, regs + TSI721_DMAC_INT);
17262306a36Sopenharmony_ci		iowrite32(TSI721_DMAC_CTL_INIT, regs + TSI721_DMAC_CTL);
17362306a36Sopenharmony_ci		udelay(10);
17462306a36Sopenharmony_ci		iowrite32(0, regs + TSI721_DMAC_DWRCNT);
17562306a36Sopenharmony_ci		udelay(1);
17662306a36Sopenharmony_ci		if (!do_wr)
17762306a36Sopenharmony_ci			*data = 0xffffffff;
17862306a36Sopenharmony_ci		err = -EIO;
17962306a36Sopenharmony_ci		goto err_out;
18062306a36Sopenharmony_ci	}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	if (!do_wr)
18362306a36Sopenharmony_ci		*data = be32_to_cpu(bd_ptr[0].data[0]);
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	/*
18662306a36Sopenharmony_ci	 * Update descriptor status FIFO RD pointer.
18762306a36Sopenharmony_ci	 * NOTE: Skipping check and clear FIFO entries because we are waiting
18862306a36Sopenharmony_ci	 * for transfer to be completed.
18962306a36Sopenharmony_ci	 */
19062306a36Sopenharmony_ci	swr_ptr = ioread32(regs + TSI721_DMAC_DSWP);
19162306a36Sopenharmony_ci	iowrite32(swr_ptr, regs + TSI721_DMAC_DSRP);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_cierr_out:
19462306a36Sopenharmony_ci	spin_unlock_irqrestore(&tsi721_maint_lock, flags);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	return err;
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci/**
20062306a36Sopenharmony_ci * tsi721_cread_dma - Generate a RapidIO maintenance read transaction
20162306a36Sopenharmony_ci *                    using Tsi721 BDMA engine.
20262306a36Sopenharmony_ci * @mport: RapidIO master port control structure
20362306a36Sopenharmony_ci * @index: ID of RapdiIO interface
20462306a36Sopenharmony_ci * @destid: Destination ID of transaction
20562306a36Sopenharmony_ci * @hopcount: Number of hops to target device
20662306a36Sopenharmony_ci * @offset: Offset into configuration space
20762306a36Sopenharmony_ci * @len: Length (in bytes) of the maintenance transaction
20862306a36Sopenharmony_ci * @val: Location to be read into
20962306a36Sopenharmony_ci *
21062306a36Sopenharmony_ci * Generates a RapidIO maintenance read transaction.
21162306a36Sopenharmony_ci * Returns %0 on success and %-EINVAL or %-EFAULT on failure.
21262306a36Sopenharmony_ci */
21362306a36Sopenharmony_cistatic int tsi721_cread_dma(struct rio_mport *mport, int index, u16 destid,
21462306a36Sopenharmony_ci			u8 hopcount, u32 offset, int len, u32 *data)
21562306a36Sopenharmony_ci{
21662306a36Sopenharmony_ci	struct tsi721_device *priv = mport->priv;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	return tsi721_maint_dma(priv, mport->sys_size, destid, hopcount,
21962306a36Sopenharmony_ci				offset, len, data, 0);
22062306a36Sopenharmony_ci}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci/**
22362306a36Sopenharmony_ci * tsi721_cwrite_dma - Generate a RapidIO maintenance write transaction
22462306a36Sopenharmony_ci *                     using Tsi721 BDMA engine
22562306a36Sopenharmony_ci * @mport: RapidIO master port control structure
22662306a36Sopenharmony_ci * @index: ID of RapdiIO interface
22762306a36Sopenharmony_ci * @destid: Destination ID of transaction
22862306a36Sopenharmony_ci * @hopcount: Number of hops to target device
22962306a36Sopenharmony_ci * @offset: Offset into configuration space
23062306a36Sopenharmony_ci * @len: Length (in bytes) of the maintenance transaction
23162306a36Sopenharmony_ci * @val: Value to be written
23262306a36Sopenharmony_ci *
23362306a36Sopenharmony_ci * Generates a RapidIO maintenance write transaction.
23462306a36Sopenharmony_ci * Returns %0 on success and %-EINVAL or %-EFAULT on failure.
23562306a36Sopenharmony_ci */
23662306a36Sopenharmony_cistatic int tsi721_cwrite_dma(struct rio_mport *mport, int index, u16 destid,
23762306a36Sopenharmony_ci			 u8 hopcount, u32 offset, int len, u32 data)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	struct tsi721_device *priv = mport->priv;
24062306a36Sopenharmony_ci	u32 temp = data;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	return tsi721_maint_dma(priv, mport->sys_size, destid, hopcount,
24362306a36Sopenharmony_ci				offset, len, &temp, 1);
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci/**
24762306a36Sopenharmony_ci * tsi721_pw_handler - Tsi721 inbound port-write interrupt handler
24862306a36Sopenharmony_ci * @priv:  tsi721 device private structure
24962306a36Sopenharmony_ci *
25062306a36Sopenharmony_ci * Handles inbound port-write interrupts. Copies PW message from an internal
25162306a36Sopenharmony_ci * buffer into PW message FIFO and schedules deferred routine to process
25262306a36Sopenharmony_ci * queued messages.
25362306a36Sopenharmony_ci */
25462306a36Sopenharmony_cistatic int
25562306a36Sopenharmony_citsi721_pw_handler(struct tsi721_device *priv)
25662306a36Sopenharmony_ci{
25762306a36Sopenharmony_ci	u32 pw_stat;
25862306a36Sopenharmony_ci	u32 pw_buf[TSI721_RIO_PW_MSG_SIZE/sizeof(u32)];
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	pw_stat = ioread32(priv->regs + TSI721_RIO_PW_RX_STAT);
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	if (pw_stat & TSI721_RIO_PW_RX_STAT_PW_VAL) {
26462306a36Sopenharmony_ci		pw_buf[0] = ioread32(priv->regs + TSI721_RIO_PW_RX_CAPT(0));
26562306a36Sopenharmony_ci		pw_buf[1] = ioread32(priv->regs + TSI721_RIO_PW_RX_CAPT(1));
26662306a36Sopenharmony_ci		pw_buf[2] = ioread32(priv->regs + TSI721_RIO_PW_RX_CAPT(2));
26762306a36Sopenharmony_ci		pw_buf[3] = ioread32(priv->regs + TSI721_RIO_PW_RX_CAPT(3));
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci		/* Queue PW message (if there is room in FIFO),
27062306a36Sopenharmony_ci		 * otherwise discard it.
27162306a36Sopenharmony_ci		 */
27262306a36Sopenharmony_ci		spin_lock(&priv->pw_fifo_lock);
27362306a36Sopenharmony_ci		if (kfifo_avail(&priv->pw_fifo) >= TSI721_RIO_PW_MSG_SIZE)
27462306a36Sopenharmony_ci			kfifo_in(&priv->pw_fifo, pw_buf,
27562306a36Sopenharmony_ci						TSI721_RIO_PW_MSG_SIZE);
27662306a36Sopenharmony_ci		else
27762306a36Sopenharmony_ci			priv->pw_discard_count++;
27862306a36Sopenharmony_ci		spin_unlock(&priv->pw_fifo_lock);
27962306a36Sopenharmony_ci	}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	/* Clear pending PW interrupts */
28262306a36Sopenharmony_ci	iowrite32(TSI721_RIO_PW_RX_STAT_PW_DISC | TSI721_RIO_PW_RX_STAT_PW_VAL,
28362306a36Sopenharmony_ci		  priv->regs + TSI721_RIO_PW_RX_STAT);
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	schedule_work(&priv->pw_work);
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	return 0;
28862306a36Sopenharmony_ci}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_cistatic void tsi721_pw_dpc(struct work_struct *work)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	struct tsi721_device *priv = container_of(work, struct tsi721_device,
29362306a36Sopenharmony_ci						    pw_work);
29462306a36Sopenharmony_ci	union rio_pw_msg pwmsg;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	/*
29762306a36Sopenharmony_ci	 * Process port-write messages
29862306a36Sopenharmony_ci	 */
29962306a36Sopenharmony_ci	while (kfifo_out_spinlocked(&priv->pw_fifo, (unsigned char *)&pwmsg,
30062306a36Sopenharmony_ci			 TSI721_RIO_PW_MSG_SIZE, &priv->pw_fifo_lock)) {
30162306a36Sopenharmony_ci		/* Pass the port-write message to RIO core for processing */
30262306a36Sopenharmony_ci		rio_inb_pwrite_handler(&priv->mport, &pwmsg);
30362306a36Sopenharmony_ci	}
30462306a36Sopenharmony_ci}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci/**
30762306a36Sopenharmony_ci * tsi721_pw_enable - enable/disable port-write interface init
30862306a36Sopenharmony_ci * @mport: Master port implementing the port write unit
30962306a36Sopenharmony_ci * @enable:    1=enable; 0=disable port-write message handling
31062306a36Sopenharmony_ci */
31162306a36Sopenharmony_cistatic int tsi721_pw_enable(struct rio_mport *mport, int enable)
31262306a36Sopenharmony_ci{
31362306a36Sopenharmony_ci	struct tsi721_device *priv = mport->priv;
31462306a36Sopenharmony_ci	u32 rval;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	rval = ioread32(priv->regs + TSI721_RIO_EM_INT_ENABLE);
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	if (enable)
31962306a36Sopenharmony_ci		rval |= TSI721_RIO_EM_INT_ENABLE_PW_RX;
32062306a36Sopenharmony_ci	else
32162306a36Sopenharmony_ci		rval &= ~TSI721_RIO_EM_INT_ENABLE_PW_RX;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	/* Clear pending PW interrupts */
32462306a36Sopenharmony_ci	iowrite32(TSI721_RIO_PW_RX_STAT_PW_DISC | TSI721_RIO_PW_RX_STAT_PW_VAL,
32562306a36Sopenharmony_ci		  priv->regs + TSI721_RIO_PW_RX_STAT);
32662306a36Sopenharmony_ci	/* Update enable bits */
32762306a36Sopenharmony_ci	iowrite32(rval, priv->regs + TSI721_RIO_EM_INT_ENABLE);
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	return 0;
33062306a36Sopenharmony_ci}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci/**
33362306a36Sopenharmony_ci * tsi721_dsend - Send a RapidIO doorbell
33462306a36Sopenharmony_ci * @mport: RapidIO master port info
33562306a36Sopenharmony_ci * @index: ID of RapidIO interface
33662306a36Sopenharmony_ci * @destid: Destination ID of target device
33762306a36Sopenharmony_ci * @data: 16-bit info field of RapidIO doorbell
33862306a36Sopenharmony_ci *
33962306a36Sopenharmony_ci * Sends a RapidIO doorbell message. Always returns %0.
34062306a36Sopenharmony_ci */
34162306a36Sopenharmony_cistatic int tsi721_dsend(struct rio_mport *mport, int index,
34262306a36Sopenharmony_ci			u16 destid, u16 data)
34362306a36Sopenharmony_ci{
34462306a36Sopenharmony_ci	struct tsi721_device *priv = mport->priv;
34562306a36Sopenharmony_ci	u32 offset;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	offset = (((mport->sys_size) ? RIO_TT_CODE_16 : RIO_TT_CODE_8) << 18) |
34862306a36Sopenharmony_ci		 (destid << 2);
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	tsi_debug(DBELL, &priv->pdev->dev,
35162306a36Sopenharmony_ci		  "Send Doorbell 0x%04x to destID 0x%x", data, destid);
35262306a36Sopenharmony_ci	iowrite16be(data, priv->odb_base + offset);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	return 0;
35562306a36Sopenharmony_ci}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci/**
35862306a36Sopenharmony_ci * tsi721_dbell_handler - Tsi721 doorbell interrupt handler
35962306a36Sopenharmony_ci * @priv: tsi721 device-specific data structure
36062306a36Sopenharmony_ci *
36162306a36Sopenharmony_ci * Handles inbound doorbell interrupts. Copies doorbell entry from an internal
36262306a36Sopenharmony_ci * buffer into DB message FIFO and schedules deferred  routine to process
36362306a36Sopenharmony_ci * queued DBs.
36462306a36Sopenharmony_ci */
36562306a36Sopenharmony_cistatic int
36662306a36Sopenharmony_citsi721_dbell_handler(struct tsi721_device *priv)
36762306a36Sopenharmony_ci{
36862306a36Sopenharmony_ci	u32 regval;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	/* Disable IDB interrupts */
37162306a36Sopenharmony_ci	regval = ioread32(priv->regs + TSI721_SR_CHINTE(IDB_QUEUE));
37262306a36Sopenharmony_ci	regval &= ~TSI721_SR_CHINT_IDBQRCV;
37362306a36Sopenharmony_ci	iowrite32(regval,
37462306a36Sopenharmony_ci		priv->regs + TSI721_SR_CHINTE(IDB_QUEUE));
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	schedule_work(&priv->idb_work);
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	return 0;
37962306a36Sopenharmony_ci}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_cistatic void tsi721_db_dpc(struct work_struct *work)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	struct tsi721_device *priv = container_of(work, struct tsi721_device,
38462306a36Sopenharmony_ci						    idb_work);
38562306a36Sopenharmony_ci	struct rio_mport *mport;
38662306a36Sopenharmony_ci	struct rio_dbell *dbell;
38762306a36Sopenharmony_ci	int found = 0;
38862306a36Sopenharmony_ci	u32 wr_ptr, rd_ptr;
38962306a36Sopenharmony_ci	u64 *idb_entry;
39062306a36Sopenharmony_ci	u32 regval;
39162306a36Sopenharmony_ci	union {
39262306a36Sopenharmony_ci		u64 msg;
39362306a36Sopenharmony_ci		u8  bytes[8];
39462306a36Sopenharmony_ci	} idb;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	/*
39762306a36Sopenharmony_ci	 * Process queued inbound doorbells
39862306a36Sopenharmony_ci	 */
39962306a36Sopenharmony_ci	mport = &priv->mport;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	wr_ptr = ioread32(priv->regs + TSI721_IDQ_WP(IDB_QUEUE)) % IDB_QSIZE;
40262306a36Sopenharmony_ci	rd_ptr = ioread32(priv->regs + TSI721_IDQ_RP(IDB_QUEUE)) % IDB_QSIZE;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	while (wr_ptr != rd_ptr) {
40562306a36Sopenharmony_ci		idb_entry = (u64 *)(priv->idb_base +
40662306a36Sopenharmony_ci					(TSI721_IDB_ENTRY_SIZE * rd_ptr));
40762306a36Sopenharmony_ci		rd_ptr++;
40862306a36Sopenharmony_ci		rd_ptr %= IDB_QSIZE;
40962306a36Sopenharmony_ci		idb.msg = *idb_entry;
41062306a36Sopenharmony_ci		*idb_entry = 0;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci		/* Process one doorbell */
41362306a36Sopenharmony_ci		list_for_each_entry(dbell, &mport->dbells, node) {
41462306a36Sopenharmony_ci			if ((dbell->res->start <= DBELL_INF(idb.bytes)) &&
41562306a36Sopenharmony_ci			    (dbell->res->end >= DBELL_INF(idb.bytes))) {
41662306a36Sopenharmony_ci				found = 1;
41762306a36Sopenharmony_ci				break;
41862306a36Sopenharmony_ci			}
41962306a36Sopenharmony_ci		}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci		if (found) {
42262306a36Sopenharmony_ci			dbell->dinb(mport, dbell->dev_id, DBELL_SID(idb.bytes),
42362306a36Sopenharmony_ci				    DBELL_TID(idb.bytes), DBELL_INF(idb.bytes));
42462306a36Sopenharmony_ci		} else {
42562306a36Sopenharmony_ci			tsi_debug(DBELL, &priv->pdev->dev,
42662306a36Sopenharmony_ci				  "spurious IDB sid %2.2x tid %2.2x info %4.4x",
42762306a36Sopenharmony_ci				  DBELL_SID(idb.bytes), DBELL_TID(idb.bytes),
42862306a36Sopenharmony_ci				  DBELL_INF(idb.bytes));
42962306a36Sopenharmony_ci		}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci		wr_ptr = ioread32(priv->regs +
43262306a36Sopenharmony_ci				  TSI721_IDQ_WP(IDB_QUEUE)) % IDB_QSIZE;
43362306a36Sopenharmony_ci	}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	iowrite32(rd_ptr & (IDB_QSIZE - 1),
43662306a36Sopenharmony_ci		priv->regs + TSI721_IDQ_RP(IDB_QUEUE));
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	/* Re-enable IDB interrupts */
43962306a36Sopenharmony_ci	regval = ioread32(priv->regs + TSI721_SR_CHINTE(IDB_QUEUE));
44062306a36Sopenharmony_ci	regval |= TSI721_SR_CHINT_IDBQRCV;
44162306a36Sopenharmony_ci	iowrite32(regval,
44262306a36Sopenharmony_ci		priv->regs + TSI721_SR_CHINTE(IDB_QUEUE));
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	wr_ptr = ioread32(priv->regs + TSI721_IDQ_WP(IDB_QUEUE)) % IDB_QSIZE;
44562306a36Sopenharmony_ci	if (wr_ptr != rd_ptr)
44662306a36Sopenharmony_ci		schedule_work(&priv->idb_work);
44762306a36Sopenharmony_ci}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci/**
45062306a36Sopenharmony_ci * tsi721_irqhandler - Tsi721 interrupt handler
45162306a36Sopenharmony_ci * @irq: Linux interrupt number
45262306a36Sopenharmony_ci * @ptr: Pointer to interrupt-specific data (tsi721_device structure)
45362306a36Sopenharmony_ci *
45462306a36Sopenharmony_ci * Handles Tsi721 interrupts signaled using MSI and INTA. Checks reported
45562306a36Sopenharmony_ci * interrupt events and calls an event-specific handler(s).
45662306a36Sopenharmony_ci */
45762306a36Sopenharmony_cistatic irqreturn_t tsi721_irqhandler(int irq, void *ptr)
45862306a36Sopenharmony_ci{
45962306a36Sopenharmony_ci	struct tsi721_device *priv = (struct tsi721_device *)ptr;
46062306a36Sopenharmony_ci	u32 dev_int;
46162306a36Sopenharmony_ci	u32 dev_ch_int;
46262306a36Sopenharmony_ci	u32 intval;
46362306a36Sopenharmony_ci	u32 ch_inte;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	/* For MSI mode disable all device-level interrupts */
46662306a36Sopenharmony_ci	if (priv->flags & TSI721_USING_MSI)
46762306a36Sopenharmony_ci		iowrite32(0, priv->regs + TSI721_DEV_INTE);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	dev_int = ioread32(priv->regs + TSI721_DEV_INT);
47062306a36Sopenharmony_ci	if (!dev_int)
47162306a36Sopenharmony_ci		return IRQ_NONE;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	dev_ch_int = ioread32(priv->regs + TSI721_DEV_CHAN_INT);
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	if (dev_int & TSI721_DEV_INT_SR2PC_CH) {
47662306a36Sopenharmony_ci		/* Service SR2PC Channel interrupts */
47762306a36Sopenharmony_ci		if (dev_ch_int & TSI721_INT_SR2PC_CHAN(IDB_QUEUE)) {
47862306a36Sopenharmony_ci			/* Service Inbound Doorbell interrupt */
47962306a36Sopenharmony_ci			intval = ioread32(priv->regs +
48062306a36Sopenharmony_ci						TSI721_SR_CHINT(IDB_QUEUE));
48162306a36Sopenharmony_ci			if (intval & TSI721_SR_CHINT_IDBQRCV)
48262306a36Sopenharmony_ci				tsi721_dbell_handler(priv);
48362306a36Sopenharmony_ci			else
48462306a36Sopenharmony_ci				tsi_info(&priv->pdev->dev,
48562306a36Sopenharmony_ci					"Unsupported SR_CH_INT %x", intval);
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci			/* Clear interrupts */
48862306a36Sopenharmony_ci			iowrite32(intval,
48962306a36Sopenharmony_ci				priv->regs + TSI721_SR_CHINT(IDB_QUEUE));
49062306a36Sopenharmony_ci			ioread32(priv->regs + TSI721_SR_CHINT(IDB_QUEUE));
49162306a36Sopenharmony_ci		}
49262306a36Sopenharmony_ci	}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	if (dev_int & TSI721_DEV_INT_SMSG_CH) {
49562306a36Sopenharmony_ci		int ch;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci		/*
49862306a36Sopenharmony_ci		 * Service channel interrupts from Messaging Engine
49962306a36Sopenharmony_ci		 */
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci		if (dev_ch_int & TSI721_INT_IMSG_CHAN_M) { /* Inbound Msg */
50262306a36Sopenharmony_ci			/* Disable signaled OB MSG Channel interrupts */
50362306a36Sopenharmony_ci			ch_inte = ioread32(priv->regs + TSI721_DEV_CHAN_INTE);
50462306a36Sopenharmony_ci			ch_inte &= ~(dev_ch_int & TSI721_INT_IMSG_CHAN_M);
50562306a36Sopenharmony_ci			iowrite32(ch_inte, priv->regs + TSI721_DEV_CHAN_INTE);
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci			/*
50862306a36Sopenharmony_ci			 * Process Inbound Message interrupt for each MBOX
50962306a36Sopenharmony_ci			 */
51062306a36Sopenharmony_ci			for (ch = 4; ch < RIO_MAX_MBOX + 4; ch++) {
51162306a36Sopenharmony_ci				if (!(dev_ch_int & TSI721_INT_IMSG_CHAN(ch)))
51262306a36Sopenharmony_ci					continue;
51362306a36Sopenharmony_ci				tsi721_imsg_handler(priv, ch);
51462306a36Sopenharmony_ci			}
51562306a36Sopenharmony_ci		}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci		if (dev_ch_int & TSI721_INT_OMSG_CHAN_M) { /* Outbound Msg */
51862306a36Sopenharmony_ci			/* Disable signaled OB MSG Channel interrupts */
51962306a36Sopenharmony_ci			ch_inte = ioread32(priv->regs + TSI721_DEV_CHAN_INTE);
52062306a36Sopenharmony_ci			ch_inte &= ~(dev_ch_int & TSI721_INT_OMSG_CHAN_M);
52162306a36Sopenharmony_ci			iowrite32(ch_inte, priv->regs + TSI721_DEV_CHAN_INTE);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci			/*
52462306a36Sopenharmony_ci			 * Process Outbound Message interrupts for each MBOX
52562306a36Sopenharmony_ci			 */
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci			for (ch = 0; ch < RIO_MAX_MBOX; ch++) {
52862306a36Sopenharmony_ci				if (!(dev_ch_int & TSI721_INT_OMSG_CHAN(ch)))
52962306a36Sopenharmony_ci					continue;
53062306a36Sopenharmony_ci				tsi721_omsg_handler(priv, ch);
53162306a36Sopenharmony_ci			}
53262306a36Sopenharmony_ci		}
53362306a36Sopenharmony_ci	}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	if (dev_int & TSI721_DEV_INT_SRIO) {
53662306a36Sopenharmony_ci		/* Service SRIO MAC interrupts */
53762306a36Sopenharmony_ci		intval = ioread32(priv->regs + TSI721_RIO_EM_INT_STAT);
53862306a36Sopenharmony_ci		if (intval & TSI721_RIO_EM_INT_STAT_PW_RX)
53962306a36Sopenharmony_ci			tsi721_pw_handler(priv);
54062306a36Sopenharmony_ci	}
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci#ifdef CONFIG_RAPIDIO_DMA_ENGINE
54362306a36Sopenharmony_ci	if (dev_int & TSI721_DEV_INT_BDMA_CH) {
54462306a36Sopenharmony_ci		int ch;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci		if (dev_ch_int & TSI721_INT_BDMA_CHAN_M) {
54762306a36Sopenharmony_ci			tsi_debug(DMA, &priv->pdev->dev,
54862306a36Sopenharmony_ci				  "IRQ from DMA channel 0x%08x", dev_ch_int);
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci			for (ch = 0; ch < TSI721_DMA_MAXCH; ch++) {
55162306a36Sopenharmony_ci				if (!(dev_ch_int & TSI721_INT_BDMA_CHAN(ch)))
55262306a36Sopenharmony_ci					continue;
55362306a36Sopenharmony_ci				tsi721_bdma_handler(&priv->bdma[ch]);
55462306a36Sopenharmony_ci			}
55562306a36Sopenharmony_ci		}
55662306a36Sopenharmony_ci	}
55762306a36Sopenharmony_ci#endif
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	/* For MSI mode re-enable device-level interrupts */
56062306a36Sopenharmony_ci	if (priv->flags & TSI721_USING_MSI) {
56162306a36Sopenharmony_ci		dev_int = TSI721_DEV_INT_SR2PC_CH | TSI721_DEV_INT_SRIO |
56262306a36Sopenharmony_ci			TSI721_DEV_INT_SMSG_CH | TSI721_DEV_INT_BDMA_CH;
56362306a36Sopenharmony_ci		iowrite32(dev_int, priv->regs + TSI721_DEV_INTE);
56462306a36Sopenharmony_ci	}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	return IRQ_HANDLED;
56762306a36Sopenharmony_ci}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_cistatic void tsi721_interrupts_init(struct tsi721_device *priv)
57062306a36Sopenharmony_ci{
57162306a36Sopenharmony_ci	u32 intr;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	/* Enable IDB interrupts */
57462306a36Sopenharmony_ci	iowrite32(TSI721_SR_CHINT_ALL,
57562306a36Sopenharmony_ci		priv->regs + TSI721_SR_CHINT(IDB_QUEUE));
57662306a36Sopenharmony_ci	iowrite32(TSI721_SR_CHINT_IDBQRCV,
57762306a36Sopenharmony_ci		priv->regs + TSI721_SR_CHINTE(IDB_QUEUE));
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	/* Enable SRIO MAC interrupts */
58062306a36Sopenharmony_ci	iowrite32(TSI721_RIO_EM_DEV_INT_EN_INT,
58162306a36Sopenharmony_ci		priv->regs + TSI721_RIO_EM_DEV_INT_EN);
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	/* Enable interrupts from channels in use */
58462306a36Sopenharmony_ci#ifdef CONFIG_RAPIDIO_DMA_ENGINE
58562306a36Sopenharmony_ci	intr = TSI721_INT_SR2PC_CHAN(IDB_QUEUE) |
58662306a36Sopenharmony_ci		(TSI721_INT_BDMA_CHAN_M &
58762306a36Sopenharmony_ci		 ~TSI721_INT_BDMA_CHAN(TSI721_DMACH_MAINT));
58862306a36Sopenharmony_ci#else
58962306a36Sopenharmony_ci	intr = TSI721_INT_SR2PC_CHAN(IDB_QUEUE);
59062306a36Sopenharmony_ci#endif
59162306a36Sopenharmony_ci	iowrite32(intr,	priv->regs + TSI721_DEV_CHAN_INTE);
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	if (priv->flags & TSI721_USING_MSIX)
59462306a36Sopenharmony_ci		intr = TSI721_DEV_INT_SRIO;
59562306a36Sopenharmony_ci	else
59662306a36Sopenharmony_ci		intr = TSI721_DEV_INT_SR2PC_CH | TSI721_DEV_INT_SRIO |
59762306a36Sopenharmony_ci			TSI721_DEV_INT_SMSG_CH | TSI721_DEV_INT_BDMA_CH;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	iowrite32(intr, priv->regs + TSI721_DEV_INTE);
60062306a36Sopenharmony_ci	ioread32(priv->regs + TSI721_DEV_INTE);
60162306a36Sopenharmony_ci}
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci#ifdef CONFIG_PCI_MSI
60462306a36Sopenharmony_ci/**
60562306a36Sopenharmony_ci * tsi721_omsg_msix - MSI-X interrupt handler for outbound messaging
60662306a36Sopenharmony_ci * @irq: Linux interrupt number
60762306a36Sopenharmony_ci * @ptr: Pointer to interrupt-specific data (tsi721_device structure)
60862306a36Sopenharmony_ci *
60962306a36Sopenharmony_ci * Handles outbound messaging interrupts signaled using MSI-X.
61062306a36Sopenharmony_ci */
61162306a36Sopenharmony_cistatic irqreturn_t tsi721_omsg_msix(int irq, void *ptr)
61262306a36Sopenharmony_ci{
61362306a36Sopenharmony_ci	struct tsi721_device *priv = (struct tsi721_device *)ptr;
61462306a36Sopenharmony_ci	int mbox;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	mbox = (irq - priv->msix[TSI721_VECT_OMB0_DONE].vector) % RIO_MAX_MBOX;
61762306a36Sopenharmony_ci	tsi721_omsg_handler(priv, mbox);
61862306a36Sopenharmony_ci	return IRQ_HANDLED;
61962306a36Sopenharmony_ci}
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci/**
62262306a36Sopenharmony_ci * tsi721_imsg_msix - MSI-X interrupt handler for inbound messaging
62362306a36Sopenharmony_ci * @irq: Linux interrupt number
62462306a36Sopenharmony_ci * @ptr: Pointer to interrupt-specific data (tsi721_device structure)
62562306a36Sopenharmony_ci *
62662306a36Sopenharmony_ci * Handles inbound messaging interrupts signaled using MSI-X.
62762306a36Sopenharmony_ci */
62862306a36Sopenharmony_cistatic irqreturn_t tsi721_imsg_msix(int irq, void *ptr)
62962306a36Sopenharmony_ci{
63062306a36Sopenharmony_ci	struct tsi721_device *priv = (struct tsi721_device *)ptr;
63162306a36Sopenharmony_ci	int mbox;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	mbox = (irq - priv->msix[TSI721_VECT_IMB0_RCV].vector) % RIO_MAX_MBOX;
63462306a36Sopenharmony_ci	tsi721_imsg_handler(priv, mbox + 4);
63562306a36Sopenharmony_ci	return IRQ_HANDLED;
63662306a36Sopenharmony_ci}
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci/**
63962306a36Sopenharmony_ci * tsi721_srio_msix - Tsi721 MSI-X SRIO MAC interrupt handler
64062306a36Sopenharmony_ci * @irq: Linux interrupt number
64162306a36Sopenharmony_ci * @ptr: Pointer to interrupt-specific data (tsi721_device structure)
64262306a36Sopenharmony_ci *
64362306a36Sopenharmony_ci * Handles Tsi721 interrupts from SRIO MAC.
64462306a36Sopenharmony_ci */
64562306a36Sopenharmony_cistatic irqreturn_t tsi721_srio_msix(int irq, void *ptr)
64662306a36Sopenharmony_ci{
64762306a36Sopenharmony_ci	struct tsi721_device *priv = (struct tsi721_device *)ptr;
64862306a36Sopenharmony_ci	u32 srio_int;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	/* Service SRIO MAC interrupts */
65162306a36Sopenharmony_ci	srio_int = ioread32(priv->regs + TSI721_RIO_EM_INT_STAT);
65262306a36Sopenharmony_ci	if (srio_int & TSI721_RIO_EM_INT_STAT_PW_RX)
65362306a36Sopenharmony_ci		tsi721_pw_handler(priv);
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	return IRQ_HANDLED;
65662306a36Sopenharmony_ci}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci/**
65962306a36Sopenharmony_ci * tsi721_sr2pc_ch_msix - Tsi721 MSI-X SR2PC Channel interrupt handler
66062306a36Sopenharmony_ci * @irq: Linux interrupt number
66162306a36Sopenharmony_ci * @ptr: Pointer to interrupt-specific data (tsi721_device structure)
66262306a36Sopenharmony_ci *
66362306a36Sopenharmony_ci * Handles Tsi721 interrupts from SR2PC Channel.
66462306a36Sopenharmony_ci * NOTE: At this moment services only one SR2PC channel associated with inbound
66562306a36Sopenharmony_ci * doorbells.
66662306a36Sopenharmony_ci */
66762306a36Sopenharmony_cistatic irqreturn_t tsi721_sr2pc_ch_msix(int irq, void *ptr)
66862306a36Sopenharmony_ci{
66962306a36Sopenharmony_ci	struct tsi721_device *priv = (struct tsi721_device *)ptr;
67062306a36Sopenharmony_ci	u32 sr_ch_int;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	/* Service Inbound DB interrupt from SR2PC channel */
67362306a36Sopenharmony_ci	sr_ch_int = ioread32(priv->regs + TSI721_SR_CHINT(IDB_QUEUE));
67462306a36Sopenharmony_ci	if (sr_ch_int & TSI721_SR_CHINT_IDBQRCV)
67562306a36Sopenharmony_ci		tsi721_dbell_handler(priv);
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	/* Clear interrupts */
67862306a36Sopenharmony_ci	iowrite32(sr_ch_int, priv->regs + TSI721_SR_CHINT(IDB_QUEUE));
67962306a36Sopenharmony_ci	/* Read back to ensure that interrupt was cleared */
68062306a36Sopenharmony_ci	sr_ch_int = ioread32(priv->regs + TSI721_SR_CHINT(IDB_QUEUE));
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	return IRQ_HANDLED;
68362306a36Sopenharmony_ci}
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci/**
68662306a36Sopenharmony_ci * tsi721_request_msix - register interrupt service for MSI-X mode.
68762306a36Sopenharmony_ci * @priv: tsi721 device-specific data structure
68862306a36Sopenharmony_ci *
68962306a36Sopenharmony_ci * Registers MSI-X interrupt service routines for interrupts that are active
69062306a36Sopenharmony_ci * immediately after mport initialization. Messaging interrupt service routines
69162306a36Sopenharmony_ci * should be registered during corresponding open requests.
69262306a36Sopenharmony_ci */
69362306a36Sopenharmony_cistatic int tsi721_request_msix(struct tsi721_device *priv)
69462306a36Sopenharmony_ci{
69562306a36Sopenharmony_ci	int err = 0;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	err = request_irq(priv->msix[TSI721_VECT_IDB].vector,
69862306a36Sopenharmony_ci			tsi721_sr2pc_ch_msix, 0,
69962306a36Sopenharmony_ci			priv->msix[TSI721_VECT_IDB].irq_name, (void *)priv);
70062306a36Sopenharmony_ci	if (err)
70162306a36Sopenharmony_ci		return err;
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	err = request_irq(priv->msix[TSI721_VECT_PWRX].vector,
70462306a36Sopenharmony_ci			tsi721_srio_msix, 0,
70562306a36Sopenharmony_ci			priv->msix[TSI721_VECT_PWRX].irq_name, (void *)priv);
70662306a36Sopenharmony_ci	if (err) {
70762306a36Sopenharmony_ci		free_irq(priv->msix[TSI721_VECT_IDB].vector, (void *)priv);
70862306a36Sopenharmony_ci		return err;
70962306a36Sopenharmony_ci	}
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	return 0;
71262306a36Sopenharmony_ci}
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci/**
71562306a36Sopenharmony_ci * tsi721_enable_msix - Attempts to enable MSI-X support for Tsi721.
71662306a36Sopenharmony_ci * @priv: pointer to tsi721 private data
71762306a36Sopenharmony_ci *
71862306a36Sopenharmony_ci * Configures MSI-X support for Tsi721. Supports only an exact number
71962306a36Sopenharmony_ci * of requested vectors.
72062306a36Sopenharmony_ci */
72162306a36Sopenharmony_cistatic int tsi721_enable_msix(struct tsi721_device *priv)
72262306a36Sopenharmony_ci{
72362306a36Sopenharmony_ci	struct msix_entry entries[TSI721_VECT_MAX];
72462306a36Sopenharmony_ci	int err;
72562306a36Sopenharmony_ci	int i;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	entries[TSI721_VECT_IDB].entry = TSI721_MSIX_SR2PC_IDBQ_RCV(IDB_QUEUE);
72862306a36Sopenharmony_ci	entries[TSI721_VECT_PWRX].entry = TSI721_MSIX_SRIO_MAC_INT;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	/*
73162306a36Sopenharmony_ci	 * Initialize MSI-X entries for Messaging Engine:
73262306a36Sopenharmony_ci	 * this driver supports four RIO mailboxes (inbound and outbound)
73362306a36Sopenharmony_ci	 * NOTE: Inbound message MBOX 0...4 use IB channels 4...7. Therefore
73462306a36Sopenharmony_ci	 * offset +4 is added to IB MBOX number.
73562306a36Sopenharmony_ci	 */
73662306a36Sopenharmony_ci	for (i = 0; i < RIO_MAX_MBOX; i++) {
73762306a36Sopenharmony_ci		entries[TSI721_VECT_IMB0_RCV + i].entry =
73862306a36Sopenharmony_ci					TSI721_MSIX_IMSG_DQ_RCV(i + 4);
73962306a36Sopenharmony_ci		entries[TSI721_VECT_IMB0_INT + i].entry =
74062306a36Sopenharmony_ci					TSI721_MSIX_IMSG_INT(i + 4);
74162306a36Sopenharmony_ci		entries[TSI721_VECT_OMB0_DONE + i].entry =
74262306a36Sopenharmony_ci					TSI721_MSIX_OMSG_DONE(i);
74362306a36Sopenharmony_ci		entries[TSI721_VECT_OMB0_INT + i].entry =
74462306a36Sopenharmony_ci					TSI721_MSIX_OMSG_INT(i);
74562306a36Sopenharmony_ci	}
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci#ifdef CONFIG_RAPIDIO_DMA_ENGINE
74862306a36Sopenharmony_ci	/*
74962306a36Sopenharmony_ci	 * Initialize MSI-X entries for Block DMA Engine:
75062306a36Sopenharmony_ci	 * this driver supports XXX DMA channels
75162306a36Sopenharmony_ci	 * (one is reserved for SRIO maintenance transactions)
75262306a36Sopenharmony_ci	 */
75362306a36Sopenharmony_ci	for (i = 0; i < TSI721_DMA_CHNUM; i++) {
75462306a36Sopenharmony_ci		entries[TSI721_VECT_DMA0_DONE + i].entry =
75562306a36Sopenharmony_ci					TSI721_MSIX_DMACH_DONE(i);
75662306a36Sopenharmony_ci		entries[TSI721_VECT_DMA0_INT + i].entry =
75762306a36Sopenharmony_ci					TSI721_MSIX_DMACH_INT(i);
75862306a36Sopenharmony_ci	}
75962306a36Sopenharmony_ci#endif /* CONFIG_RAPIDIO_DMA_ENGINE */
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	err = pci_enable_msix_exact(priv->pdev, entries, ARRAY_SIZE(entries));
76262306a36Sopenharmony_ci	if (err) {
76362306a36Sopenharmony_ci		tsi_err(&priv->pdev->dev,
76462306a36Sopenharmony_ci			"Failed to enable MSI-X (err=%d)", err);
76562306a36Sopenharmony_ci		return err;
76662306a36Sopenharmony_ci	}
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	/*
76962306a36Sopenharmony_ci	 * Copy MSI-X vector information into tsi721 private structure
77062306a36Sopenharmony_ci	 */
77162306a36Sopenharmony_ci	priv->msix[TSI721_VECT_IDB].vector = entries[TSI721_VECT_IDB].vector;
77262306a36Sopenharmony_ci	snprintf(priv->msix[TSI721_VECT_IDB].irq_name, IRQ_DEVICE_NAME_MAX,
77362306a36Sopenharmony_ci		 DRV_NAME "-idb@pci:%s", pci_name(priv->pdev));
77462306a36Sopenharmony_ci	priv->msix[TSI721_VECT_PWRX].vector = entries[TSI721_VECT_PWRX].vector;
77562306a36Sopenharmony_ci	snprintf(priv->msix[TSI721_VECT_PWRX].irq_name, IRQ_DEVICE_NAME_MAX,
77662306a36Sopenharmony_ci		 DRV_NAME "-pwrx@pci:%s", pci_name(priv->pdev));
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	for (i = 0; i < RIO_MAX_MBOX; i++) {
77962306a36Sopenharmony_ci		priv->msix[TSI721_VECT_IMB0_RCV + i].vector =
78062306a36Sopenharmony_ci				entries[TSI721_VECT_IMB0_RCV + i].vector;
78162306a36Sopenharmony_ci		snprintf(priv->msix[TSI721_VECT_IMB0_RCV + i].irq_name,
78262306a36Sopenharmony_ci			 IRQ_DEVICE_NAME_MAX, DRV_NAME "-imbr%d@pci:%s",
78362306a36Sopenharmony_ci			 i, pci_name(priv->pdev));
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci		priv->msix[TSI721_VECT_IMB0_INT + i].vector =
78662306a36Sopenharmony_ci				entries[TSI721_VECT_IMB0_INT + i].vector;
78762306a36Sopenharmony_ci		snprintf(priv->msix[TSI721_VECT_IMB0_INT + i].irq_name,
78862306a36Sopenharmony_ci			 IRQ_DEVICE_NAME_MAX, DRV_NAME "-imbi%d@pci:%s",
78962306a36Sopenharmony_ci			 i, pci_name(priv->pdev));
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci		priv->msix[TSI721_VECT_OMB0_DONE + i].vector =
79262306a36Sopenharmony_ci				entries[TSI721_VECT_OMB0_DONE + i].vector;
79362306a36Sopenharmony_ci		snprintf(priv->msix[TSI721_VECT_OMB0_DONE + i].irq_name,
79462306a36Sopenharmony_ci			 IRQ_DEVICE_NAME_MAX, DRV_NAME "-ombd%d@pci:%s",
79562306a36Sopenharmony_ci			 i, pci_name(priv->pdev));
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci		priv->msix[TSI721_VECT_OMB0_INT + i].vector =
79862306a36Sopenharmony_ci				entries[TSI721_VECT_OMB0_INT + i].vector;
79962306a36Sopenharmony_ci		snprintf(priv->msix[TSI721_VECT_OMB0_INT + i].irq_name,
80062306a36Sopenharmony_ci			 IRQ_DEVICE_NAME_MAX, DRV_NAME "-ombi%d@pci:%s",
80162306a36Sopenharmony_ci			 i, pci_name(priv->pdev));
80262306a36Sopenharmony_ci	}
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci#ifdef CONFIG_RAPIDIO_DMA_ENGINE
80562306a36Sopenharmony_ci	for (i = 0; i < TSI721_DMA_CHNUM; i++) {
80662306a36Sopenharmony_ci		priv->msix[TSI721_VECT_DMA0_DONE + i].vector =
80762306a36Sopenharmony_ci				entries[TSI721_VECT_DMA0_DONE + i].vector;
80862306a36Sopenharmony_ci		snprintf(priv->msix[TSI721_VECT_DMA0_DONE + i].irq_name,
80962306a36Sopenharmony_ci			 IRQ_DEVICE_NAME_MAX, DRV_NAME "-dmad%d@pci:%s",
81062306a36Sopenharmony_ci			 i, pci_name(priv->pdev));
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci		priv->msix[TSI721_VECT_DMA0_INT + i].vector =
81362306a36Sopenharmony_ci				entries[TSI721_VECT_DMA0_INT + i].vector;
81462306a36Sopenharmony_ci		snprintf(priv->msix[TSI721_VECT_DMA0_INT + i].irq_name,
81562306a36Sopenharmony_ci			 IRQ_DEVICE_NAME_MAX, DRV_NAME "-dmai%d@pci:%s",
81662306a36Sopenharmony_ci			 i, pci_name(priv->pdev));
81762306a36Sopenharmony_ci	}
81862306a36Sopenharmony_ci#endif /* CONFIG_RAPIDIO_DMA_ENGINE */
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	return 0;
82162306a36Sopenharmony_ci}
82262306a36Sopenharmony_ci#endif /* CONFIG_PCI_MSI */
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_cistatic int tsi721_request_irq(struct tsi721_device *priv)
82562306a36Sopenharmony_ci{
82662306a36Sopenharmony_ci	int err;
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci#ifdef CONFIG_PCI_MSI
82962306a36Sopenharmony_ci	if (priv->flags & TSI721_USING_MSIX)
83062306a36Sopenharmony_ci		err = tsi721_request_msix(priv);
83162306a36Sopenharmony_ci	else
83262306a36Sopenharmony_ci#endif
83362306a36Sopenharmony_ci		err = request_irq(priv->pdev->irq, tsi721_irqhandler,
83462306a36Sopenharmony_ci			  (priv->flags & TSI721_USING_MSI) ? 0 : IRQF_SHARED,
83562306a36Sopenharmony_ci			  DRV_NAME, (void *)priv);
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	if (err)
83862306a36Sopenharmony_ci		tsi_err(&priv->pdev->dev,
83962306a36Sopenharmony_ci			"Unable to allocate interrupt, err=%d", err);
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	return err;
84262306a36Sopenharmony_ci}
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_cistatic void tsi721_free_irq(struct tsi721_device *priv)
84562306a36Sopenharmony_ci{
84662306a36Sopenharmony_ci#ifdef CONFIG_PCI_MSI
84762306a36Sopenharmony_ci	if (priv->flags & TSI721_USING_MSIX) {
84862306a36Sopenharmony_ci		free_irq(priv->msix[TSI721_VECT_IDB].vector, (void *)priv);
84962306a36Sopenharmony_ci		free_irq(priv->msix[TSI721_VECT_PWRX].vector, (void *)priv);
85062306a36Sopenharmony_ci	} else
85162306a36Sopenharmony_ci#endif
85262306a36Sopenharmony_ci	free_irq(priv->pdev->irq, (void *)priv);
85362306a36Sopenharmony_ci}
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_cistatic int
85662306a36Sopenharmony_citsi721_obw_alloc(struct tsi721_device *priv, struct tsi721_obw_bar *pbar,
85762306a36Sopenharmony_ci		 u32 size, int *win_id)
85862306a36Sopenharmony_ci{
85962306a36Sopenharmony_ci	u64 win_base;
86062306a36Sopenharmony_ci	u64 bar_base;
86162306a36Sopenharmony_ci	u64 bar_end;
86262306a36Sopenharmony_ci	u32 align;
86362306a36Sopenharmony_ci	struct tsi721_ob_win *win;
86462306a36Sopenharmony_ci	struct tsi721_ob_win *new_win = NULL;
86562306a36Sopenharmony_ci	int new_win_idx = -1;
86662306a36Sopenharmony_ci	int i = 0;
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	bar_base = pbar->base;
86962306a36Sopenharmony_ci	bar_end =  bar_base + pbar->size;
87062306a36Sopenharmony_ci	win_base = bar_base;
87162306a36Sopenharmony_ci	align = size/TSI721_PC2SR_ZONES;
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	while (i < TSI721_IBWIN_NUM) {
87462306a36Sopenharmony_ci		for (i = 0; i < TSI721_IBWIN_NUM; i++) {
87562306a36Sopenharmony_ci			if (!priv->ob_win[i].active) {
87662306a36Sopenharmony_ci				if (new_win == NULL) {
87762306a36Sopenharmony_ci					new_win = &priv->ob_win[i];
87862306a36Sopenharmony_ci					new_win_idx = i;
87962306a36Sopenharmony_ci				}
88062306a36Sopenharmony_ci				continue;
88162306a36Sopenharmony_ci			}
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci			/*
88462306a36Sopenharmony_ci			 * If this window belongs to the current BAR check it
88562306a36Sopenharmony_ci			 * for overlap
88662306a36Sopenharmony_ci			 */
88762306a36Sopenharmony_ci			win = &priv->ob_win[i];
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci			if (win->base >= bar_base && win->base < bar_end) {
89062306a36Sopenharmony_ci				if (win_base < (win->base + win->size) &&
89162306a36Sopenharmony_ci						(win_base + size) > win->base) {
89262306a36Sopenharmony_ci					/* Overlap detected */
89362306a36Sopenharmony_ci					win_base = win->base + win->size;
89462306a36Sopenharmony_ci					win_base = ALIGN(win_base, align);
89562306a36Sopenharmony_ci					break;
89662306a36Sopenharmony_ci				}
89762306a36Sopenharmony_ci			}
89862306a36Sopenharmony_ci		}
89962306a36Sopenharmony_ci	}
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	if (win_base + size > bar_end)
90262306a36Sopenharmony_ci		return -ENOMEM;
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	if (!new_win) {
90562306a36Sopenharmony_ci		tsi_err(&priv->pdev->dev, "OBW count tracking failed");
90662306a36Sopenharmony_ci		return -EIO;
90762306a36Sopenharmony_ci	}
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	new_win->active = true;
91062306a36Sopenharmony_ci	new_win->base = win_base;
91162306a36Sopenharmony_ci	new_win->size = size;
91262306a36Sopenharmony_ci	new_win->pbar = pbar;
91362306a36Sopenharmony_ci	priv->obwin_cnt--;
91462306a36Sopenharmony_ci	pbar->free -= size;
91562306a36Sopenharmony_ci	*win_id = new_win_idx;
91662306a36Sopenharmony_ci	return 0;
91762306a36Sopenharmony_ci}
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_cistatic int tsi721_map_outb_win(struct rio_mport *mport, u16 destid, u64 rstart,
92062306a36Sopenharmony_ci			u32 size, u32 flags, dma_addr_t *laddr)
92162306a36Sopenharmony_ci{
92262306a36Sopenharmony_ci	struct tsi721_device *priv = mport->priv;
92362306a36Sopenharmony_ci	int i;
92462306a36Sopenharmony_ci	struct tsi721_obw_bar *pbar;
92562306a36Sopenharmony_ci	struct tsi721_ob_win *ob_win;
92662306a36Sopenharmony_ci	int obw = -1;
92762306a36Sopenharmony_ci	u32 rval;
92862306a36Sopenharmony_ci	u64 rio_addr;
92962306a36Sopenharmony_ci	u32 zsize;
93062306a36Sopenharmony_ci	int ret = -ENOMEM;
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	tsi_debug(OBW, &priv->pdev->dev,
93362306a36Sopenharmony_ci		  "did=%d ra=0x%llx sz=0x%x", destid, rstart, size);
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	if (!is_power_of_2(size) || (size < 0x8000) || (rstart & (size - 1)))
93662306a36Sopenharmony_ci		return -EINVAL;
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	if (priv->obwin_cnt == 0)
93962306a36Sopenharmony_ci		return -EBUSY;
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	for (i = 0; i < 2; i++) {
94262306a36Sopenharmony_ci		if (priv->p2r_bar[i].free >= size) {
94362306a36Sopenharmony_ci			pbar = &priv->p2r_bar[i];
94462306a36Sopenharmony_ci			ret = tsi721_obw_alloc(priv, pbar, size, &obw);
94562306a36Sopenharmony_ci			if (!ret)
94662306a36Sopenharmony_ci				break;
94762306a36Sopenharmony_ci		}
94862306a36Sopenharmony_ci	}
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	if (ret)
95162306a36Sopenharmony_ci		return ret;
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	WARN_ON(obw == -1);
95462306a36Sopenharmony_ci	ob_win = &priv->ob_win[obw];
95562306a36Sopenharmony_ci	ob_win->destid = destid;
95662306a36Sopenharmony_ci	ob_win->rstart = rstart;
95762306a36Sopenharmony_ci	tsi_debug(OBW, &priv->pdev->dev,
95862306a36Sopenharmony_ci		  "allocated OBW%d @%llx", obw, ob_win->base);
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	/*
96162306a36Sopenharmony_ci	 * Configure Outbound Window
96262306a36Sopenharmony_ci	 */
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	zsize = size/TSI721_PC2SR_ZONES;
96562306a36Sopenharmony_ci	rio_addr = rstart;
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	/*
96862306a36Sopenharmony_ci	 * Program Address Translation Zones:
96962306a36Sopenharmony_ci	 *  This implementation uses all 8 zones associated wit window.
97062306a36Sopenharmony_ci	 */
97162306a36Sopenharmony_ci	for (i = 0; i < TSI721_PC2SR_ZONES; i++) {
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci		while (ioread32(priv->regs + TSI721_ZONE_SEL) &
97462306a36Sopenharmony_ci			TSI721_ZONE_SEL_GO) {
97562306a36Sopenharmony_ci			udelay(1);
97662306a36Sopenharmony_ci		}
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci		rval = (u32)(rio_addr & TSI721_LUT_DATA0_ADD) |
97962306a36Sopenharmony_ci			TSI721_LUT_DATA0_NREAD | TSI721_LUT_DATA0_NWR;
98062306a36Sopenharmony_ci		iowrite32(rval, priv->regs + TSI721_LUT_DATA0);
98162306a36Sopenharmony_ci		rval = (u32)(rio_addr >> 32);
98262306a36Sopenharmony_ci		iowrite32(rval, priv->regs + TSI721_LUT_DATA1);
98362306a36Sopenharmony_ci		rval = destid;
98462306a36Sopenharmony_ci		iowrite32(rval, priv->regs + TSI721_LUT_DATA2);
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci		rval = TSI721_ZONE_SEL_GO | (obw << 3) | i;
98762306a36Sopenharmony_ci		iowrite32(rval, priv->regs + TSI721_ZONE_SEL);
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci		rio_addr += zsize;
99062306a36Sopenharmony_ci	}
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	iowrite32(TSI721_OBWIN_SIZE(size) << 8,
99362306a36Sopenharmony_ci		  priv->regs + TSI721_OBWINSZ(obw));
99462306a36Sopenharmony_ci	iowrite32((u32)(ob_win->base >> 32), priv->regs + TSI721_OBWINUB(obw));
99562306a36Sopenharmony_ci	iowrite32((u32)(ob_win->base & TSI721_OBWINLB_BA) | TSI721_OBWINLB_WEN,
99662306a36Sopenharmony_ci		  priv->regs + TSI721_OBWINLB(obw));
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	*laddr = ob_win->base;
99962306a36Sopenharmony_ci	return 0;
100062306a36Sopenharmony_ci}
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_cistatic void tsi721_unmap_outb_win(struct rio_mport *mport,
100362306a36Sopenharmony_ci				  u16 destid, u64 rstart)
100462306a36Sopenharmony_ci{
100562306a36Sopenharmony_ci	struct tsi721_device *priv = mport->priv;
100662306a36Sopenharmony_ci	struct tsi721_ob_win *ob_win;
100762306a36Sopenharmony_ci	int i;
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	tsi_debug(OBW, &priv->pdev->dev, "did=%d ra=0x%llx", destid, rstart);
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci	for (i = 0; i < TSI721_OBWIN_NUM; i++) {
101262306a36Sopenharmony_ci		ob_win = &priv->ob_win[i];
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci		if (ob_win->active &&
101562306a36Sopenharmony_ci		    ob_win->destid == destid && ob_win->rstart == rstart) {
101662306a36Sopenharmony_ci			tsi_debug(OBW, &priv->pdev->dev,
101762306a36Sopenharmony_ci				  "free OBW%d @%llx", i, ob_win->base);
101862306a36Sopenharmony_ci			ob_win->active = false;
101962306a36Sopenharmony_ci			iowrite32(0, priv->regs + TSI721_OBWINLB(i));
102062306a36Sopenharmony_ci			ob_win->pbar->free += ob_win->size;
102162306a36Sopenharmony_ci			priv->obwin_cnt++;
102262306a36Sopenharmony_ci			break;
102362306a36Sopenharmony_ci		}
102462306a36Sopenharmony_ci	}
102562306a36Sopenharmony_ci}
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci/**
102862306a36Sopenharmony_ci * tsi721_init_pc2sr_mapping - initializes outbound (PCIe->SRIO)
102962306a36Sopenharmony_ci * translation regions.
103062306a36Sopenharmony_ci * @priv: pointer to tsi721 private data
103162306a36Sopenharmony_ci *
103262306a36Sopenharmony_ci * Disables SREP translation regions.
103362306a36Sopenharmony_ci */
103462306a36Sopenharmony_cistatic void tsi721_init_pc2sr_mapping(struct tsi721_device *priv)
103562306a36Sopenharmony_ci{
103662306a36Sopenharmony_ci	int i, z;
103762306a36Sopenharmony_ci	u32 rval;
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	/* Disable all PC2SR translation windows */
104062306a36Sopenharmony_ci	for (i = 0; i < TSI721_OBWIN_NUM; i++)
104162306a36Sopenharmony_ci		iowrite32(0, priv->regs + TSI721_OBWINLB(i));
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	/* Initialize zone lookup tables to avoid ECC errors on reads */
104462306a36Sopenharmony_ci	iowrite32(0, priv->regs + TSI721_LUT_DATA0);
104562306a36Sopenharmony_ci	iowrite32(0, priv->regs + TSI721_LUT_DATA1);
104662306a36Sopenharmony_ci	iowrite32(0, priv->regs + TSI721_LUT_DATA2);
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci	for (i = 0; i < TSI721_OBWIN_NUM; i++) {
104962306a36Sopenharmony_ci		for (z = 0; z < TSI721_PC2SR_ZONES; z++) {
105062306a36Sopenharmony_ci			while (ioread32(priv->regs + TSI721_ZONE_SEL) &
105162306a36Sopenharmony_ci				TSI721_ZONE_SEL_GO) {
105262306a36Sopenharmony_ci				udelay(1);
105362306a36Sopenharmony_ci			}
105462306a36Sopenharmony_ci			rval = TSI721_ZONE_SEL_GO | (i << 3) | z;
105562306a36Sopenharmony_ci			iowrite32(rval, priv->regs + TSI721_ZONE_SEL);
105662306a36Sopenharmony_ci		}
105762306a36Sopenharmony_ci	}
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	if (priv->p2r_bar[0].size == 0 && priv->p2r_bar[1].size == 0) {
106062306a36Sopenharmony_ci		priv->obwin_cnt = 0;
106162306a36Sopenharmony_ci		return;
106262306a36Sopenharmony_ci	}
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	priv->p2r_bar[0].free = priv->p2r_bar[0].size;
106562306a36Sopenharmony_ci	priv->p2r_bar[1].free = priv->p2r_bar[1].size;
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	for (i = 0; i < TSI721_OBWIN_NUM; i++)
106862306a36Sopenharmony_ci		priv->ob_win[i].active = false;
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	priv->obwin_cnt = TSI721_OBWIN_NUM;
107162306a36Sopenharmony_ci}
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci/**
107462306a36Sopenharmony_ci * tsi721_rio_map_inb_mem -- Mapping inbound memory region.
107562306a36Sopenharmony_ci * @mport: RapidIO master port
107662306a36Sopenharmony_ci * @lstart: Local memory space start address.
107762306a36Sopenharmony_ci * @rstart: RapidIO space start address.
107862306a36Sopenharmony_ci * @size: The mapping region size.
107962306a36Sopenharmony_ci * @flags: Flags for mapping. 0 for using default flags.
108062306a36Sopenharmony_ci *
108162306a36Sopenharmony_ci * Return: 0 -- Success.
108262306a36Sopenharmony_ci *
108362306a36Sopenharmony_ci * This function will create the inbound mapping
108462306a36Sopenharmony_ci * from rstart to lstart.
108562306a36Sopenharmony_ci */
108662306a36Sopenharmony_cistatic int tsi721_rio_map_inb_mem(struct rio_mport *mport, dma_addr_t lstart,
108762306a36Sopenharmony_ci		u64 rstart, u64 size, u32 flags)
108862306a36Sopenharmony_ci{
108962306a36Sopenharmony_ci	struct tsi721_device *priv = mport->priv;
109062306a36Sopenharmony_ci	int i, avail = -1;
109162306a36Sopenharmony_ci	u32 regval;
109262306a36Sopenharmony_ci	struct tsi721_ib_win *ib_win;
109362306a36Sopenharmony_ci	bool direct = (lstart == rstart);
109462306a36Sopenharmony_ci	u64 ibw_size;
109562306a36Sopenharmony_ci	dma_addr_t loc_start;
109662306a36Sopenharmony_ci	u64 ibw_start;
109762306a36Sopenharmony_ci	struct tsi721_ib_win_mapping *map = NULL;
109862306a36Sopenharmony_ci	int ret = -EBUSY;
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci	/* Max IBW size supported by HW is 16GB */
110162306a36Sopenharmony_ci	if (size > 0x400000000UL)
110262306a36Sopenharmony_ci		return -EINVAL;
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci	if (direct) {
110562306a36Sopenharmony_ci		/* Calculate minimal acceptable window size and base address */
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci		ibw_size = roundup_pow_of_two(size);
110862306a36Sopenharmony_ci		ibw_start = lstart & ~(ibw_size - 1);
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci		tsi_debug(IBW, &priv->pdev->dev,
111162306a36Sopenharmony_ci			"Direct (RIO_0x%llx -> PCIe_%pad), size=0x%llx, ibw_start = 0x%llx",
111262306a36Sopenharmony_ci			rstart, &lstart, size, ibw_start);
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci		while ((lstart + size) > (ibw_start + ibw_size)) {
111562306a36Sopenharmony_ci			ibw_size *= 2;
111662306a36Sopenharmony_ci			ibw_start = lstart & ~(ibw_size - 1);
111762306a36Sopenharmony_ci			/* Check for crossing IBW max size 16GB */
111862306a36Sopenharmony_ci			if (ibw_size > 0x400000000UL)
111962306a36Sopenharmony_ci				return -EBUSY;
112062306a36Sopenharmony_ci		}
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci		loc_start = ibw_start;
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci		map = kzalloc(sizeof(struct tsi721_ib_win_mapping), GFP_ATOMIC);
112562306a36Sopenharmony_ci		if (map == NULL)
112662306a36Sopenharmony_ci			return -ENOMEM;
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	} else {
112962306a36Sopenharmony_ci		tsi_debug(IBW, &priv->pdev->dev,
113062306a36Sopenharmony_ci			"Translated (RIO_0x%llx -> PCIe_%pad), size=0x%llx",
113162306a36Sopenharmony_ci			rstart, &lstart, size);
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci		if (!is_power_of_2(size) || size < 0x1000 ||
113462306a36Sopenharmony_ci		    ((u64)lstart & (size - 1)) || (rstart & (size - 1)))
113562306a36Sopenharmony_ci			return -EINVAL;
113662306a36Sopenharmony_ci		if (priv->ibwin_cnt == 0)
113762306a36Sopenharmony_ci			return -EBUSY;
113862306a36Sopenharmony_ci		ibw_start = rstart;
113962306a36Sopenharmony_ci		ibw_size = size;
114062306a36Sopenharmony_ci		loc_start = lstart;
114162306a36Sopenharmony_ci	}
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	/*
114462306a36Sopenharmony_ci	 * Scan for overlapping with active regions and mark the first available
114562306a36Sopenharmony_ci	 * IB window at the same time.
114662306a36Sopenharmony_ci	 */
114762306a36Sopenharmony_ci	for (i = 0; i < TSI721_IBWIN_NUM; i++) {
114862306a36Sopenharmony_ci		ib_win = &priv->ib_win[i];
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci		if (!ib_win->active) {
115162306a36Sopenharmony_ci			if (avail == -1) {
115262306a36Sopenharmony_ci				avail = i;
115362306a36Sopenharmony_ci				ret = 0;
115462306a36Sopenharmony_ci			}
115562306a36Sopenharmony_ci		} else if (ibw_start < (ib_win->rstart + ib_win->size) &&
115662306a36Sopenharmony_ci			   (ibw_start + ibw_size) > ib_win->rstart) {
115762306a36Sopenharmony_ci			/* Return error if address translation involved */
115862306a36Sopenharmony_ci			if (!direct || ib_win->xlat) {
115962306a36Sopenharmony_ci				ret = -EFAULT;
116062306a36Sopenharmony_ci				break;
116162306a36Sopenharmony_ci			}
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci			/*
116462306a36Sopenharmony_ci			 * Direct mappings usually are larger than originally
116562306a36Sopenharmony_ci			 * requested fragments - check if this new request fits
116662306a36Sopenharmony_ci			 * into it.
116762306a36Sopenharmony_ci			 */
116862306a36Sopenharmony_ci			if (rstart >= ib_win->rstart &&
116962306a36Sopenharmony_ci			    (rstart + size) <= (ib_win->rstart +
117062306a36Sopenharmony_ci							ib_win->size)) {
117162306a36Sopenharmony_ci				/* We are in - no further mapping required */
117262306a36Sopenharmony_ci				map->lstart = lstart;
117362306a36Sopenharmony_ci				list_add_tail(&map->node, &ib_win->mappings);
117462306a36Sopenharmony_ci				return 0;
117562306a36Sopenharmony_ci			}
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci			ret = -EFAULT;
117862306a36Sopenharmony_ci			break;
117962306a36Sopenharmony_ci		}
118062306a36Sopenharmony_ci	}
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci	if (ret)
118362306a36Sopenharmony_ci		goto out;
118462306a36Sopenharmony_ci	i = avail;
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci	/* Sanity check: available IB window must be disabled at this point */
118762306a36Sopenharmony_ci	regval = ioread32(priv->regs + TSI721_IBWIN_LB(i));
118862306a36Sopenharmony_ci	if (WARN_ON(regval & TSI721_IBWIN_LB_WEN)) {
118962306a36Sopenharmony_ci		ret = -EIO;
119062306a36Sopenharmony_ci		goto out;
119162306a36Sopenharmony_ci	}
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	ib_win = &priv->ib_win[i];
119462306a36Sopenharmony_ci	ib_win->active = true;
119562306a36Sopenharmony_ci	ib_win->rstart = ibw_start;
119662306a36Sopenharmony_ci	ib_win->lstart = loc_start;
119762306a36Sopenharmony_ci	ib_win->size = ibw_size;
119862306a36Sopenharmony_ci	ib_win->xlat = (lstart != rstart);
119962306a36Sopenharmony_ci	INIT_LIST_HEAD(&ib_win->mappings);
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	/*
120262306a36Sopenharmony_ci	 * When using direct IBW mapping and have larger than requested IBW size
120362306a36Sopenharmony_ci	 * we can have multiple local memory blocks mapped through the same IBW
120462306a36Sopenharmony_ci	 * To handle this situation we maintain list of "clients" for such IBWs.
120562306a36Sopenharmony_ci	 */
120662306a36Sopenharmony_ci	if (direct) {
120762306a36Sopenharmony_ci		map->lstart = lstart;
120862306a36Sopenharmony_ci		list_add_tail(&map->node, &ib_win->mappings);
120962306a36Sopenharmony_ci	}
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	iowrite32(TSI721_IBWIN_SIZE(ibw_size) << 8,
121262306a36Sopenharmony_ci			priv->regs + TSI721_IBWIN_SZ(i));
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci	iowrite32(((u64)loc_start >> 32), priv->regs + TSI721_IBWIN_TUA(i));
121562306a36Sopenharmony_ci	iowrite32(((u64)loc_start & TSI721_IBWIN_TLA_ADD),
121662306a36Sopenharmony_ci		  priv->regs + TSI721_IBWIN_TLA(i));
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci	iowrite32(ibw_start >> 32, priv->regs + TSI721_IBWIN_UB(i));
121962306a36Sopenharmony_ci	iowrite32((ibw_start & TSI721_IBWIN_LB_BA) | TSI721_IBWIN_LB_WEN,
122062306a36Sopenharmony_ci		priv->regs + TSI721_IBWIN_LB(i));
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	priv->ibwin_cnt--;
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	tsi_debug(IBW, &priv->pdev->dev,
122562306a36Sopenharmony_ci		"Configured IBWIN%d (RIO_0x%llx -> PCIe_%pad), size=0x%llx",
122662306a36Sopenharmony_ci		i, ibw_start, &loc_start, ibw_size);
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci	return 0;
122962306a36Sopenharmony_ciout:
123062306a36Sopenharmony_ci	kfree(map);
123162306a36Sopenharmony_ci	return ret;
123262306a36Sopenharmony_ci}
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_ci/**
123562306a36Sopenharmony_ci * tsi721_rio_unmap_inb_mem -- Unmapping inbound memory region.
123662306a36Sopenharmony_ci * @mport: RapidIO master port
123762306a36Sopenharmony_ci * @lstart: Local memory space start address.
123862306a36Sopenharmony_ci */
123962306a36Sopenharmony_cistatic void tsi721_rio_unmap_inb_mem(struct rio_mport *mport,
124062306a36Sopenharmony_ci				dma_addr_t lstart)
124162306a36Sopenharmony_ci{
124262306a36Sopenharmony_ci	struct tsi721_device *priv = mport->priv;
124362306a36Sopenharmony_ci	struct tsi721_ib_win *ib_win;
124462306a36Sopenharmony_ci	int i;
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci	tsi_debug(IBW, &priv->pdev->dev,
124762306a36Sopenharmony_ci		"Unmap IBW mapped to PCIe_%pad", &lstart);
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci	/* Search for matching active inbound translation window */
125062306a36Sopenharmony_ci	for (i = 0; i < TSI721_IBWIN_NUM; i++) {
125162306a36Sopenharmony_ci		ib_win = &priv->ib_win[i];
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ci		/* Address translating IBWs must to be an exact march */
125462306a36Sopenharmony_ci		if (!ib_win->active ||
125562306a36Sopenharmony_ci		    (ib_win->xlat && lstart != ib_win->lstart))
125662306a36Sopenharmony_ci			continue;
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci		if (lstart >= ib_win->lstart &&
125962306a36Sopenharmony_ci		    lstart < (ib_win->lstart + ib_win->size)) {
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci			if (!ib_win->xlat) {
126262306a36Sopenharmony_ci				struct tsi721_ib_win_mapping *map;
126362306a36Sopenharmony_ci				int found = 0;
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci				list_for_each_entry(map,
126662306a36Sopenharmony_ci						    &ib_win->mappings, node) {
126762306a36Sopenharmony_ci					if (map->lstart == lstart) {
126862306a36Sopenharmony_ci						list_del(&map->node);
126962306a36Sopenharmony_ci						kfree(map);
127062306a36Sopenharmony_ci						found = 1;
127162306a36Sopenharmony_ci						break;
127262306a36Sopenharmony_ci					}
127362306a36Sopenharmony_ci				}
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_ci				if (!found)
127662306a36Sopenharmony_ci					continue;
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci				if (!list_empty(&ib_win->mappings))
127962306a36Sopenharmony_ci					break;
128062306a36Sopenharmony_ci			}
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci			tsi_debug(IBW, &priv->pdev->dev, "Disable IBWIN_%d", i);
128362306a36Sopenharmony_ci			iowrite32(0, priv->regs + TSI721_IBWIN_LB(i));
128462306a36Sopenharmony_ci			ib_win->active = false;
128562306a36Sopenharmony_ci			priv->ibwin_cnt++;
128662306a36Sopenharmony_ci			break;
128762306a36Sopenharmony_ci		}
128862306a36Sopenharmony_ci	}
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci	if (i == TSI721_IBWIN_NUM)
129162306a36Sopenharmony_ci		tsi_debug(IBW, &priv->pdev->dev,
129262306a36Sopenharmony_ci			"IB window mapped to %pad not found", &lstart);
129362306a36Sopenharmony_ci}
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci/**
129662306a36Sopenharmony_ci * tsi721_init_sr2pc_mapping - initializes inbound (SRIO->PCIe)
129762306a36Sopenharmony_ci * translation regions.
129862306a36Sopenharmony_ci * @priv: pointer to tsi721 private data
129962306a36Sopenharmony_ci *
130062306a36Sopenharmony_ci * Disables inbound windows.
130162306a36Sopenharmony_ci */
130262306a36Sopenharmony_cistatic void tsi721_init_sr2pc_mapping(struct tsi721_device *priv)
130362306a36Sopenharmony_ci{
130462306a36Sopenharmony_ci	int i;
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci	/* Disable all SR2PC inbound windows */
130762306a36Sopenharmony_ci	for (i = 0; i < TSI721_IBWIN_NUM; i++)
130862306a36Sopenharmony_ci		iowrite32(0, priv->regs + TSI721_IBWIN_LB(i));
130962306a36Sopenharmony_ci	priv->ibwin_cnt = TSI721_IBWIN_NUM;
131062306a36Sopenharmony_ci}
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci/*
131362306a36Sopenharmony_ci * tsi721_close_sr2pc_mapping - closes all active inbound (SRIO->PCIe)
131462306a36Sopenharmony_ci * translation regions.
131562306a36Sopenharmony_ci * @priv: pointer to tsi721 device private data
131662306a36Sopenharmony_ci */
131762306a36Sopenharmony_cistatic void tsi721_close_sr2pc_mapping(struct tsi721_device *priv)
131862306a36Sopenharmony_ci{
131962306a36Sopenharmony_ci	struct tsi721_ib_win *ib_win;
132062306a36Sopenharmony_ci	int i;
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_ci	/* Disable all active SR2PC inbound windows */
132362306a36Sopenharmony_ci	for (i = 0; i < TSI721_IBWIN_NUM; i++) {
132462306a36Sopenharmony_ci		ib_win = &priv->ib_win[i];
132562306a36Sopenharmony_ci		if (ib_win->active) {
132662306a36Sopenharmony_ci			iowrite32(0, priv->regs + TSI721_IBWIN_LB(i));
132762306a36Sopenharmony_ci			ib_win->active = false;
132862306a36Sopenharmony_ci		}
132962306a36Sopenharmony_ci	}
133062306a36Sopenharmony_ci}
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci/**
133362306a36Sopenharmony_ci * tsi721_port_write_init - Inbound port write interface init
133462306a36Sopenharmony_ci * @priv: pointer to tsi721 private data
133562306a36Sopenharmony_ci *
133662306a36Sopenharmony_ci * Initializes inbound port write handler.
133762306a36Sopenharmony_ci * Returns %0 on success or %-ENOMEM on failure.
133862306a36Sopenharmony_ci */
133962306a36Sopenharmony_cistatic int tsi721_port_write_init(struct tsi721_device *priv)
134062306a36Sopenharmony_ci{
134162306a36Sopenharmony_ci	priv->pw_discard_count = 0;
134262306a36Sopenharmony_ci	INIT_WORK(&priv->pw_work, tsi721_pw_dpc);
134362306a36Sopenharmony_ci	spin_lock_init(&priv->pw_fifo_lock);
134462306a36Sopenharmony_ci	if (kfifo_alloc(&priv->pw_fifo,
134562306a36Sopenharmony_ci			TSI721_RIO_PW_MSG_SIZE * 32, GFP_KERNEL)) {
134662306a36Sopenharmony_ci		tsi_err(&priv->pdev->dev, "PW FIFO allocation failed");
134762306a36Sopenharmony_ci		return -ENOMEM;
134862306a36Sopenharmony_ci	}
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci	/* Use reliable port-write capture mode */
135162306a36Sopenharmony_ci	iowrite32(TSI721_RIO_PW_CTL_PWC_REL, priv->regs + TSI721_RIO_PW_CTL);
135262306a36Sopenharmony_ci	return 0;
135362306a36Sopenharmony_ci}
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_cistatic void tsi721_port_write_free(struct tsi721_device *priv)
135662306a36Sopenharmony_ci{
135762306a36Sopenharmony_ci	kfifo_free(&priv->pw_fifo);
135862306a36Sopenharmony_ci}
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_cistatic int tsi721_doorbell_init(struct tsi721_device *priv)
136162306a36Sopenharmony_ci{
136262306a36Sopenharmony_ci	/* Outbound Doorbells do not require any setup.
136362306a36Sopenharmony_ci	 * Tsi721 uses dedicated PCI BAR1 to generate doorbells.
136462306a36Sopenharmony_ci	 * That BAR1 was mapped during the probe routine.
136562306a36Sopenharmony_ci	 */
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ci	/* Initialize Inbound Doorbell processing DPC and queue */
136862306a36Sopenharmony_ci	priv->db_discard_count = 0;
136962306a36Sopenharmony_ci	INIT_WORK(&priv->idb_work, tsi721_db_dpc);
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ci	/* Allocate buffer for inbound doorbells queue */
137262306a36Sopenharmony_ci	priv->idb_base = dma_alloc_coherent(&priv->pdev->dev,
137362306a36Sopenharmony_ci					    IDB_QSIZE * TSI721_IDB_ENTRY_SIZE,
137462306a36Sopenharmony_ci					    &priv->idb_dma, GFP_KERNEL);
137562306a36Sopenharmony_ci	if (!priv->idb_base)
137662306a36Sopenharmony_ci		return -ENOMEM;
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_ci	tsi_debug(DBELL, &priv->pdev->dev,
137962306a36Sopenharmony_ci		  "Allocated IDB buffer @ %p (phys = %pad)",
138062306a36Sopenharmony_ci		  priv->idb_base, &priv->idb_dma);
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci	iowrite32(TSI721_IDQ_SIZE_VAL(IDB_QSIZE),
138362306a36Sopenharmony_ci		priv->regs + TSI721_IDQ_SIZE(IDB_QUEUE));
138462306a36Sopenharmony_ci	iowrite32(((u64)priv->idb_dma >> 32),
138562306a36Sopenharmony_ci		priv->regs + TSI721_IDQ_BASEU(IDB_QUEUE));
138662306a36Sopenharmony_ci	iowrite32(((u64)priv->idb_dma & TSI721_IDQ_BASEL_ADDR),
138762306a36Sopenharmony_ci		priv->regs + TSI721_IDQ_BASEL(IDB_QUEUE));
138862306a36Sopenharmony_ci	/* Enable accepting all inbound doorbells */
138962306a36Sopenharmony_ci	iowrite32(0, priv->regs + TSI721_IDQ_MASK(IDB_QUEUE));
139062306a36Sopenharmony_ci
139162306a36Sopenharmony_ci	iowrite32(TSI721_IDQ_INIT, priv->regs + TSI721_IDQ_CTL(IDB_QUEUE));
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci	iowrite32(0, priv->regs + TSI721_IDQ_RP(IDB_QUEUE));
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci	return 0;
139662306a36Sopenharmony_ci}
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_cistatic void tsi721_doorbell_free(struct tsi721_device *priv)
139962306a36Sopenharmony_ci{
140062306a36Sopenharmony_ci	if (priv->idb_base == NULL)
140162306a36Sopenharmony_ci		return;
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci	/* Free buffer allocated for inbound doorbell queue */
140462306a36Sopenharmony_ci	dma_free_coherent(&priv->pdev->dev, IDB_QSIZE * TSI721_IDB_ENTRY_SIZE,
140562306a36Sopenharmony_ci			  priv->idb_base, priv->idb_dma);
140662306a36Sopenharmony_ci	priv->idb_base = NULL;
140762306a36Sopenharmony_ci}
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci/**
141062306a36Sopenharmony_ci * tsi721_bdma_maint_init - Initialize maintenance request BDMA channel.
141162306a36Sopenharmony_ci * @priv: pointer to tsi721 private data
141262306a36Sopenharmony_ci *
141362306a36Sopenharmony_ci * Initialize BDMA channel allocated for RapidIO maintenance read/write
141462306a36Sopenharmony_ci * request generation
141562306a36Sopenharmony_ci * Returns %0 on success or %-ENOMEM on failure.
141662306a36Sopenharmony_ci */
141762306a36Sopenharmony_cistatic int tsi721_bdma_maint_init(struct tsi721_device *priv)
141862306a36Sopenharmony_ci{
141962306a36Sopenharmony_ci	struct tsi721_dma_desc *bd_ptr;
142062306a36Sopenharmony_ci	u64		*sts_ptr;
142162306a36Sopenharmony_ci	dma_addr_t	bd_phys, sts_phys;
142262306a36Sopenharmony_ci	int		sts_size;
142362306a36Sopenharmony_ci	int		bd_num = 2;
142462306a36Sopenharmony_ci	void __iomem	*regs;
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_ci	tsi_debug(MAINT, &priv->pdev->dev,
142762306a36Sopenharmony_ci		  "Init BDMA_%d Maintenance requests", TSI721_DMACH_MAINT);
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci	/*
143062306a36Sopenharmony_ci	 * Initialize DMA channel for maintenance requests
143162306a36Sopenharmony_ci	 */
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_ci	priv->mdma.ch_id = TSI721_DMACH_MAINT;
143462306a36Sopenharmony_ci	regs = priv->regs + TSI721_DMAC_BASE(TSI721_DMACH_MAINT);
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_ci	/* Allocate space for DMA descriptors */
143762306a36Sopenharmony_ci	bd_ptr = dma_alloc_coherent(&priv->pdev->dev,
143862306a36Sopenharmony_ci				    bd_num * sizeof(struct tsi721_dma_desc),
143962306a36Sopenharmony_ci				    &bd_phys, GFP_KERNEL);
144062306a36Sopenharmony_ci	if (!bd_ptr)
144162306a36Sopenharmony_ci		return -ENOMEM;
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci	priv->mdma.bd_num = bd_num;
144462306a36Sopenharmony_ci	priv->mdma.bd_phys = bd_phys;
144562306a36Sopenharmony_ci	priv->mdma.bd_base = bd_ptr;
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci	tsi_debug(MAINT, &priv->pdev->dev, "DMA descriptors @ %p (phys = %pad)",
144862306a36Sopenharmony_ci		  bd_ptr, &bd_phys);
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci	/* Allocate space for descriptor status FIFO */
145162306a36Sopenharmony_ci	sts_size = (bd_num >= TSI721_DMA_MINSTSSZ) ?
145262306a36Sopenharmony_ci					bd_num : TSI721_DMA_MINSTSSZ;
145362306a36Sopenharmony_ci	sts_size = roundup_pow_of_two(sts_size);
145462306a36Sopenharmony_ci	sts_ptr = dma_alloc_coherent(&priv->pdev->dev,
145562306a36Sopenharmony_ci				     sts_size * sizeof(struct tsi721_dma_sts),
145662306a36Sopenharmony_ci				     &sts_phys, GFP_KERNEL);
145762306a36Sopenharmony_ci	if (!sts_ptr) {
145862306a36Sopenharmony_ci		/* Free space allocated for DMA descriptors */
145962306a36Sopenharmony_ci		dma_free_coherent(&priv->pdev->dev,
146062306a36Sopenharmony_ci				  bd_num * sizeof(struct tsi721_dma_desc),
146162306a36Sopenharmony_ci				  bd_ptr, bd_phys);
146262306a36Sopenharmony_ci		priv->mdma.bd_base = NULL;
146362306a36Sopenharmony_ci		return -ENOMEM;
146462306a36Sopenharmony_ci	}
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci	priv->mdma.sts_phys = sts_phys;
146762306a36Sopenharmony_ci	priv->mdma.sts_base = sts_ptr;
146862306a36Sopenharmony_ci	priv->mdma.sts_size = sts_size;
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci	tsi_debug(MAINT, &priv->pdev->dev,
147162306a36Sopenharmony_ci		"desc status FIFO @ %p (phys = %pad) size=0x%x",
147262306a36Sopenharmony_ci		sts_ptr, &sts_phys, sts_size);
147362306a36Sopenharmony_ci
147462306a36Sopenharmony_ci	/* Initialize DMA descriptors ring */
147562306a36Sopenharmony_ci	bd_ptr[bd_num - 1].type_id = cpu_to_le32(DTYPE3 << 29);
147662306a36Sopenharmony_ci	bd_ptr[bd_num - 1].next_lo = cpu_to_le32((u64)bd_phys &
147762306a36Sopenharmony_ci						 TSI721_DMAC_DPTRL_MASK);
147862306a36Sopenharmony_ci	bd_ptr[bd_num - 1].next_hi = cpu_to_le32((u64)bd_phys >> 32);
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci	/* Setup DMA descriptor pointers */
148162306a36Sopenharmony_ci	iowrite32(((u64)bd_phys >> 32),	regs + TSI721_DMAC_DPTRH);
148262306a36Sopenharmony_ci	iowrite32(((u64)bd_phys & TSI721_DMAC_DPTRL_MASK),
148362306a36Sopenharmony_ci		regs + TSI721_DMAC_DPTRL);
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ci	/* Setup descriptor status FIFO */
148662306a36Sopenharmony_ci	iowrite32(((u64)sts_phys >> 32), regs + TSI721_DMAC_DSBH);
148762306a36Sopenharmony_ci	iowrite32(((u64)sts_phys & TSI721_DMAC_DSBL_MASK),
148862306a36Sopenharmony_ci		regs + TSI721_DMAC_DSBL);
148962306a36Sopenharmony_ci	iowrite32(TSI721_DMAC_DSSZ_SIZE(sts_size),
149062306a36Sopenharmony_ci		regs + TSI721_DMAC_DSSZ);
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci	/* Clear interrupt bits */
149362306a36Sopenharmony_ci	iowrite32(TSI721_DMAC_INT_ALL, regs + TSI721_DMAC_INT);
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_ci	ioread32(regs + TSI721_DMAC_INT);
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_ci	/* Toggle DMA channel initialization */
149862306a36Sopenharmony_ci	iowrite32(TSI721_DMAC_CTL_INIT,	regs + TSI721_DMAC_CTL);
149962306a36Sopenharmony_ci	ioread32(regs + TSI721_DMAC_CTL);
150062306a36Sopenharmony_ci	udelay(10);
150162306a36Sopenharmony_ci
150262306a36Sopenharmony_ci	return 0;
150362306a36Sopenharmony_ci}
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_cistatic int tsi721_bdma_maint_free(struct tsi721_device *priv)
150662306a36Sopenharmony_ci{
150762306a36Sopenharmony_ci	u32 ch_stat;
150862306a36Sopenharmony_ci	struct tsi721_bdma_maint *mdma = &priv->mdma;
150962306a36Sopenharmony_ci	void __iomem *regs = priv->regs + TSI721_DMAC_BASE(mdma->ch_id);
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_ci	if (mdma->bd_base == NULL)
151262306a36Sopenharmony_ci		return 0;
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_ci	/* Check if DMA channel still running */
151562306a36Sopenharmony_ci	ch_stat = ioread32(regs + TSI721_DMAC_STS);
151662306a36Sopenharmony_ci	if (ch_stat & TSI721_DMAC_STS_RUN)
151762306a36Sopenharmony_ci		return -EFAULT;
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_ci	/* Put DMA channel into init state */
152062306a36Sopenharmony_ci	iowrite32(TSI721_DMAC_CTL_INIT,	regs + TSI721_DMAC_CTL);
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_ci	/* Free space allocated for DMA descriptors */
152362306a36Sopenharmony_ci	dma_free_coherent(&priv->pdev->dev,
152462306a36Sopenharmony_ci		mdma->bd_num * sizeof(struct tsi721_dma_desc),
152562306a36Sopenharmony_ci		mdma->bd_base, mdma->bd_phys);
152662306a36Sopenharmony_ci	mdma->bd_base = NULL;
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_ci	/* Free space allocated for status FIFO */
152962306a36Sopenharmony_ci	dma_free_coherent(&priv->pdev->dev,
153062306a36Sopenharmony_ci		mdma->sts_size * sizeof(struct tsi721_dma_sts),
153162306a36Sopenharmony_ci		mdma->sts_base, mdma->sts_phys);
153262306a36Sopenharmony_ci	mdma->sts_base = NULL;
153362306a36Sopenharmony_ci	return 0;
153462306a36Sopenharmony_ci}
153562306a36Sopenharmony_ci
153662306a36Sopenharmony_ci/* Enable Inbound Messaging Interrupts */
153762306a36Sopenharmony_cistatic void
153862306a36Sopenharmony_citsi721_imsg_interrupt_enable(struct tsi721_device *priv, int ch,
153962306a36Sopenharmony_ci				  u32 inte_mask)
154062306a36Sopenharmony_ci{
154162306a36Sopenharmony_ci	u32 rval;
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci	if (!inte_mask)
154462306a36Sopenharmony_ci		return;
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_ci	/* Clear pending Inbound Messaging interrupts */
154762306a36Sopenharmony_ci	iowrite32(inte_mask, priv->regs + TSI721_IBDMAC_INT(ch));
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci	/* Enable Inbound Messaging interrupts */
155062306a36Sopenharmony_ci	rval = ioread32(priv->regs + TSI721_IBDMAC_INTE(ch));
155162306a36Sopenharmony_ci	iowrite32(rval | inte_mask, priv->regs + TSI721_IBDMAC_INTE(ch));
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_ci	if (priv->flags & TSI721_USING_MSIX)
155462306a36Sopenharmony_ci		return; /* Finished if we are in MSI-X mode */
155562306a36Sopenharmony_ci
155662306a36Sopenharmony_ci	/*
155762306a36Sopenharmony_ci	 * For MSI and INTA interrupt signalling we need to enable next levels
155862306a36Sopenharmony_ci	 */
155962306a36Sopenharmony_ci
156062306a36Sopenharmony_ci	/* Enable Device Channel Interrupt */
156162306a36Sopenharmony_ci	rval = ioread32(priv->regs + TSI721_DEV_CHAN_INTE);
156262306a36Sopenharmony_ci	iowrite32(rval | TSI721_INT_IMSG_CHAN(ch),
156362306a36Sopenharmony_ci		  priv->regs + TSI721_DEV_CHAN_INTE);
156462306a36Sopenharmony_ci}
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ci/* Disable Inbound Messaging Interrupts */
156762306a36Sopenharmony_cistatic void
156862306a36Sopenharmony_citsi721_imsg_interrupt_disable(struct tsi721_device *priv, int ch,
156962306a36Sopenharmony_ci				   u32 inte_mask)
157062306a36Sopenharmony_ci{
157162306a36Sopenharmony_ci	u32 rval;
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_ci	if (!inte_mask)
157462306a36Sopenharmony_ci		return;
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_ci	/* Clear pending Inbound Messaging interrupts */
157762306a36Sopenharmony_ci	iowrite32(inte_mask, priv->regs + TSI721_IBDMAC_INT(ch));
157862306a36Sopenharmony_ci
157962306a36Sopenharmony_ci	/* Disable Inbound Messaging interrupts */
158062306a36Sopenharmony_ci	rval = ioread32(priv->regs + TSI721_IBDMAC_INTE(ch));
158162306a36Sopenharmony_ci	rval &= ~inte_mask;
158262306a36Sopenharmony_ci	iowrite32(rval, priv->regs + TSI721_IBDMAC_INTE(ch));
158362306a36Sopenharmony_ci
158462306a36Sopenharmony_ci	if (priv->flags & TSI721_USING_MSIX)
158562306a36Sopenharmony_ci		return; /* Finished if we are in MSI-X mode */
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_ci	/*
158862306a36Sopenharmony_ci	 * For MSI and INTA interrupt signalling we need to disable next levels
158962306a36Sopenharmony_ci	 */
159062306a36Sopenharmony_ci
159162306a36Sopenharmony_ci	/* Disable Device Channel Interrupt */
159262306a36Sopenharmony_ci	rval = ioread32(priv->regs + TSI721_DEV_CHAN_INTE);
159362306a36Sopenharmony_ci	rval &= ~TSI721_INT_IMSG_CHAN(ch);
159462306a36Sopenharmony_ci	iowrite32(rval, priv->regs + TSI721_DEV_CHAN_INTE);
159562306a36Sopenharmony_ci}
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_ci/* Enable Outbound Messaging interrupts */
159862306a36Sopenharmony_cistatic void
159962306a36Sopenharmony_citsi721_omsg_interrupt_enable(struct tsi721_device *priv, int ch,
160062306a36Sopenharmony_ci				  u32 inte_mask)
160162306a36Sopenharmony_ci{
160262306a36Sopenharmony_ci	u32 rval;
160362306a36Sopenharmony_ci
160462306a36Sopenharmony_ci	if (!inte_mask)
160562306a36Sopenharmony_ci		return;
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ci	/* Clear pending Outbound Messaging interrupts */
160862306a36Sopenharmony_ci	iowrite32(inte_mask, priv->regs + TSI721_OBDMAC_INT(ch));
160962306a36Sopenharmony_ci
161062306a36Sopenharmony_ci	/* Enable Outbound Messaging channel interrupts */
161162306a36Sopenharmony_ci	rval = ioread32(priv->regs + TSI721_OBDMAC_INTE(ch));
161262306a36Sopenharmony_ci	iowrite32(rval | inte_mask, priv->regs + TSI721_OBDMAC_INTE(ch));
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_ci	if (priv->flags & TSI721_USING_MSIX)
161562306a36Sopenharmony_ci		return; /* Finished if we are in MSI-X mode */
161662306a36Sopenharmony_ci
161762306a36Sopenharmony_ci	/*
161862306a36Sopenharmony_ci	 * For MSI and INTA interrupt signalling we need to enable next levels
161962306a36Sopenharmony_ci	 */
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci	/* Enable Device Channel Interrupt */
162262306a36Sopenharmony_ci	rval = ioread32(priv->regs + TSI721_DEV_CHAN_INTE);
162362306a36Sopenharmony_ci	iowrite32(rval | TSI721_INT_OMSG_CHAN(ch),
162462306a36Sopenharmony_ci		  priv->regs + TSI721_DEV_CHAN_INTE);
162562306a36Sopenharmony_ci}
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_ci/* Disable Outbound Messaging interrupts */
162862306a36Sopenharmony_cistatic void
162962306a36Sopenharmony_citsi721_omsg_interrupt_disable(struct tsi721_device *priv, int ch,
163062306a36Sopenharmony_ci				   u32 inte_mask)
163162306a36Sopenharmony_ci{
163262306a36Sopenharmony_ci	u32 rval;
163362306a36Sopenharmony_ci
163462306a36Sopenharmony_ci	if (!inte_mask)
163562306a36Sopenharmony_ci		return;
163662306a36Sopenharmony_ci
163762306a36Sopenharmony_ci	/* Clear pending Outbound Messaging interrupts */
163862306a36Sopenharmony_ci	iowrite32(inte_mask, priv->regs + TSI721_OBDMAC_INT(ch));
163962306a36Sopenharmony_ci
164062306a36Sopenharmony_ci	/* Disable Outbound Messaging interrupts */
164162306a36Sopenharmony_ci	rval = ioread32(priv->regs + TSI721_OBDMAC_INTE(ch));
164262306a36Sopenharmony_ci	rval &= ~inte_mask;
164362306a36Sopenharmony_ci	iowrite32(rval, priv->regs + TSI721_OBDMAC_INTE(ch));
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_ci	if (priv->flags & TSI721_USING_MSIX)
164662306a36Sopenharmony_ci		return; /* Finished if we are in MSI-X mode */
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_ci	/*
164962306a36Sopenharmony_ci	 * For MSI and INTA interrupt signalling we need to disable next levels
165062306a36Sopenharmony_ci	 */
165162306a36Sopenharmony_ci
165262306a36Sopenharmony_ci	/* Disable Device Channel Interrupt */
165362306a36Sopenharmony_ci	rval = ioread32(priv->regs + TSI721_DEV_CHAN_INTE);
165462306a36Sopenharmony_ci	rval &= ~TSI721_INT_OMSG_CHAN(ch);
165562306a36Sopenharmony_ci	iowrite32(rval, priv->regs + TSI721_DEV_CHAN_INTE);
165662306a36Sopenharmony_ci}
165762306a36Sopenharmony_ci
165862306a36Sopenharmony_ci/**
165962306a36Sopenharmony_ci * tsi721_add_outb_message - Add message to the Tsi721 outbound message queue
166062306a36Sopenharmony_ci * @mport: Master port with outbound message queue
166162306a36Sopenharmony_ci * @rdev: Target of outbound message
166262306a36Sopenharmony_ci * @mbox: Outbound mailbox
166362306a36Sopenharmony_ci * @buffer: Message to add to outbound queue
166462306a36Sopenharmony_ci * @len: Length of message
166562306a36Sopenharmony_ci */
166662306a36Sopenharmony_cistatic int
166762306a36Sopenharmony_citsi721_add_outb_message(struct rio_mport *mport, struct rio_dev *rdev, int mbox,
166862306a36Sopenharmony_ci			void *buffer, size_t len)
166962306a36Sopenharmony_ci{
167062306a36Sopenharmony_ci	struct tsi721_device *priv = mport->priv;
167162306a36Sopenharmony_ci	struct tsi721_omsg_desc *desc;
167262306a36Sopenharmony_ci	u32 tx_slot;
167362306a36Sopenharmony_ci	unsigned long flags;
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_ci	if (!priv->omsg_init[mbox] ||
167662306a36Sopenharmony_ci	    len > TSI721_MSG_MAX_SIZE || len < 8)
167762306a36Sopenharmony_ci		return -EINVAL;
167862306a36Sopenharmony_ci
167962306a36Sopenharmony_ci	spin_lock_irqsave(&priv->omsg_ring[mbox].lock, flags);
168062306a36Sopenharmony_ci
168162306a36Sopenharmony_ci	tx_slot = priv->omsg_ring[mbox].tx_slot;
168262306a36Sopenharmony_ci
168362306a36Sopenharmony_ci	/* Copy copy message into transfer buffer */
168462306a36Sopenharmony_ci	memcpy(priv->omsg_ring[mbox].omq_base[tx_slot], buffer, len);
168562306a36Sopenharmony_ci
168662306a36Sopenharmony_ci	if (len & 0x7)
168762306a36Sopenharmony_ci		len += 8;
168862306a36Sopenharmony_ci
168962306a36Sopenharmony_ci	/* Build descriptor associated with buffer */
169062306a36Sopenharmony_ci	desc = priv->omsg_ring[mbox].omd_base;
169162306a36Sopenharmony_ci	desc[tx_slot].type_id = cpu_to_le32((DTYPE4 << 29) | rdev->destid);
169262306a36Sopenharmony_ci#ifdef TSI721_OMSG_DESC_INT
169362306a36Sopenharmony_ci	/* Request IOF_DONE interrupt generation for each N-th frame in queue */
169462306a36Sopenharmony_ci	if (tx_slot % 4 == 0)
169562306a36Sopenharmony_ci		desc[tx_slot].type_id |= cpu_to_le32(TSI721_OMD_IOF);
169662306a36Sopenharmony_ci#endif
169762306a36Sopenharmony_ci	desc[tx_slot].msg_info =
169862306a36Sopenharmony_ci		cpu_to_le32((mport->sys_size << 26) | (mbox << 22) |
169962306a36Sopenharmony_ci			    (0xe << 12) | (len & 0xff8));
170062306a36Sopenharmony_ci	desc[tx_slot].bufptr_lo =
170162306a36Sopenharmony_ci		cpu_to_le32((u64)priv->omsg_ring[mbox].omq_phys[tx_slot] &
170262306a36Sopenharmony_ci			    0xffffffff);
170362306a36Sopenharmony_ci	desc[tx_slot].bufptr_hi =
170462306a36Sopenharmony_ci		cpu_to_le32((u64)priv->omsg_ring[mbox].omq_phys[tx_slot] >> 32);
170562306a36Sopenharmony_ci
170662306a36Sopenharmony_ci	priv->omsg_ring[mbox].wr_count++;
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_ci	/* Go to next descriptor */
170962306a36Sopenharmony_ci	if (++priv->omsg_ring[mbox].tx_slot == priv->omsg_ring[mbox].size) {
171062306a36Sopenharmony_ci		priv->omsg_ring[mbox].tx_slot = 0;
171162306a36Sopenharmony_ci		/* Move through the ring link descriptor at the end */
171262306a36Sopenharmony_ci		priv->omsg_ring[mbox].wr_count++;
171362306a36Sopenharmony_ci	}
171462306a36Sopenharmony_ci
171562306a36Sopenharmony_ci	mb();
171662306a36Sopenharmony_ci
171762306a36Sopenharmony_ci	/* Set new write count value */
171862306a36Sopenharmony_ci	iowrite32(priv->omsg_ring[mbox].wr_count,
171962306a36Sopenharmony_ci		priv->regs + TSI721_OBDMAC_DWRCNT(mbox));
172062306a36Sopenharmony_ci	ioread32(priv->regs + TSI721_OBDMAC_DWRCNT(mbox));
172162306a36Sopenharmony_ci
172262306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->omsg_ring[mbox].lock, flags);
172362306a36Sopenharmony_ci
172462306a36Sopenharmony_ci	return 0;
172562306a36Sopenharmony_ci}
172662306a36Sopenharmony_ci
172762306a36Sopenharmony_ci/**
172862306a36Sopenharmony_ci * tsi721_omsg_handler - Outbound Message Interrupt Handler
172962306a36Sopenharmony_ci * @priv: pointer to tsi721 private data
173062306a36Sopenharmony_ci * @ch:   number of OB MSG channel to service
173162306a36Sopenharmony_ci *
173262306a36Sopenharmony_ci * Services channel interrupts from outbound messaging engine.
173362306a36Sopenharmony_ci */
173462306a36Sopenharmony_cistatic void tsi721_omsg_handler(struct tsi721_device *priv, int ch)
173562306a36Sopenharmony_ci{
173662306a36Sopenharmony_ci	u32 omsg_int;
173762306a36Sopenharmony_ci	struct rio_mport *mport = &priv->mport;
173862306a36Sopenharmony_ci	void *dev_id = NULL;
173962306a36Sopenharmony_ci	u32 tx_slot = 0xffffffff;
174062306a36Sopenharmony_ci	int do_callback = 0;
174162306a36Sopenharmony_ci
174262306a36Sopenharmony_ci	spin_lock(&priv->omsg_ring[ch].lock);
174362306a36Sopenharmony_ci
174462306a36Sopenharmony_ci	omsg_int = ioread32(priv->regs + TSI721_OBDMAC_INT(ch));
174562306a36Sopenharmony_ci
174662306a36Sopenharmony_ci	if (omsg_int & TSI721_OBDMAC_INT_ST_FULL)
174762306a36Sopenharmony_ci		tsi_info(&priv->pdev->dev,
174862306a36Sopenharmony_ci			"OB MBOX%d: Status FIFO is full", ch);
174962306a36Sopenharmony_ci
175062306a36Sopenharmony_ci	if (omsg_int & (TSI721_OBDMAC_INT_DONE | TSI721_OBDMAC_INT_IOF_DONE)) {
175162306a36Sopenharmony_ci		u32 srd_ptr;
175262306a36Sopenharmony_ci		u64 *sts_ptr, last_ptr = 0, prev_ptr = 0;
175362306a36Sopenharmony_ci		int i, j;
175462306a36Sopenharmony_ci
175562306a36Sopenharmony_ci		/*
175662306a36Sopenharmony_ci		 * Find last successfully processed descriptor
175762306a36Sopenharmony_ci		 */
175862306a36Sopenharmony_ci
175962306a36Sopenharmony_ci		/* Check and clear descriptor status FIFO entries */
176062306a36Sopenharmony_ci		srd_ptr = priv->omsg_ring[ch].sts_rdptr;
176162306a36Sopenharmony_ci		sts_ptr = priv->omsg_ring[ch].sts_base;
176262306a36Sopenharmony_ci		j = srd_ptr * 8;
176362306a36Sopenharmony_ci		while (sts_ptr[j]) {
176462306a36Sopenharmony_ci			for (i = 0; i < 8 && sts_ptr[j]; i++, j++) {
176562306a36Sopenharmony_ci				prev_ptr = last_ptr;
176662306a36Sopenharmony_ci				last_ptr = le64_to_cpu(sts_ptr[j]);
176762306a36Sopenharmony_ci				sts_ptr[j] = 0;
176862306a36Sopenharmony_ci			}
176962306a36Sopenharmony_ci
177062306a36Sopenharmony_ci			++srd_ptr;
177162306a36Sopenharmony_ci			srd_ptr %= priv->omsg_ring[ch].sts_size;
177262306a36Sopenharmony_ci			j = srd_ptr * 8;
177362306a36Sopenharmony_ci		}
177462306a36Sopenharmony_ci
177562306a36Sopenharmony_ci		if (last_ptr == 0)
177662306a36Sopenharmony_ci			goto no_sts_update;
177762306a36Sopenharmony_ci
177862306a36Sopenharmony_ci		priv->omsg_ring[ch].sts_rdptr = srd_ptr;
177962306a36Sopenharmony_ci		iowrite32(srd_ptr, priv->regs + TSI721_OBDMAC_DSRP(ch));
178062306a36Sopenharmony_ci
178162306a36Sopenharmony_ci		if (!mport->outb_msg[ch].mcback)
178262306a36Sopenharmony_ci			goto no_sts_update;
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_ci		/* Inform upper layer about transfer completion */
178562306a36Sopenharmony_ci
178662306a36Sopenharmony_ci		tx_slot = (last_ptr - (u64)priv->omsg_ring[ch].omd_phys)/
178762306a36Sopenharmony_ci						sizeof(struct tsi721_omsg_desc);
178862306a36Sopenharmony_ci
178962306a36Sopenharmony_ci		/*
179062306a36Sopenharmony_ci		 * Check if this is a Link Descriptor (LD).
179162306a36Sopenharmony_ci		 * If yes, ignore LD and use descriptor processed
179262306a36Sopenharmony_ci		 * before LD.
179362306a36Sopenharmony_ci		 */
179462306a36Sopenharmony_ci		if (tx_slot == priv->omsg_ring[ch].size) {
179562306a36Sopenharmony_ci			if (prev_ptr)
179662306a36Sopenharmony_ci				tx_slot = (prev_ptr -
179762306a36Sopenharmony_ci					(u64)priv->omsg_ring[ch].omd_phys)/
179862306a36Sopenharmony_ci						sizeof(struct tsi721_omsg_desc);
179962306a36Sopenharmony_ci			else
180062306a36Sopenharmony_ci				goto no_sts_update;
180162306a36Sopenharmony_ci		}
180262306a36Sopenharmony_ci
180362306a36Sopenharmony_ci		if (tx_slot >= priv->omsg_ring[ch].size)
180462306a36Sopenharmony_ci			tsi_debug(OMSG, &priv->pdev->dev,
180562306a36Sopenharmony_ci				  "OB_MSG tx_slot=%x > size=%x",
180662306a36Sopenharmony_ci				  tx_slot, priv->omsg_ring[ch].size);
180762306a36Sopenharmony_ci		WARN_ON(tx_slot >= priv->omsg_ring[ch].size);
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_ci		/* Move slot index to the next message to be sent */
181062306a36Sopenharmony_ci		++tx_slot;
181162306a36Sopenharmony_ci		if (tx_slot == priv->omsg_ring[ch].size)
181262306a36Sopenharmony_ci			tx_slot = 0;
181362306a36Sopenharmony_ci
181462306a36Sopenharmony_ci		dev_id = priv->omsg_ring[ch].dev_id;
181562306a36Sopenharmony_ci		do_callback = 1;
181662306a36Sopenharmony_ci	}
181762306a36Sopenharmony_ci
181862306a36Sopenharmony_cino_sts_update:
181962306a36Sopenharmony_ci
182062306a36Sopenharmony_ci	if (omsg_int & TSI721_OBDMAC_INT_ERROR) {
182162306a36Sopenharmony_ci		/*
182262306a36Sopenharmony_ci		* Outbound message operation aborted due to error,
182362306a36Sopenharmony_ci		* reinitialize OB MSG channel
182462306a36Sopenharmony_ci		*/
182562306a36Sopenharmony_ci
182662306a36Sopenharmony_ci		tsi_debug(OMSG, &priv->pdev->dev, "OB MSG ABORT ch_stat=%x",
182762306a36Sopenharmony_ci			  ioread32(priv->regs + TSI721_OBDMAC_STS(ch)));
182862306a36Sopenharmony_ci
182962306a36Sopenharmony_ci		iowrite32(TSI721_OBDMAC_INT_ERROR,
183062306a36Sopenharmony_ci				priv->regs + TSI721_OBDMAC_INT(ch));
183162306a36Sopenharmony_ci		iowrite32(TSI721_OBDMAC_CTL_RETRY_THR | TSI721_OBDMAC_CTL_INIT,
183262306a36Sopenharmony_ci				priv->regs + TSI721_OBDMAC_CTL(ch));
183362306a36Sopenharmony_ci		ioread32(priv->regs + TSI721_OBDMAC_CTL(ch));
183462306a36Sopenharmony_ci
183562306a36Sopenharmony_ci		/* Inform upper level to clear all pending tx slots */
183662306a36Sopenharmony_ci		dev_id = priv->omsg_ring[ch].dev_id;
183762306a36Sopenharmony_ci		tx_slot = priv->omsg_ring[ch].tx_slot;
183862306a36Sopenharmony_ci		do_callback = 1;
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_ci		/* Synch tx_slot tracking */
184162306a36Sopenharmony_ci		iowrite32(priv->omsg_ring[ch].tx_slot,
184262306a36Sopenharmony_ci			priv->regs + TSI721_OBDMAC_DRDCNT(ch));
184362306a36Sopenharmony_ci		ioread32(priv->regs + TSI721_OBDMAC_DRDCNT(ch));
184462306a36Sopenharmony_ci		priv->omsg_ring[ch].wr_count = priv->omsg_ring[ch].tx_slot;
184562306a36Sopenharmony_ci		priv->omsg_ring[ch].sts_rdptr = 0;
184662306a36Sopenharmony_ci	}
184762306a36Sopenharmony_ci
184862306a36Sopenharmony_ci	/* Clear channel interrupts */
184962306a36Sopenharmony_ci	iowrite32(omsg_int, priv->regs + TSI721_OBDMAC_INT(ch));
185062306a36Sopenharmony_ci
185162306a36Sopenharmony_ci	if (!(priv->flags & TSI721_USING_MSIX)) {
185262306a36Sopenharmony_ci		u32 ch_inte;
185362306a36Sopenharmony_ci
185462306a36Sopenharmony_ci		/* Re-enable channel interrupts */
185562306a36Sopenharmony_ci		ch_inte = ioread32(priv->regs + TSI721_DEV_CHAN_INTE);
185662306a36Sopenharmony_ci		ch_inte |= TSI721_INT_OMSG_CHAN(ch);
185762306a36Sopenharmony_ci		iowrite32(ch_inte, priv->regs + TSI721_DEV_CHAN_INTE);
185862306a36Sopenharmony_ci	}
185962306a36Sopenharmony_ci
186062306a36Sopenharmony_ci	spin_unlock(&priv->omsg_ring[ch].lock);
186162306a36Sopenharmony_ci
186262306a36Sopenharmony_ci	if (mport->outb_msg[ch].mcback && do_callback)
186362306a36Sopenharmony_ci		mport->outb_msg[ch].mcback(mport, dev_id, ch, tx_slot);
186462306a36Sopenharmony_ci}
186562306a36Sopenharmony_ci
186662306a36Sopenharmony_ci/**
186762306a36Sopenharmony_ci * tsi721_open_outb_mbox - Initialize Tsi721 outbound mailbox
186862306a36Sopenharmony_ci * @mport: Master port implementing Outbound Messaging Engine
186962306a36Sopenharmony_ci * @dev_id: Device specific pointer to pass on event
187062306a36Sopenharmony_ci * @mbox: Mailbox to open
187162306a36Sopenharmony_ci * @entries: Number of entries in the outbound mailbox ring
187262306a36Sopenharmony_ci */
187362306a36Sopenharmony_cistatic int tsi721_open_outb_mbox(struct rio_mport *mport, void *dev_id,
187462306a36Sopenharmony_ci				 int mbox, int entries)
187562306a36Sopenharmony_ci{
187662306a36Sopenharmony_ci	struct tsi721_device *priv = mport->priv;
187762306a36Sopenharmony_ci	struct tsi721_omsg_desc *bd_ptr;
187862306a36Sopenharmony_ci	int i, rc = 0;
187962306a36Sopenharmony_ci
188062306a36Sopenharmony_ci	if ((entries < TSI721_OMSGD_MIN_RING_SIZE) ||
188162306a36Sopenharmony_ci	    (entries > (TSI721_OMSGD_RING_SIZE)) ||
188262306a36Sopenharmony_ci	    (!is_power_of_2(entries)) || mbox >= RIO_MAX_MBOX) {
188362306a36Sopenharmony_ci		rc = -EINVAL;
188462306a36Sopenharmony_ci		goto out;
188562306a36Sopenharmony_ci	}
188662306a36Sopenharmony_ci
188762306a36Sopenharmony_ci	if ((mbox_sel & (1 << mbox)) == 0) {
188862306a36Sopenharmony_ci		rc = -ENODEV;
188962306a36Sopenharmony_ci		goto out;
189062306a36Sopenharmony_ci	}
189162306a36Sopenharmony_ci
189262306a36Sopenharmony_ci	priv->omsg_ring[mbox].dev_id = dev_id;
189362306a36Sopenharmony_ci	priv->omsg_ring[mbox].size = entries;
189462306a36Sopenharmony_ci	priv->omsg_ring[mbox].sts_rdptr = 0;
189562306a36Sopenharmony_ci	spin_lock_init(&priv->omsg_ring[mbox].lock);
189662306a36Sopenharmony_ci
189762306a36Sopenharmony_ci	/* Outbound Msg Buffer allocation based on
189862306a36Sopenharmony_ci	   the number of maximum descriptor entries */
189962306a36Sopenharmony_ci	for (i = 0; i < entries; i++) {
190062306a36Sopenharmony_ci		priv->omsg_ring[mbox].omq_base[i] =
190162306a36Sopenharmony_ci			dma_alloc_coherent(
190262306a36Sopenharmony_ci				&priv->pdev->dev, TSI721_MSG_BUFFER_SIZE,
190362306a36Sopenharmony_ci				&priv->omsg_ring[mbox].omq_phys[i],
190462306a36Sopenharmony_ci				GFP_KERNEL);
190562306a36Sopenharmony_ci		if (priv->omsg_ring[mbox].omq_base[i] == NULL) {
190662306a36Sopenharmony_ci			tsi_debug(OMSG, &priv->pdev->dev,
190762306a36Sopenharmony_ci				  "ENOMEM for OB_MSG_%d data buffer", mbox);
190862306a36Sopenharmony_ci			rc = -ENOMEM;
190962306a36Sopenharmony_ci			goto out_buf;
191062306a36Sopenharmony_ci		}
191162306a36Sopenharmony_ci	}
191262306a36Sopenharmony_ci
191362306a36Sopenharmony_ci	/* Outbound message descriptor allocation */
191462306a36Sopenharmony_ci	priv->omsg_ring[mbox].omd_base = dma_alloc_coherent(
191562306a36Sopenharmony_ci				&priv->pdev->dev,
191662306a36Sopenharmony_ci				(entries + 1) * sizeof(struct tsi721_omsg_desc),
191762306a36Sopenharmony_ci				&priv->omsg_ring[mbox].omd_phys, GFP_KERNEL);
191862306a36Sopenharmony_ci	if (priv->omsg_ring[mbox].omd_base == NULL) {
191962306a36Sopenharmony_ci		tsi_debug(OMSG, &priv->pdev->dev,
192062306a36Sopenharmony_ci			"ENOMEM for OB_MSG_%d descriptor memory", mbox);
192162306a36Sopenharmony_ci		rc = -ENOMEM;
192262306a36Sopenharmony_ci		goto out_buf;
192362306a36Sopenharmony_ci	}
192462306a36Sopenharmony_ci
192562306a36Sopenharmony_ci	priv->omsg_ring[mbox].tx_slot = 0;
192662306a36Sopenharmony_ci
192762306a36Sopenharmony_ci	/* Outbound message descriptor status FIFO allocation */
192862306a36Sopenharmony_ci	priv->omsg_ring[mbox].sts_size = roundup_pow_of_two(entries + 1);
192962306a36Sopenharmony_ci	priv->omsg_ring[mbox].sts_base = dma_alloc_coherent(&priv->pdev->dev,
193062306a36Sopenharmony_ci							    priv->omsg_ring[mbox].sts_size * sizeof(struct tsi721_dma_sts),
193162306a36Sopenharmony_ci							    &priv->omsg_ring[mbox].sts_phys,
193262306a36Sopenharmony_ci							    GFP_KERNEL);
193362306a36Sopenharmony_ci	if (priv->omsg_ring[mbox].sts_base == NULL) {
193462306a36Sopenharmony_ci		tsi_debug(OMSG, &priv->pdev->dev,
193562306a36Sopenharmony_ci			"ENOMEM for OB_MSG_%d status FIFO", mbox);
193662306a36Sopenharmony_ci		rc = -ENOMEM;
193762306a36Sopenharmony_ci		goto out_desc;
193862306a36Sopenharmony_ci	}
193962306a36Sopenharmony_ci
194062306a36Sopenharmony_ci	/*
194162306a36Sopenharmony_ci	 * Configure Outbound Messaging Engine
194262306a36Sopenharmony_ci	 */
194362306a36Sopenharmony_ci
194462306a36Sopenharmony_ci	/* Setup Outbound Message descriptor pointer */
194562306a36Sopenharmony_ci	iowrite32(((u64)priv->omsg_ring[mbox].omd_phys >> 32),
194662306a36Sopenharmony_ci			priv->regs + TSI721_OBDMAC_DPTRH(mbox));
194762306a36Sopenharmony_ci	iowrite32(((u64)priv->omsg_ring[mbox].omd_phys &
194862306a36Sopenharmony_ci					TSI721_OBDMAC_DPTRL_MASK),
194962306a36Sopenharmony_ci			priv->regs + TSI721_OBDMAC_DPTRL(mbox));
195062306a36Sopenharmony_ci
195162306a36Sopenharmony_ci	/* Setup Outbound Message descriptor status FIFO */
195262306a36Sopenharmony_ci	iowrite32(((u64)priv->omsg_ring[mbox].sts_phys >> 32),
195362306a36Sopenharmony_ci			priv->regs + TSI721_OBDMAC_DSBH(mbox));
195462306a36Sopenharmony_ci	iowrite32(((u64)priv->omsg_ring[mbox].sts_phys &
195562306a36Sopenharmony_ci					TSI721_OBDMAC_DSBL_MASK),
195662306a36Sopenharmony_ci			priv->regs + TSI721_OBDMAC_DSBL(mbox));
195762306a36Sopenharmony_ci	iowrite32(TSI721_DMAC_DSSZ_SIZE(priv->omsg_ring[mbox].sts_size),
195862306a36Sopenharmony_ci		priv->regs + (u32)TSI721_OBDMAC_DSSZ(mbox));
195962306a36Sopenharmony_ci
196062306a36Sopenharmony_ci	/* Enable interrupts */
196162306a36Sopenharmony_ci
196262306a36Sopenharmony_ci#ifdef CONFIG_PCI_MSI
196362306a36Sopenharmony_ci	if (priv->flags & TSI721_USING_MSIX) {
196462306a36Sopenharmony_ci		int idx = TSI721_VECT_OMB0_DONE + mbox;
196562306a36Sopenharmony_ci
196662306a36Sopenharmony_ci		/* Request interrupt service if we are in MSI-X mode */
196762306a36Sopenharmony_ci		rc = request_irq(priv->msix[idx].vector, tsi721_omsg_msix, 0,
196862306a36Sopenharmony_ci				 priv->msix[idx].irq_name, (void *)priv);
196962306a36Sopenharmony_ci
197062306a36Sopenharmony_ci		if (rc) {
197162306a36Sopenharmony_ci			tsi_debug(OMSG, &priv->pdev->dev,
197262306a36Sopenharmony_ci				"Unable to get MSI-X IRQ for OBOX%d-DONE",
197362306a36Sopenharmony_ci				mbox);
197462306a36Sopenharmony_ci			goto out_stat;
197562306a36Sopenharmony_ci		}
197662306a36Sopenharmony_ci
197762306a36Sopenharmony_ci		idx = TSI721_VECT_OMB0_INT + mbox;
197862306a36Sopenharmony_ci		rc = request_irq(priv->msix[idx].vector, tsi721_omsg_msix, 0,
197962306a36Sopenharmony_ci				 priv->msix[idx].irq_name, (void *)priv);
198062306a36Sopenharmony_ci
198162306a36Sopenharmony_ci		if (rc)	{
198262306a36Sopenharmony_ci			tsi_debug(OMSG, &priv->pdev->dev,
198362306a36Sopenharmony_ci				"Unable to get MSI-X IRQ for MBOX%d-INT", mbox);
198462306a36Sopenharmony_ci			idx = TSI721_VECT_OMB0_DONE + mbox;
198562306a36Sopenharmony_ci			free_irq(priv->msix[idx].vector, (void *)priv);
198662306a36Sopenharmony_ci			goto out_stat;
198762306a36Sopenharmony_ci		}
198862306a36Sopenharmony_ci	}
198962306a36Sopenharmony_ci#endif /* CONFIG_PCI_MSI */
199062306a36Sopenharmony_ci
199162306a36Sopenharmony_ci	tsi721_omsg_interrupt_enable(priv, mbox, TSI721_OBDMAC_INT_ALL);
199262306a36Sopenharmony_ci
199362306a36Sopenharmony_ci	/* Initialize Outbound Message descriptors ring */
199462306a36Sopenharmony_ci	bd_ptr = priv->omsg_ring[mbox].omd_base;
199562306a36Sopenharmony_ci	bd_ptr[entries].type_id = cpu_to_le32(DTYPE5 << 29);
199662306a36Sopenharmony_ci	bd_ptr[entries].msg_info = 0;
199762306a36Sopenharmony_ci	bd_ptr[entries].next_lo =
199862306a36Sopenharmony_ci		cpu_to_le32((u64)priv->omsg_ring[mbox].omd_phys &
199962306a36Sopenharmony_ci		TSI721_OBDMAC_DPTRL_MASK);
200062306a36Sopenharmony_ci	bd_ptr[entries].next_hi =
200162306a36Sopenharmony_ci		cpu_to_le32((u64)priv->omsg_ring[mbox].omd_phys >> 32);
200262306a36Sopenharmony_ci	priv->omsg_ring[mbox].wr_count = 0;
200362306a36Sopenharmony_ci	mb();
200462306a36Sopenharmony_ci
200562306a36Sopenharmony_ci	/* Initialize Outbound Message engine */
200662306a36Sopenharmony_ci	iowrite32(TSI721_OBDMAC_CTL_RETRY_THR | TSI721_OBDMAC_CTL_INIT,
200762306a36Sopenharmony_ci		  priv->regs + TSI721_OBDMAC_CTL(mbox));
200862306a36Sopenharmony_ci	ioread32(priv->regs + TSI721_OBDMAC_DWRCNT(mbox));
200962306a36Sopenharmony_ci	udelay(10);
201062306a36Sopenharmony_ci
201162306a36Sopenharmony_ci	priv->omsg_init[mbox] = 1;
201262306a36Sopenharmony_ci
201362306a36Sopenharmony_ci	return 0;
201462306a36Sopenharmony_ci
201562306a36Sopenharmony_ci#ifdef CONFIG_PCI_MSI
201662306a36Sopenharmony_ciout_stat:
201762306a36Sopenharmony_ci	dma_free_coherent(&priv->pdev->dev,
201862306a36Sopenharmony_ci		priv->omsg_ring[mbox].sts_size * sizeof(struct tsi721_dma_sts),
201962306a36Sopenharmony_ci		priv->omsg_ring[mbox].sts_base,
202062306a36Sopenharmony_ci		priv->omsg_ring[mbox].sts_phys);
202162306a36Sopenharmony_ci
202262306a36Sopenharmony_ci	priv->omsg_ring[mbox].sts_base = NULL;
202362306a36Sopenharmony_ci#endif /* CONFIG_PCI_MSI */
202462306a36Sopenharmony_ci
202562306a36Sopenharmony_ciout_desc:
202662306a36Sopenharmony_ci	dma_free_coherent(&priv->pdev->dev,
202762306a36Sopenharmony_ci		(entries + 1) * sizeof(struct tsi721_omsg_desc),
202862306a36Sopenharmony_ci		priv->omsg_ring[mbox].omd_base,
202962306a36Sopenharmony_ci		priv->omsg_ring[mbox].omd_phys);
203062306a36Sopenharmony_ci
203162306a36Sopenharmony_ci	priv->omsg_ring[mbox].omd_base = NULL;
203262306a36Sopenharmony_ci
203362306a36Sopenharmony_ciout_buf:
203462306a36Sopenharmony_ci	for (i = 0; i < priv->omsg_ring[mbox].size; i++) {
203562306a36Sopenharmony_ci		if (priv->omsg_ring[mbox].omq_base[i]) {
203662306a36Sopenharmony_ci			dma_free_coherent(&priv->pdev->dev,
203762306a36Sopenharmony_ci				TSI721_MSG_BUFFER_SIZE,
203862306a36Sopenharmony_ci				priv->omsg_ring[mbox].omq_base[i],
203962306a36Sopenharmony_ci				priv->omsg_ring[mbox].omq_phys[i]);
204062306a36Sopenharmony_ci
204162306a36Sopenharmony_ci			priv->omsg_ring[mbox].omq_base[i] = NULL;
204262306a36Sopenharmony_ci		}
204362306a36Sopenharmony_ci	}
204462306a36Sopenharmony_ci
204562306a36Sopenharmony_ciout:
204662306a36Sopenharmony_ci	return rc;
204762306a36Sopenharmony_ci}
204862306a36Sopenharmony_ci
204962306a36Sopenharmony_ci/**
205062306a36Sopenharmony_ci * tsi721_close_outb_mbox - Close Tsi721 outbound mailbox
205162306a36Sopenharmony_ci * @mport: Master port implementing the outbound message unit
205262306a36Sopenharmony_ci * @mbox: Mailbox to close
205362306a36Sopenharmony_ci */
205462306a36Sopenharmony_cistatic void tsi721_close_outb_mbox(struct rio_mport *mport, int mbox)
205562306a36Sopenharmony_ci{
205662306a36Sopenharmony_ci	struct tsi721_device *priv = mport->priv;
205762306a36Sopenharmony_ci	u32 i;
205862306a36Sopenharmony_ci
205962306a36Sopenharmony_ci	if (!priv->omsg_init[mbox])
206062306a36Sopenharmony_ci		return;
206162306a36Sopenharmony_ci	priv->omsg_init[mbox] = 0;
206262306a36Sopenharmony_ci
206362306a36Sopenharmony_ci	/* Disable Interrupts */
206462306a36Sopenharmony_ci
206562306a36Sopenharmony_ci	tsi721_omsg_interrupt_disable(priv, mbox, TSI721_OBDMAC_INT_ALL);
206662306a36Sopenharmony_ci
206762306a36Sopenharmony_ci#ifdef CONFIG_PCI_MSI
206862306a36Sopenharmony_ci	if (priv->flags & TSI721_USING_MSIX) {
206962306a36Sopenharmony_ci		free_irq(priv->msix[TSI721_VECT_OMB0_DONE + mbox].vector,
207062306a36Sopenharmony_ci			 (void *)priv);
207162306a36Sopenharmony_ci		free_irq(priv->msix[TSI721_VECT_OMB0_INT + mbox].vector,
207262306a36Sopenharmony_ci			 (void *)priv);
207362306a36Sopenharmony_ci	}
207462306a36Sopenharmony_ci#endif /* CONFIG_PCI_MSI */
207562306a36Sopenharmony_ci
207662306a36Sopenharmony_ci	/* Free OMSG Descriptor Status FIFO */
207762306a36Sopenharmony_ci	dma_free_coherent(&priv->pdev->dev,
207862306a36Sopenharmony_ci		priv->omsg_ring[mbox].sts_size * sizeof(struct tsi721_dma_sts),
207962306a36Sopenharmony_ci		priv->omsg_ring[mbox].sts_base,
208062306a36Sopenharmony_ci		priv->omsg_ring[mbox].sts_phys);
208162306a36Sopenharmony_ci
208262306a36Sopenharmony_ci	priv->omsg_ring[mbox].sts_base = NULL;
208362306a36Sopenharmony_ci
208462306a36Sopenharmony_ci	/* Free OMSG descriptors */
208562306a36Sopenharmony_ci	dma_free_coherent(&priv->pdev->dev,
208662306a36Sopenharmony_ci		(priv->omsg_ring[mbox].size + 1) *
208762306a36Sopenharmony_ci			sizeof(struct tsi721_omsg_desc),
208862306a36Sopenharmony_ci		priv->omsg_ring[mbox].omd_base,
208962306a36Sopenharmony_ci		priv->omsg_ring[mbox].omd_phys);
209062306a36Sopenharmony_ci
209162306a36Sopenharmony_ci	priv->omsg_ring[mbox].omd_base = NULL;
209262306a36Sopenharmony_ci
209362306a36Sopenharmony_ci	/* Free message buffers */
209462306a36Sopenharmony_ci	for (i = 0; i < priv->omsg_ring[mbox].size; i++) {
209562306a36Sopenharmony_ci		if (priv->omsg_ring[mbox].omq_base[i]) {
209662306a36Sopenharmony_ci			dma_free_coherent(&priv->pdev->dev,
209762306a36Sopenharmony_ci				TSI721_MSG_BUFFER_SIZE,
209862306a36Sopenharmony_ci				priv->omsg_ring[mbox].omq_base[i],
209962306a36Sopenharmony_ci				priv->omsg_ring[mbox].omq_phys[i]);
210062306a36Sopenharmony_ci
210162306a36Sopenharmony_ci			priv->omsg_ring[mbox].omq_base[i] = NULL;
210262306a36Sopenharmony_ci		}
210362306a36Sopenharmony_ci	}
210462306a36Sopenharmony_ci}
210562306a36Sopenharmony_ci
210662306a36Sopenharmony_ci/**
210762306a36Sopenharmony_ci * tsi721_imsg_handler - Inbound Message Interrupt Handler
210862306a36Sopenharmony_ci * @priv: pointer to tsi721 private data
210962306a36Sopenharmony_ci * @ch: inbound message channel number to service
211062306a36Sopenharmony_ci *
211162306a36Sopenharmony_ci * Services channel interrupts from inbound messaging engine.
211262306a36Sopenharmony_ci */
211362306a36Sopenharmony_cistatic void tsi721_imsg_handler(struct tsi721_device *priv, int ch)
211462306a36Sopenharmony_ci{
211562306a36Sopenharmony_ci	u32 mbox = ch - 4;
211662306a36Sopenharmony_ci	u32 imsg_int;
211762306a36Sopenharmony_ci	struct rio_mport *mport = &priv->mport;
211862306a36Sopenharmony_ci
211962306a36Sopenharmony_ci	spin_lock(&priv->imsg_ring[mbox].lock);
212062306a36Sopenharmony_ci
212162306a36Sopenharmony_ci	imsg_int = ioread32(priv->regs + TSI721_IBDMAC_INT(ch));
212262306a36Sopenharmony_ci
212362306a36Sopenharmony_ci	if (imsg_int & TSI721_IBDMAC_INT_SRTO)
212462306a36Sopenharmony_ci		tsi_info(&priv->pdev->dev, "IB MBOX%d SRIO timeout", mbox);
212562306a36Sopenharmony_ci
212662306a36Sopenharmony_ci	if (imsg_int & TSI721_IBDMAC_INT_PC_ERROR)
212762306a36Sopenharmony_ci		tsi_info(&priv->pdev->dev, "IB MBOX%d PCIe error", mbox);
212862306a36Sopenharmony_ci
212962306a36Sopenharmony_ci	if (imsg_int & TSI721_IBDMAC_INT_FQ_LOW)
213062306a36Sopenharmony_ci		tsi_info(&priv->pdev->dev, "IB MBOX%d IB free queue low", mbox);
213162306a36Sopenharmony_ci
213262306a36Sopenharmony_ci	/* Clear IB channel interrupts */
213362306a36Sopenharmony_ci	iowrite32(imsg_int, priv->regs + TSI721_IBDMAC_INT(ch));
213462306a36Sopenharmony_ci
213562306a36Sopenharmony_ci	/* If an IB Msg is received notify the upper layer */
213662306a36Sopenharmony_ci	if (imsg_int & TSI721_IBDMAC_INT_DQ_RCV &&
213762306a36Sopenharmony_ci		mport->inb_msg[mbox].mcback)
213862306a36Sopenharmony_ci		mport->inb_msg[mbox].mcback(mport,
213962306a36Sopenharmony_ci				priv->imsg_ring[mbox].dev_id, mbox, -1);
214062306a36Sopenharmony_ci
214162306a36Sopenharmony_ci	if (!(priv->flags & TSI721_USING_MSIX)) {
214262306a36Sopenharmony_ci		u32 ch_inte;
214362306a36Sopenharmony_ci
214462306a36Sopenharmony_ci		/* Re-enable channel interrupts */
214562306a36Sopenharmony_ci		ch_inte = ioread32(priv->regs + TSI721_DEV_CHAN_INTE);
214662306a36Sopenharmony_ci		ch_inte |= TSI721_INT_IMSG_CHAN(ch);
214762306a36Sopenharmony_ci		iowrite32(ch_inte, priv->regs + TSI721_DEV_CHAN_INTE);
214862306a36Sopenharmony_ci	}
214962306a36Sopenharmony_ci
215062306a36Sopenharmony_ci	spin_unlock(&priv->imsg_ring[mbox].lock);
215162306a36Sopenharmony_ci}
215262306a36Sopenharmony_ci
215362306a36Sopenharmony_ci/**
215462306a36Sopenharmony_ci * tsi721_open_inb_mbox - Initialize Tsi721 inbound mailbox
215562306a36Sopenharmony_ci * @mport: Master port implementing the Inbound Messaging Engine
215662306a36Sopenharmony_ci * @dev_id: Device specific pointer to pass on event
215762306a36Sopenharmony_ci * @mbox: Mailbox to open
215862306a36Sopenharmony_ci * @entries: Number of entries in the inbound mailbox ring
215962306a36Sopenharmony_ci */
216062306a36Sopenharmony_cistatic int tsi721_open_inb_mbox(struct rio_mport *mport, void *dev_id,
216162306a36Sopenharmony_ci				int mbox, int entries)
216262306a36Sopenharmony_ci{
216362306a36Sopenharmony_ci	struct tsi721_device *priv = mport->priv;
216462306a36Sopenharmony_ci	int ch = mbox + 4;
216562306a36Sopenharmony_ci	int i;
216662306a36Sopenharmony_ci	u64 *free_ptr;
216762306a36Sopenharmony_ci	int rc = 0;
216862306a36Sopenharmony_ci
216962306a36Sopenharmony_ci	if ((entries < TSI721_IMSGD_MIN_RING_SIZE) ||
217062306a36Sopenharmony_ci	    (entries > TSI721_IMSGD_RING_SIZE) ||
217162306a36Sopenharmony_ci	    (!is_power_of_2(entries)) || mbox >= RIO_MAX_MBOX) {
217262306a36Sopenharmony_ci		rc = -EINVAL;
217362306a36Sopenharmony_ci		goto out;
217462306a36Sopenharmony_ci	}
217562306a36Sopenharmony_ci
217662306a36Sopenharmony_ci	if ((mbox_sel & (1 << mbox)) == 0) {
217762306a36Sopenharmony_ci		rc = -ENODEV;
217862306a36Sopenharmony_ci		goto out;
217962306a36Sopenharmony_ci	}
218062306a36Sopenharmony_ci
218162306a36Sopenharmony_ci	/* Initialize IB Messaging Ring */
218262306a36Sopenharmony_ci	priv->imsg_ring[mbox].dev_id = dev_id;
218362306a36Sopenharmony_ci	priv->imsg_ring[mbox].size = entries;
218462306a36Sopenharmony_ci	priv->imsg_ring[mbox].rx_slot = 0;
218562306a36Sopenharmony_ci	priv->imsg_ring[mbox].desc_rdptr = 0;
218662306a36Sopenharmony_ci	priv->imsg_ring[mbox].fq_wrptr = 0;
218762306a36Sopenharmony_ci	for (i = 0; i < priv->imsg_ring[mbox].size; i++)
218862306a36Sopenharmony_ci		priv->imsg_ring[mbox].imq_base[i] = NULL;
218962306a36Sopenharmony_ci	spin_lock_init(&priv->imsg_ring[mbox].lock);
219062306a36Sopenharmony_ci
219162306a36Sopenharmony_ci	/* Allocate buffers for incoming messages */
219262306a36Sopenharmony_ci	priv->imsg_ring[mbox].buf_base =
219362306a36Sopenharmony_ci		dma_alloc_coherent(&priv->pdev->dev,
219462306a36Sopenharmony_ci				   entries * TSI721_MSG_BUFFER_SIZE,
219562306a36Sopenharmony_ci				   &priv->imsg_ring[mbox].buf_phys,
219662306a36Sopenharmony_ci				   GFP_KERNEL);
219762306a36Sopenharmony_ci
219862306a36Sopenharmony_ci	if (priv->imsg_ring[mbox].buf_base == NULL) {
219962306a36Sopenharmony_ci		tsi_err(&priv->pdev->dev,
220062306a36Sopenharmony_ci			"Failed to allocate buffers for IB MBOX%d", mbox);
220162306a36Sopenharmony_ci		rc = -ENOMEM;
220262306a36Sopenharmony_ci		goto out;
220362306a36Sopenharmony_ci	}
220462306a36Sopenharmony_ci
220562306a36Sopenharmony_ci	/* Allocate memory for circular free list */
220662306a36Sopenharmony_ci	priv->imsg_ring[mbox].imfq_base =
220762306a36Sopenharmony_ci		dma_alloc_coherent(&priv->pdev->dev,
220862306a36Sopenharmony_ci				   entries * 8,
220962306a36Sopenharmony_ci				   &priv->imsg_ring[mbox].imfq_phys,
221062306a36Sopenharmony_ci				   GFP_KERNEL);
221162306a36Sopenharmony_ci
221262306a36Sopenharmony_ci	if (priv->imsg_ring[mbox].imfq_base == NULL) {
221362306a36Sopenharmony_ci		tsi_err(&priv->pdev->dev,
221462306a36Sopenharmony_ci			"Failed to allocate free queue for IB MBOX%d", mbox);
221562306a36Sopenharmony_ci		rc = -ENOMEM;
221662306a36Sopenharmony_ci		goto out_buf;
221762306a36Sopenharmony_ci	}
221862306a36Sopenharmony_ci
221962306a36Sopenharmony_ci	/* Allocate memory for Inbound message descriptors */
222062306a36Sopenharmony_ci	priv->imsg_ring[mbox].imd_base =
222162306a36Sopenharmony_ci		dma_alloc_coherent(&priv->pdev->dev,
222262306a36Sopenharmony_ci				   entries * sizeof(struct tsi721_imsg_desc),
222362306a36Sopenharmony_ci				   &priv->imsg_ring[mbox].imd_phys, GFP_KERNEL);
222462306a36Sopenharmony_ci
222562306a36Sopenharmony_ci	if (priv->imsg_ring[mbox].imd_base == NULL) {
222662306a36Sopenharmony_ci		tsi_err(&priv->pdev->dev,
222762306a36Sopenharmony_ci			"Failed to allocate descriptor memory for IB MBOX%d",
222862306a36Sopenharmony_ci			mbox);
222962306a36Sopenharmony_ci		rc = -ENOMEM;
223062306a36Sopenharmony_ci		goto out_dma;
223162306a36Sopenharmony_ci	}
223262306a36Sopenharmony_ci
223362306a36Sopenharmony_ci	/* Fill free buffer pointer list */
223462306a36Sopenharmony_ci	free_ptr = priv->imsg_ring[mbox].imfq_base;
223562306a36Sopenharmony_ci	for (i = 0; i < entries; i++)
223662306a36Sopenharmony_ci		free_ptr[i] = cpu_to_le64(
223762306a36Sopenharmony_ci				(u64)(priv->imsg_ring[mbox].buf_phys) +
223862306a36Sopenharmony_ci				i * 0x1000);
223962306a36Sopenharmony_ci
224062306a36Sopenharmony_ci	mb();
224162306a36Sopenharmony_ci
224262306a36Sopenharmony_ci	/*
224362306a36Sopenharmony_ci	 * For mapping of inbound SRIO Messages into appropriate queues we need
224462306a36Sopenharmony_ci	 * to set Inbound Device ID register in the messaging engine. We do it
224562306a36Sopenharmony_ci	 * once when first inbound mailbox is requested.
224662306a36Sopenharmony_ci	 */
224762306a36Sopenharmony_ci	if (!(priv->flags & TSI721_IMSGID_SET)) {
224862306a36Sopenharmony_ci		iowrite32((u32)priv->mport.host_deviceid,
224962306a36Sopenharmony_ci			priv->regs + TSI721_IB_DEVID);
225062306a36Sopenharmony_ci		priv->flags |= TSI721_IMSGID_SET;
225162306a36Sopenharmony_ci	}
225262306a36Sopenharmony_ci
225362306a36Sopenharmony_ci	/*
225462306a36Sopenharmony_ci	 * Configure Inbound Messaging channel (ch = mbox + 4)
225562306a36Sopenharmony_ci	 */
225662306a36Sopenharmony_ci
225762306a36Sopenharmony_ci	/* Setup Inbound Message free queue */
225862306a36Sopenharmony_ci	iowrite32(((u64)priv->imsg_ring[mbox].imfq_phys >> 32),
225962306a36Sopenharmony_ci		priv->regs + TSI721_IBDMAC_FQBH(ch));
226062306a36Sopenharmony_ci	iowrite32(((u64)priv->imsg_ring[mbox].imfq_phys &
226162306a36Sopenharmony_ci			TSI721_IBDMAC_FQBL_MASK),
226262306a36Sopenharmony_ci		priv->regs+TSI721_IBDMAC_FQBL(ch));
226362306a36Sopenharmony_ci	iowrite32(TSI721_DMAC_DSSZ_SIZE(entries),
226462306a36Sopenharmony_ci		priv->regs + TSI721_IBDMAC_FQSZ(ch));
226562306a36Sopenharmony_ci
226662306a36Sopenharmony_ci	/* Setup Inbound Message descriptor queue */
226762306a36Sopenharmony_ci	iowrite32(((u64)priv->imsg_ring[mbox].imd_phys >> 32),
226862306a36Sopenharmony_ci		priv->regs + TSI721_IBDMAC_DQBH(ch));
226962306a36Sopenharmony_ci	iowrite32(((u32)priv->imsg_ring[mbox].imd_phys &
227062306a36Sopenharmony_ci		   (u32)TSI721_IBDMAC_DQBL_MASK),
227162306a36Sopenharmony_ci		priv->regs+TSI721_IBDMAC_DQBL(ch));
227262306a36Sopenharmony_ci	iowrite32(TSI721_DMAC_DSSZ_SIZE(entries),
227362306a36Sopenharmony_ci		priv->regs + TSI721_IBDMAC_DQSZ(ch));
227462306a36Sopenharmony_ci
227562306a36Sopenharmony_ci	/* Enable interrupts */
227662306a36Sopenharmony_ci
227762306a36Sopenharmony_ci#ifdef CONFIG_PCI_MSI
227862306a36Sopenharmony_ci	if (priv->flags & TSI721_USING_MSIX) {
227962306a36Sopenharmony_ci		int idx = TSI721_VECT_IMB0_RCV + mbox;
228062306a36Sopenharmony_ci
228162306a36Sopenharmony_ci		/* Request interrupt service if we are in MSI-X mode */
228262306a36Sopenharmony_ci		rc = request_irq(priv->msix[idx].vector, tsi721_imsg_msix, 0,
228362306a36Sopenharmony_ci				 priv->msix[idx].irq_name, (void *)priv);
228462306a36Sopenharmony_ci
228562306a36Sopenharmony_ci		if (rc) {
228662306a36Sopenharmony_ci			tsi_debug(IMSG, &priv->pdev->dev,
228762306a36Sopenharmony_ci				"Unable to get MSI-X IRQ for IBOX%d-DONE",
228862306a36Sopenharmony_ci				mbox);
228962306a36Sopenharmony_ci			goto out_desc;
229062306a36Sopenharmony_ci		}
229162306a36Sopenharmony_ci
229262306a36Sopenharmony_ci		idx = TSI721_VECT_IMB0_INT + mbox;
229362306a36Sopenharmony_ci		rc = request_irq(priv->msix[idx].vector, tsi721_imsg_msix, 0,
229462306a36Sopenharmony_ci				 priv->msix[idx].irq_name, (void *)priv);
229562306a36Sopenharmony_ci
229662306a36Sopenharmony_ci		if (rc)	{
229762306a36Sopenharmony_ci			tsi_debug(IMSG, &priv->pdev->dev,
229862306a36Sopenharmony_ci				"Unable to get MSI-X IRQ for IBOX%d-INT", mbox);
229962306a36Sopenharmony_ci			free_irq(
230062306a36Sopenharmony_ci				priv->msix[TSI721_VECT_IMB0_RCV + mbox].vector,
230162306a36Sopenharmony_ci				(void *)priv);
230262306a36Sopenharmony_ci			goto out_desc;
230362306a36Sopenharmony_ci		}
230462306a36Sopenharmony_ci	}
230562306a36Sopenharmony_ci#endif /* CONFIG_PCI_MSI */
230662306a36Sopenharmony_ci
230762306a36Sopenharmony_ci	tsi721_imsg_interrupt_enable(priv, ch, TSI721_IBDMAC_INT_ALL);
230862306a36Sopenharmony_ci
230962306a36Sopenharmony_ci	/* Initialize Inbound Message Engine */
231062306a36Sopenharmony_ci	iowrite32(TSI721_IBDMAC_CTL_INIT, priv->regs + TSI721_IBDMAC_CTL(ch));
231162306a36Sopenharmony_ci	ioread32(priv->regs + TSI721_IBDMAC_CTL(ch));
231262306a36Sopenharmony_ci	udelay(10);
231362306a36Sopenharmony_ci	priv->imsg_ring[mbox].fq_wrptr = entries - 1;
231462306a36Sopenharmony_ci	iowrite32(entries - 1, priv->regs + TSI721_IBDMAC_FQWP(ch));
231562306a36Sopenharmony_ci
231662306a36Sopenharmony_ci	priv->imsg_init[mbox] = 1;
231762306a36Sopenharmony_ci	return 0;
231862306a36Sopenharmony_ci
231962306a36Sopenharmony_ci#ifdef CONFIG_PCI_MSI
232062306a36Sopenharmony_ciout_desc:
232162306a36Sopenharmony_ci	dma_free_coherent(&priv->pdev->dev,
232262306a36Sopenharmony_ci		priv->imsg_ring[mbox].size * sizeof(struct tsi721_imsg_desc),
232362306a36Sopenharmony_ci		priv->imsg_ring[mbox].imd_base,
232462306a36Sopenharmony_ci		priv->imsg_ring[mbox].imd_phys);
232562306a36Sopenharmony_ci
232662306a36Sopenharmony_ci	priv->imsg_ring[mbox].imd_base = NULL;
232762306a36Sopenharmony_ci#endif /* CONFIG_PCI_MSI */
232862306a36Sopenharmony_ci
232962306a36Sopenharmony_ciout_dma:
233062306a36Sopenharmony_ci	dma_free_coherent(&priv->pdev->dev,
233162306a36Sopenharmony_ci		priv->imsg_ring[mbox].size * 8,
233262306a36Sopenharmony_ci		priv->imsg_ring[mbox].imfq_base,
233362306a36Sopenharmony_ci		priv->imsg_ring[mbox].imfq_phys);
233462306a36Sopenharmony_ci
233562306a36Sopenharmony_ci	priv->imsg_ring[mbox].imfq_base = NULL;
233662306a36Sopenharmony_ci
233762306a36Sopenharmony_ciout_buf:
233862306a36Sopenharmony_ci	dma_free_coherent(&priv->pdev->dev,
233962306a36Sopenharmony_ci		priv->imsg_ring[mbox].size * TSI721_MSG_BUFFER_SIZE,
234062306a36Sopenharmony_ci		priv->imsg_ring[mbox].buf_base,
234162306a36Sopenharmony_ci		priv->imsg_ring[mbox].buf_phys);
234262306a36Sopenharmony_ci
234362306a36Sopenharmony_ci	priv->imsg_ring[mbox].buf_base = NULL;
234462306a36Sopenharmony_ci
234562306a36Sopenharmony_ciout:
234662306a36Sopenharmony_ci	return rc;
234762306a36Sopenharmony_ci}
234862306a36Sopenharmony_ci
234962306a36Sopenharmony_ci/**
235062306a36Sopenharmony_ci * tsi721_close_inb_mbox - Shut down Tsi721 inbound mailbox
235162306a36Sopenharmony_ci * @mport: Master port implementing the Inbound Messaging Engine
235262306a36Sopenharmony_ci * @mbox: Mailbox to close
235362306a36Sopenharmony_ci */
235462306a36Sopenharmony_cistatic void tsi721_close_inb_mbox(struct rio_mport *mport, int mbox)
235562306a36Sopenharmony_ci{
235662306a36Sopenharmony_ci	struct tsi721_device *priv = mport->priv;
235762306a36Sopenharmony_ci	u32 rx_slot;
235862306a36Sopenharmony_ci	int ch = mbox + 4;
235962306a36Sopenharmony_ci
236062306a36Sopenharmony_ci	if (!priv->imsg_init[mbox]) /* mbox isn't initialized yet */
236162306a36Sopenharmony_ci		return;
236262306a36Sopenharmony_ci	priv->imsg_init[mbox] = 0;
236362306a36Sopenharmony_ci
236462306a36Sopenharmony_ci	/* Disable Inbound Messaging Engine */
236562306a36Sopenharmony_ci
236662306a36Sopenharmony_ci	/* Disable Interrupts */
236762306a36Sopenharmony_ci	tsi721_imsg_interrupt_disable(priv, ch, TSI721_OBDMAC_INT_MASK);
236862306a36Sopenharmony_ci
236962306a36Sopenharmony_ci#ifdef CONFIG_PCI_MSI
237062306a36Sopenharmony_ci	if (priv->flags & TSI721_USING_MSIX) {
237162306a36Sopenharmony_ci		free_irq(priv->msix[TSI721_VECT_IMB0_RCV + mbox].vector,
237262306a36Sopenharmony_ci				(void *)priv);
237362306a36Sopenharmony_ci		free_irq(priv->msix[TSI721_VECT_IMB0_INT + mbox].vector,
237462306a36Sopenharmony_ci				(void *)priv);
237562306a36Sopenharmony_ci	}
237662306a36Sopenharmony_ci#endif /* CONFIG_PCI_MSI */
237762306a36Sopenharmony_ci
237862306a36Sopenharmony_ci	/* Clear Inbound Buffer Queue */
237962306a36Sopenharmony_ci	for (rx_slot = 0; rx_slot < priv->imsg_ring[mbox].size; rx_slot++)
238062306a36Sopenharmony_ci		priv->imsg_ring[mbox].imq_base[rx_slot] = NULL;
238162306a36Sopenharmony_ci
238262306a36Sopenharmony_ci	/* Free memory allocated for message buffers */
238362306a36Sopenharmony_ci	dma_free_coherent(&priv->pdev->dev,
238462306a36Sopenharmony_ci		priv->imsg_ring[mbox].size * TSI721_MSG_BUFFER_SIZE,
238562306a36Sopenharmony_ci		priv->imsg_ring[mbox].buf_base,
238662306a36Sopenharmony_ci		priv->imsg_ring[mbox].buf_phys);
238762306a36Sopenharmony_ci
238862306a36Sopenharmony_ci	priv->imsg_ring[mbox].buf_base = NULL;
238962306a36Sopenharmony_ci
239062306a36Sopenharmony_ci	/* Free memory allocated for free pointr list */
239162306a36Sopenharmony_ci	dma_free_coherent(&priv->pdev->dev,
239262306a36Sopenharmony_ci		priv->imsg_ring[mbox].size * 8,
239362306a36Sopenharmony_ci		priv->imsg_ring[mbox].imfq_base,
239462306a36Sopenharmony_ci		priv->imsg_ring[mbox].imfq_phys);
239562306a36Sopenharmony_ci
239662306a36Sopenharmony_ci	priv->imsg_ring[mbox].imfq_base = NULL;
239762306a36Sopenharmony_ci
239862306a36Sopenharmony_ci	/* Free memory allocated for RX descriptors */
239962306a36Sopenharmony_ci	dma_free_coherent(&priv->pdev->dev,
240062306a36Sopenharmony_ci		priv->imsg_ring[mbox].size * sizeof(struct tsi721_imsg_desc),
240162306a36Sopenharmony_ci		priv->imsg_ring[mbox].imd_base,
240262306a36Sopenharmony_ci		priv->imsg_ring[mbox].imd_phys);
240362306a36Sopenharmony_ci
240462306a36Sopenharmony_ci	priv->imsg_ring[mbox].imd_base = NULL;
240562306a36Sopenharmony_ci}
240662306a36Sopenharmony_ci
240762306a36Sopenharmony_ci/**
240862306a36Sopenharmony_ci * tsi721_add_inb_buffer - Add buffer to the Tsi721 inbound message queue
240962306a36Sopenharmony_ci * @mport: Master port implementing the Inbound Messaging Engine
241062306a36Sopenharmony_ci * @mbox: Inbound mailbox number
241162306a36Sopenharmony_ci * @buf: Buffer to add to inbound queue
241262306a36Sopenharmony_ci */
241362306a36Sopenharmony_cistatic int tsi721_add_inb_buffer(struct rio_mport *mport, int mbox, void *buf)
241462306a36Sopenharmony_ci{
241562306a36Sopenharmony_ci	struct tsi721_device *priv = mport->priv;
241662306a36Sopenharmony_ci	u32 rx_slot;
241762306a36Sopenharmony_ci	int rc = 0;
241862306a36Sopenharmony_ci
241962306a36Sopenharmony_ci	rx_slot = priv->imsg_ring[mbox].rx_slot;
242062306a36Sopenharmony_ci	if (priv->imsg_ring[mbox].imq_base[rx_slot]) {
242162306a36Sopenharmony_ci		tsi_err(&priv->pdev->dev,
242262306a36Sopenharmony_ci			"Error adding inbound buffer %d, buffer exists",
242362306a36Sopenharmony_ci			rx_slot);
242462306a36Sopenharmony_ci		rc = -EINVAL;
242562306a36Sopenharmony_ci		goto out;
242662306a36Sopenharmony_ci	}
242762306a36Sopenharmony_ci
242862306a36Sopenharmony_ci	priv->imsg_ring[mbox].imq_base[rx_slot] = buf;
242962306a36Sopenharmony_ci
243062306a36Sopenharmony_ci	if (++priv->imsg_ring[mbox].rx_slot == priv->imsg_ring[mbox].size)
243162306a36Sopenharmony_ci		priv->imsg_ring[mbox].rx_slot = 0;
243262306a36Sopenharmony_ci
243362306a36Sopenharmony_ciout:
243462306a36Sopenharmony_ci	return rc;
243562306a36Sopenharmony_ci}
243662306a36Sopenharmony_ci
243762306a36Sopenharmony_ci/**
243862306a36Sopenharmony_ci * tsi721_get_inb_message - Fetch inbound message from the Tsi721 MSG Queue
243962306a36Sopenharmony_ci * @mport: Master port implementing the Inbound Messaging Engine
244062306a36Sopenharmony_ci * @mbox: Inbound mailbox number
244162306a36Sopenharmony_ci *
244262306a36Sopenharmony_ci * Returns pointer to the message on success or NULL on failure.
244362306a36Sopenharmony_ci */
244462306a36Sopenharmony_cistatic void *tsi721_get_inb_message(struct rio_mport *mport, int mbox)
244562306a36Sopenharmony_ci{
244662306a36Sopenharmony_ci	struct tsi721_device *priv = mport->priv;
244762306a36Sopenharmony_ci	struct tsi721_imsg_desc *desc;
244862306a36Sopenharmony_ci	u32 rx_slot;
244962306a36Sopenharmony_ci	void *rx_virt = NULL;
245062306a36Sopenharmony_ci	u64 rx_phys;
245162306a36Sopenharmony_ci	void *buf = NULL;
245262306a36Sopenharmony_ci	u64 *free_ptr;
245362306a36Sopenharmony_ci	int ch = mbox + 4;
245462306a36Sopenharmony_ci	int msg_size;
245562306a36Sopenharmony_ci
245662306a36Sopenharmony_ci	if (!priv->imsg_init[mbox])
245762306a36Sopenharmony_ci		return NULL;
245862306a36Sopenharmony_ci
245962306a36Sopenharmony_ci	desc = priv->imsg_ring[mbox].imd_base;
246062306a36Sopenharmony_ci	desc += priv->imsg_ring[mbox].desc_rdptr;
246162306a36Sopenharmony_ci
246262306a36Sopenharmony_ci	if (!(le32_to_cpu(desc->msg_info) & TSI721_IMD_HO))
246362306a36Sopenharmony_ci		goto out;
246462306a36Sopenharmony_ci
246562306a36Sopenharmony_ci	rx_slot = priv->imsg_ring[mbox].rx_slot;
246662306a36Sopenharmony_ci	while (priv->imsg_ring[mbox].imq_base[rx_slot] == NULL) {
246762306a36Sopenharmony_ci		if (++rx_slot == priv->imsg_ring[mbox].size)
246862306a36Sopenharmony_ci			rx_slot = 0;
246962306a36Sopenharmony_ci	}
247062306a36Sopenharmony_ci
247162306a36Sopenharmony_ci	rx_phys = ((u64)le32_to_cpu(desc->bufptr_hi) << 32) |
247262306a36Sopenharmony_ci			le32_to_cpu(desc->bufptr_lo);
247362306a36Sopenharmony_ci
247462306a36Sopenharmony_ci	rx_virt = priv->imsg_ring[mbox].buf_base +
247562306a36Sopenharmony_ci		  (rx_phys - (u64)priv->imsg_ring[mbox].buf_phys);
247662306a36Sopenharmony_ci
247762306a36Sopenharmony_ci	buf = priv->imsg_ring[mbox].imq_base[rx_slot];
247862306a36Sopenharmony_ci	msg_size = le32_to_cpu(desc->msg_info) & TSI721_IMD_BCOUNT;
247962306a36Sopenharmony_ci	if (msg_size == 0)
248062306a36Sopenharmony_ci		msg_size = RIO_MAX_MSG_SIZE;
248162306a36Sopenharmony_ci
248262306a36Sopenharmony_ci	memcpy(buf, rx_virt, msg_size);
248362306a36Sopenharmony_ci	priv->imsg_ring[mbox].imq_base[rx_slot] = NULL;
248462306a36Sopenharmony_ci
248562306a36Sopenharmony_ci	desc->msg_info &= cpu_to_le32(~TSI721_IMD_HO);
248662306a36Sopenharmony_ci	if (++priv->imsg_ring[mbox].desc_rdptr == priv->imsg_ring[mbox].size)
248762306a36Sopenharmony_ci		priv->imsg_ring[mbox].desc_rdptr = 0;
248862306a36Sopenharmony_ci
248962306a36Sopenharmony_ci	iowrite32(priv->imsg_ring[mbox].desc_rdptr,
249062306a36Sopenharmony_ci		priv->regs + TSI721_IBDMAC_DQRP(ch));
249162306a36Sopenharmony_ci
249262306a36Sopenharmony_ci	/* Return free buffer into the pointer list */
249362306a36Sopenharmony_ci	free_ptr = priv->imsg_ring[mbox].imfq_base;
249462306a36Sopenharmony_ci	free_ptr[priv->imsg_ring[mbox].fq_wrptr] = cpu_to_le64(rx_phys);
249562306a36Sopenharmony_ci
249662306a36Sopenharmony_ci	if (++priv->imsg_ring[mbox].fq_wrptr == priv->imsg_ring[mbox].size)
249762306a36Sopenharmony_ci		priv->imsg_ring[mbox].fq_wrptr = 0;
249862306a36Sopenharmony_ci
249962306a36Sopenharmony_ci	iowrite32(priv->imsg_ring[mbox].fq_wrptr,
250062306a36Sopenharmony_ci		priv->regs + TSI721_IBDMAC_FQWP(ch));
250162306a36Sopenharmony_ciout:
250262306a36Sopenharmony_ci	return buf;
250362306a36Sopenharmony_ci}
250462306a36Sopenharmony_ci
250562306a36Sopenharmony_ci/**
250662306a36Sopenharmony_ci * tsi721_messages_init - Initialization of Messaging Engine
250762306a36Sopenharmony_ci * @priv: pointer to tsi721 private data
250862306a36Sopenharmony_ci *
250962306a36Sopenharmony_ci * Configures Tsi721 messaging engine.
251062306a36Sopenharmony_ci */
251162306a36Sopenharmony_cistatic int tsi721_messages_init(struct tsi721_device *priv)
251262306a36Sopenharmony_ci{
251362306a36Sopenharmony_ci	int	ch;
251462306a36Sopenharmony_ci
251562306a36Sopenharmony_ci	iowrite32(0, priv->regs + TSI721_SMSG_ECC_LOG);
251662306a36Sopenharmony_ci	iowrite32(0, priv->regs + TSI721_RETRY_GEN_CNT);
251762306a36Sopenharmony_ci	iowrite32(0, priv->regs + TSI721_RETRY_RX_CNT);
251862306a36Sopenharmony_ci
251962306a36Sopenharmony_ci	/* Set SRIO Message Request/Response Timeout */
252062306a36Sopenharmony_ci	iowrite32(TSI721_RQRPTO_VAL, priv->regs + TSI721_RQRPTO);
252162306a36Sopenharmony_ci
252262306a36Sopenharmony_ci	/* Initialize Inbound Messaging Engine Registers */
252362306a36Sopenharmony_ci	for (ch = 0; ch < TSI721_IMSG_CHNUM; ch++) {
252462306a36Sopenharmony_ci		/* Clear interrupt bits */
252562306a36Sopenharmony_ci		iowrite32(TSI721_IBDMAC_INT_MASK,
252662306a36Sopenharmony_ci			priv->regs + TSI721_IBDMAC_INT(ch));
252762306a36Sopenharmony_ci		/* Clear Status */
252862306a36Sopenharmony_ci		iowrite32(0, priv->regs + TSI721_IBDMAC_STS(ch));
252962306a36Sopenharmony_ci
253062306a36Sopenharmony_ci		iowrite32(TSI721_SMSG_ECC_COR_LOG_MASK,
253162306a36Sopenharmony_ci				priv->regs + TSI721_SMSG_ECC_COR_LOG(ch));
253262306a36Sopenharmony_ci		iowrite32(TSI721_SMSG_ECC_NCOR_MASK,
253362306a36Sopenharmony_ci				priv->regs + TSI721_SMSG_ECC_NCOR(ch));
253462306a36Sopenharmony_ci	}
253562306a36Sopenharmony_ci
253662306a36Sopenharmony_ci	return 0;
253762306a36Sopenharmony_ci}
253862306a36Sopenharmony_ci
253962306a36Sopenharmony_ci/**
254062306a36Sopenharmony_ci * tsi721_query_mport - Fetch inbound message from the Tsi721 MSG Queue
254162306a36Sopenharmony_ci * @mport: Master port implementing the Inbound Messaging Engine
254262306a36Sopenharmony_ci * @mbox: Inbound mailbox number
254362306a36Sopenharmony_ci *
254462306a36Sopenharmony_ci * Returns pointer to the message on success or NULL on failure.
254562306a36Sopenharmony_ci */
254662306a36Sopenharmony_cistatic int tsi721_query_mport(struct rio_mport *mport,
254762306a36Sopenharmony_ci			      struct rio_mport_attr *attr)
254862306a36Sopenharmony_ci{
254962306a36Sopenharmony_ci	struct tsi721_device *priv = mport->priv;
255062306a36Sopenharmony_ci	u32 rval;
255162306a36Sopenharmony_ci
255262306a36Sopenharmony_ci	rval = ioread32(priv->regs + 0x100 + RIO_PORT_N_ERR_STS_CSR(0, 0));
255362306a36Sopenharmony_ci	if (rval & RIO_PORT_N_ERR_STS_PORT_OK) {
255462306a36Sopenharmony_ci		rval = ioread32(priv->regs + 0x100 + RIO_PORT_N_CTL2_CSR(0, 0));
255562306a36Sopenharmony_ci		attr->link_speed = (rval & RIO_PORT_N_CTL2_SEL_BAUD) >> 28;
255662306a36Sopenharmony_ci		rval = ioread32(priv->regs + 0x100 + RIO_PORT_N_CTL_CSR(0, 0));
255762306a36Sopenharmony_ci		attr->link_width = (rval & RIO_PORT_N_CTL_IPW) >> 27;
255862306a36Sopenharmony_ci	} else
255962306a36Sopenharmony_ci		attr->link_speed = RIO_LINK_DOWN;
256062306a36Sopenharmony_ci
256162306a36Sopenharmony_ci#ifdef CONFIG_RAPIDIO_DMA_ENGINE
256262306a36Sopenharmony_ci	attr->flags = RIO_MPORT_DMA | RIO_MPORT_DMA_SG;
256362306a36Sopenharmony_ci	attr->dma_max_sge = 0;
256462306a36Sopenharmony_ci	attr->dma_max_size = TSI721_BDMA_MAX_BCOUNT;
256562306a36Sopenharmony_ci	attr->dma_align = 0;
256662306a36Sopenharmony_ci#else
256762306a36Sopenharmony_ci	attr->flags = 0;
256862306a36Sopenharmony_ci#endif
256962306a36Sopenharmony_ci	return 0;
257062306a36Sopenharmony_ci}
257162306a36Sopenharmony_ci
257262306a36Sopenharmony_ci/**
257362306a36Sopenharmony_ci * tsi721_disable_ints - disables all device interrupts
257462306a36Sopenharmony_ci * @priv: pointer to tsi721 private data
257562306a36Sopenharmony_ci */
257662306a36Sopenharmony_cistatic void tsi721_disable_ints(struct tsi721_device *priv)
257762306a36Sopenharmony_ci{
257862306a36Sopenharmony_ci	int ch;
257962306a36Sopenharmony_ci
258062306a36Sopenharmony_ci	/* Disable all device level interrupts */
258162306a36Sopenharmony_ci	iowrite32(0, priv->regs + TSI721_DEV_INTE);
258262306a36Sopenharmony_ci
258362306a36Sopenharmony_ci	/* Disable all Device Channel interrupts */
258462306a36Sopenharmony_ci	iowrite32(0, priv->regs + TSI721_DEV_CHAN_INTE);
258562306a36Sopenharmony_ci
258662306a36Sopenharmony_ci	/* Disable all Inbound Msg Channel interrupts */
258762306a36Sopenharmony_ci	for (ch = 0; ch < TSI721_IMSG_CHNUM; ch++)
258862306a36Sopenharmony_ci		iowrite32(0, priv->regs + TSI721_IBDMAC_INTE(ch));
258962306a36Sopenharmony_ci
259062306a36Sopenharmony_ci	/* Disable all Outbound Msg Channel interrupts */
259162306a36Sopenharmony_ci	for (ch = 0; ch < TSI721_OMSG_CHNUM; ch++)
259262306a36Sopenharmony_ci		iowrite32(0, priv->regs + TSI721_OBDMAC_INTE(ch));
259362306a36Sopenharmony_ci
259462306a36Sopenharmony_ci	/* Disable all general messaging interrupts */
259562306a36Sopenharmony_ci	iowrite32(0, priv->regs + TSI721_SMSG_INTE);
259662306a36Sopenharmony_ci
259762306a36Sopenharmony_ci	/* Disable all BDMA Channel interrupts */
259862306a36Sopenharmony_ci	for (ch = 0; ch < TSI721_DMA_MAXCH; ch++)
259962306a36Sopenharmony_ci		iowrite32(0,
260062306a36Sopenharmony_ci			priv->regs + TSI721_DMAC_BASE(ch) + TSI721_DMAC_INTE);
260162306a36Sopenharmony_ci
260262306a36Sopenharmony_ci	/* Disable all general BDMA interrupts */
260362306a36Sopenharmony_ci	iowrite32(0, priv->regs + TSI721_BDMA_INTE);
260462306a36Sopenharmony_ci
260562306a36Sopenharmony_ci	/* Disable all SRIO Channel interrupts */
260662306a36Sopenharmony_ci	for (ch = 0; ch < TSI721_SRIO_MAXCH; ch++)
260762306a36Sopenharmony_ci		iowrite32(0, priv->regs + TSI721_SR_CHINTE(ch));
260862306a36Sopenharmony_ci
260962306a36Sopenharmony_ci	/* Disable all general SR2PC interrupts */
261062306a36Sopenharmony_ci	iowrite32(0, priv->regs + TSI721_SR2PC_GEN_INTE);
261162306a36Sopenharmony_ci
261262306a36Sopenharmony_ci	/* Disable all PC2SR interrupts */
261362306a36Sopenharmony_ci	iowrite32(0, priv->regs + TSI721_PC2SR_INTE);
261462306a36Sopenharmony_ci
261562306a36Sopenharmony_ci	/* Disable all I2C interrupts */
261662306a36Sopenharmony_ci	iowrite32(0, priv->regs + TSI721_I2C_INT_ENABLE);
261762306a36Sopenharmony_ci
261862306a36Sopenharmony_ci	/* Disable SRIO MAC interrupts */
261962306a36Sopenharmony_ci	iowrite32(0, priv->regs + TSI721_RIO_EM_INT_ENABLE);
262062306a36Sopenharmony_ci	iowrite32(0, priv->regs + TSI721_RIO_EM_DEV_INT_EN);
262162306a36Sopenharmony_ci}
262262306a36Sopenharmony_ci
262362306a36Sopenharmony_cistatic struct rio_ops tsi721_rio_ops = {
262462306a36Sopenharmony_ci	.lcread			= tsi721_lcread,
262562306a36Sopenharmony_ci	.lcwrite		= tsi721_lcwrite,
262662306a36Sopenharmony_ci	.cread			= tsi721_cread_dma,
262762306a36Sopenharmony_ci	.cwrite			= tsi721_cwrite_dma,
262862306a36Sopenharmony_ci	.dsend			= tsi721_dsend,
262962306a36Sopenharmony_ci	.open_inb_mbox		= tsi721_open_inb_mbox,
263062306a36Sopenharmony_ci	.close_inb_mbox		= tsi721_close_inb_mbox,
263162306a36Sopenharmony_ci	.open_outb_mbox		= tsi721_open_outb_mbox,
263262306a36Sopenharmony_ci	.close_outb_mbox	= tsi721_close_outb_mbox,
263362306a36Sopenharmony_ci	.add_outb_message	= tsi721_add_outb_message,
263462306a36Sopenharmony_ci	.add_inb_buffer		= tsi721_add_inb_buffer,
263562306a36Sopenharmony_ci	.get_inb_message	= tsi721_get_inb_message,
263662306a36Sopenharmony_ci	.map_inb		= tsi721_rio_map_inb_mem,
263762306a36Sopenharmony_ci	.unmap_inb		= tsi721_rio_unmap_inb_mem,
263862306a36Sopenharmony_ci	.pwenable		= tsi721_pw_enable,
263962306a36Sopenharmony_ci	.query_mport		= tsi721_query_mport,
264062306a36Sopenharmony_ci	.map_outb		= tsi721_map_outb_win,
264162306a36Sopenharmony_ci	.unmap_outb		= tsi721_unmap_outb_win,
264262306a36Sopenharmony_ci};
264362306a36Sopenharmony_ci
264462306a36Sopenharmony_cistatic void tsi721_mport_release(struct device *dev)
264562306a36Sopenharmony_ci{
264662306a36Sopenharmony_ci	struct rio_mport *mport = to_rio_mport(dev);
264762306a36Sopenharmony_ci
264862306a36Sopenharmony_ci	tsi_debug(EXIT, dev, "%s id=%d", mport->name, mport->id);
264962306a36Sopenharmony_ci}
265062306a36Sopenharmony_ci
265162306a36Sopenharmony_ci/**
265262306a36Sopenharmony_ci * tsi721_setup_mport - Setup Tsi721 as RapidIO subsystem master port
265362306a36Sopenharmony_ci * @priv: pointer to tsi721 private data
265462306a36Sopenharmony_ci *
265562306a36Sopenharmony_ci * Configures Tsi721 as RapidIO master port.
265662306a36Sopenharmony_ci */
265762306a36Sopenharmony_cistatic int tsi721_setup_mport(struct tsi721_device *priv)
265862306a36Sopenharmony_ci{
265962306a36Sopenharmony_ci	struct pci_dev *pdev = priv->pdev;
266062306a36Sopenharmony_ci	int err = 0;
266162306a36Sopenharmony_ci	struct rio_mport *mport = &priv->mport;
266262306a36Sopenharmony_ci
266362306a36Sopenharmony_ci	err = rio_mport_initialize(mport);
266462306a36Sopenharmony_ci	if (err)
266562306a36Sopenharmony_ci		return err;
266662306a36Sopenharmony_ci
266762306a36Sopenharmony_ci	mport->ops = &tsi721_rio_ops;
266862306a36Sopenharmony_ci	mport->index = 0;
266962306a36Sopenharmony_ci	mport->sys_size = 0; /* small system */
267062306a36Sopenharmony_ci	mport->priv = (void *)priv;
267162306a36Sopenharmony_ci	mport->phys_efptr = 0x100;
267262306a36Sopenharmony_ci	mport->phys_rmap = 1;
267362306a36Sopenharmony_ci	mport->dev.parent = &pdev->dev;
267462306a36Sopenharmony_ci	mport->dev.release = tsi721_mport_release;
267562306a36Sopenharmony_ci
267662306a36Sopenharmony_ci	INIT_LIST_HEAD(&mport->dbells);
267762306a36Sopenharmony_ci
267862306a36Sopenharmony_ci	rio_init_dbell_res(&mport->riores[RIO_DOORBELL_RESOURCE], 0, 0xffff);
267962306a36Sopenharmony_ci	rio_init_mbox_res(&mport->riores[RIO_INB_MBOX_RESOURCE], 0, 3);
268062306a36Sopenharmony_ci	rio_init_mbox_res(&mport->riores[RIO_OUTB_MBOX_RESOURCE], 0, 3);
268162306a36Sopenharmony_ci	snprintf(mport->name, RIO_MAX_MPORT_NAME, "%s(%s)",
268262306a36Sopenharmony_ci		 dev_driver_string(&pdev->dev), dev_name(&pdev->dev));
268362306a36Sopenharmony_ci
268462306a36Sopenharmony_ci	/* Hook up interrupt handler */
268562306a36Sopenharmony_ci
268662306a36Sopenharmony_ci#ifdef CONFIG_PCI_MSI
268762306a36Sopenharmony_ci	if (!tsi721_enable_msix(priv))
268862306a36Sopenharmony_ci		priv->flags |= TSI721_USING_MSIX;
268962306a36Sopenharmony_ci	else if (!pci_enable_msi(pdev))
269062306a36Sopenharmony_ci		priv->flags |= TSI721_USING_MSI;
269162306a36Sopenharmony_ci	else
269262306a36Sopenharmony_ci		tsi_debug(MPORT, &pdev->dev,
269362306a36Sopenharmony_ci			 "MSI/MSI-X is not available. Using legacy INTx.");
269462306a36Sopenharmony_ci#endif /* CONFIG_PCI_MSI */
269562306a36Sopenharmony_ci
269662306a36Sopenharmony_ci	err = tsi721_request_irq(priv);
269762306a36Sopenharmony_ci
269862306a36Sopenharmony_ci	if (err) {
269962306a36Sopenharmony_ci		tsi_err(&pdev->dev, "Unable to get PCI IRQ %02X (err=0x%x)",
270062306a36Sopenharmony_ci			pdev->irq, err);
270162306a36Sopenharmony_ci		return err;
270262306a36Sopenharmony_ci	}
270362306a36Sopenharmony_ci
270462306a36Sopenharmony_ci#ifdef CONFIG_RAPIDIO_DMA_ENGINE
270562306a36Sopenharmony_ci	err = tsi721_register_dma(priv);
270662306a36Sopenharmony_ci	if (err)
270762306a36Sopenharmony_ci		goto err_exit;
270862306a36Sopenharmony_ci#endif
270962306a36Sopenharmony_ci	/* Enable SRIO link */
271062306a36Sopenharmony_ci	iowrite32(ioread32(priv->regs + TSI721_DEVCTL) |
271162306a36Sopenharmony_ci		  TSI721_DEVCTL_SRBOOT_CMPL,
271262306a36Sopenharmony_ci		  priv->regs + TSI721_DEVCTL);
271362306a36Sopenharmony_ci
271462306a36Sopenharmony_ci	if (mport->host_deviceid >= 0)
271562306a36Sopenharmony_ci		iowrite32(RIO_PORT_GEN_HOST | RIO_PORT_GEN_MASTER |
271662306a36Sopenharmony_ci			  RIO_PORT_GEN_DISCOVERED,
271762306a36Sopenharmony_ci			  priv->regs + (0x100 + RIO_PORT_GEN_CTL_CSR));
271862306a36Sopenharmony_ci	else
271962306a36Sopenharmony_ci		iowrite32(0, priv->regs + (0x100 + RIO_PORT_GEN_CTL_CSR));
272062306a36Sopenharmony_ci
272162306a36Sopenharmony_ci	err = rio_register_mport(mport);
272262306a36Sopenharmony_ci	if (err) {
272362306a36Sopenharmony_ci		tsi721_unregister_dma(priv);
272462306a36Sopenharmony_ci		goto err_exit;
272562306a36Sopenharmony_ci	}
272662306a36Sopenharmony_ci
272762306a36Sopenharmony_ci	return 0;
272862306a36Sopenharmony_ci
272962306a36Sopenharmony_cierr_exit:
273062306a36Sopenharmony_ci	tsi721_free_irq(priv);
273162306a36Sopenharmony_ci	return err;
273262306a36Sopenharmony_ci}
273362306a36Sopenharmony_ci
273462306a36Sopenharmony_cistatic int tsi721_probe(struct pci_dev *pdev,
273562306a36Sopenharmony_ci				  const struct pci_device_id *id)
273662306a36Sopenharmony_ci{
273762306a36Sopenharmony_ci	struct tsi721_device *priv;
273862306a36Sopenharmony_ci	int err;
273962306a36Sopenharmony_ci
274062306a36Sopenharmony_ci	priv = kzalloc(sizeof(struct tsi721_device), GFP_KERNEL);
274162306a36Sopenharmony_ci	if (!priv) {
274262306a36Sopenharmony_ci		err = -ENOMEM;
274362306a36Sopenharmony_ci		goto err_exit;
274462306a36Sopenharmony_ci	}
274562306a36Sopenharmony_ci
274662306a36Sopenharmony_ci	err = pci_enable_device(pdev);
274762306a36Sopenharmony_ci	if (err) {
274862306a36Sopenharmony_ci		tsi_err(&pdev->dev, "Failed to enable PCI device");
274962306a36Sopenharmony_ci		goto err_clean;
275062306a36Sopenharmony_ci	}
275162306a36Sopenharmony_ci
275262306a36Sopenharmony_ci	priv->pdev = pdev;
275362306a36Sopenharmony_ci
275462306a36Sopenharmony_ci#ifdef DEBUG
275562306a36Sopenharmony_ci	{
275662306a36Sopenharmony_ci		int i;
275762306a36Sopenharmony_ci
275862306a36Sopenharmony_ci		for (i = 0; i < PCI_STD_NUM_BARS; i++) {
275962306a36Sopenharmony_ci			tsi_debug(INIT, &pdev->dev, "res%d %pR",
276062306a36Sopenharmony_ci				  i, &pdev->resource[i]);
276162306a36Sopenharmony_ci		}
276262306a36Sopenharmony_ci	}
276362306a36Sopenharmony_ci#endif
276462306a36Sopenharmony_ci	/*
276562306a36Sopenharmony_ci	 * Verify BAR configuration
276662306a36Sopenharmony_ci	 */
276762306a36Sopenharmony_ci
276862306a36Sopenharmony_ci	/* BAR_0 (registers) must be 512KB+ in 32-bit address space */
276962306a36Sopenharmony_ci	if (!(pci_resource_flags(pdev, BAR_0) & IORESOURCE_MEM) ||
277062306a36Sopenharmony_ci	    pci_resource_flags(pdev, BAR_0) & IORESOURCE_MEM_64 ||
277162306a36Sopenharmony_ci	    pci_resource_len(pdev, BAR_0) < TSI721_REG_SPACE_SIZE) {
277262306a36Sopenharmony_ci		tsi_err(&pdev->dev, "Missing or misconfigured CSR BAR0");
277362306a36Sopenharmony_ci		err = -ENODEV;
277462306a36Sopenharmony_ci		goto err_disable_pdev;
277562306a36Sopenharmony_ci	}
277662306a36Sopenharmony_ci
277762306a36Sopenharmony_ci	/* BAR_1 (outbound doorbells) must be 16MB+ in 32-bit address space */
277862306a36Sopenharmony_ci	if (!(pci_resource_flags(pdev, BAR_1) & IORESOURCE_MEM) ||
277962306a36Sopenharmony_ci	    pci_resource_flags(pdev, BAR_1) & IORESOURCE_MEM_64 ||
278062306a36Sopenharmony_ci	    pci_resource_len(pdev, BAR_1) < TSI721_DB_WIN_SIZE) {
278162306a36Sopenharmony_ci		tsi_err(&pdev->dev, "Missing or misconfigured Doorbell BAR1");
278262306a36Sopenharmony_ci		err = -ENODEV;
278362306a36Sopenharmony_ci		goto err_disable_pdev;
278462306a36Sopenharmony_ci	}
278562306a36Sopenharmony_ci
278662306a36Sopenharmony_ci	/*
278762306a36Sopenharmony_ci	 * BAR_2 and BAR_4 (outbound translation) must be in 64-bit PCIe address
278862306a36Sopenharmony_ci	 * space.
278962306a36Sopenharmony_ci	 * NOTE: BAR_2 and BAR_4 are not used by this version of driver.
279062306a36Sopenharmony_ci	 * It may be a good idea to keep them disabled using HW configuration
279162306a36Sopenharmony_ci	 * to save PCI memory space.
279262306a36Sopenharmony_ci	 */
279362306a36Sopenharmony_ci
279462306a36Sopenharmony_ci	priv->p2r_bar[0].size = priv->p2r_bar[1].size = 0;
279562306a36Sopenharmony_ci
279662306a36Sopenharmony_ci	if (pci_resource_flags(pdev, BAR_2) & IORESOURCE_MEM_64) {
279762306a36Sopenharmony_ci		if (pci_resource_flags(pdev, BAR_2) & IORESOURCE_PREFETCH)
279862306a36Sopenharmony_ci			tsi_debug(INIT, &pdev->dev,
279962306a36Sopenharmony_ci				 "Prefetchable OBW BAR2 will not be used");
280062306a36Sopenharmony_ci		else {
280162306a36Sopenharmony_ci			priv->p2r_bar[0].base = pci_resource_start(pdev, BAR_2);
280262306a36Sopenharmony_ci			priv->p2r_bar[0].size = pci_resource_len(pdev, BAR_2);
280362306a36Sopenharmony_ci		}
280462306a36Sopenharmony_ci	}
280562306a36Sopenharmony_ci
280662306a36Sopenharmony_ci	if (pci_resource_flags(pdev, BAR_4) & IORESOURCE_MEM_64) {
280762306a36Sopenharmony_ci		if (pci_resource_flags(pdev, BAR_4) & IORESOURCE_PREFETCH)
280862306a36Sopenharmony_ci			tsi_debug(INIT, &pdev->dev,
280962306a36Sopenharmony_ci				 "Prefetchable OBW BAR4 will not be used");
281062306a36Sopenharmony_ci		else {
281162306a36Sopenharmony_ci			priv->p2r_bar[1].base = pci_resource_start(pdev, BAR_4);
281262306a36Sopenharmony_ci			priv->p2r_bar[1].size = pci_resource_len(pdev, BAR_4);
281362306a36Sopenharmony_ci		}
281462306a36Sopenharmony_ci	}
281562306a36Sopenharmony_ci
281662306a36Sopenharmony_ci	err = pci_request_regions(pdev, DRV_NAME);
281762306a36Sopenharmony_ci	if (err) {
281862306a36Sopenharmony_ci		tsi_err(&pdev->dev, "Unable to obtain PCI resources");
281962306a36Sopenharmony_ci		goto err_disable_pdev;
282062306a36Sopenharmony_ci	}
282162306a36Sopenharmony_ci
282262306a36Sopenharmony_ci	pci_set_master(pdev);
282362306a36Sopenharmony_ci
282462306a36Sopenharmony_ci	priv->regs = pci_ioremap_bar(pdev, BAR_0);
282562306a36Sopenharmony_ci	if (!priv->regs) {
282662306a36Sopenharmony_ci		tsi_err(&pdev->dev, "Unable to map device registers space");
282762306a36Sopenharmony_ci		err = -ENOMEM;
282862306a36Sopenharmony_ci		goto err_free_res;
282962306a36Sopenharmony_ci	}
283062306a36Sopenharmony_ci
283162306a36Sopenharmony_ci	priv->odb_base = pci_ioremap_bar(pdev, BAR_1);
283262306a36Sopenharmony_ci	if (!priv->odb_base) {
283362306a36Sopenharmony_ci		tsi_err(&pdev->dev, "Unable to map outbound doorbells space");
283462306a36Sopenharmony_ci		err = -ENOMEM;
283562306a36Sopenharmony_ci		goto err_unmap_bars;
283662306a36Sopenharmony_ci	}
283762306a36Sopenharmony_ci
283862306a36Sopenharmony_ci	/* Configure DMA attributes. */
283962306a36Sopenharmony_ci	if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(64))) {
284062306a36Sopenharmony_ci		err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
284162306a36Sopenharmony_ci		if (err) {
284262306a36Sopenharmony_ci			tsi_err(&pdev->dev, "Unable to set DMA mask");
284362306a36Sopenharmony_ci			goto err_unmap_bars;
284462306a36Sopenharmony_ci		}
284562306a36Sopenharmony_ci
284662306a36Sopenharmony_ci		if (dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)))
284762306a36Sopenharmony_ci			tsi_info(&pdev->dev, "Unable to set consistent DMA mask");
284862306a36Sopenharmony_ci	} else {
284962306a36Sopenharmony_ci		err = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64));
285062306a36Sopenharmony_ci		if (err)
285162306a36Sopenharmony_ci			tsi_info(&pdev->dev, "Unable to set consistent DMA mask");
285262306a36Sopenharmony_ci	}
285362306a36Sopenharmony_ci
285462306a36Sopenharmony_ci	BUG_ON(!pci_is_pcie(pdev));
285562306a36Sopenharmony_ci
285662306a36Sopenharmony_ci	/* Clear "no snoop" and "relaxed ordering" bits. */
285762306a36Sopenharmony_ci	pcie_capability_clear_and_set_word(pdev, PCI_EXP_DEVCTL,
285862306a36Sopenharmony_ci		PCI_EXP_DEVCTL_RELAX_EN | PCI_EXP_DEVCTL_NOSNOOP_EN, 0);
285962306a36Sopenharmony_ci
286062306a36Sopenharmony_ci	/* Override PCIe Maximum Read Request Size setting if requested */
286162306a36Sopenharmony_ci	if (pcie_mrrs >= 0) {
286262306a36Sopenharmony_ci		if (pcie_mrrs <= 5)
286362306a36Sopenharmony_ci			pcie_capability_clear_and_set_word(pdev, PCI_EXP_DEVCTL,
286462306a36Sopenharmony_ci					PCI_EXP_DEVCTL_READRQ, pcie_mrrs << 12);
286562306a36Sopenharmony_ci		else
286662306a36Sopenharmony_ci			tsi_info(&pdev->dev,
286762306a36Sopenharmony_ci				 "Invalid MRRS override value %d", pcie_mrrs);
286862306a36Sopenharmony_ci	}
286962306a36Sopenharmony_ci
287062306a36Sopenharmony_ci	/* Set PCIe completion timeout to 1-10ms */
287162306a36Sopenharmony_ci	pcie_capability_clear_and_set_word(pdev, PCI_EXP_DEVCTL2,
287262306a36Sopenharmony_ci					   PCI_EXP_DEVCTL2_COMP_TIMEOUT, 0x2);
287362306a36Sopenharmony_ci
287462306a36Sopenharmony_ci	/*
287562306a36Sopenharmony_ci	 * FIXUP: correct offsets of MSI-X tables in the MSI-X Capability Block
287662306a36Sopenharmony_ci	 */
287762306a36Sopenharmony_ci	pci_write_config_dword(pdev, TSI721_PCIECFG_EPCTL, 0x01);
287862306a36Sopenharmony_ci	pci_write_config_dword(pdev, TSI721_PCIECFG_MSIXTBL,
287962306a36Sopenharmony_ci						TSI721_MSIXTBL_OFFSET);
288062306a36Sopenharmony_ci	pci_write_config_dword(pdev, TSI721_PCIECFG_MSIXPBA,
288162306a36Sopenharmony_ci						TSI721_MSIXPBA_OFFSET);
288262306a36Sopenharmony_ci	pci_write_config_dword(pdev, TSI721_PCIECFG_EPCTL, 0);
288362306a36Sopenharmony_ci	/* End of FIXUP */
288462306a36Sopenharmony_ci
288562306a36Sopenharmony_ci	tsi721_disable_ints(priv);
288662306a36Sopenharmony_ci
288762306a36Sopenharmony_ci	tsi721_init_pc2sr_mapping(priv);
288862306a36Sopenharmony_ci	tsi721_init_sr2pc_mapping(priv);
288962306a36Sopenharmony_ci
289062306a36Sopenharmony_ci	if (tsi721_bdma_maint_init(priv)) {
289162306a36Sopenharmony_ci		tsi_err(&pdev->dev, "BDMA initialization failed");
289262306a36Sopenharmony_ci		err = -ENOMEM;
289362306a36Sopenharmony_ci		goto err_unmap_bars;
289462306a36Sopenharmony_ci	}
289562306a36Sopenharmony_ci
289662306a36Sopenharmony_ci	err = tsi721_doorbell_init(priv);
289762306a36Sopenharmony_ci	if (err)
289862306a36Sopenharmony_ci		goto err_free_bdma;
289962306a36Sopenharmony_ci
290062306a36Sopenharmony_ci	tsi721_port_write_init(priv);
290162306a36Sopenharmony_ci
290262306a36Sopenharmony_ci	err = tsi721_messages_init(priv);
290362306a36Sopenharmony_ci	if (err)
290462306a36Sopenharmony_ci		goto err_free_consistent;
290562306a36Sopenharmony_ci
290662306a36Sopenharmony_ci	err = tsi721_setup_mport(priv);
290762306a36Sopenharmony_ci	if (err)
290862306a36Sopenharmony_ci		goto err_free_consistent;
290962306a36Sopenharmony_ci
291062306a36Sopenharmony_ci	pci_set_drvdata(pdev, priv);
291162306a36Sopenharmony_ci	tsi721_interrupts_init(priv);
291262306a36Sopenharmony_ci
291362306a36Sopenharmony_ci	return 0;
291462306a36Sopenharmony_ci
291562306a36Sopenharmony_cierr_free_consistent:
291662306a36Sopenharmony_ci	tsi721_port_write_free(priv);
291762306a36Sopenharmony_ci	tsi721_doorbell_free(priv);
291862306a36Sopenharmony_cierr_free_bdma:
291962306a36Sopenharmony_ci	tsi721_bdma_maint_free(priv);
292062306a36Sopenharmony_cierr_unmap_bars:
292162306a36Sopenharmony_ci	if (priv->regs)
292262306a36Sopenharmony_ci		iounmap(priv->regs);
292362306a36Sopenharmony_ci	if (priv->odb_base)
292462306a36Sopenharmony_ci		iounmap(priv->odb_base);
292562306a36Sopenharmony_cierr_free_res:
292662306a36Sopenharmony_ci	pci_release_regions(pdev);
292762306a36Sopenharmony_cierr_disable_pdev:
292862306a36Sopenharmony_ci	pci_disable_device(pdev);
292962306a36Sopenharmony_cierr_clean:
293062306a36Sopenharmony_ci	kfree(priv);
293162306a36Sopenharmony_cierr_exit:
293262306a36Sopenharmony_ci	return err;
293362306a36Sopenharmony_ci}
293462306a36Sopenharmony_ci
293562306a36Sopenharmony_cistatic void tsi721_remove(struct pci_dev *pdev)
293662306a36Sopenharmony_ci{
293762306a36Sopenharmony_ci	struct tsi721_device *priv = pci_get_drvdata(pdev);
293862306a36Sopenharmony_ci
293962306a36Sopenharmony_ci	tsi_debug(EXIT, &pdev->dev, "enter");
294062306a36Sopenharmony_ci
294162306a36Sopenharmony_ci	tsi721_disable_ints(priv);
294262306a36Sopenharmony_ci	tsi721_free_irq(priv);
294362306a36Sopenharmony_ci	flush_work(&priv->idb_work);
294462306a36Sopenharmony_ci	flush_work(&priv->pw_work);
294562306a36Sopenharmony_ci	rio_unregister_mport(&priv->mport);
294662306a36Sopenharmony_ci
294762306a36Sopenharmony_ci	tsi721_unregister_dma(priv);
294862306a36Sopenharmony_ci	tsi721_bdma_maint_free(priv);
294962306a36Sopenharmony_ci	tsi721_doorbell_free(priv);
295062306a36Sopenharmony_ci	tsi721_port_write_free(priv);
295162306a36Sopenharmony_ci	tsi721_close_sr2pc_mapping(priv);
295262306a36Sopenharmony_ci
295362306a36Sopenharmony_ci	if (priv->regs)
295462306a36Sopenharmony_ci		iounmap(priv->regs);
295562306a36Sopenharmony_ci	if (priv->odb_base)
295662306a36Sopenharmony_ci		iounmap(priv->odb_base);
295762306a36Sopenharmony_ci#ifdef CONFIG_PCI_MSI
295862306a36Sopenharmony_ci	if (priv->flags & TSI721_USING_MSIX)
295962306a36Sopenharmony_ci		pci_disable_msix(priv->pdev);
296062306a36Sopenharmony_ci	else if (priv->flags & TSI721_USING_MSI)
296162306a36Sopenharmony_ci		pci_disable_msi(priv->pdev);
296262306a36Sopenharmony_ci#endif
296362306a36Sopenharmony_ci	pci_release_regions(pdev);
296462306a36Sopenharmony_ci	pci_disable_device(pdev);
296562306a36Sopenharmony_ci	pci_set_drvdata(pdev, NULL);
296662306a36Sopenharmony_ci	kfree(priv);
296762306a36Sopenharmony_ci	tsi_debug(EXIT, &pdev->dev, "exit");
296862306a36Sopenharmony_ci}
296962306a36Sopenharmony_ci
297062306a36Sopenharmony_cistatic void tsi721_shutdown(struct pci_dev *pdev)
297162306a36Sopenharmony_ci{
297262306a36Sopenharmony_ci	struct tsi721_device *priv = pci_get_drvdata(pdev);
297362306a36Sopenharmony_ci
297462306a36Sopenharmony_ci	tsi_debug(EXIT, &pdev->dev, "enter");
297562306a36Sopenharmony_ci
297662306a36Sopenharmony_ci	tsi721_disable_ints(priv);
297762306a36Sopenharmony_ci	tsi721_dma_stop_all(priv);
297862306a36Sopenharmony_ci	pci_disable_device(pdev);
297962306a36Sopenharmony_ci}
298062306a36Sopenharmony_ci
298162306a36Sopenharmony_cistatic const struct pci_device_id tsi721_pci_tbl[] = {
298262306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_IDT, PCI_DEVICE_ID_TSI721) },
298362306a36Sopenharmony_ci	{ 0, }	/* terminate list */
298462306a36Sopenharmony_ci};
298562306a36Sopenharmony_ci
298662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, tsi721_pci_tbl);
298762306a36Sopenharmony_ci
298862306a36Sopenharmony_cistatic struct pci_driver tsi721_driver = {
298962306a36Sopenharmony_ci	.name		= "tsi721",
299062306a36Sopenharmony_ci	.id_table	= tsi721_pci_tbl,
299162306a36Sopenharmony_ci	.probe		= tsi721_probe,
299262306a36Sopenharmony_ci	.remove		= tsi721_remove,
299362306a36Sopenharmony_ci	.shutdown	= tsi721_shutdown,
299462306a36Sopenharmony_ci};
299562306a36Sopenharmony_ci
299662306a36Sopenharmony_cimodule_pci_driver(tsi721_driver);
299762306a36Sopenharmony_ci
299862306a36Sopenharmony_ciMODULE_DESCRIPTION("IDT Tsi721 PCIExpress-to-SRIO bridge driver");
299962306a36Sopenharmony_ciMODULE_AUTHOR("Integrated Device Technology, Inc.");
300062306a36Sopenharmony_ciMODULE_LICENSE("GPL");
3001