e7253182e47d72882b27a2126ba5a0b34183cdee
[cascardo/linux.git] / drivers / usb / phy / phy-omap-control.c
1 /*
2  * omap-control-usb.c - The USB part of control module.
3  *
4  * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * Author: Kishon Vijay Abraham I <kishon@ti.com>
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  */
18
19 #include <linux/module.h>
20 #include <linux/platform_device.h>
21 #include <linux/slab.h>
22 #include <linux/of.h>
23 #include <linux/of_device.h>
24 #include <linux/err.h>
25 #include <linux/io.h>
26 #include <linux/clk.h>
27 #include <linux/usb/omap_control_usb.h>
28
29 /**
30  * omap_control_usb_phy_power - power on/off the phy using control module reg
31  * @dev: the control module device
32  * @on: 0 or 1, based on powering on or off the PHY
33  */
34 void omap_control_usb_phy_power(struct device *dev, int on)
35 {
36         u32 val;
37         unsigned long rate;
38         struct omap_control_usb *control_usb;
39
40         if (IS_ERR(dev) || !dev) {
41                 pr_err("%s: invalid device\n", __func__);
42                 return;
43         }
44
45         control_usb = dev_get_drvdata(dev);
46         if (!control_usb) {
47                 dev_err(dev, "%s: invalid control usb device\n", __func__);
48                 return;
49         }
50
51         if (control_usb->type == OMAP_CTRL_TYPE_OTGHS)
52                 return;
53
54         val = readl(control_usb->power);
55
56         switch (control_usb->type) {
57         case OMAP_CTRL_TYPE_USB2:
58                 if (on)
59                         val &= ~OMAP_CTRL_DEV_PHY_PD;
60                 else
61                         val |= OMAP_CTRL_DEV_PHY_PD;
62                 break;
63
64         case OMAP_CTRL_TYPE_PIPE3:
65                 rate = clk_get_rate(control_usb->sys_clk);
66                 rate = rate/1000000;
67
68                 if (on) {
69                         val &= ~(OMAP_CTRL_USB_PWRCTL_CLK_CMD_MASK |
70                                         OMAP_CTRL_USB_PWRCTL_CLK_FREQ_MASK);
71                         val |= OMAP_CTRL_USB3_PHY_TX_RX_POWERON <<
72                                 OMAP_CTRL_USB_PWRCTL_CLK_CMD_SHIFT;
73                         val |= rate << OMAP_CTRL_USB_PWRCTL_CLK_FREQ_SHIFT;
74                 } else {
75                         val &= ~OMAP_CTRL_USB_PWRCTL_CLK_CMD_MASK;
76                         val |= OMAP_CTRL_USB3_PHY_TX_RX_POWEROFF <<
77                                 OMAP_CTRL_USB_PWRCTL_CLK_CMD_SHIFT;
78                 }
79                 break;
80
81         case OMAP_CTRL_TYPE_DRA7USB2:
82                 if (on)
83                         val &= ~OMAP_CTRL_USB2_PHY_PD;
84                 else
85                         val |= OMAP_CTRL_USB2_PHY_PD;
86                 break;
87
88         case OMAP_CTRL_TYPE_AM437USB2:
89                 if (on) {
90                         val &= ~(AM437X_CTRL_USB2_PHY_PD |
91                                         AM437X_CTRL_USB2_OTG_PD);
92                         val |= (AM437X_CTRL_USB2_OTGVDET_EN |
93                                         AM437X_CTRL_USB2_OTGSESSEND_EN);
94                 } else {
95                         val &= ~(AM437X_CTRL_USB2_OTGVDET_EN |
96                                         AM437X_CTRL_USB2_OTGSESSEND_EN);
97                         val |= (AM437X_CTRL_USB2_PHY_PD |
98                                          AM437X_CTRL_USB2_OTG_PD);
99                 }
100                 break;
101         default:
102                 dev_err(dev, "%s: type %d not recognized\n",
103                                         __func__, control_usb->type);
104                 break;
105         }
106
107         writel(val, control_usb->power);
108 }
109 EXPORT_SYMBOL_GPL(omap_control_usb_phy_power);
110
111 /**
112  * omap_control_usb_host_mode - set AVALID, VBUSVALID and ID pin in grounded
113  * @ctrl_usb: struct omap_control_usb *
114  *
115  * Writes to the mailbox register to notify the usb core that a usb
116  * device has been connected.
117  */
118 static void omap_control_usb_host_mode(struct omap_control_usb *ctrl_usb)
119 {
120         u32 val;
121
122         val = readl(ctrl_usb->otghs_control);
123         val &= ~(OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND);
124         val |= OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID;
125         writel(val, ctrl_usb->otghs_control);
126 }
127
128 /**
129  * omap_control_usb_device_mode - set AVALID, VBUSVALID and ID pin in high
130  * impedance
131  * @ctrl_usb: struct omap_control_usb *
132  *
133  * Writes to the mailbox register to notify the usb core that it has been
134  * connected to a usb host.
135  */
136 static void omap_control_usb_device_mode(struct omap_control_usb *ctrl_usb)
137 {
138         u32 val;
139
140         val = readl(ctrl_usb->otghs_control);
141         val &= ~OMAP_CTRL_DEV_SESSEND;
142         val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_AVALID |
143                 OMAP_CTRL_DEV_VBUSVALID;
144         writel(val, ctrl_usb->otghs_control);
145 }
146
147 /**
148  * omap_control_usb_set_sessionend - Enable SESSIONEND and IDIG to high
149  * impedance
150  * @ctrl_usb: struct omap_control_usb *
151  *
152  * Writes to the mailbox register to notify the usb core it's now in
153  * disconnected state.
154  */
155 static void omap_control_usb_set_sessionend(struct omap_control_usb *ctrl_usb)
156 {
157         u32 val;
158
159         val = readl(ctrl_usb->otghs_control);
160         val &= ~(OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID);
161         val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND;
162         writel(val, ctrl_usb->otghs_control);
163 }
164
165 /**
166  * omap_control_usb_set_mode - Calls to functions to set USB in one of host mode
167  * or device mode or to denote disconnected state
168  * @dev: the control module device
169  * @mode: The mode to which usb should be configured
170  *
171  * This is an API to write to the mailbox register to notify the usb core that
172  * a usb device has been connected.
173  */
174 void omap_control_usb_set_mode(struct device *dev,
175         enum omap_control_usb_mode mode)
176 {
177         struct omap_control_usb *ctrl_usb;
178
179         if (IS_ERR(dev) || !dev)
180                 return;
181
182         ctrl_usb = dev_get_drvdata(dev);
183
184         if (!ctrl_usb) {
185                 dev_err(dev, "Invalid control usb device\n");
186                 return;
187         }
188
189         if (ctrl_usb->type != OMAP_CTRL_TYPE_OTGHS)
190                 return;
191
192         switch (mode) {
193         case USB_MODE_HOST:
194                 omap_control_usb_host_mode(ctrl_usb);
195                 break;
196         case USB_MODE_DEVICE:
197                 omap_control_usb_device_mode(ctrl_usb);
198                 break;
199         case USB_MODE_DISCONNECT:
200                 omap_control_usb_set_sessionend(ctrl_usb);
201                 break;
202         default:
203                 dev_vdbg(dev, "invalid omap control usb mode\n");
204         }
205 }
206 EXPORT_SYMBOL_GPL(omap_control_usb_set_mode);
207
208 #ifdef CONFIG_OF
209
210 static const enum omap_control_usb_type otghs_data = OMAP_CTRL_TYPE_OTGHS;
211 static const enum omap_control_usb_type usb2_data = OMAP_CTRL_TYPE_USB2;
212 static const enum omap_control_usb_type pipe3_data = OMAP_CTRL_TYPE_PIPE3;
213 static const enum omap_control_usb_type dra7usb2_data = OMAP_CTRL_TYPE_DRA7USB2;
214 static const enum omap_control_usb_type am437usb2_data = OMAP_CTRL_TYPE_AM437USB2;
215
216 static const struct of_device_id omap_control_usb_id_table[] = {
217         {
218                 .compatible = "ti,control-phy-otghs",
219                 .data = &otghs_data,
220         },
221         {
222                 .compatible = "ti,control-phy-usb2",
223                 .data = &usb2_data,
224         },
225         {
226                 .compatible = "ti,control-phy-pipe3",
227                 .data = &pipe3_data,
228         },
229         {
230                 .compatible = "ti,control-phy-dra7usb2",
231                 .data = &dra7usb2_data,
232         },
233         {
234                 .compatible = "ti,control-phy-am437usb2",
235                 .data = &am437usb2_data,
236         },
237         {},
238 };
239 MODULE_DEVICE_TABLE(of, omap_control_usb_id_table);
240 #endif
241
242
243 static int omap_control_usb_probe(struct platform_device *pdev)
244 {
245         struct resource *res;
246         const struct of_device_id *of_id;
247         struct omap_control_usb *control_usb;
248
249         of_id = of_match_device(of_match_ptr(omap_control_usb_id_table),
250                                                                 &pdev->dev);
251         if (!of_id)
252                 return -EINVAL;
253
254         control_usb = devm_kzalloc(&pdev->dev, sizeof(*control_usb),
255                 GFP_KERNEL);
256         if (!control_usb) {
257                 dev_err(&pdev->dev, "unable to alloc memory for control usb\n");
258                 return -ENOMEM;
259         }
260
261         control_usb->dev = &pdev->dev;
262         control_usb->type = *(enum omap_control_usb_type *)of_id->data;
263
264         if (control_usb->type == OMAP_CTRL_TYPE_OTGHS) {
265                 res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
266                         "otghs_control");
267                 control_usb->otghs_control = devm_ioremap_resource(
268                         &pdev->dev, res);
269                 if (IS_ERR(control_usb->otghs_control))
270                         return PTR_ERR(control_usb->otghs_control);
271         } else {
272                 res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
273                                 "power");
274                 control_usb->power = devm_ioremap_resource(&pdev->dev, res);
275                 if (IS_ERR(control_usb->power)) {
276                         dev_err(&pdev->dev, "Couldn't get power register\n");
277                         return PTR_ERR(control_usb->power);
278                 }
279         }
280
281         if (control_usb->type == OMAP_CTRL_TYPE_PIPE3) {
282                 control_usb->sys_clk = devm_clk_get(control_usb->dev,
283                         "sys_clkin");
284                 if (IS_ERR(control_usb->sys_clk)) {
285                         pr_err("%s: unable to get sys_clkin\n", __func__);
286                         return -EINVAL;
287                 }
288         }
289
290         dev_set_drvdata(control_usb->dev, control_usb);
291
292         return 0;
293 }
294
295 static struct platform_driver omap_control_usb_driver = {
296         .probe          = omap_control_usb_probe,
297         .driver         = {
298                 .name   = "omap-control-usb",
299                 .owner  = THIS_MODULE,
300                 .of_match_table = of_match_ptr(omap_control_usb_id_table),
301         },
302 };
303
304 static int __init omap_control_usb_init(void)
305 {
306         return platform_driver_register(&omap_control_usb_driver);
307 }
308 subsys_initcall(omap_control_usb_init);
309
310 static void __exit omap_control_usb_exit(void)
311 {
312         platform_driver_unregister(&omap_control_usb_driver);
313 }
314 module_exit(omap_control_usb_exit);
315
316 MODULE_ALIAS("platform: omap_control_usb");
317 MODULE_AUTHOR("Texas Instruments Inc.");
318 MODULE_DESCRIPTION("OMAP Control Module USB Driver");
319 MODULE_LICENSE("GPL v2");