lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1481674313-30378-13-git-send-email-dengler@linutronix.de>
Date:   Wed, 14 Dec 2016 01:11:53 +0100
From:   Holger Dengler <dengler@...utronix.de>
To:     Lee Jones <lee.jones@...aro.org>, Arnd Bergmann <arnd@...db.de>,
        Greg Kroah-Hartman <gregkh@...uxfoundation.org>,
        Vinod Koul <vinod.koul@...el.com>
Cc:     linux-kernel@...r.kernel.org, dmaengine@...r.kernel.org,
        Thomas Gleixner <tglx@...utronix.de>,
        Sebastian Siewior <bigeasy@...utronix.de>,
        Juergen Bubeck <bubeck@...ug.com>,
        Peter Mahler <mahler@...ug.com>,
        Holger Dengler <dengler@...utronix.de>,
        Benedikt Spranger <b.spranger@...utronix.de>
Subject: [PATCH 12/12] dma: Flexcard DMA ringbuffer demux driver

The Flexcard interface design split packet receive and transmit. All
received packets and card status information are multiplexed with a
Flexcard specific protocol and handled through a DMA capable ringbuffer.
The TX path has to poke each available component separate.

Add a Flexcard DMA ringbuffer driver and packet demultiplexer.

Signed-off-by: Benedikt Spranger <b.spranger@...utronix.de>
Signed-off-by: Holger Dengler <dengler@...utronix.de>
cc: Vinod Koul <vinod.koul@...el.com>
---
 drivers/dma/Kconfig                 |   9 ++
 drivers/dma/Makefile                |   1 +
 drivers/dma/flexcard/Makefile       |   2 +
 drivers/dma/flexcard/core.c         | 292 ++++++++++++++++++++++++++++++++++++
 drivers/dma/flexcard/flexcard-dma.h | 218 +++++++++++++++++++++++++++
 drivers/dma/flexcard/parser.c       | 227 ++++++++++++++++++++++++++++
 drivers/mfd/Kconfig                 |   1 +
 include/linux/mfd/flexcard.h        |   4 +
 8 files changed, 754 insertions(+)
 create mode 100644 drivers/dma/flexcard/Makefile
 create mode 100644 drivers/dma/flexcard/core.c
 create mode 100644 drivers/dma/flexcard/flexcard-dma.h
 create mode 100644 drivers/dma/flexcard/parser.c

diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index 141aefb..b158544 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -40,6 +40,15 @@ config ASYNC_TX_ENABLE_CHANNEL_SWITCH
 config ARCH_HAS_ASYNC_TX_FIND_CHANNEL
 	bool
 
+config FLEXCARD_DMA
+	tristate "DMA support for Eberspaecher Flexcard PMC II Carrier Board"
+	depends on MFD_FLEXCARD
+	help
+	  The Eberspaecher Flexcard PMC (PCI Mezzanine Card) II carrier
+	  board support one DMA capable receive ringbuffer for all devices.
+	  A card specific protocol is used to multiplex the received packets
+	  through the ringbuffer. Enable DMA and Packet parser.
+
 config DMA_ENGINE
 	bool
 
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index e4dc9ca..a9a5b3f 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -27,6 +27,7 @@ obj-$(CONFIG_DMA_SUN4I) += sun4i-dma.o
 obj-$(CONFIG_DMA_SUN6I) += sun6i-dma.o
 obj-$(CONFIG_DW_DMAC_CORE) += dw/
 obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o
+obj-$(CONFIG_FLEXCARD_DMA) += flexcard/
 obj-$(CONFIG_FSL_DMA) += fsldma.o
 obj-$(CONFIG_FSL_EDMA) += fsl-edma.o
 obj-$(CONFIG_FSL_RAID) += fsl_raid.o
diff --git a/drivers/dma/flexcard/Makefile b/drivers/dma/flexcard/Makefile
new file mode 100644
index 0000000..62ae627
--- /dev/null
+++ b/drivers/dma/flexcard/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_FLEXCARD_DMA) += flexcard_dma.o
+flexcard_dma-objs := core.o parser.o
diff --git a/drivers/dma/flexcard/core.c b/drivers/dma/flexcard/core.c
new file mode 100644
index 0000000..6809840
--- /dev/null
+++ b/drivers/dma/flexcard/core.c
@@ -0,0 +1,292 @@
+/*
+ * Eberspächer Flexcard PMC II Carrier Board PCI Driver - DMA controller
+ *
+ * Copyright (c) 2014 - 2016, Linutronix GmbH
+ * Author: Benedikt Spranger <b.spranger@...utronix.de>
+ *         Holger Dengler <dengler@...utronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/flexcard.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/flexcard.h>
+
+#include "flexcard-dma.h"
+
+/*
+ * Allocate twice the size of FLEXCARD_DMA_BUF_SIZE for the receiving
+ * ring buffer to easily handle wrap-arounds.
+ */
+#define DMA_TOTAL_BUF_SIZE		(2*FLEXCARD_DMA_BUF_SIZE)
+
+static int flexcard_dma_stop(struct flexcard_dma *dma)
+{
+	u32 __iomem *dma_ctrl = &dma->reg->dma_ctrl;
+	u32 __iomem *dma_stat = &dma->reg->dma_stat;
+	int retry;
+
+	writel(FLEXCARD_DMA_CTRL_STOP_REQ, dma_ctrl);
+
+	/*
+	 * DMA_IDLE bit reads 1 when the DMA state machine is in idle state
+	 * after a stop request, otherwise 0. DMA stop should complete in at
+	 * least 200us.
+	 */
+	retry = 200;
+	while (!(readl(dma_ctrl) & FLEXCARD_DMA_CTRL_DMA_IDLE) && retry--)
+		udelay(1);
+	if (!retry)
+		return -EBUSY;
+
+	/*
+	 * Check for max. 200us, if there are DMA jobs in progress.
+	 */
+	retry = 200;
+	while ((readl(dma_stat) & FLEXCARD_DMA_STAT_BUSY) && retry--)
+		udelay(1);
+
+	return retry ? 0 : -EBUSY;
+}
+
+static int flexcard_dma_reset(struct flexcard_dma *dma)
+{
+	u32 __iomem *dma_ctrl = &dma->reg->dma_ctrl;
+	int retry = 500;
+
+	writel(FLEXCARD_DMA_CTRL_RST_DMA, dma_ctrl);
+
+	/*
+	 * DMA_IDLE bit reads 1 when the DMA state machine is in idle state
+	 * after a reset request, otherwise 0. DMA reset should complete in
+	 * at least 5ms.
+	 */
+	while (!(readl(dma_ctrl) & FLEXCARD_DMA_CTRL_DMA_IDLE) && retry--)
+		udelay(10);
+
+	return retry ? 0 : -EIO;
+}
+
+static int flexcard_dma_setup(struct flexcard_dma *dma)
+{
+	int ret;
+
+	ret = flexcard_dma_reset(dma);
+	if (ret)
+		return ret;
+
+	writel(0x0, &dma->reg->dma_rptr);
+	writel(0x0, &dma->reg->dma_wptr);
+	writel(0x0, &dma->reg->dma_ctrl);
+
+	writeq(dma->phys, &dma->reg->dma_cba);
+	writel(FLEXCARD_DMA_BUF_SIZE, &dma->reg->dma_cbs);
+
+	return ret;
+}
+
+static irqreturn_t flexcard_dma_isr(int irq, void *dev_id)
+{
+	struct platform_device *pdev = dev_id;
+	struct flexcard_dma *dma = platform_get_drvdata(pdev);
+	u32 avail, parsed, rptr = dma->rptr;
+
+	avail = readl(&dma->reg->dma_cblr);
+	if (!avail)
+		return IRQ_NONE;
+
+	do {
+		u32 tocp = rptr + FLEXCARD_MAX_PAKET_SIZE;
+		/*
+		 * For simplicity the parser always looks at contiguous
+		 * buffer space.
+		 *
+		 * We ensure that by copying the eventually wrapped
+		 * bytes of the next message from the bottom of the
+		 * dma buffer to the space right after the dma buffer
+		 * which has been allocated just for that reason.
+		 */
+		if (tocp > FLEXCARD_DMA_BUF_SIZE) {
+			tocp &= FLEXCARD_DMA_BUF_MASK;
+			memcpy(dma->buf + FLEXCARD_DMA_BUF_SIZE,
+			       dma->buf, tocp);
+		}
+
+		parsed = flexcard_parse_packet(dma->buf + rptr, avail, dma);
+		if (parsed > avail) {
+			dev_err(&pdev->dev, "Parser overrun\n");
+			rptr = (rptr + parsed) & FLEXCARD_DMA_BUF_MASK;
+			break;
+		}
+		avail -= parsed;
+		rptr = (rptr + parsed) & FLEXCARD_DMA_BUF_MASK;
+	} while (parsed && avail);
+
+	/* Update the read pointer in the device if we processed data */
+	if (dma->rptr != rptr) {
+		dma->rptr = rptr;
+		writel(rptr, &dma->reg->dma_rptr);
+	} else {
+		/* This may happen if no packets has been parsed */
+		dev_err_ratelimited(&pdev->dev, "rptr unchanged\n");
+		return IRQ_NONE;
+	}
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t flexcard_dma_ovr(int irq, void *dev_id)
+{
+	struct platform_device *pdev = dev_id;
+	struct flexcard_dma *dma = platform_get_drvdata(pdev);
+	u32 stat;
+
+	/* check overflow flag */
+	stat = readl(&dma->reg->dma_stat);
+	if (!(stat & FLEXCARD_DMA_STAT_OFL))
+		return IRQ_NONE;
+
+	dev_err(&pdev->dev, "DMA buffer overflow\n");
+
+	writel(0x0, &dma->reg->dma_rptr);
+
+	/* reset overflow flag */
+	writel(FLEXCARD_DMA_STAT_OFL, &dma->reg->dma_stat);
+
+	return IRQ_HANDLED;
+}
+
+static int flexcard_dma_resource(struct platform_device *pdev)
+{
+	struct flexcard_dma *dma = platform_get_drvdata(pdev);
+	struct resource *res;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENXIO;
+
+	dma->reg = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+	if (!dma->reg) {
+		dev_err(&pdev->dev, "failed to map DMA register\n");
+		return -ENOMEM;
+	}
+
+	dma->irq = platform_get_irq(pdev, 0);
+	if (dma->irq < 0) {
+		dev_err(&pdev->dev, "failed to get CBL IRQ\n");
+		return -ENXIO;
+	}
+
+	dma->irq_ovr = platform_get_irq(pdev, 1);
+	if (dma->irq_ovr < 0) {
+		dev_err(&pdev->dev, "failed to get CO IRQ\n");
+		return -ENXIO;
+	}
+	return 0;
+}
+
+static int flexcard_dma_probe(struct platform_device *pdev)
+{
+	const struct mfd_cell *cell;
+	struct flexcard_dma *dma;
+	int ret;
+
+	cell = mfd_get_cell(pdev);
+	if (!cell)
+		return -ENODEV;
+
+	dma = devm_kzalloc(&pdev->dev, sizeof(*dma), GFP_KERNEL);
+	if (!dma)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, dma);
+
+	dma->buf = dma_alloc_coherent(&pdev->dev, DMA_TOTAL_BUF_SIZE,
+				      &dma->phys, GFP_KERNEL);
+	if (!dma->buf) {
+		dev_err(&pdev->dev, "could not allocate DMA memory\n");
+		return -ENOMEM;
+	}
+
+	ret = flexcard_dma_resource(pdev);
+	if (ret)
+		goto out_free_buf;
+
+	ret = flexcard_dma_setup(dma);
+	if (ret) {
+		dev_err(&pdev->dev, "could not setup Flexcard DMA: %d\n", ret);
+		goto out_free_buf;
+	}
+
+	ret = devm_request_threaded_irq(&pdev->dev, dma->irq, NULL,
+					flexcard_dma_isr, IRQF_ONESHOT,
+					"flexcard-CBL", pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "could not request Flexcard DMA CBL IRQ\n");
+		goto out_free_buf;
+	}
+
+	ret = devm_request_irq(&pdev->dev, dma->irq_ovr, flexcard_dma_ovr, 0,
+			  "flexcard-CO", pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "could not request Flexcard DMA CO IRQ\n");
+		goto out_free_irq;
+	}
+
+	writel(FLEXCARD_DMA_CTRL_DMA_ENA, &dma->reg->dma_ctrl);
+	writel(0x300, &dma->reg->dma_cbcr);
+
+	dev_info(&pdev->dev, "Flexcard DMA registered");
+
+	return 0;
+
+out_free_irq:
+	writel(0x0, &dma->reg->dma_ctrl);
+out_free_buf:
+	dma_free_coherent(&pdev->dev, DMA_TOTAL_BUF_SIZE,
+			  dma->buf, dma->phys);
+	return ret;
+}
+
+static int flexcard_dma_remove(struct platform_device *pdev)
+{
+	struct flexcard_dma *dma = platform_get_drvdata(pdev);
+	int ret;
+
+	ret = flexcard_dma_stop(dma);
+	if (ret) {
+		dev_err(&pdev->dev, "could not stop DMA state machine\n");
+		return ret;
+	}
+
+	dma_free_coherent(&pdev->dev, DMA_TOTAL_BUF_SIZE,
+			  dma->buf, dma->phys);
+
+	return ret;
+}
+
+static struct platform_driver flexcard_dma_driver = {
+	.probe		= flexcard_dma_probe,
+	.remove		= flexcard_dma_remove,
+	.driver		= {
+		.name   = "flexcard-dma",
+	}
+};
+
+module_platform_driver(flexcard_dma_driver);
+
+MODULE_AUTHOR("Holger Dengler <dengler@...utronix.de>");
+MODULE_AUTHOR("Benedikt Spranger <b.spranger@...utronix.de>");
+MODULE_DESCRIPTION("Eberspaecher Flexcard PMC II DMA Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:flexcard-dma");
diff --git a/drivers/dma/flexcard/flexcard-dma.h b/drivers/dma/flexcard/flexcard-dma.h
new file mode 100644
index 0000000..6fc4ccf
--- /dev/null
+++ b/drivers/dma/flexcard/flexcard-dma.h
@@ -0,0 +1,218 @@
+/*
+ * Eberspächer Flexcard PMC II Carrier Board PCI Driver - DMA controller
+ *
+ * Copyright (c) 2014 - 2016, Linutronix GmbH
+ * Author: Benedikt Spranger <b.spranger@...utronix.de>
+ *         Holger Dengler <dengler@...utronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+#ifndef __FLEXCARD_DMA_H
+#define __FLEXCARD_DMA_H
+
+#define FLEXCARD_DMA_BUF_SIZE		0x200000
+#define FLEXCARD_DMA_BUF_MASK		(FLEXCARD_DMA_BUF_SIZE - 1)
+
+#define FLEXCARD_DMA_CTRL_DMA_ENA	(1 << 0)
+#define FLEXCARD_DMA_CTRL_MAN_ENA	(1 << 1)
+#define FLEXCARD_DMA_CTRL_STOP_REQ	(1 << 16)
+#define FLEXCARD_DMA_CTRL_DMA_IDLE	(1 << 17)
+#define FLEXCARD_DMA_CTRL_RST_DMA	(1 << 31)
+
+#define FLEXCARD_DMA_STAT_BUSY		(1 << 15)
+#define FLEXCARD_DMA_STAT_OFL		(1 << 31)
+
+#define FLEXCARD_MAX_PAKET_SIZE		0x200
+
+#define FLEXCARD_BUF_HEADER_LEN_SHIFT	15
+#define FLEXCARD_BUF_HEADER_LEN_MASK	0xfe
+
+#define FLEXCARD_CANIF_OFFSET		0x20
+
+struct flexcard_dma_reg {
+	u32 dma_ctrl;
+	u32 dma_stat;
+	u32 r1[2];
+	u64 dma_cba;
+	u32 dma_cbs;
+	u32 dma_txr;
+	u32 dma_irer;
+	u32 dma_irsr;
+	u32 r2[10];
+	u32 dma_cbcr;
+	u32 dma_cblr;
+	u32 r3[2];
+	u32 dma_itcr;
+	u32 dma_itr;
+	u32 r4[2];
+	u32 dma_wptr;
+	u32 dma_rptr;
+	u32 r5[2];
+} __packed;
+
+struct flexcard_dma {
+	int irq;
+	int irq_ovr;
+	u32 rptr;
+	void *buf;
+	dma_addr_t phys;
+	int nr_eray;
+	struct flexcard_dma_reg __iomem *reg;
+};
+
+enum fc_packet_type {
+	FC_PACKET_TYPE_INFO = 1,
+	FC_PACKET_TYPE_FLEXRAY_FRAME = 2,
+	FC_PACKET_TYPE_ERROR = 3,
+	FC_PACKET_TYPE_STATUS = 4,
+	FC_PACKET_TYPE_TRIGGER = 5,
+	FC_PACKET_TYPE_TX_ACK = 6,
+	FC_PACKET_TYPE_NMV_VECTOR = 7,
+	FC_PACKET_TYPE_NOTIFICATION = 8,
+	FC_PACKET_TYPE_TRIGGER_EX = 9,
+	FC_PACKET_TYPE_CAN = 10,
+	FC_PACKET_TYPE_CAN_ERROR = 11,
+};
+
+struct fc_packet {
+	__u32 type;
+	__u32 p_packet;
+	__u32 p_next_packet;
+} __packed;
+
+struct fc_info_packet {
+	__u32 current_cycle;
+	__u32 timestamp;
+	__u32 offset_rate_correction;
+	__u32 pta_ccf_count;
+	__u32 cc;
+} __packed;
+
+struct fc_flexray_frame {
+	__u32 header;
+	__u32 header_crc;
+	__u32 pdata;
+	__u32 channel;
+	__u32 frame_crc;
+	__u32 timestamp;
+	__u32 cc;
+} __packed;
+
+struct fc_error_packet {
+	__u32 flag;
+	__u32 timestamp;
+	__u32 cycle_count;
+	__u64 additional_info;
+	__u32 cc;
+	__u32 reserved;
+} __packed;
+
+struct fc_status_packet {
+	__u32 flag;
+	__u32 timestamp;
+	__u32 cycle_count;
+	__u32 additional_info;
+	__u32 cc;
+	__u32 reserved[2];
+} __packed;
+
+struct fc_tx_ack_packet {
+	__u32 bufferid;
+	__u32 timestamp;
+	__u32 cycle_count;
+	__u32 header;
+	__u32 header_crc;
+	__u32 pdata;
+	__u32 channel;
+	__u32 cc;
+} __packed;
+
+struct fc_nm_vector_packet {
+	__u32 timestamp;
+	__u32 cycle_count;
+	__u32 nmv_vector_length;
+	__u32 nmv_vector[3];
+	__u32 cc;
+	__u32 reserved;
+} __packed;
+
+struct fc_notification_packet {
+	__u32 timestamp;
+	__u32 sequence_count;
+	__u32 reserved;
+} __packed;
+
+struct fc_trigger_ex_info_packet {
+	__u32 condition;
+	__u32 timestamp;
+	__u32 sequence_count;
+	__u32 reserved1;
+	__u64 performance_counter;
+	__u32 edge;
+	__u32 trigger_line;
+	__u32 reserved[4];
+} __packed;
+
+struct fc_can_packet {
+	__u32 id;
+	__u32 timestamp;
+	__u32 flags;
+	__u32 reserved;
+	__u32 cc;
+	__u8 data[8];
+} __packed;
+
+struct fc_can_error_packet {
+	__u32 type;
+	__u32 state;
+	__u32 timestamp;
+	__u32 rx_error_counter;
+	__u32 tx_error_counter;
+	__u32 cc;
+	__u32 reserved[2];
+} __packed;
+
+enum fc_can_cc_state {
+	fc_can_state_unknown = 0,
+	fc_can_state_config,
+	fc_can_state_normalActive,
+	fc_can_state_warning,
+	fc_can_state_error_passive,
+	fc_can_state_bus_off,
+};
+
+enum fc_can_error_type {
+	fc_can_error_none = 0,
+	fc_can_error_stuff,
+	fc_can_error_form,
+	fc_can_error_acknowledge,
+	fc_can_error_bit1,
+	fc_can_error_bit0,
+	fc_can_error_crc,
+	fc_can_error_parity,
+};
+
+union fc_packet_types {
+	struct fc_info_packet		info_packet;
+	struct fc_flexray_frame		flexray_frame;
+	struct fc_error_packet		error_packet;
+	struct fc_status_packet		status_packet;
+	struct fc_tx_ack_packet		tx_ack_packet;
+	struct fc_nm_vector_packet	nm_vector_packet;
+	struct fc_notification_packet	notification_packet;
+	struct fc_trigger_ex_info_packet ex_info_packet;
+	struct fc_can_packet		can_packet;
+	struct fc_can_error_packet	can_error_packet;
+};
+
+struct fc_packet_buf {
+	struct  fc_packet	header;
+	union   fc_packet_types	packet;
+} __packed;
+
+u32 flexcard_parse_packet(struct fc_packet_buf *pb, u32 avail,
+			  struct flexcard_dma *dma);
+
+#endif /* __FLEXCARD_DMA_H */
diff --git a/drivers/dma/flexcard/parser.c b/drivers/dma/flexcard/parser.c
new file mode 100644
index 0000000..2450229
--- /dev/null
+++ b/drivers/dma/flexcard/parser.c
@@ -0,0 +1,227 @@
+/*
+ * Eberspächer Flexcard PMC II Carrier Board PCI Driver - packet parser/mux
+ *
+ * Copyright (c) 2014 - 2016, Linutronix GmbH
+ * Author: Benedikt Spranger <b.spranger@...utronix.de>
+ *         Holger Dengler <dengler@...utronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+#include <linux/mfd/flexcard.h>
+#include "flexcard-dma.h"
+
+static LIST_HEAD(rx_cb_list);
+static DEFINE_SPINLOCK(rx_cb_lock);
+
+struct fc_rx_cb {
+	struct list_head list;
+	int (*rx_cb)(void *priv, void *data, size_t len);
+	int cc;
+	void *priv;
+};
+
+/**
+ * flexcard_register_rx_cb() - Registers a callback for received packages
+ * @cc:		communication controller id
+ * @priv:	pointer to private data of the cc
+ * @rx_cp:	pionter to the receive callback
+ *
+ * Registers a callback for a communication controller specific handling for
+ * received packages. The callback is called by the generic parser, if the
+ * communication controller id inside of the received package matches the cc
+ * of the callback owner.
+ *
+ * Return: 0 is returned on success and a negative errno code for failure.
+ */
+int flexcard_register_rx_cb(int cc, void *priv,
+			    int (*rx_cb)(void *priv, void *data, size_t len))
+{
+	unsigned long flags;
+	struct fc_rx_cb *cb, *next;
+
+	if (!rx_cb)
+		return -EINVAL;
+
+	cb = kmalloc(sizeof(*cb), GFP_KERNEL);
+	if (!cb)
+		return -ENOMEM;
+
+	cb->cc = cc;
+	cb->priv = priv;
+	cb->rx_cb = rx_cb;
+
+	spin_lock_irqsave(&rx_cb_lock, flags);
+	list_for_each_entry(next, &rx_cb_list, list)
+		if (next->cc == cc)
+			goto out;
+
+	list_add_tail(&cb->list, &rx_cb_list);
+	spin_unlock_irqrestore(&rx_cb_lock, flags);
+
+	return 0;
+out:
+	spin_unlock_irqrestore(&rx_cb_lock, flags);
+	kfree(cb);
+
+	return -EBUSY;
+}
+EXPORT_SYMBOL_GPL(flexcard_register_rx_cb);
+
+/**
+ * flexcard_unregister_rx_cb() - Unregisters a callback for received packages
+ * @cc:		communication controller id
+ *
+ * Unregisters a callback for a communication controller specific handling for
+ * received packages.
+ *
+ * Return: 0 is returned on success and a negative errno code for failure.
+ */
+void flexcard_unregister_rx_cb(int cc)
+{
+	unsigned long flags;
+	struct fc_rx_cb *cur, *next;
+	int found = 0;
+
+	spin_lock_irqsave(&rx_cb_lock, flags);
+	list_for_each_entry_safe(cur, next, &rx_cb_list, list) {
+		if (cur->cc == cc) {
+			list_del(&cur->list);
+			kfree(cur);
+			found = 1;
+			break;
+		}
+	}
+
+	if (!found)
+		pr_err("no callback registered for cc %d\n", cc);
+
+	spin_unlock_irqrestore(&rx_cb_lock, flags);
+}
+EXPORT_SYMBOL_GPL(flexcard_unregister_rx_cb);
+
+static int flexcard_queue_rx(int cc, void *buf, size_t len)
+{
+	struct fc_rx_cb *next;
+	unsigned long flags;
+	int ret = -ENODEV;
+
+	spin_lock_irqsave(&rx_cb_lock, flags);
+	list_for_each_entry(next, &rx_cb_list, list)
+		if (next->cc == cc)
+			ret = next->rx_cb(next->priv, buf, len);
+	spin_unlock_irqrestore(&rx_cb_lock, flags);
+
+	return ret;
+}
+
+static u32 flexcard_get_packet_len(u32 header)
+{
+	u32 len;
+
+	/*
+	 * header contains the number of transmitted 16bit words in bits 30-16.
+	 * if the number is odd the DMA engine padded with zero to 32bit.
+	 * calculate the number of transmitted bytes.
+	 */
+
+	len = le32_to_cpu(header);
+
+	len >>= FLEXCARD_BUF_HEADER_LEN_SHIFT;
+	len &= FLEXCARD_BUF_HEADER_LEN_MASK;
+
+	len = roundup(len, 4);
+
+	return len;
+}
+
+/**
+ * selfsync_cc - adjust the cc number for self-sync packages
+ * @dma:	pointer to dma structure
+ * @cc:		package cc
+ *
+ * Some Flexcards has support for self-synci bus configurations. With this
+ * feature it is possible to get a synchronized bus configuration with a
+ * single card.
+ * Indication for a self-sync package is eray_nr == 1 and cc == 1. The
+ * packages are always handled by communication controller 0.
+ */
+static inline u32 selfsync_cc(struct flexcard_dma *dma, u32 cc)
+{
+		if ((dma->nr_eray == 1) && (cc == 1))
+			return 0;
+		return cc;
+}
+
+u32 flexcard_parse_packet(struct fc_packet_buf *pb, u32 avail,
+			  struct flexcard_dma *dma)
+{
+	u32 l, cc, len = sizeof(struct fc_packet);
+	union fc_packet_types *pt = &pb->packet;
+
+	switch (le32_to_cpu(pb->header.type)) {
+	case FC_PACKET_TYPE_INFO:
+		len += sizeof(struct fc_info_packet);
+		cc = pt->info_packet.cc;
+		break;
+	case FC_PACKET_TYPE_ERROR:
+		len += sizeof(struct fc_error_packet);
+		cc = pt->error_packet.cc;
+		break;
+	case FC_PACKET_TYPE_STATUS:
+		len += sizeof(struct fc_status_packet);
+		cc = selfsync_cc(dma, pt->status_packet.cc);
+		break;
+	case FC_PACKET_TYPE_NMV_VECTOR:
+		len += sizeof(struct fc_nm_vector_packet);
+		cc = pt->nm_vector_packet.cc;
+		break;
+	case FC_PACKET_TYPE_NOTIFICATION:
+		len += sizeof(struct fc_notification_packet);
+		cc = 0;
+		break;
+	case FC_PACKET_TYPE_TRIGGER_EX:
+		len += sizeof(struct fc_trigger_ex_info_packet);
+		cc = 0;
+		break;
+	case FC_PACKET_TYPE_CAN:
+		len += sizeof(struct fc_can_packet);
+		cc = FLEXCARD_CANIF_OFFSET + pt->can_packet.cc;
+		break;
+	case FC_PACKET_TYPE_CAN_ERROR:
+		len += sizeof(struct fc_can_error_packet);
+		cc = FLEXCARD_CANIF_OFFSET + pt->can_error_packet.cc;
+		break;
+	case FC_PACKET_TYPE_FLEXRAY_FRAME:
+		len += sizeof(struct fc_flexray_frame);
+		pt->flexray_frame.pdata = len;
+		l = flexcard_get_packet_len(pt->flexray_frame.header);
+		len += l;
+		cc = pt->flexray_frame.cc;
+		break;
+	case FC_PACKET_TYPE_TX_ACK:
+		len += sizeof(struct fc_tx_ack_packet);
+		pt->tx_ack_packet.pdata = len;
+		l = flexcard_get_packet_len(pt->tx_ack_packet.header);
+		len += l;
+		cc = selfsync_cc(dma, pt->tx_ack_packet.cc);
+		break;
+	case FC_PACKET_TYPE_TRIGGER:
+	default:
+		pr_debug("pkt->type = %08x\n", pb->header.type);
+		return 0;
+	}
+
+	if (len > avail)
+		return 0;
+
+	flexcard_queue_rx(cc, pb, len);
+
+	return len;
+}
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 490b435..cb87c27 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -303,6 +303,7 @@ config MFD_FLEXCARD
 	tristate "Eberspaecher Flexcard PMC II Carrier Board"
 	select MFD_CORE
 	select IRQ_DOMAIN
+	select FLEXCARD_DMA
 	select FLEXCARD_MISC
 	select FLEXCARD_PCLOCK
 	depends on PCI
diff --git a/include/linux/mfd/flexcard.h b/include/linux/mfd/flexcard.h
index 819c6ef..eeabc9e 100644
--- a/include/linux/mfd/flexcard.h
+++ b/include/linux/mfd/flexcard.h
@@ -109,4 +109,8 @@ struct flexcard_device {
 int flexcard_setup_irq(struct pci_dev *pdev);
 void flexcard_remove_irq(struct pci_dev *pdev);
 
+int flexcard_register_rx_cb(int cc, void *priv,
+			    int (*rx_cb)(void *priv, void *data, size_t len));
+void flexcard_unregister_rx_cb(int cc);
+
 #endif /* _LINUX_FLEXCARD_H */
-- 
2.1.4

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ