i40e/base: support LED blinking with new PHY

This patch adds functions to blink led on devices using a new
PHY since MAC registers used in other designs do not work in
this device configuration.

Signed-off-by: Helin Zhang <helin.zhang@intel.com>
Acked-by: Jingjing Wu <jingjing.wu@intel.com>
Acked-by: Remy Horton <remy.horton@intel.com>
This commit is contained in:
Helin Zhang 2016-03-08 16:14:26 +08:00 committed by Thomas Monjalon
parent 3db9af65de
commit fd72a2284a
3 changed files with 358 additions and 0 deletions

View File

@ -5969,6 +5969,335 @@ enum i40e_status_code i40e_aq_configure_partition_bw(struct i40e_hw *hw,
return status;
}
/**
* i40e_read_phy_register
* @hw: pointer to the HW structure
* @page: registers page number
* @reg: register address in the page
* @phy_adr: PHY address on MDIO interface
* @value: PHY register value
*
* Reads specified PHY register value
**/
enum i40e_status_code i40e_read_phy_register(struct i40e_hw *hw,
u8 page, u16 reg, u8 phy_addr,
u16 *value)
{
enum i40e_status_code status = I40E_ERR_TIMEOUT;
u32 command = 0;
u16 retry = 1000;
u8 port_num = (u8)hw->func_caps.mdio_port_num;
command = (reg << I40E_GLGEN_MSCA_MDIADD_SHIFT) |
(page << I40E_GLGEN_MSCA_DEVADD_SHIFT) |
(phy_addr << I40E_GLGEN_MSCA_PHYADD_SHIFT) |
(I40E_MDIO_OPCODE_ADDRESS) |
(I40E_MDIO_STCODE) |
(I40E_GLGEN_MSCA_MDICMD_MASK) |
(I40E_GLGEN_MSCA_MDIINPROGEN_MASK);
wr32(hw, I40E_GLGEN_MSCA(port_num), command);
do {
command = rd32(hw, I40E_GLGEN_MSCA(port_num));
if (!(command & I40E_GLGEN_MSCA_MDICMD_MASK)) {
status = I40E_SUCCESS;
break;
}
i40e_usec_delay(10);
retry--;
} while (retry);
if (status) {
i40e_debug(hw, I40E_DEBUG_PHY,
"PHY: Can't write command to external PHY.\n");
goto phy_read_end;
}
command = (page << I40E_GLGEN_MSCA_DEVADD_SHIFT) |
(phy_addr << I40E_GLGEN_MSCA_PHYADD_SHIFT) |
(I40E_MDIO_OPCODE_READ) |
(I40E_MDIO_STCODE) |
(I40E_GLGEN_MSCA_MDICMD_MASK) |
(I40E_GLGEN_MSCA_MDIINPROGEN_MASK);
status = I40E_ERR_TIMEOUT;
retry = 1000;
wr32(hw, I40E_GLGEN_MSCA(port_num), command);
do {
command = rd32(hw, I40E_GLGEN_MSCA(port_num));
if (!(command & I40E_GLGEN_MSCA_MDICMD_MASK)) {
status = I40E_SUCCESS;
break;
}
i40e_usec_delay(10);
retry--;
} while (retry);
if (!status) {
command = rd32(hw, I40E_GLGEN_MSRWD(port_num));
*value = (command & I40E_GLGEN_MSRWD_MDIRDDATA_MASK) >>
I40E_GLGEN_MSRWD_MDIRDDATA_SHIFT;
} else {
i40e_debug(hw, I40E_DEBUG_PHY,
"PHY: Can't read register value from external PHY.\n");
}
phy_read_end:
return status;
}
/**
* i40e_write_phy_register
* @hw: pointer to the HW structure
* @page: registers page number
* @reg: register address in the page
* @phy_adr: PHY address on MDIO interface
* @value: PHY register value
*
* Writes value to specified PHY register
**/
enum i40e_status_code i40e_write_phy_register(struct i40e_hw *hw,
u8 page, u16 reg, u8 phy_addr,
u16 value)
{
enum i40e_status_code status = I40E_ERR_TIMEOUT;
u32 command = 0;
u16 retry = 1000;
u8 port_num = (u8)hw->func_caps.mdio_port_num;
command = (reg << I40E_GLGEN_MSCA_MDIADD_SHIFT) |
(page << I40E_GLGEN_MSCA_DEVADD_SHIFT) |
(phy_addr << I40E_GLGEN_MSCA_PHYADD_SHIFT) |
(I40E_MDIO_OPCODE_ADDRESS) |
(I40E_MDIO_STCODE) |
(I40E_GLGEN_MSCA_MDICMD_MASK) |
(I40E_GLGEN_MSCA_MDIINPROGEN_MASK);
wr32(hw, I40E_GLGEN_MSCA(port_num), command);
do {
command = rd32(hw, I40E_GLGEN_MSCA(port_num));
if (!(command & I40E_GLGEN_MSCA_MDICMD_MASK)) {
status = I40E_SUCCESS;
break;
}
i40e_usec_delay(10);
retry--;
} while (retry);
if (status) {
i40e_debug(hw, I40E_DEBUG_PHY,
"PHY: Can't write command to external PHY.\n");
goto phy_write_end;
}
command = value << I40E_GLGEN_MSRWD_MDIWRDATA_SHIFT;
wr32(hw, I40E_GLGEN_MSRWD(port_num), command);
command = (page << I40E_GLGEN_MSCA_DEVADD_SHIFT) |
(phy_addr << I40E_GLGEN_MSCA_PHYADD_SHIFT) |
(I40E_MDIO_OPCODE_WRITE) |
(I40E_MDIO_STCODE) |
(I40E_GLGEN_MSCA_MDICMD_MASK) |
(I40E_GLGEN_MSCA_MDIINPROGEN_MASK);
status = I40E_ERR_TIMEOUT;
retry = 1000;
wr32(hw, I40E_GLGEN_MSCA(port_num), command);
do {
command = rd32(hw, I40E_GLGEN_MSCA(port_num));
if (!(command & I40E_GLGEN_MSCA_MDICMD_MASK)) {
status = I40E_SUCCESS;
break;
}
i40e_usec_delay(10);
retry--;
} while (retry);
phy_write_end:
return status;
}
/**
* i40e_get_phy_address
* @hw: pointer to the HW structure
* @dev_num: PHY port num that address we want
* @phy_addr: Returned PHY address
*
* Gets PHY address for current port
**/
u8 i40e_get_phy_address(struct i40e_hw *hw, u8 dev_num)
{
u8 port_num = (u8)hw->func_caps.mdio_port_num;
u32 reg_val = rd32(hw, I40E_GLGEN_MDIO_I2C_SEL(port_num));
return (u8)(reg_val >> ((dev_num + 1) * 5)) & 0x1f;
}
/**
* i40e_blink_phy_led
* @hw: pointer to the HW structure
* @time: time how long led will blinks in secs
* @interval: gap between LED on and off in msecs
*
* Blinks PHY link LED
**/
enum i40e_status_code i40e_blink_phy_link_led(struct i40e_hw *hw,
u32 time, u32 interval)
{
enum i40e_status_code status = I40E_SUCCESS;
u32 i;
u16 led_ctl = 0;
u16 gpio_led_port;
u16 led_reg;
u16 led_addr = I40E_PHY_LED_PROV_REG_1;
u8 phy_addr = 0;
u8 port_num;
i = rd32(hw, I40E_PFGEN_PORTNUM);
port_num = (u8)(i & I40E_PFGEN_PORTNUM_PORT_NUM_MASK);
phy_addr = i40e_get_phy_address(hw, port_num);
for (gpio_led_port = 0; gpio_led_port < 3; gpio_led_port++,
led_addr++) {
status = i40e_read_phy_register(hw, I40E_PHY_COM_REG_PAGE,
led_addr, phy_addr, &led_reg);
if (status)
goto phy_blinking_end;
led_ctl = led_reg;
if (led_reg & I40E_PHY_LED_LINK_MODE_MASK) {
led_reg = 0;
status = i40e_write_phy_register(hw,
I40E_PHY_COM_REG_PAGE,
led_addr, phy_addr,
led_reg);
if (status)
goto phy_blinking_end;
break;
}
}
if (time > 0 && interval > 0) {
for (i = 0; i < time * 1000; i += interval) {
status = i40e_read_phy_register(hw,
I40E_PHY_COM_REG_PAGE,
led_addr, phy_addr,
&led_reg);
if (status)
goto restore_config;
if (led_reg & I40E_PHY_LED_MANUAL_ON)
led_reg = 0;
else
led_reg = I40E_PHY_LED_MANUAL_ON;
status = i40e_write_phy_register(hw,
I40E_PHY_COM_REG_PAGE,
led_addr, phy_addr,
led_reg);
if (status)
goto restore_config;
i40e_msec_delay(interval);
}
}
restore_config:
status = i40e_write_phy_register(hw, I40E_PHY_COM_REG_PAGE, led_addr,
phy_addr, led_ctl);
phy_blinking_end:
return status;
}
/**
* i40e_led_get_phy - return current on/off mode
* @hw: pointer to the hw struct
* @led_addr: address of led register to use
* @val: original value of register to use
*
**/
enum i40e_status_code i40e_led_get_phy(struct i40e_hw *hw, u16 *led_addr,
u16 *val)
{
enum i40e_status_code status = I40E_SUCCESS;
u16 gpio_led_port;
u8 phy_addr = 0;
u16 reg_val;
u16 temp_addr;
u8 port_num;
u32 i;
temp_addr = I40E_PHY_LED_PROV_REG_1;
i = rd32(hw, I40E_PFGEN_PORTNUM);
port_num = (u8)(i & I40E_PFGEN_PORTNUM_PORT_NUM_MASK);
phy_addr = i40e_get_phy_address(hw, port_num);
for (gpio_led_port = 0; gpio_led_port < 3; gpio_led_port++,
temp_addr++) {
status = i40e_read_phy_register(hw, I40E_PHY_COM_REG_PAGE,
temp_addr, phy_addr, &reg_val);
if (status)
return status;
*val = reg_val;
if (reg_val & I40E_PHY_LED_LINK_MODE_MASK) {
*led_addr = temp_addr;
break;
}
}
return status;
}
/**
* i40e_led_set_phy
* @hw: pointer to the HW structure
* @on: true or false
* @mode: original val plus bit for set or ignore
* Set led's on or off when controlled by the PHY
*
**/
enum i40e_status_code i40e_led_set_phy(struct i40e_hw *hw, bool on,
u16 led_addr, u32 mode)
{
enum i40e_status_code status = I40E_SUCCESS;
u16 led_ctl = 0;
u16 led_reg = 0;
u8 phy_addr = 0;
u8 port_num;
u32 i;
i = rd32(hw, I40E_PFGEN_PORTNUM);
port_num = (u8)(i & I40E_PFGEN_PORTNUM_PORT_NUM_MASK);
phy_addr = i40e_get_phy_address(hw, port_num);
status = i40e_read_phy_register(hw, I40E_PHY_COM_REG_PAGE, led_addr,
phy_addr, &led_reg);
if (status)
return status;
led_ctl = led_reg;
if (led_reg & I40E_PHY_LED_LINK_MODE_MASK) {
led_reg = 0;
status = i40e_write_phy_register(hw, I40E_PHY_COM_REG_PAGE,
led_addr, phy_addr, led_reg);
if (status)
return status;
}
status = i40e_read_phy_register(hw, I40E_PHY_COM_REG_PAGE,
led_addr, phy_addr, &led_reg);
if (status)
goto restore_config;
if (on)
led_reg = I40E_PHY_LED_MANUAL_ON;
else
led_reg = 0;
status = i40e_write_phy_register(hw, I40E_PHY_COM_REG_PAGE,
led_addr, phy_addr, led_reg);
if (status)
goto restore_config;
if (mode & I40E_PHY_LED_MODE_ORIG) {
led_ctl = (mode & I40E_PHY_LED_MODE_MASK);
status = i40e_write_phy_register(hw,
I40E_PHY_COM_REG_PAGE,
led_addr, phy_addr, led_ctl);
}
return status;
restore_config:
status = i40e_write_phy_register(hw, I40E_PHY_COM_REG_PAGE, led_addr,
phy_addr, led_ctl);
return status;
}
#endif /* PF_DRIVER */
#ifdef VF_DRIVER

View File

@ -99,6 +99,12 @@ const char *i40e_stat_str(struct i40e_hw *hw, enum i40e_status_code stat_err);
u32 i40e_led_get(struct i40e_hw *hw);
void i40e_led_set(struct i40e_hw *hw, u32 mode, bool blink);
enum i40e_status_code i40e_led_set_phy(struct i40e_hw *hw, bool on,
u16 led_addr, u32 mode);
enum i40e_status_code i40e_led_get_phy(struct i40e_hw *hw, u16 *led_addr,
u16 *val);
enum i40e_status_code i40e_blink_phy_link_led(struct i40e_hw *hw,
u32 time, u32 interval);
/* admin send queue commands */
@ -527,4 +533,11 @@ enum i40e_status_code i40e_aq_get_wake_event_reason(struct i40e_hw *hw,
u16 *wake_reason,
struct i40e_asq_cmd_details *cmd_details);
#endif
enum i40e_status_code i40e_read_phy_register(struct i40e_hw *hw, u8 page,
u16 reg, u8 phy_addr, u16 *value);
enum i40e_status_code i40e_write_phy_register(struct i40e_hw *hw, u8 page,
u16 reg, u8 phy_addr, u16 value);
u8 i40e_get_phy_address(struct i40e_hw *hw, u8 dev_num);
enum i40e_status_code i40e_blink_phy_link_led(struct i40e_hw *hw,
u32 time, u32 interval);
#endif /* _I40E_PROTOTYPE_H_ */

View File

@ -157,6 +157,22 @@ enum i40e_debug_mask {
#define I40E_PCI_LINK_SPEED_5000 0x2
#define I40E_PCI_LINK_SPEED_8000 0x3
#define I40E_MDIO_STCODE 0
#define I40E_MDIO_OPCODE_ADDRESS 0
#define I40E_MDIO_OPCODE_WRITE I40E_MASK(1, \
I40E_GLGEN_MSCA_OPCODE_SHIFT)
#define I40E_MDIO_OPCODE_READ_INC_ADDR I40E_MASK(2, \
I40E_GLGEN_MSCA_OPCODE_SHIFT)
#define I40E_MDIO_OPCODE_READ I40E_MASK(3, \
I40E_GLGEN_MSCA_OPCODE_SHIFT)
#define I40E_PHY_COM_REG_PAGE 0x1E
#define I40E_PHY_LED_LINK_MODE_MASK 0xF0
#define I40E_PHY_LED_MANUAL_ON 0x100
#define I40E_PHY_LED_PROV_REG_1 0xC430
#define I40E_PHY_LED_MODE_MASK 0xFFFF
#define I40E_PHY_LED_MODE_ORIG 0x80000000
/* Memory types */
enum i40e_memset_type {
I40E_NONDMA_MEM = 0,