* [PATCH RFCv1 2/3] fpga: add CARMA DATA-FPGA Access Driver
From: Ira W. Snyder @ 2010-09-03 22:30 UTC (permalink / raw)
To: linuxppc-dev; +Cc: linux-kernel, Ira W. Snyder
In-Reply-To: <1283553052-23576-1-git-send-email-iws@ovro.caltech.edu>
This driver allows userspace to access the data processing FPGAs on the
OVRO CARMA board. It has two modes of operation:
1) random access
This allows users to poke any DATA-FPGA registers by using mmap to map
the address region directly into their memory map.
2) correlation dumping
When correlating, the DATA-FPGA's have special requirements for getting
the data out of their memory before the next correlation. This nominally
happens at 64Hz (every 15.625ms). If the data is not dumped before the
next correlation, data is lost.
The data dumping driver handles buffering up to 1 second worth of
correlation data from the FPGAs. This lowers the realtime scheduling
requirements for the userspace process reading the device.
Signed-off-by: Ira W. Snyder <iws@ovro.caltech.edu>
---
drivers/fpga/carma/Kconfig | 9 +
drivers/fpga/carma/Makefile | 1 +
drivers/fpga/carma/carma-fpga.c | 1447 +++++++++++++++++++++++++++++++++++++++
3 files changed, 1457 insertions(+), 0 deletions(-)
create mode 100644 drivers/fpga/carma/carma-fpga.c
diff --git a/drivers/fpga/carma/Kconfig b/drivers/fpga/carma/Kconfig
index 448885e..5592f73 100644
--- a/drivers/fpga/carma/Kconfig
+++ b/drivers/fpga/carma/Kconfig
@@ -18,4 +18,13 @@ config CARMA
Say Y here to include basic support for the CARMA System Controller
FPGA. This option allows the other more advanced drivers to be built.
+config CARMA_FPGA
+ tristate "CARMA DATA-FPGA Access Driver"
+ depends on CARMA
+ select VIDEOBUF_DMA_SG
+ default n
+ help
+ Say Y here to include support for communicating with the data
+ processing FPGAs on the CARMA board.
+
endif # FPGA_DRIVERS
diff --git a/drivers/fpga/carma/Makefile b/drivers/fpga/carma/Makefile
index 90d0594..c175d34 100644
--- a/drivers/fpga/carma/Makefile
+++ b/drivers/fpga/carma/Makefile
@@ -1 +1,2 @@
obj-$(CONFIG_CARMA) += carma.o
+obj-$(CONFIG_CARMA_FPGA) += carma-fpga.o
diff --git a/drivers/fpga/carma/carma-fpga.c b/drivers/fpga/carma/carma-fpga.c
new file mode 100644
index 0000000..ab1b536
--- /dev/null
+++ b/drivers/fpga/carma/carma-fpga.c
@@ -0,0 +1,1447 @@
+/*
+ * CARMA DATA-FPGA Access Driver
+ *
+ * Copyright (c) 2009-2010 Ira W. Snyder <iws@ovro.caltech.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+/*
+ * FPGA Memory Dump Format
+ *
+ * FPGA #0 control registers (32 x 32-bit words)
+ * FPGA #1 control registers (32 x 32-bit words)
+ * FPGA #2 control registers (32 x 32-bit words)
+ * FPGA #3 control registers (32 x 32-bit words)
+ * SYSFPGA control registers (32 x 32-bit words)
+ * FPGA #0 correlation array (NUM_CORL0 correlation blocks)
+ * FPGA #1 correlation array (NUM_CORL1 correlation blocks)
+ * FPGA #2 correlation array (NUM_CORL2 correlation blocks)
+ * FPGA #3 correlation array (NUM_CORL3 correlation blocks)
+ *
+ * Each correlation array consists of:
+ *
+ * Correlation Data (2 x NUM_LAGSn x 32-bit words)
+ * Pipeline Metadata (2 x NUM_METAn x 32-bit words)
+ * Quantization Counters (2 x NUM_QCNTn x 32-bit words)
+ *
+ * The NUM_CORLn, NUM_LAGSn, NUM_METAn, and NUM_QCNTn values come from
+ * the FPGA configuration registers. They do not change once the FPGA's
+ * have been programmed, they only change on re-programming.
+ */
+
+/*
+ * Basic Description:
+ *
+ * This driver is used to capture correlation spectra off of the four data
+ * processing FPGAs. The FPGAs are often reprogrammed at runtime, therefore
+ * this driver supports dynamic enable/disable of capture while the device
+ * remains open.
+ *
+ * The nominal capture rate is 64Hz (every 15.625ms). To facilitate this fast
+ * capture rate, all buffers are pre-allocated to avoid any potentially long
+ * running memory allocations while capturing.
+ *
+ * There are three lists which are used to keep track of the different states
+ * of data buffers.
+ *
+ * 1) free list
+ * This list holds all empty data buffers which are ready to receive data.
+ *
+ * 2) inflight list
+ * This list holds data buffers which are currently waiting for a DMA operation
+ * to complete.
+ *
+ * 3) used list
+ * This list holds data buffers which have been filled, and are waiting to be
+ * read by userspace.
+ *
+ * All buffers start life on the free list, then move successively to the
+ * inflight list, and then to the used list. After they have been read by
+ * userspace, they are moved back to the free list. The cycle repeats as long
+ * as necessary.
+ */
+
+/*
+ * Notes on the IRQ masking scheme:
+ *
+ * The IRQ masking scheme here is different than most other hardware. The only
+ * way for the DATA-FPGAs to detect if the kernel has taken too long to copy
+ * the data is if the status registers are not cleared before the next
+ * correlation data dump is ready.
+ *
+ * The interrupt line is connected to the status registers, such that when they
+ * are cleared, the interrupt is de-asserted. Therein lies our problem. We need
+ * to schedule a long-running DMA operation and return from the interrupt
+ * handler quickly, but we cannot clear the status registers.
+ *
+ * To handle this, the system controller FPGA has the capability to connect the
+ * interrupt line to a user-controlled GPIO pin. This pin is driven high
+ * (unasserted) and left that way. To mask the interrupt, we change the
+ * interrupt source to the GPIO pin. Tada, we hid the interrupt. :)
+ */
+
+#include <linux/of_platform.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/dmaengine.h>
+#include <linux/highmem.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/cdev.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/io.h>
+
+#include <media/videobuf-dma-sg.h>
+#include <asm/fsldma.h>
+
+#include "carma.h"
+
+/* system controller registers */
+#define SYS_IRQ_SOURCE_CTL 0x24
+#define SYS_IRQ_OUTPUT_EN 0x28
+#define SYS_IRQ_OUTPUT_DATA 0x2C
+#define SYS_IRQ_INPUT_DATA 0x30
+
+/* GPIO IRQ line assignment */
+#define IRQ_CORL_DONE 0x10
+
+/* FPGA registers */
+#define MMAP_REG_CORL_CONF1 0x08
+#define MMAP_REG_CORL_CONF2 0x0C
+#define MMAP_REG_STATUS 0x48
+
+#define SYS_FPGA_BLOCK 0xF0000000
+
+static const char drv_name[] = "carma-fpga";
+
+#define NUM_FPGA 4
+
+#define MIN_DATA_BUFS 8
+#define MAX_DATA_BUFS 64
+
+struct fpga_info {
+ unsigned int num_corl;
+ unsigned int blk_size;
+};
+
+struct data_buf {
+ struct list_head entry;
+ struct videobuf_dmabuf vb;
+ bool mapped;
+ size_t size;
+};
+
+struct fpga_device {
+ struct cdev cdev;
+ dev_t devno;
+
+ struct device *dev;
+ struct mutex mutex;
+
+ /* FPGA registers and information */
+ struct fpga_info info[NUM_FPGA];
+ void __iomem *regs;
+ int irq;
+
+ /* FPGA Physical Address/Size Information */
+ resource_size_t phys_addr;
+ size_t phys_size;
+
+ /* DMA structures */
+ struct fsl_dma_slave *slave;
+ struct dma_chan *chan;
+ struct device *dmadev;
+
+ /* Protection for all members below */
+ spinlock_t lock;
+
+ /* Device enable/disable flag */
+ bool enabled;
+
+ /* Correlation data buffers */
+ wait_queue_head_t wait;
+ struct list_head free;
+ struct list_head used;
+ struct list_head inflight;
+
+ /* Information about data buffers */
+ unsigned int num_dropped;
+ unsigned int num_buffers;
+ size_t bufsize;
+};
+
+struct fpga_reader {
+ struct fpga_device *priv;
+ struct data_buf *buf;
+ off_t buf_start;
+};
+
+#define inode_to_dev(inode) container_of(inode->i_cdev, struct fpga_device, cdev)
+
+/*
+ * Data Buffer Allocation Helpers
+ */
+
+static int data_map_buffer(struct device *dev, struct data_buf *buf)
+{
+ int ret;
+
+ /* if the buffer is already mapped, we're done */
+ if (buf->mapped)
+ return 0;
+
+ ret = videobuf_dma_map(dev, &buf->vb);
+ if (ret)
+ return ret;
+
+ buf->mapped = true;
+ return 0;
+}
+
+static void data_unmap_buffer(struct device *dev, struct data_buf *buf)
+{
+ /* the buffer is already unmapped, we're done */
+ if (!buf->mapped)
+ return;
+
+ videobuf_dma_unmap(dev, &buf->vb);
+ buf->mapped = false;
+}
+
+/*
+ * Free a single data buffer and all allocated pages
+ *
+ * This will free all of the pages allocated to the given data buffer, and
+ * then free the structure itself
+ *
+ * @dev: the DMA device to map for
+ * @buf: the buffer to free
+ */
+static void data_free_buffer(struct device *dev, struct data_buf *buf)
+{
+ /* It is ok to free a NULL buffer */
+ if (!buf)
+ return;
+
+ /* Make sure the buffer is not on any list */
+ list_del_init(&buf->entry);
+
+ /* unmap it for DMA */
+ data_unmap_buffer(dev, buf);
+
+ /* free all memory */
+ videobuf_dma_free(&buf->vb);
+ kfree(buf);
+}
+
+/*
+ * Allocate and fill a data buffer
+ *
+ * This allocates all space needed for a data buffer, and gets it ready to be
+ * used in a DMA transaction. It only needs to be used, never mapped before
+ * use. This avoids calling vmalloc in hardirq context.
+ *
+ * @dev: the DMA device to map for
+ * @bytes: the number of bytes required
+ * @return: a new buffer or NULL on failure
+ */
+static struct data_buf *data_alloc_buffer(struct device *dev, const size_t bytes)
+{
+ unsigned int nr_pages;
+ struct data_buf *buf;
+ int ret;
+
+ /* calculate the number of pages necessary */
+ nr_pages = DIV_ROUND_UP(bytes, PAGE_SIZE);
+
+ /* allocate the buffer structure */
+ buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+ if (!buf)
+ goto out_return;
+
+ /* initialize internal fields */
+ INIT_LIST_HEAD(&buf->entry);
+ buf->size = bytes;
+
+ /* allocate the videobuf */
+ videobuf_dma_init(&buf->vb);
+ ret = videobuf_dma_init_kernel(&buf->vb, DMA_FROM_DEVICE, nr_pages);
+ if (ret)
+ goto out_free_buf;
+
+ /* map it for DMA */
+ ret = data_map_buffer(dev, buf);
+ if (ret)
+ goto out_free_videobuf;
+
+ return buf;
+
+out_free_videobuf:
+ videobuf_dma_free(&buf->vb);
+out_free_buf:
+ kfree(buf);
+out_return:
+ return NULL;
+}
+
+/*
+ * Free all of the buffers allocated for correlation data
+ *
+ * This routine will free all data buffers that are being used by the driver.
+ *
+ * REQUIREMENTS: all list manipulation must have ceased
+ * REQUIREMENTS: all DMA must be complete, and the engine stopped
+ *
+ * CONTEXT: user
+ *
+ * @param priv the driver's private data structure
+ */
+static void data_free_buffers(struct fpga_device *priv)
+{
+ struct device *dev = priv->dmadev;
+ struct data_buf *buf, *tmp;
+
+ spin_lock_irq(&priv->lock);
+ BUG_ON(!list_empty(&priv->inflight));
+
+ list_for_each_entry_safe(buf, tmp, &priv->free, entry) {
+ list_del_init(&buf->entry);
+ spin_unlock_irq(&priv->lock);
+ data_free_buffer(dev, buf);
+ spin_lock_irq(&priv->lock);
+ }
+
+ list_for_each_entry_safe(buf, tmp, &priv->used, entry) {
+ list_del_init(&buf->entry);
+ spin_unlock_irq(&priv->lock);
+ data_free_buffer(dev, buf);
+ spin_lock_irq(&priv->lock);
+ }
+
+ priv->num_buffers = 0;
+ priv->bufsize = 0;
+
+ spin_unlock_irq(&priv->lock);
+}
+
+/*
+ * Allocate enough buffers for a whole second worth of data
+ *
+ * This routine will attempt to degrade nicely by succeeding even if a full
+ * second worth of data buffers could not be allocated, as long as a minimum
+ * number were allocated. In this case, it will print a message to the kernel
+ * log.
+ *
+ * CONTEXT: user
+ *
+ * @param priv the driver's private data structure
+ * @return 0 on success, -ERRNO otherwise
+ */
+static int data_alloc_buffers(struct fpga_device *priv)
+{
+ struct device *dev = priv->dmadev;
+ struct data_buf *buf;
+ int i;
+
+ for (i = 0; i < MAX_DATA_BUFS; i++) {
+ buf = data_alloc_buffer(dev, priv->bufsize);
+ if (!buf)
+ break;
+
+ spin_lock_irq(&priv->lock);
+ list_add_tail(&buf->entry, &priv->free);
+ spin_unlock_irq(&priv->lock);
+ }
+
+ /* Make sure we allocated the minimum required number of buffers */
+ if (i < MIN_DATA_BUFS) {
+ dev_err(priv->dev, "Unable to allocate enough data buffers\n");
+ data_free_buffers(priv);
+ return -ENOMEM;
+ }
+
+ /* Warn if we are running in a degraded state, but do not fail */
+ if (i < MAX_DATA_BUFS) {
+ dev_warn(priv->dev, "Unable to allocate one second worth of "
+ "buffers, using %d buffers instead\n", i);
+ }
+
+ priv->num_buffers = i;
+ return 0;
+}
+
+/*
+ * DMA Operations Helpers
+ */
+
+/*
+ * Calculate the physical address of the start of FPGA memory
+ *
+ * @param priv the driver's private data structure
+ * @param fpga the FPGA number
+ * @return the physical address of the correlation data block
+ */
+static dma_addr_t fpga_start_addr(struct fpga_device *priv, unsigned int fpga)
+{
+ return priv->phys_addr + 0x400000 + (0x80000 * fpga);
+}
+
+/*
+ * Calculate the physical address of an FPGA correlation data block
+ *
+ * @param priv the driver's private data structure
+ * @param fpga the FPGA number
+ * @param blknum the correlation data block number
+ * @return the physical address of the correlation data block
+ */
+static dma_addr_t fpga_block_addr(struct fpga_device *priv, unsigned int fpga,
+ unsigned int blknum)
+{
+ return fpga_start_addr(priv, fpga) + (0x10000 * (1 + blknum));
+}
+
+/*
+ * Add the correlation data for a single DATA FPGA to the DMA_SLAVE structure
+ *
+ * @param priv the driver's private data structure
+ * @param slave the Freescale DMA_SLAVE structure
+ * @param fpga the FPGA number
+ * @return 0 on success, -ERRNO otherwise
+ */
+static int fpga_append_correlation_data(struct fpga_device *priv,
+ struct fsl_dma_slave *slave,
+ unsigned int fpga)
+{
+ struct fpga_info *info = &priv->info[fpga];
+ dma_addr_t addr;
+ size_t len;
+ int i, ret;
+
+ for (i = 0; i < info->num_corl; i++) {
+ addr = fpga_block_addr(priv, fpga, i);
+ len = info->blk_size;
+ ret = fsl_dma_slave_append(slave, addr, len);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Create the DMA_SLAVE structure for transferring data from the DATA FPGA's
+ *
+ * This structure will be reused for each buffer that needs to be filled
+ * with correlation data from the DATA FPGA's
+ *
+ * @param priv the driver's private data structure
+ * @return 0 on success, -ERRNO otherwise
+ */
+static int data_setup_dma_slave(struct fpga_device *priv)
+{
+ static const char prefix[] = "DMA_SLAVE: unable to";
+ struct fsl_dma_slave *slave;
+ dma_addr_t addr;
+ size_t len;
+ int i, ret;
+
+ /* Create the Freescale DMA_SLAVE structure */
+ slave = fsl_dma_slave_alloc(GFP_KERNEL);
+ if (!slave) {
+ dev_err(priv->dev, "%s allocate structure\n", prefix);
+ ret = -ENOMEM;
+ goto out_return;
+ }
+
+ /* Add the FPGA registers to the slave list */
+ for (i = 0; i < NUM_FPGA; i++) {
+ addr = fpga_start_addr(priv, i);
+ len = 32 * 4;
+ ret = fsl_dma_slave_append(slave, addr, len);
+ if (ret) {
+ dev_err(priv->dev, "%s add FPGA registers\n", prefix);
+ goto out_free_slave;
+ }
+ }
+
+ /* Add the SYS-FPGA registers to the slave list */
+ addr = SYS_FPGA_BLOCK;
+ len = 32 * 4;
+ ret = fsl_dma_slave_append(slave, addr, len);
+ if (ret) {
+ dev_err(priv->dev, "%s add SYS-FPGA registers\n", prefix);
+ goto out_free_slave;
+ }
+
+ /* Add the FPGA correlation data blocks to the slave list */
+ for (i = 0; i < NUM_FPGA; i++) {
+ ret = fpga_append_correlation_data(priv, slave, i);
+ if (ret) {
+ dev_err(priv->dev, "%s add correlation data\n", prefix);
+ goto out_free_slave;
+ }
+ }
+
+ /*
+ * That's everything, this slave structure can be re-used for
+ * every FPGA DATA interrupt
+ */
+ priv->slave = slave;
+ return 0;
+
+out_free_slave:
+ fsl_dma_slave_free(slave);
+out_return:
+ return ret;
+}
+
+/*
+ * FPGA Register Access Helpers
+ */
+
+static void fpga_write_reg(struct fpga_device *priv, unsigned int fpga,
+ unsigned int reg, u32 val)
+{
+ iowrite32be(val, priv->regs + 0x400000 + (fpga * 0x80000) + reg);
+}
+
+static u32 fpga_read_reg(struct fpga_device *priv, unsigned int fpga,
+ unsigned int reg)
+{
+ return ioread32be(priv->regs + 0x400000 + (fpga * 0x80000) + reg);
+}
+
+/*
+ * Calculate the total buffer size needed to hold a single block
+ * of correlation data
+ *
+ * CONTEXT: user
+ *
+ * @param priv the driver's private data structure
+ * @return 0 on success, -ERRNO otherwise
+ */
+static int data_calculate_bufsize(struct fpga_device *priv)
+{
+ u32 num_corl, num_lags, num_meta, num_qcnt, blk_size;
+ u32 conf1, conf2;
+ int i;
+
+ /* Zero the total buffer size */
+ priv->bufsize = 0;
+
+ /* Read and store the configuration data for each FPGA */
+ for (i = 0; i < NUM_FPGA; i++) {
+ conf1 = fpga_read_reg(priv, i, MMAP_REG_CORL_CONF1);
+ conf2 = fpga_read_reg(priv, i, MMAP_REG_CORL_CONF2);
+
+ num_corl = (conf1 & 0x000000F0) >> 4;
+ num_lags = (conf1 & 0x000FFF00) >> 8;
+ num_meta = (conf1 & 0x7FF00000) >> 20;
+ num_qcnt = (conf2 & 0x00000FFF) >> 0;
+ blk_size = (num_lags + num_meta + num_qcnt) * 8;
+
+ priv->info[i].num_corl = num_corl;
+ priv->info[i].blk_size = blk_size;
+ priv->bufsize += num_corl * blk_size;
+
+ dev_dbg(priv->dev, "FPGA %d NUM_CORL: %d\n", i, num_corl);
+ dev_dbg(priv->dev, "FPGA %d NUM_LAGS: %d\n", i, num_lags);
+ dev_dbg(priv->dev, "FPGA %d NUM_META: %d\n", i, num_meta);
+ dev_dbg(priv->dev, "FPGA %d NUM_QCNT: %d\n", i, num_qcnt);
+ dev_dbg(priv->dev, "FPGA %d BLK_SIZE: %d\n", i, blk_size);
+ }
+
+ /* Add in the 5 FPGA register areas */
+ priv->bufsize += 5 * (32 * 4);
+ dev_dbg(priv->dev, "TOTAL BUFFER SIZE: %zu bytes\n", priv->bufsize);
+
+ return 0;
+}
+
+/*
+ * Interrupt Handling
+ */
+
+/*
+ * Hide interrupts by switching to GPIO interrupt source
+ *
+ * LOCKING: must hold dev->lock
+ */
+static void data_disable_interrupts(struct fpga_device *priv)
+{
+ /* hide the interrupt by switching the IRQ driver to GPIO */
+ iowrite32be(0x2F, priv->regs + SYS_IRQ_SOURCE_CTL);
+}
+
+/*
+ * Unhide interrupts by switching to the FPGA interrupt source
+ *
+ * LOCKING: must hold dev->lock
+ */
+static void data_enable_interrupts(struct fpga_device *priv)
+{
+ /* clear the actual FPGA corl_done interrupt */
+ fpga_write_reg(priv, 0, MMAP_REG_STATUS, 0x0);
+ fpga_write_reg(priv, 1, MMAP_REG_STATUS, 0x0);
+ fpga_write_reg(priv, 2, MMAP_REG_STATUS, 0x0);
+ fpga_write_reg(priv, 3, MMAP_REG_STATUS, 0x0);
+
+ /* flush the writes */
+ fpga_read_reg(priv, 0, MMAP_REG_STATUS);
+
+ /* switch back to the external interrupt source */
+ iowrite32be(0x3F, priv->regs + SYS_IRQ_SOURCE_CTL);
+}
+
+/*
+ * Complete a DMA transfer from the FPGA's
+ *
+ * This is called via the DMA callback mechanism, and will handle moving the
+ * completed DMA transaction to the used list, and then wake any processes
+ * waiting for new data
+ *
+ * CONTEXT: any, softirq expected
+ *
+ * @param data the driver's private data structure
+ */
+static void data_dma_cb(void *data)
+{
+ struct fpga_device *priv = data;
+ struct data_buf *buf;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ /* clear the FPGA status and re-enable interrupts */
+ data_enable_interrupts(priv);
+
+ /* If the inflight list is empty, we've got a bug */
+ BUG_ON(list_empty(&priv->inflight));
+
+ /* Grab the first buffer from the inflight list */
+ buf = list_first_entry(&priv->inflight, struct data_buf, entry);
+ list_del_init(&buf->entry);
+
+ /* Add it to the used list */
+ list_add_tail(&buf->entry, &priv->used);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ /* We've changed both the inflight and used lists, so we need
+ * to wake up any processes that are blocking for those events */
+ wake_up(&priv->wait);
+}
+
+/*
+ * Prepare and submit a DMA_SLAVE transaction for a correlation data buffer
+ *
+ * LOCKING: must hold dev->lock
+ * CONTEXT: hardirq only
+ *
+ * @param priv the driver's private data structure
+ * @param buf the data buffer to DMA into
+ * @return 0 on success, -ERRNO otherwise
+ */
+static int data_submit_dma(struct fpga_device *priv, struct data_buf *buf)
+{
+ struct scatterlist *sg = buf->vb.sglist;
+ unsigned int nents = buf->vb.sglen;
+ struct dma_chan *chan = priv->chan;
+ struct dma_async_tx_descriptor *tx;
+ dma_cookie_t cookie;
+ dma_addr_t dst, src;
+
+ /*
+ * All buffers passed to this function should be ready and mapped
+ * for DMA already. Therefore, we don't need to do anything except
+ * submit it to the Freescale DMA Engine for processing
+ */
+
+ /* setup the DMA_SLAVE transaction */
+ chan->private = priv->slave;
+ tx = chan->device->device_prep_slave_sg(chan, sg, nents,
+ DMA_FROM_DEVICE, 0);
+ if (!tx) {
+ dev_err(priv->dev, "unable to prep slave DMA 1\n");
+ return -ENOMEM;
+ }
+
+ /* submit the transaction to the DMA controller */
+ cookie = tx->tx_submit(tx);
+ if (dma_submit_error(cookie)) {
+ dev_err(priv->dev, "unable to submit slave DMA 1\n");
+ return -ENOMEM;
+ }
+
+ /* Prepare the re-read of the SYS-FPGA block */
+ dst = sg_dma_address(sg) + (NUM_FPGA * 32 * 4);
+ src = SYS_FPGA_BLOCK;
+ tx = chan->device->device_prep_dma_memcpy(chan, dst, src, 32 * 4,
+ DMA_PREP_INTERRUPT);
+ if (!tx) {
+ dev_err(priv->dev, "unable to prep slave DMA 2\n");
+ return -ENOMEM;
+ }
+
+ /* Setup the callback */
+ tx->callback = data_dma_cb;
+ tx->callback_param = priv;
+
+ /* submit the transaction to the DMA controller */
+ cookie = tx->tx_submit(tx);
+ if (dma_submit_error(cookie)) {
+ dev_err(priv->dev, "unable to submit slave DMA 2\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+#define CORL_DONE 0x1
+#define CORL_ERR 0x2
+
+static irqreturn_t data_irq(int irq, void *dev_id)
+{
+ struct fpga_device *priv = dev_id;
+ struct data_buf *buf;
+ u32 status;
+ int i;
+
+ /* detect spurious interrupts via FPGA status */
+ for (i = 0; i < 4; i++) {
+ status = fpga_read_reg(priv, i, MMAP_REG_STATUS);
+ if (!(status & (CORL_DONE | CORL_ERR))) {
+ dev_err(priv->dev, "spurious irq detected (FPGA)\n");
+ return IRQ_NONE;
+ }
+ }
+
+ /* detect spurious interrupts via raw IRQ pin readback */
+ status = ioread32be(priv->regs + SYS_IRQ_INPUT_DATA);
+ if (status & IRQ_CORL_DONE) {
+ dev_err(priv->dev, "spurious irq detected (IRQ)\n");
+ return IRQ_NONE;
+ }
+
+ spin_lock(&priv->lock);
+
+ /* hide the interrupt by switching the IRQ driver to GPIO */
+ data_disable_interrupts(priv);
+
+ /* Check that we actually have a free buffer */
+ if (list_empty(&priv->free)) {
+ priv->num_dropped++;
+ data_enable_interrupts(priv);
+ goto out_unlock;
+ }
+
+ buf = list_first_entry(&priv->free, struct data_buf, entry);
+ list_del_init(&buf->entry);
+
+ /* Check the buffer size */
+ BUG_ON(buf->size != priv->bufsize);
+
+ /* Submit a DMA transfer to get the correlation data */
+ if (data_submit_dma(priv, buf)) {
+ dev_err(priv->dev, "Unable to setup DMA transfer\n");
+ list_add_tail(&buf->entry, &priv->free);
+ data_enable_interrupts(priv);
+ goto out_unlock;
+ }
+
+ /* DMA setup succeeded, GO!!! */
+ list_add_tail(&buf->entry, &priv->inflight);
+ dma_async_memcpy_issue_pending(priv->chan);
+
+out_unlock:
+ spin_unlock(&priv->lock);
+ return IRQ_HANDLED;
+}
+
+/*
+ * Realtime Device Enable Helpers
+ */
+
+/*
+ * Enable the device for buffered dumping
+ *
+ * ASSUMES: the FPGA's are powered on and reads will succeed
+ * LOCKING: dev->mutex held
+ * CONTEXT: user context only
+ */
+static int data_device_enable(struct fpga_device *priv)
+{
+ u32 val;
+ int ret;
+
+ /* multiple enables are safe: they do nothing */
+ if (priv->enabled)
+ return 0;
+
+ /* check that the FPGAs are programmed */
+ val = ioread32be(priv->regs + 0x44);
+ if (!(val & (1 << 18))) {
+ dev_err(priv->dev, "DATA-FPGAs are not enabled\n");
+ return -ENODATA;
+ }
+
+ /* read the FPGAs to calculate the buffer size */
+ ret = data_calculate_bufsize(priv);
+ if (ret) {
+ dev_err(priv->dev, "unable to calculate buffer size\n");
+ goto out_error;
+ }
+
+ /* allocate the correlation data buffers */
+ ret = data_alloc_buffers(priv);
+ if (ret) {
+ dev_err(priv->dev, "unable to allocate buffers\n");
+ goto out_error;
+ }
+
+ /* allocate the DMA_SLAVE structure for correlation data */
+ ret = data_setup_dma_slave(priv);
+ if (ret) {
+ dev_err(priv->dev, "unable to setup DMA list\n");
+ goto out_error;
+ }
+
+ /* switch to the external FPGA IRQ line */
+ data_enable_interrupts(priv);
+
+ /* hookup the irq handler */
+ ret = request_irq(priv->irq, data_irq, IRQF_SHARED, drv_name, priv);
+ if (ret) {
+ dev_err(priv->dev, "unable to request IRQ handler\n");
+ goto out_free_slave;
+ }
+
+ /* success, we're enabled */
+ priv->enabled = true;
+ return 0;
+
+out_free_slave:
+ fsl_dma_slave_free(priv->slave);
+ priv->slave = NULL;
+out_error:
+ data_free_buffers(priv);
+ return ret;
+}
+
+/*
+ * Disable the device for buffered dumping
+ *
+ * LOCKING: must hold dev->mutex
+ * CONTEXT: user only
+ */
+static int data_device_disable(struct fpga_device *priv)
+{
+ struct list_head *list;
+ int ret;
+
+ /* allow multiple disable */
+ if (!priv->enabled)
+ return 0;
+
+ /* switch to the internal GPIO IRQ line */
+ data_disable_interrupts(priv);
+
+ /* unhook the irq handler */
+ free_irq(priv->irq, priv);
+
+ /* wait for all outstanding DMA to complete */
+ list = &priv->inflight;
+
+ spin_lock_irq(&priv->lock);
+ while (!list_empty(list)) {
+ spin_unlock_irq(&priv->lock);
+
+ ret = wait_event_interruptible(priv->wait, list_empty(list));
+ if (ret)
+ return -ERESTARTSYS;
+
+ spin_lock_irq(&priv->lock);
+ }
+ spin_unlock_irq(&priv->lock);
+
+ /* free the DMA_SLAVE structure */
+ fsl_dma_slave_free(priv->slave);
+ priv->slave = NULL;
+
+ /* free all of the buffers */
+ data_free_buffers(priv);
+ priv->enabled = false;
+ return 0;
+}
+
+/*
+ * SYSFS Attributes
+ */
+
+/*
+ * Count the number of entries in the given list
+ */
+static unsigned int list_num_entries(struct list_head *list)
+{
+ struct list_head *entry;
+ unsigned int ret = 0;
+
+ list_for_each(entry, list)
+ ret++;
+
+ return ret;
+}
+
+static ssize_t data_num_buffers_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fpga_device *priv = dev_get_drvdata(dev);
+ unsigned int num;
+
+ spin_lock_irq(&priv->lock);
+ num = priv->num_buffers;
+ spin_unlock_irq(&priv->lock);
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", num);
+}
+
+static ssize_t data_bufsize_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fpga_device *priv = dev_get_drvdata(dev);
+ size_t num;
+
+ spin_lock_irq(&priv->lock);
+ num = priv->bufsize;
+ spin_unlock_irq(&priv->lock);
+
+ return snprintf(buf, PAGE_SIZE, "%zu\n", num);
+}
+
+static ssize_t data_inflight_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fpga_device *priv = dev_get_drvdata(dev);
+ unsigned int num;
+
+ spin_lock_irq(&priv->lock);
+ num = list_num_entries(&priv->inflight);
+ spin_unlock_irq(&priv->lock);
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", num);
+}
+
+static ssize_t data_free_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fpga_device *priv = dev_get_drvdata(dev);
+ unsigned int num;
+
+ spin_lock_irq(&priv->lock);
+ num = list_num_entries(&priv->free);
+ spin_unlock_irq(&priv->lock);
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", num);
+}
+
+static ssize_t data_used_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fpga_device *priv = dev_get_drvdata(dev);
+ unsigned int num;
+
+ spin_lock_irq(&priv->lock);
+ num = list_num_entries(&priv->used);
+ spin_unlock_irq(&priv->lock);
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", num);
+}
+
+static ssize_t data_num_dropped_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fpga_device *priv = dev_get_drvdata(dev);
+ unsigned int num;
+
+ spin_lock_irq(&priv->lock);
+ num = priv->num_dropped;
+ spin_unlock_irq(&priv->lock);
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", num);
+}
+
+static ssize_t data_en_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct fpga_device *priv = dev_get_drvdata(dev);
+ ssize_t count;
+
+ if (mutex_lock_interruptible(&priv->mutex))
+ return -ERESTARTSYS;
+
+ count = snprintf(buf, PAGE_SIZE, "%u\n", priv->enabled);
+ mutex_unlock(&priv->mutex);
+ return count;
+}
+
+static ssize_t data_en_set(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fpga_device *priv = dev_get_drvdata(dev);
+ unsigned long enable;
+ int ret;
+
+ ret = strict_strtoul(buf, 0, &enable);
+ if (ret) {
+ dev_err(priv->dev, "unable to parse enable input\n");
+ return -EINVAL;
+ }
+
+ if (mutex_lock_interruptible(&priv->mutex))
+ return -ERESTARTSYS;
+
+ if (enable)
+ ret = data_device_enable(priv);
+ else
+ ret = data_device_disable(priv);
+
+ if (ret) {
+ dev_err(priv->dev, "device %s failed\n",
+ enable ? "enable" : "disable");
+ count = ret;
+ goto out_unlock;
+ }
+
+out_unlock:
+ mutex_unlock(&priv->mutex);
+ return count;
+}
+
+static DEVICE_ATTR(num_buffers, S_IRUGO, data_num_buffers_show, NULL);
+static DEVICE_ATTR(buffer_size, S_IRUGO, data_bufsize_show, NULL);
+static DEVICE_ATTR(num_inflight, S_IRUGO, data_inflight_show, NULL);
+static DEVICE_ATTR(num_free, S_IRUGO, data_free_show, NULL);
+static DEVICE_ATTR(num_used, S_IRUGO, data_used_show, NULL);
+static DEVICE_ATTR(num_dropped, S_IRUGO, data_num_dropped_show, NULL);
+static DEVICE_ATTR(enable, S_IWUGO | S_IRUGO, data_en_show, data_en_set);
+
+static struct attribute *data_sysfs_attrs[] = {
+ &dev_attr_num_buffers.attr,
+ &dev_attr_buffer_size.attr,
+ &dev_attr_num_inflight.attr,
+ &dev_attr_num_free.attr,
+ &dev_attr_num_used.attr,
+ &dev_attr_num_dropped.attr,
+ &dev_attr_enable.attr,
+ NULL,
+};
+
+static const struct attribute_group rt_sysfs_attr_group = {
+ .attrs = data_sysfs_attrs,
+};
+
+/*
+ * FPGA Realtime Data Character Device
+ */
+
+static int data_open(struct inode *inode, struct file *filp)
+{
+ struct fpga_device *priv = inode_to_dev(inode);
+ struct fpga_reader *reader;
+ int ret;
+
+ /* allocate private data */
+ reader = kzalloc(sizeof(*reader), GFP_KERNEL);
+ if (!reader)
+ return -ENOMEM;
+
+ reader->priv = priv;
+ reader->buf = NULL;
+
+ filp->private_data = reader;
+ ret = nonseekable_open(inode, filp);
+ if (ret) {
+ dev_err(priv->dev, "nonseekable-open failed\n");
+ kfree(reader);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int data_release(struct inode *inode, struct file *filp)
+{
+ struct fpga_reader *reader = filp->private_data;
+ struct fpga_device *priv = reader->priv;
+
+ /* free the per-reader structure */
+ data_free_buffer(priv->dmadev, reader->buf);
+ kfree(reader);
+ return 0;
+}
+
+static ssize_t data_read(struct file *filp, char __user *ubuf, size_t count,
+ loff_t *f_pos)
+{
+ struct fpga_reader *reader = filp->private_data;
+ struct fpga_device *priv = reader->priv;
+ struct list_head *used = &priv->used;
+ struct data_buf *dbuf;
+ size_t avail;
+ void *data;
+ int ret;
+
+ /* check if we already have a partial buffer */
+ if (reader->buf) {
+ dbuf = reader->buf;
+ goto have_buffer;
+ }
+
+ spin_lock_irq(&priv->lock);
+
+ /* Block until there is at least one buffer on the used list */
+ while (list_empty(used)) {
+ spin_unlock_irq(&priv->lock);
+
+ if (filp->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+
+ if (wait_event_interruptible(priv->wait, !list_empty(used)))
+ return -ERESTARTSYS;
+
+ spin_lock_irq(&priv->lock);
+ }
+
+ /* Grab the first buffer off of the used list */
+ dbuf = list_first_entry(used, struct data_buf, entry);
+ list_del_init(&dbuf->entry);
+
+ spin_unlock_irq(&priv->lock);
+
+ /* Buffers are always mapped: unmap it */
+ data_unmap_buffer(priv->dmadev, dbuf);
+
+ /* save the buffer for later */
+ reader->buf = dbuf;
+ reader->buf_start = 0;
+
+ /* we removed a buffer from the used list: wake any waiters */
+ wake_up(&priv->wait);
+
+have_buffer:
+ /* Get the number of bytes available */
+ avail = dbuf->size - reader->buf_start;
+ data = dbuf->vb.vaddr + reader->buf_start;
+
+ /* Get the number of bytes we can transfer */
+ count = min(count, avail);
+
+ /* Copy the data to the userspace buffer */
+ if (copy_to_user(ubuf, data, count))
+ return -EFAULT;
+
+ /* Update the amount of available space */
+ avail -= count;
+
+ /* Lock against concurrent enable/disable */
+ if (mutex_lock_interruptible(&priv->mutex))
+ return -ERESTARTSYS;
+
+ /* Still some space available: save the buffer for later */
+ if (avail != 0) {
+ reader->buf_start += count;
+ reader->buf = dbuf;
+ goto out_unlock;
+ }
+
+ /*
+ * No space is available in this buffer
+ *
+ * This is a complicated decision:
+ * - if the device is not enabled: free the buffer
+ * - if the buffer is too small: free the buffer
+ */
+ if (!priv->enabled || dbuf->size != priv->bufsize) {
+ data_free_buffer(priv->dmadev, dbuf);
+ reader->buf = NULL;
+ goto out_unlock;
+ }
+
+ /*
+ * The buffer is safe to recycle: remap it and finish
+ *
+ * If this fails, we pretend that the read never happened, and return
+ * -EFAULT to userspace. They'll retry the read again.
+ */
+ ret = data_map_buffer(priv->dmadev, dbuf);
+ if (ret) {
+ dev_err(priv->dev, "unable to remap buffer for DMA\n");
+ count = -EFAULT;
+ goto out_unlock;
+ }
+
+ /* Add the buffer back to the free list */
+ reader->buf = NULL;
+ spin_lock_irq(&priv->lock);
+ list_add_tail(&dbuf->entry, &priv->free);
+ spin_unlock_irq(&priv->lock);
+
+out_unlock:
+ mutex_unlock(&priv->mutex);
+ return count;
+}
+
+static unsigned int data_poll(struct file *filp, struct poll_table_struct *tbl)
+{
+ struct fpga_reader *reader = filp->private_data;
+ struct fpga_device *priv = reader->priv;
+ unsigned int mask = 0;
+
+ poll_wait(filp, &priv->wait, tbl);
+
+ spin_lock_irq(&priv->lock);
+
+ if (!list_empty(&priv->used))
+ mask |= POLLIN | POLLRDNORM;
+
+ spin_unlock_irq(&priv->lock);
+ return mask;
+}
+
+static int data_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct fpga_reader *reader = filp->private_data;
+ struct fpga_device *priv = reader->priv;
+ unsigned long offset, vsize, psize, addr;
+
+ /* VMA properties */
+ offset = vma->vm_pgoff << PAGE_SHIFT;
+ vsize = vma->vm_end - vma->vm_start;
+ psize = priv->phys_size - offset;
+ addr = (priv->phys_addr + offset) >> PAGE_SHIFT;
+
+ /* Check against the FPGA region's physical memory size */
+ if (vsize > psize) {
+ dev_err(priv->dev, "requested mmap mapping too large\n");
+ return -EINVAL;
+ }
+
+ /* IO memory (stop cacheing) */
+ vma->vm_flags |= VM_IO | VM_RESERVED;
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ return io_remap_pfn_range(vma, vma->vm_start, addr, vsize,
+ vma->vm_page_prot);
+}
+
+static const struct file_operations data_fops = {
+ .owner = THIS_MODULE,
+ .open = data_open,
+ .release = data_release,
+ .read = data_read,
+ .poll = data_poll,
+ .mmap = data_mmap,
+ .llseek = no_llseek,
+};
+
+/*
+ * OpenFirmware Device Subsystem
+ */
+
+static bool dma_filter(struct dma_chan *chan, void *data)
+{
+ /*
+ * DMA Channel #0 is used for the FPGA Programmer, so ignore it
+ *
+ * This probably won't survive an unload/load cycle of the Freescale
+ * DMAEngine driver, but that won't be a problem
+ */
+ if (chan->chan_id == 0 && chan->device->dev_id == 0)
+ return false;
+
+ return true;
+}
+
+static int data_of_probe(struct platform_device *op,
+ const struct of_device_id *match)
+{
+ struct device_node *of_node = op->dev.of_node;
+ struct fpga_device *priv;
+ struct resource res;
+ dma_cap_mask_t mask;
+ int ret;
+
+ /* Allocate private data */
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(&op->dev, "Unable to allocate device private data\n");
+ ret = -ENOMEM;
+ goto out_return;
+ }
+
+ dev_set_drvdata(&op->dev, priv);
+ priv->dmadev = &op->dev;
+
+ /* Allocate the character device */
+ ret = alloc_chrdev_region(&priv->devno, 0, 1, drv_name);
+ if (ret) {
+ dev_err(&op->dev, "Unable to allocate chardev region\n");
+ goto out_free_priv;
+ }
+
+ /* Get the physical address of the FPGA registers */
+ ret = of_address_to_resource(of_node, 0, &res);
+ if (ret) {
+ dev_err(&op->dev, "Unable to find FPGA physical address\n");
+ ret = -ENODEV;
+ goto out_unregister_chrdev_region;
+ }
+
+ priv->phys_addr = res.start;
+ priv->phys_size = resource_size(&res);
+
+ /* ioremap the registers for use */
+ priv->regs = of_iomap(of_node, 0);
+ if (!priv->regs) {
+ dev_err(&op->dev, "Unable to ioremap registers\n");
+ ret = -ENOMEM;
+ goto out_unregister_chrdev_region;
+ }
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_MEMCPY, mask);
+ dma_cap_set(DMA_INTERRUPT, mask);
+ dma_cap_set(DMA_SLAVE, mask);
+
+ /* Request a DMA channel */
+ priv->chan = dma_request_channel(mask, dma_filter, NULL);
+ if (!priv->chan) {
+ dev_err(&op->dev, "Unable to request DMA channel\n");
+ ret = -ENODEV;
+ goto out_unmap_regs;
+ }
+
+ /* Find the correct IRQ number */
+ priv->irq = irq_of_parse_and_map(of_node, 0);
+ if (priv->irq == NO_IRQ) {
+ dev_err(&op->dev, "Unable to find IRQ line\n");
+ ret = -ENODEV;
+ goto out_release_dma;
+ }
+
+ priv->dev = carma_device_create(&op->dev, priv->devno, drv_name);
+ if (IS_ERR(priv->dev)) {
+ dev_err(&op->dev, "Unable to create CARMA device\n");
+ ret = PTR_ERR(priv->dev);
+ goto out_irq_dispose_mapping;
+ }
+
+ dev_set_drvdata(priv->dev, priv);
+ cdev_init(&priv->cdev, &data_fops);
+ mutex_init(&priv->mutex);
+ spin_lock_init(&priv->lock);
+ INIT_LIST_HEAD(&priv->free);
+ INIT_LIST_HEAD(&priv->used);
+ INIT_LIST_HEAD(&priv->inflight);
+ init_waitqueue_head(&priv->wait);
+
+ /* Drive the GPIO for FPGA IRQ high (no interrupt) */
+ iowrite32be(IRQ_CORL_DONE, priv->regs + SYS_IRQ_OUTPUT_DATA);
+
+ /* Register the character device */
+ ret = cdev_add(&priv->cdev, priv->devno, 1);
+ if (ret) {
+ dev_err(&op->dev, "Unable to add character device\n");
+ goto out_destroy_carma_device;
+ }
+
+ /* Create the sysfs files */
+ ret = sysfs_create_group(&priv->dev->kobj, &rt_sysfs_attr_group);
+ if (ret) {
+ dev_err(&op->dev, "Unable to create sysfs files\n");
+ goto out_cdev_del;
+ }
+
+ dev_info(&op->dev, "CARMA FPGA Realtime Data Driver Loaded\n");
+ return 0;
+
+out_cdev_del:
+ cdev_del(&priv->cdev);
+out_destroy_carma_device:
+ carma_device_destroy(priv->devno);
+out_irq_dispose_mapping:
+ irq_dispose_mapping(priv->irq);
+out_release_dma:
+ dma_release_channel(priv->chan);
+out_unmap_regs:
+ iounmap(priv->regs);
+out_unregister_chrdev_region:
+ unregister_chrdev_region(priv->devno, 1);
+out_free_priv:
+ kfree(priv);
+out_return:
+ return ret;
+}
+
+static int data_of_remove(struct platform_device *op)
+{
+ struct fpga_device *priv = dev_get_drvdata(&op->dev);
+
+ /* make sure the IRQ line is disabled */
+ mutex_lock(&priv->mutex);
+ data_device_disable(priv);
+ mutex_unlock(&priv->mutex);
+
+ sysfs_remove_group(&priv->dev->kobj, &rt_sysfs_attr_group);
+ cdev_del(&priv->cdev);
+ carma_device_destroy(priv->devno);
+
+ irq_dispose_mapping(priv->irq);
+ dma_release_channel(priv->chan);
+ iounmap(priv->regs);
+ unregister_chrdev_region(priv->devno, 1);
+ kfree(priv);
+
+ return 0;
+}
+
+static struct of_device_id data_of_match[] = {
+ { .compatible = "carma,carma-fpga", },
+ {},
+};
+
+static struct of_platform_driver data_of_driver = {
+ .probe = data_of_probe,
+ .remove = data_of_remove,
+ .driver = {
+ .name = drv_name,
+ .of_match_table = data_of_match,
+ .owner = THIS_MODULE,
+ },
+};
+
+/*
+ * Module Init / Exit
+ */
+
+static int __init data_init(void)
+{
+ return of_register_platform_driver(&data_of_driver);
+}
+
+static void __exit data_exit(void)
+{
+ of_unregister_platform_driver(&data_of_driver);
+}
+
+MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>");
+MODULE_DESCRIPTION("CARMA DATA-FPGA Access Driver");
+MODULE_LICENSE("GPL");
+
+module_init(data_init);
+module_exit(data_exit);
--
1.7.1
^ permalink raw reply related
* [PATCH RFCv1 3/3] fpga: add CARMA DATA-FPGA Programmer support
From: Ira W. Snyder @ 2010-09-03 22:30 UTC (permalink / raw)
To: linuxppc-dev; +Cc: linux-kernel, Ira W. Snyder
In-Reply-To: <1283553052-23576-1-git-send-email-iws@ovro.caltech.edu>
This adds support for programming the data processing FPGAs on the OVRO
CARMA board. These FPGAs have a special programming sequence that
requires that we program the Freescale DMA engine, which is only
available inside the kernel.
Signed-off-by: Ira W. Snyder <iws@ovro.caltech.edu>
---
drivers/fpga/carma/Kconfig | 8 +
drivers/fpga/carma/Makefile | 1 +
drivers/fpga/carma/carma-fpga-program.c | 1136 +++++++++++++++++++++++++++++++
3 files changed, 1145 insertions(+), 0 deletions(-)
create mode 100644 drivers/fpga/carma/carma-fpga-program.c
diff --git a/drivers/fpga/carma/Kconfig b/drivers/fpga/carma/Kconfig
index 5592f73..c9df01a 100644
--- a/drivers/fpga/carma/Kconfig
+++ b/drivers/fpga/carma/Kconfig
@@ -27,4 +27,12 @@ config CARMA_FPGA
Say Y here to include support for communicating with the data
processing FPGAs on the CARMA board.
+config CARMA_FPGA_PROGRAM
+ tristate "CARMA DATA-FPGA Programmer"
+ depends on CARMA
+ default n
+ help
+ Say Y here to include support for programming the data processing
+ FPGAs on the CARMA board.
+
endif # FPGA_DRIVERS
diff --git a/drivers/fpga/carma/Makefile b/drivers/fpga/carma/Makefile
index c175d34..4948bbb 100644
--- a/drivers/fpga/carma/Makefile
+++ b/drivers/fpga/carma/Makefile
@@ -1,2 +1,3 @@
obj-$(CONFIG_CARMA) += carma.o
obj-$(CONFIG_CARMA_FPGA) += carma-fpga.o
+obj-$(CONFIG_CARMA_FPGA_PROGRAM) += carma-fpga-program.o
diff --git a/drivers/fpga/carma/carma-fpga-program.c b/drivers/fpga/carma/carma-fpga-program.c
new file mode 100644
index 0000000..04dc115
--- /dev/null
+++ b/drivers/fpga/carma/carma-fpga-program.c
@@ -0,0 +1,1136 @@
+/*
+ * CARMA Board DATA-FPGA Programmer
+ *
+ * Copyright (c) 2009-2010 Ira W. Snyder <iws@ovro.caltech.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/cdev.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/highmem.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_platform.h>
+#include <linux/dmaengine.h>
+
+/* Freescale DMA Controller DMA_SLAVE interface */
+#include <asm/fsldma.h>
+
+/* MPC8349EMDS specific get_immrbase() */
+#include <sysdev/fsl_soc.h>
+
+/* CARMA device class */
+#include "carma.h"
+
+static const char drv_name[] = "carma-fpga-program";
+
+/*
+ * Maximum firmware size
+ *
+ * 12849552 bytes for a CARMA Digitizer Board
+ * 18662880 bytes for a CARMA Correlator Board
+ */
+#define FW_SIZE_EP2S90 12849552
+#define FW_SIZE_EP2S130 18662880
+
+struct fpga_fw_elem {
+ struct page *page;
+ unsigned int len;
+ struct list_head entry;
+};
+
+struct fpga_dev {
+
+ /* Character Device */
+ struct cdev cdev;
+ dev_t devno;
+
+ /* Device Registers */
+ struct device *dev;
+ void __iomem *regs;
+ void __iomem *immr;
+
+ /* Freescale DMA Device */
+ struct device *dmadev;
+ struct dma_chan *chan;
+
+ /* Interrupts */
+ int irq, status;
+ struct completion completion;
+
+ /* FPGA Bitfile */
+ struct mutex lock;
+ struct list_head list;
+ size_t bytes;
+
+ size_t fw_size;
+};
+
+#define to_fpga_dev(X) container_of(X, struct fpga_dev, cdev)
+
+/*
+ * FPGA Bitfile Element Allocation Helpers
+ */
+
+static struct fpga_fw_elem *fpga_fw_elem_alloc(gfp_t gfp)
+{
+ struct fpga_fw_elem *elem;
+
+ elem = kzalloc(sizeof(*elem), gfp);
+ if (!elem)
+ return NULL;
+
+ elem->page = alloc_page(gfp);
+ if (!elem->page) {
+ kfree(elem);
+ return NULL;
+ }
+
+ INIT_LIST_HEAD(&elem->entry);
+ return elem;
+}
+
+static void fpga_fw_elem_free(struct fpga_fw_elem *elem)
+{
+ if (elem) {
+ __free_page(elem->page);
+ kfree(elem);
+ }
+}
+
+/*
+ * FPGA Bitfile Helpers
+ */
+
+/*
+ * Drop the firmware bitfile image from memory
+ *
+ * LOCKING: you must hold the priv->lock mutex
+ *
+ * @param priv the driver's private data structure
+ */
+static void fpga_drop_firmware_data(struct fpga_dev *priv)
+{
+ struct fpga_fw_elem *elem, *tmp;
+
+ priv->bytes = 0;
+ list_for_each_entry_safe(elem, tmp, &priv->list, entry) {
+ list_del(&elem->entry);
+ fpga_fw_elem_free(elem);
+ }
+}
+
+static unsigned int list_num_entries(struct list_head *list)
+{
+ struct list_head *tmp;
+ unsigned int num = 0;
+
+ list_for_each(tmp, list)
+ num++;
+
+ return num;
+}
+
+/*
+ * LED Trigger (could be a seperate module)
+ */
+
+/*
+ * NOTE: this whole thing does have the problem that whenever the led's are
+ * NOTE: first set to use the fpga trigger, they could be in the wrong state
+ */
+
+DEFINE_LED_TRIGGER(ledtrig_fpga);
+
+static void ledtrig_fpga_programmed(bool enabled)
+{
+ if (enabled)
+ led_trigger_event(ledtrig_fpga, LED_FULL);
+ else
+ led_trigger_event(ledtrig_fpga, LED_OFF);
+}
+
+/*
+ * FPGA Register Helpers
+ */
+
+/* Register Definitions */
+#define FPGA_CONFIG_CONTROL 0x40
+#define FPGA_CONFIG_STATUS 0x44
+#define FPGA_CONFIG_FIFO_SIZE 0x48
+#define FPGA_CONFIG_FIFO_USED 0x4C
+#define FPGA_CONFIG_TOTAL_BYTE_COUNT 0x50
+#define FPGA_CONFIG_CUR_BYTE_COUNT 0x54
+
+#define FPGA_FIFO_ADDRESS 0x3000
+
+static int fpga_fifo_size(void __iomem *regs)
+{
+ return ioread32be(regs + FPGA_CONFIG_FIFO_SIZE);
+}
+
+static int fpga_config_error(void __iomem *regs)
+{
+ return ioread32be(regs + FPGA_CONFIG_STATUS) & 0xFFFE;
+}
+
+static int fpga_fifo_empty(void __iomem *regs)
+{
+ return ioread32be(regs + FPGA_CONFIG_FIFO_USED) == 0;
+}
+
+static void fpga_fifo_write(void __iomem *regs, u32 val)
+{
+ iowrite32be(val, regs + FPGA_FIFO_ADDRESS);
+}
+
+static void fpga_set_byte_count(void __iomem *regs, u32 count)
+{
+ iowrite32be(count, regs + FPGA_CONFIG_TOTAL_BYTE_COUNT);
+}
+
+static void fpga_programmer_enable(struct fpga_dev *priv, bool dma)
+{
+ if (dma)
+ iowrite32be(0x5, priv->regs + FPGA_CONFIG_CONTROL);
+ else
+ iowrite32be(0x1, priv->regs + FPGA_CONFIG_CONTROL);
+}
+
+static void fpga_programmer_disable(struct fpga_dev *priv)
+{
+ iowrite32be(0x0, priv->regs + FPGA_CONFIG_CONTROL);
+}
+
+static void fpga_dump_registers(struct fpga_dev *priv)
+{
+ /* good status: do nothing */
+ if (priv->status == 0)
+ return;
+
+ /* Dump all status registers */
+ dev_err(priv->dev, "Configuration failed, dumping status registers\n");
+ dev_err(priv->dev, "Control: 0x%.8x\n", ioread32be(priv->regs + FPGA_CONFIG_CONTROL));
+ dev_err(priv->dev, "Status: 0x%.8x\n", ioread32be(priv->regs + FPGA_CONFIG_STATUS));
+ dev_err(priv->dev, "FIFO Size: 0x%.8x\n", ioread32be(priv->regs + FPGA_CONFIG_FIFO_SIZE));
+ dev_err(priv->dev, "FIFO Used: 0x%.8x\n", ioread32be(priv->regs + FPGA_CONFIG_FIFO_USED));
+ dev_err(priv->dev, "FIFO Total: 0x%.8x\n", ioread32be(priv->regs + FPGA_CONFIG_TOTAL_BYTE_COUNT));
+ dev_err(priv->dev, "FIFO Curr: 0x%.8x\n", ioread32be(priv->regs + FPGA_CONFIG_CUR_BYTE_COUNT));
+}
+
+/*
+ * FPGA Power Supply Code
+ */
+
+#define CTL_PWR_CONTROL 0x2006
+#define CTL_PWR_STATUS 0x200A
+#define CTL_PWR_FAIL 0x200B
+
+/*
+ * Determine if the FPGA power is good for all supplies
+ */
+static bool fpga_power_good(struct fpga_dev *priv)
+{
+ u8 val;
+
+ val = ioread8(priv->regs + CTL_PWR_STATUS);
+ if (val & 0x10)
+ return false;
+
+ return val == 0x0F;
+}
+
+/*
+ * Disable the FPGA power supplies
+ */
+static void fpga_disable_power_supplies(struct fpga_dev *priv)
+{
+ unsigned long start;
+ u8 val;
+
+ iowrite8(0x00, priv->regs + CTL_PWR_CONTROL);
+
+ /*
+ * Wait 500ms for the power rails to discharge
+ *
+ * Without this delay, the CTL-CPLD state machine can get into a
+ * state where it is waiting for the power-goods to assert, but they
+ * never do. This only happens when enabling and disabling the
+ * power sequencer very rapidly.
+ *
+ * The loop below will also wait for the power goods to de-assert,
+ * but testing has shown that they are always disabled by the time
+ * the sleep completes. However, omitting the sleep and only waiting
+ * for the power-goods to de-assert was not sufficient to ensure
+ * that the power sequencer would not wedge itself.
+ */
+ msleep(500);
+
+ start = jiffies;
+ while (time_before(jiffies, start + HZ)) {
+ val = ioread8(priv->regs + CTL_PWR_STATUS);
+ if (!(val & 0x0f))
+ break;
+
+ msleep(10);
+ }
+
+ val = ioread8(priv->regs + CTL_PWR_STATUS);
+ if (val & 0x0f) {
+ dev_err(priv->dev, "power disable failed: "
+ "power goods: status 0x%.2x\n", val);
+ }
+
+ if (val & 0x10) {
+ dev_err(priv->dev, "power disable failed: "
+ "alarm bit set: status 0x%.2x\n", val);
+ }
+}
+
+/*
+ * Enable the FPGA power supplies, waiting up to 1 second
+ * for them to enable successfully.
+ *
+ * @return 0 if the power went good, -ERRNO otherwise
+ */
+static int fpga_enable_power_supplies(struct fpga_dev *priv)
+{
+ unsigned long start = jiffies;
+
+ if (fpga_power_good(priv)) {
+ dev_dbg(priv->dev, "power was already good\n");
+ return 0;
+ }
+
+ iowrite8(0x01, priv->regs + CTL_PWR_CONTROL);
+ while (time_before(jiffies, start + HZ)) {
+ if (fpga_power_good(priv))
+ return 0;
+
+ msleep(10);
+ }
+
+ return fpga_power_good(priv) ? 0 : -EBUSY;
+}
+
+/*
+ * Determine if the FPGA power supplies are all enabled
+ */
+static bool fpga_power_enabled(struct fpga_dev *priv)
+{
+ u8 val;
+
+ val = ioread8(priv->regs + CTL_PWR_CONTROL);
+ if (val & 0x01)
+ return true;
+
+ return false;
+}
+
+/*
+ * Determine if the FPGA's are programmed and running correctly
+ */
+static bool fpga_running(struct fpga_dev *priv)
+{
+ if (!fpga_power_good(priv))
+ return false;
+
+ /* Check the config done bit */
+ return ioread32be(priv->regs + FPGA_CONFIG_STATUS) & (1 << 18);
+}
+
+/*
+ * FPGA Programming Code
+ */
+
+/*
+ * Program some data to the FPGA fifo
+ *
+ * @priv the private data
+ * @buf the data to program
+ * @count the length of data to program (must be a multiple of 4 bytes)
+ *
+ * @return 0 on success, -ERRNO otherwise
+ */
+static int fpga_program_block(struct fpga_dev *priv, void *buf, size_t count)
+{
+ u32 *data = buf;
+ int size = fpga_fifo_size(priv->regs);
+ int i, len;
+ unsigned long timeout;
+
+ /* FIXME: BUG_ON instead */
+ WARN_ON_ONCE(count % 4 != 0);
+
+ while (count > 0) {
+
+ /* Get the size of the block to write (maximum is FIFO_SIZE) */
+ len = min_t(size_t, count, size);
+ timeout = jiffies + HZ / 4;
+
+ /* Write the block */
+ for (i = 0; i < len / 4; i++)
+ fpga_fifo_write(priv->regs, data[i]);
+
+ /* Update the amounts left */
+ count -= len;
+ data += len / 4;
+
+ /* Wait for the fifo to empty */
+ while (true) {
+
+ if (fpga_fifo_empty(priv->regs)) {
+ break;
+ } else {
+ dev_dbg(priv->dev, "Fifo not empty\n");
+ cpu_relax();
+ }
+
+ if (fpga_config_error(priv->regs)) {
+ dev_err(priv->dev, "Error detected\n");
+ return -EIO;
+ }
+
+ if (time_after(jiffies, timeout)) {
+ dev_err(priv->dev, "Fifo timed out\n");
+ return -ETIMEDOUT;
+ }
+
+ msleep(10);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Program the FPGA's using the CPU
+ *
+ * @param priv the driver's private data structure
+ * @return 0 on success, -ERRNO otherwise
+ */
+static noinline int fpga_program_cpu(struct fpga_dev *priv)
+{
+ struct fpga_fw_elem *elem;
+ void *data;
+ int ret;
+
+ /* Disable the programmer */
+ fpga_programmer_disable(priv);
+
+ /* Set the total byte count */
+ fpga_set_byte_count(priv->regs, priv->bytes);
+ dev_dbg(priv->dev, "total byte count %u bytes\n", priv->bytes);
+
+ /* Enable the controller for programming */
+ fpga_programmer_enable(priv, false);
+ dev_dbg(priv->dev, "enabled the controller\n");
+
+ /* Write each chunk of the FPGA bitfile to FPGA programmer */
+ list_for_each_entry(elem, &priv->list, entry) {
+ data = kmap(elem->page);
+ ret = fpga_program_block(priv, data, elem->len);
+ kunmap(elem->page);
+
+ if (ret)
+ goto out_disable_controller;
+ }
+
+ /* Wait for the interrupt handler to notify us that programming finished */
+ ret = wait_for_completion_timeout(&priv->completion, 2 * HZ);
+ if (!ret) {
+ dev_err(priv->dev, "Timed out waiting for completion\n");
+ ret = -ETIMEDOUT;
+ goto out_disable_controller;
+ }
+
+ /* Retrieve the status from the interrupt handler */
+ ret = priv->status;
+
+out_disable_controller:
+ fpga_programmer_disable(priv);
+ return ret;
+}
+
+/*
+ * Program the FPGA's using the DMA controller
+ *
+ * @param priv the driver's private data structure
+ * @return 0 on success, -ERRNO otherwise
+ */
+static noinline int fpga_program_dma(struct fpga_dev *priv)
+{
+ struct dma_chan *chan = priv->chan;
+ struct dma_async_tx_descriptor *tx;
+ struct fsl_dma_slave *slave;
+ struct sg_table table;
+ dma_cookie_t cookie;
+ unsigned int nents, npages;
+ size_t len, avail = 0;
+ int ret;
+ struct scatterlist *sg;
+ struct fpga_fw_elem *elem;
+
+ /* Disable the programmer */
+ fpga_programmer_disable(priv);
+ //chan->device->device_terminate_all(chan);
+
+ /* Allocate the DMA_SLAVE structure */
+ slave = fsl_dma_slave_alloc(GFP_KERNEL);
+ if (!slave) {
+ dev_err(priv->dev, "Unable to allocate DMA_SLAVE structure\n");
+ ret = -ENOMEM;
+ goto out_return;
+ }
+
+ /* Set the DMA controller in external start mode */
+ slave->external_start = true;
+ slave->request_count = 8 * 32;
+
+ /* Allocate the SG table */
+ npages = list_num_entries(&priv->list);
+ ret = sg_alloc_table(&table, npages, GFP_KERNEL);
+ if (ret) {
+ dev_err(priv->dev, "Unable to allocate SG table\n");
+ goto out_free_slave;
+ }
+
+ /* Fill the SG table */
+ sg = table.sgl;
+ list_for_each_entry(elem, &priv->list, entry) {
+ sg_set_page(sg, elem->page, elem->len, 0);
+ sg = sg_next(sg);
+ }
+
+ /* Map the SG table for DMA */
+ nents = dma_map_sg(priv->dmadev, table.sgl, npages, DMA_TO_DEVICE);
+ if (nents <= 0) {
+ dev_err(priv->dev, "Unable to DMA map SG table\n");
+ ret = -ENOMEM;
+ goto out_free_table;
+ }
+
+ /* Append the addresses to the DMA_SLAVE structure */
+ avail = priv->bytes;
+ while (avail > 0) {
+ len = min_t(size_t, avail, PAGE_SIZE);
+ ret = fsl_dma_slave_append(slave, 0xf0003000, len);
+ if (ret) {
+ dev_err(priv->dev, "Unable to append FIFO address\n");
+ goto out_unmap_table;
+ }
+
+ avail -= len;
+ }
+
+ /* Submit the DMA slave */
+ chan->private = slave;
+ tx = chan->device->device_prep_slave_sg(chan, table.sgl, nents,
+ DMA_TO_DEVICE, 0);
+ if (!tx) {
+ dev_err(priv->dev, "Unable to prep DMA_SLAVE transaction\n");
+ ret = -ENOMEM;
+ goto out_unmap_table;
+ }
+
+ /*
+ * Submit the transaction to the DMA controller
+ *
+ * We would leak memory if the submission failed, but that doesn't
+ * happen in the fsldma driver, so it isn't an issue
+ */
+ cookie = tx->tx_submit(tx);
+ if (dma_submit_error(cookie)) {
+ dev_err(priv->dev, "Unable to submit DMA_SLAVE transaction\n");
+ ret = -ENOMEM;
+ goto out_unmap_table;
+ }
+
+ dma_async_memcpy_issue_pending(chan);
+
+ /* Set the total byte count */
+ fpga_set_byte_count(priv->regs, priv->bytes);
+ dev_dbg(priv->dev, "total byte count %u bytes\n", priv->bytes);
+
+ /* Enable the controller for DMA programming */
+ fpga_programmer_enable(priv, true);
+ dev_dbg(priv->dev, "enabled the controller\n");
+
+ /* Wait for the interrupt handler to notify us that programming finished */
+ ret = wait_for_completion_timeout(&priv->completion, 2 * HZ);
+ if (!ret) {
+ dev_err(priv->dev, "Timed out waiting for completion\n");
+ ret = -ETIMEDOUT;
+ goto out_disable_controller;
+ }
+
+ /* Retrieve the status from the interrupt handler */
+ ret = priv->status;
+
+out_disable_controller:
+ fpga_programmer_disable(priv);
+ //chan->device->device_terminate_all(chan);
+out_unmap_table:
+ dma_unmap_sg(priv->dmadev, table.sgl, npages, DMA_TO_DEVICE);
+out_free_table:
+ sg_free_table(&table);
+out_free_slave:
+ fsl_dma_slave_free(slave);
+out_return:
+ return ret;
+}
+
+/*
+ * Interrupt Handling
+ */
+
+static irqreturn_t fpga_interrupt(int irq, void *dev_id)
+{
+ struct fpga_dev *priv = dev_id;
+
+ /* Save the status */
+ priv->status = fpga_config_error(priv->regs) ? -EIO : 0;
+ dev_dbg(priv->dev, "INTERRUPT status %d\n", priv->status);
+ fpga_dump_registers(priv);
+
+ /* Disabling the programmer clears the interrupt */
+ fpga_programmer_disable(priv);
+
+ /* Notify any waiters */
+ complete(&priv->completion);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * SYSFS Helpers
+ */
+
+/*
+ * Reset the FPGA's, deconfiguring them
+ *
+ * LOCKING: priv->lock mutex must be held
+ */
+static int fpga_do_stop(struct fpga_dev *priv)
+{
+ /* Set the led to unprogrammed */
+ ledtrig_fpga_programmed(false);
+
+ /* Pulse the config line to reset the FPGA's */
+ iowrite32be(0x3, priv->regs + FPGA_CONFIG_CONTROL);
+ iowrite32be(0x0, priv->regs + FPGA_CONFIG_CONTROL);
+
+ return 0;
+}
+
+static noinline int fpga_do_program(struct fpga_dev *priv)
+{
+ int ret;
+
+ if (list_empty(&priv->list)) {
+ dev_err(priv->dev, "No data to program\n");
+ return -EINVAL;
+ }
+
+ if (priv->bytes != priv->fw_size) {
+ dev_err(priv->dev, "Incorrect bitfile size: got %zu bytes, "
+ "should be %zu bytes\n",
+ priv->bytes, priv->fw_size);
+ return -EINVAL;
+ }
+
+ if (!fpga_power_enabled(priv)) {
+ dev_err(priv->dev, "Power not enabled\n");
+ return -EINVAL;
+ }
+
+ if (!fpga_power_good(priv)) {
+ dev_err(priv->dev, "Power not good\n");
+ return -EINVAL;
+ }
+
+ /* Set the LED to unprogrammed */
+ ledtrig_fpga_programmed(false);
+
+ /* Try to program the FPGA's using DMA */
+ ret = fpga_program_dma(priv);
+
+ /* If DMA failed or doesn't exist, try with CPU */
+ if (ret) {
+ dev_warn(priv->dev, "Falling back to CPU programming\n");
+ ret = fpga_program_cpu(priv);
+ }
+
+ if (ret) {
+ dev_err(priv->dev, "Unable to program FPGA's\n");
+ return ret;
+ }
+
+ /* Drop the firmware bitfile from memory */
+ fpga_drop_firmware_data(priv);
+
+ dev_dbg(priv->dev, "FPGA programming successful\n");
+ ledtrig_fpga_programmed(true);
+
+ return 0;
+}
+
+/*
+ * File Operations
+ */
+
+static int fpga_open(struct inode *inode, struct file *filp)
+{
+ struct fpga_dev *priv = to_fpga_dev(inode->i_cdev);
+
+ /* We only allow one process at a time */
+ if (mutex_lock_interruptible(&priv->lock))
+ return -ERESTARTSYS;
+
+ filp->private_data = priv;
+
+ /* Free any data left over from a previous open (if any) */
+ fpga_drop_firmware_data(priv);
+
+ return nonseekable_open(inode, filp);
+}
+
+static int fpga_release(struct inode *inode, struct file *filp)
+{
+ struct fpga_dev *priv = filp->private_data;
+
+ mutex_unlock(&priv->lock);
+ return 0;
+}
+
+static ssize_t fpga_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ struct fpga_dev *priv = filp->private_data;
+ struct fpga_fw_elem *elem, *tmp;
+ size_t used, len, copy;
+ struct list_head list;
+ void *mem;
+ int ret;
+
+ /* Disallow firmware images larger than 16MB */
+ if (priv->bytes >= priv->fw_size)
+ return -ENOSPC;
+
+ count = min_t(size_t, priv->fw_size - priv->bytes, count);
+
+ INIT_LIST_HEAD(&list);
+ len = count;
+ used = 0;
+
+ while (len > 0) {
+
+ /* Allocate a list element */
+ elem = fpga_fw_elem_alloc(GFP_KERNEL);
+ if (!elem) {
+ count = used;
+ goto out_success;
+ }
+
+ /* Copy the data from userspace */
+ copy = min_t(size_t, PAGE_SIZE, len);
+ mem = kmap(elem->page);
+ ret = copy_from_user(mem, buf + used, copy);
+ kunmap(elem->page);
+
+ if (ret) {
+ count = -EFAULT;
+ goto out_cleanup;
+ }
+
+ elem->len = copy;
+ list_add_tail(&elem->entry, &list);
+
+ len -= copy;
+ used += copy;
+ }
+
+out_success:
+ if (list_empty(&list))
+ return -ENOMEM;
+
+ list_splice_tail_init(&list, &priv->list);
+ priv->bytes += count;
+
+ *f_pos += count;
+ return count;
+
+out_cleanup:
+ /* Free the last allocated element */
+ fpga_fw_elem_free(elem);
+
+ /* Free all of the elements on the temporary list */
+ list_for_each_entry_safe(elem, tmp, &list, entry) {
+ list_del(&elem->entry);
+ fpga_fw_elem_free(elem);
+ }
+
+ return count;
+}
+
+static const struct file_operations fpga_fops = {
+ .open = fpga_open,
+ .release = fpga_release,
+ .write = fpga_write,
+ .llseek = no_llseek,
+};
+
+/*
+ * Device Attributes
+ */
+
+static ssize_t pfail_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct fpga_dev *priv = dev_get_drvdata(dev);
+ u8 val;
+
+ val = ioread8(priv->regs + CTL_PWR_FAIL);
+ return snprintf(buf, PAGE_SIZE, "0x%.2x\n", val);
+}
+
+static ssize_t pgood_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct fpga_dev *priv = dev_get_drvdata(dev);
+ return snprintf(buf, PAGE_SIZE, "%d\n", fpga_power_good(priv));
+}
+
+static ssize_t penable_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct fpga_dev *priv = dev_get_drvdata(dev);
+ return snprintf(buf, PAGE_SIZE, "%d\n", fpga_power_enabled(priv));
+}
+
+static ssize_t penable_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fpga_dev *priv = dev_get_drvdata(dev);
+ unsigned long val;
+ int ret;
+
+ if (strict_strtoul(buf, 0, &val))
+ return -EINVAL;
+
+ if (val) {
+ ret = fpga_enable_power_supplies(priv);
+ if (ret)
+ return ret;
+ } else {
+ fpga_do_stop(priv);
+ fpga_disable_power_supplies(priv);
+ }
+
+ return count;
+}
+
+static ssize_t program_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct fpga_dev *priv = dev_get_drvdata(dev);
+ return snprintf(buf, PAGE_SIZE, "%d\n", fpga_running(priv));
+}
+
+static ssize_t program_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fpga_dev *priv = dev_get_drvdata(dev);
+ unsigned long val;
+ int ret;
+
+ if (strict_strtoul(buf, 0, &val))
+ return -EINVAL;
+
+ /* We can't have an image writer and be programming simultaneously */
+ if (mutex_lock_interruptible(&priv->lock))
+ return -ERESTARTSYS;
+
+ /* Program or Reset the FPGA's */
+ ret = val ? fpga_do_program(priv) : fpga_do_stop(priv);
+ if (ret)
+ goto out_unlock;
+
+ /* Success */
+ ret = count;
+
+out_unlock:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static DEVICE_ATTR(power_fail, S_IRUGO, pfail_show, NULL);
+static DEVICE_ATTR(power_good, S_IRUGO, pgood_show, NULL);
+static DEVICE_ATTR(power_enable, S_IRUGO | S_IWUGO, penable_show, penable_store);
+static DEVICE_ATTR(program, S_IRUGO | S_IWUGO, program_show, program_store);
+
+static struct attribute *fpga_attributes[] = {
+ &dev_attr_power_fail.attr,
+ &dev_attr_power_good.attr,
+ &dev_attr_power_enable.attr,
+ &dev_attr_program.attr,
+ NULL,
+};
+
+static const struct attribute_group fpga_attr_group = {
+ .attrs = fpga_attributes,
+};
+
+/*
+ * OpenFirmware Device Subsystem
+ */
+
+#define SYS_REG_VERSION 0x00
+#define SYS_REG_GEOGRAPHIC 0x10
+
+static bool dma_filter(struct dma_chan *chan, void *data)
+{
+ /*
+ * DMA Channel #0 is the only acceptable device
+ *
+ * This probably won't survive an unload/load cycle of the Freescale
+ * DMAEngine driver, but that won't be a problem
+ */
+ return chan->chan_id == 0 && chan->device->dev_id == 0;
+}
+
+static int fpga_of_remove(struct platform_device *op)
+{
+ struct fpga_dev *priv = dev_get_drvdata(&op->dev);
+
+ sysfs_remove_group(&priv->dev->kobj, &fpga_attr_group);
+
+ cdev_del(&priv->cdev);
+ free_irq(priv->irq, priv);
+
+ iounmap(priv->immr);
+
+ fpga_disable_power_supplies(priv);
+ iounmap(priv->regs);
+
+ dma_release_channel(priv->chan);
+ carma_device_destroy(priv->devno);
+ unregister_chrdev_region(priv->devno, 1);
+
+ /* Free any firmware image that has not been programmed */
+ fpga_drop_firmware_data(priv);
+
+ mutex_destroy(&priv->lock);
+ kfree(priv);
+
+ return 0;
+}
+
+static int fpga_of_probe(struct platform_device *op, const struct of_device_id *match)
+{
+ struct device_node *of_node = op->dev.of_node;
+ struct fpga_dev *priv;
+ dma_cap_mask_t mask;
+ u32 ver;
+ int ret;
+
+ /* Allocate private data */
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(&op->dev, "Unable to allocate private data\n");
+ ret = -ENOMEM;
+ goto out_return;
+ }
+
+ dev_set_drvdata(&op->dev, priv);
+ mutex_init(&priv->lock);
+ init_completion(&priv->completion);
+ cdev_init(&priv->cdev, &fpga_fops);
+ priv->dmadev = &op->dev;
+ INIT_LIST_HEAD(&priv->list);
+
+ /* Allocate the character device */
+ ret = alloc_chrdev_region(&priv->devno, 0, 1, drv_name);
+ if (ret) {
+ dev_err(&op->dev, "Unable to allocate chardev region\n");
+ goto out_free_priv;
+ }
+
+ /* Allocate the CARMA device */
+ priv->dev = carma_device_create(&op->dev, priv->devno, drv_name);
+ if (IS_ERR(priv->dev)) {
+ dev_err(&op->dev, "Unable to create CARMA device\n");
+ ret = PTR_ERR(priv->dev);
+ goto out_unregister_chrdev_region;
+ }
+
+ dev_set_drvdata(priv->dev, priv);
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_MEMCPY, mask);
+ dma_cap_set(DMA_INTERRUPT, mask);
+ dma_cap_set(DMA_SLAVE, mask);
+
+ /* Get control of DMA channel #0 */
+ priv->chan = dma_request_channel(mask, dma_filter, NULL);
+ if (!priv->chan) {
+ dev_err(&op->dev, "Unable to acquire DMA channel #0\n");
+ ret = -ENODEV;
+ goto out_carma_device_destroy;
+ }
+
+ /* Remap the registers for use */
+ priv->regs = of_iomap(of_node, 0);
+ if (!priv->regs) {
+ dev_err(&op->dev, "Unable to ioremap registers\n");
+ ret = -ENOMEM;
+ goto out_dma_release_channel;
+ }
+
+ /* Remap the IMMR for use */
+ priv->immr = ioremap(get_immrbase(), 0x100000);
+ if (!priv->immr) {
+ dev_err(&op->dev, "Unable to ioremap IMMR\n");
+ ret = -ENOMEM;
+ goto out_unmap_regs;
+ }
+
+ /*
+ * Check that external DMA is configured
+ *
+ * U-Boot does this for us, but we should check it and bail out if
+ * there is a problem. Failing to have this register setup correctly
+ * will cause the DMA controller to transfer a single cacheline
+ * worth of data, then wedge itself.
+ */
+ if ((ioread32be(priv->immr + 0x114) & 0xE00) != 0xE00) {
+ dev_err(&op->dev, "External DMA control not configured\n");
+ ret = -ENODEV;
+ goto out_unmap_immr;
+ }
+
+ /*
+ * Check the CTL-CPLD version
+ *
+ * This drivers uses the CTL-CPLD DATA-FPGA power sequencer, and we
+ * don't want to run on any version of the CTL-CPLD that does not use
+ * a compatible register layout.
+ *
+ * v2: changed register layout, added power sequencer
+ * v3: added glitch filter on the i2c overcurrent/overtemp outputs
+ */
+ ver = ioread8(priv->regs + 0x2000);
+ if (ver != 0x02 && ver != 0x03) {
+ dev_err(&op->dev, "CTL-CPLD is not version 0x02 or 0x03!\n");
+ ret = -ENODEV;
+ goto out_unmap_immr;
+ }
+
+ /*
+ * Set the exact size that the firmware image should be
+ */
+ ver = ioread32be(priv->regs + SYS_REG_VERSION);
+ priv->fw_size = (ver & (1 << 18)) ? FW_SIZE_EP2S130 : FW_SIZE_EP2S90;
+
+ /* Find the correct IRQ number */
+ priv->irq = irq_of_parse_and_map(of_node, 0);
+ ret = request_irq(priv->irq, fpga_interrupt, IRQF_SHARED, drv_name, priv);
+ if (ret) {
+ dev_err(&op->dev, "Unable to request IRQ %d\n", priv->irq);
+ ret = -ENODEV;
+ goto out_unmap_immr;
+ }
+
+ /* Reset and stop the FPGA's, just in case */
+ fpga_do_stop(priv);
+
+ /* Register the character device */
+ ret = cdev_add(&priv->cdev, priv->devno, 1);
+ if (ret) {
+ dev_err(&op->dev, "Unable to add character device\n");
+ goto out_free_irq;
+ }
+
+ /* Create the sysfs files */
+ ret = sysfs_create_group(&priv->dev->kobj, &fpga_attr_group);
+ if (ret) {
+ dev_err(&op->dev, "Unable to create sysfs files\n");
+ goto out_cdev_del;
+ }
+
+ dev_info(priv->dev, "CARMA FPGA Programmer: %s rev%s with %s FPGAs\n",
+ (ver & (1 << 17)) ? "Correlator" : "Digitizer",
+ (ver & (1 << 16)) ? "B" : "A",
+ (ver & (1 << 18)) ? "EP2S130" : "EP2S90");
+
+ return 0;
+
+out_cdev_del:
+ cdev_del(&priv->cdev);
+out_free_irq:
+ free_irq(priv->irq, priv);
+out_unmap_immr:
+ iounmap(priv->immr);
+out_unmap_regs:
+ iounmap(priv->regs);
+out_dma_release_channel:
+ dma_release_channel(priv->chan);
+out_carma_device_destroy:
+ carma_device_destroy(priv->devno);
+out_unregister_chrdev_region:
+ unregister_chrdev_region(priv->devno, 1);
+out_free_priv:
+ mutex_destroy(&priv->lock);
+ kfree(priv);
+out_return:
+ return ret;
+}
+
+static struct of_device_id fpga_of_match[] = {
+ { .compatible = "carma,fpga-programmer", },
+ {},
+};
+
+static struct of_platform_driver fpga_of_driver = {
+ .probe = fpga_of_probe,
+ .remove = fpga_of_remove,
+ .driver = {
+ .name = drv_name,
+ .of_match_table = fpga_of_match,
+ .owner = THIS_MODULE,
+ },
+};
+
+/*
+ * Module Init / Exit
+ */
+
+static int __init fpga_init(void)
+{
+ led_trigger_register_simple("fpga", &ledtrig_fpga);
+ return of_register_platform_driver(&fpga_of_driver);
+}
+
+static void __exit fpga_exit(void)
+{
+ of_unregister_platform_driver(&fpga_of_driver);
+ led_trigger_unregister_simple(ledtrig_fpga);
+}
+
+MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>");
+MODULE_DESCRIPTION("CARMA Board DATA-FPGA Programmer");
+MODULE_LICENSE("GPL");
+
+module_init(fpga_init);
+module_exit(fpga_exit);
--
1.7.1
^ permalink raw reply related
* Re: [PATCH RFCv1 2/3] fpga: add CARMA DATA-FPGA Access Driver
From: Randy Dunlap @ 2010-09-03 22:44 UTC (permalink / raw)
To: Ira W. Snyder; +Cc: linuxppc-dev, linux-kernel
In-Reply-To: <1283553052-23576-3-git-send-email-iws@ovro.caltech.edu>
On Fri, 3 Sep 2010 15:30:51 -0700 Ira W. Snyder wrote:
> This driver allows userspace to access the data processing FPGAs on the
> OVRO CARMA board. It has two modes of operation:
>
> 1) random access
>
> This allows users to poke any DATA-FPGA registers by using mmap to map
> the address region directly into their memory map.
>
> 2) correlation dumping
>
> When correlating, the DATA-FPGA's have special requirements for getting
> the data out of their memory before the next correlation. This nominally
> happens at 64Hz (every 15.625ms). If the data is not dumped before the
> next correlation, data is lost.
>
> The data dumping driver handles buffering up to 1 second worth of
> correlation data from the FPGAs. This lowers the realtime scheduling
> requirements for the userspace process reading the device.
>
> Signed-off-by: Ira W. Snyder <iws@ovro.caltech.edu>
> ---
> drivers/fpga/carma/Kconfig | 9 +
> drivers/fpga/carma/Makefile | 1 +
> drivers/fpga/carma/carma-fpga.c | 1447 +++++++++++++++++++++++++++++++++++++++
> 3 files changed, 1457 insertions(+), 0 deletions(-)
> create mode 100644 drivers/fpga/carma/carma-fpga.c
>
> diff --git a/drivers/fpga/carma/Kconfig b/drivers/fpga/carma/Kconfig
> index 448885e..5592f73 100644
> --- a/drivers/fpga/carma/Kconfig
> +++ b/drivers/fpga/carma/Kconfig
> @@ -18,4 +18,13 @@ config CARMA
> Say Y here to include basic support for the CARMA System Controller
> FPGA. This option allows the other more advanced drivers to be built.
>
> +config CARMA_FPGA
> + tristate "CARMA DATA-FPGA Access Driver"
> + depends on CARMA
> + select VIDEOBUF_DMA_SG
You can't safely select VIDEOBUF_DMA_SG unless MEDIA_SUPPORT && HAS_DMA are
enabled, so I would add
depends on MEDIA_SUPPORT && HAS_DMA
to this config symbol.
> + default n
> + help
> + Say Y here to include support for communicating with the data
> + processing FPGAs on the CARMA board.
> +
> endif # FPGA_DRIVERS
> diff --git a/drivers/fpga/carma/carma-fpga.c b/drivers/fpga/carma/carma-fpga.c
> new file mode 100644
> index 0000000..ab1b536
> --- /dev/null
> +++ b/drivers/fpga/carma/carma-fpga.c
> @@ -0,0 +1,1447 @@
> +/*
> + * Free a single data buffer and all allocated pages
> + *
> + * This will free all of the pages allocated to the given data buffer, and
> + * then free the structure itself
> + *
> + * @dev: the DMA device to map for
> + * @buf: the buffer to free
> + */
> +static void data_free_buffer(struct device *dev, struct data_buf *buf)
> +{
The comments above are OK, but please don't add the (doxygen?) style comments
as below (this is just one example of multiple occurrences).
Specifically, the "@param" parts.
It would be better to use the style above (if not using kernel-doc notation).
> +/*
> + * Prepare and submit a DMA_SLAVE transaction for a correlation data buffer
> + *
> + * LOCKING: must hold dev->lock
> + * CONTEXT: hardirq only
> + *
> + * @param priv the driver's private data structure
> + * @param buf the data buffer to DMA into
> + * @return 0 on success, -ERRNO otherwise
> + */
> +static int data_submit_dma(struct fpga_device *priv, struct data_buf *buf)
> +{
---
~Randy
*** Remember to use Documentation/SubmitChecklist when testing your code ***
^ permalink raw reply
* Re: [PATCH] fsldma: fix missing header include
From: Andrew Morton @ 2010-09-03 23:40 UTC (permalink / raw)
To: Ira W. Snyder; +Cc: Dan Williams, linuxppc-dev, linux-kernel
In-Reply-To: <20100902194752.GA24861@ovro.caltech.edu>
On Thu, 2 Sep 2010 12:47:52 -0700
"Ira W. Snyder" <iws@ovro.caltech.edu> wrote:
> The slab.h header is required to use the kmalloc() family of functions.
> Due to recent kernel changes, this header must be directly included by
> code that calls into the memory allocator.
>
> Signed-off-by: Ira W. Snyder <iws@ovro.caltech.edu>
> ---
>
> Without this patch, any code which includes this header fails to build.
>
> arch/powerpc/include/asm/fsldma.h | 1 +
> 1 files changed, 1 insertions(+), 0 deletions(-)
>
> diff --git a/arch/powerpc/include/asm/fsldma.h b/arch/powerpc/include/asm/fsldma.h
> index a67aeed..debc5ed 100644
> --- a/arch/powerpc/include/asm/fsldma.h
> +++ b/arch/powerpc/include/asm/fsldma.h
> @@ -11,6 +11,7 @@
> #ifndef __ARCH_POWERPC_ASM_FSLDMA_H__
> #define __ARCH_POWERPC_ASM_FSLDMA_H__
>
> +#include <linux/slab.h>
> #include <linux/dmaengine.h>
>
It also needs list.h, but appears to get it via sheer luck.
The functions in that header simply shouldn't have been inlined.
It's peculiar that fsl_dma_slave_append() hardwires GFP_ATOMIC, whereas
fsl_dma_slave_alloc() takes a gfp_t.
^ permalink raw reply
* Re: [PATCH] fsldma: fix missing header include
From: Ira W. Snyder @ 2010-09-04 0:47 UTC (permalink / raw)
To: Andrew Morton; +Cc: Dan Williams, linuxppc-dev, linux-kernel
In-Reply-To: <20100903164033.6ae37727.akpm@linux-foundation.org>
On Fri, Sep 03, 2010 at 04:40:33PM -0700, Andrew Morton wrote:
> On Thu, 2 Sep 2010 12:47:52 -0700
> "Ira W. Snyder" <iws@ovro.caltech.edu> wrote:
>
> > The slab.h header is required to use the kmalloc() family of functions.
> > Due to recent kernel changes, this header must be directly included by
> > code that calls into the memory allocator.
> >
> > Signed-off-by: Ira W. Snyder <iws@ovro.caltech.edu>
> > ---
> >
> > Without this patch, any code which includes this header fails to build.
> >
> > arch/powerpc/include/asm/fsldma.h | 1 +
> > 1 files changed, 1 insertions(+), 0 deletions(-)
> >
> > diff --git a/arch/powerpc/include/asm/fsldma.h b/arch/powerpc/include/asm/fsldma.h
> > index a67aeed..debc5ed 100644
> > --- a/arch/powerpc/include/asm/fsldma.h
> > +++ b/arch/powerpc/include/asm/fsldma.h
> > @@ -11,6 +11,7 @@
> > #ifndef __ARCH_POWERPC_ASM_FSLDMA_H__
> > #define __ARCH_POWERPC_ASM_FSLDMA_H__
> >
> > +#include <linux/slab.h>
> > #include <linux/dmaengine.h>
> >
>
> It also needs list.h, but appears to get it via sheer luck.
>
> The functions in that header simply shouldn't have been inlined.
>
> It's peculiar that fsl_dma_slave_append() hardwires GFP_ATOMIC, whereas
> fsl_dma_slave_alloc() takes a gfp_t.
>
Would you like a patch that moves the functions to drivers/dma/fsldma.c
and EXPORT_SYMBOL_GPL()'s them? I don't know the consequences to doing
so, which is why I avoided it when I wrote
arch/powerpc/include/asm/fsldma.h.
Thanks,
Ira
^ permalink raw reply
* Re: [PATCH] fsldma: fix missing header include
From: Andrew Morton @ 2010-09-04 0:58 UTC (permalink / raw)
To: Ira W. Snyder; +Cc: Dan Williams, linuxppc-dev, linux-kernel
In-Reply-To: <20100904004724.GA4085@ovro.caltech.edu>
On Fri, 3 Sep 2010 17:47:24 -0700 "Ira W. Snyder" <iws@ovro.caltech.edu> wrote:
> On Fri, Sep 03, 2010 at 04:40:33PM -0700, Andrew Morton wrote:
> > On Thu, 2 Sep 2010 12:47:52 -0700
> > "Ira W. Snyder" <iws@ovro.caltech.edu> wrote:
> >
> > > The slab.h header is required to use the kmalloc() family of functions.
> > > Due to recent kernel changes, this header must be directly included by
> > > code that calls into the memory allocator.
> > >
> > > Signed-off-by: Ira W. Snyder <iws@ovro.caltech.edu>
> > > ---
> > >
> > > Without this patch, any code which includes this header fails to build.
> > >
> > > arch/powerpc/include/asm/fsldma.h | 1 +
> > > 1 files changed, 1 insertions(+), 0 deletions(-)
> > >
> > > diff --git a/arch/powerpc/include/asm/fsldma.h b/arch/powerpc/include/asm/fsldma.h
> > > index a67aeed..debc5ed 100644
> > > --- a/arch/powerpc/include/asm/fsldma.h
> > > +++ b/arch/powerpc/include/asm/fsldma.h
> > > @@ -11,6 +11,7 @@
> > > #ifndef __ARCH_POWERPC_ASM_FSLDMA_H__
> > > #define __ARCH_POWERPC_ASM_FSLDMA_H__
> > >
> > > +#include <linux/slab.h>
> > > #include <linux/dmaengine.h>
> > >
> >
> > It also needs list.h, but appears to get it via sheer luck.
> >
> > The functions in that header simply shouldn't have been inlined.
> >
> > It's peculiar that fsl_dma_slave_append() hardwires GFP_ATOMIC, whereas
> > fsl_dma_slave_alloc() takes a gfp_t.
> >
>
> Would you like a patch that moves the functions to drivers/dma/fsldma.c
> and EXPORT_SYMBOL_GPL()'s them?
Sure, thanks. Then I can harrass Dan with it ;)
> I don't know the consequences to doing
> so, which is why I avoided it when I wrote
> arch/powerpc/include/asm/fsldma.h.
The minimal fix for the bug was appropriate.
^ permalink raw reply
* [PATCH] arch/powerpc/platforms/52xx/efika.c: Add of_node_put to avoid memory leak
From: Julia Lawall @ 2010-09-04 7:13 UTC (permalink / raw)
To: Grant Likely
Cc: devicetree-discuss, kernel-janitors, linux-kernel, Paul Mackerras,
linuxppc-dev
This function is implemented as though the function of_get_next_child does
not increment the reference count of its result, but actually it does.
Thus the patch adds of_node_put in error handling code and drops a call to
of_node_get.
The semantic match that finds this problem is as follows:
(http://coccinelle.lip6.fr/)
// <smpl>
@r exists@
local idexpression x;
expression E1;
position p1,p2;
@@
x@p1 = of_get_next_child(...);
... when != x = E1
of_node_get@p2(x)
@script:python@
p1 << r.p1;
p2 << r.p2;
@@
cocci.print_main("call",p1)
cocci.print_secs("get",p2)
// </smpl>
Signed-off-by: Julia Lawall <julia@diku.dk>
---
arch/powerpc/platforms/52xx/efika.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/arch/powerpc/platforms/52xx/efika.c b/arch/powerpc/platforms/52xx/efika.c
index 45c0cb9..18c1048 100644
--- a/arch/powerpc/platforms/52xx/efika.c
+++ b/arch/powerpc/platforms/52xx/efika.c
@@ -99,7 +99,7 @@ static void __init efika_pcisetup(void)
if (bus_range == NULL || len < 2 * sizeof(int)) {
printk(KERN_WARNING EFIKA_PLATFORM_NAME
": Can't get bus-range for %s\n", pcictrl->full_name);
- return;
+ goto out_put;
}
if (bus_range[1] == bus_range[0])
@@ -111,12 +111,12 @@ static void __init efika_pcisetup(void)
printk(" controlled by %s\n", pcictrl->full_name);
printk("\n");
- hose = pcibios_alloc_controller(of_node_get(pcictrl));
+ hose = pcibios_alloc_controller(pcictrl);
if (!hose) {
printk(KERN_WARNING EFIKA_PLATFORM_NAME
": Can't allocate PCI controller structure for %s\n",
pcictrl->full_name);
- return;
+ goto out_put;
}
hose->first_busno = bus_range[0];
@@ -124,6 +124,9 @@ static void __init efika_pcisetup(void)
hose->ops = &rtas_pci_ops;
pci_process_bridge_OF_ranges(hose, pcictrl, 0);
+ return;
+out_put:
+ of_node_put(pcictrl);
}
#else
^ permalink raw reply related
* [PATCH 2/2] arch/powerpc/kernel/irq.c: Add of_node_put to avoid memory leak
From: Julia Lawall @ 2010-09-04 10:12 UTC (permalink / raw)
To: Benjamin Herrenschmidt
Cc: Paul Mackerras, kernel-janitors, linuxppc-dev, linux-kernel
In this case, a device_node structure is stored in another structure that
is then freed without first decrementing the reference count of the
device_node structure.
The semantic match that finds this problem is as follows:
(http://coccinelle.lip6.fr/)
// <smpl>
@r exists@
expression x;
identifier f;
position p1,p2;
@@
x@p1->f = \(of_find_node_by_path\|of_find_node_by_name\|of_find_node_by_phandle\|of_get_parent\|of_get_next_parent\|of_get_next_child\|of_find_compatible_node\|of_match_node\|of_find_node_by_type\|of_find_node_with_property\|of_find_matching_node\|of_parse_phandle\|of_node_get\)(...);
... when != of_node_put(x)
kfree@p2(x)
@script:python@
p1 << r.p1;
p2 << r.p2;
@@
cocci.print_main("call",p1)
cocci.print_secs("free",p2)
// </smpl>
Signed-off-by: Julia Lawall <julia@diku.dk>
---
arch/powerpc/kernel/irq.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c
index 4a65386..0a5bbe5 100644
--- a/arch/powerpc/kernel/irq.c
+++ b/arch/powerpc/kernel/irq.c
@@ -587,8 +587,10 @@ struct irq_host *irq_alloc_host(struct device_node *of_node,
* this will be fixed once slab is made available early
* instead of the current cruft
*/
- if (mem_init_done)
+ if (mem_init_done) {
+ of_node_put(host->of_node);
kfree(host);
+ }
return NULL;
}
irq_map[0].host = host;
^ permalink raw reply related
* [PATCH 1/2] drivers/net/fs_enet/fs_enet-main.c: Add of_node_put to avoid memory leak
From: Julia Lawall @ 2010-09-04 10:12 UTC (permalink / raw)
To: Pantelis Antoniou
Cc: netdev, devicetree-discuss, kernel-janitors, linux-kernel,
Vitaly Bordug, linuxppc-dev
In this case, a device_node structure is stored in another structure that
is then freed without first decrementing the reference count of the
device_node structure.
The semantic match that finds this problem is as follows:
(http://coccinelle.lip6.fr/)
// <smpl>
@r exists@
expression x;
identifier f;
position p1,p2;
@@
x@p1->f = \(of_find_node_by_path\|of_find_node_by_name\|of_find_node_by_phandle\|of_get_parent\|of_get_next_parent\|of_get_next_child\|of_find_compatible_node\|of_match_node\|of_find_node_by_type\|of_find_node_with_property\|of_find_matching_node\|of_parse_phandle\|of_node_get\)(...);
... when != of_node_put(x)
kfree@p2(x)
@script:python@
p1 << r.p1;
p2 << r.p2;
@@
cocci.print_main("call",p1)
cocci.print_secs("free",p2)
// </smpl>
Signed-off-by: Julia Lawall <julia@diku.dk>
---
drivers/net/fs_enet/fs_enet-main.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/net/fs_enet/fs_enet-main.c b/drivers/net/fs_enet/fs_enet-main.c
index d6e3111..d684f18 100644
--- a/drivers/net/fs_enet/fs_enet-main.c
+++ b/drivers/net/fs_enet/fs_enet-main.c
@@ -1036,7 +1036,7 @@ static int __devinit fs_enet_probe(struct platform_device *ofdev,
ndev = alloc_etherdev(privsize);
if (!ndev) {
ret = -ENOMEM;
- goto out_free_fpi;
+ goto out_put;
}
SET_NETDEV_DEV(ndev, &ofdev->dev);
@@ -1099,6 +1099,7 @@ out_cleanup_data:
out_free_dev:
free_netdev(ndev);
dev_set_drvdata(&ofdev->dev, NULL);
+out_put:
of_node_put(fpi->phy_node);
out_free_fpi:
kfree(fpi);
^ permalink raw reply related
* Re: [PATCH 1/2] drivers/net/fs_enet/fs_enet-main.c: Add of_node_put to avoid memory leak
From: Wolfram Sang @ 2010-09-04 15:48 UTC (permalink / raw)
To: Julia Lawall
Cc: netdev, devicetree-discuss, kernel-janitors, linux-kernel,
Vitaly Bordug, linuxppc-dev
In-Reply-To: <1283595164-29146-1-git-send-email-julia@diku.dk>
[-- Attachment #1: Type: text/plain, Size: 2141 bytes --]
On Sat, Sep 04, 2010 at 12:12:43PM +0200, Julia Lawall wrote:
> In this case, a device_node structure is stored in another structure that
> is then freed without first decrementing the reference count of the
> device_node structure.
>
> The semantic match that finds this problem is as follows:
> (http://coccinelle.lip6.fr/)
>
> // <smpl>
> @r exists@
> expression x;
> identifier f;
> position p1,p2;
> @@
>
> x@p1->f = \(of_find_node_by_path\|of_find_node_by_name\|of_find_node_by_phandle\|of_get_parent\|of_get_next_parent\|of_get_next_child\|of_find_compatible_node\|of_match_node\|of_find_node_by_type\|of_find_node_with_property\|of_find_matching_node\|of_parse_phandle\|of_node_get\)(...);
> ... when != of_node_put(x)
> kfree@p2(x)
>
> @script:python@
> p1 << r.p1;
> p2 << r.p2;
> @@
> cocci.print_main("call",p1)
> cocci.print_secs("free",p2)
> // </smpl>
>
> Signed-off-by: Julia Lawall <julia@diku.dk>
Acked-by: Wolfram Sang <w.sang@pengutronix.de>
>
> ---
> drivers/net/fs_enet/fs_enet-main.c | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/net/fs_enet/fs_enet-main.c b/drivers/net/fs_enet/fs_enet-main.c
> index d6e3111..d684f18 100644
> --- a/drivers/net/fs_enet/fs_enet-main.c
> +++ b/drivers/net/fs_enet/fs_enet-main.c
> @@ -1036,7 +1036,7 @@ static int __devinit fs_enet_probe(struct platform_device *ofdev,
> ndev = alloc_etherdev(privsize);
> if (!ndev) {
> ret = -ENOMEM;
> - goto out_free_fpi;
> + goto out_put;
> }
>
> SET_NETDEV_DEV(ndev, &ofdev->dev);
> @@ -1099,6 +1099,7 @@ out_cleanup_data:
> out_free_dev:
> free_netdev(ndev);
> dev_set_drvdata(&ofdev->dev, NULL);
> +out_put:
> of_node_put(fpi->phy_node);
> out_free_fpi:
> kfree(fpi);
>
> _______________________________________________
> devicetree-discuss mailing list
> devicetree-discuss@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/devicetree-discuss
--
Pengutronix e.K. | Wolfram Sang |
Industrial Linux Solutions | http://www.pengutronix.de/ |
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 197 bytes --]
^ permalink raw reply
* [PATCH 4/8] drivers/i2c/busses/i2c-pasemi.c: Fix unsigned return type
From: Julia Lawall @ 2010-09-05 19:00 UTC (permalink / raw)
To: Olof Johansson
Cc: kernel-janitors, linux-kernel, linux-i2c,
Ben Dooks (embedded platforms), Jean Delvare (PC drivers, core),
linuxppc-dev
In-Reply-To: <1283713226-8429-1-git-send-email-julia@diku.dk>
The function has an unsigned return type, but returns a negative constant
to indicate an error condition. The result of calling the function is
always stored in a variable of type (signed) int, and thus unsigned can be
dropped from the return type.
A sematic match that finds this problem is as follows:
(http://coccinelle.lip6.fr/)
// <smpl>
@exists@
identifier f;
constant C;
@@
unsigned f(...)
{ <+...
* return -C;
...+> }
// </smpl>
Signed-off-by: Julia Lawall <julia@diku.dk>
---
drivers/i2c/busses/i2c-pasemi.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/i2c/busses/i2c-pasemi.c b/drivers/i2c/busses/i2c-pasemi.c
index 4174101..837b8c1 100644
--- a/drivers/i2c/busses/i2c-pasemi.c
+++ b/drivers/i2c/busses/i2c-pasemi.c
@@ -88,7 +88,7 @@ static void pasemi_smb_clear(struct pasemi_smbus *smbus)
reg_write(smbus, REG_SMSTA, status);
}
-static unsigned int pasemi_smb_waitready(struct pasemi_smbus *smbus)
+static int pasemi_smb_waitready(struct pasemi_smbus *smbus)
{
int timeout = 10;
unsigned int status;
^ permalink raw reply related
* Re: [PATCH] APM821xx: Add support for new SoC APM821xx
From: Olof Johansson @ 2010-09-05 22:23 UTC (permalink / raw)
To: Tirumala Marri; +Cc: linuxppc-dev
In-Reply-To: <197d4509c0b1206ce2d686c03701a6b4@mail.gmail.com>
On Fri, Sep 03, 2010 at 01:38:46PM -0700, Tirumala Marri wrote:
> > >APM821xx is Applied Micro Circuits Corporations naming convention for
> > >new line of SoCs.
> >
> > So is it a 440x6 core then? Or what core is inside the SoC?
> [Marri] It is 464 core.
Then the device tree identifier, and the cpu setup functions, etc, should indicate
464, not APM821xx.
Also, why add yet another defconfig? Isn't the eval board similar to
many others and can be supported with just a tweak of some existing
common defconfig instead?
-Olof
^ permalink raw reply
* RE: [PATCH 1/3][MTD] P4080/eLBC: Make Freescale elbc interrupt common to elbc devices
From: Zang Roy-R61911 @ 2010-09-06 3:38 UTC (permalink / raw)
To: Anton Vorontsov
Cc: Wood Scott-B07421, Lan Chunhe-B25806, linuxppc-dev, linux-mtd,
akpm, Gala Kumar-B11780
In-Reply-To: <20100903112755.GA11847@oksana.dev.rtsoft.ru>
DQoNCj4gLS0tLS1PcmlnaW5hbCBNZXNzYWdlLS0tLS0NCj4gRnJvbTogQW50b24gVm9yb250c292
IFttYWlsdG86Y2JvdWF0bWFpbHJ1QGdtYWlsLmNvbV0NCj4gU2VudDogRnJpZGF5LCBTZXB0ZW1i
ZXIgMDMsIDIwMTAgMTk6MjggUE0NCj4gVG86IFphbmcgUm95LVI2MTkxMQ0KPiBDYzogbGludXgt
bXRkQGxpc3RzLmluZnJhZGVhZC5vcmc7IExhbiBDaHVuaGUtQjI1ODA2OyBsaW51eHBwYy1kZXZA
b3psYWJzLm9yZzsNCj4gYWtwbUBsaW51eC1mb3VuZGF0aW9uLm9yZzsgR2FsYSBLdW1hci1CMTE3
ODANCj4gU3ViamVjdDogUmU6IFtQQVRDSCAxLzNdW01URF0gUDQwODAvZUxCQzogTWFrZSBGcmVl
c2NhbGUgZWxiYyBpbnRlcnJ1cHQgY29tbW9uDQo+IHRvIGVsYmMgZGV2aWNlcw0KPiANCj4gT24g
RnJpLCBBdWcgMDYsIDIwMTAgYXQgMTA6NTE6MzRBTSArMDgwMCwgUm95IFphbmcgd3JvdGU6DQo+
ID4gRnJvbTogTGFuIENodW5oZS1CMjU4MDYgPGIyNTgwNkBmcmVlc2NhbGUuY29tPg0KPiA+DQo+
ID4gTW92ZSBGcmVlc2NhbGUgZWxiYyBpbnRlcnJ1cHQgZnJvbSBuYW5kIGRpcnZlciB0byBlbGJj
IGRyaXZlci4NCj4gPiBUaGVuIGFsbCBlbGJjIGRldmljZXMgY2FuIHVzZSB0aGUgaW50ZXJydXB0
IGluc3RlYWQgb2YgT05MWSBuYW5kLg0KPiA+DQo+ID4gU2lnbmVkLW9mZi1ieTogTGFuIENodW5o
ZS1CMjU4MDYgPGIyNTgwNkBmcmVlc2NhbGUuY29tPg0KPiA+IFNpZ25lZC1vZmYtYnk6IFJveSBa
YW5nIDx0aWUtZmVpLnphbmdAZnJlZXNjYWxlLmNvbT4NCj4gPiAtLS0NCj4gPiBzZW5kIHRoZSBw
YXRjaCB0byBsaW51eC1tdGRAbGlzdHMuaW5mcmFkZWFkLm9yZw0KPiA+IGl0IGhhcyBiZWVuIHBv
c3RlZCB0byBsaW51eHBwYy1kZXZAb3psYWJzLm9yZyBhbmQgZG8gbm90IGdldCBhbnkgY29tbWVu
dC4NCj4gPg0KPiA+ICBhcmNoL3Bvd2VycGMvS2NvbmZpZyAgICAgICAgICAgICAgIHwgICAgNyAr
LQ0KPiA+ICBhcmNoL3Bvd2VycGMvaW5jbHVkZS9hc20vZnNsX2xiYy5oIHwgICAzNCArKysrKy0N
Cj4gPiAgYXJjaC9wb3dlcnBjL3N5c2Rldi9mc2xfbGJjLmMgICAgICB8ICAyNTQgKysrKysrKysr
KysrKysrKysrKysrKysrKysrKysrLS0tDQo+IC0tLQ0KPiA+ICAzIGZpbGVzIGNoYW5nZWQsIDI1
MyBpbnNlcnRpb25zKCspLCA0MiBkZWxldGlvbnMoLSkNCj4gPg0KPiA+IGRpZmYgLS1naXQgYS9h
cmNoL3Bvd2VycGMvS2NvbmZpZyBiL2FyY2gvcG93ZXJwYy9LY29uZmlnDQo+ID4gaW5kZXggMjAz
MWEyOC4uNTE1NWI1MyAxMDA2NDQNCj4gPiAtLS0gYS9hcmNoL3Bvd2VycGMvS2NvbmZpZw0KPiA+
ICsrKyBiL2FyY2gvcG93ZXJwYy9LY29uZmlnDQo+ID4gQEAgLTcwMyw5ICs3MDMsMTIgQEAgY29u
ZmlnIDR4eF9TT0MNCj4gPiAgCWJvb2wNCj4gPg0KPiA+ICBjb25maWcgRlNMX0xCQw0KPiA+IC0J
Ym9vbA0KPiA+ICsJYm9vbCAiRnJlZXNjYWxlIExvY2FsIEJ1cyBzdXBwb3J0Ig0KPiA+ICsJZGVw
ZW5kcyBvbiBGU0xfU09DDQo+ID4gIAloZWxwDQo+ID4gLQkgIEZyZWVzY2FsZSBMb2NhbGJ1cyBz
dXBwb3J0DQo+ID4gKwkgIEVuYWJsZXMgcmVwb3J0aW5nIG9mIGVycm9ycyBmcm9tIHRoZSBGcmVl
c2NhbGUgbG9jYWwgYnVzDQo+ID4gKwkgIGNvbnRyb2xsZXIuICBBbHNvIGNvbnRhaW5zIHNvbWUg
Y29tbW9uIGNvZGUgdXNlZCBieQ0KPiA+ICsJICBkcml2ZXJzIGZvciBzcGVjaWZpYyBsb2NhbCBi
dXMgcGVyaXBoZXJhbHMuDQo+ID4NCj4gPiAgY29uZmlnIEZTTF9HVE0NCj4gPiAgCWJvb2wNCj4g
Wy4uLl0NCj4gPiBkaWZmIC0tZ2l0IGEvYXJjaC9wb3dlcnBjL3N5c2Rldi9mc2xfbGJjLmMgYi9h
cmNoL3Bvd2VycGMvc3lzZGV2L2ZzbF9sYmMuYw0KPiA+IGluZGV4IGRjZWI4ZDEuLjljOWU0NGYg
MTAwNjQ0DQo+ID4gLS0tIGEvYXJjaC9wb3dlcnBjL3N5c2Rldi9mc2xfbGJjLmMNCj4gPiArKysg
Yi9hcmNoL3Bvd2VycGMvc3lzZGV2L2ZzbF9sYmMuYw0KPiA+IEBAIC01LDYgKzUsMTAgQEANCj4g
PiAgICoNCj4gPiAgICogQXV0aG9yOiBBbnRvbiBWb3JvbnRzb3YgPGF2b3JvbnRzb3ZAcnUubXZp
c3RhLmNvbT4NCj4gPiAgICoNCj4gPiArICogQ29weXJpZ2h0IChjKSAyMDEwIEZyZWVzY2FsZSBT
ZW1pY29uZHVjdG9yDQo+ID4gKyAqDQo+ID4gKyAqIEF1dGhvcnM6IEphY2sgTGFuIDxKYWNrLkxh
bkBmcmVlc2NhbGUuY29tPg0KPiANCj4gV291bGQgYmUgcHJldHRpZXIgaWYgeW91J2QgZ3JvdXAg
Y29weXJpZ2h0IGFuZCBhdXRob3JzaGlwIG5vdGljZXMuDQo+IEkuZS4NCj4gDQo+IENvcHlyaWdo
dCAyMDA4IE1WDQo+IENvcHlyaWdodCAyMDEwIEZTTA0KPiANCj4gQXV0aG9yczogQW50b24NCj4g
ICAgICAgICAgSmFjaw0KVGhlbiBob3cgdG8gcmVmbGVjdCB0aGUgcmVsYXRpb25zaGlwIG9mIHRo
ZSBBdXRob3IgYW5kIHRoZSBjb21wYW55PyBmcm9tIGVtYWlsIGFkZHJlc3M/DQoNCj4gDQo+ID4g
KyAqDQo+ID4gICAqIFRoaXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlz
dHJpYnV0ZSBpdCBhbmQvb3IgbW9kaWZ5DQo+ID4gICAqIGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0
aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5DQo+ID4gICAqIHRo
ZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb247IGVpdGhlciB2ZXJzaW9uIDIgb2YgdGhlIExpY2Vu
c2UsIG9yDQo+ID4gQEAgLTIzLDM1ICsyNyw4IEBADQo+ID4gICNpbmNsdWRlIDxhc20vZnNsX2xi
Yy5oPg0KPiA+DQo+ID4gIHN0YXRpYyBzcGlubG9ja190IGZzbF9sYmNfbG9jayA9IF9fU1BJTl9M
T0NLX1VOTE9DS0VEKGZzbF9sYmNfbG9jayk7DQo+ID4gLXN0YXRpYyBzdHJ1Y3QgZnNsX2xiY19y
ZWdzIF9faW9tZW0gKmZzbF9sYmNfcmVnczsNCj4gPiAtDQo+ID4gLXN0YXRpYyBjaGFyIF9faW5p
dGRhdGEgKmNvbXBhdF9sYmNbXSA9IHsNCj4gPiAtCSJmc2wscHEyLWxvY2FsYnVzIiwNCj4gPiAt
CSJmc2wscHEycHJvLWxvY2FsYnVzIiwNCj4gPiAtCSJmc2wscHEzLWxvY2FsYnVzIiwNCj4gPiAt
CSJmc2wsZWxiYyIsDQo+ID4gLX07DQo+ID4gLQ0KPiA+IC1zdGF0aWMgaW50IF9faW5pdCBmc2xf
bGJjX2luaXQodm9pZCkNCj4gPiAtew0KPiA+IC0Jc3RydWN0IGRldmljZV9ub2RlICpsYnVzOw0K
PiA+IC0JaW50IGk7DQo+ID4gLQ0KPiA+IC0JZm9yIChpID0gMDsgaSA8IEFSUkFZX1NJWkUoY29t
cGF0X2xiYyk7IGkrKykgew0KPiA+IC0JCWxidXMgPSBvZl9maW5kX2NvbXBhdGlibGVfbm9kZShO
VUxMLCBOVUxMLCBjb21wYXRfbGJjW2ldKTsNCj4gPiAtCQlpZiAobGJ1cykNCj4gPiAtCQkJZ290
byBmb3VuZDsNCj4gPiAtCX0NCj4gPiAtCXJldHVybiAtRU5PREVWOw0KPiA+IC0NCj4gPiAtZm91
bmQ6DQo+ID4gLQlmc2xfbGJjX3JlZ3MgPSBvZl9pb21hcChsYnVzLCAwKTsNCj4gPiAtCW9mX25v
ZGVfcHV0KGxidXMpOw0KPiA+IC0JaWYgKCFmc2xfbGJjX3JlZ3MpDQo+ID4gLQkJcmV0dXJuIC1F
Tk9NRU07DQo+ID4gLQlyZXR1cm4gMDsNCj4gPiAtfQ0KPiA+IC1hcmNoX2luaXRjYWxsKGZzbF9s
YmNfaW5pdCk7DQo+ID4gK3N0cnVjdCBmc2xfbGJjX2N0cmwgKmZzbF9sYmNfY3RybF9kZXY7DQo+
ID4gK0VYUE9SVF9TWU1CT0woZnNsX2xiY19jdHJsX2Rldik7DQo+ID4NCj4gPiAgLyoqDQo+ID4g
ICAqIGZzbF9sYmNfZmluZCAtIGZpbmQgTG9jYWxidXMgYmFuaw0KPiA+IEBAIC02NiwxMiArNDMs
MTIgQEAgaW50IGZzbF9sYmNfZmluZChwaHlzX2FkZHJfdCBhZGRyX2Jhc2UpDQo+ID4gIHsNCj4g
PiAgCWludCBpOw0KPiA+DQo+ID4gLQlpZiAoIWZzbF9sYmNfcmVncykNCj4gPiArCWlmICghZnNs
X2xiY19jdHJsX2RldiB8fCAhZnNsX2xiY19jdHJsX2Rldi0+cmVncykNCj4gPiAgCQlyZXR1cm4g
LUVOT0RFVjsNCj4gPg0KPiA+IC0JZm9yIChpID0gMDsgaSA8IEFSUkFZX1NJWkUoZnNsX2xiY19y
ZWdzLT5iYW5rKTsgaSsrKSB7DQo+ID4gLQkJX19iZTMyIGJyID0gaW5fYmUzMigmZnNsX2xiY19y
ZWdzLT5iYW5rW2ldLmJyKTsNCj4gPiAtCQlfX2JlMzIgb3IgPSBpbl9iZTMyKCZmc2xfbGJjX3Jl
Z3MtPmJhbmtbaV0ub3IpOw0KPiA+ICsJZm9yIChpID0gMDsgaSA8IEFSUkFZX1NJWkUoZnNsX2xi
Y19jdHJsX2Rldi0+cmVncy0+YmFuayk7IGkrKykgew0KPiA+ICsJCV9fYmUzMiBiciA9IGluX2Jl
MzIoJmZzbF9sYmNfY3RybF9kZXYtPnJlZ3MtPmJhbmtbaV0uYnIpOw0KPiA+ICsJCV9fYmUzMiBv
ciA9IGluX2JlMzIoJmZzbF9sYmNfY3RybF9kZXYtPnJlZ3MtPmJhbmtbaV0ub3IpOw0KPiANCj4g
QSBkZWRpY2F0ZWQgdmFyaWFibGUgZm9yIHJlZ3Mgd291bGQgbWFrZSB0aGlzIG11Y2ggbW9yZSBy
ZWFkYWJsZT8NClllcy4gY29uc2lkZXJpbmcgdGhlIGZvbGxvd2luZyBjb25kaXRpb24gbGluZS4N
Cj4gDQo+ID4NCj4gPiAgCQlpZiAoYnIgJiBCUl9WICYmIChiciAmIG9yICYgQlJfQkEpID09IGFk
ZHJfYmFzZSkNCj4gPiAgCQkJcmV0dXJuIGk7DQo+ID4gQEAgLTk5LDE3ICs3NiwyMCBAQCBpbnQg
ZnNsX3VwbV9maW5kKHBoeXNfYWRkcl90IGFkZHJfYmFzZSwgc3RydWN0IGZzbF91cG0NCj4gKnVw
bSkNCj4gPiAgCWlmIChiYW5rIDwgMCkNCj4gPiAgCQlyZXR1cm4gYmFuazsNCj4gPg0KPiA+IC0J
YnIgPSBpbl9iZTMyKCZmc2xfbGJjX3JlZ3MtPmJhbmtbYmFua10uYnIpOw0KPiA+ICsJaWYgKCFm
c2xfbGJjX2N0cmxfZGV2IHx8ICFmc2xfbGJjX2N0cmxfZGV2LT5yZWdzKQ0KPiA+ICsJCXJldHVy
biAtRU5PREVWOw0KPiA+ICsNCj4gPiArCWJyID0gaW5fYmUzMigmZnNsX2xiY19jdHJsX2Rldi0+
cmVncy0+YmFua1tiYW5rXS5icik7DQo+ID4NCj4gPiAgCXN3aXRjaCAoYnIgJiBCUl9NU0VMKSB7
DQo+ID4gIAljYXNlIEJSX01TX1VQTUE6DQo+ID4gLQkJdXBtLT5teG1yID0gJmZzbF9sYmNfcmVn
cy0+bWFtcjsNCj4gPiArCQl1cG0tPm14bXIgPSAmZnNsX2xiY19jdHJsX2Rldi0+cmVncy0+bWFt
cjsNCj4gDQo+IERpdHRvLCBhIHZlcnkgcmVwZXRpdGl2ZSBzdHVmZiwgZGVzaXJlcyBhIHZhcmlh
YmxlIGZvciByZWdzPw0KQnV0IHRoZSBmYWN0IGlzIHRoYXQgdGhlIHZhcmlhYmxlIHJlcHJlc2Vu
dHMgZGlmZmVyZW50IHJlZyBhZGRyZXNzIGFjY29yZGluZyB0byB0aGUgY29uZGl0aW9uLiBJdCB3
aWxsIGJlIHVnbHkgdG8gdXNlIHRoZSByZWcgYWRkcmVzcyBkaXJlY3RvbHkuDQo+IA0KPiA+ICAJ
CWJyZWFrOw0KPiA+ICAJY2FzZSBCUl9NU19VUE1COg0KPiA+IC0JCXVwbS0+bXhtciA9ICZmc2xf
bGJjX3JlZ3MtPm1ibXI7DQo+ID4gKwkJdXBtLT5teG1yID0gJmZzbF9sYmNfY3RybF9kZXYtPnJl
Z3MtPm1ibXI7DQo+ID4gIAkJYnJlYWs7DQo+ID4gIAljYXNlIEJSX01TX1VQTUM6DQo+ID4gLQkJ
dXBtLT5teG1yID0gJmZzbF9sYmNfcmVncy0+bWNtcjsNCj4gPiArCQl1cG0tPm14bXIgPSAmZnNs
X2xiY19jdHJsX2Rldi0+cmVncy0+bWNtcjsNCj4gPiAgCQlicmVhazsNCj4gPiAgCWRlZmF1bHQ6
DQo+ID4gIAkJcmV0dXJuIC1FSU5WQUw7DQo+ID4gQEAgLTE0MywxNCArMTIzLDE4IEBAIEVYUE9S
VF9TWU1CT0woZnNsX3VwbV9maW5kKTsNCj4gPiAgICogdGh1cyBVUE0gcGF0dGVybiBhY3R1YWxs
eSBleGVjdXRlZC4gTm90ZSB0aGF0IG1hciB1c2FnZSBkZXBlbmRzIG9uIHRoZQ0KPiA+ICAgKiBw
cmUtcHJvZ3JhbW1lZCBBTVggYml0cyBpbiB0aGUgVVBNIFJBTS4NCj4gPiAgICovDQo+ID4gKw0K
PiA+ICBpbnQgZnNsX3VwbV9ydW5fcGF0dGVybihzdHJ1Y3QgZnNsX3VwbSAqdXBtLCB2b2lkIF9f
aW9tZW0gKmlvX2Jhc2UsIHUzMiBtYXIpDQo+ID4gIHsNCj4gPiAgCWludCByZXQgPSAwOw0KPiA+
ICAJdW5zaWduZWQgbG9uZyBmbGFnczsNCj4gPg0KPiA+ICsJaWYgKCFmc2xfbGJjX2N0cmxfZGV2
IHx8ICFmc2xfbGJjX2N0cmxfZGV2LT5yZWdzKQ0KPiA+ICsJCXJldHVybiAtRU5PREVWOw0KPiA+
ICsNCj4gPiAgCXNwaW5fbG9ja19pcnFzYXZlKCZmc2xfbGJjX2xvY2ssIGZsYWdzKTsNCj4gPg0K
PiA+IC0Jb3V0X2JlMzIoJmZzbF9sYmNfcmVncy0+bWFyLCBtYXIpOw0KPiA+ICsJb3V0X2JlMzIo
JmZzbF9sYmNfY3RybF9kZXYtPnJlZ3MtPm1hciwgbWFyKTsNCj4gPg0KPiA+ICAJc3dpdGNoICh1
cG0tPndpZHRoKSB7DQo+ID4gIAljYXNlIDg6DQo+ID4gQEAgLTE3MiwzICsxNTYsMTk3IEBAIGlu
dCBmc2xfdXBtX3J1bl9wYXR0ZXJuKHN0cnVjdCBmc2xfdXBtICp1cG0sIHZvaWQNCj4gX19pb21l
bSAqaW9fYmFzZSwgdTMyIG1hcikNCj4gPiAgCXJldHVybiByZXQ7DQo+ID4gIH0NCj4gPiAgRVhQ
T1JUX1NZTUJPTChmc2xfdXBtX3J1bl9wYXR0ZXJuKTsNCj4gPiArDQo+ID4gK3N0YXRpYyBpbnQg
X19kZXZpbml0IGZzbF9sYmNfY3RybF9pbml0KHN0cnVjdCBmc2xfbGJjX2N0cmwgKmN0cmwpDQo+
ID4gK3sNCj4gPiArCXN0cnVjdCBmc2xfbGJjX3JlZ3MgX19pb21lbSAqbGJjID0gY3RybC0+cmVn
czsNCj4gDQo+IFllcCwgc29tZXRoaW5nIGxpa2UgdGhpcyBmb3IgdGhlIGZ1bmN0aW9ucyBhYm92
ZS4NCkkgZG8gbm90IHNlZSBhbnkgaGFybSB0byBhZGQgYSB2YXJpYWJsZSBsYmMgaW4gdGhlIGZ1
bmN0aW9uIHRvIGluY3JlYXNlIHRoZSByZWFkYWJsZS4NCj4gDQo+ID4gKw0KPiA+ICsJLyogY2xl
YXIgZXZlbnQgcmVnaXN0ZXJzICovDQo+ID4gKwlzZXRiaXRzMzIoJmxiYy0+bHRlc3IsIExURVNS
X0NMRUFSKTsNCj4gPiArCW91dF9iZTMyKCZsYmMtPmx0ZWF0ciwgMCk7DQo+ID4gKwlvdXRfYmUz
MigmbGJjLT5sdGVhciwgMCk7DQo+ID4gKwlvdXRfYmUzMigmbGJjLT5sdGVjY3IsIExURUNDUl9D
TEVBUik7DQo+ID4gKwlvdXRfYmUzMigmbGJjLT5sdGVkciwgTFRFRFJfRU5BQkxFKTsNCj4gPiAr
DQo+ID4gKwkvKiBFbmFibGUgaW50ZXJydXB0cyBmb3IgYW55IGRldGVjdGVkIGV2ZW50cyAqLw0K
PiA+ICsJb3V0X2JlMzIoJmxiYy0+bHRlaXIsIExURUlSX0VOQUJMRSk7DQo+ID4gKw0KPiA+ICsJ
cmV0dXJuIDA7DQo+ID4gK30NCj4gPiArDQo+ID4gK3N0YXRpYyBpbnQgX19kZXZleGl0IGZzbF9s
YmNfY3RybF9yZW1vdmUoc3RydWN0IG9mX2RldmljZSAqb2ZkZXYpDQo+ID4gK3sNCj4gPiArCXN0
cnVjdCBmc2xfbGJjX2N0cmwgKmN0cmwgPSBkZXZfZ2V0X2RydmRhdGEoJm9mZGV2LT5kZXYpOw0K
PiA+ICsNCj4gPiArCWlmIChjdHJsLT5pcnEpDQo+ID4gKwkJZnJlZV9pcnEoY3RybC0+aXJxLCBj
dHJsKTsNCj4gPiArDQo+ID4gKwlpZiAoY3RybC0+cmVncykNCj4gPiArCQlpb3VubWFwKGN0cmwt
PnJlZ3MpOw0KPiA+ICsNCj4gPiArCWRldl9zZXRfZHJ2ZGF0YSgmb2ZkZXYtPmRldiwgTlVMTCk7
DQo+ID4gKwlrZnJlZShjdHJsKTsNCj4gPiArDQo+ID4gKwlyZXR1cm4gMDsNCj4gPiArfQ0KPiA+
ICsNCj4gPiArLyogTk9URTogVGhpcyBpbnRlcnJ1cHQgaXMgdXNlZCB0byByZXBvcnQgbG9jYWxi
dXMgZXZlbnRzIG9mIHZhcmlvdXMga2luZHMsDQo+ID4gKyAqIHN1Y2ggYXMgdHJhbnNhY3Rpb24g
ZXJyb3JzIG9uIHRoZSBjaGlwc2VsZWN0cy4NCj4gPiArICovDQo+IA0KPiAvKg0KPiAgKiBtdWx0
aSBsaW5lIGNvbW1lbnRzDQo+ICAqIHNob3VsZCBiZSBsaWtlIHRoaXMNCj4gICovDQpPSy4NCg0K
PiANCj4gWy4uLl0NCj4gPiArLyogZnNsX2xiY19jdHJsX3Byb2JlDQo+ID4gKyAqDQo+ID4gKyAq
IGNhbGxlZCBieSBkZXZpY2UgbGF5ZXIgd2hlbiBpdCBmaW5kcyBhIGRldmljZSBtYXRjaGluZw0K
PiA+ICsgKiBvbmUgb3VyIGRyaXZlciBjYW4gaGFuZGxlZC4gVGhpcyBjb2RlIGFsbG9jYXRlcyBh
bGwgb2YNCj4gPiArICogdGhlIHJlc291cmNlcyBuZWVkZWQgZm9yIHRoZSBjb250cm9sbGVyIG9u
bHkuICBUaGUNCj4gPiArICogcmVzb3VyY2VzIGZvciB0aGUgTkFORCBiYW5rcyB0aGVtc2VsdmVz
IGFyZSBhbGxvY2F0ZWQNCj4gPiArICogaW4gdGhlIGNoaXAgcHJvYmUgZnVuY3Rpb24uDQo+ID4g
KyovDQo+IA0KPiBkaXR0by4NCk9LLg0KPiANCj4gPiArc3RhdGljIGludCBfX2RldmluaXQgZnNs
X2xiY19jdHJsX3Byb2JlKHN0cnVjdCBvZl9kZXZpY2UgKm9mZGV2LA0KPiA+ICsJCQkJCSBjb25z
dCBzdHJ1Y3Qgb2ZfZGV2aWNlX2lkICptYXRjaCkNCj4gPiArew0KPiA+ICsJaW50IHJldCA9IDA7
DQo+IA0KPiBubyBuZWVkIGZvciB0aGUgaW5pdGlhbCB2YWx1ZSBoZXJlLg0KQW55IGhhcm0/DQoN
Cj4gDQo+ID4gKw0KPiA+ICsJZnNsX2xiY19jdHJsX2RldiA9IGt6YWxsb2Moc2l6ZW9mKCpmc2xf
bGJjX2N0cmxfZGV2KSwgR0ZQX0tFUk5FTCk7DQo+ID4gKwlpZiAoIWZzbF9sYmNfY3RybF9kZXYp
DQo+ID4gKwkJcmV0dXJuIC1FTk9NRU07DQo+ID4gKw0KPiA+ICsJZGV2X3NldF9kcnZkYXRhKCZv
ZmRldi0+ZGV2LCBmc2xfbGJjX2N0cmxfZGV2KTsNCj4gPiArDQo+ID4gKwlzcGluX2xvY2tfaW5p
dCgmZnNsX2xiY19jdHJsX2Rldi0+bG9jayk7DQo+ID4gKwlpbml0X3dhaXRxdWV1ZV9oZWFkKCZm
c2xfbGJjX2N0cmxfZGV2LT5pcnFfd2FpdCk7DQo+ID4gKw0KPiA+ICsJZnNsX2xiY19jdHJsX2Rl
di0+cmVncyA9IG9mX2lvbWFwKG9mZGV2LT5kZXYub2Zfbm9kZSwgMCk7DQo+ID4gKwlpZiAoIWZz
bF9sYmNfY3RybF9kZXYtPnJlZ3MpIHsNCj4gPiArCQlkZXZfZXJyKCZvZmRldi0+ZGV2LCAiZmFp
bGVkIHRvIGdldCBtZW1vcnkgcmVnaW9uXG4iKTsNCj4gPiArCQlyZXQgPSAtRU5PREVWOw0KPiA+
ICsJCWdvdG8gZXJyOw0KPiA+ICsJfQ0KPiA+ICsNCj4gPiArCWZzbF9sYmNfY3RybF9kZXYtPmly
cSA9IG9mX2lycV90b19yZXNvdXJjZShvZmRldi0+ZGV2Lm9mX25vZGUsIDAsIE5VTEwpOw0KPiA+
ICsJaWYgKGZzbF9sYmNfY3RybF9kZXYtPmlycSA9PSBOT19JUlEpIHsNCj4gPiArCQlkZXZfZXJy
KCZvZmRldi0+ZGV2LCAiZmFpbGVkIHRvIGdldCBpcnEgcmVzb3VyY2VcbiIpOw0KPiA+ICsJCXJl
dCA9IC1FTk9ERVY7DQo+ID4gKwkJZ290byBlcnI7DQo+ID4gKwl9DQo+ID4gKw0KPiA+ICsJZnNs
X2xiY19jdHJsX2Rldi0+ZGV2ID0gJm9mZGV2LT5kZXY7DQo+ID4gKw0KPiA+ICsJcmV0ID0gZnNs
X2xiY19jdHJsX2luaXQoZnNsX2xiY19jdHJsX2Rldik7DQo+ID4gKwlpZiAocmV0IDwgMCkNCj4g
PiArCQlnb3RvIGVycjsNCj4gPiArDQo+ID4gKwlyZXQgPSByZXF1ZXN0X2lycShmc2xfbGJjX2N0
cmxfZGV2LT5pcnEsIGZzbF9sYmNfY3RybF9pcnEsIDAsDQo+ID4gKwkJCQkiZnNsLWxiYyIsIGZz
bF9sYmNfY3RybF9kZXYpOw0KPiA+ICsJaWYgKHJldCAhPSAwKSB7DQo+ID4gKwkJZGV2X2Vycigm
b2ZkZXYtPmRldiwgImZhaWxlZCB0byBpbnN0YWxsIGlycSAoJWQpXG4iLA0KPiA+ICsJCQlmc2xf
bGJjX2N0cmxfZGV2LT5pcnEpOw0KPiA+ICsJCXJldCA9IGZzbF9sYmNfY3RybF9kZXYtPmlycTsN
Cj4gPiArCQlnb3RvIGVycjsNCj4gPiArCX0NCj4gPiArDQo+ID4gKwlyZXR1cm4gMDsNCj4gPiAr
DQo+ID4gK2VycjoNCj4gDQo+IGZzbF9sYmNfY3RybF9kZXYgbGVha2VkLiBQbHVzLCBhZnRlciBm
cmVlaW5nIGl0LCB5b3Ugc2hvdWxkDQo+IE5VTExpZnkgaXQgYXMgd2VsbCwgYXMgb3RoZXIgZnVu
Y3Rpb25zIGNoZWNrcyBpdCBmb3IgIU5VTEwuDQpBZ3JlZS4NCj4gDQo+IGZzbF9sYmNfY3RybF9k
ZXYtPnJlZ3MgbGVha3MgdG9vLg0KQWdyZWUuDQo+IA0KPiA+ICsJcmV0dXJuIHJldDsNCj4gPiAr
fQ0KPiA+ICsNCj4gPiArc3RhdGljIGNvbnN0IHN0cnVjdCBvZl9kZXZpY2VfaWQgZnNsX2xiY19t
YXRjaFtdID0gew0KPiA+ICsJew0KPiA+ICsJCS5jb21wYXRpYmxlID0gImZzbCxlbGJjIiwNCj4g
PiArCX0sDQo+ID4gKwl7DQo+ID4gKwkJLmNvbXBhdGlibGUgPSAiZnNsLHBxMy1sb2NhbGJ1cyIs
DQo+ID4gKwl9LA0KPiA+ICsJew0KPiA+ICsJCS5jb21wYXRpYmxlID0gImZzbCxwcTItbG9jYWxi
dXMiLA0KPiA+ICsJfSwNCj4gPiArCXsNCj4gPiArCQkuY29tcGF0aWJsZSA9ICJmc2wscHEycHJv
LWxvY2FsYnVzIiwNCj4gPiArCX0sDQo+ID4gKwl7fSwNCj4gDQo+IENvdWxkIHNhdmUgOCBsaW5l
cyBieSB3cml0aW5nICJ7IC5jb21wYXRpYmxlID0gIi4uLiIsIH0sIi4NCg0KQWdyZWUuDQo+IA0K
PiA+ICt9Ow0KPiA+ICsNCj4gPiArc3RhdGljIHN0cnVjdCBvZl9wbGF0Zm9ybV9kcml2ZXIgZnNs
X2xiY19jdHJsX2RyaXZlciA9IHsNCj4gDQo+IEkgdGhpbmsgeW91IHNob3VsZG4ndCB1c2Ugb2Zf
cGxhdGZvcm0gZm9yIG5ldyBjb2RlLiBqdXN0IHBsYXRmb3JtDQo+IHdpbGwgd29yayAodGhlcmUn
cyBwbGF0Zm9ybV9kcml2ZXIuZHJpdmVyLm9mX21hdGNoX3RhYmxlIG5vd2FkYXlzKS4NCj4gDQo+
ID4gKwkuZHJpdmVyID0gew0KPiA+ICsJCS5uYW1lID0gImZzbC1sYmMiLA0KPiA+ICsJCS5vZl9t
YXRjaF90YWJsZSA9IGZzbF9sYmNfbWF0Y2gsDQo+ID4gKwl9LA0KPiA+ICsJLnByb2JlID0gZnNs
X2xiY19jdHJsX3Byb2JlLA0KPiA+ICsJLnJlbW92ZSA9IF9fZGV2ZXhpdF9wKGZzbF9sYmNfY3Ry
bF9yZW1vdmUpLA0KPiANCj4gVGhlIGRldmljZSBpcyBub3QgcmVtb3ZhYmxlLCBJIHRoaW5rIHlv
dSBkb24ndCBuZWVkIHRoaXMuDQpBZ3JlZS4NCg0KPiANCj4gPiArfTsNCj4gPiArDQo+ID4gK3N0
YXRpYyBpbnQgX19pbml0IGZzbF9sYmNfaW5pdCh2b2lkKQ0KPiA+ICt7DQo+ID4gKwlyZXR1cm4g
b2ZfcmVnaXN0ZXJfcGxhdGZvcm1fZHJpdmVyKCZmc2xfbGJjX2N0cmxfZHJpdmVyKTsNCj4gPiAr
fQ0KPiA+ICsNCj4gPiArc3RhdGljIHZvaWQgX19leGl0IGZzbF9sYmNfZXhpdCh2b2lkKQ0KPiA+
ICt7DQo+ID4gKwlvZl91bnJlZ2lzdGVyX3BsYXRmb3JtX2RyaXZlcigmZnNsX2xiY19jdHJsX2Ry
aXZlcik7DQo+ID4gK30NCj4gPiArDQo+ID4gK21vZHVsZV9pbml0KGZzbF9sYmNfaW5pdCk7DQo+
ID4gK21vZHVsZV9leGl0KGZzbF9sYmNfZXhpdCk7DQo+IA0KPiBmc2xfbGJjIGlzIG5vdCBhIG1v
ZHVsZSwgc28geW91IGRvbid0IG5lZWQgX2V4aXQoKS4NCkFncmVlLg0KPiANCj4gPiArTU9EVUxF
X0xJQ0VOU0UoIkdQTCIpOw0KPiA+ICtNT0RVTEVfQVVUSE9SKCJGcmVlc2NhbGUgU2VtaWNvbmR1
Y3RvciIpOw0KPiA+ICtNT0RVTEVfREVTQ1JJUFRJT04oIkZyZWVzY2FsZSBFbmhhbmNlZCBMb2Nh
bCBCdXMgQ29udHJvbGxlciBkcml2ZXIiKTsNCj4gDQo+IGRpdHRvLCBubyBuZWVkIGZvciB0aGlz
Lg0KQWdyZWUuDQpUaGFua3MuDQpSb3kNCg==
^ permalink raw reply
* RE: [PATCH 2/3][MTD] P4080/nand: Only make elbc nand driver detect nand flash partitions
From: Zang Roy-R61911 @ 2010-09-06 3:40 UTC (permalink / raw)
To: Anton Vorontsov
Cc: Wood Scott-B07421, Lan Chunhe-B25806, linuxppc-dev, linux-mtd,
akpm, Gala Kumar-B11780
In-Reply-To: <20100903114357.GC11847@oksana.dev.rtsoft.ru>
DQoNCj4gLS0tLS1PcmlnaW5hbCBNZXNzYWdlLS0tLS0NCj4gRnJvbTogQW50b24gVm9yb250c292
IFttYWlsdG86Y2JvdWF0bWFpbHJ1QGdtYWlsLmNvbV0NCj4gU2VudDogRnJpZGF5LCBTZXB0ZW1i
ZXIgMDMsIDIwMTAgMTk6NDQgUE0NCj4gVG86IFphbmcgUm95LVI2MTkxMQ0KPiBDYzogbGludXgt
bXRkQGxpc3RzLmluZnJhZGVhZC5vcmc7IExhbiBDaHVuaGUtQjI1ODA2OyBsaW51eHBwYy1kZXZA
b3psYWJzLm9yZzsNCj4gYWtwbUBsaW51eC1mb3VuZGF0aW9uLm9yZzsgR2FsYSBLdW1hci1CMTE3
ODA7IFdvb2QgU2NvdHQtQjA3NDIxDQo+IFN1YmplY3Q6IFJlOiBbUEFUQ0ggMi8zXVtNVERdIFA0
MDgwL25hbmQ6IE9ubHkgbWFrZSBlbGJjIG5hbmQgZHJpdmVyIGRldGVjdA0KPiBuYW5kIGZsYXNo
IHBhcnRpdGlvbnMNCj4gDQo+IE9uIEZyaSwgQXVnIDA2LCAyMDEwIGF0IDEwOjUxOjM1QU0gKzA4
MDAsIFJveSBaYW5nIHdyb3RlOg0KPiBbLi4uXQ0KPiA+DQo+ID4gK3N0YXRpYyBzdHJ1Y3QgZnNs
X2VsYmNfZmNtX2N0cmwgKmVsYmNfZmNtX2N0cmw7DQo+ID4gKw0KPiANCj4gQXJlIHlvdSBzdXJl
IHRoYXQgeW91IHdhbnQgaXQgYXMgYSBnbG9iYWwgdmFyPyBBIGJpdCBzY2FyeSBjaGFuZ2UuDQo+
IA0KPiBPaCwgeW91IHByb2JhYmx5IGRvbid0IG5lZWQgaXQsIGFzIHlvdSBjYW4gZ2V0IGl0IGZy
b20NCj4gZnNsX2xiY19jdHJsX2Rldi0+bmFuZD8NCj4gDQo+IEkgd29uZGVyIGlmIFNjb3R0IHNh
dyB0aGVzZSBwYXRjaGVzPyBDYydlZC4NClllcy4NClJveQ0K
^ permalink raw reply
* RE: [PATCH 3/3][MTD] P4080/nand: Fix the freescale lbc issue with 36bit mode
From: Zang Roy-R61911 @ 2010-09-06 3:56 UTC (permalink / raw)
To: Anton Vorontsov
Cc: Lan Chunhe-B25806, linuxppc-dev, akpm, linux-mtd,
Gala Kumar-B11780
In-Reply-To: <20100903113604.GB11847@oksana.dev.rtsoft.ru>
DQoNCj4gLS0tLS1PcmlnaW5hbCBNZXNzYWdlLS0tLS0NCj4gRnJvbTogQW50b24gVm9yb250c292
IFttYWlsdG86Y2JvdWF0bWFpbHJ1QGdtYWlsLmNvbV0NCj4gU2VudDogRnJpZGF5LCBTZXB0ZW1i
ZXIgMDMsIDIwMTAgMTk6MzYgUE0NCj4gVG86IFphbmcgUm95LVI2MTkxMQ0KPiBDYzogbGludXgt
bXRkQGxpc3RzLmluZnJhZGVhZC5vcmc7IExhbiBDaHVuaGUtQjI1ODA2OyBsaW51eHBwYy1kZXZA
b3psYWJzLm9yZzsNCj4gYWtwbUBsaW51eC1mb3VuZGF0aW9uLm9yZzsgR2FsYSBLdW1hci1CMTE3
ODANCj4gU3ViamVjdDogUmU6IFtQQVRDSCAzLzNdW01URF0gUDQwODAvbmFuZDogRml4IHRoZSBm
cmVlc2NhbGUgbGJjIGlzc3VlIHdpdGgNCj4gMzZiaXQgbW9kZQ0KPiANCj4gT24gRnJpLCBBdWcg
MDYsIDIwMTAgYXQgMTA6NTE6MzZBTSArMDgwMCwgUm95IFphbmcgd3JvdGU6DQo+IFsuLi5dDQo+
ID4gIC8qKg0KPiA+ICsgKiBjb252ZXJ0X2xiY19hZGRyZXNzIC0gY29udmVydCB0aGUgYmFzZSBh
ZGRyZXNzDQo+ID4gKyAqIEBhZGRyX2Jhc2U6CWJhc2UgYWRkcmVzcyBvZiB0aGUgbWVtb3J5IGJh
bmsNCj4gPiArICoNCj4gPiArICogVGhpcyBmdW5jdGlvbiBjb252ZXJ0cyBhIGJhc2UgYWRkcmVz
cyBvZiBsYmMgaW50byB0aGUgcmlnaHQgZm9ybWF0IGZvcg0KPiB0aGUgQlINCj4gPiArICogcmVn
aXN0ZXJzLiBJZiB0aGUgU09DIGhhcyBlTEJDIHRoZW4gaXQgcmV0dXJucyAzMmJpdCBwaHlzaWNh
bCBhZGRyZXNzDQo+IGVsc2UNCj4gPiArICogaXQgcmV0dXJucyAzNGJpdCBwaHlzaWNhbCBhZGRy
ZXNzIGZvciBsb2NhbCBidXMoRXhhbXBsZTogTVBDODY0MSkuDQo+ID4gKyAqLw0KPiA+ICt1bnNp
Z25lZCBpbnQgY29udmVydF9sYmNfYWRkcmVzcyhwaHlzX2FkZHJfdCBhZGRyX2Jhc2UpDQo+ID4g
K3sNCj4gPiArCXZvaWQgKmRldjsNCj4gPiArCWludCBjb21wYXRpYmxlOw0KPiA+ICsNCj4gPiAr
CWRldiA9IG9mX2ZpbmRfbm9kZV9ieV9uYW1lKE5VTEwsICJsb2NhbGJ1cyIpOw0KPiANCj4gTm9w
ZSwgeW91IHNob3VsZG4ndCBkbyB0aGlzLiBOZXZlciBzZWFyY2ggYnkgbmFtZS4NCk9LLiBJIHdp
bGwgc2VhcmNoIGJ5IGNvbXBhdGlibGUuDQo+IA0KPiBBbHNvLCBhcmVuJ3QgdGhlcmUgYWxyZWFk
eSBhIGdsb2JhbCBkZXYsIHdoaWNoIHdhcyBmb3VuZCBieSB0aGUNCj4gX3Byb2JlKCkgc3R1ZmY/
DQpJIGRvIG5vdCBzZWUgdGhlIGdsb2JhbCBkZXYgY2FuIGJlIHVzZWQgaW4gcHJvYmUuDQoNCj4g
DQo+ID4gKwlpZiAoIWRldikgew0KPiA+ICsJCXByaW50ayhLRVJOX0lORk8gImZzbC1sYmM6IGNh
bid0IGZpbmQgbG9jYWxidXMgbm9kZVxuIik7DQo+ID4gKwkJb2Zfbm9kZV9wdXQoZGV2KTsNCj4g
PiArCQlyZXR1cm4gMDsNCj4gPiArCX0NCj4gPiArDQo+ID4gKwljb21wYXRpYmxlID0gb2ZfZGV2
aWNlX2lzX2NvbXBhdGlibGUoZGV2LCAiZnNsLGVsYmMiKTsNCj4gPiArCW9mX25vZGVfcHV0KGRl
dik7DQo+ID4gKwlpZiAoY29tcGF0aWJsZSkNCj4gPiArCQlyZXR1cm4gYWRkcl9iYXNlICYgMHhm
ZmZmODAwMDsNCj4gPiArCWVsc2UNCj4gPiArCQlyZXR1cm4gKGFkZHJfYmFzZSAmIDB4MGZmZmY4
MDAwdWxsKSBcDQo+ID4gKwkJCXwgKChhZGRyX2Jhc2UgJiAweDMwMDAwMDAwMHVsbCkgPj4gMTkp
Ow0KPiA+ICt9DQo+ID4gK0VYUE9SVF9TWU1CT0woY29udmVydF9sYmNfYWRkcmVzcyk7DQo+ID4g
Kw0KPiA+ICsvKioNCj4gPiAgICogZnNsX2xiY19maW5kIC0gZmluZCBMb2NhbGJ1cyBiYW5rDQo+
ID4gICAqIEBhZGRyX2Jhc2U6CWJhc2UgYWRkcmVzcyBvZiB0aGUgbWVtb3J5IGJhbmsNCj4gPiAg
ICoNCj4gPiBAQCAtNTAsNyArODAsOCBAQCBpbnQgZnNsX2xiY19maW5kKHBoeXNfYWRkcl90IGFk
ZHJfYmFzZSkNCj4gPiAgCQlfX2JlMzIgYnIgPSBpbl9iZTMyKCZmc2xfbGJjX2N0cmxfZGV2LT5y
ZWdzLT5iYW5rW2ldLmJyKTsNCj4gPiAgCQlfX2JlMzIgb3IgPSBpbl9iZTMyKCZmc2xfbGJjX2N0
cmxfZGV2LT5yZWdzLT5iYW5rW2ldLm9yKTsNCj4gPg0KPiA+IC0JCWlmIChiciAmIEJSX1YgJiYg
KGJyICYgb3IgJiBCUl9CQSkgPT0gYWRkcl9iYXNlKQ0KPiA+ICsJCWlmIChiciAmIEJSX1YgJiYg
KGJyICYgb3IgJiBCUl9CQSkgXA0KPiANCj4gTm8gbmVlZCBmb3IgIlwiIGF0IHRoZSBlbmQgb2Yg
dGhlIGxpbmUsIGtlZXAgPT0gb24gdGhlIHNhbWUgbGluZS4NCj4gDQo+ID4gKwkJCQk9PSBjb252
ZXJ0X2xiY19hZGRyZXNzKGFkZHJfYmFzZSkpDQo+IA0KPiBXb3VsZCBiZSBwcmV0dGllciBpZiB5
b3UgbmFtZSBpdCBmc2xfbGJjX2FkZHIoKS4gS2VlcHMgcHJlZml4DQo+IHRoZSBzYW1lIGZvciB0
aGUgcmVzdCBvZiB0aGUgZmlsZSwgcGx1cyBtYWtlcyBpdCBzaG9ydGVyIChzbw0KPiB0aGVyZSBw
cm9iYWJseSB3b24ndCBiZSBhbnkgbmVlZCBmb3IgYnJlYWtpbmcgdGhlIGxpbmUpLg0KQWdyZWUg
dGhlIG5ldyBuYW1lLg0KVGhhbmtzLg0KUm95DQo=
^ permalink raw reply
* RE: [PATCH 2/3][MTD] P4080/nand: Only make elbc nand driver detect nand flash partitions
From: Zang Roy-R61911 @ 2010-09-06 4:49 UTC (permalink / raw)
To: Anton Vorontsov
Cc: Wood Scott-B07421, Lan Chunhe-B25806, linuxppc-dev, linux-mtd,
akpm, Gala Kumar-B11780
In-Reply-To: <20100903114357.GC11847@oksana.dev.rtsoft.ru>
DQoNCj4gLS0tLS1PcmlnaW5hbCBNZXNzYWdlLS0tLS0NCj4gRnJvbTogQW50b24gVm9yb250c292
IFttYWlsdG86Y2JvdWF0bWFpbHJ1QGdtYWlsLmNvbV0NCj4gU2VudDogRnJpZGF5LCBTZXB0ZW1i
ZXIgMDMsIDIwMTAgMTk6NDQgUE0NCj4gVG86IFphbmcgUm95LVI2MTkxMQ0KPiBDYzogbGludXgt
bXRkQGxpc3RzLmluZnJhZGVhZC5vcmc7IExhbiBDaHVuaGUtQjI1ODA2OyBsaW51eHBwYy1kZXZA
b3psYWJzLm9yZzsNCj4gYWtwbUBsaW51eC1mb3VuZGF0aW9uLm9yZzsgR2FsYSBLdW1hci1CMTE3
ODA7IFdvb2QgU2NvdHQtQjA3NDIxDQo+IFN1YmplY3Q6IFJlOiBbUEFUQ0ggMi8zXVtNVERdIFA0
MDgwL25hbmQ6IE9ubHkgbWFrZSBlbGJjIG5hbmQgZHJpdmVyIGRldGVjdA0KPiBuYW5kIGZsYXNo
IHBhcnRpdGlvbnMNCj4gDQo+IE9uIEZyaSwgQXVnIDA2LCAyMDEwIGF0IDEwOjUxOjM1QU0gKzA4
MDAsIFJveSBaYW5nIHdyb3RlOg0KPiBbLi4uXQ0KPiA+DQo+ID4gK3N0YXRpYyBzdHJ1Y3QgZnNs
X2VsYmNfZmNtX2N0cmwgKmVsYmNfZmNtX2N0cmw7DQo+ID4gKw0KPiANCj4gQXJlIHlvdSBzdXJl
IHRoYXQgeW91IHdhbnQgaXQgYXMgYSBnbG9iYWwgdmFyPyBBIGJpdCBzY2FyeSBjaGFuZ2UuDQo+
IA0KPiBPaCwgeW91IHByb2JhYmx5IGRvbid0IG5lZWQgaXQsIGFzIHlvdSBjYW4gZ2V0IGl0IGZy
b20NCj4gZnNsX2xiY19jdHJsX2Rldi0+bmFuZD8NCkdldCBpdCBmb3JtIGZzbF9sYmNfY3RybF9k
ZXYtPm5hbmQgb3IgYXNzaWduIGl0IHRvIGZzbF9sYmNfY3RybF9kZXYtPm5hbmQgaW4gcHJvYmU/
DQpUaGFua3MuDQpSb3kNCg0K
^ permalink raw reply
* RE: [PATCH] APM821xx: Add support for new SoC APM821xx
From: Tirumala Marri @ 2010-09-06 5:19 UTC (permalink / raw)
To: Olof Johansson; +Cc: linuxppc-dev
In-Reply-To: <20100905222340.GC2150@lixom.net>
>
> Then the device tree identifier, and the cpu setup functions, etc,
> should indicate
> 464, not APM821xx.
This is new SoC based on 464 cpu core. All the previous SoC device tree
CPU portion uses SoC name.
>
> Also, why add yet another defconfig? Isn't the eval board similar to
> many others and can be supported with just a tweak of some existing
> common defconfig instead?
>
Every new board needs new defconfig. And it is not same as others. It has
Different features from other.
^ permalink raw reply
* Re: [PATCH 1/5] ptp: Added a brand new class driver for ptp clocks.
From: Richard Cochran @ 2010-09-06 6:33 UTC (permalink / raw)
To: John Stultz
Cc: Rodolfo Giometti, Arnd Bergmann, netdev, devicetree-discuss,
linux-kernel, linuxppc-dev, linux-arm-kernel, Krzysztof Halasa
In-Reply-To: <1282948239.2268.155.camel@jstultz-laptop>
On Fri, Aug 27, 2010 at 03:30:39PM -0700, John Stultz wrote:
> On Fri, 2010-08-27 at 14:38 +0200, Richard Cochran wrote:
> > We have not introduced new PPS interface. We use existing PPS subsystem.
>
> Doesn't the pps subsystem have its own way to control the pps signal
> interrupt? I'm not totally sure here, but given your point above that
> having multiple pps events it seems like they should be selectable. It
> seems something that we'd want to control via the global pps interface,
> rather then having a pps-enable flag on every random bit of hardware
> that can support it.
The PPS subsystem offers no way to disable PPS interrupts.
> > > Same for the timestamps and periodic output (ie: and how do they differ
> > > from reading or setting a timer on CLOCK_PTP?)
> >
> > The posix timer calls won't work:
> >
> > I have a PTP hardware clocks with multiple external timestamp
> > channels. Using timer_gettime, how can I specify (or decode) the
> > channel of interest to me?
>
> I guess I'm not following you here. Again, I'm not super familiar with
> the hardware involved. Could you clarify a bit more? What do you mean by
> external timestamp? Is this what is used on the packet timestamping?
No, the packet timestamp occurs in the PHY, MAC, or on the MII bus and
is an essential feature to support the PTP.
An external timestamp is just a wire going into the clock and is an
optional feature to make the clock more useful. The clock can latch
the current time value when an edge is dectected on the wire. Using
external timestamps, you correlate real world events with the absolute
time in the clock.
Typically, a clock offers two or more such input channels (wires), but
timer_gettime does not offer a way to differentiate between them, and
thus is not suitable.
> The posix clock id interface is frustrating because the flat static
> enumeration is really limiting.
>
> I wonder if a dynamic enumeration for posix clocks would be possibly a
> way to go?
I am perfectly happy with this.
> In other words, a driver registers a clock with the system, and the
> system reserves a clock_id for it from the designated available pool and
> assigns it back. Then userland could query the driver via something like
> sysfs to get the clockid for the hardware.
>
> Would that maybe work?
I have now posted a sample implementation of this idea. Do you like it?
> > The sysfs will include one class device for each PTP clock. Each clock
> > has a sysfs attribute with the corresponding clock id.
>
> Do you mean the clock_id # in the posix clocks namespace?
Yes.
> > I would also be happy with the character device idea already
> > posted. Just pick one of the two, and I'll resubmit the patch set...
>
> Personally, with regard to exposing the ptp clock to userland I'm more
> comfortable via the chardev. However, the posix clocks/timer api is
> really close to what you need, so I'm not totally set against it. And
> maybe the dynamic enumeration would resolve my main problems with it?
Okay, I have posted a draft of the dynamic idea. Can you support it?
> That said, with the chardev method, I don't like the idea of duplicating
> the existing time apis via a systemtime device. Dropping that from your
> earlier patch would ease the majority of my objections.
Well, the clock interface needs to offer basic services:
1. Set time
2. Get time
3. Jump offset
4. Adjust frequency
This is similar to what the posix clock and ntp API offer. Using a
chardev, should I make the ioctls really different, just for the
purpose of being different?
To me, it makes more sense to offer a familiar interface.
I was perfectly happy with the chardev idea. In fact, that is the way
I first implemented it. Now, I have also gone ahead and implemented
the dynamic posix clock idea, too.
> > At this point I would just like to go forward with one of the two
> > proposed APIs. I had modelled the character device on the posix clock
> > calls in order to make it immediately familar, and I think it is a
> > viable approach. After the lkml discussion, I think it is even cleaner
> > and nicer to just offer a new clock id.
I would like to repeat the sentiment in this last paragraph! I already
implemented and would be content with either form for the new clock
control API:
1. Character device
2. POSIX clock with dynamic ids
Please, just take your pick ;^)
Thanks,
Richard
^ permalink raw reply
* Re: [PATCH 2/3][MTD] P4080/nand: Only make elbc nand driver detect nand flash partitions
From: Anton Vorontsov @ 2010-09-06 8:09 UTC (permalink / raw)
To: Zang Roy-R61911
Cc: Wood Scott-B07421, Lan Chunhe-B25806, linuxppc-dev, linux-mtd,
akpm, Gala Kumar-B11780
In-Reply-To: <3850A844E6A3854C827AC5C0BEC7B60A1B37B0@zch01exm23.fsl.freescale.net>
On Mon, Sep 06, 2010 at 12:49:17PM +0800, Zang Roy-R61911 wrote:
> > On Fri, Aug 06, 2010 at 10:51:35AM +0800, Roy Zang wrote:
> > [...]
> > >
> > > +static struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl;
> > > +
> >
> > Are you sure that you want it as a global var? A bit scary change.
> >
> > Oh, you probably don't need it, as you can get it from
> > fsl_lbc_ctrl_dev->nand?
> Get it form fsl_lbc_ctrl_dev->nand or assign it to
> fsl_lbc_ctrl_dev->nand in probe?
I meant to get it from fsl_lbc_ctrl_dev->nand. I.e. in
probe() you do: fsl_lbc_ctrl_dev->nand = elbc_fcm_ctrl, so
you probably don't need the global var.
(fsl_lbc_ctrl_dev seems to be global as well, duh. Well,
one variable less in the global name space. But I'd
probably use lbc_np->data to store the LBC private
struct).
Scott seem to be fine with it as there are probably no
plans to to add several localbus controllers into the SoCs.
But, I saw a custom board with two MPC82xx SoCs connected
together, one as a master (core + peripheral devs), and
other as a slave (its core was halted, and only slave's
CPM peripheral devices were used by the master CPU).
I think it is possible to connect two (or more) SoCs in
a such way so that two or more LBC controllers would
be visible for the Linux.
Thanks,
--
Anton Vorontsov
email: cbouatmailru@gmail.com
irc://irc.freenode.net/bd2
^ permalink raw reply
* Re: [PATCH 1/3][MTD] P4080/eLBC: Make Freescale elbc interrupt common to elbc devices
From: Anton Vorontsov @ 2010-09-06 8:21 UTC (permalink / raw)
To: Zang Roy-R61911
Cc: Wood Scott-B07421, Lan Chunhe-B25806, linuxppc-dev, linux-mtd,
akpm, Gala Kumar-B11780
In-Reply-To: <3850A844E6A3854C827AC5C0BEC7B60A1B378F@zch01exm23.fsl.freescale.net>
On Mon, Sep 06, 2010 at 11:38:09AM +0800, Zang Roy-R61911 wrote:
[...]
> > > switch (br & BR_MSEL) {
> > > case BR_MS_UPMA:
> > > - upm->mxmr = &fsl_lbc_regs->mamr;
> > > + upm->mxmr = &fsl_lbc_ctrl_dev->regs->mamr;
> >
> > Ditto, a very repetitive stuff, desires a variable for regs?
> But the fact is that the variable represents different reg
> address according to the condition. It will be ugly to use
> the reg address directoly.
I meant a dedicated var for 'fsl_lbc_ctrl_dev->regs'.
I.e.
regs = fsl_lbc_ctrl_dev->regs;
...
mxmr = ®s->mamr;
...
mxmr = ®s->mbmr;
..
mxmr = ®s->mcmr;
Instead of
mxmr = &fsl_lbc_ctrl_dev->regs->mamr;
...
mxmr = &fsl_lbc_ctrl_dev->regs->mbmr;
..
mxmr = &fsl_lbc_ctrl_dev->regs->mcmr;
[...]
> > > +static int __devinit fsl_lbc_ctrl_probe(struct of_device *ofdev,
> > > + const struct of_device_id *match)
> > > +{
> > > + int ret = 0;
> >
> > no need for the initial value here.
> Any harm?
Probably not as gcc will likely optimize it away,
but it's not needed, so why keep it there?
Thanks,
--
Anton Vorontsov
email: cbouatmailru@gmail.com
irc://irc.freenode.net/bd2
^ permalink raw reply
* RE: [PATCH 2/3][MTD] P4080/nand: Only make elbc nand driver detect nand flash partitions
From: Zang Roy-R61911 @ 2010-09-06 8:38 UTC (permalink / raw)
To: Anton Vorontsov
Cc: Wood Scott-B07421, Lan Chunhe-B25806, linuxppc-dev, linux-mtd,
akpm, Gala Kumar-B11780
In-Reply-To: <20100906080938.GA13755@oksana.dev.rtsoft.ru>
DQoNCj4gLS0tLS1PcmlnaW5hbCBNZXNzYWdlLS0tLS0NCj4gRnJvbTogQW50b24gVm9yb250c292
IFttYWlsdG86Y2JvdWF0bWFpbHJ1QGdtYWlsLmNvbV0NCj4gU2VudDogTW9uZGF5LCBTZXB0ZW1i
ZXIgMDYsIDIwMTAgMTY6MTAgUE0NCj4gVG86IFphbmcgUm95LVI2MTkxMQ0KPiBDYzogbGludXgt
bXRkQGxpc3RzLmluZnJhZGVhZC5vcmc7IExhbiBDaHVuaGUtQjI1ODA2OyBsaW51eHBwYy1kZXZA
b3psYWJzLm9yZzsNCj4gYWtwbUBsaW51eC1mb3VuZGF0aW9uLm9yZzsgR2FsYSBLdW1hci1CMTE3
ODA7IFdvb2QgU2NvdHQtQjA3NDIxDQo+IFN1YmplY3Q6IFJlOiBbUEFUQ0ggMi8zXVtNVERdIFA0
MDgwL25hbmQ6IE9ubHkgbWFrZSBlbGJjIG5hbmQgZHJpdmVyIGRldGVjdA0KPiBuYW5kIGZsYXNo
IHBhcnRpdGlvbnMNCj4gDQo+IE9uIE1vbiwgU2VwIDA2LCAyMDEwIGF0IDEyOjQ5OjE3UE0gKzA4
MDAsIFphbmcgUm95LVI2MTkxMSB3cm90ZToNCj4gPiA+IE9uIEZyaSwgQXVnIDA2LCAyMDEwIGF0
IDEwOjUxOjM1QU0gKzA4MDAsIFJveSBaYW5nIHdyb3RlOg0KPiA+ID4gWy4uLl0NCj4gPiA+ID4N
Cj4gPiA+ID4gK3N0YXRpYyBzdHJ1Y3QgZnNsX2VsYmNfZmNtX2N0cmwgKmVsYmNfZmNtX2N0cmw7
DQo+ID4gPiA+ICsNCj4gPiA+DQo+ID4gPiBBcmUgeW91IHN1cmUgdGhhdCB5b3Ugd2FudCBpdCBh
cyBhIGdsb2JhbCB2YXI/IEEgYml0IHNjYXJ5IGNoYW5nZS4NCj4gPiA+DQo+ID4gPiBPaCwgeW91
IHByb2JhYmx5IGRvbid0IG5lZWQgaXQsIGFzIHlvdSBjYW4gZ2V0IGl0IGZyb20NCj4gPiA+IGZz
bF9sYmNfY3RybF9kZXYtPm5hbmQ/DQo+ID4gR2V0IGl0IGZvcm0gZnNsX2xiY19jdHJsX2Rldi0+
bmFuZCBvciBhc3NpZ24gaXQgdG8NCj4gPiBmc2xfbGJjX2N0cmxfZGV2LT5uYW5kIGluIHByb2Jl
Pw0KPiANCj4gSSBtZWFudCB0byBnZXQgaXQgZnJvbSBmc2xfbGJjX2N0cmxfZGV2LT5uYW5kLiBJ
LmUuIGluDQo+IHByb2JlKCkgeW91IGRvOiBmc2xfbGJjX2N0cmxfZGV2LT5uYW5kID0gZWxiY19m
Y21fY3RybCwgc28NCj4geW91IHByb2JhYmx5IGRvbid0IG5lZWQgdGhlIGdsb2JhbCB2YXIuDQo+
IA0KPiAoZnNsX2xiY19jdHJsX2RldiBzZWVtcyB0byBiZSBnbG9iYWwgYXMgd2VsbCwgZHVoLiBX
ZWxsLA0KPiBvbmUgdmFyaWFibGUgbGVzcyBpbiB0aGUgZ2xvYmFsIG5hbWUgc3BhY2UuIEJ1dCBJ
J2QNCj4gcHJvYmFibHkgdXNlIGxiY19ucC0+ZGF0YSB0byBzdG9yZSB0aGUgTEJDIHByaXZhdGUN
Cj4gc3RydWN0KS4NClRoYXQgbWFrZXMgc2Vuc2UuDQo+IA0KPiBTY290dCBzZWVtIHRvIGJlIGZp
bmUgd2l0aCBpdCBhcyB0aGVyZSBhcmUgcHJvYmFibHkgbm8NCj4gcGxhbnMgdG8gdG8gYWRkIHNl
dmVyYWwgbG9jYWxidXMgY29udHJvbGxlcnMgaW50byB0aGUgU29Dcy4NCj4gDQo+IEJ1dCwgSSBz
YXcgYSBjdXN0b20gYm9hcmQgd2l0aCB0d28gTVBDODJ4eCBTb0NzIGNvbm5lY3RlZA0KPiB0b2dl
dGhlciwgb25lIGFzIGEgbWFzdGVyIChjb3JlICsgcGVyaXBoZXJhbCBkZXZzKSwgYW5kDQo+IG90
aGVyIGFzIGEgc2xhdmUgKGl0cyBjb3JlIHdhcyBoYWx0ZWQsIGFuZCBvbmx5IHNsYXZlJ3MNCj4g
Q1BNIHBlcmlwaGVyYWwgZGV2aWNlcyB3ZXJlIHVzZWQgYnkgdGhlIG1hc3RlciBDUFUpLg0KPiAN
Cj4gSSB0aGluayBpdCBpcyBwb3NzaWJsZSB0byBjb25uZWN0IHR3byAob3IgbW9yZSkgU29DcyBp
bg0KPiBhIHN1Y2ggd2F5IHNvIHRoYXQgdHdvIG9yIG1vcmUgTEJDIGNvbnRyb2xsZXJzIHdvdWxk
DQo+IGJlIHZpc2libGUgZm9yIHRoZSBMaW51eC4NClRoYXQgaXMgYW4gaW50ZXJlc3RpbmcgY2Fz
ZS4gRG8geW91IGhhdmUgYW55IHRob3VnaHQgaGVyZT8NClRoYW5rcy4NClJveQ0K
^ permalink raw reply
* RE: [PATCH 1/3][MTD] P4080/eLBC: Make Freescale elbc interrupt common to elbc devices
From: Zang Roy-R61911 @ 2010-09-06 9:24 UTC (permalink / raw)
To: Anton Vorontsov
Cc: Wood Scott-B07421, Lan Chunhe-B25806, linuxppc-dev, linux-mtd,
akpm, Gala Kumar-B11780
In-Reply-To: <20100906082142.GB13755@oksana.dev.rtsoft.ru>
DQoNCj4gLS0tLS1PcmlnaW5hbCBNZXNzYWdlLS0tLS0NCj4gRnJvbTogQW50b24gVm9yb250c292
IFttYWlsdG86Y2JvdWF0bWFpbHJ1QGdtYWlsLmNvbV0NCj4gU2VudDogTW9uZGF5LCBTZXB0ZW1i
ZXIgMDYsIDIwMTAgMTY6MjIgUE0NCj4gVG86IFphbmcgUm95LVI2MTkxMQ0KPiBDYzogbGludXgt
bXRkQGxpc3RzLmluZnJhZGVhZC5vcmc7IExhbiBDaHVuaGUtQjI1ODA2OyBsaW51eHBwYy1kZXZA
b3psYWJzLm9yZzsNCj4gYWtwbUBsaW51eC1mb3VuZGF0aW9uLm9yZzsgR2FsYSBLdW1hci1CMTE3
ODA7IFdvb2QgU2NvdHQtQjA3NDIxDQo+IFN1YmplY3Q6IFJlOiBbUEFUQ0ggMS8zXVtNVERdIFA0
MDgwL2VMQkM6IE1ha2UgRnJlZXNjYWxlIGVsYmMgaW50ZXJydXB0IGNvbW1vbg0KPiB0byBlbGJj
IGRldmljZXMNCj4gDQo+IE9uIE1vbiwgU2VwIDA2LCAyMDEwIGF0IDExOjM4OjA5QU0gKzA4MDAs
IFphbmcgUm95LVI2MTkxMSB3cm90ZToNCj4gWy4uLl0NCj4gPiA+ID4gIAlzd2l0Y2ggKGJyICYg
QlJfTVNFTCkgew0KPiA+ID4gPiAgCWNhc2UgQlJfTVNfVVBNQToNCj4gPiA+ID4gLQkJdXBtLT5t
eG1yID0gJmZzbF9sYmNfcmVncy0+bWFtcjsNCj4gPiA+ID4gKwkJdXBtLT5teG1yID0gJmZzbF9s
YmNfY3RybF9kZXYtPnJlZ3MtPm1hbXI7DQo+ID4gPg0KPiA+ID4gRGl0dG8sIGEgdmVyeSByZXBl
dGl0aXZlIHN0dWZmLCBkZXNpcmVzIGEgdmFyaWFibGUgZm9yIHJlZ3M/DQo+ID4gQnV0IHRoZSBm
YWN0IGlzIHRoYXQgdGhlIHZhcmlhYmxlIHJlcHJlc2VudHMgZGlmZmVyZW50IHJlZw0KPiA+IGFk
ZHJlc3MgYWNjb3JkaW5nIHRvIHRoZSBjb25kaXRpb24uIEl0IHdpbGwgYmUgdWdseSB0byB1c2UN
Cj4gPiB0aGUgcmVnIGFkZHJlc3MgZGlyZWN0b2x5Lg0KPiANCj4gSSBtZWFudCBhIGRlZGljYXRl
ZCB2YXIgZm9yICdmc2xfbGJjX2N0cmxfZGV2LT5yZWdzJy4NCj4gSS5lLg0KPiANCj4gcmVncyA9
IGZzbF9sYmNfY3RybF9kZXYtPnJlZ3M7DQo+IC4uLg0KPiBteG1yID0gJnJlZ3MtPm1hbXI7DQo+
IC4uLg0KPiBteG1yID0gJnJlZ3MtPm1ibXI7DQo+IC4uDQo+IG14bXIgPSAmcmVncy0+bWNtcjsN
Cj4gDQo+IEluc3RlYWQgb2YNCj4gDQo+IG14bXIgPSAmZnNsX2xiY19jdHJsX2Rldi0+cmVncy0+
bWFtcjsNCj4gLi4uDQo+IG14bXIgPSAmZnNsX2xiY19jdHJsX2Rldi0+cmVncy0+bWJtcjsNCj4g
Li4NCj4gbXhtciA9ICZmc2xfbGJjX2N0cmxfZGV2LT5yZWdzLT5tY21yOw0KVGhhdCBtYWtlcyBz
ZW5zZS4gIEEgZ2xvYmFsIG9yIGxvY2FsIHZhcmlhYmxlIGZvciBmc2xfbGJjX2N0cmxfZGV2LT5y
ZWdzPyBXaGljaCBvbmUgaXMgYmV0dGVyPw0KDQo+IA0KPiBbLi4uXQ0KPiA+ID4gPiArc3RhdGlj
IGludCBfX2RldmluaXQgZnNsX2xiY19jdHJsX3Byb2JlKHN0cnVjdCBvZl9kZXZpY2UgKm9mZGV2
LA0KPiA+ID4gPiArCQkJCQkgY29uc3Qgc3RydWN0IG9mX2RldmljZV9pZCAqbWF0Y2gpDQo+ID4g
PiA+ICt7DQo+ID4gPiA+ICsJaW50IHJldCA9IDA7DQo+ID4gPg0KPiA+ID4gbm8gbmVlZCBmb3Ig
dGhlIGluaXRpYWwgdmFsdWUgaGVyZS4NCj4gPiBBbnkgaGFybT8NCj4gDQo+IFByb2JhYmx5IG5v
dCBhcyBnY2Mgd2lsbCBsaWtlbHkgb3B0aW1pemUgaXQgYXdheSwNCj4gYnV0IGl0J3Mgbm90IG5l
ZWRlZCwgc28gd2h5IGtlZXAgaXQgdGhlcmU/DQpoYWJpdC4NClRoYW5rcy4NClJveQ0KDQo=
^ permalink raw reply
* Re: [PATCH 1/3][MTD] P4080/eLBC: Make Freescale elbc interrupt common to elbc devices
From: Anton Vorontsov @ 2010-09-06 9:44 UTC (permalink / raw)
To: Zang Roy-R61911
Cc: Wood Scott-B07421, Lan Chunhe-B25806, linuxppc-dev, linux-mtd,
akpm, Gala Kumar-B11780
In-Reply-To: <3850A844E6A3854C827AC5C0BEC7B60A1B3872@zch01exm23.fsl.freescale.net>
On Mon, Sep 06, 2010 at 05:24:35PM +0800, Zang Roy-R61911 wrote:
[..]
> > mxmr = &fsl_lbc_ctrl_dev->regs->mcmr;
> That makes sense. A global or local variable for fsl_lbc_ctrl_dev->regs? Which one is better?
The less global variables, the better. So, I'd vote for
a local one.
> > [...]
> > > > > +static int __devinit fsl_lbc_ctrl_probe(struct of_device *ofdev,
> > > > > + const struct of_device_id *match)
> > > > > +{
> > > > > + int ret = 0;
> > > >
> > > > no need for the initial value here.
> > > Any harm?
> >
> > Probably not as gcc will likely optimize it away,
> > but it's not needed, so why keep it there?
> habit.
;-)
Thanks,
--
Anton Vorontsov
email: cbouatmailru@gmail.com
irc://irc.freenode.net/bd2
^ permalink raw reply
* RE: [PATCH 1/3][MTD] P4080/eLBC: Make Freescale elbc interrupt common to elbc devices
From: Zang Roy-R61911 @ 2010-09-06 10:15 UTC (permalink / raw)
To: Anton Vorontsov
Cc: Wood Scott-B07421, Lan Chunhe-B25806, linuxppc-dev, linux-mtd,
akpm, Gala Kumar-B11780
In-Reply-To: <20100906094414.GA27034@oksana.dev.rtsoft.ru>
DQoNCj4gLS0tLS1PcmlnaW5hbCBNZXNzYWdlLS0tLS0NCj4gRnJvbTogQW50b24gVm9yb250c292
IFttYWlsdG86Y2JvdWF0bWFpbHJ1QGdtYWlsLmNvbV0NCj4gU2VudDogTW9uZGF5LCBTZXB0ZW1i
ZXIgMDYsIDIwMTAgMTc6NDQgUE0NCj4gVG86IFphbmcgUm95LVI2MTkxMQ0KPiBDYzogbGludXgt
bXRkQGxpc3RzLmluZnJhZGVhZC5vcmc7IExhbiBDaHVuaGUtQjI1ODA2OyBsaW51eHBwYy1kZXZA
b3psYWJzLm9yZzsNCj4gYWtwbUBsaW51eC1mb3VuZGF0aW9uLm9yZzsgR2FsYSBLdW1hci1CMTE3
ODA7IFdvb2QgU2NvdHQtQjA3NDIxDQo+IFN1YmplY3Q6IFJlOiBbUEFUQ0ggMS8zXVtNVERdIFA0
MDgwL2VMQkM6IE1ha2UgRnJlZXNjYWxlIGVsYmMgaW50ZXJydXB0IGNvbW1vbg0KPiB0byBlbGJj
IGRldmljZXMNCj4gDQo+IE9uIE1vbiwgU2VwIDA2LCAyMDEwIGF0IDA1OjI0OjM1UE0gKzA4MDAs
IFphbmcgUm95LVI2MTkxMSB3cm90ZToNCj4gWy4uXQ0KPiA+ID4gbXhtciA9ICZmc2xfbGJjX2N0
cmxfZGV2LT5yZWdzLT5tY21yOw0KPiA+IFRoYXQgbWFrZXMgc2Vuc2UuICBBIGdsb2JhbCBvciBs
b2NhbCB2YXJpYWJsZSBmb3IgZnNsX2xiY19jdHJsX2Rldi0+cmVncz8NCj4gV2hpY2ggb25lIGlz
IGJldHRlcj8NCj4gDQo+IFRoZSBsZXNzIGdsb2JhbCB2YXJpYWJsZXMsIHRoZSBiZXR0ZXIuIFNv
LCBJJ2Qgdm90ZSBmb3INCj4gYSBsb2NhbCBvbmUuDQpJIGFsc28gcHJlZmVyIGxvY2FsIG9uZS4N
Cj4gDQo+ID4gPiBbLi4uXQ0KPiA+ID4gPiA+ID4gK3N0YXRpYyBpbnQgX19kZXZpbml0IGZzbF9s
YmNfY3RybF9wcm9iZShzdHJ1Y3Qgb2ZfZGV2aWNlICpvZmRldiwNCj4gPiA+ID4gPiA+ICsJCQkJ
CSBjb25zdCBzdHJ1Y3Qgb2ZfZGV2aWNlX2lkICptYXRjaCkNCj4gPiA+ID4gPiA+ICt7DQo+ID4g
PiA+ID4gPiArCWludCByZXQgPSAwOw0KPiA+ID4gPiA+DQo+ID4gPiA+ID4gbm8gbmVlZCBmb3Ig
dGhlIGluaXRpYWwgdmFsdWUgaGVyZS4NCj4gPiA+ID4gQW55IGhhcm0/DQo+ID4gPg0KPiA+ID4g
UHJvYmFibHkgbm90IGFzIGdjYyB3aWxsIGxpa2VseSBvcHRpbWl6ZSBpdCBhd2F5LA0KPiA+ID4g
YnV0IGl0J3Mgbm90IG5lZWRlZCwgc28gd2h5IGtlZXAgaXQgdGhlcmU/DQo+ID4gaGFiaXQuDQo+
IA0KPiA7LSkNCjotKC4NClRoYW5rcyBmb3IgYWxsIHlvdXIgY29tbWVudHMuIFRoYXQgaXMgdmFs
dWFibGUuDQpJIHdpbGwgdXBkYXRlIHRoZSBwYXRjaCBhY2NvcmRpbmcgdG8geW91ciBjb21tZW50
IGFuZCB0aGUgbmV3IHBsYXRmb3JtIGRldmljZSBhcmNoLg0KUm95DQo=
^ permalink raw reply
* Re: How to define an I2C-to-SPI bridge device ?
From: Andre Schwarz @ 2010-09-06 11:40 UTC (permalink / raw)
To: Anton Vorontsov; +Cc: LinuxPPC List, DevTreeDiscuss
In-Reply-To: <20100903120858.GA19380@oksana.dev.rtsoft.ru>
Anton,
> we're about to get new MPC8377 based hardware with various peripherals.
>> There are two I2C-to-SPI bridge devices (NXP SC18IS602) and I'm not sure
>> how to define a proper dts...
>>
>> Of course it's an easy thing creating 2 child nodes on the CPU's I2C
>> device - but how can I represent the created SPI bus ?
> Um.. the same as the other SPI buses? I.e.
>
> i2c-controller { /* SOC I2C controller */
> spi-controller { /* The I2C-to-SPI bridge */
> spi-device@0 {
> };
> spi-device@1 {
> };
> };
> };
>
ok , thanks - looks straight forward.
Is this any more than plain definition, i.e. will this trigger any I2C
or SPI device registration/linking ?
>> Is the (possibly) required driver (of_sc18is60x_spi ?) supposed to be an
>> I2C slave or an SPI host driver ?
> It should be an I2C driver that registers an SPI master (i.e.
> calls spi_alloc_master() and spi_register_master()).
hmm - ok. Will have to do it manually then ...
I still wonder how to make the driver arch-generic *and* of-capable.
Do we need a generic I2C slave driver that can be probed along with an
"of glue driver" or should the of-binding be part of a single device
driver ?
Sorry for the dumb questions - looks like I expected a little too much
functionality already existing.
Regards,
André
MATRIX VISION GmbH, Talstrasse 16, DE-71570 Oppenweiler
Registergericht: Amtsgericht Stuttgart, HRB 271090
Geschaeftsfuehrer: Gerhard Thullner, Werner Armingeon, Uwe Furtner, Hans-Joachim Reich
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox