summaryrefslogtreecommitdiff
path: root/drivers/tty/ehv_bytechan.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty/ehv_bytechan.c')
-rw-r--r--drivers/tty/ehv_bytechan.c199
1 files changed, 73 insertions, 126 deletions
diff --git a/drivers/tty/ehv_bytechan.c b/drivers/tty/ehv_bytechan.c
index 9bffcec5ad82..69508d7a4135 100644
--- a/drivers/tty/ehv_bytechan.c
+++ b/drivers/tty/ehv_bytechan.c
@@ -1,13 +1,10 @@
+// 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 file is licensed under the terms of the GNU General Public License
- * version 2. This program is licensed "as is" without any warranty of any
- * kind, whether express or implied.
- *
* This driver support three distinct interfaces, all of which are related to
* ePAPR hypervisor byte channels.
*
@@ -23,7 +20,6 @@
* byte channel used for the console is designated as the default tty.
*/
-#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/err.h>
@@ -32,6 +28,7 @@
#include <linux/poll.h>
#include <asm/epapr_hcalls.h>
#include <linux/of.h>
+#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/console.h>
@@ -52,7 +49,7 @@ struct ehv_bc_data {
unsigned int tx_irq;
spinlock_t lock; /* lock for transmit buffer */
- unsigned char buf[BUF_SIZE]; /* transmit circular buffer */
+ u8 buf[BUF_SIZE]; /* transmit circular buffer */
unsigned int head; /* circular buffer head */
unsigned int tail; /* circular buffer tail */
@@ -107,55 +104,22 @@ static void disable_tx_interrupt(struct ehv_bc_data *bc)
*
* The byte channel to be used for the console is specified via a "stdout"
* property in the /chosen node.
- *
- * For compatible with legacy device trees, we also look for a "stdout" alias.
*/
static int find_console_handle(void)
{
- struct device_node *np, *np2;
- const char *sprop = NULL;
+ struct device_node *np = of_stdout;
const uint32_t *iprop;
- np = of_find_node_by_path("/chosen");
- if (np)
- sprop = of_get_property(np, "stdout-path", NULL);
-
- if (!np || !sprop) {
- of_node_put(np);
- np = of_find_node_by_name(NULL, "aliases");
- if (np)
- sprop = of_get_property(np, "stdout", NULL);
- }
-
- if (!sprop) {
- of_node_put(np);
- return 0;
- }
-
/* We don't care what the aliased node is actually called. We only
* care if it's compatible with "epapr,hv-byte-channel", because that
- * indicates that it's a byte channel node. We use a temporary
- * variable, 'np2', because we can't release 'np' until we're done with
- * 'sprop'.
+ * indicates that it's a byte channel node.
*/
- np2 = of_find_node_by_path(sprop);
- of_node_put(np);
- np = np2;
- if (!np) {
- pr_warning("ehv-bc: stdout node '%s' does not exist\n", sprop);
- return 0;
- }
-
- /* Is it a byte channel? */
- if (!of_device_is_compatible(np, "epapr,hv-byte-channel")) {
- of_node_put(np);
+ if (!np || !of_device_is_compatible(np, "epapr,hv-byte-channel"))
return 0;
- }
stdout_irq = irq_of_parse_and_map(np, 0);
- if (stdout_irq == NO_IRQ) {
- pr_err("ehv-bc: no 'interrupts' property in %s node\n", sprop);
- of_node_put(np);
+ if (!stdout_irq) {
+ pr_err("ehv-bc: no 'interrupts' property in %pOF node\n", np);
return 0;
}
@@ -164,17 +128,32 @@ static int find_console_handle(void)
*/
iprop = of_get_property(np, "hv-handle", NULL);
if (!iprop) {
- pr_err("ehv-bc: no 'hv-handle' property in %s node\n",
- np->name);
- of_node_put(np);
+ pr_err("ehv-bc: no 'hv-handle' property in %pOFn node\n",
+ np);
return 0;
}
stdout_bc = be32_to_cpu(*iprop);
-
- of_node_put(np);
return 1;
}
+static unsigned int local_ev_byte_channel_send(unsigned int handle,
+ unsigned int *count,
+ const u8 *p)
+{
+ u8 buffer[EV_BYTE_CHANNEL_MAX_BYTES];
+ unsigned int c = *count;
+
+ /*
+ * ev_byte_channel_send() expects at least EV_BYTE_CHANNEL_MAX_BYTES
+ * (16 B) in the buffer. Fake it using a local buffer if needed.
+ */
+ if (c < sizeof(buffer)) {
+ memcpy_and_pad(buffer, sizeof(buffer), p, c, 0);
+ p = buffer;
+ }
+ return ev_byte_channel_send(handle, count, p);
+}
+
/*************************** EARLY CONSOLE DRIVER ***************************/
#ifdef CONFIG_PPC_EARLY_DEBUG_EHV_BC
@@ -187,13 +166,13 @@ static int find_console_handle(void)
* has been sent, or if some error has occurred.
*
*/
-static void byte_channel_spin_send(const char data)
+static void byte_channel_spin_send(const u8 data)
{
int ret, count;
do {
count = 1;
- ret = ev_byte_channel_send(CONFIG_PPC_EARLY_DEBUG_EHV_BC_HANDLE,
+ ret = local_ev_byte_channel_send(CONFIG_PPC_EARLY_DEBUG_EHV_BC_HANDLE,
&count, &data);
} while (ret == EV_EAGAIN);
}
@@ -260,7 +239,7 @@ static int ehv_bc_console_byte_channel_send(unsigned int handle, const char *s,
while (count) {
len = min_t(unsigned int, count, EV_BYTE_CHANNEL_MAX_BYTES);
do {
- ret = ev_byte_channel_send(handle, &len, s);
+ ret = local_ev_byte_channel_send(handle, &len, s);
} while (ret == EV_EAGAIN);
count -= len;
s += len;
@@ -343,8 +322,8 @@ static int __init ehv_bc_console_init(void)
* handle for udbg.
*/
if (stdout_bc != CONFIG_PPC_EARLY_DEBUG_EHV_BC_HANDLE)
- pr_warning("ehv-bc: udbg handle %u is not the stdout handle\n",
- CONFIG_PPC_EARLY_DEBUG_EHV_BC_HANDLE);
+ pr_warn("ehv-bc: udbg handle %u is not the stdout handle\n",
+ CONFIG_PPC_EARLY_DEBUG_EHV_BC_HANDLE);
#endif
/* add_preferred_console() must be called before register_console(),
@@ -364,7 +343,7 @@ console_initcall(ehv_bc_console_init);
/******************************** TTY DRIVER ********************************/
/*
- * byte channel receive interupt handler
+ * byte channel receive interrupt handler
*
* This ISR is called whenever data is available on a byte channel.
*/
@@ -440,7 +419,7 @@ static void ehv_bc_tx_dequeue(struct ehv_bc_data *bc)
CIRC_CNT_TO_END(bc->head, bc->tail, BUF_SIZE),
EV_BYTE_CHANNEL_MAX_BYTES);
- ret = ev_byte_channel_send(bc->handle, &len, bc->buf + bc->tail);
+ ret = local_ev_byte_channel_send(bc->handle, &len, bc->buf + bc->tail);
/* 'len' is valid only if the return code is 0 or EV_EAGAIN */
if (!ret || (ret == EV_EAGAIN))
@@ -464,7 +443,7 @@ static void ehv_bc_tx_dequeue(struct ehv_bc_data *bc)
}
/*
- * byte channel transmit interupt handler
+ * byte channel transmit interrupt handler
*
* This ISR is called whenever space becomes available for transmitting
* characters on a byte channel.
@@ -490,13 +469,12 @@ static irqreturn_t ehv_bc_tty_tx_isr(int irq, void *data)
* ehv_bc_tty_write_room() will never lie, so the tty layer will never send us
* too much data.
*/
-static int ehv_bc_tty_write(struct tty_struct *ttys, const unsigned char *s,
- int count)
+static ssize_t ehv_bc_tty_write(struct tty_struct *ttys, const u8 *s,
+ size_t count)
{
struct ehv_bc_data *bc = ttys->driver_data;
unsigned long flags;
- unsigned int len;
- unsigned int written = 0;
+ size_t len, written = 0;
while (1) {
spin_lock_irqsave(&bc->lock, flags);
@@ -560,11 +538,11 @@ static void ehv_bc_tty_close(struct tty_struct *ttys, struct file *filp)
* how much write room the driver can guarantee will be sent OR BUFFERED. This
* driver MUST honor the return value.
*/
-static int ehv_bc_tty_write_room(struct tty_struct *ttys)
+static unsigned int ehv_bc_tty_write_room(struct tty_struct *ttys)
{
struct ehv_bc_data *bc = ttys->driver_data;
unsigned long flags;
- int count;
+ unsigned int count;
spin_lock_irqsave(&bc->lock, flags);
count = CIRC_SPACE(bc->head, bc->tail, BUF_SIZE);
@@ -700,8 +678,8 @@ static int ehv_bc_tty_probe(struct platform_device *pdev)
iprop = of_get_property(np, "hv-handle", NULL);
if (!iprop) {
- dev_err(&pdev->dev, "no 'hv-handle' property in %s node\n",
- np->name);
+ dev_err(&pdev->dev, "no 'hv-handle' property in %pOFn node\n",
+ np);
return -ENODEV;
}
@@ -720,9 +698,9 @@ static int ehv_bc_tty_probe(struct platform_device *pdev)
bc->rx_irq = irq_of_parse_and_map(np, 0);
bc->tx_irq = irq_of_parse_and_map(np, 1);
- if ((bc->rx_irq == NO_IRQ) || (bc->tx_irq == NO_IRQ)) {
- dev_err(&pdev->dev, "no 'interrupts' property in %s node\n",
- np->name);
+ if (!bc->rx_irq || !bc->tx_irq) {
+ dev_err(&pdev->dev, "no 'interrupts' property in %pOFn node\n",
+ np);
ret = -ENODEV;
goto error;
}
@@ -754,19 +732,6 @@ error:
return ret;
}
-static int ehv_bc_tty_remove(struct platform_device *pdev)
-{
- struct ehv_bc_data *bc = dev_get_drvdata(&pdev->dev);
-
- tty_unregister_device(ehv_bc_driver, bc - bcs);
-
- tty_port_destroy(&bc->port);
- irq_dispose_mapping(bc->tx_irq);
- irq_dispose_mapping(bc->rx_irq);
-
- return 0;
-}
-
static const struct of_device_id ehv_bc_tty_of_ids[] = {
{ .compatible = "epapr,hv-byte-channel" },
{}
@@ -774,21 +739,21 @@ static const struct of_device_id ehv_bc_tty_of_ids[] = {
static struct platform_driver ehv_bc_tty_driver = {
.driver = {
- .owner = THIS_MODULE,
.name = "ehv-bc",
.of_match_table = ehv_bc_tty_of_ids,
+ .suppress_bind_attrs = true,
},
.probe = ehv_bc_tty_probe,
- .remove = ehv_bc_tty_remove,
};
/**
* ehv_bc_init - ePAPR hypervisor byte channel driver initialization
*
- * This function is called when this module is loaded.
+ * This function is called when this driver is loaded.
*/
static int __init ehv_bc_init(void)
{
+ struct tty_driver *driver;
struct device_node *np;
unsigned int count = 0; /* Number of elements in bcs[] */
int ret;
@@ -807,67 +772,49 @@ static int __init ehv_bc_init(void)
* array, then you can use pointer math (e.g. "bc - bcs") to get its
* tty index.
*/
- bcs = kzalloc(count * sizeof(struct ehv_bc_data), GFP_KERNEL);
+ bcs = kcalloc(count, sizeof(struct ehv_bc_data), GFP_KERNEL);
if (!bcs)
return -ENOMEM;
- ehv_bc_driver = alloc_tty_driver(count);
- if (!ehv_bc_driver) {
- ret = -ENOMEM;
- goto error;
+ driver = tty_alloc_driver(count, TTY_DRIVER_REAL_RAW |
+ TTY_DRIVER_DYNAMIC_DEV);
+ if (IS_ERR(driver)) {
+ ret = PTR_ERR(driver);
+ goto err_free_bcs;
}
- ehv_bc_driver->driver_name = "ehv-bc";
- ehv_bc_driver->name = ehv_bc_console.name;
- ehv_bc_driver->type = TTY_DRIVER_TYPE_CONSOLE;
- ehv_bc_driver->subtype = SYSTEM_TYPE_CONSOLE;
- ehv_bc_driver->init_termios = tty_std_termios;
- ehv_bc_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
- tty_set_operations(ehv_bc_driver, &ehv_bc_ops);
+ driver->driver_name = "ehv-bc";
+ driver->name = ehv_bc_console.name;
+ driver->type = TTY_DRIVER_TYPE_CONSOLE;
+ driver->subtype = SYSTEM_TYPE_CONSOLE;
+ driver->init_termios = tty_std_termios;
+ tty_set_operations(driver, &ehv_bc_ops);
- ret = tty_register_driver(ehv_bc_driver);
+ ret = tty_register_driver(driver);
if (ret) {
pr_err("ehv-bc: could not register tty driver (ret=%i)\n", ret);
- goto error;
+ goto err_tty_driver_kref_put;
}
+ ehv_bc_driver = driver;
+
ret = platform_driver_register(&ehv_bc_tty_driver);
if (ret) {
pr_err("ehv-bc: could not register platform driver (ret=%i)\n",
ret);
- goto error;
+ goto err_deregister_tty_driver;
}
return 0;
-error:
- if (ehv_bc_driver) {
- tty_unregister_driver(ehv_bc_driver);
- put_tty_driver(ehv_bc_driver);
- }
-
+err_deregister_tty_driver:
+ ehv_bc_driver = NULL;
+ tty_unregister_driver(driver);
+err_tty_driver_kref_put:
+ tty_driver_kref_put(driver);
+err_free_bcs:
kfree(bcs);
return ret;
}
-
-
-/**
- * ehv_bc_exit - ePAPR hypervisor byte channel driver termination
- *
- * This function is called when this driver is unloaded.
- */
-static void __exit ehv_bc_exit(void)
-{
- platform_driver_unregister(&ehv_bc_tty_driver);
- tty_unregister_driver(ehv_bc_driver);
- put_tty_driver(ehv_bc_driver);
- kfree(bcs);
-}
-
-module_init(ehv_bc_init);
-module_exit(ehv_bc_exit);
-
-MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
-MODULE_DESCRIPTION("ePAPR hypervisor byte channel driver");
-MODULE_LICENSE("GPL v2");
+device_initcall(ehv_bc_init);