ide_startstop_t do_rw_taskfile (ide_drive_t *drive, ide_task_t *task) { ide_hwif_t *hwif = HWIF(drive); struct ide_taskfile *tf = &task->tf; ide_handler_t *handler = NULL; const struct ide_tp_ops *tp_ops = hwif->tp_ops; const struct ide_dma_ops *dma_ops = hwif->dma_ops; if (task->data_phase == TASKFILE_MULTI_IN || task->data_phase == TASKFILE_MULTI_OUT) { if (!drive->mult_count) { printk(KERN_ERR "%s: multimode not set!\n", drive->name); return ide_stopped; } } if (task->tf_flags & IDE_TFLAG_FLAGGED) task->tf_flags |= IDE_TFLAG_FLAGGED_SET_IN_FLAGS; if ((task->tf_flags & IDE_TFLAG_DMA_PIO_FALLBACK) == 0) { ide_tf_dump(drive->name, tf); tp_ops->set_irq(hwif, 1); SELECT_MASK(drive, 0); tp_ops->tf_load(drive, task); } switch (task->data_phase) { case TASKFILE_MULTI_OUT: case TASKFILE_OUT: tp_ops->exec_command(hwif, tf->command); ndelay(400); /* FIXME */ return pre_task_out_intr(drive, task->rq); case TASKFILE_MULTI_IN: case TASKFILE_IN: handler = task_in_intr; /* fall-through */ case TASKFILE_NO_DATA: if (handler == NULL) handler = task_no_data_intr; /* WIN_{SPECIFY,RESTORE,SETMULT} use custom handlers */ if (task->tf_flags & IDE_TFLAG_CUSTOM_HANDLER) { switch (tf->command) { case WIN_SPECIFY: handler = set_geometry_intr; break; case WIN_RESTORE: handler = recal_intr; break; case WIN_SETMULT: handler = set_multmode_intr; break; } } ide_execute_command(drive, tf->command, handler, WAIT_WORSTCASE, NULL); return ide_started; default: if (drive->using_dma == 0 || dma_ops->dma_setup(drive)) return ide_stopped; dma_ops->dma_exec_cmd(drive, tf->command); dma_ops->dma_start(drive); return ide_started; } }
ide_startstop_t do_rw_taskfile (ide_drive_t *drive, ide_task_t *task) { ide_hwif_t *hwif = drive->hwif; struct ide_taskfile *tf = &task->tf; ide_handler_t *handler = NULL; const struct ide_tp_ops *tp_ops = hwif->tp_ops; const struct ide_dma_ops *dma_ops = hwif->dma_ops; if (task->data_phase == TASKFILE_MULTI_IN || task->data_phase == TASKFILE_MULTI_OUT) { if (!drive->mult_count) { printk(KERN_ERR "%s: multimode not set!\n", drive->name); return ide_stopped; } } if (task->tf_flags & IDE_TFLAG_FLAGGED) task->tf_flags |= IDE_TFLAG_FLAGGED_SET_IN_FLAGS; memcpy(&hwif->task, task, sizeof(*task)); if ((task->tf_flags & IDE_TFLAG_DMA_PIO_FALLBACK) == 0) { ide_tf_dump(drive->name, tf); tp_ops->set_irq(hwif, 1); SELECT_MASK(drive, 0); tp_ops->tf_load(drive, task); } switch (task->data_phase) { case TASKFILE_MULTI_OUT: case TASKFILE_OUT: tp_ops->exec_command(hwif, tf->command); ndelay(400); /* FIXME */ return pre_task_out_intr(drive, task->rq); case TASKFILE_MULTI_IN: case TASKFILE_IN: handler = task_in_intr; /* fall-through */ case TASKFILE_NO_DATA: if (handler == NULL) handler = task_no_data_intr; ide_execute_command(drive, tf->command, handler, WAIT_WORSTCASE, NULL); return ide_started; default: if ((drive->dev_flags & IDE_DFLAG_USING_DMA) == 0 || dma_ops->dma_setup(drive)) return ide_stopped; dma_ops->dma_exec_cmd(drive, tf->command); dma_ops->dma_start(drive); return ide_started; } }
/* * Handler for commands without a data phase */ static ide_startstop_t task_no_data_intr(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; ide_task_t *task = &hwif->task; struct ide_taskfile *tf = &task->tf; int custom = (task->tf_flags & IDE_TFLAG_CUSTOM_HANDLER) ? 1 : 0; int retries = (custom && tf->command == ATA_CMD_INIT_DEV_PARAMS) ? 5 : 1; u8 stat; local_irq_enable_in_hardirq(); while (1) { stat = hwif->tp_ops->read_status(hwif); if ((stat & ATA_BUSY) == 0 || retries-- == 0) break; udelay(10); }; if (!OK_STAT(stat, ATA_DRDY, BAD_STAT)) { if (custom && tf->command == ATA_CMD_SET_MULTI) { drive->mult_req = drive->mult_count = 0; drive->special.b.recalibrate = 1; (void)ide_dump_status(drive, __func__, stat); return ide_stopped; } else if (custom && tf->command == ATA_CMD_INIT_DEV_PARAMS) { if ((stat & (ATA_ERR | ATA_DRQ)) == 0) { ide_set_handler(drive, &task_no_data_intr, WAIT_WORSTCASE, NULL); return ide_started; } } return ide_error(drive, "task_no_data_intr", stat); /* calls ide_end_drive_cmd */ } if (!custom) ide_end_drive_cmd(drive, stat, ide_read_error(drive)); else if (tf->command == ATA_CMD_IDLEIMMEDIATE) { hwif->tp_ops->tf_read(drive, task); if (tf->lbal != 0xc4) { printk(KERN_ERR "%s: head unload failed!\n", drive->name); ide_tf_dump(drive->name, tf); } else drive->dev_flags |= IDE_DFLAG_PARKED; ide_end_drive_cmd(drive, stat, ide_read_error(drive)); } else if (tf->command == ATA_CMD_SET_MULTI) drive->mult_count = drive->mult_req; return ide_stopped; }
void ide_pktcmd_tf_load(ide_drive_t *drive, u32 tf_flags, u16 bcount, u8 dma) { ide_hwif_t *hwif = drive->hwif; ide_task_t task; memset(&task, 0, sizeof(task)); task.tf_flags = IDE_TFLAG_OUT_LBAH | IDE_TFLAG_OUT_LBAM | IDE_TFLAG_OUT_FEATURE | tf_flags; task.tf.feature = dma; /* Use PIO/DMA */ task.tf.lbam = bcount & 0xff; task.tf.lbah = (bcount >> 8) & 0xff; ide_tf_dump(drive->name, &task.tf); hwif->tp_ops->set_irq(hwif, 1); SELECT_MASK(drive, 0); hwif->tp_ops->tf_load(drive, &task); }
void ide_complete_cmd(ide_drive_t *drive, struct ide_cmd *cmd, u8 stat, u8 err) { const struct ide_tp_ops *tp_ops = drive->hwif->tp_ops; struct ide_taskfile *tf = &cmd->tf; struct request *rq = cmd->rq; u8 tf_cmd = tf->command; tf->error = err; tf->status = stat; if (cmd->ftf_flags & IDE_FTFLAG_IN_DATA) { u8 data[2]; tp_ops->input_data(drive, cmd, data, 2); cmd->tf.data = data[0]; cmd->hob.data = data[1]; } ide_tf_readback(drive, cmd); if ((cmd->tf_flags & IDE_TFLAG_CUSTOM_HANDLER) && tf_cmd == ATA_CMD_IDLEIMMEDIATE) { if (tf->lbal != 0xc4) { printk(KERN_ERR "%s: head unload failed!\n", drive->name); ide_tf_dump(drive->name, cmd); } else drive->dev_flags |= IDE_DFLAG_PARKED; } if (rq && rq->cmd_type == REQ_TYPE_ATA_TASKFILE) { struct ide_cmd *orig_cmd = rq->special; if (cmd->tf_flags & IDE_TFLAG_DYN) kfree(orig_cmd); else memcpy(orig_cmd, cmd, sizeof(*cmd)); } }
ide_startstop_t do_rw_taskfile(ide_drive_t *drive, struct ide_cmd *orig_cmd) { ide_hwif_t *hwif = drive->hwif; struct ide_cmd *cmd = &hwif->cmd; struct ide_taskfile *tf = &cmd->tf; ide_handler_t *handler = NULL; const struct ide_tp_ops *tp_ops = hwif->tp_ops; const struct ide_dma_ops *dma_ops = hwif->dma_ops; if (orig_cmd->protocol == ATA_PROT_PIO && (orig_cmd->tf_flags & IDE_TFLAG_MULTI_PIO) && drive->mult_count == 0) { pr_err("%s: multimode not set!\n", drive->name); return ide_stopped; } if (orig_cmd->ftf_flags & IDE_FTFLAG_FLAGGED) orig_cmd->ftf_flags |= IDE_FTFLAG_SET_IN_FLAGS; memcpy(cmd, orig_cmd, sizeof(*cmd)); if ((cmd->tf_flags & IDE_TFLAG_DMA_PIO_FALLBACK) == 0) { ide_tf_dump(drive->name, cmd); tp_ops->write_devctl(hwif, ATA_DEVCTL_OBS); if (cmd->ftf_flags & IDE_FTFLAG_OUT_DATA) { u8 data[2] = { cmd->tf.data, cmd->hob.data }; tp_ops->output_data(drive, cmd, data, 2); } if (cmd->valid.out.tf & IDE_VALID_DEVICE) { u8 HIHI = (cmd->tf_flags & IDE_TFLAG_LBA48) ? 0xE0 : 0xEF; if (!(cmd->ftf_flags & IDE_FTFLAG_FLAGGED)) cmd->tf.device &= HIHI; cmd->tf.device |= drive->select; } tp_ops->tf_load(drive, &cmd->hob, cmd->valid.out.hob); tp_ops->tf_load(drive, &cmd->tf, cmd->valid.out.tf); } switch (cmd->protocol) { case ATA_PROT_PIO: if (cmd->tf_flags & IDE_TFLAG_WRITE) { tp_ops->exec_command(hwif, tf->command); ndelay(400); return pre_task_out_intr(drive, cmd); } handler = task_pio_intr; /* fall-through */ case ATA_PROT_NODATA: if (handler == NULL) handler = task_no_data_intr; ide_execute_command(drive, cmd, handler, WAIT_WORSTCASE); return ide_started; case ATA_PROT_DMA: if (ide_dma_prepare(drive, cmd)) return ide_stopped; hwif->expiry = dma_ops->dma_timer_expiry; ide_execute_command(drive, cmd, ide_dma_intr, 2 * WAIT_CMD); dma_ops->dma_start(drive); default: return ide_started; } }