]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/usb/chipidea/ci_hdrc_msm.c
7e870a253f555d5feb044f8c23cb412ce43b8dd2
[karo-tx-linux.git] / drivers / usb / chipidea / ci_hdrc_msm.c
1 /* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
2  *
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License version 2 and
5  * only version 2 as published by the Free Software Foundation.
6  */
7
8 #include <linux/module.h>
9 #include <linux/platform_device.h>
10 #include <linux/pm_runtime.h>
11 #include <linux/usb/gadget.h>
12 #include <linux/usb/chipidea.h>
13 #include <linux/clk.h>
14 #include <linux/reset.h>
15
16 #include "ci.h"
17
18 #define HS_PHY_AHB_MODE                 0x0098
19
20 struct ci_hdrc_msm {
21         struct platform_device *ci;
22         struct clk *core_clk;
23         struct clk *iface_clk;
24         struct clk *fs_clk;
25 };
26
27 static void ci_hdrc_msm_notify_event(struct ci_hdrc *ci, unsigned event)
28 {
29         struct device *dev = ci->gadget.dev.parent;
30
31         switch (event) {
32         case CI_HDRC_CONTROLLER_RESET_EVENT:
33                 dev_dbg(dev, "CI_HDRC_CONTROLLER_RESET_EVENT received\n");
34                 /* use AHB transactor, allow posted data writes */
35                 hw_write_id_reg(ci, HS_PHY_AHB_MODE, 0xffffffff, 0x8);
36                 usb_phy_init(ci->usb_phy);
37                 break;
38         case CI_HDRC_CONTROLLER_STOPPED_EVENT:
39                 dev_dbg(dev, "CI_HDRC_CONTROLLER_STOPPED_EVENT received\n");
40                 /*
41                  * Put the phy in non-driving mode. Otherwise host
42                  * may not detect soft-disconnection.
43                  */
44                 usb_phy_notify_disconnect(ci->usb_phy, USB_SPEED_UNKNOWN);
45                 break;
46         default:
47                 dev_dbg(dev, "unknown ci_hdrc event\n");
48                 break;
49         }
50 }
51
52 static struct ci_hdrc_platform_data ci_hdrc_msm_platdata = {
53         .name                   = "ci_hdrc_msm",
54         .capoffset              = DEF_CAPOFFSET,
55         .flags                  = CI_HDRC_REGS_SHARED |
56                                   CI_HDRC_DISABLE_STREAMING |
57                                   CI_HDRC_OVERRIDE_AHB_BURST,
58
59         .notify_event           = ci_hdrc_msm_notify_event,
60 };
61
62 static int ci_hdrc_msm_probe(struct platform_device *pdev)
63 {
64         struct ci_hdrc_msm *ci;
65         struct platform_device *plat_ci;
66         struct usb_phy *phy;
67         struct clk *clk;
68         struct reset_control *reset;
69         int ret;
70
71         dev_dbg(&pdev->dev, "ci_hdrc_msm_probe\n");
72
73         ci = devm_kzalloc(&pdev->dev, sizeof(*ci), GFP_KERNEL);
74         if (!ci)
75                 return -ENOMEM;
76         platform_set_drvdata(pdev, ci);
77
78         /*
79          * OTG(PHY) driver takes care of PHY initialization, clock management,
80          * powering up VBUS, mapping of registers address space and power
81          * management.
82          */
83         phy = devm_usb_get_phy_by_phandle(&pdev->dev, "usb-phy", 0);
84         if (IS_ERR(phy))
85                 return PTR_ERR(phy);
86
87         ci_hdrc_msm_platdata.usb_phy = phy;
88
89         reset = devm_reset_control_get(&pdev->dev, "core");
90         if (IS_ERR(reset))
91                 return PTR_ERR(reset);
92
93         ci->core_clk = clk = devm_clk_get(&pdev->dev, "core");
94         if (IS_ERR(clk))
95                 return PTR_ERR(clk);
96
97         ci->iface_clk = clk = devm_clk_get(&pdev->dev, "iface");
98         if (IS_ERR(clk))
99                 return PTR_ERR(clk);
100
101         ci->fs_clk = clk = devm_clk_get(&pdev->dev, "fs");
102         if (IS_ERR(clk)) {
103                 if (PTR_ERR(clk) == -EPROBE_DEFER)
104                         return -EPROBE_DEFER;
105                 ci->fs_clk = NULL;
106         }
107
108         ret = clk_prepare_enable(ci->fs_clk);
109         if (ret)
110                 return ret;
111
112         reset_control_assert(reset);
113         usleep_range(10000, 12000);
114         reset_control_deassert(reset);
115
116         clk_disable_unprepare(ci->fs_clk);
117
118         ret = clk_prepare_enable(ci->core_clk);
119         if (ret)
120                 return ret;
121
122         ret = clk_prepare_enable(ci->iface_clk);
123         if (ret)
124                 goto err_iface;
125
126         plat_ci = ci_hdrc_add_device(&pdev->dev,
127                                 pdev->resource, pdev->num_resources,
128                                 &ci_hdrc_msm_platdata);
129         if (IS_ERR(plat_ci)) {
130                 dev_err(&pdev->dev, "ci_hdrc_add_device failed!\n");
131                 ret = PTR_ERR(plat_ci);
132                 goto err_mux;
133         }
134
135         ci->ci = plat_ci;
136
137         pm_runtime_set_active(&pdev->dev);
138         pm_runtime_no_callbacks(&pdev->dev);
139         pm_runtime_enable(&pdev->dev);
140
141         return 0;
142
143 err_mux:
144         clk_disable_unprepare(ci->iface_clk);
145 err_iface:
146         clk_disable_unprepare(ci->core_clk);
147         return ret;
148 }
149
150 static int ci_hdrc_msm_remove(struct platform_device *pdev)
151 {
152         struct ci_hdrc_msm *ci = platform_get_drvdata(pdev);
153
154         pm_runtime_disable(&pdev->dev);
155         ci_hdrc_remove_device(ci->ci);
156         clk_disable_unprepare(ci->iface_clk);
157         clk_disable_unprepare(ci->core_clk);
158
159         return 0;
160 }
161
162 static const struct of_device_id msm_ci_dt_match[] = {
163         { .compatible = "qcom,ci-hdrc", },
164         { }
165 };
166 MODULE_DEVICE_TABLE(of, msm_ci_dt_match);
167
168 static struct platform_driver ci_hdrc_msm_driver = {
169         .probe = ci_hdrc_msm_probe,
170         .remove = ci_hdrc_msm_remove,
171         .driver = {
172                 .name = "msm_hsusb",
173                 .of_match_table = msm_ci_dt_match,
174         },
175 };
176
177 module_platform_driver(ci_hdrc_msm_driver);
178
179 MODULE_ALIAS("platform:msm_hsusb");
180 MODULE_ALIAS("platform:ci13xxx_msm");
181 MODULE_LICENSE("GPL v2");