aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc
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
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')
-rw-r--r--drivers/misc/Kconfig33
-rw-r--r--drivers/misc/Makefile7
-rw-r--r--drivers/misc/hdpuftrs/Makefile1
-rw-r--r--drivers/misc/hdpuftrs/hdpu_cpustate.c234
-rw-r--r--drivers/misc/hdpuftrs/hdpu_nexus.c111
-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
21 files changed, 2962 insertions, 0 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
new file mode 100644
index 000000000000..4cecdafeb87d
--- /dev/null
+++ b/drivers/misc/Kconfig
@@ -0,0 +1,33 @@
1#
2# Misc strange devices
3#
4
5menu "Misc devices"
6
7config IBM_ASM
8 tristate "Device driver for IBM RSA service processor"
9 depends on X86 && EXPERIMENTAL
10 default n
11 ---help---
12 This option enables device driver support for in-band access to the
13 IBM RSA (Condor) service processor in eServer xSeries systems.
14 The ibmasm device driver allows user space application to access
15 ASM (Advanced Systems Management) functions on the service
16 processor. The driver is meant to be used in conjunction with
17 a user space API.
18 The ibmasm driver also enables the OS to use the UART on the
19 service processor board as a regular serial port. To make use of
20 this feature serial driver support (CONFIG_SERIAL_8250) must be
21 enabled.
22
23 WARNING: This software may not be supported or function
24 correctly on your IBM server. Please consult the IBM ServerProven
25 website <http://www.pc.ibm/ww/eserver/xseries/serverproven> for
26 information on the specific driver level and support statement
27 for your IBM server.
28
29
30 If unsure, say N.
31
32endmenu
33
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
new file mode 100644
index 000000000000..19c2b85249c3
--- /dev/null
+++ b/drivers/misc/Makefile
@@ -0,0 +1,7 @@
1#
2# Makefile for misc devices that really don't fit anywhere else.
3#
4obj- := misc.o # Dummy rule to force built-in.o to be made
5
6obj-$(CONFIG_IBM_ASM) += ibmasm/
7obj-$(CONFIG_HDPU_FEATURES) += hdpuftrs/
diff --git a/drivers/misc/hdpuftrs/Makefile b/drivers/misc/hdpuftrs/Makefile
new file mode 100644
index 000000000000..ac74ae679230
--- /dev/null
+++ b/drivers/misc/hdpuftrs/Makefile
@@ -0,0 +1 @@
obj-$(CONFIG_HDPU_FEATURES) := hdpu_cpustate.o hdpu_nexus.o
diff --git a/drivers/misc/hdpuftrs/hdpu_cpustate.c b/drivers/misc/hdpuftrs/hdpu_cpustate.c
new file mode 100644
index 000000000000..7501fab349e4
--- /dev/null
+++ b/drivers/misc/hdpuftrs/hdpu_cpustate.c
@@ -0,0 +1,234 @@
1/*
2 * Sky CPU State Driver
3 *
4 * Copyright (C) 2002 Brian Waite
5 *
6 * This driver allows use of the CPU state bits
7 * It exports the /dev/sky_cpustate and also
8 * /proc/sky_cpustate pseudo-file for status information.
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version
13 * 2 of the License, or (at your option) any later version.
14 *
15 */
16
17#include <linux/version.h>
18#include <linux/module.h>
19#include <linux/kernel.h>
20#include <linux/spinlock.h>
21#include <linux/miscdevice.h>
22#include <linux/pci.h>
23#include <linux/proc_fs.h>
24#include <linux/device.h>
25#include <asm/uaccess.h>
26#include <linux/hdpu_features.h>
27
28#define SKY_CPUSTATE_VERSION "1.1"
29
30static int hdpu_cpustate_probe(struct device *ddev);
31static int hdpu_cpustate_remove(struct device *ddev);
32
33struct cpustate_t cpustate;
34
35static int cpustate_get_ref(int excl)
36{
37
38 int retval = -EBUSY;
39
40 spin_lock(&cpustate.lock);
41
42 if (cpustate.excl)
43 goto out_busy;
44
45 if (excl) {
46 if (cpustate.open_count)
47 goto out_busy;
48 cpustate.excl = 1;
49 }
50
51 cpustate.open_count++;
52 retval = 0;
53
54 out_busy:
55 spin_unlock(&cpustate.lock);
56 return retval;
57}
58
59static int cpustate_free_ref(void)
60{
61
62 spin_lock(&cpustate.lock);
63
64 cpustate.excl = 0;
65 cpustate.open_count--;
66
67 spin_unlock(&cpustate.lock);
68 return 0;
69}
70
71unsigned char cpustate_get_state(void)
72{
73
74 return cpustate.cached_val;
75}
76
77void cpustate_set_state(unsigned char new_state)
78{
79 unsigned int state = (new_state << 21);
80
81#ifdef DEBUG_CPUSTATE
82 printk("CPUSTATE -> 0x%x\n", new_state);
83#endif
84 spin_lock(&cpustate.lock);
85 cpustate.cached_val = new_state;
86 writel((0xff << 21), cpustate.clr_addr);
87 writel(state, cpustate.set_addr);
88 spin_unlock(&cpustate.lock);
89}
90
91/*
92 * Now all the various file operations that we export.
93 */
94
95static ssize_t cpustate_read(struct file *file, char *buf,
96 size_t count, loff_t * ppos)
97{
98 unsigned char data;
99
100 if (count < 0)
101 return -EFAULT;
102 if (count == 0)
103 return 0;
104
105 data = cpustate_get_state();
106 if (copy_to_user(buf, &data, sizeof(unsigned char)))
107 return -EFAULT;
108 return sizeof(unsigned char);
109}
110
111static ssize_t cpustate_write(struct file *file, const char *buf,
112 size_t count, loff_t * ppos)
113{
114 unsigned char data;
115
116 if (count < 0)
117 return -EFAULT;
118
119 if (count == 0)
120 return 0;
121
122 if (copy_from_user((unsigned char *)&data, buf, sizeof(unsigned char)))
123 return -EFAULT;
124
125 cpustate_set_state(data);
126 return sizeof(unsigned char);
127}
128
129static int cpustate_open(struct inode *inode, struct file *file)
130{
131 return cpustate_get_ref((file->f_flags & O_EXCL));
132}
133
134static int cpustate_release(struct inode *inode, struct file *file)
135{
136 return cpustate_free_ref();
137}
138
139/*
140 * Info exported via "/proc/sky_cpustate".
141 */
142static int cpustate_read_proc(char *page, char **start, off_t off,
143 int count, int *eof, void *data)
144{
145 char *p = page;
146 int len = 0;
147
148 p += sprintf(p, "CPU State: %04x\n", cpustate_get_state());
149 len = p - page;
150
151 if (len <= off + count)
152 *eof = 1;
153 *start = page + off;
154 len -= off;
155 if (len > count)
156 len = count;
157 if (len < 0)
158 len = 0;
159 return len;
160}
161
162static struct device_driver hdpu_cpustate_driver = {
163 .name = HDPU_CPUSTATE_NAME,
164 .bus = &platform_bus_type,
165 .probe = hdpu_cpustate_probe,
166 .remove = hdpu_cpustate_remove,
167};
168
169/*
170 * The various file operations we support.
171 */
172static struct file_operations cpustate_fops = {
173 owner:THIS_MODULE,
174 open:cpustate_open,
175 release:cpustate_release,
176 read:cpustate_read,
177 write:cpustate_write,
178 fasync:NULL,
179 poll:NULL,
180 ioctl:NULL,
181 llseek:no_llseek,
182
183};
184
185static struct miscdevice cpustate_dev = {
186 MISC_DYNAMIC_MINOR,
187 "sky_cpustate",
188 &cpustate_fops
189};
190
191static int hdpu_cpustate_probe(struct device *ddev)
192{
193 struct platform_device *pdev = to_platform_device(ddev);
194 struct resource *res;
195
196 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
197 cpustate.set_addr = (unsigned long *)res->start;
198 cpustate.clr_addr = (unsigned long *)res->end - 1;
199
200 misc_register(&cpustate_dev);
201 create_proc_read_entry("sky_cpustate", 0, 0, cpustate_read_proc, NULL);
202
203 printk(KERN_INFO "Sky CPU State Driver v" SKY_CPUSTATE_VERSION "\n");
204 return 0;
205}
206static int hdpu_cpustate_remove(struct device *ddev)
207{
208
209 cpustate.set_addr = 0;
210 cpustate.clr_addr = 0;
211
212 remove_proc_entry("sky_cpustate", NULL);
213 misc_deregister(&cpustate_dev);
214 return 0;
215
216}
217
218static int __init cpustate_init(void)
219{
220 int rc;
221 rc = driver_register(&hdpu_cpustate_driver);
222 return rc;
223}
224
225static void __exit cpustate_exit(void)
226{
227 driver_unregister(&hdpu_cpustate_driver);
228}
229
230module_init(cpustate_init);
231module_exit(cpustate_exit);
232
233MODULE_AUTHOR("Brian Waite");
234MODULE_LICENSE("GPL");
diff --git a/drivers/misc/hdpuftrs/hdpu_nexus.c b/drivers/misc/hdpuftrs/hdpu_nexus.c
new file mode 100644
index 000000000000..c203b27269ea
--- /dev/null
+++ b/drivers/misc/hdpuftrs/hdpu_nexus.c
@@ -0,0 +1,111 @@
1/*
2 * Sky Nexus Register Driver
3 *
4 * Copyright (C) 2002 Brian Waite
5 *
6 * This driver allows reading the Nexus register
7 * It exports the /proc/sky_chassis_id and also
8 * /proc/sky_slot_id pseudo-file for status information.
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version
13 * 2 of the License, or (at your option) any later version.
14 *
15 */
16
17#include <linux/version.h>
18#include <linux/module.h>
19#include <linux/kernel.h>
20#include <linux/proc_fs.h>
21#include <linux/hdpu_features.h>
22#include <linux/pci.h>
23
24#include <linux/device.h>
25
26static int hdpu_nexus_probe(struct device *ddev);
27static int hdpu_nexus_remove(struct device *ddev);
28
29static struct proc_dir_entry *hdpu_slot_id;
30static struct proc_dir_entry *hdpu_chassis_id;
31static int slot_id = -1;
32static int chassis_id = -1;
33
34static struct device_driver hdpu_nexus_driver = {
35 .name = HDPU_NEXUS_NAME,
36 .bus = &platform_bus_type,
37 .probe = hdpu_nexus_probe,
38 .remove = hdpu_nexus_remove,
39};
40
41int hdpu_slot_id_read(char *buffer, char **buffer_location, off_t offset,
42 int buffer_length, int *zero, void *ptr)
43{
44
45 if (offset > 0)
46 return 0;
47 return sprintf(buffer, "%d\n", slot_id);
48}
49
50int hdpu_chassis_id_read(char *buffer, char **buffer_location, off_t offset,
51 int buffer_length, int *zero, void *ptr)
52{
53
54 if (offset > 0)
55 return 0;
56 return sprintf(buffer, "%d\n", chassis_id);
57}
58
59static int hdpu_nexus_probe(struct device *ddev)
60{
61 struct platform_device *pdev = to_platform_device(ddev);
62 struct resource *res;
63
64 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
65 int *nexus_id_addr;
66 nexus_id_addr =
67 ioremap(res->start, (unsigned long)(res->end - res->start));
68 if (nexus_id_addr) {
69 slot_id = (*nexus_id_addr >> 8) & 0x1f;
70 chassis_id = *nexus_id_addr & 0xff;
71 iounmap(nexus_id_addr);
72 } else
73 printk("Could not map slot id\n");
74 hdpu_slot_id = create_proc_entry("sky_slot_id", 0666, &proc_root);
75 hdpu_slot_id->read_proc = hdpu_slot_id_read;
76 hdpu_slot_id->nlink = 1;
77
78 hdpu_chassis_id = create_proc_entry("sky_chassis_id", 0666, &proc_root);
79 hdpu_chassis_id->read_proc = hdpu_chassis_id_read;
80 hdpu_chassis_id->nlink = 1;
81 return 0;
82}
83
84static int hdpu_nexus_remove(struct device *ddev)
85{
86 slot_id = -1;
87 chassis_id = -1;
88 remove_proc_entry("sky_slot_id", &proc_root);
89 remove_proc_entry("sky_chassis_id", &proc_root);
90 hdpu_slot_id = 0;
91 hdpu_chassis_id = 0;
92 return 0;
93}
94
95static int __init nexus_init(void)
96{
97 int rc;
98 rc = driver_register(&hdpu_nexus_driver);
99 return rc;
100}
101
102static void __exit nexus_exit(void)
103{
104 driver_unregister(&hdpu_nexus_driver);
105}
106
107module_init(nexus_init);
108module_exit(nexus_exit);
109
110MODULE_AUTHOR("Brian Waite");
111MODULE_LICENSE("GPL");
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}