⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 at_hw.c

📁 Atheros公司AR8121/AR8113无线网卡的Linux驱动
💻 C
📖 第 1 页 / 共 2 页
字号:
* data - data to write to the PHY
********************************************************************/
s32
at_write_phy_reg(
        struct at_hw *hw,
        u32 reg_addr,
        u16 phy_data)
{
    int i;
    u32 val;
    
    val =   ((u32)(phy_data & MDIO_DATA_MASK)) << MDIO_DATA_SHIFT |
            (reg_addr&MDIO_REG_ADDR_MASK) << MDIO_REG_ADDR_SHIFT |
            MDIO_SUP_PREAMBLE |
            MDIO_START |
            MDIO_CLK_25_4 << MDIO_CLK_SEL_SHIFT;
    AT_WRITE_REG(hw, REG_MDIO_CTRL, val);
//    DEBUGOUT1("phy write 0x%x <- 0x%x, value = 0x%x", i
//              reg_addr, 
//              phy_data, 
//              val);
    
    wmb();
    
    for (i=0; i<MDIO_WAIT_TIMES; i++) {
        usec_delay(2);
        val = AT_READ_REG(hw, REG_MDIO_CTRL);
        if (!(val&(MDIO_START|MDIO_BUSY))) {
            break;
        }
        wmb();
    }

    if (!(val&(MDIO_START|MDIO_BUSY)))
        return AT_SUCCESS;

    return AT_ERR_PHY;
}

/********************************************************************
* Configures PHY autoneg and flow control advertisement settings
*
* hw - Struct containing variables accessed by shared code
********************************************************************/
s32
at_phy_setup_autoneg_adv(struct at_hw *hw)
{
    s32 ret_val;
    u16 mii_autoneg_adv_reg;
    u16 mii_1000t_ctrl_reg;

    DEBUGFUNC("at_phy_setup_autoneg_adv");

    if (0 != hw->mii_autoneg_adv_reg)
        return AT_SUCCESS;
    
    /* Read the MII Auto-Neg Advertisement Register (Address 4/9). */
    mii_autoneg_adv_reg = MII_AR_DEFAULT_CAP_MASK;
	mii_1000t_ctrl_reg = MII_AT001_CR_1000T_DEFAULT_CAP_MASK;
    
    /* Need to parse autoneg_advertised  and set up
    * the appropriate PHY registers.  First we will parse for
    * autoneg_advertised software override.  Since we can advertise
    * a plethora of combinations, we need to check each bit
    * individually.
    */
    
    /* First we clear all the 10/100 mb speed bits in the Auto-Neg
    * Advertisement Register (Address 4) and the 1000 mb speed bits in
    * the  1000Base-T Control Register (Address 9).
    */
    mii_autoneg_adv_reg &= ~MII_AR_SPEED_MASK;
    mii_1000t_ctrl_reg &= ~MII_AT001_CR_1000T_SPEED_MASK;
    
    /* Need to parse MediaType and setup the 
     * appropriate PHY registers.
     */
    switch (hw->MediaType)
    {
    case MEDIA_TYPE_AUTO_SENSOR:
        mii_autoneg_adv_reg |= (MII_AR_10T_HD_CAPS|
                                MII_AR_10T_FD_CAPS|
                                MII_AR_100TX_HD_CAPS|
                                MII_AR_100TX_FD_CAPS);
	if (hw->nic_type == athr_l1e) {
	    mii_1000t_ctrl_reg |= MII_AT001_CR_1000T_FD_CAPS; 
	}
        hw->autoneg_advertised = ADVERTISE_10_HALF|
        			ADVERTISE_10_FULL |
        			ADVERTISE_100_HALF|
        			ADVERTISE_100_FULL;
        if (hw->nic_type == athr_l1e) {
            hw->autoneg_advertised |= ADVERTISE_1000_FULL;
        }
        break;
    case MEDIA_TYPE_1000M_FULL:
        if (hw->nic_type != athr_l1e)
            break; 
        mii_1000t_ctrl_reg |= MII_AT001_CR_1000T_FD_CAPS;
        hw->autoneg_advertised |= ADVERTISE_1000_FULL;
        break;
 
    case MEDIA_TYPE_100M_FULL:
        mii_autoneg_adv_reg |= MII_AR_100TX_FD_CAPS;
        hw->autoneg_advertised = ADVERTISE_100_FULL;
        break;
        
    case MEDIA_TYPE_100M_HALF:
        mii_autoneg_adv_reg |= MII_AR_100TX_HD_CAPS;
        hw->autoneg_advertised = ADVERTISE_100_HALF;
        break;
        
    case MEDIA_TYPE_10M_FULL:
        mii_autoneg_adv_reg |= MII_AR_10T_FD_CAPS;
        hw->autoneg_advertised = ADVERTISE_10_FULL;
        break;
        
    default:
        mii_autoneg_adv_reg |= MII_AR_10T_HD_CAPS;
        hw->autoneg_advertised = ADVERTISE_10_HALF;
        break;
    }
     							
    /* flow control fixed to enable all */
    mii_autoneg_adv_reg |= (MII_AR_ASM_DIR | MII_AR_PAUSE);

    hw->mii_autoneg_adv_reg = mii_autoneg_adv_reg;
	hw->mii_1000t_ctrl_reg = mii_1000t_ctrl_reg;
    
    ret_val = at_write_phy_reg(hw, 
                               MII_ADVERTISE, 
                               mii_autoneg_adv_reg);
    if(ret_val)
        return ret_val;

   if (hw->nic_type == athr_l1e ||
       hw->nic_type == athr_l2e_revA ) {                                    
           ret_val = at_write_phy_reg(hw, 
	        	MII_AT001_CR, 
			mii_1000t_ctrl_reg);
    }
	
    if (ret_val)
	return ret_val;
    
    return AT_SUCCESS;
}

s32
at_phy_init(struct at_hw* hw)
{
    s32 ret_val;
    u16 phy_val;
	
    if (hw->phy_configured) {
        if (hw->re_autoneg) {
            hw->re_autoneg = FALSE;
            return at_restart_autoneg(hw);
        }
        return 0;
    }
		
    // RESET GPHY Core
    AT_WRITE_REGW(hw, REG_GPHY_CTRL, GPHY_CTRL_DEFAULT);
    msec_delay(2);
    AT_WRITE_REGW(hw, REG_GPHY_CTRL, GPHY_CTRL_DEFAULT|GPHY_CTRL_EXT_RESET);
    msec_delay(2);
    
	/* patches */
	/* p1. eable hibernation mode */
	ret_val = at_write_phy_reg(hw, MII_DBG_ADDR, 0xB);
    if (ret_val) 	return ret_val;
	ret_val = at_write_phy_reg(hw, MII_DBG_DATA, 0xBC00);
    if (ret_val) 	return ret_val;    	
	/* p2. set Class A/B for all modes */
	ret_val = at_write_phy_reg(hw, MII_DBG_ADDR, 0);
    if (ret_val) 	return ret_val;
        /* remove Class AB, just use ClassA(0x02ef) */
	//phy_val = hw->emi_ca ? 0x02ef : 0x02df;
	phy_val = 0x02ef; /* ClassA */
	ret_val = at_write_phy_reg(hw, MII_DBG_DATA, phy_val);
    if (ret_val) 	return ret_val;    		
	/* p3. 10B ??? */
	ret_val = at_write_phy_reg(hw, MII_DBG_ADDR, 0x12);
    if (ret_val) 	return ret_val;
	ret_val = at_write_phy_reg(hw, MII_DBG_DATA, 0x4C04);
    if (ret_val) 	return ret_val; 	
	/* p4. 1000T power */
	ret_val = at_write_phy_reg(hw, MII_DBG_ADDR, 0x4);
    if (ret_val) 	return ret_val;
	ret_val = at_write_phy_reg(hw, MII_DBG_DATA, 0x8BBB);
    if (ret_val) 	return ret_val;
    	
	ret_val = at_write_phy_reg(hw, MII_DBG_ADDR, 0x5);
    if (ret_val) 	return ret_val;
	ret_val = at_write_phy_reg(hw, MII_DBG_DATA, 0x2C46);
    if (ret_val) 	return ret_val;
	    		
	msec_delay(1);
	
    /*Enable PHY LinkChange Interrupt */
    ret_val = at_write_phy_reg(hw, 18, 0xC00);
    if (ret_val)
        return ret_val;

    /* setup AutoNeg parameters */
    ret_val = at_phy_setup_autoneg_adv(hw);
    if(ret_val) {
        DEBUGOUT("Error Setting up Auto-Negotiation");
        return ret_val;
    }
    
    /* SW.Reset & En-Auto-Neg to restart Auto-Neg*/
    DEBUGOUT("Restarting Auto-Neg");
    ret_val = at_phy_commit(hw);
    if (ret_val) {
        DEBUGOUT("Error Resetting the phy");
        return ret_val;
    }
 
    hw->phy_configured = TRUE;
    
    return ret_val;
}

/*******************************************************************
* Resets the PHY and make all config validate
*
* hw - Struct containing variables accessed by shared code
*
* Sets bit 15 and 12 of the MII Control regiser (for F001 bug)
*******************************************************************/
s32
at_phy_commit(struct at_hw *hw)
{
    s32 ret_val;
    u16 phy_data;
    
    DEBUGFUNC("at_phy_commit");

    phy_data = MII_CR_RESET | MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG;
    
    ret_val = at_write_phy_reg(hw, MII_BMCR, phy_data);
    if (ret_val) { // bug fixed
        u32 val;
        int i;
        /**************************************
        * pcie serdes link may be down !
        **************************************/
        DEBUGOUT("Auto-Neg make pcie phy link down !");

        for (i=0; i < 25; i++) {
            msec_delay(1);
            val = AT_READ_REG(hw, REG_MDIO_CTRL);
            if (!(val&(MDIO_START|MDIO_BUSY))) {
                break;
            }
        }
     
        if (0 != (val&(MDIO_START|MDIO_BUSY))) {
            AT_ERR("pcie linkdown at least for 25ms !\n");
            return ret_val;

        DEBUGOUT1("pcie linkup after %dms", i);
        }
    }
    return AT_SUCCESS;
}

void
set_mac_addr(struct at_hw* hw)
{
    u32 value;
    // 00-0B-6A-F6-00-DC
    // 0:  6AF600DC   1: 000B
    //  low dword
    value = (((u32)hw->mac_addr[2]) << 24) |
            (((u32)hw->mac_addr[3]) << 16) |
            (((u32)hw->mac_addr[4]) << 8 ) |
            (((u32)hw->mac_addr[5])      ) ;
    AT_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 0, value);
    // hight dword
    value = (((u32)hw->mac_addr[0]) << 8 ) |
            (((u32)hw->mac_addr[1])      ) ;
    AT_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 1, value);
}


/*************************************** function about EEPROM **********************************/
/*
 * check_eeprom_exist
 * return 0 if eeprom exist
 */
int 
check_eeprom_exist(struct at_hw* hw)
{
    u32 value;
    
    value = AT_READ_REG(hw, REG_SPI_FLASH_CTRL);
    if (value & SPI_FLASH_CTRL_EN_VPD) 
    {
        value &= ~SPI_FLASH_CTRL_EN_VPD;
        AT_WRITE_REG(hw, REG_SPI_FLASH_CTRL, value);
    }
    value = AT_READ_REGW(hw, REG_PCIE_CAP_LIST);
    return ((value & 0xFF00) == 0x6C00) ? 0 : 1;
}

/*
 * get_permanent_address
 * return 0 if get valid mac address, 
 */
int 
get_permanent_address(struct at_hw *hw)
{
#define AT_TWSI_EEPROM_TIMEOUT 10
    u32 Addr[2];
    u32 i;
    u32 twsi_ctrl_data;
    u8  EthAddr[NODE_ADDRESS_SIZE];
    
    if (is_valid_ether_addr(hw->perm_mac_addr))
        return 0;
    // init
    Addr[0] = Addr[1] = 0;    
    
    if (!check_eeprom_exist(hw)) { // eeprom exist
        i = 0;
        twsi_ctrl_data = AT_READ_REG(hw, REG_TWSI_CTRL);
        twsi_ctrl_data |= TWSI_CTRL_SW_LDSTART; 
        AT_WRITE_REG(hw, REG_TWSI_CTRL, twsi_ctrl_data);
        for (i = 0; i < AT_TWSI_EEPROM_TIMEOUT; i++) {
            msec_delay(5);
            twsi_ctrl_data = AT_READ_REG(hw, REG_TWSI_CTRL);
            if ((twsi_ctrl_data & TWSI_CTRL_SW_LDSTART) == 0) {
                break;
            }
        }
        if (i >= AT_TWSI_EEPROM_TIMEOUT)
            return -1;
    } 
    
    Addr[0] = AT_READ_REG(hw,REG_MAC_STA_ADDR);
    Addr[1] = AT_READ_REG(hw,REG_MAC_STA_ADDR+4);
    *(u32*) &EthAddr[2] = LONGSWAP(Addr[0]);
    *(u16*) &EthAddr[0] = SHORTSWAP(*(u16*)&Addr[1]);
    
    if (is_valid_ether_addr(EthAddr)) {
        memcpy(hw->perm_mac_addr, EthAddr, NODE_ADDRESS_SIZE);
        return 0;
    }
    
    return 1;
#undef AT_TWSI_EEPROM_TIMEOUT
}

boolean_t 
write_eeprom(struct at_hw* hw, u32 offset, u32 value)
{
    return TRUE;
}

boolean_t
read_eeprom(struct at_hw* hw, u32 Offset, u32* pValue)
{
    int i;
    u32    Control;
    
    if (Offset&3)   return FALSE; //address do not align

    AT_WRITE_REG(hw, REG_VPD_DATA, 0);
    Control = (Offset&VPD_CAP_VPD_ADDR_MASK)<<VPD_CAP_VPD_ADDR_SHIFT;
    AT_WRITE_REG(hw, REG_VPD_CAP, Control);
    
    for (i=0; i < 10; i++)
    {
        msec_delay(2);
        Control = AT_READ_REG(hw, REG_VPD_CAP);
        if (Control & VPD_CAP_VPD_FLAG)
            break;
    }
    if (Control & VPD_CAP_VPD_FLAG)
    {
        *pValue = AT_READ_REG(hw, REG_VPD_DATA);
        return TRUE;
    }
    return FALSE; // timeout
}

void at_force_ps(struct at_hw* hw)
{
	AT_WRITE_REGW(hw, 
		REG_GPHY_CTRL, 
		GPHY_CTRL_PW_WOL_DIS|GPHY_CTRL_EXT_RESET);
}




⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -