update to latest 2.6.27 and 2.6.30 kernels
[openwrt/svn-archive/archive.git] / target / linux / goldfish / patches-2.6.30 / 0122--ARM-goldfish-tty-Adding-tty-driver-for-goldfish.patch
1 From 4ff5a10b94c0c41088c8bbfa5be1ebab3822371b Mon Sep 17 00:00:00 2001
2 From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@google.com>
3 Date: Fri, 29 Jun 2007 21:41:20 -0700
4 Subject: [PATCH 122/134] [ARM] goldfish: tty: Adding tty driver for goldfish.
5 MIME-Version: 1.0
6 Content-Type: text/plain; charset=utf-8
7 Content-Transfer-Encoding: 8bit
8
9 Signed-off-by: Mike A. Chan <mikechan@google.com>
10 Signed-off-by: Arve Hjønnevåg <arve@android.com>
11 ---
12 drivers/char/Kconfig | 6 +
13 drivers/char/Makefile | 1 +
14 drivers/char/goldfish_tty.c | 323 +++++++++++++++++++++++++++++++++++++++++++
15 3 files changed, 330 insertions(+), 0 deletions(-)
16 create mode 100644 drivers/char/goldfish_tty.c
17
18 --- a/drivers/char/Kconfig
19 +++ b/drivers/char/Kconfig
20 @@ -1106,6 +1106,12 @@ config DEVPORT
21 depends on ISA || PCI
22 default y
23
24 +config GOLDFISH_TTY
25 + tristate "Goldfish TTY Driver"
26 + default n
27 + help
28 + TTY driver for Goldfish Virtual Platform.
29 +
30 source "drivers/s390/char/Kconfig"
31
32 endmenu
33 --- a/drivers/char/Makefile
34 +++ b/drivers/char/Makefile
35 @@ -98,6 +98,7 @@ obj-$(CONFIG_GPIO_DEVICE) += gpio_dev.o
36 obj-$(CONFIG_GPIO_VR41XX) += vr41xx_giu.o
37 obj-$(CONFIG_GPIO_TB0219) += tb0219.o
38 obj-$(CONFIG_TELCLOCK) += tlclk.o
39 +obj-$(CONFIG_GOLDFISH_TTY) += goldfish_tty.o
40
41 obj-$(CONFIG_MWAVE) += mwave/
42 obj-$(CONFIG_AGP) += agp/
43 --- /dev/null
44 +++ b/drivers/char/goldfish_tty.c
45 @@ -0,0 +1,323 @@
46 +/* drivers/char/goldfish_tty.c
47 +**
48 +** Copyright (C) 2007 Google, Inc.
49 +**
50 +** This software is licensed under the terms of the GNU General Public
51 +** License version 2, as published by the Free Software Foundation, and
52 +** may be copied, distributed, and modified under those terms.
53 +**
54 +** This program is distributed in the hope that it will be useful,
55 +** but WITHOUT ANY WARRANTY; without even the implied warranty of
56 +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
57 +** GNU General Public License for more details.
58 +**
59 +*/
60 +
61 +#include <linux/console.h>
62 +#include <linux/init.h>
63 +#include <linux/interrupt.h>
64 +#include <linux/platform_device.h>
65 +#include <linux/tty.h>
66 +#include <linux/tty_flip.h>
67 +
68 +#include <mach/hardware.h>
69 +#include <asm/io.h>
70 +
71 +enum {
72 + GOLDFISH_TTY_PUT_CHAR = 0x00,
73 + GOLDFISH_TTY_BYTES_READY = 0x04,
74 + GOLDFISH_TTY_CMD = 0x08,
75 +
76 + GOLDFISH_TTY_DATA_PTR = 0x10,
77 + GOLDFISH_TTY_DATA_LEN = 0x14,
78 +
79 + GOLDFISH_TTY_CMD_INT_DISABLE = 0,
80 + GOLDFISH_TTY_CMD_INT_ENABLE = 1,
81 + GOLDFISH_TTY_CMD_WRITE_BUFFER = 2,
82 + GOLDFISH_TTY_CMD_READ_BUFFER = 3,
83 +};
84 +
85 +struct goldfish_tty {
86 + spinlock_t lock;
87 + uint32_t base;
88 + uint32_t irq;
89 + int opencount;
90 + struct tty_struct *tty;
91 + struct console console;
92 +};
93 +
94 +static DEFINE_MUTEX(goldfish_tty_lock);
95 +static struct tty_driver *goldfish_tty_driver;
96 +static uint32_t goldfish_tty_line_count = 8;
97 +static uint32_t goldfish_tty_current_line_count;
98 +static struct goldfish_tty *goldfish_ttys;
99 +
100 +static void goldfish_tty_do_write(int line, const char *buf, unsigned count)
101 +{
102 + unsigned long irq_flags;
103 + struct goldfish_tty *qtty = &goldfish_ttys[line];
104 + uint32_t base = qtty->base;
105 + spin_lock_irqsave(&qtty->lock, irq_flags);
106 + writel(buf, base + GOLDFISH_TTY_DATA_PTR);
107 + writel(count, base + GOLDFISH_TTY_DATA_LEN);
108 + writel(GOLDFISH_TTY_CMD_WRITE_BUFFER, base + GOLDFISH_TTY_CMD);
109 + spin_unlock_irqrestore(&qtty->lock, irq_flags);
110 +}
111 +
112 +static irqreturn_t goldfish_tty_interrupt(int irq, void *dev_id)
113 +{
114 + struct platform_device *pdev = dev_id;
115 + struct goldfish_tty *qtty = &goldfish_ttys[pdev->id];
116 + uint32_t base = qtty->base;
117 + unsigned long irq_flags;
118 + unsigned char *buf;
119 + uint32_t count;
120 +
121 + count = readl(base + GOLDFISH_TTY_BYTES_READY);
122 + if(count == 0) {
123 + return IRQ_NONE;
124 + }
125 + count = tty_prepare_flip_string(qtty->tty, &buf, count);
126 + spin_lock_irqsave(&qtty->lock, irq_flags);
127 + writel(buf, base + GOLDFISH_TTY_DATA_PTR);
128 + writel(count, base + GOLDFISH_TTY_DATA_LEN);
129 + writel(GOLDFISH_TTY_CMD_READ_BUFFER, base + GOLDFISH_TTY_CMD);
130 + spin_unlock_irqrestore(&qtty->lock, irq_flags);
131 + tty_schedule_flip(qtty->tty);
132 + return IRQ_HANDLED;
133 +}
134 +
135 +static int goldfish_tty_open(struct tty_struct * tty, struct file * filp)
136 +{
137 + int ret;
138 + struct goldfish_tty *qtty = &goldfish_ttys[tty->index];
139 +
140 + mutex_lock(&goldfish_tty_lock);
141 + if(qtty->tty == NULL || qtty->tty == tty) {
142 + if(qtty->opencount++ == 0) {
143 + qtty->tty = tty;
144 + writel(GOLDFISH_TTY_CMD_INT_ENABLE, qtty->base + GOLDFISH_TTY_CMD);
145 + }
146 + ret = 0;
147 + }
148 + else
149 + ret = -EBUSY;
150 + mutex_unlock(&goldfish_tty_lock);
151 + return ret;
152 +}
153 +
154 +static void goldfish_tty_close(struct tty_struct * tty, struct file * filp)
155 +{
156 + struct goldfish_tty *qtty = &goldfish_ttys[tty->index];
157 +
158 + mutex_lock(&goldfish_tty_lock);
159 + if(qtty->tty == tty) {
160 + if(--qtty->opencount == 0) {
161 + writel(GOLDFISH_TTY_CMD_INT_DISABLE, qtty->base + GOLDFISH_TTY_CMD);
162 + qtty->tty = NULL;
163 + }
164 + }
165 + mutex_unlock(&goldfish_tty_lock);
166 +}
167 +
168 +static int goldfish_tty_write(struct tty_struct * tty, const unsigned char *buf, int count)
169 +{
170 + goldfish_tty_do_write(tty->index, buf, count);
171 + return count;
172 +}
173 +
174 +static int goldfish_tty_write_room(struct tty_struct *tty)
175 +{
176 + return 0x10000;
177 +}
178 +
179 +static int goldfish_tty_chars_in_buffer(struct tty_struct *tty)
180 +{
181 + struct goldfish_tty *qtty = &goldfish_ttys[tty->index];
182 + uint32_t base = qtty->base;
183 + return readl(base + GOLDFISH_TTY_BYTES_READY);
184 +}
185 +
186 +static void goldfish_tty_console_write(struct console *co, const char *b, unsigned count)
187 +{
188 + goldfish_tty_do_write(co->index, b, count);
189 +}
190 +
191 +static struct tty_driver *goldfish_tty_console_device(struct console *c, int *index)
192 +{
193 + *index = c->index;
194 + return goldfish_tty_driver;
195 +}
196 +
197 +static int goldfish_tty_console_setup(struct console *co, char *options)
198 +{
199 + if((unsigned)co->index > goldfish_tty_line_count)
200 + return -ENODEV;
201 + if(goldfish_ttys[co->index].base == 0)
202 + return -ENODEV;
203 + return 0;
204 +}
205 +
206 +static struct tty_operations goldfish_tty_ops = {
207 + .open = goldfish_tty_open,
208 + .close = goldfish_tty_close,
209 + .write = goldfish_tty_write,
210 + .write_room = goldfish_tty_write_room,
211 + .chars_in_buffer = goldfish_tty_chars_in_buffer,
212 +};
213 +
214 +static int __devinit goldfish_tty_create_driver(void)
215 +{
216 + int ret;
217 + struct tty_driver *tty;
218 +
219 + goldfish_ttys = kzalloc(sizeof(*goldfish_ttys) * goldfish_tty_line_count, GFP_KERNEL);
220 + if(goldfish_ttys == NULL) {
221 + ret = -ENOMEM;
222 + goto err_alloc_goldfish_ttys_failed;
223 + }
224 +
225 + tty = alloc_tty_driver(goldfish_tty_line_count);
226 + if(tty == NULL) {
227 + ret = -ENOMEM;
228 + goto err_alloc_tty_driver_failed;
229 + }
230 + tty->driver_name = "goldfish";
231 + tty->name = "ttyS";
232 + tty->type = TTY_DRIVER_TYPE_SERIAL;
233 + tty->subtype = SERIAL_TYPE_NORMAL;
234 + tty->init_termios = tty_std_termios;
235 + tty->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
236 + tty_set_operations(tty, &goldfish_tty_ops);
237 + ret = tty_register_driver(tty);
238 + if(ret)
239 + goto err_tty_register_driver_failed;
240 +
241 + goldfish_tty_driver = tty;
242 + return 0;
243 +
244 +err_tty_register_driver_failed:
245 + put_tty_driver(tty);
246 +err_alloc_tty_driver_failed:
247 + kfree(goldfish_ttys);
248 + goldfish_ttys = NULL;
249 +err_alloc_goldfish_ttys_failed:
250 + return ret;
251 +}
252 +
253 +static void goldfish_tty_delete_driver(void)
254 +{
255 + tty_unregister_driver(goldfish_tty_driver);
256 + put_tty_driver(goldfish_tty_driver);
257 + goldfish_tty_driver = NULL;
258 + kfree(goldfish_ttys);
259 + goldfish_ttys = NULL;
260 +}
261 +
262 +static int __devinit goldfish_tty_probe(struct platform_device *pdev)
263 +{
264 + int ret;
265 + int i;
266 + struct resource *r;
267 + struct device *ttydev;
268 + uint32_t base;
269 + uint32_t irq;
270 +
271 + r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
272 + if(r == NULL)
273 + return -EINVAL;
274 + base = IO_ADDRESS(r->start - IO_START);
275 + r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
276 + if(r == NULL)
277 + return -EINVAL;
278 + irq = r->start;
279 +
280 + if(pdev->id >= goldfish_tty_line_count)
281 + return -EINVAL;
282 +
283 + mutex_lock(&goldfish_tty_lock);
284 + if(goldfish_tty_current_line_count == 0) {
285 + ret = goldfish_tty_create_driver();
286 + if(ret)
287 + goto err_create_driver_failed;
288 + }
289 + goldfish_tty_current_line_count++;
290 +
291 + spin_lock_init(&goldfish_ttys[pdev->id].lock);
292 + goldfish_ttys[pdev->id].base = base;
293 + goldfish_ttys[pdev->id].irq = irq;
294 +
295 + writel(GOLDFISH_TTY_CMD_INT_DISABLE, base + GOLDFISH_TTY_CMD);
296 +
297 + ret = request_irq(irq, goldfish_tty_interrupt, IRQF_SHARED, "goldfish_tty", pdev);
298 + if(ret)
299 + goto err_request_irq_failed;
300 +
301 +
302 + ttydev = tty_register_device(goldfish_tty_driver, pdev->id, NULL);
303 + if(IS_ERR(ttydev)) {
304 + ret = PTR_ERR(ttydev);
305 + goto err_tty_register_device_failed;
306 + }
307 +
308 + strcpy(goldfish_ttys[pdev->id].console.name, "ttyS");
309 + goldfish_ttys[pdev->id].console.write = goldfish_tty_console_write;
310 + goldfish_ttys[pdev->id].console.device = goldfish_tty_console_device;
311 + goldfish_ttys[pdev->id].console.setup = goldfish_tty_console_setup;
312 + goldfish_ttys[pdev->id].console.flags = CON_PRINTBUFFER;
313 + goldfish_ttys[pdev->id].console.index = pdev->id;
314 + register_console(&goldfish_ttys[pdev->id].console);
315 +
316 +
317 + mutex_unlock(&goldfish_tty_lock);
318 +
319 + return 0;
320 +
321 + tty_unregister_device(goldfish_tty_driver, i);
322 +err_tty_register_device_failed:
323 + free_irq(irq, pdev);
324 +err_request_irq_failed:
325 + goldfish_tty_current_line_count--;
326 + if(goldfish_tty_current_line_count == 0) {
327 + goldfish_tty_delete_driver();
328 + }
329 +err_create_driver_failed:
330 + mutex_unlock(&goldfish_tty_lock);
331 + return ret;
332 +}
333 +
334 +static int __devexit goldfish_tty_remove(struct platform_device *pdev)
335 +{
336 + mutex_lock(&goldfish_tty_lock);
337 + unregister_console(&goldfish_ttys[pdev->id].console);
338 + tty_unregister_device(goldfish_tty_driver, pdev->id);
339 + goldfish_ttys[pdev->id].base = 0;
340 + free_irq(goldfish_ttys[pdev->id].irq, pdev);
341 + goldfish_tty_current_line_count--;
342 + if(goldfish_tty_current_line_count == 0) {
343 + goldfish_tty_delete_driver();
344 + }
345 + mutex_unlock(&goldfish_tty_lock);
346 + return 0;
347 +}
348 +
349 +static struct platform_driver goldfish_tty_platform_driver = {
350 + .probe = goldfish_tty_probe,
351 + .remove = __devexit_p(goldfish_tty_remove),
352 + .driver = {
353 + .name = "goldfish_tty"
354 + }
355 +};
356 +
357 +static int __init goldfish_tty_init(void)
358 +{
359 + return platform_driver_register(&goldfish_tty_platform_driver);
360 +}
361 +
362 +static void __exit goldfish_tty_exit(void)
363 +{
364 + platform_driver_unregister(&goldfish_tty_platform_driver);
365 +}
366 +
367 +module_init(goldfish_tty_init);
368 +module_exit(goldfish_tty_exit);