diff options
author | Bartlomiej Zolnierkiewicz <bzolnier@gmail.com> | 2010-01-05 02:07:27 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-01-12 04:49:23 -0500 |
commit | f75d4a238770d83d3a0475ce7f34e3fa37de161e (patch) | |
tree | 522f9aa82d79993fb670e638757bae1240c03ca8 | |
parent | 4cd7d9247ffa2a27508c69563b66713519c196f5 (diff) |
icside: bring back ->maskproc method
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>
-rw-r--r-- | drivers/ide/icside.c | 64 |
1 files changed, 60 insertions, 4 deletions
diff --git a/drivers/ide/icside.c b/drivers/ide/icside.c index 0f67f1abbbd3..d7e6f09aa86b 100644 --- a/drivers/ide/icside.c +++ b/drivers/ide/icside.c | |||
@@ -65,6 +65,8 @@ static struct cardinfo icside_cardinfo_v6_2 = { | |||
65 | }; | 65 | }; |
66 | 66 | ||
67 | struct icside_state { | 67 | struct icside_state { |
68 | unsigned int channel; | ||
69 | unsigned int enabled; | ||
68 | void __iomem *irq_port; | 70 | void __iomem *irq_port; |
69 | void __iomem *ioc_base; | 71 | void __iomem *ioc_base; |
70 | unsigned int sel; | 72 | unsigned int sel; |
@@ -114,11 +116,18 @@ static void icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr) | |||
114 | struct icside_state *state = ec->irq_data; | 116 | struct icside_state *state = ec->irq_data; |
115 | void __iomem *base = state->irq_port; | 117 | void __iomem *base = state->irq_port; |
116 | 118 | ||
117 | writeb(0, base + ICS_ARCIN_V6_INTROFFSET_1); | 119 | state->enabled = 1; |
118 | readb(base + ICS_ARCIN_V6_INTROFFSET_2); | ||
119 | 120 | ||
120 | writeb(0, base + ICS_ARCIN_V6_INTROFFSET_2); | 121 | switch (state->channel) { |
121 | readb(base + ICS_ARCIN_V6_INTROFFSET_1); | 122 | case 0: |
123 | writeb(0, base + ICS_ARCIN_V6_INTROFFSET_1); | ||
124 | readb(base + ICS_ARCIN_V6_INTROFFSET_2); | ||
125 | break; | ||
126 | case 1: | ||
127 | writeb(0, base + ICS_ARCIN_V6_INTROFFSET_2); | ||
128 | readb(base + ICS_ARCIN_V6_INTROFFSET_1); | ||
129 | break; | ||
130 | } | ||
122 | } | 131 | } |
123 | 132 | ||
124 | /* Prototype: icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr) | 133 | /* Prototype: icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr) |
@@ -128,6 +137,8 @@ static void icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr) | |||
128 | { | 137 | { |
129 | struct icside_state *state = ec->irq_data; | 138 | struct icside_state *state = ec->irq_data; |
130 | 139 | ||
140 | state->enabled = 0; | ||
141 | |||
131 | readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1); | 142 | readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1); |
132 | readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_2); | 143 | readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_2); |
133 | } | 144 | } |
@@ -149,6 +160,44 @@ static const expansioncard_ops_t icside_ops_arcin_v6 = { | |||
149 | .irqpending = icside_irqpending_arcin_v6, | 160 | .irqpending = icside_irqpending_arcin_v6, |
150 | }; | 161 | }; |
151 | 162 | ||
163 | /* | ||
164 | * Handle routing of interrupts. This is called before | ||
165 | * we write the command to the drive. | ||
166 | */ | ||
167 | static void icside_maskproc(ide_drive_t *drive, int mask) | ||
168 | { | ||
169 | ide_hwif_t *hwif = drive->hwif; | ||
170 | struct expansion_card *ec = ECARD_DEV(hwif->dev); | ||
171 | struct icside_state *state = ecard_get_drvdata(ec); | ||
172 | unsigned long flags; | ||
173 | |||
174 | local_irq_save(flags); | ||
175 | |||
176 | state->channel = hwif->channel; | ||
177 | |||
178 | if (state->enabled && !mask) { | ||
179 | switch (hwif->channel) { | ||
180 | case 0: | ||
181 | writeb(0, state->irq_port + ICS_ARCIN_V6_INTROFFSET_1); | ||
182 | readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_2); | ||
183 | break; | ||
184 | case 1: | ||
185 | writeb(0, state->irq_port + ICS_ARCIN_V6_INTROFFSET_2); | ||
186 | readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1); | ||
187 | break; | ||
188 | } | ||
189 | } else { | ||
190 | readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_2); | ||
191 | readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1); | ||
192 | } | ||
193 | |||
194 | local_irq_restore(flags); | ||
195 | } | ||
196 | |||
197 | static const struct ide_port_ops icside_v6_no_dma_port_ops = { | ||
198 | .maskproc = icside_maskproc, | ||
199 | }; | ||
200 | |||
152 | #ifdef CONFIG_BLK_DEV_IDEDMA_ICS | 201 | #ifdef CONFIG_BLK_DEV_IDEDMA_ICS |
153 | /* | 202 | /* |
154 | * SG-DMA support. | 203 | * SG-DMA support. |
@@ -228,6 +277,7 @@ static void icside_set_dma_mode(ide_drive_t *drive, const u8 xfer_mode) | |||
228 | 277 | ||
229 | static const struct ide_port_ops icside_v6_port_ops = { | 278 | static const struct ide_port_ops icside_v6_port_ops = { |
230 | .set_dma_mode = icside_set_dma_mode, | 279 | .set_dma_mode = icside_set_dma_mode, |
280 | .maskproc = icside_maskproc, | ||
231 | }; | 281 | }; |
232 | 282 | ||
233 | static void icside_dma_host_set(ide_drive_t *drive, int on) | 283 | static void icside_dma_host_set(ide_drive_t *drive, int on) |
@@ -272,6 +322,11 @@ static int icside_dma_setup(ide_drive_t *drive, struct ide_cmd *cmd) | |||
272 | BUG_ON(dma_channel_active(ec->dma)); | 322 | BUG_ON(dma_channel_active(ec->dma)); |
273 | 323 | ||
274 | /* | 324 | /* |
325 | * Ensure that we have the right interrupt routed. | ||
326 | */ | ||
327 | icside_maskproc(drive, 0); | ||
328 | |||
329 | /* | ||
275 | * Route the DMA signals to the correct interface. | 330 | * Route the DMA signals to the correct interface. |
276 | */ | 331 | */ |
277 | writeb(state->sel | hwif->channel, state->ioc_base); | 332 | writeb(state->sel | hwif->channel, state->ioc_base); |
@@ -399,6 +454,7 @@ err_free: | |||
399 | 454 | ||
400 | static const struct ide_port_info icside_v6_port_info __initdata = { | 455 | static const struct ide_port_info icside_v6_port_info __initdata = { |
401 | .init_dma = icside_dma_off_init, | 456 | .init_dma = icside_dma_off_init, |
457 | .port_ops = &icside_v6_no_dma_port_ops, | ||
402 | .dma_ops = &icside_v6_dma_ops, | 458 | .dma_ops = &icside_v6_dma_ops, |
403 | .host_flags = IDE_HFLAG_SERIALIZE | IDE_HFLAG_MMIO, | 459 | .host_flags = IDE_HFLAG_SERIALIZE | IDE_HFLAG_MMIO, |
404 | .mwdma_mask = ATA_MWDMA2, | 460 | .mwdma_mask = ATA_MWDMA2, |