linux-old/arch/sparc64/kernel/chmc.c
<<
>>
Prefs
   1/* $Id: chmc.c,v 1.3 2001/04/03 12:49:47 davem 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. */
  24struct obp_map {
  25        unsigned char   dimm_map[144];
  26        unsigned char   pin_map[576];
  27};
  28
  29#define DIMM_LABEL_SZ   8
  30
  31struct 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
  48struct 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
  63struct 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
  82static LIST_HEAD(mctrl_list);
  83
  84/* Does BANK decode PHYS_ADDR? */
  85static 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. */
 117static 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
 146int 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 */
 239static 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 */
 257static 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
 272static 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
 320static 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
 335static 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
 391fail:
 392        if (mp) {
 393                if (mp->regs != NULL)
 394                        iounmap(mp->regs);
 395                kfree(mp);
 396        }
 397        return -1;
 398}
 399
 400static 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
 418static 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
 432static 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
 450module_init(chmc_init);
 451module_exit(chmc_cleanup);
 452
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.