Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Samsung SoC USB 1.1/2.0 PHY driver
0004  *
0005  * Copyright (C) 2013 Samsung Electronics Co., Ltd.
0006  * Author: Kamil Debski <k.debski@samsung.com>
0007  */
0008 
0009 #include <linux/clk.h>
0010 #include <linux/mfd/syscon.h>
0011 #include <linux/module.h>
0012 #include <linux/of.h>
0013 #include <linux/of_address.h>
0014 #include <linux/of_device.h>
0015 #include <linux/phy/phy.h>
0016 #include <linux/platform_device.h>
0017 #include <linux/spinlock.h>
0018 #include "phy-samsung-usb2.h"
0019 
0020 static int samsung_usb2_phy_power_on(struct phy *phy)
0021 {
0022     struct samsung_usb2_phy_instance *inst = phy_get_drvdata(phy);
0023     struct samsung_usb2_phy_driver *drv = inst->drv;
0024     int ret;
0025 
0026     dev_dbg(drv->dev, "Request to power_on \"%s\" usb phy\n",
0027         inst->cfg->label);
0028 
0029     if (drv->vbus) {
0030         ret = regulator_enable(drv->vbus);
0031         if (ret)
0032             goto err_regulator;
0033     }
0034 
0035     ret = clk_prepare_enable(drv->clk);
0036     if (ret)
0037         goto err_main_clk;
0038     ret = clk_prepare_enable(drv->ref_clk);
0039     if (ret)
0040         goto err_instance_clk;
0041     if (inst->cfg->power_on) {
0042         spin_lock(&drv->lock);
0043         ret = inst->cfg->power_on(inst);
0044         spin_unlock(&drv->lock);
0045         if (ret)
0046             goto err_power_on;
0047     }
0048 
0049     return 0;
0050 
0051 err_power_on:
0052     clk_disable_unprepare(drv->ref_clk);
0053 err_instance_clk:
0054     clk_disable_unprepare(drv->clk);
0055 err_main_clk:
0056     if (drv->vbus)
0057         regulator_disable(drv->vbus);
0058 err_regulator:
0059     return ret;
0060 }
0061 
0062 static int samsung_usb2_phy_power_off(struct phy *phy)
0063 {
0064     struct samsung_usb2_phy_instance *inst = phy_get_drvdata(phy);
0065     struct samsung_usb2_phy_driver *drv = inst->drv;
0066     int ret = 0;
0067 
0068     dev_dbg(drv->dev, "Request to power_off \"%s\" usb phy\n",
0069         inst->cfg->label);
0070     if (inst->cfg->power_off) {
0071         spin_lock(&drv->lock);
0072         ret = inst->cfg->power_off(inst);
0073         spin_unlock(&drv->lock);
0074         if (ret)
0075             return ret;
0076     }
0077     clk_disable_unprepare(drv->ref_clk);
0078     clk_disable_unprepare(drv->clk);
0079     if (drv->vbus)
0080         ret = regulator_disable(drv->vbus);
0081 
0082     return ret;
0083 }
0084 
0085 static const struct phy_ops samsung_usb2_phy_ops = {
0086     .power_on   = samsung_usb2_phy_power_on,
0087     .power_off  = samsung_usb2_phy_power_off,
0088     .owner      = THIS_MODULE,
0089 };
0090 
0091 static struct phy *samsung_usb2_phy_xlate(struct device *dev,
0092                     struct of_phandle_args *args)
0093 {
0094     struct samsung_usb2_phy_driver *drv;
0095 
0096     drv = dev_get_drvdata(dev);
0097     if (!drv)
0098         return ERR_PTR(-EINVAL);
0099 
0100     if (WARN_ON(args->args[0] >= drv->cfg->num_phys))
0101         return ERR_PTR(-ENODEV);
0102 
0103     return drv->instances[args->args[0]].phy;
0104 }
0105 
0106 static const struct of_device_id samsung_usb2_phy_of_match[] = {
0107 #ifdef CONFIG_PHY_EXYNOS4X12_USB2
0108     {
0109         .compatible = "samsung,exynos3250-usb2-phy",
0110         .data = &exynos3250_usb2_phy_config,
0111     },
0112 #endif
0113 #ifdef CONFIG_PHY_EXYNOS4210_USB2
0114     {
0115         .compatible = "samsung,exynos4210-usb2-phy",
0116         .data = &exynos4210_usb2_phy_config,
0117     },
0118 #endif
0119 #ifdef CONFIG_PHY_EXYNOS4X12_USB2
0120     {
0121         .compatible = "samsung,exynos4x12-usb2-phy",
0122         .data = &exynos4x12_usb2_phy_config,
0123     },
0124 #endif
0125 #ifdef CONFIG_PHY_EXYNOS5250_USB2
0126     {
0127         .compatible = "samsung,exynos5250-usb2-phy",
0128         .data = &exynos5250_usb2_phy_config,
0129     },
0130     {
0131         .compatible = "samsung,exynos5420-usb2-phy",
0132         .data = &exynos5420_usb2_phy_config,
0133     },
0134 #endif
0135 #ifdef CONFIG_PHY_S5PV210_USB2
0136     {
0137         .compatible = "samsung,s5pv210-usb2-phy",
0138         .data = &s5pv210_usb2_phy_config,
0139     },
0140 #endif
0141     { },
0142 };
0143 MODULE_DEVICE_TABLE(of, samsung_usb2_phy_of_match);
0144 
0145 static int samsung_usb2_phy_probe(struct platform_device *pdev)
0146 {
0147     const struct samsung_usb2_phy_config *cfg;
0148     struct device *dev = &pdev->dev;
0149     struct phy_provider *phy_provider;
0150     struct samsung_usb2_phy_driver *drv;
0151     int i, ret;
0152 
0153     if (!pdev->dev.of_node) {
0154         dev_err(dev, "This driver is required to be instantiated from device tree\n");
0155         return -EINVAL;
0156     }
0157 
0158     cfg = of_device_get_match_data(dev);
0159     if (!cfg)
0160         return -EINVAL;
0161 
0162     drv = devm_kzalloc(dev, struct_size(drv, instances, cfg->num_phys),
0163                GFP_KERNEL);
0164     if (!drv)
0165         return -ENOMEM;
0166 
0167     dev_set_drvdata(dev, drv);
0168     spin_lock_init(&drv->lock);
0169 
0170     drv->cfg = cfg;
0171     drv->dev = dev;
0172 
0173     drv->reg_phy = devm_platform_ioremap_resource(pdev, 0);
0174     if (IS_ERR(drv->reg_phy)) {
0175         dev_err(dev, "Failed to map register memory (phy)\n");
0176         return PTR_ERR(drv->reg_phy);
0177     }
0178 
0179     drv->reg_pmu = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
0180         "samsung,pmureg-phandle");
0181     if (IS_ERR(drv->reg_pmu)) {
0182         dev_err(dev, "Failed to map PMU registers (via syscon)\n");
0183         return PTR_ERR(drv->reg_pmu);
0184     }
0185 
0186     if (drv->cfg->has_mode_switch) {
0187         drv->reg_sys = syscon_regmap_lookup_by_phandle(
0188                 pdev->dev.of_node, "samsung,sysreg-phandle");
0189         if (IS_ERR(drv->reg_sys)) {
0190             dev_err(dev, "Failed to map system registers (via syscon)\n");
0191             return PTR_ERR(drv->reg_sys);
0192         }
0193     }
0194 
0195     drv->clk = devm_clk_get(dev, "phy");
0196     if (IS_ERR(drv->clk)) {
0197         dev_err(dev, "Failed to get clock of phy controller\n");
0198         return PTR_ERR(drv->clk);
0199     }
0200 
0201     drv->ref_clk = devm_clk_get(dev, "ref");
0202     if (IS_ERR(drv->ref_clk)) {
0203         dev_err(dev, "Failed to get reference clock for the phy controller\n");
0204         return PTR_ERR(drv->ref_clk);
0205     }
0206 
0207     drv->ref_rate = clk_get_rate(drv->ref_clk);
0208     if (drv->cfg->rate_to_clk) {
0209         ret = drv->cfg->rate_to_clk(drv->ref_rate, &drv->ref_reg_val);
0210         if (ret)
0211             return ret;
0212     }
0213 
0214     drv->vbus = devm_regulator_get(dev, "vbus");
0215     if (IS_ERR(drv->vbus)) {
0216         ret = PTR_ERR(drv->vbus);
0217         if (ret == -EPROBE_DEFER)
0218             return ret;
0219         drv->vbus = NULL;
0220     }
0221 
0222     for (i = 0; i < drv->cfg->num_phys; i++) {
0223         char *label = drv->cfg->phys[i].label;
0224         struct samsung_usb2_phy_instance *p = &drv->instances[i];
0225 
0226         dev_dbg(dev, "Creating phy \"%s\"\n", label);
0227         p->phy = devm_phy_create(dev, NULL, &samsung_usb2_phy_ops);
0228         if (IS_ERR(p->phy)) {
0229             dev_err(drv->dev, "Failed to create usb2_phy \"%s\"\n",
0230                 label);
0231             return PTR_ERR(p->phy);
0232         }
0233 
0234         p->cfg = &drv->cfg->phys[i];
0235         p->drv = drv;
0236         phy_set_bus_width(p->phy, 8);
0237         phy_set_drvdata(p->phy, p);
0238     }
0239 
0240     phy_provider = devm_of_phy_provider_register(dev,
0241                             samsung_usb2_phy_xlate);
0242     if (IS_ERR(phy_provider)) {
0243         dev_err(drv->dev, "Failed to register phy provider\n");
0244         return PTR_ERR(phy_provider);
0245     }
0246 
0247     return 0;
0248 }
0249 
0250 static struct platform_driver samsung_usb2_phy_driver = {
0251     .probe  = samsung_usb2_phy_probe,
0252     .driver = {
0253         .of_match_table = samsung_usb2_phy_of_match,
0254         .name       = "samsung-usb2-phy",
0255         .suppress_bind_attrs = true,
0256     }
0257 };
0258 
0259 module_platform_driver(samsung_usb2_phy_driver);
0260 MODULE_DESCRIPTION("Samsung S5P/Exynos SoC USB PHY driver");
0261 MODULE_AUTHOR("Kamil Debski <k.debski@samsung.com>");
0262 MODULE_LICENSE("GPL v2");
0263 MODULE_ALIAS("platform:samsung-usb2-phy");