Skip to content

Commit 3439297

Browse files
Jiawen WuPaolo Abeni
authored andcommitted
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: Jiawen Wu <[email protected]> Reviewed-by: Simon Horman <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Paolo Abeni <[email protected]>
1 parent 6f8b4c0 commit 3439297

File tree

8 files changed

+433
-2
lines changed

8 files changed

+433
-2
lines changed

drivers/net/ethernet/wangxun/libwx/wx_lib.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <net/ip6_checksum.h>
66
#include <net/page_pool/helpers.h>
77
#include <net/inet_ecn.h>
8+
#include <linux/workqueue.h>
89
#include <linux/iopoll.h>
910
#include <linux/sctp.h>
1011
#include <linux/pci.h>
@@ -3095,5 +3096,35 @@ void wx_set_ring(struct wx *wx, u32 new_tx_count,
30953096
}
30963097
EXPORT_SYMBOL(wx_set_ring);
30973098

3099+
void wx_service_event_schedule(struct wx *wx)
3100+
{
3101+
if (!test_and_set_bit(WX_STATE_SERVICE_SCHED, wx->state))
3102+
queue_work(system_power_efficient_wq, &wx->service_task);
3103+
}
3104+
EXPORT_SYMBOL(wx_service_event_schedule);
3105+
3106+
void wx_service_event_complete(struct wx *wx)
3107+
{
3108+
if (WARN_ON(!test_bit(WX_STATE_SERVICE_SCHED, wx->state)))
3109+
return;
3110+
3111+
/* flush memory to make sure state is correct before next watchdog */
3112+
smp_mb__before_atomic();
3113+
clear_bit(WX_STATE_SERVICE_SCHED, wx->state);
3114+
}
3115+
EXPORT_SYMBOL(wx_service_event_complete);
3116+
3117+
void wx_service_timer(struct timer_list *t)
3118+
{
3119+
struct wx *wx = from_timer(wx, t, service_timer);
3120+
unsigned long next_event_offset = HZ * 2;
3121+
3122+
/* Reset the timer */
3123+
mod_timer(&wx->service_timer, next_event_offset + jiffies);
3124+
3125+
wx_service_event_schedule(wx);
3126+
}
3127+
EXPORT_SYMBOL(wx_service_timer);
3128+
30983129
MODULE_DESCRIPTION("Common library for Wangxun(R) Ethernet drivers.");
30993130
MODULE_LICENSE("GPL");

drivers/net/ethernet/wangxun/libwx/wx_lib.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,8 @@ netdev_features_t wx_features_check(struct sk_buff *skb,
3838
netdev_features_t features);
3939
void wx_set_ring(struct wx *wx, u32 new_tx_count,
4040
u32 new_rx_count, struct wx_ring *temp_ring);
41+
void wx_service_event_schedule(struct wx *wx);
42+
void wx_service_event_complete(struct wx *wx);
43+
void wx_service_timer(struct timer_list *t);
4144

4245
#endif /* _WX_LIB_H_ */

drivers/net/ethernet/wangxun/libwx/wx_type.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1154,6 +1154,7 @@ enum wx_state {
11541154
WX_STATE_SWFW_BUSY,
11551155
WX_STATE_PTP_RUNNING,
11561156
WX_STATE_PTP_TX_IN_PROGRESS,
1157+
WX_STATE_SERVICE_SCHED,
11571158
WX_STATE_NBITS /* must be last */
11581159
};
11591160

@@ -1197,6 +1198,8 @@ enum wx_pf_flags {
11971198
WX_FLAG_RX_HWTSTAMP_ENABLED,
11981199
WX_FLAG_RX_HWTSTAMP_IN_REGISTER,
11991200
WX_FLAG_PTP_PPS_ENABLED,
1201+
WX_FLAG_NEED_LINK_CONFIG,
1202+
WX_FLAG_NEED_SFP_RESET,
12001203
WX_PF_FLAGS_NBITS /* must be last */
12011204
};
12021205

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

12361239
/* PHY stuff */
12371240
bool notify_down;
1241+
int adv_speed;
1242+
int adv_duplex;
12381243
unsigned int link;
12391244
int speed;
12401245
int duplex;
@@ -1332,6 +1337,9 @@ struct wx {
13321337
struct ptp_clock_info ptp_caps;
13331338
struct kernel_hwtstamp_config tstamp_config;
13341339
struct sk_buff *ptp_tx_skb;
1340+
1341+
struct timer_list service_timer;
1342+
struct work_struct service_task;
13351343
};
13361344

13371345
#define WX_INTR_ALL (~0ULL)

drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,96 @@
1313
#include "txgbe_aml.h"
1414
#include "txgbe_hw.h"
1515

16+
void txgbe_gpio_init_aml(struct wx *wx)
17+
{
18+
u32 status;
19+
20+
wr32(wx, WX_GPIO_INTTYPE_LEVEL, TXGBE_GPIOBIT_2 | TXGBE_GPIOBIT_3);
21+
wr32(wx, WX_GPIO_INTEN, TXGBE_GPIOBIT_2 | TXGBE_GPIOBIT_3);
22+
23+
status = rd32(wx, WX_GPIO_INTSTATUS);
24+
for (int i = 0; i < 6; i++) {
25+
if (status & BIT(i))
26+
wr32(wx, WX_GPIO_EOI, BIT(i));
27+
}
28+
}
29+
30+
irqreturn_t txgbe_gpio_irq_handler_aml(int irq, void *data)
31+
{
32+
struct txgbe *txgbe = data;
33+
struct wx *wx = txgbe->wx;
34+
u32 status;
35+
36+
wr32(wx, WX_GPIO_INTMASK, 0xFF);
37+
status = rd32(wx, WX_GPIO_INTSTATUS);
38+
if (status & TXGBE_GPIOBIT_2) {
39+
set_bit(WX_FLAG_NEED_SFP_RESET, wx->flags);
40+
wr32(wx, WX_GPIO_EOI, TXGBE_GPIOBIT_2);
41+
wx_service_event_schedule(wx);
42+
}
43+
if (status & TXGBE_GPIOBIT_3) {
44+
set_bit(WX_FLAG_NEED_LINK_CONFIG, wx->flags);
45+
wx_service_event_schedule(wx);
46+
wr32(wx, WX_GPIO_EOI, TXGBE_GPIOBIT_3);
47+
}
48+
49+
wr32(wx, WX_GPIO_INTMASK, 0);
50+
return IRQ_HANDLED;
51+
}
52+
53+
static int txgbe_identify_sfp_hostif(struct wx *wx, struct txgbe_hic_i2c_read *buffer)
54+
{
55+
buffer->hdr.cmd = FW_READ_SFP_INFO_CMD;
56+
buffer->hdr.buf_len = sizeof(struct txgbe_hic_i2c_read) -
57+
sizeof(struct wx_hic_hdr);
58+
buffer->hdr.cmd_or_resp.cmd_resv = FW_CEM_CMD_RESERVED;
59+
60+
return wx_host_interface_command(wx, (u32 *)buffer,
61+
sizeof(struct txgbe_hic_i2c_read),
62+
WX_HI_COMMAND_TIMEOUT, true);
63+
}
64+
65+
static int txgbe_set_phy_link_hostif(struct wx *wx, int speed, int autoneg, int duplex)
66+
{
67+
struct txgbe_hic_ephy_setlink buffer;
68+
69+
buffer.hdr.cmd = FW_PHY_SET_LINK_CMD;
70+
buffer.hdr.buf_len = sizeof(struct txgbe_hic_ephy_setlink) -
71+
sizeof(struct wx_hic_hdr);
72+
buffer.hdr.cmd_or_resp.cmd_resv = FW_CEM_CMD_RESERVED;
73+
74+
switch (speed) {
75+
case SPEED_25000:
76+
buffer.speed = TXGBE_LINK_SPEED_25GB_FULL;
77+
break;
78+
case SPEED_10000:
79+
buffer.speed = TXGBE_LINK_SPEED_10GB_FULL;
80+
break;
81+
}
82+
83+
buffer.fec_mode = TXGBE_PHY_FEC_AUTO;
84+
buffer.autoneg = autoneg;
85+
buffer.duplex = duplex;
86+
87+
return wx_host_interface_command(wx, (u32 *)&buffer, sizeof(buffer),
88+
WX_HI_COMMAND_TIMEOUT, true);
89+
}
90+
91+
static void txgbe_get_link_capabilities(struct wx *wx)
92+
{
93+
struct txgbe *txgbe = wx->priv;
94+
95+
if (test_bit(PHY_INTERFACE_MODE_25GBASER, txgbe->sfp_interfaces))
96+
wx->adv_speed = SPEED_25000;
97+
else if (test_bit(PHY_INTERFACE_MODE_10GBASER, txgbe->sfp_interfaces))
98+
wx->adv_speed = SPEED_10000;
99+
else
100+
wx->adv_speed = SPEED_UNKNOWN;
101+
102+
wx->adv_duplex = wx->adv_speed == SPEED_UNKNOWN ?
103+
DUPLEX_HALF : DUPLEX_FULL;
104+
}
105+
16106
static void txgbe_get_phy_link(struct wx *wx, int *speed)
17107
{
18108
u32 status;
@@ -28,6 +118,120 @@ static void txgbe_get_phy_link(struct wx *wx, int *speed)
28118
*speed = SPEED_UNKNOWN;
29119
}
30120

121+
int txgbe_set_phy_link(struct wx *wx)
122+
{
123+
int speed, err;
124+
u32 gpio;
125+
126+
/* Check RX signal */
127+
gpio = rd32(wx, WX_GPIO_EXT);
128+
if (gpio & TXGBE_GPIOBIT_3)
129+
return -ENODEV;
130+
131+
txgbe_get_link_capabilities(wx);
132+
if (wx->adv_speed == SPEED_UNKNOWN)
133+
return -ENODEV;
134+
135+
txgbe_get_phy_link(wx, &speed);
136+
if (speed == wx->adv_speed)
137+
return 0;
138+
139+
err = txgbe_set_phy_link_hostif(wx, wx->adv_speed, 0, wx->adv_duplex);
140+
if (err) {
141+
wx_err(wx, "Failed to setup link\n");
142+
return err;
143+
}
144+
145+
return 0;
146+
}
147+
148+
static int txgbe_sfp_to_linkmodes(struct wx *wx, struct txgbe_sfp_id *id)
149+
{
150+
__ETHTOOL_DECLARE_LINK_MODE_MASK(modes) = { 0, };
151+
DECLARE_PHY_INTERFACE_MASK(interfaces);
152+
struct txgbe *txgbe = wx->priv;
153+
154+
if (id->com_25g_code & (TXGBE_SFF_25GBASESR_CAPABLE |
155+
TXGBE_SFF_25GBASEER_CAPABLE |
156+
TXGBE_SFF_25GBASELR_CAPABLE)) {
157+
phylink_set(modes, 25000baseSR_Full);
158+
__set_bit(PHY_INTERFACE_MODE_25GBASER, interfaces);
159+
}
160+
if (id->com_10g_code & TXGBE_SFF_10GBASESR_CAPABLE) {
161+
phylink_set(modes, 10000baseSR_Full);
162+
__set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces);
163+
}
164+
if (id->com_10g_code & TXGBE_SFF_10GBASELR_CAPABLE) {
165+
phylink_set(modes, 10000baseLR_Full);
166+
__set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces);
167+
}
168+
169+
if (phy_interface_empty(interfaces)) {
170+
wx_err(wx, "unsupported SFP module\n");
171+
return -EINVAL;
172+
}
173+
174+
phylink_set(modes, Pause);
175+
phylink_set(modes, Asym_Pause);
176+
phylink_set(modes, FIBRE);
177+
txgbe->link_port = PORT_FIBRE;
178+
179+
if (!linkmode_equal(txgbe->sfp_support, modes)) {
180+
linkmode_copy(txgbe->sfp_support, modes);
181+
phy_interface_and(txgbe->sfp_interfaces,
182+
wx->phylink_config.supported_interfaces,
183+
interfaces);
184+
linkmode_copy(txgbe->advertising, modes);
185+
186+
set_bit(WX_FLAG_NEED_LINK_CONFIG, wx->flags);
187+
}
188+
189+
return 0;
190+
}
191+
192+
int txgbe_identify_sfp(struct wx *wx)
193+
{
194+
struct txgbe_hic_i2c_read buffer;
195+
struct txgbe_sfp_id *id;
196+
int err = 0;
197+
u32 gpio;
198+
199+
gpio = rd32(wx, WX_GPIO_EXT);
200+
if (gpio & TXGBE_GPIOBIT_2)
201+
return -ENODEV;
202+
203+
err = txgbe_identify_sfp_hostif(wx, &buffer);
204+
if (err) {
205+
wx_err(wx, "Failed to identify SFP module\n");
206+
return err;
207+
}
208+
209+
id = &buffer.id;
210+
if (id->identifier != TXGBE_SFF_IDENTIFIER_SFP) {
211+
wx_err(wx, "Invalid SFP module\n");
212+
return -ENODEV;
213+
}
214+
215+
err = txgbe_sfp_to_linkmodes(wx, id);
216+
if (err)
217+
return err;
218+
219+
if (gpio & TXGBE_GPIOBIT_3)
220+
set_bit(WX_FLAG_NEED_LINK_CONFIG, wx->flags);
221+
222+
return 0;
223+
}
224+
225+
void txgbe_setup_link(struct wx *wx)
226+
{
227+
struct txgbe *txgbe = wx->priv;
228+
229+
phy_interface_zero(txgbe->sfp_interfaces);
230+
linkmode_zero(txgbe->sfp_support);
231+
232+
txgbe_identify_sfp(wx);
233+
}
234+
31235
static void txgbe_get_link_state(struct phylink_config *config,
32236
struct phylink_link_state *state)
33237
{

drivers/net/ethernet/wangxun/txgbe/txgbe_aml.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44
#ifndef _TXGBE_AML_H_
55
#define _TXGBE_AML_H_
66

7+
void txgbe_gpio_init_aml(struct wx *wx);
8+
irqreturn_t txgbe_gpio_irq_handler_aml(int irq, void *data);
9+
int txgbe_set_phy_link(struct wx *wx);
10+
int txgbe_identify_sfp(struct wx *wx);
11+
void txgbe_setup_link(struct wx *wx);
712
int txgbe_phylink_init_aml(struct txgbe *txgbe);
813

914
#endif /* _TXGBE_AML_H_ */

0 commit comments

Comments
 (0)