📄 phy.c
字号:
phy->remote_rx = e1000_1000t_rx_status_undefined; } return ret_val;}/** * e1000e_get_phy_info_igp - Retrieve igp PHY information * @hw: pointer to the HW structure * * Read PHY status to determine if link is up. If link is up, then * set/determine 10base-T extended distance and polarity correction. Read * PHY port status to determine MDI/MDIx and speed. Based on the speed, * determine on the cable length, local and remote receiver. **/s32 e1000e_get_phy_info_igp(struct e1000_hw *hw){ struct e1000_phy_info *phy = &hw->phy; s32 ret_val; u16 data; bool link; ret_val = e1000e_phy_has_link_generic(hw, 1, 0, &link); if (ret_val) return ret_val; if (!link) { hw_dbg(hw, "Phy info is only valid if link is up\n"); return -E1000_ERR_CONFIG; } phy->polarity_correction = 1; ret_val = e1000_check_polarity_igp(hw); if (ret_val) return ret_val; ret_val = e1e_rphy(hw, IGP01E1000_PHY_PORT_STATUS, &data); if (ret_val) return ret_val; phy->is_mdix = (data & IGP01E1000_PSSR_MDIX); if ((data & IGP01E1000_PSSR_SPEED_MASK) == IGP01E1000_PSSR_SPEED_1000MBPS) { ret_val = e1000_get_cable_length(hw); if (ret_val) return ret_val; ret_val = e1e_rphy(hw, PHY_1000T_STATUS, &data); if (ret_val) return ret_val; phy->local_rx = (data & SR_1000T_LOCAL_RX_STATUS) ? e1000_1000t_rx_status_ok : e1000_1000t_rx_status_not_ok; phy->remote_rx = (data & SR_1000T_REMOTE_RX_STATUS) ? e1000_1000t_rx_status_ok : e1000_1000t_rx_status_not_ok; } else { phy->cable_length = E1000_CABLE_LENGTH_UNDEFINED; phy->local_rx = e1000_1000t_rx_status_undefined; phy->remote_rx = e1000_1000t_rx_status_undefined; } return ret_val;}/** * e1000e_phy_sw_reset - PHY software reset * @hw: pointer to the HW structure * * Does a software reset of the PHY by reading the PHY control register and * setting/write the control register reset bit to the PHY. **/s32 e1000e_phy_sw_reset(struct e1000_hw *hw){ s32 ret_val; u16 phy_ctrl; ret_val = e1e_rphy(hw, PHY_CONTROL, &phy_ctrl); if (ret_val) return ret_val; phy_ctrl |= MII_CR_RESET; ret_val = e1e_wphy(hw, PHY_CONTROL, phy_ctrl); if (ret_val) return ret_val; udelay(1); return ret_val;}/** * e1000e_phy_hw_reset_generic - PHY hardware reset * @hw: pointer to the HW structure * * Verify the reset block is not blocking us from resetting. Acquire * semaphore (if necessary) and read/set/write the device control reset * bit in the PHY. Wait the appropriate delay time for the device to * reset and release the semaphore (if necessary). **/s32 e1000e_phy_hw_reset_generic(struct e1000_hw *hw){ struct e1000_phy_info *phy = &hw->phy; s32 ret_val; u32 ctrl; ret_val = e1000_check_reset_block(hw); if (ret_val) return 0; ret_val = phy->ops.acquire_phy(hw); if (ret_val) return ret_val; ctrl = er32(CTRL); ew32(CTRL, ctrl | E1000_CTRL_PHY_RST); e1e_flush(); udelay(phy->reset_delay_us); ew32(CTRL, ctrl); e1e_flush(); udelay(150); phy->ops.release_phy(hw); return e1000_get_phy_cfg_done(hw);}/** * e1000e_get_cfg_done - Generic configuration done * @hw: pointer to the HW structure * * Generic function to wait 10 milli-seconds for configuration to complete * and return success. **/s32 e1000e_get_cfg_done(struct e1000_hw *hw){ mdelay(10); return 0;}/* Internal function pointers *//** * e1000_get_phy_cfg_done - Generic PHY configuration done * @hw: pointer to the HW structure * * Return success if silicon family did not implement a family specific * get_cfg_done function. **/static s32 e1000_get_phy_cfg_done(struct e1000_hw *hw){ if (hw->phy.ops.get_cfg_done) return hw->phy.ops.get_cfg_done(hw); return 0;}/** * e1000_phy_force_speed_duplex - Generic force PHY speed/duplex * @hw: pointer to the HW structure * * When the silicon family has not implemented a forced speed/duplex * function for the PHY, simply return 0. **/static s32 e1000_phy_force_speed_duplex(struct e1000_hw *hw){ if (hw->phy.ops.force_speed_duplex) return hw->phy.ops.force_speed_duplex(hw); return 0;}/** * e1000e_get_phy_type_from_id - Get PHY type from id * @phy_id: phy_id read from the phy * * Returns the phy type from the id. **/enum e1000_phy_type e1000e_get_phy_type_from_id(u32 phy_id){ enum e1000_phy_type phy_type = e1000_phy_unknown; switch (phy_id) { case M88E1000_I_PHY_ID: case M88E1000_E_PHY_ID: case M88E1111_I_PHY_ID: case M88E1011_I_PHY_ID: phy_type = e1000_phy_m88; break; case IGP01E1000_I_PHY_ID: /* IGP 1 & 2 share this */ phy_type = e1000_phy_igp_2; break; case GG82563_E_PHY_ID: phy_type = e1000_phy_gg82563; break; case IGP03E1000_E_PHY_ID: phy_type = e1000_phy_igp_3; break; case IFE_E_PHY_ID: case IFE_PLUS_E_PHY_ID: case IFE_C_E_PHY_ID: phy_type = e1000_phy_ife; break; case BME1000_E_PHY_ID: case BME1000_E_PHY_ID_R2: phy_type = e1000_phy_bm; break; default: phy_type = e1000_phy_unknown; break; } return phy_type;}/** * e1000e_determine_phy_address - Determines PHY address. * @hw: pointer to the HW structure * * This uses a trial and error method to loop through possible PHY * addresses. It tests each by reading the PHY ID registers and * checking for a match. **/s32 e1000e_determine_phy_address(struct e1000_hw *hw){ s32 ret_val = -E1000_ERR_PHY_TYPE; u32 phy_addr= 0; u32 i = 0; enum e1000_phy_type phy_type = e1000_phy_unknown; do { for (phy_addr = 0; phy_addr < 4; phy_addr++) { hw->phy.addr = phy_addr; e1000e_get_phy_id(hw); phy_type = e1000e_get_phy_type_from_id(hw->phy.id); /* * If phy_type is valid, break - we found our * PHY address */ if (phy_type != e1000_phy_unknown) { ret_val = 0; break; } } i++; } while ((ret_val != 0) && (i < 100)); return ret_val;}/** * e1000_get_phy_addr_for_bm_page - Retrieve PHY page address * @page: page to access * * Returns the phy address for the page requested. **/static u32 e1000_get_phy_addr_for_bm_page(u32 page, u32 reg){ u32 phy_addr = 2; if ((page >= 768) || (page == 0 && reg == 25) || (reg == 31)) phy_addr = 1; return phy_addr;}/** * e1000e_write_phy_reg_bm - Write BM PHY register * @hw: pointer to the HW structure * @offset: register offset to write to * @data: data to write at register offset * * Acquires semaphore, if necessary, then writes the data to PHY register * at the offset. Release any acquired semaphores before exiting. **/s32 e1000e_write_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 data){ s32 ret_val; u32 page_select = 0; u32 page = offset >> IGP_PAGE_SHIFT; u32 page_shift = 0; /* Page 800 works differently than the rest so it has its own func */ if (page == BM_WUC_PAGE) { ret_val = e1000_access_phy_wakeup_reg_bm(hw, offset, &data, FALSE); goto out; } ret_val = hw->phy.ops.acquire_phy(hw); if (ret_val) goto out; hw->phy.addr = e1000_get_phy_addr_for_bm_page(page, offset); if (offset > MAX_PHY_MULTI_PAGE_REG) { /* * Page select is register 31 for phy address 1 and 22 for * phy address 2 and 3. Page select is shifted only for * phy address 1. */ if (hw->phy.addr == 1) { page_shift = IGP_PAGE_SHIFT; page_select = IGP01E1000_PHY_PAGE_SELECT; } else { page_shift = 0; page_select = BM_PHY_PAGE_SELECT; } /* Page is shifted left, PHY expects (page x 32) */ ret_val = e1000e_write_phy_reg_mdic(hw, page_select, (page << page_shift)); if (ret_val) { hw->phy.ops.release_phy(hw); goto out; } } ret_val = e1000e_write_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset, data); hw->phy.ops.release_phy(hw);out: return ret_val;}/** * e1000e_read_phy_reg_bm - Read BM PHY register * @hw: pointer to the HW structure * @offset: register offset to be read * @data: pointer to the read data * * Acquires semaphore, if necessary, then reads the PHY register at offset * and storing the retrieved information in data. Release any acquired * semaphores before exiting. **/s32 e1000e_read_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 *data){ s32 ret_val; u32 page_select = 0; u32 page = offset >> IGP_PAGE_SHIFT; u32 page_shift = 0; /* Page 800 works differently than the rest so it has its own func */ if (page == BM_WUC_PAGE) { ret_val = e1000_access_phy_wakeup_reg_bm(hw, offset, data, TRUE); goto out; } ret_val = hw->phy.ops.acquire_phy(hw); if (ret_val) goto out; hw->phy.addr = e1000_get_phy_addr_for_bm_page(page, offset); if (offset > MAX_PHY_MULTI_PAGE_REG) { /* * Page select is register 31 for phy address 1 and 22 for * phy address 2 and 3. Page select is shifted only for * phy address 1. */ if (hw->phy.addr == 1) { page_shift = IGP_PAGE_SHIFT; page_select = IGP01E1000_PHY_PAGE_SELECT; } else { page_shift = 0; page_select = BM_PHY_PAGE_SELECT; } /* Page is shifted left, PHY expects (page x 32) */ ret_val = e1000e_write_phy_reg_mdic(hw, page_select, (page << page_shift)); if (ret_val) { hw->phy.ops.release_phy(hw); goto out; } } ret_val = e1000e_read_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset, data); hw->phy.ops.release_phy(hw);out: return ret_val;}/** * e1000_access_phy_wakeup_reg_bm - Read BM PHY wakeup register * @hw: pointer to the HW structure * @offset: register offset to be read or written * @data: pointer to the data to read or write * @read: determines if operation is read or write * * Acquires semaphore, if necessary, then reads the PHY register at offset * and storing the retrieved information in data. Release any acquired * semaphores before exiting. Note that procedure to read the wakeup * registers are different. It works as such: * 1) Set page 769, register 17, bit 2 = 1 * 2) Set page to 800 for host (801 if we were manageability) * 3) Write the address using the address opcode (0x11) * 4) Read or write the data using the data opcode (0x12) * 5) Restore 769_17.2 to its original value **/static s32 e1000_access_phy_wakeup_reg_bm(struct e1000_hw *hw, u32 offset, u16 *data, bool read){ s32 ret_val; u16 reg = ((u16)offset) & PHY_REG_MASK; u16 phy_reg = 0; u8 phy_acquired = 1; ret_val = hw->phy.ops.acquire_phy(hw); if (ret_val) { phy_acquired = 0; goto out; } /* All operations in this function are phy address 1 */ hw->phy.addr = 1; /* Set page 769 */ e1000e_write_phy_reg_mdic(hw, IGP01E1000_PHY_PAGE_SELECT, (BM_WUC_ENABLE_PAGE << IGP_PAGE_SHIFT)); ret_val = e1000e_read_phy_reg_mdic(hw, BM_WUC_ENABLE_REG, &phy_reg); if (ret_val) goto out; /* First clear bit 4 to avoid a power state change */ phy_reg &= ~(BM_WUC_HOST_WU_BIT); ret_val = e1000e_write_phy_reg_mdic(hw, BM_WUC_ENABLE_REG, phy_reg); if (ret_val) goto out; /* Write bit 2 = 1, and clear bit 4 to 769_17 */ ret_val = e1000e_write_phy_reg_mdic(hw, BM_WUC_ENABLE_REG, phy_reg | BM_WUC_ENABLE_BIT); if (ret_val) goto out; /* Select page 800 */ ret_val = e1000e_write_phy_reg_mdic(hw, IGP01E1000_PHY_PAGE_SELECT, (BM_WUC_PAGE << IGP_PAGE_SHIFT)); /* Write the page 800 offset value using opcode 0x11 */ ret_val = e1000e_write_phy_reg_mdic(hw, BM_WUC_ADDRESS_OPCODE, reg); if (ret_val) goto out; if (read) { /* Read the page 800 value using opcode 0x12 */ ret_val = e1000e_read_phy_reg_mdic(hw, BM_WUC_DATA_OPCODE, data); } else { /* Read the page 800 value using opcode 0x12 */ ret_val = e1000e_write_phy_reg_mdic(hw, BM_WUC_DATA_OPCODE, *data); } if (ret_val) goto out; /* * Restore 769_17.2 to its original value * Set page 769 */ e1000e_write_phy_reg_mdic(hw, IGP01E1000_PHY_PAGE_SELECT, (BM_WUC_ENABLE_PAGE << IGP_PAGE_SHIFT)); /* Clear 769_17.2 */ ret_val = e1000e_write_phy_reg_mdic(hw, BM_WUC_ENABLE_REG, phy_reg);out: if (phy_acquired == 1) hw->phy.ops.release_phy(hw); return ret_val;}/** * e1000e_commit_phy - Soft PHY reset * @hw: pointer to the HW structure * * Performs a soft PHY reset on those that apply. This is a function pointer * entry point called by drivers. **/s32 e1000e_commit_phy(struct e1000_hw *hw){ if (hw->phy.ops.commit_phy) return hw->phy.ops.commit_phy(hw); return 0;}/** * e1000_set_d0_lplu_state - Sets low power link up state for D0 * @hw: pointer to the HW structure * @active: boolean used to enable/disable lplu * * Success returns 0, Failure returns 1 * * The low power link up (lplu) state is set to the power management level D0 * and SmartSpeed is disabled when active is true, else clear lplu for D0 * and enable Smartspeed. LPLU and Smartspeed are mutually exclusive. LPLU * is used during Dx states where the power conservation is most important. * During driver activity, SmartSpeed should be enabled so performance is * maintained. This is a function pointer entry point called by drivers. **/static s32 e1000_set_d0_lplu_state(struct e1000_hw *hw, bool active){ if (hw->phy.ops.set_d0_lplu_state) return hw->phy.ops.set_d0_lplu_state(hw, active); return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -