Something went wrong. Try again.
(I'm so sorry!) Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438// SPDX-License-Identifier: GPL-2.0/* * Microchip Switchtec(tm) DMA Controller Driver * Copyright (c) 2025, Kelvin Cao <kelvin.cao@microchip.com> * Copyright (c) 2025, Microchip Corporation */
#include <linux/bitfield.h>#include <linux/circ_buf.h>#include <linux/dmaengine.h>#include <linux/module.h>#include <linux/pci.h>#include <linux/delay.h>#include <linux/iopoll.h>
#include "dmaengine.h"
MODULE_DESCRIPTION("Switchtec PCIe Switch DMA Engine");MODULE_LICENSE("GPL");MODULE_AUTHOR("Kelvin Cao");
#define SWITCHTEC_DMAC_CHAN_CTRL_OFFSET 0x1000#define SWITCHTEC_DMAC_CHAN_CFG_STS_OFFSET 0x160000
#define SWITCHTEC_DMA_CHAN_HW_REGS_SIZE 0x1000#define SWITCHTEC_DMA_CHAN_FW_REGS_SIZE 0x80
#define SWITCHTEC_REG_CAP 0x80#define SWITCHTEC_REG_CHAN_CNT 0x84#define SWITCHTEC_REG_TAG_LIMIT 0x90#define SWITCHTEC_REG_CHAN_STS_VEC 0x94#define SWITCHTEC_REG_SE_BUF_CNT 0x98#define SWITCHTEC_REG_SE_BUF_BASE 0x9a
#define SWITCHTEC_DESC_MAX_SIZE 0x100000
#define SWITCHTEC_CHAN_CTRL_PAUSE BIT(0)#define SWITCHTEC_CHAN_CTRL_HALT BIT(1)#define SWITCHTEC_CHAN_CTRL_RESET BIT(2)#define SWITCHTEC_CHAN_CTRL_ERR_PAUSE BIT(3)
#define SWITCHTEC_CHAN_STS_PAUSED BIT(9)#define SWITCHTEC_CHAN_STS_HALTED BIT(10)#define SWITCHTEC_CHAN_STS_PAUSED_MASK GENMASK(29, 13)
#define SWITCHTEC_INVALID_HFID 0xffff
#define SWITCHTEC_DMA_SQ_SIZE SZ_32K#define SWITCHTEC_DMA_CQ_SIZE SZ_32K
#define SWITCHTEC_DMA_RING_SIZE SZ_32K
static const char * const channel_status_str[] = { [13] = "received a VDM with length error status", [14] = "received a VDM or Cpl with Unsupported Request error status", [15] = "received a VDM or Cpl with Completion Abort error status", [16] = "received a VDM with ECRC error status", [17] = "received a VDM with EP error status", [18] = "received a VDM with Reserved Cpl error status", [19] = "received only part of split SE CplD", [20] = "the ISP_DMAC detected a Completion Time Out", [21] = "received a Cpl with Unsupported Request status", [22] = "received a Cpl with Completion Abort status", [23] = "received a Cpl with a reserved status", [24] = "received a TLP with ECRC error status in its metadata", [25] = "received a TLP with the EP bit set in the header", [26] = "the ISP_DMAC tried to process a SE with an invalid Connection ID", [27] = "the ISP_DMAC tried to process a SE with an invalid Remote Host interrupt", [28] = "a reserved opcode was detected in an SE", [29] = "received a SE Cpl with error status",};
struct chan_hw_regs { u16 cq_head; u16 rsvd1; u16 sq_tail; u16 rsvd2; u8 ctrl; u8 rsvd3[3]; u16 status; u16 rsvd4;};
#define PERF_BURST_SCALE_MASK GENMASK_U32(3, 2)#define PERF_MRRS_MASK GENMASK_U32(6, 4)#define PERF_INTERVAL_MASK GENMASK_U32(10, 8)#define PERF_BURST_SIZE_MASK GENMASK_U32(14, 12)#define PERF_ARB_WEIGHT_MASK GENMASK_U32(31, 24)
#define SE_BUF_BASE_MASK GENMASK_U32(10, 2)#define SE_BUF_LEN_MASK GENMASK_U32(20, 12)#define SE_THRESH_MASK GENMASK_U32(31, 23)
#define SWITCHTEC_CHAN_ENABLE BIT(1)
struct chan_fw_regs { u32 valid_en_se; u32 cq_base_lo; u32 cq_base_hi; u16 cq_size; u16 rsvd1; u32 sq_base_lo; u32 sq_base_hi; u16 sq_size; u16 rsvd2; u32 int_vec; u32 perf_cfg; u32 rsvd3; u32 perf_latency_selector; u32 perf_fetched_se_cnt_lo; u32 perf_fetched_se_cnt_hi; u32 perf_byte_cnt_lo; u32 perf_byte_cnt_hi; u32 rsvd4; u16 perf_se_pending; u16 perf_se_buf_empty; u32 perf_chan_idle; u32 perf_lat_max; u32 perf_lat_min; u32 perf_lat_last; u16 sq_current; u16 sq_phase; u16 cq_current; u16 cq_phase;};
struct switchtec_dma_chan { struct switchtec_dma_dev *swdma_dev; struct dma_chan dma_chan; struct chan_hw_regs __iomem *mmio_chan_hw; struct chan_fw_regs __iomem *mmio_chan_fw;
/* Serialize hardware control register access */ spinlock_t hw_ctrl_lock;
struct tasklet_struct desc_task;
/* Serialize descriptor preparation */ spinlock_t submit_lock; bool ring_active; int cid;
/* Serialize completion processing */ spinlock_t complete_lock; bool comp_ring_active;
/* channel index and irq */ int index; int irq;
/* * In driver context, head is advanced by producer while * tail is advanced by consumer. */
/* the head and tail for both desc_ring and hw_sq */ int head; int tail; int phase_tag; struct switchtec_dma_hw_se_desc *hw_sq; dma_addr_t dma_addr_sq;
/* the tail for hw_cq */ int cq_tail; struct switchtec_dma_hw_ce *hw_cq; dma_addr_t dma_addr_cq;
struct list_head list;
struct switchtec_dma_desc *desc_ring[SWITCHTEC_DMA_RING_SIZE];};
struct switchtec_dma_dev { struct dma_device dma_dev; struct pci_dev __rcu *pdev; void __iomem *bar;
struct switchtec_dma_chan **swdma_chans; int chan_cnt; int chan_status_irq;};
enum chan_op { ENABLE_CHAN, DISABLE_CHAN,};
enum switchtec_dma_opcode { SWITCHTEC_DMA_OPC_MEMCPY = 0, SWITCHTEC_DMA_OPC_RDIMM = 0x1, SWITCHTEC_DMA_OPC_WRIMM = 0x2, SWITCHTEC_DMA_OPC_RHI = 0x6, SWITCHTEC_DMA_OPC_NOP = 0x7,};
struct switchtec_dma_hw_se_desc { u8 opc; u8 ctrl; __le16 tlp_setting; __le16 rsvd1; __le16 cid; __le32 byte_cnt; __le32 addr_lo; /* SADDR_LO/WIADDR_LO */ __le32 addr_hi; /* SADDR_HI/WIADDR_HI */ __le32 daddr_lo; __le32 daddr_hi; __le16 dfid; __le16 sfid;};
#define SWITCHTEC_SE_DFM BIT(5)#define SWITCHTEC_SE_LIOF BIT(6)#define SWITCHTEC_SE_BRR BIT(7)#define SWITCHTEC_SE_CID_MASK GENMASK(15, 0)
#define SWITCHTEC_CE_SC_LEN_ERR BIT(0)#define SWITCHTEC_CE_SC_UR BIT(1)#define SWITCHTEC_CE_SC_CA BIT(2)#define SWITCHTEC_CE_SC_RSVD_CPL BIT(3)#define SWITCHTEC_CE_SC_ECRC_ERR BIT(4)#define SWITCHTEC_CE_SC_EP_SET BIT(5)#define SWITCHTEC_CE_SC_D_RD_CTO BIT(8)#define SWITCHTEC_CE_SC_D_RIMM_UR BIT(9)#define SWITCHTEC_CE_SC_D_RIMM_CA BIT(10)#define SWITCHTEC_CE_SC_D_RIMM_RSVD_CPL BIT(11)#define SWITCHTEC_CE_SC_D_ECRC BIT(12)#define SWITCHTEC_CE_SC_D_EP_SET BIT(13)#define SWITCHTEC_CE_SC_D_BAD_CONNID BIT(14)#define SWITCHTEC_CE_SC_D_BAD_RHI_ADDR BIT(15)#define SWITCHTEC_CE_SC_D_INVD_CMD BIT(16)#define SWITCHTEC_CE_SC_MASK GENMASK(16, 0)
struct switchtec_dma_hw_ce { __le32 rdimm_cpl_dw0; __le32 rdimm_cpl_dw1; __le32 rsvd1; __le32 cpl_byte_cnt; __le16 sq_head; __le16 rsvd2; __le32 rsvd3; __le32 sts_code; __le16 cid; __le16 phase_tag;};
struct switchtec_dma_desc { struct dma_async_tx_descriptor txd; struct switchtec_dma_hw_se_desc *hw; u32 orig_size; bool completed;};
static int wait_for_chan_status(struct chan_hw_regs __iomem *chan_hw, u32 mask, bool set){ u32 status;
return readl_poll_timeout_atomic(&chan_hw->status, status, (set && (status & mask)) || (!set && !(status & mask)), 10, 100 * USEC_PER_MSEC);}
static int halt_channel(struct switchtec_dma_chan *swdma_chan){ struct chan_hw_regs __iomem *chan_hw = swdma_chan->mmio_chan_hw; struct pci_dev *pdev; int ret;
rcu_read_lock(); pdev = rcu_dereference(swdma_chan->swdma_dev->pdev); if (!pdev) { ret = -ENODEV; goto unlock_and_exit; }
spin_lock(&swdma_chan->hw_ctrl_lock); writeb(SWITCHTEC_CHAN_CTRL_HALT, &chan_hw->ctrl); ret = wait_for_chan_status(chan_hw, SWITCHTEC_CHAN_STS_HALTED, true); spin_unlock(&swdma_chan->hw_ctrl_lock);
unlock_and_exit: rcu_read_unlock(); return ret;}
static int unhalt_channel(struct switchtec_dma_chan *swdma_chan){ struct chan_hw_regs __iomem *chan_hw = swdma_chan->mmio_chan_hw; struct pci_dev *pdev; u8 ctrl; int ret;
rcu_read_lock(); pdev = rcu_dereference(swdma_chan->swdma_dev->pdev); if (!pdev) { ret = -ENODEV; goto unlock_and_exit; }
spin_lock(&swdma_chan->hw_ctrl_lock); ctrl = readb(&chan_hw->ctrl); ctrl &= ~SWITCHTEC_CHAN_CTRL_HALT; writeb(ctrl, &chan_hw->ctrl); ret = wait_for_chan_status(chan_hw, SWITCHTEC_CHAN_STS_HALTED, false); spin_unlock(&swdma_chan->hw_ctrl_lock);
unlock_and_exit: rcu_read_unlock(); return ret;}
static void flush_pci_write(struct chan_hw_regs __iomem *chan_hw){ readl(&chan_hw->cq_head);}
static int reset_channel(struct switchtec_dma_chan *swdma_chan){ struct chan_hw_regs __iomem *chan_hw = swdma_chan->mmio_chan_hw; struct pci_dev *pdev;
rcu_read_lock(); pdev = rcu_dereference(swdma_chan->swdma_dev->pdev); if (!pdev) { rcu_read_unlock(); return -ENODEV; }
spin_lock(&swdma_chan->hw_ctrl_lock); writel(SWITCHTEC_CHAN_CTRL_RESET | SWITCHTEC_CHAN_CTRL_ERR_PAUSE, &chan_hw->ctrl); flush_pci_write(chan_hw);
udelay(1000);
writel(SWITCHTEC_CHAN_CTRL_ERR_PAUSE, &chan_hw->ctrl); spin_unlock(&swdma_chan->hw_ctrl_lock); flush_pci_write(chan_hw);
rcu_read_unlock(); return 0;}
static int pause_reset_channel(struct switchtec_dma_chan *swdma_chan){ struct chan_hw_regs __iomem *chan_hw = swdma_chan->mmio_chan_hw; struct pci_dev *pdev;
rcu_read_lock(); pdev = rcu_dereference(swdma_chan->swdma_dev->pdev); if (!pdev) { rcu_read_unlock(); return -ENODEV; }
spin_lock(&swdma_chan->hw_ctrl_lock); writeb(SWITCHTEC_CHAN_CTRL_PAUSE, &chan_hw->ctrl); spin_unlock(&swdma_chan->hw_ctrl_lock);
flush_pci_write(chan_hw);
rcu_read_unlock();
/* wait 60ms to ensure no pending CEs */ mdelay(60);
return reset_channel(swdma_chan);}
static int channel_op(struct switchtec_dma_chan *swdma_chan, int op){ struct chan_fw_regs __iomem *chan_fw = swdma_chan->mmio_chan_fw; struct pci_dev *pdev; u32 valid_en_se;
rcu_read_lock(); pdev = rcu_dereference(swdma_chan->swdma_dev->pdev); if (!pdev) { rcu_read_unlock(); return -ENODEV; }
valid_en_se = readl(&chan_fw->valid_en_se); if (op == ENABLE_CHAN) valid_en_se |= SWITCHTEC_CHAN_ENABLE; else valid_en_se &= ~SWITCHTEC_CHAN_ENABLE;
writel(valid_en_se, &chan_fw->valid_en_se);
rcu_read_unlock(); return 0;}
static int enable_channel(struct switchtec_dma_chan *swdma_chan){ return channel_op(swdma_chan, ENABLE_CHAN);}
static int disable_channel(struct switchtec_dma_chan *swdma_chan){ return channel_op(swdma_chan, DISABLE_CHAN);}
static voidswitchtec_dma_cleanup_completed(struct switchtec_dma_chan *swdma_chan){ struct device *chan_dev = &swdma_chan->dma_chan.dev->device; struct switchtec_dma_desc *desc; struct switchtec_dma_hw_ce *ce; struct dmaengine_result res; int tail, cid, se_idx, i; __le16 phase_tag; u32 sts_code; __le32 *p;
do { spin_lock_bh(&swdma_chan->complete_lock); if (!swdma_chan->comp_ring_active) { spin_unlock_bh(&swdma_chan->complete_lock); break; }
ce = &swdma_chan->hw_cq[swdma_chan->cq_tail]; /* * phase_tag is updated by hardware, ensure the value is * not from the cache */ phase_tag = smp_load_acquire(&ce->phase_tag); if (le16_to_cpu(phase_tag) == swdma_chan->phase_tag) { spin_unlock_bh(&swdma_chan->complete_lock); break; }
cid = le16_to_cpu(ce->cid); se_idx = cid & (SWITCHTEC_DMA_SQ_SIZE - 1); desc = swdma_chan->desc_ring[se_idx];
tail = swdma_chan->tail;
res.residue = desc->orig_size - le32_to_cpu(ce->cpl_byte_cnt);
sts_code = le32_to_cpu(ce->sts_code);
if (!(sts_code & SWITCHTEC_CE_SC_MASK)) { res.result = DMA_TRANS_NOERROR; } else { if (sts_code & SWITCHTEC_CE_SC_D_RD_CTO) res.result = DMA_TRANS_READ_FAILED; else res.result = DMA_TRANS_WRITE_FAILED;
dev_err(chan_dev, "CID 0x%04x failed, SC 0x%08x\n", cid, (u32)(sts_code & SWITCHTEC_CE_SC_MASK));
p = (__le32 *)ce; for (i = 0; i < sizeof(*ce) / 4; i++) { dev_err(chan_dev, "CE DW%d: 0x%08x\n", i, le32_to_cpu(*p)); p++; } }
desc->completed = true;
swdma_chan->cq_tail++; swdma_chan->cq_tail &= SWITCHTEC_DMA_CQ_SIZE - 1;
rcu_read_lock(); if (!rcu_dereference(swdma_chan->swdma_dev->pdev)) { rcu_read_unlock(); spin_unlock_bh(&swdma_chan->complete_lock); return; } writew(swdma_chan->cq_tail, &swdma_chan->mmio_chan_hw->cq_head); rcu_read_unlock();
if (swdma_chan->cq_tail == 0) swdma_chan->phase_tag = !swdma_chan->phase_tag;
/* Out of order CE */ if (se_idx != tail) { spin_unlock_bh(&swdma_chan->complete_lock); continue; }
do { dma_cookie_complete(&desc->txd); dma_descriptor_unmap(&desc->txd); dmaengine_desc_get_callback_invoke(&desc->txd, &res); desc->txd.callback = NULL; desc->txd.callback_result = NULL; desc->completed = false;
tail++; tail &= SWITCHTEC_DMA_SQ_SIZE - 1;
/* * Ensure the desc updates are visible before updating * the tail index */ smp_store_release(&swdma_chan->tail, tail); desc = swdma_chan->desc_ring[swdma_chan->tail]; if (!desc->completed) break; } while (CIRC_CNT(READ_ONCE(swdma_chan->head), swdma_chan->tail, SWITCHTEC_DMA_SQ_SIZE));
spin_unlock_bh(&swdma_chan->complete_lock); } while (1);}
static voidswitchtec_dma_abort_desc(struct switchtec_dma_chan *swdma_chan, int force){ struct switchtec_dma_desc *desc; struct dmaengine_result res;
if (!force) switchtec_dma_cleanup_completed(swdma_chan);
spin_lock_bh(&swdma_chan->complete_lock);
while (CIRC_CNT(swdma_chan->head, swdma_chan->tail, SWITCHTEC_DMA_SQ_SIZE) >= 1) { desc = swdma_chan->desc_ring[swdma_chan->tail];
res.residue = desc->orig_size; res.result = DMA_TRANS_ABORTED;
dma_cookie_complete(&desc->txd); dma_descriptor_unmap(&desc->txd); if (!force) dmaengine_desc_get_callback_invoke(&desc->txd, &res); desc->txd.callback = NULL; desc->txd.callback_result = NULL;
swdma_chan->tail++; swdma_chan->tail &= SWITCHTEC_DMA_SQ_SIZE - 1; }
spin_unlock_bh(&swdma_chan->complete_lock);}
static void switchtec_dma_chan_stop(struct switchtec_dma_chan *swdma_chan){ int rc;
rc = halt_channel(swdma_chan); if (rc) return;
rcu_read_lock(); if (!rcu_dereference(swdma_chan->swdma_dev->pdev)) { rcu_read_unlock(); return; }
writel(0, &swdma_chan->mmio_chan_fw->sq_base_lo); writel(0, &swdma_chan->mmio_chan_fw->sq_base_hi); writel(0, &swdma_chan->mmio_chan_fw->cq_base_lo); writel(0, &swdma_chan->mmio_chan_fw->cq_base_hi);
rcu_read_unlock();}
static int switchtec_dma_terminate_all(struct dma_chan *chan){ struct switchtec_dma_chan *swdma_chan = container_of(chan, struct switchtec_dma_chan, dma_chan);
spin_lock_bh(&swdma_chan->complete_lock); swdma_chan->comp_ring_active = false; spin_unlock_bh(&swdma_chan->complete_lock);
return pause_reset_channel(swdma_chan);}
static void switchtec_dma_synchronize(struct dma_chan *chan){ struct switchtec_dma_chan *swdma_chan = container_of(chan, struct switchtec_dma_chan, dma_chan);
int rc;
switchtec_dma_abort_desc(swdma_chan, 1);
rc = enable_channel(swdma_chan); if (rc) return;
rc = reset_channel(swdma_chan); if (rc) return;
rc = unhalt_channel(swdma_chan); if (rc) return;
spin_lock_bh(&swdma_chan->submit_lock); swdma_chan->head = 0; spin_unlock_bh(&swdma_chan->submit_lock);
spin_lock_bh(&swdma_chan->complete_lock); swdma_chan->comp_ring_active = true; swdma_chan->phase_tag = 0; swdma_chan->tail = 0; swdma_chan->cq_tail = 0; swdma_chan->cid = 0; dma_cookie_init(chan); spin_unlock_bh(&swdma_chan->complete_lock);}
static struct dma_async_tx_descriptor *switchtec_dma_prep_desc(struct dma_chan *c, u16 dst_fid, dma_addr_t dma_dst, u16 src_fid, dma_addr_t dma_src, u64 data, size_t len, unsigned long flags) __acquires(swdma_chan->submit_lock){ struct switchtec_dma_chan *swdma_chan = container_of(c, struct switchtec_dma_chan, dma_chan); struct switchtec_dma_desc *desc; int head, tail;
spin_lock_bh(&swdma_chan->submit_lock);
if (!swdma_chan->ring_active) goto err_unlock;
tail = READ_ONCE(swdma_chan->tail); head = swdma_chan->head;
if (!CIRC_SPACE(head, tail, SWITCHTEC_DMA_RING_SIZE)) goto err_unlock;
desc = swdma_chan->desc_ring[head];
if (src_fid != SWITCHTEC_INVALID_HFID && dst_fid != SWITCHTEC_INVALID_HFID) desc->hw->ctrl |= SWITCHTEC_SE_DFM;
if (flags & DMA_PREP_INTERRUPT) desc->hw->ctrl |= SWITCHTEC_SE_LIOF;
if (flags & DMA_PREP_FENCE) desc->hw->ctrl |= SWITCHTEC_SE_BRR;
desc->txd.flags = flags;
desc->completed = false; desc->hw->opc = SWITCHTEC_DMA_OPC_MEMCPY; desc->hw->addr_lo = cpu_to_le32(lower_32_bits(dma_src)); desc->hw->addr_hi = cpu_to_le32(upper_32_bits(dma_src)); desc->hw->daddr_lo = cpu_to_le32(lower_32_bits(dma_dst)); desc->hw->daddr_hi = cpu_to_le32(upper_32_bits(dma_dst)); desc->hw->byte_cnt = cpu_to_le32(len); desc->hw->tlp_setting = 0; desc->hw->dfid = cpu_to_le16(dst_fid); desc->hw->sfid = cpu_to_le16(src_fid); swdma_chan->cid &= SWITCHTEC_SE_CID_MASK; desc->hw->cid = cpu_to_le16(swdma_chan->cid++); desc->orig_size = len;
/* return with the lock held, it will be released in tx_submit */
return &desc->txd;
err_unlock: /* * Keep sparse happy by restoring an even lock count on * this lock. */ __acquire(swdma_chan->submit_lock);
spin_unlock_bh(&swdma_chan->submit_lock); return NULL;}
static struct dma_async_tx_descriptor *switchtec_dma_prep_memcpy(struct dma_chan *c, dma_addr_t dma_dst, dma_addr_t dma_src, size_t len, unsigned long flags) __acquires(swdma_chan->submit_lock){ if (len > SWITCHTEC_DESC_MAX_SIZE) { /* * Keep sparse happy by restoring an even lock count on * this lock. */ __acquire(swdma_chan->submit_lock); return NULL; }
return switchtec_dma_prep_desc(c, SWITCHTEC_INVALID_HFID, dma_dst, SWITCHTEC_INVALID_HFID, dma_src, 0, len, flags);}
static dma_cookie_tswitchtec_dma_tx_submit(struct dma_async_tx_descriptor *desc) __releases(swdma_chan->submit_lock){ struct switchtec_dma_chan *swdma_chan = container_of(desc->chan, struct switchtec_dma_chan, dma_chan); dma_cookie_t cookie; int head;
head = swdma_chan->head + 1; head &= SWITCHTEC_DMA_RING_SIZE - 1;
/* * Ensure the desc updates are visible before updating the head index */ smp_store_release(&swdma_chan->head, head);
cookie = dma_cookie_assign(desc);
spin_unlock_bh(&swdma_chan->submit_lock);
return cookie;}
static enum dma_status switchtec_dma_tx_status(struct dma_chan *chan, dma_cookie_t cookie, struct dma_tx_state *txstate){ struct switchtec_dma_chan *swdma_chan = container_of(chan, struct switchtec_dma_chan, dma_chan); enum dma_status ret;
ret = dma_cookie_status(chan, cookie, txstate); if (ret == DMA_COMPLETE) return ret;
/* * For jobs where the interrupts are disabled, this is the only place * to process the completions returned by the hardware. Callers that * disable interrupts must call tx_status() to determine when a job * is done, so it is safe to process completions here. If a job has * interrupts enabled, then the completions will normally be processed * in the tasklet that is triggered by the interrupt and tx_status() * does not need to be called. */ switchtec_dma_cleanup_completed(swdma_chan);
return dma_cookie_status(chan, cookie, txstate);}
static void switchtec_dma_issue_pending(struct dma_chan *chan){ struct switchtec_dma_chan *swdma_chan = container_of(chan, struct switchtec_dma_chan, dma_chan); struct switchtec_dma_dev *swdma_dev = swdma_chan->swdma_dev;
/* * The sq_tail register is actually for the head of the * submisssion queue. Chip has the opposite define of head/tail * to the Linux kernel. */
rcu_read_lock(); if (!rcu_dereference(swdma_dev->pdev)) { rcu_read_unlock(); return; }
spin_lock_bh(&swdma_chan->submit_lock); writew(swdma_chan->head, &swdma_chan->mmio_chan_hw->sq_tail); spin_unlock_bh(&swdma_chan->submit_lock);
rcu_read_unlock();}
static int switchtec_dma_pause(struct dma_chan *chan){ struct switchtec_dma_chan *swdma_chan = container_of(chan, struct switchtec_dma_chan, dma_chan); struct chan_hw_regs __iomem *chan_hw = swdma_chan->mmio_chan_hw; struct pci_dev *pdev; int ret;
rcu_read_lock(); pdev = rcu_dereference(swdma_chan->swdma_dev->pdev); if (!pdev) { ret = -ENODEV; goto unlock_and_exit; }
spin_lock(&swdma_chan->hw_ctrl_lock); writeb(SWITCHTEC_CHAN_CTRL_PAUSE, &chan_hw->ctrl); ret = wait_for_chan_status(chan_hw, SWITCHTEC_CHAN_STS_PAUSED, true); spin_unlock(&swdma_chan->hw_ctrl_lock);
unlock_and_exit: rcu_read_unlock(); return ret;}
static int switchtec_dma_resume(struct dma_chan *chan){ struct switchtec_dma_chan *swdma_chan = container_of(chan, struct switchtec_dma_chan, dma_chan); struct chan_hw_regs __iomem *chan_hw = swdma_chan->mmio_chan_hw; struct pci_dev *pdev; int ret;
rcu_read_lock(); pdev = rcu_dereference(swdma_chan->swdma_dev->pdev); if (!pdev) { ret = -ENODEV; goto unlock_and_exit; }
spin_lock(&swdma_chan->hw_ctrl_lock); writeb(0, &chan_hw->ctrl); ret = wait_for_chan_status(chan_hw, SWITCHTEC_CHAN_STS_PAUSED, false); spin_unlock(&swdma_chan->hw_ctrl_lock);
unlock_and_exit: rcu_read_unlock(); return ret;}
static void switchtec_dma_desc_task(unsigned long data){ struct switchtec_dma_chan *swdma_chan = (void *)data;
switchtec_dma_cleanup_completed(swdma_chan);}
static irqreturn_t switchtec_dma_isr(int irq, void *chan){ struct switchtec_dma_chan *swdma_chan = chan;
if (swdma_chan->comp_ring_active) tasklet_schedule(&swdma_chan->desc_task);
return IRQ_HANDLED;}
static irqreturn_t switchtec_dma_chan_status_isr(int irq, void *dma){ struct switchtec_dma_dev *swdma_dev = dma; struct dma_device *dma_dev = &swdma_dev->dma_dev; struct switchtec_dma_chan *swdma_chan; struct chan_hw_regs __iomem *chan_hw; struct device *chan_dev; struct dma_chan *chan; u32 chan_status; int bit;
list_for_each_entry(chan, &dma_dev->channels, device_node) { swdma_chan = container_of(chan, struct switchtec_dma_chan, dma_chan); chan_dev = &swdma_chan->dma_chan.dev->device; chan_hw = swdma_chan->mmio_chan_hw;
rcu_read_lock(); if (!rcu_dereference(swdma_dev->pdev)) { rcu_read_unlock(); goto out; }
chan_status = readl(&chan_hw->status); chan_status &= SWITCHTEC_CHAN_STS_PAUSED_MASK; rcu_read_unlock();
bit = ffs(chan_status); if (!bit) dev_dbg(chan_dev, "No pause bit set.\n"); else dev_err(chan_dev, "Paused, %s\n", channel_status_str[bit - 1]); }
out: return IRQ_HANDLED;}
static void switchtec_dma_free_desc(struct switchtec_dma_chan *swdma_chan){ struct switchtec_dma_dev *swdma_dev = swdma_chan->swdma_dev; size_t size; int i;
size = SWITCHTEC_DMA_SQ_SIZE * sizeof(*swdma_chan->hw_sq); if (swdma_chan->hw_sq) dma_free_coherent(swdma_dev->dma_dev.dev, size, swdma_chan->hw_sq, swdma_chan->dma_addr_sq);
size = SWITCHTEC_DMA_CQ_SIZE * sizeof(*swdma_chan->hw_cq); if (swdma_chan->hw_cq) dma_free_coherent(swdma_dev->dma_dev.dev, size, swdma_chan->hw_cq, swdma_chan->dma_addr_cq);
for (i = 0; i < SWITCHTEC_DMA_RING_SIZE; i++) kfree(swdma_chan->desc_ring[i]);}
static int switchtec_dma_alloc_desc(struct switchtec_dma_chan *swdma_chan){ struct switchtec_dma_dev *swdma_dev = swdma_chan->swdma_dev; struct chan_fw_regs __iomem *chan_fw = swdma_chan->mmio_chan_fw; struct switchtec_dma_desc *desc; struct pci_dev *pdev; size_t size; int rc, i;
swdma_chan->head = 0; swdma_chan->tail = 0; swdma_chan->cq_tail = 0;
size = SWITCHTEC_DMA_SQ_SIZE * sizeof(*swdma_chan->hw_sq); swdma_chan->hw_sq = dma_alloc_coherent(swdma_dev->dma_dev.dev, size, &swdma_chan->dma_addr_sq, GFP_NOWAIT); if (!swdma_chan->hw_sq) { rc = -ENOMEM; goto free_and_exit; }
size = SWITCHTEC_DMA_CQ_SIZE * sizeof(*swdma_chan->hw_cq); swdma_chan->hw_cq = dma_alloc_coherent(swdma_dev->dma_dev.dev, size, &swdma_chan->dma_addr_cq, GFP_NOWAIT); if (!swdma_chan->hw_cq) { rc = -ENOMEM; goto free_and_exit; }
/* reset host phase tag */ swdma_chan->phase_tag = 0;
for (i = 0; i < SWITCHTEC_DMA_RING_SIZE; i++) { desc = kzalloc_obj(*desc, GFP_NOWAIT); if (!desc) { rc = -ENOMEM; goto free_and_exit; }
dma_async_tx_descriptor_init(&desc->txd, &swdma_chan->dma_chan); desc->txd.tx_submit = switchtec_dma_tx_submit; desc->hw = &swdma_chan->hw_sq[i]; desc->completed = true;
swdma_chan->desc_ring[i] = desc; }
rcu_read_lock(); pdev = rcu_dereference(swdma_dev->pdev); if (!pdev) { rcu_read_unlock(); rc = -ENODEV; goto free_and_exit; }
/* set sq/cq */ writel(lower_32_bits(swdma_chan->dma_addr_sq), &chan_fw->sq_base_lo); writel(upper_32_bits(swdma_chan->dma_addr_sq), &chan_fw->sq_base_hi); writel(lower_32_bits(swdma_chan->dma_addr_cq), &chan_fw->cq_base_lo); writel(upper_32_bits(swdma_chan->dma_addr_cq), &chan_fw->cq_base_hi);
writew(SWITCHTEC_DMA_SQ_SIZE, &swdma_chan->mmio_chan_fw->sq_size); writew(SWITCHTEC_DMA_CQ_SIZE, &swdma_chan->mmio_chan_fw->cq_size);
rcu_read_unlock(); return 0;
free_and_exit: switchtec_dma_free_desc(swdma_chan); return rc;}
static int switchtec_dma_alloc_chan_resources(struct dma_chan *chan){ struct switchtec_dma_chan *swdma_chan = container_of(chan, struct switchtec_dma_chan, dma_chan); struct switchtec_dma_dev *swdma_dev = swdma_chan->swdma_dev; u32 perf_cfg; int rc;
rc = switchtec_dma_alloc_desc(swdma_chan); if (rc) return rc;
rc = enable_channel(swdma_chan); if (rc) return rc;
rc = reset_channel(swdma_chan); if (rc) return rc;
rc = unhalt_channel(swdma_chan); if (rc) return rc;
swdma_chan->ring_active = true; swdma_chan->comp_ring_active = true; swdma_chan->cid = 0;
dma_cookie_init(chan);
rcu_read_lock(); if (!rcu_dereference(swdma_dev->pdev)) { rcu_read_unlock(); return -ENODEV; }
perf_cfg = readl(&swdma_chan->mmio_chan_fw->perf_cfg); rcu_read_unlock();
dev_dbg(&chan->dev->device, "Burst Size: 0x%x\n", FIELD_GET(PERF_BURST_SIZE_MASK, perf_cfg));
dev_dbg(&chan->dev->device, "Burst Scale: 0x%x\n", FIELD_GET(PERF_BURST_SCALE_MASK, perf_cfg));
dev_dbg(&chan->dev->device, "Interval: 0x%x\n", FIELD_GET(PERF_INTERVAL_MASK, perf_cfg));
dev_dbg(&chan->dev->device, "Arb Weight: 0x%x\n", FIELD_GET(PERF_ARB_WEIGHT_MASK, perf_cfg));
dev_dbg(&chan->dev->device, "MRRS: 0x%x\n", FIELD_GET(PERF_MRRS_MASK, perf_cfg));
return SWITCHTEC_DMA_SQ_SIZE;}
static void switchtec_dma_free_chan_resources(struct dma_chan *chan){ struct switchtec_dma_chan *swdma_chan = container_of(chan, struct switchtec_dma_chan, dma_chan);
spin_lock_bh(&swdma_chan->submit_lock); swdma_chan->ring_active = false; spin_unlock_bh(&swdma_chan->submit_lock);
spin_lock_bh(&swdma_chan->complete_lock); swdma_chan->comp_ring_active = false; spin_unlock_bh(&swdma_chan->complete_lock);
switchtec_dma_chan_stop(swdma_chan); switchtec_dma_abort_desc(swdma_chan, 0); switchtec_dma_free_desc(swdma_chan);
disable_channel(swdma_chan);}
static int switchtec_dma_chan_init(struct switchtec_dma_dev *swdma_dev, struct pci_dev *pdev, int i){ struct dma_device *dma = &swdma_dev->dma_dev; struct switchtec_dma_chan *swdma_chan; u32 valid_en_se, thresh; int se_buf_len, irq, rc; struct dma_chan *chan;
swdma_chan = kzalloc_obj(*swdma_chan); if (!swdma_chan) return -ENOMEM;
swdma_chan->phase_tag = 0; swdma_chan->index = i; swdma_chan->swdma_dev = swdma_dev;
spin_lock_init(&swdma_chan->hw_ctrl_lock); spin_lock_init(&swdma_chan->submit_lock); spin_lock_init(&swdma_chan->complete_lock); tasklet_init(&swdma_chan->desc_task, switchtec_dma_desc_task, (unsigned long)swdma_chan);
swdma_chan->mmio_chan_fw = swdma_dev->bar + SWITCHTEC_DMAC_CHAN_CFG_STS_OFFSET + i * SWITCHTEC_DMA_CHAN_FW_REGS_SIZE; swdma_chan->mmio_chan_hw = swdma_dev->bar + SWITCHTEC_DMAC_CHAN_CTRL_OFFSET + i * SWITCHTEC_DMA_CHAN_HW_REGS_SIZE;
swdma_dev->swdma_chans[i] = swdma_chan;
rc = pause_reset_channel(swdma_chan); if (rc) goto free_and_exit;
/* init perf tuner */ writel(FIELD_PREP(PERF_BURST_SCALE_MASK, 1) | FIELD_PREP(PERF_MRRS_MASK, 3) | FIELD_PREP(PERF_BURST_SIZE_MASK, 6) | FIELD_PREP(PERF_ARB_WEIGHT_MASK, 1), &swdma_chan->mmio_chan_fw->perf_cfg);
valid_en_se = readl(&swdma_chan->mmio_chan_fw->valid_en_se);
dev_dbg(&pdev->dev, "Channel %d: SE buffer base %d\n", i, FIELD_GET(SE_BUF_BASE_MASK, valid_en_se));
se_buf_len = FIELD_GET(SE_BUF_LEN_MASK, valid_en_se); dev_dbg(&pdev->dev, "Channel %d: SE buffer count %d\n", i, se_buf_len);
thresh = se_buf_len / 2; valid_en_se |= FIELD_PREP(SE_THRESH_MASK, thresh); writel(valid_en_se, &swdma_chan->mmio_chan_fw->valid_en_se);
/* request irqs */ irq = readl(&swdma_chan->mmio_chan_fw->int_vec); dev_dbg(&pdev->dev, "Channel %d: CE irq vector %d\n", i, irq);
rc = pci_request_irq(pdev, irq, switchtec_dma_isr, NULL, swdma_chan, KBUILD_MODNAME); if (rc) goto free_and_exit;
swdma_chan->irq = irq;
chan = &swdma_chan->dma_chan; chan->device = dma; dma_cookie_init(chan);
list_add_tail(&chan->device_node, &dma->channels);
return 0;
free_and_exit: kfree(swdma_chan); return rc;}
static int switchtec_dma_chan_free(struct pci_dev *pdev, struct switchtec_dma_chan *swdma_chan){ spin_lock_bh(&swdma_chan->submit_lock); swdma_chan->ring_active = false; spin_unlock_bh(&swdma_chan->submit_lock);
spin_lock_bh(&swdma_chan->complete_lock); swdma_chan->comp_ring_active = false; spin_unlock_bh(&swdma_chan->complete_lock);
pci_free_irq(pdev, swdma_chan->irq, swdma_chan); tasklet_kill(&swdma_chan->desc_task);
switchtec_dma_chan_stop(swdma_chan);
return 0;}
static int switchtec_dma_chans_release(struct pci_dev *pdev, struct switchtec_dma_dev *swdma_dev){ int i;
for (i = 0; i < swdma_dev->chan_cnt; i++) switchtec_dma_chan_free(pdev, swdma_dev->swdma_chans[i]);
return 0;}
static int switchtec_dma_chans_enumerate(struct switchtec_dma_dev *swdma_dev, struct pci_dev *pdev, int chan_cnt){ struct dma_device *dma = &swdma_dev->dma_dev; int base, cnt, rc, i;
swdma_dev->swdma_chans = kzalloc_objs(*swdma_dev->swdma_chans, chan_cnt);
if (!swdma_dev->swdma_chans) return -ENOMEM;
base = readw(swdma_dev->bar + SWITCHTEC_REG_SE_BUF_BASE); cnt = readw(swdma_dev->bar + SWITCHTEC_REG_SE_BUF_CNT);
dev_dbg(&pdev->dev, "EP SE buffer base %d\n", base); dev_dbg(&pdev->dev, "EP SE buffer count %d\n", cnt);
INIT_LIST_HEAD(&dma->channels);
for (i = 0; i < chan_cnt; i++) { rc = switchtec_dma_chan_init(swdma_dev, pdev, i); if (rc) { dev_err(&pdev->dev, "Channel %d: init channel failed\n", i); chan_cnt = i; goto err_exit; } }
return chan_cnt;
err_exit: for (i = 0; i < chan_cnt; i++) switchtec_dma_chan_free(pdev, swdma_dev->swdma_chans[i]);
kfree(swdma_dev->swdma_chans);
return rc;}
static void switchtec_dma_release(struct dma_device *dma_dev){ struct switchtec_dma_dev *swdma_dev = container_of(dma_dev, struct switchtec_dma_dev, dma_dev); int i;
for (i = 0; i < swdma_dev->chan_cnt; i++) kfree(swdma_dev->swdma_chans[i]);
kfree(swdma_dev->swdma_chans);
put_device(dma_dev->dev); kfree(swdma_dev);}
static int switchtec_dma_create(struct pci_dev *pdev){ struct switchtec_dma_dev *swdma_dev; int chan_cnt, nr_vecs, irq, rc; struct dma_device *dma; struct dma_chan *chan;
/* * Create the switchtec dma device */ swdma_dev = kzalloc_obj(*swdma_dev); if (!swdma_dev) return -ENOMEM;
swdma_dev->bar = ioremap(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
RCU_INIT_POINTER(swdma_dev->pdev, pdev);
nr_vecs = pci_msix_vec_count(pdev); rc = pci_alloc_irq_vectors(pdev, nr_vecs, nr_vecs, PCI_IRQ_MSIX); if (rc < 0) goto err_exit;
irq = readw(swdma_dev->bar + SWITCHTEC_REG_CHAN_STS_VEC); pci_dbg(pdev, "Channel pause irq vector %d\n", irq);
rc = pci_request_irq(pdev, irq, NULL, switchtec_dma_chan_status_isr, swdma_dev, KBUILD_MODNAME); if (rc) goto err_exit;
swdma_dev->chan_status_irq = irq;
chan_cnt = readl(swdma_dev->bar + SWITCHTEC_REG_CHAN_CNT); if (!chan_cnt) { pci_err(pdev, "No channel configured.\n"); rc = -ENXIO; goto err_exit; }
chan_cnt = switchtec_dma_chans_enumerate(swdma_dev, pdev, chan_cnt); if (chan_cnt < 0) { pci_err(pdev, "Failed to enumerate dma channels: %d\n", chan_cnt); rc = -ENXIO; goto err_exit; }
swdma_dev->chan_cnt = chan_cnt;
dma = &swdma_dev->dma_dev; dma->copy_align = DMAENGINE_ALIGN_8_BYTES; dma_cap_set(DMA_MEMCPY, dma->cap_mask); dma_cap_set(DMA_PRIVATE, dma->cap_mask); dma->dev = get_device(&pdev->dev);
dma->device_alloc_chan_resources = switchtec_dma_alloc_chan_resources; dma->device_free_chan_resources = switchtec_dma_free_chan_resources; dma->device_prep_dma_memcpy = switchtec_dma_prep_memcpy; dma->device_tx_status = switchtec_dma_tx_status; dma->device_issue_pending = switchtec_dma_issue_pending; dma->device_pause = switchtec_dma_pause; dma->device_resume = switchtec_dma_resume; dma->device_terminate_all = switchtec_dma_terminate_all; dma->device_synchronize = switchtec_dma_synchronize; dma->device_release = switchtec_dma_release;
rc = dma_async_device_register(dma); if (rc) { pci_err(pdev, "Failed to register dma device: %d\n", rc); goto err_chans_release_exit; }
pci_dbg(pdev, "Channel count: %d\n", chan_cnt);
list_for_each_entry(chan, &dma->channels, device_node) pci_dbg(pdev, "%s\n", dma_chan_name(chan));
pci_set_drvdata(pdev, swdma_dev);
return 0;
err_chans_release_exit: switchtec_dma_chans_release(pdev, swdma_dev);
err_exit: if (swdma_dev->chan_status_irq) free_irq(swdma_dev->chan_status_irq, swdma_dev);
iounmap(swdma_dev->bar); kfree(swdma_dev); return rc;}
static int switchtec_dma_probe(struct pci_dev *pdev, const struct pci_device_id *id){ int rc;
rc = pci_enable_device(pdev); if (rc) return rc;
dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
rc = pci_request_mem_regions(pdev, KBUILD_MODNAME); if (rc) goto err_disable;
pci_set_master(pdev);
rc = switchtec_dma_create(pdev); if (rc) goto err_free;
return 0;
err_free: pci_free_irq_vectors(pdev); pci_release_mem_regions(pdev);
err_disable: pci_disable_device(pdev);
return rc;}
static void switchtec_dma_remove(struct pci_dev *pdev){ struct switchtec_dma_dev *swdma_dev = pci_get_drvdata(pdev);
switchtec_dma_chans_release(pdev, swdma_dev);
rcu_assign_pointer(swdma_dev->pdev, NULL); synchronize_rcu();
pci_free_irq(pdev, swdma_dev->chan_status_irq, swdma_dev);
pci_free_irq_vectors(pdev);
dma_async_device_unregister(&swdma_dev->dma_dev);
iounmap(swdma_dev->bar); pci_release_mem_regions(pdev); pci_disable_device(pdev);}
/* * Also use the class code to identify the devices, as some of the * device IDs are also used for other devices with other classes by * Microsemi. */#define SW_ID(vendor_id, device_id) \ { \ .vendor = vendor_id, \ .device = device_id, \ .subvendor = PCI_ANY_ID, \ .subdevice = PCI_ANY_ID, \ .class = PCI_CLASS_SYSTEM_OTHER << 8, \ .class_mask = 0xffffffff, \ }
static const struct pci_device_id switchtec_dma_pci_tbl[] = { SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x4000), /* PFX 100XG4 */ SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x4084), /* PFX 84XG4 */ SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x4068), /* PFX 68XG4 */ SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x4052), /* PFX 52XG4 */ SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x4036), /* PFX 36XG4 */ SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x4028), /* PFX 28XG4 */ SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x4100), /* PSX 100XG4 */ SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x4184), /* PSX 84XG4 */ SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x4168), /* PSX 68XG4 */ SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x4152), /* PSX 52XG4 */ SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x4136), /* PSX 36XG4 */ SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x4128), /* PSX 28XG4 */ SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x4352), /* PFXA 52XG4 */ SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x4336), /* PFXA 36XG4 */ SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x4328), /* PFXA 28XG4 */ SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x4452), /* PSXA 52XG4 */ SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x4436), /* PSXA 36XG4 */ SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x4428), /* PSXA 28XG4 */ SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5000), /* PFX 100XG5 */ SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5084), /* PFX 84XG5 */ SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5068), /* PFX 68XG5 */ SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5052), /* PFX 52XG5 */ SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5036), /* PFX 36XG5 */ SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5028), /* PFX 28XG5 */ SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5100), /* PSX 100XG5 */ SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5184), /* PSX 84XG5 */ SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5168), /* PSX 68XG5 */ SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5152), /* PSX 52XG5 */ SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5136), /* PSX 36XG5 */ SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5128), /* PSX 28XG5 */ SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5300), /* PFXA 100XG5 */ SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5384), /* PFXA 84XG5 */ SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5368), /* PFXA 68XG5 */ SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5352), /* PFXA 52XG5 */ SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5336), /* PFXA 36XG5 */ SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5328), /* PFXA 28XG5 */ SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5400), /* PSXA 100XG5 */ SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5484), /* PSXA 84XG5 */ SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5468), /* PSXA 68XG5 */ SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5452), /* PSXA 52XG5 */ SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5436), /* PSXA 36XG5 */ SW_ID(PCI_VENDOR_ID_MICROSEMI, 0x5428), /* PSXA 28XG5 */ SW_ID(PCI_VENDOR_ID_EFAR, 0x1001), /* PCI1001 16XG4 */ SW_ID(PCI_VENDOR_ID_EFAR, 0x1002), /* PCI1002 16XG4 */ SW_ID(PCI_VENDOR_ID_EFAR, 0x1003), /* PCI1003 16XG4 */ SW_ID(PCI_VENDOR_ID_EFAR, 0x1004), /* PCI1004 16XG4 */ SW_ID(PCI_VENDOR_ID_EFAR, 0x1005), /* PCI1005 16XG4 */ SW_ID(PCI_VENDOR_ID_EFAR, 0x1006), /* PCI1006 16XG4 */ SW_ID(PCI_VENDOR_ID_EFAR, 0x1008), /* PCI1008 16XG4 */ {0}};MODULE_DEVICE_TABLE(pci, switchtec_dma_pci_tbl);
static struct pci_driver switchtec_dma_pci_driver = { .name = KBUILD_MODNAME, .id_table = switchtec_dma_pci_tbl, .probe = switchtec_dma_probe, .remove = switchtec_dma_remove,};module_pci_driver(switchtec_dma_pci_driver);