diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/usb/chipidea/Makefile | 3 | ||||
-rw-r--r-- | drivers/usb/chipidea/ci13xxx_imx.c | 198 |
2 files changed, 201 insertions, 0 deletions
diff --git a/drivers/usb/chipidea/Makefile b/drivers/usb/chipidea/Makefile index b69900a62a4d..5c66d9c330ca 100644 --- a/drivers/usb/chipidea/Makefile +++ b/drivers/usb/chipidea/Makefile | |||
@@ -14,3 +14,6 @@ ifneq ($(CONFIG_PCI),) | |||
14 | obj-$(CONFIG_USB_CHIPIDEA) += ci13xxx_pci.o | 14 | obj-$(CONFIG_USB_CHIPIDEA) += ci13xxx_pci.o |
15 | endif | 15 | endif |
16 | 16 | ||
17 | ifneq ($(CONFIG_OF_DEVICE),) | ||
18 | obj-$(CONFIG_USB_CHIPIDEA) += ci13xxx_imx.o | ||
19 | endif | ||
diff --git a/drivers/usb/chipidea/ci13xxx_imx.c b/drivers/usb/chipidea/ci13xxx_imx.c new file mode 100644 index 000000000000..ef60d06835d0 --- /dev/null +++ b/drivers/usb/chipidea/ci13xxx_imx.c | |||
@@ -0,0 +1,198 @@ | |||
1 | /* | ||
2 | * Copyright 2012 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/of_platform.h> | ||
16 | #include <linux/of_gpio.h> | ||
17 | #include <linux/platform_device.h> | ||
18 | #include <linux/pm_runtime.h> | ||
19 | #include <linux/dma-mapping.h> | ||
20 | #include <linux/usb/chipidea.h> | ||
21 | #include <linux/clk.h> | ||
22 | #include <linux/regulator/consumer.h> | ||
23 | |||
24 | #include "ci.h" | ||
25 | |||
26 | #define pdev_to_phy(pdev) \ | ||
27 | ((struct usb_phy *)platform_get_drvdata(pdev)) | ||
28 | |||
29 | struct ci13xxx_imx_data { | ||
30 | struct device_node *phy_np; | ||
31 | struct usb_phy *phy; | ||
32 | struct platform_device *ci_pdev; | ||
33 | struct clk *clk; | ||
34 | struct regulator *reg_vbus; | ||
35 | }; | ||
36 | |||
37 | static struct ci13xxx_platform_data ci13xxx_imx_platdata __devinitdata = { | ||
38 | .name = "ci13xxx_imx", | ||
39 | .flags = CI13XXX_REQUIRE_TRANSCEIVER | | ||
40 | CI13XXX_PULLUP_ON_VBUS | | ||
41 | CI13XXX_DISABLE_STREAMING, | ||
42 | .capoffset = DEF_CAPOFFSET, | ||
43 | }; | ||
44 | |||
45 | static int __devinit ci13xxx_imx_probe(struct platform_device *pdev) | ||
46 | { | ||
47 | struct ci13xxx_imx_data *data; | ||
48 | struct platform_device *plat_ci, *phy_pdev; | ||
49 | struct device_node *phy_np; | ||
50 | struct resource *res; | ||
51 | struct regulator *reg_vbus; | ||
52 | int ret; | ||
53 | |||
54 | data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); | ||
55 | if (!data) { | ||
56 | dev_err(&pdev->dev, "Failed to allocate CI13xxx-IMX data!\n"); | ||
57 | return -ENOMEM; | ||
58 | } | ||
59 | |||
60 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
61 | if (!res) { | ||
62 | dev_err(&pdev->dev, "Can't get device resources!\n"); | ||
63 | return -ENOENT; | ||
64 | } | ||
65 | |||
66 | data->clk = devm_clk_get(&pdev->dev, NULL); | ||
67 | if (IS_ERR(data->clk)) { | ||
68 | dev_err(&pdev->dev, | ||
69 | "Failed to get clock, err=%ld\n", PTR_ERR(data->clk)); | ||
70 | return PTR_ERR(data->clk); | ||
71 | } | ||
72 | |||
73 | ret = clk_prepare_enable(data->clk); | ||
74 | if (ret) { | ||
75 | dev_err(&pdev->dev, | ||
76 | "Failed to prepare or enable clock, err=%d\n", ret); | ||
77 | return ret; | ||
78 | } | ||
79 | |||
80 | phy_np = of_parse_phandle(pdev->dev.of_node, "fsl,usbphy", 0); | ||
81 | if (phy_np) { | ||
82 | data->phy_np = phy_np; | ||
83 | phy_pdev = of_find_device_by_node(phy_np); | ||
84 | if (phy_pdev) { | ||
85 | struct usb_phy *phy; | ||
86 | phy = pdev_to_phy(phy_pdev); | ||
87 | if (phy && | ||
88 | try_module_get(phy_pdev->dev.driver->owner)) { | ||
89 | usb_phy_init(phy); | ||
90 | data->phy = phy; | ||
91 | } | ||
92 | } | ||
93 | } | ||
94 | |||
95 | /* we only support host now, so enable vbus here */ | ||
96 | reg_vbus = devm_regulator_get(&pdev->dev, "vbus"); | ||
97 | if (!IS_ERR(reg_vbus)) { | ||
98 | ret = regulator_enable(reg_vbus); | ||
99 | if (ret) { | ||
100 | dev_err(&pdev->dev, | ||
101 | "Failed to enable vbus regulator, err=%d\n", | ||
102 | ret); | ||
103 | goto put_np; | ||
104 | } | ||
105 | data->reg_vbus = reg_vbus; | ||
106 | } else { | ||
107 | reg_vbus = NULL; | ||
108 | } | ||
109 | |||
110 | ci13xxx_imx_platdata.phy = data->phy; | ||
111 | |||
112 | if (!pdev->dev.dma_mask) { | ||
113 | pdev->dev.dma_mask = devm_kzalloc(&pdev->dev, | ||
114 | sizeof(*pdev->dev.dma_mask), GFP_KERNEL); | ||
115 | if (!pdev->dev.dma_mask) { | ||
116 | ret = -ENOMEM; | ||
117 | dev_err(&pdev->dev, "Failed to alloc dma_mask!\n"); | ||
118 | goto err; | ||
119 | } | ||
120 | *pdev->dev.dma_mask = DMA_BIT_MASK(32); | ||
121 | dma_set_coherent_mask(&pdev->dev, *pdev->dev.dma_mask); | ||
122 | } | ||
123 | plat_ci = ci13xxx_add_device(&pdev->dev, | ||
124 | pdev->resource, pdev->num_resources, | ||
125 | &ci13xxx_imx_platdata); | ||
126 | if (IS_ERR(plat_ci)) { | ||
127 | ret = PTR_ERR(plat_ci); | ||
128 | dev_err(&pdev->dev, | ||
129 | "Can't register ci_hdrc platform device, err=%d\n", | ||
130 | ret); | ||
131 | goto err; | ||
132 | } | ||
133 | |||
134 | data->ci_pdev = plat_ci; | ||
135 | platform_set_drvdata(pdev, data); | ||
136 | |||
137 | pm_runtime_no_callbacks(&pdev->dev); | ||
138 | pm_runtime_enable(&pdev->dev); | ||
139 | |||
140 | return 0; | ||
141 | |||
142 | err: | ||
143 | if (reg_vbus) | ||
144 | regulator_disable(reg_vbus); | ||
145 | put_np: | ||
146 | if (phy_np) | ||
147 | of_node_put(phy_np); | ||
148 | clk_disable_unprepare(data->clk); | ||
149 | return ret; | ||
150 | } | ||
151 | |||
152 | static int __devexit ci13xxx_imx_remove(struct platform_device *pdev) | ||
153 | { | ||
154 | struct ci13xxx_imx_data *data = platform_get_drvdata(pdev); | ||
155 | |||
156 | pm_runtime_disable(&pdev->dev); | ||
157 | ci13xxx_remove_device(data->ci_pdev); | ||
158 | |||
159 | if (data->reg_vbus) | ||
160 | regulator_disable(data->reg_vbus); | ||
161 | |||
162 | if (data->phy) { | ||
163 | usb_phy_shutdown(data->phy); | ||
164 | module_put(data->phy->dev->driver->owner); | ||
165 | } | ||
166 | |||
167 | of_node_put(data->phy_np); | ||
168 | |||
169 | clk_disable_unprepare(data->clk); | ||
170 | |||
171 | platform_set_drvdata(pdev, NULL); | ||
172 | |||
173 | return 0; | ||
174 | } | ||
175 | |||
176 | static const struct of_device_id ci13xxx_imx_dt_ids[] = { | ||
177 | { .compatible = "fsl,imx27-usb", }, | ||
178 | { /* sentinel */ } | ||
179 | }; | ||
180 | MODULE_DEVICE_TABLE(of, ci13xxx_imx_dt_ids); | ||
181 | |||
182 | static struct platform_driver ci13xxx_imx_driver = { | ||
183 | .probe = ci13xxx_imx_probe, | ||
184 | .remove = __devexit_p(ci13xxx_imx_remove), | ||
185 | .driver = { | ||
186 | .name = "imx_usb", | ||
187 | .owner = THIS_MODULE, | ||
188 | .of_match_table = ci13xxx_imx_dt_ids, | ||
189 | }, | ||
190 | }; | ||
191 | |||
192 | module_platform_driver(ci13xxx_imx_driver); | ||
193 | |||
194 | MODULE_ALIAS("platform:imx-usb"); | ||
195 | MODULE_LICENSE("GPL v2"); | ||
196 | MODULE_DESCRIPTION("CI13xxx i.MX USB binding"); | ||
197 | MODULE_AUTHOR("Marek Vasut <marex@denx.de>"); | ||
198 | MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>"); | ||