| // SPDX-License-Identifier: GPL-2.0+ |
| // Copyright (c) 2024 Hisilicon Limited. |
| |
| #include <linux/interrupt.h> |
| #include "hbg_irq.h" |
| #include "hbg_hw.h" |
| |
| static void hbg_irq_handle_err(struct hbg_priv *priv, |
| const struct hbg_irq_info *irq_info) |
| { |
| if (irq_info->need_print) |
| dev_err(&priv->pdev->dev, |
| "receive error interrupt: %s\n", irq_info->name); |
| |
| if (irq_info->need_reset) |
| hbg_err_reset_task_schedule(priv); |
| } |
| |
| static void hbg_irq_handle_tx(struct hbg_priv *priv, |
| const struct hbg_irq_info *irq_info) |
| { |
| napi_schedule(&priv->tx_ring.napi); |
| } |
| |
| static void hbg_irq_handle_rx(struct hbg_priv *priv, |
| const struct hbg_irq_info *irq_info) |
| { |
| napi_schedule(&priv->rx_ring.napi); |
| } |
| |
| static void hbg_irq_handle_rx_buf_val(struct hbg_priv *priv, |
| const struct hbg_irq_info *irq_info) |
| { |
| priv->stats.rx_fifo_less_empty_thrsld_cnt++; |
| } |
| |
| #define HBG_IRQ_I(name, handle) \ |
| {#name, HBG_INT_MSK_##name##_B, false, false, false, handle} |
| #define HBG_ERR_IRQ_I(name, need_print, ndde_reset) \ |
| {#name, HBG_INT_MSK_##name##_B, true, need_print, \ |
| ndde_reset, hbg_irq_handle_err} |
| |
| static const struct hbg_irq_info hbg_irqs[] = { |
| HBG_IRQ_I(RX, hbg_irq_handle_rx), |
| HBG_IRQ_I(TX, hbg_irq_handle_tx), |
| HBG_ERR_IRQ_I(TX_PKT_CPL, true, true), |
| HBG_ERR_IRQ_I(MAC_MII_FIFO_ERR, true, true), |
| HBG_ERR_IRQ_I(MAC_PCS_RX_FIFO_ERR, true, true), |
| HBG_ERR_IRQ_I(MAC_PCS_TX_FIFO_ERR, true, true), |
| HBG_ERR_IRQ_I(MAC_APP_RX_FIFO_ERR, true, true), |
| HBG_ERR_IRQ_I(MAC_APP_TX_FIFO_ERR, true, true), |
| HBG_ERR_IRQ_I(SRAM_PARITY_ERR, true, false), |
| HBG_ERR_IRQ_I(TX_AHB_ERR, true, true), |
| HBG_IRQ_I(RX_BUF_AVL, hbg_irq_handle_rx_buf_val), |
| HBG_ERR_IRQ_I(REL_BUF_ERR, true, false), |
| HBG_ERR_IRQ_I(TXCFG_AVL, false, false), |
| HBG_ERR_IRQ_I(TX_DROP, false, false), |
| HBG_ERR_IRQ_I(RX_DROP, false, false), |
| HBG_ERR_IRQ_I(RX_AHB_ERR, true, false), |
| HBG_ERR_IRQ_I(MAC_FIFO_ERR, true, true), |
| HBG_ERR_IRQ_I(RBREQ_ERR, true, true), |
| HBG_ERR_IRQ_I(WE_ERR, true, true), |
| }; |
| |
| static irqreturn_t hbg_irq_handle(int irq_num, void *p) |
| { |
| const struct hbg_irq_info *info; |
| struct hbg_priv *priv = p; |
| u32 status; |
| u32 i; |
| |
| status = hbg_hw_get_irq_status(priv); |
| for (i = 0; i < priv->vectors.info_array_len; i++) { |
| info = &priv->vectors.info_array[i]; |
| if (status & info->mask) { |
| if (!hbg_hw_irq_is_enabled(priv, info->mask)) |
| continue; |
| |
| hbg_hw_irq_enable(priv, info->mask, false); |
| hbg_hw_irq_clear(priv, info->mask); |
| |
| priv->vectors.stats_array[i]++; |
| if (info->irq_handle) |
| info->irq_handle(priv, info); |
| |
| if (info->re_enable) |
| hbg_hw_irq_enable(priv, info->mask, true); |
| } |
| } |
| |
| return IRQ_HANDLED; |
| } |
| |
| static const char *irq_names_map[HBG_VECTOR_NUM] = { "tx", "rx", |
| "err", "mdio" }; |
| |
| int hbg_irq_init(struct hbg_priv *priv) |
| { |
| struct hbg_vector *vectors = &priv->vectors; |
| struct device *dev = &priv->pdev->dev; |
| int ret, id; |
| u32 i; |
| |
| /* used pcim_enable_device(), so the vectors become device managed */ |
| ret = pci_alloc_irq_vectors(priv->pdev, HBG_VECTOR_NUM, HBG_VECTOR_NUM, |
| PCI_IRQ_MSI | PCI_IRQ_MSIX); |
| if (ret < 0) |
| return dev_err_probe(dev, ret, "failed to allocate vectors\n"); |
| |
| if (ret != HBG_VECTOR_NUM) |
| return dev_err_probe(dev, -EINVAL, |
| "requested %u MSI, but allocated %d MSI\n", |
| HBG_VECTOR_NUM, ret); |
| |
| /* mdio irq not requested, so the number of requested interrupts |
| * is HBG_VECTOR_NUM - 1. |
| */ |
| for (i = 0; i < HBG_VECTOR_NUM - 1; i++) { |
| id = pci_irq_vector(priv->pdev, i); |
| if (id < 0) |
| return dev_err_probe(dev, id, "failed to get irq id\n"); |
| |
| snprintf(vectors->name[i], sizeof(vectors->name[i]), "%s-%s-%s", |
| dev_driver_string(dev), pci_name(priv->pdev), |
| irq_names_map[i]); |
| |
| ret = devm_request_irq(dev, id, hbg_irq_handle, 0, |
| vectors->name[i], priv); |
| if (ret) |
| return dev_err_probe(dev, ret, |
| "failed to request irq: %s\n", |
| irq_names_map[i]); |
| } |
| |
| vectors->stats_array = devm_kcalloc(&priv->pdev->dev, |
| ARRAY_SIZE(hbg_irqs), |
| sizeof(u64), GFP_KERNEL); |
| if (!vectors->stats_array) |
| return -ENOMEM; |
| |
| vectors->info_array = hbg_irqs; |
| vectors->info_array_len = ARRAY_SIZE(hbg_irqs); |
| return 0; |
| } |