linux/drivers/tty/goldfish.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2007 Google, Inc.
   3 * Copyright (C) 2012 Intel, Inc.
   4 *
   5 * This software is licensed under the terms of the GNU General Public
   6 * License version 2, as published by the Free Software Foundation, and
   7 * may be copied, distributed, and modified under those terms.
   8 *
   9 * This program is distributed in the hope that it will be useful,
  10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12 * GNU General Public License for more details.
  13 *
  14 */
  15
  16#include <linux/console.h>
  17#include <linux/interrupt.h>
  18#include <linux/platform_device.h>
  19#include <linux/tty.h>
  20#include <linux/tty_flip.h>
  21#include <linux/slab.h>
  22#include <linux/io.h>
  23#include <linux/module.h>
  24#include <linux/goldfish.h>
  25
  26enum {
  27        GOLDFISH_TTY_PUT_CHAR       = 0x00,
  28        GOLDFISH_TTY_BYTES_READY    = 0x04,
  29        GOLDFISH_TTY_CMD            = 0x08,
  30
  31        GOLDFISH_TTY_DATA_PTR       = 0x10,
  32        GOLDFISH_TTY_DATA_LEN       = 0x14,
  33        GOLDFISH_TTY_DATA_PTR_HIGH  = 0x18,
  34
  35        GOLDFISH_TTY_CMD_INT_DISABLE    = 0,
  36        GOLDFISH_TTY_CMD_INT_ENABLE     = 1,
  37        GOLDFISH_TTY_CMD_WRITE_BUFFER   = 2,
  38        GOLDFISH_TTY_CMD_READ_BUFFER    = 3,
  39};
  40
  41struct goldfish_tty {
  42        struct tty_port port;
  43        spinlock_t lock;
  44        void __iomem *base;
  45        u32 irq;
  46        int opencount;
  47        struct console console;
  48};
  49
  50static DEFINE_MUTEX(goldfish_tty_lock);
  51static struct tty_driver *goldfish_tty_driver;
  52static u32 goldfish_tty_line_count = 8;
  53static u32 goldfish_tty_current_line_count;
  54static struct goldfish_tty *goldfish_ttys;
  55
  56static void goldfish_tty_do_write(int line, const char *buf, unsigned count)
  57{
  58        unsigned long irq_flags;
  59        struct goldfish_tty *qtty = &goldfish_ttys[line];
  60        void __iomem *base = qtty->base;
  61        spin_lock_irqsave(&qtty->lock, irq_flags);
  62        gf_write_ptr(buf, base + GOLDFISH_TTY_DATA_PTR,
  63                                base + GOLDFISH_TTY_DATA_PTR_HIGH);
  64        writel(count, base + GOLDFISH_TTY_DATA_LEN);
  65        writel(GOLDFISH_TTY_CMD_WRITE_BUFFER, base + GOLDFISH_TTY_CMD);
  66        spin_unlock_irqrestore(&qtty->lock, irq_flags);
  67}
  68
  69static irqreturn_t goldfish_tty_interrupt(int irq, void *dev_id)
  70{
  71        struct platform_device *pdev = dev_id;
  72        struct goldfish_tty *qtty = &goldfish_ttys[pdev->id];
  73        void __iomem *base = qtty->base;
  74        unsigned long irq_flags;
  75        unsigned char *buf;
  76        u32 count;
  77
  78        count = readl(base + GOLDFISH_TTY_BYTES_READY);
  79        if (count == 0)
  80                return IRQ_NONE;
  81
  82        count = tty_prepare_flip_string(&qtty->port, &buf, count);
  83        spin_lock_irqsave(&qtty->lock, irq_flags);
  84        gf_write_ptr(buf, base + GOLDFISH_TTY_DATA_PTR,
  85                                base + GOLDFISH_TTY_DATA_PTR_HIGH);
  86        writel(count, base + GOLDFISH_TTY_DATA_LEN);
  87        writel(GOLDFISH_TTY_CMD_READ_BUFFER, base + GOLDFISH_TTY_CMD);
  88        spin_unlock_irqrestore(&qtty->lock, irq_flags);
  89        tty_schedule_flip(&qtty->port);
  90        return IRQ_HANDLED;
  91}
  92
  93static int goldfish_tty_activate(struct tty_port *port, struct tty_struct *tty)
  94{
  95        struct goldfish_tty *qtty = container_of(port, struct goldfish_tty,
  96                                                                        port);
  97        writel(GOLDFISH_TTY_CMD_INT_ENABLE, qtty->base + GOLDFISH_TTY_CMD);
  98        return 0;
  99}
 100
 101static void goldfish_tty_shutdown(struct tty_port *port)
 102{
 103        struct goldfish_tty *qtty = container_of(port, struct goldfish_tty,
 104                                                                        port);
 105        writel(GOLDFISH_TTY_CMD_INT_DISABLE, qtty->base + GOLDFISH_TTY_CMD);
 106}
 107
 108static int goldfish_tty_open(struct tty_struct *tty, struct file *filp)
 109{
 110        struct goldfish_tty *qtty = &goldfish_ttys[tty->index];
 111        return tty_port_open(&qtty->port, tty, filp);
 112}
 113
 114static void goldfish_tty_close(struct tty_struct *tty, struct file *filp)
 115{
 116        tty_port_close(tty->port, tty, filp);
 117}
 118
 119static void goldfish_tty_hangup(struct tty_struct *tty)
 120{
 121        tty_port_hangup(tty->port);
 122}
 123
 124static int goldfish_tty_write(struct tty_struct *tty, const unsigned char *buf,
 125                                                                int count)
 126{
 127        goldfish_tty_do_write(tty->index, buf, count);
 128        return count;
 129}
 130
 131static int goldfish_tty_write_room(struct tty_struct *tty)
 132{
 133        return 0x10000;
 134}
 135
 136static int goldfish_tty_chars_in_buffer(struct tty_struct *tty)
 137{
 138        struct goldfish_tty *qtty = &goldfish_ttys[tty->index];
 139        void __iomem *base = qtty->base;
 140        return readl(base + GOLDFISH_TTY_BYTES_READY);
 141}
 142
 143static void goldfish_tty_console_write(struct console *co, const char *b,
 144                                                                unsigned count)
 145{
 146        goldfish_tty_do_write(co->index, b, count);
 147}
 148
 149static struct tty_driver *goldfish_tty_console_device(struct console *c,
 150                                                                int *index)
 151{
 152        *index = c->index;
 153        return goldfish_tty_driver;
 154}
 155
 156static int goldfish_tty_console_setup(struct console *co, char *options)
 157{
 158        if ((unsigned)co->index >= goldfish_tty_line_count)
 159                return -ENODEV;
 160        if (!goldfish_ttys[co->index].base)
 161                return -ENODEV;
 162        return 0;
 163}
 164
 165static struct tty_port_operations goldfish_port_ops = {
 166        .activate = goldfish_tty_activate,
 167        .shutdown = goldfish_tty_shutdown
 168};
 169
 170static const struct tty_operations goldfish_tty_ops = {
 171        .open = goldfish_tty_open,
 172        .close = goldfish_tty_close,
 173        .hangup = goldfish_tty_hangup,
 174        .write = goldfish_tty_write,
 175        .write_room = goldfish_tty_write_room,
 176        .chars_in_buffer = goldfish_tty_chars_in_buffer,
 177};
 178
 179static int goldfish_tty_create_driver(void)
 180{
 181        int ret;
 182        struct tty_driver *tty;
 183
 184        goldfish_ttys = kzalloc(sizeof(*goldfish_ttys) *
 185                                goldfish_tty_line_count, GFP_KERNEL);
 186        if (goldfish_ttys == NULL) {
 187                ret = -ENOMEM;
 188                goto err_alloc_goldfish_ttys_failed;
 189        }
 190        tty = alloc_tty_driver(goldfish_tty_line_count);
 191        if (tty == NULL) {
 192                ret = -ENOMEM;
 193                goto err_alloc_tty_driver_failed;
 194        }
 195        tty->driver_name = "goldfish";
 196        tty->name = "ttyGF";
 197        tty->type = TTY_DRIVER_TYPE_SERIAL;
 198        tty->subtype = SERIAL_TYPE_NORMAL;
 199        tty->init_termios = tty_std_termios;
 200        tty->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW |
 201                                                TTY_DRIVER_DYNAMIC_DEV;
 202        tty_set_operations(tty, &goldfish_tty_ops);
 203        ret = tty_register_driver(tty);
 204        if (ret)
 205                goto err_tty_register_driver_failed;
 206
 207        goldfish_tty_driver = tty;
 208        return 0;
 209
 210err_tty_register_driver_failed:
 211        put_tty_driver(tty);
 212err_alloc_tty_driver_failed:
 213        kfree(goldfish_ttys);
 214        goldfish_ttys = NULL;
 215err_alloc_goldfish_ttys_failed:
 216        return ret;
 217}
 218
 219static void goldfish_tty_delete_driver(void)
 220{
 221        tty_unregister_driver(goldfish_tty_driver);
 222        put_tty_driver(goldfish_tty_driver);
 223        goldfish_tty_driver = NULL;
 224        kfree(goldfish_ttys);
 225        goldfish_ttys = NULL;
 226}
 227
 228static int goldfish_tty_probe(struct platform_device *pdev)
 229{
 230        struct goldfish_tty *qtty;
 231        int ret = -EINVAL;
 232        struct resource *r;
 233        struct device *ttydev;
 234        void __iomem *base;
 235        u32 irq;
 236
 237        r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 238        if (r == NULL)
 239                return -EINVAL;
 240
 241        base = ioremap(r->start, 0x1000);
 242        if (base == NULL)
 243                pr_err("goldfish_tty: unable to remap base\n");
 244
 245        r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
 246        if (r == NULL)
 247                goto err_unmap;
 248
 249        irq = r->start;
 250
 251        if (pdev->id >= goldfish_tty_line_count)
 252                goto err_unmap;
 253
 254        mutex_lock(&goldfish_tty_lock);
 255        if (goldfish_tty_current_line_count == 0) {
 256                ret = goldfish_tty_create_driver();
 257                if (ret)
 258                        goto err_create_driver_failed;
 259        }
 260        goldfish_tty_current_line_count++;
 261
 262        qtty = &goldfish_ttys[pdev->id];
 263        spin_lock_init(&qtty->lock);
 264        tty_port_init(&qtty->port);
 265        qtty->port.ops = &goldfish_port_ops;
 266        qtty->base = base;
 267        qtty->irq = irq;
 268
 269        writel(GOLDFISH_TTY_CMD_INT_DISABLE, base + GOLDFISH_TTY_CMD);
 270
 271        ret = request_irq(irq, goldfish_tty_interrupt, IRQF_SHARED,
 272                                                "goldfish_tty", pdev);
 273        if (ret)
 274                goto err_request_irq_failed;
 275
 276
 277        ttydev = tty_port_register_device(&qtty->port, goldfish_tty_driver,
 278                                                        pdev->id, &pdev->dev);
 279        if (IS_ERR(ttydev)) {
 280                ret = PTR_ERR(ttydev);
 281                goto err_tty_register_device_failed;
 282        }
 283
 284        strcpy(qtty->console.name, "ttyGF");
 285        qtty->console.write = goldfish_tty_console_write;
 286        qtty->console.device = goldfish_tty_console_device;
 287        qtty->console.setup = goldfish_tty_console_setup;
 288        qtty->console.flags = CON_PRINTBUFFER;
 289        qtty->console.index = pdev->id;
 290        register_console(&qtty->console);
 291
 292        mutex_unlock(&goldfish_tty_lock);
 293        return 0;
 294
 295err_tty_register_device_failed:
 296        free_irq(irq, pdev);
 297err_request_irq_failed:
 298        goldfish_tty_current_line_count--;
 299        if (goldfish_tty_current_line_count == 0)
 300                goldfish_tty_delete_driver();
 301err_create_driver_failed:
 302        mutex_unlock(&goldfish_tty_lock);
 303err_unmap:
 304        iounmap(base);
 305        return ret;
 306}
 307
 308static int goldfish_tty_remove(struct platform_device *pdev)
 309{
 310        struct goldfish_tty *qtty;
 311
 312        mutex_lock(&goldfish_tty_lock);
 313
 314        qtty = &goldfish_ttys[pdev->id];
 315        unregister_console(&qtty->console);
 316        tty_unregister_device(goldfish_tty_driver, pdev->id);
 317        iounmap(qtty->base);
 318        qtty->base = NULL;
 319        free_irq(qtty->irq, pdev);
 320        goldfish_tty_current_line_count--;
 321        if (goldfish_tty_current_line_count == 0)
 322                goldfish_tty_delete_driver();
 323        mutex_unlock(&goldfish_tty_lock);
 324        return 0;
 325}
 326
 327static struct platform_driver goldfish_tty_platform_driver = {
 328        .probe = goldfish_tty_probe,
 329        .remove = goldfish_tty_remove,
 330        .driver = {
 331                .name = "goldfish_tty"
 332        }
 333};
 334
 335module_platform_driver(goldfish_tty_platform_driver);
 336
 337MODULE_LICENSE("GPL v2");
 338