mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 12:28:41 +08:00
drivers: remove the SGI SN2 IOC4 base support
The IOC4 is a multi-function chip seen on SGI SN2 and some SGI MIPS systems. This removes the base driver, which while not having an SN2 Kconfig dependency was only for sub-drivers that had one. Signed-off-by: Christoph Hellwig <hch@lst.de> Link: https://lkml.kernel.org/r/20190813072514.23299-15-hch@lst.de Signed-off-by: Tony Luck <tony.luck@intel.com>
This commit is contained in:
parent
c9fa9c327b
commit
f7bc6e42bf
@ -1,49 +0,0 @@
|
||||
====================================
|
||||
SGI IOC4 PCI (multi function) device
|
||||
====================================
|
||||
|
||||
The SGI IOC4 PCI device is a bit of a strange beast, so some notes on
|
||||
it are in order.
|
||||
|
||||
First, even though the IOC4 performs multiple functions, such as an
|
||||
IDE controller, a serial controller, a PS/2 keyboard/mouse controller,
|
||||
and an external interrupt mechanism, it's not implemented as a
|
||||
multifunction device. The consequence of this from a software
|
||||
standpoint is that all these functions share a single IRQ, and
|
||||
they can't all register to own the same PCI device ID. To make
|
||||
matters a bit worse, some of the register blocks (and even registers
|
||||
themselves) present in IOC4 are mixed-purpose between these several
|
||||
functions, meaning that there's no clear "owning" device driver.
|
||||
|
||||
The solution is to organize the IOC4 driver into several independent
|
||||
drivers, "ioc4", "sgiioc4", and "ioc4_serial". Note that there is no
|
||||
PS/2 controller driver as this functionality has never been wired up
|
||||
on a shipping IO card.
|
||||
|
||||
ioc4
|
||||
====
|
||||
This is the core (or shim) driver for IOC4. It is responsible for
|
||||
initializing the basic functionality of the chip, and allocating
|
||||
the PCI resources that are shared between the IOC4 functions.
|
||||
|
||||
This driver also provides registration functions that the other
|
||||
IOC4 drivers can call to make their presence known. Each driver
|
||||
needs to provide a probe and remove function, which are invoked
|
||||
by the core driver at appropriate times. The interface of these
|
||||
IOC4 function probe and remove operations isn't precisely the same
|
||||
as PCI device probe and remove operations, but is logically the
|
||||
same operation.
|
||||
|
||||
sgiioc4
|
||||
=======
|
||||
This is the IDE driver for IOC4. Its name isn't very descriptive
|
||||
simply for historical reasons (it used to be the only IOC4 driver
|
||||
component). There's not much to say about it other than it hooks
|
||||
up to the ioc4 driver via the appropriate registration, probe, and
|
||||
remove functions.
|
||||
|
||||
ioc4_serial
|
||||
===========
|
||||
This is the serial driver for IOC4. There's not much to say about it
|
||||
other than it hooks up to the ioc4 driver via the appropriate registration,
|
||||
probe, and remove functions.
|
@ -44,7 +44,6 @@ CONFIG_BLK_DEV_LOOP=m
|
||||
CONFIG_BLK_DEV_CRYPTOLOOP=m
|
||||
CONFIG_BLK_DEV_NBD=m
|
||||
CONFIG_BLK_DEV_RAM=y
|
||||
CONFIG_SGI_IOC4=y
|
||||
CONFIG_SGI_XP=m
|
||||
CONFIG_IDE=y
|
||||
CONFIG_BLK_DEV_IDECD=y
|
||||
|
@ -36,7 +36,6 @@ CONFIG_BLK_DEV_LOOP=m
|
||||
CONFIG_BLK_DEV_CRYPTOLOOP=m
|
||||
CONFIG_BLK_DEV_NBD=m
|
||||
CONFIG_BLK_DEV_RAM=y
|
||||
CONFIG_SGI_IOC4=y
|
||||
CONFIG_IDE=y
|
||||
CONFIG_BLK_DEV_IDECD=y
|
||||
CONFIG_IDE_GENERIC=y
|
||||
|
@ -103,7 +103,6 @@ CONFIG_FW_LOADER=m
|
||||
CONFIG_BLK_DEV_LOOP=m
|
||||
CONFIG_BLK_DEV_CRYPTOLOOP=m
|
||||
CONFIG_BLK_DEV_NBD=m
|
||||
CONFIG_SGI_IOC4=m
|
||||
CONFIG_EEPROM_LEGACY=y
|
||||
CONFIG_EEPROM_MAX6875=y
|
||||
CONFIG_IDE=y
|
||||
|
@ -46,7 +46,6 @@ CONFIG_CONNECTOR=y
|
||||
CONFIG_BLK_DEV_LOOP=m
|
||||
CONFIG_BLK_DEV_CRYPTOLOOP=m
|
||||
CONFIG_BLK_DEV_NBD=m
|
||||
CONFIG_SGI_IOC4=y
|
||||
CONFIG_RAID_ATTRS=y
|
||||
CONFIG_SCSI=y
|
||||
CONFIG_BLK_DEV_SD=y
|
||||
|
@ -117,7 +117,6 @@ CONFIG_MTD_CFI_AMDSTD=y
|
||||
CONFIG_MTD_PHYSMAP=y
|
||||
CONFIG_BLK_DEV_LOOP=m
|
||||
CONFIG_BLK_DEV_CRYPTOLOOP=m
|
||||
CONFIG_SGI_IOC4=m
|
||||
CONFIG_SCSI=m
|
||||
# CONFIG_SCSI_PROC_FS is not set
|
||||
CONFIG_BLK_DEV_SD=m
|
||||
|
@ -198,7 +198,6 @@ CONFIG_BLK_DEV_SX8=m
|
||||
CONFIG_BLK_DEV_RAM=m
|
||||
CONFIG_CDROM_PKTCDVD=m
|
||||
CONFIG_ATA_OVER_ETH=m
|
||||
CONFIG_SGI_IOC4=m
|
||||
CONFIG_RAID_ATTRS=m
|
||||
CONFIG_SCSI=y
|
||||
CONFIG_BLK_DEV_SD=y
|
||||
|
@ -49,7 +49,6 @@ CONFIG_BLK_DEV_RAM=y
|
||||
CONFIG_BLK_DEV_RAM_SIZE=9220
|
||||
CONFIG_CDROM_PKTCDVD=m
|
||||
CONFIG_ATA_OVER_ETH=m
|
||||
CONFIG_SGI_IOC4=m
|
||||
CONFIG_IDE=y
|
||||
CONFIG_BLK_DEV_IDECD=y
|
||||
CONFIG_BLK_DEV_IDETAPE=y
|
||||
|
@ -126,18 +126,6 @@ config INTEL_MID_PTI
|
||||
an Intel Atom (non-netbook) mobile device containing a MIPI
|
||||
P1149.7 standard implementation.
|
||||
|
||||
config SGI_IOC4
|
||||
tristate "SGI IOC4 Base IO support"
|
||||
depends on PCI
|
||||
---help---
|
||||
This option enables basic support for the IOC4 chip on certain
|
||||
SGI IO controller cards (IO9, IO10, and PCI-RT). This option
|
||||
does not enable any specific functions on such a card, but provides
|
||||
necessary infrastructure for other drivers to utilize.
|
||||
|
||||
If you have an SGI Altix with an IOC4-based card say Y.
|
||||
Otherwise say N.
|
||||
|
||||
config TIFM_CORE
|
||||
tristate "TI Flash Media interface support"
|
||||
depends on PCI
|
||||
|
@ -21,7 +21,6 @@ obj-$(CONFIG_QCOM_COINCELL) += qcom-coincell.o
|
||||
obj-$(CONFIG_QCOM_FASTRPC) += fastrpc.o
|
||||
obj-$(CONFIG_SENSORS_BH1770) += bh1770glc.o
|
||||
obj-$(CONFIG_SENSORS_APDS990X) += apds990x.o
|
||||
obj-$(CONFIG_SGI_IOC4) += ioc4.o
|
||||
obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o
|
||||
obj-$(CONFIG_KGDB_TESTS) += kgdbts.o
|
||||
obj-$(CONFIG_SGI_XP) += sgi-xp/
|
||||
|
@ -1,498 +0,0 @@
|
||||
/*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file "COPYING" in the main directory of this archive
|
||||
* for more details.
|
||||
*
|
||||
* Copyright (C) 2005-2006 Silicon Graphics, Inc. All Rights Reserved.
|
||||
*/
|
||||
|
||||
/* This file contains the master driver module for use by SGI IOC4 subdrivers.
|
||||
*
|
||||
* It allocates any resources shared between multiple subdevices, and
|
||||
* provides accessor functions (where needed) and the like for those
|
||||
* resources. It also provides a mechanism for the subdevice modules
|
||||
* to support loading and unloading.
|
||||
*
|
||||
* Non-shared resources (e.g. external interrupt A_INT_OUT register page
|
||||
* alias, serial port and UART registers) are handled by the subdevice
|
||||
* modules themselves.
|
||||
*
|
||||
* This is all necessary because IOC4 is not implemented as a multi-function
|
||||
* PCI device, but an amalgamation of disparate registers for several
|
||||
* types of device (ATA, serial, external interrupts). The normal
|
||||
* resource management in the kernel doesn't have quite the right interfaces
|
||||
* to handle this situation (e.g. multiple modules can't claim the same
|
||||
* PCI ID), thus this IOC4 master module.
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/ioc4.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/time.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
/***************
|
||||
* Definitions *
|
||||
***************/
|
||||
|
||||
/* Tweakable values */
|
||||
|
||||
/* PCI bus speed detection/calibration */
|
||||
#define IOC4_CALIBRATE_COUNT 63 /* Calibration cycle period */
|
||||
#define IOC4_CALIBRATE_CYCLES 256 /* Average over this many cycles */
|
||||
#define IOC4_CALIBRATE_DISCARD 2 /* Discard first few cycles */
|
||||
#define IOC4_CALIBRATE_LOW_MHZ 25 /* Lower bound on bus speed sanity */
|
||||
#define IOC4_CALIBRATE_HIGH_MHZ 75 /* Upper bound on bus speed sanity */
|
||||
#define IOC4_CALIBRATE_DEFAULT_MHZ 66 /* Assumed if sanity check fails */
|
||||
|
||||
/************************
|
||||
* Submodule management *
|
||||
************************/
|
||||
|
||||
static DEFINE_MUTEX(ioc4_mutex);
|
||||
|
||||
static LIST_HEAD(ioc4_devices);
|
||||
static LIST_HEAD(ioc4_submodules);
|
||||
|
||||
/* Register an IOC4 submodule */
|
||||
int
|
||||
ioc4_register_submodule(struct ioc4_submodule *is)
|
||||
{
|
||||
struct ioc4_driver_data *idd;
|
||||
|
||||
mutex_lock(&ioc4_mutex);
|
||||
list_add(&is->is_list, &ioc4_submodules);
|
||||
|
||||
/* Initialize submodule for each IOC4 */
|
||||
if (!is->is_probe)
|
||||
goto out;
|
||||
|
||||
list_for_each_entry(idd, &ioc4_devices, idd_list) {
|
||||
if (is->is_probe(idd)) {
|
||||
printk(KERN_WARNING
|
||||
"%s: IOC4 submodule %s probe failed "
|
||||
"for pci_dev %s",
|
||||
__func__, module_name(is->is_owner),
|
||||
pci_name(idd->idd_pdev));
|
||||
}
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&ioc4_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Unregister an IOC4 submodule */
|
||||
void
|
||||
ioc4_unregister_submodule(struct ioc4_submodule *is)
|
||||
{
|
||||
struct ioc4_driver_data *idd;
|
||||
|
||||
mutex_lock(&ioc4_mutex);
|
||||
list_del(&is->is_list);
|
||||
|
||||
/* Remove submodule for each IOC4 */
|
||||
if (!is->is_remove)
|
||||
goto out;
|
||||
|
||||
list_for_each_entry(idd, &ioc4_devices, idd_list) {
|
||||
if (is->is_remove(idd)) {
|
||||
printk(KERN_WARNING
|
||||
"%s: IOC4 submodule %s remove failed "
|
||||
"for pci_dev %s.\n",
|
||||
__func__, module_name(is->is_owner),
|
||||
pci_name(idd->idd_pdev));
|
||||
}
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&ioc4_mutex);
|
||||
}
|
||||
|
||||
/*********************
|
||||
* Device management *
|
||||
*********************/
|
||||
|
||||
#define IOC4_CALIBRATE_LOW_LIMIT \
|
||||
(1000*IOC4_EXTINT_COUNT_DIVISOR/IOC4_CALIBRATE_LOW_MHZ)
|
||||
#define IOC4_CALIBRATE_HIGH_LIMIT \
|
||||
(1000*IOC4_EXTINT_COUNT_DIVISOR/IOC4_CALIBRATE_HIGH_MHZ)
|
||||
#define IOC4_CALIBRATE_DEFAULT \
|
||||
(1000*IOC4_EXTINT_COUNT_DIVISOR/IOC4_CALIBRATE_DEFAULT_MHZ)
|
||||
|
||||
#define IOC4_CALIBRATE_END \
|
||||
(IOC4_CALIBRATE_CYCLES + IOC4_CALIBRATE_DISCARD)
|
||||
|
||||
#define IOC4_INT_OUT_MODE_TOGGLE 0x7 /* Toggle INT_OUT every COUNT+1 ticks */
|
||||
|
||||
/* Determines external interrupt output clock period of the PCI bus an
|
||||
* IOC4 is attached to. This value can be used to determine the PCI
|
||||
* bus speed.
|
||||
*
|
||||
* IOC4 has a design feature that various internal timers are derived from
|
||||
* the PCI bus clock. This causes IOC4 device drivers to need to take the
|
||||
* bus speed into account when setting various register values (e.g. INT_OUT
|
||||
* register COUNT field, UART divisors, etc). Since this information is
|
||||
* needed by several subdrivers, it is determined by the main IOC4 driver,
|
||||
* even though the following code utilizes external interrupt registers
|
||||
* to perform the speed calculation.
|
||||
*/
|
||||
static void
|
||||
ioc4_clock_calibrate(struct ioc4_driver_data *idd)
|
||||
{
|
||||
union ioc4_int_out int_out;
|
||||
union ioc4_gpcr gpcr;
|
||||
unsigned int state, last_state;
|
||||
uint64_t start, end, period;
|
||||
unsigned int count;
|
||||
|
||||
/* Enable output */
|
||||
gpcr.raw = 0;
|
||||
gpcr.fields.dir = IOC4_GPCR_DIR_0;
|
||||
gpcr.fields.int_out_en = 1;
|
||||
writel(gpcr.raw, &idd->idd_misc_regs->gpcr_s.raw);
|
||||
|
||||
/* Reset to power-on state */
|
||||
writel(0, &idd->idd_misc_regs->int_out.raw);
|
||||
|
||||
/* Set up square wave */
|
||||
int_out.raw = 0;
|
||||
int_out.fields.count = IOC4_CALIBRATE_COUNT;
|
||||
int_out.fields.mode = IOC4_INT_OUT_MODE_TOGGLE;
|
||||
int_out.fields.diag = 0;
|
||||
writel(int_out.raw, &idd->idd_misc_regs->int_out.raw);
|
||||
|
||||
/* Check square wave period averaged over some number of cycles */
|
||||
start = ktime_get_ns();
|
||||
state = 1; /* make sure the first read isn't a rising edge */
|
||||
for (count = 0; count <= IOC4_CALIBRATE_END; count++) {
|
||||
do { /* wait for a rising edge */
|
||||
last_state = state;
|
||||
int_out.raw = readl(&idd->idd_misc_regs->int_out.raw);
|
||||
state = int_out.fields.int_out;
|
||||
} while (last_state || !state);
|
||||
|
||||
/* discard the first few cycles */
|
||||
if (count == IOC4_CALIBRATE_DISCARD)
|
||||
start = ktime_get_ns();
|
||||
}
|
||||
end = ktime_get_ns();
|
||||
|
||||
/* Calculation rearranged to preserve intermediate precision.
|
||||
* Logically:
|
||||
* 1. "end - start" gives us the measurement period over all
|
||||
* the square wave cycles.
|
||||
* 2. Divide by number of square wave cycles to get the period
|
||||
* of a square wave cycle.
|
||||
* 3. Divide by 2*(int_out.fields.count+1), which is the formula
|
||||
* by which the IOC4 generates the square wave, to get the
|
||||
* period of an IOC4 INT_OUT count.
|
||||
*/
|
||||
period = (end - start) /
|
||||
(IOC4_CALIBRATE_CYCLES * 2 * (IOC4_CALIBRATE_COUNT + 1));
|
||||
|
||||
/* Bounds check the result. */
|
||||
if (period > IOC4_CALIBRATE_LOW_LIMIT ||
|
||||
period < IOC4_CALIBRATE_HIGH_LIMIT) {
|
||||
printk(KERN_INFO
|
||||
"IOC4 %s: Clock calibration failed. Assuming"
|
||||
"PCI clock is %d ns.\n",
|
||||
pci_name(idd->idd_pdev),
|
||||
IOC4_CALIBRATE_DEFAULT / IOC4_EXTINT_COUNT_DIVISOR);
|
||||
period = IOC4_CALIBRATE_DEFAULT;
|
||||
} else {
|
||||
u64 ns = period;
|
||||
|
||||
do_div(ns, IOC4_EXTINT_COUNT_DIVISOR);
|
||||
printk(KERN_DEBUG
|
||||
"IOC4 %s: PCI clock is %llu ns.\n",
|
||||
pci_name(idd->idd_pdev), (unsigned long long)ns);
|
||||
}
|
||||
|
||||
/* Remember results. We store the extint clock period rather
|
||||
* than the PCI clock period so that greater precision is
|
||||
* retained. Divide by IOC4_EXTINT_COUNT_DIVISOR to get
|
||||
* PCI clock period.
|
||||
*/
|
||||
idd->count_period = period;
|
||||
}
|
||||
|
||||
/* There are three variants of IOC4 cards: IO9, IO10, and PCI-RT.
|
||||
* Each brings out different combinations of IOC4 signals, thus.
|
||||
* the IOC4 subdrivers need to know to which we're attached.
|
||||
*
|
||||
* We look for the presence of a SCSI (IO9) or SATA (IO10) controller
|
||||
* on the same PCI bus at slot number 3 to differentiate IO9 from IO10.
|
||||
* If neither is present, it's a PCI-RT.
|
||||
*/
|
||||
static unsigned int
|
||||
ioc4_variant(struct ioc4_driver_data *idd)
|
||||
{
|
||||
struct pci_dev *pdev = NULL;
|
||||
int found = 0;
|
||||
|
||||
/* IO9: Look for a QLogic ISP 12160 at the same bus and slot 3. */
|
||||
do {
|
||||
pdev = pci_get_device(PCI_VENDOR_ID_QLOGIC,
|
||||
PCI_DEVICE_ID_QLOGIC_ISP12160, pdev);
|
||||
if (pdev &&
|
||||
idd->idd_pdev->bus->number == pdev->bus->number &&
|
||||
3 == PCI_SLOT(pdev->devfn))
|
||||
found = 1;
|
||||
} while (pdev && !found);
|
||||
if (NULL != pdev) {
|
||||
pci_dev_put(pdev);
|
||||
return IOC4_VARIANT_IO9;
|
||||
}
|
||||
|
||||
/* IO10: Look for a Vitesse VSC 7174 at the same bus and slot 3. */
|
||||
pdev = NULL;
|
||||
do {
|
||||
pdev = pci_get_device(PCI_VENDOR_ID_VITESSE,
|
||||
PCI_DEVICE_ID_VITESSE_VSC7174, pdev);
|
||||
if (pdev &&
|
||||
idd->idd_pdev->bus->number == pdev->bus->number &&
|
||||
3 == PCI_SLOT(pdev->devfn))
|
||||
found = 1;
|
||||
} while (pdev && !found);
|
||||
if (NULL != pdev) {
|
||||
pci_dev_put(pdev);
|
||||
return IOC4_VARIANT_IO10;
|
||||
}
|
||||
|
||||
/* PCI-RT: No SCSI/SATA controller will be present */
|
||||
return IOC4_VARIANT_PCI_RT;
|
||||
}
|
||||
|
||||
static void
|
||||
ioc4_load_modules(struct work_struct *work)
|
||||
{
|
||||
request_module("sgiioc4");
|
||||
}
|
||||
|
||||
static DECLARE_WORK(ioc4_load_modules_work, ioc4_load_modules);
|
||||
|
||||
/* Adds a new instance of an IOC4 card */
|
||||
static int
|
||||
ioc4_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id)
|
||||
{
|
||||
struct ioc4_driver_data *idd;
|
||||
struct ioc4_submodule *is;
|
||||
uint32_t pcmd;
|
||||
int ret;
|
||||
|
||||
/* Enable IOC4 and take ownership of it */
|
||||
if ((ret = pci_enable_device(pdev))) {
|
||||
printk(KERN_WARNING
|
||||
"%s: Failed to enable IOC4 device for pci_dev %s.\n",
|
||||
__func__, pci_name(pdev));
|
||||
goto out;
|
||||
}
|
||||
pci_set_master(pdev);
|
||||
|
||||
/* Set up per-IOC4 data */
|
||||
idd = kmalloc(sizeof(struct ioc4_driver_data), GFP_KERNEL);
|
||||
if (!idd) {
|
||||
printk(KERN_WARNING
|
||||
"%s: Failed to allocate IOC4 data for pci_dev %s.\n",
|
||||
__func__, pci_name(pdev));
|
||||
ret = -ENODEV;
|
||||
goto out_idd;
|
||||
}
|
||||
idd->idd_pdev = pdev;
|
||||
idd->idd_pci_id = pci_id;
|
||||
|
||||
/* Map IOC4 misc registers. These are shared between subdevices
|
||||
* so the main IOC4 module manages them.
|
||||
*/
|
||||
idd->idd_bar0 = pci_resource_start(idd->idd_pdev, 0);
|
||||
if (!idd->idd_bar0) {
|
||||
printk(KERN_WARNING
|
||||
"%s: Unable to find IOC4 misc resource "
|
||||
"for pci_dev %s.\n",
|
||||
__func__, pci_name(idd->idd_pdev));
|
||||
ret = -ENODEV;
|
||||
goto out_pci;
|
||||
}
|
||||
if (!request_mem_region(idd->idd_bar0, sizeof(struct ioc4_misc_regs),
|
||||
"ioc4_misc")) {
|
||||
printk(KERN_WARNING
|
||||
"%s: Unable to request IOC4 misc region "
|
||||
"for pci_dev %s.\n",
|
||||
__func__, pci_name(idd->idd_pdev));
|
||||
ret = -ENODEV;
|
||||
goto out_pci;
|
||||
}
|
||||
idd->idd_misc_regs = ioremap(idd->idd_bar0,
|
||||
sizeof(struct ioc4_misc_regs));
|
||||
if (!idd->idd_misc_regs) {
|
||||
printk(KERN_WARNING
|
||||
"%s: Unable to remap IOC4 misc region "
|
||||
"for pci_dev %s.\n",
|
||||
__func__, pci_name(idd->idd_pdev));
|
||||
ret = -ENODEV;
|
||||
goto out_misc_region;
|
||||
}
|
||||
|
||||
/* Failsafe portion of per-IOC4 initialization */
|
||||
|
||||
/* Detect card variant */
|
||||
idd->idd_variant = ioc4_variant(idd);
|
||||
printk(KERN_INFO "IOC4 %s: %s card detected.\n", pci_name(pdev),
|
||||
idd->idd_variant == IOC4_VARIANT_IO9 ? "IO9" :
|
||||
idd->idd_variant == IOC4_VARIANT_PCI_RT ? "PCI-RT" :
|
||||
idd->idd_variant == IOC4_VARIANT_IO10 ? "IO10" : "unknown");
|
||||
|
||||
/* Initialize IOC4 */
|
||||
pci_read_config_dword(idd->idd_pdev, PCI_COMMAND, &pcmd);
|
||||
pci_write_config_dword(idd->idd_pdev, PCI_COMMAND,
|
||||
pcmd | PCI_COMMAND_PARITY | PCI_COMMAND_SERR);
|
||||
|
||||
/* Determine PCI clock */
|
||||
ioc4_clock_calibrate(idd);
|
||||
|
||||
/* Disable/clear all interrupts. Need to do this here lest
|
||||
* one submodule request the shared IOC4 IRQ, but interrupt
|
||||
* is generated by a different subdevice.
|
||||
*/
|
||||
/* Disable */
|
||||
writel(~0, &idd->idd_misc_regs->other_iec.raw);
|
||||
writel(~0, &idd->idd_misc_regs->sio_iec);
|
||||
/* Clear (i.e. acknowledge) */
|
||||
writel(~0, &idd->idd_misc_regs->other_ir.raw);
|
||||
writel(~0, &idd->idd_misc_regs->sio_ir);
|
||||
|
||||
/* Track PCI-device specific data */
|
||||
idd->idd_serial_data = NULL;
|
||||
pci_set_drvdata(idd->idd_pdev, idd);
|
||||
|
||||
mutex_lock(&ioc4_mutex);
|
||||
list_add_tail(&idd->idd_list, &ioc4_devices);
|
||||
|
||||
/* Add this IOC4 to all submodules */
|
||||
list_for_each_entry(is, &ioc4_submodules, is_list) {
|
||||
if (is->is_probe && is->is_probe(idd)) {
|
||||
printk(KERN_WARNING
|
||||
"%s: IOC4 submodule 0x%s probe failed "
|
||||
"for pci_dev %s.\n",
|
||||
__func__, module_name(is->is_owner),
|
||||
pci_name(idd->idd_pdev));
|
||||
}
|
||||
}
|
||||
mutex_unlock(&ioc4_mutex);
|
||||
|
||||
/* Request sgiioc4 IDE driver on boards that bring that functionality
|
||||
* off of IOC4. The root filesystem may be hosted on a drive connected
|
||||
* to IOC4, so we need to make sure the sgiioc4 driver is loaded as it
|
||||
* won't be picked up by modprobes due to the ioc4 module owning the
|
||||
* PCI device.
|
||||
*/
|
||||
if (idd->idd_variant != IOC4_VARIANT_PCI_RT) {
|
||||
/* Request the module from a work procedure as the modprobe
|
||||
* goes out to a userland helper and that will hang if done
|
||||
* directly from ioc4_probe().
|
||||
*/
|
||||
printk(KERN_INFO "IOC4 loading sgiioc4 submodule\n");
|
||||
schedule_work(&ioc4_load_modules_work);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_misc_region:
|
||||
release_mem_region(idd->idd_bar0, sizeof(struct ioc4_misc_regs));
|
||||
out_pci:
|
||||
kfree(idd);
|
||||
out_idd:
|
||||
pci_disable_device(pdev);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Removes a particular instance of an IOC4 card. */
|
||||
static void
|
||||
ioc4_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct ioc4_submodule *is;
|
||||
struct ioc4_driver_data *idd;
|
||||
|
||||
idd = pci_get_drvdata(pdev);
|
||||
|
||||
/* Remove this IOC4 from all submodules */
|
||||
mutex_lock(&ioc4_mutex);
|
||||
list_for_each_entry(is, &ioc4_submodules, is_list) {
|
||||
if (is->is_remove && is->is_remove(idd)) {
|
||||
printk(KERN_WARNING
|
||||
"%s: IOC4 submodule 0x%s remove failed "
|
||||
"for pci_dev %s.\n",
|
||||
__func__, module_name(is->is_owner),
|
||||
pci_name(idd->idd_pdev));
|
||||
}
|
||||
}
|
||||
mutex_unlock(&ioc4_mutex);
|
||||
|
||||
/* Release resources */
|
||||
iounmap(idd->idd_misc_regs);
|
||||
if (!idd->idd_bar0) {
|
||||
printk(KERN_WARNING
|
||||
"%s: Unable to get IOC4 misc mapping for pci_dev %s. "
|
||||
"Device removal may be incomplete.\n",
|
||||
__func__, pci_name(idd->idd_pdev));
|
||||
}
|
||||
release_mem_region(idd->idd_bar0, sizeof(struct ioc4_misc_regs));
|
||||
|
||||
/* Disable IOC4 and relinquish */
|
||||
pci_disable_device(pdev);
|
||||
|
||||
/* Remove and free driver data */
|
||||
mutex_lock(&ioc4_mutex);
|
||||
list_del(&idd->idd_list);
|
||||
mutex_unlock(&ioc4_mutex);
|
||||
kfree(idd);
|
||||
}
|
||||
|
||||
static const struct pci_device_id ioc4_id_table[] = {
|
||||
{PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC4, PCI_ANY_ID,
|
||||
PCI_ANY_ID, 0x0b4000, 0xFFFFFF},
|
||||
{0}
|
||||
};
|
||||
|
||||
static struct pci_driver ioc4_driver = {
|
||||
.name = "IOC4",
|
||||
.id_table = ioc4_id_table,
|
||||
.probe = ioc4_probe,
|
||||
.remove = ioc4_remove,
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, ioc4_id_table);
|
||||
|
||||
/*********************
|
||||
* Module management *
|
||||
*********************/
|
||||
|
||||
/* Module load */
|
||||
static int __init
|
||||
ioc4_init(void)
|
||||
{
|
||||
return pci_register_driver(&ioc4_driver);
|
||||
}
|
||||
|
||||
/* Module unload */
|
||||
static void __exit
|
||||
ioc4_exit(void)
|
||||
{
|
||||
/* Ensure ioc4_load_modules() has completed before exiting */
|
||||
flush_work(&ioc4_load_modules_work);
|
||||
pci_unregister_driver(&ioc4_driver);
|
||||
}
|
||||
|
||||
module_init(ioc4_init);
|
||||
module_exit(ioc4_exit);
|
||||
|
||||
MODULE_AUTHOR("Brent Casavant - Silicon Graphics, Inc. <bcasavan@sgi.com>");
|
||||
MODULE_DESCRIPTION("PCI driver master module for SGI IOC4 Base-IO Card");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
EXPORT_SYMBOL(ioc4_register_submodule);
|
||||
EXPORT_SYMBOL(ioc4_unregister_submodule);
|
@ -1,184 +0,0 @@
|
||||
/*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file "COPYING" in the main directory of this archive
|
||||
* for more details.
|
||||
*
|
||||
* Copyright (c) 2005 Silicon Graphics, Inc. All Rights Reserved.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_IOC4_H
|
||||
#define _LINUX_IOC4_H
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
/***************
|
||||
* Definitions *
|
||||
***************/
|
||||
|
||||
/* Miscellaneous values inherent to hardware */
|
||||
|
||||
#define IOC4_EXTINT_COUNT_DIVISOR 520 /* PCI clocks per COUNT tick */
|
||||
|
||||
/***********************************
|
||||
* Structures needed by subdrivers *
|
||||
***********************************/
|
||||
|
||||
/* This structure fully describes the IOC4 miscellaneous registers which
|
||||
* appear at bar[0]+0x00000 through bar[0]+0x0005c. The corresponding
|
||||
* PCI resource is managed by the main IOC4 driver because it contains
|
||||
* registers of interest to many different IOC4 subdrivers.
|
||||
*/
|
||||
struct ioc4_misc_regs {
|
||||
/* Miscellaneous IOC4 registers */
|
||||
union ioc4_pci_err_addr_l {
|
||||
uint32_t raw;
|
||||
struct {
|
||||
uint32_t valid:1; /* Address captured */
|
||||
uint32_t master_id:4; /* Unit causing error
|
||||
* 0/1: Serial port 0 TX/RX
|
||||
* 2/3: Serial port 1 TX/RX
|
||||
* 4/5: Serial port 2 TX/RX
|
||||
* 6/7: Serial port 3 TX/RX
|
||||
* 8: ATA/ATAPI
|
||||
* 9-15: Undefined
|
||||
*/
|
||||
uint32_t mul_err:1; /* Multiple errors occurred */
|
||||
uint32_t addr:26; /* Bits 31-6 of error addr */
|
||||
} fields;
|
||||
} pci_err_addr_l;
|
||||
uint32_t pci_err_addr_h; /* Bits 63-32 of error addr */
|
||||
union ioc4_sio_int {
|
||||
uint32_t raw;
|
||||
struct {
|
||||
uint8_t tx_mt:1; /* TX ring buffer empty */
|
||||
uint8_t rx_full:1; /* RX ring buffer full */
|
||||
uint8_t rx_high:1; /* RX high-water exceeded */
|
||||
uint8_t rx_timer:1; /* RX timer has triggered */
|
||||
uint8_t delta_dcd:1; /* DELTA_DCD seen */
|
||||
uint8_t delta_cts:1; /* DELTA_CTS seen */
|
||||
uint8_t intr_pass:1; /* Interrupt pass-through */
|
||||
uint8_t tx_explicit:1; /* TX, MCW, or delay complete */
|
||||
} fields[4];
|
||||
} sio_ir; /* Serial interrupt state */
|
||||
union ioc4_other_int {
|
||||
uint32_t raw;
|
||||
struct {
|
||||
uint32_t ata_int:1; /* ATA port passthru */
|
||||
uint32_t ata_memerr:1; /* ATA halted by mem error */
|
||||
uint32_t memerr:4; /* Serial halted by mem err */
|
||||
uint32_t kbd_int:1; /* kbd/mouse intr asserted */
|
||||
uint32_t reserved:16; /* zero */
|
||||
uint32_t rt_int:1; /* INT_OUT section latch */
|
||||
uint32_t gen_int:8; /* Intr. from generic pins */
|
||||
} fields;
|
||||
} other_ir; /* Other interrupt state */
|
||||
union ioc4_sio_int sio_ies; /* Serial interrupt enable set */
|
||||
union ioc4_other_int other_ies; /* Other interrupt enable set */
|
||||
union ioc4_sio_int sio_iec; /* Serial interrupt enable clear */
|
||||
union ioc4_other_int other_iec; /* Other interrupt enable clear */
|
||||
union ioc4_sio_cr {
|
||||
uint32_t raw;
|
||||
struct {
|
||||
uint32_t cmd_pulse:4; /* Bytebus strobe width */
|
||||
uint32_t arb_diag:3; /* PCI bus requester */
|
||||
uint32_t sio_diag_idle:1; /* Active ser req? */
|
||||
uint32_t ata_diag_idle:1; /* Active ATA req? */
|
||||
uint32_t ata_diag_active:1; /* ATA req is winner */
|
||||
uint32_t reserved:22; /* zero */
|
||||
} fields;
|
||||
} sio_cr;
|
||||
uint32_t unused1;
|
||||
union ioc4_int_out {
|
||||
uint32_t raw;
|
||||
struct {
|
||||
uint32_t count:16; /* Period control */
|
||||
uint32_t mode:3; /* Output signal shape */
|
||||
uint32_t reserved:11; /* zero */
|
||||
uint32_t diag:1; /* Timebase control */
|
||||
uint32_t int_out:1; /* Current value */
|
||||
} fields;
|
||||
} int_out; /* External interrupt output control */
|
||||
uint32_t unused2;
|
||||
union ioc4_gpcr {
|
||||
uint32_t raw;
|
||||
struct {
|
||||
uint32_t dir:8; /* Pin direction */
|
||||
uint32_t edge:8; /* Edge/level mode */
|
||||
uint32_t reserved1:4; /* zero */
|
||||
uint32_t int_out_en:1; /* INT_OUT enable */
|
||||
uint32_t reserved2:11; /* zero */
|
||||
} fields;
|
||||
} gpcr_s; /* Generic PIO control set */
|
||||
union ioc4_gpcr gpcr_c; /* Generic PIO control clear */
|
||||
union ioc4_gpdr {
|
||||
uint32_t raw;
|
||||
struct {
|
||||
uint32_t gen_pin:8; /* State of pins */
|
||||
uint32_t reserved:24;
|
||||
} fields;
|
||||
} gpdr; /* Generic PIO data */
|
||||
uint32_t unused3;
|
||||
union ioc4_gppr {
|
||||
uint32_t raw;
|
||||
struct {
|
||||
uint32_t gen_pin:1; /* Single pin state */
|
||||
uint32_t reserved:31;
|
||||
} fields;
|
||||
} gppr[8]; /* Generic PIO pins */
|
||||
};
|
||||
|
||||
/* Masks for GPCR DIR pins */
|
||||
#define IOC4_GPCR_DIR_0 0x01 /* External interrupt output */
|
||||
#define IOC4_GPCR_DIR_1 0x02 /* External interrupt input */
|
||||
#define IOC4_GPCR_DIR_2 0x04
|
||||
#define IOC4_GPCR_DIR_3 0x08 /* Keyboard/mouse presence */
|
||||
#define IOC4_GPCR_DIR_4 0x10 /* Ser. port 0 xcvr select (0=232, 1=422) */
|
||||
#define IOC4_GPCR_DIR_5 0x20 /* Ser. port 1 xcvr select (0=232, 1=422) */
|
||||
#define IOC4_GPCR_DIR_6 0x40 /* Ser. port 2 xcvr select (0=232, 1=422) */
|
||||
#define IOC4_GPCR_DIR_7 0x80 /* Ser. port 3 xcvr select (0=232, 1=422) */
|
||||
|
||||
/* Masks for GPCR EDGE pins */
|
||||
#define IOC4_GPCR_EDGE_0 0x01
|
||||
#define IOC4_GPCR_EDGE_1 0x02 /* External interrupt input */
|
||||
#define IOC4_GPCR_EDGE_2 0x04
|
||||
#define IOC4_GPCR_EDGE_3 0x08
|
||||
#define IOC4_GPCR_EDGE_4 0x10
|
||||
#define IOC4_GPCR_EDGE_5 0x20
|
||||
#define IOC4_GPCR_EDGE_6 0x40
|
||||
#define IOC4_GPCR_EDGE_7 0x80
|
||||
|
||||
#define IOC4_VARIANT_IO9 0x0900
|
||||
#define IOC4_VARIANT_PCI_RT 0x0901
|
||||
#define IOC4_VARIANT_IO10 0x1000
|
||||
|
||||
/* One of these per IOC4 */
|
||||
struct ioc4_driver_data {
|
||||
struct list_head idd_list;
|
||||
unsigned long idd_bar0;
|
||||
struct pci_dev *idd_pdev;
|
||||
const struct pci_device_id *idd_pci_id;
|
||||
struct ioc4_misc_regs __iomem *idd_misc_regs;
|
||||
unsigned long count_period;
|
||||
void *idd_serial_data;
|
||||
unsigned int idd_variant;
|
||||
};
|
||||
|
||||
/* One per submodule */
|
||||
struct ioc4_submodule {
|
||||
struct list_head is_list;
|
||||
char *is_name;
|
||||
struct module *is_owner;
|
||||
int (*is_probe) (struct ioc4_driver_data *);
|
||||
int (*is_remove) (struct ioc4_driver_data *);
|
||||
};
|
||||
|
||||
#define IOC4_NUM_CARDS 8 /* max cards per partition */
|
||||
|
||||
/**********************************
|
||||
* Functions needed by submodules *
|
||||
**********************************/
|
||||
|
||||
extern int ioc4_register_submodule(struct ioc4_submodule *);
|
||||
extern void ioc4_unregister_submodule(struct ioc4_submodule *);
|
||||
|
||||
#endif /* _LINUX_IOC4_H */
|
@ -1070,7 +1070,6 @@
|
||||
#define PCI_VENDOR_ID_SGI 0x10a9
|
||||
#define PCI_DEVICE_ID_SGI_IOC3 0x0003
|
||||
#define PCI_DEVICE_ID_SGI_LITHIUM 0x1002
|
||||
#define PCI_DEVICE_ID_SGI_IOC4 0x100a
|
||||
|
||||
#define PCI_VENDOR_ID_WINBOND 0x10ad
|
||||
#define PCI_DEVICE_ID_WINBOND_82C105 0x0105
|
||||
|
Loading…
Reference in New Issue
Block a user