Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/ixgbe/ixgbe_phy.c
/****************************************************************************** | /****************************************************************************** | ||||
Copyright (c) 2001-2015, Intel Corporation | Copyright (c) 2001-2017, Intel Corporation | ||||
All rights reserved. | All rights reserved. | ||||
Redistribution and use in source and binary forms, with or without | Redistribution and use in source and binary forms, with or without | ||||
modification, are permitted provided that the following conditions are met: | modification, are permitted provided that the following conditions are met: | ||||
1. Redistributions of source code must retain the above copyright notice, | 1. Redistributions of source code must retain the above copyright notice, | ||||
this list of conditions and the following disclaimer. | this list of conditions and the following disclaimer. | ||||
2. Redistributions in binary form must reproduce the above copyright | 2. Redistributions in binary form must reproduce the above copyright | ||||
notice, this list of conditions and the following disclaimer in the | notice, this list of conditions and the following disclaimer in the | ||||
documentation and/or other materials provided with the distribution. | documentation and/or other materials provided with the distribution. | ||||
3. Neither the name of the Intel Corporation nor the names of its | 3. Neither the name of the Intel Corporation nor the names of its | ||||
contributors may be used to endorse or promote products derived from | contributors may be used to endorse or promote products derived from | ||||
this software without specific prior written permission. | this software without specific prior written permission. | ||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | ||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||||
POSSIBILITY OF SUCH DAMAGE. | POSSIBILITY OF SUCH DAMAGE. | ||||
******************************************************************************/ | ******************************************************************************/ | ||||
/*$FreeBSD$*/ | /*$FreeBSD$*/ | ||||
#include "ixgbe_api.h" | #include "ixgbe_api.h" | ||||
#include "ixgbe_common.h" | #include "ixgbe_common.h" | ||||
▲ Show 20 Lines • Show All 68 Lines • ▼ Show 20 Lines | |||||
* @hw: pointer to the hardware structure | * @hw: pointer to the hardware structure | ||||
* @addr: I2C bus address to read from | * @addr: I2C bus address to read from | ||||
* @reg: I2C device register to read from | * @reg: I2C device register to read from | ||||
* @val: pointer to location to receive read value | * @val: pointer to location to receive read value | ||||
* @lock: TRUE if to take and release semaphore | * @lock: TRUE if to take and release semaphore | ||||
* | * | ||||
* Returns an error code on error. | * Returns an error code on error. | ||||
*/ | */ | ||||
static s32 ixgbe_read_i2c_combined_generic_int(struct ixgbe_hw *hw, u8 addr, | s32 ixgbe_read_i2c_combined_generic_int(struct ixgbe_hw *hw, u8 addr, u16 reg, | ||||
u16 reg, u16 *val, bool lock) | u16 *val, bool lock) | ||||
{ | { | ||||
u32 swfw_mask = hw->phy.phy_semaphore_mask; | u32 swfw_mask = hw->phy.phy_semaphore_mask; | ||||
int max_retry = 10; | int max_retry = 3; | ||||
int retry = 0; | int retry = 0; | ||||
u8 csum_byte; | u8 csum_byte; | ||||
u8 high_bits; | u8 high_bits; | ||||
u8 low_bits; | u8 low_bits; | ||||
u8 reg_high; | u8 reg_high; | ||||
u8 csum; | u8 csum; | ||||
if (hw->mac.type >= ixgbe_mac_X550) | |||||
max_retry = 3; | |||||
reg_high = ((reg >> 7) & 0xFE) | 1; /* Indicate read combined */ | reg_high = ((reg >> 7) & 0xFE) | 1; /* Indicate read combined */ | ||||
csum = ixgbe_ones_comp_byte_add(reg_high, reg & 0xFF); | csum = ixgbe_ones_comp_byte_add(reg_high, reg & 0xFF); | ||||
csum = ~csum; | csum = ~csum; | ||||
do { | do { | ||||
if (lock && hw->mac.ops.acquire_swfw_sync(hw, swfw_mask)) | if (lock && hw->mac.ops.acquire_swfw_sync(hw, swfw_mask)) | ||||
return IXGBE_ERR_SWFW_SYNC; | return IXGBE_ERR_SWFW_SYNC; | ||||
ixgbe_i2c_start(hw); | ixgbe_i2c_start(hw); | ||||
/* Device Address and write indication */ | /* Device Address and write indication */ | ||||
▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | fail: | ||||
else | else | ||||
DEBUGOUT("I2C byte read combined error.\n"); | DEBUGOUT("I2C byte read combined error.\n"); | ||||
} while (retry < max_retry); | } while (retry < max_retry); | ||||
return IXGBE_ERR_I2C; | return IXGBE_ERR_I2C; | ||||
} | } | ||||
/** | /** | ||||
* ixgbe_read_i2c_combined_generic - Perform I2C read combined operation | |||||
* @hw: pointer to the hardware structure | |||||
* @addr: I2C bus address to read from | |||||
* @reg: I2C device register to read from | |||||
* @val: pointer to location to receive read value | |||||
* | |||||
* Returns an error code on error. | |||||
**/ | |||||
static s32 ixgbe_read_i2c_combined_generic(struct ixgbe_hw *hw, u8 addr, | |||||
u16 reg, u16 *val) | |||||
{ | |||||
return ixgbe_read_i2c_combined_generic_int(hw, addr, reg, val, TRUE); | |||||
} | |||||
/** | |||||
* ixgbe_read_i2c_combined_generic_unlocked - Do I2C read combined operation | |||||
* @hw: pointer to the hardware structure | |||||
* @addr: I2C bus address to read from | |||||
* @reg: I2C device register to read from | |||||
* @val: pointer to location to receive read value | |||||
* | |||||
* Returns an error code on error. | |||||
**/ | |||||
static s32 | |||||
ixgbe_read_i2c_combined_generic_unlocked(struct ixgbe_hw *hw, u8 addr, | |||||
u16 reg, u16 *val) | |||||
{ | |||||
return ixgbe_read_i2c_combined_generic_int(hw, addr, reg, val, FALSE); | |||||
} | |||||
/** | |||||
* ixgbe_write_i2c_combined_generic_int - Perform I2C write combined operation | * ixgbe_write_i2c_combined_generic_int - Perform I2C write combined operation | ||||
* @hw: pointer to the hardware structure | * @hw: pointer to the hardware structure | ||||
* @addr: I2C bus address to write to | * @addr: I2C bus address to write to | ||||
* @reg: I2C device register to write to | * @reg: I2C device register to write to | ||||
* @val: value to write | * @val: value to write | ||||
* @lock: TRUE if to take and release semaphore | * @lock: TRUE if to take and release semaphore | ||||
* | * | ||||
* Returns an error code on error. | * Returns an error code on error. | ||||
*/ | */ | ||||
static s32 ixgbe_write_i2c_combined_generic_int(struct ixgbe_hw *hw, u8 addr, | s32 ixgbe_write_i2c_combined_generic_int(struct ixgbe_hw *hw, u8 addr, u16 reg, | ||||
u16 reg, u16 val, bool lock) | u16 val, bool lock) | ||||
{ | { | ||||
u32 swfw_mask = hw->phy.phy_semaphore_mask; | u32 swfw_mask = hw->phy.phy_semaphore_mask; | ||||
int max_retry = 1; | int max_retry = 1; | ||||
int retry = 0; | int retry = 0; | ||||
u8 reg_high; | u8 reg_high; | ||||
u8 csum; | u8 csum; | ||||
reg_high = (reg >> 7) & 0xFE; /* Indicate write combined */ | reg_high = (reg >> 7) & 0xFE; /* Indicate write combined */ | ||||
Show All 38 Lines | fail: | ||||
else | else | ||||
DEBUGOUT("I2C byte write combined error.\n"); | DEBUGOUT("I2C byte write combined error.\n"); | ||||
} while (retry < max_retry); | } while (retry < max_retry); | ||||
return IXGBE_ERR_I2C; | return IXGBE_ERR_I2C; | ||||
} | } | ||||
/** | /** | ||||
* ixgbe_write_i2c_combined_generic - Perform I2C write combined operation | |||||
* @hw: pointer to the hardware structure | |||||
* @addr: I2C bus address to write to | |||||
* @reg: I2C device register to write to | |||||
* @val: value to write | |||||
* | |||||
* Returns an error code on error. | |||||
**/ | |||||
static s32 ixgbe_write_i2c_combined_generic(struct ixgbe_hw *hw, | |||||
u8 addr, u16 reg, u16 val) | |||||
{ | |||||
return ixgbe_write_i2c_combined_generic_int(hw, addr, reg, val, TRUE); | |||||
} | |||||
/** | |||||
* ixgbe_write_i2c_combined_generic_unlocked - Do I2C write combined operation | |||||
* @hw: pointer to the hardware structure | |||||
* @addr: I2C bus address to write to | |||||
* @reg: I2C device register to write to | |||||
* @val: value to write | |||||
* | |||||
* Returns an error code on error. | |||||
**/ | |||||
static s32 | |||||
ixgbe_write_i2c_combined_generic_unlocked(struct ixgbe_hw *hw, | |||||
u8 addr, u16 reg, u16 val) | |||||
{ | |||||
return ixgbe_write_i2c_combined_generic_int(hw, addr, reg, val, FALSE); | |||||
} | |||||
/** | |||||
* ixgbe_init_phy_ops_generic - Inits PHY function ptrs | * ixgbe_init_phy_ops_generic - Inits PHY function ptrs | ||||
* @hw: pointer to the hardware structure | * @hw: pointer to the hardware structure | ||||
* | * | ||||
* Initialize the function pointers. | * Initialize the function pointers. | ||||
**/ | **/ | ||||
s32 ixgbe_init_phy_ops_generic(struct ixgbe_hw *hw) | s32 ixgbe_init_phy_ops_generic(struct ixgbe_hw *hw) | ||||
{ | { | ||||
struct ixgbe_phy_info *phy = &hw->phy; | struct ixgbe_phy_info *phy = &hw->phy; | ||||
Show All 14 Lines | s32 ixgbe_init_phy_ops_generic(struct ixgbe_hw *hw) | ||||
phy->ops.read_i2c_byte = ixgbe_read_i2c_byte_generic; | phy->ops.read_i2c_byte = ixgbe_read_i2c_byte_generic; | ||||
phy->ops.write_i2c_byte = ixgbe_write_i2c_byte_generic; | phy->ops.write_i2c_byte = ixgbe_write_i2c_byte_generic; | ||||
phy->ops.read_i2c_sff8472 = ixgbe_read_i2c_sff8472_generic; | phy->ops.read_i2c_sff8472 = ixgbe_read_i2c_sff8472_generic; | ||||
phy->ops.read_i2c_eeprom = ixgbe_read_i2c_eeprom_generic; | phy->ops.read_i2c_eeprom = ixgbe_read_i2c_eeprom_generic; | ||||
phy->ops.write_i2c_eeprom = ixgbe_write_i2c_eeprom_generic; | phy->ops.write_i2c_eeprom = ixgbe_write_i2c_eeprom_generic; | ||||
phy->ops.i2c_bus_clear = ixgbe_i2c_bus_clear; | phy->ops.i2c_bus_clear = ixgbe_i2c_bus_clear; | ||||
phy->ops.identify_sfp = ixgbe_identify_module_generic; | phy->ops.identify_sfp = ixgbe_identify_module_generic; | ||||
phy->sfp_type = ixgbe_sfp_type_unknown; | phy->sfp_type = ixgbe_sfp_type_unknown; | ||||
phy->ops.read_i2c_combined = ixgbe_read_i2c_combined_generic; | |||||
phy->ops.write_i2c_combined = ixgbe_write_i2c_combined_generic; | |||||
phy->ops.read_i2c_combined_unlocked = | |||||
ixgbe_read_i2c_combined_generic_unlocked; | |||||
phy->ops.write_i2c_combined_unlocked = | |||||
ixgbe_write_i2c_combined_generic_unlocked; | |||||
phy->ops.read_i2c_byte_unlocked = ixgbe_read_i2c_byte_generic_unlocked; | phy->ops.read_i2c_byte_unlocked = ixgbe_read_i2c_byte_generic_unlocked; | ||||
phy->ops.write_i2c_byte_unlocked = | phy->ops.write_i2c_byte_unlocked = | ||||
ixgbe_write_i2c_byte_generic_unlocked; | ixgbe_write_i2c_byte_generic_unlocked; | ||||
phy->ops.check_overtemp = ixgbe_tn_check_overtemp; | phy->ops.check_overtemp = ixgbe_tn_check_overtemp; | ||||
return IXGBE_SUCCESS; | return IXGBE_SUCCESS; | ||||
} | } | ||||
/** | /** | ||||
* ixgbe_probe_phy - Probe a single address for a PHY | |||||
* @hw: pointer to hardware structure | |||||
* @phy_addr: PHY address to probe | |||||
* | |||||
* Returns TRUE if PHY found | |||||
*/ | |||||
static bool ixgbe_probe_phy(struct ixgbe_hw *hw, u16 phy_addr) | |||||
{ | |||||
u16 ext_ability = 0; | |||||
if (!ixgbe_validate_phy_addr(hw, phy_addr)) { | |||||
DEBUGOUT1("Unable to validate PHY address 0x%04X\n", | |||||
phy_addr); | |||||
return FALSE; | |||||
} | |||||
if (ixgbe_get_phy_id(hw)) | |||||
return FALSE; | |||||
hw->phy.type = ixgbe_get_phy_type_from_id(hw->phy.id); | |||||
if (hw->phy.type == ixgbe_phy_unknown) { | |||||
hw->phy.ops.read_reg(hw, IXGBE_MDIO_PHY_EXT_ABILITY, | |||||
IXGBE_MDIO_PMA_PMD_DEV_TYPE, &ext_ability); | |||||
if (ext_ability & | |||||
(IXGBE_MDIO_PHY_10GBASET_ABILITY | | |||||
IXGBE_MDIO_PHY_1000BASET_ABILITY)) | |||||
hw->phy.type = ixgbe_phy_cu_unknown; | |||||
else | |||||
hw->phy.type = ixgbe_phy_generic; | |||||
} | |||||
return TRUE; | |||||
} | |||||
/** | |||||
* ixgbe_identify_phy_generic - Get physical layer module | * ixgbe_identify_phy_generic - Get physical layer module | ||||
* @hw: pointer to hardware structure | * @hw: pointer to hardware structure | ||||
* | * | ||||
* Determines the physical layer module found on the current adapter. | * Determines the physical layer module found on the current adapter. | ||||
**/ | **/ | ||||
s32 ixgbe_identify_phy_generic(struct ixgbe_hw *hw) | s32 ixgbe_identify_phy_generic(struct ixgbe_hw *hw) | ||||
{ | { | ||||
s32 status = IXGBE_ERR_PHY_ADDR_INVALID; | s32 status = IXGBE_ERR_PHY_ADDR_INVALID; | ||||
u32 phy_addr; | u16 phy_addr; | ||||
u16 ext_ability = 0; | |||||
DEBUGFUNC("ixgbe_identify_phy_generic"); | DEBUGFUNC("ixgbe_identify_phy_generic"); | ||||
if (!hw->phy.phy_semaphore_mask) { | if (!hw->phy.phy_semaphore_mask) { | ||||
if (hw->bus.lan_id) | if (hw->bus.lan_id) | ||||
hw->phy.phy_semaphore_mask = IXGBE_GSSR_PHY1_SM; | hw->phy.phy_semaphore_mask = IXGBE_GSSR_PHY1_SM; | ||||
else | else | ||||
hw->phy.phy_semaphore_mask = IXGBE_GSSR_PHY0_SM; | hw->phy.phy_semaphore_mask = IXGBE_GSSR_PHY0_SM; | ||||
} | } | ||||
if (hw->phy.type == ixgbe_phy_unknown) { | if (hw->phy.type != ixgbe_phy_unknown) | ||||
for (phy_addr = 0; phy_addr < IXGBE_MAX_PHY_ADDR; phy_addr++) { | return IXGBE_SUCCESS; | ||||
if (ixgbe_validate_phy_addr(hw, phy_addr)) { | |||||
hw->phy.addr = phy_addr; | |||||
ixgbe_get_phy_id(hw); | |||||
hw->phy.type = | |||||
ixgbe_get_phy_type_from_id(hw->phy.id); | |||||
if (hw->phy.type == ixgbe_phy_unknown) { | if (hw->phy.nw_mng_if_sel) { | ||||
hw->phy.ops.read_reg(hw, | phy_addr = (hw->phy.nw_mng_if_sel & | ||||
IXGBE_MDIO_PHY_EXT_ABILITY, | IXGBE_NW_MNG_IF_SEL_MDIO_PHY_ADD) >> | ||||
IXGBE_MDIO_PMA_PMD_DEV_TYPE, | IXGBE_NW_MNG_IF_SEL_MDIO_PHY_ADD_SHIFT; | ||||
&ext_ability); | if (ixgbe_probe_phy(hw, phy_addr)) | ||||
if (ext_ability & | return IXGBE_SUCCESS; | ||||
(IXGBE_MDIO_PHY_10GBASET_ABILITY | | |||||
IXGBE_MDIO_PHY_1000BASET_ABILITY)) | |||||
hw->phy.type = | |||||
ixgbe_phy_cu_unknown; | |||||
else | else | ||||
hw->phy.type = | return IXGBE_ERR_PHY_ADDR_INVALID; | ||||
ixgbe_phy_generic; | |||||
} | } | ||||
for (phy_addr = 0; phy_addr < IXGBE_MAX_PHY_ADDR; phy_addr++) { | |||||
if (ixgbe_probe_phy(hw, phy_addr)) { | |||||
status = IXGBE_SUCCESS; | status = IXGBE_SUCCESS; | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
/* Certain media types do not have a phy so an address will not | /* Certain media types do not have a phy so an address will not | ||||
* be found and the code will take this path. Caller has to | * be found and the code will take this path. Caller has to | ||||
* decide if it is an error or not. | * decide if it is an error or not. | ||||
*/ | */ | ||||
if (status != IXGBE_SUCCESS) { | if (status != IXGBE_SUCCESS) | ||||
hw->phy.addr = 0; | hw->phy.addr = 0; | ||||
} | |||||
} else { | |||||
status = IXGBE_SUCCESS; | |||||
} | |||||
return status; | return status; | ||||
} | } | ||||
/** | /** | ||||
* ixgbe_check_reset_blocked - check status of MNG FW veto bit | * ixgbe_check_reset_blocked - check status of MNG FW veto bit | ||||
* @hw: pointer to the hardware structure | * @hw: pointer to the hardware structure | ||||
* | * | ||||
Show All 36 Lines | bool ixgbe_validate_phy_addr(struct ixgbe_hw *hw, u32 phy_addr) | ||||
hw->phy.addr = phy_addr; | hw->phy.addr = phy_addr; | ||||
hw->phy.ops.read_reg(hw, IXGBE_MDIO_PHY_ID_HIGH, | hw->phy.ops.read_reg(hw, IXGBE_MDIO_PHY_ID_HIGH, | ||||
IXGBE_MDIO_PMA_PMD_DEV_TYPE, &phy_id); | IXGBE_MDIO_PMA_PMD_DEV_TYPE, &phy_id); | ||||
if (phy_id != 0xFFFF && phy_id != 0x0) | if (phy_id != 0xFFFF && phy_id != 0x0) | ||||
valid = TRUE; | valid = TRUE; | ||||
DEBUGOUT1("PHY ID HIGH is 0x%04X\n", phy_id); | |||||
return valid; | return valid; | ||||
} | } | ||||
/** | /** | ||||
* ixgbe_get_phy_id - Get the phy type | * ixgbe_get_phy_id - Get the phy type | ||||
* @hw: pointer to hardware structure | * @hw: pointer to hardware structure | ||||
* | * | ||||
**/ | **/ | ||||
Show All 12 Lines | s32 ixgbe_get_phy_id(struct ixgbe_hw *hw) | ||||
if (status == IXGBE_SUCCESS) { | if (status == IXGBE_SUCCESS) { | ||||
hw->phy.id = (u32)(phy_id_high << 16); | hw->phy.id = (u32)(phy_id_high << 16); | ||||
status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_PHY_ID_LOW, | status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_PHY_ID_LOW, | ||||
IXGBE_MDIO_PMA_PMD_DEV_TYPE, | IXGBE_MDIO_PMA_PMD_DEV_TYPE, | ||||
&phy_id_low); | &phy_id_low); | ||||
hw->phy.id |= (u32)(phy_id_low & IXGBE_PHY_REVISION_MASK); | hw->phy.id |= (u32)(phy_id_low & IXGBE_PHY_REVISION_MASK); | ||||
hw->phy.revision = (u32)(phy_id_low & ~IXGBE_PHY_REVISION_MASK); | hw->phy.revision = (u32)(phy_id_low & ~IXGBE_PHY_REVISION_MASK); | ||||
} | } | ||||
DEBUGOUT2("PHY_ID_HIGH 0x%04X, PHY_ID_LOW 0x%04X\n", | |||||
phy_id_high, phy_id_low); | |||||
return status; | return status; | ||||
} | } | ||||
/** | /** | ||||
* ixgbe_get_phy_type_from_id - Get the phy type | * ixgbe_get_phy_type_from_id - Get the phy type | ||||
* @hw: pointer to hardware structure | * @phy_id: PHY ID information | ||||
* | * | ||||
**/ | **/ | ||||
enum ixgbe_phy_type ixgbe_get_phy_type_from_id(u32 phy_id) | enum ixgbe_phy_type ixgbe_get_phy_type_from_id(u32 phy_id) | ||||
{ | { | ||||
enum ixgbe_phy_type phy_type; | enum ixgbe_phy_type phy_type; | ||||
DEBUGFUNC("ixgbe_get_phy_type_from_id"); | DEBUGFUNC("ixgbe_get_phy_type_from_id"); | ||||
switch (phy_id) { | switch (phy_id) { | ||||
case TN1010_PHY_ID: | case TN1010_PHY_ID: | ||||
phy_type = ixgbe_phy_tn; | phy_type = ixgbe_phy_tn; | ||||
break; | break; | ||||
case X550_PHY_ID1: | |||||
case X550_PHY_ID2: | case X550_PHY_ID2: | ||||
case X550_PHY_ID3: | case X550_PHY_ID3: | ||||
case X540_PHY_ID: | case X540_PHY_ID: | ||||
phy_type = ixgbe_phy_aq; | phy_type = ixgbe_phy_aq; | ||||
break; | break; | ||||
case QT2022_PHY_ID: | case QT2022_PHY_ID: | ||||
phy_type = ixgbe_phy_qt; | phy_type = ixgbe_phy_qt; | ||||
break; | break; | ||||
case ATH_PHY_ID: | case ATH_PHY_ID: | ||||
phy_type = ixgbe_phy_nl; | phy_type = ixgbe_phy_nl; | ||||
break; | break; | ||||
case X557_PHY_ID: | case X557_PHY_ID: | ||||
case X557_PHY_ID2: | |||||
phy_type = ixgbe_phy_x550em_ext_t; | phy_type = ixgbe_phy_x550em_ext_t; | ||||
break; | break; | ||||
default: | default: | ||||
phy_type = ixgbe_phy_unknown; | phy_type = ixgbe_phy_unknown; | ||||
break; | break; | ||||
} | } | ||||
DEBUGOUT1("phy type found is %d\n", phy_type); | DEBUGOUT1("phy type found is %d\n", phy_type); | ||||
Show All 37 Lines | s32 ixgbe_reset_phy_generic(struct ixgbe_hw *hw) | ||||
/* | /* | ||||
* Poll for reset bit to self-clear indicating reset is complete. | * Poll for reset bit to self-clear indicating reset is complete. | ||||
* Some PHYs could take up to 3 seconds to complete and need about | * Some PHYs could take up to 3 seconds to complete and need about | ||||
* 1.7 usec delay after the reset is complete. | * 1.7 usec delay after the reset is complete. | ||||
*/ | */ | ||||
for (i = 0; i < 30; i++) { | for (i = 0; i < 30; i++) { | ||||
msec_delay(100); | msec_delay(100); | ||||
hw->phy.ops.read_reg(hw, IXGBE_MDIO_PHY_XS_CONTROL, | if (hw->phy.type == ixgbe_phy_x550em_ext_t) { | ||||
IXGBE_MDIO_PHY_XS_DEV_TYPE, &ctrl); | status = hw->phy.ops.read_reg(hw, | ||||
IXGBE_MDIO_TX_VENDOR_ALARMS_3, | |||||
IXGBE_MDIO_PMA_PMD_DEV_TYPE, | |||||
&ctrl); | |||||
if (status != IXGBE_SUCCESS) | |||||
return status; | |||||
if (ctrl & IXGBE_MDIO_TX_VENDOR_ALARMS_3_RST_MASK) { | |||||
usec_delay(2); | |||||
break; | |||||
} | |||||
} else { | |||||
status = hw->phy.ops.read_reg(hw, | |||||
IXGBE_MDIO_PHY_XS_CONTROL, | |||||
IXGBE_MDIO_PHY_XS_DEV_TYPE, | |||||
&ctrl); | |||||
if (status != IXGBE_SUCCESS) | |||||
return status; | |||||
if (!(ctrl & IXGBE_MDIO_PHY_XS_RESET)) { | if (!(ctrl & IXGBE_MDIO_PHY_XS_RESET)) { | ||||
usec_delay(2); | usec_delay(2); | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
} | |||||
if (ctrl & IXGBE_MDIO_PHY_XS_RESET) { | if (ctrl & IXGBE_MDIO_PHY_XS_RESET) { | ||||
status = IXGBE_ERR_RESET_FAILED; | status = IXGBE_ERR_RESET_FAILED; | ||||
ERROR_REPORT1(IXGBE_ERROR_POLLING, | ERROR_REPORT1(IXGBE_ERROR_POLLING, | ||||
"PHY reset polling failed to complete.\n"); | "PHY reset polling failed to complete.\n"); | ||||
} | } | ||||
out: | out: | ||||
▲ Show 20 Lines • Show All 89 Lines • ▼ Show 20 Lines | |||||
s32 ixgbe_read_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr, | s32 ixgbe_read_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr, | ||||
u32 device_type, u16 *phy_data) | u32 device_type, u16 *phy_data) | ||||
{ | { | ||||
s32 status; | s32 status; | ||||
u32 gssr = hw->phy.phy_semaphore_mask; | u32 gssr = hw->phy.phy_semaphore_mask; | ||||
DEBUGFUNC("ixgbe_read_phy_reg_generic"); | DEBUGFUNC("ixgbe_read_phy_reg_generic"); | ||||
if (hw->mac.ops.acquire_swfw_sync(hw, gssr) == IXGBE_SUCCESS) { | if (hw->mac.ops.acquire_swfw_sync(hw, gssr)) | ||||
status = ixgbe_read_phy_reg_mdi(hw, reg_addr, device_type, | return IXGBE_ERR_SWFW_SYNC; | ||||
phy_data); | |||||
status = hw->phy.ops.read_reg_mdi(hw, reg_addr, device_type, phy_data); | |||||
hw->mac.ops.release_swfw_sync(hw, gssr); | hw->mac.ops.release_swfw_sync(hw, gssr); | ||||
} else { | |||||
status = IXGBE_ERR_SWFW_SYNC; | |||||
} | |||||
return status; | return status; | ||||
} | } | ||||
/** | /** | ||||
* ixgbe_write_phy_reg_mdi - Writes a value to specified PHY register | * ixgbe_write_phy_reg_mdi - Writes a value to specified PHY register | ||||
* without SWFW lock | * without SWFW lock | ||||
* @hw: pointer to hardware structure | * @hw: pointer to hardware structure | ||||
▲ Show 20 Lines • Show All 79 Lines • ▼ Show 20 Lines | s32 ixgbe_write_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr, | ||||
u32 device_type, u16 phy_data) | u32 device_type, u16 phy_data) | ||||
{ | { | ||||
s32 status; | s32 status; | ||||
u32 gssr = hw->phy.phy_semaphore_mask; | u32 gssr = hw->phy.phy_semaphore_mask; | ||||
DEBUGFUNC("ixgbe_write_phy_reg_generic"); | DEBUGFUNC("ixgbe_write_phy_reg_generic"); | ||||
if (hw->mac.ops.acquire_swfw_sync(hw, gssr) == IXGBE_SUCCESS) { | if (hw->mac.ops.acquire_swfw_sync(hw, gssr) == IXGBE_SUCCESS) { | ||||
status = ixgbe_write_phy_reg_mdi(hw, reg_addr, device_type, | status = hw->phy.ops.write_reg_mdi(hw, reg_addr, device_type, | ||||
phy_data); | phy_data); | ||||
hw->mac.ops.release_swfw_sync(hw, gssr); | hw->mac.ops.release_swfw_sync(hw, gssr); | ||||
} else { | } else { | ||||
status = IXGBE_ERR_SWFW_SYNC; | status = IXGBE_ERR_SWFW_SYNC; | ||||
} | } | ||||
return status; | return status; | ||||
} | } | ||||
Show All 10 Lines | s32 ixgbe_setup_phy_link_generic(struct ixgbe_hw *hw) | ||||
u16 autoneg_reg = IXGBE_MII_AUTONEG_REG; | u16 autoneg_reg = IXGBE_MII_AUTONEG_REG; | ||||
bool autoneg = FALSE; | bool autoneg = FALSE; | ||||
ixgbe_link_speed speed; | ixgbe_link_speed speed; | ||||
DEBUGFUNC("ixgbe_setup_phy_link_generic"); | DEBUGFUNC("ixgbe_setup_phy_link_generic"); | ||||
ixgbe_get_copper_link_capabilities_generic(hw, &speed, &autoneg); | ixgbe_get_copper_link_capabilities_generic(hw, &speed, &autoneg); | ||||
if (speed & IXGBE_LINK_SPEED_10GB_FULL) { | |||||
/* Set or unset auto-negotiation 10G advertisement */ | /* Set or unset auto-negotiation 10G advertisement */ | ||||
hw->phy.ops.read_reg(hw, IXGBE_MII_10GBASE_T_AUTONEG_CTRL_REG, | hw->phy.ops.read_reg(hw, IXGBE_MII_10GBASE_T_AUTONEG_CTRL_REG, | ||||
IXGBE_MDIO_AUTO_NEG_DEV_TYPE, | IXGBE_MDIO_AUTO_NEG_DEV_TYPE, | ||||
&autoneg_reg); | &autoneg_reg); | ||||
autoneg_reg &= ~IXGBE_MII_10GBASE_T_ADVERTISE; | autoneg_reg &= ~IXGBE_MII_10GBASE_T_ADVERTISE; | ||||
if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_10GB_FULL) | if ((hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_10GB_FULL) && | ||||
(speed & IXGBE_LINK_SPEED_10GB_FULL)) | |||||
autoneg_reg |= IXGBE_MII_10GBASE_T_ADVERTISE; | autoneg_reg |= IXGBE_MII_10GBASE_T_ADVERTISE; | ||||
hw->phy.ops.write_reg(hw, IXGBE_MII_10GBASE_T_AUTONEG_CTRL_REG, | hw->phy.ops.write_reg(hw, IXGBE_MII_10GBASE_T_AUTONEG_CTRL_REG, | ||||
IXGBE_MDIO_AUTO_NEG_DEV_TYPE, | IXGBE_MDIO_AUTO_NEG_DEV_TYPE, | ||||
autoneg_reg); | autoneg_reg); | ||||
} | |||||
if (hw->mac.type == ixgbe_mac_X550) { | hw->phy.ops.read_reg(hw, IXGBE_MII_AUTONEG_VENDOR_PROVISION_1_REG, | ||||
if (speed & IXGBE_LINK_SPEED_5GB_FULL) { | |||||
/* Set or unset auto-negotiation 5G advertisement */ | |||||
hw->phy.ops.read_reg(hw, | |||||
IXGBE_MII_AUTONEG_VENDOR_PROVISION_1_REG, | |||||
IXGBE_MDIO_AUTO_NEG_DEV_TYPE, | IXGBE_MDIO_AUTO_NEG_DEV_TYPE, | ||||
&autoneg_reg); | &autoneg_reg); | ||||
if (hw->mac.type == ixgbe_mac_X550) { | |||||
/* Set or unset auto-negotiation 5G advertisement */ | |||||
autoneg_reg &= ~IXGBE_MII_5GBASE_T_ADVERTISE; | autoneg_reg &= ~IXGBE_MII_5GBASE_T_ADVERTISE; | ||||
if (hw->phy.autoneg_advertised & | if ((hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_5GB_FULL) && | ||||
IXGBE_LINK_SPEED_5GB_FULL) | (speed & IXGBE_LINK_SPEED_5GB_FULL)) | ||||
autoneg_reg |= IXGBE_MII_5GBASE_T_ADVERTISE; | autoneg_reg |= IXGBE_MII_5GBASE_T_ADVERTISE; | ||||
hw->phy.ops.write_reg(hw, | |||||
IXGBE_MII_AUTONEG_VENDOR_PROVISION_1_REG, | |||||
IXGBE_MDIO_AUTO_NEG_DEV_TYPE, | |||||
autoneg_reg); | |||||
} | |||||
if (speed & IXGBE_LINK_SPEED_2_5GB_FULL) { | |||||
/* Set or unset auto-negotiation 2.5G advertisement */ | /* Set or unset auto-negotiation 2.5G advertisement */ | ||||
hw->phy.ops.read_reg(hw, | |||||
IXGBE_MII_AUTONEG_VENDOR_PROVISION_1_REG, | |||||
IXGBE_MDIO_AUTO_NEG_DEV_TYPE, | |||||
&autoneg_reg); | |||||
autoneg_reg &= ~IXGBE_MII_2_5GBASE_T_ADVERTISE; | autoneg_reg &= ~IXGBE_MII_2_5GBASE_T_ADVERTISE; | ||||
if (hw->phy.autoneg_advertised & | if ((hw->phy.autoneg_advertised & | ||||
IXGBE_LINK_SPEED_2_5GB_FULL) | IXGBE_LINK_SPEED_2_5GB_FULL) && | ||||
(speed & IXGBE_LINK_SPEED_2_5GB_FULL)) | |||||
autoneg_reg |= IXGBE_MII_2_5GBASE_T_ADVERTISE; | autoneg_reg |= IXGBE_MII_2_5GBASE_T_ADVERTISE; | ||||
hw->phy.ops.write_reg(hw, | |||||
IXGBE_MII_AUTONEG_VENDOR_PROVISION_1_REG, | |||||
IXGBE_MDIO_AUTO_NEG_DEV_TYPE, | |||||
autoneg_reg); | |||||
} | } | ||||
} | |||||
if (speed & IXGBE_LINK_SPEED_1GB_FULL) { | |||||
/* Set or unset auto-negotiation 1G advertisement */ | /* Set or unset auto-negotiation 1G advertisement */ | ||||
hw->phy.ops.read_reg(hw, | |||||
IXGBE_MII_AUTONEG_VENDOR_PROVISION_1_REG, | |||||
IXGBE_MDIO_AUTO_NEG_DEV_TYPE, | |||||
&autoneg_reg); | |||||
autoneg_reg &= ~IXGBE_MII_1GBASE_T_ADVERTISE; | autoneg_reg &= ~IXGBE_MII_1GBASE_T_ADVERTISE; | ||||
if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_1GB_FULL) | if ((hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_1GB_FULL) && | ||||
(speed & IXGBE_LINK_SPEED_1GB_FULL)) | |||||
autoneg_reg |= IXGBE_MII_1GBASE_T_ADVERTISE; | autoneg_reg |= IXGBE_MII_1GBASE_T_ADVERTISE; | ||||
hw->phy.ops.write_reg(hw, | hw->phy.ops.write_reg(hw, IXGBE_MII_AUTONEG_VENDOR_PROVISION_1_REG, | ||||
IXGBE_MII_AUTONEG_VENDOR_PROVISION_1_REG, | |||||
IXGBE_MDIO_AUTO_NEG_DEV_TYPE, | IXGBE_MDIO_AUTO_NEG_DEV_TYPE, | ||||
autoneg_reg); | autoneg_reg); | ||||
} | |||||
if (speed & IXGBE_LINK_SPEED_100_FULL) { | |||||
/* Set or unset auto-negotiation 100M advertisement */ | /* Set or unset auto-negotiation 100M advertisement */ | ||||
hw->phy.ops.read_reg(hw, IXGBE_MII_AUTONEG_ADVERTISE_REG, | hw->phy.ops.read_reg(hw, IXGBE_MII_AUTONEG_ADVERTISE_REG, | ||||
IXGBE_MDIO_AUTO_NEG_DEV_TYPE, | IXGBE_MDIO_AUTO_NEG_DEV_TYPE, | ||||
&autoneg_reg); | &autoneg_reg); | ||||
autoneg_reg &= ~(IXGBE_MII_100BASE_T_ADVERTISE | | autoneg_reg &= ~(IXGBE_MII_100BASE_T_ADVERTISE | | ||||
IXGBE_MII_100BASE_T_ADVERTISE_HALF); | IXGBE_MII_100BASE_T_ADVERTISE_HALF); | ||||
if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_100_FULL) | if ((hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_100_FULL) && | ||||
(speed & IXGBE_LINK_SPEED_100_FULL)) | |||||
autoneg_reg |= IXGBE_MII_100BASE_T_ADVERTISE; | autoneg_reg |= IXGBE_MII_100BASE_T_ADVERTISE; | ||||
hw->phy.ops.write_reg(hw, IXGBE_MII_AUTONEG_ADVERTISE_REG, | hw->phy.ops.write_reg(hw, IXGBE_MII_AUTONEG_ADVERTISE_REG, | ||||
IXGBE_MDIO_AUTO_NEG_DEV_TYPE, | IXGBE_MDIO_AUTO_NEG_DEV_TYPE, | ||||
autoneg_reg); | autoneg_reg); | ||||
} | |||||
/* Blocked by MNG FW so don't reset PHY */ | /* Blocked by MNG FW so don't reset PHY */ | ||||
if (ixgbe_check_reset_blocked(hw)) | if (ixgbe_check_reset_blocked(hw)) | ||||
return status; | return status; | ||||
/* Restart PHY auto-negotiation. */ | /* Restart PHY auto-negotiation. */ | ||||
hw->phy.ops.read_reg(hw, IXGBE_MDIO_AUTO_NEG_CONTROL, | hw->phy.ops.read_reg(hw, IXGBE_MDIO_AUTO_NEG_CONTROL, | ||||
IXGBE_MDIO_AUTO_NEG_DEV_TYPE, &autoneg_reg); | IXGBE_MDIO_AUTO_NEG_DEV_TYPE, &autoneg_reg); | ||||
Show All 35 Lines | if (speed & IXGBE_LINK_SPEED_2_5GB_FULL) | ||||
hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_2_5GB_FULL; | hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_2_5GB_FULL; | ||||
if (speed & IXGBE_LINK_SPEED_1GB_FULL) | if (speed & IXGBE_LINK_SPEED_1GB_FULL) | ||||
hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_1GB_FULL; | hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_1GB_FULL; | ||||
if (speed & IXGBE_LINK_SPEED_100_FULL) | if (speed & IXGBE_LINK_SPEED_100_FULL) | ||||
hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_100_FULL; | hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_100_FULL; | ||||
if (speed & IXGBE_LINK_SPEED_10_FULL) | |||||
hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_10_FULL; | |||||
/* Setup link based on the new speed settings */ | /* Setup link based on the new speed settings */ | ||||
ixgbe_setup_phy_link(hw); | ixgbe_setup_phy_link(hw); | ||||
return IXGBE_SUCCESS; | return IXGBE_SUCCESS; | ||||
} | } | ||||
/** | /** | ||||
* ixgbe_get_copper_speeds_supported - Get copper link speeds from phy | * ixgbe_get_copper_speeds_supported - Get copper link speeds from phy | ||||
Show All 21 Lines | if (speed_ability & IXGBE_MDIO_PHY_SPEED_100M) | ||||
hw->phy.speeds_supported |= IXGBE_LINK_SPEED_100_FULL; | hw->phy.speeds_supported |= IXGBE_LINK_SPEED_100_FULL; | ||||
switch (hw->mac.type) { | switch (hw->mac.type) { | ||||
case ixgbe_mac_X550: | case ixgbe_mac_X550: | ||||
hw->phy.speeds_supported |= IXGBE_LINK_SPEED_2_5GB_FULL; | hw->phy.speeds_supported |= IXGBE_LINK_SPEED_2_5GB_FULL; | ||||
hw->phy.speeds_supported |= IXGBE_LINK_SPEED_5GB_FULL; | hw->phy.speeds_supported |= IXGBE_LINK_SPEED_5GB_FULL; | ||||
break; | break; | ||||
case ixgbe_mac_X550EM_x: | case ixgbe_mac_X550EM_x: | ||||
case ixgbe_mac_X550EM_a: | |||||
hw->phy.speeds_supported &= ~IXGBE_LINK_SPEED_100_FULL; | hw->phy.speeds_supported &= ~IXGBE_LINK_SPEED_100_FULL; | ||||
break; | break; | ||||
default: | default: | ||||
break; | break; | ||||
} | } | ||||
return status; | return status; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 587 Lines • ▼ Show 20 Lines | if (!(enforce_sfp & IXGBE_DEVICE_CAPS_ALLOW_ANY_SFP) && | ||||
"Connections are quality tested " | "Connections are quality tested " | ||||
"using Intel (R) Ethernet Optics." | "using Intel (R) Ethernet Optics." | ||||
" Using untested modules is not " | " Using untested modules is not " | ||||
"supported and may cause unstable" | "supported and may cause unstable" | ||||
" operation or damage to the " | " operation or damage to the " | ||||
"module or the adapter. Intel " | "module or the adapter. Intel " | ||||
"Corporation is not responsible " | "Corporation is not responsible " | ||||
"for any harm caused by using " | "for any harm caused by using " | ||||
"untested modules.\n", status); | "untested modules.\n"); | ||||
status = IXGBE_SUCCESS; | status = IXGBE_SUCCESS; | ||||
} else { | } else { | ||||
DEBUGOUT("SFP+ module not supported\n"); | DEBUGOUT("SFP+ module not supported\n"); | ||||
hw->phy.type = | hw->phy.type = | ||||
ixgbe_phy_sfp_unsupported; | ixgbe_phy_sfp_unsupported; | ||||
status = IXGBE_ERR_SFP_NOT_SUPPORTED; | status = IXGBE_ERR_SFP_NOT_SUPPORTED; | ||||
} | } | ||||
} | } | ||||
Show All 15 Lines | |||||
} | } | ||||
/** | /** | ||||
* ixgbe_get_supported_phy_sfp_layer_generic - Returns physical layer type | * ixgbe_get_supported_phy_sfp_layer_generic - Returns physical layer type | ||||
* @hw: pointer to hardware structure | * @hw: pointer to hardware structure | ||||
* | * | ||||
* Determines physical layer capabilities of the current SFP. | * Determines physical layer capabilities of the current SFP. | ||||
*/ | */ | ||||
s32 ixgbe_get_supported_phy_sfp_layer_generic(struct ixgbe_hw *hw) | u64 ixgbe_get_supported_phy_sfp_layer_generic(struct ixgbe_hw *hw) | ||||
{ | { | ||||
u32 physical_layer = IXGBE_PHYSICAL_LAYER_UNKNOWN; | u64 physical_layer = IXGBE_PHYSICAL_LAYER_UNKNOWN; | ||||
u8 comp_codes_10g = 0; | u8 comp_codes_10g = 0; | ||||
u8 comp_codes_1g = 0; | u8 comp_codes_1g = 0; | ||||
DEBUGFUNC("ixgbe_get_supported_phy_sfp_layer_generic"); | DEBUGFUNC("ixgbe_get_supported_phy_sfp_layer_generic"); | ||||
hw->phy.ops.identify_sfp(hw); | hw->phy.ops.identify_sfp(hw); | ||||
if (hw->phy.sfp_type == ixgbe_sfp_type_not_present) | if (hw->phy.sfp_type == ixgbe_sfp_type_not_present) | ||||
return physical_layer; | return physical_layer; | ||||
▲ Show 20 Lines • Show All 211 Lines • ▼ Show 20 Lines | if (!(enforce_sfp & IXGBE_DEVICE_CAPS_ALLOW_ANY_SFP)) { | ||||
"Connections are quality tested " | "Connections are quality tested " | ||||
"using Intel (R) Ethernet Optics." | "using Intel (R) Ethernet Optics." | ||||
" Using untested modules is not " | " Using untested modules is not " | ||||
"supported and may cause unstable" | "supported and may cause unstable" | ||||
" operation or damage to the " | " operation or damage to the " | ||||
"module or the adapter. Intel " | "module or the adapter. Intel " | ||||
"Corporation is not responsible " | "Corporation is not responsible " | ||||
"for any harm caused by using " | "for any harm caused by using " | ||||
"untested modules.\n", status); | "untested modules.\n"); | ||||
status = IXGBE_SUCCESS; | status = IXGBE_SUCCESS; | ||||
} else { | } else { | ||||
DEBUGOUT("QSFP module not supported\n"); | DEBUGOUT("QSFP module not supported\n"); | ||||
hw->phy.type = | hw->phy.type = | ||||
ixgbe_phy_sfp_unsupported; | ixgbe_phy_sfp_unsupported; | ||||
status = IXGBE_ERR_SFP_NOT_SUPPORTED; | status = IXGBE_ERR_SFP_NOT_SUPPORTED; | ||||
} | } | ||||
} | } | ||||
▲ Show 20 Lines • Show All 891 Lines • Show Last 20 Lines |