/*------------------------------------------------------------------------------------------*\ * Copyright (C) 2011,2012 AVM GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA \*------------------------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "avmnet_debug.h" #include "avmnet_module.h" #include "avmnet_config.h" #include #include "ag934x.h" #include "atheros_mac.h" /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ #if defined(CONFIG_MACH_QCA955x) || defined(CONFIG_SOC_QCA955X) void athrs_sgmii_res_cal(void) { unsigned int read_data, read_data_otp, otp_value, otp_per_val, rbias_per; unsigned int rbias_pos_or_neg, res_cal_val; unsigned int sgmii_pos, sgmii_res_cal_value; unsigned int reversed_sgmii_value, use_value; ath_reg_wr(OTP_INTF2_ADDRESS, 0x7d); ath_reg_wr(OTP_LDO_CONTROL_ADDRESS, 0x0); while (ath_reg_rd(OTP_LDO_STATUS_ADDRESS) & OTP_LDO_STATUS_POWER_ON_MASK); read_data = ath_reg_rd(OTP_MEM_0_ADDRESS + 4); while (!(ath_reg_rd(OTP_STATUS0_ADDRESS) & OTP_STATUS0_EFUSE_READ_DATA_VALID_MASK)); read_data_otp = ath_reg_rd(OTP_STATUS1_ADDRESS); if (read_data_otp & 0x1fff) { read_data = read_data_otp; } if (read_data & 0x00001000) { otp_value = (read_data & 0xfc0) >> 6; } else { otp_value = read_data & 0x3f; } if (otp_value > 31) { otp_per_val = 63 - otp_value; rbias_pos_or_neg = 1; } else { otp_per_val = otp_value; rbias_pos_or_neg = 0; } rbias_per = otp_per_val * 15; if (rbias_pos_or_neg == 1) { res_cal_val = (rbias_per + 34) / 21; sgmii_pos = 1; } else { if (rbias_per > 34) { res_cal_val = (rbias_per - 34) / 21; sgmii_pos = 0; } else { res_cal_val = (34 - rbias_per) / 21; sgmii_pos = 1; } } if (sgmii_pos == 1) { sgmii_res_cal_value = 8 + res_cal_val; } else { sgmii_res_cal_value = 8 - res_cal_val; } reversed_sgmii_value = 0; use_value = 0x8; reversed_sgmii_value = reversed_sgmii_value | ((sgmii_res_cal_value & use_value) >> 3); use_value = 0x4; reversed_sgmii_value = reversed_sgmii_value | ((sgmii_res_cal_value & use_value) >> 1); use_value = 0x2; reversed_sgmii_value = reversed_sgmii_value | ((sgmii_res_cal_value & use_value) << 1); use_value = 0x1; reversed_sgmii_value = reversed_sgmii_value | ((sgmii_res_cal_value & use_value) << 3); reversed_sgmii_value &= 0xf; // To Check the locking of the SGMII PLL read_data = (ath_reg_rd(SGMII_SERDES_ADDRESS) & ~SGMII_SERDES_RES_CALIBRATION_MASK) | SGMII_SERDES_RES_CALIBRATION_SET(reversed_sgmii_value); ath_reg_wr(SGMII_SERDES_ADDRESS, read_data); ath_reg_wr(ETH_SGMII_SERDES_ADDRESS, ETH_SGMII_SERDES_EN_LOCK_DETECT_MASK | ETH_SGMII_SERDES_PLL_REFCLK_SEL_MASK | ETH_SGMII_SERDES_EN_PLL_MASK); AVMNET_DEBUG("{%s} SGMII_SERDES_CTRL 0x%x\n", __func__, ath_reg_rd(SGMII_SERDES_ADDRESS)); { unsigned int sgmii_serdes_ctl = ath_reg_rd(SGMII_SERDES_ADDRESS); sgmii_serdes_ctl &= ~( SGMII_SERDES_CDR_BW_MASK | SGMII_SERDES_TX_DR_CTRL_MASK | SGMII_SERDES_PLL_BW_MASK | SGMII_SERDES_EN_SIGNAL_DETECT_MASK | SGMII_SERDES_FIBER_SDO_MASK | SGMII_SERDES_VCO_REG_MASK ); sgmii_serdes_ctl |= ( SGMII_SERDES_CDR_BW_SET(3) | SGMII_SERDES_TX_DR_CTRL_SET(4) | /*--- 900mV Amplitude ---*/ SGMII_SERDES_PLL_BW_SET(1) | SGMII_SERDES_EN_SIGNAL_DETECT_SET(1) | SGMII_SERDES_FIBER_SDO_SET(1) | SGMII_SERDES_VCO_REG_SET(3) ); ath_reg_wr(SGMII_SERDES_ADDRESS, sgmii_serdes_ctl); } AVMNET_DEBUG("{%s} SGMII_SERDES_CTRL 0x%x\n", __func__, ath_reg_rd(SGMII_SERDES_ADDRESS)); ath_reg_rmw_clear(ATH_RESET, ATH_RESET_GE1_PHY); udelay(500); ath_reg_rmw_clear(ATH_RESET, ATH_RESET_GE0_PHY); while (!(ath_reg_rd(SGMII_SERDES_ADDRESS) & SGMII_SERDES_LOCK_DETECT_STATUS_MASK)); } #endif /*--- #if defined(CONFIG_MACH_QCA955x) || defined(CONFIG_SOC_QCA955X) ---*/ /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ #if 0 /*--- not used ---*/ static int athr_gmac_do_ioctl(struct net_device *dev __attribute__((unused)),struct ifreq *ifr __attribute__((unused)), int cmd) { int ret = -EPERM; switch (cmd){ #if 0 case ATHR_GMAC_CTRL_IOC: ret = athr_gmac_ctrl(dev,ifr,cmd); break; case ATHR_PHY_CTRL_IOC: ret = athr_phy_ctrl(dev,ifr,cmd); break; #ifdef CONFIG_ATHRS_QOS case ATHR_GMAC_QOS_CTRL_IOC: ret = athr_config_qos(ifr->ifr_data,cmd); break; #endif #ifdef CONFIG_ATHR_VLAN_IGMP case ATHR_VLAN_IGMP_IOC: ret = athr_vlan_ctrls(dev,ifr,cmd); break; #endif #ifdef CONFIG_ATHRS_HW_ACL case ATHR_HW_ACL_IOC: ret = athr_hw_acl_config(ifr->ifr_data, cmd, dev); break; #endif # if defined(CONFIG_AVM_CPMAC) && CONFIG_AVM_CPMAC case AVM_CPMAC_IOCTL_GENERIC: /* If the ioctl is not defined, fall through to the error message */ if(!IS_ERR(&cpmac_virian_ioctl) && &cpmac_virian_ioctl) { ret = cpmac_virian_ioctl(dev, ifr, cmd); } # endif /*--- #if defined(CONFIG_AVM_CPMAC) && CONFIG_AVM_CPMAC ---*/ #endif default: break; } return ret; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void athr_gmac_vet_tx_len_per_pkt(unsigned int *len) { unsigned int l; /* make it into words */ l = *len & ~3; /* * Not too small */ if (l < ATHR_GMAC_TX_MIN_DS_LEN) { l = ATHR_GMAC_TX_MIN_DS_LEN; } else { /* Avoid len where we know we will deadlock, that * is the range between fif_len/2 and the MTU size */ if (l > ATHR_GMAC_TX_FIFO_LEN/2) { if (l < ATHR_GMAC_TX_MTU_LEN) l = ATHR_GMAC_TX_MTU_LEN; else if (l > ATHR_GMAC_TX_MAX_DS_LEN) l = ATHR_GMAC_TX_MAX_DS_LEN; *len = l; } } } #endif int fifo_3 = 0x1f00140; /*--- "fifo cfg 3 settings" ---*/ /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int athmac_setup(avmnet_module_t *this) { int i, result; AVMNET_INFO("[%s] Setup on module %s called.\n", __func__, this->name); if (0 == fifo_3) fifo_3 = 0x000001ff | ((ATHR_GMAC_TX_FIFO_LEN - CONFIG_ATHR_GMAC_LEN_PER_TX_DS)/4)<<16; /*--- reset Hardware ---*/ #if defined(CONFIG_MACH_QCA955x) || defined(CONFIG_SOC_QCA955X) ath_reg_rmw_set(ATH_RESET, ATH_RESET_GE0_PHY | ATH_RESET_GE1_PHY); #if 0 if ( ! is_qca9556()) { /*--- beim qca9556 bleibt das analoge Interface im Reset ---*/ mdelay(50); ath_reg_rmw_clear(ATH_RESET, ATH_RESET_GE0_PHY | ATH_RESET_GE1_PHY); mdelay(200); } #endif #else /*--- #if defined(CONFIG_MACH_QCA955x) ---*/ ath_reg_rmw_set(ATH_RESET, ATH_RESET_GE0_PHY | ATH_RESET_GE1_PHY); mdelay(50); ath_reg_rmw_clear(ATH_RESET, ATH_RESET_GE0_PHY | ATH_RESET_GE1_PHY); mdelay(200); #endif /*--- #else ---*/ /*--- #if defined(CONFIG_MACH_QCA955x) ---*/ ath_reg_rmw_set(ATH_RESET, ATH_RESET_GE0_MAC | ATH_RESET_GE1_MAC | ATH_RESET_GE0_MDIO | ATH_RESET_GE1_MDIO); mdelay(100); ath_reg_rmw_clear(ATH_RESET, ATH_RESET_GE0_MAC | ATH_RESET_GE1_MAC | ATH_RESET_GE0_MDIO | ATH_RESET_GE1_MDIO); #if defined(CONFIG_MACH_QCA955x) || defined(CONFIG_SOC_QCA955X) ath_reg_wr(ETH_SGMII_SERDES_ADDRESS, ETH_SGMII_SERDES_PLL_REFCLK_SEL_SET(1) | ETH_SGMII_SERDES_EN_LOCK_DETECT_SET(1)); ath_reg_wr(SWITCH_CLOCK_SPARE_ADDRESS, 0x520); athrs_sgmii_res_cal(); #endif /*--- #if defined(CONFIG_MACH_QCA955x) ---*/ for(i = 0; i < this->num_children; ++i){ result = this->children[i]->setup(this->children[i]); if(result != 0){ // handle error } } avmnet_cfg_register_module(this); return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int athmac_init(avmnet_module_t *this) { int i, result; AVMNET_DEBUG("{%s} Init on module %s called.\n", __func__, this->name); for(i = 0; i < this->num_children; ++i){ result = this->children[i]->init(this->children[i]); if(result < 0){ // handle error } } return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int athmac_exit(avmnet_module_t *this) { int i, result; AVMNET_DEBUG("{%s} Init on module %s called.\n", __func__, this->name); for(i = 0; i < this->num_children; ++i){ result = this->children[i]->exit(this->children[i]); if(result != 0){ // handle error } } /* * clean up our mess */ return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int athmac_set_status(avmnet_module_t *this, avmnet_device_t *device_id, avmnet_linkstatus_t status) { if(this->parent != NULL){ return this->parent->set_status(this->parent, device_id, status); } return 0; }