dwc3-of-simple.c 4.8 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/**
 * dwc3-of-simple.c - OF glue layer for simple integrations
 *
 * Copyright (c) 2015 Texas Instruments Incorporated - http://www.ti.com
 *
 * Author: Felipe Balbi <balbi@ti.com>
 *
 * This is a combination of the old dwc3-qcom.c by Ivan T. Ivanov
 * <iivanov@mm-sol.com> and the original patch adding support for Xilinx' SoC
 * by Subbaraya Sundeep Bhatta <subbaraya.sundeep.bhatta@xilinx.com>
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/clk.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/pm_runtime.h>
23
#include <linux/reset.h>
24 25 26 27 28

struct dwc3_of_simple {
	struct device		*dev;
	struct clk		**clks;
	int			num_clocks;
29
	struct reset_control	*resets;
30 31
};

32
static int dwc3_of_simple_clk_init(struct dwc3_of_simple *simple, int count)
33
{
34
	struct device		*dev = simple->dev;
35 36 37
	struct device_node	*np = dev->of_node;
	int			i;

38
	simple->num_clocks = count;
39

40
	if (!count)
41
		return 0;
42 43 44 45 46 47 48 49

	simple->clks = devm_kcalloc(dev, simple->num_clocks,
			sizeof(struct clk *), GFP_KERNEL);
	if (!simple->clks)
		return -ENOMEM;

	for (i = 0; i < simple->num_clocks; i++) {
		struct clk	*clk;
50
		int		ret;
51 52 53

		clk = of_clk_get(np, i);
		if (IS_ERR(clk)) {
54 55
			while (--i >= 0) {
				clk_disable_unprepare(simple->clks[i]);
56
				clk_put(simple->clks[i]);
57
			}
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
			return PTR_ERR(clk);
		}

		ret = clk_prepare_enable(clk);
		if (ret < 0) {
			while (--i >= 0) {
				clk_disable_unprepare(simple->clks[i]);
				clk_put(simple->clks[i]);
			}
			clk_put(clk);

			return ret;
		}

		simple->clks[i] = clk;
	}

75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
	return 0;
}

static int dwc3_of_simple_probe(struct platform_device *pdev)
{
	struct dwc3_of_simple	*simple;
	struct device		*dev = &pdev->dev;
	struct device_node	*np = dev->of_node;

	int			ret;
	int			i;

	simple = devm_kzalloc(dev, sizeof(*simple), GFP_KERNEL);
	if (!simple)
		return -ENOMEM;

	platform_set_drvdata(pdev, simple);
	simple->dev = dev;

94 95 96 97 98 99 100 101 102 103 104
	simple->resets = of_reset_control_array_get_optional_exclusive(np);
	if (IS_ERR(simple->resets)) {
		ret = PTR_ERR(simple->resets);
		dev_err(dev, "failed to get device resets, err=%d\n", ret);
		return ret;
	}

	ret = reset_control_deassert(simple->resets);
	if (ret)
		goto err_resetc_put;

105 106
	ret = dwc3_of_simple_clk_init(simple, of_count_phandle_with_args(np,
						"clocks", "#clock-cells"));
107
	if (ret)
108
		goto err_resetc_assert;
109

110 111 112 113 114 115 116
	ret = of_platform_populate(np, NULL, NULL, dev);
	if (ret) {
		for (i = 0; i < simple->num_clocks; i++) {
			clk_disable_unprepare(simple->clks[i]);
			clk_put(simple->clks[i]);
		}

117
		goto err_resetc_assert;
118 119 120 121 122 123 124
	}

	pm_runtime_set_active(dev);
	pm_runtime_enable(dev);
	pm_runtime_get_sync(dev);

	return 0;
125 126 127 128 129 130 131

err_resetc_assert:
	reset_control_assert(simple->resets);

err_resetc_put:
	reset_control_put(simple->resets);
	return ret;
132 133 134 135 136 137 138 139
}

static int dwc3_of_simple_remove(struct platform_device *pdev)
{
	struct dwc3_of_simple	*simple = platform_get_drvdata(pdev);
	struct device		*dev = &pdev->dev;
	int			i;

140 141
	of_platform_depopulate(dev);

142
	for (i = 0; i < simple->num_clocks; i++) {
143
		clk_disable_unprepare(simple->clks[i]);
144 145
		clk_put(simple->clks[i]);
	}
146
	simple->num_clocks = 0;
147

148 149 150
	reset_control_assert(simple->resets);
	reset_control_put(simple->resets);

151 152 153 154 155 156
	pm_runtime_put_sync(dev);
	pm_runtime_disable(dev);

	return 0;
}

157
#ifdef CONFIG_PM
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
static int dwc3_of_simple_runtime_suspend(struct device *dev)
{
	struct dwc3_of_simple	*simple = dev_get_drvdata(dev);
	int			i;

	for (i = 0; i < simple->num_clocks; i++)
		clk_disable(simple->clks[i]);

	return 0;
}

static int dwc3_of_simple_runtime_resume(struct device *dev)
{
	struct dwc3_of_simple	*simple = dev_get_drvdata(dev);
	int			ret;
	int			i;

	for (i = 0; i < simple->num_clocks; i++) {
		ret = clk_enable(simple->clks[i]);
		if (ret < 0) {
			while (--i >= 0)
				clk_disable(simple->clks[i]);
			return ret;
		}
	}

	return 0;
}
186
#endif
187 188 189 190 191 192 193 194

static const struct dev_pm_ops dwc3_of_simple_dev_pm_ops = {
	SET_RUNTIME_PM_OPS(dwc3_of_simple_runtime_suspend,
			dwc3_of_simple_runtime_resume, NULL)
};

static const struct of_device_id of_dwc3_simple_match[] = {
	{ .compatible = "qcom,dwc3" },
195
	{ .compatible = "rockchip,rk3399-dwc3" },
196
	{ .compatible = "xlnx,zynqmp-dwc3" },
197
	{ .compatible = "cavium,octeon-7130-usb-uctl" },
198
	{ .compatible = "sprd,sc9860-dwc3" },
199 200 201 202 203 204 205 206 207 208
	{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, of_dwc3_simple_match);

static struct platform_driver dwc3_of_simple_driver = {
	.probe		= dwc3_of_simple_probe,
	.remove		= dwc3_of_simple_remove,
	.driver		= {
		.name	= "dwc3-of-simple",
		.of_match_table = of_dwc3_simple_match,
209
		.pm	= &dwc3_of_simple_dev_pm_ops,
210 211 212 213 214 215 216
	},
};

module_platform_driver(dwc3_of_simple_driver);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("DesignWare USB3 OF Simple Glue Layer");
MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");