Commit 89f4eb95 authored by Ke Chen's avatar Ke Chen Committed by Wang Wensheng
Browse files

roh/hns3: Add support for roh abnormal interruption

driver inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/I5WKYW



-----------------------------------------------------------------------

This patch adds initialization and deinitialization for roh
interrupt. This interrupt will be used to handle link event
reporting.

The ROH core provides a mechanism for processing ROH event
notifications, which is used by the driver to report link
events to roh core.

Signed-off-by: default avatarKe Chen <chenke54@huawei.com>
Reviewed-by: default avatarGang Zhang <gang.zhang@huawei.com>
Reviewed-by: default avatarYefeng Yan <yanyefeng@huawei.com>
Reviewed-by: default avatarJingchao Dai <daijingchao1@huawei.com>
parent aa4a5ce0
Loading
Loading
Loading
Loading
+23 −0
Original line number Diff line number Diff line
@@ -574,6 +574,29 @@ enum roh_link_status roh_device_query_link_status(struct roh_device *device)
	return device->link_status;
}

static void roh_update_link_status(struct roh_device *device, u32 ls)
{
	device->link_status = ls;
}

void roh_event_notify(struct roh_event *event)
{
	struct roh_device *device = event->device;

	switch (event->type) {
	case ROH_EVENT_LINK_UP:
		roh_update_link_status(device, ROH_LINK_UP);
		break;
	case ROH_EVENT_LINK_DOWN:
		roh_update_link_status(device, ROH_LINK_DOWN);
		break;
	default:
		pr_err("roh_core: not support event type(%d).\n", event->type);
		break;
	}
}
EXPORT_SYMBOL(roh_event_notify);

int roh_core_init(void)
{
	int ret;
+12 −0
Original line number Diff line number Diff line
@@ -95,6 +95,16 @@ struct roh_device {
	struct roh_mib_stats *hw_private_stats;
};

enum roh_event_type {
	ROH_EVENT_LINK_DOWN = 0,
	ROH_EVENT_LINK_UP
};

struct roh_event {
	struct roh_device *device;
	enum roh_event_type type;
};

static inline bool roh_device_try_get(struct roh_device *device)
{
	return refcount_inc_not_zero(&device->refcount);
@@ -108,6 +118,8 @@ void roh_dealloc_device(struct roh_device *device);
int roh_register_device(struct roh_device *device);
void roh_unregister_device(struct roh_device *device);

void roh_event_notify(struct roh_event *event);

int roh_core_init(void);
void roh_core_cleanup(void);

+1 −0
Original line number Diff line number Diff line
@@ -9,5 +9,6 @@ ccflags-y += -I $(srctree)/drivers/roh/hw/hns3

hns-roh-v1-objs := hns3_cmdq.o	\
		   hns3_verbs.o	\
		   hns3_intr.o	\
		   hns3_main.o
obj-$(CONFIG_ROH_HNS) += hns-roh-v1.o
+135 −0
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@
#include <linux/delay.h>

#include "core.h"
#include "hns3_device.h"
#include "hns3_common.h"
#include "hns3_cmdq.h"
#include "hns3_reg.h"
@@ -315,3 +316,137 @@ int hns3_roh_cmdq_send(struct hns3_roh_device *hroh_dev, struct hns3_roh_desc *d

	return ret;
}

int hns3_roh_get_link_status(struct hns3_roh_device *hroh_dev, u32 *link_status)
{
	struct hns3_roh_query_link_status_info *req;
	struct hns3_roh_desc desc;
	u32 link_val;
	int ret;

	hns3_roh_cmdq_setup_basic_desc(&desc, HNS3_ROH_OPC_QUERY_PORT_LINK_STATUS, true);
	ret = hns3_roh_cmdq_send(hroh_dev, &desc, 1);
	if (ret) {
		dev_err(hroh_dev->dev, "failed to query link status, ret = %d\n", ret);
		return ret;
	}

	req = (struct hns3_roh_query_link_status_info *)desc.data;
	link_val = le32_to_cpu(req->query_link_status);

	*link_status = link_val ? HNS3_ROH_LINK_STATUS_UP : HNS3_ROH_LINK_STATUS_DOWN;

	return 0;
}

static void hns3_roh_dispatch_event(struct hns3_roh_device *hroh_dev, enum roh_event_type type)
{
	struct roh_event event = {0};

	event.device = &hroh_dev->roh_dev;
	event.type = type;
	roh_event_notify(&event);
}

void hns3_roh_update_link_status(struct hns3_roh_device *hroh_dev)
{
	u32 state = HNS3_ROH_LINK_STATUS_DOWN;
	enum roh_event_type type;
	int ret;

	if (test_and_set_bit(HNS3_ROH_SW_STATE_LINK_UPDATING, &hroh_dev->state))
		return;

	ret = hns3_roh_get_link_status(hroh_dev, &state);
	if (ret) {
		state = HNS3_ROH_LINK_STATUS_DOWN;
		clear_bit(HNS3_ROH_SW_STATE_LINK_UPDATING, &hroh_dev->state);
		return;
	}

	type = (state == HNS3_ROH_LINK_STATUS_DOWN) ? ROH_EVENT_LINK_DOWN : ROH_EVENT_LINK_UP;
	hns3_roh_dispatch_event(hroh_dev, type);

	clear_bit(HNS3_ROH_SW_STATE_LINK_UPDATING, &hroh_dev->state);
}

static void hns3_roh_link_fail_parse(struct hns3_roh_device *hroh_dev,
				     u8 link_fail_code)
{
	switch (link_fail_code) {
	case HNS3_ROH_LF_REF_CLOCK_LOST:
		dev_warn(hroh_dev->dev, "reference clock lost!\n");
		break;
	case HNS3_ROH_LF_XSFP_TX_DISABLE:
		dev_warn(hroh_dev->dev, "SFP tx is disabled!\n");
		break;
	case HNS3_ROH_LF_XSFP_ABSENT:
		dev_warn(hroh_dev->dev, "SFP is absent!\n");
		break;
	default:
		break;
	}
}

static void hns3_roh_handle_link_change_event(struct hns3_roh_device *hroh_dev,
					      struct hns3_roh_mbx_vf_to_pf_cmd *req)
{
	int link_status = req->msg.subcode;

	hns3_roh_task_schedule(hroh_dev, 0);

	if (link_status == HNS3_ROH_LINK_STATUS_DOWN)
		hns3_roh_link_fail_parse(hroh_dev, req->msg.data[0]);
}

static bool hns3_roh_cmd_crq_empty(struct hns3_roh_device *hroh_dev)
{
	struct hns3_roh_priv *priv = (struct hns3_roh_priv *)hroh_dev->priv;
	u32 tail = hns3_roh_read(hroh_dev, HNS3_ROH_RX_CMDQ_TAIL_REG);

	return tail == priv->cmdq.crq.next_to_use;
}

void hns3_roh_mbx_handler(struct hns3_roh_device *hroh_dev)
{
	struct hns3_roh_priv *priv = (struct hns3_roh_priv *)hroh_dev->priv;
	struct hns3_roh_cmdq_ring *crq = &priv->cmdq.crq;
	struct hns3_roh_mbx_vf_to_pf_cmd *req;
	struct hns3_roh_desc *desc;
	unsigned int flag;

	/* handle all the mailbox requests in the queue */
	while (!hns3_roh_cmd_crq_empty(hroh_dev)) {
		desc = &crq->desc[crq->next_to_use];
		req = (struct hns3_roh_mbx_vf_to_pf_cmd *)desc->data;

		flag = le16_to_cpu(crq->desc[crq->next_to_use].flag);
		if (unlikely(!hns3_roh_get_bit(flag, HNS3_ROH_CMDQ_RX_OUTVLD_B))) {
			dev_warn(hroh_dev->dev,
				 "dropped invalid mbx message, code = %u\n",
				 req->msg.code);

			/* dropping/not processing this invalid message */
			crq->desc[crq->next_to_use].flag = 0;
			hns3_roh_mbx_ring_ptr_move_crq(crq);
			continue;
		}

		switch (req->msg.code) {
		case HNS3_ROH_MBX_PUSH_LINK_STATUS:
			hns3_roh_handle_link_change_event(hroh_dev, req);
			break;
		default:
			dev_err(hroh_dev->dev,
				"un-supported mbx message, code = %u\n",
				req->msg.code);
			break;
		}

		crq->desc[crq->next_to_use].flag = 0;
		hns3_roh_mbx_ring_ptr_move_crq(crq);
	}

	/* write back CMDQ_RQ header ptr, M7 need this ptr */
	hns3_roh_write(hroh_dev, HNS3_ROH_RX_CMDQ_HEAD_REG, crq->next_to_use);
}
+46 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ enum { HNS3_ROH_CMDQ_CRQ = 0, HNS3_ROH_CMDQ_CSQ };

enum hns3_roh_opcode_type {
	HNS3_ROH_OPC_GET_INTR_INFO = 0x0023,
	HNS3_ROH_OPC_QUERY_PORT_LINK_STATUS = 0x038a,
	HNS3_ROH_OPC_SET_EID = 0x9001,
	HNS3_ROH_OPC_GET_GUID = 0x9002,
	HNS3_ROH_OPC_QUERY_MIB_PUBLIC = 0x9005,
@@ -44,6 +45,24 @@ enum hns3_roh_cmd_return_status {
	HNS3_ROH_CMD_EXEC_TIMEOUT
};

enum hns3_roh_mbx_opcode {
	HNS3_ROH_MBX_PUSH_LINK_STATUS = 201 /* (M7 -> PF) get port link status */
};

struct hns3_roh_get_intr_info {
	__le16 tqp_num;
	__le16 packet_buffer_cell_cnt;
	__le16 msixcap_localid_ba_nic;
	__le16 msixcap_localid_number_nic;
	__le16 pf_intr_vector_number_roce;
	__le16 pf_own_fun_number;
	__le16 tx_pkt_buffer_cell_cnt;
	__le16 delay_value_cell_num;
	__le16 tqp_number_1k;
	__le16 pf_intr_vector_number_roh;
	u8 rsv[4];
};

struct hns3_roh_set_eid_info {
	__le32 base_eid;
	__le32 num_eid;
@@ -55,6 +74,31 @@ struct hns3_roh_get_guid_info {
	u8 rsv[8];
};

struct hns3_roh_query_link_status_info {
	__le32 query_link_status;
	u8 rsv[20];
};

#define HNS3_ROH_MBX_MAX_MSG_SIZE 14

struct hns3_roh_vf_to_pf_msg {
	u8 code;
	struct {
		u8 subcode;
		u8 data[HNS3_ROH_MBX_MAX_MSG_SIZE];
	};
};

struct hns3_roh_mbx_vf_to_pf_cmd {
	u8 rsv;
	u8 mbx_src_vfid; /* Auto filled by IMP */
	u8 mbx_need_resp;
	u8 rsv1;
	u8 msg_len;
	u8 rsv2[3];
	struct hns3_roh_vf_to_pf_msg msg;
};

static inline void hns3_roh_mbx_ring_ptr_move_crq(struct hns3_roh_cmdq_ring *crq)
{
	crq->next_to_use = (crq->next_to_use + 1) % crq->desc_num;
@@ -66,5 +110,7 @@ int hns3_roh_cmdq_send(struct hns3_roh_device *hroh_dev,
		       struct hns3_roh_desc *desc, int num);
void hns3_roh_cmdq_setup_basic_desc(struct hns3_roh_desc *desc,
				    enum hns3_roh_opcode_type opcode, bool is_read);
int hns3_roh_get_link_status(struct hns3_roh_device *hroh_dev, u32 *link_status);
void hns3_roh_update_link_status(struct hns3_roh_device *hroh_dev);

#endif /* __HNS3_ROH_CMDQ_H__ */
Loading