summaryrefslogtreecommitdiffstats
path: root/drivers/ipack/devices
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-08-30 09:22:00 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-08-30 09:22:00 -0700
commit034dd340b08be1f2f0477ad16131d609f9dbd53c (patch)
tree2536f4b2d7893ccd916b5abdf3c454ad6edc03c2 /drivers/ipack/devices
downloadlinux-stable-034dd340b08be1f2f0477ad16131d609f9dbd53c.tar.gz
linux-stable-034dd340b08be1f2f0477ad16131d609f9dbd53c.zip
Merge tag 'trace-v7.3-2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-tracegrafted
Pull tracing fixes from Steven Rostedt: - Fix error output of boot instance creation failure Currently if a boot instance creation fails, instead of printing out the name of the instance that failed, it prints "(null)". That is because it prints "cur_str" that had already been processed by strsep(). Print the saved name instead. While at it, print the error code of the failure. - Fix use-after-free for same named historgrams Histograms can be named so that they can be used in multiple events. But if the named histogram has a variable attached, the second event that uses the named histogram which duplicates it and needs to free the original after duplication leaves the old variable in place and still visible. If another histogram uses than variable, it will use the stale one which will try to reference the freed duplicate histogram and crash the kernel. Free the duplicate variables along with the duplicated histogram data. - Check return value of kthread_run() in event self test The events self tests uses a kthread for testing but does not check if it succeeded in creating a kthread. If the kthread creation were to fail, the code will still try to call kthread_stop() on the error returned. - Fix race between reading trace_pipe and updating subbuffer size If a user is reading the trace_pipe file at the same time they update the ring buffer sub-buffer size, can cause the trace_pipe read to read stale data. Add trace_access_lock() around updating the ring buffer sub-buffer size. - Fix eventfs_inode on failure path in creation of the events directory In the creation of the "events" directory, if after allocating the eventfs_inode a failure is detected, it calls cleanup_ei() which calls free_ei(). The free_ei() will test if eventfs_inode being freed has no children. It is a bug if it does. But on the failure case of the creation of the "events" directory, the children lists have not yet been initialized and the free will trigger a warning because list_empty() on an uninitialized list returns false. Move the initialization into init_ei() where it makes more sense and makes sure that a created eventfs_inode has its lists initialized upon creation. - Check return value of kthread_run() in ftrace direct sample code The sample code that shows how to use the ftrace direct calls does not test the return of kthread_run() to see if it succeeds. Return a failure if the kthread_run() doesn't succeed. - Clear user events state on fork in case of alloc failure On fork, the child gets a pointer to the parent's user events state. It makes a copy of it then updates the child's pointer to it. But if the allocation fails, the duplication function leaves the child with a pointer to its parent's descriptor. When the child cleans up its data, it will free the parent's descriptor while the parent is still using it. In the duplication function, set the child's user_event_mm to NULL before testing if the allocation succeeded, and when it exits it will not free the parent's descriptor. - Fix retry exhaustion in simple ring buffer reader swap simple_ring_buffer_swap_reader_page() starts with retry set to 8 and post-decrements it only after a failed link replacement. On the final attempt, a successful replacement leaves retry at zero, while a failed replacement leaves it at -1. But the check for success expects the retry value to be non-zero and exits with an error on zero. This is the opposite result. Fix it. - Fail nicely when the remote swap_reader_page() returns an error Currently, if the swap_reader_page() of a remote buffer fails, it triggers a WARN_ON_ONCE() and continues normally. Instead, have it exit with an error and a pr_warn() print instead of a full WARNING. * tag 'trace-v7.3-2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace: ring-buffer: Stop remote reader update when page swap fails tracing: Fix retry exhaustion in simple ring buffer reader swap tracing/user_events: Clear copied tracing state before fork duplication samples/ftrace: Fix kthread_stop() on ERR_PTR in ftrace-direct-multi-modify samples/ftrace: Fix kthread_stop() on ERR_PTR in ftrace-direct-modify eventfs: Initialize ei->children and ei->list in init_ei() tracing: Fix use-after-free in trace_pipe read on sub-buffer order change tracing: Fix crash passing ERR_PTR to kthread_stop() tracing: Fix use-after-free with same-name named triggers tracing: Fix logged instance name on creation failure
Diffstat (limited to 'drivers/ipack/devices')
-rw-r--r--drivers/ipack/devices/Kconfig7
-rw-r--r--drivers/ipack/devices/Makefile2
-rw-r--r--drivers/ipack/devices/ipoctal.c824
-rw-r--r--drivers/ipack/devices/ipoctal.h36
-rw-r--r--drivers/ipack/devices/scc2698.h225
5 files changed, 1094 insertions, 0 deletions
diff --git a/drivers/ipack/devices/Kconfig b/drivers/ipack/devices/Kconfig
new file mode 100644
index 000000000..75a62d170
--- /dev/null
+++ b/drivers/ipack/devices/Kconfig
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config SERIAL_IPOCTAL
+ tristate "IndustryPack IP-OCTAL uart support"
+ depends on IPACK_BUS && TTY
+ help
+ This driver supports the IPOCTAL serial port device for the IndustryPack bus.
+ default n
diff --git a/drivers/ipack/devices/Makefile b/drivers/ipack/devices/Makefile
new file mode 100644
index 000000000..67a7ed684
--- /dev/null
+++ b/drivers/ipack/devices/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_SERIAL_IPOCTAL) += ipoctal.o
diff --git a/drivers/ipack/devices/ipoctal.c b/drivers/ipack/devices/ipoctal.c
new file mode 100644
index 000000000..2169e4b75
--- /dev/null
+++ b/drivers/ipack/devices/ipoctal.c
@@ -0,0 +1,824 @@
+// 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>
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/kref.h>
+#include <linux/rwsem.h>
+#include <linux/sched.h>
+#include <linux/tty.h>
+#include <linux/serial.h>
+#include <linux/tty_flip.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/ipack.h>
+#include "ipoctal.h"
+#include "scc2698.h"
+
+#define IP_OCTAL_ID_SPACE_VECTOR 0x41
+#define IP_OCTAL_NB_BLOCKS 4
+
+static const struct tty_operations ipoctal_fops;
+
+static void ipoctal_release(struct kref *kref);
+
+struct ipoctal_channel {
+ struct ipoctal_stats stats;
+ unsigned int nb_bytes;
+ wait_queue_head_t queue;
+ spinlock_t lock;
+ 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;
+ u8 isr_rx_rdy_mask;
+ u8 isr_tx_rdy_mask;
+ unsigned int rx_enable;
+};
+
+struct ipoctal {
+ struct ipack_device *dev;
+ unsigned int board_id;
+ struct ipoctal_channel channel[NR_CHANNELS];
+ struct tty_driver *tty_drv;
+ u8 __iomem *mem8_space;
+ u8 __iomem *int_space;
+ struct kref kref;
+ struct module *carrier_owner;
+ struct rw_semaphore remove_sem;
+ bool removed;
+};
+
+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;
+ struct ipoctal *ipoctal;
+
+ channel = dev_get_drvdata(tty->dev);
+ ipoctal = chan_to_ipoctal(channel, tty->index);
+
+ guard(rwsem_read)(&ipoctal->remove_sem);
+ if (ipoctal->removed)
+ return -ENODEV;
+
+ /*
+ * Enable RX. TX will be enabled when
+ * there is something to send
+ */
+ iowrite8(CR_ENABLE_RX, &channel->regs->w.cr);
+ channel->rx_enable = 1;
+ return 0;
+}
+
+static int ipoctal_install(struct tty_driver *driver, struct tty_struct *tty)
+{
+ 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;
+
+ kref_get(&ipoctal->kref);
+ 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);
+}
+
+static void ipoctal_reset_stats(struct ipoctal_stats *stats)
+{
+ stats->tx = 0;
+ stats->rx = 0;
+ stats->rcv_break = 0;
+ stats->framing_err = 0;
+ stats->overrun_err = 0;
+ stats->parity_err = 0;
+}
+
+static void ipoctal_free_channel(struct ipoctal_channel *channel)
+{
+ ipoctal_reset_stats(&channel->stats);
+ channel->pointer_read = 0;
+ channel->pointer_write = 0;
+ channel->nb_bytes = 0;
+}
+
+static void ipoctal_close(struct tty_struct *tty, struct file *filp)
+{
+ struct ipoctal_channel *channel = tty->driver_data;
+
+ tty_port_close(&channel->tty_port, tty, filp);
+ ipoctal_free_channel(channel);
+}
+
+static int ipoctal_get_icount(struct tty_struct *tty,
+ struct serial_icounter_struct *icount)
+{
+ struct ipoctal_channel *channel = tty->driver_data;
+
+ icount->cts = 0;
+ icount->dsr = 0;
+ icount->rng = 0;
+ icount->dcd = 0;
+ icount->rx = channel->stats.rx;
+ icount->tx = channel->stats.tx;
+ icount->frame = channel->stats.framing_err;
+ icount->parity = channel->stats.parity_err;
+ icount->brk = channel->stats.rcv_break;
+ return 0;
+}
+
+static void ipoctal_irq_rx(struct ipoctal_channel *channel, u8 sr)
+{
+ struct tty_port *port = &channel->tty_port;
+ u8 isr, value, flag;
+
+ do {
+ value = ioread8(&channel->regs->r.rhr);
+ flag = TTY_NORMAL;
+ /* Error: count statistics */
+ if (sr & SR_ERROR) {
+ iowrite8(CR_CMD_RESET_ERR_STATUS, &channel->regs->w.cr);
+
+ if (sr & SR_OVERRUN_ERROR) {
+ channel->stats.overrun_err++;
+ /* Overrun doesn't affect the current character*/
+ tty_insert_flip_char(port, 0, TTY_OVERRUN);
+ }
+ if (sr & SR_PARITY_ERROR) {
+ channel->stats.parity_err++;
+ flag = TTY_PARITY;
+ }
+ if (sr & SR_FRAMING_ERROR) {
+ channel->stats.framing_err++;
+ flag = TTY_FRAME;
+ }
+ if (sr & SR_RECEIVED_BREAK) {
+ channel->stats.rcv_break++;
+ flag = TTY_BREAK;
+ }
+ }
+ tty_insert_flip_char(port, value, flag);
+
+ /* Check if there are more characters in RX FIFO
+ * If there are more, the isr register for this channel
+ * has enabled the RxRDY|FFULL bit.
+ */
+ isr = ioread8(&channel->block_regs->r.isr);
+ sr = ioread8(&channel->regs->r.sr);
+ } while (isr & channel->isr_rx_rdy_mask);
+
+ tty_flip_buffer_push(port);
+}
+
+static void ipoctal_irq_tx(struct ipoctal_channel *channel)
+{
+ 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;
+
+ /* 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.
+ * Half-duplex. */
+ if (channel->board_id == IPACK1_DEVICE_ID_SBS_OCTAL_485) {
+ iowrite8(CR_CMD_NEGATE_RTSN, &channel->regs->w.cr);
+ iowrite8(CR_ENABLE_RX, &channel->regs->w.cr);
+ channel->rx_enable = 1;
+ }
+ }
+
+ /* RX data */
+ if ((isr & channel->isr_rx_rdy_mask) && (sr & SR_RX_READY))
+ ipoctal_irq_rx(channel, sr);
+
+ /* TX of each character */
+ if ((isr & channel->isr_tx_rdy_mask) && (sr & SR_TX_READY))
+ ipoctal_irq_tx(channel);
+}
+
+static irqreturn_t ipoctal_irq_handler(void *arg)
+{
+ unsigned int i;
+ struct ipoctal *ipoctal = arg;
+
+ /* Clear the IPack device interrupt */
+ readw(ipoctal->int_space + ACK_INT_REQ0);
+ readw(ipoctal->int_space + ACK_INT_REQ1);
+
+ /* Check all channels */
+ for (i = 0; i < NR_CHANNELS; i++)
+ ipoctal_irq_channel(&ipoctal->channel[i]);
+
+ return IRQ_HANDLED;
+}
+
+static const struct tty_port_operations ipoctal_tty_port_ops = {
+ .dtr_rts = NULL,
+ .activate = ipoctal_port_activate,
+};
+
+static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr,
+ unsigned int slot)
+{
+ int res;
+ int i;
+ struct tty_driver *drv;
+ struct ipoctal_channel *channel;
+ struct ipack_region *region;
+ void __iomem *addr;
+ union scc2698_channel __iomem *chan_regs;
+ union scc2698_block __iomem *block_regs;
+
+ ipoctal->board_id = ipoctal->dev->id_device;
+
+ region = &ipoctal->dev->region[IPACK_IO_SPACE];
+ addr = devm_ioremap(&ipoctal->dev->dev,
+ region->start, region->size);
+ if (!addr) {
+ dev_err(&ipoctal->dev->dev,
+ "Unable to map slot [%d:%d] IO space!\n",
+ bus_nr, slot);
+ return -EADDRNOTAVAIL;
+ }
+ /* Save the virtual address to access the registers easily */
+ chan_regs =
+ (union scc2698_channel __iomem *) addr;
+ block_regs =
+ (union scc2698_block __iomem *) addr;
+
+ region = &ipoctal->dev->region[IPACK_INT_SPACE];
+ ipoctal->int_space =
+ devm_ioremap(&ipoctal->dev->dev,
+ region->start, region->size);
+ if (!ipoctal->int_space) {
+ dev_err(&ipoctal->dev->dev,
+ "Unable to map slot [%d:%d] INT space!\n",
+ bus_nr, slot);
+ return -EADDRNOTAVAIL;
+ }
+
+ region = &ipoctal->dev->region[IPACK_MEM8_SPACE];
+ ipoctal->mem8_space =
+ devm_ioremap(&ipoctal->dev->dev,
+ region->start, 0x8000);
+ if (!ipoctal->mem8_space) {
+ dev_err(&ipoctal->dev->dev,
+ "Unable to map slot [%d:%d] MEM8 space!\n",
+ bus_nr, slot);
+ return -EADDRNOTAVAIL;
+ }
+
+
+ /* Disable RX and TX before touching anything */
+ for (i = 0; i < NR_CHANNELS ; i++) {
+ struct ipoctal_channel *channel = &ipoctal->channel[i];
+ channel->regs = chan_regs + i;
+ channel->block_regs = block_regs + (i >> 1);
+ channel->board_id = ipoctal->board_id;
+ if (i & 1) {
+ channel->isr_tx_rdy_mask = ISR_TxRDY_B;
+ channel->isr_rx_rdy_mask = ISR_RxRDY_FFULL_B;
+ } else {
+ channel->isr_tx_rdy_mask = ISR_TxRDY_A;
+ channel->isr_rx_rdy_mask = ISR_RxRDY_FFULL_A;
+ }
+
+ 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 */
+ iowrite8(TX_CLK_9600 | RX_CLK_9600, &channel->regs->w.csr);
+ }
+
+ for (i = 0; i < IP_OCTAL_NB_BLOCKS; i++) {
+ iowrite8(ACR_BRG_SET2, &block_regs[i].w.acr);
+ iowrite8(OPCR_MPP_OUTPUT | OPCR_MPOa_RTSN | OPCR_MPOb_RTSN,
+ &block_regs[i].w.opcr);
+ iowrite8(IMR_TxRDY_A | IMR_RxRDY_FFULL_A | IMR_DELTA_BREAK_A |
+ IMR_TxRDY_B | IMR_RxRDY_FFULL_B | IMR_DELTA_BREAK_B,
+ &block_regs[i].w.imr);
+ }
+
+ /* Dummy write */
+ iowrite8(1, ipoctal->mem8_space + 1);
+
+ /* Register the TTY device */
+
+ /* Each IP-OCTAL channel is a TTY port */
+ 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 */
+ 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");
+ goto err_free_name;
+ }
+
+ /* Save struct tty_driver for use it when uninstalling the device */
+ 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);
+ 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);
+ channel->nb_bytes = 0;
+ spin_lock_init(&channel->lock);
+ channel->pointer_read = 0;
+ channel->pointer_write = 0;
+ 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;
+ }
+ 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 size_t ipoctal_copy_write_buffer(struct ipoctal_channel *channel,
+ const u8 *buf, size_t count)
+{
+ unsigned long flags;
+ size_t i;
+ unsigned int *pointer_read = &channel->pointer_read;
+
+ /* Copy the bytes from the user buffer to the internal one */
+ for (i = 0; i < count; i++) {
+ if (i <= (PAGE_SIZE - channel->nb_bytes)) {
+ spin_lock_irqsave(&channel->lock, flags);
+ channel->tty_port.xmit_buf[*pointer_read] = buf[i];
+ *pointer_read = (*pointer_read + 1) % PAGE_SIZE;
+ channel->nb_bytes++;
+ spin_unlock_irqrestore(&channel->lock, flags);
+ } else {
+ break;
+ }
+ }
+ return i;
+}
+
+static ssize_t ipoctal_write_tty(struct tty_struct *tty, const u8 *buf,
+ size_t count)
+{
+ struct ipoctal_channel *channel = tty->driver_data;
+ struct ipoctal *ipoctal = chan_to_ipoctal(channel, tty->index);
+ size_t char_copied;
+
+ guard(rwsem_read)(&ipoctal->remove_sem);
+ if (ipoctal->removed || !channel->tty_port.xmit_buf)
+ return 0;
+
+ char_copied = ipoctal_copy_write_buffer(channel, buf, count);
+
+ /* As the IP-OCTAL 485 only supports half duplex, do it manually */
+ if (channel->board_id == IPACK1_DEVICE_ID_SBS_OCTAL_485) {
+ iowrite8(CR_DISABLE_RX, &channel->regs->w.cr);
+ channel->rx_enable = 0;
+ iowrite8(CR_CMD_ASSERT_RTSN, &channel->regs->w.cr);
+ }
+
+ /*
+ * Send a packet and then disable TX to avoid failure after several send
+ * operations
+ */
+ iowrite8(CR_ENABLE_TX, &channel->regs->w.cr);
+ return char_copied;
+}
+
+static unsigned int ipoctal_write_room(struct tty_struct *tty)
+{
+ struct ipoctal_channel *channel = tty->driver_data;
+
+ return PAGE_SIZE - channel->nb_bytes;
+}
+
+static unsigned int ipoctal_chars_in_buffer(struct tty_struct *tty)
+{
+ struct ipoctal_channel *channel = tty->driver_data;
+
+ return channel->nb_bytes;
+}
+
+static void ipoctal_set_termios(struct tty_struct *tty,
+ const struct ktermios *old_termios)
+{
+ unsigned int cflag;
+ unsigned char mr1 = 0;
+ unsigned char mr2 = 0;
+ unsigned char csr = 0;
+ struct ipoctal_channel *channel = tty->driver_data;
+ struct ipoctal *ipoctal = chan_to_ipoctal(channel, tty->index);
+ speed_t baud;
+
+ guard(rwsem_read)(&ipoctal->remove_sem);
+ if (ipoctal->removed)
+ return;
+
+ cflag = tty->termios.c_cflag;
+
+ /* Disable and reset everything before change the setup */
+ ipoctal_reset_channel(channel);
+
+ /* Set Bits per chars */
+ switch (cflag & CSIZE) {
+ case CS6:
+ mr1 |= MR1_CHRL_6_BITS;
+ break;
+ case CS7:
+ mr1 |= MR1_CHRL_7_BITS;
+ break;
+ case CS8:
+ default:
+ mr1 |= MR1_CHRL_8_BITS;
+ /* By default, select CS8 */
+ tty->termios.c_cflag = (cflag & ~CSIZE) | CS8;
+ break;
+ }
+
+ /* Set Parity */
+ if (cflag & PARENB)
+ if (cflag & PARODD)
+ mr1 |= MR1_PARITY_ON | MR1_PARITY_ODD;
+ else
+ mr1 |= MR1_PARITY_ON | MR1_PARITY_EVEN;
+ else
+ mr1 |= MR1_PARITY_OFF;
+
+ /* Mark or space parity is not supported */
+ tty->termios.c_cflag &= ~CMSPAR;
+
+ /* Set stop bits */
+ if (cflag & CSTOPB)
+ mr2 |= MR2_STOP_BITS_LENGTH_2;
+ else
+ mr2 |= MR2_STOP_BITS_LENGTH_1;
+
+ /* Set the flow control */
+ switch (channel->board_id) {
+ case IPACK1_DEVICE_ID_SBS_OCTAL_232:
+ if (cflag & CRTSCTS) {
+ mr1 |= MR1_RxRTS_CONTROL_ON;
+ mr2 |= MR2_TxRTS_CONTROL_OFF | MR2_CTS_ENABLE_TX_ON;
+ } else {
+ mr1 |= MR1_RxRTS_CONTROL_OFF;
+ mr2 |= MR2_TxRTS_CONTROL_OFF | MR2_CTS_ENABLE_TX_OFF;
+ }
+ break;
+ case IPACK1_DEVICE_ID_SBS_OCTAL_422:
+ mr1 |= MR1_RxRTS_CONTROL_OFF;
+ mr2 |= MR2_TxRTS_CONTROL_OFF | MR2_CTS_ENABLE_TX_OFF;
+ break;
+ case IPACK1_DEVICE_ID_SBS_OCTAL_485:
+ mr1 |= MR1_RxRTS_CONTROL_OFF;
+ mr2 |= MR2_TxRTS_CONTROL_ON | MR2_CTS_ENABLE_TX_OFF;
+ break;
+ default:
+ return;
+ }
+
+ baud = tty_get_baud_rate(tty);
+ tty_termios_encode_baud_rate(&tty->termios, baud, baud);
+
+ /* Set baud rate */
+ switch (baud) {
+ case 75:
+ csr |= TX_CLK_75 | RX_CLK_75;
+ break;
+ case 110:
+ csr |= TX_CLK_110 | RX_CLK_110;
+ break;
+ case 150:
+ csr |= TX_CLK_150 | RX_CLK_150;
+ break;
+ case 300:
+ csr |= TX_CLK_300 | RX_CLK_300;
+ break;
+ case 600:
+ csr |= TX_CLK_600 | RX_CLK_600;
+ break;
+ case 1200:
+ csr |= TX_CLK_1200 | RX_CLK_1200;
+ break;
+ case 1800:
+ csr |= TX_CLK_1800 | RX_CLK_1800;
+ break;
+ case 2000:
+ csr |= TX_CLK_2000 | RX_CLK_2000;
+ break;
+ case 2400:
+ csr |= TX_CLK_2400 | RX_CLK_2400;
+ break;
+ case 4800:
+ csr |= TX_CLK_4800 | RX_CLK_4800;
+ break;
+ case 9600:
+ csr |= TX_CLK_9600 | RX_CLK_9600;
+ break;
+ case 19200:
+ csr |= TX_CLK_19200 | RX_CLK_19200;
+ break;
+ case 38400:
+ default:
+ csr |= TX_CLK_38400 | RX_CLK_38400;
+ /* In case of default, we establish 38400 bps */
+ tty_termios_encode_baud_rate(&tty->termios, 38400, 38400);
+ break;
+ }
+
+ mr1 |= MR1_ERROR_CHAR;
+ mr1 |= MR1_RxINT_RxRDY;
+
+ /* Write the control registers */
+ iowrite8(mr1, &channel->regs->w.mr);
+ iowrite8(mr2, &channel->regs->w.mr);
+ iowrite8(csr, &channel->regs->w.csr);
+
+ /* Enable again the RX, if it was before */
+ if (channel->rx_enable)
+ iowrite8(CR_ENABLE_RX, &channel->regs->w.cr);
+}
+
+static void ipoctal_hangup(struct tty_struct *tty)
+{
+ unsigned long flags;
+ struct ipoctal_channel *channel = tty->driver_data;
+ struct ipoctal *ipoctal;
+
+ if (channel == NULL)
+ return;
+
+ ipoctal = chan_to_ipoctal(channel, tty->index);
+ guard(rwsem_read)(&ipoctal->remove_sem);
+ if (ipoctal->removed)
+ return;
+
+ spin_lock_irqsave(&channel->lock, flags);
+ channel->nb_bytes = 0;
+ channel->pointer_read = 0;
+ channel->pointer_write = 0;
+ spin_unlock_irqrestore(&channel->lock, flags);
+
+ tty_port_hangup(&channel->tty_port);
+
+ ipoctal_reset_channel(channel);
+ tty_port_set_initialized(&channel->tty_port, false);
+ wake_up_interruptible(&channel->tty_port.open_wait);
+}
+
+static void ipoctal_shutdown(struct tty_struct *tty)
+{
+ struct ipoctal_channel *channel = tty->driver_data;
+ struct ipoctal *ipoctal;
+
+ if (channel == NULL)
+ return;
+
+ ipoctal = chan_to_ipoctal(channel, tty->index);
+ guard(rwsem_read)(&ipoctal->remove_sem);
+ if (ipoctal->removed)
+ return;
+
+ 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 via cached owner */
+ module_put(ipoctal->carrier_owner);
+ kref_put(&ipoctal->kref, ipoctal_release);
+}
+
+static const struct tty_operations ipoctal_fops = {
+ .ioctl = NULL,
+ .install = ipoctal_install,
+ .open = ipoctal_open,
+ .close = ipoctal_close,
+ .write = ipoctal_write_tty,
+ .set_termios = ipoctal_set_termios,
+ .write_room = ipoctal_write_room,
+ .chars_in_buffer = ipoctal_chars_in_buffer,
+ .get_icount = ipoctal_get_icount,
+ .hangup = ipoctal_hangup,
+ .shutdown = ipoctal_shutdown,
+ .cleanup = ipoctal_cleanup,
+};
+
+static void ipoctal_release(struct kref *kref)
+{
+ struct ipoctal *ipoctal = container_of(kref, struct ipoctal, kref);
+
+ kfree(ipoctal);
+}
+
+static int ipoctal_probe(struct ipack_device *dev)
+{
+ int res;
+ struct ipoctal *ipoctal;
+
+ ipoctal = kzalloc_obj(struct ipoctal);
+ if (ipoctal == NULL)
+ return -ENOMEM;
+
+ kref_init(&ipoctal->kref);
+ init_rwsem(&ipoctal->remove_sem);
+
+ ipoctal->dev = dev;
+ ipoctal->carrier_owner = dev->bus->owner;
+ res = ipoctal_inst_slot(ipoctal, dev->bus->bus_nr, dev->slot);
+ if (res)
+ goto out_uninst;
+
+ dev_set_drvdata(&dev->dev, ipoctal);
+ return 0;
+
+out_uninst:
+ kref_put(&ipoctal->kref, ipoctal_release);
+ return res;
+}
+
+static void __ipoctal_remove(struct ipoctal *ipoctal)
+{
+ int i;
+
+ scoped_guard(rwsem_write, &ipoctal->remove_sem)
+ ipoctal->removed = true;
+
+ ipoctal->dev->bus->ops->free_irq(ipoctal->dev);
+
+ 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);
+ kfree(ipoctal->tty_drv->name);
+ tty_driver_kref_put(ipoctal->tty_drv);
+ kref_put(&ipoctal->kref, ipoctal_release);
+}
+
+static void ipoctal_remove(struct ipack_device *idev)
+{
+ __ipoctal_remove(dev_get_drvdata(&idev->dev));
+}
+
+static DEFINE_IPACK_DEVICE_TABLE(ipoctal_ids) = {
+ { IPACK_DEVICE(IPACK_ID_VERSION_1, IPACK1_VENDOR_ID_SBS,
+ IPACK1_DEVICE_ID_SBS_OCTAL_232) },
+ { IPACK_DEVICE(IPACK_ID_VERSION_1, IPACK1_VENDOR_ID_SBS,
+ IPACK1_DEVICE_ID_SBS_OCTAL_422) },
+ { IPACK_DEVICE(IPACK_ID_VERSION_1, IPACK1_VENDOR_ID_SBS,
+ IPACK1_DEVICE_ID_SBS_OCTAL_485) },
+ { 0, },
+};
+
+MODULE_DEVICE_TABLE(ipack, ipoctal_ids);
+
+static const struct ipack_driver_ops ipoctal_drv_ops = {
+ .probe = ipoctal_probe,
+ .remove = ipoctal_remove,
+};
+
+static struct ipack_driver driver = {
+ .ops = &ipoctal_drv_ops,
+ .id_table = ipoctal_ids,
+};
+
+static int __init ipoctal_init(void)
+{
+ return ipack_driver_register(&driver, THIS_MODULE, KBUILD_MODNAME);
+}
+
+static void __exit ipoctal_exit(void)
+{
+ ipack_driver_unregister(&driver);
+}
+
+MODULE_DESCRIPTION("IP-Octal 232, 422 and 485 device driver");
+MODULE_LICENSE("GPL");
+
+module_init(ipoctal_init);
+module_exit(ipoctal_exit);
diff --git a/drivers/ipack/devices/ipoctal.h b/drivers/ipack/devices/ipoctal.h
new file mode 100644
index 000000000..773dc41bd
--- /dev/null
+++ b/drivers/ipack/devices/ipoctal.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * driver for the IPOCTAL boards
+ *
+ * Copyright (C) 2009-2012 CERN (www.cern.ch)
+ * Author: Nicolas Serafini, EIC2 SA
+ * Author: Samuel Iglesias Gonsalvez <siglesias@igalia.com>
+ */
+
+#ifndef _IPOCTAL_H_
+#define _IPOCTAL_H_
+
+#define NR_CHANNELS 8
+#define IPOCTAL_MAX_BOARDS 16
+#define MAX_DEVICES (NR_CHANNELS * IPOCTAL_MAX_BOARDS)
+
+/**
+ * struct ipoctal_stats -- Stats since last reset
+ *
+ * @tx: Number of transmitted bytes
+ * @rx: Number of received bytes
+ * @overrun: Number of overrun errors
+ * @parity_err: Number of parity errors
+ * @framing_err: Number of framing errors
+ * @rcv_break: Number of break received
+ */
+struct ipoctal_stats {
+ unsigned long tx;
+ unsigned long rx;
+ unsigned long overrun_err;
+ unsigned long parity_err;
+ unsigned long framing_err;
+ unsigned long rcv_break;
+};
+
+#endif /* _IPOCTAL_H_ */
diff --git a/drivers/ipack/devices/scc2698.h b/drivers/ipack/devices/scc2698.h
new file mode 100644
index 000000000..9649576ce
--- /dev/null
+++ b/drivers/ipack/devices/scc2698.h
@@ -0,0 +1,225 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * scc2698.h
+ *
+ * driver for the IPOCTAL boards
+ *
+ * Copyright (C) 2009-2012 CERN (www.cern.ch)
+ * Author: Nicolas Serafini, EIC2 SA
+ * Author: Samuel Iglesias Gonsalvez <siglesias@igalia.com>
+ */
+
+#ifndef SCC2698_H_
+#define SCC2698_H_
+
+/*
+ * union scc2698_channel - Channel access to scc2698 IO
+ *
+ * dn value are only spacer.
+ *
+ */
+union scc2698_channel {
+ struct {
+ u8 d0, mr; /* Mode register 1/2*/
+ u8 d1, sr; /* Status register */
+ u8 d2, r1; /* reserved */
+ u8 d3, rhr; /* Receive holding register (R) */
+ u8 junk[8]; /* other crap for block control */
+ } __packed r; /* Read access */
+ struct {
+ u8 d0, mr; /* Mode register 1/2 */
+ u8 d1, csr; /* Clock select register */
+ u8 d2, cr; /* Command register */
+ u8 d3, thr; /* Transmit holding register */
+ u8 junk[8]; /* other crap for block control */
+ } __packed w; /* Write access */
+};
+
+/*
+ * union scc2698_block - Block access to scc2698 IO
+ *
+ * The scc2698 contain 4 block.
+ * Each block containt two channel a and b.
+ * dn value are only spacer.
+ *
+ */
+union scc2698_block {
+ struct {
+ u8 d0, mra; /* Mode register 1/2 (a) */
+ u8 d1, sra; /* Status register (a) */
+ u8 d2, r1; /* reserved */
+ u8 d3, rhra; /* Receive holding register (a) */
+ u8 d4, ipcr; /* Input port change register of block */
+ u8 d5, isr; /* Interrupt status register of block */
+ u8 d6, ctur; /* Counter timer upper register of block */
+ u8 d7, ctlr; /* Counter timer lower register of block */
+ u8 d8, mrb; /* Mode register 1/2 (b) */
+ u8 d9, srb; /* Status register (b) */
+ u8 da, r2; /* reserved */
+ u8 db, rhrb; /* Receive holding register (b) */
+ u8 dc, r3; /* reserved */
+ u8 dd, ip; /* Input port register of block */
+ u8 de, ctg; /* Start counter timer of block */
+ u8 df, cts; /* Stop counter timer of block */
+ } __packed r; /* Read access */
+ struct {
+ u8 d0, mra; /* Mode register 1/2 (a) */
+ u8 d1, csra; /* Clock select register (a) */
+ u8 d2, cra; /* Command register (a) */
+ u8 d3, thra; /* Transmit holding register (a) */
+ u8 d4, acr; /* Auxiliary control register of block */
+ u8 d5, imr; /* Interrupt mask register of block */
+ u8 d6, ctu; /* Counter timer upper register of block */
+ u8 d7, ctl; /* Counter timer lower register of block */
+ u8 d8, mrb; /* Mode register 1/2 (b) */
+ u8 d9, csrb; /* Clock select register (a) */
+ u8 da, crb; /* Command register (b) */
+ u8 db, thrb; /* Transmit holding register (b) */
+ u8 dc, r1; /* reserved */
+ u8 dd, opcr; /* Output port configuration register of block */
+ u8 de, r2; /* reserved */
+ u8 df, r3; /* reserved */
+ } __packed w; /* Write access */
+};
+
+#define MR1_CHRL_5_BITS (0x0 << 0)
+#define MR1_CHRL_6_BITS (0x1 << 0)
+#define MR1_CHRL_7_BITS (0x2 << 0)
+#define MR1_CHRL_8_BITS (0x3 << 0)
+#define MR1_PARITY_EVEN (0x1 << 2)
+#define MR1_PARITY_ODD (0x0 << 2)
+#define MR1_PARITY_ON (0x0 << 3)
+#define MR1_PARITY_FORCE (0x1 << 3)
+#define MR1_PARITY_OFF (0x2 << 3)
+#define MR1_PARITY_SPECIAL (0x3 << 3)
+#define MR1_ERROR_CHAR (0x0 << 5)
+#define MR1_ERROR_BLOCK (0x1 << 5)
+#define MR1_RxINT_RxRDY (0x0 << 6)
+#define MR1_RxINT_FFULL (0x1 << 6)
+#define MR1_RxRTS_CONTROL_ON (0x1 << 7)
+#define MR1_RxRTS_CONTROL_OFF (0x0 << 7)
+
+#define MR2_STOP_BITS_LENGTH_1 (0x7 << 0)
+#define MR2_STOP_BITS_LENGTH_2 (0xF << 0)
+#define MR2_CTS_ENABLE_TX_ON (0x1 << 4)
+#define MR2_CTS_ENABLE_TX_OFF (0x0 << 4)
+#define MR2_TxRTS_CONTROL_ON (0x1 << 5)
+#define MR2_TxRTS_CONTROL_OFF (0x0 << 5)
+#define MR2_CH_MODE_NORMAL (0x0 << 6)
+#define MR2_CH_MODE_ECHO (0x1 << 6)
+#define MR2_CH_MODE_LOCAL (0x2 << 6)
+#define MR2_CH_MODE_REMOTE (0x3 << 6)
+
+#define CR_ENABLE_RX (0x1 << 0)
+#define CR_DISABLE_RX (0x1 << 1)
+#define CR_ENABLE_TX (0x1 << 2)
+#define CR_DISABLE_TX (0x1 << 3)
+#define CR_CMD_RESET_MR (0x1 << 4)
+#define CR_CMD_RESET_RX (0x2 << 4)
+#define CR_CMD_RESET_TX (0x3 << 4)
+#define CR_CMD_RESET_ERR_STATUS (0x4 << 4)
+#define CR_CMD_RESET_BREAK_CHANGE (0x5 << 4)
+#define CR_CMD_START_BREAK (0x6 << 4)
+#define CR_CMD_STOP_BREAK (0x7 << 4)
+#define CR_CMD_ASSERT_RTSN (0x8 << 4)
+#define CR_CMD_NEGATE_RTSN (0x9 << 4)
+#define CR_CMD_SET_TIMEOUT_MODE (0xA << 4)
+#define CR_CMD_DISABLE_TIMEOUT_MODE (0xC << 4)
+
+#define SR_RX_READY (0x1 << 0)
+#define SR_FIFO_FULL (0x1 << 1)
+#define SR_TX_READY (0x1 << 2)
+#define SR_TX_EMPTY (0x1 << 3)
+#define SR_OVERRUN_ERROR (0x1 << 4)
+#define SR_PARITY_ERROR (0x1 << 5)
+#define SR_FRAMING_ERROR (0x1 << 6)
+#define SR_RECEIVED_BREAK (0x1 << 7)
+
+#define SR_ERROR (0xF0)
+
+#define ACR_DELTA_IP0_IRQ_EN (0x1 << 0)
+#define ACR_DELTA_IP1_IRQ_EN (0x1 << 1)
+#define ACR_DELTA_IP2_IRQ_EN (0x1 << 2)
+#define ACR_DELTA_IP3_IRQ_EN (0x1 << 3)
+#define ACR_CT_Mask (0x7 << 4)
+#define ACR_CExt (0x0 << 4)
+#define ACR_CTxCA (0x1 << 4)
+#define ACR_CTxCB (0x2 << 4)
+#define ACR_CClk16 (0x3 << 4)
+#define ACR_TExt (0x4 << 4)
+#define ACR_TExt16 (0x5 << 4)
+#define ACR_TClk (0x6 << 4)
+#define ACR_TClk16 (0x7 << 4)
+#define ACR_BRG_SET1 (0x0 << 7)
+#define ACR_BRG_SET2 (0x1 << 7)
+
+#define TX_CLK_75 (0x0 << 0)
+#define TX_CLK_110 (0x1 << 0)
+#define TX_CLK_38400 (0x2 << 0)
+#define TX_CLK_150 (0x3 << 0)
+#define TX_CLK_300 (0x4 << 0)
+#define TX_CLK_600 (0x5 << 0)
+#define TX_CLK_1200 (0x6 << 0)
+#define TX_CLK_2000 (0x7 << 0)
+#define TX_CLK_2400 (0x8 << 0)
+#define TX_CLK_4800 (0x9 << 0)
+#define TX_CLK_1800 (0xA << 0)
+#define TX_CLK_9600 (0xB << 0)
+#define TX_CLK_19200 (0xC << 0)
+#define RX_CLK_75 (0x0 << 4)
+#define RX_CLK_110 (0x1 << 4)
+#define RX_CLK_38400 (0x2 << 4)
+#define RX_CLK_150 (0x3 << 4)
+#define RX_CLK_300 (0x4 << 4)
+#define RX_CLK_600 (0x5 << 4)
+#define RX_CLK_1200 (0x6 << 4)
+#define RX_CLK_2000 (0x7 << 4)
+#define RX_CLK_2400 (0x8 << 4)
+#define RX_CLK_4800 (0x9 << 4)
+#define RX_CLK_1800 (0xA << 4)
+#define RX_CLK_9600 (0xB << 4)
+#define RX_CLK_19200 (0xC << 4)
+
+#define OPCR_MPOa_RTSN (0x0 << 0)
+#define OPCR_MPOa_C_TO (0x1 << 0)
+#define OPCR_MPOa_TxC1X (0x2 << 0)
+#define OPCR_MPOa_TxC16X (0x3 << 0)
+#define OPCR_MPOa_RxC1X (0x4 << 0)
+#define OPCR_MPOa_RxC16X (0x5 << 0)
+#define OPCR_MPOa_TxRDY (0x6 << 0)
+#define OPCR_MPOa_RxRDY_FF (0x7 << 0)
+
+#define OPCR_MPOb_RTSN (0x0 << 4)
+#define OPCR_MPOb_C_TO (0x1 << 4)
+#define OPCR_MPOb_TxC1X (0x2 << 4)
+#define OPCR_MPOb_TxC16X (0x3 << 4)
+#define OPCR_MPOb_RxC1X (0x4 << 4)
+#define OPCR_MPOb_RxC16X (0x5 << 4)
+#define OPCR_MPOb_TxRDY (0x6 << 4)
+#define OPCR_MPOb_RxRDY_FF (0x7 << 4)
+
+#define OPCR_MPP_INPUT (0x0 << 7)
+#define OPCR_MPP_OUTPUT (0x1 << 7)
+
+#define IMR_TxRDY_A (0x1 << 0)
+#define IMR_RxRDY_FFULL_A (0x1 << 1)
+#define IMR_DELTA_BREAK_A (0x1 << 2)
+#define IMR_COUNTER_READY (0x1 << 3)
+#define IMR_TxRDY_B (0x1 << 4)
+#define IMR_RxRDY_FFULL_B (0x1 << 5)
+#define IMR_DELTA_BREAK_B (0x1 << 6)
+#define IMR_INPUT_PORT_CHANGE (0x1 << 7)
+
+#define ISR_TxRDY_A (0x1 << 0)
+#define ISR_RxRDY_FFULL_A (0x1 << 1)
+#define ISR_DELTA_BREAK_A (0x1 << 2)
+#define ISR_COUNTER_READY (0x1 << 3)
+#define ISR_TxRDY_B (0x1 << 4)
+#define ISR_RxRDY_FFULL_B (0x1 << 5)
+#define ISR_DELTA_BREAK_B (0x1 << 6)
+#define ISR_INPUT_PORT_CHANGE (0x1 << 7)
+
+#define ACK_INT_REQ0 0
+#define ACK_INT_REQ1 2
+
+#endif /* SCC2698_H_ */