i40e/base: support LED blinking with new PHY
authorHelin Zhang <helin.zhang@intel.com>
Tue, 8 Mar 2016 08:14:26 +0000 (16:14 +0800)
committerThomas Monjalon <thomas.monjalon@6wind.com>
Wed, 16 Mar 2016 16:31:50 +0000 (17:31 +0100)
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>
drivers/net/i40e/base/i40e_common.c
drivers/net/i40e/base/i40e_prototype.h
drivers/net/i40e/base/i40e_type.h

index c800fd8..2383153 100644 (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
 
index cbe9961..e0a409f 100644 (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_ */
index f566e30..61ee166 100644 (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,