/*------------------------------------------------------------------------------------------*\ * Copyright (C) 2011 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 #undef INFINEON_CPU #if defined(CONFIG_VR9) || defined(CONFIG_AR9) || defined(CONFIG_AR10) #define INFINEON_CPU #include #include #endif #if defined(CONFIG_MACH_ATHEROS) || defined(CONFIG_ATH79) #include #include #endif #include #include #include #include #include "phy_plc.h" static void reset_plc(avmnet_module_t *this, enum rst_type type); /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int avmnet_phy_plc_init(avmnet_module_t *this) { struct avmnet_phy_plc_context *plc; int i, result; AVMNET_INFO("[%s] Init on module %s called.\n", __func__, this->name); plc = kzalloc(sizeof(struct avmnet_phy_plc_context), GFP_KERNEL); if(plc == NULL){ AVMNET_ERR("[%s] Init of avmnet-module %s failed.\n", __func__, this->name); return -ENOMEM; } this->priv = plc; if(this->initdata.phy.flags & AVMNET_CONFIG_FLAG_RESET){ plc->reset = this->initdata.phy.reset; #if defined(CONFIG_MACH_ATHEROS) if(plc->reset < 100){ /*--- Hängt nicht am Shift-Register => GPIO Resourcen anmelden ---*/ plc->gpio.name = "plc_reset", plc->gpio.flags = IORESOURCE_IO; plc->gpio.start = plc->reset; plc->gpio.end = plc->reset; if(request_resource(&gpio_resource, &(plc->gpio))){ AVMNET_ERR(KERN_ERR "[%s] ERROR request resource gpio %d\n", __FUNCTION__, plc->reset); kfree(plc); return -EIO; } } #endif } result = 0; for(i = 0; i < this->num_children; ++i){ result |= this->children[i]->init(this->children[i]); if(result < 0){ // handle error } } return result; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int avmnet_phy_plc_setup(avmnet_module_t *this) { int i, result; struct avmnet_phy_plc_context *plc; AVMNET_INFO("[%s] Setup on module %s called.\n", __func__, this->name); /* * do cunning setup-stuff */ plc = (struct avmnet_phy_plc_context *) this->priv; sema_init(&(plc->mutex), 1); down(&(plc->mutex)); // should not be necessary, just to shut up reset_plc() if(this->initdata.phy.flags & AVMNET_CONFIG_FLAG_RESET){ reset_plc(this, rst_on); plc->powerdown = 1; } up(&(plc->mutex)); result = 0; for(i = 0; i < this->num_children; ++i){ result |= this->children[i]->setup(this->children[i]); if(result != 0){ // handle error } } result = avmnet_cfg_register_module(this); if(result < 0){ AVMNET_ERR("[%s] avmnet_cfg_register_module failed for module %s\n", __func__, this->name); } return result; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int avmnet_phy_plc_exit(avmnet_module_t *this) { int i, result; struct avmnet_phy_plc_context *plc; AVMNET_INFO("[%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 */ plc = (struct avmnet_phy_plc_context *) this->priv; #if defined(CONFIG_MACH_ATHEROS) if(plc->reset < 100){ release_resource(&(plc->gpio)); } #endif this->priv = NULL; kfree(plc); return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int avmnet_phy_plc_setup_irq(avmnet_module_t *this __attribute__ ((unused)), unsigned int on __attribute__ ((unused))) { int result = 0; return result; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ unsigned int avmnet_phy_plc_reg_read(avmnet_module_t *this __attribute__ ((unused)), unsigned int addr __attribute__ ((unused)), unsigned int reg __attribute__ ((unused))) { return 0xffff; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int avmnet_phy_plc_reg_write(avmnet_module_t *this __attribute__ ((unused)), unsigned int addr __attribute__ ((unused)), unsigned int reg __attribute__ ((unused)), unsigned int val __attribute__ ((unused))) { return -EINVAL; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int avmnet_phy_plc_lock(avmnet_module_t *this __attribute__ ((unused))) { struct avmnet_phy_plc_context *plc; plc = (struct avmnet_phy_plc_context *) this->priv; return down_interruptible(&(plc->mutex)); return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void avmnet_phy_plc_unlock(avmnet_module_t *this __attribute__ ((unused))) { struct avmnet_phy_plc_context *plc; plc = (struct avmnet_phy_plc_context *) this->priv; up(&(plc->mutex)); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void avmnet_phy_plc_status_changed(avmnet_module_t *this, avmnet_module_t *caller) { int i; avmnet_module_t *child = NULL; /* * find out which of our progeny demands our attention */ for(i = 0; i < this->num_children; ++i){ if(this->children[i] == caller){ child = this->children[i]; break; } } if(child == NULL){ AVMNET_ERR("[%s] module %s: received status_changed from unknown module.\n", __func__, this->name); return; } /* * handle status change */ return; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int avmnet_phy_plc_poll(avmnet_module_t *this) { int i, result; for(i = 0; i < this->num_children; ++i){ result = this->children[i]->poll(this->children[i]); if(result < 0){ AVMNET_WARN("Module %s: poll() failed on child %s\n", this->name, this->children[i]->name); } } return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int avmnet_phy_plc_set_status(avmnet_module_t *this, avmnet_device_t *device_id, avmnet_linkstatus_t status) { return this->parent->set_status(this->parent, device_id, status); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int avmnet_phy_plc_powerdown(avmnet_module_t *this __attribute__ ((unused))) { struct avmnet_phy_plc_context *plc; AVMNET_INFO("[%s] Powerdown on module %s called.\n", __func__, this->name); if(!(this->initdata.phy.flags & AVMNET_CONFIG_FLAG_RESET)){ AVMNET_WARN("[%s] Module %s: no reset configured.\n", __func__, this->name); return 0; } plc = (struct avmnet_phy_plc_context *) this->priv; if(down_interruptible(&(plc->mutex))){ return -EINTR; } reset_plc(this, rst_on); plc->powerdown = 1; up(&(plc->mutex)); return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int avmnet_phy_plc_powerup(avmnet_module_t *this __attribute__ ((unused))) { struct avmnet_phy_plc_context *plc; AVMNET_INFO("[%s] Powerup on module %s called.\n", __func__, this->name); if(!(this->initdata.phy.flags & AVMNET_CONFIG_FLAG_RESET)){ AVMNET_WARN("[%s] Module %s: no reset configured.\n", __func__, this->name); return 0; } plc = (struct avmnet_phy_plc_context *) this->priv; if(down_interruptible(&(plc->mutex))){ return -EINTR; } reset_plc(this, rst_off); plc->powerdown = 0; up(&(plc->mutex)); return 0; } #ifdef INFINEON_CPU #error FIXME static void reset_plc(avmnet_module_t *this, enum rst_type type) { struct avmnet_phy_plc_context *plc = (struct avmnet_phy_plc_context *) this->priv; if((this->initdata.phy.flags & AVMNET_CONFIG_FLAG_RESET) == 0){ AVMNET_ERR("[%s] Reset not configured for module %s\n", __func__, this->name); return; } if(!down_trylock(&(plc->mutex))){ AVMNET_ERR("[%s] Called without MUTEX held!\n", __func__); dump_stack(); up(&(phy->mutex)); } if(type == rst_on || type == rst_pulse){ AVMNET_INFOTRC("[%s] PHY %s reset on\n", __func__, this->name); ifx_gpio_output_clear(this->initdata.phy.reset, IFX_GPIO_MODULE_EXTPHY_RESET); mdelay(10); } if(type == rst_off || type == rst_pulse){ AVMNET_INFOTRC("[%s] PHY %s reset off\n", __func__, this->name); ifx_gpio_output_set(this->initdata.phy.reset, IFX_GPIO_MODULE_EXTPHY_RESET); mdelay(10); } } #elif defined(CONFIG_MACH_ATHEROS) || defined(CONFIG_ATH79) static void reset_plc(avmnet_module_t *this, enum rst_type type) { struct avmnet_phy_plc_context *plc = (struct avmnet_phy_plc_context *) this->priv; if((this->initdata.phy.flags & AVMNET_CONFIG_FLAG_RESET) == 0){ AVMNET_ERR("[%s] Reset not configured for module %s\n", __func__, this->name); return; } if(!down_trylock(&(plc->mutex))){ AVMNET_ERR("[%s] Called without MUTEX held!\n", __func__); dump_stack(); up(&(plc->mutex)); } if(type == rst_on || type == rst_pulse){ ath_avm_gpio_out_bit(plc->reset, 0); mdelay(100); } if(type == rst_off || type == rst_pulse){ ath_avm_gpio_out_bit(plc->reset, 1); mdelay(100); } } #endif // INFINEON_CPU