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 initializes 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 = MSR_DEVICE_PRESENT;
97        msr_dev_lookup[MSR_DEVICE_GEODELX_GLIU1].address_from_cpu =
98            MSR_ADDRESS_GLIU1;
99        msr_dev_lookup[MSR_DEVICE_GEODELX_GLIU1].device_id = MSR_DEVICE_PRESENT;
100        msr_dev_lookup[MSR_DEVICE_5535_GLIU].address_from_cpu =
101            MSR_ADDRESS_GLIU2;
102        msr_dev_lookup[MSR_DEVICE_5535_GLIU].device_id = MSR_DEVICE_PRESENT;
103        msr_dev_lookup[MSR_DEVICE_GEODELX_VAIL].address_from_cpu =
104            MSR_ADDRESS_VAIL;
105        msr_dev_lookup[MSR_DEVICE_GEODELX_VAIL].device_id = MSR_DEVICE_PRESENT;
106
107        for (i = 0; i < MSR_DEVICE_EMPTY; i++) {
108            if (msr_dev_lookup[i].device_id == MSR_DEVICE_NOTFOUND) {
109                for (j = 0; j < 24; j++) {
110                    if (gliu_nodes[j].device_id == i)
111                        break;
112                }
113
114                if (j == 24)
115                    msr_dev_lookup[i].device_id = MSR_DEVICE_NOTFOUND;
116                else {
117                    msr_dev_lookup[i].device_id = MSR_DEVICE_PRESENT;
118                    msr_dev_lookup[i].address_from_cpu =
119                        gliu_nodes[j].address_from_cpu;
120                }
121            }
122        }
123    }
124    else {
125        /* ERROR OUT THE GEODELINK TABLES */
126
127        for (i = 0; i < 24; i++) {
128            gliu_nodes[i].address_from_cpu = 0xFFFFFFFF;
129            gliu_nodes[i].device_id = MSR_DEVICE_EMPTY;
130        }
131
132        for (i = 0; i < MSR_DEVICE_EMPTY; i++) {
133            msr_dev_lookup[i].address_from_cpu = 0xFFFFFFFF;
134            msr_dev_lookup[i].device_id = MSR_DEVICE_NOTFOUND;
135        }
136    }
137    return return_value;
138}
139
140/*---------------------------------------------------------------------------
141 * msr_create_geodelink_table
142 *
143 * This routine dumps the contents of the GeodeLink bus into an array of
144 * 24 GEODELINK_NODE structures.  Indexes 0-7 represent ports 0-7 of GLIU0,
145 * indexes 8-15 represent ports 0-7 of GLIU1 and indexes 16-23 represent
146 * ports 0-7 of GLIU2 (5535).
147 *--------------------------------------------------------------------------*/
148
149int
150msr_create_geodelink_table(GEODELINK_NODE * gliu_nodes)
151{
152    unsigned long mbiu_port_count, reflective;
153    unsigned long port, index;
154    unsigned long gliu_count = 0;
155    int glcp_count = 0;
156    int usb_count = 0;
157    int mpci_count = 0;
158    Q_WORD msr_value = { 0, 0 };
159
160    /* ALL THREE GLIUS ARE IN ONE ARRAY                               */
161    /* Entries 0-7 contain the port information for GLIU0, entries    */
162    /* 8-15 contain GLIU1 and 15-23 contain GLIU2.  We perform the    */
163    /* enumeration in two passes.  The first simply fills in the      */
164    /* addresses and class codes at each node.  The second pass       */
165    /* translates the class codes into indexes into Cimarron's device */
166    /* lookup table.                                                  */
167
168    /* COUNT GLIU0 PORTS */
169
170    MSR_READ(MSR_GLIU_CAP, MSR_ADDRESS_GLIU0, &msr_value);
171    mbiu_port_count = (msr_value.high >> NUM_PORTS_SHIFT) & 7;
172
173    /* FIND REFLECTIVE PORT */
174    /* Query the GLIU for the port through which we are communicating. */
175    /* We will avoid accesses to this port to avoid a self-reference.  */
176
177    MSR_READ(MSR_GLIU_WHOAMI, MSR_ADDRESS_GLIU0, &msr_value);
178    reflective = msr_value.low & WHOAMI_MASK;
179
180    /* SPECIAL CASE FOR PORT 0 */
181    /* GLIU0 port 0 is a special case, as it points back to GLIU0.  GLIU0 */
182    /* responds at address 0x10000xxx, which does not equal 0 << 29.      */
183
184    gliu_nodes[0].address_from_cpu = MSR_ADDRESS_GLIU0;
185    gliu_nodes[0].device_id = MSR_CLASS_CODE_GLIU;
186
187    /* ENUMERATE ALL PORTS */
188
189    for (port = 1; port < 8; port++) {
190        /* FILL IN ADDRESS */
191
192        gliu_nodes[port].address_from_cpu = port << 29;
193
194        if (port == reflective)
195            gliu_nodes[port].device_id = MSR_CLASS_CODE_REFLECTIVE;
196        else if (port > mbiu_port_count)
197            gliu_nodes[port].device_id = MSR_CLASS_CODE_UNPOPULATED;
198        else {
199            MSR_READ(MSR_GEODELINK_CAP, gliu_nodes[port].address_from_cpu,
200                     &msr_value);
201            gliu_nodes[port].device_id =
202                GET_DEVICE_ID(msr_value.high, msr_value.low);
203        }
204    }
205
206    /* COUNT GLIU1 PORTS */
207
208    MSR_READ(MSR_GLIU_CAP, MSR_ADDRESS_GLIU1, &msr_value);
209    mbiu_port_count = (msr_value.high >> NUM_PORTS_SHIFT) & 7;
210
211    /* FIND REFLECTIVE PORT */
212
213    MSR_READ(MSR_GLIU_WHOAMI, MSR_ADDRESS_GLIU1, &msr_value);
214    reflective = msr_value.low & WHOAMI_MASK;
215
216    /* ENUMERATE ALL PORTS */
217
218    for (port = 0; port < 8; port++) {
219        index = port + 8;
220
221        /* FILL IN ADDRESS */
222
223        gliu_nodes[index].address_from_cpu = (0x02l << 29) + (port << 26);
224
225        if (port == reflective)
226            gliu_nodes[index].device_id = MSR_CLASS_CODE_REFLECTIVE;
227        else if (port > mbiu_port_count)
228            gliu_nodes[index].device_id = MSR_CLASS_CODE_UNPOPULATED;
229        else {
230            MSR_READ(MSR_GEODELINK_CAP, gliu_nodes[index].address_from_cpu,
231                     &msr_value);
232            gliu_nodes[index].device_id =
233                GET_DEVICE_ID(msr_value.high, msr_value.low);
234        }
235    }
236
237    /* COUNT GLIU2 PORTS */
238
239    MSR_READ(MSR_GLIU_CAP, MSR_ADDRESS_GLIU2, &msr_value);
240    mbiu_port_count = (msr_value.high >> NUM_PORTS_SHIFT) & 7;
241
242    /* FIND REFLECTIVE PORT */
243
244    MSR_READ(MSR_GLIU_WHOAMI, MSR_ADDRESS_GLIU2, &msr_value);
245    reflective = msr_value.low & WHOAMI_MASK;
246
247    /* FILL IN PORT 0 AND 1 */
248    /* Port 0 on 5535 is MBIU2.  Port 1 is MPCI, but it is referenced at */
249    /* a special address.                                                */
250
251    gliu_nodes[16].address_from_cpu = MSR_ADDRESS_GLIU2;
252    gliu_nodes[16].device_id = MSR_CLASS_CODE_GLIU;
253
254    gliu_nodes[17].address_from_cpu = MSR_ADDRESS_5535MPCI;
255    gliu_nodes[17].device_id = MSR_CLASS_CODE_MPCI;
256
257    /* ENUMERATE ALL PORTS */
258
259    for (port = 2; port < 8; port++) {
260        index = port + 16;
261
262        /* FILL IN ADDRESS */
263
264        gliu_nodes[index].address_from_cpu =
265            (0x02l << 29) + (0x04l << 26) + (0x02l << 23) + (port << 20);
266
267        if (port == reflective)
268            gliu_nodes[index].device_id = MSR_CLASS_CODE_REFLECTIVE;
269        else if (port > mbiu_port_count)
270            gliu_nodes[index].device_id = MSR_CLASS_CODE_UNPOPULATED;
271        else {
272            MSR_READ(MSR_GEODELINK_CAP, gliu_nodes[index].address_from_cpu,
273                     &msr_value);
274            gliu_nodes[index].device_id =
275                GET_DEVICE_ID(msr_value.high, msr_value.low);
276        }
277    }
278
279    /* SECOND PASS - TRANSLATION */
280    /* Now that the class codes for each device are stored in the  */
281    /* array, we walk through the array and translate the class    */
282    /* codes to table indexes.  For class codes that have multiple */
283    /* instances, the table indexes are sequential.                */
284
285    for (port = 0; port < 24; port++) {
286        /* SPECIAL CASE FOR GLIU UNITS */
287        /* A GLIU can be both on another port and on its own port.  These  */
288        /* end up as the same address, but are shown as duplicate nodes in */
289        /* the GeodeLink table.                                            */
290
291        if ((port & 7) == 0)
292            gliu_count = port >> 3;
293
294        switch (gliu_nodes[port].device_id) {
295            /* UNPOPULATED OR REFLECTIVE NODES */
296
297        case MSR_CLASS_CODE_UNPOPULATED:
298            index = MSR_DEVICE_EMPTY;
299            break;
300        case MSR_CLASS_CODE_REFLECTIVE:
301            index = MSR_DEVICE_REFLECTIVE;
302            break;
303
304            /* KNOWN CLASS CODES */
305
306        case MSR_CLASS_CODE_GLIU:
307            index = MSR_DEVICE_GEODELX_GLIU0 + gliu_count++;
308            break;
309        case MSR_CLASS_CODE_GLCP:
310            index = MSR_DEVICE_GEODELX_GLCP + glcp_count++;
311            break;
312        case MSR_CLASS_CODE_MPCI:
313            index = MSR_DEVICE_GEODELX_MPCI + mpci_count++;
314            break;
315        case MSR_CLASS_CODE_USB:
316            index = MSR_DEVICE_5535_USB2 + usb_count++;
317            break;
318        case MSR_CLASS_CODE_USB2:
319            index = MSR_DEVICE_5536_USB_2_0;
320            break;
321        case MSR_CLASS_CODE_ATAC:
322            index = MSR_DEVICE_5535_ATAC;
323            break;
324        case MSR_CLASS_CODE_MDD:
325            index = MSR_DEVICE_5535_MDD;
326            break;
327        case MSR_CLASS_CODE_ACC:
328            index = MSR_DEVICE_5535_ACC;
329            break;
330        case MSR_CLASS_CODE_MC:
331            index = MSR_DEVICE_GEODELX_MC;
332            break;
333        case MSR_CLASS_CODE_GP:
334            index = MSR_DEVICE_GEODELX_GP;
335            break;
336        case MSR_CLASS_CODE_VG:
337            index = MSR_DEVICE_GEODELX_VG;
338            break;
339        case MSR_CLASS_CODE_DF:
340            index = MSR_DEVICE_GEODELX_DF;
341            break;
342        case MSR_CLASS_CODE_FG:
343            index = MSR_DEVICE_GEODELX_FG;
344            break;
345        case MSR_CLASS_CODE_VIP:
346            index = MSR_DEVICE_GEODELX_VIP;
347            break;
348        case MSR_CLASS_CODE_AES:
349            index = MSR_DEVICE_GEODELX_AES;
350            break;
351        case MSR_CLASS_CODE_VAIL:
352            index = MSR_DEVICE_GEODELX_VAIL;
353            break;
354        default:
355            index = MSR_DEVICE_EMPTY;
356            break;
357        }
358
359        gliu_nodes[port].device_id = index;
360    }
361
362    return CIM_STATUS_OK;
363}
364
365/*---------------------------------------------------------------------------
366 * msr_create_device_list
367 *
368 * This routine dumps a list of all known GeodeLX/5535 devices as well as their
369 * respective status and address.
370 *--------------------------------------------------------------------------*/
371
372int
373msr_create_device_list(GEODELINK_NODE * gliu_nodes, int max_devices)
374{
375    int i, count;
376
377    if (max_devices < MSR_DEVICE_EMPTY)
378        count = max_devices;
379    else
380        count = MSR_DEVICE_EMPTY;
381
382    for (i = 0; i < count; i++) {
383        gliu_nodes[i].address_from_cpu = msr_dev_lookup[i].address_from_cpu;
384        gliu_nodes[i].device_id = msr_dev_lookup[i].device_id;
385    }
386
387    return CIM_STATUS_OK;
388}
389
390/*--------------------------------------------------------------------
391 * msr_read64
392 *
393 * Performs a 64-bit read from 'msr_register' in device 'device'.  'device' is
394 * an index into Cimarron's table of known GeodeLink devices.
395 *-------------------------------------------------------------------*/
396
397int
398msr_read64(unsigned long device, unsigned long msr_register, Q_WORD * msr_value)
399{
400    if (device < MSR_DEVICE_EMPTY) {
401        if (msr_dev_lookup[device].device_id == MSR_DEVICE_PRESENT) {
402            MSR_READ(msr_register, msr_dev_lookup[device].address_from_cpu,
403                     msr_value);
404            return CIM_STATUS_OK;
405        }
406    }
407
408    msr_value->low = msr_value->high = 0;
409    return CIM_STATUS_DEVNOTFOUND;
410}
411
412/*--------------------------------------------------------------------
413 * msr_write64
414 *
415 * Performs a 64-bit write to 'msr_register' in device 'device'.  'device' is
416 * an index into Cimarron's table of known GeodeLink devices.
417 *-------------------------------------------------------------------*/
418
419int
420msr_write64(unsigned long device, unsigned long msr_register,
421            Q_WORD * msr_value)
422{
423    if (device < MSR_DEVICE_EMPTY) {
424        if (msr_dev_lookup[device].device_id == MSR_DEVICE_PRESENT) {
425            MSR_WRITE(msr_register, msr_dev_lookup[device].address_from_cpu,
426                      msr_value);
427            return CIM_STATUS_OK;
428        }
429    }
430    return CIM_STATUS_DEVNOTFOUND;
431}
432