diff options
Diffstat (limited to 'drivers/usb/chipidea')
-rw-r--r-- | drivers/usb/chipidea/Kconfig | 7 | ||||
-rw-r--r-- | drivers/usb/chipidea/Makefile | 2 | ||||
-rw-r--r-- | drivers/usb/chipidea/bits.h | 10 | ||||
-rw-r--r-- | drivers/usb/chipidea/ci.h | 8 | ||||
-rw-r--r-- | drivers/usb/chipidea/ci_hdrc_imx.c | 110 | ||||
-rw-r--r-- | drivers/usb/chipidea/ci_hdrc_imx.h | 17 | ||||
-rw-r--r-- | drivers/usb/chipidea/ci_hdrc_msm.c | 1 | ||||
-rw-r--r-- | drivers/usb/chipidea/core.c | 197 | ||||
-rw-r--r-- | drivers/usb/chipidea/host.c | 31 | ||||
-rw-r--r-- | drivers/usb/chipidea/host.h | 6 | ||||
-rw-r--r-- | drivers/usb/chipidea/otg.c | 120 | ||||
-rw-r--r-- | drivers/usb/chipidea/otg.h | 35 | ||||
-rw-r--r-- | drivers/usb/chipidea/udc.c | 78 | ||||
-rw-r--r-- | drivers/usb/chipidea/udc.h | 6 | ||||
-rw-r--r-- | drivers/usb/chipidea/usbmisc_imx.c | 95 |
15 files changed, 480 insertions, 243 deletions
diff --git a/drivers/usb/chipidea/Kconfig b/drivers/usb/chipidea/Kconfig index d1bd8ef1f9c1..4a851e15e58c 100644 --- a/drivers/usb/chipidea/Kconfig +++ b/drivers/usb/chipidea/Kconfig | |||
@@ -1,6 +1,6 @@ | |||
1 | config USB_CHIPIDEA | 1 | config USB_CHIPIDEA |
2 | tristate "ChipIdea Highspeed Dual Role Controller" | 2 | tristate "ChipIdea Highspeed Dual Role Controller" |
3 | depends on USB || USB_GADGET | 3 | depends on (USB_EHCI_HCD && USB_GADGET) || (USB_EHCI_HCD && !USB_GADGET) || (!USB_EHCI_HCD && USB_GADGET) |
4 | help | 4 | help |
5 | Say Y here if your system has a dual role high speed USB | 5 | Say Y here if your system has a dual role high speed USB |
6 | controller based on ChipIdea silicon IP. Currently, only the | 6 | controller based on ChipIdea silicon IP. Currently, only the |
@@ -12,15 +12,14 @@ if USB_CHIPIDEA | |||
12 | 12 | ||
13 | config USB_CHIPIDEA_UDC | 13 | config USB_CHIPIDEA_UDC |
14 | bool "ChipIdea device controller" | 14 | bool "ChipIdea device controller" |
15 | depends on USB_GADGET=y || (USB_CHIPIDEA=m && USB_GADGET=m) | 15 | depends on USB_GADGET |
16 | help | 16 | help |
17 | Say Y here to enable device controller functionality of the | 17 | Say Y here to enable device controller functionality of the |
18 | ChipIdea driver. | 18 | ChipIdea driver. |
19 | 19 | ||
20 | config USB_CHIPIDEA_HOST | 20 | config USB_CHIPIDEA_HOST |
21 | bool "ChipIdea host controller" | 21 | bool "ChipIdea host controller" |
22 | depends on USB=y | 22 | depends on USB_EHCI_HCD |
23 | depends on USB_EHCI_HCD=y || (USB_CHIPIDEA=m && USB_EHCI_HCD=m) | ||
24 | select USB_EHCI_ROOT_HUB_TT | 23 | select USB_EHCI_ROOT_HUB_TT |
25 | help | 24 | help |
26 | Say Y here to enable host controller functionality of the | 25 | Say Y here to enable host controller functionality of the |
diff --git a/drivers/usb/chipidea/Makefile b/drivers/usb/chipidea/Makefile index 6cf5f68dedd8..a99d980454a6 100644 --- a/drivers/usb/chipidea/Makefile +++ b/drivers/usb/chipidea/Makefile | |||
@@ -2,7 +2,7 @@ ccflags-$(CONFIG_USB_CHIPIDEA_DEBUG) := -DDEBUG | |||
2 | 2 | ||
3 | obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc.o | 3 | obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc.o |
4 | 4 | ||
5 | ci_hdrc-y := core.o | 5 | ci_hdrc-y := core.o otg.o |
6 | ci_hdrc-$(CONFIG_USB_CHIPIDEA_UDC) += udc.o | 6 | ci_hdrc-$(CONFIG_USB_CHIPIDEA_UDC) += udc.o |
7 | ci_hdrc-$(CONFIG_USB_CHIPIDEA_HOST) += host.o | 7 | ci_hdrc-$(CONFIG_USB_CHIPIDEA_HOST) += host.o |
8 | ci_hdrc-$(CONFIG_USB_CHIPIDEA_DEBUG) += debug.o | 8 | ci_hdrc-$(CONFIG_USB_CHIPIDEA_DEBUG) += debug.o |
diff --git a/drivers/usb/chipidea/bits.h b/drivers/usb/chipidea/bits.h index 1b23e354f9fb..464584c6ccae 100644 --- a/drivers/usb/chipidea/bits.h +++ b/drivers/usb/chipidea/bits.h | |||
@@ -79,11 +79,21 @@ | |||
79 | #define OTGSC_ASVIS BIT(18) | 79 | #define OTGSC_ASVIS BIT(18) |
80 | #define OTGSC_BSVIS BIT(19) | 80 | #define OTGSC_BSVIS BIT(19) |
81 | #define OTGSC_BSEIS BIT(20) | 81 | #define OTGSC_BSEIS BIT(20) |
82 | #define OTGSC_1MSIS BIT(21) | ||
83 | #define OTGSC_DPIS BIT(22) | ||
82 | #define OTGSC_IDIE BIT(24) | 84 | #define OTGSC_IDIE BIT(24) |
83 | #define OTGSC_AVVIE BIT(25) | 85 | #define OTGSC_AVVIE BIT(25) |
84 | #define OTGSC_ASVIE BIT(26) | 86 | #define OTGSC_ASVIE BIT(26) |
85 | #define OTGSC_BSVIE BIT(27) | 87 | #define OTGSC_BSVIE BIT(27) |
86 | #define OTGSC_BSEIE BIT(28) | 88 | #define OTGSC_BSEIE BIT(28) |
89 | #define OTGSC_1MSIE BIT(29) | ||
90 | #define OTGSC_DPIE BIT(30) | ||
91 | #define OTGSC_INT_EN_BITS (OTGSC_IDIE | OTGSC_AVVIE | OTGSC_ASVIE \ | ||
92 | | OTGSC_BSVIE | OTGSC_BSEIE | OTGSC_1MSIE \ | ||
93 | | OTGSC_DPIE) | ||
94 | #define OTGSC_INT_STATUS_BITS (OTGSC_IDIS | OTGSC_AVVIS | OTGSC_ASVIS \ | ||
95 | | OTGSC_BSVIS | OTGSC_BSEIS | OTGSC_1MSIS \ | ||
96 | | OTGSC_DPIS) | ||
87 | 97 | ||
88 | /* USBMODE */ | 98 | /* USBMODE */ |
89 | #define USBMODE_CM (0x03UL << 0) | 99 | #define USBMODE_CM (0x03UL << 0) |
diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index 33cb29f36e06..1c94fc5257f4 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h | |||
@@ -132,6 +132,9 @@ struct hw_bank { | |||
132 | * @transceiver: pointer to USB PHY, if any | 132 | * @transceiver: pointer to USB PHY, if any |
133 | * @hcd: pointer to usb_hcd for ehci host driver | 133 | * @hcd: pointer to usb_hcd for ehci host driver |
134 | * @debugfs: root dentry for this controller in debugfs | 134 | * @debugfs: root dentry for this controller in debugfs |
135 | * @id_event: indicates there is an id event, and handled at ci_otg_work | ||
136 | * @b_sess_valid_event: indicates there is a vbus event, and handled | ||
137 | * at ci_otg_work | ||
135 | */ | 138 | */ |
136 | struct ci_hdrc { | 139 | struct ci_hdrc { |
137 | struct device *dev; | 140 | struct device *dev; |
@@ -168,6 +171,8 @@ struct ci_hdrc { | |||
168 | struct usb_phy *transceiver; | 171 | struct usb_phy *transceiver; |
169 | struct usb_hcd *hcd; | 172 | struct usb_hcd *hcd; |
170 | struct dentry *debugfs; | 173 | struct dentry *debugfs; |
174 | bool id_event; | ||
175 | bool b_sess_valid_event; | ||
171 | }; | 176 | }; |
172 | 177 | ||
173 | static inline struct ci_role_driver *ci_role(struct ci_hdrc *ci) | 178 | static inline struct ci_role_driver *ci_role(struct ci_hdrc *ci) |
@@ -303,4 +308,7 @@ int hw_port_test_set(struct ci_hdrc *ci, u8 mode); | |||
303 | 308 | ||
304 | u8 hw_port_test_get(struct ci_hdrc *ci); | 309 | u8 hw_port_test_get(struct ci_hdrc *ci); |
305 | 310 | ||
311 | int hw_wait_reg(struct ci_hdrc *ci, enum ci_hw_regs reg, u32 mask, | ||
312 | u32 value, unsigned int timeout_ms); | ||
313 | |||
306 | #endif /* __DRIVERS_USB_CHIPIDEA_CI_H */ | 314 | #endif /* __DRIVERS_USB_CHIPIDEA_CI_H */ |
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index 14362c00db3f..74d998d9b45b 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c | |||
@@ -19,70 +19,56 @@ | |||
19 | #include <linux/dma-mapping.h> | 19 | #include <linux/dma-mapping.h> |
20 | #include <linux/usb/chipidea.h> | 20 | #include <linux/usb/chipidea.h> |
21 | #include <linux/clk.h> | 21 | #include <linux/clk.h> |
22 | #include <linux/regulator/consumer.h> | ||
23 | 22 | ||
24 | #include "ci.h" | 23 | #include "ci.h" |
25 | #include "ci_hdrc_imx.h" | 24 | #include "ci_hdrc_imx.h" |
26 | 25 | ||
27 | #define pdev_to_phy(pdev) \ | ||
28 | ((struct usb_phy *)platform_get_drvdata(pdev)) | ||
29 | |||
30 | struct ci_hdrc_imx_data { | 26 | struct ci_hdrc_imx_data { |
31 | struct usb_phy *phy; | 27 | struct usb_phy *phy; |
32 | struct platform_device *ci_pdev; | 28 | struct platform_device *ci_pdev; |
33 | struct clk *clk; | 29 | struct clk *clk; |
34 | struct regulator *reg_vbus; | 30 | struct imx_usbmisc_data *usbmisc_data; |
35 | }; | 31 | }; |
36 | 32 | ||
37 | static const struct usbmisc_ops *usbmisc_ops; | ||
38 | |||
39 | /* Common functions shared by usbmisc drivers */ | 33 | /* Common functions shared by usbmisc drivers */ |
40 | 34 | ||
41 | int usbmisc_set_ops(const struct usbmisc_ops *ops) | 35 | static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev) |
42 | { | ||
43 | if (usbmisc_ops) | ||
44 | return -EBUSY; | ||
45 | |||
46 | usbmisc_ops = ops; | ||
47 | |||
48 | return 0; | ||
49 | } | ||
50 | EXPORT_SYMBOL_GPL(usbmisc_set_ops); | ||
51 | |||
52 | void usbmisc_unset_ops(const struct usbmisc_ops *ops) | ||
53 | { | ||
54 | usbmisc_ops = NULL; | ||
55 | } | ||
56 | EXPORT_SYMBOL_GPL(usbmisc_unset_ops); | ||
57 | |||
58 | int usbmisc_get_init_data(struct device *dev, struct usbmisc_usb_device *usbdev) | ||
59 | { | 36 | { |
60 | struct device_node *np = dev->of_node; | 37 | struct device_node *np = dev->of_node; |
61 | struct of_phandle_args args; | 38 | struct of_phandle_args args; |
39 | struct imx_usbmisc_data *data; | ||
62 | int ret; | 40 | int ret; |
63 | 41 | ||
64 | usbdev->dev = dev; | 42 | /* |
43 | * In case the fsl,usbmisc property is not present this device doesn't | ||
44 | * need usbmisc. Return NULL (which is no error here) | ||
45 | */ | ||
46 | if (!of_get_property(np, "fsl,usbmisc", NULL)) | ||
47 | return NULL; | ||
48 | |||
49 | data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); | ||
50 | if (!data) | ||
51 | return ERR_PTR(-ENOMEM); | ||
65 | 52 | ||
66 | ret = of_parse_phandle_with_args(np, "fsl,usbmisc", "#index-cells", | 53 | ret = of_parse_phandle_with_args(np, "fsl,usbmisc", "#index-cells", |
67 | 0, &args); | 54 | 0, &args); |
68 | if (ret) { | 55 | if (ret) { |
69 | dev_err(dev, "Failed to parse property fsl,usbmisc, errno %d\n", | 56 | dev_err(dev, "Failed to parse property fsl,usbmisc, errno %d\n", |
70 | ret); | 57 | ret); |
71 | memset(usbdev, 0, sizeof(*usbdev)); | 58 | return ERR_PTR(ret); |
72 | return ret; | ||
73 | } | 59 | } |
74 | usbdev->index = args.args[0]; | 60 | |
61 | data->index = args.args[0]; | ||
75 | of_node_put(args.np); | 62 | of_node_put(args.np); |
76 | 63 | ||
77 | if (of_find_property(np, "disable-over-current", NULL)) | 64 | if (of_find_property(np, "disable-over-current", NULL)) |
78 | usbdev->disable_oc = 1; | 65 | data->disable_oc = 1; |
79 | 66 | ||
80 | if (of_find_property(np, "external-vbus-divider", NULL)) | 67 | if (of_find_property(np, "external-vbus-divider", NULL)) |
81 | usbdev->evdo = 1; | 68 | data->evdo = 1; |
82 | 69 | ||
83 | return 0; | 70 | return data; |
84 | } | 71 | } |
85 | EXPORT_SYMBOL_GPL(usbmisc_get_init_data); | ||
86 | 72 | ||
87 | /* End of common functions shared by usbmisc drivers*/ | 73 | /* End of common functions shared by usbmisc drivers*/ |
88 | 74 | ||
@@ -93,27 +79,19 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) | |||
93 | .name = "ci_hdrc_imx", | 79 | .name = "ci_hdrc_imx", |
94 | .capoffset = DEF_CAPOFFSET, | 80 | .capoffset = DEF_CAPOFFSET, |
95 | .flags = CI_HDRC_REQUIRE_TRANSCEIVER | | 81 | .flags = CI_HDRC_REQUIRE_TRANSCEIVER | |
96 | CI_HDRC_PULLUP_ON_VBUS | | ||
97 | CI_HDRC_DISABLE_STREAMING, | 82 | CI_HDRC_DISABLE_STREAMING, |
98 | }; | 83 | }; |
99 | struct resource *res; | ||
100 | int ret; | 84 | int ret; |
101 | 85 | ||
102 | if (of_find_property(pdev->dev.of_node, "fsl,usbmisc", NULL) | ||
103 | && !usbmisc_ops) | ||
104 | return -EPROBE_DEFER; | ||
105 | |||
106 | data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); | 86 | data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); |
107 | if (!data) { | 87 | if (!data) { |
108 | dev_err(&pdev->dev, "Failed to allocate ci_hdrc-imx data!\n"); | 88 | dev_err(&pdev->dev, "Failed to allocate ci_hdrc-imx data!\n"); |
109 | return -ENOMEM; | 89 | return -ENOMEM; |
110 | } | 90 | } |
111 | 91 | ||
112 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 92 | data->usbmisc_data = usbmisc_get_init_data(&pdev->dev); |
113 | if (!res) { | 93 | if (IS_ERR(data->usbmisc_data)) |
114 | dev_err(&pdev->dev, "Can't get device resources!\n"); | 94 | return PTR_ERR(data->usbmisc_data); |
115 | return -ENOENT; | ||
116 | } | ||
117 | 95 | ||
118 | data->clk = devm_clk_get(&pdev->dev, NULL); | 96 | data->clk = devm_clk_get(&pdev->dev, NULL); |
119 | if (IS_ERR(data->clk)) { | 97 | if (IS_ERR(data->clk)) { |
@@ -141,20 +119,6 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) | |||
141 | goto err_clk; | 119 | goto err_clk; |
142 | } | 120 | } |
143 | 121 | ||
144 | /* we only support host now, so enable vbus here */ | ||
145 | data->reg_vbus = devm_regulator_get(&pdev->dev, "vbus"); | ||
146 | if (!IS_ERR(data->reg_vbus)) { | ||
147 | ret = regulator_enable(data->reg_vbus); | ||
148 | if (ret) { | ||
149 | dev_err(&pdev->dev, | ||
150 | "Failed to enable vbus regulator, err=%d\n", | ||
151 | ret); | ||
152 | goto err_clk; | ||
153 | } | ||
154 | } else { | ||
155 | data->reg_vbus = NULL; | ||
156 | } | ||
157 | |||
158 | pdata.phy = data->phy; | 122 | pdata.phy = data->phy; |
159 | 123 | ||
160 | if (!pdev->dev.dma_mask) | 124 | if (!pdev->dev.dma_mask) |
@@ -162,12 +126,12 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) | |||
162 | if (!pdev->dev.coherent_dma_mask) | 126 | if (!pdev->dev.coherent_dma_mask) |
163 | pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); | 127 | pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); |
164 | 128 | ||
165 | if (usbmisc_ops && usbmisc_ops->init) { | 129 | if (data->usbmisc_data) { |
166 | ret = usbmisc_ops->init(&pdev->dev); | 130 | ret = imx_usbmisc_init(data->usbmisc_data); |
167 | if (ret) { | 131 | if (ret) { |
168 | dev_err(&pdev->dev, | 132 | dev_err(&pdev->dev, "usbmisc init failed, ret=%d\n", |
169 | "usbmisc init failed, ret=%d\n", ret); | 133 | ret); |
170 | goto err; | 134 | goto err_clk; |
171 | } | 135 | } |
172 | } | 136 | } |
173 | 137 | ||
@@ -179,14 +143,14 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) | |||
179 | dev_err(&pdev->dev, | 143 | dev_err(&pdev->dev, |
180 | "Can't register ci_hdrc platform device, err=%d\n", | 144 | "Can't register ci_hdrc platform device, err=%d\n", |
181 | ret); | 145 | ret); |
182 | goto err; | 146 | goto err_clk; |
183 | } | 147 | } |
184 | 148 | ||
185 | if (usbmisc_ops && usbmisc_ops->post) { | 149 | if (data->usbmisc_data) { |
186 | ret = usbmisc_ops->post(&pdev->dev); | 150 | ret = imx_usbmisc_init_post(data->usbmisc_data); |
187 | if (ret) { | 151 | if (ret) { |
188 | dev_err(&pdev->dev, | 152 | dev_err(&pdev->dev, "usbmisc post failed, ret=%d\n", |
189 | "usbmisc post failed, ret=%d\n", ret); | 153 | ret); |
190 | goto disable_device; | 154 | goto disable_device; |
191 | } | 155 | } |
192 | } | 156 | } |
@@ -200,9 +164,6 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) | |||
200 | 164 | ||
201 | disable_device: | 165 | disable_device: |
202 | ci_hdrc_remove_device(data->ci_pdev); | 166 | ci_hdrc_remove_device(data->ci_pdev); |
203 | err: | ||
204 | if (data->reg_vbus) | ||
205 | regulator_disable(data->reg_vbus); | ||
206 | err_clk: | 167 | err_clk: |
207 | clk_disable_unprepare(data->clk); | 168 | clk_disable_unprepare(data->clk); |
208 | return ret; | 169 | return ret; |
@@ -215,13 +176,8 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev) | |||
215 | pm_runtime_disable(&pdev->dev); | 176 | pm_runtime_disable(&pdev->dev); |
216 | ci_hdrc_remove_device(data->ci_pdev); | 177 | ci_hdrc_remove_device(data->ci_pdev); |
217 | 178 | ||
218 | if (data->reg_vbus) | 179 | if (data->phy) |
219 | regulator_disable(data->reg_vbus); | ||
220 | |||
221 | if (data->phy) { | ||
222 | usb_phy_shutdown(data->phy); | 180 | usb_phy_shutdown(data->phy); |
223 | module_put(data->phy->dev->driver->owner); | ||
224 | } | ||
225 | 181 | ||
226 | clk_disable_unprepare(data->clk); | 182 | clk_disable_unprepare(data->clk); |
227 | 183 | ||
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.h b/drivers/usb/chipidea/ci_hdrc_imx.h index 550bfa457620..c7271590dd0a 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.h +++ b/drivers/usb/chipidea/ci_hdrc_imx.h | |||
@@ -9,23 +9,12 @@ | |||
9 | * http://www.gnu.org/copyleft/gpl.html | 9 | * http://www.gnu.org/copyleft/gpl.html |
10 | */ | 10 | */ |
11 | 11 | ||
12 | /* Used to set SoC specific callbacks */ | 12 | struct imx_usbmisc_data { |
13 | struct usbmisc_ops { | ||
14 | /* It's called once when probe a usb device */ | ||
15 | int (*init)(struct device *dev); | ||
16 | /* It's called once after adding a usb device */ | ||
17 | int (*post)(struct device *dev); | ||
18 | }; | ||
19 | |||
20 | struct usbmisc_usb_device { | ||
21 | struct device *dev; /* usb controller device */ | ||
22 | int index; | 13 | int index; |
23 | 14 | ||
24 | unsigned int disable_oc:1; /* over current detect disabled */ | 15 | unsigned int disable_oc:1; /* over current detect disabled */ |
25 | unsigned int evdo:1; /* set external vbus divider option */ | 16 | unsigned int evdo:1; /* set external vbus divider option */ |
26 | }; | 17 | }; |
27 | 18 | ||
28 | int usbmisc_set_ops(const struct usbmisc_ops *ops); | 19 | int imx_usbmisc_init(struct imx_usbmisc_data *); |
29 | void usbmisc_unset_ops(const struct usbmisc_ops *ops); | 20 | int imx_usbmisc_init_post(struct imx_usbmisc_data *); |
30 | int | ||
31 | usbmisc_get_init_data(struct device *dev, struct usbmisc_usb_device *usbdev); | ||
diff --git a/drivers/usb/chipidea/ci_hdrc_msm.c b/drivers/usb/chipidea/ci_hdrc_msm.c index fb657ef50a9c..2d51d852b474 100644 --- a/drivers/usb/chipidea/ci_hdrc_msm.c +++ b/drivers/usb/chipidea/ci_hdrc_msm.c | |||
@@ -49,7 +49,6 @@ static struct ci_hdrc_platform_data ci_hdrc_msm_platdata = { | |||
49 | .name = "ci_hdrc_msm", | 49 | .name = "ci_hdrc_msm", |
50 | .flags = CI_HDRC_REGS_SHARED | | 50 | .flags = CI_HDRC_REGS_SHARED | |
51 | CI_HDRC_REQUIRE_TRANSCEIVER | | 51 | CI_HDRC_REQUIRE_TRANSCEIVER | |
52 | CI_HDRC_PULLUP_ON_VBUS | | ||
53 | CI_HDRC_DISABLE_STREAMING, | 52 | CI_HDRC_DISABLE_STREAMING, |
54 | 53 | ||
55 | .notify_event = ci_hdrc_msm_notify_event, | 54 | .notify_event = ci_hdrc_msm_notify_event, |
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index a5df24c578fc..94626409559a 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c | |||
@@ -65,12 +65,14 @@ | |||
65 | #include <linux/usb/chipidea.h> | 65 | #include <linux/usb/chipidea.h> |
66 | #include <linux/usb/of.h> | 66 | #include <linux/usb/of.h> |
67 | #include <linux/phy.h> | 67 | #include <linux/phy.h> |
68 | #include <linux/regulator/consumer.h> | ||
68 | 69 | ||
69 | #include "ci.h" | 70 | #include "ci.h" |
70 | #include "udc.h" | 71 | #include "udc.h" |
71 | #include "bits.h" | 72 | #include "bits.h" |
72 | #include "host.h" | 73 | #include "host.h" |
73 | #include "debug.h" | 74 | #include "debug.h" |
75 | #include "otg.h" | ||
74 | 76 | ||
75 | /* Controller register map */ | 77 | /* Controller register map */ |
76 | static uintptr_t ci_regs_nolpm[] = { | 78 | static uintptr_t ci_regs_nolpm[] = { |
@@ -197,6 +199,12 @@ static int hw_device_init(struct ci_hdrc *ci, void __iomem *base) | |||
197 | if (ci->hw_ep_max > ENDPT_MAX) | 199 | if (ci->hw_ep_max > ENDPT_MAX) |
198 | return -ENODEV; | 200 | return -ENODEV; |
199 | 201 | ||
202 | /* Disable all interrupts bits */ | ||
203 | hw_write(ci, OP_USBINTR, 0xffffffff, 0); | ||
204 | |||
205 | /* Clear all interrupts status bits*/ | ||
206 | hw_write(ci, OP_USBSTS, 0xffffffff, 0xffffffff); | ||
207 | |||
200 | dev_dbg(ci->dev, "ChipIdea HDRC found, lpm: %d; cap: %p op: %p\n", | 208 | dev_dbg(ci->dev, "ChipIdea HDRC found, lpm: %d; cap: %p op: %p\n", |
201 | ci->hw_bank.lpm, ci->hw_bank.cap, ci->hw_bank.op); | 209 | ci->hw_bank.lpm, ci->hw_bank.cap, ci->hw_bank.op); |
202 | 210 | ||
@@ -264,8 +272,6 @@ int hw_device_reset(struct ci_hdrc *ci, u32 mode) | |||
264 | while (hw_read(ci, OP_USBCMD, USBCMD_RST)) | 272 | while (hw_read(ci, OP_USBCMD, USBCMD_RST)) |
265 | udelay(10); /* not RTOS friendly */ | 273 | udelay(10); /* not RTOS friendly */ |
266 | 274 | ||
267 | hw_phymode_configure(ci); | ||
268 | |||
269 | if (ci->platdata->notify_event) | 275 | if (ci->platdata->notify_event) |
270 | ci->platdata->notify_event(ci, | 276 | ci->platdata->notify_event(ci, |
271 | CI_HDRC_CONTROLLER_RESET_EVENT); | 277 | CI_HDRC_CONTROLLER_RESET_EVENT); |
@@ -289,37 +295,35 @@ int hw_device_reset(struct ci_hdrc *ci, u32 mode) | |||
289 | } | 295 | } |
290 | 296 | ||
291 | /** | 297 | /** |
292 | * ci_otg_role - pick role based on ID pin state | 298 | * hw_wait_reg: wait the register value |
299 | * | ||
300 | * Sometimes, it needs to wait register value before going on. | ||
301 | * Eg, when switch to device mode, the vbus value should be lower | ||
302 | * than OTGSC_BSV before connects to host. | ||
303 | * | ||
293 | * @ci: the controller | 304 | * @ci: the controller |
305 | * @reg: register index | ||
306 | * @mask: mast bit | ||
307 | * @value: the bit value to wait | ||
308 | * @timeout_ms: timeout in millisecond | ||
309 | * | ||
310 | * This function returns an error code if timeout | ||
294 | */ | 311 | */ |
295 | static enum ci_role ci_otg_role(struct ci_hdrc *ci) | 312 | int hw_wait_reg(struct ci_hdrc *ci, enum ci_hw_regs reg, u32 mask, |
296 | { | 313 | u32 value, unsigned int timeout_ms) |
297 | u32 sts = hw_read(ci, OP_OTGSC, ~0); | ||
298 | enum ci_role role = sts & OTGSC_ID | ||
299 | ? CI_ROLE_GADGET | ||
300 | : CI_ROLE_HOST; | ||
301 | |||
302 | return role; | ||
303 | } | ||
304 | |||
305 | /** | ||
306 | * ci_role_work - perform role changing based on ID pin | ||
307 | * @work: work struct | ||
308 | */ | ||
309 | static void ci_role_work(struct work_struct *work) | ||
310 | { | 314 | { |
311 | struct ci_hdrc *ci = container_of(work, struct ci_hdrc, work); | 315 | unsigned long elapse = jiffies + msecs_to_jiffies(timeout_ms); |
312 | enum ci_role role = ci_otg_role(ci); | 316 | |
313 | 317 | while (hw_read(ci, reg, mask) != value) { | |
314 | if (role != ci->role) { | 318 | if (time_after(jiffies, elapse)) { |
315 | dev_dbg(ci->dev, "switching from %s to %s\n", | 319 | dev_err(ci->dev, "timeout waiting for %08x in %d\n", |
316 | ci_role(ci)->name, ci->roles[role]->name); | 320 | mask, reg); |
317 | 321 | return -ETIMEDOUT; | |
318 | ci_role_stop(ci); | 322 | } |
319 | ci_role_start(ci, role); | 323 | msleep(20); |
320 | } | 324 | } |
321 | 325 | ||
322 | enable_irq(ci->irq); | 326 | return 0; |
323 | } | 327 | } |
324 | 328 | ||
325 | static irqreturn_t ci_irq(int irq, void *data) | 329 | static irqreturn_t ci_irq(int irq, void *data) |
@@ -331,19 +335,55 @@ static irqreturn_t ci_irq(int irq, void *data) | |||
331 | if (ci->is_otg) | 335 | if (ci->is_otg) |
332 | otgsc = hw_read(ci, OP_OTGSC, ~0); | 336 | otgsc = hw_read(ci, OP_OTGSC, ~0); |
333 | 337 | ||
334 | if (ci->role != CI_ROLE_END) | 338 | /* |
335 | ret = ci_role(ci)->irq(ci); | 339 | * Handle id change interrupt, it indicates device/host function |
340 | * switch. | ||
341 | */ | ||
342 | if (ci->is_otg && (otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS)) { | ||
343 | ci->id_event = true; | ||
344 | ci_clear_otg_interrupt(ci, OTGSC_IDIS); | ||
345 | disable_irq_nosync(ci->irq); | ||
346 | queue_work(ci->wq, &ci->work); | ||
347 | return IRQ_HANDLED; | ||
348 | } | ||
336 | 349 | ||
337 | if (ci->is_otg && (otgsc & OTGSC_IDIS)) { | 350 | /* |
338 | hw_write(ci, OP_OTGSC, OTGSC_IDIS, OTGSC_IDIS); | 351 | * Handle vbus change interrupt, it indicates device connection |
352 | * and disconnection events. | ||
353 | */ | ||
354 | if (ci->is_otg && (otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS)) { | ||
355 | ci->b_sess_valid_event = true; | ||
356 | ci_clear_otg_interrupt(ci, OTGSC_BSVIS); | ||
339 | disable_irq_nosync(ci->irq); | 357 | disable_irq_nosync(ci->irq); |
340 | queue_work(ci->wq, &ci->work); | 358 | queue_work(ci->wq, &ci->work); |
341 | ret = IRQ_HANDLED; | 359 | return IRQ_HANDLED; |
342 | } | 360 | } |
343 | 361 | ||
362 | /* Handle device/host interrupt */ | ||
363 | if (ci->role != CI_ROLE_END) | ||
364 | ret = ci_role(ci)->irq(ci); | ||
365 | |||
344 | return ret; | 366 | return ret; |
345 | } | 367 | } |
346 | 368 | ||
369 | static int ci_get_platdata(struct device *dev, | ||
370 | struct ci_hdrc_platform_data *platdata) | ||
371 | { | ||
372 | /* Get the vbus regulator */ | ||
373 | platdata->reg_vbus = devm_regulator_get(dev, "vbus"); | ||
374 | if (PTR_ERR(platdata->reg_vbus) == -EPROBE_DEFER) { | ||
375 | return -EPROBE_DEFER; | ||
376 | } else if (PTR_ERR(platdata->reg_vbus) == -ENODEV) { | ||
377 | platdata->reg_vbus = NULL; /* no vbus regualator is needed */ | ||
378 | } else if (IS_ERR(platdata->reg_vbus)) { | ||
379 | dev_err(dev, "Getting regulator error: %ld\n", | ||
380 | PTR_ERR(platdata->reg_vbus)); | ||
381 | return PTR_ERR(platdata->reg_vbus); | ||
382 | } | ||
383 | |||
384 | return 0; | ||
385 | } | ||
386 | |||
347 | static DEFINE_IDA(ci_ida); | 387 | static DEFINE_IDA(ci_ida); |
348 | 388 | ||
349 | struct platform_device *ci_hdrc_add_device(struct device *dev, | 389 | struct platform_device *ci_hdrc_add_device(struct device *dev, |
@@ -353,6 +393,10 @@ struct platform_device *ci_hdrc_add_device(struct device *dev, | |||
353 | struct platform_device *pdev; | 393 | struct platform_device *pdev; |
354 | int id, ret; | 394 | int id, ret; |
355 | 395 | ||
396 | ret = ci_get_platdata(dev, platdata); | ||
397 | if (ret) | ||
398 | return ERR_PTR(ret); | ||
399 | |||
356 | id = ida_simple_get(&ci_ida, 0, 0, GFP_KERNEL); | 400 | id = ida_simple_get(&ci_ida, 0, 0, GFP_KERNEL); |
357 | if (id < 0) | 401 | if (id < 0) |
358 | return ERR_PTR(id); | 402 | return ERR_PTR(id); |
@@ -398,6 +442,29 @@ void ci_hdrc_remove_device(struct platform_device *pdev) | |||
398 | } | 442 | } |
399 | EXPORT_SYMBOL_GPL(ci_hdrc_remove_device); | 443 | EXPORT_SYMBOL_GPL(ci_hdrc_remove_device); |
400 | 444 | ||
445 | static inline void ci_role_destroy(struct ci_hdrc *ci) | ||
446 | { | ||
447 | ci_hdrc_gadget_destroy(ci); | ||
448 | ci_hdrc_host_destroy(ci); | ||
449 | if (ci->is_otg) | ||
450 | ci_hdrc_otg_destroy(ci); | ||
451 | } | ||
452 | |||
453 | static void ci_get_otg_capable(struct ci_hdrc *ci) | ||
454 | { | ||
455 | if (ci->platdata->flags & CI_HDRC_DUAL_ROLE_NOT_OTG) | ||
456 | ci->is_otg = false; | ||
457 | else | ||
458 | ci->is_otg = (hw_read(ci, CAP_DCCPARAMS, | ||
459 | DCCPARAMS_DC | DCCPARAMS_HC) | ||
460 | == (DCCPARAMS_DC | DCCPARAMS_HC)); | ||
461 | if (ci->is_otg) { | ||
462 | dev_dbg(ci->dev, "It is OTG capable controller\n"); | ||
463 | ci_disable_otg_interrupt(ci, OTGSC_INT_EN_BITS); | ||
464 | ci_clear_otg_interrupt(ci, OTGSC_INT_STATUS_BITS); | ||
465 | } | ||
466 | } | ||
467 | |||
401 | static int ci_hdrc_probe(struct platform_device *pdev) | 468 | static int ci_hdrc_probe(struct platform_device *pdev) |
402 | { | 469 | { |
403 | struct device *dev = &pdev->dev; | 470 | struct device *dev = &pdev->dev; |
@@ -406,15 +473,13 @@ static int ci_hdrc_probe(struct platform_device *pdev) | |||
406 | void __iomem *base; | 473 | void __iomem *base; |
407 | int ret; | 474 | int ret; |
408 | enum usb_dr_mode dr_mode; | 475 | enum usb_dr_mode dr_mode; |
476 | struct device_node *of_node = dev->of_node ?: dev->parent->of_node; | ||
409 | 477 | ||
410 | if (!dev->platform_data) { | 478 | if (!dev->platform_data) { |
411 | dev_err(dev, "platform data missing\n"); | 479 | dev_err(dev, "platform data missing\n"); |
412 | return -ENODEV; | 480 | return -ENODEV; |
413 | } | 481 | } |
414 | 482 | ||
415 | if (!dev->of_node && dev->parent) | ||
416 | dev->of_node = dev->parent->of_node; | ||
417 | |||
418 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 483 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
419 | base = devm_ioremap_resource(dev, res); | 484 | base = devm_ioremap_resource(dev, res); |
420 | if (IS_ERR(base)) | 485 | if (IS_ERR(base)) |
@@ -447,18 +512,15 @@ static int ci_hdrc_probe(struct platform_device *pdev) | |||
447 | return -ENODEV; | 512 | return -ENODEV; |
448 | } | 513 | } |
449 | 514 | ||
450 | INIT_WORK(&ci->work, ci_role_work); | 515 | ci_get_otg_capable(ci); |
451 | ci->wq = create_singlethread_workqueue("ci_otg"); | ||
452 | if (!ci->wq) { | ||
453 | dev_err(dev, "can't create workqueue\n"); | ||
454 | return -ENODEV; | ||
455 | } | ||
456 | 516 | ||
457 | if (!ci->platdata->phy_mode) | 517 | if (!ci->platdata->phy_mode) |
458 | ci->platdata->phy_mode = of_usb_get_phy_mode(dev->of_node); | 518 | ci->platdata->phy_mode = of_usb_get_phy_mode(of_node); |
519 | |||
520 | hw_phymode_configure(ci); | ||
459 | 521 | ||
460 | if (!ci->platdata->dr_mode) | 522 | if (!ci->platdata->dr_mode) |
461 | ci->platdata->dr_mode = of_usb_get_dr_mode(dev->of_node); | 523 | ci->platdata->dr_mode = of_usb_get_dr_mode(of_node); |
462 | 524 | ||
463 | if (ci->platdata->dr_mode == USB_DR_MODE_UNKNOWN) | 525 | if (ci->platdata->dr_mode == USB_DR_MODE_UNKNOWN) |
464 | ci->platdata->dr_mode = USB_DR_MODE_OTG; | 526 | ci->platdata->dr_mode = USB_DR_MODE_OTG; |
@@ -479,15 +541,34 @@ static int ci_hdrc_probe(struct platform_device *pdev) | |||
479 | 541 | ||
480 | if (!ci->roles[CI_ROLE_HOST] && !ci->roles[CI_ROLE_GADGET]) { | 542 | if (!ci->roles[CI_ROLE_HOST] && !ci->roles[CI_ROLE_GADGET]) { |
481 | dev_err(dev, "no supported roles\n"); | 543 | dev_err(dev, "no supported roles\n"); |
482 | ret = -ENODEV; | 544 | return -ENODEV; |
483 | goto rm_wq; | 545 | } |
546 | |||
547 | if (ci->is_otg) { | ||
548 | ret = ci_hdrc_otg_init(ci); | ||
549 | if (ret) { | ||
550 | dev_err(dev, "init otg fails, ret = %d\n", ret); | ||
551 | goto stop; | ||
552 | } | ||
484 | } | 553 | } |
485 | 554 | ||
486 | if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET]) { | 555 | if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET]) { |
487 | ci->is_otg = true; | 556 | if (ci->is_otg) { |
488 | /* ID pin needs 1ms debouce time, we delay 2ms for safe */ | 557 | /* |
489 | mdelay(2); | 558 | * ID pin needs 1ms debouce time, |
490 | ci->role = ci_otg_role(ci); | 559 | * we delay 2ms for safe. |
560 | */ | ||
561 | mdelay(2); | ||
562 | ci->role = ci_otg_role(ci); | ||
563 | ci_enable_otg_interrupt(ci, OTGSC_IDIE); | ||
564 | } else { | ||
565 | /* | ||
566 | * If the controller is not OTG capable, but support | ||
567 | * role switch, the defalt role is gadget, and the | ||
568 | * user can switch it through debugfs. | ||
569 | */ | ||
570 | ci->role = CI_ROLE_GADGET; | ||
571 | } | ||
491 | } else { | 572 | } else { |
492 | ci->role = ci->roles[CI_ROLE_HOST] | 573 | ci->role = ci->roles[CI_ROLE_HOST] |
493 | ? CI_ROLE_HOST | 574 | ? CI_ROLE_HOST |
@@ -497,8 +578,7 @@ static int ci_hdrc_probe(struct platform_device *pdev) | |||
497 | ret = ci_role_start(ci, ci->role); | 578 | ret = ci_role_start(ci, ci->role); |
498 | if (ret) { | 579 | if (ret) { |
499 | dev_err(dev, "can't start %s role\n", ci_role(ci)->name); | 580 | dev_err(dev, "can't start %s role\n", ci_role(ci)->name); |
500 | ret = -ENODEV; | 581 | goto stop; |
501 | goto rm_wq; | ||
502 | } | 582 | } |
503 | 583 | ||
504 | platform_set_drvdata(pdev, ci); | 584 | platform_set_drvdata(pdev, ci); |
@@ -507,19 +587,13 @@ static int ci_hdrc_probe(struct platform_device *pdev) | |||
507 | if (ret) | 587 | if (ret) |
508 | goto stop; | 588 | goto stop; |
509 | 589 | ||
510 | if (ci->is_otg) | ||
511 | hw_write(ci, OP_OTGSC, OTGSC_IDIE, OTGSC_IDIE); | ||
512 | |||
513 | ret = dbg_create_files(ci); | 590 | ret = dbg_create_files(ci); |
514 | if (!ret) | 591 | if (!ret) |
515 | return 0; | 592 | return 0; |
516 | 593 | ||
517 | free_irq(ci->irq, ci); | 594 | free_irq(ci->irq, ci); |
518 | stop: | 595 | stop: |
519 | ci_role_stop(ci); | 596 | ci_role_destroy(ci); |
520 | rm_wq: | ||
521 | flush_workqueue(ci->wq); | ||
522 | destroy_workqueue(ci->wq); | ||
523 | 597 | ||
524 | return ret; | 598 | return ret; |
525 | } | 599 | } |
@@ -529,10 +603,8 @@ static int ci_hdrc_remove(struct platform_device *pdev) | |||
529 | struct ci_hdrc *ci = platform_get_drvdata(pdev); | 603 | struct ci_hdrc *ci = platform_get_drvdata(pdev); |
530 | 604 | ||
531 | dbg_remove_files(ci); | 605 | dbg_remove_files(ci); |
532 | flush_workqueue(ci->wq); | ||
533 | destroy_workqueue(ci->wq); | ||
534 | free_irq(ci->irq, ci); | 606 | free_irq(ci->irq, ci); |
535 | ci_role_stop(ci); | 607 | ci_role_destroy(ci); |
536 | 608 | ||
537 | return 0; | 609 | return 0; |
538 | } | 610 | } |
@@ -548,7 +620,6 @@ static struct platform_driver ci_hdrc_driver = { | |||
548 | module_platform_driver(ci_hdrc_driver); | 620 | module_platform_driver(ci_hdrc_driver); |
549 | 621 | ||
550 | MODULE_ALIAS("platform:ci_hdrc"); | 622 | MODULE_ALIAS("platform:ci_hdrc"); |
551 | MODULE_ALIAS("platform:ci13xxx"); | ||
552 | MODULE_LICENSE("GPL v2"); | 623 | MODULE_LICENSE("GPL v2"); |
553 | MODULE_AUTHOR("David Lopo <dlopo@chipidea.mips.com>"); | 624 | MODULE_AUTHOR("David Lopo <dlopo@chipidea.mips.com>"); |
554 | MODULE_DESCRIPTION("ChipIdea HDRC Driver"); | 625 | MODULE_DESCRIPTION("ChipIdea HDRC Driver"); |
diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c index 40d0fda4f66c..6f96795dd20c 100644 --- a/drivers/usb/chipidea/host.c +++ b/drivers/usb/chipidea/host.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include <linux/usb.h> | 24 | #include <linux/usb.h> |
25 | #include <linux/usb/hcd.h> | 25 | #include <linux/usb/hcd.h> |
26 | #include <linux/usb/chipidea.h> | 26 | #include <linux/usb/chipidea.h> |
27 | #include <linux/regulator/consumer.h> | ||
27 | 28 | ||
28 | #include "../host/ehci.h" | 29 | #include "../host/ehci.h" |
29 | 30 | ||
@@ -63,10 +64,21 @@ static int host_start(struct ci_hdrc *ci) | |||
63 | ehci = hcd_to_ehci(hcd); | 64 | ehci = hcd_to_ehci(hcd); |
64 | ehci->caps = ci->hw_bank.cap; | 65 | ehci->caps = ci->hw_bank.cap; |
65 | ehci->has_hostpc = ci->hw_bank.lpm; | 66 | ehci->has_hostpc = ci->hw_bank.lpm; |
67 | ehci->has_tdi_phy_lpm = ci->hw_bank.lpm; | ||
68 | |||
69 | if (ci->platdata->reg_vbus) { | ||
70 | ret = regulator_enable(ci->platdata->reg_vbus); | ||
71 | if (ret) { | ||
72 | dev_err(ci->dev, | ||
73 | "Failed to enable vbus regulator, ret=%d\n", | ||
74 | ret); | ||
75 | goto put_hcd; | ||
76 | } | ||
77 | } | ||
66 | 78 | ||
67 | ret = usb_add_hcd(hcd, 0, 0); | 79 | ret = usb_add_hcd(hcd, 0, 0); |
68 | if (ret) | 80 | if (ret) |
69 | usb_put_hcd(hcd); | 81 | goto disable_reg; |
70 | else | 82 | else |
71 | ci->hcd = hcd; | 83 | ci->hcd = hcd; |
72 | 84 | ||
@@ -74,6 +86,14 @@ static int host_start(struct ci_hdrc *ci) | |||
74 | hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS); | 86 | hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS); |
75 | 87 | ||
76 | return ret; | 88 | return ret; |
89 | |||
90 | disable_reg: | ||
91 | regulator_disable(ci->platdata->reg_vbus); | ||
92 | |||
93 | put_hcd: | ||
94 | usb_put_hcd(hcd); | ||
95 | |||
96 | return ret; | ||
77 | } | 97 | } |
78 | 98 | ||
79 | static void host_stop(struct ci_hdrc *ci) | 99 | static void host_stop(struct ci_hdrc *ci) |
@@ -82,6 +102,15 @@ static void host_stop(struct ci_hdrc *ci) | |||
82 | 102 | ||
83 | usb_remove_hcd(hcd); | 103 | usb_remove_hcd(hcd); |
84 | usb_put_hcd(hcd); | 104 | usb_put_hcd(hcd); |
105 | if (ci->platdata->reg_vbus) | ||
106 | regulator_disable(ci->platdata->reg_vbus); | ||
107 | } | ||
108 | |||
109 | |||
110 | void ci_hdrc_host_destroy(struct ci_hdrc *ci) | ||
111 | { | ||
112 | if (ci->role == CI_ROLE_HOST) | ||
113 | host_stop(ci); | ||
85 | } | 114 | } |
86 | 115 | ||
87 | int ci_hdrc_host_init(struct ci_hdrc *ci) | 116 | int ci_hdrc_host_init(struct ci_hdrc *ci) |
diff --git a/drivers/usb/chipidea/host.h b/drivers/usb/chipidea/host.h index 058875c15333..5707bf379bfb 100644 --- a/drivers/usb/chipidea/host.h +++ b/drivers/usb/chipidea/host.h | |||
@@ -4,6 +4,7 @@ | |||
4 | #ifdef CONFIG_USB_CHIPIDEA_HOST | 4 | #ifdef CONFIG_USB_CHIPIDEA_HOST |
5 | 5 | ||
6 | int ci_hdrc_host_init(struct ci_hdrc *ci); | 6 | int ci_hdrc_host_init(struct ci_hdrc *ci); |
7 | void ci_hdrc_host_destroy(struct ci_hdrc *ci); | ||
7 | 8 | ||
8 | #else | 9 | #else |
9 | 10 | ||
@@ -12,6 +13,11 @@ static inline int ci_hdrc_host_init(struct ci_hdrc *ci) | |||
12 | return -ENXIO; | 13 | return -ENXIO; |
13 | } | 14 | } |
14 | 15 | ||
16 | static inline void ci_hdrc_host_destroy(struct ci_hdrc *ci) | ||
17 | { | ||
18 | |||
19 | } | ||
20 | |||
15 | #endif | 21 | #endif |
16 | 22 | ||
17 | #endif /* __DRIVERS_USB_CHIPIDEA_HOST_H */ | 23 | #endif /* __DRIVERS_USB_CHIPIDEA_HOST_H */ |
diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c new file mode 100644 index 000000000000..39bd7ec8bf75 --- /dev/null +++ b/drivers/usb/chipidea/otg.c | |||
@@ -0,0 +1,120 @@ | |||
1 | /* | ||
2 | * otg.c - ChipIdea USB IP core OTG driver | ||
3 | * | ||
4 | * Copyright (C) 2013 Freescale Semiconductor, Inc. | ||
5 | * | ||
6 | * Author: Peter Chen | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | /* | ||
14 | * This file mainly handles otgsc register, it may include OTG operation | ||
15 | * in the future. | ||
16 | */ | ||
17 | |||
18 | #include <linux/usb/otg.h> | ||
19 | #include <linux/usb/gadget.h> | ||
20 | #include <linux/usb/chipidea.h> | ||
21 | |||
22 | #include "ci.h" | ||
23 | #include "bits.h" | ||
24 | #include "otg.h" | ||
25 | |||
26 | /** | ||
27 | * ci_otg_role - pick role based on ID pin state | ||
28 | * @ci: the controller | ||
29 | */ | ||
30 | enum ci_role ci_otg_role(struct ci_hdrc *ci) | ||
31 | { | ||
32 | u32 sts = hw_read(ci, OP_OTGSC, ~0); | ||
33 | enum ci_role role = sts & OTGSC_ID | ||
34 | ? CI_ROLE_GADGET | ||
35 | : CI_ROLE_HOST; | ||
36 | |||
37 | return role; | ||
38 | } | ||
39 | |||
40 | void ci_handle_vbus_change(struct ci_hdrc *ci) | ||
41 | { | ||
42 | u32 otgsc; | ||
43 | |||
44 | if (!ci->is_otg) | ||
45 | return; | ||
46 | |||
47 | otgsc = hw_read(ci, OP_OTGSC, ~0); | ||
48 | |||
49 | if (otgsc & OTGSC_BSV) | ||
50 | usb_gadget_vbus_connect(&ci->gadget); | ||
51 | else | ||
52 | usb_gadget_vbus_disconnect(&ci->gadget); | ||
53 | } | ||
54 | |||
55 | #define CI_VBUS_STABLE_TIMEOUT_MS 5000 | ||
56 | static void ci_handle_id_switch(struct ci_hdrc *ci) | ||
57 | { | ||
58 | enum ci_role role = ci_otg_role(ci); | ||
59 | |||
60 | if (role != ci->role) { | ||
61 | dev_dbg(ci->dev, "switching from %s to %s\n", | ||
62 | ci_role(ci)->name, ci->roles[role]->name); | ||
63 | |||
64 | ci_role_stop(ci); | ||
65 | /* wait vbus lower than OTGSC_BSV */ | ||
66 | hw_wait_reg(ci, OP_OTGSC, OTGSC_BSV, 0, | ||
67 | CI_VBUS_STABLE_TIMEOUT_MS); | ||
68 | ci_role_start(ci, role); | ||
69 | } | ||
70 | } | ||
71 | /** | ||
72 | * ci_otg_work - perform otg (vbus/id) event handle | ||
73 | * @work: work struct | ||
74 | */ | ||
75 | static void ci_otg_work(struct work_struct *work) | ||
76 | { | ||
77 | struct ci_hdrc *ci = container_of(work, struct ci_hdrc, work); | ||
78 | |||
79 | if (ci->id_event) { | ||
80 | ci->id_event = false; | ||
81 | ci_handle_id_switch(ci); | ||
82 | } else if (ci->b_sess_valid_event) { | ||
83 | ci->b_sess_valid_event = false; | ||
84 | ci_handle_vbus_change(ci); | ||
85 | } else | ||
86 | dev_err(ci->dev, "unexpected event occurs at %s\n", __func__); | ||
87 | |||
88 | enable_irq(ci->irq); | ||
89 | } | ||
90 | |||
91 | |||
92 | /** | ||
93 | * ci_hdrc_otg_init - initialize otg struct | ||
94 | * ci: the controller | ||
95 | */ | ||
96 | int ci_hdrc_otg_init(struct ci_hdrc *ci) | ||
97 | { | ||
98 | INIT_WORK(&ci->work, ci_otg_work); | ||
99 | ci->wq = create_singlethread_workqueue("ci_otg"); | ||
100 | if (!ci->wq) { | ||
101 | dev_err(ci->dev, "can't create workqueue\n"); | ||
102 | return -ENODEV; | ||
103 | } | ||
104 | |||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | /** | ||
109 | * ci_hdrc_otg_destroy - destroy otg struct | ||
110 | * ci: the controller | ||
111 | */ | ||
112 | void ci_hdrc_otg_destroy(struct ci_hdrc *ci) | ||
113 | { | ||
114 | if (ci->wq) { | ||
115 | flush_workqueue(ci->wq); | ||
116 | destroy_workqueue(ci->wq); | ||
117 | } | ||
118 | ci_disable_otg_interrupt(ci, OTGSC_INT_EN_BITS); | ||
119 | ci_clear_otg_interrupt(ci, OTGSC_INT_STATUS_BITS); | ||
120 | } | ||
diff --git a/drivers/usb/chipidea/otg.h b/drivers/usb/chipidea/otg.h new file mode 100644 index 000000000000..2d9f090733bc --- /dev/null +++ b/drivers/usb/chipidea/otg.h | |||
@@ -0,0 +1,35 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2013 Freescale Semiconductor, Inc. | ||
3 | * | ||
4 | * Author: Peter Chen | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | |||
11 | #ifndef __DRIVERS_USB_CHIPIDEA_OTG_H | ||
12 | #define __DRIVERS_USB_CHIPIDEA_OTG_H | ||
13 | |||
14 | static inline void ci_clear_otg_interrupt(struct ci_hdrc *ci, u32 bits) | ||
15 | { | ||
16 | /* Only clear request bits */ | ||
17 | hw_write(ci, OP_OTGSC, OTGSC_INT_STATUS_BITS, bits); | ||
18 | } | ||
19 | |||
20 | static inline void ci_enable_otg_interrupt(struct ci_hdrc *ci, u32 bits) | ||
21 | { | ||
22 | hw_write(ci, OP_OTGSC, bits, bits); | ||
23 | } | ||
24 | |||
25 | static inline void ci_disable_otg_interrupt(struct ci_hdrc *ci, u32 bits) | ||
26 | { | ||
27 | hw_write(ci, OP_OTGSC, bits, 0); | ||
28 | } | ||
29 | |||
30 | int ci_hdrc_otg_init(struct ci_hdrc *ci); | ||
31 | void ci_hdrc_otg_destroy(struct ci_hdrc *ci); | ||
32 | enum ci_role ci_otg_role(struct ci_hdrc *ci); | ||
33 | void ci_handle_vbus_change(struct ci_hdrc *ci); | ||
34 | |||
35 | #endif /* __DRIVERS_USB_CHIPIDEA_OTG_H */ | ||
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index e475fcda1d68..6b4c2f2eb946 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include "udc.h" | 27 | #include "udc.h" |
28 | #include "bits.h" | 28 | #include "bits.h" |
29 | #include "debug.h" | 29 | #include "debug.h" |
30 | #include "otg.h" | ||
30 | 31 | ||
31 | /* control endpoint description */ | 32 | /* control endpoint description */ |
32 | static const struct usb_endpoint_descriptor | 33 | static const struct usb_endpoint_descriptor |
@@ -84,8 +85,10 @@ static int hw_device_state(struct ci_hdrc *ci, u32 dma) | |||
84 | /* interrupt, error, port change, reset, sleep/suspend */ | 85 | /* interrupt, error, port change, reset, sleep/suspend */ |
85 | hw_write(ci, OP_USBINTR, ~0, | 86 | hw_write(ci, OP_USBINTR, ~0, |
86 | USBi_UI|USBi_UEI|USBi_PCI|USBi_URI|USBi_SLI); | 87 | USBi_UI|USBi_UEI|USBi_PCI|USBi_URI|USBi_SLI); |
88 | hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS); | ||
87 | } else { | 89 | } else { |
88 | hw_write(ci, OP_USBINTR, ~0, 0); | 90 | hw_write(ci, OP_USBINTR, ~0, 0); |
91 | hw_write(ci, OP_USBCMD, USBCMD_RS, 0); | ||
89 | } | 92 | } |
90 | return 0; | 93 | return 0; |
91 | } | 94 | } |
@@ -1445,9 +1448,6 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active) | |||
1445 | unsigned long flags; | 1448 | unsigned long flags; |
1446 | int gadget_ready = 0; | 1449 | int gadget_ready = 0; |
1447 | 1450 | ||
1448 | if (!(ci->platdata->flags & CI_HDRC_PULLUP_ON_VBUS)) | ||
1449 | return -EOPNOTSUPP; | ||
1450 | |||
1451 | spin_lock_irqsave(&ci->lock, flags); | 1451 | spin_lock_irqsave(&ci->lock, flags); |
1452 | ci->vbus_active = is_active; | 1452 | ci->vbus_active = is_active; |
1453 | if (ci->driver) | 1453 | if (ci->driver) |
@@ -1459,6 +1459,7 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active) | |||
1459 | pm_runtime_get_sync(&_gadget->dev); | 1459 | pm_runtime_get_sync(&_gadget->dev); |
1460 | hw_device_reset(ci, USBMODE_CM_DC); | 1460 | hw_device_reset(ci, USBMODE_CM_DC); |
1461 | hw_device_state(ci, ci->ep0out->qh.dma); | 1461 | hw_device_state(ci, ci->ep0out->qh.dma); |
1462 | dev_dbg(ci->dev, "Connected to host\n"); | ||
1462 | } else { | 1463 | } else { |
1463 | hw_device_state(ci, 0); | 1464 | hw_device_state(ci, 0); |
1464 | if (ci->platdata->notify_event) | 1465 | if (ci->platdata->notify_event) |
@@ -1466,6 +1467,7 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active) | |||
1466 | CI_HDRC_CONTROLLER_STOPPED_EVENT); | 1467 | CI_HDRC_CONTROLLER_STOPPED_EVENT); |
1467 | _gadget_stop_activity(&ci->gadget); | 1468 | _gadget_stop_activity(&ci->gadget); |
1468 | pm_runtime_put_sync(&_gadget->dev); | 1469 | pm_runtime_put_sync(&_gadget->dev); |
1470 | dev_dbg(ci->dev, "Disconnected from host\n"); | ||
1469 | } | 1471 | } |
1470 | } | 1472 | } |
1471 | 1473 | ||
@@ -1509,6 +1511,9 @@ static int ci_udc_pullup(struct usb_gadget *_gadget, int is_on) | |||
1509 | { | 1511 | { |
1510 | struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget); | 1512 | struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget); |
1511 | 1513 | ||
1514 | if (!ci->vbus_active) | ||
1515 | return -EOPNOTSUPP; | ||
1516 | |||
1512 | if (is_on) | 1517 | if (is_on) |
1513 | hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS); | 1518 | hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS); |
1514 | else | 1519 | else |
@@ -1630,14 +1635,11 @@ static int ci_udc_start(struct usb_gadget *gadget, | |||
1630 | 1635 | ||
1631 | ci->driver = driver; | 1636 | ci->driver = driver; |
1632 | pm_runtime_get_sync(&ci->gadget.dev); | 1637 | pm_runtime_get_sync(&ci->gadget.dev); |
1633 | if (ci->platdata->flags & CI_HDRC_PULLUP_ON_VBUS) { | 1638 | if (ci->vbus_active) { |
1634 | if (ci->vbus_active) { | 1639 | hw_device_reset(ci, USBMODE_CM_DC); |
1635 | if (ci->platdata->flags & CI_HDRC_REGS_SHARED) | 1640 | } else { |
1636 | hw_device_reset(ci, USBMODE_CM_DC); | 1641 | pm_runtime_put_sync(&ci->gadget.dev); |
1637 | } else { | 1642 | goto done; |
1638 | pm_runtime_put_sync(&ci->gadget.dev); | ||
1639 | goto done; | ||
1640 | } | ||
1641 | } | 1643 | } |
1642 | 1644 | ||
1643 | retval = hw_device_state(ci, ci->ep0out->qh.dma); | 1645 | retval = hw_device_state(ci, ci->ep0out->qh.dma); |
@@ -1660,8 +1662,7 @@ static int ci_udc_stop(struct usb_gadget *gadget, | |||
1660 | 1662 | ||
1661 | spin_lock_irqsave(&ci->lock, flags); | 1663 | spin_lock_irqsave(&ci->lock, flags); |
1662 | 1664 | ||
1663 | if (!(ci->platdata->flags & CI_HDRC_PULLUP_ON_VBUS) || | 1665 | if (ci->vbus_active) { |
1664 | ci->vbus_active) { | ||
1665 | hw_device_state(ci, 0); | 1666 | hw_device_state(ci, 0); |
1666 | if (ci->platdata->notify_event) | 1667 | if (ci->platdata->notify_event) |
1667 | ci->platdata->notify_event(ci, | 1668 | ci->platdata->notify_event(ci, |
@@ -1796,16 +1797,15 @@ static int udc_start(struct ci_hdrc *ci) | |||
1796 | } | 1797 | } |
1797 | } | 1798 | } |
1798 | 1799 | ||
1799 | if (!(ci->platdata->flags & CI_HDRC_REGS_SHARED)) { | ||
1800 | retval = hw_device_reset(ci, USBMODE_CM_DC); | ||
1801 | if (retval) | ||
1802 | goto put_transceiver; | ||
1803 | } | ||
1804 | |||
1805 | if (ci->transceiver) { | 1800 | if (ci->transceiver) { |
1806 | retval = otg_set_peripheral(ci->transceiver->otg, | 1801 | retval = otg_set_peripheral(ci->transceiver->otg, |
1807 | &ci->gadget); | 1802 | &ci->gadget); |
1808 | if (retval) | 1803 | /* |
1804 | * If we implement all USB functions using chipidea drivers, | ||
1805 | * it doesn't need to call above API, meanwhile, if we only | ||
1806 | * use gadget function, calling above API is useless. | ||
1807 | */ | ||
1808 | if (retval && retval != -ENOTSUPP) | ||
1809 | goto put_transceiver; | 1809 | goto put_transceiver; |
1810 | } | 1810 | } |
1811 | 1811 | ||
@@ -1816,6 +1816,9 @@ static int udc_start(struct ci_hdrc *ci) | |||
1816 | pm_runtime_no_callbacks(&ci->gadget.dev); | 1816 | pm_runtime_no_callbacks(&ci->gadget.dev); |
1817 | pm_runtime_enable(&ci->gadget.dev); | 1817 | pm_runtime_enable(&ci->gadget.dev); |
1818 | 1818 | ||
1819 | /* Update ci->vbus_active */ | ||
1820 | ci_handle_vbus_change(ci); | ||
1821 | |||
1819 | return retval; | 1822 | return retval; |
1820 | 1823 | ||
1821 | remove_trans: | 1824 | remove_trans: |
@@ -1839,13 +1842,13 @@ free_qh_pool: | |||
1839 | } | 1842 | } |
1840 | 1843 | ||
1841 | /** | 1844 | /** |
1842 | * udc_remove: parent remove must call this to remove UDC | 1845 | * ci_hdrc_gadget_destroy: parent remove must call this to remove UDC |
1843 | * | 1846 | * |
1844 | * No interrupts active, the IRQ has been released | 1847 | * No interrupts active, the IRQ has been released |
1845 | */ | 1848 | */ |
1846 | static void udc_stop(struct ci_hdrc *ci) | 1849 | void ci_hdrc_gadget_destroy(struct ci_hdrc *ci) |
1847 | { | 1850 | { |
1848 | if (ci == NULL) | 1851 | if (!ci->roles[CI_ROLE_GADGET]) |
1849 | return; | 1852 | return; |
1850 | 1853 | ||
1851 | usb_del_gadget_udc(&ci->gadget); | 1854 | usb_del_gadget_udc(&ci->gadget); |
@@ -1860,15 +1863,32 @@ static void udc_stop(struct ci_hdrc *ci) | |||
1860 | if (ci->global_phy) | 1863 | if (ci->global_phy) |
1861 | usb_put_phy(ci->transceiver); | 1864 | usb_put_phy(ci->transceiver); |
1862 | } | 1865 | } |
1863 | /* my kobject is dynamic, I swear! */ | 1866 | } |
1864 | memset(&ci->gadget, 0, sizeof(ci->gadget)); | 1867 | |
1868 | static int udc_id_switch_for_device(struct ci_hdrc *ci) | ||
1869 | { | ||
1870 | if (ci->is_otg) { | ||
1871 | ci_clear_otg_interrupt(ci, OTGSC_BSVIS); | ||
1872 | ci_enable_otg_interrupt(ci, OTGSC_BSVIE); | ||
1873 | } | ||
1874 | |||
1875 | return 0; | ||
1876 | } | ||
1877 | |||
1878 | static void udc_id_switch_for_host(struct ci_hdrc *ci) | ||
1879 | { | ||
1880 | if (ci->is_otg) { | ||
1881 | /* host doesn't care B_SESSION_VALID event */ | ||
1882 | ci_clear_otg_interrupt(ci, OTGSC_BSVIS); | ||
1883 | ci_disable_otg_interrupt(ci, OTGSC_BSVIE); | ||
1884 | } | ||
1865 | } | 1885 | } |
1866 | 1886 | ||
1867 | /** | 1887 | /** |
1868 | * ci_hdrc_gadget_init - initialize device related bits | 1888 | * ci_hdrc_gadget_init - initialize device related bits |
1869 | * ci: the controller | 1889 | * ci: the controller |
1870 | * | 1890 | * |
1871 | * This function enables the gadget role, if the device is "device capable". | 1891 | * This function initializes the gadget, if the device is "device capable". |
1872 | */ | 1892 | */ |
1873 | int ci_hdrc_gadget_init(struct ci_hdrc *ci) | 1893 | int ci_hdrc_gadget_init(struct ci_hdrc *ci) |
1874 | { | 1894 | { |
@@ -1881,11 +1901,11 @@ int ci_hdrc_gadget_init(struct ci_hdrc *ci) | |||
1881 | if (!rdrv) | 1901 | if (!rdrv) |
1882 | return -ENOMEM; | 1902 | return -ENOMEM; |
1883 | 1903 | ||
1884 | rdrv->start = udc_start; | 1904 | rdrv->start = udc_id_switch_for_device; |
1885 | rdrv->stop = udc_stop; | 1905 | rdrv->stop = udc_id_switch_for_host; |
1886 | rdrv->irq = udc_irq; | 1906 | rdrv->irq = udc_irq; |
1887 | rdrv->name = "gadget"; | 1907 | rdrv->name = "gadget"; |
1888 | ci->roles[CI_ROLE_GADGET] = rdrv; | 1908 | ci->roles[CI_ROLE_GADGET] = rdrv; |
1889 | 1909 | ||
1890 | return 0; | 1910 | return udc_start(ci); |
1891 | } | 1911 | } |
diff --git a/drivers/usb/chipidea/udc.h b/drivers/usb/chipidea/udc.h index 455ac2169226..e66df0020bd4 100644 --- a/drivers/usb/chipidea/udc.h +++ b/drivers/usb/chipidea/udc.h | |||
@@ -84,6 +84,7 @@ struct ci_hw_req { | |||
84 | #ifdef CONFIG_USB_CHIPIDEA_UDC | 84 | #ifdef CONFIG_USB_CHIPIDEA_UDC |
85 | 85 | ||
86 | int ci_hdrc_gadget_init(struct ci_hdrc *ci); | 86 | int ci_hdrc_gadget_init(struct ci_hdrc *ci); |
87 | void ci_hdrc_gadget_destroy(struct ci_hdrc *ci); | ||
87 | 88 | ||
88 | #else | 89 | #else |
89 | 90 | ||
@@ -92,6 +93,11 @@ static inline int ci_hdrc_gadget_init(struct ci_hdrc *ci) | |||
92 | return -ENXIO; | 93 | return -ENXIO; |
93 | } | 94 | } |
94 | 95 | ||
96 | static inline void ci_hdrc_gadget_destroy(struct ci_hdrc *ci) | ||
97 | { | ||
98 | |||
99 | } | ||
100 | |||
95 | #endif | 101 | #endif |
96 | 102 | ||
97 | #endif /* __DRIVERS_USB_CHIPIDEA_UDC_H */ | 103 | #endif /* __DRIVERS_USB_CHIPIDEA_UDC_H */ |
diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index ac5a46155200..8a1094b1182f 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c | |||
@@ -18,8 +18,6 @@ | |||
18 | 18 | ||
19 | #include "ci_hdrc_imx.h" | 19 | #include "ci_hdrc_imx.h" |
20 | 20 | ||
21 | #define USB_DEV_MAX 4 | ||
22 | |||
23 | #define MX25_USB_PHY_CTRL_OFFSET 0x08 | 21 | #define MX25_USB_PHY_CTRL_OFFSET 0x08 |
24 | #define MX25_BM_EXTERNAL_VBUS_DIVIDER BIT(23) | 22 | #define MX25_BM_EXTERNAL_VBUS_DIVIDER BIT(23) |
25 | 23 | ||
@@ -32,51 +30,34 @@ | |||
32 | 30 | ||
33 | #define MX6_BM_OVER_CUR_DIS BIT(7) | 31 | #define MX6_BM_OVER_CUR_DIS BIT(7) |
34 | 32 | ||
33 | struct usbmisc_ops { | ||
34 | /* It's called once when probe a usb device */ | ||
35 | int (*init)(struct imx_usbmisc_data *data); | ||
36 | /* It's called once after adding a usb device */ | ||
37 | int (*post)(struct imx_usbmisc_data *data); | ||
38 | }; | ||
39 | |||
35 | struct imx_usbmisc { | 40 | struct imx_usbmisc { |
36 | void __iomem *base; | 41 | void __iomem *base; |
37 | spinlock_t lock; | 42 | spinlock_t lock; |
38 | struct clk *clk; | 43 | struct clk *clk; |
39 | struct usbmisc_usb_device usbdev[USB_DEV_MAX]; | ||
40 | const struct usbmisc_ops *ops; | 44 | const struct usbmisc_ops *ops; |
41 | }; | 45 | }; |
42 | 46 | ||
43 | static struct imx_usbmisc *usbmisc; | 47 | static struct imx_usbmisc *usbmisc; |
44 | 48 | ||
45 | static struct usbmisc_usb_device *get_usbdev(struct device *dev) | 49 | static int usbmisc_imx25_post(struct imx_usbmisc_data *data) |
46 | { | ||
47 | int i, ret; | ||
48 | |||
49 | for (i = 0; i < USB_DEV_MAX; i++) { | ||
50 | if (usbmisc->usbdev[i].dev == dev) | ||
51 | return &usbmisc->usbdev[i]; | ||
52 | else if (!usbmisc->usbdev[i].dev) | ||
53 | break; | ||
54 | } | ||
55 | |||
56 | if (i >= USB_DEV_MAX) | ||
57 | return ERR_PTR(-EBUSY); | ||
58 | |||
59 | ret = usbmisc_get_init_data(dev, &usbmisc->usbdev[i]); | ||
60 | if (ret) | ||
61 | return ERR_PTR(ret); | ||
62 | |||
63 | return &usbmisc->usbdev[i]; | ||
64 | } | ||
65 | |||
66 | static int usbmisc_imx25_post(struct device *dev) | ||
67 | { | 50 | { |
68 | struct usbmisc_usb_device *usbdev; | ||
69 | void __iomem *reg; | 51 | void __iomem *reg; |
70 | unsigned long flags; | 52 | unsigned long flags; |
71 | u32 val; | 53 | u32 val; |
72 | 54 | ||
73 | usbdev = get_usbdev(dev); | 55 | if (data->index > 2) |
74 | if (IS_ERR(usbdev)) | 56 | return -EINVAL; |
75 | return PTR_ERR(usbdev); | ||
76 | 57 | ||
77 | reg = usbmisc->base + MX25_USB_PHY_CTRL_OFFSET; | 58 | reg = usbmisc->base + MX25_USB_PHY_CTRL_OFFSET; |
78 | 59 | ||
79 | if (usbdev->evdo) { | 60 | if (data->evdo) { |
80 | spin_lock_irqsave(&usbmisc->lock, flags); | 61 | spin_lock_irqsave(&usbmisc->lock, flags); |
81 | val = readl(reg); | 62 | val = readl(reg); |
82 | writel(val | MX25_BM_EXTERNAL_VBUS_DIVIDER, reg); | 63 | writel(val | MX25_BM_EXTERNAL_VBUS_DIVIDER, reg); |
@@ -87,20 +68,18 @@ static int usbmisc_imx25_post(struct device *dev) | |||
87 | return 0; | 68 | return 0; |
88 | } | 69 | } |
89 | 70 | ||
90 | static int usbmisc_imx53_init(struct device *dev) | 71 | static int usbmisc_imx53_init(struct imx_usbmisc_data *data) |
91 | { | 72 | { |
92 | struct usbmisc_usb_device *usbdev; | ||
93 | void __iomem *reg = NULL; | 73 | void __iomem *reg = NULL; |
94 | unsigned long flags; | 74 | unsigned long flags; |
95 | u32 val = 0; | 75 | u32 val = 0; |
96 | 76 | ||
97 | usbdev = get_usbdev(dev); | 77 | if (data->index > 3) |
98 | if (IS_ERR(usbdev)) | 78 | return -EINVAL; |
99 | return PTR_ERR(usbdev); | ||
100 | 79 | ||
101 | if (usbdev->disable_oc) { | 80 | if (data->disable_oc) { |
102 | spin_lock_irqsave(&usbmisc->lock, flags); | 81 | spin_lock_irqsave(&usbmisc->lock, flags); |
103 | switch (usbdev->index) { | 82 | switch (data->index) { |
104 | case 0: | 83 | case 0: |
105 | reg = usbmisc->base + MX53_USB_OTG_PHY_CTRL_0_OFFSET; | 84 | reg = usbmisc->base + MX53_USB_OTG_PHY_CTRL_0_OFFSET; |
106 | val = readl(reg) | MX53_BM_OVER_CUR_DIS_OTG; | 85 | val = readl(reg) | MX53_BM_OVER_CUR_DIS_OTG; |
@@ -126,22 +105,19 @@ static int usbmisc_imx53_init(struct device *dev) | |||
126 | return 0; | 105 | return 0; |
127 | } | 106 | } |
128 | 107 | ||
129 | static int usbmisc_imx6q_init(struct device *dev) | 108 | static int usbmisc_imx6q_init(struct imx_usbmisc_data *data) |
130 | { | 109 | { |
131 | |||
132 | struct usbmisc_usb_device *usbdev; | ||
133 | unsigned long flags; | 110 | unsigned long flags; |
134 | u32 reg; | 111 | u32 reg; |
135 | 112 | ||
136 | usbdev = get_usbdev(dev); | 113 | if (data->index > 3) |
137 | if (IS_ERR(usbdev)) | 114 | return -EINVAL; |
138 | return PTR_ERR(usbdev); | ||
139 | 115 | ||
140 | if (usbdev->disable_oc) { | 116 | if (data->disable_oc) { |
141 | spin_lock_irqsave(&usbmisc->lock, flags); | 117 | spin_lock_irqsave(&usbmisc->lock, flags); |
142 | reg = readl(usbmisc->base + usbdev->index * 4); | 118 | reg = readl(usbmisc->base + data->index * 4); |
143 | writel(reg | MX6_BM_OVER_CUR_DIS, | 119 | writel(reg | MX6_BM_OVER_CUR_DIS, |
144 | usbmisc->base + usbdev->index * 4); | 120 | usbmisc->base + data->index * 4); |
145 | spin_unlock_irqrestore(&usbmisc->lock, flags); | 121 | spin_unlock_irqrestore(&usbmisc->lock, flags); |
146 | } | 122 | } |
147 | 123 | ||
@@ -160,6 +136,26 @@ static const struct usbmisc_ops imx6q_usbmisc_ops = { | |||
160 | .init = usbmisc_imx6q_init, | 136 | .init = usbmisc_imx6q_init, |
161 | }; | 137 | }; |
162 | 138 | ||
139 | int imx_usbmisc_init(struct imx_usbmisc_data *data) | ||
140 | { | ||
141 | if (!usbmisc) | ||
142 | return -EPROBE_DEFER; | ||
143 | if (!usbmisc->ops->init) | ||
144 | return 0; | ||
145 | return usbmisc->ops->init(data); | ||
146 | } | ||
147 | EXPORT_SYMBOL_GPL(imx_usbmisc_init); | ||
148 | |||
149 | int imx_usbmisc_init_post(struct imx_usbmisc_data *data) | ||
150 | { | ||
151 | if (!usbmisc) | ||
152 | return -EPROBE_DEFER; | ||
153 | if (!usbmisc->ops->post) | ||
154 | return 0; | ||
155 | return usbmisc->ops->post(data); | ||
156 | } | ||
157 | EXPORT_SYMBOL_GPL(imx_usbmisc_init_post); | ||
158 | |||
163 | static const struct of_device_id usbmisc_imx_dt_ids[] = { | 159 | static const struct of_device_id usbmisc_imx_dt_ids[] = { |
164 | { | 160 | { |
165 | .compatible = "fsl,imx25-usbmisc", | 161 | .compatible = "fsl,imx25-usbmisc", |
@@ -216,19 +212,12 @@ static int usbmisc_imx_probe(struct platform_device *pdev) | |||
216 | of_match_device(usbmisc_imx_dt_ids, &pdev->dev); | 212 | of_match_device(usbmisc_imx_dt_ids, &pdev->dev); |
217 | data->ops = (const struct usbmisc_ops *)tmp_dev->data; | 213 | data->ops = (const struct usbmisc_ops *)tmp_dev->data; |
218 | usbmisc = data; | 214 | usbmisc = data; |
219 | ret = usbmisc_set_ops(data->ops); | ||
220 | if (ret) { | ||
221 | usbmisc = NULL; | ||
222 | clk_disable_unprepare(data->clk); | ||
223 | return ret; | ||
224 | } | ||
225 | 215 | ||
226 | return 0; | 216 | return 0; |
227 | } | 217 | } |
228 | 218 | ||
229 | static int usbmisc_imx_remove(struct platform_device *pdev) | 219 | static int usbmisc_imx_remove(struct platform_device *pdev) |
230 | { | 220 | { |
231 | usbmisc_unset_ops(usbmisc->ops); | ||
232 | clk_disable_unprepare(usbmisc->clk); | 221 | clk_disable_unprepare(usbmisc->clk); |
233 | usbmisc = NULL; | 222 | usbmisc = NULL; |
234 | return 0; | 223 | return 0; |