| /* | 
 |  * Copyright (C) 2010 ST-Ericsson AB | 
 |  * Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com> | 
 |  * | 
 |  * Based on omap2430.c | 
 |  * | 
 |  * This program is free software; you can redistribute it and/or modify | 
 |  * it under the terms of the GNU General Public License as published by | 
 |  * the Free Software Foundation; either version 2 of the License, or | 
 |  * (at your option) any later version. | 
 |  * | 
 |  * This program is distributed in the hope that it will be useful, | 
 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 |  * GNU General Public License for more details. | 
 |  * | 
 |  * You should have received a copy of the GNU General Public License | 
 |  * along with this program; if not, write to the Free Software | 
 |  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | 
 |  */ | 
 |  | 
 | #include <linux/module.h> | 
 | #include <linux/kernel.h> | 
 | #include <linux/init.h> | 
 | #include <linux/clk.h> | 
 | #include <linux/io.h> | 
 | #include <linux/platform_device.h> | 
 |  | 
 | #include "musb_core.h" | 
 |  | 
 | struct ux500_glue { | 
 | 	struct device		*dev; | 
 | 	struct platform_device	*musb; | 
 | 	struct clk		*clk; | 
 | }; | 
 | #define glue_to_musb(g)	platform_get_drvdata(g->musb) | 
 |  | 
 | static int ux500_musb_init(struct musb *musb) | 
 | { | 
 | 	musb->xceiv = otg_get_transceiver(); | 
 | 	if (!musb->xceiv) { | 
 | 		pr_err("HS USB OTG: no transceiver configured\n"); | 
 | 		return -ENODEV; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int ux500_musb_exit(struct musb *musb) | 
 | { | 
 | 	otg_put_transceiver(musb->xceiv); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static const struct musb_platform_ops ux500_ops = { | 
 | 	.init		= ux500_musb_init, | 
 | 	.exit		= ux500_musb_exit, | 
 | }; | 
 |  | 
 | static int __init ux500_probe(struct platform_device *pdev) | 
 | { | 
 | 	struct musb_hdrc_platform_data	*pdata = pdev->dev.platform_data; | 
 | 	struct platform_device		*musb; | 
 | 	struct ux500_glue		*glue; | 
 | 	struct clk			*clk; | 
 |  | 
 | 	int				ret = -ENOMEM; | 
 |  | 
 | 	glue = kzalloc(sizeof(*glue), GFP_KERNEL); | 
 | 	if (!glue) { | 
 | 		dev_err(&pdev->dev, "failed to allocate glue context\n"); | 
 | 		goto err0; | 
 | 	} | 
 |  | 
 | 	musb = platform_device_alloc("musb-hdrc", -1); | 
 | 	if (!musb) { | 
 | 		dev_err(&pdev->dev, "failed to allocate musb device\n"); | 
 | 		goto err1; | 
 | 	} | 
 |  | 
 | 	clk = clk_get(&pdev->dev, "usb"); | 
 | 	if (IS_ERR(clk)) { | 
 | 		dev_err(&pdev->dev, "failed to get clock\n"); | 
 | 		ret = PTR_ERR(clk); | 
 | 		goto err2; | 
 | 	} | 
 |  | 
 | 	ret = clk_enable(clk); | 
 | 	if (ret) { | 
 | 		dev_err(&pdev->dev, "failed to enable clock\n"); | 
 | 		goto err3; | 
 | 	} | 
 |  | 
 | 	musb->dev.parent		= &pdev->dev; | 
 | 	musb->dev.dma_mask		= pdev->dev.dma_mask; | 
 | 	musb->dev.coherent_dma_mask	= pdev->dev.coherent_dma_mask; | 
 |  | 
 | 	glue->dev			= &pdev->dev; | 
 | 	glue->musb			= musb; | 
 | 	glue->clk			= clk; | 
 |  | 
 | 	pdata->platform_ops		= &ux500_ops; | 
 |  | 
 | 	platform_set_drvdata(pdev, glue); | 
 |  | 
 | 	ret = platform_device_add_resources(musb, pdev->resource, | 
 | 			pdev->num_resources); | 
 | 	if (ret) { | 
 | 		dev_err(&pdev->dev, "failed to add resources\n"); | 
 | 		goto err4; | 
 | 	} | 
 |  | 
 | 	ret = platform_device_add_data(musb, pdata, sizeof(*pdata)); | 
 | 	if (ret) { | 
 | 		dev_err(&pdev->dev, "failed to add platform_data\n"); | 
 | 		goto err4; | 
 | 	} | 
 |  | 
 | 	ret = platform_device_add(musb); | 
 | 	if (ret) { | 
 | 		dev_err(&pdev->dev, "failed to register musb device\n"); | 
 | 		goto err4; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 |  | 
 | err4: | 
 | 	clk_disable(clk); | 
 |  | 
 | err3: | 
 | 	clk_put(clk); | 
 |  | 
 | err2: | 
 | 	platform_device_put(musb); | 
 |  | 
 | err1: | 
 | 	kfree(glue); | 
 |  | 
 | err0: | 
 | 	return ret; | 
 | } | 
 |  | 
 | static int __exit ux500_remove(struct platform_device *pdev) | 
 | { | 
 | 	struct ux500_glue	*glue = platform_get_drvdata(pdev); | 
 |  | 
 | 	platform_device_del(glue->musb); | 
 | 	platform_device_put(glue->musb); | 
 | 	clk_disable(glue->clk); | 
 | 	clk_put(glue->clk); | 
 | 	kfree(glue); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | #ifdef CONFIG_PM | 
 | static int ux500_suspend(struct device *dev) | 
 | { | 
 | 	struct ux500_glue	*glue = dev_get_drvdata(dev); | 
 | 	struct musb		*musb = glue_to_musb(glue); | 
 |  | 
 | 	otg_set_suspend(musb->xceiv, 1); | 
 | 	clk_disable(glue->clk); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int ux500_resume(struct device *dev) | 
 | { | 
 | 	struct ux500_glue	*glue = dev_get_drvdata(dev); | 
 | 	struct musb		*musb = glue_to_musb(glue); | 
 | 	int			ret; | 
 |  | 
 | 	ret = clk_enable(glue->clk); | 
 | 	if (ret) { | 
 | 		dev_err(dev, "failed to enable clock\n"); | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	otg_set_suspend(musb->xceiv, 0); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static const struct dev_pm_ops ux500_pm_ops = { | 
 | 	.suspend	= ux500_suspend, | 
 | 	.resume		= ux500_resume, | 
 | }; | 
 |  | 
 | #define DEV_PM_OPS	(&ux500_pm_ops) | 
 | #else | 
 | #define DEV_PM_OPS	NULL | 
 | #endif | 
 |  | 
 | static struct platform_driver ux500_driver = { | 
 | 	.remove		= __exit_p(ux500_remove), | 
 | 	.driver		= { | 
 | 		.name	= "musb-ux500", | 
 | 		.pm	= DEV_PM_OPS, | 
 | 	}, | 
 | }; | 
 |  | 
 | MODULE_DESCRIPTION("UX500 MUSB Glue Layer"); | 
 | MODULE_AUTHOR("Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com>"); | 
 | MODULE_LICENSE("GPL v2"); | 
 |  | 
 | static int __init ux500_init(void) | 
 | { | 
 | 	return platform_driver_probe(&ux500_driver, ux500_probe); | 
 | } | 
 | subsys_initcall(ux500_init); | 
 |  | 
 | static void __exit ux500_exit(void) | 
 | { | 
 | 	platform_driver_unregister(&ux500_driver); | 
 | } | 
 | module_exit(ux500_exit); |