Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.8-rc5 485 lines 12 kB view raw
1/* 2 * Copyright 2012 Freescale Semiconductor, Inc. 3 * Copyright (C) 2012 Marek Vasut <marex@denx.de> 4 * on behalf of DENX Software Engineering GmbH 5 * 6 * The code contained herein is licensed under the GNU General Public 7 * License. You may obtain a copy of the GNU General Public License 8 * Version 2 or later at the following locations: 9 * 10 * http://www.opensource.org/licenses/gpl-license.html 11 * http://www.gnu.org/copyleft/gpl.html 12 */ 13 14#include <linux/module.h> 15#include <linux/of_platform.h> 16#include <linux/of_gpio.h> 17#include <linux/platform_device.h> 18#include <linux/pm_runtime.h> 19#include <linux/dma-mapping.h> 20#include <linux/usb/chipidea.h> 21#include <linux/clk.h> 22 23#include "ci.h" 24#include "ci_hdrc_imx.h" 25 26struct ci_hdrc_imx_platform_flag { 27 unsigned int flags; 28 bool runtime_pm; 29}; 30 31static const struct ci_hdrc_imx_platform_flag imx23_usb_data = { 32 .flags = CI_HDRC_TURN_VBUS_EARLY_ON | 33 CI_HDRC_DISABLE_STREAMING, 34}; 35 36static const struct ci_hdrc_imx_platform_flag imx27_usb_data = { 37 CI_HDRC_DISABLE_STREAMING, 38}; 39 40static const struct ci_hdrc_imx_platform_flag imx28_usb_data = { 41 .flags = CI_HDRC_IMX28_WRITE_FIX | 42 CI_HDRC_TURN_VBUS_EARLY_ON | 43 CI_HDRC_DISABLE_STREAMING, 44}; 45 46static const struct ci_hdrc_imx_platform_flag imx6q_usb_data = { 47 .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | 48 CI_HDRC_TURN_VBUS_EARLY_ON | 49 CI_HDRC_DISABLE_STREAMING, 50}; 51 52static const struct ci_hdrc_imx_platform_flag imx6sl_usb_data = { 53 .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | 54 CI_HDRC_TURN_VBUS_EARLY_ON | 55 CI_HDRC_DISABLE_HOST_STREAMING, 56}; 57 58static const struct ci_hdrc_imx_platform_flag imx6sx_usb_data = { 59 .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | 60 CI_HDRC_TURN_VBUS_EARLY_ON | 61 CI_HDRC_DISABLE_HOST_STREAMING, 62}; 63 64static const struct ci_hdrc_imx_platform_flag imx6ul_usb_data = { 65 .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | 66 CI_HDRC_TURN_VBUS_EARLY_ON, 67}; 68 69static const struct ci_hdrc_imx_platform_flag imx7d_usb_data = { 70 .flags = CI_HDRC_SUPPORTS_RUNTIME_PM, 71}; 72 73static const struct of_device_id ci_hdrc_imx_dt_ids[] = { 74 { .compatible = "fsl,imx23-usb", .data = &imx23_usb_data}, 75 { .compatible = "fsl,imx28-usb", .data = &imx28_usb_data}, 76 { .compatible = "fsl,imx27-usb", .data = &imx27_usb_data}, 77 { .compatible = "fsl,imx6q-usb", .data = &imx6q_usb_data}, 78 { .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 { /* sentinel */ } 83}; 84MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids); 85 86struct ci_hdrc_imx_data { 87 struct usb_phy *phy; 88 struct platform_device *ci_pdev; 89 struct clk *clk; 90 struct imx_usbmisc_data *usbmisc_data; 91 bool supports_runtime_pm; 92 bool in_lpm; 93 /* SoC before i.mx6 (except imx23/imx28) needs three clks */ 94 bool need_three_clks; 95 struct clk *clk_ipg; 96 struct clk *clk_ahb; 97 struct clk *clk_per; 98 /* --------------------------------- */ 99}; 100 101/* Common functions shared by usbmisc drivers */ 102 103static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev) 104{ 105 struct platform_device *misc_pdev; 106 struct device_node *np = dev->of_node; 107 struct of_phandle_args args; 108 struct imx_usbmisc_data *data; 109 int ret; 110 111 /* 112 * In case the fsl,usbmisc property is not present this device doesn't 113 * need usbmisc. Return NULL (which is no error here) 114 */ 115 if (!of_get_property(np, "fsl,usbmisc", NULL)) 116 return NULL; 117 118 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 119 if (!data) 120 return ERR_PTR(-ENOMEM); 121 122 ret = of_parse_phandle_with_args(np, "fsl,usbmisc", "#index-cells", 123 0, &args); 124 if (ret) { 125 dev_err(dev, "Failed to parse property fsl,usbmisc, errno %d\n", 126 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 of_node_put(args.np); 134 135 if (!misc_pdev || !platform_get_drvdata(misc_pdev)) 136 return ERR_PTR(-EPROBE_DEFER); 137 138 data->dev = &misc_pdev->dev; 139 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*/ 150static int imx_get_clks(struct device *dev) 151{ 152 struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 153 int ret = 0; 154 155 data->clk_ipg = devm_clk_get(dev, "ipg"); 156 if (IS_ERR(data->clk_ipg)) { 157 /* If the platform only needs one clocks */ 158 data->clk = devm_clk_get(dev, NULL); 159 if (IS_ERR(data->clk)) { 160 ret = PTR_ERR(data->clk); 161 dev_err(dev, 162 "Failed to get clks, err=%ld,%ld\n", 163 PTR_ERR(data->clk), PTR_ERR(data->clk_ipg)); 164 return ret; 165 } 166 return ret; 167 } 168 169 data->clk_ahb = devm_clk_get(dev, "ahb"); 170 if (IS_ERR(data->clk_ahb)) { 171 ret = PTR_ERR(data->clk_ahb); 172 dev_err(dev, 173 "Failed to get ahb clock, err=%d\n", ret); 174 return ret; 175 } 176 177 data->clk_per = devm_clk_get(dev, "per"); 178 if (IS_ERR(data->clk_per)) { 179 ret = PTR_ERR(data->clk_per); 180 dev_err(dev, 181 "Failed to get per clock, err=%d\n", ret); 182 return ret; 183 } 184 185 data->need_three_clks = true; 186 return ret; 187} 188 189static int imx_prepare_enable_clks(struct device *dev) 190{ 191 struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 192 int ret = 0; 193 194 if (data->need_three_clks) { 195 ret = clk_prepare_enable(data->clk_ipg); 196 if (ret) { 197 dev_err(dev, 198 "Failed to prepare/enable ipg clk, err=%d\n", 199 ret); 200 return ret; 201 } 202 203 ret = clk_prepare_enable(data->clk_ahb); 204 if (ret) { 205 dev_err(dev, 206 "Failed to prepare/enable ahb clk, err=%d\n", 207 ret); 208 clk_disable_unprepare(data->clk_ipg); 209 return ret; 210 } 211 212 ret = clk_prepare_enable(data->clk_per); 213 if (ret) { 214 dev_err(dev, 215 "Failed to prepare/enable per clk, err=%d\n", 216 ret); 217 clk_disable_unprepare(data->clk_ahb); 218 clk_disable_unprepare(data->clk_ipg); 219 return ret; 220 } 221 } else { 222 ret = clk_prepare_enable(data->clk); 223 if (ret) { 224 dev_err(dev, 225 "Failed to prepare/enable clk, err=%d\n", 226 ret); 227 return ret; 228 } 229 } 230 231 return ret; 232} 233 234static void imx_disable_unprepare_clks(struct device *dev) 235{ 236 struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 237 238 if (data->need_three_clks) { 239 clk_disable_unprepare(data->clk_per); 240 clk_disable_unprepare(data->clk_ahb); 241 clk_disable_unprepare(data->clk_ipg); 242 } else { 243 clk_disable_unprepare(data->clk); 244 } 245} 246 247static int ci_hdrc_imx_probe(struct platform_device *pdev) 248{ 249 struct ci_hdrc_imx_data *data; 250 struct ci_hdrc_platform_data pdata = { 251 .name = dev_name(&pdev->dev), 252 .capoffset = DEF_CAPOFFSET, 253 }; 254 int ret; 255 const struct of_device_id *of_id; 256 const struct ci_hdrc_imx_platform_flag *imx_platform_flag; 257 258 of_id = of_match_device(ci_hdrc_imx_dt_ids, &pdev->dev); 259 if (!of_id) 260 return -ENODEV; 261 262 imx_platform_flag = of_id->data; 263 264 data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 265 if (!data) 266 return -ENOMEM; 267 268 platform_set_drvdata(pdev, data); 269 data->usbmisc_data = usbmisc_get_init_data(&pdev->dev); 270 if (IS_ERR(data->usbmisc_data)) 271 return PTR_ERR(data->usbmisc_data); 272 273 ret = imx_get_clks(&pdev->dev); 274 if (ret) 275 return ret; 276 277 ret = imx_prepare_enable_clks(&pdev->dev); 278 if (ret) 279 return ret; 280 281 data->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "fsl,usbphy", 0); 282 if (IS_ERR(data->phy)) { 283 ret = PTR_ERR(data->phy); 284 /* Return -EINVAL if no usbphy is available */ 285 if (ret == -ENODEV) 286 ret = -EINVAL; 287 goto err_clk; 288 } 289 290 pdata.usb_phy = data->phy; 291 pdata.flags |= imx_platform_flag->flags; 292 if (pdata.flags & CI_HDRC_SUPPORTS_RUNTIME_PM) 293 data->supports_runtime_pm = true; 294 295 ret = imx_usbmisc_init(data->usbmisc_data); 296 if (ret) { 297 dev_err(&pdev->dev, "usbmisc init failed, ret=%d\n", ret); 298 goto err_clk; 299 } 300 301 data->ci_pdev = ci_hdrc_add_device(&pdev->dev, 302 pdev->resource, pdev->num_resources, 303 &pdata); 304 if (IS_ERR(data->ci_pdev)) { 305 ret = PTR_ERR(data->ci_pdev); 306 if (ret != -EPROBE_DEFER) 307 dev_err(&pdev->dev, 308 "ci_hdrc_add_device failed, err=%d\n", ret); 309 goto err_clk; 310 } 311 312 ret = imx_usbmisc_init_post(data->usbmisc_data); 313 if (ret) { 314 dev_err(&pdev->dev, "usbmisc post failed, ret=%d\n", ret); 315 goto disable_device; 316 } 317 318 if (data->supports_runtime_pm) { 319 pm_runtime_set_active(&pdev->dev); 320 pm_runtime_enable(&pdev->dev); 321 } 322 323 device_set_wakeup_capable(&pdev->dev, true); 324 325 return 0; 326 327disable_device: 328 ci_hdrc_remove_device(data->ci_pdev); 329err_clk: 330 imx_disable_unprepare_clks(&pdev->dev); 331 return ret; 332} 333 334static int ci_hdrc_imx_remove(struct platform_device *pdev) 335{ 336 struct ci_hdrc_imx_data *data = platform_get_drvdata(pdev); 337 338 if (data->supports_runtime_pm) { 339 pm_runtime_get_sync(&pdev->dev); 340 pm_runtime_disable(&pdev->dev); 341 pm_runtime_put_noidle(&pdev->dev); 342 } 343 ci_hdrc_remove_device(data->ci_pdev); 344 imx_disable_unprepare_clks(&pdev->dev); 345 346 return 0; 347} 348 349static void ci_hdrc_imx_shutdown(struct platform_device *pdev) 350{ 351 ci_hdrc_imx_remove(pdev); 352} 353 354#ifdef CONFIG_PM 355static int imx_controller_suspend(struct device *dev) 356{ 357 struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 358 359 dev_dbg(dev, "at %s\n", __func__); 360 361 imx_disable_unprepare_clks(dev); 362 data->in_lpm = true; 363 364 return 0; 365} 366 367static int imx_controller_resume(struct device *dev) 368{ 369 struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 370 int ret = 0; 371 372 dev_dbg(dev, "at %s\n", __func__); 373 374 if (!data->in_lpm) { 375 WARN_ON(1); 376 return 0; 377 } 378 379 ret = imx_prepare_enable_clks(dev); 380 if (ret) 381 return ret; 382 383 data->in_lpm = false; 384 385 ret = imx_usbmisc_set_wakeup(data->usbmisc_data, false); 386 if (ret) { 387 dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret); 388 goto clk_disable; 389 } 390 391 return 0; 392 393clk_disable: 394 imx_disable_unprepare_clks(dev); 395 return ret; 396} 397 398#ifdef CONFIG_PM_SLEEP 399static int ci_hdrc_imx_suspend(struct device *dev) 400{ 401 int ret; 402 403 struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 404 405 if (data->in_lpm) 406 /* The core's suspend doesn't run */ 407 return 0; 408 409 if (device_may_wakeup(dev)) { 410 ret = imx_usbmisc_set_wakeup(data->usbmisc_data, true); 411 if (ret) { 412 dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", 413 ret); 414 return ret; 415 } 416 } 417 418 return imx_controller_suspend(dev); 419} 420 421static int ci_hdrc_imx_resume(struct device *dev) 422{ 423 struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 424 int ret; 425 426 ret = imx_controller_resume(dev); 427 if (!ret && data->supports_runtime_pm) { 428 pm_runtime_disable(dev); 429 pm_runtime_set_active(dev); 430 pm_runtime_enable(dev); 431 } 432 433 return ret; 434} 435#endif /* CONFIG_PM_SLEEP */ 436 437static int ci_hdrc_imx_runtime_suspend(struct device *dev) 438{ 439 struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 440 int ret; 441 442 if (data->in_lpm) { 443 WARN_ON(1); 444 return 0; 445 } 446 447 ret = imx_usbmisc_set_wakeup(data->usbmisc_data, true); 448 if (ret) { 449 dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret); 450 return ret; 451 } 452 453 return imx_controller_suspend(dev); 454} 455 456static int ci_hdrc_imx_runtime_resume(struct device *dev) 457{ 458 return imx_controller_resume(dev); 459} 460 461#endif /* CONFIG_PM */ 462 463static const struct dev_pm_ops ci_hdrc_imx_pm_ops = { 464 SET_SYSTEM_SLEEP_PM_OPS(ci_hdrc_imx_suspend, ci_hdrc_imx_resume) 465 SET_RUNTIME_PM_OPS(ci_hdrc_imx_runtime_suspend, 466 ci_hdrc_imx_runtime_resume, NULL) 467}; 468static struct platform_driver ci_hdrc_imx_driver = { 469 .probe = ci_hdrc_imx_probe, 470 .remove = ci_hdrc_imx_remove, 471 .shutdown = ci_hdrc_imx_shutdown, 472 .driver = { 473 .name = "imx_usb", 474 .of_match_table = ci_hdrc_imx_dt_ids, 475 .pm = &ci_hdrc_imx_pm_ops, 476 }, 477}; 478 479module_platform_driver(ci_hdrc_imx_driver); 480 481MODULE_ALIAS("platform:imx-usb"); 482MODULE_LICENSE("GPL v2"); 483MODULE_DESCRIPTION("CI HDRC i.MX USB binding"); 484MODULE_AUTHOR("Marek Vasut <marex@denx.de>"); 485MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");