/* * SBUS and OpenPROM access functions. * * Copyright (C) 2000 Jakub Jelinek (jakub@redhat.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * JAKUB JELINEK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* $XFree86: xc/programs/Xserver/hw/xfree86/os-support/bus/Sbus.c,v 1.4 2003/10/02 13:30:06 eich Exp $ */ #include #include #include #include #include #include #ifdef sun #include #endif #include "xf86.h" #include "xf86Priv.h" #include "xf86_OSlib.h" #include "xf86sbusBus.h" #include "xf86Sbus.h" int promRootNode; static int promFd = -1; static int promCurrentNode; static int promOpenCount = 0; static int promP1275 = -1; #define MAX_PROP 128 #define MAX_VAL (4096-128-4) static struct openpromio *promOpio; sbusDevicePtr *xf86SbusInfo = NULL; struct sbus_devtable sbusDeviceTable[] = { { SBUS_DEVICE_BW2, FBTYPE_SUN2BW, "bwtwo", "Sun Monochrome (bwtwo)" }, { SBUS_DEVICE_CG2, FBTYPE_SUN2COLOR, "cgtwo", "Sun Color2 (cgtwo)" }, { SBUS_DEVICE_CG3, FBTYPE_SUN3COLOR, "cgthree", "Sun Color3 (cgthree)" }, { SBUS_DEVICE_CG4, FBTYPE_SUN4COLOR, "cgfour", "Sun Color4 (cgfour)" }, { SBUS_DEVICE_CG6, FBTYPE_SUNFAST_COLOR, "cgsix", "Sun GX" }, { SBUS_DEVICE_CG8, FBTYPE_MEMCOLOR, "cgeight", "Sun CG8/RasterOps" }, { SBUS_DEVICE_CG12, FBTYPE_SUNGP3, "cgtwelve", "Sun GS (cgtwelve)" }, { SBUS_DEVICE_CG14, FBTYPE_MDICOLOR, "cgfourteen", "Sun SX" }, { SBUS_DEVICE_GT, FBTYPE_SUNGT, "gt", "Sun Graphics Tower" }, { SBUS_DEVICE_MGX, -1, "mgx", "Quantum 3D MGXplus" }, { SBUS_DEVICE_LEO, FBTYPE_SUNLEO, "leo", "Sun ZX or Turbo ZX" }, { SBUS_DEVICE_TCX, FBTYPE_TCXCOLOR, "tcx", "Sun TCX" }, { SBUS_DEVICE_FFB, FBTYPE_CREATOR, "ffb", "Sun FFB" }, { SBUS_DEVICE_FFB, FBTYPE_CREATOR, "afb", "Sun Elite3D" }, { 0, 0, NULL } }; int promGetSibling(int node) { promOpio->oprom_size = sizeof(int); if (node == -1) return 0; *(int *)promOpio->oprom_array = node; if (ioctl(promFd, OPROMNEXT, promOpio) < 0) return 0; promCurrentNode = *(int *)promOpio->oprom_array; return *(int *)promOpio->oprom_array; } int promGetChild(int node) { promOpio->oprom_size = sizeof(int); if (!node || node == -1) return 0; *(int *)promOpio->oprom_array = node; if (ioctl(promFd, OPROMCHILD, promOpio) < 0) return 0; promCurrentNode = *(int *)promOpio->oprom_array; return *(int *)promOpio->oprom_array; } char * promGetProperty(const char *prop, int *lenp) { promOpio->oprom_size = MAX_VAL; strcpy(promOpio->oprom_array, prop); if (ioctl(promFd, OPROMGETPROP, promOpio) < 0) return 0; if (lenp) *lenp = promOpio->oprom_size; return promOpio->oprom_array; } int promGetBool(const char *prop) { promOpio->oprom_size = 0; *(int *)promOpio->oprom_array = 0; for (;;) { promOpio->oprom_size = MAX_PROP; if (ioctl(promFd, OPROMNXTPROP, promOpio) < 0) return 0; if (!promOpio->oprom_size) return 0; if (!strcmp(promOpio->oprom_array, prop)) return 1; } } #define PROM_NODE_SIBLING 0x01 #define PROM_NODE_PREF 0x02 #define PROM_NODE_SBUS 0x04 #define PROM_NODE_EBUS 0x08 #define PROM_NODE_PCI 0x10 static int promSetNode(sbusPromNodePtr pnode) { int node; if (!pnode->node || pnode->node == -1) return -1; if (pnode->cookie[0] & PROM_NODE_SIBLING) node = promGetSibling(pnode->cookie[1]); else node = promGetChild(pnode->cookie[1]); if (pnode->node != node) return -1; return 0; } static void promIsP1275(void) { #ifdef linux FILE *f; char buffer[1024]; if (promP1275 != -1) return; promP1275 = 0; f = fopen("/proc/cpuinfo","r"); if (!f) return; while (fgets(buffer, 1024, f) != NULL) if (!strncmp (buffer, "type", 4) && strstr (buffer, "sun4u")) { promP1275 = 1; break; } fclose(f); #elif defined(sun) struct utsname buffer; if ((uname(&buffer) == 0) && !strcmp(buffer.machine, "sun4u")) promP1275 = TRUE; else promP1275 = FALSE; #elif defined(__FreeBSD__) promP1275 = TRUE; #else #error Missing promIsP1275() function for this OS #endif } void sparcPromClose(void) { if (promOpenCount > 1) { promOpenCount--; return; } if (promFd != -1) { close(promFd); promFd = -1; } if (promOpio) { xfree(promOpio); promOpio = NULL; } promOpenCount = 0; } int sparcPromInit(void) { if (promOpenCount) { promOpenCount++; return 0; } promFd = open("/dev/openprom", O_RDONLY, 0); if (promFd == -1) return -1; promOpio = (struct openpromio *)xalloc(4096); if (!promOpio) { sparcPromClose(); return -1; } promRootNode = promGetSibling(0); if (!promRootNode) { sparcPromClose(); return -1; } promIsP1275(); promOpenCount++; return 0; } char * sparcPromGetProperty(sbusPromNodePtr pnode, const char *prop, int *lenp) { if (promSetNode(pnode)) return NULL; return promGetProperty(prop, lenp); } int sparcPromGetBool(sbusPromNodePtr pnode, const char *prop) { if (promSetNode(pnode)) return 0; return promGetBool(prop); } static void promWalkAssignNodes(int node, int oldnode, int flags, sbusDevicePtr *devicePtrs) { int nextnode; int len, sbus = flags & PROM_NODE_SBUS; char *prop; int devId, i, j; sbusPromNode pNode, pNode2; prop = promGetProperty("device_type", &len); if (prop && (len > 0)) do { if (!strcmp(prop, "display")) { prop = promGetProperty("name", &len); if (!prop || len <= 0) break; while ((*prop >= 'A' && *prop <= 'Z') || *prop == ',') prop++; for (i = 0; sbusDeviceTable[i].devId; i++) if (!strcmp(prop, sbusDeviceTable[i].promName)) break; devId = sbusDeviceTable[i].devId; if (!devId) break; if (!sbus) { if (devId == SBUS_DEVICE_FFB) { /* * All /SUNW,ffb outside of SBUS tree come before all * /SUNW,afb outside of SBUS tree in Linux. */ if (!strcmp(prop, "afb")) flags |= PROM_NODE_PREF; } else if (devId != SBUS_DEVICE_CG14) break; } for (i = 0; i < 32; i++) { if (!devicePtrs[i] || devicePtrs[i]->devId != devId) continue; if (devicePtrs[i]->node.node) { if ((devicePtrs[i]->node.cookie[0] & ~PROM_NODE_SIBLING) <= (flags & ~PROM_NODE_SIBLING)) continue; for (j = i + 1, pNode = devicePtrs[i]->node; j < 32; j++) { if (!devicePtrs[j] || devicePtrs[j]->devId != devId) continue; pNode2 = devicePtrs[j]->node; devicePtrs[j]->node = pNode; pNode = pNode2; } } devicePtrs[i]->node.node = node; devicePtrs[i]->node.cookie[0] = flags; devicePtrs[i]->node.cookie[1] = oldnode; break; } break; } } while (0); prop = promGetProperty("name", &len); if (prop && len > 0) { if (!strcmp(prop, "sbus") || !strcmp(prop, "sbi")) sbus = PROM_NODE_SBUS; } nextnode = promGetChild(node); if (nextnode) promWalkAssignNodes(nextnode, node, sbus, devicePtrs); nextnode = promGetSibling(node); if (nextnode) promWalkAssignNodes(nextnode, node, PROM_NODE_SIBLING | sbus, devicePtrs); } void sparcPromAssignNodes(void) { sbusDevicePtr psdp, *psdpp; int n, holes = 0, i, j; FILE *f; sbusDevicePtr devicePtrs[32]; (void)memset(devicePtrs, 0, sizeof(devicePtrs)); for (psdpp = xf86SbusInfo, n = 0; (psdp = *psdpp); psdpp++, n++) { if (psdp->fbNum != n) holes = 1; devicePtrs[psdp->fbNum] = psdp; } if (holes && (f = fopen("/proc/fb", "r")) != NULL) { /* We could not open one of fb devices, check /proc/fb to see what * were the types of the cards missed. */ char buffer[64]; int fbNum, devId; static struct { int devId; char *prefix; } procFbPrefixes[] = { { SBUS_DEVICE_BW2, "BWtwo" }, { SBUS_DEVICE_CG14, "CGfourteen" }, { SBUS_DEVICE_CG6, "CGsix" }, { SBUS_DEVICE_CG3, "CGthree" }, { SBUS_DEVICE_FFB, "Creator" }, { SBUS_DEVICE_FFB, "Elite 3D" }, { SBUS_DEVICE_LEO, "Leo" }, { SBUS_DEVICE_TCX, "TCX" }, { 0, NULL }, }; while (fscanf(f, "%d %63s\n", &fbNum, buffer) == 2) { for (i = 0; procFbPrefixes[i].devId; i++) if (! strncmp(procFbPrefixes[i].prefix, buffer, strlen(procFbPrefixes[i].prefix))) break; devId = procFbPrefixes[i].devId; if (! devId) continue; if (devicePtrs[fbNum]) { if (devicePtrs[fbNum]->devId != devId) xf86ErrorF("Inconsistent /proc/fb with FBIOGATTR\n"); } else if (!devicePtrs[fbNum]) { devicePtrs[fbNum] = psdp = xnfcalloc(sizeof (sbusDevice), 1); psdp->devId = devId; psdp->fbNum = fbNum; psdp->fd = -2; } } fclose(f); } promGetSibling(0); promWalkAssignNodes(promRootNode, 0, PROM_NODE_PREF, devicePtrs); for (i = 0, j = 0; i < 32; i++) if (devicePtrs[i] && devicePtrs[i]->fbNum == -1) j++; xf86SbusInfo = xnfrealloc(xf86SbusInfo, sizeof(psdp) * (n + j + 1)); for (i = 0, psdpp = xf86SbusInfo; i < 32; i++) if (devicePtrs[i]) { if (devicePtrs[i]->fbNum == -1) { memmove(psdpp + 1, psdpp, sizeof(psdpp) * (n + 1)); *psdpp = devicePtrs[i]; } else n--; } } static char * promGetReg(int type) { char *prop; int len; static char regstr[40]; regstr[0] = 0; prop = promGetProperty("reg", &len); if (prop && len >= 4) { unsigned int *reg = (unsigned int *)prop; if (!promP1275 || (type == PROM_NODE_SBUS) || (type == PROM_NODE_EBUS)) sprintf (regstr, "@%x,%x", reg[0], reg[1]); else if (type == PROM_NODE_PCI) { if ((reg[0] >> 8) & 7) sprintf (regstr, "@%x,%x", (reg[0] >> 11) & 0x1f, (reg[0] >> 8) & 7); else sprintf (regstr, "@%x", (reg[0] >> 11) & 0x1f); } else if (len == 4) sprintf (regstr, "@%x", reg[0]); else { unsigned int regs[2]; /* Things get more complicated on UPA. If upa-portid exists, then address is @upa-portid,second-int-in-reg, otherwise it is @first-int-in-reg/16,second-int-in-reg (well, probably upa-portid always exists, but just to be safe). */ memcpy (regs, reg, sizeof(regs)); prop = promGetProperty("upa-portid", &len); if (prop && len == 4) { reg = (unsigned int *)prop; sprintf (regstr, "@%x,%x", reg[0], regs[1]); } else sprintf (regstr, "@%x,%x", regs[0] >> 4, regs[1]); } } return regstr; } static int promWalkNode2Pathname(char *path, int parent, int node, int searchNode, int type) { int nextnode; int len, ntype = type; char *prop, *p; prop = promGetProperty("name", &len); *path = '/'; if (!prop || len <= 0) return 0; if ((!strcmp(prop, "sbus") || !strcmp(prop, "sbi")) && !type) ntype = PROM_NODE_SBUS; else if (!strcmp(prop, "ebus") && type == PROM_NODE_PCI) ntype = PROM_NODE_EBUS; else if (!strcmp(prop, "pci") && !type) ntype = PROM_NODE_PCI; strcpy (path + 1, prop); p = promGetReg(type); if (*p) strcat (path, p); if (node == searchNode) return 1; nextnode = promGetChild(node); if (nextnode && promWalkNode2Pathname(strchr(path, 0), node, nextnode, searchNode, ntype)) return 1; nextnode = promGetSibling(node); if (nextnode && promWalkNode2Pathname(path, parent, nextnode, searchNode, type)) return 1; return 0; } char * sparcPromNode2Pathname(sbusPromNodePtr pnode) { char *ret; if (!pnode->node) return NULL; ret = xalloc(4096); if (!ret) return NULL; if (promWalkNode2Pathname(ret, promRootNode, promGetChild(promRootNode), pnode->node, 0)) return ret; xfree(ret); return NULL; } static int promWalkPathname2Node(char *name, char *regstr, int parent, int type) { int len, node, ret; char *prop, *p; for (;;) { prop = promGetProperty("name", &len); if (!prop || len <= 0) return 0; if ((!strcmp(prop, "sbus") || !strcmp(prop, "sbi")) && !type) type = PROM_NODE_SBUS; else if (!strcmp(prop, "ebus") && type == PROM_NODE_PCI) type = PROM_NODE_EBUS; else if (!strcmp(prop, "pci") && !type) type = PROM_NODE_PCI; for (node = promGetChild(parent); node; node = promGetSibling(node)) { prop = promGetProperty("name", &len); if (!prop || len <= 0) continue; if (*name && strcmp(name, prop)) continue; if (*regstr) { p = promGetReg(type); if (! *p || strcmp(p + 1, regstr)) continue; } break; } if (!node) { for (node = promGetChild(parent); node; node = promGetSibling(node)) { ret = promWalkPathname2Node(name, regstr, node, type); if (ret) return ret; } return 0; } name = strchr(regstr, 0) + 1; if (! *name) return node; p = strchr(name, '/'); if (p) *p = 0; else p = strchr(name, 0); regstr = strchr(name, '@'); if (regstr) *regstr++ = 0; else regstr = p; if (name == regstr) return 0; parent = node; } } int sparcPromPathname2Node(const char *pathName) { int i; char *name, *regstr, *p; i = strlen(pathName); name = xalloc(i + 2); if (! name) return 0; strcpy (name, pathName); name [i + 1] = 0; if (name[0] != '/') return 0; p = strchr(name + 1, '/'); if (p) *p = 0; else p = strchr(name, 0); regstr = strchr(name, '@'); if (regstr) *regstr++ = 0; else regstr = p; if (name + 1 == regstr) return 0; promGetSibling(0); i = promWalkPathname2Node(name + 1, regstr, promRootNode, 0); xfree(name); return i; } pointer xf86MapSbusMem(sbusDevicePtr psdp, unsigned long offset, unsigned long size) { pointer ret; unsigned long pagemask = xf86getpagesize() - 1; unsigned long off = offset & ~pagemask; unsigned long len = ((offset + size + pagemask) & ~pagemask) - off; if (psdp->fd == -1) { psdp->fd = open(psdp->device, O_RDWR); if (psdp->fd == -1) return NULL; } else if (psdp->fd < 0) return NULL; ret = (pointer) mmap (NULL, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, psdp->fd, off); if (ret == (pointer) -1) { ret = (pointer) mmap (NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, psdp->fd, off); } if (ret == (pointer) -1) return NULL; return (char *)ret + (offset - off); } void xf86UnmapSbusMem(sbusDevicePtr psdp, pointer addr, unsigned long size) { unsigned long mask = xf86getpagesize() - 1; unsigned long base = (unsigned long)addr & ~mask; unsigned long len = (((unsigned long)addr + size + mask) & ~mask) - base; munmap ((pointer)base, len); } /* Tell OS that we are driving the HW cursor ourselves. */ void xf86SbusHideOsHwCursor(sbusDevicePtr psdp) { struct fbcursor fbcursor; unsigned char zeros[8]; memset(&fbcursor, 0, sizeof(fbcursor)); memset(&zeros, 0, sizeof(zeros)); fbcursor.cmap.count = 2; fbcursor.cmap.red = zeros; fbcursor.cmap.green = zeros; fbcursor.cmap.blue = zeros; fbcursor.image = (char *)zeros; fbcursor.mask = (char *)zeros; fbcursor.size.x = 32; fbcursor.size.y = 1; fbcursor.set = FB_CUR_SETALL; ioctl(psdp->fd, FBIOSCURSOR, &fbcursor); } /* Set HW cursor colormap. */ void xf86SbusSetOsHwCursorCmap(sbusDevicePtr psdp, int bg, int fg) { struct fbcursor fbcursor; unsigned char red[2], green[2], blue[2]; memset(&fbcursor, 0, sizeof(fbcursor)); red[0] = bg >> 16; green[0] = bg >> 8; blue[0] = bg; red[1] = fg >> 16; green[1] = fg >> 8; blue[1] = fg; fbcursor.cmap.count = 2; fbcursor.cmap.red = red; fbcursor.cmap.green = green; fbcursor.cmap.blue = blue; fbcursor.set = FB_CUR_SETCMAP; ioctl(psdp->fd, FBIOSCURSOR, &fbcursor); }