/* $XFree86$ */
/*
 * $Workfile: msr_rdcl.c $
 *
 * This file contains MSR access routines for Redcloud.
 *
 * NSC_LIC_ALTERNATIVE_PREAMBLE
 *
 * Revision 1.0
 *
 * National Semiconductor Alternative GPL-BSD License
 *
 * National Semiconductor Corporation licenses this software 
 * ("Software"):
 *
 *      Durango
 *
 * under one of the two following licenses, depending on how the 
 * Software is received by the Licensee.
 * 
 * If this Software is received as part of the Linux Framebuffer or
 * other GPL licensed software, then the GPL license designated 
 * NSC_LIC_GPL applies to this Software; in all other circumstances 
 * then the BSD-style license designated NSC_LIC_BSD shall apply.
 *
 * END_NSC_LIC_ALTERNATIVE_PREAMBLE */

/* NSC_LIC_BSD
 *
 * National Semiconductor Corporation Open Source License for Durango
 *
 * (BSD License with Export Notice)
 *
 * Copyright (c) 1999-2001
 * National Semiconductor Corporation.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions 
 * are met: 
 *
 *   * Redistributions of source code must retain the above copyright 
 *     notice, this list of conditions and the following disclaimer. 
 *
 *   * Redistributions in binary form must reproduce the above 
 *     copyright notice, this list of conditions and the following 
 *     disclaimer in the documentation and/or other materials provided 
 *     with the distribution. 
 *
 *   * Neither the name of the National Semiconductor Corporation nor 
 *     the names of its contributors may be used to endorse or promote 
 *     products derived from this software without specific prior 
 *     written permission. 
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 * NATIONAL SEMICONDUCTOR CORPORATION OR CONTRIBUTORS BE LIABLE FOR ANY 
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE,
 * INTELLECTUAL PROPERTY INFRINGEMENT, OR OTHERWISE) ARISING IN ANY WAY 
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
 * OF SUCH DAMAGE.
 *
 * EXPORT LAWS: THIS LICENSE ADDS NO RESTRICTIONS TO THE EXPORT LAWS OF 
 * YOUR JURISDICTION. It is licensee's responsibility to comply with 
 * any export regulations applicable in licensee's jurisdiction. Under 
 * CURRENT (2001) U.S. export regulations this software 
 * is eligible for export from the U.S. and can be downloaded by or 
 * otherwise exported or reexported worldwide EXCEPT to U.S. embargoed 
 * destinations which include Cuba, Iraq, Libya, North Korea, Iran, 
 * Syria, Sudan, Afghanistan and any other country to which the U.S. 
 * has embargoed goods and services. 
 *
 * END_NSC_LIC_BSD */

/* NSC_LIC_GPL
 *
 * National Semiconductor Corporation Gnu General Public License for Durango
 *
 * (GPL License with Export Notice)
 *
 * Copyright (c) 1999-2001
 * National Semiconductor Corporation.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted under the terms of the GNU General 
 * Public License as published by the Free Software Foundation; either 
 * version 2 of the License, or (at your option) any later version  
 *
 * In addition to the terms of the GNU General Public License, neither 
 * the name of the National Semiconductor Corporation nor the names of 
 * its contributors may be used to endorse or promote products derived 
 * from this software without specific prior written permission. 
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 * NATIONAL SEMICONDUCTOR CORPORATION OR CONTRIBUTORS BE LIABLE FOR ANY 
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE, 
 * INTELLECTUAL PROPERTY INFRINGEMENT, OR OTHERWISE) ARISING IN ANY WAY 
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
 * OF SUCH DAMAGE. See the GNU General Public License for more details. 
 *
 * EXPORT LAWS: THIS LICENSE ADDS NO RESTRICTIONS TO THE EXPORT LAWS OF 
 * YOUR JURISDICTION. It is licensee's responsibility to comply with 
 * any export regulations applicable in licensee's jurisdiction. Under 
 * CURRENT (2001) U.S. export regulations this software 
 * is eligible for export from the U.S. and can be downloaded by or 
 * otherwise exported or reexported worldwide EXCEPT to U.S. embargoed 
 * destinations which include Cuba, Iraq, Libya, North Korea, Iran, 
 * Syria, Sudan, Afghanistan and any other country to which the U.S. 
 * has embargoed goods and services. 
 *
 * You should have received a copy of the GNU General Public License 
 * along with this file; if not, write to the Free Software Foundation, 
 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
 *
 * END_NSC_LIC_GPL */

int redcloud_msr_init(void);
DEV_STATUS redcloud_id_msr_device(MSR * pDev, unsigned long address);
DEV_STATUS redcloud_get_msr_dev_address(unsigned int device,
					unsigned long *address);
DEV_STATUS redcloud_get_glink_id_at_address(unsigned int *device,
					    unsigned long address);
DEV_STATUS redcloud_msr_read(unsigned int device, unsigned int msrRegister,
			     Q_WORD * msrValue);
DEV_STATUS redcloud_msr_write(unsigned int device, unsigned int msrRegister,
			      Q_WORD * msrValue);

void redcloud_build_mbus_tree(void);	/* private routine definition */
int redcloud_init_msr_devices(MSR aDev[], unsigned int array_size);	/* private routine definition */
DEV_STATUS redcloud_find_msr_device(MSR * pDev);	/* private routine definition */

/* REDCLOUD MSR BITMASKS */

#define MBD_MSR_CAP			0x2000
#define MSR_CAP_ID_MASK		0xFF000
#define MSR_CAP_ID_SHIFT  	12
#define MSR_CAP_REV_MASK    0x0F
#define MBIU_CAP			0x86
#define NUM_PORTS_MASK		0x00380000
#define NUM_PORTS_SHIFT  	19
#define MBIU_WHOAMI			0x8B
#define WHOAMI_MASK			0x07

/* REDCLOUD and CS5535 MSR DEVICES */

MSR msrDev[] = {
   {FOUND, RC_CC_MBIU, RC_MB0_MBIU0},
   {FOUND, RC_CC_MBIU, RC_MB0_MBIU1},
   {NOT_KNOWN, RC_CC_MCP, FAKE_ADDRESS},
   {NOT_KNOWN, RC_CC_MPCI, FAKE_ADDRESS},
   {NOT_KNOWN, RC_CC_MC, FAKE_ADDRESS},
   {NOT_KNOWN, RC_CC_GP, FAKE_ADDRESS},
   {NOT_KNOWN, RC_CC_VG, FAKE_ADDRESS},
   {NOT_KNOWN, RC_CC_DF, FAKE_ADDRESS},
   {NOT_KNOWN, RC_CC_FG, FAKE_ADDRESS},
   {FOUND, RC_CC_VA, RC_MB0_CPU},
   {FOUND, CP_CC_MBIU, CP_MB0_MBIU0},
   {NOT_KNOWN, CP_CC_MPCI, FAKE_ADDRESS},
   {NOT_KNOWN, CP_CC_USB2, FAKE_ADDRESS},
   {NOT_KNOWN, CP_CC_ATAC, FAKE_ADDRESS},
   {NOT_KNOWN, CP_CC_MDD, FAKE_ADDRESS},
   {NOT_KNOWN, CP_CC_ACC, FAKE_ADDRESS},
   {NOT_KNOWN, CP_CC_USB1, FAKE_ADDRESS},
   {NOT_KNOWN, CP_CC_MCP, FAKE_ADDRESS},
};

#define NUM_DEVS sizeof(msrDev) / sizeof(struct msr)

/* CAPISTRANO DEVICE INDEX LIMITS */
/* These defines represent the start and stop indexes into the device array  */
/* for all Capistrano devices.  These should be updated whenever a device is */
/* added or removed to the Capistrano list.                                  */

#define CP_INDEX_START CP_ID_MBIU
#define CP_INDEX_STOP  CP_ID_MCP

/* GLOBAL MBUS CACHE STRUCTURES */
/* These structures contain a "cached" copy of the MBUS topology */
/* for easy future lookup.                                       */

MBUS_NODE MBIU0[8], MBIU1[8], MBIU2[8];

/* REGISTER MACROS */

#define GET_DEVICE_ID( CAPABILITIES_HIGH, CAPABILITIES_LOW ) \
					 ((unsigned int)(( (CAPABILITIES_LOW) & MSR_CAP_ID_MASK ) >> MSR_CAP_ID_SHIFT ))

#define GET_NUM_PORTS( MBIU_CAP_HIGH, MBIU_CAP_LOW ) (((MBIU_CAP_HIGH) & NUM_PORTS_MASK ) >> NUM_PORTS_SHIFT)

/*-----------------------------------------------------------------------------
 * gfx_msr_init
 * 
 * This routine initializes the base addresses of all known MBUS devices.  
 *-----------------------------------------------------------------------------
 */
#if GFX_MSR_DYNAMIC
int
redcloud_msr_init(void)
#else
int
gfx_msr_init(void)
#endif
{
   Q_WORD msrValue;
   int return_value = 1;

   /* CHECK FOR VALID MBUS CONFIGURATION */
   /* The CPU and the two MBIUs are assumed to be at known static addresses, so */
   /* we will check the device IDs at these addresses as proof of a valid mbus  */
   /* configuration.                                                            */

   MSR_READ(MBD_MSR_CAP, RC_MB0_CPU, &(msrValue.high), &(msrValue.low));
   if (GET_DEVICE_ID(msrValue.high, msrValue.low) != RC_CC_VA)
      return_value = 0;

   MSR_READ(MBD_MSR_CAP, RC_MB0_MBIU0, &(msrValue.high), &(msrValue.low));
   if (GET_DEVICE_ID(msrValue.high, msrValue.low) != RC_CC_MBIU)
      return_value = 0;

   MSR_READ(MBD_MSR_CAP, RC_MB0_MBIU1, &(msrValue.high), &(msrValue.low));
   if (GET_DEVICE_ID(msrValue.high, msrValue.low) != RC_CC_MBIU)
      return_value = 0;

   /* ENUMERATE VALID BUS */
   /* If all static devices were identified, continue with the enumeration */

   if (return_value) {
      /* OPTIMIZATION */
      /* Build a local copy of the MBUS topology.  This allows us to  */
      /* quickly search the entire MBUS for a given device ID without */
      /* repeated MSR accesses.                                       */

      redcloud_build_mbus_tree();

      /* INITIALIZE MSR DEVICES */

      return_value = redcloud_init_msr_devices(msrDev, NUM_DEVS);

   }

   return return_value;

}

/*--------------------------------------------------------------------------
 * void	redcloud_build_mbus_tree() (PRIVATE ROUTINE - NOT PART OF DURANGO API)
 *
 * This routine walks through the MBUS and records the address value and 
 * device ID found at each node.  If a node (aka port) is not populated, 
 * that node returns '0'.  The deviceID for that node is set to '0' 
 * (NOT_POPULATED) to reflect this. If the node being queried points back to 
 * Vail or MBIU0, the deviceID for that node is set to 'REFLECTIVE'.  Reflective 
 * nodes are nodes that forward the given MBUS address BACK to the initiator.
 *-----------------------------------------------------------------------------
 */
void
redcloud_build_mbus_tree(void)
{
   unsigned long mbiu_port_count, reflective;
   unsigned long port;
   Q_WORD msrValue;

   /*                  */
   /* ENUMERATE MBIU0  */
   /*                  */

   /* COUNT MBIU PORTS */

   MSR_READ(MBIU_CAP, RC_MB0_MBIU0, &(msrValue.high), &(msrValue.low));
   mbiu_port_count = GET_NUM_PORTS(msrValue.high, msrValue.low);

   /* FIND REFLECTIVE PORT */
   /* Query the MBIU for the port through which we are communicating. */
   /* We will avoid accesses to this port to avoid a self-reference.  */

   MSR_READ(MBIU_WHOAMI, RC_MB0_MBIU0, &(msrValue.high), &(msrValue.low));
   reflective = msrValue.low & WHOAMI_MASK;

   /* ENUMERATE ALL PORTS */
   /* For every possible port, set the MBIU.deviceId to something. */

   for (port = 0; port < 8; port++) {
      /* FILL IN CLAIMED FIELD */
      /* All MBIU ports can only be assigned to one device from the */
      /* Durango table                                              */

      MBIU0[port].claimed = 0;

      /* MBIU0 PORT NUMBERS ARE IN ADDRESS BITS 31:29 */

      MBIU0[port].address = port << 29;

      /* SPECIAL CASE FOR MBIU0 */
      /* MBIU0 port 0 is a special case, as it points back to MBIU0.  MBIU0 */
      /* responds at address 0x40000xxx, which does not equal 0 << 29.      */

      if (port == 0)
	 MBIU0[port].deviceId = RC_CC_MBIU;
      else if (port == reflective)
	 MBIU0[port].deviceId = REFLECTIVE;
      else if (port > mbiu_port_count)
	 MBIU0[port].deviceId = NOT_POPULATED;
      else {
	 MSR_READ(MBD_MSR_CAP, MBIU0[port].address, &(msrValue.high),
		  &(msrValue.low));
	 MBIU0[port].deviceId = GET_DEVICE_ID(msrValue.high, msrValue.low);
      }
   }

   /*                  */
   /* ENUMERATE MBIU1  */
   /*                  */

   /* COUNT MBIU PORTS */

   MSR_READ(MBIU_CAP, RC_MB0_MBIU1, &(msrValue.high), &(msrValue.low));
   mbiu_port_count = GET_NUM_PORTS(msrValue.high, msrValue.low);

   /* FIND REFLECTIVE PORT */
   /* Query the MBIU for the port through which we are communicating. */
   /* We will avoid accesses to this port to avoid a self-reference.  */

   MSR_READ(MBIU_WHOAMI, RC_MB0_MBIU1, &(msrValue.high), &(msrValue.low));
   reflective = msrValue.low & WHOAMI_MASK;

   /* ENUMERATE ALL PORTS */
   /* For every possible port, set the MBIU.deviceId to something. */

   for (port = 0; port < 8; port++) {
      /* FILL IN CLAIMED FIELD */
      /* All MBIU ports can only be assigned to one device from the */
      /* Durango table                                              */

      MBIU1[port].claimed = 0;

      /* MBIU1 PORT NUMBERS ARE IN 28:26 AND 31:29 = 010B */

      MBIU1[port].address = (0x02l << 29) + (port << 26);

      if (port == reflective)
	 MBIU1[port].deviceId = REFLECTIVE;
      else if (port > mbiu_port_count)
	 MBIU1[port].deviceId = NOT_POPULATED;
      else {
	 MSR_READ(MBD_MSR_CAP, MBIU1[port].address, &(msrValue.high),
		  &(msrValue.low));
	 MBIU1[port].deviceId = GET_DEVICE_ID(msrValue.high, msrValue.low);
      }
   }

   /*                          */
   /* ENUMERATE MBIU2 (CS5535) */
   /*  (if present)            */

   MSR_READ(MBD_MSR_CAP, CP_MB0_MBIU0, &(msrValue.high), &(msrValue.low));
   if (GET_DEVICE_ID(msrValue.high, msrValue.low) == CP_CC_MBIU) {
      /* COUNT MBIU PORTS */

      MSR_READ(MBIU_CAP, CP_MB0_MBIU0, &(msrValue.high), &(msrValue.low));
      mbiu_port_count = GET_NUM_PORTS(msrValue.high, msrValue.low);

      /* FIND REFLECTIVE PORT */
      /* Query the MBIU for the port through which we are communicating. */
      /* We will avoid accesses to this port to avoid a self-reference.  */

      MSR_READ(MBIU_WHOAMI, CP_MB0_MBIU0, &(msrValue.high), &(msrValue.low));
      reflective = msrValue.low & WHOAMI_MASK;

      /* ENUMERATE ALL PORTS */
      /* For every possible port, set the MBIU.deviceId to something. */

      for (port = 0; port < 8; port++) {
	 /* FILL IN CLAIMED FIELD */
	 /* All MBIU ports can only be assigned to one device from the */
	 /* Durango table                                              */

	 MBIU2[port].claimed = 0;

	 /* MBIU2 PORT NUMBERS ARE IN 22:20 AND 31:23 = 010100010B */

	 MBIU2[port].address =
	       (0x02l << 29) + (0x04l << 26) + (0x02l << 23) + (port << 20);

	 if (port == reflective)
	    MBIU2[port].deviceId = REFLECTIVE;
	 else if (port > mbiu_port_count)
	    MBIU2[port].deviceId = NOT_POPULATED;
	 else {
	    MSR_READ(MBD_MSR_CAP, MBIU2[port].address, &(msrValue.high),
		     &(msrValue.low));
	    MBIU2[port].deviceId = GET_DEVICE_ID(msrValue.high, msrValue.low);
	 }
      }
   } else {
      /* NO 5535                                                  */
      /* If the CS5535 is not installed, fill in the cached table */
      /* with the 'NOT_INSTALLED' flag.  Also, fill in the device */
      /* status from NOT_KNOWN to REQ_NOT_INSTALLED.              */

      for (port = 0; port < 8; port++) {
	 MBIU2[port].claimed = 0;
	 MBIU2[port].deviceId = NOT_INSTALLED;
	 MBIU2[port].address =
	       (0x02l << 29) + (0x04l << 26) + (0x02l << 23) + (port << 20);
      }
      for (port = CP_INDEX_START; port <= CP_INDEX_STOP; port++) {
	 msrDev[port].Present = REQ_NOT_INSTALLED;
      }
   }
}

/*------------------------------------------------------------------
 * redcloud_init_msr_devices (PRIVATE ROUTINE - NOT PART OF DURANGO API)

 * Handles the details of finding each possible device on the MBUS.
 * If a given device is not found, its structure is left uninitialized.
 * If a given device is found, its structure is updated.
 *
 * This init routine only checks for devices in aDev[].
 *
 *  Passed:
 *		aDev - is a pointer to the array of MBUS devices.
 *		arraySize - number of elements in aDev.
 *
 *	Returns:
 *		1 - If, for every device, its address was found.
 *		0 - If, for any device, an error was encountered.
 *------------------------------------------------------------------
 */
int
redcloud_init_msr_devices(MSR aDev[], unsigned int array_size)
{
   unsigned int i, issues = 0;

   /* TRY TO FIND EACH ITEM IN THE ARRAY */

   for (i = 0; i < array_size; i++) {
      /* IGNORE DEVICES THAT ARE ALREADY FOUND                */
      /* The addresses for "found" devices are already known. */

      if (aDev[i].Present == FOUND || aDev[i].Present == REQ_NOT_INSTALLED)
	 continue;

      /* TRY TO FIND THE DEVICE ON THE MBUS */

      aDev[i].Present = redcloud_find_msr_device(&aDev[i]);

      /* INCREMENT ERROR COUNT IF DEVICE NOT FOUND */

      if (aDev[i].Present != FOUND)
	 issues++;
   }

   return (issues == 0);
}

/*------------------------------------------------------------------
 *  redcloud_find_msr_device (PRIVATE ROUTINE - NOT PART OF DURANGO API)
 *
 *  Passed:
 *	    pDev - is a pointer to one element in the array of MBUS devices
 *
 *  Returns:
 *	    FOUND - Device was found and pDev->Address has been updated.
 *
 *		REQ_NOT_FOUND - Device was not found and pDev->Address has not
 *						been updated.
 *
 *------------------------------------------------------------------
*/
DEV_STATUS
redcloud_find_msr_device(MSR * pDev)
{
   unsigned int i;

   /* SEARCH DURANGO'S CACHED MBUS TOPOLOGY */
   /* This gets a little tricky.  As the only identifier we have for each   */
   /* device is the device ID and we have multiple devices of the same type */
   /* MCP, MPCI, USB, etc. we need to make some assumptions based on table  */
   /* order.  These are as follows:                                         */
   /* 1. All Redcloud nodes are searched first, as we assume that they      */
   /*    are first in the table.                                            */
   /* 2. If two devices have the same device ID and are found on the same   */
   /*    device (GX2, CS5535, etc.) we assume that they are listed such     */
   /*    that the first device in the table with this device ID has a lower */
   /*    port address.                                                      */
   /* 3. After a device ID has been matched, the port is marked as          */
   /*    'claimed', such that future enumerations continue searching the    */
   /*    GeodeLink topology.                                                */

   /* SEARCH MBIU0 */

   for (i = 0; i < 8; i++) {
      if (MBIU0[i].deviceId == pDev->Id && !(MBIU0[i].claimed)) {
	 MBIU0[i].claimed = 1;
	 pDev->Address = MBIU0[i].address;
	 return FOUND;
      }
   }

   /* SEARCH MBIU1 */

   for (i = 0; i < 8; i++) {
      if (MBIU1[i].deviceId == pDev->Id && !(MBIU1[i].claimed)) {
	 MBIU1[i].claimed = 1;
	 pDev->Address = MBIU1[i].address;
	 return FOUND;
      }
   }

   /* SEARCH MBIU2 */

   for (i = 0; i < 8; i++) {
      if (MBIU2[i].deviceId == pDev->Id && !(MBIU2[i].claimed)) {
	 MBIU2[i].claimed = 1;
	 pDev->Address = MBIU2[i].address;
	 return FOUND;
      }
   }

   return REQ_NOT_FOUND;
}

/*--------------------------------------------------------------------
 * gfx_id_msr_device 
 *
 *	This routine handles reading the capabilities MSR register (typically 0x2000)
 *	and checking if the 'id' field matchs  pDev.Id.  This routine is
 *  used by applications/drivers that need to extend the list of known
 *  MBUS devices beyond those known by Durango.
 *
 *		Passed:
 *			pDev - Pointer to MSR structure containing the device's ID.
 *		    address - device address.
 *
 *		Returns:
 *			FOUND - The IDs do match.
 *			REQ_NOT_FOUND - There was not a match.
 *
 *--------------------------------------------------------------------
 */
#if GFX_MSR_DYNAMIC
DEV_STATUS
redcloud_id_msr_device(MSR * pDev, unsigned long address)
#else
DEV_STATUS
gfx_id_msr_device(MSR * pDev, unsigned long address)
#endif
{
   Q_WORD msrValue;

   MSR_READ(MBD_MSR_CAP, address, &(msrValue.high), &(msrValue.low));

   if (GET_DEVICE_ID(msrValue.high, msrValue.low) == pDev->Id)
      return FOUND;
   else
      return REQ_NOT_FOUND;
}

/*--------------------------------------------------------------------
 * gfx_get_msr_dev_address
 * 
 * This function returns the 32-bit address of the requested device.
 * The device must be a known MBUS device.  (It must be in Durango's 
 * device table.)  DEV_STATUS should be checked to verify that the address 
 * was updated.
 *
 *
 * Passed:
 *     device - device index of the device in question.
 *	   *address - ptr to location where address should be stored.
 *         
 * Returns:
 *	   DEV_STATUS of device in question.  (NOT_KNOWN if device is out of range.)
 *     *address - updated if 'device' is within range
 *   
 *	Notes:
 *     This function should only be called after gfx_msr_init
 *
 *--------------------------------------------------------------------
 */
#if GFX_MSR_DYNAMIC
DEV_STATUS
redcloud_get_msr_dev_address(unsigned int device, unsigned long *address)
#else
DEV_STATUS
gfx_get_msr_dev_address(unsigned int device, unsigned long *address)
#endif
{
   if (device < NUM_DEVS) {
      if (msrDev[device].Present == FOUND)
	 *address = msrDev[device].Address;

      return msrDev[device].Present;
   }
   return NOT_KNOWN;

}

/*--------------------------------------------------------------------
 *  gfx_get_glink_id_at_address
 *   
 *	This function returns the 16-bit deviceId at the requested address.
 *  DEV_STATUS should be checked to make sure that device was updated.
 *
 *	Passed:
 *	    device - ptr to location where device ID should be stored.
 *		address - address of desired device ID.
 *         
 *  Returns:
 *	    FOUND if address is a valid address, NOT_KNOWN if address cannot be found 
 *      on the mbus.
 *      *device - updated with device Id info.
 *
 *	Notes:
 *      This function should be called after gfx_msr_init 
 *
 *--------------------------------------------------------------------
 */
#if GFX_MSR_DYNAMIC
DEV_STATUS
redcloud_get_glink_id_at_address(unsigned int *device, unsigned long address)
#else
DEV_STATUS
gfx_get_glink_id_at_address(unsigned int *device, unsigned long address)
#endif
{
   int port;

   for (port = 0; port < 8; port++) {
      if (MBIU0[port].address == address) {
	 *device = MBIU0[port].deviceId;
	 return FOUND;
      } else if (MBIU1[port].address == address) {
	 *device = MBIU1[port].deviceId;
	 return FOUND;
      } else if (MBIU2[port].address == address) {
	 *device = MBIU2[port].deviceId;
	 return FOUND;
      }
   }

   return NOT_KNOWN;

}

/*--------------------------------------------------------------------
 * gfx_msr_read
 *
 * Performs a 64-bit read from 'msrRegister' in device 'device'.  'device' is 
 * an index into Durango's table of known MBUS devices.
 *
 * Returns:
 *     FOUND - if no errors were detected and msrValue has been updated.
 *	   NOT_KNOWN	- an error was detected.  msrValue is not updated.
 *	   REQ_NOT_FOUND - 'msrAddress' for 'devID' is unknown.  Caller
 *						should call msrInit() first.  msrValue is not updated.
 *	   Notes:
 *         This function should be called after gfx_msr_init
 *--------------------------------------------------------------------
 */
#if GFX_MSR_DYNAMIC
DEV_STATUS
redcloud_msr_read(unsigned int device, unsigned int msrRegister,
		  Q_WORD * msrValue)
#else
DEV_STATUS
gfx_msr_read(unsigned int device, unsigned int msrRegister, Q_WORD * msrValue)
#endif
{
   if (device < NUM_DEVS) {
      if (msrDev[device].Present == FOUND)
	 MSR_READ(msrRegister, msrDev[device].Address, &(msrValue->high),
		  &(msrValue->low));

      return msrDev[device].Present;
   }
   return NOT_KNOWN;
}

/*--------------------------------------------------------------------
 * gfx_msr_write
 *
 *		Performs a 64-bit write to 'msrRegister' in device 'devID'.
 *
 *		Returns:
 *			FOUND - if no errors were detected and msrValue has been updated.
 *			NOT_KNOWN	- an error was detected.  msrValue is not updated.
 *			REQ_NOT_FOUND - 'msrAddress' for 'devID' is unknown.  Caller
 *						should call msrInit() first.  msrValue is not updated.
 *
 *		Notes:
 *      	This function is valid to call after initMSR_API() 
 *
 *--------------------------------------------------------------------
 */
#if GFX_MSR_DYNAMIC
DEV_STATUS
redcloud_msr_write(unsigned int device, unsigned int msrRegister,
		   Q_WORD * msrValue)
#else
DEV_STATUS
gfx_msr_write(unsigned int device, unsigned int msrRegister,
	      Q_WORD * msrValue)
#endif
{
   if (device < NUM_DEVS) {
      if (msrDev[device].Present == FOUND)
	 MSR_WRITE(msrRegister, msrDev[device].Address, &(msrValue->high),
		   &(msrValue->low));

      return msrDev[device].Present;
   }
   return NOT_KNOWN;
}
