mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	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: Aaron Lu <aaron.lu@intel.com> Acked-by: Tejun Heo <tj@kernel.org> Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
This commit is contained in:
		
							parent
							
								
									1757d902b0
								
							
						
					
					
						commit
						afe7595118
					
				
					 8 changed files with 170 additions and 1 deletions
				
			
		| 
						 | 
				
			
			@ -58,6 +58,19 @@ config ATA_ACPI
 | 
			
		|||
	  You can disable this at kernel boot time by using the
 | 
			
		||||
	  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
 | 
			
		||||
	bool "SATA Port Multiplier support"
 | 
			
		||||
	default y
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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_SATA_PMP)	+= libata-pmp.o
 | 
			
		||||
libata-$(CONFIG_ATA_ACPI)	+= libata-acpi.o
 | 
			
		||||
libata-$(CONFIG_SATA_ZPODD)	+= libata-zpodd.o
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2401,8 +2401,10 @@ int ata_dev_configure(struct ata_device *dev)
 | 
			
		|||
			dma_dir_string = ", DMADIR";
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (ata_id_has_da(dev->id))
 | 
			
		||||
		if (ata_id_has_da(dev->id)) {
 | 
			
		||||
			dev->flags |= ATA_DFLAG_DA;
 | 
			
		||||
			zpodd_init(dev);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* print device info to dmesg */
 | 
			
		||||
		if (ata_msg_drv(ap) && print_info)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3755,6 +3755,8 @@ static void ata_scsi_remove_dev(struct ata_device *dev)
 | 
			
		|||
	mutex_lock(&ap->scsi_host->scan_mutex);
 | 
			
		||||
	spin_lock_irqsave(ap->lock, flags);
 | 
			
		||||
 | 
			
		||||
	if (zpodd_dev_enabled(dev))
 | 
			
		||||
		zpodd_exit(dev);
 | 
			
		||||
	ata_acpi_unbind(dev);
 | 
			
		||||
 | 
			
		||||
	/* clearing dev->sdev is protected by host lock */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										100
									
								
								drivers/ata/libata-zpodd.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								drivers/ata/libata-zpodd.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,100 @@
 | 
			
		|||
#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;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -230,4 +230,18 @@ static inline void ata_sff_exit(void)
 | 
			
		|||
{ }
 | 
			
		||||
#endif /* CONFIG_ATA_SFF */
 | 
			
		||||
 | 
			
		||||
/* libata-zpodd.c */
 | 
			
		||||
#ifdef CONFIG_SATA_ZPODD
 | 
			
		||||
void zpodd_init(struct ata_device *dev);
 | 
			
		||||
void zpodd_exit(struct ata_device *dev);
 | 
			
		||||
static inline bool zpodd_dev_enabled(struct ata_device *dev)
 | 
			
		||||
{
 | 
			
		||||
	return dev->zpodd != NULL;
 | 
			
		||||
}
 | 
			
		||||
#else /* CONFIG_SATA_ZPODD */
 | 
			
		||||
static inline void zpodd_init(struct ata_device *dev) {}
 | 
			
		||||
static inline void zpodd_exit(struct ata_device *dev) {}
 | 
			
		||||
static inline bool zpodd_dev_enabled(struct ata_device *dev) { return false; }
 | 
			
		||||
#endif /* CONFIG_SATA_ZPODD */
 | 
			
		||||
 | 
			
		||||
#endif /* __LIBATA_H__ */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -620,6 +620,9 @@ struct ata_device {
 | 
			
		|||
#ifdef CONFIG_ATA_ACPI
 | 
			
		||||
	union acpi_object	*gtf_cache;
 | 
			
		||||
	unsigned int		gtf_filter;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef CONFIG_SATA_ZPODD
 | 
			
		||||
	void			*zpodd;
 | 
			
		||||
#endif
 | 
			
		||||
	struct device		tdev;
 | 
			
		||||
	/* n_sector is CLEAR_BEGIN, read comment above CLEAR_BEGIN */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -908,5 +908,39 @@ struct mode_page_header {
 | 
			
		|||
	__be16 desc_length;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* removable medium feature descriptor */
 | 
			
		||||
struct rm_feature_desc {
 | 
			
		||||
	__be16 feature_code;
 | 
			
		||||
#if defined(__BIG_ENDIAN_BITFIELD)
 | 
			
		||||
	__u8 reserved1:2;
 | 
			
		||||
	__u8 feature_version:4;
 | 
			
		||||
	__u8 persistent:1;
 | 
			
		||||
	__u8 curr:1;
 | 
			
		||||
#elif defined(__LITTLE_ENDIAN_BITFIELD)
 | 
			
		||||
	__u8 curr:1;
 | 
			
		||||
	__u8 persistent:1;
 | 
			
		||||
	__u8 feature_version:4;
 | 
			
		||||
	__u8 reserved1:2;
 | 
			
		||||
#endif
 | 
			
		||||
	__u8 add_len;
 | 
			
		||||
#if defined(__BIG_ENDIAN_BITFIELD)
 | 
			
		||||
	__u8 mech_type:3;
 | 
			
		||||
	__u8 load:1;
 | 
			
		||||
	__u8 eject:1;
 | 
			
		||||
	__u8 pvnt_jmpr:1;
 | 
			
		||||
	__u8 dbml:1;
 | 
			
		||||
	__u8 lock:1;
 | 
			
		||||
#elif defined(__LITTLE_ENDIAN_BITFIELD)
 | 
			
		||||
	__u8 lock:1;
 | 
			
		||||
	__u8 dbml:1;
 | 
			
		||||
	__u8 pvnt_jmpr:1;
 | 
			
		||||
	__u8 eject:1;
 | 
			
		||||
	__u8 load:1;
 | 
			
		||||
	__u8 mech_type:3;
 | 
			
		||||
#endif
 | 
			
		||||
	__u8 reserved2;
 | 
			
		||||
	__u8 reserved3;
 | 
			
		||||
	__u8 reserved4;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif /* _UAPI_LINUX_CDROM_H */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue