/*------------------------------------------------------------------------------------------*\ * 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 #include #include #include #include #include "avmnet_debug.h" #include "avmnet_module.h" #include "avmnet_config.h" #include "avmnet_common.h" #include "../management/avmnet_links.h" #include #include "ag934x.h" #include "atheros_mac.h" static struct resource ath_gmac_gpio = { .flags = IORESOURCE_IO, .start = -1, .end = -1 }; static unsigned athr_tx_desc_num[ATHR_GMAC_NMACS] = { CONFIG_ATHR_GMAC_NUMBER_TX_PKTS, CONFIG_ATHR_GMAC_NUMBER_TX_PKTS_1 }; static unsigned athr_rx_desc_num[ATHR_GMAC_NMACS] = { CONFIG_ATHR_GMAC_NUMBER_RX_PKTS, CONFIG_ATHR_GMAC_NUMBER_RX_PKTS_1 }; static unsigned int tx_max_desc_per_ds_pkt = 2; /*--- darf nicht kleiner als 2 sein, sonst funktioniert athr_gmac_tx_reap nicht ---*/ int frame_sz = 0; extern int fifo_3; /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static inline avmnet_device_t *find_avmnet_device(unsigned int port) { avmnet_device_t *avm_dev = NULL; int i; for(i = 0; i < avmnet_hw_config_entry->nr_avm_devices; ++i){ if(avmnet_hw_config_entry->avm_devices[i]->vlanID == port){ avm_dev = avmnet_hw_config_entry->avm_devices[i]; break; } } return avm_dev; } #if 0 /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ atomic_t athr_rx_disable_count; static inline void athr_gmac_intr_disable_recv(struct athmac_context *mac) { unsigned long flags; spin_lock_irqsave(&(mac->spinlock), flags); if (atomic_inc_return(&athr_rx_disable_count) != 1) { printk(KERN_ERR "{%s} ERROR IRQ disable count 0x%x 0x%x\n", __func__, athr_rx_disable_count, athr_gmac_reg_rd((mac->BaseAddress), ATHR_GMAC_DMA_INTR_MASK)); atomic_set(&athr_rx_disable_count, 1); } athr_gmac_reg_rmw_clear(mac->BaseAddress, ATHR_GMAC_DMA_INTR_MASK, ATHR_GMAC_INTR_RX ); spin_unlock_irqrestore(&mac->spinlock, flags); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static inline void athr_gmac_intr_enable_recv(struct athmac_context *mac) { unsigned long flags; spin_lock_irqsave(&(mac->spinlock), flags); if (atomic_dec_return(&athr_rx_disable_count) != 0) { printk(KERN_ERR "{%s} ERROR IRQ enable count 0x%x 0x%x\n", __func__, athr_rx_disable_count, athr_gmac_reg_rd((mac->BaseAddress), ATHR_GMAC_DMA_INTR_MASK)); atomic_set(&athr_rx_disable_count, 0); } athr_gmac_reg_rmw_set(mac->BaseAddress, ATHR_GMAC_DMA_INTR_MASK, ATHR_GMAC_INTR_RX ); } #endif /*------------------------------------------------------------------------------------------*\ * allocate and init rings, descriptors etc. \*------------------------------------------------------------------------------------------*/ static inline void athr_gmac_desc_alloc(athr_gmac_ring_t *r, int count) { r->ring_desc = dma_alloc_coherent(NULL, sizeof(athr_gmac_desc_t) * count, &r->ring_desc_dma, GFP_DMA); memset(r->ring_desc, 0, sizeof(athr_gmac_desc_t) * count); AVMNET_INFO("[%s] ring_desc 0x%p ring_desc_dma 0x%p len 0x%x\n", __func__, r->ring_desc, (void *) r->ring_desc_dma, sizeof(athr_gmac_desc_t) * count); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ #if 0 void athr_print_descriptors(struct athmac_context *mac) { unsigned int i; athr_gmac_ring_t *r; athr_gmac_desc_t *dc; r = &mac->mac_rxring; dc = r->ring_desc; printk("[%s] head %d tail %d anzahl %d\n", __FUNCTION__, r->ring_head, r->ring_tail, r->ring_nelem); for(i = 0 ; i < r->ring_nelem; i++, dc++ ) { printk("[%3d] %#p -> %#p %08x %c %c %d\n", i, dc, dc->next_desc, dc->pkt_start_addr, dc->is_empty ? 'E' : '-', dc->more ? 'M' : '-', dc->pkt_size ); } } #endif /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static int athr_gmac_ring_alloc(athr_gmac_ring_t *r, int count) { r->ring_desc = dma_alloc_coherent(NULL, sizeof(athr_gmac_desc_t) * count, &r->ring_desc_dma, GFP_DMA); if (! r->ring_desc) { AVMNET_ERR("[%s] : unable to allocate coherent descs\n", __func__); return 1; } memset(r->ring_desc, 0, sizeof(athr_gmac_desc_t) * count); return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void athr_gmac_ring_free(athr_gmac_ring_t *r) { dma_free_coherent(NULL, sizeof(athr_gmac_desc_t)*r->ring_nelem, r->ring_desc, r->ring_desc_dma); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void athr_gmac_rx_free(struct athmac_context *mac) { athr_gmac_ring_free(&mac->mac_rxring); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void athr_gmac_tx_free(struct athmac_context *mac) { unsigned int ac; for(ac = 0;ac < mac->mac_noacs; ac++) { athr_gmac_ring_free(&mac->mac_txring[ac]); } } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static int athr_gmac_tx_alloc(struct athmac_context *mac) { athr_gmac_ring_t *r ; athr_gmac_desc_t *ds; unsigned int ac; int ringlen; int i, next; AVMNET_DEBUG("[%s] mac->mac_noacs %d mac->num_tx_desc %d\n", __func__, mac->mac_noacs, mac->num_tx_desc); for(ac = 0; ac < mac->mac_noacs; ac++) { r = &mac->mac_txring[ac]; memset(r, 0, sizeof(athr_gmac_ring_t)); r->ring_nelem = mac->num_tx_desc; r->mac_txheader = dma_alloc_coherent(NULL, sizeof(athr_mac_packet_header_t) * r->ring_nelem, &r->txheader_dma, GFP_DMA); if (athr_gmac_ring_alloc(r, r->ring_nelem * 2)) return 1; /*----------------------------------------------------------------------------------*\ * Es werden immer descriptor paare allozeiert, deshalb sind in Wirklichkeit * ring_nelem * 2 Descriptoren vorhanden \*----------------------------------------------------------------------------------*/ ds = r->ring_desc; ringlen = r->ring_nelem * 2; for(i = 0; i < ringlen ; i++ ) { next = (i == (ringlen - 1)) ? 0 : (i + 1); ds[i].next_desc = virt_to_phys(&ds[next]); athr_gmac_tx_own(&ds[i]); } } return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static inline struct sk_buff * athr_gmac_buffer_alloc(void) { return dev_alloc_skb(ATHR_GMAC_RX_BUF_SIZE + frame_sz); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static int athr_gmac_rx_alloc(struct athmac_context *mac) { athr_gmac_ring_t *r; athr_gmac_desc_t *ds; unsigned int i, next; /*--- AVMNET_DEBUG("[%s] num_rx_desc %d\n", __func__, mac->num_rx_desc); ---*/ r = &mac->mac_rxring; memset(r, 0, sizeof(athr_gmac_ring_t)); r->ring_nelem = mac->num_rx_desc; if (athr_gmac_ring_alloc(r, r->ring_nelem)) return 1; ds = r->ring_desc; for(i = 0; i < r->ring_nelem; i++ ) { next = (i == (r->ring_nelem - 1)) ? 0 : (i + 1); ds[i].next_desc = virt_to_phys(&ds[next]); } for (i = 0; i < r->ring_nelem; i++) { ds = &r->ring_desc[i]; ds->pskb = athr_gmac_buffer_alloc(); if ( ! ds->pskb) goto error; if (mac->mac_unit == 0) skb_reserve(ds->pskb, NET_IP_ALIGN); /*--- athr_mac_cache_inv((unsigned long)(bf->buf_pkt->data - 2), ATHR_GMAC_RX_BUF_SIZE + frame_sz - ATHR_GMAC_RX_RESERVE); ---*/ dma_cache_wback((unsigned long)skb_shinfo(ds->pskb), sizeof(struct skb_shared_info)); dma_cache_inv((unsigned long)(ds->pskb->data), ATHR_GMAC_RX_BUF_SIZE + frame_sz); ds->pkt_start_addr = virt_to_phys(ds->pskb->data); ds->res1 = 0; ds->res2 = 0; ds->ftpp_override = 0; ds->res3 = 0; ds->more = 0; athr_gmac_rx_give_to_dma(ds); } return 0; error: AVMNET_ERR("[%s] : unable to allocate rx\n", __func__); athr_gmac_rx_free(mac); return 1; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void athmac_gmac_enable_hw(struct athmac_context *mac) { /*--- enable FLOW-Control ---*/ #if defined(CONFIG_MACH_QCA955x) || defined(CONFIG_SOC_QCA955X) athr_gmac_reg_wr(mac->BaseAddress, ATHR_GMAC_CFG1, (ATHR_GMAC_CFG1_RX_EN | ATHR_GMAC_CFG1_TX_EN | ATHR_GMAC_CFG1_RX_FCTL)); #else athr_gmac_reg_wr(mac->BaseAddress, ATHR_GMAC_CFG1, (ATHR_GMAC_CFG1_RX_EN | ATHR_GMAC_CFG1_TX_EN | ATHR_GMAC_CFG1_RX_FCTL | ATHR_GMAC_CFG1_TX_FCTL)); #endif AVMNET_TRC("[%s] : cfg1 %#x cfg2 %#x\n", __func__, athr_gmac_reg_rd(mac->BaseAddress, ATHR_GMAC_CFG1), athr_gmac_reg_rd(mac->BaseAddress, ATHR_GMAC_CFG2)); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void athmac_gmac_setup_hw(struct athmac_context *mac) { athr_gmac_ring_t *tx, *rx; AVMNET_INFO("[%s] mac->mac_unit %d\n", __func__, mac->mac_unit); /* clear the rx fifo state if any */ while (athr_gmac_reg_rd(mac->BaseAddress, ATHR_GMAC_DMA_RX_STATUS) & ATHR_GMAC_RX_STATUS_PKT_RCVD) { athr_gmac_intr_ack_rx(mac->BaseAddress); } switch (mac->mac_unit) { case 0: #if defined(CONFIG_MACH_QCA955x) || defined(CONFIG_SOC_QCA955X) if (is_qca9556()) { athr_gmac_reg_rmw_set(mac->BaseAddress, ATHR_GMAC_CFG2, (ATHR_GMAC_CFG2_PAD_CRC_EN | ATHR_GMAC_CFG2_LEN_CHECK)); athr_gmac_reg_rmw_set(mac->BaseAddress, ATHR_GMAC_FIFO_CFG_5, ATHR_GMAC_BYTE_PER_CLK_EN); } else #endif { athr_gmac_reg_rmw_set(mac->BaseAddress, ATHR_GMAC_CFG2, (ATHR_GMAC_CFG2_PAD_CRC_EN | ATHR_GMAC_CFG2_LEN_CHECK | ATHR_GMAC_CFG2_IF_10_100)); athr_gmac_reg_rmw_clear(mac->BaseAddress, ATHR_GMAC_FIFO_CFG_5, ATHR_GMAC_BYTE_PER_CLK_EN); } break; case 1: athr_gmac_reg_rmw_set(mac->BaseAddress, ATHR_GMAC_CFG2, (ATHR_GMAC_CFG2_FDX | ATHR_GMAC_CFG2_PAD_CRC_EN | ATHR_GMAC_CFG2_LEN_CHECK | ATHR_GMAC_CFG2_IF_1000)); athr_gmac_reg_rmw_set(mac->BaseAddress, ATHR_GMAC_FIFO_CFG_5, ATHR_GMAC_BYTE_PER_CLK_EN); break; } #if defined(ATH_FLAGS) /*------------------------------------------------------------------------------------------*\ * Disable ATHR_GMAC_CFG2_LEN_CHECK to fix the bug that the mac address is mistaken as * length when enabling Atheros header \*------------------------------------------------------------------------------------------*/ if (mac_has_flag(mac,ATHR_HEADER_ENABLED)) athr_gmac_reg_rmw_clear(mac->BaseAddress, ATHR_GMAC_CFG2, ATHR_GMAC_CFG2_LEN_CHECK); #endif athr_gmac_reg_wr(mac->BaseAddress, ATHR_GMAC_FIFO_CFG_0, 0x1f00); athr_gmac_reg_wr(mac->BaseAddress, ATHR_GMAC_FIFO_CFG_1, 0x10ffff); #if defined(CONFIG_MACH_QCA955x) || defined(CONFIG_SOC_QCA955X) athr_gmac_reg_wr(mac->BaseAddress, ATHR_GMAC_FIFO_CFG_2, 0x027001aa); #else athr_gmac_reg_wr(mac->BaseAddress, ATHR_GMAC_FIFO_CFG_2, 0x015500aa); #endif athr_gmac_reg_wr(mac->BaseAddress, ATHR_GMAC_FIFO_CFG_3, fifo_3); athr_gmac_reg_wr(mac->BaseAddress, ATHR_GMAC_FIFO_CFG_4, ATHR_ALL_FRAMES); /*--- athr_gmac_reg_wr(mac->BaseAddress, ATHR_GMAC_FIFO_CFG_5, ATHR_ALL_FRAMES); ---*/ /*--- erstmal alles empfangen ---*/ athr_gmac_reg_wr(mac->BaseAddress, ATHR_GMAC_FIFO_CFG_5, ATHR_DEFAULT_FRAMES); athr_gmac_reg_wr(mac->BaseAddress, ATHR_GMAC_DMA_TXFIFO_THRESH ,0x01d80160); #if defined(ATH_FLAGS) if (mac_has_flag(mac,ATHR_JUMBO_FR)) { athr_gmac_reg_rmw_set(mac->BaseAddress, ATHR_GMAC_CFG2, ATHR_GMAC_CFG2_HUGE_FRAME_EN); athr_gmac_reg_wr(mac->BaseAddress, ATHR_GMAC_MAX_PKTLEN, frame_sz); } #endif #if defined(ATH_FLAGS) if (mac_has_flag(mac, WAN_QOS_SOFT_CLASS)) { athr_gmac_reg_wr(mac->BaseAddress,ATHR_GMAC_DMA_TX_ARB_CFG, ATHR_GMAC_TX_QOS_MODE_WEIGHTED | ATHR_GMAC_TX_QOS_WGT_0(0x7) | ATHR_GMAC_TX_QOS_WGT_1(0x5) | ATHR_GMAC_TX_QOS_WGT_2(0x3) | ATHR_GMAC_TX_QOS_WGT_3(0x1)); for(ac = 0;ac < mac->mac_noacs; ac++) { tx = &mac->mac_txring[ac]; t0 = &tx->ring_desc[0]; switch(ac) { case ENET_AC_VO: athr_gmac_reg_wr(mac->BaseAddress, ATHR_GMAC_DMA_TX_DESC_Q0, athr_gmac_desc_dma_addr(tx, t0)); break; case ENET_AC_VI: athr_gmac_reg_wr(mac->BaseAddress, ATHR_GMAC_DMA_TX_DESC_Q1, athr_gmac_desc_dma_addr(tx, t0)); break; case ENET_AC_BK: athr_gmac_reg_wr(mac->BaseAddress, ATHR_GMAC_DMA_TX_DESC_Q2, athr_gmac_desc_dma_addr(tx, t0)); break; case ENET_AC_BE: athr_gmac_reg_wr(mac->BaseAddress, ATHR_GMAC_DMA_TX_DESC_Q3, athr_gmac_desc_dma_addr(tx, t0)); break; } } } else #endif { athr_gmac_reg_rmw_clear(mac->BaseAddress, ATHR_GMAC_DMA_TX_ARB_CFG, ATHR_GMAC_TX_QOS_MODE_WEIGHTED); tx = &mac->mac_txring[0]; athr_gmac_reg_wr(mac->BaseAddress, ATHR_GMAC_DMA_TX_DESC_Q0, virt_to_phys(&tx->ring_desc[0])); } rx = &mac->mac_rxring; athr_gmac_reg_wr(mac->BaseAddress, ATHR_GMAC_DMA_RX_DESC, virt_to_phys(&rx->ring_desc[0])); AVMNET_TRC("[%s] : ATHR_GMAC_DMA_TX_ARB_CFG 0x%x\n", __func__, athr_gmac_reg_rd(mac->BaseAddress, ATHR_GMAC_DMA_TX_ARB_CFG)); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int athmac_setup_irq(avmnet_module_t *this, unsigned int on) { struct athmac_context *mac = (struct athmac_context *)this->priv; AVMNET_INFOTRC("[%s] %s %s\n", __func__, this->name, on ? "on":"off"); if (on) { athr_gmac_int_enable(mac->BaseAddress); } else { athr_gmac_int_disable(mac->BaseAddress); } return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static inline unsigned int athr_gmac_ndesc_unused(athr_gmac_ring_t *ring) { int head = ring->ring_head, tail = ring->ring_tail; return ((tail > head ? 0 : ring->ring_nelem) + tail - head); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static inline unsigned int athr_gmac_tx_ring_full(avmnet_module_t *this,int ac) { struct athmac_context *mac = (struct athmac_context *)this->priv; athr_gmac_ring_t *r = &mac->mac_txring[ac]; #if (AVMNET_USE_DEBUG_LEVEL_DEBUG != 0) if (athr_gmac_ndesc_unused(r) < 10) printk(KERN_ERR "{%s} ndesc_unused %d head %d tail %d\n", __func__, athr_gmac_ndesc_unused(r), r->ring_head, r->ring_tail); #endif return (athr_gmac_ndesc_unused(r) < tx_max_desc_per_ds_pkt); } /*------------------------------------------------------------------------------------------*\ * Reap from tail till the head or whenever we encounter an unxmited packet. \*------------------------------------------------------------------------------------------*/ static void athr_gmac_tx_reap(avmnet_module_t *this, int ac, unsigned int sent) { struct athmac_context *mac = (struct athmac_context *)this->priv; avmnet_device_t *avmnet_device; unsigned long flags; athr_gmac_ring_t *r = &mac->mac_txring[ac]; athr_gmac_desc_t *ds; unsigned int tail; int i; tail = r->ring_tail; while (sent) { ds = &r->ring_desc[tail * 2]; if(unlikely(athr_gmac_tx_owned_by_dma(ds))) { AVMNET_ERR("[%s] header desc not ready (tail %d)\n", __FUNCTION__, tail); break; } if ( ! mac->txdma_acked) { athr_gmac_intr_ack_tx(mac->BaseAddress); } else mac->txdma_acked--; if(unlikely(athr_gmac_tx_owned_by_dma(ds + 1))) { AVMNET_ERR("[%s] data desc not ready (tail %d)\n", __FUNCTION__, tail); break; } /*--- AVMNET_DEBUG("[%s] acc desc (tail %d) sent %d \n", __FUNCTION__, tail, sent); ---*/ if ( ! mac->txdma_acked) { athr_gmac_intr_ack_tx(mac->BaseAddress); } else mac->txdma_acked--; dev_kfree_skb_any(ds->pskb); ds->pskb = NULL; athr_gmac_ring_incr(tail); sent--; } r->ring_tail = tail; #if defined(ATH_FLAGS) if (mac_has_flag(mac,WAN_QOS_SOFT_CLASS) && (athr_gmac_ndesc_unused(r) >= mac->qstart_thresh) && netif_carrier_ok(mac->mac_dev)) { mac->tx_ac_bt &= ~(1 << ac); } #endif if (mac->dma_stopped) { if (athr_gmac_ndesc_unused(r) > mac->qstart_thresh) { /* Keep reaping till all the queues have enough descriptors. */ #if defined(ATH_FLAGS) if (mac_has_flag(mac,WAN_QOS_SOFT_CLASS) && (mac->tx_ac_bt != 0)) { athr_gmac_intr_enable_tx(mac->BaseAddress); goto done; } #endif mac->dma_stopped = 0; for(i = 0; i < avmnet_hw_config_entry->nr_avm_devices; ++i){ avmnet_device = avmnet_hw_config_entry->avm_devices[i]; if(avmnet_device->mac_module != this) continue; if( netif_queue_stopped(avmnet_device->device) && netif_carrier_ok(avmnet_device->device)) { AVMNET_DEBUG("{%s} wakeup device %s unused %d\n", __func__, avmnet_device->device->name, athr_gmac_ndesc_unused(r)); netif_tx_wake_all_queues(avmnet_device->device); } } } else { AVMNET_DEBUG("{%s} enable Interrupt unused Buffer %d\n", __func__, athr_gmac_ndesc_unused(r)); spin_lock_irqsave(&mac->spinlock, flags); athr_gmac_intr_enable_tx(mac->BaseAddress); spin_unlock_irqrestore(&mac->spinlock, flags); } } /*--- AVMNET_DEBUG("[%s] tx_status 0x%x tx_enable 0x%x\n", __func__, athr_gmac_reg_rd(mac->BaseAddress, ATHR_GMAC_DMA_TX_STATUS), athr_gmac_reg_rd(mac->BaseAddress, ATHR_GMAC_DMA_TX_CTRL)); ---*/ #if defined(ATH_FLAGS) done: #endif return; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void athr_gmac_txreap_task(unsigned long data) { avmnet_module_t *this = (avmnet_module_t *)data; struct athmac_context *mac = (struct athmac_context *)this->priv; unsigned int ac; unsigned int sent = (athr_gmac_reg_rd(mac->BaseAddress, ATHR_GMAC_DMA_TX_STATUS) >> 16) & 0xFF; unsigned long flags; AVMNET_TRC("[%s] %s sent 0x%x\n", __func__, this->name, sent); if ((mac->txdma_acked + sent) > (mac->reap_thresh >> 1)) { for(ac = 0; ac < mac->mac_noacs; ac++) { AVMNET_DEBUG("[%s] txdma_acked %d sent %d\n", __func__, mac->txdma_acked, sent); athr_gmac_tx_reap(this, ac, (mac->txdma_acked + sent)>>1); } } else { mac->txdma_acked++; athr_gmac_intr_ack_tx(mac->BaseAddress); spin_lock_irqsave(&mac->spinlock, flags); athr_gmac_intr_enable_tx(mac->BaseAddress); spin_unlock_irqrestore(&mac->spinlock, flags); } return; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ athr_gmac_desc_t *athr_gmac_get_tx_ds(struct athmac_context *mac, struct sk_buff *skb) { athr_gmac_desc_t *ds; athr_gmac_ring_t *r; #if defined(ATH_FLAGS) athr_gmac_buffer_t *bp; #endif r = &mac->mac_txring[skb->priority]; if (unlikely( ! r)) { AVMNET_ERR("[%s] skb->priority %d\n", __func__, skb->priority); panic("!r"); } ds = &r->ring_desc[r->ring_head * 2]; /* immer paarweise */ ds->pkt_start_addr = (unsigned long)&(r->mac_txheader[r->ring_head]); ds->pskb = skb; if (unlikely(athr_gmac_tx_owned_by_dma(ds))) { panic("get_tx_ds desc athr_gmac_tx_owned_by_dma\n"); } athr_gmac_ring_incr(r->ring_head); return ds; } /* * Tx operation: * We do lazy reaping - only when the ring is "thresh" full. If the ring is * full and the hardware is not even done with the first pkt we q'd, we turn * on the tx interrupt, stop all q's and wait for h/w to * tell us when its done with a "few" pkts, and then turn the Qs on again. * * Locking: * The interrupt only touches the ring when Q's stopped => Tx is lockless, * except when handling ring full. * * Desc Flushing: Flushing needs to be handled at various levels, broadly: * - The DDr FIFOs for desc reads. * - WB's for desc writes. */ static void athr_gmac_handle_tx_full(avmnet_module_t *this, int ac __attribute__((unused))) { avmnet_device_t *avmnet_device; struct athmac_context *mac = (struct athmac_context *)this->priv; int i; AVMNET_DEBUG("[%s]: %s\n", __func__, this->name); /*--- Do nothing if the link went down while we are here. ---*/ if (mac->dma_stopped) { AVMNET_ERR("[%s] %s queue always stoped\n", __func__, this->name); return; } mac->dma_stopped = 1; #if defined(ATH_FLAGS) if (mac_has_flag(mac,WAN_QOS_SOFT_CLASS)) { mac->tx_ac_bt |= (1 << ac); if (!((mac->tx_ac_bt & 0xf) == 0xf)) return; } #endif for(i = 0; i < avmnet_hw_config_entry->nr_avm_devices; ++i){ avmnet_device = avmnet_hw_config_entry->avm_devices[i]; if(avmnet_device->mac_module != this) continue; if( ! netif_queue_stopped(avmnet_device->device)){ AVMNET_DEBUG("{%s} %s stop queue %d\n", __func__, avmnet_device->device->name); netif_tx_lock(avmnet_device->device); netif_tx_stop_all_queues(avmnet_device->device); netif_tx_unlock(avmnet_device->device); } avmnet_device->device->stats.tx_fifo_errors ++; } athr_gmac_intr_enable_tx(mac->BaseAddress); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int athr_gmac_hard_start(struct sk_buff *skb, struct net_device *device) { avmnet_device_t *avm_device = ((avmnet_netdev_priv_t *)netdev_priv(device))->avm_dev; avmnet_module_t *this = (avmnet_module_t *)avm_device->mac_module; struct athmac_context *mac = (struct athmac_context *)this->priv; athr_gmac_desc_t *ds, *head_ds; unsigned int ac; unsigned long flags; athr_mac_packet_header_t *header; unsigned int sent = (athr_gmac_reg_rd(mac->BaseAddress, ATHR_GMAC_DMA_TX_STATUS) >> 16) & 0xFF; /*--------------------------------------------------------------------------------------*\ * TX DMA Descriptoren aufräumen, Achtung wir nutzen immer 2 Descriptoren \*--------------------------------------------------------------------------------------*/ if (((mac->txdma_acked + sent) >> 1) > mac->reap_thresh) { for (ac = 0;ac < mac->mac_noacs; ac++) { athr_gmac_tx_reap(this, ac, (sent + mac->txdma_acked) >> 1); } } if(skb->len < 16) { AVMNET_ERR("[%s] skb->len %u < 16. Dropping skb %p.\n", __func__, skb->len, skb); goto dropit; } skb->priority = ENET_AC_BE; spin_lock_irqsave(&mac->spinlock, flags); #if 0 athr_gmac_tx_qos(mac,skb); if (athr_gmac_check_qos_bt(mac,skb)) goto dropit; #if defined(ATH_FLAGS) if (mac_has_flag(mac,ATHR_HEADER_ENABLED)) athr_gmac_add_hdr(mac,skb); #endif #endif #if (AVMNET_USE_DEBUG_LEVEL_DEBUG != 0) { athr_gmac_ring_t *r = &mac->mac_txring[skb->priority]; printk(KERN_ERR "sent - h %d t %d l %d 0x%x 0x%x 0x%x 0x%x\n", r->ring_head, r->ring_tail, skb->len, athr_gmac_reg_rd(mac->BaseAddress, ATHR_GMAC_DMA_TX_STATUS), athr_gmac_reg_rd(mac->BaseAddress, ATHR_GMAC_DMA_TX_CTRL), athr_gmac_reg_rd(mac->BaseAddress, ATHR_GMAC_DMA_FIFO_DEPTH), athr_gmac_reg_rd(mac->BaseAddress, ATHR_GMAC_DMA_TXFIFO_THRESH)); } #endif # if defined(CONFIG_IP_MULTICAST_FASTFORWARD) { avmnet_netdev_priv_t *priv = netdev_priv(device); mcfw_portset set; mcfw_portset_reset(&set); mcfw_portset_port_add(&set, 0); (void) mcfw_snoop_send(&priv->mcfw_netdrv, set, skb); } # endif /*--- #if defined(CONFIG_IP_MULTICAST_FASTFORWARD) ---*/ switch (mac->mac_unit) { case 0: /*------------------------------------------------------------------------------*\ * DMA Descriptor für den MAC Header ohne den Atheros Spezial Header (12 Bytes) 1. * DMA Descriptor für den Inhalt des Paketes ohne MAC Header (offset 12 Bytes) 2. \*------------------------------------------------------------------------------*/ head_ds = athr_gmac_get_tx_ds(mac, skb); head_ds->res1 = 0; head_ds->more = 1; head_ds->res2 = 0; head_ds->ftpp_override = 0; head_ds->res3 = 0; header = (athr_mac_packet_header_t *)head_ds->pkt_start_addr; memcpy(header->buff, skb->data, 2 * ETH_ALEN); head_ds->pkt_size = 2 * ETH_ALEN; head_ds->pkt_start_addr = virt_to_phys(header); ds = head_ds + 1; ds->res1 = 0; ds->more = 0; ds->res2 = 0; ds->ftpp_override = 0; ds->res3 = 0; ds->pkt_size = skb->len - 2 * ETH_ALEN; ds->pkt_start_addr = virt_to_phys((skb->data + 2 * ETH_ALEN)); dma_cache_wback_inv((unsigned long)(skb->data + (2 * ETH_ALEN)), skb->len - 2 * ETH_ALEN); break; case 1: /*------------------------------------------------------------------------------*\ * DMA Descriptor für den MAC Header und den Atheros Spezial Header (14 Bytes) 1. * DMA Descriptor für den Inhalt des Paketes ohne MAC Header (offset 12 Bytes) 2. \*------------------------------------------------------------------------------*/ head_ds = athr_gmac_get_tx_ds(mac, skb); head_ds->res1 = 0; head_ds->more = 1; head_ds->res2 = 0; head_ds->ftpp_override = 0; head_ds->res3 = 0; header = (athr_mac_packet_header_t *)head_ds->pkt_start_addr; memcpy(header->buff, skb->data, 2 * ETH_ALEN); header->s.header.version = 2; header->s.header.priority = skb->priority; header->s.header.type = 0; header->s.header.from_cpu = 1; header->s.header.portnum = 1 << (avm_device->vlanID); header->s.data = *(unsigned short *)(skb->data + 2 * ETH_ALEN); head_ds->pkt_size = 2 * ETH_ALEN + 2 + 2; head_ds->pkt_start_addr = virt_to_phys(header); ds = head_ds + 1; ds->res1 = 0; ds->more = 0; ds->res2 = 0; ds->ftpp_override = 0; ds->res3 = 0; ds->pkt_size = skb->len - 2 * ETH_ALEN - 2; ds->pkt_start_addr = virt_to_phys((skb->data + 2 * ETH_ALEN + 2)); dma_cache_wback_inv((unsigned long)(skb->data + 2 * ETH_ALEN + 2), skb->len - 2 * ETH_ALEN - 2); break; default: panic("illegal mac unit\n"); } /*--- #ifdef CONFIG_ATHRS_HW_NAT ---*/ /*--- athr_nat_process_egress_pkts(mac->mac_unit, skb, ds); ---*/ /*--- #endif ---*/ /*--- #ifdef CONFIG_ATHRS_HW_ACL ---*/ /*--- athr_acl_enable_tx(mac,ds); ---*/ /*--- #endif ---*/ wmb(); /*--- ath_flush_ge(mac->mac_unit); ---*/ /*--------------------------------------------------------------------------------------*\ * Bei zweigeteilten Frames zuerst den Zweiten Teil auf DMA Owner setzen und anschließend * den ersten. Wenn man das umgekehrt macht, besteht die Gefahr, dass zwischen die beiden * 'give_to_dma' aufrufe ein Interrupt kommt und den Vorgang verzögert. Dann könnte der * DMA Controller schon mit dem Senden des ersten Teil angefangen haben und findet kein * zweiten Teil. Dadurch wird der Senden des gesamtframe unterbrochen und somit der Frame * zerstört. \*--------------------------------------------------------------------------------------*/ athr_gmac_tx_give_to_dma(ds); athr_gmac_tx_give_to_dma(head_ds); wmb(); device->trans_start = jiffies; #if defined(ATH_FLAGS) if (mac_has_flag(mac,WAN_QOS_SOFT_CLASS)) { athr_gmac_tx_start_qos(mac,skb->priority); } else { athr_gmac_tx_start(mac); } #else athr_gmac_tx_start(mac->BaseAddress); #endif for ( ac = 0;ac < mac->mac_noacs; ac++) { if (unlikely(athr_gmac_tx_ring_full(this, ac))) { athr_gmac_handle_tx_full(this, ac); } } spin_unlock_irqrestore(&mac->spinlock, flags); avm_device->device->stats.tx_packets ++; avm_device->device->stats.tx_bytes += skb->len; return NETDEV_TX_OK; dropit: #if defined(ATH_FLAGS) if (mac_has_flag(mac,WAN_QOS_SOFT_CLASS)) mac->tx_ac_drops++; else #endif dev_kfree_skb_any(skb); return NETDEV_TX_OK; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void athr_gmac_fast_reset(struct athmac_context *mac) { avmnet_module_t *this = mac->this_module; uint32_t rx_ds, tx_ds; unsigned long flags; unsigned int isr, irq_mask, resetmask; unsigned int w1 = 0, w2 = 0; unsigned int unused, sent; athr_gmac_ring_t *t = &mac->mac_txring[0]; spin_lock_irqsave(&mac->spinlock, flags); irq_mask = athr_gmac_get_msr(mac->BaseAddress); isr = athr_gmac_get_isr(mac->BaseAddress); unused = athr_gmac_ndesc_unused(t); sent = (athr_gmac_reg_rd(mac->BaseAddress, ATHR_GMAC_DMA_TX_STATUS) >> 16) & 0xFF; AVMNET_ERR("<%s> %s irq 0x%x 0x%x unused %d sent %d\n", __func__, this->name, irq_mask, isr, unused, sent); athr_gmac_tx_stop(mac->BaseAddress); athr_gmac_rx_stop(mac->BaseAddress); rx_ds = athr_gmac_reg_rd(mac->BaseAddress, ATHR_GMAC_DMA_RX_DESC); tx_ds = athr_gmac_reg_rd(mac->BaseAddress, ATHR_GMAC_DMA_TX_DESC); /*------------------------------------------------------------------------------------------*\ * after reset ATHR_GMAC_DMA_TX_STATUS == 0, do not ack too much descriptors on next tx_reap \*------------------------------------------------------------------------------------------*/ mac->txdma_acked = (mac->num_tx_desc - unused) << 1; resetmask = athr_gmac_reset_mask(mac->mac_unit); /*--- put into reset, hold, pull out. ---*/ ath_reg_rmw_set(ATH_RESET, resetmask); udelay(10); ath_reg_rmw_clear(ATH_RESET, resetmask); udelay(10); /*--- athr_init_hwaccels(mac); ---*/ athmac_gmac_setup_hw(mac); /*--- restore RX/TX- Descriptors ---*/ athr_gmac_reg_wr(mac->BaseAddress, ATHR_GMAC_DMA_TX_DESC, tx_ds); athr_gmac_reg_wr(mac->BaseAddress, ATHR_GMAC_DMA_RX_DESC, rx_ds); /*--- set the mac addr ---*/ addr_to_words(mac->device->dev_addr, w1, w2); athr_gmac_reg_wr(mac->BaseAddress, ATHR_GMAC_GE_MAC_ADDR1, w1); athr_gmac_reg_wr(mac->BaseAddress, ATHR_GMAC_GE_MAC_ADDR2, w2); mac->dma_reset = 3; /*--- Restore interrupts ---*/ athr_gmac_reg_wr(mac->BaseAddress, ATHR_GMAC_DMA_INTR_MASK, irq_mask); spin_unlock_irqrestore(&mac->spinlock, flags); if(this->initdata.mac.flags & AVMNET_CONFIG_FLAG_SWITCHPORT) athmac_gmac_enable_hw(mac); athr_gmac_tx_start(mac->BaseAddress); athr_gmac_rx_start(mac->BaseAddress); #if 0 AVMNET_ERR("<%s> irq 0x%x rx 0x%x\n", __func__, athr_gmac_get_isr(mac->BaseAddress), athr_gmac_reg_rd(mac->BaseAddress, ATHR_GMAC_DMA_RX_STATUS)); AVMNET_ERR("enable RX-IRQ 0x%x\n", athr_gmac_get_msr(mac->BaseAddress)); #endif } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void athr_gmac_tx_timeout(struct net_device *device) { avmnet_device_t *avm_device = ((avmnet_netdev_priv_t *)netdev_priv(device))->avm_dev; avmnet_module_t *this = (avmnet_module_t *)avm_device->mac_module; struct athmac_context *mac = (struct athmac_context *)this->priv; athr_gmac_fast_reset(mac); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static inline void athr_gmac_set_mac_duplex(struct athmac_context *mac, int fdx) { if (fdx) { #if defined(CONFIG_MACH_QCA955x) || defined(CONFIG_SOC_QCA955X) athr_gmac_reg_rmw_set(mac->BaseAddress, ATHR_GMAC_CFG1, ATHR_GMAC_CFG1_TX_FCTL); #endif athr_gmac_reg_rmw_set(mac->BaseAddress, ATHR_GMAC_CFG2, ATHR_GMAC_CFG2_FDX); } else { #if defined(CONFIG_MACH_QCA955x) || defined(CONFIG_SOC_QCA955X) athr_gmac_reg_rmw_clear(mac->BaseAddress, ATHR_GMAC_CFG1, ATHR_GMAC_CFG1_TX_FCTL); #endif athr_gmac_reg_rmw_clear(mac->BaseAddress, ATHR_GMAC_CFG2, ATHR_GMAC_CFG2_FDX); } } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static inline void athr_gmac_set_mac_speed(struct athmac_context *mac, int speed) { switch (speed) { case ATHR_PHY_SPEED_10T: athr_gmac_reg_rmw_clear(mac->BaseAddress, ATHR_GMAC_IFCTL, ATHR_GMAC_IFCTL_SPEED); break; case ATHR_PHY_SPEED_100T: athr_gmac_reg_rmw_set(mac->BaseAddress, ATHR_GMAC_IFCTL, ATHR_GMAC_IFCTL_SPEED); break; case ATHR_PHY_SPEED_1000T: case ATHR_PHY_SPEED_UNKNOWN: break; } } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static inline void athr_gmac_set_mac_if(struct athmac_context *mac, int speed) { athr_gmac_reg_rmw_clear(mac->BaseAddress, ATHR_GMAC_CFG2, (ATHR_GMAC_CFG2_IF_1000| ATHR_GMAC_CFG2_IF_10_100)); switch (speed) { case ATHR_PHY_SPEED_10T: case ATHR_PHY_SPEED_100T: athr_gmac_reg_rmw_set(mac->BaseAddress, ATHR_GMAC_CFG2, ATHR_GMAC_CFG2_IF_10_100); athr_gmac_reg_rmw_clear(mac->BaseAddress,ATHR_GMAC_FIFO_CFG_5, ATHR_GMAC_BYTE_PER_CLK_EN); break; case ATHR_PHY_SPEED_1000T: athr_gmac_reg_rmw_set(mac->BaseAddress, ATHR_GMAC_CFG2, ATHR_GMAC_CFG2_IF_1000); athr_gmac_reg_rmw_set(mac->BaseAddress, ATHR_GMAC_FIFO_CFG_5, ATHR_GMAC_BYTE_PER_CLK_EN); break; case ATHR_PHY_SPEED_UNKNOWN: break; } } /*------------------------------------------------------------------------------------------*\ * Several fields need to be programmed based on what the PHY negotiated * Ideally we should quiesce everything before touching the pll, but: * 1. If its a linkup/linkdown, we dont care about quiescing the traffic. * 2. If its a single gigE PHY, this can only happen on lup/ldown. * 3. If its a 100Mpbs switch, the link will always remain at 100 (or nothing) * 4. If its a gigE switch then the speed should always be set at 1000Mpbs, * and the switch should provide buffering for slower devices. * * XXX Only gigE PLL can be changed as a parameter for now. 100/10 is hardcoded. * XXX Need defines for them - * XXX FIFO settings based on the mode \*------------------------------------------------------------------------------------------*/ void athr_gmac_set_mac_from_link(avmnet_module_t *this, avmnet_linkstatus_t status) { struct athmac_context *mac = (struct athmac_context *)this->priv; /*--- printk(KERN_ERR "{%s} %s\n", __func__, this->name); ---*/ if (mac->mac_unit == 0) { AVMNET_ERR("[%s] %s : carrier %s %sduplex\n", __func__, this->name, status.Details.link ? "on":"off", status.Details.fullduplex ? "full":"half"); athr_gmac_set_mac_if(mac, status.Details.speed); athr_gmac_set_mac_speed(mac, status.Details.speed); athr_gmac_set_mac_duplex(mac, status.Details.fullduplex); athr_gmac_reg_wr(mac->BaseAddress, ATHR_GMAC_FIFO_CFG_3, fifo_3); athr_gmac_reg_wr(mac->BaseAddress, ATHR_GMAC_DMA_TXFIFO_THRESH ,0x01d80160); switch (status.Details.speed) { case ATHR_PHY_SPEED_1000T: AVMNET_ERR("[%s] ATHR_PHY_SPEED_1000T\n", __func__); #if defined(CONFIG_MACH_QCA955x) || defined(CONFIG_SOC_QCA955X) if (is_qca9556()) { ath_reg_wr(ETH_SGMII_ADDRESS, ETH_SGMII_GIGE_SET(1) | ETH_SGMII_CLK_SEL_SET(1)); } else { ath_reg_wr(ETH_XMII_ADDRESS, ETH_XMII_TX_INVERT_SET(1) | ETH_XMII_GIGE_SET(1)); } #elif defined(CONFIG_MACH_AR724x) || defined(CONFIG_SOC_AR724X) ath_reg_wr(ATH_ETH_XMII_CONFIG, ETH_XMII_GIGE_SET(1) | ETH_XMII_TX_DELAY_SET(1) | ETH_XMII_RX_DELAY_SET(1)); #else ath_reg_wr(ATH_ETH_XMII_CONFIG, ETH_XMII_GIGE_SET(1)); #endif break; case ATHR_PHY_SPEED_100T: AVMNET_ERR("[%s] ATHR_PHY_SPEED_100T\n", __func__); if (! status.Details.fullduplex) { athr_gmac_reg_wr(mac->BaseAddress, ATHR_GMAC_DMA_TXFIFO_THRESH ,0x00880060); athr_gmac_reg_wr(mac->BaseAddress, ATHR_GMAC_FIFO_CFG_3, 0x00f00040); } #if defined(CONFIG_MACH_QCA955x) || defined(CONFIG_SOC_QCA955X) if (is_qca9556()) { ath_reg_wr(ETH_SGMII_ADDRESS, ETH_SGMII_PHASE0_COUNT_SET(0x01)|ETH_SGMII_PHASE1_COUNT_SET(0x01)); } else { ath_reg_wr(ETH_XMII_ADDRESS, ETH_XMII_TX_INVERT_SET(1)|ETH_XMII_PHASE0_COUNT_SET(0x01)|ETH_XMII_PHASE1_COUNT_SET(0x01)); } #else ath_reg_wr(ATH_ETH_XMII_CONFIG, ETH_XMII_PHASE0_COUNT_SET(0x01)|ETH_XMII_PHASE1_COUNT_SET(0x01)); #endif break; case ATHR_PHY_SPEED_10T: AVMNET_ERR("[%s] ATHR_PHY_SPEED_10T\n", __func__); if (! status.Details.fullduplex) { athr_gmac_reg_wr(mac->BaseAddress, ATHR_GMAC_DMA_TXFIFO_THRESH ,0x00880060); athr_gmac_reg_wr(mac->BaseAddress, ATHR_GMAC_FIFO_CFG_3, 0x00f00040); } #if defined(CONFIG_MACH_QCA955x) || defined(CONFIG_SOC_QCA955X) if (is_qca9556()) { ath_reg_wr(ETH_SGMII_ADDRESS, ETH_SGMII_PHASE0_COUNT_SET(0x13)|ETH_SGMII_PHASE1_COUNT_SET(0x13)); } else { ath_reg_wr(ATH_ETH_XMII_CONFIG, ETH_XMII_TX_INVERT_SET(1)|ETH_XMII_PHASE0_COUNT_SET(0x13)|ETH_XMII_PHASE1_COUNT_SET(0x13)); } #else ath_reg_wr(ATH_ETH_XMII_CONFIG, ETH_XMII_PHASE0_COUNT_SET(0x13)|ETH_XMII_PHASE1_COUNT_SET(0x13)); #endif break; default: AVMNET_ERR("[%s] ATHR_PHY_SPEED_UNKNOWN\n", __func__); /*--- assert(0); ---*/ } } AVMNET_DEBUG("[%s]: cfg_1: %#x\n", __func__, athr_gmac_reg_rd(mac->BaseAddress, ATHR_GMAC_FIFO_CFG_1)); AVMNET_DEBUG("[%s]: cfg_2: %#x\n", __func__, athr_gmac_reg_rd(mac->BaseAddress, ATHR_GMAC_FIFO_CFG_2)); AVMNET_DEBUG("[%s]: cfg_3: %#x\n", __func__, athr_gmac_reg_rd(mac->BaseAddress, ATHR_GMAC_FIFO_CFG_3)); AVMNET_DEBUG("[%s]: cfg_4: %#x\n", __func__, athr_gmac_reg_rd(mac->BaseAddress, ATHR_GMAC_FIFO_CFG_4)); AVMNET_DEBUG("[%s]: cfg_5: %#x\n", __func__, athr_gmac_reg_rd(mac->BaseAddress, ATHR_GMAC_FIFO_CFG_5)); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void athmac_reg_busy_wait(avmnet_module_t *this) { struct athmac_context *mac = (struct athmac_context *)this->priv; int rddata; unsigned short ii = 0x1000; do { schedule(); rddata = athr_gmac_reg_rd(mac->BaseAddressMdio, ATHR_GMAC_MII_MGMT_IND) & 0x7; } while (rddata && --ii); if (ii == 0) { AVMNET_ERR("ERROR:%s:%d transaction failed\n",__func__,__LINE__); } } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ unsigned int athmac_reg_read(avmnet_module_t *this, unsigned int phyAddr, unsigned int reg) { struct athmac_context *mac = (struct athmac_context *)this->priv; unsigned int addr = (phyAddr << ATHR_GMAC_ADDR_SHIFT) | reg, val = 0; /*--- printk(KERN_ERR "[%s/%d] unit 0x%x phy_addr 0x%x reg_addr 0x%x\n", __func__, __LINE__, mac, phyAddr, reg); ---*/ athr_gmac_reg_wr(mac->BaseAddressMdio, ATHR_GMAC_MII_MGMT_ADDRESS, addr); athr_gmac_reg_wr(mac->BaseAddressMdio, ATHR_GMAC_MII_MGMT_CMD, ATHR_GMAC_MII_MGMT_CMD_READ); athmac_reg_busy_wait(this); val = athr_gmac_reg_rd(mac->BaseAddressMdio, ATHR_GMAC_MII_MGMT_STATUS); athr_gmac_reg_wr(mac->BaseAddressMdio, ATHR_GMAC_MII_MGMT_CMD, 0x0); athmac_reg_busy_wait(this); return val; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int athmac_reg_write(avmnet_module_t *this, unsigned int phyAddr, unsigned int reg, unsigned int Value) { struct athmac_context *mac = (struct athmac_context *)this->priv; unsigned short addr = (phyAddr << ATHR_GMAC_ADDR_SHIFT) | reg; /*--- AVMNET_DEBUG("[%s/%d] count %u unit 0x%x phy_addr 0x%x reg_addr 0x%x value 0x%x\n", __func__, __LINE__, mdio_count, mac, phyAddr, reg, Value); ---*/ athr_gmac_reg_wr(mac->BaseAddressMdio, ATHR_GMAC_MII_MGMT_ADDRESS, addr); athr_gmac_reg_wr(mac->BaseAddressMdio, ATHR_GMAC_MII_MGMT_CTRL, Value); athmac_reg_busy_wait(this); return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int athmac_lock(avmnet_module_t *this) { struct athmac_context *mac = (struct athmac_context *)this->priv; /*--- AVMNET_DEBUG("[%s]\n", __func__); ---*/ return down_interruptible(&mac->lock); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void athmac_unlock(avmnet_module_t *this) { struct athmac_context *mac = (struct athmac_context *)this->priv; /*--- AVMNET_DEBUG("[%s]\n", __func__); ---*/ up(&mac->lock); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void athmac_status_changed(avmnet_module_t *this, avmnet_module_t *caller) { avmnet_module_t *child = NULL; int i; /* * 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 athmac_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 athgmac_reinit(avmnet_module_t *this) { struct athmac_context *mac = (struct athmac_context *) this->priv; athr_gmac_fast_reset(mac); return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int athgmac_set_status(avmnet_module_t *this, avmnet_device_t *id, avmnet_linkstatus_t status) { struct athmac_context *mac; struct net_device *device = id->device; unsigned long flags; unsigned int setup_link = 0; AVMNET_DEBUG("[%s] %s - %s: carrier %s %sduplex\n", __func__, this->name, device->name, status.Details.link ? "on":"off", status.Details.fullduplex ? "full":"half"); mac = (struct athmac_context *) this->priv; if (id->status.Status != status.Status) { /*--- Interface nur anfassen, wenn sich der Status geändert hat ---*/ id->status = status; avmnet_links_port_update(id); if (this->reinit && status.Details.link) this->reinit(this); /*--- ruft bei Scorpion mit AR8033 bei linkup athr_gmac_fast_reset auf ---*/ setup_link = 1; } if (status.Details.link) { if( ! (this->initdata.mac.flags & AVMNET_CONFIG_FLAG_SWITCHPORT)) { if (setup_link || mac->dma_reset) { spin_lock_irqsave(&mac->spinlock, flags); athr_gmac_set_mac_from_link(this, status); /*--- set Interface ---*/ athmac_gmac_enable_hw(mac); spin_unlock_irqrestore(&mac->spinlock, flags); mac->dma_reset = 0; } } else { AVMNET_DEBUG("[%s] %s: Connected to switch, not touching interface config.\n", __func__, this->name); } if( ! netif_carrier_ok(device)){ AVMNET_DEBUG("[%s] %s: setting carrier on\n", __func__, device->name); netif_carrier_on(device); } if ( ! down_trylock(&(mac->lock))) { if (netif_queue_stopped(device) && ! mac->dma_stopped) { AVMNET_DEBUG("[%s] %s: starting tx queues\n", __func__, device->name); netif_tx_wake_all_queues(device); } up(&(mac->lock)); } } else { /*--- link down ---*/ if( ! (this->initdata.mac.flags & AVMNET_CONFIG_FLAG_SWITCHPORT)){ spin_lock_irqsave(&mac->spinlock, flags); athr_gmac_intr_disable_tx(mac->BaseAddress); /*--- disable TX ---*/ athr_gmac_reg_wr(mac->BaseAddress, ATHR_GMAC_CFG1, 0); /*--- disable HW ---*/ spin_unlock_irqrestore(&mac->spinlock, flags); }else{ AVMNET_DEBUG("[%s] %s: Connected to switch, not disabling MAC.\n", __func__, this->name); } if(netif_carrier_ok(device)) { AVMNET_DEBUG("[%s] %s: seting carrier off\n", __func__, device->name); netif_carrier_off(device); } if( ! netif_queue_stopped(device)){ AVMNET_DEBUG("[%s] %s: stopping tx queues\n", __func__, device->name); netif_tx_lock(device); netif_tx_stop_all_queues(device); netif_tx_unlock(device); } } return 0; }; /*------------------------------------------------------------------------------------------*\ * Head is the first slot with a valid buffer. Tail is the last slot replenished. * Tries to refill buffers from tail to head. \*------------------------------------------------------------------------------------------*/ static int athr_gmac_rx_replenish(avmnet_module_t *this) { struct athmac_context *mac = (struct athmac_context *)this->priv; athr_gmac_ring_t *r = &mac->mac_rxring; unsigned int head, tail, refilled = 0; athr_gmac_desc_t *ds; head = r->ring_head; tail = r->ring_tail; AVMNET_DEBUG("[%s] name %s head %d tail %d\n", __func__, this->name, head, tail); while ( tail != head ) { ds = &r->ring_desc[tail]; BUG_ON(athr_gmac_rx_owned_by_dma(ds)); ds->pskb = athr_gmac_buffer_alloc(); if (unlikely( ! ds->pskb)) { refilled = -1; AVMNET_ERR("[%s] : outta skbs!\n", __func__); break; } if (mac->mac_unit == 0) skb_reserve(ds->pskb, NET_IP_ALIGN); /* Invalidate cache should be chache aligned */ /*--- athr_mac_cache_inv((unsigned long)(bf->buf_pkt->data - 2), ATHR_GMAC_RX_BUF_SIZE + frame_sz - ATHR_GMAC_RX_RESERVE); ---*/ dma_cache_wback((unsigned long)skb_shinfo(ds->pskb), sizeof(struct skb_shared_info)); dma_cache_inv((unsigned long)(ds->pskb->data), ATHR_GMAC_RX_BUF_SIZE + frame_sz); ds->pkt_start_addr = virt_to_phys(ds->pskb->data); athr_gmac_rx_give_to_dma(ds); wmb(); refilled ++; athr_gmac_ring_incr(tail); } /* Flush descriptors */ wmb(); r->ring_tail = tail; return refilled; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static athr_gmac_rx_status_t athr_gmac_recv_packets(struct net_device *dev, avmnet_module_t *this, int quota, int *work_done) { struct athmac_context *mac = (struct athmac_context *)this->priv; unsigned short port = GMAC_MAX_ETH_DEVS; athr_gmac_ring_t *r = &mac->mac_rxring; athr_gmac_desc_t *ds; struct sk_buff *skb; athr_gmac_rx_status_t ret = ATHR_GMAC_RX_STATUS_DONE; unsigned int head, len, status, more_pkts; int start_quota = quota; status = athr_gmac_reg_rd(mac->BaseAddress, ATHR_GMAC_DMA_RX_STATUS); /*--- AVMNET_DEBUG("[%s] %s status 0x%x quota %d\n", __func__, this->name, status, quota); ---*/ if (unlikely(mac->dma_reset)) { if (mac->dma_reset & 1) mac->dma_reset &= ~1; } else { if (unlikely((status & ATHR_GMAC_RX_STATUS_PKT_RCVD) == 0)) { /*--- AVMNET_ERR("[%s] exit ATHR_GMAC_RX_STATUS_PKT_RCVD == 0\n", __FUNCTION__); ---*/ goto done; } } /*--- Flush the DDR FIFOs for gmac ---*/ /*--- ath_flush_ge(mac->mac_unit); ---*/ process_pkts: while (quota) { head = r->ring_head; ds = &r->ring_desc[head]; if (athr_gmac_rx_owned_by_dma(ds)) { break; } athr_gmac_intr_ack_rx(mac->BaseAddress); skb = ds->pskb; ds->pskb = NULL; BUG_ON( ! skb); athr_gmac_ring_incr(head); r->ring_head = head; len = ds->pkt_size; quota--; if (skb->data != phys_to_virt(ds->pkt_start_addr)) { AVMNET_ERR("[%s] Received buffer modified 0x%p 0x%p\n", __func__, skb->data, phys_to_virt(ds->pkt_start_addr)); dev_kfree_skb_any(skb); continue; } skb_put(skb, len - ETHERNET_FCS_SIZE); /* * Data corruption sometimes with large buffer size of 4k.Cached region * is not synced with non cached region putting temporary fix for * invalidating cache contents Need to find the root cause. */ dma_cache_inv((unsigned long)(skb->data), skb->len); /*------------------------------------------------------------------------------------------*\ * hier die VLANs auseinanderpflücken und in die Daten ins richtige Device stecken \*------------------------------------------------------------------------------------------*/ skb_reset_mac_header(skb); switch(mac->mac_unit) { case 1: { p_athr_special_header_t header; /* * find out which switch port (SW MAC 1-5) this frame was received on * and then remove the special header */ header = (p_athr_special_header_t)(skb->data + 12); if (likely(header->version == 2) && (header->type == 0)) { port = header->portnum; memmove(skb->data + 2, skb->data, 12); skb_pull(skb, 2); } if (unlikely((port < GMAC_MAX_ETH_DEVS) && ( ! port))) { AVMNET_ERR("[%s] Received illegal Frame on %s version %d priority %d type %d portnum %d\n", __func__, this->name, header->version, header->priority, header->type, header->portnum); dev_kfree_skb_any(skb); continue; } } break; case 0: // Phy on GMAC0 is always port 0 port = 0; break; default: panic("illegal mac unit\n"); } /* * complain loudly and abort if avm_device is not in lookup table. */ if(unlikely(mac->devices_lookup[port] == NULL)){ AVMNET_ERR("[%s] Received Frame from illegal port %d on %s\n", __func__, port, this->name); dev_kfree_skb_any(skb); continue; } dev = mac->devices_lookup[port]->device; dev->stats.rx_packets++; dev->stats.rx_bytes += skb->len; /*--- AVMNET_DEBUG("[%s] dev %s\n", __func__, dev->name); ---*/ skb->dev = dev; #if defined(CONFIG_IP_MULTICAST_FASTFORWARD) { avmnet_netdev_priv_t *priv = netdev_priv(dev); (void) mcfw_snoop_recv(&priv->mcfw_netdrv, 0, skb); } #endif /*--- #if defined(CONFIG_IP_MULTICAST_FASTFORWARD) ---*/ skb->protocol = eth_type_trans(skb, dev); dev->last_rx = jiffies; #ifdef CONFIG_ATHRS_HW_NAT athr_nat_process_ingress_pkts(mac->mac_unit, skb, ds); #endif #if defined(CONFIG_AVM_PA) if(unlikely(avm_pa_dev_receive(AVM_PA_DEVINFO(dev), skb) != AVM_PA_RX_ACCELERATED)) { netif_receive_skb(skb); } #else netif_receive_skb(skb); #endif /*--- #if defined(CONFIG_AVM_PA) ---*/ } if (start_quota == quota) { printk(KERN_ERR "{%s} %d\n", __func__, quota); } if (unlikely(athr_gmac_rx_replenish(this) < 0)) { *work_done = 0; return ATHR_GMAC_RX_STATUS_OOM; /*--- bei OOM muss work_done == 0 sein ! ---*/ } /* * let's see what changed while we were slogging. * ack Rx in the loop above is no flush version. It will get flushed now. */ status = athr_gmac_reg_rd(mac->BaseAddress, ATHR_GMAC_DMA_RX_STATUS); more_pkts = (status & ATHR_GMAC_RX_STATUS_PKT_RCVD); /*--- AVMNET_DEBUG("[%s] status 0x%x quota %d\n", __func__, status, quota); ---*/ if (!more_pkts) goto done; /*--- more pkts arrived; if we have quota left, get rolling again ---*/ if (quota) goto process_pkts; /*--- out of quota ---*/ /*--- AVMNET_DEBUG("[%s] status 0x%x more_pkts 0x%x quota %d RXE %d\n", __func__, status, more_pkts, quota, athr_gmac_reg_rd(mac->BaseAddress, ATHR_GMAC_DMA_RX_CTRL)); ---*/ ret = ATHR_GMAC_RX_STATUS_NOT_DONE; done: *work_done = (start_quota - quota); athr_gmac_rx_start(mac->BaseAddress); return ret; } /*------------------------------------------------------------------------------------------*\ * Interrupt handling: * - Recv NAPI style (refer to Documentation/networking/NAPI) * * 2 Rx interrupts: RX and Overflow (OVF). * - If we get RX and/or OVF, schedule a poll. Turn off _both_ interurpts. * * - When our poll's called, we * a) Have one or more packets to process and replenish * b) The hardware may have stopped because of an OVF. * * - We process and replenish as much as we can. For every rcvd pkt * indicated up the stack, the head moves. For every such slot that we * replenish with an skb, the tail moves. If head catches up with the tail * we're OOM. When all's done, we consider where we're at: * * if no OOM: * - if we're out of quota, let the ints be disabled and poll scheduled. * - If we've processed everything, enable ints and cancel poll. * * If OOM: * - Start a timer. Cancel poll. Ints still disabled. * If the hardware's stopped, no point in restarting yet. * * Note that in general, whether we're OOM or not, we still try to * indicate everything recvd, up. * * Locking: * The interrupt doesnt touch the ring => Rx is lockless \*------------------------------------------------------------------------------------------*/ static irqreturn_t athr_gmac_intr(int irq __attribute__((unused)), void *dev) { avmnet_module_t *this = (avmnet_module_t *)dev; struct athmac_context *mac = (struct athmac_context *)this->priv; int isr, imr, handled = 0; isr = athr_gmac_get_isr(mac->BaseAddress); imr = athr_gmac_get_msr(mac->BaseAddress); AVMNET_DEBUG("[%s] %s isr 0x%x imr 0x%x\n", __func__, this->name, isr, imr); /*--- assert(isr == (isr & imr)); ---*/ if (likely(isr & ATHR_GMAC_INTR_RX)) { handled = 1; athr_gmac_intr_disable_recv(mac->BaseAddress); if (likely(napi_schedule_prep(&mac->napi))) { #if AVMNET_DEBUG_LEVEL & AVMNET_DEBUG_LEVEL_DEBUG int status = athr_gmac_reg_rd(mac->BaseAddress, ATHR_GMAC_DMA_RX_STATUS); BUG_ON(!(status & ATHR_GMAC_RX_STATUS_PKT_RCVD)); BUG_ON(!(status >> 16)); #endif __napi_schedule(&mac->napi); /*--- napi_schedule ohne Prüfung ---*/ } else { AVMNET_ERR("[%s]: %s driver bug! interrupt while in poll isr 0x%x imr 0x%x\n", __func__, this->name, isr, imr); /*--- AVMNET_ERR("[%s]: epc = %pF athr_rx_disable_count %ld\n", this->name, (void *)read_c0_epc(), atomic_read(&athr_rx_disable_count)); ---*/ } } if (unlikely(isr & ATHR_GMAC_INTR_RX_OVF)) { handled = 1; /*--- AVMNET_DEBUG("[%s] RX_OVERRUN\n", __FUNCTION__); ---*/ athr_gmac_intr_ack_rxovf(mac->BaseAddress); } if (likely(isr & ATHR_GMAC_INTR_TX)) { handled = 1; /*--- AVMNET_DEBUG("[%s] ATHR_GMAC_INTR_TX\n", __func__); ---*/ athr_gmac_intr_disable_tx(mac->BaseAddress); tasklet_schedule(&mac->txreaptq); } if (unlikely(isr & ATHR_GMAC_INTR_RX_BUS_ERROR)) { handled = 1; AVMNET_ERR("[%s] ATHR_GMAC_INTR_RX_BUS_ERROR\n", __func__); athr_gmac_intr_ack_rxbe(mac->BaseAddress); } if (unlikely(isr & ATHR_GMAC_INTR_TX_BUS_ERROR)) { handled = 1; AVMNET_ERR("[%s] ATHR_GMAC_INTR_TX_BUS_ERROR\n", __func__); athr_gmac_intr_ack_txbe(mac->BaseAddress); } if (unlikely( ! handled)) { AVMNET_ERR("[%s]: unhandled intr isr %#x\n", __func__, isr); BUG_ON(1); } return IRQ_HANDLED; } /*------------------------------------------------------------------------------------------*\ * Error timers \*------------------------------------------------------------------------------------------*/ static void athr_gmac_oom_timer(unsigned long data) { avmnet_module_t *this = (avmnet_module_t *)data; struct athmac_context *mac = (struct athmac_context *)this->priv; int count = athr_gmac_rx_replenish(this); AVMNET_ERR("[%s] %s count %d\n", __func__, this->name, count); if (count < 0) { /*--- OOM ---*/ int val = mod_timer(&mac->mac_oom_timer, jiffies + 2000); BUG_ON(val); } else { athr_gmac_rx_start(mac->BaseAddress); napi_reschedule(&mac->napi); } } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void athmac_gmac_setup_mii(struct athmac_context *mac) { unsigned int mgmt_cfg_val = 0; #if ! defined(CONFIG_MACH_QCA955x) && ! defined(CONFIG_SOC_QCA955X) unsigned int sw_only; #endif switch (mac->mac_unit) { case 0: #if defined(CONFIG_MACH_QCA955x) || defined(CONFIG_SOC_QCA955X) if (is_qca9556()) { ath_reg_wr(ATHR_GMAC_ETH_CFG, ATHR_GMAC_ETH_CFG_SGMII_GE0); } else { /*--- External RMII Mode on GE0,Master and High speed ---*/ AVMNET_INFO("set External RMII Mode on GE0\n"); ath_reg_wr(ATHR_GMAC_ETH_CFG, ATHR_GMAC_ETH_CFG_RGMII_EN); } #else /*--- External RMII Mode on GE0,Master and High speed ---*/ AVMNET_INFO("set External RMII Mode on GE0\n"); // make sure we keep WAN PHY attachment to switch, if set sw_only = ath_reg_rd(ATHR_GMAC_ETH_CFG) & ATHR_GMAC_ETH_CFG_SW_ONLY_MODE; ath_reg_wr(ATHR_GMAC_ETH_CFG, ATHR_GMAC_ETH_CFG_RGMII_EN | sw_only); #endif break; case 1: #if defined(CONFIG_MACH_QCA955x) || defined(CONFIG_SOC_QCA955X) if (is_qca9558()) { AVMNET_INFO("set External SGMII Mode on GE1\n"); ath_reg_wr(ATHR_GMAC_ETH_CFG, ATHR_GMAC_ETH_CFG_RGMII_EN); } #else // if configured, connect WAN port PHY to switch MAC 5 if(mac->this_module->initdata.mac.flags & AVMNET_CONFIG_FLAG_NO_WANPORT){ ath_reg_rmw_set(ATHR_GMAC_ETH_CFG, ATHR_GMAC_ETH_CFG_SW_ONLY_MODE); } #endif break; } #if ! defined(CONFIG_MACH_AR724x) && ! defined(CONFIG_SOC_AR724X) if (soc_is_ar934x() || soc_is_qca955x() || soc_is_qca9531()) { ath_reg_rmw_set(SWITCH_CLOCK_SPARE_ADDRESS, (1 << 6)); /*--- MDIO_CLK = 100MHz ---*/ } #endif if (soc_is_qca955x()) { mgmt_cfg_val = 0x7; } else { mgmt_cfg_val = 0x6; } athr_gmac_reg_wr(mac->BaseAddressMdio, ATHR_GMAC_MII_MGMT_CFG, mgmt_cfg_val | (1 << 31)); athr_gmac_reg_wr(mac->BaseAddressMdio, ATHR_GMAC_MII_MGMT_CFG, mgmt_cfg_val); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static int athr_gmac_napi_poll(struct napi_struct *napi, int budget) { struct athmac_context *mac = (struct athmac_context *)container_of(napi, struct athmac_context, napi); avmnet_module_t *this = mac->this_module; struct net_device *dev = mac->device; unsigned long flags; int work_done = 0; athr_gmac_rx_status_t ret; ret = athr_gmac_recv_packets(dev, this, budget, &work_done); /*--- AVMNET_INFO("[%s] device %s ret 0x%x\n", __func__, dev->name, ret); ---*/ if (likely(ret == ATHR_GMAC_RX_STATUS_DONE) && (work_done < budget)) { napi_complete(&mac->napi); spin_lock_irqsave(&mac->spinlock, flags); athr_gmac_intr_enable_recv(mac->BaseAddress); spin_unlock_irqrestore(&mac->spinlock, flags); } else if (ret == ATHR_GMAC_RX_STATUS_OOM) { printk("[%s] athr_gmac_recv_packets: ATHR_GMAC_RX_STATUS_OOM\n", __func__); AVMNET_INFO("[%s] oom..?\n", __func__); athr_gmac_rx_stop(mac->BaseAddress); /*--- disable RX ---*/ napi_complete(&mac->napi); mod_timer(&mac->mac_oom_timer, jiffies + 2000); } return work_done; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void athr_gmac_setup_eth( struct net_device* device) { ether_setup(device); /*--- setup eth-defaults ---*/ device->hard_header_len += 48; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int avmnet_set_macaddr(int device_nr, struct net_device *dev); void athr_gmac_setup_eth_priv(avmnet_device_t *avm_dev) { avmnet_netdev_priv_t *priv; struct net_device *device = avm_dev->device; AVMNET_INFO("[%s] devicename %s module %s\n", __func__, device->name, avm_dev->mac_module->name); priv = netdev_priv( device ); if ( priv ){ priv->avm_dev = avm_dev; } else { AVMNET_ERR("[%s] ERROR: priv = %p avm_dev = %p\n", __func__, priv, avm_dev); return; } if ( ! strncmp("eth0", avm_dev->device_name, sizeof("eth0")-1)) { if (avmnet_set_macaddr(0, device)) { AVMNET_ERR("[%s] ERROR: %s set mac-Address\n" , __func__, avm_dev->device_name); BUG_ON(1); } { /* set the mac addr - Stichwort Pauseframes */ avmnet_module_t *this = (avmnet_module_t *)avm_dev->mac_module; struct athmac_context *mac = (struct athmac_context *)this->priv; unsigned int w1 = 0, w2 = 0; addr_to_words(device->dev_addr, w1, w2); athr_gmac_reg_wr(mac->BaseAddress, ATHR_GMAC_GE_MAC_ADDR1, w1); athr_gmac_reg_wr(mac->BaseAddress, ATHR_GMAC_GE_MAC_ADDR2, w2); } } if ( ! strncmp("eth1", avm_dev->device_name, sizeof("eth1")-1)) { if (avmnet_set_macaddr(1, device)) { AVMNET_ERR("[%s] ERROR: %s set mac-Address\n" , __func__, avm_dev->device_name); BUG_ON(1); } { /* set the mac addr - Stichwort Pauseframes */ avmnet_module_t *this = (avmnet_module_t *)avm_dev->mac_module; struct athmac_context *mac = (struct athmac_context *)this->priv; unsigned int w1 = 0, w2 = 0; addr_to_words(device->dev_addr, w1, w2); athr_gmac_reg_wr(mac->BaseAddress, ATHR_GMAC_GE_MAC_ADDR1, w1); athr_gmac_reg_wr(mac->BaseAddress, ATHR_GMAC_GE_MAC_ADDR2, w2); } } if ( ! strncmp("eth2", avm_dev->device_name, sizeof("eth2")-1)) { if (avmnet_set_macaddr(2, device)) { AVMNET_ERR("[%s] ERROR: %s set mac-Address\n" , __func__, avm_dev->device_name); BUG_ON(1); } } if ( ! strncmp("eth3", avm_dev->device_name, sizeof("eth3")-1)) { if (avmnet_set_macaddr(3, device)) { AVMNET_ERR("[%s] ERROR: %s set mac-Address\n" , __func__, avm_dev->device_name); BUG_ON(1); } } if ( ! strncmp("plc", avm_dev->device_name, sizeof("plc")-1)) { if (avmnet_set_macaddr(2, device)) { AVMNET_ERR("[%s] ERROR: %s set mac-Address\n" , __func__, avm_dev->device_name); BUG_ON(1); } } } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int athmac_gmac_setup(avmnet_module_t *this) { struct athmac_context *mac = (struct athmac_context *)this->priv; avmnet_device_t *avm_dev; int i, result = -1; AVMNET_TRC("[%s] Setup on module %s called.\n", __func__, this->name); /*--- reset HW ---*/ athr_gmac_reg_rmw_set(mac->BaseAddress, ATHR_GMAC_CFG1, ATHR_GMAC_CFG1_SOFT_RST | ATHR_GMAC_CFG1_RX_RST | ATHR_GMAC_CFG1_TX_RST); #if defined(ATH_FLAGS) if (mac_has_flag(mac,WAN_QOS_SOFT_CLASS)) mac->mac_noacs = 4; else #endif mac->mac_noacs = 1; /*--- mac->tx_ac_bt = 0; ---*/ /*--- mac->tx_ac_drops = 0; ---*/ if (athr_gmac_tx_alloc(mac)) goto tx_failed; if (athr_gmac_rx_alloc(mac)) goto rx_failed; athmac_gmac_setup_mii(mac); /*--- athr_init_hwaccels(mac); ---*/ /*------------------------------------------------------------------------------------------*\ * muss auf jeden Fall vor dem Setup der PHYs aufgerufen werden!!! Atheos-Phy latchen die Config * beim Reset an den MII Pins, das Interface muss als am Prozessor schon initialisiert sein \*------------------------------------------------------------------------------------------*/ athmac_gmac_setup_hw(mac); if (this->initdata.mac.flags & AVMNET_CONFIG_FLAG_SWITCHPORT) athmac_gmac_enable_hw(mac); register_netdev( mac->device ); netif_napi_add(mac->device , &mac->napi, athr_gmac_napi_poll, ATHR_GMAC_NAPI_WEIGHT); netif_carrier_off(mac->device); netif_stop_queue(mac->device); napi_enable(&mac->napi); if (this->initdata.mac.flags & AVMNET_CONFIG_FLAG_IRQ) { result = request_irq(mac->irq, athr_gmac_intr, IRQF_DISABLED, this->name, this); if(result < 0) { AVMNET_ERR("[%s] \n", __func__, this->name, mac->irq); return -EINTR; } } // find avmnet devices associated with this MAC unit and put them in its lookup table for(i = 0; i < GMAC_MAX_ETH_DEVS; ++i){ avm_dev = find_avmnet_device(i); if(avm_dev && avm_dev->mac_module == this){ mac->devices_lookup[i] = avm_dev; } } 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); athmac_setup_irq(this, 1); athr_gmac_rx_start(mac->BaseAddress); return 0; rx_failed: athr_gmac_tx_free(mac); tx_failed: return 1; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int gmac_open(struct net_device *device __attribute__ ((unused))) { AVMNET_TRC("[%s] %s\n", __func__, ((struct athmac_context *)netdev_priv(device))->this_module->name); return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int gmac_stop(struct net_device *device __attribute__ ((unused))) { AVMNET_TRC("[%s] %s\n", __func__, ((struct athmac_context *)netdev_priv(device))->this_module->name); return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void gmac_tx_timeout(struct net_device *device) { avmnet_module_t *this = ((struct athmac_context *)netdev_priv(device))->this_module; AVMNET_WARN("[%s] %s\n", __func__, this->name); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int gmac_hard_start(struct sk_buff *skb, struct net_device *device __attribute__ ((unused))) { AVMNET_ERR("[%s] %s\n", __func__, ((struct athmac_context *)netdev_priv(device))->this_module->name); dev_kfree_skb_any(skb); return 0; } struct net_device_ops athmac_device_ops = { .ndo_open = gmac_open, .ndo_stop = gmac_stop, .ndo_tx_timeout = gmac_tx_timeout, .ndo_start_xmit = gmac_hard_start }; /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int athmac_gmac_init(avmnet_module_t *this) { struct net_device *device; struct athmac_context *mac; int i, result; AVMNET_INFOTRC("[%s] '%s'\n", __func__, this->name); device = alloc_netdev( sizeof(struct athmac_context), this->name, athr_gmac_setup_eth ); if(!device) { AVMNET_ERR("[%s] Could not allocate net_device %s\n", __func__, this->name); return -1; } device->netdev_ops = &athmac_device_ops; this->priv = netdev_priv( device ); mac = (struct athmac_context *)this->priv; mac->device = device; mac->this_module = this; mac->mac_unit = this->initdata.mac.mac_nr; if (this->initdata.mac.flags & AVMNET_CONFIG_FLAG_BASEADDR) { mac->BaseAddress = this->initdata.mac.base_addr; } if (this->initdata.mac.flags & AVMNET_CONFIG_FLAG_BASEADDR_MDIO) { mac->BaseAddressMdio = this->initdata.mac.base_mdio; } else { mac->BaseAddressMdio = mac->BaseAddress; } if (this->initdata.mac.flags & AVMNET_CONFIG_FLAG_RESET) { mac->reset = this->initdata.mac.reset; /*--- Resourcen anmelden ---*/ ath_gmac_gpio.start = mac->reset; ath_gmac_gpio.end = mac->reset; ath_gmac_gpio.name = this->name; if(request_resource(&gpio_resource, &ath_gmac_gpio)) { AVMNET_ERR("[%s] ERROR request resource gpio %d\n", __func__, mac->reset); kfree(mac); return -EIO; } } if (this->initdata.mac.flags & AVMNET_CONFIG_FLAG_IRQ) { mac->irq = this->initdata.mac.irq; } mac->num_tx_desc = athr_tx_desc_num[mac->mac_unit]; mac->num_rx_desc = athr_rx_desc_num[mac->mac_unit]; mac->reap_thresh = 16; mac->qstart_thresh = 5 * tx_max_desc_per_ds_pkt; spin_lock_init(&mac->spinlock); sema_init(&mac->lock, 1); init_timer(&mac->mac_oom_timer); mac->mac_oom_timer.data = (unsigned long)this; mac->mac_oom_timer.function = (void *)athr_gmac_oom_timer; tasklet_init(&mac->txreaptq, athr_gmac_txreap_task, (unsigned long)this); 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_gmac_exit(avmnet_module_t *this) { struct athmac_context *mac = (struct athmac_context *)this->priv; struct net_device *device = mac->device; int i, result; AVMNET_TRC("[%s] Exit 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 } } athr_gmac_rx_stop(mac->BaseAddress); athr_gmac_tx_stop(mac->BaseAddress); athmac_setup_irq(this, 0); netif_napi_del(&mac->napi); unregister_netdev(device); athr_gmac_tx_free(mac); athr_gmac_rx_free(mac); free_irq(mac->irq, athr_gmac_intr); free_netdev(device); /*--- kein device kein mac ! ---*/ return 0; }