/* Rx IRQ Count and Time Limits */ #define CANFD_CTL_IRQ_CL_DEF 16 /* Rx msg max nb per IRQ in Rx DMA */ #define CANFD_CTL_IRQ_TL_DEF 10 /* Time before IRQ if < CL (x100 µs) */
/* Tx anticipation window (link logical address should be aligned on 2K * boundary)
*/ #define PCIEFD_TX_PAGE_COUNT (PCIEFD_TX_DMA_SIZE / PCIEFD_TX_PAGE_SIZE)
#define CANFD_MSG_LNK_TX 0x1001 /* Tx msgs link */
/* 32-bits IRQ status fields, heading Rx DMA area */ staticinlineint pciefd_irq_tag(u32 irq_status)
{ return irq_status & 0x0000000f;
}
/* read a 32 bits value from a SYS block register */ staticinline u32 pciefd_sys_readreg(conststruct pciefd_board *priv, u16 reg)
{ return readl(priv->reg_base + reg);
}
/* write a 32 bits value into a SYS block register */ staticinlinevoid pciefd_sys_writereg(conststruct pciefd_board *priv,
u32 val, u16 reg)
{
writel(val, priv->reg_base + reg);
}
/* read a 32 bits value from CAN-FD block register */ staticinline u32 pciefd_can_readreg(conststruct pciefd_can *priv, u16 reg)
{ return readl(priv->reg_base + reg);
}
/* write a 32 bits value into a CAN-FD block register */ staticinlinevoid pciefd_can_writereg(conststruct pciefd_can *priv,
u32 val, u16 reg)
{
writel(val, priv->reg_base + reg);
}
/* give a channel logical Rx DMA address to the board */ staticvoid pciefd_can_setup_rx_dma(struct pciefd_can *priv)
{ #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT const u32 dma_addr_h = (u32)(priv->rx_dma_laddr >> 32); #else const u32 dma_addr_h = 0; #endif
/* (DMA must be reset for Rx) */
pciefd_can_writereg(priv, CANFD_CTL_RST_BIT, PCIEFD_REG_CAN_RX_CTL_SET);
/* write the logical address of the Rx DMA area for this channel */
pciefd_can_writereg(priv, (u32)priv->rx_dma_laddr,
PCIEFD_REG_CAN_RX_DMA_ADDR_L);
pciefd_can_writereg(priv, dma_addr_h, PCIEFD_REG_CAN_RX_DMA_ADDR_H);
/* also indicates that Rx DMA is cacheable */
pciefd_can_writereg(priv, CANFD_CTL_UNC_BIT, PCIEFD_REG_CAN_RX_CTL_CLR);
}
/* clear channel logical Rx DMA address from the board */ staticvoid pciefd_can_clear_rx_dma(struct pciefd_can *priv)
{ /* DMA must be reset for Rx */
pciefd_can_writereg(priv, CANFD_CTL_RST_BIT, PCIEFD_REG_CAN_RX_CTL_SET);
/* clear the logical address of the Rx DMA area for this channel */
pciefd_can_writereg(priv, 0, PCIEFD_REG_CAN_RX_DMA_ADDR_L);
pciefd_can_writereg(priv, 0, PCIEFD_REG_CAN_RX_DMA_ADDR_H);
}
/* give a channel logical Tx DMA address to the board */ staticvoid pciefd_can_setup_tx_dma(struct pciefd_can *priv)
{ #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT const u32 dma_addr_h = (u32)(priv->tx_dma_laddr >> 32); #else const u32 dma_addr_h = 0; #endif
/* (DMA must be reset for Tx) */
pciefd_can_writereg(priv, CANFD_CTL_RST_BIT, PCIEFD_REG_CAN_TX_CTL_SET);
/* write the logical address of the Tx DMA area for this channel */
pciefd_can_writereg(priv, (u32)priv->tx_dma_laddr,
PCIEFD_REG_CAN_TX_DMA_ADDR_L);
pciefd_can_writereg(priv, dma_addr_h, PCIEFD_REG_CAN_TX_DMA_ADDR_H);
/* also indicates that Tx DMA is cacheable */
pciefd_can_writereg(priv, CANFD_CTL_UNC_BIT, PCIEFD_REG_CAN_TX_CTL_CLR);
}
/* clear channel logical Tx DMA address from the board */ staticvoid pciefd_can_clear_tx_dma(struct pciefd_can *priv)
{ /* DMA must be reset for Tx */
pciefd_can_writereg(priv, CANFD_CTL_RST_BIT, PCIEFD_REG_CAN_TX_CTL_SET);
/* clear the logical address of the Tx DMA area for this channel */
pciefd_can_writereg(priv, 0, PCIEFD_REG_CAN_TX_DMA_ADDR_L);
pciefd_can_writereg(priv, 0, PCIEFD_REG_CAN_TX_DMA_ADDR_H);
}
staticvoid pciefd_can_ack_rx_dma(struct pciefd_can *priv)
{ /* read value of current IRQ tag and inc it for next one */
priv->irq_tag = le32_to_cpu(*(__le32 *)priv->rx_dma_vaddr);
priv->irq_tag++;
priv->irq_tag &= 0xf;
/* write the next IRQ tag for this CAN */
pciefd_can_writereg(priv, priv->irq_tag, PCIEFD_REG_CAN_RX_CTL_ACK);
}
/* INTA mode only to sync with PCIe transaction */ if (!pci_dev_msi_enabled(priv->board->pci_dev))
(void)pciefd_sys_readreg(priv->board, PCIEFD_REG_SYS_VER1);
/* read IRQ status from the first 32-bits of the Rx DMA area */
priv->irq_status = le32_to_cpu(rx_dma->irq_status);
/* check if this (shared) IRQ is for this CAN */ if (pciefd_irq_tag(priv->irq_status) != priv->irq_tag) return IRQ_NONE;
/* wake producer up (only if enough room in echo_skb array) */
spin_lock_irqsave(&priv->ucan.echo_lock, flags); if (!priv->ucan.can.echo_skb[priv->ucan.echo_idx])
netif_wake_queue(priv->ucan.ndev);
if (page->offset + msg_size > page->size) { struct pciefd_tx_link *lk;
/* not enough space in this page: try another one */ if (!priv->tx_pages_free) {
spin_unlock_irqrestore(&priv->tx_lock, flags);
/* Tx overflow */ return NULL;
}
priv->tx_pages_free--;
/* keep address of the very last free slot of current page */
lk = page->vbase + page->offset;
/* next, move on a new free page */
priv->tx_page_index = (priv->tx_page_index + 1) %
PCIEFD_TX_PAGE_COUNT;
page = priv->tx_pages + priv->tx_page_index;
/* put link record to this new page at the end of prev one */
lk->size = cpu_to_le16(sizeof(*lk));
lk->type = cpu_to_le16(CANFD_MSG_LNK_TX);
lk->laddr_lo = cpu_to_le32(page->lbase);
#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
lk->laddr_hi = cpu_to_le32(page->lbase >> 32); #else
lk->laddr_hi = 0; #endif /* next msgs will be put from the begininng of this new page */
page->offset = 0;
}
*room_left = priv->tx_pages_free * page->size;
spin_unlock_irqrestore(&priv->tx_lock, flags);
msg = page->vbase + page->offset;
/* give back room left in the tx ring */
*room_left += page->size - (page->offset + msg_size);
/* allocate the candev object with default isize of echo skbs ring */
ndev = alloc_peak_canfd_dev(sizeof(*priv), pciefd->can_count,
PCIEFD_ECHO_SKB_MAX); if (!ndev) {
dev_err(&pciefd->pci_dev->dev, "failed to alloc candev object\n"); goto failure;
}
err = register_candev(ndev); if (err) {
dev_err(&pciefd->pci_dev->dev, "couldn't register CAN device: %d\n", err); goto err_free_candev;
}
spin_lock_init(&priv->tx_lock);
/* save the object address in the board structure */
pciefd->can[pciefd->can_count] = priv;
dev_info(&pciefd->pci_dev->dev, "%s at reg_base=0x%p irq=%d\n",
ndev->name, priv->reg_base, ndev->irq);
return 0;
err_free_candev:
free_candev(ndev);
failure: return -ENOMEM;
}
/* remove a CAN-FD channel by releasing all of its resources */ staticvoid pciefd_can_remove(struct pciefd_can *priv)
{ /* unregister (close) the can device to go back to RST mode first */
unregister_candev(priv->ucan.ndev);
/* finally, free the candev object */
free_candev(priv->ucan.ndev);
}
/* remove all CAN-FD channels by releasing their own resources */ staticvoid pciefd_can_remove_all(struct pciefd_board *pciefd)
{ while (pciefd->can_count > 0)
pciefd_can_remove(pciefd->can[--pciefd->can_count]);
}
err = pci_enable_device(pdev); if (err) return err;
err = pci_request_regions(pdev, PCIEFD_DRV_NAME); if (err) goto err_disable_pci;
/* the number of channels depends on sub-system id */
err = pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &sub_sys_id); if (err) goto err_release_regions;
/* initialize the board structure */
pciefd->pci_dev = pdev;
spin_lock_init(&pciefd->cmd_lock);
/* save the PCI BAR0 virtual address for further system regs access */
pciefd->reg_base = pci_iomap(pdev, 0, PCIEFD_BAR0_SIZE); if (!pciefd->reg_base) {
dev_err(&pdev->dev, "failed to map PCI resource #0\n");
err = -ENOMEM; goto err_release_regions;
}
/* read the firmware version number */
v2 = pciefd_sys_readreg(pciefd, PCIEFD_REG_SYS_VER2);
#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT /* FW < v3.3.0 DMA logic doesn't handle correctly the mix of 32-bit and * 64-bit logical addresses: this workaround forces usage of 32-bit * DMA addresses only when such a fw is detected.
*/ if (PCIEFD_FW_VERSION(hw_ver_major, hw_ver_minor, hw_ver_sub) <
PCIEFD_FW_VERSION(3, 3, 0)) {
err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); if (err)
dev_warn(&pdev->dev, "warning: can't set DMA mask %llxh (err %d)\n",
DMA_BIT_MASK(32), err);
} #endif
/* stop system clock */
pciefd_sys_writereg(pciefd, PCIEFD_SYS_CTL_CLK_EN,
PCIEFD_REG_SYS_CTL_CLR);
pci_set_master(pdev);
/* create now the corresponding channels objects */ while (pciefd->can_count < can_count) {
err = pciefd_can_probe(pciefd); if (err) goto err_free_canfd;
pciefd->can_count++;
}
/* set system timestamps counter in RST mode */
pciefd_sys_writereg(pciefd, PCIEFD_SYS_CTL_TS_RST,
PCIEFD_REG_SYS_CTL_SET);
/* wait a bit (read cycle) */
(void)pciefd_sys_readreg(pciefd, PCIEFD_REG_SYS_VER1);
/* free all clocks */
pciefd_sys_writereg(pciefd, PCIEFD_SYS_CTL_TS_RST,
PCIEFD_REG_SYS_CTL_CLR);
/* start system clock */
pciefd_sys_writereg(pciefd, PCIEFD_SYS_CTL_CLK_EN,
PCIEFD_REG_SYS_CTL_SET);
/* remember the board structure address in the device user data */
pci_set_drvdata(pdev, pciefd);
return 0;
err_free_canfd:
pciefd_can_remove_all(pciefd);
pci_iounmap(pdev, pciefd->reg_base);
err_release_regions:
pci_release_regions(pdev);
err_disable_pci:
pci_disable_device(pdev);
/* pci_xxx_config_word() return positive PCIBIOS_xxx error codes while * the probe() function must return a negative errno in case of failure * (err is unchanged if negative)
*/ return pcibios_err_to_errno(err);
}
/* free the board structure object, as well as its resources: */ staticvoid peak_pciefd_remove(struct pci_dev *pdev)
{ struct pciefd_board *pciefd = pci_get_drvdata(pdev);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.