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