Source
// SPDX-License-Identifier: GPL-2.0
/* ePAPR hypervisor byte channel device driver
*
* Copyright 2009-2011 Freescale Semiconductor, Inc.
*
* Author: Timur Tabi <timur@freescale.com>
*
* This driver support three distinct interfaces, all of which are related to
* ePAPR hypervisor byte channels.
*
* 1) An early-console (udbg) driver. This provides early console output
* through a byte channel. The byte channel handle must be specified in a
* Kconfig option.
*
* 2) A normal console driver. Output is sent to the byte channel designated
* for stdout in the device tree. The console driver is for handling kernel
* printk calls.
*
* 3) A tty driver, which is used to handle user-space input and output. The
* byte channel used for the console is designated as the default tty.
*/
/* The size of the transmit circular buffer. This must be a power of two. */
/* Per-byte channel private data */
struct ehv_bc_data {
struct device *dev;
struct tty_port port;
uint32_t handle;
unsigned int rx_irq;
unsigned int tx_irq;
spinlock_t lock; /* lock for transmit buffer */
unsigned char buf[BUF_SIZE]; /* transmit circular buffer */
unsigned int head; /* circular buffer head */
unsigned int tail; /* circular buffer tail */
int tx_irq_enabled; /* true == TX interrupt is enabled */
};
/* Array of byte channel objects */
static struct ehv_bc_data *bcs;
/* Byte channel handle for stdout (and stdin), taken from device tree */
static unsigned int stdout_bc;
/* Virtual IRQ for the byte channel handle for stdin, taken from device tree */
static unsigned int stdout_irq;
/**************************** SUPPORT FUNCTIONS ****************************/
/*
* Enable the transmit interrupt
*
* Unlike a serial device, byte channels have no mechanism for disabling their
* own receive or transmit interrupts. To emulate that feature, we toggle
* the IRQ in the kernel.
*
* We cannot just blindly call enable_irq() or disable_irq(), because these
* calls are reference counted. This means that we cannot call enable_irq()
* if interrupts are already enabled. This can happen in two situations:
*
* 1. The tty layer makes two back-to-back calls to ehv_bc_tty_write()
* 2. A transmit interrupt occurs while executing ehv_bc_tx_dequeue()
*
* To work around this, we keep a flag to tell us if the IRQ is enabled or not.
*/
static void enable_tx_interrupt(struct ehv_bc_data *bc)
{
if (!bc->tx_irq_enabled) {
enable_irq(bc->tx_irq);
bc->tx_irq_enabled = 1;
}
}