ci_hdrc_imx.c 11.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
 * Copyright 2012 Freescale Semiconductor, Inc.
 * Copyright (C) 2012 Marek Vasut <marex@denx.de>
 * on behalf of DENX Software Engineering GmbH
 *
 * The code contained herein is licensed under the GNU General Public
 * License. You may obtain a copy of the GNU General Public License
 * Version 2 or later at the following locations:
 *
 * http://www.opensource.org/licenses/gpl-license.html
 * http://www.gnu.org/copyleft/gpl.html
 */

#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/dma-mapping.h>
#include <linux/usb/chipidea.h>
#include <linux/clk.h>

#include "ci.h"
24
#include "ci_hdrc_imx.h"
25

26 27
struct ci_hdrc_imx_platform_flag {
	unsigned int flags;
28
	bool runtime_pm;
29 30
};

31 32 33 34 35
static const struct ci_hdrc_imx_platform_flag imx23_usb_data = {
	.flags = CI_HDRC_TURN_VBUS_EARLY_ON |
		CI_HDRC_DISABLE_STREAMING,
};

36
static const struct ci_hdrc_imx_platform_flag imx27_usb_data = {
37
		CI_HDRC_DISABLE_STREAMING,
38 39 40
};

static const struct ci_hdrc_imx_platform_flag imx28_usb_data = {
41
	.flags = CI_HDRC_IMX28_WRITE_FIX |
42 43
		CI_HDRC_TURN_VBUS_EARLY_ON |
		CI_HDRC_DISABLE_STREAMING,
44 45
};

46
static const struct ci_hdrc_imx_platform_flag imx6q_usb_data = {
47
	.flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
48 49
		CI_HDRC_TURN_VBUS_EARLY_ON |
		CI_HDRC_DISABLE_STREAMING,
50 51 52
};

static const struct ci_hdrc_imx_platform_flag imx6sl_usb_data = {
53
	.flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
54 55
		CI_HDRC_TURN_VBUS_EARLY_ON |
		CI_HDRC_DISABLE_HOST_STREAMING,
56 57 58
};

static const struct ci_hdrc_imx_platform_flag imx6sx_usb_data = {
59
	.flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
60 61
		CI_HDRC_TURN_VBUS_EARLY_ON |
		CI_HDRC_DISABLE_HOST_STREAMING,
62 63
};

64 65 66 67 68
static const struct ci_hdrc_imx_platform_flag imx6ul_usb_data = {
	.flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
		CI_HDRC_TURN_VBUS_EARLY_ON,
};

69 70 71 72
static const struct ci_hdrc_imx_platform_flag imx7d_usb_data = {
	.flags = CI_HDRC_SUPPORTS_RUNTIME_PM,
};

73
static const struct of_device_id ci_hdrc_imx_dt_ids[] = {
74
	{ .compatible = "fsl,imx23-usb", .data = &imx23_usb_data},
75 76
	{ .compatible = "fsl,imx28-usb", .data = &imx28_usb_data},
	{ .compatible = "fsl,imx27-usb", .data = &imx27_usb_data},
77 78
	{ .compatible = "fsl,imx6q-usb", .data = &imx6q_usb_data},
	{ .compatible = "fsl,imx6sl-usb", .data = &imx6sl_usb_data},
79
	{ .compatible = "fsl,imx6sx-usb", .data = &imx6sx_usb_data},
80
	{ .compatible = "fsl,imx6ul-usb", .data = &imx6ul_usb_data},
81
	{ .compatible = "fsl,imx7d-usb", .data = &imx7d_usb_data},
82 83 84 85
	{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids);

86
struct ci_hdrc_imx_data {
87 88 89
	struct usb_phy *phy;
	struct platform_device *ci_pdev;
	struct clk *clk;
90
	struct imx_usbmisc_data *usbmisc_data;
91 92
	bool supports_runtime_pm;
	bool in_lpm;
93 94 95 96 97 98
	/* SoC before i.mx6 (except imx23/imx28) needs three clks */
	bool need_three_clks;
	struct clk *clk_ipg;
	struct clk *clk_ahb;
	struct clk *clk_per;
	/* --------------------------------- */
99 100
};

101 102
/* Common functions shared by usbmisc drivers */

103
static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev)
104
{
105
	struct platform_device *misc_pdev;
106 107
	struct device_node *np = dev->of_node;
	struct of_phandle_args args;
108
	struct imx_usbmisc_data *data;
109 110
	int ret;

111 112 113 114 115 116 117 118 119 120
	/*
	 * In case the fsl,usbmisc property is not present this device doesn't
	 * need usbmisc. Return NULL (which is no error here)
	 */
	if (!of_get_property(np, "fsl,usbmisc", NULL))
		return NULL;

	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
	if (!data)
		return ERR_PTR(-ENOMEM);
121 122 123 124 125 126

	ret = of_parse_phandle_with_args(np, "fsl,usbmisc", "#index-cells",
					0, &args);
	if (ret) {
		dev_err(dev, "Failed to parse property fsl,usbmisc, errno %d\n",
			ret);
127
		return ERR_PTR(ret);
128
	}
129 130

	data->index = args.args[0];
131 132

	misc_pdev = of_find_device_by_node(args.np);
133 134
	of_node_put(args.np);

135
	if (!misc_pdev || !platform_get_drvdata(misc_pdev))
136 137 138 139
		return ERR_PTR(-EPROBE_DEFER);

	data->dev = &misc_pdev->dev;

140
	if (of_find_property(np, "disable-over-current", NULL))
141
		data->disable_oc = 1;
142

143
	if (of_find_property(np, "external-vbus-divider", NULL))
144
		data->evdo = 1;
145

146
	return data;
147 148 149
}

/* End of common functions shared by usbmisc drivers*/
150 151 152 153 154 155 156 157 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 186 187 188 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 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
static int imx_get_clks(struct device *dev)
{
	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
	int ret = 0;

	data->clk_ipg = devm_clk_get(dev, "ipg");
	if (IS_ERR(data->clk_ipg)) {
		/* If the platform only needs one clocks */
		data->clk = devm_clk_get(dev, NULL);
		if (IS_ERR(data->clk)) {
			ret = PTR_ERR(data->clk);
			dev_err(dev,
				"Failed to get clks, err=%ld,%ld\n",
				PTR_ERR(data->clk), PTR_ERR(data->clk_ipg));
			return ret;
		}
		return ret;
	}

	data->clk_ahb = devm_clk_get(dev, "ahb");
	if (IS_ERR(data->clk_ahb)) {
		ret = PTR_ERR(data->clk_ahb);
		dev_err(dev,
			"Failed to get ahb clock, err=%d\n", ret);
		return ret;
	}

	data->clk_per = devm_clk_get(dev, "per");
	if (IS_ERR(data->clk_per)) {
		ret = PTR_ERR(data->clk_per);
		dev_err(dev,
			"Failed to get per clock, err=%d\n", ret);
		return ret;
	}

	data->need_three_clks = true;
	return ret;
}

static int imx_prepare_enable_clks(struct device *dev)
{
	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
	int ret = 0;

	if (data->need_three_clks) {
		ret = clk_prepare_enable(data->clk_ipg);
		if (ret) {
			dev_err(dev,
				"Failed to prepare/enable ipg clk, err=%d\n",
				ret);
			return ret;
		}

		ret = clk_prepare_enable(data->clk_ahb);
		if (ret) {
			dev_err(dev,
				"Failed to prepare/enable ahb clk, err=%d\n",
				ret);
			clk_disable_unprepare(data->clk_ipg);
			return ret;
		}

		ret = clk_prepare_enable(data->clk_per);
		if (ret) {
			dev_err(dev,
				"Failed to prepare/enable per clk, err=%d\n",
				ret);
			clk_disable_unprepare(data->clk_ahb);
			clk_disable_unprepare(data->clk_ipg);
			return ret;
		}
	} else {
		ret = clk_prepare_enable(data->clk);
		if (ret) {
			dev_err(dev,
				"Failed to prepare/enable clk, err=%d\n",
				ret);
			return ret;
		}
	}

	return ret;
}

static void imx_disable_unprepare_clks(struct device *dev)
{
	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);

	if (data->need_three_clks) {
		clk_disable_unprepare(data->clk_per);
		clk_disable_unprepare(data->clk_ahb);
		clk_disable_unprepare(data->clk_ipg);
	} else {
		clk_disable_unprepare(data->clk);
	}
}
246

247
static int ci_hdrc_imx_probe(struct platform_device *pdev)
248
{
249 250
	struct ci_hdrc_imx_data *data;
	struct ci_hdrc_platform_data pdata = {
251
		.name		= dev_name(&pdev->dev),
252
		.capoffset	= DEF_CAPOFFSET,
253
		.flags		= CI_HDRC_SET_NON_ZERO_TTHA,
254
	};
255
	int ret;
256 257 258 259 260 261 262 263
	const struct of_device_id *of_id;
	const struct ci_hdrc_imx_platform_flag *imx_platform_flag;

	of_id = of_match_device(ci_hdrc_imx_dt_ids, &pdev->dev);
	if (!of_id)
		return -ENODEV;

	imx_platform_flag = of_id->data;
264 265

	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
266
	if (!data)
267 268
		return -ENOMEM;

269
	platform_set_drvdata(pdev, data);
270 271 272 273
	data->usbmisc_data = usbmisc_get_init_data(&pdev->dev);
	if (IS_ERR(data->usbmisc_data))
		return PTR_ERR(data->usbmisc_data);

274 275 276
	ret = imx_get_clks(&pdev->dev);
	if (ret)
		return ret;
277

278 279
	ret = imx_prepare_enable_clks(&pdev->dev);
	if (ret)
280 281
		return ret;

282
	data->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "fsl,usbphy", 0);
283 284
	if (IS_ERR(data->phy)) {
		ret = PTR_ERR(data->phy);
285 286 287
		/* Return -EINVAL if no usbphy is available */
		if (ret == -ENODEV)
			ret = -EINVAL;
288
		goto err_clk;
289 290
	}

291
	pdata.usb_phy = data->phy;
292
	pdata.flags |= imx_platform_flag->flags;
293 294
	if (pdata.flags & CI_HDRC_SUPPORTS_RUNTIME_PM)
		data->supports_runtime_pm = true;
295

296
	ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
297 298
	if (ret)
		goto err_clk;
299

300 301 302 303
	ret = imx_usbmisc_init(data->usbmisc_data);
	if (ret) {
		dev_err(&pdev->dev, "usbmisc init failed, ret=%d\n", ret);
		goto err_clk;
304 305
	}

306
	data->ci_pdev = ci_hdrc_add_device(&pdev->dev,
307
				pdev->resource, pdev->num_resources,
308
				&pdata);
309 310
	if (IS_ERR(data->ci_pdev)) {
		ret = PTR_ERR(data->ci_pdev);
311 312 313
		dev_err(&pdev->dev,
			"Can't register ci_hdrc platform device, err=%d\n",
			ret);
314
		goto err_clk;
315 316
	}

317 318 319 320
	ret = imx_usbmisc_init_post(data->usbmisc_data);
	if (ret) {
		dev_err(&pdev->dev, "usbmisc post failed, ret=%d\n", ret);
		goto disable_device;
321 322
	}

323 324 325 326
	if (data->supports_runtime_pm) {
		pm_runtime_set_active(&pdev->dev);
		pm_runtime_enable(&pdev->dev);
	}
327

328 329
	device_set_wakeup_capable(&pdev->dev, true);

330 331
	return 0;

332
disable_device:
333
	ci_hdrc_remove_device(data->ci_pdev);
334
err_clk:
335
	imx_disable_unprepare_clks(&pdev->dev);
336 337 338
	return ret;
}

339
static int ci_hdrc_imx_remove(struct platform_device *pdev)
340
{
341
	struct ci_hdrc_imx_data *data = platform_get_drvdata(pdev);
342

343 344 345 346 347
	if (data->supports_runtime_pm) {
		pm_runtime_get_sync(&pdev->dev);
		pm_runtime_disable(&pdev->dev);
		pm_runtime_put_noidle(&pdev->dev);
	}
348
	ci_hdrc_remove_device(data->ci_pdev);
349
	imx_disable_unprepare_clks(&pdev->dev);
350 351 352 353

	return 0;
}

354 355 356 357 358
static void ci_hdrc_imx_shutdown(struct platform_device *pdev)
{
	ci_hdrc_imx_remove(pdev);
}

359
#ifdef CONFIG_PM
360 361 362 363 364 365
static int imx_controller_suspend(struct device *dev)
{
	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);

	dev_dbg(dev, "at %s\n", __func__);

366
	imx_disable_unprepare_clks(dev);
367
	data->in_lpm = true;
368 369 370 371 372 373 374

	return 0;
}

static int imx_controller_resume(struct device *dev)
{
	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
375
	int ret = 0;
376 377 378

	dev_dbg(dev, "at %s\n", __func__);

379 380 381 382 383
	if (!data->in_lpm) {
		WARN_ON(1);
		return 0;
	}

384
	ret = imx_prepare_enable_clks(dev);
385 386 387 388 389 390 391 392 393 394 395 396 397 398
	if (ret)
		return ret;

	data->in_lpm = false;

	ret = imx_usbmisc_set_wakeup(data->usbmisc_data, false);
	if (ret) {
		dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret);
		goto clk_disable;
	}

	return 0;

clk_disable:
399
	imx_disable_unprepare_clks(dev);
400
	return ret;
401 402
}

403
#ifdef CONFIG_PM_SLEEP
404 405
static int ci_hdrc_imx_suspend(struct device *dev)
{
406 407
	int ret;

408 409 410 411 412 413
	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);

	if (data->in_lpm)
		/* The core's suspend doesn't run */
		return 0;

414 415 416 417 418 419 420 421 422
	if (device_may_wakeup(dev)) {
		ret = imx_usbmisc_set_wakeup(data->usbmisc_data, true);
		if (ret) {
			dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n",
					ret);
			return ret;
		}
	}

423 424 425 426 427
	return imx_controller_suspend(dev);
}

static int ci_hdrc_imx_resume(struct device *dev)
{
428 429 430 431 432 433 434 435 436 437 438
	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
	int ret;

	ret = imx_controller_resume(dev);
	if (!ret && data->supports_runtime_pm) {
		pm_runtime_disable(dev);
		pm_runtime_set_active(dev);
		pm_runtime_enable(dev);
	}

	return ret;
439 440 441
}
#endif /* CONFIG_PM_SLEEP */

442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467
static int ci_hdrc_imx_runtime_suspend(struct device *dev)
{
	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
	int ret;

	if (data->in_lpm) {
		WARN_ON(1);
		return 0;
	}

	ret = imx_usbmisc_set_wakeup(data->usbmisc_data, true);
	if (ret) {
		dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret);
		return ret;
	}

	return imx_controller_suspend(dev);
}

static int ci_hdrc_imx_runtime_resume(struct device *dev)
{
	return imx_controller_resume(dev);
}

#endif /* CONFIG_PM */

468 469
static const struct dev_pm_ops ci_hdrc_imx_pm_ops = {
	SET_SYSTEM_SLEEP_PM_OPS(ci_hdrc_imx_suspend, ci_hdrc_imx_resume)
470 471
	SET_RUNTIME_PM_OPS(ci_hdrc_imx_runtime_suspend,
			ci_hdrc_imx_runtime_resume, NULL)
472
};
473 474 475
static struct platform_driver ci_hdrc_imx_driver = {
	.probe = ci_hdrc_imx_probe,
	.remove = ci_hdrc_imx_remove,
476
	.shutdown = ci_hdrc_imx_shutdown,
477 478
	.driver = {
		.name = "imx_usb",
479
		.of_match_table = ci_hdrc_imx_dt_ids,
480
		.pm = &ci_hdrc_imx_pm_ops,
481 482 483
	 },
};

484
module_platform_driver(ci_hdrc_imx_driver);
485 486 487

MODULE_ALIAS("platform:imx-usb");
MODULE_LICENSE("GPL v2");
488
MODULE_DESCRIPTION("CI HDRC i.MX USB binding");
489 490
MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");