OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [arch/] [sparc64/] [kernel/] [chmc.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1275 phoenix
/* $Id: chmc.c,v 1.1.1.1 2004-04-15 01:34:32 phoenix Exp $
2
 * memctrlr.c: Driver for UltraSPARC-III memory controller.
3
 *
4
 * Copyright (C) 2001 David S. Miller (davem@redhat.com)
5
 */
6
 
7
#include <linux/module.h>
8
#include <linux/kernel.h>
9
#include <linux/types.h>
10
#include <linux/slab.h>
11
#include <linux/list.h>
12
#include <linux/init.h>
13
#include <asm/spitfire.h>
14
#include <asm/chmctrl.h>
15
#include <asm/oplib.h>
16
#include <asm/io.h>
17
 
18
#define CHMCTRL_NDGRPS  2
19
#define CHMCTRL_NDIMMS  4
20
 
21
#define DIMMS_PER_MC    (CHMCTRL_NDGRPS * CHMCTRL_NDIMMS)
22
 
23
/* OBP memory-layout property format. */
24
struct obp_map {
25
        unsigned char   dimm_map[144];
26
        unsigned char   pin_map[576];
27
};
28
 
29
#define DIMM_LABEL_SZ   8
30
 
31
struct obp_mem_layout {
32
        /* One max 8-byte string label per DIMM.  Usually
33
         * this matches the label on the motherboard where
34
         * that DIMM resides.
35
         */
36
        char            dimm_labels[DIMMS_PER_MC][DIMM_LABEL_SZ];
37
 
38
        /* If symmetric use map[0], else it is
39
         * asymmetric and map[1] should be used.
40
         */
41
        char            symmetric;
42
 
43
        struct obp_map  map[2];
44
};
45
 
46
#define CHMCTRL_NBANKS  4
47
 
48
struct bank_info {
49
        struct mctrl_info       *mp;
50
        int                     bank_id;
51
 
52
        u64                     raw_reg;
53
        int                     valid;
54
        int                     uk;
55
        int                     um;
56
        int                     lk;
57
        int                     lm;
58
        int                     interleave;
59
        unsigned long           base;
60
        unsigned long           size;
61
};
62
 
63
struct mctrl_info {
64
        struct list_head        list;
65
        int                     portid;
66
        int                     index;
67
 
68
        struct obp_mem_layout   layout_prop;
69
        int                     layout_size;
70
 
71
        void                    *regs;
72
 
73
        u64                     timing_control1;
74
        u64                     timing_control2;
75
        u64                     timing_control3;
76
        u64                     timing_control4;
77
        u64                     memaddr_control;
78
 
79
        struct bank_info        logical_banks[CHMCTRL_NBANKS];
80
};
81
 
82
static LIST_HEAD(mctrl_list);
83
 
84
/* Does BANK decode PHYS_ADDR? */
85
static int bank_match(struct bank_info *bp, unsigned long phys_addr)
86
{
87
        unsigned long upper_bits = (phys_addr & PA_UPPER_BITS) >> PA_UPPER_BITS_SHIFT;
88
        unsigned long lower_bits = (phys_addr & PA_LOWER_BITS) >> PA_LOWER_BITS_SHIFT;
89
 
90
        /* Bank must be enabled to match. */
91
        if (bp->valid == 0)
92
                return 0;
93
 
94
        /* Would BANK match upper bits? */
95
        upper_bits ^= bp->um;           /* What bits are different? */
96
        upper_bits  = ~upper_bits;      /* Invert. */
97
        upper_bits |= bp->uk;           /* What bits don't matter for matching? */
98
        upper_bits  = ~upper_bits;      /* Invert. */
99
 
100
        if (upper_bits)
101
                return 0;
102
 
103
        /* Would BANK match lower bits? */
104
        lower_bits ^= bp->lm;           /* What bits are different? */
105
        lower_bits  = ~lower_bits;      /* Invert. */
106
        lower_bits |= bp->lk;           /* What bits don't matter for matching? */
107
        lower_bits  = ~lower_bits;      /* Invert. */
108
 
109
        if (lower_bits)
110
                return 0;
111
 
112
        /* I always knew you'd be the one. */
113
        return 1;
114
}
115
 
116
/* Given PHYS_ADDR, search memory controller banks for a match. */
117
static struct bank_info *find_bank(unsigned long phys_addr)
118
{
119
        struct list_head *mctrl_head = &mctrl_list;
120
        struct list_head *mctrl_entry = mctrl_head->next;
121
 
122
        for (;;) {
123
                struct mctrl_info *mp =
124
                        list_entry(mctrl_entry, struct mctrl_info, list);
125
                int bank_no;
126
 
127
                if (mctrl_entry == mctrl_head)
128
                        break;
129
                mctrl_entry = mctrl_entry->next;
130
 
131
                for (bank_no = 0; bank_no < CHMCTRL_NBANKS; bank_no++) {
132
                        struct bank_info *bp;
133
 
134
                        bp = &mp->logical_banks[bank_no];
135
                        if (bank_match(bp, phys_addr))
136
                                return bp;
137
                }
138
        }
139
 
140
        return NULL;
141
}
142
 
143
/* This is the main purpose of this driver. */
144
#define SYNDROME_MIN    -1
145
#define SYNDROME_MAX    144
146
int chmc_getunumber(int syndrome_code,
147
                    unsigned long phys_addr,
148
                    char *buf, int buflen)
149
{
150
        struct bank_info *bp;
151
        struct obp_mem_layout *prop;
152
        int bank_in_controller, first_dimm;
153
 
154
        bp = find_bank(phys_addr);
155
        if (bp == NULL ||
156
            syndrome_code < SYNDROME_MIN ||
157
            syndrome_code > SYNDROME_MAX) {
158
                buf[0] = '?';
159
                buf[1] = '?';
160
                buf[2] = '?';
161
                buf[3] = '\0';
162
                return 0;
163
        }
164
 
165
        prop = &bp->mp->layout_prop;
166
        bank_in_controller = bp->bank_id & (CHMCTRL_NBANKS - 1);
167
        first_dimm  = (bank_in_controller & (CHMCTRL_NDGRPS - 1));
168
        first_dimm *= CHMCTRL_NDIMMS;
169
 
170
        if (syndrome_code != SYNDROME_MIN) {
171
                struct obp_map *map;
172
                int qword, where_in_line, where, map_index, map_offset;
173
                unsigned int map_val;
174
 
175
                /* Yaay, single bit error so we can figure out
176
                 * the exact dimm.
177
                 */
178
                if (prop->symmetric)
179
                        map = &prop->map[0];
180
                else
181
                        map = &prop->map[1];
182
 
183
                /* Covert syndrome code into the way the bits are
184
                 * positioned on the bus.
185
                 */
186
                if (syndrome_code < 144 - 16)
187
                        syndrome_code += 16;
188
                else if (syndrome_code < 144)
189
                        syndrome_code -= (144 - 7);
190
                else if (syndrome_code < (144 + 3))
191
                        syndrome_code -= (144 + 3 - 4);
192
                else
193
                        syndrome_code -= 144 + 3;
194
 
195
                /* All this magic has to do with how a cache line
196
                 * comes over the wire on Safari.  A 64-bit line
197
                 * comes over in 4 quadword cycles, each of which
198
                 * transmit ECC/MTAG info as well as the actual
199
                 * data.  144 bits per quadword, 576 total.
200
                 */
201
#define LINE_SIZE       64
202
#define LINE_ADDR_MSK   (LINE_SIZE - 1)
203
#define QW_PER_LINE     4
204
#define QW_BYTES        (LINE_SIZE / QW_PER_LINE)
205
#define QW_BITS         144
206
#define LAST_BIT        (576 - 1)
207
 
208
                qword = (phys_addr & LINE_ADDR_MSK) / QW_BYTES;
209
                where_in_line = ((3 - qword) * QW_BITS) + syndrome_code;
210
                where = (LAST_BIT - where_in_line);
211
                map_index = where >> 2;
212
                map_offset = where & 0x3;
213
                map_val = map->dimm_map[map_index];
214
                map_val = ((map_val >> ((3 - map_offset) << 1)) & (2 - 1));
215
 
216
                sprintf(buf, "%s, pin %3d",
217
                        prop->dimm_labels[first_dimm + map_val],
218
                        map->pin_map[where_in_line]);
219
        } else {
220
                int dimm;
221
 
222
                /* Multi-bit error, we just dump out all the
223
                 * dimm labels assosciated with this bank.
224
                 */
225
                for (dimm = 0; dimm < CHMCTRL_NDIMMS; dimm++) {
226
                        sprintf(buf, "%s ",
227
                                prop->dimm_labels[first_dimm + dimm]);
228
                        buf += strlen(buf);
229
                }
230
        }
231
        return 0;
232
}
233
 
234
/* Accessing the registers is slightly complicated.  If you want
235
 * to get at the memory controller which is on the same processor
236
 * the code is executing, you must use special ASI load/store else
237
 * you go through the global mapping.
238
 */
239
static u64 read_mcreg(struct mctrl_info *mp, unsigned long offset)
240
{
241
        unsigned long ret;
242
 
243
        if (mp->portid == smp_processor_id()) {
244
                __asm__ __volatile__("ldxa      [%1] %2, %0"
245
                                     : "=r" (ret)
246
                                     : "r" (offset), "i" (ASI_MCU_CTRL_REG));
247
        } else {
248
                __asm__ __volatile__("ldxa      [%1] %2, %0"
249
                                     : "=r" (ret)
250
                                     : "r" (mp->regs + offset),
251
                                       "i" (ASI_PHYS_BYPASS_EC_E));
252
        }
253
        return ret;
254
}
255
 
256
#if 0 /* currently unused */
257
static void write_mcreg(struct mctrl_info *mp, unsigned long offset, u64 val)
258
{
259
        if (mp->portid == smp_processor_id()) {
260
                __asm__ __volatile__("stxa      %0, [%1] %2"
261
                                     : : "r" (val),
262
                                         "r" (offset), "i" (ASI_MCU_CTRL_REG));
263
        } else {
264
                __asm__ __volatile__("ldxa      %0, [%1] %2"
265
                                     : : "r" (val),
266
                                         "r" (mp->regs + offset),
267
                                         "i" (ASI_PHYS_BYPASS_EC_E));
268
        }
269
}
270
#endif
271
 
272
static void interpret_one_decode_reg(struct mctrl_info *mp, int which_bank, u64 val)
273
{
274
        struct bank_info *p = &mp->logical_banks[which_bank];
275
 
276
        p->mp = mp;
277
        p->bank_id = (CHMCTRL_NBANKS * mp->portid) + which_bank;
278
        p->raw_reg = val;
279
        p->valid = (val & MEM_DECODE_VALID) >> MEM_DECODE_VALID_SHIFT;
280
        p->uk = (val & MEM_DECODE_UK) >> MEM_DECODE_UK_SHIFT;
281
        p->um = (val & MEM_DECODE_UM) >> MEM_DECODE_UM_SHIFT;
282
        p->lk = (val & MEM_DECODE_LK) >> MEM_DECODE_LK_SHIFT;
283
        p->lm = (val & MEM_DECODE_LM) >> MEM_DECODE_LM_SHIFT;
284
 
285
        p->base  =  (p->um);
286
        p->base &= ~(p->uk);
287
        p->base <<= PA_UPPER_BITS_SHIFT;
288
 
289
        switch(p->lk) {
290
        case 0xf:
291
        default:
292
                p->interleave = 1;
293
                break;
294
 
295
        case 0xe:
296
                p->interleave = 2;
297
                break;
298
 
299
        case 0xc:
300
                p->interleave = 4;
301
                break;
302
 
303
        case 0x8:
304
                p->interleave = 8;
305
                break;
306
 
307
        case 0x0:
308
                p->interleave = 16;
309
                break;
310
        };
311
 
312
        /* UK[10] is reserved, and UK[11] is not set for the SDRAM
313
         * bank size definition.
314
         */
315
        p->size = (((unsigned long)p->uk &
316
                    ((1UL << 10UL) - 1UL)) + 1UL) << PA_UPPER_BITS_SHIFT;
317
        p->size /= p->interleave;
318
}
319
 
320
static void fetch_decode_regs(struct mctrl_info *mp)
321
{
322
        if (mp->layout_size == 0)
323
                return;
324
 
325
        interpret_one_decode_reg(mp, 0,
326
                                 read_mcreg(mp, CHMCTRL_DECODE1));
327
        interpret_one_decode_reg(mp, 1,
328
                                 read_mcreg(mp, CHMCTRL_DECODE2));
329
        interpret_one_decode_reg(mp, 2,
330
                                 read_mcreg(mp, CHMCTRL_DECODE3));
331
        interpret_one_decode_reg(mp, 3,
332
                                 read_mcreg(mp, CHMCTRL_DECODE4));
333
}
334
 
335
static int init_one_mctrl(int node, int index)
336
{
337
        struct mctrl_info *mp = kmalloc(sizeof(*mp), GFP_KERNEL);
338
        int portid = prom_getintdefault(node, "portid", -1);
339
        struct linux_prom64_registers p_reg_prop;
340
        int t;
341
 
342
        if (!mp)
343
                return -1;
344
        memset(mp, 0, sizeof(*mp));
345
        if (portid == -1)
346
                goto fail;
347
 
348
        mp->portid = portid;
349
        mp->layout_size = prom_getproplen(node, "memory-layout");
350
        if (mp->layout_size < 0)
351
                mp->layout_size = 0;
352
        if (mp->layout_size > sizeof(mp->layout_prop))
353
                goto fail;
354
 
355
        if (mp->layout_size > 0)
356
                prom_getproperty(node, "memory-layout",
357
                                 (char *) &mp->layout_prop,
358
                                 mp->layout_size);
359
 
360
        t = prom_getproperty(node, "reg",
361
                             (char *) &p_reg_prop,
362
                             sizeof(p_reg_prop));
363
        if (t < 0 || p_reg_prop.reg_size != 0x48)
364
                goto fail;
365
 
366
        mp->regs = ioremap(p_reg_prop.phys_addr, p_reg_prop.reg_size);
367
        if (mp->regs == NULL)
368
                goto fail;
369
 
370
        if (mp->layout_size != 0UL) {
371
                mp->timing_control1 = read_mcreg(mp, CHMCTRL_TCTRL1);
372
                mp->timing_control2 = read_mcreg(mp, CHMCTRL_TCTRL2);
373
                mp->timing_control3 = read_mcreg(mp, CHMCTRL_TCTRL3);
374
                mp->timing_control4 = read_mcreg(mp, CHMCTRL_TCTRL4);
375
                mp->memaddr_control = read_mcreg(mp, CHMCTRL_MACTRL);
376
        }
377
 
378
        fetch_decode_regs(mp);
379
 
380
        mp->index = index;
381
 
382
        list_add(&mp->list, &mctrl_list);
383
 
384
        /* Report the device. */
385
        printk(KERN_INFO "chmc%d: US3 memory controller at %p [%s]\n",
386
               mp->index,
387
               mp->regs, (mp->layout_size ? "ACTIVE" : "INACTIVE"));
388
 
389
        return 0;
390
 
391
fail:
392
        if (mp) {
393
                if (mp->regs != NULL)
394
                        iounmap(mp->regs);
395
                kfree(mp);
396
        }
397
        return -1;
398
}
399
 
400
static int __init probe_for_string(char *name, int index)
401
{
402
        int node = prom_getchild(prom_root_node);
403
 
404
        while ((node = prom_searchsiblings(node, name)) != 0) {
405
                int ret = init_one_mctrl(node, index);
406
 
407
                if (!ret)
408
                        index++;
409
 
410
                node = prom_getsibling(node);
411
                if (!node)
412
                        break;
413
        }
414
 
415
        return index;
416
}
417
 
418
static int __init chmc_init(void)
419
{
420
        int index;
421
 
422
        /* This driver is only for cheetah platforms. */
423
        if (tlb_type != cheetah && tlb_type != cheetah_plus)
424
                return -ENODEV;
425
 
426
        index = probe_for_string("memory-controller", 0);
427
        index = probe_for_string("mc-us3", index);
428
 
429
        return 0;
430
}
431
 
432
static void __exit chmc_cleanup(void)
433
{
434
        struct list_head *head = &mctrl_list;
435
        struct list_head *tmp = head->next;
436
 
437
        for (;;) {
438
                struct mctrl_info *p =
439
                        list_entry(tmp, struct mctrl_info, list);
440
                if (tmp == head)
441
                        break;
442
                tmp = tmp->next;
443
 
444
                list_del(&p->list);
445
                iounmap(p->regs);
446
                kfree(p);
447
        }
448
}
449
 
450
module_init(chmc_init);
451
module_exit(chmc_cleanup);

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.