Commit 4d089035 authored by Jijie Shao's avatar Jijie Shao Committed by Paolo Abeni
Browse files

net: hibmcge: Add interrupt supported in this module



The driver supports four interrupts: TX interrupt, RX interrupt,
mdio interrupt, and error interrupt.

Actually, the driver does not use the mdio interrupt.
Therefore, the driver does not request the mdio interrupt.

The error interrupt distinguishes different error information
by using different masks. To distinguish different errors,
the statistics count is added for each error.

To ensure the consistency of the code process, masks are added for the
TX interrupt and RX interrupt.

This patch implements interrupt request, and provides a
unified entry for the interrupt handler function. However,
the specific interrupt handler function of each interrupt
is not implemented currently.

Because of pcim_enable_device(), the interrupt vector
is already device managed and does not need to be free actively.

Signed-off-by: default avatarJijie Shao <shaojijie@huawei.com>
Signed-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
parent a239b2b1
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@
#define HBG_STATUS_ENABLE		0x1
#define HBG_RX_SKIP1			0x00
#define HBG_RX_SKIP2			0x01
#define HBG_VECTOR_NUM			4

enum hbg_nic_state {
	HBG_NIC_STATE_EVENT_HANDLING = 0,
@@ -37,6 +38,22 @@ struct hbg_dev_specs {
	u32 rx_buf_size;
};

struct hbg_irq_info {
	const char *name;
	u32 mask;
	bool re_enable;
	bool need_print;
	u64 count;

	void (*irq_handle)(struct hbg_priv *priv, struct hbg_irq_info *info);
};

struct hbg_vector {
	char name[HBG_VECTOR_NUM][32];
	struct hbg_irq_info *info_array;
	u32 info_array_len;
};

struct hbg_mac {
	struct mii_bus *mdio_bus;
	struct phy_device *phydev;
@@ -55,6 +72,7 @@ struct hbg_priv {
	struct hbg_dev_specs dev_specs;
	unsigned long state;
	struct hbg_mac mac;
	struct hbg_vector vectors;
};

#endif
+57 −0
Original line number Diff line number Diff line
@@ -75,6 +75,63 @@ static int hbg_hw_dev_specs_init(struct hbg_priv *priv)
	return 0;
}

u32 hbg_hw_get_irq_status(struct hbg_priv *priv)
{
	u32 status;

	status = hbg_reg_read(priv, HBG_REG_CF_INTRPT_STAT_ADDR);

	hbg_field_modify(status, HBG_INT_MSK_TX_B,
			 hbg_reg_read(priv, HBG_REG_CF_IND_TXINT_STAT_ADDR));
	hbg_field_modify(status, HBG_INT_MSK_RX_B,
			 hbg_reg_read(priv, HBG_REG_CF_IND_RXINT_STAT_ADDR));

	return status;
}

void hbg_hw_irq_clear(struct hbg_priv *priv, u32 mask)
{
	if (FIELD_GET(HBG_INT_MSK_TX_B, mask))
		return hbg_reg_write(priv, HBG_REG_CF_IND_TXINT_CLR_ADDR, 0x1);

	if (FIELD_GET(HBG_INT_MSK_RX_B, mask))
		return hbg_reg_write(priv, HBG_REG_CF_IND_RXINT_CLR_ADDR, 0x1);

	return hbg_reg_write(priv, HBG_REG_CF_INTRPT_CLR_ADDR, mask);
}

bool hbg_hw_irq_is_enabled(struct hbg_priv *priv, u32 mask)
{
	if (FIELD_GET(HBG_INT_MSK_TX_B, mask))
		return hbg_reg_read(priv, HBG_REG_CF_IND_TXINT_MSK_ADDR);

	if (FIELD_GET(HBG_INT_MSK_RX_B, mask))
		return hbg_reg_read(priv, HBG_REG_CF_IND_RXINT_MSK_ADDR);

	return hbg_reg_read(priv, HBG_REG_CF_INTRPT_MSK_ADDR) & mask;
}

void hbg_hw_irq_enable(struct hbg_priv *priv, u32 mask, bool enable)
{
	u32 value;

	if (FIELD_GET(HBG_INT_MSK_TX_B, mask))
		return hbg_reg_write(priv,
				     HBG_REG_CF_IND_TXINT_MSK_ADDR, enable);

	if (FIELD_GET(HBG_INT_MSK_RX_B, mask))
		return hbg_reg_write(priv,
				     HBG_REG_CF_IND_RXINT_MSK_ADDR, enable);

	value = hbg_reg_read(priv, HBG_REG_CF_INTRPT_MSK_ADDR);
	if (enable)
		value |= mask;
	else
		value &= ~mask;

	hbg_reg_write(priv, HBG_REG_CF_INTRPT_MSK_ADDR, value);
}

void hbg_hw_adjust_link(struct hbg_priv *priv, u32 speed, u32 duplex)
{
	hbg_reg_write_field(priv, HBG_REG_PORT_MODE_ADDR,
+4 −0
Original line number Diff line number Diff line
@@ -45,5 +45,9 @@ int hbg_hw_event_notify(struct hbg_priv *priv,
			enum hbg_hw_event_type event_type);
int hbg_hw_init(struct hbg_priv *priv);
void hbg_hw_adjust_link(struct hbg_priv *priv, u32 speed, u32 duplex);
u32 hbg_hw_get_irq_status(struct hbg_priv *priv);
void hbg_hw_irq_clear(struct hbg_priv *priv, u32 mask);
bool hbg_hw_irq_is_enabled(struct hbg_priv *priv, u32 mask);
void hbg_hw_irq_enable(struct hbg_priv *priv, u32 mask, bool enable);

#endif
+115 −0
Original line number Diff line number Diff line
// 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,
			       struct hbg_irq_info *irq_info)
{
	if (irq_info->need_print)
		dev_err(&priv->pdev->dev,
			"receive error interrupt: %s\n", irq_info->name);
}

#define HBG_TXRX_IRQ_I(name, handle) \
	{#name, HBG_INT_MSK_##name##_B, false, false, 0, handle}
#define HBG_ERR_IRQ_I(name, need_print) \
	{#name, HBG_INT_MSK_##name##_B, true, need_print, 0, hbg_irq_handle_err}

static struct hbg_irq_info hbg_irqs[] = {
	HBG_TXRX_IRQ_I(RX, NULL),
	HBG_TXRX_IRQ_I(TX, NULL),
	HBG_ERR_IRQ_I(MAC_MII_FIFO_ERR, true),
	HBG_ERR_IRQ_I(MAC_PCS_RX_FIFO_ERR, true),
	HBG_ERR_IRQ_I(MAC_PCS_TX_FIFO_ERR, true),
	HBG_ERR_IRQ_I(MAC_APP_RX_FIFO_ERR, true),
	HBG_ERR_IRQ_I(MAC_APP_TX_FIFO_ERR, true),
	HBG_ERR_IRQ_I(SRAM_PARITY_ERR, true),
	HBG_ERR_IRQ_I(TX_AHB_ERR, true),
	HBG_ERR_IRQ_I(RX_BUF_AVL, false),
	HBG_ERR_IRQ_I(REL_BUF_ERR, true),
	HBG_ERR_IRQ_I(TXCFG_AVL, false),
	HBG_ERR_IRQ_I(TX_DROP, false),
	HBG_ERR_IRQ_I(RX_DROP, false),
	HBG_ERR_IRQ_I(RX_AHB_ERR, true),
	HBG_ERR_IRQ_I(MAC_FIFO_ERR, false),
	HBG_ERR_IRQ_I(RBREQ_ERR, false),
	HBG_ERR_IRQ_I(WE_ERR, false),
};

static irqreturn_t hbg_irq_handle(int irq_num, void *p)
{
	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);

			info->count++;
			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->info_array = hbg_irqs;
	vectors->info_array_len = ARRAY_SIZE(hbg_irqs);
	return 0;
}
+11 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0+ */
/* Copyright (c) 2024 Hisilicon Limited. */

#ifndef __HBG_IRQ_H
#define __HBG_IRQ_H

#include "hbg_common.h"

int hbg_irq_init(struct hbg_priv *priv);

#endif
Loading