/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include <linux/clk.h> #include <linux/err.h> #include <linux/io.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_platform.h> #include <linux/platform_device.h> struct dwc3_qcom { struct device *dev; struct clk *core_clk; struct clk *iface_clk; struct clk *sleep_clk; }; static int dwc3_qcom_probe(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; struct dwc3_qcom *qdwc; int ret; qdwc = devm_kzalloc(&pdev->dev, sizeof(*qdwc), GFP_KERNEL); if (!qdwc) return -ENOMEM; platform_set_drvdata(pdev, qdwc); qdwc->dev = &pdev->dev; qdwc->core_clk = devm_clk_get(qdwc->dev, "core"); if (IS_ERR(qdwc->core_clk)) { dev_err(qdwc->dev, "failed to get core clock\n"); return PTR_ERR(qdwc->core_clk); } qdwc->iface_clk = devm_clk_get(qdwc->dev, "iface"); if (IS_ERR(qdwc->iface_clk)) { dev_dbg(qdwc->dev, "failed to get optional iface clock\n"); qdwc->iface_clk = NULL; } qdwc->sleep_clk = devm_clk_get(qdwc->dev, "sleep"); if (IS_ERR(qdwc->sleep_clk)) { dev_dbg(qdwc->dev, "failed to get optional sleep clock\n"); qdwc->sleep_clk = NULL; } ret = clk_prepare_enable(qdwc->core_clk); if (ret) { dev_err(qdwc->dev, "failed to enable core clock\n"); goto err_core; } ret = clk_prepare_enable(qdwc->iface_clk); if (ret) { dev_err(qdwc->dev, "failed to enable optional iface clock\n"); goto err_iface; } ret = clk_prepare_enable(qdwc->sleep_clk); if (ret) { dev_err(qdwc->dev, "failed to enable optional sleep clock\n"); goto err_sleep; } ret = of_platform_populate(node, NULL, NULL, qdwc->dev); if (ret) { dev_err(qdwc->dev, "failed to register core - %d\n", ret); goto err_clks; } return 0; err_clks: clk_disable_unprepare(qdwc->sleep_clk); err_sleep: clk_disable_unprepare(qdwc->iface_clk); err_iface: clk_disable_unprepare(qdwc->core_clk); err_core: return ret; } static int dwc3_qcom_remove(struct platform_device *pdev) { struct dwc3_qcom *qdwc = platform_get_drvdata(pdev); of_platform_depopulate(&pdev->dev); clk_disable_unprepare(qdwc->sleep_clk); clk_disable_unprepare(qdwc->iface_clk); clk_disable_unprepare(qdwc->core_clk); return 0; } static const struct of_device_id of_dwc3_match[] = { { .compatible = "qcom,dwc3" }, { /* Sentinel */ } }; MODULE_DEVICE_TABLE(of, of_dwc3_match); static struct platform_driver dwc3_qcom_driver = { .probe = dwc3_qcom_probe, .remove = dwc3_qcom_remove, .driver = { .name = "qcom-dwc3", .of_match_table = of_dwc3_match, }, }; module_platform_driver(dwc3_qcom_driver); MODULE_ALIAS("platform:qcom-dwc3"); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("DesignWare USB3 QCOM Glue Layer"); MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>");