18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * RapidIO mport driver for Tsi721 PCIExpress-to-SRIO bridge
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2011 Integrated Device Technology, Inc.
68c2ecf20Sopenharmony_ci * Alexandre Bounine <alexandre.bounine@idt.com>
78c2ecf20Sopenharmony_ci * Chul Kim <chul.kim@idt.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/io.h>
118c2ecf20Sopenharmony_ci#include <linux/errno.h>
128c2ecf20Sopenharmony_ci#include <linux/init.h>
138c2ecf20Sopenharmony_ci#include <linux/ioport.h>
148c2ecf20Sopenharmony_ci#include <linux/kernel.h>
158c2ecf20Sopenharmony_ci#include <linux/module.h>
168c2ecf20Sopenharmony_ci#include <linux/pci.h>
178c2ecf20Sopenharmony_ci#include <linux/rio.h>
188c2ecf20Sopenharmony_ci#include <linux/rio_drv.h>
198c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
208c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
218c2ecf20Sopenharmony_ci#include <linux/kfifo.h>
228c2ecf20Sopenharmony_ci#include <linux/delay.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include "tsi721.h"
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#ifdef DEBUG
278c2ecf20Sopenharmony_ciu32 tsi_dbg_level;
288c2ecf20Sopenharmony_cimodule_param_named(dbg_level, tsi_dbg_level, uint, S_IWUSR | S_IRUGO);
298c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dbg_level, "Debugging output level (default 0 = none)");
308c2ecf20Sopenharmony_ci#endif
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic int pcie_mrrs = -1;
338c2ecf20Sopenharmony_cimodule_param(pcie_mrrs, int, S_IRUGO);
348c2ecf20Sopenharmony_ciMODULE_PARM_DESC(pcie_mrrs, "PCIe MRRS override value (0...5)");
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic u8 mbox_sel = 0x0f;
378c2ecf20Sopenharmony_cimodule_param(mbox_sel, byte, S_IRUGO);
388c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mbox_sel,
398c2ecf20Sopenharmony_ci		 "RIO Messaging MBOX Selection Mask (default: 0x0f = all)");
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(tsi721_maint_lock);
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic void tsi721_omsg_handler(struct tsi721_device *priv, int ch);
448c2ecf20Sopenharmony_cistatic void tsi721_imsg_handler(struct tsi721_device *priv, int ch);
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci/**
478c2ecf20Sopenharmony_ci * tsi721_lcread - read from local SREP config space
488c2ecf20Sopenharmony_ci * @mport: RapidIO master port info
498c2ecf20Sopenharmony_ci * @index: ID of RapdiIO interface
508c2ecf20Sopenharmony_ci * @offset: Offset into configuration space
518c2ecf20Sopenharmony_ci * @len: Length (in bytes) of the maintenance transaction
528c2ecf20Sopenharmony_ci * @data: Value to be read into
538c2ecf20Sopenharmony_ci *
548c2ecf20Sopenharmony_ci * Generates a local SREP space read. Returns %0 on
558c2ecf20Sopenharmony_ci * success or %-EINVAL on failure.
568c2ecf20Sopenharmony_ci */
578c2ecf20Sopenharmony_cistatic int tsi721_lcread(struct rio_mport *mport, int index, u32 offset,
588c2ecf20Sopenharmony_ci			 int len, u32 *data)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	struct tsi721_device *priv = mport->priv;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	if (len != sizeof(u32))
638c2ecf20Sopenharmony_ci		return -EINVAL; /* only 32-bit access is supported */
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	*data = ioread32(priv->regs + offset);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	return 0;
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci/**
718c2ecf20Sopenharmony_ci * tsi721_lcwrite - write into local SREP config space
728c2ecf20Sopenharmony_ci * @mport: RapidIO master port info
738c2ecf20Sopenharmony_ci * @index: ID of RapdiIO interface
748c2ecf20Sopenharmony_ci * @offset: Offset into configuration space
758c2ecf20Sopenharmony_ci * @len: Length (in bytes) of the maintenance transaction
768c2ecf20Sopenharmony_ci * @data: Value to be written
778c2ecf20Sopenharmony_ci *
788c2ecf20Sopenharmony_ci * Generates a local write into SREP configuration space. Returns %0 on
798c2ecf20Sopenharmony_ci * success or %-EINVAL on failure.
808c2ecf20Sopenharmony_ci */
818c2ecf20Sopenharmony_cistatic int tsi721_lcwrite(struct rio_mport *mport, int index, u32 offset,
828c2ecf20Sopenharmony_ci			  int len, u32 data)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	struct tsi721_device *priv = mport->priv;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	if (len != sizeof(u32))
878c2ecf20Sopenharmony_ci		return -EINVAL; /* only 32-bit access is supported */
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	iowrite32(data, priv->regs + offset);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	return 0;
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci/**
958c2ecf20Sopenharmony_ci * tsi721_maint_dma - Helper function to generate RapidIO maintenance
968c2ecf20Sopenharmony_ci *                    transactions using designated Tsi721 DMA channel.
978c2ecf20Sopenharmony_ci * @priv: pointer to tsi721 private data
988c2ecf20Sopenharmony_ci * @sys_size: RapdiIO transport system size
998c2ecf20Sopenharmony_ci * @destid: Destination ID of transaction
1008c2ecf20Sopenharmony_ci * @hopcount: Number of hops to target device
1018c2ecf20Sopenharmony_ci * @offset: Offset into configuration space
1028c2ecf20Sopenharmony_ci * @len: Length (in bytes) of the maintenance transaction
1038c2ecf20Sopenharmony_ci * @data: Location to be read from or write into
1048c2ecf20Sopenharmony_ci * @do_wr: Operation flag (1 == MAINT_WR)
1058c2ecf20Sopenharmony_ci *
1068c2ecf20Sopenharmony_ci * Generates a RapidIO maintenance transaction (Read or Write).
1078c2ecf20Sopenharmony_ci * Returns %0 on success and %-EINVAL or %-EFAULT on failure.
1088c2ecf20Sopenharmony_ci */
1098c2ecf20Sopenharmony_cistatic int tsi721_maint_dma(struct tsi721_device *priv, u32 sys_size,
1108c2ecf20Sopenharmony_ci			u16 destid, u8 hopcount, u32 offset, int len,
1118c2ecf20Sopenharmony_ci			u32 *data, int do_wr)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	void __iomem *regs = priv->regs + TSI721_DMAC_BASE(priv->mdma.ch_id);
1148c2ecf20Sopenharmony_ci	struct tsi721_dma_desc *bd_ptr;
1158c2ecf20Sopenharmony_ci	u32 rd_count, swr_ptr, ch_stat;
1168c2ecf20Sopenharmony_ci	unsigned long flags;
1178c2ecf20Sopenharmony_ci	int i, err = 0;
1188c2ecf20Sopenharmony_ci	u32 op = do_wr ? MAINT_WR : MAINT_RD;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	if (offset > (RIO_MAINT_SPACE_SZ - len) || (len != sizeof(u32)))
1218c2ecf20Sopenharmony_ci		return -EINVAL;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	spin_lock_irqsave(&tsi721_maint_lock, flags);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	bd_ptr = priv->mdma.bd_base;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	rd_count = ioread32(regs + TSI721_DMAC_DRDCNT);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	/* Initialize DMA descriptor */
1308c2ecf20Sopenharmony_ci	bd_ptr[0].type_id = cpu_to_le32((DTYPE2 << 29) | (op << 19) | destid);
1318c2ecf20Sopenharmony_ci	bd_ptr[0].bcount = cpu_to_le32((sys_size << 26) | 0x04);
1328c2ecf20Sopenharmony_ci	bd_ptr[0].raddr_lo = cpu_to_le32((hopcount << 24) | offset);
1338c2ecf20Sopenharmony_ci	bd_ptr[0].raddr_hi = 0;
1348c2ecf20Sopenharmony_ci	if (do_wr)
1358c2ecf20Sopenharmony_ci		bd_ptr[0].data[0] = cpu_to_be32p(data);
1368c2ecf20Sopenharmony_ci	else
1378c2ecf20Sopenharmony_ci		bd_ptr[0].data[0] = 0xffffffff;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	mb();
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	/* Start DMA operation */
1428c2ecf20Sopenharmony_ci	iowrite32(rd_count + 2,	regs + TSI721_DMAC_DWRCNT);
1438c2ecf20Sopenharmony_ci	ioread32(regs + TSI721_DMAC_DWRCNT);
1448c2ecf20Sopenharmony_ci	i = 0;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	/* Wait until DMA transfer is finished */
1478c2ecf20Sopenharmony_ci	while ((ch_stat = ioread32(regs + TSI721_DMAC_STS))
1488c2ecf20Sopenharmony_ci							& TSI721_DMAC_STS_RUN) {
1498c2ecf20Sopenharmony_ci		udelay(1);
1508c2ecf20Sopenharmony_ci		if (++i >= 5000000) {
1518c2ecf20Sopenharmony_ci			tsi_debug(MAINT, &priv->pdev->dev,
1528c2ecf20Sopenharmony_ci				"DMA[%d] read timeout ch_status=%x",
1538c2ecf20Sopenharmony_ci				priv->mdma.ch_id, ch_stat);
1548c2ecf20Sopenharmony_ci			if (!do_wr)
1558c2ecf20Sopenharmony_ci				*data = 0xffffffff;
1568c2ecf20Sopenharmony_ci			err = -EIO;
1578c2ecf20Sopenharmony_ci			goto err_out;
1588c2ecf20Sopenharmony_ci		}
1598c2ecf20Sopenharmony_ci	}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	if (ch_stat & TSI721_DMAC_STS_ABORT) {
1628c2ecf20Sopenharmony_ci		/* If DMA operation aborted due to error,
1638c2ecf20Sopenharmony_ci		 * reinitialize DMA channel
1648c2ecf20Sopenharmony_ci		 */
1658c2ecf20Sopenharmony_ci		tsi_debug(MAINT, &priv->pdev->dev, "DMA ABORT ch_stat=%x",
1668c2ecf20Sopenharmony_ci			  ch_stat);
1678c2ecf20Sopenharmony_ci		tsi_debug(MAINT, &priv->pdev->dev,
1688c2ecf20Sopenharmony_ci			  "OP=%d : destid=%x hc=%x off=%x",
1698c2ecf20Sopenharmony_ci			  do_wr ? MAINT_WR : MAINT_RD,
1708c2ecf20Sopenharmony_ci			  destid, hopcount, offset);
1718c2ecf20Sopenharmony_ci		iowrite32(TSI721_DMAC_INT_ALL, regs + TSI721_DMAC_INT);
1728c2ecf20Sopenharmony_ci		iowrite32(TSI721_DMAC_CTL_INIT, regs + TSI721_DMAC_CTL);
1738c2ecf20Sopenharmony_ci		udelay(10);
1748c2ecf20Sopenharmony_ci		iowrite32(0, regs + TSI721_DMAC_DWRCNT);
1758c2ecf20Sopenharmony_ci		udelay(1);
1768c2ecf20Sopenharmony_ci		if (!do_wr)
1778c2ecf20Sopenharmony_ci			*data = 0xffffffff;
1788c2ecf20Sopenharmony_ci		err = -EIO;
1798c2ecf20Sopenharmony_ci		goto err_out;
1808c2ecf20Sopenharmony_ci	}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	if (!do_wr)
1838c2ecf20Sopenharmony_ci		*data = be32_to_cpu(bd_ptr[0].data[0]);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	/*
1868c2ecf20Sopenharmony_ci	 * Update descriptor status FIFO RD pointer.
1878c2ecf20Sopenharmony_ci	 * NOTE: Skipping check and clear FIFO entries because we are waiting
1888c2ecf20Sopenharmony_ci	 * for transfer to be completed.
1898c2ecf20Sopenharmony_ci	 */
1908c2ecf20Sopenharmony_ci	swr_ptr = ioread32(regs + TSI721_DMAC_DSWP);
1918c2ecf20Sopenharmony_ci	iowrite32(swr_ptr, regs + TSI721_DMAC_DSRP);
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_cierr_out:
1948c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&tsi721_maint_lock, flags);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	return err;
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci/**
2008c2ecf20Sopenharmony_ci * tsi721_cread_dma - Generate a RapidIO maintenance read transaction
2018c2ecf20Sopenharmony_ci *                    using Tsi721 BDMA engine.
2028c2ecf20Sopenharmony_ci * @mport: RapidIO master port control structure
2038c2ecf20Sopenharmony_ci * @index: ID of RapdiIO interface
2048c2ecf20Sopenharmony_ci * @destid: Destination ID of transaction
2058c2ecf20Sopenharmony_ci * @hopcount: Number of hops to target device
2068c2ecf20Sopenharmony_ci * @offset: Offset into configuration space
2078c2ecf20Sopenharmony_ci * @len: Length (in bytes) of the maintenance transaction
2088c2ecf20Sopenharmony_ci * @val: Location to be read into
2098c2ecf20Sopenharmony_ci *
2108c2ecf20Sopenharmony_ci * Generates a RapidIO maintenance read transaction.
2118c2ecf20Sopenharmony_ci * Returns %0 on success and %-EINVAL or %-EFAULT on failure.
2128c2ecf20Sopenharmony_ci */
2138c2ecf20Sopenharmony_cistatic int tsi721_cread_dma(struct rio_mport *mport, int index, u16 destid,
2148c2ecf20Sopenharmony_ci			u8 hopcount, u32 offset, int len, u32 *data)
2158c2ecf20Sopenharmony_ci{
2168c2ecf20Sopenharmony_ci	struct tsi721_device *priv = mport->priv;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	return tsi721_maint_dma(priv, mport->sys_size, destid, hopcount,
2198c2ecf20Sopenharmony_ci				offset, len, data, 0);
2208c2ecf20Sopenharmony_ci}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci/**
2238c2ecf20Sopenharmony_ci * tsi721_cwrite_dma - Generate a RapidIO maintenance write transaction
2248c2ecf20Sopenharmony_ci *                     using Tsi721 BDMA engine
2258c2ecf20Sopenharmony_ci * @mport: RapidIO master port control structure
2268c2ecf20Sopenharmony_ci * @index: ID of RapdiIO interface
2278c2ecf20Sopenharmony_ci * @destid: Destination ID of transaction
2288c2ecf20Sopenharmony_ci * @hopcount: Number of hops to target device
2298c2ecf20Sopenharmony_ci * @offset: Offset into configuration space
2308c2ecf20Sopenharmony_ci * @len: Length (in bytes) of the maintenance transaction
2318c2ecf20Sopenharmony_ci * @val: Value to be written
2328c2ecf20Sopenharmony_ci *
2338c2ecf20Sopenharmony_ci * Generates a RapidIO maintenance write transaction.
2348c2ecf20Sopenharmony_ci * Returns %0 on success and %-EINVAL or %-EFAULT on failure.
2358c2ecf20Sopenharmony_ci */
2368c2ecf20Sopenharmony_cistatic int tsi721_cwrite_dma(struct rio_mport *mport, int index, u16 destid,
2378c2ecf20Sopenharmony_ci			 u8 hopcount, u32 offset, int len, u32 data)
2388c2ecf20Sopenharmony_ci{
2398c2ecf20Sopenharmony_ci	struct tsi721_device *priv = mport->priv;
2408c2ecf20Sopenharmony_ci	u32 temp = data;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	return tsi721_maint_dma(priv, mport->sys_size, destid, hopcount,
2438c2ecf20Sopenharmony_ci				offset, len, &temp, 1);
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci/**
2478c2ecf20Sopenharmony_ci * tsi721_pw_handler - Tsi721 inbound port-write interrupt handler
2488c2ecf20Sopenharmony_ci * @priv:  tsi721 device private structure
2498c2ecf20Sopenharmony_ci *
2508c2ecf20Sopenharmony_ci * Handles inbound port-write interrupts. Copies PW message from an internal
2518c2ecf20Sopenharmony_ci * buffer into PW message FIFO and schedules deferred routine to process
2528c2ecf20Sopenharmony_ci * queued messages.
2538c2ecf20Sopenharmony_ci */
2548c2ecf20Sopenharmony_cistatic int
2558c2ecf20Sopenharmony_citsi721_pw_handler(struct tsi721_device *priv)
2568c2ecf20Sopenharmony_ci{
2578c2ecf20Sopenharmony_ci	u32 pw_stat;
2588c2ecf20Sopenharmony_ci	u32 pw_buf[TSI721_RIO_PW_MSG_SIZE/sizeof(u32)];
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	pw_stat = ioread32(priv->regs + TSI721_RIO_PW_RX_STAT);
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	if (pw_stat & TSI721_RIO_PW_RX_STAT_PW_VAL) {
2648c2ecf20Sopenharmony_ci		pw_buf[0] = ioread32(priv->regs + TSI721_RIO_PW_RX_CAPT(0));
2658c2ecf20Sopenharmony_ci		pw_buf[1] = ioread32(priv->regs + TSI721_RIO_PW_RX_CAPT(1));
2668c2ecf20Sopenharmony_ci		pw_buf[2] = ioread32(priv->regs + TSI721_RIO_PW_RX_CAPT(2));
2678c2ecf20Sopenharmony_ci		pw_buf[3] = ioread32(priv->regs + TSI721_RIO_PW_RX_CAPT(3));
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci		/* Queue PW message (if there is room in FIFO),
2708c2ecf20Sopenharmony_ci		 * otherwise discard it.
2718c2ecf20Sopenharmony_ci		 */
2728c2ecf20Sopenharmony_ci		spin_lock(&priv->pw_fifo_lock);
2738c2ecf20Sopenharmony_ci		if (kfifo_avail(&priv->pw_fifo) >= TSI721_RIO_PW_MSG_SIZE)
2748c2ecf20Sopenharmony_ci			kfifo_in(&priv->pw_fifo, pw_buf,
2758c2ecf20Sopenharmony_ci						TSI721_RIO_PW_MSG_SIZE);
2768c2ecf20Sopenharmony_ci		else
2778c2ecf20Sopenharmony_ci			priv->pw_discard_count++;
2788c2ecf20Sopenharmony_ci		spin_unlock(&priv->pw_fifo_lock);
2798c2ecf20Sopenharmony_ci	}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	/* Clear pending PW interrupts */
2828c2ecf20Sopenharmony_ci	iowrite32(TSI721_RIO_PW_RX_STAT_PW_DISC | TSI721_RIO_PW_RX_STAT_PW_VAL,
2838c2ecf20Sopenharmony_ci		  priv->regs + TSI721_RIO_PW_RX_STAT);
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	schedule_work(&priv->pw_work);
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	return 0;
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_cistatic void tsi721_pw_dpc(struct work_struct *work)
2918c2ecf20Sopenharmony_ci{
2928c2ecf20Sopenharmony_ci	struct tsi721_device *priv = container_of(work, struct tsi721_device,
2938c2ecf20Sopenharmony_ci						    pw_work);
2948c2ecf20Sopenharmony_ci	union rio_pw_msg pwmsg;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	/*
2978c2ecf20Sopenharmony_ci	 * Process port-write messages
2988c2ecf20Sopenharmony_ci	 */
2998c2ecf20Sopenharmony_ci	while (kfifo_out_spinlocked(&priv->pw_fifo, (unsigned char *)&pwmsg,
3008c2ecf20Sopenharmony_ci			 TSI721_RIO_PW_MSG_SIZE, &priv->pw_fifo_lock)) {
3018c2ecf20Sopenharmony_ci		/* Pass the port-write message to RIO core for processing */
3028c2ecf20Sopenharmony_ci		rio_inb_pwrite_handler(&priv->mport, &pwmsg);
3038c2ecf20Sopenharmony_ci	}
3048c2ecf20Sopenharmony_ci}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci/**
3078c2ecf20Sopenharmony_ci * tsi721_pw_enable - enable/disable port-write interface init
3088c2ecf20Sopenharmony_ci * @mport: Master port implementing the port write unit
3098c2ecf20Sopenharmony_ci * @enable:    1=enable; 0=disable port-write message handling
3108c2ecf20Sopenharmony_ci */
3118c2ecf20Sopenharmony_cistatic int tsi721_pw_enable(struct rio_mport *mport, int enable)
3128c2ecf20Sopenharmony_ci{
3138c2ecf20Sopenharmony_ci	struct tsi721_device *priv = mport->priv;
3148c2ecf20Sopenharmony_ci	u32 rval;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	rval = ioread32(priv->regs + TSI721_RIO_EM_INT_ENABLE);
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	if (enable)
3198c2ecf20Sopenharmony_ci		rval |= TSI721_RIO_EM_INT_ENABLE_PW_RX;
3208c2ecf20Sopenharmony_ci	else
3218c2ecf20Sopenharmony_ci		rval &= ~TSI721_RIO_EM_INT_ENABLE_PW_RX;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	/* Clear pending PW interrupts */
3248c2ecf20Sopenharmony_ci	iowrite32(TSI721_RIO_PW_RX_STAT_PW_DISC | TSI721_RIO_PW_RX_STAT_PW_VAL,
3258c2ecf20Sopenharmony_ci		  priv->regs + TSI721_RIO_PW_RX_STAT);
3268c2ecf20Sopenharmony_ci	/* Update enable bits */
3278c2ecf20Sopenharmony_ci	iowrite32(rval, priv->regs + TSI721_RIO_EM_INT_ENABLE);
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	return 0;
3308c2ecf20Sopenharmony_ci}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci/**
3338c2ecf20Sopenharmony_ci * tsi721_dsend - Send a RapidIO doorbell
3348c2ecf20Sopenharmony_ci * @mport: RapidIO master port info
3358c2ecf20Sopenharmony_ci * @index: ID of RapidIO interface
3368c2ecf20Sopenharmony_ci * @destid: Destination ID of target device
3378c2ecf20Sopenharmony_ci * @data: 16-bit info field of RapidIO doorbell
3388c2ecf20Sopenharmony_ci *
3398c2ecf20Sopenharmony_ci * Sends a RapidIO doorbell message. Always returns %0.
3408c2ecf20Sopenharmony_ci */
3418c2ecf20Sopenharmony_cistatic int tsi721_dsend(struct rio_mport *mport, int index,
3428c2ecf20Sopenharmony_ci			u16 destid, u16 data)
3438c2ecf20Sopenharmony_ci{
3448c2ecf20Sopenharmony_ci	struct tsi721_device *priv = mport->priv;
3458c2ecf20Sopenharmony_ci	u32 offset;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	offset = (((mport->sys_size) ? RIO_TT_CODE_16 : RIO_TT_CODE_8) << 18) |
3488c2ecf20Sopenharmony_ci		 (destid << 2);
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	tsi_debug(DBELL, &priv->pdev->dev,
3518c2ecf20Sopenharmony_ci		  "Send Doorbell 0x%04x to destID 0x%x", data, destid);
3528c2ecf20Sopenharmony_ci	iowrite16be(data, priv->odb_base + offset);
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	return 0;
3558c2ecf20Sopenharmony_ci}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci/**
3588c2ecf20Sopenharmony_ci * tsi721_dbell_handler - Tsi721 doorbell interrupt handler
3598c2ecf20Sopenharmony_ci * @priv: tsi721 device-specific data structure
3608c2ecf20Sopenharmony_ci *
3618c2ecf20Sopenharmony_ci * Handles inbound doorbell interrupts. Copies doorbell entry from an internal
3628c2ecf20Sopenharmony_ci * buffer into DB message FIFO and schedules deferred  routine to process
3638c2ecf20Sopenharmony_ci * queued DBs.
3648c2ecf20Sopenharmony_ci */
3658c2ecf20Sopenharmony_cistatic int
3668c2ecf20Sopenharmony_citsi721_dbell_handler(struct tsi721_device *priv)
3678c2ecf20Sopenharmony_ci{
3688c2ecf20Sopenharmony_ci	u32 regval;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	/* Disable IDB interrupts */
3718c2ecf20Sopenharmony_ci	regval = ioread32(priv->regs + TSI721_SR_CHINTE(IDB_QUEUE));
3728c2ecf20Sopenharmony_ci	regval &= ~TSI721_SR_CHINT_IDBQRCV;
3738c2ecf20Sopenharmony_ci	iowrite32(regval,
3748c2ecf20Sopenharmony_ci		priv->regs + TSI721_SR_CHINTE(IDB_QUEUE));
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	schedule_work(&priv->idb_work);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	return 0;
3798c2ecf20Sopenharmony_ci}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_cistatic void tsi721_db_dpc(struct work_struct *work)
3828c2ecf20Sopenharmony_ci{
3838c2ecf20Sopenharmony_ci	struct tsi721_device *priv = container_of(work, struct tsi721_device,
3848c2ecf20Sopenharmony_ci						    idb_work);
3858c2ecf20Sopenharmony_ci	struct rio_mport *mport;
3868c2ecf20Sopenharmony_ci	struct rio_dbell *dbell;
3878c2ecf20Sopenharmony_ci	int found = 0;
3888c2ecf20Sopenharmony_ci	u32 wr_ptr, rd_ptr;
3898c2ecf20Sopenharmony_ci	u64 *idb_entry;
3908c2ecf20Sopenharmony_ci	u32 regval;
3918c2ecf20Sopenharmony_ci	union {
3928c2ecf20Sopenharmony_ci		u64 msg;
3938c2ecf20Sopenharmony_ci		u8  bytes[8];
3948c2ecf20Sopenharmony_ci	} idb;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	/*
3978c2ecf20Sopenharmony_ci	 * Process queued inbound doorbells
3988c2ecf20Sopenharmony_ci	 */
3998c2ecf20Sopenharmony_ci	mport = &priv->mport;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	wr_ptr = ioread32(priv->regs + TSI721_IDQ_WP(IDB_QUEUE)) % IDB_QSIZE;
4028c2ecf20Sopenharmony_ci	rd_ptr = ioread32(priv->regs + TSI721_IDQ_RP(IDB_QUEUE)) % IDB_QSIZE;
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	while (wr_ptr != rd_ptr) {
4058c2ecf20Sopenharmony_ci		idb_entry = (u64 *)(priv->idb_base +
4068c2ecf20Sopenharmony_ci					(TSI721_IDB_ENTRY_SIZE * rd_ptr));
4078c2ecf20Sopenharmony_ci		rd_ptr++;
4088c2ecf20Sopenharmony_ci		rd_ptr %= IDB_QSIZE;
4098c2ecf20Sopenharmony_ci		idb.msg = *idb_entry;
4108c2ecf20Sopenharmony_ci		*idb_entry = 0;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci		/* Process one doorbell */
4138c2ecf20Sopenharmony_ci		list_for_each_entry(dbell, &mport->dbells, node) {
4148c2ecf20Sopenharmony_ci			if ((dbell->res->start <= DBELL_INF(idb.bytes)) &&
4158c2ecf20Sopenharmony_ci			    (dbell->res->end >= DBELL_INF(idb.bytes))) {
4168c2ecf20Sopenharmony_ci				found = 1;
4178c2ecf20Sopenharmony_ci				break;
4188c2ecf20Sopenharmony_ci			}
4198c2ecf20Sopenharmony_ci		}
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci		if (found) {
4228c2ecf20Sopenharmony_ci			dbell->dinb(mport, dbell->dev_id, DBELL_SID(idb.bytes),
4238c2ecf20Sopenharmony_ci				    DBELL_TID(idb.bytes), DBELL_INF(idb.bytes));
4248c2ecf20Sopenharmony_ci		} else {
4258c2ecf20Sopenharmony_ci			tsi_debug(DBELL, &priv->pdev->dev,
4268c2ecf20Sopenharmony_ci				  "spurious IDB sid %2.2x tid %2.2x info %4.4x",
4278c2ecf20Sopenharmony_ci				  DBELL_SID(idb.bytes), DBELL_TID(idb.bytes),
4288c2ecf20Sopenharmony_ci				  DBELL_INF(idb.bytes));
4298c2ecf20Sopenharmony_ci		}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci		wr_ptr = ioread32(priv->regs +
4328c2ecf20Sopenharmony_ci				  TSI721_IDQ_WP(IDB_QUEUE)) % IDB_QSIZE;
4338c2ecf20Sopenharmony_ci	}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	iowrite32(rd_ptr & (IDB_QSIZE - 1),
4368c2ecf20Sopenharmony_ci		priv->regs + TSI721_IDQ_RP(IDB_QUEUE));
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	/* Re-enable IDB interrupts */
4398c2ecf20Sopenharmony_ci	regval = ioread32(priv->regs + TSI721_SR_CHINTE(IDB_QUEUE));
4408c2ecf20Sopenharmony_ci	regval |= TSI721_SR_CHINT_IDBQRCV;
4418c2ecf20Sopenharmony_ci	iowrite32(regval,
4428c2ecf20Sopenharmony_ci		priv->regs + TSI721_SR_CHINTE(IDB_QUEUE));
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	wr_ptr = ioread32(priv->regs + TSI721_IDQ_WP(IDB_QUEUE)) % IDB_QSIZE;
4458c2ecf20Sopenharmony_ci	if (wr_ptr != rd_ptr)
4468c2ecf20Sopenharmony_ci		schedule_work(&priv->idb_work);
4478c2ecf20Sopenharmony_ci}
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci/**
4508c2ecf20Sopenharmony_ci * tsi721_irqhandler - Tsi721 interrupt handler
4518c2ecf20Sopenharmony_ci * @irq: Linux interrupt number
4528c2ecf20Sopenharmony_ci * @ptr: Pointer to interrupt-specific data (tsi721_device structure)
4538c2ecf20Sopenharmony_ci *
4548c2ecf20Sopenharmony_ci * Handles Tsi721 interrupts signaled using MSI and INTA. Checks reported
4558c2ecf20Sopenharmony_ci * interrupt events and calls an event-specific handler(s).
4568c2ecf20Sopenharmony_ci */
4578c2ecf20Sopenharmony_cistatic irqreturn_t tsi721_irqhandler(int irq, void *ptr)
4588c2ecf20Sopenharmony_ci{
4598c2ecf20Sopenharmony_ci	struct tsi721_device *priv = (struct tsi721_device *)ptr;
4608c2ecf20Sopenharmony_ci	u32 dev_int;
4618c2ecf20Sopenharmony_ci	u32 dev_ch_int;
4628c2ecf20Sopenharmony_ci	u32 intval;
4638c2ecf20Sopenharmony_ci	u32 ch_inte;
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	/* For MSI mode disable all device-level interrupts */
4668c2ecf20Sopenharmony_ci	if (priv->flags & TSI721_USING_MSI)
4678c2ecf20Sopenharmony_ci		iowrite32(0, priv->regs + TSI721_DEV_INTE);
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	dev_int = ioread32(priv->regs + TSI721_DEV_INT);
4708c2ecf20Sopenharmony_ci	if (!dev_int)
4718c2ecf20Sopenharmony_ci		return IRQ_NONE;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	dev_ch_int = ioread32(priv->regs + TSI721_DEV_CHAN_INT);
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	if (dev_int & TSI721_DEV_INT_SR2PC_CH) {
4768c2ecf20Sopenharmony_ci		/* Service SR2PC Channel interrupts */
4778c2ecf20Sopenharmony_ci		if (dev_ch_int & TSI721_INT_SR2PC_CHAN(IDB_QUEUE)) {
4788c2ecf20Sopenharmony_ci			/* Service Inbound Doorbell interrupt */
4798c2ecf20Sopenharmony_ci			intval = ioread32(priv->regs +
4808c2ecf20Sopenharmony_ci						TSI721_SR_CHINT(IDB_QUEUE));
4818c2ecf20Sopenharmony_ci			if (intval & TSI721_SR_CHINT_IDBQRCV)
4828c2ecf20Sopenharmony_ci				tsi721_dbell_handler(priv);
4838c2ecf20Sopenharmony_ci			else
4848c2ecf20Sopenharmony_ci				tsi_info(&priv->pdev->dev,
4858c2ecf20Sopenharmony_ci					"Unsupported SR_CH_INT %x", intval);
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci			/* Clear interrupts */
4888c2ecf20Sopenharmony_ci			iowrite32(intval,
4898c2ecf20Sopenharmony_ci				priv->regs + TSI721_SR_CHINT(IDB_QUEUE));
4908c2ecf20Sopenharmony_ci			ioread32(priv->regs + TSI721_SR_CHINT(IDB_QUEUE));
4918c2ecf20Sopenharmony_ci		}
4928c2ecf20Sopenharmony_ci	}
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	if (dev_int & TSI721_DEV_INT_SMSG_CH) {
4958c2ecf20Sopenharmony_ci		int ch;
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci		/*
4988c2ecf20Sopenharmony_ci		 * Service channel interrupts from Messaging Engine
4998c2ecf20Sopenharmony_ci		 */
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci		if (dev_ch_int & TSI721_INT_IMSG_CHAN_M) { /* Inbound Msg */
5028c2ecf20Sopenharmony_ci			/* Disable signaled OB MSG Channel interrupts */
5038c2ecf20Sopenharmony_ci			ch_inte = ioread32(priv->regs + TSI721_DEV_CHAN_INTE);
5048c2ecf20Sopenharmony_ci			ch_inte &= ~(dev_ch_int & TSI721_INT_IMSG_CHAN_M);
5058c2ecf20Sopenharmony_ci			iowrite32(ch_inte, priv->regs + TSI721_DEV_CHAN_INTE);
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci			/*
5088c2ecf20Sopenharmony_ci			 * Process Inbound Message interrupt for each MBOX
5098c2ecf20Sopenharmony_ci			 */
5108c2ecf20Sopenharmony_ci			for (ch = 4; ch < RIO_MAX_MBOX + 4; ch++) {
5118c2ecf20Sopenharmony_ci				if (!(dev_ch_int & TSI721_INT_IMSG_CHAN(ch)))
5128c2ecf20Sopenharmony_ci					continue;
5138c2ecf20Sopenharmony_ci				tsi721_imsg_handler(priv, ch);
5148c2ecf20Sopenharmony_ci			}
5158c2ecf20Sopenharmony_ci		}
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci		if (dev_ch_int & TSI721_INT_OMSG_CHAN_M) { /* Outbound Msg */
5188c2ecf20Sopenharmony_ci			/* Disable signaled OB MSG Channel interrupts */
5198c2ecf20Sopenharmony_ci			ch_inte = ioread32(priv->regs + TSI721_DEV_CHAN_INTE);
5208c2ecf20Sopenharmony_ci			ch_inte &= ~(dev_ch_int & TSI721_INT_OMSG_CHAN_M);
5218c2ecf20Sopenharmony_ci			iowrite32(ch_inte, priv->regs + TSI721_DEV_CHAN_INTE);
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci			/*
5248c2ecf20Sopenharmony_ci			 * Process Outbound Message interrupts for each MBOX
5258c2ecf20Sopenharmony_ci			 */
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci			for (ch = 0; ch < RIO_MAX_MBOX; ch++) {
5288c2ecf20Sopenharmony_ci				if (!(dev_ch_int & TSI721_INT_OMSG_CHAN(ch)))
5298c2ecf20Sopenharmony_ci					continue;
5308c2ecf20Sopenharmony_ci				tsi721_omsg_handler(priv, ch);
5318c2ecf20Sopenharmony_ci			}
5328c2ecf20Sopenharmony_ci		}
5338c2ecf20Sopenharmony_ci	}
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	if (dev_int & TSI721_DEV_INT_SRIO) {
5368c2ecf20Sopenharmony_ci		/* Service SRIO MAC interrupts */
5378c2ecf20Sopenharmony_ci		intval = ioread32(priv->regs + TSI721_RIO_EM_INT_STAT);
5388c2ecf20Sopenharmony_ci		if (intval & TSI721_RIO_EM_INT_STAT_PW_RX)
5398c2ecf20Sopenharmony_ci			tsi721_pw_handler(priv);
5408c2ecf20Sopenharmony_ci	}
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci#ifdef CONFIG_RAPIDIO_DMA_ENGINE
5438c2ecf20Sopenharmony_ci	if (dev_int & TSI721_DEV_INT_BDMA_CH) {
5448c2ecf20Sopenharmony_ci		int ch;
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci		if (dev_ch_int & TSI721_INT_BDMA_CHAN_M) {
5478c2ecf20Sopenharmony_ci			tsi_debug(DMA, &priv->pdev->dev,
5488c2ecf20Sopenharmony_ci				  "IRQ from DMA channel 0x%08x", dev_ch_int);
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci			for (ch = 0; ch < TSI721_DMA_MAXCH; ch++) {
5518c2ecf20Sopenharmony_ci				if (!(dev_ch_int & TSI721_INT_BDMA_CHAN(ch)))
5528c2ecf20Sopenharmony_ci					continue;
5538c2ecf20Sopenharmony_ci				tsi721_bdma_handler(&priv->bdma[ch]);
5548c2ecf20Sopenharmony_ci			}
5558c2ecf20Sopenharmony_ci		}
5568c2ecf20Sopenharmony_ci	}
5578c2ecf20Sopenharmony_ci#endif
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	/* For MSI mode re-enable device-level interrupts */
5608c2ecf20Sopenharmony_ci	if (priv->flags & TSI721_USING_MSI) {
5618c2ecf20Sopenharmony_ci		dev_int = TSI721_DEV_INT_SR2PC_CH | TSI721_DEV_INT_SRIO |
5628c2ecf20Sopenharmony_ci			TSI721_DEV_INT_SMSG_CH | TSI721_DEV_INT_BDMA_CH;
5638c2ecf20Sopenharmony_ci		iowrite32(dev_int, priv->regs + TSI721_DEV_INTE);
5648c2ecf20Sopenharmony_ci	}
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
5678c2ecf20Sopenharmony_ci}
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_cistatic void tsi721_interrupts_init(struct tsi721_device *priv)
5708c2ecf20Sopenharmony_ci{
5718c2ecf20Sopenharmony_ci	u32 intr;
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	/* Enable IDB interrupts */
5748c2ecf20Sopenharmony_ci	iowrite32(TSI721_SR_CHINT_ALL,
5758c2ecf20Sopenharmony_ci		priv->regs + TSI721_SR_CHINT(IDB_QUEUE));
5768c2ecf20Sopenharmony_ci	iowrite32(TSI721_SR_CHINT_IDBQRCV,
5778c2ecf20Sopenharmony_ci		priv->regs + TSI721_SR_CHINTE(IDB_QUEUE));
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	/* Enable SRIO MAC interrupts */
5808c2ecf20Sopenharmony_ci	iowrite32(TSI721_RIO_EM_DEV_INT_EN_INT,
5818c2ecf20Sopenharmony_ci		priv->regs + TSI721_RIO_EM_DEV_INT_EN);
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	/* Enable interrupts from channels in use */
5848c2ecf20Sopenharmony_ci#ifdef CONFIG_RAPIDIO_DMA_ENGINE
5858c2ecf20Sopenharmony_ci	intr = TSI721_INT_SR2PC_CHAN(IDB_QUEUE) |
5868c2ecf20Sopenharmony_ci		(TSI721_INT_BDMA_CHAN_M &
5878c2ecf20Sopenharmony_ci		 ~TSI721_INT_BDMA_CHAN(TSI721_DMACH_MAINT));
5888c2ecf20Sopenharmony_ci#else
5898c2ecf20Sopenharmony_ci	intr = TSI721_INT_SR2PC_CHAN(IDB_QUEUE);
5908c2ecf20Sopenharmony_ci#endif
5918c2ecf20Sopenharmony_ci	iowrite32(intr,	priv->regs + TSI721_DEV_CHAN_INTE);
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	if (priv->flags & TSI721_USING_MSIX)
5948c2ecf20Sopenharmony_ci		intr = TSI721_DEV_INT_SRIO;
5958c2ecf20Sopenharmony_ci	else
5968c2ecf20Sopenharmony_ci		intr = TSI721_DEV_INT_SR2PC_CH | TSI721_DEV_INT_SRIO |
5978c2ecf20Sopenharmony_ci			TSI721_DEV_INT_SMSG_CH | TSI721_DEV_INT_BDMA_CH;
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	iowrite32(intr, priv->regs + TSI721_DEV_INTE);
6008c2ecf20Sopenharmony_ci	ioread32(priv->regs + TSI721_DEV_INTE);
6018c2ecf20Sopenharmony_ci}
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI_MSI
6048c2ecf20Sopenharmony_ci/**
6058c2ecf20Sopenharmony_ci * tsi721_omsg_msix - MSI-X interrupt handler for outbound messaging
6068c2ecf20Sopenharmony_ci * @irq: Linux interrupt number
6078c2ecf20Sopenharmony_ci * @ptr: Pointer to interrupt-specific data (tsi721_device structure)
6088c2ecf20Sopenharmony_ci *
6098c2ecf20Sopenharmony_ci * Handles outbound messaging interrupts signaled using MSI-X.
6108c2ecf20Sopenharmony_ci */
6118c2ecf20Sopenharmony_cistatic irqreturn_t tsi721_omsg_msix(int irq, void *ptr)
6128c2ecf20Sopenharmony_ci{
6138c2ecf20Sopenharmony_ci	struct tsi721_device *priv = (struct tsi721_device *)ptr;
6148c2ecf20Sopenharmony_ci	int mbox;
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	mbox = (irq - priv->msix[TSI721_VECT_OMB0_DONE].vector) % RIO_MAX_MBOX;
6178c2ecf20Sopenharmony_ci	tsi721_omsg_handler(priv, mbox);
6188c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
6198c2ecf20Sopenharmony_ci}
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci/**
6228c2ecf20Sopenharmony_ci * tsi721_imsg_msix - MSI-X interrupt handler for inbound messaging
6238c2ecf20Sopenharmony_ci * @irq: Linux interrupt number
6248c2ecf20Sopenharmony_ci * @ptr: Pointer to interrupt-specific data (tsi721_device structure)
6258c2ecf20Sopenharmony_ci *
6268c2ecf20Sopenharmony_ci * Handles inbound messaging interrupts signaled using MSI-X.
6278c2ecf20Sopenharmony_ci */
6288c2ecf20Sopenharmony_cistatic irqreturn_t tsi721_imsg_msix(int irq, void *ptr)
6298c2ecf20Sopenharmony_ci{
6308c2ecf20Sopenharmony_ci	struct tsi721_device *priv = (struct tsi721_device *)ptr;
6318c2ecf20Sopenharmony_ci	int mbox;
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	mbox = (irq - priv->msix[TSI721_VECT_IMB0_RCV].vector) % RIO_MAX_MBOX;
6348c2ecf20Sopenharmony_ci	tsi721_imsg_handler(priv, mbox + 4);
6358c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
6368c2ecf20Sopenharmony_ci}
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci/**
6398c2ecf20Sopenharmony_ci * tsi721_srio_msix - Tsi721 MSI-X SRIO MAC interrupt handler
6408c2ecf20Sopenharmony_ci * @irq: Linux interrupt number
6418c2ecf20Sopenharmony_ci * @ptr: Pointer to interrupt-specific data (tsi721_device structure)
6428c2ecf20Sopenharmony_ci *
6438c2ecf20Sopenharmony_ci * Handles Tsi721 interrupts from SRIO MAC.
6448c2ecf20Sopenharmony_ci */
6458c2ecf20Sopenharmony_cistatic irqreturn_t tsi721_srio_msix(int irq, void *ptr)
6468c2ecf20Sopenharmony_ci{
6478c2ecf20Sopenharmony_ci	struct tsi721_device *priv = (struct tsi721_device *)ptr;
6488c2ecf20Sopenharmony_ci	u32 srio_int;
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	/* Service SRIO MAC interrupts */
6518c2ecf20Sopenharmony_ci	srio_int = ioread32(priv->regs + TSI721_RIO_EM_INT_STAT);
6528c2ecf20Sopenharmony_ci	if (srio_int & TSI721_RIO_EM_INT_STAT_PW_RX)
6538c2ecf20Sopenharmony_ci		tsi721_pw_handler(priv);
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
6568c2ecf20Sopenharmony_ci}
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci/**
6598c2ecf20Sopenharmony_ci * tsi721_sr2pc_ch_msix - Tsi721 MSI-X SR2PC Channel interrupt handler
6608c2ecf20Sopenharmony_ci * @irq: Linux interrupt number
6618c2ecf20Sopenharmony_ci * @ptr: Pointer to interrupt-specific data (tsi721_device structure)
6628c2ecf20Sopenharmony_ci *
6638c2ecf20Sopenharmony_ci * Handles Tsi721 interrupts from SR2PC Channel.
6648c2ecf20Sopenharmony_ci * NOTE: At this moment services only one SR2PC channel associated with inbound
6658c2ecf20Sopenharmony_ci * doorbells.
6668c2ecf20Sopenharmony_ci */
6678c2ecf20Sopenharmony_cistatic irqreturn_t tsi721_sr2pc_ch_msix(int irq, void *ptr)
6688c2ecf20Sopenharmony_ci{
6698c2ecf20Sopenharmony_ci	struct tsi721_device *priv = (struct tsi721_device *)ptr;
6708c2ecf20Sopenharmony_ci	u32 sr_ch_int;
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	/* Service Inbound DB interrupt from SR2PC channel */
6738c2ecf20Sopenharmony_ci	sr_ch_int = ioread32(priv->regs + TSI721_SR_CHINT(IDB_QUEUE));
6748c2ecf20Sopenharmony_ci	if (sr_ch_int & TSI721_SR_CHINT_IDBQRCV)
6758c2ecf20Sopenharmony_ci		tsi721_dbell_handler(priv);
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	/* Clear interrupts */
6788c2ecf20Sopenharmony_ci	iowrite32(sr_ch_int, priv->regs + TSI721_SR_CHINT(IDB_QUEUE));
6798c2ecf20Sopenharmony_ci	/* Read back to ensure that interrupt was cleared */
6808c2ecf20Sopenharmony_ci	sr_ch_int = ioread32(priv->regs + TSI721_SR_CHINT(IDB_QUEUE));
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
6838c2ecf20Sopenharmony_ci}
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci/**
6868c2ecf20Sopenharmony_ci * tsi721_request_msix - register interrupt service for MSI-X mode.
6878c2ecf20Sopenharmony_ci * @priv: tsi721 device-specific data structure
6888c2ecf20Sopenharmony_ci *
6898c2ecf20Sopenharmony_ci * Registers MSI-X interrupt service routines for interrupts that are active
6908c2ecf20Sopenharmony_ci * immediately after mport initialization. Messaging interrupt service routines
6918c2ecf20Sopenharmony_ci * should be registered during corresponding open requests.
6928c2ecf20Sopenharmony_ci */
6938c2ecf20Sopenharmony_cistatic int tsi721_request_msix(struct tsi721_device *priv)
6948c2ecf20Sopenharmony_ci{
6958c2ecf20Sopenharmony_ci	int err = 0;
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	err = request_irq(priv->msix[TSI721_VECT_IDB].vector,
6988c2ecf20Sopenharmony_ci			tsi721_sr2pc_ch_msix, 0,
6998c2ecf20Sopenharmony_ci			priv->msix[TSI721_VECT_IDB].irq_name, (void *)priv);
7008c2ecf20Sopenharmony_ci	if (err)
7018c2ecf20Sopenharmony_ci		return err;
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	err = request_irq(priv->msix[TSI721_VECT_PWRX].vector,
7048c2ecf20Sopenharmony_ci			tsi721_srio_msix, 0,
7058c2ecf20Sopenharmony_ci			priv->msix[TSI721_VECT_PWRX].irq_name, (void *)priv);
7068c2ecf20Sopenharmony_ci	if (err) {
7078c2ecf20Sopenharmony_ci		free_irq(priv->msix[TSI721_VECT_IDB].vector, (void *)priv);
7088c2ecf20Sopenharmony_ci		return err;
7098c2ecf20Sopenharmony_ci	}
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	return 0;
7128c2ecf20Sopenharmony_ci}
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci/**
7158c2ecf20Sopenharmony_ci * tsi721_enable_msix - Attempts to enable MSI-X support for Tsi721.
7168c2ecf20Sopenharmony_ci * @priv: pointer to tsi721 private data
7178c2ecf20Sopenharmony_ci *
7188c2ecf20Sopenharmony_ci * Configures MSI-X support for Tsi721. Supports only an exact number
7198c2ecf20Sopenharmony_ci * of requested vectors.
7208c2ecf20Sopenharmony_ci */
7218c2ecf20Sopenharmony_cistatic int tsi721_enable_msix(struct tsi721_device *priv)
7228c2ecf20Sopenharmony_ci{
7238c2ecf20Sopenharmony_ci	struct msix_entry entries[TSI721_VECT_MAX];
7248c2ecf20Sopenharmony_ci	int err;
7258c2ecf20Sopenharmony_ci	int i;
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	entries[TSI721_VECT_IDB].entry = TSI721_MSIX_SR2PC_IDBQ_RCV(IDB_QUEUE);
7288c2ecf20Sopenharmony_ci	entries[TSI721_VECT_PWRX].entry = TSI721_MSIX_SRIO_MAC_INT;
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	/*
7318c2ecf20Sopenharmony_ci	 * Initialize MSI-X entries for Messaging Engine:
7328c2ecf20Sopenharmony_ci	 * this driver supports four RIO mailboxes (inbound and outbound)
7338c2ecf20Sopenharmony_ci	 * NOTE: Inbound message MBOX 0...4 use IB channels 4...7. Therefore
7348c2ecf20Sopenharmony_ci	 * offset +4 is added to IB MBOX number.
7358c2ecf20Sopenharmony_ci	 */
7368c2ecf20Sopenharmony_ci	for (i = 0; i < RIO_MAX_MBOX; i++) {
7378c2ecf20Sopenharmony_ci		entries[TSI721_VECT_IMB0_RCV + i].entry =
7388c2ecf20Sopenharmony_ci					TSI721_MSIX_IMSG_DQ_RCV(i + 4);
7398c2ecf20Sopenharmony_ci		entries[TSI721_VECT_IMB0_INT + i].entry =
7408c2ecf20Sopenharmony_ci					TSI721_MSIX_IMSG_INT(i + 4);
7418c2ecf20Sopenharmony_ci		entries[TSI721_VECT_OMB0_DONE + i].entry =
7428c2ecf20Sopenharmony_ci					TSI721_MSIX_OMSG_DONE(i);
7438c2ecf20Sopenharmony_ci		entries[TSI721_VECT_OMB0_INT + i].entry =
7448c2ecf20Sopenharmony_ci					TSI721_MSIX_OMSG_INT(i);
7458c2ecf20Sopenharmony_ci	}
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci#ifdef CONFIG_RAPIDIO_DMA_ENGINE
7488c2ecf20Sopenharmony_ci	/*
7498c2ecf20Sopenharmony_ci	 * Initialize MSI-X entries for Block DMA Engine:
7508c2ecf20Sopenharmony_ci	 * this driver supports XXX DMA channels
7518c2ecf20Sopenharmony_ci	 * (one is reserved for SRIO maintenance transactions)
7528c2ecf20Sopenharmony_ci	 */
7538c2ecf20Sopenharmony_ci	for (i = 0; i < TSI721_DMA_CHNUM; i++) {
7548c2ecf20Sopenharmony_ci		entries[TSI721_VECT_DMA0_DONE + i].entry =
7558c2ecf20Sopenharmony_ci					TSI721_MSIX_DMACH_DONE(i);
7568c2ecf20Sopenharmony_ci		entries[TSI721_VECT_DMA0_INT + i].entry =
7578c2ecf20Sopenharmony_ci					TSI721_MSIX_DMACH_INT(i);
7588c2ecf20Sopenharmony_ci	}
7598c2ecf20Sopenharmony_ci#endif /* CONFIG_RAPIDIO_DMA_ENGINE */
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	err = pci_enable_msix_exact(priv->pdev, entries, ARRAY_SIZE(entries));
7628c2ecf20Sopenharmony_ci	if (err) {
7638c2ecf20Sopenharmony_ci		tsi_err(&priv->pdev->dev,
7648c2ecf20Sopenharmony_ci			"Failed to enable MSI-X (err=%d)", err);
7658c2ecf20Sopenharmony_ci		return err;
7668c2ecf20Sopenharmony_ci	}
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	/*
7698c2ecf20Sopenharmony_ci	 * Copy MSI-X vector information into tsi721 private structure
7708c2ecf20Sopenharmony_ci	 */
7718c2ecf20Sopenharmony_ci	priv->msix[TSI721_VECT_IDB].vector = entries[TSI721_VECT_IDB].vector;
7728c2ecf20Sopenharmony_ci	snprintf(priv->msix[TSI721_VECT_IDB].irq_name, IRQ_DEVICE_NAME_MAX,
7738c2ecf20Sopenharmony_ci		 DRV_NAME "-idb@pci:%s", pci_name(priv->pdev));
7748c2ecf20Sopenharmony_ci	priv->msix[TSI721_VECT_PWRX].vector = entries[TSI721_VECT_PWRX].vector;
7758c2ecf20Sopenharmony_ci	snprintf(priv->msix[TSI721_VECT_PWRX].irq_name, IRQ_DEVICE_NAME_MAX,
7768c2ecf20Sopenharmony_ci		 DRV_NAME "-pwrx@pci:%s", pci_name(priv->pdev));
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	for (i = 0; i < RIO_MAX_MBOX; i++) {
7798c2ecf20Sopenharmony_ci		priv->msix[TSI721_VECT_IMB0_RCV + i].vector =
7808c2ecf20Sopenharmony_ci				entries[TSI721_VECT_IMB0_RCV + i].vector;
7818c2ecf20Sopenharmony_ci		snprintf(priv->msix[TSI721_VECT_IMB0_RCV + i].irq_name,
7828c2ecf20Sopenharmony_ci			 IRQ_DEVICE_NAME_MAX, DRV_NAME "-imbr%d@pci:%s",
7838c2ecf20Sopenharmony_ci			 i, pci_name(priv->pdev));
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci		priv->msix[TSI721_VECT_IMB0_INT + i].vector =
7868c2ecf20Sopenharmony_ci				entries[TSI721_VECT_IMB0_INT + i].vector;
7878c2ecf20Sopenharmony_ci		snprintf(priv->msix[TSI721_VECT_IMB0_INT + i].irq_name,
7888c2ecf20Sopenharmony_ci			 IRQ_DEVICE_NAME_MAX, DRV_NAME "-imbi%d@pci:%s",
7898c2ecf20Sopenharmony_ci			 i, pci_name(priv->pdev));
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci		priv->msix[TSI721_VECT_OMB0_DONE + i].vector =
7928c2ecf20Sopenharmony_ci				entries[TSI721_VECT_OMB0_DONE + i].vector;
7938c2ecf20Sopenharmony_ci		snprintf(priv->msix[TSI721_VECT_OMB0_DONE + i].irq_name,
7948c2ecf20Sopenharmony_ci			 IRQ_DEVICE_NAME_MAX, DRV_NAME "-ombd%d@pci:%s",
7958c2ecf20Sopenharmony_ci			 i, pci_name(priv->pdev));
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci		priv->msix[TSI721_VECT_OMB0_INT + i].vector =
7988c2ecf20Sopenharmony_ci				entries[TSI721_VECT_OMB0_INT + i].vector;
7998c2ecf20Sopenharmony_ci		snprintf(priv->msix[TSI721_VECT_OMB0_INT + i].irq_name,
8008c2ecf20Sopenharmony_ci			 IRQ_DEVICE_NAME_MAX, DRV_NAME "-ombi%d@pci:%s",
8018c2ecf20Sopenharmony_ci			 i, pci_name(priv->pdev));
8028c2ecf20Sopenharmony_ci	}
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci#ifdef CONFIG_RAPIDIO_DMA_ENGINE
8058c2ecf20Sopenharmony_ci	for (i = 0; i < TSI721_DMA_CHNUM; i++) {
8068c2ecf20Sopenharmony_ci		priv->msix[TSI721_VECT_DMA0_DONE + i].vector =
8078c2ecf20Sopenharmony_ci				entries[TSI721_VECT_DMA0_DONE + i].vector;
8088c2ecf20Sopenharmony_ci		snprintf(priv->msix[TSI721_VECT_DMA0_DONE + i].irq_name,
8098c2ecf20Sopenharmony_ci			 IRQ_DEVICE_NAME_MAX, DRV_NAME "-dmad%d@pci:%s",
8108c2ecf20Sopenharmony_ci			 i, pci_name(priv->pdev));
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci		priv->msix[TSI721_VECT_DMA0_INT + i].vector =
8138c2ecf20Sopenharmony_ci				entries[TSI721_VECT_DMA0_INT + i].vector;
8148c2ecf20Sopenharmony_ci		snprintf(priv->msix[TSI721_VECT_DMA0_INT + i].irq_name,
8158c2ecf20Sopenharmony_ci			 IRQ_DEVICE_NAME_MAX, DRV_NAME "-dmai%d@pci:%s",
8168c2ecf20Sopenharmony_ci			 i, pci_name(priv->pdev));
8178c2ecf20Sopenharmony_ci	}
8188c2ecf20Sopenharmony_ci#endif /* CONFIG_RAPIDIO_DMA_ENGINE */
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	return 0;
8218c2ecf20Sopenharmony_ci}
8228c2ecf20Sopenharmony_ci#endif /* CONFIG_PCI_MSI */
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_cistatic int tsi721_request_irq(struct tsi721_device *priv)
8258c2ecf20Sopenharmony_ci{
8268c2ecf20Sopenharmony_ci	int err;
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI_MSI
8298c2ecf20Sopenharmony_ci	if (priv->flags & TSI721_USING_MSIX)
8308c2ecf20Sopenharmony_ci		err = tsi721_request_msix(priv);
8318c2ecf20Sopenharmony_ci	else
8328c2ecf20Sopenharmony_ci#endif
8338c2ecf20Sopenharmony_ci		err = request_irq(priv->pdev->irq, tsi721_irqhandler,
8348c2ecf20Sopenharmony_ci			  (priv->flags & TSI721_USING_MSI) ? 0 : IRQF_SHARED,
8358c2ecf20Sopenharmony_ci			  DRV_NAME, (void *)priv);
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	if (err)
8388c2ecf20Sopenharmony_ci		tsi_err(&priv->pdev->dev,
8398c2ecf20Sopenharmony_ci			"Unable to allocate interrupt, err=%d", err);
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci	return err;
8428c2ecf20Sopenharmony_ci}
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_cistatic void tsi721_free_irq(struct tsi721_device *priv)
8458c2ecf20Sopenharmony_ci{
8468c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI_MSI
8478c2ecf20Sopenharmony_ci	if (priv->flags & TSI721_USING_MSIX) {
8488c2ecf20Sopenharmony_ci		free_irq(priv->msix[TSI721_VECT_IDB].vector, (void *)priv);
8498c2ecf20Sopenharmony_ci		free_irq(priv->msix[TSI721_VECT_PWRX].vector, (void *)priv);
8508c2ecf20Sopenharmony_ci	} else
8518c2ecf20Sopenharmony_ci#endif
8528c2ecf20Sopenharmony_ci	free_irq(priv->pdev->irq, (void *)priv);
8538c2ecf20Sopenharmony_ci}
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_cistatic int
8568c2ecf20Sopenharmony_citsi721_obw_alloc(struct tsi721_device *priv, struct tsi721_obw_bar *pbar,
8578c2ecf20Sopenharmony_ci		 u32 size, int *win_id)
8588c2ecf20Sopenharmony_ci{
8598c2ecf20Sopenharmony_ci	u64 win_base;
8608c2ecf20Sopenharmony_ci	u64 bar_base;
8618c2ecf20Sopenharmony_ci	u64 bar_end;
8628c2ecf20Sopenharmony_ci	u32 align;
8638c2ecf20Sopenharmony_ci	struct tsi721_ob_win *win;
8648c2ecf20Sopenharmony_ci	struct tsi721_ob_win *new_win = NULL;
8658c2ecf20Sopenharmony_ci	int new_win_idx = -1;
8668c2ecf20Sopenharmony_ci	int i = 0;
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci	bar_base = pbar->base;
8698c2ecf20Sopenharmony_ci	bar_end =  bar_base + pbar->size;
8708c2ecf20Sopenharmony_ci	win_base = bar_base;
8718c2ecf20Sopenharmony_ci	align = size/TSI721_PC2SR_ZONES;
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci	while (i < TSI721_IBWIN_NUM) {
8748c2ecf20Sopenharmony_ci		for (i = 0; i < TSI721_IBWIN_NUM; i++) {
8758c2ecf20Sopenharmony_ci			if (!priv->ob_win[i].active) {
8768c2ecf20Sopenharmony_ci				if (new_win == NULL) {
8778c2ecf20Sopenharmony_ci					new_win = &priv->ob_win[i];
8788c2ecf20Sopenharmony_ci					new_win_idx = i;
8798c2ecf20Sopenharmony_ci				}
8808c2ecf20Sopenharmony_ci				continue;
8818c2ecf20Sopenharmony_ci			}
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci			/*
8848c2ecf20Sopenharmony_ci			 * If this window belongs to the current BAR check it
8858c2ecf20Sopenharmony_ci			 * for overlap
8868c2ecf20Sopenharmony_ci			 */
8878c2ecf20Sopenharmony_ci			win = &priv->ob_win[i];
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci			if (win->base >= bar_base && win->base < bar_end) {
8908c2ecf20Sopenharmony_ci				if (win_base < (win->base + win->size) &&
8918c2ecf20Sopenharmony_ci						(win_base + size) > win->base) {
8928c2ecf20Sopenharmony_ci					/* Overlap detected */
8938c2ecf20Sopenharmony_ci					win_base = win->base + win->size;
8948c2ecf20Sopenharmony_ci					win_base = ALIGN(win_base, align);
8958c2ecf20Sopenharmony_ci					break;
8968c2ecf20Sopenharmony_ci				}
8978c2ecf20Sopenharmony_ci			}
8988c2ecf20Sopenharmony_ci		}
8998c2ecf20Sopenharmony_ci	}
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	if (win_base + size > bar_end)
9028c2ecf20Sopenharmony_ci		return -ENOMEM;
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	if (!new_win) {
9058c2ecf20Sopenharmony_ci		tsi_err(&priv->pdev->dev, "OBW count tracking failed");
9068c2ecf20Sopenharmony_ci		return -EIO;
9078c2ecf20Sopenharmony_ci	}
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci	new_win->active = true;
9108c2ecf20Sopenharmony_ci	new_win->base = win_base;
9118c2ecf20Sopenharmony_ci	new_win->size = size;
9128c2ecf20Sopenharmony_ci	new_win->pbar = pbar;
9138c2ecf20Sopenharmony_ci	priv->obwin_cnt--;
9148c2ecf20Sopenharmony_ci	pbar->free -= size;
9158c2ecf20Sopenharmony_ci	*win_id = new_win_idx;
9168c2ecf20Sopenharmony_ci	return 0;
9178c2ecf20Sopenharmony_ci}
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_cistatic int tsi721_map_outb_win(struct rio_mport *mport, u16 destid, u64 rstart,
9208c2ecf20Sopenharmony_ci			u32 size, u32 flags, dma_addr_t *laddr)
9218c2ecf20Sopenharmony_ci{
9228c2ecf20Sopenharmony_ci	struct tsi721_device *priv = mport->priv;
9238c2ecf20Sopenharmony_ci	int i;
9248c2ecf20Sopenharmony_ci	struct tsi721_obw_bar *pbar;
9258c2ecf20Sopenharmony_ci	struct tsi721_ob_win *ob_win;
9268c2ecf20Sopenharmony_ci	int obw = -1;
9278c2ecf20Sopenharmony_ci	u32 rval;
9288c2ecf20Sopenharmony_ci	u64 rio_addr;
9298c2ecf20Sopenharmony_ci	u32 zsize;
9308c2ecf20Sopenharmony_ci	int ret = -ENOMEM;
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci	tsi_debug(OBW, &priv->pdev->dev,
9338c2ecf20Sopenharmony_ci		  "did=%d ra=0x%llx sz=0x%x", destid, rstart, size);
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci	if (!is_power_of_2(size) || (size < 0x8000) || (rstart & (size - 1)))
9368c2ecf20Sopenharmony_ci		return -EINVAL;
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci	if (priv->obwin_cnt == 0)
9398c2ecf20Sopenharmony_ci		return -EBUSY;
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci	for (i = 0; i < 2; i++) {
9428c2ecf20Sopenharmony_ci		if (priv->p2r_bar[i].free >= size) {
9438c2ecf20Sopenharmony_ci			pbar = &priv->p2r_bar[i];
9448c2ecf20Sopenharmony_ci			ret = tsi721_obw_alloc(priv, pbar, size, &obw);
9458c2ecf20Sopenharmony_ci			if (!ret)
9468c2ecf20Sopenharmony_ci				break;
9478c2ecf20Sopenharmony_ci		}
9488c2ecf20Sopenharmony_ci	}
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci	if (ret)
9518c2ecf20Sopenharmony_ci		return ret;
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci	WARN_ON(obw == -1);
9548c2ecf20Sopenharmony_ci	ob_win = &priv->ob_win[obw];
9558c2ecf20Sopenharmony_ci	ob_win->destid = destid;
9568c2ecf20Sopenharmony_ci	ob_win->rstart = rstart;
9578c2ecf20Sopenharmony_ci	tsi_debug(OBW, &priv->pdev->dev,
9588c2ecf20Sopenharmony_ci		  "allocated OBW%d @%llx", obw, ob_win->base);
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci	/*
9618c2ecf20Sopenharmony_ci	 * Configure Outbound Window
9628c2ecf20Sopenharmony_ci	 */
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci	zsize = size/TSI721_PC2SR_ZONES;
9658c2ecf20Sopenharmony_ci	rio_addr = rstart;
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci	/*
9688c2ecf20Sopenharmony_ci	 * Program Address Translation Zones:
9698c2ecf20Sopenharmony_ci	 *  This implementation uses all 8 zones associated wit window.
9708c2ecf20Sopenharmony_ci	 */
9718c2ecf20Sopenharmony_ci	for (i = 0; i < TSI721_PC2SR_ZONES; i++) {
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci		while (ioread32(priv->regs + TSI721_ZONE_SEL) &
9748c2ecf20Sopenharmony_ci			TSI721_ZONE_SEL_GO) {
9758c2ecf20Sopenharmony_ci			udelay(1);
9768c2ecf20Sopenharmony_ci		}
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci		rval = (u32)(rio_addr & TSI721_LUT_DATA0_ADD) |
9798c2ecf20Sopenharmony_ci			TSI721_LUT_DATA0_NREAD | TSI721_LUT_DATA0_NWR;
9808c2ecf20Sopenharmony_ci		iowrite32(rval, priv->regs + TSI721_LUT_DATA0);
9818c2ecf20Sopenharmony_ci		rval = (u32)(rio_addr >> 32);
9828c2ecf20Sopenharmony_ci		iowrite32(rval, priv->regs + TSI721_LUT_DATA1);
9838c2ecf20Sopenharmony_ci		rval = destid;
9848c2ecf20Sopenharmony_ci		iowrite32(rval, priv->regs + TSI721_LUT_DATA2);
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci		rval = TSI721_ZONE_SEL_GO | (obw << 3) | i;
9878c2ecf20Sopenharmony_ci		iowrite32(rval, priv->regs + TSI721_ZONE_SEL);
9888c2ecf20Sopenharmony_ci
9898c2ecf20Sopenharmony_ci		rio_addr += zsize;
9908c2ecf20Sopenharmony_ci	}
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_ci	iowrite32(TSI721_OBWIN_SIZE(size) << 8,
9938c2ecf20Sopenharmony_ci		  priv->regs + TSI721_OBWINSZ(obw));
9948c2ecf20Sopenharmony_ci	iowrite32((u32)(ob_win->base >> 32), priv->regs + TSI721_OBWINUB(obw));
9958c2ecf20Sopenharmony_ci	iowrite32((u32)(ob_win->base & TSI721_OBWINLB_BA) | TSI721_OBWINLB_WEN,
9968c2ecf20Sopenharmony_ci		  priv->regs + TSI721_OBWINLB(obw));
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_ci	*laddr = ob_win->base;
9998c2ecf20Sopenharmony_ci	return 0;
10008c2ecf20Sopenharmony_ci}
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_cistatic void tsi721_unmap_outb_win(struct rio_mport *mport,
10038c2ecf20Sopenharmony_ci				  u16 destid, u64 rstart)
10048c2ecf20Sopenharmony_ci{
10058c2ecf20Sopenharmony_ci	struct tsi721_device *priv = mport->priv;
10068c2ecf20Sopenharmony_ci	struct tsi721_ob_win *ob_win;
10078c2ecf20Sopenharmony_ci	int i;
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_ci	tsi_debug(OBW, &priv->pdev->dev, "did=%d ra=0x%llx", destid, rstart);
10108c2ecf20Sopenharmony_ci
10118c2ecf20Sopenharmony_ci	for (i = 0; i < TSI721_OBWIN_NUM; i++) {
10128c2ecf20Sopenharmony_ci		ob_win = &priv->ob_win[i];
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci		if (ob_win->active &&
10158c2ecf20Sopenharmony_ci		    ob_win->destid == destid && ob_win->rstart == rstart) {
10168c2ecf20Sopenharmony_ci			tsi_debug(OBW, &priv->pdev->dev,
10178c2ecf20Sopenharmony_ci				  "free OBW%d @%llx", i, ob_win->base);
10188c2ecf20Sopenharmony_ci			ob_win->active = false;
10198c2ecf20Sopenharmony_ci			iowrite32(0, priv->regs + TSI721_OBWINLB(i));
10208c2ecf20Sopenharmony_ci			ob_win->pbar->free += ob_win->size;
10218c2ecf20Sopenharmony_ci			priv->obwin_cnt++;
10228c2ecf20Sopenharmony_ci			break;
10238c2ecf20Sopenharmony_ci		}
10248c2ecf20Sopenharmony_ci	}
10258c2ecf20Sopenharmony_ci}
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_ci/**
10288c2ecf20Sopenharmony_ci * tsi721_init_pc2sr_mapping - initializes outbound (PCIe->SRIO)
10298c2ecf20Sopenharmony_ci * translation regions.
10308c2ecf20Sopenharmony_ci * @priv: pointer to tsi721 private data
10318c2ecf20Sopenharmony_ci *
10328c2ecf20Sopenharmony_ci * Disables SREP translation regions.
10338c2ecf20Sopenharmony_ci */
10348c2ecf20Sopenharmony_cistatic void tsi721_init_pc2sr_mapping(struct tsi721_device *priv)
10358c2ecf20Sopenharmony_ci{
10368c2ecf20Sopenharmony_ci	int i, z;
10378c2ecf20Sopenharmony_ci	u32 rval;
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ci	/* Disable all PC2SR translation windows */
10408c2ecf20Sopenharmony_ci	for (i = 0; i < TSI721_OBWIN_NUM; i++)
10418c2ecf20Sopenharmony_ci		iowrite32(0, priv->regs + TSI721_OBWINLB(i));
10428c2ecf20Sopenharmony_ci
10438c2ecf20Sopenharmony_ci	/* Initialize zone lookup tables to avoid ECC errors on reads */
10448c2ecf20Sopenharmony_ci	iowrite32(0, priv->regs + TSI721_LUT_DATA0);
10458c2ecf20Sopenharmony_ci	iowrite32(0, priv->regs + TSI721_LUT_DATA1);
10468c2ecf20Sopenharmony_ci	iowrite32(0, priv->regs + TSI721_LUT_DATA2);
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci	for (i = 0; i < TSI721_OBWIN_NUM; i++) {
10498c2ecf20Sopenharmony_ci		for (z = 0; z < TSI721_PC2SR_ZONES; z++) {
10508c2ecf20Sopenharmony_ci			while (ioread32(priv->regs + TSI721_ZONE_SEL) &
10518c2ecf20Sopenharmony_ci				TSI721_ZONE_SEL_GO) {
10528c2ecf20Sopenharmony_ci				udelay(1);
10538c2ecf20Sopenharmony_ci			}
10548c2ecf20Sopenharmony_ci			rval = TSI721_ZONE_SEL_GO | (i << 3) | z;
10558c2ecf20Sopenharmony_ci			iowrite32(rval, priv->regs + TSI721_ZONE_SEL);
10568c2ecf20Sopenharmony_ci		}
10578c2ecf20Sopenharmony_ci	}
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_ci	if (priv->p2r_bar[0].size == 0 && priv->p2r_bar[1].size == 0) {
10608c2ecf20Sopenharmony_ci		priv->obwin_cnt = 0;
10618c2ecf20Sopenharmony_ci		return;
10628c2ecf20Sopenharmony_ci	}
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ci	priv->p2r_bar[0].free = priv->p2r_bar[0].size;
10658c2ecf20Sopenharmony_ci	priv->p2r_bar[1].free = priv->p2r_bar[1].size;
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci	for (i = 0; i < TSI721_OBWIN_NUM; i++)
10688c2ecf20Sopenharmony_ci		priv->ob_win[i].active = false;
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci	priv->obwin_cnt = TSI721_OBWIN_NUM;
10718c2ecf20Sopenharmony_ci}
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci/**
10748c2ecf20Sopenharmony_ci * tsi721_rio_map_inb_mem -- Mapping inbound memory region.
10758c2ecf20Sopenharmony_ci * @mport: RapidIO master port
10768c2ecf20Sopenharmony_ci * @lstart: Local memory space start address.
10778c2ecf20Sopenharmony_ci * @rstart: RapidIO space start address.
10788c2ecf20Sopenharmony_ci * @size: The mapping region size.
10798c2ecf20Sopenharmony_ci * @flags: Flags for mapping. 0 for using default flags.
10808c2ecf20Sopenharmony_ci *
10818c2ecf20Sopenharmony_ci * Return: 0 -- Success.
10828c2ecf20Sopenharmony_ci *
10838c2ecf20Sopenharmony_ci * This function will create the inbound mapping
10848c2ecf20Sopenharmony_ci * from rstart to lstart.
10858c2ecf20Sopenharmony_ci */
10868c2ecf20Sopenharmony_cistatic int tsi721_rio_map_inb_mem(struct rio_mport *mport, dma_addr_t lstart,
10878c2ecf20Sopenharmony_ci		u64 rstart, u64 size, u32 flags)
10888c2ecf20Sopenharmony_ci{
10898c2ecf20Sopenharmony_ci	struct tsi721_device *priv = mport->priv;
10908c2ecf20Sopenharmony_ci	int i, avail = -1;
10918c2ecf20Sopenharmony_ci	u32 regval;
10928c2ecf20Sopenharmony_ci	struct tsi721_ib_win *ib_win;
10938c2ecf20Sopenharmony_ci	bool direct = (lstart == rstart);
10948c2ecf20Sopenharmony_ci	u64 ibw_size;
10958c2ecf20Sopenharmony_ci	dma_addr_t loc_start;
10968c2ecf20Sopenharmony_ci	u64 ibw_start;
10978c2ecf20Sopenharmony_ci	struct tsi721_ib_win_mapping *map = NULL;
10988c2ecf20Sopenharmony_ci	int ret = -EBUSY;
10998c2ecf20Sopenharmony_ci
11008c2ecf20Sopenharmony_ci	/* Max IBW size supported by HW is 16GB */
11018c2ecf20Sopenharmony_ci	if (size > 0x400000000UL)
11028c2ecf20Sopenharmony_ci		return -EINVAL;
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_ci	if (direct) {
11058c2ecf20Sopenharmony_ci		/* Calculate minimal acceptable window size and base address */
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci		ibw_size = roundup_pow_of_two(size);
11088c2ecf20Sopenharmony_ci		ibw_start = lstart & ~(ibw_size - 1);
11098c2ecf20Sopenharmony_ci
11108c2ecf20Sopenharmony_ci		tsi_debug(IBW, &priv->pdev->dev,
11118c2ecf20Sopenharmony_ci			"Direct (RIO_0x%llx -> PCIe_%pad), size=0x%llx, ibw_start = 0x%llx",
11128c2ecf20Sopenharmony_ci			rstart, &lstart, size, ibw_start);
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci		while ((lstart + size) > (ibw_start + ibw_size)) {
11158c2ecf20Sopenharmony_ci			ibw_size *= 2;
11168c2ecf20Sopenharmony_ci			ibw_start = lstart & ~(ibw_size - 1);
11178c2ecf20Sopenharmony_ci			/* Check for crossing IBW max size 16GB */
11188c2ecf20Sopenharmony_ci			if (ibw_size > 0x400000000UL)
11198c2ecf20Sopenharmony_ci				return -EBUSY;
11208c2ecf20Sopenharmony_ci		}
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci		loc_start = ibw_start;
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_ci		map = kzalloc(sizeof(struct tsi721_ib_win_mapping), GFP_ATOMIC);
11258c2ecf20Sopenharmony_ci		if (map == NULL)
11268c2ecf20Sopenharmony_ci			return -ENOMEM;
11278c2ecf20Sopenharmony_ci
11288c2ecf20Sopenharmony_ci	} else {
11298c2ecf20Sopenharmony_ci		tsi_debug(IBW, &priv->pdev->dev,
11308c2ecf20Sopenharmony_ci			"Translated (RIO_0x%llx -> PCIe_%pad), size=0x%llx",
11318c2ecf20Sopenharmony_ci			rstart, &lstart, size);
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_ci		if (!is_power_of_2(size) || size < 0x1000 ||
11348c2ecf20Sopenharmony_ci		    ((u64)lstart & (size - 1)) || (rstart & (size - 1)))
11358c2ecf20Sopenharmony_ci			return -EINVAL;
11368c2ecf20Sopenharmony_ci		if (priv->ibwin_cnt == 0)
11378c2ecf20Sopenharmony_ci			return -EBUSY;
11388c2ecf20Sopenharmony_ci		ibw_start = rstart;
11398c2ecf20Sopenharmony_ci		ibw_size = size;
11408c2ecf20Sopenharmony_ci		loc_start = lstart;
11418c2ecf20Sopenharmony_ci	}
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_ci	/*
11448c2ecf20Sopenharmony_ci	 * Scan for overlapping with active regions and mark the first available
11458c2ecf20Sopenharmony_ci	 * IB window at the same time.
11468c2ecf20Sopenharmony_ci	 */
11478c2ecf20Sopenharmony_ci	for (i = 0; i < TSI721_IBWIN_NUM; i++) {
11488c2ecf20Sopenharmony_ci		ib_win = &priv->ib_win[i];
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_ci		if (!ib_win->active) {
11518c2ecf20Sopenharmony_ci			if (avail == -1) {
11528c2ecf20Sopenharmony_ci				avail = i;
11538c2ecf20Sopenharmony_ci				ret = 0;
11548c2ecf20Sopenharmony_ci			}
11558c2ecf20Sopenharmony_ci		} else if (ibw_start < (ib_win->rstart + ib_win->size) &&
11568c2ecf20Sopenharmony_ci			   (ibw_start + ibw_size) > ib_win->rstart) {
11578c2ecf20Sopenharmony_ci			/* Return error if address translation involved */
11588c2ecf20Sopenharmony_ci			if (!direct || ib_win->xlat) {
11598c2ecf20Sopenharmony_ci				ret = -EFAULT;
11608c2ecf20Sopenharmony_ci				break;
11618c2ecf20Sopenharmony_ci			}
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ci			/*
11648c2ecf20Sopenharmony_ci			 * Direct mappings usually are larger than originally
11658c2ecf20Sopenharmony_ci			 * requested fragments - check if this new request fits
11668c2ecf20Sopenharmony_ci			 * into it.
11678c2ecf20Sopenharmony_ci			 */
11688c2ecf20Sopenharmony_ci			if (rstart >= ib_win->rstart &&
11698c2ecf20Sopenharmony_ci			    (rstart + size) <= (ib_win->rstart +
11708c2ecf20Sopenharmony_ci							ib_win->size)) {
11718c2ecf20Sopenharmony_ci				/* We are in - no further mapping required */
11728c2ecf20Sopenharmony_ci				map->lstart = lstart;
11738c2ecf20Sopenharmony_ci				list_add_tail(&map->node, &ib_win->mappings);
11748c2ecf20Sopenharmony_ci				return 0;
11758c2ecf20Sopenharmony_ci			}
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_ci			ret = -EFAULT;
11788c2ecf20Sopenharmony_ci			break;
11798c2ecf20Sopenharmony_ci		}
11808c2ecf20Sopenharmony_ci	}
11818c2ecf20Sopenharmony_ci
11828c2ecf20Sopenharmony_ci	if (ret)
11838c2ecf20Sopenharmony_ci		goto out;
11848c2ecf20Sopenharmony_ci	i = avail;
11858c2ecf20Sopenharmony_ci
11868c2ecf20Sopenharmony_ci	/* Sanity check: available IB window must be disabled at this point */
11878c2ecf20Sopenharmony_ci	regval = ioread32(priv->regs + TSI721_IBWIN_LB(i));
11888c2ecf20Sopenharmony_ci	if (WARN_ON(regval & TSI721_IBWIN_LB_WEN)) {
11898c2ecf20Sopenharmony_ci		ret = -EIO;
11908c2ecf20Sopenharmony_ci		goto out;
11918c2ecf20Sopenharmony_ci	}
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_ci	ib_win = &priv->ib_win[i];
11948c2ecf20Sopenharmony_ci	ib_win->active = true;
11958c2ecf20Sopenharmony_ci	ib_win->rstart = ibw_start;
11968c2ecf20Sopenharmony_ci	ib_win->lstart = loc_start;
11978c2ecf20Sopenharmony_ci	ib_win->size = ibw_size;
11988c2ecf20Sopenharmony_ci	ib_win->xlat = (lstart != rstart);
11998c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ib_win->mappings);
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_ci	/*
12028c2ecf20Sopenharmony_ci	 * When using direct IBW mapping and have larger than requested IBW size
12038c2ecf20Sopenharmony_ci	 * we can have multiple local memory blocks mapped through the same IBW
12048c2ecf20Sopenharmony_ci	 * To handle this situation we maintain list of "clients" for such IBWs.
12058c2ecf20Sopenharmony_ci	 */
12068c2ecf20Sopenharmony_ci	if (direct) {
12078c2ecf20Sopenharmony_ci		map->lstart = lstart;
12088c2ecf20Sopenharmony_ci		list_add_tail(&map->node, &ib_win->mappings);
12098c2ecf20Sopenharmony_ci	}
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci	iowrite32(TSI721_IBWIN_SIZE(ibw_size) << 8,
12128c2ecf20Sopenharmony_ci			priv->regs + TSI721_IBWIN_SZ(i));
12138c2ecf20Sopenharmony_ci
12148c2ecf20Sopenharmony_ci	iowrite32(((u64)loc_start >> 32), priv->regs + TSI721_IBWIN_TUA(i));
12158c2ecf20Sopenharmony_ci	iowrite32(((u64)loc_start & TSI721_IBWIN_TLA_ADD),
12168c2ecf20Sopenharmony_ci		  priv->regs + TSI721_IBWIN_TLA(i));
12178c2ecf20Sopenharmony_ci
12188c2ecf20Sopenharmony_ci	iowrite32(ibw_start >> 32, priv->regs + TSI721_IBWIN_UB(i));
12198c2ecf20Sopenharmony_ci	iowrite32((ibw_start & TSI721_IBWIN_LB_BA) | TSI721_IBWIN_LB_WEN,
12208c2ecf20Sopenharmony_ci		priv->regs + TSI721_IBWIN_LB(i));
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_ci	priv->ibwin_cnt--;
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci	tsi_debug(IBW, &priv->pdev->dev,
12258c2ecf20Sopenharmony_ci		"Configured IBWIN%d (RIO_0x%llx -> PCIe_%pad), size=0x%llx",
12268c2ecf20Sopenharmony_ci		i, ibw_start, &loc_start, ibw_size);
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_ci	return 0;
12298c2ecf20Sopenharmony_ciout:
12308c2ecf20Sopenharmony_ci	kfree(map);
12318c2ecf20Sopenharmony_ci	return ret;
12328c2ecf20Sopenharmony_ci}
12338c2ecf20Sopenharmony_ci
12348c2ecf20Sopenharmony_ci/**
12358c2ecf20Sopenharmony_ci * tsi721_rio_unmap_inb_mem -- Unmapping inbound memory region.
12368c2ecf20Sopenharmony_ci * @mport: RapidIO master port
12378c2ecf20Sopenharmony_ci * @lstart: Local memory space start address.
12388c2ecf20Sopenharmony_ci */
12398c2ecf20Sopenharmony_cistatic void tsi721_rio_unmap_inb_mem(struct rio_mport *mport,
12408c2ecf20Sopenharmony_ci				dma_addr_t lstart)
12418c2ecf20Sopenharmony_ci{
12428c2ecf20Sopenharmony_ci	struct tsi721_device *priv = mport->priv;
12438c2ecf20Sopenharmony_ci	struct tsi721_ib_win *ib_win;
12448c2ecf20Sopenharmony_ci	int i;
12458c2ecf20Sopenharmony_ci
12468c2ecf20Sopenharmony_ci	tsi_debug(IBW, &priv->pdev->dev,
12478c2ecf20Sopenharmony_ci		"Unmap IBW mapped to PCIe_%pad", &lstart);
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ci	/* Search for matching active inbound translation window */
12508c2ecf20Sopenharmony_ci	for (i = 0; i < TSI721_IBWIN_NUM; i++) {
12518c2ecf20Sopenharmony_ci		ib_win = &priv->ib_win[i];
12528c2ecf20Sopenharmony_ci
12538c2ecf20Sopenharmony_ci		/* Address translating IBWs must to be an exact march */
12548c2ecf20Sopenharmony_ci		if (!ib_win->active ||
12558c2ecf20Sopenharmony_ci		    (ib_win->xlat && lstart != ib_win->lstart))
12568c2ecf20Sopenharmony_ci			continue;
12578c2ecf20Sopenharmony_ci
12588c2ecf20Sopenharmony_ci		if (lstart >= ib_win->lstart &&
12598c2ecf20Sopenharmony_ci		    lstart < (ib_win->lstart + ib_win->size)) {
12608c2ecf20Sopenharmony_ci
12618c2ecf20Sopenharmony_ci			if (!ib_win->xlat) {
12628c2ecf20Sopenharmony_ci				struct tsi721_ib_win_mapping *map;
12638c2ecf20Sopenharmony_ci				int found = 0;
12648c2ecf20Sopenharmony_ci
12658c2ecf20Sopenharmony_ci				list_for_each_entry(map,
12668c2ecf20Sopenharmony_ci						    &ib_win->mappings, node) {
12678c2ecf20Sopenharmony_ci					if (map->lstart == lstart) {
12688c2ecf20Sopenharmony_ci						list_del(&map->node);
12698c2ecf20Sopenharmony_ci						kfree(map);
12708c2ecf20Sopenharmony_ci						found = 1;
12718c2ecf20Sopenharmony_ci						break;
12728c2ecf20Sopenharmony_ci					}
12738c2ecf20Sopenharmony_ci				}
12748c2ecf20Sopenharmony_ci
12758c2ecf20Sopenharmony_ci				if (!found)
12768c2ecf20Sopenharmony_ci					continue;
12778c2ecf20Sopenharmony_ci
12788c2ecf20Sopenharmony_ci				if (!list_empty(&ib_win->mappings))
12798c2ecf20Sopenharmony_ci					break;
12808c2ecf20Sopenharmony_ci			}
12818c2ecf20Sopenharmony_ci
12828c2ecf20Sopenharmony_ci			tsi_debug(IBW, &priv->pdev->dev, "Disable IBWIN_%d", i);
12838c2ecf20Sopenharmony_ci			iowrite32(0, priv->regs + TSI721_IBWIN_LB(i));
12848c2ecf20Sopenharmony_ci			ib_win->active = false;
12858c2ecf20Sopenharmony_ci			priv->ibwin_cnt++;
12868c2ecf20Sopenharmony_ci			break;
12878c2ecf20Sopenharmony_ci		}
12888c2ecf20Sopenharmony_ci	}
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_ci	if (i == TSI721_IBWIN_NUM)
12918c2ecf20Sopenharmony_ci		tsi_debug(IBW, &priv->pdev->dev,
12928c2ecf20Sopenharmony_ci			"IB window mapped to %pad not found", &lstart);
12938c2ecf20Sopenharmony_ci}
12948c2ecf20Sopenharmony_ci
12958c2ecf20Sopenharmony_ci/**
12968c2ecf20Sopenharmony_ci * tsi721_init_sr2pc_mapping - initializes inbound (SRIO->PCIe)
12978c2ecf20Sopenharmony_ci * translation regions.
12988c2ecf20Sopenharmony_ci * @priv: pointer to tsi721 private data
12998c2ecf20Sopenharmony_ci *
13008c2ecf20Sopenharmony_ci * Disables inbound windows.
13018c2ecf20Sopenharmony_ci */
13028c2ecf20Sopenharmony_cistatic void tsi721_init_sr2pc_mapping(struct tsi721_device *priv)
13038c2ecf20Sopenharmony_ci{
13048c2ecf20Sopenharmony_ci	int i;
13058c2ecf20Sopenharmony_ci
13068c2ecf20Sopenharmony_ci	/* Disable all SR2PC inbound windows */
13078c2ecf20Sopenharmony_ci	for (i = 0; i < TSI721_IBWIN_NUM; i++)
13088c2ecf20Sopenharmony_ci		iowrite32(0, priv->regs + TSI721_IBWIN_LB(i));
13098c2ecf20Sopenharmony_ci	priv->ibwin_cnt = TSI721_IBWIN_NUM;
13108c2ecf20Sopenharmony_ci}
13118c2ecf20Sopenharmony_ci
13128c2ecf20Sopenharmony_ci/*
13138c2ecf20Sopenharmony_ci * tsi721_close_sr2pc_mapping - closes all active inbound (SRIO->PCIe)
13148c2ecf20Sopenharmony_ci * translation regions.
13158c2ecf20Sopenharmony_ci * @priv: pointer to tsi721 device private data
13168c2ecf20Sopenharmony_ci */
13178c2ecf20Sopenharmony_cistatic void tsi721_close_sr2pc_mapping(struct tsi721_device *priv)
13188c2ecf20Sopenharmony_ci{
13198c2ecf20Sopenharmony_ci	struct tsi721_ib_win *ib_win;
13208c2ecf20Sopenharmony_ci	int i;
13218c2ecf20Sopenharmony_ci
13228c2ecf20Sopenharmony_ci	/* Disable all active SR2PC inbound windows */
13238c2ecf20Sopenharmony_ci	for (i = 0; i < TSI721_IBWIN_NUM; i++) {
13248c2ecf20Sopenharmony_ci		ib_win = &priv->ib_win[i];
13258c2ecf20Sopenharmony_ci		if (ib_win->active) {
13268c2ecf20Sopenharmony_ci			iowrite32(0, priv->regs + TSI721_IBWIN_LB(i));
13278c2ecf20Sopenharmony_ci			ib_win->active = false;
13288c2ecf20Sopenharmony_ci		}
13298c2ecf20Sopenharmony_ci	}
13308c2ecf20Sopenharmony_ci}
13318c2ecf20Sopenharmony_ci
13328c2ecf20Sopenharmony_ci/**
13338c2ecf20Sopenharmony_ci * tsi721_port_write_init - Inbound port write interface init
13348c2ecf20Sopenharmony_ci * @priv: pointer to tsi721 private data
13358c2ecf20Sopenharmony_ci *
13368c2ecf20Sopenharmony_ci * Initializes inbound port write handler.
13378c2ecf20Sopenharmony_ci * Returns %0 on success or %-ENOMEM on failure.
13388c2ecf20Sopenharmony_ci */
13398c2ecf20Sopenharmony_cistatic int tsi721_port_write_init(struct tsi721_device *priv)
13408c2ecf20Sopenharmony_ci{
13418c2ecf20Sopenharmony_ci	priv->pw_discard_count = 0;
13428c2ecf20Sopenharmony_ci	INIT_WORK(&priv->pw_work, tsi721_pw_dpc);
13438c2ecf20Sopenharmony_ci	spin_lock_init(&priv->pw_fifo_lock);
13448c2ecf20Sopenharmony_ci	if (kfifo_alloc(&priv->pw_fifo,
13458c2ecf20Sopenharmony_ci			TSI721_RIO_PW_MSG_SIZE * 32, GFP_KERNEL)) {
13468c2ecf20Sopenharmony_ci		tsi_err(&priv->pdev->dev, "PW FIFO allocation failed");
13478c2ecf20Sopenharmony_ci		return -ENOMEM;
13488c2ecf20Sopenharmony_ci	}
13498c2ecf20Sopenharmony_ci
13508c2ecf20Sopenharmony_ci	/* Use reliable port-write capture mode */
13518c2ecf20Sopenharmony_ci	iowrite32(TSI721_RIO_PW_CTL_PWC_REL, priv->regs + TSI721_RIO_PW_CTL);
13528c2ecf20Sopenharmony_ci	return 0;
13538c2ecf20Sopenharmony_ci}
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_cistatic void tsi721_port_write_free(struct tsi721_device *priv)
13568c2ecf20Sopenharmony_ci{
13578c2ecf20Sopenharmony_ci	kfifo_free(&priv->pw_fifo);
13588c2ecf20Sopenharmony_ci}
13598c2ecf20Sopenharmony_ci
13608c2ecf20Sopenharmony_cistatic int tsi721_doorbell_init(struct tsi721_device *priv)
13618c2ecf20Sopenharmony_ci{
13628c2ecf20Sopenharmony_ci	/* Outbound Doorbells do not require any setup.
13638c2ecf20Sopenharmony_ci	 * Tsi721 uses dedicated PCI BAR1 to generate doorbells.
13648c2ecf20Sopenharmony_ci	 * That BAR1 was mapped during the probe routine.
13658c2ecf20Sopenharmony_ci	 */
13668c2ecf20Sopenharmony_ci
13678c2ecf20Sopenharmony_ci	/* Initialize Inbound Doorbell processing DPC and queue */
13688c2ecf20Sopenharmony_ci	priv->db_discard_count = 0;
13698c2ecf20Sopenharmony_ci	INIT_WORK(&priv->idb_work, tsi721_db_dpc);
13708c2ecf20Sopenharmony_ci
13718c2ecf20Sopenharmony_ci	/* Allocate buffer for inbound doorbells queue */
13728c2ecf20Sopenharmony_ci	priv->idb_base = dma_alloc_coherent(&priv->pdev->dev,
13738c2ecf20Sopenharmony_ci					    IDB_QSIZE * TSI721_IDB_ENTRY_SIZE,
13748c2ecf20Sopenharmony_ci					    &priv->idb_dma, GFP_KERNEL);
13758c2ecf20Sopenharmony_ci	if (!priv->idb_base)
13768c2ecf20Sopenharmony_ci		return -ENOMEM;
13778c2ecf20Sopenharmony_ci
13788c2ecf20Sopenharmony_ci	tsi_debug(DBELL, &priv->pdev->dev,
13798c2ecf20Sopenharmony_ci		  "Allocated IDB buffer @ %p (phys = %pad)",
13808c2ecf20Sopenharmony_ci		  priv->idb_base, &priv->idb_dma);
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ci	iowrite32(TSI721_IDQ_SIZE_VAL(IDB_QSIZE),
13838c2ecf20Sopenharmony_ci		priv->regs + TSI721_IDQ_SIZE(IDB_QUEUE));
13848c2ecf20Sopenharmony_ci	iowrite32(((u64)priv->idb_dma >> 32),
13858c2ecf20Sopenharmony_ci		priv->regs + TSI721_IDQ_BASEU(IDB_QUEUE));
13868c2ecf20Sopenharmony_ci	iowrite32(((u64)priv->idb_dma & TSI721_IDQ_BASEL_ADDR),
13878c2ecf20Sopenharmony_ci		priv->regs + TSI721_IDQ_BASEL(IDB_QUEUE));
13888c2ecf20Sopenharmony_ci	/* Enable accepting all inbound doorbells */
13898c2ecf20Sopenharmony_ci	iowrite32(0, priv->regs + TSI721_IDQ_MASK(IDB_QUEUE));
13908c2ecf20Sopenharmony_ci
13918c2ecf20Sopenharmony_ci	iowrite32(TSI721_IDQ_INIT, priv->regs + TSI721_IDQ_CTL(IDB_QUEUE));
13928c2ecf20Sopenharmony_ci
13938c2ecf20Sopenharmony_ci	iowrite32(0, priv->regs + TSI721_IDQ_RP(IDB_QUEUE));
13948c2ecf20Sopenharmony_ci
13958c2ecf20Sopenharmony_ci	return 0;
13968c2ecf20Sopenharmony_ci}
13978c2ecf20Sopenharmony_ci
13988c2ecf20Sopenharmony_cistatic void tsi721_doorbell_free(struct tsi721_device *priv)
13998c2ecf20Sopenharmony_ci{
14008c2ecf20Sopenharmony_ci	if (priv->idb_base == NULL)
14018c2ecf20Sopenharmony_ci		return;
14028c2ecf20Sopenharmony_ci
14038c2ecf20Sopenharmony_ci	/* Free buffer allocated for inbound doorbell queue */
14048c2ecf20Sopenharmony_ci	dma_free_coherent(&priv->pdev->dev, IDB_QSIZE * TSI721_IDB_ENTRY_SIZE,
14058c2ecf20Sopenharmony_ci			  priv->idb_base, priv->idb_dma);
14068c2ecf20Sopenharmony_ci	priv->idb_base = NULL;
14078c2ecf20Sopenharmony_ci}
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_ci/**
14108c2ecf20Sopenharmony_ci * tsi721_bdma_maint_init - Initialize maintenance request BDMA channel.
14118c2ecf20Sopenharmony_ci * @priv: pointer to tsi721 private data
14128c2ecf20Sopenharmony_ci *
14138c2ecf20Sopenharmony_ci * Initialize BDMA channel allocated for RapidIO maintenance read/write
14148c2ecf20Sopenharmony_ci * request generation
14158c2ecf20Sopenharmony_ci * Returns %0 on success or %-ENOMEM on failure.
14168c2ecf20Sopenharmony_ci */
14178c2ecf20Sopenharmony_cistatic int tsi721_bdma_maint_init(struct tsi721_device *priv)
14188c2ecf20Sopenharmony_ci{
14198c2ecf20Sopenharmony_ci	struct tsi721_dma_desc *bd_ptr;
14208c2ecf20Sopenharmony_ci	u64		*sts_ptr;
14218c2ecf20Sopenharmony_ci	dma_addr_t	bd_phys, sts_phys;
14228c2ecf20Sopenharmony_ci	int		sts_size;
14238c2ecf20Sopenharmony_ci	int		bd_num = 2;
14248c2ecf20Sopenharmony_ci	void __iomem	*regs;
14258c2ecf20Sopenharmony_ci
14268c2ecf20Sopenharmony_ci	tsi_debug(MAINT, &priv->pdev->dev,
14278c2ecf20Sopenharmony_ci		  "Init BDMA_%d Maintenance requests", TSI721_DMACH_MAINT);
14288c2ecf20Sopenharmony_ci
14298c2ecf20Sopenharmony_ci	/*
14308c2ecf20Sopenharmony_ci	 * Initialize DMA channel for maintenance requests
14318c2ecf20Sopenharmony_ci	 */
14328c2ecf20Sopenharmony_ci
14338c2ecf20Sopenharmony_ci	priv->mdma.ch_id = TSI721_DMACH_MAINT;
14348c2ecf20Sopenharmony_ci	regs = priv->regs + TSI721_DMAC_BASE(TSI721_DMACH_MAINT);
14358c2ecf20Sopenharmony_ci
14368c2ecf20Sopenharmony_ci	/* Allocate space for DMA descriptors */
14378c2ecf20Sopenharmony_ci	bd_ptr = dma_alloc_coherent(&priv->pdev->dev,
14388c2ecf20Sopenharmony_ci				    bd_num * sizeof(struct tsi721_dma_desc),
14398c2ecf20Sopenharmony_ci				    &bd_phys, GFP_KERNEL);
14408c2ecf20Sopenharmony_ci	if (!bd_ptr)
14418c2ecf20Sopenharmony_ci		return -ENOMEM;
14428c2ecf20Sopenharmony_ci
14438c2ecf20Sopenharmony_ci	priv->mdma.bd_num = bd_num;
14448c2ecf20Sopenharmony_ci	priv->mdma.bd_phys = bd_phys;
14458c2ecf20Sopenharmony_ci	priv->mdma.bd_base = bd_ptr;
14468c2ecf20Sopenharmony_ci
14478c2ecf20Sopenharmony_ci	tsi_debug(MAINT, &priv->pdev->dev, "DMA descriptors @ %p (phys = %pad)",
14488c2ecf20Sopenharmony_ci		  bd_ptr, &bd_phys);
14498c2ecf20Sopenharmony_ci
14508c2ecf20Sopenharmony_ci	/* Allocate space for descriptor status FIFO */
14518c2ecf20Sopenharmony_ci	sts_size = (bd_num >= TSI721_DMA_MINSTSSZ) ?
14528c2ecf20Sopenharmony_ci					bd_num : TSI721_DMA_MINSTSSZ;
14538c2ecf20Sopenharmony_ci	sts_size = roundup_pow_of_two(sts_size);
14548c2ecf20Sopenharmony_ci	sts_ptr = dma_alloc_coherent(&priv->pdev->dev,
14558c2ecf20Sopenharmony_ci				     sts_size * sizeof(struct tsi721_dma_sts),
14568c2ecf20Sopenharmony_ci				     &sts_phys, GFP_KERNEL);
14578c2ecf20Sopenharmony_ci	if (!sts_ptr) {
14588c2ecf20Sopenharmony_ci		/* Free space allocated for DMA descriptors */
14598c2ecf20Sopenharmony_ci		dma_free_coherent(&priv->pdev->dev,
14608c2ecf20Sopenharmony_ci				  bd_num * sizeof(struct tsi721_dma_desc),
14618c2ecf20Sopenharmony_ci				  bd_ptr, bd_phys);
14628c2ecf20Sopenharmony_ci		priv->mdma.bd_base = NULL;
14638c2ecf20Sopenharmony_ci		return -ENOMEM;
14648c2ecf20Sopenharmony_ci	}
14658c2ecf20Sopenharmony_ci
14668c2ecf20Sopenharmony_ci	priv->mdma.sts_phys = sts_phys;
14678c2ecf20Sopenharmony_ci	priv->mdma.sts_base = sts_ptr;
14688c2ecf20Sopenharmony_ci	priv->mdma.sts_size = sts_size;
14698c2ecf20Sopenharmony_ci
14708c2ecf20Sopenharmony_ci	tsi_debug(MAINT, &priv->pdev->dev,
14718c2ecf20Sopenharmony_ci		"desc status FIFO @ %p (phys = %pad) size=0x%x",
14728c2ecf20Sopenharmony_ci		sts_ptr, &sts_phys, sts_size);
14738c2ecf20Sopenharmony_ci
14748c2ecf20Sopenharmony_ci	/* Initialize DMA descriptors ring */
14758c2ecf20Sopenharmony_ci	bd_ptr[bd_num - 1].type_id = cpu_to_le32(DTYPE3 << 29);
14768c2ecf20Sopenharmony_ci	bd_ptr[bd_num - 1].next_lo = cpu_to_le32((u64)bd_phys &
14778c2ecf20Sopenharmony_ci						 TSI721_DMAC_DPTRL_MASK);
14788c2ecf20Sopenharmony_ci	bd_ptr[bd_num - 1].next_hi = cpu_to_le32((u64)bd_phys >> 32);
14798c2ecf20Sopenharmony_ci
14808c2ecf20Sopenharmony_ci	/* Setup DMA descriptor pointers */
14818c2ecf20Sopenharmony_ci	iowrite32(((u64)bd_phys >> 32),	regs + TSI721_DMAC_DPTRH);
14828c2ecf20Sopenharmony_ci	iowrite32(((u64)bd_phys & TSI721_DMAC_DPTRL_MASK),
14838c2ecf20Sopenharmony_ci		regs + TSI721_DMAC_DPTRL);
14848c2ecf20Sopenharmony_ci
14858c2ecf20Sopenharmony_ci	/* Setup descriptor status FIFO */
14868c2ecf20Sopenharmony_ci	iowrite32(((u64)sts_phys >> 32), regs + TSI721_DMAC_DSBH);
14878c2ecf20Sopenharmony_ci	iowrite32(((u64)sts_phys & TSI721_DMAC_DSBL_MASK),
14888c2ecf20Sopenharmony_ci		regs + TSI721_DMAC_DSBL);
14898c2ecf20Sopenharmony_ci	iowrite32(TSI721_DMAC_DSSZ_SIZE(sts_size),
14908c2ecf20Sopenharmony_ci		regs + TSI721_DMAC_DSSZ);
14918c2ecf20Sopenharmony_ci
14928c2ecf20Sopenharmony_ci	/* Clear interrupt bits */
14938c2ecf20Sopenharmony_ci	iowrite32(TSI721_DMAC_INT_ALL, regs + TSI721_DMAC_INT);
14948c2ecf20Sopenharmony_ci
14958c2ecf20Sopenharmony_ci	ioread32(regs + TSI721_DMAC_INT);
14968c2ecf20Sopenharmony_ci
14978c2ecf20Sopenharmony_ci	/* Toggle DMA channel initialization */
14988c2ecf20Sopenharmony_ci	iowrite32(TSI721_DMAC_CTL_INIT,	regs + TSI721_DMAC_CTL);
14998c2ecf20Sopenharmony_ci	ioread32(regs + TSI721_DMAC_CTL);
15008c2ecf20Sopenharmony_ci	udelay(10);
15018c2ecf20Sopenharmony_ci
15028c2ecf20Sopenharmony_ci	return 0;
15038c2ecf20Sopenharmony_ci}
15048c2ecf20Sopenharmony_ci
15058c2ecf20Sopenharmony_cistatic int tsi721_bdma_maint_free(struct tsi721_device *priv)
15068c2ecf20Sopenharmony_ci{
15078c2ecf20Sopenharmony_ci	u32 ch_stat;
15088c2ecf20Sopenharmony_ci	struct tsi721_bdma_maint *mdma = &priv->mdma;
15098c2ecf20Sopenharmony_ci	void __iomem *regs = priv->regs + TSI721_DMAC_BASE(mdma->ch_id);
15108c2ecf20Sopenharmony_ci
15118c2ecf20Sopenharmony_ci	if (mdma->bd_base == NULL)
15128c2ecf20Sopenharmony_ci		return 0;
15138c2ecf20Sopenharmony_ci
15148c2ecf20Sopenharmony_ci	/* Check if DMA channel still running */
15158c2ecf20Sopenharmony_ci	ch_stat = ioread32(regs + TSI721_DMAC_STS);
15168c2ecf20Sopenharmony_ci	if (ch_stat & TSI721_DMAC_STS_RUN)
15178c2ecf20Sopenharmony_ci		return -EFAULT;
15188c2ecf20Sopenharmony_ci
15198c2ecf20Sopenharmony_ci	/* Put DMA channel into init state */
15208c2ecf20Sopenharmony_ci	iowrite32(TSI721_DMAC_CTL_INIT,	regs + TSI721_DMAC_CTL);
15218c2ecf20Sopenharmony_ci
15228c2ecf20Sopenharmony_ci	/* Free space allocated for DMA descriptors */
15238c2ecf20Sopenharmony_ci	dma_free_coherent(&priv->pdev->dev,
15248c2ecf20Sopenharmony_ci		mdma->bd_num * sizeof(struct tsi721_dma_desc),
15258c2ecf20Sopenharmony_ci		mdma->bd_base, mdma->bd_phys);
15268c2ecf20Sopenharmony_ci	mdma->bd_base = NULL;
15278c2ecf20Sopenharmony_ci
15288c2ecf20Sopenharmony_ci	/* Free space allocated for status FIFO */
15298c2ecf20Sopenharmony_ci	dma_free_coherent(&priv->pdev->dev,
15308c2ecf20Sopenharmony_ci		mdma->sts_size * sizeof(struct tsi721_dma_sts),
15318c2ecf20Sopenharmony_ci		mdma->sts_base, mdma->sts_phys);
15328c2ecf20Sopenharmony_ci	mdma->sts_base = NULL;
15338c2ecf20Sopenharmony_ci	return 0;
15348c2ecf20Sopenharmony_ci}
15358c2ecf20Sopenharmony_ci
15368c2ecf20Sopenharmony_ci/* Enable Inbound Messaging Interrupts */
15378c2ecf20Sopenharmony_cistatic void
15388c2ecf20Sopenharmony_citsi721_imsg_interrupt_enable(struct tsi721_device *priv, int ch,
15398c2ecf20Sopenharmony_ci				  u32 inte_mask)
15408c2ecf20Sopenharmony_ci{
15418c2ecf20Sopenharmony_ci	u32 rval;
15428c2ecf20Sopenharmony_ci
15438c2ecf20Sopenharmony_ci	if (!inte_mask)
15448c2ecf20Sopenharmony_ci		return;
15458c2ecf20Sopenharmony_ci
15468c2ecf20Sopenharmony_ci	/* Clear pending Inbound Messaging interrupts */
15478c2ecf20Sopenharmony_ci	iowrite32(inte_mask, priv->regs + TSI721_IBDMAC_INT(ch));
15488c2ecf20Sopenharmony_ci
15498c2ecf20Sopenharmony_ci	/* Enable Inbound Messaging interrupts */
15508c2ecf20Sopenharmony_ci	rval = ioread32(priv->regs + TSI721_IBDMAC_INTE(ch));
15518c2ecf20Sopenharmony_ci	iowrite32(rval | inte_mask, priv->regs + TSI721_IBDMAC_INTE(ch));
15528c2ecf20Sopenharmony_ci
15538c2ecf20Sopenharmony_ci	if (priv->flags & TSI721_USING_MSIX)
15548c2ecf20Sopenharmony_ci		return; /* Finished if we are in MSI-X mode */
15558c2ecf20Sopenharmony_ci
15568c2ecf20Sopenharmony_ci	/*
15578c2ecf20Sopenharmony_ci	 * For MSI and INTA interrupt signalling we need to enable next levels
15588c2ecf20Sopenharmony_ci	 */
15598c2ecf20Sopenharmony_ci
15608c2ecf20Sopenharmony_ci	/* Enable Device Channel Interrupt */
15618c2ecf20Sopenharmony_ci	rval = ioread32(priv->regs + TSI721_DEV_CHAN_INTE);
15628c2ecf20Sopenharmony_ci	iowrite32(rval | TSI721_INT_IMSG_CHAN(ch),
15638c2ecf20Sopenharmony_ci		  priv->regs + TSI721_DEV_CHAN_INTE);
15648c2ecf20Sopenharmony_ci}
15658c2ecf20Sopenharmony_ci
15668c2ecf20Sopenharmony_ci/* Disable Inbound Messaging Interrupts */
15678c2ecf20Sopenharmony_cistatic void
15688c2ecf20Sopenharmony_citsi721_imsg_interrupt_disable(struct tsi721_device *priv, int ch,
15698c2ecf20Sopenharmony_ci				   u32 inte_mask)
15708c2ecf20Sopenharmony_ci{
15718c2ecf20Sopenharmony_ci	u32 rval;
15728c2ecf20Sopenharmony_ci
15738c2ecf20Sopenharmony_ci	if (!inte_mask)
15748c2ecf20Sopenharmony_ci		return;
15758c2ecf20Sopenharmony_ci
15768c2ecf20Sopenharmony_ci	/* Clear pending Inbound Messaging interrupts */
15778c2ecf20Sopenharmony_ci	iowrite32(inte_mask, priv->regs + TSI721_IBDMAC_INT(ch));
15788c2ecf20Sopenharmony_ci
15798c2ecf20Sopenharmony_ci	/* Disable Inbound Messaging interrupts */
15808c2ecf20Sopenharmony_ci	rval = ioread32(priv->regs + TSI721_IBDMAC_INTE(ch));
15818c2ecf20Sopenharmony_ci	rval &= ~inte_mask;
15828c2ecf20Sopenharmony_ci	iowrite32(rval, priv->regs + TSI721_IBDMAC_INTE(ch));
15838c2ecf20Sopenharmony_ci
15848c2ecf20Sopenharmony_ci	if (priv->flags & TSI721_USING_MSIX)
15858c2ecf20Sopenharmony_ci		return; /* Finished if we are in MSI-X mode */
15868c2ecf20Sopenharmony_ci
15878c2ecf20Sopenharmony_ci	/*
15888c2ecf20Sopenharmony_ci	 * For MSI and INTA interrupt signalling we need to disable next levels
15898c2ecf20Sopenharmony_ci	 */
15908c2ecf20Sopenharmony_ci
15918c2ecf20Sopenharmony_ci	/* Disable Device Channel Interrupt */
15928c2ecf20Sopenharmony_ci	rval = ioread32(priv->regs + TSI721_DEV_CHAN_INTE);
15938c2ecf20Sopenharmony_ci	rval &= ~TSI721_INT_IMSG_CHAN(ch);
15948c2ecf20Sopenharmony_ci	iowrite32(rval, priv->regs + TSI721_DEV_CHAN_INTE);
15958c2ecf20Sopenharmony_ci}
15968c2ecf20Sopenharmony_ci
15978c2ecf20Sopenharmony_ci/* Enable Outbound Messaging interrupts */
15988c2ecf20Sopenharmony_cistatic void
15998c2ecf20Sopenharmony_citsi721_omsg_interrupt_enable(struct tsi721_device *priv, int ch,
16008c2ecf20Sopenharmony_ci				  u32 inte_mask)
16018c2ecf20Sopenharmony_ci{
16028c2ecf20Sopenharmony_ci	u32 rval;
16038c2ecf20Sopenharmony_ci
16048c2ecf20Sopenharmony_ci	if (!inte_mask)
16058c2ecf20Sopenharmony_ci		return;
16068c2ecf20Sopenharmony_ci
16078c2ecf20Sopenharmony_ci	/* Clear pending Outbound Messaging interrupts */
16088c2ecf20Sopenharmony_ci	iowrite32(inte_mask, priv->regs + TSI721_OBDMAC_INT(ch));
16098c2ecf20Sopenharmony_ci
16108c2ecf20Sopenharmony_ci	/* Enable Outbound Messaging channel interrupts */
16118c2ecf20Sopenharmony_ci	rval = ioread32(priv->regs + TSI721_OBDMAC_INTE(ch));
16128c2ecf20Sopenharmony_ci	iowrite32(rval | inte_mask, priv->regs + TSI721_OBDMAC_INTE(ch));
16138c2ecf20Sopenharmony_ci
16148c2ecf20Sopenharmony_ci	if (priv->flags & TSI721_USING_MSIX)
16158c2ecf20Sopenharmony_ci		return; /* Finished if we are in MSI-X mode */
16168c2ecf20Sopenharmony_ci
16178c2ecf20Sopenharmony_ci	/*
16188c2ecf20Sopenharmony_ci	 * For MSI and INTA interrupt signalling we need to enable next levels
16198c2ecf20Sopenharmony_ci	 */
16208c2ecf20Sopenharmony_ci
16218c2ecf20Sopenharmony_ci	/* Enable Device Channel Interrupt */
16228c2ecf20Sopenharmony_ci	rval = ioread32(priv->regs + TSI721_DEV_CHAN_INTE);
16238c2ecf20Sopenharmony_ci	iowrite32(rval | TSI721_INT_OMSG_CHAN(ch),
16248c2ecf20Sopenharmony_ci		  priv->regs + TSI721_DEV_CHAN_INTE);
16258c2ecf20Sopenharmony_ci}
16268c2ecf20Sopenharmony_ci
16278c2ecf20Sopenharmony_ci/* Disable Outbound Messaging interrupts */
16288c2ecf20Sopenharmony_cistatic void
16298c2ecf20Sopenharmony_citsi721_omsg_interrupt_disable(struct tsi721_device *priv, int ch,
16308c2ecf20Sopenharmony_ci				   u32 inte_mask)
16318c2ecf20Sopenharmony_ci{
16328c2ecf20Sopenharmony_ci	u32 rval;
16338c2ecf20Sopenharmony_ci
16348c2ecf20Sopenharmony_ci	if (!inte_mask)
16358c2ecf20Sopenharmony_ci		return;
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_ci	/* Clear pending Outbound Messaging interrupts */
16388c2ecf20Sopenharmony_ci	iowrite32(inte_mask, priv->regs + TSI721_OBDMAC_INT(ch));
16398c2ecf20Sopenharmony_ci
16408c2ecf20Sopenharmony_ci	/* Disable Outbound Messaging interrupts */
16418c2ecf20Sopenharmony_ci	rval = ioread32(priv->regs + TSI721_OBDMAC_INTE(ch));
16428c2ecf20Sopenharmony_ci	rval &= ~inte_mask;
16438c2ecf20Sopenharmony_ci	iowrite32(rval, priv->regs + TSI721_OBDMAC_INTE(ch));
16448c2ecf20Sopenharmony_ci
16458c2ecf20Sopenharmony_ci	if (priv->flags & TSI721_USING_MSIX)
16468c2ecf20Sopenharmony_ci		return; /* Finished if we are in MSI-X mode */
16478c2ecf20Sopenharmony_ci
16488c2ecf20Sopenharmony_ci	/*
16498c2ecf20Sopenharmony_ci	 * For MSI and INTA interrupt signalling we need to disable next levels
16508c2ecf20Sopenharmony_ci	 */
16518c2ecf20Sopenharmony_ci
16528c2ecf20Sopenharmony_ci	/* Disable Device Channel Interrupt */
16538c2ecf20Sopenharmony_ci	rval = ioread32(priv->regs + TSI721_DEV_CHAN_INTE);
16548c2ecf20Sopenharmony_ci	rval &= ~TSI721_INT_OMSG_CHAN(ch);
16558c2ecf20Sopenharmony_ci	iowrite32(rval, priv->regs + TSI721_DEV_CHAN_INTE);
16568c2ecf20Sopenharmony_ci}
16578c2ecf20Sopenharmony_ci
16588c2ecf20Sopenharmony_ci/**
16598c2ecf20Sopenharmony_ci * tsi721_add_outb_message - Add message to the Tsi721 outbound message queue
16608c2ecf20Sopenharmony_ci * @mport: Master port with outbound message queue
16618c2ecf20Sopenharmony_ci * @rdev: Target of outbound message
16628c2ecf20Sopenharmony_ci * @mbox: Outbound mailbox
16638c2ecf20Sopenharmony_ci * @buffer: Message to add to outbound queue
16648c2ecf20Sopenharmony_ci * @len: Length of message
16658c2ecf20Sopenharmony_ci */
16668c2ecf20Sopenharmony_cistatic int
16678c2ecf20Sopenharmony_citsi721_add_outb_message(struct rio_mport *mport, struct rio_dev *rdev, int mbox,
16688c2ecf20Sopenharmony_ci			void *buffer, size_t len)
16698c2ecf20Sopenharmony_ci{
16708c2ecf20Sopenharmony_ci	struct tsi721_device *priv = mport->priv;
16718c2ecf20Sopenharmony_ci	struct tsi721_omsg_desc *desc;
16728c2ecf20Sopenharmony_ci	u32 tx_slot;
16738c2ecf20Sopenharmony_ci	unsigned long flags;
16748c2ecf20Sopenharmony_ci
16758c2ecf20Sopenharmony_ci	if (!priv->omsg_init[mbox] ||
16768c2ecf20Sopenharmony_ci	    len > TSI721_MSG_MAX_SIZE || len < 8)
16778c2ecf20Sopenharmony_ci		return -EINVAL;
16788c2ecf20Sopenharmony_ci
16798c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->omsg_ring[mbox].lock, flags);
16808c2ecf20Sopenharmony_ci
16818c2ecf20Sopenharmony_ci	tx_slot = priv->omsg_ring[mbox].tx_slot;
16828c2ecf20Sopenharmony_ci
16838c2ecf20Sopenharmony_ci	/* Copy copy message into transfer buffer */
16848c2ecf20Sopenharmony_ci	memcpy(priv->omsg_ring[mbox].omq_base[tx_slot], buffer, len);
16858c2ecf20Sopenharmony_ci
16868c2ecf20Sopenharmony_ci	if (len & 0x7)
16878c2ecf20Sopenharmony_ci		len += 8;
16888c2ecf20Sopenharmony_ci
16898c2ecf20Sopenharmony_ci	/* Build descriptor associated with buffer */
16908c2ecf20Sopenharmony_ci	desc = priv->omsg_ring[mbox].omd_base;
16918c2ecf20Sopenharmony_ci	desc[tx_slot].type_id = cpu_to_le32((DTYPE4 << 29) | rdev->destid);
16928c2ecf20Sopenharmony_ci#ifdef TSI721_OMSG_DESC_INT
16938c2ecf20Sopenharmony_ci	/* Request IOF_DONE interrupt generation for each N-th frame in queue */
16948c2ecf20Sopenharmony_ci	if (tx_slot % 4 == 0)
16958c2ecf20Sopenharmony_ci		desc[tx_slot].type_id |= cpu_to_le32(TSI721_OMD_IOF);
16968c2ecf20Sopenharmony_ci#endif
16978c2ecf20Sopenharmony_ci	desc[tx_slot].msg_info =
16988c2ecf20Sopenharmony_ci		cpu_to_le32((mport->sys_size << 26) | (mbox << 22) |
16998c2ecf20Sopenharmony_ci			    (0xe << 12) | (len & 0xff8));
17008c2ecf20Sopenharmony_ci	desc[tx_slot].bufptr_lo =
17018c2ecf20Sopenharmony_ci		cpu_to_le32((u64)priv->omsg_ring[mbox].omq_phys[tx_slot] &
17028c2ecf20Sopenharmony_ci			    0xffffffff);
17038c2ecf20Sopenharmony_ci	desc[tx_slot].bufptr_hi =
17048c2ecf20Sopenharmony_ci		cpu_to_le32((u64)priv->omsg_ring[mbox].omq_phys[tx_slot] >> 32);
17058c2ecf20Sopenharmony_ci
17068c2ecf20Sopenharmony_ci	priv->omsg_ring[mbox].wr_count++;
17078c2ecf20Sopenharmony_ci
17088c2ecf20Sopenharmony_ci	/* Go to next descriptor */
17098c2ecf20Sopenharmony_ci	if (++priv->omsg_ring[mbox].tx_slot == priv->omsg_ring[mbox].size) {
17108c2ecf20Sopenharmony_ci		priv->omsg_ring[mbox].tx_slot = 0;
17118c2ecf20Sopenharmony_ci		/* Move through the ring link descriptor at the end */
17128c2ecf20Sopenharmony_ci		priv->omsg_ring[mbox].wr_count++;
17138c2ecf20Sopenharmony_ci	}
17148c2ecf20Sopenharmony_ci
17158c2ecf20Sopenharmony_ci	mb();
17168c2ecf20Sopenharmony_ci
17178c2ecf20Sopenharmony_ci	/* Set new write count value */
17188c2ecf20Sopenharmony_ci	iowrite32(priv->omsg_ring[mbox].wr_count,
17198c2ecf20Sopenharmony_ci		priv->regs + TSI721_OBDMAC_DWRCNT(mbox));
17208c2ecf20Sopenharmony_ci	ioread32(priv->regs + TSI721_OBDMAC_DWRCNT(mbox));
17218c2ecf20Sopenharmony_ci
17228c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->omsg_ring[mbox].lock, flags);
17238c2ecf20Sopenharmony_ci
17248c2ecf20Sopenharmony_ci	return 0;
17258c2ecf20Sopenharmony_ci}
17268c2ecf20Sopenharmony_ci
17278c2ecf20Sopenharmony_ci/**
17288c2ecf20Sopenharmony_ci * tsi721_omsg_handler - Outbound Message Interrupt Handler
17298c2ecf20Sopenharmony_ci * @priv: pointer to tsi721 private data
17308c2ecf20Sopenharmony_ci * @ch:   number of OB MSG channel to service
17318c2ecf20Sopenharmony_ci *
17328c2ecf20Sopenharmony_ci * Services channel interrupts from outbound messaging engine.
17338c2ecf20Sopenharmony_ci */
17348c2ecf20Sopenharmony_cistatic void tsi721_omsg_handler(struct tsi721_device *priv, int ch)
17358c2ecf20Sopenharmony_ci{
17368c2ecf20Sopenharmony_ci	u32 omsg_int;
17378c2ecf20Sopenharmony_ci	struct rio_mport *mport = &priv->mport;
17388c2ecf20Sopenharmony_ci	void *dev_id = NULL;
17398c2ecf20Sopenharmony_ci	u32 tx_slot = 0xffffffff;
17408c2ecf20Sopenharmony_ci	int do_callback = 0;
17418c2ecf20Sopenharmony_ci
17428c2ecf20Sopenharmony_ci	spin_lock(&priv->omsg_ring[ch].lock);
17438c2ecf20Sopenharmony_ci
17448c2ecf20Sopenharmony_ci	omsg_int = ioread32(priv->regs + TSI721_OBDMAC_INT(ch));
17458c2ecf20Sopenharmony_ci
17468c2ecf20Sopenharmony_ci	if (omsg_int & TSI721_OBDMAC_INT_ST_FULL)
17478c2ecf20Sopenharmony_ci		tsi_info(&priv->pdev->dev,
17488c2ecf20Sopenharmony_ci			"OB MBOX%d: Status FIFO is full", ch);
17498c2ecf20Sopenharmony_ci
17508c2ecf20Sopenharmony_ci	if (omsg_int & (TSI721_OBDMAC_INT_DONE | TSI721_OBDMAC_INT_IOF_DONE)) {
17518c2ecf20Sopenharmony_ci		u32 srd_ptr;
17528c2ecf20Sopenharmony_ci		u64 *sts_ptr, last_ptr = 0, prev_ptr = 0;
17538c2ecf20Sopenharmony_ci		int i, j;
17548c2ecf20Sopenharmony_ci
17558c2ecf20Sopenharmony_ci		/*
17568c2ecf20Sopenharmony_ci		 * Find last successfully processed descriptor
17578c2ecf20Sopenharmony_ci		 */
17588c2ecf20Sopenharmony_ci
17598c2ecf20Sopenharmony_ci		/* Check and clear descriptor status FIFO entries */
17608c2ecf20Sopenharmony_ci		srd_ptr = priv->omsg_ring[ch].sts_rdptr;
17618c2ecf20Sopenharmony_ci		sts_ptr = priv->omsg_ring[ch].sts_base;
17628c2ecf20Sopenharmony_ci		j = srd_ptr * 8;
17638c2ecf20Sopenharmony_ci		while (sts_ptr[j]) {
17648c2ecf20Sopenharmony_ci			for (i = 0; i < 8 && sts_ptr[j]; i++, j++) {
17658c2ecf20Sopenharmony_ci				prev_ptr = last_ptr;
17668c2ecf20Sopenharmony_ci				last_ptr = le64_to_cpu(sts_ptr[j]);
17678c2ecf20Sopenharmony_ci				sts_ptr[j] = 0;
17688c2ecf20Sopenharmony_ci			}
17698c2ecf20Sopenharmony_ci
17708c2ecf20Sopenharmony_ci			++srd_ptr;
17718c2ecf20Sopenharmony_ci			srd_ptr %= priv->omsg_ring[ch].sts_size;
17728c2ecf20Sopenharmony_ci			j = srd_ptr * 8;
17738c2ecf20Sopenharmony_ci		}
17748c2ecf20Sopenharmony_ci
17758c2ecf20Sopenharmony_ci		if (last_ptr == 0)
17768c2ecf20Sopenharmony_ci			goto no_sts_update;
17778c2ecf20Sopenharmony_ci
17788c2ecf20Sopenharmony_ci		priv->omsg_ring[ch].sts_rdptr = srd_ptr;
17798c2ecf20Sopenharmony_ci		iowrite32(srd_ptr, priv->regs + TSI721_OBDMAC_DSRP(ch));
17808c2ecf20Sopenharmony_ci
17818c2ecf20Sopenharmony_ci		if (!mport->outb_msg[ch].mcback)
17828c2ecf20Sopenharmony_ci			goto no_sts_update;
17838c2ecf20Sopenharmony_ci
17848c2ecf20Sopenharmony_ci		/* Inform upper layer about transfer completion */
17858c2ecf20Sopenharmony_ci
17868c2ecf20Sopenharmony_ci		tx_slot = (last_ptr - (u64)priv->omsg_ring[ch].omd_phys)/
17878c2ecf20Sopenharmony_ci						sizeof(struct tsi721_omsg_desc);
17888c2ecf20Sopenharmony_ci
17898c2ecf20Sopenharmony_ci		/*
17908c2ecf20Sopenharmony_ci		 * Check if this is a Link Descriptor (LD).
17918c2ecf20Sopenharmony_ci		 * If yes, ignore LD and use descriptor processed
17928c2ecf20Sopenharmony_ci		 * before LD.
17938c2ecf20Sopenharmony_ci		 */
17948c2ecf20Sopenharmony_ci		if (tx_slot == priv->omsg_ring[ch].size) {
17958c2ecf20Sopenharmony_ci			if (prev_ptr)
17968c2ecf20Sopenharmony_ci				tx_slot = (prev_ptr -
17978c2ecf20Sopenharmony_ci					(u64)priv->omsg_ring[ch].omd_phys)/
17988c2ecf20Sopenharmony_ci						sizeof(struct tsi721_omsg_desc);
17998c2ecf20Sopenharmony_ci			else
18008c2ecf20Sopenharmony_ci				goto no_sts_update;
18018c2ecf20Sopenharmony_ci		}
18028c2ecf20Sopenharmony_ci
18038c2ecf20Sopenharmony_ci		if (tx_slot >= priv->omsg_ring[ch].size)
18048c2ecf20Sopenharmony_ci			tsi_debug(OMSG, &priv->pdev->dev,
18058c2ecf20Sopenharmony_ci				  "OB_MSG tx_slot=%x > size=%x",
18068c2ecf20Sopenharmony_ci				  tx_slot, priv->omsg_ring[ch].size);
18078c2ecf20Sopenharmony_ci		WARN_ON(tx_slot >= priv->omsg_ring[ch].size);
18088c2ecf20Sopenharmony_ci
18098c2ecf20Sopenharmony_ci		/* Move slot index to the next message to be sent */
18108c2ecf20Sopenharmony_ci		++tx_slot;
18118c2ecf20Sopenharmony_ci		if (tx_slot == priv->omsg_ring[ch].size)
18128c2ecf20Sopenharmony_ci			tx_slot = 0;
18138c2ecf20Sopenharmony_ci
18148c2ecf20Sopenharmony_ci		dev_id = priv->omsg_ring[ch].dev_id;
18158c2ecf20Sopenharmony_ci		do_callback = 1;
18168c2ecf20Sopenharmony_ci	}
18178c2ecf20Sopenharmony_ci
18188c2ecf20Sopenharmony_cino_sts_update:
18198c2ecf20Sopenharmony_ci
18208c2ecf20Sopenharmony_ci	if (omsg_int & TSI721_OBDMAC_INT_ERROR) {
18218c2ecf20Sopenharmony_ci		/*
18228c2ecf20Sopenharmony_ci		* Outbound message operation aborted due to error,
18238c2ecf20Sopenharmony_ci		* reinitialize OB MSG channel
18248c2ecf20Sopenharmony_ci		*/
18258c2ecf20Sopenharmony_ci
18268c2ecf20Sopenharmony_ci		tsi_debug(OMSG, &priv->pdev->dev, "OB MSG ABORT ch_stat=%x",
18278c2ecf20Sopenharmony_ci			  ioread32(priv->regs + TSI721_OBDMAC_STS(ch)));
18288c2ecf20Sopenharmony_ci
18298c2ecf20Sopenharmony_ci		iowrite32(TSI721_OBDMAC_INT_ERROR,
18308c2ecf20Sopenharmony_ci				priv->regs + TSI721_OBDMAC_INT(ch));
18318c2ecf20Sopenharmony_ci		iowrite32(TSI721_OBDMAC_CTL_RETRY_THR | TSI721_OBDMAC_CTL_INIT,
18328c2ecf20Sopenharmony_ci				priv->regs + TSI721_OBDMAC_CTL(ch));
18338c2ecf20Sopenharmony_ci		ioread32(priv->regs + TSI721_OBDMAC_CTL(ch));
18348c2ecf20Sopenharmony_ci
18358c2ecf20Sopenharmony_ci		/* Inform upper level to clear all pending tx slots */
18368c2ecf20Sopenharmony_ci		dev_id = priv->omsg_ring[ch].dev_id;
18378c2ecf20Sopenharmony_ci		tx_slot = priv->omsg_ring[ch].tx_slot;
18388c2ecf20Sopenharmony_ci		do_callback = 1;
18398c2ecf20Sopenharmony_ci
18408c2ecf20Sopenharmony_ci		/* Synch tx_slot tracking */
18418c2ecf20Sopenharmony_ci		iowrite32(priv->omsg_ring[ch].tx_slot,
18428c2ecf20Sopenharmony_ci			priv->regs + TSI721_OBDMAC_DRDCNT(ch));
18438c2ecf20Sopenharmony_ci		ioread32(priv->regs + TSI721_OBDMAC_DRDCNT(ch));
18448c2ecf20Sopenharmony_ci		priv->omsg_ring[ch].wr_count = priv->omsg_ring[ch].tx_slot;
18458c2ecf20Sopenharmony_ci		priv->omsg_ring[ch].sts_rdptr = 0;
18468c2ecf20Sopenharmony_ci	}
18478c2ecf20Sopenharmony_ci
18488c2ecf20Sopenharmony_ci	/* Clear channel interrupts */
18498c2ecf20Sopenharmony_ci	iowrite32(omsg_int, priv->regs + TSI721_OBDMAC_INT(ch));
18508c2ecf20Sopenharmony_ci
18518c2ecf20Sopenharmony_ci	if (!(priv->flags & TSI721_USING_MSIX)) {
18528c2ecf20Sopenharmony_ci		u32 ch_inte;
18538c2ecf20Sopenharmony_ci
18548c2ecf20Sopenharmony_ci		/* Re-enable channel interrupts */
18558c2ecf20Sopenharmony_ci		ch_inte = ioread32(priv->regs + TSI721_DEV_CHAN_INTE);
18568c2ecf20Sopenharmony_ci		ch_inte |= TSI721_INT_OMSG_CHAN(ch);
18578c2ecf20Sopenharmony_ci		iowrite32(ch_inte, priv->regs + TSI721_DEV_CHAN_INTE);
18588c2ecf20Sopenharmony_ci	}
18598c2ecf20Sopenharmony_ci
18608c2ecf20Sopenharmony_ci	spin_unlock(&priv->omsg_ring[ch].lock);
18618c2ecf20Sopenharmony_ci
18628c2ecf20Sopenharmony_ci	if (mport->outb_msg[ch].mcback && do_callback)
18638c2ecf20Sopenharmony_ci		mport->outb_msg[ch].mcback(mport, dev_id, ch, tx_slot);
18648c2ecf20Sopenharmony_ci}
18658c2ecf20Sopenharmony_ci
18668c2ecf20Sopenharmony_ci/**
18678c2ecf20Sopenharmony_ci * tsi721_open_outb_mbox - Initialize Tsi721 outbound mailbox
18688c2ecf20Sopenharmony_ci * @mport: Master port implementing Outbound Messaging Engine
18698c2ecf20Sopenharmony_ci * @dev_id: Device specific pointer to pass on event
18708c2ecf20Sopenharmony_ci * @mbox: Mailbox to open
18718c2ecf20Sopenharmony_ci * @entries: Number of entries in the outbound mailbox ring
18728c2ecf20Sopenharmony_ci */
18738c2ecf20Sopenharmony_cistatic int tsi721_open_outb_mbox(struct rio_mport *mport, void *dev_id,
18748c2ecf20Sopenharmony_ci				 int mbox, int entries)
18758c2ecf20Sopenharmony_ci{
18768c2ecf20Sopenharmony_ci	struct tsi721_device *priv = mport->priv;
18778c2ecf20Sopenharmony_ci	struct tsi721_omsg_desc *bd_ptr;
18788c2ecf20Sopenharmony_ci	int i, rc = 0;
18798c2ecf20Sopenharmony_ci
18808c2ecf20Sopenharmony_ci	if ((entries < TSI721_OMSGD_MIN_RING_SIZE) ||
18818c2ecf20Sopenharmony_ci	    (entries > (TSI721_OMSGD_RING_SIZE)) ||
18828c2ecf20Sopenharmony_ci	    (!is_power_of_2(entries)) || mbox >= RIO_MAX_MBOX) {
18838c2ecf20Sopenharmony_ci		rc = -EINVAL;
18848c2ecf20Sopenharmony_ci		goto out;
18858c2ecf20Sopenharmony_ci	}
18868c2ecf20Sopenharmony_ci
18878c2ecf20Sopenharmony_ci	if ((mbox_sel & (1 << mbox)) == 0) {
18888c2ecf20Sopenharmony_ci		rc = -ENODEV;
18898c2ecf20Sopenharmony_ci		goto out;
18908c2ecf20Sopenharmony_ci	}
18918c2ecf20Sopenharmony_ci
18928c2ecf20Sopenharmony_ci	priv->omsg_ring[mbox].dev_id = dev_id;
18938c2ecf20Sopenharmony_ci	priv->omsg_ring[mbox].size = entries;
18948c2ecf20Sopenharmony_ci	priv->omsg_ring[mbox].sts_rdptr = 0;
18958c2ecf20Sopenharmony_ci	spin_lock_init(&priv->omsg_ring[mbox].lock);
18968c2ecf20Sopenharmony_ci
18978c2ecf20Sopenharmony_ci	/* Outbound Msg Buffer allocation based on
18988c2ecf20Sopenharmony_ci	   the number of maximum descriptor entries */
18998c2ecf20Sopenharmony_ci	for (i = 0; i < entries; i++) {
19008c2ecf20Sopenharmony_ci		priv->omsg_ring[mbox].omq_base[i] =
19018c2ecf20Sopenharmony_ci			dma_alloc_coherent(
19028c2ecf20Sopenharmony_ci				&priv->pdev->dev, TSI721_MSG_BUFFER_SIZE,
19038c2ecf20Sopenharmony_ci				&priv->omsg_ring[mbox].omq_phys[i],
19048c2ecf20Sopenharmony_ci				GFP_KERNEL);
19058c2ecf20Sopenharmony_ci		if (priv->omsg_ring[mbox].omq_base[i] == NULL) {
19068c2ecf20Sopenharmony_ci			tsi_debug(OMSG, &priv->pdev->dev,
19078c2ecf20Sopenharmony_ci				  "ENOMEM for OB_MSG_%d data buffer", mbox);
19088c2ecf20Sopenharmony_ci			rc = -ENOMEM;
19098c2ecf20Sopenharmony_ci			goto out_buf;
19108c2ecf20Sopenharmony_ci		}
19118c2ecf20Sopenharmony_ci	}
19128c2ecf20Sopenharmony_ci
19138c2ecf20Sopenharmony_ci	/* Outbound message descriptor allocation */
19148c2ecf20Sopenharmony_ci	priv->omsg_ring[mbox].omd_base = dma_alloc_coherent(
19158c2ecf20Sopenharmony_ci				&priv->pdev->dev,
19168c2ecf20Sopenharmony_ci				(entries + 1) * sizeof(struct tsi721_omsg_desc),
19178c2ecf20Sopenharmony_ci				&priv->omsg_ring[mbox].omd_phys, GFP_KERNEL);
19188c2ecf20Sopenharmony_ci	if (priv->omsg_ring[mbox].omd_base == NULL) {
19198c2ecf20Sopenharmony_ci		tsi_debug(OMSG, &priv->pdev->dev,
19208c2ecf20Sopenharmony_ci			"ENOMEM for OB_MSG_%d descriptor memory", mbox);
19218c2ecf20Sopenharmony_ci		rc = -ENOMEM;
19228c2ecf20Sopenharmony_ci		goto out_buf;
19238c2ecf20Sopenharmony_ci	}
19248c2ecf20Sopenharmony_ci
19258c2ecf20Sopenharmony_ci	priv->omsg_ring[mbox].tx_slot = 0;
19268c2ecf20Sopenharmony_ci
19278c2ecf20Sopenharmony_ci	/* Outbound message descriptor status FIFO allocation */
19288c2ecf20Sopenharmony_ci	priv->omsg_ring[mbox].sts_size = roundup_pow_of_two(entries + 1);
19298c2ecf20Sopenharmony_ci	priv->omsg_ring[mbox].sts_base = dma_alloc_coherent(&priv->pdev->dev,
19308c2ecf20Sopenharmony_ci							    priv->omsg_ring[mbox].sts_size * sizeof(struct tsi721_dma_sts),
19318c2ecf20Sopenharmony_ci							    &priv->omsg_ring[mbox].sts_phys,
19328c2ecf20Sopenharmony_ci							    GFP_KERNEL);
19338c2ecf20Sopenharmony_ci	if (priv->omsg_ring[mbox].sts_base == NULL) {
19348c2ecf20Sopenharmony_ci		tsi_debug(OMSG, &priv->pdev->dev,
19358c2ecf20Sopenharmony_ci			"ENOMEM for OB_MSG_%d status FIFO", mbox);
19368c2ecf20Sopenharmony_ci		rc = -ENOMEM;
19378c2ecf20Sopenharmony_ci		goto out_desc;
19388c2ecf20Sopenharmony_ci	}
19398c2ecf20Sopenharmony_ci
19408c2ecf20Sopenharmony_ci	/*
19418c2ecf20Sopenharmony_ci	 * Configure Outbound Messaging Engine
19428c2ecf20Sopenharmony_ci	 */
19438c2ecf20Sopenharmony_ci
19448c2ecf20Sopenharmony_ci	/* Setup Outbound Message descriptor pointer */
19458c2ecf20Sopenharmony_ci	iowrite32(((u64)priv->omsg_ring[mbox].omd_phys >> 32),
19468c2ecf20Sopenharmony_ci			priv->regs + TSI721_OBDMAC_DPTRH(mbox));
19478c2ecf20Sopenharmony_ci	iowrite32(((u64)priv->omsg_ring[mbox].omd_phys &
19488c2ecf20Sopenharmony_ci					TSI721_OBDMAC_DPTRL_MASK),
19498c2ecf20Sopenharmony_ci			priv->regs + TSI721_OBDMAC_DPTRL(mbox));
19508c2ecf20Sopenharmony_ci
19518c2ecf20Sopenharmony_ci	/* Setup Outbound Message descriptor status FIFO */
19528c2ecf20Sopenharmony_ci	iowrite32(((u64)priv->omsg_ring[mbox].sts_phys >> 32),
19538c2ecf20Sopenharmony_ci			priv->regs + TSI721_OBDMAC_DSBH(mbox));
19548c2ecf20Sopenharmony_ci	iowrite32(((u64)priv->omsg_ring[mbox].sts_phys &
19558c2ecf20Sopenharmony_ci					TSI721_OBDMAC_DSBL_MASK),
19568c2ecf20Sopenharmony_ci			priv->regs + TSI721_OBDMAC_DSBL(mbox));
19578c2ecf20Sopenharmony_ci	iowrite32(TSI721_DMAC_DSSZ_SIZE(priv->omsg_ring[mbox].sts_size),
19588c2ecf20Sopenharmony_ci		priv->regs + (u32)TSI721_OBDMAC_DSSZ(mbox));
19598c2ecf20Sopenharmony_ci
19608c2ecf20Sopenharmony_ci	/* Enable interrupts */
19618c2ecf20Sopenharmony_ci
19628c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI_MSI
19638c2ecf20Sopenharmony_ci	if (priv->flags & TSI721_USING_MSIX) {
19648c2ecf20Sopenharmony_ci		int idx = TSI721_VECT_OMB0_DONE + mbox;
19658c2ecf20Sopenharmony_ci
19668c2ecf20Sopenharmony_ci		/* Request interrupt service if we are in MSI-X mode */
19678c2ecf20Sopenharmony_ci		rc = request_irq(priv->msix[idx].vector, tsi721_omsg_msix, 0,
19688c2ecf20Sopenharmony_ci				 priv->msix[idx].irq_name, (void *)priv);
19698c2ecf20Sopenharmony_ci
19708c2ecf20Sopenharmony_ci		if (rc) {
19718c2ecf20Sopenharmony_ci			tsi_debug(OMSG, &priv->pdev->dev,
19728c2ecf20Sopenharmony_ci				"Unable to get MSI-X IRQ for OBOX%d-DONE",
19738c2ecf20Sopenharmony_ci				mbox);
19748c2ecf20Sopenharmony_ci			goto out_stat;
19758c2ecf20Sopenharmony_ci		}
19768c2ecf20Sopenharmony_ci
19778c2ecf20Sopenharmony_ci		idx = TSI721_VECT_OMB0_INT + mbox;
19788c2ecf20Sopenharmony_ci		rc = request_irq(priv->msix[idx].vector, tsi721_omsg_msix, 0,
19798c2ecf20Sopenharmony_ci				 priv->msix[idx].irq_name, (void *)priv);
19808c2ecf20Sopenharmony_ci
19818c2ecf20Sopenharmony_ci		if (rc)	{
19828c2ecf20Sopenharmony_ci			tsi_debug(OMSG, &priv->pdev->dev,
19838c2ecf20Sopenharmony_ci				"Unable to get MSI-X IRQ for MBOX%d-INT", mbox);
19848c2ecf20Sopenharmony_ci			idx = TSI721_VECT_OMB0_DONE + mbox;
19858c2ecf20Sopenharmony_ci			free_irq(priv->msix[idx].vector, (void *)priv);
19868c2ecf20Sopenharmony_ci			goto out_stat;
19878c2ecf20Sopenharmony_ci		}
19888c2ecf20Sopenharmony_ci	}
19898c2ecf20Sopenharmony_ci#endif /* CONFIG_PCI_MSI */
19908c2ecf20Sopenharmony_ci
19918c2ecf20Sopenharmony_ci	tsi721_omsg_interrupt_enable(priv, mbox, TSI721_OBDMAC_INT_ALL);
19928c2ecf20Sopenharmony_ci
19938c2ecf20Sopenharmony_ci	/* Initialize Outbound Message descriptors ring */
19948c2ecf20Sopenharmony_ci	bd_ptr = priv->omsg_ring[mbox].omd_base;
19958c2ecf20Sopenharmony_ci	bd_ptr[entries].type_id = cpu_to_le32(DTYPE5 << 29);
19968c2ecf20Sopenharmony_ci	bd_ptr[entries].msg_info = 0;
19978c2ecf20Sopenharmony_ci	bd_ptr[entries].next_lo =
19988c2ecf20Sopenharmony_ci		cpu_to_le32((u64)priv->omsg_ring[mbox].omd_phys &
19998c2ecf20Sopenharmony_ci		TSI721_OBDMAC_DPTRL_MASK);
20008c2ecf20Sopenharmony_ci	bd_ptr[entries].next_hi =
20018c2ecf20Sopenharmony_ci		cpu_to_le32((u64)priv->omsg_ring[mbox].omd_phys >> 32);
20028c2ecf20Sopenharmony_ci	priv->omsg_ring[mbox].wr_count = 0;
20038c2ecf20Sopenharmony_ci	mb();
20048c2ecf20Sopenharmony_ci
20058c2ecf20Sopenharmony_ci	/* Initialize Outbound Message engine */
20068c2ecf20Sopenharmony_ci	iowrite32(TSI721_OBDMAC_CTL_RETRY_THR | TSI721_OBDMAC_CTL_INIT,
20078c2ecf20Sopenharmony_ci		  priv->regs + TSI721_OBDMAC_CTL(mbox));
20088c2ecf20Sopenharmony_ci	ioread32(priv->regs + TSI721_OBDMAC_DWRCNT(mbox));
20098c2ecf20Sopenharmony_ci	udelay(10);
20108c2ecf20Sopenharmony_ci
20118c2ecf20Sopenharmony_ci	priv->omsg_init[mbox] = 1;
20128c2ecf20Sopenharmony_ci
20138c2ecf20Sopenharmony_ci	return 0;
20148c2ecf20Sopenharmony_ci
20158c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI_MSI
20168c2ecf20Sopenharmony_ciout_stat:
20178c2ecf20Sopenharmony_ci	dma_free_coherent(&priv->pdev->dev,
20188c2ecf20Sopenharmony_ci		priv->omsg_ring[mbox].sts_size * sizeof(struct tsi721_dma_sts),
20198c2ecf20Sopenharmony_ci		priv->omsg_ring[mbox].sts_base,
20208c2ecf20Sopenharmony_ci		priv->omsg_ring[mbox].sts_phys);
20218c2ecf20Sopenharmony_ci
20228c2ecf20Sopenharmony_ci	priv->omsg_ring[mbox].sts_base = NULL;
20238c2ecf20Sopenharmony_ci#endif /* CONFIG_PCI_MSI */
20248c2ecf20Sopenharmony_ci
20258c2ecf20Sopenharmony_ciout_desc:
20268c2ecf20Sopenharmony_ci	dma_free_coherent(&priv->pdev->dev,
20278c2ecf20Sopenharmony_ci		(entries + 1) * sizeof(struct tsi721_omsg_desc),
20288c2ecf20Sopenharmony_ci		priv->omsg_ring[mbox].omd_base,
20298c2ecf20Sopenharmony_ci		priv->omsg_ring[mbox].omd_phys);
20308c2ecf20Sopenharmony_ci
20318c2ecf20Sopenharmony_ci	priv->omsg_ring[mbox].omd_base = NULL;
20328c2ecf20Sopenharmony_ci
20338c2ecf20Sopenharmony_ciout_buf:
20348c2ecf20Sopenharmony_ci	for (i = 0; i < priv->omsg_ring[mbox].size; i++) {
20358c2ecf20Sopenharmony_ci		if (priv->omsg_ring[mbox].omq_base[i]) {
20368c2ecf20Sopenharmony_ci			dma_free_coherent(&priv->pdev->dev,
20378c2ecf20Sopenharmony_ci				TSI721_MSG_BUFFER_SIZE,
20388c2ecf20Sopenharmony_ci				priv->omsg_ring[mbox].omq_base[i],
20398c2ecf20Sopenharmony_ci				priv->omsg_ring[mbox].omq_phys[i]);
20408c2ecf20Sopenharmony_ci
20418c2ecf20Sopenharmony_ci			priv->omsg_ring[mbox].omq_base[i] = NULL;
20428c2ecf20Sopenharmony_ci		}
20438c2ecf20Sopenharmony_ci	}
20448c2ecf20Sopenharmony_ci
20458c2ecf20Sopenharmony_ciout:
20468c2ecf20Sopenharmony_ci	return rc;
20478c2ecf20Sopenharmony_ci}
20488c2ecf20Sopenharmony_ci
20498c2ecf20Sopenharmony_ci/**
20508c2ecf20Sopenharmony_ci * tsi721_close_outb_mbox - Close Tsi721 outbound mailbox
20518c2ecf20Sopenharmony_ci * @mport: Master port implementing the outbound message unit
20528c2ecf20Sopenharmony_ci * @mbox: Mailbox to close
20538c2ecf20Sopenharmony_ci */
20548c2ecf20Sopenharmony_cistatic void tsi721_close_outb_mbox(struct rio_mport *mport, int mbox)
20558c2ecf20Sopenharmony_ci{
20568c2ecf20Sopenharmony_ci	struct tsi721_device *priv = mport->priv;
20578c2ecf20Sopenharmony_ci	u32 i;
20588c2ecf20Sopenharmony_ci
20598c2ecf20Sopenharmony_ci	if (!priv->omsg_init[mbox])
20608c2ecf20Sopenharmony_ci		return;
20618c2ecf20Sopenharmony_ci	priv->omsg_init[mbox] = 0;
20628c2ecf20Sopenharmony_ci
20638c2ecf20Sopenharmony_ci	/* Disable Interrupts */
20648c2ecf20Sopenharmony_ci
20658c2ecf20Sopenharmony_ci	tsi721_omsg_interrupt_disable(priv, mbox, TSI721_OBDMAC_INT_ALL);
20668c2ecf20Sopenharmony_ci
20678c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI_MSI
20688c2ecf20Sopenharmony_ci	if (priv->flags & TSI721_USING_MSIX) {
20698c2ecf20Sopenharmony_ci		free_irq(priv->msix[TSI721_VECT_OMB0_DONE + mbox].vector,
20708c2ecf20Sopenharmony_ci			 (void *)priv);
20718c2ecf20Sopenharmony_ci		free_irq(priv->msix[TSI721_VECT_OMB0_INT + mbox].vector,
20728c2ecf20Sopenharmony_ci			 (void *)priv);
20738c2ecf20Sopenharmony_ci	}
20748c2ecf20Sopenharmony_ci#endif /* CONFIG_PCI_MSI */
20758c2ecf20Sopenharmony_ci
20768c2ecf20Sopenharmony_ci	/* Free OMSG Descriptor Status FIFO */
20778c2ecf20Sopenharmony_ci	dma_free_coherent(&priv->pdev->dev,
20788c2ecf20Sopenharmony_ci		priv->omsg_ring[mbox].sts_size * sizeof(struct tsi721_dma_sts),
20798c2ecf20Sopenharmony_ci		priv->omsg_ring[mbox].sts_base,
20808c2ecf20Sopenharmony_ci		priv->omsg_ring[mbox].sts_phys);
20818c2ecf20Sopenharmony_ci
20828c2ecf20Sopenharmony_ci	priv->omsg_ring[mbox].sts_base = NULL;
20838c2ecf20Sopenharmony_ci
20848c2ecf20Sopenharmony_ci	/* Free OMSG descriptors */
20858c2ecf20Sopenharmony_ci	dma_free_coherent(&priv->pdev->dev,
20868c2ecf20Sopenharmony_ci		(priv->omsg_ring[mbox].size + 1) *
20878c2ecf20Sopenharmony_ci			sizeof(struct tsi721_omsg_desc),
20888c2ecf20Sopenharmony_ci		priv->omsg_ring[mbox].omd_base,
20898c2ecf20Sopenharmony_ci		priv->omsg_ring[mbox].omd_phys);
20908c2ecf20Sopenharmony_ci
20918c2ecf20Sopenharmony_ci	priv->omsg_ring[mbox].omd_base = NULL;
20928c2ecf20Sopenharmony_ci
20938c2ecf20Sopenharmony_ci	/* Free message buffers */
20948c2ecf20Sopenharmony_ci	for (i = 0; i < priv->omsg_ring[mbox].size; i++) {
20958c2ecf20Sopenharmony_ci		if (priv->omsg_ring[mbox].omq_base[i]) {
20968c2ecf20Sopenharmony_ci			dma_free_coherent(&priv->pdev->dev,
20978c2ecf20Sopenharmony_ci				TSI721_MSG_BUFFER_SIZE,
20988c2ecf20Sopenharmony_ci				priv->omsg_ring[mbox].omq_base[i],
20998c2ecf20Sopenharmony_ci				priv->omsg_ring[mbox].omq_phys[i]);
21008c2ecf20Sopenharmony_ci
21018c2ecf20Sopenharmony_ci			priv->omsg_ring[mbox].omq_base[i] = NULL;
21028c2ecf20Sopenharmony_ci		}
21038c2ecf20Sopenharmony_ci	}
21048c2ecf20Sopenharmony_ci}
21058c2ecf20Sopenharmony_ci
21068c2ecf20Sopenharmony_ci/**
21078c2ecf20Sopenharmony_ci * tsi721_imsg_handler - Inbound Message Interrupt Handler
21088c2ecf20Sopenharmony_ci * @priv: pointer to tsi721 private data
21098c2ecf20Sopenharmony_ci * @ch: inbound message channel number to service
21108c2ecf20Sopenharmony_ci *
21118c2ecf20Sopenharmony_ci * Services channel interrupts from inbound messaging engine.
21128c2ecf20Sopenharmony_ci */
21138c2ecf20Sopenharmony_cistatic void tsi721_imsg_handler(struct tsi721_device *priv, int ch)
21148c2ecf20Sopenharmony_ci{
21158c2ecf20Sopenharmony_ci	u32 mbox = ch - 4;
21168c2ecf20Sopenharmony_ci	u32 imsg_int;
21178c2ecf20Sopenharmony_ci	struct rio_mport *mport = &priv->mport;
21188c2ecf20Sopenharmony_ci
21198c2ecf20Sopenharmony_ci	spin_lock(&priv->imsg_ring[mbox].lock);
21208c2ecf20Sopenharmony_ci
21218c2ecf20Sopenharmony_ci	imsg_int = ioread32(priv->regs + TSI721_IBDMAC_INT(ch));
21228c2ecf20Sopenharmony_ci
21238c2ecf20Sopenharmony_ci	if (imsg_int & TSI721_IBDMAC_INT_SRTO)
21248c2ecf20Sopenharmony_ci		tsi_info(&priv->pdev->dev, "IB MBOX%d SRIO timeout", mbox);
21258c2ecf20Sopenharmony_ci
21268c2ecf20Sopenharmony_ci	if (imsg_int & TSI721_IBDMAC_INT_PC_ERROR)
21278c2ecf20Sopenharmony_ci		tsi_info(&priv->pdev->dev, "IB MBOX%d PCIe error", mbox);
21288c2ecf20Sopenharmony_ci
21298c2ecf20Sopenharmony_ci	if (imsg_int & TSI721_IBDMAC_INT_FQ_LOW)
21308c2ecf20Sopenharmony_ci		tsi_info(&priv->pdev->dev, "IB MBOX%d IB free queue low", mbox);
21318c2ecf20Sopenharmony_ci
21328c2ecf20Sopenharmony_ci	/* Clear IB channel interrupts */
21338c2ecf20Sopenharmony_ci	iowrite32(imsg_int, priv->regs + TSI721_IBDMAC_INT(ch));
21348c2ecf20Sopenharmony_ci
21358c2ecf20Sopenharmony_ci	/* If an IB Msg is received notify the upper layer */
21368c2ecf20Sopenharmony_ci	if (imsg_int & TSI721_IBDMAC_INT_DQ_RCV &&
21378c2ecf20Sopenharmony_ci		mport->inb_msg[mbox].mcback)
21388c2ecf20Sopenharmony_ci		mport->inb_msg[mbox].mcback(mport,
21398c2ecf20Sopenharmony_ci				priv->imsg_ring[mbox].dev_id, mbox, -1);
21408c2ecf20Sopenharmony_ci
21418c2ecf20Sopenharmony_ci	if (!(priv->flags & TSI721_USING_MSIX)) {
21428c2ecf20Sopenharmony_ci		u32 ch_inte;
21438c2ecf20Sopenharmony_ci
21448c2ecf20Sopenharmony_ci		/* Re-enable channel interrupts */
21458c2ecf20Sopenharmony_ci		ch_inte = ioread32(priv->regs + TSI721_DEV_CHAN_INTE);
21468c2ecf20Sopenharmony_ci		ch_inte |= TSI721_INT_IMSG_CHAN(ch);
21478c2ecf20Sopenharmony_ci		iowrite32(ch_inte, priv->regs + TSI721_DEV_CHAN_INTE);
21488c2ecf20Sopenharmony_ci	}
21498c2ecf20Sopenharmony_ci
21508c2ecf20Sopenharmony_ci	spin_unlock(&priv->imsg_ring[mbox].lock);
21518c2ecf20Sopenharmony_ci}
21528c2ecf20Sopenharmony_ci
21538c2ecf20Sopenharmony_ci/**
21548c2ecf20Sopenharmony_ci * tsi721_open_inb_mbox - Initialize Tsi721 inbound mailbox
21558c2ecf20Sopenharmony_ci * @mport: Master port implementing the Inbound Messaging Engine
21568c2ecf20Sopenharmony_ci * @dev_id: Device specific pointer to pass on event
21578c2ecf20Sopenharmony_ci * @mbox: Mailbox to open
21588c2ecf20Sopenharmony_ci * @entries: Number of entries in the inbound mailbox ring
21598c2ecf20Sopenharmony_ci */
21608c2ecf20Sopenharmony_cistatic int tsi721_open_inb_mbox(struct rio_mport *mport, void *dev_id,
21618c2ecf20Sopenharmony_ci				int mbox, int entries)
21628c2ecf20Sopenharmony_ci{
21638c2ecf20Sopenharmony_ci	struct tsi721_device *priv = mport->priv;
21648c2ecf20Sopenharmony_ci	int ch = mbox + 4;
21658c2ecf20Sopenharmony_ci	int i;
21668c2ecf20Sopenharmony_ci	u64 *free_ptr;
21678c2ecf20Sopenharmony_ci	int rc = 0;
21688c2ecf20Sopenharmony_ci
21698c2ecf20Sopenharmony_ci	if ((entries < TSI721_IMSGD_MIN_RING_SIZE) ||
21708c2ecf20Sopenharmony_ci	    (entries > TSI721_IMSGD_RING_SIZE) ||
21718c2ecf20Sopenharmony_ci	    (!is_power_of_2(entries)) || mbox >= RIO_MAX_MBOX) {
21728c2ecf20Sopenharmony_ci		rc = -EINVAL;
21738c2ecf20Sopenharmony_ci		goto out;
21748c2ecf20Sopenharmony_ci	}
21758c2ecf20Sopenharmony_ci
21768c2ecf20Sopenharmony_ci	if ((mbox_sel & (1 << mbox)) == 0) {
21778c2ecf20Sopenharmony_ci		rc = -ENODEV;
21788c2ecf20Sopenharmony_ci		goto out;
21798c2ecf20Sopenharmony_ci	}
21808c2ecf20Sopenharmony_ci
21818c2ecf20Sopenharmony_ci	/* Initialize IB Messaging Ring */
21828c2ecf20Sopenharmony_ci	priv->imsg_ring[mbox].dev_id = dev_id;
21838c2ecf20Sopenharmony_ci	priv->imsg_ring[mbox].size = entries;
21848c2ecf20Sopenharmony_ci	priv->imsg_ring[mbox].rx_slot = 0;
21858c2ecf20Sopenharmony_ci	priv->imsg_ring[mbox].desc_rdptr = 0;
21868c2ecf20Sopenharmony_ci	priv->imsg_ring[mbox].fq_wrptr = 0;
21878c2ecf20Sopenharmony_ci	for (i = 0; i < priv->imsg_ring[mbox].size; i++)
21888c2ecf20Sopenharmony_ci		priv->imsg_ring[mbox].imq_base[i] = NULL;
21898c2ecf20Sopenharmony_ci	spin_lock_init(&priv->imsg_ring[mbox].lock);
21908c2ecf20Sopenharmony_ci
21918c2ecf20Sopenharmony_ci	/* Allocate buffers for incoming messages */
21928c2ecf20Sopenharmony_ci	priv->imsg_ring[mbox].buf_base =
21938c2ecf20Sopenharmony_ci		dma_alloc_coherent(&priv->pdev->dev,
21948c2ecf20Sopenharmony_ci				   entries * TSI721_MSG_BUFFER_SIZE,
21958c2ecf20Sopenharmony_ci				   &priv->imsg_ring[mbox].buf_phys,
21968c2ecf20Sopenharmony_ci				   GFP_KERNEL);
21978c2ecf20Sopenharmony_ci
21988c2ecf20Sopenharmony_ci	if (priv->imsg_ring[mbox].buf_base == NULL) {
21998c2ecf20Sopenharmony_ci		tsi_err(&priv->pdev->dev,
22008c2ecf20Sopenharmony_ci			"Failed to allocate buffers for IB MBOX%d", mbox);
22018c2ecf20Sopenharmony_ci		rc = -ENOMEM;
22028c2ecf20Sopenharmony_ci		goto out;
22038c2ecf20Sopenharmony_ci	}
22048c2ecf20Sopenharmony_ci
22058c2ecf20Sopenharmony_ci	/* Allocate memory for circular free list */
22068c2ecf20Sopenharmony_ci	priv->imsg_ring[mbox].imfq_base =
22078c2ecf20Sopenharmony_ci		dma_alloc_coherent(&priv->pdev->dev,
22088c2ecf20Sopenharmony_ci				   entries * 8,
22098c2ecf20Sopenharmony_ci				   &priv->imsg_ring[mbox].imfq_phys,
22108c2ecf20Sopenharmony_ci				   GFP_KERNEL);
22118c2ecf20Sopenharmony_ci
22128c2ecf20Sopenharmony_ci	if (priv->imsg_ring[mbox].imfq_base == NULL) {
22138c2ecf20Sopenharmony_ci		tsi_err(&priv->pdev->dev,
22148c2ecf20Sopenharmony_ci			"Failed to allocate free queue for IB MBOX%d", mbox);
22158c2ecf20Sopenharmony_ci		rc = -ENOMEM;
22168c2ecf20Sopenharmony_ci		goto out_buf;
22178c2ecf20Sopenharmony_ci	}
22188c2ecf20Sopenharmony_ci
22198c2ecf20Sopenharmony_ci	/* Allocate memory for Inbound message descriptors */
22208c2ecf20Sopenharmony_ci	priv->imsg_ring[mbox].imd_base =
22218c2ecf20Sopenharmony_ci		dma_alloc_coherent(&priv->pdev->dev,
22228c2ecf20Sopenharmony_ci				   entries * sizeof(struct tsi721_imsg_desc),
22238c2ecf20Sopenharmony_ci				   &priv->imsg_ring[mbox].imd_phys, GFP_KERNEL);
22248c2ecf20Sopenharmony_ci
22258c2ecf20Sopenharmony_ci	if (priv->imsg_ring[mbox].imd_base == NULL) {
22268c2ecf20Sopenharmony_ci		tsi_err(&priv->pdev->dev,
22278c2ecf20Sopenharmony_ci			"Failed to allocate descriptor memory for IB MBOX%d",
22288c2ecf20Sopenharmony_ci			mbox);
22298c2ecf20Sopenharmony_ci		rc = -ENOMEM;
22308c2ecf20Sopenharmony_ci		goto out_dma;
22318c2ecf20Sopenharmony_ci	}
22328c2ecf20Sopenharmony_ci
22338c2ecf20Sopenharmony_ci	/* Fill free buffer pointer list */
22348c2ecf20Sopenharmony_ci	free_ptr = priv->imsg_ring[mbox].imfq_base;
22358c2ecf20Sopenharmony_ci	for (i = 0; i < entries; i++)
22368c2ecf20Sopenharmony_ci		free_ptr[i] = cpu_to_le64(
22378c2ecf20Sopenharmony_ci				(u64)(priv->imsg_ring[mbox].buf_phys) +
22388c2ecf20Sopenharmony_ci				i * 0x1000);
22398c2ecf20Sopenharmony_ci
22408c2ecf20Sopenharmony_ci	mb();
22418c2ecf20Sopenharmony_ci
22428c2ecf20Sopenharmony_ci	/*
22438c2ecf20Sopenharmony_ci	 * For mapping of inbound SRIO Messages into appropriate queues we need
22448c2ecf20Sopenharmony_ci	 * to set Inbound Device ID register in the messaging engine. We do it
22458c2ecf20Sopenharmony_ci	 * once when first inbound mailbox is requested.
22468c2ecf20Sopenharmony_ci	 */
22478c2ecf20Sopenharmony_ci	if (!(priv->flags & TSI721_IMSGID_SET)) {
22488c2ecf20Sopenharmony_ci		iowrite32((u32)priv->mport.host_deviceid,
22498c2ecf20Sopenharmony_ci			priv->regs + TSI721_IB_DEVID);
22508c2ecf20Sopenharmony_ci		priv->flags |= TSI721_IMSGID_SET;
22518c2ecf20Sopenharmony_ci	}
22528c2ecf20Sopenharmony_ci
22538c2ecf20Sopenharmony_ci	/*
22548c2ecf20Sopenharmony_ci	 * Configure Inbound Messaging channel (ch = mbox + 4)
22558c2ecf20Sopenharmony_ci	 */
22568c2ecf20Sopenharmony_ci
22578c2ecf20Sopenharmony_ci	/* Setup Inbound Message free queue */
22588c2ecf20Sopenharmony_ci	iowrite32(((u64)priv->imsg_ring[mbox].imfq_phys >> 32),
22598c2ecf20Sopenharmony_ci		priv->regs + TSI721_IBDMAC_FQBH(ch));
22608c2ecf20Sopenharmony_ci	iowrite32(((u64)priv->imsg_ring[mbox].imfq_phys &
22618c2ecf20Sopenharmony_ci			TSI721_IBDMAC_FQBL_MASK),
22628c2ecf20Sopenharmony_ci		priv->regs+TSI721_IBDMAC_FQBL(ch));
22638c2ecf20Sopenharmony_ci	iowrite32(TSI721_DMAC_DSSZ_SIZE(entries),
22648c2ecf20Sopenharmony_ci		priv->regs + TSI721_IBDMAC_FQSZ(ch));
22658c2ecf20Sopenharmony_ci
22668c2ecf20Sopenharmony_ci	/* Setup Inbound Message descriptor queue */
22678c2ecf20Sopenharmony_ci	iowrite32(((u64)priv->imsg_ring[mbox].imd_phys >> 32),
22688c2ecf20Sopenharmony_ci		priv->regs + TSI721_IBDMAC_DQBH(ch));
22698c2ecf20Sopenharmony_ci	iowrite32(((u32)priv->imsg_ring[mbox].imd_phys &
22708c2ecf20Sopenharmony_ci		   (u32)TSI721_IBDMAC_DQBL_MASK),
22718c2ecf20Sopenharmony_ci		priv->regs+TSI721_IBDMAC_DQBL(ch));
22728c2ecf20Sopenharmony_ci	iowrite32(TSI721_DMAC_DSSZ_SIZE(entries),
22738c2ecf20Sopenharmony_ci		priv->regs + TSI721_IBDMAC_DQSZ(ch));
22748c2ecf20Sopenharmony_ci
22758c2ecf20Sopenharmony_ci	/* Enable interrupts */
22768c2ecf20Sopenharmony_ci
22778c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI_MSI
22788c2ecf20Sopenharmony_ci	if (priv->flags & TSI721_USING_MSIX) {
22798c2ecf20Sopenharmony_ci		int idx = TSI721_VECT_IMB0_RCV + mbox;
22808c2ecf20Sopenharmony_ci
22818c2ecf20Sopenharmony_ci		/* Request interrupt service if we are in MSI-X mode */
22828c2ecf20Sopenharmony_ci		rc = request_irq(priv->msix[idx].vector, tsi721_imsg_msix, 0,
22838c2ecf20Sopenharmony_ci				 priv->msix[idx].irq_name, (void *)priv);
22848c2ecf20Sopenharmony_ci
22858c2ecf20Sopenharmony_ci		if (rc) {
22868c2ecf20Sopenharmony_ci			tsi_debug(IMSG, &priv->pdev->dev,
22878c2ecf20Sopenharmony_ci				"Unable to get MSI-X IRQ for IBOX%d-DONE",
22888c2ecf20Sopenharmony_ci				mbox);
22898c2ecf20Sopenharmony_ci			goto out_desc;
22908c2ecf20Sopenharmony_ci		}
22918c2ecf20Sopenharmony_ci
22928c2ecf20Sopenharmony_ci		idx = TSI721_VECT_IMB0_INT + mbox;
22938c2ecf20Sopenharmony_ci		rc = request_irq(priv->msix[idx].vector, tsi721_imsg_msix, 0,
22948c2ecf20Sopenharmony_ci				 priv->msix[idx].irq_name, (void *)priv);
22958c2ecf20Sopenharmony_ci
22968c2ecf20Sopenharmony_ci		if (rc)	{
22978c2ecf20Sopenharmony_ci			tsi_debug(IMSG, &priv->pdev->dev,
22988c2ecf20Sopenharmony_ci				"Unable to get MSI-X IRQ for IBOX%d-INT", mbox);
22998c2ecf20Sopenharmony_ci			free_irq(
23008c2ecf20Sopenharmony_ci				priv->msix[TSI721_VECT_IMB0_RCV + mbox].vector,
23018c2ecf20Sopenharmony_ci				(void *)priv);
23028c2ecf20Sopenharmony_ci			goto out_desc;
23038c2ecf20Sopenharmony_ci		}
23048c2ecf20Sopenharmony_ci	}
23058c2ecf20Sopenharmony_ci#endif /* CONFIG_PCI_MSI */
23068c2ecf20Sopenharmony_ci
23078c2ecf20Sopenharmony_ci	tsi721_imsg_interrupt_enable(priv, ch, TSI721_IBDMAC_INT_ALL);
23088c2ecf20Sopenharmony_ci
23098c2ecf20Sopenharmony_ci	/* Initialize Inbound Message Engine */
23108c2ecf20Sopenharmony_ci	iowrite32(TSI721_IBDMAC_CTL_INIT, priv->regs + TSI721_IBDMAC_CTL(ch));
23118c2ecf20Sopenharmony_ci	ioread32(priv->regs + TSI721_IBDMAC_CTL(ch));
23128c2ecf20Sopenharmony_ci	udelay(10);
23138c2ecf20Sopenharmony_ci	priv->imsg_ring[mbox].fq_wrptr = entries - 1;
23148c2ecf20Sopenharmony_ci	iowrite32(entries - 1, priv->regs + TSI721_IBDMAC_FQWP(ch));
23158c2ecf20Sopenharmony_ci
23168c2ecf20Sopenharmony_ci	priv->imsg_init[mbox] = 1;
23178c2ecf20Sopenharmony_ci	return 0;
23188c2ecf20Sopenharmony_ci
23198c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI_MSI
23208c2ecf20Sopenharmony_ciout_desc:
23218c2ecf20Sopenharmony_ci	dma_free_coherent(&priv->pdev->dev,
23228c2ecf20Sopenharmony_ci		priv->imsg_ring[mbox].size * sizeof(struct tsi721_imsg_desc),
23238c2ecf20Sopenharmony_ci		priv->imsg_ring[mbox].imd_base,
23248c2ecf20Sopenharmony_ci		priv->imsg_ring[mbox].imd_phys);
23258c2ecf20Sopenharmony_ci
23268c2ecf20Sopenharmony_ci	priv->imsg_ring[mbox].imd_base = NULL;
23278c2ecf20Sopenharmony_ci#endif /* CONFIG_PCI_MSI */
23288c2ecf20Sopenharmony_ci
23298c2ecf20Sopenharmony_ciout_dma:
23308c2ecf20Sopenharmony_ci	dma_free_coherent(&priv->pdev->dev,
23318c2ecf20Sopenharmony_ci		priv->imsg_ring[mbox].size * 8,
23328c2ecf20Sopenharmony_ci		priv->imsg_ring[mbox].imfq_base,
23338c2ecf20Sopenharmony_ci		priv->imsg_ring[mbox].imfq_phys);
23348c2ecf20Sopenharmony_ci
23358c2ecf20Sopenharmony_ci	priv->imsg_ring[mbox].imfq_base = NULL;
23368c2ecf20Sopenharmony_ci
23378c2ecf20Sopenharmony_ciout_buf:
23388c2ecf20Sopenharmony_ci	dma_free_coherent(&priv->pdev->dev,
23398c2ecf20Sopenharmony_ci		priv->imsg_ring[mbox].size * TSI721_MSG_BUFFER_SIZE,
23408c2ecf20Sopenharmony_ci		priv->imsg_ring[mbox].buf_base,
23418c2ecf20Sopenharmony_ci		priv->imsg_ring[mbox].buf_phys);
23428c2ecf20Sopenharmony_ci
23438c2ecf20Sopenharmony_ci	priv->imsg_ring[mbox].buf_base = NULL;
23448c2ecf20Sopenharmony_ci
23458c2ecf20Sopenharmony_ciout:
23468c2ecf20Sopenharmony_ci	return rc;
23478c2ecf20Sopenharmony_ci}
23488c2ecf20Sopenharmony_ci
23498c2ecf20Sopenharmony_ci/**
23508c2ecf20Sopenharmony_ci * tsi721_close_inb_mbox - Shut down Tsi721 inbound mailbox
23518c2ecf20Sopenharmony_ci * @mport: Master port implementing the Inbound Messaging Engine
23528c2ecf20Sopenharmony_ci * @mbox: Mailbox to close
23538c2ecf20Sopenharmony_ci */
23548c2ecf20Sopenharmony_cistatic void tsi721_close_inb_mbox(struct rio_mport *mport, int mbox)
23558c2ecf20Sopenharmony_ci{
23568c2ecf20Sopenharmony_ci	struct tsi721_device *priv = mport->priv;
23578c2ecf20Sopenharmony_ci	u32 rx_slot;
23588c2ecf20Sopenharmony_ci	int ch = mbox + 4;
23598c2ecf20Sopenharmony_ci
23608c2ecf20Sopenharmony_ci	if (!priv->imsg_init[mbox]) /* mbox isn't initialized yet */
23618c2ecf20Sopenharmony_ci		return;
23628c2ecf20Sopenharmony_ci	priv->imsg_init[mbox] = 0;
23638c2ecf20Sopenharmony_ci
23648c2ecf20Sopenharmony_ci	/* Disable Inbound Messaging Engine */
23658c2ecf20Sopenharmony_ci
23668c2ecf20Sopenharmony_ci	/* Disable Interrupts */
23678c2ecf20Sopenharmony_ci	tsi721_imsg_interrupt_disable(priv, ch, TSI721_OBDMAC_INT_MASK);
23688c2ecf20Sopenharmony_ci
23698c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI_MSI
23708c2ecf20Sopenharmony_ci	if (priv->flags & TSI721_USING_MSIX) {
23718c2ecf20Sopenharmony_ci		free_irq(priv->msix[TSI721_VECT_IMB0_RCV + mbox].vector,
23728c2ecf20Sopenharmony_ci				(void *)priv);
23738c2ecf20Sopenharmony_ci		free_irq(priv->msix[TSI721_VECT_IMB0_INT + mbox].vector,
23748c2ecf20Sopenharmony_ci				(void *)priv);
23758c2ecf20Sopenharmony_ci	}
23768c2ecf20Sopenharmony_ci#endif /* CONFIG_PCI_MSI */
23778c2ecf20Sopenharmony_ci
23788c2ecf20Sopenharmony_ci	/* Clear Inbound Buffer Queue */
23798c2ecf20Sopenharmony_ci	for (rx_slot = 0; rx_slot < priv->imsg_ring[mbox].size; rx_slot++)
23808c2ecf20Sopenharmony_ci		priv->imsg_ring[mbox].imq_base[rx_slot] = NULL;
23818c2ecf20Sopenharmony_ci
23828c2ecf20Sopenharmony_ci	/* Free memory allocated for message buffers */
23838c2ecf20Sopenharmony_ci	dma_free_coherent(&priv->pdev->dev,
23848c2ecf20Sopenharmony_ci		priv->imsg_ring[mbox].size * TSI721_MSG_BUFFER_SIZE,
23858c2ecf20Sopenharmony_ci		priv->imsg_ring[mbox].buf_base,
23868c2ecf20Sopenharmony_ci		priv->imsg_ring[mbox].buf_phys);
23878c2ecf20Sopenharmony_ci
23888c2ecf20Sopenharmony_ci	priv->imsg_ring[mbox].buf_base = NULL;
23898c2ecf20Sopenharmony_ci
23908c2ecf20Sopenharmony_ci	/* Free memory allocated for free pointr list */
23918c2ecf20Sopenharmony_ci	dma_free_coherent(&priv->pdev->dev,
23928c2ecf20Sopenharmony_ci		priv->imsg_ring[mbox].size * 8,
23938c2ecf20Sopenharmony_ci		priv->imsg_ring[mbox].imfq_base,
23948c2ecf20Sopenharmony_ci		priv->imsg_ring[mbox].imfq_phys);
23958c2ecf20Sopenharmony_ci
23968c2ecf20Sopenharmony_ci	priv->imsg_ring[mbox].imfq_base = NULL;
23978c2ecf20Sopenharmony_ci
23988c2ecf20Sopenharmony_ci	/* Free memory allocated for RX descriptors */
23998c2ecf20Sopenharmony_ci	dma_free_coherent(&priv->pdev->dev,
24008c2ecf20Sopenharmony_ci		priv->imsg_ring[mbox].size * sizeof(struct tsi721_imsg_desc),
24018c2ecf20Sopenharmony_ci		priv->imsg_ring[mbox].imd_base,
24028c2ecf20Sopenharmony_ci		priv->imsg_ring[mbox].imd_phys);
24038c2ecf20Sopenharmony_ci
24048c2ecf20Sopenharmony_ci	priv->imsg_ring[mbox].imd_base = NULL;
24058c2ecf20Sopenharmony_ci}
24068c2ecf20Sopenharmony_ci
24078c2ecf20Sopenharmony_ci/**
24088c2ecf20Sopenharmony_ci * tsi721_add_inb_buffer - Add buffer to the Tsi721 inbound message queue
24098c2ecf20Sopenharmony_ci * @mport: Master port implementing the Inbound Messaging Engine
24108c2ecf20Sopenharmony_ci * @mbox: Inbound mailbox number
24118c2ecf20Sopenharmony_ci * @buf: Buffer to add to inbound queue
24128c2ecf20Sopenharmony_ci */
24138c2ecf20Sopenharmony_cistatic int tsi721_add_inb_buffer(struct rio_mport *mport, int mbox, void *buf)
24148c2ecf20Sopenharmony_ci{
24158c2ecf20Sopenharmony_ci	struct tsi721_device *priv = mport->priv;
24168c2ecf20Sopenharmony_ci	u32 rx_slot;
24178c2ecf20Sopenharmony_ci	int rc = 0;
24188c2ecf20Sopenharmony_ci
24198c2ecf20Sopenharmony_ci	rx_slot = priv->imsg_ring[mbox].rx_slot;
24208c2ecf20Sopenharmony_ci	if (priv->imsg_ring[mbox].imq_base[rx_slot]) {
24218c2ecf20Sopenharmony_ci		tsi_err(&priv->pdev->dev,
24228c2ecf20Sopenharmony_ci			"Error adding inbound buffer %d, buffer exists",
24238c2ecf20Sopenharmony_ci			rx_slot);
24248c2ecf20Sopenharmony_ci		rc = -EINVAL;
24258c2ecf20Sopenharmony_ci		goto out;
24268c2ecf20Sopenharmony_ci	}
24278c2ecf20Sopenharmony_ci
24288c2ecf20Sopenharmony_ci	priv->imsg_ring[mbox].imq_base[rx_slot] = buf;
24298c2ecf20Sopenharmony_ci
24308c2ecf20Sopenharmony_ci	if (++priv->imsg_ring[mbox].rx_slot == priv->imsg_ring[mbox].size)
24318c2ecf20Sopenharmony_ci		priv->imsg_ring[mbox].rx_slot = 0;
24328c2ecf20Sopenharmony_ci
24338c2ecf20Sopenharmony_ciout:
24348c2ecf20Sopenharmony_ci	return rc;
24358c2ecf20Sopenharmony_ci}
24368c2ecf20Sopenharmony_ci
24378c2ecf20Sopenharmony_ci/**
24388c2ecf20Sopenharmony_ci * tsi721_get_inb_message - Fetch inbound message from the Tsi721 MSG Queue
24398c2ecf20Sopenharmony_ci * @mport: Master port implementing the Inbound Messaging Engine
24408c2ecf20Sopenharmony_ci * @mbox: Inbound mailbox number
24418c2ecf20Sopenharmony_ci *
24428c2ecf20Sopenharmony_ci * Returns pointer to the message on success or NULL on failure.
24438c2ecf20Sopenharmony_ci */
24448c2ecf20Sopenharmony_cistatic void *tsi721_get_inb_message(struct rio_mport *mport, int mbox)
24458c2ecf20Sopenharmony_ci{
24468c2ecf20Sopenharmony_ci	struct tsi721_device *priv = mport->priv;
24478c2ecf20Sopenharmony_ci	struct tsi721_imsg_desc *desc;
24488c2ecf20Sopenharmony_ci	u32 rx_slot;
24498c2ecf20Sopenharmony_ci	void *rx_virt = NULL;
24508c2ecf20Sopenharmony_ci	u64 rx_phys;
24518c2ecf20Sopenharmony_ci	void *buf = NULL;
24528c2ecf20Sopenharmony_ci	u64 *free_ptr;
24538c2ecf20Sopenharmony_ci	int ch = mbox + 4;
24548c2ecf20Sopenharmony_ci	int msg_size;
24558c2ecf20Sopenharmony_ci
24568c2ecf20Sopenharmony_ci	if (!priv->imsg_init[mbox])
24578c2ecf20Sopenharmony_ci		return NULL;
24588c2ecf20Sopenharmony_ci
24598c2ecf20Sopenharmony_ci	desc = priv->imsg_ring[mbox].imd_base;
24608c2ecf20Sopenharmony_ci	desc += priv->imsg_ring[mbox].desc_rdptr;
24618c2ecf20Sopenharmony_ci
24628c2ecf20Sopenharmony_ci	if (!(le32_to_cpu(desc->msg_info) & TSI721_IMD_HO))
24638c2ecf20Sopenharmony_ci		goto out;
24648c2ecf20Sopenharmony_ci
24658c2ecf20Sopenharmony_ci	rx_slot = priv->imsg_ring[mbox].rx_slot;
24668c2ecf20Sopenharmony_ci	while (priv->imsg_ring[mbox].imq_base[rx_slot] == NULL) {
24678c2ecf20Sopenharmony_ci		if (++rx_slot == priv->imsg_ring[mbox].size)
24688c2ecf20Sopenharmony_ci			rx_slot = 0;
24698c2ecf20Sopenharmony_ci	}
24708c2ecf20Sopenharmony_ci
24718c2ecf20Sopenharmony_ci	rx_phys = ((u64)le32_to_cpu(desc->bufptr_hi) << 32) |
24728c2ecf20Sopenharmony_ci			le32_to_cpu(desc->bufptr_lo);
24738c2ecf20Sopenharmony_ci
24748c2ecf20Sopenharmony_ci	rx_virt = priv->imsg_ring[mbox].buf_base +
24758c2ecf20Sopenharmony_ci		  (rx_phys - (u64)priv->imsg_ring[mbox].buf_phys);
24768c2ecf20Sopenharmony_ci
24778c2ecf20Sopenharmony_ci	buf = priv->imsg_ring[mbox].imq_base[rx_slot];
24788c2ecf20Sopenharmony_ci	msg_size = le32_to_cpu(desc->msg_info) & TSI721_IMD_BCOUNT;
24798c2ecf20Sopenharmony_ci	if (msg_size == 0)
24808c2ecf20Sopenharmony_ci		msg_size = RIO_MAX_MSG_SIZE;
24818c2ecf20Sopenharmony_ci
24828c2ecf20Sopenharmony_ci	memcpy(buf, rx_virt, msg_size);
24838c2ecf20Sopenharmony_ci	priv->imsg_ring[mbox].imq_base[rx_slot] = NULL;
24848c2ecf20Sopenharmony_ci
24858c2ecf20Sopenharmony_ci	desc->msg_info &= cpu_to_le32(~TSI721_IMD_HO);
24868c2ecf20Sopenharmony_ci	if (++priv->imsg_ring[mbox].desc_rdptr == priv->imsg_ring[mbox].size)
24878c2ecf20Sopenharmony_ci		priv->imsg_ring[mbox].desc_rdptr = 0;
24888c2ecf20Sopenharmony_ci
24898c2ecf20Sopenharmony_ci	iowrite32(priv->imsg_ring[mbox].desc_rdptr,
24908c2ecf20Sopenharmony_ci		priv->regs + TSI721_IBDMAC_DQRP(ch));
24918c2ecf20Sopenharmony_ci
24928c2ecf20Sopenharmony_ci	/* Return free buffer into the pointer list */
24938c2ecf20Sopenharmony_ci	free_ptr = priv->imsg_ring[mbox].imfq_base;
24948c2ecf20Sopenharmony_ci	free_ptr[priv->imsg_ring[mbox].fq_wrptr] = cpu_to_le64(rx_phys);
24958c2ecf20Sopenharmony_ci
24968c2ecf20Sopenharmony_ci	if (++priv->imsg_ring[mbox].fq_wrptr == priv->imsg_ring[mbox].size)
24978c2ecf20Sopenharmony_ci		priv->imsg_ring[mbox].fq_wrptr = 0;
24988c2ecf20Sopenharmony_ci
24998c2ecf20Sopenharmony_ci	iowrite32(priv->imsg_ring[mbox].fq_wrptr,
25008c2ecf20Sopenharmony_ci		priv->regs + TSI721_IBDMAC_FQWP(ch));
25018c2ecf20Sopenharmony_ciout:
25028c2ecf20Sopenharmony_ci	return buf;
25038c2ecf20Sopenharmony_ci}
25048c2ecf20Sopenharmony_ci
25058c2ecf20Sopenharmony_ci/**
25068c2ecf20Sopenharmony_ci * tsi721_messages_init - Initialization of Messaging Engine
25078c2ecf20Sopenharmony_ci * @priv: pointer to tsi721 private data
25088c2ecf20Sopenharmony_ci *
25098c2ecf20Sopenharmony_ci * Configures Tsi721 messaging engine.
25108c2ecf20Sopenharmony_ci */
25118c2ecf20Sopenharmony_cistatic int tsi721_messages_init(struct tsi721_device *priv)
25128c2ecf20Sopenharmony_ci{
25138c2ecf20Sopenharmony_ci	int	ch;
25148c2ecf20Sopenharmony_ci
25158c2ecf20Sopenharmony_ci	iowrite32(0, priv->regs + TSI721_SMSG_ECC_LOG);
25168c2ecf20Sopenharmony_ci	iowrite32(0, priv->regs + TSI721_RETRY_GEN_CNT);
25178c2ecf20Sopenharmony_ci	iowrite32(0, priv->regs + TSI721_RETRY_RX_CNT);
25188c2ecf20Sopenharmony_ci
25198c2ecf20Sopenharmony_ci	/* Set SRIO Message Request/Response Timeout */
25208c2ecf20Sopenharmony_ci	iowrite32(TSI721_RQRPTO_VAL, priv->regs + TSI721_RQRPTO);
25218c2ecf20Sopenharmony_ci
25228c2ecf20Sopenharmony_ci	/* Initialize Inbound Messaging Engine Registers */
25238c2ecf20Sopenharmony_ci	for (ch = 0; ch < TSI721_IMSG_CHNUM; ch++) {
25248c2ecf20Sopenharmony_ci		/* Clear interrupt bits */
25258c2ecf20Sopenharmony_ci		iowrite32(TSI721_IBDMAC_INT_MASK,
25268c2ecf20Sopenharmony_ci			priv->regs + TSI721_IBDMAC_INT(ch));
25278c2ecf20Sopenharmony_ci		/* Clear Status */
25288c2ecf20Sopenharmony_ci		iowrite32(0, priv->regs + TSI721_IBDMAC_STS(ch));
25298c2ecf20Sopenharmony_ci
25308c2ecf20Sopenharmony_ci		iowrite32(TSI721_SMSG_ECC_COR_LOG_MASK,
25318c2ecf20Sopenharmony_ci				priv->regs + TSI721_SMSG_ECC_COR_LOG(ch));
25328c2ecf20Sopenharmony_ci		iowrite32(TSI721_SMSG_ECC_NCOR_MASK,
25338c2ecf20Sopenharmony_ci				priv->regs + TSI721_SMSG_ECC_NCOR(ch));
25348c2ecf20Sopenharmony_ci	}
25358c2ecf20Sopenharmony_ci
25368c2ecf20Sopenharmony_ci	return 0;
25378c2ecf20Sopenharmony_ci}
25388c2ecf20Sopenharmony_ci
25398c2ecf20Sopenharmony_ci/**
25408c2ecf20Sopenharmony_ci * tsi721_query_mport - Fetch inbound message from the Tsi721 MSG Queue
25418c2ecf20Sopenharmony_ci * @mport: Master port implementing the Inbound Messaging Engine
25428c2ecf20Sopenharmony_ci * @mbox: Inbound mailbox number
25438c2ecf20Sopenharmony_ci *
25448c2ecf20Sopenharmony_ci * Returns pointer to the message on success or NULL on failure.
25458c2ecf20Sopenharmony_ci */
25468c2ecf20Sopenharmony_cistatic int tsi721_query_mport(struct rio_mport *mport,
25478c2ecf20Sopenharmony_ci			      struct rio_mport_attr *attr)
25488c2ecf20Sopenharmony_ci{
25498c2ecf20Sopenharmony_ci	struct tsi721_device *priv = mport->priv;
25508c2ecf20Sopenharmony_ci	u32 rval;
25518c2ecf20Sopenharmony_ci
25528c2ecf20Sopenharmony_ci	rval = ioread32(priv->regs + 0x100 + RIO_PORT_N_ERR_STS_CSR(0, 0));
25538c2ecf20Sopenharmony_ci	if (rval & RIO_PORT_N_ERR_STS_PORT_OK) {
25548c2ecf20Sopenharmony_ci		rval = ioread32(priv->regs + 0x100 + RIO_PORT_N_CTL2_CSR(0, 0));
25558c2ecf20Sopenharmony_ci		attr->link_speed = (rval & RIO_PORT_N_CTL2_SEL_BAUD) >> 28;
25568c2ecf20Sopenharmony_ci		rval = ioread32(priv->regs + 0x100 + RIO_PORT_N_CTL_CSR(0, 0));
25578c2ecf20Sopenharmony_ci		attr->link_width = (rval & RIO_PORT_N_CTL_IPW) >> 27;
25588c2ecf20Sopenharmony_ci	} else
25598c2ecf20Sopenharmony_ci		attr->link_speed = RIO_LINK_DOWN;
25608c2ecf20Sopenharmony_ci
25618c2ecf20Sopenharmony_ci#ifdef CONFIG_RAPIDIO_DMA_ENGINE
25628c2ecf20Sopenharmony_ci	attr->flags = RIO_MPORT_DMA | RIO_MPORT_DMA_SG;
25638c2ecf20Sopenharmony_ci	attr->dma_max_sge = 0;
25648c2ecf20Sopenharmony_ci	attr->dma_max_size = TSI721_BDMA_MAX_BCOUNT;
25658c2ecf20Sopenharmony_ci	attr->dma_align = 0;
25668c2ecf20Sopenharmony_ci#else
25678c2ecf20Sopenharmony_ci	attr->flags = 0;
25688c2ecf20Sopenharmony_ci#endif
25698c2ecf20Sopenharmony_ci	return 0;
25708c2ecf20Sopenharmony_ci}
25718c2ecf20Sopenharmony_ci
25728c2ecf20Sopenharmony_ci/**
25738c2ecf20Sopenharmony_ci * tsi721_disable_ints - disables all device interrupts
25748c2ecf20Sopenharmony_ci * @priv: pointer to tsi721 private data
25758c2ecf20Sopenharmony_ci */
25768c2ecf20Sopenharmony_cistatic void tsi721_disable_ints(struct tsi721_device *priv)
25778c2ecf20Sopenharmony_ci{
25788c2ecf20Sopenharmony_ci	int ch;
25798c2ecf20Sopenharmony_ci
25808c2ecf20Sopenharmony_ci	/* Disable all device level interrupts */
25818c2ecf20Sopenharmony_ci	iowrite32(0, priv->regs + TSI721_DEV_INTE);
25828c2ecf20Sopenharmony_ci
25838c2ecf20Sopenharmony_ci	/* Disable all Device Channel interrupts */
25848c2ecf20Sopenharmony_ci	iowrite32(0, priv->regs + TSI721_DEV_CHAN_INTE);
25858c2ecf20Sopenharmony_ci
25868c2ecf20Sopenharmony_ci	/* Disable all Inbound Msg Channel interrupts */
25878c2ecf20Sopenharmony_ci	for (ch = 0; ch < TSI721_IMSG_CHNUM; ch++)
25888c2ecf20Sopenharmony_ci		iowrite32(0, priv->regs + TSI721_IBDMAC_INTE(ch));
25898c2ecf20Sopenharmony_ci
25908c2ecf20Sopenharmony_ci	/* Disable all Outbound Msg Channel interrupts */
25918c2ecf20Sopenharmony_ci	for (ch = 0; ch < TSI721_OMSG_CHNUM; ch++)
25928c2ecf20Sopenharmony_ci		iowrite32(0, priv->regs + TSI721_OBDMAC_INTE(ch));
25938c2ecf20Sopenharmony_ci
25948c2ecf20Sopenharmony_ci	/* Disable all general messaging interrupts */
25958c2ecf20Sopenharmony_ci	iowrite32(0, priv->regs + TSI721_SMSG_INTE);
25968c2ecf20Sopenharmony_ci
25978c2ecf20Sopenharmony_ci	/* Disable all BDMA Channel interrupts */
25988c2ecf20Sopenharmony_ci	for (ch = 0; ch < TSI721_DMA_MAXCH; ch++)
25998c2ecf20Sopenharmony_ci		iowrite32(0,
26008c2ecf20Sopenharmony_ci			priv->regs + TSI721_DMAC_BASE(ch) + TSI721_DMAC_INTE);
26018c2ecf20Sopenharmony_ci
26028c2ecf20Sopenharmony_ci	/* Disable all general BDMA interrupts */
26038c2ecf20Sopenharmony_ci	iowrite32(0, priv->regs + TSI721_BDMA_INTE);
26048c2ecf20Sopenharmony_ci
26058c2ecf20Sopenharmony_ci	/* Disable all SRIO Channel interrupts */
26068c2ecf20Sopenharmony_ci	for (ch = 0; ch < TSI721_SRIO_MAXCH; ch++)
26078c2ecf20Sopenharmony_ci		iowrite32(0, priv->regs + TSI721_SR_CHINTE(ch));
26088c2ecf20Sopenharmony_ci
26098c2ecf20Sopenharmony_ci	/* Disable all general SR2PC interrupts */
26108c2ecf20Sopenharmony_ci	iowrite32(0, priv->regs + TSI721_SR2PC_GEN_INTE);
26118c2ecf20Sopenharmony_ci
26128c2ecf20Sopenharmony_ci	/* Disable all PC2SR interrupts */
26138c2ecf20Sopenharmony_ci	iowrite32(0, priv->regs + TSI721_PC2SR_INTE);
26148c2ecf20Sopenharmony_ci
26158c2ecf20Sopenharmony_ci	/* Disable all I2C interrupts */
26168c2ecf20Sopenharmony_ci	iowrite32(0, priv->regs + TSI721_I2C_INT_ENABLE);
26178c2ecf20Sopenharmony_ci
26188c2ecf20Sopenharmony_ci	/* Disable SRIO MAC interrupts */
26198c2ecf20Sopenharmony_ci	iowrite32(0, priv->regs + TSI721_RIO_EM_INT_ENABLE);
26208c2ecf20Sopenharmony_ci	iowrite32(0, priv->regs + TSI721_RIO_EM_DEV_INT_EN);
26218c2ecf20Sopenharmony_ci}
26228c2ecf20Sopenharmony_ci
26238c2ecf20Sopenharmony_cistatic struct rio_ops tsi721_rio_ops = {
26248c2ecf20Sopenharmony_ci	.lcread			= tsi721_lcread,
26258c2ecf20Sopenharmony_ci	.lcwrite		= tsi721_lcwrite,
26268c2ecf20Sopenharmony_ci	.cread			= tsi721_cread_dma,
26278c2ecf20Sopenharmony_ci	.cwrite			= tsi721_cwrite_dma,
26288c2ecf20Sopenharmony_ci	.dsend			= tsi721_dsend,
26298c2ecf20Sopenharmony_ci	.open_inb_mbox		= tsi721_open_inb_mbox,
26308c2ecf20Sopenharmony_ci	.close_inb_mbox		= tsi721_close_inb_mbox,
26318c2ecf20Sopenharmony_ci	.open_outb_mbox		= tsi721_open_outb_mbox,
26328c2ecf20Sopenharmony_ci	.close_outb_mbox	= tsi721_close_outb_mbox,
26338c2ecf20Sopenharmony_ci	.add_outb_message	= tsi721_add_outb_message,
26348c2ecf20Sopenharmony_ci	.add_inb_buffer		= tsi721_add_inb_buffer,
26358c2ecf20Sopenharmony_ci	.get_inb_message	= tsi721_get_inb_message,
26368c2ecf20Sopenharmony_ci	.map_inb		= tsi721_rio_map_inb_mem,
26378c2ecf20Sopenharmony_ci	.unmap_inb		= tsi721_rio_unmap_inb_mem,
26388c2ecf20Sopenharmony_ci	.pwenable		= tsi721_pw_enable,
26398c2ecf20Sopenharmony_ci	.query_mport		= tsi721_query_mport,
26408c2ecf20Sopenharmony_ci	.map_outb		= tsi721_map_outb_win,
26418c2ecf20Sopenharmony_ci	.unmap_outb		= tsi721_unmap_outb_win,
26428c2ecf20Sopenharmony_ci};
26438c2ecf20Sopenharmony_ci
26448c2ecf20Sopenharmony_cistatic void tsi721_mport_release(struct device *dev)
26458c2ecf20Sopenharmony_ci{
26468c2ecf20Sopenharmony_ci	struct rio_mport *mport = to_rio_mport(dev);
26478c2ecf20Sopenharmony_ci
26488c2ecf20Sopenharmony_ci	tsi_debug(EXIT, dev, "%s id=%d", mport->name, mport->id);
26498c2ecf20Sopenharmony_ci}
26508c2ecf20Sopenharmony_ci
26518c2ecf20Sopenharmony_ci/**
26528c2ecf20Sopenharmony_ci * tsi721_setup_mport - Setup Tsi721 as RapidIO subsystem master port
26538c2ecf20Sopenharmony_ci * @priv: pointer to tsi721 private data
26548c2ecf20Sopenharmony_ci *
26558c2ecf20Sopenharmony_ci * Configures Tsi721 as RapidIO master port.
26568c2ecf20Sopenharmony_ci */
26578c2ecf20Sopenharmony_cistatic int tsi721_setup_mport(struct tsi721_device *priv)
26588c2ecf20Sopenharmony_ci{
26598c2ecf20Sopenharmony_ci	struct pci_dev *pdev = priv->pdev;
26608c2ecf20Sopenharmony_ci	int err = 0;
26618c2ecf20Sopenharmony_ci	struct rio_mport *mport = &priv->mport;
26628c2ecf20Sopenharmony_ci
26638c2ecf20Sopenharmony_ci	err = rio_mport_initialize(mport);
26648c2ecf20Sopenharmony_ci	if (err)
26658c2ecf20Sopenharmony_ci		return err;
26668c2ecf20Sopenharmony_ci
26678c2ecf20Sopenharmony_ci	mport->ops = &tsi721_rio_ops;
26688c2ecf20Sopenharmony_ci	mport->index = 0;
26698c2ecf20Sopenharmony_ci	mport->sys_size = 0; /* small system */
26708c2ecf20Sopenharmony_ci	mport->priv = (void *)priv;
26718c2ecf20Sopenharmony_ci	mport->phys_efptr = 0x100;
26728c2ecf20Sopenharmony_ci	mport->phys_rmap = 1;
26738c2ecf20Sopenharmony_ci	mport->dev.parent = &pdev->dev;
26748c2ecf20Sopenharmony_ci	mport->dev.release = tsi721_mport_release;
26758c2ecf20Sopenharmony_ci
26768c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&mport->dbells);
26778c2ecf20Sopenharmony_ci
26788c2ecf20Sopenharmony_ci	rio_init_dbell_res(&mport->riores[RIO_DOORBELL_RESOURCE], 0, 0xffff);
26798c2ecf20Sopenharmony_ci	rio_init_mbox_res(&mport->riores[RIO_INB_MBOX_RESOURCE], 0, 3);
26808c2ecf20Sopenharmony_ci	rio_init_mbox_res(&mport->riores[RIO_OUTB_MBOX_RESOURCE], 0, 3);
26818c2ecf20Sopenharmony_ci	snprintf(mport->name, RIO_MAX_MPORT_NAME, "%s(%s)",
26828c2ecf20Sopenharmony_ci		 dev_driver_string(&pdev->dev), dev_name(&pdev->dev));
26838c2ecf20Sopenharmony_ci
26848c2ecf20Sopenharmony_ci	/* Hook up interrupt handler */
26858c2ecf20Sopenharmony_ci
26868c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI_MSI
26878c2ecf20Sopenharmony_ci	if (!tsi721_enable_msix(priv))
26888c2ecf20Sopenharmony_ci		priv->flags |= TSI721_USING_MSIX;
26898c2ecf20Sopenharmony_ci	else if (!pci_enable_msi(pdev))
26908c2ecf20Sopenharmony_ci		priv->flags |= TSI721_USING_MSI;
26918c2ecf20Sopenharmony_ci	else
26928c2ecf20Sopenharmony_ci		tsi_debug(MPORT, &pdev->dev,
26938c2ecf20Sopenharmony_ci			 "MSI/MSI-X is not available. Using legacy INTx.");
26948c2ecf20Sopenharmony_ci#endif /* CONFIG_PCI_MSI */
26958c2ecf20Sopenharmony_ci
26968c2ecf20Sopenharmony_ci	err = tsi721_request_irq(priv);
26978c2ecf20Sopenharmony_ci
26988c2ecf20Sopenharmony_ci	if (err) {
26998c2ecf20Sopenharmony_ci		tsi_err(&pdev->dev, "Unable to get PCI IRQ %02X (err=0x%x)",
27008c2ecf20Sopenharmony_ci			pdev->irq, err);
27018c2ecf20Sopenharmony_ci		return err;
27028c2ecf20Sopenharmony_ci	}
27038c2ecf20Sopenharmony_ci
27048c2ecf20Sopenharmony_ci#ifdef CONFIG_RAPIDIO_DMA_ENGINE
27058c2ecf20Sopenharmony_ci	err = tsi721_register_dma(priv);
27068c2ecf20Sopenharmony_ci	if (err)
27078c2ecf20Sopenharmony_ci		goto err_exit;
27088c2ecf20Sopenharmony_ci#endif
27098c2ecf20Sopenharmony_ci	/* Enable SRIO link */
27108c2ecf20Sopenharmony_ci	iowrite32(ioread32(priv->regs + TSI721_DEVCTL) |
27118c2ecf20Sopenharmony_ci		  TSI721_DEVCTL_SRBOOT_CMPL,
27128c2ecf20Sopenharmony_ci		  priv->regs + TSI721_DEVCTL);
27138c2ecf20Sopenharmony_ci
27148c2ecf20Sopenharmony_ci	if (mport->host_deviceid >= 0)
27158c2ecf20Sopenharmony_ci		iowrite32(RIO_PORT_GEN_HOST | RIO_PORT_GEN_MASTER |
27168c2ecf20Sopenharmony_ci			  RIO_PORT_GEN_DISCOVERED,
27178c2ecf20Sopenharmony_ci			  priv->regs + (0x100 + RIO_PORT_GEN_CTL_CSR));
27188c2ecf20Sopenharmony_ci	else
27198c2ecf20Sopenharmony_ci		iowrite32(0, priv->regs + (0x100 + RIO_PORT_GEN_CTL_CSR));
27208c2ecf20Sopenharmony_ci
27218c2ecf20Sopenharmony_ci	err = rio_register_mport(mport);
27228c2ecf20Sopenharmony_ci	if (err) {
27238c2ecf20Sopenharmony_ci		tsi721_unregister_dma(priv);
27248c2ecf20Sopenharmony_ci		goto err_exit;
27258c2ecf20Sopenharmony_ci	}
27268c2ecf20Sopenharmony_ci
27278c2ecf20Sopenharmony_ci	return 0;
27288c2ecf20Sopenharmony_ci
27298c2ecf20Sopenharmony_cierr_exit:
27308c2ecf20Sopenharmony_ci	tsi721_free_irq(priv);
27318c2ecf20Sopenharmony_ci	return err;
27328c2ecf20Sopenharmony_ci}
27338c2ecf20Sopenharmony_ci
27348c2ecf20Sopenharmony_cistatic int tsi721_probe(struct pci_dev *pdev,
27358c2ecf20Sopenharmony_ci				  const struct pci_device_id *id)
27368c2ecf20Sopenharmony_ci{
27378c2ecf20Sopenharmony_ci	struct tsi721_device *priv;
27388c2ecf20Sopenharmony_ci	int err;
27398c2ecf20Sopenharmony_ci
27408c2ecf20Sopenharmony_ci	priv = kzalloc(sizeof(struct tsi721_device), GFP_KERNEL);
27418c2ecf20Sopenharmony_ci	if (!priv) {
27428c2ecf20Sopenharmony_ci		err = -ENOMEM;
27438c2ecf20Sopenharmony_ci		goto err_exit;
27448c2ecf20Sopenharmony_ci	}
27458c2ecf20Sopenharmony_ci
27468c2ecf20Sopenharmony_ci	err = pci_enable_device(pdev);
27478c2ecf20Sopenharmony_ci	if (err) {
27488c2ecf20Sopenharmony_ci		tsi_err(&pdev->dev, "Failed to enable PCI device");
27498c2ecf20Sopenharmony_ci		goto err_clean;
27508c2ecf20Sopenharmony_ci	}
27518c2ecf20Sopenharmony_ci
27528c2ecf20Sopenharmony_ci	priv->pdev = pdev;
27538c2ecf20Sopenharmony_ci
27548c2ecf20Sopenharmony_ci#ifdef DEBUG
27558c2ecf20Sopenharmony_ci	{
27568c2ecf20Sopenharmony_ci		int i;
27578c2ecf20Sopenharmony_ci
27588c2ecf20Sopenharmony_ci		for (i = 0; i < PCI_STD_NUM_BARS; i++) {
27598c2ecf20Sopenharmony_ci			tsi_debug(INIT, &pdev->dev, "res%d %pR",
27608c2ecf20Sopenharmony_ci				  i, &pdev->resource[i]);
27618c2ecf20Sopenharmony_ci		}
27628c2ecf20Sopenharmony_ci	}
27638c2ecf20Sopenharmony_ci#endif
27648c2ecf20Sopenharmony_ci	/*
27658c2ecf20Sopenharmony_ci	 * Verify BAR configuration
27668c2ecf20Sopenharmony_ci	 */
27678c2ecf20Sopenharmony_ci
27688c2ecf20Sopenharmony_ci	/* BAR_0 (registers) must be 512KB+ in 32-bit address space */
27698c2ecf20Sopenharmony_ci	if (!(pci_resource_flags(pdev, BAR_0) & IORESOURCE_MEM) ||
27708c2ecf20Sopenharmony_ci	    pci_resource_flags(pdev, BAR_0) & IORESOURCE_MEM_64 ||
27718c2ecf20Sopenharmony_ci	    pci_resource_len(pdev, BAR_0) < TSI721_REG_SPACE_SIZE) {
27728c2ecf20Sopenharmony_ci		tsi_err(&pdev->dev, "Missing or misconfigured CSR BAR0");
27738c2ecf20Sopenharmony_ci		err = -ENODEV;
27748c2ecf20Sopenharmony_ci		goto err_disable_pdev;
27758c2ecf20Sopenharmony_ci	}
27768c2ecf20Sopenharmony_ci
27778c2ecf20Sopenharmony_ci	/* BAR_1 (outbound doorbells) must be 16MB+ in 32-bit address space */
27788c2ecf20Sopenharmony_ci	if (!(pci_resource_flags(pdev, BAR_1) & IORESOURCE_MEM) ||
27798c2ecf20Sopenharmony_ci	    pci_resource_flags(pdev, BAR_1) & IORESOURCE_MEM_64 ||
27808c2ecf20Sopenharmony_ci	    pci_resource_len(pdev, BAR_1) < TSI721_DB_WIN_SIZE) {
27818c2ecf20Sopenharmony_ci		tsi_err(&pdev->dev, "Missing or misconfigured Doorbell BAR1");
27828c2ecf20Sopenharmony_ci		err = -ENODEV;
27838c2ecf20Sopenharmony_ci		goto err_disable_pdev;
27848c2ecf20Sopenharmony_ci	}
27858c2ecf20Sopenharmony_ci
27868c2ecf20Sopenharmony_ci	/*
27878c2ecf20Sopenharmony_ci	 * BAR_2 and BAR_4 (outbound translation) must be in 64-bit PCIe address
27888c2ecf20Sopenharmony_ci	 * space.
27898c2ecf20Sopenharmony_ci	 * NOTE: BAR_2 and BAR_4 are not used by this version of driver.
27908c2ecf20Sopenharmony_ci	 * It may be a good idea to keep them disabled using HW configuration
27918c2ecf20Sopenharmony_ci	 * to save PCI memory space.
27928c2ecf20Sopenharmony_ci	 */
27938c2ecf20Sopenharmony_ci
27948c2ecf20Sopenharmony_ci	priv->p2r_bar[0].size = priv->p2r_bar[1].size = 0;
27958c2ecf20Sopenharmony_ci
27968c2ecf20Sopenharmony_ci	if (pci_resource_flags(pdev, BAR_2) & IORESOURCE_MEM_64) {
27978c2ecf20Sopenharmony_ci		if (pci_resource_flags(pdev, BAR_2) & IORESOURCE_PREFETCH)
27988c2ecf20Sopenharmony_ci			tsi_debug(INIT, &pdev->dev,
27998c2ecf20Sopenharmony_ci				 "Prefetchable OBW BAR2 will not be used");
28008c2ecf20Sopenharmony_ci		else {
28018c2ecf20Sopenharmony_ci			priv->p2r_bar[0].base = pci_resource_start(pdev, BAR_2);
28028c2ecf20Sopenharmony_ci			priv->p2r_bar[0].size = pci_resource_len(pdev, BAR_2);
28038c2ecf20Sopenharmony_ci		}
28048c2ecf20Sopenharmony_ci	}
28058c2ecf20Sopenharmony_ci
28068c2ecf20Sopenharmony_ci	if (pci_resource_flags(pdev, BAR_4) & IORESOURCE_MEM_64) {
28078c2ecf20Sopenharmony_ci		if (pci_resource_flags(pdev, BAR_4) & IORESOURCE_PREFETCH)
28088c2ecf20Sopenharmony_ci			tsi_debug(INIT, &pdev->dev,
28098c2ecf20Sopenharmony_ci				 "Prefetchable OBW BAR4 will not be used");
28108c2ecf20Sopenharmony_ci		else {
28118c2ecf20Sopenharmony_ci			priv->p2r_bar[1].base = pci_resource_start(pdev, BAR_4);
28128c2ecf20Sopenharmony_ci			priv->p2r_bar[1].size = pci_resource_len(pdev, BAR_4);
28138c2ecf20Sopenharmony_ci		}
28148c2ecf20Sopenharmony_ci	}
28158c2ecf20Sopenharmony_ci
28168c2ecf20Sopenharmony_ci	err = pci_request_regions(pdev, DRV_NAME);
28178c2ecf20Sopenharmony_ci	if (err) {
28188c2ecf20Sopenharmony_ci		tsi_err(&pdev->dev, "Unable to obtain PCI resources");
28198c2ecf20Sopenharmony_ci		goto err_disable_pdev;
28208c2ecf20Sopenharmony_ci	}
28218c2ecf20Sopenharmony_ci
28228c2ecf20Sopenharmony_ci	pci_set_master(pdev);
28238c2ecf20Sopenharmony_ci
28248c2ecf20Sopenharmony_ci	priv->regs = pci_ioremap_bar(pdev, BAR_0);
28258c2ecf20Sopenharmony_ci	if (!priv->regs) {
28268c2ecf20Sopenharmony_ci		tsi_err(&pdev->dev, "Unable to map device registers space");
28278c2ecf20Sopenharmony_ci		err = -ENOMEM;
28288c2ecf20Sopenharmony_ci		goto err_free_res;
28298c2ecf20Sopenharmony_ci	}
28308c2ecf20Sopenharmony_ci
28318c2ecf20Sopenharmony_ci	priv->odb_base = pci_ioremap_bar(pdev, BAR_1);
28328c2ecf20Sopenharmony_ci	if (!priv->odb_base) {
28338c2ecf20Sopenharmony_ci		tsi_err(&pdev->dev, "Unable to map outbound doorbells space");
28348c2ecf20Sopenharmony_ci		err = -ENOMEM;
28358c2ecf20Sopenharmony_ci		goto err_unmap_bars;
28368c2ecf20Sopenharmony_ci	}
28378c2ecf20Sopenharmony_ci
28388c2ecf20Sopenharmony_ci	/* Configure DMA attributes. */
28398c2ecf20Sopenharmony_ci	if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) {
28408c2ecf20Sopenharmony_ci		err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
28418c2ecf20Sopenharmony_ci		if (err) {
28428c2ecf20Sopenharmony_ci			tsi_err(&pdev->dev, "Unable to set DMA mask");
28438c2ecf20Sopenharmony_ci			goto err_unmap_bars;
28448c2ecf20Sopenharmony_ci		}
28458c2ecf20Sopenharmony_ci
28468c2ecf20Sopenharmony_ci		if (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)))
28478c2ecf20Sopenharmony_ci			tsi_info(&pdev->dev, "Unable to set consistent DMA mask");
28488c2ecf20Sopenharmony_ci	} else {
28498c2ecf20Sopenharmony_ci		err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
28508c2ecf20Sopenharmony_ci		if (err)
28518c2ecf20Sopenharmony_ci			tsi_info(&pdev->dev, "Unable to set consistent DMA mask");
28528c2ecf20Sopenharmony_ci	}
28538c2ecf20Sopenharmony_ci
28548c2ecf20Sopenharmony_ci	BUG_ON(!pci_is_pcie(pdev));
28558c2ecf20Sopenharmony_ci
28568c2ecf20Sopenharmony_ci	/* Clear "no snoop" and "relaxed ordering" bits. */
28578c2ecf20Sopenharmony_ci	pcie_capability_clear_and_set_word(pdev, PCI_EXP_DEVCTL,
28588c2ecf20Sopenharmony_ci		PCI_EXP_DEVCTL_RELAX_EN | PCI_EXP_DEVCTL_NOSNOOP_EN, 0);
28598c2ecf20Sopenharmony_ci
28608c2ecf20Sopenharmony_ci	/* Override PCIe Maximum Read Request Size setting if requested */
28618c2ecf20Sopenharmony_ci	if (pcie_mrrs >= 0) {
28628c2ecf20Sopenharmony_ci		if (pcie_mrrs <= 5)
28638c2ecf20Sopenharmony_ci			pcie_capability_clear_and_set_word(pdev, PCI_EXP_DEVCTL,
28648c2ecf20Sopenharmony_ci					PCI_EXP_DEVCTL_READRQ, pcie_mrrs << 12);
28658c2ecf20Sopenharmony_ci		else
28668c2ecf20Sopenharmony_ci			tsi_info(&pdev->dev,
28678c2ecf20Sopenharmony_ci				 "Invalid MRRS override value %d", pcie_mrrs);
28688c2ecf20Sopenharmony_ci	}
28698c2ecf20Sopenharmony_ci
28708c2ecf20Sopenharmony_ci	/* Set PCIe completion timeout to 1-10ms */
28718c2ecf20Sopenharmony_ci	pcie_capability_clear_and_set_word(pdev, PCI_EXP_DEVCTL2,
28728c2ecf20Sopenharmony_ci					   PCI_EXP_DEVCTL2_COMP_TIMEOUT, 0x2);
28738c2ecf20Sopenharmony_ci
28748c2ecf20Sopenharmony_ci	/*
28758c2ecf20Sopenharmony_ci	 * FIXUP: correct offsets of MSI-X tables in the MSI-X Capability Block
28768c2ecf20Sopenharmony_ci	 */
28778c2ecf20Sopenharmony_ci	pci_write_config_dword(pdev, TSI721_PCIECFG_EPCTL, 0x01);
28788c2ecf20Sopenharmony_ci	pci_write_config_dword(pdev, TSI721_PCIECFG_MSIXTBL,
28798c2ecf20Sopenharmony_ci						TSI721_MSIXTBL_OFFSET);
28808c2ecf20Sopenharmony_ci	pci_write_config_dword(pdev, TSI721_PCIECFG_MSIXPBA,
28818c2ecf20Sopenharmony_ci						TSI721_MSIXPBA_OFFSET);
28828c2ecf20Sopenharmony_ci	pci_write_config_dword(pdev, TSI721_PCIECFG_EPCTL, 0);
28838c2ecf20Sopenharmony_ci	/* End of FIXUP */
28848c2ecf20Sopenharmony_ci
28858c2ecf20Sopenharmony_ci	tsi721_disable_ints(priv);
28868c2ecf20Sopenharmony_ci
28878c2ecf20Sopenharmony_ci	tsi721_init_pc2sr_mapping(priv);
28888c2ecf20Sopenharmony_ci	tsi721_init_sr2pc_mapping(priv);
28898c2ecf20Sopenharmony_ci
28908c2ecf20Sopenharmony_ci	if (tsi721_bdma_maint_init(priv)) {
28918c2ecf20Sopenharmony_ci		tsi_err(&pdev->dev, "BDMA initialization failed");
28928c2ecf20Sopenharmony_ci		err = -ENOMEM;
28938c2ecf20Sopenharmony_ci		goto err_unmap_bars;
28948c2ecf20Sopenharmony_ci	}
28958c2ecf20Sopenharmony_ci
28968c2ecf20Sopenharmony_ci	err = tsi721_doorbell_init(priv);
28978c2ecf20Sopenharmony_ci	if (err)
28988c2ecf20Sopenharmony_ci		goto err_free_bdma;
28998c2ecf20Sopenharmony_ci
29008c2ecf20Sopenharmony_ci	tsi721_port_write_init(priv);
29018c2ecf20Sopenharmony_ci
29028c2ecf20Sopenharmony_ci	err = tsi721_messages_init(priv);
29038c2ecf20Sopenharmony_ci	if (err)
29048c2ecf20Sopenharmony_ci		goto err_free_consistent;
29058c2ecf20Sopenharmony_ci
29068c2ecf20Sopenharmony_ci	err = tsi721_setup_mport(priv);
29078c2ecf20Sopenharmony_ci	if (err)
29088c2ecf20Sopenharmony_ci		goto err_free_consistent;
29098c2ecf20Sopenharmony_ci
29108c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, priv);
29118c2ecf20Sopenharmony_ci	tsi721_interrupts_init(priv);
29128c2ecf20Sopenharmony_ci
29138c2ecf20Sopenharmony_ci	return 0;
29148c2ecf20Sopenharmony_ci
29158c2ecf20Sopenharmony_cierr_free_consistent:
29168c2ecf20Sopenharmony_ci	tsi721_port_write_free(priv);
29178c2ecf20Sopenharmony_ci	tsi721_doorbell_free(priv);
29188c2ecf20Sopenharmony_cierr_free_bdma:
29198c2ecf20Sopenharmony_ci	tsi721_bdma_maint_free(priv);
29208c2ecf20Sopenharmony_cierr_unmap_bars:
29218c2ecf20Sopenharmony_ci	if (priv->regs)
29228c2ecf20Sopenharmony_ci		iounmap(priv->regs);
29238c2ecf20Sopenharmony_ci	if (priv->odb_base)
29248c2ecf20Sopenharmony_ci		iounmap(priv->odb_base);
29258c2ecf20Sopenharmony_cierr_free_res:
29268c2ecf20Sopenharmony_ci	pci_release_regions(pdev);
29278c2ecf20Sopenharmony_ci	pci_clear_master(pdev);
29288c2ecf20Sopenharmony_cierr_disable_pdev:
29298c2ecf20Sopenharmony_ci	pci_disable_device(pdev);
29308c2ecf20Sopenharmony_cierr_clean:
29318c2ecf20Sopenharmony_ci	kfree(priv);
29328c2ecf20Sopenharmony_cierr_exit:
29338c2ecf20Sopenharmony_ci	return err;
29348c2ecf20Sopenharmony_ci}
29358c2ecf20Sopenharmony_ci
29368c2ecf20Sopenharmony_cistatic void tsi721_remove(struct pci_dev *pdev)
29378c2ecf20Sopenharmony_ci{
29388c2ecf20Sopenharmony_ci	struct tsi721_device *priv = pci_get_drvdata(pdev);
29398c2ecf20Sopenharmony_ci
29408c2ecf20Sopenharmony_ci	tsi_debug(EXIT, &pdev->dev, "enter");
29418c2ecf20Sopenharmony_ci
29428c2ecf20Sopenharmony_ci	tsi721_disable_ints(priv);
29438c2ecf20Sopenharmony_ci	tsi721_free_irq(priv);
29448c2ecf20Sopenharmony_ci	flush_scheduled_work();
29458c2ecf20Sopenharmony_ci	rio_unregister_mport(&priv->mport);
29468c2ecf20Sopenharmony_ci
29478c2ecf20Sopenharmony_ci	tsi721_unregister_dma(priv);
29488c2ecf20Sopenharmony_ci	tsi721_bdma_maint_free(priv);
29498c2ecf20Sopenharmony_ci	tsi721_doorbell_free(priv);
29508c2ecf20Sopenharmony_ci	tsi721_port_write_free(priv);
29518c2ecf20Sopenharmony_ci	tsi721_close_sr2pc_mapping(priv);
29528c2ecf20Sopenharmony_ci
29538c2ecf20Sopenharmony_ci	if (priv->regs)
29548c2ecf20Sopenharmony_ci		iounmap(priv->regs);
29558c2ecf20Sopenharmony_ci	if (priv->odb_base)
29568c2ecf20Sopenharmony_ci		iounmap(priv->odb_base);
29578c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI_MSI
29588c2ecf20Sopenharmony_ci	if (priv->flags & TSI721_USING_MSIX)
29598c2ecf20Sopenharmony_ci		pci_disable_msix(priv->pdev);
29608c2ecf20Sopenharmony_ci	else if (priv->flags & TSI721_USING_MSI)
29618c2ecf20Sopenharmony_ci		pci_disable_msi(priv->pdev);
29628c2ecf20Sopenharmony_ci#endif
29638c2ecf20Sopenharmony_ci	pci_release_regions(pdev);
29648c2ecf20Sopenharmony_ci	pci_clear_master(pdev);
29658c2ecf20Sopenharmony_ci	pci_disable_device(pdev);
29668c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, NULL);
29678c2ecf20Sopenharmony_ci	kfree(priv);
29688c2ecf20Sopenharmony_ci	tsi_debug(EXIT, &pdev->dev, "exit");
29698c2ecf20Sopenharmony_ci}
29708c2ecf20Sopenharmony_ci
29718c2ecf20Sopenharmony_cistatic void tsi721_shutdown(struct pci_dev *pdev)
29728c2ecf20Sopenharmony_ci{
29738c2ecf20Sopenharmony_ci	struct tsi721_device *priv = pci_get_drvdata(pdev);
29748c2ecf20Sopenharmony_ci
29758c2ecf20Sopenharmony_ci	tsi_debug(EXIT, &pdev->dev, "enter");
29768c2ecf20Sopenharmony_ci
29778c2ecf20Sopenharmony_ci	tsi721_disable_ints(priv);
29788c2ecf20Sopenharmony_ci	tsi721_dma_stop_all(priv);
29798c2ecf20Sopenharmony_ci	pci_clear_master(pdev);
29808c2ecf20Sopenharmony_ci	pci_disable_device(pdev);
29818c2ecf20Sopenharmony_ci}
29828c2ecf20Sopenharmony_ci
29838c2ecf20Sopenharmony_cistatic const struct pci_device_id tsi721_pci_tbl[] = {
29848c2ecf20Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_IDT, PCI_DEVICE_ID_TSI721) },
29858c2ecf20Sopenharmony_ci	{ 0, }	/* terminate list */
29868c2ecf20Sopenharmony_ci};
29878c2ecf20Sopenharmony_ci
29888c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, tsi721_pci_tbl);
29898c2ecf20Sopenharmony_ci
29908c2ecf20Sopenharmony_cistatic struct pci_driver tsi721_driver = {
29918c2ecf20Sopenharmony_ci	.name		= "tsi721",
29928c2ecf20Sopenharmony_ci	.id_table	= tsi721_pci_tbl,
29938c2ecf20Sopenharmony_ci	.probe		= tsi721_probe,
29948c2ecf20Sopenharmony_ci	.remove		= tsi721_remove,
29958c2ecf20Sopenharmony_ci	.shutdown	= tsi721_shutdown,
29968c2ecf20Sopenharmony_ci};
29978c2ecf20Sopenharmony_ci
29988c2ecf20Sopenharmony_cimodule_pci_driver(tsi721_driver);
29998c2ecf20Sopenharmony_ci
30008c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("IDT Tsi721 PCIExpress-to-SRIO bridge driver");
30018c2ecf20Sopenharmony_ciMODULE_AUTHOR("Integrated Device Technology, Inc.");
30028c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
3003