Merge branch 'amd-xgbe-fixes'
aTom Lendacky says: ==================== amd-xgbe: AMD XGBE driver fixes 2018-04-23 This patch series addresses some issues in the AMD XGBE driver. The following fixes are included in this driver update series: - Improve KR auto-negotiation and training (2 patches) - Add pre and post auto-negotiation hooks - Use the pre and post auto-negotiation hooks to disable CDR tracking during auto-negotiation page exchange in KR mode - Check for SFP tranceiver signal support and only use the signal if the SFP indicates that it is supported This patch series is based on net. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
6cd968f448
|
@ -1321,6 +1321,10 @@
|
|||
#define MDIO_VEND2_AN_STAT 0x8002
|
||||
#endif
|
||||
|
||||
#ifndef MDIO_VEND2_PMA_CDR_CONTROL
|
||||
#define MDIO_VEND2_PMA_CDR_CONTROL 0x8056
|
||||
#endif
|
||||
|
||||
#ifndef MDIO_CTRL1_SPEED1G
|
||||
#define MDIO_CTRL1_SPEED1G (MDIO_CTRL1_SPEED10G & ~BMCR_SPEED100)
|
||||
#endif
|
||||
|
@ -1369,6 +1373,10 @@
|
|||
#define XGBE_AN_CL37_TX_CONFIG_MASK 0x08
|
||||
#define XGBE_AN_CL37_MII_CTRL_8BIT 0x0100
|
||||
|
||||
#define XGBE_PMA_CDR_TRACK_EN_MASK 0x01
|
||||
#define XGBE_PMA_CDR_TRACK_EN_OFF 0x00
|
||||
#define XGBE_PMA_CDR_TRACK_EN_ON 0x01
|
||||
|
||||
/* Bit setting and getting macros
|
||||
* The get macro will extract the current bit field value from within
|
||||
* the variable
|
||||
|
|
|
@ -519,6 +519,22 @@ void xgbe_debugfs_init(struct xgbe_prv_data *pdata)
|
|||
"debugfs_create_file failed\n");
|
||||
}
|
||||
|
||||
if (pdata->vdata->an_cdr_workaround) {
|
||||
pfile = debugfs_create_bool("an_cdr_workaround", 0600,
|
||||
pdata->xgbe_debugfs,
|
||||
&pdata->debugfs_an_cdr_workaround);
|
||||
if (!pfile)
|
||||
netdev_err(pdata->netdev,
|
||||
"debugfs_create_bool failed\n");
|
||||
|
||||
pfile = debugfs_create_bool("an_cdr_track_early", 0600,
|
||||
pdata->xgbe_debugfs,
|
||||
&pdata->debugfs_an_cdr_track_early);
|
||||
if (!pfile)
|
||||
netdev_err(pdata->netdev,
|
||||
"debugfs_create_bool failed\n");
|
||||
}
|
||||
|
||||
kfree(buf);
|
||||
}
|
||||
|
||||
|
|
|
@ -349,6 +349,7 @@ int xgbe_config_netdev(struct xgbe_prv_data *pdata)
|
|||
XGMAC_SET_BITS(pdata->rss_options, MAC_RSSCR, UDP4TE, 1);
|
||||
|
||||
/* Call MDIO/PHY initialization routine */
|
||||
pdata->debugfs_an_cdr_workaround = pdata->vdata->an_cdr_workaround;
|
||||
ret = pdata->phy_if.phy_init(pdata);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
|
@ -432,11 +432,16 @@ static void xgbe_an73_disable(struct xgbe_prv_data *pdata)
|
|||
xgbe_an73_set(pdata, false, false);
|
||||
xgbe_an73_disable_interrupts(pdata);
|
||||
|
||||
pdata->an_start = 0;
|
||||
|
||||
netif_dbg(pdata, link, pdata->netdev, "CL73 AN disabled\n");
|
||||
}
|
||||
|
||||
static void xgbe_an_restart(struct xgbe_prv_data *pdata)
|
||||
{
|
||||
if (pdata->phy_if.phy_impl.an_pre)
|
||||
pdata->phy_if.phy_impl.an_pre(pdata);
|
||||
|
||||
switch (pdata->an_mode) {
|
||||
case XGBE_AN_MODE_CL73:
|
||||
case XGBE_AN_MODE_CL73_REDRV:
|
||||
|
@ -453,6 +458,9 @@ static void xgbe_an_restart(struct xgbe_prv_data *pdata)
|
|||
|
||||
static void xgbe_an_disable(struct xgbe_prv_data *pdata)
|
||||
{
|
||||
if (pdata->phy_if.phy_impl.an_post)
|
||||
pdata->phy_if.phy_impl.an_post(pdata);
|
||||
|
||||
switch (pdata->an_mode) {
|
||||
case XGBE_AN_MODE_CL73:
|
||||
case XGBE_AN_MODE_CL73_REDRV:
|
||||
|
@ -505,11 +513,11 @@ static enum xgbe_an xgbe_an73_tx_training(struct xgbe_prv_data *pdata,
|
|||
XMDIO_WRITE(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL,
|
||||
reg);
|
||||
|
||||
if (pdata->phy_if.phy_impl.kr_training_post)
|
||||
pdata->phy_if.phy_impl.kr_training_post(pdata);
|
||||
|
||||
netif_dbg(pdata, link, pdata->netdev,
|
||||
"KR training initiated\n");
|
||||
|
||||
if (pdata->phy_if.phy_impl.kr_training_post)
|
||||
pdata->phy_if.phy_impl.kr_training_post(pdata);
|
||||
}
|
||||
|
||||
return XGBE_AN_PAGE_RECEIVED;
|
||||
|
@ -637,11 +645,11 @@ static enum xgbe_an xgbe_an73_incompat_link(struct xgbe_prv_data *pdata)
|
|||
return XGBE_AN_NO_LINK;
|
||||
}
|
||||
|
||||
xgbe_an73_disable(pdata);
|
||||
xgbe_an_disable(pdata);
|
||||
|
||||
xgbe_switch_mode(pdata);
|
||||
|
||||
xgbe_an73_restart(pdata);
|
||||
xgbe_an_restart(pdata);
|
||||
|
||||
return XGBE_AN_INCOMPAT_LINK;
|
||||
}
|
||||
|
@ -820,6 +828,9 @@ static void xgbe_an37_state_machine(struct xgbe_prv_data *pdata)
|
|||
pdata->an_result = pdata->an_state;
|
||||
pdata->an_state = XGBE_AN_READY;
|
||||
|
||||
if (pdata->phy_if.phy_impl.an_post)
|
||||
pdata->phy_if.phy_impl.an_post(pdata);
|
||||
|
||||
netif_dbg(pdata, link, pdata->netdev, "CL37 AN result: %s\n",
|
||||
xgbe_state_as_string(pdata->an_result));
|
||||
}
|
||||
|
@ -903,6 +914,9 @@ static void xgbe_an73_state_machine(struct xgbe_prv_data *pdata)
|
|||
pdata->kx_state = XGBE_RX_BPA;
|
||||
pdata->an_start = 0;
|
||||
|
||||
if (pdata->phy_if.phy_impl.an_post)
|
||||
pdata->phy_if.phy_impl.an_post(pdata);
|
||||
|
||||
netif_dbg(pdata, link, pdata->netdev, "CL73 AN result: %s\n",
|
||||
xgbe_state_as_string(pdata->an_result));
|
||||
}
|
||||
|
|
|
@ -456,6 +456,7 @@ static const struct xgbe_version_data xgbe_v2a = {
|
|||
.irq_reissue_support = 1,
|
||||
.tx_desc_prefetch = 5,
|
||||
.rx_desc_prefetch = 5,
|
||||
.an_cdr_workaround = 1,
|
||||
};
|
||||
|
||||
static const struct xgbe_version_data xgbe_v2b = {
|
||||
|
@ -470,6 +471,7 @@ static const struct xgbe_version_data xgbe_v2b = {
|
|||
.irq_reissue_support = 1,
|
||||
.tx_desc_prefetch = 5,
|
||||
.rx_desc_prefetch = 5,
|
||||
.an_cdr_workaround = 1,
|
||||
};
|
||||
|
||||
static const struct pci_device_id xgbe_pci_table[] = {
|
||||
|
|
|
@ -147,6 +147,14 @@
|
|||
/* Rate-change complete wait/retry count */
|
||||
#define XGBE_RATECHANGE_COUNT 500
|
||||
|
||||
/* CDR delay values for KR support (in usec) */
|
||||
#define XGBE_CDR_DELAY_INIT 10000
|
||||
#define XGBE_CDR_DELAY_INC 10000
|
||||
#define XGBE_CDR_DELAY_MAX 100000
|
||||
|
||||
/* RRC frequency during link status check */
|
||||
#define XGBE_RRC_FREQUENCY 10
|
||||
|
||||
enum xgbe_port_mode {
|
||||
XGBE_PORT_MODE_RSVD = 0,
|
||||
XGBE_PORT_MODE_BACKPLANE,
|
||||
|
@ -245,6 +253,10 @@ enum xgbe_sfp_speed {
|
|||
#define XGBE_SFP_BASE_VENDOR_SN 4
|
||||
#define XGBE_SFP_BASE_VENDOR_SN_LEN 16
|
||||
|
||||
#define XGBE_SFP_EXTD_OPT1 1
|
||||
#define XGBE_SFP_EXTD_OPT1_RX_LOS BIT(1)
|
||||
#define XGBE_SFP_EXTD_OPT1_TX_FAULT BIT(3)
|
||||
|
||||
#define XGBE_SFP_EXTD_DIAG 28
|
||||
#define XGBE_SFP_EXTD_DIAG_ADDR_CHANGE BIT(2)
|
||||
|
||||
|
@ -324,6 +336,7 @@ struct xgbe_phy_data {
|
|||
|
||||
unsigned int sfp_gpio_address;
|
||||
unsigned int sfp_gpio_mask;
|
||||
unsigned int sfp_gpio_inputs;
|
||||
unsigned int sfp_gpio_rx_los;
|
||||
unsigned int sfp_gpio_tx_fault;
|
||||
unsigned int sfp_gpio_mod_absent;
|
||||
|
@ -355,6 +368,10 @@ struct xgbe_phy_data {
|
|||
unsigned int redrv_addr;
|
||||
unsigned int redrv_lane;
|
||||
unsigned int redrv_model;
|
||||
|
||||
/* KR AN support */
|
||||
unsigned int phy_cdr_notrack;
|
||||
unsigned int phy_cdr_delay;
|
||||
};
|
||||
|
||||
/* I2C, MDIO and GPIO lines are muxed, so only one device at a time */
|
||||
|
@ -974,6 +991,49 @@ static void xgbe_phy_sfp_external_phy(struct xgbe_prv_data *pdata)
|
|||
phy_data->sfp_phy_avail = 1;
|
||||
}
|
||||
|
||||
static bool xgbe_phy_check_sfp_rx_los(struct xgbe_phy_data *phy_data)
|
||||
{
|
||||
u8 *sfp_extd = phy_data->sfp_eeprom.extd;
|
||||
|
||||
if (!(sfp_extd[XGBE_SFP_EXTD_OPT1] & XGBE_SFP_EXTD_OPT1_RX_LOS))
|
||||
return false;
|
||||
|
||||
if (phy_data->sfp_gpio_mask & XGBE_GPIO_NO_RX_LOS)
|
||||
return false;
|
||||
|
||||
if (phy_data->sfp_gpio_inputs & (1 << phy_data->sfp_gpio_rx_los))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool xgbe_phy_check_sfp_tx_fault(struct xgbe_phy_data *phy_data)
|
||||
{
|
||||
u8 *sfp_extd = phy_data->sfp_eeprom.extd;
|
||||
|
||||
if (!(sfp_extd[XGBE_SFP_EXTD_OPT1] & XGBE_SFP_EXTD_OPT1_TX_FAULT))
|
||||
return false;
|
||||
|
||||
if (phy_data->sfp_gpio_mask & XGBE_GPIO_NO_TX_FAULT)
|
||||
return false;
|
||||
|
||||
if (phy_data->sfp_gpio_inputs & (1 << phy_data->sfp_gpio_tx_fault))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool xgbe_phy_check_sfp_mod_absent(struct xgbe_phy_data *phy_data)
|
||||
{
|
||||
if (phy_data->sfp_gpio_mask & XGBE_GPIO_NO_MOD_ABSENT)
|
||||
return false;
|
||||
|
||||
if (phy_data->sfp_gpio_inputs & (1 << phy_data->sfp_gpio_mod_absent))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool xgbe_phy_belfuse_parse_quirks(struct xgbe_prv_data *pdata)
|
||||
{
|
||||
struct xgbe_phy_data *phy_data = pdata->phy_data;
|
||||
|
@ -1019,6 +1079,10 @@ static void xgbe_phy_sfp_parse_eeprom(struct xgbe_prv_data *pdata)
|
|||
if (sfp_base[XGBE_SFP_BASE_EXT_ID] != XGBE_SFP_EXT_ID_SFP)
|
||||
return;
|
||||
|
||||
/* Update transceiver signals (eeprom extd/options) */
|
||||
phy_data->sfp_tx_fault = xgbe_phy_check_sfp_tx_fault(phy_data);
|
||||
phy_data->sfp_rx_los = xgbe_phy_check_sfp_rx_los(phy_data);
|
||||
|
||||
if (xgbe_phy_sfp_parse_quirks(pdata))
|
||||
return;
|
||||
|
||||
|
@ -1184,7 +1248,6 @@ static int xgbe_phy_sfp_read_eeprom(struct xgbe_prv_data *pdata)
|
|||
static void xgbe_phy_sfp_signals(struct xgbe_prv_data *pdata)
|
||||
{
|
||||
struct xgbe_phy_data *phy_data = pdata->phy_data;
|
||||
unsigned int gpio_input;
|
||||
u8 gpio_reg, gpio_ports[2];
|
||||
int ret;
|
||||
|
||||
|
@ -1199,23 +1262,9 @@ static void xgbe_phy_sfp_signals(struct xgbe_prv_data *pdata)
|
|||
return;
|
||||
}
|
||||
|
||||
gpio_input = (gpio_ports[1] << 8) | gpio_ports[0];
|
||||
phy_data->sfp_gpio_inputs = (gpio_ports[1] << 8) | gpio_ports[0];
|
||||
|
||||
if (phy_data->sfp_gpio_mask & XGBE_GPIO_NO_MOD_ABSENT) {
|
||||
/* No GPIO, just assume the module is present for now */
|
||||
phy_data->sfp_mod_absent = 0;
|
||||
} else {
|
||||
if (!(gpio_input & (1 << phy_data->sfp_gpio_mod_absent)))
|
||||
phy_data->sfp_mod_absent = 0;
|
||||
}
|
||||
|
||||
if (!(phy_data->sfp_gpio_mask & XGBE_GPIO_NO_RX_LOS) &&
|
||||
(gpio_input & (1 << phy_data->sfp_gpio_rx_los)))
|
||||
phy_data->sfp_rx_los = 1;
|
||||
|
||||
if (!(phy_data->sfp_gpio_mask & XGBE_GPIO_NO_TX_FAULT) &&
|
||||
(gpio_input & (1 << phy_data->sfp_gpio_tx_fault)))
|
||||
phy_data->sfp_tx_fault = 1;
|
||||
phy_data->sfp_mod_absent = xgbe_phy_check_sfp_mod_absent(phy_data);
|
||||
}
|
||||
|
||||
static void xgbe_phy_sfp_mod_absent(struct xgbe_prv_data *pdata)
|
||||
|
@ -2361,7 +2410,7 @@ static int xgbe_phy_link_status(struct xgbe_prv_data *pdata, int *an_restart)
|
|||
return 1;
|
||||
|
||||
/* No link, attempt a receiver reset cycle */
|
||||
if (phy_data->rrc_count++) {
|
||||
if (phy_data->rrc_count++ > XGBE_RRC_FREQUENCY) {
|
||||
phy_data->rrc_count = 0;
|
||||
xgbe_phy_rrc(pdata);
|
||||
}
|
||||
|
@ -2669,6 +2718,103 @@ static bool xgbe_phy_port_enabled(struct xgbe_prv_data *pdata)
|
|||
return true;
|
||||
}
|
||||
|
||||
static void xgbe_phy_cdr_track(struct xgbe_prv_data *pdata)
|
||||
{
|
||||
struct xgbe_phy_data *phy_data = pdata->phy_data;
|
||||
|
||||
if (!pdata->debugfs_an_cdr_workaround)
|
||||
return;
|
||||
|
||||
if (!phy_data->phy_cdr_notrack)
|
||||
return;
|
||||
|
||||
usleep_range(phy_data->phy_cdr_delay,
|
||||
phy_data->phy_cdr_delay + 500);
|
||||
|
||||
XMDIO_WRITE_BITS(pdata, MDIO_MMD_PMAPMD, MDIO_VEND2_PMA_CDR_CONTROL,
|
||||
XGBE_PMA_CDR_TRACK_EN_MASK,
|
||||
XGBE_PMA_CDR_TRACK_EN_ON);
|
||||
|
||||
phy_data->phy_cdr_notrack = 0;
|
||||
}
|
||||
|
||||
static void xgbe_phy_cdr_notrack(struct xgbe_prv_data *pdata)
|
||||
{
|
||||
struct xgbe_phy_data *phy_data = pdata->phy_data;
|
||||
|
||||
if (!pdata->debugfs_an_cdr_workaround)
|
||||
return;
|
||||
|
||||
if (phy_data->phy_cdr_notrack)
|
||||
return;
|
||||
|
||||
XMDIO_WRITE_BITS(pdata, MDIO_MMD_PMAPMD, MDIO_VEND2_PMA_CDR_CONTROL,
|
||||
XGBE_PMA_CDR_TRACK_EN_MASK,
|
||||
XGBE_PMA_CDR_TRACK_EN_OFF);
|
||||
|
||||
xgbe_phy_rrc(pdata);
|
||||
|
||||
phy_data->phy_cdr_notrack = 1;
|
||||
}
|
||||
|
||||
static void xgbe_phy_kr_training_post(struct xgbe_prv_data *pdata)
|
||||
{
|
||||
if (!pdata->debugfs_an_cdr_track_early)
|
||||
xgbe_phy_cdr_track(pdata);
|
||||
}
|
||||
|
||||
static void xgbe_phy_kr_training_pre(struct xgbe_prv_data *pdata)
|
||||
{
|
||||
if (pdata->debugfs_an_cdr_track_early)
|
||||
xgbe_phy_cdr_track(pdata);
|
||||
}
|
||||
|
||||
static void xgbe_phy_an_post(struct xgbe_prv_data *pdata)
|
||||
{
|
||||
struct xgbe_phy_data *phy_data = pdata->phy_data;
|
||||
|
||||
switch (pdata->an_mode) {
|
||||
case XGBE_AN_MODE_CL73:
|
||||
case XGBE_AN_MODE_CL73_REDRV:
|
||||
if (phy_data->cur_mode != XGBE_MODE_KR)
|
||||
break;
|
||||
|
||||
xgbe_phy_cdr_track(pdata);
|
||||
|
||||
switch (pdata->an_result) {
|
||||
case XGBE_AN_READY:
|
||||
case XGBE_AN_COMPLETE:
|
||||
break;
|
||||
default:
|
||||
if (phy_data->phy_cdr_delay < XGBE_CDR_DELAY_MAX)
|
||||
phy_data->phy_cdr_delay += XGBE_CDR_DELAY_INC;
|
||||
else
|
||||
phy_data->phy_cdr_delay = XGBE_CDR_DELAY_INIT;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void xgbe_phy_an_pre(struct xgbe_prv_data *pdata)
|
||||
{
|
||||
struct xgbe_phy_data *phy_data = pdata->phy_data;
|
||||
|
||||
switch (pdata->an_mode) {
|
||||
case XGBE_AN_MODE_CL73:
|
||||
case XGBE_AN_MODE_CL73_REDRV:
|
||||
if (phy_data->cur_mode != XGBE_MODE_KR)
|
||||
break;
|
||||
|
||||
xgbe_phy_cdr_notrack(pdata);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void xgbe_phy_stop(struct xgbe_prv_data *pdata)
|
||||
{
|
||||
struct xgbe_phy_data *phy_data = pdata->phy_data;
|
||||
|
@ -2680,6 +2826,9 @@ static void xgbe_phy_stop(struct xgbe_prv_data *pdata)
|
|||
xgbe_phy_sfp_reset(phy_data);
|
||||
xgbe_phy_sfp_mod_absent(pdata);
|
||||
|
||||
/* Reset CDR support */
|
||||
xgbe_phy_cdr_track(pdata);
|
||||
|
||||
/* Power off the PHY */
|
||||
xgbe_phy_power_off(pdata);
|
||||
|
||||
|
@ -2712,6 +2861,9 @@ static int xgbe_phy_start(struct xgbe_prv_data *pdata)
|
|||
/* Start in highest supported mode */
|
||||
xgbe_phy_set_mode(pdata, phy_data->start_mode);
|
||||
|
||||
/* Reset CDR support */
|
||||
xgbe_phy_cdr_track(pdata);
|
||||
|
||||
/* After starting the I2C controller, we can check for an SFP */
|
||||
switch (phy_data->port_mode) {
|
||||
case XGBE_PORT_MODE_SFP:
|
||||
|
@ -3019,6 +3171,8 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata)
|
|||
}
|
||||
}
|
||||
|
||||
phy_data->phy_cdr_delay = XGBE_CDR_DELAY_INIT;
|
||||
|
||||
/* Register for driving external PHYs */
|
||||
mii = devm_mdiobus_alloc(pdata->dev);
|
||||
if (!mii) {
|
||||
|
@ -3071,4 +3225,10 @@ void xgbe_init_function_ptrs_phy_v2(struct xgbe_phy_if *phy_if)
|
|||
phy_impl->an_advertising = xgbe_phy_an_advertising;
|
||||
|
||||
phy_impl->an_outcome = xgbe_phy_an_outcome;
|
||||
|
||||
phy_impl->an_pre = xgbe_phy_an_pre;
|
||||
phy_impl->an_post = xgbe_phy_an_post;
|
||||
|
||||
phy_impl->kr_training_pre = xgbe_phy_kr_training_pre;
|
||||
phy_impl->kr_training_post = xgbe_phy_kr_training_post;
|
||||
}
|
||||
|
|
|
@ -833,6 +833,7 @@ struct xgbe_hw_if {
|
|||
/* This structure represents implementation specific routines for an
|
||||
* implementation of a PHY. All routines are required unless noted below.
|
||||
* Optional routines:
|
||||
* an_pre, an_post
|
||||
* kr_training_pre, kr_training_post
|
||||
*/
|
||||
struct xgbe_phy_impl_if {
|
||||
|
@ -875,6 +876,10 @@ struct xgbe_phy_impl_if {
|
|||
/* Process results of auto-negotiation */
|
||||
enum xgbe_mode (*an_outcome)(struct xgbe_prv_data *);
|
||||
|
||||
/* Pre/Post auto-negotiation support */
|
||||
void (*an_pre)(struct xgbe_prv_data *);
|
||||
void (*an_post)(struct xgbe_prv_data *);
|
||||
|
||||
/* Pre/Post KR training enablement support */
|
||||
void (*kr_training_pre)(struct xgbe_prv_data *);
|
||||
void (*kr_training_post)(struct xgbe_prv_data *);
|
||||
|
@ -989,6 +994,7 @@ struct xgbe_version_data {
|
|||
unsigned int irq_reissue_support;
|
||||
unsigned int tx_desc_prefetch;
|
||||
unsigned int rx_desc_prefetch;
|
||||
unsigned int an_cdr_workaround;
|
||||
};
|
||||
|
||||
struct xgbe_vxlan_data {
|
||||
|
@ -1257,6 +1263,9 @@ struct xgbe_prv_data {
|
|||
unsigned int debugfs_xprop_reg;
|
||||
|
||||
unsigned int debugfs_xi2c_reg;
|
||||
|
||||
bool debugfs_an_cdr_workaround;
|
||||
bool debugfs_an_cdr_track_early;
|
||||
};
|
||||
|
||||
/* Function prototypes*/
|
||||
|
|
Loading…
Reference in New Issue
Block a user