Skip to content

Commit

Permalink
icside: bring back ->maskproc method
Browse files Browse the repository at this point in the history
Bring back ->maskproc method since it is still needed for proper operation,
as noticed by Russell King:

> This change is bogus.
>
>         writeb(0, base + ICS_ARCIN_V6_INTROFFSET_1);
>         readb(base + ICS_ARCIN_V6_INTROFFSET_2);
>
>         writeb(0, base + ICS_ARCIN_V6_INTROFFSET_2);
>         readb(base + ICS_ARCIN_V6_INTROFFSET_1);
>
> This sequence of code does:
>
> 1. enable interrupt 1
> 2. disable interrupt 2
> 3. enable interrupt 2
> 4. disable interrupt 1
>
> which results in the interrupt for the second channel being enabled -
> leaving channel 1 blocked.
>
> Firstly, icside shares its two IDE channels with one DMA engine - so it's
> a simplex interface.  IDE supports those (or did when the code was written)
> serializing requests between the two interfaces.  libata does not.
>
> Secondly, the interrupt lines on icside float when there's no drive connected
> or when the drive has its NIEN bit set, which means that you get spurious
> screaming interrupts which can kill off all expansion card interrupts on
> the machine unless you disable the channel interrupt on the card.
>
> Since libata can not serialize the operation of the two channels like IDE
> can, the libata version of the icside driver does not contain the interrupt
> stearing logic.  Instead, it looks at the status after reset, and if
> nothing was found on that channel, it masks the interrupt from that
> channel.

This patch reverts changes done in commit dff8817 (I became confused due to
non-standard & undocumented ->maskproc method, anyway sorry about that).

Noticed-by: Russell King <rmk@arm.linux.org.uk>
Signed-off-by: Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Bartlomiej Zolnierkiewicz authored and David S. Miller committed Jan 12, 2010
1 parent 4cd7d92 commit f75d4a2
Showing 1 changed file with 60 additions and 4 deletions.
64 changes: 60 additions & 4 deletions drivers/ide/icside.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ static struct cardinfo icside_cardinfo_v6_2 = {
};

struct icside_state {
unsigned int channel;
unsigned int enabled;
void __iomem *irq_port;
void __iomem *ioc_base;
unsigned int sel;
Expand Down Expand Up @@ -114,11 +116,18 @@ static void icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr)
struct icside_state *state = ec->irq_data;
void __iomem *base = state->irq_port;

writeb(0, base + ICS_ARCIN_V6_INTROFFSET_1);
readb(base + ICS_ARCIN_V6_INTROFFSET_2);
state->enabled = 1;

writeb(0, base + ICS_ARCIN_V6_INTROFFSET_2);
readb(base + ICS_ARCIN_V6_INTROFFSET_1);
switch (state->channel) {
case 0:
writeb(0, base + ICS_ARCIN_V6_INTROFFSET_1);
readb(base + ICS_ARCIN_V6_INTROFFSET_2);
break;
case 1:
writeb(0, base + ICS_ARCIN_V6_INTROFFSET_2);
readb(base + ICS_ARCIN_V6_INTROFFSET_1);
break;
}
}

/* Prototype: icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr)
Expand All @@ -128,6 +137,8 @@ static void icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr)
{
struct icside_state *state = ec->irq_data;

state->enabled = 0;

readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1);
readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_2);
}
Expand All @@ -149,6 +160,44 @@ static const expansioncard_ops_t icside_ops_arcin_v6 = {
.irqpending = icside_irqpending_arcin_v6,
};

/*
* Handle routing of interrupts. This is called before
* we write the command to the drive.
*/
static void icside_maskproc(ide_drive_t *drive, int mask)
{
ide_hwif_t *hwif = drive->hwif;
struct expansion_card *ec = ECARD_DEV(hwif->dev);
struct icside_state *state = ecard_get_drvdata(ec);
unsigned long flags;

local_irq_save(flags);

state->channel = hwif->channel;

if (state->enabled && !mask) {
switch (hwif->channel) {
case 0:
writeb(0, state->irq_port + ICS_ARCIN_V6_INTROFFSET_1);
readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_2);
break;
case 1:
writeb(0, state->irq_port + ICS_ARCIN_V6_INTROFFSET_2);
readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1);
break;
}
} else {
readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_2);
readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1);
}

local_irq_restore(flags);
}

static const struct ide_port_ops icside_v6_no_dma_port_ops = {
.maskproc = icside_maskproc,
};

#ifdef CONFIG_BLK_DEV_IDEDMA_ICS
/*
* SG-DMA support.
Expand Down Expand Up @@ -228,6 +277,7 @@ static void icside_set_dma_mode(ide_drive_t *drive, const u8 xfer_mode)

static const struct ide_port_ops icside_v6_port_ops = {
.set_dma_mode = icside_set_dma_mode,
.maskproc = icside_maskproc,
};

static void icside_dma_host_set(ide_drive_t *drive, int on)
Expand Down Expand Up @@ -271,6 +321,11 @@ static int icside_dma_setup(ide_drive_t *drive, struct ide_cmd *cmd)
*/
BUG_ON(dma_channel_active(ec->dma));

/*
* Ensure that we have the right interrupt routed.
*/
icside_maskproc(drive, 0);

/*
* Route the DMA signals to the correct interface.
*/
Expand Down Expand Up @@ -399,6 +454,7 @@ icside_register_v5(struct icside_state *state, struct expansion_card *ec)

static const struct ide_port_info icside_v6_port_info __initdata = {
.init_dma = icside_dma_off_init,
.port_ops = &icside_v6_no_dma_port_ops,
.dma_ops = &icside_v6_dma_ops,
.host_flags = IDE_HFLAG_SERIALIZE | IDE_HFLAG_MMIO,
.mwdma_mask = ATA_MWDMA2,
Expand Down

0 comments on commit f75d4a2

Please sign in to comment.