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