diff -urN linux-2.6.1/drivers/Kconfig linux-2.6.1-ibmasm/drivers/Kconfig --- linux-2.6.1/drivers/Kconfig 2004-01-08 23:00:03.000000000 -0800 +++ linux-2.6.1-ibmasm/drivers/Kconfig 2004-01-14 13:21:36.000000000 -0800 @@ -38,7 +38,7 @@ source "drivers/char/Kconfig" -# source "drivers/misc/Kconfig" +source "drivers/misc/Kconfig" source "drivers/media/Kconfig" diff -urN linux-2.6.1/drivers/misc/ibmasm/command.c linux-2.6.1-ibmasm/drivers/misc/ibmasm/command.c --- linux-2.6.1/drivers/misc/ibmasm/command.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.1-ibmasm/drivers/misc/ibmasm/command.c 2004-01-22 11:57:28.000000000 -0800 @@ -0,0 +1,175 @@ + +/* + * IBM ASM Service Processor Device Driver + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) IBM Corporation, 2004 + * + * Author: Max Asböck + * + */ + +#include "ibmasm.h" + +static void exec_next_command(struct service_processor *sp); +static void free_command(struct kobject *kobj); + +static struct kobj_type ibmasm_cmd_kobj_type = { + .release = free_command, +}; + + +struct command *ibmasm_new_command(size_t buffer_size) +{ + struct command *cmd; + + if (buffer_size > IBMASM_CMD_MAX_BUFFER_SIZE) + return NULL; + + cmd = kmalloc(sizeof(struct command), GFP_KERNEL); + if (cmd == NULL) + return NULL; + + memset(cmd, 0, sizeof(*cmd)); + + cmd->buffer = kmalloc(buffer_size, GFP_KERNEL); + if (cmd->buffer == NULL) { + kfree(cmd); + return NULL; + } + memset(cmd->buffer, 0, buffer_size); + cmd->buffer_size = buffer_size; + + kobject_init(&cmd->kobj); + cmd->kobj.ktype = &ibmasm_cmd_kobj_type; + + cmd->status = IBMASM_CMD_PENDING; + init_waitqueue_head(&cmd->wait); + INIT_LIST_HEAD(&cmd->queue_node); + + return cmd; +} + +static void free_command(struct kobject *kobj) +{ + struct command *cmd = to_command(kobj); + + list_del(&cmd->queue_node); + kfree(cmd->buffer); + kfree(cmd); +} + +static void enqueue_command(struct service_processor *sp, struct command *cmd) +{ + list_add_tail(&cmd->queue_node, &sp->command_queue); +} + +static struct command *dequeue_command(struct service_processor *sp) +{ + struct command *cmd; + struct list_head *next; + + if ( list_empty(&sp->command_queue) ) + return NULL; + + next = sp->command_queue.next; + list_del_init(next); + cmd = list_entry(next, struct command, queue_node); + + return cmd; +} + +static inline void do_exec_command(struct service_processor *sp) +{ + if ( ibmasm_send_i2o_message(sp) ) { + sp->current_command->status = IBMASM_CMD_FAILED; + exec_next_command(sp); + } +} + +/** + * exec_command + * send a command to a service processor + * Commands are executed sequentially. One command (sp->current_command) + * is sent to the service processor. Once the interrupt handler gets a + * message of type command_response, the message is copied into + * the current commands buffer, + */ +void ibmasm_exec_command(struct service_processor *sp, struct command *cmd) +{ + unsigned long flags; + + spin_lock_irqsave(&sp->lock, flags); + + if ( !sp->current_command ) { + command_get(cmd); + sp->current_command = cmd; + spin_unlock_irqrestore(&sp->lock, flags); + + do_exec_command(sp); + } else { + enqueue_command(sp, cmd); + spin_unlock_irqrestore(&sp->lock, flags); + } +} + +static void exec_next_command(struct service_processor *sp) +{ + unsigned long flags; + + wake_up(&sp->current_command->wait); + command_put(sp->current_command); + + spin_lock_irqsave(&sp->lock, flags); + sp->current_command = dequeue_command(sp); + if ( sp->current_command ) { + command_get(sp->current_command); + spin_unlock_irqrestore(&sp->lock, flags); + do_exec_command(sp); + } else { + spin_unlock_irqrestore(&sp->lock, flags); + } +} + +/** + * Sleep until a command has failed or a response has been received + * and the command status been updated by the interrupt handler. + * (see receive_response). + */ +void ibmasm_wait_for_response(struct command *cmd, int timeout) +{ + wait_event_interruptible_timeout(cmd->wait, + cmd->status == IBMASM_CMD_COMPLETE || + cmd->status == IBMASM_CMD_FAILED, + timeout * HZ); +} + +/** + * receive_command_response + * called by the interrupt handler when a dot command of type command_response + * was received. + */ +void ibmasm_receive_command_response(struct service_processor *sp, void *response, size_t size) +{ + struct command *cmd = sp->current_command; + + if (!sp->current_command) + return; + + memcpy(cmd->buffer, response, min(size, cmd->buffer_size)); + cmd->status = IBMASM_CMD_COMPLETE; + exec_next_command(sp); +} diff -urN linux-2.6.1/drivers/misc/ibmasm/dot_command.c linux-2.6.1-ibmasm/drivers/misc/ibmasm/dot_command.c --- linux-2.6.1/drivers/misc/ibmasm/dot_command.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.1-ibmasm/drivers/misc/ibmasm/dot_command.c 2004-01-22 11:10:20.000000000 -0800 @@ -0,0 +1,146 @@ +/* + * IBM ASM Service Processor Device Driver + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) IBM Corporation, 2004 + * + * Author: Max Asböck + * + */ + +#include "ibmasm.h" +#include "dot_command.h" + +/** + * Dispatch an incoming message to the specific handler for the message. + * Called from interrupt context. + */ +void ibmasm_receive_message(struct service_processor *sp, void *message, int message_size) +{ + u32 size; + struct dot_command_header *header = (struct dot_command_header *)message; + + size = get_dot_command_size(message); + if (size > message_size) + size = message_size; + + switch (header->type) { + case sp_event: + ibmasm_receive_event(sp, message, size); + break; + case sp_command_response: + ibmasm_receive_command_response(sp, message, size); + break; + case sp_heartbeat: + ibmasm_receive_heartbeat(sp, message, size); + break; + default: + dev_err(sp->dev, "Received unknown message from service processor\n"); + } +} + + +#define INIT_BUFFER_SIZE 32 + + +/** + * send the 4.3.5.10 dot command (driver VPD) to the service processor + */ +int ibmasm_send_driver_vpd(struct service_processor *sp) +{ + struct command *command; + struct dot_command_header *header; + u8 *vpd_command; + u8 *vpd_data; + int result = 0; + + command = ibmasm_new_command(INIT_BUFFER_SIZE); + if (command == NULL) + return -ENOMEM; + + header = (struct dot_command_header *)command->buffer; + header->type = sp_write; + header->command_size = 4; + header->data_size = 16; + header->status = 0; + header->reserved = 0; + + vpd_command = command->buffer + sizeof(struct dot_command_header); + vpd_command[0] = 0x4; + vpd_command[1] = 0x3; + vpd_command[2] = 0x5; + vpd_command[3] = 0xa; + + vpd_data = vpd_command + header->command_size; + vpd_data[0] = 0; + strcat(vpd_data, IBMASM_DRIVER_VPD); + vpd_data[10] = 0; + vpd_data[15] = 0; + + ibmasm_exec_command(sp, command); + ibmasm_wait_for_response(command, IBMASM_CMD_TIMEOUT_NORMAL); + + if (command->status != IBMASM_CMD_COMPLETE) + result = -ENODEV; + + command_put(command); + + return result; +} + +struct os_state_command { + struct dot_command_header header; + unsigned char command[3]; + unsigned char data; +}; + +/** + * send the 4.3.6 dot command (os state) to the service processor + * During driver init this function is called with os state "up". + * This causes the service processor to start sending heartbeats the + * driver. + * During driver exit the function is called with os state "down", + * causing the service processor to stop the heartbeats. + */ +int ibmasm_send_os_state(struct service_processor *sp, int os_state) +{ + struct command *cmd; + struct os_state_command *os_state_cmd; + int result = 0; + + cmd = ibmasm_new_command(sizeof(struct os_state_command)); + if (cmd == NULL) + return -ENOMEM; + + os_state_cmd = (struct os_state_command *)cmd->buffer; + os_state_cmd->header.type = sp_write; + os_state_cmd->header.command_size = 3; + os_state_cmd->header.data_size = 1; + os_state_cmd->header.status = 0; + os_state_cmd->command[0] = 4; + os_state_cmd->command[1] = 3; + os_state_cmd->command[2] = 6; + os_state_cmd->data = os_state; + + ibmasm_exec_command(sp, cmd); + ibmasm_wait_for_response(cmd, IBMASM_CMD_TIMEOUT_NORMAL); + + if (cmd->status != IBMASM_CMD_COMPLETE) + result = -ENODEV; + + command_put(cmd); + return result; +} diff -urN linux-2.6.1/drivers/misc/ibmasm/dot_command.h linux-2.6.1-ibmasm/drivers/misc/ibmasm/dot_command.h --- linux-2.6.1/drivers/misc/ibmasm/dot_command.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.1-ibmasm/drivers/misc/ibmasm/dot_command.h 2004-01-23 15:40:55.000000000 -0800 @@ -0,0 +1,78 @@ +/* + * IBM ASM Service Processor Device Driver + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) IBM Corporation, 2004 + * + * Author: Max Asböck + * + */ + +#ifndef __DOT_COMMAND_H__ +#define __DOT_COMMAND_H__ + +/* + * dot commands are the protocol used to communicate with the service + * processor. + * They consist of header, a command of variable length and data of + * variable length. + */ + +/* dot command types */ +#define sp_write 0 +#define sp_write_next 1 +#define sp_read 2 +#define sp_read_next 3 +#define sp_command_response 4 +#define sp_event 5 +#define sp_heartbeat 6 + +#pragma pack(1) +struct dot_command_header { + u8 type; + u8 command_size; + u16 data_size; + u8 status; + u8 reserved; +}; +#pragma pack() + +static inline size_t get_dot_command_size(void *buffer) +{ + struct dot_command_header *cmd = (struct dot_command_header *)buffer; + return sizeof(struct dot_command_header) + cmd->command_size + cmd->data_size; +} + +static inline unsigned int get_dot_command_timeout(void *buffer) +{ + struct dot_command_header *header = (struct dot_command_header *)buffer; + unsigned char *cmd = buffer + sizeof(struct dot_command_header); + + /* dot commands 6.3.1, 7.1 and 8.x need a longer timeout */ + + if (header->command_size == 3) { + if ( (cmd[0] == 6) && (cmd[1] == 3) && (cmd[2] == 1) ) + return IBMASM_CMD_TIMEOUT_EXTRA; + } else if (header->command_size == 2) { + if ( (cmd[0] == 7) && (cmd[1] == 1) ) + return IBMASM_CMD_TIMEOUT_EXTRA; + if (cmd[0] == 8) + return IBMASM_CMD_TIMEOUT_EXTRA; + } + return IBMASM_CMD_TIMEOUT_NORMAL; +} + +#endif /* __DOT_COMMAND_H__ */ diff -urN linux-2.6.1/drivers/misc/ibmasm/event.c linux-2.6.1-ibmasm/drivers/misc/ibmasm/event.c --- linux-2.6.1/drivers/misc/ibmasm/event.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.1-ibmasm/drivers/misc/ibmasm/event.c 2004-01-20 11:16:29.000000000 -0800 @@ -0,0 +1,172 @@ + +/* + * IBM ASM Service Processor Device Driver + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) IBM Corporation, 2004 + * + * Author: Max Asböck + * + */ + +#include "ibmasm.h" + +/* + * ASM service processor event handling routines. + * + * Events are signalled to the device drivers through interrupts. + * They have the format of dot commands, with the type field set to + * sp_event. + * The driver does not interpret the events, it simply stores them in a + * circular buffer. + */ + + +static void wake_up_event_readers(struct service_processor *sp) +{ + struct event_reader *reader; + struct list_head *entry; + + list_for_each(entry, &sp->event_buffer->readers) { + reader = list_entry(entry, struct event_reader, node); + wake_up_interruptible(&reader->wait); + } +} + +/** + * receive_event + * Called by the interrupt handler when a dot command of type sp_event is + * received. + * Store the event in the circular event buffer, wake up any sleeping + * event readers. + * There is no reader marker in the buffer, therefore readers are + * responsible for keeping up with the writer, or they will loose events. + */ +void ibmasm_receive_event(struct service_processor *sp, void *data, size_t data_size) +{ + struct event_buffer *buffer = sp->event_buffer; + struct ibmasm_event *event; + unsigned long flags; + + data_size = min(data_size, IBMASM_EVENT_MAX_SIZE); + + spin_lock_irqsave(&sp->lock, flags); + /* copy the event into the next slot in the circular buffer */ + event = &buffer->events[buffer->next_index]; + memcpy(event->data, data, data_size); + event->data_size = data_size; + event->serial_number = buffer->next_serial_number; + + /* advance indices in the buffer */ + buffer->next_index = ++(buffer->next_index) % IBMASM_NUM_EVENTS; + buffer->next_serial_number++; + spin_unlock_irqrestore(&sp->lock, flags); + + wake_up_event_readers(sp); +} + +static inline int event_available(struct event_buffer *b, struct event_reader *r) +{ + return (r->next_serial_number < b->next_serial_number); +} + +/** + * get_next_event + * Called by event readers (initiated from user space through the file + * system). + * Sleeps until a new event is available. + */ +int ibmasm_get_next_event(struct service_processor *sp, struct event_reader *reader) +{ + struct event_buffer *buffer = sp->event_buffer; + struct ibmasm_event *event; + unsigned int index; + unsigned long flags; + + if ( wait_event_interruptible(reader->wait, event_available(buffer, reader)) ) + return -ERESTARTSYS; + + if (!event_available(buffer, reader)) + return 0; + + spin_lock_irqsave(&sp->lock, flags); + + index = buffer->next_index; + event = &buffer->events[index]; + while (event->serial_number < reader->next_serial_number) { + index = (index + 1) % IBMASM_NUM_EVENTS; + event = &buffer->events[index]; + } + memcpy(reader->data, event->data, event->data_size); + reader->data_size = event->data_size; + reader->next_serial_number = event->serial_number + 1; + + spin_unlock_irqrestore(&sp->lock, flags); + + return event->data_size; +} + +void ibmasm_event_reader_register(struct service_processor *sp, struct event_reader *reader) +{ + unsigned long flags; + + reader->next_serial_number = sp->event_buffer->next_serial_number; + init_waitqueue_head(&reader->wait); + spin_lock_irqsave(&sp->lock, flags); + list_add(&reader->node, &sp->event_buffer->readers); + spin_unlock_irqrestore(&sp->lock, flags); +} + +void ibmasm_event_reader_unregister(struct service_processor *sp, struct event_reader *reader) +{ + unsigned long flags; + + wake_up_interruptible(&reader->wait); + + spin_lock_irqsave(&sp->lock, flags); + list_del(&reader->node); + spin_unlock_irqrestore(&sp->lock, flags); +} + +int ibmasm_event_buffer_init(struct service_processor *sp) +{ + struct event_buffer *buffer; + struct ibmasm_event *event; + int i; + + buffer = kmalloc(sizeof(struct event_buffer), GFP_KERNEL); + if (!buffer) + return 1; + + buffer->next_index = 0; + buffer->next_serial_number = 1; + + event = buffer->events; + for (i=0; iserial_number = 0; + + INIT_LIST_HEAD(&buffer->readers); + + sp->event_buffer = buffer; + + return 0; +} + +void ibmasm_event_buffer_exit(struct service_processor *sp) +{ + wake_up_event_readers(sp); + kfree(sp->event_buffer); +} diff -urN linux-2.6.1/drivers/misc/ibmasm/heartbeat.c linux-2.6.1-ibmasm/drivers/misc/ibmasm/heartbeat.c --- linux-2.6.1/drivers/misc/ibmasm/heartbeat.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.1-ibmasm/drivers/misc/ibmasm/heartbeat.c 2004-01-22 11:09:09.000000000 -0800 @@ -0,0 +1,91 @@ + +/* + * IBM ASM Service Processor Device Driver + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) IBM Corporation, 2004 + * + * Author: Max Asböck + * + */ + +#include +#include "ibmasm.h" +#include "dot_command.h" + +static int suspend_heartbeats = 0; + +/* + * Once the driver indicates to the service processor that it is running + * - see send_os_state() - the service processor sends periodic heartbeats + * to the driver. The driver must respond to the heartbeats or else the OS + * will be rebooted. + * In the case of a panic the interrupt handler continues to work and thus + * continues to respond to heartbeats, making the service processor believe + * the OS is still running and thus preventing a reboot. + * To prevent this from happening a callback is added the panic_notifier_list. + * Before responding to a heartbeat the driver checks if a panic has happened, + * if yes it suspends heartbeat, causing the service processor to reboot as + * expected. + */ +static int panic_happened(struct notifier_block *n, unsigned long val, void *v) +{ + suspend_heartbeats = 1; + return 0; +} + +static struct notifier_block panic_notifier = { panic_happened, NULL, 1 }; + +void ibmasm_register_panic_notifier(void) +{ + notifier_chain_register(&panic_notifier_list, &panic_notifier); +} + +void ibmasm_unregister_panic_notifier(void) +{ + notifier_chain_unregister(&panic_notifier_list, &panic_notifier); +} + + +int ibmasm_heartbeat_init(struct service_processor *sp) +{ + sp->heartbeat = ibmasm_new_command(HEARTBEAT_BUFFER_SIZE); + if (sp->heartbeat == NULL) + return -ENOMEM; + + return 0; +} + +void ibmasm_heartbeat_exit(struct service_processor *sp) +{ + command_put(sp->heartbeat); +} + +void ibmasm_receive_heartbeat(struct service_processor *sp, void *message, size_t size) +{ + struct command *cmd = sp->heartbeat; + struct dot_command_header *header = (struct dot_command_header *)cmd->buffer; + + if (suspend_heartbeats) + return; + + /* return the received dot command to sender */ + cmd->status = IBMASM_CMD_PENDING; + size = min(size, cmd->buffer_size); + memcpy(cmd->buffer, message, size); + header->type = sp_write; + ibmasm_exec_command(sp, cmd); +} diff -urN linux-2.6.1/drivers/misc/ibmasm/i2o.h linux-2.6.1-ibmasm/drivers/misc/ibmasm/i2o.h --- linux-2.6.1/drivers/misc/ibmasm/i2o.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.1-ibmasm/drivers/misc/ibmasm/i2o.h 2004-01-23 15:41:15.000000000 -0800 @@ -0,0 +1,77 @@ +/* + * IBM ASM Service Processor Device Driver + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) IBM Corporation, 2004 + * + * Author: Max Asböck + * + */ + +#pragma pack(1) +struct i2o_header { + u8 version; + u8 message_flags; + u16 message_size; + u8 target; + u8 initiator_and_target; + u8 initiator; + u8 function; + u32 initiator_context; +}; +#pragma pack() + +#define I2O_HEADER_TEMPLATE \ + { .version = 0x01, \ + .message_flags = 0x00, \ + .function = 0xFF, \ + .initiator = 0x00, \ + .initiator_and_target = 0x40, \ + .target = 0x00, \ + .initiator_context = 0x0 } + +#define I2O_MESSAGE_SIZE 0x1000 +#define I2O_COMMAND_SIZE (I2O_MESSAGE_SIZE - sizeof(struct i2o_header)) + +#pragma pack(1) +struct i2o_message { + struct i2o_header header; + void *data; +}; +#pragma pack() + +static inline unsigned short outgoing_message_size(unsigned int data_size) +{ + unsigned int size; + unsigned short i2o_size; + + if (data_size > I2O_COMMAND_SIZE) + data_size = I2O_COMMAND_SIZE; + + size = sizeof(struct i2o_header) + data_size; + + i2o_size = size / sizeof(u32); + + if (size % sizeof(u32)) + i2o_size++; + + return i2o_size; +} + +static inline u32 incoming_data_size(struct i2o_message *i2o_message) +{ + return (sizeof(u32) * i2o_message->header.message_size); +} diff -urN linux-2.6.1/drivers/misc/ibmasm/ibmasm.h linux-2.6.1-ibmasm/drivers/misc/ibmasm/ibmasm.h --- linux-2.6.1/drivers/misc/ibmasm/ibmasm.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.1-ibmasm/drivers/misc/ibmasm/ibmasm.h 2004-01-26 10:05:25.000000000 -0800 @@ -0,0 +1,227 @@ + +/* + * IBM ASM Service Processor Device Driver + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) IBM Corporation, 2004 + * + * Author: Max Asböck + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Driver identification */ +#define DRIVER_NAME "ibmasm" +#define DRIVER_VERSION "0.3" +#define DRIVER_AUTHOR "Max Asbock" +#define DRIVER_DESC "IBM ASM Service Processor Driver" + +#define err(msg) printk(KERN_ERR "%s: " msg "\n", DRIVER_NAME) +#define info(msg) printk(KERN_INFO "%s: " msg "\n", DRIVER_NAME) + + +#define IBMASM_CMD_PENDING 0 +#define IBMASM_CMD_COMPLETE 1 +#define IBMASM_CMD_FAILED 2 + +#define IBMASM_CMD_TIMEOUT_NORMAL 45 +#define IBMASM_CMD_TIMEOUT_EXTRA 240 + +#define IBMASM_CMD_MAX_BUFFER_SIZE 0x4000 + +#define REVERSE_HEARTBEAT_TIMEOUT 120 + +#define HEARTBEAT_BUFFER_SIZE 0x400 + +#ifdef IA64 +#define IBMASM_DRIVER_VPD "Lin64 6.08 " +#else +#define IBMASM_DRIVER_VPD "Lin32 6.08 " +#endif + +#define SYSTEM_STATE_OS_UP 5 +#define SYSTEM_STATE_OS_DOWN 4 + +#define IBMASM_IOCTL_MAGIC 'f' +#define IBMASM_IO_CANCEL _IO(IBMASM_IOCTL_MAGIC, 0) + +#define IBMASM_NAME_SIZE 16 + +#define IBMASM_NUM_EVENTS 10 +#define IBMASM_EVENT_MAX_SIZE 2048u + + +struct command { + struct list_head queue_node; + wait_queue_head_t wait; + unsigned char *buffer; + size_t buffer_size; + int status; + struct kobject kobj; +}; +#define to_command(c) container_of(c, struct command, kobj) + +static inline void command_put(struct command *cmd) +{ + kobject_put(&cmd->kobj); +} + +static inline void command_get(struct command *cmd) +{ + kobject_get(&cmd->kobj); +} + + +struct ibmasm_event { + unsigned int serial_number; + unsigned int data_size; + unsigned char data[IBMASM_EVENT_MAX_SIZE]; +}; + +struct event_buffer { + struct ibmasm_event events[IBMASM_NUM_EVENTS]; + unsigned int next_serial_number; + unsigned int next_index; + struct list_head readers; +}; + +struct event_reader { + unsigned int next_serial_number; + wait_queue_head_t wait; + struct list_head node; + unsigned int data_size; + unsigned char data[IBMASM_EVENT_MAX_SIZE]; +}; + +struct reverse_heartbeat { + wait_queue_head_t wait; + unsigned int stopped; +}; + + +/* remote console events */ +struct mouse_event { + long x; + long y; + unsigned char buttons; + unsigned char transitions; +}; + +struct keyboard_event { + unsigned long key_code; + unsigned char key_down; +}; + +struct remote_event { + unsigned long type; + union { + struct mouse_event mouse; + struct keyboard_event keyboard; + } data; +}; + +#define DRIVER_REMOTE_QUEUE_SIZE 240 + +struct remote_queue { + struct remote_event *start; + struct remote_event *end; + struct remote_event *reader; + struct remote_event *writer; + unsigned int size; + int open; + wait_queue_head_t wait; +}; + + +struct service_processor { + struct list_head node; + spinlock_t lock; + void *base_address; + unsigned int irq; + struct command *current_command; + struct command *heartbeat; + struct list_head command_queue; + struct event_buffer *event_buffer; + char dirname[IBMASM_NAME_SIZE]; + char devname[IBMASM_NAME_SIZE]; + unsigned int number; + struct remote_queue remote_queue; + int serial_line; + struct device *dev; +}; + +/* command processing */ +extern struct command *ibmasm_new_command(size_t buffer_size); +extern void ibmasm_exec_command(struct service_processor *sp, struct command *cmd); +extern void ibmasm_wait_for_response(struct command *cmd, int timeout); +extern void ibmasm_receive_command_response(struct service_processor *sp, void *response, size_t size); + +/* event processing */ +extern int ibmasm_event_buffer_init(struct service_processor *sp); +extern void ibmasm_event_buffer_exit(struct service_processor *sp); +extern void ibmasm_receive_event(struct service_processor *sp, void *data, size_t data_size); +extern void ibmasm_event_reader_register(struct service_processor *sp, struct event_reader *reader); +extern void ibmasm_event_reader_unregister(struct service_processor *sp, struct event_reader *reader); +extern int ibmasm_get_next_event(struct service_processor *sp, struct event_reader *reader); + +/* heartbeat - from SP to OS */ +extern void ibmasm_register_panic_notifier(void); +extern void ibmasm_unregister_panic_notifier(void); +extern int ibmasm_heartbeat_init(struct service_processor *sp); +extern void ibmasm_heartbeat_exit(struct service_processor *sp); +extern void ibmasm_receive_heartbeat(struct service_processor *sp, void *message, size_t size); + +/* reverse heartbeat - from OS to SP */ +extern void ibmasm_init_reverse_heartbeat(struct service_processor *sp, struct reverse_heartbeat *rhb); +extern int ibmasm_start_reverse_heartbeat(struct service_processor *sp, struct reverse_heartbeat *rhb); +extern void ibmasm_stop_reverse_heartbeat(struct reverse_heartbeat *rhb); + +/* dot commands */ +extern void ibmasm_receive_message(struct service_processor *sp, void *data, int data_size); +extern int ibmasm_send_driver_vpd(struct service_processor *sp); +extern int ibmasm_send_os_state(struct service_processor *sp, int os_state); + +/* low level message processing */ +extern int ibmasm_send_i2o_message(struct service_processor *sp); +extern irqreturn_t ibmasm_interrupt_handler(int irq, void * dev_id, struct pt_regs *regs); + +/* remote console */ +extern void ibmasm_handle_mouse_interrupt(struct service_processor *sp); +extern int ibmasm_init_remote_queue(struct service_processor *sp); +extern void ibmasm_free_remote_queue(struct service_processor *sp); +extern void ibmasm_advance_reader(struct remote_queue *q, unsigned int n); +extern size_t ibmasm_events_available(struct remote_queue *q); + +/* file system */ +extern int ibmasmfs_register(void); +extern void ibmasmfs_unregister(void); +extern void ibmasmfs_add_sp(struct service_processor *sp); + +/* uart */ +extern void ibmasm_register_uart(struct service_processor *sp); +extern void ibmasm_unregister_uart(struct service_processor *sp); diff -urN linux-2.6.1/drivers/misc/ibmasm/lowlevel.c linux-2.6.1-ibmasm/drivers/misc/ibmasm/lowlevel.c --- linux-2.6.1/drivers/misc/ibmasm/lowlevel.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.1-ibmasm/drivers/misc/ibmasm/lowlevel.c 2004-01-14 16:41:31.000000000 -0800 @@ -0,0 +1,81 @@ +/* + * IBM ASM Service Processor Device Driver + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) IBM Corporation, 2004 + * + * Author: Max Asböck + * + */ + +#include "ibmasm.h" +#include "lowlevel.h" +#include "i2o.h" +#include "dot_command.h" +#include "remote.h" + +static struct i2o_header header = I2O_HEADER_TEMPLATE; + + +int ibmasm_send_i2o_message(struct service_processor *sp) +{ + u32 mfa; + unsigned int command_size; + struct i2o_message *message; + struct command *command = sp->current_command; + + mfa = get_mfa_inbound(sp->base_address); + if ( !mfa ) + return 1; + + command_size = get_dot_command_size(command->buffer); + header.message_size = outgoing_message_size(command_size); + + message = get_i2o_message(sp->base_address, mfa); + + memcpy(&message->header, &header, sizeof(struct i2o_header)); + memcpy(&message->data, command->buffer, command_size); + + set_mfa_inbound(sp->base_address, mfa); + + return 0; +} + +irqreturn_t ibmasm_interrupt_handler(int irq, void * dev_id, struct pt_regs *regs) +{ + u32 mfa; + struct service_processor *sp = (struct service_processor *)dev_id; + void *base_address = sp->base_address; + + if (!sp_interrupt_pending(base_address)) + return IRQ_NONE; + + if ( mouse_interrupt_pending(sp) ) { + ibmasm_handle_mouse_interrupt(sp); + mfa = get_mfa_outbound(base_address); + clear_mouse_interrupt(sp); + set_mfa_outbound(base_address, mfa); + return IRQ_HANDLED; + } + + mfa = get_mfa_outbound(base_address); + if ( valid_mfa(mfa) ) { + struct i2o_message *msg = get_i2o_message(base_address, mfa); + ibmasm_receive_message(sp, &msg->data, incoming_data_size(msg)); + } + set_mfa_outbound(base_address, mfa); + return IRQ_HANDLED; +} diff -urN linux-2.6.1/drivers/misc/ibmasm/lowlevel.h linux-2.6.1-ibmasm/drivers/misc/ibmasm/lowlevel.h --- linux-2.6.1/drivers/misc/ibmasm/lowlevel.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.1-ibmasm/drivers/misc/ibmasm/lowlevel.h 2004-01-14 16:41:42.000000000 -0800 @@ -0,0 +1,137 @@ +/* + * IBM ASM Service Processor Device Driver + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) IBM Corporation, 2004 + * + * Author: Max Asböck + * + */ + +/* Condor service processor specific hardware definitions */ + +#ifndef __IBMASM_CONDOR_H__ +#define __IBMASM_CONDOR_H__ + +#include + +#define VENDORID_IBM 0x1014 +#define DEVICEID_RSA 0x010F + +#define GET_MFA_ADDR(x) (x & 0xFFFFFF00) + +#define MAILBOX_FULL(x) (x & 0x00000001) + +#define NO_MFAS_AVAILABLE 0xFFFFFFFF + + +#define INBOUND_QUEUE_PORT 0x40 /* contains address of next free MFA */ +#define OUTBOUND_QUEUE_PORT 0x44 /* contains address of posted MFA */ + +#define SP_INTR_MASK 0x00000008 +#define UART_INTR_MASK 0x00000010 + +#define INTR_STATUS_REGISTER 0x13A0 +#define INTR_CONTROL_REGISTER 0x13A4 + +#define SCOUT_COM_A_BASE 0x0000 +#define SCOUT_COM_B_BASE 0x0100 +#define SCOUT_COM_C_BASE 0x0200 +#define SCOUT_COM_D_BASE 0x0300 + +static inline int sp_interrupt_pending(void *base_address) +{ + return SP_INTR_MASK & readl(base_address + INTR_STATUS_REGISTER); +} + +static inline int uart_interrupt_pending(void *base_address) +{ + return UART_INTR_MASK & readl(base_address + INTR_STATUS_REGISTER); +} + +static inline void ibmasm_enable_interrupts(void *base_address, int mask) +{ + void *ctrl_reg = base_address + INTR_CONTROL_REGISTER; + writel( readl(ctrl_reg) & ~mask, ctrl_reg); +} + +static inline void ibmasm_disable_interrupts(void *base_address, int mask) +{ + void *ctrl_reg = base_address + INTR_CONTROL_REGISTER; + writel( readl(ctrl_reg) | mask, ctrl_reg); +} + +static inline void enable_sp_interrupts(void *base_address) +{ + ibmasm_enable_interrupts(base_address, SP_INTR_MASK); +} + +static inline void disable_sp_interrupts(void *base_address) +{ + ibmasm_disable_interrupts(base_address, SP_INTR_MASK); +} + +static inline void enable_uart_interrupts(void *base_address) +{ + ibmasm_enable_interrupts(base_address, UART_INTR_MASK); +} + +static inline void disable_uart_interrupts(void *base_address) +{ + ibmasm_disable_interrupts(base_address, UART_INTR_MASK); +} + +#define valid_mfa(mfa) ( (mfa) != NO_MFAS_AVAILABLE ) + +static inline u32 get_mfa_outbound(void *base_address) +{ + int retry; + u32 mfa; + + for (retry=0; retry<=10; retry++) { + mfa = readl(base_address + OUTBOUND_QUEUE_PORT); + if ( valid_mfa(mfa) ) + break; + } + return mfa; +} + +static inline void set_mfa_outbound(void *base_address, u32 mfa) +{ + writel(mfa, base_address + OUTBOUND_QUEUE_PORT); +} + +static inline u32 get_mfa_inbound(void *base_address) +{ + u32 mfa = readl(base_address + INBOUND_QUEUE_PORT); + + if ( MAILBOX_FULL(mfa) ) + return 0; + + return mfa; +} + +static inline void set_mfa_inbound(void *base_address, u32 mfa) +{ + writel(mfa, base_address + INBOUND_QUEUE_PORT); +} + +static inline struct i2o_message *get_i2o_message(void *base_address, u32 mfa) +{ + return (struct i2o_message *)(GET_MFA_ADDR(mfa) + base_address); +} + +#endif /* __IBMASM_CONDOR_H__ */ diff -urN linux-2.6.1/drivers/misc/ibmasm/Makefile linux-2.6.1-ibmasm/drivers/misc/ibmasm/Makefile --- linux-2.6.1/drivers/misc/ibmasm/Makefile 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.1-ibmasm/drivers/misc/ibmasm/Makefile 2004-01-14 14:50:21.000000000 -0800 @@ -0,0 +1,13 @@ + +obj-$(CONFIG_IBM_ASM) := ibmasm.o + +ibmasm-objs := module.o \ + ibmasmfs.o \ + event.o \ + command.o \ + remote.o \ + heartbeat.o \ + r_heartbeat.o \ + dot_command.o \ + lowlevel.o \ + uart.o diff -urN linux-2.6.1/drivers/misc/ibmasm/module.c linux-2.6.1-ibmasm/drivers/misc/ibmasm/module.c --- linux-2.6.1/drivers/misc/ibmasm/module.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.1-ibmasm/drivers/misc/ibmasm/module.c 2004-01-20 13:05:38.000000000 -0800 @@ -0,0 +1,212 @@ + +/* + * IBM ASM Service Processor Device Driver + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) IBM Corporation, 2004 + * + * Author: Max Asböck + * + * This driver is based on code originally written by Pete Reynolds + * and others. + * + */ + +/* + * The ASM device driver does the following things: + * + * 1) When loaded it sends a message to the service processor, + * indicating that an OS is * running. This causes the service processor + * to send periodic heartbeats to the OS. + * + * 2) Answers the periodic heartbeats sent by the service processor. + * Failure to do so would result in system reboot. + * + * 3) Acts as a pass through for dot commands sent from user applications. + * The interface for this is the ibmasmfs file system. + * + * 4) Allows user applications to register for event notification. Events + * are sent to the driver through interrupts. They can be read from user + * space through the ibmasmfs file system. + * + * 5) Allows user space applications to send heartbeats to the service + * processor (aka reverse heartbeats). Again this happens through ibmasmfs. + * + * 6) Handles remote mouse and keyboard event interrupts and makes them + * available to user applications through ibmasmfs. + * + */ + +#include +#include +#include "ibmasm.h" +#include "lowlevel.h" +#include "remote.h" + + +static int __init ibmasm_init_one(struct pci_dev *pdev, const struct pci_device_id *id) +{ + int result = 0; + struct service_processor *sp; + + sp = kmalloc(sizeof(struct service_processor), GFP_KERNEL); + if (sp == NULL) { + dev_err(&pdev->dev, "Failed to allocate memory\n"); + return -ENOMEM; + } + memset(sp, 0, sizeof(struct service_processor)); + + pci_set_drvdata(pdev, (void *)sp); + sp->dev = &pdev->dev; + sp->number = pdev->bus->number; + snprintf(sp->dirname, IBMASM_NAME_SIZE, "%d", sp->number); + snprintf(sp->devname, IBMASM_NAME_SIZE, "%s%d", DRIVER_NAME, sp->number); + + if (ibmasm_event_buffer_init(sp)) { + dev_err(sp->dev, "Failed to allocate event buffer\n"); + result = -ENOMEM; + goto error_eventbuffer; + } + + if (ibmasm_heartbeat_init(sp)) { + dev_err(sp->dev, "Failed to allocate heartbeat command\n"); + result = -ENOMEM; + goto error_heartbeat; + } + + sp->irq = pdev->irq; + sp->base_address = ioremap(pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); + if (sp->base_address == 0) { + dev_err(sp->dev, "Failed to ioremap pci memory\n"); + result = -ENODEV; + goto error_ioremap; + } + + result = ibmasm_init_remote_queue(sp); + if (result) { + dev_err(sp->dev, "Failed to initialize remote queue\n"); + goto error_remote_queue; + } + + sp->lock = SPIN_LOCK_UNLOCKED; + INIT_LIST_HEAD(&sp->command_queue); + + result = request_irq(sp->irq, ibmasm_interrupt_handler, SA_SHIRQ, sp->devname, (void*)sp); + if (result) { + dev_err(sp->dev, "Failed to register interrupt handler\n"); + goto error_request_irq; + } + + enable_sp_interrupts(sp->base_address); + disable_mouse_interrupts(sp); + + result = ibmasm_send_driver_vpd(sp); + if (result) { + dev_err(sp->dev, "Failed to send driver VPD to service processor\n"); + goto error_send_message; + } + result = ibmasm_send_os_state(sp, SYSTEM_STATE_OS_UP); + if (result) { + dev_err(sp->dev, "Failed to send OS state to service processor\n"); + goto error_send_message; + } + ibmasmfs_add_sp(sp); + + ibmasm_register_uart(sp); + + return 0; + +error_send_message: + disable_sp_interrupts(sp->base_address); + free_irq(sp->irq, (void *)sp); +error_request_irq: + ibmasm_free_remote_queue(sp); +error_remote_queue: + iounmap(sp->base_address); +error_ioremap: + ibmasm_heartbeat_exit(sp); +error_heartbeat: + ibmasm_event_buffer_exit(sp); +error_eventbuffer: + kfree(sp); + + return result; +} + +static void __exit ibmasm_remove_one(struct pci_dev *pdev) +{ + struct service_processor *sp = (struct service_processor *)pci_get_drvdata(pdev); + + ibmasm_unregister_uart(sp); + ibmasm_send_os_state(sp, SYSTEM_STATE_OS_DOWN); + disable_sp_interrupts(sp->base_address); + disable_mouse_interrupts(sp); + free_irq(sp->irq, (void *)sp); + ibmasm_heartbeat_exit(sp); + ibmasm_free_remote_queue(sp); + iounmap(sp->base_address); + ibmasm_event_buffer_exit(sp); + kfree(sp); +} + +static struct pci_device_id ibmasm_pci_table[] = +{ + { PCI_DEVICE(VENDORID_IBM, DEVICEID_RSA) }, + {}, +}; + +static struct pci_driver ibmasm_driver = { + .name = DRIVER_NAME, + .id_table = ibmasm_pci_table, + .probe = ibmasm_init_one, + .remove = __devexit_p(ibmasm_remove_one), +}; + +static void __exit ibmasm_exit (void) +{ + ibmasm_unregister_panic_notifier(); + ibmasmfs_unregister(); + pci_unregister_driver(&ibmasm_driver); + info(DRIVER_DESC " version " DRIVER_VERSION " unloaded"); +} + +static int __init ibmasm_init(void) +{ + int result; + + result = ibmasmfs_register(); + if (result) { + err("Failed to register ibmasmfs file system"); + return result; + } + result = pci_register_driver(&ibmasm_driver); + if (result <= 0) { + pci_unregister_driver(&ibmasm_driver); + ibmasmfs_unregister(); + return -ENODEV; + } + ibmasm_register_panic_notifier(); + info(DRIVER_DESC " version " DRIVER_VERSION " loaded"); + return 0; +} + +module_init(ibmasm_init); +module_exit(ibmasm_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff -urN linux-2.6.1/drivers/misc/ibmasm/remote.c linux-2.6.1-ibmasm/drivers/misc/ibmasm/remote.c --- linux-2.6.1/drivers/misc/ibmasm/remote.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.1-ibmasm/drivers/misc/ibmasm/remote.c 2004-01-14 16:42:01.000000000 -0800 @@ -0,0 +1,156 @@ + +/* + * IBM ASM Service Processor Device Driver + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) IBM Corporation, 2004 + * + * Author: Max Asböck + * + */ + +/* Remote mouse and keyboard event handling functions */ + +#include "ibmasm.h" +#include "remote.h" + +int ibmasm_init_remote_queue(struct service_processor *sp) +{ + struct remote_queue *q = &sp->remote_queue; + + disable_mouse_interrupts(sp); + + q->open = 0; + q->size = 0; + + q->start = kmalloc(DRIVER_REMOTE_QUEUE_SIZE * sizeof(struct remote_event), GFP_KERNEL); + if (q->start == 0) + return -ENOMEM; + + q->end = q->start + DRIVER_REMOTE_QUEUE_SIZE; + q->reader = q->start; + q->writer = q->start; + q->size = DRIVER_REMOTE_QUEUE_SIZE; + init_waitqueue_head(&q->wait); + + return 0; +} + +void ibmasm_free_remote_queue(struct service_processor *sp) +{ + if (sp->remote_queue.start) + kfree(sp->remote_queue.start); +} + +void ibmasm_advance_reader(struct remote_queue *q, unsigned int n) +{ + q->reader += n; + if (q->reader >= q->end) + q->reader -= q->size; +} + +size_t ibmasm_events_available(struct remote_queue *q) +{ + ssize_t diff = q->writer - q->reader; + + if (diff >= 0) + return diff; + else + return (q->end - q->reader); +} + + +static int space_free(struct remote_queue *q) +{ + if (q->reader == q->writer) + return q->size - 1; + + return ( (q->reader + q->size - q->writer) % q->size ) - 1; +} + +static void set_mouse_event(struct remote_input *input, struct mouse_event *mouse) +{ + static char last_buttons = 0; + + mouse->x = input->data.mouse.x; + mouse->y = input->data.mouse.y; + + if (input->mouse_buttons == REMOTE_MOUSE_DOUBLE_CLICK) { + mouse->buttons = REMOTE_MOUSE_DOUBLE_CLICK; + last_buttons = 0; + return; + } + mouse->transitions = last_buttons ^ input->mouse_buttons; + mouse->buttons = input->mouse_buttons; + + last_buttons = input->mouse_buttons; +} + +static void set_keyboard_event(struct remote_input *input, struct keyboard_event *keyboard) +{ + keyboard->key_code = input->data.keyboard.key_code; + keyboard->key_down = input->data.keyboard.key_down; +} + +static int add_to_driver_queue(struct remote_queue *q, struct remote_input *input) +{ + struct remote_event *event = q->writer; + + if ( space_free(q) < 1 ) { + return 1; + } + + switch(input->type) { + case (INPUT_TYPE_MOUSE): + event->type = INPUT_TYPE_MOUSE; + set_mouse_event(input, &event->data.mouse); + break; + case (INPUT_TYPE_KEYBOARD): + event->type = INPUT_TYPE_KEYBOARD; + set_keyboard_event(input, &event->data.keyboard); + break; + default: + return 0; + } + event->type = input->type; + + q->writer++; + if (q->writer == q->end) + q->writer = q->start; + + return 0; +} + + +void ibmasm_handle_mouse_interrupt(struct service_processor *sp) +{ + unsigned long reader; + unsigned long writer; + struct remote_input input; + + reader = get_queue_reader(sp); + writer = get_queue_writer(sp); + + while (reader != writer) { + memcpy(&input, (void *)get_queue_entry(sp, reader), sizeof(struct remote_input)); + + if ( add_to_driver_queue(&sp->remote_queue, &input) ) + break; + + reader = advance_queue_reader(sp, reader); + } + wake_up_interruptible(&sp->remote_queue.wait); +} diff -urN linux-2.6.1/drivers/misc/ibmasm/remote.h linux-2.6.1-ibmasm/drivers/misc/ibmasm/remote.h --- linux-2.6.1/drivers/misc/ibmasm/remote.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.1-ibmasm/drivers/misc/ibmasm/remote.h 2004-01-14 16:42:14.000000000 -0800 @@ -0,0 +1,119 @@ + +/* + * IBM ASM Service Processor Device Driver + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) IBM Corporation, 2004 + * + * Author: Max Asböck + * + * Orignally written by Pete Reynolds + */ + +#ifndef _IBMASM_REMOTE_H_ +#define _IBMASM_REMOTE_H_ + +#include + +/* pci offsets */ +#define CONDOR_MOUSE_DATA 0x000AC000 +#define CONDOR_MOUSE_ISR_CONTROL 0x00 +#define CONDOR_MOUSE_ISR_STATUS 0x04 +#define CONDOR_MOUSE_Q_READER 0x08 +#define CONDOR_MOUSE_Q_WRITER 0x0C +#define CONDOR_MOUSE_Q_BEGIN 0x10 +#define CONDOR_MOUSE_MAX_X 0x14 +#define CONDOR_MOUSE_MAX_Y 0x18 + +#define CONDOR_INPUT_DESKTOP_INFO 0x1F0 +#define CONDOR_INPUT_DISPLAY_RESX 0x1F4 +#define CONDOR_INPUT_DISPLAY_RESY 0x1F8 +#define CONDOR_INPUT_DISPLAY_BITS 0x1FC +#define CONDOR_OUTPUT_VNC_STATUS 0x200 + +#define CONDOR_MOUSE_INTR_STATUS_MASK 0x00000001 + +#define INPUT_TYPE_MOUSE 0x1 +#define INPUT_TYPE_KEYBOARD 0x2 + + +/* mouse button states received from SP */ +#define REMOTE_MOUSE_DOUBLE_CLICK 0xF0 +#define REMOTE_MOUSE_BUTTON_LEFT 0x01 +#define REMOTE_MOUSE_BUTTON_MIDDLE 0x02 +#define REMOTE_MOUSE_BUTTON_RIGHT 0x04 + + +struct mouse_input { + unsigned short y; + unsigned short x; +}; + + +struct keyboard_input { + unsigned short key_code; + unsigned char key_flag; + unsigned char key_down; +}; + + + +struct remote_input { + union { + struct mouse_input mouse; + struct keyboard_input keyboard; + } data; + + unsigned char type; + unsigned char pad1; + unsigned char mouse_buttons; + unsigned char pad3; +}; + +#define mouse_addr(sp) sp->base_address + CONDOR_MOUSE_DATA +#define display_width(sp) mouse_addr(sp) + CONDOR_INPUT_DISPLAY_RESX +#define display_height(sp) mouse_addr(sp) + CONDOR_INPUT_DISPLAY_RESY +#define display_depth(sp) mouse_addr(sp) + CONDOR_INPUT_DISPLAY_BITS +#define vnc_status(sp) mouse_addr(sp) + CONDOR_OUTPUT_VNC_STATUS + +#define mouse_interrupt_pending(sp) readl(mouse_addr(sp) + CONDOR_MOUSE_ISR_STATUS) +#define clear_mouse_interrupt(sp) writel(0, mouse_addr(sp) + CONDOR_MOUSE_ISR_STATUS) +#define enable_mouse_interrupts(sp) writel(1, mouse_addr(sp) + CONDOR_MOUSE_ISR_CONTROL) +#define disable_mouse_interrupts(sp) writel(0, mouse_addr(sp) + CONDOR_MOUSE_ISR_CONTROL) + +/* remote input queue operations */ +#define REMOTE_QUEUE_SIZE 60 + +#define get_queue_writer(sp) readl(mouse_addr(sp) + CONDOR_MOUSE_Q_WRITER) +#define get_queue_reader(sp) readl(mouse_addr(sp) + CONDOR_MOUSE_Q_READER) +#define set_queue_reader(sp, reader) writel(reader, mouse_addr(sp) + CONDOR_MOUSE_Q_READER) + +#define queue_begin mouse_addr(sp) + CONDOR_MOUSE_Q_BEGIN + +#define get_queue_entry(sp, read_index) \ + queue_begin + read_index * sizeof(struct remote_input) + +static inline int advance_queue_reader(struct service_processor *sp, unsigned long reader) +{ + reader++; + if (reader == REMOTE_QUEUE_SIZE) + reader = 0; + + set_queue_reader(sp, reader); + return reader; +} + +#endif /* _IBMASM_REMOTE_H_ */ diff -urN linux-2.6.1/drivers/misc/ibmasm/r_heartbeat.c linux-2.6.1-ibmasm/drivers/misc/ibmasm/r_heartbeat.c --- linux-2.6.1/drivers/misc/ibmasm/r_heartbeat.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.1-ibmasm/drivers/misc/ibmasm/r_heartbeat.c 2004-01-22 11:08:48.000000000 -0800 @@ -0,0 +1,95 @@ + +/* + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) IBM Corporation, 2004 + * + * Author: Max Asböck + * + */ + +#include "ibmasm.h" +#include "dot_command.h" + +/* + * Reverse Heartbeat, i.e. heartbeats sent from the driver to the + * service processor. + * These heartbeats are initiated by user level programs. + */ + +/* the reverse heartbeat dot command */ +static struct { + struct dot_command_header header; + unsigned char command[3]; +} rhb_dot_cmd = { + .header = { + .type = sp_read, + .command_size = 3, + .data_size = 0, + .status = 0 + }, + .command = { 4, 3, 6 } +}; + +void ibmasm_init_reverse_heartbeat(struct service_processor *sp, struct reverse_heartbeat *rhb) +{ + init_waitqueue_head(&rhb->wait); + rhb->stopped = 0; +} + +/** + * start_reverse_heartbeat + * Loop forever, sending a reverse heartbeat dot command to the service + * processor, then sleeping. The loop comes to an end if the service + * processor fails to respond 3 times or we were interrupted. + */ +int ibmasm_start_reverse_heartbeat(struct service_processor *sp, struct reverse_heartbeat *rhb) +{ + struct command *cmd; + int times_failed = 0; + + cmd = ibmasm_new_command(sizeof rhb_dot_cmd); + if (!cmd) + return -ENOMEM; + + while ( times_failed < 3 && !rhb->stopped ) { + memcpy(cmd->buffer, (void *)&rhb_dot_cmd, sizeof rhb_dot_cmd); + cmd->status = IBMASM_CMD_PENDING; + ibmasm_exec_command(sp, cmd); + ibmasm_wait_for_response(cmd, IBMASM_CMD_TIMEOUT_NORMAL); + + if (cmd->status != IBMASM_CMD_COMPLETE) + times_failed++; + + wait_event_interruptible_timeout(rhb->wait, + rhb->stopped, + REVERSE_HEARTBEAT_TIMEOUT * HZ); + if (signal_pending(current)) { + command_put(cmd); + return -EINTR; + } + } + command_put(cmd); + if (rhb->stopped) + return -EINTR; + + return 1; +} + +void ibmasm_stop_reverse_heartbeat(struct reverse_heartbeat *rhb) +{ + rhb->stopped = 1; + wake_up_interruptible(&rhb->wait); +} diff -urN linux-2.6.1/drivers/misc/ibmasm/uart.c linux-2.6.1-ibmasm/drivers/misc/ibmasm/uart.c --- linux-2.6.1/drivers/misc/ibmasm/uart.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.1-ibmasm/drivers/misc/ibmasm/uart.c 2004-01-19 14:02:56.000000000 -0800 @@ -0,0 +1,72 @@ + +/* + * IBM ASM Service Processor Device Driver + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) IBM Corporation, 2004 + * + * Author: Max Asböck + * + */ + +#include +#include +#include +#include +#include +#include "ibmasm.h" +#include "lowlevel.h" + + +void ibmasm_register_uart(struct service_processor *sp) +{ + struct serial_struct serial; + unsigned char *iomem_base; + + iomem_base = sp->base_address + SCOUT_COM_B_BASE; + + /* read the uart scratch register to determine if the UART + * is dedicated to the service processor or if the OS can use it + */ + if ( 0 == readl(iomem_base + UART_SCR) ) { + dev_info(sp->dev, "IBM SP UART not registered, owned by service processor\n"); + sp->serial_line = -1; + return; + } + + memset(&serial, 0, sizeof(serial)); + serial.irq = sp->irq; + serial.baud_base = 3686400 / 16; + serial.flags = UPF_AUTOPROBE | UPF_SHARE_IRQ; + serial.io_type = UPIO_MEM; + serial.iomem_base = iomem_base; + + sp->serial_line = register_serial(&serial); + if (sp->serial_line < 0) { + dev_err(sp->dev, "Failed to register serial port\n"); + return; + } + enable_uart_interrupts(sp->base_address); +} + +void ibmasm_unregister_uart(struct service_processor *sp) +{ + if (sp->serial_line < 0) + return; + + disable_uart_interrupts(sp->base_address); + unregister_serial(sp->serial_line); +} diff -urN linux-2.6.1/drivers/misc/Kconfig linux-2.6.1-ibmasm/drivers/misc/Kconfig --- linux-2.6.1/drivers/misc/Kconfig 2004-01-08 22:59:18.000000000 -0800 +++ linux-2.6.1-ibmasm/drivers/misc/Kconfig 2004-01-15 10:00:13.000000000 -0800 @@ -4,5 +4,21 @@ menu "Misc devices" +config IBM_ASM + tristate "Device driver for IBM RSA service processor" + default n + ---help--- + This option enables device driver support for in-band access to the + IBM RSA (Condor) service processor in eServer xSeries systems. + The ibmasm device driver allows user space application to access + ASM (Advanced Systems Management) functions on the service + processor. The driver is meant to be used in conjunction with + a user space API. + The ibmasm driver also enables the OS to use the UART on the + service processor board as a regular serial port. + + + If unsure, say N. + endmenu diff -urN linux-2.6.1/drivers/misc/Makefile linux-2.6.1-ibmasm/drivers/misc/Makefile --- linux-2.6.1/drivers/misc/Makefile 2004-01-08 23:00:03.000000000 -0800 +++ linux-2.6.1-ibmasm/drivers/misc/Makefile 2004-01-14 17:15:39.000000000 -0800 @@ -1,4 +1,6 @@ # # Makefile for misc devices that really don't fit anywhere else. # -obj- := misc.o # Dummy rule to force built-in.o to be made +obj- := misc.o # Dummy rule to force built-in.o to be made + +obj-$(CONFIG_IBM_ASM) += ibmasm/