162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Microsemi Switchtec(tm) PCIe Management Driver 462306a36Sopenharmony_ci * Copyright (c) 2019, Logan Gunthorpe <logang@deltatee.com> 562306a36Sopenharmony_ci * Copyright (c) 2019, GigaIO Networks, Inc 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include "dmaengine.h" 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/circ_buf.h> 1162306a36Sopenharmony_ci#include <linux/dmaengine.h> 1262306a36Sopenharmony_ci#include <linux/kref.h> 1362306a36Sopenharmony_ci#include <linux/list.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/pci.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ciMODULE_DESCRIPTION("PLX ExpressLane PEX PCI Switch DMA Engine"); 1862306a36Sopenharmony_ciMODULE_VERSION("0.1"); 1962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2062306a36Sopenharmony_ciMODULE_AUTHOR("Logan Gunthorpe"); 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define PLX_REG_DESC_RING_ADDR 0x214 2362306a36Sopenharmony_ci#define PLX_REG_DESC_RING_ADDR_HI 0x218 2462306a36Sopenharmony_ci#define PLX_REG_DESC_RING_NEXT_ADDR 0x21C 2562306a36Sopenharmony_ci#define PLX_REG_DESC_RING_COUNT 0x220 2662306a36Sopenharmony_ci#define PLX_REG_DESC_RING_LAST_ADDR 0x224 2762306a36Sopenharmony_ci#define PLX_REG_DESC_RING_LAST_SIZE 0x228 2862306a36Sopenharmony_ci#define PLX_REG_PREF_LIMIT 0x234 2962306a36Sopenharmony_ci#define PLX_REG_CTRL 0x238 3062306a36Sopenharmony_ci#define PLX_REG_CTRL2 0x23A 3162306a36Sopenharmony_ci#define PLX_REG_INTR_CTRL 0x23C 3262306a36Sopenharmony_ci#define PLX_REG_INTR_STATUS 0x23E 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define PLX_REG_PREF_LIMIT_PREF_FOUR 8 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define PLX_REG_CTRL_GRACEFUL_PAUSE BIT(0) 3762306a36Sopenharmony_ci#define PLX_REG_CTRL_ABORT BIT(1) 3862306a36Sopenharmony_ci#define PLX_REG_CTRL_WRITE_BACK_EN BIT(2) 3962306a36Sopenharmony_ci#define PLX_REG_CTRL_START BIT(3) 4062306a36Sopenharmony_ci#define PLX_REG_CTRL_RING_STOP_MODE BIT(4) 4162306a36Sopenharmony_ci#define PLX_REG_CTRL_DESC_MODE_BLOCK (0 << 5) 4262306a36Sopenharmony_ci#define PLX_REG_CTRL_DESC_MODE_ON_CHIP (1 << 5) 4362306a36Sopenharmony_ci#define PLX_REG_CTRL_DESC_MODE_OFF_CHIP (2 << 5) 4462306a36Sopenharmony_ci#define PLX_REG_CTRL_DESC_INVALID BIT(8) 4562306a36Sopenharmony_ci#define PLX_REG_CTRL_GRACEFUL_PAUSE_DONE BIT(9) 4662306a36Sopenharmony_ci#define PLX_REG_CTRL_ABORT_DONE BIT(10) 4762306a36Sopenharmony_ci#define PLX_REG_CTRL_IMM_PAUSE_DONE BIT(12) 4862306a36Sopenharmony_ci#define PLX_REG_CTRL_IN_PROGRESS BIT(30) 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#define PLX_REG_CTRL_RESET_VAL (PLX_REG_CTRL_DESC_INVALID | \ 5162306a36Sopenharmony_ci PLX_REG_CTRL_GRACEFUL_PAUSE_DONE | \ 5262306a36Sopenharmony_ci PLX_REG_CTRL_ABORT_DONE | \ 5362306a36Sopenharmony_ci PLX_REG_CTRL_IMM_PAUSE_DONE) 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci#define PLX_REG_CTRL_START_VAL (PLX_REG_CTRL_WRITE_BACK_EN | \ 5662306a36Sopenharmony_ci PLX_REG_CTRL_DESC_MODE_OFF_CHIP | \ 5762306a36Sopenharmony_ci PLX_REG_CTRL_START | \ 5862306a36Sopenharmony_ci PLX_REG_CTRL_RESET_VAL) 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci#define PLX_REG_CTRL2_MAX_TXFR_SIZE_64B 0 6162306a36Sopenharmony_ci#define PLX_REG_CTRL2_MAX_TXFR_SIZE_128B 1 6262306a36Sopenharmony_ci#define PLX_REG_CTRL2_MAX_TXFR_SIZE_256B 2 6362306a36Sopenharmony_ci#define PLX_REG_CTRL2_MAX_TXFR_SIZE_512B 3 6462306a36Sopenharmony_ci#define PLX_REG_CTRL2_MAX_TXFR_SIZE_1KB 4 6562306a36Sopenharmony_ci#define PLX_REG_CTRL2_MAX_TXFR_SIZE_2KB 5 6662306a36Sopenharmony_ci#define PLX_REG_CTRL2_MAX_TXFR_SIZE_4B 7 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci#define PLX_REG_INTR_CRTL_ERROR_EN BIT(0) 6962306a36Sopenharmony_ci#define PLX_REG_INTR_CRTL_INV_DESC_EN BIT(1) 7062306a36Sopenharmony_ci#define PLX_REG_INTR_CRTL_ABORT_DONE_EN BIT(3) 7162306a36Sopenharmony_ci#define PLX_REG_INTR_CRTL_PAUSE_DONE_EN BIT(4) 7262306a36Sopenharmony_ci#define PLX_REG_INTR_CRTL_IMM_PAUSE_DONE_EN BIT(5) 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci#define PLX_REG_INTR_STATUS_ERROR BIT(0) 7562306a36Sopenharmony_ci#define PLX_REG_INTR_STATUS_INV_DESC BIT(1) 7662306a36Sopenharmony_ci#define PLX_REG_INTR_STATUS_DESC_DONE BIT(2) 7762306a36Sopenharmony_ci#define PLX_REG_INTR_CRTL_ABORT_DONE BIT(3) 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistruct plx_dma_hw_std_desc { 8062306a36Sopenharmony_ci __le32 flags_and_size; 8162306a36Sopenharmony_ci __le16 dst_addr_hi; 8262306a36Sopenharmony_ci __le16 src_addr_hi; 8362306a36Sopenharmony_ci __le32 dst_addr_lo; 8462306a36Sopenharmony_ci __le32 src_addr_lo; 8562306a36Sopenharmony_ci}; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci#define PLX_DESC_SIZE_MASK 0x7ffffff 8862306a36Sopenharmony_ci#define PLX_DESC_FLAG_VALID BIT(31) 8962306a36Sopenharmony_ci#define PLX_DESC_FLAG_INT_WHEN_DONE BIT(30) 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci#define PLX_DESC_WB_SUCCESS BIT(30) 9262306a36Sopenharmony_ci#define PLX_DESC_WB_RD_FAIL BIT(29) 9362306a36Sopenharmony_ci#define PLX_DESC_WB_WR_FAIL BIT(28) 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci#define PLX_DMA_RING_COUNT 2048 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistruct plx_dma_desc { 9862306a36Sopenharmony_ci struct dma_async_tx_descriptor txd; 9962306a36Sopenharmony_ci struct plx_dma_hw_std_desc *hw; 10062306a36Sopenharmony_ci u32 orig_size; 10162306a36Sopenharmony_ci}; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistruct plx_dma_dev { 10462306a36Sopenharmony_ci struct dma_device dma_dev; 10562306a36Sopenharmony_ci struct dma_chan dma_chan; 10662306a36Sopenharmony_ci struct pci_dev __rcu *pdev; 10762306a36Sopenharmony_ci void __iomem *bar; 10862306a36Sopenharmony_ci struct tasklet_struct desc_task; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci spinlock_t ring_lock; 11162306a36Sopenharmony_ci bool ring_active; 11262306a36Sopenharmony_ci int head; 11362306a36Sopenharmony_ci int tail; 11462306a36Sopenharmony_ci struct plx_dma_hw_std_desc *hw_ring; 11562306a36Sopenharmony_ci dma_addr_t hw_ring_dma; 11662306a36Sopenharmony_ci struct plx_dma_desc **desc_ring; 11762306a36Sopenharmony_ci}; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic struct plx_dma_dev *chan_to_plx_dma_dev(struct dma_chan *c) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci return container_of(c, struct plx_dma_dev, dma_chan); 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic struct plx_dma_desc *to_plx_desc(struct dma_async_tx_descriptor *txd) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci return container_of(txd, struct plx_dma_desc, txd); 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic struct plx_dma_desc *plx_dma_get_desc(struct plx_dma_dev *plxdev, int i) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci return plxdev->desc_ring[i & (PLX_DMA_RING_COUNT - 1)]; 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic void plx_dma_process_desc(struct plx_dma_dev *plxdev) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci struct dmaengine_result res; 13762306a36Sopenharmony_ci struct plx_dma_desc *desc; 13862306a36Sopenharmony_ci u32 flags; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci spin_lock(&plxdev->ring_lock); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci while (plxdev->tail != plxdev->head) { 14362306a36Sopenharmony_ci desc = plx_dma_get_desc(plxdev, plxdev->tail); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci flags = le32_to_cpu(READ_ONCE(desc->hw->flags_and_size)); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (flags & PLX_DESC_FLAG_VALID) 14862306a36Sopenharmony_ci break; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci res.residue = desc->orig_size - (flags & PLX_DESC_SIZE_MASK); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (flags & PLX_DESC_WB_SUCCESS) 15362306a36Sopenharmony_ci res.result = DMA_TRANS_NOERROR; 15462306a36Sopenharmony_ci else if (flags & PLX_DESC_WB_WR_FAIL) 15562306a36Sopenharmony_ci res.result = DMA_TRANS_WRITE_FAILED; 15662306a36Sopenharmony_ci else 15762306a36Sopenharmony_ci res.result = DMA_TRANS_READ_FAILED; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci dma_cookie_complete(&desc->txd); 16062306a36Sopenharmony_ci dma_descriptor_unmap(&desc->txd); 16162306a36Sopenharmony_ci dmaengine_desc_get_callback_invoke(&desc->txd, &res); 16262306a36Sopenharmony_ci desc->txd.callback = NULL; 16362306a36Sopenharmony_ci desc->txd.callback_result = NULL; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci plxdev->tail++; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci spin_unlock(&plxdev->ring_lock); 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic void plx_dma_abort_desc(struct plx_dma_dev *plxdev) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci struct dmaengine_result res; 17462306a36Sopenharmony_ci struct plx_dma_desc *desc; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci plx_dma_process_desc(plxdev); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci spin_lock_bh(&plxdev->ring_lock); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci while (plxdev->tail != plxdev->head) { 18162306a36Sopenharmony_ci desc = plx_dma_get_desc(plxdev, plxdev->tail); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci res.residue = desc->orig_size; 18462306a36Sopenharmony_ci res.result = DMA_TRANS_ABORTED; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci dma_cookie_complete(&desc->txd); 18762306a36Sopenharmony_ci dma_descriptor_unmap(&desc->txd); 18862306a36Sopenharmony_ci dmaengine_desc_get_callback_invoke(&desc->txd, &res); 18962306a36Sopenharmony_ci desc->txd.callback = NULL; 19062306a36Sopenharmony_ci desc->txd.callback_result = NULL; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci plxdev->tail++; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci spin_unlock_bh(&plxdev->ring_lock); 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic void __plx_dma_stop(struct plx_dma_dev *plxdev) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci unsigned long timeout = jiffies + msecs_to_jiffies(1000); 20162306a36Sopenharmony_ci u32 val; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci val = readl(plxdev->bar + PLX_REG_CTRL); 20462306a36Sopenharmony_ci if (!(val & ~PLX_REG_CTRL_GRACEFUL_PAUSE)) 20562306a36Sopenharmony_ci return; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci writel(PLX_REG_CTRL_RESET_VAL | PLX_REG_CTRL_GRACEFUL_PAUSE, 20862306a36Sopenharmony_ci plxdev->bar + PLX_REG_CTRL); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci while (!time_after(jiffies, timeout)) { 21162306a36Sopenharmony_ci val = readl(plxdev->bar + PLX_REG_CTRL); 21262306a36Sopenharmony_ci if (val & PLX_REG_CTRL_GRACEFUL_PAUSE_DONE) 21362306a36Sopenharmony_ci break; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci cpu_relax(); 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci if (!(val & PLX_REG_CTRL_GRACEFUL_PAUSE_DONE)) 21962306a36Sopenharmony_ci dev_err(plxdev->dma_dev.dev, 22062306a36Sopenharmony_ci "Timeout waiting for graceful pause!\n"); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci writel(PLX_REG_CTRL_RESET_VAL | PLX_REG_CTRL_GRACEFUL_PAUSE, 22362306a36Sopenharmony_ci plxdev->bar + PLX_REG_CTRL); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci writel(0, plxdev->bar + PLX_REG_DESC_RING_COUNT); 22662306a36Sopenharmony_ci writel(0, plxdev->bar + PLX_REG_DESC_RING_ADDR); 22762306a36Sopenharmony_ci writel(0, plxdev->bar + PLX_REG_DESC_RING_ADDR_HI); 22862306a36Sopenharmony_ci writel(0, plxdev->bar + PLX_REG_DESC_RING_NEXT_ADDR); 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic void plx_dma_stop(struct plx_dma_dev *plxdev) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci rcu_read_lock(); 23462306a36Sopenharmony_ci if (!rcu_dereference(plxdev->pdev)) { 23562306a36Sopenharmony_ci rcu_read_unlock(); 23662306a36Sopenharmony_ci return; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci __plx_dma_stop(plxdev); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci rcu_read_unlock(); 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic void plx_dma_desc_task(struct tasklet_struct *t) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci struct plx_dma_dev *plxdev = from_tasklet(plxdev, t, desc_task); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci plx_dma_process_desc(plxdev); 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *plx_dma_prep_memcpy(struct dma_chan *c, 25262306a36Sopenharmony_ci dma_addr_t dma_dst, dma_addr_t dma_src, size_t len, 25362306a36Sopenharmony_ci unsigned long flags) 25462306a36Sopenharmony_ci __acquires(plxdev->ring_lock) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci struct plx_dma_dev *plxdev = chan_to_plx_dma_dev(c); 25762306a36Sopenharmony_ci struct plx_dma_desc *plxdesc; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci spin_lock_bh(&plxdev->ring_lock); 26062306a36Sopenharmony_ci if (!plxdev->ring_active) 26162306a36Sopenharmony_ci goto err_unlock; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (!CIRC_SPACE(plxdev->head, plxdev->tail, PLX_DMA_RING_COUNT)) 26462306a36Sopenharmony_ci goto err_unlock; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (len > PLX_DESC_SIZE_MASK) 26762306a36Sopenharmony_ci goto err_unlock; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci plxdesc = plx_dma_get_desc(plxdev, plxdev->head); 27062306a36Sopenharmony_ci plxdev->head++; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci plxdesc->hw->dst_addr_lo = cpu_to_le32(lower_32_bits(dma_dst)); 27362306a36Sopenharmony_ci plxdesc->hw->dst_addr_hi = cpu_to_le16(upper_32_bits(dma_dst)); 27462306a36Sopenharmony_ci plxdesc->hw->src_addr_lo = cpu_to_le32(lower_32_bits(dma_src)); 27562306a36Sopenharmony_ci plxdesc->hw->src_addr_hi = cpu_to_le16(upper_32_bits(dma_src)); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci plxdesc->orig_size = len; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (flags & DMA_PREP_INTERRUPT) 28062306a36Sopenharmony_ci len |= PLX_DESC_FLAG_INT_WHEN_DONE; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci plxdesc->hw->flags_and_size = cpu_to_le32(len); 28362306a36Sopenharmony_ci plxdesc->txd.flags = flags; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci /* return with the lock held, it will be released in tx_submit */ 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci return &plxdesc->txd; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cierr_unlock: 29062306a36Sopenharmony_ci /* 29162306a36Sopenharmony_ci * Keep sparse happy by restoring an even lock count on 29262306a36Sopenharmony_ci * this lock. 29362306a36Sopenharmony_ci */ 29462306a36Sopenharmony_ci __acquire(plxdev->ring_lock); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci spin_unlock_bh(&plxdev->ring_lock); 29762306a36Sopenharmony_ci return NULL; 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cistatic dma_cookie_t plx_dma_tx_submit(struct dma_async_tx_descriptor *desc) 30162306a36Sopenharmony_ci __releases(plxdev->ring_lock) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci struct plx_dma_dev *plxdev = chan_to_plx_dma_dev(desc->chan); 30462306a36Sopenharmony_ci struct plx_dma_desc *plxdesc = to_plx_desc(desc); 30562306a36Sopenharmony_ci dma_cookie_t cookie; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci cookie = dma_cookie_assign(desc); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci /* 31062306a36Sopenharmony_ci * Ensure the descriptor updates are visible to the dma device 31162306a36Sopenharmony_ci * before setting the valid bit. 31262306a36Sopenharmony_ci */ 31362306a36Sopenharmony_ci wmb(); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci plxdesc->hw->flags_and_size |= cpu_to_le32(PLX_DESC_FLAG_VALID); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci spin_unlock_bh(&plxdev->ring_lock); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci return cookie; 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic enum dma_status plx_dma_tx_status(struct dma_chan *chan, 32362306a36Sopenharmony_ci dma_cookie_t cookie, struct dma_tx_state *txstate) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci struct plx_dma_dev *plxdev = chan_to_plx_dma_dev(chan); 32662306a36Sopenharmony_ci enum dma_status ret; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci ret = dma_cookie_status(chan, cookie, txstate); 32962306a36Sopenharmony_ci if (ret == DMA_COMPLETE) 33062306a36Sopenharmony_ci return ret; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci plx_dma_process_desc(plxdev); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci return dma_cookie_status(chan, cookie, txstate); 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic void plx_dma_issue_pending(struct dma_chan *chan) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci struct plx_dma_dev *plxdev = chan_to_plx_dma_dev(chan); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci rcu_read_lock(); 34262306a36Sopenharmony_ci if (!rcu_dereference(plxdev->pdev)) { 34362306a36Sopenharmony_ci rcu_read_unlock(); 34462306a36Sopenharmony_ci return; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci /* 34862306a36Sopenharmony_ci * Ensure the valid bits are visible before starting the 34962306a36Sopenharmony_ci * DMA engine. 35062306a36Sopenharmony_ci */ 35162306a36Sopenharmony_ci wmb(); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci writew(PLX_REG_CTRL_START_VAL, plxdev->bar + PLX_REG_CTRL); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci rcu_read_unlock(); 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cistatic irqreturn_t plx_dma_isr(int irq, void *devid) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci struct plx_dma_dev *plxdev = devid; 36162306a36Sopenharmony_ci u32 status; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci status = readw(plxdev->bar + PLX_REG_INTR_STATUS); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (!status) 36662306a36Sopenharmony_ci return IRQ_NONE; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci if (status & PLX_REG_INTR_STATUS_DESC_DONE && plxdev->ring_active) 36962306a36Sopenharmony_ci tasklet_schedule(&plxdev->desc_task); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci writew(status, plxdev->bar + PLX_REG_INTR_STATUS); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci return IRQ_HANDLED; 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_cistatic int plx_dma_alloc_desc(struct plx_dma_dev *plxdev) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci struct plx_dma_desc *desc; 37962306a36Sopenharmony_ci int i; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci plxdev->desc_ring = kcalloc(PLX_DMA_RING_COUNT, 38262306a36Sopenharmony_ci sizeof(*plxdev->desc_ring), GFP_KERNEL); 38362306a36Sopenharmony_ci if (!plxdev->desc_ring) 38462306a36Sopenharmony_ci return -ENOMEM; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci for (i = 0; i < PLX_DMA_RING_COUNT; i++) { 38762306a36Sopenharmony_ci desc = kzalloc(sizeof(*desc), GFP_KERNEL); 38862306a36Sopenharmony_ci if (!desc) 38962306a36Sopenharmony_ci goto free_and_exit; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci dma_async_tx_descriptor_init(&desc->txd, &plxdev->dma_chan); 39262306a36Sopenharmony_ci desc->txd.tx_submit = plx_dma_tx_submit; 39362306a36Sopenharmony_ci desc->hw = &plxdev->hw_ring[i]; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci plxdev->desc_ring[i] = desc; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci return 0; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cifree_and_exit: 40162306a36Sopenharmony_ci for (i = 0; i < PLX_DMA_RING_COUNT; i++) 40262306a36Sopenharmony_ci kfree(plxdev->desc_ring[i]); 40362306a36Sopenharmony_ci kfree(plxdev->desc_ring); 40462306a36Sopenharmony_ci return -ENOMEM; 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_cistatic int plx_dma_alloc_chan_resources(struct dma_chan *chan) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci struct plx_dma_dev *plxdev = chan_to_plx_dma_dev(chan); 41062306a36Sopenharmony_ci size_t ring_sz = PLX_DMA_RING_COUNT * sizeof(*plxdev->hw_ring); 41162306a36Sopenharmony_ci int rc; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci plxdev->head = plxdev->tail = 0; 41462306a36Sopenharmony_ci plxdev->hw_ring = dma_alloc_coherent(plxdev->dma_dev.dev, ring_sz, 41562306a36Sopenharmony_ci &plxdev->hw_ring_dma, GFP_KERNEL); 41662306a36Sopenharmony_ci if (!plxdev->hw_ring) 41762306a36Sopenharmony_ci return -ENOMEM; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci rc = plx_dma_alloc_desc(plxdev); 42062306a36Sopenharmony_ci if (rc) 42162306a36Sopenharmony_ci goto out_free_hw_ring; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci rcu_read_lock(); 42462306a36Sopenharmony_ci if (!rcu_dereference(plxdev->pdev)) { 42562306a36Sopenharmony_ci rcu_read_unlock(); 42662306a36Sopenharmony_ci rc = -ENODEV; 42762306a36Sopenharmony_ci goto out_free_hw_ring; 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci writel(PLX_REG_CTRL_RESET_VAL, plxdev->bar + PLX_REG_CTRL); 43162306a36Sopenharmony_ci writel(lower_32_bits(plxdev->hw_ring_dma), 43262306a36Sopenharmony_ci plxdev->bar + PLX_REG_DESC_RING_ADDR); 43362306a36Sopenharmony_ci writel(upper_32_bits(plxdev->hw_ring_dma), 43462306a36Sopenharmony_ci plxdev->bar + PLX_REG_DESC_RING_ADDR_HI); 43562306a36Sopenharmony_ci writel(lower_32_bits(plxdev->hw_ring_dma), 43662306a36Sopenharmony_ci plxdev->bar + PLX_REG_DESC_RING_NEXT_ADDR); 43762306a36Sopenharmony_ci writel(PLX_DMA_RING_COUNT, plxdev->bar + PLX_REG_DESC_RING_COUNT); 43862306a36Sopenharmony_ci writel(PLX_REG_PREF_LIMIT_PREF_FOUR, plxdev->bar + PLX_REG_PREF_LIMIT); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci plxdev->ring_active = true; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci rcu_read_unlock(); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci return PLX_DMA_RING_COUNT; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ciout_free_hw_ring: 44762306a36Sopenharmony_ci dma_free_coherent(plxdev->dma_dev.dev, ring_sz, plxdev->hw_ring, 44862306a36Sopenharmony_ci plxdev->hw_ring_dma); 44962306a36Sopenharmony_ci return rc; 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_cistatic void plx_dma_free_chan_resources(struct dma_chan *chan) 45362306a36Sopenharmony_ci{ 45462306a36Sopenharmony_ci struct plx_dma_dev *plxdev = chan_to_plx_dma_dev(chan); 45562306a36Sopenharmony_ci size_t ring_sz = PLX_DMA_RING_COUNT * sizeof(*plxdev->hw_ring); 45662306a36Sopenharmony_ci struct pci_dev *pdev; 45762306a36Sopenharmony_ci int irq = -1; 45862306a36Sopenharmony_ci int i; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci spin_lock_bh(&plxdev->ring_lock); 46162306a36Sopenharmony_ci plxdev->ring_active = false; 46262306a36Sopenharmony_ci spin_unlock_bh(&plxdev->ring_lock); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci plx_dma_stop(plxdev); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci rcu_read_lock(); 46762306a36Sopenharmony_ci pdev = rcu_dereference(plxdev->pdev); 46862306a36Sopenharmony_ci if (pdev) 46962306a36Sopenharmony_ci irq = pci_irq_vector(pdev, 0); 47062306a36Sopenharmony_ci rcu_read_unlock(); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci if (irq > 0) 47362306a36Sopenharmony_ci synchronize_irq(irq); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci tasklet_kill(&plxdev->desc_task); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci plx_dma_abort_desc(plxdev); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci for (i = 0; i < PLX_DMA_RING_COUNT; i++) 48062306a36Sopenharmony_ci kfree(plxdev->desc_ring[i]); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci kfree(plxdev->desc_ring); 48362306a36Sopenharmony_ci dma_free_coherent(plxdev->dma_dev.dev, ring_sz, plxdev->hw_ring, 48462306a36Sopenharmony_ci plxdev->hw_ring_dma); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_cistatic void plx_dma_release(struct dma_device *dma_dev) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci struct plx_dma_dev *plxdev = 49162306a36Sopenharmony_ci container_of(dma_dev, struct plx_dma_dev, dma_dev); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci put_device(dma_dev->dev); 49462306a36Sopenharmony_ci kfree(plxdev); 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cistatic int plx_dma_create(struct pci_dev *pdev) 49862306a36Sopenharmony_ci{ 49962306a36Sopenharmony_ci struct plx_dma_dev *plxdev; 50062306a36Sopenharmony_ci struct dma_device *dma; 50162306a36Sopenharmony_ci struct dma_chan *chan; 50262306a36Sopenharmony_ci int rc; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci plxdev = kzalloc(sizeof(*plxdev), GFP_KERNEL); 50562306a36Sopenharmony_ci if (!plxdev) 50662306a36Sopenharmony_ci return -ENOMEM; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci rc = request_irq(pci_irq_vector(pdev, 0), plx_dma_isr, 0, 50962306a36Sopenharmony_ci KBUILD_MODNAME, plxdev); 51062306a36Sopenharmony_ci if (rc) 51162306a36Sopenharmony_ci goto free_plx; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci spin_lock_init(&plxdev->ring_lock); 51462306a36Sopenharmony_ci tasklet_setup(&plxdev->desc_task, plx_dma_desc_task); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci RCU_INIT_POINTER(plxdev->pdev, pdev); 51762306a36Sopenharmony_ci plxdev->bar = pcim_iomap_table(pdev)[0]; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci dma = &plxdev->dma_dev; 52062306a36Sopenharmony_ci INIT_LIST_HEAD(&dma->channels); 52162306a36Sopenharmony_ci dma_cap_set(DMA_MEMCPY, dma->cap_mask); 52262306a36Sopenharmony_ci dma->copy_align = DMAENGINE_ALIGN_1_BYTE; 52362306a36Sopenharmony_ci dma->dev = get_device(&pdev->dev); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci dma->device_alloc_chan_resources = plx_dma_alloc_chan_resources; 52662306a36Sopenharmony_ci dma->device_free_chan_resources = plx_dma_free_chan_resources; 52762306a36Sopenharmony_ci dma->device_prep_dma_memcpy = plx_dma_prep_memcpy; 52862306a36Sopenharmony_ci dma->device_issue_pending = plx_dma_issue_pending; 52962306a36Sopenharmony_ci dma->device_tx_status = plx_dma_tx_status; 53062306a36Sopenharmony_ci dma->device_release = plx_dma_release; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci chan = &plxdev->dma_chan; 53362306a36Sopenharmony_ci chan->device = dma; 53462306a36Sopenharmony_ci dma_cookie_init(chan); 53562306a36Sopenharmony_ci list_add_tail(&chan->device_node, &dma->channels); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci rc = dma_async_device_register(dma); 53862306a36Sopenharmony_ci if (rc) { 53962306a36Sopenharmony_ci pci_err(pdev, "Failed to register dma device: %d\n", rc); 54062306a36Sopenharmony_ci goto put_device; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci pci_set_drvdata(pdev, plxdev); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci return 0; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ciput_device: 54862306a36Sopenharmony_ci put_device(&pdev->dev); 54962306a36Sopenharmony_ci free_irq(pci_irq_vector(pdev, 0), plxdev); 55062306a36Sopenharmony_cifree_plx: 55162306a36Sopenharmony_ci kfree(plxdev); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci return rc; 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_cistatic int plx_dma_probe(struct pci_dev *pdev, 55762306a36Sopenharmony_ci const struct pci_device_id *id) 55862306a36Sopenharmony_ci{ 55962306a36Sopenharmony_ci int rc; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci rc = pcim_enable_device(pdev); 56262306a36Sopenharmony_ci if (rc) 56362306a36Sopenharmony_ci return rc; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(48)); 56662306a36Sopenharmony_ci if (rc) 56762306a36Sopenharmony_ci rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); 56862306a36Sopenharmony_ci if (rc) 56962306a36Sopenharmony_ci return rc; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci rc = pcim_iomap_regions(pdev, 1, KBUILD_MODNAME); 57262306a36Sopenharmony_ci if (rc) 57362306a36Sopenharmony_ci return rc; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci rc = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); 57662306a36Sopenharmony_ci if (rc <= 0) 57762306a36Sopenharmony_ci return rc; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci pci_set_master(pdev); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci rc = plx_dma_create(pdev); 58262306a36Sopenharmony_ci if (rc) 58362306a36Sopenharmony_ci goto err_free_irq_vectors; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci pci_info(pdev, "PLX DMA Channel Registered\n"); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci return 0; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_cierr_free_irq_vectors: 59062306a36Sopenharmony_ci pci_free_irq_vectors(pdev); 59162306a36Sopenharmony_ci return rc; 59262306a36Sopenharmony_ci} 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_cistatic void plx_dma_remove(struct pci_dev *pdev) 59562306a36Sopenharmony_ci{ 59662306a36Sopenharmony_ci struct plx_dma_dev *plxdev = pci_get_drvdata(pdev); 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci free_irq(pci_irq_vector(pdev, 0), plxdev); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci rcu_assign_pointer(plxdev->pdev, NULL); 60162306a36Sopenharmony_ci synchronize_rcu(); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci spin_lock_bh(&plxdev->ring_lock); 60462306a36Sopenharmony_ci plxdev->ring_active = false; 60562306a36Sopenharmony_ci spin_unlock_bh(&plxdev->ring_lock); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci __plx_dma_stop(plxdev); 60862306a36Sopenharmony_ci plx_dma_abort_desc(plxdev); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci plxdev->bar = NULL; 61162306a36Sopenharmony_ci dma_async_device_unregister(&plxdev->dma_dev); 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci pci_free_irq_vectors(pdev); 61462306a36Sopenharmony_ci} 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_cistatic const struct pci_device_id plx_dma_pci_tbl[] = { 61762306a36Sopenharmony_ci { 61862306a36Sopenharmony_ci .vendor = PCI_VENDOR_ID_PLX, 61962306a36Sopenharmony_ci .device = 0x87D0, 62062306a36Sopenharmony_ci .subvendor = PCI_ANY_ID, 62162306a36Sopenharmony_ci .subdevice = PCI_ANY_ID, 62262306a36Sopenharmony_ci .class = PCI_CLASS_SYSTEM_OTHER << 8, 62362306a36Sopenharmony_ci .class_mask = 0xFFFFFFFF, 62462306a36Sopenharmony_ci }, 62562306a36Sopenharmony_ci {0} 62662306a36Sopenharmony_ci}; 62762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, plx_dma_pci_tbl); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_cistatic struct pci_driver plx_dma_pci_driver = { 63062306a36Sopenharmony_ci .name = KBUILD_MODNAME, 63162306a36Sopenharmony_ci .id_table = plx_dma_pci_tbl, 63262306a36Sopenharmony_ci .probe = plx_dma_probe, 63362306a36Sopenharmony_ci .remove = plx_dma_remove, 63462306a36Sopenharmony_ci}; 63562306a36Sopenharmony_cimodule_pci_driver(plx_dma_pci_driver); 636