blob: a8a504f62cbeb918942302f8bdca0feb3011015c [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* arch/arm/mach-msm/smd_tty.c
2 *
3 * Copyright (C) 2007 Google, Inc.
4 * Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
5 * Author: Brian Swetland <swetland@google.com>
6 *
7 * This software is licensed under the terms of the GNU General Public
8 * License version 2, as published by the Free Software Foundation, and
9 * may be copied, distributed, and modified under those terms.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 */
17
18#include <linux/module.h>
19#include <linux/fs.h>
20#include <linux/cdev.h>
21#include <linux/device.h>
22#include <linux/interrupt.h>
23#include <linux/delay.h>
24#include <linux/wakelock.h>
25#include <linux/platform_device.h>
26#include <linux/sched.h>
27
28#include <linux/tty.h>
29#include <linux/tty_driver.h>
30#include <linux/tty_flip.h>
31
32#include <mach/msm_smd.h>
33#include <mach/peripheral-loader.h>
34
35#include "smd_private.h"
36
37#define MAX_SMD_TTYS 37
38#define MAX_TTY_BUF_SIZE 2048
39
40static DEFINE_MUTEX(smd_tty_lock);
41
42static uint smd_tty_modem_wait;
43module_param_named(modem_wait, smd_tty_modem_wait,
44 uint, S_IRUGO | S_IWUSR | S_IWGRP);
45
46struct smd_tty_info {
47 smd_channel_t *ch;
48 struct tty_struct *tty;
49 struct wake_lock wake_lock;
50 int open_count;
51 struct tasklet_struct tty_tsklt;
52 struct timer_list buf_req_timer;
53 struct completion ch_allocated;
54 struct platform_driver driver;
55 void *pil;
56 int in_reset;
57 int in_reset_updated;
58 spinlock_t reset_lock;
59};
60
61#define LOOPBACK_IDX 36
62static char *smd_ch_name[] = {
63 [0] = "DS",
64 [1] = "APPS_FM",
65 [2] = "APPS_RIVA_BT_ACL",
66 [3] = "APPS_RIVA_BT_CMD",
67 [7] = "DATA1",
68 [21] = "DATA21",
69 [27] = "GPSNMEA",
70 [36] = "LOOPBACK",
71};
72
73static uint32_t smd_ch_edge[] = {
74 [0] = SMD_APPS_MODEM,
75 [1] = SMD_APPS_WCNSS,
76 [2] = SMD_APPS_WCNSS,
77 [3] = SMD_APPS_WCNSS,
78 [7] = SMD_APPS_MODEM,
79 [21] = SMD_APPS_MODEM,
80 [27] = SMD_APPS_MODEM,
81 [36] = SMD_APPS_MODEM,
82};
83
84
85static struct delayed_work loopback_work;
86static struct smd_tty_info smd_tty[MAX_SMD_TTYS];
87
88static int is_in_reset(struct smd_tty_info *info)
89{
90 return info->in_reset;
91}
92
93static void buf_req_retry(unsigned long param)
94{
95 struct smd_tty_info *info = (struct smd_tty_info *)param;
96 tasklet_hi_schedule(&info->tty_tsklt);
97}
98
99static void smd_tty_read(unsigned long param)
100{
101 unsigned char *ptr;
102 int avail;
103 struct smd_tty_info *info = (struct smd_tty_info *)param;
104 struct tty_struct *tty = info->tty;
105
106 if (!tty)
107 return;
108
109 for (;;) {
110 if (is_in_reset(info)) {
111 /* signal TTY clients using TTY_BREAK */
112 tty_insert_flip_char(tty, 0x00, TTY_BREAK);
113 tty_flip_buffer_push(tty);
114 break;
115 }
116
117 if (test_bit(TTY_THROTTLED, &tty->flags)) break;
118 avail = smd_read_avail(info->ch);
119 if (avail == 0)
120 break;
121
122 if (avail > MAX_TTY_BUF_SIZE)
123 avail = MAX_TTY_BUF_SIZE;
124
125 avail = tty_prepare_flip_string(tty, &ptr, avail);
126 if (avail <= 0) {
127 if (!timer_pending(&info->buf_req_timer)) {
128 init_timer(&info->buf_req_timer);
129 info->buf_req_timer.expires = jiffies +
130 ((30 * HZ)/1000);
131 info->buf_req_timer.function = buf_req_retry;
132 info->buf_req_timer.data = param;
133 add_timer(&info->buf_req_timer);
134 }
135 return;
136 }
137
138 if (smd_read(info->ch, ptr, avail) != avail) {
139 /* shouldn't be possible since we're in interrupt
140 ** context here and nobody else could 'steal' our
141 ** characters.
142 */
143 printk(KERN_ERR "OOPS - smd_tty_buffer mismatch?!");
144 }
145
146 wake_lock_timeout(&info->wake_lock, HZ / 2);
147 tty_flip_buffer_push(tty);
148 }
149
150 /* XXX only when writable and necessary */
151 tty_wakeup(tty);
152}
153
154static void smd_tty_notify(void *priv, unsigned event)
155{
156 struct smd_tty_info *info = priv;
157 unsigned long flags;
158
159 switch (event) {
160 case SMD_EVENT_DATA:
161 /* There may be clients (tty framework) that are blocked
162 * waiting for space to write data, so if a possible read
163 * interrupt came in wake anyone waiting and disable the
164 * interrupts
165 */
166 if (smd_write_avail(info->ch)) {
167 smd_disable_read_intr(info->ch);
168 if (info->tty)
169 wake_up_interruptible(&info->tty->write_wait);
170 }
171 tasklet_hi_schedule(&info->tty_tsklt);
172 break;
173
174 case SMD_EVENT_OPEN:
175 spin_lock_irqsave(&info->reset_lock, flags);
176 info->in_reset = 0;
177 info->in_reset_updated = 1;
178 spin_unlock_irqrestore(&info->reset_lock, flags);
179 break;
180
181 case SMD_EVENT_CLOSE:
182 spin_lock_irqsave(&info->reset_lock, flags);
183 info->in_reset = 1;
184 info->in_reset_updated = 1;
185 spin_unlock_irqrestore(&info->reset_lock, flags);
186 /* schedule task to send TTY_BREAK */
187 tasklet_hi_schedule(&info->tty_tsklt);
188
189 if (info->tty->index == LOOPBACK_IDX)
190 schedule_delayed_work(&loopback_work,
191 msecs_to_jiffies(1000));
192 break;
193 }
194}
195
196static uint32_t is_modem_smsm_inited(void)
197{
198 uint32_t modem_state;
199 uint32_t ready_state = (SMSM_INIT | SMSM_SMDINIT);
200
201 modem_state = smsm_get_state(SMSM_MODEM_STATE);
202 return (modem_state & ready_state) == ready_state;
203}
204
205static int smd_tty_open(struct tty_struct *tty, struct file *f)
206{
207 int res = 0;
208 int n = tty->index;
209 struct smd_tty_info *info;
210 char *peripheral = NULL;
211
212
213 if (!smd_ch_name[n])
214 return -ENODEV;
215
216 info = smd_tty + n;
217
218 mutex_lock(&smd_tty_lock);
219 tty->driver_data = info;
220
221 if (info->open_count++ == 0) {
222 if (smd_ch_edge[n] == SMD_APPS_MODEM)
223 peripheral = "modem";
224
225 if (peripheral) {
226 info->pil = pil_get("modem");
227 if (IS_ERR(info->pil)) {
228 res = PTR_ERR(info->pil);
229 goto out;
230 }
231
232 /* Wait for the modem SMSM to be inited for the SMD
233 * Loopback channel to be allocated at the modem. Since
234 * the wait need to be done atmost once, using msleep
235 * doesn't degrade the performance.
236 */
237 if (n == 36) {
238 if (!is_modem_smsm_inited())
239 msleep(5000);
240 smsm_change_state(SMSM_APPS_STATE,
241 0, SMSM_SMD_LOOPBACK);
242 msleep(100);
243 }
244
245
246 /*
247 * Wait for a channel to be allocated so we know
248 * the modem is ready enough.
249 */
250 if (smd_tty_modem_wait) {
251 res = wait_for_completion_interruptible_timeout(
252 &info->ch_allocated,
253 msecs_to_jiffies(smd_tty_modem_wait *
254 1000));
255
256 if (res == 0) {
257 pr_err("Timed out waiting for SMD"
258 " channel\n");
259 res = -ETIMEDOUT;
260 goto release_pil;
261 } else if (res < 0) {
262 pr_err("Error waiting for SMD channel:"
263 " %d\n",
264 res);
265 goto release_pil;
266 }
267
268 res = 0;
269 }
270 }
271
272
273 info->tty = tty;
274 tasklet_init(&info->tty_tsklt, smd_tty_read,
275 (unsigned long)info);
276 wake_lock_init(&info->wake_lock, WAKE_LOCK_SUSPEND,
277 smd_ch_name[n]);
278 if (!info->ch) {
279 res = smd_named_open_on_edge(smd_ch_name[n],
280 smd_ch_edge[n],
281 &info->ch, info,
282 smd_tty_notify);
283 }
284 }
285
286release_pil:
287 if (res < 0)
288 pil_put(info->pil);
289 else
290 smd_disable_read_intr(info->ch);
291out:
292 mutex_unlock(&smd_tty_lock);
293
294 return res;
295}
296
297static void smd_tty_close(struct tty_struct *tty, struct file *f)
298{
299 struct smd_tty_info *info = tty->driver_data;
300
301 if (info == 0)
302 return;
303
304 mutex_lock(&smd_tty_lock);
305 if (--info->open_count == 0) {
306 if (info->tty) {
307 tasklet_kill(&info->tty_tsklt);
308 wake_lock_destroy(&info->wake_lock);
309 info->tty = 0;
310 }
311 tty->driver_data = 0;
312 del_timer(&info->buf_req_timer);
313 if (info->ch) {
314 smd_close(info->ch);
315 info->ch = 0;
316 pil_put(info->pil);
317 }
318 }
319 mutex_unlock(&smd_tty_lock);
320}
321
322static int smd_tty_write(struct tty_struct *tty, const unsigned char *buf, int len)
323{
324 struct smd_tty_info *info = tty->driver_data;
325 int avail;
326
327 /* if we're writing to a packet channel we will
328 ** never be able to write more data than there
329 ** is currently space for
330 */
331 if (is_in_reset(info))
332 return -ENETRESET;
333
334 avail = smd_write_avail(info->ch);
335 /* if no space, we'll have to setup a notification later to wake up the
336 * tty framework when space becomes avaliable
337 */
338 if (!avail) {
339 smd_enable_read_intr(info->ch);
340 return 0;
341 }
342 if (len > avail)
343 len = avail;
344
345 return smd_write(info->ch, buf, len);
346}
347
348static int smd_tty_write_room(struct tty_struct *tty)
349{
350 struct smd_tty_info *info = tty->driver_data;
351 return smd_write_avail(info->ch);
352}
353
354static int smd_tty_chars_in_buffer(struct tty_struct *tty)
355{
356 struct smd_tty_info *info = tty->driver_data;
357 return smd_read_avail(info->ch);
358}
359
360static void smd_tty_unthrottle(struct tty_struct *tty)
361{
362 struct smd_tty_info *info = tty->driver_data;
363 tasklet_hi_schedule(&info->tty_tsklt);
364 return;
365}
366
367/*
368 * Returns the current TIOCM status bits including:
369 * SMD Signals (DTR/DSR, CTS/RTS, CD, RI)
370 * TIOCM_OUT1 - reset state (1=in reset)
371 * TIOCM_OUT2 - reset state updated (1=updated)
372 */
373static int smd_tty_tiocmget(struct tty_struct *tty)
374{
375 struct smd_tty_info *info = tty->driver_data;
376 unsigned long flags;
377 int tiocm;
378
379 tiocm = smd_tiocmget(info->ch);
380
381 spin_lock_irqsave(&info->reset_lock, flags);
382 tiocm |= (info->in_reset ? TIOCM_OUT1 : 0);
383 if (info->in_reset_updated) {
384 tiocm |= TIOCM_OUT2;
385 info->in_reset_updated = 0;
386 }
387 spin_unlock_irqrestore(&info->reset_lock, flags);
388
389 return tiocm;
390}
391
392static int smd_tty_tiocmset(struct tty_struct *tty,
393 unsigned int set, unsigned int clear)
394{
395 struct smd_tty_info *info = tty->driver_data;
396
397 if (info->in_reset)
398 return -ENETRESET;
399
400 return smd_tiocmset(info->ch, set, clear);
401}
402
403static void loopback_probe_worker(struct work_struct *work)
404{
405 /* wait for modem to restart before requesting loopback server */
406 if (!is_modem_smsm_inited())
407 schedule_delayed_work(&loopback_work, msecs_to_jiffies(1000));
408 else
409 smsm_change_state(SMSM_APPS_STATE,
410 0, SMSM_SMD_LOOPBACK);
411}
412
413static struct tty_operations smd_tty_ops = {
414 .open = smd_tty_open,
415 .close = smd_tty_close,
416 .write = smd_tty_write,
417 .write_room = smd_tty_write_room,
418 .chars_in_buffer = smd_tty_chars_in_buffer,
419 .unthrottle = smd_tty_unthrottle,
420 .tiocmget = smd_tty_tiocmget,
421 .tiocmset = smd_tty_tiocmset,
422};
423
424static int smd_tty_dummy_probe(struct platform_device *pdev)
425{
426 if (!strncmp(pdev->name, smd_ch_name[0],
427 strnlen(smd_ch_name[0], SMD_MAX_CH_NAME_LEN)))
428 complete_all(&smd_tty[0].ch_allocated);
429 else if (!strncmp(pdev->name, smd_ch_name[1],
430 strnlen(smd_ch_name[1], SMD_MAX_CH_NAME_LEN)))
431 complete_all(&smd_tty[1].ch_allocated);
432 else if (!strncmp(pdev->name, smd_ch_name[2],
433 strnlen(smd_ch_name[2], SMD_MAX_CH_NAME_LEN)))
434 complete_all(&smd_tty[2].ch_allocated);
435 else if (!strncmp(pdev->name, smd_ch_name[3],
436 strnlen(smd_ch_name[3], SMD_MAX_CH_NAME_LEN)))
437 complete_all(&smd_tty[3].ch_allocated);
438 else if (!strncmp(pdev->name, smd_ch_name[7],
439 strnlen(smd_ch_name[7], SMD_MAX_CH_NAME_LEN)))
440 complete_all(&smd_tty[7].ch_allocated);
441 else if (!strncmp(pdev->name, smd_ch_name[21],
442 strnlen(smd_ch_name[21], SMD_MAX_CH_NAME_LEN)))
443 complete_all(&smd_tty[21].ch_allocated);
444 else if (!strncmp(pdev->name, smd_ch_name[27],
445 strnlen(smd_ch_name[27], SMD_MAX_CH_NAME_LEN)))
446 complete_all(&smd_tty[27].ch_allocated);
447 else if (!strncmp(pdev->name, "LOOPBACK_TTY",
448 strnlen("LOOPBACK_TTY", SMD_MAX_CH_NAME_LEN)))
449 complete_all(&smd_tty[36].ch_allocated);
450
451 return 0;
452}
453
454static struct tty_driver *smd_tty_driver;
455
456static int __init smd_tty_init(void)
457{
458 int ret;
459
460 smd_tty_driver = alloc_tty_driver(MAX_SMD_TTYS);
461 if (smd_tty_driver == 0)
462 return -ENOMEM;
463
464 smd_tty_driver->owner = THIS_MODULE;
465 smd_tty_driver->driver_name = "smd_tty_driver";
466 smd_tty_driver->name = "smd";
467 smd_tty_driver->major = 0;
468 smd_tty_driver->minor_start = 0;
469 smd_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
470 smd_tty_driver->subtype = SERIAL_TYPE_NORMAL;
471 smd_tty_driver->init_termios = tty_std_termios;
472 smd_tty_driver->init_termios.c_iflag = 0;
473 smd_tty_driver->init_termios.c_oflag = 0;
474 smd_tty_driver->init_termios.c_cflag = B38400 | CS8 | CREAD;
475 smd_tty_driver->init_termios.c_lflag = 0;
476 smd_tty_driver->flags = TTY_DRIVER_RESET_TERMIOS |
477 TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
478 tty_set_operations(smd_tty_driver, &smd_tty_ops);
479
480 ret = tty_register_driver(smd_tty_driver);
481 if (ret) return ret;
482
483 /* this should be dynamic */
484 tty_register_device(smd_tty_driver, 0, 0);
485 tty_register_device(smd_tty_driver, 1, 0);
486 tty_register_device(smd_tty_driver, 2, 0);
487 tty_register_device(smd_tty_driver, 3, 0);
488 tty_register_device(smd_tty_driver, 7, 0);
489 tty_register_device(smd_tty_driver, 21, 0);
490 tty_register_device(smd_tty_driver, 27, 0);
491 tty_register_device(smd_tty_driver, 36, 0);
492
493 init_completion(&smd_tty[0].ch_allocated);
494 init_completion(&smd_tty[1].ch_allocated);
495 init_completion(&smd_tty[2].ch_allocated);
496 init_completion(&smd_tty[3].ch_allocated);
497 init_completion(&smd_tty[7].ch_allocated);
498 init_completion(&smd_tty[21].ch_allocated);
499 init_completion(&smd_tty[27].ch_allocated);
500 init_completion(&smd_tty[36].ch_allocated);
501
502 smd_tty[0].driver.probe = smd_tty_dummy_probe;
503 smd_tty[0].driver.driver.name = smd_ch_name[0];
504 smd_tty[0].driver.driver.owner = THIS_MODULE;
505 spin_lock_init(&smd_tty[0].reset_lock);
506 ret = platform_driver_register(&smd_tty[0].driver);
507 if (ret)
508 goto out;
509 smd_tty[1].driver.probe = smd_tty_dummy_probe;
510 smd_tty[1].driver.driver.name = smd_ch_name[1];
511 smd_tty[1].driver.driver.owner = THIS_MODULE;
512 spin_lock_init(&smd_tty[1].reset_lock);
513 ret = platform_driver_register(&smd_tty[1].driver);
514 if (ret)
515 goto unreg0;
516 smd_tty[2].driver.probe = smd_tty_dummy_probe;
517 smd_tty[2].driver.driver.name = smd_ch_name[2];
518 smd_tty[2].driver.driver.owner = THIS_MODULE;
519 spin_lock_init(&smd_tty[2].reset_lock);
520 ret = platform_driver_register(&smd_tty[2].driver);
521 if (ret)
522 goto unreg1;
523 smd_tty[3].driver.probe = smd_tty_dummy_probe;
524 smd_tty[3].driver.driver.name = smd_ch_name[3];
525 smd_tty[3].driver.driver.owner = THIS_MODULE;
526 spin_lock_init(&smd_tty[3].reset_lock);
527 ret = platform_driver_register(&smd_tty[3].driver);
528 if (ret)
529 goto unreg2;
530 smd_tty[7].driver.probe = smd_tty_dummy_probe;
531 smd_tty[7].driver.driver.name = smd_ch_name[7];
532 smd_tty[7].driver.driver.owner = THIS_MODULE;
533 spin_lock_init(&smd_tty[7].reset_lock);
534 ret = platform_driver_register(&smd_tty[7].driver);
535 if (ret)
536 goto unreg3;
537 smd_tty[21].driver.probe = smd_tty_dummy_probe;
538 smd_tty[21].driver.driver.name = smd_ch_name[21];
539 smd_tty[21].driver.driver.owner = THIS_MODULE;
540 spin_lock_init(&smd_tty[21].reset_lock);
541 ret = platform_driver_register(&smd_tty[21].driver);
542 if (ret)
543 goto unreg7;
544 smd_tty[27].driver.probe = smd_tty_dummy_probe;
545 smd_tty[27].driver.driver.name = smd_ch_name[27];
546 smd_tty[27].driver.driver.owner = THIS_MODULE;
547 spin_lock_init(&smd_tty[27].reset_lock);
548 ret = platform_driver_register(&smd_tty[27].driver);
549 if (ret)
550 goto unreg21;
551 smd_tty[36].driver.probe = smd_tty_dummy_probe;
552 smd_tty[36].driver.driver.name = "LOOPBACK_TTY";
553 smd_tty[36].driver.driver.owner = THIS_MODULE;
554 spin_lock_init(&smd_tty[36].reset_lock);
555 INIT_DELAYED_WORK(&loopback_work, loopback_probe_worker);
556 ret = platform_driver_register(&smd_tty[36].driver);
557 if (ret)
558 goto unreg27;
559
560 return 0;
561
562unreg27:
563 platform_driver_unregister(&smd_tty[27].driver);
564unreg21:
565 platform_driver_unregister(&smd_tty[21].driver);
566unreg7:
567 platform_driver_unregister(&smd_tty[7].driver);
568unreg3:
569 platform_driver_unregister(&smd_tty[3].driver);
570unreg2:
571 platform_driver_unregister(&smd_tty[2].driver);
572unreg1:
573 platform_driver_unregister(&smd_tty[1].driver);
574unreg0:
575 platform_driver_unregister(&smd_tty[0].driver);
576out:
577 tty_unregister_device(smd_tty_driver, 0);
578 tty_unregister_device(smd_tty_driver, 1);
579 tty_unregister_device(smd_tty_driver, 2);
580 tty_unregister_device(smd_tty_driver, 3);
581 tty_unregister_device(smd_tty_driver, 7);
582 tty_unregister_device(smd_tty_driver, 21);
583 tty_unregister_device(smd_tty_driver, 27);
584 tty_unregister_device(smd_tty_driver, 36);
585 tty_unregister_driver(smd_tty_driver);
586 put_tty_driver(smd_tty_driver);
587 return ret;
588}
589
590module_init(smd_tty_init);