From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752729AbbCYJxo (ORCPT ); Wed, 25 Mar 2015 05:53:44 -0400 Received: from www.linutronix.de ([62.245.132.108]:50579 "EHLO Galois.linutronix.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752515AbbCYJwt (ORCPT ); Wed, 25 Mar 2015 05:52:49 -0400 From: Holger Dengler To: linux-kernel@vger.kernel.org Cc: Peter Mahler , Juergen Bubeck , Benedikt Spranger , Holger Dengler , Samuel Ortiz , Lee Jones Subject: [PATCH 09/11] mfd: flexcard: add DMA ringbuffer demux driver Date: Wed, 25 Mar 2015 10:51:58 +0100 Message-Id: <1427277120-16924-10-git-send-email-dengler@linutronix.de> X-Mailer: git-send-email 2.1.4 In-Reply-To: <1427277120-16924-1-git-send-email-dengler@linutronix.de> References: <1427277120-16924-1-git-send-email-dengler@linutronix.de> 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,URIBL_BLOCKED=0.001 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Benedikt Spranger 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: Holger Dengler Signed-off-by: Benedikt Spranger cc: Samuel Ortiz cc: Lee Jones --- drivers/mfd/Kconfig | 9 ++ drivers/mfd/flexcard/Makefile | 3 + drivers/mfd/flexcard/dma.c | 286 ++++++++++++++++++++++++++++++++++++ drivers/mfd/flexcard/flexcard-dma.h | 207 ++++++++++++++++++++++++++ drivers/mfd/flexcard/parser.c | 193 ++++++++++++++++++++++++ include/linux/mfd/flexcard.h | 5 + 6 files changed, 703 insertions(+) create mode 100644 drivers/mfd/flexcard/dma.c create mode 100644 drivers/mfd/flexcard/flexcard-dma.h create mode 100644 drivers/mfd/flexcard/parser.c diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index f624b7f..cd798a6 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -228,6 +228,15 @@ config MFD_FLEXCARD can take up to 4 exchangeable physical layer boards for CAN, FlexRay or Ethernet. +config MFD_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 MFD_MC13XXX tristate depends on (SPI_MASTER || I2C) diff --git a/drivers/mfd/flexcard/Makefile b/drivers/mfd/flexcard/Makefile index 4f72c9c..695ba44 100644 --- a/drivers/mfd/flexcard/Makefile +++ b/drivers/mfd/flexcard/Makefile @@ -1,2 +1,5 @@ obj-$(CONFIG_MFD_FLEXCARD) += flexcard.o flexcard-objs := core.o attr.o irq.o + +obj-$(CONFIG_MFD_FLEXCARD_DMA) += flexcard-dma.o +flexcard-dma-objs := dma.o parser.o diff --git a/drivers/mfd/flexcard/dma.c b/drivers/mfd/flexcard/dma.c new file mode 100644 index 0000000..ed4e2ea --- /dev/null +++ b/drivers/mfd/flexcard/dma.c @@ -0,0 +1,286 @@ +/* + * Eberspaecher Flexcard PMC II Carrier Board PCI Driver - DMA controller + * + * Copyright (c) 2014,2015 Linutronix GmbH + * Author: Holger Dengler + * Benedikt Spranger + * + * 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 + +#include "flexcard-dma.h" + +static int flexcard_dma_stop(struct flexcard_dma *dma) +{ + void *dma_ctrl = &dma->reg->dma_ctrl; + void *dma_stat = &dma->reg->dma_stat; + int retry = 200; + + writel(FLEXCARD_DMA_CTRL_STOP_REQ, dma_ctrl); + + while (!(readl(dma_ctrl) & FLEXCARD_DMA_CTRL_DMA_IDLE) && retry--) + udelay(1); + if (!retry) + return -EBUSY; + + 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) +{ + void *dma_ctrl = &dma->reg->dma_ctrl; + int retry = 500; + + writel(FLEXCARD_DMA_CTRL_RST_DMA, dma_ctrl); + + 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) + goto out; + + 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); +out: + 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) { + u32 *p = dma->buf + 2*FLEXCARD_DMA_BUF_SIZE - 4; + *p = dma->rptr = rptr; + writel(rptr, &dma->reg->dma_rptr); + } else { + /* This should not happen. Or can it ? */ + dev_err(&pdev->dev, "rptr unchanged\n"); + } + + 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 = -ENOMEM; + + cell = mfd_get_cell(pdev); + if (!cell) + return -ENODEV; + + dma = devm_kzalloc(&pdev->dev, sizeof(*dma), GFP_KERNEL); + if (!dma) + goto out; + + platform_set_drvdata(pdev, dma); + + dma->buf = dma_alloc_coherent(&pdev->dev, 2*FLEXCARD_DMA_BUF_SIZE, + &dma->phys, GFP_KERNEL); + if (!dma->buf) { + dev_err(&pdev->dev, "could not allocate DMA memory\n"); + goto out; + } + + 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, 2*FLEXCARD_DMA_BUF_SIZE, + dma->buf, dma->phys); +out: + 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"); + goto out; + } + + dma_free_coherent(&pdev->dev, 2*FLEXCARD_DMA_BUF_SIZE, + dma->buf, dma->phys); + +out: + return ret; +} + +static struct platform_driver flexcard_dma_driver = { + .probe = flexcard_dma_probe, + .remove = flexcard_dma_remove, + .driver = { + .name = "flexcard-dma", + } +}; + +static int __init flexcard_dma_init(void) +{ + return platform_driver_register(&flexcard_dma_driver); +} + +static void __exit flexcard_dma_exit(void) +{ + platform_driver_unregister(&flexcard_dma_driver); +} + +module_init(flexcard_dma_init); +module_exit(flexcard_dma_exit); + +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/mfd/flexcard/flexcard-dma.h b/drivers/mfd/flexcard/flexcard-dma.h new file mode 100644 index 0000000..7de1b23 --- /dev/null +++ b/drivers/mfd/flexcard/flexcard-dma.h @@ -0,0 +1,207 @@ +#ifndef __FLEXCARD_H +#define __FLEXCARD_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 __iomem { + 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_H */ diff --git a/drivers/mfd/flexcard/parser.c b/drivers/mfd/flexcard/parser.c new file mode 100644 index 0000000..83a6e8e --- /dev/null +++ b/drivers/mfd/flexcard/parser.c @@ -0,0 +1,193 @@ +/* + * Eberspaecher Flexcard PMC II Carrier Board PCI Driver - packet parser/mux + * + * Copyright (c) 2014,2015 Linutronix GmbH + * Author: Holger Dengler + * Benedikt Spranger + * + * 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 "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; +}; + +int flexcard_register_rx_pkt(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_ATOMIC); + 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_pkt); + +void flexcard_unregister_rx_pkt(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; + } + } + + WARN_ON(!found); + + spin_unlock_irqrestore(&rx_cb_lock, flags); +} +EXPORT_SYMBOL_GPL(flexcard_unregister_rx_pkt); + +static int flexcard_send_pkt(int cc, void *buf, size_t len) +{ + struct fc_rx_cb *next; + int ret = -ENODEV; + + spin_lock(&rx_cb_lock); + list_for_each_entry(next, &rx_cb_list, list) + if (next->cc == cc) + ret = next->rx_cb(next->priv, buf, len); + spin_unlock(&rx_cb_lock); + + return ret; +} + +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; +} +EXPORT_SYMBOL_GPL(flexcard_get_packet_len); + +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 = pt->status_packet.cc; + + /* self sync status */ + if ((dma->nr_eray == 1) && (cc == 1)) + cc = 0; + 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 = pt->tx_ack_packet.cc; + + /* self sync tx ack */ + if ((dma->nr_eray == 1) && (cc == 1)) + cc = 0; + break; + case fc_packet_type_trigger: + default: + pr_debug("pkt->type = %08x\n", pb->header.type); + return 0; + } + + if (len > avail) + return 0; + + flexcard_send_pkt(cc, pb, len); + + return len; +} diff --git a/include/linux/mfd/flexcard.h b/include/linux/mfd/flexcard.h index f9b0962..964d78c 100644 --- a/include/linux/mfd/flexcard.h +++ b/include/linux/mfd/flexcard.h @@ -62,4 +62,9 @@ enum flexcard_cell_id { #define FLEXCARD_DMA_IRQ_TI 2 #define FLEXCARD_DMA_IRQ_CBL 3 +int flexcard_register_rx_pkt(int cc, void *priv, + int (*rx_cb)(void *priv, void *data, size_t len)); +void flexcard_unregister_rx_pkt(int cc); +u32 flexcard_get_packet_len(u32 header); + #endif /* FLEXCARD_H */ -- 2.1.4