18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/** 38c2ecf20Sopenharmony_ci * dwc3-exynos.c - Samsung Exynos DWC3 Specific Glue layer 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2012 Samsung Electronics Co., Ltd. 68c2ecf20Sopenharmony_ci * http://www.samsung.com 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Author: Anton Tikhomirov <av.tikhomirov@samsung.com> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 158c2ecf20Sopenharmony_ci#include <linux/clk.h> 168c2ecf20Sopenharmony_ci#include <linux/of.h> 178c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 188c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define DWC3_EXYNOS_MAX_CLOCKS 4 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistruct dwc3_exynos_driverdata { 238c2ecf20Sopenharmony_ci const char *clk_names[DWC3_EXYNOS_MAX_CLOCKS]; 248c2ecf20Sopenharmony_ci int num_clks; 258c2ecf20Sopenharmony_ci int suspend_clk_idx; 268c2ecf20Sopenharmony_ci}; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistruct dwc3_exynos { 298c2ecf20Sopenharmony_ci struct device *dev; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci const char **clk_names; 328c2ecf20Sopenharmony_ci struct clk *clks[DWC3_EXYNOS_MAX_CLOCKS]; 338c2ecf20Sopenharmony_ci int num_clks; 348c2ecf20Sopenharmony_ci int suspend_clk_idx; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci struct regulator *vdd33; 378c2ecf20Sopenharmony_ci struct regulator *vdd10; 388c2ecf20Sopenharmony_ci}; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic int dwc3_exynos_probe(struct platform_device *pdev) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci struct dwc3_exynos *exynos; 438c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 448c2ecf20Sopenharmony_ci struct device_node *node = dev->of_node; 458c2ecf20Sopenharmony_ci const struct dwc3_exynos_driverdata *driver_data; 468c2ecf20Sopenharmony_ci int i, ret; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci exynos = devm_kzalloc(dev, sizeof(*exynos), GFP_KERNEL); 498c2ecf20Sopenharmony_ci if (!exynos) 508c2ecf20Sopenharmony_ci return -ENOMEM; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci driver_data = of_device_get_match_data(dev); 538c2ecf20Sopenharmony_ci exynos->dev = dev; 548c2ecf20Sopenharmony_ci exynos->num_clks = driver_data->num_clks; 558c2ecf20Sopenharmony_ci exynos->clk_names = (const char **)driver_data->clk_names; 568c2ecf20Sopenharmony_ci exynos->suspend_clk_idx = driver_data->suspend_clk_idx; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, exynos); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci for (i = 0; i < exynos->num_clks; i++) { 618c2ecf20Sopenharmony_ci exynos->clks[i] = devm_clk_get(dev, exynos->clk_names[i]); 628c2ecf20Sopenharmony_ci if (IS_ERR(exynos->clks[i])) { 638c2ecf20Sopenharmony_ci dev_err(dev, "failed to get clock: %s\n", 648c2ecf20Sopenharmony_ci exynos->clk_names[i]); 658c2ecf20Sopenharmony_ci return PTR_ERR(exynos->clks[i]); 668c2ecf20Sopenharmony_ci } 678c2ecf20Sopenharmony_ci } 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci for (i = 0; i < exynos->num_clks; i++) { 708c2ecf20Sopenharmony_ci ret = clk_prepare_enable(exynos->clks[i]); 718c2ecf20Sopenharmony_ci if (ret) { 728c2ecf20Sopenharmony_ci while (i-- > 0) 738c2ecf20Sopenharmony_ci clk_disable_unprepare(exynos->clks[i]); 748c2ecf20Sopenharmony_ci return ret; 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci if (exynos->suspend_clk_idx >= 0) 798c2ecf20Sopenharmony_ci clk_prepare_enable(exynos->clks[exynos->suspend_clk_idx]); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci exynos->vdd33 = devm_regulator_get(dev, "vdd33"); 828c2ecf20Sopenharmony_ci if (IS_ERR(exynos->vdd33)) { 838c2ecf20Sopenharmony_ci ret = PTR_ERR(exynos->vdd33); 848c2ecf20Sopenharmony_ci goto vdd33_err; 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ci ret = regulator_enable(exynos->vdd33); 878c2ecf20Sopenharmony_ci if (ret) { 888c2ecf20Sopenharmony_ci dev_err(dev, "Failed to enable VDD33 supply\n"); 898c2ecf20Sopenharmony_ci goto vdd33_err; 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci exynos->vdd10 = devm_regulator_get(dev, "vdd10"); 938c2ecf20Sopenharmony_ci if (IS_ERR(exynos->vdd10)) { 948c2ecf20Sopenharmony_ci ret = PTR_ERR(exynos->vdd10); 958c2ecf20Sopenharmony_ci goto vdd10_err; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci ret = regulator_enable(exynos->vdd10); 988c2ecf20Sopenharmony_ci if (ret) { 998c2ecf20Sopenharmony_ci dev_err(dev, "Failed to enable VDD10 supply\n"); 1008c2ecf20Sopenharmony_ci goto vdd10_err; 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci if (node) { 1048c2ecf20Sopenharmony_ci ret = of_platform_populate(node, NULL, NULL, dev); 1058c2ecf20Sopenharmony_ci if (ret) { 1068c2ecf20Sopenharmony_ci dev_err(dev, "failed to add dwc3 core\n"); 1078c2ecf20Sopenharmony_ci goto populate_err; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci } else { 1108c2ecf20Sopenharmony_ci dev_err(dev, "no device node, failed to add dwc3 core\n"); 1118c2ecf20Sopenharmony_ci ret = -ENODEV; 1128c2ecf20Sopenharmony_ci goto populate_err; 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci return 0; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cipopulate_err: 1188c2ecf20Sopenharmony_ci regulator_disable(exynos->vdd10); 1198c2ecf20Sopenharmony_civdd10_err: 1208c2ecf20Sopenharmony_ci regulator_disable(exynos->vdd33); 1218c2ecf20Sopenharmony_civdd33_err: 1228c2ecf20Sopenharmony_ci for (i = exynos->num_clks - 1; i >= 0; i--) 1238c2ecf20Sopenharmony_ci clk_disable_unprepare(exynos->clks[i]); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (exynos->suspend_clk_idx >= 0) 1268c2ecf20Sopenharmony_ci clk_disable_unprepare(exynos->clks[exynos->suspend_clk_idx]); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci return ret; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic int dwc3_exynos_remove(struct platform_device *pdev) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci struct dwc3_exynos *exynos = platform_get_drvdata(pdev); 1348c2ecf20Sopenharmony_ci int i; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci of_platform_depopulate(&pdev->dev); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci for (i = exynos->num_clks - 1; i >= 0; i--) 1398c2ecf20Sopenharmony_ci clk_disable_unprepare(exynos->clks[i]); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci if (exynos->suspend_clk_idx >= 0) 1428c2ecf20Sopenharmony_ci clk_disable_unprepare(exynos->clks[exynos->suspend_clk_idx]); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci regulator_disable(exynos->vdd33); 1458c2ecf20Sopenharmony_ci regulator_disable(exynos->vdd10); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci return 0; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic const struct dwc3_exynos_driverdata exynos5250_drvdata = { 1518c2ecf20Sopenharmony_ci .clk_names = { "usbdrd30" }, 1528c2ecf20Sopenharmony_ci .num_clks = 1, 1538c2ecf20Sopenharmony_ci .suspend_clk_idx = -1, 1548c2ecf20Sopenharmony_ci}; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic const struct dwc3_exynos_driverdata exynos5433_drvdata = { 1578c2ecf20Sopenharmony_ci .clk_names = { "aclk", "susp_clk", "pipe_pclk", "phyclk" }, 1588c2ecf20Sopenharmony_ci .num_clks = 4, 1598c2ecf20Sopenharmony_ci .suspend_clk_idx = 1, 1608c2ecf20Sopenharmony_ci}; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic const struct dwc3_exynos_driverdata exynos7_drvdata = { 1638c2ecf20Sopenharmony_ci .clk_names = { "usbdrd30", "usbdrd30_susp_clk", "usbdrd30_axius_clk" }, 1648c2ecf20Sopenharmony_ci .num_clks = 3, 1658c2ecf20Sopenharmony_ci .suspend_clk_idx = 1, 1668c2ecf20Sopenharmony_ci}; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic const struct of_device_id exynos_dwc3_match[] = { 1698c2ecf20Sopenharmony_ci { 1708c2ecf20Sopenharmony_ci .compatible = "samsung,exynos5250-dwusb3", 1718c2ecf20Sopenharmony_ci .data = &exynos5250_drvdata, 1728c2ecf20Sopenharmony_ci }, { 1738c2ecf20Sopenharmony_ci .compatible = "samsung,exynos5433-dwusb3", 1748c2ecf20Sopenharmony_ci .data = &exynos5433_drvdata, 1758c2ecf20Sopenharmony_ci }, { 1768c2ecf20Sopenharmony_ci .compatible = "samsung,exynos7-dwusb3", 1778c2ecf20Sopenharmony_ci .data = &exynos7_drvdata, 1788c2ecf20Sopenharmony_ci }, { 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci}; 1818c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, exynos_dwc3_match); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 1848c2ecf20Sopenharmony_cistatic int dwc3_exynos_suspend(struct device *dev) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci struct dwc3_exynos *exynos = dev_get_drvdata(dev); 1878c2ecf20Sopenharmony_ci int i; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci for (i = exynos->num_clks - 1; i >= 0; i--) 1908c2ecf20Sopenharmony_ci clk_disable_unprepare(exynos->clks[i]); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci regulator_disable(exynos->vdd33); 1938c2ecf20Sopenharmony_ci regulator_disable(exynos->vdd10); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci return 0; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic int dwc3_exynos_resume(struct device *dev) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci struct dwc3_exynos *exynos = dev_get_drvdata(dev); 2018c2ecf20Sopenharmony_ci int i, ret; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci ret = regulator_enable(exynos->vdd33); 2048c2ecf20Sopenharmony_ci if (ret) { 2058c2ecf20Sopenharmony_ci dev_err(dev, "Failed to enable VDD33 supply\n"); 2068c2ecf20Sopenharmony_ci return ret; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci ret = regulator_enable(exynos->vdd10); 2098c2ecf20Sopenharmony_ci if (ret) { 2108c2ecf20Sopenharmony_ci dev_err(dev, "Failed to enable VDD10 supply\n"); 2118c2ecf20Sopenharmony_ci return ret; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci for (i = 0; i < exynos->num_clks; i++) { 2158c2ecf20Sopenharmony_ci ret = clk_prepare_enable(exynos->clks[i]); 2168c2ecf20Sopenharmony_ci if (ret) { 2178c2ecf20Sopenharmony_ci while (i-- > 0) 2188c2ecf20Sopenharmony_ci clk_disable_unprepare(exynos->clks[i]); 2198c2ecf20Sopenharmony_ci return ret; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci return 0; 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic const struct dev_pm_ops dwc3_exynos_dev_pm_ops = { 2278c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(dwc3_exynos_suspend, dwc3_exynos_resume) 2288c2ecf20Sopenharmony_ci}; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci#define DEV_PM_OPS (&dwc3_exynos_dev_pm_ops) 2318c2ecf20Sopenharmony_ci#else 2328c2ecf20Sopenharmony_ci#define DEV_PM_OPS NULL 2338c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic struct platform_driver dwc3_exynos_driver = { 2368c2ecf20Sopenharmony_ci .probe = dwc3_exynos_probe, 2378c2ecf20Sopenharmony_ci .remove = dwc3_exynos_remove, 2388c2ecf20Sopenharmony_ci .driver = { 2398c2ecf20Sopenharmony_ci .name = "exynos-dwc3", 2408c2ecf20Sopenharmony_ci .of_match_table = exynos_dwc3_match, 2418c2ecf20Sopenharmony_ci .pm = DEV_PM_OPS, 2428c2ecf20Sopenharmony_ci }, 2438c2ecf20Sopenharmony_ci}; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cimodule_platform_driver(dwc3_exynos_driver); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ciMODULE_AUTHOR("Anton Tikhomirov <av.tikhomirov@samsung.com>"); 2488c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 2498c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("DesignWare USB3 Exynos Glue Layer"); 250