linux/drivers/usb/dwc3/dwc3-of-simple.c
<<
>>
Prefs
   1/**
   2 * dwc3-of-simple.c - OF glue layer for simple integrations
   3 *
   4 * Copyright (c) 2015 Texas Instruments Incorporated - http://www.ti.com
   5 *
   6 * Author: Felipe Balbi <balbi@ti.com>
   7 *
   8 * This program is free software: you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License version 2  of
  10 * the License as published by the Free Software Foundation.
  11 *
  12 * This program is distributed in the hope that it will be useful,
  13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15 * GNU General Public License for more details.
  16 *
  17 * This is a combination of the old dwc3-qcom.c by Ivan T. Ivanov
  18 * <iivanov@mm-sol.com> and the original patch adding support for Xilinx' SoC
  19 * by Subbaraya Sundeep Bhatta <subbaraya.sundeep.bhatta@xilinx.com>
  20 */
  21
  22#include <linux/module.h>
  23#include <linux/kernel.h>
  24#include <linux/slab.h>
  25#include <linux/platform_device.h>
  26#include <linux/dma-mapping.h>
  27#include <linux/clk.h>
  28#include <linux/clk-provider.h>
  29#include <linux/of.h>
  30#include <linux/of_platform.h>
  31#include <linux/pm_runtime.h>
  32
  33struct dwc3_of_simple {
  34        struct device           *dev;
  35        struct clk              **clks;
  36        int                     num_clocks;
  37};
  38
  39static int dwc3_of_simple_probe(struct platform_device *pdev)
  40{
  41        struct dwc3_of_simple   *simple;
  42        struct device           *dev = &pdev->dev;
  43        struct device_node      *np = dev->of_node;
  44
  45        unsigned int            count;
  46        int                     ret;
  47        int                     i;
  48
  49        simple = devm_kzalloc(dev, sizeof(*simple), GFP_KERNEL);
  50        if (!simple)
  51                return -ENOMEM;
  52
  53        count = of_clk_get_parent_count(np);
  54        if (!count)
  55                return -ENOENT;
  56
  57        simple->num_clocks = count;
  58
  59        simple->clks = devm_kcalloc(dev, simple->num_clocks,
  60                        sizeof(struct clk *), GFP_KERNEL);
  61        if (!simple->clks)
  62                return -ENOMEM;
  63
  64        simple->dev = dev;
  65
  66        for (i = 0; i < simple->num_clocks; i++) {
  67                struct clk      *clk;
  68
  69                clk = of_clk_get(np, i);
  70                if (IS_ERR(clk)) {
  71                        while (--i >= 0)
  72                                clk_put(simple->clks[i]);
  73                        return PTR_ERR(clk);
  74                }
  75
  76                ret = clk_prepare_enable(clk);
  77                if (ret < 0) {
  78                        while (--i >= 0) {
  79                                clk_disable_unprepare(simple->clks[i]);
  80                                clk_put(simple->clks[i]);
  81                        }
  82                        clk_put(clk);
  83
  84                        return ret;
  85                }
  86
  87                simple->clks[i] = clk;
  88        }
  89
  90        ret = of_platform_populate(np, NULL, NULL, dev);
  91        if (ret) {
  92                for (i = 0; i < simple->num_clocks; i++) {
  93                        clk_disable_unprepare(simple->clks[i]);
  94                        clk_put(simple->clks[i]);
  95                }
  96
  97                return ret;
  98        }
  99
 100        platform_set_drvdata(pdev, simple);
 101
 102        pm_runtime_set_active(dev);
 103        pm_runtime_enable(dev);
 104        pm_runtime_get_sync(dev);
 105
 106        return 0;
 107}
 108
 109static int dwc3_of_simple_remove(struct platform_device *pdev)
 110{
 111        struct dwc3_of_simple   *simple = platform_get_drvdata(pdev);
 112        struct device           *dev = &pdev->dev;
 113        int                     i;
 114
 115        of_platform_depopulate(dev);
 116
 117        for (i = 0; i < simple->num_clocks; i++) {
 118                clk_disable_unprepare(simple->clks[i]);
 119                clk_put(simple->clks[i]);
 120        }
 121
 122        pm_runtime_put_sync(dev);
 123        pm_runtime_disable(dev);
 124
 125        return 0;
 126}
 127
 128#ifdef CONFIG_PM
 129static int dwc3_of_simple_runtime_suspend(struct device *dev)
 130{
 131        struct dwc3_of_simple   *simple = dev_get_drvdata(dev);
 132        int                     i;
 133
 134        for (i = 0; i < simple->num_clocks; i++)
 135                clk_disable(simple->clks[i]);
 136
 137        return 0;
 138}
 139
 140static int dwc3_of_simple_runtime_resume(struct device *dev)
 141{
 142        struct dwc3_of_simple   *simple = dev_get_drvdata(dev);
 143        int                     ret;
 144        int                     i;
 145
 146        for (i = 0; i < simple->num_clocks; i++) {
 147                ret = clk_enable(simple->clks[i]);
 148                if (ret < 0) {
 149                        while (--i >= 0)
 150                                clk_disable(simple->clks[i]);
 151                        return ret;
 152                }
 153        }
 154
 155        return 0;
 156}
 157#endif
 158
 159static const struct dev_pm_ops dwc3_of_simple_dev_pm_ops = {
 160        SET_RUNTIME_PM_OPS(dwc3_of_simple_runtime_suspend,
 161                        dwc3_of_simple_runtime_resume, NULL)
 162};
 163
 164static const struct of_device_id of_dwc3_simple_match[] = {
 165        { .compatible = "qcom,dwc3" },
 166        { .compatible = "xlnx,zynqmp-dwc3" },
 167        { /* Sentinel */ }
 168};
 169MODULE_DEVICE_TABLE(of, of_dwc3_simple_match);
 170
 171static struct platform_driver dwc3_of_simple_driver = {
 172        .probe          = dwc3_of_simple_probe,
 173        .remove         = dwc3_of_simple_remove,
 174        .driver         = {
 175                .name   = "dwc3-of-simple",
 176                .of_match_table = of_dwc3_simple_match,
 177        },
 178};
 179
 180module_platform_driver(dwc3_of_simple_driver);
 181MODULE_LICENSE("GPL v2");
 182MODULE_DESCRIPTION("DesignWare USB3 OF Simple Glue Layer");
 183MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
 184