/* $Id: sbusprobe.c,v 1.1 1999/02/23 04:42:22 msw Exp $ * sbus.c: Probe for Sun SBUS and UPA framebuffers using OpenPROM. * * Copyright (C) 1998 Jakub Jelinek (jj@ultra.linux.cz) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "sbusprobe.h" #ifdef __sparc__ #include static char *promdev = "/dev/openprom"; static int promfd; static int prom_root_node, prom_current_node; #define MAX_PROP 128 #define MAX_VAL (4096-128-4) static char buf[4096]; #define DECL_OP(size) struct openpromio *op = (struct openpromio *)buf; op->oprom_size = (size) static int prom_getsibling(int node) { DECL_OP(sizeof(int)); if (node == -1) return 0; *(int *)op->oprom_array = node; if (ioctl (promfd, OPROMNEXT, op) < 0) return 0; prom_current_node = *(int *)op->oprom_array; return *(int *)op->oprom_array; } static int prom_getchild(int node) { DECL_OP(sizeof(int)); if (!node || node == -1) return 0; *(int *)op->oprom_array = node; if (ioctl (promfd, OPROMCHILD, op) < 0) return 0; prom_current_node = *(int *)op->oprom_array; return *(int *)op->oprom_array; } static char *prom_getproperty(char *prop, int *lenp) { DECL_OP(MAX_VAL); strcpy (op->oprom_array, prop); if (ioctl (promfd, OPROMGETPROP, op) < 0) return 0; if (lenp) *lenp = op->oprom_size; return op->oprom_array; } static int prom_getbool(char *prop) { DECL_OP(0); *(int *)op->oprom_array = 0; for (;;) { op->oprom_size = MAX_PROP; if (ioctl(promfd, OPROMNXTPROP, op) < 0) return 0; if (!op->oprom_size) return 0; if (!strcmp (op->oprom_array, prop)) return 1; } } static int sbus_ndevs; static enum sbusClass sbus_class; static struct sbusDevice ***sbus_devs; /* create a new clean sbusDevice entry */ struct sbusDevice *sbusNewDevice( void ) { struct sbusDevice *p; p = (struct sbusDevice *) malloc( sizeof(struct sbusDevice) ); p->nhits = 1; p->name=malloc(sizeof(char *)); p->module=malloc(sizeof(char *)); p->fb=NULL; p->class=SBUS_UNSET; return p; } /* remove a sbusDevice */ void sbusFreeDevice( struct sbusDevice *p ) { if (p->name) { free(p->name[0]); free(p->name); } if (p->module) { free(p->module[0]); free(p->module); } if (p->fb) { free(p->fb); } free(p); } static void prom_walk(int node, int sbus) { int nextnode; int len, nsbus = sbus; char *prop = prom_getproperty("device_type", &len); char *type = NULL, *module; int depth, width = 1152, height = 900; int freq = 0, sense = -1; if (!prop || len <= 0) goto next; switch (sbus_class) { case SBUS_ETHERNET: if (strcmp(prop, "network")) goto next; prop = prom_getproperty("name", &len); if (!prop || len <= 0) goto next; while ((*prop >= 'A' && *prop <= 'Z') || *prop == ',') prop++; if (!sbus) goto next; if (!strcmp(prop, "hme")) { type = "Sun Happy Meal Ethernet"; module = "sunhme"; } else if (!strcmp(prop, "le")) { type = "Sun Lance Ethernet"; module = ""; } else if (!strcmp(prop, "qe")) { prop = prom_getproperty("channel#", &len); if (prop && len == 4 && *(int *)prop) goto next; type = "Sun Quad Ethernet"; module = "sunqe"; } else if (!strcmp(prop, "mlanai") || !strcmp(prop, "myri")) { type = "MyriCOM MyriNET Gigabit Ethernet"; module = "myri_sbus"; } break; case SBUS_SCSI: depth = strcmp(prop, "scsi"); prop = prom_getproperty("name", &len); if (!prop || len <= 0) goto next; while ((*prop >= 'A' && *prop <= 'Z') || *prop == ',') prop++; if (!sbus) goto next; if (depth && strcmp(prop, "soc")) goto next; if (!strcmp(prop, "soc")) { type = "Sun SPARCStorage Array"; module = "fc4:soc:pluto"; } else if (!strcmp(prop, "esp")) { type = "Sun Enhanced SCSI Processor (ESP)"; module = ""; } else if (!strcmp(prop, "fas")) { type = "Sun Swift (ESP)"; module = ""; } else if (!strcmp(prop, "ptisp")) { type = "Performance Technologies ISP"; module = "qlogicpti"; } else if (!strcmp(prop, "isp")) { type = "QLogic ISP"; module = "qlogicpti"; } break; case SBUS_VIDEO: if (strcmp(prop, "display")) goto next; prop = prom_getproperty("name", &len); if (!prop || len <= 0) goto next; while ((*prop >= 'A' && *prop <= 'Z') || *prop == ',') prop++; if (!sbus && strcmp(prop, "ffb") && strcmp(prop, "afb") && strcmp(prop, "cgfourteen")) goto next; if (!strcmp(prop, "bwtwo")) { type = "Sun Monochrome (bwtwo)"; depth = 1; } else if (!strcmp(prop, "cgthree")) { type = "Sun Color3 (cgthree)"; depth = 8; } else if (!strcmp(prop, "cgeight")) { type = "Sun CG8/RasterOps"; depth = 8; } else if (!strcmp(prop, "cgtwelve")) { type = "Sun GS (cgtwelve)"; depth = 24; } else if (!strcmp(prop, "gt")) { type = "Sun Graphics Tower"; depth = 24; } else if (!strcmp(prop, "mgx")) { prop = prom_getproperty("fb_size", &len); if (prop && len == 4 && *(int *)prop == 0x400000) type = "Quantum 3D MGXplus with 4M VRAM"; else type = "Quantum 3D MGXplus"; depth = 24; } else if (!strcmp(prop, "cgsix")) { int chiprev = 0; int vmsize = 0; prop = prom_getproperty("chiprev", &len); if (prop && len == 4) chiprev = *(int *)prop; prop = prom_getproperty("montype", &len); if (prop && len == 4) sense = *(int *)prop; prop = prom_getproperty("vfreq", &len); if (prop && len == 4) freq = *(int *)prop; prop = prom_getproperty("vmsize", &len); if (prop && len == 4) vmsize = *(int *)prop; switch (chiprev) { case 0: type = "Sun Unknown GX"; break; case 1 ... 4: type = "Sun Double width GX"; break; case 5 ... 9: type = "Sun Single width GX"; break; case 11: switch (vmsize) { case 2: type = "Sun Turbo GX with 1M VSIMM"; break; case 4: type = "Sun Turbo GX Plus"; break; default: type = "Sun Turbo GX"; break; } } depth = 8; } else if (!strcmp(prop, "cgfourteen")) { int size = 0; prop = prom_getproperty("vfreq", &len); if (prop && len == 4) freq = *(int *)prop; prop = prom_getproperty("reg", &len); if (prop && !(len % 12) && len > 0) size = *(int *)(prop + len - 4); switch (size) { case 0x400000: type = "Sun SX with 4M VSIMM"; break; case 0x800000: type = "Sun SX with 8M VSIMM"; break; default: type = "Sun SX"; } depth = 24; } else if (!strcmp(prop, "leo")) { prop = prom_getproperty("model", &len); if (prop && len > 0 && !strstr(prop, "501-2503")) type = "Sun Turbo ZX"; else type = "Sun ZX or Turbo ZX"; depth = 24; } else if (!strcmp(prop, "tcx")) { if (prom_getbool("tcx-8-bit")) { type = "Sun TCX (8bit)"; depth = 8; } else { type = "Sun TCX (S24)"; depth = 24; } } else if (!strcmp(prop, "afb")) { int btype = 0; prop = prom_getproperty("vfreq", &len); if (prop && len == 4) freq = *(int *)prop; prop = prom_getproperty("board_type", &len); if (prop && len == 4) btype = *(int *)prop; if (btype == 3) type = "Sun Elite3D-M6 Horizontal"; else type = "Sun Elite3D"; depth = 24; } else if (!strcmp(prop, "ffb")) { int btype = 0; prop = prom_getproperty("vfreq", &len); if (prop && len == 4) freq = *(int *)prop; prop = prom_getproperty("board_type", &len); if (prop && len == 4) btype = *(int *)prop; switch (btype) { case 0x08: type="Sun FFB 67MHz Creator"; break; case 0x0b: type="Sun FFB 67MHz Creator 3D"; break; case 0x1b: type="Sun FFB 75MHz Creator 3D"; break; case 0x20: case 0x28: type="Sun FFB2 Vertical Creator"; break; case 0x23: case 0x2b: type="Sun FFB2 Vertical Creator 3D"; break; case 0x30: type="Sun FFB2+ Vertical Creator"; break; case 0x33: type="Sun FFB2+ Vertical Creator 3D"; break; case 0x40: case 0x48: type="Sun FFB2 Horizontal Creator"; break; case 0x43: case 0x4b: type="Sun FFB2 Horizontal Creator 3D"; break; default: type = "Sun FFB"; break; } depth = 24; } if (type) { prop = prom_getproperty("width", &len); if (prop && len == 4) width = *(int *)prop; prop = prom_getproperty("height", &len); if (prop && len == 4) height = *(int *)prop; } default: break; } if (type) { if ((sbus_ndevs % 4) == 0) *sbus_devs=(struct sbusDevice **) realloc(*sbus_devs,(sbus_ndevs+4)*sizeof(struct sbusDevice *)); (*sbus_devs)[sbus_ndevs]=sbusNewDevice(); if (sbus_class == SBUS_VIDEO) { (*sbus_devs)[sbus_ndevs]->fb = (struct sbusFB *)malloc(sizeof (struct sbusFB)); (*sbus_devs)[sbus_ndevs]->fb->width = width; (*sbus_devs)[sbus_ndevs]->fb->height = height; (*sbus_devs)[sbus_ndevs]->fb->freq = freq; (*sbus_devs)[sbus_ndevs]->fb->monitor = sense; } (*sbus_devs)[sbus_ndevs]->name[0]=strdup(type); if (sbus_class == SBUS_VIDEO) { switch (depth) { case 1: type = "Server:SunMono"; break; case 8: type = "Server:Sun"; break; case 24: type = "Server:Sun24"; break; } (*sbus_devs)[sbus_ndevs]->module[0]=strdup(type); } else { (*sbus_devs)[sbus_ndevs]->module[0]=strdup(module); } (*sbus_devs)[sbus_ndevs]->class=sbus_class; sbus_ndevs++; } next: prop = prom_getproperty("name", &len); if (prop && len > 0 && (!strcmp(prop, "sbus") || !strcmp(prop, "sbi"))) nsbus = 1; nextnode = prom_getchild(node); if (nextnode) prom_walk(nextnode, nsbus); nextnode = prom_getsibling(node); if (nextnode) prom_walk(nextnode, sbus); } /* given a class to probe, returns an array of devices found which match */ /* all entries are malloc'd, so caller must free when done. Use */ /* sbusDeviceFree() to free individual sbusDevice's, since they have */ /* internal elements which are malloc'd */ /* returns number cards found. If nothing found returns 0 and */ /* setsdevs is set to NULL */ /* if system error occurs returns -1 */ int sbusProbeDevice( enum sbusClass type, struct sbusDevice ***devs ) { sbus_ndevs = 0; sbus_class = type; sbus_devs = devs; *devs = NULL; promfd = open(promdev, O_RDONLY); if (promfd == -1) return -1; prom_root_node = prom_getsibling(0); if (!prom_root_node) return -1; prom_walk(prom_root_node, 0); close(promfd); return sbus_ndevs; } #else int sbusProbeDevice( enum sbusClass type, struct sbusDevice ***devs ) { return -1; } struct sbusDevice *sbusNewDevice( void ) { return NULL; } void sbusFreeDevice( struct sbusDevice *p ) { } #endif