Thread (15 messages) 15 messages, 7 authors, 2024-06-28

Re: [PATCH v2 net-next 2/2] net: airoha: Introduce ethernet support for EN7581 SoC

From: "Arnd Bergmann" <arnd@arndb.de>
Date: 2024-06-28 19:34:32
Also in: linux-arm-kernel, linux-clk, linux-devicetree

On Fri, Jun 28, 2024, at 19:27, Lorenzo Bianconi wrote:
quoted
quoted
+static void airoha_qdma_set_irqmask(struct airoha_eth *eth, int index,
+				    u32 clear, u32 set)
+{
+	unsigned long flags;
+
+	if (WARN_ON_ONCE(index >= ARRAY_SIZE(eth->irqmask)))
+		return;
+
+	spin_lock_irqsave(&eth->irq_lock, flags);
+
+	eth->irqmask[index] &= ~clear;
+	eth->irqmask[index] |= set;
+	airoha_qdma_wr(eth, REG_INT_ENABLE(index), eth->irqmask[index]);
+
+	spin_unlock_irqrestore(&eth->irq_lock, flags);
+}
spin_lock_irqsave() is fairly expensive here, and it doesn't
actually protect the register write since that is posted
and can leak out of the spinlock.

You can probably just remove the lock and instead do the mask
with atomic_cmpxchg() here.
I did not get what you mean here. I guess the spin_lock is used to avoid
concurrent irq registers updates from user/bh context or irq handler.
Am I missing something?
What I meant is that the airoha_qdma_wr() doesn't complete
until after the unlock because this is a normal 'posted' ioremap()
mapping.
quoted
quoted
+static irqreturn_t airoha_irq_handler(int irq, void *dev_instance)
+{
+	struct airoha_eth *eth = dev_instance;
+	u32 intr[ARRAY_SIZE(eth->irqmask)];
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(eth->irqmask); i++) {
+		intr[i] = airoha_qdma_rr(eth, REG_INT_STATUS(i));
+		intr[i] &= eth->irqmask[i];
+		airoha_qdma_wr(eth, REG_INT_STATUS(i), intr[i]);
+	}
This looks like you send an interrupt Ack to each
interrupt in order to re-arm it, but then you disable
it again. Would it be possible to leave the interrupt enabled
but defer the Ack until the napi poll function is completed?
I guess doing so we are not using NAPIs as expected since they are
supposed to run with interrupt disabled. Agree?
The idea of NAPI is that you don't get the same interrupt
again until all remaining events have been processed.

How this is achieved is device dependent, and it can either
be done by masking the interrupt as you do here, or by
not rearming the interrupt if it gets turned off automatically
by the hardware. My guess is that writing to REG_INT_STATUS(i)
is the rearming here, but the device documentation should
clarify that. It's also possible that this is an Ack that
is required so you don't immediately get another interrupt.
quoted
quoted
+	if (!test_bit(DEV_STATE_INITIALIZED, &eth->state))
+		return IRQ_NONE;
+
+	if (intr[1] & RX_DONE_INT_MASK) {
+		airoha_qdma_irq_disable(eth, QDMA_INT_REG_IDX1,
+					RX_DONE_INT_MASK);
+		airoha_qdma_for_each_q_rx(eth, i) {
+			if (intr[1] & BIT(i))
+				napi_schedule(&eth->q_rx[i].napi);
+		}
+	}
Something seems wrong here, but that's probably just me
misunderstanding the design: if all queues are signaled
through the same interrupt handler, and you then do
napi_schedule() for each queue, I would expect them to
just all get run on the same CPU.

If you have separate queues, doesn't that mean you also need
separate irq numbers here so they can be distributed to the
available CPUs?
Actually I missed to mark the NAPI as threaded. Doing so, even if we have a
single irq line shared by all Rx queues, the scheduler can run the NAPIs in
parallel on different CPUs. I will fix it in v4.
Ok. It's a bit disappointing that the hardware integration
messed this up by not having multiple IRQ lines because
that adds a lot of latency, but I guess there is not much else
you can do about it.
quoted
quoted
b/drivers/net/ethernet/mediatek/airoha_eth.h
new file mode 100644
index 000000000000..fcd684e1418a
--- /dev/null
+++ b/drivers/net/ethernet/mediatek/airoha_eth.h
@@ -0,0 +1,793 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2024 Lorenzo Bianconi <lorenzo@kernel.org>
+ */
+
+#define AIROHA_MAX_NUM_RSTS		3
+#define AIROHA_MAX_NUM_XSI_RSTS		4
If your driver only has a single .c file, I would suggest moving all the
contents of the .h file into that as well for better readability.
I do not have a strong opinion about it but since we will extend the driver
in the future, keeping .c and .h in different files, seems a bit more tidy.
What do you think?
I would still keep it all in one file. If you extend it to multiple
files later, it's easy to move those parts that you actually need to
share into an interface header. Most likely the majority of the current contents won't be needed by other files.

      Arnd
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help