int __pm_runtime_set_status()

in power/runtime.c [1209:1307]


int __pm_runtime_set_status(struct device *dev, unsigned int status)
{
	struct device *parent = dev->parent;
	bool notify_parent = false;
	int error = 0;

	if (status != RPM_ACTIVE && status != RPM_SUSPENDED)
		return -EINVAL;

	spin_lock_irq(&dev->power.lock);

	/*
	 * Prevent PM-runtime from being enabled for the device or return an
	 * error if it is enabled already and working.
	 */
	if (dev->power.runtime_error || dev->power.disable_depth)
		dev->power.disable_depth++;
	else
		error = -EAGAIN;

	spin_unlock_irq(&dev->power.lock);

	if (error)
		return error;

	/*
	 * If the new status is RPM_ACTIVE, the suppliers can be activated
	 * upfront regardless of the current status, because next time
	 * rpm_put_suppliers() runs, the rpm_active refcounts of the links
	 * involved will be dropped down to one anyway.
	 */
	if (status == RPM_ACTIVE) {
		int idx = device_links_read_lock();

		error = rpm_get_suppliers(dev);
		if (error)
			status = RPM_SUSPENDED;

		device_links_read_unlock(idx);
	}

	spin_lock_irq(&dev->power.lock);

	if (dev->power.runtime_status == status || !parent)
		goto out_set;

	if (status == RPM_SUSPENDED) {
		atomic_add_unless(&parent->power.child_count, -1, 0);
		notify_parent = !parent->power.ignore_children;
	} else {
		spin_lock_nested(&parent->power.lock, SINGLE_DEPTH_NESTING);

		/*
		 * It is invalid to put an active child under a parent that is
		 * not active, has runtime PM enabled and the
		 * 'power.ignore_children' flag unset.
		 */
		if (!parent->power.disable_depth
		    && !parent->power.ignore_children
		    && parent->power.runtime_status != RPM_ACTIVE) {
			dev_err(dev, "runtime PM trying to activate child device %s but parent (%s) is not active\n",
				dev_name(dev),
				dev_name(parent));
			error = -EBUSY;
		} else if (dev->power.runtime_status == RPM_SUSPENDED) {
			atomic_inc(&parent->power.child_count);
		}

		spin_unlock(&parent->power.lock);

		if (error) {
			status = RPM_SUSPENDED;
			goto out;
		}
	}

 out_set:
	__update_runtime_status(dev, status);
	if (!error)
		dev->power.runtime_error = 0;

 out:
	spin_unlock_irq(&dev->power.lock);

	if (notify_parent)
		pm_request_idle(parent);

	if (status == RPM_SUSPENDED) {
		int idx = device_links_read_lock();

		rpm_put_suppliers(dev);

		device_links_read_unlock(idx);
	}

	pm_runtime_enable(dev);

	return error;
}