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