get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/34483/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 34483,
    "url": "http://patchwork.ozlabs.org/api/patches/34483/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/linuxppc-dev/patch/20090929204248.16225.22818.stgit@riker/",
    "project": {
        "id": 2,
        "url": "http://patchwork.ozlabs.org/api/projects/2/?format=api",
        "name": "Linux PPC development",
        "link_name": "linuxppc-dev",
        "list_id": "linuxppc-dev.lists.ozlabs.org",
        "list_email": "linuxppc-dev@lists.ozlabs.org",
        "web_url": "https://github.com/linuxppc/wiki/wiki",
        "scm_url": "https://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux.git",
        "webscm_url": "https://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux.git/",
        "list_archive_url": "https://lore.kernel.org/linuxppc-dev/",
        "list_archive_url_format": "https://lore.kernel.org/linuxppc-dev/{}/",
        "commit_url_format": "https://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux.git/commit/?id={}"
    },
    "msgid": "<20090929204248.16225.22818.stgit@riker>",
    "list_archive_url": "https://lore.kernel.org/linuxppc-dev/20090929204248.16225.22818.stgit@riker/",
    "date": "2009-09-29T20:43:42",
    "name": "powerpc/5200: add LocalPlus bus FIFO device driver",
    "commit_ref": "3c9059d79f5eea6b8b75ddac97693127c3c41db4",
    "pull_url": null,
    "state": "accepted",
    "archived": true,
    "hash": "9241f9bf705a1475f4103271b8cc5e20dcd0ef39",
    "submitter": {
        "id": 2941,
        "url": "http://patchwork.ozlabs.org/api/people/2941/?format=api",
        "name": "John Bonesio",
        "email": "bones@secretlab.ca"
    },
    "delegate": {
        "id": 8,
        "url": "http://patchwork.ozlabs.org/api/users/8/?format=api",
        "username": "glikely",
        "first_name": "Grant",
        "last_name": "Likely",
        "email": "grant.likely@secretlab.ca"
    },
    "mbox": "http://patchwork.ozlabs.org/project/linuxppc-dev/patch/20090929204248.16225.22818.stgit@riker/mbox/",
    "series": [],
    "comments": "http://patchwork.ozlabs.org/api/patches/34483/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/34483/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<linuxppc-dev-bounces+patchwork-incoming=ozlabs.org@lists.ozlabs.org>",
        "X-Original-To": "patchwork-incoming@ozlabs.org",
        "Delivered-To": [
            "patchwork-incoming@ozlabs.org",
            "linuxppc-dev@ozlabs.org"
        ],
        "Received": [
            "from bilbo.ozlabs.org (localhost [127.0.0.1])\n\tby ozlabs.org (Postfix) with ESMTP id 5ECAF100859\n\tfor <patchwork-incoming@ozlabs.org>;\n\tWed, 30 Sep 2009 07:53:01 +1000 (EST)",
            "by ozlabs.org (Postfix)\n\tid 7791CB7C16; Wed, 30 Sep 2009 06:43:54 +1000 (EST)",
            "from mail-fx0-f208.google.com (mail-fx0-f208.google.com\n\t[209.85.220.208]) by ozlabs.org (Postfix) with ESMTP id F176CB7C07\n\tfor <linuxppc-dev@ozlabs.org>; Wed, 30 Sep 2009 06:43:52 +1000 (EST)",
            "by fxm4 with SMTP id 4so1351023fxm.40\n\tfor <linuxppc-dev@ozlabs.org>; Tue, 29 Sep 2009 13:43:50 -0700 (PDT)",
            "by 10.86.231.13 with SMTP id d13mr108783fgh.41.1254257030450;\n\tTue, 29 Sep 2009 13:43:50 -0700 (PDT)",
            "from ?127.0.1.1? (c-76-114-26-60.hsd1.ca.comcast.net\n\t[76.114.26.60])\n\tby mx.google.com with ESMTPS id d4sm93209fga.17.2009.09.29.13.43.47\n\t(version=TLSv1/SSLv3 cipher=RC4-MD5);\n\tTue, 29 Sep 2009 13:43:48 -0700 (PDT)"
        ],
        "From": "John Bonesio <bones@secretlab.ca>",
        "Subject": "[PATCH] powerpc/5200: add LocalPlus bus FIFO device driver",
        "To": "linuxppc-dev@ozlabs.org",
        "Date": "Tue, 29 Sep 2009 13:43:42 -0700",
        "Message-ID": "<20090929204248.16225.22818.stgit@riker>",
        "User-Agent": "StGIT/0.14.2",
        "MIME-Version": "1.0",
        "X-Mailman-Approved-At": "Wed, 30 Sep 2009 07:52:53 +1000",
        "X-BeenThere": "linuxppc-dev@lists.ozlabs.org",
        "X-Mailman-Version": "2.1.12",
        "Precedence": "list",
        "List-Id": "Linux on PowerPC Developers Mail List\n\t<linuxppc-dev.lists.ozlabs.org>",
        "List-Unsubscribe": "<https://lists.ozlabs.org/options/linuxppc-dev>,\n\t<mailto:linuxppc-dev-request@lists.ozlabs.org?subject=unsubscribe>",
        "List-Archive": "<http://lists.ozlabs.org/pipermail/linuxppc-dev>",
        "List-Post": "<mailto:linuxppc-dev@lists.ozlabs.org>",
        "List-Help": "<mailto:linuxppc-dev-request@lists.ozlabs.org?subject=help>",
        "List-Subscribe": "<https://lists.ozlabs.org/listinfo/linuxppc-dev>,\n\t<mailto:linuxppc-dev-request@lists.ozlabs.org?subject=subscribe>",
        "Content-Type": "text/plain; charset=\"us-ascii\"",
        "Content-Transfer-Encoding": "7bit",
        "Sender": "linuxppc-dev-bounces+patchwork-incoming=ozlabs.org@lists.ozlabs.org",
        "Errors-To": "linuxppc-dev-bounces+patchwork-incoming=ozlabs.org@lists.ozlabs.org"
    },
    "content": "This is a driver for the FIFO device on the LocalPlus bus on an mpc5200 system.\nThe driver supports programmed I/O through the FIFO as well as setting up DMA\nvia the BestComm engine through the FIFO.\n\nSigned-off-by: Grant Likely <grant.likely@secretlab.ca>\nSigned-off-by: John Bonesio <bones@secretlab.ca>\n---\nThis driver was originally written by Grant Likely. I have updated it so that\ntransmitting data (as opposed to receiving data) works better. The driver has\nbeen tested for all 6 transfer modes:\n        PIO mode (rx and tx)\n        DMA polled mode (rx and tx)\n        DMA interrupt mode (rx and tx)\n\n- John\n\n arch/powerpc/include/asm/mpc52xx.h            |   39 ++\n arch/powerpc/platforms/52xx/Kconfig           |    5 \n arch/powerpc/platforms/52xx/Makefile          |    1 \n arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c |  560 +++++++++++++++++++++++++\n 4 files changed, 605 insertions(+), 0 deletions(-)\n create mode 100644 arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c",
    "diff": "diff --git a/arch/powerpc/include/asm/mpc52xx.h b/arch/powerpc/include/asm/mpc52xx.h\nindex 8273357..819a0be 100644\n--- a/arch/powerpc/include/asm/mpc52xx.h\n+++ b/arch/powerpc/include/asm/mpc52xx.h\n@@ -282,6 +282,45 @@ extern int mpc52xx_gpt_start_timer(struct mpc52xx_gpt_priv *gpt, int period,\n                             int continuous);\n extern void mpc52xx_gpt_stop_timer(struct mpc52xx_gpt_priv *gpt);\n \n+/* mpc52xx_lpbfifo.c */\n+#define MPC52XX_LPBFIFO_FLAG_READ\t\t(0)\n+#define MPC52XX_LPBFIFO_FLAG_WRITE\t\t(1<<0)\n+#define MPC52XX_LPBFIFO_FLAG_NO_INCREMENT\t(1<<1)\n+#define MPC52XX_LPBFIFO_FLAG_NO_DMA\t\t(1<<2)\n+#define MPC52XX_LPBFIFO_FLAG_POLL_DMA\t\t(1<<3)\n+\n+struct mpc52xx_lpbfifo_request {\n+\tstruct list_head list;\n+\n+\t/* localplus bus address */\n+\tunsigned int cs;\n+\tsize_t offset;\n+\n+\t/* Memory address */\n+\tvoid *data;\n+\tphys_addr_t data_phys;\n+\n+\t/* Details of transfer */\n+\tsize_t size;\n+\tsize_t pos;\t/* current position of transfer */\n+\tint flags;\n+\n+\t/* What to do when finished */\n+\tvoid (*callback)(struct mpc52xx_lpbfifo_request *);\n+\n+\tvoid *priv;\t\t/* Driver private data */\n+\n+\t/* statistics */\n+\tint irq_count;\n+\tint irq_ticks;\n+\tu8 last_byte;\n+\tint buffer_not_done_cnt;\n+};\n+\n+extern int mpc52xx_lpbfifo_submit(struct mpc52xx_lpbfifo_request *req);\n+extern void mpc52xx_lpbfifo_abort(struct mpc52xx_lpbfifo_request *req);\n+extern void mpc52xx_lpbfifo_poll(void);\n+\n /* mpc52xx_pic.c */\n extern void mpc52xx_init_irq(void);\n extern unsigned int mpc52xx_get_irq(void);\ndiff --git a/arch/powerpc/platforms/52xx/Kconfig b/arch/powerpc/platforms/52xx/Kconfig\nindex 696a5ee..ea00b3d 100644\n--- a/arch/powerpc/platforms/52xx/Kconfig\n+++ b/arch/powerpc/platforms/52xx/Kconfig\n@@ -51,3 +51,8 @@ config PPC_MPC5200_GPIO\n \tselect GENERIC_GPIO\n \thelp\n \t  Enable gpiolib support for mpc5200 based boards\n+\n+config PPC_MPC5200_LPBFIFO\n+\ttristate \"MPC5200 LocalPlus bus FIFO driver\"\n+\tdepends on PPC_MPC52xx\n+\tselect PPC_BESTCOMM_GEN_BD\ndiff --git a/arch/powerpc/platforms/52xx/Makefile b/arch/powerpc/platforms/52xx/Makefile\nindex d6ade3d..8df23af 100644\n--- a/arch/powerpc/platforms/52xx/Makefile\n+++ b/arch/powerpc/platforms/52xx/Makefile\n@@ -14,3 +14,4 @@ ifeq ($(CONFIG_PPC_LITE5200),y)\n endif\n \n obj-$(CONFIG_PPC_MPC5200_GPIO)\t+= mpc52xx_gpio.o\n+obj-$(CONFIG_PPC_MPC5200_LPBFIFO)\t+= mpc52xx_lpbfifo.o\ndiff --git a/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c b/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c\nnew file mode 100644\nindex 0000000..929d017\n--- /dev/null\n+++ b/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c\n@@ -0,0 +1,560 @@\n+/*\n+ * LocalPlus Bus FIFO driver for the Freescale MPC52xx.\n+ *\n+ * Copyright (C) 2009 Secret Lab Technologies Ltd.\n+ *\n+ * This file is released under the GPLv2\n+ *\n+ * Todo:\n+ * - Add support for multiple requests to be queued.\n+ */\n+\n+#include <linux/interrupt.h>\n+#include <linux/kernel.h>\n+#include <linux/of.h>\n+#include <linux/of_platform.h>\n+#include <linux/spinlock.h>\n+#include <asm/io.h>\n+#include <asm/prom.h>\n+#include <asm/mpc52xx.h>\n+#include <asm/time.h>\n+\n+#include <sysdev/bestcomm/bestcomm.h>\n+#include <sysdev/bestcomm/bestcomm_priv.h>\n+#include <sysdev/bestcomm/gen_bd.h>\n+\n+MODULE_AUTHOR(\"Grant Likely <grant.likely@secretlab.ca>\");\n+MODULE_DESCRIPTION(\"MPC5200 LocalPlus FIFO device driver\");\n+MODULE_LICENSE(\"GPL\");\n+\n+#define LPBFIFO_REG_PACKET_SIZE\t\t(0x00)\n+#define LPBFIFO_REG_START_ADDRESS\t(0x04)\n+#define LPBFIFO_REG_CONTROL\t\t(0x08)\n+#define LPBFIFO_REG_ENABLE\t\t(0x0C)\n+#define LPBFIFO_REG_BYTES_DONE_STATUS\t(0x14)\n+#define LPBFIFO_REG_FIFO_DATA\t\t(0x40)\n+#define LPBFIFO_REG_FIFO_STATUS\t\t(0x44)\n+#define LPBFIFO_REG_FIFO_CONTROL\t(0x48)\n+#define LPBFIFO_REG_FIFO_ALARM\t\t(0x4C)\n+\n+struct mpc52xx_lpbfifo {\n+\tstruct device *dev;\n+\tphys_addr_t regs_phys;\n+\tvoid __iomem *regs;\n+\tint irq;\n+\tspinlock_t lock;\n+\n+\tstruct bcom_task *bcom_tx_task;\n+\tstruct bcom_task *bcom_rx_task;\n+\tstruct bcom_task *bcom_cur_task;\n+\n+\t/* Current state data */\n+\tstruct mpc52xx_lpbfifo_request *req;\n+\tint dma_irqs_enabled;\n+};\n+\n+/* The MPC5200 has only one fifo, so only need one instance structure */\n+static struct mpc52xx_lpbfifo lpbfifo;\n+\n+/**\n+ * mpc52xx_lpbfifo_kick - Trigger the next block of data to be transfered\n+ */\n+static void mpc52xx_lpbfifo_kick(struct mpc52xx_lpbfifo_request *req)\n+{\n+\tsize_t transfer_size = req->size - req->pos;\n+\tstruct bcom_bd *bd;\n+\tvoid __iomem *reg;\n+\tu32 *data;\n+\tint i;\n+\tint bit_fields;\n+\tint dma = !(req->flags & MPC52XX_LPBFIFO_FLAG_NO_DMA);\n+\tint write = req->flags & MPC52XX_LPBFIFO_FLAG_WRITE;\n+\tint poll_dma = req->flags & MPC52XX_LPBFIFO_FLAG_POLL_DMA;\n+\n+\t/* Set and clear the reset bits; is good practice in User Manual */\n+\tout_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x01010000);\n+\n+\t/* set master enable bit */\n+\tout_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x00000001);\n+\tif (!dma) {\n+\t\t/* While the FIFO can be setup for transfer sizes as large as\n+\t\t * 16M-1, the FIFO itself is only 512 bytes deep and it does\n+\t\t * not generate interrupts for FIFO full events (only transfer\n+\t\t * complete will raise an IRQ).  Therefore when not using\n+\t\t * Bestcomm to drive the FIFO it needs to either be polled, or\n+\t\t * transfers need to constrained to the size of the fifo.\n+\t\t *\n+\t\t * This driver restricts the size of the transfer\n+\t\t */\n+\t\tif (transfer_size > 512)\n+\t\t\ttransfer_size = 512;\n+\n+\t\t/* Load the FIFO with data */\n+\t\tif (write) {\n+\t\t\treg = lpbfifo.regs + LPBFIFO_REG_FIFO_DATA;\n+\t\t\tdata = req->data + req->pos;\n+\t\t\tfor (i = 0; i < transfer_size; i += 4)\n+\t\t\t\tout_be32(reg, *data++);\n+\t\t}\n+\n+\t\t/* Unmask both error and completion irqs */\n+\t\tout_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x00000301);\n+\t} else {\n+\t\t/* Choose the correct direction\n+\t\t *\n+\t\t * Configure the watermarks so DMA will always complete correctly.\n+\t\t * It may be worth experimenting with the ALARM value to see if\n+\t\t * there is a performance impacit.  However, if it is wrong there\n+\t\t * is a risk of DMA not transferring the last chunk of data\n+\t\t */\n+\t\tif (write) {\n+\t\t\tout_be32(lpbfifo.regs + LPBFIFO_REG_FIFO_ALARM, 0x1e4);\n+\t\t\tout_8(lpbfifo.regs + LPBFIFO_REG_FIFO_CONTROL, 7);\n+\t\t\tlpbfifo.bcom_cur_task = lpbfifo.bcom_tx_task;\n+\t\t} else {\n+\t\t\tout_be32(lpbfifo.regs + LPBFIFO_REG_FIFO_ALARM, 0x1ff);\n+\t\t\tout_8(lpbfifo.regs + LPBFIFO_REG_FIFO_CONTROL, 0);\n+\t\t\tlpbfifo.bcom_cur_task = lpbfifo.bcom_rx_task;\n+\n+\t\t\tif (poll_dma) {\n+\t\t\t\tif (lpbfifo.dma_irqs_enabled) {\n+\t\t\t\t\tdisable_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task));\n+\t\t\t\t\tlpbfifo.dma_irqs_enabled = 0;\n+\t\t\t\t}\n+\t\t\t} else {\n+\t\t\t\tif (!lpbfifo.dma_irqs_enabled) {\n+\t\t\t\t\tenable_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task));\n+\t\t\t\t\tlpbfifo.dma_irqs_enabled = 1;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\n+\t\tbd = bcom_prepare_next_buffer(lpbfifo.bcom_cur_task);\n+\t\tbd->status = transfer_size;\n+\t\tif (!write) {\n+\t\t\t/*\n+\t\t\t * In the DMA read case, the DMA doesn't complete,\n+\t\t\t * possibly due to incorrect watermarks in the ALARM\n+\t\t\t * and CONTROL regs. For now instead of trying to\n+\t\t\t * determine the right watermarks that will make this\n+\t\t\t * work, just increase the number of bytes the FIFO is\n+\t\t\t * expecting.\n+\t\t\t *\n+\t\t\t * When submitting another operation, the FIFO will get\n+\t\t\t * reset, so the condition of the FIFO waiting for a\n+\t\t\t * non-existent 4 bytes will get cleared.\n+\t\t\t */\n+\t\t\ttransfer_size += 4; /* BLECH! */\n+\t\t}\n+\t\tbd->data[0] = req->data_phys + req->pos;\n+\t\tbcom_submit_next_buffer(lpbfifo.bcom_cur_task, NULL);\n+\n+\t\t/* error irq & master enabled bit */\n+\t\tbit_fields = 0x00000201;\n+\n+\t\t/* Unmask irqs */\n+\t\tif (write && (!poll_dma))\n+\t\t\tbit_fields |= 0x00000100; /* completion irq too */\n+\t\tout_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, bit_fields);\n+\t}\n+\n+\t/* Set transfer size, width, chip select and READ mode */\n+\tout_be32(lpbfifo.regs + LPBFIFO_REG_START_ADDRESS,\n+\t\t req->offset + req->pos);\n+\tout_be32(lpbfifo.regs + LPBFIFO_REG_PACKET_SIZE, transfer_size);\n+\n+\tbit_fields = req->cs << 24 | 0x000008;\n+\tif (!write)\n+\t\tbit_fields |= 0x010000; /* read mode */\n+\tout_be32(lpbfifo.regs + LPBFIFO_REG_CONTROL, bit_fields);\n+\n+\t/* Kick it off */\n+\tout_8(lpbfifo.regs + LPBFIFO_REG_PACKET_SIZE, 0x01);\n+\tif (dma)\n+\t\tbcom_enable(lpbfifo.bcom_cur_task);\n+}\n+\n+/**\n+ * mpc52xx_lpbfifo_irq - IRQ handler for LPB FIFO\n+ *\n+ * On transmit, the dma completion irq triggers before the fifo completion\n+ * triggers.  Handle the dma completion here instead of the LPB FIFO Bestcomm\n+ * task completion irq becuase everyting is not really done until the LPB FIFO\n+ * completion irq triggers.\n+ *\n+ * In other words:\n+ * For DMA, on receive, the \"Fat Lady\" is the bestcom completion irq. on\n+ * transmit, the fifo completion irq is the \"Fat Lady\". The opera (or in this\n+ * case the DMA/FIFO operation) is not finished until the \"Fat Lady\" sings.\n+ *\n+ * Reasons for entering this routine:\n+ * 1) PIO mode rx and tx completion irq\n+ * 2) DMA interrupt mode tx completion irq\n+ * 3) DMA polled mode tx\n+ *\n+ * Exit conditions:\n+ * 1) Transfer aborted\n+ * 2) FIFO complete without DMA; more data to do\n+ * 3) FIFO complete without DMA; all data transfered\n+ * 4) FIFO complete using DMA\n+ *\n+ * Condition 1 can occur regardless of whether or not DMA is used.\n+ * It requires executing the callback to report the error and exiting\n+ * immediately.\n+ *\n+ * Condition 2 requires programming the FIFO with the next block of data\n+ *\n+ * Condition 3 requires executing the callback to report completion\n+ *\n+ * Condition 4 means the same as 3, except that we also retrieve the bcom\n+ * buffer so DMA doesn't get clogged up.\n+ *\n+ * To make things trickier, the spinlock must be dropped before\n+ * executing the callback, otherwise we could end up with a deadlock\n+ * or nested spinlock condition.  The out path is non-trivial, so\n+ * extra fiddling is done to make sure all paths lead to the same\n+ * outbound code.\n+ */\n+static irqreturn_t mpc52xx_lpbfifo_irq(int irq, void *dev_id)\n+{\n+\tstruct mpc52xx_lpbfifo_request *req;\n+\tu32 status = in_8(lpbfifo.regs + LPBFIFO_REG_BYTES_DONE_STATUS);\n+\tvoid __iomem *reg;\n+\tu32 *data;\n+\tint count, i;\n+\tint do_callback = 0;\n+\tu32 ts;\n+\tunsigned long flags;\n+\tint dma, write, poll_dma;\n+\n+\tspin_lock_irqsave(&lpbfifo.lock, flags);\n+\tts = get_tbl();\n+\n+\treq = lpbfifo.req;\n+\tif (!req) {\n+\t\tspin_unlock_irqrestore(&lpbfifo.lock, flags);\n+\t\tpr_err(\"bogus LPBFIFO IRQ\\n\");\n+\t\treturn IRQ_HANDLED;\n+\t}\n+\n+\tdma = !(req->flags & MPC52XX_LPBFIFO_FLAG_NO_DMA);\n+\twrite = req->flags & MPC52XX_LPBFIFO_FLAG_WRITE;\n+\tpoll_dma = req->flags & MPC52XX_LPBFIFO_FLAG_POLL_DMA;\n+\n+\tif (dma && !write) {\n+\t\tspin_unlock_irqrestore(&lpbfifo.lock, flags);\n+\t\tpr_err(\"bogus LPBFIFO IRQ (dma and not writting)\\n\");\n+\t\treturn IRQ_HANDLED;\n+\t}\n+\n+\tif ((status & 0x01) == 0) {\n+\t\tgoto out;\n+\t}\n+\n+\t/* check abort bit */\n+\tif (status & 0x10) {\n+\t\tout_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x01010000);\n+\t\tdo_callback = 1;\n+\t\tgoto out;\n+\t}\n+\n+\t/* Read result from hardware */\n+\tcount = in_be32(lpbfifo.regs + LPBFIFO_REG_BYTES_DONE_STATUS);\n+\tcount &= 0x00ffffff;\n+\n+\tif (!dma && !write) {\n+\t\t/* copy the data out of the FIFO */\n+\t\treg = lpbfifo.regs + LPBFIFO_REG_FIFO_DATA;\n+\t\tdata = req->data + req->pos;\n+\t\tfor (i = 0; i < count; i += 4)\n+\t\t\t*data++ = in_be32(reg);\n+\t}\n+\n+\t/* Update transfer position and count */\n+\treq->pos += count;\n+\n+\t/* Decide what to do next */\n+\tif (req->size - req->pos)\n+\t\tmpc52xx_lpbfifo_kick(req); /* more work to do */\n+\telse\n+\t\tdo_callback = 1;\n+\n+ out:\n+\t/* Clear the IRQ */\n+\tout_8(lpbfifo.regs + LPBFIFO_REG_BYTES_DONE_STATUS, 0x01);\n+\n+\tif (dma && (status & 0x11)) {\n+\t\t/*\n+\t\t * Count the DMA as complete only when the FIFO completion\n+\t\t * status or abort bits are set.\n+\t\t *\n+\t\t * (status & 0x01) should always be the case except sometimes\n+\t\t * when using polled DMA.\n+\t\t *\n+\t\t * (status & 0x10) {transfer aborted}: This case needs more\n+\t\t * testing.\n+\t\t */\n+\t\tbcom_retrieve_buffer(lpbfifo.bcom_cur_task, &status, NULL);\n+\t}\n+\treq->last_byte = ((u8 *)req->data)[req->size - 1];\n+\n+\t/* When the do_callback flag is set; it means the transfer is finished\n+\t * so set the FIFO as idle */\n+\tif (do_callback)\n+\t\tlpbfifo.req = NULL;\n+\n+\tif (irq != 0) /* don't increment on polled case */\n+\t\treq->irq_count++;\n+\n+\treq->irq_ticks += get_tbl() - ts;\n+\tspin_unlock_irqrestore(&lpbfifo.lock, flags);\n+\n+\t/* Spinlock is released; it is now safe to call the callback */\n+\tif (do_callback && req->callback)\n+\t\treq->callback(req);\n+\n+\treturn IRQ_HANDLED;\n+}\n+\n+/**\n+ * mpc52xx_lpbfifo_bcom_irq - IRQ handler for LPB FIFO Bestcomm task\n+ *\n+ * Only used when receiving data.\n+ */\n+static irqreturn_t mpc52xx_lpbfifo_bcom_irq(int irq, void *dev_id)\n+{\n+\tstruct mpc52xx_lpbfifo_request *req;\n+\tunsigned long flags;\n+\tu32 status;\n+\tu32 ts;\n+\n+\tspin_lock_irqsave(&lpbfifo.lock, flags);\n+\tts = get_tbl();\n+\n+\treq = lpbfifo.req;\n+\tif (!req || (req->flags & MPC52XX_LPBFIFO_FLAG_NO_DMA)) {\n+\t\tspin_unlock_irqrestore(&lpbfifo.lock, flags);\n+\t\treturn IRQ_HANDLED;\n+\t}\n+\n+\tif (irq != 0) /* don't increment on polled case */\n+\t\treq->irq_count++;\n+\n+\tif (!bcom_buffer_done(lpbfifo.bcom_cur_task)) {\n+\t\tspin_unlock_irqrestore(&lpbfifo.lock, flags);\n+\n+\t\treq->buffer_not_done_cnt++;\n+\t\tif ((req->buffer_not_done_cnt % 1000) == 0)\n+\t\t\tpr_err(\"transfer stalled\\n\");\n+\n+\t\treturn IRQ_HANDLED;\n+\t}\n+\n+\tbcom_retrieve_buffer(lpbfifo.bcom_cur_task, &status, NULL);\n+\n+\treq->last_byte = ((u8 *)req->data)[req->size - 1];\n+\n+\treq->pos = status & 0x00ffffff;\n+\n+\t/* Mark the FIFO as idle */\n+\tlpbfifo.req = NULL;\n+\n+\t/* Release the lock before calling out to the callback. */\n+\treq->irq_ticks += get_tbl() - ts;\n+\tspin_unlock_irqrestore(&lpbfifo.lock, flags);\n+\n+\tif (req->callback)\n+\t\treq->callback(req);\n+\n+\treturn IRQ_HANDLED;\n+}\n+\n+/**\n+ * mpc52xx_lpbfifo_bcom_poll - Poll for DMA completion\n+ */\n+void mpc52xx_lpbfifo_poll(void)\n+{\n+\tstruct mpc52xx_lpbfifo_request *req = lpbfifo.req;\n+\tint dma = !(req->flags & MPC52XX_LPBFIFO_FLAG_NO_DMA);\n+\tint write = req->flags & MPC52XX_LPBFIFO_FLAG_WRITE;\n+\n+\t/*\n+\t * For more information, see comments on the \"Fat Lady\" \n+\t */\n+\tif (dma && write)\n+\t\tmpc52xx_lpbfifo_irq(0, NULL);\n+\telse \n+\t\tmpc52xx_lpbfifo_bcom_irq(0, NULL);\n+}\n+EXPORT_SYMBOL(mpc52xx_lpbfifo_poll);\n+\n+/**\n+ * mpc52xx_lpbfifo_submit - Submit an LPB FIFO transfer request.\n+ * @req: Pointer to request structure\n+ */\n+int mpc52xx_lpbfifo_submit(struct mpc52xx_lpbfifo_request *req)\n+{\n+\tunsigned long flags;\n+\n+\tif (!lpbfifo.regs)\n+\t\treturn -ENODEV;\n+\n+\tspin_lock_irqsave(&lpbfifo.lock, flags);\n+\n+\t/* If the req pointer is already set, then a transfer is in progress */\n+\tif (lpbfifo.req) {\n+\t\tspin_unlock_irqrestore(&lpbfifo.lock, flags);\n+\t\treturn -EBUSY;\n+\t}\n+\n+\t/* Setup the transfer */\n+\tlpbfifo.req = req;\n+\treq->irq_count = 0;\n+\treq->irq_ticks = 0;\n+\treq->buffer_not_done_cnt = 0;\n+\treq->pos = 0;\n+\n+\tmpc52xx_lpbfifo_kick(req);\n+\tspin_unlock_irqrestore(&lpbfifo.lock, flags);\n+\treturn 0;\n+}\n+EXPORT_SYMBOL(mpc52xx_lpbfifo_submit);\n+\n+void mpc52xx_lpbfifo_abort(struct mpc52xx_lpbfifo_request *req)\n+{\n+\tunsigned long flags;\n+\n+\tspin_lock_irqsave(&lpbfifo.lock, flags);\n+\tif (lpbfifo.req == req) {\n+\t\t/* Put it into reset and clear the state */\n+\t\tbcom_gen_bd_rx_reset(lpbfifo.bcom_rx_task);\n+\t\tbcom_gen_bd_tx_reset(lpbfifo.bcom_tx_task);\n+\t\tout_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x01010000);\n+\t\tlpbfifo.req = NULL;\n+\t}\n+\tspin_unlock_irqrestore(&lpbfifo.lock, flags);\n+}\n+EXPORT_SYMBOL(mpc52xx_lpbfifo_abort);\n+\n+static int __devinit\n+mpc52xx_lpbfifo_probe(struct of_device *op, const struct of_device_id *match)\n+{\n+\tstruct resource res;\n+\tint rc = -ENOMEM;\n+\n+\tif (lpbfifo.dev != NULL)\n+\t\treturn -ENOSPC;\n+\n+\tlpbfifo.irq = irq_of_parse_and_map(op->node, 0);\n+\tif (!lpbfifo.irq)\n+\t\treturn -ENODEV;\n+\n+\tif (of_address_to_resource(op->node, 0, &res))\n+\t\treturn -ENODEV;\n+\tlpbfifo.regs_phys = res.start;\n+\tlpbfifo.regs = of_iomap(op->node, 0);\n+\tif (!lpbfifo.regs)\n+\t\treturn -ENOMEM;\n+\n+\tspin_lock_init(&lpbfifo.lock);\n+\n+\t/* Put FIFO into reset */\n+\tout_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x01010000);\n+\n+\t/* Register the interrupt handler */\n+\trc = request_irq(lpbfifo.irq, mpc52xx_lpbfifo_irq, 0,\n+\t\t\t \"mpc52xx-lpbfifo\", &lpbfifo);\n+\tif (rc)\n+\t\tgoto err_irq;\n+\n+\t/* Request the Bestcomm receive (fifo --> memory) task and IRQ */\n+\tlpbfifo.bcom_rx_task =\n+\t\tbcom_gen_bd_rx_init(2, res.start + LPBFIFO_REG_FIFO_DATA,\n+\t\t\t\t    BCOM_INITIATOR_SCLPC, BCOM_IPR_SCLPC,\n+\t\t\t\t    16*1024*1024);\n+\tif (!lpbfifo.bcom_rx_task)\n+\t\tgoto err_bcom_rx;\n+\n+\trc = request_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task),\n+\t\t\t mpc52xx_lpbfifo_bcom_irq, 0,\n+\t\t\t \"mpc52xx-lpbfifo-rx\", &lpbfifo);\n+\tif (rc)\n+\t\tgoto err_bcom_rx_irq;\n+\n+\t/* Request the Bestcomm transmit (memory --> fifo) task and IRQ */\n+\tlpbfifo.bcom_tx_task =\n+\t\tbcom_gen_bd_tx_init(2, res.start + LPBFIFO_REG_FIFO_DATA,\n+\t\t\t\t    BCOM_INITIATOR_SCLPC, BCOM_IPR_SCLPC);\n+\tif (!lpbfifo.bcom_tx_task)\n+\t\tgoto err_bcom_tx;\n+\n+\tlpbfifo.dev = &op->dev;\n+\treturn 0;\n+\n+ err_bcom_tx:\n+\tfree_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task), &lpbfifo);\n+ err_bcom_rx_irq:\n+\tbcom_gen_bd_rx_release(lpbfifo.bcom_rx_task);\n+ err_bcom_rx:\n+ err_irq:\n+\tiounmap(lpbfifo.regs);\n+\tlpbfifo.regs = NULL;\n+\n+\tdev_err(&op->dev, \"mpc52xx_lpbfifo_probe() failed\\n\");\n+\treturn -ENODEV;\n+}\n+\n+\n+static int __devexit mpc52xx_lpbfifo_remove(struct of_device *op)\n+{\n+\tif (lpbfifo.dev != &op->dev)\n+\t\treturn 0;\n+\n+\t/* Put FIFO in reset */\n+\tout_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x01010000);\n+\n+\t/* Release the bestcomm transmit task */\n+\tfree_irq(bcom_get_task_irq(lpbfifo.bcom_tx_task), &lpbfifo);\n+\tbcom_gen_bd_tx_release(lpbfifo.bcom_tx_task);\n+\t\n+\t/* Release the bestcomm receive task */\n+\tfree_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task), &lpbfifo);\n+\tbcom_gen_bd_rx_release(lpbfifo.bcom_rx_task);\n+\n+\tfree_irq(lpbfifo.irq, &lpbfifo);\n+\tiounmap(lpbfifo.regs);\n+\tlpbfifo.regs = NULL;\n+\tlpbfifo.dev = NULL;\n+\n+\treturn 0;\n+}\n+\n+static struct of_device_id mpc52xx_lpbfifo_match[] __devinitconst = {\n+\t{ .compatible = \"fsl,mpc5200-lpbfifo\", },\n+\t{},\n+};\n+\n+static struct of_platform_driver mpc52xx_lpbfifo_driver = {\n+\t.owner = THIS_MODULE,\n+\t.name = \"mpc52xx-lpbfifo\",\n+\t.match_table = mpc52xx_lpbfifo_match,\n+\t.probe = mpc52xx_lpbfifo_probe,\n+\t.remove = __devexit_p(mpc52xx_lpbfifo_remove),\n+};\n+\n+/***********************************************************************\n+ * Module init/exit\n+ */\n+static int __init mpc52xx_lpbfifo_init(void)\n+{\n+\tpr_debug(\"Registering LocalPlus bus FIFO driver\\n\");\n+\treturn of_register_platform_driver(&mpc52xx_lpbfifo_driver);\n+}\n+module_init(mpc52xx_lpbfifo_init);\n+\n+static void __exit mpc52xx_lpbfifo_exit(void)\n+{\n+\tpr_debug(\"Unregistering LocalPlus bus FIFO driver\\n\");\n+\tof_unregister_platform_driver(&mpc52xx_lpbfifo_driver);\n+}\n+module_exit(mpc52xx_lpbfifo_exit);\n",
    "prefixes": []
}