/********************************************************************** * Author: Cavium, Inc. * * Contact: support@cavium.com * Please include "LiquidIO" in the subject. * * Copyright (c) 2003-2016 Cavium, Inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 2, as * published by the Free Software Foundation. * * This file is distributed in the hope that it will be useful, but * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or * NONINFRINGEMENT. See the GNU General Public License for more details.
***********************************************************************/ #include <linux/pci.h> #include <linux/netdevice.h> #include"liquidio_common.h" #include"octeon_droq.h" #include"octeon_iq.h" #include"response_manager.h" #include"octeon_device.h" #include"octeon_main.h" #include"cn66xx_regs.h" #include"cn66xx_device.h"
int lio_cn6xxx_soft_reset(struct octeon_device *oct)
{
octeon_write_csr64(oct, CN6XXX_WIN_WR_MASK_REG, 0xFF);
dev_dbg(&oct->pci_dev->dev, "BIST enabled for soft reset\n");
/* Read config register for MPS */
pci_read_config_dword(oct->pci_dev, CN6XXX_PCIE_DEVCTL, &val);
if (mps == PCIE_MPS_DEFAULT) {
mps = ((val & (0x7 << 5)) >> 5);
} else {
val &= ~(0x7 << 5); /* Turn off any MPS bits */
val |= (mps << 5); /* Set MPS */
pci_write_config_dword(oct->pci_dev, CN6XXX_PCIE_DEVCTL, val);
}
/* Set MPS in DPI_SLI_PRT0_CFG to the same value. */
r64 = lio_pci_readq(oct, CN6XXX_DPI_SLI_PRTX_CFG(oct->pcie_port));
r64 |= (mps << 4);
lio_pci_writeq(oct, r64, CN6XXX_DPI_SLI_PRTX_CFG(oct->pcie_port));
}
/* Read config register for MRRS */
pci_read_config_dword(oct->pci_dev, CN6XXX_PCIE_DEVCTL, &val);
if (mrrs == PCIE_MRRS_DEFAULT) {
mrrs = ((val & (0x7 << 12)) >> 12);
} else {
val &= ~(0x7 << 12); /* Turn off any MRRS bits */
val |= (mrrs << 12); /* Set MRRS */
pci_write_config_dword(oct->pci_dev, CN6XXX_PCIE_DEVCTL, val);
}
/* Set MRRS in SLI_S2M_PORT0_CTL to the same value. */
r64 = octeon_read_csr64(oct, CN6XXX_SLI_S2M_PORTX_CTL(oct->pcie_port));
r64 |= mrrs;
octeon_write_csr64(oct, CN6XXX_SLI_S2M_PORTX_CTL(oct->pcie_port), r64);
/* Set MRRS in DPI_SLI_PRT0_CFG to the same value. */
r64 = lio_pci_readq(oct, CN6XXX_DPI_SLI_PRTX_CFG(oct->pcie_port));
r64 |= mrrs;
lio_pci_writeq(oct, r64, CN6XXX_DPI_SLI_PRTX_CFG(oct->pcie_port));
}
u32 lio_cn6xxx_coprocessor_clock(struct octeon_device *oct)
{ /* Bits 29:24 of MIO_RST_BOOT holds the ref. clock multiplier * for SLI.
*/ return ((lio_pci_readq(oct, CN6XXX_MIO_RST_BOOT) >> 24) & 0x3f) * 50;
}
u32 lio_cn6xxx_get_oq_ticks(struct octeon_device *oct,
u32 time_intr_in_us)
{ /* This gives the SLI clock per microsec */
u32 oqticks_per_us = lio_cn6xxx_coprocessor_clock(oct);
/* core clock per us / oq ticks will be fractional. TO avoid that * we use the method below.
*/
/* This gives the clock cycles per millisecond */
oqticks_per_us *= 1000;
/* This gives the oq ticks (1024 core clock cycles) per millisecond */
oqticks_per_us /= 1024;
/* time_intr is in microseconds. The next 2 steps gives the oq ticks * corressponding to time_intr.
*/
oqticks_per_us *= time_intr_in_us;
oqticks_per_us /= 1000;
/* / Select PCI-E Port for all Output queues */
octeon_write_csr64(oct, CN6XXX_SLI_PKT_PCIE_PORT64,
(oct->pcie_port * 0x5555555555555555ULL));
if (CFG_GET_IS_SLI_BP_ON(cn6xxx->conf)) {
octeon_write_csr64(oct, CN6XXX_SLI_OQ_WMARK, 32);
} else { /* / Set Output queue watermark to 0 to disable backpressure */
octeon_write_csr64(oct, CN6XXX_SLI_OQ_WMARK, 0);
}
/* / Select Packet count instead of bytes for SLI_PKTi_CNTS[CNT] */
octeon_write_csr(oct, CN6XXX_SLI_PKT_OUT_BMODE, 0);
/* Select ES, RO, NS setting from register for Output Queue Packet * Address
*/
octeon_write_csr(oct, CN6XXX_SLI_PKT_DPADDR, 0xFFFFFFFF);
/* No Relaxed Ordering, No Snoop, 64-bit swap for Output * Queue ScatterList
*/
octeon_write_csr(oct, CN6XXX_SLI_PKT_SLIST_ROR, 0);
octeon_write_csr(oct, CN6XXX_SLI_PKT_SLIST_NS, 0);
/* / ENDIAN_SPECIFIC CHANGES - 0 works for LE. */ #ifdef __BIG_ENDIAN_BITFIELD
octeon_write_csr64(oct, CN6XXX_SLI_PKT_SLIST_ES64,
0x5555555555555555ULL); #else
octeon_write_csr64(oct, CN6XXX_SLI_PKT_SLIST_ES64, 0ULL); #endif
/* / No Relaxed Ordering, No Snoop, 64-bit swap for Output Queue Data */
octeon_write_csr(oct, CN6XXX_SLI_PKT_DATA_OUT_ROR, 0);
octeon_write_csr(oct, CN6XXX_SLI_PKT_DATA_OUT_NS, 0);
octeon_write_csr64(oct, CN6XXX_SLI_PKT_DATA_OUT_ES64,
0x5555555555555555ULL);
/* / Set up interrupt packet and time threshold */
octeon_write_csr(oct, CN6XXX_SLI_OQ_INT_LEVEL_PKTS,
(u32)CFG_GET_OQ_INTR_PKT(cn6xxx->conf));
time_threshold =
lio_cn6xxx_get_oq_ticks(oct, (u32)
CFG_GET_OQ_INTR_TIME(cn6xxx->conf));
/* Default error timeout value should be 0x200000 to avoid host hang * when reads invalid register
*/
octeon_write_csr64(oct, CN6XXX_SLI_WINDOW_CTL, 0x200000ULL); return 0;
}
/* Write the start of the input queue's ring and its size */
octeon_write_csr64(oct, CN6XXX_SLI_IQ_BASE_ADDR64(iq_no),
iq->base_addr_dma);
octeon_write_csr(oct, CN6XXX_SLI_IQ_SIZE(iq_no), iq->max_count);
/* Backpressure for this queue - WMARK set to all F's. This effectively * disables the backpressure mechanism.
*/
octeon_write_csr64(oct, CN66XX_SLI_IQ_BP64(iq_no),
(0xFFFFFFFFULL << 32));
}
/* Get the mapped address of the pkt_sent and pkts_credit regs */
droq->pkts_sent_reg =
oct->mmio[0].hw_addr + CN6XXX_SLI_OQ_PKTS_SENT(oq_no);
droq->pkts_credit_reg =
oct->mmio[0].hw_addr + CN6XXX_SLI_OQ_PKTS_CREDIT(oq_no);
/* Reset the Enable bits for Input Queues. */
mask = octeon_read_csr(oct, CN6XXX_SLI_PKT_INSTR_ENB);
mask ^= oct->io_qmask.iq;
octeon_write_csr(oct, CN6XXX_SLI_PKT_INSTR_ENB, mask);
/* Wait until hardware indicates that the queues are out of reset. */
mask = (u32)oct->io_qmask.iq;
d32 = octeon_read_csr(oct, CN6XXX_SLI_PORT_IN_RST_IQ); while (((d32 & mask) != mask) && loop--) {
d32 = octeon_read_csr(oct, CN6XXX_SLI_PORT_IN_RST_IQ);
schedule_timeout_uninterruptible(1);
}
/* Reset the doorbell register for each Input queue. */ for (i = 0; i < MAX_OCTEON_INSTR_QUEUES(oct); i++) { if (!(oct->io_qmask.iq & BIT_ULL(i))) continue;
octeon_write_csr(oct, CN6XXX_SLI_IQ_DOORBELL(i), 0xFFFFFFFF);
d32 = octeon_read_csr(oct, CN6XXX_SLI_IQ_DOORBELL(i));
}
/* Reset the Enable bits for Output Queues. */
mask = octeon_read_csr(oct, CN6XXX_SLI_PKT_OUT_ENB);
mask ^= oct->io_qmask.oq;
octeon_write_csr(oct, CN6XXX_SLI_PKT_OUT_ENB, mask);
/* Wait until hardware indicates that the queues are out of reset. */
loop = HZ;
mask = (u32)oct->io_qmask.oq;
d32 = octeon_read_csr(oct, CN6XXX_SLI_PORT_IN_RST_OQ); while (((d32 & mask) != mask) && loop--) {
d32 = octeon_read_csr(oct, CN6XXX_SLI_PORT_IN_RST_OQ);
schedule_timeout_uninterruptible(1);
}
;
/* Reset the doorbell register for each Output queue. */ for (i = 0; i < MAX_OCTEON_OUTPUT_QUEUES(oct); i++) { if (!(oct->io_qmask.oq & BIT_ULL(i))) continue;
octeon_write_csr(oct, CN6XXX_SLI_OQ_PKTS_CREDIT(i), 0xFFFFFFFF);
d32 = octeon_read_csr(oct, CN6XXX_SLI_OQ_PKTS_CREDIT(i));
/* The new instr cnt reg is a 32-bit counter that can roll over. We have * noted the counter's initial value at init time into * reset_instr_cnt
*/ if (iq->reset_instr_cnt < new_idx)
new_idx -= iq->reset_instr_cnt; else
new_idx += (0xffffffff - iq->reset_instr_cnt) + 1;
/* Modulo of the new index with the IQ size will give us * the new index.
*/
new_idx %= iq->max_count;
staticvoid lio_cn6xxx_get_pcie_qlmport(struct octeon_device *oct)
{ /* CN63xx Pass2 and newer parts implements the SLI_MAC_NUMBER register * to determine the PCIE port #
*/
oct->pcie_port = octeon_read_csr(oct, CN6XXX_SLI_MAC_NUMBER) & 0xff;
dev_dbg(&oct->pci_dev->dev, "Using PCIE Port %d\n", oct->pcie_port);
}
/* If our device has interrupted, then proceed. * Also check for all f's if interrupt was triggered on an error * and the PCI read fails.
*/ if (!intr64 || (intr64 == 0xFFFFFFFFFFFFFFFFULL)) return IRQ_NONE;
oct->int_status = 0;
if (intr64 & CN6XXX_INTR_ERR)
lio_cn6xxx_process_pcie_error_intr(oct, intr64);
if (intr64 & CN6XXX_INTR_PKT_DATA) {
lio_cn6xxx_process_droq_intr_regs(oct);
oct->int_status |= OCT_DEV_INTR_PKT_DATA;
}
if (intr64 & CN6XXX_INTR_DMA0_FORCE)
oct->int_status |= OCT_DEV_INTR_DMA0_FORCE;
if (intr64 & CN6XXX_INTR_DMA1_FORCE)
oct->int_status |= OCT_DEV_INTR_DMA1_FORCE;
/* Clear the current interrupts */
writeq(intr64, cn6xxx->intr_sum_reg64);
int lio_validate_cn6xxx_config_info(struct octeon_device *oct, struct octeon_config *conf6xxx)
{ if (CFG_GET_IQ_MAX_Q(conf6xxx) > CN6XXX_MAX_INPUT_QUEUES) {
dev_err(&oct->pci_dev->dev, "%s: Num IQ (%d) exceeds Max (%d)\n",
__func__, CFG_GET_IQ_MAX_Q(conf6xxx),
CN6XXX_MAX_INPUT_QUEUES); return 1;
}
if (CFG_GET_OQ_MAX_Q(conf6xxx) > CN6XXX_MAX_OUTPUT_QUEUES) {
dev_err(&oct->pci_dev->dev, "%s: Num OQ (%d) exceeds Max (%d)\n",
__func__, CFG_GET_OQ_MAX_Q(conf6xxx),
CN6XXX_MAX_OUTPUT_QUEUES); return 1;
}
if (CFG_GET_IQ_INSTR_TYPE(conf6xxx) != OCTEON_32BYTE_INSTR &&
CFG_GET_IQ_INSTR_TYPE(conf6xxx) != OCTEON_64BYTE_INSTR) {
dev_err(&oct->pci_dev->dev, "%s: Invalid instr type for IQ\n",
__func__); return 1;
} if (!CFG_GET_OQ_REFILL_THRESHOLD(conf6xxx)) {
dev_err(&oct->pci_dev->dev, "%s: Invalid parameter for OQ\n",
__func__); return 1;
}
if (!(CFG_GET_OQ_INTR_TIME(conf6xxx))) {
dev_err(&oct->pci_dev->dev, "%s: No Time Interrupt for OQ\n",
__func__); return 1;
}
return 0;
}
Messung V0.5
¤ Dauer der Verarbeitung: 0.16 Sekunden
(vorverarbeitet)
¤
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.