summaryrefslogtreecommitdiff
path: root/drivers/ipack/devices/ipoctal.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/ipack/devices/ipoctal.c')
-rw-r--r--drivers/ipack/devices/ipoctal.c227
1 files changed, 133 insertions, 94 deletions
diff --git a/drivers/ipack/devices/ipoctal.c b/drivers/ipack/devices/ipoctal.c
index 141094e7c06e..ba2e9e52d72b 100644
--- a/drivers/ipack/devices/ipoctal.c
+++ b/drivers/ipack/devices/ipoctal.c
@@ -1,15 +1,10 @@
-/**
- * ipoctal.c
- *
+// SPDX-License-Identifier: GPL-2.0-only
+/*
* driver for the GE IP-OCTAL boards
*
* Copyright (C) 2009-2012 CERN (www.cern.ch)
* Author: Nicolas Serafini, EIC2 SA
* Author: Samuel Iglesias Gonsalvez <siglesias@igalia.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; version 2 of the License.
*/
#include <linux/device.h>
@@ -38,6 +33,7 @@ struct ipoctal_channel {
unsigned int pointer_read;
unsigned int pointer_write;
struct tty_port tty_port;
+ bool tty_registered;
union scc2698_channel __iomem *regs;
union scc2698_block __iomem *block_regs;
unsigned int board_id;
@@ -55,6 +51,22 @@ struct ipoctal {
u8 __iomem *int_space;
};
+static inline struct ipoctal *chan_to_ipoctal(struct ipoctal_channel *chan,
+ unsigned int index)
+{
+ return container_of(chan, struct ipoctal, channel[index]);
+}
+
+static void ipoctal_reset_channel(struct ipoctal_channel *channel)
+{
+ iowrite8(CR_DISABLE_RX | CR_DISABLE_TX, &channel->regs->w.cr);
+ channel->rx_enable = 0;
+ iowrite8(CR_CMD_RESET_RX, &channel->regs->w.cr);
+ iowrite8(CR_CMD_RESET_TX, &channel->regs->w.cr);
+ iowrite8(CR_CMD_RESET_ERR_STATUS, &channel->regs->w.cr);
+ iowrite8(CR_CMD_RESET_MR, &channel->regs->w.cr);
+}
+
static int ipoctal_port_activate(struct tty_port *port, struct tty_struct *tty)
{
struct ipoctal_channel *channel;
@@ -70,13 +82,33 @@ static int ipoctal_port_activate(struct tty_port *port, struct tty_struct *tty)
return 0;
}
-static int ipoctal_open(struct tty_struct *tty, struct file *file)
+static int ipoctal_install(struct tty_driver *driver, struct tty_struct *tty)
{
- struct ipoctal_channel *channel;
+ struct ipoctal_channel *channel = dev_get_drvdata(tty->dev);
+ struct ipoctal *ipoctal = chan_to_ipoctal(channel, tty->index);
+ int res;
+
+ if (!ipack_get_carrier(ipoctal->dev))
+ return -EBUSY;
+
+ res = tty_standard_install(driver, tty);
+ if (res)
+ goto err_put_carrier;
- channel = dev_get_drvdata(tty->dev);
tty->driver_data = channel;
+ return 0;
+
+err_put_carrier:
+ ipack_put_carrier(ipoctal->dev);
+
+ return res;
+}
+
+static int ipoctal_open(struct tty_struct *tty, struct file *file)
+{
+ struct ipoctal_channel *channel = tty->driver_data;
+
return tty_port_open(&channel->tty_port, tty, file);
}
@@ -126,9 +158,7 @@ static int ipoctal_get_icount(struct tty_struct *tty,
static void ipoctal_irq_rx(struct ipoctal_channel *channel, u8 sr)
{
struct tty_port *port = &channel->tty_port;
- unsigned char value;
- unsigned char flag;
- u8 isr;
+ u8 isr, value, flag;
do {
value = ioread8(&channel->regs->r.rhr);
@@ -151,7 +181,6 @@ static void ipoctal_irq_rx(struct ipoctal_channel *channel, u8 sr)
flag = TTY_FRAME;
}
if (sr & SR_RECEIVED_BREAK) {
- iowrite8(CR_CMD_RESET_BREAK_CHANGE, &channel->regs->w.cr);
channel->stats.rcv_break++;
flag = TTY_BREAK;
}
@@ -171,30 +200,34 @@ static void ipoctal_irq_rx(struct ipoctal_channel *channel, u8 sr)
static void ipoctal_irq_tx(struct ipoctal_channel *channel)
{
- unsigned char value;
unsigned int *pointer_write = &channel->pointer_write;
+ u8 value;
if (channel->nb_bytes == 0)
return;
+ spin_lock(&channel->lock);
value = channel->tty_port.xmit_buf[*pointer_write];
iowrite8(value, &channel->regs->w.thr);
channel->stats.tx++;
(*pointer_write)++;
*pointer_write = *pointer_write % PAGE_SIZE;
channel->nb_bytes--;
+ spin_unlock(&channel->lock);
}
static void ipoctal_irq_channel(struct ipoctal_channel *channel)
{
u8 isr, sr;
- spin_lock(&channel->lock);
/* The HW is organized in pair of channels. See which register we need
* to read from */
isr = ioread8(&channel->block_regs->r.isr);
sr = ioread8(&channel->regs->r.sr);
+ if (isr & (IMR_DELTA_BREAK_A | IMR_DELTA_BREAK_B))
+ iowrite8(CR_CMD_RESET_BREAK_CHANGE, &channel->regs->w.cr);
+
if ((sr & SR_TX_EMPTY) && (channel->nb_bytes == 0)) {
iowrite8(CR_DISABLE_TX, &channel->regs->w.cr);
/* In case of RS-485, change from TX to RX when finishing TX.
@@ -213,14 +246,12 @@ static void ipoctal_irq_channel(struct ipoctal_channel *channel)
/* TX of each character */
if ((isr & channel->isr_tx_rdy_mask) && (sr & SR_TX_READY))
ipoctal_irq_tx(channel);
-
- spin_unlock(&channel->lock);
}
static irqreturn_t ipoctal_irq_handler(void *arg)
{
unsigned int i;
- struct ipoctal *ipoctal = (struct ipoctal *) arg;
+ struct ipoctal *ipoctal = arg;
/* Clear the IPack device interrupt */
readw(ipoctal->int_space + ACK_INT_REQ0);
@@ -243,8 +274,7 @@ static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr,
{
int res;
int i;
- struct tty_driver *tty;
- char name[20];
+ struct tty_driver *drv;
struct ipoctal_channel *channel;
struct ipack_region *region;
void __iomem *addr;
@@ -254,7 +284,7 @@ static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr,
ipoctal->board_id = ipoctal->dev->id_device;
region = &ipoctal->dev->region[IPACK_IO_SPACE];
- addr = devm_ioremap_nocache(&ipoctal->dev->dev,
+ addr = devm_ioremap(&ipoctal->dev->dev,
region->start, region->size);
if (!addr) {
dev_err(&ipoctal->dev->dev,
@@ -270,7 +300,7 @@ static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr,
region = &ipoctal->dev->region[IPACK_INT_SPACE];
ipoctal->int_space =
- devm_ioremap_nocache(&ipoctal->dev->dev,
+ devm_ioremap(&ipoctal->dev->dev,
region->start, region->size);
if (!ipoctal->int_space) {
dev_err(&ipoctal->dev->dev,
@@ -281,7 +311,7 @@ static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr,
region = &ipoctal->dev->region[IPACK_MEM8_SPACE];
ipoctal->mem8_space =
- devm_ioremap_nocache(&ipoctal->dev->dev,
+ devm_ioremap(&ipoctal->dev->dev,
region->start, 0x8000);
if (!ipoctal->mem8_space) {
dev_err(&ipoctal->dev->dev,
@@ -305,10 +335,7 @@ static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr,
channel->isr_rx_rdy_mask = ISR_RxRDY_FFULL_A;
}
- iowrite8(CR_DISABLE_RX | CR_DISABLE_TX, &channel->regs->w.cr);
- channel->rx_enable = 0;
- iowrite8(CR_CMD_RESET_RX, &channel->regs->w.cr);
- iowrite8(CR_CMD_RESET_TX, &channel->regs->w.cr);
+ ipoctal_reset_channel(channel);
iowrite8(MR1_CHRL_8_BITS | MR1_ERROR_CHAR | MR1_RxINT_RxRDY,
&channel->regs->w.mr); /* mr1 */
iowrite8(0, &channel->regs->w.mr); /* mr2 */
@@ -324,57 +351,53 @@ static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr,
&block_regs[i].w.imr);
}
- /*
- * IP-OCTAL has different addresses to copy its IRQ vector.
- * Depending of the carrier these addresses are accesible or not.
- * More info in the datasheet.
- */
- ipoctal->dev->bus->ops->request_irq(ipoctal->dev,
- ipoctal_irq_handler, ipoctal);
/* Dummy write */
iowrite8(1, ipoctal->mem8_space + 1);
/* Register the TTY device */
/* Each IP-OCTAL channel is a TTY port */
- tty = alloc_tty_driver(NR_CHANNELS);
-
- if (!tty)
- return -ENOMEM;
+ drv = tty_alloc_driver(NR_CHANNELS, TTY_DRIVER_REAL_RAW |
+ TTY_DRIVER_DYNAMIC_DEV);
+ if (IS_ERR(drv))
+ return PTR_ERR(drv);
/* Fill struct tty_driver with ipoctal data */
- tty->owner = THIS_MODULE;
- tty->driver_name = KBUILD_MODNAME;
- sprintf(name, KBUILD_MODNAME ".%d.%d.", bus_nr, slot);
- tty->name = name;
- tty->major = 0;
-
- tty->minor_start = 0;
- tty->type = TTY_DRIVER_TYPE_SERIAL;
- tty->subtype = SERIAL_TYPE_NORMAL;
- tty->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
- tty->init_termios = tty_std_termios;
- tty->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
- tty->init_termios.c_ispeed = 9600;
- tty->init_termios.c_ospeed = 9600;
-
- tty_set_operations(tty, &ipoctal_fops);
- res = tty_register_driver(tty);
+ drv->owner = THIS_MODULE;
+ drv->driver_name = KBUILD_MODNAME;
+ drv->name = kasprintf(GFP_KERNEL, KBUILD_MODNAME ".%d.%d.", bus_nr, slot);
+ if (!drv->name) {
+ res = -ENOMEM;
+ goto err_put_driver;
+ }
+ drv->major = 0;
+
+ drv->minor_start = 0;
+ drv->type = TTY_DRIVER_TYPE_SERIAL;
+ drv->subtype = SERIAL_TYPE_NORMAL;
+ drv->init_termios = tty_std_termios;
+ drv->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ drv->init_termios.c_ispeed = 9600;
+ drv->init_termios.c_ospeed = 9600;
+
+ tty_set_operations(drv, &ipoctal_fops);
+ res = tty_register_driver(drv);
if (res) {
dev_err(&ipoctal->dev->dev, "Can't register tty driver.\n");
- put_tty_driver(tty);
- return res;
+ goto err_free_name;
}
/* Save struct tty_driver for use it when uninstalling the device */
- ipoctal->tty_drv = tty;
+ ipoctal->tty_drv = drv;
for (i = 0; i < NR_CHANNELS; i++) {
struct device *tty_dev;
channel = &ipoctal->channel[i];
tty_port_init(&channel->tty_port);
- tty_port_alloc_xmit_buf(&channel->tty_port);
+ res = tty_port_alloc_xmit_buf(&channel->tty_port);
+ if (res)
+ continue;
channel->tty_port.ops = &ipoctal_tty_port_ops;
ipoctal_reset_stats(&channel->stats);
@@ -382,24 +405,40 @@ static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr,
spin_lock_init(&channel->lock);
channel->pointer_read = 0;
channel->pointer_write = 0;
- tty_dev = tty_port_register_device(&channel->tty_port, tty, i, NULL);
+ tty_dev = tty_port_register_device_attr(&channel->tty_port, drv,
+ i, NULL, channel, NULL);
if (IS_ERR(tty_dev)) {
dev_err(&ipoctal->dev->dev, "Failed to register tty device.\n");
+ tty_port_free_xmit_buf(&channel->tty_port);
tty_port_destroy(&channel->tty_port);
continue;
}
- dev_set_drvdata(tty_dev, channel);
+ channel->tty_registered = true;
}
+ /*
+ * IP-OCTAL has different addresses to copy its IRQ vector.
+ * Depending of the carrier these addresses are accesible or not.
+ * More info in the datasheet.
+ */
+ ipoctal->dev->bus->ops->request_irq(ipoctal->dev,
+ ipoctal_irq_handler, ipoctal);
+
return 0;
+
+err_free_name:
+ kfree(drv->name);
+err_put_driver:
+ tty_driver_kref_put(drv);
+
+ return res;
}
-static inline int ipoctal_copy_write_buffer(struct ipoctal_channel *channel,
- const unsigned char *buf,
- int count)
+static inline size_t ipoctal_copy_write_buffer(struct ipoctal_channel *channel,
+ const u8 *buf, size_t count)
{
unsigned long flags;
- int i;
+ size_t i;
unsigned int *pointer_read = &channel->pointer_read;
/* Copy the bytes from the user buffer to the internal one */
@@ -417,11 +456,11 @@ static inline int ipoctal_copy_write_buffer(struct ipoctal_channel *channel,
return i;
}
-static int ipoctal_write_tty(struct tty_struct *tty,
- const unsigned char *buf, int count)
+static ssize_t ipoctal_write_tty(struct tty_struct *tty, const u8 *buf,
+ size_t count)
{
struct ipoctal_channel *channel = tty->driver_data;
- unsigned int char_copied;
+ size_t char_copied;
char_copied = ipoctal_copy_write_buffer(channel, buf, count);
@@ -440,14 +479,14 @@ static int ipoctal_write_tty(struct tty_struct *tty,
return char_copied;
}
-static int ipoctal_write_room(struct tty_struct *tty)
+static unsigned int ipoctal_write_room(struct tty_struct *tty)
{
struct ipoctal_channel *channel = tty->driver_data;
return PAGE_SIZE - channel->nb_bytes;
}
-static int ipoctal_chars_in_buffer(struct tty_struct *tty)
+static unsigned int ipoctal_chars_in_buffer(struct tty_struct *tty)
{
struct ipoctal_channel *channel = tty->driver_data;
@@ -455,7 +494,7 @@ static int ipoctal_chars_in_buffer(struct tty_struct *tty)
}
static void ipoctal_set_termios(struct tty_struct *tty,
- struct ktermios *old_termios)
+ const struct ktermios *old_termios)
{
unsigned int cflag;
unsigned char mr1 = 0;
@@ -467,11 +506,7 @@ static void ipoctal_set_termios(struct tty_struct *tty,
cflag = tty->termios.c_cflag;
/* Disable and reset everything before change the setup */
- iowrite8(CR_DISABLE_RX | CR_DISABLE_TX, &channel->regs->w.cr);
- iowrite8(CR_CMD_RESET_RX, &channel->regs->w.cr);
- iowrite8(CR_CMD_RESET_TX, &channel->regs->w.cr);
- iowrite8(CR_CMD_RESET_ERR_STATUS, &channel->regs->w.cr);
- iowrite8(CR_CMD_RESET_MR, &channel->regs->w.cr);
+ ipoctal_reset_channel(channel);
/* Set Bits per chars */
switch (cflag & CSIZE) {
@@ -528,7 +563,6 @@ static void ipoctal_set_termios(struct tty_struct *tty,
break;
default:
return;
- break;
}
baud = tty_get_baud_rate(tty);
@@ -609,14 +643,8 @@ static void ipoctal_hangup(struct tty_struct *tty)
tty_port_hangup(&channel->tty_port);
- iowrite8(CR_DISABLE_RX | CR_DISABLE_TX, &channel->regs->w.cr);
- channel->rx_enable = 0;
- iowrite8(CR_CMD_RESET_RX, &channel->regs->w.cr);
- iowrite8(CR_CMD_RESET_TX, &channel->regs->w.cr);
- iowrite8(CR_CMD_RESET_ERR_STATUS, &channel->regs->w.cr);
- iowrite8(CR_CMD_RESET_MR, &channel->regs->w.cr);
-
- clear_bit(ASYNCB_INITIALIZED, &channel->tty_port.flags);
+ ipoctal_reset_channel(channel);
+ tty_port_set_initialized(&channel->tty_port, false);
wake_up_interruptible(&channel->tty_port.open_wait);
}
@@ -627,17 +655,22 @@ static void ipoctal_shutdown(struct tty_struct *tty)
if (channel == NULL)
return;
- iowrite8(CR_DISABLE_RX | CR_DISABLE_TX, &channel->regs->w.cr);
- channel->rx_enable = 0;
- iowrite8(CR_CMD_RESET_RX, &channel->regs->w.cr);
- iowrite8(CR_CMD_RESET_TX, &channel->regs->w.cr);
- iowrite8(CR_CMD_RESET_ERR_STATUS, &channel->regs->w.cr);
- iowrite8(CR_CMD_RESET_MR, &channel->regs->w.cr);
- clear_bit(ASYNCB_INITIALIZED, &channel->tty_port.flags);
+ ipoctal_reset_channel(channel);
+ tty_port_set_initialized(&channel->tty_port, false);
+}
+
+static void ipoctal_cleanup(struct tty_struct *tty)
+{
+ struct ipoctal_channel *channel = tty->driver_data;
+ struct ipoctal *ipoctal = chan_to_ipoctal(channel, tty->index);
+
+ /* release the carrier driver */
+ ipack_put_carrier(ipoctal->dev);
}
static const struct tty_operations ipoctal_fops = {
.ioctl = NULL,
+ .install = ipoctal_install,
.open = ipoctal_open,
.close = ipoctal_close,
.write = ipoctal_write_tty,
@@ -647,6 +680,7 @@ static const struct tty_operations ipoctal_fops = {
.get_icount = ipoctal_get_icount,
.hangup = ipoctal_hangup,
.shutdown = ipoctal_shutdown,
+ .cleanup = ipoctal_cleanup,
};
static int ipoctal_probe(struct ipack_device *dev)
@@ -679,13 +713,18 @@ static void __ipoctal_remove(struct ipoctal *ipoctal)
for (i = 0; i < NR_CHANNELS; i++) {
struct ipoctal_channel *channel = &ipoctal->channel[i];
+
+ if (!channel->tty_registered)
+ continue;
+
tty_unregister_device(ipoctal->tty_drv, i);
tty_port_free_xmit_buf(&channel->tty_port);
tty_port_destroy(&channel->tty_port);
}
tty_unregister_driver(ipoctal->tty_drv);
- put_tty_driver(ipoctal->tty_drv);
+ kfree(ipoctal->tty_drv->name);
+ tty_driver_kref_put(ipoctal->tty_drv);
kfree(ipoctal);
}