From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753899AbcLNAOh (ORCPT ); Tue, 13 Dec 2016 19:14:37 -0500 Received: from Galois.linutronix.de ([146.0.238.70]:42905 "EHLO Galois.linutronix.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753455AbcLNANp (ORCPT ); Tue, 13 Dec 2016 19:13:45 -0500 From: Holger Dengler To: Lee Jones , Arnd Bergmann , Greg Kroah-Hartman , Vinod Koul Cc: linux-kernel@vger.kernel.org, dmaengine@vger.kernel.org, Thomas Gleixner , Sebastian Siewior , Juergen Bubeck , Peter Mahler , Holger Dengler , Benedikt Spranger Subject: [PATCH 12/12] dma: Flexcard DMA ringbuffer demux driver Date: Wed, 14 Dec 2016 01:11:53 +0100 Message-Id: <1481674313-30378-13-git-send-email-dengler@linutronix.de> X-Mailer: git-send-email 2.1.4 In-Reply-To: <1481674313-30378-1-git-send-email-dengler@linutronix.de> References: <1481674313-30378-1-git-send-email-dengler@linutronix.de> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Linutronix-Spam-Score: -1.0 X-Linutronix-Spam-Level: - X-Linutronix-Spam-Status: No , -1.0 points, 5.0 required, ALL_TRUSTED=-1,SHORTCIRCUIT=-0.0001 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org 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 Signed-off-by: Holger Dengler cc: Vinod Koul --- 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 + * Holger Dengler + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 "); +MODULE_AUTHOR("Benedikt Spranger "); +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 + * Holger Dengler + * + * 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 + * Holger Dengler + * + * 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 +#include +#include + +#include +#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