11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This file is subject to the terms and conditions of the GNU General Public 31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * License. See the file "COPYING" in the main directory of this archive 41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * for more details. 51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 6107d5a72f2c6a6819b66eebcb0281c7a67b6baaaBrent Casavant * Copyright (C) 2005-2006 Silicon Graphics, Inc. All Rights Reserved. 71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 922329b511a97557b293583194037d1f4c71e1504Brent Casavant/* This file contains the master driver module for use by SGI IOC4 subdrivers. 1022329b511a97557b293583194037d1f4c71e1504Brent Casavant * 1122329b511a97557b293583194037d1f4c71e1504Brent Casavant * It allocates any resources shared between multiple subdevices, and 1222329b511a97557b293583194037d1f4c71e1504Brent Casavant * provides accessor functions (where needed) and the like for those 1322329b511a97557b293583194037d1f4c71e1504Brent Casavant * resources. It also provides a mechanism for the subdevice modules 1422329b511a97557b293583194037d1f4c71e1504Brent Casavant * to support loading and unloading. 1522329b511a97557b293583194037d1f4c71e1504Brent Casavant * 1622329b511a97557b293583194037d1f4c71e1504Brent Casavant * Non-shared resources (e.g. external interrupt A_INT_OUT register page 1722329b511a97557b293583194037d1f4c71e1504Brent Casavant * alias, serial port and UART registers) are handled by the subdevice 1822329b511a97557b293583194037d1f4c71e1504Brent Casavant * modules themselves. 1922329b511a97557b293583194037d1f4c71e1504Brent Casavant * 2022329b511a97557b293583194037d1f4c71e1504Brent Casavant * This is all necessary because IOC4 is not implemented as a multi-function 2122329b511a97557b293583194037d1f4c71e1504Brent Casavant * PCI device, but an amalgamation of disparate registers for several 2222329b511a97557b293583194037d1f4c71e1504Brent Casavant * types of device (ATA, serial, external interrupts). The normal 2322329b511a97557b293583194037d1f4c71e1504Brent Casavant * resource management in the kernel doesn't have quite the right interfaces 2422329b511a97557b293583194037d1f4c71e1504Brent Casavant * to handle this situation (e.g. multiple modules can't claim the same 2522329b511a97557b293583194037d1f4c71e1504Brent Casavant * PCI ID), thus this IOC4 master module. 261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h> 291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h> 301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/pci.h> 3122329b511a97557b293583194037d1f4c71e1504Brent Casavant#include <linux/ioc4.h> 32107d5a72f2c6a6819b66eebcb0281c7a67b6baaaBrent Casavant#include <linux/ktime.h> 335a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h> 346e586f32931d6c98431d54cd0430d4366195b0baJes Sorensen#include <linux/mutex.h> 35107d5a72f2c6a6819b66eebcb0281c7a67b6baaaBrent Casavant#include <linux/time.h> 362099c99e3b24f86b131566aa9854249189ae9ea2Al Viro#include <asm/io.h> 37d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant 38d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant/*************** 39d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant * Definitions * 40d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant ***************/ 41d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant 42d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant/* Tweakable values */ 43d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant 44d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant/* PCI bus speed detection/calibration */ 45107d5a72f2c6a6819b66eebcb0281c7a67b6baaaBrent Casavant#define IOC4_CALIBRATE_COUNT 63 /* Calibration cycle period */ 46d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant#define IOC4_CALIBRATE_CYCLES 256 /* Average over this many cycles */ 47d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant#define IOC4_CALIBRATE_DISCARD 2 /* Discard first few cycles */ 48d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant#define IOC4_CALIBRATE_LOW_MHZ 25 /* Lower bound on bus speed sanity */ 49d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant#define IOC4_CALIBRATE_HIGH_MHZ 75 /* Upper bound on bus speed sanity */ 50d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant#define IOC4_CALIBRATE_DEFAULT_MHZ 66 /* Assumed if sanity check fails */ 511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5222329b511a97557b293583194037d1f4c71e1504Brent Casavant/************************ 5322329b511a97557b293583194037d1f4c71e1504Brent Casavant * Submodule management * 5422329b511a97557b293583194037d1f4c71e1504Brent Casavant ************************/ 551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 566e586f32931d6c98431d54cd0430d4366195b0baJes Sorensenstatic DEFINE_MUTEX(ioc4_mutex); 5722329b511a97557b293583194037d1f4c71e1504Brent Casavant 586e586f32931d6c98431d54cd0430d4366195b0baJes Sorensenstatic LIST_HEAD(ioc4_devices); 5922329b511a97557b293583194037d1f4c71e1504Brent Casavantstatic LIST_HEAD(ioc4_submodules); 6022329b511a97557b293583194037d1f4c71e1504Brent Casavant 6122329b511a97557b293583194037d1f4c71e1504Brent Casavant/* Register an IOC4 submodule */ 6222329b511a97557b293583194037d1f4c71e1504Brent Casavantint 6322329b511a97557b293583194037d1f4c71e1504Brent Casavantioc4_register_submodule(struct ioc4_submodule *is) 6422329b511a97557b293583194037d1f4c71e1504Brent Casavant{ 6522329b511a97557b293583194037d1f4c71e1504Brent Casavant struct ioc4_driver_data *idd; 6622329b511a97557b293583194037d1f4c71e1504Brent Casavant 676e586f32931d6c98431d54cd0430d4366195b0baJes Sorensen mutex_lock(&ioc4_mutex); 6822329b511a97557b293583194037d1f4c71e1504Brent Casavant list_add(&is->is_list, &ioc4_submodules); 6922329b511a97557b293583194037d1f4c71e1504Brent Casavant 7022329b511a97557b293583194037d1f4c71e1504Brent Casavant /* Initialize submodule for each IOC4 */ 7122329b511a97557b293583194037d1f4c71e1504Brent Casavant if (!is->is_probe) 726e586f32931d6c98431d54cd0430d4366195b0baJes Sorensen goto out; 7322329b511a97557b293583194037d1f4c71e1504Brent Casavant 7422329b511a97557b293583194037d1f4c71e1504Brent Casavant list_for_each_entry(idd, &ioc4_devices, idd_list) { 7522329b511a97557b293583194037d1f4c71e1504Brent Casavant if (is->is_probe(idd)) { 7622329b511a97557b293583194037d1f4c71e1504Brent Casavant printk(KERN_WARNING 7722329b511a97557b293583194037d1f4c71e1504Brent Casavant "%s: IOC4 submodule %s probe failed " 7822329b511a97557b293583194037d1f4c71e1504Brent Casavant "for pci_dev %s", 796e574195b75543bc6a6240306313988b1952470cHarvey Harrison __func__, module_name(is->is_owner), 8022329b511a97557b293583194037d1f4c71e1504Brent Casavant pci_name(idd->idd_pdev)); 8122329b511a97557b293583194037d1f4c71e1504Brent Casavant } 8222329b511a97557b293583194037d1f4c71e1504Brent Casavant } 836e586f32931d6c98431d54cd0430d4366195b0baJes Sorensen out: 846e586f32931d6c98431d54cd0430d4366195b0baJes Sorensen mutex_unlock(&ioc4_mutex); 8522329b511a97557b293583194037d1f4c71e1504Brent Casavant return 0; 8622329b511a97557b293583194037d1f4c71e1504Brent Casavant} 8722329b511a97557b293583194037d1f4c71e1504Brent Casavant 8822329b511a97557b293583194037d1f4c71e1504Brent Casavant/* Unregister an IOC4 submodule */ 8922329b511a97557b293583194037d1f4c71e1504Brent Casavantvoid 9022329b511a97557b293583194037d1f4c71e1504Brent Casavantioc4_unregister_submodule(struct ioc4_submodule *is) 911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 9222329b511a97557b293583194037d1f4c71e1504Brent Casavant struct ioc4_driver_data *idd; 9322329b511a97557b293583194037d1f4c71e1504Brent Casavant 946e586f32931d6c98431d54cd0430d4366195b0baJes Sorensen mutex_lock(&ioc4_mutex); 9522329b511a97557b293583194037d1f4c71e1504Brent Casavant list_del(&is->is_list); 9622329b511a97557b293583194037d1f4c71e1504Brent Casavant 9722329b511a97557b293583194037d1f4c71e1504Brent Casavant /* Remove submodule for each IOC4 */ 9822329b511a97557b293583194037d1f4c71e1504Brent Casavant if (!is->is_remove) 996e586f32931d6c98431d54cd0430d4366195b0baJes Sorensen goto out; 10022329b511a97557b293583194037d1f4c71e1504Brent Casavant 10122329b511a97557b293583194037d1f4c71e1504Brent Casavant list_for_each_entry(idd, &ioc4_devices, idd_list) { 10222329b511a97557b293583194037d1f4c71e1504Brent Casavant if (is->is_remove(idd)) { 10322329b511a97557b293583194037d1f4c71e1504Brent Casavant printk(KERN_WARNING 10422329b511a97557b293583194037d1f4c71e1504Brent Casavant "%s: IOC4 submodule %s remove failed " 10522329b511a97557b293583194037d1f4c71e1504Brent Casavant "for pci_dev %s.\n", 1066e574195b75543bc6a6240306313988b1952470cHarvey Harrison __func__, module_name(is->is_owner), 10722329b511a97557b293583194037d1f4c71e1504Brent Casavant pci_name(idd->idd_pdev)); 10822329b511a97557b293583194037d1f4c71e1504Brent Casavant } 10922329b511a97557b293583194037d1f4c71e1504Brent Casavant } 1106e586f32931d6c98431d54cd0430d4366195b0baJes Sorensen out: 1116e586f32931d6c98431d54cd0430d4366195b0baJes Sorensen mutex_unlock(&ioc4_mutex); 11222329b511a97557b293583194037d1f4c71e1504Brent Casavant} 11322329b511a97557b293583194037d1f4c71e1504Brent Casavant 11422329b511a97557b293583194037d1f4c71e1504Brent Casavant/********************* 11522329b511a97557b293583194037d1f4c71e1504Brent Casavant * Device management * 11622329b511a97557b293583194037d1f4c71e1504Brent Casavant *********************/ 11722329b511a97557b293583194037d1f4c71e1504Brent Casavant 118d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant#define IOC4_CALIBRATE_LOW_LIMIT \ 119d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant (1000*IOC4_EXTINT_COUNT_DIVISOR/IOC4_CALIBRATE_LOW_MHZ) 120d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant#define IOC4_CALIBRATE_HIGH_LIMIT \ 121d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant (1000*IOC4_EXTINT_COUNT_DIVISOR/IOC4_CALIBRATE_HIGH_MHZ) 122d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant#define IOC4_CALIBRATE_DEFAULT \ 123d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant (1000*IOC4_EXTINT_COUNT_DIVISOR/IOC4_CALIBRATE_DEFAULT_MHZ) 124d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant 125d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant#define IOC4_CALIBRATE_END \ 126d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant (IOC4_CALIBRATE_CYCLES + IOC4_CALIBRATE_DISCARD) 127d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant 128d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant#define IOC4_INT_OUT_MODE_TOGGLE 0x7 /* Toggle INT_OUT every COUNT+1 ticks */ 129d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant 130d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant/* Determines external interrupt output clock period of the PCI bus an 131d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant * IOC4 is attached to. This value can be used to determine the PCI 132d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant * bus speed. 133d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant * 134d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant * IOC4 has a design feature that various internal timers are derived from 135d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant * the PCI bus clock. This causes IOC4 device drivers to need to take the 136d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant * bus speed into account when setting various register values (e.g. INT_OUT 137d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant * register COUNT field, UART divisors, etc). Since this information is 138d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant * needed by several subdrivers, it is determined by the main IOC4 driver, 139d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant * even though the following code utilizes external interrupt registers 140d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant * to perform the speed calculation. 141d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant */ 14280c8ae289266529445fad030fabf5fcf01ccda0dBill Pembertonstatic void 143d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavantioc4_clock_calibrate(struct ioc4_driver_data *idd) 144d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant{ 145d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant union ioc4_int_out int_out; 146d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant union ioc4_gpcr gpcr; 147d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant unsigned int state, last_state = 1; 148107d5a72f2c6a6819b66eebcb0281c7a67b6baaaBrent Casavant uint64_t start, end, period; 149d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant unsigned int count = 0; 150d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant 151d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant /* Enable output */ 152d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant gpcr.raw = 0; 153d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant gpcr.fields.dir = IOC4_GPCR_DIR_0; 154d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant gpcr.fields.int_out_en = 1; 155d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant writel(gpcr.raw, &idd->idd_misc_regs->gpcr_s.raw); 156d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant 157d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant /* Reset to power-on state */ 158d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant writel(0, &idd->idd_misc_regs->int_out.raw); 159d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant mmiowb(); 160d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant 161d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant /* Set up square wave */ 162d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant int_out.raw = 0; 163d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant int_out.fields.count = IOC4_CALIBRATE_COUNT; 164d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant int_out.fields.mode = IOC4_INT_OUT_MODE_TOGGLE; 165d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant int_out.fields.diag = 0; 166d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant writel(int_out.raw, &idd->idd_misc_regs->int_out.raw); 167d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant mmiowb(); 168d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant 169d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant /* Check square wave period averaged over some number of cycles */ 170d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant do { 171d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant int_out.raw = readl(&idd->idd_misc_regs->int_out.raw); 172d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant state = int_out.fields.int_out; 173d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant if (!last_state && state) { 174d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant count++; 175d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant if (count == IOC4_CALIBRATE_END) { 1766d9b757c6ce850d0fb61d31f48e7bae56240643bThomas Gleixner end = ktime_get_ns(); 177d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant break; 178d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant } else if (count == IOC4_CALIBRATE_DISCARD) 1796d9b757c6ce850d0fb61d31f48e7bae56240643bThomas Gleixner start = ktime_get_ns(); 180d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant } 181d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant last_state = state; 182d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant } while (1); 183d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant 184d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant /* Calculation rearranged to preserve intermediate precision. 185d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant * Logically: 186107d5a72f2c6a6819b66eebcb0281c7a67b6baaaBrent Casavant * 1. "end - start" gives us the measurement period over all 187107d5a72f2c6a6819b66eebcb0281c7a67b6baaaBrent Casavant * the square wave cycles. 188107d5a72f2c6a6819b66eebcb0281c7a67b6baaaBrent Casavant * 2. Divide by number of square wave cycles to get the period 189107d5a72f2c6a6819b66eebcb0281c7a67b6baaaBrent Casavant * of a square wave cycle. 190d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant * 3. Divide by 2*(int_out.fields.count+1), which is the formula 191d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant * by which the IOC4 generates the square wave, to get the 192107d5a72f2c6a6819b66eebcb0281c7a67b6baaaBrent Casavant * period of an IOC4 INT_OUT count. 193d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant */ 194107d5a72f2c6a6819b66eebcb0281c7a67b6baaaBrent Casavant period = (end - start) / 195107d5a72f2c6a6819b66eebcb0281c7a67b6baaaBrent Casavant (IOC4_CALIBRATE_CYCLES * 2 * (IOC4_CALIBRATE_COUNT + 1)); 196d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant 197d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant /* Bounds check the result. */ 198d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant if (period > IOC4_CALIBRATE_LOW_LIMIT || 199d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant period < IOC4_CALIBRATE_HIGH_LIMIT) { 200f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant printk(KERN_INFO 201f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant "IOC4 %s: Clock calibration failed. Assuming" 202f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant "PCI clock is %d ns.\n", 203f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant pci_name(idd->idd_pdev), 204d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant IOC4_CALIBRATE_DEFAULT / IOC4_EXTINT_COUNT_DIVISOR); 205d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant period = IOC4_CALIBRATE_DEFAULT; 206d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant } else { 20759f148005cfd3d41537a4b872c266213d5fe4dc6Brent Casavant u64 ns = period; 20859f148005cfd3d41537a4b872c266213d5fe4dc6Brent Casavant 20959f148005cfd3d41537a4b872c266213d5fe4dc6Brent Casavant do_div(ns, IOC4_EXTINT_COUNT_DIVISOR); 210f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant printk(KERN_DEBUG 211760fe9ad1692361770bb56fa5c69cf6b3354858cRandy Dunlap "IOC4 %s: PCI clock is %llu ns.\n", 212760fe9ad1692361770bb56fa5c69cf6b3354858cRandy Dunlap pci_name(idd->idd_pdev), (unsigned long long)ns); 213d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant } 214d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant 215d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant /* Remember results. We store the extint clock period rather 216d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant * than the PCI clock period so that greater precision is 217d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant * retained. Divide by IOC4_EXTINT_COUNT_DIVISOR to get 218d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant * PCI clock period. 219d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant */ 220d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant idd->count_period = period; 221d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant} 222d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant 223f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant/* There are three variants of IOC4 cards: IO9, IO10, and PCI-RT. 224f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant * Each brings out different combinations of IOC4 signals, thus. 225f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant * the IOC4 subdrivers need to know to which we're attached. 226f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant * 227f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant * We look for the presence of a SCSI (IO9) or SATA (IO10) controller 228f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant * on the same PCI bus at slot number 3 to differentiate IO9 from IO10. 229f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant * If neither is present, it's a PCI-RT. 230f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant */ 23180c8ae289266529445fad030fabf5fcf01ccda0dBill Pembertonstatic unsigned int 232f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavantioc4_variant(struct ioc4_driver_data *idd) 233f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant{ 234f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant struct pci_dev *pdev = NULL; 235f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant int found = 0; 236f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant 237f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant /* IO9: Look for a QLogic ISP 12160 at the same bus and slot 3. */ 238f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant do { 239f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant pdev = pci_get_device(PCI_VENDOR_ID_QLOGIC, 240f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant PCI_DEVICE_ID_QLOGIC_ISP12160, pdev); 241f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant if (pdev && 242f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant idd->idd_pdev->bus->number == pdev->bus->number && 243f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant 3 == PCI_SLOT(pdev->devfn)) 244f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant found = 1; 245f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant } while (pdev && !found); 24690d8dabf74179e6615bd4688a118e12ec29ab7aaJulia Lawall if (NULL != pdev) { 24790d8dabf74179e6615bd4688a118e12ec29ab7aaJulia Lawall pci_dev_put(pdev); 248f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant return IOC4_VARIANT_IO9; 24990d8dabf74179e6615bd4688a118e12ec29ab7aaJulia Lawall } 250f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant 251f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant /* IO10: Look for a Vitesse VSC 7174 at the same bus and slot 3. */ 252f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant pdev = NULL; 253f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant do { 254f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant pdev = pci_get_device(PCI_VENDOR_ID_VITESSE, 255f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant PCI_DEVICE_ID_VITESSE_VSC7174, pdev); 256f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant if (pdev && 257f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant idd->idd_pdev->bus->number == pdev->bus->number && 258f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant 3 == PCI_SLOT(pdev->devfn)) 259f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant found = 1; 260f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant } while (pdev && !found); 26190d8dabf74179e6615bd4688a118e12ec29ab7aaJulia Lawall if (NULL != pdev) { 26290d8dabf74179e6615bd4688a118e12ec29ab7aaJulia Lawall pci_dev_put(pdev); 263f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant return IOC4_VARIANT_IO10; 26490d8dabf74179e6615bd4688a118e12ec29ab7aaJulia Lawall } 265f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant 266f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant /* PCI-RT: No SCSI/SATA controller will be present */ 267f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant return IOC4_VARIANT_PCI_RT; 268f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant} 269f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant 2701fc6e987d8f606371337211f52ff74c6753298a6Ralf Baechlestatic void 27108adefd4791772d8b3fe23cc9d2554123e21dfa3Brent Casavantioc4_load_modules(struct work_struct *work) 27208adefd4791772d8b3fe23cc9d2554123e21dfa3Brent Casavant{ 27308adefd4791772d8b3fe23cc9d2554123e21dfa3Brent Casavant request_module("sgiioc4"); 27408adefd4791772d8b3fe23cc9d2554123e21dfa3Brent Casavant} 27508adefd4791772d8b3fe23cc9d2554123e21dfa3Brent Casavant 276883624a08cb4144343e7362d9fff0e2c69613ebfTejun Heostatic DECLARE_WORK(ioc4_load_modules_work, ioc4_load_modules); 277883624a08cb4144343e7362d9fff0e2c69613ebfTejun Heo 27822329b511a97557b293583194037d1f4c71e1504Brent Casavant/* Adds a new instance of an IOC4 card */ 27980c8ae289266529445fad030fabf5fcf01ccda0dBill Pembertonstatic int 28022329b511a97557b293583194037d1f4c71e1504Brent Casavantioc4_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) 28122329b511a97557b293583194037d1f4c71e1504Brent Casavant{ 28222329b511a97557b293583194037d1f4c71e1504Brent Casavant struct ioc4_driver_data *idd; 28322329b511a97557b293583194037d1f4c71e1504Brent Casavant struct ioc4_submodule *is; 28422329b511a97557b293583194037d1f4c71e1504Brent Casavant uint32_t pcmd; 2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int ret; 2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 28722329b511a97557b293583194037d1f4c71e1504Brent Casavant /* Enable IOC4 and take ownership of it */ 2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if ((ret = pci_enable_device(pdev))) { 2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_WARNING 29022329b511a97557b293583194037d1f4c71e1504Brent Casavant "%s: Failed to enable IOC4 device for pci_dev %s.\n", 2916e574195b75543bc6a6240306313988b1952470cHarvey Harrison __func__, pci_name(pdev)); 29222329b511a97557b293583194037d1f4c71e1504Brent Casavant goto out; 2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds pci_set_master(pdev); 2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 29622329b511a97557b293583194037d1f4c71e1504Brent Casavant /* Set up per-IOC4 data */ 29722329b511a97557b293583194037d1f4c71e1504Brent Casavant idd = kmalloc(sizeof(struct ioc4_driver_data), GFP_KERNEL); 29822329b511a97557b293583194037d1f4c71e1504Brent Casavant if (!idd) { 29922329b511a97557b293583194037d1f4c71e1504Brent Casavant printk(KERN_WARNING 30022329b511a97557b293583194037d1f4c71e1504Brent Casavant "%s: Failed to allocate IOC4 data for pci_dev %s.\n", 3016e574195b75543bc6a6240306313988b1952470cHarvey Harrison __func__, pci_name(pdev)); 30222329b511a97557b293583194037d1f4c71e1504Brent Casavant ret = -ENODEV; 30322329b511a97557b293583194037d1f4c71e1504Brent Casavant goto out_idd; 30422329b511a97557b293583194037d1f4c71e1504Brent Casavant } 30522329b511a97557b293583194037d1f4c71e1504Brent Casavant idd->idd_pdev = pdev; 30622329b511a97557b293583194037d1f4c71e1504Brent Casavant idd->idd_pci_id = pci_id; 30722329b511a97557b293583194037d1f4c71e1504Brent Casavant 30822329b511a97557b293583194037d1f4c71e1504Brent Casavant /* Map IOC4 misc registers. These are shared between subdevices 30922329b511a97557b293583194037d1f4c71e1504Brent Casavant * so the main IOC4 module manages them. 31022329b511a97557b293583194037d1f4c71e1504Brent Casavant */ 31122329b511a97557b293583194037d1f4c71e1504Brent Casavant idd->idd_bar0 = pci_resource_start(idd->idd_pdev, 0); 31222329b511a97557b293583194037d1f4c71e1504Brent Casavant if (!idd->idd_bar0) { 31322329b511a97557b293583194037d1f4c71e1504Brent Casavant printk(KERN_WARNING 31422329b511a97557b293583194037d1f4c71e1504Brent Casavant "%s: Unable to find IOC4 misc resource " 31522329b511a97557b293583194037d1f4c71e1504Brent Casavant "for pci_dev %s.\n", 3166e574195b75543bc6a6240306313988b1952470cHarvey Harrison __func__, pci_name(idd->idd_pdev)); 31722329b511a97557b293583194037d1f4c71e1504Brent Casavant ret = -ENODEV; 31822329b511a97557b293583194037d1f4c71e1504Brent Casavant goto out_pci; 31922329b511a97557b293583194037d1f4c71e1504Brent Casavant } 32052c9ae0ac7576c94f6a2371b44039e7ba12a0439Brent Casavant if (!request_mem_region(idd->idd_bar0, sizeof(struct ioc4_misc_regs), 32122329b511a97557b293583194037d1f4c71e1504Brent Casavant "ioc4_misc")) { 32222329b511a97557b293583194037d1f4c71e1504Brent Casavant printk(KERN_WARNING 32322329b511a97557b293583194037d1f4c71e1504Brent Casavant "%s: Unable to request IOC4 misc region " 32422329b511a97557b293583194037d1f4c71e1504Brent Casavant "for pci_dev %s.\n", 3256e574195b75543bc6a6240306313988b1952470cHarvey Harrison __func__, pci_name(idd->idd_pdev)); 32622329b511a97557b293583194037d1f4c71e1504Brent Casavant ret = -ENODEV; 32722329b511a97557b293583194037d1f4c71e1504Brent Casavant goto out_pci; 32822329b511a97557b293583194037d1f4c71e1504Brent Casavant } 32922329b511a97557b293583194037d1f4c71e1504Brent Casavant idd->idd_misc_regs = ioremap(idd->idd_bar0, 33022329b511a97557b293583194037d1f4c71e1504Brent Casavant sizeof(struct ioc4_misc_regs)); 33122329b511a97557b293583194037d1f4c71e1504Brent Casavant if (!idd->idd_misc_regs) { 33222329b511a97557b293583194037d1f4c71e1504Brent Casavant printk(KERN_WARNING 33322329b511a97557b293583194037d1f4c71e1504Brent Casavant "%s: Unable to remap IOC4 misc region " 33422329b511a97557b293583194037d1f4c71e1504Brent Casavant "for pci_dev %s.\n", 3356e574195b75543bc6a6240306313988b1952470cHarvey Harrison __func__, pci_name(idd->idd_pdev)); 33622329b511a97557b293583194037d1f4c71e1504Brent Casavant ret = -ENODEV; 33722329b511a97557b293583194037d1f4c71e1504Brent Casavant goto out_misc_region; 33822329b511a97557b293583194037d1f4c71e1504Brent Casavant } 33922329b511a97557b293583194037d1f4c71e1504Brent Casavant 34022329b511a97557b293583194037d1f4c71e1504Brent Casavant /* Failsafe portion of per-IOC4 initialization */ 34122329b511a97557b293583194037d1f4c71e1504Brent Casavant 342f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant /* Detect card variant */ 343f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant idd->idd_variant = ioc4_variant(idd); 344f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant printk(KERN_INFO "IOC4 %s: %s card detected.\n", pci_name(pdev), 345f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant idd->idd_variant == IOC4_VARIANT_IO9 ? "IO9" : 346f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant idd->idd_variant == IOC4_VARIANT_PCI_RT ? "PCI-RT" : 347f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant idd->idd_variant == IOC4_VARIANT_IO10 ? "IO10" : "unknown"); 348f5befceb5cfecba49fdf61f8e0eb4d453200eac9Brent Casavant 34922329b511a97557b293583194037d1f4c71e1504Brent Casavant /* Initialize IOC4 */ 35022329b511a97557b293583194037d1f4c71e1504Brent Casavant pci_read_config_dword(idd->idd_pdev, PCI_COMMAND, &pcmd); 35122329b511a97557b293583194037d1f4c71e1504Brent Casavant pci_write_config_dword(idd->idd_pdev, PCI_COMMAND, 35222329b511a97557b293583194037d1f4c71e1504Brent Casavant pcmd | PCI_COMMAND_PARITY | PCI_COMMAND_SERR); 35322329b511a97557b293583194037d1f4c71e1504Brent Casavant 354d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant /* Determine PCI clock */ 355d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant ioc4_clock_calibrate(idd); 356d4c477ca5448f19afaaf6c0cfd655009ea9e614dBrent Casavant 35722329b511a97557b293583194037d1f4c71e1504Brent Casavant /* Disable/clear all interrupts. Need to do this here lest 35822329b511a97557b293583194037d1f4c71e1504Brent Casavant * one submodule request the shared IOC4 IRQ, but interrupt 35922329b511a97557b293583194037d1f4c71e1504Brent Casavant * is generated by a different subdevice. 36022329b511a97557b293583194037d1f4c71e1504Brent Casavant */ 36122329b511a97557b293583194037d1f4c71e1504Brent Casavant /* Disable */ 36222329b511a97557b293583194037d1f4c71e1504Brent Casavant writel(~0, &idd->idd_misc_regs->other_iec.raw); 36322329b511a97557b293583194037d1f4c71e1504Brent Casavant writel(~0, &idd->idd_misc_regs->sio_iec); 36422329b511a97557b293583194037d1f4c71e1504Brent Casavant /* Clear (i.e. acknowledge) */ 36522329b511a97557b293583194037d1f4c71e1504Brent Casavant writel(~0, &idd->idd_misc_regs->other_ir.raw); 36622329b511a97557b293583194037d1f4c71e1504Brent Casavant writel(~0, &idd->idd_misc_regs->sio_ir); 36722329b511a97557b293583194037d1f4c71e1504Brent Casavant 36822329b511a97557b293583194037d1f4c71e1504Brent Casavant /* Track PCI-device specific data */ 36922329b511a97557b293583194037d1f4c71e1504Brent Casavant idd->idd_serial_data = NULL; 37022329b511a97557b293583194037d1f4c71e1504Brent Casavant pci_set_drvdata(idd->idd_pdev, idd); 3716e586f32931d6c98431d54cd0430d4366195b0baJes Sorensen 3726e586f32931d6c98431d54cd0430d4366195b0baJes Sorensen mutex_lock(&ioc4_mutex); 3738683dc9990158c221e05959935e7dd50a956c574Brent Casavant list_add_tail(&idd->idd_list, &ioc4_devices); 37422329b511a97557b293583194037d1f4c71e1504Brent Casavant 37522329b511a97557b293583194037d1f4c71e1504Brent Casavant /* Add this IOC4 to all submodules */ 37622329b511a97557b293583194037d1f4c71e1504Brent Casavant list_for_each_entry(is, &ioc4_submodules, is_list) { 37722329b511a97557b293583194037d1f4c71e1504Brent Casavant if (is->is_probe && is->is_probe(idd)) { 37822329b511a97557b293583194037d1f4c71e1504Brent Casavant printk(KERN_WARNING 37922329b511a97557b293583194037d1f4c71e1504Brent Casavant "%s: IOC4 submodule 0x%s probe failed " 38022329b511a97557b293583194037d1f4c71e1504Brent Casavant "for pci_dev %s.\n", 3816e574195b75543bc6a6240306313988b1952470cHarvey Harrison __func__, module_name(is->is_owner), 38222329b511a97557b293583194037d1f4c71e1504Brent Casavant pci_name(idd->idd_pdev)); 38322329b511a97557b293583194037d1f4c71e1504Brent Casavant } 38422329b511a97557b293583194037d1f4c71e1504Brent Casavant } 3856e586f32931d6c98431d54cd0430d4366195b0baJes Sorensen mutex_unlock(&ioc4_mutex); 38622329b511a97557b293583194037d1f4c71e1504Brent Casavant 38708adefd4791772d8b3fe23cc9d2554123e21dfa3Brent Casavant /* Request sgiioc4 IDE driver on boards that bring that functionality 38808adefd4791772d8b3fe23cc9d2554123e21dfa3Brent Casavant * off of IOC4. The root filesystem may be hosted on a drive connected 38908adefd4791772d8b3fe23cc9d2554123e21dfa3Brent Casavant * to IOC4, so we need to make sure the sgiioc4 driver is loaded as it 39008adefd4791772d8b3fe23cc9d2554123e21dfa3Brent Casavant * won't be picked up by modprobes due to the ioc4 module owning the 39108adefd4791772d8b3fe23cc9d2554123e21dfa3Brent Casavant * PCI device. 39208adefd4791772d8b3fe23cc9d2554123e21dfa3Brent Casavant */ 39308adefd4791772d8b3fe23cc9d2554123e21dfa3Brent Casavant if (idd->idd_variant != IOC4_VARIANT_PCI_RT) { 394883624a08cb4144343e7362d9fff0e2c69613ebfTejun Heo /* Request the module from a work procedure as the modprobe 395883624a08cb4144343e7362d9fff0e2c69613ebfTejun Heo * goes out to a userland helper and that will hang if done 396883624a08cb4144343e7362d9fff0e2c69613ebfTejun Heo * directly from ioc4_probe(). 397883624a08cb4144343e7362d9fff0e2c69613ebfTejun Heo */ 398883624a08cb4144343e7362d9fff0e2c69613ebfTejun Heo printk(KERN_INFO "IOC4 loading sgiioc4 submodule\n"); 399883624a08cb4144343e7362d9fff0e2c69613ebfTejun Heo schedule_work(&ioc4_load_modules_work); 40008adefd4791772d8b3fe23cc9d2554123e21dfa3Brent Casavant } 40108adefd4791772d8b3fe23cc9d2554123e21dfa3Brent Casavant 40222329b511a97557b293583194037d1f4c71e1504Brent Casavant return 0; 40322329b511a97557b293583194037d1f4c71e1504Brent Casavant 40422329b511a97557b293583194037d1f4c71e1504Brent Casavantout_misc_region: 40552c9ae0ac7576c94f6a2371b44039e7ba12a0439Brent Casavant release_mem_region(idd->idd_bar0, sizeof(struct ioc4_misc_regs)); 40622329b511a97557b293583194037d1f4c71e1504Brent Casavantout_pci: 40722329b511a97557b293583194037d1f4c71e1504Brent Casavant kfree(idd); 40822329b511a97557b293583194037d1f4c71e1504Brent Casavantout_idd: 40922329b511a97557b293583194037d1f4c71e1504Brent Casavant pci_disable_device(pdev); 41022329b511a97557b293583194037d1f4c71e1504Brent Casavantout: 41122329b511a97557b293583194037d1f4c71e1504Brent Casavant return ret; 4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 41422329b511a97557b293583194037d1f4c71e1504Brent Casavant/* Removes a particular instance of an IOC4 card. */ 415486a5c28c2e7d6a80c393ac7d612b77d80447b84Bill Pembertonstatic void 41622329b511a97557b293583194037d1f4c71e1504Brent Casavantioc4_remove(struct pci_dev *pdev) 41722329b511a97557b293583194037d1f4c71e1504Brent Casavant{ 41822329b511a97557b293583194037d1f4c71e1504Brent Casavant struct ioc4_submodule *is; 41922329b511a97557b293583194037d1f4c71e1504Brent Casavant struct ioc4_driver_data *idd; 42022329b511a97557b293583194037d1f4c71e1504Brent Casavant 42122329b511a97557b293583194037d1f4c71e1504Brent Casavant idd = pci_get_drvdata(pdev); 42222329b511a97557b293583194037d1f4c71e1504Brent Casavant 42322329b511a97557b293583194037d1f4c71e1504Brent Casavant /* Remove this IOC4 from all submodules */ 4246e586f32931d6c98431d54cd0430d4366195b0baJes Sorensen mutex_lock(&ioc4_mutex); 42522329b511a97557b293583194037d1f4c71e1504Brent Casavant list_for_each_entry(is, &ioc4_submodules, is_list) { 42622329b511a97557b293583194037d1f4c71e1504Brent Casavant if (is->is_remove && is->is_remove(idd)) { 42722329b511a97557b293583194037d1f4c71e1504Brent Casavant printk(KERN_WARNING 42822329b511a97557b293583194037d1f4c71e1504Brent Casavant "%s: IOC4 submodule 0x%s remove failed " 42922329b511a97557b293583194037d1f4c71e1504Brent Casavant "for pci_dev %s.\n", 4306e574195b75543bc6a6240306313988b1952470cHarvey Harrison __func__, module_name(is->is_owner), 43122329b511a97557b293583194037d1f4c71e1504Brent Casavant pci_name(idd->idd_pdev)); 43222329b511a97557b293583194037d1f4c71e1504Brent Casavant } 43322329b511a97557b293583194037d1f4c71e1504Brent Casavant } 4346e586f32931d6c98431d54cd0430d4366195b0baJes Sorensen mutex_unlock(&ioc4_mutex); 43522329b511a97557b293583194037d1f4c71e1504Brent Casavant 43622329b511a97557b293583194037d1f4c71e1504Brent Casavant /* Release resources */ 43722329b511a97557b293583194037d1f4c71e1504Brent Casavant iounmap(idd->idd_misc_regs); 43822329b511a97557b293583194037d1f4c71e1504Brent Casavant if (!idd->idd_bar0) { 43922329b511a97557b293583194037d1f4c71e1504Brent Casavant printk(KERN_WARNING 44022329b511a97557b293583194037d1f4c71e1504Brent Casavant "%s: Unable to get IOC4 misc mapping for pci_dev %s. " 44122329b511a97557b293583194037d1f4c71e1504Brent Casavant "Device removal may be incomplete.\n", 4426e574195b75543bc6a6240306313988b1952470cHarvey Harrison __func__, pci_name(idd->idd_pdev)); 44322329b511a97557b293583194037d1f4c71e1504Brent Casavant } 44452c9ae0ac7576c94f6a2371b44039e7ba12a0439Brent Casavant release_mem_region(idd->idd_bar0, sizeof(struct ioc4_misc_regs)); 44522329b511a97557b293583194037d1f4c71e1504Brent Casavant 44622329b511a97557b293583194037d1f4c71e1504Brent Casavant /* Disable IOC4 and relinquish */ 44722329b511a97557b293583194037d1f4c71e1504Brent Casavant pci_disable_device(pdev); 44822329b511a97557b293583194037d1f4c71e1504Brent Casavant 44922329b511a97557b293583194037d1f4c71e1504Brent Casavant /* Remove and free driver data */ 4506e586f32931d6c98431d54cd0430d4366195b0baJes Sorensen mutex_lock(&ioc4_mutex); 45122329b511a97557b293583194037d1f4c71e1504Brent Casavant list_del(&idd->idd_list); 4526e586f32931d6c98431d54cd0430d4366195b0baJes Sorensen mutex_unlock(&ioc4_mutex); 45322329b511a97557b293583194037d1f4c71e1504Brent Casavant kfree(idd); 45422329b511a97557b293583194037d1f4c71e1504Brent Casavant} 45522329b511a97557b293583194037d1f4c71e1504Brent Casavant 45622329b511a97557b293583194037d1f4c71e1504Brent Casavantstatic struct pci_device_id ioc4_id_table[] = { 4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds {PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC4, PCI_ANY_ID, 4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds PCI_ANY_ID, 0x0b4000, 0xFFFFFF}, 4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds {0} 4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 46285bd84345f64cb953101ddd8ab1340dde351c579Dave Jonesstatic struct pci_driver ioc4_driver = { 46322329b511a97557b293583194037d1f4c71e1504Brent Casavant .name = "IOC4", 46422329b511a97557b293583194037d1f4c71e1504Brent Casavant .id_table = ioc4_id_table, 46522329b511a97557b293583194037d1f4c71e1504Brent Casavant .probe = ioc4_probe, 4662d6bed9ca93e98685bc5038d686984fd449cd978Bill Pemberton .remove = ioc4_remove, 4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 46922329b511a97557b293583194037d1f4c71e1504Brent CasavantMODULE_DEVICE_TABLE(pci, ioc4_id_table); 47022329b511a97557b293583194037d1f4c71e1504Brent Casavant 47122329b511a97557b293583194037d1f4c71e1504Brent Casavant/********************* 47222329b511a97557b293583194037d1f4c71e1504Brent Casavant * Module management * 47322329b511a97557b293583194037d1f4c71e1504Brent Casavant *********************/ 47422329b511a97557b293583194037d1f4c71e1504Brent Casavant 47522329b511a97557b293583194037d1f4c71e1504Brent Casavant/* Module load */ 4762ea5d35a49f5c89d1d2d677fe90c71ad5a6278b6Jean Delvarestatic int __init 47722329b511a97557b293583194037d1f4c71e1504Brent Casavantioc4_init(void) 4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 47922329b511a97557b293583194037d1f4c71e1504Brent Casavant return pci_register_driver(&ioc4_driver); 48022329b511a97557b293583194037d1f4c71e1504Brent Casavant} 4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 48222329b511a97557b293583194037d1f4c71e1504Brent Casavant/* Module unload */ 4832ea5d35a49f5c89d1d2d677fe90c71ad5a6278b6Jean Delvarestatic void __exit 48422329b511a97557b293583194037d1f4c71e1504Brent Casavantioc4_exit(void) 48522329b511a97557b293583194037d1f4c71e1504Brent Casavant{ 48608adefd4791772d8b3fe23cc9d2554123e21dfa3Brent Casavant /* Ensure ioc4_load_modules() has completed before exiting */ 48743829731dd372d04d6706c51052b9dabab9ca356Tejun Heo flush_work(&ioc4_load_modules_work); 48822329b511a97557b293583194037d1f4c71e1504Brent Casavant pci_unregister_driver(&ioc4_driver); 4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 49122329b511a97557b293583194037d1f4c71e1504Brent Casavantmodule_init(ioc4_init); 49222329b511a97557b293583194037d1f4c71e1504Brent Casavantmodule_exit(ioc4_exit); 49322329b511a97557b293583194037d1f4c71e1504Brent Casavant 49422329b511a97557b293583194037d1f4c71e1504Brent CasavantMODULE_AUTHOR("Brent Casavant - Silicon Graphics, Inc. <bcasavan@sgi.com>"); 49522329b511a97557b293583194037d1f4c71e1504Brent CasavantMODULE_DESCRIPTION("PCI driver master module for SGI IOC4 Base-IO Card"); 4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL"); 49722329b511a97557b293583194037d1f4c71e1504Brent Casavant 49822329b511a97557b293583194037d1f4c71e1504Brent CasavantEXPORT_SYMBOL(ioc4_register_submodule); 49922329b511a97557b293583194037d1f4c71e1504Brent CasavantEXPORT_SYMBOL(ioc4_unregister_submodule); 500