diff mbox

[5/5] cmd646: synchronise UDMA interrupt status with DMA interrupt status

Message ID 1407515016-26273-6-git-send-email-mark.cave-ayland@ilande.co.uk
State New
Headers show

Commit Message

Mark Cave-Ayland Aug. 8, 2014, 4:23 p.m. UTC
Make sure that both registers are synchronised when being accessed through
PCI configuration space.

Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
---
 hw/ide/cmd646.c |   24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

Comments

Stefan Hajnoczi Aug. 11, 2014, 3:12 p.m. UTC | #1
On Fri, Aug 08, 2014 at 05:23:36PM +0100, Mark Cave-Ayland wrote:
> @@ -322,6 +342,10 @@ static int pci_cmd646_ide_initfn(PCIDevice *dev)
>      }
>  
>      /* Set write-to-clear interrupt bits */
> +    dev->wmask[CFR] = 0x0;
> +    dev->w1cmask[CFR] = CFR_INTR_CH0;
> +    dev->wmask[ARTTIM23] = 0x0;
> +    dev->w1cmask[ARTTIM23] = ARTTIM23_INTR_CH1;
>      dev->wmask[MRDMODE] = 0x0;
>      dev->w1cmask[MRDMODE] = MRDMODE_INTR_CH0 | MRDMODE_INTR_CH1;

It is not clear to me why the mask for MRDMODE has both Channel 0 and 1
but the ARTTIM23 and CFR masks only have one channel each.

Please post a link to the datasheet.
Mark Cave-Ayland Aug. 11, 2014, 3:33 p.m. UTC | #2
On 11/08/14 16:12, Stefan Hajnoczi wrote:

> On Fri, Aug 08, 2014 at 05:23:36PM +0100, Mark Cave-Ayland wrote:
>> @@ -322,6 +342,10 @@ static int pci_cmd646_ide_initfn(PCIDevice *dev)
>>       }
>>
>>       /* Set write-to-clear interrupt bits */
>> +    dev->wmask[CFR] = 0x0;
>> +    dev->w1cmask[CFR] = CFR_INTR_CH0;
>> +    dev->wmask[ARTTIM23] = 0x0;
>> +    dev->w1cmask[ARTTIM23] = ARTTIM23_INTR_CH1;
>>       dev->wmask[MRDMODE] = 0x0;
>>       dev->w1cmask[MRDMODE] = MRDMODE_INTR_CH0 | MRDMODE_INTR_CH1;
>
> It is not clear to me why the mask for MRDMODE has both Channel 0 and 1
> but the ARTTIM23 and CFR masks only have one channel each.
>
> Please post a link to the datasheet.

Hi Stefan,

Thanks for the review. You can find a copy of the 646U2 datasheet at 
http://gkernel.sourceforge.net/specs/sii/PCI0646U2Specr030399.PDF.bz2.

My understanding from the datasheet is that CFR is the primary channel 
interrupt whilst ARTTIM23 is the secondary channel interrupt, and the 
note on page 28 explains that these bits are also accessible via the 
relevant bits of the MRDMODE register.

This fixes cmd646 under NetBSD since it programs UDMA via MRDMODE but 
clears the interrupts via CFR and ARTTIM23, so without this patchset the 
driver hangs because the interrupt isn't properly cleared across both 
registers. And I can also verify that Linux experiences no regressions 
with this patchset applied.


Many thanks,

Mark.
Stefan Hajnoczi Aug. 12, 2014, 8:47 a.m. UTC | #3
On Mon, Aug 11, 2014 at 04:33:01PM +0100, Mark Cave-Ayland wrote:
> On 11/08/14 16:12, Stefan Hajnoczi wrote:
> 
> >On Fri, Aug 08, 2014 at 05:23:36PM +0100, Mark Cave-Ayland wrote:
> >>@@ -322,6 +342,10 @@ static int pci_cmd646_ide_initfn(PCIDevice *dev)
> >>      }
> >>
> >>      /* Set write-to-clear interrupt bits */
> >>+    dev->wmask[CFR] = 0x0;
> >>+    dev->w1cmask[CFR] = CFR_INTR_CH0;
> >>+    dev->wmask[ARTTIM23] = 0x0;
> >>+    dev->w1cmask[ARTTIM23] = ARTTIM23_INTR_CH1;
> >>      dev->wmask[MRDMODE] = 0x0;
> >>      dev->w1cmask[MRDMODE] = MRDMODE_INTR_CH0 | MRDMODE_INTR_CH1;
> >
> >It is not clear to me why the mask for MRDMODE has both Channel 0 and 1
> >but the ARTTIM23 and CFR masks only have one channel each.
> >
> >Please post a link to the datasheet.
> 
> Hi Stefan,
> 
> Thanks for the review. You can find a copy of the 646U2 datasheet at
> http://gkernel.sourceforge.net/specs/sii/PCI0646U2Specr030399.PDF.bz2.
> 
> My understanding from the datasheet is that CFR is the primary channel
> interrupt whilst ARTTIM23 is the secondary channel interrupt, and the note
> on page 28 explains that these bits are also accessible via the relevant
> bits of the MRDMODE register.
> 
> This fixes cmd646 under NetBSD since it programs UDMA via MRDMODE but clears
> the interrupts via CFR and ARTTIM23, so without this patchset the driver
> hangs because the interrupt isn't properly cleared across both registers.
> And I can also verify that Linux experiences no regressions with this
> patchset applied.

Great, thanks!

Stefan
diff mbox

Patch

diff --git a/hw/ide/cmd646.c b/hw/ide/cmd646.c
index b8dc4ab..74d0deb 100644
--- a/hw/ide/cmd646.c
+++ b/hw/ide/cmd646.c
@@ -146,6 +146,22 @@  static void cmd646_update_dma_interrupts(PCIDevice *pd)
     }
 }
 
+static void cmd646_update_udma_interrupts(PCIDevice *pd)
+{
+    /* Sync UDMA interrupt status from DMA interrupt status */
+    if (pd->config[CFR] & CFR_INTR_CH0) {
+        pd->config[MRDMODE] |= MRDMODE_INTR_CH0;
+    } else {
+        pd->config[MRDMODE] &= ~MRDMODE_INTR_CH0;
+    }
+
+    if (pd->config[ARTTIM23] & ARTTIM23_INTR_CH1) {
+        pd->config[MRDMODE] |= MRDMODE_INTR_CH1;
+    } else {
+        pd->config[MRDMODE] &= ~MRDMODE_INTR_CH1;
+    }
+}
+
 static uint64_t bmdma_read(void *opaque, hwaddr addr,
                            unsigned size)
 {
@@ -296,6 +312,10 @@  static void cmd646_pci_config_write(PCIDevice *d, uint32_t addr, uint32_t val,
 
     for (i = addr; i < addr + l; i++) {
         switch (i) {
+        case CFR:
+        case ARTTIM23:
+            cmd646_update_udma_interrupts(d);
+            break;
         case MRDMODE:
             cmd646_update_dma_interrupts(d);
             break;
@@ -322,6 +342,10 @@  static int pci_cmd646_ide_initfn(PCIDevice *dev)
     }
 
     /* Set write-to-clear interrupt bits */
+    dev->wmask[CFR] = 0x0;
+    dev->w1cmask[CFR] = CFR_INTR_CH0;
+    dev->wmask[ARTTIM23] = 0x0;
+    dev->w1cmask[ARTTIM23] = ARTTIM23_INTR_CH1;
     dev->wmask[MRDMODE] = 0x0;
     dev->w1cmask[MRDMODE] = MRDMODE_INTR_CH0 | MRDMODE_INTR_CH1;