e8767e6bdda4701be3292e636287b232742eeafd
[openwrt.git] / 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);