dwc3-exynos.c 5.2 KB
Newer Older
1 2 3 4 5 6 7 8
/**
 * dwc3-exynos.c - Samsung EXYNOS DWC3 Specific Glue layer
 *
 * Copyright (c) 2012 Samsung Electronics Co., Ltd.
 *		http://www.samsung.com
 *
 * Author: Anton Tikhomirov <av.tikhomirov@samsung.com>
 *
F
Felipe Balbi 已提交
9 10 11 12 13 14 15 16
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2  of
 * the License 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.
17 18 19 20 21 22 23 24 25
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/platform_data/dwc3-exynos.h>
#include <linux/dma-mapping.h>
#include <linux/clk.h>
26
#include <linux/usb/otg.h>
27
#include <linux/usb/usb_phy_gen_xceiv.h>
28
#include <linux/of.h>
29
#include <linux/of_platform.h>
30 31

struct dwc3_exynos {
32 33
	struct platform_device	*usb2_phy;
	struct platform_device	*usb3_phy;
34 35 36 37 38
	struct device		*dev;

	struct clk		*clk;
};

B
Bill Pemberton 已提交
39
static int dwc3_exynos_register_phys(struct dwc3_exynos *exynos)
40
{
41
	struct usb_phy_gen_xceiv_platform_data pdata;
42 43 44 45 46
	struct platform_device	*pdev;
	int			ret;

	memset(&pdata, 0x00, sizeof(pdata));

47
	pdev = platform_device_alloc("usb_phy_gen_xceiv", PLATFORM_DEVID_AUTO);
48 49 50 51 52
	if (!pdev)
		return -ENOMEM;

	exynos->usb2_phy = pdev;
	pdata.type = USB_PHY_TYPE_USB2;
53
	pdata.gpio_reset = -1;
54 55 56 57 58

	ret = platform_device_add_data(exynos->usb2_phy, &pdata, sizeof(pdata));
	if (ret)
		goto err1;

59
	pdev = platform_device_alloc("usb_phy_gen_xceiv", PLATFORM_DEVID_AUTO);
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
	if (!pdev) {
		ret = -ENOMEM;
		goto err1;
	}

	exynos->usb3_phy = pdev;
	pdata.type = USB_PHY_TYPE_USB3;

	ret = platform_device_add_data(exynos->usb3_phy, &pdata, sizeof(pdata));
	if (ret)
		goto err2;

	ret = platform_device_add(exynos->usb2_phy);
	if (ret)
		goto err2;

	ret = platform_device_add(exynos->usb3_phy);
	if (ret)
		goto err3;

	return 0;

err3:
	platform_device_del(exynos->usb2_phy);

err2:
	platform_device_put(exynos->usb3_phy);

err1:
	platform_device_put(exynos->usb2_phy);

	return ret;
}

94 95 96 97 98 99 100 101 102
static int dwc3_exynos_remove_child(struct device *dev, void *unused)
{
	struct platform_device *pdev = to_platform_device(dev);

	platform_device_unregister(pdev);

	return 0;
}

B
Bill Pemberton 已提交
103
static int dwc3_exynos_probe(struct platform_device *pdev)
104 105 106
{
	struct dwc3_exynos	*exynos;
	struct clk		*clk;
107
	struct device		*dev = &pdev->dev;
108
	struct device_node	*node = dev->of_node;
109 110 111

	int			ret = -ENOMEM;

112
	exynos = devm_kzalloc(dev, sizeof(*exynos), GFP_KERNEL);
113
	if (!exynos) {
114
		dev_err(dev, "not enough memory\n");
115
		goto err1;
116 117
	}

118 119 120 121 122
	/*
	 * Right now device-tree probed devices don't get dma_mask set.
	 * Since shared usb code relies on it, set it here for now.
	 * Once we move to full device tree support this will vanish off.
	 */
123
	ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32));
124 125
	if (ret)
		goto err1;
126

127 128
	platform_set_drvdata(pdev, exynos);

129 130
	ret = dwc3_exynos_register_phys(exynos);
	if (ret) {
131
		dev_err(dev, "couldn't register PHYs\n");
132
		goto err1;
133 134
	}

135
	clk = devm_clk_get(dev, "usbdrd30");
136
	if (IS_ERR(clk)) {
137
		dev_err(dev, "couldn't get clock\n");
138
		ret = -EINVAL;
139
		goto err1;
140 141
	}

142
	exynos->dev	= dev;
143 144
	exynos->clk	= clk;

145
	clk_prepare_enable(exynos->clk);
146

147 148 149 150 151 152 153 154 155
	if (node) {
		ret = of_platform_populate(node, NULL, NULL, dev);
		if (ret) {
			dev_err(dev, "failed to add dwc3 core\n");
			goto err2;
		}
	} else {
		dev_err(dev, "no device node, failed to add dwc3 core\n");
		ret = -ENODEV;
156
		goto err2;
157 158 159 160
	}

	return 0;

161
err2:
162
	clk_disable_unprepare(clk);
163 164 165 166
err1:
	return ret;
}

B
Bill Pemberton 已提交
167
static int dwc3_exynos_remove(struct platform_device *pdev)
168 169 170
{
	struct dwc3_exynos	*exynos = platform_get_drvdata(pdev);

171
	device_for_each_child(&pdev->dev, NULL, dwc3_exynos_remove_child);
172 173
	platform_device_unregister(exynos->usb2_phy);
	platform_device_unregister(exynos->usb3_phy);
174

175
	clk_disable_unprepare(exynos->clk);
176 177 178 179

	return 0;
}

180 181
#ifdef CONFIG_OF
static const struct of_device_id exynos_dwc3_match[] = {
182
	{ .compatible = "samsung,exynos5250-dwusb3" },
183 184 185 186 187
	{},
};
MODULE_DEVICE_TABLE(of, exynos_dwc3_match);
#endif

188
#ifdef CONFIG_PM_SLEEP
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
static int dwc3_exynos_suspend(struct device *dev)
{
	struct dwc3_exynos *exynos = dev_get_drvdata(dev);

	clk_disable(exynos->clk);

	return 0;
}

static int dwc3_exynos_resume(struct device *dev)
{
	struct dwc3_exynos *exynos = dev_get_drvdata(dev);

	clk_enable(exynos->clk);

	/* runtime set active to reflect active state. */
	pm_runtime_disable(dev);
	pm_runtime_set_active(dev);
	pm_runtime_enable(dev);

	return 0;
}

static const struct dev_pm_ops dwc3_exynos_dev_pm_ops = {
	SET_SYSTEM_SLEEP_PM_OPS(dwc3_exynos_suspend, dwc3_exynos_resume)
};

#define DEV_PM_OPS	(&dwc3_exynos_dev_pm_ops)
#else
#define DEV_PM_OPS	NULL
219
#endif /* CONFIG_PM_SLEEP */
220

221 222
static struct platform_driver dwc3_exynos_driver = {
	.probe		= dwc3_exynos_probe,
B
Bill Pemberton 已提交
223
	.remove		= dwc3_exynos_remove,
224 225
	.driver		= {
		.name	= "exynos-dwc3",
226
		.of_match_table = of_match_ptr(exynos_dwc3_match),
227
		.pm	= DEV_PM_OPS,
228 229 230 231 232 233 234
	},
};

module_platform_driver(dwc3_exynos_driver);

MODULE_ALIAS("platform:exynos-dwc3");
MODULE_AUTHOR("Anton Tikhomirov <av.tikhomirov@samsung.com>");
F
Felipe Balbi 已提交
235
MODULE_LICENSE("GPL v2");
236
MODULE_DESCRIPTION("DesignWare USB3 EXYNOS Glue Layer");