sfc: Add support for SFC9000 family (2)

This integrates support for the SFC9000 family of 10G Ethernet
controllers and LAN-on-motherboard chips, starting with the SFL9021
'Siena' and SFC9020 'Bethpage'.

Credit for this code is largely due to my colleagues at Solarflare:

   Guido Barzini
   Steve Hodgson
   Kieran Mansley
   Matthew Slattery
   Neil Turton

Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
diff --git a/drivers/net/sfc/nic.c b/drivers/net/sfc/nic.c
index 55dbd79..5ac4b1a 100644
--- a/drivers/net/sfc/nic.c
+++ b/drivers/net/sfc/nic.c
@@ -997,6 +997,9 @@
 		case FSE_AZ_EV_CODE_DRIVER_EV:
 			efx_handle_driver_event(channel, &event);
 			break;
+		case FSE_CZ_EV_CODE_MCDI_EV:
+			efx_mcdi_process_event(channel, &event);
+			break;
 		default:
 			EFX_ERR(channel->efx, "channel %d unknown event type %d"
 				" (data " EFX_QWORD_FMT ")\n", channel->channel,
@@ -1025,13 +1028,21 @@
 
 void efx_nic_init_eventq(struct efx_channel *channel)
 {
-	efx_oword_t evq_ptr;
+	efx_oword_t reg;
 	struct efx_nic *efx = channel->efx;
 
 	EFX_LOG(efx, "channel %d event queue in special buffers %d-%d\n",
 		channel->channel, channel->eventq.index,
 		channel->eventq.index + channel->eventq.entries - 1);
 
+	if (efx_nic_rev(efx) >= EFX_REV_SIENA_A0) {
+		EFX_POPULATE_OWORD_3(reg,
+				     FRF_CZ_TIMER_Q_EN, 1,
+				     FRF_CZ_HOST_NOTIFY_MODE, 0,
+				     FRF_CZ_TIMER_MODE, FFE_CZ_TIMER_MODE_DIS);
+		efx_writeo_table(efx, &reg, FR_BZ_TIMER_TBL, channel->channel);
+	}
+
 	/* Pin event queue buffer */
 	efx_init_special_buffer(efx, &channel->eventq);
 
@@ -1039,11 +1050,11 @@
 	memset(channel->eventq.addr, 0xff, channel->eventq.len);
 
 	/* Push event queue to card */
-	EFX_POPULATE_OWORD_3(evq_ptr,
+	EFX_POPULATE_OWORD_3(reg,
 			     FRF_AZ_EVQ_EN, 1,
 			     FRF_AZ_EVQ_SIZE, __ffs(channel->eventq.entries),
 			     FRF_AZ_EVQ_BUF_BASE_ID, channel->eventq.index);
-	efx_writeo_table(efx, &evq_ptr, efx->type->evq_ptr_tbl_base,
+	efx_writeo_table(efx, &reg, efx->type->evq_ptr_tbl_base,
 			 channel->channel);
 
 	efx->type->push_irq_moderation(channel);
@@ -1051,13 +1062,15 @@
 
 void efx_nic_fini_eventq(struct efx_channel *channel)
 {
-	efx_oword_t eventq_ptr;
+	efx_oword_t reg;
 	struct efx_nic *efx = channel->efx;
 
 	/* Remove event queue from card */
-	EFX_ZERO_OWORD(eventq_ptr);
-	efx_writeo_table(efx, &eventq_ptr, efx->type->evq_ptr_tbl_base,
+	EFX_ZERO_OWORD(reg);
+	efx_writeo_table(efx, &reg, efx->type->evq_ptr_tbl_base,
 			 channel->channel);
+	if (efx_nic_rev(efx) >= EFX_REV_SIENA_A0)
+		efx_writeo_table(efx, &reg, FR_BZ_TIMER_TBL, channel->channel);
 
 	/* Unpin event queue */
 	efx_fini_special_buffer(efx, &channel->eventq);
@@ -1220,8 +1233,15 @@
 				      bool enabled, bool force)
 {
 	efx_oword_t int_en_reg_ker;
+	unsigned int level = 0;
 
-	EFX_POPULATE_OWORD_2(int_en_reg_ker,
+	if (EFX_WORKAROUND_17213(efx) && !EFX_INT_MODE_USE_MSI(efx))
+		/* Set the level always even if we're generating a test
+		 * interrupt, because our legacy interrupt handler is safe */
+		level = 0x1f;
+
+	EFX_POPULATE_OWORD_3(int_en_reg_ker,
+			     FRF_AZ_KER_INT_LEVE_SEL, level,
 			     FRF_AZ_KER_INT_KER, force,
 			     FRF_AZ_DRV_INT_EN_KER, enabled);
 	efx_writeo(efx, &int_en_reg_ker, FR_AZ_INT_EN_KER);
@@ -1334,15 +1354,30 @@
 	if (unlikely(syserr))
 		return efx_nic_fatal_interrupt(efx);
 
-	/* Schedule processing of any interrupting queues */
-	efx_for_each_channel(channel, efx) {
-		if ((queues & 1) ||
-		    efx_event_present(
-			    efx_event(channel, channel->eventq_read_ptr))) {
+	if (queues != 0) {
+		if (EFX_WORKAROUND_15783(efx))
+			efx->irq_zero_count = 0;
+
+		/* Schedule processing of any interrupting queues */
+		efx_for_each_channel(channel, efx) {
+			if (queues & 1)
 				efx_schedule_channel(channel);
-			result = IRQ_HANDLED;
+			queues >>= 1;
 		}
-		queues >>= 1;
+		result = IRQ_HANDLED;
+
+	} else if (EFX_WORKAROUND_15783(efx) &&
+		   efx->irq_zero_count++ == 0) {
+		efx_qword_t *event;
+
+		/* Ensure we rearm all event queues */
+		efx_for_each_channel(channel, efx) {
+			event = efx_event(channel, channel->eventq_read_ptr);
+			if (efx_event_present(event))
+				efx_schedule_channel(channel);
+		}
+
+		result = IRQ_HANDLED;
 	}
 
 	if (result == IRQ_HANDLED) {