Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * A wrapper for multiple PHYs which passes all phy_* function calls to
0004  * multiple (actual) PHY devices. This is comes handy when initializing
0005  * all PHYs on a HCD and to keep them all in the same state.
0006  *
0007  * Copyright (C) 2018 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
0008  */
0009 
0010 #include <linux/device.h>
0011 #include <linux/list.h>
0012 #include <linux/phy/phy.h>
0013 #include <linux/of.h>
0014 
0015 #include "phy.h"
0016 
0017 struct usb_phy_roothub {
0018     struct phy      *phy;
0019     struct list_head    list;
0020 };
0021 
0022 static int usb_phy_roothub_add_phy(struct device *dev, int index,
0023                    struct list_head *list)
0024 {
0025     struct usb_phy_roothub *roothub_entry;
0026     struct phy *phy;
0027 
0028     phy = devm_of_phy_get_by_index(dev, dev->of_node, index);
0029     if (IS_ERR(phy)) {
0030         if (PTR_ERR(phy) == -ENODEV)
0031             return 0;
0032         else
0033             return PTR_ERR(phy);
0034     }
0035 
0036     roothub_entry = devm_kzalloc(dev, sizeof(*roothub_entry), GFP_KERNEL);
0037     if (!roothub_entry)
0038         return -ENOMEM;
0039 
0040     INIT_LIST_HEAD(&roothub_entry->list);
0041 
0042     roothub_entry->phy = phy;
0043 
0044     list_add_tail(&roothub_entry->list, list);
0045 
0046     return 0;
0047 }
0048 
0049 struct usb_phy_roothub *usb_phy_roothub_alloc(struct device *dev)
0050 {
0051     struct usb_phy_roothub *phy_roothub;
0052     int i, num_phys, err;
0053 
0054     if (!IS_ENABLED(CONFIG_GENERIC_PHY))
0055         return NULL;
0056 
0057     num_phys = of_count_phandle_with_args(dev->of_node, "phys",
0058                           "#phy-cells");
0059     if (num_phys <= 0)
0060         return NULL;
0061 
0062     phy_roothub = devm_kzalloc(dev, sizeof(*phy_roothub), GFP_KERNEL);
0063     if (!phy_roothub)
0064         return ERR_PTR(-ENOMEM);
0065 
0066     INIT_LIST_HEAD(&phy_roothub->list);
0067 
0068     for (i = 0; i < num_phys; i++) {
0069         err = usb_phy_roothub_add_phy(dev, i, &phy_roothub->list);
0070         if (err)
0071             return ERR_PTR(err);
0072     }
0073 
0074     return phy_roothub;
0075 }
0076 EXPORT_SYMBOL_GPL(usb_phy_roothub_alloc);
0077 
0078 int usb_phy_roothub_init(struct usb_phy_roothub *phy_roothub)
0079 {
0080     struct usb_phy_roothub *roothub_entry;
0081     struct list_head *head;
0082     int err;
0083 
0084     if (!phy_roothub)
0085         return 0;
0086 
0087     head = &phy_roothub->list;
0088 
0089     list_for_each_entry(roothub_entry, head, list) {
0090         err = phy_init(roothub_entry->phy);
0091         if (err)
0092             goto err_exit_phys;
0093     }
0094 
0095     return 0;
0096 
0097 err_exit_phys:
0098     list_for_each_entry_continue_reverse(roothub_entry, head, list)
0099         phy_exit(roothub_entry->phy);
0100 
0101     return err;
0102 }
0103 EXPORT_SYMBOL_GPL(usb_phy_roothub_init);
0104 
0105 int usb_phy_roothub_exit(struct usb_phy_roothub *phy_roothub)
0106 {
0107     struct usb_phy_roothub *roothub_entry;
0108     struct list_head *head;
0109     int err, ret = 0;
0110 
0111     if (!phy_roothub)
0112         return 0;
0113 
0114     head = &phy_roothub->list;
0115 
0116     list_for_each_entry(roothub_entry, head, list) {
0117         err = phy_exit(roothub_entry->phy);
0118         if (err)
0119             ret = err;
0120     }
0121 
0122     return ret;
0123 }
0124 EXPORT_SYMBOL_GPL(usb_phy_roothub_exit);
0125 
0126 int usb_phy_roothub_set_mode(struct usb_phy_roothub *phy_roothub,
0127                  enum phy_mode mode)
0128 {
0129     struct usb_phy_roothub *roothub_entry;
0130     struct list_head *head;
0131     int err;
0132 
0133     if (!phy_roothub)
0134         return 0;
0135 
0136     head = &phy_roothub->list;
0137 
0138     list_for_each_entry(roothub_entry, head, list) {
0139         err = phy_set_mode(roothub_entry->phy, mode);
0140         if (err)
0141             goto err_out;
0142     }
0143 
0144     return 0;
0145 
0146 err_out:
0147     list_for_each_entry_continue_reverse(roothub_entry, head, list)
0148         phy_power_off(roothub_entry->phy);
0149 
0150     return err;
0151 }
0152 EXPORT_SYMBOL_GPL(usb_phy_roothub_set_mode);
0153 
0154 int usb_phy_roothub_calibrate(struct usb_phy_roothub *phy_roothub)
0155 {
0156     struct usb_phy_roothub *roothub_entry;
0157     struct list_head *head;
0158     int err;
0159 
0160     if (!phy_roothub)
0161         return 0;
0162 
0163     head = &phy_roothub->list;
0164 
0165     list_for_each_entry(roothub_entry, head, list) {
0166         err = phy_calibrate(roothub_entry->phy);
0167         if (err)
0168             return err;
0169     }
0170 
0171     return 0;
0172 }
0173 EXPORT_SYMBOL_GPL(usb_phy_roothub_calibrate);
0174 
0175 int usb_phy_roothub_power_on(struct usb_phy_roothub *phy_roothub)
0176 {
0177     struct usb_phy_roothub *roothub_entry;
0178     struct list_head *head;
0179     int err;
0180 
0181     if (!phy_roothub)
0182         return 0;
0183 
0184     head = &phy_roothub->list;
0185 
0186     list_for_each_entry(roothub_entry, head, list) {
0187         err = phy_power_on(roothub_entry->phy);
0188         if (err)
0189             goto err_out;
0190     }
0191 
0192     return 0;
0193 
0194 err_out:
0195     list_for_each_entry_continue_reverse(roothub_entry, head, list)
0196         phy_power_off(roothub_entry->phy);
0197 
0198     return err;
0199 }
0200 EXPORT_SYMBOL_GPL(usb_phy_roothub_power_on);
0201 
0202 void usb_phy_roothub_power_off(struct usb_phy_roothub *phy_roothub)
0203 {
0204     struct usb_phy_roothub *roothub_entry;
0205 
0206     if (!phy_roothub)
0207         return;
0208 
0209     list_for_each_entry_reverse(roothub_entry, &phy_roothub->list, list)
0210         phy_power_off(roothub_entry->phy);
0211 }
0212 EXPORT_SYMBOL_GPL(usb_phy_roothub_power_off);
0213 
0214 int usb_phy_roothub_suspend(struct device *controller_dev,
0215                 struct usb_phy_roothub *phy_roothub)
0216 {
0217     usb_phy_roothub_power_off(phy_roothub);
0218 
0219     /* keep the PHYs initialized so the device can wake up the system */
0220     if (device_may_wakeup(controller_dev))
0221         return 0;
0222 
0223     return usb_phy_roothub_exit(phy_roothub);
0224 }
0225 EXPORT_SYMBOL_GPL(usb_phy_roothub_suspend);
0226 
0227 int usb_phy_roothub_resume(struct device *controller_dev,
0228                struct usb_phy_roothub *phy_roothub)
0229 {
0230     int err;
0231 
0232     /* if the device can't wake up the system _exit was called */
0233     if (!device_may_wakeup(controller_dev)) {
0234         err = usb_phy_roothub_init(phy_roothub);
0235         if (err)
0236             return err;
0237     }
0238 
0239     err = usb_phy_roothub_power_on(phy_roothub);
0240 
0241     /* undo _init if _power_on failed */
0242     if (err && !device_may_wakeup(controller_dev))
0243         usb_phy_roothub_exit(phy_roothub);
0244 
0245     return err;
0246 }
0247 EXPORT_SYMBOL_GPL(usb_phy_roothub_resume);