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