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