1f16aa97dSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 255bd2133SJag Raman /* vcc.c: sun4v virtual channel concentrator 355bd2133SJag Raman * 455bd2133SJag Raman * Copyright (C) 2017 Oracle. All rights reserved. 555bd2133SJag Raman */ 655bd2133SJag Raman 75d171050SJag Raman #include <linux/delay.h> 85d171050SJag Raman #include <linux/interrupt.h> 955bd2133SJag Raman #include <linux/module.h> 105d171050SJag Raman #include <linux/slab.h> 118868e44aSJag Raman #include <linux/sysfs.h> 12ce808b74SJag Raman #include <linux/tty.h> 13f8c55335SJag Raman #include <linux/tty_flip.h> 14*c9874d3fSAl Viro #include <linux/termios_internal.h> 155d171050SJag Raman #include <asm/vio.h> 165d171050SJag Raman #include <asm/ldc.h> 1755bd2133SJag Raman 18f283ebd5SJag Raman MODULE_DESCRIPTION("Sun LDOM virtual console concentrator driver"); 19f283ebd5SJag Raman MODULE_LICENSE("GPL"); 2073300191SJiri Slaby MODULE_VERSION("1.1"); 21f283ebd5SJag Raman 225d171050SJag Raman struct vcc_port { 235d171050SJag Raman struct vio_driver_state vio; 245d171050SJag Raman 255d171050SJag Raman spinlock_t lock; 265d171050SJag Raman char *domain; 275d171050SJag Raman struct tty_struct *tty; /* only populated while dev is open */ 285d171050SJag Raman unsigned long index; /* index into the vcc_table */ 295d171050SJag Raman 305d171050SJag Raman u64 refcnt; 315d171050SJag Raman bool excl_locked; 325d171050SJag Raman 335d171050SJag Raman bool removed; 345d171050SJag Raman 355d171050SJag Raman /* This buffer is required to support the tty write_room interface 365d171050SJag Raman * and guarantee that any characters that the driver accepts will 375d171050SJag Raman * be eventually sent, either immediately or later. 385d171050SJag Raman */ 395d171050SJag Raman int chars_in_buffer; 405d171050SJag Raman struct vio_vcc buffer; 415d171050SJag Raman 425d171050SJag Raman struct timer_list rx_timer; 435d171050SJag Raman struct timer_list tx_timer; 445d171050SJag Raman }; 455d171050SJag Raman 465d171050SJag Raman /* Microseconds that thread will delay waiting for a vcc port ref */ 475d171050SJag Raman #define VCC_REF_DELAY 100 485d171050SJag Raman 49ce808b74SJag Raman #define VCC_MAX_PORTS 1024 50ce808b74SJag Raman #define VCC_MINOR_START 0 /* must be zero */ 51f8c55335SJag Raman #define VCC_BUFF_LEN VIO_VCC_MTU_SIZE 52ce808b74SJag Raman 538868e44aSJag Raman #define VCC_CTL_BREAK -1 548868e44aSJag Raman #define VCC_CTL_HUP -2 558868e44aSJag Raman 56ce808b74SJag Raman static struct tty_driver *vcc_tty_driver; 57ce808b74SJag Raman 585d171050SJag Raman static struct vcc_port *vcc_table[VCC_MAX_PORTS]; 595d171050SJag Raman static DEFINE_SPINLOCK(vcc_table_lock); 605d171050SJag Raman 614c472fc0SJiri Slaby static unsigned int vcc_dbg; 624c472fc0SJiri Slaby static unsigned int vcc_dbg_ldc; 634c472fc0SJiri Slaby static unsigned int vcc_dbg_vio; 64f283ebd5SJag Raman 65f283ebd5SJag Raman module_param(vcc_dbg, uint, 0664); 66f283ebd5SJag Raman module_param(vcc_dbg_ldc, uint, 0664); 67f283ebd5SJag Raman module_param(vcc_dbg_vio, uint, 0664); 68f283ebd5SJag Raman 69f283ebd5SJag Raman #define VCC_DBG_DRV 0x1 70f283ebd5SJag Raman #define VCC_DBG_LDC 0x2 71f283ebd5SJag Raman #define VCC_DBG_PKT 0x4 72f283ebd5SJag Raman 73f283ebd5SJag Raman #define vccdbg(f, a...) \ 74f283ebd5SJag Raman do { \ 75f283ebd5SJag Raman if (vcc_dbg & VCC_DBG_DRV) \ 76f283ebd5SJag Raman pr_info(f, ## a); \ 77f283ebd5SJag Raman } while (0) \ 78f283ebd5SJag Raman 79f283ebd5SJag Raman #define vccdbgl(l) \ 80f283ebd5SJag Raman do { \ 81f283ebd5SJag Raman if (vcc_dbg & VCC_DBG_LDC) \ 82f283ebd5SJag Raman ldc_print(l); \ 83f283ebd5SJag Raman } while (0) \ 84f283ebd5SJag Raman 85f283ebd5SJag Raman #define vccdbgp(pkt) \ 86f283ebd5SJag Raman do { \ 87f283ebd5SJag Raman if (vcc_dbg & VCC_DBG_PKT) { \ 88f283ebd5SJag Raman int i; \ 89f283ebd5SJag Raman for (i = 0; i < pkt.tag.stype; i++) \ 90f283ebd5SJag Raman pr_info("[%c]", pkt.data[i]); \ 91f283ebd5SJag Raman } \ 92f283ebd5SJag Raman } while (0) \ 93f283ebd5SJag Raman 94ce808b74SJag Raman /* Note: Be careful when adding flags to this line discipline. Don't 95ce808b74SJag Raman * add anything that will cause echoing or we'll go into recursive 96ce808b74SJag Raman * loop echoing chars back and forth with the console drivers. 97ce808b74SJag Raman */ 985bd0ea91SBhumika Goyal static const struct ktermios vcc_tty_termios = { 99ce808b74SJag Raman .c_iflag = IGNBRK | IGNPAR, 100ce808b74SJag Raman .c_oflag = OPOST, 101ce808b74SJag Raman .c_cflag = B38400 | CS8 | CREAD | HUPCL, 102ce808b74SJag Raman .c_cc = INIT_C_CC, 103ce808b74SJag Raman .c_ispeed = 38400, 104ce808b74SJag Raman .c_ospeed = 38400 105ce808b74SJag Raman }; 106ce808b74SJag Raman 1075d171050SJag Raman /** 1085d171050SJag Raman * vcc_table_add() - Add VCC port to the VCC table 1095d171050SJag Raman * @port: pointer to the VCC port 1105d171050SJag Raman * 1115d171050SJag Raman * Return: index of the port in the VCC table on success, 1125d171050SJag Raman * -1 on failure 1135d171050SJag Raman */ 1145d171050SJag Raman static int vcc_table_add(struct vcc_port *port) 1155d171050SJag Raman { 1165d171050SJag Raman unsigned long flags; 1175d171050SJag Raman int i; 1185d171050SJag Raman 1195d171050SJag Raman spin_lock_irqsave(&vcc_table_lock, flags); 1205d171050SJag Raman for (i = VCC_MINOR_START; i < VCC_MAX_PORTS; i++) { 1215d171050SJag Raman if (!vcc_table[i]) { 1225d171050SJag Raman vcc_table[i] = port; 1235d171050SJag Raman break; 1245d171050SJag Raman } 1255d171050SJag Raman } 1265d171050SJag Raman spin_unlock_irqrestore(&vcc_table_lock, flags); 1275d171050SJag Raman 1285d171050SJag Raman if (i < VCC_MAX_PORTS) 1295d171050SJag Raman return i; 1305d171050SJag Raman else 1315d171050SJag Raman return -1; 1325d171050SJag Raman } 1335d171050SJag Raman 1345d171050SJag Raman /** 1355d171050SJag Raman * vcc_table_remove() - Removes a VCC port from the VCC table 1365d171050SJag Raman * @index: Index into the VCC table 1375d171050SJag Raman */ 1385d171050SJag Raman static void vcc_table_remove(unsigned long index) 1395d171050SJag Raman { 1405d171050SJag Raman unsigned long flags; 1415d171050SJag Raman 1425d171050SJag Raman if (WARN_ON(index >= VCC_MAX_PORTS)) 1435d171050SJag Raman return; 1445d171050SJag Raman 1455d171050SJag Raman spin_lock_irqsave(&vcc_table_lock, flags); 1465d171050SJag Raman vcc_table[index] = NULL; 1475d171050SJag Raman spin_unlock_irqrestore(&vcc_table_lock, flags); 1485d171050SJag Raman } 1495d171050SJag Raman 1505d171050SJag Raman /** 1515d171050SJag Raman * vcc_get() - Gets a reference to VCC port 1525d171050SJag Raman * @index: Index into the VCC table 1535d171050SJag Raman * @excl: Indicates if an exclusive access is requested 1545d171050SJag Raman * 1555d171050SJag Raman * Return: reference to the VCC port, if found 1565d171050SJag Raman * NULL, if port not found 1575d171050SJag Raman */ 1585d171050SJag Raman static struct vcc_port *vcc_get(unsigned long index, bool excl) 1595d171050SJag Raman { 1605d171050SJag Raman struct vcc_port *port; 1615d171050SJag Raman unsigned long flags; 1625d171050SJag Raman 1635d171050SJag Raman try_again: 1645d171050SJag Raman spin_lock_irqsave(&vcc_table_lock, flags); 1655d171050SJag Raman 1665d171050SJag Raman port = vcc_table[index]; 1675d171050SJag Raman if (!port) { 1685d171050SJag Raman spin_unlock_irqrestore(&vcc_table_lock, flags); 1695d171050SJag Raman return NULL; 1705d171050SJag Raman } 1715d171050SJag Raman 1725d171050SJag Raman if (!excl) { 1735d171050SJag Raman if (port->excl_locked) { 1745d171050SJag Raman spin_unlock_irqrestore(&vcc_table_lock, flags); 1755d171050SJag Raman udelay(VCC_REF_DELAY); 1765d171050SJag Raman goto try_again; 1775d171050SJag Raman } 1785d171050SJag Raman port->refcnt++; 1795d171050SJag Raman spin_unlock_irqrestore(&vcc_table_lock, flags); 1805d171050SJag Raman return port; 1815d171050SJag Raman } 1825d171050SJag Raman 1835d171050SJag Raman if (port->refcnt) { 1845d171050SJag Raman spin_unlock_irqrestore(&vcc_table_lock, flags); 1855d171050SJag Raman /* Threads wanting exclusive access will wait half the time, 1865d171050SJag Raman * probably giving them higher priority in the case of 1875d171050SJag Raman * multiple waiters. 1885d171050SJag Raman */ 1895d171050SJag Raman udelay(VCC_REF_DELAY/2); 1905d171050SJag Raman goto try_again; 1915d171050SJag Raman } 1925d171050SJag Raman 1935d171050SJag Raman port->refcnt++; 1945d171050SJag Raman port->excl_locked = true; 1955d171050SJag Raman spin_unlock_irqrestore(&vcc_table_lock, flags); 1965d171050SJag Raman 1975d171050SJag Raman return port; 1985d171050SJag Raman } 1995d171050SJag Raman 2005d171050SJag Raman /** 2015d171050SJag Raman * vcc_put() - Returns a reference to VCC port 2025d171050SJag Raman * @port: pointer to VCC port 2035d171050SJag Raman * @excl: Indicates if the returned reference is an exclusive reference 2045d171050SJag Raman * 2055d171050SJag Raman * Note: It's the caller's responsibility to ensure the correct value 2065d171050SJag Raman * for the excl flag 2075d171050SJag Raman */ 2085d171050SJag Raman static void vcc_put(struct vcc_port *port, bool excl) 2095d171050SJag Raman { 2105d171050SJag Raman unsigned long flags; 2115d171050SJag Raman 2125d171050SJag Raman if (!port) 2135d171050SJag Raman return; 2145d171050SJag Raman 2155d171050SJag Raman spin_lock_irqsave(&vcc_table_lock, flags); 2165d171050SJag Raman 2175d171050SJag Raman /* check if caller attempted to put with the wrong flags */ 2185d171050SJag Raman if (WARN_ON((excl && !port->excl_locked) || 2195d171050SJag Raman (!excl && port->excl_locked))) 2205d171050SJag Raman goto done; 2215d171050SJag Raman 2225d171050SJag Raman port->refcnt--; 2235d171050SJag Raman 2245d171050SJag Raman if (excl) 2255d171050SJag Raman port->excl_locked = false; 2265d171050SJag Raman 2275d171050SJag Raman done: 2285d171050SJag Raman spin_unlock_irqrestore(&vcc_table_lock, flags); 2295d171050SJag Raman } 2305d171050SJag Raman 2315d171050SJag Raman /** 2325d171050SJag Raman * vcc_get_ne() - Get a non-exclusive reference to VCC port 2335d171050SJag Raman * @index: Index into the VCC table 2345d171050SJag Raman * 2355d171050SJag Raman * Gets a non-exclusive reference to VCC port, if it's not removed 2365d171050SJag Raman * 2375d171050SJag Raman * Return: pointer to the VCC port, if found 2385d171050SJag Raman * NULL, if port not found 2395d171050SJag Raman */ 2405d171050SJag Raman static struct vcc_port *vcc_get_ne(unsigned long index) 2415d171050SJag Raman { 2425d171050SJag Raman struct vcc_port *port; 2435d171050SJag Raman 2445d171050SJag Raman port = vcc_get(index, false); 2455d171050SJag Raman 2465d171050SJag Raman if (port && port->removed) { 2475d171050SJag Raman vcc_put(port, false); 2485d171050SJag Raman return NULL; 2495d171050SJag Raman } 2505d171050SJag Raman 2515d171050SJag Raman return port; 2525d171050SJag Raman } 2535d171050SJag Raman 254f8c55335SJag Raman static void vcc_kick_rx(struct vcc_port *port) 255f8c55335SJag Raman { 256f8c55335SJag Raman struct vio_driver_state *vio = &port->vio; 257f8c55335SJag Raman 258f8c55335SJag Raman assert_spin_locked(&port->lock); 259f8c55335SJag Raman 260f8c55335SJag Raman if (!timer_pending(&port->rx_timer) && !port->removed) { 261f8c55335SJag Raman disable_irq_nosync(vio->vdev->rx_irq); 262f8c55335SJag Raman port->rx_timer.expires = (jiffies + 1); 263f8c55335SJag Raman add_timer(&port->rx_timer); 264f8c55335SJag Raman } 265f8c55335SJag Raman } 266f8c55335SJag Raman 267f8c55335SJag Raman static void vcc_kick_tx(struct vcc_port *port) 268f8c55335SJag Raman { 269f8c55335SJag Raman assert_spin_locked(&port->lock); 270f8c55335SJag Raman 271f8c55335SJag Raman if (!timer_pending(&port->tx_timer) && !port->removed) { 272f8c55335SJag Raman port->tx_timer.expires = (jiffies + 1); 273f8c55335SJag Raman add_timer(&port->tx_timer); 274f8c55335SJag Raman } 275f8c55335SJag Raman } 276f8c55335SJag Raman 277f8c55335SJag Raman static int vcc_rx_check(struct tty_struct *tty, int size) 278f8c55335SJag Raman { 279f8c55335SJag Raman if (WARN_ON(!tty || !tty->port)) 280f8c55335SJag Raman return 1; 281f8c55335SJag Raman 282f8c55335SJag Raman /* tty_buffer_request_room won't sleep because it uses 283f8c55335SJag Raman * GFP_ATOMIC flag to allocate buffer 284f8c55335SJag Raman */ 285f8c55335SJag Raman if (test_bit(TTY_THROTTLED, &tty->flags) || 286f8c55335SJag Raman (tty_buffer_request_room(tty->port, VCC_BUFF_LEN) < VCC_BUFF_LEN)) 287f8c55335SJag Raman return 0; 288f8c55335SJag Raman 289f8c55335SJag Raman return 1; 290f8c55335SJag Raman } 291f8c55335SJag Raman 292f8c55335SJag Raman static int vcc_rx(struct tty_struct *tty, char *buf, int size) 293f8c55335SJag Raman { 294f8c55335SJag Raman int len = 0; 295f8c55335SJag Raman 296f8c55335SJag Raman if (WARN_ON(!tty || !tty->port)) 297f8c55335SJag Raman return len; 298f8c55335SJag Raman 299f8c55335SJag Raman len = tty_insert_flip_string(tty->port, buf, size); 300f8c55335SJag Raman if (len) 301f8c55335SJag Raman tty_flip_buffer_push(tty->port); 302f8c55335SJag Raman 303f8c55335SJag Raman return len; 304f8c55335SJag Raman } 305f8c55335SJag Raman 306f8c55335SJag Raman static int vcc_ldc_read(struct vcc_port *port) 307f8c55335SJag Raman { 308f8c55335SJag Raman struct vio_driver_state *vio = &port->vio; 309f8c55335SJag Raman struct tty_struct *tty; 310f8c55335SJag Raman struct vio_vcc pkt; 311f8c55335SJag Raman int rv = 0; 312f8c55335SJag Raman 313f8c55335SJag Raman tty = port->tty; 314f8c55335SJag Raman if (!tty) { 315f8c55335SJag Raman rv = ldc_rx_reset(vio->lp); 316f8c55335SJag Raman vccdbg("VCC: reset rx q: rv=%d\n", rv); 317f8c55335SJag Raman goto done; 318f8c55335SJag Raman } 319f8c55335SJag Raman 320f8c55335SJag Raman /* Read as long as LDC has incoming data. */ 321f8c55335SJag Raman while (1) { 322f8c55335SJag Raman if (!vcc_rx_check(tty, VIO_VCC_MTU_SIZE)) { 323f8c55335SJag Raman vcc_kick_rx(port); 324f8c55335SJag Raman break; 325f8c55335SJag Raman } 326f8c55335SJag Raman 327f8c55335SJag Raman vccdbgl(vio->lp); 328f8c55335SJag Raman 329f8c55335SJag Raman rv = ldc_read(vio->lp, &pkt, sizeof(pkt)); 330f8c55335SJag Raman if (rv <= 0) 331f8c55335SJag Raman break; 332f8c55335SJag Raman 333f8c55335SJag Raman vccdbg("VCC: ldc_read()=%d\n", rv); 334f8c55335SJag Raman vccdbg("TAG [%02x:%02x:%04x:%08x]\n", 335f8c55335SJag Raman pkt.tag.type, pkt.tag.stype, 336f8c55335SJag Raman pkt.tag.stype_env, pkt.tag.sid); 337f8c55335SJag Raman 338f8c55335SJag Raman if (pkt.tag.type == VIO_TYPE_DATA) { 339f8c55335SJag Raman vccdbgp(pkt); 340f8c55335SJag Raman /* vcc_rx_check ensures memory availability */ 341f8c55335SJag Raman vcc_rx(tty, pkt.data, pkt.tag.stype); 342f8c55335SJag Raman } else { 343f8c55335SJag Raman pr_err("VCC: unknown msg [%02x:%02x:%04x:%08x]\n", 344f8c55335SJag Raman pkt.tag.type, pkt.tag.stype, 345f8c55335SJag Raman pkt.tag.stype_env, pkt.tag.sid); 346f8c55335SJag Raman rv = -ECONNRESET; 347f8c55335SJag Raman break; 348f8c55335SJag Raman } 349f8c55335SJag Raman 350f8c55335SJag Raman WARN_ON(rv != LDC_PACKET_SIZE); 351f8c55335SJag Raman } 352f8c55335SJag Raman 353f8c55335SJag Raman done: 354f8c55335SJag Raman return rv; 355f8c55335SJag Raman } 356f8c55335SJag Raman 3574790b6dcSKees Cook static void vcc_rx_timer(struct timer_list *t) 358f8c55335SJag Raman { 3594790b6dcSKees Cook struct vcc_port *port = from_timer(port, t, rx_timer); 360f8c55335SJag Raman struct vio_driver_state *vio; 361f8c55335SJag Raman unsigned long flags; 362f8c55335SJag Raman int rv; 363f8c55335SJag Raman 364f8c55335SJag Raman spin_lock_irqsave(&port->lock, flags); 365f8c55335SJag Raman port->rx_timer.expires = 0; 366f8c55335SJag Raman 367f8c55335SJag Raman vio = &port->vio; 368f8c55335SJag Raman 369f8c55335SJag Raman enable_irq(vio->vdev->rx_irq); 370f8c55335SJag Raman 371f8c55335SJag Raman if (!port->tty || port->removed) 372f8c55335SJag Raman goto done; 373f8c55335SJag Raman 374f8c55335SJag Raman rv = vcc_ldc_read(port); 375f8c55335SJag Raman if (rv == -ECONNRESET) 376f8c55335SJag Raman vio_conn_reset(vio); 377f8c55335SJag Raman 378f8c55335SJag Raman done: 379f8c55335SJag Raman spin_unlock_irqrestore(&port->lock, flags); 380f8c55335SJag Raman vcc_put(port, false); 381f8c55335SJag Raman } 382f8c55335SJag Raman 3834790b6dcSKees Cook static void vcc_tx_timer(struct timer_list *t) 384f8c55335SJag Raman { 3854790b6dcSKees Cook struct vcc_port *port = from_timer(port, t, tx_timer); 386f8c55335SJag Raman struct vio_vcc *pkt; 387f8c55335SJag Raman unsigned long flags; 388f8c55335SJag Raman int tosend = 0; 389f8c55335SJag Raman int rv; 390f8c55335SJag Raman 391f8c55335SJag Raman spin_lock_irqsave(&port->lock, flags); 392f8c55335SJag Raman port->tx_timer.expires = 0; 393f8c55335SJag Raman 394f8c55335SJag Raman if (!port->tty || port->removed) 395f8c55335SJag Raman goto done; 396f8c55335SJag Raman 397f8c55335SJag Raman tosend = min(VCC_BUFF_LEN, port->chars_in_buffer); 398f8c55335SJag Raman if (!tosend) 399f8c55335SJag Raman goto done; 400f8c55335SJag Raman 401f8c55335SJag Raman pkt = &port->buffer; 402f8c55335SJag Raman pkt->tag.type = VIO_TYPE_DATA; 403f8c55335SJag Raman pkt->tag.stype = tosend; 404f8c55335SJag Raman vccdbgl(port->vio.lp); 405f8c55335SJag Raman 406f8c55335SJag Raman rv = ldc_write(port->vio.lp, pkt, (VIO_TAG_SIZE + tosend)); 407f8c55335SJag Raman WARN_ON(!rv); 408f8c55335SJag Raman 409f8c55335SJag Raman if (rv < 0) { 410f8c55335SJag Raman vccdbg("VCC: ldc_write()=%d\n", rv); 411f8c55335SJag Raman vcc_kick_tx(port); 412f8c55335SJag Raman } else { 413f8c55335SJag Raman struct tty_struct *tty = port->tty; 414f8c55335SJag Raman 415f8c55335SJag Raman port->chars_in_buffer = 0; 416f8c55335SJag Raman if (tty) 417f8c55335SJag Raman tty_wakeup(tty); 418f8c55335SJag Raman } 419f8c55335SJag Raman 420f8c55335SJag Raman done: 421f8c55335SJag Raman spin_unlock_irqrestore(&port->lock, flags); 422f8c55335SJag Raman vcc_put(port, false); 423f8c55335SJag Raman } 424f8c55335SJag Raman 425103b9b0bSJag Raman /** 426103b9b0bSJag Raman * vcc_event() - LDC event processing engine 427103b9b0bSJag Raman * @arg: VCC private data 428103b9b0bSJag Raman * @event: LDC event 429103b9b0bSJag Raman * 430103b9b0bSJag Raman * Handles LDC events for VCC 431103b9b0bSJag Raman */ 4325d171050SJag Raman static void vcc_event(void *arg, int event) 4335d171050SJag Raman { 434103b9b0bSJag Raman struct vio_driver_state *vio; 435103b9b0bSJag Raman struct vcc_port *port; 436103b9b0bSJag Raman unsigned long flags; 437103b9b0bSJag Raman int rv; 438103b9b0bSJag Raman 439103b9b0bSJag Raman port = arg; 440103b9b0bSJag Raman vio = &port->vio; 441103b9b0bSJag Raman 442103b9b0bSJag Raman spin_lock_irqsave(&port->lock, flags); 443103b9b0bSJag Raman 444103b9b0bSJag Raman switch (event) { 445103b9b0bSJag Raman case LDC_EVENT_RESET: 446103b9b0bSJag Raman case LDC_EVENT_UP: 447103b9b0bSJag Raman vio_link_state_change(vio, event); 448103b9b0bSJag Raman break; 449103b9b0bSJag Raman 450103b9b0bSJag Raman case LDC_EVENT_DATA_READY: 451103b9b0bSJag Raman rv = vcc_ldc_read(port); 452103b9b0bSJag Raman if (rv == -ECONNRESET) 453103b9b0bSJag Raman vio_conn_reset(vio); 454103b9b0bSJag Raman break; 455103b9b0bSJag Raman 456103b9b0bSJag Raman default: 457103b9b0bSJag Raman pr_err("VCC: unexpected LDC event(%d)\n", event); 458103b9b0bSJag Raman } 459103b9b0bSJag Raman 460103b9b0bSJag Raman spin_unlock_irqrestore(&port->lock, flags); 4615d171050SJag Raman } 4625d171050SJag Raman 4635d171050SJag Raman static struct ldc_channel_config vcc_ldc_cfg = { 4645d171050SJag Raman .event = vcc_event, 4655d171050SJag Raman .mtu = VIO_VCC_MTU_SIZE, 4665d171050SJag Raman .mode = LDC_MODE_RAW, 4675d171050SJag Raman .debug = 0, 4685d171050SJag Raman }; 4695d171050SJag Raman 4705d171050SJag Raman /* Ordered from largest major to lowest */ 4715d171050SJag Raman static struct vio_version vcc_versions[] = { 4725d171050SJag Raman { .major = 1, .minor = 0 }, 4735d171050SJag Raman }; 4745d171050SJag Raman 4758f03f948SJag Raman static struct tty_port_operations vcc_port_ops = { 0 }; 4768f03f948SJag Raman 4772877389fSZhen Lei static ssize_t domain_show(struct device *dev, 4788868e44aSJag Raman struct device_attribute *attr, 4798868e44aSJag Raman char *buf) 4808868e44aSJag Raman { 4818868e44aSJag Raman struct vcc_port *port; 4828868e44aSJag Raman int rv; 4838868e44aSJag Raman 4848868e44aSJag Raman port = dev_get_drvdata(dev); 4858868e44aSJag Raman if (!port) 4868868e44aSJag Raman return -ENODEV; 4878868e44aSJag Raman 4888868e44aSJag Raman rv = scnprintf(buf, PAGE_SIZE, "%s\n", port->domain); 4898868e44aSJag Raman 4908868e44aSJag Raman return rv; 4918868e44aSJag Raman } 4928868e44aSJag Raman 4938868e44aSJag Raman static int vcc_send_ctl(struct vcc_port *port, int ctl) 4948868e44aSJag Raman { 4958868e44aSJag Raman struct vio_vcc pkt; 4968868e44aSJag Raman int rv; 4978868e44aSJag Raman 4988868e44aSJag Raman pkt.tag.type = VIO_TYPE_CTRL; 4998868e44aSJag Raman pkt.tag.sid = ctl; 5008868e44aSJag Raman pkt.tag.stype = 0; 5018868e44aSJag Raman 5028868e44aSJag Raman rv = ldc_write(port->vio.lp, &pkt, sizeof(pkt.tag)); 5038868e44aSJag Raman WARN_ON(!rv); 5048868e44aSJag Raman vccdbg("VCC: ldc_write(%ld)=%d\n", sizeof(pkt.tag), rv); 5058868e44aSJag Raman 5068868e44aSJag Raman return rv; 5078868e44aSJag Raman } 5088868e44aSJag Raman 5092877389fSZhen Lei static ssize_t break_store(struct device *dev, 5108868e44aSJag Raman struct device_attribute *attr, 5118868e44aSJag Raman const char *buf, size_t count) 5128868e44aSJag Raman { 5138868e44aSJag Raman struct vcc_port *port; 5148868e44aSJag Raman unsigned long flags; 5158868e44aSJag Raman int rv = count; 5168868e44aSJag Raman int brk; 5178868e44aSJag Raman 5188868e44aSJag Raman port = dev_get_drvdata(dev); 5198868e44aSJag Raman if (!port) 5208868e44aSJag Raman return -ENODEV; 5218868e44aSJag Raman 5228868e44aSJag Raman spin_lock_irqsave(&port->lock, flags); 5238868e44aSJag Raman 5248868e44aSJag Raman if (sscanf(buf, "%ud", &brk) != 1 || brk != 1) 5258868e44aSJag Raman rv = -EINVAL; 5268868e44aSJag Raman else if (vcc_send_ctl(port, VCC_CTL_BREAK) < 0) 527f8c55335SJag Raman vcc_kick_tx(port); 5288868e44aSJag Raman 5298868e44aSJag Raman spin_unlock_irqrestore(&port->lock, flags); 5308868e44aSJag Raman 5318868e44aSJag Raman return rv; 5328868e44aSJag Raman } 5338868e44aSJag Raman 5342877389fSZhen Lei static DEVICE_ATTR_ADMIN_RO(domain); 5352877389fSZhen Lei static DEVICE_ATTR_WO(break); 5368868e44aSJag Raman 5378868e44aSJag Raman static struct attribute *vcc_sysfs_entries[] = { 5388868e44aSJag Raman &dev_attr_domain.attr, 5398868e44aSJag Raman &dev_attr_break.attr, 5408868e44aSJag Raman NULL 5418868e44aSJag Raman }; 5428868e44aSJag Raman 5438868e44aSJag Raman static struct attribute_group vcc_attribute_group = { 5448868e44aSJag Raman .name = NULL, 5458868e44aSJag Raman .attrs = vcc_sysfs_entries, 5468868e44aSJag Raman }; 5478868e44aSJag Raman 5485d171050SJag Raman /** 5495d171050SJag Raman * vcc_probe() - Initialize VCC port 5505d171050SJag Raman * @vdev: Pointer to VIO device of the new VCC port 5515d171050SJag Raman * @id: VIO device ID 5525d171050SJag Raman * 5535d171050SJag Raman * Initializes a VCC port to receive serial console data from 5545d171050SJag Raman * the guest domain. Sets up a TTY end point on the control 5555d171050SJag Raman * domain. Sets up VIO/LDC link between the guest & control 5565d171050SJag Raman * domain endpoints. 5575d171050SJag Raman * 5585d171050SJag Raman * Return: status of the probe 5595d171050SJag Raman */ 5605d171050SJag Raman static int vcc_probe(struct vio_dev *vdev, const struct vio_device_id *id) 5615d171050SJag Raman { 5625d171050SJag Raman struct mdesc_handle *hp; 5635d171050SJag Raman struct vcc_port *port; 5645d171050SJag Raman struct device *dev; 5655d171050SJag Raman const char *domain; 5665d171050SJag Raman char *name; 5675d171050SJag Raman u64 node; 5685d171050SJag Raman int rv; 5695d171050SJag Raman 5705d171050SJag Raman vccdbg("VCC: name=%s\n", dev_name(&vdev->dev)); 5715d171050SJag Raman 5725d171050SJag Raman if (!vcc_tty_driver) { 5735d171050SJag Raman pr_err("VCC: TTY driver not registered\n"); 5745d171050SJag Raman return -ENODEV; 5755d171050SJag Raman } 5765d171050SJag Raman 5775d171050SJag Raman port = kzalloc(sizeof(struct vcc_port), GFP_KERNEL); 5785d171050SJag Raman if (!port) 5795d171050SJag Raman return -ENOMEM; 5805d171050SJag Raman 5815d171050SJag Raman name = kstrdup(dev_name(&vdev->dev), GFP_KERNEL); 5825d171050SJag Raman 5835d171050SJag Raman rv = vio_driver_init(&port->vio, vdev, VDEV_CONSOLE_CON, vcc_versions, 5845d171050SJag Raman ARRAY_SIZE(vcc_versions), NULL, name); 5855d171050SJag Raman if (rv) 5865d171050SJag Raman goto free_port; 5875d171050SJag Raman 5885d171050SJag Raman port->vio.debug = vcc_dbg_vio; 5895d171050SJag Raman vcc_ldc_cfg.debug = vcc_dbg_ldc; 5905d171050SJag Raman 5915d171050SJag Raman rv = vio_ldc_alloc(&port->vio, &vcc_ldc_cfg, port); 5925d171050SJag Raman if (rv) 5935d171050SJag Raman goto free_port; 5945d171050SJag Raman 5955d171050SJag Raman spin_lock_init(&port->lock); 5965d171050SJag Raman 5975d171050SJag Raman port->index = vcc_table_add(port); 5985d171050SJag Raman if (port->index == -1) { 5995d171050SJag Raman pr_err("VCC: no more TTY indices left for allocation\n"); 600ff62255aSWei Yongjun rv = -ENOMEM; 6015d171050SJag Raman goto free_ldc; 6025d171050SJag Raman } 6035d171050SJag Raman 6045d171050SJag Raman /* Register the device using VCC table index as TTY index */ 6055d171050SJag Raman dev = tty_register_device(vcc_tty_driver, port->index, &vdev->dev); 6065d171050SJag Raman if (IS_ERR(dev)) { 6075d171050SJag Raman rv = PTR_ERR(dev); 6085d171050SJag Raman goto free_table; 6095d171050SJag Raman } 6105d171050SJag Raman 6115d171050SJag Raman hp = mdesc_grab(); 6125d171050SJag Raman 6135d171050SJag Raman node = vio_vdev_node(hp, vdev); 6145d171050SJag Raman if (node == MDESC_NODE_NULL) { 6155d171050SJag Raman rv = -ENXIO; 6165d171050SJag Raman mdesc_release(hp); 6175d171050SJag Raman goto unreg_tty; 6185d171050SJag Raman } 6195d171050SJag Raman 6205d171050SJag Raman domain = mdesc_get_property(hp, node, "vcc-domain-name", NULL); 6215d171050SJag Raman if (!domain) { 6225d171050SJag Raman rv = -ENXIO; 6235d171050SJag Raman mdesc_release(hp); 6245d171050SJag Raman goto unreg_tty; 6255d171050SJag Raman } 6265d171050SJag Raman port->domain = kstrdup(domain, GFP_KERNEL); 6275d171050SJag Raman 6285d171050SJag Raman mdesc_release(hp); 6295d171050SJag Raman 6308868e44aSJag Raman rv = sysfs_create_group(&vdev->dev.kobj, &vcc_attribute_group); 6318868e44aSJag Raman if (rv) 6328868e44aSJag Raman goto free_domain; 6338868e44aSJag Raman 6344790b6dcSKees Cook timer_setup(&port->rx_timer, vcc_rx_timer, 0); 6354790b6dcSKees Cook timer_setup(&port->tx_timer, vcc_tx_timer, 0); 636f8c55335SJag Raman 6375d171050SJag Raman dev_set_drvdata(&vdev->dev, port); 6385d171050SJag Raman 6395d171050SJag Raman /* It's possible to receive IRQs in the middle of vio_port_up. Disable 6405d171050SJag Raman * IRQs until the port is up. 6415d171050SJag Raman */ 6425d171050SJag Raman disable_irq_nosync(vdev->rx_irq); 6435d171050SJag Raman vio_port_up(&port->vio); 6445d171050SJag Raman enable_irq(vdev->rx_irq); 6455d171050SJag Raman 6465d171050SJag Raman return 0; 6475d171050SJag Raman 6488868e44aSJag Raman free_domain: 6498868e44aSJag Raman kfree(port->domain); 6505d171050SJag Raman unreg_tty: 6515d171050SJag Raman tty_unregister_device(vcc_tty_driver, port->index); 6525d171050SJag Raman free_table: 6535d171050SJag Raman vcc_table_remove(port->index); 6545d171050SJag Raman free_ldc: 6555d171050SJag Raman vio_ldc_free(&port->vio); 6565d171050SJag Raman free_port: 6575d171050SJag Raman kfree(name); 6585d171050SJag Raman kfree(port); 6595d171050SJag Raman 6605d171050SJag Raman return rv; 6615d171050SJag Raman } 6625d171050SJag Raman 6635d171050SJag Raman /** 6645d171050SJag Raman * vcc_remove() - Terminate a VCC port 6655d171050SJag Raman * @vdev: Pointer to VIO device of the VCC port 6665d171050SJag Raman * 6675d171050SJag Raman * Terminates a VCC port. Sets up the teardown of TTY and 6685d171050SJag Raman * VIO/LDC link between guest and primary domains. 6695d171050SJag Raman * 6705d171050SJag Raman * Return: status of removal 6715d171050SJag Raman */ 6721553573cSUwe Kleine-König static void vcc_remove(struct vio_dev *vdev) 6735d171050SJag Raman { 6745d171050SJag Raman struct vcc_port *port = dev_get_drvdata(&vdev->dev); 6755d171050SJag Raman 676f8c55335SJag Raman del_timer_sync(&port->rx_timer); 677f8c55335SJag Raman del_timer_sync(&port->tx_timer); 678f8c55335SJag Raman 6795d171050SJag Raman /* If there's a process with the device open, do a synchronous 6805d171050SJag Raman * hangup of the TTY. This *may* cause the process to call close 6815d171050SJag Raman * asynchronously, but it's not guaranteed. 6825d171050SJag Raman */ 6835d171050SJag Raman if (port->tty) 6845d171050SJag Raman tty_vhangup(port->tty); 6855d171050SJag Raman 6865d171050SJag Raman /* Get exclusive reference to VCC, ensures that there are no other 68763e34e70SUwe Kleine-König * clients to this port. This cannot fail. 6885d171050SJag Raman */ 68963e34e70SUwe Kleine-König vcc_get(port->index, true); 6905d171050SJag Raman 6915d171050SJag Raman tty_unregister_device(vcc_tty_driver, port->index); 6925d171050SJag Raman 6935d171050SJag Raman del_timer_sync(&port->vio.timer); 6945d171050SJag Raman vio_ldc_free(&port->vio); 6958868e44aSJag Raman sysfs_remove_group(&vdev->dev.kobj, &vcc_attribute_group); 6965d171050SJag Raman dev_set_drvdata(&vdev->dev, NULL); 6975d171050SJag Raman if (port->tty) { 6985d171050SJag Raman port->removed = true; 6995d171050SJag Raman vcc_put(port, true); 7005d171050SJag Raman } else { 7015d171050SJag Raman vcc_table_remove(port->index); 7025d171050SJag Raman 7035d171050SJag Raman kfree(port->vio.name); 7045d171050SJag Raman kfree(port->domain); 7055d171050SJag Raman kfree(port); 7065d171050SJag Raman } 7075d171050SJag Raman } 7085d171050SJag Raman 7095d171050SJag Raman static const struct vio_device_id vcc_match[] = { 7105d171050SJag Raman { 7115d171050SJag Raman .type = "vcc-port", 7125d171050SJag Raman }, 7135d171050SJag Raman {}, 7145d171050SJag Raman }; 7155d171050SJag Raman MODULE_DEVICE_TABLE(vio, vcc_match); 7165d171050SJag Raman 7175d171050SJag Raman static struct vio_driver vcc_driver = { 7185d171050SJag Raman .id_table = vcc_match, 7195d171050SJag Raman .probe = vcc_probe, 7205d171050SJag Raman .remove = vcc_remove, 7215d171050SJag Raman .name = "vcc", 7225d171050SJag Raman }; 7235d171050SJag Raman 72463a71744SJag Raman static int vcc_open(struct tty_struct *tty, struct file *vcc_file) 72563a71744SJag Raman { 72663a71744SJag Raman struct vcc_port *port; 72763a71744SJag Raman 72863a71744SJag Raman if (tty->count > 1) 72963a71744SJag Raman return -EBUSY; 73063a71744SJag Raman 73163a71744SJag Raman port = vcc_get_ne(tty->index); 73263a71744SJag Raman if (unlikely(!port)) { 73363a71744SJag Raman pr_err("VCC: open: Failed to find VCC port\n"); 73463a71744SJag Raman return -ENODEV; 73563a71744SJag Raman } 73663a71744SJag Raman 73763a71744SJag Raman if (unlikely(!port->vio.lp)) { 73863a71744SJag Raman pr_err("VCC: open: LDC channel not configured\n"); 73963a71744SJag Raman vcc_put(port, false); 74063a71744SJag Raman return -EPIPE; 74163a71744SJag Raman } 74263a71744SJag Raman vccdbgl(port->vio.lp); 74363a71744SJag Raman 74463a71744SJag Raman vcc_put(port, false); 74563a71744SJag Raman 74663a71744SJag Raman if (unlikely(!tty->port)) { 74763a71744SJag Raman pr_err("VCC: open: TTY port not found\n"); 74863a71744SJag Raman return -ENXIO; 74963a71744SJag Raman } 75063a71744SJag Raman 75163a71744SJag Raman if (unlikely(!tty->port->ops)) { 75263a71744SJag Raman pr_err("VCC: open: TTY ops not defined\n"); 75363a71744SJag Raman return -ENXIO; 75463a71744SJag Raman } 75563a71744SJag Raman 75663a71744SJag Raman return tty_port_open(tty->port, tty, vcc_file); 75763a71744SJag Raman } 75863a71744SJag Raman 75963a71744SJag Raman static void vcc_close(struct tty_struct *tty, struct file *vcc_file) 76063a71744SJag Raman { 76163a71744SJag Raman if (unlikely(tty->count > 1)) 76263a71744SJag Raman return; 76363a71744SJag Raman 76463a71744SJag Raman if (unlikely(!tty->port)) { 76563a71744SJag Raman pr_err("VCC: close: TTY port not found\n"); 76663a71744SJag Raman return; 76763a71744SJag Raman } 76863a71744SJag Raman 76963a71744SJag Raman tty_port_close(tty->port, tty, vcc_file); 77063a71744SJag Raman } 77163a71744SJag Raman 7726a3ff25bSJag Raman static void vcc_ldc_hup(struct vcc_port *port) 7736a3ff25bSJag Raman { 7746a3ff25bSJag Raman unsigned long flags; 7756a3ff25bSJag Raman 7766a3ff25bSJag Raman spin_lock_irqsave(&port->lock, flags); 7776a3ff25bSJag Raman 7786a3ff25bSJag Raman if (vcc_send_ctl(port, VCC_CTL_HUP) < 0) 7796a3ff25bSJag Raman vcc_kick_tx(port); 7806a3ff25bSJag Raman 7816a3ff25bSJag Raman spin_unlock_irqrestore(&port->lock, flags); 7826a3ff25bSJag Raman } 7836a3ff25bSJag Raman 7846a3ff25bSJag Raman static void vcc_hangup(struct tty_struct *tty) 7856a3ff25bSJag Raman { 7866a3ff25bSJag Raman struct vcc_port *port; 7876a3ff25bSJag Raman 7886a3ff25bSJag Raman port = vcc_get_ne(tty->index); 7896a3ff25bSJag Raman if (unlikely(!port)) { 7906a3ff25bSJag Raman pr_err("VCC: hangup: Failed to find VCC port\n"); 7916a3ff25bSJag Raman return; 7926a3ff25bSJag Raman } 7936a3ff25bSJag Raman 7946a3ff25bSJag Raman if (unlikely(!tty->port)) { 7956a3ff25bSJag Raman pr_err("VCC: hangup: TTY port not found\n"); 7966a3ff25bSJag Raman vcc_put(port, false); 7976a3ff25bSJag Raman return; 7986a3ff25bSJag Raman } 7996a3ff25bSJag Raman 8006a3ff25bSJag Raman vcc_ldc_hup(port); 8016a3ff25bSJag Raman 8026a3ff25bSJag Raman vcc_put(port, false); 8036a3ff25bSJag Raman 8046a3ff25bSJag Raman tty_port_hangup(tty->port); 8056a3ff25bSJag Raman } 8066a3ff25bSJag Raman 807cf045260SJag Raman static int vcc_write(struct tty_struct *tty, const unsigned char *buf, 808cf045260SJag Raman int count) 809cf045260SJag Raman { 810cf045260SJag Raman struct vcc_port *port; 811cf045260SJag Raman struct vio_vcc *pkt; 812cf045260SJag Raman unsigned long flags; 813cf045260SJag Raman int total_sent = 0; 814cf045260SJag Raman int tosend = 0; 815cf045260SJag Raman int rv = -EINVAL; 816cf045260SJag Raman 817cf045260SJag Raman port = vcc_get_ne(tty->index); 818cf045260SJag Raman if (unlikely(!port)) { 819cf045260SJag Raman pr_err("VCC: write: Failed to find VCC port"); 820cf045260SJag Raman return -ENODEV; 821cf045260SJag Raman } 822cf045260SJag Raman 823cf045260SJag Raman spin_lock_irqsave(&port->lock, flags); 824cf045260SJag Raman 825cf045260SJag Raman pkt = &port->buffer; 826cf045260SJag Raman pkt->tag.type = VIO_TYPE_DATA; 827cf045260SJag Raman 828cf045260SJag Raman while (count > 0) { 829cf045260SJag Raman /* Minimum of data to write and space available */ 830cf045260SJag Raman tosend = min(count, (VCC_BUFF_LEN - port->chars_in_buffer)); 831cf045260SJag Raman 832cf045260SJag Raman if (!tosend) 833cf045260SJag Raman break; 834cf045260SJag Raman 835cf045260SJag Raman memcpy(&pkt->data[port->chars_in_buffer], &buf[total_sent], 836cf045260SJag Raman tosend); 837cf045260SJag Raman port->chars_in_buffer += tosend; 838cf045260SJag Raman pkt->tag.stype = tosend; 839cf045260SJag Raman 840cf045260SJag Raman vccdbg("TAG [%02x:%02x:%04x:%08x]\n", pkt->tag.type, 841cf045260SJag Raman pkt->tag.stype, pkt->tag.stype_env, pkt->tag.sid); 842cf045260SJag Raman vccdbg("DATA [%s]\n", pkt->data); 843cf045260SJag Raman vccdbgl(port->vio.lp); 844cf045260SJag Raman 845cf045260SJag Raman /* Since we know we have enough room in VCC buffer for tosend 846cf045260SJag Raman * we record that it was sent regardless of whether the 847cf045260SJag Raman * hypervisor actually took it because we have it buffered. 848cf045260SJag Raman */ 849cf045260SJag Raman rv = ldc_write(port->vio.lp, pkt, (VIO_TAG_SIZE + tosend)); 850cf045260SJag Raman vccdbg("VCC: write: ldc_write(%d)=%d\n", 851cf045260SJag Raman (VIO_TAG_SIZE + tosend), rv); 852cf045260SJag Raman 853cf045260SJag Raman total_sent += tosend; 854cf045260SJag Raman count -= tosend; 855cf045260SJag Raman if (rv < 0) { 856cf045260SJag Raman vcc_kick_tx(port); 857cf045260SJag Raman break; 858cf045260SJag Raman } 859cf045260SJag Raman 860cf045260SJag Raman port->chars_in_buffer = 0; 861cf045260SJag Raman } 862cf045260SJag Raman 863cf045260SJag Raman spin_unlock_irqrestore(&port->lock, flags); 864cf045260SJag Raman 865cf045260SJag Raman vcc_put(port, false); 866cf045260SJag Raman 867cf045260SJag Raman vccdbg("VCC: write: total=%d rv=%d", total_sent, rv); 868cf045260SJag Raman 869cf045260SJag Raman return total_sent ? total_sent : rv; 870cf045260SJag Raman } 871cf045260SJag Raman 87203b3b1a2SJiri Slaby static unsigned int vcc_write_room(struct tty_struct *tty) 873cf045260SJag Raman { 874cf045260SJag Raman struct vcc_port *port; 87503b3b1a2SJiri Slaby unsigned int num; 876cf045260SJag Raman 877cf045260SJag Raman port = vcc_get_ne(tty->index); 878cf045260SJag Raman if (unlikely(!port)) { 879cf045260SJag Raman pr_err("VCC: write_room: Failed to find VCC port\n"); 8806bfbfcfcSJiri Slaby return 0; 881cf045260SJag Raman } 882cf045260SJag Raman 883cf045260SJag Raman num = VCC_BUFF_LEN - port->chars_in_buffer; 884cf045260SJag Raman 885cf045260SJag Raman vcc_put(port, false); 886cf045260SJag Raman 887cf045260SJag Raman return num; 888cf045260SJag Raman } 889cf045260SJag Raman 890fff4ef17SJiri Slaby static unsigned int vcc_chars_in_buffer(struct tty_struct *tty) 891aeee7debSJag Raman { 892aeee7debSJag Raman struct vcc_port *port; 893fff4ef17SJiri Slaby unsigned int num; 894aeee7debSJag Raman 895aeee7debSJag Raman port = vcc_get_ne(tty->index); 896aeee7debSJag Raman if (unlikely(!port)) { 897aeee7debSJag Raman pr_err("VCC: chars_in_buffer: Failed to find VCC port\n"); 89810eb63e5SJiri Slaby return 0; 899aeee7debSJag Raman } 900aeee7debSJag Raman 901aeee7debSJag Raman num = port->chars_in_buffer; 902aeee7debSJag Raman 903aeee7debSJag Raman vcc_put(port, false); 904aeee7debSJag Raman 905aeee7debSJag Raman return num; 906aeee7debSJag Raman } 907aeee7debSJag Raman 908e942e577SJag Raman static int vcc_break_ctl(struct tty_struct *tty, int state) 909e942e577SJag Raman { 910e942e577SJag Raman struct vcc_port *port; 911e942e577SJag Raman unsigned long flags; 912e942e577SJag Raman 913e942e577SJag Raman port = vcc_get_ne(tty->index); 914e942e577SJag Raman if (unlikely(!port)) { 915e942e577SJag Raman pr_err("VCC: break_ctl: Failed to find VCC port\n"); 916e942e577SJag Raman return -ENODEV; 917e942e577SJag Raman } 918e942e577SJag Raman 919e942e577SJag Raman /* Turn off break */ 920e942e577SJag Raman if (state == 0) { 921e942e577SJag Raman vcc_put(port, false); 922e942e577SJag Raman return 0; 923e942e577SJag Raman } 924e942e577SJag Raman 925e942e577SJag Raman spin_lock_irqsave(&port->lock, flags); 926e942e577SJag Raman 927e942e577SJag Raman if (vcc_send_ctl(port, VCC_CTL_BREAK) < 0) 928e942e577SJag Raman vcc_kick_tx(port); 929e942e577SJag Raman 930e942e577SJag Raman spin_unlock_irqrestore(&port->lock, flags); 931e942e577SJag Raman 932e942e577SJag Raman vcc_put(port, false); 933e942e577SJag Raman 934e942e577SJag Raman return 0; 935e942e577SJag Raman } 936e942e577SJag Raman 9378f03f948SJag Raman static int vcc_install(struct tty_driver *driver, struct tty_struct *tty) 9388f03f948SJag Raman { 9398f03f948SJag Raman struct vcc_port *port_vcc; 9408f03f948SJag Raman struct tty_port *port_tty; 9418f03f948SJag Raman int ret; 9428f03f948SJag Raman 9438f03f948SJag Raman if (tty->index >= VCC_MAX_PORTS) 9448f03f948SJag Raman return -EINVAL; 9458f03f948SJag Raman 9468f03f948SJag Raman ret = tty_standard_install(driver, tty); 9478f03f948SJag Raman if (ret) 9488f03f948SJag Raman return ret; 9498f03f948SJag Raman 9508f03f948SJag Raman port_tty = kzalloc(sizeof(struct tty_port), GFP_KERNEL); 9518f03f948SJag Raman if (!port_tty) 9528f03f948SJag Raman return -ENOMEM; 9538f03f948SJag Raman 9548f03f948SJag Raman port_vcc = vcc_get(tty->index, true); 9558f03f948SJag Raman if (!port_vcc) { 9568f03f948SJag Raman pr_err("VCC: install: Failed to find VCC port\n"); 9578f03f948SJag Raman tty->port = NULL; 9588f03f948SJag Raman kfree(port_tty); 9598f03f948SJag Raman return -ENODEV; 9608f03f948SJag Raman } 9618f03f948SJag Raman 9628f03f948SJag Raman tty_port_init(port_tty); 9638f03f948SJag Raman port_tty->ops = &vcc_port_ops; 9648f03f948SJag Raman tty->port = port_tty; 9658f03f948SJag Raman 9668f03f948SJag Raman port_vcc->tty = tty; 9678f03f948SJag Raman 9688f03f948SJag Raman vcc_put(port_vcc, true); 9698f03f948SJag Raman 9708f03f948SJag Raman return 0; 9718f03f948SJag Raman } 9728f03f948SJag Raman 9738f03f948SJag Raman static void vcc_cleanup(struct tty_struct *tty) 9748f03f948SJag Raman { 9758f03f948SJag Raman struct vcc_port *port; 9768f03f948SJag Raman 9778f03f948SJag Raman port = vcc_get(tty->index, true); 9788f03f948SJag Raman if (port) { 9798f03f948SJag Raman port->tty = NULL; 9808f03f948SJag Raman 9818f03f948SJag Raman if (port->removed) { 9828f03f948SJag Raman vcc_table_remove(tty->index); 9838f03f948SJag Raman kfree(port->vio.name); 9848f03f948SJag Raman kfree(port->domain); 9858f03f948SJag Raman kfree(port); 9868f03f948SJag Raman } else { 9878f03f948SJag Raman vcc_put(port, true); 9888f03f948SJag Raman } 9898f03f948SJag Raman } 9908f03f948SJag Raman 9918f03f948SJag Raman tty_port_destroy(tty->port); 9928f03f948SJag Raman kfree(tty->port); 9938f03f948SJag Raman tty->port = NULL; 9948f03f948SJag Raman } 9958f03f948SJag Raman 99663a71744SJag Raman static const struct tty_operations vcc_ops = { 99763a71744SJag Raman .open = vcc_open, 99863a71744SJag Raman .close = vcc_close, 9996a3ff25bSJag Raman .hangup = vcc_hangup, 1000cf045260SJag Raman .write = vcc_write, 1001cf045260SJag Raman .write_room = vcc_write_room, 1002aeee7debSJag Raman .chars_in_buffer = vcc_chars_in_buffer, 1003e942e577SJag Raman .break_ctl = vcc_break_ctl, 10048f03f948SJag Raman .install = vcc_install, 10058f03f948SJag Raman .cleanup = vcc_cleanup, 100663a71744SJag Raman }; 1007ce808b74SJag Raman 1008ce808b74SJag Raman #define VCC_TTY_FLAGS (TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_REAL_RAW) 1009ce808b74SJag Raman 1010ce808b74SJag Raman static int vcc_tty_init(void) 1011ce808b74SJag Raman { 1012ce808b74SJag Raman int rv; 1013ce808b74SJag Raman 1014ce808b74SJag Raman vcc_tty_driver = tty_alloc_driver(VCC_MAX_PORTS, VCC_TTY_FLAGS); 1015c6b4ee9eSDan Carpenter if (IS_ERR(vcc_tty_driver)) { 1016ce808b74SJag Raman pr_err("VCC: TTY driver alloc failed\n"); 1017c6b4ee9eSDan Carpenter return PTR_ERR(vcc_tty_driver); 1018ce808b74SJag Raman } 1019ce808b74SJag Raman 10209af6f74dSJiri Slaby vcc_tty_driver->driver_name = "vcc"; 10219af6f74dSJiri Slaby vcc_tty_driver->name = "vcc"; 1022ce808b74SJag Raman 1023ce808b74SJag Raman vcc_tty_driver->minor_start = VCC_MINOR_START; 1024ce808b74SJag Raman vcc_tty_driver->type = TTY_DRIVER_TYPE_SYSTEM; 1025ce808b74SJag Raman vcc_tty_driver->init_termios = vcc_tty_termios; 1026ce808b74SJag Raman 1027ce808b74SJag Raman tty_set_operations(vcc_tty_driver, &vcc_ops); 1028ce808b74SJag Raman 1029ce808b74SJag Raman rv = tty_register_driver(vcc_tty_driver); 1030ce808b74SJag Raman if (rv) { 1031ce808b74SJag Raman pr_err("VCC: TTY driver registration failed\n"); 10329f90a4ddSJiri Slaby tty_driver_kref_put(vcc_tty_driver); 1033ce808b74SJag Raman vcc_tty_driver = NULL; 1034ce808b74SJag Raman return rv; 1035ce808b74SJag Raman } 1036ce808b74SJag Raman 1037ce808b74SJag Raman vccdbg("VCC: TTY driver registered\n"); 1038ce808b74SJag Raman 1039ce808b74SJag Raman return 0; 1040ce808b74SJag Raman } 1041ce808b74SJag Raman 1042ce808b74SJag Raman static void vcc_tty_exit(void) 1043ce808b74SJag Raman { 1044ce808b74SJag Raman tty_unregister_driver(vcc_tty_driver); 10459f90a4ddSJiri Slaby tty_driver_kref_put(vcc_tty_driver); 1046ce808b74SJag Raman vccdbg("VCC: TTY driver unregistered\n"); 1047ce808b74SJag Raman 1048ce808b74SJag Raman vcc_tty_driver = NULL; 1049ce808b74SJag Raman } 1050ce808b74SJag Raman 105155bd2133SJag Raman static int __init vcc_init(void) 105255bd2133SJag Raman { 1053ce808b74SJag Raman int rv; 1054ce808b74SJag Raman 1055ce808b74SJag Raman rv = vcc_tty_init(); 1056ce808b74SJag Raman if (rv) { 1057ce808b74SJag Raman pr_err("VCC: TTY init failed\n"); 1058ce808b74SJag Raman return rv; 1059ce808b74SJag Raman } 1060ce808b74SJag Raman 10615d171050SJag Raman rv = vio_register_driver(&vcc_driver); 10625d171050SJag Raman if (rv) { 10635d171050SJag Raman pr_err("VCC: VIO driver registration failed\n"); 10645d171050SJag Raman vcc_tty_exit(); 10655d171050SJag Raman } else { 10665d171050SJag Raman vccdbg("VCC: VIO driver registered successfully\n"); 10675d171050SJag Raman } 10685d171050SJag Raman 1069ce808b74SJag Raman return rv; 107055bd2133SJag Raman } 107155bd2133SJag Raman 107255bd2133SJag Raman static void __exit vcc_exit(void) 107355bd2133SJag Raman { 10745d171050SJag Raman vio_unregister_driver(&vcc_driver); 10755d171050SJag Raman vccdbg("VCC: VIO driver unregistered\n"); 1076ce808b74SJag Raman vcc_tty_exit(); 1077ce808b74SJag Raman vccdbg("VCC: TTY driver unregistered\n"); 107855bd2133SJag Raman } 107955bd2133SJag Raman 108055bd2133SJag Raman module_init(vcc_init); 108155bd2133SJag Raman module_exit(vcc_exit); 1082