Fix IDE commands issued, fix endian issues, fix non MMIO

Fixes IDE issues found on the Malta board under Qemu:

1) DMA implied commands were sent to the controller in stead of the PIO
variants. The rest of the code is DMA free and written for PIO operation.

2) direct pointer access was used to read and write the registers instead
of the inb/inw/outb/outw functions/macros. Registers don't have to be
memory mapped and ATA_CURR_BASE() does not have to return an offset from
address zero.

3) Endian isues in ide_ident() and reading/writing data in general. Names
were corrupted and sizes misreported.

Tested malta_defconfig and maltael_defconfig to work again in Qemu.

Signed-off-by: Reinoud Zandijk <reinoud@NetBSD.org>
Tested-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
This commit is contained in:
Reinoud Zandijk 2021-02-24 17:44:42 +01:00 committed by Tom Rini
parent 57c675d699
commit 0a527fda78
2 changed files with 48 additions and 114 deletions

View File

@ -130,56 +130,38 @@ OUT:
* ATAPI Support * ATAPI Support
*/ */
#if defined(CONFIG_IDE_SWAP_IO)
/* since ATAPI may use commands with not 4 bytes alligned length /* since ATAPI may use commands with not 4 bytes alligned length
* we have our own transfer functions, 2 bytes alligned */ * we have our own transfer functions, 2 bytes alligned */
__weak void ide_output_data_shorts(int dev, ushort *sect_buf, int shorts) __weak void ide_output_data_shorts(int dev, ushort *sect_buf, int shorts)
{ {
uintptr_t paddr = (ATA_CURR_BASE(dev) + ATA_DATA_REG);
ushort *dbuf; ushort *dbuf;
volatile ushort *pbuf;
pbuf = (ushort *)(ATA_CURR_BASE(dev) + ATA_DATA_REG);
dbuf = (ushort *)sect_buf; dbuf = (ushort *)sect_buf;
debug("in output data shorts base for read is %lx\n", debug("in output data shorts base for read is %p\n", (void *)paddr);
(unsigned long) pbuf);
while (shorts--) { while (shorts--) {
EIEIO; EIEIO;
*pbuf = *dbuf++; outw(cpu_to_le16(*dbuf++), paddr);
} }
} }
__weak void ide_input_data_shorts(int dev, ushort *sect_buf, int shorts) __weak void ide_input_data_shorts(int dev, ushort *sect_buf, int shorts)
{ {
uintptr_t paddr = (ATA_CURR_BASE(dev) + ATA_DATA_REG);
ushort *dbuf; ushort *dbuf;
volatile ushort *pbuf;
pbuf = (ushort *)(ATA_CURR_BASE(dev) + ATA_DATA_REG);
dbuf = (ushort *)sect_buf; dbuf = (ushort *)sect_buf;
debug("in input data shorts base for read is %lx\n", debug("in input data shorts base for read is %p\n", (void *)paddr);
(unsigned long) pbuf);
while (shorts--) { while (shorts--) {
EIEIO; EIEIO;
*dbuf++ = *pbuf; *dbuf++ = le16_to_cpu(inw(paddr));
} }
} }
#else /* ! CONFIG_IDE_SWAP_IO */
__weak void ide_output_data_shorts(int dev, ushort *sect_buf, int shorts)
{
outsw(ATA_CURR_BASE(dev) + ATA_DATA_REG, sect_buf, shorts);
}
__weak void ide_input_data_shorts(int dev, ushort *sect_buf, int shorts)
{
insw(ATA_CURR_BASE(dev) + ATA_DATA_REG, sect_buf, shorts);
}
#endif /* CONFIG_IDE_SWAP_IO */
/* /*
* Wait until (Status & mask) == res, or timeout (in ms) * Wait until (Status & mask) == res, or timeout (in ms)
* Return last status * Return last status
@ -636,19 +618,6 @@ static void ide_ident(struct blk_desc *dev_desc)
sizeof(dev_desc->vendor)); sizeof(dev_desc->vendor));
ident_cpy((unsigned char *)dev_desc->product, iop.serial_no, ident_cpy((unsigned char *)dev_desc->product, iop.serial_no,
sizeof(dev_desc->product)); sizeof(dev_desc->product));
#ifdef __LITTLE_ENDIAN
/*
* firmware revision, model, and serial number have Big Endian Byte
* order in Word. Convert all three to little endian.
*
* See CF+ and CompactFlash Specification Revision 2.0:
* 6.2.1.6: Identify Drive, Table 39 for more details
*/
strswab(dev_desc->revision);
strswab(dev_desc->vendor);
strswab(dev_desc->product);
#endif /* __LITTLE_ENDIAN */
if ((iop.config & 0x0080) == 0x0080) if ((iop.config & 0x0080) == 0x0080)
dev_desc->removable = 1; dev_desc->removable = 1;
@ -662,26 +631,22 @@ static void ide_ident(struct blk_desc *dev_desc)
} }
#endif /* CONFIG_ATAPI */ #endif /* CONFIG_ATAPI */
#ifdef __BIG_ENDIAN iop.lba_capacity[0] = be16_to_cpu(iop.lba_capacity[0]);
/* swap shorts */ iop.lba_capacity[1] = be16_to_cpu(iop.lba_capacity[1]);
dev_desc->lba = (iop.lba_capacity << 16) | (iop.lba_capacity >> 16); dev_desc->lba =
#else /* ! __BIG_ENDIAN */ ((unsigned long)iop.lba_capacity[0]) |
/* ((unsigned long)iop.lba_capacity[1] << 16);
* do not swap shorts on little endian
*
* See CF+ and CompactFlash Specification Revision 2.0:
* 6.2.1.6: Identfy Drive, Table 39, Word Address 57-58 for details.
*/
dev_desc->lba = iop.lba_capacity;
#endif /* __BIG_ENDIAN */
#ifdef CONFIG_LBA48 #ifdef CONFIG_LBA48
if (iop.command_set_2 & 0x0400) { /* LBA 48 support */ if (iop.command_set_2 & 0x0400) { /* LBA 48 support */
dev_desc->lba48 = 1; dev_desc->lba48 = 1;
dev_desc->lba = (unsigned long long) iop.lba48_capacity[0] | for (int i = 0; i < 4; i++)
((unsigned long long) iop.lba48_capacity[1] << 16) | iop.lba48_capacity[i] = be16_to_cpu(iop.lba48_capacity[i]);
((unsigned long long) iop.lba48_capacity[2] << 32) | dev_desc->lba =
((unsigned long long) iop.lba48_capacity[3] << 48); ((unsigned long long)iop.lba48_capacity[0] |
((unsigned long long)iop.lba48_capacity[1] << 16) |
((unsigned long long)iop.lba48_capacity[2] << 32) |
((unsigned long long)iop.lba48_capacity[3] << 48));
} else { } else {
dev_desc->lba48 = 0; dev_desc->lba48 = 0;
} }
@ -846,90 +811,59 @@ void ide_init(void)
#endif #endif
} }
/* We only need to swap data if we are running on a big endian cpu. */
#if defined(__LITTLE_ENDIAN)
__weak void ide_input_swap_data(int dev, ulong *sect_buf, int words) __weak void ide_input_swap_data(int dev, ulong *sect_buf, int words)
{ {
ide_input_data(dev, sect_buf, words); uintptr_t paddr = (ATA_CURR_BASE(dev) + ATA_DATA_REG);
}
#else
__weak void ide_input_swap_data(int dev, ulong *sect_buf, int words)
{
volatile ushort *pbuf =
(ushort *)(ATA_CURR_BASE(dev) + ATA_DATA_REG);
ushort *dbuf = (ushort *)sect_buf; ushort *dbuf = (ushort *)sect_buf;
debug("in input swap data base for read is %lx\n", debug("in input swap data base for read is %p\n", (void *)paddr);
(unsigned long) pbuf);
while (words--) { while (words--) {
#ifdef __MIPS__ EIEIO;
*dbuf++ = swab16p((u16 *)pbuf); *dbuf++ = be16_to_cpu(inw(paddr));
*dbuf++ = swab16p((u16 *)pbuf); EIEIO;
#else *dbuf++ = be16_to_cpu(inw(paddr));
*dbuf++ = ld_le16(pbuf);
*dbuf++ = ld_le16(pbuf);
#endif /* !MIPS */
} }
} }
#endif /* __LITTLE_ENDIAN */
#if defined(CONFIG_IDE_SWAP_IO)
__weak void ide_output_data(int dev, const ulong *sect_buf, int words)
{
ushort *dbuf;
volatile ushort *pbuf;
pbuf = (ushort *)(ATA_CURR_BASE(dev) + ATA_DATA_REG);
dbuf = (ushort *)sect_buf;
while (words--) {
EIEIO;
*pbuf = *dbuf++;
EIEIO;
*pbuf = *dbuf++;
}
}
#else /* ! CONFIG_IDE_SWAP_IO */
__weak void ide_output_data(int dev, const ulong *sect_buf, int words) __weak void ide_output_data(int dev, const ulong *sect_buf, int words)
{ {
#if defined(CONFIG_IDE_AHB) #if defined(CONFIG_IDE_AHB)
ide_write_data(dev, sect_buf, words); ide_write_data(dev, sect_buf, words);
#else #else
outsw(ATA_CURR_BASE(dev) + ATA_DATA_REG, sect_buf, words << 1); uintptr_t paddr = (ATA_CURR_BASE(dev) + ATA_DATA_REG);
#endif
}
#endif /* CONFIG_IDE_SWAP_IO */
#if defined(CONFIG_IDE_SWAP_IO)
__weak void ide_input_data(int dev, ulong *sect_buf, int words)
{
ushort *dbuf; ushort *dbuf;
volatile ushort *pbuf;
pbuf = (ushort *)(ATA_CURR_BASE(dev) + ATA_DATA_REG);
dbuf = (ushort *)sect_buf; dbuf = (ushort *)sect_buf;
debug("in input data base for read is %lx\n", (unsigned long) pbuf);
while (words--) { while (words--) {
EIEIO; EIEIO;
*dbuf++ = *pbuf; outw(cpu_to_le16(*dbuf++), paddr);
EIEIO; EIEIO;
*dbuf++ = *pbuf; outw(cpu_to_le16(*dbuf++), paddr);
} }
#endif /* CONFIG_IDE_AHB */
} }
#else /* ! CONFIG_IDE_SWAP_IO */
__weak void ide_input_data(int dev, ulong *sect_buf, int words) __weak void ide_input_data(int dev, ulong *sect_buf, int words)
{ {
#if defined(CONFIG_IDE_AHB) #if defined(CONFIG_IDE_AHB)
ide_read_data(dev, sect_buf, words); ide_read_data(dev, sect_buf, words);
#else #else
insw(ATA_CURR_BASE(dev) + ATA_DATA_REG, sect_buf, words << 1); uintptr_t paddr = (ATA_CURR_BASE(dev) + ATA_DATA_REG);
#endif ushort *dbuf;
}
#endif /* CONFIG_IDE_SWAP_IO */ dbuf = (ushort *)sect_buf;
debug("in input data base for read is %p\n", (void *)paddr);
while (words--) {
EIEIO;
*dbuf++ = le16_to_cpu(inw(paddr));
EIEIO;
*dbuf++ = le16_to_cpu(inw(paddr));
}
#endif /* CONFIG_IDE_AHB */
}
#ifdef CONFIG_BLK #ifdef CONFIG_BLK
ulong ide_read(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, ulong ide_read(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
@ -1019,14 +953,14 @@ ulong ide_read(struct blk_desc *block_dev, lbaint_t blknr, lbaint_t blkcnt,
if (lba48) { if (lba48) {
ide_outb(device, ATA_DEV_HD, ide_outb(device, ATA_DEV_HD,
ATA_LBA | ATA_DEVICE(device)); ATA_LBA | ATA_DEVICE(device));
ide_outb(device, ATA_COMMAND, ATA_CMD_READ_EXT); ide_outb(device, ATA_COMMAND, ATA_CMD_PIO_READ_EXT);
} else } else
#endif #endif
{ {
ide_outb(device, ATA_DEV_HD, ATA_LBA | ide_outb(device, ATA_DEV_HD, ATA_LBA |
ATA_DEVICE(device) | ((blknr >> 24) & 0xF)); ATA_DEVICE(device) | ((blknr >> 24) & 0xF));
ide_outb(device, ATA_COMMAND, ATA_CMD_READ); ide_outb(device, ATA_COMMAND, ATA_CMD_PIO_READ);
} }
udelay(50); udelay(50);
@ -1116,14 +1050,14 @@ ulong ide_write(struct blk_desc *block_dev, lbaint_t blknr, lbaint_t blkcnt,
if (lba48) { if (lba48) {
ide_outb(device, ATA_DEV_HD, ide_outb(device, ATA_DEV_HD,
ATA_LBA | ATA_DEVICE(device)); ATA_LBA | ATA_DEVICE(device));
ide_outb(device, ATA_COMMAND, ATA_CMD_WRITE_EXT); ide_outb(device, ATA_COMMAND, ATA_CMD_PIO_WRITE_EXT);
} else } else
#endif #endif
{ {
ide_outb(device, ATA_DEV_HD, ATA_LBA | ide_outb(device, ATA_DEV_HD, ATA_LBA |
ATA_DEVICE(device) | ((blknr >> 24) & 0xF)); ATA_DEVICE(device) | ((blknr >> 24) & 0xF));
ide_outb(device, ATA_COMMAND, ATA_CMD_WRITE); ide_outb(device, ATA_COMMAND, ATA_CMD_PIO_WRITE);
} }
udelay(50); udelay(50);

View File

@ -134,7 +134,7 @@ typedef struct hd_driveid {
unsigned short cur_capacity1; /* (2 words, misaligned int) */ unsigned short cur_capacity1; /* (2 words, misaligned int) */
unsigned char multsect; /* current multiple sector count */ unsigned char multsect; /* current multiple sector count */
unsigned char multsect_valid; /* when (bit0==1) multsect is ok */ unsigned char multsect_valid; /* when (bit0==1) multsect is ok */
unsigned int lba_capacity; /* total number of sectors */ unsigned short lba_capacity[2];/* two words containing total number of sectors */
unsigned short dma_1word; /* single-word dma info */ unsigned short dma_1word; /* single-word dma info */
unsigned short dma_mword; /* multiple-word dma info */ unsigned short dma_mword; /* multiple-word dma info */
unsigned short eide_pio_modes; /* bits 0:mode3 1:mode4 */ unsigned short eide_pio_modes; /* bits 0:mode3 1:mode4 */