/*------------------------------------------------------------------------------------------*\ * 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_NETCHIP_ADM69961) #define CONFIG_NETCHIP_ADM69961 #endif #include #if defined(CONFIG_MIPS_OHIO) #include #endif /*--- #if defined(CONFIG_MIPS_OHIO) ---*/ #include #include #include #include #include "cpmac_if.h" #include "cpmac_const.h" #include "cpmac_debug.h" #include "cpphy_const.h" #include "cpphy_types.h" #include "cpphy_main.h" #include "cpmac_main.h" #include "cpphy_adm6996.h" #include "cpgmac_f.h" MODULE_AUTHOR("Maintainer: AVM GmbH"); MODULE_DESCRIPTION("Ethernet driver for FRITZ!Box"); /*----------------------------------------------------------------------------------*\ Zugriff auf cpmac_cpphy_global aus anderen Modulen ueber phy_handle \*----------------------------------------------------------------------------------*/ cpmac_global_t cpmac_global; /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static unsigned int cpphy_entry_check_for_emv(void) { unsigned long long m0, m1, m2, m3, m4, m5, mac; unsigned char *macstring = prom_getenv("maca"); if(macstring == NULL) return 0; sscanf(macstring, "%llx:%llx:%llx:%llx:%llx:%llx", &m0, &m1, &m2, &m3, &m4, &m5); mac = ((m0 & 0xff) << 40) | ((m1 & 0xff) << 32) | ((m2 & 0xff) << 24) | ((m3 & 0xff) << 16) | ((m4 & 0xff) << 8) | ((m5 & 0xff) << 0); return ( 0 || ((mac >= 0x001C4A000000ull) && (mac <= 0x001C4AFFFFFFull)) || ((mac >= 0x001F3F000000ull) && (mac <= 0x001F3F345DC9ull)) || ((mac >= 0x001F3F3AE817ull) && (mac <= 0x001F3F6DE11Cull)) || ((mac >= 0x0024FE000000ull) && (mac <= 0x0024FE1EE1F9ull)) ); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static int __init cpphy_entry_probe(void) { char *hwrev, buff[AVM_CPMAC_MAX_HWREV_LENGTH], *p; unsigned char instance; /* Print driver version */ DEB_ERR("Version: %s\n", AVM_CPMAC_VERSION); /* Initialize administrative array */ for(instance = 0; instance < CPMAC_MAX_PHY; instance++) { cpmac_global.cpphy[instance].is_used = 0; } instance = 0; /*--------------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------------*/ hwrev = prom_getenv("HWRevision"); /*--- DEB_TRC("HWRevision=\"%s\"\n", hwrev); ---*/ if(!hwrev) { return ENODEV; } strncpy(buff, hwrev, 17); buff[AVM_CPMAC_MAX_HWREV_LENGTH - 1] = 0; assert(strlen(buff) < AVM_CPMAC_MAX_HWREV_LENGTH - 2); /* To ease Klocwork checking */ strncat(buff, " ", 1); assert(strlen(buff) < AVM_CPMAC_MAX_HWREV_LENGTH); /* To ease Klocwork checking */ /*----------------------------------------------------------------------------------*\ * aus "94.0.0.1 " --> "94 " erzeugen \*----------------------------------------------------------------------------------*/ p = strchr(buff, '.'); if(p) { *p++ = ' '; *p++ = '\0'; } /* HWREV_LISTs are generated in the init_cpmac script */ /* Low PHY checks */ cpmac_global.cpphy[instance].is_used = (strstr(AVM_CPMAC_NO_PHY0_HWREV_LIST, buff)) ? 0 : 1; if(cpmac_global.cpphy[instance].is_used != 0) { cpmac_global.cpphy[instance].instance = instance; cpmac_global.cpphy[instance].high_phy = 0; # if defined(CONFIG_AVM_CPMAC_SWITCH) cpmac_global.cpphy[instance].cpmac_switch = (strstr(AVM_CPMAC_SWITCH_HWREV_LIST, buff)) ? AVM_CPMAC_SWITCH_ANY : 0; cpmac_global.cpphy[instance].is_vinax = strstr(AVM_CPMAC_VINAX_HWREV_LIST, buff) != 0; # else /*--- #if defined(CONFIG_AVM_CPMAC_SWITCH) ---*/ cpmac_global.cpphy[instance].cpmac_switch = AVM_CPMAC_SWITCH_NONE; # endif /*--- #else ---*/ /*--- #if defined(CONFIG_AVM_CPMAC_SWITCH) ---*/ instance++; } # if defined(CONFIG_MIPS_AR7) || defined(CONFIG_MIPS_UR8) cpmac_global.cpphy[instance].is_used = (strstr(AVM_CPMAC_NO_PHY1_HWREV_LIST, buff)) ? 0 : 1; if(cpmac_global.cpphy[instance].is_used != 0) { cpmac_global.cpphy[instance].instance = instance; cpmac_global.cpphy[instance].high_phy = 1; # if defined(CONFIG_AVM_CPMAC_SWITCH) cpmac_global.cpphy[instance].cpmac_switch = (strstr(AVM_CPMAC_SWITCH_HWREV_LIST, buff)) ? AVM_CPMAC_SWITCH_ANY : 0; cpmac_global.cpphy[instance].is_vinax = strstr(AVM_CPMAC_VINAX_HWREV_LIST, buff) != 0; # else /*--- #if defined(CONFIG_AVM_CPMAC_SWITCH) ---*/ cpmac_global.cpphy[instance].cpmac_switch = AVM_CPMAC_SWITCH_NONE; # endif /*--- #else ---*/ /*--- #if defined(CONFIG_AVM_CPMAC_SWITCH) ---*/ instance++; } # endif /*--- #if defined(CONFIG_MIPS_AR7) || defined(CONFIG_MIPS_UR8) ---*/ /* Check, whether we need EMV handling for the W503V */ if(!(strncmp( "136 ", hwrev, 3))) { for(instance = 0; instance < CPMAC_MAX_PHY; instance++) { if(cpmac_global.cpphy[instance].is_used) { cpmac_global.cpphy[instance].switch_use_EMV = cpphy_entry_check_for_emv() ? 1 : 0; } } } cpmac_global.workqueue = create_singlethread_workqueue("CPMAC"); /* Register all instances */ for(instance = 0; instance < CPMAC_MAX_PHY; instance++) { if(cpmac_global.cpphy[instance].is_used) { DEB_INFO("cpphy_entry_probe, built-in, high: %u, switch: %u vdsl: %u\n", cpmac_global.cpphy[instance].high_phy, cpmac_global.cpphy[instance].cpmac_switch, cpmac_global.cpphy[instance].is_vinax); if(CPMAC_ERR_NOERR != cpphy_main_register(&cpmac_global.cpphy[instance])) { return -ENODEV; } } } cpmac_main_probe(); /* Continuation via callback in cpphy_if_init() */ return 0; } /*----------------------------------------------------------------------------------*\ \*----------------------------------------------------------------------------------*/ static void cpphy_entry_exit(void) { unsigned char instance; DEB_INFO("cpphy_entry_exit, init\n"); for(instance = 0; instance < CPMAC_MAX_PHY; instance++) { cpmac_if_release(cpmac_global.cpphy[instance].cpmac_priv); } flush_workqueue(cpmac_global.workqueue); destroy_workqueue(cpmac_global.workqueue); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int cpphy_entry_prepare_reboot(struct notifier_block *self, unsigned long kind, void *cmd) { unsigned char instance; /* The arguments are not really needed here, but prescribed by the function interface */ self = self; kind = kind; cmd = cmd; /* We are neither interested in the kind of powerdown nor the command */ for(instance = 0; instance < CPMAC_MAX_PHY; instance++) { if(cpmac_global.cpphy[instance].mdio.cpmac_switch) { DEB_INFO("System is going down. Resetting VLAN.\n"); adm_prepare_reboot(&cpmac_global.cpphy[instance].mdio); } } return NOTIFY_DONE; } module_init(cpphy_entry_probe); module_exit(cpphy_entry_exit);