Skip to content

Commit

Permalink
net: txgbe: use irq_domain for interrupt controller
Browse files Browse the repository at this point in the history
In the current interrupt controller, the MAC interrupt acts as the
parent interrupt in the GPIO IRQ chip. But when the number of Rx/Tx
ring changes, the PCI IRQ vector needs to be reallocated. Then this
interrupt controller would be corrupted. So use irq_domain structure
to avoid the above problem.

Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Jiawen Wu authored and David S. Miller committed Jan 27, 2024
1 parent 63aabc3 commit aefd013
Show file tree
Hide file tree
Showing 9 changed files with 193 additions and 54 deletions.
2 changes: 0 additions & 2 deletions drivers/net/ethernet/wangxun/libwx/wx_hw.c
Original file line number Diff line number Diff line change
Expand Up @@ -1958,8 +1958,6 @@ int wx_sw_init(struct wx *wx)
return -ENOMEM;
}

wx->msix_in_use = false;

return 0;
}
EXPORT_SYMBOL(wx_sw_init);
Expand Down
20 changes: 8 additions & 12 deletions drivers/net/ethernet/wangxun/libwx/wx_lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -1614,14 +1614,12 @@ static int wx_acquire_msix_vectors(struct wx *wx)
/* One for non-queue interrupts */
nvecs += 1;

if (!wx->msix_in_use) {
wx->msix_entry = kcalloc(1, sizeof(struct msix_entry),
GFP_KERNEL);
if (!wx->msix_entry) {
kfree(wx->msix_q_entries);
wx->msix_q_entries = NULL;
return -ENOMEM;
}
wx->msix_entry = kcalloc(1, sizeof(struct msix_entry),
GFP_KERNEL);
if (!wx->msix_entry) {
kfree(wx->msix_q_entries);
wx->msix_q_entries = NULL;
return -ENOMEM;
}

nvecs = pci_alloc_irq_vectors_affinity(wx->pdev, nvecs,
Expand Down Expand Up @@ -1931,10 +1929,8 @@ void wx_reset_interrupt_capability(struct wx *wx)
if (pdev->msix_enabled) {
kfree(wx->msix_q_entries);
wx->msix_q_entries = NULL;
if (!wx->msix_in_use) {
kfree(wx->msix_entry);
wx->msix_entry = NULL;
}
kfree(wx->msix_entry);
wx->msix_entry = NULL;
}
pci_free_irq_vectors(wx->pdev);
}
Expand Down
1 change: 0 additions & 1 deletion drivers/net/ethernet/wangxun/libwx/wx_type.h
Original file line number Diff line number Diff line change
Expand Up @@ -1047,7 +1047,6 @@ struct wx {
unsigned int queues_per_pool;
struct msix_entry *msix_q_entries;
struct msix_entry *msix_entry;
bool msix_in_use;
struct wx_ring_feature ring_feature[RING_F_ARRAY_SIZE];

/* misc interrupt status block */
Expand Down
132 changes: 132 additions & 0 deletions drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2015 - 2024 Beijing WangXun Technology Co., Ltd. */

#include <linux/irqdomain.h>
#include <linux/pci.h>

#include "../libwx/wx_type.h"
#include "../libwx/wx_lib.h"
#include "../libwx/wx_hw.h"
#include "txgbe_type.h"
#include "txgbe_phy.h"
#include "txgbe_irq.h"

/**
Expand Down Expand Up @@ -135,3 +137,133 @@ int txgbe_request_irq(struct wx *wx)

return err;
}

static int txgbe_request_gpio_irq(struct txgbe *txgbe)
{
txgbe->gpio_irq = irq_find_mapping(txgbe->misc.domain, TXGBE_IRQ_GPIO);
return request_threaded_irq(txgbe->gpio_irq, NULL,
txgbe_gpio_irq_handler,
IRQF_ONESHOT, "txgbe-gpio-irq", txgbe);
}

static int txgbe_request_link_irq(struct txgbe *txgbe)
{
txgbe->link_irq = irq_find_mapping(txgbe->misc.domain, TXGBE_IRQ_LINK);
return request_threaded_irq(txgbe->link_irq, NULL,
txgbe_link_irq_handler,
IRQF_ONESHOT, "txgbe-link-irq", txgbe);
}

static const struct irq_chip txgbe_irq_chip = {
.name = "txgbe-misc-irq",
};

static int txgbe_misc_irq_domain_map(struct irq_domain *d,
unsigned int irq,
irq_hw_number_t hwirq)
{
struct txgbe *txgbe = d->host_data;

irq_set_chip_data(irq, txgbe);
irq_set_chip(irq, &txgbe->misc.chip);
irq_set_nested_thread(irq, true);
irq_set_noprobe(irq);

return 0;
}

static const struct irq_domain_ops txgbe_misc_irq_domain_ops = {
.map = txgbe_misc_irq_domain_map,
};

static irqreturn_t txgbe_misc_irq_handle(int irq, void *data)
{
struct txgbe *txgbe = data;
struct wx *wx = txgbe->wx;
unsigned int nhandled = 0;
unsigned int sub_irq;
u32 eicr;

eicr = wx_misc_isb(wx, WX_ISB_MISC);
if (eicr & TXGBE_PX_MISC_GPIO) {
sub_irq = irq_find_mapping(txgbe->misc.domain, TXGBE_IRQ_GPIO);
handle_nested_irq(sub_irq);
nhandled++;
}
if (eicr & (TXGBE_PX_MISC_ETH_LK | TXGBE_PX_MISC_ETH_LKDN |
TXGBE_PX_MISC_ETH_AN)) {
sub_irq = irq_find_mapping(txgbe->misc.domain, TXGBE_IRQ_LINK);
handle_nested_irq(sub_irq);
nhandled++;
}

wx_intr_enable(wx, TXGBE_INTR_MISC);
return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE);
}

static void txgbe_del_irq_domain(struct txgbe *txgbe)
{
int hwirq, virq;

for (hwirq = 0; hwirq < txgbe->misc.nirqs; hwirq++) {
virq = irq_find_mapping(txgbe->misc.domain, hwirq);
irq_dispose_mapping(virq);
}

irq_domain_remove(txgbe->misc.domain);
}

void txgbe_free_misc_irq(struct txgbe *txgbe)
{
free_irq(txgbe->gpio_irq, txgbe);
free_irq(txgbe->link_irq, txgbe);
free_irq(txgbe->misc.irq, txgbe);
txgbe_del_irq_domain(txgbe);
}

int txgbe_setup_misc_irq(struct txgbe *txgbe)
{
struct wx *wx = txgbe->wx;
int hwirq, err;

txgbe->misc.nirqs = 2;
txgbe->misc.domain = irq_domain_add_simple(NULL, txgbe->misc.nirqs, 0,
&txgbe_misc_irq_domain_ops, txgbe);
if (!txgbe->misc.domain)
return -ENOMEM;

for (hwirq = 0; hwirq < txgbe->misc.nirqs; hwirq++)
irq_create_mapping(txgbe->misc.domain, hwirq);

txgbe->misc.chip = txgbe_irq_chip;
if (wx->pdev->msix_enabled)
txgbe->misc.irq = wx->msix_entry->vector;
else
txgbe->misc.irq = wx->pdev->irq;

err = request_threaded_irq(txgbe->misc.irq, NULL,
txgbe_misc_irq_handle,
IRQF_ONESHOT,
wx->netdev->name, txgbe);
if (err)
goto del_misc_irq;

err = txgbe_request_gpio_irq(txgbe);
if (err)
goto free_msic_irq;

err = txgbe_request_link_irq(txgbe);
if (err)
goto free_gpio_irq;

return 0;

free_gpio_irq:
free_irq(txgbe->gpio_irq, txgbe);
free_msic_irq:
free_irq(txgbe->misc.irq, txgbe);
del_misc_irq:
txgbe_del_irq_domain(txgbe);

return err;
}
2 changes: 2 additions & 0 deletions drivers/net/ethernet/wangxun/txgbe/txgbe_irq.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@

void txgbe_irq_enable(struct wx *wx, bool queues);
int txgbe_request_irq(struct wx *wx);
void txgbe_free_misc_irq(struct txgbe *txgbe);
int txgbe_setup_misc_irq(struct txgbe *txgbe);
12 changes: 11 additions & 1 deletion drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,7 @@ static void txgbe_shutdown(struct pci_dev *pdev)
int txgbe_setup_tc(struct net_device *dev, u8 tc)
{
struct wx *wx = netdev_priv(dev);
struct txgbe *txgbe = wx->priv;

/* Hardware has to reinitialize queues and interrupts to
* match packet buffer alignment. Unfortunately, the
Expand All @@ -402,6 +403,7 @@ int txgbe_setup_tc(struct net_device *dev, u8 tc)
else
txgbe_reset(wx);

txgbe_free_misc_irq(txgbe);
wx_clear_interrupt_scheme(wx);

if (tc)
Expand All @@ -410,6 +412,7 @@ int txgbe_setup_tc(struct net_device *dev, u8 tc)
netdev_reset_tc(dev);

wx_init_interrupt_scheme(wx);
txgbe_setup_misc_irq(txgbe);

if (netif_running(dev))
txgbe_open(dev);
Expand Down Expand Up @@ -625,10 +628,14 @@ static int txgbe_probe(struct pci_dev *pdev,
txgbe->wx = wx;
wx->priv = txgbe;

err = txgbe_init_phy(txgbe);
err = txgbe_setup_misc_irq(txgbe);
if (err)
goto err_release_hw;

err = txgbe_init_phy(txgbe);
if (err)
goto err_free_misc_irq;

err = register_netdev(netdev);
if (err)
goto err_remove_phy;
Expand All @@ -655,6 +662,8 @@ static int txgbe_probe(struct pci_dev *pdev,

err_remove_phy:
txgbe_remove_phy(txgbe);
err_free_misc_irq:
txgbe_free_misc_irq(txgbe);
err_release_hw:
wx_clear_interrupt_scheme(wx);
wx_control_hw(wx, false);
Expand Down Expand Up @@ -687,6 +696,7 @@ static void txgbe_remove(struct pci_dev *pdev)
unregister_netdev(netdev);

txgbe_remove_phy(txgbe);
txgbe_free_misc_irq(txgbe);

pci_release_selected_regions(pdev,
pci_select_bars(pdev, IORESOURCE_MEM));
Expand Down
59 changes: 21 additions & 38 deletions drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,21 @@ static int txgbe_phylink_init(struct txgbe *txgbe)
return 0;
}

irqreturn_t txgbe_link_irq_handler(int irq, void *data)
{
struct txgbe *txgbe = data;
struct wx *wx = txgbe->wx;
u32 status;
bool up;

status = rd32(wx, TXGBE_CFG_PORT_ST);
up = !!(status & TXGBE_CFG_PORT_ST_LINK_UP);

phylink_mac_change(wx->phylink, up);

return IRQ_HANDLED;
}

static int txgbe_gpio_get(struct gpio_chip *chip, unsigned int offset)
{
struct wx *wx = gpiochip_get_data(chip);
Expand Down Expand Up @@ -437,7 +452,7 @@ static int txgbe_gpio_set_type(struct irq_data *d, unsigned int type)
}

static const struct irq_chip txgbe_gpio_irq_chip = {
.name = "txgbe_gpio_irq",
.name = "txgbe-gpio-irq",
.irq_ack = txgbe_gpio_irq_ack,
.irq_mask = txgbe_gpio_irq_mask,
.irq_unmask = txgbe_gpio_irq_unmask,
Expand All @@ -446,20 +461,14 @@ static const struct irq_chip txgbe_gpio_irq_chip = {
GPIOCHIP_IRQ_RESOURCE_HELPERS,
};

static void txgbe_irq_handler(struct irq_desc *desc)
irqreturn_t txgbe_gpio_irq_handler(int irq, void *data)
{
struct irq_chip *chip = irq_desc_get_chip(desc);
struct wx *wx = irq_desc_get_handler_data(desc);
struct txgbe *txgbe = wx->priv;
struct txgbe *txgbe = data;
struct wx *wx = txgbe->wx;
irq_hw_number_t hwirq;
unsigned long gpioirq;
struct gpio_chip *gc;
unsigned long flags;
u32 eicr;

eicr = wx_misc_isb(wx, WX_ISB_MISC);

chained_irq_enter(chip, desc);

gpioirq = rd32(wx, WX_GPIO_INTSTATUS);

Expand All @@ -468,7 +477,7 @@ static void txgbe_irq_handler(struct irq_desc *desc)
int gpio = irq_find_mapping(gc->irq.domain, hwirq);
u32 irq_type = irq_get_trigger_type(gpio);

generic_handle_domain_irq(gc->irq.domain, hwirq);
handle_nested_irq(gpio);

if ((irq_type & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) {
raw_spin_lock_irqsave(&wx->gpio_lock, flags);
Expand All @@ -477,17 +486,7 @@ static void txgbe_irq_handler(struct irq_desc *desc)
}
}

chained_irq_exit(chip, desc);

if (eicr & (TXGBE_PX_MISC_ETH_LK | TXGBE_PX_MISC_ETH_LKDN |
TXGBE_PX_MISC_ETH_AN)) {
u32 reg = rd32(wx, TXGBE_CFG_PORT_ST);

phylink_mac_change(wx->phylink, !!(reg & TXGBE_CFG_PORT_ST_LINK_UP));
}

/* unmask interrupt */
wx_intr_enable(wx, TXGBE_INTR_MISC);
return IRQ_HANDLED;
}

static int txgbe_gpio_init(struct txgbe *txgbe)
Expand Down Expand Up @@ -524,19 +523,6 @@ static int txgbe_gpio_init(struct txgbe *txgbe)

girq = &gc->irq;
gpio_irq_chip_set_chip(girq, &txgbe_gpio_irq_chip);
girq->parent_handler = txgbe_irq_handler;
girq->parent_handler_data = wx;
girq->num_parents = 1;
girq->parents = devm_kcalloc(dev, girq->num_parents,
sizeof(*girq->parents), GFP_KERNEL);
if (!girq->parents)
return -ENOMEM;

/* now only suuported on MSI-X interrupt */
if (!wx->msix_entry)
return -EPERM;

girq->parents[0] = wx->msix_entry->vector;
girq->default_type = IRQ_TYPE_NONE;
girq->handler = handle_bad_irq;

Expand Down Expand Up @@ -754,8 +740,6 @@ int txgbe_init_phy(struct txgbe *txgbe)
goto err_unregister_i2c;
}

wx->msix_in_use = true;

return 0;

err_unregister_i2c:
Expand Down Expand Up @@ -788,5 +772,4 @@ void txgbe_remove_phy(struct txgbe *txgbe)
phylink_destroy(txgbe->wx->phylink);
xpcs_destroy(txgbe->xpcs);
software_node_unregister_node_group(txgbe->nodes.group);
txgbe->wx->msix_in_use = false;
}
2 changes: 2 additions & 0 deletions drivers/net/ethernet/wangxun/txgbe/txgbe_phy.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#ifndef _TXGBE_PHY_H_
#define _TXGBE_PHY_H_

irqreturn_t txgbe_gpio_irq_handler(int irq, void *data);
irqreturn_t txgbe_link_irq_handler(int irq, void *data);
int txgbe_init_phy(struct txgbe *txgbe);
void txgbe_remove_phy(struct txgbe *txgbe);

Expand Down
Loading

0 comments on commit aefd013

Please sign in to comment.