msr_rdcl.c revision f29dbc25
1/* Copyright (c) 2005 Advanced Micro Devices, Inc.
2 *
3 * Permission is hereby granted, free of charge, to any person obtaining a copy
4 * of this software and associated documentation files (the "Software"), to
5 * deal in the Software without restriction, including without limitation the
6 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7 * sell copies of the Software, and to permit persons to whom the Software is
8 * furnished to do so, subject to the following conditions:
9 *
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19 * IN THE SOFTWARE.
20 *
21 * Neither the name of the Advanced Micro Devices, Inc. nor the names of its
22 * contributors may be used to endorse or promote products derived from this
23 * software without specific prior written permission.
24 * */
25
26/*
27 * This file contains MSR access routines for Redcloud.
28 * */
29
30void redcloud_build_mbus_tree(void);   /* private routine definition */
31int redcloud_init_msr_devices(MSR aDev[], unsigned int array_size);
32
33                                                                                /* private routine definition */
34DEV_STATUS redcloud_find_msr_device(MSR * pDev);
35
36                                                                                /* private routine definition */
37
38/* REDCLOUD MSR BITMASKS */
39
40#define MBD_MSR_CAP			0x2000
41#define MSR_CAP_ID_MASK		0xFF000
42#define MSR_CAP_ID_SHIFT  	12
43#define MSR_CAP_REV_MASK    0x0F
44#define MBIU_CAP			0x86
45#define NUM_PORTS_MASK		0x00380000
46#define NUM_PORTS_SHIFT  	19
47#define MBIU_WHOAMI			0x8B
48#define WHOAMI_MASK			0x07
49
50/* REDCLOUD and CS5535 MSR DEVICES */
51
52MSR msrDev[] = {
53    {FOUND, RC_CC_MBIU, RC_MB0_MBIU0},
54    {FOUND, RC_CC_MBIU, RC_MB0_MBIU1},
55    {NOT_KNOWN, RC_CC_MCP, FAKE_ADDRESS},
56    {NOT_KNOWN, RC_CC_MPCI, FAKE_ADDRESS},
57    {NOT_KNOWN, RC_CC_MC, FAKE_ADDRESS},
58    {NOT_KNOWN, RC_CC_GP, FAKE_ADDRESS},
59    {NOT_KNOWN, RC_CC_VG, FAKE_ADDRESS},
60    {NOT_KNOWN, RC_CC_DF, FAKE_ADDRESS},
61    {NOT_KNOWN, RC_CC_FG, FAKE_ADDRESS},
62    {FOUND, RC_CC_VA, RC_MB0_CPU},
63    {FOUND, CP_CC_MBIU, CP_MB0_MBIU0},
64    {NOT_KNOWN, CP_CC_MPCI, FAKE_ADDRESS},
65    {NOT_KNOWN, CP_CC_USB2, FAKE_ADDRESS},
66    {NOT_KNOWN, CP_CC_ATAC, FAKE_ADDRESS},
67    {NOT_KNOWN, CP_CC_MDD, FAKE_ADDRESS},
68    {NOT_KNOWN, CP_CC_ACC, FAKE_ADDRESS},
69    {NOT_KNOWN, CP_CC_USB1, FAKE_ADDRESS},
70    {NOT_KNOWN, CP_CC_MCP, FAKE_ADDRESS},
71};
72
73#define NUM_DEVS sizeof(msrDev) / sizeof(struct msr)
74
75/* CAPISTRANO DEVICE INDEX LIMITS */
76/* These defines represent the start and stop indexes into the device array
77 * for all Capistrano devices.  These should be updated whenever a device is
78 * added or removed to the Capistrano list.
79 * */
80
81#define CP_INDEX_START CP_ID_MBIU
82#define CP_INDEX_STOP  CP_ID_MCP
83
84/* GLOBAL MBUS CACHE STRUCTURES */
85/* These structures contain a "cached" copy of the MBUS topology */
86/* for easy future lookup.                                       */
87
88MBUS_NODE MBIU0[8], MBIU1[8], MBIU2[8];
89
90/* REGISTER MACROS */
91
92#define GET_DEVICE_ID( CAPABILITIES_HIGH, CAPABILITIES_LOW ) \
93					 ((unsigned int)(( (CAPABILITIES_LOW) & MSR_CAP_ID_MASK ) >> MSR_CAP_ID_SHIFT ))
94
95#define GET_NUM_PORTS( MBIU_CAP_HIGH, MBIU_CAP_LOW ) (((MBIU_CAP_HIGH) & NUM_PORTS_MASK ) >> NUM_PORTS_SHIFT)
96
97/*----------------------------------------------------------------------------
98 * gfx_msr_init
99 *
100 * This routine initializes the base addresses of all known MBUS devices.
101 *----------------------------------------------------------------------------
102 */
103#if GFX_MSR_DYNAMIC
104int
105redcloud_msr_init(void)
106#else
107int
108gfx_msr_init(void)
109#endif
110{
111    Q_WORD msrValue;
112    int return_value = 1;
113
114    /* CHECK FOR VALID MBUS CONFIGURATION */
115    /* The CPU and the two MBIUs are assumed to be at known static addresses,
116     * so we will check the device IDs at these addresses as proof of a valid
117     * mbus  configuration.
118     * */
119
120    MSR_READ(MBD_MSR_CAP, RC_MB0_CPU, &(msrValue.high), &(msrValue.low));
121    if (GET_DEVICE_ID(msrValue.high, msrValue.low) != RC_CC_VA)
122        return_value = 0;
123
124    MSR_READ(MBD_MSR_CAP, RC_MB0_MBIU0, &(msrValue.high), &(msrValue.low));
125    if (GET_DEVICE_ID(msrValue.high, msrValue.low) != RC_CC_MBIU)
126        return_value = 0;
127
128    MSR_READ(MBD_MSR_CAP, RC_MB0_MBIU1, &(msrValue.high), &(msrValue.low));
129    if (GET_DEVICE_ID(msrValue.high, msrValue.low) != RC_CC_MBIU)
130        return_value = 0;
131
132    /* ENUMERATE VALID BUS */
133    /* If all static devices were identified, continue with the enumeration */
134
135    if (return_value) {
136        /* OPTIMIZATION */
137        /* Build a local copy of the MBUS topology.  This allows us to  */
138        /* quickly search the entire MBUS for a given device ID without */
139        /* repeated MSR accesses.                                       */
140
141        redcloud_build_mbus_tree();
142
143        /* INITIALIZE MSR DEVICES */
144
145        return_value = redcloud_init_msr_devices(msrDev, NUM_DEVS);
146
147    }
148
149    return return_value;
150
151}
152
153/*--------------------------------------------------------------------------
154 * void	redcloud_build_mbus_tree() (PRIVATE ROUTINE - NOT PART OF DURANGO API)
155 *
156 * This routine walks through the MBUS and records the address value and
157 * device ID found at each node.  If a node (aka port) is not populated,
158 * that node returns '0'.  The deviceID for that node is set to '0'
159 * (NOT_POPULATED) to reflect this. If the node being queried points back to
160 * Vail or MBIU0, the deviceID for that node is set to 'REFLECTIVE'.
161 * Reflective nodes are nodes that forward the given MBUS address BACK to the
162 *  initiator.
163 *----------------------------------------------------------------------------
164 */
165void
166redcloud_build_mbus_tree(void)
167{
168    unsigned long mbiu_port_count, reflective;
169    unsigned long port;
170    Q_WORD msrValue;
171
172    /*                  */
173    /* ENUMERATE MBIU0  */
174    /*                  */
175
176    /* COUNT MBIU PORTS */
177
178    MSR_READ(MBIU_CAP, RC_MB0_MBIU0, &(msrValue.high), &(msrValue.low));
179    mbiu_port_count = GET_NUM_PORTS(msrValue.high, msrValue.low);
180
181    /* FIND REFLECTIVE PORT */
182    /* Query the MBIU for the port through which we are communicating. */
183    /* We will avoid accesses to this port to avoid a self-reference.  */
184
185    MSR_READ(MBIU_WHOAMI, RC_MB0_MBIU0, &(msrValue.high), &(msrValue.low));
186    reflective = msrValue.low & WHOAMI_MASK;
187
188    /* ENUMERATE ALL PORTS */
189    /* For every possible port, set the MBIU.deviceId to something. */
190
191    for (port = 0; port < 8; port++) {
192        /* FILL IN CLAIMED FIELD */
193        /* All MBIU ports can only be assigned to one device from the */
194        /* Durango table                                              */
195
196        MBIU0[port].claimed = 0;
197
198        /* MBIU0 PORT NUMBERS ARE IN ADDRESS BITS 31:29 */
199
200        MBIU0[port].address = port << 29;
201
202        /* SPECIAL CASE FOR MBIU0 */
203        /* MBIU0 port 0 is a special case, as it points back to MBIU0.  MBIU0
204         * responds at address 0x40000xxx, which does not equal 0 << 29.
205         * */
206
207        if (port == 0)
208            MBIU0[port].deviceId = RC_CC_MBIU;
209        else if (port == reflective)
210            MBIU0[port].deviceId = REFLECTIVE;
211        else if (port > mbiu_port_count)
212            MBIU0[port].deviceId = NOT_POPULATED;
213        else {
214            MSR_READ(MBD_MSR_CAP, MBIU0[port].address, &(msrValue.high),
215                &(msrValue.low));
216            MBIU0[port].deviceId = GET_DEVICE_ID(msrValue.high, msrValue.low);
217        }
218    }
219
220    /*                  */
221    /* ENUMERATE MBIU1  */
222    /*                  */
223
224    /* COUNT MBIU PORTS */
225
226    MSR_READ(MBIU_CAP, RC_MB0_MBIU1, &(msrValue.high), &(msrValue.low));
227    mbiu_port_count = GET_NUM_PORTS(msrValue.high, msrValue.low);
228
229    /* FIND REFLECTIVE PORT */
230    /* Query the MBIU for the port through which we are communicating. */
231    /* We will avoid accesses to this port to avoid a self-reference.  */
232
233    MSR_READ(MBIU_WHOAMI, RC_MB0_MBIU1, &(msrValue.high), &(msrValue.low));
234    reflective = msrValue.low & WHOAMI_MASK;
235
236    /* ENUMERATE ALL PORTS */
237    /* For every possible port, set the MBIU.deviceId to something. */
238
239    for (port = 0; port < 8; port++) {
240        /* FILL IN CLAIMED FIELD */
241        /* All MBIU ports can only be assigned to one device from the */
242        /* Durango table                                              */
243
244        MBIU1[port].claimed = 0;
245
246        /* MBIU1 PORT NUMBERS ARE IN 28:26 AND 31:29 = 010B */
247
248        MBIU1[port].address = (0x02l << 29) + (port << 26);
249
250        if (port == reflective)
251            MBIU1[port].deviceId = REFLECTIVE;
252        else if (port > mbiu_port_count)
253            MBIU1[port].deviceId = NOT_POPULATED;
254        else {
255            MSR_READ(MBD_MSR_CAP, MBIU1[port].address, &(msrValue.high),
256                &(msrValue.low));
257            MBIU1[port].deviceId = GET_DEVICE_ID(msrValue.high, msrValue.low);
258        }
259    }
260
261    /*                          */
262    /* ENUMERATE MBIU2 (CS5535) */
263    /*  (if present)            */
264
265    MSR_READ(MBD_MSR_CAP, CP_MB0_MBIU0, &(msrValue.high), &(msrValue.low));
266    if (GET_DEVICE_ID(msrValue.high, msrValue.low) == CP_CC_MBIU) {
267        /* COUNT MBIU PORTS */
268
269        MSR_READ(MBIU_CAP, CP_MB0_MBIU0, &(msrValue.high), &(msrValue.low));
270        mbiu_port_count = GET_NUM_PORTS(msrValue.high, msrValue.low);
271
272        /* FIND REFLECTIVE PORT */
273        /* Query the MBIU for the port through which we are communicating. */
274        /* We will avoid accesses to this port to avoid a self-reference.  */
275
276        MSR_READ(MBIU_WHOAMI, CP_MB0_MBIU0, &(msrValue.high),
277            &(msrValue.low));
278        reflective = msrValue.low & WHOAMI_MASK;
279
280        /* ENUMERATE ALL PORTS */
281        /* For every possible port, set the MBIU.deviceId to something. */
282
283        for (port = 0; port < 8; port++) {
284            /* FILL IN CLAIMED FIELD */
285            /* All MBIU ports can only be assigned to one device from the */
286            /* Durango table                                              */
287
288            MBIU2[port].claimed = 0;
289
290            /* MBIU2 PORT NUMBERS ARE IN 22:20 AND 31:23 = 010100010B */
291
292            MBIU2[port].address =
293                (0x02l << 29) + (0x04l << 26) + (0x02l << 23) + (port << 20);
294
295            if (port == reflective)
296                MBIU2[port].deviceId = REFLECTIVE;
297            else if (port > mbiu_port_count)
298                MBIU2[port].deviceId = NOT_POPULATED;
299            else {
300                MSR_READ(MBD_MSR_CAP, MBIU2[port].address, &(msrValue.high),
301                    &(msrValue.low));
302                MBIU2[port].deviceId =
303                    GET_DEVICE_ID(msrValue.high, msrValue.low);
304            }
305        }
306    } else {
307        /* NO 5535                                                  */
308        /* If the CS5535 is not installed, fill in the cached table */
309        /* with the 'NOT_INSTALLED' flag.  Also, fill in the device */
310        /* status from NOT_KNOWN to REQ_NOT_INSTALLED.              */
311
312        for (port = 0; port < 8; port++) {
313            MBIU2[port].claimed = 0;
314            MBIU2[port].deviceId = NOT_INSTALLED;
315            MBIU2[port].address =
316                (0x02l << 29) + (0x04l << 26) + (0x02l << 23) + (port << 20);
317        }
318        for (port = CP_INDEX_START; port <= CP_INDEX_STOP; port++) {
319            msrDev[port].Present = REQ_NOT_INSTALLED;
320        }
321    }
322}
323
324/*------------------------------------------------------------------
325 * redcloud_init_msr_devices (PRIVATE ROUTINE - NOT PART OF DURANGO API)
326 *
327 * Handles the details of finding each possible device on the MBUS.
328 * If a given device is not found, its structure is left uninitialized.
329 * If a given device is found, its structure is updated.
330 *
331 * This init routine only checks for devices in aDev[].
332 *
333 *  Passed:
334 *		aDev - is a pointer to the array of MBUS devices.
335 *		arraySize - number of elements in aDev.
336 *
337 *	Returns:
338 *		1 - If, for every device, its address was found.
339 *		0 - If, for any device, an error was encountered.
340 *------------------------------------------------------------------
341 */
342int
343redcloud_init_msr_devices(MSR aDev[], unsigned int array_size)
344{
345    unsigned int i, issues = 0;
346
347    /* TRY TO FIND EACH ITEM IN THE ARRAY */
348
349    for (i = 0; i < array_size; i++) {
350        /* IGNORE DEVICES THAT ARE ALREADY FOUND                */
351        /* The addresses for "found" devices are already known. */
352
353        if (aDev[i].Present == FOUND || aDev[i].Present == REQ_NOT_INSTALLED)
354            continue;
355
356        /* TRY TO FIND THE DEVICE ON THE MBUS */
357
358        aDev[i].Present = redcloud_find_msr_device(&aDev[i]);
359
360        /* INCREMENT ERROR COUNT IF DEVICE NOT FOUND */
361
362        if (aDev[i].Present != FOUND)
363            issues++;
364    }
365
366    return (issues == 0);
367}
368
369/*------------------------------------------------------------------
370 *  redcloud_find_msr_device (PRIVATE ROUTINE - NOT PART OF DURANGO API)
371 *
372 *  Passed:
373 *	    pDev - is a pointer to one element in the array of MBUS devices
374 *
375 *  Returns:
376 *	    FOUND - Device was found and pDev->Address has been updated.
377 *
378 *		REQ_NOT_FOUND - Device was not found and pDev->Address has not
379 *						been updated.
380 *
381 *------------------------------------------------------------------
382 */
383DEV_STATUS
384redcloud_find_msr_device(MSR * pDev)
385{
386    unsigned int i;
387
388    /* SEARCH DURANGO'S CACHED MBUS TOPOLOGY */
389    /* This gets a little tricky.  As the only identifier we have for each
390     * device is the device ID and we have multiple devices of the same type
391     * MCP, MPCI, USB, etc. we need to make some assumptions based on table
392     * order.  These are as follows:
393     * 1. All Redcloud nodes are searched first, as we assume that they
394     *    are first in the table.
395     * 2. If two devices have the same device ID and are found on the same
396     *    device (GX2, CS5535, etc.) we assume that they are listed such
397     *    that the first device in the table with this device ID has a lower
398     *    port address.
399     * 3. After a device ID has been matched, the port is marked as
400     *    'claimed', such that future enumerations continue searching the
401     *    GeodeLink topology.
402     */
403
404    /* SEARCH MBIU0 */
405
406    for (i = 0; i < 8; i++) {
407        if (MBIU0[i].deviceId == pDev->Id && !(MBIU0[i].claimed)) {
408            MBIU0[i].claimed = 1;
409            pDev->Address = MBIU0[i].address;
410            return FOUND;
411        }
412    }
413
414    /* SEARCH MBIU1 */
415
416    for (i = 0; i < 8; i++) {
417        if (MBIU1[i].deviceId == pDev->Id && !(MBIU1[i].claimed)) {
418            MBIU1[i].claimed = 1;
419            pDev->Address = MBIU1[i].address;
420            return FOUND;
421        }
422    }
423
424    /* SEARCH MBIU2 */
425
426    for (i = 0; i < 8; i++) {
427        if (MBIU2[i].deviceId == pDev->Id && !(MBIU2[i].claimed)) {
428            MBIU2[i].claimed = 1;
429            pDev->Address = MBIU2[i].address;
430            return FOUND;
431        }
432    }
433
434    return REQ_NOT_FOUND;
435}
436
437/*--------------------------------------------------------------------
438 * gfx_id_msr_device
439 *
440 *	This routine handles reading the capabilities MSR register (typically
441 *	0x2000) and checking if the 'id' field matchs  pDev.Id.  This routine is
442 *  used by applications/drivers that need to extend the list of known
443 *  MBUS devices beyond those known by Durango.
444 *
445 *		Passed:
446 *			pDev - Pointer to MSR structure containing the device's ID.
447 *		    address - device address.
448 *
449 *		Returns:
450 *			FOUND - The IDs do match.
451 *			REQ_NOT_FOUND - There was not a match.
452 *
453 *--------------------------------------------------------------------
454 */
455#if GFX_MSR_DYNAMIC
456DEV_STATUS
457redcloud_id_msr_device(MSR * pDev, unsigned long address)
458#else
459DEV_STATUS
460gfx_id_msr_device(MSR * pDev, unsigned long address)
461#endif
462{
463    Q_WORD msrValue;
464
465    MSR_READ(MBD_MSR_CAP, address, &(msrValue.high), &(msrValue.low));
466
467    if (GET_DEVICE_ID(msrValue.high, msrValue.low) == pDev->Id)
468        return FOUND;
469    else
470        return REQ_NOT_FOUND;
471}
472
473/*--------------------------------------------------------------------
474 * gfx_get_msr_dev_address
475 *
476 * This function returns the 32-bit address of the requested device.
477 * The device must be a known MBUS device.  (It must be in Durango's
478 * device table.)  DEV_STATUS should be checked to verify that the address
479 * was updated.
480 *
481 *
482 * Passed:
483 *     device - device index of the device in question.
484 *	   *address - ptr to location where address should be stored.
485 *
486 * Returns:
487 *	   DEV_STATUS of device in question.  (NOT_KNOWN if device is out of range.)
488 *     *address - updated if 'device' is within range
489 *
490 *	Notes:
491 *     This function should only be called after gfx_msr_init
492 *
493 *--------------------------------------------------------------------
494 */
495#if GFX_MSR_DYNAMIC
496DEV_STATUS
497redcloud_get_msr_dev_address(unsigned int device, unsigned long *address)
498#else
499DEV_STATUS
500gfx_get_msr_dev_address(unsigned int device, unsigned long *address)
501#endif
502{
503    if (device < NUM_DEVS) {
504        if (msrDev[device].Present == FOUND)
505            *address = msrDev[device].Address;
506
507        return msrDev[device].Present;
508    }
509    return NOT_KNOWN;
510
511}
512
513/*--------------------------------------------------------------------
514 *  gfx_get_glink_id_at_address
515 *
516 *	This function returns the 16-bit deviceId at the requested address.
517 *  DEV_STATUS should be checked to make sure that device was updated.
518 *
519 *	Passed:
520 *	    device - ptr to location where device ID should be stored.
521 *		address - address of desired device ID.
522 *
523 *  Returns:
524 *	    FOUND if address is a valid address, NOT_KNOWN if address cannot be
525 *	    found on the mbus.
526 *      *device - updated with device Id info.
527 *
528 *	Notes:
529 *      This function should be called after gfx_msr_init
530 *
531 *--------------------------------------------------------------------
532 */
533#if GFX_MSR_DYNAMIC
534DEV_STATUS
535redcloud_get_glink_id_at_address(unsigned int *device, unsigned long address)
536#else
537DEV_STATUS
538gfx_get_glink_id_at_address(unsigned int *device, unsigned long address)
539#endif
540{
541    int port;
542
543    for (port = 0; port < 8; port++) {
544        if (MBIU0[port].address == address) {
545            *device = MBIU0[port].deviceId;
546            return FOUND;
547        } else if (MBIU1[port].address == address) {
548            *device = MBIU1[port].deviceId;
549            return FOUND;
550        } else if (MBIU2[port].address == address) {
551            *device = MBIU2[port].deviceId;
552            return FOUND;
553        }
554    }
555
556    return NOT_KNOWN;
557
558}
559
560/*--------------------------------------------------------------------
561 * gfx_msr_read
562 *
563 * Performs a 64-bit read from 'msrRegister' in device 'device'.  'device' is
564 * an index into Durango's table of known MBUS devices.
565 *
566 * Returns:
567 *     FOUND			- if no errors were detected and msrValue has been
568 *     						updated.
569 *	   NOT_KNOWN		- an error was detected.  msrValue is not updated.
570 *	   REQ_NOT_FOUND 	- 'msrAddress' for 'devID' is unknown.  Caller
571 *							should call msrInit() first.  msrValue is not
572 *							updated.
573 * Notes:
574 *	 This function should be called after gfx_msr_init
575 *--------------------------------------------------------------------
576 */
577#if GFX_MSR_DYNAMIC
578DEV_STATUS
579redcloud_msr_read(unsigned int device, unsigned int msrRegister,
580    Q_WORD * msrValue)
581#else
582DEV_STATUS
583gfx_msr_read(unsigned int device, unsigned int msrRegister, Q_WORD * msrValue)
584#endif
585{
586    if (device < NUM_DEVS) {
587        if (msrDev[device].Present == FOUND)
588            MSR_READ(msrRegister, msrDev[device].Address, &(msrValue->high),
589                &(msrValue->low));
590
591        return msrDev[device].Present;
592    }
593    return NOT_KNOWN;
594}
595
596/*--------------------------------------------------------------------
597 * gfx_msr_write
598 *
599 *		Performs a 64-bit write to 'msrRegister' in device 'devID'.
600 *
601 * Returns:
602 *		FOUND 			- if no errors were detected and msrValue has been
603 *							updated.
604 *		NOT_KNOWN		- an error was detected.  msrValue is not updated.
605 *		REQ_NOT_FOUND 	- 'msrAddress' for 'devID' is unknown.  Caller
606 *							should call msrInit() first.  msrValue is not
607 *							updated.
608 *
609 *Notes:
610 *     	This function is valid to call after initMSR_API()
611 *
612 *--------------------------------------------------------------------
613 */
614#if GFX_MSR_DYNAMIC
615DEV_STATUS
616redcloud_msr_write(unsigned int device, unsigned int msrRegister,
617    Q_WORD * msrValue)
618#else
619DEV_STATUS
620gfx_msr_write(unsigned int device, unsigned int msrRegister,
621    Q_WORD * msrValue)
622#endif
623{
624    if (device < NUM_DEVS) {
625        if (msrDev[device].Present == FOUND)
626            MSR_WRITE(msrRegister, msrDev[device].Address, &(msrValue->high),
627                &(msrValue->low));
628
629        return msrDev[device].Present;
630    }
631
632    return NOT_KNOWN;
633}
634