linux-old/arch/sparc64/kernel/central.c
<<
>>
Prefs
   1/* $Id: central.c,v 1.14.2.2 2002/03/01 01:26:50 davem Exp $
   2 * central.c: Central FHC driver for Sunfire/Starfire/Wildfire.
   3 *
   4 * Copyright (C) 1997, 1999 David S. Miller (davem@redhat.com)
   5 */
   6
   7#include <linux/kernel.h>
   8#include <linux/types.h>
   9#include <linux/string.h>
  10#include <linux/timer.h>
  11#include <linux/sched.h>
  12#include <linux/delay.h>
  13#include <linux/init.h>
  14#include <linux/bootmem.h>
  15
  16#include <asm/page.h>
  17#include <asm/fhc.h>
  18#include <asm/starfire.h>
  19
  20struct linux_central *central_bus = NULL;
  21struct linux_fhc *fhc_list = NULL;
  22
  23#define IS_CENTRAL_FHC(__fhc)   ((__fhc) == central_bus->child)
  24
  25static inline unsigned long long_align(unsigned long addr)
  26{
  27        return ((addr + (sizeof(unsigned long) - 1)) &
  28                ~(sizeof(unsigned long) - 1));
  29}
  30
  31static void central_ranges_init(int cnode, struct linux_central *central)
  32{
  33        int success;
  34        
  35        central->num_central_ranges = 0;
  36        success = prom_getproperty(central->prom_node, "ranges",
  37                                   (char *) central->central_ranges,
  38                                   sizeof (central->central_ranges));
  39        if (success != -1)
  40                central->num_central_ranges = (success/sizeof(struct linux_prom_ranges));
  41}
  42
  43static void fhc_ranges_init(int fnode, struct linux_fhc *fhc)
  44{
  45        int success;
  46        
  47        fhc->num_fhc_ranges = 0;
  48        success = prom_getproperty(fhc->prom_node, "ranges",
  49                                   (char *) fhc->fhc_ranges,
  50                                   sizeof (fhc->fhc_ranges));
  51        if (success != -1)
  52                fhc->num_fhc_ranges = (success/sizeof(struct linux_prom_ranges));
  53}
  54
  55/* Range application routines are exported to various drivers,
  56 * so do not __init this.
  57 */
  58static void adjust_regs(struct linux_prom_registers *regp, int nregs,
  59                        struct linux_prom_ranges *rangep, int nranges)
  60{
  61        int regc, rngc;
  62
  63        for (regc = 0; regc < nregs; regc++) {
  64                for (rngc = 0; rngc < nranges; rngc++)
  65                        if (regp[regc].which_io == rangep[rngc].ot_child_space)
  66                                break; /* Fount it */
  67                if (rngc == nranges) /* oops */
  68                        prom_printf("adjust_regs: Could not find range with matching bus type...\n");
  69                regp[regc].which_io = rangep[rngc].ot_parent_space;
  70                regp[regc].phys_addr -= rangep[rngc].ot_child_base;
  71                regp[regc].phys_addr += rangep[rngc].ot_parent_base;
  72        }
  73}
  74
  75/* Apply probed fhc ranges to registers passed, if no ranges return. */
  76void apply_fhc_ranges(struct linux_fhc *fhc,
  77                      struct linux_prom_registers *regs,
  78                      int nregs)
  79{
  80        if(fhc->num_fhc_ranges)
  81                adjust_regs(regs, nregs, fhc->fhc_ranges,
  82                            fhc->num_fhc_ranges);
  83}
  84
  85/* Apply probed central ranges to registers passed, if no ranges return. */
  86void apply_central_ranges(struct linux_central *central,
  87                          struct linux_prom_registers *regs, int nregs)
  88{
  89        if(central->num_central_ranges)
  90                adjust_regs(regs, nregs, central->central_ranges,
  91                            central->num_central_ranges);
  92}
  93
  94void * __init central_alloc_bootmem(unsigned long size)
  95{
  96        void *ret;
  97
  98        ret = __alloc_bootmem(size, SMP_CACHE_BYTES, 0UL);
  99        if (ret != NULL)
 100                memset(ret, 0, size);
 101
 102        return ret;
 103}
 104
 105static void probe_other_fhcs(void)
 106{
 107        struct linux_prom64_registers fpregs[6];
 108        char namebuf[128];
 109        int node;
 110
 111        node = prom_getchild(prom_root_node);
 112        node = prom_searchsiblings(node, "fhc");
 113        if (node == 0) {
 114                prom_printf("FHC: Cannot find any toplevel firehose controllers.\n");
 115                prom_halt();
 116        }
 117        while(node) {
 118                struct linux_fhc *fhc;
 119                int board;
 120                u32 tmp;
 121
 122                fhc = (struct linux_fhc *)
 123                        central_alloc_bootmem(sizeof(struct linux_fhc));
 124                if (fhc == NULL) {
 125                        prom_printf("probe_other_fhcs: Cannot alloc fhc.\n");
 126                        prom_halt();
 127                }
 128
 129                /* Link it into the FHC chain. */
 130                fhc->next = fhc_list;
 131                fhc_list = fhc;
 132
 133                /* Toplevel FHCs have no parent. */
 134                fhc->parent = NULL;
 135                
 136                fhc->prom_node = node;
 137                prom_getstring(node, "name", namebuf, sizeof(namebuf));
 138                strcpy(fhc->prom_name, namebuf);
 139                fhc_ranges_init(node, fhc);
 140
 141                /* Non-central FHC's have 64-bit OBP format registers. */
 142                if(prom_getproperty(node, "reg",
 143                                    (char *)&fpregs[0], sizeof(fpregs)) == -1) {
 144                        prom_printf("FHC: Fatal error, cannot get fhc regs.\n");
 145                        prom_halt();
 146                }
 147
 148                /* Only central FHC needs special ranges applied. */
 149                fhc->fhc_regs.pregs = fpregs[0].phys_addr;
 150                fhc->fhc_regs.ireg = fpregs[1].phys_addr;
 151                fhc->fhc_regs.ffregs = fpregs[2].phys_addr;
 152                fhc->fhc_regs.sregs = fpregs[3].phys_addr;
 153                fhc->fhc_regs.uregs = fpregs[4].phys_addr;
 154                fhc->fhc_regs.tregs = fpregs[5].phys_addr;
 155
 156                board = prom_getintdefault(node, "board#", -1);
 157                fhc->board = board;
 158
 159                tmp = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_JCTRL);
 160                if((tmp & FHC_JTAG_CTRL_MENAB) != 0)
 161                        fhc->jtag_master = 1;
 162                else
 163                        fhc->jtag_master = 0;
 164
 165                tmp = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_ID);
 166                printk("FHC(board %d): Version[%x] PartID[%x] Manuf[%x] %s\n",
 167                       board,
 168                       (tmp & FHC_ID_VERS) >> 28,
 169                       (tmp & FHC_ID_PARTID) >> 12,
 170                       (tmp & FHC_ID_MANUF) >> 1,
 171                       (fhc->jtag_master ? "(JTAG Master)" : ""));
 172                
 173                /* This bit must be set in all non-central FHC's in
 174                 * the system.  When it is clear, this identifies
 175                 * the central board.
 176                 */
 177                tmp = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_CTRL);
 178                tmp |= FHC_CONTROL_IXIST;
 179                upa_writel(tmp, fhc->fhc_regs.pregs + FHC_PREGS_CTRL);
 180
 181                /* Look for the next FHC. */
 182                node = prom_getsibling(node);
 183                if(node == 0)
 184                        break;
 185                node = prom_searchsiblings(node, "fhc");
 186                if(node == 0)
 187                        break;
 188        }
 189}
 190
 191static void probe_clock_board(struct linux_central *central,
 192                              struct linux_fhc *fhc,
 193                              int cnode, int fnode)
 194{
 195        struct linux_prom_registers cregs[3];
 196        int clknode, nslots, tmp, nregs;
 197
 198        clknode = prom_searchsiblings(prom_getchild(fnode), "clock-board");
 199        if(clknode == 0 || clknode == -1) {
 200                prom_printf("Critical error, central lacks clock-board.\n");
 201                prom_halt();
 202        }
 203        nregs = prom_getproperty(clknode, "reg", (char *)&cregs[0], sizeof(cregs));
 204        if (nregs == -1) {
 205                prom_printf("CENTRAL: Fatal error, cannot map clock-board regs.\n");
 206                prom_halt();
 207        }
 208        nregs /= sizeof(struct linux_prom_registers);
 209        apply_fhc_ranges(fhc, &cregs[0], nregs);
 210        apply_central_ranges(central, &cregs[0], nregs);
 211        central->cfreg = ((((unsigned long)cregs[0].which_io) << 32UL) |
 212                          ((unsigned long)cregs[0].phys_addr));
 213        central->clkregs = ((((unsigned long)cregs[1].which_io) << 32UL) |
 214                            ((unsigned long)cregs[1].phys_addr));
 215
 216        if(nregs == 2)
 217                central->clkver = 0UL;
 218        else
 219                central->clkver = ((((unsigned long)cregs[2].which_io) << 32UL) |
 220                                   ((unsigned long)cregs[2].phys_addr));
 221
 222        tmp = upa_readb(central->clkregs + CLOCK_STAT1);
 223        tmp &= 0xc0;
 224        switch(tmp) {
 225        case 0x40:
 226                nslots = 16;
 227                break;
 228        case 0xc0:
 229                nslots = 8;
 230                break;
 231        case 0x80:
 232                if(central->clkver != 0UL &&
 233                   upa_readb(central->clkver) != 0) {
 234                        if((upa_readb(central->clkver) & 0x80) != 0)
 235                                nslots = 4;
 236                        else
 237                                nslots = 5;
 238                        break;
 239                }
 240        default:
 241                nslots = 4;
 242                break;
 243        };
 244        central->slots = nslots;
 245        printk("CENTRAL: Detected %d slot Enterprise system. cfreg[%02x] cver[%02x]\n",
 246               central->slots, upa_readb(central->cfreg),
 247               (central->clkver ? upa_readb(central->clkver) : 0x00));
 248}
 249
 250static void init_all_fhc_hw(void)
 251{
 252        struct linux_fhc *fhc;
 253
 254        for(fhc = fhc_list; fhc != NULL; fhc = fhc->next) {
 255                u32 tmp;
 256
 257                /* Clear all of the interrupt mapping registers
 258                 * just in case OBP left them in a foul state.
 259                 */
 260#define ZAP(ICLR, IMAP) \
 261do {    u32 imap_tmp; \
 262        upa_writel(0, (ICLR)); \
 263        upa_readl(ICLR); \
 264        imap_tmp = upa_readl(IMAP); \
 265        imap_tmp &= ~(0x80000000); \
 266        upa_writel(imap_tmp, (IMAP)); \
 267        upa_readl(IMAP); \
 268} while (0)
 269
 270                ZAP(fhc->fhc_regs.ffregs + FHC_FFREGS_ICLR,
 271                    fhc->fhc_regs.ffregs + FHC_FFREGS_IMAP);
 272                ZAP(fhc->fhc_regs.sregs + FHC_SREGS_ICLR,
 273                    fhc->fhc_regs.sregs + FHC_SREGS_IMAP);
 274                ZAP(fhc->fhc_regs.uregs + FHC_UREGS_ICLR,
 275                    fhc->fhc_regs.uregs + FHC_UREGS_IMAP);
 276                ZAP(fhc->fhc_regs.tregs + FHC_TREGS_ICLR,
 277                    fhc->fhc_regs.tregs + FHC_TREGS_IMAP);
 278
 279#undef ZAP
 280
 281                /* Setup FHC control register. */
 282                tmp = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_CTRL);
 283
 284                /* All non-central boards have this bit set. */
 285                if(! IS_CENTRAL_FHC(fhc))
 286                        tmp |= FHC_CONTROL_IXIST;
 287
 288                /* For all FHCs, clear the firmware synchronization
 289                 * line and both low power mode enables.
 290                 */
 291                tmp &= ~(FHC_CONTROL_AOFF | FHC_CONTROL_BOFF | FHC_CONTROL_SLINE);
 292
 293                upa_writel(tmp, fhc->fhc_regs.pregs + FHC_PREGS_CTRL);
 294                upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_CTRL);
 295        }
 296
 297}
 298
 299void central_probe(void)
 300{
 301        struct linux_prom_registers fpregs[6];
 302        struct linux_fhc *fhc;
 303        char namebuf[128];
 304        int cnode, fnode, err;
 305
 306        cnode = prom_finddevice("/central");
 307        if(cnode == 0 || cnode == -1) {
 308                if (this_is_starfire)
 309                        starfire_cpu_setup();
 310                return;
 311        }
 312
 313        /* Ok we got one, grab some memory for software state. */
 314        central_bus = (struct linux_central *)
 315                central_alloc_bootmem(sizeof(struct linux_central));
 316        if (central_bus == NULL) {
 317                prom_printf("central_probe: Cannot alloc central_bus.\n");
 318                prom_halt();
 319        }
 320
 321        fhc = (struct linux_fhc *)
 322                central_alloc_bootmem(sizeof(struct linux_fhc));
 323        if (fhc == NULL) {
 324                prom_printf("central_probe: Cannot alloc central fhc.\n");
 325                prom_halt();
 326        }
 327
 328        /* First init central. */
 329        central_bus->child = fhc;
 330        central_bus->prom_node = cnode;
 331
 332        prom_getstring(cnode, "name", namebuf, sizeof(namebuf));
 333        strcpy(central_bus->prom_name, namebuf);
 334
 335        central_ranges_init(cnode, central_bus);
 336
 337        /* And then central's FHC. */
 338        fhc->next = fhc_list;
 339        fhc_list = fhc;
 340
 341        fhc->parent = central_bus;
 342        fnode = prom_searchsiblings(prom_getchild(cnode), "fhc");
 343        if(fnode == 0 || fnode == -1) {
 344                prom_printf("Critical error, central board lacks fhc.\n");
 345                prom_halt();
 346        }
 347        fhc->prom_node = fnode;
 348        prom_getstring(fnode, "name", namebuf, sizeof(namebuf));
 349        strcpy(fhc->prom_name, namebuf);
 350
 351        fhc_ranges_init(fnode, fhc);
 352
 353        /* Now, map in FHC register set. */
 354        if (prom_getproperty(fnode, "reg", (char *)&fpregs[0], sizeof(fpregs)) == -1) {
 355                prom_printf("CENTRAL: Fatal error, cannot get fhc regs.\n");
 356                prom_halt();
 357        }
 358        apply_central_ranges(central_bus, &fpregs[0], 6);
 359        
 360        fhc->fhc_regs.pregs = ((((unsigned long)fpregs[0].which_io)<<32UL) |
 361                               ((unsigned long)fpregs[0].phys_addr));
 362        fhc->fhc_regs.ireg = ((((unsigned long)fpregs[1].which_io)<<32UL) |
 363                              ((unsigned long)fpregs[1].phys_addr));
 364        fhc->fhc_regs.ffregs = ((((unsigned long)fpregs[2].which_io)<<32UL) |
 365                                ((unsigned long)fpregs[2].phys_addr));
 366        fhc->fhc_regs.sregs = ((((unsigned long)fpregs[3].which_io)<<32UL) |
 367                               ((unsigned long)fpregs[3].phys_addr));
 368        fhc->fhc_regs.uregs = ((((unsigned long)fpregs[4].which_io)<<32UL) |
 369                               ((unsigned long)fpregs[4].phys_addr));
 370        fhc->fhc_regs.tregs = ((((unsigned long)fpregs[5].which_io)<<32UL) |
 371                               ((unsigned long)fpregs[5].phys_addr));
 372
 373        /* Obtain board number from board status register, Central's
 374         * FHC lacks "board#" property.
 375         */
 376        err = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_BSR);
 377        fhc->board = (((err >> 16) & 0x01) |
 378                      ((err >> 12) & 0x0e));
 379
 380        fhc->jtag_master = 0;
 381
 382        /* Attach the clock board registers for CENTRAL. */
 383        probe_clock_board(central_bus, fhc, cnode, fnode);
 384
 385        err = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_ID);
 386        printk("FHC(board %d): Version[%x] PartID[%x] Manuf[%x] (CENTRAL)\n",
 387               fhc->board,
 388               ((err & FHC_ID_VERS) >> 28),
 389               ((err & FHC_ID_PARTID) >> 12),
 390               ((err & FHC_ID_MANUF) >> 1));
 391
 392        probe_other_fhcs();
 393
 394        init_all_fhc_hw();
 395}
 396
 397static __inline__ void fhc_ledblink(struct linux_fhc *fhc, int on)
 398{
 399        u32 tmp;
 400
 401        tmp = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_CTRL);
 402
 403        /* NOTE: reverse logic on this bit */
 404        if (on)
 405                tmp &= ~(FHC_CONTROL_RLED);
 406        else
 407                tmp |= FHC_CONTROL_RLED;
 408        tmp &= ~(FHC_CONTROL_AOFF | FHC_CONTROL_BOFF | FHC_CONTROL_SLINE);
 409
 410        upa_writel(tmp, fhc->fhc_regs.pregs + FHC_PREGS_CTRL);
 411        upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_CTRL);
 412}
 413
 414static __inline__ void central_ledblink(struct linux_central *central, int on)
 415{
 416        u8 tmp;
 417
 418        tmp = upa_readb(central->clkregs + CLOCK_CTRL);
 419
 420        /* NOTE: reverse logic on this bit */
 421        if(on)
 422                tmp &= ~(CLOCK_CTRL_RLED);
 423        else
 424                tmp |= CLOCK_CTRL_RLED;
 425
 426        upa_writeb(tmp, central->clkregs + CLOCK_CTRL);
 427        upa_readb(central->clkregs + CLOCK_CTRL);
 428}
 429
 430static struct timer_list sftimer;
 431static int led_state;
 432
 433static void sunfire_timer(unsigned long __ignored)
 434{
 435        struct linux_fhc *fhc;
 436
 437        central_ledblink(central_bus, led_state);
 438        for(fhc = fhc_list; fhc != NULL; fhc = fhc->next)
 439                if(! IS_CENTRAL_FHC(fhc))
 440                        fhc_ledblink(fhc, led_state);
 441        led_state = ! led_state;
 442        sftimer.expires = jiffies + (HZ >> 1);
 443        add_timer(&sftimer);
 444}
 445
 446/* After PCI/SBUS busses have been probed, this is called to perform
 447 * final initialization of all FireHose Controllers in the system.
 448 */
 449void firetruck_init(void)
 450{
 451        struct linux_central *central = central_bus;
 452        u8 ctrl;
 453
 454        /* No central bus, nothing to do. */
 455        if (central == NULL)
 456                return;
 457
 458        /* OBP leaves it on, turn it off so clock board timer LED
 459         * is in sync with FHC ones.
 460         */
 461        ctrl = upa_readb(central->clkregs + CLOCK_CTRL);
 462        ctrl &= ~(CLOCK_CTRL_RLED);
 463        upa_writeb(ctrl, central->clkregs + CLOCK_CTRL);
 464
 465        led_state = 0;
 466        init_timer(&sftimer);
 467        sftimer.data = 0;
 468        sftimer.function = &sunfire_timer;
 469        sftimer.expires = jiffies + (HZ >> 1);
 470        add_timer(&sftimer);
 471}
 472
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.