/*

    Copyright (C) Corollary, Inc., 1993, 1994.
    All Rights Reserved.
    This Module contains Proprietary Information of
    Corollary, Inc., and should be treated as Confidential.

Module Name:

    cbdrivers.c

Abstract:

    This OS/2 2.x module creates a software-viewable table of the valid
    element spaces present in a Corollary C-bus II machine.  Since
    searching through non-existent memory space can cause NMIs,
    it is highly recommended that all areas needing this information
    consult the built software table rather than rescanning themselves.

Author:

    Landy Wang (landy@corollary.com) 11-Jan-1994
 */

#include "cbustype.h"
#include "cbus.h"		/* C-bus hardware architecture definitions */

#ifndef MIN
#define MIN(a,b)		(((a)>(b))?(b):(a))
#endif

/*
 *	C-bus II half-card device driver interface:
 *
 *	this scheme allows a single driver can have multiple
 *	cards (ie: elements) present, and the single driver can
 *	support all of them.
 *
 *	to identify what I/O devices exist on the Cbus2 bus, software
 *	would need to disable NMI, and check all element spaces for
 *	valid element_type IDs (ie: NOT 0xff == BUS_TIMEOUT).  since this
 *	work is HIGHLY Cbus2 specific (poking the interrupt control
 *	registers, turning off fault-enables, etc), this will be done
 *	by our ROM and passed to the PSD.
 *
 *	the kernel will build a software table of all elements,
 *	CbusIoElements[].  software can scan this table at any time,
 *	looking for specific io_type IDs.  no entries in this table
 *	should ever change once the table has been initially built.
 *	in this way, other pieces of OS/2 can determine what's in the
 *	machine without worrying about generating NMIs, etc, etc.
 *	this software still needs to be integrated into the registry.
 *
 *	it is assumed drivers MUST use the same intr map lines on all
 *	CBCs where his cards are attached.  ie: it is ILLEGAL for a
 *	SCSI driver to use local irq4 for CPU1's adapter 6 and local
 *	irq5 for CPU2's adapter 6.
 *
 *	it is legal for CPU3's localirq 5 can be a SCSI, whilst CPU4's
 *	localirq 5 can be an SIO.  we distinguish them in the kernel
 *	because SIO and SCSI will have different io_type IDs.
 *
 *	public I/O devices, control I/O, Prog I/O space of each
 *	element is to be mapped in by the drivers themselves.  the
 *	PSD will not do this.  the driver is provided the physical
 *	start and length of each element space via CbusIoElements[].
 *	as described above.  hence, no driver should scan global memory
 *	space searching for elements, which can cause NMIs, etc.
 */

typedef struct _cbus_io_elements {

	ULONG	EntryLength;
	ULONG	ElementID;
	ULONG	ElementType;
	ULONG	IoAttribute;
	ULONG	AddressStart;			/* physical address in bytes */
	ULONG	AddressLength;			/* length in bytes */
	ULONG	CbcNumber;			/* BusNumber to drivers */
	PVOID	Csr;				/* virtually mapped space */

} CBUS_IO_ELEMENTS_T, *PCBUS_IO_ELEMENTS;

CBUS_IO_ELEMENTS_T CbusIoElements[MAX_ELEMENT_CSRS];

ULONG CbusIoElementIndex;

ULONG CBCIndex;

/*
Routine Description:

    Convert the CBC number to a CSR pointer.

Arguments:

    CbcNumber - Supplies the CBC number whose registers need to be accessed.

Return Value:

    Pointer to the CSR referenced by the argument CBC number, 0 if the
    argument was invalid.
 */
PVOID
CbusCBCtoCSR(
IN ULONG CbcNumber
)
{
    PCBUS_IO_ELEMENTS	p;

    if (CbcNumber >= CBCIndex) {
	return (PVOID)0;
    }

    for (p = CbusIoElements; p < &CbusIoElements[CbusIoElementIndex]; p++) {
	if (p->CbcNumber == CbcNumber) {
	    return (PVOID)(p->Csr);
	}
    }

    return (PVOID)0;
}

/*

Routine Description:

    Add the passed I/O entry to the table of Cbus I/O devices.
    Note this table is for Cbus I/O devices only - EISA/ISA/MCA
    devices are not part of this table.

    This table containing all the elements is constructed so we can
    give Cbus hardware drivers (and the rest of OS/2) meaningful information.

Arguments:

    From - Supplies a pointer to the RRD source table

    To - Supplies a pointer to the destination storage for the table

Return Value:

    Number of valid table entries.

 */

VOID
CbusIOPresent(
IN ULONG Id,
IN ULONG IoFunction,
IN ULONG IoAttribute,
IN ULONG IoStart,
IN ULONG IoSize,
IN PVOID Csr
)
{
	PCBUS_IO_ELEMENTS	p, q;

	/*
	 *
	 * If no room, can't register this I/O device.  Should never happen.
	 *
	 */

	if (CbusIoElementIndex < MAX_ELEMENT_CSRS) {

		p = &CbusIoElements[CbusIoElementIndex];
	
		/*
		 *
		 * Storing the length field allows the PSD to add more
		 * information to an entry in the future, and still
		 * maintain compatibility with older driver binaries.
		 *
		 */
		p->EntryLength = sizeof (CBUS_IO_ELEMENTS_T);

		/*
		 *
		 * Process valid elements - these may or may
		 * not have a CPU on the card, but all can be accessed
		 * from any CPU in the system.  memory cards and processors
		 * are not included here - this is for device drivers only.
		 *
		 */
		p->ElementID = Id;
		p->ElementType = IoFunction;
		p->IoAttribute = IoAttribute;
		p->AddressStart = IoStart;
		p->AddressLength = IoSize;
		p->Csr = Csr;

		/*
		 *
		 * More than one I/O element may share a single CBC.
		 * This is handled right here.
		 *
		 */
		for (q = CbusIoElements; q < p; q++) {
			if (q->ElementID == Id) {
				p->CbcNumber = q->CbcNumber;
				break;
			}
		}
		if (q == p) {
			p->CbcNumber = CBCIndex++;
		}
	
		CbusIoElementIndex++;
	}
}

/*

Routine Description:

    The function returns the Corollary Cbus data for a slot or address.  
    Drivers will be expected to call this function repeatedly with
    incrementing SlotNumbers, until this function returns 0, indicating
    that the last slot has been read.

    Each chunk of slot data should then be examined by the driver.
    The driver may skip any chunk that doesn't belong to him.
    Once a chunk is encountered that belongs to him, the CBCNumber
    field must be treated as the "Cbus BusNumber" (it's really a CBC number,
    not including the CBCs of the EISA bridges), to pass in when he asks for
    an interrupt vector via CbusAllocateVector().

Arguments:

    BusNumber - Indicates which bus.

    SlotNumber - Indicates which entry.

    Buffer - Supplies the space to store the data.

    Length - Supplies a count in bytes of the maximum amount to return.

Return Value:

    Returns the amount of data stored into the buffer.

 */

ULONG
CbusGetInternalData(
IN ULONG BusNumber,
IN ULONG SlotNumber,
IN PVOID Buffer,
IN ULONG Length
)
{
    PVOID From;
    ULONG MaxLength;

    /*
     * let our caller know when he's hit the end
     */
    if (SlotNumber >= CbusIoElementIndex)
	return 0;

    From = (PVOID)(CbusIoElements + SlotNumber);
    MaxLength = (CbusIoElementIndex - SlotNumber) * sizeof (CBUS_IO_ELEMENTS_T);

    Length = MIN(Length, MaxLength);

    bcopy(From, Buffer, Length);

    return Length;
}

/*

Routine Description:

    The function sets the Corollary Cbus data for a slot or address.

Arguments:

    BusNumber - Indicates which bus.

    SlotNumber - Indicates which entry.

    Buffer - Supplies the space to store the data.

    Length - Supplies a count in bytes of the maximum amount to return.

Return Value:

    Returns the amount of data stored into the buffer.

 */

ULONG
CbusSetInternalData(
IN ULONG BusNumber,
IN ULONG SlotNumber,
IN PVOID Buffer,
IN ULONG Length
)
{
    PVOID To;
    ULONG MaxLength;

    /*
     * don't allow writes beyond the end
     */
    if (SlotNumber >= CbusIoElementIndex)
	return 0;

    To = (PVOID)(CbusIoElements + SlotNumber);
    MaxLength = (CbusIoElementIndex - SlotNumber) * sizeof (CBUS_IO_ELEMENTS_T);

    Length = MIN(Length, MaxLength);

    bcopy(Buffer, To, Length);

    return Length;
}
