Commit 34392979 authored by Jiawen Wu's avatar Jiawen Wu Committed by Paolo Abeni
Browse files

net: txgbe: Support to handle GPIO IRQs for AML devices



The driver needs to handle GPIO interrupts to identify SFP module and
configure PHY by sending mailbox messages to firmware.

Since the SFP module needs to wait for ready to get information when it
is inserted, workqueue is added to handle delayed tasks. And each SW-FW
interaction takes time to wait, so they are processed in the workqueue
instead of IRQ handler function.

Signed-off-by: default avatarJiawen Wu <jiawenwu@trustnetic.com>
Reviewed-by: default avatarSimon Horman <horms@kernel.org>
Link: https://patch.msgid.link/399624AF221E8E28+20250521064402.22348-6-jiawenwu@trustnetic.com


Signed-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
parent 6f8b4c01
Loading
Loading
Loading
Loading
+31 −0
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@
#include <net/ip6_checksum.h>
#include <net/page_pool/helpers.h>
#include <net/inet_ecn.h>
#include <linux/workqueue.h>
#include <linux/iopoll.h>
#include <linux/sctp.h>
#include <linux/pci.h>
@@ -3095,5 +3096,35 @@ void wx_set_ring(struct wx *wx, u32 new_tx_count,
}
EXPORT_SYMBOL(wx_set_ring);

void wx_service_event_schedule(struct wx *wx)
{
	if (!test_and_set_bit(WX_STATE_SERVICE_SCHED, wx->state))
		queue_work(system_power_efficient_wq, &wx->service_task);
}
EXPORT_SYMBOL(wx_service_event_schedule);

void wx_service_event_complete(struct wx *wx)
{
	if (WARN_ON(!test_bit(WX_STATE_SERVICE_SCHED, wx->state)))
		return;

	/* flush memory to make sure state is correct before next watchdog */
	smp_mb__before_atomic();
	clear_bit(WX_STATE_SERVICE_SCHED, wx->state);
}
EXPORT_SYMBOL(wx_service_event_complete);

void wx_service_timer(struct timer_list *t)
{
	struct wx *wx = from_timer(wx, t, service_timer);
	unsigned long next_event_offset = HZ * 2;

	/* Reset the timer */
	mod_timer(&wx->service_timer, next_event_offset + jiffies);

	wx_service_event_schedule(wx);
}
EXPORT_SYMBOL(wx_service_timer);

MODULE_DESCRIPTION("Common library for Wangxun(R) Ethernet drivers.");
MODULE_LICENSE("GPL");
+3 −0
Original line number Diff line number Diff line
@@ -38,5 +38,8 @@ netdev_features_t wx_features_check(struct sk_buff *skb,
				    netdev_features_t features);
void wx_set_ring(struct wx *wx, u32 new_tx_count,
		 u32 new_rx_count, struct wx_ring *temp_ring);
void wx_service_event_schedule(struct wx *wx);
void wx_service_event_complete(struct wx *wx);
void wx_service_timer(struct timer_list *t);

#endif /* _WX_LIB_H_ */
+8 −0
Original line number Diff line number Diff line
@@ -1154,6 +1154,7 @@ enum wx_state {
	WX_STATE_SWFW_BUSY,
	WX_STATE_PTP_RUNNING,
	WX_STATE_PTP_TX_IN_PROGRESS,
	WX_STATE_SERVICE_SCHED,
	WX_STATE_NBITS		/* must be last */
};

@@ -1197,6 +1198,8 @@ enum wx_pf_flags {
	WX_FLAG_RX_HWTSTAMP_ENABLED,
	WX_FLAG_RX_HWTSTAMP_IN_REGISTER,
	WX_FLAG_PTP_PPS_ENABLED,
	WX_FLAG_NEED_LINK_CONFIG,
	WX_FLAG_NEED_SFP_RESET,
	WX_PF_FLAGS_NBITS               /* must be last */
};

@@ -1235,6 +1238,8 @@ struct wx {

	/* PHY stuff */
	bool notify_down;
	int adv_speed;
	int adv_duplex;
	unsigned int link;
	int speed;
	int duplex;
@@ -1332,6 +1337,9 @@ struct wx {
	struct ptp_clock_info ptp_caps;
	struct kernel_hwtstamp_config tstamp_config;
	struct sk_buff *ptp_tx_skb;

	struct timer_list service_timer;
	struct work_struct service_task;
};

#define WX_INTR_ALL (~0ULL)
+204 −0
Original line number Diff line number Diff line
@@ -13,6 +13,96 @@
#include "txgbe_aml.h"
#include "txgbe_hw.h"

void txgbe_gpio_init_aml(struct wx *wx)
{
	u32 status;

	wr32(wx, WX_GPIO_INTTYPE_LEVEL, TXGBE_GPIOBIT_2 | TXGBE_GPIOBIT_3);
	wr32(wx, WX_GPIO_INTEN, TXGBE_GPIOBIT_2 | TXGBE_GPIOBIT_3);

	status = rd32(wx, WX_GPIO_INTSTATUS);
	for (int i = 0; i < 6; i++) {
		if (status & BIT(i))
			wr32(wx, WX_GPIO_EOI, BIT(i));
	}
}

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

	wr32(wx, WX_GPIO_INTMASK, 0xFF);
	status = rd32(wx, WX_GPIO_INTSTATUS);
	if (status & TXGBE_GPIOBIT_2) {
		set_bit(WX_FLAG_NEED_SFP_RESET, wx->flags);
		wr32(wx, WX_GPIO_EOI, TXGBE_GPIOBIT_2);
		wx_service_event_schedule(wx);
	}
	if (status & TXGBE_GPIOBIT_3) {
		set_bit(WX_FLAG_NEED_LINK_CONFIG, wx->flags);
		wx_service_event_schedule(wx);
		wr32(wx, WX_GPIO_EOI, TXGBE_GPIOBIT_3);
	}

	wr32(wx, WX_GPIO_INTMASK, 0);
	return IRQ_HANDLED;
}

static int txgbe_identify_sfp_hostif(struct wx *wx, struct txgbe_hic_i2c_read *buffer)
{
	buffer->hdr.cmd = FW_READ_SFP_INFO_CMD;
	buffer->hdr.buf_len = sizeof(struct txgbe_hic_i2c_read) -
			      sizeof(struct wx_hic_hdr);
	buffer->hdr.cmd_or_resp.cmd_resv = FW_CEM_CMD_RESERVED;

	return wx_host_interface_command(wx, (u32 *)buffer,
					 sizeof(struct txgbe_hic_i2c_read),
					 WX_HI_COMMAND_TIMEOUT, true);
}

static int txgbe_set_phy_link_hostif(struct wx *wx, int speed, int autoneg, int duplex)
{
	struct txgbe_hic_ephy_setlink buffer;

	buffer.hdr.cmd = FW_PHY_SET_LINK_CMD;
	buffer.hdr.buf_len = sizeof(struct txgbe_hic_ephy_setlink) -
			     sizeof(struct wx_hic_hdr);
	buffer.hdr.cmd_or_resp.cmd_resv = FW_CEM_CMD_RESERVED;

	switch (speed) {
	case SPEED_25000:
		buffer.speed = TXGBE_LINK_SPEED_25GB_FULL;
		break;
	case SPEED_10000:
		buffer.speed = TXGBE_LINK_SPEED_10GB_FULL;
		break;
	}

	buffer.fec_mode = TXGBE_PHY_FEC_AUTO;
	buffer.autoneg = autoneg;
	buffer.duplex = duplex;

	return wx_host_interface_command(wx, (u32 *)&buffer, sizeof(buffer),
					 WX_HI_COMMAND_TIMEOUT, true);
}

static void txgbe_get_link_capabilities(struct wx *wx)
{
	struct txgbe *txgbe = wx->priv;

	if (test_bit(PHY_INTERFACE_MODE_25GBASER, txgbe->sfp_interfaces))
		wx->adv_speed = SPEED_25000;
	else if (test_bit(PHY_INTERFACE_MODE_10GBASER, txgbe->sfp_interfaces))
		wx->adv_speed = SPEED_10000;
	else
		wx->adv_speed = SPEED_UNKNOWN;

	wx->adv_duplex = wx->adv_speed == SPEED_UNKNOWN ?
			 DUPLEX_HALF : DUPLEX_FULL;
}

static void txgbe_get_phy_link(struct wx *wx, int *speed)
{
	u32 status;
@@ -28,6 +118,120 @@ static void txgbe_get_phy_link(struct wx *wx, int *speed)
		*speed = SPEED_UNKNOWN;
}

int txgbe_set_phy_link(struct wx *wx)
{
	int speed, err;
	u32 gpio;

	/* Check RX signal */
	gpio = rd32(wx, WX_GPIO_EXT);
	if (gpio & TXGBE_GPIOBIT_3)
		return -ENODEV;

	txgbe_get_link_capabilities(wx);
	if (wx->adv_speed == SPEED_UNKNOWN)
		return -ENODEV;

	txgbe_get_phy_link(wx, &speed);
	if (speed == wx->adv_speed)
		return 0;

	err = txgbe_set_phy_link_hostif(wx, wx->adv_speed, 0, wx->adv_duplex);
	if (err) {
		wx_err(wx, "Failed to setup link\n");
		return err;
	}

	return 0;
}

static int txgbe_sfp_to_linkmodes(struct wx *wx, struct txgbe_sfp_id *id)
{
	__ETHTOOL_DECLARE_LINK_MODE_MASK(modes) = { 0, };
	DECLARE_PHY_INTERFACE_MASK(interfaces);
	struct txgbe *txgbe = wx->priv;

	if (id->com_25g_code & (TXGBE_SFF_25GBASESR_CAPABLE |
				TXGBE_SFF_25GBASEER_CAPABLE |
				TXGBE_SFF_25GBASELR_CAPABLE)) {
		phylink_set(modes, 25000baseSR_Full);
		__set_bit(PHY_INTERFACE_MODE_25GBASER, interfaces);
	}
	if (id->com_10g_code & TXGBE_SFF_10GBASESR_CAPABLE) {
		phylink_set(modes, 10000baseSR_Full);
		__set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces);
	}
	if (id->com_10g_code & TXGBE_SFF_10GBASELR_CAPABLE) {
		phylink_set(modes, 10000baseLR_Full);
		__set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces);
	}

	if (phy_interface_empty(interfaces)) {
		wx_err(wx, "unsupported SFP module\n");
		return -EINVAL;
	}

	phylink_set(modes, Pause);
	phylink_set(modes, Asym_Pause);
	phylink_set(modes, FIBRE);
	txgbe->link_port = PORT_FIBRE;

	if (!linkmode_equal(txgbe->sfp_support, modes)) {
		linkmode_copy(txgbe->sfp_support, modes);
		phy_interface_and(txgbe->sfp_interfaces,
				  wx->phylink_config.supported_interfaces,
				  interfaces);
		linkmode_copy(txgbe->advertising, modes);

		set_bit(WX_FLAG_NEED_LINK_CONFIG, wx->flags);
	}

	return 0;
}

int txgbe_identify_sfp(struct wx *wx)
{
	struct txgbe_hic_i2c_read buffer;
	struct txgbe_sfp_id *id;
	int err = 0;
	u32 gpio;

	gpio = rd32(wx, WX_GPIO_EXT);
	if (gpio & TXGBE_GPIOBIT_2)
		return -ENODEV;

	err = txgbe_identify_sfp_hostif(wx, &buffer);
	if (err) {
		wx_err(wx, "Failed to identify SFP module\n");
		return err;
	}

	id = &buffer.id;
	if (id->identifier != TXGBE_SFF_IDENTIFIER_SFP) {
		wx_err(wx, "Invalid SFP module\n");
		return -ENODEV;
	}

	err = txgbe_sfp_to_linkmodes(wx, id);
	if (err)
		return err;

	if (gpio & TXGBE_GPIOBIT_3)
		set_bit(WX_FLAG_NEED_LINK_CONFIG, wx->flags);

	return 0;
}

void txgbe_setup_link(struct wx *wx)
{
	struct txgbe *txgbe = wx->priv;

	phy_interface_zero(txgbe->sfp_interfaces);
	linkmode_zero(txgbe->sfp_support);

	txgbe_identify_sfp(wx);
}

static void txgbe_get_link_state(struct phylink_config *config,
				 struct phylink_link_state *state)
{
+5 −0
Original line number Diff line number Diff line
@@ -4,6 +4,11 @@
#ifndef _TXGBE_AML_H_
#define _TXGBE_AML_H_

void txgbe_gpio_init_aml(struct wx *wx);
irqreturn_t txgbe_gpio_irq_handler_aml(int irq, void *data);
int txgbe_set_phy_link(struct wx *wx);
int txgbe_identify_sfp(struct wx *wx);
void txgbe_setup_link(struct wx *wx);
int txgbe_phylink_init_aml(struct txgbe *txgbe);

#endif /* _TXGBE_AML_H_ */
Loading