summaryrefslogtreecommitdiff
path: root/drivers/ide/ide-lib.c
blob: 7b9f655adbc2d184e1c07ded0f41def2326b4951 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/types.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/export.h>
#include <linux/interrupt.h>
#include <linux/ide.h>
#include <linux/bitops.h>

u64 ide_get_lba_addr(struct ide_cmd *cmd, int lba48)
{
	struct ide_taskfile *tf = &cmd->tf;
	u32 high, low;

	low  = (tf->lbah << 16) | (tf->lbam << 8) | tf->lbal;
	if (lba48) {
		tf = &cmd->hob;
		high = (tf->lbah << 16) | (tf->lbam << 8) | tf->lbal;
	} else
		high = tf->device & 0xf;

	return ((u64)high << 24) | low;
}
EXPORT_SYMBOL_GPL(ide_get_lba_addr);

static void ide_dump_sector(ide_drive_t *drive)
{
	struct ide_cmd cmd;
	struct ide_taskfile *tf = &cmd.tf;
	u8 lba48 = !!(drive->dev_flags & IDE_DFLAG_LBA48);

	memset(&cmd, 0, sizeof(cmd));
	if (lba48) {
		cmd.valid.in.tf  = IDE_VALID_LBA;
		cmd.valid.in.hob = IDE_VALID_LBA;
		cmd.tf_flags = IDE_TFLAG_LBA48;
	} else
		cmd.valid.in.tf  = IDE_VALID_LBA | IDE_VALID_DEVICE;

	ide_tf_readback(drive, &cmd);

	if (lba48 || (tf->device & ATA_LBA))
		printk(KERN_CONT ", LBAsect=%llu",
			(unsigned long long)ide_get_lba_addr(&cmd, lba48));
	else
		printk(KERN_CONT ", CHS=%d/%d/%d", (tf->lbah << 8) + tf->lbam,
			tf->device & 0xf, tf->lbal);
}

static void ide_dump_ata_error(ide_drive_t *drive, u8 err)
{
	printk(KERN_CONT "{ ");
	if (err & ATA_ABORTED)
		printk(KERN_CONT "DriveStatusError ");
	if (err & ATA_ICRC)
		printk(KERN_CONT "%s",
			(err & ATA_ABORTED) ? "BadCRC " : "BadSector ");
	if (err & ATA_UNC)
		printk(KERN_CONT "UncorrectableError ");
	if (err & ATA_IDNF)
		printk(KERN_CONT "SectorIdNotFound ");
	if (err & ATA_TRK0NF)
		printk(KERN_CONT "TrackZeroNotFound ");
	if (err & ATA_AMNF)
		printk(KERN_CONT "AddrMarkNotFound ");
	printk(KERN_CONT "}");
	if ((err & (ATA_BBK | ATA_ABORTED)) == ATA_BBK ||
	    (err & (ATA_UNC | ATA_IDNF | ATA_AMNF))) {
		struct request *rq = drive->hwif->rq;

		ide_dump_sector(drive);

		if (rq)
			printk(KERN_CONT ", sector=%llu",
			       (unsigned long long)blk_rq_pos(rq));
	}
	printk(KERN_CONT "\n");
}

static void ide_dump_atapi_error(ide_drive_t *drive, u8 err)
{
	printk(KERN_CONT "{ ");
	if (err & ATAPI_ILI)
		printk(KERN_CONT "IllegalLengthIndication ");
	if (err & ATAPI_EOM)
		printk(KERN_CONT "EndOfMedia ");
	if (err & ATA_ABORTED)
		printk(KERN_CONT "AbortedCommand ");
	if (err & ATA_MCR)
		printk(KERN_CONT "MediaChangeRequested ");
	if (err & ATAPI_LFS)
		printk(KERN_CONT "LastFailedSense=0x%02x ",
			(err & ATAPI_LFS) >> 4);
	printk(KERN_CONT "}\n");
}

/**
 *	ide_dump_status		-	translate ATA/ATAPI error
 *	@drive: drive that status applies to
 *	@msg: text message to print
 *	@stat: status byte to decode
 *
 *	Error reporting, in human readable form (luxurious, but a memory hog).
 *	Combines the drive name, message and status byte to provide a
 *	user understandable explanation of the device error.
 */

u8 ide_dump_status(ide_drive_t *drive, const char *msg, u8 stat)
{
	u8 err = 0;

	printk(KERN_ERR "%s: %s: status=0x%02x { ", drive->name, msg, stat);
	if (stat & ATA_BUSY)
		printk(KERN_CONT "Busy ");
	else {
		if (stat & ATA_DRDY)
			printk(KERN_CONT "DriveReady ");
		if (stat & ATA_DF)
			printk(KERN_CONT "DeviceFault ");
		if (stat & ATA_DSC)
			printk(KERN_CONT "SeekComplete ");
		if (stat & ATA_DRQ)
			printk(KERN_CONT "DataRequest ");
		if (stat & ATA_CORR)
			printk(KERN_CONT "CorrectedError ");
		if (stat & ATA_SENSE)
			printk(KERN_CONT "Sense ");
		if (stat & ATA_ERR)
			printk(KERN_CONT "Error ");
	}
	printk(KERN_CONT "}\n");
	if ((stat & (ATA_BUSY | ATA_ERR)) == ATA_ERR) {
		err = ide_read_error(drive);
		printk(KERN_ERR "%s: %s: error=0x%02x ", drive->name, msg, err);
		if (drive->media == ide_disk)
			ide_dump_ata_error(drive, err);
		else
			ide_dump_atapi_error(drive, err);
	}

	printk(KERN_ERR "%s: possibly failed opcode: 0x%02x\n",
		drive->name, drive->hwif->cmd.tf.command);

	return err;
}
EXPORT_SYMBOL(ide_dump_status);