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