xref: /linux/drivers/misc/ibmasm/module.c (revision 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2)
1*1da177e4SLinus Torvalds 
2*1da177e4SLinus Torvalds /*
3*1da177e4SLinus Torvalds  * IBM ASM Service Processor Device Driver
4*1da177e4SLinus Torvalds  *
5*1da177e4SLinus Torvalds  * This program is free software; you can redistribute it and/or modify
6*1da177e4SLinus Torvalds  * it under the terms of the GNU General Public License as published by
7*1da177e4SLinus Torvalds  * the Free Software Foundation; either version 2 of the License, or
8*1da177e4SLinus Torvalds  * (at your option) any later version.
9*1da177e4SLinus Torvalds  *
10*1da177e4SLinus Torvalds  * This program is distributed in the hope that it will be useful,
11*1da177e4SLinus Torvalds  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12*1da177e4SLinus Torvalds  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13*1da177e4SLinus Torvalds  * GNU General Public License for more details.
14*1da177e4SLinus Torvalds  *
15*1da177e4SLinus Torvalds  * You should have received a copy of the GNU General Public License
16*1da177e4SLinus Torvalds  * along with this program; if not, write to the Free Software
17*1da177e4SLinus Torvalds  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18*1da177e4SLinus Torvalds  *
19*1da177e4SLinus Torvalds  * Copyright (C) IBM Corporation, 2004
20*1da177e4SLinus Torvalds  *
21*1da177e4SLinus Torvalds  * Author: Max Asb�ck <amax@us.ibm.com>
22*1da177e4SLinus Torvalds  *
23*1da177e4SLinus Torvalds  * This driver is based on code originally written by Pete Reynolds
24*1da177e4SLinus Torvalds  * and others.
25*1da177e4SLinus Torvalds  *
26*1da177e4SLinus Torvalds  */
27*1da177e4SLinus Torvalds 
28*1da177e4SLinus Torvalds /*
29*1da177e4SLinus Torvalds  * The ASM device driver does the following things:
30*1da177e4SLinus Torvalds  *
31*1da177e4SLinus Torvalds  * 1) When loaded it sends a message to the service processor,
32*1da177e4SLinus Torvalds  * indicating that an OS is * running. This causes the service processor
33*1da177e4SLinus Torvalds  * to send periodic heartbeats to the OS.
34*1da177e4SLinus Torvalds  *
35*1da177e4SLinus Torvalds  * 2) Answers the periodic heartbeats sent by the service processor.
36*1da177e4SLinus Torvalds  * Failure to do so would result in system reboot.
37*1da177e4SLinus Torvalds  *
38*1da177e4SLinus Torvalds  * 3) Acts as a pass through for dot commands sent from user applications.
39*1da177e4SLinus Torvalds  * The interface for this is the ibmasmfs file system.
40*1da177e4SLinus Torvalds  *
41*1da177e4SLinus Torvalds  * 4) Allows user applications to register for event notification. Events
42*1da177e4SLinus Torvalds  * are sent to the driver through interrupts. They can be read from user
43*1da177e4SLinus Torvalds  * space through the ibmasmfs file system.
44*1da177e4SLinus Torvalds  *
45*1da177e4SLinus Torvalds  * 5) Allows user space applications to send heartbeats to the service
46*1da177e4SLinus Torvalds  * processor (aka reverse heartbeats). Again this happens through ibmasmfs.
47*1da177e4SLinus Torvalds  *
48*1da177e4SLinus Torvalds  * 6) Handles remote mouse and keyboard event interrupts and makes them
49*1da177e4SLinus Torvalds  * available to user applications through ibmasmfs.
50*1da177e4SLinus Torvalds  *
51*1da177e4SLinus Torvalds  */
52*1da177e4SLinus Torvalds 
53*1da177e4SLinus Torvalds #include <linux/pci.h>
54*1da177e4SLinus Torvalds #include <linux/init.h>
55*1da177e4SLinus Torvalds #include "ibmasm.h"
56*1da177e4SLinus Torvalds #include "lowlevel.h"
57*1da177e4SLinus Torvalds #include "remote.h"
58*1da177e4SLinus Torvalds 
59*1da177e4SLinus Torvalds 
60*1da177e4SLinus Torvalds static int __devinit ibmasm_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
61*1da177e4SLinus Torvalds {
62*1da177e4SLinus Torvalds 	int err, result = -ENOMEM;
63*1da177e4SLinus Torvalds 	struct service_processor *sp;
64*1da177e4SLinus Torvalds 
65*1da177e4SLinus Torvalds 	if ((err = pci_enable_device(pdev))) {
66*1da177e4SLinus Torvalds 		printk(KERN_ERR "%s: can't enable PCI device at %s\n",
67*1da177e4SLinus Torvalds 			DRIVER_NAME, pci_name(pdev));
68*1da177e4SLinus Torvalds 		return err;
69*1da177e4SLinus Torvalds 	}
70*1da177e4SLinus Torvalds 
71*1da177e4SLinus Torvalds 	sp = kmalloc(sizeof(struct service_processor), GFP_KERNEL);
72*1da177e4SLinus Torvalds 	if (sp == NULL) {
73*1da177e4SLinus Torvalds 		dev_err(&pdev->dev, "Failed to allocate memory\n");
74*1da177e4SLinus Torvalds 		result = -ENOMEM;
75*1da177e4SLinus Torvalds 		goto error_kmalloc;
76*1da177e4SLinus Torvalds 	}
77*1da177e4SLinus Torvalds 	memset(sp, 0, sizeof(struct service_processor));
78*1da177e4SLinus Torvalds 
79*1da177e4SLinus Torvalds 	pci_set_drvdata(pdev, (void *)sp);
80*1da177e4SLinus Torvalds 	sp->dev = &pdev->dev;
81*1da177e4SLinus Torvalds 	sp->number = pdev->bus->number;
82*1da177e4SLinus Torvalds 	snprintf(sp->dirname, IBMASM_NAME_SIZE, "%d", sp->number);
83*1da177e4SLinus Torvalds 	snprintf(sp->devname, IBMASM_NAME_SIZE, "%s%d", DRIVER_NAME, sp->number);
84*1da177e4SLinus Torvalds 
85*1da177e4SLinus Torvalds 	if (ibmasm_event_buffer_init(sp)) {
86*1da177e4SLinus Torvalds 		dev_err(sp->dev, "Failed to allocate event buffer\n");
87*1da177e4SLinus Torvalds 		goto error_eventbuffer;
88*1da177e4SLinus Torvalds 	}
89*1da177e4SLinus Torvalds 
90*1da177e4SLinus Torvalds 	if (ibmasm_heartbeat_init(sp)) {
91*1da177e4SLinus Torvalds 		dev_err(sp->dev, "Failed to allocate heartbeat command\n");
92*1da177e4SLinus Torvalds 		goto error_heartbeat;
93*1da177e4SLinus Torvalds 	}
94*1da177e4SLinus Torvalds 
95*1da177e4SLinus Torvalds 	sp->irq = pdev->irq;
96*1da177e4SLinus Torvalds 	sp->base_address = ioremap(pci_resource_start(pdev, 0),
97*1da177e4SLinus Torvalds 					pci_resource_len(pdev, 0));
98*1da177e4SLinus Torvalds 	if (sp->base_address == 0) {
99*1da177e4SLinus Torvalds 		dev_err(sp->dev, "Failed to ioremap pci memory\n");
100*1da177e4SLinus Torvalds 		result =  -ENODEV;
101*1da177e4SLinus Torvalds 		goto error_ioremap;
102*1da177e4SLinus Torvalds 	}
103*1da177e4SLinus Torvalds 
104*1da177e4SLinus Torvalds 	result = ibmasm_init_remote_queue(sp);
105*1da177e4SLinus Torvalds 	if (result) {
106*1da177e4SLinus Torvalds 		dev_err(sp->dev, "Failed to initialize remote queue\n");
107*1da177e4SLinus Torvalds 		goto error_remote_queue;
108*1da177e4SLinus Torvalds 	}
109*1da177e4SLinus Torvalds 
110*1da177e4SLinus Torvalds 	spin_lock_init(&sp->lock);
111*1da177e4SLinus Torvalds 	INIT_LIST_HEAD(&sp->command_queue);
112*1da177e4SLinus Torvalds 
113*1da177e4SLinus Torvalds 	result = request_irq(sp->irq, ibmasm_interrupt_handler, SA_SHIRQ, sp->devname, (void*)sp);
114*1da177e4SLinus Torvalds 	if (result) {
115*1da177e4SLinus Torvalds 		dev_err(sp->dev, "Failed to register interrupt handler\n");
116*1da177e4SLinus Torvalds 		goto error_request_irq;
117*1da177e4SLinus Torvalds 	}
118*1da177e4SLinus Torvalds 
119*1da177e4SLinus Torvalds 	enable_sp_interrupts(sp->base_address);
120*1da177e4SLinus Torvalds 	disable_mouse_interrupts(sp);
121*1da177e4SLinus Torvalds 
122*1da177e4SLinus Torvalds 	result = ibmasm_send_driver_vpd(sp);
123*1da177e4SLinus Torvalds 	if (result) {
124*1da177e4SLinus Torvalds 		dev_err(sp->dev, "Failed to send driver VPD to service processor\n");
125*1da177e4SLinus Torvalds 		goto error_send_message;
126*1da177e4SLinus Torvalds 	}
127*1da177e4SLinus Torvalds 	result = ibmasm_send_os_state(sp, SYSTEM_STATE_OS_UP);
128*1da177e4SLinus Torvalds 	if (result) {
129*1da177e4SLinus Torvalds 		dev_err(sp->dev, "Failed to send OS state to service processor\n");
130*1da177e4SLinus Torvalds 		goto error_send_message;
131*1da177e4SLinus Torvalds 	}
132*1da177e4SLinus Torvalds 	ibmasmfs_add_sp(sp);
133*1da177e4SLinus Torvalds 
134*1da177e4SLinus Torvalds 	ibmasm_register_uart(sp);
135*1da177e4SLinus Torvalds 
136*1da177e4SLinus Torvalds 	dev_printk(KERN_DEBUG, &pdev->dev, "WARNING: This software may not be supported or function\n");
137*1da177e4SLinus Torvalds 	dev_printk(KERN_DEBUG, &pdev->dev, "correctly on your IBM server. Please consult the IBM\n");
138*1da177e4SLinus Torvalds 	dev_printk(KERN_DEBUG, &pdev->dev, "ServerProven website\n");
139*1da177e4SLinus Torvalds 	dev_printk(KERN_DEBUG, &pdev->dev, "http://www.pc.ibm.com/ww/eserver/xseries/serverproven\n");
140*1da177e4SLinus Torvalds 	dev_printk(KERN_DEBUG, &pdev->dev, "for information on the specific driver level and support\n");
141*1da177e4SLinus Torvalds 	dev_printk(KERN_DEBUG, &pdev->dev, "statement for your IBM server.\n");
142*1da177e4SLinus Torvalds 
143*1da177e4SLinus Torvalds 	return 0;
144*1da177e4SLinus Torvalds 
145*1da177e4SLinus Torvalds error_send_message:
146*1da177e4SLinus Torvalds 	disable_sp_interrupts(sp->base_address);
147*1da177e4SLinus Torvalds 	free_irq(sp->irq, (void *)sp);
148*1da177e4SLinus Torvalds error_request_irq:
149*1da177e4SLinus Torvalds 	ibmasm_free_remote_queue(sp);
150*1da177e4SLinus Torvalds error_remote_queue:
151*1da177e4SLinus Torvalds 	iounmap(sp->base_address);
152*1da177e4SLinus Torvalds error_ioremap:
153*1da177e4SLinus Torvalds 	ibmasm_heartbeat_exit(sp);
154*1da177e4SLinus Torvalds error_heartbeat:
155*1da177e4SLinus Torvalds 	ibmasm_event_buffer_exit(sp);
156*1da177e4SLinus Torvalds error_eventbuffer:
157*1da177e4SLinus Torvalds 	kfree(sp);
158*1da177e4SLinus Torvalds error_kmalloc:
159*1da177e4SLinus Torvalds 	pci_disable_device(pdev);
160*1da177e4SLinus Torvalds 
161*1da177e4SLinus Torvalds 	return result;
162*1da177e4SLinus Torvalds }
163*1da177e4SLinus Torvalds 
164*1da177e4SLinus Torvalds static void __devexit ibmasm_remove_one(struct pci_dev *pdev)
165*1da177e4SLinus Torvalds {
166*1da177e4SLinus Torvalds 	struct service_processor *sp = (struct service_processor *)pci_get_drvdata(pdev);
167*1da177e4SLinus Torvalds 
168*1da177e4SLinus Torvalds 	ibmasm_unregister_uart(sp);
169*1da177e4SLinus Torvalds 	ibmasm_send_os_state(sp, SYSTEM_STATE_OS_DOWN);
170*1da177e4SLinus Torvalds 	disable_sp_interrupts(sp->base_address);
171*1da177e4SLinus Torvalds 	disable_mouse_interrupts(sp);
172*1da177e4SLinus Torvalds 	free_irq(sp->irq, (void *)sp);
173*1da177e4SLinus Torvalds 	ibmasm_heartbeat_exit(sp);
174*1da177e4SLinus Torvalds 	ibmasm_free_remote_queue(sp);
175*1da177e4SLinus Torvalds 	iounmap(sp->base_address);
176*1da177e4SLinus Torvalds 	ibmasm_event_buffer_exit(sp);
177*1da177e4SLinus Torvalds 	kfree(sp);
178*1da177e4SLinus Torvalds 	pci_disable_device(pdev);
179*1da177e4SLinus Torvalds }
180*1da177e4SLinus Torvalds 
181*1da177e4SLinus Torvalds static struct pci_device_id ibmasm_pci_table[] =
182*1da177e4SLinus Torvalds {
183*1da177e4SLinus Torvalds 	{ PCI_DEVICE(VENDORID_IBM, DEVICEID_RSA) },
184*1da177e4SLinus Torvalds 	{},
185*1da177e4SLinus Torvalds };
186*1da177e4SLinus Torvalds 
187*1da177e4SLinus Torvalds static struct pci_driver ibmasm_driver = {
188*1da177e4SLinus Torvalds 	.name		= DRIVER_NAME,
189*1da177e4SLinus Torvalds 	.id_table	= ibmasm_pci_table,
190*1da177e4SLinus Torvalds 	.probe		= ibmasm_init_one,
191*1da177e4SLinus Torvalds 	.remove		= __devexit_p(ibmasm_remove_one),
192*1da177e4SLinus Torvalds };
193*1da177e4SLinus Torvalds 
194*1da177e4SLinus Torvalds static void __exit ibmasm_exit (void)
195*1da177e4SLinus Torvalds {
196*1da177e4SLinus Torvalds 	ibmasm_unregister_panic_notifier();
197*1da177e4SLinus Torvalds 	ibmasmfs_unregister();
198*1da177e4SLinus Torvalds 	pci_unregister_driver(&ibmasm_driver);
199*1da177e4SLinus Torvalds 	info(DRIVER_DESC " version " DRIVER_VERSION " unloaded");
200*1da177e4SLinus Torvalds }
201*1da177e4SLinus Torvalds 
202*1da177e4SLinus Torvalds static int __init ibmasm_init(void)
203*1da177e4SLinus Torvalds {
204*1da177e4SLinus Torvalds 	int result;
205*1da177e4SLinus Torvalds 
206*1da177e4SLinus Torvalds 	result = ibmasmfs_register();
207*1da177e4SLinus Torvalds 	if (result) {
208*1da177e4SLinus Torvalds 		err("Failed to register ibmasmfs file system");
209*1da177e4SLinus Torvalds 		return result;
210*1da177e4SLinus Torvalds 	}
211*1da177e4SLinus Torvalds 	result = pci_register_driver(&ibmasm_driver);
212*1da177e4SLinus Torvalds 	if (result) {
213*1da177e4SLinus Torvalds 		ibmasmfs_unregister();
214*1da177e4SLinus Torvalds 		return result;
215*1da177e4SLinus Torvalds 	}
216*1da177e4SLinus Torvalds 	ibmasm_register_panic_notifier();
217*1da177e4SLinus Torvalds 	info(DRIVER_DESC " version " DRIVER_VERSION " loaded");
218*1da177e4SLinus Torvalds 	return 0;
219*1da177e4SLinus Torvalds }
220*1da177e4SLinus Torvalds 
221*1da177e4SLinus Torvalds module_init(ibmasm_init);
222*1da177e4SLinus Torvalds module_exit(ibmasm_exit);
223*1da177e4SLinus Torvalds 
224*1da177e4SLinus Torvalds MODULE_AUTHOR(DRIVER_AUTHOR);
225*1da177e4SLinus Torvalds MODULE_DESCRIPTION(DRIVER_DESC);
226*1da177e4SLinus Torvalds MODULE_LICENSE("GPL");
227*1da177e4SLinus Torvalds MODULE_DEVICE_TABLE(pci, ibmasm_pci_table);
228*1da177e4SLinus Torvalds 
229