linux/drivers/misc/bcm-vk/bcm_vk_tty.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright 2018-2020 Broadcom.
   4 */
   5
   6#include <linux/tty.h>
   7#include <linux/tty_driver.h>
   8#include <linux/tty_flip.h>
   9
  10#include "bcm_vk.h"
  11
  12/* TTYVK base offset is 0x30000 into BAR1 */
  13#define BAR1_TTYVK_BASE_OFFSET  0x300000
  14/* Each TTYVK channel (TO or FROM) is 0x10000 */
  15#define BAR1_TTYVK_CHAN_OFFSET  0x100000
  16/* Each TTYVK channel has TO and FROM, hence the * 2 */
  17#define BAR1_TTYVK_BASE(index)  (BAR1_TTYVK_BASE_OFFSET + \
  18                                 ((index) * BAR1_TTYVK_CHAN_OFFSET * 2))
  19/* TO TTYVK channel base comes before FROM for each index */
  20#define TO_TTYK_BASE(index)     BAR1_TTYVK_BASE(index)
  21#define FROM_TTYK_BASE(index)   (BAR1_TTYVK_BASE(index) + \
  22                                 BAR1_TTYVK_CHAN_OFFSET)
  23
  24struct bcm_vk_tty_chan {
  25        u32 reserved;
  26        u32 size;
  27        u32 wr;
  28        u32 rd;
  29        u32 *data;
  30};
  31
  32#define VK_BAR_CHAN(v, DIR, e)  ((v)->DIR##_offset \
  33                                 + offsetof(struct bcm_vk_tty_chan, e))
  34#define VK_BAR_CHAN_SIZE(v, DIR)        VK_BAR_CHAN(v, DIR, size)
  35#define VK_BAR_CHAN_WR(v, DIR)          VK_BAR_CHAN(v, DIR, wr)
  36#define VK_BAR_CHAN_RD(v, DIR)          VK_BAR_CHAN(v, DIR, rd)
  37#define VK_BAR_CHAN_DATA(v, DIR, off)   (VK_BAR_CHAN(v, DIR, data) + (off))
  38
  39#define VK_BAR0_REGSEG_TTY_DB_OFFSET    0x86c
  40
  41/* Poll every 1/10 of second - temp hack till we use MSI interrupt */
  42#define SERIAL_TIMER_VALUE (HZ / 10)
  43
  44static void bcm_vk_tty_poll(struct timer_list *t)
  45{
  46        struct bcm_vk *vk = from_timer(vk, t, serial_timer);
  47
  48        queue_work(vk->tty_wq_thread, &vk->tty_wq_work);
  49        mod_timer(&vk->serial_timer, jiffies + SERIAL_TIMER_VALUE);
  50}
  51
  52irqreturn_t bcm_vk_tty_irqhandler(int irq, void *dev_id)
  53{
  54        struct bcm_vk *vk = dev_id;
  55
  56        queue_work(vk->tty_wq_thread, &vk->tty_wq_work);
  57
  58        return IRQ_HANDLED;
  59}
  60
  61static void bcm_vk_tty_wq_handler(struct work_struct *work)
  62{
  63        struct bcm_vk *vk = container_of(work, struct bcm_vk, tty_wq_work);
  64        struct bcm_vk_tty *vktty;
  65        int card_status;
  66        int count;
  67        unsigned char c;
  68        int i;
  69        int wr;
  70
  71        card_status = vkread32(vk, BAR_0, BAR_CARD_STATUS);
  72        if (BCM_VK_INTF_IS_DOWN(card_status))
  73                return;
  74
  75        for (i = 0; i < BCM_VK_NUM_TTY; i++) {
  76                count = 0;
  77                /* Check the card status that the tty channel is ready */
  78                if ((card_status & BIT(i)) == 0)
  79                        continue;
  80
  81                vktty = &vk->tty[i];
  82
  83                /* Don't increment read index if tty app is closed */
  84                if (!vktty->is_opened)
  85                        continue;
  86
  87                /* Fetch the wr offset in buffer from VK */
  88                wr = vkread32(vk, BAR_1, VK_BAR_CHAN_WR(vktty, from));
  89
  90                /* safe to ignore until bar read gives proper size */
  91                if (vktty->from_size == 0)
  92                        continue;
  93
  94                if (wr >= vktty->from_size) {
  95                        dev_err(&vk->pdev->dev,
  96                                "ERROR: wq handler ttyVK%d wr:0x%x > 0x%x\n",
  97                                i, wr, vktty->from_size);
  98                        /* Need to signal and close device in this case */
  99                        continue;
 100                }
 101
 102                /*
 103                 * Simple read of circular buffer and
 104                 * insert into tty flip buffer
 105                 */
 106                while (vk->tty[i].rd != wr) {
 107                        c = vkread8(vk, BAR_1,
 108                                    VK_BAR_CHAN_DATA(vktty, from, vktty->rd));
 109                        vktty->rd++;
 110                        if (vktty->rd >= vktty->from_size)
 111                                vktty->rd = 0;
 112                        tty_insert_flip_char(&vktty->port, c, TTY_NORMAL);
 113                        count++;
 114                }
 115
 116                if (count) {
 117                        tty_flip_buffer_push(&vktty->port);
 118
 119                        /* Update read offset from shadow register to card */
 120                        vkwrite32(vk, vktty->rd, BAR_1,
 121                                  VK_BAR_CHAN_RD(vktty, from));
 122                }
 123        }
 124}
 125
 126static int bcm_vk_tty_open(struct tty_struct *tty, struct file *file)
 127{
 128        int card_status;
 129        struct bcm_vk *vk;
 130        struct bcm_vk_tty *vktty;
 131        int index;
 132
 133        /* initialize the pointer in case something fails */
 134        tty->driver_data = NULL;
 135
 136        vk = (struct bcm_vk *)dev_get_drvdata(tty->dev);
 137        index = tty->index;
 138
 139        if (index >= BCM_VK_NUM_TTY)
 140                return -EINVAL;
 141
 142        vktty = &vk->tty[index];
 143
 144        vktty->pid = task_pid_nr(current);
 145        vktty->to_offset = TO_TTYK_BASE(index);
 146        vktty->from_offset = FROM_TTYK_BASE(index);
 147
 148        /* Do not allow tty device to be opened if tty on card not ready */
 149        card_status = vkread32(vk, BAR_0, BAR_CARD_STATUS);
 150        if (BCM_VK_INTF_IS_DOWN(card_status) || ((card_status & BIT(index)) == 0))
 151                return -EBUSY;
 152
 153        /*
 154         * Get shadow registers of the buffer sizes and the "to" write offset
 155         * and "from" read offset
 156         */
 157        vktty->to_size = vkread32(vk, BAR_1, VK_BAR_CHAN_SIZE(vktty, to));
 158        vktty->wr = vkread32(vk, BAR_1,  VK_BAR_CHAN_WR(vktty, to));
 159        vktty->from_size = vkread32(vk, BAR_1, VK_BAR_CHAN_SIZE(vktty, from));
 160        vktty->rd = vkread32(vk, BAR_1,  VK_BAR_CHAN_RD(vktty, from));
 161        vktty->is_opened = true;
 162
 163        if (tty->count == 1 && !vktty->irq_enabled) {
 164                timer_setup(&vk->serial_timer, bcm_vk_tty_poll, 0);
 165                mod_timer(&vk->serial_timer, jiffies + SERIAL_TIMER_VALUE);
 166        }
 167        return 0;
 168}
 169
 170static void bcm_vk_tty_close(struct tty_struct *tty, struct file *file)
 171{
 172        struct bcm_vk *vk = dev_get_drvdata(tty->dev);
 173
 174        if (tty->index >= BCM_VK_NUM_TTY)
 175                return;
 176
 177        vk->tty[tty->index].is_opened = false;
 178
 179        if (tty->count == 1)
 180                del_timer_sync(&vk->serial_timer);
 181}
 182
 183static void bcm_vk_tty_doorbell(struct bcm_vk *vk, u32 db_val)
 184{
 185        vkwrite32(vk, db_val, BAR_0,
 186                  VK_BAR0_REGSEG_DB_BASE + VK_BAR0_REGSEG_TTY_DB_OFFSET);
 187}
 188
 189static int bcm_vk_tty_write(struct tty_struct *tty,
 190                            const unsigned char *buffer,
 191                            int count)
 192{
 193        int index;
 194        struct bcm_vk *vk;
 195        struct bcm_vk_tty *vktty;
 196        int i;
 197
 198        index = tty->index;
 199        vk = dev_get_drvdata(tty->dev);
 200        vktty = &vk->tty[index];
 201
 202        /* Simple write each byte to circular buffer */
 203        for (i = 0; i < count; i++) {
 204                vkwrite8(vk, buffer[i], BAR_1,
 205                         VK_BAR_CHAN_DATA(vktty, to, vktty->wr));
 206                vktty->wr++;
 207                if (vktty->wr >= vktty->to_size)
 208                        vktty->wr = 0;
 209        }
 210        /* Update write offset from shadow register to card */
 211        vkwrite32(vk, vktty->wr, BAR_1, VK_BAR_CHAN_WR(vktty, to));
 212        bcm_vk_tty_doorbell(vk, 0);
 213
 214        return count;
 215}
 216
 217static unsigned int bcm_vk_tty_write_room(struct tty_struct *tty)
 218{
 219        struct bcm_vk *vk = dev_get_drvdata(tty->dev);
 220
 221        return vk->tty[tty->index].to_size - 1;
 222}
 223
 224static const struct tty_operations serial_ops = {
 225        .open = bcm_vk_tty_open,
 226        .close = bcm_vk_tty_close,
 227        .write = bcm_vk_tty_write,
 228        .write_room = bcm_vk_tty_write_room,
 229};
 230
 231int bcm_vk_tty_init(struct bcm_vk *vk, char *name)
 232{
 233        int i;
 234        int err;
 235        struct tty_driver *tty_drv;
 236        struct device *dev = &vk->pdev->dev;
 237
 238        tty_drv = tty_alloc_driver
 239                                (BCM_VK_NUM_TTY,
 240                                 TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV);
 241        if (IS_ERR(tty_drv))
 242                return PTR_ERR(tty_drv);
 243
 244        /* Save struct tty_driver for uninstalling the device */
 245        vk->tty_drv = tty_drv;
 246
 247        /* initialize the tty driver */
 248        tty_drv->driver_name = KBUILD_MODNAME;
 249        tty_drv->name = kstrdup(name, GFP_KERNEL);
 250        if (!tty_drv->name) {
 251                err = -ENOMEM;
 252                goto err_put_tty_driver;
 253        }
 254        tty_drv->type = TTY_DRIVER_TYPE_SERIAL;
 255        tty_drv->subtype = SERIAL_TYPE_NORMAL;
 256        tty_drv->init_termios = tty_std_termios;
 257        tty_set_operations(tty_drv, &serial_ops);
 258
 259        /* register the tty driver */
 260        err = tty_register_driver(tty_drv);
 261        if (err) {
 262                dev_err(dev, "tty_register_driver failed\n");
 263                goto err_kfree_tty_name;
 264        }
 265
 266        for (i = 0; i < BCM_VK_NUM_TTY; i++) {
 267                struct device *tty_dev;
 268
 269                tty_port_init(&vk->tty[i].port);
 270                tty_dev = tty_port_register_device(&vk->tty[i].port, tty_drv,
 271                                                   i, dev);
 272                if (IS_ERR(tty_dev)) {
 273                        err = PTR_ERR(tty_dev);
 274                        goto unwind;
 275                }
 276                dev_set_drvdata(tty_dev, vk);
 277                vk->tty[i].is_opened = false;
 278        }
 279
 280        INIT_WORK(&vk->tty_wq_work, bcm_vk_tty_wq_handler);
 281        vk->tty_wq_thread = create_singlethread_workqueue("tty");
 282        if (!vk->tty_wq_thread) {
 283                dev_err(dev, "Fail to create tty workqueue thread\n");
 284                err = -ENOMEM;
 285                goto unwind;
 286        }
 287        return 0;
 288
 289unwind:
 290        while (--i >= 0)
 291                tty_port_unregister_device(&vk->tty[i].port, tty_drv, i);
 292        tty_unregister_driver(tty_drv);
 293
 294err_kfree_tty_name:
 295        kfree(tty_drv->name);
 296        tty_drv->name = NULL;
 297
 298err_put_tty_driver:
 299        put_tty_driver(tty_drv);
 300
 301        return err;
 302}
 303
 304void bcm_vk_tty_exit(struct bcm_vk *vk)
 305{
 306        int i;
 307
 308        del_timer_sync(&vk->serial_timer);
 309        for (i = 0; i < BCM_VK_NUM_TTY; ++i) {
 310                tty_port_unregister_device(&vk->tty[i].port,
 311                                           vk->tty_drv,
 312                                           i);
 313                tty_port_destroy(&vk->tty[i].port);
 314        }
 315        tty_unregister_driver(vk->tty_drv);
 316
 317        kfree(vk->tty_drv->name);
 318        vk->tty_drv->name = NULL;
 319
 320        put_tty_driver(vk->tty_drv);
 321}
 322
 323void bcm_vk_tty_terminate_tty_user(struct bcm_vk *vk)
 324{
 325        struct bcm_vk_tty *vktty;
 326        int i;
 327
 328        for (i = 0; i < BCM_VK_NUM_TTY; ++i) {
 329                vktty = &vk->tty[i];
 330                if (vktty->pid)
 331                        kill_pid(find_vpid(vktty->pid), SIGKILL, 1);
 332        }
 333}
 334
 335void bcm_vk_tty_wq_exit(struct bcm_vk *vk)
 336{
 337        cancel_work_sync(&vk->tty_wq_work);
 338        destroy_workqueue(vk->tty_wq_thread);
 339}
 340