155bd2133SJag Raman /* vcc.c: sun4v virtual channel concentrator 255bd2133SJag Raman * 355bd2133SJag Raman * Copyright (C) 2017 Oracle. All rights reserved. 455bd2133SJag Raman */ 555bd2133SJag Raman 65d171050SJag Raman #include <linux/delay.h> 75d171050SJag Raman #include <linux/interrupt.h> 855bd2133SJag Raman #include <linux/module.h> 95d171050SJag Raman #include <linux/slab.h> 10*8868e44aSJag Raman #include <linux/sysfs.h> 11ce808b74SJag Raman #include <linux/tty.h> 125d171050SJag Raman #include <asm/vio.h> 135d171050SJag Raman #include <asm/ldc.h> 1455bd2133SJag Raman 15f283ebd5SJag Raman #define DRV_MODULE_NAME "vcc" 16f283ebd5SJag Raman #define DRV_MODULE_VERSION "1.1" 17f283ebd5SJag Raman #define DRV_MODULE_RELDATE "July 1, 2017" 18f283ebd5SJag Raman 19ce808b74SJag Raman static char version[] = 20ce808b74SJag Raman DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")"; 21ce808b74SJag Raman 22f283ebd5SJag Raman MODULE_DESCRIPTION("Sun LDOM virtual console concentrator driver"); 23f283ebd5SJag Raman MODULE_LICENSE("GPL"); 24f283ebd5SJag Raman MODULE_VERSION(DRV_MODULE_VERSION); 25f283ebd5SJag Raman 265d171050SJag Raman struct vcc_port { 275d171050SJag Raman struct vio_driver_state vio; 285d171050SJag Raman 295d171050SJag Raman spinlock_t lock; 305d171050SJag Raman char *domain; 315d171050SJag Raman struct tty_struct *tty; /* only populated while dev is open */ 325d171050SJag Raman unsigned long index; /* index into the vcc_table */ 335d171050SJag Raman 345d171050SJag Raman u64 refcnt; 355d171050SJag Raman bool excl_locked; 365d171050SJag Raman 375d171050SJag Raman bool removed; 385d171050SJag Raman 395d171050SJag Raman /* This buffer is required to support the tty write_room interface 405d171050SJag Raman * and guarantee that any characters that the driver accepts will 415d171050SJag Raman * be eventually sent, either immediately or later. 425d171050SJag Raman */ 435d171050SJag Raman int chars_in_buffer; 445d171050SJag Raman struct vio_vcc buffer; 455d171050SJag Raman 465d171050SJag Raman struct timer_list rx_timer; 475d171050SJag Raman struct timer_list tx_timer; 485d171050SJag Raman }; 495d171050SJag Raman 505d171050SJag Raman /* Microseconds that thread will delay waiting for a vcc port ref */ 515d171050SJag Raman #define VCC_REF_DELAY 100 525d171050SJag Raman 53ce808b74SJag Raman #define VCC_MAX_PORTS 1024 54ce808b74SJag Raman #define VCC_MINOR_START 0 /* must be zero */ 55ce808b74SJag Raman 56*8868e44aSJag Raman #define VCC_CTL_BREAK -1 57*8868e44aSJag Raman #define VCC_CTL_HUP -2 58*8868e44aSJag Raman 59ce808b74SJag Raman static const char vcc_driver_name[] = "vcc"; 60ce808b74SJag Raman static const char vcc_device_node[] = "vcc"; 61ce808b74SJag Raman static struct tty_driver *vcc_tty_driver; 62ce808b74SJag Raman 635d171050SJag Raman static struct vcc_port *vcc_table[VCC_MAX_PORTS]; 645d171050SJag Raman static DEFINE_SPINLOCK(vcc_table_lock); 655d171050SJag Raman 66f283ebd5SJag Raman int vcc_dbg; 67f283ebd5SJag Raman int vcc_dbg_ldc; 68f283ebd5SJag Raman int vcc_dbg_vio; 69f283ebd5SJag Raman 70f283ebd5SJag Raman module_param(vcc_dbg, uint, 0664); 71f283ebd5SJag Raman module_param(vcc_dbg_ldc, uint, 0664); 72f283ebd5SJag Raman module_param(vcc_dbg_vio, uint, 0664); 73f283ebd5SJag Raman 74f283ebd5SJag Raman #define VCC_DBG_DRV 0x1 75f283ebd5SJag Raman #define VCC_DBG_LDC 0x2 76f283ebd5SJag Raman #define VCC_DBG_PKT 0x4 77f283ebd5SJag Raman 78f283ebd5SJag Raman #define vccdbg(f, a...) \ 79f283ebd5SJag Raman do { \ 80f283ebd5SJag Raman if (vcc_dbg & VCC_DBG_DRV) \ 81f283ebd5SJag Raman pr_info(f, ## a); \ 82f283ebd5SJag Raman } while (0) \ 83f283ebd5SJag Raman 84f283ebd5SJag Raman #define vccdbgl(l) \ 85f283ebd5SJag Raman do { \ 86f283ebd5SJag Raman if (vcc_dbg & VCC_DBG_LDC) \ 87f283ebd5SJag Raman ldc_print(l); \ 88f283ebd5SJag Raman } while (0) \ 89f283ebd5SJag Raman 90f283ebd5SJag Raman #define vccdbgp(pkt) \ 91f283ebd5SJag Raman do { \ 92f283ebd5SJag Raman if (vcc_dbg & VCC_DBG_PKT) { \ 93f283ebd5SJag Raman int i; \ 94f283ebd5SJag Raman for (i = 0; i < pkt.tag.stype; i++) \ 95f283ebd5SJag Raman pr_info("[%c]", pkt.data[i]); \ 96f283ebd5SJag Raman } \ 97f283ebd5SJag Raman } while (0) \ 98f283ebd5SJag Raman 99ce808b74SJag Raman /* Note: Be careful when adding flags to this line discipline. Don't 100ce808b74SJag Raman * add anything that will cause echoing or we'll go into recursive 101ce808b74SJag Raman * loop echoing chars back and forth with the console drivers. 102ce808b74SJag Raman */ 103ce808b74SJag Raman static struct ktermios vcc_tty_termios = { 104ce808b74SJag Raman .c_iflag = IGNBRK | IGNPAR, 105ce808b74SJag Raman .c_oflag = OPOST, 106ce808b74SJag Raman .c_cflag = B38400 | CS8 | CREAD | HUPCL, 107ce808b74SJag Raman .c_cc = INIT_C_CC, 108ce808b74SJag Raman .c_ispeed = 38400, 109ce808b74SJag Raman .c_ospeed = 38400 110ce808b74SJag Raman }; 111ce808b74SJag Raman 1125d171050SJag Raman /** 1135d171050SJag Raman * vcc_table_add() - Add VCC port to the VCC table 1145d171050SJag Raman * @port: pointer to the VCC port 1155d171050SJag Raman * 1165d171050SJag Raman * Return: index of the port in the VCC table on success, 1175d171050SJag Raman * -1 on failure 1185d171050SJag Raman */ 1195d171050SJag Raman static int vcc_table_add(struct vcc_port *port) 1205d171050SJag Raman { 1215d171050SJag Raman unsigned long flags; 1225d171050SJag Raman int i; 1235d171050SJag Raman 1245d171050SJag Raman spin_lock_irqsave(&vcc_table_lock, flags); 1255d171050SJag Raman for (i = VCC_MINOR_START; i < VCC_MAX_PORTS; i++) { 1265d171050SJag Raman if (!vcc_table[i]) { 1275d171050SJag Raman vcc_table[i] = port; 1285d171050SJag Raman break; 1295d171050SJag Raman } 1305d171050SJag Raman } 1315d171050SJag Raman spin_unlock_irqrestore(&vcc_table_lock, flags); 1325d171050SJag Raman 1335d171050SJag Raman if (i < VCC_MAX_PORTS) 1345d171050SJag Raman return i; 1355d171050SJag Raman else 1365d171050SJag Raman return -1; 1375d171050SJag Raman } 1385d171050SJag Raman 1395d171050SJag Raman /** 1405d171050SJag Raman * vcc_table_remove() - Removes a VCC port from the VCC table 1415d171050SJag Raman * @index: Index into the VCC table 1425d171050SJag Raman */ 1435d171050SJag Raman static void vcc_table_remove(unsigned long index) 1445d171050SJag Raman { 1455d171050SJag Raman unsigned long flags; 1465d171050SJag Raman 1475d171050SJag Raman if (WARN_ON(index >= VCC_MAX_PORTS)) 1485d171050SJag Raman return; 1495d171050SJag Raman 1505d171050SJag Raman spin_lock_irqsave(&vcc_table_lock, flags); 1515d171050SJag Raman vcc_table[index] = NULL; 1525d171050SJag Raman spin_unlock_irqrestore(&vcc_table_lock, flags); 1535d171050SJag Raman } 1545d171050SJag Raman 1555d171050SJag Raman /** 1565d171050SJag Raman * vcc_get() - Gets a reference to VCC port 1575d171050SJag Raman * @index: Index into the VCC table 1585d171050SJag Raman * @excl: Indicates if an exclusive access is requested 1595d171050SJag Raman * 1605d171050SJag Raman * Return: reference to the VCC port, if found 1615d171050SJag Raman * NULL, if port not found 1625d171050SJag Raman */ 1635d171050SJag Raman static struct vcc_port *vcc_get(unsigned long index, bool excl) 1645d171050SJag Raman { 1655d171050SJag Raman struct vcc_port *port; 1665d171050SJag Raman unsigned long flags; 1675d171050SJag Raman 1685d171050SJag Raman try_again: 1695d171050SJag Raman spin_lock_irqsave(&vcc_table_lock, flags); 1705d171050SJag Raman 1715d171050SJag Raman port = vcc_table[index]; 1725d171050SJag Raman if (!port) { 1735d171050SJag Raman spin_unlock_irqrestore(&vcc_table_lock, flags); 1745d171050SJag Raman return NULL; 1755d171050SJag Raman } 1765d171050SJag Raman 1775d171050SJag Raman if (!excl) { 1785d171050SJag Raman if (port->excl_locked) { 1795d171050SJag Raman spin_unlock_irqrestore(&vcc_table_lock, flags); 1805d171050SJag Raman udelay(VCC_REF_DELAY); 1815d171050SJag Raman goto try_again; 1825d171050SJag Raman } 1835d171050SJag Raman port->refcnt++; 1845d171050SJag Raman spin_unlock_irqrestore(&vcc_table_lock, flags); 1855d171050SJag Raman return port; 1865d171050SJag Raman } 1875d171050SJag Raman 1885d171050SJag Raman if (port->refcnt) { 1895d171050SJag Raman spin_unlock_irqrestore(&vcc_table_lock, flags); 1905d171050SJag Raman /* Threads wanting exclusive access will wait half the time, 1915d171050SJag Raman * probably giving them higher priority in the case of 1925d171050SJag Raman * multiple waiters. 1935d171050SJag Raman */ 1945d171050SJag Raman udelay(VCC_REF_DELAY/2); 1955d171050SJag Raman goto try_again; 1965d171050SJag Raman } 1975d171050SJag Raman 1985d171050SJag Raman port->refcnt++; 1995d171050SJag Raman port->excl_locked = true; 2005d171050SJag Raman spin_unlock_irqrestore(&vcc_table_lock, flags); 2015d171050SJag Raman 2025d171050SJag Raman return port; 2035d171050SJag Raman } 2045d171050SJag Raman 2055d171050SJag Raman /** 2065d171050SJag Raman * vcc_put() - Returns a reference to VCC port 2075d171050SJag Raman * @port: pointer to VCC port 2085d171050SJag Raman * @excl: Indicates if the returned reference is an exclusive reference 2095d171050SJag Raman * 2105d171050SJag Raman * Note: It's the caller's responsibility to ensure the correct value 2115d171050SJag Raman * for the excl flag 2125d171050SJag Raman */ 2135d171050SJag Raman static void vcc_put(struct vcc_port *port, bool excl) 2145d171050SJag Raman { 2155d171050SJag Raman unsigned long flags; 2165d171050SJag Raman 2175d171050SJag Raman if (!port) 2185d171050SJag Raman return; 2195d171050SJag Raman 2205d171050SJag Raman spin_lock_irqsave(&vcc_table_lock, flags); 2215d171050SJag Raman 2225d171050SJag Raman /* check if caller attempted to put with the wrong flags */ 2235d171050SJag Raman if (WARN_ON((excl && !port->excl_locked) || 2245d171050SJag Raman (!excl && port->excl_locked))) 2255d171050SJag Raman goto done; 2265d171050SJag Raman 2275d171050SJag Raman port->refcnt--; 2285d171050SJag Raman 2295d171050SJag Raman if (excl) 2305d171050SJag Raman port->excl_locked = false; 2315d171050SJag Raman 2325d171050SJag Raman done: 2335d171050SJag Raman spin_unlock_irqrestore(&vcc_table_lock, flags); 2345d171050SJag Raman } 2355d171050SJag Raman 2365d171050SJag Raman /** 2375d171050SJag Raman * vcc_get_ne() - Get a non-exclusive reference to VCC port 2385d171050SJag Raman * @index: Index into the VCC table 2395d171050SJag Raman * 2405d171050SJag Raman * Gets a non-exclusive reference to VCC port, if it's not removed 2415d171050SJag Raman * 2425d171050SJag Raman * Return: pointer to the VCC port, if found 2435d171050SJag Raman * NULL, if port not found 2445d171050SJag Raman */ 2455d171050SJag Raman static struct vcc_port *vcc_get_ne(unsigned long index) 2465d171050SJag Raman { 2475d171050SJag Raman struct vcc_port *port; 2485d171050SJag Raman 2495d171050SJag Raman port = vcc_get(index, false); 2505d171050SJag Raman 2515d171050SJag Raman if (port && port->removed) { 2525d171050SJag Raman vcc_put(port, false); 2535d171050SJag Raman return NULL; 2545d171050SJag Raman } 2555d171050SJag Raman 2565d171050SJag Raman return port; 2575d171050SJag Raman } 2585d171050SJag Raman 2595d171050SJag Raman static void vcc_event(void *arg, int event) 2605d171050SJag Raman { 2615d171050SJag Raman } 2625d171050SJag Raman 2635d171050SJag Raman static struct ldc_channel_config vcc_ldc_cfg = { 2645d171050SJag Raman .event = vcc_event, 2655d171050SJag Raman .mtu = VIO_VCC_MTU_SIZE, 2665d171050SJag Raman .mode = LDC_MODE_RAW, 2675d171050SJag Raman .debug = 0, 2685d171050SJag Raman }; 2695d171050SJag Raman 2705d171050SJag Raman /* Ordered from largest major to lowest */ 2715d171050SJag Raman static struct vio_version vcc_versions[] = { 2725d171050SJag Raman { .major = 1, .minor = 0 }, 2735d171050SJag Raman }; 2745d171050SJag Raman 275*8868e44aSJag Raman static ssize_t vcc_sysfs_domain_show(struct device *dev, 276*8868e44aSJag Raman struct device_attribute *attr, 277*8868e44aSJag Raman char *buf) 278*8868e44aSJag Raman { 279*8868e44aSJag Raman struct vcc_port *port; 280*8868e44aSJag Raman int rv; 281*8868e44aSJag Raman 282*8868e44aSJag Raman port = dev_get_drvdata(dev); 283*8868e44aSJag Raman if (!port) 284*8868e44aSJag Raman return -ENODEV; 285*8868e44aSJag Raman 286*8868e44aSJag Raman rv = scnprintf(buf, PAGE_SIZE, "%s\n", port->domain); 287*8868e44aSJag Raman 288*8868e44aSJag Raman return rv; 289*8868e44aSJag Raman } 290*8868e44aSJag Raman 291*8868e44aSJag Raman static int vcc_send_ctl(struct vcc_port *port, int ctl) 292*8868e44aSJag Raman { 293*8868e44aSJag Raman struct vio_vcc pkt; 294*8868e44aSJag Raman int rv; 295*8868e44aSJag Raman 296*8868e44aSJag Raman pkt.tag.type = VIO_TYPE_CTRL; 297*8868e44aSJag Raman pkt.tag.sid = ctl; 298*8868e44aSJag Raman pkt.tag.stype = 0; 299*8868e44aSJag Raman 300*8868e44aSJag Raman rv = ldc_write(port->vio.lp, &pkt, sizeof(pkt.tag)); 301*8868e44aSJag Raman WARN_ON(!rv); 302*8868e44aSJag Raman vccdbg("VCC: ldc_write(%ld)=%d\n", sizeof(pkt.tag), rv); 303*8868e44aSJag Raman 304*8868e44aSJag Raman return rv; 305*8868e44aSJag Raman } 306*8868e44aSJag Raman 307*8868e44aSJag Raman static ssize_t vcc_sysfs_break_store(struct device *dev, 308*8868e44aSJag Raman struct device_attribute *attr, 309*8868e44aSJag Raman const char *buf, size_t count) 310*8868e44aSJag Raman { 311*8868e44aSJag Raman struct vcc_port *port; 312*8868e44aSJag Raman unsigned long flags; 313*8868e44aSJag Raman int rv = count; 314*8868e44aSJag Raman int brk; 315*8868e44aSJag Raman 316*8868e44aSJag Raman port = dev_get_drvdata(dev); 317*8868e44aSJag Raman if (!port) 318*8868e44aSJag Raman return -ENODEV; 319*8868e44aSJag Raman 320*8868e44aSJag Raman spin_lock_irqsave(&port->lock, flags); 321*8868e44aSJag Raman 322*8868e44aSJag Raman if (sscanf(buf, "%ud", &brk) != 1 || brk != 1) 323*8868e44aSJag Raman rv = -EINVAL; 324*8868e44aSJag Raman else if (vcc_send_ctl(port, VCC_CTL_BREAK) < 0) 325*8868e44aSJag Raman pr_err("VCC: unable to send CTL_BREAK\n"); 326*8868e44aSJag Raman 327*8868e44aSJag Raman spin_unlock_irqrestore(&port->lock, flags); 328*8868e44aSJag Raman 329*8868e44aSJag Raman return rv; 330*8868e44aSJag Raman } 331*8868e44aSJag Raman 332*8868e44aSJag Raman static DEVICE_ATTR(domain, 0400, vcc_sysfs_domain_show, NULL); 333*8868e44aSJag Raman static DEVICE_ATTR(break, 0200, NULL, vcc_sysfs_break_store); 334*8868e44aSJag Raman 335*8868e44aSJag Raman static struct attribute *vcc_sysfs_entries[] = { 336*8868e44aSJag Raman &dev_attr_domain.attr, 337*8868e44aSJag Raman &dev_attr_break.attr, 338*8868e44aSJag Raman NULL 339*8868e44aSJag Raman }; 340*8868e44aSJag Raman 341*8868e44aSJag Raman static struct attribute_group vcc_attribute_group = { 342*8868e44aSJag Raman .name = NULL, 343*8868e44aSJag Raman .attrs = vcc_sysfs_entries, 344*8868e44aSJag Raman }; 345*8868e44aSJag Raman 3465d171050SJag Raman /** 3475d171050SJag Raman * vcc_probe() - Initialize VCC port 3485d171050SJag Raman * @vdev: Pointer to VIO device of the new VCC port 3495d171050SJag Raman * @id: VIO device ID 3505d171050SJag Raman * 3515d171050SJag Raman * Initializes a VCC port to receive serial console data from 3525d171050SJag Raman * the guest domain. Sets up a TTY end point on the control 3535d171050SJag Raman * domain. Sets up VIO/LDC link between the guest & control 3545d171050SJag Raman * domain endpoints. 3555d171050SJag Raman * 3565d171050SJag Raman * Return: status of the probe 3575d171050SJag Raman */ 3585d171050SJag Raman static int vcc_probe(struct vio_dev *vdev, const struct vio_device_id *id) 3595d171050SJag Raman { 3605d171050SJag Raman struct mdesc_handle *hp; 3615d171050SJag Raman struct vcc_port *port; 3625d171050SJag Raman struct device *dev; 3635d171050SJag Raman const char *domain; 3645d171050SJag Raman char *name; 3655d171050SJag Raman u64 node; 3665d171050SJag Raman int rv; 3675d171050SJag Raman 3685d171050SJag Raman vccdbg("VCC: name=%s\n", dev_name(&vdev->dev)); 3695d171050SJag Raman 3705d171050SJag Raman if (!vcc_tty_driver) { 3715d171050SJag Raman pr_err("VCC: TTY driver not registered\n"); 3725d171050SJag Raman return -ENODEV; 3735d171050SJag Raman } 3745d171050SJag Raman 3755d171050SJag Raman port = kzalloc(sizeof(struct vcc_port), GFP_KERNEL); 3765d171050SJag Raman if (!port) 3775d171050SJag Raman return -ENOMEM; 3785d171050SJag Raman 3795d171050SJag Raman name = kstrdup(dev_name(&vdev->dev), GFP_KERNEL); 3805d171050SJag Raman 3815d171050SJag Raman rv = vio_driver_init(&port->vio, vdev, VDEV_CONSOLE_CON, vcc_versions, 3825d171050SJag Raman ARRAY_SIZE(vcc_versions), NULL, name); 3835d171050SJag Raman if (rv) 3845d171050SJag Raman goto free_port; 3855d171050SJag Raman 3865d171050SJag Raman port->vio.debug = vcc_dbg_vio; 3875d171050SJag Raman vcc_ldc_cfg.debug = vcc_dbg_ldc; 3885d171050SJag Raman 3895d171050SJag Raman rv = vio_ldc_alloc(&port->vio, &vcc_ldc_cfg, port); 3905d171050SJag Raman if (rv) 3915d171050SJag Raman goto free_port; 3925d171050SJag Raman 3935d171050SJag Raman spin_lock_init(&port->lock); 3945d171050SJag Raman 3955d171050SJag Raman port->index = vcc_table_add(port); 3965d171050SJag Raman if (port->index == -1) { 3975d171050SJag Raman pr_err("VCC: no more TTY indices left for allocation\n"); 3985d171050SJag Raman goto free_ldc; 3995d171050SJag Raman } 4005d171050SJag Raman 4015d171050SJag Raman /* Register the device using VCC table index as TTY index */ 4025d171050SJag Raman dev = tty_register_device(vcc_tty_driver, port->index, &vdev->dev); 4035d171050SJag Raman if (IS_ERR(dev)) { 4045d171050SJag Raman rv = PTR_ERR(dev); 4055d171050SJag Raman goto free_table; 4065d171050SJag Raman } 4075d171050SJag Raman 4085d171050SJag Raman hp = mdesc_grab(); 4095d171050SJag Raman 4105d171050SJag Raman node = vio_vdev_node(hp, vdev); 4115d171050SJag Raman if (node == MDESC_NODE_NULL) { 4125d171050SJag Raman rv = -ENXIO; 4135d171050SJag Raman mdesc_release(hp); 4145d171050SJag Raman goto unreg_tty; 4155d171050SJag Raman } 4165d171050SJag Raman 4175d171050SJag Raman domain = mdesc_get_property(hp, node, "vcc-domain-name", NULL); 4185d171050SJag Raman if (!domain) { 4195d171050SJag Raman rv = -ENXIO; 4205d171050SJag Raman mdesc_release(hp); 4215d171050SJag Raman goto unreg_tty; 4225d171050SJag Raman } 4235d171050SJag Raman port->domain = kstrdup(domain, GFP_KERNEL); 4245d171050SJag Raman 4255d171050SJag Raman mdesc_release(hp); 4265d171050SJag Raman 427*8868e44aSJag Raman rv = sysfs_create_group(&vdev->dev.kobj, &vcc_attribute_group); 428*8868e44aSJag Raman if (rv) 429*8868e44aSJag Raman goto free_domain; 430*8868e44aSJag Raman 4315d171050SJag Raman dev_set_drvdata(&vdev->dev, port); 4325d171050SJag Raman 4335d171050SJag Raman /* It's possible to receive IRQs in the middle of vio_port_up. Disable 4345d171050SJag Raman * IRQs until the port is up. 4355d171050SJag Raman */ 4365d171050SJag Raman disable_irq_nosync(vdev->rx_irq); 4375d171050SJag Raman vio_port_up(&port->vio); 4385d171050SJag Raman enable_irq(vdev->rx_irq); 4395d171050SJag Raman 4405d171050SJag Raman return 0; 4415d171050SJag Raman 442*8868e44aSJag Raman free_domain: 443*8868e44aSJag Raman kfree(port->domain); 4445d171050SJag Raman unreg_tty: 4455d171050SJag Raman tty_unregister_device(vcc_tty_driver, port->index); 4465d171050SJag Raman free_table: 4475d171050SJag Raman vcc_table_remove(port->index); 4485d171050SJag Raman free_ldc: 4495d171050SJag Raman vio_ldc_free(&port->vio); 4505d171050SJag Raman free_port: 4515d171050SJag Raman kfree(name); 4525d171050SJag Raman kfree(port); 4535d171050SJag Raman 4545d171050SJag Raman return rv; 4555d171050SJag Raman } 4565d171050SJag Raman 4575d171050SJag Raman /** 4585d171050SJag Raman * vcc_remove() - Terminate a VCC port 4595d171050SJag Raman * @vdev: Pointer to VIO device of the VCC port 4605d171050SJag Raman * 4615d171050SJag Raman * Terminates a VCC port. Sets up the teardown of TTY and 4625d171050SJag Raman * VIO/LDC link between guest and primary domains. 4635d171050SJag Raman * 4645d171050SJag Raman * Return: status of removal 4655d171050SJag Raman */ 4665d171050SJag Raman static int vcc_remove(struct vio_dev *vdev) 4675d171050SJag Raman { 4685d171050SJag Raman struct vcc_port *port = dev_get_drvdata(&vdev->dev); 4695d171050SJag Raman 4705d171050SJag Raman if (!port) 4715d171050SJag Raman return -ENODEV; 4725d171050SJag Raman 4735d171050SJag Raman /* If there's a process with the device open, do a synchronous 4745d171050SJag Raman * hangup of the TTY. This *may* cause the process to call close 4755d171050SJag Raman * asynchronously, but it's not guaranteed. 4765d171050SJag Raman */ 4775d171050SJag Raman if (port->tty) 4785d171050SJag Raman tty_vhangup(port->tty); 4795d171050SJag Raman 4805d171050SJag Raman /* Get exclusive reference to VCC, ensures that there are no other 4815d171050SJag Raman * clients to this port 4825d171050SJag Raman */ 4835d171050SJag Raman port = vcc_get(port->index, true); 4845d171050SJag Raman 4855d171050SJag Raman if (WARN_ON(!port)) 4865d171050SJag Raman return -ENODEV; 4875d171050SJag Raman 4885d171050SJag Raman tty_unregister_device(vcc_tty_driver, port->index); 4895d171050SJag Raman 4905d171050SJag Raman del_timer_sync(&port->vio.timer); 4915d171050SJag Raman vio_ldc_free(&port->vio); 492*8868e44aSJag Raman sysfs_remove_group(&vdev->dev.kobj, &vcc_attribute_group); 4935d171050SJag Raman dev_set_drvdata(&vdev->dev, NULL); 4945d171050SJag Raman 4955d171050SJag Raman if (port->tty) { 4965d171050SJag Raman port->removed = true; 4975d171050SJag Raman vcc_put(port, true); 4985d171050SJag Raman } else { 4995d171050SJag Raman vcc_table_remove(port->index); 5005d171050SJag Raman 5015d171050SJag Raman kfree(port->vio.name); 5025d171050SJag Raman kfree(port->domain); 5035d171050SJag Raman kfree(port); 5045d171050SJag Raman } 5055d171050SJag Raman 5065d171050SJag Raman return 0; 5075d171050SJag Raman } 5085d171050SJag Raman 5095d171050SJag Raman static const struct vio_device_id vcc_match[] = { 5105d171050SJag Raman { 5115d171050SJag Raman .type = "vcc-port", 5125d171050SJag Raman }, 5135d171050SJag Raman {}, 5145d171050SJag Raman }; 5155d171050SJag Raman MODULE_DEVICE_TABLE(vio, vcc_match); 5165d171050SJag Raman 5175d171050SJag Raman static struct vio_driver vcc_driver = { 5185d171050SJag Raman .id_table = vcc_match, 5195d171050SJag Raman .probe = vcc_probe, 5205d171050SJag Raman .remove = vcc_remove, 5215d171050SJag Raman .name = "vcc", 5225d171050SJag Raman }; 5235d171050SJag Raman 524ce808b74SJag Raman static const struct tty_operations vcc_ops; 525ce808b74SJag Raman 526ce808b74SJag Raman #define VCC_TTY_FLAGS (TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_REAL_RAW) 527ce808b74SJag Raman 528ce808b74SJag Raman static int vcc_tty_init(void) 529ce808b74SJag Raman { 530ce808b74SJag Raman int rv; 531ce808b74SJag Raman 532ce808b74SJag Raman pr_info("VCC: %s\n", version); 533ce808b74SJag Raman 534ce808b74SJag Raman vcc_tty_driver = tty_alloc_driver(VCC_MAX_PORTS, VCC_TTY_FLAGS); 535ce808b74SJag Raman if (!vcc_tty_driver) { 536ce808b74SJag Raman pr_err("VCC: TTY driver alloc failed\n"); 537ce808b74SJag Raman return -ENOMEM; 538ce808b74SJag Raman } 539ce808b74SJag Raman 540ce808b74SJag Raman vcc_tty_driver->driver_name = vcc_driver_name; 541ce808b74SJag Raman vcc_tty_driver->name = vcc_device_node; 542ce808b74SJag Raman 543ce808b74SJag Raman vcc_tty_driver->minor_start = VCC_MINOR_START; 544ce808b74SJag Raman vcc_tty_driver->type = TTY_DRIVER_TYPE_SYSTEM; 545ce808b74SJag Raman vcc_tty_driver->init_termios = vcc_tty_termios; 546ce808b74SJag Raman 547ce808b74SJag Raman tty_set_operations(vcc_tty_driver, &vcc_ops); 548ce808b74SJag Raman 549ce808b74SJag Raman rv = tty_register_driver(vcc_tty_driver); 550ce808b74SJag Raman if (rv) { 551ce808b74SJag Raman pr_err("VCC: TTY driver registration failed\n"); 552ce808b74SJag Raman put_tty_driver(vcc_tty_driver); 553ce808b74SJag Raman vcc_tty_driver = NULL; 554ce808b74SJag Raman return rv; 555ce808b74SJag Raman } 556ce808b74SJag Raman 557ce808b74SJag Raman vccdbg("VCC: TTY driver registered\n"); 558ce808b74SJag Raman 559ce808b74SJag Raman return 0; 560ce808b74SJag Raman } 561ce808b74SJag Raman 562ce808b74SJag Raman static void vcc_tty_exit(void) 563ce808b74SJag Raman { 564ce808b74SJag Raman tty_unregister_driver(vcc_tty_driver); 565ce808b74SJag Raman put_tty_driver(vcc_tty_driver); 566ce808b74SJag Raman vccdbg("VCC: TTY driver unregistered\n"); 567ce808b74SJag Raman 568ce808b74SJag Raman vcc_tty_driver = NULL; 569ce808b74SJag Raman } 570ce808b74SJag Raman 57155bd2133SJag Raman static int __init vcc_init(void) 57255bd2133SJag Raman { 573ce808b74SJag Raman int rv; 574ce808b74SJag Raman 575ce808b74SJag Raman rv = vcc_tty_init(); 576ce808b74SJag Raman if (rv) { 577ce808b74SJag Raman pr_err("VCC: TTY init failed\n"); 578ce808b74SJag Raman return rv; 579ce808b74SJag Raman } 580ce808b74SJag Raman 5815d171050SJag Raman rv = vio_register_driver(&vcc_driver); 5825d171050SJag Raman if (rv) { 5835d171050SJag Raman pr_err("VCC: VIO driver registration failed\n"); 5845d171050SJag Raman vcc_tty_exit(); 5855d171050SJag Raman } else { 5865d171050SJag Raman vccdbg("VCC: VIO driver registered successfully\n"); 5875d171050SJag Raman } 5885d171050SJag Raman 589ce808b74SJag Raman return rv; 59055bd2133SJag Raman } 59155bd2133SJag Raman 59255bd2133SJag Raman static void __exit vcc_exit(void) 59355bd2133SJag Raman { 5945d171050SJag Raman vio_unregister_driver(&vcc_driver); 5955d171050SJag Raman vccdbg("VCC: VIO driver unregistered\n"); 596ce808b74SJag Raman vcc_tty_exit(); 597ce808b74SJag Raman vccdbg("VCC: TTY driver unregistered\n"); 59855bd2133SJag Raman } 59955bd2133SJag Raman 60055bd2133SJag Raman module_init(vcc_init); 60155bd2133SJag Raman module_exit(vcc_exit); 602