linux/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c
Sergey Matyukevich d1365e794e qtnfmac: fix INTx interrupt handling
In the current implementation INTx interrupt is deasserted after the
control path processing. However this may lead to missed interrupts
from the wireless card. For instance, this may happen as a result
of control path activity, when another interrupt arrives before
INTx is deasserted.

Signed-off-by: Sergey Matyukevich <sergey.matyukevich.os@quantenna.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2019-02-01 14:11:52 +02:00

1220 lines
29 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/* Copyright (c) 2018 Quantenna Communications */
#include <linux/kernel.h>
#include <linux/firmware.h>
#include <linux/pci.h>
#include <linux/vmalloc.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/crc32.h>
#include <linux/completion.h>
#include <linux/spinlock.h>
#include <linux/circ_buf.h>
#include "pcie_priv.h"
#include "topaz_pcie_regs.h"
#include "topaz_pcie_ipc.h"
#include "qtn_hw_ids.h"
#include "core.h"
#include "bus.h"
#include "shm_ipc.h"
#include "debug.h"
#define TOPAZ_TX_BD_SIZE_DEFAULT 128
struct qtnf_topaz_tx_bd {
__le32 addr;
__le32 info;
} __packed;
struct qtnf_topaz_rx_bd {
__le32 addr;
__le32 info;
} __packed;
struct qtnf_extra_bd_params {
__le32 param1;
__le32 param2;
__le32 param3;
__le32 param4;
} __packed;
#define QTNF_BD_PARAM_OFFSET(n) offsetof(struct qtnf_extra_bd_params, param##n)
struct vmac_pkt_info {
__le32 addr;
__le32 info;
};
struct qtnf_topaz_bda {
__le16 bda_len;
__le16 bda_version;
__le32 bda_bootstate;
__le32 bda_dma_mask;
__le32 bda_dma_offset;
__le32 bda_flags;
__le32 bda_img;
__le32 bda_img_size;
__le32 bda_ep2h_irqstatus;
__le32 bda_h2ep_irqstatus;
__le32 bda_msi_addr;
u8 reserved1[56];
__le32 bda_flashsz;
u8 bda_boardname[PCIE_BDA_NAMELEN];
__le32 bda_pci_pre_status;
__le32 bda_pci_endian;
__le32 bda_pci_post_status;
__le32 bda_h2ep_txd_budget;
__le32 bda_ep2h_txd_budget;
__le32 bda_rc_rx_bd_base;
__le32 bda_rc_rx_bd_num;
__le32 bda_rc_tx_bd_base;
__le32 bda_rc_tx_bd_num;
u8 bda_ep_link_state;
u8 bda_rc_link_state;
u8 bda_rc_msi_enabled;
u8 reserved2;
__le32 bda_ep_next_pkt;
struct vmac_pkt_info request[QTN_PCIE_RC_TX_QUEUE_LEN];
struct qtnf_shm_ipc_region bda_shm_reg1 __aligned(4096);
struct qtnf_shm_ipc_region bda_shm_reg2 __aligned(4096);
} __packed;
struct qtnf_pcie_topaz_state {
struct qtnf_pcie_bus_priv base;
struct qtnf_topaz_bda __iomem *bda;
dma_addr_t dma_msi_dummy;
u32 dma_msi_imwr;
struct qtnf_topaz_tx_bd *tx_bd_vbase;
struct qtnf_topaz_rx_bd *rx_bd_vbase;
__le32 __iomem *ep_next_rx_pkt;
__le32 __iomem *txqueue_wake;
__le32 __iomem *ep_pmstate;
unsigned long rx_pkt_count;
};
static void qtnf_deassert_intx(struct qtnf_pcie_topaz_state *ts)
{
void __iomem *reg = ts->base.sysctl_bar + TOPAZ_PCIE_CFG0_OFFSET;
u32 cfg;
cfg = readl(reg);
cfg &= ~TOPAZ_ASSERT_INTX;
qtnf_non_posted_write(cfg, reg);
}
static inline int qtnf_topaz_intx_asserted(struct qtnf_pcie_topaz_state *ts)
{
void __iomem *reg = ts->base.sysctl_bar + TOPAZ_PCIE_CFG0_OFFSET;
u32 cfg = readl(reg);
return !!(cfg & TOPAZ_ASSERT_INTX);
}
static void qtnf_topaz_reset_ep(struct qtnf_pcie_topaz_state *ts)
{
writel(TOPAZ_IPC_IRQ_WORD(TOPAZ_RC_RST_EP_IRQ),
TOPAZ_LH_IPC4_INT(ts->base.sysctl_bar));
msleep(QTN_EP_RESET_WAIT_MS);
pci_restore_state(ts->base.pdev);
}
static void setup_rx_irqs(struct qtnf_pcie_topaz_state *ts)
{
void __iomem *reg = PCIE_DMA_WR_DONE_IMWR_ADDR_LOW(ts->base.dmareg_bar);
ts->dma_msi_imwr = readl(reg);
}
static void enable_rx_irqs(struct qtnf_pcie_topaz_state *ts)
{
void __iomem *reg = PCIE_DMA_WR_DONE_IMWR_ADDR_LOW(ts->base.dmareg_bar);
qtnf_non_posted_write(ts->dma_msi_imwr, reg);
}
static void disable_rx_irqs(struct qtnf_pcie_topaz_state *ts)
{
void __iomem *reg = PCIE_DMA_WR_DONE_IMWR_ADDR_LOW(ts->base.dmareg_bar);
qtnf_non_posted_write(QTN_HOST_LO32(ts->dma_msi_dummy), reg);
}
static void qtnf_topaz_ipc_gen_ep_int(void *arg)
{
struct qtnf_pcie_topaz_state *ts = arg;
writel(TOPAZ_IPC_IRQ_WORD(TOPAZ_RC_CTRL_IRQ),
TOPAZ_CTL_M2L_INT(ts->base.sysctl_bar));
}
static int qtnf_is_state(__le32 __iomem *reg, u32 state)
{
u32 s = readl(reg);
return (s == state);
}
static void qtnf_set_state(__le32 __iomem *reg, u32 state)
{
qtnf_non_posted_write(state, reg);
}
static int qtnf_poll_state(__le32 __iomem *reg, u32 state, u32 delay_in_ms)
{
u32 timeout = 0;
while ((qtnf_is_state(reg, state) == 0)) {
usleep_range(1000, 1200);
if (++timeout > delay_in_ms)
return -1;
}
return 0;
}
static int topaz_alloc_bd_table(struct qtnf_pcie_topaz_state *ts,
struct qtnf_topaz_bda __iomem *bda)
{
struct qtnf_extra_bd_params __iomem *extra_params;
struct qtnf_pcie_bus_priv *priv = &ts->base;
dma_addr_t paddr;
void *vaddr;
int len;
int i;
/* bd table */
len = priv->tx_bd_num * sizeof(struct qtnf_topaz_tx_bd) +
priv->rx_bd_num * sizeof(struct qtnf_topaz_rx_bd) +
sizeof(struct qtnf_extra_bd_params);
vaddr = dmam_alloc_coherent(&priv->pdev->dev, len, &paddr, GFP_KERNEL);
if (!vaddr)
return -ENOMEM;
memset(vaddr, 0, len);
/* tx bd */
ts->tx_bd_vbase = vaddr;
qtnf_non_posted_write(paddr, &bda->bda_rc_tx_bd_base);
for (i = 0; i < priv->tx_bd_num; i++)
ts->tx_bd_vbase[i].info |= cpu_to_le32(QTN_BD_EMPTY);
pr_debug("TX descriptor table: vaddr=0x%p paddr=%pad\n", vaddr, &paddr);
priv->tx_bd_r_index = 0;
priv->tx_bd_w_index = 0;
/* rx bd */
vaddr = ((struct qtnf_topaz_tx_bd *)vaddr) + priv->tx_bd_num;
paddr += priv->tx_bd_num * sizeof(struct qtnf_topaz_tx_bd);
ts->rx_bd_vbase = vaddr;
qtnf_non_posted_write(paddr, &bda->bda_rc_rx_bd_base);
pr_debug("RX descriptor table: vaddr=0x%p paddr=%pad\n", vaddr, &paddr);
/* extra shared params */
vaddr = ((struct qtnf_topaz_rx_bd *)vaddr) + priv->rx_bd_num;
paddr += priv->rx_bd_num * sizeof(struct qtnf_topaz_rx_bd);
extra_params = (struct qtnf_extra_bd_params __iomem *)vaddr;
ts->ep_next_rx_pkt = &extra_params->param1;
qtnf_non_posted_write(paddr + QTNF_BD_PARAM_OFFSET(1),
&bda->bda_ep_next_pkt);
ts->txqueue_wake = &extra_params->param2;
ts->ep_pmstate = &extra_params->param3;
ts->dma_msi_dummy = paddr + QTNF_BD_PARAM_OFFSET(4);
return 0;
}
static int
topaz_skb2rbd_attach(struct qtnf_pcie_topaz_state *ts, u16 index, u32 wrap)
{
struct qtnf_topaz_rx_bd *rxbd = &ts->rx_bd_vbase[index];
struct sk_buff *skb;
dma_addr_t paddr;
skb = __netdev_alloc_skb_ip_align(NULL, SKB_BUF_SIZE, GFP_ATOMIC);
if (!skb) {
ts->base.rx_skb[index] = NULL;
return -ENOMEM;
}
ts->base.rx_skb[index] = skb;
paddr = pci_map_single(ts->base.pdev, skb->data,
SKB_BUF_SIZE, PCI_DMA_FROMDEVICE);
if (pci_dma_mapping_error(ts->base.pdev, paddr)) {
pr_err("skb mapping error: %pad\n", &paddr);
return -ENOMEM;
}
rxbd->addr = cpu_to_le32(QTN_HOST_LO32(paddr));
rxbd->info = cpu_to_le32(QTN_BD_EMPTY | wrap);
ts->base.rx_bd_w_index = index;
return 0;
}
static int topaz_alloc_rx_buffers(struct qtnf_pcie_topaz_state *ts)
{
u16 i;
int ret = 0;
memset(ts->rx_bd_vbase, 0x0,
ts->base.rx_bd_num * sizeof(struct qtnf_topaz_rx_bd));
for (i = 0; i < ts->base.rx_bd_num; i++) {
ret = topaz_skb2rbd_attach(ts, i, 0);
if (ret)
break;
}
ts->rx_bd_vbase[ts->base.rx_bd_num - 1].info |=
cpu_to_le32(QTN_BD_WRAP);
return ret;
}
/* all rx/tx activity should have ceased before calling this function */
static void qtnf_topaz_free_xfer_buffers(struct qtnf_pcie_topaz_state *ts)
{
struct qtnf_pcie_bus_priv *priv = &ts->base;
struct qtnf_topaz_rx_bd *rxbd;
struct qtnf_topaz_tx_bd *txbd;
struct sk_buff *skb;
dma_addr_t paddr;
int i;
/* free rx buffers */
for (i = 0; i < priv->rx_bd_num; i++) {
if (priv->rx_skb && priv->rx_skb[i]) {
rxbd = &ts->rx_bd_vbase[i];
skb = priv->rx_skb[i];
paddr = QTN_HOST_ADDR(0x0, le32_to_cpu(rxbd->addr));
pci_unmap_single(priv->pdev, paddr, SKB_BUF_SIZE,
PCI_DMA_FROMDEVICE);
dev_kfree_skb_any(skb);
priv->rx_skb[i] = NULL;
rxbd->addr = 0;
rxbd->info = 0;
}
}
/* free tx buffers */
for (i = 0; i < priv->tx_bd_num; i++) {
if (priv->tx_skb && priv->tx_skb[i]) {
txbd = &ts->tx_bd_vbase[i];
skb = priv->tx_skb[i];
paddr = QTN_HOST_ADDR(0x0, le32_to_cpu(txbd->addr));
pci_unmap_single(priv->pdev, paddr, SKB_BUF_SIZE,
PCI_DMA_TODEVICE);
dev_kfree_skb_any(skb);
priv->tx_skb[i] = NULL;
txbd->addr = 0;
txbd->info = 0;
}
}
}
static int qtnf_pcie_topaz_init_xfer(struct qtnf_pcie_topaz_state *ts,
unsigned int tx_bd_size)
{
struct qtnf_topaz_bda __iomem *bda = ts->bda;
struct qtnf_pcie_bus_priv *priv = &ts->base;
int ret;
if (tx_bd_size == 0)
tx_bd_size = TOPAZ_TX_BD_SIZE_DEFAULT;
/* check TX BD queue max length according to struct qtnf_topaz_bda */
if (tx_bd_size > QTN_PCIE_RC_TX_QUEUE_LEN) {
pr_warn("TX BD queue cannot exceed %d\n",
QTN_PCIE_RC_TX_QUEUE_LEN);
tx_bd_size = QTN_PCIE_RC_TX_QUEUE_LEN;
}
priv->tx_bd_num = tx_bd_size;
qtnf_non_posted_write(priv->tx_bd_num, &bda->bda_rc_tx_bd_num);
qtnf_non_posted_write(priv->rx_bd_num, &bda->bda_rc_rx_bd_num);
priv->rx_bd_w_index = 0;
priv->rx_bd_r_index = 0;
ret = qtnf_pcie_alloc_skb_array(priv);
if (ret) {
pr_err("failed to allocate skb array\n");
return ret;
}
ret = topaz_alloc_bd_table(ts, bda);
if (ret) {
pr_err("failed to allocate bd table\n");
return ret;
}
ret = topaz_alloc_rx_buffers(ts);
if (ret) {
pr_err("failed to allocate rx buffers\n");
return ret;
}
return ret;
}
static void qtnf_topaz_data_tx_reclaim(struct qtnf_pcie_topaz_state *ts)
{
struct qtnf_pcie_bus_priv *priv = &ts->base;
struct qtnf_topaz_tx_bd *txbd;
struct sk_buff *skb;
unsigned long flags;
dma_addr_t paddr;
u32 tx_done_index;
int count = 0;
int i;
spin_lock_irqsave(&priv->tx_reclaim_lock, flags);
tx_done_index = readl(ts->ep_next_rx_pkt);
i = priv->tx_bd_r_index;
if (CIRC_CNT(priv->tx_bd_w_index, tx_done_index, priv->tx_bd_num))
writel(TOPAZ_IPC_IRQ_WORD(TOPAZ_RC_TX_DONE_IRQ),
TOPAZ_LH_IPC4_INT(priv->sysctl_bar));
while (CIRC_CNT(tx_done_index, i, priv->tx_bd_num)) {
skb = priv->tx_skb[i];
if (likely(skb)) {
txbd = &ts->tx_bd_vbase[i];
paddr = QTN_HOST_ADDR(0x0, le32_to_cpu(txbd->addr));
pci_unmap_single(priv->pdev, paddr, skb->len,
PCI_DMA_TODEVICE);
if (skb->dev) {
qtnf_update_tx_stats(skb->dev, skb);
if (unlikely(priv->tx_stopped)) {
qtnf_wake_all_queues(skb->dev);
priv->tx_stopped = 0;
}
}
dev_kfree_skb_any(skb);
}
priv->tx_skb[i] = NULL;
count++;
if (++i >= priv->tx_bd_num)
i = 0;
}
priv->tx_reclaim_done += count;
priv->tx_reclaim_req++;
priv->tx_bd_r_index = i;
spin_unlock_irqrestore(&priv->tx_reclaim_lock, flags);
}
static void qtnf_try_stop_xmit(struct qtnf_bus *bus, struct net_device *ndev)
{
struct qtnf_pcie_topaz_state *ts = (void *)get_bus_priv(bus);
if (ndev) {
netif_tx_stop_all_queues(ndev);
ts->base.tx_stopped = 1;
}
writel(0x0, ts->txqueue_wake);
/* sync up tx queue status before generating interrupt */
dma_wmb();
/* send irq to card: tx stopped */
writel(TOPAZ_IPC_IRQ_WORD(TOPAZ_RC_TX_STOP_IRQ),
TOPAZ_LH_IPC4_INT(ts->base.sysctl_bar));
/* schedule reclaim attempt */
tasklet_hi_schedule(&ts->base.reclaim_tq);
}
static void qtnf_try_wake_xmit(struct qtnf_bus *bus, struct net_device *ndev)
{
struct qtnf_pcie_topaz_state *ts = get_bus_priv(bus);
int ready;
ready = readl(ts->txqueue_wake);
if (ready) {
netif_wake_queue(ndev);
} else {
/* re-send irq to card: tx stopped */
writel(TOPAZ_IPC_IRQ_WORD(TOPAZ_RC_TX_STOP_IRQ),
TOPAZ_LH_IPC4_INT(ts->base.sysctl_bar));
}
}
static int qtnf_tx_queue_ready(struct qtnf_pcie_topaz_state *ts)
{
struct qtnf_pcie_bus_priv *priv = &ts->base;
if (!CIRC_SPACE(priv->tx_bd_w_index, priv->tx_bd_r_index,
priv->tx_bd_num)) {
qtnf_topaz_data_tx_reclaim(ts);
if (!CIRC_SPACE(priv->tx_bd_w_index, priv->tx_bd_r_index,
priv->tx_bd_num)) {
priv->tx_full_count++;
return 0;
}
}
return 1;
}
static int qtnf_pcie_data_tx(struct qtnf_bus *bus, struct sk_buff *skb)
{
struct qtnf_pcie_topaz_state *ts = (void *)get_bus_priv(bus);
struct qtnf_pcie_bus_priv *priv = &ts->base;
struct qtnf_topaz_bda __iomem *bda = ts->bda;
struct qtnf_topaz_tx_bd *txbd;
dma_addr_t skb_paddr;
unsigned long flags;
int ret = 0;
int len;
int i;
spin_lock_irqsave(&priv->tx_lock, flags);
if (!qtnf_tx_queue_ready(ts)) {
qtnf_try_stop_xmit(bus, skb->dev);
spin_unlock_irqrestore(&priv->tx_lock, flags);
return NETDEV_TX_BUSY;
}
i = priv->tx_bd_w_index;
priv->tx_skb[i] = skb;
len = skb->len;
skb_paddr = pci_map_single(priv->pdev, skb->data,
skb->len, PCI_DMA_TODEVICE);
if (pci_dma_mapping_error(priv->pdev, skb_paddr)) {
ret = -ENOMEM;
goto tx_done;
}
txbd = &ts->tx_bd_vbase[i];
txbd->addr = cpu_to_le32(QTN_HOST_LO32(skb_paddr));
writel(QTN_HOST_LO32(skb_paddr), &bda->request[i].addr);
writel(len | QTN_PCIE_TX_VALID_PKT, &bda->request[i].info);
/* sync up descriptor updates before generating interrupt */
dma_wmb();
/* generate irq to card: tx done */
writel(TOPAZ_IPC_IRQ_WORD(TOPAZ_RC_TX_DONE_IRQ),
TOPAZ_LH_IPC4_INT(priv->sysctl_bar));
if (++i >= priv->tx_bd_num)
i = 0;
priv->tx_bd_w_index = i;
tx_done:
if (ret) {
if (skb->dev)
skb->dev->stats.tx_dropped++;
dev_kfree_skb_any(skb);
}
priv->tx_done_count++;
spin_unlock_irqrestore(&priv->tx_lock, flags);
qtnf_topaz_data_tx_reclaim(ts);
return NETDEV_TX_OK;
}
static irqreturn_t qtnf_pcie_topaz_interrupt(int irq, void *data)
{
struct qtnf_bus *bus = (struct qtnf_bus *)data;
struct qtnf_pcie_topaz_state *ts = (void *)get_bus_priv(bus);
struct qtnf_pcie_bus_priv *priv = &ts->base;
if (!priv->msi_enabled && !qtnf_topaz_intx_asserted(ts))
return IRQ_NONE;
if (!priv->msi_enabled)
qtnf_deassert_intx(ts);
priv->pcie_irq_count++;
qtnf_shm_ipc_irq_handler(&priv->shm_ipc_ep_in);
qtnf_shm_ipc_irq_handler(&priv->shm_ipc_ep_out);
if (napi_schedule_prep(&bus->mux_napi)) {
disable_rx_irqs(ts);
__napi_schedule(&bus->mux_napi);
}
tasklet_hi_schedule(&priv->reclaim_tq);
return IRQ_HANDLED;
}
static int qtnf_rx_data_ready(struct qtnf_pcie_topaz_state *ts)
{
u16 index = ts->base.rx_bd_r_index;
struct qtnf_topaz_rx_bd *rxbd;
u32 descw;
rxbd = &ts->rx_bd_vbase[index];
descw = le32_to_cpu(rxbd->info);
if (descw & QTN_BD_EMPTY)
return 0;
return 1;
}
static int qtnf_topaz_rx_poll(struct napi_struct *napi, int budget)
{
struct qtnf_bus *bus = container_of(napi, struct qtnf_bus, mux_napi);
struct qtnf_pcie_topaz_state *ts = (void *)get_bus_priv(bus);
struct qtnf_pcie_bus_priv *priv = &ts->base;
struct net_device *ndev = NULL;
struct sk_buff *skb = NULL;
int processed = 0;
struct qtnf_topaz_rx_bd *rxbd;
dma_addr_t skb_paddr;
int consume;
u32 descw;
u32 poffset;
u32 psize;
u16 r_idx;
u16 w_idx;
int ret;
while (processed < budget) {
if (!qtnf_rx_data_ready(ts))
goto rx_out;
r_idx = priv->rx_bd_r_index;
rxbd = &ts->rx_bd_vbase[r_idx];
descw = le32_to_cpu(rxbd->info);
skb = priv->rx_skb[r_idx];
poffset = QTN_GET_OFFSET(descw);
psize = QTN_GET_LEN(descw);
consume = 1;
if (descw & QTN_BD_EMPTY) {
pr_warn("skip invalid rxbd[%d]\n", r_idx);
consume = 0;
}
if (!skb) {
pr_warn("skip missing rx_skb[%d]\n", r_idx);
consume = 0;
}
if (skb && (skb_tailroom(skb) < psize)) {
pr_err("skip packet with invalid length: %u > %u\n",
psize, skb_tailroom(skb));
consume = 0;
}
if (skb) {
skb_paddr = QTN_HOST_ADDR(0x0, le32_to_cpu(rxbd->addr));
pci_unmap_single(priv->pdev, skb_paddr, SKB_BUF_SIZE,
PCI_DMA_FROMDEVICE);
}
if (consume) {
skb_reserve(skb, poffset);
skb_put(skb, psize);
ndev = qtnf_classify_skb(bus, skb);
if (likely(ndev)) {
qtnf_update_rx_stats(ndev, skb);
skb->protocol = eth_type_trans(skb, ndev);
netif_receive_skb(skb);
} else {
pr_debug("drop untagged skb\n");
bus->mux_dev.stats.rx_dropped++;
dev_kfree_skb_any(skb);
}
} else {
if (skb) {
bus->mux_dev.stats.rx_dropped++;
dev_kfree_skb_any(skb);
}
}
/* notify card about recv packets once per several packets */
if (((++ts->rx_pkt_count) & RX_DONE_INTR_MSK) == 0)
writel(TOPAZ_IPC_IRQ_WORD(TOPAZ_RC_RX_DONE_IRQ),
TOPAZ_LH_IPC4_INT(priv->sysctl_bar));
priv->rx_skb[r_idx] = NULL;
if (++r_idx >= priv->rx_bd_num)
r_idx = 0;
priv->rx_bd_r_index = r_idx;
/* repalce processed buffer by a new one */
w_idx = priv->rx_bd_w_index;
while (CIRC_SPACE(priv->rx_bd_w_index, priv->rx_bd_r_index,
priv->rx_bd_num) > 0) {
if (++w_idx >= priv->rx_bd_num)
w_idx = 0;
ret = topaz_skb2rbd_attach(ts, w_idx,
descw & QTN_BD_WRAP);
if (ret) {
pr_err("failed to allocate new rx_skb[%d]\n",
w_idx);
break;
}
}
processed++;
}
rx_out:
if (processed < budget) {
napi_complete(napi);
enable_rx_irqs(ts);
}
return processed;
}
static void
qtnf_pcie_data_tx_timeout(struct qtnf_bus *bus, struct net_device *ndev)
{
struct qtnf_pcie_topaz_state *ts = get_bus_priv(bus);
qtnf_try_wake_xmit(bus, ndev);
tasklet_hi_schedule(&ts->base.reclaim_tq);
}
static void qtnf_pcie_data_rx_start(struct qtnf_bus *bus)
{
struct qtnf_pcie_topaz_state *ts = get_bus_priv(bus);
napi_enable(&bus->mux_napi);
enable_rx_irqs(ts);
}
static void qtnf_pcie_data_rx_stop(struct qtnf_bus *bus)
{
struct qtnf_pcie_topaz_state *ts = get_bus_priv(bus);
disable_rx_irqs(ts);
napi_disable(&bus->mux_napi);
}
static const struct qtnf_bus_ops qtnf_pcie_topaz_bus_ops = {
/* control path methods */
.control_tx = qtnf_pcie_control_tx,
/* data path methods */
.data_tx = qtnf_pcie_data_tx,
.data_tx_timeout = qtnf_pcie_data_tx_timeout,
.data_rx_start = qtnf_pcie_data_rx_start,
.data_rx_stop = qtnf_pcie_data_rx_stop,
};
static int qtnf_dbg_irq_stats(struct seq_file *s, void *data)
{
struct qtnf_bus *bus = dev_get_drvdata(s->private);
struct qtnf_pcie_topaz_state *ts = get_bus_priv(bus);
seq_printf(s, "pcie_irq_count(%u)\n", ts->base.pcie_irq_count);
return 0;
}
static int qtnf_dbg_pkt_stats(struct seq_file *s, void *data)
{
struct qtnf_bus *bus = dev_get_drvdata(s->private);
struct qtnf_pcie_topaz_state *ts = get_bus_priv(bus);
struct qtnf_pcie_bus_priv *priv = &ts->base;
u32 tx_done_index = readl(ts->ep_next_rx_pkt);
seq_printf(s, "tx_full_count(%u)\n", priv->tx_full_count);
seq_printf(s, "tx_done_count(%u)\n", priv->tx_done_count);
seq_printf(s, "tx_reclaim_done(%u)\n", priv->tx_reclaim_done);
seq_printf(s, "tx_reclaim_req(%u)\n", priv->tx_reclaim_req);
seq_printf(s, "tx_bd_r_index(%u)\n", priv->tx_bd_r_index);
seq_printf(s, "tx_done_index(%u)\n", tx_done_index);
seq_printf(s, "tx_bd_w_index(%u)\n", priv->tx_bd_w_index);
seq_printf(s, "tx host queue len(%u)\n",
CIRC_CNT(priv->tx_bd_w_index, priv->tx_bd_r_index,
priv->tx_bd_num));
seq_printf(s, "tx reclaim queue len(%u)\n",
CIRC_CNT(tx_done_index, priv->tx_bd_r_index,
priv->tx_bd_num));
seq_printf(s, "tx card queue len(%u)\n",
CIRC_CNT(priv->tx_bd_w_index, tx_done_index,
priv->tx_bd_num));
seq_printf(s, "rx_bd_r_index(%u)\n", priv->rx_bd_r_index);
seq_printf(s, "rx_bd_w_index(%u)\n", priv->rx_bd_w_index);
seq_printf(s, "rx alloc queue len(%u)\n",
CIRC_SPACE(priv->rx_bd_w_index, priv->rx_bd_r_index,
priv->rx_bd_num));
return 0;
}
static void qtnf_reset_dma_offset(struct qtnf_pcie_topaz_state *ts)
{
struct qtnf_topaz_bda __iomem *bda = ts->bda;
u32 offset = readl(&bda->bda_dma_offset);
if ((offset & PCIE_DMA_OFFSET_ERROR_MASK) != PCIE_DMA_OFFSET_ERROR)
return;
writel(0x0, &bda->bda_dma_offset);
}
static int qtnf_pcie_endian_detect(struct qtnf_pcie_topaz_state *ts)
{
struct qtnf_topaz_bda __iomem *bda = ts->bda;
u32 timeout = 0;
u32 endian;
int ret = 0;
writel(QTN_PCI_ENDIAN_DETECT_DATA, &bda->bda_pci_endian);
/* flush endian modifications before status update */
dma_wmb();
writel(QTN_PCI_ENDIAN_VALID_STATUS, &bda->bda_pci_pre_status);
while (readl(&bda->bda_pci_post_status) !=
QTN_PCI_ENDIAN_VALID_STATUS) {
usleep_range(1000, 1200);
if (++timeout > QTN_FW_DL_TIMEOUT_MS) {
pr_err("card endianness detection timed out\n");
ret = -ETIMEDOUT;
goto endian_out;
}
}
/* do not read before status is updated */
dma_rmb();
endian = readl(&bda->bda_pci_endian);
WARN(endian != QTN_PCI_LITTLE_ENDIAN,
"%s: unexpected card endianness", __func__);
endian_out:
writel(0, &bda->bda_pci_pre_status);
writel(0, &bda->bda_pci_post_status);
writel(0, &bda->bda_pci_endian);
return ret;
}
static int qtnf_pre_init_ep(struct qtnf_bus *bus)
{
struct qtnf_pcie_topaz_state *ts = (void *)get_bus_priv(bus);
struct qtnf_topaz_bda __iomem *bda = ts->bda;
u32 flags;
int ret;
ret = qtnf_pcie_endian_detect(ts);
if (ret < 0) {
pr_err("failed to detect card endianness\n");
return ret;
}
writeb(ts->base.msi_enabled, &ts->bda->bda_rc_msi_enabled);
qtnf_reset_dma_offset(ts);
/* notify card about driver type and boot mode */
flags = readl(&bda->bda_flags) | QTN_BDA_HOST_QLINK_DRV;
if (ts->base.flashboot)
flags |= QTN_BDA_FLASH_BOOT;
else
flags &= ~QTN_BDA_FLASH_BOOT;
writel(flags, &bda->bda_flags);
qtnf_set_state(&ts->bda->bda_bootstate, QTN_BDA_FW_HOST_RDY);
if (qtnf_poll_state(&ts->bda->bda_bootstate, QTN_BDA_FW_TARGET_RDY,
QTN_FW_DL_TIMEOUT_MS)) {
pr_err("card is not ready to boot...\n");
return -ETIMEDOUT;
}
return ret;
}
static int qtnf_post_init_ep(struct qtnf_pcie_topaz_state *ts)
{
struct pci_dev *pdev = ts->base.pdev;
setup_rx_irqs(ts);
disable_rx_irqs(ts);
if (qtnf_poll_state(&ts->bda->bda_bootstate, QTN_BDA_FW_QLINK_DONE,
QTN_FW_QLINK_TIMEOUT_MS))
return -ETIMEDOUT;
enable_irq(pdev->irq);
return 0;
}
static int
qtnf_ep_fw_load(struct qtnf_pcie_topaz_state *ts, const u8 *fw, u32 fw_size)
{
struct qtnf_topaz_bda __iomem *bda = ts->bda;
struct pci_dev *pdev = ts->base.pdev;
u32 remaining = fw_size;
u8 *curr = (u8 *)fw;
u32 blksize;
u32 nblocks;
u32 offset;
u32 count;
u32 size;
dma_addr_t paddr;
void *data;
int ret = 0;
pr_debug("FW upload started: fw_addr = 0x%p, size=%d\n", fw, fw_size);
blksize = ts->base.fw_blksize;
if (blksize < PAGE_SIZE)
blksize = PAGE_SIZE;
while (blksize >= PAGE_SIZE) {
pr_debug("allocating %u bytes to upload FW\n", blksize);
data = dma_alloc_coherent(&pdev->dev, blksize,
&paddr, GFP_KERNEL);
if (data)
break;
blksize /= 2;
}
if (!data) {
pr_err("failed to allocate DMA buffer for FW upload\n");
ret = -ENOMEM;
goto fw_load_out;
}
nblocks = NBLOCKS(fw_size, blksize);
offset = readl(&bda->bda_dma_offset);
qtnf_set_state(&ts->bda->bda_bootstate, QTN_BDA_FW_HOST_LOAD);
if (qtnf_poll_state(&ts->bda->bda_bootstate, QTN_BDA_FW_EP_RDY,
QTN_FW_DL_TIMEOUT_MS)) {
pr_err("card is not ready to download FW\n");
ret = -ETIMEDOUT;
goto fw_load_map;
}
for (count = 0 ; count < nblocks; count++) {
size = (remaining > blksize) ? blksize : remaining;
memcpy(data, curr, size);
qtnf_non_posted_write(paddr + offset, &bda->bda_img);
qtnf_non_posted_write(size, &bda->bda_img_size);
pr_debug("chunk[%u] VA[0x%p] PA[%pad] sz[%u]\n",
count, (void *)curr, &paddr, size);
qtnf_set_state(&ts->bda->bda_bootstate, QTN_BDA_FW_BLOCK_RDY);
if (qtnf_poll_state(&ts->bda->bda_bootstate,
QTN_BDA_FW_BLOCK_DONE,
QTN_FW_DL_TIMEOUT_MS)) {
pr_err("confirmation for block #%d timed out\n", count);
ret = -ETIMEDOUT;
goto fw_load_map;
}
remaining = (remaining < size) ? remaining : (remaining - size);
curr += size;
}
/* upload completion mark: zero-sized block */
qtnf_non_posted_write(0, &bda->bda_img);
qtnf_non_posted_write(0, &bda->bda_img_size);
qtnf_set_state(&ts->bda->bda_bootstate, QTN_BDA_FW_BLOCK_RDY);
if (qtnf_poll_state(&ts->bda->bda_bootstate, QTN_BDA_FW_BLOCK_DONE,
QTN_FW_DL_TIMEOUT_MS)) {
pr_err("confirmation for the last block timed out\n");
ret = -ETIMEDOUT;
goto fw_load_map;
}
/* RC is done */
qtnf_set_state(&ts->bda->bda_bootstate, QTN_BDA_FW_BLOCK_END);
if (qtnf_poll_state(&ts->bda->bda_bootstate, QTN_BDA_FW_LOAD_DONE,
QTN_FW_DL_TIMEOUT_MS)) {
pr_err("confirmation for FW upload completion timed out\n");
ret = -ETIMEDOUT;
goto fw_load_map;
}
pr_debug("FW upload completed: totally sent %d blocks\n", count);
fw_load_map:
dma_free_coherent(&pdev->dev, blksize, data, paddr);
fw_load_out:
return ret;
}
static int qtnf_topaz_fw_upload(struct qtnf_pcie_topaz_state *ts,
const char *fwname)
{
const struct firmware *fw;
struct pci_dev *pdev = ts->base.pdev;
int ret;
if (qtnf_poll_state(&ts->bda->bda_bootstate,
QTN_BDA_FW_LOAD_RDY,
QTN_FW_DL_TIMEOUT_MS)) {
pr_err("%s: card is not ready\n", fwname);
return -1;
}
pr_info("starting firmware upload: %s\n", fwname);
ret = request_firmware(&fw, fwname, &pdev->dev);
if (ret < 0) {
pr_err("%s: request_firmware error %d\n", fwname, ret);
return -1;
}
ret = qtnf_ep_fw_load(ts, fw->data, fw->size);
release_firmware(fw);
if (ret)
pr_err("%s: FW upload error\n", fwname);
return ret;
}
static void qtnf_topaz_fw_work_handler(struct work_struct *work)
{
struct qtnf_bus *bus = container_of(work, struct qtnf_bus, fw_work);
struct qtnf_pcie_topaz_state *ts = (void *)get_bus_priv(bus);
int ret;
int bootloader_needed = readl(&ts->bda->bda_flags) & QTN_BDA_XMIT_UBOOT;
qtnf_set_state(&ts->bda->bda_bootstate, QTN_BDA_FW_TARGET_BOOT);
if (bootloader_needed) {
ret = qtnf_topaz_fw_upload(ts, QTN_PCI_TOPAZ_BOOTLD_NAME);
if (ret)
goto fw_load_exit;
ret = qtnf_pre_init_ep(bus);
if (ret)
goto fw_load_exit;
qtnf_set_state(&ts->bda->bda_bootstate,
QTN_BDA_FW_TARGET_BOOT);
}
if (ts->base.flashboot) {
pr_info("booting firmware from flash\n");
ret = qtnf_poll_state(&ts->bda->bda_bootstate,
QTN_BDA_FW_FLASH_BOOT,
QTN_FW_DL_TIMEOUT_MS);
if (ret)
goto fw_load_exit;
} else {
ret = qtnf_topaz_fw_upload(ts, QTN_PCI_TOPAZ_FW_NAME);
if (ret)
goto fw_load_exit;
qtnf_set_state(&ts->bda->bda_bootstate, QTN_BDA_FW_START);
ret = qtnf_poll_state(&ts->bda->bda_bootstate,
QTN_BDA_FW_CONFIG,
QTN_FW_QLINK_TIMEOUT_MS);
if (ret) {
pr_err("FW bringup timed out\n");
goto fw_load_exit;
}
qtnf_set_state(&ts->bda->bda_bootstate, QTN_BDA_FW_RUN);
ret = qtnf_poll_state(&ts->bda->bda_bootstate,
QTN_BDA_FW_RUNNING,
QTN_FW_QLINK_TIMEOUT_MS);
if (ret) {
pr_err("card bringup timed out\n");
goto fw_load_exit;
}
}
pr_info("firmware is up and running\n");
ret = qtnf_post_init_ep(ts);
if (ret)
pr_err("FW runtime failure\n");
fw_load_exit:
qtnf_pcie_fw_boot_done(bus, ret ? false : true);
if (ret == 0) {
qtnf_debugfs_add_entry(bus, "pkt_stats", qtnf_dbg_pkt_stats);
qtnf_debugfs_add_entry(bus, "irq_stats", qtnf_dbg_irq_stats);
}
}
static void qtnf_reclaim_tasklet_fn(unsigned long data)
{
struct qtnf_pcie_topaz_state *ts = (void *)data;
qtnf_topaz_data_tx_reclaim(ts);
}
static u64 qtnf_topaz_dma_mask_get(void)
{
return DMA_BIT_MASK(32);
}
static int qtnf_pcie_topaz_probe(struct qtnf_bus *bus, unsigned int tx_bd_num)
{
struct qtnf_pcie_topaz_state *ts = get_bus_priv(bus);
struct pci_dev *pdev = ts->base.pdev;
struct qtnf_shm_ipc_int ipc_int;
unsigned long irqflags;
int ret;
bus->bus_ops = &qtnf_pcie_topaz_bus_ops;
INIT_WORK(&bus->fw_work, qtnf_topaz_fw_work_handler);
ts->bda = ts->base.epmem_bar;
/* assign host msi irq before card init */
if (ts->base.msi_enabled)
irqflags = IRQF_NOBALANCING;
else
irqflags = IRQF_NOBALANCING | IRQF_SHARED;
ret = devm_request_irq(&pdev->dev, pdev->irq,
&qtnf_pcie_topaz_interrupt,
irqflags, "qtnf_topaz_irq", (void *)bus);
if (ret) {
pr_err("failed to request pcie irq %d\n", pdev->irq);
return ret;
}
disable_irq(pdev->irq);
ret = qtnf_pre_init_ep(bus);
if (ret) {
pr_err("failed to init card\n");
return ret;
}
ret = qtnf_pcie_topaz_init_xfer(ts, tx_bd_num);
if (ret) {
pr_err("PCIE xfer init failed\n");
return ret;
}
tasklet_init(&ts->base.reclaim_tq, qtnf_reclaim_tasklet_fn,
(unsigned long)ts);
netif_napi_add(&bus->mux_dev, &bus->mux_napi,
qtnf_topaz_rx_poll, 10);
ipc_int.fn = qtnf_topaz_ipc_gen_ep_int;
ipc_int.arg = ts;
qtnf_pcie_init_shm_ipc(&ts->base, &ts->bda->bda_shm_reg1,
&ts->bda->bda_shm_reg2, &ipc_int);
return 0;
}
static void qtnf_pcie_topaz_remove(struct qtnf_bus *bus)
{
struct qtnf_pcie_topaz_state *ts = get_bus_priv(bus);
qtnf_topaz_reset_ep(ts);
qtnf_topaz_free_xfer_buffers(ts);
}
#ifdef CONFIG_PM_SLEEP
static int qtnf_pcie_topaz_suspend(struct qtnf_bus *bus)
{
struct qtnf_pcie_topaz_state *ts = get_bus_priv(bus);
struct pci_dev *pdev = ts->base.pdev;
writel((u32 __force)PCI_D3hot, ts->ep_pmstate);
dma_wmb();
writel(TOPAZ_IPC_IRQ_WORD(TOPAZ_RC_PM_EP_IRQ),
TOPAZ_LH_IPC4_INT(ts->base.sysctl_bar));
pci_save_state(pdev);
pci_enable_wake(pdev, PCI_D3hot, 1);
pci_set_power_state(pdev, PCI_D3hot);
return 0;
}
static int qtnf_pcie_topaz_resume(struct qtnf_bus *bus)
{
struct qtnf_pcie_topaz_state *ts = get_bus_priv(bus);
struct pci_dev *pdev = ts->base.pdev;
pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);
pci_enable_wake(pdev, PCI_D0, 0);
writel((u32 __force)PCI_D0, ts->ep_pmstate);
dma_wmb();
writel(TOPAZ_IPC_IRQ_WORD(TOPAZ_RC_PM_EP_IRQ),
TOPAZ_LH_IPC4_INT(ts->base.sysctl_bar));
return 0;
}
#endif
struct qtnf_bus *qtnf_pcie_topaz_alloc(struct pci_dev *pdev)
{
struct qtnf_bus *bus;
struct qtnf_pcie_topaz_state *ts;
bus = devm_kzalloc(&pdev->dev, sizeof(*bus) + sizeof(*ts), GFP_KERNEL);
if (!bus)
return NULL;
ts = get_bus_priv(bus);
ts->base.probe_cb = qtnf_pcie_topaz_probe;
ts->base.remove_cb = qtnf_pcie_topaz_remove;
ts->base.dma_mask_get_cb = qtnf_topaz_dma_mask_get;
#ifdef CONFIG_PM_SLEEP
ts->base.resume_cb = qtnf_pcie_topaz_resume;
ts->base.suspend_cb = qtnf_pcie_topaz_suspend;
#endif
return bus;
}