diff options
author | Dan Williams <dan.j.williams@intel.com> | 2007-02-23 18:36:43 -0500 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2007-02-24 20:54:26 -0500 |
commit | ea34e45a4670c4fa0da3442fc74789fd66c1201b (patch) | |
tree | ace85060ad169f4c48f2ba8e02e9f8c54f53e781 /drivers/ata | |
parent | e2f8fb72144a9f38d44ccf3f939e939392eda659 (diff) |
sata_vsc: refactor vsc_sata_interrupt and hook up error handling
Separate sata_vsc interrupt handling into a normal (per-port) path and an
error path with the addition of vsc_port_intr and vsc_error_intr
respectively. The error path handles interrupt based
hotplug events which requires the definition of vsc_freeze and vsc_thaw.
Note: vsc_port_intr has a workaround for unexpected interrupts that occur
during polled commands. This fixes a regression between 2.6.19 and 2.6.20.
Changes in take2:
* removed definition of invalid fis bit
* let standard ata-error-handling handle the serror register
* clear all unhandled interrupts
* revert changes to vsc_intr_mask_update (vsc_thaw enables all interrupts)
* use unlikely() for the pci-abort and not-our-interrupt cases in vsc_sata_interrupt
Changes in take3:
* Unify the "add" + "hook-up" patches into this single patch
[htejun@gmail.com: clean up comments and suggestions]
Cc: Jeremy Higdon <jeremy@sgi.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers/ata')
-rw-r--r-- | drivers/ata/sata_vsc.c | 123 |
1 files changed, 74 insertions, 49 deletions
diff --git a/drivers/ata/sata_vsc.c b/drivers/ata/sata_vsc.c index 2fd037bde090..f0d86cbe2e59 100644 --- a/drivers/ata/sata_vsc.c +++ b/drivers/ata/sata_vsc.c | |||
@@ -98,10 +98,6 @@ enum { | |||
98 | VSC_SATA_INT_PHY_CHANGE), | 98 | VSC_SATA_INT_PHY_CHANGE), |
99 | }; | 99 | }; |
100 | 100 | ||
101 | #define is_vsc_sata_int_err(port_idx, int_status) \ | ||
102 | (int_status & (VSC_SATA_INT_ERROR << (8 * port_idx))) | ||
103 | |||
104 | |||
105 | static u32 vsc_sata_scr_read (struct ata_port *ap, unsigned int sc_reg) | 101 | static u32 vsc_sata_scr_read (struct ata_port *ap, unsigned int sc_reg) |
106 | { | 102 | { |
107 | if (sc_reg > SCR_CONTROL) | 103 | if (sc_reg > SCR_CONTROL) |
@@ -119,6 +115,28 @@ static void vsc_sata_scr_write (struct ata_port *ap, unsigned int sc_reg, | |||
119 | } | 115 | } |
120 | 116 | ||
121 | 117 | ||
118 | static void vsc_freeze(struct ata_port *ap) | ||
119 | { | ||
120 | void __iomem *mask_addr; | ||
121 | |||
122 | mask_addr = ap->host->iomap[VSC_MMIO_BAR] + | ||
123 | VSC_SATA_INT_MASK_OFFSET + ap->port_no; | ||
124 | |||
125 | writeb(0, mask_addr); | ||
126 | } | ||
127 | |||
128 | |||
129 | static void vsc_thaw(struct ata_port *ap) | ||
130 | { | ||
131 | void __iomem *mask_addr; | ||
132 | |||
133 | mask_addr = ap->host->iomap[VSC_MMIO_BAR] + | ||
134 | VSC_SATA_INT_MASK_OFFSET + ap->port_no; | ||
135 | |||
136 | writeb(0xff, mask_addr); | ||
137 | } | ||
138 | |||
139 | |||
122 | static void vsc_intr_mask_update(struct ata_port *ap, u8 ctl) | 140 | static void vsc_intr_mask_update(struct ata_port *ap, u8 ctl) |
123 | { | 141 | { |
124 | void __iomem *mask_addr; | 142 | void __iomem *mask_addr; |
@@ -203,6 +221,36 @@ static void vsc_sata_tf_read(struct ata_port *ap, struct ata_taskfile *tf) | |||
203 | } | 221 | } |
204 | } | 222 | } |
205 | 223 | ||
224 | static inline void vsc_error_intr(u8 port_status, struct ata_port *ap) | ||
225 | { | ||
226 | if (port_status & (VSC_SATA_INT_PHY_CHANGE | VSC_SATA_INT_ERROR_M)) | ||
227 | ata_port_freeze(ap); | ||
228 | else | ||
229 | ata_port_abort(ap); | ||
230 | } | ||
231 | |||
232 | static void vsc_port_intr(u8 port_status, struct ata_port *ap) | ||
233 | { | ||
234 | struct ata_queued_cmd *qc; | ||
235 | int handled = 0; | ||
236 | |||
237 | if (unlikely(port_status & VSC_SATA_INT_ERROR)) { | ||
238 | vsc_error_intr(port_status, ap); | ||
239 | return; | ||
240 | } | ||
241 | |||
242 | qc = ata_qc_from_tag(ap, ap->active_tag); | ||
243 | if (qc && likely(!(qc->tf.flags & ATA_TFLAG_POLLING))) | ||
244 | handled = ata_host_intr(ap, qc); | ||
245 | |||
246 | /* We received an interrupt during a polled command, | ||
247 | * or some other spurious condition. Interrupt reporting | ||
248 | * with this hardware is fairly reliable so it is safe to | ||
249 | * simply clear the interrupt | ||
250 | */ | ||
251 | if (unlikely(!handled)) | ||
252 | ata_chk_status(ap); | ||
253 | } | ||
206 | 254 | ||
207 | /* | 255 | /* |
208 | * vsc_sata_interrupt | 256 | * vsc_sata_interrupt |
@@ -214,59 +262,36 @@ static irqreturn_t vsc_sata_interrupt (int irq, void *dev_instance) | |||
214 | struct ata_host *host = dev_instance; | 262 | struct ata_host *host = dev_instance; |
215 | unsigned int i; | 263 | unsigned int i; |
216 | unsigned int handled = 0; | 264 | unsigned int handled = 0; |
217 | u32 int_status; | 265 | u32 status; |
218 | |||
219 | spin_lock(&host->lock); | ||
220 | 266 | ||
221 | int_status = readl(host->iomap[VSC_MMIO_BAR] + | 267 | status = readl(host->iomap[VSC_MMIO_BAR] + VSC_SATA_INT_STAT_OFFSET); |
222 | VSC_SATA_INT_STAT_OFFSET); | ||
223 | 268 | ||
224 | for (i = 0; i < host->n_ports; i++) { | 269 | if (unlikely(status == 0xffffffff || status == 0)) { |
225 | if (int_status & ((u32) 0xFF << (8 * i))) { | 270 | if (status) |
226 | struct ata_port *ap; | 271 | dev_printk(KERN_ERR, host->dev, |
272 | ": IRQ status == 0xffffffff, " | ||
273 | "PCI fault or device removal?\n"); | ||
274 | goto out; | ||
275 | } | ||
227 | 276 | ||
228 | ap = host->ports[i]; | 277 | spin_lock(&host->lock); |
229 | 278 | ||
230 | if (is_vsc_sata_int_err(i, int_status)) { | 279 | for (i = 0; i < host->n_ports; i++) { |
231 | u32 err_status; | 280 | u8 port_status = (status >> (8 * i)) & 0xff; |
232 | printk(KERN_DEBUG "%s: ignoring interrupt(s)\n", __FUNCTION__); | 281 | if (port_status) { |
233 | err_status = ap ? vsc_sata_scr_read(ap, SCR_ERROR) : 0; | 282 | struct ata_port *ap = host->ports[i]; |
234 | vsc_sata_scr_write(ap, SCR_ERROR, err_status); | ||
235 | handled++; | ||
236 | } | ||
237 | 283 | ||
238 | if (ap && !(ap->flags & ATA_FLAG_DISABLED)) { | 284 | if (ap && !(ap->flags & ATA_FLAG_DISABLED)) { |
239 | struct ata_queued_cmd *qc; | 285 | vsc_port_intr(port_status, ap); |
240 | 286 | handled++; | |
241 | qc = ata_qc_from_tag(ap, ap->active_tag); | 287 | } else |
242 | if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING))) | 288 | dev_printk(KERN_ERR, host->dev, |
243 | handled += ata_host_intr(ap, qc); | 289 | ": interrupt from disabled port %d\n", i); |
244 | else if (is_vsc_sata_int_err(i, int_status)) { | ||
245 | /* | ||
246 | * On some chips (i.e. Intel 31244), an error | ||
247 | * interrupt will sneak in at initialization | ||
248 | * time (phy state changes). Clearing the SCR | ||
249 | * error register is not required, but it prevents | ||
250 | * the phy state change interrupts from recurring | ||
251 | * later. | ||
252 | */ | ||
253 | u32 err_status; | ||
254 | err_status = vsc_sata_scr_read(ap, SCR_ERROR); | ||
255 | printk(KERN_DEBUG "%s: clearing interrupt, " | ||
256 | "status %x; sata err status %x\n", | ||
257 | __FUNCTION__, | ||
258 | int_status, err_status); | ||
259 | vsc_sata_scr_write(ap, SCR_ERROR, err_status); | ||
260 | /* Clear interrupt status */ | ||
261 | ata_chk_status(ap); | ||
262 | handled++; | ||
263 | } | ||
264 | } | ||
265 | } | 290 | } |
266 | } | 291 | } |
267 | 292 | ||
268 | spin_unlock(&host->lock); | 293 | spin_unlock(&host->lock); |
269 | 294 | out: | |
270 | return IRQ_RETVAL(handled); | 295 | return IRQ_RETVAL(handled); |
271 | } | 296 | } |
272 | 297 | ||
@@ -304,8 +329,8 @@ static const struct ata_port_operations vsc_sata_ops = { | |||
304 | .qc_prep = ata_qc_prep, | 329 | .qc_prep = ata_qc_prep, |
305 | .qc_issue = ata_qc_issue_prot, | 330 | .qc_issue = ata_qc_issue_prot, |
306 | .data_xfer = ata_data_xfer, | 331 | .data_xfer = ata_data_xfer, |
307 | .freeze = ata_bmdma_freeze, | 332 | .freeze = vsc_freeze, |
308 | .thaw = ata_bmdma_thaw, | 333 | .thaw = vsc_thaw, |
309 | .error_handler = ata_bmdma_error_handler, | 334 | .error_handler = ata_bmdma_error_handler, |
310 | .post_internal_cmd = ata_bmdma_post_internal_cmd, | 335 | .post_internal_cmd = ata_bmdma_post_internal_cmd, |
311 | .irq_handler = vsc_sata_interrupt, | 336 | .irq_handler = vsc_sata_interrupt, |