Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/misc/ibmasm/module.c
17308 views
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 <[email protected]>
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 <linux/slab.h>
56
#include "ibmasm.h"
57
#include "lowlevel.h"
58
#include "remote.h"
59
60
int ibmasm_debug = 0;
61
module_param(ibmasm_debug, int , S_IRUGO | S_IWUSR);
62
MODULE_PARM_DESC(ibmasm_debug, " Set debug mode on or off");
63
64
65
static int __devinit ibmasm_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
66
{
67
int result;
68
struct service_processor *sp;
69
70
if ((result = pci_enable_device(pdev))) {
71
dev_err(&pdev->dev, "Failed to enable PCI device\n");
72
return result;
73
}
74
if ((result = pci_request_regions(pdev, DRIVER_NAME))) {
75
dev_err(&pdev->dev, "Failed to allocate PCI resources\n");
76
goto error_resources;
77
}
78
/* vnc client won't work without bus-mastering */
79
pci_set_master(pdev);
80
81
sp = kzalloc(sizeof(struct service_processor), GFP_KERNEL);
82
if (sp == NULL) {
83
dev_err(&pdev->dev, "Failed to allocate memory\n");
84
result = -ENOMEM;
85
goto error_kmalloc;
86
}
87
88
spin_lock_init(&sp->lock);
89
INIT_LIST_HEAD(&sp->command_queue);
90
91
pci_set_drvdata(pdev, (void *)sp);
92
sp->dev = &pdev->dev;
93
sp->number = pdev->bus->number;
94
snprintf(sp->dirname, IBMASM_NAME_SIZE, "%d", sp->number);
95
snprintf(sp->devname, IBMASM_NAME_SIZE, "%s%d", DRIVER_NAME, sp->number);
96
97
if (ibmasm_event_buffer_init(sp)) {
98
dev_err(sp->dev, "Failed to allocate event buffer\n");
99
goto error_eventbuffer;
100
}
101
102
if (ibmasm_heartbeat_init(sp)) {
103
dev_err(sp->dev, "Failed to allocate heartbeat command\n");
104
goto error_heartbeat;
105
}
106
107
sp->irq = pdev->irq;
108
sp->base_address = pci_ioremap_bar(pdev, 0);
109
if (!sp->base_address) {
110
dev_err(sp->dev, "Failed to ioremap pci memory\n");
111
result = -ENODEV;
112
goto error_ioremap;
113
}
114
115
result = request_irq(sp->irq, ibmasm_interrupt_handler, IRQF_SHARED, sp->devname, (void*)sp);
116
if (result) {
117
dev_err(sp->dev, "Failed to register interrupt handler\n");
118
goto error_request_irq;
119
}
120
121
enable_sp_interrupts(sp->base_address);
122
123
result = ibmasm_init_remote_input_dev(sp);
124
if (result) {
125
dev_err(sp->dev, "Failed to initialize remote queue\n");
126
goto error_send_message;
127
}
128
129
result = ibmasm_send_driver_vpd(sp);
130
if (result) {
131
dev_err(sp->dev, "Failed to send driver VPD to service processor\n");
132
goto error_send_message;
133
}
134
result = ibmasm_send_os_state(sp, SYSTEM_STATE_OS_UP);
135
if (result) {
136
dev_err(sp->dev, "Failed to send OS state to service processor\n");
137
goto error_send_message;
138
}
139
ibmasmfs_add_sp(sp);
140
141
ibmasm_register_uart(sp);
142
143
return 0;
144
145
error_send_message:
146
disable_sp_interrupts(sp->base_address);
147
ibmasm_free_remote_input_dev(sp);
148
free_irq(sp->irq, (void *)sp);
149
error_request_irq:
150
iounmap(sp->base_address);
151
error_ioremap:
152
ibmasm_heartbeat_exit(sp);
153
error_heartbeat:
154
ibmasm_event_buffer_exit(sp);
155
error_eventbuffer:
156
pci_set_drvdata(pdev, NULL);
157
kfree(sp);
158
error_kmalloc:
159
pci_release_regions(pdev);
160
error_resources:
161
pci_disable_device(pdev);
162
163
return result;
164
}
165
166
static void __devexit ibmasm_remove_one(struct pci_dev *pdev)
167
{
168
struct service_processor *sp = (struct service_processor *)pci_get_drvdata(pdev);
169
170
dbg("Unregistering UART\n");
171
ibmasm_unregister_uart(sp);
172
dbg("Sending OS down message\n");
173
if (ibmasm_send_os_state(sp, SYSTEM_STATE_OS_DOWN))
174
err("failed to get repsonse to 'Send OS State' command\n");
175
dbg("Disabling heartbeats\n");
176
ibmasm_heartbeat_exit(sp);
177
dbg("Disabling interrupts\n");
178
disable_sp_interrupts(sp->base_address);
179
dbg("Freeing SP irq\n");
180
free_irq(sp->irq, (void *)sp);
181
dbg("Cleaning up\n");
182
ibmasm_free_remote_input_dev(sp);
183
iounmap(sp->base_address);
184
ibmasm_event_buffer_exit(sp);
185
pci_set_drvdata(pdev, NULL);
186
kfree(sp);
187
pci_release_regions(pdev);
188
pci_disable_device(pdev);
189
}
190
191
static struct pci_device_id ibmasm_pci_table[] =
192
{
193
{ PCI_DEVICE(VENDORID_IBM, DEVICEID_RSA) },
194
{},
195
};
196
197
static struct pci_driver ibmasm_driver = {
198
.name = DRIVER_NAME,
199
.id_table = ibmasm_pci_table,
200
.probe = ibmasm_init_one,
201
.remove = __devexit_p(ibmasm_remove_one),
202
};
203
204
static void __exit ibmasm_exit (void)
205
{
206
ibmasm_unregister_panic_notifier();
207
ibmasmfs_unregister();
208
pci_unregister_driver(&ibmasm_driver);
209
info(DRIVER_DESC " version " DRIVER_VERSION " unloaded");
210
}
211
212
static int __init ibmasm_init(void)
213
{
214
int result;
215
216
result = ibmasmfs_register();
217
if (result) {
218
err("Failed to register ibmasmfs file system");
219
return result;
220
}
221
result = pci_register_driver(&ibmasm_driver);
222
if (result) {
223
ibmasmfs_unregister();
224
return result;
225
}
226
ibmasm_register_panic_notifier();
227
info(DRIVER_DESC " version " DRIVER_VERSION " loaded");
228
return 0;
229
}
230
231
module_init(ibmasm_init);
232
module_exit(ibmasm_exit);
233
234
MODULE_AUTHOR(DRIVER_AUTHOR);
235
MODULE_DESCRIPTION(DRIVER_DESC);
236
MODULE_LICENSE("GPL");
237
MODULE_DEVICE_TABLE(pci, ibmasm_pci_table);
238
239
240