Commit c53a741a authored by Martin K. Petersen's avatar Martin K. Petersen
Browse files

Merge patch series "Support power resources defined in acpi on ata"

Markus Probst <markus.probst@posteo.de> says:

This series adds support for power resources defined in acpi on ata
ports/devices. A device can define a power resource in an ata port/device,
which then gets powered on right before the port is probed. This can be
useful for devices, which have sata power connectors that are:
  a: powered down by default
  b: can be individually powered on
like in some synology nas devices. If thats the case it will be assumed,
that the power resource won't survive reboots and therefore the disk will
be stopped.

Link: https://patch.msgid.link/20251104142413.322347-1-markus.probst@posteo.de


Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parents 5b5dedf0 8c59fc1c
Loading
Loading
Loading
Loading
+67 −0
Original line number Diff line number Diff line
@@ -245,6 +245,73 @@ void ata_acpi_bind_dev(struct ata_device *dev)
				   ata_acpi_dev_uevent);
}

/**
 * ata_acpi_dev_manage_restart - if the disk should be stopped (spun down) on
 *                               system restart.
 * @dev: target ATA device
 *
 * RETURNS:
 * true if the disk should be stopped, otherwise false.
 */
bool ata_acpi_dev_manage_restart(struct ata_device *dev)
{
	struct device *tdev;

	/*
	 * If ATA_FLAG_ACPI_SATA is set, the acpi fwnode is attached to the
	 * ata_device instead of the ata_port.
	 */
	if (dev->link->ap->flags & ATA_FLAG_ACPI_SATA)
		tdev = &dev->tdev;
	else
		tdev = &dev->link->ap->tdev;

	if (!is_acpi_device_node(tdev->fwnode))
		return false;
	return acpi_bus_power_manageable(ACPI_HANDLE(tdev));
}

/**
 * ata_acpi_port_power_on - set the power state of the ata port to D0
 * @ap: target ATA port
 *
 * This function is called at the beginning of ata_port_probe().
 */
void ata_acpi_port_power_on(struct ata_port *ap)
{
	acpi_handle handle;
	int i;

	/*
	 * If ATA_FLAG_ACPI_SATA is set, the acpi fwnode is attached to the
	 * ata_device instead of the ata_port.
	 */
	if (ap->flags & ATA_FLAG_ACPI_SATA) {
		for (i = 0; i < ATA_MAX_DEVICES; i++) {
			struct ata_device *dev = &ap->link.device[i];

			if (!is_acpi_device_node(dev->tdev.fwnode))
				continue;
			handle = ACPI_HANDLE(&dev->tdev);
			if (!acpi_bus_power_manageable(handle))
				continue;
			if (acpi_bus_set_power(handle, ACPI_STATE_D0))
				ata_dev_err(dev,
					    "acpi: failed to set power state to D0\n");
		}
		return;
	}

	if (!is_acpi_device_node(ap->tdev.fwnode))
		return;
	handle = ACPI_HANDLE(&ap->tdev);
	if (!acpi_bus_power_manageable(handle))
		return;

	if (acpi_bus_set_power(handle, ACPI_STATE_D0))
		ata_port_err(ap, "acpi: failed to set power state to D0\n");
}

/**
 * ata_acpi_dissociate - dissociate ATA host from ACPI objects
 * @host: target ATA host
+2 −0
Original line number Diff line number Diff line
@@ -5904,6 +5904,8 @@ void ata_port_probe(struct ata_port *ap)
	struct ata_eh_info *ehi = &ap->link.eh_info;
	unsigned long flags;

	ata_acpi_port_power_on(ap);

	/* kick EH for boot probing */
	spin_lock_irqsave(ap->lock, flags);

+1 −0
Original line number Diff line number Diff line
@@ -1095,6 +1095,7 @@ int ata_scsi_dev_config(struct scsi_device *sdev, struct queue_limits *lim,
		 */
		sdev->manage_runtime_start_stop = 1;
		sdev->manage_shutdown = 1;
		sdev->manage_restart = ata_acpi_dev_manage_restart(dev);
		sdev->force_runtime_start_on_system_start = 1;
	}

+4 −0
Original line number Diff line number Diff line
@@ -130,6 +130,8 @@ extern void ata_acpi_on_disable(struct ata_device *dev);
extern void ata_acpi_set_state(struct ata_port *ap, pm_message_t state);
extern void ata_acpi_bind_port(struct ata_port *ap);
extern void ata_acpi_bind_dev(struct ata_device *dev);
extern void ata_acpi_port_power_on(struct ata_port *ap);
extern bool ata_acpi_dev_manage_restart(struct ata_device *dev);
extern acpi_handle ata_dev_acpi_handle(struct ata_device *dev);
#else
static inline void ata_acpi_dissociate(struct ata_host *host) { }
@@ -140,6 +142,8 @@ static inline void ata_acpi_set_state(struct ata_port *ap,
				      pm_message_t state) { }
static inline void ata_acpi_bind_port(struct ata_port *ap) {}
static inline void ata_acpi_bind_dev(struct ata_device *dev) {}
static inline void ata_acpi_port_power_on(struct ata_port *ap) {}
static inline bool ata_acpi_dev_manage_restart(struct ata_device *dev) { return 0; }
#endif

/* libata-scsi.c */
+33 −1
Original line number Diff line number Diff line
@@ -318,6 +318,35 @@ static ssize_t manage_shutdown_store(struct device *dev,
}
static DEVICE_ATTR_RW(manage_shutdown);

static ssize_t manage_restart_show(struct device *dev,
				   struct device_attribute *attr, char *buf)
{
	struct scsi_disk *sdkp = to_scsi_disk(dev);
	struct scsi_device *sdp = sdkp->device;

	return sysfs_emit(buf, "%u\n", sdp->manage_restart);
}

static ssize_t manage_restart_store(struct device *dev,
				    struct device_attribute *attr,
				    const char *buf, size_t count)
{
	struct scsi_disk *sdkp = to_scsi_disk(dev);
	struct scsi_device *sdp = sdkp->device;
	bool v;

	if (!capable(CAP_SYS_ADMIN))
		return -EACCES;

	if (kstrtobool(buf, &v))
		return -EINVAL;

	sdp->manage_restart = v;

	return count;
}
static DEVICE_ATTR_RW(manage_restart);

static ssize_t
allow_restart_show(struct device *dev, struct device_attribute *attr, char *buf)
{
@@ -654,6 +683,7 @@ static struct attribute *sd_disk_attrs[] = {
	&dev_attr_manage_system_start_stop.attr,
	&dev_attr_manage_runtime_start_stop.attr,
	&dev_attr_manage_shutdown.attr,
	&dev_attr_manage_restart.attr,
	&dev_attr_protection_type.attr,
	&dev_attr_protection_mode.attr,
	&dev_attr_app_tag_own.attr,
@@ -4177,7 +4207,9 @@ static void sd_shutdown(struct device *dev)
	    (system_state == SYSTEM_POWER_OFF &&
	     sdkp->device->manage_shutdown) ||
	    (system_state == SYSTEM_RUNNING &&
	     sdkp->device->manage_runtime_start_stop)) {
	     sdkp->device->manage_runtime_start_stop) ||
	    (system_state == SYSTEM_RESTART &&
	     sdkp->device->manage_restart)) {
		sd_printk(KERN_NOTICE, sdkp, "Stopping disk\n");
		sd_start_stop_device(sdkp, 0);
	}
Loading