/*------------------------------------------------------------------------------------------*\ * Copyright (C) 2016 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 "synopsys.h" #include #include #include #include #include #include #include #include #include #include #define MDIO_READ_FUNC "DWC_ETH_QOS_mdio_read_direct" #define MDIO_WRITE_FUNC "DWC_ETH_QOS_mdio_write_direct" #define MDIO_DEV_NAME "nsgmii0" #define GBE_SET_CLUSTER_SPEED_FUNC "gbe_netdev_update_cluster_speed" int synopsys_mdio_write(struct avmnet_master_dev *master, uint32_t addr, uint32_t reg, uint16_t data) { avmnet_module_t *this; struct avmnet_synopsys_context *ctx; int result; this = (avmnet_module_t *) master->priv; ctx = (struct avmnet_synopsys_context *) this->priv; BUG_ON(ctx->mdio_write == NULL); result = ctx->mdio_write(netdev_priv(ctx->mdio_dev), ctx->mdio_bus, addr, reg, data); // AVMNET_ERR("[%s] addr: 0x%04x reg: 0x%04x data: 0x%04x result: %d\n", __func__, addr, reg, data, result); return result; } int synopsys_mdio_read(struct avmnet_master_dev *master, uint32_t addr, uint32_t reg, uint16_t *data) { avmnet_module_t *this; struct avmnet_synopsys_context *ctx; int result, rd_data; this = (avmnet_module_t *) master->priv; ctx = (struct avmnet_synopsys_context *) this->priv; BUG_ON(ctx->mdio_read == NULL); result = ctx->mdio_read(netdev_priv(ctx->mdio_dev), ctx->mdio_bus, addr, reg, &rd_data); if(result == 0){ *data = rd_data & 0xFFFFu; } // AVMNET_ERR("[%s] addr: 0x%04x reg: 0x%04x data: 0x%04x result: %d\n", __func__, addr, reg, *data, result); return result; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int avmnet_synopsys_init(avmnet_module_t *this) { struct avmnet_synopsys_context *my_ctx; int i, result; AVMNET_ERR("[%s] Init on module %s called.\n", __func__, this->name); my_ctx = kzalloc(sizeof(struct avmnet_synopsys_context), GFP_KERNEL); if(my_ctx == NULL){ AVMNET_ERR("[%s] init of avmnet-module %s failed.\n", __func__, this->name); return -ENOMEM; } my_ctx->mdio_bus = this->initdata.dev.mdio_bus; my_ctx->master.mdio_read = &synopsys_mdio_read; my_ctx->master.mdio_write = &synopsys_mdio_write; my_ctx->master.priv = this; this->priv = my_ctx; for(i = 0; i < this->num_children; ++i){ result = this->children[i]->init(this->children[i]); if(result != 0){ AVMNET_WARN("Module %s: init() failed on child %s\n", this->name, this->children[i]->name); return result; } } return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static int setup_children(avmnet_module_t *this) { int i, result; bool was_down; struct avmnet_synopsys_context *ctx; avmnet_device_t *avm_dev; struct ifreq ifr; struct ifr_data_struct req; mm_segment_t oldfs = get_fs(); ctx = (struct avmnet_synopsys_context *) this->priv; result = 0; /* check if our master device is present */ if(ctx->master.master_dev == NULL){ ctx->master.master_dev = dev_get_by_name(&init_net, this->initdata.dev.orig_name); } if(ctx->master.master_dev == NULL){ AVMNET_ERR("[%s] No VLAN master device %s, deferring setup\n", __func__, this->initdata.dev.orig_name); goto err_out; } /* check if our master device is present */ if(ctx->mdio_dev == NULL){ ctx->mdio_dev = dev_get_by_name(&init_net, MDIO_DEV_NAME); } if(ctx->mdio_dev == NULL){ AVMNET_ERR("[%s] No MDIO device %s, deferring setup\n", __func__, MDIO_DEV_NAME); goto err_out; } /* try to get pointers to the MDIO read/write functions */ if(ctx->mdio_read == NULL){ ctx->mdio_read = __symbol_get(MDIO_READ_FUNC); } if(ctx->mdio_read == NULL){ AVMNET_ERR("[%s] Symbol %s not found!\n", __func__, MDIO_READ_FUNC); goto err_out; } if(ctx->mdio_write == NULL){ ctx->mdio_write = __symbol_get(MDIO_WRITE_FUNC); } if(ctx->mdio_write == NULL){ AVMNET_ERR("[%s] Symbol %s not found!\n", __func__, MDIO_WRITE_FUNC); goto err_out; } #if defined(CONFIG_MACH_PUMA7) && defined(CONFIG_TI_PACKET_PROCESSOR) if(ctx->gbe_update_cluster_speed == NULL){ ctx->gbe_update_cluster_speed = __symbol_get(GBE_SET_CLUSTER_SPEED_FUNC); } if(ctx->gbe_update_cluster_speed == NULL){ AVMNET_ERR("[%s] Symbol %s not found!\n", __func__, GBE_SET_CLUSTER_SPEED_FUNC); goto err_out; } #endif /* we have the master device and MDIO functions. Set up MDIO MUX via ioctl */ if(ctx->sock == NULL){ result = sock_create_kern(PF_INET, SOCK_DGRAM, 0, &(ctx->sock)); if(result != 0){ AVMNET_ERR("[%s] Error creating control socket: %d\n", __func__, result); goto err_out; } } /* ioctl only works on open device */ was_down = false; if(!(ctx->mdio_dev->flags & IFF_UP)){ was_down = true; rtnl_lock(); result = dev_open(ctx->mdio_dev); rtnl_unlock(); if(result != 0){ AVMNET_ERR("[%s] opening device %s failed.\n", __func__, ctx->mdio_dev->name); goto err_out; } } /* send ioctl */ strncpy(ifr.ifr_name, MDIO_DEV_NAME, IFNAMSIZ); memset(&req, 0, sizeof(req)); req.cmd = DWC_ETH_QOS_MUX_SET_CMD; req.mdioNum = ctx->mdio_bus; ifr.ifr_ifru.ifru_data = &req; result = kernel_sock_ioctl(ctx->sock, SIOCDEVPRIVATE, (unsigned long) &ifr); if (result == -ENOIOCTLCMD){ set_fs(KERNEL_DS); result = dev_ioctl(&init_net, SIOCDEVPRIVATE, (void __user *) &ifr); set_fs(oldfs); } /* restore master device open/close state */ if(was_down){ rtnl_lock(); (void) dev_close(ctx->mdio_dev); rtnl_unlock(); } if(result != 0){ AVMNET_ERR("[%s] %s ioctl failed: %d req: %d\n", __func__, ifr.ifr_name, result, req.command_error); goto err_out; } AVMNET_ERR("[%s] MDIO MUX on %s configured\n", __func__, ifr.ifr_name); /* register master device with avmnet and call setup() on all children */ result = avmnet_register_master(&(ctx->master)); if(result != 0){ AVMNET_ERR("[%s] register_master failed: %d\n", __func__, result); ctx->state = syn_state_err; goto err_out; } ctx->state = syn_state_up; /* Call set-up in all children */ for(i = 0; i < this->num_children; ++i){ result = this->children[i]->setup(this->children[i]); if(result != 0){ AVMNET_ERR("Module %s: setup() failed on child %s\n", this->name, this->children[i]->name); goto err_out; } } ti_hil_pp_event (TI_PP_ADD_VPID, (void *)ctx->master.master_dev); for(i = 0; i < avmnet_hw_config_entry->nr_avm_devices; ++i){ avm_dev = avmnet_hw_config_entry->avm_devices[i]; BUG_ON(avm_dev == NULL); if(avm_dev->device == NULL){ AVMNET_TRC("[%s] avm_device %s has no device\n", __func__, avm_dev->device_name); continue; } ti_hil_pp_event (TI_PP_ADD_VPID, (void *)avm_dev->device); } err_out: return result; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ extern int gpio_set_value(int gpio, int value); int avmnet_synopsys_setup(avmnet_module_t *this) { int result; struct avmnet_synopsys_context *ctx; AVMNET_ERR("[%s] Setup on module %s called.\n", __func__, this->name); /* * do cunning setup-stuff */ ctx = (struct avmnet_synopsys_context *) this->priv; avmnet_cfg_register_module(this); AVMNET_ERR("[%s] Asserting switch reset GPIO\n", __func__); result = gpio_set_value(PUMA7_GPIO_EXTSWITCH_RESET, 0); if(result != 0){ AVMNET_ERR("[%s] Putting switch in reset failed\n", __func__); goto err_out; } result = setup_children(this); if(result != 0){ if(ctx->state != syn_state_err){ result = 0; } goto err_out; } err_out: return result; } #if defined(CONFIG_MACH_PUMA7) && defined(CONFIG_TI_PACKET_PROCESSOR) static int update_gbe_cluster_speed(avmnet_module_t *this, avmnet_device_t *device_id, avmnet_linkstatus_t status) { /* Map the two avmnet speed bits to gbe driver scale, which is megabit */ const unsigned speed_to_gbedrv[] = { 10, 100, 1000, 1000 }; struct avmnet_synopsys_context *ctx = this->priv; struct net_device *netdev = device_id->device; int ret; if (!ctx->gbe_update_cluster_speed) { pr_err_once("avmnet: %s [%s]: Missing gbe driver callback\n", this->name, __func__); return -ENODEV; } AVMNET_INFOTRC("[%s] Updating PP cluster speed=%u for port %u (%s)\n", __func__, speed_to_gbedrv[status.Details.speed], device_id->external_port_no, netdev->name); ret = ctx->gbe_update_cluster_speed(netdev, speed_to_gbedrv[status.Details.speed], status.Details.fullduplex, netdev->name, 0); return ret ? -EINVAL : 0; /* gbe module only returns an error bit */ } #else static int update_gbe_cluster_speed(avmnet_module_t *this, avmnet_device_t *device_id, avmnet_linkstatus_t status) { return 0; } #endif /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int avmnet_synopsys_exit(avmnet_module_t *this) { int i, result; struct avmnet_synopsys_context *ctx; 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){ AVMNET_WARN("Module %s: exit() failed on child %s\n", this->name, this->children[i]->name); } } /* * clean up our mess */ ctx = (struct avmnet_synopsys_context *) this->priv; this->priv = NULL; kfree(ctx); return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int avmnet_synopsys_setup_irq(avmnet_module_t *this __maybe_unused, unsigned int on __maybe_unused) { return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ unsigned int avmnet_synopsys_reg_read(avmnet_module_t *this, unsigned int addr, unsigned int reg) { return 0xFFFF; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int avmnet_synopsys_reg_write(avmnet_module_t *this, unsigned int addr, unsigned int reg, unsigned int data) { return -ENODEV; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int avmnet_synopsys_lock(avmnet_module_t *this) { return -EINVAL; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void avmnet_synopsys_unlock(avmnet_module_t *this) { } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int avmnet_synopsys_trylock(avmnet_module_t *this) { return -EINVAL; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void avmnet_synopsys_status_changed(avmnet_module_t *this, avmnet_module_t *caller) { } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int avmnet_synopsys_set_status(avmnet_module_t *this, avmnet_device_t *device_id, avmnet_linkstatus_t status) { int ret = 0; if (device_id->status.Status != status.Status) { device_id->status = status; ret = update_gbe_cluster_speed(this, device_id, status); } return ret; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int avmnet_synopsys_poll(avmnet_module_t *this) { struct avmnet_synopsys_context *ctx; int i, result; ctx = (struct avmnet_synopsys_context *) this->priv; result = 0; if(ctx->state == syn_state_init){ result = setup_children(this); if(result != 0){ if(ctx->state != syn_state_err){ result = 0; } goto err_out; } } if(ctx->state != syn_state_up){ goto err_out; } for(i = 0; i < this->num_children; ++i){ if(this->children[i]->poll){ 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); } } } err_out: return result; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int avmnet_synopsys_powerdown(avmnet_module_t *this __maybe_unused) { AVMNET_DEBUG("[%s] Called.\n", __func__); return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int avmnet_synopsys_powerup(avmnet_module_t *this __maybe_unused) { AVMNET_DEBUG("[%s] Called.\n", __func__); return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int avmnet_synopsys_suspend(avmnet_module_t *this __maybe_unused, avmnet_module_t *caller __maybe_unused) { AVMNET_DEBUG("[%s] Called.\n", __func__); return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int avmnet_synopsys_resume(avmnet_module_t *this __maybe_unused, avmnet_module_t *caller __maybe_unused) { AVMNET_DEBUG("[%s] Called.\n", __func__); return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int avmnet_synopsys_reinit(avmnet_module_t *this __maybe_unused) { AVMNET_DEBUG("[%s] Called.\n", __func__); return 0; }