diff --git a/Documentation/phy.txt b/Documentation/phy.txt index ebff6ee52441edbca95a0ebc8b20cd49ac218d3d..3ea6ee7f7fdac5c0dba015f290a95146b7c66739 100644 --- a/Documentation/phy.txt +++ b/Documentation/phy.txt @@ -61,9 +61,9 @@ struct phy *devm_phy_create(struct device *dev, const struct phy_ops *ops, The PHY drivers can use one of the above 2 APIs to create the PHY by passing the device pointer, phy ops and init_data. phy_ops is a set of function pointers for performing PHY operations such as -init, exit, power_on and power_off. *init_data* is mandatory to get a reference -to the PHY in the case of non-dt boot. See section *Board File Initialization* -on how init_data should be used. +init, exit, power_on and power_off, , suspend, resume and advertise_quirks. +*init_data* is mandatory to get a reference to the PHY in the case of non-dt +boot. See section *Board File Initialization* on how init_data should be used. Inorder to dereference the private data (in phy_ops), the phy provider driver can use phy_set_drvdata() after creating the PHY and use phy_get_drvdata() in diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index c64a2f3b2d624fb9d8c266b979416d6b53ed5b9f..107d16c40f7d871447af0a9dc8c572258eebc160 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -276,6 +276,64 @@ int phy_power_off(struct phy *phy) } EXPORT_SYMBOL_GPL(phy_power_off); +int phy_suspend(struct phy *phy) +{ + int ret = 0; + + if (!phy->ops->suspend) + return -ENOTSUPP; + + mutex_lock(&phy->mutex); + + if (--phy->resume_count == 0) { + ret = phy->ops->suspend(phy); + if (ret) { + dev_err(&phy->dev, "phy suspend failed --> %d\n", ret); + /* reverting the resume_count since suspend failed */ + phy->resume_count++; + goto out; + } + } +out: + mutex_unlock(&phy->mutex); + return ret; +} +EXPORT_SYMBOL(phy_suspend); + +int phy_resume(struct phy *phy) +{ + int ret = 0; + + if (!phy->ops->resume) + return -ENOTSUPP; + + mutex_lock(&phy->mutex); + + if (phy->resume_count++ == 0) { + ret = phy->ops->resume(phy); + if (ret) { + dev_err(&phy->dev, "phy resume failed --> %d\n", ret); + /* reverting the resume_count since resume failed */ + phy->resume_count--; + goto out; + } + } +out: + mutex_unlock(&phy->mutex); + return ret; +} +EXPORT_SYMBOL(phy_resume); + +void phy_advertise_quirks(struct phy *phy) +{ + if (phy->ops->advertise_quirks) { + mutex_lock(&phy->mutex); + phy->ops->advertise_quirks(phy); + mutex_unlock(&phy->mutex); + } +} +EXPORT_SYMBOL(phy_advertise_quirks); + /** * _of_phy_get() - lookup and obtain a reference to a phy by phandle * @np: device_node for which to get the phy diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h index 2760744cb2a75ff59e3d172a6dd864ef42b32106..43cfdcf39fa4dbf5b411ca6499c4cc7a19635038 100644 --- a/include/linux/phy/phy.h +++ b/include/linux/phy/phy.h @@ -27,6 +27,14 @@ struct phy; * @exit: operation to be performed while exiting * @power_on: powering on the phy * @power_off: powering off the phy + * @advertise_quirks: setting specific phy quirks. this api is for an + internal use of the device driver, and its + purpose is to exteriorize the driver's phy quirks + according to phy version (or other parameters), + so further behaviour of the driver's phy is based + on those quirks. + * @suspend: suspending the phy + * @resume: resuming the phy * @owner: the module owner containing the ops */ struct phy_ops { @@ -34,6 +42,9 @@ struct phy_ops { int (*exit)(struct phy *phy); int (*power_on)(struct phy *phy); int (*power_off)(struct phy *phy); + void (*advertise_quirks)(struct phy *phy); + int (*suspend)(struct phy *phy); + int (*resume)(struct phy *phy); struct module *owner; }; @@ -55,6 +66,8 @@ struct phy_attrs { * @init_count: used to protect when the PHY is used by multiple consumers * @power_count: used to protect when the PHY is used by multiple consumers * @phy_attrs: used to specify PHY specific attributes + * @resume_count: used to protect when the PHY is used by multiple consumers + * that resume and suspend it */ struct phy { struct device dev; @@ -65,6 +78,7 @@ struct phy { int init_count; int power_count; struct phy_attrs attrs; + int resume_count; }; /** @@ -137,6 +151,10 @@ int phy_init(struct phy *phy); int phy_exit(struct phy *phy); int phy_power_on(struct phy *phy); int phy_power_off(struct phy *phy); +void phy_advertise_quirks(struct phy *phy); +int phy_suspend(struct phy *phy); +int phy_resume(struct phy *phy); + static inline int phy_get_bus_width(struct phy *phy) { return phy->attrs.bus_width; @@ -248,6 +266,21 @@ static inline void phy_set_bus_width(struct phy *phy, int bus_width) return; } +static inline void phy_advertise_quirks(struct phy *phy) +{ + return; +} + +static inline int phy_suspend(struct phy *phy) +{ + return -ENOSYS; +} + +static inline int phy_resume(struct phy *phy) +{ + return -ENOSYS; +} + static inline struct phy *phy_get(struct device *dev, const char *string) { return ERR_PTR(-ENOSYS);