ehea: interface to network stack
Subsystems (2)
PATCH 1/6 ehea: interface to network stack
Signed-off-by Jan-Bernd Themann <[email protected]>
drivers/net/ehea/ehea_main.c | 2557 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2557 insertions(+)
drivers/net/ehea/ehea_main.c +2557 -0
@@ -0,0 +1,2557 @@ +/* +* linux/drivers/net/ehea/ehea_main.c +* +* eHEA ethernet device driver for IBM eServer System p +* +* (C) Copyright IBM Corp. 2006 +* +* Authors: +* Christoph Raisch <[email protected]> +* Jan-Bernd Themann <[email protected]> +* Heiko-Joerg Schick <[email protected]> +* Thomas Klein <[email protected]> +* +* +* 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; either version 2, or (at your option) +* any later version. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#define DEB_PREFIX "main" + +#include <linux/in.h> +#include <linux/ip.h> +#include <linux/tcp.h> +#include <linux/udp.h> +#include <linux/if.h> +#include <linux/list.h> +#include <net/ip.h> + +#include "ehea.h" +#include "ehea_qmr.h" +#include "ehea_phyp.h" + +struct task_struct *ehea_irq_helper_thread; +extern int ehea_irq_helper (void ); + + +#define USE_INTERRUPTS +#define POLL_PORT_INTERVAL (HZ / 20) +#define SKB_CLEANUP_INTERVAL (HZ / 1000) + +MODULE_LICENSE ("GPL" ); +MODULE_AUTHOR ("Christoph Raisch <[email protected]>" ); +MODULE_DESCRIPTION ("IBM eServer HEA Driver" ); +MODULE_VERSION (EHEA_DRIVER_VERSION ); + +static int __devinit ehea_probe (struct ibmebus_dev *dev, +const struct of_device_id *id); +static int __devexit ehea_remove(struct ibmebus_dev *dev); +static int ehea_sense_port_attr (struct ehea_adapter *adapter,int portnum); + +int ehea_trace_level =5 ; + +static struct net_device_stats *ehea_get_stats (struct net_device *dev) +{ +u64 hret; +struct ehea_port *port = (struct ehea_port *)dev->priv; +struct ehea_adapter *adapter = port->adapter; +struct hcp_query_ehea_port_cb_2 *cb2 =NULL ; +struct net_device_stats *stats = &port->stats; + +EDEB_EN (7 ,"net_device=%p" , dev); + + cb2 =kzalloc (H_CB_ALIGNMENT ,GFP_KERNEL ); +if (!cb2) { +EDEB_ERR (4 ,"No memory for cb2" ); + goto get_stat_exit; + } + + hret =ehea_h_query_ehea_port (adapter->handle, + port->logical_port_id, +H_PORT_CB2 , +H_PORT_CB2_ALL , + cb2); + +if (hret !=H_SUCCESS ) { +EDEB_ERR (4 ,"query_ehea_port failed for cb2" ); + goto get_stat_exit; + } + +EDEB_DMP (7 , (u8 *)cb2, +sizeof (struct hcp_query_ehea_port_cb_2 ),"After HCALL" ); + + stats->tx_packets = cb2->txucp + cb2->txmcp + cb2->txbcp; + stats->multicast = cb2->rxmcp; + stats->rx_errors = cb2->rxuerr; + stats->rx_bytes = cb2->rxo; + stats->tx_bytes = cb2->txo; + +get_stat_exit: +EDEB_EX (7 ,"" ); +return stats; +} + +static inline int ehea_refill_rq1 (struct ehea_port_res *port_res,int arr_index, +int nr_of_wqes) +{ +int i; +int ret =0 ; +struct ehea_qp *qp; +int skb_arr_rq1_len = port_res->skb_arr_rq1_len; +struct sk_buff **skb_arr_rq1 = port_res->skb_arr_rq1; +EDEB_EN (7 ,"port_res=%p, arr_index=%d, nr_of_wqes=%d, arr_rq1_len=%d" , + port_res, arr_index, nr_of_wqes, skb_arr_rq1_len); + + qp = port_res->qp; +if (unlikely (nr_of_wqes ==0 )) +return -EINVAL ; +for (i =0 ; i < nr_of_wqes; i++) { +int index = ((skb_arr_rq1_len + arr_index) - i) + % skb_arr_rq1_len; +if (!skb_arr_rq1[index]) { + skb_arr_rq1[index] =dev_alloc_skb (EHEA_LL_PKT_SIZE ); + +if (!skb_arr_rq1[index]) { +EDEB_ERR (4 ,"No mem for skb/%d wqes filled" , i); + ret = -ENOMEM ; +break ; + } + } + } +/* Ring doorbell */ +ehea_update_rq1a (qp, nr_of_wqes); +EDEB_EX (7 ,"" ); +return ret; +} + +static int ehea_init_fill_rq1 (struct ehea_port_res *port_res,int nr_rq1a) +{ +int i; +int ret =0 ; +struct ehea_qp *qp; +EDEB_EN (7 ,"port_res=%p, nr_rq1a=%d" , port_res, nr_rq1a); + qp = port_res->qp; + +for (i =0 ; i < port_res->skb_arr_rq1_len; i++) { + port_res->skb_arr_rq1[i] =dev_alloc_skb (EHEA_LL_PKT_SIZE ); +if (!port_res->skb_arr_rq1[i]) { +EDEB_ERR (4 ,"dev_alloc_skb failed. Only %d skb filled." , + i); + ret = -ENOMEM ; +break ; + } + } +/* Ring doorbell */ +ehea_update_rq1a (qp, nr_rq1a); +EDEB_EX (7 ,"" ); +return ret; +} + +static inline int ehea_refill_rq2 (struct ehea_port_res *pr,int nr_of_wqes) +{ +int i; +int ret =0 ; +struct ehea_qp *qp; +struct ehea_rwqe *rwqe; +int skb_arr_rq2_len = pr->skb_arr_rq2_len; +struct sk_buff **skb_arr_rq2 = pr->skb_arr_rq2; + +EDEB_EN (8 ,"pr=%p, nr_of_wqes=%d" , pr, nr_of_wqes); +if (nr_of_wqes ==0 ) +return -EINVAL ; + qp = pr->qp; +for (i =0 ; i < nr_of_wqes; i++) { +int index = pr->skb_rq2_index++; +struct sk_buff *skb =dev_alloc_skb (EHEA_PAGESIZE ); + +if (!skb) { +EDEB_ERR (4 ,"dev_alloc_ask failed. Only %d skb filled." , + i); + ret = -ENOMEM ; +break ; + } + + rwqe =ehea_get_next_rwqe (qp,2 ); + pr->skb_rq2_index %= skb_arr_rq2_len; + skb_arr_rq2[index] = skb; + rwqe->wr_id =EHEA_BMASK_SET (EHEA_WR_ID_TYPE ,EHEA_RWQE2_TYPE ) + |EHEA_BMASK_SET (EHEA_WR_ID_INDEX , index); + rwqe->sg_list[0 ].l_key =ehea_get_lkey (pr->port->adapter, + (void *)skb->data); + rwqe->sg_list[0 ].vaddr = (u64 )skb->data; + rwqe->sg_list[0 ].len =EHEA_PAGESIZE ; + rwqe->data_segments =1 ; + } + +/* Ring doorbell */ +iosync (); +ehea_update_rq2a (qp, i); +EDEB_EX (8 ,"" ); +return ret; +} + +#define EHEA_CACHE_LINE_ADD 256 + +static inline int ehea_refill_rq3 (struct ehea_port_res *pr,int nr_of_wqes) +{ +int i; +int ret =0 ; +struct ehea_qp *qp; +struct ehea_rwqe *rwqe; +int skb_arr_rq3_len = pr->skb_arr_rq3_len; +struct sk_buff **skb_arr_rq3 = pr->skb_arr_rq3; +EDEB_EN (8 ,"pr=%p, nr_of_wqes=%d" , pr, nr_of_wqes); +if (nr_of_wqes ==0 ) +return -EINVAL ; + qp = pr->qp; +for (i =0 ; i < nr_of_wqes; i++) { +int index = pr->skb_rq3_index++; +struct sk_buff *skb =dev_alloc_skb (EHEA_MAX_PACKET_SIZE + +EHEA_CACHE_LINE_ADD ); + +if (!skb) { +EDEB_ERR (4 ,"No memory for skb. Only %d rwqe filled." , + i); + ret = -ENOMEM ; +break ; + } + + rwqe =ehea_get_next_rwqe (qp,3 ); + pr->skb_rq3_index %= skb_arr_rq3_len; + skb_arr_rq3[index] = skb; + rwqe->wr_id =EHEA_BMASK_SET (EHEA_WR_ID_TYPE ,EHEA_RWQE3_TYPE ) + |EHEA_BMASK_SET (EHEA_WR_ID_INDEX , index); + rwqe->sg_list[0 ].l_key =ehea_get_lkey (pr->port->adapter, + (void *)skb->data); + rwqe->sg_list[0 ].vaddr = (u64 )skb->data; + rwqe->sg_list[0 ].len =EHEA_MAX_PACKET_SIZE + +EHEA_CACHE_LINE_ADD ; + rwqe->data_segments =1 ; + } + +/* Ring doorbell */ +iosync (); +ehea_update_rq3a (qp, i); +EDEB_EX (8 ,"" ); +return ret; +} + +static inline int ehea_check_cqe (struct ehea_cqe *cqe,int *rq_num) +{ + *rq_num = (cqe->type &EHEA_CQE_TYPE_RQ ) >>5 ; +EDEB (7 ,"RQ used=%d, status=%X" , *rq_num, cqe->status); +if ((cqe->status &EHEA_CQE_STAT_ERR_MASK ) ==0 ) +return 0 ; +if (((cqe->status &EHEA_CQE_STAT_ERR_TCP ) !=0 ) + && (cqe->header_length ==0 )) +return 0 ; +else +printk ("WARNING: Packet discarded. Wrong TCP/UDP chksum" +"and header_length != 0. cqe->status=%X" , cqe->status); + +return -EINVAL ; +} + +static inline void ehea_fill_skb_ll (struct net_device *dev, +struct sk_buff *skb,struct ehea_cqe *cqe) +{ +int length = cqe->num_bytes_transfered -4 ;/*remove CRC */ +EDEB_EN (7 ,"dev=%p, skb=%p, cqe=%p" , dev, skb, cqe); +memcpy (skb->data, ((char *)cqe) +64 , length); +skb_put (skb, length); + skb->dev = dev; + skb->ip_summed =CHECKSUM_UNNECESSARY ; + skb->protocol =eth_type_trans (skb, dev); +EDEB_EX (7 ,"" ); +} + +static inline void ehea_fill_skb (struct net_device *dev, +struct sk_buff *skb,struct ehea_cqe *cqe) +{ +int length = cqe->num_bytes_transfered -4 ;/*remove CRC */ +EDEB_EN (7 ,"dev=%p, skb=%p, cqe=%p" , dev, skb, cqe); +skb_put (skb, length); + skb->dev = dev; + skb->ip_summed =CHECKSUM_UNNECESSARY ; + skb->protocol =eth_type_trans (skb, dev); +EDEB_EX (7 ,"" ); +} + +#define EHEA_MAX_RWQE 1000 + +static int ehea_poll (struct net_device *dev,int *budget) +{ +struct ehea_port *port = (struct ehea_port *)dev->priv; +struct ehea_port_res *port_res = &port->port_res[0 ]; +struct ehea_cqe *cqe; +struct ehea_qp *qp = port_res->qp; +int wqe_index =0 ; +int last_wqe_index =0 ; +int x =0 ; +int processed =0 ; +int processed_RQ1 =0 ; +int processed_RQ2 =0 ; +int processed_RQ3 =0 ; +int rq; +int intreq; +struct sk_buff **skb_arr_rq1 = port_res->skb_arr_rq1; +struct sk_buff **skb_arr_rq2 = port_res->skb_arr_rq2; +struct sk_buff **skb_arr_rq3 = port_res->skb_arr_rq3; +int skb_arr_rq1_len = port_res->skb_arr_rq1_len; +int my_quota =min (*budget, dev->quota); + +EDEB_EN (7 ,"dev=%p, port_res=%p, budget=%d, quota=%d, qp_nr=%x" , + dev, port_res, *budget, dev->quota, + port_res->qp->init_attr.qp_nr); + my_quota =min (my_quota,EHEA_MAX_RWQE ); + +/* rq0 is low latency RQ */ + cqe =ehea_poll_rq1 (qp, &wqe_index); +while ((my_quota >0 ) && cqe) { +ehea_inc_rq1 (qp); + processed_RQ1++; + processed++; + my_quota--; + +EDEB_DMP (6 , (u8 *)cqe,4 *16 ,"CQE" ); + last_wqe_index = wqe_index; +if (!ehea_check_cqe (cqe, &rq)) { +struct sk_buff *skb; +if (rq ==1 ) {/* LL RQ1 */ +void *pref; + + x = (wqe_index +1 ) % skb_arr_rq1_len; + pref = (void *)skb_arr_rq1[x]; +prefetchw (pref); +prefetchw (pref +EHEA_CACHE_LINE ); + + x = (wqe_index +1 ) % skb_arr_rq1_len; + pref = (void *)(skb_arr_rq1[x]->data); +prefetchw (pref); +prefetchw (pref +EHEA_CACHE_LINE ); + + skb = skb_arr_rq1[wqe_index]; +if (unlikely (!skb)) { +EDEB_ERR (4 ,"LL SBK=NULL, wqe_index=%d" , + wqe_index); + skb =dev_alloc_skb (EHEA_LL_PKT_SIZE ); +if (!skb) +panic ("Alloc SKB failed" ); + } + skb_arr_rq1[wqe_index] =NULL ; +ehea_fill_skb_ll (dev, skb, cqe); + }else if (rq ==2 ) {/* RQ2 */ +void *pref; +int skb_index =EHEA_BMASK_GET (EHEA_WR_ID_INDEX , + cqe->wr_id); + x = (skb_index +1 ) % port_res->skb_arr_rq2_len; + pref = (void *)skb_arr_rq2[x]; +prefetchw (pref); +prefetchw (pref +EHEA_CACHE_LINE ); + + x = (skb_index +1 ) % port_res->skb_arr_rq2_len; + pref = (void *)(skb_arr_rq2[x]->data); + +prefetch (pref); +prefetch (pref +EHEA_CACHE_LINE ); +prefetch (pref +EHEA_CACHE_LINE *2 ); +prefetch (pref +EHEA_CACHE_LINE *3 ); + skb = skb_arr_rq2[skb_index]; + +if (unlikely (!skb)) { +EDEB_ERR (4 ,"rq2: SKB=NULL, index=%d" , + skb_index); +break ; + } + skb_arr_rq2[skb_index] =NULL ; +ehea_fill_skb (dev, skb, cqe); + processed_RQ2++; + }else { +void *pref; +int skb_index =EHEA_BMASK_GET (EHEA_WR_ID_INDEX , + cqe->wr_id); + x = (skb_index +1 ) % port_res->skb_arr_rq3_len; + pref = (void *)skb_arr_rq3[x]; +prefetchw (pref); +prefetchw (pref +EHEA_CACHE_LINE ); + + x = (skb_index +1 ) % port_res->skb_arr_rq3_len; + pref = (void *)(skb_arr_rq3[x]->data); +prefetch (pref); +prefetch (pref +EHEA_CACHE_LINE ); +prefetch (pref +EHEA_CACHE_LINE *2 ); +prefetch (pref +EHEA_CACHE_LINE *3 ); + + skb = skb_arr_rq3[skb_index]; +if (unlikely (!skb)) { +EDEB_ERR (4 ,"rq3: SKB=NULL, index=%d" , + skb_index); +break ; + } + skb_arr_rq3[skb_index] =NULL ; +ehea_fill_skb (dev, skb, cqe); + processed_RQ3++; + } + +EDEB (6 ,"About to pass SKB: dev=%p\n" +"skb=%p skb->data=%p skb->len=%d" +" skb->data_len=0x%x nr_frags=%d" , + dev, + skb, + skb->data, + skb->len, + skb->data_len,skb_shinfo (skb)->nr_frags); +if (cqe->status &EHEA_CQE_VLAN_TAG_XTRACT ) { +EDEB (7 ,"VLAN TAG extracted: %4x, vgrp=%p" , + cqe->vlan_tag, port->vgrp); +EDEB (7 ,"vlan_devices[vlan_tag]=%p" , + port->vgrp->vlan_devices[cqe->vlan_tag]); +vlan_hwaccel_receive_skb (skb, port->vgrp, + cqe->vlan_tag); + }else { +EDEB (7 ,"netif_receive_skb" ); +netif_receive_skb (skb); + } +EDEB (7 ,"SKB passed (netif_receive(skb) called)" ); + + }else { +/* TODO: Error treatment still to be done */ +struct sk_buff *skb; + +EDEB_ERR (4 ,"cqe->status indicating error: CQE:" ); +EDEB_DMP (4 , (u8 *)cqe,4 *16 ,"" ); +if (rq ==2 ) { + processed_RQ2++; + skb = skb_arr_rq2[ +EHEA_BMASK_GET (EHEA_WR_ID_INDEX , + cqe->wr_id)]; + skb_arr_rq2[EHEA_BMASK_GET (EHEA_WR_ID_INDEX , + cqe->wr_id)] =NULL ; +dev_kfree_skb (skb); + } +if (rq ==3 ) { + processed_RQ3++; + skb = skb_arr_rq3[ +EHEA_BMASK_GET (EHEA_WR_ID_INDEX , + cqe->wr_id)]; + skb_arr_rq3[EHEA_BMASK_GET (EHEA_WR_ID_INDEX , + cqe->wr_id)] =NULL ; +dev_kfree_skb (skb); + } + } + cqe =ehea_poll_rq1 (qp, &wqe_index); + } + + dev->quota -= processed; + *budget -= processed; + + port->p_state.ehea_poll +=1 ; + + port->stats.rx_packets += processed; + +ehea_refill_rq1 (port_res, last_wqe_index, processed_RQ1); +ehea_refill_rq2 (port_res, processed_RQ2); +ehea_refill_rq3 (port_res, processed_RQ3); + + intreq = ((port->p_state.ehea_poll &0xF ) ==0xF ); + +EDEB_EX (7 ,"processed=%d, *budget=%d, dev->quota=%d" , + processed, *budget, dev->quota); + +if (!cqe || intreq) { +netif_rx_complete (dev); +ehea_reset_cq_ep (port_res->recv_cq); +ehea_reset_cq_n1 (port_res->recv_cq); + cqe =ipz_qeit_get_valid (&qp->ipz_rqueue1); +EDEB (7 ,"CQE=%p, break ehea_poll while loop" , cqe); +if (!cqe || intreq) +return 0 ; +if (!netif_rx_reschedule (dev, my_quota)) +return 0 ; + } +return 1 ; +} + +#define MAX_SENDCOMP_QUOTA 400 +void ehea_send_irq_tasklet (unsigned long data) +{ +int quota =MAX_SENDCOMP_QUOTA ; +int skb_index; +int cqe_counter =0 ; +int swqe_av =0 ; +unsigned long flags; +struct sk_buff *skb; +struct ehea_cqe *cqe; +struct ehea_port_res *port_res = (struct ehea_port_res *)data; +struct ehea_cq *send_cq = port_res->send_cq; +struct net_device *dev = port_res->port->netdev; + +EDEB_EN (7 ,"port_res=%p" , port_res); + + +do { + cqe =ehea_poll_cq (send_cq); +if (!cqe) { +EDEB (7 ,"No further cqe found" ); +ehea_reset_cq_ep (send_cq); +ehea_reset_cq_n1 (send_cq); + cqe =ehea_poll_cq (send_cq); +if (likely (!cqe)) { +EDEB (7 ,"No cqe found after having" +" reset N1/EP\n" ); +break ; + } + } + cqe_counter++; +EDEB (7 ,"CQE found on Send-CQ:" ); +EDEB_DMP (7 , (u8 *)cqe,4 *16 ,"" ); + +if (likely (EHEA_BMASK_GET (EHEA_WR_ID_TYPE , cqe->wr_id) + ==EHEA_SWQE2_TYPE )) {/* is swqe format 2 */ +int i; +int index =EHEA_BMASK_GET (EHEA_WR_ID_INDEX , + cqe->wr_id); +for (i =0 ; i <EHEA_BMASK_GET (EHEA_WR_ID_REFILL , + cqe->wr_id); i++) { + + skb_index = ((index - i + + port_res->skb_arr_sq_len) + % port_res->skb_arr_sq_len); + skb = port_res->skb_arr_sq[skb_index]; + port_res->skb_arr_sq[skb_index] =NULL ; + +if (unlikely (!skb)) { +EDEB_ERR (4 ,"s_irq_tasklet: SKB=NULL " +"WQ_ID=%lX, loop=%d, index=%d" , + cqe->wr_id, i, skb_index); +break ; + } +dev_kfree_skb (skb); + } + } + swqe_av +=EHEA_BMASK_GET (EHEA_WR_ID_REFILL , cqe->wr_id); + quota--; + }while (quota >0 ); + +ehea_update_feca (send_cq, cqe_counter); + +atomic_add (swqe_av, &port_res->swqe_avail); +EDEB (7 ,"port_res->swqe_avail=%d" , +atomic_read (&port_res->swqe_avail)); + +if (unlikely (netif_queue_stopped (dev))) { +spin_lock_irqsave (&port_res->netif_queue, flags); +if (unlikely ((atomic_read (&port_res->swqe_avail) + >=EHEA_SWQE_REFILL_TH ))) { +EDEB (7 ,"port %d swqes_avail >=10 (%d)," +"netif_wake_queue called" , + port_res->port->logical_port_id, +atomic_read (&port_res->swqe_avail)); +netif_wake_queue (port_res->port->netdev); + } +spin_unlock_irqrestore (&port_res->netif_queue, flags); + } + +if (unlikely (cqe)) +tasklet_hi_schedule (&port_res->send_comp_task); + +EDEB_EX (7 ,"" ); +} + +irqreturn_t ehea_send_irq_handler (int irq,void *param,struct pt_regs *regs) +{ +struct ehea_port_res *pr = (struct ehea_port_res *)param; +EDEB_EN (7 ,"irq=%d, param=%p, pt_regs=%p" , irq, param, regs); +tasklet_hi_schedule (&pr->send_comp_task); +EDEB_EX (7 ,"" ); +return IRQ_HANDLED ; +} + +irqreturn_t ehea_recv_irq_handler (int irq,void *param,struct pt_regs * regs) +{ +struct ehea_port_res *pr = (struct ehea_port_res *)param; +struct ehea_port *port = pr->port; +EDEB_EN (7 ,"irq=%d, param=%p, pt_regs=%p" , irq, param, regs); +netif_rx_schedule (port->netdev); +EDEB_EX (7 ,"" ); +return IRQ_HANDLED ; +} + +irqreturn_t ehea_qp_aff_irq_handler (int irq,void *param,struct pt_regs * regs) +{ +struct ehea_port_res *pr = (struct ehea_port_res *)param; +struct ehea_port *port = pr->port; +struct ehea_eqe *eqe; +struct ehea_cqe *cqe; + +EDEB_EN (7 ,"irq=%d, param=%p, pt_regs=%p" , irq, param, regs); + eqe = (struct ehea_eqe *)ehea_poll_eq (port->adapter, pr->qp_eq); +EDEB (7 ,"eqe=%p" , eqe); +while (eqe) { +EDEB (7 ,"*eqe=%lx" , *(u64 *)eqe); + eqe = (struct ehea_eqe *)ehea_poll_eq (port->adapter, pr->qp_eq); +EDEB (7 ,"next eqe=%p" , eqe); + } + +/* TODO: Error treatment still to be implemented */ + cqe =ehea_poll_cq (pr->recv_cq); +if (cqe) +EDEB_DMP (7 , (u8 *)cqe,64 ,"CQE on Receive Queue" ); + +EDEB_EX (7 ,"" ); +return IRQ_HANDLED ; +} + +static struct ehea_port *ehea_get_port (struct ehea_adapter *adapter, +int logical_port) +{ +int i; + +for (i =0 ; i < adapter->num_ports; i++) +if (adapter->port[i]->logical_port_id == logical_port) +return adapter->port[i]; +return NULL ; +} + +static void ehea_parse_eqe (struct ehea_adapter *adapter,u64 eqe) +{ +int ret = -EINVAL ; +u8 ec =0 ; +u8 portnum =0 ; +struct ehea_port *port =NULL ; + +EDEB_EN (7 ,"eqe=%lx" , eqe); + + ec =EHEA_BMASK_GET (NEQE_EVENT_CODE , eqe); + +switch (ec) { +case EHEA_EC_PORTSTATE_CHG :/* port state change */ +EDEB (7 ,"Port state change" ); + portnum =EHEA_BMASK_GET (NEQE_PORTNUM , eqe); + port =ehea_get_port (adapter, portnum); + +if (!port) { +EDEB_ERR (4 ,"Unknown portnum %x" , portnum); +break ; + } + +if (EHEA_BMASK_GET (NEQE_PORT_UP , eqe)) { +if (!netif_carrier_ok (port->netdev)) { + ret =ehea_sense_port_attr (adapter, portnum); +if (ret) { +EDEB_ERR (4 ,"Failed resensing port" ); +break ; + } + +printk ("%s: Logical port up: %dMbps %s Duplex" , + port->netdev->name, + port->port_speed, + port->full_duplex == +1 ?"Full" :"Half" ); + +netif_carrier_on (port->netdev); +netif_wake_queue (port->netdev); + } + }else +if (netif_carrier_ok (port->netdev)) { +printk ("%s: Logical port down" , + port->netdev->name); +netif_carrier_off (port->netdev); +netif_stop_queue (port->netdev); + } + +if (EHEA_BMASK_GET (NEQE_EXTSWITCH_PORT_UP , eqe)) +printk ("%s: Physical port up" , port->netdev->name); +else +printk ("%s: Physical port down" , port->netdev->name); + +if (EHEA_BMASK_GET (NEQE_EXTSWITCH_PRIMARY , eqe)) +printk ("Externel switch port is primary port" ); +else +printk ("Externel switch port is backup port" ); + +break ; +case EHEA_EC_ADAPTER_MALFUNC :/* adapter malfunction */ +EDEB_ERR (4 ,"Adapter malfunction" ); +break ; +case EHEA_EC_PORT_MALFUNC :/* port malfunction */ +EDEB_ERR (4 ,"Port malfunction" ); +/* TODO Determine the port structure of the malfunctioning port +* netif_carrier_off(port->netdev); +* netif_stop_queue(port->netdev); +*/ +break ; +default : +EDEB_ERR (4 ,"Unknown event code %x" , ec); +break ; + } + +EDEB_EX (7 ,"" ); +} + +void ehea_neq_tasklet (unsigned long data) +{ +struct ehea_adapter *adapter = (struct ehea_adapter *)data; +struct ehea_eqe *eqe; +u64 event_mask; + +EDEB_EN (7 ,"" ); + eqe = (struct ehea_eqe *)ehea_poll_eq (adapter, adapter->neq); +EDEB (7 ,"eqe=%p" , eqe); + +while (eqe) { +EDEB (7 ,"*eqe=%lx" , eqe->entry); +ehea_parse_eqe (adapter, eqe->entry); + eqe = (struct ehea_eqe *)ehea_poll_eq (adapter, adapter->neq); +EDEB (7 ,"next eqe=%p" , eqe); + } + + event_mask =EHEA_BMASK_SET (NELR_PORTSTATE_CHG ,1 ) + |EHEA_BMASK_SET (NELR_ADAPTER_MALFUNC ,1 ) + |EHEA_BMASK_SET (NELR_PORT_MALFUNC ,1 ); + +ehea_h_reset_events (adapter->handle, + adapter->neq->ipz_eq_handle, event_mask); +EDEB_EX (7 ,"" ); +} + +irqreturn_t ehea_interrupt_neq (int irq,void *param,struct pt_regs *regs) +{ +struct ehea_adapter *adapter = (struct ehea_adapter *)param; +EDEB_EN (7 ,"dev_id=%p" , adapter); +tasklet_hi_schedule (&adapter->neq_tasklet); +EDEB_EX (7 ,"" ); +return IRQ_HANDLED ; +} + + + + +static void ehea_fill_port_res (struct ehea_port_res *pr) +{ +struct ehea_qp_init_attr *init_attr = &pr->qp->init_attr; + +/* RQ 1 */ +ehea_init_fill_rq1 (pr, init_attr->act_nr_rwqes_rq1 + - init_attr->act_nr_rwqes_rq2 + - init_attr->act_nr_rwqes_rq3 -1 ); + +/* RQ 2 */ +ehea_refill_rq2 (pr, init_attr->act_nr_rwqes_rq2); + +/* RQ 3 */ +ehea_refill_rq3 (pr, init_attr->act_nr_rwqes_rq3); +} + +static int ehea_reg_interrupts (struct net_device *dev) +{ +int ret = -EINVAL ; +struct ehea_port *port = (struct ehea_port *)dev->priv; +struct ehea_port_res *pr = &port->port_res[0 ]; + +EDEB_EN (7 ,"" ); +snprintf (port->int_send_name,11 ,"%s-send" , dev->name); + ret =ibmebus_request_irq (NULL , + pr->send_eq->attr.ist1, + ehea_send_irq_handler, +SA_INTERRUPT , port->int_send_name, pr); +if (ret) { +EDEB_ERR (4 ,"Failed registering irq for ehea_send_int()" ); + goto failure; + } +EDEB (7 ,"irq_handle %X for function ehea_send_int registered" , + pr->send_eq->attr.ist1); + +snprintf (port->int_recv_name,11 ,"%s-recv" , dev->name); + ret =ibmebus_request_irq (NULL , + pr->recv_eq->attr.ist1, + ehea_recv_irq_handler, +SA_INTERRUPT , port->int_recv_name, pr); +if (ret) { +EDEB_ERR (4 ,"Failed registering irq for ehea_recv_int()" ); + goto failure; + } +EDEB (7 ,"irq_handle %X for function ehea_recv_int registered" , + pr->recv_eq->attr.ist1); + +snprintf (port->int_aff_name,11 ,"%s-aff" , dev->name); + ret =ibmebus_request_irq (NULL , + pr->qp_eq->attr.ist1, + ehea_qp_aff_irq_handler, +SA_INTERRUPT , port->int_aff_name, pr); +if (ret) { +EDEB_ERR (4 ,"Failed registering irq for qp_aff_irq_handler()" ); + goto failure; + } +EDEB (7 ,"irq_handle %X for function qp_aff_irq_handler registered" , + pr->qp_eq->attr.ist1); +failure: +EDEB_EX (7 ,"ret=%d" , ret); +return ret; +} + +static void ehea_free_interrupts (struct net_device *dev) +{ +struct ehea_port *port = (struct ehea_port *)dev->priv; +struct ehea_port_res *pr = &port->port_res[0 ]; + +EDEB_EN (7 ,"" ); + +/* send */ +ibmebus_free_irq (NULL , pr->send_eq->attr.ist1, pr); + +/* receive */ +ibmebus_free_irq (NULL , pr->recv_eq->attr.ist1, pr); + +/* associated events */ +ibmebus_free_irq (NULL , pr->qp_eq->attr.ist1, pr); +EDEB_EX (7 ,"" ); +} + +static int ehea_configure_port (struct ehea_port *port) +{ +int ret = -EINVAL ; +u64 hret =H_HARDWARE ; +struct hcp_query_ehea_port_cb_0 *ehea_port_cb_0 =NULL ; +u64 mask =0 ; +int i; + +EDEB_EN (7 ,"" ); + + ehea_port_cb_0 =kzalloc (H_CB_ALIGNMENT ,GFP_KERNEL ); + +if (!ehea_port_cb_0) { +EDEB_ERR (4 ,"No memory for ehea_port control block" ); + ret = -ENOMEM ; + goto kzalloc_failed; + } + + ehea_port_cb_0->port_rc =EHEA_BMASK_SET (PXLY_RC_VALID ,1 ) + |EHEA_BMASK_SET (PXLY_RC_IP_CHKSUM ,1 ) + |EHEA_BMASK_SET (PXLY_RC_TCP_UDP_CHKSUM ,1 ) + |EHEA_BMASK_SET (PXLY_RC_VLAN_XTRACT ,1 ) + |EHEA_BMASK_SET (PXLY_RC_VLAN_TAG_FILTER , +PXLY_RC_VLAN_FILTER ) + |EHEA_BMASK_SET (PXLY_RC_JUMBO_FRAME ,1 ); + +for (i =0 ; i < port->num_def_qps; i++) { + ehea_port_cb_0->default_qpn_array[i] = + port->port_res[0 ].qp->init_attr.qp_nr; + +EDEB (7 ,"default_qpn_array[%d]=%d" , + i, port->port_res[0 ].qp->init_attr.qp_nr); + } + +EDEB (7 ,"ehea_port_cb_0" ); +EDEB_DMP (7 , (u8 *)ehea_port_cb_0,sizeof (*ehea_port_cb_0),"" ); + + mask =EHEA_BMASK_SET (H_PORT_CB0_PRC ,1 ) + |EHEA_BMASK_SET (H_PORT_CB0_DEFQPNARRAY ,1 ); + + hret =ehea_h_modify_ehea_port (port->adapter->handle, + port->logical_port_id, +H_PORT_CB0 , + mask, + (void *)ehea_port_cb_0); + +if (hret !=H_SUCCESS ) { + goto modify_ehea_port_failed; + } + + ret =0 ; + +modify_ehea_port_failed: +kfree (ehea_port_cb_0); + +kzalloc_failed: +EDEB_EX (7 ,"ret=%d" , ret); +return ret; +} + +static int ehea_init_port_res (struct ehea_port *port,struct ehea_port_res *pr) +{ +int ret = -EINVAL ; +int max_rq_entries =0 ; +enum ehea_eq_type eq_type =EHEA_EQ ; +struct ehea_qp_init_attr *init_attr; + +EDEB_EN (7 ,"port=%p, pr=%p" , port, pr); +memset (pr,0 ,sizeof (struct ehea_port_res )); + + pr->port = port; +spin_lock_init (&pr->send_lock); +spin_lock_init (&pr->recv_lock); +spin_lock_init (&pr->xmit_lock); + pr->recv_eq =ehea_create_eq (port->adapter, eq_type, +EHEA_MAX_ENTRIES_EQ ,0 ); +if (!pr->recv_eq) { +EDEB_ERR (4 ,"ehea_create_eq failed (recv_eq)" ); + goto ehea_init_port_res_err1; + } + pr->send_eq =ehea_create_eq (port->adapter, eq_type, +EHEA_MAX_ENTRIES_EQ ,0 ); +if (!pr->send_eq) { +EDEB_ERR (4 ,"ehea_create_eq failed (send_eq)" ); + goto ehea_init_port_res_err2; + } + + pr->qp_eq =ehea_create_eq (port->adapter, eq_type, +EHEA_MAX_ENTRIES_EQ ,1 ); +if (!pr->qp_eq) { +EDEB_ERR (4 ,"ehea_create_eq failed (qp_eq)" ); + goto ehea_init_port_res_err3; + } + + pr->recv_cq =ehea_create_cq (port->adapter,EHEA_MAX_CQE_COUNT , + pr->recv_eq->ipz_eq_handle, + port->logical_port_id); +if (!pr->recv_cq) { +EDEB_ERR (4 ,"ehea_create_cq failed (cq_recv)" ); + goto ehea_init_port_res_err4; + } + + pr->send_cq =ehea_create_cq (port->adapter,EHEA_MAX_CQE_COUNT , + pr->send_eq->ipz_eq_handle, + port->logical_port_id); +if (!pr->send_cq) { +EDEB_ERR (4 ,"ehea_create_cq failed (cq_send)" ); + goto ehea_init_port_res_err5; + } + + init_attr = (struct ehea_qp_init_attr *) +kzalloc (sizeof (struct ehea_qp_init_attr ),GFP_KERNEL ); + +if (!init_attr) { +EDEB_ERR (4 ,"no mem for init_attr struct" ); + ret = -ENOMEM ; + goto ehea_init_port_res_err5; + } + + init_attr->low_lat_rq1 =1 ; + init_attr->signalingtype =1 ;/* generate CQE if specified in WQE */ + init_attr->rq_count =3 ; + init_attr->qp_token = port->logical_port_id +1 ; + + init_attr->max_nr_send_wqes =EHEA_MAX_ENTRIES_SQ ; + init_attr->max_nr_rwqes_rq1 =EHEA_MAX_ENTRIES_RQ1 ; + init_attr->max_nr_rwqes_rq2 =EHEA_MAX_ENTRIES_RQ2 ; + init_attr->max_nr_rwqes_rq3 =EHEA_MAX_ENTRIES_RQ3 ; + + init_attr->wqe_size_enc_sq =EHEA_SG_SQ ; + init_attr->wqe_size_enc_rq1 =EHEA_SG_RQ1 ; + init_attr->wqe_size_enc_rq2 =EHEA_SG_RQ2 ; + init_attr->wqe_size_enc_rq3 =EHEA_SG_RQ3 ; + + init_attr->rq2_threshold =EHEA_RQ2_THRESHOLD ; + init_attr->rq3_threshold =EHEA_RQ3_THRESHOLD ; + init_attr->port_nr = port->logical_port_id; + init_attr->send_cq_handle = pr->send_cq->ipz_cq_handle; + init_attr->recv_cq_handle = pr->recv_cq->ipz_cq_handle; + init_attr->aff_eq_handle = pr->qp_eq->ipz_eq_handle; + + pr->qp =ehea_create_qp (port->adapter, port->adapter->pd, init_attr); +if (!pr->qp) { +EDEB_ERR (4 ,"could not create queue pair" ); + goto ehea_init_port_res_err6; + } + +/* SQ */ + max_rq_entries = init_attr->act_nr_send_wqes; + pr->skb_arr_sq = (struct sk_buff **)vmalloc (sizeof (struct sk_buff *) + * (max_rq_entries +1 )); +if (!pr->skb_arr_sq) { +EDEB_ERR (4 ,"vmalloc for skb_arr_sq failed" ); + goto ehea_init_port_res_err7; + } +memset (pr->skb_arr_sq,0 ,sizeof (void *) * (max_rq_entries +1 )); + pr->skb_sq_index =0 ; + pr->skb_arr_sq_len = max_rq_entries +1 ; + +/* RQ 1 */ + max_rq_entries = init_attr->act_nr_rwqes_rq1; + pr->skb_arr_rq1 = (struct sk_buff **)vmalloc (sizeof (struct sk_buff *) + * (max_rq_entries +1 )); +if (!pr->skb_arr_rq1) { +EDEB_ERR (4 ,"vmalloc for skb_arr_rq1 failed" ); + goto ehea_init_port_res_err8; + } +memset (pr->skb_arr_rq1,0 ,sizeof (void *) * (max_rq_entries +1 )); + pr->skb_arr_rq1_len = max_rq_entries +1 ; + +/* RQ 2 */ + max_rq_entries = init_attr->act_nr_rwqes_rq2; + pr->skb_arr_rq2 = (struct sk_buff **)vmalloc (sizeof (struct sk_buff *) + * (max_rq_entries +1 )); +if (!pr->skb_arr_rq2) { +EDEB_ERR (4 ,"vmalloc for skb_arr_rq2 failed" ); + goto ehea_init_port_res_err9; + } +memset (pr->skb_arr_rq2,0 ,sizeof (void *) * (max_rq_entries +1 )); + pr->skb_arr_rq2_len = max_rq_entries; + pr->skb_rq2_index =0 ; + +/* RQ 3 */ + max_rq_entries = init_attr->act_nr_rwqes_rq3; + pr->skb_arr_rq3 = (struct sk_buff **)vmalloc (sizeof (struct sk_buff *) + * (max_rq_entries +1 )); +if (!pr->skb_arr_rq3) { +EDEB_ERR (4 ,"vmalloc for skb_arr_rq3 failed" ); + goto ehea_init_port_res_err10; + } +memset (pr->skb_arr_rq3,0 ,sizeof (void *) * (max_rq_entries +1 )); + pr->skb_arr_rq3_len = max_rq_entries; + pr->skb_rq3_index =0 ; + +/* Set default QP for this port */ + ret =ehea_configure_port (port); + +if (ret) { +EDEB_ERR (4 ,"ehea_configure_port failed" ); + goto ehea_init_port_res_err11; + } + +tasklet_init (&pr->send_comp_task, ehea_send_irq_tasklet, + (unsigned long )pr); + +atomic_set (&pr->swqe_avail,EHEA_MAX_ENTRIES_SQ -1 ); + + + ret =0 ; + goto done; + +ehea_init_port_res_err11: +vfree (pr->skb_arr_rq3); +ehea_init_port_res_err10: +vfree (pr->skb_arr_rq2); +ehea_init_port_res_err9: +vfree (pr->skb_arr_rq1); +ehea_init_port_res_err8: +vfree (pr->skb_arr_sq); +ehea_init_port_res_err7: +ehea_destroy_qp (pr->qp); +ehea_init_port_res_err6: +ehea_destroy_cq (pr->send_cq); +ehea_init_port_res_err5: +ehea_destroy_cq (pr->recv_cq); +ehea_init_port_res_err4: +ehea_destroy_eq (port->adapter, pr->qp_eq); +ehea_init_port_res_err3: +ehea_destroy_eq (port->adapter, pr->send_eq); +ehea_init_port_res_err2: +ehea_destroy_eq (port->adapter, pr->recv_eq); +ehea_init_port_res_err1: +done: +EDEB_EX (7 ,"ret=%d" , ret); +return ret; +} + +static int ehea_clean_port_res (struct ehea_port *port,struct ehea_port_res *pr) +{ +int i; +int ret = -EINVAL ; + +EDEB_EN (7 ,"Not completed yet..." ); + + ret =ehea_destroy_qp (pr->qp); +if (ret) +EDEB_ERR (4 ,"could not destroy queue pair" ); + + ret =ehea_destroy_cq (pr->send_cq); +if (ret) +EDEB_ERR (4 ,"could not destroy send_cq" ); + + ret =ehea_destroy_cq (pr->recv_cq); +if (ret) +EDEB_ERR (4 ,"could not destroy recv_cq" ); + + ret =ehea_destroy_eq (port->adapter, pr->send_eq); +if (ret) +EDEB_ERR (4 ,"could not destroy send_eq" ); + + ret =ehea_destroy_eq (port->adapter, pr->recv_eq); +if (ret) +EDEB_ERR (4 ,"could not destroy recv_eq" ); + + ret =ehea_destroy_eq (port->adapter, pr->qp_eq); +if (ret) +EDEB_ERR (4 ,"could not destroy qp_eq" ); + +for (i =0 ; i < pr->skb_arr_rq1_len; i++) { +if (pr->skb_arr_rq1[i]) +dev_kfree_skb (pr->skb_arr_rq1[i]); + } + +for (i =0 ; i < pr->skb_arr_rq2_len; i++) +if (pr->skb_arr_rq2[i]) +dev_kfree_skb (pr->skb_arr_rq2[i]); + +for (i =0 ; i < pr->skb_arr_rq3_len; i++) +if (pr->skb_arr_rq3[i]) +dev_kfree_skb (pr->skb_arr_rq3[i]); + +for (i =0 ; i < pr->skb_arr_sq_len; i++) +if (pr->skb_arr_sq[i]) +dev_kfree_skb (pr->skb_arr_sq[i]); + +vfree (pr->skb_arr_sq); +vfree (pr->skb_arr_rq1); +vfree (pr->skb_arr_rq2); +vfree (pr->skb_arr_rq3); + +EDEB_EX (7 ,"ret=%d" , ret); +return ret; +} + +static inline void write_ip_start_end (struct ehea_swqe *swqe, +const struct sk_buff *skb) +{ + + swqe->ip_start = (u8)(((u64 )skb->nh.iph) - ((u64 )skb->data)); + swqe->ip_end = (u8)(swqe->ip_start + skb->nh.iph->ihl *4 -1 ); +} + +static inline void write_tcp_offset_end (struct ehea_swqe *swqe, +const struct sk_buff *skb) +{ + swqe->tcp_offset = (u8)(swqe->ip_end +1 + offsetof(struct tcphdr , + check)); + swqe->tcp_end = (u16 )skb->len -1 ; +} + +static inline void write_udp_offset_end (struct ehea_swqe *swqe, +const struct sk_buff *skb) +{ + swqe->tcp_offset = (u8)(swqe->ip_end +1 + offsetof(struct udphdr , + check)); + swqe->tcp_end = (u16 )skb->len -1 ; +} + +static inline void write_swqe2_data (struct sk_buff *skb, +struct net_device *dev, +struct ehea_swqe *swqe) +{ +int skb_data_size, nfrags, headersize, i, sg1entry_contains_frag_data; +struct ehea_port *port = (struct ehea_port *)dev->priv; +struct ehea_vsgentry *sg_list; +struct ehea_vsgentry *sg1entry; +struct ehea_vsgentry *sgentry; +u8 *imm_data; +skb_frag_t *frag; +EDEB_EN (7 ,"" ); + + skb_data_size = skb->len - skb->data_len; + nfrags =skb_shinfo (skb)->nr_frags; + sg1entry = &swqe->u.immdata_desc.sg_entry; + sg_list = (struct ehea_vsgentry *)&swqe->u.immdata_desc.sg_list; + imm_data = &swqe->u.immdata_desc.immediate_data[0 ]; + swqe->descriptors =0 ; + sg1entry_contains_frag_data =0 ; + +if ((dev->features &NETIF_F_TSO ) &&skb_shinfo (skb)->tso_size) { +/* Packet is TCP with TSO enabled */ + swqe->tx_control |=EHEA_SWQE_TSO ; + swqe->mss =skb_shinfo (skb)->tso_size; +/* copy only eth/ip/tcp headers to immediate data and +* the rest of skb->data to sg1entry +*/ + headersize =ETH_HLEN + (skb->nh.iph->ihl *4 ) + + skb->h.th->doff *4 ; + skb_data_size = skb->len - skb->data_len; + +if (skb_data_size >= headersize) { +/* copy immediate data */ +memcpy (imm_data, skb->data, headersize); + swqe->immediate_data_length = headersize; + +if (skb_data_size > headersize) { +/* set sg1entry data */ + sg1entry->l_key =ehea_get_lkey (port->adapter, + (void *)skb-> + data + + headersize); + sg1entry->vaddr = + (u64)(skb->data + headersize); + sg1entry->len = skb_data_size - headersize; + + swqe->descriptors++; + } + }else +EDEB_ERR (4 ,"Cannot handle fragmented headers" ); + }else { +/* Packet is any nonTSO type +* +* Copy as much as possible skb->data to immediate data and +* the rest to sg1entry +*/ +if (skb_data_size >=SWQE2_MAX_IMM ) { +/* copy immediate data */ +memcpy (imm_data, skb->data,SWQE2_MAX_IMM ); + + swqe->immediate_data_length =SWQE2_MAX_IMM ; + +if (skb_data_size >SWQE2_MAX_IMM ) { +/* copy sg1entry data */ + sg1entry->l_key =ehea_get_lkey (port->adapter, + (void *)(skb-> + data + +SWQE2_MAX_IMM )); + sg1entry->vaddr = + (u64)(skb->data +SWQE2_MAX_IMM ); + sg1entry->len = skb_data_size -SWQE2_MAX_IMM ; + + swqe->descriptors++; + } + }else { +memcpy (imm_data, skb->data, skb_data_size); + swqe->immediate_data_length = skb_data_size; + } + } + +/* write descriptors */ +if (nfrags >0 ) { +if (swqe->descriptors ==0 ) { +/* sg1entry not yet used */ + frag = &skb_shinfo (skb)->frags[0 ]; + +/* copy sg1entry data */ + sg1entry->l_key =ehea_get_lkey (port->adapter, + (void + *)(page_address (frag-> + page) + + frag->page_offset)); + sg1entry->vaddr = + (u64)(page_address (frag->page) + + frag->page_offset); + sg1entry->len = frag->size; + + swqe->descriptors++; + sg1entry_contains_frag_data =1 ; + } + +for (i = sg1entry_contains_frag_data; i < nfrags; i++) { + + frag = &skb_shinfo (skb)->frags[i]; + sgentry = &sg_list[i - sg1entry_contains_frag_data]; + + sgentry->l_key = +ehea_get_lkey (port->adapter, + (void *)(page_address (frag->page) + + frag->page_offset)); + sgentry->vaddr = + (u64)(page_address (frag->page) + frag->page_offset); + sgentry->len = frag->size; + swqe->descriptors++; + } + } + +EDEB_EX (7 ,"" ); +} + +static int ehea_ioctl (struct net_device *dev,struct ifreq *ifr,int cmd) +{ +EDEB_ERR (4 ,"ioctl not supported: dev=%s cmd=%d" , dev->name, cmd); +return -EOPNOTSUPP ; +} + +static u64 ehea_broadcast_reg_helper (struct ehea_port *port,u32 hcallid) +{ +u64 hret =H_HARDWARE ; +u8 reg_type =0 ; + +if (hcallid ==H_REG_BCMC ) { +EDEB (7 ,"REGistering MAC for broadcast" ); + }else { +EDEB (7 ,"DEREGistering MAC for broadcast" ); + } + +/* De/Register untagged packets */ + reg_type =EHEA_BCMC_BROADCAST |EHEA_BCMC_UNTAGGED ; + hret =ehea_h_reg_dereg_bcmc (port->adapter->handle, + port->logical_port_id, + reg_type, port->mac_addr,0 , hcallid); +if (hret !=H_SUCCESS ) + goto hcall_failed; + +/* De/Register VLAN packets */ + reg_type =EHEA_BCMC_BROADCAST |EHEA_BCMC_VLANID_ALL ; + hret =ehea_h_reg_dereg_bcmc (port->adapter->handle, + port->logical_port_id, + reg_type, port->mac_addr,0 , hcallid); +hcall_failed: +return hret; +} + +static int ehea_set_mac_addr (struct net_device *dev,void *sa) +{ +int ret = -EOPNOTSUPP ; +u64 hret =H_HARDWARE ; +struct hcp_query_ehea_port_cb_0 *ehea_port_cb_0 =NULL ; +struct ehea_port *port = (struct ehea_port *)dev->priv; +struct sockaddr *mac_addr = (struct sockaddr *)sa; + +EDEB_EN (7 ,"devname=%s" , dev->name); +EDEB_DMP (7 , (u8 *)&(mac_addr->sa_data[0 ]),14 ,"" ); + +if (!is_valid_ether_addr (mac_addr->sa_data)) { + ret = -EADDRNOTAVAIL ; + goto invalid_mac; + } + + ehea_port_cb_0 =kzalloc (H_CB_ALIGNMENT ,GFP_KERNEL ); + +if (!ehea_port_cb_0) { +EDEB_ERR (4 ,"No memory for ehea_port control block" ); + ret = -ENOMEM ; + goto kzalloc_failed; + } + +memcpy ((u8 *)(&(ehea_port_cb_0->port_mac_addr)), + (u8 *)&(mac_addr->sa_data[0 ]),6 ); + + ehea_port_cb_0->port_mac_addr = ehea_port_cb_0->port_mac_addr >>16 ; + +EDEB (7 ,"ehea_port_cb_0" ); +EDEB_DMP (7 , (u8 *)ehea_port_cb_0, +sizeof (struct hcp_query_ehea_port_cb_0 ),"" ); + + hret =ehea_h_modify_ehea_port (port->adapter->handle, + port->logical_port_id, +H_PORT_CB0 , +EHEA_BMASK_SET (H_PORT_CB0_MAC ,1 ), + (void *)ehea_port_cb_0); +if (hret !=H_SUCCESS ) { + ret = -EOPNOTSUPP ; + goto hcall_failed; + } + +memcpy (dev->dev_addr, mac_addr->sa_data, dev->addr_len); + +/* Deregister old MAC in PHYP */ + hret =ehea_broadcast_reg_helper (port,H_DEREG_BCMC ); +if (hret) { + ret = -EOPNOTSUPP ; + goto hcall_failed; + } + + port->mac_addr = ehea_port_cb_0->port_mac_addr <<16 ; + +/* Register new MAC in PHYP */ + hret =ehea_broadcast_reg_helper (port,H_REG_BCMC ); +if (hret) { + ret = -EOPNOTSUPP ; + goto hcall_failed; + } + + ret =0 ; + +hcall_failed: +kfree (ehea_port_cb_0); + +kzalloc_failed: +invalid_mac: +EDEB_EX (7 ,"ret=%d" , ret); +return ret; +} + +static void ehea_promiscuous (struct net_device *dev,int enable) +{ +struct ehea_port *port = dev->priv; + +if (!port->promisc) { +if (enable) { +/* Enable promiscuous mode */ +EDEB (7 ,"Enabling IFF_PROMISC" ); +EDEB_ERR (4 ,"Enable promiscuous mode: " +"not yet implemented" ); + port->promisc =EHEA_ENABLE ; + } + }else { +if (!enable) { +/* Disable promiscuous mode */ +EDEB (7 ,"Disabling IFF_PROMISC" ); +EDEB_ERR (4 ,"Disable promiscuous mode: " +"not yet implemented" ); + port->promisc =EHEA_DISABLE ; + } + } +} + +static u64 ehea_multicast_reg_helper (struct ehea_port *port, +u64 mc_mac_addr, +u32 hcallid) +{ +u64 hret =H_HARDWARE ; +u8 reg_type =0 ; + + reg_type =EHEA_BCMC_SCOPE_ALL |EHEA_BCMC_MULTICAST + |EHEA_BCMC_UNTAGGED ; + + hret =ehea_h_reg_dereg_bcmc (port->adapter->handle, + port->logical_port_id, + reg_type, mc_mac_addr,0 , hcallid); +if (hret) + goto hcall_failed; + + reg_type =EHEA_BCMC_SCOPE_ALL |EHEA_BCMC_MULTICAST + |EHEA_BCMC_VLANID_ALL ; + + hret =ehea_h_reg_dereg_bcmc (port->adapter->handle, + port->logical_port_id, + reg_type, mc_mac_addr,0 , hcallid); +hcall_failed: +return hret; +} + +static int ehea_drop_multicast_list (struct net_device *dev) +{ +int ret =0 ; +u64 hret =H_HARDWARE ; +struct ehea_port *port = dev->priv; +struct ehea_mc_list *mc_entry = port->mc_list; +struct list_head *pos; +struct list_head *temp; + +EDEB_EN (7 ,"devname=%s" , dev->name); + +if (!list_empty (&mc_entry->list)) { +list_for_each_safe (pos, temp, &(port->mc_list->list)) { + mc_entry = list_entry(pos, struct ehea_mc_list, list); + +EDEB (7 ,"Deregistering MAC %lx" , mc_entry->macaddr); + + hret =ehea_multicast_reg_helper (port, + mc_entry->macaddr, +H_DEREG_BCMC ); +if (hret) { +EDEB_ERR (4 ,"Failed deregistering mcast MAC" ); + ret = -EINVAL ; + } + +list_del (pos); +kfree (mc_entry); + } + } + +EDEB_EX (7 ,"ret=%d" , ret); +return ret; +} + +static void ehea_allmulti (struct net_device *dev,int enable) +{ +u64 hret =H_HARDWARE ; +struct ehea_port *port = dev->priv; + +if (!port->allmulti) { +if (enable) { +/* Enable ALLMULTI */ +EDEB (7 ,"Enabling IFF_ALLMULTI" ); +ehea_drop_multicast_list (dev); + hret =ehea_multicast_reg_helper (port,0 ,H_REG_BCMC ); +if (!hret) + port->allmulti =EHEA_ENABLE ; +else +EDEB_ERR (4 ,"Enabling IFF_ALLMULTI failed!" ); + } + }else +if (!enable) { +/* Disable ALLMULTI */ +EDEB (7 ,"Disabling IFF_ALLMULTI" ); + hret =ehea_multicast_reg_helper (port,0 ,H_DEREG_BCMC ); +if (!hret) + port->allmulti =EHEA_DISABLE ; +else +EDEB_ERR (4 ,"Disabling IFF_ALLMULTI failed!" ); + } +} + +static void ehea_add_multicast_entry (struct ehea_port * port,u8 * mc_mac_addr) +{ +u64 hret =H_HARDWARE ; +struct ehea_mc_list *ehea_mcl_entry; + +EDEB_EN (7 ,"port=%p" , port); +EDEB_DMP (7 , mc_mac_addr,MAX_ADDR_LEN ,"dmi_addr" ); + + ehea_mcl_entry = + (struct ehea_mc_list *)kzalloc (sizeof (struct ehea_mc_list ), +GFP_KERNEL ); +if (!ehea_mcl_entry) { +EDEB_ERR (4 ,"Out of memory" ); +return ; + } + +INIT_LIST_HEAD (&ehea_mcl_entry->list); + +memcpy ((u8 *)&ehea_mcl_entry->macaddr, mc_mac_addr,ETH_ALEN ); + + hret =ehea_multicast_reg_helper (port, ehea_mcl_entry->macaddr, +H_REG_BCMC ); +if (!hret) +list_add (&ehea_mcl_entry->list, &port->mc_list->list); +else { +EDEB_ERR (4 ,"Failed registering mcast MAC" ); +kfree (ehea_mcl_entry); + } + +EDEB_EX (7 ,"" ); +} + +static void ehea_set_multicast_list (struct net_device *dev) +{ +int ret; +int i; +struct ehea_port *port = dev->priv; +struct dev_mc_list *k_mcl_entry; + +EDEB_EN (7 ,"devname=%s, mc_count=%d" , dev->name, dev->mc_count); + +if (dev->flags &IFF_PROMISC ) { +ehea_promiscuous (dev,EHEA_ENABLE ); +return ; + } +ehea_promiscuous (dev,EHEA_DISABLE ); + +if (dev->flags &IFF_ALLMULTI ) { +ehea_allmulti (dev,EHEA_ENABLE ); +return ; + } +ehea_allmulti (dev,EHEA_DISABLE ); + +EDEB (7 ,"Set individual multicast list" ); +if (dev->mc_count) { + ret =ehea_drop_multicast_list (dev); +if (ret) { +/* Dropping the current multicast list failed. +* Enabling ALL_MULTI is the best we can do. +*/ +ehea_allmulti (dev,EHEA_ENABLE ); + } + +if (dev->mc_count > port->adapter->max_mc_mac) { +EDEB_ERR (4 ,"Mcast registration limit reached " +"(0x%lx). Use ALLMULTI!" , + port->adapter->max_mc_mac); + goto escape; + } + +for (i =0 , k_mcl_entry = dev->mc_list; + i < dev->mc_count; + i++, k_mcl_entry = k_mcl_entry->next) { +ehea_add_multicast_entry (port, + k_mcl_entry->dmi_addr); + } + } + +escape: +EDEB_EX (7 ,"" ); +return ; +} + +static int ehea_change_mtu (struct net_device *dev,int new_mtu) +{ +if ((new_mtu <68 ) || (new_mtu >EHEA_MAX_PACKET_SIZE )) +return -EINVAL ; + dev->mtu = new_mtu; +return 0 ; +} + +static inline void ehea_xmit2 (struct sk_buff *skb, +struct net_device *dev,struct ehea_swqe *swqe) +{ +int nfrags; +unsigned short skb_protocol = skb->protocol; + nfrags =skb_shinfo (skb)->nr_frags; +EDEB_EN (7 ,"skb->nfrags=%d (0x%X)" , nfrags, nfrags); + +if (skb_protocol ==ETH_P_IP ) { +/* IPv4 */ + swqe->tx_control |=EHEA_SWQE_CRC + |EHEA_SWQE_IP_CHECKSUM + |EHEA_SWQE_TCP_CHECKSUM + |EHEA_SWQE_IMM_DATA_PRESENT + |EHEA_SWQE_DESCRIPTORS_PRESENT ; + +write_ip_start_end (swqe, skb); + +if (skb->nh.iph->protocol ==IPPROTO_UDP ) { +if ((skb->nh.iph->frag_off &IP_MF ) + || (skb->nh.iph->frag_off &IP_OFFSET )) +/* IP fragment, so don't change cs */ + swqe->tx_control &= ~EHEA_SWQE_TCP_CHECKSUM ; +else +write_udp_offset_end (swqe, skb); + + }else if (skb->nh.iph->protocol ==IPPROTO_TCP ) { +write_tcp_offset_end (swqe, skb); + } + +/* icmp (big data) and ip segmentation packets (all other ip +packets) do not require any special handling */ + + }else { +/* Other Ethernet Protocol */ + swqe->tx_control |=EHEA_SWQE_CRC + |EHEA_SWQE_IMM_DATA_PRESENT + |EHEA_SWQE_DESCRIPTORS_PRESENT ; + } + +write_swqe2_data (skb, dev, swqe); + +EDEB_EX (7 ,"" ); +} + +static inline void ehea_xmit3 (struct sk_buff *skb, +struct net_device *dev,struct ehea_swqe *swqe) +{ +int i; +skb_frag_t *frag; +int nfrags =skb_shinfo (skb)->nr_frags; +u8 *imm_data = &swqe->u.immdata_nodesc.immediate_data[0 ]; +u64 skb_protocol = skb->protocol; + +EDEB_EN (7 ,"" ); +if (likely (skb_protocol ==ETH_P_IP )) { +/* IPv4 */ +write_ip_start_end (swqe, skb); + +if (skb->nh.iph->protocol ==IPPROTO_TCP ) { + swqe->tx_control |=EHEA_SWQE_CRC + |EHEA_SWQE_IP_CHECKSUM + |EHEA_SWQE_TCP_CHECKSUM + |EHEA_SWQE_IMM_DATA_PRESENT ; + +write_tcp_offset_end (swqe, skb); + + }else if (skb->nh.iph->protocol ==IPPROTO_UDP ) { +if ((skb->nh.iph->frag_off &IP_MF ) + || (skb->nh.iph->frag_off &IP_OFFSET )) +/* IP fragment, so don't change cs */ + swqe->tx_control |=EHEA_SWQE_CRC + |EHEA_SWQE_IMM_DATA_PRESENT ; +else { + swqe->tx_control |=EHEA_SWQE_CRC + |EHEA_SWQE_IP_CHECKSUM + |EHEA_SWQE_TCP_CHECKSUM + |EHEA_SWQE_IMM_DATA_PRESENT ; +write_udp_offset_end (swqe, skb); + } + }else { +/* icmp (big data) and +ip segmentation packets (all other ip packets) */ + swqe->tx_control |=EHEA_SWQE_CRC + |EHEA_SWQE_IP_CHECKSUM + |EHEA_SWQE_IMM_DATA_PRESENT ; + } + }else { +/* Other Ethernet Protocol */ + swqe->tx_control |=EHEA_SWQE_CRC |EHEA_SWQE_IMM_DATA_PRESENT ; + } +/* copy (immediate) data */ +if (nfrags ==0 ) { +/* data is in a single piece */ +memcpy (imm_data, skb->data, skb->len); + }else { +/* first copy data from the skb->data buffer ... */ +memcpy (imm_data, skb->data, skb->len - skb->data_len); + imm_data += skb->len - skb->data_len; + +/* ... then copy data from the fragments */ +for (i =0 ; i < nfrags; i++) { + frag = &skb_shinfo (skb)->frags[i]; +memcpy (imm_data, +page_address (frag->page) + frag->page_offset, + frag->size); + imm_data += frag->size; + } + } + swqe->immediate_data_length = skb->len; +dev_kfree_skb (skb); + +EDEB_EX (7 ,"" ); +} + + +static int ehea_start_xmit (struct sk_buff *skb,struct net_device *dev) +{ +unsigned long flags; +struct ehea_port *port = (struct ehea_port *)dev->priv; +struct ehea_port_res *pr = &port->port_res[0 ]; +struct ehea_swqe *swqe; +EDEB_EN (7 ,"" ); +EDEB (6 ,"MACaddr=0x%X, dev=%p\n" +"PAGE_SIZE=%d MAX_SKB_FRAGS=%d" +"skb=%p skb->data=%p skb->len=%d skb->data_len=0x%x nr_frags=%d" , + *(u32 *)port->netdev->dev_addr, dev, + (int )PAGE_SIZE , + (int )MAX_SKB_FRAGS , + skb, + skb->data, skb->len, skb->data_len,skb_shinfo (skb)->nr_frags); + + +if (unlikely ((atomic_read (&pr->swqe_avail) ==5000 ))) { +tasklet_hi_schedule (&pr->send_comp_task); + } + +if (unlikely (atomic_read (&pr->swqe_avail) <=1 )) { +spin_lock_irqsave (&pr->netif_queue, flags); +if (unlikely (atomic_read (&pr->swqe_avail) <=1 )) { +EDEB (7 ,"netif_stop_queue to be called" ); +netif_stop_queue (dev); +spin_unlock_irqrestore (&pr->netif_queue, flags); +return NETDEV_TX_BUSY ; + } +spin_unlock_irqrestore (&pr->netif_queue, flags); + } +atomic_dec (&pr->swqe_avail); + + +EDEB_DMP (7 , (u8 *)skb->data, (skb->len - skb->data_len),"SKB_DATA" ); + + swqe =ehea_get_swqe (port->port_res[0 ].qp); +EDEB (7 ,"Posting SWQE on QP wit qp_nr=%X\n" , + port->port_res[0 ].qp->init_attr.qp_nr); + +memset (swqe,0 ,32 ); + +if (skb->len <=SWQE3_MAX_IMM ) { +u32 swqe_num = pr->swqe_id_counter; +ehea_xmit3 (skb, dev, swqe); + swqe->wr_id =EHEA_BMASK_SET (EHEA_WR_ID_REFILL ,EHEA_SIG_IV ) + |EHEA_BMASK_SET (EHEA_WR_ID_TYPE ,EHEA_SWQE3_TYPE ) + |EHEA_BMASK_SET (EHEA_WR_ID_COUNT , swqe_num); +if (pr->swqe_ll_count >= (EHEA_SIG_IV -1 )) { + swqe->tx_control |=EHEA_SWQE_SIGNALLED_COMPLETION ; + pr->swqe_ll_count =0 ; + }else + pr->swqe_ll_count +=1 ; +/* only for debugging purpose */ +EDEB_DMP (6 , (u8 *)swqe,128 ,"swqe format 3" ); + + }else { + swqe->wr_id = +EHEA_BMASK_SET (EHEA_WR_ID_REFILL ,EHEA_SIG_IV_LONG ) + |EHEA_BMASK_SET (EHEA_WR_ID_TYPE ,EHEA_SWQE2_TYPE ) + |EHEA_BMASK_SET (EHEA_WR_ID_COUNT , pr->swqe_id_counter) + |EHEA_BMASK_SET (EHEA_WR_ID_INDEX , pr->skb_sq_index); + pr->skb_arr_sq[pr->skb_sq_index] = skb; + pr->skb_sq_index = (pr->skb_sq_index +1 ) % pr->skb_arr_sq_len; +ehea_xmit2 (skb, dev, swqe); + +if (pr->swqe_count >= (EHEA_SIG_IV_LONG -1 )) { + swqe->tx_control |=EHEA_SWQE_SIGNALLED_COMPLETION ; + pr->swqe_count =0 ; + }else + pr->swqe_count +=1 ; +/* only for debugging purpose */ +EDEB_DMP (6 , (u8 *)swqe,128 ,"swqe format 2" ); + } + pr->swqe_id_counter +=1 ; + +if (port->vgrp &&vlan_tx_tag_present (skb)) { +EDEB (7 ,"VLAN TAG included" ); + swqe->tx_control |=EHEA_SWQE_VLAN_INSERT ; + swqe->vlan_tag =vlan_tx_tag_get (skb); + } +ehea_post_swqe (port->port_res[0 ].qp, swqe); + port->stats.tx_packets++; + +EDEB_EX (7 ,"" ); +return NETDEV_TX_OK ; +} + +static void ehea_vlan_rx_register (struct net_device *dev, +struct vlan_group *grp) +{ +u64 hret; +struct hcp_query_ehea_port_cb_1 *cb1 =NULL ; +struct ehea_port *port = (struct ehea_port *)dev->priv; +struct ehea_adapter *adapter = port->adapter; + +EDEB_EN (7 ,"net_device=%p port=%p adapter=%p" , dev, port, adapter); + + port->vgrp = grp; + + cb1 =kzalloc (H_CB_ALIGNMENT ,GFP_KERNEL ); +if (!cb1) { +EDEB_ERR (4 ,"No memory for cb1" ); + goto vlan_reg_exit; + } + +if (grp) +memset (cb1->vlan_filter,0 ,sizeof (cb1->vlan_filter)); +else +memset (cb1->vlan_filter,1 ,sizeof (cb1->vlan_filter)); + + hret =ehea_h_modify_ehea_port (adapter->handle, + port->logical_port_id, +H_PORT_CB1 , +H_PORT_CB1_ALL , + cb1); +if (hret !=H_SUCCESS ) { +EDEB_ERR (4 ,"query_ehea_port failed for cb3" ); + } +kfree (cb1); + +vlan_reg_exit: +EDEB_EN (7 ,"dev=%p, vlan_group=%p" , dev, grp); +} + +static void ehea_vlan_rx_add_vid (struct net_device *dev,unsigned short vid) +{ +int index; +u64 hret; +struct ehea_port *port = (struct ehea_port *)dev->priv; +struct hcp_query_ehea_port_cb_1 *cb1 =NULL ; +struct ehea_adapter *adapter = port->adapter; +EDEB_EN (7 ,"dev=%p, vlan_id=%d" , dev, vid); + + cb1 =kzalloc (H_CB_ALIGNMENT ,GFP_KERNEL ); +if (!cb1) { +EDEB_ERR (4 ,"No memory for cb1" ); + goto vlan_kill_exit; + } + hret =ehea_h_query_ehea_port (adapter->handle, + port->logical_port_id, +H_PORT_CB1 , +H_PORT_CB1_ALL , + cb1); + +if (hret !=H_SUCCESS ) { +EDEB_ERR (4 ,"query_ehea_port failed for cb1" ); + goto vlan_kill_exit; + } + + index = (vid /64 ); + cb1->vlan_filter[index] |= ((u64)(1 << (vid &0x3F ))); + + hret =ehea_h_modify_ehea_port (adapter->handle, + port->logical_port_id, +H_PORT_CB1 , +H_PORT_CB1_ALL , + cb1); +if (hret !=H_SUCCESS ) +EDEB_ERR (4 ,"modify_ehea_port failed for cb3" ); + +kfree (cb1); + +vlan_kill_exit: +EDEB_EX (7 ,"" ); + +} + +static void ehea_vlan_rx_kill_vid (struct net_device *dev,unsigned short vid) +{ +int index; +u64 hret; +struct ehea_port *port = (struct ehea_port *)dev->priv; +struct ehea_adapter *adapter = port->adapter; +struct hcp_query_ehea_port_cb_1 *cb1 =NULL ; +EDEB_EN (7 ,"dev=%p, vlan_id=%d" , dev, vid); +if (port->vgrp) + port->vgrp->vlan_devices[vid] =NULL ; + + cb1 =kzalloc (H_CB_ALIGNMENT ,GFP_KERNEL ); +if (!cb1) { +EDEB_ERR (4 ,"No memory for cb1" ); + goto vlan_kill_exit; + } + hret =ehea_h_query_ehea_port (adapter->handle, + port->logical_port_id, +H_PORT_CB1 , +H_PORT_CB1_ALL , + cb1); + +if (hret !=H_SUCCESS ) { +EDEB_ERR (4 ,"query_ehea_port failed for cb1" ); + goto vlan_kill_exit; + } + + index = (vid /64 ); + cb1->vlan_filter[index] &= ~((u64)(1 << (vid &0x3F ))); + + hret =ehea_h_modify_ehea_port (adapter->handle, + port->logical_port_id, +H_PORT_CB1 , +H_PORT_CB1_ALL , + cb1); +if (hret !=H_SUCCESS ) +EDEB_ERR (4 ,"modify_ehea_port failed for cb3" ); +kfree (cb1); + +vlan_kill_exit: +EDEB_EX (7 ,"" ); +} + +int ehea_stop_qp (struct ehea_adapter *adapter,struct ehea_qp *qp) +{ +int ret = -EINVAL ; +u64 hret =H_HARDWARE ; +u16 dummy16; +u64 dummy64; +struct hcp_modify_qp_cb_0 *cb0 =NULL ; + +EDEB_EN (7 ,"qp=%p" , qp); + + cb0 =kzalloc (H_CB_ALIGNMENT ,GFP_KERNEL ); + +if (!cb0) { +EDEB_ERR (4 ,"No memory for modify_qp control block" ); + ret = -ENOMEM ; + goto kzalloc_failed; + } + +/* Reset queue pair */ + cb0->qp_ctl_reg =H_QP_CR_ENABLED |H_QP_CR_STATE_RESET ; + hret =ehea_h_modify_ehea_qp (adapter->handle, +0 , + qp->ipz_qp_handle, +EHEA_BMASK_SET (H_QPCB0_QP_CTL_REG ,1 ), + cb0, + &dummy64, &dummy64, &dummy16, &dummy16); +if (hret !=H_SUCCESS ) { +EDEB_ERR (4 ,"Failed resetting the queue pair" ); + goto modify_qp_failed; + } + +/* Disable queue pair */ + cb0->qp_ctl_reg =H_QP_CR_STATE_RESET ; + hret =ehea_h_modify_ehea_qp (adapter->handle, +0 , + qp->ipz_qp_handle, +EHEA_BMASK_SET (H_QPCB0_QP_CTL_REG ,1 ), + cb0, + &dummy64, &dummy64, &dummy16, &dummy16); +if (hret !=H_SUCCESS ) { +EDEB_ERR (4 ,"Failed disabling the queue pair" ); + goto modify_qp_failed; + } + + ret =0 ; + +modify_qp_failed: +kfree (cb0); + +kzalloc_failed: +return ret; +} + +int ehea_activate_qp (struct ehea_adapter *adapter,struct ehea_qp *qp) +{ +int ret = -EINVAL ; +u64 hret =H_HARDWARE ; +u16 dummy16 =0 ; +u64 dummy64 =0 ; +struct hcp_modify_qp_cb_0 * cb0 =NULL ; + +EDEB_EN (7 ,"qp=%p" , qp); + + cb0 =kzalloc (H_CB_ALIGNMENT ,GFP_KERNEL ); +if (!cb0) { +EDEB_ERR (4 ,"No mem to allocate control block" ); + ret = -ENOMEM ; + goto failure; + } + + hret =ehea_h_query_ehea_qp (adapter->handle, +0 , qp->ipz_qp_handle, +EHEA_BMASK_SET (H_QPCB0_ALL ,0xFFFF ), cb0); +if (hret !=H_SUCCESS ) { +EDEB_ERR (4 ,"query_ehea_qp failed (1)\n" ); + goto failure; + } + + cb0->qp_ctl_reg =H_QP_CR_STATE_INITIALIZED ; + hret =ehea_h_modify_ehea_qp (adapter->handle, +0 , qp->ipz_qp_handle, +EHEA_BMASK_SET (H_QPCB0_QP_CTL_REG ,1 ), cb0, + &dummy64, &dummy64, &dummy16, &dummy16); +if (hret !=H_SUCCESS ) { +EDEB_ERR (4 ,"modify_ehea_qp failed (1)\n" ); + goto failure; + } + + hret =ehea_h_query_ehea_qp (adapter->handle, +0 , qp->ipz_qp_handle, +EHEA_BMASK_SET (H_QPCB0_ALL ,0xFFFF ), cb0); +if (hret !=H_SUCCESS ) { +EDEB_ERR (4 ,"query_ehea_qp failed (2)\n" ); + goto failure; + } + + cb0->qp_ctl_reg =H_QP_CR_ENABLED |H_QP_CR_STATE_INITIALIZED ; + hret =ehea_h_modify_ehea_qp (adapter->handle, +0 , qp->ipz_qp_handle, +EHEA_BMASK_SET (H_QPCB0_QP_CTL_REG ,1 ), cb0, + &dummy64, &dummy64, &dummy16, &dummy16); +if (hret !=H_SUCCESS ) { +EDEB_ERR (4 ,"modify_ehea_qp failed (2)\n" ); + goto failure; + } + + hret =ehea_h_query_ehea_qp (adapter->handle, +0 , qp->ipz_qp_handle, +EHEA_BMASK_SET (H_QPCB0_ALL ,0xFFFF ), cb0); +if (hret !=H_SUCCESS ) { +EDEB_ERR (4 ,"query_ehea_qp failed (3)\n" ); + goto failure; + } + + cb0->qp_ctl_reg =H_QP_CR_ENABLED |H_QP_CR_STATE_RDY2SND ; + hret =ehea_h_modify_ehea_qp (adapter->handle, +0 , qp->ipz_qp_handle, +EHEA_BMASK_SET (H_QPCB0_QP_CTL_REG ,1 ), cb0, + &dummy64, &dummy64, &dummy16, &dummy16); +if (hret !=H_SUCCESS ) { +EDEB_ERR (4 ,"modify_ehea_qp failed (3)\n" ); + goto failure; + } + + hret =ehea_h_query_ehea_qp (adapter->handle, +0 , qp->ipz_qp_handle, +EHEA_BMASK_SET (H_QPCB0_ALL ,0xFFFF ), cb0); +if (hret !=H_SUCCESS ) { +EDEB_ERR (4 ,"query_ehea_qp failed (4)\n" ); + goto failure; + } + + ret =0 ; + +failure: +kfree (cb0); +EDEB_EX (7 ,"ret=%d" , ret); +return ret; +} + +int ehea_open (struct net_device *dev) +{ +u64 hret =H_HARDWARE ; +int ret = -EINVAL ; +struct ehea_port *port = (struct ehea_port *)dev->priv; +struct ehea_qp *my_qp =NULL ; +u64 mac_addr =0 ; + +EDEB_EN (7 ,"logical_port_id=%d" , port->logical_port_id); + + ret =ehea_init_port_res (port, &port->port_res[0 ]); +if (ret) + goto init_port_res_failed; + + hret =ehea_broadcast_reg_helper (port,H_REG_BCMC ); +if (hret) + goto reg_failed; + my_qp = port->port_res[0 ].qp; + mac_addr = (*(u64 *)dev->dev_addr) >>16 ; + + ret =ehea_reg_interrupts (dev); +if (ret) + goto reg_failed; + + ret =ehea_activate_qp (port->adapter, my_qp); +if (ret) + goto activate_qp_failed; + +ehea_fill_port_res (&port->port_res[0 ]); +netif_start_queue (dev); + ret =0 ; + goto done; + +activate_qp_failed: +ehea_free_interrupts (dev); + +reg_failed: +ehea_clean_port_res (port, &port->port_res[0 ]); + +init_port_res_failed: +done: +EDEB_EX (7 ,"ret=%d" , ret); +return ret; +} + +static int ehea_stop (struct net_device *dev) +{ +struct ehea_port *port = (struct ehea_port *)dev->priv; + +EDEB_EN (7 ,"" ); +netif_stop_queue (dev); +ehea_drop_multicast_list (dev); +ehea_stop_qp (port->adapter, port->port_res[0 ].qp); +ehea_free_interrupts (dev); + +ehea_broadcast_reg_helper (port,H_DEREG_BCMC ); +ehea_clean_port_res (port, &port->port_res[0 ]); + +EDEB_EX (7 ,"" ); +return 0 ; +} + +static struct of_device_id ehea_device_table[] = { + { + .name ="lhea" , + .compatible ="IBM,lhea" , + }, + {}, +}; + +static struct ibmebus_driver ehea_driver = { + .name ="ehea" , + .id_table = ehea_device_table, + .probe = ehea_probe, + .remove = ehea_remove, +}; + +int ehea_sense_adapter_attr (struct ehea_adapter *adapter) +{ +int ret = -EINVAL ; +u64 hret =H_HARDWARE ; +struct hcp_query_ehea *query_ehea_cb =NULL ; + +EDEB_EN (7 ,"" ); + + query_ehea_cb =kzalloc (H_CB_ALIGNMENT ,GFP_KERNEL ); + +if (!query_ehea_cb) { +EDEB_ERR (4 ,"No memory for query_ehea control block" ); + ret = -ENOMEM ; + goto kzalloc_failed; + } + + hret =ehea_h_query_ehea (adapter->handle, query_ehea_cb); +if (hret !=H_SUCCESS ) { + goto query_ehea_failed; + } + + adapter->num_ports = query_ehea_cb->num_ports; + adapter->max_mc_mac = query_ehea_cb->max_mc_mac -1 ; + + ret =0 ; + +query_ehea_failed: +kfree (query_ehea_cb); + +kzalloc_failed: +EDEB_EX (7 ,"ret=%d, num_ports=%d, max_mc_mac=0x%lx" , + ret, adapter->num_ports, adapter->max_mc_mac); +return ret; +} + +static int ehea_sense_port_attr (struct ehea_adapter *adapter,int portnum) +{ +int ret = -EINVAL ; +u64 hret =H_HARDWARE ; +struct ehea_port *port = adapter->port[portnum]; +struct hcp_query_ehea_port_cb_0 *query_ehea_port_cb_0 =NULL ; + +EDEB_EN (7 ,"adapter=%p, portnum=%d" , adapter, portnum); + +/* allocate memory for control block */ + query_ehea_port_cb_0 = +kzalloc (sizeof (*query_ehea_port_cb_0),GFP_KERNEL ); + +if (!query_ehea_port_cb_0) { +EDEB_ERR (4 ,"No memory for query_ehea_port control block" ); + ret = -ENOMEM ; + goto kzalloc_failed; + } + + hret =ehea_h_query_ehea_port (adapter->handle, + adapter->port[portnum]->logical_port_id, +H_PORT_CB0 , +EHEA_BMASK_SET (H_PORT_CB0_ALL ,0xFFFF ), + query_ehea_port_cb_0); + +if (hret !=H_SUCCESS ) { + ret = -EPERM ; + goto query_ehea_port_failed; + } + +EDEB_DMP (7 , (u8 *) query_ehea_port_cb_0, +sizeof (struct hcp_query_ehea_port_cb_0 ),"After HCALL" ); + + +/* MAC address */ + port->mac_addr = query_ehea_port_cb_0->port_mac_addr <<16 ; + +/* Port speed */ +switch (query_ehea_port_cb_0->port_speed) { +case H_PORT_SPEED_10M_H : + port->port_speed =EHEA_SPEED_10M ; + port->full_duplex =0 ; +break ; +case H_PORT_SPEED_10M_F : + port->port_speed =EHEA_SPEED_10M ; + port->full_duplex =1 ; +break ; +case H_PORT_SPEED_100M_H : + port->port_speed =EHEA_SPEED_100M ; + port->full_duplex =0 ; +break ; +case H_PORT_SPEED_100M_F : + port->port_speed =EHEA_SPEED_100M ; + port->full_duplex =1 ; +break ; +case H_PORT_SPEED_1G_F : + port->port_speed =EHEA_SPEED_1G ; + port->full_duplex =1 ; +break ; +case H_PORT_SPEED_10G_F : + port->port_speed =EHEA_SPEED_10G ; + port->full_duplex =1 ; +break ; +default : + port->port_speed =0 ; + port->full_duplex =0 ; +break ; + } + +/* Number of default QPs */ + port->num_def_qps = query_ehea_port_cb_0->num_default_qps; + +EDEB (7 ,"MAC address=0x%lX" , adapter->port[portnum]->mac_addr >>16 ); + + ret =0 ; + +query_ehea_port_failed: +kfree (query_ehea_port_cb_0); + +kzalloc_failed: +EDEB_EX (7 ,"MACaddr=0x%lX, portspeed=%dMbps, fullduplex=%d, " +"num_def_qps=%d" , + port->mac_addr >>16 , + port->port_speed, port->full_duplex, port->num_def_qps); +return ret; +} + +static int ehea_setup_single_port (struct ehea_adapter *adapter, +int portnum,struct device_node *dn) +{ +int ret = -EINVAL ; +u64 hret; +struct ehea_port *port =NULL ; +struct net_device *dev =NULL ; +struct hcp_query_ehea_port_cb_4 *cb4; +u32 *dn_log_port_id =NULL ; + +EDEB_EN (7 ,"Initializing port #%d. adapter=%p" , portnum, adapter); + + port = adapter->port[portnum]; + +if (!dn) { +EDEB_ERR (4 ,"Bad device node: dn=%p" , dn); + goto done; + } + + port->of_dev_node = dn; + +/* Determine logical port id */ + dn_log_port_id = (u32 *)get_property (dn,"ibm,hea-port-no" ,NULL ); + +if (!dn_log_port_id) { +EDEB_ERR (4 ,"Bad device node: dn_log_port_id=%p" , + dn_log_port_id); + goto done; + } + port->logical_port_id = *dn_log_port_id; + port->adapter = adapter; + port->mc_list = + (struct ehea_mc_list *)kzalloc (sizeof (struct ehea_mc_list ), +GFP_KERNEL ); +if (!port->mc_list) { +EDEB_ERR (4 ,"No memory for multicast list" ); + goto done; + } + +INIT_LIST_HEAD (&port->mc_list->list); + + ret =ehea_sense_port_attr (adapter, portnum); +if (ret) + goto done; + +/* initialize net_device structure */ + dev =alloc_etherdev (sizeof (struct ehea_port )); + +if (!dev) { +EDEB_ERR (4 ,"No memory for net_device" ); + ret = -ENOMEM ; + goto done; + } + + cb4 =kzalloc (H_CB_ALIGNMENT ,GFP_KERNEL ); +if (!cb4) { +EDEB_ERR (4 ,"No memory for cb4" ); + }else { + cb4->jumbo_frame =1 ; + hret =ehea_h_modify_ehea_port (adapter->handle, + port->logical_port_id, +H_PORT_CB4 , +H_PORT_CB4_JUMBO , + (void *)cb4); +if (hret !=H_SUCCESS ) +EDEB (4 ,"Jumbo frames not activated" ); + } + +SET_MODULE_OWNER (dev); + +memcpy (dev->dev_addr, &port->mac_addr,ETH_ALEN ); + + dev->open = ehea_open; + dev->poll = ehea_poll; + dev->weight =64 ; + dev->stop = ehea_stop; + dev->hard_start_xmit = ehea_start_xmit; + dev->get_stats = ehea_get_stats; + dev->set_multicast_list = ehea_set_multicast_list; + dev->do_ioctl = ehea_ioctl; + dev->set_mac_address = ehea_set_mac_addr; + dev->change_mtu = ehea_change_mtu; + dev->priv = port; + + dev->features =NETIF_F_SG |NETIF_F_FRAGLIST |NETIF_F_TSO + |NETIF_F_HIGHDMA |NETIF_F_HW_CSUM |NETIF_F_HW_VLAN_TX + |NETIF_F_HW_VLAN_RX |NETIF_F_HW_VLAN_FILTER ; + + dev->vlan_rx_register = ehea_vlan_rx_register; + dev->vlan_rx_add_vid = ehea_vlan_rx_add_vid; + dev->vlan_rx_kill_vid = ehea_vlan_rx_kill_vid; + +ehea_set_ethtool_ops (dev); + + ret =register_netdev (dev); +if (ret) { +EDEB_ERR (4 ,"register_netdev failed. ret=%d" , ret); + goto reg_netdev_failed; + } + + port->netdev = dev; + + ret =0 ; + + goto done; + +reg_netdev_failed: +free_netdev (port->netdev); +kfree (port->mc_list); + +done: +EDEB_EX (7 ,"logical port id=0x%x" , port->logical_port_id); +return ret; +} + +static int ehea_setup_ports (struct ehea_adapter *adapter) +{ +int ret = -EINVAL ; +int i; +int port_setup_ok =0 ; +struct ehea_port *port =NULL ; +struct device_node *dn =NULL ; + +EDEB_EX (7 ,"" ); + +/* get port properties for all ports */ +for (i =0 ; i < adapter->num_ports; i++) { + +if (adapter->port[i]) +continue ;/* port already up and running */ + +/* allocate memory for the port structures */ + port =kzalloc (sizeof (struct ehea_port ),GFP_KERNEL ); + +if (!port) { +EDEB_ERR (4 ,"No memory for ehea_port" ); +break ;/* continuing not reasonable */ + } + + adapter->port[i] = port; + dn =of_find_node_by_name (dn,"ethernet" ); + ret =ehea_setup_single_port (adapter, i, dn); + +if (ret) { +/* Free mem for this port struct. the others will be +processed on rollback */ +kfree (port); + adapter->port[i] =NULL ; + } + } + +of_node_put (dn); + +/* Check for succesfully set up ports */ +for (i =0 ; i < adapter->num_ports; i++) +if (adapter->port[i]) + port_setup_ok++; + +if (port_setup_ok >0 ) + ret =0 ;/* At least some ports are setup correctly */ + +EDEB_EX (7 ,"" ); +return ret; +} + +static int __devinit ehea_probe (struct ibmebus_dev *dev, +const struct of_device_id *id) +{ +int ret = -EINVAL ; +struct ehea_adapter *adapter =NULL ; +u64 *adapter_handle =NULL ; + +EDEB_EN (7 ,"ibmebus_dev=%p, of_device_id=%p" , dev, id); + + adapter =kzalloc (sizeof (*adapter),GFP_KERNEL ); + +if (!adapter) { +EDEB_ERR (4 ,"No memory for ehea_adapter" ); + ret = -ENOMEM ; + goto kzalloc_adapter_failed; + } + + adapter_handle = (u64 *)get_property (dev->ofdev.node,"ibm,hea-handle" , +NULL ); + +if (!adapter_handle) { +EDEB_ERR (4 ,"Failed getting handle for adapter '%s'" , + dev->ofdev.node->full_name); + ret = -ENODEV ; + goto get_property_failed; + } + + adapter->handle = *adapter_handle; + adapter->pd =EHEA_PD_ID ; + + dev->ofdev.dev.driver_data = adapter; + ret =ehea_reg_mr_adapter (adapter); +if (ret) + goto register_mr_failed; + +/* initialize adapter and ports */ +/* get adapter properties */ + ret =ehea_sense_adapter_attr (adapter); +if (ret) + goto sense_adapter_failed; + +/* create notification event queue +* has te be registered, handler not available yet +*/ + + adapter->neq =ehea_create_eq (adapter, +EHEA_NEQ ,EHEA_MAX_ENTRIES_EQ ,1 ); +if (!adapter->neq) + goto create_neq_failed; + +tasklet_init (&adapter->neq_tasklet, ehea_neq_tasklet, + (unsigned long )adapter); + + ret =ibmebus_request_irq (NULL , adapter->neq->attr.ist1, + ehea_interrupt_neq,SA_INTERRUPT ,"ehea_neq" , + (void *)adapter); +if (ret) + goto request_irq_failed; + + ret =ehea_setup_ports (adapter); +if (ret) + goto setup_ports_failed; + + ret =0 ; + goto done; + +setup_ports_failed: +request_irq_failed: +ehea_destroy_eq (adapter, adapter->neq); + +create_neq_failed: +sense_adapter_failed: +ehea_dereg_mr_adapter (adapter); +register_mr_failed: +kfree (adapter); + +get_property_failed: +kzalloc_adapter_failed: +done: +EDEB_EX (7 ,"" ); +return ret; +} + +int __init ehea_module_init (void ) +{ +int ret = -EINVAL ; + +EDEB_EN (7 ,"" ); + +printk (KERN_INFO "%s Ethernet Device Driver (Release %s)\n" , +EHEA_DRIVER_NAME , +EHEA_DRIVER_VERSION ); + + ret =ibmebus_register_driver (&ehea_driver); +if (ret) { +EDEB_ERR (4 ,"Failed registering eHEA device driver on ebus" ); +return -EINVAL ; + } + +EDEB_EX (7 ,"" ); +return 0 ; +} + +static void ehea_shutdown_single_port (struct ehea_adapter *adapter,int portnum) +{ +struct ehea_port *port = adapter->port[portnum]; + +EDEB_EN (7 ,"" ); + +if (port) { +EDEB (7 ,"Shutting down port #%d." , portnum); +unregister_netdev (port->netdev); +free_netdev (port->netdev); +kfree (port->mc_list); +kfree (port); + adapter->port[portnum] =NULL ; + }else +EDEB (7 ,"Port #%d not setup. Doing nothing." , portnum); + +EDEB_EX (7 ,"" ); +} + +static int __devexit ehea_remove(struct ibmebus_dev *dev) +{ +int ret = -EINVAL ; +struct ehea_adapter *adapter = dev->ofdev.dev.driver_data; +int i; + +EDEB_EN (7 ,"ibmebus_dev=%p" , dev); + +for (i =0 ; i < adapter->num_ports; i++) +ehea_shutdown_single_port (adapter, i); + +ibmebus_free_irq (NULL , adapter->neq->attr.ist1, adapter); + +ehea_destroy_eq (adapter, adapter->neq); + + ret =ehea_dereg_mr_adapter (adapter); +if (ret) + goto deregister_mr_failed; +kfree (adapter); + ret =0 ; + +deregister_mr_failed: +EDEB_EX (7 ,"" ); +return ret; +} + +static void __exit ehea_module_exit(void) +{ +EDEB_EN (7 ,"" ); + +ibmebus_unregister_driver (&ehea_driver); +EDEB_EX (7 ,"" ); +} + +module_init (ehea_module_init); +module_exit (ehea_module_exit);