/*------------------------------------------------------------------------------------------*\ * Copyright (C) 2006,2007,2008,2009 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 #if defined(CONFIG_MIPS_OHIO) #include #include #endif/*--- #if defined(CONFIG_MIPS_AR7) ---*/ #if defined(CONFIG_MIPS_AR7) #include #endif/*--- #if defined(CONFIG_MIPS_AR7) ---*/ #if defined(CONFIG_MIPS_UR8) #include #endif /*--- #if defined(CONFIG_MIPS_UR8) ---*/ #if defined(CONFIG_AVM_POWERMETER) #include #endif /*--- #if defined(CONFIG_AVM_POWERMETER) ---*/ #if !defined(CONFIG_NETCHIP_ADM69961) #define CONFIG_NETCHIP_ADM69961 #endif /*--- #if !defined(CONFIG_NETCHIP_ADM69961) ---*/ #if !defined(CONFIG_NETCHIP_AR8216) #define CONFIG_NETCHIP_AR8216 #endif /*--- #if !defined(CONFIG_NETCHIP_AR8216) ---*/ #include #include #include #include #include "cpmac_if.h" #include "cpmac_const.h" #include "cpmac_debug.h" #include "cpmac_reg.h" #include "cpphy_const.h" #include "cpphy_types.h" #include "cpphy_mdio.h" #include "cpphy_mgmt.h" #include "adm6996.h" #include "tantos.h" #include "cpphy_adm6996.h" #include "cpphy_ar8216.h" /* FIXME: Temporarily define this here, until the include file is available */ #define TNETD73XX_EPHY_TRIM_VALUE 0xAC #if !defined(CONFIG_MIPS_UR8) extern unsigned int cpmac_devices_installed; #endif /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void cpphy_mdio_mdix_delay(cpphy_mdio_t *mdio) { if(mdio->nway_mode & NWAY_AUTOMDIX) { mdio->timeout = CPPHY_MDIX_TO + CPPHY_AUTOMDIX_DELAY_MIN / 10; } } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void cpphy_mdio_wait_for_access_complete(cpphy_mdio_t *mdio) { while(MDIO_USER_ACCESS(mdio->inst) & MDIO_USER_ACCESS_GO) { if(unlikely(in_atomic())) { /* TODO This is an ugly hack that will harm system stability! At the moment it *\ \* is necessary for the W721V, but should be fixed ASAP */ /* busy waiting */; } else { schedule(); } } } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void cpphy_mdio_user_access(cpphy_mdio_t *mdio, unsigned int method, unsigned int regadr, unsigned int phyadr, unsigned int data) { unsigned int control; control = MDIO_USER_ACCESS_GO | (method) | MDIO_USER_ACCESS_REGADDR(regadr & 0x1f) | MDIO_USER_ACCESS_PHYADDR(phyadr & 0x1f) | MDIO_USER_ACCESS_DATA(data & 0xffff); MDIO_USER_ACCESS(mdio->inst) = control; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ unsigned int cpphy_mdio_user_access_read(cpphy_mdio_t *mdio, unsigned int regadr, unsigned int phyadr) { unsigned int value; down_interruptible(&mdio->semaphore); cpphy_mdio_wait_for_access_complete(mdio); /* Wait until UserAccess ready */ cpphy_mdio_user_access(mdio, 0, regadr, phyadr, 0); /* no Write Bit */ cpphy_mdio_wait_for_access_complete(mdio); /* Wait for Read to complete */ value = MDIO_USER_ACCESS(mdio->inst) & MDIO_USER_ACCESS_DATA(0xffff); up(&mdio->semaphore); return value; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void cpphy_mdio_user_access_write(cpphy_mdio_t *mdio, unsigned int regadr, unsigned int phyadr, unsigned int data) { down_interruptible(&mdio->semaphore); cpphy_mdio_wait_for_access_complete(mdio); /* Wait until UserAccess ready */ cpphy_mdio_user_access(mdio, MDIO_USER_ACCESS_WRITE, regadr, phyadr, data); up(&mdio->semaphore); } /*------------------------------------------------------------------------------------------*\ * Function Name : WriteEphyTestRegs * * Purpose : Program EPHY registers * * Parameters : PHY number, register ID, register value * \*------------------------------------------------------------------------------------------*/ #if defined(CONFIG_MIPS_AR7) || defined(CONFIG_MIPS_OHIO) static void cpphy_mdio_WriteEphyTestRegs(cpphy_mdio_t *mdio, unsigned int MiiPhyNum, unsigned int TestCfgRegNum, unsigned int CfgValue) { /* This sequence will change the PHY to test mode */ cpphy_mdio_user_access_write(mdio, 20, MiiPhyNum, 0x0000); cpphy_mdio_user_access_write(mdio, 20, MiiPhyNum, 0x0400); cpphy_mdio_user_access_write(mdio, 20, MiiPhyNum, 0x0000); cpphy_mdio_user_access_write(mdio, 20, MiiPhyNum, 0x0400); /* Write register value */ cpphy_mdio_user_access_write(mdio, 23, MiiPhyNum, 0x8100 | (CfgValue & 0xFF)); cpphy_mdio_user_access_write(mdio, 20, MiiPhyNum, 0x4400 | (TestCfgRegNum & 0xFF)); /* Back to normal mode */ cpphy_mdio_user_access_write(mdio, 20, MiiPhyNum, 0x0000); } #endif /*--- #if defined(CONFIG_MIPS_AR7) || defined(CONFIG_MIPS_OHIO) ---*/ /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void cpphy_mdio_mdix_update(unsigned int On) { # if defined(CONFIG_MIPS_UR8) On = On; /* Not used yet */ # warning GPIO_BIT_MII_MDIX support??? # else /*--- #if defined(CONFIG_MIPS_UR8) ---*/ avm_gpio_out_bit(GPIO_BIT_MII_MDIX, On ? 1 : 0); # endif /*--- #else ---*/ /*--- #if defined(CONFIG_MIPS_UR8) ---*/ } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void cpphy_mdio_timeout(cpphy_mdio_t *mdio) { if(mdio->nway_mode & NWAY_AUTOMDIX) { /* AutoMdix not supported */ /* Indicate to Ticks() that MDI/MDIX mode switch is needed */ mdio->mdix ^= CPPHY_MDIO_FLG_MDIX_ON; /* toggle MDI / MDIX */ DEB_INFO("cpphy_mdio_timeout, xchange mdix: %u\n", mdio->mdix); cpphy_mdio_mdix_update(mdio->mdix); /* Reset state machine to FOUND */ mdio->state = CPPHY_MDIO_ST_FOUND; } } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void cpphy_mdio_update(cpphy_mdio_t *mdio, struct cpmac_event_struct *event_struct) { if(mdio->cpmac_switch) { memcpy((unsigned char *) event_struct, (unsigned char *) &mdio->event_data, sizeof (struct cpmac_event_struct)); event_struct->ports = 5; /* patch all switch ports */ } else { event_struct->port[mdio->inst] = mdio->event_data.port[mdio->inst]; } } /*------------------------------------------------------------------------------------------*\ unsigned int rx_errors; unsigned int tx_errors; unsigned int collisions; \*------------------------------------------------------------------------------------------*/ void cpphy_mdio_update_hw_status(cpphy_mdio_t *mdio, struct net_device_stats *stats) { switch(mdio->cpmac_switch) { case AVM_CPMAC_SWITCH_ADM6996: case AVM_CPMAC_SWITCH_ADM6996L: stats->collisions += ADM_GET_SERIAL_REG(mdio, REG_ADM_LC_COLLISIONCNT0) + ADM_GET_SERIAL_REG(mdio, REG_ADM_LC_COLLISIONCNT1) + ADM_GET_SERIAL_REG(mdio, REG_ADM_LC_COLLISIONCNT2) + ADM_GET_SERIAL_REG(mdio, REG_ADM_LC_COLLISIONCNT3) + ADM_GET_SERIAL_REG(mdio, REG_ADM_LC_COLLISIONCNT4) + ADM_GET_SERIAL_REG(mdio, REG_ADM_LC_COLLISIONCNT5); stats->rx_errors += ADM_GET_SERIAL_REG(mdio, REG_ADM_LC_ERRCNT0) + ADM_GET_SERIAL_REG(mdio, REG_ADM_LC_ERRCNT1) + ADM_GET_SERIAL_REG(mdio, REG_ADM_LC_ERRCNT2) + ADM_GET_SERIAL_REG(mdio, REG_ADM_LC_ERRCNT3) + ADM_GET_SERIAL_REG(mdio, REG_ADM_LC_ERRCNT4) + ADM_GET_SERIAL_REG(mdio, REG_ADM_LC_ERRCNT5); /* unfortunately switch has no distinction for rx/tx errors */ stats->tx_errors = stats->rx_errors; break; case AVM_CPMAC_SWITCH_TANTOS: case AVM_CPMAC_SWITCH_AR8216: case AVM_CPMAC_SWITCH_ANY: case AVM_CPMAC_SWITCH_NONE: break; } } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void cpphy_mdio_reset_phy(cpphy_mdio_t *mdio) { DEB_INFO("cpphy_mdio_reset_phy (%u)\n", mdio->phy_num); cpphy_mdio_user_access_write(mdio, PHY_CONTROL_REG, mdio->phy_num, PHY_RESET); /* Read control register until Phy Reset is complete */ while(cpphy_mdio_user_access_read(mdio, PHY_CONTROL_REG, mdio->phy_num) & PHY_RESET); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void cpphy_mdio_check_pdown_state(cpphy_mdio_t *mdio) { mdio->adm_power.timeout = 1 * HZ; if(mdio->power_down && !(cpphy_mdio_user_access_read(mdio, PHY_GENERIC_STATUS_REG, mdio->phy_num) & PHY_STATUS_MD)) { cpphy_mdio_user_access_write(mdio, PHY_CONTROL_REG, mdio->phy_num, PHY_PDOWN); mdio->state = CPPHY_MDIO_ST_PDOWN; } else if(mdio->timeout) { mdio->timeout--; } else { DEB_INFO("cpphy_mdio_check_pdown_state, tmo\n"); cpphy_mdio_timeout(mdio); } } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void cpphy_mdio_finding_state(cpphy_mdio_t *mdio) { if(mdio->timeout) { mdio->timeout--; } else if(MDIO_ALIVE & (1 << mdio->phy_num)) { unsigned int power_down_capable; /* Phy Found! */ mdio->state = CPPHY_MDIO_ST_FOUND; /* powerdown for external phy */ power_down_capable = (cpphy_mdio_user_access_read(mdio, PHY_Identifier_1, mdio->phy_num) == 0x2e) && ((cpphy_mdio_user_access_read(mdio, PHY_Identifier_2, mdio->phy_num) & 0xfff0) == 0xcc60); mdio->power_down = mdio->power_down && power_down_capable; DEB_INFO("cpphy_mdio_finding_state, phy_num: %u %u\n", mdio->phy_num, mdio->power_down); } else { DEB_INFO("cpphy_mdio_finding_state, timed out looking for a Phy (MDIO_ALIVE = %x)\n", MDIO_ALIVE); } } /*------------------------------------------------------------------------------------------*\ This algo is almost according to spec of TMS320C6000 DSP \*------------------------------------------------------------------------------------------*/ static void cpphy_mdio_found_state(cpphy_mdio_t *mdio) { /* Reset the Phy and proceed with auto-negotiation */ cpphy_mdio_reset_phy(mdio); # if defined(CONFIG_MIPS_AR7) || defined(CONFIG_MIPS_OHIO) /* Fix phy according to TI */ if(mdio->cpmac_switch == AVM_CPMAC_SWITCH_NONE) { cpphy_mdio_WriteEphyTestRegs(mdio, 31, 0x16, TNETD73XX_EPHY_TRIM_VALUE); } # endif /*--- #if defined(CONFIG_MIPS_AR7) || defined(CONFIG_MIPS_OHIO) ---*/ /* Now setup the MDIOUserPhySel register */ MDIO_USER_PHY_SELECT(mdio->inst) = mdio->phy_num; /* only using NWAY compliant Phy */ DEB_INFO("cpphy_mdio_found_state, NWAY\n"); cpphy_mdio_user_access_write(mdio, NWAY_ADVERTIZE_REG, mdio->phy_num, (mdio->nway_mode & ~NWAY_AUTOMDIX)); cpphy_mdio_user_access_write(mdio, PHY_CONTROL_REG, mdio->phy_num, AUTO_NEGOTIATE_EN); cpphy_mdio_user_access_write(mdio, PHY_CONTROL_REG, mdio->phy_num, AUTO_NEGOTIATE_EN|RENEGOTIATE); mdio->state = CPPHY_MDIO_ST_NWAY_START; mdio->timeout = CPPHY_NWST_TO; cpphy_mdio_mdix_delay(mdio); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void cpphy_mdio_pdown_state(cpphy_mdio_t *mdio) { if(cpphy_mdio_user_access_read(mdio, PHY_GENERIC_STATUS_REG, mdio->phy_num) & PHY_STATUS_MD) { /* Reset state machine to FOUND */ mdio->state = CPPHY_MDIO_ST_FOUND; } } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void cpphy_mdio_nway_start_state(cpphy_mdio_t *mdio) { unsigned int PhyMode; /* Wait for Negotiation to start */ PhyMode = cpphy_mdio_user_access_read(mdio, PHY_CONTROL_REG, mdio->phy_num); /* auto negotiation has started */ if(!(PhyMode & RENEGOTIATE)) { /*Flush pending latch bits*/ cpphy_mdio_user_access_read(mdio, PHY_STATUS_REG, mdio->phy_num); mdio->timeout = CPPHY_NWDN_TO; mdio->state = CPPHY_MDIO_ST_NWAY_WAIT; cpphy_mdio_mdix_delay(mdio); /* If AutoMdix set new delay */ } else if(mdio->timeout) { mdio->timeout--; } else { cpphy_mdio_timeout(mdio); } } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void cpphy_mdio_nway_wait_state(cpphy_mdio_t *mdio) { unsigned int PhyStatus, NWAYREadvertise, NegMode, i, j; switch(mdio->cpmac_switch) { case AVM_CPMAC_SWITCH_ADM6996: case AVM_CPMAC_SWITCH_ADM6996L: case AVM_CPMAC_SWITCH_TANTOS: mdio->state = CPPHY_MDIO_ST_LINK_WAIT; DEB_WARN("cpphy_mdio_nway_wait_state, should never get here\n"); break; case AVM_CPMAC_SWITCH_AR8216: break; case AVM_CPMAC_SWITCH_NONE: PhyStatus = cpphy_mdio_user_access_read(mdio, PHY_STATUS_REG, mdio->phy_num); if(PhyStatus & NWAY_COMPLETE) { NWAYREadvertise = cpphy_mdio_user_access_read(mdio, NWAY_REMADVERTISE_REG, mdio->phy_num); /* Negotiated mode is what we and the remote have in common (limit to fields below) */ NegMode = (mdio->nway_mode & NWAYREadvertise) & (NWAY_FD100|NWAY_HD100|NWAY_FD10|NWAY_HD10); DEB_INFO("cpphy_mdio_nway_wait_state, Phy %u, Status %#x, NegMode %#x, NWAYRE %#x\n", mdio->phy_num, PhyStatus, NegMode, NWAYREadvertise); if(!NegMode) { /* or 10 ?? who knows, Phy is not MII compliant*/ NegMode = mdio->nway_mode & (NWAY_HD100|NWAY_HD10); DEB_WARN("cpphy_mdio_nway_wait_state, Negotiation w/o agreement, default is HD\n"); } /* select 'highest' mode */ for(j=0x8000, i=0; (i<16) && !(j&NegMode); i++, j>>=1); NegMode=j; if(NegMode == NWAY_FD100) { DEB_INFO("cpphy_mdio_nway_wait_state, FullDuplex 100 Mbps\n"); } else if(NegMode == NWAY_HD100) { mdio->half_duplex = 1; DEB_INFO("cpphy_mdio_nway_wait_state, HalfDuplex 100 Mbps\n"); } else if(NegMode == NWAY_FD10) { mdio->slow_speed = 1; DEB_INFO("cpphy_mdio_nway_wait_state, FullDuplex 10 Mbps\n"); } else if(NegMode == NWAY_HD10) { mdio->slow_speed = 1; mdio->half_duplex = 1; DEB_INFO("cpphy_mdio_nway_wait_state, HalfDuplex 10 Mbps\n"); } else { /* should never get here */ DEB_ERR("cpphy_mdio_nway_wait_state, ill default nway mode\n"); } if(PhyStatus & PHY_LINKED) { mdio->state = CPPHY_MDIO_ST_LINKED; cpphy_mgmt_event_dataupdate(mdio); /* Status changed to LINKED */ } else { mdio->state = CPPHY_MDIO_ST_LINK_WAIT; /* to check: tmo missing? */ } } else { cpphy_mdio_check_pdown_state(mdio); } break; case AVM_CPMAC_SWITCH_ANY: default: break; } } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void cpphy_mdio_link_wait_state(cpphy_mdio_t *mdio) { unsigned int PhyStatus; switch(mdio->cpmac_switch) { case AVM_CPMAC_SWITCH_ADM6996: case AVM_CPMAC_SWITCH_ADM6996L: case AVM_CPMAC_SWITCH_TANTOS: case AVM_CPMAC_SWITCH_AR8216: case AVM_CPMAC_SWITCH_ANY: if(mdio->linked) { DEB_TRC("cpphy_mdio_link_wait_state, linked: %#x\n", mdio->linked); mdio->state = CPPHY_MDIO_ST_LINKED; } break; case AVM_CPMAC_SWITCH_NONE: PhyStatus = cpphy_mdio_user_access_read(mdio, PHY_STATUS_REG, mdio->phy_num); if(PhyStatus & PHY_LINKED) { mdio->state = CPPHY_MDIO_ST_LINKED; cpphy_mgmt_event_dataupdate(mdio); /* Status changed to LINKED */ # if defined(CONFIG_AVM_POWERMETER) PowerManagmentRessourceInfo(powerdevice_ethernet, 100); # endif /*--- #if defined(CONFIG_AVM_POWERMETER) ---*/ } else { cpphy_mdio_check_pdown_state(mdio); } break; default: break; } } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void cpphy_mdio_linked_state(cpphy_mdio_t *mdio) { switch(mdio->cpmac_switch) { case AVM_CPMAC_SWITCH_ADM6996: case AVM_CPMAC_SWITCH_ADM6996L: case AVM_CPMAC_SWITCH_TANTOS: case AVM_CPMAC_SWITCH_ANY: if(!mdio->linked) { mdio->state = CPPHY_MDIO_ST_LINK_WAIT; } break; case AVM_CPMAC_SWITCH_AR8216: break; case AVM_CPMAC_SWITCH_NONE: default: if(!(MDIO_LINK & (1 << mdio->phy_num))) { /* No switch, not linked */ if(mdio->nway_mode & NWAY_AUTO) { mdio->state = CPPHY_MDIO_ST_NWAY_WAIT; mdio->timeout = CPPHY_NWDN_TO; } else { mdio->state = CPPHY_MDIO_ST_LINK_WAIT; mdio->timeout = CPPHY_LINK_TO; } # if defined(CONFIG_AVM_POWERMETER) PowerManagmentRessourceInfo(powerdevice_ethernet, 0); # endif /*--- #if defined(CONFIG_AVM_POWERMETER) ---*/ cpphy_mgmt_event_dataupdate(mdio); /* State changed from LINKED */ cpphy_mdio_mdix_delay(mdio); } break; } } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void cpphy_mdio_default_state(cpphy_mdio_t *mdio) { mdio = mdio; /* Nothing to be done, variable not used */ } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void cpphy_mdio_phy_check_link(cpphy_mdio_t *mdio) { if(!(MDIO_LINK & (1 << mdio->phy_num))) { /* No switch, not linked */ if(mdio->nway_mode & NWAY_AUTO) { mdio->state = CPPHY_MDIO_ST_NWAY_WAIT; mdio->timeout = CPPHY_NWDN_TO; } else { mdio->state = CPPHY_MDIO_ST_LINK_WAIT; mdio->timeout = CPPHY_LINK_TO; } # if defined(CONFIG_AVM_POWERMETER) PowerManagmentRessourceInfo(powerdevice_ethernet, 0); # endif /*--- #if defined(CONFIG_AVM_POWERMETER) ---*/ cpphy_mgmt_event_dataupdate(mdio); /* State changed from LINKED */ cpphy_mdio_mdix_delay(mdio); } } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ unsigned long cpphy_mdio_tick(cpphy_mdio_t *mdio) { unsigned int last_state = mdio->state; # if defined(CPU_PROFILE_LOG) ohio_simple_profiling_text("cpphy_mdio_tick"); # endif /* Update link information for switch products, if necessary */ mdio->adm_power.timeout = CPMAC_TICK_LENGTH; cpphy_mgmt_check_link(mdio); /*--- DEB_TEST("[cpphy_mdio_tick] [%d]: state %u, %s\n", mdio->inst, mdio->state, ---*/ /*--- (mdio->state == CPPHY_MDIO_ST_LINKED) ? "Linked" : "Not Linked"); ---*/ switch(mdio->state) { case CPPHY_MDIO_ST_FINDING: cpphy_mdio_finding_state(mdio); break; case CPPHY_MDIO_ST_FOUND: cpphy_mdio_found_state(mdio); break; case CPPHY_MDIO_ST_PDOWN: cpphy_mdio_pdown_state(mdio); break; case CPPHY_MDIO_ST_NWAY_START: cpphy_mdio_nway_start_state(mdio); break; case CPPHY_MDIO_ST_NWAY_WAIT: cpphy_mdio_nway_wait_state(mdio); break; case CPPHY_MDIO_ST_LINK_WAIT: cpphy_mdio_link_wait_state(mdio); break; case CPPHY_MDIO_ST_LINKED: cpphy_mdio_linked_state(mdio); break; default: cpphy_mdio_default_state(mdio); break; } if(last_state != mdio->state) { # if defined(CONFIG_MIPS_UR8) DEB_INFO("cpphy_mdio_tick[%d]: state %u -> %u, MACCONTROL 0x%08X, %s\n", mdio->inst, last_state, mdio->state, mdio->cpmac_priv->CPGMAC_F->MAC_CONTROL.Reg, (mdio->state == CPPHY_MDIO_ST_LINKED) ? "Linked" : "Not Linked"); # else /*--- #if defined(CONFIG_MIPS_UR8) ---*/ struct net_device *p_dev = mdio->cpmac_priv->owner; /* Phy State Change Detected */ if((mdio->state == CPPHY_MDIO_ST_LINKED) && !mdio->half_duplex) { /* to check: ok to assume always full duplex? */ CPMAC_MACCONTROL(p_dev->base_addr) |= FULLDUPLEX; } DEB_INFO("cpphy_mdio_tick[%d]: state %u -> %u, MACCONTROL 0x%08X, %s\n", mdio->inst, last_state, mdio->state, CPMAC_MACCONTROL(p_dev->base_addr), (mdio->state == CPPHY_MDIO_ST_LINKED) ? "Linked" : "Not Linked"); # endif /*--- #else ---*/ /*--- #if defined(CONFIG_MIPS_UR8) ---*/ cpmac_main_handle_mdio_status_ind(mdio->cpmac_priv, mdio->state == CPPHY_MDIO_ST_LINKED); /* If the state changed, we should check at once for another change */ if(mdio->adm_power.timeout == CPMAC_TICK_LENGTH) { mdio->adm_power.timeout = 0; } } tasklet_schedule(&mdio->cpmac_priv->tasklet); return mdio->adm_power.timeout; } /*----------------------------------------------------------------------------------*\ Start MDIO Negotiation \*----------------------------------------------------------------------------------*/ void cpphy_mdio_init(cpphy_global_t *cpphy_global) { unsigned int port; char *hwrev = prom_getenv("HWRevision"); cpphy_mdio_t *mdio = &cpphy_global->mdio; cpmac_priv_t *cpmac_priv = cpphy_global->cpmac_priv; mdio->linked = 0; mdio->cpmac_priv = cpmac_priv; for(port = 0; port < CPMAC_MAX_LED_HANDLES; port++) { mdio->cpmac_priv->led[port].handle = 0; mdio->cpmac_priv->led[port].on = 0; } # if !defined(CONFIG_MIPS_UR8) /* MII not setup, Setup initial condition */ if(cpmac_devices_installed == 1) { /* reset only during the first cpphy driver registration */ avm_reset_device(MDIO_RESET_BIT, 10); msleep(2); /* wait after device out of reset */ } MDIO_CR = MDIO_CR_ENABLE | MDIO_CR_CLKDIV((avm_get_clock(avm_clock_id_vbus) / CPPHY_CLK_FREQ) - 1); DEB_INFO("cpphy_mdio_init, vbus clk: %u\n", avm_get_clock(avm_clock_id_vbus)); # endif /*--- #if !defined(CONFIG_MIPS_UR8) ---*/ #ifdef undef /* int pacing: chance to reduce (high) number of ints -> better caching + less redundancy */ /* trick: with reduced clock in prescale register even smaller trigger values */ /* than 2 Int/ms possible */ /* currently no better performance */ ohioint_ctrl_irq_pacing(OHIOINT_CPMAC0, 0, avm_get_clock(avm_clock_id_vbus) / (1000000 * 4 / 2), 2); #endif /* design: must contain NWAY_HD100 or NWAY_HD10 */ mdio->nway_mode = NWAY_AUTO|NWAY_HD10|NWAY_HD100|NWAY_FD10|NWAY_FD100; mdio->half_duplex = 0; mdio->slow_speed = 0; if(hwrev && !(strncmp("154 ", hwrev, 3))) { if(cpphy_global->high_phy) { /* to check */ mdio->phy_num = 1; mdio->inst = 0; } else { mdio->phy_num = 31; mdio->inst = 1; } } else { if(cpphy_global->high_phy) { /* to check */ mdio->phy_num = 1; mdio->inst = 1; } else { mdio->phy_num = 31; mdio->inst = 0; } } init_MUTEX(&mdio->semaphore); mdio->global_power = CPPHY_POWER_GLOBAL_OFF; mdio->cpmac_switch = cpphy_global->cpmac_switch; mdio->switch_use_EMV = cpphy_global->switch_use_EMV; mdio->cpmac_irq = 0; mdio->is_vinax = cpphy_global->is_vinax; if(mdio->cpmac_switch) { /*--- genauer switch type hier noch nicht bekannt ---*/ if(ar_init(mdio)) { /* kein ar82xx gefunden */ adm_init(mdio); adm_vlan_fbox_reset(mdio); } else { /* TODO */ /*--- ar_vlan_fbox_reset(mdio); ---*/ } /* wait for link up (switch is in auto mode) */ mdio->state = CPPHY_MDIO_ST_LINK_WAIT; } else { /*--- kein switch ---*/ /* If disabling the power_down for PHYs is needed */ /*--- char *hwrev = prom_getenv("HWRevision"); ---*/ mdio->power_down = 1; /*--- if(hwrev && !(strncmp("138", hwrev, 3))) { ---*/ /*--- mdio->power_down = 0; ---*/ /*--- } ---*/ mdio->state = CPPHY_MDIO_ST_FINDING; mdio->timeout = CPPHY_FIND_TO; if(!cpphy_global->high_phy) { /* only internal (low) phy can be handled with GPIO_BIT_MII_MDIX! */ mdio->nway_mode |= NWAY_AUTOMDIX; } # if defined(CONFIG_MIPS_AR7) || defined(CONFIG_MIPS_OHIO) avm_gpio_ctrl(GPIO_BIT_MII_MDIX, GPIO_PIN, GPIO_OUTPUT_PIN); # endif /*--- #if defined(CONFIG_MIPS_AR7) || defined(CONFIG_MIPS_OHIO) ---*/ /* start with assumption: connected to pc with normal cable */ cpphy_mdio_mdix_update(mdio->mdix); avm_gpio_out_bit(30, 0); /* to check */ # if defined(CONFIG_MIPS_AR7) || defined(CONFIG_MIPS_OHIO) cpphy_mdio_WriteEphyTestRegs(mdio, 31, 0x16, TNETD73XX_EPHY_TRIM_VALUE); # endif /*--- #if defined(CONFIG_MIPS_AR7) || defined(CONFIG_MIPS_OHIO) ---*/ } cpphy_mgmt_init(mdio); cpphy_mgmt_work_start(mdio); cpphy_mgmt_work_add(mdio, CPMAC_WORK_TICK, cpphy_mdio_tick, 0); if(mdio->cpmac_switch == AVM_CPMAC_SWITCH_AR8216) { cpphy_mgmt_work_add(mdio, CPMAC_WORK_UPDATE_MAC_TABLE, ar8216_ar_work_item, CPMAC_ARL_UPDATE_INTERVAL); } }