d7adca3738ff52bda42411aa2c8ec84e86235079
[firefly-linux-kernel-4.4.55.git] / drivers / usb / phy / phy-mxs-usb.c
1 /*
2  * Copyright 2012-2013 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/of_device.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_ENAUTOSET_USBCLKS        BIT(26)
35 #define BM_USBPHY_CTRL_ENAUTOCLR_USBCLKGATE     BIT(25)
36 #define BM_USBPHY_CTRL_ENAUTOCLR_PHY_PWD        BIT(20)
37 #define BM_USBPHY_CTRL_ENAUTOCLR_CLKGATE        BIT(19)
38 #define BM_USBPHY_CTRL_ENAUTO_PWRON_PLL         BIT(18)
39 #define BM_USBPHY_CTRL_ENUTMILEVEL3             BIT(15)
40 #define BM_USBPHY_CTRL_ENUTMILEVEL2             BIT(14)
41 #define BM_USBPHY_CTRL_ENHOSTDISCONDETECT       BIT(1)
42
43 #define to_mxs_phy(p) container_of((p), struct mxs_phy, phy)
44
45 /* Do disconnection between PHY and controller without vbus */
46 #define MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS    BIT(0)
47
48 /*
49  * The PHY will be in messy if there is a wakeup after putting
50  * bus to suspend (set portsc.suspendM) but before setting PHY to low
51  * power mode (set portsc.phcd).
52  */
53 #define MXS_PHY_ABNORMAL_IN_SUSPEND             BIT(1)
54
55 /*
56  * The SOF sends too fast after resuming, it will cause disconnection
57  * between host and high speed device.
58  */
59 #define MXS_PHY_SENDING_SOF_TOO_FAST            BIT(2)
60
61 struct mxs_phy_data {
62         unsigned int flags;
63 };
64
65 static const struct mxs_phy_data imx23_phy_data = {
66         .flags = MXS_PHY_ABNORMAL_IN_SUSPEND | MXS_PHY_SENDING_SOF_TOO_FAST,
67 };
68
69 static const struct mxs_phy_data imx6q_phy_data = {
70         .flags = MXS_PHY_SENDING_SOF_TOO_FAST |
71                 MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS,
72 };
73
74 static const struct mxs_phy_data imx6sl_phy_data = {
75         .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS,
76 };
77
78 static const struct of_device_id mxs_phy_dt_ids[] = {
79         { .compatible = "fsl,imx6sl-usbphy", .data = &imx6sl_phy_data, },
80         { .compatible = "fsl,imx6q-usbphy", .data = &imx6q_phy_data, },
81         { .compatible = "fsl,imx23-usbphy", .data = &imx23_phy_data, },
82         { /* sentinel */ }
83 };
84 MODULE_DEVICE_TABLE(of, mxs_phy_dt_ids);
85
86 struct mxs_phy {
87         struct usb_phy phy;
88         struct clk *clk;
89         const struct mxs_phy_data *data;
90 };
91
92 static int mxs_phy_hw_init(struct mxs_phy *mxs_phy)
93 {
94         int ret;
95         void __iomem *base = mxs_phy->phy.io_priv;
96
97         ret = stmp_reset_block(base + HW_USBPHY_CTRL);
98         if (ret)
99                 return ret;
100
101         /* Power up the PHY */
102         writel(0, base + HW_USBPHY_PWD);
103
104         /*
105          * USB PHY Ctrl Setting
106          * - Auto clock/power on
107          * - Enable full/low speed support
108          */
109         writel(BM_USBPHY_CTRL_ENAUTOSET_USBCLKS |
110                 BM_USBPHY_CTRL_ENAUTOCLR_USBCLKGATE |
111                 BM_USBPHY_CTRL_ENAUTOCLR_PHY_PWD |
112                 BM_USBPHY_CTRL_ENAUTOCLR_CLKGATE |
113                 BM_USBPHY_CTRL_ENAUTO_PWRON_PLL |
114                 BM_USBPHY_CTRL_ENUTMILEVEL2 |
115                 BM_USBPHY_CTRL_ENUTMILEVEL3,
116                base + HW_USBPHY_CTRL_SET);
117
118         return 0;
119 }
120
121 static int mxs_phy_init(struct usb_phy *phy)
122 {
123         int ret;
124         struct mxs_phy *mxs_phy = to_mxs_phy(phy);
125
126         ret = clk_prepare_enable(mxs_phy->clk);
127         if (ret)
128                 return ret;
129
130         return mxs_phy_hw_init(mxs_phy);
131 }
132
133 static void mxs_phy_shutdown(struct usb_phy *phy)
134 {
135         struct mxs_phy *mxs_phy = to_mxs_phy(phy);
136
137         writel(BM_USBPHY_CTRL_CLKGATE,
138                phy->io_priv + HW_USBPHY_CTRL_SET);
139
140         clk_disable_unprepare(mxs_phy->clk);
141 }
142
143 static int mxs_phy_suspend(struct usb_phy *x, int suspend)
144 {
145         int ret;
146         struct mxs_phy *mxs_phy = to_mxs_phy(x);
147
148         if (suspend) {
149                 writel(0xffffffff, x->io_priv + HW_USBPHY_PWD);
150                 writel(BM_USBPHY_CTRL_CLKGATE,
151                        x->io_priv + HW_USBPHY_CTRL_SET);
152                 clk_disable_unprepare(mxs_phy->clk);
153         } else {
154                 ret = clk_prepare_enable(mxs_phy->clk);
155                 if (ret)
156                         return ret;
157                 writel(BM_USBPHY_CTRL_CLKGATE,
158                        x->io_priv + HW_USBPHY_CTRL_CLR);
159                 writel(0, x->io_priv + HW_USBPHY_PWD);
160         }
161
162         return 0;
163 }
164
165 static int mxs_phy_on_connect(struct usb_phy *phy,
166                 enum usb_device_speed speed)
167 {
168         dev_dbg(phy->dev, "%s speed device has connected\n",
169                 (speed == USB_SPEED_HIGH) ? "high" : "non-high");
170
171         if (speed == USB_SPEED_HIGH)
172                 writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT,
173                        phy->io_priv + HW_USBPHY_CTRL_SET);
174
175         return 0;
176 }
177
178 static int mxs_phy_on_disconnect(struct usb_phy *phy,
179                 enum usb_device_speed speed)
180 {
181         dev_dbg(phy->dev, "%s speed device has disconnected\n",
182                 (speed == USB_SPEED_HIGH) ? "high" : "non-high");
183
184         if (speed == USB_SPEED_HIGH)
185                 writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT,
186                        phy->io_priv + HW_USBPHY_CTRL_CLR);
187
188         return 0;
189 }
190
191 static int mxs_phy_probe(struct platform_device *pdev)
192 {
193         struct resource *res;
194         void __iomem *base;
195         struct clk *clk;
196         struct mxs_phy *mxs_phy;
197         int ret;
198         const struct of_device_id *of_id =
199                         of_match_device(mxs_phy_dt_ids, &pdev->dev);
200
201         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
202         base = devm_ioremap_resource(&pdev->dev, res);
203         if (IS_ERR(base))
204                 return PTR_ERR(base);
205
206         clk = devm_clk_get(&pdev->dev, NULL);
207         if (IS_ERR(clk)) {
208                 dev_err(&pdev->dev,
209                         "can't get the clock, err=%ld", PTR_ERR(clk));
210                 return PTR_ERR(clk);
211         }
212
213         mxs_phy = devm_kzalloc(&pdev->dev, sizeof(*mxs_phy), GFP_KERNEL);
214         if (!mxs_phy) {
215                 dev_err(&pdev->dev, "Failed to allocate USB PHY structure!\n");
216                 return -ENOMEM;
217         }
218
219         mxs_phy->phy.io_priv            = base;
220         mxs_phy->phy.dev                = &pdev->dev;
221         mxs_phy->phy.label              = DRIVER_NAME;
222         mxs_phy->phy.init               = mxs_phy_init;
223         mxs_phy->phy.shutdown           = mxs_phy_shutdown;
224         mxs_phy->phy.set_suspend        = mxs_phy_suspend;
225         mxs_phy->phy.notify_connect     = mxs_phy_on_connect;
226         mxs_phy->phy.notify_disconnect  = mxs_phy_on_disconnect;
227         mxs_phy->phy.type               = USB_PHY_TYPE_USB2;
228
229         mxs_phy->clk = clk;
230         mxs_phy->data = of_id->data;
231
232         platform_set_drvdata(pdev, mxs_phy);
233
234         ret = usb_add_phy_dev(&mxs_phy->phy);
235         if (ret)
236                 return ret;
237
238         return 0;
239 }
240
241 static int mxs_phy_remove(struct platform_device *pdev)
242 {
243         struct mxs_phy *mxs_phy = platform_get_drvdata(pdev);
244
245         usb_remove_phy(&mxs_phy->phy);
246
247         return 0;
248 }
249
250 static struct platform_driver mxs_phy_driver = {
251         .probe = mxs_phy_probe,
252         .remove = mxs_phy_remove,
253         .driver = {
254                 .name = DRIVER_NAME,
255                 .owner  = THIS_MODULE,
256                 .of_match_table = mxs_phy_dt_ids,
257          },
258 };
259
260 static int __init mxs_phy_module_init(void)
261 {
262         return platform_driver_register(&mxs_phy_driver);
263 }
264 postcore_initcall(mxs_phy_module_init);
265
266 static void __exit mxs_phy_module_exit(void)
267 {
268         platform_driver_unregister(&mxs_phy_driver);
269 }
270 module_exit(mxs_phy_module_exit);
271
272 MODULE_ALIAS("platform:mxs-usb-phy");
273 MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
274 MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");
275 MODULE_DESCRIPTION("Freescale MXS USB PHY driver");
276 MODULE_LICENSE("GPL");