// SPDX-License-Identifier: GPL-2.0-only/* * Mediated virtual PCI serial host device driver * * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. * Author: Neo Jia <cjia@nvidia.com> * Kirti Wankhede <kwankhede@nvidia.com> * * Sample driver that creates mdev device that simulates serial port over PCI * card. */ #include <linux/init.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/poll.h>#include <linux/slab.h>#include <linux/cdev.h>#include <linux/sched.h>#include <linux/wait.h>#include <linux/vfio.h>#include <linux/iommu.h>#include <linux/sysfs.h>#include <linux/ctype.h>#include <linux/file.h>#include <linux/mdev.h>#include <linux/pci.h>#include <linux/serial.h>#include <uapi/linux/serial_reg.h>#include <linux/eventfd.h>#include <linux/anon_inodes.h> /* * #defines */ #define VERSION_STRING "0.1"#define DRIVER_AUTHOR "NVIDIA Corporation" #define MTTY_CLASS_NAME "mtty" #define MTTY_NAME "mtty" #define MTTY_STRING_LEN 16 #define MTTY_CONFIG_SPACE_SIZE 0xff#define MTTY_IO_BAR_SIZE 0x8#define MTTY_MMIO_BAR_SIZE 0x100000 #define STORE_LE16(addr, val) (*(u16 *)addr = val)#define STORE_LE32(addr, val) (*(u32 *)addr = val) #define MAX_FIFO_SIZE 16 #define CIRCULAR_BUF_INC_IDX(idx) (idx = (idx + 1) & (MAX_FIFO_SIZE - 1)) #define MTTY_VFIO_PCI_OFFSET_SHIFT 40 #define MTTY_VFIO_PCI_OFFSET_TO_INDEX(off) (off >> MTTY_VFIO_PCI_OFFSET_SHIFT)#define MTTY_VFIO_PCI_INDEX_TO_OFFSET(index) \ ((u64)(index) << MTTY_VFIO_PCI_OFFSET_SHIFT)#define MTTY_VFIO_PCI_OFFSET_MASK \ (((u64)(1) << MTTY_VFIO_PCI_OFFSET_SHIFT) - 1)#define MAX_MTTYS 24 /* * Global Structures */ static const struct class mtty_class = { .name = MTTY_CLASS_NAME}; static struct mtty_dev { dev_t vd_devt; struct cdev vd_cdev; struct idr vd_idr; struct device dev; struct mdev_parent parent;} mtty_dev; struct mdev_region_info { u64 start; u64 phys_start; u32 size; u64 vfio_offset;}; #if defined(DEBUG_REGS)static const char *wr_reg[] = { "TX", "IER", "FCR", "LCR", "MCR", "LSR", "MSR", "SCR"}; static const char *rd_reg[] = { "RX", "IER", "IIR", "LCR", "MCR", "LSR", "MSR", "SCR"};#endif /* loop back buffer */struct rxtx { u8 fifo[MAX_FIFO_SIZE]; u8 head, tail; u8 count;}; struct serial_port { u8 uart_reg[8]; /* 8 registers */ struct rxtx rxtx; /* loop back buffer */ bool dlab; bool overrun; u16 divisor; u8 fcr; /* FIFO control register */ u8 max_fifo_size; u8 intr_trigger_level; /* interrupt trigger level */}; struct mtty_data { u64 magic;#define MTTY_MAGIC 0x7e9d09898c3e2c4e /* Nothing clever, just random */ u32 major_ver;#define MTTY_MAJOR_VER 1 u32 minor_ver;#define MTTY_MINOR_VER 0 u32 nr_ports; u32 flags; struct serial_port ports[2];}; struct mdev_state; struct mtty_migration_file { struct file *filp; struct mutex lock; struct mdev_state *mdev_state; struct mtty_data data; ssize_t filled_size; u8 disabled:1;}; /* State of each mdev device */struct mdev_state { struct vfio_device vdev; struct eventfd_ctx *intx_evtfd; struct eventfd_ctx *msi_evtfd; int irq_index; u8 *vconfig; struct mutex ops_lock; struct mdev_device *mdev; struct mdev_region_info region_info[VFIO_PCI_NUM_REGIONS]; u32 bar_mask[VFIO_PCI_NUM_REGIONS]; struct list_head next; struct serial_port s[2]; struct mutex rxtx_lock; struct vfio_device_info dev_info; int nr_ports; enum vfio_device_mig_state state; struct mutex state_mutex; struct mutex reset_mutex; struct mtty_migration_file *saving_migf; struct mtty_migration_file *resuming_migf; u8 deferred_reset:1; u8 intx_mask:1;}; static struct mtty_type { struct mdev_type type; int nr_ports;} mtty_types[2] = { { .nr_ports = 1, .type.sysfs_name = "1", .type.pretty_name = "Single port serial" }, { .nr_ports = 2, .type.sysfs_name = "2", .type.pretty_name = "Dual port serial" },}; static struct mdev_type *mtty_mdev_types[] = { &mtty_types[0].type, &mtty_types[1].type,}; static atomic_t mdev_avail_ports = ATOMIC_INIT(MAX_MTTYS); static const struct file_operations vd_fops = { .owner = THIS_MODULE,}; static const struct vfio_device_ops mtty_dev_ops; /* Helper functions */ static void dump_buffer(u8 *buf, uint32_t count){#if defined(DEBUG) int i; pr_info("Buffer:\n"); for (i = 0; i < count; i++) { pr_info("%2x ", *(buf + i)); if ((i + 1) % 16 == 0) pr_info("\n"); }#endif} static bool is_intx(struct mdev_state *mdev_state){ return mdev_state->irq_index == VFIO_PCI_INTX_IRQ_INDEX;} static bool is_msi(struct mdev_state *mdev_state){ return mdev_state->irq_index == VFIO_PCI_MSI_IRQ_INDEX;} static bool is_noirq(struct mdev_state *mdev_state){ return !is_intx(mdev_state) && !is_msi(mdev_state);} static void mtty_trigger_interrupt(struct mdev_state *mdev_state){ lockdep_assert_held(&mdev_state->ops_lock); if (is_msi(mdev_state)) { if (mdev_state->msi_evtfd) eventfd_signal(mdev_state->msi_evtfd); } else if (is_intx(mdev_state)) { if (mdev_state->intx_evtfd && !mdev_state->intx_mask) { eventfd_signal(mdev_state->intx_evtfd); mdev_state->intx_mask = true; } }} static void mtty_create_config_space(struct mdev_state *mdev_state){ /* PCI dev ID */ STORE_LE32((u32 *) &mdev_state->vconfig[0x0], 0x32534348); /* Control: I/O+, Mem-, BusMaster- */ STORE_LE16((u16 *) &mdev_state->vconfig[0x4], 0x0001); /* Status: capabilities list absent */ STORE_LE16((u16 *) &mdev_state->vconfig[0x6], 0x0200); /* Rev ID */ mdev_state->vconfig[0x8] = 0x10; /* programming interface class : 16550-compatible serial controller */ mdev_state->vconfig[0x9] = 0x02; /* Sub class : 00 */ mdev_state->vconfig[0xa] = 0x00; /* Base class : Simple Communication controllers */ mdev_state->vconfig[0xb] = 0x07; /* base address registers */ /* BAR0: IO space */ STORE_LE32((u32 *) &mdev_state->vconfig[0x10], 0x000001); mdev_state->bar_mask[0] = ~(MTTY_IO_BAR_SIZE) + 1; if (mdev_state->nr_ports == 2) { /* BAR1: IO space */ STORE_LE32((u32 *) &mdev_state->vconfig[0x14], 0x000001); mdev_state->bar_mask[1] = ~(MTTY_IO_BAR_SIZE) + 1; } /* Subsystem ID */ STORE_LE32((u32 *) &mdev_state->vconfig[0x2c], 0x32534348); mdev_state->vconfig[0x34] = 0x00; /* Cap Ptr */ mdev_state->vconfig[0x3d] = 0x01; /* interrupt pin (INTA#) */ /* Vendor specific data */ mdev_state->vconfig[0x40] = 0x23; mdev_state->vconfig[0x43] = 0x80; mdev_state->vconfig[0x44] = 0x23; mdev_state->vconfig[0x48] = 0x23; mdev_state->vconfig[0x4c] = 0x23; mdev_state->vconfig[0x60] = 0x50; mdev_state->vconfig[0x61] = 0x43; mdev_state->vconfig[0x62] = 0x49; mdev_state->vconfig[0x63] = 0x20; mdev_state->vconfig[0x64] = 0x53; mdev_state->vconfig[0x65] = 0x65; mdev_state->vconfig[0x66] = 0x72; mdev_state->vconfig[0x67] = 0x69; mdev_state->vconfig[0x68] = 0x61; mdev_state->vconfig[0x69] = 0x6c; mdev_state->vconfig[0x6a] = 0x2f; mdev_state->vconfig[0x6b] = 0x55; mdev_state->vconfig[0x6c] = 0x41; mdev_state->vconfig[0x6d] = 0x52; mdev_state->vconfig[0x6e] = 0x54;} static void handle_pci_cfg_write(struct mdev_state *mdev_state, u16 offset, u8 *buf, u32 count){ u32 cfg_addr, bar_mask, bar_index = 0; switch (offset) { case 0x04: /* device control */ case 0x06: /* device status */ /* do nothing */ break; case 0x3c: /* interrupt line */ mdev_state->vconfig[0x3c] = buf[0]; break; case 0x3d: /* * Interrupt Pin is hardwired to INTA. * This field is write protected by hardware */ break; case 0x10: /* BAR0 */ case 0x14: /* BAR1 */ if (offset == 0x10) bar_index = 0; else if (offset == 0x14) bar_index = 1; if ((mdev_state->nr_ports == 1) && (bar_index == 1)) { STORE_LE32(&mdev_state->vconfig[offset], 0); break; } cfg_addr = *(u32 *)buf; pr_info("BAR%d addr 0x%x\n", bar_index, cfg_addr); if (cfg_addr == 0xffffffff) { bar_mask = mdev_state->bar_mask[bar_index]; cfg_addr = (cfg_addr & bar_mask); } cfg_addr |= (mdev_state->vconfig[offset] & 0x3ul); STORE_LE32(&mdev_state->vconfig[offset], cfg_addr); break; case 0x18: /* BAR2 */ case 0x1c: /* BAR3 */ case 0x20: /* BAR4 */ STORE_LE32(&mdev_state->vconfig[offset], 0); break; default: pr_info("PCI config write @0x%x of %d bytes not handled\n", offset, count); break; }} static void handle_bar_write(unsigned int index, struct mdev_state *mdev_state, u16 offset, u8 *buf, u32 count){ u8 data = *buf; /* Handle data written by guest */ switch (offset) { case UART_TX: /* if DLAB set, data is LSB of divisor */ if (mdev_state->s[index].dlab) { mdev_state->s[index].divisor |= data; break; } mutex_lock(&mdev_state->rxtx_lock); /* save in TX buffer */ if (mdev_state->s[index].rxtx.count < mdev_state->s[index].max_fifo_size) { mdev_state->s[index].rxtx.fifo[ mdev_state->s[index].rxtx.head] = data; mdev_state->s[index].rxtx.count++; CIRCULAR_BUF_INC_IDX(mdev_state->s[index].rxtx.head); mdev_state->s[index].overrun = false; /* * Trigger interrupt if receive data interrupt is * enabled and fifo reached trigger level */ if ((mdev_state->s[index].uart_reg[UART_IER] & UART_IER_RDI) && (mdev_state->s[index].rxtx.count == mdev_state->s[index].intr_trigger_level)) { /* trigger interrupt */#if defined(DEBUG_INTR) pr_err("Serial port %d: Fifo level trigger\n", index);#endif mtty_trigger_interrupt(mdev_state); } } else {#if defined(DEBUG_INTR) pr_err("Serial port %d: Buffer Overflow\n", index);#endif mdev_state->s[index].overrun = true; /* * Trigger interrupt if receiver line status interrupt * is enabled */ if (mdev_state->s[index].uart_reg[UART_IER] & UART_IER_RLSI) mtty_trigger_interrupt(mdev_state); } mutex_unlock(&mdev_state->rxtx_lock); break; case UART_IER: /* if DLAB set, data is MSB of divisor */ if (mdev_state->s[index].dlab) mdev_state->s[index].divisor |= (u16)data << 8; else { mdev_state->s[index].uart_reg[offset] = data; mutex_lock(&mdev_state->rxtx_lock); if ((data & UART_IER_THRI) && (mdev_state->s[index].rxtx.head == mdev_state->s[index].rxtx.tail)) {#if defined(DEBUG_INTR) pr_err("Serial port %d: IER_THRI write\n", index);#endif mtty_trigger_interrupt(mdev_state); } mutex_unlock(&mdev_state->rxtx_lock); } break; case UART_FCR: mdev_state->s[index].fcr = data; mutex_lock(&mdev_state->rxtx_lock); if (data & (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT)) { /* clear loop back FIFO */ mdev_state->s[index].rxtx.count = 0; mdev_state->s[index].rxtx.head = 0; mdev_state->s[index].rxtx.tail = 0; } mutex_unlock(&mdev_state->rxtx_lock); switch (data & UART_FCR_TRIGGER_MASK) { case UART_FCR_TRIGGER_1: mdev_state->s[index].intr_trigger_level = 1; break; case UART_FCR_TRIGGER_4: mdev_state->s[index].intr_trigger_level = 4; break; case UART_FCR_TRIGGER_8: mdev_state->s[index].intr_trigger_level = 8; break; case UART_FCR_TRIGGER_14: mdev_state->s[index].intr_trigger_level = 14; break; } /* * Set trigger level to 1 otherwise or implement timer with * timeout of 4 characters and on expiring that timer set * Recevice data timeout in IIR register */ mdev_state->s[index].intr_trigger_level = 1; if (data & UART_FCR_ENABLE_FIFO) mdev_state->s[index].max_fifo_size = MAX_FIFO_SIZE; else { mdev_state->s[index].max_fifo_size = 1; mdev_state->s[index].intr_trigger_level = 1; } break; case UART_LCR: if (data & UART_LCR_DLAB) { mdev_state->s[index].dlab = true; mdev_state->s[index].divisor = 0; } else mdev_state->s[index].dlab = false; mdev_state->s[index].uart_reg[offset] = data; break; Lines 1–500 of 2035. Load more
// SPDX-License-Identifier: GPL-2.0-only /* * Mediated virtual PCI serial host device driver * * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. * Author: Neo Jia <cjia@nvidia.com> * Kirti Wankhede <kwankhede@nvidia.com> * * Sample driver that creates mdev device that simulates serial port over PCI * card. */ #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/poll.h> #include <linux/slab.h> #include <linux/cdev.h> #include <linux/sched.h> #include <linux/wait.h> #include <linux/vfio.h> #include <linux/iommu.h> #include <linux/sysfs.h> #include <linux/ctype.h> #include <linux/file.h> #include <linux/mdev.h> #include <linux/pci.h> #include <linux/serial.h> #include <uapi/linux/serial_reg.h> #include <linux/eventfd.h> #include <linux/anon_inodes.h> /* * #defines */ #define VERSION_STRING "0.1" #define DRIVER_AUTHOR "NVIDIA Corporation" #define MTTY_CLASS_NAME "mtty" #define MTTY_NAME "mtty" #define MTTY_STRING_LEN 16 #define MTTY_CONFIG_SPACE_SIZE 0xff #define MTTY_IO_BAR_SIZE 0x8 #define MTTY_MMIO_BAR_SIZE 0x100000 #define STORE_LE16(addr, val) (*(u16 *)addr = val) #define STORE_LE32(addr, val) (*(u32 *)addr = val) #define MAX_FIFO_SIZE 16 #define CIRCULAR_BUF_INC_IDX(idx) (idx = (idx + 1) & (MAX_FIFO_SIZE - 1)) #define MTTY_VFIO_PCI_OFFSET_SHIFT 40 #define MTTY_VFIO_PCI_OFFSET_TO_INDEX(off) (off >> MTTY_VFIO_PCI_OFFSET_SHIFT) #define MTTY_VFIO_PCI_INDEX_TO_OFFSET(index) \ ((u64)(index) << MTTY_VFIO_PCI_OFFSET_SHIFT) #define MTTY_VFIO_PCI_OFFSET_MASK \ (((u64)(1) << MTTY_VFIO_PCI_OFFSET_SHIFT) - 1) #define MAX_MTTYS 24 /* * Global Structures */ static const struct class mtty_class = { .name = MTTY_CLASS_NAME }; static struct mtty_dev { dev_t vd_devt; struct cdev vd_cdev; struct idr vd_idr; struct device dev; struct mdev_parent parent; } mtty_dev; struct mdev_region_info { u64 start; u64 phys_start; u32 size; u64 vfio_offset; }; #if defined(DEBUG_REGS) static const char *wr_reg[] = { "TX", "IER", "FCR", "LCR", "MCR", "LSR", "MSR", "SCR" }; static const char *rd_reg[] = { "RX", "IER", "IIR", "LCR", "MCR", "LSR", "MSR", "SCR" }; #endif /* loop back buffer */ struct rxtx { u8 fifo[MAX_FIFO_SIZE]; u8 head, tail; u8 count; }; struct serial_port { u8 uart_reg[8]; /* 8 registers */ struct rxtx rxtx; /* loop back buffer */ bool dlab; bool overrun; u16 divisor; u8 fcr; /* FIFO control register */ u8 max_fifo_size; u8 intr_trigger_level; /* interrupt trigger level */ }; struct mtty_data { u64 magic; #define MTTY_MAGIC 0x7e9d09898c3e2c4e /* Nothing clever, just random */ u32 major_ver; #define MTTY_MAJOR_VER 1 u32 minor_ver; #define MTTY_MINOR_VER 0 u32 nr_ports; u32 flags; struct serial_port ports[2]; }; struct mdev_state; struct mtty_migration_file { struct file *filp; struct mutex lock; struct mdev_state *mdev_state; struct mtty_data data; ssize_t filled_size; u8 disabled:1; }; /* State of each mdev device */ struct mdev_state { struct vfio_device vdev; struct eventfd_ctx *intx_evtfd; struct eventfd_ctx *msi_evtfd; int irq_index; u8 *vconfig; struct mutex ops_lock; struct mdev_device *mdev; struct mdev_region_info region_info[VFIO_PCI_NUM_REGIONS]; u32 bar_mask[VFIO_PCI_NUM_REGIONS]; struct list_head next; struct serial_port s[2]; struct mutex rxtx_lock; struct vfio_device_info dev_info; int nr_ports; enum vfio_device_mig_state state; struct mutex state_mutex; struct mutex reset_mutex; struct mtty_migration_file *saving_migf; struct mtty_migration_file *resuming_migf; u8 deferred_reset:1; u8 intx_mask:1; }; static struct mtty_type { struct mdev_type type; int nr_ports; } mtty_types[2] = { { .nr_ports = 1, .type.sysfs_name = "1", .type.pretty_name = "Single port serial" }, { .nr_ports = 2, .type.sysfs_name = "2", .type.pretty_name = "Dual port serial" }, }; static struct mdev_type *mtty_mdev_types[] = { &mtty_types[0].type, &mtty_types[1].type, }; static atomic_t mdev_avail_ports = ATOMIC_INIT(MAX_MTTYS); static const struct file_operations vd_fops = { .owner = THIS_MODULE, }; static const struct vfio_device_ops mtty_dev_ops; /* Helper functions */ static void dump_buffer(u8 *buf, uint32_t count) { #if defined(DEBUG) int i; pr_info("Buffer:\n"); for (i = 0; i < count; i++) { pr_info("%2x ", *(buf + i)); if ((i + 1) % 16 == 0) pr_info("\n"); } #endif } static bool is_intx(struct mdev_state *mdev_state) { return mdev_state->irq_index == VFIO_PCI_INTX_IRQ_INDEX; } static bool is_msi(struct mdev_state *mdev_state) { return mdev_state->irq_index == VFIO_PCI_MSI_IRQ_INDEX; } static bool is_noirq(struct mdev_state *mdev_state) { return !is_intx(mdev_state) && !is_msi(mdev_state); } static void mtty_trigger_interrupt(struct mdev_state *mdev_state) { lockdep_assert_held(&mdev_state->ops_lock); if (is_msi(mdev_state)) { if (mdev_state->msi_evtfd) eventfd_signal(mdev_state->msi_evtfd); } else if (is_intx(mdev_state)) { if (mdev_state->intx_evtfd && !mdev_state->intx_mask) { eventfd_signal(mdev_state->intx_evtfd); mdev_state->intx_mask = true; } } } static void mtty_create_config_space(struct mdev_state *mdev_state) { /* PCI dev ID */ STORE_LE32((u32 *) &mdev_state->vconfig[0x0], 0x32534348); /* Control: I/O+, Mem-, BusMaster- */ STORE_LE16((u16 *) &mdev_state->vconfig[0x4], 0x0001); /* Status: capabilities list absent */ STORE_LE16((u16 *) &mdev_state->vconfig[0x6], 0x0200); /* Rev ID */ mdev_state->vconfig[0x8] = 0x10; /* programming interface class : 16550-compatible serial controller */ mdev_state->vconfig[0x9] = 0x02; /* Sub class : 00 */ mdev_state->vconfig[0xa] = 0x00; /* Base class : Simple Communication controllers */ mdev_state->vconfig[0xb] = 0x07; /* base address registers */ /* BAR0: IO space */ STORE_LE32((u32 *) &mdev_state->vconfig[0x10], 0x000001); mdev_state->bar_mask[0] = ~(MTTY_IO_BAR_SIZE) + 1; if (mdev_state->nr_ports == 2) { /* BAR1: IO space */ STORE_LE32((u32 *) &mdev_state->vconfig[0x14], 0x000001); mdev_state->bar_mask[1] = ~(MTTY_IO_BAR_SIZE) + 1; } /* Subsystem ID */ STORE_LE32((u32 *) &mdev_state->vconfig[0x2c], 0x32534348); mdev_state->vconfig[0x34] = 0x00; /* Cap Ptr */ mdev_state->vconfig[0x3d] = 0x01; /* interrupt pin (INTA#) */ /* Vendor specific data */ mdev_state->vconfig[0x40] = 0x23; mdev_state->vconfig[0x43] = 0x80; mdev_state->vconfig[0x44] = 0x23; mdev_state->vconfig[0x48] = 0x23; mdev_state->vconfig[0x4c] = 0x23; mdev_state->vconfig[0x60] = 0x50; mdev_state->vconfig[0x61] = 0x43; mdev_state->vconfig[0x62] = 0x49; mdev_state->vconfig[0x63] = 0x20; mdev_state->vconfig[0x64] = 0x53; mdev_state->vconfig[0x65] = 0x65; mdev_state->vconfig[0x66] = 0x72; mdev_state->vconfig[0x67] = 0x69; mdev_state->vconfig[0x68] = 0x61; mdev_state->vconfig[0x69] = 0x6c; mdev_state->vconfig[0x6a] = 0x2f; mdev_state->vconfig[0x6b] = 0x55; mdev_state->vconfig[0x6c] = 0x41; mdev_state->vconfig[0x6d] = 0x52; mdev_state->vconfig[0x6e] = 0x54; } static void handle_pci_cfg_write(struct mdev_state *mdev_state, u16 offset, u8 *buf, u32 count) { u32 cfg_addr, bar_mask, bar_index = 0; switch (offset) { case 0x04: /* device control */ case 0x06: /* device status */ /* do nothing */ break; case 0x3c: /* interrupt line */ mdev_state->vconfig[0x3c] = buf[0]; break; case 0x3d: /* * Interrupt Pin is hardwired to INTA. * This field is write protected by hardware */ break; case 0x10: /* BAR0 */ case 0x14: /* BAR1 */ if (offset == 0x10) bar_index = 0; else if (offset == 0x14) bar_index = 1; if ((mdev_state->nr_ports == 1) && (bar_index == 1)) { STORE_LE32(&mdev_state->vconfig[offset], 0); break; } cfg_addr = *(u32 *)buf; pr_info("BAR%d addr 0x%x\n", bar_index, cfg_addr); if (cfg_addr == 0xffffffff) { bar_mask = mdev_state->bar_mask[bar_index]; cfg_addr = (cfg_addr & bar_mask); } cfg_addr |= (mdev_state->vconfig[offset] & 0x3ul); STORE_LE32(&mdev_state->vconfig[offset], cfg_addr); break; case 0x18: /* BAR2 */ case 0x1c: /* BAR3 */ case 0x20: /* BAR4 */ STORE_LE32(&mdev_state->vconfig[offset], 0); break; default: pr_info("PCI config write @0x%x of %d bytes not handled\n", offset, count); break; } } static void handle_bar_write(unsigned int index, struct mdev_state *mdev_state, u16 offset, u8 *buf, u32 count) { u8 data = *buf; /* Handle data written by guest */ switch (offset) { case UART_TX: /* if DLAB set, data is LSB of divisor */ if (mdev_state->s[index].dlab) { mdev_state->s[index].divisor |= data; break; } mutex_lock(&mdev_state->rxtx_lock); /* save in TX buffer */ if (mdev_state->s[index].rxtx.count < mdev_state->s[index].max_fifo_size) { mdev_state->s[index].rxtx.fifo[ mdev_state->s[index].rxtx.head] = data; mdev_state->s[index].rxtx.count++; CIRCULAR_BUF_INC_IDX(mdev_state->s[index].rxtx.head); mdev_state->s[index].overrun = false; /* * Trigger interrupt if receive data interrupt is * enabled and fifo reached trigger level */ if ((mdev_state->s[index].uart_reg[UART_IER] & UART_IER_RDI) && (mdev_state->s[index].rxtx.count == mdev_state->s[index].intr_trigger_level)) { /* trigger interrupt */ #if defined(DEBUG_INTR) pr_err("Serial port %d: Fifo level trigger\n", index); #endif mtty_trigger_interrupt(mdev_state); } } else { #if defined(DEBUG_INTR) pr_err("Serial port %d: Buffer Overflow\n", index); #endif mdev_state->s[index].overrun = true; /* * Trigger interrupt if receiver line status interrupt * is enabled */ if (mdev_state->s[index].uart_reg[UART_IER] & UART_IER_RLSI) mtty_trigger_interrupt(mdev_state); } mutex_unlock(&mdev_state->rxtx_lock); break; case UART_IER: /* if DLAB set, data is MSB of divisor */ if (mdev_state->s[index].dlab) mdev_state->s[index].divisor |= (u16)data << 8; else { mdev_state->s[index].uart_reg[offset] = data; mutex_lock(&mdev_state->rxtx_lock); if ((data & UART_IER_THRI) && (mdev_state->s[index].rxtx.head == mdev_state->s[index].rxtx.tail)) { #if defined(DEBUG_INTR) pr_err("Serial port %d: IER_THRI write\n", index); #endif mtty_trigger_interrupt(mdev_state); } mutex_unlock(&mdev_state->rxtx_lock); } break; case UART_FCR: mdev_state->s[index].fcr = data; mutex_lock(&mdev_state->rxtx_lock); if (data & (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT)) { /* clear loop back FIFO */ mdev_state->s[index].rxtx.count = 0; mdev_state->s[index].rxtx.head = 0; mdev_state->s[index].rxtx.tail = 0; } mutex_unlock(&mdev_state->rxtx_lock); switch (data & UART_FCR_TRIGGER_MASK) { case UART_FCR_TRIGGER_1: mdev_state->s[index].intr_trigger_level = 1; break; case UART_FCR_TRIGGER_4: mdev_state->s[index].intr_trigger_level = 4; break; case UART_FCR_TRIGGER_8: mdev_state->s[index].intr_trigger_level = 8; break; case UART_FCR_TRIGGER_14: mdev_state->s[index].intr_trigger_level = 14; break; } /* * Set trigger level to 1 otherwise or implement timer with * timeout of 4 characters and on expiring that timer set * Recevice data timeout in IIR register */ mdev_state->s[index].intr_trigger_level = 1; if (data & UART_FCR_ENABLE_FIFO) mdev_state->s[index].max_fifo_size = MAX_FIFO_SIZE; else { mdev_state->s[index].max_fifo_size = 1; mdev_state->s[index].intr_trigger_level = 1; } break; case UART_LCR: if (data & UART_LCR_DLAB) { mdev_state->s[index].dlab = true; mdev_state->s[index].divisor = 0; } else mdev_state->s[index].dlab = false; mdev_state->s[index].uart_reg[offset] = data; break;