Commit 5b07aee1 authored by Raghu Vatsavayi's avatar Raghu Vatsavayi Committed by David S. Miller

liquidio: MSIX support for CN23XX

This patch adds support msix interrupt for cn23xx device.
Signed-off-by: default avatarDerek Chickles <derek.chickles@caviumnetworks.com>
Signed-off-by: default avatarSatanand Burla <satananda.burla@caviumnetworks.com>
Signed-off-by: default avatarFelix Manlunas <felix.manlunas@caviumnetworks.com>
Signed-off-by: default avatarRaghu Vatsavayi <raghu.vatsavayi@caviumnetworks.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 1b7c55c4
...@@ -567,10 +567,16 @@ static void cn23xx_setup_iq_regs(struct octeon_device *oct, u32 iq_no) ...@@ -567,10 +567,16 @@ static void cn23xx_setup_iq_regs(struct octeon_device *oct, u32 iq_no)
*/ */
pkt_in_done = readq(iq->inst_cnt_reg); pkt_in_done = readq(iq->inst_cnt_reg);
/* Clear the count by writing back what we read, but don't if (oct->msix_on) {
* enable interrupts /* Set CINT_ENB to enable IQ interrupt */
*/ writeq((pkt_in_done | CN23XX_INTR_CINT_ENB),
writeq(pkt_in_done, iq->inst_cnt_reg); iq->inst_cnt_reg);
} else {
/* Clear the count by writing back what we read, but don't
* enable interrupts
*/
writeq(pkt_in_done, iq->inst_cnt_reg);
}
iq->reset_instr_cnt = 0; iq->reset_instr_cnt = 0;
} }
...@@ -579,6 +585,9 @@ static void cn23xx_setup_oq_regs(struct octeon_device *oct, u32 oq_no) ...@@ -579,6 +585,9 @@ static void cn23xx_setup_oq_regs(struct octeon_device *oct, u32 oq_no)
{ {
u32 reg_val; u32 reg_val;
struct octeon_droq *droq = oct->droq[oq_no]; struct octeon_droq *droq = oct->droq[oq_no];
struct octeon_cn23xx_pf *cn23xx = (struct octeon_cn23xx_pf *)oct->chip;
u64 time_threshold;
u64 cnt_threshold;
oq_no += oct->sriov_info.pf_srn; oq_no += oct->sriov_info.pf_srn;
...@@ -595,19 +604,31 @@ static void cn23xx_setup_oq_regs(struct octeon_device *oct, u32 oq_no) ...@@ -595,19 +604,31 @@ static void cn23xx_setup_oq_regs(struct octeon_device *oct, u32 oq_no)
droq->pkts_credit_reg = droq->pkts_credit_reg =
(u8 *)oct->mmio[0].hw_addr + CN23XX_SLI_OQ_PKTS_CREDIT(oq_no); (u8 *)oct->mmio[0].hw_addr + CN23XX_SLI_OQ_PKTS_CREDIT(oq_no);
/* Enable this output queue to generate Packet Timer Interrupt if (!oct->msix_on) {
*/ /* Enable this output queue to generate Packet Timer Interrupt
reg_val = octeon_read_csr(oct, CN23XX_SLI_OQ_PKT_CONTROL(oq_no)); */
reg_val |= CN23XX_PKT_OUTPUT_CTL_TENB; reg_val =
octeon_write_csr(oct, CN23XX_SLI_OQ_PKT_CONTROL(oq_no), octeon_read_csr(oct, CN23XX_SLI_OQ_PKT_CONTROL(oq_no));
reg_val); reg_val |= CN23XX_PKT_OUTPUT_CTL_TENB;
octeon_write_csr(oct, CN23XX_SLI_OQ_PKT_CONTROL(oq_no),
reg_val);
/* Enable this output queue to generate Packet Count Interrupt /* Enable this output queue to generate Packet Count Interrupt
*/ */
reg_val = octeon_read_csr(oct, CN23XX_SLI_OQ_PKT_CONTROL(oq_no)); reg_val =
reg_val |= CN23XX_PKT_OUTPUT_CTL_CENB; octeon_read_csr(oct, CN23XX_SLI_OQ_PKT_CONTROL(oq_no));
octeon_write_csr(oct, CN23XX_SLI_OQ_PKT_CONTROL(oq_no), reg_val |= CN23XX_PKT_OUTPUT_CTL_CENB;
reg_val); octeon_write_csr(oct, CN23XX_SLI_OQ_PKT_CONTROL(oq_no),
reg_val);
} else {
time_threshold = cn23xx_pf_get_oq_ticks(
oct, (u32)CFG_GET_OQ_INTR_TIME(cn23xx->conf));
cnt_threshold = (u32)CFG_GET_OQ_INTR_PKT(cn23xx->conf);
octeon_write_csr64(
oct, CN23XX_SLI_OQ_PKT_INT_LEVELS(oq_no),
((time_threshold << 32 | cnt_threshold)));
}
} }
static int cn23xx_enable_io_queues(struct octeon_device *oct) static int cn23xx_enable_io_queues(struct octeon_device *oct)
...@@ -762,6 +783,110 @@ static void cn23xx_disable_io_queues(struct octeon_device *oct) ...@@ -762,6 +783,110 @@ static void cn23xx_disable_io_queues(struct octeon_device *oct)
} }
} }
static u64 cn23xx_pf_msix_interrupt_handler(void *dev)
{
struct octeon_ioq_vector *ioq_vector = (struct octeon_ioq_vector *)dev;
struct octeon_device *oct = ioq_vector->oct_dev;
u64 pkts_sent;
u64 ret = 0;
struct octeon_droq *droq = oct->droq[ioq_vector->droq_index];
dev_dbg(&oct->pci_dev->dev, "In %s octeon_dev @ %p\n", __func__, oct);
if (!droq) {
dev_err(&oct->pci_dev->dev, "23XX bringup FIXME: oct pfnum:%d ioq_vector->ioq_num :%d droq is NULL\n",
oct->pf_num, ioq_vector->ioq_num);
return 0;
}
pkts_sent = readq(droq->pkts_sent_reg);
/* 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 (!pkts_sent || (pkts_sent == 0xFFFFFFFFFFFFFFFFULL))
return ret;
/* Write count reg in sli_pkt_cnts to clear these int.*/
if ((pkts_sent & CN23XX_INTR_PO_INT) ||
(pkts_sent & CN23XX_INTR_PI_INT)) {
if (pkts_sent & CN23XX_INTR_PO_INT)
ret |= MSIX_PO_INT;
}
if (pkts_sent & CN23XX_INTR_PI_INT)
/* We will clear the count when we update the read_index. */
ret |= MSIX_PI_INT;
/* Never need to handle msix mbox intr for pf. They arrive on the last
* msix
*/
return ret;
}
static irqreturn_t cn23xx_interrupt_handler(void *dev)
{
struct octeon_device *oct = (struct octeon_device *)dev;
struct octeon_cn23xx_pf *cn23xx = (struct octeon_cn23xx_pf *)oct->chip;
u64 intr64;
dev_dbg(&oct->pci_dev->dev, "In %s octeon_dev @ %p\n", __func__, oct);
intr64 = readq(cn23xx->intr_sum_reg64);
oct->int_status = 0;
if (intr64 & CN23XX_INTR_ERR)
dev_err(&oct->pci_dev->dev, "OCTEON[%d]: Error Intr: 0x%016llx\n",
oct->octeon_id, CVM_CAST64(intr64));
if (oct->msix_on != LIO_FLAG_MSIX_ENABLED) {
if (intr64 & CN23XX_INTR_PKT_DATA)
oct->int_status |= OCT_DEV_INTR_PKT_DATA;
}
if (intr64 & (CN23XX_INTR_DMA0_FORCE))
oct->int_status |= OCT_DEV_INTR_DMA0_FORCE;
if (intr64 & (CN23XX_INTR_DMA1_FORCE))
oct->int_status |= OCT_DEV_INTR_DMA1_FORCE;
/* Clear the current interrupts */
writeq(intr64, cn23xx->intr_sum_reg64);
return IRQ_HANDLED;
}
static void cn23xx_enable_pf_interrupt(struct octeon_device *oct, u8 intr_flag)
{
struct octeon_cn23xx_pf *cn23xx = (struct octeon_cn23xx_pf *)oct->chip;
u64 intr_val = 0;
/* Divide the single write to multiple writes based on the flag. */
/* Enable Interrupt */
if (intr_flag == OCTEON_ALL_INTR) {
writeq(cn23xx->intr_mask64, cn23xx->intr_enb_reg64);
} else if (intr_flag & OCTEON_OUTPUT_INTR) {
intr_val = readq(cn23xx->intr_enb_reg64);
intr_val |= CN23XX_INTR_PKT_DATA;
writeq(intr_val, cn23xx->intr_enb_reg64);
}
}
static void cn23xx_disable_pf_interrupt(struct octeon_device *oct, u8 intr_flag)
{
struct octeon_cn23xx_pf *cn23xx = (struct octeon_cn23xx_pf *)oct->chip;
u64 intr_val = 0;
/* Disable Interrupts */
if (intr_flag == OCTEON_ALL_INTR) {
writeq(0, cn23xx->intr_enb_reg64);
} else if (intr_flag & OCTEON_OUTPUT_INTR) {
intr_val = readq(cn23xx->intr_enb_reg64);
intr_val &= ~CN23XX_INTR_PKT_DATA;
writeq(intr_val, cn23xx->intr_enb_reg64);
}
}
static void cn23xx_get_pcie_qlmport(struct octeon_device *oct) static void cn23xx_get_pcie_qlmport(struct octeon_device *oct)
{ {
oct->pcie_port = (octeon_read_csr(oct, CN23XX_SLI_MAC_NUMBER)) & 0xff; oct->pcie_port = (octeon_read_csr(oct, CN23XX_SLI_MAC_NUMBER)) & 0xff;
...@@ -816,7 +941,8 @@ static void cn23xx_setup_reg_address(struct octeon_device *oct) ...@@ -816,7 +941,8 @@ static void cn23xx_setup_reg_address(struct octeon_device *oct)
cn23xx_get_pcie_qlmport(oct); cn23xx_get_pcie_qlmport(oct);
cn23xx->intr_mask64 = CN23XX_INTR_MASK; cn23xx->intr_mask64 = CN23XX_INTR_MASK;
cn23xx->intr_mask64 |= CN23XX_INTR_PKT_TIME; if (!oct->msix_on)
cn23xx->intr_mask64 |= CN23XX_INTR_PKT_TIME;
if (oct->rev_id >= OCTEON_CN23XX_REV_1_1) if (oct->rev_id >= OCTEON_CN23XX_REV_1_1)
cn23xx->intr_mask64 |= CN23XX_INTR_VF_MBOX; cn23xx->intr_mask64 |= CN23XX_INTR_VF_MBOX;
...@@ -901,8 +1027,14 @@ int setup_cn23xx_octeon_pf_device(struct octeon_device *oct) ...@@ -901,8 +1027,14 @@ int setup_cn23xx_octeon_pf_device(struct octeon_device *oct)
oct->fn_list.setup_iq_regs = cn23xx_setup_iq_regs; oct->fn_list.setup_iq_regs = cn23xx_setup_iq_regs;
oct->fn_list.setup_oq_regs = cn23xx_setup_oq_regs; oct->fn_list.setup_oq_regs = cn23xx_setup_oq_regs;
oct->fn_list.process_interrupt_regs = cn23xx_interrupt_handler;
oct->fn_list.msix_interrupt_handler = cn23xx_pf_msix_interrupt_handler;
oct->fn_list.setup_device_regs = cn23xx_setup_pf_device_regs; oct->fn_list.setup_device_regs = cn23xx_setup_pf_device_regs;
oct->fn_list.enable_interrupt = cn23xx_enable_pf_interrupt;
oct->fn_list.disable_interrupt = cn23xx_disable_pf_interrupt;
oct->fn_list.enable_io_queues = cn23xx_enable_io_queues; oct->fn_list.enable_io_queues = cn23xx_enable_io_queues;
oct->fn_list.disable_io_queues = cn23xx_disable_io_queues; oct->fn_list.disable_io_queues = cn23xx_disable_io_queues;
......
...@@ -479,18 +479,20 @@ lio_cn6xxx_update_read_index(struct octeon_instr_queue *iq) ...@@ -479,18 +479,20 @@ lio_cn6xxx_update_read_index(struct octeon_instr_queue *iq)
return new_idx; return new_idx;
} }
void lio_cn6xxx_enable_interrupt(void *chip) void lio_cn6xxx_enable_interrupt(struct octeon_device *oct,
u8 unused __attribute__((unused)))
{ {
struct octeon_cn6xxx *cn6xxx = (struct octeon_cn6xxx *)chip; struct octeon_cn6xxx *cn6xxx = (struct octeon_cn6xxx *)oct->chip;
u64 mask = cn6xxx->intr_mask64 | CN6XXX_INTR_DMA0_FORCE; u64 mask = cn6xxx->intr_mask64 | CN6XXX_INTR_DMA0_FORCE;
/* Enable Interrupt */ /* Enable Interrupt */
writeq(mask, cn6xxx->intr_enb_reg64); writeq(mask, cn6xxx->intr_enb_reg64);
} }
void lio_cn6xxx_disable_interrupt(void *chip) void lio_cn6xxx_disable_interrupt(struct octeon_device *oct,
u8 unused __attribute__((unused)))
{ {
struct octeon_cn6xxx *cn6xxx = (struct octeon_cn6xxx *)chip; struct octeon_cn6xxx *cn6xxx = (struct octeon_cn6xxx *)oct->chip;
/* Disable Interrupts */ /* Disable Interrupts */
writeq(0, cn6xxx->intr_enb_reg64); writeq(0, cn6xxx->intr_enb_reg64);
......
...@@ -89,8 +89,8 @@ void lio_cn6xxx_bar1_idx_write(struct octeon_device *oct, u32 idx, u32 mask); ...@@ -89,8 +89,8 @@ void lio_cn6xxx_bar1_idx_write(struct octeon_device *oct, u32 idx, u32 mask);
u32 lio_cn6xxx_bar1_idx_read(struct octeon_device *oct, u32 idx); u32 lio_cn6xxx_bar1_idx_read(struct octeon_device *oct, u32 idx);
u32 u32
lio_cn6xxx_update_read_index(struct octeon_instr_queue *iq); lio_cn6xxx_update_read_index(struct octeon_instr_queue *iq);
void lio_cn6xxx_enable_interrupt(void *chip); void lio_cn6xxx_enable_interrupt(struct octeon_device *oct, u8 unused);
void lio_cn6xxx_disable_interrupt(void *chip); void lio_cn6xxx_disable_interrupt(struct octeon_device *oct, u8 unused);
void cn6xxx_get_pcie_qlmport(struct octeon_device *oct); void cn6xxx_get_pcie_qlmport(struct octeon_device *oct);
void lio_cn6xxx_setup_reg_address(struct octeon_device *oct, void *chip, void lio_cn6xxx_setup_reg_address(struct octeon_device *oct, void *chip,
struct octeon_reg_list *reg_list); struct octeon_reg_list *reg_list);
......
...@@ -746,6 +746,45 @@ struct octeon_device *octeon_allocate_device(u32 pci_id, ...@@ -746,6 +746,45 @@ struct octeon_device *octeon_allocate_device(u32 pci_id,
return oct; return oct;
} }
int
octeon_allocate_ioq_vector(struct octeon_device *oct)
{
int i, num_ioqs = 0;
struct octeon_ioq_vector *ioq_vector;
int cpu_num;
int size;
if (OCTEON_CN23XX_PF(oct))
num_ioqs = oct->sriov_info.num_pf_rings;
size = sizeof(struct octeon_ioq_vector) * num_ioqs;
oct->ioq_vector = vmalloc(size);
if (!oct->ioq_vector)
return 1;
memset(oct->ioq_vector, 0, size);
for (i = 0; i < num_ioqs; i++) {
ioq_vector = &oct->ioq_vector[i];
ioq_vector->oct_dev = oct;
ioq_vector->iq_index = i;
ioq_vector->droq_index = i;
cpu_num = i % num_online_cpus();
cpumask_set_cpu(cpu_num, &ioq_vector->affinity_mask);
if (oct->chip_id == OCTEON_CN23XX_PF_VID)
ioq_vector->ioq_num = i + oct->sriov_info.pf_srn;
else
ioq_vector->ioq_num = i;
}
return 0;
}
void
octeon_free_ioq_vector(struct octeon_device *oct)
{
vfree(oct->ioq_vector);
}
/* this function is only for setting up the first queue */ /* this function is only for setting up the first queue */
int octeon_setup_instr_queues(struct octeon_device *oct) int octeon_setup_instr_queues(struct octeon_device *oct)
{ {
......
...@@ -52,6 +52,9 @@ enum octeon_pci_swap_mode { ...@@ -52,6 +52,9 @@ enum octeon_pci_swap_mode {
OCTEON_PCI_32BIT_LW_SWAP = 3 OCTEON_PCI_32BIT_LW_SWAP = 3
}; };
#define OCTEON_OUTPUT_INTR (2)
#define OCTEON_ALL_INTR 0xff
/*--------------- PCI BAR1 index registers -------------*/ /*--------------- PCI BAR1 index registers -------------*/
/* BAR1 Mask */ /* BAR1 Mask */
...@@ -204,6 +207,7 @@ struct octeon_fn_list { ...@@ -204,6 +207,7 @@ struct octeon_fn_list {
void (*setup_oq_regs)(struct octeon_device *, u32); void (*setup_oq_regs)(struct octeon_device *, u32);
irqreturn_t (*process_interrupt_regs)(void *); irqreturn_t (*process_interrupt_regs)(void *);
u64 (*msix_interrupt_handler)(void *);
int (*soft_reset)(struct octeon_device *); int (*soft_reset)(struct octeon_device *);
int (*setup_device_regs)(struct octeon_device *); int (*setup_device_regs)(struct octeon_device *);
void (*bar1_idx_setup)(struct octeon_device *, u64, u32, int); void (*bar1_idx_setup)(struct octeon_device *, u64, u32, int);
...@@ -214,8 +218,8 @@ struct octeon_fn_list { ...@@ -214,8 +218,8 @@ struct octeon_fn_list {
void (*enable_oq_pkt_time_intr)(struct octeon_device *, u32); void (*enable_oq_pkt_time_intr)(struct octeon_device *, u32);
void (*disable_oq_pkt_time_intr)(struct octeon_device *, u32); void (*disable_oq_pkt_time_intr)(struct octeon_device *, u32);
void (*enable_interrupt)(void *); void (*enable_interrupt)(struct octeon_device *, u8);
void (*disable_interrupt)(void *); void (*disable_interrupt)(struct octeon_device *, u8);
int (*enable_io_queues)(struct octeon_device *); int (*enable_io_queues)(struct octeon_device *);
void (*disable_io_queues)(struct octeon_device *); void (*disable_io_queues)(struct octeon_device *);
...@@ -276,6 +280,10 @@ struct octdev_props { ...@@ -276,6 +280,10 @@ struct octdev_props {
struct net_device *netdev; struct net_device *netdev;
}; };
#define LIO_FLAG_MSIX_ENABLED 0x1
#define MSIX_PO_INT 0x1
#define MSIX_PI_INT 0x2
struct octeon_pf_vf_hs_word { struct octeon_pf_vf_hs_word {
#ifdef __LITTLE_ENDIAN_BITFIELD #ifdef __LITTLE_ENDIAN_BITFIELD
/** PKIND value assigned for the DPI interface */ /** PKIND value assigned for the DPI interface */
...@@ -323,6 +331,15 @@ struct octeon_sriov_info { ...@@ -323,6 +331,15 @@ struct octeon_sriov_info {
}; };
struct octeon_ioq_vector {
struct octeon_device *oct_dev;
int iq_index;
int droq_index;
int vector;
struct cpumask affinity_mask;
u32 ioq_num;
};
/** The Octeon device. /** The Octeon device.
* Each Octeon device has this structure to represent all its * Each Octeon device has this structure to represent all its
* components. * components.
...@@ -357,7 +374,6 @@ struct octeon_device { ...@@ -357,7 +374,6 @@ struct octeon_device {
u16 flags; u16 flags;
#define LIO_FLAG_MSI_ENABLED (u32)(1 << 1) #define LIO_FLAG_MSI_ENABLED (u32)(1 << 1)
#define LIO_FLAG_MSIX_ENABLED (u32)(1 << 2)
/** The state of this device */ /** The state of this device */
atomic_t status; atomic_t status;
...@@ -447,10 +463,19 @@ struct octeon_device { ...@@ -447,10 +463,19 @@ struct octeon_device {
void *priv; void *priv;
int num_msix_irqs;
void *msix_entries;
struct octeon_sriov_info sriov_info; struct octeon_sriov_info sriov_info;
struct octeon_pf_vf_hs_word pfvf_hsword; struct octeon_pf_vf_hs_word pfvf_hsword;
int msix_on;
/** IOq information of it's corresponding MSI-X interrupt. */
struct octeon_ioq_vector *ioq_vector;
int rx_pause; int rx_pause;
int tx_pause; int tx_pause;
...@@ -718,6 +743,8 @@ void *oct_get_config_info(struct octeon_device *oct, u16 card_type); ...@@ -718,6 +743,8 @@ void *oct_get_config_info(struct octeon_device *oct, u16 card_type);
*/ */
struct octeon_config *octeon_get_conf(struct octeon_device *oct); struct octeon_config *octeon_get_conf(struct octeon_device *oct);
void octeon_free_ioq_vector(struct octeon_device *oct);
int octeon_allocate_ioq_vector(struct octeon_device *oct);
void lio_enable_irq(struct octeon_droq *droq, struct octeon_instr_queue *iq); void lio_enable_irq(struct octeon_droq *droq, struct octeon_instr_queue *iq);
/* LiquidIO driver pivate flags */ /* LiquidIO driver pivate flags */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment