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 v3.7-rc3 218 lines 5.7 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/kernel.h> 16#include <linux/platform_device.h> 17#include <linux/clk.h> 18#include <linux/usb/otg.h> 19#include <linux/stmp_device.h> 20#include <linux/delay.h> 21#include <linux/err.h> 22#include <linux/io.h> 23#include <linux/workqueue.h> 24 25#define DRIVER_NAME "mxs_phy" 26 27#define HW_USBPHY_PWD 0x00 28#define HW_USBPHY_CTRL 0x30 29#define HW_USBPHY_CTRL_SET 0x34 30#define HW_USBPHY_CTRL_CLR 0x38 31 32#define BM_USBPHY_CTRL_SFTRST BIT(31) 33#define BM_USBPHY_CTRL_CLKGATE BIT(30) 34#define BM_USBPHY_CTRL_ENUTMILEVEL3 BIT(15) 35#define BM_USBPHY_CTRL_ENUTMILEVEL2 BIT(14) 36#define BM_USBPHY_CTRL_ENHOSTDISCONDETECT BIT(1) 37 38/* 39 * Amount of delay in miliseconds to safely enable ENHOSTDISCONDETECT bit 40 * so that connection and reset processing can be completed for the root hub. 41 */ 42#define MXY_PHY_ENHOSTDISCONDETECT_DELAY 250 43 44struct mxs_phy { 45 struct usb_phy phy; 46 struct clk *clk; 47 struct delayed_work enhostdiscondetect_work; 48}; 49 50#define to_mxs_phy(p) container_of((p), struct mxs_phy, phy) 51 52static void mxs_phy_hw_init(struct mxs_phy *mxs_phy) 53{ 54 void __iomem *base = mxs_phy->phy.io_priv; 55 56 stmp_reset_block(base + HW_USBPHY_CTRL); 57 58 /* Power up the PHY */ 59 writel_relaxed(0, base + HW_USBPHY_PWD); 60 61 /* enable FS/LS device */ 62 writel_relaxed(BM_USBPHY_CTRL_ENUTMILEVEL2 | 63 BM_USBPHY_CTRL_ENUTMILEVEL3, 64 base + HW_USBPHY_CTRL_SET); 65} 66 67static int mxs_phy_init(struct usb_phy *phy) 68{ 69 struct mxs_phy *mxs_phy = to_mxs_phy(phy); 70 71 clk_prepare_enable(mxs_phy->clk); 72 mxs_phy_hw_init(mxs_phy); 73 INIT_DELAYED_WORK(&mxs_phy->enhostdiscondetect_work, NULL); 74 75 return 0; 76} 77 78static void mxs_phy_shutdown(struct usb_phy *phy) 79{ 80 struct mxs_phy *mxs_phy = to_mxs_phy(phy); 81 82 writel_relaxed(BM_USBPHY_CTRL_CLKGATE, 83 phy->io_priv + HW_USBPHY_CTRL_SET); 84 85 clk_disable_unprepare(mxs_phy->clk); 86} 87 88static void mxs_phy_enhostdiscondetect_delay(struct work_struct *ws) 89{ 90 struct mxs_phy *mxs_phy = container_of(ws, struct mxs_phy, 91 enhostdiscondetect_work.work); 92 93 /* Enable HOSTDISCONDETECT after delay. */ 94 dev_dbg(mxs_phy->phy.dev, "Setting ENHOSTDISCONDETECT\n"); 95 writel_relaxed(BM_USBPHY_CTRL_ENHOSTDISCONDETECT, 96 mxs_phy->phy.io_priv + HW_USBPHY_CTRL_SET); 97} 98 99static int mxs_phy_on_connect(struct usb_phy *phy, int port) 100{ 101 struct mxs_phy *mxs_phy = to_mxs_phy(phy); 102 103 dev_dbg(phy->dev, "Connect on port %d\n", port); 104 105 mxs_phy_hw_init(mxs_phy); 106 107 /* 108 * Delay enabling ENHOSTDISCONDETECT so that connection and 109 * reset processing can be completed for the root hub. 110 */ 111 dev_dbg(phy->dev, "Delaying setting ENHOSTDISCONDETECT\n"); 112 PREPARE_DELAYED_WORK(&mxs_phy->enhostdiscondetect_work, 113 mxs_phy_enhostdiscondetect_delay); 114 schedule_delayed_work(&mxs_phy->enhostdiscondetect_work, 115 msecs_to_jiffies(MXY_PHY_ENHOSTDISCONDETECT_DELAY)); 116 117 return 0; 118} 119 120static int mxs_phy_on_disconnect(struct usb_phy *phy, int port) 121{ 122 dev_dbg(phy->dev, "Disconnect on port %d\n", port); 123 124 /* No need to delay before clearing ENHOSTDISCONDETECT. */ 125 dev_dbg(phy->dev, "Clearing ENHOSTDISCONDETECT\n"); 126 writel_relaxed(BM_USBPHY_CTRL_ENHOSTDISCONDETECT, 127 phy->io_priv + HW_USBPHY_CTRL_CLR); 128 129 return 0; 130} 131 132static int mxs_phy_probe(struct platform_device *pdev) 133{ 134 struct resource *res; 135 void __iomem *base; 136 struct clk *clk; 137 struct mxs_phy *mxs_phy; 138 139 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 140 if (!res) { 141 dev_err(&pdev->dev, "can't get device resources\n"); 142 return -ENOENT; 143 } 144 145 base = devm_request_and_ioremap(&pdev->dev, res); 146 if (!base) 147 return -EBUSY; 148 149 clk = devm_clk_get(&pdev->dev, NULL); 150 if (IS_ERR(clk)) { 151 dev_err(&pdev->dev, 152 "can't get the clock, err=%ld", PTR_ERR(clk)); 153 return PTR_ERR(clk); 154 } 155 156 mxs_phy = devm_kzalloc(&pdev->dev, sizeof(*mxs_phy), GFP_KERNEL); 157 if (!mxs_phy) { 158 dev_err(&pdev->dev, "Failed to allocate USB PHY structure!\n"); 159 return -ENOMEM; 160 } 161 162 mxs_phy->phy.io_priv = base; 163 mxs_phy->phy.dev = &pdev->dev; 164 mxs_phy->phy.label = DRIVER_NAME; 165 mxs_phy->phy.init = mxs_phy_init; 166 mxs_phy->phy.shutdown = mxs_phy_shutdown; 167 mxs_phy->phy.notify_connect = mxs_phy_on_connect; 168 mxs_phy->phy.notify_disconnect = mxs_phy_on_disconnect; 169 170 ATOMIC_INIT_NOTIFIER_HEAD(&mxs_phy->phy.notifier); 171 172 mxs_phy->clk = clk; 173 174 platform_set_drvdata(pdev, &mxs_phy->phy); 175 176 return 0; 177} 178 179static int __devexit mxs_phy_remove(struct platform_device *pdev) 180{ 181 platform_set_drvdata(pdev, NULL); 182 183 return 0; 184} 185 186static const struct of_device_id mxs_phy_dt_ids[] = { 187 { .compatible = "fsl,imx23-usbphy", }, 188 { /* sentinel */ } 189}; 190MODULE_DEVICE_TABLE(of, mxs_phy_dt_ids); 191 192static struct platform_driver mxs_phy_driver = { 193 .probe = mxs_phy_probe, 194 .remove = __devexit_p(mxs_phy_remove), 195 .driver = { 196 .name = DRIVER_NAME, 197 .owner = THIS_MODULE, 198 .of_match_table = mxs_phy_dt_ids, 199 }, 200}; 201 202static int __init mxs_phy_module_init(void) 203{ 204 return platform_driver_register(&mxs_phy_driver); 205} 206postcore_initcall(mxs_phy_module_init); 207 208static void __exit mxs_phy_module_exit(void) 209{ 210 platform_driver_unregister(&mxs_phy_driver); 211} 212module_exit(mxs_phy_module_exit); 213 214MODULE_ALIAS("platform:mxs-usb-phy"); 215MODULE_AUTHOR("Marek Vasut <marex@denx.de>"); 216MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>"); 217MODULE_DESCRIPTION("Freescale MXS USB PHY driver"); 218MODULE_LICENSE("GPL");