Commit | Line | Data |
---|---|---|
1da177e4 LT |
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 | * | |
d36b6910 | 21 | * Author: Max Asböck <amax@us.ibm.com> |
1da177e4 | 22 | * |
3110dc7a | 23 | * This driver is based on code originally written by Pete Reynolds |
1da177e4 LT |
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 | |
3110dc7a | 33 | * to send periodic heartbeats to the OS. |
1da177e4 LT |
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. | |
3110dc7a | 39 | * The interface for this is the ibmasmfs file system. |
1da177e4 LT |
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> | |
5a0e3ad6 | 55 | #include <linux/slab.h> |
1da177e4 LT |
56 | #include "ibmasm.h" |
57 | #include "lowlevel.h" | |
58 | #include "remote.h" | |
59 | ||
278d72ae MA |
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 | ||
1da177e4 | 64 | |
80c8ae28 | 65 | static int ibmasm_init_one(struct pci_dev *pdev, const struct pci_device_id *id) |
1da177e4 | 66 | { |
278d72ae | 67 | int result; |
1da177e4 LT |
68 | struct service_processor *sp; |
69 | ||
278d72ae MA |
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; | |
1da177e4 | 77 | } |
278d72ae MA |
78 | /* vnc client won't work without bus-mastering */ |
79 | pci_set_master(pdev); | |
1da177e4 | 80 | |
dd00cc48 | 81 | sp = kzalloc(sizeof(struct service_processor), GFP_KERNEL); |
1da177e4 LT |
82 | if (sp == NULL) { |
83 | dev_err(&pdev->dev, "Failed to allocate memory\n"); | |
84 | result = -ENOMEM; | |
85 | goto error_kmalloc; | |
86 | } | |
1da177e4 | 87 | |
34af946a | 88 | spin_lock_init(&sp->lock); |
278d72ae MA |
89 | INIT_LIST_HEAD(&sp->command_queue); |
90 | ||
1da177e4 LT |
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; | |
bdbeed75 | 108 | sp->base_address = pci_ioremap_bar(pdev, 0); |
5cf83b9b | 109 | if (!sp->base_address) { |
1da177e4 LT |
110 | dev_err(sp->dev, "Failed to ioremap pci memory\n"); |
111 | result = -ENODEV; | |
112 | goto error_ioremap; | |
113 | } | |
114 | ||
dace1453 | 115 | result = request_irq(sp->irq, ibmasm_interrupt_handler, IRQF_SHARED, sp->devname, (void*)sp); |
1da177e4 LT |
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); | |
278d72ae MA |
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 | } | |
1da177e4 LT |
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 | ||
1da177e4 LT |
143 | return 0; |
144 | ||
145 | error_send_message: | |
146 | disable_sp_interrupts(sp->base_address); | |
278d72ae | 147 | ibmasm_free_remote_input_dev(sp); |
1da177e4 LT |
148 | free_irq(sp->irq, (void *)sp); |
149 | error_request_irq: | |
1da177e4 LT |
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: | |
278d72ae | 156 | pci_set_drvdata(pdev, NULL); |
1da177e4 LT |
157 | kfree(sp); |
158 | error_kmalloc: | |
278d72ae MA |
159 | pci_release_regions(pdev); |
160 | error_resources: | |
161 | pci_disable_device(pdev); | |
1da177e4 LT |
162 | |
163 | return result; | |
164 | } | |
165 | ||
486a5c28 | 166 | static void ibmasm_remove_one(struct pci_dev *pdev) |
1da177e4 LT |
167 | { |
168 | struct service_processor *sp = (struct service_processor *)pci_get_drvdata(pdev); | |
169 | ||
278d72ae | 170 | dbg("Unregistering UART\n"); |
1da177e4 | 171 | ibmasm_unregister_uart(sp); |
278d72ae MA |
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"); | |
1da177e4 | 178 | disable_sp_interrupts(sp->base_address); |
278d72ae | 179 | dbg("Freeing SP irq\n"); |
1da177e4 | 180 | free_irq(sp->irq, (void *)sp); |
278d72ae MA |
181 | dbg("Cleaning up\n"); |
182 | ibmasm_free_remote_input_dev(sp); | |
1da177e4 LT |
183 | iounmap(sp->base_address); |
184 | ibmasm_event_buffer_exit(sp); | |
278d72ae | 185 | pci_set_drvdata(pdev, NULL); |
1da177e4 | 186 | kfree(sp); |
278d72ae | 187 | pci_release_regions(pdev); |
1da177e4 LT |
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, | |
2d6bed9c | 201 | .remove = ibmasm_remove_one, |
1da177e4 LT |
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 | { | |
ea23b453 AV |
214 | int result = pci_register_driver(&ibmasm_driver); |
215 | if (result) | |
216 | return result; | |
1da177e4 LT |
217 | |
218 | result = ibmasmfs_register(); | |
219 | if (result) { | |
ea23b453 | 220 | pci_unregister_driver(&ibmasm_driver); |
1da177e4 LT |
221 | err("Failed to register ibmasmfs file system"); |
222 | return result; | |
223 | } | |
ea23b453 | 224 | |
1da177e4 LT |
225 | ibmasm_register_panic_notifier(); |
226 | info(DRIVER_DESC " version " DRIVER_VERSION " loaded"); | |
227 | return 0; | |
228 | } | |
229 | ||
230 | module_init(ibmasm_init); | |
231 | module_exit(ibmasm_exit); | |
232 | ||
233 | MODULE_AUTHOR(DRIVER_AUTHOR); | |
234 | MODULE_DESCRIPTION(DRIVER_DESC); | |
235 | MODULE_LICENSE("GPL"); | |
236 | MODULE_DEVICE_TABLE(pci, ibmasm_pci_table); | |
237 |