not_suspended = 0;
list_for_each_entry(pdd, &genpd->dev_list, list_node)
if (pdd->dev->driver && (!pm_runtime_suspended(pdd->dev)
- || pdd->dev->power.irq_safe))
+ || pdd->dev->power.irq_safe || to_gpd_data(pdd)->always_on))
not_suspended++;
if (not_suspended > genpd->in_progress)
might_sleep_if(!genpd->dev_irq_safe);
+ if (dev_gpd_data(dev)->always_on)
+ return -EBUSY;
+
stop_ok = genpd->gov ? genpd->gov->stop_ok : NULL;
if (stop_ok && !stop_ok(dev))
return -EBUSY;
if (IS_ERR(genpd))
return -EINVAL;
- if (genpd->suspend_power_off
+ if (genpd->suspend_power_off || dev_gpd_data(dev)->always_on
|| (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev)))
return 0;
if (IS_ERR(genpd))
return -EINVAL;
- if (genpd->suspend_power_off
+ if (genpd->suspend_power_off || dev_gpd_data(dev)->always_on
|| (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev)))
return 0;
if (IS_ERR(genpd))
return -EINVAL;
- return genpd->suspend_power_off ? 0 : genpd_stop_dev(genpd, dev);
+ return genpd->suspend_power_off || dev_gpd_data(dev)->always_on ?
+ 0 : genpd_stop_dev(genpd, dev);
}
/**
if (IS_ERR(genpd))
return -EINVAL;
- return genpd->suspend_power_off ? 0 : genpd_start_dev(genpd, dev);
+ return genpd->suspend_power_off || dev_gpd_data(dev)->always_on ?
+ 0 : genpd_start_dev(genpd, dev);
}
/**
pm_genpd_poweron(genpd);
- return genpd_start_dev(genpd, dev);
+ return dev_gpd_data(dev)->always_on ? 0 : genpd_start_dev(genpd, dev);
}
/**
}
/**
+ * pm_genpd_dev_always_on - Set/unset the "always on" flag for a given device.
+ * @dev: Device to set/unset the flag for.
+ * @val: The new value of the device's "always on" flag.
+ */
+void pm_genpd_dev_always_on(struct device *dev, bool val)
+{
+ struct pm_subsys_data *psd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->power.lock, flags);
+
+ psd = dev_to_psd(dev);
+ if (psd && psd->domain_data)
+ to_gpd_data(psd->domain_data)->always_on = val;
+
+ spin_unlock_irqrestore(&dev->power.lock, flags);
+}
+EXPORT_SYMBOL_GPL(pm_genpd_dev_always_on);
+
+/**
* pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
* @genpd: Master PM domain to add the subdomain to.
* @subdomain: Subdomain to be added.
struct gpd_dev_ops ops;
struct gpd_timing_data td;
bool need_restore;
+ bool always_on;
};
#ifdef CONFIG_PM_GENERIC_DOMAINS
extern int pm_genpd_remove_device(struct generic_pm_domain *genpd,
struct device *dev);
+extern void pm_genpd_dev_always_on(struct device *dev, bool val);
extern int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
struct generic_pm_domain *new_subdomain);
extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
{
return -ENOSYS;
}
+static inline void pm_genpd_dev_always_on(struct device *dev, bool val) {}
static inline int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
struct generic_pm_domain *new_sd)
{