* [PATCH 3/6] ehea: queue management
From: Jan-Bernd Themann @ 2006-06-21 12:03 UTC (permalink / raw)
To: netdev; +Cc: meder, raisch, schickhj, themann, tklein
Signed-off-by: Jan-Bernd Themann <themann@de.ibm.com>
drivers/net/ehea/ehea_ethtool.c | 325 ++++++++++++++++++
drivers/net/ehea/ehea_qmr.c | 719 ++++++++++++++++++++++++++++++++++++++++
drivers/net/ehea/ehea_qmr.h | 390 +++++++++++++++++++++
3 files changed, 1434 insertions(+)
--- linux-2.6.17.1-orig/drivers/net/ehea/ehea_qmr.c 1969-12-31 16:00:00.000000000 -0800
+++ kernel/drivers/net/ehea/ehea_qmr.c 2006-06-21 04:44:50.152486432 -0700
@@ -0,0 +1,719 @@
+/*
+ * linux/drivers/net/ehea/ehea_qmr.c
+ *
+ * eHEA ethernet device driver for IBM eServer System p
+ *
+ * (C) Copyright IBM Corp. 2006
+ *
+ * Authors:
+ * Christoph Raisch <raisch@de.ibm.com>
+ * Jan-Bernd Themann <themann@de.ibm.com>
+ * Heiko-Joerg Schick <schickhj@de.ibm.com>
+ * Thomas Klein <tklein@de.ibm.com>
+ *
+ *
+ * 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.
+ */
+
+#include "ehea.h"
+#include "ehea_phyp.h"
+#include "ehea_qmr.h"
+
+static void *ipz_qpageit_get_inc(struct ipz_queue *queue)
+{
+ void *retvalue = ipz_qeit_get(queue);
+ queue->current_q_offset += queue->pagesize;
+ if (queue->current_q_offset > queue->queue_length) {
+ queue->current_q_offset -= queue->pagesize;
+ retvalue = NULL;
+ }
+ else if ((((u64) retvalue) & (EHEA_PAGESIZE-1)) != 0) {
+ EDEB(4, "ERROR!! not at PAGE-Boundary");
+ return NULL;
+ }
+ EDEB(7, "queue=%p retvalue=%p", queue, retvalue);
+ return retvalue;
+}
+
+static int ipz_queue_ctor(struct ipz_queue *queue,
+ const u32 nr_of_pages,
+ const u32 pagesize, const u32 qe_size,
+ const u32 nr_of_sg)
+{
+ int f;
+ EDEB_EN(7, "nr_of_pages=%x pagesize=%x qe_size=%x",
+ nr_of_pages, pagesize, qe_size);
+ queue->queue_length = nr_of_pages * pagesize;
+ queue->queue_pages = vmalloc(nr_of_pages * sizeof(void *));
+ if (!queue->queue_pages) {
+ EDEB(4, "ERROR!! didn't get the memory");
+ return 0;
+ }
+ memset(queue->queue_pages, 0, nr_of_pages * sizeof(void *));
+
+ for (f = 0; f < nr_of_pages; f++) {
+ (queue->queue_pages)[f] =
+ (struct ipz_page *)get_zeroed_page(GFP_KERNEL);
+ if (!(queue->queue_pages)[f]) {
+ break;
+ }
+ }
+ if (f < nr_of_pages) {
+ int g;
+ EDEB_ERR(4, "couldn't get 0ed pages queue=%p f=%x "
+ "nr_of_pages=%x", queue, f, nr_of_pages);
+ for (g = 0; g < f; g++) {
+ free_page((unsigned long)(queue->queue_pages)[g]);
+ }
+ return 0;
+ }
+ queue->current_q_offset = 0;
+ queue->qe_size = qe_size;
+ queue->act_nr_of_sg = nr_of_sg;
+ queue->pagesize = pagesize;
+ queue->toggle_state = 1;
+ EDEB_EX(7, "queue_length=%x queue_pages=%p qe_size=%x"
+ " act_nr_of_sg=%x", queue->queue_length, queue->queue_pages,
+ queue->qe_size, queue->act_nr_of_sg);
+ return 1;
+}
+
+static int ipz_queue_dtor(struct ipz_queue *queue)
+{
+ int g;
+ EDEB_EN(7, "ipz_queue pointer=%p", queue);
+ if (!queue) {
+ return 0;
+ }
+ if (!queue->queue_pages) {
+ return 0;
+ }
+ EDEB(7, "destructing a queue with the following properties:\n"
+ "queue_length=%x act_nr_of_sg=%x pagesize=%x qe_size=%x",
+ queue->queue_length, queue->act_nr_of_sg, queue->pagesize,
+ queue->qe_size);
+ for (g = 0; g < (queue->queue_length / queue->pagesize); g++) {
+ free_page((unsigned long)(queue->queue_pages)[g]);
+ }
+ vfree(queue->queue_pages);
+
+ EDEB_EX(7, "queue freed!");
+ return 1;
+}
+
+struct ehea_cq *ehea_cq_new(void)
+{
+ struct ehea_cq *cq = vmalloc(sizeof(*cq));
+ if (cq)
+ memset(cq, 0, sizeof(*cq));
+ return cq;
+}
+
+void ehea_cq_delete(struct ehea_cq *cq)
+{
+ vfree(cq);
+}
+
+struct ehea_cq *ehea_create_cq(struct ehea_adapter *adapter,
+ int nr_of_cqe, u64 eq_handle, u32 cq_token)
+{
+ struct ehea_cq *cq = NULL;
+ struct h_galpa gal;
+
+ u64 *cq_handle_ref;
+ u32 act_nr_of_entries;
+ u32 act_pages;
+ u64 hret;
+ int ipz_rc;
+ u32 counter;
+ void *vpage = NULL;
+ u64 rpage = 0;
+
+ EDEB_EN(7, "adapter=%p nr_of_cqe=%x , eq_handle: %016lX",
+ adapter, nr_of_cqe, eq_handle);
+
+ cq = ehea_cq_new();
+ if (!cq) {
+ cq = NULL;
+ EDEB_ERR(4, "ehea_create_cq ret=%p (-ENOMEM)", cq);
+ goto create_cq_exit0;
+ }
+
+ cq->attr.max_nr_of_cqes = nr_of_cqe;
+ cq->attr.cq_token = cq_token;
+ cq->attr.eq_handle = eq_handle;
+
+ cq->adapter = adapter;
+
+ cq_handle_ref = &cq->ipz_cq_handle;
+ act_nr_of_entries = 0;
+ act_pages = 0;
+
+ hret = ehea_h_alloc_resource_cq(adapter->handle,
+ cq,
+ &cq->attr,
+ &cq->ipz_cq_handle, &cq->galpas);
+ if (hret != H_SUCCESS) {
+ EDEB_ERR(4, "ehea_h_alloc_resource_cq failed. hret=%lx", hret);
+ goto create_cq_exit1;
+ }
+
+ ipz_rc = ipz_queue_ctor(&cq->ipz_queue, cq->attr.nr_pages,
+ EHEA_PAGESIZE, sizeof(struct ehea_cqe), 0);
+ if (!ipz_rc)
+ goto create_cq_exit2;
+
+ hret = H_SUCCESS;
+
+ for (counter = 0; counter < cq->attr.nr_pages; counter++) {
+ vpage = ipz_qpageit_get_inc(&cq->ipz_queue);
+ if (!vpage) {
+ EDEB_ERR(4, "ipz_qpageit_get_inc() "
+ "returns NULL adapter=%p", adapter);
+ goto create_cq_exit3;
+ }
+
+ rpage = virt_to_abs(vpage);
+
+ hret = ehea_h_register_rpage_cq(adapter->handle,
+ cq->ipz_cq_handle,
+ 0,
+ HIPZ_CQ_REGISTER_ORIG,
+ rpage, 1, cq->galpas.kernel);
+
+ if (hret < H_SUCCESS) {
+ EDEB_ERR(4, "ehea_h_register_rpage_cq() failed "
+ "ehea_cq=%p hret=%lx "
+ "counter=%i act_pages=%i",
+ cq, hret, counter, cq->attr.nr_pages);
+ goto create_cq_exit3;
+ }
+
+ if (counter == (cq->attr.nr_pages - 1)) {
+ vpage = ipz_qpageit_get_inc(&cq->ipz_queue);
+
+ if ((hret != H_SUCCESS) || (vpage)) {
+ EDEB_ERR(4, "Registration of pages not "
+ "complete ehea_cq=%p hret=%lx",
+ cq, hret)
+ goto create_cq_exit3;
+ }
+ } else {
+ if ((hret != H_PAGE_REGISTERED) || (vpage == 0)) {
+ EDEB_ERR(4, "Registration of page failed "
+ "ehea_cq=%p hret=%lx"
+ "counter=%i act_pages=%i",
+ cq, hret, counter, cq->attr.nr_pages);
+ goto create_cq_exit3;
+ }
+ }
+ }
+
+ ipz_qeit_reset(&cq->ipz_queue);
+ gal = cq->galpas.kernel;
+ ehea_reset_cq_ep(cq);
+ ehea_reset_cq_n1(cq);
+
+ EDEB_EX(7, "ret=%p ", cq);
+ return cq;
+
+create_cq_exit3:
+ ipz_queue_dtor(&cq->ipz_queue);
+
+create_cq_exit2:
+ hret = ehea_h_destroy_cq(adapter->handle, cq, cq->ipz_cq_handle,
+ &cq->galpas);
+ EDEB(7, "return code of ehea_cq_destroy=%lx", hret);
+
+create_cq_exit1:
+ ehea_cq_delete(cq);
+
+create_cq_exit0:
+ EDEB_EX(7, "ret=NULL");
+ return NULL;
+}
+
+int ehea_destroy_cq(struct ehea_cq *cq)
+{
+ int ret = 0;
+ u64 adapter_handle;
+ u64 hret;
+
+ adapter_handle = cq->adapter->handle;
+ EDEB_EN(7, "adapter=%p cq=%p", cq->adapter, cq);
+
+ /* deregister all previous registered pages */
+ hret = ehea_h_destroy_cq(adapter_handle, cq, cq->ipz_cq_handle,
+ &cq->galpas);
+ if (hret != H_SUCCESS) {
+ EDEB_ERR(4, "destroy CQ failed!");
+ return -EINVAL;
+ }
+ ipz_queue_dtor(&cq->ipz_queue);
+ ehea_cq_delete(cq);
+
+ EDEB_EX(7, "ret=%x ", ret);
+ return ret;
+}
+
+struct ehea_eq *ehea_eq_new(void)
+{
+ struct ehea_eq *eq = vmalloc(sizeof(*eq));
+ if (eq)
+ memset(eq, 0, sizeof(*eq));
+ return eq;
+}
+
+void ehea_eq_delete(struct ehea_eq *eq)
+{
+ vfree(eq);
+}
+
+struct ehea_eq *ehea_create_eq(struct ehea_adapter *adapter,
+ const enum ehea_eq_type type,
+ const u32 max_nr_of_eqes, const u8 eqe_gen)
+{
+ u64 hret = H_HARDWARE;
+ int ret = 0;
+ u32 i;
+ void *vpage = NULL;
+ struct ehea_eq *eq;
+
+ EDEB_EN(7, "adapter=%p, max_nr_of_eqes=%x", adapter, max_nr_of_eqes);
+
+ eq = ehea_eq_new();
+ if (!eq)
+ return NULL;
+
+ eq->attr.type = type;
+ eq->attr.max_nr_of_eqes = max_nr_of_eqes;
+ eq->attr.eqe_gen = eqe_gen;
+ spin_lock_init(&eq->spinlock);
+
+ hret = ehea_h_alloc_resource_eq(adapter->handle,
+ eq, &eq->attr, &eq->ipz_eq_handle);
+
+ if (hret != H_SUCCESS) {
+ EDEB_ERR(4, "ehea_h_alloc_resource_eq failed. hret=%lx", hret);
+ goto free_eq_mem;
+ }
+
+ ret = ipz_queue_ctor(&eq->ipz_queue, eq->attr.nr_pages,
+ EHEA_PAGESIZE, sizeof(struct ehea_eqe), 0);
+ if (!ret) {
+ EDEB_ERR(4, "can't allocate EQ pages");
+ goto alloc_pages_failed;
+ }
+
+ for (i = 0; i < eq->attr.nr_pages; i++) {
+ u64 rpage;
+
+ if (!(vpage = ipz_qpageit_get_inc(&eq->ipz_queue))) {
+ hret = H_RESOURCE;
+ goto register_page_failed;
+ }
+
+ rpage = virt_to_abs(vpage);
+
+ hret = ehea_h_register_rpage_eq(adapter->handle,
+ eq->ipz_eq_handle,
+ 0,
+ HIPZ_EQ_REGISTER_ORIG,
+ rpage, 1);
+
+ if (i == (eq->attr.nr_pages - 1)) {
+ /* last page */
+ vpage = ipz_qpageit_get_inc(&eq->ipz_queue);
+ if ((hret != H_SUCCESS) || (vpage)) {
+ goto register_page_failed;
+ }
+ } else {
+ if ((hret != H_PAGE_REGISTERED) || (!vpage)) {
+ goto register_page_failed;
+ }
+ }
+ }
+
+ ipz_qeit_reset(&eq->ipz_queue);
+
+ EDEB_EX(7, "hret=%lx", hret);
+ return eq;
+
+register_page_failed:
+ ipz_queue_dtor(&eq->ipz_queue);
+
+alloc_pages_failed:
+ ehea_h_destroy_eq(adapter->handle, eq, eq->ipz_eq_handle, &eq->galpas);
+free_eq_mem:
+ ehea_eq_delete(eq);
+
+ EDEB_EX(7, "return with error hret=%lx", hret);
+ return NULL;
+}
+
+void *ehea_poll_eq(struct ehea_adapter *adapter, struct ehea_eq *eq)
+{
+ void *eqe = NULL;
+ unsigned long flags = 0;
+
+ EDEB_EN(7, "adapter=%p eq=%p", adapter, eq);
+
+ spin_lock_irqsave(&eq->spinlock, flags);
+ eqe = ipz_eqit_eq_get_inc_valid(&eq->ipz_queue);
+ spin_unlock_irqrestore(&eq->spinlock, flags);
+
+ EDEB_EX(7, "eqe=%p", eqe);
+
+ return eqe;
+}
+
+int ehea_destroy_eq(struct ehea_adapter *adapter, struct ehea_eq *eq)
+{
+ unsigned long flags = 0;
+ u64 hret = H_HARDWARE;
+
+ EDEB_EN(7, "adapter=%p eq=%p", adapter, eq);
+
+ spin_lock_irqsave(&eq->spinlock, flags);
+
+ hret = ehea_h_destroy_eq(adapter->handle, eq, eq->ipz_eq_handle,
+ &eq->galpas);
+ spin_unlock_irqrestore(&eq->spinlock, flags);
+
+ if (hret != H_SUCCESS) {
+ EDEB_ERR(4, "Failed freeing EQ resources. hret=%lx", hret);
+ return -EINVAL;
+ }
+ ipz_queue_dtor(&eq->ipz_queue);
+ ehea_eq_delete(eq);
+ EDEB_EX(7, "");
+
+ return 0;
+}
+
+struct ehea_qp *ehea_qp_new(void) {
+ struct ehea_qp *qp = vmalloc(sizeof(*qp));
+ if (qp != 0) {
+ memset(qp, 0, sizeof(*qp));
+ }
+ return qp;
+}
+
+void ehea_qp_delete(struct ehea_qp *qp)
+{
+ vfree(qp);
+}
+
+/**
+ * allocates memory for a queue and registers pages in phyp
+ */
+int ehea_qp_alloc_register(struct ehea_qp *qp,
+ struct ipz_queue *ipz_queue,
+ int nr_pages,
+ int wqe_size,
+ int act_nr_sges,
+ struct ehea_adapter *adapter, int h_call_q_selector)
+{
+ u64 hret = H_HARDWARE;
+ u64 rpage = 0;
+ int iret = 0;
+ int cnt = 0;
+ void *vpage = NULL;
+
+ iret = ipz_queue_ctor(ipz_queue,
+ nr_pages, EHEA_PAGESIZE, wqe_size, act_nr_sges);
+ if (!iret) {
+ EDEB_ERR(4, "Cannot allocate page for queue. iret=%x", iret);
+ return -ENOMEM;
+ }
+
+ EDEB(7, "queue_size=%x, alloc_len=%x, toggle_state=%d",
+ ipz_queue->qe_size,
+ ipz_queue->queue_length, ipz_queue->toggle_state);
+
+ for (cnt = 0; cnt < nr_pages; cnt++) {
+ vpage = ipz_qpageit_get_inc(ipz_queue);
+ if (!vpage) {
+ EDEB_ERR(4, "SQ ipz_qpageit_get_inc() "
+ "failed p_vpage= %p", vpage);
+ goto qp_alloc_register_exit0;
+ }
+ rpage = virt_to_abs(vpage);
+
+ hret = ehea_h_register_rpage_qp(adapter->handle,
+ qp->ipz_qp_handle,
+ 0,
+ h_call_q_selector,
+ rpage,
+ 1, qp->galpas.kernel);
+
+ if (hret < H_SUCCESS) {
+ EDEB_ERR(4, "ehea_h_register_rpage_qp failed. hret=%lx",
+ hret);
+ goto qp_alloc_register_exit0;
+ }
+ }
+ ipz_qeit_reset(ipz_queue);
+
+ return 0;
+
+qp_alloc_register_exit0:
+ ipz_queue_dtor(ipz_queue);
+ return -EINVAL;
+}
+
+static inline u32 map_swqe_size(u8 swqe_enc_size)
+{
+ return 128 << swqe_enc_size;
+}
+
+static inline u32 map_rwqe_size(u8 rwqe_enc_size)
+{
+ return 128 << rwqe_enc_size;
+}
+
+struct ehea_qp *ehea_create_qp(struct ehea_adapter *adapter,
+ u32 pd, struct ehea_qp_init_attr *init_attr)
+{
+ struct ehea_qp *qp;
+ u64 hret = H_HARDWARE;
+
+ u32 wqe_size_in_bytes_sq = 0;
+ u32 wqe_size_in_bytes_rq1 = 0;
+ u32 wqe_size_in_bytes_rq2 = 0;
+ u32 wqe_size_in_bytes_rq3 = 0;
+
+ int ret = -1;
+
+ EDEB_EN(7, "init_attr=%p", init_attr);
+
+ qp = ehea_qp_new();
+
+ if (!qp) {
+ EDEB_ERR(4, "pd=%X not enough memory to alloc qp", pd);
+ return NULL;
+ }
+ qp->adapter = adapter;
+
+ EDEB(7, "send_ehea_cq->ipz_cq_handle=0x%lX"
+ "recv_ehea_cq->ipz_cq_handle=0x%lX", init_attr->send_cq_handle,
+ init_attr->recv_cq_handle);
+
+
+ hret = ehea_h_alloc_resource_qp(adapter->handle, qp,
+ init_attr,
+ pd,
+ &qp->ipz_qp_handle,
+ &qp->galpas);
+
+ if (hret != H_SUCCESS) {
+ EDEB_ERR(4, "ehea_h_alloc_resource_qp failed. hret=%lx", hret);
+ goto create_qp_exit1;
+ }
+
+ wqe_size_in_bytes_sq = map_swqe_size(init_attr->act_wqe_size_enc_sq);
+ EDEB(7, "SWQE SG %d", init_attr->wqe_size_enc_sq);
+
+ wqe_size_in_bytes_rq1 = map_rwqe_size(init_attr->act_wqe_size_enc_rq1);
+ wqe_size_in_bytes_rq2 = map_rwqe_size(init_attr->act_wqe_size_enc_rq2);
+ wqe_size_in_bytes_rq3 = map_rwqe_size(init_attr->act_wqe_size_enc_rq3);
+
+ EDEB(7, "SQ pages: %d, SQ WQE size:%d, max SWQE size enc: %d",
+ init_attr->nr_sq_pages,
+ wqe_size_in_bytes_sq, init_attr->act_wqe_size_enc_sq);
+
+ EDEB(7, "RQ1 pages: %d, RQ1 WQE size:%d, max RWQE size enc: %d",
+ init_attr->nr_rq1_pages,
+ wqe_size_in_bytes_rq1, init_attr->act_wqe_size_enc_rq1);
+
+ EDEB(7, "RQ2 pages: %d, RQ2 WQE size:%d, max RWQE size enc: %d",
+ init_attr->nr_rq2_pages,
+ wqe_size_in_bytes_rq2, init_attr->act_wqe_size_enc_rq2);
+
+ EDEB(7, "RQ3 pages: %d, RQ3 WQE size:%d, max RWQE size enc: %d",
+ init_attr->nr_rq3_pages,
+ wqe_size_in_bytes_rq3, init_attr->act_wqe_size_enc_rq3);
+
+ ret = ehea_qp_alloc_register(qp,
+ &qp->ipz_squeue,
+ init_attr->nr_sq_pages,
+ wqe_size_in_bytes_sq,
+ init_attr->act_wqe_size_enc_sq, adapter,
+ 0);
+ if (ret < H_SUCCESS) {
+ EDEB_ERR(4, "can't register for sq hret=%x", ret);
+ goto create_qp_exit2;
+ }
+
+ ret = ehea_qp_alloc_register(qp,
+ &qp->ipz_rqueue1,
+ init_attr->nr_rq1_pages,
+ wqe_size_in_bytes_rq1,
+ init_attr->act_wqe_size_enc_rq1,
+ adapter, 1);
+
+ if (ret < 0) {
+ EDEB_ERR(4, "can't register for rq1 hret=%x", ret);
+ goto create_qp_exit3;
+ }
+
+ if (init_attr->rq_count > 1) {
+ ret = ehea_qp_alloc_register(qp,
+ &qp->ipz_rqueue2,
+ init_attr->nr_rq2_pages,
+ wqe_size_in_bytes_rq2,
+ init_attr->act_wqe_size_enc_rq2,
+ adapter, 2);
+
+ if (ret < 0) {
+ EDEB_ERR(4, "can't register for rq2 hret=%x", ret);
+ goto create_qp_exit4;
+ }
+ }
+
+ if (init_attr->rq_count > 2) {
+ ret = ehea_qp_alloc_register(qp,
+ &qp->ipz_rqueue3,
+ init_attr->nr_rq3_pages,
+ wqe_size_in_bytes_rq3,
+ init_attr->act_wqe_size_enc_rq3,
+ adapter, 3);
+
+ if (ret != 0) {
+ EDEB_ERR(4, "can't register for rq3 hret=%x", ret);
+ goto create_qp_exit5;
+ }
+ }
+
+ qp->init_attr = *init_attr;
+
+ EDEB_EX(7, "");
+ return qp;
+
+create_qp_exit5:
+ ipz_queue_dtor(&qp->ipz_rqueue2);
+
+create_qp_exit4:
+ ipz_queue_dtor(&qp->ipz_rqueue1);
+
+create_qp_exit3:
+ ipz_queue_dtor(&qp->ipz_squeue);
+
+create_qp_exit2:
+ hret = ehea_h_destroy_qp(adapter->handle, qp, qp->ipz_qp_handle,
+ &qp->galpas);
+
+create_qp_exit1:
+ ehea_qp_delete(qp);
+
+ EDEB_EX(7, "hret=NULL");
+ return NULL;
+
+}
+
+int ehea_destroy_qp(struct ehea_qp *qp)
+{
+ int ret = 0;
+ u64 hret;
+ struct ehea_qp_init_attr *qp_attr = &qp->init_attr;
+ EDEB_EX(7, "");
+
+ hret = ehea_h_destroy_qp(qp->adapter->handle, qp, qp->ipz_qp_handle,
+ &qp->galpas);
+ if (hret != H_SUCCESS) {
+ EDEB_ERR(4, "destroy QP failed!");
+ ret = -EINVAL;
+ }
+
+ ipz_queue_dtor(&qp->ipz_squeue);
+ ipz_queue_dtor(&qp->ipz_rqueue1);
+
+ if(qp_attr->rq_count > 1)
+ ipz_queue_dtor(&qp->ipz_rqueue2);
+ if(qp_attr->rq_count > 2)
+ ipz_queue_dtor(&qp->ipz_rqueue3);
+ ehea_qp_delete(qp);
+
+ EDEB_EX(7, "hret=%lx", hret);
+
+ return ret;
+}
+
+#define EHEA_MEM_START 0xc000000000000000
+#define EHEA_MEM_ACC_CTRL 0x00800000
+
+int ehea_reg_mr_adapter(struct ehea_adapter *adapter)
+{
+ int i;
+ u64 hret;
+ u64 start = EHEA_MEM_START;
+ u64 end = (u64) high_memory;
+ u64 nr_pages = (end - start) / PAGE_SIZE;
+ u32 acc_ctrl = EHEA_MEM_ACC_CTRL;
+
+ EDEB_EN(7, "adapter=%p", adapter);
+
+ hret = ehea_h_alloc_resource_mr(adapter->handle,
+ start,
+ end - start,
+ acc_ctrl,
+ adapter->pd,
+ &adapter->mr_handle,
+ &adapter->lkey);
+ if (hret != H_SUCCESS) {
+ EDEB_EX(4, "Error: hret=%lX\n", hret);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < nr_pages; i++) {
+ hret = ehea_h_register_rpage_mr(adapter->handle,
+ adapter->mr_handle,
+ 0,
+ 0,
+ virt_to_abs(
+ (void *)(((u64) start)
+ + (i * PAGE_SIZE))),
+ 1);
+
+ if (((hret != H_SUCCESS) && (hret != H_PAGE_REGISTERED))) {
+ ehea_h_free_resource_mr(adapter->handle, adapter->mr_handle);
+ EDEB_EX(4, " register rpage_mr: hret=%lX\n", hret);
+ return -EINVAL;
+ }
+ }
+
+ if (hret != H_SUCCESS) {
+ ehea_h_free_resource_mr(adapter->handle, adapter->mr_handle);
+ EDEB_EX(4, " register rpage_mr failed for last page:"
+ "hret=%lX\n", hret);
+ return -EINVAL;
+ }
+
+ EDEB_EX(7, "");
+ return 0;
+}
+
+int ehea_dereg_mr_adapter(struct ehea_adapter *adapter)
+{
+ u64 hret;
+ EDEB_EN(7, "adapter=%p", adapter);
+ hret = ehea_h_free_resource_mr(adapter->handle, adapter->mr_handle);
+ if (hret != H_SUCCESS) {
+ EDEB_EX(4, "deregistering memory region failed");
+ return -EINVAL;
+ }
+ EDEB_EX(7, "");
+ return 0;
+}
--- linux-2.6.17.1-orig/drivers/net/ehea/ehea_qmr.h 1969-12-31 16:00:00.000000000 -0800
+++ kernel/drivers/net/ehea/ehea_qmr.h 2006-06-21 04:44:50.155485976 -0700
@@ -0,0 +1,390 @@
+/*
+ * linux/drivers/net/ehea/ehea_qmr.h
+ *
+ * eHEA ethernet device driver for IBM eServer System p
+ *
+ * (C) Copyright IBM Corp. 2006
+ *
+ * Authors:
+ * Christoph Raisch <raisch@de.ibm.com>
+ * Jan-Bernd Themann <themann@de.ibm.com>
+ * Heiko-Joerg Schick <schickhj@de.ibm.com>
+ * Thomas Klein <tklein@de.ibm.com>
+ *
+ *
+ * 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.
+ */
+
+#ifndef __EHEA_QMR_H__
+#define __EHEA_QMR_H__
+
+#include "ehea.h"
+#include "ehea_hw.h"
+
+/* Use of WR_ID field for EHEA */
+#define EHEA_WR_ID_COUNT EHEA_BMASK_IBM(0, 19)
+#define EHEA_WR_ID_TYPE EHEA_BMASK_IBM(20, 23)
+#define EHEA_SWQE2_TYPE 0x1
+#define EHEA_SWQE3_TYPE 0x2
+#define EHEA_RWQE2_TYPE 0x3 /* RQ2 */
+#define EHEA_RWQE3_TYPE 0x4 /* RQ3 */
+#define EHEA_WR_ID_INDEX EHEA_BMASK_IBM(24, 47)
+#define EHEA_WR_ID_REFILL EHEA_BMASK_IBM(48, 63)
+
+struct ehea_vsgentry {
+ u64 vaddr;
+ u32 l_key;
+ u32 len;
+};
+
+/* maximum number of sg entries allowed in a WQE */
+#define EHEA_MAX_WQE_SG_ENTRIES 252
+#define SWQE2_RES_IMM 14
+#define SWQE2_MAX_IMM (160-SWQE2_RES_IMM)
+#define SWQE3_MAX_IMM 224
+
+/* tx control flags for swqe */
+#define EHEA_SWQE_CRC 0x8000
+#define EHEA_SWQE_IP_CHECKSUM 0x4000
+#define EHEA_SWQE_TCP_CHECKSUM 0x2000
+#define EHEA_SWQE_TSO 0x1000
+#define EHEA_SWQE_SIGNALLED_COMPLETION 0x0800
+#define EHEA_SWQE_VLAN_INSERT 0x0400
+#define EHEA_SWQE_IMM_DATA_PRESENT 0x0200
+#define EHEA_SWQE_DESCRIPTORS_PRESENT 0x0100
+#define EHEA_SWQE_WRAP_CTL_REC 0x0080
+#define EHEA_SWQE_WRAP_CTL_FORCE 0x0040
+#define EHEA_SWQE_BIND 0x0020
+#define EHEA_SWQE_PURGE 0x0010
+
+#define SWQE_HEADER_SIZE 32
+
+struct ehea_swqe {
+ u64 wr_id;
+ u16 tx_control;
+ u16 vlan_tag;
+ u8 reserved1;
+ u8 ip_start;
+ u8 ip_end;
+ u8 immediate_data_length;
+ u8 tcp_offset;
+ u8 reserved2;
+ u16 tcp_end;
+ u8 wrap_tag;
+ u8 descriptors; /* number of valid descriptors in WQE */
+ u16 reserved3;
+ u16 reserved4;
+ u16 mss;
+ u32 reserved5;
+ union {
+ /* Send WQE Format 1 */
+ struct {
+ struct ehea_vsgentry sg_list[EHEA_MAX_WQE_SG_ENTRIES];
+ } no_immediate_data;
+
+ /* Send WQE Format 2 */
+ struct {
+ struct ehea_vsgentry sg_entry;
+ /* 0x30 */
+ u8 immediate_data[0xd0 - 0x30];
+ /* 0xd0 */
+ struct ehea_vsgentry sg_list[EHEA_MAX_WQE_SG_ENTRIES -
+ 1];
+ } immdata_desc __attribute__ ((packed));
+
+ /* Send WQE Format 3 */
+ struct {
+ u8 immediate_data[1];
+ } immdata_nodesc;
+ } u;
+};
+
+struct ehea_rwqe {
+ u64 wr_id; /* work request ID */
+ u8 reserved1[5];
+ u8 data_segments;
+ u16 reserved2;
+ u64 reserved3;
+ u64 reserved4;
+ struct ehea_vsgentry sg_list[EHEA_MAX_WQE_SG_ENTRIES];
+};
+
+#define EHEA_CQE_VLAN_TAG_XTRACT 0x0400
+
+#define EHEA_CQE_TYPE_RQ 0x60
+#define EHEA_CQE_STAT_ERR_MASK 0x7300
+#define EHEA_CQE_STAT_ERR_TCP 0x4000
+
+struct ehea_cqe {
+ u64 wr_id; /* work request ID from WQE */
+ u8 type;
+ u8 valid;
+ u16 status;
+ u16 reserved1;
+ u16 num_bytes_transfered;
+ u16 vlan_tag;
+ u16 inet_checksum_value;
+ u8 reserved2;
+ u8 header_length;
+ u16 reserved3;
+ u16 page_offset;
+ u16 wqe_count;
+ u32 qp_token;
+ u32 timestamp;
+ u32 reserved4;
+ u64 reserved5[3];
+};
+
+#define EHEA_EQE_VALID EHEA_BMASK_IBM(0, 0)
+#define EHEA_EQE_IS_CQE EHEA_BMASK_IBM(1, 1)
+#define EHEA_EQE_IDENTIFIER EHEA_BMASK_IBM(2, 7)
+#define EHEA_EQE_QP_CQ_NUMBER EHEA_BMASK_IBM(8, 31)
+#define EHEA_EQE_QP_TOKEN EHEA_BMASK_IBM(32, 63)
+#define EHEA_EQE_CQ_TOKEN EHEA_BMASK_IBM(32, 63)
+#define EHEA_EQE_KEY EHEA_BMASK_IBM(32, 63)
+#define EHEA_EQE_PORT_NUMBER EHEA_BMASK_IBM(56, 63)
+#define EHEA_EQE_EQ_NUMBER EHEA_BMASK_IBM(48, 63)
+#define EHEA_EQE_SM_ID EHEA_BMASK_IBM(48, 63)
+#define EHEA_EQE_SM_MECH_NUMBER EHEA_BMASK_IBM(48, 55)
+#define EHEA_EQE_SM_PORT_NUMBER EHEA_BMASK_IBM(56, 63)
+
+struct ehea_eqe {
+ u64 entry;
+};
+
+struct ehea_mrte {
+ u64 starting_va;
+ u64 length; /* length of memory region in bytes */
+ u32 pd;
+ u8 key_instance;
+ u8 pagesize;
+ u8 mr_control;
+ u8 local_remote_access_controll;
+ u8 reserved[0x20 - 0x18];
+ u64 at_pointer[4];
+};
+
+static inline void *ipz_qeit_calc(struct ipz_queue *queue, u64 q_offset)
+{
+ struct ipz_page *current_page = NULL;
+ if (q_offset >= queue->queue_length)
+ q_offset -= queue->queue_length;
+ current_page = (queue->queue_pages)[q_offset >> EHEA_PAGESHIFT];
+ return ¤t_page->entries[q_offset & (EHEA_PAGESIZE - 1)];
+}
+
+static inline void *ipz_qeit_get(struct ipz_queue *queue)
+{
+ return ipz_qeit_calc(queue, queue->current_q_offset);
+}
+
+static inline void ipz_qeit_inc(struct ipz_queue *queue)
+{
+ queue->current_q_offset += queue->qe_size;
+ if (queue->current_q_offset >= queue->queue_length) {
+ queue->current_q_offset = 0;
+ /* toggle the valid flag */
+ queue->toggle_state = (~queue->toggle_state) & 1;
+ }
+}
+
+static inline void *ipz_qeit_get_inc(struct ipz_queue *queue)
+{
+ void *retvalue = ipz_qeit_get(queue);
+ ipz_qeit_inc(queue);
+ EDEB(8, "queue=%p retvalue=%p new current_q_addr=%lx qe_size=%x",
+ queue, retvalue, queue->current_q_offset, queue->qe_size);
+
+ return retvalue;
+}
+
+static inline void *ipz_qeit_get_inc_valid(struct ipz_queue *queue)
+{
+ struct ehea_cqe *retvalue = ipz_qeit_get(queue);
+ void *pref;
+ u8 valid = retvalue->valid;
+ if ((valid >> 7) == (queue->toggle_state & 1)) {
+ /* this is a good one */
+ iosync();
+ ipz_qeit_inc(queue);
+ pref = ipz_qeit_calc(queue, queue->current_q_offset);
+ prefetch(pref);
+ prefetch(pref + 128);
+ } else
+ retvalue = NULL;
+ return retvalue;
+}
+
+static inline void *ipz_qeit_get_valid(struct ipz_queue *queue)
+{
+ struct ehea_cqe *retvalue = ipz_qeit_get(queue);
+ void *pref;
+ pref = ipz_qeit_calc(queue, queue->current_q_offset);
+ prefetch(pref);
+ prefetch(pref + 128);
+ prefetch(pref + 256);
+ u8 valid = retvalue->valid;
+ if ((valid >> 7) == (queue->toggle_state & 1))
+ iosync();
+ else
+ retvalue = NULL;
+ return retvalue;
+}
+
+static inline void *ipz_qeit_reset(struct ipz_queue *queue)
+{
+ queue->current_q_offset = 0;
+ return ipz_qeit_get(queue);
+}
+
+static inline void *ipz_qeit_eq_get_inc(struct ipz_queue *queue)
+{
+ void *retvalue = NULL;
+ u64 last_entry_in_q = queue->queue_length - queue->qe_size;
+
+ retvalue = ipz_qeit_get(queue);
+ queue->current_q_offset += queue->qe_size;
+ if (queue->current_q_offset > last_entry_in_q) {
+ queue->current_q_offset = 0;
+ queue->toggle_state = (~queue->toggle_state) & 1;
+ }
+
+ EDEB(7, "queue=%p retvalue=%p new current_q_offset=%lx qe_size=%x",
+ queue, retvalue, queue->current_q_offset, queue->qe_size);
+
+ return retvalue;
+}
+
+static inline void *ipz_eqit_eq_get_inc_valid(struct ipz_queue *queue)
+{
+ void *retvalue = ipz_qeit_get(queue);
+ u32 qe = *(u8 *) retvalue;
+ EDEB(7, "ipz_eqit_eq_get_inc_valid qe=%x", qe);
+ if ((qe >> 7) == (queue->toggle_state & 1))
+ ipz_qeit_eq_get_inc(queue);
+ else
+ retvalue = NULL;
+ return retvalue;
+}
+
+static inline struct ehea_rwqe *ehea_get_next_rwqe(struct ehea_qp *qp,
+ int rq_nr)
+{
+
+ struct ehea_rwqe *wqe_p = NULL;
+ struct ipz_queue *queue = NULL;
+ struct ehea_qp *my_qp = qp;
+ EDEB_EN(8, "QP=%p, RQ_nr=%d", qp, rq_nr);
+
+ if (rq_nr == 1)
+ queue = &my_qp->ipz_rqueue1;
+ else if (rq_nr == 2)
+ queue = &my_qp->ipz_rqueue2;
+ else
+ queue = &my_qp->ipz_rqueue3;
+ wqe_p = (struct ehea_rwqe *)ipz_qeit_get_inc(queue);
+
+ EDEB_EX(8, "&RWQE=%p, queue=%p", wqe_p, queue);
+ return wqe_p;
+}
+
+static inline struct ehea_swqe *ehea_get_swqe(struct ehea_qp *my_qp)
+{
+
+ struct ehea_swqe *wqe_p = NULL;
+ EDEB_EN(7, "QP=%p, queue=%p", my_qp, &my_qp->ipz_squeue);
+ wqe_p = (struct ehea_swqe *)ipz_qeit_get_inc(&my_qp->ipz_squeue);
+ EDEB_EX(7, "");
+ return wqe_p;
+}
+
+static inline void ehea_post_swqe(struct ehea_qp *my_qp, struct ehea_swqe *swqe)
+{
+
+ EDEB_EN(7, "QP=%p, SWQE=%p", my_qp, swqe);
+ EDEB(6, "SWQE workreqid = 0x%lX, imm_data_len=%d, descriptors=%d",
+ (u64) swqe->wr_id, swqe->immediate_data_length, swqe->descriptors);
+ iosync();
+ ehea_update_sqa(my_qp, 1);
+ EDEB_EX(7, "");
+}
+
+static inline struct ehea_cqe *ehea_poll_rq1(struct ehea_qp *qp, int *wqe_index)
+{
+ struct ipz_queue *queue = &qp->ipz_rqueue1;
+ struct ehea_cqe *cqe = NULL;
+
+ EDEB_EN(7, "QP=%p, RQ1 toggle state = %d, current_q_offset=%lx", qp,
+ queue->toggle_state, queue->current_q_offset);
+ *wqe_index = (queue->current_q_offset) >> (7 + EHEA_SG_RQ1);
+ cqe = (struct ehea_cqe *)ipz_qeit_get_valid(queue);
+ EDEB_EX(7, "cqe=%p, new toggle state %d, wqe_index = %d",
+ cqe, queue->toggle_state, *wqe_index);
+ return cqe;
+}
+
+static inline void ehea_inc_rq1(struct ehea_qp *qp)
+{
+ struct ipz_queue *queue = &qp->ipz_rqueue1;
+ ipz_qeit_inc(queue);
+}
+
+static inline struct ehea_cqe *ehea_poll_cq(struct ehea_cq *my_cq)
+{
+
+ struct ehea_cqe *wqe_p = NULL;
+ EDEB_EN(7, "CQ=%p", my_cq);
+
+ EDEB(7, "queue_element_size=%x, alloc_len=%x, queue=%p",
+ my_cq->ipz_queue.qe_size,
+ my_cq->ipz_queue.queue_length, &my_cq->ipz_queue);
+ wqe_p = (struct ehea_cqe *)ipz_qeit_get_inc_valid(&my_cq->ipz_queue);
+
+ EDEB_EX(7, "wqe_p=%p", wqe_p);
+ return wqe_p;
+};
+
+#define HIPZ_CQ_REGISTER_ORIG 0
+#define HIPZ_EQ_REGISTER_ORIG 0
+
+enum ehea_eq_type {
+ EHEA_EQ = 0, /* event queue */
+ EHEA_NEQ /* notification event queue */
+};
+
+struct ehea_eq *ehea_create_eq(struct ehea_adapter *adapter,
+ enum ehea_eq_type type,
+ const u32 length, const u8 eqe_gen);
+
+int ehea_destroy_eq(struct ehea_adapter *adapter, struct ehea_eq *eq);
+
+void *ehea_poll_eq(struct ehea_adapter *adapter, struct ehea_eq *eq);
+
+struct ehea_cq *ehea_create_cq(struct ehea_adapter *adapter, int cqe,
+ u64 eq_handle, u32 cq_token);
+
+int ehea_destroy_cq(struct ehea_cq *cq);
+
+
+struct ehea_qp *ehea_create_qp(struct ehea_adapter * adapter,
+ u32 pd,
+ struct ehea_qp_init_attr *init_attr);
+
+int ehea_destroy_qp(struct ehea_qp *qp);
+
+int ehea_reg_mr_adapter(struct ehea_adapter *adapter);
+int ehea_dereg_mr_adapter(struct ehea_adapter *adapter);
+
+#endif /* __EHEA_QMR_H__ */
--- linux-2.6.17.1-orig/drivers/net/ehea/ehea_ethtool.c 1969-12-31 16:00:00.000000000 -0800
+++ kernel/drivers/net/ehea/ehea_ethtool.c 2006-06-21 04:44:50.140488256 -0700
@@ -0,0 +1,325 @@
+/*
+ * linux/drivers/net/ehea/ehea_main.c
+ *
+ * eHEA ethernet device driver for IBM eServer System p
+ *
+ * (C) Copyright IBM Corp. 2006
+ *
+ * Authors:
+ * Christoph Raisch <raisch@de.ibm.com>
+ * Jan-Bernd Themann <themann@de.ibm.com>
+ * Heiko-Joerg Schick <schickhj@de.ibm.com>
+ * Thomas Klein <tklein@de.ibm.com>
+ *
+ *
+ * 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.
+ */
+
+#include "ehea.h"
+#include "ehea_phyp.h"
+
+
+static int netdev_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ u64 hret;
+ struct ehea_port *port = (struct ehea_port*)dev->priv;
+ struct ehea_adapter *adapter = port->adapter;
+ struct hcp_query_ehea_port_cb_4 *cb4 = NULL;
+
+ EDEB_EN(7, "net_device=%p", dev);
+
+ cb4 = kzalloc(H_CB_ALIGNMENT, GFP_KERNEL);
+ if(!cb4) {
+ EDEB_ERR(4, "No memory for cb4");
+ goto get_settings_exit;
+ }
+
+ hret = ehea_h_query_ehea_port(adapter->handle,
+ port->logical_port_id,
+ H_PORT_CB4,
+ H_PORT_CB4_ALL,
+ cb4);
+
+ if (hret != H_SUCCESS) {
+ EDEB_ERR(4, "query_ehea_port failed for cb4");
+ kfree(cb4);
+ goto get_settings_exit;
+ }
+
+ EDEB_DMP(7, (u8*)cb4,
+ sizeof(struct hcp_query_ehea_port_cb_4), "After HCALL");
+
+ if (netif_carrier_ok(dev)) {
+ switch(cb4->port_speed){
+ case H_PORT_SPEED_10M_H:
+ cmd->speed = SPEED_10;
+ cmd->duplex = DUPLEX_HALF;
+ break;
+ case H_PORT_SPEED_10M_F:
+ cmd->speed = SPEED_10;
+ cmd->duplex = DUPLEX_FULL;
+ break;
+ case H_PORT_SPEED_100M_H:
+ cmd->speed = SPEED_100;
+ cmd->duplex = DUPLEX_HALF;
+ break;
+ case H_PORT_SPEED_100M_F:
+ cmd->speed = SPEED_100;
+ cmd->duplex = DUPLEX_FULL;
+ break;
+ case H_PORT_SPEED_1G_F:
+ cmd->speed = SPEED_1000;
+ cmd->duplex = DUPLEX_FULL;
+ break;
+ case H_PORT_SPEED_10G_F:
+ cmd->speed = SPEED_10000;
+ cmd->duplex = DUPLEX_FULL;
+ break;
+ }
+ } else {
+ cmd->speed = -1;
+ cmd->duplex = -1;
+ }
+
+ cmd->supported =
+ (SUPPORTED_10000baseT_Full | SUPPORTED_1000baseT_Full
+ | SUPPORTED_100baseT_Full | SUPPORTED_100baseT_Half
+ | SUPPORTED_10baseT_Full | SUPPORTED_10baseT_Half
+ | SUPPORTED_Autoneg | SUPPORTED_FIBRE);
+ cmd->advertising =
+ (ADVERTISED_10000baseT_Full | ADVERTISED_Autoneg
+ | ADVERTISED_FIBRE);
+ cmd->port = PORT_FIBRE;
+ cmd->autoneg = AUTONEG_ENABLE;
+
+ kfree(cb4);
+ return 0;
+
+get_settings_exit:
+ EDEB_EX(7, "");
+ return 1;
+}
+
+static int netdev_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ printk("set settings\n");
+ return 0;
+}
+
+static void netdev_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ printk("get drvinfo\n");
+ strncpy(info->driver, EHEA_DRIVER_NAME, sizeof(info->driver) - 1);
+ strncpy(info->version, EHEA_DRIVER_VERSION, sizeof(info->version) - 1);
+}
+
+static u32 netdev_get_msglevel(struct net_device *dev)
+{
+ EDEB(7, "");
+ return (u32)ehea_trace_level;
+}
+
+static void netdev_set_msglevel(struct net_device *dev, u32 value)
+{
+ EDEB(7, "trace level set to %x", value);
+ ehea_trace_level = (int)value;
+}
+
+static int netdev_nway_reset(struct net_device *dev)
+{
+ printk("nway reset\n");
+ return 0;
+}
+
+static void netdev_get_pauseparam(struct net_device *dev,
+ struct ethtool_pauseparam *pauseparam)
+{
+ printk("get pauseparam\n");
+}
+
+static int netdev_set_pauseparam(struct net_device *dev,
+ struct ethtool_pauseparam *pauseparam)
+{
+ printk("set pauseparam\n");
+ return 0;
+}
+
+static u32 netdev_get_rx_csum(struct net_device *dev)
+{
+ printk("set rx_csum\n");
+ return 0;
+}
+
+static int netdev_set_rx_csum(struct net_device *dev, u32 value)
+{
+ printk("set rx_csum\n");
+ return 0;
+}
+
+static int netdev_self_test_count(struct net_device *dev)
+{
+ printk("self test count\n");
+ return 0;
+}
+
+static void netdev_self_test(struct net_device *dev, struct ethtool_test *test,
+ u64 *value)
+{
+ printk("self test\n");
+}
+
+static int netdev_phys_id(struct net_device *dev, u32 value)
+{
+ printk("physical id\n");
+ return 0;
+}
+
+static char ehea_ethtool_stats_keys[][ETH_GSTRING_LEN] = {
+ {"poll_max_processed"},
+ {"queue_stopped"},
+ {"min_swqe_avail"},
+ {"poll_receive_err"},
+ {"pkt_send"},
+ {"pkt_xmit"},
+ {"send_tasklet"},
+ {"ehea_poll"},
+ {"nwqe"},
+ {"swqe_available"},
+ {"rxo"},
+ {"rx64"},
+ {"rx65"},
+ {"rx128"},
+ {"rx256"},
+ {"rx512"},
+ {"rx1024"},
+ {"txo"},
+ {"tx64"},
+ {"tx65"},
+ {"tx128"},
+ {"tx256"},
+ {"tx512"},
+ {"tx1024"}
+};
+
+static void netdev_get_strings(struct net_device *dev,
+ u32 stringset, u8 * data)
+{
+ switch (stringset) {
+ case ETH_SS_TEST:
+ break;
+ case ETH_SS_STATS:
+ memcpy(data, &ehea_ethtool_stats_keys,
+ sizeof(ehea_ethtool_stats_keys));
+ }
+}
+
+static int netdev_get_stats_count(struct net_device *dev)
+{
+ return ARRAY_SIZE(ehea_ethtool_stats_keys);
+}
+
+
+static void netdev_get_ethtool_stats(struct net_device *dev,
+ struct ethtool_stats *stats, u64 *data)
+{
+ int i = 0;
+ u64 hret;
+ struct ehea_port *port = (struct ehea_port*)dev->priv;
+ struct ehea_adapter *adapter = port->adapter;
+ struct port_state *p_state = &port->p_state;
+ struct hcp_query_ehea_port_cb_6 *cb6 = NULL;
+
+ EDEB_EN(7, "net_device=%p", dev);
+
+ cb6 = kzalloc(H_CB_ALIGNMENT, GFP_KERNEL);
+ if(!cb6) {
+ EDEB_ERR(4, "No memory for cb6");
+ goto stats_exit;
+ }
+
+ hret = ehea_h_query_ehea_port(adapter->handle,
+ port->logical_port_id,
+ H_PORT_CB6,
+ H_PORT_CB6_ALL,
+ cb6);
+
+ if (hret != H_SUCCESS)
+ EDEB_ERR(4, "query_ehea_port failed for cb6");
+
+ EDEB_DMP(7, (u8*)cb6,
+ sizeof(struct hcp_query_ehea_port_cb_6), "After HCALL");
+ data[i++] = p_state->poll_max_processed;
+ data[i++] = p_state->queue_stopped;
+ data[i++] = p_state->min_swqe_avail;
+ data[i++] = p_state->poll_receive_errors;
+ data[i++] = p_state->pkt_send;
+ data[i++] = p_state->pkt_xmit;
+ data[i++] = p_state->send_tasklet;
+ data[i++] = p_state->ehea_poll;
+ data[i++] = p_state->nwqe;
+ data[i++] = atomic_read(&port->port_res[0].swqe_avail);
+
+ data[i++] = cb6->rxo;
+ data[i++] = cb6->rx64;
+ data[i++] = cb6->rx65;
+ data[i++] = cb6->rx128;
+ data[i++] = cb6->rx256;
+ data[i++] = cb6->rx512;
+ data[i++] = cb6->rx1024;
+ data[i++] = cb6->txo;
+ data[i++] = cb6->tx64;
+ data[i++] = cb6->tx65;
+ data[i++] = cb6->tx128;
+ data[i++] = cb6->tx256;
+ data[i++] = cb6->tx512;
+ data[i++] = cb6->tx1024;
+
+
+ kfree(cb6);
+stats_exit:
+ EDEB_EX(7, "");
+}
+
+struct ethtool_ops ehea_ethtool_ops = {
+ .get_settings = netdev_get_settings,
+ .set_settings = netdev_set_settings,
+ .get_drvinfo = netdev_get_drvinfo,
+ .get_msglevel = netdev_get_msglevel,
+ .set_msglevel = netdev_set_msglevel,
+ .nway_reset = netdev_nway_reset,
+ .get_link = ethtool_op_get_link,
+ .get_pauseparam = netdev_get_pauseparam,
+ .set_pauseparam = netdev_set_pauseparam,
+ .get_rx_csum = netdev_get_rx_csum,
+ .set_rx_csum = netdev_set_rx_csum,
+ .get_tx_csum = ethtool_op_get_tx_csum,
+ .set_tx_csum = ethtool_op_set_tx_csum,
+ .get_sg = ethtool_op_get_sg,
+ .set_sg = ethtool_op_set_sg,
+ .get_tso = ethtool_op_get_tso,
+ .set_tso = ethtool_op_set_tso,
+ .self_test_count = netdev_self_test_count,
+ .self_test = netdev_self_test,
+ .get_strings = netdev_get_strings,
+ .phys_id = netdev_phys_id,
+ .get_stats_count = netdev_get_stats_count,
+ .get_ethtool_stats = netdev_get_ethtool_stats
+};
+
+void ehea_set_ethtool_ops(struct net_device *netdev)
+{
+ SET_ETHTOOL_OPS(netdev, &ehea_ethtool_ops);
+}
^ permalink raw reply
* [PATCH 0/6] ehea: IBM eHEA Ethernet Device Driver
From: Jan-Bernd Themann @ 2006-06-21 12:39 UTC (permalink / raw)
To: netdev; +Cc: meder, raisch, schickhj, themann
Hi,
one piece of the patch set posted on Thu, 08 Jun 2006 got lost
due to the spam filter of this mailing list. Here is the complete
patch set once again including the (previously missing) main component
in the patch 1/5.
Feedback is highly appreciated.
Thanks,
Jan-Bernd
Signed-off-by: Jan-Bernd Themann <themann@de.ibm.com>
Changelog-by: Jan-Bernd Themann <themann@de.ibm.com>
Differences to patch set http://www.spinics.net/lists/netdev/msg06022.html
Changelog:
- Comments triggering the spam filter removed
drivers/net/Kconfig | 7
drivers/net/Makefile | 1
drivers/net/ehea/Makefile | 7
drivers/net/ehea/ehea.h | 434 ++++++
drivers/net/ehea/ehea_ethtool.c | 325 +++++
drivers/net/ehea/ehea_hcall.h | 52
drivers/net/ehea/ehea_hw.h | 319 ++++
drivers/net/ehea/ehea_main.c | 2557 ++++++++++++++++++++++++++++++++++++++++
drivers/net/ehea/ehea_phyp.c | 1020 +++++++++++++++
drivers/net/ehea/ehea_phyp.h | 625 +++++++++
drivers/net/ehea/ehea_qmr.c | 719 +++++++++++
drivers/net/ehea/ehea_qmr.h | 390 ++++++
12 files changed, 6456 insertions(+)
^ permalink raw reply
* [PATCH 2/6] ehea: pHYP interface
From: Jan-Bernd Themann @ 2006-06-21 12:40 UTC (permalink / raw)
To: netdev; +Cc: meder, raisch, schickhj, themann
Signed-off-by: Jan-Bernd Themann <themann@de.ibm.com>
drivers/net/ehea/ehea_hcall.h | 52 ++
drivers/net/ehea/ehea_phyp.c | 1020 ++++++++++++++++++++++++++++++++++++++++++
drivers/net/ehea/ehea_phyp.h | 625 +++++++++++++++++++++++++
3 files changed, 1697 insertions(+)
--- linux-2.6.17.1-orig/drivers/net/ehea/ehea_phyp.c 1969-12-31 16:00:00.000000000 -0800
+++ kernel/drivers/net/ehea/ehea_phyp.c 2006-06-21 04:44:50.147487192 -0700
@@ -0,0 +1,1020 @@
+/*
+ * linux/drivers/net/ehea/ehea_phyp.c
+ *
+ * eHEA ethernet device driver for IBM eServer System p
+ *
+ * (C) Copyright IBM Corp. 2006
+ *
+ * Authors:
+ * Christoph Raisch <raisch@de.ibm.com>
+ * Jan-Bernd Themann <themann@de.ibm.com>
+ * Heiko-Joerg Schick <schickhj@de.ibm.com>
+ * Thomas Klein <tklein@de.ibm.com>
+ *
+ *
+ * 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.
+ */
+
+#include "ehea_phyp.h"
+
+
+static inline u16 get_order_of_qentries(u16 queue_entries)
+{
+ u8 ld = 1; /* logarithmus dualis */
+ EDEB_EN(7, "queue_entries=0x%X", queue_entries);
+ while (((1U << ld) - 1) < queue_entries) {
+ ld++;
+ };
+ EDEB_EX(7, "mapped queue_entries=%d", ld - 1);
+ return ld - 1;
+}
+
+
+/* Defines for H_CALL H_ALLOC_RESOURCE */
+#define H_ALL_RES_TYPE_QP 1
+#define H_ALL_RES_TYPE_CQ 2
+#define H_ALL_RES_TYPE_EQ 3
+#define H_ALL_RES_TYPE_MR 5
+#define H_ALL_RES_TYPE_MW 6
+
+u64 ehea_h_query_ehea_qp(const u64 hcp_adapter_handle,
+ const u8 qp_category,
+ const u64 qp_handle, const u64 sel_mask, void *cb_addr)
+{
+ u64 hret = H_ADAPTER_PARM;
+ u64 dummy = 0;
+
+ EDEB_EN(7, "hcp_adapter_handle=%lX cat=%X qp_handle=%lX sel_mask=%lX "
+ "cb_addr=%p\n",
+ hcp_adapter_handle,
+ (u16) qp_category, qp_handle, sel_mask, cb_addr);
+ EDEB_DMP(7, (u8 *) cb_addr, sizeof(struct hcp_modify_qp_cb_0),
+ "Before HCALL");
+
+ if ((((u64)cb_addr) & (PAGE_SIZE - 1)) != 0)
+ panic("query_ehea_qp: cb_addr not on page boundary!!!");
+
+ hret = ehea_hcall_7arg_7ret(H_QUERY_HEA_QP,
+ hcp_adapter_handle, /* R4 */
+ qp_category, /* R5 */
+ qp_handle, /* R6 */
+ sel_mask, /* R7 */
+ virt_to_abs(cb_addr), /* R8 */
+ 0, 0, /* R9-R10 */
+ &dummy, /* R4 */
+ &dummy, /* R5 */
+ &dummy, /* R6 */
+ &dummy, /* R7 */
+ &dummy, /* R8 */
+ &dummy, /* R9 */
+ &dummy); /* R10 */
+
+ EDEB_DMP(7, (u8 *) cb_addr, sizeof(struct hcp_modify_qp_cb_0),
+ "After HCALL");
+ EDEB_EX(7, "");
+ return hret;
+}
+
+/* input param R5 */
+#define H_ALL_RES_QP_EQPO EHEA_BMASK_IBM(9, 11)
+#define H_ALL_RES_QP_QPP EHEA_BMASK_IBM(12, 12)
+#define H_ALL_RES_QP_RQR EHEA_BMASK_IBM(13, 15)
+#define H_ALL_RES_QP_EQEG EHEA_BMASK_IBM(16, 16)
+#define H_ALL_RES_QP_LL_QP EHEA_BMASK_IBM(17, 17)
+#define H_ALL_RES_QP_DMA128 EHEA_BMASK_IBM(19, 19)
+#define H_ALL_RES_QP_HSM EHEA_BMASK_IBM(20, 21)
+#define H_ALL_RES_QP_SIGT EHEA_BMASK_IBM(22, 23)
+#define H_ALL_RES_QP_TENURE EHEA_BMASK_IBM(48, 55)
+#define H_ALL_RES_QP_RES_TYP EHEA_BMASK_IBM(56, 63)
+
+/* input param R9 */
+#define H_ALL_RES_QP_TOKEN EHEA_BMASK_IBM(0, 31)
+#define H_ALL_RES_QP_PD EHEA_BMASK_IBM(32,63)
+
+/* input param R10 */
+#define H_ALL_RES_QP_MAX_SWQE EHEA_BMASK_IBM(4, 7)
+#define H_ALL_RES_QP_MAX_R1WQE EHEA_BMASK_IBM(12, 15)
+#define H_ALL_RES_QP_MAX_R2WQE EHEA_BMASK_IBM(20, 23)
+#define H_ALL_RES_QP_MAX_R3WQE EHEA_BMASK_IBM(28, 31)
+/* Max Send Scatter Gather Elements */
+#define H_ALL_RES_QP_MAX_SSGE EHEA_BMASK_IBM(37, 39)
+#define H_ALL_RES_QP_MAX_R1SGE EHEA_BMASK_IBM(45, 47)
+/* Max Receive SG Elements RQ1 */
+#define H_ALL_RES_QP_MAX_R2SGE EHEA_BMASK_IBM(53, 55)
+#define H_ALL_RES_QP_MAX_R3SGE EHEA_BMASK_IBM(61, 63)
+
+/* input param R11 */
+#define H_ALL_RES_QP_SWQE_IDL EHEA_BMASK_IBM(0, 7)
+/* max swqe immediate data length */
+#define H_ALL_RES_QP_PORT_NUM EHEA_BMASK_IBM(48, 63)
+
+/* input param R12 */
+#define H_ALL_RES_QP_TH_RQ2 EHEA_BMASK_IBM(0, 15)
+/* Threshold RQ2 */
+#define H_ALL_RES_QP_TH_RQ3 EHEA_BMASK_IBM(16, 31)
+/* Threshold RQ3 */
+
+/* output param R6 */
+#define H_ALL_RES_QP_ACT_SWQE EHEA_BMASK_IBM(0, 15)
+#define H_ALL_RES_QP_ACT_R1WQE EHEA_BMASK_IBM(16, 31)
+#define H_ALL_RES_QP_ACT_R2WQE EHEA_BMASK_IBM(32, 47)
+#define H_ALL_RES_QP_ACT_R3WQE EHEA_BMASK_IBM(48, 63)
+
+/* output param, R7 */
+#define H_ALL_RES_QP_ACT_SSGE EHEA_BMASK_IBM(0, 7)
+#define H_ALL_RES_QP_ACT_R1SGE EHEA_BMASK_IBM(8, 15)
+#define H_ALL_RES_QP_ACT_R2SGE EHEA_BMASK_IBM(16, 23)
+#define H_ALL_RES_QP_ACT_R3SGE EHEA_BMASK_IBM(24, 31)
+#define H_ALL_RES_QP_ACT_SWQE_IDL EHEA_BMASK_IBM(32, 39)
+
+/* output param R8,R9 */
+#define H_ALL_RES_QP_SIZE_SQ EHEA_BMASK_IBM(0, 31)
+#define H_ALL_RES_QP_SIZE_RQ1 EHEA_BMASK_IBM(32, 63)
+#define H_ALL_RES_QP_SIZE_RQ2 EHEA_BMASK_IBM(0, 31)
+#define H_ALL_RES_QP_SIZE_RQ3 EHEA_BMASK_IBM(32, 63)
+
+/* output param R11,R12 */
+#define H_ALL_RES_QP_LIOBN_SQ EHEA_BMASK_IBM(0, 31)
+#define H_ALL_RES_QP_LIOBN_RQ1 EHEA_BMASK_IBM(32, 63)
+#define H_ALL_RES_QP_LIOBN_RQ2 EHEA_BMASK_IBM(0, 31)
+#define H_ALL_RES_QP_LIOBN_RQ3 EHEA_BMASK_IBM(32, 63)
+
+u64 ehea_h_alloc_resource_qp(const u64 adapter_handle,
+ struct ehea_qp *ehea_qp,
+ struct ehea_qp_init_attr *init_attr,
+ const u32 pd,
+ u64 *qp_handle, struct h_galpas *h_galpas)
+{
+ u64 hret = H_ADAPTER_PARM;
+
+ u64 allocate_controls =
+ EHEA_BMASK_SET(H_ALL_RES_QP_EQPO, init_attr->low_lat_rq1 ? 1 : 0)
+ | EHEA_BMASK_SET(H_ALL_RES_QP_QPP, 0)
+ | EHEA_BMASK_SET(H_ALL_RES_QP_RQR, 6) /* RQ1 & RQ2 & rq3 */
+ |EHEA_BMASK_SET(H_ALL_RES_QP_EQEG, 0) /* EQE gen. disabled */
+ |EHEA_BMASK_SET(H_ALL_RES_QP_LL_QP, init_attr->low_lat_rq1)
+ | EHEA_BMASK_SET(H_ALL_RES_QP_DMA128, 0)
+ | EHEA_BMASK_SET(H_ALL_RES_QP_HSM, 0)
+ | EHEA_BMASK_SET(H_ALL_RES_QP_SIGT, init_attr->signalingtype)
+ | EHEA_BMASK_SET(H_ALL_RES_QP_RES_TYP, H_ALL_RES_TYPE_QP);
+
+ u64 r9_reg = EHEA_BMASK_SET(H_ALL_RES_QP_PD, pd)
+ | EHEA_BMASK_SET(H_ALL_RES_QP_TOKEN, init_attr->qp_token);
+
+ u64 max_r10_reg =
+ EHEA_BMASK_SET(H_ALL_RES_QP_MAX_SWQE,
+ get_order_of_qentries(init_attr->max_nr_send_wqes))
+ | EHEA_BMASK_SET(H_ALL_RES_QP_MAX_R1WQE,
+ get_order_of_qentries(init_attr->max_nr_rwqes_rq1))
+ | EHEA_BMASK_SET(H_ALL_RES_QP_MAX_R2WQE,
+ get_order_of_qentries(init_attr->max_nr_rwqes_rq2))
+ | EHEA_BMASK_SET(H_ALL_RES_QP_MAX_R3WQE,
+ get_order_of_qentries(init_attr->max_nr_rwqes_rq3))
+ | EHEA_BMASK_SET(H_ALL_RES_QP_MAX_SSGE, init_attr->wqe_size_enc_sq)
+ | EHEA_BMASK_SET(H_ALL_RES_QP_MAX_R1SGE,
+ init_attr->wqe_size_enc_rq1)
+ | EHEA_BMASK_SET(H_ALL_RES_QP_MAX_R2SGE,
+ init_attr->wqe_size_enc_rq2)
+ | EHEA_BMASK_SET(H_ALL_RES_QP_MAX_R3SGE,
+ init_attr->wqe_size_enc_rq3);
+
+ u64 r11_in =
+ EHEA_BMASK_SET(H_ALL_RES_QP_SWQE_IDL, init_attr->swqe_imm_data_len)
+ | EHEA_BMASK_SET(H_ALL_RES_QP_PORT_NUM, init_attr->port_nr);
+ u64 threshold =
+ EHEA_BMASK_SET(H_ALL_RES_QP_TH_RQ2, init_attr->rq2_threshold)
+ | EHEA_BMASK_SET(H_ALL_RES_QP_TH_RQ3, init_attr->rq3_threshold);
+
+ u64 r5_out = 0;
+ u64 r6_out = 0;
+ u64 r7_out = 0;
+ u64 r8_out = 0;
+ u64 r9_out = 0;
+ u64 g_la_user_out = 0;
+ u64 r11_out = 0;
+ u64 r12_out = 0;
+
+ EDEB_EN(7, "adapter_handle=%lx low latency RQ1 0x%X "
+ "signalingtype=0x%X number of RQs=0x%X "
+ "send_cq_handle=%lx receive_cq_handle=%lx",
+ adapter_handle, init_attr->low_lat_rq1,
+ init_attr->signalingtype, init_attr->rq_count,
+ init_attr->send_cq_handle, init_attr->recv_cq_handle);
+ EDEB(7, "async_eq_handle=%lx qp_token=0x%X "
+ "max_nr_send_wqes=0x%X max_nr_rcv_wqes_rq1=0x%X "
+ "max_nr_rcv_wqes_rq2=0x%X max_nr_rcv_wqes_rq3=0x%X ",
+ init_attr->aff_eq_handle, init_attr->qp_token,
+ init_attr->max_nr_send_wqes, init_attr->max_nr_rwqes_rq1,
+ init_attr->max_nr_rwqes_rq2, init_attr->max_nr_rwqes_rq3);
+ EDEB(7, "wqe_enc_size=0x%X wqe_enc_size_rq1=0x%X "
+ "wqe_enc_size_rq2=0x%X wqe_enc_size_rq3=0x%X port_nr=%d",
+ init_attr->wqe_size_enc_sq, init_attr->wqe_size_enc_rq1,
+ init_attr->wqe_size_enc_rq2, init_attr->wqe_size_enc_rq3,
+ init_attr->port_nr);
+ EDEB(7, "rq2_threshold=%d rq3_threshold=%d",
+ init_attr->rq2_threshold, init_attr->rq3_threshold);
+
+ hret = ehea_hcall_9arg_9ret(H_ALLOC_HEA_RESOURCE,
+ adapter_handle, /* R4 */
+ allocate_controls, /* R5 */
+ init_attr->send_cq_handle, /* R6 */
+ init_attr->recv_cq_handle, /* R7 */
+ init_attr->aff_eq_handle, /* R8 */
+ r9_reg, /* R9 */
+ max_r10_reg, /* R10 */
+ r11_in, /* R11 */
+ threshold, /* R12 */
+ qp_handle, /* R4 */
+ &r5_out, /* R5 */
+ &r6_out, /* R6 */
+ &r7_out, /* R7 */
+ &r8_out, /* R8 */
+ &r9_out, /* R9 */
+ &g_la_user_out, /* R10 */
+ &r11_out, /* R11 */
+ &r12_out); /* R12 */
+
+ init_attr->qp_nr = (u32)r5_out;
+
+ init_attr->act_nr_send_wqes =
+ (u16)EHEA_BMASK_GET(H_ALL_RES_QP_ACT_SWQE, r6_out);
+ init_attr->act_nr_rwqes_rq1 =
+ (u16)EHEA_BMASK_GET(H_ALL_RES_QP_ACT_R1WQE, r6_out);
+ init_attr->act_nr_rwqes_rq2 =
+ (u16)EHEA_BMASK_GET(H_ALL_RES_QP_ACT_R2WQE, r6_out);
+ init_attr->act_nr_rwqes_rq3 =
+ (u16)EHEA_BMASK_GET(H_ALL_RES_QP_ACT_R3WQE, r6_out);
+
+/* Interface is under construction */
+ init_attr->act_wqe_size_enc_sq = init_attr->wqe_size_enc_sq;
+ init_attr->act_wqe_size_enc_rq1 = init_attr->wqe_size_enc_rq1;
+ init_attr->act_wqe_size_enc_rq2 = init_attr->wqe_size_enc_rq2;
+ init_attr->act_wqe_size_enc_rq3 = init_attr->wqe_size_enc_rq3;
+
+ init_attr->nr_sq_pages =
+ (u32)EHEA_BMASK_GET(H_ALL_RES_QP_SIZE_SQ, r8_out);
+ init_attr->nr_rq1_pages =
+ (u32)EHEA_BMASK_GET(H_ALL_RES_QP_SIZE_RQ1, r8_out);
+ init_attr->nr_rq2_pages =
+ (u32)EHEA_BMASK_GET(H_ALL_RES_QP_SIZE_RQ2, r9_out);
+ init_attr->nr_rq3_pages =
+ (u32)EHEA_BMASK_GET(H_ALL_RES_QP_SIZE_RQ3, r9_out);
+
+ init_attr->liobn_sq =
+ (u32)EHEA_BMASK_GET(H_ALL_RES_QP_LIOBN_SQ, r11_out);
+ init_attr->liobn_rq1 =
+ (u32)EHEA_BMASK_GET(H_ALL_RES_QP_LIOBN_RQ1, r11_out);
+ init_attr->liobn_rq2 =
+ (u32)EHEA_BMASK_GET(H_ALL_RES_QP_LIOBN_RQ2, r12_out);
+ init_attr->liobn_rq3 =
+ (u32)EHEA_BMASK_GET(H_ALL_RES_QP_LIOBN_RQ3, r12_out);
+
+ if (hret == H_SUCCESS)
+ hcp_galpas_ctor(h_galpas, g_la_user_out, g_la_user_out);
+
+ EDEB(7, " qp_nr=%X, act_nr_send_wqes=%X, "
+ "act_nr_rcv_wqes_rq1=%X, act_nr_rcv_wqes_rq2=%X, "
+ "act_nr_rcv_wqes_rq3=%X, act_nr_send_sges=%X",
+ init_attr->qp_nr, init_attr->act_nr_send_wqes,
+ init_attr->act_nr_rwqes_rq1, init_attr->act_nr_rwqes_rq2,
+ init_attr->act_nr_rwqes_rq3, init_attr->act_wqe_size_enc_sq);
+ EDEB(7, " act_nr_rcv_sges_rq1=%X, act_nr_rcv_sges_rq2=%X, "
+ "act_nr_rcv_sges_rq3=%X", init_attr->act_wqe_size_enc_rq1,
+ init_attr->act_wqe_size_enc_rq2,
+ init_attr->act_wqe_size_enc_rq3);
+ EDEB_EX(7, " nr_sq_pages=%X, nr_rq1_pages=%X, nr_rq2_pages=%X"
+ "nr_rq3_pages=%X, galpa.user=%lx galpa.kernel=%lx",
+ init_attr->nr_sq_pages, init_attr->nr_rq1_pages,
+ init_attr->nr_rq2_pages, init_attr->nr_rq3_pages,
+ h_galpas->user.fw_handle, h_galpas->kernel.fw_handle);
+
+ return hret;
+}
+
+u64 ehea_h_alloc_resource_cq(const u64 hcp_adapter_handle,
+ struct ehea_cq *ehea_cq,
+ struct ehea_cq_attr *cq_attr,
+ u64 *cq_handle, struct h_galpas *galpas)
+{
+ u64 hret = H_ADAPTER_PARM;
+ u64 dummy = 0;
+ u64 act_nr_of_cqes_out;
+ u64 act_pages_out;
+ u64 g_la_privileged_out;
+ u64 g_la_user_out;
+
+ EDEB_EN(7, "hcp_adapter_handle=%lx eq_handle=%lx"
+ " cq_token=%x max_nr_of_cqes=%x",
+ hcp_adapter_handle, cq_attr->eq_handle, cq_attr->cq_token,
+ cq_attr->max_nr_of_cqes);
+
+
+ hret = ehea_hcall_7arg_7ret(H_ALLOC_HEA_RESOURCE,
+ hcp_adapter_handle, /* R4 */
+ H_ALL_RES_TYPE_CQ, /* R5 */
+ cq_attr->eq_handle, /* R6 */
+ cq_attr->cq_token, /* R7 */
+ cq_attr->max_nr_of_cqes, /* R8 */
+ dummy, /* R9 */
+ dummy, /* R10 */
+ cq_handle, /* R4 */
+ &dummy, /* R5 */
+ &dummy, /* R6 */
+ &act_nr_of_cqes_out, /* R7 */
+ &act_pages_out, /* R8 */
+ &g_la_privileged_out, /* R9 */
+ &g_la_user_out); /* R10 */
+
+ cq_attr->act_nr_of_cqes = act_nr_of_cqes_out;
+ cq_attr->nr_pages = act_pages_out;
+
+ if (hret == H_SUCCESS)
+ hcp_galpas_ctor(galpas, g_la_privileged_out, g_la_user_out);
+
+ EDEB_EX(7, "cq_handle=%lx act_nr_of_entries=%x act_pages=%x ",
+ *cq_handle, cq_attr->act_nr_of_cqes, cq_attr->nr_pages);
+
+ return hret;
+}
+
+/* Defines for H_CALL H_ALLOC_RESOURCE */
+#define H_ALL_RES_TYPE_QP 1
+#define H_ALL_RES_TYPE_CQ 2
+#define H_ALL_RES_TYPE_EQ 3
+#define H_ALL_RES_TYPE_MR 5
+#define H_ALL_RES_TYPE_MW 6
+
+/* input param R5 */
+#define H_ALL_RES_EQ_NEQ EHEA_BMASK_IBM(0, 0)
+#define H_ALL_RES_EQ_NON_NEQ_ISN EHEA_BMASK_IBM(6, 7)
+#define H_ALL_RES_EQ_INH_EQE_GEN EHEA_BMASK_IBM(16, 16)
+#define H_ALL_RES_EQ_RES_TYPE EHEA_BMASK_IBM(56, 63)
+/* input param R6 */
+#define H_ALL_RES_EQ_MAX_EQE EHEA_BMASK_IBM(32, 63)
+
+/* output param R6 */
+#define H_ALL_RES_EQ_LIOBN EHEA_BMASK_IBM(32, 63)
+
+/* output param R7 */
+#define H_ALL_RES_EQ_ACT_EQE EHEA_BMASK_IBM(32, 63)
+
+/* output param R8 */
+#define H_ALL_RES_EQ_ACT_PS EHEA_BMASK_IBM(32, 63)
+
+/* output param R9 */
+#define H_ALL_RES_EQ_ACT_EQ_IST_C EHEA_BMASK_IBM(30, 31)
+#define H_ALL_RES_EQ_ACT_EQ_IST_1 EHEA_BMASK_IBM(40, 63)
+
+/* output param R10 */
+#define H_ALL_RES_EQ_ACT_EQ_IST_2 EHEA_BMASK_IBM(40, 63)
+
+/* output param R11 */
+#define H_ALL_RES_EQ_ACT_EQ_IST_3 EHEA_BMASK_IBM(40, 63)
+
+/* output param R12 */
+#define H_ALL_RES_EQ_ACT_EQ_IST_4 EHEA_BMASK_IBM(40, 63)
+
+u64 ehea_h_alloc_resource_eq(const u64 hcp_adapter_handle,
+ struct ehea_eq *ehea_eq,
+ struct ehea_eq_attr *eq_attr, u64 *eq_handle)
+{
+ u64 hret = H_ADAPTER_PARM;
+ u64 dummy;
+ u64 eq_liobn = 0;
+ u64 allocate_controls = 0;
+ u64 ist1_out = 0;
+ u64 ist2_out = 0;
+ u64 ist3_out = 0;
+ u64 ist4_out = 0;
+ u64 act_nr_of_eqes_out = 0;
+ u64 act_pages_out = 0;
+
+ EDEB_EN(7, "hcp_adapter_handle=%lx new_control=%x "
+ "number_of_entries=%x",
+ hcp_adapter_handle, eq_attr->type, eq_attr->max_nr_of_eqes);
+
+ /* resource type */
+ allocate_controls =
+ EHEA_BMASK_SET(H_ALL_RES_EQ_RES_TYPE, H_ALL_RES_TYPE_EQ)
+ | EHEA_BMASK_SET(H_ALL_RES_EQ_NEQ, eq_attr->type ? 1 : 0)
+ | EHEA_BMASK_SET(H_ALL_RES_EQ_INH_EQE_GEN, !eq_attr->eqe_gen)
+ | EHEA_BMASK_SET(H_ALL_RES_EQ_NON_NEQ_ISN, 1);
+
+ hret = ehea_hcall_9arg_9ret(H_ALLOC_HEA_RESOURCE,
+ hcp_adapter_handle, /* R4 */
+ allocate_controls, /* R5 */
+ eq_attr->max_nr_of_eqes, /* R6 */
+ 0, 0, 0, 0, 0, 0, /* R7-R10 */
+ eq_handle, /* R4 */
+ &dummy, /* R5 */
+ &eq_liobn, /* R6 */
+ &act_nr_of_eqes_out, /* R7 */
+ &act_pages_out, /* R8 */
+ &ist1_out, /* R9 */
+ &ist2_out, /* R10 */
+ &ist3_out, /* R11 */
+ &ist4_out); /* R12 */
+
+ eq_attr->act_nr_of_eqes = act_nr_of_eqes_out;
+ eq_attr->nr_pages = act_pages_out;
+ eq_attr->ist1 = ist1_out;
+ eq_attr->ist2 = ist2_out;
+ eq_attr->ist3 = ist3_out;
+ eq_attr->ist4 = ist4_out;
+
+ EDEB_EX(7, "act_nr_of_entries=%x act_pages=%x eq_ist1=%x",
+ eq_attr->act_nr_of_eqes, eq_attr->nr_pages, eq_attr->ist1);
+
+ return hret;
+}
+
+u64 ehea_h_modify_ehea_qp(const u64 hcp_adapter_handle,
+ const u8 cat,
+ const u64 qp_handle,
+ const u64 sel_mask,
+ void *cb_addr,
+ u64 *inv_attr_id,
+ u64 *proc_mask,
+ u16 *out_swr,
+ u16 *out_rwr)
+{
+ u64 hret = H_ADAPTER_PARM;
+ u64 dummy = 0;
+ u64 act_out_swr = 0;
+ u64 act_out_rwr = 0;
+
+ EDEB_EN(7, "hcp_adapter_handle=%lX cat=%X qp_handle=%lX sel_mask=%lX "
+ "cb_addr=%p\n",
+ hcp_adapter_handle, (u16) cat, qp_handle, sel_mask, cb_addr);
+ if ((((u64)cb_addr) & (PAGE_SIZE - 1)) != 0)
+ panic("query_ehea_qp: cb_addr not on page boundary!!!");
+
+ EDEB_DMP(7, (u8 *) cb_addr, sizeof(struct hcp_modify_qp_cb_0),
+ "Before HCALL");
+
+ hret = ehea_hcall_9arg_9ret(H_MODIFY_HEA_QP,
+ hcp_adapter_handle, /* R4 */
+ (u64) cat, /* R5 */
+ qp_handle, /* R6 */
+ sel_mask, /* R7 */
+ virt_to_abs(cb_addr), /* R8 */
+ 0, 0, 0, 0, /* R9-R12 */
+ inv_attr_id, /* R4 */
+ &dummy, /* R5 */
+ &dummy, /* R6 */
+ &act_out_swr, /* R7 */
+ &act_out_rwr, /* R8 */
+ proc_mask, /* R9 */
+ &dummy, /* R10 */
+ &dummy, /* R11 */
+ &dummy); /* R12 */
+ *out_swr = act_out_swr;
+ *out_rwr = act_out_rwr;
+ if (hret != H_SUCCESS) {
+ EDEB_ERR(4, "H_MODIFY_HEA_QP failed. hret=%lx", hret);
+ }
+
+ EDEB_EX(7, "inv_attr_id=%lX proc_mask=%lX out_swr=%X out_rwr=%X",
+ *inv_attr_id, *proc_mask, *out_swr, *out_rwr);
+
+ return hret;
+}
+
+u64 ehea_h_register_rpage(const u64 hcp_adapter_handle,
+ const u8 pagesize,
+ const u8 queue_type,
+ const u64 resource_handle,
+ const u64 log_pageaddr, u64 count)
+{
+ u64 hret = H_ADAPTER_PARM;
+ u64 dummy;
+ u64 reg_control;
+ EDEB_EN(7, "hcp_adapter_handle=%lx pagesize=%x queue_type=%x "
+ "res_handle=%lx log_pageaddr=%lx "
+ "count=%lx",
+ hcp_adapter_handle,
+ pagesize, queue_type, resource_handle, log_pageaddr, count);
+
+ reg_control = EHEA_BMASK_SET(H_REG_RPAGE_PAGE_SIZE, pagesize)
+ | EHEA_BMASK_SET(H_REG_RPAGE_QT, queue_type);
+
+ hret = ehea_hcall_7arg_7ret(H_REGISTER_HEA_RPAGES,
+ hcp_adapter_handle, /* R4 */
+ reg_control, /* R5 */
+ resource_handle, /* R6 */
+ log_pageaddr, /* R7 */
+ count, /* R8 */
+ 0, /* R9 */
+ 0, /* R10 */
+ &dummy, /* R4 */
+ &dummy, /* R5 */
+ &dummy, /* R6 */
+ &dummy, /* R7 */
+ &dummy, /* R8 */
+ &dummy, /* R9 */
+ &dummy); /* R10 */
+ EDEB_EX(7, "hret=%lx", hret);
+ return hret;
+}
+
+u64 ehea_h_register_rpage_eq(const u64 hcp_adapter_handle,
+ const u64 eq_handle,
+ const u8 pagesize,
+ const u8 queue_type,
+ const u64 log_pageaddr, const u64 count)
+{
+ u64 hret = H_ADAPTER_PARM;
+
+ EDEB_EN(7, "hcp_adapter_handle=%lx eq_handle=%lx"
+ " pagesize=%x queue_type=%x log_pageaddr=%lx"
+ " count=%lx",
+ hcp_adapter_handle,
+ eq_handle, pagesize, queue_type, log_pageaddr, count);
+
+ if (count != 1) {
+ EDEB_ERR(4, "page counter=%lx", count);
+ return H_PARAMETER;
+ }
+
+ hret = ehea_h_register_rpage(hcp_adapter_handle,
+ pagesize,
+ queue_type,
+ eq_handle, log_pageaddr, count);
+ EDEB_EX(7, "hret=%lx", hret);
+ return hret;
+}
+
+u64 ehea_h_register_rpage_cq(const u64 hcp_adapter_handle,
+ const u64 cq_handle,
+ const u8 pagesize,
+ const u8 queue_type,
+ const u64 log_pageaddr,
+ const u64 count, const struct h_galpa gal)
+{
+ u64 hret = H_ADAPTER_PARM;
+
+ EDEB_EN(7, "hcp_adapter_handle=%lx cq_handle=%lx"
+ " pagesize=%x queue_type=%x log_pageaddr=%lx"
+ " count=%lx",
+ hcp_adapter_handle,
+ cq_handle, pagesize, queue_type, log_pageaddr, count);
+
+ if (count != 1) {
+ EDEB_ERR(4, "page counter=%lx", count);
+ return H_PARAMETER;
+ }
+
+ hret = ehea_h_register_rpage(hcp_adapter_handle,
+ pagesize,
+ queue_type,
+ cq_handle, log_pageaddr, count);
+
+ EDEB_EX(7, "hret=%lx", hret);
+ return hret;
+}
+
+u64 ehea_h_register_rpage_qp(const u64 hcp_adapter_handle,
+ const u64 qp_handle,
+ const u8 pagesize,
+ const u8 queue_type,
+ const u64 log_pageaddr,
+ const u64 count, struct h_galpa galpa)
+{
+ u64 hret = H_ADAPTER_PARM;
+
+ EDEB_EN(7, "hcp_adapter_handle=%lx qp_handle=%lx"
+ " pagesize=%x queue_type=%x log_pageaddr=%lx"
+ " count=%lx",
+ hcp_adapter_handle,
+ qp_handle, pagesize, queue_type, log_pageaddr, count);
+
+ if (count != 1) {
+ EDEB_ERR(4, "page counter=%lx", count);
+ return H_PARAMETER;
+ }
+
+ hret = ehea_h_register_rpage(hcp_adapter_handle,
+ pagesize,
+ queue_type,
+ qp_handle, log_pageaddr, count);
+ EDEB_EX(7, "hret=%lx", hret);
+ return hret;
+}
+
+u64 ehea_h_destroy_qp(const u64 hcp_adapter_handle,
+ struct ehea_qp *qp,
+ u64 qp_handle, struct h_galpas *galpas)
+{
+ u64 hret = H_ADAPTER_PARM;
+ int ret = 0;
+ u64 dummy;
+ u64 ladr_next_sq_wqe_out;
+ u64 ladr_next_rq1_wqe_out;
+ u64 ladr_next_rq2_wqe_out;
+ u64 ladr_next_rq3_wqe_out;
+
+ EDEB_EN(7, "qp = %p ipz_qp_handle=%lx adapter_handle=%lx",
+ qp, qp_handle, hcp_adapter_handle);
+
+ ret = hcp_galpas_dtor(galpas);
+ if (ret) {
+ EDEB_ERR(4, "Could not destroy qp->galpas");
+ return H_RESOURCE;
+ }
+
+ hret = ehea_hcall_7arg_7ret(H_DISABLE_AND_GET_HEA,
+ hcp_adapter_handle, /* R4 */
+ H_DISABLE_GET_EHEA_WQE_P, /* R5 */
+ qp_handle, /* R6 */
+ 0, 0, 0, 0, /* R7-R10 */
+ &ladr_next_sq_wqe_out, /* R4 */
+ &ladr_next_rq1_wqe_out, /* R5 */
+ &ladr_next_rq2_wqe_out, /* R6 */
+ &ladr_next_rq3_wqe_out, /* R7 */
+ &dummy, /* R8 */
+ &dummy, /* R9 */
+ &dummy); /* R10 */
+ if (hret == H_HARDWARE) {
+ EDEB_ERR(4, "HCA NOT operational - hret=%lx", hret);
+ return hret;
+ }
+
+ hret = ehea_hcall_7arg_7ret(H_FREE_RESOURCE,
+ hcp_adapter_handle, /* R4 */
+ qp_handle, /* R5 */
+ 0, 0, 0, 0, 0, /* R6-R10 */
+ &dummy, /* R4 */
+ &dummy, /* R5 */
+ &dummy, /* R6 */
+ &dummy, /* R7 */
+ &dummy, /* R8 */
+ &dummy, /* R9 */
+ &dummy); /* R10 */
+ if (hret == H_RESOURCE)
+ EDEB_ERR(4, "resource still in use - hret=%lx", hret);
+ EDEB_EX(7, "hret=%lx", hret);
+ return hret;
+}
+
+u64 ehea_h_destroy_cq(const u64 hcp_adapter_handle,
+ struct ehea_cq *cq,
+ u64 cq_handle, struct h_galpas *galpas)
+{
+ u64 hret = H_ADAPTER_PARM;
+ u64 dummy;
+
+ EDEB_EN(7, "destroy CQ Entry:>>>> cq = %p ,ipz_cq_handle=%lx"
+ "; adapter_handle=%lx", cq, cq_handle,
+ hcp_adapter_handle);
+ hret = hcp_galpas_dtor(galpas);
+ if (hret != H_SUCCESS) {
+ EDEB_ERR(4, "could not destroy cp->galpas");
+ return H_RESOURCE;
+ }
+
+ hret = ehea_hcall_7arg_7ret(H_FREE_RESOURCE,
+ hcp_adapter_handle, /* R4 */
+ cq_handle, /* R5 */
+ 0, 0, 0, 0, 0, /* R6-R10 */
+ &dummy, /* R4 */
+ &dummy, /* R5 */
+ &dummy, /* R6 */
+ &dummy, /* R7 */
+ &dummy, /* R8 */
+ &dummy, /* R9 */
+ &dummy); /* R10 */
+
+ if (hret == H_RESOURCE)
+ EDEB_ERR(4, "resource in use - hret=%lx ", hret);
+
+ EDEB_EX(7, "hret=%lx", hret);
+ return hret;
+}
+
+u64 ehea_h_destroy_eq(const u64 hcp_adapter_handle,
+ struct ehea_eq * eq,
+ u64 eq_handle, struct h_galpas * galpas)
+{
+ u64 hret = H_ADAPTER_PARM;
+ u64 dummy;
+
+ EDEB_EN(7, "eq=%p ipz_eq_handle=%lx adapter_handle=%lx",
+ eq, eq_handle, hcp_adapter_handle);
+
+ hret = hcp_galpas_dtor(galpas);
+
+ if (hret != H_SUCCESS) {
+ EDEB_ERR(4, "could not destroy ep->galpas");
+ return H_RESOURCE;
+ }
+
+ hret = ehea_hcall_7arg_7ret(H_FREE_RESOURCE,
+ hcp_adapter_handle, /* R4 */
+ eq_handle, /* R5 */
+ 0, 0, 0, 0, 0, /* R6-R10 */
+ &dummy, /* R4 */
+ &dummy, /* R5 */
+ &dummy, /* R6 */
+ &dummy, /* R7 */
+ &dummy, /* R8 */
+ &dummy, /* R9 */
+ &dummy); /* R10 */
+
+ if (hret == H_RESOURCE)
+ EDEB_ERR(4, "resource in use - hret=%lx ", hret);
+
+ EDEB_EX(7, "hret=%lx", hret);
+ return hret;
+}
+
+u64 ehea_h_free_resource_mr(const u64 hcp_adapter_handle,
+ const u64 mr_handle)
+{
+ u64 hret = H_ADAPTER_PARM;
+ u64 dummy;
+
+ EDEB_EN(7, "adapter_handle=%lx mr_handle=%lx",
+ hcp_adapter_handle, mr_handle);
+
+ hret = ehea_hcall_7arg_7ret(H_FREE_RESOURCE,
+ hcp_adapter_handle, /* R4 */
+ mr_handle, /* R5 */
+ 0, 0, 0, 0, 0, /* R6-R10 */
+ &dummy, /* R4 */
+ &dummy, /* R5 */
+ &dummy, /* R6 */
+ &dummy, /* R7 */
+ &dummy, /* R8 */
+ &dummy, /* R9 */
+ &dummy); /* R10 */
+
+ EDEB_EX(7, "hret=%lx", hret);
+ return hret;
+}
+
+u64 ehea_h_alloc_resource_mr(const u64 hcp_adapter_handle,
+ const u64 vaddr,
+ const u64 length,
+ const u32 access_ctrl,
+ const u32 pd, u64 * mr_handle, u32 * lkey)
+{
+ u64 hret = H_ADAPTER_PARM;
+ u64 dummy;
+ u64 lkey_out;
+
+ EDEB_EN(7, "hcp_adapter_handle=%lx vaddr=%lx length=%lx "
+ "access_ctrl=%x pd=%x",
+ hcp_adapter_handle, vaddr, length, access_ctrl, pd);
+
+
+ hret = ehea_hcall_7arg_7ret(H_ALLOC_HEA_RESOURCE,
+ hcp_adapter_handle, /* R4 */
+ 5, /* R5 */
+ vaddr, /* R6 */
+ length, /* R7 */
+ (((u64) access_ctrl) << 32ULL), /* R8 */
+ pd, /* R9 */
+ 0, /* R10 */
+ mr_handle, /* R4 */
+ &dummy, /* R5 */
+ &lkey_out, /* R6 */
+ &dummy, /* R7 */
+ &dummy, /* R8 */
+ &dummy, /* R9 */
+ &dummy); /* R10 */
+ *lkey = (u32) lkey_out;
+
+ EDEB_EX(7, "hret=%lX mr_handle=%lX, lkey=%x", hret, *mr_handle, *lkey);
+ return hret;
+}
+
+u64 ehea_h_register_rpage_mr(const u64 hcp_adapter_handle,
+ const u64 mr_handle,
+ const u8 pagesize,
+ const u8 queue_type,
+ const u64 log_pageaddr, const u64 count)
+{
+ u64 hret = H_ADAPTER_PARM;
+
+ EDEB_EN(7, "hcp_adapter_handle=%lx mr_handle->handle=%lx"
+ " pagesize=%x queue_type=%x log_pageaddr=%lx"
+ " count=%lx",
+ hcp_adapter_handle,
+ mr_handle, pagesize, queue_type, log_pageaddr, count);
+
+ if ((count > 1) && (log_pageaddr & 0xfff)) {
+ EDEB_ERR(4, "log_pageaddr not on a 4k boundary");
+ hret = H_PARAMETER;
+ } else
+ hret = ehea_h_register_rpage(hcp_adapter_handle,
+ pagesize,
+ queue_type,
+ mr_handle, log_pageaddr, count);
+
+ EDEB_EX(7, "hret=%lx", hret);
+ return hret;
+}
+
+u64 ehea_h_query_ehea(const u64 hcp_adapter_handle, void *cb_addr)
+{
+ u64 hret = H_ADAPTER_PARM;
+ u64 dummy = 0;
+ struct hcp_query_ehea *query_ehea_cb = (struct hcp_query_ehea *)cb_addr;
+ u64 cb_logaddr;
+
+ EDEB_EN(7, "hcp_adapter_handle=%lX query_ehea_cb=%p",
+ hcp_adapter_handle, query_ehea_cb);
+
+ cb_logaddr = virt_to_abs(cb_addr);
+
+ hret = ehea_hcall_7arg_7ret(H_QUERY_HEA,
+ hcp_adapter_handle, /* R4 */
+ cb_logaddr, /* R5 */
+ 0, 0, 0, 0, 0, /* R6-R10 */
+ &dummy, /* R4 */
+ &dummy, /* R5 */
+ &dummy, /* R6 */
+ &dummy, /* R7 */
+ &dummy, /* R8 */
+ &dummy, /* R9 */
+ &dummy); /* R10 */
+
+ EDEB_DMP(7, (u8 *) cb_addr, sizeof(struct hcp_query_ehea),
+ "hcp_query_ehea");
+
+ if (hret != H_SUCCESS)
+ EDEB_ERR(4, "H_QUERY_HEA failed. hret=%lx", hret);
+
+ EDEB_EX(7, "hret=%lx", hret);
+ return hret;
+}
+
+u64 ehea_h_query_ehea_port(const u64 hcp_adapter_handle,
+ const u16 port_num,
+ const u8 cb_cat, const u64 select_mask,
+ void *cb_addr)
+{
+ u64 hret = H_ADAPTER_PARM;
+ u64 port_info = 0;
+ u64 arr_index = 0;
+ u64 dummy = 0;
+ u64 cb_logaddr = virt_to_abs(cb_addr);
+
+ EDEB_EN(7, "hcp_adapter_handle=%lX port_num=%X cb_cat=%X "
+ "select_mask=%lX cb_addr=%lX",
+ hcp_adapter_handle, port_num, cb_cat, select_mask, cb_logaddr);
+
+ port_info = EHEA_BMASK_SET(H_MEHEAPORT_CAT, cb_cat)
+ | EHEA_BMASK_SET(H_MEHEAPORT_PN, port_num);
+
+ hret = ehea_hcall_7arg_7ret(H_QUERY_HEA_PORT,
+ hcp_adapter_handle, /* R4 */
+ port_info, /* R5 */
+ select_mask, /* R6 */
+ arr_index, /* R7 */
+ cb_logaddr, /* R8 */
+ 0, 0, /* R9-R10 */
+ &dummy, /* R4 */
+ &dummy, /* R5 */
+ &dummy, /* R6 */
+ &dummy, /* R7 */
+ &dummy, /* R8 */
+ &dummy, /* R9 */
+ &dummy); /* R10 */
+ if (hret != H_SUCCESS)
+ EDEB_ERR(4, "H_QUERY_HEA_PORT failed. hret=%lx", hret);
+
+ EDEB_EX(7, "hret=%lx", hret);
+ return hret;
+}
+
+u64 ehea_h_modify_ehea_port(const u64 hcp_adapter_handle,
+ const u16 port_num,
+ const u8 cb_cat,
+ const u64 select_mask, void *cb_addr)
+{
+ u64 hret = H_ADAPTER_PARM;
+ u64 port_info = 0;
+ u64 arr_index = 0;
+ u64 dummy = 0;
+ u64 cb_logaddr = virt_to_abs(cb_addr);
+
+ EDEB_EN(7, "hcp_adapter_handle=%lX port_num=%X cb_cat=%X "
+ "select_mask=%lX cb_addr=%lX",
+ hcp_adapter_handle, port_num, cb_cat, select_mask, cb_logaddr);
+
+ port_info = EHEA_BMASK_SET(H_MEHEAPORT_CAT, cb_cat)
+ | EHEA_BMASK_SET(H_MEHEAPORT_PN, port_num);
+
+ EDEB_DMP(7, (u8 *) cb_addr,
+ sizeof(struct hcp_query_ehea_port_cb_0), "Before HCALL");
+
+ hret = ehea_hcall_7arg_7ret(H_MODIFY_HEA_PORT,
+ hcp_adapter_handle, /* R4 */
+ port_info, /* R5 */
+ select_mask, /* R6 */
+ arr_index, /* R7 */
+ cb_logaddr, /* R8 */
+ 0, 0, /* R9-R10 */
+ &dummy, /* R4 */
+ &dummy, /* R5 */
+ &dummy, /* R6 */
+ &dummy, /* R7 */
+ &dummy, /* R8 */
+ &dummy, /* R9 */
+ &dummy); /* R10 */
+
+
+ if (hret != H_SUCCESS)
+ EDEB_ERR(4, "H_MODIFY_HEA_PORT failed. hret=%lx", hret);
+
+ EDEB_EX(7, "hret=%lx", hret);
+ return hret;
+}
+
+u64 ehea_h_reg_dereg_bcmc(const u64 hcp_adapter_handle,
+ const u16 port_num,
+ const u8 reg_type,
+ const u64 mc_mac_addr,
+ const u16 vlan_id, const u32 hcall_id)
+{
+ u64 hret = H_ADAPTER_PARM;
+ u64 r5_port_num = 0;
+ u64 r6_reg_type = 0;
+ u64 r7_mc_mac_addr = 0;
+ u64 r8_vlan_id = 0;
+ u64 dummy = 0;
+
+ u64 mac_addr = mc_mac_addr >> 16;
+
+ EDEB_EN(7, "hcp_adapter_handle=%lX port_num=%X reg_type=%X "
+ "mc_mac_addr=%lX hcall_id=%X",
+ hcp_adapter_handle, port_num, reg_type, mc_mac_addr, hcall_id);
+
+ r5_port_num = EHEA_BMASK_SET(H_REGBCMC_PN, port_num);
+ r6_reg_type = EHEA_BMASK_SET(H_REGBCMC_REGTYPE, reg_type);
+ r7_mc_mac_addr = EHEA_BMASK_SET(H_REGBCMC_MACADDR, mac_addr);
+ r8_vlan_id = EHEA_BMASK_SET(H_REGBCMC_VLANID, vlan_id);
+
+
+ hret = ehea_hcall_7arg_7ret(hcall_id,
+ hcp_adapter_handle, /* R4 */
+ r5_port_num, /* R5 */
+ r6_reg_type, /* R6 */
+ r7_mc_mac_addr, /* R7 */
+ r8_vlan_id, /* R8 */
+ 0, 0, /* R9-R10 */
+ &dummy, /* R4 */
+ &dummy, /* R5 */
+ &dummy, /* R6 */
+ &dummy, /* R7 */
+ &dummy, /* R8 */
+ &dummy, /* R9 */
+ &dummy); /* R10 */
+
+ if (hret != H_SUCCESS)
+ EDEB_ERR(4, "HCALL 0x%x failed. hret=%lx", hcall_id, hret);
+
+ EDEB_EX(7, "hret=%lx", hret);
+ return hret;
+}
+
+u64 ehea_h_reset_events(const u64 hcp_adapter_handle,
+ const u64 neq_handle, const u64 event_mask)
+{
+ u64 hret = H_ADAPTER_PARM;
+ u64 dummy = 0;
+
+ EDEB_EN(7, "hcp_adapter_handle=%lX neq_handle=%lX event_mask=%lX ",
+ hcp_adapter_handle, neq_handle, event_mask);
+
+ hret = ehea_hcall_7arg_7ret(H_RESET_EVENTS,
+ hcp_adapter_handle, /* R4 */
+ neq_handle, /* R5 */
+ event_mask, /* R6 */
+ 0, 0, 0, 0, /* R7-R10 */
+ &dummy, /* R4 */
+ &dummy, /* R5 */
+ &dummy, /* R6 */
+ &dummy, /* R7 */
+ &dummy, /* R8 */
+ &dummy, /* R9 */
+ &dummy); /* R10 */
+ if (hret != H_SUCCESS)
+ EDEB_ERR(4, "H_RESET_EVENTS failed. hret=%lx", hret);
+
+ EDEB_EX(7, "hret=%lx", hret);
+ return hret;
+}
--- linux-2.6.17.1-orig/drivers/net/ehea/ehea_phyp.h 1969-12-31 16:00:00.000000000 -0800
+++ kernel/drivers/net/ehea/ehea_phyp.h 2006-06-21 04:44:50.904372128 -0700
@@ -0,0 +1,625 @@
+/*
+ * linux/drivers/net/ehea/ehea_phyp.h
+ *
+ * eHEA ethernet device driver for IBM eServer System p
+ *
+ * (C) Copyright IBM Corp. 2006
+ *
+ * Authors:
+ * Christoph Raisch <raisch@de.ibm.com>
+ * Jan-Bernd Themann <themann@de.ibm.com>
+ * Heiko-Joerg Schick <schickhj@de.ibm.com>
+ * Thomas Klein <tklein@de.ibm.com>
+ *
+ *
+ * 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.
+ */
+
+#ifndef __EHEA_PHYP_H__
+#define __EHEA_PHYP_H__
+
+#include <linux/delay.h>
+#include <asm/hvcall.h>
+#include "ehea.h"
+#include "ehea_hw.h"
+#include "ehea_hcall.h"
+
+
+static inline u32 get_longbusy_msecs(int long_busy_ret_code)
+{
+ switch (long_busy_ret_code) {
+ case H_LONG_BUSY_ORDER_1_MSEC:
+ return 1;
+ case H_LONG_BUSY_ORDER_10_MSEC:
+ return 10;
+ case H_LONG_BUSY_ORDER_100_MSEC:
+ return 100;
+ case H_LONG_BUSY_ORDER_1_SEC:
+ return 1000;
+ case H_LONG_BUSY_ORDER_10_SEC:
+ return 10000;
+ case H_LONG_BUSY_ORDER_100_SEC:
+ return 100000;
+ default:
+ return 1;
+ }
+}
+
+
+/* Notification Event Queue (NEQ) Entry bit masks */
+#define NEQE_EVENT_CODE EHEA_BMASK_IBM(2, 7)
+#define NEQE_PORTNUM EHEA_BMASK_IBM(32, 47)
+#define NEQE_PORT_UP EHEA_BMASK_IBM(16, 16)
+#define NEQE_EXTSWITCH_PORT_UP EHEA_BMASK_IBM(17, 17)
+#define NEQE_EXTSWITCH_PRIMARY EHEA_BMASK_IBM(18, 18)
+#define NEQE_PLID EHEA_BMASK_IBM(16, 47)
+
+/* Notification Event Codes */
+#define EHEA_EC_PORTSTATE_CHG 0x30
+#define EHEA_EC_ADAPTER_MALFUNC 0x32
+#define EHEA_EC_PORT_MALFUNC 0x33
+
+/* Notification Event Log Register (NELR) bit masks */
+#define NELR_PORT_MALFUNC EHEA_BMASK_IBM(61, 61)
+#define NELR_ADAPTER_MALFUNC EHEA_BMASK_IBM(62, 62)
+#define NELR_PORTSTATE_CHG EHEA_BMASK_IBM(63, 63)
+
+static inline long ehea_hcall_9arg_9ret(unsigned long opcode,
+ unsigned long arg1,
+ unsigned long arg2,
+ unsigned long arg3,
+ unsigned long arg4,
+ unsigned long arg5,
+ unsigned long arg6,
+ unsigned long arg7,
+ unsigned long arg8,
+ unsigned long arg9,
+ unsigned long *out1,
+ unsigned long *out2,
+ unsigned long *out3,
+ unsigned long *out4,
+ unsigned long *out5,
+ unsigned long *out6,
+ unsigned long *out7,
+ unsigned long *out8,
+ unsigned long *out9)
+{
+ long hret = H_SUCCESS;
+ int i, sleep_msecs;
+
+ EDEB_EN(7, "opcode=%lx arg1=%lx arg2=%lx arg3=%lx arg4=%lx "
+ "arg5=%lx arg6=%lx arg7=%lx arg8=%lx arg9=%lx",
+ opcode, arg1, arg2, arg3, arg4, arg5, arg6, arg7,
+ arg8, arg9);
+
+
+ for (i = 0; i < 5; i++) {
+ hret = plpar_hcall_9arg_9ret(opcode,
+ arg1, arg2, arg3, arg4,
+ arg5, arg6, arg7, arg8,
+ arg9,
+ out1, out2, out3, out4,
+ out5, out6, out7, out8,
+ out9);
+
+ if (H_IS_LONG_BUSY(hret)) {
+ sleep_msecs = get_longbusy_msecs(hret);
+ msleep_interruptible(sleep_msecs);
+ continue;
+ }
+
+ if (hret < H_SUCCESS)
+ EDEB_ERR(4, "opcode=%lx hret=%lx"
+ " arg1=%lx arg2=%lx arg3=%lx arg4=%lx"
+ " arg5=%lx arg6=%lx arg7=%lx arg8=%lx"
+ " arg9=%lx"
+ " out1=%lx out2=%lx out3=%lx out4=%lx"
+ " out5=%lx out6=%lx out7=%lx out8=%lx"
+ " out9=%lx",
+ opcode, hret,
+ arg1, arg2, arg3, arg4,
+ arg5, arg6, arg7, arg8,
+ arg9,
+ *out1, *out2, *out3, *out4,
+ *out5, *out6, *out7, *out8,
+ *out9);
+
+ EDEB_EX(7, "opcode=%lx hret=%lx out1=%lx out2=%lx out3=%lx "
+ "out4=%lx out5=%lx out6=%lx out7=%lx out8=%lx out9=%lx",
+ opcode, hret,*out1, *out2, *out3, *out4, *out5, *out6,
+ *out7, *out8, *out9);
+ return hret;
+
+ }
+
+ EDEB_EX(7, "opcode=%lx ret=H_BUSY", opcode);
+ return H_BUSY;
+}
+
+inline static long ehea_hcall_7arg_7ret(unsigned long opcode,
+ unsigned long arg1,
+ unsigned long arg2,
+ unsigned long arg3,
+ unsigned long arg4,
+ unsigned long arg5,
+ unsigned long arg6,
+ unsigned long arg7,
+ unsigned long *out1,
+ unsigned long *out2,
+ unsigned long *out3,
+ unsigned long *out4,
+ unsigned long *out5,
+ unsigned long *out6,
+ unsigned long *out7)
+{
+ long hret = H_SUCCESS;
+ int i, sleep_msecs;
+
+ EDEB_EN(7, "opcode=%lx arg1=%lx arg2=%lx arg3=%lx arg4=%lx arg5=%lx"
+ " arg6=%lx arg7=%lx", opcode, arg1, arg2, arg3, arg4, arg5,
+ arg6, arg7);
+
+ for (i = 0; i < 5; i++) {
+ hret = plpar_hcall_7arg_7ret(opcode,
+ arg1, arg2, arg3, arg4,
+ arg5, arg6, arg7,
+ out1, out2, out3, out4,
+ out5, out6, out7);
+
+ if (H_IS_LONG_BUSY(hret)) {
+ sleep_msecs = get_longbusy_msecs(hret);
+ msleep_interruptible(sleep_msecs);
+ continue;
+ }
+
+ if (hret < H_SUCCESS)
+ EDEB_ERR(4, "opcode=%lx ret=%lx"
+ " arg1=%lx arg2=%lx arg3=%lx arg4=%lx"
+ " arg5=%lx arg6=%lx arg7=%lx"
+ " out1=%lx out2=%lx out3=%lx out4=%lx"
+ " out5=%lx out6=%lx out7=%lx",
+ opcode, hret,
+ arg1, arg2, arg3, arg4,
+ arg5, arg6, arg7,
+ *out1, *out2, *out3, *out4,
+ *out5, *out6, *out7);
+
+ EDEB_EX(7, "opcode=%lx hret=%lx out1=%lx out2=%lx out3=%lx "
+ "out4=%lx out5=%lx out6=%lx out7=%lx",
+ opcode, hret, *out1, *out2, *out3, *out4, *out5,
+ *out6, *out7);
+ return hret;
+ }
+
+ EDEB_EX(7, "opcode=%lx hret=H_BUSY", opcode);
+
+ return H_BUSY;
+}
+
+static inline int hcp_galpas_ctor(struct h_galpas *galpas,
+ u64 paddr_kernel, u64 paddr_user)
+{
+
+ EDEB_EN(7, "ioremap physaddr=%lx mapaddr=%lx\n",
+ paddr_kernel, galpas->kernel.fw_handle);
+ galpas->kernel.fw_handle = (u64) ioremap(paddr_kernel, PAGE_SIZE);
+ galpas->user.fw_handle = paddr_user;
+
+ EDEB_EX(7, "paddr_kernel=%lx paddr_user=%lx galpas->kernel=%lx"
+ " galpas->user=%lx", paddr_kernel, paddr_user,
+ galpas->kernel.fw_handle, galpas->user.fw_handle);
+ return 0;
+}
+
+static inline int hcp_galpas_dtor(struct h_galpas *galpas)
+{
+
+ if (galpas->kernel.fw_handle)
+ iounmap((void *)galpas->kernel.fw_handle);
+ galpas->user.fw_handle = galpas->kernel.fw_handle = 0;
+ return 0;
+}
+
+struct hcp_modify_qp_cb_0 {
+ u64 qp_ctl_reg; /* 00 */
+ u32 max_swqe; /* 02 */
+ u32 max_rwqe; /* 03 */
+ u32 port_nb; /* 04 */
+ u32 reserved0; /* 05 */
+ u64 qp_aer; /* 06 */
+ u64 qp_tenure; /* 08 */
+};
+
+/* Hcall Query/Modify Queue Pair Control Block 0 Selection Mask Bits */
+#define H_QPCB0_ALL EHEA_BMASK_IBM(0, 5)
+#define H_QPCB0_QP_CTL_REG EHEA_BMASK_IBM(0, 0)
+#define H_QPCB0_MAX_SWQE EHEA_BMASK_IBM(1, 1)
+#define H_QPCB0_MAX_RWQE EHEA_BMASK_IBM(2, 2)
+#define H_QPCB0_PORT_NB EHEA_BMASK_IBM(3, 3)
+#define H_QPCB0_QP_AER EHEA_BMASK_IBM(4, 4)
+#define H_QPCB0_QP_TENURE EHEA_BMASK_IBM(5, 5)
+
+/* Queue Pair Control Register Status Bits */
+#define H_QP_CR_ENABLED 0x8000000000000000 /* Queue Pair enabled */
+ /* QP States: */
+#define H_QP_CR_STATE_RESET 0x0000010000000000 /* Reset */
+#define H_QP_CR_STATE_INITIALIZED 0x0000020000000000 /* Initialized */
+#define H_QP_CR_STATE_RDY2RCV 0x0000030000000000 /* Ready to receive */
+#define H_QP_CR_STATE_RDY2SND 0x0000050000000000 /* Ready to send */
+#define H_QP_CR_STATE_ERROR 0x0000800000000000 /* Error */
+
+struct hcp_modify_qp_cb_1 {
+ u32 qpn; /* 00 */
+ u32 qp_asyn_ev_eq_nb; /* 01 */
+ u64 sq_cq_handle; /* 02 */
+ u64 rq_cq_handle; /* 04 */
+ /* sgel = scatter gather element */
+ u32 sgel_nb_sq; /* 06 */
+ u32 sgel_nb_rq1; /* 07 */
+ u32 sgel_nb_rq2; /* 08 */
+ u32 sgel_nb_rq3; /* 09 */
+};
+
+/* Hcall Query/Modify Queue Pair Control Block 1 Selection Mask Bits */
+#define H_QPCB1_ALL EHEA_BMASK_IBM(0, 7)
+#define H_QPCB1_QPN EHEA_BMASK_IBM(0, 0)
+#define H_QPCB1_ASYN_EV_EQ_NB EHEA_BMASK_IBM(1, 1)
+#define H_QPCB1_SQ_CQ_HANDLE EHEA_BMASK_IBM(2, 2)
+#define H_QPCB1_RQ_CQ_HANDLE EHEA_BMASK_IBM(3, 3)
+#define H_QPCB1_SGEL_NB_SQ EHEA_BMASK_IBM(4, 4)
+#define H_QPCB1_SGEL_NB_RQ1 EHEA_BMASK_IBM(5, 5)
+#define H_QPCB1_SGEL_NB_RQ2 EHEA_BMASK_IBM(6, 6)
+#define H_QPCB1_SGEL_NB_RQ3 EHEA_BMASK_IBM(7, 7)
+
+struct hcp_query_ehea {
+ u32 cur_num_qps; /* 00 */
+ u32 cur_num_cqs; /* 01 */
+ u32 cur_num_eqs; /* 02 */
+ u32 cur_num_mrs; /* 03 */
+ u32 auth_level; /* 04 */
+ u32 max_num_qps; /* 05 */
+ u32 max_num_cqs; /* 06 */
+ u32 max_num_eqs; /* 07 */
+ u32 max_num_mrs; /* 08 */
+ u32 reserved0; /* 09 */
+ u32 int_clock_freq; /* 10 */
+ u32 max_num_pds; /* 11 */
+ u32 max_num_addr_handles; /* 12 */
+ u32 max_num_cqes; /* 13 */
+ u32 max_num_wqes; /* 14 */
+ u32 max_num_sgel_rq1wqe; /* 15 */
+ u32 max_num_sgel_rq2wqe; /* 16 */
+ u32 max_num_sgel_rq3wqe; /* 17 */
+ u32 mr_page_size; /*define */
+ u32 reserved1; /* 19 */
+ u64 max_mr_size; /* 20 */
+ u64 reserved2; /* 22 */
+ u32 num_ports; /* 24 */
+ u32 reserved3; /* 25 */
+ u32 reserved4; /* 26 */
+ u32 reserved5; /* 27 */
+ u64 max_mc_mac; /* 28 */
+ u64 ehea_cap; /* 30 */
+ u32 max_isn_per_eq; /* 32 */
+ u32 max_num_neq; /* 33 */
+ u64 max_num_vlan_ids; /* 34 */
+ u32 max_num_port_group; /* 36 */
+ u32 max_num_phys_port; /* 37 */
+
+};
+
+/* Hcall Query/Modify Port Control Block defines */
+#define H_PORT_CB0 0
+#define H_PORT_CB1 1
+#define H_PORT_CB2 2
+#define H_PORT_CB3 3
+#define H_PORT_CB4 4
+#define H_PORT_CB5 5
+#define H_PORT_CB6 6
+#define H_PORT_CB7 7
+
+struct hcp_query_ehea_port_cb_0 {
+ u64 port_mac_addr;
+ u64 port_rc;
+ u64 reserved0;
+ u32 port_op_state;
+ u32 port_speed;
+ u32 ext_swport_op_state;
+ u32 neg_tpf_prpf;
+ u32 num_default_qps;
+ u32 reserved1;
+ u64 default_qpn_array[16];
+};
+
+/* Hcall Query/Modify Port Control Block 0 Selection Mask Bits */
+#define H_PORT_CB0_ALL EHEA_BMASK_IBM(0, 7) /* Set all bits */
+#define H_PORT_CB0_MAC EHEA_BMASK_IBM(0, 0) /* MAC address */
+#define H_PORT_CB0_PRC EHEA_BMASK_IBM(1, 1) /* Port Recv Control */
+#define H_PORT_CB0_DEFQPNARRAY EHEA_BMASK_IBM(7, 7) /* Default QPN Array */
+
+/* Hcall Query Port: Returned port speed values */
+#define H_PORT_SPEED_10M_H 1 /* 10 Mbps, Half Duplex */
+#define H_PORT_SPEED_10M_F 2 /* 10 Mbps, Full Duplex */
+#define H_PORT_SPEED_100M_H 3 /* 100 Mbps, Half Duplex */
+#define H_PORT_SPEED_100M_F 4 /* 100 Mbps, Full Duplex */
+#define H_PORT_SPEED_1G_F 6 /* 1 Gbps, Full Duplex */
+#define H_PORT_SPEED_10G_F 8 /* 10 Gbps, Full Duplex */
+
+/* Port Receive Control Status Bits */
+#define PXLY_RC_VALID EHEA_BMASK_IBM(49, 49)
+#define PXLY_RC_VLAN_XTRACT EHEA_BMASK_IBM(50, 50)
+#define PXLY_RC_TCP_6_TUPLE EHEA_BMASK_IBM(51, 51)
+#define PXLY_RC_UDP_6_TUPLE EHEA_BMASK_IBM(52, 52)
+#define PXLY_RC_TCP_3_TUPLE EHEA_BMASK_IBM(53, 53)
+#define PXLY_RC_TCP_2_TUPLE EHEA_BMASK_IBM(54, 54)
+#define PXLY_RC_LLC_SNAP EHEA_BMASK_IBM(55, 55)
+#define PXLY_RC_JUMBO_FRAME EHEA_BMASK_IBM(56, 56)
+#define PXLY_RC_FRAG_IP_PKT EHEA_BMASK_IBM(57, 57)
+#define PXLY_RC_TCP_UDP_CHKSUM EHEA_BMASK_IBM(58, 58)
+#define PXLY_RC_IP_CHKSUM EHEA_BMASK_IBM(59, 59)
+#define PXLY_RC_MAC_FILTER EHEA_BMASK_IBM(60, 60)
+#define PXLY_RC_UNTAG_FILTER EHEA_BMASK_IBM(61, 61)
+#define PXLY_RC_VLAN_TAG_FILTER EHEA_BMASK_IBM(62, 63)
+
+#define PXLY_RC_VLAN_FILTER 2
+#define PXLY_RC_VLAN_PERM 0
+
+
+#define H_PORT_CB1_ALL 0x8000000000000000
+
+struct hcp_query_ehea_port_cb_1 {
+ u64 vlan_filter[64];
+};
+
+#define H_PORT_CB2_ALL 0xFFE0000000000000
+
+struct hcp_query_ehea_port_cb_2 {
+ u64 rxo;
+ u64 rxucp;
+ u64 rxufd;
+ u64 rxuerr;
+ u64 rxftl;
+ u64 rxmcp;
+ u64 rxbcp;
+ u64 txo;
+ u64 txucp;
+ u64 txmcp;
+ u64 txbcp;
+};
+
+struct hcp_query_ehea_port_cb_3 {
+ u64 vlan_bc_filter[64];
+ u64 vlan_mc_filter[64];
+ u64 vlan_un_filter[64];
+ u64 port_mac_hash_array[64];
+};
+
+#define H_PORT_CB4_ALL 0xF000000000000000
+#define H_PORT_CB4_JUMBO 0x1000000000000000
+
+struct hcp_query_ehea_port_cb_4 {
+ u32 port_speed;
+ u32 pause_frame;
+ u32 ens_port_op_state;
+ u32 jumbo_frame;
+ u32 ens_port_wrap;
+};
+
+struct hcp_query_ehea_port_cb_5 {
+ u64 prc; /* 00 */
+ u64 uaa; /* 01 */
+ u64 macvc; /* 02 */
+ u64 xpcsc; /* 03 */
+ u64 xpcsp; /* 04 */
+ u64 pcsid; /* 05 */
+ u64 xpcsst; /* 06 */
+ u64 pthlb; /* 07 */
+ u64 pthrb; /* 08 */
+ u64 pqu; /* 09 */
+ u64 pqd; /* 10 */
+ u64 prt; /* 11 */
+ u64 wsth; /* 12 */
+ u64 rcb; /* 13 */
+ u64 rcm; /* 14 */
+ u64 rcu; /* 15 */
+ u64 macc; /* 16 */
+ u64 pc; /* 17 */
+ u64 pst; /* 18 */
+ u64 ducqpn; /* 19 */
+ u64 mcqpn; /* 20 */
+ u64 mma; /* 21 */
+ u64 pmc0h; /* 22 */
+ u64 pmc0l; /* 23 */
+ u64 lbc; /* 24 */
+};
+
+#define H_PORT_CB6_ALL 0xFFFFFE7FFFFF8000
+
+struct hcp_query_ehea_port_cb_6 {
+ u64 rxo; /* 00 */
+ u64 rx64; /* 01 */
+ u64 rx65; /* 02 */
+ u64 rx128; /* 03 */
+ u64 rx256; /* 04 */
+ u64 rx512; /* 05 */
+ u64 rx1024; /* 06 */
+ u64 rxbfcs; /* 07 */
+ u64 rxime; /* 08 */
+ u64 rxrle; /* 09 */
+ u64 rxorle; /* 10 */
+ u64 rxftl; /* 11 */
+ u64 rxjab; /* 12 */
+ u64 rxse; /* 13 */
+ u64 rxce; /* 14 */
+ u64 rxrf; /* 15 */
+ u64 rxfrag; /* 16 */
+ u64 rxuoc; /* 17 */
+ u64 rxcpf; /* 18 */
+ u64 rxsb; /* 19 */
+ u64 rxfd; /* 20 */
+ u64 rxoerr; /* 21 */
+ u64 rxaln; /* 22 */
+ u64 ducqpn; /* 23 */
+ u64 reserved0; /* 24 */
+ u64 rxmcp; /* 25 */
+ u64 rxbcp; /* 26 */
+ u64 txmcp; /* 27 */
+ u64 txbcp; /* 28 */
+ u64 txo; /* 29 */
+ u64 tx64; /* 30 */
+ u64 tx65; /* 31 */
+ u64 tx128; /* 32 */
+ u64 tx256; /* 33 */
+ u64 tx512; /* 34 */
+ u64 tx1024; /* 35 */
+ u64 txbfcs; /* 36 */
+ u64 txcpf; /* 37 */
+ u64 txlf; /* 38 */
+ u64 txrf; /* 39 */
+ u64 txime; /* 40 */
+ u64 txsc; /* 41 */
+ u64 txmc; /* 42 */
+ u64 txsqe; /* 43 */
+ u64 txdef; /* 44 */
+ u64 txlcol; /* 45 */
+ u64 txexcol; /* 46 */
+ u64 txcse; /* 47 */
+ u64 txbor; /* 48 */
+};
+
+struct hcp_query_ehea_port_cb_7 {
+ u64 def_uc_qpn;
+};
+
+u64 ehea_h_query_ehea_qp(const u64 hcp_adapter_handle,
+ const u8 qp_category,
+ const u64 qp_handle, const u64 sel_mask,
+ void *cb_addr);
+
+u64 ehea_h_modify_ehea_qp(const u64 hcp_adapter_handle,
+ const u8 cat,
+ const u64 qp_handle,
+ const u64 sel_mask,
+ void *cb_addr,
+ u64 * inv_attr_id,
+ u64 * proc_mask, u16 * out_swr, u16 * out_rwr);
+
+u64 ehea_h_alloc_resource_eq(const u64 hcp_adapter_handle,
+ struct ehea_eq *ehea_eq,
+ struct ehea_eq_attr *eq_attr, u64 * eq_handle);
+
+u64 ehea_h_alloc_resource_cq(const u64 hcp_adapter_handle,
+ struct ehea_cq *ehea_cq,
+ struct ehea_cq_attr *cq_attr,
+ u64 * cq_handle, struct h_galpas *galpas);
+
+u64 ehea_h_alloc_resource_qp(const u64 adapter_handle,
+ struct ehea_qp *ehea_qp,
+ struct ehea_qp_init_attr *init_attr,
+ const u32 pd,
+ u64 * qp_handle, struct h_galpas *h_galpas);
+
+#define H_REG_RPAGE_PAGE_SIZE EHEA_BMASK_IBM(48,55)
+#define H_REG_RPAGE_QT EHEA_BMASK_IBM(62,63)
+
+u64 ehea_h_register_rpage(const u64 hcp_adapter_handle,
+ const u8 pagesize,
+ const u8 queue_type,
+ const u64 resource_handle,
+ const u64 log_pageaddr, u64 count);
+
+u64 ehea_h_register_rpage_eq(const u64 hcp_adapter_handle,
+ const u64 eq_handle,
+ const u8 pagesize,
+ const u8 queue_type,
+ const u64 log_pageaddr, const u64 count);
+
+u64 ehea_h_register_rpage_cq(const u64 hcp_adapter_handle,
+ const u64 cq_handle,
+ const u8 pagesize,
+ const u8 queue_type,
+ const u64 log_pageaddr,
+ const u64 count, const struct h_galpa gal);
+
+u64 ehea_h_register_rpage_qp(const u64 hcp_adapter_handle,
+ const u64 qp_handle,
+ const u8 pagesize,
+ const u8 queue_type,
+ const u64 log_pageaddr,
+ const u64 count, struct h_galpa galpa);
+
+#define H_DISABLE_GET_EHEA_WQE_P 1
+#define H_DISABLE_GET_SQ_WQE_P 2
+#define H_DISABLE_GET_RQC 3
+
+u64 ehea_h_destroy_qp(const u64 hcp_adapter_handle,
+ struct ehea_qp *qp,
+ u64 qp_handle, struct h_galpas *galpas);
+
+u64 ehea_h_destroy_cq(const u64 hcp_adapter_handle,
+ struct ehea_cq *cq,
+ u64 cq_handle, struct h_galpas *galpas);
+
+u64 ehea_h_destroy_eq(const u64 hcp_adapter_handle,
+ struct ehea_eq *eq,
+ u64 eq_handle, struct h_galpas *galpas);
+
+u64 ehea_h_free_resource_mr(const u64 hcp_adapter_handle,
+ const u64 mr_handle);
+
+
+u64 ehea_h_alloc_resource_mr(const u64 hcp_adapter_handle,
+ const u64 vaddr,
+ const u64 length,
+ const u32 access_ctrl,
+ const u32 pd, u64 * mr_handle, u32 * lkey);
+
+u64 ehea_h_register_rpage_mr(const u64 hcp_adapter_handle,
+ const u64 mr_handle,
+ const u8 pagesize,
+ const u8 queue_type,
+ const u64 log_pageaddr, const u64 count);
+
+u64 ehea_h_query_ehea(const u64 hcp_adapter_handle, void *cb_addr);
+
+/* output param R5 */
+#define H_MEHEAPORT_CAT EHEA_BMASK_IBM(40,47)
+#define H_MEHEAPORT_PN EHEA_BMASK_IBM(48,63)
+
+u64 ehea_h_query_ehea_port(const u64 hcp_adapter_handle,
+ const u16 port_num,
+ const u8 cb_cat,
+ const u64 select_mask, void *cb_addr);
+
+u64 ehea_h_modify_ehea_port(const u64 hcp_adapter_handle,
+ const u16 port_num,
+ const u8 cb_cat,
+ const u64 select_mask, void *cb_addr);
+
+#define H_REGBCMC_PN EHEA_BMASK_IBM(48, 63)
+#define H_REGBCMC_REGTYPE EHEA_BMASK_IBM(61, 63)
+#define H_REGBCMC_MACADDR EHEA_BMASK_IBM(16, 63)
+#define H_REGBCMC_VLANID EHEA_BMASK_IBM(52, 63)
+
+u64 ehea_h_reg_dereg_bcmc(const u64 hcp_adapter_handle,
+ const u16 port_num,
+ const u8 reg_type,
+ const u64 mc_mac_addr,
+ const u16 vlan_id, const u32 hcall_id);
+
+u64 ehea_h_reset_events(const u64 hcp_adapter_handle,
+ const u64 neq_handle, const u64 event_mask);
+
+#endif /* __EHEA_PHYP_H__ */
--- linux-2.6.17.1-orig/drivers/net/ehea/ehea_hcall.h 1969-12-31 16:00:00.000000000 -0800
+++ kernel/drivers/net/ehea/ehea_hcall.h 2006-06-21 04:44:50.158485520 -0700
@@ -0,0 +1,52 @@
+/*
+ * linux/drivers/net/ehea/ehea_hcall.h
+ *
+ * eHEA ethernet device driver for IBM eServer System p
+ *
+ * (C) Copyright IBM Corp. 2006
+ *
+ * Authors:
+ * Christoph Raisch <raisch@de.ibm.com>
+ * Jan-Bernd Themann <themann@de.ibm.com>
+ * Heiko-Joerg Schick <schickhj@de.ibm.com>
+ * Thomas Klein <tklein@de.ibm.com>
+ *
+ *
+ * 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.
+ */
+
+#ifndef __EHEA_HCALL__
+#define __EHEA_HCALL__
+
+/**
+ * This file contains HCALL defines that are to be included in the appropriate
+ * kernel files later
+ */
+
+#define H_ALLOC_HEA_RESOURCE 0x278
+#define H_MODIFY_HEA_QP 0x250
+#define H_QUERY_HEA_QP 0x254
+#define H_QUERY_HEA 0x258
+#define H_QUERY_HEA_PORT 0x25C
+#define H_MODIFY_HEA_PORT 0x260
+#define H_REG_BCMC 0x264
+#define H_DEREG_BCMC 0x268
+#define H_REGISTER_HEA_RPAGES 0x26C
+#define H_DISABLE_AND_GET_HEA 0x270
+#define H_GET_HEA_INFO 0x274
+#define H_ADD_CONN 0x284
+#define H_DEL_CONN 0x288
+
+#endif /* __EHEA_HCALL__ */
^ permalink raw reply
* [PATCH 1/6] ehea: interface to network stack
From: Jan-Bernd Themann @ 2006-06-21 12:40 UTC (permalink / raw)
To: netdev; +Cc: meder, raisch, schickhj, themann
Signed-off-by: Jan-Bernd Themann <themann@de.ibm.com>
drivers/net/ehea/ehea_main.c | 2557 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 2557 insertions(+)
--- linux-2.6.17.1-orig/drivers/net/ehea/ehea_main.c 1969-12-31 16:00:00.000000000 -0800
+++ kernel/drivers/net/ehea/ehea_main.c 2006-06-21 04:44:50.766393104 -0700
@@ -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 <raisch@de.ibm.com>
+ * Jan-Bernd Themann <themann@de.ibm.com>
+ * Heiko-Joerg Schick <schickhj@de.ibm.com>
+ * Thomas Klein <tklein@de.ibm.com>
+ *
+ *
+ * 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 <raisch@de.ibm.com>");
+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);
^ permalink raw reply
* [PATCH 4/6] ehea: main header files
From: Jan-Bernd Themann @ 2006-06-21 12:41 UTC (permalink / raw)
To: netdev; +Cc: meder, raisch, schickhj, themann
Signed-off-by: Jan-Bernd Themann <themann@de.ibm.com>
drivers/net/ehea/ehea.h | 434 +++++++++++++++++++++++++++++++++++++++++++++
drivers/net/ehea/ehea_hw.h | 319 +++++++++++++++++++++++++++++++++
2 files changed, 753 insertions(+)
--- linux-2.6.17.1-orig/drivers/net/ehea/ehea.h 1969-12-31 16:00:00.000000000 -0800
+++ kernel/drivers/net/ehea/ehea.h 2006-06-21 04:44:50.917370152 -0700
@@ -0,0 +1,434 @@
+/*
+ * linux/drivers/net/ehea/ehea.h
+ *
+ * eHEA ethernet device driver for IBM eServer System p
+ *
+ * (C) Copyright IBM Corp. 2006
+ *
+ * Authors:
+ * Christoph Raisch <raisch@de.ibm.com>
+ * Jan-Bernd Themann <themann@de.ibm.com>
+ * Heiko-Joerg Schick <schickhj@de.ibm.com>
+ * Thomas Klein <tklein@de.ibm.com>
+ *
+ *
+ * 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.
+ */
+
+#ifndef __EHEA_H__
+#define __EHEA_H__
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/err.h>
+#include <linux/list.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/kthread.h>
+#include <linux/ethtool.h>
+#include <linux/if_vlan.h>
+#include <asm/ibmebus.h>
+#include <asm/of_device.h>
+#include <asm/abs_addr.h>
+#include <asm/semaphore.h>
+#include <asm/current.h>
+#include <asm/io.h>
+
+
+#define EHEA_DRIVER_NAME "IBM eHEA"
+#define EHEA_DRIVER_VERSION "EHEA_0007"
+
+/* #define EHEA_CQE_TIMER */
+ #define EHEA_ADDITIONAL_STATISTICS
+
+#ifdef EHEA_SMALL_QUEUES
+#define EHEA_MAX_CQE_COUNT 1020
+#define EHEA_MAX_ENTRIES_SQ 1020
+#define EHEA_MAX_ENTRIES_RQ1 4080
+#define EHEA_MAX_ENTRIES_RQ2 1020
+#define EHEA_MAX_ENTRIES_RQ3 1020
+#define EHEA_SWQE_REFILL_TH 100
+#else
+#define EHEA_MAX_CQE_COUNT 32000
+#define EHEA_MAX_ENTRIES_SQ 16000
+#define EHEA_MAX_ENTRIES_RQ1 32080
+#define EHEA_MAX_ENTRIES_RQ2 4020
+#define EHEA_MAX_ENTRIES_RQ3 4020
+#define EHEA_SWQE_REFILL_TH 1000
+#endif
+
+#define EHEA_MAX_ENTRIES_EQ 20
+
+#define EHEA_SG_SQ 2
+#define EHEA_SG_RQ1 1
+#define EHEA_SG_RQ2 0
+#define EHEA_SG_RQ3 0
+
+#define EHEA_MAX_PACKET_SIZE 9022 /* for jumbo frame */
+#define EHEA_LL_PKT_SIZE 256
+
+/* Send completion signaling */
+#define EHEA_SIG_IV 1000
+#define EHEA_SIG_IV_LONG 4
+#define EHEA_SQ_HL_TRIGGER 5000
+
+/* Protection Domain Identifier */
+#define EHEA_PD_ID 0xaabcdeff
+
+#define EHEA_RQ2_THRESHOLD 1
+#define EHEA_RQ3_THRESHOLD 5
+
+#define EHEA_SPEED_10G 10000
+#define EHEA_SPEED_1G 1000
+#define EHEA_SPEED_100M 100
+#define EHEA_SPEED_10M 10
+
+/* Broadcast/Multicast registration types */
+#define EHEA_BCMC_SCOPE_ALL 0x08
+#define EHEA_BCMC_SCOPE_SINGLE 0x00
+#define EHEA_BCMC_MULTICAST 0x04
+#define EHEA_BCMC_BROADCAST 0x00
+#define EHEA_BCMC_UNTAGGED 0x02
+#define EHEA_BCMC_TAGGED 0x00
+#define EHEA_BCMC_VLANID_ALL 0x01
+#define EHEA_BCMC_VLANID_SINGLE 0x00
+
+/* Use this define to kmallocate PHYP control blocks */
+#define H_CB_ALIGNMENT 4096
+
+#define EHEA_PAGESHIFT 12
+#define EHEA_PAGESIZE 4096UL
+#define EHEA_PT_ENTRIES 512UL
+#define EHEA_CACHE_LINE 128
+
+#define EHEA_ENABLE 1
+#define EHEA_DISABLE 0
+
+void ehea_set_ethtool_ops(struct net_device *netdev);
+
+#ifndef KEEP_EDEBS_BELOW
+#define KEEP_EDEBS_BELOW 8
+#endif
+
+extern int ehea_trace_level;
+extern struct ehea_adapter *adapter_global;
+
+#ifdef EHEA_NO_EDEB
+#define EDEB_P_GENERIC(level, idstring, format, args...) \
+ while (0 == 1) { \
+ if(unlikely (level <= ehea_trace_level)) { \
+ printk("%s " idstring " "format "\n", \
+ __func__, ##args); \
+ } \
+ } \
+
+#else
+
+#define EDEB_P_GENERIC(level,idstring,format,args...) \
+if (level < KEEP_EDEBS_BELOW) { \
+ do { \
+ if(unlikely (level <= ehea_trace_level)) { \
+ printk("%s " idstring " "format "\n", \
+ __func__, ##args); \
+ } \
+ } while (1 == 0); \
+}
+#endif
+
+#define EDEB(level, format, args...) \
+ EDEB_P_GENERIC(level, "", format, ##args)
+
+#define EDEB_ERR(level, format, args...) \
+ EDEB_P_GENERIC(level, "EHEA_ERROR", format, ##args)
+
+#define EDEB_EN(level, format, args...) \
+ EDEB_P_GENERIC(level, ">>>", format, ##args)
+
+#define EDEB_EX(level, format, args...) \
+ EDEB_P_GENERIC(level, "<<<", format, ##args)
+
+#define EHEA_BMASK(pos, length) (((pos) << 16) + (length))
+#define EHEA_BMASK_IBM(from, to) (((63 - to) << 16) + ((to) - (from) + 1))
+#define EHEA_BMASK_SHIFTPOS(mask) (((mask) >> 16) & 0xffff)
+#define EHEA_BMASK_MASK(mask) (0xffffffffffffffffULL >> ((64 - (mask)) & 0xffff))
+#define EHEA_BMASK_SET(mask, value) \
+ ((EHEA_BMASK_MASK(mask) & ((u64)(value))) << EHEA_BMASK_SHIFTPOS(mask))
+#define EHEA_BMASK_GET(mask, value) \
+ (EHEA_BMASK_MASK(mask) & (((u64)(value)) >> EHEA_BMASK_SHIFTPOS(mask)))
+
+extern void exit(int);
+
+#define EDEB_DMP(level, adr, len, format, args...) \
+if (level < KEEP_EDEBS_BELOW) { \
+ if(unlikely (level <= ehea_trace_level)) { \
+ do { \
+ unsigned int x; \
+ unsigned int l = (unsigned int)(len); \
+ unsigned char *deb = (unsigned char*)(adr); \
+ for (x = 0; x < l; x += 16) { \
+ EDEB(level, format " adr=%p ofs=%04x %016lx %016lx", \
+ ##args, deb, x, *((u64 *)&deb[0]), \
+ *((u64 *)&deb[8])); \
+ deb += 16; \
+ } \
+ } while (0); \
+ } \
+}
+
+
+/*
+ * struct generic ehea page
+ */
+struct ipz_page {
+ u8 entries[PAGE_SIZE];
+};
+
+/*
+ * struct generic queue in linux kernel virtual memory
+ */
+struct ipz_queue {
+ u64 current_q_offset; /* current queue entry */
+ struct ipz_page **queue_pages; /* array of pages belonging to queue */
+ u32 qe_size; /* queue entry size */
+ u32 act_nr_of_sg;
+ u32 queue_length; /* queue length allocated in bytes */
+ u32 pagesize;
+ u32 toggle_state; /* toggle flag - per page */
+ u32 reserved; /* 64 bit alignment */
+};
+
+
+/*
+ * h_galpa:
+ * for pSeries this is a 64bit memory address where
+ * I/O memory is mapped into CPU address space
+ */
+
+struct h_galpa {
+ u64 fw_handle;
+};
+
+struct h_galpas {
+ struct h_galpa kernel; /* kernel space accessible resource,
+ set to 0 if unused */
+ struct h_galpa user; /* user space accessible resource
+ set to 0 if unused */
+ u32 pid; /* PID of userspace galpa checking */
+};
+
+struct ehea_qp;
+struct ehea_cq;
+struct ehea_eq;
+struct ehea_port;
+struct ehea_av;
+
+struct ehea_qp_init_attr {
+ /* input parameter */
+ u32 qp_token;
+ u8 low_lat_rq1;
+ u8 signalingtype;
+ u8 rq_count;
+ u8 eqe_gen;
+ u16 max_nr_send_wqes;
+ u16 max_nr_rwqes_rq1;
+ u16 max_nr_rwqes_rq2;
+ u16 max_nr_rwqes_rq3;
+ u8 wqe_size_enc_sq;
+ u8 wqe_size_enc_rq1;
+ u8 wqe_size_enc_rq2;
+ u8 wqe_size_enc_rq3;
+ u8 swqe_imm_data_len;
+ u16 port_nr;
+ u16 rq2_threshold;
+ u16 rq3_threshold;
+ u64 send_cq_handle;
+ u64 recv_cq_handle;
+ u64 aff_eq_handle;
+
+ /* output parameter */
+ u32 qp_nr;
+ u16 act_nr_send_wqes;
+ u16 act_nr_rwqes_rq1;
+ u16 act_nr_rwqes_rq2;
+ u16 act_nr_rwqes_rq3;
+ u8 act_wqe_size_enc_sq;
+ u8 act_wqe_size_enc_rq1;
+ u8 act_wqe_size_enc_rq2;
+ u8 act_wqe_size_enc_rq3;
+ u32 nr_sq_pages;
+ u32 nr_rq1_pages;
+ u32 nr_rq2_pages;
+ u32 nr_rq3_pages;
+ u32 liobn_sq;
+ u32 liobn_rq1;
+ u32 liobn_rq2;
+ u32 liobn_rq3;
+};
+
+struct ehea_eq_attr {
+ u32 type;
+ u32 max_nr_of_eqes;
+ u8 eqe_gen;
+ u64 eq_handle;
+ u32 act_nr_of_eqes;
+ u32 nr_pages;
+ u32 ist1;
+ u32 ist2;
+ u32 ist3;
+ u32 ist4;
+};
+
+struct ehea_eq {
+ struct ipz_queue ipz_queue;
+ u64 ipz_eq_handle;
+ struct h_galpas galpas;
+ spinlock_t spinlock;
+ struct ehea_eq_attr attr;
+};
+
+struct ehea_qp {
+ struct ehea_adapter *adapter;
+ u64 ipz_qp_handle; /* QP handle for h-calls */
+ struct ipz_queue ipz_squeue;
+ struct ipz_queue ipz_rqueue1;
+ struct ipz_queue ipz_rqueue2;
+ struct ipz_queue ipz_rqueue3;
+ struct h_galpas galpas;
+ struct ehea_qp_init_attr init_attr;
+};
+
+struct ehea_cq_attr {
+ /* input parameter */
+ u32 max_nr_of_cqes;
+ u32 cq_token;
+ u64 eq_handle;
+
+ /* output parameter */
+ u32 act_nr_of_cqes;
+ u32 nr_pages;
+};
+
+struct ehea_cq {
+ struct ehea_adapter *adapter;
+ u64 ipz_cq_handle;
+ struct ipz_queue ipz_queue;
+ struct h_galpas galpas;
+ struct ehea_cq_attr attr;
+};
+
+struct ehea_port_res {
+ spinlock_t xmit_lock;
+ struct ehea_port *port;
+ struct ehea_qp *qp;
+ struct ehea_cq *send_cq;
+ struct ehea_cq *recv_cq;
+ struct ehea_eq *send_eq;
+ struct ehea_eq *recv_eq;
+ struct ehea_eq *qp_eq;
+ spinlock_t send_lock;
+ struct sk_buff **skb_arr_rq1;
+ struct sk_buff **skb_arr_rq2;
+ struct sk_buff **skb_arr_rq3;
+ struct sk_buff **skb_arr_sq;
+ int skb_arr_rq1_len;
+ int skb_arr_rq2_len;
+ int skb_arr_rq3_len;
+ int skb_arr_sq_len;
+ int skb_rq2_index;
+ int skb_rq3_index;
+ int skb_sq_index;
+ spinlock_t netif_queue;
+ atomic_t swqe_avail;
+ int swqe_ll_count;
+ int swqe_count;
+ struct tasklet_struct send_comp_task;
+ spinlock_t recv_lock;
+ u32 swqe_id_counter;
+ struct timer_list timer; /* polling mode, no interrupts */
+ struct timer_list skb_timer; /* skb cleanup timer */
+};
+
+
+struct ehea_adapter {
+
+ u64 handle;
+ u8 num_ports;
+ struct ehea_port *port[16];
+ struct ehea_eq *neq;
+ struct tasklet_struct neq_tasklet;
+ u32 lkey;
+ u32 pd;
+ u64 mr_handle;
+ u64 max_mc_mac;
+
+};
+
+inline static u32 ehea_get_lkey(struct ehea_adapter *adapter, void *addr)
+{
+ EDEB(8, "ehea_adapter=%p, addr=%p", adapter, addr);
+ EDEB(8, "Lkey returned: %X", adapter->lkey);
+ return adapter->lkey;
+}
+
+struct ehea_mc_list {
+ struct list_head list;
+ u64 macaddr;
+};
+
+struct port_state {
+ int poll_max_processed;
+ int poll_receive_errors;
+ int ehea_poll;
+ int queue_stopped;
+ int min_swqe_avail;
+ u64 sqc_stop_sum;
+ int pkt_send;
+ int pkt_xmit;
+ int send_tasklet;
+ int nwqe;
+};
+
+struct ehea_port {
+ struct ehea_adapter *adapter; /* adapter that owns this port */
+ struct net_device *netdev;
+ struct net_device_stats stats;
+ struct port_state p_state;
+ struct ehea_port_res port_res[1];
+ struct device_node *of_dev_node; /* Open Firmware Device Node */
+ struct ehea_mc_list *mc_list; /* Multicast MAC addresses */
+ struct vlan_group * vgrp;
+ int allmulti; /* Indicates IFF_ALLMULTI state */
+ int promisc; /* Indicates IFF_PROMISC state */
+
+ char int_recv_name[20];
+ char int_send_name[20];
+ char int_aff_name[20];
+
+ int kernel_l_key;
+ u64 mac_addr;
+ u32 logical_port_id;
+ u32 port_speed;
+ u8 full_duplex;
+ u8 num_def_qps;
+};
+
+
+#endif /* __EHEA_H__ */
--- linux-2.6.17.1-orig/drivers/net/ehea/ehea_hw.h 1969-12-31 16:00:00.000000000 -0800
+++ kernel/drivers/net/ehea/ehea_hw.h 2006-06-21 04:44:50.135489016 -0700
@@ -0,0 +1,319 @@
+/*
+ * linux/drivers/net/ehea/ehea_hw.h
+ *
+ * eHEA ethernet device driver for IBM eServer System p
+ *
+ * (C) Copyright IBM Corp. 2006
+ *
+ * Authors:
+ * Christoph Raisch <raisch@de.ibm.com>
+ * Jan-Bernd Themann <themann@de.ibm.com>
+ * Heiko-Joerg Schick <schickhj@de.ibm.com>
+ * Thomas Klein <tklein@de.ibm.com>
+ *
+ *
+ * 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.
+ */
+
+#ifndef __EHEA_HW_H__
+#define __EHEA_HW_H__
+
+#define QPX_SQA_VALUE EHEA_BMASK_IBM(48,63)
+#define QPX_RQ1A_VALUE EHEA_BMASK_IBM(48,63)
+#define QPX_RQ2A_VALUE EHEA_BMASK_IBM(48,63)
+#define QPX_RQ3A_VALUE EHEA_BMASK_IBM(48,63)
+
+#define QPTEMM_OFFSET(x) offsetof(struct ehea_qptemm, x)
+
+struct ehea_qptemm {
+ u64 qpx_hcr;
+ u64 qpx_c;
+ u64 qpx_herr;
+ u64 qpx_aer;
+ u64 qpx_sqa;
+ u64 qpx_sqc;
+ u64 qpx_rq1a;
+ u64 qpx_rq1c;
+ u64 qpx_st;
+ u64 qpx_aerr;
+ u64 qpx_tenure;
+ u64 qpx_reserved1[(0x098 - 0x058) / 8];
+ u64 qpx_portp;
+ u64 qpx_reserved2[(0x100 - 0x0A0) / 8];
+ u64 qpx_t;
+ u64 qpx_sqhp;
+ u64 qpx_sqptp;
+ u64 qpx_reserved3[(0x140 - 0x118) / 8];
+ u64 qpx_sqwsize;
+ u64 qpx_reserved4[(0x170 - 0x148) / 8];
+ u64 qpx_sqsize;
+ u64 qpx_reserved5[(0x1B0 - 0x178) / 8];
+ u64 qpx_sigt;
+ u64 qpx_wqecnt;
+ u64 qpx_rq1hp;
+ u64 qpx_rq1ptp;
+ u64 qpx_rq1size;
+ u64 qpx_reserved6[(0x220 - 0x1D8) / 8];
+ u64 qpx_rq1wsize;
+ u64 qpx_reserved7[(0x240 - 0x228) / 8];
+ u64 qpx_pd;
+ u64 qpx_scqn;
+ u64 qpx_rcqn;
+ u64 qpx_aeqn;
+ u64 reserved49;
+ u64 qpx_ram;
+ u64 qpx_reserved8[(0x300 - 0x270) / 8];
+ u64 qpx_rq2a;
+ u64 qpx_rq2c;
+ u64 qpx_rq2hp;
+ u64 qpx_rq2ptp;
+ u64 qpx_rq2size;
+ u64 qpx_rq2wsize;
+ u64 qpx_rq2th;
+ u64 qpx_rq3a;
+ u64 qpx_rq3c;
+ u64 qpx_rq3hp;
+ u64 qpx_rq3ptp;
+ u64 qpx_rq3size;
+ u64 qpx_rq3wsize;
+ u64 qpx_rq3th;
+ u64 qpx_lpn;
+ u64 qpx_reserved9[(0x400 - 0x378) / 8];
+ u64 reserved_ext[(0x500 - 0x400) / 8];
+ u64 reserved2[(0x1000 - 0x500) / 8];
+};
+
+#define MRx_HCR_LPARID_VALID EHEA_BMASK_IBM(0, 0)
+
+#define MRMWMM_OFFSET(x) offsetof(struct ehea_mrmwmm, x)
+
+struct ehea_mrmwmm {
+ u64 mrx_hcr;
+ u64 mrx_c;
+ u64 mrx_herr;
+ u64 mrx_aer;
+ u64 mrx_pp;
+ u64 reserved1;
+ u64 reserved2;
+ u64 reserved3;
+ u64 reserved4[(0x200 - 0x40) / 8];
+ u64 mrx_ctl[64];
+};
+
+#define QPEDMM_OFFSET(x) offsetof(struct ehea_qpedmm, x)
+
+struct ehea_qpedmm {
+
+ u64 reserved0[(0x400) / 8];
+ u64 qpedx_phh;
+ u64 qpedx_ppsgp;
+ u64 qpedx_ppsgu;
+ u64 qpedx_ppdgp;
+ u64 qpedx_ppdgu;
+ u64 qpedx_aph;
+ u64 qpedx_apsgp;
+ u64 qpedx_apsgu;
+ u64 qpedx_apdgp;
+ u64 qpedx_apdgu;
+ u64 qpedx_apav;
+ u64 qpedx_apsav;
+ u64 qpedx_hcr;
+ u64 reserved1[4];
+ u64 qpedx_rrl0;
+ u64 qpedx_rrrkey0;
+ u64 qpedx_rrva0;
+ u64 reserved2;
+ u64 qpedx_rrl1;
+ u64 qpedx_rrrkey1;
+ u64 qpedx_rrva1;
+ u64 reserved3;
+ u64 qpedx_rrl2;
+ u64 qpedx_rrrkey2;
+ u64 qpedx_rrva2;
+ u64 reserved4;
+ u64 qpedx_rrl3;
+ u64 qpedx_rrrkey3;
+ u64 qpedx_rrva3;
+};
+
+#define CQX_FECADDER EHEA_BMASK_IBM(32, 63)
+#define CQX_FEC_CQE_CNT EHEA_BMASK_IBM(32, 63)
+#define CQX_N1_GENERATE_COMP_EVENT EHEA_BMASK_IBM(0, 0)
+#define CQX_EP_EVENT_PENDING EHEA_BMASK_IBM(0, 0)
+
+#define CQTEMM_OFFSET(x) offsetof(struct ehea_cqtemm, x)
+
+struct ehea_cqtemm {
+ u64 cqx_hcr;
+ u64 cqx_c;
+ u64 cqx_herr;
+ u64 cqx_aer;
+ u64 cqx_ptp;
+ u64 cqx_tp;
+ u64 cqx_fec;
+ u64 cqx_feca;
+ u64 cqx_ep;
+ u64 cqx_eq;
+ u64 reserved1;
+ u64 cqx_n0;
+ u64 cqx_n1;
+ u64 reserved2[(0x1000 - 0x60) / 8];
+};
+
+#define EQTEMM_OFFSET(x) offsetof(struct ehea_eqtemm, x)
+
+struct ehea_eqtemm {
+ u64 EQx_HCR;
+ u64 EQx_C;
+ u64 EQx_HERR;
+ u64 EQx_AER;
+ u64 EQx_PTP;
+ u64 EQx_TP;
+ u64 EQx_SSBA;
+ u64 EQx_PSBA;
+ u64 EQx_CEC;
+ u64 EQx_MEQL;
+ u64 EQx_XISBI;
+ u64 EQx_XISC;
+ u64 EQx_IT;
+};
+
+static inline u64 hipz_galpa_load(struct h_galpa galpa, u32 offset)
+{
+ u64 addr = galpa.fw_handle + offset;
+ u64 out;
+ EDEB_EN(7, "addr=%lx offset=%x ", addr, offset);
+ out = *(volatile u64 *)addr;
+ EDEB_EX(7, "addr=%lx value=%lx", addr, out);
+ return out;
+};
+
+static inline void hipz_galpa_store(struct h_galpa galpa, u32 offset, u64 value)
+{
+ u64 addr = galpa.fw_handle + offset;
+ EDEB_EN(7, "addr=%lx offset=%x value=%lx", addr, offset,
+ value);
+ *(u64 *) addr = value;
+ hipz_galpa_load(galpa, offset); /* synchronize explicitly to ehea */
+ EDEB_EX(7, "");
+};
+
+static inline void hipz_galpa_store_acc(struct h_galpa galpa, u32 offset,
+ u64 value)
+{
+ u64 addr = galpa.fw_handle + offset;
+ EDEB(7, "Accelerated store: addr=%lx offset=%x value=%lx",
+ addr, offset, value);
+ *(u64 *) addr = value;
+};
+
+#define hipz_galpa_store_eq(gal, offset, value)\
+ hipz_galpa_store(gal, EQTEMM_OFFSET(offset), value)
+#define hipz_galpa_load_eq(gal, offset)\
+ hipz_galpa_load(gal, EQTEMM_OFFSET(offset))
+
+#define hipz_galpa_store_cq(gal, offset, value)\
+ hipz_galpa_store(gal, CQTEMM_OFFSET(offset), value)
+#define hipz_galpa_load_cq(gal, offset)\
+ hipz_galpa_load(gal, CQTEMM_OFFSET(offset))
+
+#define hipz_galpa_store_qp(gal, offset, value)\
+ hipz_galpa_store(gal, QPTEMM_OFFSET(offset), value)
+#define hipz_galpa_load_qp(gal, offset)\
+ hipz_galpa_load(gal, QPTEMM_OFFSET(offset))
+
+#define hipz_galpa_store_qped(gal, offset, value)\
+ hipz_galpa_store(gal, QPEDMM_OFFSET(offset), value)
+#define hipz_galpa_load_qped(gal, offset)\
+ hipz_galpa_load(gal, QPEDMM_OFFSET(offset))
+
+#define hipz_galpa_store_mrmw(gal, offset, value)\
+ hipz_galpa_store(gal, MRMWMM_OFFSET(offset), value)
+#define hipz_galpa_load_mrmw(gal, offset)\
+ hipz_galpa_load(gal, MRMWMM_OFFSET(offset))
+
+#define hipz_galpa_store_base(gal, offset, value)\
+ hipz_galpa_store(gal, HCAGR_OFFSET(offset), value)
+#define hipz_galpa_load_base(gal, offset)\
+ hipz_galpa_load(gal, HCAGR_OFFSET(offset))
+
+static inline void ehea_update_sqa(struct ehea_qp *qp, u16 nr_wqes)
+{
+ struct h_galpa gal = qp->galpas.kernel;
+ EDEB_EN(7, "qp=%p, nr_wqes=%d", qp, nr_wqes);
+
+ hipz_galpa_store_acc(gal, QPTEMM_OFFSET(qpx_sqa),
+ EHEA_BMASK_SET(QPX_SQA_VALUE, nr_wqes));
+ EDEB_EX(7, "qpx_sqa = %i", nr_wqes);
+}
+
+static inline void ehea_update_rq3a(struct ehea_qp *qp, u16 nr_wqes)
+{
+ struct h_galpa gal = qp->galpas.kernel;
+ EDEB_EN(7, "ehea_qp=%p, nr_wqes=%d", qp, nr_wqes);
+ hipz_galpa_store_acc(gal, QPTEMM_OFFSET(qpx_rq3a),
+ EHEA_BMASK_SET(QPX_RQ1A_VALUE, nr_wqes));
+ EDEB_EX(7, "QPx_RQA = %i", nr_wqes);
+}
+
+static inline void ehea_update_rq2a(struct ehea_qp *qp, u16 nr_wqes)
+{
+ struct h_galpa gal = qp->galpas.kernel;
+ EDEB_EN(7, "ehea_qp=%p, nr_wqes=%d", qp, nr_wqes);
+ hipz_galpa_store_acc(gal, QPTEMM_OFFSET(qpx_rq2a),
+ EHEA_BMASK_SET(QPX_RQ1A_VALUE, nr_wqes));
+ EDEB_EX(7, "QPx_RQA = %i", nr_wqes);
+}
+
+static inline void ehea_update_rq1a(struct ehea_qp *qp, u16 nr_wqes)
+{
+ struct h_galpa gal = qp->galpas.kernel;
+ EDEB_EN(7, "ehea_qp=%p, nr_wqes=%d", qp, nr_wqes);
+ hipz_galpa_store_acc(gal, QPTEMM_OFFSET(qpx_rq1a),
+ EHEA_BMASK_SET(QPX_RQ1A_VALUE, nr_wqes));
+ EDEB_EX(7, "QPx_RQA = %i", nr_wqes);
+}
+
+static inline void ehea_update_feca(struct ehea_cq *cq, u32 nr_cqes)
+{
+ struct h_galpa gal = cq->galpas.kernel;
+ EDEB_EN(7, "");
+ hipz_galpa_store_acc(gal, CQTEMM_OFFSET(cqx_feca),
+ EHEA_BMASK_SET(CQX_FECADDER, nr_cqes));
+ EDEB_EX(7, "cqx_feca = %i", nr_cqes);
+}
+
+static inline void ehea_reset_cq_n1(struct ehea_cq *cq)
+{
+ struct h_galpa gal = cq->galpas.kernel;
+ EDEB_EN(7, "");
+ hipz_galpa_store_cq(gal,
+ cqx_n1,
+ EHEA_BMASK_SET(CQX_N1_GENERATE_COMP_EVENT, 1));
+ EDEB_EX(7, "");
+}
+
+static inline void ehea_reset_cq_ep(struct ehea_cq *my_cq)
+{
+ struct h_galpa gal = my_cq->galpas.kernel;
+ EDEB_EN(7, "");
+ hipz_galpa_store_acc(gal,
+ CQTEMM_OFFSET(cqx_ep),
+ EHEA_BMASK_SET(CQX_EP_EVENT_PENDING, 0));
+ EDEB_EX(7, "");
+}
+
+
+#endif /* __EHEA_HW_H__ */
^ permalink raw reply
* [PATCH 5/6] ehea: eHEA Makefile
From: Jan-Bernd Themann @ 2006-06-21 12:41 UTC (permalink / raw)
To: netdev; +Cc: meder, raisch, schickhj, themann
Signed-off-by: Jan-Bernd Themann <themann@de.ibm.com>
drivers/net/ehea/Makefile | 7 +++++++
1 file changed, 7 insertions(+)
--- linux-2.6.17.1-orig/drivers/net/ehea/Makefile 1969-12-31 16:00:00.000000000 -0800
+++ kernel/drivers/net/ehea/Makefile 2006-06-21 04:44:50.132489472 -0700
@@ -0,0 +1,7 @@
+#
+# Makefile for the eHEA ethernet device driver for IBM eServer System p
+#
+
+ehea_mod-objs = ehea_main.o ehea_phyp.o ehea_qmr.o ehea_ethtool.o ehea_phyp.o
+obj-$(CONFIG_EHEA) += ehea_mod.o
+
^ permalink raw reply
* [PATCH 6/6] ehea: Makefile & Kconfig
From: Jan-Bernd Themann @ 2006-06-21 12:42 UTC (permalink / raw)
To: netdev; +Cc: meder, raisch, schickhj, themann
Signed-off-by: Jan-Bernd Themann <themann@de.ibm.com>
drivers/net/Kconfig | 7 +++++++
drivers/net/Makefile | 1 +
2 files changed, 8 insertions(+)
diff -Nurp -X dontdiff linux-2.6.17.1/drivers/net/Kconfig patched_kernel/drivers/net/Kconfig
--- linux-2.6.17.1/drivers/net/Kconfig 2006-06-20 02:31:55.000000000 -0700
+++ patched_kernel/drivers/net/Kconfig 2006-06-20 03:53:45.896352432 -0700
@@ -2247,6 +2247,13 @@ config CHELSIO_T1
To compile this driver as a module, choose M here: the module
will be called cxgb.
+config EHEA
+ tristate "eHEA Ethernet support"
+ depends on IBMEBUS
+ ---help---
+ This driver supports the eHEA IBM ethernet adapter for eServer System p
+
+
config IXGB
tristate "Intel(R) PRO/10GbE support"
depends on PCI
diff -Nurp -X dontdiff linux-2.6.17.1/drivers/net/Makefile patched_kernel/drivers/net/Makefile
--- linux-2.6.17.1/drivers/net/Makefile 2006-06-20 02:31:55.000000000 -0700
+++ patched_kernel/drivers/net/Makefile 2006-06-20 03:54:17.239380552 -0700
@@ -10,6 +10,7 @@ obj-$(CONFIG_E1000) += e1000/
obj-$(CONFIG_IBM_EMAC) += ibm_emac/
obj-$(CONFIG_IXGB) += ixgb/
obj-$(CONFIG_CHELSIO_T1) += chelsio/
+obj-$(CONFIG_EHEA) += ehea/
obj-$(CONFIG_BONDING) += bonding/
obj-$(CONFIG_GIANFAR) += gianfar_driver.o
^ permalink raw reply
* Re: [PATCH 0/2] NET: Accurate packet scheduling for ATM/ADSL
From: Patrick McHardy @ 2006-06-21 12:54 UTC (permalink / raw)
To: Krzysztof Matusik; +Cc: netdev
In-Reply-To: <200606211421.05606.kyf@arterm.pl>
Krzysztof Matusik wrote:
> Dnia wtorek, 20 czerwca 2006 17:16, Patrick McHardy napisał:
>
>>The code wouldn't be very complicated, it just adds some overhead. If
>>you do something like I described in my previous mail the overhead for
>>people not using it would be an additional pointer test before reading
>>skb->len. I guess we could also make it a compile time option.
>>I personally think this is something that really improves our quality
>>of implementation, after all, its "wire" resources qdiscs are meant
>>to manage.
>
>
> I'd love to see this one implemented. I'm using HFSC more than a year and it
> never provides proper QoS on ATM/ADSL links; low delays can never be achieved
> even with significant throttling below the h/w link bandwidth.
Mhh .. I trust you picked a proper clocksource and timer frequency?
> This would help a lot regarding the amount of adsl users but on the other
> hand- there's not many HFSC implementations in real-life I guess (users seem
> to be afraid of it's 'complexity').
> This idea doesn't seem look dirty- is there a chance to implement it in the
> kernel and iproute?
I hacked up a patch yesterday. I want to do a bit more testing before
posting it, but its hard to really test the effectiveness because even
without this patch my DSL line already delivers higher throughput and
much better delay than it should (its a throttled SDSL line sold as
ADSL). I'll try to do some testing on an ethernet link now.
^ permalink raw reply
* No interfaces under /proc/sys/net/ipv4/conf/
From: Hasso Tepper @ 2006-06-21 13:31 UTC (permalink / raw)
To: netdev
After upgrade to 2.6.16.20 from 2.6.11 I discovered that no dynamic
interfaces (vlans, tunnels) appear under /proc/sys/net/ipv4/conf/.
/proc/sys/net/ipv6/conf/ is OK.
Bug? Or feature?
with my best wishes,
--
Hasso Tepper
^ permalink raw reply
* [patch] ipv6 source address selection in addrconf.c (2.6.17)
From: Lukasz Stelmach @ 2006-06-21 13:42 UTC (permalink / raw)
To: Lukasz Stelmach; +Cc: netdev
In-Reply-To: <44986AE6.6090102@poczta.fm>
[-- Attachment #1.1: Type: text/plain, Size: 1060 bytes --]
Lukasz Stelmach wrote:
> I found it when I was trying to figure out why when trying to connect to
>
> 2001:200:0:8002:203:47ff:fea5:3085 (www.kame.net)
>
> with two global addresses assigned to the ethernet card
>
> fd24:6f44:46bd:face::254
> 2002:531f:d667:face::254
>
> rule 8 does not work and the first address is chosen.
The answer is that fc00::/7 matches 2001:: better because it gets the same
label (ipv6_saddr_label()). Although fc00::/7 addresses are defined as global
unicast IMHO they should be treated *slightly* different. This is the patch.
Since 6to4 has its own label I have decided to assign one to Teredo too.
However, I still haven't found any clue in referneces to unassigned value of
hiscore.addr_type.
--
Było mi bardzo miło. Czwarta pospolita klęska, [...]
>Łukasz< Już nie katolicka lecz złodziejska. (c)PP
----------------------------------------------------------------------
Zobacz nowosci salonu moto w Madrycie >>> http://link.interia.pl/f1961
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1.2: addrconf.diff --]
[-- Type: text/x-patch; name="addrconf.diff", Size: 575 bytes --]
--- /usr/src/linux/net/ipv6/addrconf.c~ 2006-06-21 11:41:22.000000000 +0200
+++ /usr/src/linux/net/ipv6/addrconf.c 2006-06-21 15:33:26.000000000 +0200
@@ -862,6 +862,8 @@
* 2002::/16 2
* ::/96 3
* ::ffff:0:0/96 4
+ * fc00::/7 5
+ * 2001::/32 6
*/
if (type & IPV6_ADDR_LOOPBACK)
return 0;
@@ -871,6 +873,10 @@
return 4;
else if (addr->s6_addr16[0] == htons(0x2002))
return 2;
+ else if ((addr->s6_addr[0] & 0xfe) == 0xfc)
+ return 5;
+ else if (addr->s6_addr32[0] == htonl(0x20010000))
+ return 6;
return 1;
}
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 254 bytes --]
^ permalink raw reply
* Re: ipv6 source address selection in addrconf.c (2.6.17)
From: YOSHIFUJI Hideaki / 吉藤英明 @ 2006-06-21 14:02 UTC (permalink / raw)
To: stlman; +Cc: netdev, yoshfuji
In-Reply-To: <44986AE6.6090102@poczta.fm>
In article <44986AE6.6090102@poczta.fm> (at Tue, 20 Jun 2006 23:38:46 +0200), Lukasz Stelmach <stlman@poczta.fm> says:
> Greetings.
>
> net/ipv6/addrconf.c:971 is
> /* Rule 2: Prefer appropriate scope */
> if (hiscore.rule < 2) {
> hiscore.scope = __ipv6_addr_src_scope(hiscore.addr_type);
> hiscore.rule++;
> }
>
> I am afraid, that it does not make any sense for I find no place where a value
> is assigned to hiscore.addr_type. There are some more references to
> hiscore.addr_type below but the only assignment is when the whole structure is
> cleaned with memset(3)
hiscore is copied from score.
--yoshfuji
^ permalink raw reply
* Re: [PATCH 0/2] NET: Accurate packet scheduling for ATM/ADSL
From: Krzysztof Matusik @ 2006-06-21 14:33 UTC (permalink / raw)
To: netdev
In-Reply-To: <44994192.5050807@trash.net>
Dnia środa, 21 czerwca 2006 14:54, Patrick McHardy napisał:
> > I'd love to see this one implemented. I'm using HFSC more than a year and
> > it never provides proper QoS on ATM/ADSL links; low delays can never be
> > achieved even with significant throttling below the h/w link bandwidth.
>
> Mhh .. I trust you picked a proper clocksource and timer frequency?
250Hz or 1000Hz and jiffies (for a SMP machine)
I don't mean I've got big delays though and those are lowest with HFSC, when
link is satiated.
IMHO since ATM is sending small packets constantly thorough its link (ATM
specific) encapsulated data defragmentation cannot simply be achieved (or it
simply isn't done by modems). As a result one can't simply throttle qdisc
basing on computations like
UpperLimit = ( bandwidth*(1 - header_bits/frame_size )
having bandwidth specified by ATM link speed rate, as it can be reported by
modem, and expect it won't ever be congested.
This seems to be main problem. Link asymmetry, window size etc don't affect
line congestion and so delays that much I suppose.
The same with incorrect timer :-).
> I hacked up a patch yesterday. I want to do a bit more testing before
> posting it, but its hard to really test the effectiveness because even
> without this patch my DSL line already delivers higher throughput and
> much better delay than it should (its a throttled SDSL line sold as
> ADSL). I'll try to do some testing on an ethernet link now.
I'll follow this thread then.
regards
^ permalink raw reply
* Re: Suspending 802.11 drivers
From: Luis R. Rodriguez @ 2006-06-21 15:08 UTC (permalink / raw)
To: Stefan Rompf
Cc: Michael Buesch, Jiri Benc, John W. Linville, netdev, bcm43xx-dev
In-Reply-To: <200606162036.26518.stefan@loplof.de>
On 6/16/06, Stefan Rompf <stefan@loplof.de> wrote:
> Am Donnerstag 15 Juni 2006 21:58 schrieb Michael Buesch:
>
> I think the most important question is how a suspend/resume action should be
> translated. For the managed case, it is clearly an association loss, normally
> signalled by netif_carrier_on/off() and a corresponding SIOCGIWAP event.
> Supplicants can act on this. In AP mode, suspend is equal to disassociating
> all stations. Ad-hoc... dunno.
>
> For a softmac stack like devicescape, it makes sense to have a function that
> abstracts these scenarios as it is the stack anyway that handles association
> stuff. However, I'd rather extend the existing function
> ieee80211_netif_oper().
Since d80211 is already being patched for sysfs how about we use sysfs
(and kobjects) to maintain the state at suspend() and resume(). This
would allow userspace tools like supplicant running in the background
to pick up from sysfs where it left off and for our drivers to save
where we left off. ieee80211_hw can then just refer to their suspend()
and resume() routines from its respective struct pci_driver or struct
usb_device.
Luis
^ permalink raw reply
* [-mm patch] drivers/net/ni5010.c: fix compile error
From: Adrian Bunk @ 2006-06-21 15:10 UTC (permalink / raw)
To: Andrew Morton, Andreas Mohr; +Cc: linux-kernel, jgarzik, netdev
In-Reply-To: <20060621034857.35cfe36f.akpm@osdl.org>
On Wed, Jun 21, 2006 at 03:48:57AM -0700, Andrew Morton wrote:
>...
> Changes since 2.6.17-rc6-mm2:
>...
> +ni5010-netcard-cleanup.patch
>
> netdev cleanup
>...
This patch fixes the following compile error with CONFIG_NI5010=y:
<-- snip -->
...
LD .tmp_vmlinux1
drivers/built-in.o:(.init.data+0x2b280): undefined reference to `ni5010_probe'
make: *** [.tmp_vmlinux1] Error 1
<-- snip -->
Signed-off-by: Adrian Bunk <bunk@stusta.de>
--- linux-2.6.17-mm1-full/drivers/net/ni5010.c.old 2006-06-21 16:21:26.000000000 +0200
+++ linux-2.6.17-mm1-full/drivers/net/ni5010.c 2006-06-21 16:21:46.000000000 +0200
@@ -117,7 +117,7 @@
static int io;
static int irq;
-static struct net_device * __init ni5010_probe(int unit)
+struct net_device * __init ni5010_probe(int unit)
{
struct net_device *dev = alloc_etherdev(sizeof(struct ni5010_local));
int *port;
^ permalink raw reply
* Re: [patch] ipv6 source address selection in addrconf.c (2.6.17)
From: YOSHIFUJI Hideaki / 吉藤英明 @ 2006-06-21 15:12 UTC (permalink / raw)
To: stlman; +Cc: netdev
In-Reply-To: <44994CB3.6070302@poczta.fm>
In article <44994CB3.6070302@poczta.fm> (at Wed, 21 Jun 2006 15:42:11 +0200), Lukasz Stelmach <stlman@poczta.fm> says:
> --- /usr/src/linux/net/ipv6/addrconf.c~ 2006-06-21 11:41:22.000000000 +0200
> +++ /usr/src/linux/net/ipv6/addrconf.c 2006-06-21 15:33:26.000000000 +0200
> @@ -862,6 +862,8 @@
> * 2002::/16 2
> * ::/96 3
> * ::ffff:0:0/96 4
> + * fc00::/7 5
> + * 2001::/32 6
> */
> if (type & IPV6_ADDR_LOOPBACK)
> return 0;
> @@ -871,6 +873,10 @@
> return 4;
> else if (addr->s6_addr16[0] == htons(0x2002))
> return 2;
> + else if ((addr->s6_addr[0] & 0xfe) == 0xfc)
> + return 5;
> + else if (addr->s6_addr32[0] == htonl(0x20010000))
> + return 6;
> return 1;
> }
>
Please put the comparison for 2001::/32 before 2002::/16.
Otherwise, I'm fine with it.
In addition, give us your sign-off, please.
Regards,
--
YOSHIFUJI Hideaki @ USAGI Project <yoshfuji@linux-ipv6.org>
GPG-FP : 9022 65EB 1ECF 3AD1 0BDF 80D8 4807 F894 E062 0EEA
^ permalink raw reply
* [IPV6] ADDRCONF: Fix default source address selection without CONFIG_IPV6_PRIVACY
From: YOSHIFUJI Hideaki / 吉藤英明 @ 2006-06-21 15:23 UTC (permalink / raw)
To: davem; +Cc: yoshfuji, netdev
We need to update hiscore.rule even if we don't enable CONFIG_IPV6_PRIVACY,
because we have more less significant rule; longest match.
I think it is suitable for -stable as well.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
---
net/ipv6/addrconf.c | 3 +++
1 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -1069,6 +1069,9 @@ #ifdef CONFIG_IPV6_PRIVACY
if (hiscore.attrs & IPV6_SADDR_SCORE_PRIVACY)
continue;
}
+#else
+ if (hiscore.rule < 7)
+ hiscore.rule++;
#endif
/* Rule 8: Use longest matching prefix */
if (hiscore.rule < 8) {
--
YOSHIFUJI Hideaki @ USAGI Project <yoshfuji@linux-ipv6.org>
GPG-FP : 9022 65EB 1ECF 3AD1 0BDF 80D8 4807 F894 E062 0EEA
^ permalink raw reply
* Re: [PATCH 2/6] ehea: pHYP interface
From: Roland Dreier @ 2006-06-21 15:36 UTC (permalink / raw)
To: Jan-Bernd Themann; +Cc: netdev, meder, raisch, schickhj, themann
In-Reply-To: <44993E48.8060600@de.ibm.com>
I think this code needs to be refactored so that it can share with the
ehca InfiniBand driver (which should be merged upstream soon). For
example, you have ehea_hcall_7arg_7ret() and the ehca driver has an
identical ehca_hcall_7arg_7ret().
Also:
> +++ kernel/drivers/net/ehea/ehea_hcall.h 2006-06-21 04:44:50.158485520 -0700
> +/**
> + * This file contains HCALL defines that are to be included in the appropriate
> + * kernel files later
> + */
this stuff should go in <asm-powerpc/hvcall.h> with the other
constants, right?
^ permalink raw reply
* Re: [patch] ipv6 source address selection in addrconf.c (2.6.17)
From: Lukasz Stelmach @ 2006-06-21 16:05 UTC (permalink / raw)
To: YOSHIFUJI Hideaki / 吉藤英明
Cc: netdev, Łukasz Stelmach
In-Reply-To: <20060622.001235.96410192.yoshfuji@linux-ipv6.org>
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
- --- linux/net/ipv6/addrconf.c.orig 2006-06-21 11:41:22.000000000 +0200
+++ linux/net/ipv6/addrconf.c 2006-06-21 17:18:56.000000000 +0200
@@ -862,6 +862,8 @@ static int inline ipv6_saddr_label(const
* 2002::/16 2
* ::/96 3
* ::ffff:0:0/96 4
+ * fc00::/7 5
+ * 2001::/32 6
*/
if (type & IPV6_ADDR_LOOPBACK)
return 0;
@@ -869,8 +871,12 @@ static int inline ipv6_saddr_label(const
return 3;
else if (type & IPV6_ADDR_MAPPED)
return 4;
+ else if (addr->s6_addr32[0] == htonl(0x20010000))
+ return 6;
else if (addr->s6_addr16[0] == htons(0x2002))
return 2;
+ else if ((addr->s6_addr[0] & 0xfe) == 0xfc)
+ return 5;
return 1;
}
Two additional labels (RFC 3484, sec. 10.3) for IPv6 addreses
are defined to make a distinction between global unicast
addresses and Unique Local Addresses (fc00::/7, RFC 4193) and
Teredo (2001::/32, RFC 4380). It is necessary to avoid attempts
of connection that would either fail (eg. fec0:: to 2001:feed::)
or be sub-optimal (2001:0:: to 2001:feed::).
Signed-off-by: Łukasz Stelmach <stlman@poczta.fm>
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.2.2 (GNU/Linux)
iD4DBQFEmW0LNdzY8sm9K9wRAjVGAKCOS1MVIro3bJ6szHzuAzaXXNaq/gCY4jfO
timfJc6SmrygMer36Tdqzg==
=Qzch
-----END PGP SIGNATURE-----
--
Było mi bardzo miło. Czwarta pospolita klęska, [...]
>Łukasz< Już nie katolicka lecz złodziejska. (c)PP
----------------------------------------------------------------------
Zobacz nowosci salonu moto w Madrycie >>> http://link.interia.pl/f1961
^ permalink raw reply
* [PATCH 1/3] PAL: Support of the fixed PHY
From: Vitaly Bordug @ 2006-06-21 16:09 UTC (permalink / raw)
To: Jeff Garzik; +Cc: netdev, linux-kernel, linuxppc-embedded
This makes it possible for HW PHY-less boards to utilize PAL goodies.
Generic routines to connect to fixed PHY are provided, as well as ability
to specify software callback that fills up link, speed, etc. information
into PHY descriptor (the latter feature not tested so far).
Signed-off-by: Vitaly Bordug <vbordug@ru.mvista.com>
---
drivers/net/phy/Kconfig | 17 ++
drivers/net/phy/fixed.c | 385 ++++++++++++++++++++++++++++++++++++++++++
drivers/net/phy/phy_device.c | 51 +++---
include/linux/phy.h | 1
4 files changed, 433 insertions(+), 21 deletions(-)
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index cda3e53..425be84 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -51,5 +51,22 @@ config SMSC_PHY
---help---
Currently supports the LAN83C185 PHY
+config FIXED_PHY
+ tristate "Drivers for PHY emulation on fixed speed/link"
+ depends on PHYLIB
+ ---help---
+ Adds the driver to PHY layer to cover the boards that do not have any PHY bound,
+ but with the ability to manipulate with speed/link in software. The relavant MII
+ speed/duplex parameters could be effectively handled in user-specified fuction.
+ Currently tested with mpc866ads.
+
+config FIXED_MII_10_FDX
+ bool "Emulation for 10M Fdx fixed PHY behavior"
+ depends on FIXED_PHY
+
+config FIXED_MII_100_FDX
+ bool "Emulation for 100M Fdx fixed PHY behavior"
+ depends on FIXED_PHY
+
endmenu
diff --git a/drivers/net/phy/fixed.c b/drivers/net/phy/fixed.c
new file mode 100644
index 0000000..0360f65
--- /dev/null
+++ b/drivers/net/phy/fixed.c
@@ -0,0 +1,385 @@
+/*
+ * drivers/net/phy/fixed.c
+ *
+ * Driver for fixed PHYs, when transceiver is able to operate in one fixed mode.
+ *
+ * Author: Vitaly Bordug
+ *
+ * Copyright (c) 2006 MontaVista Software, Inc.
+ *
+ * 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 of the License, or (at your
+ * option) any later version.
+ *
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+#define MII_REGS_NUM 7
+
+/*
+ The idea is to emulate normal phy behavior by responding with
+ pre-defined values to mii BMCR read, so that read_status hook could
+ take all the needed info.
+*/
+
+struct fixed_phy_status {
+ u8 link;
+ u16 speed;
+ u8 duplex;
+};
+
+/*-----------------------------------------------------------------------------
+ * Private information hoder for mii_bus
+ *-----------------------------------------------------------------------------*/
+struct fixed_info {
+ u16 *regs;
+ u8 regs_num;
+ struct fixed_phy_status phy_status;
+ struct phy_device *phydev; /* pointer to the container */
+ /* link & speed cb */
+ int(*link_update)(struct net_device*, struct fixed_phy_status*);
+
+};
+
+/*
+ This is made global to free all the allocations on _exit call.
+ Looks a bit odd, seems the only way.
+*/
+static struct fixed_info *fixed_ptr;
+
+/*-----------------------------------------------------------------------------
+ * If something weird is required to be done with link/speed,
+ * network driver is able to assign a function to implement this.
+ * May be useful for PHY's that need to be software-driven.
+ *-----------------------------------------------------------------------------*/
+int fixed_mdio_set_link_update(struct phy_device* phydev,
+ int(*link_update)(struct net_device*, struct fixed_phy_status*))
+{
+ struct fixed_info *fixed;
+
+ if(link_update == NULL)
+ return -EINVAL;
+
+ if(phydev) {
+ if(phydev->bus) {
+ fixed = phydev->bus->priv;
+ fixed->link_update = link_update;
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+EXPORT_SYMBOL(fixed_mdio_set_link_update);
+
+/*-----------------------------------------------------------------------------
+ * This is used for updating internal mii regs from the status
+ *-----------------------------------------------------------------------------*/
+static int fixed_mdio_update_regs(struct fixed_info *fixed)
+{
+ u16 *regs = fixed->regs;
+ u16 bmsr = 0;
+ u16 bmcr = 0;
+
+ if(!regs) {
+ printk(KERN_ERR "%s: regs not set up", __FUNCTION__);
+ return -1;
+ }
+
+ if(fixed->phy_status.link)
+ bmsr |= BMSR_LSTATUS;
+
+ if(fixed->phy_status.duplex) {
+ bmcr |= BMCR_FULLDPLX;
+
+ switch ( fixed->phy_status.speed ) {
+ case 100:
+ bmsr |= BMSR_100FULL;
+ bmcr |= BMCR_SPEED100;
+ break;
+
+ case 10:
+ bmsr |= BMSR_10FULL;
+ break;
+ }
+ } else {
+ switch ( fixed->phy_status.speed ) {
+ case 100:
+ bmsr |= BMSR_100HALF;
+ bmcr |= BMCR_SPEED100;
+ break;
+
+ case 10:
+ bmsr |= BMSR_100HALF;
+ break;
+ }
+ }
+
+ regs[MII_BMCR] = bmcr;
+ regs[MII_BMSR] = bmsr | 0x800; /*we are always capable of 10 hdx*/
+
+ return 0;
+}
+
+
+static int fixed_mii_read(struct mii_bus *bus, int phy_id, int location)
+{
+ struct fixed_info *fixed = bus->priv;
+
+ /* if user has registered link update callback, use it */
+ if(fixed->phydev)
+ if(fixed->phydev->attached_dev) {
+ if(fixed->link_update) {
+ fixed->link_update(fixed->phydev->attached_dev,
+ &fixed->phy_status);
+ fixed_mdio_update_regs(fixed);
+ }
+ }
+
+ if ((unsigned int)location >= fixed->regs_num)
+ return -1;
+ return fixed->regs[location];
+}
+
+static int fixed_mii_write(struct mii_bus *bus, int phy_id, int location, u16 val)
+{
+ /* do nothing for now*/
+ return 0;
+}
+
+static int fixed_mii_reset(struct mii_bus *bus)
+{
+ /*nothing here - no way/need to reset it*/
+ return 0;
+}
+
+
+static int fixed_config_aneg(struct phy_device *phydev)
+{
+ /* :TODO:03/13/2006 09:45:37 PM::
+ The full autoneg funcionality can be emulated,
+ but no need to have anything here for now
+ */
+ return 0;
+}
+
+
+
+/*-----------------------------------------------------------------------------
+ * the manual bind will do the magic - with phy_id_mask == 0
+ * match will never return true...
+ *-----------------------------------------------------------------------------*/
+static struct phy_driver fixed_mdio_driver = {
+ .name = "Fixed PHY",
+ .features = PHY_BASIC_FEATURES,
+ .config_aneg = fixed_config_aneg,
+ .read_status = genphy_read_status,
+ .driver = { .owner = THIS_MODULE,},
+};
+
+
+
+/*-----------------------------------------------------------------------------
+ * This func is used to create all the necessary stuff, bind
+ * the fixed phy driver and register all it on the mdio_bus_type.
+ * speed is either 10 or 100, duplex is boolean.
+ * number is used to create multiple fixed PHYs, so that several devices can
+ * utilize them simultaneously.
+ *-----------------------------------------------------------------------------*/
+static int fixed_mdio_register_device(int number, int speed, int duplex)
+{
+ struct mii_bus *new_bus;
+ struct fixed_info *fixed;
+ struct phy_device *phydev;
+ int err = 0;
+
+ struct device* dev = kzalloc(sizeof(struct device), GFP_KERNEL);
+
+ if (NULL == dev)
+ return -EINVAL;
+
+ new_bus = kzalloc(sizeof(struct mii_bus), GFP_KERNEL);
+
+ if (NULL == new_bus) {
+ kfree(dev);
+ return -ENOMEM;
+ }
+ fixed = fixed_ptr = kzalloc(sizeof(struct fixed_info), GFP_KERNEL);
+
+ if (NULL == fixed) {
+ kfree(dev);
+ kfree(new_bus);
+ return -ENOMEM;
+ }
+
+ fixed->regs = kzalloc(MII_REGS_NUM*sizeof(int), GFP_KERNEL);
+
+ if (NULL == fixed->regs) {
+ kfree(dev);
+ kfree(new_bus);
+ kfree (fixed);
+ return -ENOMEM;
+ }
+
+ fixed->regs_num = MII_REGS_NUM;
+ fixed->phy_status.speed = speed;
+ fixed->phy_status.duplex = duplex;
+ fixed->phy_status.link = 1;
+
+ new_bus->name = "Fixed MII Bus",
+ new_bus->read = &fixed_mii_read,
+ new_bus->write = &fixed_mii_write,
+ new_bus->reset = &fixed_mii_reset,
+
+ /*set up workspace*/
+ fixed_mdio_update_regs(fixed);
+ new_bus->priv = fixed;
+
+ new_bus->dev = dev;
+ dev_set_drvdata(dev, new_bus);
+
+ /* create phy_device and register it on the mdio bus */
+ phydev = phy_device_create(new_bus, 0, 0);
+
+ /*
+ Put the phydev pointer into the fixed pack so that bus read/write code could be able
+ to access for instance attached netdev. Well it doesn't have to do so, only in case
+ of utilizing user-specified link-update...
+ */
+ fixed->phydev = phydev;
+
+ if (IS_ERR(phydev)) {
+ err = PTR_ERR(-ENOMEM);
+ goto bus_register_fail;
+ }
+
+ phydev->irq = -1;
+ phydev->dev.bus = &mdio_bus_type;
+
+ if(number)
+ snprintf(phydev->dev.bus_id, BUS_ID_SIZE,
+ "fixed_%d@%d:%d", number, speed, duplex);
+ else
+ snprintf(phydev->dev.bus_id, BUS_ID_SIZE,
+ "fixed@%d:%d", speed, duplex);
+ phydev->bus = new_bus;
+
+ err = device_register(&phydev->dev);
+ if(err) {
+ printk(KERN_ERR "Phy %s failed to register\n",
+ phydev->dev.bus_id);
+ goto bus_register_fail;
+ }
+
+ /*
+ the mdio bus has phy_id match... In order not to do it
+ artificially, we are binding the driver here by hand;
+ it will be the same
+ for all the fixed phys anyway.
+ */
+ down_write(&phydev->dev.bus->subsys.rwsem);
+
+ phydev->dev.driver = &fixed_mdio_driver.driver;
+
+ err = phydev->dev.driver->probe(&phydev->dev);
+ if(err < 0) {
+ printk(KERN_ERR "Phy %s: problems with fixed driver\n",
+ phydev->dev.bus_id);
+ up_write(&phydev->dev.bus->subsys.rwsem);
+ goto bus_register_fail;
+ }
+
+ device_bind_driver(&phydev->dev);
+ up_write(&phydev->dev.bus->subsys.rwsem);
+
+ return 0;
+
+bus_register_fail:
+ kfree(dev);
+ kfree (fixed);
+ kfree(new_bus);
+
+ return err;
+}
+
+
+MODULE_DESCRIPTION("Fixed PHY device & driver for PAL");
+MODULE_AUTHOR("Vitaly Bordug");
+MODULE_LICENSE("GPL");
+
+static int __init fixed_init(void)
+{
+ int ret;
+ int duplex = 0;
+
+ /* register on the bus... Not expected to be matched with anything there... */
+ phy_driver_register(&fixed_mdio_driver);
+
+ /* So let the fun begin...
+ We will create several mdio devices here, and will bound the upper
+ driver to them.
+
+ Then the external software can lookup the phy bus by searching
+ fixed@speed:duplex, e.g. fixed@100:1, to be connected to the
+ virtual 100M Fdx phy.
+
+ In case several virtual PHYs required, the bus_id will be in form
+ fixed_<num>@<speed>:<duplex>, which make it able even to define
+ driver-specific link control callback, if for instance PHY is completely
+ SW-driven.
+
+ */
+
+#ifdef CONFIG_FIXED_MII_DUPLEX
+ duplex = 1;
+#endif
+
+#ifdef CONFIG_FIXED_MII_100_FDX
+ fixed_mdio_register_device(0, 100, 1);
+#endif
+
+#ifdef CONFIX_FIXED_MII_10_FDX
+ fixed_mdio_register_device(0, 10, 1);
+#endif
+ return 0;
+}
+
+static void __exit fixed_exit(void)
+{
+ struct fixed_info *fixed = fixed_ptr;
+
+ phy_driver_unregister(&fixed_mdio_driver);
+ if (fixed) {
+ if (fixed->phydev) {
+ kfree(fixed->phydev->bus);
+ kfree(fixed->phydev);
+ }
+ kfree(fixed->regs);
+ kfree(fixed);
+ }
+}
+
+module_init(fixed_init);
+module_exit(fixed_exit);
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 7da0e3d..dfdafe9 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -46,6 +46,35 @@ static struct phy_driver genphy_driver;
extern int mdio_bus_init(void);
extern void mdio_bus_exit(void);
+struct phy_device* phy_device_create(struct mii_bus *bus, int addr, int phy_id)
+{
+ struct phy_device *dev;
+ /* We allocate the device, and initialize the
+ * default values */
+ dev = kcalloc(1, sizeof(*dev), GFP_KERNEL);
+
+ if (NULL == dev)
+ return PTR_ERR(-ENOMEM);
+
+ dev->speed = 0;
+ dev->duplex = -1;
+ dev->pause = dev->asym_pause = 0;
+ dev->link = 1;
+
+ dev->autoneg = AUTONEG_ENABLE;
+
+ dev->addr = addr;
+ dev->phy_id = phy_id;
+ dev->bus = bus;
+
+ dev->state = PHY_DOWN;
+
+ spin_lock_init(&dev->lock);
+
+ return dev;
+}
+EXPORT_SYMBOL(phy_device_create);
+
/* get_phy_device
*
* description: Reads the ID registers of the PHY at addr on the
@@ -79,27 +108,7 @@ struct phy_device * get_phy_device(struc
if (0xffffffff == phy_id)
return NULL;
- /* Otherwise, we allocate the device, and initialize the
- * default values */
- dev = kcalloc(1, sizeof(*dev), GFP_KERNEL);
-
- if (NULL == dev)
- return ERR_PTR(-ENOMEM);
-
- dev->speed = 0;
- dev->duplex = -1;
- dev->pause = dev->asym_pause = 0;
- dev->link = 1;
-
- dev->autoneg = AUTONEG_ENABLE;
-
- dev->addr = addr;
- dev->phy_id = phy_id;
- dev->bus = bus;
-
- dev->state = PHY_DOWN;
-
- spin_lock_init(&dev->lock);
+ dev = phy_device_create(bus, addr, phy_id);
return dev;
}
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 331521a..9447a57 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -378,6 +378,7 @@ int phy_mii_ioctl(struct phy_device *phy
struct mii_ioctl_data *mii_data, int cmd);
int phy_start_interrupts(struct phy_device *phydev);
void phy_print_status(struct phy_device *phydev);
+struct phy_device* phy_device_create(struct mii_bus *bus, int addr, int phy_id);
extern struct bus_type mdio_bus_type;
#endif /* __PHY_H */
^ permalink raw reply related
* [PATCH 3/3] FS_ENET: phydev pointer may be dereferenced without NULL check
From: Vitaly Bordug @ 2006-06-21 16:10 UTC (permalink / raw)
To: Jeff Garzik; +Cc: netdev, linux-kernel, linuxppc-embedded
In-Reply-To: <20060621160950.4860.92979.stgit@vitb.ru.mvista.com>
When interface is down, phy is "disconnected" from the bus and phydev is NULL.
But ethtool may try to get/set phy regs even at that time, which results in
NULL pointer dereference and OOPS hereby.
Signed-off-by: Vitaly Bordug <vbordug@ru.mvista.com>
---
drivers/net/fs_enet/fs_enet-main.c | 4 ++++
1 files changed, 4 insertions(+), 0 deletions(-)
diff --git a/drivers/net/fs_enet/fs_enet-main.c b/drivers/net/fs_enet/fs_enet-main.c
index 302ecaa..e475e22 100644
--- a/drivers/net/fs_enet/fs_enet-main.c
+++ b/drivers/net/fs_enet/fs_enet-main.c
@@ -882,12 +882,16 @@ static void fs_get_regs(struct net_devic
static int fs_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct fs_enet_private *fep = netdev_priv(dev);
+ if (!fep->phydev)
+ return -EINVAL;
return phy_ethtool_gset(fep->phydev, cmd);
}
static int fs_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct fs_enet_private *fep = netdev_priv(dev);
+ if (!fep->phydev)
+ return -EINVAL;
phy_ethtool_sset(fep->phydev, cmd);
return 0;
}
^ permalink raw reply related
* [PATCH 2/3] FS_ENET: use PAL for mii management
From: Vitaly Bordug @ 2006-06-21 16:10 UTC (permalink / raw)
To: Jeff Garzik; +Cc: netdev, linux-kernel, linuxppc-embedded
In-Reply-To: <20060621160950.4860.92979.stgit@vitb.ru.mvista.com>
This patch should update the fs_enet infrastructure to utilize
Phy Abstraction Layer subsystem. Inside there are generic driver rehaul,
board-specific portion to respect driver changes (for 8272ads and 866ads).
Signed-off-by: Vitaly Bordug <vbordug@ru.mvista.com>
---
arch/ppc/platforms/mpc8272ads_setup.c | 154 ++++++----
arch/ppc/platforms/mpc866ads_setup.c | 192 ++++++------
arch/ppc/platforms/mpc885ads_setup.c | 179 ++++--------
arch/ppc/syslib/mpc8xx_devices.c | 8 +
arch/ppc/syslib/mpc8xx_sys.c | 6
arch/ppc/syslib/pq2_devices.c | 5
arch/ppc/syslib/pq2_sys.c | 3
drivers/net/fs_enet/Makefile | 6
drivers/net/fs_enet/fec.h | 42 +++
drivers/net/fs_enet/fs_enet-main.c | 207 ++++++++-----
drivers/net/fs_enet/fs_enet-mii.c | 507 ---------------------------------
drivers/net/fs_enet/fs_enet.h | 40 ++-
drivers/net/fs_enet/mac-fcc.c | 10 -
drivers/net/fs_enet/mac-fec.c | 132 +--------
drivers/net/fs_enet/mac-scc.c | 4
drivers/net/fs_enet/mii-bitbang.c | 448 +++++++++++++++--------------
drivers/net/fs_enet/mii-fec.c | 243 ++++++++++++++++
drivers/net/fs_enet/mii-fixed.c | 92 ------
include/asm-ppc/mpc8260.h | 1
include/asm-ppc/mpc8xx.h | 1
include/linux/fs_enet_pd.h | 50 +--
21 files changed, 982 insertions(+), 1348 deletions(-)
diff --git a/arch/ppc/platforms/mpc8272ads_setup.c b/arch/ppc/platforms/mpc8272ads_setup.c
index abb7154..2a35fe2 100644
--- a/arch/ppc/platforms/mpc8272ads_setup.c
+++ b/arch/ppc/platforms/mpc8272ads_setup.c
@@ -56,64 +56,51 @@ static struct fs_uart_platform_info mpc8
},
};
-static struct fs_mii_bus_info mii_bus_info = {
- .method = fsmii_bitbang,
- .id = 0,
- .i.bitbang = {
- .mdio_port = fsiop_portc,
- .mdio_bit = 18,
- .mdc_port = fsiop_portc,
- .mdc_bit = 19,
- .delay = 1,
- },
-};
-
-static struct fs_platform_info mpc82xx_fcc1_pdata = {
- .fs_no = fsid_fcc1,
- .cp_page = CPM_CR_FCC1_PAGE,
- .cp_block = CPM_CR_FCC1_SBLOCK,
- .clk_trx = (PC_F1RXCLK | PC_F1TXCLK),
- .clk_route = CMX1_CLK_ROUTE,
- .clk_mask = CMX1_CLK_MASK,
- .init_ioports = init_fcc1_ioports,
-
- .phy_addr = 0,
-#ifdef PHY_INTERRUPT
- .phy_irq = PHY_INTERRUPT,
-#else
- .phy_irq = -1;
-#endif
- .mem_offset = FCC1_MEM_OFFSET,
- .bus_info = &mii_bus_info,
- .rx_ring = 32,
- .tx_ring = 32,
- .rx_copybreak = 240,
- .use_napi = 0,
- .napi_weight = 17,
+static struct fs_mii_bb_platform_info m82xx_mii_bb_pdata = {
+ .mdio_dat.bit = 18,
+ .mdio_dir.bit = 18,
+ .mdc_dat.bit = 19,
+ .delay = 1,
};
-static struct fs_platform_info mpc82xx_fcc2_pdata = {
- .fs_no = fsid_fcc2,
- .cp_page = CPM_CR_FCC2_PAGE,
- .cp_block = CPM_CR_FCC2_SBLOCK,
- .clk_trx = (PC_F2RXCLK | PC_F2TXCLK),
- .clk_route = CMX2_CLK_ROUTE,
- .clk_mask = CMX2_CLK_MASK,
- .init_ioports = init_fcc2_ioports,
-
- .phy_addr = 3,
-#ifdef PHY_INTERRUPT
- .phy_irq = PHY_INTERRUPT,
-#else
- .phy_irq = -1;
-#endif
- .mem_offset = FCC2_MEM_OFFSET,
- .bus_info = &mii_bus_info,
- .rx_ring = 32,
- .tx_ring = 32,
- .rx_copybreak = 240,
- .use_napi = 0,
- .napi_weight = 17,
+static struct fs_platform_info mpc82xx_enet_pdata[] = {
+ [fsid_fcc1] = {
+ .fs_no = fsid_fcc1,
+ .cp_page = CPM_CR_FCC1_PAGE,
+ .cp_block = CPM_CR_FCC1_SBLOCK,
+
+ .clk_trx = (PC_F1RXCLK | PC_F1TXCLK),
+ .clk_route = CMX1_CLK_ROUTE,
+ .clk_mask = CMX1_CLK_MASK,
+ .init_ioports = init_fcc1_ioports,
+
+ .mem_offset = FCC1_MEM_OFFSET,
+
+ .rx_ring = 32,
+ .tx_ring = 32,
+ .rx_copybreak = 240,
+ .use_napi = 0,
+ .napi_weight = 17,
+ .bus_id = "0:00",
+ },
+ [fsid_fcc2] = {
+ .fs_no = fsid_fcc2,
+ .cp_page = CPM_CR_FCC2_PAGE,
+ .cp_block = CPM_CR_FCC2_SBLOCK,
+ .clk_trx = (PC_F2RXCLK | PC_F2TXCLK),
+ .clk_route = CMX2_CLK_ROUTE,
+ .clk_mask = CMX2_CLK_MASK,
+ .init_ioports = init_fcc2_ioports,
+
+ .mem_offset = FCC2_MEM_OFFSET,
+
+ .rx_ring = 32,
+ .tx_ring = 32,
+ .rx_copybreak = 240,
+ .use_napi = 0,
+ .napi_weight = 17,
+ .bus_id = "0:03",
+ },
};
static void init_fcc1_ioports(void)
@@ -209,20 +196,21 @@ static void __init mpc8272ads_fixup_enet
bd_t* bi = (void*)__res;
int fs_no = fsid_fcc1+pdev->id-1;
- mpc82xx_fcc1_pdata.dpram_offset = mpc82xx_fcc2_pdata.dpram_offset = (u32)cpm2_immr->im_dprambase;
- mpc82xx_fcc1_pdata.fcc_regs_c = mpc82xx_fcc2_pdata.fcc_regs_c = (u32)cpm2_immr->im_fcc_c;
-
- switch(fs_no) {
- case fsid_fcc1:
- memcpy(&mpc82xx_fcc1_pdata.macaddr,bi->bi_enetaddr,6);
- pdev->dev.platform_data = &mpc82xx_fcc1_pdata;
- break;
- case fsid_fcc2:
- memcpy(&mpc82xx_fcc2_pdata.macaddr,bi->bi_enetaddr,6);
- mpc82xx_fcc2_pdata.macaddr[5] ^= 1;
- pdev->dev.platform_data = &mpc82xx_fcc2_pdata;
- break;
+ if(fs_no > ARRAY_SIZE(mpc82xx_enet_pdata)) {
+ return;
}
+
+ mpc82xx_enet_pdata[fs_no].dpram_offset=
+ (u32)cpm2_immr->im_dprambase;
+ mpc82xx_enet_pdata[fs_no].fcc_regs_c =
+ (u32)cpm2_immr->im_fcc_c;
+ memcpy(&mpc82xx_enet_pdata[fs_no].macaddr,bi->bi_enetaddr,6);
+
+ /* prevent dup mac */
+ if(fs_no == fsid_fcc2)
+ mpc82xx_enet_pdata[fs_no].macaddr[5] ^= 1;
+
+ pdev->dev.platform_data = &mpc82xx_enet_pdata[fs_no];
}
static void mpc8272ads_fixup_uart_pdata(struct platform_device *pdev,
@@ -274,6 +262,29 @@ static void init_scc4_uart_ioports(void)
iounmap(immap);
}
+static void __init mpc8272ads_fixup_mdio_pdata(struct platform_device *pdev,
+ int idx)
+{
+ m82xx_mii_bb_pdata.irq[0] = PHY_INTERRUPT;
+ m82xx_mii_bb_pdata.irq[1] = -1;
+ m82xx_mii_bb_pdata.irq[2] = -1;
+ m82xx_mii_bb_pdata.irq[3] = PHY_INTERRUPT;
+ m82xx_mii_bb_pdata.irq[31] = -1;
+
+
+ m82xx_mii_bb_pdata.mdio_dat.offset =
+ (u32)&cpm2_immr->im_ioport.iop_pdatc;
+
+ m82xx_mii_bb_pdata.mdio_dir.offset =
+ (u32)&cpm2_immr->im_ioport.iop_pdirc;
+
+ m82xx_mii_bb_pdata.mdc_dat.offset =
+ (u32)&cpm2_immr->im_ioport.iop_pdatc;
+
+
+ pdev->dev.platform_data = &m82xx_mii_bb_pdata;
+}
+
static int mpc8272ads_platform_notify(struct device *dev)
{
static const struct platform_notify_dev_map dev_map[] = {
@@ -286,6 +297,10 @@ static int mpc8272ads_platform_notify(st
.rtn = mpc8272ads_fixup_uart_pdata,
},
{
+ .bus_id = "fsl-bb-mdio",
+ .rtn = mpc8272ads_fixup_mdio_pdata,
+ },
+ {
.bus_id = NULL
}
};
@@ -319,6 +334,7 @@ int __init mpc8272ads_init(void)
ppc_sys_device_enable(MPC82xx_CPM_SCC4);
#endif
+ ppc_sys_device_enable(MPC82xx_MDIO_BB);
return 0;
}
diff --git a/arch/ppc/platforms/mpc866ads_setup.c b/arch/ppc/platforms/mpc866ads_setup.c
index d919dab..ed71dd9 100644
--- a/arch/ppc/platforms/mpc866ads_setup.c
+++ b/arch/ppc/platforms/mpc866ads_setup.c
@@ -1,10 +1,10 @@
-/*arch/ppc/platforms/mpc885ads-setup.c
+/*arch/ppc/platforms/mpc866ads-setup.c
*
- * Platform setup for the Freescale mpc885ads board
+ * Platform setup for the Freescale mpc866ads board
*
* Vitaly Bordug <vbordug@ru.mvista.com>
*
- * Copyright 2005 MontaVista Software Inc.
+ * Copyright 2005-2006 MontaVista Software Inc.
*
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed "as is" without any warranty of any
@@ -43,49 +43,36 @@ static void setup_scc1_ioports(void);
static void setup_smc1_ioports(void);
static void setup_smc2_ioports(void);
-static struct fs_mii_bus_info fec_mii_bus_info = {
- .method = fsmii_fec,
- .id = 0,
-};
-
-static struct fs_mii_bus_info scc_mii_bus_info = {
- .method = fsmii_fixed,
- .id = 0,
- .i.fixed.speed = 10,
- .i.fixed.duplex = 0,
-};
+static struct fs_mii_fec_platform_info mpc8xx_mdio_fec_pdata;
-static struct fs_platform_info mpc8xx_fec_pdata[] = {
- {
- .rx_ring = 128,
- .tx_ring = 16,
- .rx_copybreak = 240,
-
- .use_napi = 1,
- .napi_weight = 17,
+static struct fs_mii_fec_platform_info mpc8xx_mdio_fec_pdata;
- .phy_addr = 15,
- .phy_irq = -1,
+static struct fs_platform_info mpc8xx_enet_pdata[] = {
+ [fsid_fec1] = {
+ .rx_ring = 128,
+ .tx_ring = 16,
+ .rx_copybreak = 240,
- .use_rmii = 0,
+ .use_napi = 1,
+ .napi_weight = 17,
- .bus_info = &fec_mii_bus_info,
- }
-};
+ .init_ioports = setup_fec1_ioports,
-static struct fs_platform_info mpc8xx_scc_pdata = {
- .rx_ring = 64,
- .tx_ring = 8,
- .rx_copybreak = 240,
+ .bus_id = "0:0f",
+ .has_phy = 1,
+ },
+ [fsid_scc1] = {
+ .rx_ring = 64,
+ .tx_ring = 8,
+ .rx_copybreak = 240,
+ .use_napi = 1,
+ .napi_weight = 17,
- .use_napi = 1,
- .napi_weight = 17,
- .phy_addr = -1,
- .phy_irq = -1,
-
- .bus_info = &scc_mii_bus_info,
+ .init_ioports = setup_scc1_ioports,
+ .bus_id = "fixed@100:1",
+ },
};
static struct fs_uart_platform_info mpc866_uart_pdata[] = {
@@ -208,63 +195,6 @@ static void setup_scc1_ioports(void)
}
-static void mpc866ads_fixup_enet_pdata(struct platform_device *pdev, int fs_no)
-{
- struct fs_platform_info *fpi = pdev->dev.platform_data;
-
- volatile cpm8xx_t *cp;
- bd_t *bd = (bd_t *) __res;
- char *e;
- int i;
-
- /* Get pointer to Communication Processor */
- cp = cpmp;
- switch (fs_no) {
- case fsid_fec1:
- fpi = &mpc8xx_fec_pdata[0];
- fpi->init_ioports = &setup_fec1_ioports;
-
- break;
- case fsid_scc1:
- fpi = &mpc8xx_scc_pdata;
- fpi->init_ioports = &setup_scc1_ioports;
-
- break;
- default:
- printk(KERN_WARNING"Device %s is not supported!\n", pdev->name);
- return;
- }
-
- pdev->dev.platform_data = fpi;
- fpi->fs_no = fs_no;
-
- e = (unsigned char *)&bd->bi_enetaddr;
- for (i = 0; i < 6; i++)
- fpi->macaddr[i] = *e++;
-
- fpi->macaddr[5 - pdev->id]++;
-
-}
-
-static void mpc866ads_fixup_fec_enet_pdata(struct platform_device *pdev,
- int idx)
-{
- /* This is for FEC devices only */
- if (!pdev || !pdev->name || (!strstr(pdev->name, "fsl-cpm-fec")))
- return;
- mpc866ads_fixup_enet_pdata(pdev, fsid_fec1 + pdev->id - 1);
-}
-
-static void mpc866ads_fixup_scc_enet_pdata(struct platform_device *pdev,
- int idx)
-{
- /* This is for SCC devices only */
- if (!pdev || !pdev->name || (!strstr(pdev->name, "fsl-cpm-scc")))
- return;
-
- mpc866ads_fixup_enet_pdata(pdev, fsid_scc1 + pdev->id - 1);
-}
-
static void setup_smc1_ioports(void)
{
immap_t *immap = (immap_t *) IMAP_ADDR;
@@ -316,6 +246,56 @@ static void setup_smc2_ioports(void)
}
+static int ma_count = 0;
+
+static void mpc866ads_fixup_enet_pdata(struct platform_device *pdev, int fs_no)
+{
+ struct fs_platform_info *fpi;
+
+ volatile cpm8xx_t *cp;
+ bd_t *bd = (bd_t *) __res;
+ char *e;
+ int i;
+
+ /* Get pointer to Communication Processor */
+ cp = cpmp;
+
+ if(fs_no > ARRAY_SIZE(mpc8xx_enet_pdata)) {
+ printk(KERN_ERR"No network-suitable #%d device on bus", fs_no);
+ return;
+ }
+
+
+ fpi = &mpc8xx_enet_pdata[fs_no];
+ fpi->fs_no = fs_no;
+ pdev->dev.platform_data = fpi;
+
+ e = (unsigned char *)&bd->bi_enetaddr;
+ for (i = 0; i < 6; i++)
+ fpi->macaddr[i] = *e++;
+
+ fpi->macaddr[5] += ma_count++;
+}
+
+static void mpc866ads_fixup_fec_enet_pdata(struct platform_device *pdev,
+ int idx)
+{
+ /* This is for FEC devices only */
+ if (!pdev || !pdev->name || (!strstr(pdev->name, "fsl-cpm-fec")))
+ return;
+ mpc866ads_fixup_enet_pdata(pdev, fsid_fec1 + pdev->id - 1);
+}
+
+static void mpc866ads_fixup_scc_enet_pdata(struct platform_device *pdev,
+ int idx)
+{
+ /* This is for SCC devices only */
+ if (!pdev || !pdev->name || (!strstr(pdev->name, "fsl-cpm-scc")))
+ return;
+
+ mpc866ads_fixup_enet_pdata(pdev, fsid_scc1 + pdev->id - 1);
+}
+
static void __init mpc866ads_fixup_uart_pdata(struct platform_device *pdev,
int idx)
{
@@ -360,6 +340,9 @@ static int mpc866ads_platform_notify(str
int __init mpc866ads_init(void)
{
+ bd_t *bd = (bd_t *) __res;
+ struct fs_mii_fec_platform_info* fmpi;
+
printk(KERN_NOTICE "mpc866ads: Init\n");
platform_notify = mpc866ads_platform_notify;
@@ -367,11 +350,20 @@ int __init mpc866ads_init(void)
ppc_sys_device_initfunc();
ppc_sys_device_disable_all();
-#ifdef MPC8xx_SECOND_ETH_SCC1
+#ifdef CONFIG_MPC8xx_SECOND_ETH_SCC1
ppc_sys_device_enable(MPC8xx_CPM_SCC1);
#endif
ppc_sys_device_enable(MPC8xx_CPM_FEC1);
+ ppc_sys_device_enable(MPC8xx_MDIO_FEC);
+
+ fmpi = ppc_sys_platform_devices[MPC8xx_MDIO_FEC].dev.platform_data =
+ &mpc8xx_mdio_fec_pdata;
+
+ fmpi->mii_speed = ((((bd->bi_intfreq + 4999999) / 2500000) / 2) & 0x3F) << 1;
+ /* No PHY interrupt line here */
+ fmpi->irq[0xf] = -1;
+
/* Since either of the uarts could be used as console, they need to ready */
#ifdef CONFIG_SERIAL_CPM_SMC1
ppc_sys_device_enable(MPC8xx_CPM_SMC1);
@@ -382,6 +374,14 @@ int __init mpc866ads_init(void)
ppc_sys_device_enable(MPC8xx_CPM_SMC2);
ppc_sys_device_setfunc(MPC8xx_CPM_SMC2, PPC_SYS_FUNC_UART);
#endif
+ ppc_sys_device_enable(MPC8xx_MDIO_FEC);
+
+ fmpi = ppc_sys_platform_devices[MPC8xx_MDIO_FEC].dev.platform_data =
+ &mpc8xx_mdio_fec_pdata;
+
+ fmpi->mii_speed = ((((bd->bi_intfreq + 4999999) / 2500000) / 2) & 0x3F) << 1;
+ /* No PHY interrupt line here */
+ fmpi->irq[0xf] = -1;
return 0;
}
diff --git a/arch/ppc/platforms/mpc885ads_setup.c b/arch/ppc/platforms/mpc885ads_setup.c
index 4b88679..97b3002 100644
--- a/arch/ppc/platforms/mpc885ads_setup.c
+++ b/arch/ppc/platforms/mpc885ads_setup.c
@@ -39,7 +39,10 @@ extern unsigned char __res[];
static void setup_smc1_ioports(void);
static void setup_smc2_ioports(void);
-static void __init mpc885ads_scc_phy_init(char);
+static struct fs_mii_fec_platform_info mpc8xx_mdio_fec_pdata;
+static void setup_fec1_ioports(void);
+static void setup_fec2_ioports(void);
+static void setup_scc3_ioports(void);
static struct fs_uart_platform_info mpc885_uart_pdata[] = {
[fsid_smc1_uart] = {
@@ -62,23 +65,8 @@ static struct fs_uart_platform_info mpc8
},
};
-static struct fs_mii_bus_info fec_mii_bus_info = {
- .method = fsmii_fec,
- .id = 0,
-};
-
-static struct fs_mii_bus_info scc_mii_bus_info = {
-#ifdef CONFIG_SCC_ENET_8xx_FIXED
- .method = fsmii_fixed,
-#else
- .method = fsmii_fec,
-#endif
-
- .id = 0,
-};
-
-static struct fs_platform_info mpc8xx_fec_pdata[] = {
- {
+static struct fs_platform_info mpc8xx_enet_pdata[] = {
+ [fsid_fec1] = {
.rx_ring = 128,
.tx_ring = 16,
.rx_copybreak = 240,
@@ -86,11 +74,12 @@ static struct fs_platform_info mpc8xx_fe
.use_napi = 1,
.napi_weight = 17,
- .phy_addr = 0,
- .phy_irq = SIU_IRQ7,
+ .init_ioports = setup_fec1_ioports,
- .bus_info = &fec_mii_bus_info,
- }, {
+ .bus_id = "0:00",
+ .has_phy = 1,
+ },
+ [fsid_fec2] = {
.rx_ring = 128,
.tx_ring = 16,
.rx_copybreak = 240,
@@ -98,35 +87,32 @@ static struct fs_platform_info mpc8xx_fe
.use_napi = 1,
.napi_weight = 17,
- .phy_addr = 1,
- .phy_irq = SIU_IRQ7,
-
- .bus_info = &fec_mii_bus_info,
- }
-};
+ .init_ioports = setup_fec2_ioports,
-static struct fs_platform_info mpc8xx_scc_pdata = {
- .rx_ring = 64,
- .tx_ring = 8,
- .rx_copybreak = 240,
-
- .use_napi = 1,
- .napi_weight = 17,
-
- .phy_addr = 2,
-#ifdef CONFIG_MPC8xx_SCC_ENET_FIXED
- .phy_irq = -1,
+ .bus_id = "0:01",
+ .has_phy = 1,
+ },
+ [fsid_scc3] = {
+ .rx_ring = 64,
+ .tx_ring = 8,
+ .rx_copybreak = 240,
+
+ .use_napi = 1,
+ .napi_weight = 17,
+
+ .init_ioports = setup_scc3_ioports,
+#ifdef CONFIG_FIXED_MII_10_FDX
+ .bus_id = "fixed@100:1",
#else
- .phy_irq = SIU_IRQ7,
-#endif
-
- .bus_info = &scc_mii_bus_info,
+ .bus_id = "0:02",
+ #endif
+ },
};
void __init board_init(void)
{
- volatile cpm8xx_t *cp = cpmp;
- unsigned int *bcsr_io;
+ cpm8xx_t *cp = cpmp;
+ unsigned int *bcsr_io;
#ifdef CONFIG_FS_ENET
immap_t *immap = (immap_t *) IMAP_ADDR;
@@ -165,6 +151,14 @@ void __init board_init(void)
/* use MDC for MII (common) */
setbits16(&immap->im_ioport.iop_pdpar, 0x0080);
clrbits16(&immap->im_ioport.iop_pddir, 0x0080);
+ bcsr_io = ioremap(BCSR5, sizeof(unsigned long));
+ clrbits32(bcsr_io,BCSR5_MII1_EN);
+ clrbits32(bcsr_io,BCSR5_MII1_RST);
+#ifdef CONFIG_MPC8xx_SECOND_ETH_FEC2
+ clrbits32(bcsr_io,BCSR5_MII2_EN);
+ clrbits32(bcsr_io,BCSR5_MII2_RST);
+#endif
+ iounmap(bcsr_io);
#endif
}
@@ -195,8 +189,8 @@ static void setup_fec2_ioports(void)
/* configure FEC2 pins */
setbits32(&immap->im_cpm.cp_pepar, 0x0003fffc);
setbits32(&immap->im_cpm.cp_pedir, 0x0003fffc);
- setbits32(&immap->im_cpm.cp_peso, 0x00037800);
clrbits32(&immap->im_cpm.cp_peso, 0x000087fc);
+ setbits32(&immap->im_cpm.cp_peso, 0x00037800);
clrbits32(&immap->im_cpm.cp_cptr, 0x00000080);
}
@@ -214,6 +208,8 @@ static void setup_scc3_ioports(void)
/* Enable the PHY.
*/
+ clrbits32(bcsr_io+4, BCSR4_ETH10_RST);
+ udelay(1000);
setbits32(bcsr_io+4, BCSR4_ETH10_RST);
/* Configure port A pins for Txd and Rxd.
*/
@@ -255,37 +251,38 @@ static void setup_scc3_ioports(void)
clrbits32(&immap->im_cpm.cp_pedir, PE_ENET_TENA);
setbits32(&immap->im_cpm.cp_peso, PE_ENET_TENA);
- setbits32(bcsr_io+1, BCSR1_ETHEN);
+ setbits32(bcsr_io+4, BCSR1_ETHEN);
iounmap(bcsr_io);
}
+static int mac_count = 0;
+
static void mpc885ads_fixup_enet_pdata(struct platform_device *pdev, int fs_no)
{
- struct fs_platform_info *fpi = pdev->dev.platform_data;
-
- volatile cpm8xx_t *cp;
+ struct fs_platform_info *fpi;
bd_t *bd = (bd_t *) __res;
char *e;
int i;
- /* Get pointer to Communication Processor */
- cp = cpmp;
+ if(fs_no > ARRAY_SIZE(mpc8xx_enet_pdata)) {
+ printk(KERN_ERR"No network-suitable #%d device on bus", fs_no);
+ return;
+ }
+
+ fpi = &mpc8xx_enet_pdata[fs_no];
+
switch (fs_no) {
case fsid_fec1:
- fpi = &mpc8xx_fec_pdata[0];
fpi->init_ioports = &setup_fec1_ioports;
break;
case fsid_fec2:
- fpi = &mpc8xx_fec_pdata[1];
fpi->init_ioports = &setup_fec2_ioports;
break;
case fsid_scc3:
- fpi = &mpc8xx_scc_pdata;
fpi->init_ioports = &setup_scc3_ioports;
- mpc885ads_scc_phy_init(fpi->phy_addr);
break;
default:
- printk(KERN_WARNING"Device %s is not supported!\n", pdev->name);
+ printk(KERN_WARNING "Device %s is not supported!\n", pdev->name);
return;
}
@@ -296,7 +293,7 @@ static void mpc885ads_fixup_enet_pdata(s
for (i = 0; i < 6; i++)
fpi->macaddr[i] = *e++;
- fpi->macaddr[5 - pdev->id]++;
+ fpi->macaddr[5] += mac_count++;
}
@@ -319,58 +316,6 @@ static void __init mpc885ads_fixup_scc_e
mpc885ads_fixup_enet_pdata(pdev, fsid_scc1 + pdev->id - 1);
}
-/* SCC ethernet controller does not have MII management channel. FEC1 MII
- * channel is used to communicate with the 10Mbit PHY.
- */
-
-#define MII_ECNTRL_PINMUX 0x4
-#define FEC_ECNTRL_PINMUX 0x00000004
-#define FEC_RCNTRL_MII_MODE 0x00000004
-
-/* Make MII read/write commands.
- */
-#define mk_mii_write(REG, VAL, PHY_ADDR) (0x50020000 | (((REG) & 0x1f) << 18) | \
- ((VAL) & 0xffff) | ((PHY_ADDR) << 23))
-
-static void mpc885ads_scc_phy_init(char phy_addr)
-{
- volatile immap_t *immap;
- volatile fec_t *fecp;
- bd_t *bd;
-
- bd = (bd_t *) __res;
- immap = (immap_t *) IMAP_ADDR; /* pointer to internal registers */
- fecp = &(immap->im_cpm.cp_fec);
-
- /* Enable MII pins of the FEC1
- */
- setbits16(&immap->im_ioport.iop_pdpar, 0x0080);
- clrbits16(&immap->im_ioport.iop_pddir, 0x0080);
- /* Set MII speed to 2.5 MHz
- */
- out_be32(&fecp->fec_mii_speed,
- ((((bd->bi_intfreq + 4999999) / 2500000) / 2) & 0x3F) << 1);
-
- /* Enable FEC pin MUX
- */
- setbits32(&fecp->fec_ecntrl, MII_ECNTRL_PINMUX);
- setbits32(&fecp->fec_r_cntrl, FEC_RCNTRL_MII_MODE);
-
- out_be32(&fecp->fec_mii_data,
- mk_mii_write(MII_BMCR, BMCR_ISOLATE, phy_addr));
- udelay(100);
- out_be32(&fecp->fec_mii_data,
- mk_mii_write(MII_ADVERTISE,
- ADVERTISE_10HALF | ADVERTISE_CSMA, phy_addr));
- udelay(100);
-
- /* Disable FEC MII settings
- */
- clrbits32(&fecp->fec_ecntrl, MII_ECNTRL_PINMUX);
- clrbits32(&fecp->fec_r_cntrl, FEC_RCNTRL_MII_MODE);
- out_be32(&fecp->fec_mii_speed, 0);
-}
-
static void setup_smc1_ioports(void)
{
immap_t *immap = (immap_t *) IMAP_ADDR;
@@ -463,6 +408,9 @@ static int mpc885ads_platform_notify(str
int __init mpc885ads_init(void)
{
+ struct fs_mii_fec_platform_info* fmpi;
+ bd_t *bd = (bd_t *) __res;
+
printk(KERN_NOTICE "mpc885ads: Init\n");
platform_notify = mpc885ads_platform_notify;
@@ -472,8 +420,17 @@ int __init mpc885ads_init(void)
ppc_sys_device_enable(MPC8xx_CPM_FEC1);
+ ppc_sys_device_enable(MPC8xx_MDIO_FEC);
+ fmpi = ppc_sys_platform_devices[MPC8xx_MDIO_FEC].dev.platform_data =
+ &mpc8xx_mdio_fec_pdata;
+
+ fmpi->mii_speed = ((((bd->bi_intfreq + 4999999) / 2500000) / 2) & 0x3F) << 1;
+
+ /* No PHY interrupt line here */
+ fmpi->irq[0xf] = SIU_IRQ7;
+
#ifdef CONFIG_MPC8xx_SECOND_ETH_SCC3
- ppc_sys_device_enable(MPC8xx_CPM_SCC1);
+ ppc_sys_device_enable(MPC8xx_CPM_SCC3);
#endif
#ifdef CONFIG_MPC8xx_SECOND_ETH_FEC2
diff --git a/arch/ppc/syslib/mpc8xx_devices.c b/arch/ppc/syslib/mpc8xx_devices.c
index 6f53638..cf5ab47 100644
--- a/arch/ppc/syslib/mpc8xx_devices.c
+++ b/arch/ppc/syslib/mpc8xx_devices.c
@@ -218,6 +218,14 @@ struct platform_device ppc_sys_platform_
},
},
},
+
+ [MPC8xx_MDIO_FEC] = {
+ .name = "fsl-cpm-fec-mdio",
+ .id = 0,
+ .num_resources = 0,
+
+ },
+
};
static int __init mach_mpc8xx_fixup(struct platform_device *pdev)
diff --git a/arch/ppc/syslib/mpc8xx_sys.c b/arch/ppc/syslib/mpc8xx_sys.c
index eee2132..18ba1d7 100644
--- a/arch/ppc/syslib/mpc8xx_sys.c
+++ b/arch/ppc/syslib/mpc8xx_sys.c
@@ -22,7 +22,7 @@ struct ppc_sys_spec ppc_sys_specs[] = {
.ppc_sys_name = "MPC86X",
.mask = 0xFFFFFFFF,
.value = 0x00000000,
- .num_devices = 7,
+ .num_devices = 8,
.device_list = (enum ppc_sys_devices[])
{
MPC8xx_CPM_FEC1,
@@ -32,13 +32,14 @@ struct ppc_sys_spec ppc_sys_specs[] = {
MPC8xx_CPM_SCC4,
MPC8xx_CPM_SMC1,
MPC8xx_CPM_SMC2,
+ MPC8xx_MDIO_FEC,
},
},
{
.ppc_sys_name = "MPC885",
.mask = 0xFFFFFFFF,
.value = 0x00000000,
- .num_devices = 8,
+ .num_devices = 9,
.device_list = (enum ppc_sys_devices[])
{
MPC8xx_CPM_FEC1,
@@ -49,6 +50,7 @@ struct ppc_sys_spec ppc_sys_specs[] = {
MPC8xx_CPM_SCC4,
MPC8xx_CPM_SMC1,
MPC8xx_CPM_SMC2,
+ MPC8xx_MDIO_FEC,
},
},
{ /* default match */
diff --git a/arch/ppc/syslib/pq2_devices.c b/arch/ppc/syslib/pq2_devices.c
index 8692d00..fefbc21 100644
--- a/arch/ppc/syslib/pq2_devices.c
+++ b/arch/ppc/syslib/pq2_devices.c
@@ -369,6 +369,11 @@ struct platform_device ppc_sys_platform_
},
},
},
+ [MPC82xx_MDIO_BB] = {
+ .name = "fsl-bb-mdio",
+ .id = 0,
+ .num_resources = 0,
+ },
};
static int __init mach_mpc82xx_fixup(struct platform_device *pdev)
diff --git a/arch/ppc/syslib/pq2_sys.c b/arch/ppc/syslib/pq2_sys.c
index fee8948..f52600c 100644
--- a/arch/ppc/syslib/pq2_sys.c
+++ b/arch/ppc/syslib/pq2_sys.c
@@ -139,13 +139,14 @@ struct ppc_sys_spec ppc_sys_specs[] = {
.ppc_sys_name = "8272",
.mask = 0x0000ff00,
.value = 0x00000c00,
- .num_devices = 12,
+ .num_devices = 13,
.device_list = (enum ppc_sys_devices[])
{
MPC82xx_CPM_FCC1, MPC82xx_CPM_FCC2, MPC82xx_CPM_SCC1,
MPC82xx_CPM_SCC2, MPC82xx_CPM_SCC3, MPC82xx_CPM_SCC4,
MPC82xx_CPM_SMC1, MPC82xx_CPM_SMC2, MPC82xx_CPM_SPI,
MPC82xx_CPM_I2C, MPC82xx_CPM_USB, MPC82xx_SEC1,
+ MPC82xx_MDIO_BB,
},
},
/* below is a list of the 8280 family of processors */
diff --git a/drivers/net/fs_enet/Makefile b/drivers/net/fs_enet/Makefile
index d6dd3f2..02d4dc1 100644
--- a/drivers/net/fs_enet/Makefile
+++ b/drivers/net/fs_enet/Makefile
@@ -4,7 +4,7 @@
obj-$(CONFIG_FS_ENET) += fs_enet.o
-obj-$(CONFIG_8xx) += mac-fec.o mac-scc.o
-obj-$(CONFIG_8260) += mac-fcc.o
+obj-$(CONFIG_8xx) += mac-fec.o mac-scc.o mii-fec.o
+obj-$(CONFIG_CPM2) += mac-fcc.o mii-bitbang.o
-fs_enet-objs := fs_enet-main.o fs_enet-mii.o mii-bitbang.o mii-fixed.o
+fs_enet-objs := fs_enet-main.o
diff --git a/drivers/net/fs_enet/fec.h b/drivers/net/fs_enet/fec.h
new file mode 100644
index 0000000..e980527
--- /dev/null
+++ b/drivers/net/fs_enet/fec.h
@@ -0,0 +1,42 @@
+#ifndef FS_ENET_FEC_H
+#define FS_ENET_FEC_H
+
+/* CRC polynomium used by the FEC for the multicast group filtering */
+#define FEC_CRC_POLY 0x04C11DB7
+
+#define FEC_MAX_MULTICAST_ADDRS 64
+
+/* Interrupt events/masks.
+*/
+#define FEC_ENET_HBERR 0x80000000U /* Heartbeat error */
+#define FEC_ENET_BABR 0x40000000U /* Babbling receiver */
+#define FEC_ENET_BABT 0x20000000U /* Babbling transmitter */
+#define FEC_ENET_GRA 0x10000000U /* Graceful stop complete */
+#define FEC_ENET_TXF 0x08000000U /* Full frame transmitted */
+#define FEC_ENET_TXB 0x04000000U /* A buffer was transmitted */
+#define FEC_ENET_RXF 0x02000000U /* Full frame received */
+#define FEC_ENET_RXB 0x01000000U /* A buffer was received */
+#define FEC_ENET_MII 0x00800000U /* MII interrupt */
+#define FEC_ENET_EBERR 0x00400000U /* SDMA bus error */
+
+#define FEC_ECNTRL_PINMUX 0x00000004
+#define FEC_ECNTRL_ETHER_EN 0x00000002
+#define FEC_ECNTRL_RESET 0x00000001
+
+#define FEC_RCNTRL_BC_REJ 0x00000010
+#define FEC_RCNTRL_PROM 0x00000008
+#define FEC_RCNTRL_MII_MODE 0x00000004
+#define FEC_RCNTRL_DRT 0x00000002
+#define FEC_RCNTRL_LOOP 0x00000001
+
+#define FEC_TCNTRL_FDEN 0x00000004
+#define FEC_TCNTRL_HBC 0x00000002
+#define FEC_TCNTRL_GTS 0x00000001
+
+
+
+/*
+ * Delay to wait for FEC reset command to complete (in us)
+ */
+#define FEC_RESET_DELAY 50
+#endif
diff --git a/drivers/net/fs_enet/fs_enet-main.c b/drivers/net/fs_enet/fs_enet-main.c
index 196298f..302ecaa 100644
--- a/drivers/net/fs_enet/fs_enet-main.c
+++ b/drivers/net/fs_enet/fs_enet-main.c
@@ -38,6 +38,7 @@
#include <linux/bitops.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
+#include <linux/phy.h>
#include <linux/vmalloc.h>
#include <asm/pgtable.h>
@@ -683,35 +684,6 @@ static void fs_free_irq(struct net_devic
(*fep->ops->post_free_irq)(dev, irq);
}
-/**********************************************************************************/
-
-/* This interrupt occurs when the PHY detects a link change. */
-static irqreturn_t
-fs_mii_link_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
- struct net_device *dev = dev_id;
- struct fs_enet_private *fep;
- const struct fs_platform_info *fpi;
-
- fep = netdev_priv(dev);
- fpi = fep->fpi;
-
- /*
- * Acknowledge the interrupt if possible. If we have not
- * found the PHY yet we can't process or acknowledge the
- * interrupt now. Instead we ignore this interrupt for now,
- * which we can do since it is edge triggered. It will be
- * acknowledged later by fs_enet_open().
- */
- if (!fep->phy)
- return IRQ_NONE;
-
- fs_mii_ack_int(dev);
- fs_mii_link_status_change_check(dev, 0);
-
- return IRQ_HANDLED;
-}
-
static void fs_timeout(struct net_device *dev)
{
struct fs_enet_private *fep = netdev_priv(dev);
@@ -723,10 +695,13 @@ static void fs_timeout(struct net_device
spin_lock_irqsave(&fep->lock, flags);
if (dev->flags & IFF_UP) {
+ phy_stop(fep->phydev);
(*fep->ops->stop)(dev);
(*fep->ops->restart)(dev);
+ phy_start(fep->phydev);
}
+ phy_start(fep->phydev);
wake = fep->tx_free && !(CBDR_SC(fep->cur_tx) & BD_ENET_TX_READY);
spin_unlock_irqrestore(&fep->lock, flags);
@@ -734,35 +709,112 @@ static void fs_timeout(struct net_device
netif_wake_queue(dev);
}
+/*-----------------------------------------------------------------------------
+ * generic link-change handler - should be sufficient for most cases
+ *-----------------------------------------------------------------------------*/
+static void generic_adjust_link(struct net_device *dev)
+{
+ struct fs_enet_private *fep = netdev_priv(dev);
+ struct phy_device *phydev = fep->phydev;
+ int new_state = 0;
+
+ if (phydev->link) {
+
+ /* adjust to duplex mode */
+ if (phydev->duplex != fep->oldduplex){
+ new_state = 1;
+ fep->oldduplex = phydev->duplex;
+ }
+
+ if (phydev->speed != fep->oldspeed) {
+ new_state = 1;
+ fep->oldspeed = phydev->speed;
+ }
+
+ if (!fep->oldlink) {
+ new_state = 1;
+ fep->oldlink = 1;
+ netif_schedule(dev);
+ netif_carrier_on(dev);
+ netif_start_queue(dev);
+ }
+
+ if (new_state)
+ fep->ops->restart(dev);
+
+ } else if (fep->oldlink) {
+ new_state = 1;
+ fep->oldlink = 0;
+ fep->oldspeed = 0;
+ fep->oldduplex = -1;
+ netif_carrier_off(dev);
+ netif_stop_queue(dev);
+ }
+
+ if (new_state && netif_msg_link(fep))
+ phy_print_status(phydev);
+}
+
+
+static void fs_adjust_link(struct net_device *dev)
+{
+ struct fs_enet_private *fep = netdev_priv(dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&fep->lock, flags);
+
+ if(fep->ops->adjust_link)
+ fep->ops->adjust_link(dev);
+ else
+ generic_adjust_link(dev);
+
+ spin_unlock_irqrestore(&fep->lock, flags);
+}
+
+static int fs_init_phy(struct net_device *dev)
+{
+ struct fs_enet_private *fep = netdev_priv(dev);
+ struct phy_device *phydev;
+
+ fep->oldlink = 0;
+ fep->oldspeed = 0;
+ fep->oldduplex = -1;
+ if(fep->fpi->bus_id)
+ phydev = phy_connect(dev, fep->fpi->bus_id, &fs_adjust_link, 0);
+ else {
+ printk("No phy bus ID specified in BSP code\n");
+ return -EINVAL;
+ }
+ if (IS_ERR(phydev)) {
+ printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name);
+ return PTR_ERR(phydev);
+ }
+
+ fep->phydev = phydev;
+
+ return 0;
+}
+
+
static int fs_enet_open(struct net_device *dev)
{
struct fs_enet_private *fep = netdev_priv(dev);
- const struct fs_platform_info *fpi = fep->fpi;
int r;
+ int err;
/* Install our interrupt handler. */
r = fs_request_irq(dev, fep->interrupt, "fs_enet-mac", fs_enet_interrupt);
if (r != 0) {
printk(KERN_ERR DRV_MODULE_NAME
- ": %s Could not allocate FEC IRQ!", dev->name);
+ ": %s Could not allocate FS_ENET IRQ!", dev->name);
return -EINVAL;
}
- /* Install our phy interrupt handler */
- if (fpi->phy_irq != -1) {
+ err = fs_init_phy(dev);
+ if(err)
+ return err;
- r = fs_request_irq(dev, fpi->phy_irq, "fs_enet-phy", fs_mii_link_interrupt);
- if (r != 0) {
- printk(KERN_ERR DRV_MODULE_NAME
- ": %s Could not allocate PHY IRQ!", dev->name);
- fs_free_irq(dev, fep->interrupt);
- return -EINVAL;
- }
- }
-
- fs_mii_startup(dev);
- netif_carrier_off(dev);
- fs_mii_link_status_change_check(dev, 1);
+ phy_start(fep->phydev);
return 0;
}
@@ -770,20 +822,19 @@ static int fs_enet_open(struct net_devic
static int fs_enet_close(struct net_device *dev)
{
struct fs_enet_private *fep = netdev_priv(dev);
- const struct fs_platform_info *fpi = fep->fpi;
unsigned long flags;
netif_stop_queue(dev);
netif_carrier_off(dev);
- fs_mii_shutdown(dev);
+ phy_stop(fep->phydev);
spin_lock_irqsave(&fep->lock, flags);
(*fep->ops->stop)(dev);
spin_unlock_irqrestore(&fep->lock, flags);
/* release any irqs */
- if (fpi->phy_irq != -1)
- fs_free_irq(dev, fpi->phy_irq);
+ phy_disconnect(fep->phydev);
+ fep->phydev = NULL;
fs_free_irq(dev, fep->interrupt);
return 0;
@@ -831,33 +882,19 @@ static void fs_get_regs(struct net_devic
static int fs_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct fs_enet_private *fep = netdev_priv(dev);
- unsigned long flags;
- int rc;
-
- spin_lock_irqsave(&fep->lock, flags);
- rc = mii_ethtool_gset(&fep->mii_if, cmd);
- spin_unlock_irqrestore(&fep->lock, flags);
-
- return rc;
+ return phy_ethtool_gset(fep->phydev, cmd);
}
static int fs_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct fs_enet_private *fep = netdev_priv(dev);
- unsigned long flags;
- int rc;
-
- spin_lock_irqsave(&fep->lock, flags);
- rc = mii_ethtool_sset(&fep->mii_if, cmd);
- spin_unlock_irqrestore(&fep->lock, flags);
-
- return rc;
+ phy_ethtool_sset(fep->phydev, cmd);
+ return 0;
}
static int fs_nway_reset(struct net_device *dev)
{
- struct fs_enet_private *fep = netdev_priv(dev);
- return mii_nway_restart(&fep->mii_if);
+ return 0;
}
static u32 fs_get_msglevel(struct net_device *dev)
@@ -899,7 +936,7 @@ static int fs_ioctl(struct net_device *d
return -EINVAL;
spin_lock_irqsave(&fep->lock, flags);
- rc = generic_mii_ioctl(&fep->mii_if, mii, cmd, NULL);
+ rc = phy_mii_ioctl(fep->phydev, mii, cmd);
spin_unlock_irqrestore(&fep->lock, flags);
return rc;
}
@@ -1031,12 +1068,6 @@ static struct net_device *fs_init_instan
}
registered = 1;
- err = fs_mii_connect(ndev);
- if (err != 0) {
- printk(KERN_ERR DRV_MODULE_NAME
- ": %s fs_mii_connect failed.\n", ndev->name);
- goto err;
- }
return ndev;
@@ -1074,8 +1105,6 @@ static int fs_cleanup_instance(struct ne
fpi = fep->fpi;
- fs_mii_disconnect(ndev);
-
unregister_netdev(ndev);
dma_free_coherent(fep->dev, (fpi->tx_ring + fpi->rx_ring) * sizeof(cbd_t),
@@ -1197,17 +1226,39 @@ static int __init fs_init(void)
r = setup_immap();
if (r != 0)
return r;
- r = driver_register(&fs_enet_fec_driver);
+
+#ifdef CONFIG_FS_ENET_HAS_FCC
+ /* let's insert mii stuff */
+ r = fs_enet_mdio_bb_init();
+
+ if (r != 0) {
+ printk(KERN_ERR DRV_MODULE_NAME
+ "BB PHY init failed.\n");
+ return r;
+ }
+ r = driver_register(&fs_enet_fcc_driver);
if (r != 0)
goto err;
+#endif
- r = driver_register(&fs_enet_fcc_driver);
+#ifdef CONFIG_FS_ENET_HAS_FEC
+ r = fs_enet_mdio_fec_init();
+ if (r != 0) {
+ printk(KERN_ERR DRV_MODULE_NAME
+ "FEC PHY init failed.\n");
+ return r;
+ }
+
+ r = driver_register(&fs_enet_fec_driver);
if (r != 0)
goto err;
+#endif
+#ifdef CONFIG_FS_ENET_HAS_SCC
r = driver_register(&fs_enet_scc_driver);
if (r != 0)
goto err;
+#endif
return 0;
err:
diff --git a/drivers/net/fs_enet/fs_enet-mii.c b/drivers/net/fs_enet/fs_enet-mii.c
deleted file mode 100644
index c677037..0000000
--- a/drivers/net/fs_enet/fs_enet-mii.c
+++ /dev/null
@@ -1,507 +0,0 @@
-/*
- * Combined Ethernet driver for Motorola MPC8xx and MPC82xx.
- *
- * Copyright (c) 2003 Intracom S.A.
- * by Pantelis Antoniou <panto@intracom.gr>
- *
- * 2005 (c) MontaVista Software, Inc.
- * Vitaly Bordug <vbordug@ru.mvista.com>
- *
- * Heavily based on original FEC driver by Dan Malek <dan@embeddededge.com>
- * and modifications by Joakim Tjernlund <joakim.tjernlund@lumentis.se>
- *
- * This file is licensed under the terms of the GNU General Public License
- * version 2. This program is licensed "as is" without any warranty of any
- * kind, whether express or implied.
- */
-
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/string.h>
-#include <linux/ptrace.h>
-#include <linux/errno.h>
-#include <linux/ioport.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/spinlock.h>
-#include <linux/mii.h>
-#include <linux/ethtool.h>
-#include <linux/bitops.h>
-
-#include <asm/pgtable.h>
-#include <asm/irq.h>
-#include <asm/uaccess.h>
-
-#include "fs_enet.h"
-
-/*************************************************/
-
-/*
- * Generic PHY support.
- * Should work for all PHYs, but link change is detected by polling
- */
-
-static void generic_timer_callback(unsigned long data)
-{
- struct net_device *dev = (struct net_device *)data;
- struct fs_enet_private *fep = netdev_priv(dev);
-
- fep->phy_timer_list.expires = jiffies + HZ / 2;
-
- add_timer(&fep->phy_timer_list);
-
- fs_mii_link_status_change_check(dev, 0);
-}
-
-static void generic_startup(struct net_device *dev)
-{
- struct fs_enet_private *fep = netdev_priv(dev);
-
- fep->phy_timer_list.expires = jiffies + HZ / 2; /* every 500ms */
- fep->phy_timer_list.data = (unsigned long)dev;
- fep->phy_timer_list.function = generic_timer_callback;
- add_timer(&fep->phy_timer_list);
-}
-
-static void generic_shutdown(struct net_device *dev)
-{
- struct fs_enet_private *fep = netdev_priv(dev);
-
- del_timer_sync(&fep->phy_timer_list);
-}
-
-/* ------------------------------------------------------------------------- */
-/* The Davicom DM9161 is used on the NETTA board */
-
-/* register definitions */
-
-#define MII_DM9161_ANAR 4 /* Aux. Config Register */
-#define MII_DM9161_ACR 16 /* Aux. Config Register */
-#define MII_DM9161_ACSR 17 /* Aux. Config/Status Register */
-#define MII_DM9161_10TCSR 18 /* 10BaseT Config/Status Reg. */
-#define MII_DM9161_INTR 21 /* Interrupt Register */
-#define MII_DM9161_RECR 22 /* Receive Error Counter Reg. */
-#define MII_DM9161_DISCR 23 /* Disconnect Counter Register */
-
-static void dm9161_startup(struct net_device *dev)
-{
- struct fs_enet_private *fep = netdev_priv(dev);
-
- fs_mii_write(dev, fep->mii_if.phy_id, MII_DM9161_INTR, 0x0000);
- /* Start autonegotiation */
- fs_mii_write(dev, fep->mii_if.phy_id, MII_BMCR, 0x1200);
-
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(HZ*8);
-}
-
-static void dm9161_ack_int(struct net_device *dev)
-{
- struct fs_enet_private *fep = netdev_priv(dev);
-
- fs_mii_read(dev, fep->mii_if.phy_id, MII_DM9161_INTR);
-}
-
-static void dm9161_shutdown(struct net_device *dev)
-{
- struct fs_enet_private *fep = netdev_priv(dev);
-
- fs_mii_write(dev, fep->mii_if.phy_id, MII_DM9161_INTR, 0x0f00);
-}
-
-/**********************************************************************************/
-
-static const struct phy_info phy_info[] = {
- {
- .id = 0x00181b88,
- .name = "DM9161",
- .startup = dm9161_startup,
- .ack_int = dm9161_ack_int,
- .shutdown = dm9161_shutdown,
- }, {
- .id = 0,
- .name = "GENERIC",
- .startup = generic_startup,
- .shutdown = generic_shutdown,
- },
-};
-
-/**********************************************************************************/
-
-static int phy_id_detect(struct net_device *dev)
-{
- struct fs_enet_private *fep = netdev_priv(dev);
- const struct fs_platform_info *fpi = fep->fpi;
- struct fs_enet_mii_bus *bus = fep->mii_bus;
- int i, r, start, end, phytype, physubtype;
- const struct phy_info *phy;
- int phy_hwid, phy_id;
-
- phy_hwid = -1;
- fep->phy = NULL;
-
- /* auto-detect? */
- if (fpi->phy_addr == -1) {
- start = 1;
- end = 32;
- } else { /* direct */
- start = fpi->phy_addr;
- end = start + 1;
- }
-
- for (phy_id = start; phy_id < end; phy_id++) {
- /* skip already used phy addresses on this bus */
- if (bus->usage_map & (1 << phy_id))
- continue;
- r = fs_mii_read(dev, phy_id, MII_PHYSID1);
- if (r == -1 || (phytype = (r & 0xffff)) == 0xffff)
- continue;
- r = fs_mii_read(dev, phy_id, MII_PHYSID2);
- if (r == -1 || (physubtype = (r & 0xffff)) == 0xffff)
- continue;
- phy_hwid = (phytype << 16) | physubtype;
- if (phy_hwid != -1)
- break;
- }
-
- if (phy_hwid == -1) {
- printk(KERN_ERR DRV_MODULE_NAME
- ": %s No PHY detected! range=0x%02x-0x%02x\n",
- dev->name, start, end);
- return -1;
- }
-
- for (i = 0, phy = phy_info; i < ARRAY_SIZE(phy_info); i++, phy++)
- if (phy->id == (phy_hwid >> 4) || phy->id == 0)
- break;
-
- if (i >= ARRAY_SIZE(phy_info)) {
- printk(KERN_ERR DRV_MODULE_NAME
- ": %s PHY id 0x%08x is not supported!\n",
- dev->name, phy_hwid);
- return -1;
- }
-
- fep->phy = phy;
-
- /* mark this address as used */
- bus->usage_map |= (1 << phy_id);
-
- printk(KERN_INFO DRV_MODULE_NAME
- ": %s Phy @ 0x%x, type %s (0x%08x)%s\n",
- dev->name, phy_id, fep->phy->name, phy_hwid,
- fpi->phy_addr == -1 ? " (auto-detected)" : "");
-
- return phy_id;
-}
-
-void fs_mii_startup(struct net_device *dev)
-{
- struct fs_enet_private *fep = netdev_priv(dev);
-
- if (fep->phy->startup)
- (*fep->phy->startup) (dev);
-}
-
-void fs_mii_shutdown(struct net_device *dev)
-{
- struct fs_enet_private *fep = netdev_priv(dev);
-
- if (fep->phy->shutdown)
- (*fep->phy->shutdown) (dev);
-}
-
-void fs_mii_ack_int(struct net_device *dev)
-{
- struct fs_enet_private *fep = netdev_priv(dev);
-
- if (fep->phy->ack_int)
- (*fep->phy->ack_int) (dev);
-}
-
-#define MII_LINK 0x0001
-#define MII_HALF 0x0002
-#define MII_FULL 0x0004
-#define MII_BASE4 0x0008
-#define MII_10M 0x0010
-#define MII_100M 0x0020
-#define MII_1G 0x0040
-#define MII_10G 0x0080
-
-/* return full mii info at one gulp, with a usable form */
-static unsigned int mii_full_status(struct mii_if_info *mii)
-{
- unsigned int status;
- int bmsr, adv, lpa, neg;
- struct fs_enet_private* fep = netdev_priv(mii->dev);
-
- /* first, a dummy read, needed to latch some MII phys */
- (void)mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
- bmsr = mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
-
- /* no link */
- if ((bmsr & BMSR_LSTATUS) == 0)
- return 0;
-
- status = MII_LINK;
-
- /* Lets look what ANEG says if it's supported - otherwize we shall
- take the right values from the platform info*/
- if(!mii->force_media) {
- /* autoneg not completed; don't bother */
- if ((bmsr & BMSR_ANEGCOMPLETE) == 0)
- return 0;
-
- adv = (*mii->mdio_read)(mii->dev, mii->phy_id, MII_ADVERTISE);
- lpa = (*mii->mdio_read)(mii->dev, mii->phy_id, MII_LPA);
-
- neg = lpa & adv;
- } else {
- neg = fep->fpi->bus_info->lpa;
- }
-
- if (neg & LPA_100FULL)
- status |= MII_FULL | MII_100M;
- else if (neg & LPA_100BASE4)
- status |= MII_FULL | MII_BASE4 | MII_100M;
- else if (neg & LPA_100HALF)
- status |= MII_HALF | MII_100M;
- else if (neg & LPA_10FULL)
- status |= MII_FULL | MII_10M;
- else
- status |= MII_HALF | MII_10M;
-
- return status;
-}
-
-void fs_mii_link_status_change_check(struct net_device *dev, int init_media)
-{
- struct fs_enet_private *fep = netdev_priv(dev);
- struct mii_if_info *mii = &fep->mii_if;
- unsigned int mii_status;
- int ok_to_print, link, duplex, speed;
- unsigned long flags;
-
- ok_to_print = netif_msg_link(fep);
-
- mii_status = mii_full_status(mii);
-
- if (!init_media && mii_status == fep->last_mii_status)
- return;
-
- fep->last_mii_status = mii_status;
-
- link = !!(mii_status & MII_LINK);
- duplex = !!(mii_status & MII_FULL);
- speed = (mii_status & MII_100M) ? 100 : 10;
-
- if (link == 0) {
- netif_carrier_off(mii->dev);
- netif_stop_queue(dev);
- if (!init_media) {
- spin_lock_irqsave(&fep->lock, flags);
- (*fep->ops->stop)(dev);
- spin_unlock_irqrestore(&fep->lock, flags);
- }
-
- if (ok_to_print)
- printk(KERN_INFO "%s: link down\n", mii->dev->name);
-
- } else {
-
- mii->full_duplex = duplex;
-
- netif_carrier_on(mii->dev);
-
- spin_lock_irqsave(&fep->lock, flags);
- fep->duplex = duplex;
- fep->speed = speed;
- (*fep->ops->restart)(dev);
- spin_unlock_irqrestore(&fep->lock, flags);
-
- netif_start_queue(dev);
-
- if (ok_to_print)
- printk(KERN_INFO "%s: link up, %dMbps, %s-duplex\n",
- dev->name, speed, duplex ? "full" : "half");
- }
-}
-
-/**********************************************************************************/
-
-int fs_mii_read(struct net_device *dev, int phy_id, int location)
-{
- struct fs_enet_private *fep = netdev_priv(dev);
- struct fs_enet_mii_bus *bus = fep->mii_bus;
-
- unsigned long flags;
- int ret;
-
- spin_lock_irqsave(&bus->mii_lock, flags);
- ret = (*bus->mii_read)(bus, phy_id, location);
- spin_unlock_irqrestore(&bus->mii_lock, flags);
-
- return ret;
-}
-
-void fs_mii_write(struct net_device *dev, int phy_id, int location, int value)
-{
- struct fs_enet_private *fep = netdev_priv(dev);
- struct fs_enet_mii_bus *bus = fep->mii_bus;
- unsigned long flags;
-
- spin_lock_irqsave(&bus->mii_lock, flags);
- (*bus->mii_write)(bus, phy_id, location, value);
- spin_unlock_irqrestore(&bus->mii_lock, flags);
-}
-
-/*****************************************************************************/
-
-/* list of all registered mii buses */
-static LIST_HEAD(fs_mii_bus_list);
-
-static struct fs_enet_mii_bus *lookup_bus(int method, int id)
-{
- struct list_head *ptr;
- struct fs_enet_mii_bus *bus;
-
- list_for_each(ptr, &fs_mii_bus_list) {
- bus = list_entry(ptr, struct fs_enet_mii_bus, list);
- if (bus->bus_info->method == method &&
- bus->bus_info->id == id)
- return bus;
- }
- return NULL;
-}
-
-static struct fs_enet_mii_bus *create_bus(const struct fs_mii_bus_info *bi)
-{
- struct fs_enet_mii_bus *bus;
- int ret = 0;
-
- bus = kmalloc(sizeof(*bus), GFP_KERNEL);
- if (bus == NULL) {
- ret = -ENOMEM;
- goto err;
- }
- memset(bus, 0, sizeof(*bus));
- spin_lock_init(&bus->mii_lock);
- bus->bus_info = bi;
- bus->refs = 0;
- bus->usage_map = 0;
-
- /* perform initialization */
- switch (bi->method) {
-
- case fsmii_fixed:
- ret = fs_mii_fixed_init(bus);
- if (ret != 0)
- goto err;
- break;
-
- case fsmii_bitbang:
- ret = fs_mii_bitbang_init(bus);
- if (ret != 0)
- goto err;
- break;
-#ifdef CONFIG_FS_ENET_HAS_FEC
- case fsmii_fec:
- ret = fs_mii_fec_init(bus);
- if (ret != 0)
- goto err;
- break;
-#endif
- default:
- ret = -EINVAL;
- goto err;
- }
-
- list_add(&bus->list, &fs_mii_bus_list);
-
- return bus;
-
-err:
- if (bus)
- kfree(bus);
- return ERR_PTR(ret);
-}
-
-static void destroy_bus(struct fs_enet_mii_bus *bus)
-{
- /* remove from bus list */
- list_del(&bus->list);
-
- /* nothing more needed */
- kfree(bus);
-}
-
-int fs_mii_connect(struct net_device *dev)
-{
- struct fs_enet_private *fep = netdev_priv(dev);
- const struct fs_platform_info *fpi = fep->fpi;
- struct fs_enet_mii_bus *bus = NULL;
-
- /* check method validity */
- switch (fpi->bus_info->method) {
- case fsmii_fixed:
- case fsmii_bitbang:
- break;
-#ifdef CONFIG_FS_ENET_HAS_FEC
- case fsmii_fec:
- break;
-#endif
- default:
- printk(KERN_ERR DRV_MODULE_NAME
- ": %s Unknown MII bus method (%d)!\n",
- dev->name, fpi->bus_info->method);
- return -EINVAL;
- }
-
- bus = lookup_bus(fpi->bus_info->method, fpi->bus_info->id);
-
- /* if not found create new bus */
- if (bus == NULL) {
- bus = create_bus(fpi->bus_info);
- if (IS_ERR(bus)) {
- printk(KERN_ERR DRV_MODULE_NAME
- ": %s MII bus creation failure!\n", dev->name);
- return PTR_ERR(bus);
- }
- }
-
- bus->refs++;
-
- fep->mii_bus = bus;
-
- fep->mii_if.dev = dev;
- fep->mii_if.phy_id_mask = 0x1f;
- fep->mii_if.reg_num_mask = 0x1f;
- fep->mii_if.mdio_read = fs_mii_read;
- fep->mii_if.mdio_write = fs_mii_write;
- fep->mii_if.force_media = fpi->bus_info->disable_aneg;
- fep->mii_if.phy_id = phy_id_detect(dev);
-
- return 0;
-}
-
-void fs_mii_disconnect(struct net_device *dev)
-{
- struct fs_enet_private *fep = netdev_priv(dev);
- struct fs_enet_mii_bus *bus = NULL;
-
- bus = fep->mii_bus;
- fep->mii_bus = NULL;
-
- if (--bus->refs <= 0)
- destroy_bus(bus);
-}
diff --git a/drivers/net/fs_enet/fs_enet.h b/drivers/net/fs_enet/fs_enet.h
index e7ec96c..95022c0 100644
--- a/drivers/net/fs_enet/fs_enet.h
+++ b/drivers/net/fs_enet/fs_enet.h
@@ -5,6 +5,7 @@
#include <linux/netdevice.h>
#include <linux/types.h>
#include <linux/list.h>
+#include <linux/phy.h>
#include <linux/fs_enet_pd.h>
@@ -12,12 +13,30 @@
#ifdef CONFIG_CPM1
#include <asm/commproc.h>
+
+struct fec_info {
+ fec_t* fecp;
+ u32 mii_speed;
+};
#endif
#ifdef CONFIG_CPM2
#include <asm/cpm2.h>
#endif
+/* This is used to operate with pins.
+ Note that the actual port size may
+ be different; cpm(s) handle it OK */
+struct bb_info {
+ u8 mdio_dat_msk;
+ u8 mdio_dir_msk;
+ u8 *mdio_dir;
+ u8 *mdio_dat;
+ u8 mdc_msk;
+ u8 *mdc_dat;
+ int delay;
+};
+
/* hw driver ops */
struct fs_ops {
int (*setup_data)(struct net_device *dev);
@@ -25,6 +44,7 @@ struct fs_ops {
void (*free_bd)(struct net_device *dev);
void (*cleanup_data)(struct net_device *dev);
void (*set_multicast_list)(struct net_device *dev);
+ void (*adjust_link)(struct net_device *dev);
void (*restart)(struct net_device *dev);
void (*stop)(struct net_device *dev);
void (*pre_request_irq)(struct net_device *dev, int irq);
@@ -100,10 +120,6 @@ struct fs_enet_mii_bus {
};
};
-int fs_mii_bitbang_init(struct fs_enet_mii_bus *bus);
-int fs_mii_fixed_init(struct fs_enet_mii_bus *bus);
-int fs_mii_fec_init(struct fs_enet_mii_bus *bus);
-
struct fs_enet_private {
struct device *dev; /* pointer back to the device (must be initialized first) */
spinlock_t lock; /* during all ops except TX pckt processing */
@@ -130,7 +146,8 @@ struct fs_enet_private {
struct fs_enet_mii_bus *mii_bus;
int interrupt;
- int duplex, speed; /* current settings */
+ struct phy_device *phydev;
+ int oldduplex, oldspeed, oldlink; /* current settings */
/* event masks */
u32 ev_napi_rx; /* mask of NAPI rx events */
@@ -168,15 +185,9 @@ struct fs_enet_private {
};
/***************************************************************************/
-
-int fs_mii_read(struct net_device *dev, int phy_id, int location);
-void fs_mii_write(struct net_device *dev, int phy_id, int location, int value);
-
-void fs_mii_startup(struct net_device *dev);
-void fs_mii_shutdown(struct net_device *dev);
-void fs_mii_ack_int(struct net_device *dev);
-
-void fs_mii_link_status_change_check(struct net_device *dev, int init_media);
+int fs_enet_mdio_bb_init(void);
+int fs_mii_fixed_init(struct fs_enet_mii_bus *bus);
+int fs_enet_mdio_fec_init(void);
void fs_init_bds(struct net_device *dev);
void fs_cleanup_bds(struct net_device *dev);
@@ -194,7 +205,6 @@ int fs_enet_platform_init(void);
void fs_enet_platform_cleanup(void);
/***************************************************************************/
-
/* buffer descriptor access macros */
/* access macros */
diff --git a/drivers/net/fs_enet/mac-fcc.c b/drivers/net/fs_enet/mac-fcc.c
index 95e2bb8..ce40cf9 100644
--- a/drivers/net/fs_enet/mac-fcc.c
+++ b/drivers/net/fs_enet/mac-fcc.c
@@ -35,6 +35,7 @@
#include <linux/bitops.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
+#include <linux/phy.h>
#include <asm/immap_cpm2.h>
#include <asm/mpc8260.h>
@@ -395,7 +396,7 @@ static void restart(struct net_device *d
/* adjust to speed (for RMII mode) */
if (fpi->use_rmii) {
- if (fep->speed == 100)
+ if (fep->phydev->speed == 100)
C8(fcccp, fcc_gfemr, 0x20);
else
S8(fcccp, fcc_gfemr, 0x20);
@@ -421,7 +422,7 @@ static void restart(struct net_device *d
S32(fccp, fcc_fpsmr, FCC_PSMR_RMII);
/* adjust to duplex mode */
- if (fep->duplex)
+ if (fep->phydev->duplex)
S32(fccp, fcc_fpsmr, FCC_PSMR_FDE | FCC_PSMR_LPB);
else
C32(fccp, fcc_fpsmr, FCC_PSMR_FDE | FCC_PSMR_LPB);
@@ -487,7 +488,10 @@ static void rx_bd_done(struct net_device
static void tx_kickstart(struct net_device *dev)
{
- /* nothing */
+ struct fs_enet_private *fep = netdev_priv(dev);
+ fcc_t *fccp = fep->fcc.fccp;
+
+ S32(fccp, fcc_ftodr, 0x80);
}
static u32 get_int_events(struct net_device *dev)
diff --git a/drivers/net/fs_enet/mac-fec.c b/drivers/net/fs_enet/mac-fec.c
index 3dad69d..99678c3 100644
--- a/drivers/net/fs_enet/mac-fec.c
+++ b/drivers/net/fs_enet/mac-fec.c
@@ -47,6 +47,7 @@
#endif
#include "fs_enet.h"
+#include "fec.h"
/*************************************************/
@@ -76,48 +77,6 @@
/* clear bits */
#define FC(_fecp, _reg, _v) FW(_fecp, _reg, FR(_fecp, _reg) & ~(_v))
-
-/* CRC polynomium used by the FEC for the multicast group filtering */
-#define FEC_CRC_POLY 0x04C11DB7
-
-#define FEC_MAX_MULTICAST_ADDRS 64
-
-/* Interrupt events/masks.
-*/
-#define FEC_ENET_HBERR 0x80000000U /* Heartbeat error */
-#define FEC_ENET_BABR 0x40000000U /* Babbling receiver */
-#define FEC_ENET_BABT 0x20000000U /* Babbling transmitter */
-#define FEC_ENET_GRA 0x10000000U /* Graceful stop complete */
-#define FEC_ENET_TXF 0x08000000U /* Full frame transmitted */
-#define FEC_ENET_TXB 0x04000000U /* A buffer was transmitted */
-#define FEC_ENET_RXF 0x02000000U /* Full frame received */
-#define FEC_ENET_RXB 0x01000000U /* A buffer was received */
-#define FEC_ENET_MII 0x00800000U /* MII interrupt */
-#define FEC_ENET_EBERR 0x00400000U /* SDMA bus error */
-
-#define FEC_ECNTRL_PINMUX 0x00000004
-#define FEC_ECNTRL_ETHER_EN 0x00000002
-#define FEC_ECNTRL_RESET 0x00000001
-
-#define FEC_RCNTRL_BC_REJ 0x00000010
-#define FEC_RCNTRL_PROM 0x00000008
-#define FEC_RCNTRL_MII_MODE 0x00000004
-#define FEC_RCNTRL_DRT 0x00000002
-#define FEC_RCNTRL_LOOP 0x00000001
-
-#define FEC_TCNTRL_FDEN 0x00000004
-#define FEC_TCNTRL_HBC 0x00000002
-#define FEC_TCNTRL_GTS 0x00000001
-
-
-/* Make MII read/write commands for the FEC.
-*/
-#define mk_mii_read(REG) (0x60020000 | ((REG & 0x1f) << 18))
-#define mk_mii_write(REG, VAL) (0x50020000 | ((REG & 0x1f) << 18) | (VAL & 0xffff))
-#define mk_mii_end 0
-
-#define FEC_MII_LOOPS 10000
-
/*
* Delay to wait for FEC reset command to complete (in us)
*/
@@ -304,11 +263,13 @@ static void restart(struct net_device *d
int r;
u32 addrhi, addrlo;
+ struct mii_bus* mii = fep->phydev->bus;
+ struct fec_info* fec_inf = mii->priv;
+
r = whack_reset(fep->fec.fecp);
if (r != 0)
printk(KERN_ERR DRV_MODULE_NAME
": %s FEC Reset FAILED!\n", dev->name);
-
/*
* Set station address.
*/
@@ -353,7 +314,7 @@ static void restart(struct net_device *d
/*
* Set MII speed.
*/
- FW(fecp, mii_speed, fep->mii_bus->fec.mii_speed);
+ FW(fecp, mii_speed, fec_inf->mii_speed);
/*
* Clear any outstanding interrupt.
@@ -391,11 +352,12 @@ static void restart(struct net_device *d
}
#endif
+
FW(fecp, r_cntrl, FEC_RCNTRL_MII_MODE); /* MII enable */
/*
* adjust to duplex mode
*/
- if (fep->duplex) {
+ if (fep->phydev->duplex) {
FC(fecp, r_cntrl, FEC_RCNTRL_DRT);
FS(fecp, x_cntrl, FEC_TCNTRL_FDEN); /* FD enable */
} else {
@@ -419,9 +381,11 @@ static void restart(struct net_device *d
static void stop(struct net_device *dev)
{
struct fs_enet_private *fep = netdev_priv(dev);
+ const struct fs_platform_info *fpi = fep->fpi;
fec_t *fecp = fep->fec.fecp;
- struct fs_enet_mii_bus *bus = fep->mii_bus;
- const struct fs_mii_bus_info *bi = bus->bus_info;
+
+ struct fec_info* feci= fep->phydev->bus->priv;
+
int i;
if ((FR(fecp, ecntrl) & FEC_ECNTRL_ETHER_EN) == 0)
@@ -445,11 +409,11 @@ static void stop(struct net_device *dev)
fs_cleanup_bds(dev);
/* shut down FEC1? that's where the mii bus is */
- if (fep->fec.idx == 0 && bus->refs > 1 && bi->method == fsmii_fec) {
+ if (fpi->has_phy) {
FS(fecp, r_cntrl, FEC_RCNTRL_MII_MODE); /* MII enable */
FS(fecp, ecntrl, FEC_ECNTRL_PINMUX | FEC_ECNTRL_ETHER_EN);
FW(fecp, ievent, FEC_ENET_MII);
- FW(fecp, mii_speed, bus->fec.mii_speed);
+ FW(fecp, mii_speed, feci->mii_speed);
}
}
@@ -584,73 +548,3 @@ const struct fs_ops fs_fec_ops = {
.free_bd = free_bd,
};
-/***********************************************************************/
-
-static int mii_read(struct fs_enet_mii_bus *bus, int phy_id, int location)
-{
- fec_t *fecp = bus->fec.fecp;
- int i, ret = -1;
-
- if ((FR(fecp, r_cntrl) & FEC_RCNTRL_MII_MODE) == 0)
- BUG();
-
- /* Add PHY address to register command. */
- FW(fecp, mii_data, (phy_id << 23) | mk_mii_read(location));
-
- for (i = 0; i < FEC_MII_LOOPS; i++)
- if ((FR(fecp, ievent) & FEC_ENET_MII) != 0)
- break;
-
- if (i < FEC_MII_LOOPS) {
- FW(fecp, ievent, FEC_ENET_MII);
- ret = FR(fecp, mii_data) & 0xffff;
- }
-
- return ret;
-}
-
-static void mii_write(struct fs_enet_mii_bus *bus, int phy_id, int location, int value)
-{
- fec_t *fecp = bus->fec.fecp;
- int i;
-
- /* this must never happen */
- if ((FR(fecp, r_cntrl) & FEC_RCNTRL_MII_MODE) == 0)
- BUG();
-
- /* Add PHY address to register command. */
- FW(fecp, mii_data, (phy_id << 23) | mk_mii_write(location, value));
-
- for (i = 0; i < FEC_MII_LOOPS; i++)
- if ((FR(fecp, ievent) & FEC_ENET_MII) != 0)
- break;
-
- if (i < FEC_MII_LOOPS)
- FW(fecp, ievent, FEC_ENET_MII);
-}
-
-int fs_mii_fec_init(struct fs_enet_mii_bus *bus)
-{
- bd_t *bd = (bd_t *)__res;
- const struct fs_mii_bus_info *bi = bus->bus_info;
- fec_t *fecp;
-
- if (bi->id != 0)
- return -1;
-
- bus->fec.fecp = &((immap_t *)fs_enet_immap)->im_cpm.cp_fec;
- bus->fec.mii_speed = ((((bd->bi_intfreq + 4999999) / 2500000) / 2)
- & 0x3F) << 1;
-
- fecp = bus->fec.fecp;
-
- FS(fecp, r_cntrl, FEC_RCNTRL_MII_MODE); /* MII enable */
- FS(fecp, ecntrl, FEC_ECNTRL_PINMUX | FEC_ECNTRL_ETHER_EN);
- FW(fecp, ievent, FEC_ENET_MII);
- FW(fecp, mii_speed, bus->fec.mii_speed);
-
- bus->mii_read = mii_read;
- bus->mii_write = mii_write;
-
- return 0;
-}
diff --git a/drivers/net/fs_enet/mac-scc.c b/drivers/net/fs_enet/mac-scc.c
index a772b28..d829120 100644
--- a/drivers/net/fs_enet/mac-scc.c
+++ b/drivers/net/fs_enet/mac-scc.c
@@ -370,7 +370,7 @@ static void restart(struct net_device *d
W16(sccp, scc_psmr, SCC_PSMR_ENCRC | SCC_PSMR_NIB22);
/* Set full duplex mode if needed */
- if (fep->duplex)
+ if (fep->phydev->duplex)
S16(sccp, scc_psmr, SCC_PSMR_LPB | SCC_PSMR_FDE);
S32(sccp, scc_gsmrl, SCC_GSMRL_ENR | SCC_GSMRL_ENT);
@@ -501,6 +501,8 @@ static void tx_restart(struct net_device
scc_cr_cmd(fep, CPM_CR_RESTART_TX);
}
+
+
/*************************************************************************/
const struct fs_ops fs_scc_ops = {
diff --git a/drivers/net/fs_enet/mii-bitbang.c b/drivers/net/fs_enet/mii-bitbang.c
index 24a5e2e..cf2955f 100644
--- a/drivers/net/fs_enet/mii-bitbang.c
+++ b/drivers/net/fs_enet/mii-bitbang.c
@@ -34,6 +34,7 @@
#include <linux/mii.h>
#include <linux/ethtool.h>
#include <linux/bitops.h>
+#include <linux/platform_device.h>
#include <asm/pgtable.h>
#include <asm/irq.h>
@@ -41,129 +42,25 @@
#include "fs_enet.h"
-#ifdef CONFIG_8xx
-static int bitbang_prep_bit(u8 **dirp, u8 **datp, u8 *mskp, int port, int bit)
+static int bitbang_prep_bit(u8 **datp, u8 *mskp,
+ struct fs_mii_bit *mii_bit)
{
- immap_t *im = (immap_t *)fs_enet_immap;
- void *dir, *dat, *ppar;
+ void *dat;
int adv;
u8 msk;
- switch (port) {
- case fsiop_porta:
- dir = &im->im_ioport.iop_padir;
- dat = &im->im_ioport.iop_padat;
- ppar = &im->im_ioport.iop_papar;
- break;
-
- case fsiop_portb:
- dir = &im->im_cpm.cp_pbdir;
- dat = &im->im_cpm.cp_pbdat;
- ppar = &im->im_cpm.cp_pbpar;
- break;
-
- case fsiop_portc:
- dir = &im->im_ioport.iop_pcdir;
- dat = &im->im_ioport.iop_pcdat;
- ppar = &im->im_ioport.iop_pcpar;
- break;
-
- case fsiop_portd:
- dir = &im->im_ioport.iop_pddir;
- dat = &im->im_ioport.iop_pddat;
- ppar = &im->im_ioport.iop_pdpar;
- break;
-
- case fsiop_porte:
- dir = &im->im_cpm.cp_pedir;
- dat = &im->im_cpm.cp_pedat;
- ppar = &im->im_cpm.cp_pepar;
- break;
-
- default:
- printk(KERN_ERR DRV_MODULE_NAME
- "Illegal port value %d!\n", port);
- return -EINVAL;
- }
-
- adv = bit >> 3;
- dir = (char *)dir + adv;
- dat = (char *)dat + adv;
- ppar = (char *)ppar + adv;
-
- msk = 1 << (7 - (bit & 7));
- if ((in_8(ppar) & msk) != 0) {
- printk(KERN_ERR DRV_MODULE_NAME
- "pin %d on port %d is not general purpose!\n", bit, port);
- return -EINVAL;
- }
-
- *dirp = dir;
- *datp = dat;
- *mskp = msk;
-
- return 0;
-}
-#endif
-
-#ifdef CONFIG_8260
-static int bitbang_prep_bit(u8 **dirp, u8 **datp, u8 *mskp, int port, int bit)
-{
- iop_cpm2_t *io = &((cpm2_map_t *)fs_enet_immap)->im_ioport;
- void *dir, *dat, *ppar;
- int adv;
- u8 msk;
-
- switch (port) {
- case fsiop_porta:
- dir = &io->iop_pdira;
- dat = &io->iop_pdata;
- ppar = &io->iop_ppara;
- break;
-
- case fsiop_portb:
- dir = &io->iop_pdirb;
- dat = &io->iop_pdatb;
- ppar = &io->iop_pparb;
- break;
-
- case fsiop_portc:
- dir = &io->iop_pdirc;
- dat = &io->iop_pdatc;
- ppar = &io->iop_pparc;
- break;
-
- case fsiop_portd:
- dir = &io->iop_pdird;
- dat = &io->iop_pdatd;
- ppar = &io->iop_ppard;
- break;
-
- default:
- printk(KERN_ERR DRV_MODULE_NAME
- "Illegal port value %d!\n", port);
- return -EINVAL;
- }
+ dat = (void*) mii_bit->offset;
- adv = bit >> 3;
- dir = (char *)dir + adv;
+ adv = mii_bit->bit >> 3;
dat = (char *)dat + adv;
- ppar = (char *)ppar + adv;
- msk = 1 << (7 - (bit & 7));
- if ((in_8(ppar) & msk) != 0) {
- printk(KERN_ERR DRV_MODULE_NAME
- "pin %d on port %d is not general purpose!\n", bit, port);
- return -EINVAL;
- }
+ msk = 1 << (7 - (mii_bit->bit & 7));
- *dirp = dir;
*datp = dat;
*mskp = msk;
return 0;
}
-#endif
static inline void bb_set(u8 *p, u8 m)
{
@@ -180,44 +77,44 @@ static inline int bb_read(u8 *p, u8 m)
return (in_8(p) & m) != 0;
}
-static inline void mdio_active(struct fs_enet_mii_bus *bus)
+static inline void mdio_active(struct bb_info *bitbang)
{
- bb_set(bus->bitbang.mdio_dir, bus->bitbang.mdio_msk);
+ bb_set(bitbang->mdio_dir, bitbang->mdio_dir_msk);
}
-static inline void mdio_tristate(struct fs_enet_mii_bus *bus)
+static inline void mdio_tristate(struct bb_info *bitbang )
{
- bb_clr(bus->bitbang.mdio_dir, bus->bitbang.mdio_msk);
+ bb_clr(bitbang->mdio_dir, bitbang->mdio_dir_msk);
}
-static inline int mdio_read(struct fs_enet_mii_bus *bus)
+static inline int mdio_read(struct bb_info *bitbang )
{
- return bb_read(bus->bitbang.mdio_dat, bus->bitbang.mdio_msk);
+ return bb_read(bitbang->mdio_dat, bitbang->mdio_dat_msk);
}
-static inline void mdio(struct fs_enet_mii_bus *bus, int what)
+static inline void mdio(struct bb_info *bitbang , int what)
{
if (what)
- bb_set(bus->bitbang.mdio_dat, bus->bitbang.mdio_msk);
+ bb_set(bitbang->mdio_dat, bitbang->mdio_dat_msk);
else
- bb_clr(bus->bitbang.mdio_dat, bus->bitbang.mdio_msk);
+ bb_clr(bitbang->mdio_dat, bitbang->mdio_dat_msk);
}
-static inline void mdc(struct fs_enet_mii_bus *bus, int what)
+static inline void mdc(struct bb_info *bitbang , int what)
{
if (what)
- bb_set(bus->bitbang.mdc_dat, bus->bitbang.mdc_msk);
+ bb_set(bitbang->mdc_dat, bitbang->mdc_msk);
else
- bb_clr(bus->bitbang.mdc_dat, bus->bitbang.mdc_msk);
+ bb_clr(bitbang->mdc_dat, bitbang->mdc_msk);
}
-static inline void mii_delay(struct fs_enet_mii_bus *bus)
+static inline void mii_delay(struct bb_info *bitbang )
{
- udelay(bus->bus_info->i.bitbang.delay);
+ udelay(bitbang->delay);
}
/* Utility to send the preamble, address, and register (common to read and write). */
-static void bitbang_pre(struct fs_enet_mii_bus *bus, int read, u8 addr, u8 reg)
+static void bitbang_pre(struct bb_info *bitbang , int read, u8 addr, u8 reg)
{
int j;
@@ -229,177 +126,284 @@ static void bitbang_pre(struct fs_enet_m
* but it is safer and will be much more robust.
*/
- mdio_active(bus);
- mdio(bus, 1);
+ mdio_active(bitbang);
+ mdio(bitbang, 1);
for (j = 0; j < 32; j++) {
- mdc(bus, 0);
- mii_delay(bus);
- mdc(bus, 1);
- mii_delay(bus);
+ mdc(bitbang, 0);
+ mii_delay(bitbang);
+ mdc(bitbang, 1);
+ mii_delay(bitbang);
}
/* send the start bit (01) and the read opcode (10) or write (10) */
- mdc(bus, 0);
- mdio(bus, 0);
- mii_delay(bus);
- mdc(bus, 1);
- mii_delay(bus);
- mdc(bus, 0);
- mdio(bus, 1);
- mii_delay(bus);
- mdc(bus, 1);
- mii_delay(bus);
- mdc(bus, 0);
- mdio(bus, read);
- mii_delay(bus);
- mdc(bus, 1);
- mii_delay(bus);
- mdc(bus, 0);
- mdio(bus, !read);
- mii_delay(bus);
- mdc(bus, 1);
- mii_delay(bus);
+ mdc(bitbang, 0);
+ mdio(bitbang, 0);
+ mii_delay(bitbang);
+ mdc(bitbang, 1);
+ mii_delay(bitbang);
+ mdc(bitbang, 0);
+ mdio(bitbang, 1);
+ mii_delay(bitbang);
+ mdc(bitbang, 1);
+ mii_delay(bitbang);
+ mdc(bitbang, 0);
+ mdio(bitbang, read);
+ mii_delay(bitbang);
+ mdc(bitbang, 1);
+ mii_delay(bitbang);
+ mdc(bitbang, 0);
+ mdio(bitbang, !read);
+ mii_delay(bitbang);
+ mdc(bitbang, 1);
+ mii_delay(bitbang);
/* send the PHY address */
for (j = 0; j < 5; j++) {
- mdc(bus, 0);
- mdio(bus, (addr & 0x10) != 0);
- mii_delay(bus);
- mdc(bus, 1);
- mii_delay(bus);
+ mdc(bitbang, 0);
+ mdio(bitbang, (addr & 0x10) != 0);
+ mii_delay(bitbang);
+ mdc(bitbang, 1);
+ mii_delay(bitbang);
addr <<= 1;
}
/* send the register address */
for (j = 0; j < 5; j++) {
- mdc(bus, 0);
- mdio(bus, (reg & 0x10) != 0);
- mii_delay(bus);
- mdc(bus, 1);
- mii_delay(bus);
+ mdc(bitbang, 0);
+ mdio(bitbang, (reg & 0x10) != 0);
+ mii_delay(bitbang);
+ mdc(bitbang, 1);
+ mii_delay(bitbang);
reg <<= 1;
}
}
-static int mii_read(struct fs_enet_mii_bus *bus, int phy_id, int location)
+static int fs_enet_mii_bb_read(struct mii_bus *bus , int phy_id, int location)
{
u16 rdreg;
int ret, j;
u8 addr = phy_id & 0xff;
u8 reg = location & 0xff;
+ struct bb_info* bitbang = bus->priv;
- bitbang_pre(bus, 1, addr, reg);
+ bitbang_pre(bitbang, 1, addr, reg);
/* tri-state our MDIO I/O pin so we can read */
- mdc(bus, 0);
- mdio_tristate(bus);
- mii_delay(bus);
- mdc(bus, 1);
- mii_delay(bus);
+ mdc(bitbang, 0);
+ mdio_tristate(bitbang);
+ mii_delay(bitbang);
+ mdc(bitbang, 1);
+ mii_delay(bitbang);
/* check the turnaround bit: the PHY should be driving it to zero */
- if (mdio_read(bus) != 0) {
+ if (mdio_read(bitbang) != 0) {
/* PHY didn't drive TA low */
for (j = 0; j < 32; j++) {
- mdc(bus, 0);
- mii_delay(bus);
- mdc(bus, 1);
- mii_delay(bus);
+ mdc(bitbang, 0);
+ mii_delay(bitbang);
+ mdc(bitbang, 1);
+ mii_delay(bitbang);
}
ret = -1;
goto out;
}
- mdc(bus, 0);
- mii_delay(bus);
+ mdc(bitbang, 0);
+ mii_delay(bitbang);
/* read 16 bits of register data, MSB first */
rdreg = 0;
for (j = 0; j < 16; j++) {
- mdc(bus, 1);
- mii_delay(bus);
+ mdc(bitbang, 1);
+ mii_delay(bitbang);
rdreg <<= 1;
- rdreg |= mdio_read(bus);
- mdc(bus, 0);
- mii_delay(bus);
+ rdreg |= mdio_read(bitbang);
+ mdc(bitbang, 0);
+ mii_delay(bitbang);
}
- mdc(bus, 1);
- mii_delay(bus);
- mdc(bus, 0);
- mii_delay(bus);
- mdc(bus, 1);
- mii_delay(bus);
+ mdc(bitbang, 1);
+ mii_delay(bitbang);
+ mdc(bitbang, 0);
+ mii_delay(bitbang);
+ mdc(bitbang, 1);
+ mii_delay(bitbang);
ret = rdreg;
out:
return ret;
}
-static void mii_write(struct fs_enet_mii_bus *bus, int phy_id, int location, int val)
+static int fs_enet_mii_bb_write(struct mii_bus *bus, int phy_id, int location, u16 val)
{
int j;
+ struct bb_info* bitbang = bus->priv;
+
u8 addr = phy_id & 0xff;
u8 reg = location & 0xff;
u16 value = val & 0xffff;
- bitbang_pre(bus, 0, addr, reg);
+ bitbang_pre(bitbang, 0, addr, reg);
/* send the turnaround (10) */
- mdc(bus, 0);
- mdio(bus, 1);
- mii_delay(bus);
- mdc(bus, 1);
- mii_delay(bus);
- mdc(bus, 0);
- mdio(bus, 0);
- mii_delay(bus);
- mdc(bus, 1);
- mii_delay(bus);
+ mdc(bitbang, 0);
+ mdio(bitbang, 1);
+ mii_delay(bitbang);
+ mdc(bitbang, 1);
+ mii_delay(bitbang);
+ mdc(bitbang, 0);
+ mdio(bitbang, 0);
+ mii_delay(bitbang);
+ mdc(bitbang, 1);
+ mii_delay(bitbang);
/* write 16 bits of register data, MSB first */
for (j = 0; j < 16; j++) {
- mdc(bus, 0);
- mdio(bus, (value & 0x8000) != 0);
- mii_delay(bus);
- mdc(bus, 1);
- mii_delay(bus);
+ mdc(bitbang, 0);
+ mdio(bitbang, (value & 0x8000) != 0);
+ mii_delay(bitbang);
+ mdc(bitbang, 1);
+ mii_delay(bitbang);
value <<= 1;
}
/*
* Tri-state the MDIO line.
*/
- mdio_tristate(bus);
- mdc(bus, 0);
- mii_delay(bus);
- mdc(bus, 1);
- mii_delay(bus);
+ mdio_tristate(bitbang);
+ mdc(bitbang, 0);
+ mii_delay(bitbang);
+ mdc(bitbang, 1);
+ mii_delay(bitbang);
+ return 0;
}
-int fs_mii_bitbang_init(struct fs_enet_mii_bus *bus)
+static int fs_enet_mii_bb_reset(struct mii_bus *bus)
+{
+ /*nothing here - dunno how to reset it*/
+ return 0;
+}
+
+static int fs_mii_bitbang_init(struct bb_info *bitbang, struct fs_mii_bb_platform_info* fmpi)
{
- const struct fs_mii_bus_info *bi = bus->bus_info;
int r;
- r = bitbang_prep_bit(&bus->bitbang.mdio_dir,
- &bus->bitbang.mdio_dat,
- &bus->bitbang.mdio_msk,
- bi->i.bitbang.mdio_port,
- bi->i.bitbang.mdio_bit);
+ bitbang->delay = fmpi->delay;
+
+ r = bitbang_prep_bit(&bitbang->mdio_dir,
+ &bitbang->mdio_dir_msk,
+ &fmpi->mdio_dir);
if (r != 0)
return r;
- r = bitbang_prep_bit(&bus->bitbang.mdc_dir,
- &bus->bitbang.mdc_dat,
- &bus->bitbang.mdc_msk,
- bi->i.bitbang.mdc_port,
- bi->i.bitbang.mdc_bit);
+ r = bitbang_prep_bit(&bitbang->mdio_dat,
+ &bitbang->mdio_dat_msk,
+ &fmpi->mdio_dat);
if (r != 0)
return r;
- bus->mii_read = mii_read;
- bus->mii_write = mii_write;
+ r = bitbang_prep_bit(&bitbang->mdc_dat,
+ &bitbang->mdc_msk,
+ &fmpi->mdc_dat);
+ if (r != 0)
+ return r;
return 0;
}
+
+
+static int __devinit fs_enet_mdio_probe(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct fs_mii_bb_platform_info *pdata;
+ struct mii_bus *new_bus;
+ struct bb_info *bitbang;
+ int err = 0;
+
+ if (NULL == dev)
+ return -EINVAL;
+
+ new_bus = kzalloc(sizeof(struct mii_bus), GFP_KERNEL);
+
+ if (NULL == new_bus)
+ return -ENOMEM;
+
+ bitbang = kzalloc(sizeof(struct bb_info), GFP_KERNEL);
+
+ if (NULL == bitbang)
+ return -ENOMEM;
+
+ new_bus->name = "BB MII Bus",
+ new_bus->read = &fs_enet_mii_bb_read,
+ new_bus->write = &fs_enet_mii_bb_write,
+ new_bus->reset = &fs_enet_mii_bb_reset,
+ new_bus->id = pdev->id;
+
+ new_bus->phy_mask = ~0x9;
+ pdata = (struct fs_mii_bb_platform_info *)pdev->dev.platform_data;
+
+ if (NULL == pdata) {
+ printk(KERN_ERR "gfar mdio %d: Missing platform data!\n", pdev->id);
+ return -ENODEV;
+ }
+
+ /*set up workspace*/
+ fs_mii_bitbang_init(bitbang, pdata);
+
+ new_bus->priv = bitbang;
+
+ new_bus->irq = pdata->irq;
+
+ new_bus->dev = dev;
+ dev_set_drvdata(dev, new_bus);
+
+ err = mdiobus_register(new_bus);
+
+ if (0 != err) {
+ printk (KERN_ERR "%s: Cannot register as MDIO bus\n",
+ new_bus->name);
+ goto bus_register_fail;
+ }
+
+ return 0;
+
+bus_register_fail:
+ kfree(bitbang);
+ kfree(new_bus);
+
+ return err;
+}
+
+
+static int fs_enet_mdio_remove(struct device *dev)
+{
+ struct mii_bus *bus = dev_get_drvdata(dev);
+
+ mdiobus_unregister(bus);
+
+ dev_set_drvdata(dev, NULL);
+
+ iounmap((void *) (&bus->priv));
+ bus->priv = NULL;
+ kfree(bus);
+
+ return 0;
+}
+
+static struct device_driver fs_enet_bb_mdio_driver = {
+ .name = "fsl-bb-mdio",
+ .bus = &platform_bus_type,
+ .probe = fs_enet_mdio_probe,
+ .remove = fs_enet_mdio_remove,
+};
+
+int fs_enet_mdio_bb_init(void)
+{
+ return driver_register(&fs_enet_bb_mdio_driver);
+}
+
+void fs_enet_mdio_bb_exit(void)
+{
+ driver_unregister(&fs_enet_bb_mdio_driver);
+}
+
diff --git a/drivers/net/fs_enet/mii-fec.c b/drivers/net/fs_enet/mii-fec.c
new file mode 100644
index 0000000..1328e10
--- /dev/null
+++ b/drivers/net/fs_enet/mii-fec.c
@@ -0,0 +1,243 @@
+/*
+ * Combined Ethernet driver for Motorola MPC8xx and MPC82xx.
+ *
+ * Copyright (c) 2003 Intracom S.A.
+ * by Pantelis Antoniou <panto@intracom.gr>
+ *
+ * 2005 (c) MontaVista Software, Inc.
+ * Vitaly Bordug <vbordug@ru.mvista.com>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/bitops.h>
+#include <linux/platform_device.h>
+
+#include <asm/pgtable.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+#include "fs_enet.h"
+#include "fec.h"
+
+/* Make MII read/write commands for the FEC.
+*/
+#define mk_mii_read(REG) (0x60020000 | ((REG & 0x1f) << 18))
+#define mk_mii_write(REG, VAL) (0x50020000 | ((REG & 0x1f) << 18) | (VAL & 0xffff))
+#define mk_mii_end 0
+
+#define FEC_MII_LOOPS 10000
+
+static int match_has_phy (struct device *dev, void* data)
+{
+ struct platform_device* pdev = container_of(dev, struct platform_device, dev);
+ struct fs_platform_info* fpi;
+ if(strcmp(pdev->name, (char*)data))
+ {
+ return 0;
+ }
+
+ fpi = pdev->dev.platform_data;
+ if((fpi)&&(fpi->has_phy))
+ return 1;
+ return 0;
+}
+
+static int fs_mii_fec_init(struct fec_info* fec, struct fs_mii_fec_platform_info *fmpi)
+{
+ struct resource *r;
+ fec_t *fecp;
+ char* name = "fsl-cpm-fec";
+
+ /* we need fec in order to be useful */
+ struct platform_device *fec_pdev =
+ container_of(bus_find_device(&platform_bus_type, NULL, name, match_has_phy),
+ struct platform_device, dev);
+
+ if(fec_pdev == NULL) {
+ printk(KERN_ERR"Unable to find PHY for %s", name);
+ return -ENODEV;
+ }
+
+ r = platform_get_resource_byname(fec_pdev, IORESOURCE_MEM, "regs");
+
+ fec->fecp = fecp = (fec_t*)ioremap(r->start,sizeof(fec_t));
+ fec->mii_speed = fmpi->mii_speed;
+
+ setbits32(&fecp->fec_r_cntrl, FEC_RCNTRL_MII_MODE); /* MII enable */
+ setbits32(&fecp->fec_ecntrl, FEC_ECNTRL_PINMUX | FEC_ECNTRL_ETHER_EN);
+ out_be32(&fecp->fec_ievent, FEC_ENET_MII);
+ out_be32(&fecp->fec_mii_speed, fec->mii_speed);
+
+ return 0;
+}
+
+static int fs_enet_fec_mii_read(struct mii_bus *bus , int phy_id, int location)
+{
+ struct fec_info* fec = bus->priv;
+ fec_t *fecp = fec->fecp;
+ int i, ret = -1;
+
+ if ((in_be32(&fecp->fec_r_cntrl) & FEC_RCNTRL_MII_MODE) == 0)
+ BUG();
+
+ /* Add PHY address to register command. */
+ out_be32(&fecp->fec_mii_data, (phy_id << 23) | mk_mii_read(location));
+
+ for (i = 0; i < FEC_MII_LOOPS; i++)
+ if ((in_be32(&fecp->fec_ievent) & FEC_ENET_MII) != 0)
+ break;
+
+ if (i < FEC_MII_LOOPS) {
+ out_be32(&fecp->fec_ievent, FEC_ENET_MII);
+ ret = in_be32(&fecp->fec_mii_data) & 0xffff;
+ }
+
+ return ret;
+
+}
+
+static int fs_enet_fec_mii_write(struct mii_bus *bus, int phy_id, int location, u16 val)
+{
+ struct fec_info* fec = bus->priv;
+ fec_t *fecp = fec->fecp;
+ int i;
+
+ /* this must never happen */
+ if ((in_be32(&fecp->fec_r_cntrl) & FEC_RCNTRL_MII_MODE) == 0)
+ BUG();
+
+ /* Add PHY address to register command. */
+ out_be32(&fecp->fec_mii_data, (phy_id << 23) | mk_mii_write(location, val));
+
+ for (i = 0; i < FEC_MII_LOOPS; i++)
+ if ((in_be32(&fecp->fec_ievent) & FEC_ENET_MII) != 0)
+ break;
+
+ if (i < FEC_MII_LOOPS)
+ out_be32(&fecp->fec_ievent, FEC_ENET_MII);
+
+ return 0;
+
+}
+
+static int fs_enet_fec_mii_reset(struct mii_bus *bus)
+{
+ /* nothing here - for now */
+ return 0;
+}
+
+static int __devinit fs_enet_fec_mdio_probe(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct fs_mii_fec_platform_info *pdata;
+ struct mii_bus *new_bus;
+ struct fec_info *fec;
+ int err = 0;
+ if (NULL == dev)
+ return -EINVAL;
+ new_bus = kzalloc(sizeof(struct mii_bus), GFP_KERNEL);
+
+ if (NULL == new_bus)
+ return -ENOMEM;
+
+ fec = kzalloc(sizeof(struct fec_info), GFP_KERNEL);
+
+ if (NULL == fec)
+ return -ENOMEM;
+
+ new_bus->name = "FEC MII Bus",
+ new_bus->read = &fs_enet_fec_mii_read,
+ new_bus->write = &fs_enet_fec_mii_write,
+ new_bus->reset = &fs_enet_fec_mii_reset,
+ new_bus->id = pdev->id;
+
+ pdata = (struct fs_mii_fec_platform_info *)pdev->dev.platform_data;
+
+ if (NULL == pdata) {
+ printk(KERN_ERR "fs_enet FEC mdio %d: Missing platform data!\n", pdev->id);
+ return -ENODEV;
+ }
+
+ /*set up workspace*/
+
+ fs_mii_fec_init(fec, pdata);
+ new_bus->priv = fec;
+
+ new_bus->irq = pdata->irq;
+
+ new_bus->dev = dev;
+ dev_set_drvdata(dev, new_bus);
+
+ err = mdiobus_register(new_bus);
+
+ if (0 != err) {
+ printk (KERN_ERR "%s: Cannot register as MDIO bus\n",
+ new_bus->name);
+ goto bus_register_fail;
+ }
+
+ return 0;
+
+bus_register_fail:
+ kfree(new_bus);
+
+ return err;
+}
+
+
+static int fs_enet_fec_mdio_remove(struct device *dev)
+{
+ struct mii_bus *bus = dev_get_drvdata(dev);
+
+ mdiobus_unregister(bus);
+
+ dev_set_drvdata(dev, NULL);
+ kfree(bus->priv);
+
+ bus->priv = NULL;
+ kfree(bus);
+
+ return 0;
+}
+
+static struct device_driver fs_enet_fec_mdio_driver = {
+ .name = "fsl-cpm-fec-mdio",
+ .bus = &platform_bus_type,
+ .probe = fs_enet_fec_mdio_probe,
+ .remove = fs_enet_fec_mdio_remove,
+};
+
+int fs_enet_mdio_fec_init(void)
+{
+ return driver_register(&fs_enet_fec_mdio_driver);
+}
+
+void fs_enet_mdio_fec_exit(void)
+{
+ driver_unregister(&fs_enet_fec_mdio_driver);
+}
+
diff --git a/drivers/net/fs_enet/mii-fixed.c b/drivers/net/fs_enet/mii-fixed.c
deleted file mode 100644
index b3e192d..0000000
--- a/drivers/net/fs_enet/mii-fixed.c
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Combined Ethernet driver for Motorola MPC8xx and MPC82xx.
- *
- * Copyright (c) 2003 Intracom S.A.
- * by Pantelis Antoniou <panto@intracom.gr>
- *
- * 2005 (c) MontaVista Software, Inc.
- * Vitaly Bordug <vbordug@ru.mvista.com>
- *
- * This file is licensed under the terms of the GNU General Public License
- * version 2. This program is licensed "as is" without any warranty of any
- * kind, whether express or implied.
- */
-
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/string.h>
-#include <linux/ptrace.h>
-#include <linux/errno.h>
-#include <linux/ioport.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/spinlock.h>
-#include <linux/mii.h>
-#include <linux/ethtool.h>
-#include <linux/bitops.h>
-
-#include <asm/pgtable.h>
-#include <asm/irq.h>
-#include <asm/uaccess.h>
-
-#include "fs_enet.h"
-
-static const u16 mii_regs[7] = {
- 0x3100,
- 0x786d,
- 0x0fff,
- 0x0fff,
- 0x01e1,
- 0x45e1,
- 0x0003,
-};
-
-static int mii_read(struct fs_enet_mii_bus *bus, int phy_id, int location)
-{
- int ret = 0;
-
- if ((unsigned int)location >= ARRAY_SIZE(mii_regs))
- return -1;
-
- if (location != 5)
- ret = mii_regs[location];
- else
- ret = bus->fixed.lpa;
-
- return ret;
-}
-
-static void mii_write(struct fs_enet_mii_bus *bus, int phy_id, int location, int val)
-{
- /* do nothing */
-}
-
-int fs_mii_fixed_init(struct fs_enet_mii_bus *bus)
-{
- const struct fs_mii_bus_info *bi = bus->bus_info;
-
- bus->fixed.lpa = 0x45e1; /* default 100Mb, full duplex */
-
- /* if speed is fixed at 10Mb, remove 100Mb modes */
- if (bi->i.fixed.speed == 10)
- bus->fixed.lpa &= ~LPA_100;
-
- /* if duplex is half, remove full duplex modes */
- if (bi->i.fixed.duplex == 0)
- bus->fixed.lpa &= ~LPA_DUPLEX;
-
- bus->mii_read = mii_read;
- bus->mii_write = mii_write;
-
- return 0;
-}
diff --git a/include/asm-ppc/mpc8260.h b/include/asm-ppc/mpc8260.h
index 4b93481..23579d4 100644
--- a/include/asm-ppc/mpc8260.h
+++ b/include/asm-ppc/mpc8260.h
@@ -82,6 +82,7 @@ enum ppc_sys_devices {
MPC82xx_CPM_SMC2,
MPC82xx_CPM_USB,
MPC82xx_SEC1,
+ MPC82xx_MDIO_BB,
NUM_PPC_SYS_DEVS,
};
diff --git a/include/asm-ppc/mpc8xx.h b/include/asm-ppc/mpc8xx.h
index adcce33..d3a2f2f 100644
--- a/include/asm-ppc/mpc8xx.h
+++ b/include/asm-ppc/mpc8xx.h
@@ -110,6 +110,7 @@ enum ppc_sys_devices {
MPC8xx_CPM_SMC1,
MPC8xx_CPM_SMC2,
MPC8xx_CPM_USB,
+ MPC8xx_MDIO_FEC,
NUM_PPC_SYS_DEVS,
};
diff --git a/include/linux/fs_enet_pd.h b/include/linux/fs_enet_pd.h
index 783c476..74ed35a 100644
--- a/include/linux/fs_enet_pd.h
+++ b/include/linux/fs_enet_pd.h
@@ -69,34 +69,21 @@ enum fs_ioport {
fsiop_porte,
};
-struct fs_mii_bus_info {
- int method; /* mii method */
- int id; /* the id of the mii_bus */
- int disable_aneg; /* if the controller needs to negothiate speed & duplex */
- int lpa; /* the default board-specific vallues will be applied otherwise */
-
- union {
- struct {
- int duplex;
- int speed;
- } fixed;
-
- struct {
- /* nothing */
- } fec;
-
- struct {
- /* nothing */
- } scc;
-
- struct {
- int mdio_port; /* port & bit for MDIO */
- int mdio_bit;
- int mdc_port; /* port & bit for MDC */
- int mdc_bit;
- int delay; /* delay in us */
- } bitbang;
- } i;
+struct fs_mii_bit {
+ u32 offset;
+ u8 bit;
+ u8 polarity;
+};
+struct fs_mii_bb_platform_info {
+ struct fs_mii_bit mdio_dir;
+ struct fs_mii_bit mdio_dat;
+ struct fs_mii_bit mdc_dat;
+ int mdio_port; /* port & bit for MDIO */
+ int mdio_bit;
+ int mdc_port; /* port & bit for MDC */
+ int mdc_bit;
+ int delay; /* delay in us */
+ int irq[32]; /* irqs per phy's */
};
struct fs_platform_info {
@@ -119,6 +106,7 @@ struct fs_platform_info {
u32 device_flags;
int phy_addr; /* the phy address (-1 no phy) */
+ const char* bus_id;
int phy_irq; /* the phy irq (if it exists) */
const struct fs_mii_bus_info *bus_info;
@@ -130,6 +118,10 @@ struct fs_platform_info {
int napi_weight; /* NAPI weight */
int use_rmii; /* use RMII mode */
+ int has_phy; /* if the network is phy container as well...*/
+};
+struct fs_mii_fec_platform_info {
+ u32 irq[32];
+ u32 mii_speed;
};
-
#endif
^ permalink raw reply related
* Re: [PATCH v3 1/7] AMSO1100 Low Level Driver.
From: Steve Wise @ 2006-06-21 16:32 UTC (permalink / raw)
To: Arjan van de Ven; +Cc: rdreier, mshefty, linux-kernel, netdev, openib-general
In-Reply-To: <1150836226.2891.231.camel@laptopd505.fenrus.org>
On Tue, 2006-06-20 at 22:43 +0200, Arjan van de Ven wrote:
> On Tue, 2006-06-20 at 15:30 -0500, Steve Wise wrote:
>
> > +/*
> > + * Allocate TX ring elements and chain them together.
> > + * One-to-one association of adapter descriptors with ring elements.
> > + */
> > +static int c2_tx_ring_alloc(struct c2_ring *tx_ring, void *vaddr,
> > + dma_addr_t base, void __iomem * mmio_txp_ring)
> > +{
> > + struct c2_tx_desc *tx_desc;
> > + struct c2_txp_desc __iomem *txp_desc;
> > + struct c2_element *elem;
> > + int i;
> > +
> > + tx_ring->start = kmalloc(sizeof(*elem) * tx_ring->count, GFP_KERNEL);
>
> I would think this needs a dma_alloc_coherent() rather than a kmalloc...
>
No, this memory is used to describe the tx ring from the host's
perspective. The HW never touches this memory. The HW's TX descriptor
ring is in adapter memory and is mapped into host memory (see
c2dev->mmio_txp_ring).
>
> > +
> > +/* Free all buffers in RX ring, assumes receiver stopped */
> > +static void c2_rx_clean(struct c2_port *c2_port)
> > +{
> > + struct c2_dev *c2dev = c2_port->c2dev;
> > + struct c2_ring *rx_ring = &c2_port->rx_ring;
> > + struct c2_element *elem;
> > + struct c2_rx_desc *rx_desc;
> > +
> > + elem = rx_ring->start;
> > + do {
> > + rx_desc = elem->ht_desc;
> > + rx_desc->len = 0;
> > +
> > + __raw_writew(0, elem->hw_desc + C2_RXP_STATUS);
> > + __raw_writew(0, elem->hw_desc + C2_RXP_COUNT);
> > + __raw_writew(0, elem->hw_desc + C2_RXP_LEN);
>
> you seem to be a fan of the __raw_write() functions... any reason why?
> __raw_ is not a magic "go faster" prefix....
>
In this particular case, I believe this is done to avoid a swap of '0'
since its not necessary. In other places, __raw is used because the
adapter needs the data in BE and we want to explicitly swap it using
cpu_to_be* then raw_write it to the adapter memory...
> Also on a related note, have you checked the driver for the needed PCI
> posting flushes?
>
Um, what's a 'PCI posting flush'? Can you point me where its
described/used so I can see if we need it? Thanx.
> > +
> > + /* Disable IRQs by clearing the interrupt mask */
> > + writel(1, c2dev->regs + C2_IDIS);
> > + writel(0, c2dev->regs + C2_NIMR0);
>
> like here...
> > +
> > + elem = tx_ring->to_use;
> > + elem->skb = skb;
> > + elem->mapaddr = mapaddr;
> > + elem->maplen = maplen;
> > +
> > + /* Tell HW to xmit */
> > + __raw_writeq(cpu_to_be64(mapaddr), elem->hw_desc + C2_TXP_ADDR);
> > + __raw_writew(cpu_to_be16(maplen), elem->hw_desc + C2_TXP_LEN);
> > + __raw_writew(cpu_to_be16(TXP_HTXD_READY), elem->hw_desc + C2_TXP_FLAGS);
>
> or here
>
> > +static int c2_change_mtu(struct net_device *netdev, int new_mtu)
> > +{
> > + int ret = 0;
> > +
> > + if (new_mtu < ETH_ZLEN || new_mtu > ETH_JUMBO_MTU)
> > + return -EINVAL;
> > +
> > + netdev->mtu = new_mtu;
> > +
> > + if (netif_running(netdev)) {
> > + c2_down(netdev);
> > +
> > + c2_up(netdev);
> > + }
>
> this looks odd...
>
The 1100 hardware caches the dma address of the next skb that will be
used to place data. When the MTU changes, we want to free the SKBs in
the RX descriptor ring and get new ones that sufficient for the new MTU.
To effectively flush that cached address of the old skb, we must quiesce
the HW and firmware (via c2_down()), then reinitialize everything with
skb's big enough for the new mtu.
Steve.
^ permalink raw reply
* Re: [PATCH v3 1/7] AMSO1100 Low Level Driver.
From: Arjan van de Ven @ 2006-06-21 17:13 UTC (permalink / raw)
To: Steve Wise; +Cc: rdreier, mshefty, linux-kernel, netdev, openib-general
In-Reply-To: <1150907571.31600.31.camel@stevo-desktop>
> 0;
> > > +
> > > + __raw_writew(0, elem->hw_desc + C2_RXP_STATUS);
> > > + __raw_writew(0, elem->hw_desc + C2_RXP_COUNT);
> > > + __raw_writew(0, elem->hw_desc + C2_RXP_LEN);
> >
> > you seem to be a fan of the __raw_write() functions... any reason why?
> > __raw_ is not a magic "go faster" prefix....
> >
>
> In this particular case, I believe this is done to avoid a swap of '0'
> since its not necessary.
but.. that should writew() and co just autodetect (or do it at compile
time)...
(maybe it doesn't and we have an optimization opportunity here ;)
> > Also on a related note, have you checked the driver for the needed PCI
> > posting flushes?
> >
>
> Um, what's a 'PCI posting flush'? Can you point me where its
> described/used so I can see if we need it? Thanx.
ok pci posting...
basically, if you use writel() and co, the PCI bridges in the middle are
allowed (and the more fancy ones do) cache the write, to see if more
writes follow, so that the bridge can do the writes as a single burst to
the device, rather than as individual writes. This is of course great...
... except when you really want the write to hit the device before the
driver continues with other actions.
Now the PCI spec is set up such that any traffic in the other direction
(basically readl() and co) will first flush the write through the system
before the read is actually sent to the device, so doing a dummy readl()
is a good way to flush any pending posted writes.
Where does this matter?
it matters most at places such as irq enabling/disabling, IO submission
and possibly IRQ acking, but also often in eeprom-like read/write logic
(where you do manual clocking and need to do delays between the
write()'s). But in general... any place where you do writel() without
doing any readl() before doing nothing to the card for a long time, or
where you are waiting for the card to do something (or want it done NOW,
such as IRQ disabling) you need to issue a (dummy) readl() to flush
pending writes out to the hardware.
does this explanation make any sense? if not please feel free to ask any
questions, I know I'm not always very good at explaining things.
Greetings,
Arjan van de Ven
^ permalink raw reply
* Re: [openib-general] [PATCH v3 1/7] AMSO1100 Low Level Driver.
From: Grant Grundler @ 2006-06-21 17:37 UTC (permalink / raw)
To: Steve Wise
Cc: Arjan van de Ven, rdreier, linux-kernel, openib-general, netdev
In-Reply-To: <1150907571.31600.31.camel@stevo-desktop>
On Wed, Jun 21, 2006 at 11:32:51AM -0500, Steve Wise wrote:
> Um, what's a 'PCI posting flush'? Can you point me where its
> described/used so I can see if we need it? Thanx.
I've written this up before:
http://iou.parisc-linux.org/ols_2002/4Posted_vs_Non_Posted.html
grant
^ permalink raw reply
* [PATCH 0/2][RFC] Network Event Notifier Mechanism
From: Steve Wise @ 2006-06-21 18:45 UTC (permalink / raw)
To: netdev
This patch implements a mechanism that allows interested clients to
register for notification of certain network events. The intended use
is to allow RDMA devices (linux/drivers/infiniband) to be notified of
neighbour updates, ICMP redirects, path MTU changes, and route changes.
The reason these devices need update events is because they typically
cache this information in hardware and need to be notified when this
information has been updated.
This approach is one of many possibilities and may be preferred because it
uses an existing notification mechanism that has precedent in the stack.
An alternative would be to add a netdev method to notify affect devices
of these events.
This code does not yet implement path MTU change because the number of
places in which this value is updated is large and if this mechanism
seems reasonable, it would be probably be best to funnel these updates
through a single function.
We would like to get this or similar functionality included in 2.6.19
and request comments.
This patchset consists of 2 patches:
1) New files implementing the Network Event Notifier
2) Core network changes to generate network event notifications
Signed-off-by: Tom Tucker <tom@opengridcomputing.com>
Signed-off-by: Steve Wise <swise@opengridcomputing.com>
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox