All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/4] can: rx-fifo for at91 only
@ 2015-09-03  8:57 Marc Kleine-Budde
  2015-09-03  8:57 ` [PATCH 1/4] can: headers: make header files self contained Marc Kleine-Budde
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Marc Kleine-Budde @ 2015-09-03  8:57 UTC (permalink / raw)
  To: guillaume; +Cc: linux-can, kernel

Hello Guillaume,

please try the following patches if they fix your problem on the at91. They are
available at my git repot:

https://git.kernel.org/cgit/linux/kernel/git/mkl/linux-can-next.git/log/?h=at91-next

regards,
Marc


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/4] can: headers: make header files self contained
  2015-09-03  8:57 [PATCH 0/4] can: rx-fifo for at91 only Marc Kleine-Budde
@ 2015-09-03  8:57 ` Marc Kleine-Budde
  2015-09-03  8:57 ` [PATCH 2/4] can: rx-fifo: Add support for simple irq offloading Marc Kleine-Budde
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Marc Kleine-Budde @ 2015-09-03  8:57 UTC (permalink / raw)
  To: guillaume; +Cc: linux-can, kernel, Marc Kleine-Budde

This patch adds the missing #include-s to the dev.h and led.h, so that they can
be used without including further header files.

Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
---
 include/linux/can/dev.h | 3 ++-
 include/linux/can/led.h | 1 +
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/include/linux/can/dev.h b/include/linux/can/dev.h
index c3a9c8fc60fa..56dcadd83716 100644
--- a/include/linux/can/dev.h
+++ b/include/linux/can/dev.h
@@ -14,9 +14,10 @@
 #define _CAN_DEV_H
 
 #include <linux/can.h>
-#include <linux/can/netlink.h>
 #include <linux/can/error.h>
 #include <linux/can/led.h>
+#include <linux/can/netlink.h>
+#include <linux/netdevice.h>
 
 /*
  * CAN mode
diff --git a/include/linux/can/led.h b/include/linux/can/led.h
index 146de4506d21..2746f7c2f87d 100644
--- a/include/linux/can/led.h
+++ b/include/linux/can/led.h
@@ -11,6 +11,7 @@
 
 #include <linux/if.h>
 #include <linux/leds.h>
+#include <linux/netdevice.h>
 
 enum can_led_event {
 	CAN_LED_EVENT_OPEN,
-- 
2.5.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 2/4] can: rx-fifo: Add support for simple irq offloading
  2015-09-03  8:57 [PATCH 0/4] can: rx-fifo for at91 only Marc Kleine-Budde
  2015-09-03  8:57 ` [PATCH 1/4] can: headers: make header files self contained Marc Kleine-Budde
@ 2015-09-03  8:57 ` Marc Kleine-Budde
  2015-09-03  8:57 ` [PATCH 3/4] can: rx-fifo: introduce software rx-fifo implementation Marc Kleine-Budde
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Marc Kleine-Budde @ 2015-09-03  8:57 UTC (permalink / raw)
  To: guillaume; +Cc: linux-can, kernel, David Jander, Marc Kleine-Budde

From: David Jander <david@protonic.nl>

Some CAN controllers have a usable FIFO already but can still benefit from
off-loading the CAN controller FIFO. The mailboxes of the FIFO  are copied to a
ring buffer during the interrupt and then transmitted in a NAPI context.

Signed-off-by: David Jander <david@protonic.nl>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
---
 drivers/net/can/Makefile    |   3 +-
 drivers/net/can/rx-fifo.c   | 174 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/can/rx-fifo.h |  58 +++++++++++++++
 3 files changed, 234 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/can/rx-fifo.c
 create mode 100644 include/linux/can/rx-fifo.h

diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile
index c533c62b0f5e..8b3f6e96e4ba 100644
--- a/drivers/net/can/Makefile
+++ b/drivers/net/can/Makefile
@@ -6,7 +6,8 @@ obj-$(CONFIG_CAN_VCAN)		+= vcan.o
 obj-$(CONFIG_CAN_SLCAN)		+= slcan.o
 
 obj-$(CONFIG_CAN_DEV)		+= can-dev.o
-can-dev-y			:= dev.o
+can-dev-y			+= dev.o
+can-dev-y			+= rx-fifo.o
 
 can-dev-$(CONFIG_CAN_LEDS)	+= led.o
 
diff --git a/drivers/net/can/rx-fifo.c b/drivers/net/can/rx-fifo.c
new file mode 100644
index 000000000000..7896a1034588
--- /dev/null
+++ b/drivers/net/can/rx-fifo.c
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2014 David Jander, Protonic Holland
+ * Copyright (C) 2014, 2015 Pengutronix, Marc Kleine-Budde <kernel@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/circ_buf.h>
+#include <linux/can/dev.h>
+#include <linux/can/rx-fifo.h>
+
+static int can_rx_fifo_napi_read_frame(struct can_rx_fifo *fifo, int index)
+{
+	struct net_device *dev = fifo->dev;
+	struct net_device_stats *stats = &dev->stats;
+	struct sk_buff *skb;
+	struct can_frame *cf;
+
+	skb = alloc_can_skb(dev, &cf);
+	if (unlikely(!skb)) {
+		stats->rx_dropped++;
+		return 0;
+	}
+
+	memcpy(cf, &fifo->ring[index], sizeof(*cf));
+	memset(&fifo->ring[index], 0x0, sizeof(*cf));
+
+	stats->rx_packets++;
+	stats->rx_bytes += cf->can_dlc;
+	netif_receive_skb(skb);
+
+	return 1;
+}
+
+static int can_rx_fifo_napi_poll(struct napi_struct *napi, int quota)
+{
+	struct can_rx_fifo *fifo = container_of(napi, struct can_rx_fifo, napi);
+	unsigned int tail;
+	int work_done = 0;
+
+	if (fifo->poll_pre_read && work_done < quota)
+		work_done += fifo->poll_pre_read(fifo);
+
+	/* handle mailboxes */
+	tail = fifo->ring_tail;
+	while ((CIRC_CNT(smp_load_acquire(&fifo->ring_head), tail, fifo->ring_size)) &&
+	       (work_done < quota)) {
+		work_done += can_rx_fifo_napi_read_frame(fifo, tail);
+		tail++;
+		tail &= fifo->ring_size -1;
+		smp_store_release(&fifo->ring_tail, tail);
+	}
+
+	if (fifo->poll_post_read && work_done < quota)
+		work_done += fifo->poll_post_read(fifo);
+
+	if (work_done < quota) {
+		unsigned int head;
+
+		napi_complete(napi);
+
+		/* Check if there was another interrupt */
+		head = smp_load_acquire(&fifo->ring_head);
+		if (((CIRC_CNT(head, tail, fifo->ring_size)) || fifo->poll_errors) &&
+		    napi_reschedule(&fifo->napi)) {
+			fifo->poll_errors = false;
+		}
+
+		if (fifo->poll_error_interrupts_enable)
+			fifo->poll_error_interrupts_enable(fifo);
+	}
+
+	can_led_event(fifo->dev, CAN_LED_EVENT_RX);
+
+	return work_done;
+}
+
+static unsigned int can_rx_fifo_offload_one(struct can_rx_fifo *fifo, unsigned int n)
+{
+	unsigned int head, tail;
+	unsigned int ret;
+
+	head = fifo->ring_head;
+	tail = ACCESS_ONCE(fifo->ring_tail);
+	if (CIRC_SPACE(head, tail, fifo->ring_size)) {
+		ret = fifo->mailbox_read(fifo, &fifo->ring[head], n);
+		if (ret) {
+			head++;
+			head &= fifo->ring_size - 1;
+			smp_store_release(&fifo->ring_head, head);
+		}
+	} else {
+		/* Circular buffer is fill, read to discard mailbox */
+		ret = fifo->mailbox_read(fifo, &fifo->overflow, n);
+		if (ret)
+			fifo->dev->stats.rx_dropped++;
+	}
+
+	return ret;
+}
+
+int can_rx_fifo_irq_offload_simple(struct can_rx_fifo *fifo)
+{
+	unsigned int received = 0;
+	unsigned int ret;
+
+	do {
+		ret = can_rx_fifo_offload_one(fifo, 0);
+		received += ret;
+	} while (ret);
+
+	if (received)
+		can_rx_fifo_schedule(fifo);
+
+	return received;
+}
+EXPORT_SYMBOL_GPL(can_rx_fifo_irq_offload_simple);
+
+static int can_rx_fifo_init_ring(struct net_device *dev,
+				 struct can_rx_fifo *fifo, unsigned int weight)
+{
+	fifo->dev = dev;
+
+	/* Make ring-buffer a sensible size that is a power of 2 */
+	fifo->ring_size = 2 << fls(weight);
+	fifo->ring = kzalloc(sizeof(struct can_frame) * fifo->ring_size,
+			     GFP_KERNEL);
+	if (!fifo->ring)
+		return -ENOMEM;
+
+	fifo->ring_head = fifo->ring_tail = 0;
+	netif_napi_add(dev, &fifo->napi, can_rx_fifo_napi_poll, weight);
+
+	return 0;
+}
+
+int can_rx_fifo_add_simple(struct net_device *dev, struct can_rx_fifo *fifo, unsigned int weight)
+{
+	if (!fifo->mailbox_read)
+		return -EINVAL;
+
+	return can_rx_fifo_init_ring(dev, fifo, weight);
+}
+EXPORT_SYMBOL_GPL(can_rx_fifo_add_simple);
+
+void can_rx_fifo_enable(struct can_rx_fifo *fifo)
+{
+	napi_enable(&fifo->napi);
+}
+EXPORT_SYMBOL_GPL(can_rx_fifo_enable);
+
+void can_rx_fifo_irq_error(struct can_rx_fifo *fifo)
+{
+	fifo->poll_errors = true;
+	can_rx_fifo_schedule(fifo);
+}
+EXPORT_SYMBOL_GPL(can_rx_fifo_irq_error);
+
+void can_rx_fifo_del(struct can_rx_fifo *fifo)
+{
+	netif_napi_del(&fifo->napi);
+	kfree(fifo->ring);
+}
+EXPORT_SYMBOL_GPL(can_rx_fifo_del);
diff --git a/include/linux/can/rx-fifo.h b/include/linux/can/rx-fifo.h
new file mode 100644
index 000000000000..fc157f23722c
--- /dev/null
+++ b/include/linux/can/rx-fifo.h
@@ -0,0 +1,58 @@
+/*
+ * linux/can/rx-fifo.h
+ *
+ * Copyright (c) 2014 David Jander, Protonic Holland
+ * Copyright (c) 2014, 2015 Pengutronix, Marc Kleine-Budde <kernel@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ *
+ * 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.
+ */
+
+#ifndef _CAN_RX_FIFO_H
+#define _CAN_RX_FIFO_H
+
+#include <linux/netdevice.h>
+#include <linux/can.h>
+
+struct can_rx_fifo {
+	struct net_device *dev;
+
+	void (*poll_error_interrupts_enable)(struct can_rx_fifo *fifo);
+	unsigned int (*mailbox_read)(struct can_rx_fifo *fifo, struct can_frame *cf, unsigned int mb);
+	unsigned int (*poll_pre_read)(struct can_rx_fifo *fifo);
+	unsigned int (*poll_post_read)(struct can_rx_fifo *fifo);
+
+	unsigned int ring_size;
+	unsigned int ring_head;
+	unsigned int ring_tail;
+
+	struct can_frame *ring;
+	struct can_frame overflow;
+	struct napi_struct napi;
+
+	bool poll_errors;
+};
+
+int can_rx_fifo_add_simple(struct net_device *dev, struct can_rx_fifo *fifo, unsigned int weight);
+int can_rx_fifo_irq_offload_simple(struct can_rx_fifo *fifo);
+void can_rx_fifo_irq_error(struct can_rx_fifo *fifo);
+void can_rx_fifo_del(struct can_rx_fifo *fifo);
+void can_rx_fifo_enable(struct can_rx_fifo *fifo);
+
+static inline void can_rx_fifo_schedule(struct can_rx_fifo *fifo)
+{
+	napi_schedule(&fifo->napi);
+}
+
+static inline void can_rx_fifo_disable(struct can_rx_fifo *fifo)
+{
+	napi_disable(&fifo->napi);
+}
+
+#endif /* !_CAN_RX_FIFO_H */
-- 
2.5.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 3/4] can: rx-fifo: introduce software rx-fifo implementation
  2015-09-03  8:57 [PATCH 0/4] can: rx-fifo for at91 only Marc Kleine-Budde
  2015-09-03  8:57 ` [PATCH 1/4] can: headers: make header files self contained Marc Kleine-Budde
  2015-09-03  8:57 ` [PATCH 2/4] can: rx-fifo: Add support for simple irq offloading Marc Kleine-Budde
@ 2015-09-03  8:57 ` Marc Kleine-Budde
  2015-09-03  8:57 ` [PATCH 4/4] can: at91_can: switch to " Marc Kleine-Budde
  2015-09-03 12:10 ` [PATCH 0/4] can: rx-fifo for at91 only Guillaume Picquet
  4 siblings, 0 replies; 6+ messages in thread
From: Marc Kleine-Budde @ 2015-09-03  8:57 UTC (permalink / raw)
  To: guillaume; +Cc: linux-can, kernel, Marc Kleine-Budde, David Jander

This patch adds a software rx-fifo implementation for CAN controllers which
offer only a number of mailboxes, where the incoming message is stored int othe
first free one. Both controllers which start with the lowest or highest free
mailbox are supported.

The mailboxes are copied to a ring buffer during the interrupt and then
transmitted in a NAPI context.

Signed-off-by: David Jander <david@protonic.nl>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
---
 drivers/net/can/rx-fifo.c   | 178 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/can/rx-fifo.h |  14 ++++
 2 files changed, 192 insertions(+)

diff --git a/drivers/net/can/rx-fifo.c b/drivers/net/can/rx-fifo.c
index 7896a1034588..53c01d3cd157 100644
--- a/drivers/net/can/rx-fifo.c
+++ b/drivers/net/can/rx-fifo.c
@@ -19,6 +19,50 @@
 #include <linux/can/dev.h>
 #include <linux/can/rx-fifo.h>
 
+static bool can_rx_fifo_ge(struct can_rx_fifo *fifo, unsigned int a, unsigned int b)
+{
+	if (fifo->inc)
+		return a >= b;
+	else
+		return a <= b;
+}
+
+static bool can_rx_fifo_le(struct can_rx_fifo *fifo, unsigned int a, unsigned int b)
+{
+	if (fifo->inc)
+		return a <= b;
+	else
+		return a >= b;
+}
+
+static unsigned int can_rx_fifo_inc(struct can_rx_fifo *fifo, unsigned int *val)
+{
+	if (fifo->inc)
+		return (*val)++;
+	else
+		return (*val)--;
+}
+
+static u64 can_rx_fifo_mask_low(struct can_rx_fifo *fifo)
+{
+	if (fifo->inc)
+		return ~0LLU >> (64 + fifo->low_first - fifo->high_first)
+			     << fifo->low_first;
+	else
+		return ~0LLU >> (64 - fifo->low_first + fifo->high_first)
+			     << (fifo->high_first + 1);
+}
+
+static u64 can_rx_fifo_mask_high(struct can_rx_fifo *fifo)
+{
+	if (fifo->inc)
+		return ~0LLU >> (64 + fifo->high_first - fifo->high_last - 1)
+			     << fifo->high_first;
+	else
+		return ~0LLU >> (64 - fifo->high_first + fifo->high_last - 1)
+			     << fifo->high_last;
+}
+
 static int can_rx_fifo_napi_read_frame(struct can_rx_fifo *fifo, int index)
 {
 	struct net_device *dev = fifo->dev;
@@ -109,6 +153,68 @@ static unsigned int can_rx_fifo_offload_one(struct can_rx_fifo *fifo, unsigned i
 	return ret;
 }
 
+int can_rx_fifo_irq_offload(struct can_rx_fifo *fifo)
+{
+	unsigned int i;
+	unsigned int ret;
+	unsigned int received = 0;
+
+	if (fifo->scan_high_first) {
+		fifo->scan_high_first = false;
+		for (i = fifo->high_first;
+		     can_rx_fifo_le(fifo, i, fifo->high_last);
+		     can_rx_fifo_inc(fifo, &i)) {
+			received += can_rx_fifo_offload_one(fifo, i);
+			fifo->active |= BIT_ULL(i);
+			fifo->mailbox_enable(fifo, i);
+		}
+	}
+
+	/* Copy and disable FULL MBs */
+	for (i = fifo->low_first;
+	     can_rx_fifo_le(fifo, i, fifo->high_last);
+	     can_rx_fifo_inc(fifo, &i)) {
+		if (!(fifo->active & BIT_ULL(i)))
+			continue;
+
+		ret = can_rx_fifo_offload_one(fifo, i);
+		if (!ret)
+			break;
+
+		received += ret;
+		fifo->active &= ~BIT_ULL(i);
+	}
+
+	if (can_rx_fifo_ge(fifo, i, fifo->high_first) && fifo->scan_high_first)
+		netdev_warn(fifo->dev, "%s: RX order cannot be guaranteed. (count=%d)\n",
+			    __func__, i);
+
+	/* No EMPTY MB in first half? */
+	if (can_rx_fifo_ge(fifo, i, fifo->high_first)) {
+		/* Re-enable all disabled MBs */
+		fifo->active = fifo->mask_low | fifo->mask_high;
+		fifo->mailbox_enable_mask(fifo, fifo->active);
+
+		/* Next time we need to check the second half first */
+		fifo->scan_high_first = true;
+	}
+
+	if (WARN(!received, "%s: No messages found, RX-FIFO out of sync?\n",
+		 __func__)) {
+		/* This should only happen if the CAN conroller was
+		 * reset, but can_rx_fifo_reset() was not
+		 * called. WARN() the user and try to recover. This
+		 * may fail and the system may hang though.
+		 */
+		can_rx_fifo_reset(fifo);
+	} else {
+		can_rx_fifo_schedule(fifo);
+	}
+
+	return received;
+}
+EXPORT_SYMBOL_GPL(can_rx_fifo_irq_offload);
+
 int can_rx_fifo_irq_offload_simple(struct can_rx_fifo *fifo)
 {
 	unsigned int received = 0;
@@ -139,11 +245,72 @@ static int can_rx_fifo_init_ring(struct net_device *dev,
 		return -ENOMEM;
 
 	fifo->ring_head = fifo->ring_tail = 0;
+	can_rx_fifo_reset(fifo);
 	netif_napi_add(dev, &fifo->napi, can_rx_fifo_napi_poll, weight);
 
 	return 0;
 }
 
+static void rx_fifo_enable_mask_default(struct can_rx_fifo *fifo, u64 mask)
+{
+	unsigned int i;
+
+	for (i = fifo->low_first;
+	     can_rx_fifo_le(fifo, i, fifo->high_last);
+	     can_rx_fifo_inc(fifo, &i)) {
+		if (mask & BIT_ULL(i))
+			fifo->mailbox_enable(fifo, i);
+	}
+}
+
+static void rx_fifo_enable_default(struct can_rx_fifo *fifo, unsigned int mb)
+{
+	fifo->mailbox_enable_mask(fifo, BIT_ULL(mb));
+}
+
+int can_rx_fifo_add(struct net_device *dev, struct can_rx_fifo *fifo)
+{
+	unsigned int weight;
+	int ret;
+
+	if ((fifo->low_first < fifo->high_first) &&
+	    (fifo->high_first < fifo->high_last)) {
+		fifo->inc = true;
+		weight = fifo->high_last - fifo->low_first;
+	} else if ((fifo->low_first > fifo->high_first) &&
+		   (fifo->high_first > fifo->high_last)) {
+		fifo->inc = false;
+		weight = fifo->low_first - fifo->high_last;
+	} else {
+		return -EINVAL;
+	}
+
+	if ((!fifo->mailbox_enable_mask && !fifo->mailbox_enable) ||
+	    !fifo->mailbox_read)
+		return -EINVAL;
+
+	if (!fifo->mailbox_enable_mask)
+		fifo->mailbox_enable_mask = rx_fifo_enable_mask_default;
+	if (!fifo->mailbox_enable)
+		fifo->mailbox_enable = rx_fifo_enable_default;
+
+	/* init variables */
+	fifo->mask_low = can_rx_fifo_mask_low(fifo);
+	fifo->mask_high = can_rx_fifo_mask_high(fifo);
+
+	ret = can_rx_fifo_init_ring(dev, fifo, weight);
+	if (ret)
+		return ret;
+
+	netdev_dbg(dev, "%s: low_first=%d, high_first=%d, high_last=%d\n", __func__,
+		   fifo->low_first, fifo->high_first, fifo->high_last);
+	netdev_dbg(dev, "%s: mask_low=0x%016llx mask_high=0x%016llx\n", __func__,
+		   fifo->mask_low, fifo->mask_high);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(can_rx_fifo_add);
+
 int can_rx_fifo_add_simple(struct net_device *dev, struct can_rx_fifo *fifo, unsigned int weight)
 {
 	if (!fifo->mailbox_read)
@@ -155,6 +322,9 @@ EXPORT_SYMBOL_GPL(can_rx_fifo_add_simple);
 
 void can_rx_fifo_enable(struct can_rx_fifo *fifo)
 {
+	can_rx_fifo_reset(fifo);
+	if (fifo->mailbox_enable_mask)
+		fifo->mailbox_enable_mask(fifo, fifo->active);
 	napi_enable(&fifo->napi);
 }
 EXPORT_SYMBOL_GPL(can_rx_fifo_enable);
@@ -172,3 +342,11 @@ void can_rx_fifo_del(struct can_rx_fifo *fifo)
 	kfree(fifo->ring);
 }
 EXPORT_SYMBOL_GPL(can_rx_fifo_del);
+
+void can_rx_fifo_reset(struct can_rx_fifo *fifo)
+{
+	fifo->scan_high_first = false;
+	fifo->poll_errors = false;
+	fifo->active = fifo->mask_low | fifo->mask_high;
+}
+EXPORT_SYMBOL_GPL(can_rx_fifo_reset);
diff --git a/include/linux/can/rx-fifo.h b/include/linux/can/rx-fifo.h
index fc157f23722c..cd1614118b33 100644
--- a/include/linux/can/rx-fifo.h
+++ b/include/linux/can/rx-fifo.h
@@ -23,25 +23,39 @@
 struct can_rx_fifo {
 	struct net_device *dev;
 
+	void (*mailbox_enable_mask)(struct can_rx_fifo *fifo, u64 mask);
+	void (*mailbox_enable)(struct can_rx_fifo *fifo, unsigned int mb);
 	void (*poll_error_interrupts_enable)(struct can_rx_fifo *fifo);
 	unsigned int (*mailbox_read)(struct can_rx_fifo *fifo, struct can_frame *cf, unsigned int mb);
 	unsigned int (*poll_pre_read)(struct can_rx_fifo *fifo);
 	unsigned int (*poll_post_read)(struct can_rx_fifo *fifo);
 
+	u64 mask_low;
+	u64 mask_high;
+	u64 active;
+
 	unsigned int ring_size;
 	unsigned int ring_head;
 	unsigned int ring_tail;
+	unsigned int low_first;
+	unsigned int high_first;
+	unsigned int high_last;
 
 	struct can_frame *ring;
 	struct can_frame overflow;
 	struct napi_struct napi;
 
+	bool inc;
+	bool scan_high_first;
 	bool poll_errors;
 };
 
+int can_rx_fifo_add(struct net_device *dev, struct can_rx_fifo *fifo);
 int can_rx_fifo_add_simple(struct net_device *dev, struct can_rx_fifo *fifo, unsigned int weight);
+int can_rx_fifo_irq_offload(struct can_rx_fifo *fifo);
 int can_rx_fifo_irq_offload_simple(struct can_rx_fifo *fifo);
 void can_rx_fifo_irq_error(struct can_rx_fifo *fifo);
+void can_rx_fifo_reset(struct can_rx_fifo *fifo);
 void can_rx_fifo_del(struct can_rx_fifo *fifo);
 void can_rx_fifo_enable(struct can_rx_fifo *fifo);
 
-- 
2.5.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 4/4] can: at91_can: switch to rx-fifo implementation
  2015-09-03  8:57 [PATCH 0/4] can: rx-fifo for at91 only Marc Kleine-Budde
                   ` (2 preceding siblings ...)
  2015-09-03  8:57 ` [PATCH 3/4] can: rx-fifo: introduce software rx-fifo implementation Marc Kleine-Budde
@ 2015-09-03  8:57 ` Marc Kleine-Budde
  2015-09-03 12:10 ` [PATCH 0/4] can: rx-fifo for at91 only Guillaume Picquet
  4 siblings, 0 replies; 6+ messages in thread
From: Marc Kleine-Budde @ 2015-09-03  8:57 UTC (permalink / raw)
  To: guillaume; +Cc: linux-can, kernel, Marc Kleine-Budde

Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
---
 drivers/net/can/at91_can.c | 284 ++++++++++-----------------------------------
 1 file changed, 64 insertions(+), 220 deletions(-)

diff --git a/drivers/net/can/at91_can.c b/drivers/net/can/at91_can.c
index 945c0955a967..890bf6195c31 100644
--- a/drivers/net/can/at91_can.c
+++ b/drivers/net/can/at91_can.c
@@ -2,7 +2,7 @@
  * at91_can.c - CAN network driver for AT91 SoC CAN controller
  *
  * (C) 2007 by Hans J. Koch <hjk@hansjkoch.de>
- * (C) 2008, 2009, 2010, 2011 by Marc Kleine-Budde <kernel@pengutronix.de>
+ * (C) 2008-2015 Pengutronix, Marc Kleine-Budde <kernel@pengutronix.de>
  *
  * This software may be distributed under the terms of the GNU General
  * Public License ("GPL") version 2 as distributed in the 'COPYING'
@@ -38,6 +38,7 @@
 #include <linux/can/dev.h>
 #include <linux/can/error.h>
 #include <linux/can/led.h>
+#include <linux/can/rx-fifo.h>
 
 #define AT91_MB_MASK(i)		((1 << (i)) - 1)
 
@@ -138,14 +139,13 @@ struct at91_devtype_data {
 
 struct at91_priv {
 	struct can_priv can;		/* must be the first member! */
-	struct napi_struct napi;
+	struct can_rx_fifo rx_fifo;
 
 	void __iomem *reg_base;
 
 	u32 reg_sr;
 	unsigned int tx_next;
 	unsigned int tx_echo;
-	unsigned int rx_next;
 	struct at91_devtype_data devtype_data;
 
 	struct clk *clk;
@@ -154,6 +154,11 @@ struct at91_priv {
 	canid_t mb0_id;
 };
 
+static inline struct at91_priv *rx_fifo_to_priv(struct can_rx_fifo *fifo)
+{
+	return container_of(fifo, struct at91_priv, rx_fifo);
+}
+
 static const struct at91_devtype_data at91_at91sam9263_data = {
 	.rx_first = 1,
 	.rx_split = 8,
@@ -201,27 +206,11 @@ static inline unsigned int get_mb_rx_last(const struct at91_priv *priv)
 	return priv->devtype_data.rx_last;
 }
 
-static inline unsigned int get_mb_rx_split(const struct at91_priv *priv)
-{
-	return priv->devtype_data.rx_split;
-}
-
 static inline unsigned int get_mb_rx_num(const struct at91_priv *priv)
 {
 	return get_mb_rx_last(priv) - get_mb_rx_first(priv) + 1;
 }
 
-static inline unsigned int get_mb_rx_low_last(const struct at91_priv *priv)
-{
-	return get_mb_rx_split(priv) - 1;
-}
-
-static inline unsigned int get_mb_rx_low_mask(const struct at91_priv *priv)
-{
-	return AT91_MB_MASK(get_mb_rx_split(priv)) &
-		~AT91_MB_MASK(get_mb_rx_first(priv));
-}
-
 static inline unsigned int get_mb_tx_shift(const struct at91_priv *priv)
 {
 	return priv->devtype_data.tx_shift;
@@ -367,9 +356,8 @@ static void at91_setup_mailboxes(struct net_device *dev)
 	for (i = get_mb_tx_first(priv); i <= get_mb_tx_last(priv); i++)
 		set_mb_mode_prio(priv, i, AT91_MB_MODE_TX, 0);
 
-	/* Reset tx and rx helper pointers */
+	/* Reset tx helper pointers */
 	priv->tx_next = priv->tx_echo = 0;
-	priv->rx_next = get_mb_rx_first(priv);
 }
 
 static int at91_set_bittiming(struct net_device *dev)
@@ -428,6 +416,7 @@ static void at91_chip_start(struct net_device *dev)
 	priv->can.state = CAN_STATE_ERROR_ACTIVE;
 
 	/* Enable interrupts */
+	can_rx_fifo_reset(&priv->rx_fifo);
 	reg_ier = get_irq_mb_rx(priv) | AT91_IRQ_ERRP | AT91_IRQ_ERR_FRAME;
 	at91_write(priv, AT91_IDR, AT91_IRQ_ALL);
 	at91_write(priv, AT91_IER, reg_ier);
@@ -532,32 +521,6 @@ static netdev_tx_t at91_start_xmit(struct sk_buff *skb, struct net_device *dev)
 }
 
 /**
- * at91_activate_rx_low - activate lower rx mailboxes
- * @priv: a91 context
- *
- * Reenables the lower mailboxes for reception of new CAN messages
- */
-static inline void at91_activate_rx_low(const struct at91_priv *priv)
-{
-	u32 mask = get_mb_rx_low_mask(priv);
-	at91_write(priv, AT91_TCR, mask);
-}
-
-/**
- * at91_activate_rx_mb - reactive single rx mailbox
- * @priv: a91 context
- * @mb: mailbox to reactivate
- *
- * Reenables given mailbox for reception of new CAN messages
- */
-static inline void at91_activate_rx_mb(const struct at91_priv *priv,
-		unsigned int mb)
-{
-	u32 mask = 1 << mb;
-	at91_write(priv, AT91_TCR, mask);
-}
-
-/**
  * at91_rx_overflow_err - send error frame due to rx overflow
  * @dev: net device
  */
@@ -580,33 +543,34 @@ static void at91_rx_overflow_err(struct net_device *dev)
 
 	stats->rx_packets++;
 	stats->rx_bytes += cf->can_dlc;
-	netif_receive_skb(skb);
+	netif_rx(skb);
 }
 
 /**
- * at91_read_mb - read CAN msg from mailbox (lowlevel impl)
- * @dev: net device
+ * at91_mailbox_read - read CAN msg from mailbox
+ * @fifo: rx-fifo
+ * @cf: CAN frame where to store message
  * @mb: mailbox number to read from
- * @cf: can frame where to store message
  *
  * Reads a CAN message from the given mailbox and stores data into
- * given can frame. "mb" and "cf" must be valid.
+ * given can frame. "cf" and "mb" must be valid.
  */
-static void at91_read_mb(struct net_device *dev, unsigned int mb,
-		struct can_frame *cf)
+static unsigned int at91_mailbox_read(struct can_rx_fifo *fifo, struct can_frame *cf, unsigned int mb)
 {
-	const struct at91_priv *priv = netdev_priv(dev);
+	const struct at91_priv *priv = rx_fifo_to_priv(fifo);
 	u32 reg_msr, reg_mid;
 
+	reg_msr = at91_read(priv, AT91_MSR(mb));
+	if (!(reg_msr & AT91_MSR_MRDY))
+		return 0;
+
 	reg_mid = at91_read(priv, AT91_MID(mb));
 	if (reg_mid & AT91_MID_MIDE)
 		cf->can_id = ((reg_mid >> 0) & CAN_EFF_MASK) | CAN_EFF_FLAG;
 	else
 		cf->can_id = (reg_mid >> 18) & CAN_SFF_MASK;
 
-	reg_msr = at91_read(priv, AT91_MSR(mb));
 	cf->can_dlc = get_can_dlc((reg_msr >> 16) & 0xf);
-
 	if (reg_msr & AT91_MSR_MRTR)
 		cf->can_id |= CAN_RTR_FLAG;
 	else {
@@ -618,127 +582,20 @@ static void at91_read_mb(struct net_device *dev, unsigned int mb,
 	at91_write(priv, AT91_MID(mb), AT91_MID_MIDE);
 
 	if (unlikely(mb == get_mb_rx_last(priv) && reg_msr & AT91_MSR_MMI))
-		at91_rx_overflow_err(dev);
-}
-
-/**
- * at91_read_msg - read CAN message from mailbox
- * @dev: net device
- * @mb: mail box to read from
- *
- * Reads a CAN message from given mailbox, and put into linux network
- * RX queue, does all housekeeping chores (stats, ...)
- */
-static void at91_read_msg(struct net_device *dev, unsigned int mb)
-{
-	struct net_device_stats *stats = &dev->stats;
-	struct can_frame *cf;
-	struct sk_buff *skb;
-
-	skb = alloc_can_skb(dev, &cf);
-	if (unlikely(!skb)) {
-		stats->rx_dropped++;
-		return;
-	}
-
-	at91_read_mb(dev, mb, cf);
+		at91_rx_overflow_err(fifo->dev);
 
-	stats->rx_packets++;
-	stats->rx_bytes += cf->can_dlc;
-	netif_receive_skb(skb);
+	/* disable MB for now */
+	at91_write(priv, AT91_IDR, 1 << mb);
 
-	can_led_event(dev, CAN_LED_EVENT_RX);
+	return 1;
 }
 
-/**
- * at91_poll_rx - read multiple CAN messages from mailboxes
- * @dev: net device
- * @quota: max number of pkgs we're allowed to receive
- *
- * Theory of Operation:
- *
- * About 3/4 of the mailboxes (get_mb_rx_first()...get_mb_rx_last())
- * on the chip are reserved for RX. We split them into 2 groups. The
- * lower group ranges from get_mb_rx_first() to get_mb_rx_low_last().
- *
- * Like it or not, but the chip always saves a received CAN message
- * into the first free mailbox it finds (starting with the
- * lowest). This makes it very difficult to read the messages in the
- * right order from the chip. This is how we work around that problem:
- *
- * The first message goes into mb nr. 1 and issues an interrupt. All
- * rx ints are disabled in the interrupt handler and a napi poll is
- * scheduled. We read the mailbox, but do _not_ reenable the mb (to
- * receive another message).
- *
- *    lower mbxs      upper
- *     ____^______    __^__
- *    /           \  /     \
- * +-+-+-+-+-+-+-+-++-+-+-+-+
- * | |x|x|x|x|x|x|x|| | | | |
- * +-+-+-+-+-+-+-+-++-+-+-+-+
- *  0 0 0 0 0 0  0 0 0 0 1 1  \ mail
- *  0 1 2 3 4 5  6 7 8 9 0 1  / box
- *  ^
- *  |
- *   \
- *     unused, due to chip bug
- *
- * The variable priv->rx_next points to the next mailbox to read a
- * message from. As long we're in the lower mailboxes we just read the
- * mailbox but not reenable it.
- *
- * With completion of the last of the lower mailboxes, we reenable the
- * whole first group, but continue to look for filled mailboxes in the
- * upper mailboxes. Imagine the second group like overflow mailboxes,
- * which takes CAN messages if the lower goup is full. While in the
- * upper group we reenable the mailbox right after reading it. Giving
- * the chip more room to store messages.
- *
- * After finishing we look again in the lower group if we've still
- * quota.
- *
- */
-static int at91_poll_rx(struct net_device *dev, int quota)
+static void at91_mailbox_enable_mask(struct can_rx_fifo *fifo, u64 mask)
 {
-	struct at91_priv *priv = netdev_priv(dev);
-	u32 reg_sr = at91_read(priv, AT91_SR);
-	const unsigned long *addr = (unsigned long *)&reg_sr;
-	unsigned int mb;
-	int received = 0;
-
-	if (priv->rx_next > get_mb_rx_low_last(priv) &&
-	    reg_sr & get_mb_rx_low_mask(priv))
-		netdev_info(dev,
-			"order of incoming frames cannot be guaranteed\n");
-
- again:
-	for (mb = find_next_bit(addr, get_mb_tx_first(priv), priv->rx_next);
-	     mb < get_mb_tx_first(priv) && quota > 0;
-	     reg_sr = at91_read(priv, AT91_SR),
-	     mb = find_next_bit(addr, get_mb_tx_first(priv), ++priv->rx_next)) {
-		at91_read_msg(dev, mb);
-
-		/* reactivate mailboxes */
-		if (mb == get_mb_rx_low_last(priv))
-			/* all lower mailboxed, if just finished it */
-			at91_activate_rx_low(priv);
-		else if (mb > get_mb_rx_low_last(priv))
-			/* only the mailbox we read */
-			at91_activate_rx_mb(priv, mb);
-
-		received++;
-		quota--;
-	}
-
-	/* upper group completed, look again in lower */
-	if (priv->rx_next > get_mb_rx_low_last(priv) &&
-	    quota > 0 && mb > get_mb_rx_last(priv)) {
-		priv->rx_next = get_mb_rx_first(priv);
-		goto again;
-	}
+	struct at91_priv *priv = rx_fifo_to_priv(fifo);
 
-	return received;
+	at91_write(priv, AT91_TCR, mask);
+	at91_write(priv, AT91_IER, mask);
 }
 
 static void at91_poll_err_frame(struct net_device *dev,
@@ -789,12 +646,16 @@ static void at91_poll_err_frame(struct net_device *dev,
 	}
 }
 
-static int at91_poll_err(struct net_device *dev, int quota, u32 reg_sr)
+static unsigned int at91_poll_err(struct can_rx_fifo *fifo)
 {
+	struct at91_priv *priv = rx_fifo_to_priv(fifo);
+	struct net_device *dev = fifo->dev;
 	struct sk_buff *skb;
 	struct can_frame *cf;
+	u32 reg_sr;
 
-	if (quota == 0)
+	reg_sr = priv->reg_sr;
+	if (!(reg_sr & AT91_IRQ_ERR_FRAME))
 		return 0;
 
 	skb = alloc_can_err_skb(dev, &cf);
@@ -802,6 +663,7 @@ static int at91_poll_err(struct net_device *dev, int quota, u32 reg_sr)
 		return 0;
 
 	at91_poll_err_frame(dev, cf, reg_sr);
+	priv->reg_sr = 0;
 
 	dev->stats.rx_packets++;
 	dev->stats.rx_bytes += cf->can_dlc;
@@ -810,34 +672,11 @@ static int at91_poll_err(struct net_device *dev, int quota, u32 reg_sr)
 	return 1;
 }
 
-static int at91_poll(struct napi_struct *napi, int quota)
+static void at91_poll_error_interrupts_enable(struct can_rx_fifo *fifo)
 {
-	struct net_device *dev = napi->dev;
-	const struct at91_priv *priv = netdev_priv(dev);
-	u32 reg_sr = at91_read(priv, AT91_SR);
-	int work_done = 0;
-
-	if (reg_sr & get_irq_mb_rx(priv))
-		work_done += at91_poll_rx(dev, quota - work_done);
-
-	/*
-	 * The error bits are clear on read,
-	 * so use saved value from irq handler.
-	 */
-	reg_sr |= priv->reg_sr;
-	if (reg_sr & AT91_IRQ_ERR_FRAME)
-		work_done += at91_poll_err(dev, quota - work_done, reg_sr);
-
-	if (work_done < quota) {
-		/* enable IRQs for frame errors and all mailboxes >= rx_next */
-		u32 reg_ier = AT91_IRQ_ERR_FRAME;
-		reg_ier |= get_irq_mb_rx(priv) & ~AT91_MB_MASK(priv->rx_next);
+	struct at91_priv *priv = rx_fifo_to_priv(fifo);
 
-		napi_complete(napi);
-		at91_write(priv, AT91_IER, reg_ier);
-	}
-
-	return work_done;
+	at91_write(priv, AT91_IER, AT91_IRQ_ERR_FRAME);
 }
 
 /*
@@ -958,7 +797,6 @@ static void at91_irq_err_state(struct net_device *dev,
 		break;
 	}
 
-
 	/* process state changes depending on the new state */
 	switch (new_state) {
 	case CAN_STATE_ERROR_ACTIVE:
@@ -1026,7 +864,6 @@ static int at91_get_state_by_bec(const struct net_device *dev,
 	return 0;
 }
 
-
 static void at91_irq_err(struct net_device *dev)
 {
 	struct at91_priv *priv = netdev_priv(dev);
@@ -1049,7 +886,7 @@ static void at91_irq_err(struct net_device *dev)
 		else if (likely(reg_sr & AT91_IRQ_ERRA))
 			new_state = CAN_STATE_ERROR_ACTIVE;
 		else {
-			netdev_err(dev, "BUG! hardware in undefined state\n");
+			netdev_err(dev, "BUG! hardware in undefined state (reg_sr=0x%08x)\n", reg_sr);
 			return;
 		}
 	} else {
@@ -1082,7 +919,6 @@ static irqreturn_t at91_irq(int irq, void *dev_id)
 {
 	struct net_device *dev = dev_id;
 	struct at91_priv *priv = netdev_priv(dev);
-	irqreturn_t handled = IRQ_NONE;
 	u32 reg_sr, reg_imr;
 
 	reg_sr = at91_read(priv, AT91_SR);
@@ -1091,30 +927,30 @@ static irqreturn_t at91_irq(int irq, void *dev_id)
 	/* Ignore masked interrupts */
 	reg_sr &= reg_imr;
 	if (!reg_sr)
-		goto exit;
-
-	handled = IRQ_HANDLED;
+		return IRQ_NONE;
 
-	/* Receive or error interrupt? -> napi */
-	if (reg_sr & (get_irq_mb_rx(priv) | AT91_IRQ_ERR_FRAME)) {
-		/*
-		 * The error bits are clear on read,
+	/* Error interrupt? -> rx-fifo */
+	if (reg_sr & AT91_IRQ_ERR_FRAME) {
+		/* The error bits are clear on read,
 		 * save for later use.
 		 */
 		priv->reg_sr = reg_sr;
-		at91_write(priv, AT91_IDR,
-			   get_irq_mb_rx(priv) | AT91_IRQ_ERR_FRAME);
-		napi_schedule(&priv->napi);
+
+		at91_write(priv, AT91_IDR, AT91_IRQ_ERR_FRAME);
+		can_rx_fifo_irq_error(&priv->rx_fifo);
 	}
 
+	/* Receive interrupt? */
+	if (reg_sr & (get_irq_mb_rx(priv)))
+		can_rx_fifo_irq_offload(&priv->rx_fifo);
+
 	/* Transmission complete interrupt */
 	if (reg_sr & get_irq_mb_tx(priv))
 		at91_irq_tx(dev, reg_sr);
 
 	at91_irq_err(dev);
 
- exit:
-	return handled;
+	return IRQ_HANDLED;
 }
 
 static int at91_open(struct net_device *dev)
@@ -1142,7 +978,7 @@ static int at91_open(struct net_device *dev)
 
 	/* start chip and queuing */
 	at91_chip_start(dev);
-	napi_enable(&priv->napi);
+	can_rx_fifo_enable(&priv->rx_fifo);
 	netif_start_queue(dev);
 
 	return 0;
@@ -1163,7 +999,7 @@ static int at91_close(struct net_device *dev)
 	struct at91_priv *priv = netdev_priv(dev);
 
 	netif_stop_queue(dev);
-	napi_disable(&priv->napi);
+	can_rx_fifo_disable(&priv->rx_fifo);
 	at91_chip_stop(dev, CAN_STATE_STOPPED);
 
 	free_irq(dev->irq, dev);
@@ -1354,8 +1190,16 @@ static int at91_can_probe(struct platform_device *pdev)
 	priv->clk = clk;
 	priv->pdata = dev_get_platdata(&pdev->dev);
 	priv->mb0_id = 0x7ff;
-
-	netif_napi_add(dev, &priv->napi, at91_poll, get_mb_rx_num(priv));
+	priv->rx_fifo.low_first = devtype_data->rx_first;
+	priv->rx_fifo.high_first = devtype_data->rx_split;
+	priv->rx_fifo.high_last = devtype_data->rx_last;
+	priv->rx_fifo.poll_post_read = at91_poll_err;
+	priv->rx_fifo.poll_error_interrupts_enable =
+		at91_poll_error_interrupts_enable;
+	priv->rx_fifo.mailbox_enable_mask = at91_mailbox_enable_mask;
+	priv->rx_fifo.mailbox_read = at91_mailbox_read;
+
+	can_rx_fifo_add(dev, &priv->rx_fifo);
 
 	if (at91_is_sam9263(priv))
 		dev->sysfs_groups[0] = &at91_sysfs_attr_group;
-- 
2.5.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH 0/4] can: rx-fifo for at91 only
  2015-09-03  8:57 [PATCH 0/4] can: rx-fifo for at91 only Marc Kleine-Budde
                   ` (3 preceding siblings ...)
  2015-09-03  8:57 ` [PATCH 4/4] can: at91_can: switch to " Marc Kleine-Budde
@ 2015-09-03 12:10 ` Guillaume Picquet
  4 siblings, 0 replies; 6+ messages in thread
From: Guillaume Picquet @ 2015-09-03 12:10 UTC (permalink / raw)
  To: Marc Kleine-Budde, guillaume; +Cc: linux-can, kernel

Hello Marc,

I don't build kernel/module but I'll ask our hardware provider to try 
your patches.

I'll keep you updated as soon as possible.

Thanks

regards.

Le 03/09/2015 10:57, Marc Kleine-Budde a écrit :
> Hello Guillaume,
>
> please try the following patches if they fix your problem on the at91. They are
> available at my git repot:
>
> https://git.kernel.org/cgit/linux/kernel/git/mkl/linux-can-next.git/log/?h=at91-next
>
> regards,
> Marc
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-can" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2015-09-03 12:10 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-09-03  8:57 [PATCH 0/4] can: rx-fifo for at91 only Marc Kleine-Budde
2015-09-03  8:57 ` [PATCH 1/4] can: headers: make header files self contained Marc Kleine-Budde
2015-09-03  8:57 ` [PATCH 2/4] can: rx-fifo: Add support for simple irq offloading Marc Kleine-Budde
2015-09-03  8:57 ` [PATCH 3/4] can: rx-fifo: introduce software rx-fifo implementation Marc Kleine-Budde
2015-09-03  8:57 ` [PATCH 4/4] can: at91_can: switch to " Marc Kleine-Budde
2015-09-03 12:10 ` [PATCH 0/4] can: rx-fifo for at91 only Guillaume Picquet

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.