339 lines · c
1// SPDX-License-Identifier: GPL-2.02/*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 0x30000014/* Each TTYVK channel (TO or FROM) is 0x10000 */15#define BAR1_TTYVK_CHAN_OFFSET 0x10000016/* 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 0x86c40 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 int i;68 int wr;69 u8 c;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 and104 * insert into tty flip buffer105 */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 offset155 * and "from" read offset156 */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 ssize_t bcm_vk_tty_write(struct tty_struct *tty, const u8 *buffer,190 size_t count)191{192 int index;193 struct bcm_vk *vk;194 struct bcm_vk_tty *vktty;195 size_t i;196 197 index = tty->index;198 vk = dev_get_drvdata(tty->dev);199 vktty = &vk->tty[index];200 201 /* Simple write each byte to circular buffer */202 for (i = 0; i < count; i++) {203 vkwrite8(vk, buffer[i], BAR_1,204 VK_BAR_CHAN_DATA(vktty, to, vktty->wr));205 vktty->wr++;206 if (vktty->wr >= vktty->to_size)207 vktty->wr = 0;208 }209 /* Update write offset from shadow register to card */210 vkwrite32(vk, vktty->wr, BAR_1, VK_BAR_CHAN_WR(vktty, to));211 bcm_vk_tty_doorbell(vk, 0);212 213 return count;214}215 216static unsigned int bcm_vk_tty_write_room(struct tty_struct *tty)217{218 struct bcm_vk *vk = dev_get_drvdata(tty->dev);219 220 return vk->tty[tty->index].to_size - 1;221}222 223static const struct tty_operations serial_ops = {224 .open = bcm_vk_tty_open,225 .close = bcm_vk_tty_close,226 .write = bcm_vk_tty_write,227 .write_room = bcm_vk_tty_write_room,228};229 230int bcm_vk_tty_init(struct bcm_vk *vk, char *name)231{232 int i;233 int err;234 struct tty_driver *tty_drv;235 struct device *dev = &vk->pdev->dev;236 237 tty_drv = tty_alloc_driver238 (BCM_VK_NUM_TTY,239 TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV);240 if (IS_ERR(tty_drv))241 return PTR_ERR(tty_drv);242 243 /* Save struct tty_driver for uninstalling the device */244 vk->tty_drv = tty_drv;245 246 /* initialize the tty driver */247 tty_drv->driver_name = KBUILD_MODNAME;248 tty_drv->name = kstrdup(name, GFP_KERNEL);249 if (!tty_drv->name) {250 err = -ENOMEM;251 goto err_tty_driver_kref_put;252 }253 tty_drv->type = TTY_DRIVER_TYPE_SERIAL;254 tty_drv->subtype = SERIAL_TYPE_NORMAL;255 tty_drv->init_termios = tty_std_termios;256 tty_set_operations(tty_drv, &serial_ops);257 258 /* register the tty driver */259 err = tty_register_driver(tty_drv);260 if (err) {261 dev_err(dev, "tty_register_driver failed\n");262 goto err_kfree_tty_name;263 }264 265 for (i = 0; i < BCM_VK_NUM_TTY; i++) {266 struct device *tty_dev;267 268 tty_port_init(&vk->tty[i].port);269 tty_dev = tty_port_register_device_attr(&vk->tty[i].port,270 tty_drv, i, dev, vk,271 NULL);272 if (IS_ERR(tty_dev)) {273 err = PTR_ERR(tty_dev);274 goto unwind;275 }276 vk->tty[i].is_opened = false;277 }278 279 INIT_WORK(&vk->tty_wq_work, bcm_vk_tty_wq_handler);280 vk->tty_wq_thread = create_singlethread_workqueue("tty");281 if (!vk->tty_wq_thread) {282 dev_err(dev, "Fail to create tty workqueue thread\n");283 err = -ENOMEM;284 goto unwind;285 }286 return 0;287 288unwind:289 while (--i >= 0)290 tty_port_unregister_device(&vk->tty[i].port, tty_drv, i);291 tty_unregister_driver(tty_drv);292 293err_kfree_tty_name:294 kfree(tty_drv->name);295 tty_drv->name = NULL;296 297err_tty_driver_kref_put:298 tty_driver_kref_put(tty_drv);299 300 return err;301}302 303void bcm_vk_tty_exit(struct bcm_vk *vk)304{305 int i;306 307 del_timer_sync(&vk->serial_timer);308 for (i = 0; i < BCM_VK_NUM_TTY; ++i) {309 tty_port_unregister_device(&vk->tty[i].port,310 vk->tty_drv,311 i);312 tty_port_destroy(&vk->tty[i].port);313 }314 tty_unregister_driver(vk->tty_drv);315 316 kfree(vk->tty_drv->name);317 vk->tty_drv->name = NULL;318 319 tty_driver_kref_put(vk->tty_drv);320}321 322void bcm_vk_tty_terminate_tty_user(struct bcm_vk *vk)323{324 struct bcm_vk_tty *vktty;325 int i;326 327 for (i = 0; i < BCM_VK_NUM_TTY; ++i) {328 vktty = &vk->tty[i];329 if (vktty->pid)330 kill_pid(find_vpid(vktty->pid), SIGKILL, 1);331 }332}333 334void bcm_vk_tty_wq_exit(struct bcm_vk *vk)335{336 cancel_work_sync(&vk->tty_wq_work);337 destroy_workqueue(vk->tty_wq_thread);338}339