aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorPeter Korsgaard <jacmet@sunsite.dk>2008-04-27 02:59:44 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2008-05-02 13:25:56 -0400
commitb02b371e6d14961ad458ca9d88b30eefef77003d (patch)
tree3312107b1fd0d0445eecd079169f5def480fb4fb /drivers/usb
parentd6f945044ee3b91a170183e8e34c3db29696d9b8 (diff)
USB: add Cypress c67x00 OTG controller core driver
This patch add the core driver for the c67x00 USB OTG controller. The core driver is responsible for the platform bus binding and creating either USB HCD or USB Gadget instances for each of the serial interface engines on the chip. This driver does not directly implement the HCD or gadget behaviours; it just controls access to the chip. Signed-off-by: Peter Korsgaard <jacmet@sunsite.dk> Acked-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/c67x00/c67x00-drv.c230
1 files changed, 230 insertions, 0 deletions
diff --git a/drivers/usb/c67x00/c67x00-drv.c b/drivers/usb/c67x00/c67x00-drv.c
new file mode 100644
index 000000000000..f8189bdbd06d
--- /dev/null
+++ b/drivers/usb/c67x00/c67x00-drv.c
@@ -0,0 +1,230 @@
1/*
2 * c67x00-drv.c: Cypress C67X00 USB Common infrastructure
3 *
4 * Copyright (C) 2006-2008 Barco N.V.
5 * Derived from the Cypress cy7c67200/300 ezusb linux driver and
6 * based on multiple host controller drivers inside the linux kernel.
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 as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21 * MA 02110-1301 USA.
22 */
23
24/*
25 * This file implements the common infrastructure for using the c67x00.
26 * It is both the link between the platform configuration and subdrivers and
27 * the link between the common hardware parts and the subdrivers (e.g.
28 * interrupt handling).
29 *
30 * The c67x00 has 2 SIE's (serial interface engine) wich can be configured
31 * to be host, device or OTG (with some limitations, E.G. only SIE1 can be OTG).
32 *
33 * Depending on the platform configuration, the SIE's are created and
34 * the corresponding subdriver is initialized (c67x00_probe_sie).
35 */
36
37#include <linux/device.h>
38#include <linux/io.h>
39#include <linux/list.h>
40#include <linux/usb.h>
41#include <linux/usb/c67x00.h>
42
43#include "c67x00.h"
44
45static void c67x00_probe_sie(struct c67x00_sie *sie,
46 struct c67x00_device *dev, int sie_num)
47{
48 spin_lock_init(&sie->lock);
49 sie->dev = dev;
50 sie->sie_num = sie_num;
51 sie->mode = c67x00_sie_config(dev->pdata->sie_config, sie_num);
52
53 switch (sie->mode) {
54 case C67X00_SIE_UNUSED:
55 dev_info(sie_dev(sie),
56 "Not using SIE %d as requested\n", sie->sie_num);
57 break;
58
59 default:
60 dev_err(sie_dev(sie),
61 "Unsupported configuration: 0x%x for SIE %d\n",
62 sie->mode, sie->sie_num);
63 break;
64 }
65}
66
67static void c67x00_remove_sie(struct c67x00_sie *sie)
68{
69}
70
71static irqreturn_t c67x00_irq(int irq, void *__dev)
72{
73 struct c67x00_device *c67x00 = __dev;
74 struct c67x00_sie *sie;
75 u16 msg, int_status;
76 int i, count = 8;
77
78 int_status = c67x00_ll_hpi_status(c67x00);
79 if (!int_status)
80 return IRQ_NONE;
81
82 while (int_status != 0 && (count-- >= 0)) {
83 c67x00_ll_irq(c67x00, int_status);
84 for (i = 0; i < C67X00_SIES; i++) {
85 sie = &c67x00->sie[i];
86 msg = 0;
87 if (int_status & SIEMSG_FLG(i))
88 msg = c67x00_ll_fetch_siemsg(c67x00, i);
89 if (sie->irq)
90 sie->irq(sie, int_status, msg);
91 }
92 int_status = c67x00_ll_hpi_status(c67x00);
93 }
94
95 if (int_status)
96 dev_warn(&c67x00->pdev->dev, "Not all interrupts handled! "
97 "status = 0x%04x\n", int_status);
98
99 return IRQ_HANDLED;
100}
101
102/* ------------------------------------------------------------------------- */
103
104static int __devinit c67x00_drv_probe(struct platform_device *pdev)
105{
106 struct c67x00_device *c67x00;
107 struct c67x00_platform_data *pdata;
108 struct resource *res, *res2;
109 int ret, i;
110
111 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
112 if (!res)
113 return -ENODEV;
114
115 res2 = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
116 if (!res2)
117 return -ENODEV;
118
119 pdata = pdev->dev.platform_data;
120 if (!pdata)
121 return -ENODEV;
122
123 c67x00 = kzalloc(sizeof(*c67x00), GFP_KERNEL);
124 if (!c67x00)
125 return -ENOMEM;
126
127 if (!request_mem_region(res->start, res->end - res->start + 1,
128 pdev->name)) {
129 dev_err(&pdev->dev, "Memory region busy\n");
130 ret = -EBUSY;
131 goto request_mem_failed;
132 }
133 c67x00->hpi.base = ioremap(res->start, res->end - res->start + 1);
134 if (!c67x00->hpi.base) {
135 dev_err(&pdev->dev, "Unable to map HPI registers\n");
136 ret = -EIO;
137 goto map_failed;
138 }
139
140 spin_lock_init(&c67x00->hpi.lock);
141 c67x00->hpi.regstep = pdata->hpi_regstep;
142 c67x00->pdata = pdev->dev.platform_data;
143 c67x00->pdev = pdev;
144
145 c67x00_ll_init(c67x00);
146 c67x00_ll_hpi_reg_init(c67x00);
147
148 ret = request_irq(res2->start, c67x00_irq, 0, pdev->name, c67x00);
149 if (ret) {
150 dev_err(&pdev->dev, "Cannot claim IRQ\n");
151 goto request_irq_failed;
152 }
153
154 ret = c67x00_ll_reset(c67x00);
155 if (ret) {
156 dev_err(&pdev->dev, "Device reset failed\n");
157 goto reset_failed;
158 }
159
160 for (i = 0; i < C67X00_SIES; i++)
161 c67x00_probe_sie(&c67x00->sie[i], c67x00, i);
162
163 platform_set_drvdata(pdev, c67x00);
164
165 return 0;
166
167 reset_failed:
168 free_irq(res2->start, c67x00);
169 request_irq_failed:
170 iounmap(c67x00->hpi.base);
171 map_failed:
172 release_mem_region(res->start, res->end - res->start + 1);
173 request_mem_failed:
174 kfree(c67x00);
175
176 return ret;
177}
178
179static int __devexit c67x00_drv_remove(struct platform_device *pdev)
180{
181 struct c67x00_device *c67x00 = platform_get_drvdata(pdev);
182 struct resource *res;
183 int i;
184
185 for (i = 0; i < C67X00_SIES; i++)
186 c67x00_remove_sie(&c67x00->sie[i]);
187
188 c67x00_ll_release(c67x00);
189
190 res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
191 if (res)
192 free_irq(res->start, c67x00);
193
194 iounmap(c67x00->hpi.base);
195
196 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
197 if (res)
198 release_mem_region(res->start, res->end - res->start + 1);
199
200 kfree(c67x00);
201
202 return 0;
203}
204
205static struct platform_driver c67x00_driver = {
206 .probe = c67x00_drv_probe,
207 .remove = __devexit_p(c67x00_drv_remove),
208 .driver = {
209 .owner = THIS_MODULE,
210 .name = "c67x00",
211 },
212};
213MODULE_ALIAS("platform:c67x00");
214
215static int __init c67x00_init(void)
216{
217 return platform_driver_register(&c67x00_driver);
218}
219
220static void __exit c67x00_exit(void)
221{
222 platform_driver_unregister(&c67x00_driver);
223}
224
225module_init(c67x00_init);
226module_exit(c67x00_exit);
227
228MODULE_AUTHOR("Peter Korsgaard, Jan Veldeman, Grant Likely");
229MODULE_DESCRIPTION("Cypress C67X00 USB Controller Driver");
230MODULE_LICENSE("GPL");