linux/drivers/parisc/eisa_enumerator.c
<<
>>
Prefs
   1/*
   2 * eisa_enumerator.c - provide support for EISA adapters in PA-RISC machines
   3 *
   4 * This program is free software; you can redistribute it and/or
   5 * modify it under the terms of the GNU General Public License
   6 * as published by the Free Software Foundation; either version
   7 * 2 of the License, or (at your option) any later version.
   8 *
   9 * Copyright (c) 2002 Daniel Engstrom <5116@telia.com>
  10 *
  11 */
  12
  13#include <linux/ioport.h>
  14#include <linux/init.h>
  15#include <linux/kernel.h>
  16#include <linux/slab.h>
  17#include <asm/io.h>
  18#include <asm/uaccess.h>
  19#include <asm/byteorder.h>
  20
  21#include <asm/eisa_bus.h>
  22#include <asm/eisa_eeprom.h>
  23
  24
  25/*
  26 * Todo:
  27 * 
  28 * PORT init with MASK attr and other size than byte
  29 * MEMORY with other decode than 20 bit
  30 * CRC stuff
  31 * FREEFORM stuff
  32 */
  33
  34#define EPI 0xc80
  35#define NUM_SLOT 16
  36#define SLOT2PORT(x) (x<<12)
  37
  38
  39/* macros to handle unaligned accesses and 
  40 * byte swapping. The data in the EEPROM is
  41 * little-endian on the big-endian PAROSC */
  42#define get_8(x) (*(u_int8_t*)(x))
  43
  44static inline u_int16_t get_16(const unsigned char *x)
  45{ 
  46        return (x[1] << 8) | x[0];
  47}
  48
  49static inline u_int32_t get_32(const unsigned char *x)
  50{
  51        return (x[3] << 24) | (x[2] << 16) | (x[1] << 8) | x[0];
  52}
  53
  54static inline u_int32_t get_24(const unsigned char *x)
  55{
  56        return (x[2] << 24) | (x[1] << 16) | (x[0] << 8);
  57}
  58
  59static void print_eisa_id(char *s, u_int32_t id)
  60{
  61        char vendor[4];
  62        int rev;
  63        int device;
  64        
  65        rev = id & 0xff;
  66        id >>= 8;
  67        device = id & 0xff;
  68        id >>= 8;
  69        vendor[3] = '\0';
  70        vendor[2] = '@' + (id & 0x1f);
  71        id >>= 5;       
  72        vendor[1] = '@' + (id & 0x1f);
  73        id >>= 5;       
  74        vendor[0] = '@' + (id & 0x1f);
  75        id >>= 5;       
  76        
  77        sprintf(s, "%s%02X%02X", vendor, device, rev);
  78}
  79       
  80static int configure_memory(const unsigned char *buf, 
  81                       struct resource *mem_parent,
  82                       char *name)
  83{
  84        int len;
  85        u_int8_t c;
  86        int i;
  87        struct resource *res;
  88        
  89        len=0;
  90        
  91        for (i=0;i<HPEE_MEMORY_MAX_ENT;i++) {
  92                c = get_8(buf+len);
  93                
  94                if (NULL != (res = kmalloc(sizeof(struct resource), GFP_KERNEL))) {
  95                        int result;
  96                        
  97                        res->name = name;
  98                        res->start = mem_parent->start + get_24(buf+len+2);
  99                        res->end = res->start + get_16(buf+len+5)*1024;
 100                        res->flags = IORESOURCE_MEM;
 101                        printk("memory %lx-%lx ", (unsigned long)res->start, (unsigned long)res->end);
 102                        result = request_resource(mem_parent, res);
 103                        if (result < 0) {
 104                                printk(KERN_ERR "EISA Enumerator: failed to claim EISA Bus address space!\n");
 105                                return result;
 106                        }
 107                }
 108                        
 109                len+=7;      
 110        
 111                if (!(c & HPEE_MEMORY_MORE)) {
 112                        break;
 113                }
 114        }
 115        
 116        return len;
 117}
 118
 119
 120static int configure_irq(const unsigned char *buf)
 121{
 122        int len;
 123        u_int8_t c;
 124        int i;
 125        
 126        len=0;
 127        
 128        for (i=0;i<HPEE_IRQ_MAX_ENT;i++) {
 129                c = get_8(buf+len);
 130                
 131                printk("IRQ %d ", c & HPEE_IRQ_CHANNEL_MASK);
 132                if (c & HPEE_IRQ_TRIG_LEVEL) {
 133                        eisa_make_irq_level(c & HPEE_IRQ_CHANNEL_MASK);
 134                } else {
 135                        eisa_make_irq_edge(c & HPEE_IRQ_CHANNEL_MASK);
 136                }
 137                
 138                len+=2; 
 139                /* hpux seems to allow for
 140                 * two bytes of irq data but only defines one of
 141                 * them, I think */
 142                if  (!(c & HPEE_IRQ_MORE)) {
 143                        break;
 144                }
 145        }
 146        
 147        return len;
 148}
 149
 150
 151static int configure_dma(const unsigned char *buf)
 152{
 153        int len;
 154        u_int8_t c;
 155        int i;
 156        
 157        len=0;
 158        
 159        for (i=0;i<HPEE_DMA_MAX_ENT;i++) {
 160                c = get_8(buf+len);
 161                printk("DMA %d ", c&HPEE_DMA_CHANNEL_MASK);
 162                /* fixme: maybe initialize the dma channel withthe timing ? */
 163                len+=2;      
 164                if (!(c & HPEE_DMA_MORE)) {
 165                        break;
 166                }
 167        }
 168        
 169        return len;
 170}
 171
 172static int configure_port(const unsigned char *buf, struct resource *io_parent,
 173                     char *board)
 174{
 175        int len;
 176        u_int8_t c;
 177        int i;
 178        struct resource *res;
 179        int result;
 180        
 181        len=0;
 182        
 183        for (i=0;i<HPEE_PORT_MAX_ENT;i++) {
 184                c = get_8(buf+len);
 185                
 186                if (NULL != (res = kmalloc(sizeof(struct resource), GFP_KERNEL))) {
 187                        res->name = board;
 188                        res->start = get_16(buf+len+1);
 189                        res->end = get_16(buf+len+1)+(c&HPEE_PORT_SIZE_MASK)+1;
 190                        res->flags = IORESOURCE_IO;
 191                        printk("ioports %lx-%lx ", (unsigned long)res->start, (unsigned long)res->end);
 192                        result = request_resource(io_parent, res);
 193                        if (result < 0) {
 194                                printk(KERN_ERR "EISA Enumerator: failed to claim EISA Bus address space!\n");
 195                                return result;
 196                        }
 197                }
 198
 199                len+=3;      
 200                if (!(c & HPEE_PORT_MORE)) {
 201                        break;
 202                }
 203        }
 204        
 205        return len;
 206}
 207
 208
 209/* byte 1 and 2 is the port number to write
 210 * and at byte 3 the value to write starts.
 211 * I assume that there are and- and or- masks
 212 * here when HPEE_PORT_INIT_MASK is set but I have 
 213 * not yet encountered this. */
 214static int configure_port_init(const unsigned char *buf)
 215{
 216        int len=0;
 217        u_int8_t c;
 218        
 219        while (len<HPEE_PORT_INIT_MAX_LEN) {
 220                int s=0;
 221                c = get_8(buf+len);
 222                
 223                switch (c & HPEE_PORT_INIT_WIDTH_MASK)  {
 224                 case HPEE_PORT_INIT_WIDTH_BYTE:
 225                        s=1;
 226                        if (c & HPEE_PORT_INIT_MASK) {
 227                                printk(KERN_WARNING "port_init: unverified mask attribute\n");
 228                                outb((inb(get_16(buf+len+1) & 
 229                                          get_8(buf+len+3)) | 
 230                                      get_8(buf+len+4)), get_16(buf+len+1));
 231                                      
 232                        } else {
 233                                outb(get_8(buf+len+3), get_16(buf+len+1));
 234                                      
 235                        }
 236                        break;
 237                 case HPEE_PORT_INIT_WIDTH_WORD:
 238                        s=2;
 239                        if (c & HPEE_PORT_INIT_MASK) {
 240                                printk(KERN_WARNING "port_init: unverified mask attribute\n");
 241                                       outw((inw(get_16(buf+len+1)) &
 242                                             get_16(buf+len+3)) |
 243                                            get_16(buf+len+5), 
 244                                            get_16(buf+len+1));
 245                        } else {
 246                                outw(cpu_to_le16(get_16(buf+len+3)), get_16(buf+len+1));
 247                        }
 248                        break;
 249                 case HPEE_PORT_INIT_WIDTH_DWORD:
 250                        s=4;
 251                        if (c & HPEE_PORT_INIT_MASK) {
 252                                printk(KERN_WARNING "port_init: unverified mask attribute\n");
 253                                outl((inl(get_16(buf+len+1) &
 254                                          get_32(buf+len+3)) |
 255                                      get_32(buf+len+7)), get_16(buf+len+1));
 256                        } else {
 257                                outl(cpu_to_le32(get_32(buf+len+3)), get_16(buf+len+1));
 258                        }
 259
 260                        break;
 261                 default:
 262                        printk(KERN_ERR "Invalid port init word %02x\n", c);
 263                        return 0;
 264                }
 265                
 266                if (c & HPEE_PORT_INIT_MASK) {   
 267                        s*=2;
 268                }
 269                
 270                len+=s+3;
 271                if (!(c & HPEE_PORT_INIT_MORE)) {
 272                        break;
 273                }
 274        }
 275        
 276        return len;
 277}
 278
 279static int configure_choise(const unsigned char *buf, u_int8_t *info)
 280{
 281        int len;
 282        
 283        /* theis record contain the value of the functions
 284         * configuration choises and an info byte which 
 285         * describes which other records to expect in this 
 286         * function */
 287        len = get_8(buf);
 288        *info=get_8(buf+len+1);
 289         
 290        return len+2;
 291}
 292
 293static int configure_type_string(const unsigned char *buf) 
 294{
 295        int len;
 296        
 297        /* just skip past the type field */
 298        len = get_8(buf);
 299        if (len > 80) {
 300                printk(KERN_ERR "eisa_enumerator: type info field too long (%d, max is 80)\n", len);
 301        }
 302        
 303        return 1+len;
 304}
 305
 306static int configure_function(const unsigned char *buf, int *more) 
 307{
 308        /* the init field seems to be a two-byte field
 309         * which is non-zero if there are an other function following
 310         * I think it is the length of the function def 
 311         */
 312        *more = get_16(buf);
 313        
 314        return 2;
 315}
 316
 317static int parse_slot_config(int slot,
 318                             const unsigned char *buf,
 319                             struct eeprom_eisa_slot_info *es, 
 320                             struct resource *io_parent,
 321                             struct resource *mem_parent)
 322{
 323        int res=0;
 324        int function_len;
 325        unsigned int pos=0;
 326        unsigned int maxlen;
 327        int num_func=0;
 328        u_int8_t flags;
 329        int p0;
 330        
 331        char *board;
 332        int id_string_used=0;
 333        
 334        if (NULL == (board = kmalloc(8, GFP_KERNEL))) {
 335                return -1;
 336        }
 337        print_eisa_id(board, es->eisa_slot_id);
 338        printk(KERN_INFO "EISA slot %d: %s %s ", 
 339               slot, board, es->flags&HPEE_FLAG_BOARD_IS_ISA ? "ISA" : "EISA");
 340        
 341        maxlen = es->config_data_length < HPEE_MAX_LENGTH ?
 342                         es->config_data_length : HPEE_MAX_LENGTH;
 343        while ((pos < maxlen) && (num_func <= es->num_functions)) {
 344                pos+=configure_function(buf+pos, &function_len); 
 345                
 346                if (!function_len) {
 347                        break;
 348                }
 349                num_func++;
 350                p0 = pos;
 351                pos += configure_choise(buf+pos, &flags);
 352
 353                if (flags & HPEE_FUNCTION_INFO_F_DISABLED) {
 354                        /* function disabled, skip silently */
 355                        pos = p0 + function_len;
 356                        continue;
 357                }
 358                if (flags & HPEE_FUNCTION_INFO_CFG_FREE_FORM) {
 359                        /* I have no idea how to handle this */
 360                        printk("function %d have free-form confgiuration, skipping ",
 361                                num_func);
 362                        pos = p0 + function_len;
 363                        continue;
 364                }
 365
 366                /* the ordering of the sections need
 367                 * more investigation.
 368                 * Currently I think that memory comaed before IRQ
 369                 * I assume the order is LSB to MSB in the 
 370                 * info flags 
 371                 * eg type, memory, irq, dma, port, HPEE_PORT_init 
 372                 */
 373
 374                if (flags & HPEE_FUNCTION_INFO_HAVE_TYPE) {
 375                        pos += configure_type_string(buf+pos);
 376                }
 377                
 378                if (flags & HPEE_FUNCTION_INFO_HAVE_MEMORY) {
 379                        id_string_used=1;
 380                        pos += configure_memory(buf+pos, mem_parent, board);
 381                } 
 382                
 383                if (flags & HPEE_FUNCTION_INFO_HAVE_IRQ) {
 384                        pos += configure_irq(buf+pos);
 385                } 
 386                
 387                if (flags & HPEE_FUNCTION_INFO_HAVE_DMA) {
 388                        pos += configure_dma(buf+pos);
 389                } 
 390                
 391                if (flags & HPEE_FUNCTION_INFO_HAVE_PORT) {
 392                        id_string_used=1;
 393                        pos += configure_port(buf+pos, io_parent, board);
 394                } 
 395                
 396                if (flags &  HPEE_FUNCTION_INFO_HAVE_PORT_INIT) {
 397                        pos += configure_port_init(buf+pos);
 398                }
 399                
 400                if (p0 + function_len < pos) {
 401                        printk(KERN_ERR "eisa_enumerator: function %d length mis-match "
 402                               "got %d, expected %d\n",
 403                               num_func, pos-p0, function_len);
 404                        res=-1;
 405                        break;
 406                }
 407                pos = p0 + function_len;
 408        }
 409        printk("\n");
 410        if (!id_string_used) {
 411                kfree(board);
 412        }
 413        
 414        if (pos != es->config_data_length) {
 415                printk(KERN_ERR "eisa_enumerator: config data length mis-match got %d, expected %d\n",
 416                        pos, es->config_data_length);
 417                res=-1;
 418        }
 419        
 420        if (num_func != es->num_functions) {
 421                printk(KERN_ERR "eisa_enumerator: number of functions mis-match got %d, expected %d\n",
 422                        num_func, es->num_functions);
 423                res=-2;
 424        }
 425        
 426        return res;
 427        
 428}
 429
 430static int init_slot(int slot, struct eeprom_eisa_slot_info *es)
 431{
 432        unsigned int id;
 433        
 434        char id_string[8];
 435        
 436        if (!(es->slot_info&HPEE_SLOT_INFO_NO_READID)) {
 437                /* try to read the id of the board in the slot */
 438                id = le32_to_cpu(inl(SLOT2PORT(slot)+EPI));
 439                
 440                if (0xffffffff == id) {
 441                        /* Maybe we didn't expect a card to be here... */
 442                        if (es->eisa_slot_id == 0xffffffff)
 443                                return -1;
 444                        
 445                        /* this board is not here or it does not 
 446                         * support readid 
 447                         */
 448                        printk(KERN_ERR "EISA slot %d a configured board was not detected (", 
 449                               slot);
 450                        
 451                        print_eisa_id(id_string, es->eisa_slot_id);
 452                        printk(" expected %s)\n", id_string);
 453                
 454                        return -1;      
 455
 456                }
 457                if (es->eisa_slot_id != id) {
 458                        print_eisa_id(id_string, id);
 459                        printk(KERN_ERR "EISA slot %d id mis-match: got %s", 
 460                               slot, id_string);
 461                        
 462                        print_eisa_id(id_string, es->eisa_slot_id);
 463                        printk(" expected %s\n", id_string);
 464                
 465                        return -1;      
 466                        
 467                }
 468        }
 469        
 470        /* now: we need to enable the board if 
 471         * it supports enabling and run through
 472         * the port init sction if present
 473         * and finally record any interrupt polarity
 474         */
 475        if (es->slot_features & HPEE_SLOT_FEATURES_ENABLE) {
 476                /* enable board */
 477                outb(0x01| inb(SLOT2PORT(slot)+EPI+4),
 478                     SLOT2PORT(slot)+EPI+4);
 479        }
 480        
 481        return 0;
 482}
 483
 484
 485int eisa_enumerator(unsigned long eeprom_addr,
 486                    struct resource *io_parent, struct resource *mem_parent) 
 487{
 488        int i;
 489        struct eeprom_header *eh;
 490        static char eeprom_buf[HPEE_MAX_LENGTH];
 491        
 492        for (i=0; i < HPEE_MAX_LENGTH; i++) {
 493                eeprom_buf[i] = gsc_readb(eeprom_addr+i);
 494        }
 495        
 496        printk(KERN_INFO "Enumerating EISA bus\n");
 497                        
 498        eh = (struct eeprom_header*)(eeprom_buf);
 499        for (i=0;i<eh->num_slots;i++) {
 500                struct eeprom_eisa_slot_info *es;
 501                
 502                es = (struct eeprom_eisa_slot_info*)
 503                        (&eeprom_buf[HPEE_SLOT_INFO(i)]);
 504                
 505                if (-1==init_slot(i+1, es)) {
 506                        continue;
 507                }
 508                
 509                if (es->config_data_offset < HPEE_MAX_LENGTH) {
 510                        if (parse_slot_config(i+1, &eeprom_buf[es->config_data_offset],
 511                                              es, io_parent, mem_parent)) {
 512                                return -1;
 513                        }
 514                } else {
 515                        printk (KERN_WARNING "EISA EEPROM offset 0x%x out of range\n",es->config_data_offset);
 516                        return -1;
 517                }
 518        }
 519        return eh->num_slots;
 520}
 521
 522
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.