aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/ibmasm
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/misc/ibmasm
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/misc/ibmasm')
-rw-r--r--drivers/misc/ibmasm/Makefile15
-rw-r--r--drivers/misc/ibmasm/command.c175
-rw-r--r--drivers/misc/ibmasm/dot_command.c146
-rw-r--r--drivers/misc/ibmasm/dot_command.h78
-rw-r--r--drivers/misc/ibmasm/event.c169
-rw-r--r--drivers/misc/ibmasm/heartbeat.c91
-rw-r--r--drivers/misc/ibmasm/i2o.h77
-rw-r--r--drivers/misc/ibmasm/ibmasm.h229
-rw-r--r--drivers/misc/ibmasm/ibmasmfs.c709
-rw-r--r--drivers/misc/ibmasm/lowlevel.c81
-rw-r--r--drivers/misc/ibmasm/lowlevel.h137
-rw-r--r--drivers/misc/ibmasm/module.c228
-rw-r--r--drivers/misc/ibmasm/r_heartbeat.c98
-rw-r--r--drivers/misc/ibmasm/remote.c152
-rw-r--r--drivers/misc/ibmasm/remote.h119
-rw-r--r--drivers/misc/ibmasm/uart.c72
16 files changed, 2576 insertions, 0 deletions
diff --git a/drivers/misc/ibmasm/Makefile b/drivers/misc/ibmasm/Makefile
new file mode 100644
index 000000000000..9e63ade5ffd6
--- /dev/null
+++ b/drivers/misc/ibmasm/Makefile
@@ -0,0 +1,15 @@
1
2obj-$(CONFIG_IBM_ASM) := ibmasm.o
3
4ibmasm-y := module.o \
5 ibmasmfs.o \
6 event.o \
7 command.o \
8 remote.o \
9 heartbeat.o \
10 r_heartbeat.o \
11 dot_command.o \
12 lowlevel.o
13
14ibmasm-$(CONFIG_SERIAL_8250) += uart.o
15
diff --git a/drivers/misc/ibmasm/command.c b/drivers/misc/ibmasm/command.c
new file mode 100644
index 000000000000..245b0058381d
--- /dev/null
+++ b/drivers/misc/ibmasm/command.c
@@ -0,0 +1,175 @@
1
2/*
3 * IBM ASM Service Processor Device Driver
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 *
19 * Copyright (C) IBM Corporation, 2004
20 *
21 * Author: Max Asböck <amax@us.ibm.com>
22 *
23 */
24
25#include "ibmasm.h"
26
27static void exec_next_command(struct service_processor *sp);
28static void free_command(struct kobject *kobj);
29
30static struct kobj_type ibmasm_cmd_kobj_type = {
31 .release = free_command,
32};
33
34
35struct command *ibmasm_new_command(size_t buffer_size)
36{
37 struct command *cmd;
38
39 if (buffer_size > IBMASM_CMD_MAX_BUFFER_SIZE)
40 return NULL;
41
42 cmd = kmalloc(sizeof(struct command), GFP_KERNEL);
43 if (cmd == NULL)
44 return NULL;
45
46 memset(cmd, 0, sizeof(*cmd));
47
48 cmd->buffer = kmalloc(buffer_size, GFP_KERNEL);
49 if (cmd->buffer == NULL) {
50 kfree(cmd);
51 return NULL;
52 }
53 memset(cmd->buffer, 0, buffer_size);
54 cmd->buffer_size = buffer_size;
55
56 kobject_init(&cmd->kobj);
57 cmd->kobj.ktype = &ibmasm_cmd_kobj_type;
58
59 cmd->status = IBMASM_CMD_PENDING;
60 init_waitqueue_head(&cmd->wait);
61 INIT_LIST_HEAD(&cmd->queue_node);
62
63 return cmd;
64}
65
66static void free_command(struct kobject *kobj)
67{
68 struct command *cmd = to_command(kobj);
69
70 list_del(&cmd->queue_node);
71 kfree(cmd->buffer);
72 kfree(cmd);
73}
74
75static void enqueue_command(struct service_processor *sp, struct command *cmd)
76{
77 list_add_tail(&cmd->queue_node, &sp->command_queue);
78}
79
80static struct command *dequeue_command(struct service_processor *sp)
81{
82 struct command *cmd;
83 struct list_head *next;
84
85 if (list_empty(&sp->command_queue))
86 return NULL;
87
88 next = sp->command_queue.next;
89 list_del_init(next);
90 cmd = list_entry(next, struct command, queue_node);
91
92 return cmd;
93}
94
95static inline void do_exec_command(struct service_processor *sp)
96{
97 if (ibmasm_send_i2o_message(sp)) {
98 sp->current_command->status = IBMASM_CMD_FAILED;
99 exec_next_command(sp);
100 }
101}
102
103/**
104 * exec_command
105 * send a command to a service processor
106 * Commands are executed sequentially. One command (sp->current_command)
107 * is sent to the service processor. Once the interrupt handler gets a
108 * message of type command_response, the message is copied into
109 * the current commands buffer,
110 */
111void ibmasm_exec_command(struct service_processor *sp, struct command *cmd)
112{
113 unsigned long flags;
114
115 spin_lock_irqsave(&sp->lock, flags);
116
117 if (!sp->current_command) {
118 command_get(cmd);
119 sp->current_command = cmd;
120 spin_unlock_irqrestore(&sp->lock, flags);
121
122 do_exec_command(sp);
123 } else {
124 enqueue_command(sp, cmd);
125 spin_unlock_irqrestore(&sp->lock, flags);
126 }
127}
128
129static void exec_next_command(struct service_processor *sp)
130{
131 unsigned long flags;
132
133 wake_up(&sp->current_command->wait);
134 command_put(sp->current_command);
135
136 spin_lock_irqsave(&sp->lock, flags);
137 sp->current_command = dequeue_command(sp);
138 if (sp->current_command) {
139 command_get(sp->current_command);
140 spin_unlock_irqrestore(&sp->lock, flags);
141 do_exec_command(sp);
142 } else {
143 spin_unlock_irqrestore(&sp->lock, flags);
144 }
145}
146
147/**
148 * Sleep until a command has failed or a response has been received
149 * and the command status been updated by the interrupt handler.
150 * (see receive_response).
151 */
152void ibmasm_wait_for_response(struct command *cmd, int timeout)
153{
154 wait_event_interruptible_timeout(cmd->wait,
155 cmd->status == IBMASM_CMD_COMPLETE ||
156 cmd->status == IBMASM_CMD_FAILED,
157 timeout * HZ);
158}
159
160/**
161 * receive_command_response
162 * called by the interrupt handler when a dot command of type command_response
163 * was received.
164 */
165void ibmasm_receive_command_response(struct service_processor *sp, void *response, size_t size)
166{
167 struct command *cmd = sp->current_command;
168
169 if (!sp->current_command)
170 return;
171
172 memcpy(cmd->buffer, response, min(size, cmd->buffer_size));
173 cmd->status = IBMASM_CMD_COMPLETE;
174 exec_next_command(sp);
175}
diff --git a/drivers/misc/ibmasm/dot_command.c b/drivers/misc/ibmasm/dot_command.c
new file mode 100644
index 000000000000..478a8d898fc1
--- /dev/null
+++ b/drivers/misc/ibmasm/dot_command.c
@@ -0,0 +1,146 @@
1/*
2 * IBM ASM Service Processor Device Driver
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 *
18 * Copyright (C) IBM Corporation, 2004
19 *
20 * Author: Max Asböck <amax@us.ibm.com>
21 *
22 */
23
24#include "ibmasm.h"
25#include "dot_command.h"
26
27/**
28 * Dispatch an incoming message to the specific handler for the message.
29 * Called from interrupt context.
30 */
31void ibmasm_receive_message(struct service_processor *sp, void *message, int message_size)
32{
33 u32 size;
34 struct dot_command_header *header = (struct dot_command_header *)message;
35
36 size = get_dot_command_size(message);
37 if (size > message_size)
38 size = message_size;
39
40 switch (header->type) {
41 case sp_event:
42 ibmasm_receive_event(sp, message, size);
43 break;
44 case sp_command_response:
45 ibmasm_receive_command_response(sp, message, size);
46 break;
47 case sp_heartbeat:
48 ibmasm_receive_heartbeat(sp, message, size);
49 break;
50 default:
51 dev_err(sp->dev, "Received unknown message from service processor\n");
52 }
53}
54
55
56#define INIT_BUFFER_SIZE 32
57
58
59/**
60 * send the 4.3.5.10 dot command (driver VPD) to the service processor
61 */
62int ibmasm_send_driver_vpd(struct service_processor *sp)
63{
64 struct command *command;
65 struct dot_command_header *header;
66 u8 *vpd_command;
67 u8 *vpd_data;
68 int result = 0;
69
70 command = ibmasm_new_command(INIT_BUFFER_SIZE);
71 if (command == NULL)
72 return -ENOMEM;
73
74 header = (struct dot_command_header *)command->buffer;
75 header->type = sp_write;
76 header->command_size = 4;
77 header->data_size = 16;
78 header->status = 0;
79 header->reserved = 0;
80
81 vpd_command = command->buffer + sizeof(struct dot_command_header);
82 vpd_command[0] = 0x4;
83 vpd_command[1] = 0x3;
84 vpd_command[2] = 0x5;
85 vpd_command[3] = 0xa;
86
87 vpd_data = vpd_command + header->command_size;
88 vpd_data[0] = 0;
89 strcat(vpd_data, IBMASM_DRIVER_VPD);
90 vpd_data[10] = 0;
91 vpd_data[15] = 0;
92
93 ibmasm_exec_command(sp, command);
94 ibmasm_wait_for_response(command, IBMASM_CMD_TIMEOUT_NORMAL);
95
96 if (command->status != IBMASM_CMD_COMPLETE)
97 result = -ENODEV;
98
99 command_put(command);
100
101 return result;
102}
103
104struct os_state_command {
105 struct dot_command_header header;
106 unsigned char command[3];
107 unsigned char data;
108};
109
110/**
111 * send the 4.3.6 dot command (os state) to the service processor
112 * During driver init this function is called with os state "up".
113 * This causes the service processor to start sending heartbeats the
114 * driver.
115 * During driver exit the function is called with os state "down",
116 * causing the service processor to stop the heartbeats.
117 */
118int ibmasm_send_os_state(struct service_processor *sp, int os_state)
119{
120 struct command *cmd;
121 struct os_state_command *os_state_cmd;
122 int result = 0;
123
124 cmd = ibmasm_new_command(sizeof(struct os_state_command));
125 if (cmd == NULL)
126 return -ENOMEM;
127
128 os_state_cmd = (struct os_state_command *)cmd->buffer;
129 os_state_cmd->header.type = sp_write;
130 os_state_cmd->header.command_size = 3;
131 os_state_cmd->header.data_size = 1;
132 os_state_cmd->header.status = 0;
133 os_state_cmd->command[0] = 4;
134 os_state_cmd->command[1] = 3;
135 os_state_cmd->command[2] = 6;
136 os_state_cmd->data = os_state;
137
138 ibmasm_exec_command(sp, cmd);
139 ibmasm_wait_for_response(cmd, IBMASM_CMD_TIMEOUT_NORMAL);
140
141 if (cmd->status != IBMASM_CMD_COMPLETE)
142 result = -ENODEV;
143
144 command_put(cmd);
145 return result;
146}
diff --git a/drivers/misc/ibmasm/dot_command.h b/drivers/misc/ibmasm/dot_command.h
new file mode 100644
index 000000000000..2d21c2741b6a
--- /dev/null
+++ b/drivers/misc/ibmasm/dot_command.h
@@ -0,0 +1,78 @@
1/*
2 * IBM ASM Service Processor Device Driver
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 *
18 * Copyright (C) IBM Corporation, 2004
19 *
20 * Author: Max Asböck <amax@us.ibm.com>
21 *
22 */
23
24#ifndef __DOT_COMMAND_H__
25#define __DOT_COMMAND_H__
26
27/*
28 * dot commands are the protocol used to communicate with the service
29 * processor.
30 * They consist of header, a command of variable length and data of
31 * variable length.
32 */
33
34/* dot command types */
35#define sp_write 0
36#define sp_write_next 1
37#define sp_read 2
38#define sp_read_next 3
39#define sp_command_response 4
40#define sp_event 5
41#define sp_heartbeat 6
42
43#pragma pack(1)
44struct dot_command_header {
45 u8 type;
46 u8 command_size;
47 u16 data_size;
48 u8 status;
49 u8 reserved;
50};
51#pragma pack()
52
53static inline size_t get_dot_command_size(void *buffer)
54{
55 struct dot_command_header *cmd = (struct dot_command_header *)buffer;
56 return sizeof(struct dot_command_header) + cmd->command_size + cmd->data_size;
57}
58
59static inline unsigned int get_dot_command_timeout(void *buffer)
60{
61 struct dot_command_header *header = (struct dot_command_header *)buffer;
62 unsigned char *cmd = buffer + sizeof(struct dot_command_header);
63
64 /* dot commands 6.3.1, 7.1 and 8.x need a longer timeout */
65
66 if (header->command_size == 3) {
67 if ((cmd[0] == 6) && (cmd[1] == 3) && (cmd[2] == 1))
68 return IBMASM_CMD_TIMEOUT_EXTRA;
69 } else if (header->command_size == 2) {
70 if ((cmd[0] == 7) && (cmd[1] == 1))
71 return IBMASM_CMD_TIMEOUT_EXTRA;
72 if (cmd[0] == 8)
73 return IBMASM_CMD_TIMEOUT_EXTRA;
74 }
75 return IBMASM_CMD_TIMEOUT_NORMAL;
76}
77
78#endif /* __DOT_COMMAND_H__ */
diff --git a/drivers/misc/ibmasm/event.c b/drivers/misc/ibmasm/event.c
new file mode 100644
index 000000000000..e100f34f1587
--- /dev/null
+++ b/drivers/misc/ibmasm/event.c
@@ -0,0 +1,169 @@
1
2/*
3 * IBM ASM Service Processor Device Driver
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 *
19 * Copyright (C) IBM Corporation, 2004
20 *
21 * Author: Max Asböck <amax@us.ibm.com>
22 *
23 */
24
25#include "ibmasm.h"
26
27/*
28 * ASM service processor event handling routines.
29 *
30 * Events are signalled to the device drivers through interrupts.
31 * They have the format of dot commands, with the type field set to
32 * sp_event.
33 * The driver does not interpret the events, it simply stores them in a
34 * circular buffer.
35 */
36
37
38static void wake_up_event_readers(struct service_processor *sp)
39{
40 struct event_reader *reader;
41
42 list_for_each_entry(reader, &sp->event_buffer->readers, node)
43 wake_up_interruptible(&reader->wait);
44}
45
46/**
47 * receive_event
48 * Called by the interrupt handler when a dot command of type sp_event is
49 * received.
50 * Store the event in the circular event buffer, wake up any sleeping
51 * event readers.
52 * There is no reader marker in the buffer, therefore readers are
53 * responsible for keeping up with the writer, or they will loose events.
54 */
55void ibmasm_receive_event(struct service_processor *sp, void *data, unsigned int data_size)
56{
57 struct event_buffer *buffer = sp->event_buffer;
58 struct ibmasm_event *event;
59 unsigned long flags;
60
61 data_size = min(data_size, IBMASM_EVENT_MAX_SIZE);
62
63 spin_lock_irqsave(&sp->lock, flags);
64 /* copy the event into the next slot in the circular buffer */
65 event = &buffer->events[buffer->next_index];
66 memcpy(event->data, data, data_size);
67 event->data_size = data_size;
68 event->serial_number = buffer->next_serial_number;
69
70 /* advance indices in the buffer */
71 buffer->next_index = (buffer->next_index + 1) % IBMASM_NUM_EVENTS;
72 buffer->next_serial_number++;
73 spin_unlock_irqrestore(&sp->lock, flags);
74
75 wake_up_event_readers(sp);
76}
77
78static inline int event_available(struct event_buffer *b, struct event_reader *r)
79{
80 return (r->next_serial_number < b->next_serial_number);
81}
82
83/**
84 * get_next_event
85 * Called by event readers (initiated from user space through the file
86 * system).
87 * Sleeps until a new event is available.
88 */
89int ibmasm_get_next_event(struct service_processor *sp, struct event_reader *reader)
90{
91 struct event_buffer *buffer = sp->event_buffer;
92 struct ibmasm_event *event;
93 unsigned int index;
94 unsigned long flags;
95
96 if (wait_event_interruptible(reader->wait, event_available(buffer, reader)))
97 return -ERESTARTSYS;
98
99 if (!event_available(buffer, reader))
100 return 0;
101
102 spin_lock_irqsave(&sp->lock, flags);
103
104 index = buffer->next_index;
105 event = &buffer->events[index];
106 while (event->serial_number < reader->next_serial_number) {
107 index = (index + 1) % IBMASM_NUM_EVENTS;
108 event = &buffer->events[index];
109 }
110 memcpy(reader->data, event->data, event->data_size);
111 reader->data_size = event->data_size;
112 reader->next_serial_number = event->serial_number + 1;
113
114 spin_unlock_irqrestore(&sp->lock, flags);
115
116 return event->data_size;
117}
118
119void ibmasm_event_reader_register(struct service_processor *sp, struct event_reader *reader)
120{
121 unsigned long flags;
122
123 reader->next_serial_number = sp->event_buffer->next_serial_number;
124 init_waitqueue_head(&reader->wait);
125 spin_lock_irqsave(&sp->lock, flags);
126 list_add(&reader->node, &sp->event_buffer->readers);
127 spin_unlock_irqrestore(&sp->lock, flags);
128}
129
130void ibmasm_event_reader_unregister(struct service_processor *sp, struct event_reader *reader)
131{
132 unsigned long flags;
133
134 wake_up_interruptible(&reader->wait);
135
136 spin_lock_irqsave(&sp->lock, flags);
137 list_del(&reader->node);
138 spin_unlock_irqrestore(&sp->lock, flags);
139}
140
141int ibmasm_event_buffer_init(struct service_processor *sp)
142{
143 struct event_buffer *buffer;
144 struct ibmasm_event *event;
145 int i;
146
147 buffer = kmalloc(sizeof(struct event_buffer), GFP_KERNEL);
148 if (!buffer)
149 return 1;
150
151 buffer->next_index = 0;
152 buffer->next_serial_number = 1;
153
154 event = buffer->events;
155 for (i=0; i<IBMASM_NUM_EVENTS; i++, event++)
156 event->serial_number = 0;
157
158 INIT_LIST_HEAD(&buffer->readers);
159
160 sp->event_buffer = buffer;
161
162 return 0;
163}
164
165void ibmasm_event_buffer_exit(struct service_processor *sp)
166{
167 wake_up_event_readers(sp);
168 kfree(sp->event_buffer);
169}
diff --git a/drivers/misc/ibmasm/heartbeat.c b/drivers/misc/ibmasm/heartbeat.c
new file mode 100644
index 000000000000..ce09309174d6
--- /dev/null
+++ b/drivers/misc/ibmasm/heartbeat.c
@@ -0,0 +1,91 @@
1
2/*
3 * IBM ASM Service Processor Device Driver
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 *
19 * Copyright (C) IBM Corporation, 2004
20 *
21 * Author: Max Asböck <amax@us.ibm.com>
22 *
23 */
24
25#include <linux/notifier.h>
26#include "ibmasm.h"
27#include "dot_command.h"
28
29static int suspend_heartbeats = 0;
30
31/*
32 * Once the driver indicates to the service processor that it is running
33 * - see send_os_state() - the service processor sends periodic heartbeats
34 * to the driver. The driver must respond to the heartbeats or else the OS
35 * will be rebooted.
36 * In the case of a panic the interrupt handler continues to work and thus
37 * continues to respond to heartbeats, making the service processor believe
38 * the OS is still running and thus preventing a reboot.
39 * To prevent this from happening a callback is added the panic_notifier_list.
40 * Before responding to a heartbeat the driver checks if a panic has happened,
41 * if yes it suspends heartbeat, causing the service processor to reboot as
42 * expected.
43 */
44static int panic_happened(struct notifier_block *n, unsigned long val, void *v)
45{
46 suspend_heartbeats = 1;
47 return 0;
48}
49
50static struct notifier_block panic_notifier = { panic_happened, NULL, 1 };
51
52void ibmasm_register_panic_notifier(void)
53{
54 notifier_chain_register(&panic_notifier_list, &panic_notifier);
55}
56
57void ibmasm_unregister_panic_notifier(void)
58{
59 notifier_chain_unregister(&panic_notifier_list, &panic_notifier);
60}
61
62
63int ibmasm_heartbeat_init(struct service_processor *sp)
64{
65 sp->heartbeat = ibmasm_new_command(HEARTBEAT_BUFFER_SIZE);
66 if (sp->heartbeat == NULL)
67 return -ENOMEM;
68
69 return 0;
70}
71
72void ibmasm_heartbeat_exit(struct service_processor *sp)
73{
74 command_put(sp->heartbeat);
75}
76
77void ibmasm_receive_heartbeat(struct service_processor *sp, void *message, size_t size)
78{
79 struct command *cmd = sp->heartbeat;
80 struct dot_command_header *header = (struct dot_command_header *)cmd->buffer;
81
82 if (suspend_heartbeats)
83 return;
84
85 /* return the received dot command to sender */
86 cmd->status = IBMASM_CMD_PENDING;
87 size = min(size, cmd->buffer_size);
88 memcpy(cmd->buffer, message, size);
89 header->type = sp_write;
90 ibmasm_exec_command(sp, cmd);
91}
diff --git a/drivers/misc/ibmasm/i2o.h b/drivers/misc/ibmasm/i2o.h
new file mode 100644
index 000000000000..958c957a5e75
--- /dev/null
+++ b/drivers/misc/ibmasm/i2o.h
@@ -0,0 +1,77 @@
1/*
2 * IBM ASM Service Processor Device Driver
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 *
18 * Copyright (C) IBM Corporation, 2004
19 *
20 * Author: Max Asböck <amax@us.ibm.com>
21 *
22 */
23
24#pragma pack(1)
25struct i2o_header {
26 u8 version;
27 u8 message_flags;
28 u16 message_size;
29 u8 target;
30 u8 initiator_and_target;
31 u8 initiator;
32 u8 function;
33 u32 initiator_context;
34};
35#pragma pack()
36
37#define I2O_HEADER_TEMPLATE \
38 { .version = 0x01, \
39 .message_flags = 0x00, \
40 .function = 0xFF, \
41 .initiator = 0x00, \
42 .initiator_and_target = 0x40, \
43 .target = 0x00, \
44 .initiator_context = 0x0 }
45
46#define I2O_MESSAGE_SIZE 0x1000
47#define I2O_COMMAND_SIZE (I2O_MESSAGE_SIZE - sizeof(struct i2o_header))
48
49#pragma pack(1)
50struct i2o_message {
51 struct i2o_header header;
52 void *data;
53};
54#pragma pack()
55
56static inline unsigned short outgoing_message_size(unsigned int data_size)
57{
58 unsigned int size;
59 unsigned short i2o_size;
60
61 if (data_size > I2O_COMMAND_SIZE)
62 data_size = I2O_COMMAND_SIZE;
63
64 size = sizeof(struct i2o_header) + data_size;
65
66 i2o_size = size / sizeof(u32);
67
68 if (size % sizeof(u32))
69 i2o_size++;
70
71 return i2o_size;
72}
73
74static inline u32 incoming_data_size(struct i2o_message *i2o_message)
75{
76 return (sizeof(u32) * i2o_message->header.message_size);
77}
diff --git a/drivers/misc/ibmasm/ibmasm.h b/drivers/misc/ibmasm/ibmasm.h
new file mode 100644
index 000000000000..6fec7fd8cd1a
--- /dev/null
+++ b/drivers/misc/ibmasm/ibmasm.h
@@ -0,0 +1,229 @@
1
2/*
3 * IBM ASM Service Processor Device Driver
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 *
19 * Copyright (C) IBM Corporation, 2004
20 *
21 * Author: Max Asböck <amax@us.ibm.com>
22 *
23 */
24
25#include <linux/kernel.h>
26#include <linux/types.h>
27#include <linux/errno.h>
28#include <linux/list.h>
29#include <linux/wait.h>
30#include <linux/spinlock.h>
31#include <linux/slab.h>
32#include <linux/config.h>
33#include <linux/module.h>
34#include <linux/version.h>
35#include <linux/interrupt.h>
36#include <linux/device.h>
37
38/* Driver identification */
39#define DRIVER_NAME "ibmasm"
40#define DRIVER_VERSION "0.4"
41#define DRIVER_AUTHOR "Max Asbock"
42#define DRIVER_DESC "IBM ASM Service Processor Driver"
43
44#define err(msg) printk(KERN_ERR "%s: " msg "\n", DRIVER_NAME)
45#define info(msg) printk(KERN_INFO "%s: " msg "\n", DRIVER_NAME)
46
47
48#define IBMASM_CMD_PENDING 0
49#define IBMASM_CMD_COMPLETE 1
50#define IBMASM_CMD_FAILED 2
51
52#define IBMASM_CMD_TIMEOUT_NORMAL 45
53#define IBMASM_CMD_TIMEOUT_EXTRA 240
54
55#define IBMASM_CMD_MAX_BUFFER_SIZE 0x4000
56
57#define REVERSE_HEARTBEAT_TIMEOUT 120
58
59#define HEARTBEAT_BUFFER_SIZE 0x400
60
61#ifdef IA64
62#define IBMASM_DRIVER_VPD "Lin64 6.08 "
63#else
64#define IBMASM_DRIVER_VPD "Lin32 6.08 "
65#endif
66
67#define SYSTEM_STATE_OS_UP 5
68#define SYSTEM_STATE_OS_DOWN 4
69
70#define IBMASM_NAME_SIZE 16
71
72#define IBMASM_NUM_EVENTS 10
73#define IBMASM_EVENT_MAX_SIZE 2048u
74
75
76struct command {
77 struct list_head queue_node;
78 wait_queue_head_t wait;
79 unsigned char *buffer;
80 size_t buffer_size;
81 int status;
82 struct kobject kobj;
83};
84#define to_command(c) container_of(c, struct command, kobj)
85
86static inline void command_put(struct command *cmd)
87{
88 kobject_put(&cmd->kobj);
89}
90
91static inline void command_get(struct command *cmd)
92{
93 kobject_get(&cmd->kobj);
94}
95
96
97struct ibmasm_event {
98 unsigned int serial_number;
99 unsigned int data_size;
100 unsigned char data[IBMASM_EVENT_MAX_SIZE];
101};
102
103struct event_buffer {
104 struct ibmasm_event events[IBMASM_NUM_EVENTS];
105 unsigned int next_serial_number;
106 unsigned int next_index;
107 struct list_head readers;
108};
109
110struct event_reader {
111 unsigned int next_serial_number;
112 wait_queue_head_t wait;
113 struct list_head node;
114 unsigned int data_size;
115 unsigned char data[IBMASM_EVENT_MAX_SIZE];
116};
117
118struct reverse_heartbeat {
119 wait_queue_head_t wait;
120 unsigned int stopped;
121};
122
123
124/* remote console events */
125struct mouse_event {
126 long x;
127 long y;
128 unsigned char buttons;
129 unsigned char transitions;
130};
131
132struct keyboard_event {
133 unsigned long key_code;
134 unsigned char key_down;
135};
136
137struct remote_event {
138 unsigned long type;
139 union {
140 struct mouse_event mouse;
141 struct keyboard_event keyboard;
142 } data;
143};
144
145#define DRIVER_REMOTE_QUEUE_SIZE 240
146
147struct remote_queue {
148 struct remote_event *start;
149 struct remote_event *end;
150 struct remote_event *reader;
151 struct remote_event *writer;
152 unsigned int size;
153 int open;
154 wait_queue_head_t wait;
155};
156
157
158struct service_processor {
159 struct list_head node;
160 spinlock_t lock;
161 void __iomem *base_address;
162 unsigned int irq;
163 struct command *current_command;
164 struct command *heartbeat;
165 struct list_head command_queue;
166 struct event_buffer *event_buffer;
167 char dirname[IBMASM_NAME_SIZE];
168 char devname[IBMASM_NAME_SIZE];
169 unsigned int number;
170 struct remote_queue remote_queue;
171 int serial_line;
172 struct device *dev;
173};
174
175/* command processing */
176extern struct command *ibmasm_new_command(size_t buffer_size);
177extern void ibmasm_exec_command(struct service_processor *sp, struct command *cmd);
178extern void ibmasm_wait_for_response(struct command *cmd, int timeout);
179extern void ibmasm_receive_command_response(struct service_processor *sp, void *response, size_t size);
180
181/* event processing */
182extern int ibmasm_event_buffer_init(struct service_processor *sp);
183extern void ibmasm_event_buffer_exit(struct service_processor *sp);
184extern void ibmasm_receive_event(struct service_processor *sp, void *data, unsigned int data_size);
185extern void ibmasm_event_reader_register(struct service_processor *sp, struct event_reader *reader);
186extern void ibmasm_event_reader_unregister(struct service_processor *sp, struct event_reader *reader);
187extern int ibmasm_get_next_event(struct service_processor *sp, struct event_reader *reader);
188
189/* heartbeat - from SP to OS */
190extern void ibmasm_register_panic_notifier(void);
191extern void ibmasm_unregister_panic_notifier(void);
192extern int ibmasm_heartbeat_init(struct service_processor *sp);
193extern void ibmasm_heartbeat_exit(struct service_processor *sp);
194extern void ibmasm_receive_heartbeat(struct service_processor *sp, void *message, size_t size);
195
196/* reverse heartbeat - from OS to SP */
197extern void ibmasm_init_reverse_heartbeat(struct service_processor *sp, struct reverse_heartbeat *rhb);
198extern int ibmasm_start_reverse_heartbeat(struct service_processor *sp, struct reverse_heartbeat *rhb);
199extern void ibmasm_stop_reverse_heartbeat(struct reverse_heartbeat *rhb);
200
201/* dot commands */
202extern void ibmasm_receive_message(struct service_processor *sp, void *data, int data_size);
203extern int ibmasm_send_driver_vpd(struct service_processor *sp);
204extern int ibmasm_send_os_state(struct service_processor *sp, int os_state);
205
206/* low level message processing */
207extern int ibmasm_send_i2o_message(struct service_processor *sp);
208extern irqreturn_t ibmasm_interrupt_handler(int irq, void * dev_id, struct pt_regs *regs);
209
210/* remote console */
211extern void ibmasm_handle_mouse_interrupt(struct service_processor *sp);
212extern int ibmasm_init_remote_queue(struct service_processor *sp);
213extern void ibmasm_free_remote_queue(struct service_processor *sp);
214extern void ibmasm_advance_reader(struct remote_queue *q, unsigned int n);
215extern size_t ibmasm_events_available(struct remote_queue *q);
216
217/* file system */
218extern int ibmasmfs_register(void);
219extern void ibmasmfs_unregister(void);
220extern void ibmasmfs_add_sp(struct service_processor *sp);
221
222/* uart */
223#ifdef CONFIG_SERIAL_8250
224extern void ibmasm_register_uart(struct service_processor *sp);
225extern void ibmasm_unregister_uart(struct service_processor *sp);
226#else
227#define ibmasm_register_uart(sp) do { } while(0)
228#define ibmasm_unregister_uart(sp) do { } while(0)
229#endif
diff --git a/drivers/misc/ibmasm/ibmasmfs.c b/drivers/misc/ibmasm/ibmasmfs.c
new file mode 100644
index 000000000000..866e867e68f2
--- /dev/null
+++ b/drivers/misc/ibmasm/ibmasmfs.c
@@ -0,0 +1,709 @@
1/*
2 * IBM ASM Service Processor Device Driver
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 *
18 * Copyright (C) IBM Corporation, 2004
19 *
20 * Author: Max Asböck <amax@us.ibm.com>
21 *
22 */
23
24/*
25 * Parts of this code are based on an article by Jonathan Corbet
26 * that appeared in Linux Weekly News.
27 */
28
29
30/*
31 * The IBMASM file virtual filesystem. It creates the following hierarchy
32 * dymamically when mounted from user space:
33 *
34 * /ibmasm
35 * |-- 0
36 * | |-- command
37 * | |-- event
38 * | |-- reverse_heartbeat
39 * | `-- remote_video
40 * | |-- connected
41 * | |-- depth
42 * | |-- events
43 * | |-- height
44 * | `-- width
45 * .
46 * .
47 * .
48 * `-- n
49 * |-- command
50 * |-- event
51 * |-- reverse_heartbeat
52 * `-- remote_video
53 * |-- connected
54 * |-- depth
55 * |-- events
56 * |-- height
57 * `-- width
58 *
59 * For each service processor the following files are created:
60 *
61 * command: execute dot commands
62 * write: execute a dot command on the service processor
63 * read: return the result of a previously executed dot command
64 *
65 * events: listen for service processor events
66 * read: sleep (interruptible) until an event occurs
67 * write: wakeup sleeping event listener
68 *
69 * reverse_heartbeat: send a heartbeat to the service processor
70 * read: sleep (interruptible) until the reverse heartbeat fails
71 * write: wakeup sleeping heartbeat listener
72 *
73 * remote_video/width
74 * remote_video/height
75 * remote_video/width: control remote display settings
76 * write: set value
77 * read: read value
78 *
79 * remote_video/connected
80 * read: return "1" if web browser VNC java applet is connected,
81 * "0" otherwise
82 *
83 * remote_video/events
84 * read: sleep until a remote mouse or keyboard event occurs, then return
85 * then event.
86 */
87
88#include <linux/fs.h>
89#include <linux/pagemap.h>
90#include <asm/uaccess.h>
91#include <asm/io.h>
92#include "ibmasm.h"
93#include "remote.h"
94#include "dot_command.h"
95
96#define IBMASMFS_MAGIC 0x66726f67
97
98static LIST_HEAD(service_processors);
99
100static struct inode *ibmasmfs_make_inode(struct super_block *sb, int mode);
101static void ibmasmfs_create_files (struct super_block *sb, struct dentry *root);
102static int ibmasmfs_fill_super (struct super_block *sb, void *data, int silent);
103
104
105static struct super_block *ibmasmfs_get_super(struct file_system_type *fst,
106 int flags, const char *name, void *data)
107{
108 return get_sb_single(fst, flags, data, ibmasmfs_fill_super);
109}
110
111static struct super_operations ibmasmfs_s_ops = {
112 .statfs = simple_statfs,
113 .drop_inode = generic_delete_inode,
114};
115
116static struct file_operations *ibmasmfs_dir_ops = &simple_dir_operations;
117
118static struct file_system_type ibmasmfs_type = {
119 .owner = THIS_MODULE,
120 .name = "ibmasmfs",
121 .get_sb = ibmasmfs_get_super,
122 .kill_sb = kill_litter_super,
123};
124
125static int ibmasmfs_fill_super (struct super_block *sb, void *data, int silent)
126{
127 struct inode *root;
128 struct dentry *root_dentry;
129
130 sb->s_blocksize = PAGE_CACHE_SIZE;
131 sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
132 sb->s_magic = IBMASMFS_MAGIC;
133 sb->s_op = &ibmasmfs_s_ops;
134 sb->s_time_gran = 1;
135
136 root = ibmasmfs_make_inode (sb, S_IFDIR | 0500);
137 if (!root)
138 return -ENOMEM;
139
140 root->i_op = &simple_dir_inode_operations;
141 root->i_fop = ibmasmfs_dir_ops;
142
143 root_dentry = d_alloc_root(root);
144 if (!root_dentry) {
145 iput(root);
146 return -ENOMEM;
147 }
148 sb->s_root = root_dentry;
149
150 ibmasmfs_create_files(sb, root_dentry);
151 return 0;
152}
153
154static struct inode *ibmasmfs_make_inode(struct super_block *sb, int mode)
155{
156 struct inode *ret = new_inode(sb);
157
158 if (ret) {
159 ret->i_mode = mode;
160 ret->i_uid = ret->i_gid = 0;
161 ret->i_blksize = PAGE_CACHE_SIZE;
162 ret->i_blocks = 0;
163 ret->i_atime = ret->i_mtime = ret->i_ctime = CURRENT_TIME;
164 }
165 return ret;
166}
167
168static struct dentry *ibmasmfs_create_file (struct super_block *sb,
169 struct dentry *parent,
170 const char *name,
171 struct file_operations *fops,
172 void *data,
173 int mode)
174{
175 struct dentry *dentry;
176 struct inode *inode;
177
178 dentry = d_alloc_name(parent, name);
179 if (!dentry)
180 return NULL;
181
182 inode = ibmasmfs_make_inode(sb, S_IFREG | mode);
183 if (!inode) {
184 dput(dentry);
185 return NULL;
186 }
187
188 inode->i_fop = fops;
189 inode->u.generic_ip = data;
190
191 d_add(dentry, inode);
192 return dentry;
193}
194
195static struct dentry *ibmasmfs_create_dir (struct super_block *sb,
196 struct dentry *parent,
197 const char *name)
198{
199 struct dentry *dentry;
200 struct inode *inode;
201
202 dentry = d_alloc_name(parent, name);
203 if (!dentry)
204 return NULL;
205
206 inode = ibmasmfs_make_inode(sb, S_IFDIR | 0500);
207 if (!inode) {
208 dput(dentry);
209 return NULL;
210 }
211
212 inode->i_op = &simple_dir_inode_operations;
213 inode->i_fop = ibmasmfs_dir_ops;
214
215 d_add(dentry, inode);
216 return dentry;
217}
218
219int ibmasmfs_register(void)
220{
221 return register_filesystem(&ibmasmfs_type);
222}
223
224void ibmasmfs_unregister(void)
225{
226 unregister_filesystem(&ibmasmfs_type);
227}
228
229void ibmasmfs_add_sp(struct service_processor *sp)
230{
231 list_add(&sp->node, &service_processors);
232}
233
234/* struct to save state between command file operations */
235struct ibmasmfs_command_data {
236 struct service_processor *sp;
237 struct command *command;
238};
239
240/* struct to save state between event file operations */
241struct ibmasmfs_event_data {
242 struct service_processor *sp;
243 struct event_reader reader;
244 int active;
245};
246
247/* struct to save state between reverse heartbeat file operations */
248struct ibmasmfs_heartbeat_data {
249 struct service_processor *sp;
250 struct reverse_heartbeat heartbeat;
251 int active;
252};
253
254static int command_file_open(struct inode *inode, struct file *file)
255{
256 struct ibmasmfs_command_data *command_data;
257
258 if (!inode->u.generic_ip)
259 return -ENODEV;
260
261 command_data = kmalloc(sizeof(struct ibmasmfs_command_data), GFP_KERNEL);
262 if (!command_data)
263 return -ENOMEM;
264
265 command_data->command = NULL;
266 command_data->sp = inode->u.generic_ip;
267 file->private_data = command_data;
268 return 0;
269}
270
271static int command_file_close(struct inode *inode, struct file *file)
272{
273 struct ibmasmfs_command_data *command_data = file->private_data;
274
275 if (command_data->command)
276 command_put(command_data->command);
277
278 kfree(command_data);
279 return 0;
280}
281
282static ssize_t command_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
283{
284 struct ibmasmfs_command_data *command_data = file->private_data;
285 struct command *cmd;
286 int len;
287 unsigned long flags;
288
289 if (*offset < 0)
290 return -EINVAL;
291 if (count == 0 || count > IBMASM_CMD_MAX_BUFFER_SIZE)
292 return 0;
293 if (*offset != 0)
294 return 0;
295
296 spin_lock_irqsave(&command_data->sp->lock, flags);
297 cmd = command_data->command;
298 if (cmd == NULL) {
299 spin_unlock_irqrestore(&command_data->sp->lock, flags);
300 return 0;
301 }
302 command_data->command = NULL;
303 spin_unlock_irqrestore(&command_data->sp->lock, flags);
304
305 if (cmd->status != IBMASM_CMD_COMPLETE) {
306 command_put(cmd);
307 return -EIO;
308 }
309 len = min(count, cmd->buffer_size);
310 if (copy_to_user(buf, cmd->buffer, len)) {
311 command_put(cmd);
312 return -EFAULT;
313 }
314 command_put(cmd);
315
316 return len;
317}
318
319static ssize_t command_file_write(struct file *file, const char __user *ubuff, size_t count, loff_t *offset)
320{
321 struct ibmasmfs_command_data *command_data = file->private_data;
322 struct command *cmd;
323 unsigned long flags;
324
325 if (*offset < 0)
326 return -EINVAL;
327 if (count == 0 || count > IBMASM_CMD_MAX_BUFFER_SIZE)
328 return 0;
329 if (*offset != 0)
330 return 0;
331
332 /* commands are executed sequentially, only one command at a time */
333 if (command_data->command)
334 return -EAGAIN;
335
336 cmd = ibmasm_new_command(count);
337 if (!cmd)
338 return -ENOMEM;
339
340 if (copy_from_user(cmd->buffer, ubuff, count)) {
341 command_put(cmd);
342 return -EFAULT;
343 }
344
345 spin_lock_irqsave(&command_data->sp->lock, flags);
346 if (command_data->command) {
347 spin_unlock_irqrestore(&command_data->sp->lock, flags);
348 command_put(cmd);
349 return -EAGAIN;
350 }
351 command_data->command = cmd;
352 spin_unlock_irqrestore(&command_data->sp->lock, flags);
353
354 ibmasm_exec_command(command_data->sp, cmd);
355 ibmasm_wait_for_response(cmd, get_dot_command_timeout(cmd->buffer));
356
357 return count;
358}
359
360static int event_file_open(struct inode *inode, struct file *file)
361{
362 struct ibmasmfs_event_data *event_data;
363 struct service_processor *sp;
364
365 if (!inode->u.generic_ip)
366 return -ENODEV;
367
368 sp = inode->u.generic_ip;
369
370 event_data = kmalloc(sizeof(struct ibmasmfs_event_data), GFP_KERNEL);
371 if (!event_data)
372 return -ENOMEM;
373
374 ibmasm_event_reader_register(sp, &event_data->reader);
375
376 event_data->sp = sp;
377 file->private_data = event_data;
378 return 0;
379}
380
381static int event_file_close(struct inode *inode, struct file *file)
382{
383 struct ibmasmfs_event_data *event_data = file->private_data;
384
385 ibmasm_event_reader_unregister(event_data->sp, &event_data->reader);
386 kfree(event_data);
387 return 0;
388}
389
390static ssize_t event_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
391{
392 struct ibmasmfs_event_data *event_data = file->private_data;
393 struct event_reader *reader = &event_data->reader;
394 int ret;
395
396 if (*offset < 0)
397 return -EINVAL;
398 if (count == 0 || count > IBMASM_EVENT_MAX_SIZE)
399 return 0;
400 if (*offset != 0)
401 return 0;
402
403 ret = ibmasm_get_next_event(event_data->sp, reader);
404 if (ret <= 0)
405 return ret;
406
407 if (count < reader->data_size)
408 return -EINVAL;
409
410 if (copy_to_user(buf, reader->data, reader->data_size))
411 return -EFAULT;
412
413 return reader->data_size;
414}
415
416static ssize_t event_file_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)
417{
418 struct ibmasmfs_event_data *event_data = file->private_data;
419
420 if (*offset < 0)
421 return -EINVAL;
422 if (count != 1)
423 return 0;
424 if (*offset != 0)
425 return 0;
426
427 wake_up_interruptible(&event_data->reader.wait);
428 return 0;
429}
430
431static int r_heartbeat_file_open(struct inode *inode, struct file *file)
432{
433 struct ibmasmfs_heartbeat_data *rhbeat;
434
435 if (!inode->u.generic_ip)
436 return -ENODEV;
437
438 rhbeat = kmalloc(sizeof(struct ibmasmfs_heartbeat_data), GFP_KERNEL);
439 if (!rhbeat)
440 return -ENOMEM;
441
442 rhbeat->sp = (struct service_processor *)inode->u.generic_ip;
443 rhbeat->active = 0;
444 ibmasm_init_reverse_heartbeat(rhbeat->sp, &rhbeat->heartbeat);
445 file->private_data = rhbeat;
446 return 0;
447}
448
449static int r_heartbeat_file_close(struct inode *inode, struct file *file)
450{
451 struct ibmasmfs_heartbeat_data *rhbeat = file->private_data;
452
453 kfree(rhbeat);
454 return 0;
455}
456
457static ssize_t r_heartbeat_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
458{
459 struct ibmasmfs_heartbeat_data *rhbeat = file->private_data;
460 unsigned long flags;
461 int result;
462
463 if (*offset < 0)
464 return -EINVAL;
465 if (count == 0 || count > 1024)
466 return 0;
467 if (*offset != 0)
468 return 0;
469
470 /* allow only one reverse heartbeat per process */
471 spin_lock_irqsave(&rhbeat->sp->lock, flags);
472 if (rhbeat->active) {
473 spin_unlock_irqrestore(&rhbeat->sp->lock, flags);
474 return -EBUSY;
475 }
476 rhbeat->active = 1;
477 spin_unlock_irqrestore(&rhbeat->sp->lock, flags);
478
479 result = ibmasm_start_reverse_heartbeat(rhbeat->sp, &rhbeat->heartbeat);
480 rhbeat->active = 0;
481
482 return result;
483}
484
485static ssize_t r_heartbeat_file_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)
486{
487 struct ibmasmfs_heartbeat_data *rhbeat = file->private_data;
488
489 if (*offset < 0)
490 return -EINVAL;
491 if (count != 1)
492 return 0;
493 if (*offset != 0)
494 return 0;
495
496 if (rhbeat->active)
497 ibmasm_stop_reverse_heartbeat(&rhbeat->heartbeat);
498
499 return 1;
500}
501
502static int remote_settings_file_open(struct inode *inode, struct file *file)
503{
504 file->private_data = inode->u.generic_ip;
505 return 0;
506}
507
508static int remote_settings_file_close(struct inode *inode, struct file *file)
509{
510 return 0;
511}
512
513static ssize_t remote_settings_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
514{
515 void __iomem *address = (void __iomem *)file->private_data;
516 unsigned char *page;
517 int retval;
518 int len = 0;
519 unsigned int value;
520
521 if (*offset < 0)
522 return -EINVAL;
523 if (count == 0 || count > 1024)
524 return 0;
525 if (*offset != 0)
526 return 0;
527
528 page = (unsigned char *)__get_free_page(GFP_KERNEL);
529 if (!page)
530 return -ENOMEM;
531
532 value = readl(address);
533 len = sprintf(page, "%d\n", value);
534
535 if (copy_to_user(buf, page, len)) {
536 retval = -EFAULT;
537 goto exit;
538 }
539 *offset += len;
540 retval = len;
541
542exit:
543 free_page((unsigned long)page);
544 return retval;
545}
546
547static ssize_t remote_settings_file_write(struct file *file, const char __user *ubuff, size_t count, loff_t *offset)
548{
549 void __iomem *address = (void __iomem *)file->private_data;
550 char *buff;
551 unsigned int value;
552
553 if (*offset < 0)
554 return -EINVAL;
555 if (count == 0 || count > 1024)
556 return 0;
557 if (*offset != 0)
558 return 0;
559
560 buff = kmalloc (count + 1, GFP_KERNEL);
561 if (!buff)
562 return -ENOMEM;
563
564 memset(buff, 0x0, count + 1);
565
566 if (copy_from_user(buff, ubuff, count)) {
567 kfree(buff);
568 return -EFAULT;
569 }
570
571 value = simple_strtoul(buff, NULL, 10);
572 writel(value, address);
573 kfree(buff);
574
575 return count;
576}
577
578static int remote_event_file_open(struct inode *inode, struct file *file)
579{
580 struct service_processor *sp;
581 unsigned long flags;
582 struct remote_queue *q;
583
584 file->private_data = inode->u.generic_ip;
585 sp = file->private_data;
586 q = &sp->remote_queue;
587
588 /* allow only one event reader */
589 spin_lock_irqsave(&sp->lock, flags);
590 if (q->open) {
591 spin_unlock_irqrestore(&sp->lock, flags);
592 return -EBUSY;
593 }
594 q->open = 1;
595 spin_unlock_irqrestore(&sp->lock, flags);
596
597 enable_mouse_interrupts(sp);
598
599 return 0;
600}
601
602static int remote_event_file_close(struct inode *inode, struct file *file)
603{
604 struct service_processor *sp = file->private_data;
605
606 disable_mouse_interrupts(sp);
607 wake_up_interruptible(&sp->remote_queue.wait);
608 sp->remote_queue.open = 0;
609
610 return 0;
611}
612
613static ssize_t remote_event_file_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
614{
615 struct service_processor *sp = file->private_data;
616 struct remote_queue *q = &sp->remote_queue;
617 size_t data_size;
618 struct remote_event *reader = q->reader;
619 size_t num_events;
620
621 if (*offset < 0)
622 return -EINVAL;
623 if (count == 0 || count > 1024)
624 return 0;
625 if (*offset != 0)
626 return 0;
627
628 if (wait_event_interruptible(q->wait, q->reader != q->writer))
629 return -ERESTARTSYS;
630
631 /* only get multiples of struct remote_event */
632 num_events = min((count/sizeof(struct remote_event)), ibmasm_events_available(q));
633 if (!num_events)
634 return 0;
635
636 data_size = num_events * sizeof(struct remote_event);
637
638 if (copy_to_user(buf, reader, data_size))
639 return -EFAULT;
640
641 ibmasm_advance_reader(q, num_events);
642
643 return data_size;
644}
645
646
647static struct file_operations command_fops = {
648 .open = command_file_open,
649 .release = command_file_close,
650 .read = command_file_read,
651 .write = command_file_write,
652};
653
654static struct file_operations event_fops = {
655 .open = event_file_open,
656 .release = event_file_close,
657 .read = event_file_read,
658 .write = event_file_write,
659};
660
661static struct file_operations r_heartbeat_fops = {
662 .open = r_heartbeat_file_open,
663 .release = r_heartbeat_file_close,
664 .read = r_heartbeat_file_read,
665 .write = r_heartbeat_file_write,
666};
667
668static struct file_operations remote_settings_fops = {
669 .open = remote_settings_file_open,
670 .release = remote_settings_file_close,
671 .read = remote_settings_file_read,
672 .write = remote_settings_file_write,
673};
674
675static struct file_operations remote_event_fops = {
676 .open = remote_event_file_open,
677 .release = remote_event_file_close,
678 .read = remote_event_file_read,
679};
680
681
682static void ibmasmfs_create_files (struct super_block *sb, struct dentry *root)
683{
684 struct list_head *entry;
685 struct service_processor *sp;
686
687 list_for_each(entry, &service_processors) {
688 struct dentry *dir;
689 struct dentry *remote_dir;
690 sp = list_entry(entry, struct service_processor, node);
691 dir = ibmasmfs_create_dir(sb, root, sp->dirname);
692 if (!dir)
693 continue;
694
695 ibmasmfs_create_file(sb, dir, "command", &command_fops, sp, S_IRUSR|S_IWUSR);
696 ibmasmfs_create_file(sb, dir, "event", &event_fops, sp, S_IRUSR|S_IWUSR);
697 ibmasmfs_create_file(sb, dir, "reverse_heartbeat", &r_heartbeat_fops, sp, S_IRUSR|S_IWUSR);
698
699 remote_dir = ibmasmfs_create_dir(sb, dir, "remote_video");
700 if (!remote_dir)
701 continue;
702
703 ibmasmfs_create_file(sb, remote_dir, "width", &remote_settings_fops, (void *)display_width(sp), S_IRUSR|S_IWUSR);
704 ibmasmfs_create_file(sb, remote_dir, "height", &remote_settings_fops, (void *)display_height(sp), S_IRUSR|S_IWUSR);
705 ibmasmfs_create_file(sb, remote_dir, "depth", &remote_settings_fops, (void *)display_depth(sp), S_IRUSR|S_IWUSR);
706 ibmasmfs_create_file(sb, remote_dir, "connected", &remote_settings_fops, (void *)vnc_status(sp), S_IRUSR);
707 ibmasmfs_create_file(sb, remote_dir, "events", &remote_event_fops, (void *)sp, S_IRUSR);
708 }
709}
diff --git a/drivers/misc/ibmasm/lowlevel.c b/drivers/misc/ibmasm/lowlevel.c
new file mode 100644
index 000000000000..5156de2759d8
--- /dev/null
+++ b/drivers/misc/ibmasm/lowlevel.c
@@ -0,0 +1,81 @@
1/*
2 * IBM ASM Service Processor Device Driver
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 *
18 * Copyright (C) IBM Corporation, 2004
19 *
20 * Author: Max Asböck <amax@us.ibm.com>
21 *
22 */
23
24#include "ibmasm.h"
25#include "lowlevel.h"
26#include "i2o.h"
27#include "dot_command.h"
28#include "remote.h"
29
30static struct i2o_header header = I2O_HEADER_TEMPLATE;
31
32
33int ibmasm_send_i2o_message(struct service_processor *sp)
34{
35 u32 mfa;
36 unsigned int command_size;
37 struct i2o_message *message;
38 struct command *command = sp->current_command;
39
40 mfa = get_mfa_inbound(sp->base_address);
41 if (!mfa)
42 return 1;
43
44 command_size = get_dot_command_size(command->buffer);
45 header.message_size = outgoing_message_size(command_size);
46
47 message = get_i2o_message(sp->base_address, mfa);
48
49 memcpy(&message->header, &header, sizeof(struct i2o_header));
50 memcpy(&message->data, command->buffer, command_size);
51
52 set_mfa_inbound(sp->base_address, mfa);
53
54 return 0;
55}
56
57irqreturn_t ibmasm_interrupt_handler(int irq, void * dev_id, struct pt_regs *regs)
58{
59 u32 mfa;
60 struct service_processor *sp = (struct service_processor *)dev_id;
61 void __iomem *base_address = sp->base_address;
62
63 if (!sp_interrupt_pending(base_address))
64 return IRQ_NONE;
65
66 if (mouse_interrupt_pending(sp)) {
67 ibmasm_handle_mouse_interrupt(sp);
68 mfa = get_mfa_outbound(base_address);
69 clear_mouse_interrupt(sp);
70 set_mfa_outbound(base_address, mfa);
71 return IRQ_HANDLED;
72 }
73
74 mfa = get_mfa_outbound(base_address);
75 if (valid_mfa(mfa)) {
76 struct i2o_message *msg = get_i2o_message(base_address, mfa);
77 ibmasm_receive_message(sp, &msg->data, incoming_data_size(msg));
78 }
79 set_mfa_outbound(base_address, mfa);
80 return IRQ_HANDLED;
81}
diff --git a/drivers/misc/ibmasm/lowlevel.h b/drivers/misc/ibmasm/lowlevel.h
new file mode 100644
index 000000000000..e5ed59c589aa
--- /dev/null
+++ b/drivers/misc/ibmasm/lowlevel.h
@@ -0,0 +1,137 @@
1/*
2 * IBM ASM Service Processor Device Driver
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 *
18 * Copyright (C) IBM Corporation, 2004
19 *
20 * Author: Max Asböck <amax@us.ibm.com>
21 *
22 */
23
24/* Condor service processor specific hardware definitions */
25
26#ifndef __IBMASM_CONDOR_H__
27#define __IBMASM_CONDOR_H__
28
29#include <asm/io.h>
30
31#define VENDORID_IBM 0x1014
32#define DEVICEID_RSA 0x010F
33
34#define GET_MFA_ADDR(x) (x & 0xFFFFFF00)
35
36#define MAILBOX_FULL(x) (x & 0x00000001)
37
38#define NO_MFAS_AVAILABLE 0xFFFFFFFF
39
40
41#define INBOUND_QUEUE_PORT 0x40 /* contains address of next free MFA */
42#define OUTBOUND_QUEUE_PORT 0x44 /* contains address of posted MFA */
43
44#define SP_INTR_MASK 0x00000008
45#define UART_INTR_MASK 0x00000010
46
47#define INTR_STATUS_REGISTER 0x13A0
48#define INTR_CONTROL_REGISTER 0x13A4
49
50#define SCOUT_COM_A_BASE 0x0000
51#define SCOUT_COM_B_BASE 0x0100
52#define SCOUT_COM_C_BASE 0x0200
53#define SCOUT_COM_D_BASE 0x0300
54
55static inline int sp_interrupt_pending(void __iomem *base_address)
56{
57 return SP_INTR_MASK & readl(base_address + INTR_STATUS_REGISTER);
58}
59
60static inline int uart_interrupt_pending(void __iomem *base_address)
61{
62 return UART_INTR_MASK & readl(base_address + INTR_STATUS_REGISTER);
63}
64
65static inline void ibmasm_enable_interrupts(void __iomem *base_address, int mask)
66{
67 void __iomem *ctrl_reg = base_address + INTR_CONTROL_REGISTER;
68 writel( readl(ctrl_reg) & ~mask, ctrl_reg);
69}
70
71static inline void ibmasm_disable_interrupts(void __iomem *base_address, int mask)
72{
73 void __iomem *ctrl_reg = base_address + INTR_CONTROL_REGISTER;
74 writel( readl(ctrl_reg) | mask, ctrl_reg);
75}
76
77static inline void enable_sp_interrupts(void __iomem *base_address)
78{
79 ibmasm_enable_interrupts(base_address, SP_INTR_MASK);
80}
81
82static inline void disable_sp_interrupts(void __iomem *base_address)
83{
84 ibmasm_disable_interrupts(base_address, SP_INTR_MASK);
85}
86
87static inline void enable_uart_interrupts(void __iomem *base_address)
88{
89 ibmasm_enable_interrupts(base_address, UART_INTR_MASK);
90}
91
92static inline void disable_uart_interrupts(void __iomem *base_address)
93{
94 ibmasm_disable_interrupts(base_address, UART_INTR_MASK);
95}
96
97#define valid_mfa(mfa) ( (mfa) != NO_MFAS_AVAILABLE )
98
99static inline u32 get_mfa_outbound(void __iomem *base_address)
100{
101 int retry;
102 u32 mfa;
103
104 for (retry=0; retry<=10; retry++) {
105 mfa = readl(base_address + OUTBOUND_QUEUE_PORT);
106 if (valid_mfa(mfa))
107 break;
108 }
109 return mfa;
110}
111
112static inline void set_mfa_outbound(void __iomem *base_address, u32 mfa)
113{
114 writel(mfa, base_address + OUTBOUND_QUEUE_PORT);
115}
116
117static inline u32 get_mfa_inbound(void __iomem *base_address)
118{
119 u32 mfa = readl(base_address + INBOUND_QUEUE_PORT);
120
121 if (MAILBOX_FULL(mfa))
122 return 0;
123
124 return mfa;
125}
126
127static inline void set_mfa_inbound(void __iomem *base_address, u32 mfa)
128{
129 writel(mfa, base_address + INBOUND_QUEUE_PORT);
130}
131
132static inline struct i2o_message *get_i2o_message(void __iomem *base_address, u32 mfa)
133{
134 return (struct i2o_message *)(GET_MFA_ADDR(mfa) + base_address);
135}
136
137#endif /* __IBMASM_CONDOR_H__ */
diff --git a/drivers/misc/ibmasm/module.c b/drivers/misc/ibmasm/module.c
new file mode 100644
index 000000000000..777432ae764a
--- /dev/null
+++ b/drivers/misc/ibmasm/module.c
@@ -0,0 +1,228 @@
1
2/*
3 * IBM ASM Service Processor Device Driver
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 *
19 * Copyright (C) IBM Corporation, 2004
20 *
21 * Author: Max Asböck <amax@us.ibm.com>
22 *
23 * This driver is based on code originally written by Pete Reynolds
24 * and others.
25 *
26 */
27
28/*
29 * The ASM device driver does the following things:
30 *
31 * 1) When loaded it sends a message to the service processor,
32 * indicating that an OS is * running. This causes the service processor
33 * to send periodic heartbeats to the OS.
34 *
35 * 2) Answers the periodic heartbeats sent by the service processor.
36 * Failure to do so would result in system reboot.
37 *
38 * 3) Acts as a pass through for dot commands sent from user applications.
39 * The interface for this is the ibmasmfs file system.
40 *
41 * 4) Allows user applications to register for event notification. Events
42 * are sent to the driver through interrupts. They can be read from user
43 * space through the ibmasmfs file system.
44 *
45 * 5) Allows user space applications to send heartbeats to the service
46 * processor (aka reverse heartbeats). Again this happens through ibmasmfs.
47 *
48 * 6) Handles remote mouse and keyboard event interrupts and makes them
49 * available to user applications through ibmasmfs.
50 *
51 */
52
53#include <linux/pci.h>
54#include <linux/init.h>
55#include "ibmasm.h"
56#include "lowlevel.h"
57#include "remote.h"
58
59
60static int __devinit ibmasm_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
61{
62 int err, result = -ENOMEM;
63 struct service_processor *sp;
64
65 if ((err = pci_enable_device(pdev))) {
66 printk(KERN_ERR "%s: can't enable PCI device at %s\n",
67 DRIVER_NAME, pci_name(pdev));
68 return err;
69 }
70
71 sp = kmalloc(sizeof(struct service_processor), GFP_KERNEL);
72 if (sp == NULL) {
73 dev_err(&pdev->dev, "Failed to allocate memory\n");
74 result = -ENOMEM;
75 goto error_kmalloc;
76 }
77 memset(sp, 0, sizeof(struct service_processor));
78
79 pci_set_drvdata(pdev, (void *)sp);
80 sp->dev = &pdev->dev;
81 sp->number = pdev->bus->number;
82 snprintf(sp->dirname, IBMASM_NAME_SIZE, "%d", sp->number);
83 snprintf(sp->devname, IBMASM_NAME_SIZE, "%s%d", DRIVER_NAME, sp->number);
84
85 if (ibmasm_event_buffer_init(sp)) {
86 dev_err(sp->dev, "Failed to allocate event buffer\n");
87 goto error_eventbuffer;
88 }
89
90 if (ibmasm_heartbeat_init(sp)) {
91 dev_err(sp->dev, "Failed to allocate heartbeat command\n");
92 goto error_heartbeat;
93 }
94
95 sp->irq = pdev->irq;
96 sp->base_address = ioremap(pci_resource_start(pdev, 0),
97 pci_resource_len(pdev, 0));
98 if (sp->base_address == 0) {
99 dev_err(sp->dev, "Failed to ioremap pci memory\n");
100 result = -ENODEV;
101 goto error_ioremap;
102 }
103
104 result = ibmasm_init_remote_queue(sp);
105 if (result) {
106 dev_err(sp->dev, "Failed to initialize remote queue\n");
107 goto error_remote_queue;
108 }
109
110 spin_lock_init(&sp->lock);
111 INIT_LIST_HEAD(&sp->command_queue);
112
113 result = request_irq(sp->irq, ibmasm_interrupt_handler, SA_SHIRQ, sp->devname, (void*)sp);
114 if (result) {
115 dev_err(sp->dev, "Failed to register interrupt handler\n");
116 goto error_request_irq;
117 }
118
119 enable_sp_interrupts(sp->base_address);
120 disable_mouse_interrupts(sp);
121
122 result = ibmasm_send_driver_vpd(sp);
123 if (result) {
124 dev_err(sp->dev, "Failed to send driver VPD to service processor\n");
125 goto error_send_message;
126 }
127 result = ibmasm_send_os_state(sp, SYSTEM_STATE_OS_UP);
128 if (result) {
129 dev_err(sp->dev, "Failed to send OS state to service processor\n");
130 goto error_send_message;
131 }
132 ibmasmfs_add_sp(sp);
133
134 ibmasm_register_uart(sp);
135
136 dev_printk(KERN_DEBUG, &pdev->dev, "WARNING: This software may not be supported or function\n");
137 dev_printk(KERN_DEBUG, &pdev->dev, "correctly on your IBM server. Please consult the IBM\n");
138 dev_printk(KERN_DEBUG, &pdev->dev, "ServerProven website\n");
139 dev_printk(KERN_DEBUG, &pdev->dev, "http://www.pc.ibm.com/ww/eserver/xseries/serverproven\n");
140 dev_printk(KERN_DEBUG, &pdev->dev, "for information on the specific driver level and support\n");
141 dev_printk(KERN_DEBUG, &pdev->dev, "statement for your IBM server.\n");
142
143 return 0;
144
145error_send_message:
146 disable_sp_interrupts(sp->base_address);
147 free_irq(sp->irq, (void *)sp);
148error_request_irq:
149 ibmasm_free_remote_queue(sp);
150error_remote_queue:
151 iounmap(sp->base_address);
152error_ioremap:
153 ibmasm_heartbeat_exit(sp);
154error_heartbeat:
155 ibmasm_event_buffer_exit(sp);
156error_eventbuffer:
157 kfree(sp);
158error_kmalloc:
159 pci_disable_device(pdev);
160
161 return result;
162}
163
164static void __devexit ibmasm_remove_one(struct pci_dev *pdev)
165{
166 struct service_processor *sp = (struct service_processor *)pci_get_drvdata(pdev);
167
168 ibmasm_unregister_uart(sp);
169 ibmasm_send_os_state(sp, SYSTEM_STATE_OS_DOWN);
170 disable_sp_interrupts(sp->base_address);
171 disable_mouse_interrupts(sp);
172 free_irq(sp->irq, (void *)sp);
173 ibmasm_heartbeat_exit(sp);
174 ibmasm_free_remote_queue(sp);
175 iounmap(sp->base_address);
176 ibmasm_event_buffer_exit(sp);
177 kfree(sp);
178 pci_disable_device(pdev);
179}
180
181static struct pci_device_id ibmasm_pci_table[] =
182{
183 { PCI_DEVICE(VENDORID_IBM, DEVICEID_RSA) },
184 {},
185};
186
187static struct pci_driver ibmasm_driver = {
188 .name = DRIVER_NAME,
189 .id_table = ibmasm_pci_table,
190 .probe = ibmasm_init_one,
191 .remove = __devexit_p(ibmasm_remove_one),
192};
193
194static void __exit ibmasm_exit (void)
195{
196 ibmasm_unregister_panic_notifier();
197 ibmasmfs_unregister();
198 pci_unregister_driver(&ibmasm_driver);
199 info(DRIVER_DESC " version " DRIVER_VERSION " unloaded");
200}
201
202static int __init ibmasm_init(void)
203{
204 int result;
205
206 result = ibmasmfs_register();
207 if (result) {
208 err("Failed to register ibmasmfs file system");
209 return result;
210 }
211 result = pci_register_driver(&ibmasm_driver);
212 if (result) {
213 ibmasmfs_unregister();
214 return result;
215 }
216 ibmasm_register_panic_notifier();
217 info(DRIVER_DESC " version " DRIVER_VERSION " loaded");
218 return 0;
219}
220
221module_init(ibmasm_init);
222module_exit(ibmasm_exit);
223
224MODULE_AUTHOR(DRIVER_AUTHOR);
225MODULE_DESCRIPTION(DRIVER_DESC);
226MODULE_LICENSE("GPL");
227MODULE_DEVICE_TABLE(pci, ibmasm_pci_table);
228
diff --git a/drivers/misc/ibmasm/r_heartbeat.c b/drivers/misc/ibmasm/r_heartbeat.c
new file mode 100644
index 000000000000..93d9c1b2ad6f
--- /dev/null
+++ b/drivers/misc/ibmasm/r_heartbeat.c
@@ -0,0 +1,98 @@
1
2/*
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
16 *
17 * Copyright (C) IBM Corporation, 2004
18 *
19 * Author: Max Asböck <amax@us.ibm.com>
20 *
21 */
22
23#include "ibmasm.h"
24#include "dot_command.h"
25
26/*
27 * Reverse Heartbeat, i.e. heartbeats sent from the driver to the
28 * service processor.
29 * These heartbeats are initiated by user level programs.
30 */
31
32/* the reverse heartbeat dot command */
33#pragma pack(1)
34static struct {
35 struct dot_command_header header;
36 unsigned char command[3];
37} rhb_dot_cmd = {
38 .header = {
39 .type = sp_read,
40 .command_size = 3,
41 .data_size = 0,
42 .status = 0
43 },
44 .command = { 4, 3, 6 }
45};
46#pragma pack()
47
48void ibmasm_init_reverse_heartbeat(struct service_processor *sp, struct reverse_heartbeat *rhb)
49{
50 init_waitqueue_head(&rhb->wait);
51 rhb->stopped = 0;
52}
53
54/**
55 * start_reverse_heartbeat
56 * Loop forever, sending a reverse heartbeat dot command to the service
57 * processor, then sleeping. The loop comes to an end if the service
58 * processor fails to respond 3 times or we were interrupted.
59 */
60int ibmasm_start_reverse_heartbeat(struct service_processor *sp, struct reverse_heartbeat *rhb)
61{
62 struct command *cmd;
63 int times_failed = 0;
64 int result = 1;
65
66 cmd = ibmasm_new_command(sizeof rhb_dot_cmd);
67 if (!cmd)
68 return -ENOMEM;
69
70 while (times_failed < 3) {
71 memcpy(cmd->buffer, (void *)&rhb_dot_cmd, sizeof rhb_dot_cmd);
72 cmd->status = IBMASM_CMD_PENDING;
73 ibmasm_exec_command(sp, cmd);
74 ibmasm_wait_for_response(cmd, IBMASM_CMD_TIMEOUT_NORMAL);
75
76 if (cmd->status != IBMASM_CMD_COMPLETE)
77 times_failed++;
78
79 wait_event_interruptible_timeout(rhb->wait,
80 rhb->stopped,
81 REVERSE_HEARTBEAT_TIMEOUT * HZ);
82
83 if (signal_pending(current) || rhb->stopped) {
84 result = -EINTR;
85 break;
86 }
87 }
88 command_put(cmd);
89 rhb->stopped = 0;
90
91 return result;
92}
93
94void ibmasm_stop_reverse_heartbeat(struct reverse_heartbeat *rhb)
95{
96 rhb->stopped = 1;
97 wake_up_interruptible(&rhb->wait);
98}
diff --git a/drivers/misc/ibmasm/remote.c b/drivers/misc/ibmasm/remote.c
new file mode 100644
index 000000000000..520c3f10c271
--- /dev/null
+++ b/drivers/misc/ibmasm/remote.c
@@ -0,0 +1,152 @@
1
2/*
3 * IBM ASM Service Processor Device Driver
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 *
19 * Copyright (C) IBM Corporation, 2004
20 *
21 * Author: Max Asböck <amax@us.ibm.com>
22 *
23 */
24
25/* Remote mouse and keyboard event handling functions */
26
27#include "ibmasm.h"
28#include "remote.h"
29
30int ibmasm_init_remote_queue(struct service_processor *sp)
31{
32 struct remote_queue *q = &sp->remote_queue;
33
34 disable_mouse_interrupts(sp);
35
36 q->open = 0;
37 q->size = 0;
38
39 q->start = kmalloc(DRIVER_REMOTE_QUEUE_SIZE * sizeof(struct remote_event), GFP_KERNEL);
40 if (q->start == 0)
41 return -ENOMEM;
42
43 q->end = q->start + DRIVER_REMOTE_QUEUE_SIZE;
44 q->reader = q->start;
45 q->writer = q->start;
46 q->size = DRIVER_REMOTE_QUEUE_SIZE;
47 init_waitqueue_head(&q->wait);
48
49 return 0;
50}
51
52void ibmasm_free_remote_queue(struct service_processor *sp)
53{
54 kfree(sp->remote_queue.start);
55}
56
57void ibmasm_advance_reader(struct remote_queue *q, unsigned int n)
58{
59 q->reader += n;
60 if (q->reader >= q->end)
61 q->reader -= q->size;
62}
63
64size_t ibmasm_events_available(struct remote_queue *q)
65{
66 ssize_t diff = q->writer - q->reader;
67
68 return (diff >= 0) ? diff : q->end - q->reader;
69}
70
71
72static int space_free(struct remote_queue *q)
73{
74 if (q->reader == q->writer)
75 return q->size - 1;
76
77 return ( (q->reader + q->size - q->writer) % q->size ) - 1;
78}
79
80static void set_mouse_event(struct remote_input *input, struct mouse_event *mouse)
81{
82 static char last_buttons = 0;
83
84 mouse->x = input->data.mouse.x;
85 mouse->y = input->data.mouse.y;
86
87 if (input->mouse_buttons == REMOTE_MOUSE_DOUBLE_CLICK) {
88 mouse->buttons = REMOTE_MOUSE_DOUBLE_CLICK;
89 last_buttons = 0;
90 return;
91 }
92 mouse->transitions = last_buttons ^ input->mouse_buttons;
93 mouse->buttons = input->mouse_buttons;
94
95 last_buttons = input->mouse_buttons;
96}
97
98static void set_keyboard_event(struct remote_input *input, struct keyboard_event *keyboard)
99{
100 keyboard->key_code = input->data.keyboard.key_code;
101 keyboard->key_down = input->data.keyboard.key_down;
102}
103
104static int add_to_driver_queue(struct remote_queue *q, struct remote_input *input)
105{
106 struct remote_event *event = q->writer;
107
108 if (space_free(q) < 1) {
109 return 1;
110 }
111
112 switch(input->type) {
113 case (INPUT_TYPE_MOUSE):
114 event->type = INPUT_TYPE_MOUSE;
115 set_mouse_event(input, &event->data.mouse);
116 break;
117 case (INPUT_TYPE_KEYBOARD):
118 event->type = INPUT_TYPE_KEYBOARD;
119 set_keyboard_event(input, &event->data.keyboard);
120 break;
121 default:
122 return 0;
123 }
124 event->type = input->type;
125
126 q->writer++;
127 if (q->writer == q->end)
128 q->writer = q->start;
129
130 return 0;
131}
132
133
134void ibmasm_handle_mouse_interrupt(struct service_processor *sp)
135{
136 unsigned long reader;
137 unsigned long writer;
138 struct remote_input input;
139
140 reader = get_queue_reader(sp);
141 writer = get_queue_writer(sp);
142
143 while (reader != writer) {
144 memcpy(&input, (void *)get_queue_entry(sp, reader), sizeof(struct remote_input));
145
146 if (add_to_driver_queue(&sp->remote_queue, &input))
147 break;
148
149 reader = advance_queue_reader(sp, reader);
150 }
151 wake_up_interruptible(&sp->remote_queue.wait);
152}
diff --git a/drivers/misc/ibmasm/remote.h b/drivers/misc/ibmasm/remote.h
new file mode 100644
index 000000000000..a8eb19f02d3f
--- /dev/null
+++ b/drivers/misc/ibmasm/remote.h
@@ -0,0 +1,119 @@
1
2/*
3 * IBM ASM Service Processor Device Driver
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 *
19 * Copyright (C) IBM Corporation, 2004
20 *
21 * Author: Max Asböck <amax@us.ibm.com>
22 *
23 * Orignally written by Pete Reynolds
24 */
25
26#ifndef _IBMASM_REMOTE_H_
27#define _IBMASM_REMOTE_H_
28
29#include <asm/io.h>
30
31/* pci offsets */
32#define CONDOR_MOUSE_DATA 0x000AC000
33#define CONDOR_MOUSE_ISR_CONTROL 0x00
34#define CONDOR_MOUSE_ISR_STATUS 0x04
35#define CONDOR_MOUSE_Q_READER 0x08
36#define CONDOR_MOUSE_Q_WRITER 0x0C
37#define CONDOR_MOUSE_Q_BEGIN 0x10
38#define CONDOR_MOUSE_MAX_X 0x14
39#define CONDOR_MOUSE_MAX_Y 0x18
40
41#define CONDOR_INPUT_DESKTOP_INFO 0x1F0
42#define CONDOR_INPUT_DISPLAY_RESX 0x1F4
43#define CONDOR_INPUT_DISPLAY_RESY 0x1F8
44#define CONDOR_INPUT_DISPLAY_BITS 0x1FC
45#define CONDOR_OUTPUT_VNC_STATUS 0x200
46
47#define CONDOR_MOUSE_INTR_STATUS_MASK 0x00000001
48
49#define INPUT_TYPE_MOUSE 0x1
50#define INPUT_TYPE_KEYBOARD 0x2
51
52
53/* mouse button states received from SP */
54#define REMOTE_MOUSE_DOUBLE_CLICK 0xF0
55#define REMOTE_MOUSE_BUTTON_LEFT 0x01
56#define REMOTE_MOUSE_BUTTON_MIDDLE 0x02
57#define REMOTE_MOUSE_BUTTON_RIGHT 0x04
58
59
60struct mouse_input {
61 unsigned short y;
62 unsigned short x;
63};
64
65
66struct keyboard_input {
67 unsigned short key_code;
68 unsigned char key_flag;
69 unsigned char key_down;
70};
71
72
73
74struct remote_input {
75 union {
76 struct mouse_input mouse;
77 struct keyboard_input keyboard;
78 } data;
79
80 unsigned char type;
81 unsigned char pad1;
82 unsigned char mouse_buttons;
83 unsigned char pad3;
84};
85
86#define mouse_addr(sp) sp->base_address + CONDOR_MOUSE_DATA
87#define display_width(sp) mouse_addr(sp) + CONDOR_INPUT_DISPLAY_RESX
88#define display_height(sp) mouse_addr(sp) + CONDOR_INPUT_DISPLAY_RESY
89#define display_depth(sp) mouse_addr(sp) + CONDOR_INPUT_DISPLAY_BITS
90#define vnc_status(sp) mouse_addr(sp) + CONDOR_OUTPUT_VNC_STATUS
91
92#define mouse_interrupt_pending(sp) readl(mouse_addr(sp) + CONDOR_MOUSE_ISR_STATUS)
93#define clear_mouse_interrupt(sp) writel(0, mouse_addr(sp) + CONDOR_MOUSE_ISR_STATUS)
94#define enable_mouse_interrupts(sp) writel(1, mouse_addr(sp) + CONDOR_MOUSE_ISR_CONTROL)
95#define disable_mouse_interrupts(sp) writel(0, mouse_addr(sp) + CONDOR_MOUSE_ISR_CONTROL)
96
97/* remote input queue operations */
98#define REMOTE_QUEUE_SIZE 60
99
100#define get_queue_writer(sp) readl(mouse_addr(sp) + CONDOR_MOUSE_Q_WRITER)
101#define get_queue_reader(sp) readl(mouse_addr(sp) + CONDOR_MOUSE_Q_READER)
102#define set_queue_reader(sp, reader) writel(reader, mouse_addr(sp) + CONDOR_MOUSE_Q_READER)
103
104#define queue_begin mouse_addr(sp) + CONDOR_MOUSE_Q_BEGIN
105
106#define get_queue_entry(sp, read_index) \
107 queue_begin + read_index * sizeof(struct remote_input)
108
109static inline int advance_queue_reader(struct service_processor *sp, unsigned long reader)
110{
111 reader++;
112 if (reader == REMOTE_QUEUE_SIZE)
113 reader = 0;
114
115 set_queue_reader(sp, reader);
116 return reader;
117}
118
119#endif /* _IBMASM_REMOTE_H_ */
diff --git a/drivers/misc/ibmasm/uart.c b/drivers/misc/ibmasm/uart.c
new file mode 100644
index 000000000000..914804512dba
--- /dev/null
+++ b/drivers/misc/ibmasm/uart.c
@@ -0,0 +1,72 @@
1
2/*
3 * IBM ASM Service Processor Device Driver
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 *
19 * Copyright (C) IBM Corporation, 2004
20 *
21 * Author: Max Asböck <amax@us.ibm.com>
22 *
23 */
24
25#include <linux/termios.h>
26#include <linux/tty.h>
27#include <linux/serial_core.h>
28#include <linux/serial.h>
29#include <linux/serial_reg.h>
30#include "ibmasm.h"
31#include "lowlevel.h"
32
33
34void ibmasm_register_uart(struct service_processor *sp)
35{
36 struct serial_struct serial;
37 void __iomem *iomem_base;
38
39 iomem_base = sp->base_address + SCOUT_COM_B_BASE;
40
41 /* read the uart scratch register to determine if the UART
42 * is dedicated to the service processor or if the OS can use it
43 */
44 if (0 == readl(iomem_base + UART_SCR)) {
45 dev_info(sp->dev, "IBM SP UART not registered, owned by service processor\n");
46 sp->serial_line = -1;
47 return;
48 }
49
50 memset(&serial, 0, sizeof(serial));
51 serial.irq = sp->irq;
52 serial.baud_base = 3686400 / 16;
53 serial.flags = UPF_AUTOPROBE | UPF_SHARE_IRQ;
54 serial.io_type = UPIO_MEM;
55 serial.iomem_base = iomem_base;
56
57 sp->serial_line = register_serial(&serial);
58 if (sp->serial_line < 0) {
59 dev_err(sp->dev, "Failed to register serial port\n");
60 return;
61 }
62 enable_uart_interrupts(sp->base_address);
63}
64
65void ibmasm_unregister_uart(struct service_processor *sp)
66{
67 if (sp->serial_line < 0)
68 return;
69
70 disable_uart_interrupts(sp->base_address);
71 unregister_serial(sp->serial_line);
72}