aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/fsl-mph-dr-of.c
diff options
context:
space:
mode:
authorAnatolij Gustschin <agust@denx.de>2010-09-28 14:55:20 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2010-10-22 13:21:58 -0400
commit126512e3f274802ca65ebeca8660237f0361ad48 (patch)
treeb0a9e5d193269cd606de7ada312a3e3f4bdfa518 /drivers/usb/host/fsl-mph-dr-of.c
parent99c1e4f89d1033444ce4d0c064bd2826e81c3775 (diff)
USB: add platform glue driver for FSL USB DR controller
Replace FSL USB platform code by simple platform driver for creation of FSL USB platform devices. The driver creates platform devices based on the information from USB nodes in the flat device tree. This is the replacement for old arch fsl_soc usb code removed by this patch. The driver uses usual of-style binding, available EHCI-HCD and UDC drivers can be bound to the created devices. The new of-style driver additionaly instantiates USB OTG platform device, as the appropriate USB OTG driver will be added soon. Signed-off-by: Anatolij Gustschin <agust@denx.de> Cc: Kumar Gala <galak@kernel.crashing.org> Cc: Grant Likely <grant.likely@secretlab.ca> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/host/fsl-mph-dr-of.c')
-rw-r--r--drivers/usb/host/fsl-mph-dr-of.c219
1 files changed, 219 insertions, 0 deletions
diff --git a/drivers/usb/host/fsl-mph-dr-of.c b/drivers/usb/host/fsl-mph-dr-of.c
new file mode 100644
index 000000000000..12db5d5cb0bc
--- /dev/null
+++ b/drivers/usb/host/fsl-mph-dr-of.c
@@ -0,0 +1,219 @@
1/*
2 * Setup platform devices needed by the Freescale multi-port host
3 * and/or dual-role USB controller modules based on the description
4 * in flat device tree.
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version.
10 */
11
12#include <linux/kernel.h>
13#include <linux/platform_device.h>
14#include <linux/fsl_devices.h>
15#include <linux/err.h>
16#include <linux/io.h>
17#include <linux/of_platform.h>
18
19struct fsl_usb2_dev_data {
20 char *dr_mode; /* controller mode */
21 char *drivers[3]; /* drivers to instantiate for this mode */
22 enum fsl_usb2_operating_modes op_mode; /* operating mode */
23};
24
25struct fsl_usb2_dev_data dr_mode_data[] __devinitdata = {
26 {
27 .dr_mode = "host",
28 .drivers = { "fsl-ehci", NULL, NULL, },
29 .op_mode = FSL_USB2_DR_HOST,
30 },
31 {
32 .dr_mode = "otg",
33 .drivers = { "fsl-usb2-otg", "fsl-ehci", "fsl-usb2-udc", },
34 .op_mode = FSL_USB2_DR_OTG,
35 },
36 {
37 .dr_mode = "peripheral",
38 .drivers = { "fsl-usb2-udc", NULL, NULL, },
39 .op_mode = FSL_USB2_DR_DEVICE,
40 },
41};
42
43struct fsl_usb2_dev_data * __devinit get_dr_mode_data(struct device_node *np)
44{
45 const unsigned char *prop;
46 int i;
47
48 prop = of_get_property(np, "dr_mode", NULL);
49 if (prop) {
50 for (i = 0; i < ARRAY_SIZE(dr_mode_data); i++) {
51 if (!strcmp(prop, dr_mode_data[i].dr_mode))
52 return &dr_mode_data[i];
53 }
54 }
55 pr_warn("%s: Invalid 'dr_mode' property, fallback to host mode\n",
56 np->full_name);
57 return &dr_mode_data[0]; /* mode not specified, use host */
58}
59
60static enum fsl_usb2_phy_modes __devinit determine_usb_phy(const char *phy_type)
61{
62 if (!phy_type)
63 return FSL_USB2_PHY_NONE;
64 if (!strcasecmp(phy_type, "ulpi"))
65 return FSL_USB2_PHY_ULPI;
66 if (!strcasecmp(phy_type, "utmi"))
67 return FSL_USB2_PHY_UTMI;
68 if (!strcasecmp(phy_type, "utmi_wide"))
69 return FSL_USB2_PHY_UTMI_WIDE;
70 if (!strcasecmp(phy_type, "serial"))
71 return FSL_USB2_PHY_SERIAL;
72
73 return FSL_USB2_PHY_NONE;
74}
75
76struct platform_device * __devinit fsl_usb2_device_register(
77 struct platform_device *ofdev,
78 struct fsl_usb2_platform_data *pdata,
79 const char *name, int id)
80{
81 struct platform_device *pdev;
82 const struct resource *res = ofdev->resource;
83 unsigned int num = ofdev->num_resources;
84 int retval;
85
86 pdev = platform_device_alloc(name, id);
87 if (!pdev) {
88 retval = -ENOMEM;
89 goto error;
90 }
91
92 pdev->dev.parent = &ofdev->dev;
93
94 pdev->dev.coherent_dma_mask = ofdev->dev.coherent_dma_mask;
95 pdev->dev.dma_mask = &pdev->archdata.dma_mask;
96 *pdev->dev.dma_mask = *ofdev->dev.dma_mask;
97
98 retval = platform_device_add_data(pdev, pdata, sizeof(*pdata));
99 if (retval)
100 goto error;
101
102 if (num) {
103 retval = platform_device_add_resources(pdev, res, num);
104 if (retval)
105 goto error;
106 }
107
108 retval = platform_device_add(pdev);
109 if (retval)
110 goto error;
111
112 return pdev;
113
114error:
115 platform_device_put(pdev);
116 return ERR_PTR(retval);
117}
118
119static const struct of_device_id fsl_usb2_mph_dr_of_match[];
120
121static int __devinit fsl_usb2_mph_dr_of_probe(struct platform_device *ofdev)
122{
123 struct device_node *np = ofdev->dev.of_node;
124 struct platform_device *usb_dev;
125 struct fsl_usb2_platform_data data, *pdata;
126 struct fsl_usb2_dev_data *dev_data;
127 const struct of_device_id *match;
128 const unsigned char *prop;
129 static unsigned int idx;
130 int i;
131
132 if (!of_device_is_available(np))
133 return -ENODEV;
134
135 match = of_match_device(fsl_usb2_mph_dr_of_match, &ofdev->dev);
136 if (!match)
137 return -ENODEV;
138
139 pdata = &data;
140 if (match->data)
141 memcpy(pdata, match->data, sizeof(data));
142 else
143 memset(pdata, 0, sizeof(data));
144
145 dev_data = get_dr_mode_data(np);
146
147 if (of_device_is_compatible(np, "fsl-usb2-mph")) {
148 if (of_get_property(np, "port0", NULL))
149 pdata->port_enables |= FSL_USB2_PORT0_ENABLED;
150
151 if (of_get_property(np, "port1", NULL))
152 pdata->port_enables |= FSL_USB2_PORT1_ENABLED;
153
154 pdata->operating_mode = FSL_USB2_MPH_HOST;
155 } else {
156 /* setup mode selected in the device tree */
157 pdata->operating_mode = dev_data->op_mode;
158 }
159
160 prop = of_get_property(np, "phy_type", NULL);
161 pdata->phy_mode = determine_usb_phy(prop);
162
163 for (i = 0; i < ARRAY_SIZE(dev_data->drivers); i++) {
164 if (!dev_data->drivers[i])
165 continue;
166 usb_dev = fsl_usb2_device_register(ofdev, pdata,
167 dev_data->drivers[i], idx);
168 if (IS_ERR(usb_dev)) {
169 dev_err(&ofdev->dev, "Can't register usb device\n");
170 return PTR_ERR(usb_dev);
171 }
172 }
173 idx++;
174 return 0;
175}
176
177static int __devexit __unregister_subdev(struct device *dev, void *d)
178{
179 platform_device_unregister(to_platform_device(dev));
180 return 0;
181}
182
183static int __devexit fsl_usb2_mph_dr_of_remove(struct platform_device *ofdev)
184{
185 device_for_each_child(&ofdev->dev, NULL, __unregister_subdev);
186 return 0;
187}
188
189static const struct of_device_id fsl_usb2_mph_dr_of_match[] = {
190 { .compatible = "fsl-usb2-mph", },
191 { .compatible = "fsl-usb2-dr", },
192 {},
193};
194
195static struct platform_driver fsl_usb2_mph_dr_driver = {
196 .driver = {
197 .name = "fsl-usb2-mph-dr",
198 .owner = THIS_MODULE,
199 .of_match_table = fsl_usb2_mph_dr_of_match,
200 },
201 .probe = fsl_usb2_mph_dr_of_probe,
202 .remove = __devexit_p(fsl_usb2_mph_dr_of_remove),
203};
204
205static int __init fsl_usb2_mph_dr_init(void)
206{
207 return platform_driver_register(&fsl_usb2_mph_dr_driver);
208}
209module_init(fsl_usb2_mph_dr_init);
210
211static void __exit fsl_usb2_mph_dr_exit(void)
212{
213 platform_driver_unregister(&fsl_usb2_mph_dr_driver);
214}
215module_exit(fsl_usb2_mph_dr_exit);
216
217MODULE_DESCRIPTION("FSL MPH DR OF devices driver");
218MODULE_AUTHOR("Anatolij Gustschin <agust@denx.de>");
219MODULE_LICENSE("GPL");