2019-05-19 15:08:55 +03:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2009-03-25 01:22:46 +03:00
|
|
|
|
|
|
|
#include <linux/kernel.h>
|
2011-07-17 23:33:58 +04:00
|
|
|
#include <linux/export.h>
|
2009-03-25 01:22:46 +03:00
|
|
|
#include <linux/ide.h>
|
|
|
|
|
2009-03-27 14:46:26 +03:00
|
|
|
#if defined(CONFIG_ARM) || defined(CONFIG_M68K) || defined(CONFIG_MIPS) || \
|
|
|
|
defined(CONFIG_PARISC) || defined(CONFIG_PPC) || defined(CONFIG_SPARC)
|
|
|
|
#include <asm/ide.h>
|
|
|
|
#else
|
|
|
|
#include <asm-generic/ide_iops.h>
|
|
|
|
#endif
|
|
|
|
|
2009-03-25 01:22:46 +03:00
|
|
|
/*
|
|
|
|
* Conventional PIO operations for ATA devices
|
|
|
|
*/
|
|
|
|
|
|
|
|
static u8 ide_inb(unsigned long port)
|
|
|
|
{
|
|
|
|
return (u8) inb(port);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ide_outb(u8 val, unsigned long port)
|
|
|
|
{
|
|
|
|
outb(val, port);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* MMIO operations, typically used for SATA controllers
|
|
|
|
*/
|
|
|
|
|
|
|
|
static u8 ide_mm_inb(unsigned long port)
|
|
|
|
{
|
|
|
|
return (u8) readb((void __iomem *) port);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ide_mm_outb(u8 value, unsigned long port)
|
|
|
|
{
|
|
|
|
writeb(value, (void __iomem *) port);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ide_exec_command(ide_hwif_t *hwif, u8 cmd)
|
|
|
|
{
|
|
|
|
if (hwif->host_flags & IDE_HFLAG_MMIO)
|
|
|
|
writeb(cmd, (void __iomem *)hwif->io_ports.command_addr);
|
|
|
|
else
|
|
|
|
outb(cmd, hwif->io_ports.command_addr);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(ide_exec_command);
|
|
|
|
|
|
|
|
u8 ide_read_status(ide_hwif_t *hwif)
|
|
|
|
{
|
|
|
|
if (hwif->host_flags & IDE_HFLAG_MMIO)
|
|
|
|
return readb((void __iomem *)hwif->io_ports.status_addr);
|
|
|
|
else
|
|
|
|
return inb(hwif->io_ports.status_addr);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(ide_read_status);
|
|
|
|
|
|
|
|
u8 ide_read_altstatus(ide_hwif_t *hwif)
|
|
|
|
{
|
|
|
|
if (hwif->host_flags & IDE_HFLAG_MMIO)
|
|
|
|
return readb((void __iomem *)hwif->io_ports.ctl_addr);
|
|
|
|
else
|
|
|
|
return inb(hwif->io_ports.ctl_addr);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(ide_read_altstatus);
|
|
|
|
|
2009-03-31 22:15:30 +04:00
|
|
|
void ide_write_devctl(ide_hwif_t *hwif, u8 ctl)
|
2009-03-25 01:22:46 +03:00
|
|
|
{
|
|
|
|
if (hwif->host_flags & IDE_HFLAG_MMIO)
|
|
|
|
writeb(ctl, (void __iomem *)hwif->io_ports.ctl_addr);
|
|
|
|
else
|
|
|
|
outb(ctl, hwif->io_ports.ctl_addr);
|
|
|
|
}
|
2009-03-31 22:15:30 +04:00
|
|
|
EXPORT_SYMBOL_GPL(ide_write_devctl);
|
2009-03-25 01:22:46 +03:00
|
|
|
|
2009-03-31 22:15:32 +04:00
|
|
|
void ide_dev_select(ide_drive_t *drive)
|
|
|
|
{
|
|
|
|
ide_hwif_t *hwif = drive->hwif;
|
|
|
|
u8 select = drive->select | ATA_DEVICE_OBS;
|
|
|
|
|
|
|
|
if (hwif->host_flags & IDE_HFLAG_MMIO)
|
|
|
|
writeb(select, (void __iomem *)hwif->io_ports.device_addr);
|
|
|
|
else
|
|
|
|
outb(select, hwif->io_ports.device_addr);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(ide_dev_select);
|
|
|
|
|
2009-04-08 16:13:03 +04:00
|
|
|
void ide_tf_load(ide_drive_t *drive, struct ide_taskfile *tf, u8 valid)
|
2009-03-25 01:22:46 +03:00
|
|
|
{
|
|
|
|
ide_hwif_t *hwif = drive->hwif;
|
|
|
|
struct ide_io_ports *io_ports = &hwif->io_ports;
|
|
|
|
void (*tf_outb)(u8 addr, unsigned long port);
|
|
|
|
u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0;
|
|
|
|
|
|
|
|
if (mmio)
|
|
|
|
tf_outb = ide_mm_outb;
|
|
|
|
else
|
|
|
|
tf_outb = ide_outb;
|
|
|
|
|
2009-04-08 16:13:01 +04:00
|
|
|
if (valid & IDE_VALID_FEATURE)
|
2009-03-25 01:22:46 +03:00
|
|
|
tf_outb(tf->feature, io_ports->feature_addr);
|
2009-04-08 16:13:01 +04:00
|
|
|
if (valid & IDE_VALID_NSECT)
|
2009-03-25 01:22:46 +03:00
|
|
|
tf_outb(tf->nsect, io_ports->nsect_addr);
|
2009-04-08 16:13:01 +04:00
|
|
|
if (valid & IDE_VALID_LBAL)
|
2009-03-25 01:22:46 +03:00
|
|
|
tf_outb(tf->lbal, io_ports->lbal_addr);
|
2009-04-08 16:13:01 +04:00
|
|
|
if (valid & IDE_VALID_LBAM)
|
2009-03-25 01:22:46 +03:00
|
|
|
tf_outb(tf->lbam, io_ports->lbam_addr);
|
2009-04-08 16:13:01 +04:00
|
|
|
if (valid & IDE_VALID_LBAH)
|
2009-03-25 01:22:46 +03:00
|
|
|
tf_outb(tf->lbah, io_ports->lbah_addr);
|
2009-04-08 16:13:01 +04:00
|
|
|
if (valid & IDE_VALID_DEVICE)
|
2009-04-08 16:13:02 +04:00
|
|
|
tf_outb(tf->device, io_ports->device_addr);
|
2009-03-25 01:22:46 +03:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(ide_tf_load);
|
|
|
|
|
2009-04-08 16:13:03 +04:00
|
|
|
void ide_tf_read(ide_drive_t *drive, struct ide_taskfile *tf, u8 valid)
|
2009-03-25 01:22:46 +03:00
|
|
|
{
|
|
|
|
ide_hwif_t *hwif = drive->hwif;
|
|
|
|
struct ide_io_ports *io_ports = &hwif->io_ports;
|
|
|
|
u8 (*tf_inb)(unsigned long port);
|
|
|
|
u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0;
|
|
|
|
|
2009-04-08 16:13:02 +04:00
|
|
|
if (mmio)
|
2009-03-25 01:22:46 +03:00
|
|
|
tf_inb = ide_mm_inb;
|
2009-04-08 16:13:02 +04:00
|
|
|
else
|
2009-03-25 01:22:46 +03:00
|
|
|
tf_inb = ide_inb;
|
|
|
|
|
2009-04-08 16:13:01 +04:00
|
|
|
if (valid & IDE_VALID_ERROR)
|
2009-03-31 22:15:30 +04:00
|
|
|
tf->error = tf_inb(io_ports->feature_addr);
|
2009-04-08 16:13:01 +04:00
|
|
|
if (valid & IDE_VALID_NSECT)
|
2009-03-25 01:22:46 +03:00
|
|
|
tf->nsect = tf_inb(io_ports->nsect_addr);
|
2009-04-08 16:13:01 +04:00
|
|
|
if (valid & IDE_VALID_LBAL)
|
2009-03-25 01:22:46 +03:00
|
|
|
tf->lbal = tf_inb(io_ports->lbal_addr);
|
2009-04-08 16:13:01 +04:00
|
|
|
if (valid & IDE_VALID_LBAM)
|
2009-03-25 01:22:46 +03:00
|
|
|
tf->lbam = tf_inb(io_ports->lbam_addr);
|
2009-04-08 16:13:01 +04:00
|
|
|
if (valid & IDE_VALID_LBAH)
|
2009-03-25 01:22:46 +03:00
|
|
|
tf->lbah = tf_inb(io_ports->lbah_addr);
|
2009-04-08 16:13:01 +04:00
|
|
|
if (valid & IDE_VALID_DEVICE)
|
2009-03-25 01:22:46 +03:00
|
|
|
tf->device = tf_inb(io_ports->device_addr);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(ide_tf_read);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Some localbus EIDE interfaces require a special access sequence
|
|
|
|
* when using 32-bit I/O instructions to transfer data. We call this
|
|
|
|
* the "vlb_sync" sequence, which consists of three successive reads
|
|
|
|
* of the sector count register location, with interrupts disabled
|
|
|
|
* to ensure that the reads all happen together.
|
|
|
|
*/
|
|
|
|
static void ata_vlb_sync(unsigned long port)
|
|
|
|
{
|
|
|
|
(void)inb(port);
|
|
|
|
(void)inb(port);
|
|
|
|
(void)inb(port);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is used for most PIO data transfers *from* the IDE interface
|
|
|
|
*
|
|
|
|
* These routines will round up any request for an odd number of bytes,
|
|
|
|
* so if an odd len is specified, be sure that there's at least one
|
|
|
|
* extra byte allocated for the buffer.
|
|
|
|
*/
|
2009-03-27 14:46:38 +03:00
|
|
|
void ide_input_data(ide_drive_t *drive, struct ide_cmd *cmd, void *buf,
|
2009-03-25 01:22:46 +03:00
|
|
|
unsigned int len)
|
|
|
|
{
|
|
|
|
ide_hwif_t *hwif = drive->hwif;
|
|
|
|
struct ide_io_ports *io_ports = &hwif->io_ports;
|
|
|
|
unsigned long data_addr = io_ports->data_addr;
|
2009-03-31 22:15:31 +04:00
|
|
|
unsigned int words = (len + 1) >> 1;
|
2009-03-25 01:22:46 +03:00
|
|
|
u8 io_32bit = drive->io_32bit;
|
|
|
|
u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0;
|
|
|
|
|
|
|
|
if (io_32bit) {
|
treewide: Remove uninitialized_var() usage
Using uninitialized_var() is dangerous as it papers over real bugs[1]
(or can in the future), and suppresses unrelated compiler warnings
(e.g. "unused variable"). If the compiler thinks it is uninitialized,
either simply initialize the variable or make compiler changes.
In preparation for removing[2] the[3] macro[4], remove all remaining
needless uses with the following script:
git grep '\buninitialized_var\b' | cut -d: -f1 | sort -u | \
xargs perl -pi -e \
's/\buninitialized_var\(([^\)]+)\)/\1/g;
s:\s*/\* (GCC be quiet|to make compiler happy) \*/$::g;'
drivers/video/fbdev/riva/riva_hw.c was manually tweaked to avoid
pathological white-space.
No outstanding warnings were found building allmodconfig with GCC 9.3.0
for x86_64, i386, arm64, arm, powerpc, powerpc64le, s390x, mips, sparc64,
alpha, and m68k.
[1] https://lore.kernel.org/lkml/20200603174714.192027-1-glider@google.com/
[2] https://lore.kernel.org/lkml/CA+55aFw+Vbj0i=1TGqCR5vQkCzWJ0QxK6CernOU6eedsudAixw@mail.gmail.com/
[3] https://lore.kernel.org/lkml/CA+55aFwgbgqhbp1fkxvRKEpzyR5J8n1vKT1VZdz9knmPuXhOeg@mail.gmail.com/
[4] https://lore.kernel.org/lkml/CA+55aFz2500WfbKXAx8s67wrm9=yVJu65TpLgN_ybYNv0VEOKA@mail.gmail.com/
Reviewed-by: Leon Romanovsky <leonro@mellanox.com> # drivers/infiniband and mlx4/mlx5
Acked-by: Jason Gunthorpe <jgg@mellanox.com> # IB
Acked-by: Kalle Valo <kvalo@codeaurora.org> # wireless drivers
Reviewed-by: Chao Yu <yuchao0@huawei.com> # erofs
Signed-off-by: Kees Cook <keescook@chromium.org>
2020-06-03 23:09:38 +03:00
|
|
|
unsigned long flags;
|
2009-03-25 01:22:46 +03:00
|
|
|
|
|
|
|
if ((io_32bit & 2) && !mmio) {
|
|
|
|
local_irq_save(flags);
|
|
|
|
ata_vlb_sync(io_ports->nsect_addr);
|
|
|
|
}
|
|
|
|
|
2009-03-31 22:15:31 +04:00
|
|
|
words >>= 1;
|
2009-03-25 01:22:46 +03:00
|
|
|
if (mmio)
|
2009-03-31 22:15:31 +04:00
|
|
|
__ide_mm_insl((void __iomem *)data_addr, buf, words);
|
2009-03-25 01:22:46 +03:00
|
|
|
else
|
2009-03-31 22:15:31 +04:00
|
|
|
insl(data_addr, buf, words);
|
2009-03-25 01:22:46 +03:00
|
|
|
|
|
|
|
if ((io_32bit & 2) && !mmio)
|
|
|
|
local_irq_restore(flags);
|
|
|
|
|
2009-03-31 22:15:31 +04:00
|
|
|
if (((len + 1) & 3) < 2)
|
|
|
|
return;
|
|
|
|
|
|
|
|
buf += len & ~3;
|
|
|
|
words = 1;
|
2009-03-25 01:22:46 +03:00
|
|
|
}
|
2009-03-31 22:15:31 +04:00
|
|
|
|
|
|
|
if (mmio)
|
|
|
|
__ide_mm_insw((void __iomem *)data_addr, buf, words);
|
|
|
|
else
|
|
|
|
insw(data_addr, buf, words);
|
2009-03-25 01:22:46 +03:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(ide_input_data);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is used for most PIO data transfers *to* the IDE interface
|
|
|
|
*/
|
2009-03-27 14:46:38 +03:00
|
|
|
void ide_output_data(ide_drive_t *drive, struct ide_cmd *cmd, void *buf,
|
2009-03-25 01:22:46 +03:00
|
|
|
unsigned int len)
|
|
|
|
{
|
|
|
|
ide_hwif_t *hwif = drive->hwif;
|
|
|
|
struct ide_io_ports *io_ports = &hwif->io_ports;
|
|
|
|
unsigned long data_addr = io_ports->data_addr;
|
2009-03-31 22:15:31 +04:00
|
|
|
unsigned int words = (len + 1) >> 1;
|
2009-03-25 01:22:46 +03:00
|
|
|
u8 io_32bit = drive->io_32bit;
|
|
|
|
u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0;
|
|
|
|
|
|
|
|
if (io_32bit) {
|
treewide: Remove uninitialized_var() usage
Using uninitialized_var() is dangerous as it papers over real bugs[1]
(or can in the future), and suppresses unrelated compiler warnings
(e.g. "unused variable"). If the compiler thinks it is uninitialized,
either simply initialize the variable or make compiler changes.
In preparation for removing[2] the[3] macro[4], remove all remaining
needless uses with the following script:
git grep '\buninitialized_var\b' | cut -d: -f1 | sort -u | \
xargs perl -pi -e \
's/\buninitialized_var\(([^\)]+)\)/\1/g;
s:\s*/\* (GCC be quiet|to make compiler happy) \*/$::g;'
drivers/video/fbdev/riva/riva_hw.c was manually tweaked to avoid
pathological white-space.
No outstanding warnings were found building allmodconfig with GCC 9.3.0
for x86_64, i386, arm64, arm, powerpc, powerpc64le, s390x, mips, sparc64,
alpha, and m68k.
[1] https://lore.kernel.org/lkml/20200603174714.192027-1-glider@google.com/
[2] https://lore.kernel.org/lkml/CA+55aFw+Vbj0i=1TGqCR5vQkCzWJ0QxK6CernOU6eedsudAixw@mail.gmail.com/
[3] https://lore.kernel.org/lkml/CA+55aFwgbgqhbp1fkxvRKEpzyR5J8n1vKT1VZdz9knmPuXhOeg@mail.gmail.com/
[4] https://lore.kernel.org/lkml/CA+55aFz2500WfbKXAx8s67wrm9=yVJu65TpLgN_ybYNv0VEOKA@mail.gmail.com/
Reviewed-by: Leon Romanovsky <leonro@mellanox.com> # drivers/infiniband and mlx4/mlx5
Acked-by: Jason Gunthorpe <jgg@mellanox.com> # IB
Acked-by: Kalle Valo <kvalo@codeaurora.org> # wireless drivers
Reviewed-by: Chao Yu <yuchao0@huawei.com> # erofs
Signed-off-by: Kees Cook <keescook@chromium.org>
2020-06-03 23:09:38 +03:00
|
|
|
unsigned long flags;
|
2009-03-25 01:22:46 +03:00
|
|
|
|
|
|
|
if ((io_32bit & 2) && !mmio) {
|
|
|
|
local_irq_save(flags);
|
|
|
|
ata_vlb_sync(io_ports->nsect_addr);
|
|
|
|
}
|
|
|
|
|
2009-03-31 22:15:31 +04:00
|
|
|
words >>= 1;
|
2009-03-25 01:22:46 +03:00
|
|
|
if (mmio)
|
2009-03-31 22:15:31 +04:00
|
|
|
__ide_mm_outsl((void __iomem *)data_addr, buf, words);
|
2009-03-25 01:22:46 +03:00
|
|
|
else
|
2009-03-31 22:15:31 +04:00
|
|
|
outsl(data_addr, buf, words);
|
2009-03-25 01:22:46 +03:00
|
|
|
|
|
|
|
if ((io_32bit & 2) && !mmio)
|
|
|
|
local_irq_restore(flags);
|
|
|
|
|
2009-03-31 22:15:31 +04:00
|
|
|
if (((len + 1) & 3) < 2)
|
|
|
|
return;
|
|
|
|
|
|
|
|
buf += len & ~3;
|
|
|
|
words = 1;
|
2009-03-25 01:22:46 +03:00
|
|
|
}
|
2009-03-31 22:15:31 +04:00
|
|
|
|
|
|
|
if (mmio)
|
|
|
|
__ide_mm_outsw((void __iomem *)data_addr, buf, words);
|
|
|
|
else
|
|
|
|
outsw(data_addr, buf, words);
|
2009-03-25 01:22:46 +03:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(ide_output_data);
|
|
|
|
|
|
|
|
const struct ide_tp_ops default_tp_ops = {
|
|
|
|
.exec_command = ide_exec_command,
|
|
|
|
.read_status = ide_read_status,
|
|
|
|
.read_altstatus = ide_read_altstatus,
|
2009-03-31 22:15:30 +04:00
|
|
|
.write_devctl = ide_write_devctl,
|
2009-03-25 01:22:46 +03:00
|
|
|
|
2009-03-31 22:15:32 +04:00
|
|
|
.dev_select = ide_dev_select,
|
2009-03-25 01:22:46 +03:00
|
|
|
.tf_load = ide_tf_load,
|
|
|
|
.tf_read = ide_tf_read,
|
|
|
|
|
|
|
|
.input_data = ide_input_data,
|
|
|
|
.output_data = ide_output_data,
|
|
|
|
};
|