Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit afe75951 authored by Aaron Lu's avatar Aaron Lu Committed by Jeff Garzik
Browse files

libata: identify and init ZPODD devices



The ODD can be enabled for ZPODD if the following three conditions are
satisfied:
1 The ODD supports device attention;
2 The platform can runtime power off the ODD through ACPI;
3 The ODD is either slot type or drawer type.
For such ODDs, zpodd_init is called and a new structure is allocated for
it to store ZPODD related stuffs.

And the zpodd_dev_enabled function is used to test if ZPODD is currently
enabled for this ODD.

A new config CONFIG_SATA_ZPODD is added to selectively build ZPODD code.

Signed-off-by: default avatarAaron Lu <aaron.lu@intel.com>
Acked-by: default avatarTejun Heo <tj@kernel.org>
Signed-off-by: default avatarJeff Garzik <jgarzik@redhat.com>
parent 1757d902
Loading
Loading
Loading
Loading
+13 −0
Original line number Original line Diff line number Diff line
@@ -58,6 +58,19 @@ config ATA_ACPI
	  You can disable this at kernel boot time by using the
	  You can disable this at kernel boot time by using the
	  option libata.noacpi=1
	  option libata.noacpi=1


config SATA_ZPODD
	bool "SATA Zero Power ODD Support"
	depends on ATA_ACPI
	default n
	help
	  This option adds support for SATA ZPODD. It requires both
	  ODD and the platform support, and if enabled, will automatically
	  power on/off the ODD when certain condition is satisfied. This
	  does not impact user's experience of the ODD, only power is saved
	  when ODD is not in use(i.e. no disc inside).

	  If unsure, say N.

config SATA_PMP
config SATA_PMP
	bool "SATA Port Multiplier support"
	bool "SATA Port Multiplier support"
	default y
	default y
+1 −0
Original line number Original line Diff line number Diff line
@@ -107,3 +107,4 @@ libata-y := libata-core.o libata-scsi.o libata-eh.o libata-transport.o
libata-$(CONFIG_ATA_SFF)	+= libata-sff.o
libata-$(CONFIG_ATA_SFF)	+= libata-sff.o
libata-$(CONFIG_SATA_PMP)	+= libata-pmp.o
libata-$(CONFIG_SATA_PMP)	+= libata-pmp.o
libata-$(CONFIG_ATA_ACPI)	+= libata-acpi.o
libata-$(CONFIG_ATA_ACPI)	+= libata-acpi.o
libata-$(CONFIG_SATA_ZPODD)	+= libata-zpodd.o
+3 −1
Original line number Original line Diff line number Diff line
@@ -2401,8 +2401,10 @@ int ata_dev_configure(struct ata_device *dev)
			dma_dir_string = ", DMADIR";
			dma_dir_string = ", DMADIR";
		}
		}


		if (ata_id_has_da(dev->id))
		if (ata_id_has_da(dev->id)) {
			dev->flags |= ATA_DFLAG_DA;
			dev->flags |= ATA_DFLAG_DA;
			zpodd_init(dev);
		}


		/* print device info to dmesg */
		/* print device info to dmesg */
		if (ata_msg_drv(ap) && print_info)
		if (ata_msg_drv(ap) && print_info)
+2 −0
Original line number Original line Diff line number Diff line
@@ -3755,6 +3755,8 @@ static void ata_scsi_remove_dev(struct ata_device *dev)
	mutex_lock(&ap->scsi_host->scan_mutex);
	mutex_lock(&ap->scsi_host->scan_mutex);
	spin_lock_irqsave(ap->lock, flags);
	spin_lock_irqsave(ap->lock, flags);


	if (zpodd_dev_enabled(dev))
		zpodd_exit(dev);
	ata_acpi_unbind(dev);
	ata_acpi_unbind(dev);


	/* clearing dev->sdev is protected by host lock */
	/* clearing dev->sdev is protected by host lock */
+100 −0
Original line number Original line Diff line number Diff line
#include <linux/libata.h>
#include <linux/cdrom.h>

#include "libata.h"

enum odd_mech_type {
	ODD_MECH_TYPE_SLOT,
	ODD_MECH_TYPE_DRAWER,
	ODD_MECH_TYPE_UNSUPPORTED,
};

struct zpodd {
	enum odd_mech_type	mech_type; /* init during probe, RO afterwards */
	struct ata_device	*dev;
};

/* Per the spec, only slot type and drawer type ODD can be supported */
static enum odd_mech_type zpodd_get_mech_type(struct ata_device *dev)
{
	char buf[16];
	unsigned int ret;
	struct rm_feature_desc *desc = (void *)(buf + 8);
	struct ata_taskfile tf = {};

	char cdb[] = {  GPCMD_GET_CONFIGURATION,
			2,      /* only 1 feature descriptor requested */
			0, 3,   /* 3, removable medium feature */
			0, 0, 0,/* reserved */
			0, sizeof(buf),
			0, 0, 0,
	};

	tf.flags = ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
	tf.command = ATA_CMD_PACKET;
	tf.protocol = ATAPI_PROT_PIO;
	tf.lbam = sizeof(buf);

	ret = ata_exec_internal(dev, &tf, cdb, DMA_FROM_DEVICE,
				buf, sizeof(buf), 0);
	if (ret)
		return ODD_MECH_TYPE_UNSUPPORTED;

	if (be16_to_cpu(desc->feature_code) != 3)
		return ODD_MECH_TYPE_UNSUPPORTED;

	if (desc->mech_type == 0 && desc->load == 0 && desc->eject == 1)
		return ODD_MECH_TYPE_SLOT;
	else if (desc->mech_type == 1 && desc->load == 0 && desc->eject == 1)
		return ODD_MECH_TYPE_DRAWER;
	else
		return ODD_MECH_TYPE_UNSUPPORTED;
}

static bool odd_can_poweroff(struct ata_device *ata_dev)
{
	acpi_handle handle;
	acpi_status status;
	struct acpi_device *acpi_dev;

	handle = ata_dev_acpi_handle(ata_dev);
	if (!handle)
		return false;

	status = acpi_bus_get_device(handle, &acpi_dev);
	if (ACPI_FAILURE(status))
		return false;

	return acpi_device_can_poweroff(acpi_dev);
}

void zpodd_init(struct ata_device *dev)
{
	enum odd_mech_type mech_type;
	struct zpodd *zpodd;

	if (dev->zpodd)
		return;

	if (!odd_can_poweroff(dev))
		return;

	mech_type = zpodd_get_mech_type(dev);
	if (mech_type == ODD_MECH_TYPE_UNSUPPORTED)
		return;

	zpodd = kzalloc(sizeof(struct zpodd), GFP_KERNEL);
	if (!zpodd)
		return;

	zpodd->mech_type = mech_type;

	zpodd->dev = dev;
	dev->zpodd = zpodd;
}

void zpodd_exit(struct ata_device *dev)
{
	kfree(dev->zpodd);
	dev->zpodd = NULL;
}
Loading