This patch is needed to support these serial interfaces as system
interfaces to IPMI baseboard management controllers. Direct accesses to
the serial ports is needed because of the requirment for sending panic
information, as well as IPMI power off. This bi-directional communication
must be performed with interrupts off, so the use of n_tty line discipline
is not possible
Signed-off-by: David Griego <[EMAIL PROTECTED]>
---
drivers/serial/8250.c | 76 +++++++++++++++++++++++++++++++++++++++
drivers/serial/8250_pci.c | 3 +-
drivers/serial/Kconfig | 7 ++++
drivers/serial/mpsc.c | 70 ++++++++++++++++++++++++++++++++++++
drivers/serial/serial_core.c | 81 ++++++++++++++++++++++++++++++++++++++++++
5 files changed, 236 insertions(+), 1 deletions(-)
28d7e2c690793761864ab9fc857522c6d860f0a4
diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c
index bbf78aa..52b4fcf 100644
--- a/drivers/serial/8250.c
+++ b/drivers/serial/8250.c
@@ -41,6 +41,9 @@ #include <linux/serial.h>
#include <linux/serial_8250.h>
#include <linux/nmi.h>
#include <linux/mutex.h>
+#ifdef CONFIG_SERIAL_IPMI
+#include <linux/ipmi_serial.h>
+#endif /* CONFIG_SERIAL_IPMI */
#include <asm/io.h>
#include <asm/irq.h>
@@ -2183,6 +2186,7 @@ serial8250_register_ports(struct uart_dr
up->port.dev = dev;
uart_add_one_port(drv, &up->port);
}
+ uart_ipmi_register_port();
}
#ifdef CONFIG_SERIAL_8250_CONSOLE
@@ -2584,6 +2588,8 @@ int serial8250_register_port(struct uart
}
mutex_unlock(&serial_mutex);
+ uart_ipmi_register_port();
+
return ret;
}
EXPORT_SYMBOL(serial8250_register_port);
@@ -2657,6 +2663,76 @@ static int __init serial8250_init(void)
return ret;
}
+#ifdef CONFIG_SERIAL_IPMI
+static int serial8250_ipmi_cleanup(struct ipmi_serial_dev *dev)
+{
+ if(!dev->port)
+ return 0;
+
+ printk("Return IPMI serial port to system\n");
+ return uart_add_one_port(&serial8250_reg,dev->port);
+}
+
+static int serial8250_ipmi_setup(struct ipmi_serial_dev *dev)
+{
+ struct uart_port *port;
+ unsigned int type;
+
+ /*
+ * Check whether an invalid uart number has been specified
+ */
+ if ((dev->index >= UART_NR)||(dev->index < 0)){
+ return -ENODEV;
+ }
+ port = &serial8250_ports[dev->index].port;
+ if (!port->iobase && !port->membase){
+ return -ENODEV;
+ }
+
+ mutex_lock(&serial_mutex);
+ type = port->type;
+ dev->port=port;
+ port->type=PORT_UNKNOWN;
+ uart_remove_one_port(&serial8250_reg,port);
+ port->type = type;
+
+ mutex_unlock(&serial_mutex);
+ return 0;
+}
+
+/* called with interrupts disabled - used for run to completion */
+static void serial8250_ipmi_poll(struct uart_port *port)
+{
+ struct uart_8250_port *up = (struct uart_8250_port *)port;
+ unsigned int iir;
+ /* call serial port polling routine here */
+
+ iir = serial_in(up, UART_IIR);
+ if (!(iir & UART_IIR_NO_INT)) {
+ spin_lock(&up->port.lock);
+ serial8250_handle_port(up, NULL);
+ spin_unlock(&up->port.lock);
+ }
+}
+
+static struct ipmi_serial_dev serial8250_ipmi = {
+ .name = "ttyS",
+ .poll = serial8250_ipmi_poll,
+ .setup = serial8250_ipmi_setup,
+ .cleanup = serial8250_ipmi_cleanup,
+ .index = -1,
+};
+
+static int __init serial8250_ipmi_init(void)
+{
+ uart_ipmi_register(&serial8250_ipmi);
+ return 0;
+}
+
+device_initcall(serial8250_ipmi_init);
+
+#endif /* CONFIG_SERIAL_IPMI */
+
static void __exit serial8250_exit(void)
{
struct platform_device *isa_dev = serial8250_isa_devs;
diff --git a/drivers/serial/8250_pci.c b/drivers/serial/8250_pci.c
index 94886c0..3fb8aed 100644
--- a/drivers/serial/8250_pci.c
+++ b/drivers/serial/8250_pci.c
@@ -1477,7 +1477,8 @@ static struct pciserial_board pci_boards
[pbn_exar_XR17C152] = {
.flags = FL_BASE0,
.num_ports = 2,
- .base_baud = 921600,
+// .base_baud = 921600,
+ .base_baud = 230400,
.uart_offset = 0x200,
},
[pbn_exar_XR17C154] = {
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 7d22dc0..87ea32e 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -936,4 +936,11 @@ config SERIAL_SGI_IOC3
If you have an SGI Altix with an IOC3 serial card,
say Y or M. Otherwise, say N.
+config SERIAL_IPMI
+ bool "Serial support for IPMI"
+ depends on IPMI_SERIAL
+ default y
+ help
+ Say Y here if you want to communicate to an IPMI BMC through
+ the serial port
endmenu
diff --git a/drivers/serial/mpsc.c b/drivers/serial/mpsc.c
index 9468192..7f4d26c 100644
--- a/drivers/serial/mpsc.c
+++ b/drivers/serial/mpsc.c
@@ -71,6 +71,9 @@ #include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/mv643xx.h>
#include <linux/platform_device.h>
+#ifdef CONFIG_SERIAL_IPMI
+#include <linux/ipmi_serial.h>
+#endif /* CONFIG_SERIAL_IPMI */
#include <asm/io.h>
#include <asm/irq.h>
@@ -1728,6 +1731,73 @@ #define MPSC_CONSOLE &mpsc_console
#else
#define MPSC_CONSOLE NULL
#endif
+
+#ifdef CONFIG_SERIAL_IPMI
+static int serialmpsc_ipmi_cleanup(struct ipmi_serial_dev *dev)
+{
+ if(!dev->port)
+ return 0;
+
+ printk("Return IPMI serial port to system\n");
+ return uart_add_one_port(&mpsc_reg,dev->port);
+}
+
+static int serialmpsc_ipmi_setup(struct ipmi_serial_dev *dev)
+{
+ struct uart_port *port;
+ unsigned int type;
+
+ /*
+ * Check whether an invalid uart number has been specified
+ */
+ if ((dev->index >= MPSC_NUM_CTLRS)||(dev->index < 0)){
+ return -ENODEV;
+ }
+ port = &mpsc_ports[dev->index].port;
+ if (!port->iobase && !port->membase){
+ return -ENODEV;
+ }
+
+ type = port->type;
+ dev->port=port;
+ port->type=PORT_UNKNOWN;
+ uart_remove_one_port(&mpsc_reg,port);
+ port->type = type;
+
+ return 0;
+}
+
+/* called with interrupts disabled - used for run to completion */
+static void serialmpsc_ipmi_poll(struct uart_port *port)
+{
+ struct mpsc_port_info *pi = (struct mpsc_port_info *)port;
+ ulong iflags;
+
+ spin_lock_irqsave(&pi->port.lock, iflags);
+ mpsc_sdma_intr_ack(pi);
+ mpsc_rx_intr(pi, NULL);
+ mpsc_tx_intr(pi);
+ spin_unlock_irqrestore(&pi->port.lock, iflags);
+}
+
+static struct ipmi_serial_dev serialmpsc_ipmi = {
+ .name = MPSC_DEV_NAME,
+ .poll = serialmpsc_ipmi_poll,
+ .setup = serialmpsc_ipmi_setup,
+ .cleanup = serialmpsc_ipmi_cleanup,
+ .index = -1,
+};
+
+static int __init serialmpsc_ipmi_init(void)
+{
+ uart_ipmi_register(&serialmpsc_ipmi);
+ return 0;
+}
+
+device_initcall(serialmpsc_ipmi_init);
+
+#endif /* CONFIG_SERIAL_IPMI */
+
/*
******************************************************************************
*
diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c
index 17839e7..059ddcf 100644
--- a/drivers/serial/serial_core.c
+++ b/drivers/serial/serial_core.c
@@ -34,6 +34,9 @@ #include <linux/device.h>
#include <linux/serial.h> /* for serial_state and serial_icounter_struct */
#include <linux/delay.h>
#include <linux/mutex.h>
+#ifdef CONFIG_SERIAL_IPMI
+#include <linux/ipmi_serial.h>
+#endif /* CONFIG_SERIAL_IPMI */
#include <asm/irq.h>
#include <asm/uaccess.h>
@@ -45,6 +48,17 @@ #else
#define DPRINTK(x...) do { } while (0)
#endif
+#ifdef CONFIG_SERIAL_IPMI
+/*
+ * ipmi_serial_sem protects the IPMI serial driver list, and also
+ * provides serialisation for access to the ipmi serial drivers
+ */
+static DECLARE_MUTEX(ipmi_serial_sem);
+static struct ipmi_serial_dev *ipmi_serial_devices;
+static struct ipmi_serial_intf *ipmi_serial_intf;
+static int ipmi_serial_initialized = 0;
+#endif /* CONFIG_SERIAL_IPMI */
+
/*
* This is used to lock changes in serial line configuration.
*/
@@ -1904,6 +1918,73 @@ uart_set_options(struct uart_port *port,
}
#endif /* CONFIG_SERIAL_CORE_CONSOLE */
+#ifdef CONFIG_SERIAL_IPMI
+/*
+ * The IPMI driver calls this routine to indicate to the serial layer that
+ * the IPMI layer is ready to take control of IPMI communications.
+ */
+struct ipmi_serial_dev *
+register_ipmi(struct ipmi_serial_intf *intf)
+{
+ down(&ipmi_serial_sem);
+ if(ipmi_serial_initialized){
+ up(&ipmi_serial_sem);
+ printk("IPMI serial already initilized\n");
+ return NULL;
+ }
+ printk("Registering IPMI driver with serial layer\n");
+ ipmi_serial_initialized = 1;
+ /* Set the IPMI layer callback */
+ ipmi_serial_intf = intf;
+
+ up(&ipmi_serial_sem);
+ return ipmi_serial_devices;
+}
+EXPORT_SYMBOL(register_ipmi);
+
+void
+unregister_ipmi(struct ipmi_serial_intf *intf)
+{
+ down(&ipmi_serial_sem);
+ printk("Unregistering IPMI serial driver\n");
+ ipmi_serial_intf = NULL;
+ ipmi_serial_initialized = 0;
+ up(&ipmi_serial_sem);
+}
+EXPORT_SYMBOL(unregister_ipmi);
+
+/* Serial port drivers call this function if they support a system interface
+ * with* an IPMI baseboard managment controller.
+ */
+void
+uart_ipmi_register(struct ipmi_serial_dev *ipmi_serial_dev)
+{
+ down(&ipmi_serial_sem);
+ if(ipmi_serial_devices == NULL){
+ ipmi_serial_dev->next = ipmi_serial_devices;
+ ipmi_serial_devices = ipmi_serial_dev;
+ }else{
+ ipmi_serial_dev->next = ipmi_serial_devices->next;
+ ipmi_serial_devices->next = ipmi_serial_dev;
+ }
+ up(&ipmi_serial_sem);
+ if(ipmi_serial_intf)
+ ipmi_serial_intf->callback(ipmi_serial_devices);
+}
+EXPORT_SYMBOL(uart_ipmi_register);
+
+/* Serial ports after serial driver has registered can be used for IPMI
+ * communication too.
+ */
+void
+uart_ipmi_register_port()
+{
+ if(ipmi_serial_intf)
+ ipmi_serial_intf->callback(ipmi_serial_devices);
+}
+EXPORT_SYMBOL(uart_ipmi_register_port);
+#endif /* CONFIG_SERIAL_IPMI */
+
static void uart_change_pm(struct uart_state *state, int pm_state)
{
struct uart_port *port = state->port;
--
1.3.3
_______________________________________________
Openipmi-developer mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/openipmi-developer