18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * IDE I/O functions 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Basic PIO and command management functionality. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This code was split off from ide.c. See ide.c for history and original 78c2ecf20Sopenharmony_ci * copyrights. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify it 108c2ecf20Sopenharmony_ci * under the terms of the GNU General Public License as published by the 118c2ecf20Sopenharmony_ci * Free Software Foundation; either version 2, or (at your option) any 128c2ecf20Sopenharmony_ci * later version. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, but 158c2ecf20Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of 168c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 178c2ecf20Sopenharmony_ci * General Public License for more details. 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * For the avoidance of doubt the "preferred form" of this code is one which 208c2ecf20Sopenharmony_ci * is in an open non patent encumbered format. Where cryptographic key signing 218c2ecf20Sopenharmony_ci * forms part of the process of creating an executable the information 228c2ecf20Sopenharmony_ci * including keys needed to generate an equivalently functional executable 238c2ecf20Sopenharmony_ci * are deemed to be part of the source code. 248c2ecf20Sopenharmony_ci */ 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include <linux/module.h> 288c2ecf20Sopenharmony_ci#include <linux/types.h> 298c2ecf20Sopenharmony_ci#include <linux/string.h> 308c2ecf20Sopenharmony_ci#include <linux/kernel.h> 318c2ecf20Sopenharmony_ci#include <linux/timer.h> 328c2ecf20Sopenharmony_ci#include <linux/mm.h> 338c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 348c2ecf20Sopenharmony_ci#include <linux/major.h> 358c2ecf20Sopenharmony_ci#include <linux/errno.h> 368c2ecf20Sopenharmony_ci#include <linux/genhd.h> 378c2ecf20Sopenharmony_ci#include <linux/blkpg.h> 388c2ecf20Sopenharmony_ci#include <linux/slab.h> 398c2ecf20Sopenharmony_ci#include <linux/init.h> 408c2ecf20Sopenharmony_ci#include <linux/pci.h> 418c2ecf20Sopenharmony_ci#include <linux/delay.h> 428c2ecf20Sopenharmony_ci#include <linux/ide.h> 438c2ecf20Sopenharmony_ci#include <linux/completion.h> 448c2ecf20Sopenharmony_ci#include <linux/reboot.h> 458c2ecf20Sopenharmony_ci#include <linux/cdrom.h> 468c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 478c2ecf20Sopenharmony_ci#include <linux/device.h> 488c2ecf20Sopenharmony_ci#include <linux/kmod.h> 498c2ecf20Sopenharmony_ci#include <linux/scatterlist.h> 508c2ecf20Sopenharmony_ci#include <linux/bitops.h> 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#include <asm/byteorder.h> 538c2ecf20Sopenharmony_ci#include <asm/irq.h> 548c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 558c2ecf20Sopenharmony_ci#include <asm/io.h> 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ciint ide_end_rq(ide_drive_t *drive, struct request *rq, blk_status_t error, 588c2ecf20Sopenharmony_ci unsigned int nr_bytes) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci /* 618c2ecf20Sopenharmony_ci * decide whether to reenable DMA -- 3 is a random magic for now, 628c2ecf20Sopenharmony_ci * if we DMA timeout more than 3 times, just stay in PIO 638c2ecf20Sopenharmony_ci */ 648c2ecf20Sopenharmony_ci if ((drive->dev_flags & IDE_DFLAG_DMA_PIO_RETRY) && 658c2ecf20Sopenharmony_ci drive->retry_pio <= 3) { 668c2ecf20Sopenharmony_ci drive->dev_flags &= ~IDE_DFLAG_DMA_PIO_RETRY; 678c2ecf20Sopenharmony_ci ide_dma_on(drive); 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci if (!blk_update_request(rq, error, nr_bytes)) { 718c2ecf20Sopenharmony_ci if (rq == drive->sense_rq) { 728c2ecf20Sopenharmony_ci drive->sense_rq = NULL; 738c2ecf20Sopenharmony_ci drive->sense_rq_active = false; 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci __blk_mq_end_request(rq, error); 778c2ecf20Sopenharmony_ci return 0; 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci return 1; 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ide_end_rq); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_civoid ide_complete_cmd(ide_drive_t *drive, struct ide_cmd *cmd, u8 stat, u8 err) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci const struct ide_tp_ops *tp_ops = drive->hwif->tp_ops; 878c2ecf20Sopenharmony_ci struct ide_taskfile *tf = &cmd->tf; 888c2ecf20Sopenharmony_ci struct request *rq = cmd->rq; 898c2ecf20Sopenharmony_ci u8 tf_cmd = tf->command; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci tf->error = err; 928c2ecf20Sopenharmony_ci tf->status = stat; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci if (cmd->ftf_flags & IDE_FTFLAG_IN_DATA) { 958c2ecf20Sopenharmony_ci u8 data[2]; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci tp_ops->input_data(drive, cmd, data, 2); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci cmd->tf.data = data[0]; 1008c2ecf20Sopenharmony_ci cmd->hob.data = data[1]; 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci ide_tf_readback(drive, cmd); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if ((cmd->tf_flags & IDE_TFLAG_CUSTOM_HANDLER) && 1068c2ecf20Sopenharmony_ci tf_cmd == ATA_CMD_IDLEIMMEDIATE) { 1078c2ecf20Sopenharmony_ci if (tf->lbal != 0xc4) { 1088c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: head unload failed!\n", 1098c2ecf20Sopenharmony_ci drive->name); 1108c2ecf20Sopenharmony_ci ide_tf_dump(drive->name, cmd); 1118c2ecf20Sopenharmony_ci } else 1128c2ecf20Sopenharmony_ci drive->dev_flags |= IDE_DFLAG_PARKED; 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (rq && ata_taskfile_request(rq)) { 1168c2ecf20Sopenharmony_ci struct ide_cmd *orig_cmd = ide_req(rq)->special; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci if (cmd->tf_flags & IDE_TFLAG_DYN) 1198c2ecf20Sopenharmony_ci kfree(orig_cmd); 1208c2ecf20Sopenharmony_ci else if (cmd != orig_cmd) 1218c2ecf20Sopenharmony_ci memcpy(orig_cmd, cmd, sizeof(*cmd)); 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ciint ide_complete_rq(ide_drive_t *drive, blk_status_t error, unsigned int nr_bytes) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci ide_hwif_t *hwif = drive->hwif; 1288c2ecf20Sopenharmony_ci struct request *rq = hwif->rq; 1298c2ecf20Sopenharmony_ci int rc; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci /* 1328c2ecf20Sopenharmony_ci * if failfast is set on a request, override number of sectors 1338c2ecf20Sopenharmony_ci * and complete the whole request right now 1348c2ecf20Sopenharmony_ci */ 1358c2ecf20Sopenharmony_ci if (blk_noretry_request(rq) && error) 1368c2ecf20Sopenharmony_ci nr_bytes = blk_rq_sectors(rq) << 9; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci rc = ide_end_rq(drive, rq, error, nr_bytes); 1398c2ecf20Sopenharmony_ci if (rc == 0) 1408c2ecf20Sopenharmony_ci hwif->rq = NULL; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci return rc; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ide_complete_rq); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_civoid ide_kill_rq(ide_drive_t *drive, struct request *rq) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci u8 drv_req = ata_misc_request(rq) && rq->rq_disk; 1498c2ecf20Sopenharmony_ci u8 media = drive->media; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci drive->failed_pc = NULL; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if ((media == ide_floppy || media == ide_tape) && drv_req) { 1548c2ecf20Sopenharmony_ci scsi_req(rq)->result = 0; 1558c2ecf20Sopenharmony_ci } else { 1568c2ecf20Sopenharmony_ci if (media == ide_tape) 1578c2ecf20Sopenharmony_ci scsi_req(rq)->result = IDE_DRV_ERROR_GENERAL; 1588c2ecf20Sopenharmony_ci else if (blk_rq_is_passthrough(rq) && scsi_req(rq)->result == 0) 1598c2ecf20Sopenharmony_ci scsi_req(rq)->result = -EIO; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci ide_complete_rq(drive, BLK_STS_IOERR, blk_rq_bytes(rq)); 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic void ide_tf_set_specify_cmd(ide_drive_t *drive, struct ide_taskfile *tf) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci tf->nsect = drive->sect; 1688c2ecf20Sopenharmony_ci tf->lbal = drive->sect; 1698c2ecf20Sopenharmony_ci tf->lbam = drive->cyl; 1708c2ecf20Sopenharmony_ci tf->lbah = drive->cyl >> 8; 1718c2ecf20Sopenharmony_ci tf->device = (drive->head - 1) | drive->select; 1728c2ecf20Sopenharmony_ci tf->command = ATA_CMD_INIT_DEV_PARAMS; 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic void ide_tf_set_restore_cmd(ide_drive_t *drive, struct ide_taskfile *tf) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci tf->nsect = drive->sect; 1788c2ecf20Sopenharmony_ci tf->command = ATA_CMD_RESTORE; 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic void ide_tf_set_setmult_cmd(ide_drive_t *drive, struct ide_taskfile *tf) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci tf->nsect = drive->mult_req; 1848c2ecf20Sopenharmony_ci tf->command = ATA_CMD_SET_MULTI; 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci/** 1888c2ecf20Sopenharmony_ci * do_special - issue some special commands 1898c2ecf20Sopenharmony_ci * @drive: drive the command is for 1908c2ecf20Sopenharmony_ci * 1918c2ecf20Sopenharmony_ci * do_special() is used to issue ATA_CMD_INIT_DEV_PARAMS, 1928c2ecf20Sopenharmony_ci * ATA_CMD_RESTORE and ATA_CMD_SET_MULTI commands to a drive. 1938c2ecf20Sopenharmony_ci */ 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic ide_startstop_t do_special(ide_drive_t *drive) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci struct ide_cmd cmd; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci#ifdef DEBUG 2008c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: %s: 0x%02x\n", drive->name, __func__, 2018c2ecf20Sopenharmony_ci drive->special_flags); 2028c2ecf20Sopenharmony_ci#endif 2038c2ecf20Sopenharmony_ci if (drive->media != ide_disk) { 2048c2ecf20Sopenharmony_ci drive->special_flags = 0; 2058c2ecf20Sopenharmony_ci drive->mult_req = 0; 2068c2ecf20Sopenharmony_ci return ide_stopped; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 2108c2ecf20Sopenharmony_ci cmd.protocol = ATA_PROT_NODATA; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (drive->special_flags & IDE_SFLAG_SET_GEOMETRY) { 2138c2ecf20Sopenharmony_ci drive->special_flags &= ~IDE_SFLAG_SET_GEOMETRY; 2148c2ecf20Sopenharmony_ci ide_tf_set_specify_cmd(drive, &cmd.tf); 2158c2ecf20Sopenharmony_ci } else if (drive->special_flags & IDE_SFLAG_RECALIBRATE) { 2168c2ecf20Sopenharmony_ci drive->special_flags &= ~IDE_SFLAG_RECALIBRATE; 2178c2ecf20Sopenharmony_ci ide_tf_set_restore_cmd(drive, &cmd.tf); 2188c2ecf20Sopenharmony_ci } else if (drive->special_flags & IDE_SFLAG_SET_MULTMODE) { 2198c2ecf20Sopenharmony_ci drive->special_flags &= ~IDE_SFLAG_SET_MULTMODE; 2208c2ecf20Sopenharmony_ci ide_tf_set_setmult_cmd(drive, &cmd.tf); 2218c2ecf20Sopenharmony_ci } else 2228c2ecf20Sopenharmony_ci BUG(); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci cmd.valid.out.tf = IDE_VALID_OUT_TF | IDE_VALID_DEVICE; 2258c2ecf20Sopenharmony_ci cmd.valid.in.tf = IDE_VALID_IN_TF | IDE_VALID_DEVICE; 2268c2ecf20Sopenharmony_ci cmd.tf_flags = IDE_TFLAG_CUSTOM_HANDLER; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci do_rw_taskfile(drive, &cmd); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci return ide_started; 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_civoid ide_map_sg(ide_drive_t *drive, struct ide_cmd *cmd) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci ide_hwif_t *hwif = drive->hwif; 2368c2ecf20Sopenharmony_ci struct scatterlist *sg = hwif->sg_table, *last_sg = NULL; 2378c2ecf20Sopenharmony_ci struct request *rq = cmd->rq; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci cmd->sg_nents = __blk_rq_map_sg(drive->queue, rq, sg, &last_sg); 2408c2ecf20Sopenharmony_ci if (blk_rq_bytes(rq) && (blk_rq_bytes(rq) & rq->q->dma_pad_mask)) 2418c2ecf20Sopenharmony_ci last_sg->length += 2428c2ecf20Sopenharmony_ci (rq->q->dma_pad_mask & ~blk_rq_bytes(rq)) + 1; 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ide_map_sg); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_civoid ide_init_sg_cmd(struct ide_cmd *cmd, unsigned int nr_bytes) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci cmd->nbytes = cmd->nleft = nr_bytes; 2498c2ecf20Sopenharmony_ci cmd->cursg_ofs = 0; 2508c2ecf20Sopenharmony_ci cmd->cursg = NULL; 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ide_init_sg_cmd); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci/** 2558c2ecf20Sopenharmony_ci * execute_drive_command - issue special drive command 2568c2ecf20Sopenharmony_ci * @drive: the drive to issue the command on 2578c2ecf20Sopenharmony_ci * @rq: the request structure holding the command 2588c2ecf20Sopenharmony_ci * 2598c2ecf20Sopenharmony_ci * execute_drive_cmd() issues a special drive command, usually 2608c2ecf20Sopenharmony_ci * initiated by ioctl() from the external hdparm program. The 2618c2ecf20Sopenharmony_ci * command can be a drive command, drive task or taskfile 2628c2ecf20Sopenharmony_ci * operation. Weirdly you can call it with NULL to wait for 2638c2ecf20Sopenharmony_ci * all commands to finish. Don't do this as that is due to change 2648c2ecf20Sopenharmony_ci */ 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic ide_startstop_t execute_drive_cmd (ide_drive_t *drive, 2678c2ecf20Sopenharmony_ci struct request *rq) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci struct ide_cmd *cmd = ide_req(rq)->special; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci if (cmd) { 2728c2ecf20Sopenharmony_ci if (cmd->protocol == ATA_PROT_PIO) { 2738c2ecf20Sopenharmony_ci ide_init_sg_cmd(cmd, blk_rq_sectors(rq) << 9); 2748c2ecf20Sopenharmony_ci ide_map_sg(drive, cmd); 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci return do_rw_taskfile(drive, cmd); 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci /* 2818c2ecf20Sopenharmony_ci * NULL is actually a valid way of waiting for 2828c2ecf20Sopenharmony_ci * all current requests to be flushed from the queue. 2838c2ecf20Sopenharmony_ci */ 2848c2ecf20Sopenharmony_ci#ifdef DEBUG 2858c2ecf20Sopenharmony_ci printk("%s: DRIVE_CMD (null)\n", drive->name); 2868c2ecf20Sopenharmony_ci#endif 2878c2ecf20Sopenharmony_ci scsi_req(rq)->result = 0; 2888c2ecf20Sopenharmony_ci ide_complete_rq(drive, BLK_STS_OK, blk_rq_bytes(rq)); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci return ide_stopped; 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic ide_startstop_t ide_special_rq(ide_drive_t *drive, struct request *rq) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci u8 cmd = scsi_req(rq)->cmd[0]; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci switch (cmd) { 2988c2ecf20Sopenharmony_ci case REQ_PARK_HEADS: 2998c2ecf20Sopenharmony_ci case REQ_UNPARK_HEADS: 3008c2ecf20Sopenharmony_ci return ide_do_park_unpark(drive, rq); 3018c2ecf20Sopenharmony_ci case REQ_DEVSET_EXEC: 3028c2ecf20Sopenharmony_ci return ide_do_devset(drive, rq); 3038c2ecf20Sopenharmony_ci case REQ_DRIVE_RESET: 3048c2ecf20Sopenharmony_ci return ide_do_reset(drive); 3058c2ecf20Sopenharmony_ci default: 3068c2ecf20Sopenharmony_ci BUG(); 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci/** 3118c2ecf20Sopenharmony_ci * start_request - start of I/O and command issuing for IDE 3128c2ecf20Sopenharmony_ci * 3138c2ecf20Sopenharmony_ci * start_request() initiates handling of a new I/O request. It 3148c2ecf20Sopenharmony_ci * accepts commands and I/O (read/write) requests. 3158c2ecf20Sopenharmony_ci * 3168c2ecf20Sopenharmony_ci * FIXME: this function needs a rename 3178c2ecf20Sopenharmony_ci */ 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cistatic ide_startstop_t start_request (ide_drive_t *drive, struct request *rq) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci ide_startstop_t startstop; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci#ifdef DEBUG 3248c2ecf20Sopenharmony_ci printk("%s: start_request: current=0x%08lx\n", 3258c2ecf20Sopenharmony_ci drive->hwif->name, (unsigned long) rq); 3268c2ecf20Sopenharmony_ci#endif 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci /* bail early if we've exceeded max_failures */ 3298c2ecf20Sopenharmony_ci if (drive->max_failures && (drive->failures > drive->max_failures)) { 3308c2ecf20Sopenharmony_ci rq->rq_flags |= RQF_FAILED; 3318c2ecf20Sopenharmony_ci goto kill_rq; 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci if (drive->prep_rq && !drive->prep_rq(drive, rq)) 3358c2ecf20Sopenharmony_ci return ide_stopped; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci if (ata_pm_request(rq)) 3388c2ecf20Sopenharmony_ci ide_check_pm_state(drive, rq); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci drive->hwif->tp_ops->dev_select(drive); 3418c2ecf20Sopenharmony_ci if (ide_wait_stat(&startstop, drive, drive->ready_stat, 3428c2ecf20Sopenharmony_ci ATA_BUSY | ATA_DRQ, WAIT_READY)) { 3438c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: drive not ready for command\n", drive->name); 3448c2ecf20Sopenharmony_ci return startstop; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci if (drive->special_flags == 0) { 3488c2ecf20Sopenharmony_ci struct ide_driver *drv; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci /* 3518c2ecf20Sopenharmony_ci * We reset the drive so we need to issue a SETFEATURES. 3528c2ecf20Sopenharmony_ci * Do it _after_ do_special() restored device parameters. 3538c2ecf20Sopenharmony_ci */ 3548c2ecf20Sopenharmony_ci if (drive->current_speed == 0xff) 3558c2ecf20Sopenharmony_ci ide_config_drive_speed(drive, drive->desired_speed); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci if (ata_taskfile_request(rq)) 3588c2ecf20Sopenharmony_ci return execute_drive_cmd(drive, rq); 3598c2ecf20Sopenharmony_ci else if (ata_pm_request(rq)) { 3608c2ecf20Sopenharmony_ci struct ide_pm_state *pm = ide_req(rq)->special; 3618c2ecf20Sopenharmony_ci#ifdef DEBUG_PM 3628c2ecf20Sopenharmony_ci printk("%s: start_power_step(step: %d)\n", 3638c2ecf20Sopenharmony_ci drive->name, pm->pm_step); 3648c2ecf20Sopenharmony_ci#endif 3658c2ecf20Sopenharmony_ci startstop = ide_start_power_step(drive, rq); 3668c2ecf20Sopenharmony_ci if (startstop == ide_stopped && 3678c2ecf20Sopenharmony_ci pm->pm_step == IDE_PM_COMPLETED) 3688c2ecf20Sopenharmony_ci ide_complete_pm_rq(drive, rq); 3698c2ecf20Sopenharmony_ci return startstop; 3708c2ecf20Sopenharmony_ci } else if (!rq->rq_disk && ata_misc_request(rq)) 3718c2ecf20Sopenharmony_ci /* 3728c2ecf20Sopenharmony_ci * TODO: Once all ULDs have been modified to 3738c2ecf20Sopenharmony_ci * check for specific op codes rather than 3748c2ecf20Sopenharmony_ci * blindly accepting any special request, the 3758c2ecf20Sopenharmony_ci * check for ->rq_disk above may be replaced 3768c2ecf20Sopenharmony_ci * by a more suitable mechanism or even 3778c2ecf20Sopenharmony_ci * dropped entirely. 3788c2ecf20Sopenharmony_ci */ 3798c2ecf20Sopenharmony_ci return ide_special_rq(drive, rq); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci drv = *(struct ide_driver **)rq->rq_disk->private_data; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci return drv->do_request(drive, rq, blk_rq_pos(rq)); 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci return do_special(drive); 3868c2ecf20Sopenharmony_cikill_rq: 3878c2ecf20Sopenharmony_ci ide_kill_rq(drive, rq); 3888c2ecf20Sopenharmony_ci return ide_stopped; 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci/** 3928c2ecf20Sopenharmony_ci * ide_stall_queue - pause an IDE device 3938c2ecf20Sopenharmony_ci * @drive: drive to stall 3948c2ecf20Sopenharmony_ci * @timeout: time to stall for (jiffies) 3958c2ecf20Sopenharmony_ci * 3968c2ecf20Sopenharmony_ci * ide_stall_queue() can be used by a drive to give excess bandwidth back 3978c2ecf20Sopenharmony_ci * to the port by sleeping for timeout jiffies. 3988c2ecf20Sopenharmony_ci */ 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_civoid ide_stall_queue (ide_drive_t *drive, unsigned long timeout) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci if (timeout > WAIT_WORSTCASE) 4038c2ecf20Sopenharmony_ci timeout = WAIT_WORSTCASE; 4048c2ecf20Sopenharmony_ci drive->sleep = timeout + jiffies; 4058c2ecf20Sopenharmony_ci drive->dev_flags |= IDE_DFLAG_SLEEPING; 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ide_stall_queue); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_cistatic inline int ide_lock_port(ide_hwif_t *hwif) 4108c2ecf20Sopenharmony_ci{ 4118c2ecf20Sopenharmony_ci if (hwif->busy) 4128c2ecf20Sopenharmony_ci return 1; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci hwif->busy = 1; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci return 0; 4178c2ecf20Sopenharmony_ci} 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_cistatic inline void ide_unlock_port(ide_hwif_t *hwif) 4208c2ecf20Sopenharmony_ci{ 4218c2ecf20Sopenharmony_ci hwif->busy = 0; 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_cistatic inline int ide_lock_host(struct ide_host *host, ide_hwif_t *hwif) 4258c2ecf20Sopenharmony_ci{ 4268c2ecf20Sopenharmony_ci int rc = 0; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci if (host->host_flags & IDE_HFLAG_SERIALIZE) { 4298c2ecf20Sopenharmony_ci rc = test_and_set_bit_lock(IDE_HOST_BUSY, &host->host_busy); 4308c2ecf20Sopenharmony_ci if (rc == 0) { 4318c2ecf20Sopenharmony_ci if (host->get_lock) 4328c2ecf20Sopenharmony_ci host->get_lock(ide_intr, hwif); 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci return rc; 4368c2ecf20Sopenharmony_ci} 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_cistatic inline void ide_unlock_host(struct ide_host *host) 4398c2ecf20Sopenharmony_ci{ 4408c2ecf20Sopenharmony_ci if (host->host_flags & IDE_HFLAG_SERIALIZE) { 4418c2ecf20Sopenharmony_ci if (host->release_lock) 4428c2ecf20Sopenharmony_ci host->release_lock(); 4438c2ecf20Sopenharmony_ci clear_bit_unlock(IDE_HOST_BUSY, &host->host_busy); 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci} 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_civoid ide_requeue_and_plug(ide_drive_t *drive, struct request *rq) 4488c2ecf20Sopenharmony_ci{ 4498c2ecf20Sopenharmony_ci struct request_queue *q = drive->queue; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci /* Use 3ms as that was the old plug delay */ 4528c2ecf20Sopenharmony_ci if (rq) { 4538c2ecf20Sopenharmony_ci blk_mq_requeue_request(rq, false); 4548c2ecf20Sopenharmony_ci blk_mq_delay_kick_requeue_list(q, 3); 4558c2ecf20Sopenharmony_ci } else 4568c2ecf20Sopenharmony_ci blk_mq_delay_run_hw_queue(q->queue_hw_ctx[0], 3); 4578c2ecf20Sopenharmony_ci} 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ciblk_status_t ide_issue_rq(ide_drive_t *drive, struct request *rq, 4608c2ecf20Sopenharmony_ci bool local_requeue) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci ide_hwif_t *hwif = drive->hwif; 4638c2ecf20Sopenharmony_ci struct ide_host *host = hwif->host; 4648c2ecf20Sopenharmony_ci ide_startstop_t startstop; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci if (!blk_rq_is_passthrough(rq) && !(rq->rq_flags & RQF_DONTPREP)) { 4678c2ecf20Sopenharmony_ci rq->rq_flags |= RQF_DONTPREP; 4688c2ecf20Sopenharmony_ci ide_req(rq)->special = NULL; 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci /* HLD do_request() callback might sleep, make sure it's okay */ 4728c2ecf20Sopenharmony_ci might_sleep(); 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci if (ide_lock_host(host, hwif)) 4758c2ecf20Sopenharmony_ci return BLK_STS_DEV_RESOURCE; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci spin_lock_irq(&hwif->lock); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci if (!ide_lock_port(hwif)) { 4808c2ecf20Sopenharmony_ci ide_hwif_t *prev_port; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci WARN_ON_ONCE(hwif->rq); 4838c2ecf20Sopenharmony_cirepeat: 4848c2ecf20Sopenharmony_ci prev_port = hwif->host->cur_port; 4858c2ecf20Sopenharmony_ci if (drive->dev_flags & IDE_DFLAG_SLEEPING && 4868c2ecf20Sopenharmony_ci time_after(drive->sleep, jiffies)) { 4878c2ecf20Sopenharmony_ci ide_unlock_port(hwif); 4888c2ecf20Sopenharmony_ci goto plug_device; 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci if ((hwif->host->host_flags & IDE_HFLAG_SERIALIZE) && 4928c2ecf20Sopenharmony_ci hwif != prev_port) { 4938c2ecf20Sopenharmony_ci ide_drive_t *cur_dev = 4948c2ecf20Sopenharmony_ci prev_port ? prev_port->cur_dev : NULL; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci /* 4978c2ecf20Sopenharmony_ci * set nIEN for previous port, drives in the 4988c2ecf20Sopenharmony_ci * quirk list may not like intr setups/cleanups 4998c2ecf20Sopenharmony_ci */ 5008c2ecf20Sopenharmony_ci if (cur_dev && 5018c2ecf20Sopenharmony_ci (cur_dev->dev_flags & IDE_DFLAG_NIEN_QUIRK) == 0) 5028c2ecf20Sopenharmony_ci prev_port->tp_ops->write_devctl(prev_port, 5038c2ecf20Sopenharmony_ci ATA_NIEN | 5048c2ecf20Sopenharmony_ci ATA_DEVCTL_OBS); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci hwif->host->cur_port = hwif; 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci hwif->cur_dev = drive; 5098c2ecf20Sopenharmony_ci drive->dev_flags &= ~(IDE_DFLAG_SLEEPING | IDE_DFLAG_PARKED); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci /* 5128c2ecf20Sopenharmony_ci * Sanity: don't accept a request that isn't a PM request 5138c2ecf20Sopenharmony_ci * if we are currently power managed. This is very important as 5148c2ecf20Sopenharmony_ci * blk_stop_queue() doesn't prevent the blk_fetch_request() 5158c2ecf20Sopenharmony_ci * above to return us whatever is in the queue. Since we call 5168c2ecf20Sopenharmony_ci * ide_do_request() ourselves, we end up taking requests while 5178c2ecf20Sopenharmony_ci * the queue is blocked... 5188c2ecf20Sopenharmony_ci */ 5198c2ecf20Sopenharmony_ci if ((drive->dev_flags & IDE_DFLAG_BLOCKED) && 5208c2ecf20Sopenharmony_ci ata_pm_request(rq) == 0 && 5218c2ecf20Sopenharmony_ci (rq->rq_flags & RQF_PM) == 0) { 5228c2ecf20Sopenharmony_ci /* there should be no pending command at this point */ 5238c2ecf20Sopenharmony_ci ide_unlock_port(hwif); 5248c2ecf20Sopenharmony_ci goto plug_device; 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci scsi_req(rq)->resid_len = blk_rq_bytes(rq); 5288c2ecf20Sopenharmony_ci hwif->rq = rq; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci spin_unlock_irq(&hwif->lock); 5318c2ecf20Sopenharmony_ci startstop = start_request(drive, rq); 5328c2ecf20Sopenharmony_ci spin_lock_irq(&hwif->lock); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci if (startstop == ide_stopped) { 5358c2ecf20Sopenharmony_ci rq = hwif->rq; 5368c2ecf20Sopenharmony_ci hwif->rq = NULL; 5378c2ecf20Sopenharmony_ci if (rq) 5388c2ecf20Sopenharmony_ci goto repeat; 5398c2ecf20Sopenharmony_ci ide_unlock_port(hwif); 5408c2ecf20Sopenharmony_ci goto out; 5418c2ecf20Sopenharmony_ci } 5428c2ecf20Sopenharmony_ci } else { 5438c2ecf20Sopenharmony_ciplug_device: 5448c2ecf20Sopenharmony_ci if (local_requeue) 5458c2ecf20Sopenharmony_ci list_add(&rq->queuelist, &drive->rq_list); 5468c2ecf20Sopenharmony_ci spin_unlock_irq(&hwif->lock); 5478c2ecf20Sopenharmony_ci ide_unlock_host(host); 5488c2ecf20Sopenharmony_ci if (!local_requeue) 5498c2ecf20Sopenharmony_ci ide_requeue_and_plug(drive, rq); 5508c2ecf20Sopenharmony_ci return BLK_STS_OK; 5518c2ecf20Sopenharmony_ci } 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ciout: 5548c2ecf20Sopenharmony_ci spin_unlock_irq(&hwif->lock); 5558c2ecf20Sopenharmony_ci if (rq == NULL) 5568c2ecf20Sopenharmony_ci ide_unlock_host(host); 5578c2ecf20Sopenharmony_ci return BLK_STS_OK; 5588c2ecf20Sopenharmony_ci} 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci/* 5618c2ecf20Sopenharmony_ci * Issue a new request to a device. 5628c2ecf20Sopenharmony_ci */ 5638c2ecf20Sopenharmony_ciblk_status_t ide_queue_rq(struct blk_mq_hw_ctx *hctx, 5648c2ecf20Sopenharmony_ci const struct blk_mq_queue_data *bd) 5658c2ecf20Sopenharmony_ci{ 5668c2ecf20Sopenharmony_ci ide_drive_t *drive = hctx->queue->queuedata; 5678c2ecf20Sopenharmony_ci ide_hwif_t *hwif = drive->hwif; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci spin_lock_irq(&hwif->lock); 5708c2ecf20Sopenharmony_ci if (drive->sense_rq_active) { 5718c2ecf20Sopenharmony_ci spin_unlock_irq(&hwif->lock); 5728c2ecf20Sopenharmony_ci return BLK_STS_DEV_RESOURCE; 5738c2ecf20Sopenharmony_ci } 5748c2ecf20Sopenharmony_ci spin_unlock_irq(&hwif->lock); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci blk_mq_start_request(bd->rq); 5778c2ecf20Sopenharmony_ci return ide_issue_rq(drive, bd->rq, false); 5788c2ecf20Sopenharmony_ci} 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_cistatic int drive_is_ready(ide_drive_t *drive) 5818c2ecf20Sopenharmony_ci{ 5828c2ecf20Sopenharmony_ci ide_hwif_t *hwif = drive->hwif; 5838c2ecf20Sopenharmony_ci u8 stat = 0; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci if (drive->waiting_for_dma) 5868c2ecf20Sopenharmony_ci return hwif->dma_ops->dma_test_irq(drive); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci if (hwif->io_ports.ctl_addr && 5898c2ecf20Sopenharmony_ci (hwif->host_flags & IDE_HFLAG_BROKEN_ALTSTATUS) == 0) 5908c2ecf20Sopenharmony_ci stat = hwif->tp_ops->read_altstatus(hwif); 5918c2ecf20Sopenharmony_ci else 5928c2ecf20Sopenharmony_ci /* Note: this may clear a pending IRQ!! */ 5938c2ecf20Sopenharmony_ci stat = hwif->tp_ops->read_status(hwif); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci if (stat & ATA_BUSY) 5968c2ecf20Sopenharmony_ci /* drive busy: definitely not interrupting */ 5978c2ecf20Sopenharmony_ci return 0; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci /* drive ready: *might* be interrupting */ 6008c2ecf20Sopenharmony_ci return 1; 6018c2ecf20Sopenharmony_ci} 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci/** 6048c2ecf20Sopenharmony_ci * ide_timer_expiry - handle lack of an IDE interrupt 6058c2ecf20Sopenharmony_ci * @data: timer callback magic (hwif) 6068c2ecf20Sopenharmony_ci * 6078c2ecf20Sopenharmony_ci * An IDE command has timed out before the expected drive return 6088c2ecf20Sopenharmony_ci * occurred. At this point we attempt to clean up the current 6098c2ecf20Sopenharmony_ci * mess. If the current handler includes an expiry handler then 6108c2ecf20Sopenharmony_ci * we invoke the expiry handler, and providing it is happy the 6118c2ecf20Sopenharmony_ci * work is done. If that fails we apply generic recovery rules 6128c2ecf20Sopenharmony_ci * invoking the handler and checking the drive DMA status. We 6138c2ecf20Sopenharmony_ci * have an excessively incestuous relationship with the DMA 6148c2ecf20Sopenharmony_ci * logic that wants cleaning up. 6158c2ecf20Sopenharmony_ci */ 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_civoid ide_timer_expiry (struct timer_list *t) 6188c2ecf20Sopenharmony_ci{ 6198c2ecf20Sopenharmony_ci ide_hwif_t *hwif = from_timer(hwif, t, timer); 6208c2ecf20Sopenharmony_ci ide_drive_t *drive; 6218c2ecf20Sopenharmony_ci ide_handler_t *handler; 6228c2ecf20Sopenharmony_ci unsigned long flags; 6238c2ecf20Sopenharmony_ci int wait = -1; 6248c2ecf20Sopenharmony_ci int plug_device = 0; 6258c2ecf20Sopenharmony_ci struct request *rq_in_flight; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci spin_lock_irqsave(&hwif->lock, flags); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci handler = hwif->handler; 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci if (handler == NULL || hwif->req_gen != hwif->req_gen_timer) { 6328c2ecf20Sopenharmony_ci /* 6338c2ecf20Sopenharmony_ci * Either a marginal timeout occurred 6348c2ecf20Sopenharmony_ci * (got the interrupt just as timer expired), 6358c2ecf20Sopenharmony_ci * or we were "sleeping" to give other devices a chance. 6368c2ecf20Sopenharmony_ci * Either way, we don't really want to complain about anything. 6378c2ecf20Sopenharmony_ci */ 6388c2ecf20Sopenharmony_ci } else { 6398c2ecf20Sopenharmony_ci ide_expiry_t *expiry = hwif->expiry; 6408c2ecf20Sopenharmony_ci ide_startstop_t startstop = ide_stopped; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci drive = hwif->cur_dev; 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci if (expiry) { 6458c2ecf20Sopenharmony_ci wait = expiry(drive); 6468c2ecf20Sopenharmony_ci if (wait > 0) { /* continue */ 6478c2ecf20Sopenharmony_ci /* reset timer */ 6488c2ecf20Sopenharmony_ci hwif->timer.expires = jiffies + wait; 6498c2ecf20Sopenharmony_ci hwif->req_gen_timer = hwif->req_gen; 6508c2ecf20Sopenharmony_ci add_timer(&hwif->timer); 6518c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hwif->lock, flags); 6528c2ecf20Sopenharmony_ci return; 6538c2ecf20Sopenharmony_ci } 6548c2ecf20Sopenharmony_ci } 6558c2ecf20Sopenharmony_ci hwif->handler = NULL; 6568c2ecf20Sopenharmony_ci hwif->expiry = NULL; 6578c2ecf20Sopenharmony_ci /* 6588c2ecf20Sopenharmony_ci * We need to simulate a real interrupt when invoking 6598c2ecf20Sopenharmony_ci * the handler() function, which means we need to 6608c2ecf20Sopenharmony_ci * globally mask the specific IRQ: 6618c2ecf20Sopenharmony_ci */ 6628c2ecf20Sopenharmony_ci spin_unlock(&hwif->lock); 6638c2ecf20Sopenharmony_ci /* disable_irq_nosync ?? */ 6648c2ecf20Sopenharmony_ci disable_irq(hwif->irq); 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci if (hwif->polling) { 6678c2ecf20Sopenharmony_ci startstop = handler(drive); 6688c2ecf20Sopenharmony_ci } else if (drive_is_ready(drive)) { 6698c2ecf20Sopenharmony_ci if (drive->waiting_for_dma) 6708c2ecf20Sopenharmony_ci hwif->dma_ops->dma_lost_irq(drive); 6718c2ecf20Sopenharmony_ci if (hwif->port_ops && hwif->port_ops->clear_irq) 6728c2ecf20Sopenharmony_ci hwif->port_ops->clear_irq(drive); 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: lost interrupt\n", 6758c2ecf20Sopenharmony_ci drive->name); 6768c2ecf20Sopenharmony_ci startstop = handler(drive); 6778c2ecf20Sopenharmony_ci } else { 6788c2ecf20Sopenharmony_ci if (drive->waiting_for_dma) 6798c2ecf20Sopenharmony_ci startstop = ide_dma_timeout_retry(drive, wait); 6808c2ecf20Sopenharmony_ci else 6818c2ecf20Sopenharmony_ci startstop = ide_error(drive, "irq timeout", 6828c2ecf20Sopenharmony_ci hwif->tp_ops->read_status(hwif)); 6838c2ecf20Sopenharmony_ci } 6848c2ecf20Sopenharmony_ci /* Disable interrupts again, `handler' might have enabled it */ 6858c2ecf20Sopenharmony_ci spin_lock_irq(&hwif->lock); 6868c2ecf20Sopenharmony_ci enable_irq(hwif->irq); 6878c2ecf20Sopenharmony_ci if (startstop == ide_stopped && hwif->polling == 0) { 6888c2ecf20Sopenharmony_ci rq_in_flight = hwif->rq; 6898c2ecf20Sopenharmony_ci hwif->rq = NULL; 6908c2ecf20Sopenharmony_ci ide_unlock_port(hwif); 6918c2ecf20Sopenharmony_ci plug_device = 1; 6928c2ecf20Sopenharmony_ci } 6938c2ecf20Sopenharmony_ci } 6948c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hwif->lock, flags); 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci if (plug_device) { 6978c2ecf20Sopenharmony_ci ide_unlock_host(hwif->host); 6988c2ecf20Sopenharmony_ci ide_requeue_and_plug(drive, rq_in_flight); 6998c2ecf20Sopenharmony_ci } 7008c2ecf20Sopenharmony_ci} 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci/** 7038c2ecf20Sopenharmony_ci * unexpected_intr - handle an unexpected IDE interrupt 7048c2ecf20Sopenharmony_ci * @irq: interrupt line 7058c2ecf20Sopenharmony_ci * @hwif: port being processed 7068c2ecf20Sopenharmony_ci * 7078c2ecf20Sopenharmony_ci * There's nothing really useful we can do with an unexpected interrupt, 7088c2ecf20Sopenharmony_ci * other than reading the status register (to clear it), and logging it. 7098c2ecf20Sopenharmony_ci * There should be no way that an irq can happen before we're ready for it, 7108c2ecf20Sopenharmony_ci * so we needn't worry much about losing an "important" interrupt here. 7118c2ecf20Sopenharmony_ci * 7128c2ecf20Sopenharmony_ci * On laptops (and "green" PCs), an unexpected interrupt occurs whenever 7138c2ecf20Sopenharmony_ci * the drive enters "idle", "standby", or "sleep" mode, so if the status 7148c2ecf20Sopenharmony_ci * looks "good", we just ignore the interrupt completely. 7158c2ecf20Sopenharmony_ci * 7168c2ecf20Sopenharmony_ci * This routine assumes __cli() is in effect when called. 7178c2ecf20Sopenharmony_ci * 7188c2ecf20Sopenharmony_ci * If an unexpected interrupt happens on irq15 while we are handling irq14 7198c2ecf20Sopenharmony_ci * and if the two interfaces are "serialized" (CMD640), then it looks like 7208c2ecf20Sopenharmony_ci * we could screw up by interfering with a new request being set up for 7218c2ecf20Sopenharmony_ci * irq15. 7228c2ecf20Sopenharmony_ci * 7238c2ecf20Sopenharmony_ci * In reality, this is a non-issue. The new command is not sent unless 7248c2ecf20Sopenharmony_ci * the drive is ready to accept one, in which case we know the drive is 7258c2ecf20Sopenharmony_ci * not trying to interrupt us. And ide_set_handler() is always invoked 7268c2ecf20Sopenharmony_ci * before completing the issuance of any new drive command, so we will not 7278c2ecf20Sopenharmony_ci * be accidentally invoked as a result of any valid command completion 7288c2ecf20Sopenharmony_ci * interrupt. 7298c2ecf20Sopenharmony_ci */ 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_cistatic void unexpected_intr(int irq, ide_hwif_t *hwif) 7328c2ecf20Sopenharmony_ci{ 7338c2ecf20Sopenharmony_ci u8 stat = hwif->tp_ops->read_status(hwif); 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci if (!OK_STAT(stat, ATA_DRDY, BAD_STAT)) { 7368c2ecf20Sopenharmony_ci /* Try to not flood the console with msgs */ 7378c2ecf20Sopenharmony_ci static unsigned long last_msgtime, count; 7388c2ecf20Sopenharmony_ci ++count; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci if (time_after(jiffies, last_msgtime + HZ)) { 7418c2ecf20Sopenharmony_ci last_msgtime = jiffies; 7428c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: unexpected interrupt, " 7438c2ecf20Sopenharmony_ci "status=0x%02x, count=%ld\n", 7448c2ecf20Sopenharmony_ci hwif->name, stat, count); 7458c2ecf20Sopenharmony_ci } 7468c2ecf20Sopenharmony_ci } 7478c2ecf20Sopenharmony_ci} 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci/** 7508c2ecf20Sopenharmony_ci * ide_intr - default IDE interrupt handler 7518c2ecf20Sopenharmony_ci * @irq: interrupt number 7528c2ecf20Sopenharmony_ci * @dev_id: hwif 7538c2ecf20Sopenharmony_ci * @regs: unused weirdness from the kernel irq layer 7548c2ecf20Sopenharmony_ci * 7558c2ecf20Sopenharmony_ci * This is the default IRQ handler for the IDE layer. You should 7568c2ecf20Sopenharmony_ci * not need to override it. If you do be aware it is subtle in 7578c2ecf20Sopenharmony_ci * places 7588c2ecf20Sopenharmony_ci * 7598c2ecf20Sopenharmony_ci * hwif is the interface in the group currently performing 7608c2ecf20Sopenharmony_ci * a command. hwif->cur_dev is the drive and hwif->handler is 7618c2ecf20Sopenharmony_ci * the IRQ handler to call. As we issue a command the handlers 7628c2ecf20Sopenharmony_ci * step through multiple states, reassigning the handler to the 7638c2ecf20Sopenharmony_ci * next step in the process. Unlike a smart SCSI controller IDE 7648c2ecf20Sopenharmony_ci * expects the main processor to sequence the various transfer 7658c2ecf20Sopenharmony_ci * stages. We also manage a poll timer to catch up with most 7668c2ecf20Sopenharmony_ci * timeout situations. There are still a few where the handlers 7678c2ecf20Sopenharmony_ci * don't ever decide to give up. 7688c2ecf20Sopenharmony_ci * 7698c2ecf20Sopenharmony_ci * The handler eventually returns ide_stopped to indicate the 7708c2ecf20Sopenharmony_ci * request completed. At this point we issue the next request 7718c2ecf20Sopenharmony_ci * on the port and the process begins again. 7728c2ecf20Sopenharmony_ci */ 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ciirqreturn_t ide_intr (int irq, void *dev_id) 7758c2ecf20Sopenharmony_ci{ 7768c2ecf20Sopenharmony_ci ide_hwif_t *hwif = (ide_hwif_t *)dev_id; 7778c2ecf20Sopenharmony_ci struct ide_host *host = hwif->host; 7788c2ecf20Sopenharmony_ci ide_drive_t *drive; 7798c2ecf20Sopenharmony_ci ide_handler_t *handler; 7808c2ecf20Sopenharmony_ci unsigned long flags; 7818c2ecf20Sopenharmony_ci ide_startstop_t startstop; 7828c2ecf20Sopenharmony_ci irqreturn_t irq_ret = IRQ_NONE; 7838c2ecf20Sopenharmony_ci int plug_device = 0; 7848c2ecf20Sopenharmony_ci struct request *rq_in_flight; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci if (host->host_flags & IDE_HFLAG_SERIALIZE) { 7878c2ecf20Sopenharmony_ci if (hwif != host->cur_port) 7888c2ecf20Sopenharmony_ci goto out_early; 7898c2ecf20Sopenharmony_ci } 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci spin_lock_irqsave(&hwif->lock, flags); 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci if (hwif->port_ops && hwif->port_ops->test_irq && 7948c2ecf20Sopenharmony_ci hwif->port_ops->test_irq(hwif) == 0) 7958c2ecf20Sopenharmony_ci goto out; 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci handler = hwif->handler; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci if (handler == NULL || hwif->polling) { 8008c2ecf20Sopenharmony_ci /* 8018c2ecf20Sopenharmony_ci * Not expecting an interrupt from this drive. 8028c2ecf20Sopenharmony_ci * That means this could be: 8038c2ecf20Sopenharmony_ci * (1) an interrupt from another PCI device 8048c2ecf20Sopenharmony_ci * sharing the same PCI INT# as us. 8058c2ecf20Sopenharmony_ci * or (2) a drive just entered sleep or standby mode, 8068c2ecf20Sopenharmony_ci * and is interrupting to let us know. 8078c2ecf20Sopenharmony_ci * or (3) a spurious interrupt of unknown origin. 8088c2ecf20Sopenharmony_ci * 8098c2ecf20Sopenharmony_ci * For PCI, we cannot tell the difference, 8108c2ecf20Sopenharmony_ci * so in that case we just ignore it and hope it goes away. 8118c2ecf20Sopenharmony_ci */ 8128c2ecf20Sopenharmony_ci if ((host->irq_flags & IRQF_SHARED) == 0) { 8138c2ecf20Sopenharmony_ci /* 8148c2ecf20Sopenharmony_ci * Probably not a shared PCI interrupt, 8158c2ecf20Sopenharmony_ci * so we can safely try to do something about it: 8168c2ecf20Sopenharmony_ci */ 8178c2ecf20Sopenharmony_ci unexpected_intr(irq, hwif); 8188c2ecf20Sopenharmony_ci } else { 8198c2ecf20Sopenharmony_ci /* 8208c2ecf20Sopenharmony_ci * Whack the status register, just in case 8218c2ecf20Sopenharmony_ci * we have a leftover pending IRQ. 8228c2ecf20Sopenharmony_ci */ 8238c2ecf20Sopenharmony_ci (void)hwif->tp_ops->read_status(hwif); 8248c2ecf20Sopenharmony_ci } 8258c2ecf20Sopenharmony_ci goto out; 8268c2ecf20Sopenharmony_ci } 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci drive = hwif->cur_dev; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci if (!drive_is_ready(drive)) 8318c2ecf20Sopenharmony_ci /* 8328c2ecf20Sopenharmony_ci * This happens regularly when we share a PCI IRQ with 8338c2ecf20Sopenharmony_ci * another device. Unfortunately, it can also happen 8348c2ecf20Sopenharmony_ci * with some buggy drives that trigger the IRQ before 8358c2ecf20Sopenharmony_ci * their status register is up to date. Hopefully we have 8368c2ecf20Sopenharmony_ci * enough advance overhead that the latter isn't a problem. 8378c2ecf20Sopenharmony_ci */ 8388c2ecf20Sopenharmony_ci goto out; 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci hwif->handler = NULL; 8418c2ecf20Sopenharmony_ci hwif->expiry = NULL; 8428c2ecf20Sopenharmony_ci hwif->req_gen++; 8438c2ecf20Sopenharmony_ci del_timer(&hwif->timer); 8448c2ecf20Sopenharmony_ci spin_unlock(&hwif->lock); 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci if (hwif->port_ops && hwif->port_ops->clear_irq) 8478c2ecf20Sopenharmony_ci hwif->port_ops->clear_irq(drive); 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci if (drive->dev_flags & IDE_DFLAG_UNMASK) 8508c2ecf20Sopenharmony_ci local_irq_enable_in_hardirq(); 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci /* service this interrupt, may set handler for next interrupt */ 8538c2ecf20Sopenharmony_ci startstop = handler(drive); 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci spin_lock_irq(&hwif->lock); 8568c2ecf20Sopenharmony_ci /* 8578c2ecf20Sopenharmony_ci * Note that handler() may have set things up for another 8588c2ecf20Sopenharmony_ci * interrupt to occur soon, but it cannot happen until 8598c2ecf20Sopenharmony_ci * we exit from this routine, because it will be the 8608c2ecf20Sopenharmony_ci * same irq as is currently being serviced here, and Linux 8618c2ecf20Sopenharmony_ci * won't allow another of the same (on any CPU) until we return. 8628c2ecf20Sopenharmony_ci */ 8638c2ecf20Sopenharmony_ci if (startstop == ide_stopped && hwif->polling == 0) { 8648c2ecf20Sopenharmony_ci BUG_ON(hwif->handler); 8658c2ecf20Sopenharmony_ci rq_in_flight = hwif->rq; 8668c2ecf20Sopenharmony_ci hwif->rq = NULL; 8678c2ecf20Sopenharmony_ci ide_unlock_port(hwif); 8688c2ecf20Sopenharmony_ci plug_device = 1; 8698c2ecf20Sopenharmony_ci } 8708c2ecf20Sopenharmony_ci irq_ret = IRQ_HANDLED; 8718c2ecf20Sopenharmony_ciout: 8728c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hwif->lock, flags); 8738c2ecf20Sopenharmony_ciout_early: 8748c2ecf20Sopenharmony_ci if (plug_device) { 8758c2ecf20Sopenharmony_ci ide_unlock_host(hwif->host); 8768c2ecf20Sopenharmony_ci ide_requeue_and_plug(drive, rq_in_flight); 8778c2ecf20Sopenharmony_ci } 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci return irq_ret; 8808c2ecf20Sopenharmony_ci} 8818c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ide_intr); 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_civoid ide_pad_transfer(ide_drive_t *drive, int write, int len) 8848c2ecf20Sopenharmony_ci{ 8858c2ecf20Sopenharmony_ci ide_hwif_t *hwif = drive->hwif; 8868c2ecf20Sopenharmony_ci u8 buf[4] = { 0 }; 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci while (len > 0) { 8898c2ecf20Sopenharmony_ci if (write) 8908c2ecf20Sopenharmony_ci hwif->tp_ops->output_data(drive, NULL, buf, min(4, len)); 8918c2ecf20Sopenharmony_ci else 8928c2ecf20Sopenharmony_ci hwif->tp_ops->input_data(drive, NULL, buf, min(4, len)); 8938c2ecf20Sopenharmony_ci len -= 4; 8948c2ecf20Sopenharmony_ci } 8958c2ecf20Sopenharmony_ci} 8968c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ide_pad_transfer); 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_civoid ide_insert_request_head(ide_drive_t *drive, struct request *rq) 8998c2ecf20Sopenharmony_ci{ 9008c2ecf20Sopenharmony_ci drive->sense_rq_active = true; 9018c2ecf20Sopenharmony_ci list_add_tail(&rq->queuelist, &drive->rq_list); 9028c2ecf20Sopenharmony_ci kblockd_schedule_work(&drive->rq_work); 9038c2ecf20Sopenharmony_ci} 9048c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ide_insert_request_head); 905