usb: otg: cpcap-otg: Fix section mismatch warning
[firefly-linux-kernel-4.4.55.git] / drivers / usb / otg / cpcap-otg.c
1 /*
2  * Copyright (C) 2010 Google, Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by the
6  * Free Software Foundation; either version 2 of the License, or (at your
7  * option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
12  * more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17  */
18
19 #include <linux/usb.h>
20 #include <linux/usb/otg.h>
21 #include <linux/usb/gadget.h>
22 #include <linux/usb/hcd.h>
23 #include <linux/platform_device.h>
24 #include <linux/clk.h>
25 #include <linux/io.h>
26 #include <linux/delay.h>
27 #include <linux/err.h>
28 #include <mach/usb_phy.h>
29 #include <mach/legacy_irq.h>
30
31 #define TEGRA_USB_PHY_WAKEUP_REG_OFFSET         0x408
32 #define   TEGRA_VBUS_WAKEUP_SW_VALUE            (1 << 12)
33 #define   TEGRA_VBUS_WAKEUP_SW_ENABLE           (1 << 11)
34 #define   TEGRA_ID_SW_VALUE                     (1 << 4)
35 #define   TEGRA_ID_SW_ENABLE                    (1 << 3)
36
37 struct cpcap_otg_data {
38         struct otg_transceiver otg;
39         struct notifier_block nb;
40         void __iomem *regs;
41         struct clk *clk;
42         int irq;
43 };
44
45 static const char *cpcap_state_name(enum usb_otg_state state)
46 {
47         if (state == OTG_STATE_A_HOST)
48                 return "HOST";
49         if (state == OTG_STATE_B_PERIPHERAL)
50                 return "PERIPHERAL";
51         if (state == OTG_STATE_A_SUSPEND)
52                 return "SUSPEND";
53         return "INVALID";
54 }
55
56 static irqreturn_t cpcap_otg_irq(int irq, void *data)
57 {
58         if (tegra_legacy_force_irq_status(irq))
59                 tegra_legacy_force_irq_clr(irq);
60         return IRQ_HANDLED;
61 }
62
63 static int cpcap_otg_notify(struct notifier_block *nb, unsigned long event,
64                             void *ignore)
65 {
66         struct cpcap_otg_data *cpcap;
67         struct otg_transceiver *otg;
68         enum usb_otg_state from;
69         enum usb_otg_state to;
70         unsigned long val;
71         struct usb_hcd *hcd;
72
73         cpcap = container_of(nb, struct cpcap_otg_data, nb);
74         otg = &cpcap->otg;
75
76         from = otg->state;
77         if (event == USB_EVENT_VBUS)
78                 to = OTG_STATE_B_PERIPHERAL;
79         else if (event == USB_EVENT_ID)
80                 to = OTG_STATE_A_HOST;
81         else
82                 to = OTG_STATE_A_SUSPEND;
83
84         if (from == to)
85                 return 0;
86         otg->state = to;
87
88         dev_info(cpcap->otg.dev, "%s --> %s", cpcap_state_name(from),
89                                               cpcap_state_name(to));
90
91         if ((to == OTG_STATE_A_HOST) && (from == OTG_STATE_A_SUSPEND)
92                         && otg->host) {
93                 hcd = (struct usb_hcd *)otg->host;
94
95                 clk_enable(cpcap->clk);
96
97                 val = readl(cpcap->regs + TEGRA_USB_PHY_WAKEUP_REG_OFFSET);
98                 val &= ~TEGRA_ID_SW_VALUE;
99                 writel(val, cpcap->regs + TEGRA_USB_PHY_WAKEUP_REG_OFFSET);
100
101                 set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
102
103         } else if ((to == OTG_STATE_A_SUSPEND) && (from == OTG_STATE_A_HOST)
104                         && otg->host) {
105                 val = readl(cpcap->regs + TEGRA_USB_PHY_WAKEUP_REG_OFFSET);
106                 val |= TEGRA_ID_SW_VALUE;
107                 writel(val, cpcap->regs + TEGRA_USB_PHY_WAKEUP_REG_OFFSET);
108
109                 clk_disable(cpcap->clk);
110
111         } else if ((to == OTG_STATE_B_PERIPHERAL)
112                         && (from == OTG_STATE_A_SUSPEND)
113                         && otg->gadget) {
114                 clk_enable(cpcap->clk);
115
116                 val = readl(cpcap->regs + TEGRA_USB_PHY_WAKEUP_REG_OFFSET);
117                 val |= TEGRA_VBUS_WAKEUP_SW_VALUE;
118                 writel(val, cpcap->regs + TEGRA_USB_PHY_WAKEUP_REG_OFFSET);
119
120         } else if ((to == OTG_STATE_A_SUSPEND)
121                         && (from == OTG_STATE_B_PERIPHERAL)
122                         && otg->gadget) {
123                 val = readl(cpcap->regs + TEGRA_USB_PHY_WAKEUP_REG_OFFSET);
124                 val &= ~TEGRA_VBUS_WAKEUP_SW_VALUE;
125                 writel(val, cpcap->regs + TEGRA_USB_PHY_WAKEUP_REG_OFFSET);
126
127                 clk_disable(cpcap->clk);
128         } else
129                 return 0;
130
131         tegra_legacy_force_irq_set(cpcap->irq);
132         return 0;
133 }
134
135 static int cpcap_otg_set_peripheral(struct otg_transceiver *otg,
136                                 struct usb_gadget *gadget)
137 {
138         otg->gadget = gadget;
139         return 0;
140 }
141
142 static int cpcap_otg_set_host(struct otg_transceiver *otg,
143                                 struct usb_bus *host)
144 {
145         otg->host = host;
146         return 0;
147 }
148
149 static int cpcap_otg_set_power(struct otg_transceiver *otg, unsigned mA)
150 {
151         return 0;
152 }
153
154 static int cpcap_otg_set_suspend(struct otg_transceiver *otg, int suspend)
155 {
156         return 0;
157 }
158
159 static int cpcap_otg_probe(struct platform_device *pdev)
160 {
161         struct cpcap_otg_data *cpcap;
162         struct resource *res;
163         unsigned long val;
164         int err;
165
166         cpcap = kzalloc(sizeof(struct cpcap_otg_data), GFP_KERNEL);
167         if (!cpcap)
168                 return -ENOMEM;
169
170         cpcap->otg.dev = &pdev->dev;
171         cpcap->otg.label = "cpcap-otg";
172         cpcap->otg.state = OTG_STATE_UNDEFINED;
173         cpcap->otg.set_host = cpcap_otg_set_host;
174         cpcap->otg.set_peripheral = cpcap_otg_set_peripheral;
175         cpcap->otg.set_suspend = cpcap_otg_set_suspend;
176         cpcap->otg.set_power = cpcap_otg_set_power;
177
178         platform_set_drvdata(pdev, cpcap);
179
180         cpcap->clk = clk_get(&pdev->dev, NULL);
181         if (IS_ERR(cpcap->clk)) {
182                 dev_err(&pdev->dev, "Can't get otg clock\n");
183                 err = PTR_ERR(cpcap->clk);
184                 goto err_clk;
185         }
186
187         err = clk_enable(cpcap->clk);
188         if (err)
189                 goto err_clken;
190
191         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
192         if (!res) {
193                 dev_err(&pdev->dev, "Failed to get I/O memory\n");
194                 err = -ENXIO;
195                 goto err_io;
196         }
197         cpcap->regs = ioremap(res->start, resource_size(res));
198         if (!cpcap->regs) {
199                 err = -ENOMEM;
200                 goto err_io;
201         }
202
203         cpcap->irq = platform_get_irq(pdev, 0);
204         if (cpcap->irq < 0) {
205                 dev_err(&pdev->dev, "Failed to get IRQ\n");
206                 err = -ENXIO;
207                 goto err_irq;
208         }
209
210         err = request_irq(cpcap->irq, cpcap_otg_irq, IRQF_SHARED,
211                         "cpcap-otg", cpcap);
212         if (err) {
213                 dev_err(&pdev->dev, "cannot request irq %d err %d\n",
214                                 cpcap->irq, err);
215                 goto err_irq;
216         }
217
218         val = readl(cpcap->regs + TEGRA_USB_PHY_WAKEUP_REG_OFFSET);
219         val |= TEGRA_VBUS_WAKEUP_SW_ENABLE | TEGRA_ID_SW_ENABLE;
220         val |= TEGRA_ID_SW_VALUE;
221         val &= ~(TEGRA_VBUS_WAKEUP_SW_VALUE);
222         writel(val, cpcap->regs + TEGRA_USB_PHY_WAKEUP_REG_OFFSET);
223
224         clk_disable(cpcap->clk);
225         cpcap->otg.state = OTG_STATE_A_SUSPEND;
226
227         BLOCKING_INIT_NOTIFIER_HEAD(&cpcap->otg.notifier);
228         cpcap->nb.notifier_call = cpcap_otg_notify;
229         otg_register_notifier(&cpcap->otg, &cpcap->nb);
230
231         err = otg_set_transceiver(&cpcap->otg);
232         if (err) {
233                 dev_err(&pdev->dev, "can't register transceiver (%d)\n", err);
234                 goto err_otg;
235         }
236
237         return 0;
238
239 err_otg:
240         free_irq(cpcap->irq, cpcap);
241 err_irq:
242         iounmap(cpcap->regs);
243 err_io:
244         clk_disable(cpcap->clk);
245 err_clken:
246         clk_put(cpcap->clk);
247 err_clk:
248         platform_set_drvdata(pdev, NULL);
249         kfree(cpcap);
250         return err;
251 }
252
253 static int __exit cpcap_otg_remove(struct platform_device *pdev)
254 {
255         struct cpcap_otg_data *cpcap = platform_get_drvdata(pdev);
256
257         otg_set_transceiver(NULL);
258         free_irq(cpcap->irq, cpcap);
259         iounmap(cpcap->regs);
260         clk_disable(cpcap->clk);
261         clk_put(cpcap->clk);
262         platform_set_drvdata(pdev, NULL);
263         kfree(cpcap);
264
265         return 0;
266 }
267
268 static struct platform_driver cpcap_otg_driver = {
269         .driver = {
270                 .name  = "cpcap-otg",
271         },
272         .remove  = __exit_p(cpcap_otg_remove),
273         .probe   = cpcap_otg_probe,
274 };
275
276 static int __init cpcap_otg_init(void)
277 {
278         return platform_driver_register(&cpcap_otg_driver);
279 }
280 module_init(cpcap_otg_init);
281
282 static void __exit cpcap_otg_exit(void)
283 {
284         platform_driver_unregister(&cpcap_otg_driver);
285 }
286 module_exit(cpcap_otg_exit);