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 26 struct ci_hdrc_imx_platform_flag { 27 unsigned int flags; 28 bool runtime_pm; 29 }; 30 31 static const struct ci_hdrc_imx_platform_flag imx27_usb_data = { 32 CI_HDRC_DISABLE_STREAMING, 33 }; 34 35 static const struct ci_hdrc_imx_platform_flag imx28_usb_data = { 36 .flags = CI_HDRC_IMX28_WRITE_FIX | 37 CI_HDRC_TURN_VBUS_EARLY_ON | 38 CI_HDRC_DISABLE_STREAMING, 39 }; 40 41 static const struct ci_hdrc_imx_platform_flag imx6q_usb_data = { 42 .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | 43 CI_HDRC_TURN_VBUS_EARLY_ON | 44 CI_HDRC_DISABLE_STREAMING, 45 }; 46 47 static const struct ci_hdrc_imx_platform_flag imx6sl_usb_data = { 48 .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | 49 CI_HDRC_TURN_VBUS_EARLY_ON | 50 CI_HDRC_DISABLE_HOST_STREAMING, 51 }; 52 53 static const struct ci_hdrc_imx_platform_flag imx6sx_usb_data = { 54 .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | 55 CI_HDRC_TURN_VBUS_EARLY_ON | 56 CI_HDRC_DISABLE_HOST_STREAMING, 57 }; 58 59 static const struct ci_hdrc_imx_platform_flag imx6ul_usb_data = { 60 .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | 61 CI_HDRC_TURN_VBUS_EARLY_ON, 62 }; 63 64 static const struct ci_hdrc_imx_platform_flag imx7d_usb_data = { 65 .flags = CI_HDRC_SUPPORTS_RUNTIME_PM, 66 }; 67 68 static const struct of_device_id ci_hdrc_imx_dt_ids[] = { 69 { .compatible = "fsl,imx28-usb", .data = &imx28_usb_data}, 70 { .compatible = "fsl,imx27-usb", .data = &imx27_usb_data}, 71 { .compatible = "fsl,imx6q-usb", .data = &imx6q_usb_data}, 72 { .compatible = "fsl,imx6sl-usb", .data = &imx6sl_usb_data}, 73 { .compatible = "fsl,imx6sx-usb", .data = &imx6sx_usb_data}, 74 { .compatible = "fsl,imx6ul-usb", .data = &imx6ul_usb_data}, 75 { .compatible = "fsl,imx7d-usb", .data = &imx7d_usb_data}, 76 { /* sentinel */ } 77 }; 78 MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids); 79 80 struct ci_hdrc_imx_data { 81 struct usb_phy *phy; 82 struct platform_device *ci_pdev; 83 struct clk *clk; 84 struct imx_usbmisc_data *usbmisc_data; 85 bool supports_runtime_pm; 86 bool in_lpm; 87 }; 88 89 /* Common functions shared by usbmisc drivers */ 90 91 static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev) 92 { 93 struct platform_device *misc_pdev; 94 struct device_node *np = dev->of_node; 95 struct of_phandle_args args; 96 struct imx_usbmisc_data *data; 97 int ret; 98 99 /* 100 * In case the fsl,usbmisc property is not present this device doesn't 101 * need usbmisc. Return NULL (which is no error here) 102 */ 103 if (!of_get_property(np, "fsl,usbmisc", NULL)) 104 return NULL; 105 106 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 107 if (!data) 108 return ERR_PTR(-ENOMEM); 109 110 ret = of_parse_phandle_with_args(np, "fsl,usbmisc", "#index-cells", 111 0, &args); 112 if (ret) { 113 dev_err(dev, "Failed to parse property fsl,usbmisc, errno %d\n", 114 ret); 115 return ERR_PTR(ret); 116 } 117 118 data->index = args.args[0]; 119 120 misc_pdev = of_find_device_by_node(args.np); 121 of_node_put(args.np); 122 123 if (!misc_pdev || !platform_get_drvdata(misc_pdev)) 124 return ERR_PTR(-EPROBE_DEFER); 125 126 data->dev = &misc_pdev->dev; 127 128 if (of_find_property(np, "disable-over-current", NULL)) 129 data->disable_oc = 1; 130 131 if (of_find_property(np, "external-vbus-divider", NULL)) 132 data->evdo = 1; 133 134 return data; 135 } 136 137 /* End of common functions shared by usbmisc drivers*/ 138 139 static int ci_hdrc_imx_probe(struct platform_device *pdev) 140 { 141 struct ci_hdrc_imx_data *data; 142 struct ci_hdrc_platform_data pdata = { 143 .name = dev_name(&pdev->dev), 144 .capoffset = DEF_CAPOFFSET, 145 .flags = CI_HDRC_SET_NON_ZERO_TTHA, 146 }; 147 int ret; 148 const struct of_device_id *of_id = 149 of_match_device(ci_hdrc_imx_dt_ids, &pdev->dev); 150 const struct ci_hdrc_imx_platform_flag *imx_platform_flag = of_id->data; 151 152 data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 153 if (!data) 154 return -ENOMEM; 155 156 data->usbmisc_data = usbmisc_get_init_data(&pdev->dev); 157 if (IS_ERR(data->usbmisc_data)) 158 return PTR_ERR(data->usbmisc_data); 159 160 data->clk = devm_clk_get(&pdev->dev, NULL); 161 if (IS_ERR(data->clk)) { 162 dev_err(&pdev->dev, 163 "Failed to get clock, err=%ld\n", PTR_ERR(data->clk)); 164 return PTR_ERR(data->clk); 165 } 166 167 ret = clk_prepare_enable(data->clk); 168 if (ret) { 169 dev_err(&pdev->dev, 170 "Failed to prepare or enable clock, err=%d\n", ret); 171 return ret; 172 } 173 174 data->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "fsl,usbphy", 0); 175 if (IS_ERR(data->phy)) { 176 ret = PTR_ERR(data->phy); 177 /* Return -EINVAL if no usbphy is available */ 178 if (ret == -ENODEV) 179 ret = -EINVAL; 180 goto err_clk; 181 } 182 183 pdata.usb_phy = data->phy; 184 pdata.flags |= imx_platform_flag->flags; 185 if (pdata.flags & CI_HDRC_SUPPORTS_RUNTIME_PM) 186 data->supports_runtime_pm = true; 187 188 ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); 189 if (ret) 190 goto err_clk; 191 192 ret = imx_usbmisc_init(data->usbmisc_data); 193 if (ret) { 194 dev_err(&pdev->dev, "usbmisc init failed, ret=%d\n", ret); 195 goto err_clk; 196 } 197 198 data->ci_pdev = ci_hdrc_add_device(&pdev->dev, 199 pdev->resource, pdev->num_resources, 200 &pdata); 201 if (IS_ERR(data->ci_pdev)) { 202 ret = PTR_ERR(data->ci_pdev); 203 dev_err(&pdev->dev, 204 "Can't register ci_hdrc platform device, err=%d\n", 205 ret); 206 goto err_clk; 207 } 208 209 ret = imx_usbmisc_init_post(data->usbmisc_data); 210 if (ret) { 211 dev_err(&pdev->dev, "usbmisc post failed, ret=%d\n", ret); 212 goto disable_device; 213 } 214 215 platform_set_drvdata(pdev, data); 216 217 if (data->supports_runtime_pm) { 218 pm_runtime_set_active(&pdev->dev); 219 pm_runtime_enable(&pdev->dev); 220 } 221 222 device_set_wakeup_capable(&pdev->dev, true); 223 224 return 0; 225 226 disable_device: 227 ci_hdrc_remove_device(data->ci_pdev); 228 err_clk: 229 clk_disable_unprepare(data->clk); 230 return ret; 231 } 232 233 static int ci_hdrc_imx_remove(struct platform_device *pdev) 234 { 235 struct ci_hdrc_imx_data *data = platform_get_drvdata(pdev); 236 237 if (data->supports_runtime_pm) { 238 pm_runtime_get_sync(&pdev->dev); 239 pm_runtime_disable(&pdev->dev); 240 pm_runtime_put_noidle(&pdev->dev); 241 } 242 ci_hdrc_remove_device(data->ci_pdev); 243 clk_disable_unprepare(data->clk); 244 245 return 0; 246 } 247 248 #ifdef CONFIG_PM 249 static int imx_controller_suspend(struct device *dev) 250 { 251 struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 252 253 dev_dbg(dev, "at %s\n", __func__); 254 255 clk_disable_unprepare(data->clk); 256 data->in_lpm = true; 257 258 return 0; 259 } 260 261 static int imx_controller_resume(struct device *dev) 262 { 263 struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 264 int ret = 0; 265 266 dev_dbg(dev, "at %s\n", __func__); 267 268 if (!data->in_lpm) { 269 WARN_ON(1); 270 return 0; 271 } 272 273 ret = clk_prepare_enable(data->clk); 274 if (ret) 275 return ret; 276 277 data->in_lpm = false; 278 279 ret = imx_usbmisc_set_wakeup(data->usbmisc_data, false); 280 if (ret) { 281 dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret); 282 goto clk_disable; 283 } 284 285 return 0; 286 287 clk_disable: 288 clk_disable_unprepare(data->clk); 289 return ret; 290 } 291 292 #ifdef CONFIG_PM_SLEEP 293 static int ci_hdrc_imx_suspend(struct device *dev) 294 { 295 int ret; 296 297 struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 298 299 if (data->in_lpm) 300 /* The core's suspend doesn't run */ 301 return 0; 302 303 if (device_may_wakeup(dev)) { 304 ret = imx_usbmisc_set_wakeup(data->usbmisc_data, true); 305 if (ret) { 306 dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", 307 ret); 308 return ret; 309 } 310 } 311 312 return imx_controller_suspend(dev); 313 } 314 315 static int ci_hdrc_imx_resume(struct device *dev) 316 { 317 struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 318 int ret; 319 320 ret = imx_controller_resume(dev); 321 if (!ret && data->supports_runtime_pm) { 322 pm_runtime_disable(dev); 323 pm_runtime_set_active(dev); 324 pm_runtime_enable(dev); 325 } 326 327 return ret; 328 } 329 #endif /* CONFIG_PM_SLEEP */ 330 331 static int ci_hdrc_imx_runtime_suspend(struct device *dev) 332 { 333 struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 334 int ret; 335 336 if (data->in_lpm) { 337 WARN_ON(1); 338 return 0; 339 } 340 341 ret = imx_usbmisc_set_wakeup(data->usbmisc_data, true); 342 if (ret) { 343 dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret); 344 return ret; 345 } 346 347 return imx_controller_suspend(dev); 348 } 349 350 static int ci_hdrc_imx_runtime_resume(struct device *dev) 351 { 352 return imx_controller_resume(dev); 353 } 354 355 #endif /* CONFIG_PM */ 356 357 static const struct dev_pm_ops ci_hdrc_imx_pm_ops = { 358 SET_SYSTEM_SLEEP_PM_OPS(ci_hdrc_imx_suspend, ci_hdrc_imx_resume) 359 SET_RUNTIME_PM_OPS(ci_hdrc_imx_runtime_suspend, 360 ci_hdrc_imx_runtime_resume, NULL) 361 }; 362 static struct platform_driver ci_hdrc_imx_driver = { 363 .probe = ci_hdrc_imx_probe, 364 .remove = ci_hdrc_imx_remove, 365 .driver = { 366 .name = "imx_usb", 367 .of_match_table = ci_hdrc_imx_dt_ids, 368 .pm = &ci_hdrc_imx_pm_ops, 369 }, 370 }; 371 372 module_platform_driver(ci_hdrc_imx_driver); 373 374 MODULE_ALIAS("platform:imx-usb"); 375 MODULE_LICENSE("GPL v2"); 376 MODULE_DESCRIPTION("CI HDRC i.MX USB binding"); 377 MODULE_AUTHOR("Marek Vasut <marex@denx.de>"); 378 MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>"); 379