Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

misc: bcm-vk: add ttyVK support

Add ttyVK support to driver to allow console access to VK card from host.

Device node will be in the follow form /dev/bcm-vk.x_ttyVKy where:
x is the instance of the VK card
y is the tty device number on the VK card

Acked-by: Olof Johansson <olof@lixom.net>
Signed-off-by: Scott Branden <scott.branden@broadcom.com>
Link: https://lore.kernel.org/r/20210120175827.14820-14-scott.branden@broadcom.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Scott Branden and committed by
Greg Kroah-Hartman
91ca10d6 68f1fae6

+392 -2
+2 -1
drivers/misc/bcm-vk/Makefile
··· 7 7 bcm_vk-objs := \ 8 8 bcm_vk_dev.o \ 9 9 bcm_vk_msg.o \ 10 - bcm_vk_sg.o 10 + bcm_vk_sg.o \ 11 + bcm_vk_tty.o 11 12
+28
drivers/misc/bcm-vk/bcm_vk.h
··· 8 8 9 9 #include <linux/atomic.h> 10 10 #include <linux/firmware.h> 11 + #include <linux/irq.h> 11 12 #include <linux/kref.h> 12 13 #include <linux/miscdevice.h> 13 14 #include <linux/mutex.h> 14 15 #include <linux/pci.h> 15 16 #include <linux/poll.h> 16 17 #include <linux/sched/signal.h> 18 + #include <linux/tty.h> 17 19 #include <linux/uaccess.h> 18 20 #include <uapi/linux/misc/bcm_vk.h> 19 21 ··· 86 84 #define CODEPUSH_BOOT2_ENTRY 0x60000000 87 85 88 86 #define BAR_CARD_STATUS 0x410 87 + /* CARD_STATUS definitions */ 88 + #define CARD_STATUS_TTYVK0_READY BIT(0) 89 + #define CARD_STATUS_TTYVK1_READY BIT(1) 89 90 90 91 #define BAR_BOOT1_STDALONE_PROGRESS 0x420 91 92 #define BOOT1_STDALONE_SUCCESS (BIT(13) | BIT(14)) ··· 260 255 261 256 #define BCM_VK_NUM_TTY 2 262 257 258 + struct bcm_vk_tty { 259 + struct tty_port port; 260 + u32 to_offset; /* bar offset to use */ 261 + u32 to_size; /* to VK buffer size */ 262 + u32 wr; /* write offset shadow */ 263 + u32 from_offset; /* bar offset to use */ 264 + u32 from_size; /* from VK buffer size */ 265 + u32 rd; /* read offset shadow */ 266 + pid_t pid; 267 + bool irq_enabled; 268 + bool is_opened; /* tracks tty open/close */ 269 + }; 270 + 263 271 /* VK device max power state, supports 3, full, reduced and low */ 264 272 #define MAX_OPP 3 265 273 #define MAX_CARD_INFO_TAG_SIZE 64 ··· 365 347 struct mutex mutex; 366 348 struct miscdevice miscdev; 367 349 int devid; /* dev id allocated */ 350 + 351 + struct tty_driver *tty_drv; 352 + struct timer_list serial_timer; 353 + struct bcm_vk_tty tty[BCM_VK_NUM_TTY]; 354 + struct workqueue_struct *tty_wq_thread; 355 + struct work_struct tty_wq_work; 368 356 369 357 /* Reference-counting to handle file operations */ 370 358 struct kref kref; ··· 490 466 void bcm_vk_release_data(struct kref *kref); 491 467 irqreturn_t bcm_vk_msgq_irqhandler(int irq, void *dev_id); 492 468 irqreturn_t bcm_vk_notf_irqhandler(int irq, void *dev_id); 469 + irqreturn_t bcm_vk_tty_irqhandler(int irq, void *dev_id); 493 470 int bcm_vk_msg_init(struct bcm_vk *vk); 494 471 void bcm_vk_msg_remove(struct bcm_vk *vk); 495 472 void bcm_vk_drain_msg_on_reset(struct bcm_vk *vk); ··· 501 476 const pid_t pid, const u32 q_num); 502 477 void bcm_to_v_q_doorbell(struct bcm_vk *vk, u32 q_num, u32 db_val); 503 478 int bcm_vk_auto_load_all_images(struct bcm_vk *vk); 479 + int bcm_vk_tty_init(struct bcm_vk *vk, char *name); 480 + void bcm_vk_tty_exit(struct bcm_vk *vk); 481 + void bcm_vk_tty_terminate_tty_user(struct bcm_vk *vk); 504 482 void bcm_vk_hb_init(struct bcm_vk *vk); 505 483 void bcm_vk_hb_deinit(struct bcm_vk *vk); 506 484 void bcm_vk_handle_notf(struct bcm_vk *vk);
+29 -1
drivers/misc/bcm-vk/bcm_vk_dev.c
··· 525 525 } 526 526 } 527 527 } 528 + bcm_vk_tty_terminate_tty_user(vk); 528 529 spin_unlock(&vk->ctx_lock); 529 530 } 530 531 ··· 1385 1384 } 1386 1385 vk->num_irqs++; 1387 1386 1387 + for (i = 0; 1388 + (i < VK_MSIX_TTY_MAX) && (vk->num_irqs < irq); 1389 + i++, vk->num_irqs++) { 1390 + err = devm_request_irq(dev, pci_irq_vector(pdev, vk->num_irqs), 1391 + bcm_vk_tty_irqhandler, 1392 + IRQF_SHARED, DRV_MODULE_NAME, vk); 1393 + if (err) { 1394 + dev_err(dev, "failed request tty IRQ %d for MSIX %d\n", 1395 + pdev->irq + vk->num_irqs, vk->num_irqs + 1); 1396 + goto err_irq; 1397 + } 1398 + vk->tty[i].irq_enabled = true; 1399 + } 1400 + 1388 1401 id = ida_simple_get(&bcm_vk_ida, 0, 0, GFP_KERNEL); 1389 1402 if (id < 0) { 1390 1403 err = id; ··· 1451 1436 goto err_destroy_workqueue; 1452 1437 } 1453 1438 1439 + snprintf(name, sizeof(name), KBUILD_MODNAME ".%d_ttyVK", id); 1440 + err = bcm_vk_tty_init(vk, name); 1441 + if (err) 1442 + goto err_unregister_panic_notifier; 1443 + 1454 1444 /* 1455 1445 * lets trigger an auto download. We don't want to do it serially here 1456 1446 * because at probing time, it is not supposed to block for a long time. ··· 1464 1444 if (auto_load) { 1465 1445 if ((boot_status & BOOT_STATE_MASK) == BROM_RUNNING) { 1466 1446 if (bcm_vk_trigger_autoload(vk)) 1467 - goto err_unregister_panic_notifier; 1447 + goto err_bcm_vk_tty_exit; 1468 1448 } else { 1469 1449 dev_err(dev, 1470 1450 "Auto-load skipped - BROM not in proper state (0x%x)\n", ··· 1478 1458 dev_dbg(dev, "BCM-VK:%u created\n", id); 1479 1459 1480 1460 return 0; 1461 + 1462 + err_bcm_vk_tty_exit: 1463 + bcm_vk_tty_exit(vk); 1481 1464 1482 1465 err_unregister_panic_notifier: 1483 1466 atomic_notifier_chain_unregister(&panic_notifier_list, ··· 1559 1536 atomic_notifier_chain_unregister(&panic_notifier_list, 1560 1537 &vk->panic_nb); 1561 1538 1539 + bcm_vk_msg_remove(vk); 1540 + bcm_vk_tty_exit(vk); 1541 + 1562 1542 if (vk->tdma_vaddr) 1563 1543 dma_free_coherent(&pdev->dev, nr_scratch_pages * PAGE_SIZE, 1564 1544 vk->tdma_vaddr, vk->tdma_addr); ··· 1580 1554 1581 1555 cancel_work_sync(&vk->wq_work); 1582 1556 destroy_workqueue(vk->wq_thread); 1557 + cancel_work_sync(&vk->tty_wq_work); 1558 + destroy_workqueue(vk->tty_wq_thread); 1583 1559 1584 1560 for (i = 0; i < MAX_BAR; i++) { 1585 1561 if (vk->bar[i])
+333
drivers/misc/bcm-vk/bcm_vk_tty.c
··· 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 + 24 + struct 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 + 44 + static 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 + 52 + irqreturn_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 + 61 + static 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 + 126 + static 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 + 170 + static 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 + 183 + static 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 + 189 + static 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 + 217 + static 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 + 224 + static 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 + 231 + int 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 + 289 + unwind: 290 + while (--i >= 0) 291 + tty_port_unregister_device(&vk->tty[i].port, tty_drv, i); 292 + tty_unregister_driver(tty_drv); 293 + 294 + err_kfree_tty_name: 295 + kfree(tty_drv->name); 296 + tty_drv->name = NULL; 297 + 298 + err_put_tty_driver: 299 + put_tty_driver(tty_drv); 300 + 301 + return err; 302 + } 303 + 304 + void 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 + 323 + void 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 + }