1 /* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. 2 * 3 * This program is free software; you can redistribute it and/or modify 4 * it under the terms of the GNU General Public License version 2 and 5 * only version 2 as published by the Free Software Foundation. 6 * 7 * This program is distributed in the hope that it will be useful, 8 * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 * GNU General Public License for more details. 11 */ 12 13 #include <linux/clk.h> 14 #include <linux/err.h> 15 #include <linux/io.h> 16 #include <linux/module.h> 17 #include <linux/of.h> 18 #include <linux/of_platform.h> 19 #include <linux/platform_device.h> 20 21 struct dwc3_qcom { 22 struct device *dev; 23 24 struct clk *core_clk; 25 struct clk *iface_clk; 26 struct clk *sleep_clk; 27 }; 28 29 static int dwc3_qcom_probe(struct platform_device *pdev) 30 { 31 struct device_node *node = pdev->dev.of_node; 32 struct dwc3_qcom *qdwc; 33 int ret; 34 35 qdwc = devm_kzalloc(&pdev->dev, sizeof(*qdwc), GFP_KERNEL); 36 if (!qdwc) 37 return -ENOMEM; 38 39 platform_set_drvdata(pdev, qdwc); 40 41 qdwc->dev = &pdev->dev; 42 43 qdwc->core_clk = devm_clk_get(qdwc->dev, "core"); 44 if (IS_ERR(qdwc->core_clk)) { 45 dev_err(qdwc->dev, "failed to get core clock\n"); 46 return PTR_ERR(qdwc->core_clk); 47 } 48 49 qdwc->iface_clk = devm_clk_get(qdwc->dev, "iface"); 50 if (IS_ERR(qdwc->iface_clk)) { 51 dev_info(qdwc->dev, "failed to get optional iface clock\n"); 52 qdwc->iface_clk = NULL; 53 } 54 55 qdwc->sleep_clk = devm_clk_get(qdwc->dev, "sleep"); 56 if (IS_ERR(qdwc->sleep_clk)) { 57 dev_info(qdwc->dev, "failed to get optional sleep clock\n"); 58 qdwc->sleep_clk = NULL; 59 } 60 61 ret = clk_prepare_enable(qdwc->core_clk); 62 if (ret) { 63 dev_err(qdwc->dev, "failed to enable core clock\n"); 64 goto err_core; 65 } 66 67 ret = clk_prepare_enable(qdwc->iface_clk); 68 if (ret) { 69 dev_err(qdwc->dev, "failed to enable optional iface clock\n"); 70 goto err_iface; 71 } 72 73 ret = clk_prepare_enable(qdwc->sleep_clk); 74 if (ret) { 75 dev_err(qdwc->dev, "failed to enable optional sleep clock\n"); 76 goto err_sleep; 77 } 78 79 ret = of_platform_populate(node, NULL, NULL, qdwc->dev); 80 if (ret) { 81 dev_err(qdwc->dev, "failed to register core - %d\n", ret); 82 goto err_clks; 83 } 84 85 return 0; 86 87 err_clks: 88 clk_disable_unprepare(qdwc->sleep_clk); 89 err_sleep: 90 clk_disable_unprepare(qdwc->iface_clk); 91 err_iface: 92 clk_disable_unprepare(qdwc->core_clk); 93 err_core: 94 return ret; 95 } 96 97 static int dwc3_qcom_remove(struct platform_device *pdev) 98 { 99 struct dwc3_qcom *qdwc = platform_get_drvdata(pdev); 100 101 of_platform_depopulate(&pdev->dev); 102 103 clk_disable_unprepare(qdwc->sleep_clk); 104 clk_disable_unprepare(qdwc->iface_clk); 105 clk_disable_unprepare(qdwc->core_clk); 106 107 return 0; 108 } 109 110 static const struct of_device_id of_dwc3_match[] = { 111 { .compatible = "qcom,dwc3" }, 112 { /* Sentinel */ } 113 }; 114 MODULE_DEVICE_TABLE(of, of_dwc3_match); 115 116 static struct platform_driver dwc3_qcom_driver = { 117 .probe = dwc3_qcom_probe, 118 .remove = dwc3_qcom_remove, 119 .driver = { 120 .name = "qcom-dwc3", 121 .of_match_table = of_dwc3_match, 122 }, 123 }; 124 125 module_platform_driver(dwc3_qcom_driver); 126 127 MODULE_ALIAS("platform:qcom-dwc3"); 128 MODULE_LICENSE("GPL v2"); 129 MODULE_DESCRIPTION("DesignWare USB3 QCOM Glue Layer"); 130 MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>"); 131