From: Greg Howard Here's a cleaned-up version of altix-system-controller-driver.patch. Many thanks to everybody who offered suggestions and help. Signed-off-by: Greg Howard Signed-off-by: Andrew Morton --- 25-akpm/drivers/char/Kconfig | 7 25-akpm/drivers/char/Makefile | 1 25-akpm/drivers/char/snsc.c | 493 +++++++++++++++++++++++++++++++++++ 25-akpm/drivers/char/snsc.h | 50 +++ 25-akpm/drivers/serial/sn_console.c | 3 25-akpm/include/asm-ia64/sn/sn_sal.h | 142 ++++++++++ 6 files changed, 694 insertions(+), 2 deletions(-) diff -puN drivers/char/Kconfig~altix-system-controller-communication-driver drivers/char/Kconfig --- 25/drivers/char/Kconfig~altix-system-controller-communication-driver 2004-07-31 17:06:30.605647464 -0700 +++ 25-akpm/drivers/char/Kconfig 2004-07-31 17:06:30.615645944 -0700 @@ -424,6 +424,13 @@ config A2232 will also be built as a module. This has to be loaded before "ser_a2232". If you want to do this, answer M here. +config SGI_SNSC + bool "SGI Altix system controller communication support" + help + If you have an SGI Altix and you want to enable system + controller communication from user space (you want this!), + say Y. Otherwise, say N. + source "drivers/serial/Kconfig" config UNIX98_PTYS diff -puN drivers/char/Makefile~altix-system-controller-communication-driver drivers/char/Makefile --- 25/drivers/char/Makefile~altix-system-controller-communication-driver 2004-07-31 17:06:30.606647312 -0700 +++ 25-akpm/drivers/char/Makefile 2004-07-31 17:06:30.616645792 -0700 @@ -41,6 +41,7 @@ obj-$(CONFIG_SX) += sx.o generic_serial obj-$(CONFIG_RIO) += rio/ generic_serial.o obj-$(CONFIG_HVC_CONSOLE) += hvc_console.o obj-$(CONFIG_RAW_DRIVER) += raw.o +obj-$(CONFIG_SGI_SNSC) += snsc.o obj-$(CONFIG_VIOCONS) += viocons.o obj-$(CONFIG_VIOTAPE) += viotape.o obj-$(CONFIG_HVCS) += hvcs.o diff -puN /dev/null drivers/char/snsc.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/char/snsc.c 2004-07-31 17:06:30.618645488 -0700 @@ -0,0 +1,493 @@ +/* + * SN Platform system controller communication support + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2004 Silicon Graphics, Inc. All rights reserved. + */ + +/* + * System controller communication driver + * + * This driver allows a user process to communicate with the system + * controller (a.k.a. "IRouter") network in an SGI SN system. + */ + +#include "snsc.h" +#include +#include +#include +#include +#include +#include +#include +#include + + +#define SYSCTL_BASENAME "snsc" + +#define SCDRV_BUFSZ 2048 + +#ifdef SCDRV_DEBUG +#define DPRINTF(x...) printk(x) +#else +#define DPRINTF(x...) do {} while(0) +#endif + +static int scdrv_open(struct inode *, struct file *); +static int scdrv_release(struct inode *, struct file *); +static ssize_t scdrv_read(struct file *, char __user *, size_t, loff_t *); +static ssize_t scdrv_write(struct file *, const char __user *, + size_t, loff_t *); +static unsigned int scdrv_poll(struct file *, struct poll_table_struct *); +static irqreturn_t scdrv_interrupt(int, void *, struct pt_regs *); + +static struct file_operations scdrv_fops = { + .owner = THIS_MODULE, + .read = scdrv_read, + .write = scdrv_write, + .poll = scdrv_poll, + .open = scdrv_open, + .release = scdrv_release, +}; + +/* + * scdrv_wait + * + * Call this function to wait on one of the queues associated with an + * open subchannel. Avoid races by entering this function with a held + * lock that protects the wait queue; don't release the lock until after + * we've added ourselves to the queue. + */ +static inline int +scdrv_wait(wait_queue_head_t *waitq_head, spinlock_t *waitq_lock, + unsigned long flags, unsigned long timeout) +{ + DECLARE_WAITQUEUE(wait, current); + int ret; + + add_wait_queue(waitq_head, &wait); + set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(waitq_lock, flags); + + if (timeout) { + ret = schedule_timeout(timeout); + } else { + schedule(); + } + + remove_wait_queue(waitq_head, &wait); + + if (signal_pending(current)) { + return (timeout ? -ret : -1); + } + return (timeout ? ret : 1); +} + +/* + * scdrv_init + * + * Called at boot time to initialize the system controller communication + * facility. + */ +int __init +scdrv_init(void) +{ + geoid_t geoid; + cmoduleid_t cmod; + int i; + char devname[32]; + char *devnamep; + module_t *m; + struct sysctl_data_s *scd; + void *salbuf; + struct class_simple *snsc_class; + dev_t first_dev, dev; + + if (alloc_chrdev_region(&first_dev, 0, (MAX_SLABS*nummodules), "snsc") + < 0) { + printk("%s: failed to register SN system controller device\n", + __FUNCTION__); + return -ENODEV; + } + snsc_class = class_simple_create(THIS_MODULE, SYSCTL_BASENAME); + + for (cmod = 0; cmod < nummodules; cmod++) { + m = sn_modules[cmod]; + for (i = 0; i <= MAX_SLABS; i++) { + + if (m->nodes[i] == -1) { + /* node is not alive in module */ + continue; + } + + geoid = m->geoid[i]; + devnamep = devname; + format_module_id(devnamep, geo_module(geoid), + MODULE_FORMAT_BRIEF); + devnamep = devname + strlen(devname); + sprintf(devnamep, "#%d", geo_slab(geoid)); + + /* allocate sysctl device data */ + scd = kmalloc(sizeof (struct sysctl_data_s), + GFP_KERNEL); + if (!scd) { + printk("%s: failed to allocate device info" + "for %s/%s\n", __FUNCTION__, + SYSCTL_BASENAME, devname); + continue; + } + memset(scd, 0, sizeof (struct sysctl_data_s)); + + /* initialize sysctl device data fields */ + scd->scd_nasid = cnodeid_to_nasid(m->nodes[i]); + if (!(salbuf = kmalloc(SCDRV_BUFSZ, GFP_KERNEL))) { + printk("%s: failed to allocate driver buffer" + "(%s%s)\n", __FUNCTION__, + SYSCTL_BASENAME, devname); + kfree(scd); + continue; + } + + if (ia64_sn_irtr_init(scd->scd_nasid, salbuf, + SCDRV_BUFSZ) < 0) { + printk + ("%s: failed to initialize SAL for" + " system controller communication" + " (%s/%s): outdated PROM?\n", + __FUNCTION__, SYSCTL_BASENAME, devname); + kfree(scd); + kfree(salbuf); + continue; + } + + dev = first_dev + m->nodes[i]; + cdev_init(&scd->scd_cdev, &scdrv_fops); + if (cdev_add(&scd->scd_cdev, dev, 1)) { + printk("%s: failed to register system" + " controller device (%s%s)\n", + __FUNCTION__, SYSCTL_BASENAME, devname); + kfree(scd); + kfree(salbuf); + continue; + } + + class_simple_device_add(snsc_class, dev, NULL, + "%s", devname); + + ia64_sn_irtr_intr_enable(scd->scd_nasid, + 0 /*ignored */ , + SAL_IROUTER_INTR_RECV); + } + } + return 0; +} + +/* + * scdrv_open + * + * Reserve a subchannel for system controller communication. + */ + +static int +scdrv_open(struct inode *inode, struct file *file) +{ + struct sysctl_data_s *scd; + struct subch_data_s *sd; + int rv; + + /* look up device info for this device file */ + scd = container_of(inode->i_cdev, struct sysctl_data_s, scd_cdev); + + if (!scd) { + printk("%s: no such device\n", __FUNCTION__); + return -ENODEV; + } + + /* allocate memory for subchannel data */ + sd = kmalloc(sizeof (struct subch_data_s), GFP_KERNEL); + if (sd == NULL) { + printk("%s: couldn't allocate subchannel data\n", + __FUNCTION__); + return -ENOMEM; + } + + /* initialize subch_data_s fields */ + memset(sd, 0, sizeof (struct subch_data_s)); + sd->sd_nasid = scd->scd_nasid; + sd->sd_subch = ia64_sn_irtr_open(scd->scd_nasid); + + if (sd->sd_subch < 0) { + kfree(sd); + printk("%s: couldn't allocate subchannel\n", __FUNCTION__); + return -EBUSY; + } + + spin_lock_init(&sd->sd_rlock); + spin_lock_init(&sd->sd_wlock); + init_waitqueue_head(&sd->sd_rq); + init_waitqueue_head(&sd->sd_wq); + sema_init(&sd->sd_rbs, 1); + sema_init(&sd->sd_wbs, 1); + + file->private_data = sd; + + /* hook this subchannel up to the system controller interrupt */ + rv = request_irq(SGI_UART_VECTOR, scdrv_interrupt, + SA_SHIRQ | SA_INTERRUPT, + SYSCTL_BASENAME, sd); + if (rv) { + ia64_sn_irtr_close(sd->sd_nasid, sd->sd_subch); + kfree(sd); + printk("%s: irq request failed (%d)\n", __FUNCTION__, rv); + return -EBUSY; + } + + return 0; +} + +/* + * scdrv_release + * + * Release a previously-reserved subchannel. + */ + +static int +scdrv_release(struct inode *inode, struct file *file) +{ + struct subch_data_s *sd = (struct subch_data_s *) file->private_data; + int rv; + + /* free the interrupt */ + free_irq(SGI_UART_VECTOR, sd); + + /* ask SAL to close the subchannel */ + rv = ia64_sn_irtr_close(sd->sd_nasid, sd->sd_subch); + + kfree(sd); + return rv; +} + +/* + * scdrv_read + * + * Called to read bytes from the open IRouter pipe. + * + */ + +static inline int +read_status_check(struct subch_data_s *sd, int *len) +{ + return ia64_sn_irtr_recv(sd->sd_nasid, sd->sd_subch, sd->sd_rb, len); +} + +static ssize_t +scdrv_read(struct file *file, char __user *buf, size_t count, loff_t *f_pos) +{ + int status; + int len; + unsigned long flags; + struct subch_data_s *sd = (struct subch_data_s *) file->private_data; + + /* try to get control of the read buffer */ + if (down_trylock(&sd->sd_rbs)) { + /* somebody else has it now; + * if we're non-blocking, then exit... + */ + if (file->f_flags & O_NONBLOCK) { + return -EAGAIN; + } + /* ...or if we want to block, then do so here */ + if (down_interruptible(&sd->sd_rbs)) { + /* something went wrong with wait */ + return -ERESTARTSYS; + } + } + + /* anything to read? */ + len = CHUNKSIZE; + spin_lock_irqsave(&sd->sd_rlock, flags); + status = read_status_check(sd, &len); + + /* if not, and we're blocking I/O, loop */ + while (status < 0) { + if (file->f_flags & O_NONBLOCK) { + spin_unlock_irqrestore(&sd->sd_rlock, flags); + return -EAGAIN; + } + len = CHUNKSIZE; + if (scdrv_wait(&sd->sd_rq, &sd->sd_rlock, flags, 1000) < 0) { + /* something went wrong with wait */ + return -ERESTARTSYS; + } + /* sd->sd_rlock was unlocked by scdrv_wait(), above */ + spin_lock_irqsave(&sd->sd_rlock, flags); + status = read_status_check(sd, &len); + } + spin_unlock_irqrestore(&sd->sd_rlock, flags); + + if (len > 0) { + /* we read something in the last read_status_check(); copy + * it out to user space + */ + if (count < len) { + DPRINTF("%s: only accepting %d of %d bytes\n", + __FUNCTION__, (int) count, len); + } + len = min((int) count, len); + if (copy_to_user(buf, sd->sd_rb, len)) + return -EFAULT; + } + + /* release the read buffer and wake anyone who might be + * waiting for it + */ + up(&sd->sd_rbs); + + /* return the number of characters read in */ + return len; +} + +/* + * scdrv_write + * + * Writes a chunk of an IRouter packet (or other system controller data) + * to the system controller. + * + */ +static inline int +write_status_check(struct subch_data_s *sd, int count) +{ + return ia64_sn_irtr_send(sd->sd_nasid, sd->sd_subch, sd->sd_wb, count); +} + +static ssize_t +scdrv_write(struct file *file, const char __user *buf, + size_t count, loff_t *f_pos) +{ + unsigned long flags; + int status; + struct subch_data_s *sd = (struct subch_data_s *) file->private_data; + + /* try to get control of the write buffer */ + if (down_trylock(&sd->sd_wbs)) { + /* somebody else has it now; + * if we're non-blocking, then exit... + */ + if (file->f_flags & O_NONBLOCK) { + return -EAGAIN; + } + /* ...or if we want to block, then do so here */ + if (down_interruptible(&sd->sd_wbs)) { + /* something went wrong with wait */ + return -ERESTARTSYS; + } + } + + count = min((int) count, CHUNKSIZE); + if (copy_from_user(sd->sd_wb, buf, count)) + return -EFAULT; + + /* try to send the buffer */ + spin_lock_irqsave(&sd->sd_wlock, flags); + status = write_status_check(sd, count); + + /* if we failed, and we want to block, then loop */ + while (status <= 0) { + if (file->f_flags & O_NONBLOCK) { + spin_unlock(&sd->sd_wlock); + return -EAGAIN; + } + if (scdrv_wait(&sd->sd_wq, &sd->sd_wlock, flags, 1000) < 0) { + /* something went wrong with wait */ + return -ERESTARTSYS; + } + + /* sd->sd_wlock was unlocked by scdrv_wait(), above */ + spin_lock_irqsave(&sd->sd_wlock, flags); + status = write_status_check(sd, count); + } + spin_unlock_irqrestore(&sd->sd_wlock, flags); + + /* release the write buffer and wake anyone who's waiting for it */ + up(&sd->sd_wbs); + + /* return the number of characters accepted (should be the complete + * "chunk" as requested) + */ + if ((status >= 0) && (status < count)) { + DPRINTF("Didn't accept the full chunk; %d of %d\n", + status, (int) count); + } + return status; +} + +static inline void +scdrv_lock_all(struct subch_data_s *sd, unsigned long *flags) +{ + spin_lock_irqsave(&sd->sd_rlock, *flags); + spin_lock(&sd->sd_wlock); +} + +static inline void +scdrv_unlock_all(struct subch_data_s *sd, unsigned long flags) +{ + spin_unlock(&sd->sd_wlock); + spin_unlock_irqrestore(&sd->sd_rlock, flags); +} + +static unsigned int +scdrv_poll(struct file *file, struct poll_table_struct *wait) +{ + unsigned int mask = 0; + int status = 0; + struct subch_data_s *sd = (struct subch_data_s *) file->private_data; + unsigned long flags; + + poll_wait(file, &sd->sd_rq, wait); + poll_wait(file, &sd->sd_wq, wait); + + scdrv_lock_all(sd, &flags); + status = ia64_sn_irtr_intr(sd->sd_nasid, sd->sd_subch); + scdrv_unlock_all(sd, flags); + + if (status > 0) { + if (status & SAL_IROUTER_INTR_RECV) { + mask |= POLLIN | POLLRDNORM; + } + if (status & SAL_IROUTER_INTR_XMIT) { + mask |= POLLOUT | POLLWRNORM; + } + } + + return mask; +} + +static irqreturn_t +scdrv_interrupt(int irq, void *subch_data, struct pt_regs *regs) +{ + struct subch_data_s *sd = (struct subch_data_s *) subch_data; + unsigned long flags; + int status; + + scdrv_lock_all(sd, &flags); + status = ia64_sn_irtr_intr(sd->sd_nasid, sd->sd_subch); + + if (status > 0) { + if (status & SAL_IROUTER_INTR_RECV) { + wake_up_all(&sd->sd_rq); + } + if (status & SAL_IROUTER_INTR_XMIT) { + ia64_sn_irtr_intr_disable + (sd->sd_nasid, sd->sd_subch, + SAL_IROUTER_INTR_XMIT); + wake_up_all(&sd->sd_wq); + } + } + scdrv_unlock_all(sd, flags); + return IRQ_HANDLED; +} + +module_init(scdrv_init); diff -puN /dev/null drivers/char/snsc.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/char/snsc.h 2004-07-31 17:06:30.619645336 -0700 @@ -0,0 +1,50 @@ +/* + * SN Platform system controller communication support + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2004 Silicon Graphics, Inc. All rights reserved. + */ + +/* + * This file contains macros and data types for communication with the + * system controllers in SGI SN systems. + */ + +#ifndef _SN_SYSCTL_H_ +#define _SN_SYSCTL_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define CHUNKSIZE 127 + +/* This structure is used to track an open subchannel. */ +struct subch_data_s { + nasid_t sd_nasid; /* node on which the subchannel was opened */ + int sd_subch; /* subchannel number */ + spinlock_t sd_rlock; /* monitor lock for rsv */ + spinlock_t sd_wlock; /* monitor lock for wsv */ + wait_queue_head_t sd_rq; /* wait queue for readers */ + wait_queue_head_t sd_wq; /* wait queue for writers */ + struct semaphore sd_rbs; /* semaphore for read buffer */ + struct semaphore sd_wbs; /* semaphore for write buffer */ + + char sd_rb[CHUNKSIZE]; /* read buffer */ + char sd_wb[CHUNKSIZE]; /* write buffer */ +}; + +struct sysctl_data_s { + struct cdev scd_cdev; /* Character device info */ + nasid_t scd_nasid; /* Node on which subchannels are opened. */ +}; + +#endif /* _SN_SYSCTL_H_ */ diff -puN drivers/serial/sn_console.c~altix-system-controller-communication-driver drivers/serial/sn_console.c --- 25/drivers/serial/sn_console.c~altix-system-controller-communication-driver 2004-07-31 17:06:30.609646856 -0700 +++ 25-akpm/drivers/serial/sn_console.c 2004-07-31 17:06:30.619645336 -0700 @@ -714,7 +714,8 @@ sn_sal_interrupt(int irq, void *dev_id, static int sn_sal_connect_interrupt(struct sn_cons_port *port) { - if (request_irq(SGI_UART_VECTOR, sn_sal_interrupt, SA_INTERRUPT, + if (request_irq(SGI_UART_VECTOR, sn_sal_interrupt, + SA_INTERRUPT | SA_SHIRQ, "SAL console driver", port) >= 0) { return SGI_UART_VECTOR; } diff -puN include/asm-ia64/sn/sn_sal.h~altix-system-controller-communication-driver include/asm-ia64/sn/sn_sal.h --- 25/include/asm-ia64/sn/sn_sal.h~altix-system-controller-communication-driver 2004-07-31 17:06:30.611646552 -0700 +++ 25-akpm/include/asm-ia64/sn/sn_sal.h 2004-07-31 17:06:30.621645032 -0700 @@ -8,7 +8,7 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (c) 2000-2003 Silicon Graphics, Inc. All rights reserved. + * Copyright (c) 2000-2004 Silicon Graphics, Inc. All rights reserved. */ @@ -60,6 +60,7 @@ #define SN_SAL_SYSCTL_FRU_CAPTURE 0x0200003f #define SN_SAL_SYSCTL_IOBRICK_PCI_OP 0x02000042 // reentrant +#define SN_SAL_IROUTER_OP 0x02000043 /* * Service-specific constants @@ -86,6 +87,25 @@ #endif /* CONFIG_HOTPLUG_PCI_SGI */ /* + * IRouter (i.e. generalized system controller) operations + */ +#define SAL_IROUTER_OPEN 0 /* open a subchannel */ +#define SAL_IROUTER_CLOSE 1 /* close a subchannel */ +#define SAL_IROUTER_SEND 2 /* send part of an IRouter packet */ +#define SAL_IROUTER_RECV 3 /* receive part of an IRouter packet */ +#define SAL_IROUTER_INTR_STATUS 4 /* check the interrupt status for + * an open subchannel + */ +#define SAL_IROUTER_INTR_ON 5 /* enable an interrupt */ +#define SAL_IROUTER_INTR_OFF 6 /* disable an interrupt */ +#define SAL_IROUTER_INIT 7 /* initialize IRouter driver */ + +/* IRouter interrupt mask bits */ +#define SAL_IROUTER_INTR_XMIT SAL_CONSOLE_INTR_XMIT +#define SAL_IROUTER_INTR_RECV SAL_CONSOLE_INTR_RECV + + +/* * SN_SAL_GET_PARTITION_ADDR return constants */ #define SALRET_MORE_PASSES 1 @@ -704,4 +724,124 @@ ia64_sn_set_error_handling_features(cons return rv.status; } + +/* + * Open a subchannel for sending arbitrary data to the system + * controller network via the system controller device associated with + * 'nasid'. Return the subchannel number or a negative error code. + */ +static inline int +ia64_sn_irtr_open(nasid_t nasid) +{ + struct ia64_sal_retval rv; + SAL_CALL_REENTRANT(rv, SN_SAL_IROUTER_OP, SAL_IROUTER_OPEN, nasid, + 0, 0, 0, 0, 0); + return (int) rv.v0; +} + +/* + * Close system controller subchannel 'subch' previously opened on 'nasid'. + */ +static inline int +ia64_sn_irtr_close(nasid_t nasid, int subch) +{ + struct ia64_sal_retval rv; + SAL_CALL_REENTRANT(rv, SN_SAL_IROUTER_OP, SAL_IROUTER_CLOSE, + (u64) nasid, (u64) subch, 0, 0, 0, 0); + return (int) rv.status; +} + +/* + * Read data from system controller associated with 'nasid' on + * subchannel 'subch'. The buffer to be filled is pointed to by + * 'buf', and its capacity is in the integer pointed to by 'len'. The + * referent of 'len' is set to the number of bytes read by the SAL + * call. The return value is either SALRET_OK (for bytes read) or + * SALRET_ERROR (for error or "no data available"). + */ +static inline int +ia64_sn_irtr_recv(nasid_t nasid, int subch, char *buf, int *len) +{ + struct ia64_sal_retval rv; + SAL_CALL_REENTRANT(rv, SN_SAL_IROUTER_OP, SAL_IROUTER_RECV, + (u64) nasid, (u64) subch, (u64) buf, (u64) len, + 0, 0); + return (int) rv.status; +} + +/* + * Write data to the system controller network via the system + * controller associated with 'nasid' on suchannel 'subch'. The + * buffer to be written out is pointed to by 'buf', and 'len' is the + * number of bytes to be written. The return value is either the + * number of bytes written (which could be zero) or a negative error + * code. + */ +static inline int +ia64_sn_irtr_send(nasid_t nasid, int subch, char *buf, int len) +{ + struct ia64_sal_retval rv; + SAL_CALL_REENTRANT(rv, SN_SAL_IROUTER_OP, SAL_IROUTER_SEND, + (u64) nasid, (u64) subch, (u64) buf, (u64) len, + 0, 0); + return (int) rv.v0; +} + +/* + * Check whether any interrupts are pending for the system controller + * associated with 'nasid' and its subchannel 'subch'. The return + * value is a mask of pending interrupts (SAL_IROUTER_INTR_XMIT and/or + * SAL_IROUTER_INTR_RECV). + */ +static inline int +ia64_sn_irtr_intr(nasid_t nasid, int subch) +{ + struct ia64_sal_retval rv; + SAL_CALL_REENTRANT(rv, SN_SAL_IROUTER_OP, SAL_IROUTER_INTR_STATUS, + (u64) nasid, (u64) subch, 0, 0, 0, 0); + return (int) rv.v0; +} + +/* + * Enable the interrupt indicated by the intr parameter (either + * SAL_IROUTER_INTR_XMIT or SAL_IROUTER_INTR_RECV). + */ +static inline int +ia64_sn_irtr_intr_enable(nasid_t nasid, int subch, u64 intr) +{ + struct ia64_sal_retval rv; + SAL_CALL_REENTRANT(rv, SN_SAL_IROUTER_OP, SAL_IROUTER_INTR_ON, + (u64) nasid, (u64) subch, intr, 0, 0, 0); + return (int) rv.v0; +} + +/* + * Disable the interrupt indicated by the intr parameter (either + * SAL_IROUTER_INTR_XMIT or SAL_IROUTER_INTR_RECV). + */ +static inline int +ia64_sn_irtr_intr_disable(nasid_t nasid, int subch, u64 intr) +{ + struct ia64_sal_retval rv; + SAL_CALL_REENTRANT(rv, SN_SAL_IROUTER_OP, SAL_IROUTER_INTR_OFF, + (u64) nasid, (u64) subch, intr, 0, 0, 0); + return (int) rv.v0; +} + +/* + * Initialize the SAL components of the system controller + * communication driver; specifically pass in a sizable buffer that + * can be used for allocation of subchannel queues as new subchannels + * are opened. "buf" points to the buffer, and "len" specifies its + * length. + */ +static inline int +ia64_sn_irtr_init(nasid_t nasid, void *buf, int len) +{ + struct ia64_sal_retval rv; + SAL_CALL_REENTRANT(rv, SN_SAL_IROUTER_OP, SAL_IROUTER_INIT, + (u64) nasid, (u64) buf, (u64) len, 0, 0, 0); + return (int) rv.status; +} + #endif /* _ASM_IA64_SN_SN_SAL_H */ _