diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-03-21 18:55:10 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-03-21 18:55:10 -0700 |
commit | 5375871d432ae9fc581014ac117b96aaee3cd0c7 (patch) | |
tree | be98e8255b0f927fb920fb532a598b93fa140dbe /drivers/misc/carma | |
parent | b57cb7231b2ce52d3dda14a7b417ae125fb2eb97 (diff) | |
parent | dfbc2d75c1bd47c3186fa91f1655ea2f3825b0ec (diff) |
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc
Pull powerpc merge from Benjamin Herrenschmidt:
"Here's the powerpc batch for this merge window. It is going to be a
bit more nasty than usual as in touching things outside of
arch/powerpc mostly due to the big iSeriesectomy :-) We finally got
rid of the bugger (legacy iSeries support) which was a PITA to
maintain and that nobody really used anymore.
Here are some of the highlights:
- Legacy iSeries is gone. Thanks Stephen ! There's still some bits
and pieces remaining if you do a grep -ir series arch/powerpc but
they are harmless and will be removed in the next few weeks
hopefully.
- The 'fadump' functionality (Firmware Assisted Dump) replaces the
previous (equivalent) "pHyp assisted dump"... it's a rewrite of a
mechanism to get the hypervisor to do crash dumps on pSeries, the
new implementation hopefully being much more reliable. Thanks
Mahesh Salgaonkar.
- The "EEH" code (pSeries PCI error handling & recovery) got a big
spring cleaning, motivated by the need to be able to implement a
new backend for it on top of some new different type of firwmare.
The work isn't complete yet, but a good chunk of the cleanups is
there. Note that this adds a field to struct device_node which is
not very nice and which Grant objects to. I will have a patch soon
that moves that to a powerpc private data structure (hopefully
before rc1) and we'll improve things further later on (hopefully
getting rid of the need for that pointer completely). Thanks Gavin
Shan.
- I dug into our exception & interrupt handling code to improve the
way we do lazy interrupt handling (and make it work properly with
"edge" triggered interrupt sources), and while at it found & fixed
a wagon of issues in those areas, including adding support for page
fault retry & fatal signals on page faults.
- Your usual random batch of small fixes & updates, including a bunch
of new embedded boards, both Freescale and APM based ones, etc..."
I fixed up some conflicts with the generalized irq-domain changes from
Grant Likely, hopefully correctly.
* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc: (141 commits)
powerpc/ps3: Do not adjust the wrapper load address
powerpc: Remove the rest of the legacy iSeries include files
powerpc: Remove the remaining CONFIG_PPC_ISERIES pieces
init: Remove CONFIG_PPC_ISERIES
powerpc: Remove FW_FEATURE ISERIES from arch code
tty/hvc_vio: FW_FEATURE_ISERIES is no longer selectable
powerpc/spufs: Fix double unlocks
powerpc/5200: convert mpc5200 to use of_platform_populate()
powerpc/mpc5200: add options to mpc5200_defconfig
powerpc/mpc52xx: add a4m072 board support
powerpc/mpc5200: update mpc5200_defconfig to fit for charon board
Documentation/powerpc/mpc52xx.txt: Checkpatch cleanup
powerpc/44x: Add additional device support for APM821xx SoC and Bluestone board
powerpc/44x: Add support PCI-E for APM821xx SoC and Bluestone board
MAINTAINERS: Update PowerPC 4xx tree
powerpc/44x: The bug fixed support for APM821xx SoC and Bluestone board
powerpc: document the FSL MPIC message register binding
powerpc: add support for MPIC message register API
powerpc/fsl: Added aliased MSIIR register address to MSI node in dts
powerpc/85xx: mpc8548cds - add 36-bit dts
...
Diffstat (limited to 'drivers/misc/carma')
-rw-r--r-- | drivers/misc/carma/carma-fpga.c | 114 |
1 files changed, 72 insertions, 42 deletions
diff --git a/drivers/misc/carma/carma-fpga.c b/drivers/misc/carma/carma-fpga.c index 366bc156e34..8c279da0741 100644 --- a/drivers/misc/carma/carma-fpga.c +++ b/drivers/misc/carma/carma-fpga.c @@ -560,6 +560,9 @@ static void data_enable_interrupts(struct fpga_device *priv) /* flush the writes */ fpga_read_reg(priv, 0, MMAP_REG_STATUS); + fpga_read_reg(priv, 1, MMAP_REG_STATUS); + fpga_read_reg(priv, 2, MMAP_REG_STATUS); + fpga_read_reg(priv, 3, MMAP_REG_STATUS); /* switch back to the external interrupt source */ iowrite32be(0x3F, priv->regs + SYS_IRQ_SOURCE_CTL); @@ -591,8 +594,12 @@ static void data_dma_cb(void *data) list_move_tail(&priv->inflight->entry, &priv->used); priv->inflight = NULL; - /* clear the FPGA status and re-enable interrupts */ - data_enable_interrupts(priv); + /* + * If data dumping is still enabled, then clear the FPGA + * status registers and re-enable FPGA interrupts + */ + if (priv->enabled) + data_enable_interrupts(priv); spin_unlock_irqrestore(&priv->lock, flags); @@ -708,6 +715,15 @@ static irqreturn_t data_irq(int irq, void *dev_id) spin_lock(&priv->lock); + /* + * This is an error case that should never happen. + * + * If this driver has a bug and manages to re-enable interrupts while + * a DMA is in progress, then we will hit this statement and should + * start paying attention immediately. + */ + BUG_ON(priv->inflight != NULL); + /* hide the interrupt by switching the IRQ driver to GPIO */ data_disable_interrupts(priv); @@ -762,11 +778,15 @@ out: */ static int data_device_enable(struct fpga_device *priv) { + bool enabled; u32 val; int ret; /* multiple enables are safe: they do nothing */ - if (priv->enabled) + spin_lock_irq(&priv->lock); + enabled = priv->enabled; + spin_unlock_irq(&priv->lock); + if (enabled) return 0; /* check that the FPGAs are programmed */ @@ -797,6 +817,9 @@ static int data_device_enable(struct fpga_device *priv) goto out_error; } + /* prevent the FPGAs from generating interrupts */ + data_disable_interrupts(priv); + /* hookup the irq handler */ ret = request_irq(priv->irq, data_irq, IRQF_SHARED, drv_name, priv); if (ret) { @@ -804,11 +827,13 @@ static int data_device_enable(struct fpga_device *priv) goto out_error; } - /* switch to the external FPGA IRQ line */ - data_enable_interrupts(priv); - - /* success, we're enabled */ + /* allow the DMA callback to re-enable FPGA interrupts */ + spin_lock_irq(&priv->lock); priv->enabled = true; + spin_unlock_irq(&priv->lock); + + /* allow the FPGAs to generate interrupts */ + data_enable_interrupts(priv); return 0; out_error: @@ -834,41 +859,40 @@ out_error: */ static int data_device_disable(struct fpga_device *priv) { - int ret; + spin_lock_irq(&priv->lock); /* allow multiple disable */ - if (!priv->enabled) + if (!priv->enabled) { + spin_unlock_irq(&priv->lock); return 0; + } + + /* + * Mark the device disabled + * + * This stops DMA callbacks from re-enabling interrupts + */ + priv->enabled = false; - /* switch to the internal GPIO IRQ line */ + /* prevent the FPGAs from generating interrupts */ data_disable_interrupts(priv); + /* wait until all ongoing DMA has finished */ + while (priv->inflight != NULL) { + spin_unlock_irq(&priv->lock); + wait_event(priv->wait, priv->inflight == NULL); + spin_lock_irq(&priv->lock); + } + + spin_unlock_irq(&priv->lock); + /* unhook the irq handler */ free_irq(priv->irq, priv); - /* - * wait for all outstanding DMA to complete - * - * Device interrupts are disabled, therefore another buffer cannot - * be marked inflight. - */ - ret = wait_event_interruptible(priv->wait, priv->inflight == NULL); - if (ret) - return ret; - /* free the correlation table */ sg_free_table(&priv->corl_table); priv->corl_nents = 0; - /* - * We are taking the spinlock not to protect priv->enabled, but instead - * to make sure that there are no readers in the process of altering - * the free or used lists while we are setting this flag. - */ - spin_lock_irq(&priv->lock); - priv->enabled = false; - spin_unlock_irq(&priv->lock); - /* free all buffers: the free and used lists are not being changed */ data_free_buffers(priv); return 0; @@ -896,15 +920,6 @@ static unsigned int list_num_entries(struct list_head *list) static int data_debug_show(struct seq_file *f, void *offset) { struct fpga_device *priv = f->private; - int ret; - - /* - * Lock the mutex first, so that we get an accurate value for enable - * Lock the spinlock next, to get accurate list counts - */ - ret = mutex_lock_interruptible(&priv->mutex); - if (ret) - return ret; spin_lock_irq(&priv->lock); @@ -917,7 +932,6 @@ static int data_debug_show(struct seq_file *f, void *offset) seq_printf(f, "num_dropped: %d\n", priv->num_dropped); spin_unlock_irq(&priv->lock); - mutex_unlock(&priv->mutex); return 0; } @@ -970,7 +984,13 @@ static ssize_t data_en_show(struct device *dev, struct device_attribute *attr, char *buf) { struct fpga_device *priv = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE, "%u\n", priv->enabled); + int ret; + + spin_lock_irq(&priv->lock); + ret = snprintf(buf, PAGE_SIZE, "%u\n", priv->enabled); + spin_unlock_irq(&priv->lock); + + return ret; } static ssize_t data_en_set(struct device *dev, struct device_attribute *attr, @@ -986,6 +1006,7 @@ static ssize_t data_en_set(struct device *dev, struct device_attribute *attr, return -EINVAL; } + /* protect against concurrent enable/disable */ ret = mutex_lock_interruptible(&priv->mutex); if (ret) return ret; @@ -1079,6 +1100,7 @@ static ssize_t data_read(struct file *filp, char __user *ubuf, size_t count, struct fpga_reader *reader = filp->private_data; struct fpga_device *priv = reader->priv; struct list_head *used = &priv->used; + bool drop_buffer = false; struct data_buf *dbuf; size_t avail; void *data; @@ -1166,10 +1188,12 @@ have_buffer: * One of two things has happened, the device is disabled, or the * device has been reconfigured underneath us. In either case, we * should just throw away the buffer. + * + * Lockdep complains if this is done under the spinlock, so we + * handle it during the unlock path. */ if (!priv->enabled || dbuf->size != priv->bufsize) { - videobuf_dma_unmap(priv->dev, &dbuf->vb); - data_free_buffer(dbuf); + drop_buffer = true; goto out_unlock; } @@ -1178,6 +1202,12 @@ have_buffer: out_unlock: spin_unlock_irq(&priv->lock); + + if (drop_buffer) { + videobuf_dma_unmap(priv->dev, &dbuf->vb); + data_free_buffer(dbuf); + } + return count; } |