/*
* libata-acpi.c
* Provides ACPI support for PATA/SATA.
*
* Copyright (C) 2006 Intel Corp.
* Copyright (C) 2006 Randy Dunlap
*/
#include <linux/module.h>
#include <linux/ata.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/acpi.h>
#include <linux/libata.h>
#include <linux/pci.h>
#include <scsi/scsi_device.h>
#include "libata.h"
#include <acpi/acpi_bus.h>
#include <acpi/acnames.h>
#include <acpi/acnamesp.h>
#include <acpi/acparser.h>
#include <acpi/acexcep.h>
#include <acpi/acmacros.h>
#include <acpi/actypes.h>
enum {
ATA_ACPI_FILTER_SETXFER = 1 << 0,
ATA_ACPI_FILTER_LOCK = 1 << 1,
ATA_ACPI_FILTER_DIPM = 1 << 2,
ATA_ACPI_FILTER_DEFAULT = ATA_ACPI_FILTER_SETXFER |
ATA_ACPI_FILTER_LOCK |
ATA_ACPI_FILTER_DIPM,
};
static unsigned int ata_acpi_gtf_filter = ATA_ACPI_FILTER_DEFAULT;
module_param_named(acpi_gtf_filter, ata_acpi_gtf_filter, int, 0644);
MODULE_PARM_DESC(acpi_gtf_filter, "filter mask for ACPI _GTF commands, set to filter out (0x1=set xfermode, 0x2=lock/freeze lock, 0x4=DIPM)");
#define NO_PORT_MULT 0xffff
#define SATA_ADR(root, pmp) (((root) << 16) | (pmp))
#define REGS_PER_GTF 7
struct ata_acpi_gtf {
u8 tf[REGS_PER_GTF]; /* regs. 0x1f1 - 0x1f7 */
} __packed;
/*
* Helper - belongs in the PCI layer somewhere eventually
*/
static int is_pci_dev(struct device *dev)
{
return (dev->bus == &pci_bus_type);
}
static void ata_acpi_clear_gtf(struct ata_device *dev)
{
kfree(dev->gtf_cache);
dev->gtf_cache = NULL;
}
/**
* ata_acpi_associate_sata_port - associate SATA port with ACPI objects
* @ap: target SATA port
*
* Look up ACPI objects associated with @ap and initialize acpi_handle
* fields of @ap, the port and devices accordingly.
*
* LOCKING:
* EH context.
*
* RETURNS:
* 0 on success, -errno on failure.
*/
void ata_acpi_associate_sata_port(struct ata_port *ap)
{
WARN_ON(!(ap->flags & ATA_FLAG_ACPI_SATA));
if (!sata_pmp_attached(ap)) {
acpi_integer adr = SATA_ADR(ap->port_no, NO_PORT_MULT);
ap->link.device->acpi_handle =
acpi_get_child(ap->host->acpi_handle, adr);
} else {
struct ata_link *link;
ap->link.device->acpi_handle = NULL;
ata_port_for_each_link(link, ap) {
acpi_integer adr = SATA_ADR(ap->port_no, link->pmp);
link->device->acpi_handle =
acpi_get_child(ap->host->acpi_handle, adr);
}
}
}
static void ata_acpi_associate_ide_port(struct ata_port *ap)
{
int max_devices, i;
ap->acpi_handle = acpi_get_child(ap->host->acpi_handle, ap->port_no);
if (!ap->acpi_handle)
return;
max_devices = 1;
if (ap->flags & ATA_FLAG_SLAVE_POSS)
max_devices++;
for (i = 0; i < max_devices; i++) {
struct ata_device *dev = &ap->link.device[i];
dev->acpi_handle = acpi_get_child(ap->acpi_handle, i);
}
if (ata_acpi_gtm(ap, &ap->__acpi_init_gtm) == 0)
ap->pflags |= ATA_PFLAG_INIT_GTM_VALID;
}
static void ata_acpi_eject_device(acpi_handle handle)
{
struct acpi_object_list arg_list;
union acpi_object arg;
arg_list.count = 1;
arg_list.pointer = &arg;
arg.type = ACPI_TYPE_INTEGER;
arg.integer.value = 1;
if (ACPI_FAILURE(acpi_evaluate_object(handle, "_EJ0",
&arg_list, NULL)))
printk(KERN_ERR "Failed to evaluate _EJ0!\n");
}
/* @ap and @dev are the same as ata_acpi_handle_hotplug() */
static void ata_acpi_detach_device(struct ata_port *ap, struct ata_device *dev)
{
if (dev)
dev->flags |= ATA_DFLAG_DETACH;
else {
struct ata_link *tlink;
struct ata_device *tdev;
ata_port_for_each_link<