From 04b4ceacef68611dc9c32807ad196e22ed225749 Mon Sep 17 00:00:00 2001
From: "Wesley W. Terpstra" <w.terpstra@gsi.de>
Date: Wed, 27 Apr 2016 20:50:50 +0200
Subject: [PATCH] wishbone: deliver MSIs

---
 pcie-wb/wishbone.c | 117 +++++++++++++++++++++++++++++++++++++++++++--
 pcie-wb/wishbone.h |   6 ++-
 2 files changed, 117 insertions(+), 6 deletions(-)

diff --git a/pcie-wb/wishbone.c b/pcie-wb/wishbone.c
index 177472c..aa6d85c 100644
--- a/pcie-wb/wishbone.c
+++ b/pcie-wb/wishbone.c
@@ -134,6 +134,85 @@ static void claim_msi(struct etherbone_master_context* context)
 	mutex_unlock(&wb->mutex);
 }
 
+/* Must be called with wb->mutex held */
+static void advance_msi(struct wishbone* wb)
+{
+	struct wishbone_request request;
+	struct etherbone_master_context *context;
+	uint8_t *wptr;
+	int index;
+
+	/* Don't process a second MSI while a previous is inflight */
+	if (wb->msi_pending) return;
+	
+retry:	
+	/* If nothing to do, stop */
+	if (wb->wops->request(wb, &request) == 0) return;
+	
+	/* The hardware should already have done this, but be safe */
+	request.addr &= wb->mask;
+	
+	/* If no MSI handler, handle it immediately */
+	index = request.addr / ((wb->mask/WISHBONE_MAX_MSI_OPEN)+1);
+	if (!wb->msi_map[index]) {
+		wb->wops->reply(wb, 1, ~(wb_data_t)0);
+		goto retry;
+	}
+	
+	/* Fill in the MSI data */
+	context = wb->msi_map[index];
+	wptr = &context->msi[0];
+	
+	wptr[0] = ETHERBONE_BCA;
+	wptr[1] = request.mask;
+	if (request.write) {
+		wptr[2] = 1;
+		wptr[3] = 0;
+		wptr += sizeof(wb_data_t);
+		eb_from_cpu(wptr, request.addr);
+		wptr += sizeof(wb_data_t);
+		eb_from_cpu(wptr, request.data);
+		wptr += sizeof(wb_data_t);
+	} else {
+		wptr[2] = 0;
+		wptr[3] = 1;
+		wptr += sizeof(wb_data_t);
+		eb_from_cpu(wptr, WBA_DATA);
+		wptr += sizeof(wb_data_t);
+		eb_from_cpu(wptr, request.addr);
+		wptr += sizeof(wb_data_t);
+	}
+	
+	wptr[0] = ETHERBONE_CYC | ETHERBONE_BCA | ETHERBONE_RCA;
+	wptr[1] = 0xf;
+	wptr[2] = 0;
+	wptr[3] = 1;
+	wptr += sizeof(wb_data_t);
+	
+	eb_from_cpu(wptr, WBA_ERR);
+	wptr += sizeof(wb_data_t);
+	eb_from_cpu(wptr, 4); /* low bits of error status register */
+	wptr += sizeof(wb_data_t);
+	
+	/* Mark the MSI pending */
+	context->msi_unread = wptr - &context->msi[0];
+	context->msi_pending = 1;
+	wb->msi_pending = 1;
+	
+	/* Wake-up any reader of the device */
+	wake_up_interruptible(&context->waitq);
+	kill_fasync(&context->fasync, SIGIO, POLL_IN);
+}
+
+static void respond_msi(struct wishbone* wb, int error)
+{
+	mutex_lock(&wb->mutex);
+	wb->wops->reply(wb, error, wb->msi_data);
+	wb->msi_pending = 0;
+	advance_msi(wb);
+	mutex_unlock(&wb->mutex);
+}
+
 static wb_data_t handle_read_cfg(struct etherbone_master_context* context, wb_addr_t addr)
 {
 	struct wishbone *wb = context->wishbone;
@@ -153,9 +232,28 @@ static wb_data_t handle_read_cfg(struct etherbone_master_context* context, wb_ad
 
 static void handle_write_cfg(struct etherbone_master_context* context, wb_addr_t addr, wb_data_t data)
 {
+	struct wishbone *wb = context->wishbone;
 	switch (addr) {
-	case 36: if (data == 1) claim_msi(context); break;
-	default: break;
+	case 36:
+		if (data == 1) {
+			claim_msi(context);
+		}
+		break;
+		
+	case WBA_DATA:
+		if (context->msi_pending) {
+			mutex_lock(&wb->mutex);
+			wb->msi_data = data;
+			mutex_unlock(&wb->mutex);
+		}
+		break;
+	
+	case WBA_ERR:
+		if (context->msi_pending) { 
+			context->msi_pending = 0;
+			respond_msi(wb, data&1);
+		}
+		break;
 	}
 }
 
@@ -293,8 +391,9 @@ static int char_master_open(struct inode *inode, struct file *filep)
 	context->sent = 0;
 	context->processed = 0;
 	context->received = 0;
-	context->msi_unread = 0;
 	context->msi_index = -1;
+	context->msi_unread = 0;
+	context->msi_pending = 0;
 	
 	filep->private_data = context;
 	
@@ -311,6 +410,12 @@ static int char_master_release(struct inode *inode, struct file *filep)
 		wb->wops->cycle(wb, 0);
 	}
 	
+	/* Finish any unhandled MSI */
+	if (context->msi_pending) {
+		context->msi_pending = 0;
+		respond_msi(wb, 1);
+	}
+	
 	/* Unhook any MSI access */
 	if (context->msi_index != -1) {
 		mutex_lock(&wb->mutex);
@@ -457,6 +562,7 @@ int wishbone_register(struct wishbone* wb)
 	for (i = 0; i < WISHBONE_MAX_MSI_OPEN; ++i) {
 		wb->msi_map[i] = 0;
 	}
+	wb->msi_pending = 0;
 	
 	mutex_lock(&wishbone_mutex);
 	
@@ -530,8 +636,9 @@ int wishbone_unregister(struct wishbone* wb)
 
 void wishbone_slave_ready(struct wishbone* wb)
 {
-	// wake_up_interruptible(&wb->waitq);
-	// kill_fasync(&wb->fasync, SIGIO, POLL_IN);
+	mutex_lock(&wb->mutex);
+	advance_msi(wb);
+	mutex_unlock(&wb->mutex);
 }
 
 static int __init wishbone_init(void)
diff --git a/pcie-wb/wishbone.h b/pcie-wb/wishbone.h
index f54687b..8b41c13 100644
--- a/pcie-wb/wishbone.h
+++ b/pcie-wb/wishbone.h
@@ -67,6 +67,8 @@ struct wishbone
 	/* internal (EB-MSI mapping for this hardware) */
 	struct mutex mutex;
 	struct etherbone_master_context *msi_map[WISHBONE_MAX_MSI_OPEN];
+	wb_data_t msi_data;
+	int msi_pending;
 };
 
 #define RING_SIZE	8192
@@ -86,10 +88,12 @@ struct etherbone_master_context
 	
 	unsigned char buf[RING_SIZE]; /* Ring buffer */
 	
+	/* MSI resource ownership; -1 = nothing */
+	int msi_index;
 	/* MSI record data */
 	unsigned char msi[sizeof(wb_data_t)*6];
 	int msi_unread;
-	int msi_index;
+	int msi_pending;
 };
 
 #define RING_READ_LEN(ctx)   RING_POS((ctx)->processed - (ctx)->sent)
-- 
GitLab