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.
6 Content-Type: text/plain; charset=utf-8
7 Content-Transfer-Encoding: 8bit
9 Signed-off-by: Mike A. Chan <mikechan@google.com>
10 Signed-off-by: Arve Hjønnevåg <arve@android.com>
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
18 --- a/drivers/char/Kconfig
19 +++ b/drivers/char/Kconfig
20 @@ -1106,6 +1106,12 @@ config DEVPORT
25 + tristate "Goldfish TTY Driver"
28 + TTY driver for Goldfish Virtual Platform.
30 source "drivers/s390/char/Kconfig"
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
41 obj-$(CONFIG_MWAVE) += mwave/
42 obj-$(CONFIG_AGP) += agp/
44 +++ b/drivers/char/goldfish_tty.c
46 +/* drivers/char/goldfish_tty.c
48 +** Copyright (C) 2007 Google, Inc.
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.
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.
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>
68 +#include <mach/hardware.h>
72 + GOLDFISH_TTY_PUT_CHAR = 0x00,
73 + GOLDFISH_TTY_BYTES_READY = 0x04,
74 + GOLDFISH_TTY_CMD = 0x08,
76 + GOLDFISH_TTY_DATA_PTR = 0x10,
77 + GOLDFISH_TTY_DATA_LEN = 0x14,
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,
85 +struct goldfish_tty {
90 + struct tty_struct *tty;
91 + struct console console;
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;
100 +static void goldfish_tty_do_write(int line, const char *buf, unsigned count)
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);
112 +static irqreturn_t goldfish_tty_interrupt(int irq, void *dev_id)
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;
121 + count = readl(base + GOLDFISH_TTY_BYTES_READY);
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;
135 +static int goldfish_tty_open(struct tty_struct * tty, struct file * filp)
138 + struct goldfish_tty *qtty = &goldfish_ttys[tty->index];
140 + mutex_lock(&goldfish_tty_lock);
141 + if(qtty->tty == NULL || qtty->tty == tty) {
142 + if(qtty->opencount++ == 0) {
144 + writel(GOLDFISH_TTY_CMD_INT_ENABLE, qtty->base + GOLDFISH_TTY_CMD);
150 + mutex_unlock(&goldfish_tty_lock);
154 +static void goldfish_tty_close(struct tty_struct * tty, struct file * filp)
156 + struct goldfish_tty *qtty = &goldfish_ttys[tty->index];
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);
165 + mutex_unlock(&goldfish_tty_lock);
168 +static int goldfish_tty_write(struct tty_struct * tty, const unsigned char *buf, int count)
170 + goldfish_tty_do_write(tty->index, buf, count);
174 +static int goldfish_tty_write_room(struct tty_struct *tty)
179 +static int goldfish_tty_chars_in_buffer(struct tty_struct *tty)
181 + struct goldfish_tty *qtty = &goldfish_ttys[tty->index];
182 + uint32_t base = qtty->base;
183 + return readl(base + GOLDFISH_TTY_BYTES_READY);
186 +static void goldfish_tty_console_write(struct console *co, const char *b, unsigned count)
188 + goldfish_tty_do_write(co->index, b, count);
191 +static struct tty_driver *goldfish_tty_console_device(struct console *c, int *index)
194 + return goldfish_tty_driver;
197 +static int goldfish_tty_console_setup(struct console *co, char *options)
199 + if((unsigned)co->index > goldfish_tty_line_count)
201 + if(goldfish_ttys[co->index].base == 0)
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,
214 +static int __devinit goldfish_tty_create_driver(void)
217 + struct tty_driver *tty;
219 + goldfish_ttys = kzalloc(sizeof(*goldfish_ttys) * goldfish_tty_line_count, GFP_KERNEL);
220 + if(goldfish_ttys == NULL) {
222 + goto err_alloc_goldfish_ttys_failed;
225 + tty = alloc_tty_driver(goldfish_tty_line_count);
228 + goto err_alloc_tty_driver_failed;
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);
239 + goto err_tty_register_driver_failed;
241 + goldfish_tty_driver = tty;
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:
253 +static void goldfish_tty_delete_driver(void)
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;
262 +static int __devinit goldfish_tty_probe(struct platform_device *pdev)
266 + struct resource *r;
267 + struct device *ttydev;
271 + r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
274 + base = IO_ADDRESS(r->start - IO_START);
275 + r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
280 + if(pdev->id >= goldfish_tty_line_count)
283 + mutex_lock(&goldfish_tty_lock);
284 + if(goldfish_tty_current_line_count == 0) {
285 + ret = goldfish_tty_create_driver();
287 + goto err_create_driver_failed;
289 + goldfish_tty_current_line_count++;
291 + spin_lock_init(&goldfish_ttys[pdev->id].lock);
292 + goldfish_ttys[pdev->id].base = base;
293 + goldfish_ttys[pdev->id].irq = irq;
295 + writel(GOLDFISH_TTY_CMD_INT_DISABLE, base + GOLDFISH_TTY_CMD);
297 + ret = request_irq(irq, goldfish_tty_interrupt, IRQF_SHARED, "goldfish_tty", pdev);
299 + goto err_request_irq_failed;
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;
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);
317 + mutex_unlock(&goldfish_tty_lock);
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();
329 +err_create_driver_failed:
330 + mutex_unlock(&goldfish_tty_lock);
334 +static int __devexit goldfish_tty_remove(struct platform_device *pdev)
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();
345 + mutex_unlock(&goldfish_tty_lock);
349 +static struct platform_driver goldfish_tty_platform_driver = {
350 + .probe = goldfish_tty_probe,
351 + .remove = __devexit_p(goldfish_tty_remove),
353 + .name = "goldfish_tty"
357 +static int __init goldfish_tty_init(void)
359 + return platform_driver_register(&goldfish_tty_platform_driver);
362 +static void __exit goldfish_tty_exit(void)
364 + platform_driver_unregister(&goldfish_tty_platform_driver);
367 +module_init(goldfish_tty_init);
368 +module_exit(goldfish_tty_exit);