cim_msr.c revision f29dbc25
1/*
2 * Copyright (c) 2006 Advanced Micro Devices, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 * DEALINGS IN THE SOFTWARE.
21 *
22 * Neither the name of the Advanced Micro Devices, Inc. nor the names of its
23 * contributors may be used to endorse or promote products derived from this
24 * software without specific prior written permission.
25 */
26
27 /*
28  * Cimarron MSR access routines.  These routines allow the user to query the
29  * state of the GeodeLink Bus and read and write model-specfic registers.
30  */
31
32/*--------------------------------------------------------------*/
33/* MSR GLOBALS                                                  */
34/* These variables hold a local copy of the GeodeLink mapping   */
35/* as well as a lookup table for easy device addressing.        */
36/*--------------------------------------------------------------*/
37
38GEODELINK_NODE gliu_nodes[24];
39GEODELINK_NODE msr_dev_lookup[MSR_DEVICE_EMPTY];
40
41#define GET_DEVICE_ID(macrohigh, macrolow) ((macrolow >> 12) & 0xFF)
42
43/*---------------------------------------------------------------------------
44 * msr_init_table
45 *
46 * This routine intializes the internal MSR table in Cimarron.  This table is
47 * used for any MSR device accesses.
48 *--------------------------------------------------------------------------*/
49
50int
51msr_init_table(void)
52{
53    Q_WORD msr_value = {0, 0};
54    unsigned int i, j;
55    int return_value = CIM_STATUS_OK;
56
57    /* CHECK FOR VALID GEODELINK CONFIGURATION
58     * The CPU and the three GLIUs are assumed to be at known static
59     * addresses, so we will check the device IDs at these addresses as proof
60     * of a valid GeodeLink configuration
61     */
62
63    MSR_READ(MSR_GEODELINK_CAP, MSR_ADDRESS_VAIL, &msr_value);
64    if (GET_DEVICE_ID(msr_value.high, msr_value.low) != MSR_CLASS_CODE_VAIL)
65        return_value = CIM_STATUS_ERROR;
66
67    MSR_READ(MSR_GEODELINK_CAP, MSR_ADDRESS_GLIU0, &msr_value);
68    if (GET_DEVICE_ID(msr_value.high, msr_value.low) != MSR_CLASS_CODE_GLIU)
69        return_value = CIM_STATUS_ERROR;
70
71    MSR_READ(MSR_GEODELINK_CAP, MSR_ADDRESS_GLIU1, &msr_value);
72    if (GET_DEVICE_ID(msr_value.high, msr_value.low) != MSR_CLASS_CODE_GLIU)
73        return_value = CIM_STATUS_ERROR;
74
75    MSR_READ(MSR_GEODELINK_CAP, MSR_ADDRESS_GLIU2, &msr_value);
76    if (GET_DEVICE_ID(msr_value.high, msr_value.low) != MSR_CLASS_CODE_GLIU)
77        return_value = CIM_STATUS_ERROR;
78
79    if (return_value == CIM_STATUS_OK) {
80        /* BUILD LOCAL COPY OF THE GEODELINK BUS */
81
82        msr_create_geodelink_table(gliu_nodes);
83
84        /* CLEAR TABLE STATUS */
85
86        for (i = 0; i < MSR_DEVICE_EMPTY; i++)
87            msr_dev_lookup[i].device_id = MSR_DEVICE_NOTFOUND;
88
89        /* CREATE EASY LOOKUP TABLE FOR FUTURE HARDWARE ACCESS    */
90        /* Note that MSR_DEVICE_EMPTY is the index after the last */
91        /* available device.  Also note that we fill in known     */
92        /* devices before filling in the rest of the table.       */
93
94        msr_dev_lookup[MSR_DEVICE_GEODELX_GLIU0].address_from_cpu =
95            MSR_ADDRESS_GLIU0;
96        msr_dev_lookup[MSR_DEVICE_GEODELX_GLIU0].device_id =
97            MSR_DEVICE_PRESENT;
98        msr_dev_lookup[MSR_DEVICE_GEODELX_GLIU1].address_from_cpu =
99            MSR_ADDRESS_GLIU1;
100        msr_dev_lookup[MSR_DEVICE_GEODELX_GLIU1].device_id =
101            MSR_DEVICE_PRESENT;
102        msr_dev_lookup[MSR_DEVICE_5535_GLIU].address_from_cpu =
103            MSR_ADDRESS_GLIU2;
104        msr_dev_lookup[MSR_DEVICE_5535_GLIU].device_id = MSR_DEVICE_PRESENT;
105        msr_dev_lookup[MSR_DEVICE_GEODELX_VAIL].address_from_cpu =
106            MSR_ADDRESS_VAIL;
107        msr_dev_lookup[MSR_DEVICE_GEODELX_VAIL].device_id =
108            MSR_DEVICE_PRESENT;
109
110        for (i = 0; i < MSR_DEVICE_EMPTY; i++) {
111            if (msr_dev_lookup[i].device_id == MSR_DEVICE_NOTFOUND) {
112                for (j = 0; j < 24; j++) {
113                    if (gliu_nodes[j].device_id == i)
114                        break;
115                }
116
117                if (j == 24)
118                    msr_dev_lookup[i].device_id = MSR_DEVICE_NOTFOUND;
119                else {
120                    msr_dev_lookup[i].device_id = MSR_DEVICE_PRESENT;
121                    msr_dev_lookup[i].address_from_cpu =
122                        gliu_nodes[j].address_from_cpu;
123                }
124            }
125        }
126    } else {
127        /* ERROR OUT THE GEODELINK TABLES */
128
129        for (i = 0; i < 24; i++) {
130            gliu_nodes[i].address_from_cpu = 0xFFFFFFFF;
131            gliu_nodes[i].device_id = MSR_DEVICE_EMPTY;
132        }
133
134        for (i = 0; i < MSR_DEVICE_EMPTY; i++) {
135            msr_dev_lookup[i].address_from_cpu = 0xFFFFFFFF;
136            msr_dev_lookup[i].device_id = MSR_DEVICE_NOTFOUND;
137        }
138    }
139    return return_value;
140}
141
142/*---------------------------------------------------------------------------
143 * msr_create_geodelink_table
144 *
145 * This routine dumps the contents of the GeodeLink bus into an array of
146 * 24 GEODELINK_NODE structures.  Indexes 0-7 represent ports 0-7 of GLIU0,
147 * indexes 8-15 represent ports 0-7 of GLIU1 and indexes 16-23 represent
148 * ports 0-7 of GLIU2 (5535).
149 *--------------------------------------------------------------------------*/
150
151int
152msr_create_geodelink_table(GEODELINK_NODE * gliu_nodes)
153{
154    unsigned long mbiu_port_count, reflective;
155    unsigned long port, index;
156    unsigned long gliu_count = 0;
157    int glcp_count = 0;
158    int usb_count = 0;
159    int mpci_count = 0;
160    Q_WORD msr_value = {0, 0};
161
162    /* ALL THREE GLIUS ARE IN ONE ARRAY                               */
163    /* Entries 0-7 contain the port information for GLIU0, entries    */
164    /* 8-15 contain GLIU1 and 15-23 contain GLIU2.  We perform the    */
165    /* enumeration in two passes.  The first simply fills in the      */
166    /* addresses and class codes at each node.  The second pass       */
167    /* translates the class codes into indexes into Cimarron's device */
168    /* lookup table.                                                  */
169
170    /* COUNT GLIU0 PORTS */
171
172    MSR_READ(MSR_GLIU_CAP, MSR_ADDRESS_GLIU0, &msr_value);
173    mbiu_port_count = (msr_value.high >> NUM_PORTS_SHIFT) & 7;
174
175    /* FIND REFLECTIVE PORT */
176    /* Query the GLIU for the port through which we are communicating. */
177    /* We will avoid accesses to this port to avoid a self-reference.  */
178
179    MSR_READ(MSR_GLIU_WHOAMI, MSR_ADDRESS_GLIU0, &msr_value);
180    reflective = msr_value.low & WHOAMI_MASK;
181
182    /* SPECIAL CASE FOR PORT 0 */
183    /* GLIU0 port 0 is a special case, as it points back to GLIU0.  GLIU0 */
184    /* responds at address 0x10000xxx, which does not equal 0 << 29.      */
185
186    gliu_nodes[0].address_from_cpu = MSR_ADDRESS_GLIU0;
187    gliu_nodes[0].device_id = MSR_CLASS_CODE_GLIU;
188
189    /* ENUMERATE ALL PORTS */
190
191    for (port = 1; port < 8; port++) {
192        /* FILL IN ADDRESS */
193
194        gliu_nodes[port].address_from_cpu = port << 29;
195
196        if (port == reflective)
197            gliu_nodes[port].device_id = MSR_CLASS_CODE_REFLECTIVE;
198        else if (port > mbiu_port_count)
199            gliu_nodes[port].device_id = MSR_CLASS_CODE_UNPOPULATED;
200        else {
201            MSR_READ(MSR_GEODELINK_CAP, gliu_nodes[port].address_from_cpu,
202                &msr_value);
203            gliu_nodes[port].device_id =
204                GET_DEVICE_ID(msr_value.high, msr_value.low);
205        }
206    }
207
208    /* COUNT GLIU1 PORTS */
209
210    MSR_READ(MSR_GLIU_CAP, MSR_ADDRESS_GLIU1, &msr_value);
211    mbiu_port_count = (msr_value.high >> NUM_PORTS_SHIFT) & 7;
212
213    /* FIND REFLECTIVE PORT */
214
215    MSR_READ(MSR_GLIU_WHOAMI, MSR_ADDRESS_GLIU1, &msr_value);
216    reflective = msr_value.low & WHOAMI_MASK;
217
218    /* ENUMERATE ALL PORTS */
219
220    for (port = 0; port < 8; port++) {
221        index = port + 8;
222
223        /* FILL IN ADDRESS */
224
225        gliu_nodes[index].address_from_cpu = (0x02l << 29) + (port << 26);
226
227        if (port == reflective)
228            gliu_nodes[index].device_id = MSR_CLASS_CODE_REFLECTIVE;
229        else if (port > mbiu_port_count)
230            gliu_nodes[index].device_id = MSR_CLASS_CODE_UNPOPULATED;
231        else {
232            MSR_READ(MSR_GEODELINK_CAP, gliu_nodes[index].address_from_cpu,
233                &msr_value);
234            gliu_nodes[index].device_id =
235                GET_DEVICE_ID(msr_value.high, msr_value.low);
236        }
237    }
238
239    /* COUNT GLIU2 PORTS */
240
241    MSR_READ(MSR_GLIU_CAP, MSR_ADDRESS_GLIU2, &msr_value);
242    mbiu_port_count = (msr_value.high >> NUM_PORTS_SHIFT) & 7;
243
244    /* FIND REFLECTIVE PORT */
245
246    MSR_READ(MSR_GLIU_WHOAMI, MSR_ADDRESS_GLIU2, &msr_value);
247    reflective = msr_value.low & WHOAMI_MASK;
248
249    /* FILL IN PORT 0 AND 1 */
250    /* Port 0 on 5535 is MBIU2.  Port 1 is MPCI, but it is referenced at */
251    /* a special address.                                                */
252
253    gliu_nodes[16].address_from_cpu = MSR_ADDRESS_GLIU2;
254    gliu_nodes[16].device_id = MSR_CLASS_CODE_GLIU;
255
256    gliu_nodes[17].address_from_cpu = MSR_ADDRESS_5535MPCI;
257    gliu_nodes[17].device_id = MSR_CLASS_CODE_MPCI;
258
259    /* ENUMERATE ALL PORTS */
260
261    for (port = 2; port < 8; port++) {
262        index = port + 16;
263
264        /* FILL IN ADDRESS */
265
266        gliu_nodes[index].address_from_cpu =
267            (0x02l << 29) + (0x04l << 26) + (0x02l << 23) + (port << 20);
268
269        if (port == reflective)
270            gliu_nodes[index].device_id = MSR_CLASS_CODE_REFLECTIVE;
271        else if (port > mbiu_port_count)
272            gliu_nodes[index].device_id = MSR_CLASS_CODE_UNPOPULATED;
273        else {
274            MSR_READ(MSR_GEODELINK_CAP, gliu_nodes[index].address_from_cpu,
275                &msr_value);
276            gliu_nodes[index].device_id =
277                GET_DEVICE_ID(msr_value.high, msr_value.low);
278        }
279    }
280
281    /* SECOND PASS - TRANSLATION */
282    /* Now that the class codes for each device are stored in the  */
283    /* array, we walk through the array and translate the class    */
284    /* codes to table indexes.  For class codes that have multiple */
285    /* instances, the table indexes are sequential.                */
286
287    for (port = 0; port < 24; port++) {
288        /* SPECIAL CASE FOR GLIU UNITS */
289        /* A GLIU can be both on another port and on its own port.  These  */
290        /* end up as the same address, but are shown as duplicate nodes in */
291        /* the GeodeLink table.                                            */
292
293        if ((port & 7) == 0)
294            gliu_count = port >> 3;
295
296        switch (gliu_nodes[port].device_id) {
297            /* UNPOPULATED OR REFLECTIVE NODES */
298
299        case MSR_CLASS_CODE_UNPOPULATED:
300            index = MSR_DEVICE_EMPTY;
301            break;
302        case MSR_CLASS_CODE_REFLECTIVE:
303            index = MSR_DEVICE_REFLECTIVE;
304            break;
305
306            /* KNOWN CLASS CODES */
307
308        case MSR_CLASS_CODE_GLIU:
309            index = MSR_DEVICE_GEODELX_GLIU0 + gliu_count++;
310            break;
311        case MSR_CLASS_CODE_GLCP:
312            index = MSR_DEVICE_GEODELX_GLCP + glcp_count++;
313            break;
314        case MSR_CLASS_CODE_MPCI:
315            index = MSR_DEVICE_GEODELX_MPCI + mpci_count++;
316            break;
317        case MSR_CLASS_CODE_USB:
318            index = MSR_DEVICE_5535_USB2 + usb_count++;
319            break;
320        case MSR_CLASS_CODE_USB2:
321            index = MSR_DEVICE_5536_USB_2_0;
322            break;
323        case MSR_CLASS_CODE_ATAC:
324            index = MSR_DEVICE_5535_ATAC;
325            break;
326        case MSR_CLASS_CODE_MDD:
327            index = MSR_DEVICE_5535_MDD;
328            break;
329        case MSR_CLASS_CODE_ACC:
330            index = MSR_DEVICE_5535_ACC;
331            break;
332        case MSR_CLASS_CODE_MC:
333            index = MSR_DEVICE_GEODELX_MC;
334            break;
335        case MSR_CLASS_CODE_GP:
336            index = MSR_DEVICE_GEODELX_GP;
337            break;
338        case MSR_CLASS_CODE_VG:
339            index = MSR_DEVICE_GEODELX_VG;
340            break;
341        case MSR_CLASS_CODE_DF:
342            index = MSR_DEVICE_GEODELX_DF;
343            break;
344        case MSR_CLASS_CODE_FG:
345            index = MSR_DEVICE_GEODELX_FG;
346            break;
347        case MSR_CLASS_CODE_VIP:
348            index = MSR_DEVICE_GEODELX_VIP;
349            break;
350        case MSR_CLASS_CODE_AES:
351            index = MSR_DEVICE_GEODELX_AES;
352            break;
353        case MSR_CLASS_CODE_VAIL:
354            index = MSR_DEVICE_GEODELX_VAIL;
355            break;
356        default:
357            index = MSR_DEVICE_EMPTY;
358            break;
359        }
360
361        gliu_nodes[port].device_id = index;
362    }
363
364    return CIM_STATUS_OK;
365}
366
367/*---------------------------------------------------------------------------
368 * msr_create_device_list
369 *
370 * This routine dumps a list of all known GeodeLX/5535 devices as well as their
371 * respective status and address.
372 *--------------------------------------------------------------------------*/
373
374int
375msr_create_device_list(GEODELINK_NODE * gliu_nodes, int max_devices)
376{
377    int i, count;
378
379    if (max_devices < MSR_DEVICE_EMPTY)
380        count = max_devices;
381    else
382        count = MSR_DEVICE_EMPTY;
383
384    for (i = 0; i < count; i++) {
385        gliu_nodes[i].address_from_cpu = msr_dev_lookup[i].address_from_cpu;
386        gliu_nodes[i].device_id = msr_dev_lookup[i].device_id;
387    }
388
389    return CIM_STATUS_OK;
390}
391
392/*--------------------------------------------------------------------
393 * msr_read64
394 *
395 * Performs a 64-bit read from 'msr_register' in device 'device'.  'device' is
396 * an index into Cimarron's table of known GeodeLink devices.
397 *-------------------------------------------------------------------*/
398
399int
400msr_read64(unsigned long device, unsigned long msr_register,
401    Q_WORD * msr_value)
402{
403    if (device < MSR_DEVICE_EMPTY) {
404        if (msr_dev_lookup[device].device_id == MSR_DEVICE_PRESENT) {
405            MSR_READ(msr_register, msr_dev_lookup[device].address_from_cpu,
406                msr_value);
407            return CIM_STATUS_OK;
408        }
409    }
410
411    msr_value->low = msr_value->high = 0;
412    return CIM_STATUS_DEVNOTFOUND;
413}
414
415/*--------------------------------------------------------------------
416 * msr_write64
417 *
418 * Performs a 64-bit write to 'msr_register' in device 'device'.  'device' is
419 * an index into Cimarron's table of known GeodeLink devices.
420 *-------------------------------------------------------------------*/
421
422int
423msr_write64(unsigned long device, unsigned long msr_register,
424    Q_WORD * msr_value)
425{
426    if (device < MSR_DEVICE_EMPTY) {
427        if (msr_dev_lookup[device].device_id == MSR_DEVICE_PRESENT) {
428            MSR_WRITE(msr_register, msr_dev_lookup[device].address_from_cpu,
429                msr_value);
430            return CIM_STATUS_OK;
431        }
432    }
433    return CIM_STATUS_DEVNOTFOUND;
434}
435